From 0500452f3ae23a7da55e42bbfc6d36117a041262 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sat, 7 Jul 2018 13:25:55 +0200 Subject: [PATCH 1/2] Fix #4758: Revert "New phase for entering annotations" This reverts commit 2b12868070be50fb70c687bcd8a415acbe398e3e. Since we do not plan to run annotation macros during Namer, there's no need to annotate before completing. This allows again to define an annotation in the same scope as an annotated definition. So i3702.scala goes from neg to pos. --- .../src/dotty/tools/dotc/typer/FrontEnd.scala | 7 -- .../src/dotty/tools/dotc/typer/Namer.scala | 111 ++++-------------- .../src/dotty/tools/dotc/typer/ReTyper.scala | 1 - .../src/dotty/tools/dotc/typer/Typer.scala | 4 +- .../src/dotty/tools/repl/ReplFrontEnd.scala | 1 - tests/{neg => pos}/i3702.scala | 2 +- tests/pos/i4758/Test1.scala | 4 + tests/pos/i4758/Test2.scala | 7 ++ tests/pos/i4758/Test3.scala | 3 + 9 files changed, 40 insertions(+), 100 deletions(-) rename tests/{neg => pos}/i3702.scala (54%) create mode 100644 tests/pos/i4758/Test1.scala create mode 100644 tests/pos/i4758/Test2.scala create mode 100644 tests/pos/i4758/Test3.scala 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..b040acf7c53c 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,22 @@ class Namer { typer: Typer => else completeInCreationContext(denot) } + protected def addAnnotations(sym: Symbol): Unit = original match { + case original: untpd.MemberDef => + var hasInlineAnnot = false + lazy val annotCtx = + if (sym.is(Param)) ctx.outersIterator.dropWhile(_.owner == sym.owner).next + else ctx + 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 +776,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 +892,7 @@ class Namer { typer: Typer => } } - addAnnotations(denot.symbol, original) + addAnnotations(denot.symbol) val selfInfo = if (self.isEmpty) NoType @@ -978,9 +915,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 +960,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 +1157,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..a743d7689a64 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) 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 +} From 0c35b8f1b132da60fe07c866ef7b63e25b17e775 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sat, 7 Jul 2018 14:48:26 +0200 Subject: [PATCH 2/2] Address review comments --- .../src/dotty/tools/dotc/typer/Namer.scala | 5 +--- .../src/dotty/tools/dotc/typer/Typer.scala | 28 +++++++++++-------- 2 files changed, 18 insertions(+), 15 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/typer/Namer.scala b/compiler/src/dotty/tools/dotc/typer/Namer.scala index b040acf7c53c..bce824e87add 100644 --- a/compiler/src/dotty/tools/dotc/typer/Namer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Namer.scala @@ -748,10 +748,7 @@ class Namer { typer: Typer => protected def addAnnotations(sym: Symbol): Unit = original match { case original: untpd.MemberDef => - var hasInlineAnnot = false - lazy val annotCtx = - if (sym.is(Param)) ctx.outersIterator.dropWhile(_.owner == sym.owner).next - else ctx + 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)) diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index a743d7689a64..1bda76ed8ccb 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -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)) }