Skip to content

Commit 3419d50

Browse files
committed
support macro defs in TASTy
1 parent ea9cff2 commit 3419d50

File tree

22 files changed

+308
-18
lines changed

22 files changed

+308
-18
lines changed

project/DottySupport.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import sbt.librarymanagement.{
1212
* Settings to support validation of TastyUnpickler against the release of dotty with the matching TASTy version
1313
*/
1414
object TastySupport {
15-
val supportedTASTyRelease = "0.25.0-RC1" // TASTy version 23
15+
val supportedTASTyRelease = "0.25.0-RC2" // TASTy version 23
1616
val dottyCompiler = "ch.epfl.lamp" % "dotty-compiler_0.25" % supportedTASTyRelease
1717
}
1818

src/compiler/scala/tools/nsc/tasty/TastyModes.scala

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ object TastyModes {
2222
final val ReadParents: TastyMode = TastyMode(1 << 0)
2323
final val ReadAnnotation: TastyMode = TastyMode(1 << 1)
2424
final val OuterTerm: TastyMode = TastyMode(1 << 2)
25+
final val ReadMacro: TastyMode = TastyMode(1 << 3)
26+
final val IndexBody: TastyMode = TastyMode(1 << 4)
2527

2628
case class TastyMode(val toInt: Int) extends AnyVal { mode =>
2729

@@ -39,6 +41,8 @@ object TastyModes {
3941
if (mode.is(ReadParents)) sb += "ReadParents"
4042
if (mode.is(ReadAnnotation)) sb += "ReadAnnotation"
4143
if (mode.is(OuterTerm)) sb += "OuterTerm"
44+
if (mode.is(ReadMacro)) sb += "ReadMacro"
45+
if (mode.is(IndexBody)) sb += "IndexBody"
4246
sb.mkString("|")
4347
}
4448
}

src/compiler/scala/tools/nsc/tasty/TreeUnpickler.scala

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -521,16 +521,11 @@ class TreeUnpickler[Tasty <: TastyUniverse](
521521
sym.setAnnotations(annotFns.map(_(sym)))
522522
ctx.owner match {
523523
case cls if cls.isClass && canEnterInClass =>
524+
if (ctx.mode.is(IndexBody) && ctx.isLatentCandidate(sym))
525+
ctx.registerLatent(sym)
524526
val decl = if (flags.is(Object) && isClass) sym.sourceObject else sym
525-
val decls = cls.rawInfo.decls
526-
if (allowsOverload(decl)) {
527-
if (ctx.canEnterOverload(decl)) {
528-
decls.enter(decl)
529-
}
530-
}
531-
else {
532-
decls.enterIfNew(decl)
533-
}
527+
if (ctx.canEnter(decl))
528+
ctx.enter(cls, decl)
534529
case _ =>
535530
}
536531
registerSym(start, sym)
@@ -722,11 +717,12 @@ class TreeUnpickler[Tasty <: TastyUniverse](
722717
val localCtx = ctx.withOwner(sym)
723718
tag match {
724719
case DEFDEF =>
725-
val unsupported = completer.tastyFlagSet &~ (Extension | Inline | Macro | Exported)
720+
val unsupported = completer.tastyFlagSet &~ (Extension | Inline | Exported | Erased)
726721
unsupportedWhen(unsupported.hasFlags, s"flags on $sym: ${showTasty(unsupported)}")
727722
if (completer.tastyFlagSet.is(Extension)) ctx.log(s"$tname is a Scala 3 extension method.")
728-
unsupportedWhen(completer.tastyFlagSet.is(Inline, butNot = Macro), s"inline $sym")
729-
unsupportedWhen(completer.tastyFlagSet.is(Inline | Macro), s"macro $sym")
723+
unsupportedWhen(completer.tastyFlagSet.is(Inline), s"${if (sym.is(Macro)) "" else "inline "}$sym")
724+
val isMacroDef = completer.tastyFlagSet.is(Erased) && sym.is(Macro)
725+
unsupportedWhen(completer.tastyFlagSet.is(Erased) && !isMacroDef, s"erased $sym")
730726
val isCtor = sym.isClassConstructor
731727
val typeParams = {
732728
if (isCtor) {
@@ -739,6 +735,10 @@ class TreeUnpickler[Tasty <: TastyUniverse](
739735
}
740736
val vparamss = readParamss(localCtx)
741737
val tpt = readTpt()(localCtx)
738+
if (isMacroDef) {
739+
val impl = tpd.Macro(readTerm()(ctx.addMode(ReadMacro)))
740+
sym.addAnnotation(symbolTable.AnnotationInfo(symbolTable.definitions.MacroTastyImplAnnotation.tpe, List(impl), Nil))
741+
}
742742
val valueParamss = normalizeIfConstructor(vparamss.map(_.map(symFromNoCycle)), isCtor)
743743
val resType = effectiveResultType(sym, typeParams, tpt.tpe)
744744
ctx.setInfo(sym, defn.DefDefType(if (isCtor) Nil else typeParams, valueParamss, resType))
@@ -803,8 +803,10 @@ class TreeUnpickler[Tasty <: TastyUniverse](
803803
// ** MEMBERS **
804804
ctx.log(s"$symAddr Template: indexing members of $cls:")
805805
val bodyIndexer = fork
806+
val bodyCtx = ctx.addMode(IndexBody)
806807
while (bodyIndexer.reader.nextByte != DEFDEF) bodyIndexer.skipTree() // skip until primary ctor
807-
bodyIndexer.indexStats(end)
808+
bodyIndexer.indexStats(end)(bodyCtx)
809+
bodyCtx.enterLatents()
808810

809811
// ** PARENTS **
810812
ctx.log(s"$symAddr Template: adding parents of $cls:")
@@ -990,7 +992,7 @@ class TreeUnpickler[Tasty <: TastyUniverse](
990992
if (alias != untpd.EmptyTree) alias // only for opaque type alias
991993
else tpd.TypeBoundsTree(lo, hi)
992994
case BLOCK =>
993-
if (inParentCtor) {
995+
if (inParentCtor | ctx.mode.is(ReadMacro)) {
994996
val exprReader = fork
995997
skipTree()
996998
until(end)(skipTree()) //val stats = readStats(ctx.owner, end)

src/compiler/scala/tools/nsc/tasty/bridge/ContextOps.scala

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import scala.reflect.io.AbstractFile
1818
import scala.tools.tasty.{TastyName, TastyFlags}, TastyFlags._, TastyName.ObjectName
1919
import scala.tools.nsc.tasty.{TastyUniverse, TastyModes, SafeEq}, TastyModes._
2020
import scala.reflect.internal.MissingRequirementError
21+
import scala.collection.mutable
2122

2223
trait ContextOps { self: TastyUniverse =>
2324
import self.{symbolTable => u}, u.{internal => ui}
@@ -67,6 +68,25 @@ trait ContextOps { self: TastyUniverse =>
6768
final def ignoreAnnotations: Boolean = u.settings.YtastyNoAnnotations
6869
final def verboseDebug: Boolean = u.settings.debug
6970

71+
def isScala3Macro(sym: Symbol): Boolean = isScala3Inline(sym) && sym.is(Macro)
72+
def isScala3Inline(sym: Symbol): Boolean = sym.completer.tastyFlagSet.is(Inline)
73+
def isScala2Macro(sym: Symbol): Boolean = sym.completer.tastyFlagSet.is(Erased) && sym.is(Macro)
74+
75+
def isLatentCandidate(sym: Symbol): Boolean = isScala3Inline(sym) || isScala2Macro(sym)
76+
77+
def canEnter(decl: Symbol): Boolean = !isScala3Macro(decl)
78+
def enter(clazz: Symbol, decl: Symbol): Unit = enter(clazz.rawInfo.decls, decl)
79+
private[ContextOps] def enter(decls: u.Scope, decl: Symbol): Unit = {
80+
if (allowsOverload(decl)) {
81+
if (canEnterOverload(decl)) {
82+
decls.enter(decl)
83+
}
84+
}
85+
else {
86+
decls.enterIfNew(decl)
87+
}
88+
}
89+
7090
def canEnterOverload(decl: Symbol): Boolean = {
7191
!(decl.isModule && isSymbol(findObject(decl.name)))
7292
}
@@ -92,6 +112,9 @@ trait ContextOps { self: TastyUniverse =>
92112
def source: AbstractFile
93113
def mode: TastyMode
94114

115+
def registerLatent(sym: Symbol): Unit
116+
def enterLatents(): Unit
117+
95118
private final def loadingMirror: u.Mirror = u.mirrorThatLoaded(owner)
96119

97120
final def requiredPackage(fullname: TastyName): Symbol = {
@@ -390,11 +413,46 @@ trait ContextOps { self: TastyUniverse =>
390413
final class InitialContext(val topLevelClass: Symbol, val source: AbstractFile) extends Context {
391414
def mode: TastyMode = EmptyTastyMode
392415
def owner: Symbol = topLevelClass.owner
416+
def registerLatent(sym: Symbol): Unit = ()
417+
def enterLatents(): Unit = ()
393418
}
394419

395420
final class FreshContext(val owner: Symbol, val outer: Context, val mode: TastyMode) extends Context {
396421
private[this] var mySource: AbstractFile = null
422+
private[this] var myLatentDefs: mutable.ArrayBuffer[Symbol] = null
423+
private[this] var myMacros: mutable.ArrayBuffer[Symbol] = null
397424
def atSource(source: AbstractFile): this.type = { mySource = source ; this }
398425
def source: AbstractFile = if (mySource == null) outer.source else mySource
426+
def registerLatent(sym: Symbol): Unit = {
427+
if (isScala2Macro(sym)) {
428+
val macros = {
429+
if (myMacros == null) myMacros = mutable.ArrayBuffer.empty
430+
myMacros
431+
}
432+
macros += sym
433+
} else {
434+
val defs = {
435+
if (myLatentDefs == null) myLatentDefs = mutable.ArrayBuffer.empty
436+
myLatentDefs
437+
}
438+
defs += sym
439+
}
440+
}
441+
def enterLatents(): Unit = {
442+
for {
443+
owner <- Option.when(owner.isClass)(owner)
444+
defs <- Option(myLatentDefs)
445+
} {
446+
val macros = Option(myMacros).getOrElse(mutable.ArrayBuffer.empty)
447+
val decls = owner.rawInfo.decls
448+
for (d <- defs if !macros.exists(_.name == d.name)) {
449+
enter(decls, d)
450+
}
451+
defs.clear()
452+
macros.clear()
453+
}
454+
myLatentDefs = null
455+
myMacros = null
456+
}
399457
}
400458
}

src/compiler/scala/tools/nsc/tasty/bridge/FlagOps.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ trait FlagOps { self: TastyUniverse =>
2121

2222
object FlagSets {
2323
val TastyOnlyFlags: TastyFlagSet = (
24-
Erased | Internal | Inline | InlineProxy | Opaque | Extension | Given | Exported | Macro | SuperTrait | Enum
24+
Erased | Internal | Inline | InlineProxy | Opaque | Extension | Given | Exported | SuperTrait | Enum
2525
| Open | ParamAlias
2626
)
2727
val TermParamOrAccessor: TastyFlagSet = Param | ParamSetter
@@ -42,6 +42,7 @@ trait FlagOps { self: TastyUniverse =>
4242
if (tflags.is(Case)) flags |= Flag.CASE
4343
if (tflags.is(Implicit)) flags |= ModifierFlags.IMPLICIT
4444
if (tflags.is(Lazy)) flags |= Flag.LAZY
45+
if (tflags.is(Macro)) flags |= Flag.MACRO
4546
if (tflags.is(Override)) flags |= Flag.OVERRIDE
4647
if (tflags.is(Static)) flags |= ModifierFlags.STATIC
4748
if (tflags.is(Object)) flags |= Flags.MODULE
@@ -76,7 +77,6 @@ trait FlagOps { self: TastyUniverse =>
7677
case Extension => "<extension>"
7778
case Given => "given"
7879
case Exported => "<exported>"
79-
case Macro => "<tastymacro>"
8080
case SuperTrait => "<supertrait>"
8181
case Enum => "enum"
8282
case Open => "open"

src/compiler/scala/tools/nsc/tasty/bridge/SymbolOps.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ trait SymbolOps { self: TastyUniverse =>
9494
}
9595

9696
private def hasType(member: Symbol)(implicit ctx: Context) = {
97-
ctx.mode.is(ReadAnnotation) || (member.rawInfo `ne` u.NoType)
97+
ctx.mode.is(ReadAnnotation) || ctx.mode.is(ReadMacro) && (member.info `ne` u.NoType) || (member.rawInfo `ne` u.NoType)
9898
}
9999

100100
private def errorMissing[T](space: Type, tname: TastyName)(implicit ctx: Context) = {

src/compiler/scala/tools/nsc/tasty/bridge/TreeOps.scala

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ package scala.tools.nsc.tasty.bridge
1515
import scala.tools.nsc.tasty.TastyUniverse
1616

1717
import scala.tools.tasty.TastyName
18+
import scala.reflect.internal.Flags
1819

1920

2021
trait TreeOps { self: TastyUniverse =>
@@ -56,6 +57,28 @@ trait TreeOps { self: TastyUniverse =>
5657
u.TypeTree(defn.LambdaFromParams(tparams, body.tpe))
5758
}
5859

60+
def Macro(impl: Tree): Tree = impl match {
61+
case tree @ u.TypeApply(qual, args) =>
62+
u.TypeApply(Macro(qual), args).setType(tree.tpe)
63+
case tree @ u.Select(pre, sel) =>
64+
val sym = if (sel.isTermName) tree.tpe.termSymbol else tree.tpe.typeSymbol
65+
u.Select(Macro(pre), sym).setType(tree.tpe)
66+
case tree: u.TypeTree if tree.tpe.prefix !== u.NoType =>
67+
val sym = tree.tpe match {
68+
case u.SingleType(_, sym) => sym
69+
case u.TypeRef(_, sym, _) => sym
70+
}
71+
if (tree.tpe.prefix === u.NoPrefix && (sym.hasFlag(Flags.PACKAGE) && !sym.isPackageObjectOrClass || sym.isLocalToBlock)) {
72+
if (sym.isLocalToBlock) u.Ident(sym).setType(tree.tpe)
73+
else u.This(sym).setType(tree.tpe)
74+
}
75+
else {
76+
u.Select(Macro(u.TypeTree(tree.tpe.prefix)), sym).setType(tree.tpe)
77+
}
78+
case tree =>
79+
tree
80+
}
81+
5982
def Typed(expr: Tree, tpt: Tree): Tree = u.Typed(expr, tpt).setType(tpt.tpe)
6083

6184
def Apply(fun: Tree, args: List[Tree]): Tree = u.Apply(fun, args).setType(fnResult(fun.tpe))

src/compiler/scala/tools/nsc/tasty/bridge/TypeOps.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import scala.util.chaining._
2121

2222
import scala.collection.mutable
2323
import scala.reflect.internal.Flags
24+
import scala.tools.tasty.TastyName.QualifiedName
2425

2526
trait TypeOps { self: TastyUniverse =>
2627
import self.{symbolTable => u}, u.{internal => ui}

src/compiler/scala/tools/nsc/typechecker/Macros.scala

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -282,6 +282,10 @@ trait Macros extends MacroRuntimes with Traces with Helpers {
282282
macroImplBindingCache.getOrElseUpdate(macroDef,
283283
macroDef.getAnnotation(MacroImplAnnotation) collect {
284284
case AnnotationInfo(_, List(pickle), _) => MacroImplBinding.unpickle(pickle)
285+
} orElse {
286+
macroDef.getAnnotation(MacroTastyImplAnnotation) collect {
287+
case AnnotationInfo(_, List(unpickled), _) => MacroImplBinding.unpickle(MacroImplBinding.pickle(unpickled))
288+
}
285289
}
286290
)
287291
}

src/reflect/scala/reflect/internal/Definitions.scala

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -566,6 +566,34 @@ trait Definitions extends api.StandardDefinitions {
566566
def MacroContextWeakTypeTagClass = BlackboxContextClass.map(sym => getTypeMember(sym, tpnme.WeakTypeTag))
567567
def MacroContextTreeType = BlackboxContextClass.map(sym => getTypeMember(sym, tpnme.Tree))
568568
lazy val MacroImplAnnotation = requiredClass[scala.reflect.macros.internal.macroImpl]
569+
lazy val MacroInternalPackage = MacroImplAnnotation.owner.suchThat(_.isPackageClass)
570+
571+
/** Implementation of a class that is identical to `scala.reflect.macros.internal.macroImpl` but only exists at compileTime
572+
*/
573+
lazy val MacroTastyImplAnnotation = {
574+
val TastyMacroImpl = MacroInternalPackage.newClassSymbol(tpnme.TastyMacroImpl, NoPosition)
575+
TastyMacroImpl.setPrivateWithin(ScalaPackage)
576+
TastyMacroImpl.setInfoAndEnter(ClassInfoType(AnnotationClass.tpe :: Nil, newScope, TastyMacroImpl))
577+
val unpickledMacroImpl = TermName("unpickledMacroImpl")
578+
// getter
579+
val unpickledMacroImplMeth = TastyMacroImpl.newMethod(unpickledMacroImpl, newFlags = STABLE | ACCESSOR | PARAMACCESSOR)
580+
unpickledMacroImplMeth.setInfo(internal.nullaryMethodType(AnyTpe)).markAllCompleted()
581+
TastyMacroImpl.info.decls enter unpickledMacroImplMeth
582+
// field
583+
val unpickledMacroImplField = TastyMacroImpl.newValue(unpickledMacroImpl, newFlags = PRIVATE | LOCAL | PARAMACCESSOR)
584+
unpickledMacroImplField.setInfo(AnyTpe).markAllCompleted()
585+
TastyMacroImpl.info.decls enter unpickledMacroImplField
586+
// ctor
587+
val ctor = TastyMacroImpl.newConstructor(NoPosition)
588+
val param = ctor.newValueParameter(unpickledMacroImpl).setInfo(AnyTpe)
589+
ctor.setInfo(MethodType(param :: Nil, TastyMacroImpl.tpe)).markAllCompleted()
590+
TastyMacroImpl.info.decls enter ctor
591+
TastyMacroImpl.addAnnotation(
592+
sym = CompileTimeOnlyAttr,
593+
arg = Literal(Constant("TastyMacroImpl is an implementation detail of unpickling TASTy"))
594+
)
595+
TastyMacroImpl.markAllCompleted()
596+
}
569597

570598
lazy val StringContextClass = requiredClass[scala.StringContext]
571599
lazy val StringContextModule = requiredModule[scala.StringContext.type]

0 commit comments

Comments
 (0)