From 2b12868070be50fb70c687bcd8a415acbe398e3e Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 10 Nov 2016 11:54:27 +0100 Subject: New phase for entering annotations If we want to do annotation macros right, we need to add annotations before completing definitions. This commit achieves that by adding a new "phase" between index and typecheck. --- .../src/dotty/tools/dotc/core/Definitions.scala | 7 ++ compiler/src/dotty/tools/dotc/typer/FrontEnd.scala | 11 ++- compiler/src/dotty/tools/dotc/typer/Namer.scala | 109 +++++++++++++++++---- compiler/src/dotty/tools/dotc/typer/ReTyper.scala | 1 + compiler/src/dotty/tools/dotc/typer/Typer.scala | 10 +- 5 files changed, 111 insertions(+), 27 deletions(-) (limited to 'compiler/src/dotty/tools') diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index 4b090d9b1..c5ccab261 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -650,6 +650,13 @@ class Definitions { def isTupleClass(cls: Symbol) = isVarArityClass(cls, tpnme.Tuple) def isProductClass(cls: Symbol) = isVarArityClass(cls, tpnme.Product) + val predefClassNames: Set[Name] = + Set("Predef$", "DeprecatedPredef", "LowPriorityImplicits").map(_.toTypeName) + + /** Is `cls` the predef module class, or a class inherited by Predef? */ + def isPredefClass(cls: Symbol) = + (cls.owner eq ScalaPackageClass) && predefClassNames.contains(cls.name) + val StaticRootImportFns = List[() => TermRef]( () => JavaLangPackageVal.termRef, () => ScalaPackageVal.termRef diff --git a/compiler/src/dotty/tools/dotc/typer/FrontEnd.scala b/compiler/src/dotty/tools/dotc/typer/FrontEnd.scala index c444631ae..cd374e32c 100644 --- a/compiler/src/dotty/tools/dotc/typer/FrontEnd.scala +++ b/compiler/src/dotty/tools/dotc/typer/FrontEnd.scala @@ -44,6 +44,12 @@ class FrontEnd extends Phase { typr.println("entered: " + unit.source) } + def enterAnnotations(implicit ctx: Context) = monitor("annotating") { + val unit = ctx.compilationUnit + ctx.typer.annotate(unit.untpdTree :: Nil) + typr.println("annotated: " + unit.source) + } + def typeCheck(implicit ctx: Context) = monitor("typechecking") { val unit = ctx.compilationUnit unit.tpdTree = ctx.typer.typedExpr(unit.untpdTree) @@ -69,8 +75,9 @@ class FrontEnd extends Phase { } unitContexts foreach (parse(_)) record("parsedTrees", ast.Trees.ntrees) - unitContexts foreach (enterSyms(_)) - unitContexts foreach (typeCheck(_)) + unitContexts.foreach(enterSyms(_)) + unitContexts.foreach(enterAnnotations(_)) + unitContexts.foreach(typeCheck(_)) record("total trees after typer", ast.Trees.ntrees) unitContexts.map(_.compilationUnit).filterNot(discardAfterTyper) } diff --git a/compiler/src/dotty/tools/dotc/typer/Namer.scala b/compiler/src/dotty/tools/dotc/typer/Namer.scala index 148cf1da7..9b6bad291 100644 --- a/compiler/src/dotty/tools/dotc/typer/Namer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Namer.scala @@ -528,18 +528,96 @@ class Namer { typer: Typer => } } - stats foreach expand + stats.foreach(expand) mergeCompanionDefs() val ctxWithStats = (ctx /: stats) ((ctx, stat) => indexExpanded(stat)(ctx)) createCompanionLinks(ctxWithStats) ctxWithStats } + /** Add all annotations of definitions in `stats` to the defined symbols */ + def annotate(stats: List[Tree])(implicit ctx: Context): Unit = { + def recur(stat: Tree): Unit = stat match { + case pcl: PackageDef => + annotate(pcl.stats) + case stat: untpd.MemberDef => + stat.getAttachment(SymOfTree) match { + case Some(sym) => + sym.infoOrCompleter match { + case info: Completer if !defn.isPredefClass(sym.owner) => + // Annotate Predef methods only when they are completed; + // This is necessary to break a cyclic dependence between `Predef` + // and `deprecated` in test `compileStdLib`. + addAnnotations(sym, stat)(info.creationContext) + case _ => + // Annotations were already added as part of the symbol's completion + } + case none => + assert(stat.typeOpt.exists, i"no symbol for $stat") + } + case stat: untpd.Thicket => + stat.trees.foreach(recur) + case _ => + } + + for (stat <- stats) recur(expanded(stat)) + } + + /** Add annotations of `stat` to `sym`. + * This method can be called twice on a symbol (e.g. once + * during the `annotate` phase and then again during completion). + * Therefore, care needs to be taken not to add annotations again + * that are already added to the symbol. + */ + def addAnnotations(sym: Symbol, stat: MemberDef)(implicit ctx: Context) = { + // (1) The context in which an annotation of a top-evel class or module is evaluated + // is the closest enclosing context which has the enclosing package as owner. + // (2) The context in which an annotation for any other symbol is evaluated is the + // closest enclosing context which has the owner of the class enclpsing the symbol as owner. + // E.g in + // + // package p + // import a.b + // class C { + // import d.e + // @ann m() ... + // } + // + // `@ann` is evaluated in the context just outside `C`, where the `a.b` + // import is visible but the `d.e` import is forgotten. This measure is necessary + // in order to avoid cycles. + lazy val annotCtx = { + var target = sym.owner.lexicallyEnclosingClass + if (!target.is(PackageClass)) target = target.owner + var c = ctx + while (c.owner != target) c = c.outer + c + } + for (annotTree <- untpd.modsDeco(stat).mods.annotations) { + val cls = typedAheadAnnotation(annotTree)(annotCtx) + if (sym.unforcedAnnotation(cls).isEmpty) { + val ann = Annotation.deferred(cls, implicit ctx => typedAnnotation(annotTree)) + sym.addAnnotation(ann) + if (cls == defn.InlineAnnot && sym.is(Method, butNot = Accessor)) + sym.setFlag(Inline) + } + } + } + + def indexAndAnnotate(stats: List[Tree])(implicit ctx: Context): Context = { + val localCtx = index(stats) + annotate(stats) + localCtx + } + /** The completer of a symbol defined by a member def or import (except ClassSymbols) */ class Completer(val original: Tree)(implicit ctx: Context) extends LazyType { protected def localContext(owner: Symbol) = ctx.fresh.setOwner(owner).setTree(original) + /** The context with which this completer was created */ + def creationContext = ctx + protected def typeSig(sym: Symbol): Type = original match { case original: ValDef => if (sym is Module) moduleValSig(sym) @@ -572,19 +650,6 @@ class Namer { typer: Typer => completeInCreationContext(denot) } - protected def addAnnotations(denot: SymDenotation): Unit = original match { - case original: untpd.MemberDef => - var hasInlineAnnot = false - for (annotTree <- untpd.modsDeco(original).mods.annotations) { - val cls = typedAheadAnnotation(annotTree) - val ann = Annotation.deferred(cls, implicit ctx => typedAnnotation(annotTree)) - denot.addAnnotation(ann) - if (cls == defn.InlineAnnot && denot.is(Method, butNot = Accessor)) - denot.setFlag(Inline) - } - case _ => - } - private def addInlineInfo(denot: SymDenotation) = original match { case original: untpd.DefDef if denot.isInlineMethod => Inliner.registerInlineInfo( @@ -598,7 +663,10 @@ class Namer { typer: Typer => * to pick up the context at the point where the completer was created. */ def completeInCreationContext(denot: SymDenotation): Unit = { - addAnnotations(denot) + original match { + case original: MemberDef => addAnnotations(denot.symbol, original) + case _ => + } addInlineInfo(denot) denot.info = typeSig(denot.symbol) Checking.checkWellFormed(denot.symbol) @@ -742,7 +810,7 @@ class Namer { typer: Typer => ok } - addAnnotations(denot) + addAnnotations(denot.symbol, original) val selfInfo = if (self.isEmpty) NoType @@ -765,9 +833,10 @@ class Namer { typer: Typer => // accessors, that's why the constructor needs to be completed before // the parent types are elaborated. index(constr) + annotate(constr :: params) symbolOfTree(constr).ensureCompleted() - index(rest)(inClassContext(selfInfo)) + indexAndAnnotate(rest)(inClassContext(selfInfo)) val tparamAccessors = decls.filter(_ is TypeParamAccessor).toList val parentTypes = ensureFirstIsClass(parents.map(checkedParentType(_, tparamAccessors))) @@ -790,7 +859,7 @@ class Namer { typer: Typer => case Some(ttree) => ttree case none => val ttree = typer.typed(tree, pt) - xtree.pushAttachment(TypedAhead, ttree) + xtree.putAttachment(TypedAhead, ttree) ttree } } @@ -810,7 +879,7 @@ class Namer { typer: Typer => /** Enter and typecheck parameter list */ def completeParams(params: List[MemberDef])(implicit ctx: Context) = { - index(params) + indexAndAnnotate(params) for (param <- params) typedAheadExpr(param) } @@ -990,7 +1059,7 @@ class Namer { typer: Typer => // 3. Info of CP is computed (to be copied to DP). // 4. CP is completed. // 5. Info of CP is copied to DP and DP is completed. - index(tparams) + indexAndAnnotate(tparams) if (isConstructor) sym.owner.typeParams.foreach(_.ensureCompleted()) for (tparam <- tparams) typedAheadExpr(tparam) diff --git a/compiler/src/dotty/tools/dotc/typer/ReTyper.scala b/compiler/src/dotty/tools/dotc/typer/ReTyper.scala index 2413c0c22..f0acdef12 100644 --- a/compiler/src/dotty/tools/dotc/typer/ReTyper.scala +++ b/compiler/src/dotty/tools/dotc/typer/ReTyper.scala @@ -72,6 +72,7 @@ class ReTyper extends Typer { override def localTyper(sym: Symbol) = this override def index(trees: List[untpd.Tree])(implicit ctx: Context) = ctx + override def annotate(trees: List[untpd.Tree])(implicit ctx: Context) = () override def tryInsertApplyOrImplicit(tree: Tree, pt: ProtoType)(fallBack: (Tree, TyperState) => Tree)(implicit ctx: Context): Tree = fallBack(tree, ctx.typerState) diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 165d2e266..7b079d1ed 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -192,12 +192,12 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit } def selection(name: Name) = - if (unimported.contains(imp.site.termSymbol)) - NoType - else if (imp.sym.isCompleting) { + if (imp.sym.isCompleting) { ctx.warning(i"cyclic ${imp.sym}, ignored", tree.pos) NoType } + else if (unimported.nonEmpty && unimported.contains(imp.site.termSymbol)) + NoType else { // Pass refctx so that any errors are reported in the context of the // reference instead of the @@ -588,7 +588,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit } def typedBlockStats(stats: List[untpd.Tree])(implicit ctx: Context): (Context, List[tpd.Tree]) = - (index(stats), typedStats(stats, ctx.owner)) + (indexAndAnnotate(stats), typedStats(stats, ctx.owner)) def typedBlock(tree: untpd.Block, pt: Type)(implicit ctx: Context) = track("typedBlock") { val (exprCtx, stats1) = typedBlockStats(tree.stats) @@ -1070,7 +1070,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit def typedPolyTypeTree(tree: untpd.PolyTypeTree)(implicit ctx: Context): Tree = track("typedPolyTypeTree") { val PolyTypeTree(tparams, body) = tree - index(tparams) + indexAndAnnotate(tparams) val tparams1 = tparams.mapconserve(typed(_).asInstanceOf[TypeDef]) val body1 = typedType(tree.body) assignType(cpy.PolyTypeTree(tree)(tparams1, body1), tparams1, body1) -- cgit v1.2.3