Skip to content

Commit 570e840

Browse files
authored
Add missing definitions in the stdlib (from stdLibPatches) (#23562)
Add all the definitions that we used to patch in the compiler to their actual source file. No more patching. Since this will be merged during the `3.7.3` cycle, we cannot undo yet the logic, hence, we rely on the facts that: - None of the changes in this PR are visible during 3.7.3 - Patching is disabled via `-Yno-stdlib-patches` Even with all the definitions in the trees, we can still not enable back `-Ycheck:all` because the trees for `Tuple1` and `Tuple2` do not contain the definitions of the specialized functions. Based on #23510
2 parents b6c232d + 08e50eb commit 570e840

File tree

3 files changed

+465
-7
lines changed

3 files changed

+465
-7
lines changed

library/src/scala/Predef.scala

Lines changed: 122 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,10 @@ import scala.language.`2.13`
1616
import scala.language.implicitConversions
1717

1818
import scala.collection.{mutable, immutable, ArrayOps, StringOps}, immutable.WrappedString
19-
import scala.annotation.{elidable, implicitNotFound}, elidable.ASSERTION
19+
import scala.annotation.{elidable, experimental, implicitNotFound, publicInBinary, targetName }, elidable.ASSERTION
2020
import scala.annotation.meta.{ companionClass, companionMethod }
21+
import scala.annotation.internal.{ RuntimeChecked }
22+
import scala.compiletime.summonFrom
2123

2224
/** The `Predef` object provides definitions that are accessible in all Scala
2325
* compilation units without explicit qualification.
@@ -137,6 +139,23 @@ object Predef extends LowPriorityImplicits {
137139
*/
138140
@inline def valueOf[T](implicit vt: ValueOf[T]): T = vt.value
139141

142+
/**
143+
* Retrieve the single value of a type with a unique inhabitant.
144+
*
145+
* @example {{{
146+
* object Foo
147+
* val foo = valueOf[Foo.type]
148+
* // foo is Foo.type = Foo
149+
*
150+
* val bar = valueOf[23]
151+
* // bar is 23.type = 23
152+
* }}}
153+
* @group utilities
154+
*/
155+
inline def valueOf[T]: T = summonFrom {
156+
case ev: ValueOf[T] => ev.value
157+
}
158+
140159
/** The `String` type in Scala has all the methods of the underlying
141160
* [[java.lang.String]], of which it is just an alias.
142161
*
@@ -218,6 +237,13 @@ object Predef extends LowPriorityImplicits {
218237
*/
219238
@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`
220239

240+
/** Summon a given value of type `T`. Usually, the argument is not passed explicitly.
241+
*
242+
* @tparam T the type of the value to be summoned
243+
* @return the given value typed: the provided type parameter
244+
*/
245+
transparent inline def summon[T](using x: T): x.type = x
246+
221247
/** Used to mark code blocks as being expressions, instead of being taken as part of anonymous classes and the like.
222248
* This is just a different name for [[identity]].
223249
*
@@ -249,7 +275,26 @@ object Predef extends LowPriorityImplicits {
249275
*/
250276
@inline def locally[T](@deprecatedName("x") x: T): T = x
251277

252-
// assertions ---------------------------------------------------------
278+
// ==============================================================================================
279+
// ========================================= ASSERTIONS =========================================
280+
// ==============================================================================================
281+
282+
/* In Scala 3, `assert` are methods that are `transparent` and `inline`.
283+
In Scala 2, `assert` are methods that are elidable, inlinable by the optimizer
284+
For scala 2 code to be able to run with the scala 3 library in the classpath
285+
(following our own compatibility policies), we will need the `assert` methods
286+
to be available at runtime.
287+
To achieve this, we keep the Scala 3 signature publicly available.
288+
We rely on the fact that it is `inline` and will not be visible in the bytecode.
289+
To add the required Scala 2 ones, we define the `scala2Assert`, we use:
290+
- `@targetName` to swap the name in the generated code to `assert`
291+
- `@publicInBinary` to make it available during runtime.
292+
As such, we would successfully hijack the definitions of `assert` such as:
293+
- At compile time, we would have the definitions of `assert`
294+
- At runtime, the definitions of `scala2Assert` as `assert`
295+
NOTE: Tasty-Reader in Scala 2 will have to learn about this swapping if we are to
296+
allow loading the full Scala 3 library by it.
297+
*/
253298

254299
/** Tests an expression, throwing an `AssertionError` if false.
255300
* Calls to this method will not be generated if `-Xelide-below`
@@ -259,8 +304,8 @@ object Predef extends LowPriorityImplicits {
259304
* @param assertion the expression to test
260305
* @group assertions
261306
*/
262-
@elidable(ASSERTION)
263-
def assert(assertion: Boolean): Unit = {
307+
@elidable(ASSERTION) @publicInBinary
308+
@targetName("assert") private[scala] def scala2Assert(assertion: Boolean): Unit = {
264309
if (!assertion)
265310
throw new java.lang.AssertionError("assertion failed")
266311
}
@@ -274,12 +319,22 @@ object Predef extends LowPriorityImplicits {
274319
* @param message a String to include in the failure message
275320
* @group assertions
276321
*/
277-
@elidable(ASSERTION) @inline
278-
final def assert(assertion: Boolean, message: => Any): Unit = {
322+
@elidable(ASSERTION) @inline @publicInBinary
323+
@targetName("assert") private[scala] final def scala2Assert(assertion: Boolean, message: => Any): Unit = {
279324
if (!assertion)
280325
throw new java.lang.AssertionError("assertion failed: "+ message)
281326
}
282327

328+
transparent inline def assert(inline assertion: Boolean, inline message: => Any): Unit =
329+
if !assertion then scala.runtime.Scala3RunTime.assertFailed(message)
330+
331+
transparent inline def assert(inline assertion: Boolean): Unit =
332+
if !assertion then scala.runtime.Scala3RunTime.assertFailed()
333+
334+
// ==============================================================================================
335+
// ======================================== ASSUMPTIONS =========================================
336+
// ==============================================================================================
337+
283338
/** Tests an expression, throwing an `AssertionError` if false.
284339
* This method differs from assert only in the intent expressed:
285340
* assert contains a predicate which needs to be proven, while
@@ -509,6 +564,67 @@ object Predef extends LowPriorityImplicits {
509564
*/
510565
// $ to avoid accidental shadowing (e.g. scala/bug#7788)
511566
implicit def $conforms[A]: A => A = <:<.refl
567+
568+
// Extension methods for working with explicit nulls
569+
570+
/** Strips away the nullability from a value. Note that `.nn` performs a checked cast,
571+
* so if invoked on a `null` value it will throw an `NullPointerException`.
572+
* @example {{{
573+
* val s1: String | Null = "hello"
574+
* val s2: String = s1.nn
575+
*
576+
* val s3: String | Null = null
577+
* val s4: String = s3.nn // throw NullPointerException
578+
* }}}
579+
*/
580+
extension [T](x: T | Null) inline def nn: x.type & T =
581+
if x.asInstanceOf[Any] == null then scala.runtime.Scala3RunTime.nnFail()
582+
x.asInstanceOf[x.type & T]
583+
584+
extension (inline x: AnyRef | Null)
585+
/** Enables an expression of type `T|Null`, where `T` is a subtype of `AnyRef`, to be checked for `null`
586+
* using `eq` rather than only `==`. This is needed because `Null` no longer has
587+
* `eq` or `ne` methods, only `==` and `!=` inherited from `Any`. */
588+
inline infix def eq(inline y: AnyRef | Null): Boolean =
589+
x.asInstanceOf[AnyRef] eq y.asInstanceOf[AnyRef]
590+
/** Enables an expression of type `T|Null`, where `T` is a subtype of `AnyRef`, to be checked for `null`
591+
* using `ne` rather than only `!=`. This is needed because `Null` no longer has
592+
* `eq` or `ne` methods, only `==` and `!=` inherited from `Any`. */
593+
inline infix def ne(inline y: AnyRef | Null): Boolean =
594+
!(x eq y)
595+
596+
extension (opt: Option.type)
597+
@experimental
598+
inline def fromNullable[T](t: T | Null): Option[T] = Option(t).asInstanceOf[Option[T]]
599+
600+
/** A type supporting Self-based type classes.
601+
*
602+
* A is TC
603+
*
604+
* expands to
605+
*
606+
* TC { type Self = A }
607+
*
608+
* which is what is needed for a context bound `[A: TC]`.
609+
*/
610+
@experimental
611+
infix type is[A <: AnyKind, B <: Any{type Self <: AnyKind}] = B { type Self = A }
612+
613+
extension [T](x: T)
614+
/**Asserts that a term should be exempt from static checks that can be reliably checked at runtime.
615+
* @example {{{
616+
* val xs: Option[Int] = Option(1)
617+
* xs.runtimeChecked match
618+
* case Some(x) => x // `Some(_)` can be checked at runtime, so no warning
619+
* }}}
620+
* @example {{{
621+
* val xs: List[Int] = List(1,2,3)
622+
* val y :: ys = xs.runtimeChecked // `_ :: _` can be checked at runtime, so no warning
623+
* }}}
624+
*/
625+
@experimental
626+
inline def runtimeChecked: x.type @RuntimeChecked = x: @RuntimeChecked
627+
512628
}
513629

514630
/** The `LowPriorityImplicits` class provides implicit values that

0 commit comments

Comments
 (0)