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. --- compiler/src/dotty/tools/dotc/typer/Namer.scala | 109 +++++++++++++++++++----- 1 file changed, 89 insertions(+), 20 deletions(-) (limited to 'compiler/src/dotty/tools/dotc/typer/Namer.scala') 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) -- cgit v1.2.3