diff --git a/compiler/src/dotty/tools/dotc/typer/FrontEnd.scala b/compiler/src/dotty/tools/dotc/typer/FrontEnd.scala index 0bb16e42a634..ac4fa831dc18 100644 --- a/compiler/src/dotty/tools/dotc/typer/FrontEnd.scala +++ b/compiler/src/dotty/tools/dotc/typer/FrontEnd.scala @@ -55,12 +55,6 @@ 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) @@ -91,7 +85,6 @@ class FrontEnd extends Phase { enterSyms(remaining.head) remaining = remaining.tail } - 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 1adf4ad33bdd..bce824e87add 100644 --- a/compiler/src/dotty/tools/dotc/typer/Namer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Namer.scala @@ -687,90 +687,14 @@ class Namer { typer: Typer => 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-level 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 enclosing 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 = typedAheadAnnotationClass(annotTree)(annotCtx) - if (sym.unforcedAnnotation(cls).isEmpty) { - val ann = Annotation.deferred(cls, implicit ctx => typedAheadAnnotation(annotTree)) - sym.addAnnotation(ann) - if (cls == defn.ForceInlineAnnot && sym.is(Method, butNot = Accessor)) - sym.setFlag(Inline) - } - } - } - - def indexAndAnnotate(stats: List[Tree])(implicit ctx: Context): Context = { - val localCtx = index(stats) - annotate(stats) - localCtx - } - - /** Index and annotate symbols in `tree` while asserting the `lateCompile` flag. + /** Index symbols in `tree` while asserting the `lateCompile` flag. * This will cause any old top-level symbol with the same fully qualified * name as a newly created symbol to be replaced. */ def lateEnter(tree: Tree)(implicit ctx: Context) = { val saved = lateCompile lateCompile = true - try indexAndAnnotate(tree :: Nil) finally lateCompile = saved + try index(tree :: Nil) finally lateCompile = saved } def missingType(sym: Symbol, modifier: String)(implicit ctx: Context) = { @@ -822,6 +746,19 @@ class Namer { typer: Typer => else completeInCreationContext(denot) } + protected def addAnnotations(sym: Symbol): Unit = original match { + case original: untpd.MemberDef => + lazy val annotCtx = annotContext(original, sym) + for (annotTree <- untpd.modsDeco(original).mods.annotations) { + val cls = typedAheadAnnotationClass(annotTree)(annotCtx) + val ann = Annotation.deferred(cls, implicit ctx => typedAnnotation(annotTree)) + sym.addAnnotation(ann) + if (cls == defn.ForceInlineAnnot && sym.is(Method, butNot = Accessor)) + sym.setFlag(Inline) + } + case _ => + } + private def addInlineInfo(sym: Symbol) = original match { case original: untpd.DefDef if sym.isInlineableMethod => Inliner.registerInlineInfo( @@ -836,10 +773,7 @@ class Namer { typer: Typer => */ def completeInCreationContext(denot: SymDenotation): Unit = { val sym = denot.symbol - original match { - case original: MemberDef => addAnnotations(sym, original) - case _ => - } + addAnnotations(sym) addInlineInfo(sym) denot.info = typeSig(sym) Checking.checkWellFormed(sym) @@ -955,7 +889,7 @@ class Namer { typer: Typer => } } - addAnnotations(denot.symbol, original) + addAnnotations(denot.symbol) val selfInfo = if (self.isEmpty) NoType @@ -978,9 +912,7 @@ 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) - - indexAndAnnotate(rest)(ctx.inClassContext(selfInfo)) + index(rest)(ctx.inClassContext(selfInfo)) symbolOfTree(constr).ensureCompleted() val parentTypes = ensureFirstIsClass(parents.map(checkedParentType(_)), cls.pos) @@ -1025,7 +957,7 @@ class Namer { typer: Typer => /** Enter and typecheck parameter list */ def completeParams(params: List[MemberDef])(implicit ctx: Context) = { - indexAndAnnotate(params) + index(params) for (param <- params) typedAheadExpr(param) } @@ -1222,7 +1154,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. - indexAndAnnotate(tparams) + index(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 3a330209a352..6846016d0bbc 100644 --- a/compiler/src/dotty/tools/dotc/typer/ReTyper.scala +++ b/compiler/src/dotty/tools/dotc/typer/ReTyper.scala @@ -91,7 +91,6 @@ class ReTyper extends Typer with ReChecking { 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, locked: TypeVars)(fallBack: => Tree)(implicit ctx: Context): Tree = fallBack diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 59d409e395f4..1bda76ed8ccb 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -659,7 +659,7 @@ class Typer extends Namer } def typedBlockStats(stats: List[untpd.Tree])(implicit ctx: Context): (Context, List[tpd.Tree]) = - (indexAndAnnotate(stats), typedStats(stats, ctx.owner)) + (index(stats), typedStats(stats, ctx.owner)) def typedBlock(tree: untpd.Block, pt: Type)(implicit ctx: Context) = track("typedBlock") { val (exprCtx, stats1) = typedBlockStats(tree.stats) @@ -1270,7 +1270,7 @@ class Typer extends Namer def typedLambdaTypeTree(tree: untpd.LambdaTypeTree)(implicit ctx: Context): Tree = track("typedLambdaTypeTree") { val LambdaTypeTree(tparams, body) = tree - indexAndAnnotate(tparams) + index(tparams) val tparams1 = tparams.mapconserve(typed(_).asInstanceOf[TypeDef]) val body1 = typedType(tree.body) assignType(cpy.LambdaTypeTree(tree)(tparams1, body1), tparams1, body1) @@ -1339,20 +1339,26 @@ class Typer extends Namer assignType(cpy.Alternative(tree)(trees1), trees1) } + /** The context to be used for an annotation of `mdef`. + * This should be the context enclosing `mdef`, or if `mdef` defines a parameter + * the context enclosing the owner of `mdef`. + * Furthermore, we need to evaluate annotation arguments in an expression context, + * since classes defined in a such arguments should not be entered into the + * enclosing class. + */ + def annotContext(mdef: untpd.Tree, sym: Symbol)(implicit ctx: Context): Context = { + def isInner(owner: Symbol) = owner == sym || sym.is(Param) && owner == sym.owner + val c = ctx.outersIterator.dropWhile(c => isInner(c.owner)).next() + c.property(ExprOwner) match { + case Some(exprOwner) if c.owner.isClass => c.exprContext(mdef, exprOwner) + case _ => c + } + } + def completeAnnotations(mdef: untpd.MemberDef, sym: Symbol)(implicit ctx: Context): Unit = { // necessary to force annotation trees to be computed. sym.annotations.foreach(_.ensureCompleted) - lazy val annotCtx = { - val c = ctx.outersIterator.dropWhile(_.owner == sym).next() - c.property(ExprOwner) match { - case Some(exprOwner) if c.owner.isClass => - // We need to evaluate annotation arguments in an expression context, since - // classes defined in a such arguments should not be entered into the - // enclosing class. - c.exprContext(mdef, exprOwner) - case _ => c - } - } + lazy val annotCtx = annotContext(mdef, sym) // necessary in order to mark the typed ahead annotations as definitely typed: untpd.modsDeco(mdef).mods.annotations.foreach(typedAnnotation(_)(annotCtx)) } diff --git a/compiler/src/dotty/tools/repl/ReplFrontEnd.scala b/compiler/src/dotty/tools/repl/ReplFrontEnd.scala index da950eafb44b..9b10bfa9b414 100644 --- a/compiler/src/dotty/tools/repl/ReplFrontEnd.scala +++ b/compiler/src/dotty/tools/repl/ReplFrontEnd.scala @@ -21,7 +21,6 @@ private[repl] class REPLFrontEnd extends FrontEnd { val unitContext = ctx.fresh.setCompilationUnit(units.head) enterSyms(unitContext) - enterAnnotations(unitContext) typeCheck(unitContext) List(unitContext.compilationUnit) } diff --git a/tests/neg/i3702.scala b/tests/pos/i3702.scala similarity index 54% rename from tests/neg/i3702.scala rename to tests/pos/i3702.scala index 1809e2ae7e75..a79d2ef074c0 100644 --- a/tests/neg/i3702.scala +++ b/tests/pos/i3702.scala @@ -2,7 +2,7 @@ object test { class annot extends scala.annotation.Annotation def foo = { def bar(i: Int): Int = i - @annot class Silly {} // error: not found + @annot class Silly {} // used to be: not found, but now ok after backing out of 2b12868070be50fb70 bar(5) } } diff --git a/tests/pos/i4758/Test1.scala b/tests/pos/i4758/Test1.scala new file mode 100644 index 000000000000..4e0bcf3ee46a --- /dev/null +++ b/tests/pos/i4758/Test1.scala @@ -0,0 +1,4 @@ +package foo.bar + +class Bar extends annotation.StaticAnnotation + diff --git a/tests/pos/i4758/Test2.scala b/tests/pos/i4758/Test2.scala new file mode 100644 index 000000000000..89edc200742a --- /dev/null +++ b/tests/pos/i4758/Test2.scala @@ -0,0 +1,7 @@ +package foo + +import foo.bar.Bar + +@Bar +class Foo extends annotation.StaticAnnotation + diff --git a/tests/pos/i4758/Test3.scala b/tests/pos/i4758/Test3.scala new file mode 100644 index 000000000000..1257c5d8ba2c --- /dev/null +++ b/tests/pos/i4758/Test3.scala @@ -0,0 +1,3 @@ +package object foo { + @Foo class Hello +}