Permalink
Browse files

SI-7345 Produce Context#imports from the context chain

Rather than keeping a List[ImportInfo] around as a field.

This sort of change reduces our bug exposure by structurally
enforcing the invariant that `Context#imports` must mirror
the `outer` chain.

This entails changing a few elements of Contexts from mutable
vars to constructor pararameters, which seems desirable anyway.

We no longer need `makeNewImport`, as `make` now inspects
the provided tree and determines does import specific things
(tracking of used imports / mixin of ImportContext) when
appropriate.

The only wrinkle in this commit is the unfortunate need to pass
`owner = null` in the extends clause of NoContext. I don't see
cleaner way around this.

Applied minor rework to `implicitss` to avoid needlessly re-evaluating
`imports` and `nextOuter`.
  • Loading branch information...
1 parent 78e7eba commit dbd8457e441bda6a444efd19b5eb1eaec373a535 @retronym retronym committed Apr 16, 2013
View
88 src/compiler/scala/tools/nsc/typechecker/Contexts.scala
@@ -19,14 +19,20 @@ trait Contexts { self: Analyzer =>
import definitions.{ JavaLangPackage, ScalaPackage, PredefModule }
import ContextMode._
- object NoContext extends Context {
- outer = this
+ object NoContext
+ extends Context(EmptyTree, NoSymbol, EmptyScope, NoCompilationUnit,
+ outer = null /*We can't pass NoContext here, overriden below*/) {
+
+ override val outer = this
+
enclClass = this
enclMethod = this
override def nextEnclosing(p: Context => Boolean): Context = this
override def enclosingContextChain: List[Context] = Nil
override def implicitss: List[List[ImplicitInfo]] = Nil
+ override def imports: List[ImportInfo] = Nil
+ override def firstImport: Option[ImportInfo] = None
override def toString = "NoContext"
}
private object RootImports {
@@ -89,7 +95,7 @@ trait Contexts { self: Analyzer =>
def rootContext(unit: CompilationUnit, tree: Tree = EmptyTree, erasedTypes: Boolean = false): Context = {
var sc = startContext
for (sym <- rootImports(unit)) {
- sc = sc.makeNewImport(gen.mkWildcardImport(sym))
+ sc = sc.make(gen.mkWildcardImport(sym))
sc.depth += 1
}
val c = sc.make(tree, unit = unit)
@@ -107,16 +113,14 @@ trait Contexts { self: Analyzer =>
}
}
- class Context private[typechecker] {
- var unit: CompilationUnit = NoCompilationUnit
- /** Tree associated with this context */
- var tree: Tree = _
- /** The current owner */
- var owner: Symbol = NoSymbol
- /** The current scope */
- var scope: Scope = _
- /** The next outer context */
- var outer: Context = _
+ /**
+ * @param tree Tree associated with this context
+ * @param owner The current owner
+ * @param scope The current scope
+ * @param outer The next outer context.
+ */
+ class Context private[typechecker](val tree: Tree, val owner: Symbol, val scope: Scope,
+ val unit: CompilationUnit, val outer: Context) {
/** The next outer context whose tree is a template or package definition */
var enclClass: Context = _
@@ -155,7 +159,9 @@ trait Contexts { self: Analyzer =>
var depth: Int = 0
/** The currently visible imports */
- var imports: List[ImportInfo] = List()
+ def imports: List[ImportInfo] = outer.imports
+ /** Equivalent to `imports.headOption`, but more efficient */
+ def firstImport: Option[ImportInfo] = outer.firstImport
/** Types for which implicit arguments are currently searched */
var openImplicits: List[(Type,Tree)] = List()
@@ -297,10 +303,12 @@ trait Contexts { self: Analyzer =>
/**
* Construct a child context. The parent and child will share the report buffer.
* Compare with `makeSilent`, in which the child has a fresh report buffer.
+ *
+ * If `tree` is an `Import`, that import will be avaiable at the head of
+ * `Context#imports`.
*/
def make(tree: Tree = tree, owner: Symbol = owner,
- scope: Scope = scope, imports: List[ImportInfo] = imports,
- unit: CompilationUnit = unit): Context = {
+ scope: Scope = scope, unit: CompilationUnit = unit): Context = {
val isTemplateOrPackage = tree match {
case _: Template | _: PackageDef => true
case _ => false
@@ -309,6 +317,10 @@ trait Contexts { self: Analyzer =>
case _: DefDef => true
case _ => false
}
+ val isImport = tree match {
+ case _: Import => true
+ case _ => false
+ }
val sameOwner = owner == this.owner
val sameScope = scope == this.scope
val prefixInChild =
@@ -317,23 +329,20 @@ trait Contexts { self: Analyzer =>
else prefix
// The blank canvas
- val c = new Context
+ val c = if (isImport)
+ new Context(tree, owner, scope, unit, this) with ImportContext
+ else
+ new Context(tree, owner, scope, unit, this)
// Fields that are directly propagated
- c.unit = unit
- c.tree = tree
- c.owner = owner
- c.scope = scope
c.variance = variance
- c.imports = imports
c.diagnostic = diagnostic
c.typingIndentLevel = typingIndentLevel
c.openImplicits = openImplicits
c.contextMode = contextMode // note: ConstructorSuffix, a bit within `mode`, is conditionally overwritten below.
c._reportBuffer = reportBuffer
// Fields that may take on a different value in the child
- c.outer = this
c.prefix = prefixInChild
c.enclClass = if (isTemplateOrPackage) c else enclClass
c(ConstructorSuffix) = !isTemplateOrPackage && c(ConstructorSuffix)
@@ -349,16 +358,7 @@ trait Contexts { self: Analyzer =>
// TODO SI-7345 Moving this optimization into the main overload of `make` causes all tests to fail.
// even if it is extened to check that `unit == this.unit`. Why is this?
if (tree == this.tree && owner == this.owner && scope == this.scope) this
- else make(tree, owner, scope, imports, unit)
-
- /** Make a child context that includes `imp` in the list of visible imports */
- def makeNewImport(imp: Import): Context = {
- val impInfo = new ImportInfo(imp, depth)
- if (settings.lint && imp.pos.isDefined) // pos.isDefined excludes java.lang/scala/Predef imports
- allImportInfos(unit) ::= impInfo
-
- make(imp, imports = impInfo :: imports)
- }
+ else make(tree, owner, scope, unit)
/** Make a child context that represents a new nested scope */
def makeNewScope(tree: Tree, owner: Symbol): Context =
@@ -708,6 +708,8 @@ trait Contexts { self: Analyzer =>
* filtered out later by `eligibleInfos` (SI-4270 / 9129cfe9), as they don't type-check.
*/
def implicitss: List[List[ImplicitInfo]] = {
+ val imports = this.imports
+ val nextOuter = this.nextOuter
if (implicitsRunId != currentRunId) {
implicitsRunId = currentRunId
implicitsCache = List()
@@ -724,8 +726,8 @@ trait Contexts { self: Analyzer =>
} else if (scope != nextOuter.scope && !owner.isPackageClass) {
debuglog("collect local implicits " + scope.toList)//DEBUG
collectImplicits(scope, NoPrefix)
- } else if (imports != nextOuter.imports) {
- assert(imports.tail == nextOuter.imports, (imports, nextOuter.imports))
+ } else if (firstImport != nextOuter.firstImport) {
+ assert(imports.tail.headOption == nextOuter.firstImport, (imports, nextOuter.imports))
collectImplicitImports(imports.head)
} else if (owner.isPackageClass) {
// the corresponding package object may contain implicit members.
@@ -1058,6 +1060,22 @@ trait Contexts { self: Analyzer =>
}
} //class Context
+ trait ImportContext extends Context {
+ private def imp = tree.asInstanceOf[Import]
+ final def isRootImport = !imp.pos.isDefined // excludes java.lang/scala/Predef imports
+
+ private val impInfo = {
+ val info = new ImportInfo(imp, outer.depth)
+ if (settings.lint && !isRootImport)
+ allImportInfos(unit) ::= info
+ info
+ }
+
+ override final def imports = impInfo :: super.imports
+ override final def firstImport = Some(impInfo)
+ override final def toString = "<import>"
+ }
+
/** A buffer for warnings and errors that are accumulated during speculative type checking. */
final class ReportBuffer {
type Error = AbsTypeError
View
2 src/compiler/scala/tools/nsc/typechecker/Namers.scala
@@ -253,7 +253,7 @@ trait Namers extends MethodSynthesis {
case DocDef(_, defn) => enterSym(defn)
case tree @ Import(_, _) =>
assignSymbol(tree)
- returnContext = context.makeNewImport(tree)
+ returnContext = context.make(tree)
case _ =>
}
returnContext
View
2 src/compiler/scala/tools/nsc/typechecker/Typers.scala
@@ -2837,7 +2837,7 @@ trait Typers extends Adaptations with Tags {
case imp @ Import(_, _) =>
imp.symbol.initialize
if (!imp.symbol.isError) {
- context = context.makeNewImport(imp)
+ context = context.make(imp)
typedImport(imp)
} else EmptyTree
case _ =>

0 comments on commit dbd8457

Please sign in to comment.