diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index b7dbbe859ce4..97ba83e7f411 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -2092,6 +2092,13 @@ class Typer extends Namer sym.owner.info.decls.openForMutations.unlink(sym) return EmptyTree } + // TODO: - Remove this when `scala.language.experimental.erasedDefinitions` is no longer experimental. + // - Modify signature to `erased def erasedValue[T]: T` + if sym.eq(defn.Compiletime_erasedValue) then + // scala.compiletime.erasedValue should be `erased` but we cannot add this in the source. + // The library cannot use experimental language features, + // hence we special case it until `erased` is no longer experimental. + sym.setFlag(Erased) val DefDef(name, paramss, tpt, _) = ddef completeAnnotations(ddef, sym) val paramss1 = paramss.nestedMapConserve(typed(_)).asInstanceOf[List[ParamClause]] diff --git a/library/src-bootstrapped/scala/compiletime/package.scala b/library/src-bootstrapped/scala/compiletime/package.scala new file mode 100644 index 000000000000..080ef29e0fa2 --- /dev/null +++ b/library/src-bootstrapped/scala/compiletime/package.scala @@ -0,0 +1,160 @@ +package scala +package compiletime + +import annotation.compileTimeOnly + +/** Use this method when you have a type, do not have a value for it but want to + * pattern match on it. For example, given a type `Tup <: Tuple`, one can + * pattern-match on it as follows: + * ```scala + * inline erasedValue[Tup] match { + * case _: EmptyTuple => ... + * case _: h *: t => ... + * } + * ``` + * This value can only be used in an inline match and the value cannot be used in + * the branches. + * @syntax markdown + */ +// TODO add `erased` once it is not an experimental feature anymore +def erasedValue[T]: T = ??? + +/** Used as the initializer of a mutable class or object field, like this: + * + * var x: T = uninitialized + * + * This signifies that the field is not initialized on its own. It is still initialized + * as part of the bulk initialization of the object it belongs to, which assigns zero + * values such as `null`, `0`, `0.0`, `false` to all object fields. + */ +@compileTimeOnly("`uninitialized` can only be used as the right hand side of a mutable field definition") +def uninitialized: Nothing = ??? + +/** The error method is used to produce user-defined compile errors during inline expansion. + * If an inline expansion results in a call error(msgStr) the compiler produces an error message containing the given msgStr. + * + * ```scala + * error("My error message") + * ``` + * or + * ```scala + * inline def errorOnThisCode(inline x: Any) = + * error("My error of this code: " + codeOf(x)) + * ``` + * @syntax markdown + */ +inline def error(inline msg: String): Nothing = ??? + +/** Returns the string representation of argument code: + * + * ```scala + * inline def logged(inline p1: Any) = + * ("code: " + codeOf(p1), p1) + * + * logged(identity("foo")) + * // above is equivalent to: + * // ("code: scala.Predef.identity("foo")", identity("foo")) + * ``` + * + * The formatting of the code is not stable across version of the compiler. + * + * @note only `inline` arguments will be displayed as "code". + * Other values may display unintutively. + * + * @syntax markdown + */ +transparent inline def codeOf(arg: Any): String = + // implemented in dotty.tools.dotc.typer.Inliner.Intrinsics + error("Compiler bug: `codeOf` was not evaluated by the compiler") + +/** Checks at compiletime that the provided values is a constant after + * inlining and constant folding. + * + * Usage: + * ```scala + * inline def twice(inline n: Int): Int = + * requireConst(n) // compile-time assertion that the parameter `n` is a constant + * n + n + * + * twice(1) + * val m: Int = ... + * twice(m) // error: expected a constant value but found: m + * ``` + * @syntax markdown + */ +inline def requireConst(inline x: Boolean | Byte | Short | Int | Long | Float | Double | Char | String): Unit = + // implemented in dotty.tools.dotc.typer.Inliner + error("Compiler bug: `requireConst` was not evaluated by the compiler") + +/** Same as `constValue` but returns a `None` if a constant value + * cannot be constructed from the provided type. Otherwise returns + * that value wrapped in `Some`. + */ +transparent inline def constValueOpt[T]: Option[T] = + // implemented in dotty.tools.dotc.typer.Inliner + error("Compiler bug: `constValueOpt` was not evaluated by the compiler") + +/** Given a constant, singleton type `T`, convert it to a value + * of the same singleton type. For example: `assert(constValue[1] == 1)`. + */ +transparent inline def constValue[T]: T = + // implemented in dotty.tools.dotc.typer.Inliner + error("Compiler bug: `constValue` was not evaluated by the compiler") + +/** Given a tuple type `(X1, ..., Xn)`, returns a tuple value + * `(constValue[X1], ..., constValue[Xn])`. + */ +inline def constValueTuple[T <: Tuple]: T = + val res = + inline erasedValue[T] match + case _: EmptyTuple => EmptyTuple + case _: (t *: ts) => constValue[t] *: constValueTuple[ts] + end match + res.asInstanceOf[T] +end constValueTuple + +/** Summons first given matching one of the listed cases. E.g. in + * + * ```scala + * given B { ... } + * + * summonFrom { + * case given A => 1 + * case given B => 2 + * case given C => 3 + * case _ => 4 + * } + * ``` + * the returned value would be `2`. + * @syntax markdown + */ +transparent inline def summonFrom[T](f: Nothing => T): T = + error("Compiler bug: `summonFrom` was not evaluated by the compiler") + +/** Summon a given value of type `T`. Usually, the argument is not passed explicitly. + * The summoning is delayed until the call has been fully inlined. + * + * @tparam T the type of the value to be summoned + * @return the given value typed as the provided type parameter + */ +transparent inline def summonInline[T]: T = summonFrom { + case t: T => t +} + +/** Given a tuple T, summons each of its member types and returns them in + * a Tuple. + * + * @tparam T the tuple containing the types of the values to be summoned + * @return the given values typed as elements of the tuple + */ +inline def summonAll[T <: Tuple]: T = + val res = + inline erasedValue[T] match + case _: EmptyTuple => EmptyTuple + case _: (t *: ts) => summonInline[t] *: summonAll[ts] + end match + res.asInstanceOf[T] +end summonAll + +/** Assertion that an argument is by-name. Used for nullability checking. */ +def byName[T](x: => T): T = x diff --git a/library/src/scala/compiletime/package.scala b/library/src-non-bootstrapped/scala/compiletime/package.scala similarity index 100% rename from library/src/scala/compiletime/package.scala rename to library/src-non-bootstrapped/scala/compiletime/package.scala