summaryrefslogtreecommitdiff
path: root/src/compiler
diff options
context:
space:
mode:
authorEugene Burmako <xeno.by@gmail.com>2014-01-08 11:17:08 -0800
committerEugene Burmako <xeno.by@gmail.com>2014-01-08 11:17:08 -0800
commit4e4c15177b92ef25f098277dcce3f874c24640b2 (patch)
treef46a594ffb47c53cde502d3e172dd4b1c6c2e109 /src/compiler
parentada8d9156baad2d8a24c1a40e032eb4bc7154bac (diff)
parent87913661e199e3894190b7b8aa0900d7237feec0 (diff)
downloadscala-4e4c15177b92ef25f098277dcce3f874c24640b2.tar.gz
scala-4e4c15177b92ef25f098277dcce3f874c24640b2.tar.bz2
scala-4e4c15177b92ef25f098277dcce3f874c24640b2.zip
Merge pull request #3235 from xeno-by/topic/macro-plugin-interface
new hooks in AnalyzerPlugins to enable macro experimentation
Diffstat (limited to 'src/compiler')
-rw-r--r--src/compiler/scala/reflect/macros/runtime/MacroRuntimes.scala8
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/AnalyzerPlugins.scala283
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala5
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Macros.scala285
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Namers.scala40
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Typers.scala60
6 files changed, 455 insertions, 226 deletions
diff --git a/src/compiler/scala/reflect/macros/runtime/MacroRuntimes.scala b/src/compiler/scala/reflect/macros/runtime/MacroRuntimes.scala
index 7de3341304..14b0c6baba 100644
--- a/src/compiler/scala/reflect/macros/runtime/MacroRuntimes.scala
+++ b/src/compiler/scala/reflect/macros/runtime/MacroRuntimes.scala
@@ -19,8 +19,14 @@ 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(macroDef: Symbol): MacroRuntime = {
+ def standardMacroRuntime(expandee: Tree): MacroRuntime = {
+ val macroDef = expandee.symbol
macroLogVerbose(s"looking for macro implementation: $macroDef")
if (fastTrack contains macroDef) {
macroLogVerbose("macro expansion is serviced by a fast track")
diff --git a/src/compiler/scala/tools/nsc/typechecker/AnalyzerPlugins.scala b/src/compiler/scala/tools/nsc/typechecker/AnalyzerPlugins.scala
index 54e4fefc15..fa6e5399eb 100644
--- a/src/compiler/scala/tools/nsc/typechecker/AnalyzerPlugins.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/AnalyzerPlugins.scala
@@ -13,7 +13,6 @@ package typechecker
trait AnalyzerPlugins { self: Analyzer =>
import global._
-
trait AnalyzerPlugin {
/**
* Selectively activate this analyzer plugin, e.g. according to the compiler phase.
@@ -156,6 +155,117 @@ trait AnalyzerPlugins { self: Analyzer =>
def pluginsTypedReturn(tpe: Type, typer: Typer, tree: Return, pt: Type): Type = tpe
}
+ /**
+ * @define nonCumulativeReturnValueDoc Returns `None` if the plugin doesn't want to customize the default behavior
+ * or something else if the plugin knows better that the implementation provided in scala-compiler.jar.
+ * If multiple plugins return a non-empty result, it's going to be a compilation error.
+ */
+ trait MacroPlugin {
+ /**
+ * Selectively activate this analyzer plugin, e.g. according to the compiler phase.
+ *
+ * Note that the current phase can differ from the global compiler phase (look for `enteringPhase`
+ * invocations in the compiler). For instance, lazy types created by the UnPickler are completed
+ * at the phase in which their symbol is created. Observations show that this can even be the
+ * parser phase. Since symbol completion can trigger subtyping, typing etc, your plugin might
+ * need to be active also in phases other than namer and typer.
+ *
+ * Typically, this method can be implemented as
+ *
+ * global.phase.id < global.currentRun.picklerPhase.id
+ */
+ def isActive(): Boolean = true
+
+ /**
+ * Typechecks the right-hand side of a macro definition (which typically features
+ * a mere reference to a macro implementation).
+ *
+ * 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.
+ *
+ * $nonCumulativeReturnValueDoc.
+ */
+ def pluginsTypedMacroBody(typer: Typer, ddef: DefDef): Option[Tree] = None
+
+ /**
+ * 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.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`,
+ * and finally calls into the macro implementation. After the call returns, it typechecks
+ * the expansion and performs some bookkeeping.
+ *
+ * This method is typically implemented if your plugin requires significant changes to the macro engine.
+ * If you only need to customize the macro context, consider implementing `pluginsMacroArgs`.
+ * If you only need to customize how macro implementation are invoked, consider going for `pluginsMacroRuntime`.
+ *
+ * $nonCumulativeReturnValueDoc.
+ */
+ def pluginsMacroExpand(typer: Typer, expandee: Tree, mode: Mode, pt: Type): Option[Tree] = None
+
+ /**
+ * Computes the arguments that need to be passed to the macro impl corresponding to a particular expandee.
+ *
+ * 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.
+ */
+ def pluginsMacroArgs(typer: Typer, expandee: Tree): Option[MacroArgs] = None
+
+ /**
+ * Summons a function that encapsulates macro implementation invocations for a particular expandee.
+ *
+ * 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.
+ *
+ * $nonCumulativeReturnValueDoc.
+ */
+ def pluginsMacroRuntime(expandee: Tree): Option[MacroRuntime] = None
+
+ /**
+ * Creates a symbol for the given tree in lexical context encapsulated by the given namer.
+ *
+ * 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.
+ *
+ * $nonCumulativeReturnValueDoc.
+ */
+ def pluginsEnterSym(namer: Namer, tree: Tree): Boolean = false
+
+ /**
+ * Makes sure that for the given class definition, there exists a companion object 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.
+ *
+ * $nonCumulativeReturnValueDoc.
+ */
+ def pluginsEnsureCompanionObject(namer: Namer, cdef: ClassDef, creator: ClassDef => Tree = companionModuleDef(_)): Option[Symbol] = None
+
+ /**
+ * Prepares a list of statements for being typechecked by performing domain-specific type-agnostic code synthesis.
+ *
+ * Trees passed into this method are going to be named, but not typed.
+ * In particular, you can rely on the compiler having called `enterSym` on every stat prior to passing calling this method.
+ *
+ * Default implementation does nothing. Current approaches to code syntheses (generation of underlying fields
+ * for getters/setters, creation of companion objects for case classes, etc) are too disparate and ad-hoc
+ * to be treated uniformly, so I'm leaving this for future work.
+ */
+ def pluginsEnterStats(typer: Typer, stats: List[Tree]): List[Tree] = stats
+ }
+
/** A list of registered analyzer plugins */
@@ -167,59 +277,158 @@ trait AnalyzerPlugins { self: Analyzer =>
analyzerPlugins = plugin :: analyzerPlugins
}
+ private abstract class CumulativeOp[T] {
+ def default: T
+ def accumulate: (T, AnalyzerPlugin) => T
+ }
+
+ private def invoke[T](op: CumulativeOp[T]): T = {
+ if (analyzerPlugins.isEmpty) op.default
+ else analyzerPlugins.foldLeft(op.default)((current, plugin) =>
+ if (!plugin.isActive()) current else op.accumulate(current, plugin))
+ }
/** @see AnalyzerPlugin.pluginsPt */
def pluginsPt(pt: Type, typer: Typer, tree: Tree, mode: Mode): Type =
+ // performance opt
if (analyzerPlugins.isEmpty) pt
- else analyzerPlugins.foldLeft(pt)((pt, plugin) =>
- if (!plugin.isActive()) pt else plugin.pluginsPt(pt, typer, tree, mode))
+ else invoke(new CumulativeOp[Type] {
+ def default = pt
+ def accumulate = (pt, p) => p.pluginsPt(pt, typer, tree, mode)
+ })
/** @see AnalyzerPlugin.pluginsTyped */
- def pluginsTyped(tpe: Type, typer: Typer, tree: Tree, mode: Mode, pt: Type): Type = {
- // support deprecated methods in annotation checkers
- val annotCheckersTpe = addAnnotations(tree, tpe)
- if (analyzerPlugins.isEmpty) annotCheckersTpe
- else analyzerPlugins.foldLeft(annotCheckersTpe)((tpe, plugin) =>
- if (!plugin.isActive()) tpe else plugin.pluginsTyped(tpe, typer, tree, mode, pt))
- }
+ def pluginsTyped(tpe: Type, typer: Typer, tree: Tree, mode: Mode, pt: Type): Type =
+ // performance opt
+ if (analyzerPlugins.isEmpty) addAnnotations(tree, tpe)
+ else invoke(new CumulativeOp[Type] {
+ // support deprecated methods in annotation checkers
+ def default = addAnnotations(tree, tpe)
+ def accumulate = (tpe, p) => p.pluginsTyped(tpe, typer, tree, mode, pt)
+ })
/** @see AnalyzerPlugin.pluginsTypeSig */
- def pluginsTypeSig(tpe: Type, typer: Typer, defTree: Tree, pt: Type): Type =
- if (analyzerPlugins.isEmpty) tpe
- else analyzerPlugins.foldLeft(tpe)((tpe, plugin) =>
- if (!plugin.isActive()) tpe else plugin.pluginsTypeSig(tpe, typer, defTree, pt))
+ def pluginsTypeSig(tpe: Type, typer: Typer, defTree: Tree, pt: Type): Type = invoke(new CumulativeOp[Type] {
+ def default = tpe
+ def accumulate = (tpe, p) => p.pluginsTypeSig(tpe, typer, defTree, pt)
+ })
/** @see AnalyzerPlugin.pluginsTypeSigAccessor */
- def pluginsTypeSigAccessor(tpe: Type, typer: Typer, tree: ValDef, sym: Symbol): Type =
- if (analyzerPlugins.isEmpty) tpe
- else analyzerPlugins.foldLeft(tpe)((tpe, plugin) =>
- if (!plugin.isActive()) tpe else plugin.pluginsTypeSigAccessor(tpe, typer, tree, sym))
+ def pluginsTypeSigAccessor(tpe: Type, typer: Typer, tree: ValDef, sym: Symbol): Type = invoke(new CumulativeOp[Type] {
+ def default = tpe
+ def accumulate = (tpe, p) => p.pluginsTypeSigAccessor(tpe, typer, tree, sym)
+ })
/** @see AnalyzerPlugin.canAdaptAnnotations */
- def canAdaptAnnotations(tree: Tree, typer: Typer, mode: Mode, pt: Type): Boolean = {
+ def canAdaptAnnotations(tree: Tree, typer: Typer, mode: Mode, pt: Type): Boolean = invoke(new CumulativeOp[Boolean] {
// support deprecated methods in annotation checkers
- val annotCheckersExists = global.canAdaptAnnotations(tree, mode, pt)
- annotCheckersExists || {
- if (analyzerPlugins.isEmpty) false
- else analyzerPlugins.exists(plugin =>
- plugin.isActive() && plugin.canAdaptAnnotations(tree, typer, mode, pt))
- }
- }
+ def default = global.canAdaptAnnotations(tree, mode, pt)
+ def accumulate = (curr, p) => curr || p.canAdaptAnnotations(tree, typer, mode, pt)
+ })
/** @see AnalyzerPlugin.adaptAnnotations */
- def adaptAnnotations(tree: Tree, typer: Typer, mode: Mode, pt: Type): Tree = {
+ def adaptAnnotations(tree: Tree, typer: Typer, mode: Mode, pt: Type): Tree = invoke(new CumulativeOp[Tree] {
// support deprecated methods in annotation checkers
- val annotCheckersTree = global.adaptAnnotations(tree, mode, pt)
- if (analyzerPlugins.isEmpty) annotCheckersTree
- else analyzerPlugins.foldLeft(annotCheckersTree)((tree, plugin) =>
- if (!plugin.isActive()) tree else plugin.adaptAnnotations(tree, typer, mode, pt))
- }
+ def default = global.adaptAnnotations(tree, mode, pt)
+ def accumulate = (tree, p) => p.adaptAnnotations(tree, typer, mode, pt)
+ })
/** @see AnalyzerPlugin.pluginsTypedReturn */
- def pluginsTypedReturn(tpe: Type, typer: Typer, tree: Return, pt: Type): Type = {
- val annotCheckersType = adaptTypeOfReturn(tree.expr, pt, tpe)
- if (analyzerPlugins.isEmpty) annotCheckersType
- else analyzerPlugins.foldLeft(annotCheckersType)((tpe, plugin) =>
- if (!plugin.isActive()) tpe else plugin.pluginsTypedReturn(tpe, typer, tree, pt))
+ def pluginsTypedReturn(tpe: Type, typer: Typer, tree: Return, pt: Type): Type = invoke(new CumulativeOp[Type] {
+ def default = adaptTypeOfReturn(tree.expr, pt, tpe)
+ def accumulate = (tpe, p) => p.pluginsTypedReturn(tpe, typer, tree, pt)
+ })
+
+ /** A list of registered macro plugins */
+ private var macroPlugins: List[MacroPlugin] = Nil
+
+ /** Registers a new macro plugin */
+ def addMacroPlugin(plugin: MacroPlugin) {
+ if (!macroPlugins.contains(plugin))
+ macroPlugins = plugin :: macroPlugins
+ }
+
+ private abstract class NonCumulativeOp[T] {
+ def position: Position
+ def description: String
+ def default: T
+ def custom(plugin: MacroPlugin): Option[T]
+ }
+
+ private def invoke[T](op: NonCumulativeOp[T]): T = {
+ if (macroPlugins.isEmpty) op.default
+ else {
+ val results = macroPlugins.filter(_.isActive()).map(plugin => (plugin, op.custom(plugin)))
+ results.flatMap { case (p, Some(result)) => Some((p, result)); case _ => None } match {
+ case (p1, _) :: (p2, _) :: _ => typer.context.error(op.position, s"both $p1 and $p2 want to ${op.description}"); op.default
+ case (_, custom) :: Nil => custom
+ case Nil => op.default
+ }
+ }
+ }
+
+ /** @see MacroPlugin.pluginsTypedMacroBody */
+ def pluginsTypedMacroBody(typer: Typer, ddef: DefDef): Tree = invoke(new NonCumulativeOp[Tree] {
+ def position = ddef.pos
+ def description = "typecheck this macro definition"
+ 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 = 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 = 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 = standardMacroRuntime(expandee)
+ def custom(plugin: MacroPlugin) = plugin.pluginsMacroRuntime(expandee)
+ })
+
+ /** @see MacroPlugin.pluginsEnterSym */
+ 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.standardEnsureCompanionObject(cdef, creator)
+ def custom(plugin: MacroPlugin) = plugin.pluginsEnsureCompanionObject(namer, cdef, creator)
+ })
+
+ /** @see MacroPlugin.pluginsEnterStats */
+ def pluginsEnterStats(typer: Typer, stats: List[Tree]): List[Tree] = {
+ // performance opt
+ if (macroPlugins.isEmpty) stats
+ else macroPlugins.foldLeft(stats)((current, plugin) =>
+ if (!plugin.isActive()) current else plugin.pluginsEnterStats(typer, stats))
}
}
diff --git a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala
index 719d04a7f9..352ae6c8b0 100644
--- a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala
@@ -730,6 +730,11 @@ trait ContextErrors {
issueNormalTypeError(expandee, s"macro in $role role can only expand into $allowedExpansions")
}
+ def MacroIncompatibleEngineError(macroEngine: String) = {
+ val message = s"macro cannot be expanded, because it was compiled by an incompatible macro engine $macroEngine"
+ issueNormalTypeError(lastTreeToTyper, message)
+ }
+
case object MacroExpansionException extends Exception with scala.util.control.ControlThrowable
protected def macroExpansionError(expandee: Tree, msg: String, pos: Position = NoPosition) = {
diff --git a/src/compiler/scala/tools/nsc/typechecker/Macros.scala b/src/compiler/scala/tools/nsc/typechecker/Macros.scala
index 0d46a96b81..006ab792fc 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Macros.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Macros.scala
@@ -123,16 +123,15 @@ trait Macros extends FastTrack with MacroRuntimes with Traces with Helpers {
*
* @scala.reflect.macros.internal.macroImpl(
* `macro`(
+ * "macroEngine" = <current macro engine>,
* "isBundle" = false,
* "isBlackbox" = true,
* "signature" = List(Other),
* "methodName" = "impl",
- * "versionFormat" = <current version format>,
* "className" = "Macros$"))
*/
+ def macroEngine = "v7.0 (implemented in Scala 2.11.0-M8)"
object MacroImplBinding {
- val versionFormat = 6.0
-
def pickleAtom(obj: Any): Tree =
obj match {
case list: List[_] => Apply(Ident(ListModule), list map pickleAtom)
@@ -183,12 +182,12 @@ trait Macros extends FastTrack with MacroRuntimes with Traces with Helpers {
}
val payload = List[(String, Any)](
- "versionFormat" -> versionFormat,
- "isBundle" -> isBundle,
- "isBlackbox" -> isBlackbox,
- "className" -> className,
- "methodName" -> macroImpl.name.toString,
- "signature" -> signature
+ "macroEngine" -> macroEngine,
+ "isBundle" -> isBundle,
+ "isBlackbox" -> isBlackbox,
+ "className" -> className,
+ "methodName" -> macroImpl.name.toString,
+ "signature" -> signature
)
// the shape of the nucleus is chosen arbitrarily. it doesn't carry any payload.
@@ -237,8 +236,8 @@ trait Macros extends FastTrack with MacroRuntimes with Traces with Helpers {
raw.asInstanceOf[T]
}
- val pickleVersionFormat = unpickle("versionFormat", classOf[Double])
- if (versionFormat != pickleVersionFormat) fail(s"expected version format $versionFormat, actual $pickleVersionFormat")
+ val macroEngine = unpickle("macroEngine", classOf[String])
+ if (self.macroEngine != macroEngine) typer.TyperErrorGen.MacroIncompatibleEngineError(macroEngine)
val isBundle = unpickle("isBundle", classOf[Boolean])
val isBlackbox = unpickle("isBlackbox", classOf[Boolean])
@@ -315,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)
@@ -360,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)
- private 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
@@ -473,10 +481,10 @@ trait Macros extends FastTrack with MacroRuntimes with Traces with Helpers {
/** Keeps track of macros in-flight.
* See more informations in comments to `openMacros` in `scala.reflect.macros.WhiteboxContext`.
*/
- private var _openMacros = List[MacroContext]()
+ var _openMacros = List[MacroContext]()
def openMacros = _openMacros
- private def pushMacroContext(c: MacroContext) = _openMacros ::= c
- private def popMacroContext() = _openMacros = _openMacros.tail
+ def pushMacroContext(c: MacroContext) = _openMacros ::= c
+ def popMacroContext() = _openMacros = _openMacros.tail
def enclosingMacroPosition = openMacros map (_.macroApplication.pos) find (_ ne NoPosition) getOrElse NoPosition
/** Describes the role that the macro expandee is performing.
@@ -527,7 +535,7 @@ trait Macros extends FastTrack with MacroRuntimes with Traces with Helpers {
* the expandee with an error marker set if the expansion has been cancelled due malformed arguments or implementation
* the expandee with an error marker set if there has been an error
*/
- private abstract class MacroExpander[Result: ClassTag](val role: MacroRole, val typer: Typer, val expandee: Tree) {
+ abstract class MacroExpander[Result: ClassTag](val role: MacroRole, val typer: Typer, val expandee: Tree) {
def allowExpandee(expandee: Tree): Boolean = true
def allowExpanded(expanded: Tree): Boolean = true
def allowedExpansions: String = "anything"
@@ -562,7 +570,7 @@ trait Macros extends FastTrack with MacroRuntimes with Traces with Helpers {
onFailure(typer.infer.setError(expandee))
} else try {
val expanded = {
- val runtime = macroRuntime(expandee.symbol)
+ val runtime = macroRuntime(expandee)
if (runtime != null) macroExpandWithRuntime(typer, expandee, runtime)
else macroExpandWithoutRuntime(typer, expandee)
}
@@ -592,151 +600,136 @@ trait Macros extends FastTrack with MacroRuntimes with Traces with Helpers {
}
}
- /** Expands a tree that carries a term, which happens to be a term macro.
- * @see MacroExpander
- */
- private abstract class TermMacroExpander(role: MacroRole, typer: Typer, expandee: Tree, mode: Mode, pt: Type)
- extends MacroExpander[Tree](role, typer, expandee) {
- override def allowedExpansions: String = "term trees"
- override def allowExpandee(expandee: Tree) = expandee.isTerm
- override def onSuccess(expanded: Tree) = typer.typed(expanded, mode, pt)
- override def onFallback(fallback: Tree) = typer.typed(fallback, mode, pt)
- }
-
/** Expands a term macro used in apply role as `M(2)(3)` in `val x = M(2)(3)`.
* @param outerPt Expected type that comes from enclosing context (something that's traditionally called `pt`).
* @param innerPt Expected type that comes from the signature of a macro def, possibly wildcarded to help type inference.
- * @see MacroExpander
*/
- def macroExpandApply(typer: Typer, expandee: Tree, mode: Mode, outerPt: Type): Tree = {
- object expander extends TermMacroExpander(APPLY_ROLE, typer, expandee, mode, outerPt) {
- lazy val innerPt = {
- val tp = if (isNullaryInvocation(expandee)) expandee.tpe.finalResultType else expandee.tpe
- if (isBlackbox(expandee)) tp
- else {
- // approximation is necessary for whitebox macros to guide type inference
- // read more in the comments for onDelayed below
- val undetparams = tp collect { case tp if tp.typeSymbol.isTypeParameter => tp.typeSymbol }
- deriveTypeWithWildcards(undetparams)(tp)
- }
+ class DefMacroExpander(typer: Typer, expandee: Tree, mode: Mode, outerPt: Type)
+ extends MacroExpander[Tree](APPLY_ROLE, typer, expandee) {
+ lazy val innerPt = {
+ val tp = if (isNullaryInvocation(expandee)) expandee.tpe.finalResultType else expandee.tpe
+ if (isBlackbox(expandee)) tp
+ else {
+ // approximation is necessary for whitebox macros to guide type inference
+ // read more in the comments for onDelayed below
+ val undetparams = tp collect { case tp if tp.typeSymbol.isTypeParameter => tp.typeSymbol }
+ deriveTypeWithWildcards(undetparams)(tp)
}
- override def onSuccess(expanded0: Tree) = {
- // prematurely annotate the tree with a macro expansion attachment
- // so that adapt called indirectly by typer.typed knows that it needs to apply the existential fixup
- linkExpandeeAndExpanded(expandee, expanded0)
-
- def typecheck(label: String, tree: Tree, pt: Type): Tree = {
- if (tree.isErrorTyped) tree
- else {
- if (macroDebugVerbose) println(s"$label (against pt = $pt): $tree")
- // `macroExpandApply` is called from `adapt`, where implicit conversions are disabled
- // therefore we need to re-enable the conversions back temporarily
- val result = typer.context.withImplicitsEnabled(typer.typed(tree, mode, pt))
- if (result.isErrorTyped && macroDebugVerbose) println(s"$label has failed: ${typer.context.reportBuffer.errors}")
- result
- }
- }
+ }
+ override def onSuccess(expanded0: Tree) = {
+ // prematurely annotate the tree with a macro expansion attachment
+ // so that adapt called indirectly by typer.typed knows that it needs to apply the existential fixup
+ linkExpandeeAndExpanded(expandee, expanded0)
- if (isBlackbox(expandee)) {
- val expanded1 = atPos(enclosingMacroPosition.makeTransparent)(Typed(expanded0, TypeTree(innerPt)))
- typecheck("blackbox typecheck", expanded1, outerPt)
- } else {
- val expanded1 = expanded0
- val expanded2 = typecheck("whitebox typecheck #1", expanded1, outerPt)
- typecheck("whitebox typecheck #2", expanded2, innerPt)
+ def typecheck(label: String, tree: Tree, pt: Type): Tree = {
+ if (tree.isErrorTyped) tree
+ else {
+ if (macroDebugVerbose) println(s"$label (against pt = $pt): $tree")
+ // `macroExpandApply` is called from `adapt`, where implicit conversions are disabled
+ // therefore we need to re-enable the conversions back temporarily
+ val result = typer.context.withImplicitsEnabled(typer.typed(tree, mode, pt))
+ if (result.isErrorTyped && macroDebugVerbose) println(s"$label has failed: ${typer.context.reportBuffer.errors}")
+ result
}
}
- override def onDelayed(delayed: Tree) = {
- // =========== THE SITUATION ===========
- //
- // If we've been delayed (i.e. bailed out of the expansion because of undetermined type params present in the expandee),
- // then there are two possible situations we're in:
- // 1) We're in POLYmode, when the typer tests the waters wrt type inference
- // (e.g. as in typedArgToPoly in doTypedApply).
- // 2) We're out of POLYmode, which means that the typer is out of tricks to infer our type
- // (e.g. if we're an argument to a function call, then this means that no previous argument lists
- // can determine our type variables for us).
- //
- // Situation #1 is okay for us, since there's no pressure. In POLYmode we're just verifying that
- // there's nothing outrageously wrong with our undetermined type params (from what I understand!).
- //
- // Situation #2 requires measures to be taken. If we're in it, then noone's going to help us infer
- // the undetermined type params. Therefore we need to do something ourselves or otherwise this
- // expandee will forever remaing not expanded (see SI-5692). A traditional way out of this conundrum
- // is to call `instantiate` and let the inferencer try to find the way out. It works for simple cases,
- // but sometimes, if the inferencer lacks information, it will be forced to approximate.
- //
- // =========== THE PROBLEM ===========
- //
- // Consider the following example (thanks, Miles!):
- //
- // Iso represents an isomorphism between two datatypes:
- // 1) An arbitrary one (e.g. a random case class)
- // 2) A uniform representation for all datatypes (e.g. an HList)
- //
- // trait Iso[T, U] {
- // def to(t : T) : U
- // def from(u : U) : T
- // }
- // implicit def materializeIso[T, U]: Iso[T, U] = macro ???
- //
- // case class Foo(i: Int, s: String, b: Boolean)
- // def foo[C, L](c: C)(implicit iso: Iso[C, L]): L = iso.to(c)
- // foo(Foo(23, "foo", true))
- //
- // In the snippet above, even though we know that there's a fundep going from T to U
- // (in a sense that a datatype's uniform representation is unambiguously determined by the datatype,
- // e.g. for Foo it will be Int :: String :: Boolean :: HNil), there's no way to convey this information
- // to the typechecker. Therefore the typechecker will infer Nothing for L, which is hardly what we want.
- //
- // =========== THE SOLUTION (ENABLED ONLY FOR WHITEBOX MACROS) ===========
- //
- // To give materializers a chance to say their word before vanilla inference kicks in,
- // we infer as much as possible (e.g. in the example above even though L is hopeless, C still can be inferred to Foo)
- // and then trigger macro expansion with the undetermined type parameters still there.
- // Thanks to that the materializer can take a look at what's going on and react accordingly.
- val shouldInstantiate = typer.context.undetparams.nonEmpty && !mode.inPolyMode
- if (shouldInstantiate) {
- if (isBlackbox(expandee)) typer.instantiatePossiblyExpectingUnit(delayed, mode, outerPt)
- else {
- forced += delayed
- typer.infer.inferExprInstance(delayed, typer.context.extractUndetparams(), outerPt, keepNothings = false)
- macroExpandApply(typer, delayed, mode, outerPt)
- }
- } else delayed
+
+ if (isBlackbox(expandee)) {
+ val expanded1 = atPos(enclosingMacroPosition.makeTransparent)(Typed(expanded0, TypeTree(innerPt)))
+ typecheck("blackbox typecheck", expanded1, outerPt)
+ } else {
+ val expanded1 = expanded0
+ val expanded2 = typecheck("whitebox typecheck #1", expanded1, outerPt)
+ typecheck("whitebox typecheck #2", expanded2, innerPt)
}
}
- expander(expandee)
+ override def onDelayed(delayed: Tree) = {
+ // =========== THE SITUATION ===========
+ //
+ // If we've been delayed (i.e. bailed out of the expansion because of undetermined type params present in the expandee),
+ // then there are two possible situations we're in:
+ // 1) We're in POLYmode, when the typer tests the waters wrt type inference
+ // (e.g. as in typedArgToPoly in doTypedApply).
+ // 2) We're out of POLYmode, which means that the typer is out of tricks to infer our type
+ // (e.g. if we're an argument to a function call, then this means that no previous argument lists
+ // can determine our type variables for us).
+ //
+ // Situation #1 is okay for us, since there's no pressure. In POLYmode we're just verifying that
+ // there's nothing outrageously wrong with our undetermined type params (from what I understand!).
+ //
+ // Situation #2 requires measures to be taken. If we're in it, then noone's going to help us infer
+ // the undetermined type params. Therefore we need to do something ourselves or otherwise this
+ // expandee will forever remaing not expanded (see SI-5692). A traditional way out of this conundrum
+ // is to call `instantiate` and let the inferencer try to find the way out. It works for simple cases,
+ // but sometimes, if the inferencer lacks information, it will be forced to approximate.
+ //
+ // =========== THE PROBLEM ===========
+ //
+ // Consider the following example (thanks, Miles!):
+ //
+ // Iso represents an isomorphism between two datatypes:
+ // 1) An arbitrary one (e.g. a random case class)
+ // 2) A uniform representation for all datatypes (e.g. an HList)
+ //
+ // trait Iso[T, U] {
+ // def to(t : T) : U
+ // def from(u : U) : T
+ // }
+ // implicit def materializeIso[T, U]: Iso[T, U] = macro ???
+ //
+ // case class Foo(i: Int, s: String, b: Boolean)
+ // def foo[C, L](c: C)(implicit iso: Iso[C, L]): L = iso.to(c)
+ // foo(Foo(23, "foo", true))
+ //
+ // In the snippet above, even though we know that there's a fundep going from T to U
+ // (in a sense that a datatype's uniform representation is unambiguously determined by the datatype,
+ // e.g. for Foo it will be Int :: String :: Boolean :: HNil), there's no way to convey this information
+ // to the typechecker. Therefore the typechecker will infer Nothing for L, which is hardly what we want.
+ //
+ // =========== THE SOLUTION (ENABLED ONLY FOR WHITEBOX MACROS) ===========
+ //
+ // To give materializers a chance to say their word before vanilla inference kicks in,
+ // we infer as much as possible (e.g. in the example above even though L is hopeless, C still can be inferred to Foo)
+ // and then trigger macro expansion with the undetermined type parameters still there.
+ // Thanks to that the materializer can take a look at what's going on and react accordingly.
+ val shouldInstantiate = typer.context.undetparams.nonEmpty && !mode.inPolyMode
+ if (shouldInstantiate) {
+ if (isBlackbox(expandee)) typer.instantiatePossiblyExpectingUnit(delayed, mode, outerPt)
+ else {
+ forced += delayed
+ typer.infer.inferExprInstance(delayed, typer.context.extractUndetparams(), outerPt, keepNothings = false)
+ macroExpand(typer, delayed, mode, outerPt)
+ }
+ } else delayed
+ }
+ override def onFallback(fallback: Tree) = typer.typed(fallback, mode, outerPt)
}
- /** Expands a term macro used in unapply role as `u.Quasiquote(StringContext("", "")).q.unapply(x)` in `case q"$x" => ...`.
- * @see MacroExpander
+ /** 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 = pluginsMacroExpand(typer, expandee, mode, pt)
+
+ /** Default implementation of `macroExpand`.
+ * Can be overridden by analyzer plugins (see AnalyzerPlugins.pluginsMacroExpand for more details)
*/
- def macroExpandUnapply(typer: Typer, original: Tree, fun: Tree, unapply: Symbol, args: List[Tree], mode: Mode, pt: Type) = {
- val expandee = treeCopy.Apply(original, gen.mkAttributedSelect(fun, unapply), args)
- object expander extends TermMacroExpander(UNAPPLY_ROLE, typer, expandee, mode, pt) {
- override def allowedExpansions: String = "unapply trees"
- override def allowExpandee(expandee: Tree) = expandee.isInstanceOf[Apply]
- private def unsupported(what: String) = abort("unapply macros currently don't support " + what)
- override def onFallback(fallback: Tree) = unsupported("fallback")
- override def onDelayed(delayed: Tree) = unsupported("advanced interaction with type inference")
- }
- expander(original)
+ def standardMacroExpand(typer: Typer, expandee: Tree, mode: Mode, pt: Type): Tree = {
+ val expander = new DefMacroExpander(typer, expandee, mode, pt)
+ expander(expandee)
}
- private sealed abstract class MacroStatus(val result: Tree)
- private case class Success(expanded: Tree) extends MacroStatus(expanded)
- private case class Fallback(fallback: Tree) extends MacroStatus(fallback) { currentRun.seenMacroExpansionsFallingBack = true }
- private case class Delayed(delayed: Tree) extends MacroStatus(delayed)
- private case class Skipped(skipped: Tree) extends MacroStatus(skipped)
- private case class Failure(failure: Tree) extends MacroStatus(failure)
- private def Delay(expanded: Tree) = Delayed(expanded)
- private def Skip(expanded: Tree) = Skipped(expanded)
+ sealed abstract class MacroStatus(val result: Tree)
+ case class Success(expanded: Tree) extends MacroStatus(expanded)
+ case class Fallback(fallback: Tree) extends MacroStatus(fallback) { currentRun.seenMacroExpansionsFallingBack = true }
+ case class Delayed(delayed: Tree) extends MacroStatus(delayed)
+ case class Skipped(skipped: Tree) extends MacroStatus(skipped)
+ case class Failure(failure: Tree) extends MacroStatus(failure)
+ def Delay(expanded: Tree) = Delayed(expanded)
+ def Skip(expanded: Tree) = Skipped(expanded)
/** Expands a macro when a runtime (i.e. the macro implementation) can be successfully loaded
* Meant for internal use within the macro infrastructure, don't use it elsewhere.
*/
- private def macroExpandWithRuntime(typer: Typer, expandee: Tree, runtime: MacroRuntime): MacroStatus = {
+ def macroExpandWithRuntime(typer: Typer, expandee: Tree, runtime: MacroRuntime): MacroStatus = {
val wasDelayed = isDelayed(expandee)
val undetparams = calculateUndetparams(expandee)
val nowDelayed = !typer.context.macrosEnabled || undetparams.nonEmpty
@@ -793,7 +786,7 @@ trait Macros extends FastTrack with MacroRuntimes with Traces with Helpers {
/** Expands a macro when a runtime (i.e. the macro implementation) cannot be loaded
* Meant for internal use within the macro infrastructure, don't use it elsewhere.
*/
- private def macroExpandWithoutRuntime(typer: Typer, expandee: Tree): MacroStatus = {
+ def macroExpandWithoutRuntime(typer: Typer, expandee: Tree): MacroStatus = {
import typer.TyperErrorGen._
val fallbackSym = expandee.symbol.nextOverriddenSymbol orElse MacroImplementationNotFoundError(expandee)
macroLogLite(s"falling back to: $fallbackSym")
@@ -871,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
- macroExpandApply(newTyper(context), tree, EXPRmode, WildcardType)
+ macroExpand(newTyper(context), tree, EXPRmode, WildcardType)
case _ =>
tree
})
diff --git a/src/compiler/scala/tools/nsc/typechecker/Namers.scala b/src/compiler/scala/tools/nsc/typechecker/Namers.scala
index 86bb99e7fa..14695f5939 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Namers.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Namers.scala
@@ -22,7 +22,7 @@ trait Namers extends MethodSynthesis {
import global._
import definitions._
- private var _lockedCount = 0
+ var _lockedCount = 0
def lockedCount = this._lockedCount
/** Replaces any Idents for which cond is true with fresh TypeTrees().
@@ -107,8 +107,8 @@ trait Namers extends MethodSynthesis {
}
protected def owner = context.owner
- private def contextFile = context.unit.source.file
- private def typeErrorHandler[T](tree: Tree, alt: T): PartialFunction[Throwable, T] = {
+ def contextFile = context.unit.source.file
+ def typeErrorHandler[T](tree: Tree, alt: T): PartialFunction[Throwable, T] = {
case ex: TypeError =>
// H@ need to ensure that we handle only cyclic references
TypeSigError(tree, ex)
@@ -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 {
@@ -309,7 +314,7 @@ trait Namers extends MethodSynthesis {
* be transferred to the symbol as they are, supply a mask containing
* the flags to keep.
*/
- private def createMemberSymbol(tree: MemberDef, name: Name, mask: Long): Symbol = {
+ def createMemberSymbol(tree: MemberDef, name: Name, mask: Long): Symbol = {
val pos = tree.pos
val isParameter = tree.mods.isParameter
val flags = tree.mods.flags & mask
@@ -327,14 +332,14 @@ trait Namers extends MethodSynthesis {
else owner.newValue(name.toTermName, pos, flags)
}
}
- private def createFieldSymbol(tree: ValDef): TermSymbol =
+ def createFieldSymbol(tree: ValDef): TermSymbol =
owner.newValue(tree.localName, tree.pos, tree.mods.flags & FieldFlags | PrivateLocal)
- private def createImportSymbol(tree: Tree) =
+ def createImportSymbol(tree: Tree) =
NoSymbol.newImport(tree.pos) setInfo completerOf(tree)
/** All PackageClassInfoTypes come from here. */
- private def createPackageSymbol(pos: Position, pid: RefTree): Symbol = {
+ def createPackageSymbol(pos: Position, pid: RefTree): Symbol = {
val pkgOwner = pid match {
case Ident(_) => if (owner.isEmptyPackageClass) rootMirror.RootClass else owner
case Select(qual: RefTree, _) => createPackageSymbol(pos, qual).moduleClass
@@ -393,7 +398,7 @@ trait Namers extends MethodSynthesis {
/** Given a ClassDef or ModuleDef, verifies there isn't a companion which
* has been defined in a separate file.
*/
- private def validateCompanionDefs(tree: ImplDef) {
+ def validateCompanionDefs(tree: ImplDef) {
val sym = tree.symbol orElse { return }
val ctx = if (context.owner.isPackageObjectClass) context.outer else context
val module = if (sym.isModule) sym else ctx.scope lookupModule tree.name
@@ -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.
@@ -828,9 +839,10 @@ trait Namers extends MethodSynthesis {
* assigns the type to the tpt's node. Returns the type.
*/
private def assignTypeToTree(tree: ValOrDefDef, defnTyper: Typer, pt: Type): Type = {
- val rhsTpe =
- if (tree.symbol.isTermMacro) defnTyper.computeMacroDefType(tree, pt)
- else defnTyper.computeType(tree.rhs, pt)
+ val rhsTpe = tree match {
+ case ddef: DefDef if tree.symbol.isTermMacro => defnTyper.computeMacroDefType(ddef, pt)
+ case _ => defnTyper.computeType(tree.rhs, pt)
+ }
val defnTpe = widenIfNecessary(tree.symbol, rhsTpe, pt)
tree.tpt defineType defnTpe setPos tree.pos.focus
@@ -1620,7 +1632,7 @@ trait Namers extends MethodSynthesis {
val tree: Tree
}
- def mkTypeCompleter(t: Tree)(c: Symbol => Unit) = new LockingTypeCompleter {
+ def mkTypeCompleter(t: Tree)(c: Symbol => Unit) = new LockingTypeCompleter with FlagAgnosticCompleter {
val tree = t
def completeImpl(sym: Symbol) = c(sym)
}
diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
index dbe85f4f5a..14e16fc591 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
@@ -1102,7 +1102,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
if (tree.isType)
adaptType()
else if (mode.typingExprNotFun && treeInfo.isMacroApplication(tree) && !isMacroExpansionSuppressed(tree))
- macroExpandApply(this, tree, mode, pt)
+ macroExpand(this, tree, mode, pt)
else if (mode.typingConstructorPattern)
typedConstructorPattern(tree, pt)
else if (shouldInsertApply(tree))
@@ -1853,12 +1853,15 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
}
protected def enterSym(txt: Context, tree: Tree): Context =
- if (txt eq context) namer.enterSym(tree)
- else newNamer(txt).enterSym(tree)
+ if (txt eq context) namer enterSym tree
+ else newNamer(txt) enterSym tree
/** <!-- 2 --> Check that inner classes do not inherit from Annotation
*/
- def typedTemplate(templ: Template, parents1: List[Tree]): Template = {
+ def typedTemplate(templ0: Template, parents1: List[Tree]): Template = {
+ val templ = templ0
+ // please FIXME: uncommenting this line breaks everything
+ // val templ = treeCopy.Template(templ0, templ0.body, templ0.self, templ0.parents)
val clazz = context.owner
clazz.annotations.map(_.completeInfo())
if (templ.symbol == NoSymbol)
@@ -1886,7 +1889,8 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
)
// the following is necessary for templates generated later
assert(clazz.info.decls != EmptyScope, clazz)
- enterSyms(context.outer.make(templ, clazz, clazz.info.decls), templ.body)
+ val body1 = pluginsEnterStats(this, templ.body)
+ enterSyms(context.outer.make(templ, clazz, clazz.info.decls), body1)
if (!templ.isErrorTyped) // if `parentTypes` has invalidated the template, don't validate it anymore
validateParentClasses(parents1, selfType)
if (clazz.isCase)
@@ -1900,11 +1904,11 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
if (!phase.erasedTypes && !clazz.info.resultType.isError) // @S: prevent crash for duplicated type members
checkFinitary(clazz.info.resultType.asInstanceOf[ClassInfoType])
- val body = {
- val body =
- if (isPastTyper || reporter.hasErrors) templ.body
- else templ.body flatMap rewrappingWrapperTrees(namer.addDerivedTrees(Typer.this, _))
- val primaryCtor = treeInfo.firstConstructor(body)
+ val body2 = {
+ val body2 =
+ if (isPastTyper || reporter.hasErrors) body1
+ else body1 flatMap rewrappingWrapperTrees(namer.addDerivedTrees(Typer.this, _))
+ val primaryCtor = treeInfo.firstConstructor(body2)
val primaryCtor1 = primaryCtor match {
case DefDef(_, _, _, _, _, Block(earlyVals :+ global.pendingSuperCall, unit)) =>
val argss = superArgs(parents1.head) getOrElse Nil
@@ -1913,13 +1917,13 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
deriveDefDef(primaryCtor)(block => Block(earlyVals :+ superCall, unit) setPos pos) setPos pos
case _ => primaryCtor
}
- body mapConserve { case `primaryCtor` => primaryCtor1; case stat => stat }
+ body2 mapConserve { case `primaryCtor` => primaryCtor1; case stat => stat }
}
- val body1 = typedStats(body, templ.symbol)
+ val body3 = typedStats(body2, templ.symbol)
if (clazz.info.firstParent.typeSymbol == AnyValClass)
- validateDerivedValueClass(clazz, body1)
+ validateDerivedValueClass(clazz, body3)
if (clazz.isTrait) {
for (decl <- clazz.info.decls if decl.isTerm && decl.isEarlyInitialized) {
@@ -1927,7 +1931,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
}
}
- treeCopy.Template(templ, parents1, self1, body1) setType clazz.tpe_*
+ treeCopy.Template(templ, parents1, self1, body3) setType clazz.tpe_*
}
/** Remove definition annotations from modifiers (they have been saved
@@ -2309,10 +2313,11 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
}
}
- def typedBlock(block: Block, mode: Mode, pt: Type): Block = {
+ def typedBlock(block0: Block, mode: Mode, pt: Type): Block = {
val syntheticPrivates = new ListBuffer[Symbol]
try {
- namer.enterSyms(block.stats)
+ namer.enterSyms(block0.stats)
+ val block = treeCopy.Block(block0, pluginsEnterStats(this, block0.stats), block0.expr)
for (stat <- block.stats) enterLabelDef(stat)
if (phaseId(currentPeriod) <= currentRun.typerPhase.id) {
@@ -3797,7 +3802,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) { namer.enterSym(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)
@@ -4948,7 +4953,8 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
val sym: Symbol = tree.symbol
if ((sym ne null) && (sym ne NoSymbol)) sym.initialize
- def typedPackageDef(pdef: PackageDef) = {
+ def typedPackageDef(pdef0: PackageDef) = {
+ val pdef = treeCopy.PackageDef(pdef0, pdef0.pid, pluginsEnterStats(this, pdef0.stats))
val pid1 = typedQualifier(pdef.pid).asInstanceOf[RefTree]
assert(sym.moduleClass ne NoSymbol, sym)
val stats1 = newTyper(context.make(tree, sym.moduleClass, sym.info.decls))
@@ -5494,25 +5500,23 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
tpe
}
- def computeMacroDefType(tree: Tree, pt: Type): Type = {
+ def computeMacroDefType(ddef: DefDef, pt: Type): Type = {
assert(context.owner.isMacro, context.owner)
- assert(tree.symbol.isMacro, tree.symbol)
- assert(tree.isInstanceOf[DefDef], tree.getClass)
- val ddef = tree.asInstanceOf[DefDef]
+ assert(ddef.symbol.isMacro, ddef.symbol)
- val tree1 =
+ val rhs1 =
if (transformed contains ddef.rhs) {
// macro defs are typechecked in `methodSig` (by calling this method) in order to establish their link to macro implementation asap
// if a macro def doesn't have explicitly specified return type, this method will be called again by `assignTypeToTree`
// here we guard against this case
transformed(ddef.rhs)
} else {
- val tree1 = typedMacroBody(this, ddef)
- transformed(ddef.rhs) = tree1
- tree1
+ val rhs1 = typedMacroBody(this, ddef)
+ transformed(ddef.rhs) = rhs1
+ rhs1
}
- val isMacroBodyOkay = !tree.symbol.isErroneous && !(tree1 exists (_.isErroneous)) && tree1 != EmptyTree
+ val isMacroBodyOkay = !ddef.symbol.isErroneous && !(rhs1 exists (_.isErroneous)) && rhs1 != EmptyTree
val shouldInheritMacroImplReturnType = ddef.tpt.isEmpty
if (isMacroBodyOkay && shouldInheritMacroImplReturnType) {
val commonMessage = "macro defs must have explicitly specified return types"
@@ -5524,7 +5528,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
val explanation = s"inference of $inferredType from macro impl's c.Expr[$inferredType] is deprecated and is going to stop working in 2.12"
unit.deprecationWarning(ddef.pos, s"$commonMessage ($explanation)")
}
- computeMacroDefTypeFromMacroImplRef(ddef, tree1) match {
+ computeMacroDefTypeFromMacroImplRef(ddef, rhs1) match {
case ErrorType => ErrorType
case NothingTpe => NothingTpe
case NoType => reportFailure(); AnyTpe