diff --git a/library/src/scala/Predef.scala b/library/src/scala/Predef.scala index 9a1e90865070..7e2c2f09666b 100644 --- a/library/src/scala/Predef.scala +++ b/library/src/scala/Predef.scala @@ -16,8 +16,10 @@ import scala.language.`2.13` import scala.language.implicitConversions import scala.collection.{mutable, immutable, ArrayOps, StringOps}, immutable.WrappedString -import scala.annotation.{elidable, implicitNotFound}, elidable.ASSERTION +import scala.annotation.{elidable, experimental, implicitNotFound, publicInBinary, targetName }, elidable.ASSERTION import scala.annotation.meta.{ companionClass, companionMethod } +import scala.annotation.internal.{ RuntimeChecked } +import scala.compiletime.summonFrom /** The `Predef` object provides definitions that are accessible in all Scala * compilation units without explicit qualification. @@ -137,6 +139,23 @@ 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 = 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. * @@ -218,6 +237,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]]. * @@ -249,7 +275,26 @@ object Predef extends LowPriorityImplicits { */ @inline def locally[T](@deprecatedName("x") x: T): T = x - // assertions --------------------------------------------------------- + // ============================================================================================== + // ========================================= ASSERTIONS ========================================= + // ============================================================================================== + + /* In Scala 3, `assert` are methods that are `transparent` and `inline`. + In Scala 2, `assert` are methods that are elidable, inlinable by the optimizer + For scala 2 code to be able to run with the scala 3 library in the classpath + (following our own compatibility policies), we will need the `assert` methods + to be available at runtime. + To achieve this, we keep the Scala 3 signature publicly available. + We rely on the fact that it is `inline` and will not be visible in the bytecode. + To add the required Scala 2 ones, we define the `scala2Assert`, we use: + - `@targetName` to swap the name in the generated code to `assert` + - `@publicInBinary` to make it available during runtime. + As such, we would successfully hijack the definitions of `assert` such as: + - At compile time, we would have the definitions of `assert` + - At runtime, the definitions of `scala2Assert` as `assert` + NOTE: Tasty-Reader in Scala 2 will have to learn about this swapping if we are to + allow loading the full Scala 3 library by it. + */ /** Tests an expression, throwing an `AssertionError` if false. * Calls to this method will not be generated if `-Xelide-below` @@ -259,8 +304,8 @@ object Predef extends LowPriorityImplicits { * @param assertion the expression to test * @group assertions */ - @elidable(ASSERTION) - def assert(assertion: Boolean): Unit = { + @elidable(ASSERTION) @publicInBinary + @targetName("assert") private[scala] def scala2Assert(assertion: Boolean): Unit = { if (!assertion) throw new java.lang.AssertionError("assertion failed") } @@ -274,12 +319,22 @@ object Predef extends LowPriorityImplicits { * @param message a String to include in the failure message * @group assertions */ - @elidable(ASSERTION) @inline - final def assert(assertion: Boolean, message: => Any): Unit = { + @elidable(ASSERTION) @inline @publicInBinary + @targetName("assert") private[scala] final def scala2Assert(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 @@ -509,6 +564,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 infix 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 infix 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 diff --git a/library/src/scala/language.scala b/library/src/scala/language.scala index 12354e85833d..9a8cb768a4a9 100644 --- a/library/src/scala/language.scala +++ b/library/src/scala/language.scala @@ -14,6 +14,8 @@ package scala import scala.language.`2.13` +import scala.annotation.compileTimeOnly + /** * The `scala.language` object controls the language features available to the programmer, as proposed in the * [[https://docs.google.com/document/d/1nlkvpoIRkx7at1qJEZafJwthZ3GeIklTFhqmXMvTX9Q/edit '''SIP-18 document''']]. @@ -212,5 +214,343 @@ object language { * to debug and understand. */ implicit lazy val macros: macros = languageFeature.experimental.macros + + /* Experimental support for richer dependent types (disabled for now) + * One can still run the compiler with support for parsing singleton applications + * using command line option `-language:experimental.dependent`. + * But one cannot use a feature import for this as long as this entry is commented out. + */ + //object dependent + + /** Experimental support for named type arguments. + * + * @see [[https://dotty.epfl.ch/docs/reference/other-new-features/named-typeargs]] + */ + @compileTimeOnly("`namedTypeArguments` can only be used at compile time in import statements") + object namedTypeArguments + + /** Experimental support for generic number literals. + * + * @see [[https://dotty.epfl.ch/docs/reference/changed-features/numeric-literals]] + */ + @compileTimeOnly("`genericNumberLiterals` can only be used at compile time in import statements") + object genericNumberLiterals + + /** Experimental support for `erased` modifier + * + * @see [[https://dotty.epfl.ch/docs/reference/experimental/erased-defs]] + */ + @compileTimeOnly("`erasedDefinitions` can only be used at compile time in import statements") + object erasedDefinitions + + /** Experimental support for using indentation for arguments + */ + @compileTimeOnly("`fewerBraces` can only be used at compile time in import statements") + @deprecated("`fewerBraces` is now standard, no language import is needed", since = "3.3") + object fewerBraces + + /** Experimental support for typechecked exception capabilities + * + * @see [[https://dotty.epfl.ch/docs/reference/experimental/canthrow]] + */ + @compileTimeOnly("`saferExceptions` can only be used at compile time in import statements") + object saferExceptions + + /** Adds support for clause interleaving: + * Methods can now have as many type clauses as they like, this allows to have type bounds depend on terms: `def f(x: Int)[A <: x.type]: A` + * + * @see [[https://github.com/scala/improvement-proposals/blob/main/content/clause-interleaving.md]] + */ + @compileTimeOnly("`clauseInterleaving` can only be used at compile time in import statements") + @deprecated("`clauseInterleaving` is now standard, no language import is needed", since = "3.6") + object clauseInterleaving + + /** Experimental support for pure function type syntax + * + * @see [[https://dotty.epfl.ch/docs/reference/experimental/purefuns]] + */ + @compileTimeOnly("`pureFunctions` can only be used at compile time in import statements") + object pureFunctions + + /** Experimental support for capture checking; implies support for pureFunctions + * + * @see [[https://dotty.epfl.ch/docs/reference/experimental/cc]] + */ + @compileTimeOnly("`captureChecking` can only be used at compile time in import statements") + object captureChecking + + /** Experimental support for automatic conversions of arguments, without requiring + * a language import `import scala.language.implicitConversions`. + * + * @see [[https://dotty.epfl.ch/docs/reference/experimental/into-modifier]] + */ + @compileTimeOnly("`into` can only be used at compile time in import statements") + object into + + /** Experimental support for named tuples. + * + * @see [[https://dotty.epfl.ch/docs/reference/experimental/named-tuples]] + */ + @compileTimeOnly("`namedTuples` can only be used at compile time in import statements") + @deprecated("The experimental.namedTuples language import is no longer needed since the feature is now standard", since = "3.7") + object namedTuples + + /** Experimental support for new features for better modularity, including + * - better tracking of dependencies through classes + * - better usability of context bounds + * - better syntax and conventions for type classes + * - ability to merge exported types in intersections + * + * @see [[https://dotty.epfl.ch/docs/reference/experimental/modularity]] + * @see [[https://dotty.epfl.ch/docs/reference/experimental/typeclasses]] + */ + @compileTimeOnly("`modularity` can only be used at compile time in import statements") + object modularity + + /** Was needed to add support for relaxed imports of extension methods. + * The language import is no longer needed as this is now a standard feature since SIP was accepted. + * @see [[http://dotty.epfl.ch/docs/reference/contextual/extension-methods]] + */ + @compileTimeOnly("`relaxedExtensionImports` can only be used at compile time in import statements") + @deprecated("The experimental.relaxedExtensionImports language import is no longer needed since the feature is now standard", since = "3.4") + object relaxedExtensionImports + + /** Enhance match type extractors to follow aliases and singletons. + * + * @see [[https://github.com/scala/improvement-proposals/pull/84]] + */ + @compileTimeOnly("`betterMatchTypeExtractors` can only be used at compile time in import statements") + @deprecated("The experimental.betterMatchTypeExtractors language import is no longer needed since the feature is now standard. It now has no effect, including when setting an older source version.", since = "3.6") + object betterMatchTypeExtractors + + /** Experimental support for quote pattern matching with polymorphic functions + * + * @see [[https://dotty.epfl.ch/docs/reference/experimental/quoted-patterns-with-polymorphic-functions]] + */ + @compileTimeOnly("`quotedPatternsWithPolymorphicFunctions` can only be used at compile time in import statements") + object quotedPatternsWithPolymorphicFunctions + + /** Experimental support for improvements in `for` comprehensions + * + * @see [[https://github.com/scala/improvement-proposals/pull/79]] + */ + @compileTimeOnly("`betterFors` can only be used at compile time in import statements") + @deprecated("The `experimental.betterFors` language import no longer has any effect, the feature is being stablised and can be enabled using `-preview` flag", since = "3.7") + object betterFors + + /** Experimental support for package object values + */ + @compileTimeOnly("`packageObjectValues` can only be used at compile time in import statements") + object packageObjectValues + } + + /** The deprecated object contains features that are no longer officially suypported in Scala. + * Features in this object are slated for removal. New code should not use them and + * old code should migrate away from them. + */ + @compileTimeOnly("`deprecated` can only be used at compile time in import statements") + object deprecated: + + /** Symbol literals have been deprecated since 2.13. Since Scala 3.0 they + * are no longer an official part of Scala. For compatibility with legacy software, + * symbol literals are still supported with a language import, but new software + * should not use them. + */ + @compileTimeOnly("`symbolLiterals` can only be used at compile time in import statements") + object symbolLiterals + + end deprecated + + /** Where imported, auto-tupling is disabled. + * + * '''Why control the feature?''' Auto-tupling can lead to confusing and + * brittle code in presence of overloads. In particular, surprising overloads + * can be selected, and adding new overloads can change which overload is selected + * in suprising ways. + * + * '''Why allow it?''' Not allowing auto-tupling is difficult to reconcile with + * operators accepting tuples. + */ + @compileTimeOnly("`noAutoTupling` can only be used at compile time in import statements") + object noAutoTupling + + /** Where imported, loose equality using eqAny is disabled. + * + * '''Why allow and control the feature?''' For compatibility and migration reasons, + * strict equality is opt-in. See linked documentation for more information. + * + * @see [[https://dotty.epfl.ch/docs/reference/contextual/multiversal-equality]] + */ + @compileTimeOnly("`strictEquality` can only be used at compile time in import statements") + object strictEquality + + /** Where imported, ad hoc extensions of non-open classes in other + * compilation units are allowed. + * + * '''Why control the feature?''' Ad-hoc extensions should usually be avoided + * since they typically cannot rely on an "internal" contract between a class + * and its extensions. Only open classes need to specify such a contract. + * Ad-hoc extensions might break for future versions of the extended class, + * since the extended class is free to change its implementation without + * being constrained by an internal contract. + * + * '''Why allow it?''' An ad-hoc extension can sometimes be necessary, + * for instance when mocking a class in a testing framework, or to work + * around a bug or missing feature in the original class. Nevertheless, + * such extensions should be limited in scope and clearly documented. + * That's why the language import is required for them. + */ + @compileTimeOnly("`adhocExtensions` can only be used at compile time in import statements") + object adhocExtensions + + /** Unsafe Nulls fot Explicit Nulls + * Inside the "unsafe" scope, `Null` is considered as a subtype of all reference types. + * + * @see [[http://dotty.epfl.ch/docs/reference/other-new-features/explicit-nulls.html]] + */ + @compileTimeOnly("`unsafeNulls` can only be used at compile time in import statements") + object unsafeNulls + + @compileTimeOnly("`future` can only be used at compile time in import statements") + object future + + @compileTimeOnly("`future-migration` can only be used at compile time in import statements") + object `future-migration` + + /** Set source version to 2.13. Effectively, this doesn't change the source language, + * but rather adapts the generated code as if it was compiled with Scala 2.13 + */ + @compileTimeOnly("`2.13` can only be used at compile time in import statements") + private[scala] object `2.13` + + /** Set source version to 3.0-migration. + * + * @see [[https://docs.scala-lang.org/scala3/guides/migration/compatibility-intro.html]] + */ + @compileTimeOnly("`3.0-migration` can only be used at compile time in import statements") + object `3.0-migration` + + /** Set source version to 3.0. + * + * @see [[https://docs.scala-lang.org/scala3/guides/migration/compatibility-intro.html]] + */ + @compileTimeOnly("`3.0` can only be used at compile time in import statements") + object `3.0` + + /** Set source version to 3.1-migration. + * + * This is a no-op, and should not be used. A syntax error will be reported upon import. + * + * @see [[https://docs.scala-lang.org/scala3/guides/migration/compatibility-intro.html]] + */ + @compileTimeOnly("`3.1-migration` can only be used at compile time in import statements") + @deprecated("`3.1-migration` is not valid, use `3.1` instead", since = "3.2") + object `3.1-migration` + + /** Set source version to 3.1 + * + * @see [[https://docs.scala-lang.org/scala3/guides/migration/compatibility-intro.html]] + */ + @compileTimeOnly("`3.1` can only be used at compile time in import statements") + object `3.1` + + /** Set source version to 3.2-migration. + * + * @see [[https://docs.scala-lang.org/scala3/guides/migration/compatibility-intro.html]] + */ + @compileTimeOnly("`3.2-migration` can only be used at compile time in import statements") + object `3.2-migration` + + /** Set source version to 3.2 + * + * @see [[https://docs.scala-lang.org/scala3/guides/migration/compatibility-intro.html]] + */ + @compileTimeOnly("`3.2` can only be used at compile time in import statements") + object `3.2` + + /** Set source version to 3.3-migration. + * + * @see [[https://docs.scala-lang.org/scala3/guides/migration/compatibility-intro.html]] + */ + @compileTimeOnly("`3.3-migration` can only be used at compile time in import statements") + object `3.3-migration` + + /** Set source version to 3.3 + * + * @see [[https://docs.scala-lang.org/scala3/guides/migration/compatibility-intro.html]] + */ + @compileTimeOnly("`3.3` can only be used at compile time in import statements") + object `3.3` + + /** Set source version to 3.4-migration. + * + * @see [[https://docs.scala-lang.org/scala3/guides/migration/compatibility-intro.html]] + */ + @compileTimeOnly("`3.4-migration` can only be used at compile time in import statements") + object `3.4-migration` + + /** Set source version to 3.4 + * + * @see [[https://docs.scala-lang.org/scala3/guides/migration/compatibility-intro.html]] + */ + @compileTimeOnly("`3.4` can only be used at compile time in import statements") + object `3.4` + + /** Set source version to 3.5-migration. + * + * @see [[https://docs.scala-lang.org/scala3/guides/migration/compatibility-intro.html]] + */ + @compileTimeOnly("`3.5-migration` can only be used at compile time in import statements") + object `3.5-migration` + + /** Set source version to 3.5 + * + * @see [[https://docs.scala-lang.org/scala3/guides/migration/compatibility-intro.html]] + */ + @compileTimeOnly("`3.5` can only be used at compile time in import statements") + object `3.5` + + /** Set source version to 3.6-migration. + * + * @see [[https://docs.scala-lang.org/scala3/guides/migration/compatibility-intro.html]] + */ + @compileTimeOnly("`3.6-migration` can only be used at compile time in import statements") + object `3.6-migration` + + /** Set source version to 3.6 + * + * @see [[https://docs.scala-lang.org/scala3/guides/migration/compatibility-intro.html]] + */ + @compileTimeOnly("`3.6` can only be used at compile time in import statements") + object `3.6` + + /** Set source version to 3.7-migration. + * + * @see [[https://docs.scala-lang.org/scala3/guides/migration/compatibility-intro.html]] + */ + @compileTimeOnly("`3.7-migration` can only be used at compile time in import statements") + object `3.7-migration` + + /** Set source version to 3.7 + * + * @see [[https://docs.scala-lang.org/scala3/guides/migration/compatibility-intro.html]] + */ + @compileTimeOnly("`3.7` can only be used at compile time in import statements") + object `3.7` + + /** Set source version to 3.8-migration. + * + * @see [[https://docs.scala-lang.org/scala3/guides/migration/compatibility-intro.html]] + */ + @compileTimeOnly("`3.8-migration` can only be used at compile time in import statements") + object `3.8-migration` + + /** Set source version to 3.8 + * + * @see [[https://docs.scala-lang.org/scala3/guides/migration/compatibility-intro.html]] + */ + @compileTimeOnly("`3.8` can only be used at compile time in import statements") + object `3.8` + } diff --git a/project/Build.scala b/project/Build.scala index 3a614f227e84..5ae33ea9af73 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -1430,7 +1430,8 @@ object Build { Compile / unmanagedSourceDirectories += baseDirectory.value / "src-non-bootstrapped", // NOTE: The only difference here is that we drop `-Werror` and semanticDB for now Compile / scalacOptions := Seq("-deprecation", "-feature", "-unchecked", "-encoding", "UTF8", "-language:implicitConversions"), - (Compile / scalacOptions) ++= Seq( + Compile / scalacOptions += "-Yno-stdlib-patches", + Compile / scalacOptions ++= Seq( // Needed so that the library sources are visible when `dotty.tools.dotc.core.Definitions#init` is called "-sourcepath", (Compile / sourceDirectories).value.map(_.getCanonicalPath).distinct.mkString(File.pathSeparator), ), @@ -1456,6 +1457,7 @@ object Build { Compile / unmanagedSourceDirectories += baseDirectory.value / "src-bootstrapped", // NOTE: The only difference here is that we drop `-Werror` and semanticDB for now Compile / scalacOptions := Seq("-deprecation", "-feature", "-unchecked", "-encoding", "UTF8", "-language:implicitConversions"), + Compile / scalacOptions += "-Yno-stdlib-patches", Compile / scalacOptions ++= Seq( // Needed so that the library sources are visible when `dotty.tools.dotc.core.Definitions#init` is called "-sourcepath", (Compile / sourceDirectories).value.map(_.getCanonicalPath).distinct.mkString(File.pathSeparator),