Skip to content

Commit 976f4c8

Browse files
authored
Merge pull request #8811 from dotty-staging/scala2-macro-compat-core
Add experimental Scala 2 macro compat
2 parents 9f49639 + 56a38a0 commit 976f4c8

File tree

19 files changed

+140
-24
lines changed

19 files changed

+140
-24
lines changed

compiler/src/dotty/tools/dotc/ast/Desugar.scala

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -237,8 +237,10 @@ object desugar {
237237
cpy.TypeDef(tparam)(rhs = desugarContextBounds(tparam.rhs))
238238
}
239239

240-
val meth1 = addEvidenceParams(
241-
cpy.DefDef(meth)(name = methName, tparams = tparams1), epbuf.toList)
240+
val meth1 =
241+
rhs match
242+
case MacroTree(call) => cpy.DefDef(meth)(rhs = call).withMods(mods | Macro | Erased)
243+
case _ => addEvidenceParams(cpy.DefDef(meth)(name = methName, tparams = tparams1), epbuf.toList)
242244

243245
/** The longest prefix of parameter lists in vparamss whose total length does not exceed `n` */
244246
def takeUpTo(vparamss: List[List[ValDef]], n: Int): List[List[ValDef]] = vparamss match {

compiler/src/dotty/tools/dotc/ast/untpd.scala

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
112112
case class ContextBounds(bounds: TypeBoundsTree, cxBounds: List[Tree])(implicit @constructorOnly src: SourceFile) extends TypTree
113113
case class PatDef(mods: Modifiers, pats: List[Tree], tpt: Tree, rhs: Tree)(implicit @constructorOnly src: SourceFile) extends DefTree
114114
case class Export(expr: Tree, selectors: List[ImportSelector])(implicit @constructorOnly src: SourceFile) extends Tree
115+
case class MacroTree(expr: Tree)(implicit @constructorOnly src: SourceFile) extends Tree
115116

116117
case class ImportSelector(imported: Ident, renamed: Tree = EmptyTree, bound: Tree = EmptyTree)(implicit @constructorOnly src: SourceFile) extends Tree {
117118
// TODO: Make bound a typed tree?
@@ -623,6 +624,10 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
623624
case tree: TypedSplice if splice `eq` tree.splice => tree
624625
case _ => finalize(tree, untpd.TypedSplice(splice)(ctx))
625626
}
627+
def MacroTree(tree: Tree)(expr: Tree)(implicit ctx: Context): Tree = tree match {
628+
case tree: MacroTree if expr `eq` tree.expr => tree
629+
case _ => finalize(tree, untpd.MacroTree(expr)(tree.source))
630+
}
626631
}
627632

