Skip to content

Move blackbox macro expansion to ReifyQuotes #7825

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Show file tree
Hide file tree
Changes from all 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
7 changes: 5 additions & 2 deletions compiler/src/dotty/tools/dotc/core/Flags.scala
Original file line number Diff line number Diff line change
Expand Up @@ -350,6 +350,9 @@ object Flags {
/** An opaque type alias or a class containing one */
val (Opaque @ _, _, _) = newFlags(43, "opaque")

/** An transparent inline method */
val (_, Transparent @ _, _) = newFlags(44, "transparent")


// ------------ Flags following this one are not pickled ----------------------------------

Expand Down Expand Up @@ -426,7 +429,7 @@ object Flags {
CommonSourceModifierFlags.toTypeFlags | Abstract | Sealed | Opaque | Open

val TermSourceModifierFlags: FlagSet =
CommonSourceModifierFlags.toTermFlags | Inline | AbsOverride | Lazy | Erased
CommonSourceModifierFlags.toTermFlags | Inline | AbsOverride | Lazy | Erased | Transparent

/** Flags representing modifiers that can appear in trees */
val ModifierFlags: FlagSet =
Expand All @@ -443,7 +446,7 @@ object Flags {
Scala2SpecialFlags, MutableOrOpen, Opaque, Touched, JavaStatic,
OuterOrCovariant, LabelOrContravariant, CaseAccessor,
Extension, NonMember, Implicit, Given, Permanent, Synthetic,
SuperParamAliasOrScala2x, Inline, Macro)
SuperParamAliasOrScala2x, Inline, Macro, Transparent)

/** Flags that are not (re)set when completing the denotation, or, if symbol is
* a top-level class or object, when completing the denotation once the class
Expand Down
1 change: 1 addition & 0 deletions compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -699,6 +699,7 @@ class TreePickler(pickler: TastyPickler) {
if (flags.is(ParamAccessor)) writeModTag(PARAMsetter)
if (flags.is(SuperParamAlias)) writeModTag(PARAMalias)
if (flags.is(Exported)) writeModTag(EXPORTED)
if (flags.is(Transparent)) writeModTag(TRANSPARENT)
assert(!(flags.is(Label)))
}
else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -635,6 +635,7 @@ class TreeUnpickler(reader: TastyReader,
case INLINE => addFlag(Inline)
case INLINEPROXY => addFlag(InlineProxy)
case MACRO => addFlag(Macro)
case TRANSPARENT => addFlag(Transparent)
case OPAQUE => addFlag(Opaque)
case STATIC => addFlag(JavaStatic)
case OBJECT => addFlag(Module)
Expand Down
7 changes: 5 additions & 2 deletions compiler/src/dotty/tools/dotc/transform/ReifyQuotes.scala
Original file line number Diff line number Diff line change
Expand Up @@ -195,8 +195,7 @@ class ReifyQuotes extends MacroTransform {
case splice: Select => cpy.Select(splice)(body1, splice.name)
}
}
else {
assert(level == 1, "unexpected top splice outside quote")
else if (level == 1) {
val (body1, quotes) = nested(isQuote = false).splitSplice(body)(spliceContext)
val tpe = outer.embedded.getHoleType(body, splice)
val hole = makeHole(splice.isTerm, body1, quotes, tpe).withSpan(splice.span)
Expand All @@ -206,6 +205,10 @@ class ReifyQuotes extends MacroTransform {
// For example we can have a lifted tree containing the LHS of an assignment (see tests/run-with-compiler/quote-var.scala).
if (splice.isType || outer.embedded.isLiftedSymbol(body.symbol)) hole
else Inlined(EmptyTree, Nil, hole).withSpan(splice.span)
} else {
assert(level == 0, "unexpected splice insiced top splice")
val newBody = Inliner.expandMacro(body, splice.span)
transform(newBody)
}

/** Transforms the contents of a nested splice
Expand Down
51 changes: 32 additions & 19 deletions compiler/src/dotty/tools/dotc/typer/Inliner.scala
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,27 @@ object Inliner {
val errors = compileForErrors(tree, false)
packErrors(errors)
}

def expandMacro(body: Tree, span: Span)(implicit ctx: Context) = {
assert(level == 0)
val inlinedFrom = enclosingInlineds.last
val ctx1 = tastyreflect.MacroExpansion.context(inlinedFrom)

val evaluatedSplice = inContext(tastyreflect.MacroExpansion.context(inlinedFrom)) {
Splicer.splice(body, inlinedFrom.sourcePos, MacroClassLoader.fromContext)
}

val inlinedNormailizer = new TreeMap {
override def transform(tree: tpd.Tree)(implicit ctx: Context): tpd.Tree = tree match {
case Inlined(EmptyTree, Nil, expr) if enclosingInlineds.isEmpty => transform(expr)
case _ => super.transform(tree)
}
}
val normalizedSplice = inlinedNormailizer.transform(evaluatedSplice)
if (normalizedSplice.isEmpty) normalizedSplice
else normalizedSplice.withSpan(span)
}

}

/** Produces an inlined version of `call` via its `inlined` method.
Expand Down Expand Up @@ -1233,7 +1254,14 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(using Context) {
case res: Apply if res.symbol == defn.InternalQuoted_exprSplice
&& level == 0
&& !suppressInline =>
expandMacro(res.args.head, tree.span)
val body = res.args.head
checkMacroDependencies(body, call.sourcePos)
if call.symbol.is(Transparent) then
expandMacro(res.args.head, tree.span)
else
// Blackbox macros expanded later in ReifyQuotes
ctx.compilationUnit.needsStaging = true
res
case res => res
}

Expand Down Expand Up @@ -1391,31 +1419,16 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(using Context) {
}
}

private def expandMacro(body: Tree, span: Span)(using Context) = {
private def checkMacroDependencies(body: Tree, callPos: SourcePosition)(implicit ctx: Context): Unit = {
assert(level == 0)
val inlinedFrom = enclosingInlineds.last
val dependencies = macroDependencies(body)

if dependencies.nonEmpty && !ctx.reporter.errorsReported then
for sym <- dependencies do
if ctx.compilationUnit.source.file == sym.associatedFile then
ctx.error(em"Cannot call macro $sym defined in the same source file", call.sourcePos)
ctx.error(em"Cannot call macro $sym defined in the same source file", callPos)
if (ctx.settings.XprintSuspension.value)
ctx.echo(i"suspension triggered by macro call to ${sym.showLocated} in ${sym.associatedFile}", call.sourcePos)
ctx.echo(i"suspension triggered by macro call to ${sym.showLocated} in ${sym.associatedFile}", callPos)
ctx.compilationUnit.suspend() // this throws a SuspendException

val evaluatedSplice = inContext(tastyreflect.MacroExpansion.context(inlinedFrom)) {
Splicer.splice(body, inlinedFrom.sourcePos, MacroClassLoader.fromContext)
}
val inlinedNormailizer = new TreeMap {
override def transform(tree: tpd.Tree)(using Context): tpd.Tree = tree match {
case Inlined(EmptyTree, Nil, expr) if enclosingInlineds.isEmpty => transform(expr)
case _ => super.transform(tree)
}
}
val normalizedSplice = inlinedNormailizer.transform(evaluatedSplice)
if (normalizedSplice.isEmpty) normalizedSplice
else normalizedSplice.withSpan(span)
}

/** Return the set of symbols that are referred at level -1 by the tree and defined in the current run.
Expand Down
3 changes: 3 additions & 0 deletions compiler/src/dotty/tools/dotc/typer/Typer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1917,6 +1917,9 @@ class Typer extends Namer

if (sym.isInlineMethod)
val rhsToInline = PrepareInlineable.wrapRHS(ddef, tpt1, rhs1)
rhsToInline match
case _: Typed =>
case _ => sym.setFlag(Transparent) // FIXME Tag whitebox macros (do it in desugar)
PrepareInlineable.registerInlineInfo(sym, rhsToInline)

if (sym.isConstructor && !sym.isPrimaryConstructor) {
Expand Down
26 changes: 15 additions & 11 deletions tasty/src/dotty/tools/tasty/TastyFormat.scala
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,7 @@ Standard-Section: "ASTs" TopLevelStat*
OPAQUE -- opaque, also used for classes containing opaque aliases
INLINE -- inline
MACRO -- Inline method containing toplevel splices
TRANSPARENT -- Transparent inline method
INLINEPROXY -- Symbol of binding with an argument to an inline method as rhs (TODO: do we still need this?)
STATIC -- Mapped to static Java member
OBJECT -- An object or its class
Expand Down Expand Up @@ -254,7 +255,7 @@ Standard Section: "Comments" Comment*
object TastyFormat {

final val header: Array[Int] = Array(0x5C, 0xA1, 0xAB, 0x1F)
val MajorVersion: Int = 23
val MajorVersion: Int = 24
val MinorVersion: Int = 0

/** Tags used to serialize names, should update [[nameTagToString]] if a new constant is added */
Expand Down Expand Up @@ -351,16 +352,17 @@ object TastyFormat {
final val HASDEFAULT = 31
final val STABLE = 32
final val MACRO = 33
final val ERASED = 34
final val OPAQUE = 35
final val EXTENSION = 36
final val GIVEN = 37
final val PARAMsetter = 38
final val EXPORTED = 39
final val OPEN = 40
final val PARAMEND = 41
final val PARAMalias = 42
final val SUPERTRAIT = 43
final val TRANSPARENT = 34
final val ERASED = 35
final val OPAQUE = 36
final val EXTENSION = 37
final val GIVEN = 38
final val PARAMsetter = 39
final val EXPORTED = 40
final val OPEN = 41
final val PARAMEND = 42
final val PARAMalias = 43
final val SUPERTRAIT = 44

// Cat. 2: tag Nat

Expand Down Expand Up @@ -500,6 +502,7 @@ object TastyFormat {
| INLINE
| INLINEPROXY
| MACRO
| TRANSPARENT
| OPAQUE
| STATIC
| OBJECT
Expand Down Expand Up @@ -561,6 +564,7 @@ object TastyFormat {
case INLINE => "INLINE"
case INLINEPROXY => "INLINEPROXY"
case MACRO => "MACRO"
case TRANSPARENT => "TRANSPARENT"
case OPAQUE => "OPAQUE"
case STATIC => "STATIC"
case OBJECT => "OBJECT"
Expand Down
3 changes: 3 additions & 0 deletions tests/neg-macros/i6530.scala
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
object Macros {
inline def q : Int = ${ '[ Int ] } // error
val x : Int = 1 + q // error

transparent inline def q2: Int = ${ '[ Int ] } // error
val y : Int = 1 + q2 // error
}
4 changes: 2 additions & 2 deletions tests/run-custom-args/Yretain-trees/tasty-definitions-3.check
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
DefDef("foo", Nil, Nil, Inferred(), None)
ValDef("bar", Inferred(), None)
DefDef("foo", Nil, Nil, TypeIdent("Int"), Some(Apply(Select(Literal(Constant(1)), "+"), List(Literal(Constant(2))))))
ValDef("bar", TypeIdent("Int"), Some(Apply(Select(Literal(Constant(2)), "+"), List(Literal(Constant(3))))))
Bind("x", Ident("_"))
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
foo
ValDef("macro", Inferred(), None)
DefDef("main", Nil, List(List(ValDef("args", Applied(TypeIdent("Array"), List(TypeIdent("String"))), None))), TypeIdent("Unit"), Some(Block(Nil, Inlined(Some(Projection(Inferred(), "Macros$")), Nil, Typed(Apply(TypeApply(Ident("exprSplice"), List(Inferred())), List(Block(List(DefDef("$anonfun", Nil, List(List(ValDef("evidence$1", Inferred(), None))), Inferred(), Some(Apply(Apply(TypeApply(Ident("impl"), List(Inferred())), List(Apply(Select(Apply(TypeApply(Ident("exprQuote"), List(Inferred())), List(Inlined(None, Nil, Block(List(DefDef("foo", Nil, Nil, Inferred(), Some(Block(List(DefDef("bar", Nil, Nil, Inferred(), Some(Literal(Constant(1)))), ValDef("bar2", Inferred(), Some(Literal(Constant(2))))), Typed(Ident("bar"), Inferred())))), ValDef("foo2", Inferred(), Some(Block(List(DefDef("baz", Nil, Nil, Inferred(), Some(Literal(Constant(3)))), ValDef("baz2", Inferred(), Some(Literal(Constant(4))))), Typed(Ident("baz"), Inferred())))), ClassDef("A", DefDef("<init>", Nil, List(Nil), Inferred(), None), List(Apply(Select(New(Inferred()), "<init>"), Nil)), Nil, None, List(TypeDef("B", TypeIdent("Int")), DefDef("b", Nil, Nil, Inferred(), Some(Literal(Constant(5)))), ValDef("b2", Inferred(), Some(Literal(Constant(6))))))), Literal(Constant(())))))), "apply"), List(Ident("evidence$1"))))), List(Ident("evidence$1")))))), Closure(Ident("$anonfun"), None)))), TypeIdent("Unit"))))))

bar
DefDef("foo", Nil, Nil, Inferred(), None)
Expand All @@ -8,7 +8,7 @@ bar2
DefDef("foo", Nil, Nil, Inferred(), None)

foo2
ValDef("macro", Inferred(), None)
DefDef("main", Nil, List(List(ValDef("args", Applied(TypeIdent("Array"), List(TypeIdent("String"))), None))), TypeIdent("Unit"), Some(Block(Nil, Inlined(Some(Projection(Inferred(), "Macros$")), Nil, Typed(Apply(TypeApply(Ident("exprSplice"), List(Inferred())), List(Block(List(DefDef("$anonfun", Nil, List(List(ValDef("evidence$1", Inferred(), None))), Inferred(), Some(Apply(Apply(TypeApply(Ident("impl"), List(Inferred())), List(Apply(Select(Apply(TypeApply(Ident("exprQuote"), List(Inferred())), List(Inlined(None, Nil, Block(List(DefDef("foo", Nil, Nil, Inferred(), Some(Block(List(DefDef("bar", Nil, Nil, Inferred(), Some(Literal(Constant(1)))), ValDef("bar2", Inferred(), Some(Literal(Constant(2))))), Typed(Ident("bar"), Inferred())))), ValDef("foo2", Inferred(), Some(Block(List(DefDef("baz", Nil, Nil, Inferred(), Some(Literal(Constant(3)))), ValDef("baz2", Inferred(), Some(Literal(Constant(4))))), Typed(Ident("baz"), Inferred())))), ClassDef("A", DefDef("<init>", Nil, List(Nil), Inferred(), None), List(Apply(Select(New(Inferred()), "<init>"), Nil)), Nil, None, List(TypeDef("B", TypeIdent("Int")), DefDef("b", Nil, Nil, Inferred(), Some(Literal(Constant(5)))), ValDef("b2", Inferred(), Some(Literal(Constant(6))))))), Literal(Constant(())))))), "apply"), List(Ident("evidence$1"))))), List(Ident("evidence$1")))))), Closure(Ident("$anonfun"), None)))), TypeIdent("Unit"))))))

baz
ValDef("foo2", Inferred(), None)
Expand Down
4 changes: 2 additions & 2 deletions tests/run-custom-args/Yretain-trees/tasty-load-tree-2.check
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
DefDef("foo", Nil, Nil, Inferred(), None)
ValDef("bar", Inferred(), None)
DefDef("foo", Nil, Nil, TypeIdent("Int"), Some(Apply(Select(Literal(Constant(1)), "+"), List(Literal(Constant(2))))))
ValDef("bar", TypeIdent("Int"), Some(Apply(Select(Literal(Constant(2)), "+"), List(Literal(Constant(3))))))
11 changes: 11 additions & 0 deletions tests/run-macros/box/Macro_1.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import scala.quoted._

object Macros {

inline def blackbox: Int = ${one}

transparent inline def whitebox: Int = ${one}

private def one(using QuoteContext): Expr[Int] = Expr(1)

}
12 changes: 12 additions & 0 deletions tests/run-macros/box/Test_2.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import scala.quoted._
import Macros._

object Test {
def main(args: Array[String]): Unit = {
val a: Int = blackbox
val b: 1 = whitebox

assert(a == 1)
assert(b == 1)
}
}
4 changes: 2 additions & 2 deletions tests/run-macros/reflect-inline/assert_1.scala
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import scala.quoted._

object api {
inline def (inline x: String) stripMargin: String =
transparent inline def (inline x: String) stripMargin: String =
${ stripImpl('x) }

private def stripImpl(x: Expr[String])(using qctx: QuoteContext): Expr[String] =
Expr(augmentString(x.unliftOrError).stripMargin)

inline def typeChecks(inline x: String): Boolean =
transparent inline def typeChecks(inline x: String): Boolean =
${ typeChecksImpl('{scala.compiletime.testing.typeChecks(x)}) }

private def typeChecksImpl(b: Expr[Boolean])(using qctx: QuoteContext): Expr[Boolean] = {
Expand Down