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
}

compiler/test/dotty/tools/dotc/semanticdb/SemanticdbTests.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ class SemanticdbTests:
113113
// "-Xprint:extractSemanticDB",
114114
"-sourceroot", expectSrc.toString,
115115
"-classpath", target.toString,
116+
"-Xignore-scala2-macros",
116117
"-usejavacp"
117118
) ++ inputFiles().map(_.toString)
118119
val exit = Main.process(args)
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
object Macro1 {
2+
import scala.language.experimental.macros
3+
def lineNumber: Int = macro LineNumberMacro2.thisLineNumberImpl // error: No Scala 3 implementation found for this Scala 2 macro.
4+
}
5+
6+
object LineNumberMacro2 {
7+
class Context: // Dummy scala.reflect.macros.Context
8+
type Expr[+T]
9+
10+
def thisLineNumberImpl(context: Context): context.Expr[Int] = {
11+
// val lineNumber = context.enclosingPosition.line
12+
// context.literal(lineNumber)
13+
???
14+
}
15+
}

tests/pos/macro.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
class A {
22
def foo: Int = macro ???
3+
inline def foo: Int = ???
34
}

tests/pos/scala2-macro-compat-1.scala

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
2+
3+
object Macro1 {
4+
import scala.language.experimental.macros
5+
def lineNumber: Int = macro LineNumberMacro2.thisLineNumberImpl
6+
inline def lineNumber: Int = ${ LineNumberMacro3.thisLineNumberExpr }
7+
}
8+
9+
object LineNumberMacro2 {
10+
class Context: // Dummy scala.reflect.macros.Context
11+
type Expr[+T]
12+
13+
def thisLineNumberImpl(context: Context): context.Expr[Int] = {
14+
// val lineNumber = context.enclosingPosition.line
15+
// context.literal(lineNumber)
16+
???
17+
}
18+
}
19+
20+
object LineNumberMacro3 {
21+
import scala.quoted._
22+
def thisLineNumberExpr(using qctx: QuoteContext): Expr[Int] = {
23+
import qctx.tasty.{_, given _}
24+
Expr(rootPosition.startLine + 1)
25+
}
26+
}

tests/pos/scala2-macro-compat-2.scala

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import scala.language.experimental.macros
2+
3+
class Context: // Dummy scala.reflect.macros.Context
4+
type Expr[T]
5+
6+
object Macros {
7+
8+
def foo1(x: Int): Int = macro foo1Impl
9+
def foo1(x: Int): Int = ???
10+
11+
def foo2(x: Int, y: String): Int = macro foo2Impl
12+
def foo2(x: Int, y: String): Int = ???
13+
14+
def foo1Impl(context: Context)(x: context.Expr[Int]): context.Expr[Int] = ???
15+
def foo2Impl(context: Context)(x: context.Expr[Int], y: context.Expr[String]): context.Expr[Int] = ???
16+
17+
}

tests/pos/scala2-macro-compat-3.scala

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
2+
3+
object Macro1 {
4+
import scala.language.experimental.macros
5+
def lineNumber: Int = macro LineNumberMacro2.thisLineNumberImpl
6+
inline def lineNumber: Int = 4
7+
}
8+
9+
object Macro1Consume {
10+
val test = Macro1.lineNumber
11+
}
12+
13+
object LineNumberMacro2 {
14+
class Context: // Dummy scala.reflect.macros.Context
15+
type Expr[+T]
16+
17+
def thisLineNumberImpl(context: Context): context.Expr[Int] = {
18+
// val lineNumber = context.enclosingPosition.line
19+
// context.literal(lineNumber)
20+
???
21+
}
22+
}
23+

tests/semanticdb/expect/Annotations.expect.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ class B/*<-annot::B#*/ @ConstructorAnnotation/*->com::javacp::annot::Constructor
2727
@ObjectAnnotation/*->com::javacp::annot::ObjectAnnotation#*/
2828
object M/*<-annot::M.*/ {
2929
@MacroAnnotation/*->com::javacp::annot::MacroAnnotation#*/
30-
def m/*<-annot::M.m().*/[TT/*<-annot::M.m().[TT]*/]: Int/*->scala::Int#*//*->scala::Predef.`???`().*/ = macro ???
30+
def m/*<-annot::M.m().*/[TT/*<-annot::M.m().[TT]*/]: Int/*->scala::Int#*/ = macro ???/*->scala::Predef.`???`().*/
3131
}
3232

