From 9737b808c12821162e2a5888b17175a396037f50 Mon Sep 17 00:00:00 2001 From: Eugene Burmako Date: Sat, 7 Dec 2013 11:41:29 +0100 Subject: macroExpandApply => macroExpand MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Back then, when we needed separate macro expanders for both applications and unapplications, it made sense to have two different methods that do macro expansions. However, after @paulp’s upgrade of the pattern matching engine, we no longer need a dedicated expander for unapply, so I’m removing it and renaming `macroExpandApply` to just `macroExpand`. --- test/files/run/macro-whitebox-fundep-materialization/Test_2.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'test/files/run') diff --git a/test/files/run/macro-whitebox-fundep-materialization/Test_2.scala b/test/files/run/macro-whitebox-fundep-materialization/Test_2.scala index a00f4ed7db..40ca1d549c 100644 --- a/test/files/run/macro-whitebox-fundep-materialization/Test_2.scala +++ b/test/files/run/macro-whitebox-fundep-materialization/Test_2.scala @@ -1,4 +1,4 @@ -// see the comments for macroExpandApply.onDelayed for an explanation of what's tested here +// see the comments for macroExpand.onDelayed for an explanation of what's tested here object Test extends App { case class Foo(i: Int, s: String, b: Boolean) def foo[C, L](c: C)(implicit iso: Iso[C, L]): L = iso.to(c) -- cgit v1.2.3 From 4d92aec651def608628a2275e1b6bf2d1fcbabe7 Mon Sep 17 00:00:00 2001 From: Eugene Burmako Date: Sat, 7 Dec 2013 16:55:38 +0100 Subject: unprivates important helpers in Namers.scala This is the first of two commits that enable hooks necessary to implement macro annotations in an honest, hackless compiler plugin. This particular commit turns certain helpers into public methods. Of course, there is a probability that with the evolution of macro paradise, I will need more helper methods, and those will have to be called via reflection, but at least for now it's nice to have a reflection-less plugin :) --- .../scala/tools/nsc/typechecker/Analyzer.scala | 2 +- .../tools/nsc/typechecker/AnalyzerPlugins.scala | 66 ++++++++++++++++++++++ .../scala/tools/nsc/typechecker/Namers.scala | 28 ++++----- .../scala/tools/nsc/typechecker/Typers.scala | 40 +++++++------ .../scala/tools/nsc/interpreter/ReplGlobal.scala | 2 +- test/files/run/macroPlugins-macroArgs.check | 2 + .../run/macroPlugins-macroArgs/Macros_2.scala | 11 ++++ .../run/macroPlugins-macroArgs/Plugin_1.scala | 21 +++++++ test/files/run/macroPlugins-macroArgs/Test_3.flags | 1 + test/files/run/macroPlugins-macroArgs/Test_3.scala | 4 ++ .../run/macroPlugins-macroArgs/scalac-plugin.xml | 4 ++ test/files/run/macroPlugins-macroExpand.check | 2 + .../run/macroPlugins-macroExpand/Macros_2.scala | 18 ++++++ .../run/macroPlugins-macroExpand/Plugin_1.scala | 27 +++++++++ .../run/macroPlugins-macroExpand/Test_3.flags | 1 + .../run/macroPlugins-macroExpand/Test_3.scala | 4 ++ .../run/macroPlugins-macroExpand/scalac-plugin.xml | 4 ++ test/files/run/macroPlugins-macroRuntime.check | 2 + .../run/macroPlugins-macroRuntime/Macros_2.scala | 11 ++++ .../run/macroPlugins-macroRuntime/Plugin_1.scala | 20 +++++++ .../run/macroPlugins-macroRuntime/Test_3.flags | 1 + .../run/macroPlugins-macroRuntime/Test_3.scala | 4 ++ .../macroPlugins-macroRuntime/scalac-plugin.xml | 4 ++ test/files/run/macroPlugins-namerHooks.check | 45 +++++++++++++++ test/files/run/macroPlugins-namerHooks.scala | 38 +++++++++++++ test/files/run/macroPlugins-typedMacroBody.check | 2 + .../run/macroPlugins-typedMacroBody/Macros_2.flags | 1 + .../run/macroPlugins-typedMacroBody/Macros_2.scala | 18 ++++++ .../run/macroPlugins-typedMacroBody/Plugin_1.scala | 21 +++++++ .../run/macroPlugins-typedMacroBody/Test_3.scala | 4 ++ .../macroPlugins-typedMacroBody/scalac-plugin.xml | 4 ++ 31 files changed, 379 insertions(+), 33 deletions(-) create mode 100644 test/files/run/macroPlugins-macroArgs.check create mode 100644 test/files/run/macroPlugins-macroArgs/Macros_2.scala create mode 100644 test/files/run/macroPlugins-macroArgs/Plugin_1.scala create mode 100644 test/files/run/macroPlugins-macroArgs/Test_3.flags create mode 100644 test/files/run/macroPlugins-macroArgs/Test_3.scala create mode 100644 test/files/run/macroPlugins-macroArgs/scalac-plugin.xml create mode 100644 test/files/run/macroPlugins-macroExpand.check create mode 100644 test/files/run/macroPlugins-macroExpand/Macros_2.scala create mode 100644 test/files/run/macroPlugins-macroExpand/Plugin_1.scala create mode 100644 test/files/run/macroPlugins-macroExpand/Test_3.flags create mode 100644 test/files/run/macroPlugins-macroExpand/Test_3.scala create mode 100644 test/files/run/macroPlugins-macroExpand/scalac-plugin.xml create mode 100644 test/files/run/macroPlugins-macroRuntime.check create mode 100644 test/files/run/macroPlugins-macroRuntime/Macros_2.scala create mode 100644 test/files/run/macroPlugins-macroRuntime/Plugin_1.scala create mode 100644 test/files/run/macroPlugins-macroRuntime/Test_3.flags create mode 100644 test/files/run/macroPlugins-macroRuntime/Test_3.scala create mode 100644 test/files/run/macroPlugins-macroRuntime/scalac-plugin.xml create mode 100644 test/files/run/macroPlugins-namerHooks.check create mode 100644 test/files/run/macroPlugins-namerHooks.scala create mode 100644 test/files/run/macroPlugins-typedMacroBody.check create mode 100644 test/files/run/macroPlugins-typedMacroBody/Macros_2.flags create mode 100644 test/files/run/macroPlugins-typedMacroBody/Macros_2.scala create mode 100644 test/files/run/macroPlugins-typedMacroBody/Plugin_1.scala create mode 100644 test/files/run/macroPlugins-typedMacroBody/Test_3.scala create mode 100644 test/files/run/macroPlugins-typedMacroBody/scalac-plugin.xml (limited to 'test/files/run') diff --git a/src/compiler/scala/tools/nsc/typechecker/Analyzer.scala b/src/compiler/scala/tools/nsc/typechecker/Analyzer.scala index 5c02516c47..70ff1051c1 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Analyzer.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Analyzer.scala @@ -40,7 +40,7 @@ trait Analyzer extends AnyRef override def keepsTypeParams = false def apply(unit: CompilationUnit) { - newNamer(rootContext(unit)).enterSym(unit.body) + pluginsEnterSym(newNamer(rootContext(unit)), unit.body) } } } diff --git a/src/compiler/scala/tools/nsc/typechecker/AnalyzerPlugins.scala b/src/compiler/scala/tools/nsc/typechecker/AnalyzerPlugins.scala index 09a6a553ee..61becab483 100644 --- a/src/compiler/scala/tools/nsc/typechecker/AnalyzerPlugins.scala +++ b/src/compiler/scala/tools/nsc/typechecker/AnalyzerPlugins.scala @@ -229,6 +229,41 @@ trait AnalyzerPlugins { self: Analyzer => * $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.enterSym` 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.ensureCompanionObject` 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 } @@ -363,4 +398,35 @@ trait AnalyzerPlugins { self: Analyzer => def default = macroRuntime(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 + } + }) + + /** @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 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/Namers.scala b/src/compiler/scala/tools/nsc/typechecker/Namers.scala index 01d61a253e..ee597528a9 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) @@ -253,7 +253,7 @@ trait Namers extends MethodSynthesis { case tree @ ValDef(_, _, _, _) => enterValDef(tree) case tree @ DefDef(_, _, _, _, _, _) => enterDefDef(tree) case tree @ TypeDef(_, _, _, _) => enterTypeDef(tree) - case DocDef(_, defn) => enterSym(defn) + case DocDef(_, defn) => pluginsEnterSym(this, defn) case tree @ Import(_, _) => assignSymbol(tree) returnContext = context.make(tree) @@ -309,7 +309,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 +327,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 +393,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 @@ -452,7 +452,7 @@ trait Namers extends MethodSynthesis { def enterSyms(trees: List[Tree]): Namer = { trees.foldLeft(this: Namer) { (namer, t) => - val ctx = namer enterSym t + val ctx = pluginsEnterSym(namer, t) // for Import trees, enterSym returns a changed context, so we need a new namer if (ctx eq namer.context) namer else newNamer(ctx) @@ -662,7 +662,7 @@ trait Namers extends MethodSynthesis { tree.symbol setInfo completerOf(tree) if (mods.isCase) { - val m = ensureCompanionObject(tree, caseModuleDef) + val m = pluginsEnsureCompanionObject(this, tree, caseModuleDef) m.moduleClass.updateAttachment(new ClassForCaseCompanionAttachment(tree)) } val hasDefault = impl.body exists { @@ -670,7 +670,7 @@ trait Namers extends MethodSynthesis { case _ => false } if (hasDefault) { - val m = ensureCompanionObject(tree) + val m = pluginsEnsureCompanionObject(this, tree) m.updateAttachment(new ConstructorDefaultsAttachment(tree, null)) } val owner = tree.symbol.owner @@ -697,7 +697,7 @@ trait Namers extends MethodSynthesis { def enterIfNotThere(sym: Symbol) { } def enterSyntheticSym(tree: Tree): Symbol = { - enterSym(tree) + pluginsEnterSym(this, tree) context.unit.synthetics(tree.symbol) = tree tree.symbol } @@ -931,7 +931,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. - enclosingNamerWithScope(clazz.owner.rawInfo.decls).ensureCompanionObject(cdef) + pluginsEnsureCompanionObject(enclosingNamerWithScope(clazz.owner.rawInfo.decls), cdef) } pluginsTp } diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index 5c1026394a..8ddaccf16c 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -1863,12 +1863,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) pluginsEnterSym(namer, tree) + else pluginsEnterSym(newNamer(txt), tree) /** 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) @@ -1896,7 +1899,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) @@ -1910,11 +1914,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 @@ -1923,13 +1927,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) { @@ -1937,7 +1941,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 @@ -2319,10 +2323,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) { @@ -3807,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) { namer.enterSym(wc); wc.symbol setFlag EXISTENTIAL } + if (wc.symbol == NoSymbol) { pluginsEnterSym(namer, wc); wc.symbol setFlag EXISTENTIAL } else context.scope enter wc.symbol val whereClauses1 = typedStats(tree.whereClauses, context.owner) for (vd @ ValDef(_, _, _, _) <- whereClauses1) @@ -4954,7 +4959,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)) diff --git a/src/repl/scala/tools/nsc/interpreter/ReplGlobal.scala b/src/repl/scala/tools/nsc/interpreter/ReplGlobal.scala index 51fab3082e..498ff46624 100644 --- a/src/repl/scala/tools/nsc/interpreter/ReplGlobal.scala +++ b/src/repl/scala/tools/nsc/interpreter/ReplGlobal.scala @@ -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) - // newNamer(rootContext(unit)).enterSym(unit.body) + // pluginsEnterSym(newNamer(rootContext(unit)), unit.body) } } } diff --git a/test/files/run/macroPlugins-macroArgs.check b/test/files/run/macroPlugins-macroArgs.check new file mode 100644 index 0000000000..a68f8069b6 --- /dev/null +++ b/test/files/run/macroPlugins-macroArgs.check @@ -0,0 +1,2 @@ +hijacked 1 +hijacked 2 diff --git a/test/files/run/macroPlugins-macroArgs/Macros_2.scala b/test/files/run/macroPlugins-macroArgs/Macros_2.scala new file mode 100644 index 0000000000..d6521dfbcb --- /dev/null +++ b/test/files/run/macroPlugins-macroArgs/Macros_2.scala @@ -0,0 +1,11 @@ +import scala.language.experimental.macros +import scala.reflect.macros.BlackboxContext + +object Macros { + def impl(c: BlackboxContext)(arg: c.Tree) = { + import c.universe._ + q"""println($arg)""" + } + + def foo(arg: String): Unit = macro impl +} \ No newline at end of file diff --git a/test/files/run/macroPlugins-macroArgs/Plugin_1.scala b/test/files/run/macroPlugins-macroArgs/Plugin_1.scala new file mode 100644 index 0000000000..04cedbdc64 --- /dev/null +++ b/test/files/run/macroPlugins-macroArgs/Plugin_1.scala @@ -0,0 +1,21 @@ +package macroArgs + +import scala.tools.nsc.Global +import scala.tools.nsc.plugins.{Plugin => NscPlugin} + +class Plugin(val global: Global) extends NscPlugin { + import global._ + import analyzer._ + + val name = "macroArgs" + val description = "A sample analyzer plugin that overrides macroArgs." + val components = Nil + addMacroPlugin(MacroPlugin) + + object MacroPlugin extends MacroPlugin { + override def pluginsMacroArgs(typer: Typer, expandee: Tree): Option[MacroArgs] = { + val MacroArgs(c, List(Literal(Constant(s: String)))) = macroArgs(typer, expandee) + Some(MacroArgs(c, List(Literal(Constant("hijacked " + s))))) + } + } +} \ No newline at end of file diff --git a/test/files/run/macroPlugins-macroArgs/Test_3.flags b/test/files/run/macroPlugins-macroArgs/Test_3.flags new file mode 100644 index 0000000000..966df731d0 --- /dev/null +++ b/test/files/run/macroPlugins-macroArgs/Test_3.flags @@ -0,0 +1 @@ +-Xplugin:. \ No newline at end of file diff --git a/test/files/run/macroPlugins-macroArgs/Test_3.scala b/test/files/run/macroPlugins-macroArgs/Test_3.scala new file mode 100644 index 0000000000..a54d608178 --- /dev/null +++ b/test/files/run/macroPlugins-macroArgs/Test_3.scala @@ -0,0 +1,4 @@ +object Test extends App { + Macros.foo("1") + Macros.foo("2") +} \ No newline at end of file diff --git a/test/files/run/macroPlugins-macroArgs/scalac-plugin.xml b/test/files/run/macroPlugins-macroArgs/scalac-plugin.xml new file mode 100644 index 0000000000..0849f0f4ea --- /dev/null +++ b/test/files/run/macroPlugins-macroArgs/scalac-plugin.xml @@ -0,0 +1,4 @@ + + macro-args + macroArgs.Plugin + \ No newline at end of file diff --git a/test/files/run/macroPlugins-macroExpand.check b/test/files/run/macroPlugins-macroExpand.check new file mode 100644 index 0000000000..6f685c2af4 --- /dev/null +++ b/test/files/run/macroPlugins-macroExpand.check @@ -0,0 +1,2 @@ +expanded into println("impl1") +expanded into println("impl2") diff --git a/test/files/run/macroPlugins-macroExpand/Macros_2.scala b/test/files/run/macroPlugins-macroExpand/Macros_2.scala new file mode 100644 index 0000000000..f16503b415 --- /dev/null +++ b/test/files/run/macroPlugins-macroExpand/Macros_2.scala @@ -0,0 +1,18 @@ +import scala.language.experimental.macros +import scala.reflect.macros.BlackboxContext + +object Macros { + def impl1(c: BlackboxContext) = { + import c.universe._ + q"""println("impl1")""" + } + + def impl2(c: BlackboxContext) = { + import c.universe._ + q"""println("impl2")""" + } + + def foo1: Unit = macro impl1 + + def foo2: Unit = macro impl2 +} \ No newline at end of file diff --git a/test/files/run/macroPlugins-macroExpand/Plugin_1.scala b/test/files/run/macroPlugins-macroExpand/Plugin_1.scala new file mode 100644 index 0000000000..13df85cb23 --- /dev/null +++ b/test/files/run/macroPlugins-macroExpand/Plugin_1.scala @@ -0,0 +1,27 @@ +package macroExpand + +import scala.tools.nsc.Global +import scala.tools.nsc.plugins.{Plugin => NscPlugin} + +class Plugin(val global: Global) extends NscPlugin { + import global._ + import analyzer._ + import scala.reflect.internal.Mode + + val name = "macroExpand" + val description = "A sample analyzer plugin that overrides macroExpand." + val components = Nil + addMacroPlugin(MacroPlugin) + + object MacroPlugin extends MacroPlugin { + override def pluginsMacroExpand(typer: Typer, expandee: Tree, mode: Mode, pt: Type): Option[Tree] = { + object expander extends DefMacroExpander(typer, expandee, mode, pt) { + override def onSuccess(expanded: Tree) = { + val message = s"expanded into ${expanded.toString}" + typer.typed(q"println($message)") + } + } + Some(expander(expandee)) + } + } +} \ No newline at end of file diff --git a/test/files/run/macroPlugins-macroExpand/Test_3.flags b/test/files/run/macroPlugins-macroExpand/Test_3.flags new file mode 100644 index 0000000000..966df731d0 --- /dev/null +++ b/test/files/run/macroPlugins-macroExpand/Test_3.flags @@ -0,0 +1 @@ +-Xplugin:. \ No newline at end of file diff --git a/test/files/run/macroPlugins-macroExpand/Test_3.scala b/test/files/run/macroPlugins-macroExpand/Test_3.scala new file mode 100644 index 0000000000..def9b5608a --- /dev/null +++ b/test/files/run/macroPlugins-macroExpand/Test_3.scala @@ -0,0 +1,4 @@ +object Test extends App { + Macros.foo1 + Macros.foo2 +} \ No newline at end of file diff --git a/test/files/run/macroPlugins-macroExpand/scalac-plugin.xml b/test/files/run/macroPlugins-macroExpand/scalac-plugin.xml new file mode 100644 index 0000000000..860150865c --- /dev/null +++ b/test/files/run/macroPlugins-macroExpand/scalac-plugin.xml @@ -0,0 +1,4 @@ + + macro-expand + macroExpand.Plugin + \ No newline at end of file diff --git a/test/files/run/macroPlugins-macroRuntime.check b/test/files/run/macroPlugins-macroRuntime.check new file mode 100644 index 0000000000..af16d1ac36 --- /dev/null +++ b/test/files/run/macroPlugins-macroRuntime.check @@ -0,0 +1,2 @@ +hijacked +hijacked diff --git a/test/files/run/macroPlugins-macroRuntime/Macros_2.scala b/test/files/run/macroPlugins-macroRuntime/Macros_2.scala new file mode 100644 index 0000000000..d6521dfbcb --- /dev/null +++ b/test/files/run/macroPlugins-macroRuntime/Macros_2.scala @@ -0,0 +1,11 @@ +import scala.language.experimental.macros +import scala.reflect.macros.BlackboxContext + +object Macros { + def impl(c: BlackboxContext)(arg: c.Tree) = { + import c.universe._ + q"""println($arg)""" + } + + def foo(arg: String): Unit = macro impl +} \ No newline at end of file diff --git a/test/files/run/macroPlugins-macroRuntime/Plugin_1.scala b/test/files/run/macroPlugins-macroRuntime/Plugin_1.scala new file mode 100644 index 0000000000..a55adadb48 --- /dev/null +++ b/test/files/run/macroPlugins-macroRuntime/Plugin_1.scala @@ -0,0 +1,20 @@ +package macroRuntime + +import scala.tools.nsc.Global +import scala.tools.nsc.plugins.{Plugin => NscPlugin} + +class Plugin(val global: Global) extends NscPlugin { + import global._ + import analyzer._ + + val name = "macroRuntime" + val description = "A sample analyzer plugin that overrides macroRuntime." + val components = Nil + addMacroPlugin(MacroPlugin) + + object MacroPlugin extends MacroPlugin { + override def pluginsMacroRuntime(expandee: Tree): Option[MacroRuntime] = Some({ + case MacroArgs(_, List(msg)) => q"""println("hijacked")""" + }) + } +} \ No newline at end of file diff --git a/test/files/run/macroPlugins-macroRuntime/Test_3.flags b/test/files/run/macroPlugins-macroRuntime/Test_3.flags new file mode 100644 index 0000000000..966df731d0 --- /dev/null +++ b/test/files/run/macroPlugins-macroRuntime/Test_3.flags @@ -0,0 +1 @@ +-Xplugin:. \ No newline at end of file diff --git a/test/files/run/macroPlugins-macroRuntime/Test_3.scala b/test/files/run/macroPlugins-macroRuntime/Test_3.scala new file mode 100644 index 0000000000..a54d608178 --- /dev/null +++ b/test/files/run/macroPlugins-macroRuntime/Test_3.scala @@ -0,0 +1,4 @@ +object Test extends App { + Macros.foo("1") + Macros.foo("2") +} \ No newline at end of file diff --git a/test/files/run/macroPlugins-macroRuntime/scalac-plugin.xml b/test/files/run/macroPlugins-macroRuntime/scalac-plugin.xml new file mode 100644 index 0000000000..8001af1054 --- /dev/null +++ b/test/files/run/macroPlugins-macroRuntime/scalac-plugin.xml @@ -0,0 +1,4 @@ + + macro-runtime + macroRuntime.Plugin + \ No newline at end of file diff --git a/test/files/run/macroPlugins-namerHooks.check b/test/files/run/macroPlugins-namerHooks.check new file mode 100644 index 0000000000..c2db5935d4 --- /dev/null +++ b/test/files/run/macroPlugins-namerHooks.check @@ -0,0 +1,45 @@ +enterSym(package { case class C extends scala.Product with scala.Serializable { val x: Int = _; val y: Int = _; def (x: Int, y: Int) = { super.(); () } } }) +enterSym(case class C extends scala.Product with scala.Serializable { val x: Int = _; val y: Int = _; def (x: Int, y: Int) = { super.(); () } }) +ensureCompanionObject(case class C extends scala.Product with scala.Serializable { val x: Int = _; val y: Int = _; def (x: Int, y: Int) = { super.(); () } }, ...) +enterSym( object C extends runtime.this.AbstractFunction2[Int, Int, C] { def () = { super.(); () }; final override def toString() = "C" }) +enterStat(case class C extends scala.Product with scala.Serializable { val x: Int = _; val y: Int = _; def (x: Int, y: Int) = { super.(); () } }) +enterSym( val x: Int = _) +enterSym( val y: Int = _) +enterSym(def (x: Int, y: Int) = { super.(); () }) +enterSym( def copy(x = x, y = y) = new C(x, y)) +enterStat( private[this] val x: Int = _) +enterStat( private[this] val y: Int = _) +enterStat(def (x: Int, y: Int) = { super.(); () }) +enterSym( private[this] val x: Int = _) +enterSym( private[this] val y: Int = _) +enterSym(def (x: Int, y: Int) = { super.(); () }) +enterSym(super.()) +enterStat(super.()) +enterSym( def copy$default$1 = x) +enterSym( def copy$default$2 = y) +enterSym( var acc: Int = -889275714) +enterSym(acc = Statics.this.mix(acc, x)) +enterSym(acc = Statics.this.mix(acc, y)) +enterStat( var acc: Int = -889275714) +enterStat(acc = Statics.this.mix(acc, x)) +enterStat(acc = Statics.this.mix(acc, y)) +enterSym( val C$1: C = x$1.asInstanceOf[C]) +enterStat( val C$1: C = x$1.asInstanceOf[C]) +enterSym(def () = { super.(); () }) +enterSym(final override def toString() = "C") +enterSym(case def apply(x: Int, y: Int): C = new C(x, y)) +enterSym(case def unapply(x$0: C) = if (x$0.==(null)) scala.this.None else Some(scala.Tuple2(x$0.x, x$0.y))) +enterStat(def () = { super.(); () }) +enterStat(final override def toString() = "C") +enterSym(def () = { super.(); () }) +enterSym(final override def toString() = "C") +enterSym(super.()) +enterStat(super.()) +enterSym(case val x1: Int = x$1) +enterStat(case val x1: Int = x$1) +enterSym(case val x1: Any = x$1) +enterSym(case5(){ if (x1.isInstanceOf[C]) matchEnd4(true) else case6() }) +enterSym(case6(){ matchEnd4(false) }) +enterStat(case val x1: Any = x$1) +enterStat(case5(){ if (x1.isInstanceOf[C]) matchEnd4(true) else case6() }) +enterStat(case6(){ matchEnd4(false) }) diff --git a/test/files/run/macroPlugins-namerHooks.scala b/test/files/run/macroPlugins-namerHooks.scala new file mode 100644 index 0000000000..b0f29cbbed --- /dev/null +++ b/test/files/run/macroPlugins-namerHooks.scala @@ -0,0 +1,38 @@ +import scala.tools.partest._ +import scala.tools.nsc._ + +object Test extends DirectTest { + override def extraSettings: String = "-usejavacp" + + def code = """ + case class C(x: Int, y: Int) + """.trim + + def show() { + val global = newCompiler() + import global._ + import analyzer._ + + val output = collection.mutable.ListBuffer[String]() + + object macroPlugin extends MacroPlugin { + override def pluginsEnterSym(namer: Namer, tree: Tree): Boolean = { + output += s"enterSym(${tree.toString.replace('\n', ' ')})" + namer.enterSym(tree) + true + } + override def pluginsEnsureCompanionObject(namer: Namer, cdef: ClassDef, creator: ClassDef => Tree = companionModuleDef(_)): Option[Symbol] = { + output += s"ensureCompanionObject(${cdef.toString.replace('\n', ' ')}, ...)" + Some(namer.ensureCompanionObject(cdef, creator)) + } + override def pluginsEnterStats(typer: Typer, stats: List[Tree]): List[Tree] = { + stats.foreach(stat => output += s"enterStat(${stat.toString.replace('\n', ' ')})") + stats + } + } + + addMacroPlugin(macroPlugin) + compileString(global)(code) + println(output.mkString("\n")) + } +} diff --git a/test/files/run/macroPlugins-typedMacroBody.check b/test/files/run/macroPlugins-typedMacroBody.check new file mode 100644 index 0000000000..b6f8436189 --- /dev/null +++ b/test/files/run/macroPlugins-typedMacroBody.check @@ -0,0 +1,2 @@ +impl1 +impl2 diff --git a/test/files/run/macroPlugins-typedMacroBody/Macros_2.flags b/test/files/run/macroPlugins-typedMacroBody/Macros_2.flags new file mode 100644 index 0000000000..966df731d0 --- /dev/null +++ b/test/files/run/macroPlugins-typedMacroBody/Macros_2.flags @@ -0,0 +1 @@ +-Xplugin:. \ No newline at end of file diff --git a/test/files/run/macroPlugins-typedMacroBody/Macros_2.scala b/test/files/run/macroPlugins-typedMacroBody/Macros_2.scala new file mode 100644 index 0000000000..fa8522e729 --- /dev/null +++ b/test/files/run/macroPlugins-typedMacroBody/Macros_2.scala @@ -0,0 +1,18 @@ +import scala.language.experimental.macros +import scala.reflect.macros.BlackboxContext + +object Macros { + def impl1(c: BlackboxContext) = { + import c.universe._ + q"""println("impl1")""" + } + + def impl2(c: BlackboxContext) = { + import c.universe._ + q"""println("impl2")""" + } + + def foo1: Unit = macro 1 + + def foo2: Unit = macro 2 +} \ No newline at end of file diff --git a/test/files/run/macroPlugins-typedMacroBody/Plugin_1.scala b/test/files/run/macroPlugins-typedMacroBody/Plugin_1.scala new file mode 100644 index 0000000000..4632c36ef5 --- /dev/null +++ b/test/files/run/macroPlugins-typedMacroBody/Plugin_1.scala @@ -0,0 +1,21 @@ +package typedMacroBody + +import scala.tools.nsc.Global +import scala.tools.nsc.plugins.{Plugin => NscPlugin} + +class Plugin(val global: Global) extends NscPlugin { + import global._ + import analyzer._ + + val name = "typedMacroBody" + val description = "A sample analyzer plugin that overrides typedMacroBody." + val components = Nil + addMacroPlugin(MacroPlugin) + + object MacroPlugin extends MacroPlugin { + override def pluginsTypedMacroBody(typer: Typer, ddef: DefDef): Option[Tree] = { + val DefDef(_, _, _, _, _, Literal(Constant(num: Int))) = ddef + Some(typedMacroBody(typer, copyDefDef(ddef)(rhs = Ident(TermName("impl" + num))))) + } + } +} \ No newline at end of file diff --git a/test/files/run/macroPlugins-typedMacroBody/Test_3.scala b/test/files/run/macroPlugins-typedMacroBody/Test_3.scala new file mode 100644 index 0000000000..def9b5608a --- /dev/null +++ b/test/files/run/macroPlugins-typedMacroBody/Test_3.scala @@ -0,0 +1,4 @@ +object Test extends App { + Macros.foo1 + Macros.foo2 +} \ No newline at end of file diff --git a/test/files/run/macroPlugins-typedMacroBody/scalac-plugin.xml b/test/files/run/macroPlugins-typedMacroBody/scalac-plugin.xml new file mode 100644 index 0000000000..e223fa5dca --- /dev/null +++ b/test/files/run/macroPlugins-typedMacroBody/scalac-plugin.xml @@ -0,0 +1,4 @@ + + typed-macro-body + typedMacroBody.Plugin + \ No newline at end of file -- cgit v1.2.3 From 87913661e199e3894190b7b8aa0900d7237feec0 Mon Sep 17 00:00:00 2001 From: Eugene Burmako Date: Mon, 9 Dec 2013 09:45:31 +0100 Subject: 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. --- .../reflect/macros/runtime/MacroRuntimes.scala | 7 ++- .../scala/tools/nsc/typechecker/Analyzer.scala | 2 +- .../tools/nsc/typechecker/AnalyzerPlugins.scala | 50 +++++++++++----------- .../scala/tools/nsc/typechecker/Macros.scala | 30 +++++++++---- .../scala/tools/nsc/typechecker/Namers.scala | 27 ++++++++---- .../scala/tools/nsc/typechecker/Typers.scala | 12 +++--- .../scala/tools/nsc/interpreter/ReplGlobal.scala | 2 +- .../macro-incompatible-macro-engine/Plugin_1.scala | 2 +- .../run/macroPlugins-macroArgs/Plugin_1.scala | 2 +- test/files/run/macroPlugins-namerHooks.scala | 4 +- .../run/macroPlugins-typedMacroBody/Plugin_1.scala | 2 +- 11 files changed, 86 insertions(+), 54 deletions(-) (limited to 'test/files/run') diff --git a/src/compiler/scala/reflect/macros/runtime/MacroRuntimes.scala b/src/compiler/scala/reflect/macros/runtime/MacroRuntimes.scala index 2b73d932d4..14b0c6baba 100644 --- a/src/compiler/scala/reflect/macros/runtime/MacroRuntimes.scala +++ b/src/compiler/scala/reflect/macros/runtime/MacroRuntimes.scala @@ -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) { diff --git a/src/compiler/scala/tools/nsc/typechecker/Analyzer.scala b/src/compiler/scala/tools/nsc/typechecker/Analyzer.scala index 70ff1051c1..5c02516c47 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Analyzer.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Analyzer.scala @@ -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) } } } diff --git a/src/compiler/scala/tools/nsc/typechecker/AnalyzerPlugins.scala b/src/compiler/scala/tools/nsc/typechecker/AnalyzerPlugins.scala index 61becab483..fa6e5399eb 100644 --- a/src/compiler/scala/tools/nsc/typechecker/AnalyzerPlugins.scala +++ b/src/compiler/scala/tools/nsc/typechecker/AnalyzerPlugins.scala @@ -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,7 +371,7 @@ 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) }) @@ -379,7 +379,7 @@ trait AnalyzerPlugins { self: Analyzer => 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) }) @@ -387,7 +387,7 @@ trait AnalyzerPlugins { self: Analyzer => 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) }) @@ -395,30 +395,32 @@ trait AnalyzerPlugins { self: Analyzer => 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) }) diff --git a/src/compiler/scala/tools/nsc/typechecker/Macros.scala b/src/compiler/scala/tools/nsc/typechecker/Macros.scala index 105e975c86..006ab792fc 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Macros.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Macros.scala @@ -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 }) diff --git a/src/compiler/scala/tools/nsc/typechecker/Namers.scala b/src/compiler/scala/tools/nsc/typechecker/Namers.scala index ee597528a9..6bb13c4e0b 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Namers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Namers.scala @@ -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,7 +673,7 @@ 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 { @@ -670,7 +681,7 @@ trait Namers extends MethodSynthesis { 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 } diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index 8ddaccf16c..38adb35bfa 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -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 /** 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 } diff --git a/src/repl/scala/tools/nsc/interpreter/ReplGlobal.scala b/src/repl/scala/tools/nsc/interpreter/ReplGlobal.scala index 498ff46624..51fab3082e 100644 --- a/src/repl/scala/tools/nsc/interpreter/ReplGlobal.scala +++ b/src/repl/scala/tools/nsc/interpreter/ReplGlobal.scala @@ -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) } } } diff --git a/test/files/neg/macro-incompatible-macro-engine/Plugin_1.scala b/test/files/neg/macro-incompatible-macro-engine/Plugin_1.scala index a450573755..44ed91d2fb 100644 --- a/test/files/neg/macro-incompatible-macro-engine/Plugin_1.scala +++ b/test/files/neg/macro-incompatible-macro-engine/Plugin_1.scala @@ -25,7 +25,7 @@ class Plugin(val global: Global) extends NscPlugin { }.transform(tree) override def pluginsTypedMacroBody(typer: Typer, ddef: DefDef): Option[Tree] = { - val result = typedMacroBody(typer, ddef) + val result = standardTypedMacroBody(typer, ddef) val List(AnnotationInfo(atp, List(Apply(nucleus, _ :: others)), Nil)) = ddef.symbol.annotations val updatedBinding = Apply(nucleus, Assign(Literal(Constant("macroEngine")), Literal(Constant("vxxx (implemented in the incompatibleMacroEngine plugin)"))) :: others) ddef.symbol.setAnnotations(List(AnnotationInfo(atp, List(fixupBinding(updatedBinding)), Nil))) diff --git a/test/files/run/macroPlugins-macroArgs/Plugin_1.scala b/test/files/run/macroPlugins-macroArgs/Plugin_1.scala index 04cedbdc64..23e80ced3b 100644 --- a/test/files/run/macroPlugins-macroArgs/Plugin_1.scala +++ b/test/files/run/macroPlugins-macroArgs/Plugin_1.scala @@ -14,7 +14,7 @@ class Plugin(val global: Global) extends NscPlugin { object MacroPlugin extends MacroPlugin { override def pluginsMacroArgs(typer: Typer, expandee: Tree): Option[MacroArgs] = { - val MacroArgs(c, List(Literal(Constant(s: String)))) = macroArgs(typer, expandee) + val MacroArgs(c, List(Literal(Constant(s: String)))) = standardMacroArgs(typer, expandee) Some(MacroArgs(c, List(Literal(Constant("hijacked " + s))))) } } diff --git a/test/files/run/macroPlugins-namerHooks.scala b/test/files/run/macroPlugins-namerHooks.scala index b0f29cbbed..a71d685f7f 100644 --- a/test/files/run/macroPlugins-namerHooks.scala +++ b/test/files/run/macroPlugins-namerHooks.scala @@ -18,12 +18,12 @@ object Test extends DirectTest { object macroPlugin extends MacroPlugin { override def pluginsEnterSym(namer: Namer, tree: Tree): Boolean = { output += s"enterSym(${tree.toString.replace('\n', ' ')})" - namer.enterSym(tree) + namer.standardEnterSym(tree) true } override def pluginsEnsureCompanionObject(namer: Namer, cdef: ClassDef, creator: ClassDef => Tree = companionModuleDef(_)): Option[Symbol] = { output += s"ensureCompanionObject(${cdef.toString.replace('\n', ' ')}, ...)" - Some(namer.ensureCompanionObject(cdef, creator)) + Some(namer.standardEnsureCompanionObject(cdef, creator)) } override def pluginsEnterStats(typer: Typer, stats: List[Tree]): List[Tree] = { stats.foreach(stat => output += s"enterStat(${stat.toString.replace('\n', ' ')})") diff --git a/test/files/run/macroPlugins-typedMacroBody/Plugin_1.scala b/test/files/run/macroPlugins-typedMacroBody/Plugin_1.scala index 4632c36ef5..e99cf7f75d 100644 --- a/test/files/run/macroPlugins-typedMacroBody/Plugin_1.scala +++ b/test/files/run/macroPlugins-typedMacroBody/Plugin_1.scala @@ -15,7 +15,7 @@ class Plugin(val global: Global) extends NscPlugin { object MacroPlugin extends MacroPlugin { override def pluginsTypedMacroBody(typer: Typer, ddef: DefDef): Option[Tree] = { val DefDef(_, _, _, _, _, Literal(Constant(num: Int))) = ddef - Some(typedMacroBody(typer, copyDefDef(ddef)(rhs = Ident(TermName("impl" + num))))) + Some(standardTypedMacroBody(typer, copyDefDef(ddef)(rhs = Ident(TermName("impl" + num))))) } } } \ No newline at end of file -- cgit v1.2.3