Skip to content

Commit 2b12868

Browse files
committed
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.
1 parent 4ae76f2 commit 2b12868

File tree

6 files changed

+116
-27
lines changed

6 files changed

+116
-27
lines changed

compiler/src/dotty/tools/dotc/core/Definitions.scala

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -650,6 +650,13 @@ class Definitions {
650650
def isTupleClass(cls: Symbol) = isVarArityClass(cls, tpnme.Tuple)
651651
def isProductClass(cls: Symbol) = isVarArityClass(cls, tpnme.Product)
652652

653+
val predefClassNames: Set[Name] =
654+
Set("Predef$", "DeprecatedPredef", "LowPriorityImplicits").map(_.toTypeName)
655+
656+
/** Is `cls` the predef module class, or a class inherited by Predef? */
657+
def isPredefClass(cls: Symbol) =
658+
(cls.owner eq ScalaPackageClass) && predefClassNames.contains(cls.name)
659+
653660
val StaticRootImportFns = List[() => TermRef](
654661
() => JavaLangPackageVal.termRef,
655662
() => ScalaPackageVal.termRef

compiler/src/dotty/tools/dotc/typer/FrontEnd.scala

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,12 @@ class FrontEnd extends Phase {
4444
typr.println("entered: " + unit.source)
4545
}
4646

47+
def enterAnnotations(implicit ctx: Context) = monitor("annotating") {
48+
val unit = ctx.compilationUnit
49+
ctx.typer.annotate(unit.untpdTree :: Nil)
50+
typr.println("annotated: " + unit.source)
51+
}
52+
4753
def typeCheck(implicit ctx: Context) = monitor("typechecking") {
4854
val unit = ctx.compilationUnit
4955
unit.tpdTree = ctx.typer.typedExpr(unit.untpdTree)
@@ -69,8 +75,9 @@ class FrontEnd extends Phase {
6975
}
7076
unitContexts foreach (parse(_))
7177
record("parsedTrees", ast.Trees.ntrees)
72-
unitContexts foreach (enterSyms(_))
73-
unitContexts foreach (typeCheck(_))
78+
unitContexts.foreach(enterSyms(_))
79+
unitContexts.foreach(enterAnnotations(_))
80+
unitContexts.foreach(typeCheck(_))
7481
record("total trees after typer", ast.Trees.ntrees)
7582
unitContexts.map(_.compilationUnit).filterNot(discardAfterTyper)
7683
}

compiler/src/dotty/tools/dotc/typer/Namer.scala

Lines changed: 89 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -528,18 +528,96 @@ class Namer { typer: Typer =>
528528
}
529529
}
530530

531-
stats foreach expand
531+
stats.foreach(expand)
532532
mergeCompanionDefs()
533533
val ctxWithStats = (ctx /: stats) ((ctx, stat) => indexExpanded(stat)(ctx))
534534
createCompanionLinks(ctxWithStats)
535535
ctxWithStats
536536
}
537537

