From a5e054d6bee64673b9be3cacbb1552459e09f235 Mon Sep 17 00:00:00 2001 From: Hamza Remmal Date: Mon, 19 May 2025 16:16:41 +0200 Subject: [PATCH] chore: port definitions to Predef --- library/src/scala/Predef.scala | 111 +++++++++++++++++++++++++++++++-- 1 file changed, 106 insertions(+), 5 deletions(-) diff --git a/library/src/scala/Predef.scala b/library/src/scala/Predef.scala index 26dbc568a9ab..02492aecee59 100644 --- a/library/src/scala/Predef.scala +++ b/library/src/scala/Predef.scala @@ -15,8 +15,9 @@ package scala import scala.language.implicitConversions import scala.collection.{mutable, immutable, ArrayOps, StringOps}, immutable.WrappedString -import scala.annotation.{elidable, implicitNotFound}, elidable.ASSERTION +import scala.annotation.{elidable, implicitNotFound, publicInBinary, targetName, experimental}, elidable.ASSERTION import scala.annotation.meta.{ companionClass, companionMethod } +import scala.annotation.internal.RuntimeChecked /** The `Predef` object provides definitions that are accessible in all Scala * compilation units without explicit qualification. @@ -136,6 +137,24 @@ object Predef extends LowPriorityImplicits { */ @inline def valueOf[T](implicit vt: ValueOf[T]): T = vt.value + /** + * Retrieve the single value of a type with a unique inhabitant. + * + * @example {{{ + * object Foo + * val foo = valueOf[Foo.type] + * // foo is Foo.type = Foo + * + * val bar = valueOf[23] + * // bar is 23.type = 23 + * }}} + * @group utilities + */ + inline def valueOf[T]: T = compiletime.summonFrom { + case ev: ValueOf[T] => ev.value + } + + /** The `String` type in Scala has all the methods of the underlying * [[java.lang.String]], of which it is just an alias. * @@ -217,6 +236,13 @@ object Predef extends LowPriorityImplicits { */ @inline def implicitly[T](implicit e: T): T = e // TODO: when dependent method types are on by default, give this result type `e.type`, so that inliner has better chance of knowing which method to inline in calls like `implicitly[MatchingStrategy[Option]].zero` + /** Summon a given value of type `T`. Usually, the argument is not passed explicitly. + * + * @tparam T the type of the value to be summoned + * @return the given value typed: the provided type parameter + */ + transparent inline def summon[T](using x: T): x.type = x + /** Used to mark code blocks as being expressions, instead of being taken as part of anonymous classes and the like. * This is just a different name for [[identity]]. * @@ -248,7 +274,9 @@ object Predef extends LowPriorityImplicits { */ @inline def locally[T](@deprecatedName("x") x: T): T = x - // assertions --------------------------------------------------------- + // ============================================================================================== + // ========================================= ASSERTIONS ========================================= + // ============================================================================================== /** Tests an expression, throwing an `AssertionError` if false. * Calls to this method will not be generated if `-Xelide-below` @@ -259,7 +287,8 @@ object Predef extends LowPriorityImplicits { * @group assertions */ @elidable(ASSERTION) - def assert(assertion: Boolean): Unit = { + @targetName("assert") @publicInBinary + protected def oldAssert(assertion: Boolean): Unit = { if (!assertion) throw new java.lang.AssertionError("assertion failed") } @@ -274,11 +303,22 @@ object Predef extends LowPriorityImplicits { * @group assertions */ @elidable(ASSERTION) @inline - final def assert(assertion: Boolean, message: => Any): Unit = { + @targetName("assert") @publicInBinary + protected def oldAssert(assertion: Boolean, message: => Any): Unit = { if (!assertion) throw new java.lang.AssertionError("assertion failed: "+ message) } + transparent inline def assert(inline assertion: Boolean, inline message: => Any): Unit = + if !assertion then scala.runtime.Scala3RunTime.assertFailed(message) + + transparent inline def assert(inline assertion: Boolean): Unit = + if !assertion then scala.runtime.Scala3RunTime.assertFailed() + + // ============================================================================================== + // ========================================= ASSUMPTIONS ======================================== + // ============================================================================================== + /** Tests an expression, throwing an `AssertionError` if false. * This method differs from assert only in the intent expressed: * assert contains a predicate which needs to be proven, while @@ -370,7 +410,7 @@ object Predef extends LowPriorityImplicits { @inline def formatted(fmtstr: String): String = fmtstr format self } - /** Injects String concatenation operator `+` to any classes. + /** Injects String concatenation operator `+` to any classes. * @group implicit-classes-any */ @(deprecated @companionMethod)("Implicit injection of + is deprecated. Convert to String to call +", "2.13.0") @@ -508,6 +548,67 @@ object Predef extends LowPriorityImplicits { */ // $ to avoid accidental shadowing (e.g. scala/bug#7788) implicit def $conforms[A]: A => A = <:<.refl + + // Extension methods for working with explicit nulls + + /** Strips away the nullability from a value. Note that `.nn` performs a checked cast, + * so if invoked on a `null` value it will throw an `NullPointerException`. + * @example {{{ + * val s1: String | Null = "hello" + * val s2: String = s1.nn + * + * val s3: String | Null = null + * val s4: String = s3.nn // throw NullPointerException + * }}} + */ + extension [T](x: T | Null) inline def nn: x.type & T = + if x.asInstanceOf[Any] == null then scala.runtime.Scala3RunTime.nnFail() + x.asInstanceOf[x.type & T] + + extension (inline x: AnyRef | Null) + /** Enables an expression of type `T|Null`, where `T` is a subtype of `AnyRef`, to be checked for `null` + * using `eq` rather than only `==`. This is needed because `Null` no longer has + * `eq` or `ne` methods, only `==` and `!=` inherited from `Any`. */ + inline def eq(inline y: AnyRef | Null): Boolean = + x.asInstanceOf[AnyRef] eq y.asInstanceOf[AnyRef] + /** Enables an expression of type `T|Null`, where `T` is a subtype of `AnyRef`, to be checked for `null` + * using `ne` rather than only `!=`. This is needed because `Null` no longer has + * `eq` or `ne` methods, only `==` and `!=` inherited from `Any`. */ + inline def ne(inline y: AnyRef | Null): Boolean = + !(x eq y) + + extension (opt: Option.type) + @experimental + inline def fromNullable[T](t: T | Null): Option[T] = Option(t).asInstanceOf[Option[T]] + + /** A type supporting Self-based type classes. + * + * A is TC + * + * expands to + * + * TC { type Self = A } + * + * which is what is needed for a context bound `[A: TC]`. + */ + @experimental + infix type is[A <: AnyKind, B <: Any{type Self <: AnyKind}] = B { type Self = A } + + extension [T](x: T) + /**Asserts that a term should be exempt from static checks that can be reliably checked at runtime. + * @example {{{ + * val xs: Option[Int] = Option(1) + * xs.runtimeChecked match + * case Some(x) => x // `Some(_)` can be checked at runtime, so no warning + * }}} + * @example {{{ + * val xs: List[Int] = List(1,2,3) + * val y :: ys = xs.runtimeChecked // `_ :: _` can be checked at runtime, so no warning + * }}} + */ + @experimental + inline def runtimeChecked: x.type @RuntimeChecked = x: @RuntimeChecked + } /** The `LowPriorityImplicits` class provides implicit values that