Skip to content

Commit 8791366

Browse files
committed
hooks for naming and synthesis in Namers.scala and Typers.scala
Interestingly enough, despite of the implementation surface being quite noticeable, it is enough to hijack just `enterSym` and typechecking of stats for packages, templates and blocks in order to enable macro annotations. That and `ensureCompanionObject`, which I couldn't abstract away so far. An architectural note: given that a hooked method is called `X`, there are two implementations of this method. `pluginsX` is defined in AnalyzerPlugins.scala and lets macro plugins customize `X`. `standardX` is defined next to `X` and provides a default implementation. Finally `X` is changed to trivially forward to `pluginsX`. Existing and future callers of `X` now can be completely oblivious of the introduced hooks, because calls to `X` will continue working and will be correctly hooked. This makes the infrastructure more robust. The only downside is that in case when a macro plugin wants to call into the default implementation, it needs to call `standardX`, because `X` will lead to a stack overflow. However, in my opinion this not a big problem, because such failures are load and clear + for every `pluginsX` we actually provide documentation that says what is its standard impl is.
1 parent 4d92aec commit 8791366

File tree

11 files changed

+86
-54
lines changed

11 files changed

+86
-54
lines changed

src/compiler/scala/reflect/macros/runtime/MacroRuntimes.scala

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,13 @@ trait MacroRuntimes extends JavaReflectionRuntimes with ScalaReflectionRuntimes
1919
* @return Requested runtime if macro implementation can be loaded successfully from either of the mirrors,
2020
* `null` otherwise.
2121
*/
22+
def macroRuntime(expandee: Tree): MacroRuntime = pluginsMacroRuntime(expandee)
23+
24+
/** Default implementation of `macroRuntime`.
25+
* Can be overridden by analyzer plugins (see AnalyzerPlugins.pluginsMacroRuntime for more details)
26+
*/
2227
private val macroRuntimesCache = perRunCaches.newWeakMap[Symbol, MacroRuntime]
23-
def macroRuntime(expandee: Tree): MacroRuntime = {
28+
def standardMacroRuntime(expandee: Tree): MacroRuntime = {
2429
val macroDef = expandee.symbol
2530
macroLogVerbose(s"looking for macro implementation: $macroDef")
2631
if (fastTrack contains macroDef) {

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ trait Analyzer extends AnyRef
4040
override def keepsTypeParams = false
4141

4242
def apply(unit: CompilationUnit) {
43-
pluginsEnterSym(newNamer(rootContext(unit)), unit.body)
43+
newNamer(rootContext(unit)).enterSym(unit.body)
4444
}
4545
}
4646
}

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

Lines changed: 26 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,7 @@ trait AnalyzerPlugins { self: Analyzer =>
180180
* Typechecks the right-hand side of a macro definition (which typically features
181181
* a mere reference to a macro implementation).
182182
*
183-
* Default implementation provided in `self.typedMacroBody` makes sure that the rhs
183+
* Default implementation provided in `self.standardTypedMacroBody` makes sure that the rhs
184184
* resolves to a reference to a method in either a static object or a macro bundle,
185185
* verifies that the referred method is compatible with the macro def and upon success
186186
* attaches a macro impl binding to the macro def's symbol.
@@ -193,7 +193,7 @@ trait AnalyzerPlugins { self: Analyzer =>
193193
* Expands an application of a def macro (i.e. of a symbol that has the MACRO flag set),
194194
* possibly using the current typer mode and the provided prototype.
195195
*
196-
* Default implementation provided in `self.macroExpand` figures out whether the `expandee`
196+
* Default implementation provided in `self.standardMacroExpand` figures out whether the `expandee`
197197
* needs to be expanded right away or its expansion has to be delayed until all undetermined
198198
* parameters are inferred, then loads the macro implementation using `self.pluginsMacroRuntime`,
199199
* prepares the invocation arguments for the macro implementation using `self.pluginsMacroArgs`,
@@ -211,7 +211,7 @@ trait AnalyzerPlugins { self: Analyzer =>
211211
/**
212212
* Computes the arguments that need to be passed to the macro impl corresponding to a particular expandee.
213213
*
214-
* Default implementation provided in `self.macroArgs` instantiates a `scala.reflect.macros.contexts.Context`,
214+
* Default implementation provided in `self.standardMacroArgs` instantiates a `scala.reflect.macros.contexts.Context`,
215215
* gathers type and value arguments of the macro application and throws them together into `MacroArgs`.
216216
*
217217
* $nonCumulativeReturnValueDoc.
@@ -221,7 +221,7 @@ trait AnalyzerPlugins { self: Analyzer =>
221221
/**
222222
* Summons a function that encapsulates macro implementation invocations for a particular expandee.
223223
*
224-
* Default implementation provided in `self.macroRuntime` returns a function that
224+
* Default implementation provided in `self.standardMacroRuntime` returns a function that
225225
* loads the macro implementation binding from the macro definition symbol,
226226
* then uses either Java or Scala reflection to acquire the method that corresponds to the impl,
227227
* and then reflectively calls into that method.
@@ -233,7 +233,7 @@ trait AnalyzerPlugins { self: Analyzer =>
233233
/**
234234
* Creates a symbol for the given tree in lexical context encapsulated by the given namer.
235235
*
236-
* Default implementation provided in `namer.enterSym` handles MemberDef's and Imports,
236+
* Default implementation provided in `namer.standardEnterSym` handles MemberDef's and Imports,
237237
* doing nothing for other trees (DocDef's are seen through and rewrapped). Typical implementation
238238
* of `enterSym` for a particular tree flavor creates a corresponding symbol, assigns it to the tree,
239239
* enters the symbol into scope and then might even perform some code generation.
@@ -245,7 +245,7 @@ trait AnalyzerPlugins { self: Analyzer =>
245245
/**
246246
* Makes sure that for the given class definition, there exists a companion object definition.
247247
*
248-
* Default implementation provided in `namer.ensureCompanionObject` looks up a companion symbol for the class definition
248+
* Default implementation provided in `namer.standardEnsureCompanionObject` looks up a companion symbol for the class definition
249249
* and then checks whether the resulting symbol exists or not. If it exists, then nothing else is done.
250250
* If not, a synthetic object definition is created using the provided factory, which is then entered into namer's scope.
251251
*
@@ -371,54 +371,56 @@ trait AnalyzerPlugins { self: Analyzer =>
371371
def pluginsTypedMacroBody(typer: Typer, ddef: DefDef): Tree = invoke(new NonCumulativeOp[Tree] {
372372
def position = ddef.pos
373373
def description = "typecheck this macro definition"
374-
def default = typedMacroBody(typer, ddef)
374+
def default = standardTypedMacroBody(typer, ddef)
375375
def custom(plugin: MacroPlugin) = plugin.pluginsTypedMacroBody(typer, ddef)
376376
})
377377

378378
/** @see MacroPlugin.pluginsMacroExpand */
379379
def pluginsMacroExpand(typer: Typer, expandee: Tree, mode: Mode, pt: Type): Tree = invoke(new NonCumulativeOp[Tree] {
380380
def position = expandee.pos
381381
def description = "expand this macro application"
382-
def default = macroExpand(typer, expandee, mode, pt)
382+
def default = standardMacroExpand(typer, expandee, mode, pt)
383383
def custom(plugin: MacroPlugin) = plugin.pluginsMacroExpand(typer, expandee, mode, pt)
384384
})
385385

386386
/** @see MacroPlugin.pluginsMacroArgs */
387387
def pluginsMacroArgs(typer: Typer, expandee: Tree): MacroArgs = invoke(new NonCumulativeOp[MacroArgs] {
388388
def position = expandee.pos
389389
def description = "compute macro arguments for this macro application"
390-
def default = macroArgs(typer, expandee)
390+
def default = standardMacroArgs(typer, expandee)
391391
def custom(plugin: MacroPlugin) = plugin.pluginsMacroArgs(typer, expandee)
392392
})
393393

394394
/** @see MacroPlugin.pluginsMacroRuntime */
395395
def pluginsMacroRuntime(expandee: Tree): MacroRuntime = invoke(new NonCumulativeOp[MacroRuntime] {
396396
def position = expandee.pos
397397
def description = "compute macro runtime for this macro application"
398-
def default = macroRuntime(expandee)
398+
def default = standardMacroRuntime(expandee)
399399
def custom(plugin: MacroPlugin) = plugin.pluginsMacroRuntime(expandee)
400400
})
401401

402402
/** @see MacroPlugin.pluginsEnterSym */
403-
def pluginsEnterSym(namer: Namer, tree: Tree): Context = invoke(new NonCumulativeOp[Context] {
404-
def position = tree.pos
405-
def description = "enter a symbol for this tree"
406-
def default = namer.enterSym(tree)
407-
def custom(plugin: MacroPlugin) = {
408-
val hasExistingSym = tree.symbol != NoSymbol
409-
val result = plugin.pluginsEnterSym(namer, tree)
410-
if (result && hasExistingSym) Some(namer.context)
411-
else if (result && tree.isInstanceOf[Import]) Some(namer.context.make(tree))
412-
else if (result) Some(namer.context)
413-
else None
414-
}
415-
})
403+
def pluginsEnterSym(namer: Namer, tree: Tree): Context =
404+
if (macroPlugins.isEmpty) namer.standardEnterSym(tree)
405+
else invoke(new NonCumulativeOp[Context] {
406+
def position = tree.pos
407+
def description = "enter a symbol for this tree"
408+
def default = namer.standardEnterSym(tree)
409+
def custom(plugin: MacroPlugin) = {
410+
val hasExistingSym = tree.symbol != NoSymbol
411+
val result = plugin.pluginsEnterSym(namer, tree)
412+
if (result && hasExistingSym) Some(namer.context)
413+
else if (result && tree.isInstanceOf[Import]) Some(namer.context.make(tree))
414+
else if (result) Some(namer.context)
415+
else None
416+
}
417+
})
416418

417419
/** @see MacroPlugin.pluginsEnsureCompanionObject */
418420
def pluginsEnsureCompanionObject(namer: Namer, cdef: ClassDef, creator: ClassDef => Tree = companionModuleDef(_)): Symbol = invoke(new NonCumulativeOp[Symbol] {
419421
def position = cdef.pos
420422
def description = "enter a companion symbol for this tree"
421-
def default = namer.ensureCompanionObject(cdef, creator)
423+
def default = namer.standardEnsureCompanionObject(cdef, creator)
422424
def custom(plugin: MacroPlugin) = plugin.pluginsEnsureCompanionObject(namer, cdef, creator)
423425
})
424426

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

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -314,7 +314,12 @@ trait Macros extends FastTrack with MacroRuntimes with Traces with Helpers {
314314
* @return Macro impl reference for the given macro definition if everything is okay.
315315
* EmptyTree if an error occurs.
316316
*/
317-
def typedMacroBody(typer: Typer, macroDdef: DefDef): Tree = {
317+
def typedMacroBody(typer: Typer, macroDdef: DefDef): Tree = pluginsTypedMacroBody(typer, macroDdef)
318+
319+
/** Default implementation of `typedMacroBody`.
320+
* Can be overridden by analyzer plugins (see AnalyzerPlugins.pluginsTypedMacroBody for more details)
321+
*/
322+
def standardTypedMacroBody(typer: Typer, macroDdef: DefDef): Tree = {
318323
val macroDef = macroDdef.symbol
319324
assert(macroDef.isMacro, macroDdef)
320325

@@ -359,8 +364,12 @@ trait Macros extends FastTrack with MacroRuntimes with Traces with Helpers {
359364
/** Calculate the arguments to pass to a macro implementation when expanding the provided tree.
360365
*/
361366
case class MacroArgs(c: MacroContext, others: List[Any])
367+
def macroArgs(typer: Typer, expandee: Tree): MacroArgs = pluginsMacroArgs(typer, expandee)
362368

363-
def macroArgs(typer: Typer, expandee: Tree): MacroArgs = {
369+
/** Default implementation of `macroArgs`.
370+
* Can be overridden by analyzer plugins (see AnalyzerPlugins.pluginsMacroArgs for more details)
371+
*/
372+
def standardMacroArgs(typer: Typer, expandee: Tree): MacroArgs = {
364373
val macroDef = expandee.symbol
365374
val paramss = macroDef.paramss
366375
val treeInfo.Applied(core, targs, argss) = expandee
@@ -561,7 +570,7 @@ trait Macros extends FastTrack with MacroRuntimes with Traces with Helpers {
561570
onFailure(typer.infer.setError(expandee))
562571
} else try {
563572
val expanded = {
564-
val runtime = pluginsMacroRuntime(expandee)
573+
val runtime = macroRuntime(expandee)
565574
if (runtime != null) macroExpandWithRuntime(typer, expandee, runtime)
566575
else macroExpandWithoutRuntime(typer, expandee)
567576
}
@@ -688,7 +697,7 @@ trait Macros extends FastTrack with MacroRuntimes with Traces with Helpers {
688697
else {
689698
forced += delayed
690699
typer.infer.inferExprInstance(delayed, typer.context.extractUndetparams(), outerPt, keepNothings = false)
691-
pluginsMacroExpand(typer, delayed, mode, outerPt)
700+
macroExpand(typer, delayed, mode, outerPt)
692701
}
693702
} else delayed
694703
}
@@ -698,7 +707,12 @@ trait Macros extends FastTrack with MacroRuntimes with Traces with Helpers {
698707
/** Expands a term macro used in apply role as `M(2)(3)` in `val x = M(2)(3)`.
699708
* @see DefMacroExpander
700709
*/
701-
def macroExpand(typer: Typer, expandee: Tree, mode: Mode, pt: Type): Tree = {
710+
def macroExpand(typer: Typer, expandee: Tree, mode: Mode, pt: Type): Tree = pluginsMacroExpand(typer, expandee, mode, pt)
711+
712+
/** Default implementation of `macroExpand`.
713+
* Can be overridden by analyzer plugins (see AnalyzerPlugins.pluginsMacroExpand for more details)
714+
*/
715+
def standardMacroExpand(typer: Typer, expandee: Tree, mode: Mode, pt: Type): Tree = {
702716
val expander = new DefMacroExpander(typer, expandee, mode, pt)
703717
expander(expandee)
704718
}
@@ -730,12 +744,12 @@ trait Macros extends FastTrack with MacroRuntimes with Traces with Helpers {
730744
case (false, true) =>
731745
macroLogLite("macro expansion is delayed: %s".format(expandee))
732746
delayed += expandee -> undetparams
733-
expandee updateAttachment MacroRuntimeAttachment(delayed = true, typerContext = typer.context, macroContext = Some(pluginsMacroArgs(typer, expandee).c))
747+
expandee updateAttachment MacroRuntimeAttachment(delayed = true, typerContext = typer.context, macroContext = Some(macroArgs(typer, expandee).c))
734748
Delay(expandee)
735749
case (false, false) =>
736750
import typer.TyperErrorGen._
737751
macroLogLite("performing macro expansion %s at %s".format(expandee, expandee.pos))
738-
val args = pluginsMacroArgs(typer, expandee)
752+
val args = macroArgs(typer, expandee)
739753
try {
740754
val numErrors = reporter.ERROR.count
741755
def hasNewErrors = reporter.ERROR.count > numErrors
@@ -850,7 +864,7 @@ trait Macros extends FastTrack with MacroRuntimes with Traces with Helpers {
850864
context.implicitsEnabled = typer.context.implicitsEnabled
851865
context.enrichmentEnabled = typer.context.enrichmentEnabled
852866
context.macrosEnabled = typer.context.macrosEnabled
853-
pluginsMacroExpand(newTyper(context), tree, EXPRmode, WildcardType)
867+
macroExpand(newTyper(context), tree, EXPRmode, WildcardType)
854868
case _ =>
855869
tree
856870
})

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

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -243,7 +243,12 @@ trait Namers extends MethodSynthesis {
243243
validate(sym2.companionClass)
244244
}
245245

246-
def enterSym(tree: Tree): Context = {
246+
def enterSym(tree: Tree): Context = pluginsEnterSym(this, tree)
247+
248+
/** Default implementation of `enterSym`.
249+
* Can be overridden by analyzer plugins (see AnalyzerPlugins.pluginsEnterSym for more details)
250+
*/
251+
def standardEnterSym(tree: Tree): Context = {
247252
def dispatch() = {
248253
var returnContext = this.context
249254
tree match {
@@ -253,7 +258,7 @@ trait Namers extends MethodSynthesis {
253258
case tree @ ValDef(_, _, _, _) => enterValDef(tree)
254259
case tree @ DefDef(_, _, _, _, _, _) => enterDefDef(tree)
255260
case tree @ TypeDef(_, _, _, _) => enterTypeDef(tree)
256-
case DocDef(_, defn) => pluginsEnterSym(this, defn)
261+
case DocDef(_, defn) => enterSym(defn)
257262
case tree @ Import(_, _) =>
258263
assignSymbol(tree)
259264
returnContext = context.make(tree)
@@ -452,7 +457,7 @@ trait Namers extends MethodSynthesis {
452457

453458
def enterSyms(trees: List[Tree]): Namer = {
454459
trees.foldLeft(this: Namer) { (namer, t) =>
455-
val ctx = pluginsEnterSym(namer, t)
460+
val ctx = namer enterSym t
456461
// for Import trees, enterSym returns a changed context, so we need a new namer
457462
if (ctx eq namer.context) namer
458463
else newNamer(ctx)
@@ -466,7 +471,13 @@ trait Namers extends MethodSynthesis {
466471
* class definition tree.
467472
* @return the companion object symbol.
468473
*/
469-
def ensureCompanionObject(cdef: ClassDef, creator: ClassDef => Tree = companionModuleDef(_)): Symbol = {
474+
def ensureCompanionObject(cdef: ClassDef, creator: ClassDef => Tree = companionModuleDef(_)): Symbol =
475+
pluginsEnsureCompanionObject(this, cdef, creator)
476+
477+
/** Default implementation of `ensureCompanionObject`.
478+
* Can be overridden by analyzer plugins (see AnalyzerPlugins.pluginsEnsureCompanionObject for more details)
479+
*/
480+
def standardEnsureCompanionObject(cdef: ClassDef, creator: ClassDef => Tree = companionModuleDef(_)): Symbol = {
470481
val m = companionSymbolOf(cdef.symbol, context)
471482
// @luc: not sure why "currentRun.compiles(m)" is needed, things breaks
472483
// otherwise. documentation welcome.
@@ -662,15 +673,15 @@ trait Namers extends MethodSynthesis {
662673
tree.symbol setInfo completerOf(tree)
663674

664675
if (mods.isCase) {
665-
val m = pluginsEnsureCompanionObject(this, tree, caseModuleDef)
676+
val m = ensureCompanionObject(tree, caseModuleDef)
666677
m.moduleClass.updateAttachment(new ClassForCaseCompanionAttachment(tree))
667678
}
668679
val hasDefault = impl.body exists {
669680
case DefDef(_, nme.CONSTRUCTOR, _, vparamss, _, _) => mexists(vparamss)(_.mods.hasDefault)
670681
case _ => false
671682
}
672683
if (hasDefault) {
673-
val m = pluginsEnsureCompanionObject(this, tree)
684+
val m = ensureCompanionObject(tree)
674685
m.updateAttachment(new ConstructorDefaultsAttachment(tree, null))
675686
}
676687
val owner = tree.symbol.owner
@@ -697,7 +708,7 @@ trait Namers extends MethodSynthesis {
697708
def enterIfNotThere(sym: Symbol) { }
698709

699710
def enterSyntheticSym(tree: Tree): Symbol = {
700-
pluginsEnterSym(this, tree)
711+
enterSym(tree)
701712
context.unit.synthetics(tree.symbol) = tree
702713
tree.symbol
703714
}
@@ -931,7 +942,7 @@ trait Namers extends MethodSynthesis {
931942
log("Ensuring companion for derived value class " + cdef.name + " at " + cdef.pos.show)
932943
clazz setFlag FINAL
933944
// Don't force the owner's info lest we create cycles as in SI-6357.
934-
pluginsEnsureCompanionObject(enclosingNamerWithScope(clazz.owner.rawInfo.decls), cdef)
945+
enclosingNamerWithScope(clazz.owner.rawInfo.decls).ensureCompanionObject(cdef)
935946
}
936947
pluginsTp
937948
}

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

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1112,7 +1112,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
11121112
if (tree.isType)
11131113
adaptType()
11141114
else if (mode.typingExprNotFun && treeInfo.isMacroApplication(tree) && !isMacroExpansionSuppressed(tree))
1115-
pluginsMacroExpand(this, tree, mode, pt)
1115+
macroExpand(this, tree, mode, pt)
11161116
else if (mode.typingConstructorPattern)
11171117
typedConstructorPattern(tree, pt)
11181118
else if (shouldInsertApply(tree))
@@ -1863,8 +1863,8 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
18631863
}
18641864

18651865
protected def enterSym(txt: Context, tree: Tree): Context =
1866-
if (txt eq context) pluginsEnterSym(namer, tree)
1867-
else pluginsEnterSym(newNamer(txt), tree)
1866+
if (txt eq context) namer enterSym tree
1867+
else newNamer(txt) enterSym tree
18681868

18691869
/** <!-- 2 --> Check that inner classes do not inherit from Annotation
18701870
*/
@@ -2213,7 +2213,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
22132213
} else if (meth.isMacro) {
22142214
// typechecking macro bodies is sort of unconventional
22152215
// that's why we employ our custom typing scheme orchestrated outside of the typer
2216-
transformedOr(ddef.rhs, pluginsTypedMacroBody(this, ddef))
2216+
transformedOr(ddef.rhs, typedMacroBody(this, ddef))
22172217
} else {
22182218
transformedOrTyped(ddef.rhs, EXPRmode, tpt1.tpe)
22192219
}
@@ -3812,7 +3812,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
38123812

38133813
protected def typedExistentialTypeTree(tree: ExistentialTypeTree, mode: Mode): Tree = {
38143814
for (wc <- tree.whereClauses)
3815-
if (wc.symbol == NoSymbol) { pluginsEnterSym(namer, wc); wc.symbol setFlag EXISTENTIAL }
3815+
if (wc.symbol == NoSymbol) { namer enterSym wc; wc.symbol setFlag EXISTENTIAL }
38163816
else context.scope enter wc.symbol
38173817
val whereClauses1 = typedStats(tree.whereClauses, context.owner)
38183818
for (vd @ ValDef(_, _, _, _) <- whereClauses1)
@@ -5517,7 +5517,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
55175517
// here we guard against this case
55185518
transformed(ddef.rhs)
55195519
} else {
5520-
val rhs1 = pluginsTypedMacroBody(this, ddef)
5520+
val rhs1 = typedMacroBody(this, ddef)
55215521
transformed(ddef.rhs) = rhs1
55225522
rhs1
55235523
}

src/repl/scala/tools/nsc/interpreter/ReplGlobal.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ trait ReplGlobal extends Global {
5252
def newPhase(_prev: Phase): StdPhase = new StdPhase(_prev) {
5353
def apply(unit: CompilationUnit) {
5454
repldbg("Running replPhase on " + unit.body)
55-
// pluginsEnterSym(newNamer(rootContext(unit)), unit.body)
55+
// newNamer(rootContext(unit)).enterSym(unit.body)
5656
}
5757
}
5858
}

0 commit comments

Comments
 (0)