538+
/** Add all annotations of definitions in `stats` to the defined symbols */
539+
def annotate(stats: List[Tree])(implicit ctx: Context): Unit = {
540+
def recur(stat: Tree): Unit = stat match {
541+
case pcl: PackageDef =>
542+
annotate(pcl.stats)
543+
case stat: untpd.MemberDef =>
544+
stat.getAttachment(SymOfTree) match {
545+
case Some(sym) =>
546+
sym.infoOrCompleter match {
547+
case info: Completer if !defn.isPredefClass(sym.owner) =>
548+
// Annotate Predef methods only when they are completed;
549+
// This is necessary to break a cyclic dependence between `Predef`
550+
// and `deprecated` in test `compileStdLib`.
551+
addAnnotations(sym, stat)(info.creationContext)
552+
case _ =>
553+
// Annotations were already added as part of the symbol's completion
554+
}
555+
case none =>
556+
assert(stat.typeOpt.exists, i"no symbol for $stat")
557+
}
558+
case stat: untpd.Thicket =>
559+
stat.trees.foreach(recur)
560+
case _ =>
561+
}
562+
563+
for (stat <- stats) recur(expanded(stat))
564+
}
565+
566+
/** Add annotations of `stat` to `sym`.
567+
* This method can be called twice on a symbol (e.g. once
568+
* during the `annotate` phase and then again during completion).
569+
* Therefore, care needs to be taken not to add annotations again
570+
* that are already added to the symbol.
571+
*/
572+
def addAnnotations(sym: Symbol, stat: MemberDef)(implicit ctx: Context) = {
573+
// (1) The context in which an annotation of a top-evel class or module is evaluated
574+
// is the closest enclosing context which has the enclosing package as owner.
575+
// (2) The context in which an annotation for any other symbol is evaluated is the
576+
// closest enclosing context which has the owner of the class enclpsing the symbol as owner.
577+
// E.g in
578+
//
579+
// package p
580+
// import a.b
581+
// class C {
582+
// import d.e
583+
// @ann m() ...
584+
// }
585+
//
586+
// `@ann` is evaluated in the context just outside `C`, where the `a.b`
587+
// import is visible but the `d.e` import is forgotten. This measure is necessary
588+
// in order to avoid cycles.
589+
lazy val annotCtx = {
590+
var target = sym.owner.lexicallyEnclosingClass
591+
if (!target.is(PackageClass)) target = target.owner
592+
var c = ctx
593+
while (c.owner != target) c = c.outer
594+
c
595+
}
596+
for (annotTree <- untpd.modsDeco(stat).mods.annotations) {
597+
val cls = typedAheadAnnotation(annotTree)(annotCtx)
598+
if (sym.unforcedAnnotation(cls).isEmpty) {
599+
val ann = Annotation.deferred(cls, implicit ctx => typedAnnotation(annotTree))
600+
sym.addAnnotation(ann)
601+
if (cls == defn.InlineAnnot && sym.is(Method, butNot = Accessor))
602+
sym.setFlag(Inline)
603+
}
604+
}
605+
}
606+
607+
def indexAndAnnotate(stats: List[Tree])(implicit ctx: Context): Context = {
608+
val localCtx = index(stats)
609+
annotate(stats)
610+
localCtx
611+
}
612+
538613
/** The completer of a symbol defined by a member def or import (except ClassSymbols) */
539614
class Completer(val original: Tree)(implicit ctx: Context) extends LazyType {
540615

541616
protected def localContext(owner: Symbol) = ctx.fresh.setOwner(owner).setTree(original)
542617

618+
/** The context with which this completer was created */
619+
def creationContext = ctx
620+
543621
protected def typeSig(sym: Symbol): Type = original match {
544622
case original: ValDef =>
545623
if (sym is Module) moduleValSig(sym)
@@ -572,19 +650,6 @@ class Namer { typer: Typer =>
572650
completeInCreationContext(denot)
573651
}
574652

575-
protected def addAnnotations(denot: SymDenotation): Unit = original match {
576-
case original: untpd.MemberDef =>
577-
var hasInlineAnnot = false
578-
for (annotTree <- untpd.modsDeco(original).mods.annotations) {
579-
val cls = typedAheadAnnotation(annotTree)
580-
val ann = Annotation.deferred(cls, implicit ctx => typedAnnotation(annotTree))
581-
denot.addAnnotation(ann)
582-
if (cls == defn.InlineAnnot && denot.is(Method, butNot = Accessor))
583-
denot.setFlag(Inline)
584-
}
585-
case _ =>
586-
}
587-
588653
private def addInlineInfo(denot: SymDenotation) = original match {
589654
case original: untpd.DefDef if denot.isInlineMethod =>
590655
Inliner.registerInlineInfo(
@@ -598,7 +663,10 @@ class Namer { typer: Typer =>
598663
* to pick up the context at the point where the completer was created.
599664
*/
600665
def completeInCreationContext(denot: SymDenotation): Unit = {
601-
addAnnotations(denot)
666+
original match {
667+
case original: MemberDef => addAnnotations(denot.symbol, original)
668+
case _ =>
669+
}
602670
addInlineInfo(denot)
603671
denot.info = typeSig(denot.symbol)
604672
Checking.checkWellFormed(denot.symbol)
@@ -742,7 +810,7 @@ class Namer { typer: Typer =>
742810
ok
743811
}
744812

745-
addAnnotations(denot)
813+
addAnnotations(denot.symbol, original)
746814

747815
val selfInfo =
748816
if (self.isEmpty) NoType
@@ -765,9 +833,10 @@ class Namer { typer: Typer =>
765833
// accessors, that's why the constructor needs to be completed before
766834
// the parent types are elaborated.
767835
index(constr)
836+
annotate(constr :: params)
768837
symbolOfTree(constr).ensureCompleted()
769838

770-
index(rest)(inClassContext(selfInfo))
839+
indexAndAnnotate(rest)(inClassContext(selfInfo))
771840

772841
val tparamAccessors = decls.filter(_ is TypeParamAccessor).toList
773842
val parentTypes = ensureFirstIsClass(parents.map(checkedParentType(_, tparamAccessors)))
@@ -790,7 +859,7 @@ class Namer { typer: Typer =>
790859
case Some(ttree) => ttree
791860
case none =>
792861
val ttree = typer.typed(tree, pt)
793-
xtree.pushAttachment(TypedAhead, ttree)
862+
xtree.putAttachment(TypedAhead, ttree)
794863
ttree
795864
}
796865
}
@@ -810,7 +879,7 @@ class Namer { typer: Typer =>
810879

811880
/** Enter and typecheck parameter list */
812881
def completeParams(params: List[MemberDef])(implicit ctx: Context) = {
813-
index(params)
882+
indexAndAnnotate(params)
814883
for (param <- params) typedAheadExpr(param)
815884
}
816885

@@ -990,7 +1059,7 @@ class Namer { typer: Typer =>
9901059
// 3. Info of CP is computed (to be copied to DP).
9911060
// 4. CP is completed.
9921061
// 5. Info of CP is copied to DP and DP is completed.
993-
index(tparams)
1062+
indexAndAnnotate(tparams)
9941063
if (isConstructor) sym.owner.typeParams.foreach(_.ensureCompleted())
9951064
for (tparam <- tparams) typedAheadExpr(tparam)
9961065

compiler/src/dotty/tools/dotc/typer/ReTyper.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ class ReTyper extends Typer {
7272
override def localTyper(sym: Symbol) = this
7373

7474
override def index(trees: List[untpd.Tree])(implicit ctx: Context) = ctx
75+
override def annotate(trees: List[untpd.Tree])(implicit ctx: Context) = ()
7576

7677
override def tryInsertApplyOrImplicit(tree: Tree, pt: ProtoType)(fallBack: (Tree, TyperState) => Tree)(implicit ctx: Context): Tree =
7778
fallBack(tree, ctx.typerState)

compiler/src/dotty/tools/dotc/typer/Typer.scala

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -192,12 +192,12 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
192192
}
193193

194194
def selection(name: Name) =
195-
if (unimported.contains(imp.site.termSymbol))
196-
NoType
197-
else if (imp.sym.isCompleting) {
195+
if (imp.sym.isCompleting) {
198196
ctx.warning(i"cyclic ${imp.sym}, ignored", tree.pos)
199197
NoType
200198
}
199+
else if (unimported.nonEmpty && unimported.contains(imp.site.termSymbol))
200+
NoType
201201
else {
202202
// Pass refctx so that any errors are reported in the context of the
203203
// reference instead of the
@@ -588,7 +588,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
588588
}
589589

590590
def typedBlockStats(stats: List[untpd.Tree])(implicit ctx: Context): (Context, List[tpd.Tree]) =
591-
(index(stats), typedStats(stats, ctx.owner))
591+
(indexAndAnnotate(stats), typedStats(stats, ctx.owner))
592592

593593
def typedBlock(tree: untpd.Block, pt: Type)(implicit ctx: Context) = track("typedBlock") {
594594
val (exprCtx, stats1) = typedBlockStats(tree.stats)
@@ -1070,7 +1070,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
10701070

10711071
def typedPolyTypeTree(tree: untpd.PolyTypeTree)(implicit ctx: Context): Tree = track("typedPolyTypeTree") {
10721072
val PolyTypeTree(tparams, body) = tree
1073-
index(tparams)
1073+
indexAndAnnotate(tparams)
10741074
val tparams1 = tparams.mapconserve(typed(_).asInstanceOf[TypeDef])
10751075
val body1 = typedType(tree.body)
10761076
assignType(cpy.PolyTypeTree(tree)(tparams1, body1), tparams1, body1)

tests/neg/nopredef.scala

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import Predef.{assert => _}
2+
3+
object Test {
4+
assert("asdf" == "asdf") // error: not found assert
5+
}

0 commit comments

Comments
 (0)