Skip to content

Commit 47d2084

Browse files
authored
Merge pull request #1693 from dotty-staging/add-annotations-phase
Add annotations phase
2 parents b9350f4 + e1d79a2 commit 47d2084

17 files changed

+227
-85
lines changed

compiler/src/dotty/tools/dotc/ast/Trees.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ object Trees {
3232
/** Property key for trees with documentation strings attached */
3333
val DocComment = new Property.Key[Comment]
3434

35-
@sharable private var nextId = 0 // for debugging
35+
@sharable private var nextId = 0 // for debugging
3636

3737
type LazyTree = AnyRef /* really: Tree | Lazy[Tree] */
3838
type LazyTreeList = AnyRef /* really: List[Tree] | Lazy[List[Tree]] */

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

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -216,8 +216,8 @@ object Contexts {
216216
else if (isNonEmptyScopeContext) scope.implicitDecls
217217
else Nil
218218
val outerImplicits =
219-
if (isImportContext && importInfo.hiddenRoot.exists)
220-
outer.implicits exclude importInfo.hiddenRoot
219+
if (isImportContext && importInfo.unimported.exists)
220+
outer.implicits exclude importInfo.unimported
221221
else
222222
outer.implicits
223223
if (implicitRefs.isEmpty) outerImplicits
@@ -422,9 +422,18 @@ object Contexts {
422422
final def withOwner(owner: Symbol): Context =
423423
if (owner ne this.owner) fresh.setOwner(owner) else this
424424

425-
override def toString =
425+
final def withProperty[T](key: Key[T], value: Option[T]): Context =
426+
if (property(key) == value) this
427+
else value match {
428+
case Some(v) => fresh.setProperty(key, v)
429+
case None => fresh.dropProperty(key)
430+
}
431+
432+
override def toString = {
433+
def iinfo(implicit ctx: Context) = if (ctx.importInfo == null) "" else i"${ctx.importInfo.selectors}%, %"
426434
"Context(\n" +
427-
(outersIterator map ( ctx => s" owner = ${ctx.owner}, scope = ${ctx.scope}") mkString "\n")
435+
(outersIterator map ( ctx => s" owner = ${ctx.owner}, scope = ${ctx.scope}, import = ${iinfo(ctx)}") mkString "\n")
436+
}
428437
}
429438

430439
/** A condensed context provides only a small memory footprint over
@@ -468,6 +477,9 @@ object Contexts {
468477
def setProperty[T](key: Key[T], value: T): this.type =
469478
setMoreProperties(moreProperties.updated(key, value))
470479

480+
def dropProperty(key: Key[_]): this.type =
481+
setMoreProperties(moreProperties - key)
482+
471483
def setPhase(pid: PhaseId): this.type = setPeriod(Period(runId, pid))
472484
def setPhase(phase: Phase): this.type = setPeriod(Period(runId, phase.start, phase.end))
473485

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/core/Phases.scala

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,10 @@ trait Phases {
2626

2727
def phasesStack: List[Phase] =
2828
if ((this eq NoContext) || !phase.exists) Nil
29-
else phase :: outersIterator.dropWhile(_.phase == phase).next.phasesStack
29+
else {
30+
val rest = outersIterator.dropWhile(_.phase == phase)
31+
phase :: (if (rest.hasNext) rest.next.phasesStack else Nil)
32+
}
3033

3134
/** Execute `op` at given phase */
3235
def atPhase[T](phase: Phase)(op: Context => T): T =

compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -614,14 +614,12 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
614614
(sym.allOverriddenSymbols exists (_ is TypeParam))
615615

616616
override def toText(sym: Symbol): Text = {
617-
if (sym.isImport) {
618-
def importString(tree: untpd.Tree) = s"import ${tree.show}"
617+
if (sym.isImport)
619618
sym.infoOrCompleter match {
620-
case info: Namer#Completer => return importString(info.original)
621-
case info: ImportType => return importString(info.expr)
619+
case info: Namer#Completer => return info.original.show
620+
case info: ImportType => return s"import $info.expr.show"
622621
case _ =>
623622
}
624-
}
625623
if (sym.is(ModuleClass))
626624
kindString(sym) ~~ (nameString(sym.name.stripModuleClassSuffix) + idString(sym))
627625
else

compiler/src/dotty/tools/dotc/repl/CompilingInterpreter.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import java.io.{
66
File, PrintWriter, PrintStream, StringWriter, Writer, OutputStream,
77
ByteArrayOutputStream => ByteOutputStream
88
}
9-
import java.lang.{Class, ClassLoader}
9+
import java.lang.{Class, ClassLoader, Thread, System, StringBuffer}
1010
import java.net.{URL, URLClassLoader}
1111

1212
import scala.collection.immutable.ListSet

compiler/src/dotty/tools/dotc/repl/InterpreterLoop.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ package repl
44

55
import java.io.{BufferedReader, File, FileReader, PrintWriter}
66
import java.io.IOException
7-
import java.lang.{ClassLoader, System}
7+
import java.lang.{ClassLoader, System, Thread}
88
import scala.concurrent.{Future, Await}
99
import scala.concurrent.duration.Duration
1010
import reporting.Reporter

compiler/src/dotty/tools/dotc/transform/CollectEntryPoints.scala

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import dotty.tools.dotc.core.Symbols.NoSymbol
99
import scala.annotation.tailrec
1010
import dotty.tools.dotc.core._
1111
import Symbols._
12-
import scala.Some
1312
import dotty.tools.dotc.transform.TreeTransforms.{NXTransformations, TransformerInfo, TreeTransform, TreeTransformer}
1413
import dotty.tools.dotc.ast.tpd
1514
import dotty.tools.dotc.core.Contexts.Context

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/ImportInfo.scala

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -95,14 +95,22 @@ class ImportInfo(symf: => Symbol, val selectors: List[untpd.Tree], val isRootImp
9595
/** The root import symbol hidden by this symbol, or NoSymbol if no such symbol is hidden.
9696
* Note: this computation needs to work even for un-initialized import infos, and
9797
* is not allowed to force initialization.
98+
*
99+
* TODO: Once we have fully bootstrapped, I would prefer if we expressed
100+
* unimport with an `override` modifier, and generalized it to all imports.
101+
* I believe this would be more transparent than the curren set of conditions. E.g.
102+
*
103+
* override import Predef.{any2stringAdd => _, StringAdd => _, _} // disables String +
104+
* override import java.lang.{} // disables all imports
98105
*/
99-
lazy val hiddenRoot: Symbol = {
100-
val sym = site.termSymbol
101-
def hasMaskingSelector = selectors exists {
106+
lazy val unimported: Symbol = {
107+
lazy val sym = site.termSymbol
108+
val hasMaskingSelector = selectors exists {
102109
case Thicket(_ :: Ident(nme.WILDCARD) :: Nil) => true
103110
case _ => false
104111
}
105-
if ((defn.RootImportTypes exists (_.symbol == sym)) && hasMaskingSelector) sym else NoSymbol
112+
if (hasMaskingSelector && defn.RootImportTypes.exists(_.symbol == sym)) sym
113+
else NoSymbol
106114
}
107115

108116
override def toString = {

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-level 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 enclosing 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)

0 commit comments

Comments
 (0)