628633
abstract class UntypedTreeMap(cpy: UntypedTreeCopier = untpd.cpy) extends TreeMap(cpy) {
@@ -677,6 +682,8 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
677682
cpy.ImportSelector(tree)(transformSub(imported), transform(renamed), transform(bound))
678683
case Number(_, _) | TypedSplice(_) =>
679684
tree
685+
case MacroTree(expr) =>
686+
cpy.MacroTree(tree)(transform(expr))
680687
case _ =>
681688
super.transformMoreCases(tree)
682689
}
@@ -736,6 +743,8 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
736743
x
737744
case TypedSplice(splice) =>
738745
this(x, splice)
746+
case MacroTree(expr) =>
747+
this(x, expr)
739748
case _ =>
740749
super.foldMoreCases(x, tree)
741750
}

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

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1000,7 +1000,12 @@ object SymDenotations {
10001000
is(Method, butNot = Accessor) && isRetainedInline
10011001

10021002
/** Is this a Scala 2 macro */
1003-
final def isScala2Macro(implicit ctx: Context): Boolean = is(Macro) && symbol.owner.is(Scala2x)
1003+
final def isScala2Macro(implicit ctx: Context): Boolean =
1004+
isScala2MacroInScala3 || (is(Macro) && symbol.owner.is(Scala2x))
1005+
1006+
/** Is this a Scala 2 macro defined */
1007+
final def isScala2MacroInScala3(implicit ctx: Context): Boolean =
1008+
is(Macro, butNot = Inline) && is(Erased)
10041009

10051010
/** An erased value or an erased inline method or field */
10061011
def isEffectivelyErased(implicit ctx: Context): Boolean =

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -580,8 +580,9 @@ class TreeUnpickler(reader: TastyReader,
580580
}
581581
sym.annotations = annotFns.map(_(sym))
582582
if sym.isOpaqueAlias then sym.setFlag(Deferred)
583+
val isScala2MacroDefinedInScala3 = flags.is(Macro, butNot = Inline) && flags.is(Erased)
583584
ctx.owner match {
584-
case cls: ClassSymbol => cls.enter(sym)
585+
case cls: ClassSymbol if !isScala2MacroDefinedInScala3 => cls.enter(sym)
585586
case _ =>
586587
}
587588
registerSym(start, sym)

compiler/src/dotty/tools/dotc/parsing/Parsers.scala

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2234,15 +2234,7 @@ object Parsers {
22342234
newExpr()
22352235
case MACRO =>
22362236
val start = in.skipToken()
2237-
val call = ident()
2238-
// The standard library uses "macro ???" to denote "fast track" macros
2239-
// hardcoded in the compiler, don't issue an error for those macros
2240-
// since we want to be able to compile the standard library.
2241-
if (call `ne` nme.???)
2242-
syntaxError(
2243-
"Scala 2 macros are not supported, see https://dotty.epfl.ch/docs/reference/dropped-features/macros.html",
2244-
start)
2245-
unimplementedExpr
2237+
MacroTree(simpleExpr())
22462238
case COLONEOL =>
22472239
syntaxError("':' not allowed here")
22482240
in.nextToken()

compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -632,6 +632,8 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
632632
toText(tree.app) ~ Str("(with integrated type args)").provided(printDebug)
633633
case Thicket(trees) =>
634634
"Thicket {" ~~ toTextGlobal(trees, "\n") ~~ "}"
635+
case MacroTree(call) =>
636+
keywordStr("macro ") ~ toTextGlobal(call)
635637
case _ =>
636638
tree.fallbackToText(this)
637639
}
@@ -786,7 +788,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
786788

787789
addVparamssText(prefix ~ tparamsText(tree.tparams), vparamss) ~
788790
optAscription(tree.tpt) ~
789-
optText(tree.rhs)(" = " ~ _)
791+
optText(tree.rhs)(" = " ~ keywordText("macro ").provided(tree.symbol.isScala2Macro) ~ _)
790792
}
791793
}
792794
}

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,10 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase
146146
tree match
147147
case tree: ValOrDefDef if !tree.symbol.is(Synthetic) =>
148148
checkInferredWellFormed(tree.tpt)
149+
val sym = tree.symbol
150+
if sym.isScala2Macro && !ctx.settings.XignoreScala2Macros.value then
151+
if !sym.owner.unforcedDecls.exists(p => !p.isScala2Macro && p.name == sym.name && p.signature == sym.signature) then
152+
ctx.error("No Scala 3 implementation found for this Scala 2 macro.", tree.sourcePos)
149153
case _ =>
150154
processMemberDef(tree)
151155

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -435,7 +435,8 @@ class Namer { typer: Typer =>
435435
* package members are not entered twice in the same run.
436436
*/
437437
def enterSymbol(sym: Symbol)(using Context): Symbol = {
438-
if (sym.exists) {
438+
// We do not enter Scala 2 macros defined in Scala 3 as they have an equivalent Scala 3 inline method.
439+
if (sym.exists && !sym.isScala2MacroInScala3) {
439440
typr.println(s"entered: $sym in ${ctx.owner}")
440441
ctx.enter(sym)
441442
}

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

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1863,10 +1863,12 @@ class Typer extends Namer
18631863
}
18641864

18651865
if (sym.isInlineMethod) rhsCtx.addMode(Mode.InlineableBody)
1866-
val rhs1 = typedExpr(ddef.rhs, tpt1.tpe.widenExpr)(using rhsCtx)
1867-
val rhsToInline = PrepareInlineable.wrapRHS(ddef, tpt1, rhs1)
1866+
val rhs1 =
1867+
if sym.isScala2Macro then typedScala2MacroBody(ddef.rhs)(using rhsCtx)
1868+
else typedExpr(ddef.rhs, tpt1.tpe.widenExpr)(using rhsCtx)
18681869

18691870
if (sym.isInlineMethod)
1871+
val rhsToInline = PrepareInlineable.wrapRHS(ddef, tpt1, rhs1)
18701872
PrepareInlineable.registerInlineInfo(sym, rhsToInline)
18711873

18721874
if (sym.isConstructor && !sym.isPrimaryConstructor) {
@@ -3440,4 +3442,19 @@ class Typer extends Namer
34403442
if (!tree.tpe.isErroneous && !ctx.isAfterTyper && isPureExpr(tree) &&
34413443
!tree.tpe.isRef(defn.UnitClass) && !isSelfOrSuperConstrCall(tree))
34423444
ctx.warning(PureExpressionInStatementPosition(original, exprOwner), original.sourcePos)
3445+
3446+
/** Types the body Scala 2 macro declaration `def f = macro <body>` */
3447+
private def typedScala2MacroBody(call: untpd.Tree)(using Context): Tree =
3448+
// TODO check that call is to a method with valid signature
3449+
call match
3450+
case rhs0: untpd.Ident =>
3451+
typedIdent(rhs0, defn.AnyType)
3452+
case rhs0: untpd.Select =>
3453+
typedSelect(rhs0, defn.AnyType)
3454+
case rhs0: untpd.TypeApply =>
3455+
typedTypeApply(rhs0, defn.AnyType)
3456+
case _ =>
3457+
ctx.error("Invalid Scala 2 macro", call.sourcePos)
3458+
EmptyTree
3459+
34433460
}

0 commit comments

Comments
 (0)