3333
@TraitAnnotation/*->com::javacp::annot::TraitAnnotation#*/

tests/semanticdb/expect/semanticdb-Flags.expect.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ package object p {
66
p/*<-flags::p::package.*/rivate lazy val x/*<-flags::p::package.x.*/ = 1
77
protected implicit var y/*<-flags::p::package.y().*/: Int/*->scala::Int#*/ = 2
88
def z/*<-flags::p::package.z().*/(pp/*<-flags::p::package.z().(pp)*/: Int/*->scala::Int#*/) = 3
9-
def m/*<-flags::p::package.m().*/[TT/*<-flags::p::package.m().[TT]*/]: Int/*->scala::Int#*//*->scala::Predef.`???`().*/ = macro ???
9+
def m/*<-flags::p::package.m().*/[TT/*<-flags::p::package.m().[TT]*/]: Int/*->scala::Int#*/ = macro ???/*->scala::Predef.`???`().*/
1010
abstract class C/*<-flags::p::package.C#*/[+T/*<-flags::p::package.C#[T]*/, -U/*<-flags::p::package.C#[U]*/, V/*<-flags::p::package.C#[V]*/](x/*<-flags::p::package.C#x.*/: T/*->flags::p::package.C#[T]*/, y/*<-flags::p::package.C#y.*/: U/*->flags::p::package.C#[U]*/, z/*<-flags::p::package.C#z.*/: V/*->flags::p::package.C#[V]*/) {
1111
def this()/*<-flags::p::package.C#`<init>`(+1).*/ = this(???/*->scala::Predef.`???`().*/, ???/*->scala::Predef.`???`().*/, ???/*->scala::Predef.`???`().*/)
1212
def this(t/*<-flags::p::package.C#`<init>`(+2).*//*<-flags::p::package.C#`<init>`(+2).(t)*/: T/*->flags::p::package.C#[T]*/) = this(t/*->flags::p::package.C#`<init>`(+2).(t)*/, ???/*->scala::Predef.`???`().*/, ???/*->scala::Predef.`???`().*/)

tests/semanticdb/metac.expect

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -210,7 +210,7 @@ annot/B#`<init>`().(x) => param x
210210
annot/B#`<init>`(+1). => ctor <init>
211211
annot/B#x. => val method x
212212
annot/M. => final object M
213-
annot/M.m(). => method m
213+
annot/M.m(). => macro m
214214
annot/M.m().[TT] => typeparam TT
215215
annot/T# => trait T
216216
annot/T#`<init>`(). => primary ctor <init>
@@ -262,7 +262,7 @@ Occurrences:
262262
[29:6..29:7): m <- annot/M.m().
263263
[29:8..29:10): TT <- annot/M.m().[TT]
264264
[29:13..29:16): Int -> scala/Int#
265-
[29:16..29:16): -> scala/Predef.`???`().
265+
[29:25..29:28): ??? -> scala/Predef.`???`().
266266
[32:0..32:0): <- annot/T#`<init>`().
267267
[32:1..32:16): TraitAnnotation -> com/javacp/annot/TraitAnnotation#
268268
[33:6..33:7): T <- annot/T#
@@ -2986,7 +2986,7 @@ flags/p/package.Z# => sealed trait Z
29862986
flags/p/package.Z#`<init>`(). => primary ctor <init>
29872987
flags/p/package.`y_=`(). => var method y_=
29882988
flags/p/package.`y_=`().(x$1) => param x$1
2989-
flags/p/package.m(). => method m
2989+
flags/p/package.m(). => macro m
29902990
flags/p/package.m().[TT] => typeparam TT
29912991
flags/p/package.x. => lazy val method x
29922992
flags/p/package.xs1. => val method xs1
@@ -3013,7 +3013,7 @@ Occurrences:
30133013
[8:6..8:7): m <- flags/p/package.m().
30143014
[8:8..8:10): TT <- flags/p/package.m().[TT]
30153015
[8:13..8:16): Int -> scala/Int#
3016-
[8:16..8:16): -> scala/Predef.`???`().
3016+
[8:25..8:28): ??? -> scala/Predef.`???`().
30173017
[9:17..9:18): C <- flags/p/package.C#
30183018
[9:18..9:47): <- flags/p/package.C#`<init>`().
30193019
[9:20..9:21): T <- flags/p/package.C#[T]

0 commit comments

Comments
 (0)