diff options
author | Eugene Burmako <xeno.by@gmail.com> | 2013-12-07 16:55:38 +0100 |
---|---|---|
committer | Eugene Burmako <xeno.by@gmail.com> | 2013-12-30 19:07:05 +0300 |
commit | 4d92aec651def608628a2275e1b6bf2d1fcbabe7 (patch) | |
tree | d1d51ad3d92343c4345b0b454835a4053234508d /src | |
parent | 6c7b003003e6cb6a1f0b00f79f784e6deea37a39 (diff) | |
download | scala-4d92aec651def608628a2275e1b6bf2d1fcbabe7.tar.gz scala-4d92aec651def608628a2275e1b6bf2d1fcbabe7.tar.bz2 scala-4d92aec651def608628a2275e1b6bf2d1fcbabe7.zip |
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 :)
Diffstat (limited to 'src')
5 files changed, 105 insertions, 33 deletions
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) /** <!-- 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) @@ -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) } } } |