Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions compiler/src/dotty/tools/dotc/core/Definitions.scala
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,7 @@ class Definitions {
@tu lazy val ScalaXmlPackageClass: Symbol = getPackageClassIfDefined("scala.xml")

@tu lazy val CompiletimePackageObject: Symbol = requiredModule("scala.compiletime.package")
@tu lazy val Compiletime_code: Symbol = CompiletimePackageObject.requiredMethod("code")
@tu lazy val Compiletime_erasedValue : Symbol = CompiletimePackageObject.requiredMethod("erasedValue")
@tu lazy val Compiletime_error : Symbol = CompiletimePackageObject.requiredMethod(nme.error)
@tu lazy val Compiletime_requireConst: Symbol = CompiletimePackageObject.requiredMethod("requireConst")
Expand Down
28 changes: 28 additions & 0 deletions compiler/src/dotty/tools/dotc/typer/Inliner.scala
Original file line number Diff line number Diff line change
Expand Up @@ -332,6 +332,32 @@ object Inliner {
def typeCheckErrors(tree: Tree)(using Context): Tree =
val errors = compileForErrors(tree, false)
packErrors(errors)

def code(strCtx: Tree, argsTree: Tree, pos: SrcPos)(using Context): Tree =
object Consts:
def unapply(trees: List[Tree]): Option[List[String]] =
trees match
case Nil => Some(Nil)
case Literal(Constant(part: String)) :: Consts(rest) => Some(part :: rest)
case _ => None
strCtx match
case Apply(stringContextApply, List(Typed(SeqLiteral(Consts(parts), _), _)))
if stringContextApply.symbol == defn.StringContextModule_apply =>
argsTree match
case Typed(SeqLiteral(args, _), _) =>
if parts.size == args.size + 1 then
val argStrs = args.map(_.show)
val showed = StringContext(parts: _*).s(argStrs: _*)
Literal(Constant(showed)).withSpan(pos.span)
else
report.error("Wrong number of arguments for StringContext.s", strCtx.srcPos)
ref(defn.Predef_undefined)
case _ =>
report.error("Exprected explicit arguments to code", strCtx.srcPos)
ref(defn.Predef_undefined)
case _ =>
report.error("Exprected StringContext.apply with explicit `parts` arguments", strCtx.srcPos)
ref(defn.Predef_undefined)
}
}

Expand Down Expand Up @@ -622,6 +648,8 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(using Context) {
arg match
case ConstantValue(_) | Inlined(_, Nil, Typed(ConstantValue(_), _)) => // ok
case _ => report.error(em"expected a constant value but found: $arg", arg.srcPos)
case (strCtx :: Nil) :: (args :: Nil) :: Nil if inlinedMethod == defn.Compiletime_code =>
return Intrinsics.code(strCtx, args, call.srcPos)
case _ =>

// Special handling of `constValue[T]` and `constValueOpt[T]`
Expand Down
11 changes: 0 additions & 11 deletions library/src-bootstrapped/dotty/internal/CompileTimeMacros.scala

This file was deleted.

This file was deleted.

31 changes: 23 additions & 8 deletions library/src/scala/compiletime/package.scala
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,19 @@ package object compiletime {
* ```scala
* error(code"My error of this code: ${println("foo")}")
* ```
* or
* ```scala
* inline def errorOnThisCode(inline x: Any) =
* error(code"My error of this code: $x")
* ```
*/
inline def error(inline msg: String): Nothing = ???

extension (inline self: StringContext):
/** Returns the string representation of interpolated elaborated code:
*
* ```scala
* inline def logged(p1: => Any) = {
* inline def logged(inline p1: Any) = {
* val c = code"code: $p1"
* val res = p1
* (c, p1)
Expand All @@ -45,11 +50,15 @@ package object compiletime {
* // ("code: scala.Predef.identity("foo")", identity("foo"))
* ```
*
* @note only by-name arguments will be displayed as "code".
* 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.
*/
transparent inline def code (inline args: Any*): String =
${ dotty.internal.CompileTimeMacros.codeExpr('self, 'args) }
// implemented in dotty.tools.dotc.typer.Inliner.Intrinsics
error("`code` was not evaluated by the compiler")

end extension

/** Checks at compiletime that the provided values is a constant after
Expand All @@ -66,18 +75,24 @@ package object compiletime {
* twice(m) // error: expected a constant value but found: m
* ```
*/
inline def requireConst(inline x: Boolean | Byte | Short | Int | Long | Float | Double | Char | String): Unit = ()
inline def requireConst(inline x: Boolean | Byte | Short | Int | Long | Float | Double | Char | String): Unit =
// implemented in dotty.tools.dotc.typer.Inliner
error("`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`.
*/
inline def constValueOpt[T]: Option[T] = ???
inline def constValueOpt[T]: Option[T] =
// implemented in dotty.tools.dotc.typer.Inliner
error("`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)`.
*/
inline def constValue[T]: T = ???
inline def constValue[T]: T =
// implemented in dotty.tools.dotc.typer.Inliner
error("`constValue` was not evaluated by the compiler")

/** Given a tuple type `(X1, ..., Xn)`, returns a tuple value
* `(constValue[X1], ..., constValue[Xn])`.
Expand All @@ -104,8 +119,8 @@ package object compiletime {
*
* the returned value would be `2`.
*/
transparent inline def summonFrom[T](f: Nothing => T): T = ???

transparent inline def summonFrom[T](f: Nothing => T): T =
error("`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.
Expand Down
2 changes: 2 additions & 0 deletions library/src/scala/compiletime/testing/package.scala
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ package object testing {
* The code should be a sequence of expressions or statements that may appear in a block.
*/
inline def typeChecks(inline code: String): Boolean =
// implemented in package dotty.tools.dotc.typer.Inliner.Intrinsics
error("`typeChecks` was not checked by the compiler")

/** Whether the code type checks in the current context? If not,
Expand All @@ -27,5 +28,6 @@ package object testing {
* The code should be a sequence of expressions or statements that may appear in a block.
*/
inline def typeCheckErrors(inline code: String): List[Error] =
// implemented in package dotty.tools.dotc.typer.Inliner.Intrinsics
error("`typeCheckErrors` was not checked by the compiler")
}
4 changes: 2 additions & 2 deletions tests/run-macros/beta-reduce-inline-result.check
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
compile-time: (4: scala.Int)
compile-time: 4:Int
run-time: 4
compile-time: (1: scala.Int)
compile-time: 1:Int
run-time: 1
run-time: 5
run-time: 7
Expand Down
6 changes: 6 additions & 0 deletions tests/run-macros/i6622.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
abc println(34) ...
abc println(34)
println(34) ...
println(34)
...

12 changes: 6 additions & 6 deletions tests/run-macros/i6622.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@ import scala.compiletime._
object Test {

def main(args: Array[String]): Unit = {
assert(code"abc ${println(34)} ..." == "abc scala.Predef.println(34) ...")
assert(code"abc ${println(34)}" == "abc scala.Predef.println(34)")
assert(code"${println(34)} ..." == "scala.Predef.println(34) ...")
assert(code"${println(34)}" == "scala.Predef.println(34)")
assert(code"..." == "...")
assert(testConstant(code"") == "")
println(code"abc ${println(34)} ...")
println(code"abc ${println(34)}")
println(code"${println(34)} ...")
println(code"${println(34)}")
println(code"...")
println(testConstant(code""))
}

inline def testConstant(inline msg: String): String = msg
Expand Down