Skip to content

Commit 4da92f1

Browse files
committed
Expand non-transparent macros after Pickling
1 parent d2a160e commit 4da92f1

File tree

20 files changed

+320
-57
lines changed

20 files changed

+320
-57
lines changed

compiler/src/dotty/tools/dotc/Compiler.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ class Compiler {
5050
/** Phases dealing with TASTY tree pickling and unpickling */
5151
protected def picklerPhases: List[List[Phase]] =
5252
List(new Pickler) :: // Generate TASTY info
53+
List(new Macros) :: // Evaluate non-transparent macros
5354
List(new ReifyQuotes) :: // Turn quoted trees into explicit run-time data structures
5455
Nil
5556

compiler/src/dotty/tools/dotc/config/Printers.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ object Printers {
3232
val inlining = noPrinter
3333
val interactiv = noPrinter
3434
val nullables = noPrinter
35+
val macros = noPrinter
3536
val overload = noPrinter
3637
val patmatch = noPrinter
3738
val pickling = noPrinter

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)
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
package dotty.tools.dotc
2+
package transform
3+
4+
import core._
5+
import Decorators._
6+
import Flags._
7+
import Types._
8+
import Contexts._
9+
import Symbols._
10+
import Constants._
11+
import ast.Trees._
12+
import ast.{TreeTypeMap, untpd}
13+
import util.Spans._
14+
import tasty.TreePickler.Hole
15+
import SymUtils._
16+
import NameKinds._
17+
import dotty.tools.dotc.ast.tpd
18+
import typer.Implicits.SearchFailureType
19+
20+
import scala.collection.mutable
21+
import dotty.tools.dotc.config.Printers
22+
import dotty.tools.dotc.core.Annotations._
23+
import dotty.tools.dotc.core.Names._
24+
import dotty.tools.dotc.core.StdNames._
25+
import dotty.tools.dotc.quoted._
26+
import dotty.tools.dotc.transform.TreeMapWithStages._
27+
import dotty.tools.dotc.typer.Inliner
28+
29+
import scala.annotation.constructorOnly
30+
31+
32+
object Macros:
33+
val name: String = "macros"
34+
35+
/** TODO */
36+
class Macros extends MacroTransform:
37+
import tpd._
38+
39+
override def phaseName: String = Macros.name
40+
41+
override def allowsImplicitSearch: Boolean = true
42+
43+
override def checkPostCondition(tree: Tree)(using Context): Unit =
44+
tree match
45+
case tree: PackageDef =>
46+
new MacroExpander(freshStagingContext) {
47+
override def transform(tree: Tree)(using Context): Tree = {
48+
tree match
49+
case _: Select | _: GenericApply[_] if tree.symbol.is(Inline) =>
50+
assert(!tree.symbol.is(Inline),
51+
s"Inline method call was not expanded:\n ${tree.show}\n $tree")
52+
case _ =>
53+
super.transform(tree)
54+
}
55+
override def transformSplice(body: Tree, splice: Apply)(using Context): Tree = {
56+
assert(StagingContext.level != 0,
57+
s"Macro was not expanded:\n ${splice.show}\n $splice")
58+
super.transformSplice(body, splice)
59+
}
60+
}.transform(tree)
61+
case _ =>
62+
end checkPostCondition
63+
64+
override def run(using Context): Unit =
65+
if (ctx.compilationUnit.needsStaging) super.run(using freshStagingContext)
66+
67+
protected def newTransformer(using Context): Transformer =
68+
new Transformer:
69+
override def transform(tree: tpd.Tree)(using Context): tpd.Tree =
70+
new MacroExpander(ctx).transform(tree)
71+
end new
72+
73+
private class MacroExpander(@constructorOnly ictx: Context) extends TreeMapWithStages(ictx):
74+
import StagingContext._
75+
76+
override def transform(tree: Tree)(using Context): Tree =
77+
if Inliner.inInlineMethod then tree
78+
else tree match
79+
case _: Select | _: GenericApply[_] if tree.symbol.is(Inline) =>
80+
Printers.macros.println(i"Inlining: $tree")
81+
val inlined = Inliner.inlineCall(tree)
82+
Printers.macros.println(i"Inlined: $inlined")
83+
transform(inlined)
84+
case _ =>
85+
super.transform(tree)
86+
87+
protected override def transformQuotation(body: Tree, quote: Tree)(using Context): Tree =
88+
val body1 = transform(body)(using quoteContext)
89+
quote match
90+
case quote: Apply => cpy.Apply(quote)(quote.fun, body1 :: Nil)
91+
case quote: TypeApply => cpy.TypeApply(quote)(quote.fun, body1 :: Nil)
92+
93+
protected def transformSplice(body: Tree, splice: Apply)(using Context): Tree =
94+
val body1 = transform(body)(using spliceContext)
95+
if level == 0 then
96+
Printers.macros.println(i"Expanding macro: $body1")
97+
val expanded = Inliner.expandMacro(body1, splice.span)
98+
Printers.macros.println(i"Expanded macro: $expanded")
99+
transform(expanded) // inline methods generated by macros
100+
else splice.fun match
101+
case fun @ TypeApply(_, _) => cpy.Apply(splice)(fun, body1 :: Nil)
102+
case fun @ Apply(_, _) => cpy.Apply(splice)(fun, body1 :: Nil)
103+
104+
protected def transformSpliceType(body: Tree, splice: Select)(using Context): Tree =
105+
// Type splices cannot contain macros or inline calls.
106+
// Therfore it does not need to be transformed.
107+
splice
108+
109+
end MacroExpander
110+
111+
end Macros

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: 4 additions & 1 deletion
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-
}
4+
5+
transparent inline def q2: Int = ${ '[ Int ] } // error
6+
val y : Int = 1 + q2
7+
}

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

0 commit comments

Comments
 (0)