Skip to content

Add annotations phase #1693

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 9 commits into from
Dec 1, 2016
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
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/ast/Trees.scala
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ object Trees {
/** Property key for trees with documentation strings attached */
val DocComment = new Property.Key[Comment]

@sharable private var nextId = 0 // for debugging
@sharable private var nextId = 0 // for debugging

type LazyTree = AnyRef /* really: Tree | Lazy[Tree] */
type LazyTreeList = AnyRef /* really: List[Tree] | Lazy[List[Tree]] */
Expand Down
20 changes: 16 additions & 4 deletions compiler/src/dotty/tools/dotc/core/Contexts.scala
Original file line number Diff line number Diff line change
Expand Up @@ -216,8 +216,8 @@ object Contexts {
else if (isNonEmptyScopeContext) scope.implicitDecls
else Nil
val outerImplicits =
if (isImportContext && importInfo.hiddenRoot.exists)
outer.implicits exclude importInfo.hiddenRoot
if (isImportContext && importInfo.unimported.exists)
outer.implicits exclude importInfo.unimported
else
outer.implicits
if (implicitRefs.isEmpty) outerImplicits
Expand Down Expand Up @@ -422,9 +422,18 @@ object Contexts {
final def withOwner(owner: Symbol): Context =
if (owner ne this.owner) fresh.setOwner(owner) else this

override def toString =
final def withProperty[T](key: Key[T], value: Option[T]): Context =
if (property(key) == value) this
else value match {
case Some(v) => fresh.setProperty(key, v)
case None => fresh.dropProperty(key)
}

override def toString = {
def iinfo(implicit ctx: Context) = if (ctx.importInfo == null) "" else i"${ctx.importInfo.selectors}%, %"
"Context(\n" +
(outersIterator map ( ctx => s" owner = ${ctx.owner}, scope = ${ctx.scope}") mkString "\n")
(outersIterator map ( ctx => s" owner = ${ctx.owner}, scope = ${ctx.scope}, import = ${iinfo(ctx)}") mkString "\n")
}
}

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

def dropProperty(key: Key[_]): this.type =
setMoreProperties(moreProperties - key)

def setPhase(pid: PhaseId): this.type = setPeriod(Period(runId, pid))
def setPhase(phase: Phase): this.type = setPeriod(Period(runId, phase.start, phase.end))

Expand Down
7 changes: 7 additions & 0 deletions compiler/src/dotty/tools/dotc/core/Definitions.scala
Original file line number Diff line number Diff line change
Expand Up @@ -650,6 +650,13 @@ class Definitions {
def isTupleClass(cls: Symbol) = isVarArityClass(cls, tpnme.Tuple)
def isProductClass(cls: Symbol) = isVarArityClass(cls, tpnme.Product)

val predefClassNames: Set[Name] =
Set("Predef$", "DeprecatedPredef", "LowPriorityImplicits").map(_.toTypeName)

/** Is `cls` the predef module class, or a class inherited by Predef? */
def isPredefClass(cls: Symbol) =
(cls.owner eq ScalaPackageClass) && predefClassNames.contains(cls.name)

val StaticRootImportFns = List[() => TermRef](
() => JavaLangPackageVal.termRef,
() => ScalaPackageVal.termRef
Expand Down
5 changes: 4 additions & 1 deletion compiler/src/dotty/tools/dotc/core/Phases.scala
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,10 @@ trait Phases {

def phasesStack: List[Phase] =
if ((this eq NoContext) || !phase.exists) Nil
else phase :: outersIterator.dropWhile(_.phase == phase).next.phasesStack
else {
val rest = outersIterator.dropWhile(_.phase == phase)
phase :: (if (rest.hasNext) rest.next.phasesStack else Nil)
}

/** Execute `op` at given phase */
def atPhase[T](phase: Phase)(op: Context => T): T =
Expand Down
8 changes: 3 additions & 5 deletions compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala
Original file line number Diff line number Diff line change
Expand Up @@ -614,14 +614,12 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
(sym.allOverriddenSymbols exists (_ is TypeParam))

override def toText(sym: Symbol): Text = {
if (sym.isImport) {
def importString(tree: untpd.Tree) = s"import ${tree.show}"
if (sym.isImport)
sym.infoOrCompleter match {
case info: Namer#Completer => return importString(info.original)
case info: ImportType => return importString(info.expr)
case info: Namer#Completer => return info.original.show
case info: ImportType => return s"import $info.expr.show"
case _ =>
}
}
if (sym.is(ModuleClass))
kindString(sym) ~~ (nameString(sym.name.stripModuleClassSuffix) + idString(sym))
else
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import java.io.{
File, PrintWriter, PrintStream, StringWriter, Writer, OutputStream,
ByteArrayOutputStream => ByteOutputStream
}
import java.lang.{Class, ClassLoader}
import java.lang.{Class, ClassLoader, Thread, System, StringBuffer}
import java.net.{URL, URLClassLoader}

import scala.collection.immutable.ListSet
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/repl/InterpreterLoop.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ package repl

import java.io.{BufferedReader, File, FileReader, PrintWriter}
import java.io.IOException
import java.lang.{ClassLoader, System}
import java.lang.{ClassLoader, System, Thread}
import scala.concurrent.{Future, Await}
import scala.concurrent.duration.Duration
import reporting.Reporter
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import dotty.tools.dotc.core.Symbols.NoSymbol
import scala.annotation.tailrec
import dotty.tools.dotc.core._
import Symbols._
import scala.Some
import dotty.tools.dotc.transform.TreeTransforms.{NXTransformations, TransformerInfo, TreeTransform, TreeTransformer}
import dotty.tools.dotc.ast.tpd
import dotty.tools.dotc.core.Contexts.Context
Expand Down
11 changes: 9 additions & 2 deletions compiler/src/dotty/tools/dotc/typer/FrontEnd.scala
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,12 @@ 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 All @@ -69,8 +75,9 @@ class FrontEnd extends Phase {
}
unitContexts foreach (parse(_))
record("parsedTrees", ast.Trees.ntrees)
unitContexts foreach (enterSyms(_))
unitContexts foreach (typeCheck(_))
unitContexts.foreach(enterSyms(_))
unitContexts.foreach(enterAnnotations(_))
unitContexts.foreach(typeCheck(_))
record("total trees after typer", ast.Trees.ntrees)
unitContexts.map(_.compilationUnit).filterNot(discardAfterTyper)
}
Expand Down
16 changes: 12 additions & 4 deletions compiler/src/dotty/tools/dotc/typer/ImportInfo.scala
Original file line number Diff line number Diff line change
Expand Up @@ -95,14 +95,22 @@ class ImportInfo(symf: => Symbol, val selectors: List[untpd.Tree], val isRootImp
/** The root import symbol hidden by this symbol, or NoSymbol if no such symbol is hidden.
* Note: this computation needs to work even for un-initialized import infos, and
* is not allowed to force initialization.
*
* TODO: Once we have fully bootstrapped, I would prefer if we expressed
* unimport with an `override` modifier, and generalized it to all imports.
* I believe this would be more transparent than the curren set of conditions. E.g.
*
* override import Predef.{any2stringAdd => _, StringAdd => _, _} // disables String +
* override import java.lang.{} // disables all imports
*/
lazy val hiddenRoot: Symbol = {
val sym = site.termSymbol
def hasMaskingSelector = selectors exists {
lazy val unimported: Symbol = {
lazy val sym = site.termSymbol
val hasMaskingSelector = selectors exists {
case Thicket(_ :: Ident(nme.WILDCARD) :: Nil) => true
case _ => false
}
if ((defn.RootImportTypes exists (_.symbol == sym)) && hasMaskingSelector) sym else NoSymbol
if (hasMaskingSelector && defn.RootImportTypes.exists(_.symbol == sym)) sym
else NoSymbol
}

override def toString = {
Expand Down
109 changes: 89 additions & 20 deletions compiler/src/dotty/tools/dotc/typer/Namer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -528,18 +528,96 @@ class Namer { typer: Typer =>
}
}

stats foreach expand
stats.foreach(expand)
mergeCompanionDefs()
val ctxWithStats = (ctx /: stats) ((ctx, stat) => indexExpanded(stat)(ctx))
createCompanionLinks(ctxWithStats)
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 = typedAheadAnnotation(annotTree)(annotCtx)
if (sym.unforcedAnnotation(cls).isEmpty) {
val ann = Annotation.deferred(cls, implicit ctx => typedAnnotation(annotTree))
sym.addAnnotation(ann)
if (cls == defn.InlineAnnot && sym.is(Method, butNot = Accessor))
sym.setFlag(Inline)
}
}
}

def indexAndAnnotate(stats: List[Tree])(implicit ctx: Context): Context = {
val localCtx = index(stats)
annotate(stats)
localCtx
}

/** The completer of a symbol defined by a member def or import (except ClassSymbols) */
class Completer(val original: Tree)(implicit ctx: Context) extends LazyType {

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

/** The context with which this completer was created */
def creationContext = ctx

protected def typeSig(sym: Symbol): Type = original match {
case original: ValDef =>
if (sym is Module) moduleValSig(sym)
Expand Down Expand Up @@ -572,19 +650,6 @@ class Namer { typer: Typer =>
completeInCreationContext(denot)
}

protected def addAnnotations(denot: SymDenotation): Unit = original match {
case original: untpd.MemberDef =>
var hasInlineAnnot = false
for (annotTree <- untpd.modsDeco(original).mods.annotations) {
val cls = typedAheadAnnotation(annotTree)
val ann = Annotation.deferred(cls, implicit ctx => typedAnnotation(annotTree))
denot.addAnnotation(ann)
if (cls == defn.InlineAnnot && denot.is(Method, butNot = Accessor))
denot.setFlag(Inline)
}
case _ =>
}

private def addInlineInfo(denot: SymDenotation) = original match {
case original: untpd.DefDef if denot.isInlineMethod =>
Inliner.registerInlineInfo(
Expand All @@ -598,7 +663,10 @@ class Namer { typer: Typer =>
* to pick up the context at the point where the completer was created.
*/
def completeInCreationContext(denot: SymDenotation): Unit = {
addAnnotations(denot)
original match {
case original: MemberDef => addAnnotations(denot.symbol, original)
case _ =>
}
addInlineInfo(denot)
denot.info = typeSig(denot.symbol)
Checking.checkWellFormed(denot.symbol)
Expand Down Expand Up @@ -742,7 +810,7 @@ class Namer { typer: Typer =>
ok
}

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

val selfInfo =
if (self.isEmpty) NoType
Expand All @@ -765,9 +833,10 @@ 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)
symbolOfTree(constr).ensureCompleted()

index(rest)(inClassContext(selfInfo))
indexAndAnnotate(rest)(inClassContext(selfInfo))

val tparamAccessors = decls.filter(_ is TypeParamAccessor).toList
val parentTypes = ensureFirstIsClass(parents.map(checkedParentType(_, tparamAccessors)))
Expand All @@ -790,7 +859,7 @@ class Namer { typer: Typer =>
case Some(ttree) => ttree
case none =>
val ttree = typer.typed(tree, pt)
xtree.pushAttachment(TypedAhead, ttree)
xtree.putAttachment(TypedAhead, ttree)
ttree
}
}
Expand All @@ -810,7 +879,7 @@ class Namer { typer: Typer =>

/** Enter and typecheck parameter list */
def completeParams(params: List[MemberDef])(implicit ctx: Context) = {
index(params)
indexAndAnnotate(params)
for (param <- params) typedAheadExpr(param)
}

Expand Down Expand Up @@ -990,7 +1059,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.
index(tparams)
indexAndAnnotate(tparams)
if (isConstructor) sym.owner.typeParams.foreach(_.ensureCompleted())
for (tparam <- tparams) typedAheadExpr(tparam)

Expand Down
1 change: 1 addition & 0 deletions compiler/src/dotty/tools/dotc/typer/ReTyper.scala
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ class ReTyper extends Typer {
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)(fallBack: (Tree, TyperState) => Tree)(implicit ctx: Context): Tree =
fallBack(tree, ctx.typerState)
Expand Down
Loading