Skip to content

Commit db0dd51

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 79ca94c commit db0dd51

File tree

6 files changed

+121
-32
lines changed

6 files changed

+121
-32
lines changed

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

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -633,16 +633,23 @@ class Definitions {
633633
name.startsWith(prefix) && name.drop(prefix.length).forall(_.isDigit)
634634
}
635635

636-
def isBottomClass(cls: Symbol) =
636+
def isBottomClass(cls: Symbol) =
637637
cls == NothingClass || cls == NullClass
638-
def isBottomType(tp: Type) =
638+
def isBottomType(tp: Type) =
639639
tp.derivesFrom(NothingClass) || tp.derivesFrom(NullClass)
640640

641641
def isFunctionClass(cls: Symbol) = isVarArityClass(cls, tpnme.Function)
642642
def isAbstractFunctionClass(cls: Symbol) = isVarArityClass(cls, tpnme.AbstractFunction)
643643
def isTupleClass(cls: Symbol) = isVarArityClass(cls, tpnme.Tuple)
644644
def isProductClass(cls: Symbol) = isVarArityClass(cls, tpnme.Product)
645645

646+
val predefClassNames: Set[Name] =
647+
Set("Predef$", "DeprecatedPredef", "LowPriorityImplicits").map(_.toTypeName)
648+
649+
/** Is `cls` the predef module class, or a class inherited by Predef? */
650+
def isPredefClass(cls: Symbol) =
651+
(cls.owner eq ScalaPackageClass) && predefClassNames.contains(cls.name)
652+
646653
val StaticRootImportFns = List[() => TermRef](
647654
() => JavaLangPackageVal.termRef,
648655
() => ScalaPackageVal.termRef
@@ -785,8 +792,8 @@ class Definitions {
785792
if (!_isInitialized) {
786793
// force initialization of every symbol that is synthesized or hijacked by the compiler
787794
val forced = syntheticCoreClasses ++ syntheticCoreMethods ++ ScalaValueClasses()
788-
789-
// Enter all symbols from the scalaShadowing package in the scala package
795+
796+
// Enter all symbols from the scalaShadowing package in the scala package
790797
for (m <- ScalaShadowingPackageClass.info.decls)
791798
ScalaPackageClass.enter(m)
792799

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

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ class FrontEnd extends Phase {
3535
else new Parser(unit.source).parse()
3636
val printer = if (ctx.settings.Xprint.value.contains("parser")) default else typr
3737
printer.println("parsed:\n" + unit.untpdTree.show)
38-
if (Config.checkPositions)
38+
if (Config.checkPositions)
3939
unit.untpdTree.checkPos(nonOverlapping = !unit.isJava && !ctx.reporter.hasErrors)
4040
}
4141

@@ -45,6 +45,12 @@ class FrontEnd extends Phase {
4545
typr.println("entered: " + unit.source)
4646
}
4747

48+
def enterAnnotations(implicit ctx: Context) = monitor("annotating") {
49+
val unit = ctx.compilationUnit
50+
ctx.typer.annotate(unit.untpdTree :: Nil)
51+
typr.println("annotated: " + unit.source)
52+
}
53+
4854
def typeCheck(implicit ctx: Context) = monitor("typechecking") {
4955
val unit = ctx.compilationUnit
5056
unit.tpdTree = ctx.typer.typedExpr(unit.untpdTree)
@@ -68,8 +74,9 @@ class FrontEnd extends Phase {
6874
ctx.fresh.setCompilationUnit(unit).setFreshNames(new FreshNameCreator.Default)
6975
unitContexts foreach (parse(_))
7076
record("parsedTrees", ast.Trees.ntrees)
71-
unitContexts foreach (enterSyms(_))
72-
unitContexts foreach (typeCheck(_))
77+
unitContexts foreach(enterSyms(_))
78+
unitContexts.foreach(enterAnnotations(_))
79+
unitContexts foreach(typeCheck(_))
7380
record("totalTrees", ast.Trees.ntrees)
7481
unitContexts.map(_.compilationUnit).filterNot(discardAfterTyper)
7582
}

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

Lines changed: 89 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -517,18 +517,96 @@ class Namer { typer: Typer =>
517517
}
518518
}
519519

520-
stats foreach expand
520+
stats.foreach(expand)
521521
mergeCompanionDefs()
522522
val ctxWithStats = (ctx /: stats) ((ctx, stat) => indexExpanded(stat)(ctx))
523523
createCompanionLinks(ctxWithStats)
524524
ctxWithStats
525525
}
526526

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

530605
protected def localContext(owner: Symbol) = ctx.fresh.setOwner(owner).setTree(original)
531606

607+
/** The context with which this completer was created */
608+
def creationContext = ctx
609+
532610
protected def typeSig(sym: Symbol): Type = original match {
533611
case original: ValDef =>
534612
if (sym is Module) moduleValSig(sym)
@@ -561,19 +639,6 @@ class Namer { typer: Typer =>
561639
completeInCreationContext(denot)
562640
}
563641

564-
protected def addAnnotations(denot: SymDenotation): Unit = original match {
565-
case original: untpd.MemberDef =>
566-
var hasInlineAnnot = false
567-
for (annotTree <- untpd.modsDeco(original).mods.annotations) {
568-
val cls = typedAheadAnnotation(annotTree)
569-
val ann = Annotation.deferred(cls, implicit ctx => typedAnnotation(annotTree))
570-
denot.addAnnotation(ann)
571-
if (cls == defn.InlineAnnot && denot.is(Method, butNot = Accessor))
572-
denot.setFlag(Inline)
573-
}
574-
case _ =>
575-
}
576-
577642
private def addInlineInfo(denot: SymDenotation) = original match {
578643
case original: untpd.DefDef if denot.isInlineMethod =>
579644
Inliner.registerInlineInfo(
@@ -587,7 +652,10 @@ class Namer { typer: Typer =>
587652
* to pick up the context at the point where the completer was created.
588653
*/
589654
def completeInCreationContext(denot: SymDenotation): Unit = {
590-
addAnnotations(denot)
655+
original match {
656+
case original: MemberDef => addAnnotations(denot.symbol, original)
657+
case _ =>
658+
}
591659
addInlineInfo(denot)
592660
denot.info = typeSig(denot.symbol)
593661
Checking.checkWellFormed(denot.symbol)
@@ -718,7 +786,7 @@ class Namer { typer: Typer =>
718786
ok
719787
}
720788

721-
addAnnotations(denot)
789+
addAnnotations(denot.symbol, original)
722790

723791
val selfInfo =
724792
if (self.isEmpty) NoType
@@ -741,9 +809,10 @@ class Namer { typer: Typer =>
741809
// accessors, that's why the constructor needs to be completed before
742810
// the parent types are elaborated.
743811
index(constr)
812+
annotate(constr :: params)
744813
symbolOfTree(constr).ensureCompleted()
745814

746-
index(rest)(inClassContext(selfInfo))
815+
indexAndAnnotate(rest)(inClassContext(selfInfo))
747816

748817
val tparamAccessors = decls.filter(_ is TypeParamAccessor).toList
749818
val parentTypes = ensureFirstIsClass(parents.map(checkedParentType(_, tparamAccessors)))
@@ -766,7 +835,7 @@ class Namer { typer: Typer =>
766835
case Some(ttree) => ttree
767836
case none =>
768837
val ttree = typer.typed(tree, pt)
769-
xtree.pushAttachment(TypedAhead, ttree)
838+
xtree.putAttachment(TypedAhead, ttree)
770839
ttree
771840
}
772841
}
@@ -786,7 +855,7 @@ class Namer { typer: Typer =>
786855

787856
/** Enter and typecheck parameter list */
788857
def completeParams(params: List[MemberDef])(implicit ctx: Context) = {
789-
index(params)
858+
indexAndAnnotate(params)
790859
for (param <- params) typedAheadExpr(param)
791860
}
792861

@@ -966,7 +1035,7 @@ class Namer { typer: Typer =>
9661035
// 3. Info of CP is computed (to be copied to DP).
9671036
// 4. CP is completed.
9681037
// 5. Info of CP is copied to DP and DP is completed.
969-
index(tparams)
1038+
indexAndAnnotate(tparams)
9701039
if (isConstructor) sym.owner.typeParams.foreach(_.ensureCompleted())
9711040
for (tparam <- tparams) typedAheadExpr(tparam)
9721041

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)

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

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

189189
def selection(name: Name) =
190-
if (unimported.contains(imp.site.termSymbol))
191-
NoType
192-
else if (imp.sym.isCompleting) {
190+
if (imp.sym.isCompleting) {
193191
ctx.warning(i"cyclic ${imp.sym}, ignored", tree.pos)
194192
NoType
195193
}
194+
else if (unimported.nonEmpty && unimported.contains(imp.site.termSymbol))
195+
NoType
196196
else {
197197
// Pass refctx so that any errors are reported in the context of the
198198
// reference instead of the
@@ -583,7 +583,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
583583
}
584584

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

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

10731073
def typedPolyTypeTree(tree: untpd.PolyTypeTree)(implicit ctx: Context): Tree = track("typedPolyTypeTree") {
10741074
val PolyTypeTree(tparams, body) = tree
1075-
index(tparams)
1075+
indexAndAnnotate(tparams)
10761076
val tparams1 = tparams.mapconserve(typed(_).asInstanceOf[TypeDef])
10771077
val body1 = typedType(tree.body)
10781078
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)