Skip to content

Commit af0ed15

Browse files
committed
Expand non-transparent macros after Typer
1 parent d2a160e commit af0ed15

File tree

17 files changed

+216
-60
lines changed

17 files changed

+216
-60
lines changed

compiler/src/dotty/tools/dotc/core/Flags.scala

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -350,6 +350,8 @@ object Flags {
350350
/** An opaque type alias or a class containing one */
351351
val (Opaque @ _, _, _) = newFlags(43, "opaque")
352352

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

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

@@ -421,7 +423,7 @@ object Flags {
421423
CommonSourceModifierFlags.toTypeFlags | Abstract | Sealed | Opaque | Open
422424

423425
val TermSourceModifierFlags: FlagSet =
424-
CommonSourceModifierFlags.toTermFlags | Inline | AbsOverride | Lazy | Erased
426+
CommonSourceModifierFlags.toTermFlags | Inline | AbsOverride | Lazy | Erased | Transparent
425427

426428
/** Flags representing modifiers that can appear in trees */
427429
val ModifierFlags: FlagSet =
@@ -438,7 +440,7 @@ object Flags {
438440
Scala2SpecialFlags, MutableOrOpen, Opaque, Touched, JavaStatic,
439441
OuterOrCovariant, LabelOrContravariant, CaseAccessor,
440442
Extension, NonMember, Implicit, Given, Permanent, Synthetic,
441-
SuperParamAliasOrScala2x, Inline, Macro)
443+
SuperParamAliasOrScala2x, Inline, Macro, Transparent)
442444

443445
/** Flags that are not (re)set when completing the denotation, or, if symbol is
444446
* a top-level class or object, when completing the denotation once the class

compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -723,6 +723,7 @@ class TreePickler(pickler: TastyPickler) {
723723
if (flags.is(ParamAccessor)) writeModTag(PARAMsetter)
724724
if (flags.is(SuperParamAlias)) writeModTag(PARAMalias)
725725
if (flags.is(Exported)) writeModTag(EXPORTED)
726+
if (flags.is(Transparent)) writeModTag(TRANSPARENT)
726727
assert(!(flags.is(Label)))
727728
}
728729
else {

compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -644,6 +644,7 @@ class TreeUnpickler(reader: TastyReader,
644644
case INLINE => addFlag(Inline)
645645
case INLINEPROXY => addFlag(InlineProxy)
646646
case MACRO => addFlag(Macro)
647+
case TRANSPARENT => addFlag(Transparent)
647648
case OPAQUE => addFlag(Opaque)
648649
case STATIC => addFlag(JavaStatic)
649650
case OBJECT => addFlag(Module)

compiler/src/dotty/tools/dotc/transform/ReifyQuotes.scala

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -186,12 +186,10 @@ class ReifyQuotes extends MacroTransform {
186186
* are in the body of an inline method.
187187
*/
188188
protected def transformSplice(body: Tree, splice: Apply)(using Context): Tree =
189-
if (level > 1) {
189+
if level > 1 then
190190
val body1 = nested(isQuote = false).transform(body)(using spliceContext)
191191
cpy.Apply(splice)(splice.fun, body1 :: Nil)
192-
}
193-
else {
194-
assert(level == 1, "unexpected top splice outside quote")
192+
else if level == 1 then
195193
val (body1, quotes) = nested(isQuote = false).splitSplice(body)(using spliceContext)
196194
val tpe = outer.embedded.getHoleType(body, splice)
197195
val hole = makeHole(splice.isTerm, body1, quotes, tpe).withSpan(splice.span)
@@ -201,7 +199,11 @@ class ReifyQuotes extends MacroTransform {
201199
// For example we can have a lifted tree containing the LHS of an assignment (see tests/run-with-compiler/quote-var.scala).
202200
if (outer.embedded.isLiftedSymbol(body.symbol)) hole
203201
else Inlined(EmptyTree, Nil, hole).withSpan(splice.span)
204-
}
202+
else
203+
assert(level == 0, "unexpected splice inside top splice")
204+
val newBody = Inliner.expandMacro(body, splice.span)
205+
transform(newBody)
206+
end if
205207

206208
/** If inside a quote, split the body of the splice into a core and a list of embedded quotes
207209
* and make a hole from these parts. Otherwise issue an error, unless we
@@ -386,6 +388,10 @@ class ReifyQuotes extends MacroTransform {
386388
}
387389
tree.symbol.annotations = newAnnotations
388390
super.transform(tree)
391+
case _: Select | _: GenericApply[_] if tree.symbol.is(Inline) =>
392+
// Inline calls to inline definitions that where generated by a macro
393+
val inlined = Inliner.inlineCall(tree)
394+
transform(inlined)
389395
case _ =>
390396
super.transform(tree)
391397
}

compiler/src/dotty/tools/dotc/typer/Inliner.scala

Lines changed: 32 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -331,6 +331,24 @@ object Inliner {
331331
val errors = compileForErrors(tree, false)
332332
packErrors(errors)
333333
}
334+
335+
def expandMacro(body: Tree, span: Span)(implicit ctx: Context) = {
336+
assert(level == 0)
337+
val inlinedFrom = enclosingInlineds.last
338+
val evaluatedSplice = inContext(dotc.quoted.MacroExpansion.context(inlinedFrom)) {
339+
Splicer.splice(body, inlinedFrom.sourcePos, MacroClassLoader.fromContext)
340+
}
341+
342+
val inlinedNormailizer = new TreeMap {
343+
override def transform(tree: tpd.Tree)(implicit ctx: Context): tpd.Tree = tree match {
344+
case Inlined(EmptyTree, Nil, expr) if enclosingInlineds.isEmpty => transform(expr)
345+
case _ => super.transform(tree)
346+
}
347+
}
348+
val normalizedSplice = inlinedNormailizer.transform(evaluatedSplice)
349+
if (normalizedSplice.isEmpty) normalizedSplice
350+
else normalizedSplice.withSpan(span)
351+
}
334352
}
335353

336354
/** Produces an inlined version of `call` via its `inlined` method.
@@ -1254,14 +1272,21 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(using Context) {
12541272
super.typedValDef(vdef1, sym)
12551273

12561274
override def typedApply(tree: untpd.Apply, pt: Type)(using Context): Tree =
1257-
constToLiteral(betaReduce(super.typedApply(tree, pt))) match {
1275+
constToLiteral(betaReduce(super.typedApply(tree, pt))) match
12581276
case res: Apply if res.symbol == defn.InternalQuoted_exprSplice
12591277
&& level == 0
12601278
&& !suppressInline =>
1261-
val expanded = expandMacro(res.args.head, tree.span)
1262-
typedExpr(expanded) // Inline calls and constant fold code generated by the macro
1279+
val body = res.args.head
1280+
checkMacroDependencies(body, call.sourcePos)
1281+
if call.symbol.is(Transparent) then
1282+
val expanded = expandMacro(res.args.head, tree.span)
1283+
typedExpr(expanded) // Inline calls and constant fold code generated by the macro
1284+
else
1285+
// Blackbox macros expanded later in ReifyQuotes
1286+
ctx.compilationUnit.needsStaging = true
1287+
res
12631288
case res => res
1264-
}
1289+
end match
12651290

12661291
override def typedMatchFinish(tree: untpd.Match, sel: Tree, wideSelType: Type, cases: List[untpd.CaseDef], pt: Type)(using Context) =
12671292
if (!tree.isInline || ctx.owner.isInlineMethod) // don't reduce match of nested inline method yet
@@ -1417,32 +1442,19 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(using Context) {
14171442
}
14181443
}
14191444

1420-
private def expandMacro(body: Tree, span: Span)(using Context) = {
1445+
private def checkMacroDependencies(body: Tree, callPos: SrcPos)(implicit ctx: Context): Unit = {
14211446
assert(level == 0)
14221447
val inlinedFrom = enclosingInlineds.last
14231448
val dependencies = macroDependencies(body)
14241449
val suspendable = ctx.compilationUnit.isSuspendable
14251450
if dependencies.nonEmpty && !ctx.reporter.errorsReported then
14261451
for sym <- dependencies do
14271452
if ctx.compilationUnit.source.file == sym.associatedFile then
1428-
report.error(em"Cannot call macro $sym defined in the same source file", call.srcPos)
1453+
report.error(em"Cannot call macro $sym defined in the same source file", callPos)
14291454
if (suspendable && ctx.settings.XprintSuspension.value)
1430-
report.echo(i"suspension triggered by macro call to ${sym.showLocated} in ${sym.associatedFile}", call.srcPos)
1455+
report.echo(i"suspension triggered by macro call to ${sym.showLocated} in ${sym.associatedFile}", callPos)
14311456
if suspendable then
14321457
ctx.compilationUnit.suspend() // this throws a SuspendException
1433-
1434-
val evaluatedSplice = inContext(quoted.MacroExpansion.context(inlinedFrom)) {
1435-
Splicer.splice(body, inlinedFrom.srcPos, MacroClassLoader.fromContext)
1436-
}
1437-
val inlinedNormailizer = new TreeMap {
1438-
override def transform(tree: tpd.Tree)(using Context): tpd.Tree = tree match {
1439-
case Inlined(EmptyTree, Nil, expr) if enclosingInlineds.isEmpty => transform(expr)
1440-
case _ => super.transform(tree)
1441-
}
1442-
}
1443-
val normalizedSplice = inlinedNormailizer.transform(evaluatedSplice)
1444-
if (normalizedSplice.isEmpty) normalizedSplice
1445-
else normalizedSplice.withSpan(span)
14461458
}
14471459

14481460
/** Return the set of symbols that are referred at level -1 by the tree and defined in the current run.

compiler/src/dotty/tools/dotc/typer/Typer.scala

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1983,6 +1983,9 @@ class Typer extends Namer
19831983

19841984
if sym.isInlineMethod then
19851985
val rhsToInline = PrepareInlineable.wrapRHS(ddef, tpt1, rhs1)
1986+
rhsToInline match
1987+
case _: Typed =>
1988+
case _ => sym.setFlag(Transparent) // FIXME Tag whitebox macros (do it in desugar)
19861989
PrepareInlineable.registerInlineInfo(sym, rhsToInline)
19871990

19881991
if (sym.isConstructor && !sym.isPrimaryConstructor) {

tasty/src/dotty/tools/tasty/TastyFormat.scala

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -253,7 +253,7 @@ Standard Section: "Comments" Comment*
253253
object TastyFormat {
254254

255255
final val header: Array[Int] = Array(0x5C, 0xA1, 0xAB, 0x1F)
256-
val MajorVersion: Int = 24
256+
val MajorVersion: Int = 25
257257
val MinorVersion: Int = 0
258258

259259
/** Tags used to serialize names, should update [[nameTagToString]] if a new constant is added */
@@ -350,16 +350,17 @@ object TastyFormat {
350350
final val HASDEFAULT = 31
351351
final val STABLE = 32
352352
final val MACRO = 33
353-
final val ERASED = 34
354-
final val OPAQUE = 35
355-
final val EXTENSION = 36
356-
final val GIVEN = 37
357-
final val PARAMsetter = 38
358-
final val EXPORTED = 39
359-
final val OPEN = 40
360-
final val PARAMEND = 41
361-
final val PARAMalias = 42
362-
final val SUPERTRAIT = 43
353+
final val TRANSPARENT = 34
354+
final val ERASED = 35
355+
final val OPAQUE = 36
356+
final val EXTENSION = 37
357+
final val GIVEN = 38
358+
final val PARAMsetter = 39
359+
final val EXPORTED = 40
360+
final val OPEN = 41
361+
final val PARAMEND = 42
362+
final val PARAMalias = 43
363+
final val SUPERTRAIT = 44
363364

364365
// Cat. 2: tag Nat
365366

@@ -498,6 +499,7 @@ object TastyFormat {
498499
| INLINE
499500
| INLINEPROXY
500501
| MACRO
502+
| TRANSPARENT
501503
| OPAQUE
502504
| STATIC
503505
| OBJECT
@@ -559,6 +561,7 @@ object TastyFormat {
559561
case INLINE => "INLINE"
560562
case INLINEPROXY => "INLINEPROXY"
561563
case MACRO => "MACRO"
564+
case TRANSPARENT => "TRANSPARENT"
562565
case OPAQUE => "OPAQUE"
563566
case STATIC => "STATIC"
564567
case OBJECT => "OBJECT"

tests/neg-macros/i6530.scala

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
object Macros {
22
inline def q : Int = ${ '[ Int ] } // error
33
val x : Int = 1 + q
4+
5+
transparent inline def q2: Int = ${ '[ Int ] } // error
6+
val y : Int = 1 + q2 // error
47
}

tests/neg-macros/i9014.check

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,5 @@
22
-- Error: tests/neg-macros/i9014/Test_2.scala:1:23 ---------------------------------------------------------------------
33
1 |val tests = summon[Bar] // error
44
| ^
5-
| no implicit argument of type Bar was found for parameter x of method summon in object DottyPredef.
6-
| I found:
7-
|
8-
| given_Bar
9-
|
10-
| But method given_Bar does not match type Bar.
5+
| Failed to expand!
6+
| This location contains code that was inlined from Test_2.scala:1

tests/pos-macros/i8764/Macro_1.scala

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
package izumi.reflect.dottyreflection
2+
3+
import scala.quoted.{Expr, QuoteContext}
4+
5+
object Inspect {
6+
inline def inspect[T <: AnyKind]: String = ${ inspectAny[T] }
7+
8+
def inspectAny[T <: AnyKind : quoted.Type](using qctx0: QuoteContext): Expr[String] = {
9+
val ref = new Inspector().buildTypeRef[T]
10+
// println(s"result: $ref")
11+
Expr(ref.toString)
12+
}
13+
}
14+
15+
final case class NameReference(ref: String, prefix: Option[NameReference]) {
16+
override def toString: String = prefix.fold("")(p => s"$p::") + ref
17+
}
18+
19+
class Inspector(implicit qctx: QuoteContext) {
20+
import qctx.tasty._
21+
22+
protected def log(s: String) = {
23+
// println(" -> " + s)
24+
}
25+
26+
def buildTypeRef[T <: AnyKind](using tpe: quoted.Type[T]): NameReference = {
27+
val uns = tpe.unseal
28+
log(s" -------- about to inspect ${tpe} --------")
29+
val v = inspectTree(uns)
30+
log(s" -------- done inspecting ${tpe} --------")
31+
v
32+
}
33+
34+
private def inspectTree(tpeTree: TypeTree): NameReference = {
35+
log(s"INSPECT: $tpeTree: ${tpeTree.getClass}")
36+
if (tpeTree.symbol.isNoSymbol) {
37+
inspectType(tpeTree.tpe)
38+
} else {
39+
inspectSymbol(tpeTree.symbol)
40+
}
41+
}
42+
43+
private def inspectType(tpe: Type): NameReference = {
44+
log(s"INSPECT: type `$tpe`")
45+
tpe match {
46+
case r: TypeRef =>
47+
inspectSymbol(r.typeSymbol)
48+
49+
case a: AnnotatedType =>
50+
inspectType(a.underlying)
51+
52+
case lazyref if lazyref.getClass.getName.contains("LazyRef") => // upstream bug seems like
53+
log(s"LazyRef occured $lazyref")
54+
throw new RuntimeException(s"LazyRef occured $lazyref")
55+
56+
case o =>
57+
log(s"Type, UNSUPPORTED: ${o.getClass} - $o")
58+
throw new RuntimeException(s"Type, UNSUPPORTED: ${o.getClass} - $o")
59+
}
60+
}
61+
62+
private def inspectSymbol(symbol: Symbol): NameReference = {
63+
log(s"INSPECT: symbol `$symbol`")
64+
symbol.tree match {
65+
case c: ClassDef =>
66+
symbolToNameReference(symbol)
67+
68+
case t: TypeDef =>
69+
inspectTree(t.rhs.asInstanceOf[TypeTree])
70+
71+
case v: ValDef =>
72+
inspectTree(v.tpt)
73+
74+
case o =>
75+
log(s"SYMBOL TREE, UNSUPPORTED: $o")
76+
throw new RuntimeException(s"SYMBOL TREE, UNSUPPORTED: ${o.getClass} - $o")
77+
}
78+
}
79+
80+
private def symbolToNameReference(sym: Symbol): NameReference = {
81+
val prefix = if (sym.maybeOwner.isNoSymbol) {
82+
None
83+
} else {
84+
if sym.owner.isPackageDef then None
85+
else Some(inspectSymbol(sym.maybeOwner))
86+
}
87+
NameReference(sym.fullName, prefix)
88+
}
89+
90+
}

tests/pos-macros/i8764/Macro_2.scala

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package test
2+
3+
import izumi.reflect.dottyreflection.Inspect.inspect
4+
5+
object Test1 {
6+
7+
class Foo
8+
println(inspect[Foo])
9+
10+
def test1(): Unit =
11+
inspect[Foo]
12+
13+
def test2() =
14+
inspect[Foo]
15+
16+
}
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
DefDef("foo", Nil, Nil, Inferred(), None)
2-
ValDef("bar", Inferred(), None)
1+
DefDef("foo", Nil, Nil, TypeIdent("Int"), Some(Apply(Select(Literal(Constant.Int(1)), "+"), List(Literal(Constant.Int(2))))))
2+
ValDef("bar", TypeIdent("Int"), Some(Apply(Select(Literal(Constant.Int(2)), "+"), List(Literal(Constant.Int(3))))))

tests/run-macros/box/Macro_1.scala

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import scala.quoted._
2+
3+
object Macros {
4+
5+
inline def blackbox: Int = ${one}
6+
7+
transparent inline def whitebox: Int = ${one}
8+
9+
private def one(using QuoteContext): Expr[Int] = Expr(1)
10+
11+
}

tests/run-macros/box/Test_2.scala

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import scala.quoted._
2+
import Macros._
3+
4+
object Test {
5+
def main(args: Array[String]): Unit = {
6+
val a: Int = blackbox
7+
val b: 1 = whitebox
8+
9+
assert(a == 1)
10+
assert(b == 1)
11+
}
12+
}

0 commit comments

Comments
 (0)