diff options
Diffstat (limited to 'src/compiler/scala/tools')
4 files changed, 104 insertions, 32 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)) |