Permalink
Browse files

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.
  • Loading branch information...
1 parent 4d92aec commit 87913661e199e3894190b7b8aa0900d7237feec0 @xeno-by xeno-by committed Dec 9, 2013
@@ -19,8 +19,13 @@ trait MacroRuntimes extends JavaReflectionRuntimes with ScalaReflectionRuntimes
* @return Requested runtime if macro implementation can be loaded successfully from either of the mirrors,
* `null` otherwise.
*/
+ def macroRuntime(expandee: Tree): MacroRuntime = pluginsMacroRuntime(expandee)
+
+ /** Default implementation of `macroRuntime`.
+ * Can be overridden by analyzer plugins (see AnalyzerPlugins.pluginsMacroRuntime for more details)
+ */
private val macroRuntimesCache = perRunCaches.newWeakMap[Symbol, MacroRuntime]
- def macroRuntime(expandee: Tree): MacroRuntime = {
+ def standardMacroRuntime(expandee: Tree): MacroRuntime = {
val macroDef = expandee.symbol
macroLogVerbose(s"looking for macro implementation: $macroDef")
if (fastTrack contains macroDef) {
@@ -40,7 +40,7 @@ trait Analyzer extends AnyRef
override def keepsTypeParams = false
def apply(unit: CompilationUnit) {
- pluginsEnterSym(newNamer(rootContext(unit)), unit.body)
+ newNamer(rootContext(unit)).enterSym(unit.body)
}
}
}
@@ -180,7 +180,7 @@ trait AnalyzerPlugins { self: Analyzer =>
* Typechecks the right-hand side of a macro definition (which typically features
* a mere reference to a macro implementation).
*
- * Default implementation provided in `self.typedMacroBody` makes sure that the rhs
+ * Default implementation provided in `self.standardTypedMacroBody` makes sure that the rhs
* resolves to a reference to a method in either a static object or a macro bundle,
* verifies that the referred method is compatible with the macro def and upon success
* attaches a macro impl binding to the macro def's symbol.
@@ -193,7 +193,7 @@ trait AnalyzerPlugins { self: Analyzer =>
* Expands an application of a def macro (i.e. of a symbol that has the MACRO flag set),
* possibly using the current typer mode and the provided prototype.
*
- * Default implementation provided in `self.macroExpand` figures out whether the `expandee`
+ * Default implementation provided in `self.standardMacroExpand` figures out whether the `expandee`
* needs to be expanded right away or its expansion has to be delayed until all undetermined
* parameters are inferred, then loads the macro implementation using `self.pluginsMacroRuntime`,
* prepares the invocation arguments for the macro implementation using `self.pluginsMacroArgs`,
@@ -211,7 +211,7 @@ trait AnalyzerPlugins { self: Analyzer =>
/**
* Computes the arguments that need to be passed to the macro impl corresponding to a particular expandee.
*
- * Default implementation provided in `self.macroArgs` instantiates a `scala.reflect.macros.contexts.Context`,
+ * Default implementation provided in `self.standardMacroArgs` instantiates a `scala.reflect.macros.contexts.Context`,
* gathers type and value arguments of the macro application and throws them together into `MacroArgs`.
*
* $nonCumulativeReturnValueDoc.
@@ -221,7 +221,7 @@ trait AnalyzerPlugins { self: Analyzer =>
/**
* Summons a function that encapsulates macro implementation invocations for a particular expandee.
*
- * Default implementation provided in `self.macroRuntime` returns a function that
+ * Default implementation provided in `self.standardMacroRuntime` returns a function that
* loads the macro implementation binding from the macro definition symbol,
* then uses either Java or Scala reflection to acquire the method that corresponds to the impl,
* and then reflectively calls into that method.
@@ -233,7 +233,7 @@ trait AnalyzerPlugins { self: Analyzer =>
/**
* Creates a symbol for the given tree in lexical context encapsulated by the given namer.
*
- * Default implementation provided in `namer.enterSym` handles MemberDef's and Imports,
+ * Default implementation provided in `namer.standardEnterSym` handles MemberDef's and Imports,
* doing nothing for other trees (DocDef's are seen through and rewrapped). Typical implementation
* of `enterSym` for a particular tree flavor creates a corresponding symbol, assigns it to the tree,
* enters the symbol into scope and then might even perform some code generation.
@@ -245,7 +245,7 @@ trait AnalyzerPlugins { self: Analyzer =>
/**
* Makes sure that for the given class definition, there exists a companion object definition.
*
- * Default implementation provided in `namer.ensureCompanionObject` looks up a companion symbol for the class definition
+ * Default implementation provided in `namer.standardEnsureCompanionObject` looks up a companion symbol for the class definition
* and then checks whether the resulting symbol exists or not. If it exists, then nothing else is done.
* If not, a synthetic object definition is created using the provided factory, which is then entered into namer's scope.
*
@@ -371,54 +371,56 @@ trait AnalyzerPlugins { self: Analyzer =>
def pluginsTypedMacroBody(typer: Typer, ddef: DefDef): Tree = invoke(new NonCumulativeOp[Tree] {
def position = ddef.pos
def description = "typecheck this macro definition"
- def default = typedMacroBody(typer, ddef)
+ def default = standardTypedMacroBody(typer, ddef)
def custom(plugin: MacroPlugin) = plugin.pluginsTypedMacroBody(typer, ddef)
})
/** @see MacroPlugin.pluginsMacroExpand */
def pluginsMacroExpand(typer: Typer, expandee: Tree, mode: Mode, pt: Type): Tree = invoke(new NonCumulativeOp[Tree] {
def position = expandee.pos
def description = "expand this macro application"
- def default = macroExpand(typer, expandee, mode, pt)
+ def default = standardMacroExpand(typer, expandee, mode, pt)
def custom(plugin: MacroPlugin) = plugin.pluginsMacroExpand(typer, expandee, mode, pt)
})
/** @see MacroPlugin.pluginsMacroArgs */
def pluginsMacroArgs(typer: Typer, expandee: Tree): MacroArgs = invoke(new NonCumulativeOp[MacroArgs] {
def position = expandee.pos
def description = "compute macro arguments for this macro application"
- def default = macroArgs(typer, expandee)
+ def default = standardMacroArgs(typer, expandee)
def custom(plugin: MacroPlugin) = plugin.pluginsMacroArgs(typer, expandee)
})
/** @see MacroPlugin.pluginsMacroRuntime */
def pluginsMacroRuntime(expandee: Tree): MacroRuntime = invoke(new NonCumulativeOp[MacroRuntime] {
def position = expandee.pos
def description = "compute macro runtime for this macro application"
- def default = macroRuntime(expandee)
+ def default = standardMacroRuntime(expandee)
def custom(plugin: MacroPlugin) = plugin.pluginsMacroRuntime(expandee)
})
/** @see MacroPlugin.pluginsEnterSym */
- def pluginsEnterSym(namer: Namer, tree: Tree): Context = invoke(new NonCumulativeOp[Context] {
- def position = tree.pos
- def description = "enter a symbol for this tree"
- def default = namer.enterSym(tree)
- def custom(plugin: MacroPlugin) = {
- val hasExistingSym = tree.symbol != NoSymbol
- val result = plugin.pluginsEnterSym(namer, tree)
- if (result && hasExistingSym) Some(namer.context)
- else if (result && tree.isInstanceOf[Import]) Some(namer.context.make(tree))
- else if (result) Some(namer.context)
- else None
- }
- })
+ def pluginsEnterSym(namer: Namer, tree: Tree): Context =
+ if (macroPlugins.isEmpty) namer.standardEnterSym(tree)
+ else invoke(new NonCumulativeOp[Context] {
+ def position = tree.pos
+ def description = "enter a symbol for this tree"
+ def default = namer.standardEnterSym(tree)
+ def custom(plugin: MacroPlugin) = {
+ val hasExistingSym = tree.symbol != NoSymbol
+ val result = plugin.pluginsEnterSym(namer, tree)
+ if (result && hasExistingSym) Some(namer.context)
+ else if (result && tree.isInstanceOf[Import]) Some(namer.context.make(tree))
+ else if (result) Some(namer.context)
+ else None
+ }
+ })
/** @see MacroPlugin.pluginsEnsureCompanionObject */
def pluginsEnsureCompanionObject(namer: Namer, cdef: ClassDef, creator: ClassDef => Tree = companionModuleDef(_)): Symbol = invoke(new NonCumulativeOp[Symbol] {
def position = cdef.pos
def description = "enter a companion symbol for this tree"
- def default = namer.ensureCompanionObject(cdef, creator)
+ def default = namer.standardEnsureCompanionObject(cdef, creator)
def custom(plugin: MacroPlugin) = plugin.pluginsEnsureCompanionObject(namer, cdef, creator)
})
@@ -314,7 +314,12 @@ trait Macros extends FastTrack with MacroRuntimes with Traces with Helpers {
* @return Macro impl reference for the given macro definition if everything is okay.
* EmptyTree if an error occurs.
*/
- def typedMacroBody(typer: Typer, macroDdef: DefDef): Tree = {
+ def typedMacroBody(typer: Typer, macroDdef: DefDef): Tree = pluginsTypedMacroBody(typer, macroDdef)
+
+ /** Default implementation of `typedMacroBody`.
+ * Can be overridden by analyzer plugins (see AnalyzerPlugins.pluginsTypedMacroBody for more details)
+ */
+ def standardTypedMacroBody(typer: Typer, macroDdef: DefDef): Tree = {
val macroDef = macroDdef.symbol
assert(macroDef.isMacro, macroDdef)
@@ -359,8 +364,12 @@ trait Macros extends FastTrack with MacroRuntimes with Traces with Helpers {
/** Calculate the arguments to pass to a macro implementation when expanding the provided tree.
*/
case class MacroArgs(c: MacroContext, others: List[Any])
+ def macroArgs(typer: Typer, expandee: Tree): MacroArgs = pluginsMacroArgs(typer, expandee)
- def macroArgs(typer: Typer, expandee: Tree): MacroArgs = {
+ /** Default implementation of `macroArgs`.
+ * Can be overridden by analyzer plugins (see AnalyzerPlugins.pluginsMacroArgs for more details)
+ */
+ def standardMacroArgs(typer: Typer, expandee: Tree): MacroArgs = {
val macroDef = expandee.symbol
val paramss = macroDef.paramss
val treeInfo.Applied(core, targs, argss) = expandee
@@ -561,7 +570,7 @@ trait Macros extends FastTrack with MacroRuntimes with Traces with Helpers {
onFailure(typer.infer.setError(expandee))
} else try {
val expanded = {
- val runtime = pluginsMacroRuntime(expandee)
+ val runtime = macroRuntime(expandee)
if (runtime != null) macroExpandWithRuntime(typer, expandee, runtime)
else macroExpandWithoutRuntime(typer, expandee)
}
@@ -688,7 +697,7 @@ trait Macros extends FastTrack with MacroRuntimes with Traces with Helpers {
else {
forced += delayed
typer.infer.inferExprInstance(delayed, typer.context.extractUndetparams(), outerPt, keepNothings = false)
- pluginsMacroExpand(typer, delayed, mode, outerPt)
+ macroExpand(typer, delayed, mode, outerPt)
}
} else delayed
}
@@ -698,7 +707,12 @@ trait Macros extends FastTrack with MacroRuntimes with Traces with Helpers {
/** Expands a term macro used in apply role as `M(2)(3)` in `val x = M(2)(3)`.
* @see DefMacroExpander
*/
- def macroExpand(typer: Typer, expandee: Tree, mode: Mode, pt: Type): Tree = {
+ def macroExpand(typer: Typer, expandee: Tree, mode: Mode, pt: Type): Tree = pluginsMacroExpand(typer, expandee, mode, pt)
+
+ /** Default implementation of `macroExpand`.
+ * Can be overridden by analyzer plugins (see AnalyzerPlugins.pluginsMacroExpand for more details)
+ */
+ def standardMacroExpand(typer: Typer, expandee: Tree, mode: Mode, pt: Type): Tree = {
val expander = new DefMacroExpander(typer, expandee, mode, pt)
expander(expandee)
}
@@ -730,12 +744,12 @@ trait Macros extends FastTrack with MacroRuntimes with Traces with Helpers {
case (false, true) =>
macroLogLite("macro expansion is delayed: %s".format(expandee))
delayed += expandee -> undetparams
- expandee updateAttachment MacroRuntimeAttachment(delayed = true, typerContext = typer.context, macroContext = Some(pluginsMacroArgs(typer, expandee).c))
+ expandee updateAttachment MacroRuntimeAttachment(delayed = true, typerContext = typer.context, macroContext = Some(macroArgs(typer, expandee).c))
Delay(expandee)
case (false, false) =>
import typer.TyperErrorGen._
macroLogLite("performing macro expansion %s at %s".format(expandee, expandee.pos))
- val args = pluginsMacroArgs(typer, expandee)
+ val args = macroArgs(typer, expandee)
try {
val numErrors = reporter.ERROR.count
def hasNewErrors = reporter.ERROR.count > numErrors
@@ -850,7 +864,7 @@ trait Macros extends FastTrack with MacroRuntimes with Traces with Helpers {
context.implicitsEnabled = typer.context.implicitsEnabled
context.enrichmentEnabled = typer.context.enrichmentEnabled
context.macrosEnabled = typer.context.macrosEnabled
- pluginsMacroExpand(newTyper(context), tree, EXPRmode, WildcardType)
+ macroExpand(newTyper(context), tree, EXPRmode, WildcardType)
case _ =>
tree
})
@@ -243,7 +243,12 @@ trait Namers extends MethodSynthesis {
validate(sym2.companionClass)
}
- def enterSym(tree: Tree): Context = {
+ def enterSym(tree: Tree): Context = pluginsEnterSym(this, tree)
+
+ /** Default implementation of `enterSym`.
+ * Can be overridden by analyzer plugins (see AnalyzerPlugins.pluginsEnterSym for more details)
+ */
+ def standardEnterSym(tree: Tree): Context = {
def dispatch() = {
var returnContext = this.context
tree match {
@@ -253,7 +258,7 @@ trait Namers extends MethodSynthesis {
case tree @ ValDef(_, _, _, _) => enterValDef(tree)
case tree @ DefDef(_, _, _, _, _, _) => enterDefDef(tree)
case tree @ TypeDef(_, _, _, _) => enterTypeDef(tree)
- case DocDef(_, defn) => pluginsEnterSym(this, defn)
+ case DocDef(_, defn) => enterSym(defn)
case tree @ Import(_, _) =>
assignSymbol(tree)
returnContext = context.make(tree)
@@ -452,7 +457,7 @@ trait Namers extends MethodSynthesis {
def enterSyms(trees: List[Tree]): Namer = {
trees.foldLeft(this: Namer) { (namer, t) =>
- val ctx = pluginsEnterSym(namer, t)
+ val ctx = namer enterSym t
// for Import trees, enterSym returns a changed context, so we need a new namer
if (ctx eq namer.context) namer
else newNamer(ctx)
@@ -466,7 +471,13 @@ trait Namers extends MethodSynthesis {
* class definition tree.
* @return the companion object symbol.
*/
- def ensureCompanionObject(cdef: ClassDef, creator: ClassDef => Tree = companionModuleDef(_)): Symbol = {
+ def ensureCompanionObject(cdef: ClassDef, creator: ClassDef => Tree = companionModuleDef(_)): Symbol =
+ pluginsEnsureCompanionObject(this, cdef, creator)
+
+ /** Default implementation of `ensureCompanionObject`.
+ * Can be overridden by analyzer plugins (see AnalyzerPlugins.pluginsEnsureCompanionObject for more details)
+ */
+ def standardEnsureCompanionObject(cdef: ClassDef, creator: ClassDef => Tree = companionModuleDef(_)): Symbol = {
val m = companionSymbolOf(cdef.symbol, context)
// @luc: not sure why "currentRun.compiles(m)" is needed, things breaks
// otherwise. documentation welcome.
@@ -662,15 +673,15 @@ trait Namers extends MethodSynthesis {
tree.symbol setInfo completerOf(tree)
if (mods.isCase) {
- val m = pluginsEnsureCompanionObject(this, tree, caseModuleDef)
+ val m = ensureCompanionObject(tree, caseModuleDef)
m.moduleClass.updateAttachment(new ClassForCaseCompanionAttachment(tree))
}
val hasDefault = impl.body exists {
case DefDef(_, nme.CONSTRUCTOR, _, vparamss, _, _) => mexists(vparamss)(_.mods.hasDefault)
case _ => false
}
if (hasDefault) {
- val m = pluginsEnsureCompanionObject(this, tree)
+ val m = ensureCompanionObject(tree)
m.updateAttachment(new ConstructorDefaultsAttachment(tree, null))
}
val owner = tree.symbol.owner
@@ -697,7 +708,7 @@ trait Namers extends MethodSynthesis {
def enterIfNotThere(sym: Symbol) { }
def enterSyntheticSym(tree: Tree): Symbol = {
- pluginsEnterSym(this, tree)
+ enterSym(tree)
context.unit.synthetics(tree.symbol) = tree
tree.symbol
}
@@ -931,7 +942,7 @@ trait Namers extends MethodSynthesis {
log("Ensuring companion for derived value class " + cdef.name + " at " + cdef.pos.show)
clazz setFlag FINAL
// Don't force the owner's info lest we create cycles as in SI-6357.
- pluginsEnsureCompanionObject(enclosingNamerWithScope(clazz.owner.rawInfo.decls), cdef)
+ enclosingNamerWithScope(clazz.owner.rawInfo.decls).ensureCompanionObject(cdef)
}
pluginsTp
}
@@ -1112,7 +1112,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
if (tree.isType)
adaptType()
else if (mode.typingExprNotFun && treeInfo.isMacroApplication(tree) && !isMacroExpansionSuppressed(tree))
- pluginsMacroExpand(this, tree, mode, pt)
+ macroExpand(this, tree, mode, pt)
else if (mode.typingConstructorPattern)
typedConstructorPattern(tree, pt)
else if (shouldInsertApply(tree))
@@ -1863,8 +1863,8 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
}
protected def enterSym(txt: Context, tree: Tree): Context =
- if (txt eq context) pluginsEnterSym(namer, tree)
- else pluginsEnterSym(newNamer(txt), tree)
+ if (txt eq context) namer enterSym tree
+ else newNamer(txt) enterSym tree
/** <!-- 2 --> Check that inner classes do not inherit from Annotation
*/
@@ -2213,7 +2213,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
} else if (meth.isMacro) {
// typechecking macro bodies is sort of unconventional
// that's why we employ our custom typing scheme orchestrated outside of the typer
- transformedOr(ddef.rhs, pluginsTypedMacroBody(this, ddef))
+ transformedOr(ddef.rhs, typedMacroBody(this, ddef))
} else {
transformedOrTyped(ddef.rhs, EXPRmode, tpt1.tpe)
}
@@ -3812,7 +3812,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
protected def typedExistentialTypeTree(tree: ExistentialTypeTree, mode: Mode): Tree = {
for (wc <- tree.whereClauses)
- if (wc.symbol == NoSymbol) { pluginsEnterSym(namer, wc); wc.symbol setFlag EXISTENTIAL }
+ if (wc.symbol == NoSymbol) { namer enterSym wc; wc.symbol setFlag EXISTENTIAL }
else context.scope enter wc.symbol
val whereClauses1 = typedStats(tree.whereClauses, context.owner)
for (vd @ ValDef(_, _, _, _) <- whereClauses1)
@@ -5517,7 +5517,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
// here we guard against this case
transformed(ddef.rhs)
} else {
- val rhs1 = pluginsTypedMacroBody(this, ddef)
+ val rhs1 = typedMacroBody(this, ddef)
transformed(ddef.rhs) = rhs1
rhs1
}
@@ -52,7 +52,7 @@ trait ReplGlobal extends Global {
def newPhase(_prev: Phase): StdPhase = new StdPhase(_prev) {
def apply(unit: CompilationUnit) {
repldbg("Running replPhase on " + unit.body)
- // pluginsEnterSym(newNamer(rootContext(unit)), unit.body)
+ // newNamer(rootContext(unit)).enterSym(unit.body)
}
}
}
Oops, something went wrong.

0 comments on commit 8791366

Please sign in to comment.