Skip to content

Fix #4758: Revert "New phase for entering annotations" #4776

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jul 8, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 0 additions & 7 deletions compiler/src/dotty/tools/dotc/typer/FrontEnd.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down
108 changes: 20 additions & 88 deletions compiler/src/dotty/tools/dotc/typer/Namer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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) = {
Expand Down Expand Up @@ -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(
Expand All @@ -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)
Expand Down Expand Up @@ -955,7 +889,7 @@ class Namer { typer: Typer =>
}
}

addAnnotations(denot.symbol, original)
addAnnotations(denot.symbol)

val selfInfo =
if (self.isEmpty) NoType
Expand All @@ -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)
Expand Down Expand Up @@ -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)
}

Expand Down Expand Up @@ -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)

Expand Down
1 change: 0 additions & 1 deletion compiler/src/dotty/tools/dotc/typer/ReTyper.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
32 changes: 19 additions & 13 deletions compiler/src/dotty/tools/dotc/typer/Typer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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))
}
Expand Down
1 change: 0 additions & 1 deletion compiler/src/dotty/tools/repl/ReplFrontEnd.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand Down
2 changes: 1 addition & 1 deletion tests/neg/i3702.scala → tests/pos/i3702.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}
4 changes: 4 additions & 0 deletions tests/pos/i4758/Test1.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package foo.bar

class Bar extends annotation.StaticAnnotation

7 changes: 7 additions & 0 deletions tests/pos/i4758/Test2.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package foo

import foo.bar.Bar

@Bar
class Foo extends annotation.StaticAnnotation

3 changes: 3 additions & 0 deletions tests/pos/i4758/Test3.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package object foo {
@Foo class Hello
}