summaryrefslogtreecommitdiff
path: root/src/compiler
diff options
context:
space:
mode:
authorPaul Phillips <paulp@improving.org>2013-04-24 12:51:13 -0700
committerPaul Phillips <paulp@improving.org>2013-04-24 12:51:13 -0700
commit62b6bdc088db2e3607814dfd2f83714bec97b935 (patch)
tree0376965e18d6cd5d61cd14544323897dc1390e23 /src/compiler
parentcdffcf8962c9fa606c027fcb5a50a4273976a576 (diff)
parent10845adebe741fc2bb5bce70df0a8da7788aa722 (diff)
downloadscala-62b6bdc088db2e3607814dfd2f83714bec97b935.tar.gz
scala-62b6bdc088db2e3607814dfd2f83714bec97b935.tar.bz2
scala-62b6bdc088db2e3607814dfd2f83714bec97b935.zip
Merge branch 'master' into pr/warning-cleanup
Conflicts: src/compiler/scala/tools/nsc/typechecker/Contexts.scala
Diffstat (limited to 'src/compiler')
-rw-r--r--src/compiler/scala/reflect/macros/runtime/Typers.scala6
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Contexts.scala727
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Implicits.scala44
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Infer.scala53
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Macros.scala4
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Namers.scala5
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala29
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/RefChecks.scala2
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Typers.scala77
-rw-r--r--src/compiler/scala/tools/reflect/ToolBoxFactory.scala4
10 files changed, 601 insertions, 350 deletions
diff --git a/src/compiler/scala/reflect/macros/runtime/Typers.scala b/src/compiler/scala/reflect/macros/runtime/Typers.scala
index 070b288c3a..398770ab35 100644
--- a/src/compiler/scala/reflect/macros/runtime/Typers.scala
+++ b/src/compiler/scala/reflect/macros/runtime/Typers.scala
@@ -54,8 +54,10 @@ trait Typers {
wrapper(universe.analyzer.inferImplicit(tree, pt, reportAmbiguous = true, isView = isView, context = context, saveAmbiguousDivergent = !silent, pos = pos)) match {
case failure if failure.tree.isEmpty =>
macroLogVerbose("implicit search has failed. to find out the reason, turn on -Xlog-implicits")
- if (context.hasErrors) throw new TypecheckException(context.errBuffer.head.errPos, context.errBuffer.head.errMsg)
- universe.EmptyTree
+ context.firstError match {
+ case Some(err) => throw new TypecheckException(err.errPos, err.errMsg)
+ case None => universe.EmptyTree
+ }
case success =>
success.tree
}
diff --git a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala
index cccaac6426..538b3b3b6c 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala
@@ -6,7 +6,7 @@
package scala.tools.nsc
package typechecker
-import scala.collection.mutable
+import scala.collection.{ immutable, mutable }
import scala.annotation.tailrec
import scala.reflect.internal.util.shortClassOfInstance
@@ -17,15 +17,23 @@ import scala.reflect.internal.util.shortClassOfInstance
trait Contexts { self: Analyzer =>
import global._
import definitions.{ JavaLangPackage, ScalaPackage, PredefModule }
+ import ContextMode._
+
+ object NoContext
+ extends Context(EmptyTree, NoSymbol, EmptyScope, NoCompilationUnit,
+ outer = null /*We can't pass NoContext here, overriden below*/) {
+
+ override val outer = this
- object NoContext extends Context {
- outer = this
enclClass = this
enclMethod = this
+ override val depth = 0
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 {
@@ -88,281 +96,347 @@ trait Contexts { self: Analyzer =>
else RootImports.completeList
}
- def rootContext(unit: CompilationUnit): Context = rootContext(unit, EmptyTree, erasedTypes = false)
- def rootContext(unit: CompilationUnit, tree: Tree): Context = rootContext(unit, tree, erasedTypes = false)
- def rootContext(unit: CompilationUnit, tree: Tree, erasedTypes: Boolean): Context = {
- var sc = startContext
- for (sym <- rootImports(unit)) {
- sc = sc.makeNewImport(sym)
- sc.depth += 1
- }
- val c = sc.make(unit, tree, sc.owner, sc.scope, sc.imports)
+ def rootContext(unit: CompilationUnit, tree: Tree = EmptyTree, erasedTypes: Boolean = false): Context = {
+ val rootImportsContext = (startContext /: rootImports(unit))((c, sym) => c.make(gen.mkWildcardImport(sym)))
+ val c = rootImportsContext.make(tree, unit = unit)
if (erasedTypes) c.setThrowErrors() else c.setReportErrors()
- c.implicitsEnabled = !erasedTypes
- c.enrichmentEnabled = c.implicitsEnabled
+ c(EnrichmentEnabled | ImplicitsEnabled) = !erasedTypes
c
}
def resetContexts() {
- var sc = startContext
- while (sc != NoContext) {
- sc.tree match {
+ startContext.enclosingContextChain foreach { context =>
+ context.tree match {
case Import(qual, _) => qual setType singleType(qual.symbol.owner.thisType, qual.symbol)
case _ =>
}
- sc.flushAndReturnBuffer()
- sc.flushAndReturnWarningsBuffer()
- sc = sc.outer
+ context.reportBuffer.clearAll()
}
}
- private object Errors {
- final val ReportErrors = 1 << 0
- final val BufferErrors = 1 << 1
- final val AmbiguousErrors = 1 << 2
- final val notThrowMask = ReportErrors | BufferErrors
- final val AllMask = ReportErrors | BufferErrors | AmbiguousErrors
- }
+ /**
+ * A motley collection of the state and loosely associated behaviour of the type checker.
+ * Each `Typer` has an associated context, and as it descends into the tree new `(Typer, Context)`
+ * pairs are spawned.
+ *
+ * Meet the crew; first the state:
+ *
+ * - A tree, symbol, and scope representing the focus of the typechecker
+ * - An enclosing context, `outer`.
+ * - The current compilation unit.
+ * - A variety of bits that track the current error reporting policy (more on this later);
+ * whether or not implicits/macros are enabled, whether we are in a self or super call or
+ * in a constructor suffix. These are represented as bits in the mask `contextMode`.
+ * - Some odds and ends: undetermined type pararameters of the current line of type inference;
+ * contextual augmentation for error messages, tracking of the nesting depth.
+ *
+ * And behaviour:
+ *
+ * - The central point for issuing errors and warnings from the typechecker, with a means
+ * to buffer these for use in 'silent' type checking, when some recovery might be possible.
+ * - `Context` is something of a Zipper for the tree were are typechecking: it `enclosingContextChain`
+ * is the path back to the root. This is exactly what we need to resolve names (`lookupSymbol`)
+ * and to collect in-scope implicit defintions (`implicitss`)
+ * Supporting these are `imports`, which represents all `Import` trees in in the enclosing context chain.
+ * - In a similar vein, we can assess accessiblity (`isAccessible`.)
+ *
+ * More on error buffering:
+ * When are type errors recoverable? In quite a few places, it turns out. Some examples:
+ * trying to type an application with/without the expected type, or with/without implicit views
+ * enabled. This is usually mediated by `Typer.silent`, `Inferencer#tryTwice`.
+ *
+ * Intially, starting from the `typer` phase, the contexts either buffer or report errors;
+ * afterwards errors are thrown. This is configured in `rootContext`. Additionally, more
+ * fine grained control is needed based on the kind of error; ambiguity errors are often
+ * suppressed during exploraratory typing, such as determining whether `a == b` in an argument
+ * position is an assignment or a named argument, when `Infererencer#isApplicableSafe` type checks
+ * applications with and without an expected type, or whtn `Typer#tryTypedApply` tries to fit arguments to
+ * a function type with/without implicit views.
+ *
+ * When the error policies entails error/warning buffering, the mutable [[ReportBuffer]] records
+ * everything that is issued. It is important to note, that child Contexts created with `make`
+ * "inherit" the very same `ReportBuffer` instance, whereas children spawned through `makeSilent`
+ * receive an separate, fresh buffer.
+ *
+ * @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 = _
- class Context private[typechecker] {
- import Errors._
-
- var unit: CompilationUnit = NoCompilationUnit
- var tree: Tree = _ // Tree associated with this context
- var owner: Symbol = NoSymbol // The current owner
- var scope: Scope = _ // The current scope
- var outer: Context = _ // The next outer context
- var enclClass: Context = _ // The next outer context whose tree is a
- // template or package definition
- @inline final def savingEnclClass[A](c: Context)(a: => A): A = {
+ @inline private def savingEnclClass[A](c: Context)(a: => A): A = {
val saved = enclClass
enclClass = c
try a finally enclClass = saved
}
- var enclMethod: Context = _ // The next outer context whose tree is a method
- var variance: Variance = Variance.Invariant // Variance relative to enclosing class
- private var _undetparams: List[Symbol] = List() // Undetermined type parameters,
- // not inherited to child contexts
- var depth: Int = 0
- var imports: List[ImportInfo] = List() // currently visible imports
- var openImplicits: List[(Type,Tree)] = List() // types for which implicit arguments
- // are currently searched
- // for a named application block (Tree) the corresponding NamedApplyInfo
+ /** A bitmask containing all the boolean flags in a context, e.g. are implicit views enabled */
+ var contextMode: ContextMode = ContextMode.DefaultMode
+
+ /** Update all modes in `mask` to `value` */
+ def update(mask: ContextMode, value: Boolean) {
+ contextMode = contextMode.set(value, mask)
+ }
+
+ /** Set all modes in the mask `enable` to true, and all in `disable` to false. */
+ def set(enable: ContextMode = NOmode, disable: ContextMode = NOmode): this.type = {
+ contextMode = contextMode.set(true, enable).set(false, disable)
+ this
+ }
+
+ /** Is this context in all modes in the given `mask`? */
+ def apply(mask: ContextMode): Boolean = contextMode.inAll(mask)
+
+ /** The next outer context whose tree is a method */
+ var enclMethod: Context = _
+
+ /** Variance relative to enclosing class */
+ var variance: Variance = Variance.Invariant
+
+ private var _undetparams: List[Symbol] = List()
+
+ val depth: Int = {
+ val increasesDepth = isRootImport || (outer eq null) || (outer.scope != scope)
+ def outerDepth = if (outer eq null) 0 else outer.depth
+ ( if (increasesDepth) 1 else 0 ) + outerDepth
+ }
+
+ /** The currently visible imports */
+ def imports: List[ImportInfo] = outer.imports
+ /** Equivalent to `imports.headOption`, but more efficient */
+ def firstImport: Option[ImportInfo] = outer.firstImport
+ def isRootImport: Boolean = false
+
+ /** Types for which implicit arguments are currently searched */
+ var openImplicits: List[(Type,Tree)] = List()
+
+ /* For a named application block (`Tree`) the corresponding `NamedApplyInfo`. */
var namedApplyBlockInfo: Option[(Tree, NamedApplyInfo)] = None
var prefix: Type = NoPrefix
- var inConstructorSuffix = false // are we in a secondary constructor
- // after the this constructor call?
- var returnsSeen = false // for method context: were returns encountered?
- var inSelfSuperCall = false // is this context (enclosed in) a constructor call?
- // (the call to the super or self constructor in the first line of a constructor)
- // in this context the object's fields should not be in scope
-
- var diagnostic: List[String] = Nil // these messages are printed when issuing an error
- var implicitsEnabled = false
- var macrosEnabled = true
- var enrichmentEnabled = false // to selectively allow enrichment in patterns, where other kinds of implicit conversions are not allowed
- var checking = false
- var retyping = false
-
- var savedTypeBounds: List[(Symbol, Type)] = List() // saved type bounds
- // for type parameters which are narrowed in a GADT
+ def inConstructorSuffix_=(value: Boolean) = this(ConstructorSuffix) = value
+ def inConstructorSuffix = this(ConstructorSuffix)
+ def returnsSeen_=(value: Boolean) = this(ReturnsSeen) = value
+ def returnsSeen = this(ReturnsSeen)
+ def inSelfSuperCall_=(value: Boolean) = this(SelfSuperCall) = value
+ def inSelfSuperCall = this(SelfSuperCall)
+ def implicitsEnabled_=(value: Boolean) = this(ImplicitsEnabled) = value
+ def implicitsEnabled = this(ImplicitsEnabled)
+ def macrosEnabled_=(value: Boolean) = this(MacrosEnabled) = value
+ def macrosEnabled = this(MacrosEnabled)
+ def enrichmentEnabled_=(value: Boolean) = this(EnrichmentEnabled) = value
+ def enrichmentEnabled = this(EnrichmentEnabled)
+ def checking_=(value: Boolean) = this(Checking) = value
+ def checking = this(Checking)
+ def retyping_=(value: Boolean) = this(ReTyping) = value
+ def retyping = this(ReTyping)
+
+ /** These messages are printed when issuing an error */
+ var diagnostic: List[String] = Nil
+
+ /** Saved type bounds for type parameters which are narrowed in a GADT. */
+ var savedTypeBounds: List[(Symbol, Type)] = List()
+
+ /** Indentation level, in columns, for output under -Ytyper-debug */
var typingIndentLevel: Int = 0
def typingIndent = " " * typingIndentLevel
- var buffer: mutable.Set[AbsTypeError] = _
- var warningsBuffer: mutable.Set[(Position, String)] = _
-
+ /** The next enclosing context (potentially `this`) that is owned by a class or method */
def enclClassOrMethod: Context =
if ((owner eq NoSymbol) || (owner.isClass) || (owner.isMethod)) this
else outer.enclClassOrMethod
- def enclosingApply = nextEnclosing(_.tree.isInstanceOf[Apply])
+ /** The next enclosing context (potentially `this`) that has a `CaseDef` as a tree */
def enclosingCaseDef = nextEnclosing(_.tree.isInstanceOf[CaseDef])
+
+ /** ...or an Apply. */
+ def enclosingApply = nextEnclosing(_.tree.isInstanceOf[Apply])
+
+ //
+ // Tracking undetermined type parameters for type argument inference.
+ //
def undetparamsString =
if (undetparams.isEmpty) ""
else undetparams.mkString("undetparams=", ", ", "")
- def undetparams = _undetparams
+ /** Undetermined type parameters. See `Infer#{inferExprInstance, adjustTypeArgs}`. Not inherited to child contexts */
+ def undetparams: List[Symbol] = _undetparams
def undetparams_=(ps: List[Symbol]) = { _undetparams = ps }
- def extractUndetparams() = {
+ /** Return and clear the undetermined type parameters */
+ def extractUndetparams(): List[Symbol] = {
val tparams = undetparams
undetparams = List()
tparams
}
- private[this] var mode = 0
-
- def errBuffer = buffer
- def hasErrors = buffer.nonEmpty
- def hasWarnings = warningsBuffer.nonEmpty
-
- def state: Int = mode
- def restoreState(state0: Int) = mode = state0
-
- def reportErrors = (state & ReportErrors) != 0
- def bufferErrors = (state & BufferErrors) != 0
- def ambiguousErrors = (state & AmbiguousErrors) != 0
- def throwErrors = (state & notThrowMask) == 0
-
- def setReportErrors() = mode = (ReportErrors | AmbiguousErrors)
- def setBufferErrors() = {
- //assert(bufferErrors || !hasErrors, "When entering the buffer state, context has to be clean. Current buffer: " + buffer)
- mode = BufferErrors
+ /** Run `body` with this context with no undetermined type parameters, restore the original
+ * the original list afterwards.
+ * @param reportAmbiguous Should ambiguous errors be reported during evaluation of `body`?
+ */
+ def savingUndeterminedTypeParams[A](reportAmbiguous: Boolean = ambiguousErrors)(body: => A): A = {
+ withMode() {
+ this(AmbiguousErrors) = reportAmbiguous
+ val saved = extractUndetparams()
+ try body
+ finally undetparams = saved
+ }
}
- def setThrowErrors() = mode &= (~AllMask)
- def setAmbiguousErrors(report: Boolean) = if (report) mode |= AmbiguousErrors else mode &= notThrowMask
- def updateBuffer(errors: mutable.Set[AbsTypeError]) = buffer ++= errors
- def condBufferFlush(removeP: AbsTypeError => Boolean) {
- val elems = buffer.filter(removeP)
- buffer --= elems
- }
- def flushBuffer() { buffer.clear() }
- def flushAndReturnBuffer(): mutable.Set[AbsTypeError] = {
- val current = buffer.clone()
- buffer.clear()
+ //
+ // Error reporting policies and buffer.
+ //
+
+ private var _reportBuffer: ReportBuffer = new ReportBuffer
+ /** A buffer for errors and warnings, used with `this.bufferErrors == true` */
+ def reportBuffer = _reportBuffer
+ /** Discard the current report buffer, and replace with an empty one */
+ def useFreshReportBuffer() = _reportBuffer = new ReportBuffer
+ /** Discard the current report buffer, and replace with `other` */
+ def restoreReportBuffer(other: ReportBuffer) = _reportBuffer = other
+
+ /** The first error, if any, in the report buffer */
+ def firstError: Option[AbsTypeError] = reportBuffer.firstError
+ /** Does the report buffer contain any errors? */
+ def hasErrors = reportBuffer.hasErrors
+
+ def reportErrors = this(ReportErrors)
+ def bufferErrors = this(BufferErrors)
+ def ambiguousErrors = this(AmbiguousErrors)
+ def throwErrors = contextMode.inNone(ReportErrors | BufferErrors)
+
+ def setReportErrors(): Unit = set(enable = ReportErrors | AmbiguousErrors, disable = BufferErrors)
+ def setBufferErrors(): Unit = set(enable = BufferErrors, disable = ReportErrors | AmbiguousErrors)
+ def setThrowErrors(): Unit = this(ReportErrors | AmbiguousErrors | BufferErrors) = false
+ def setAmbiguousErrors(report: Boolean): Unit = this(AmbiguousErrors) = report
+
+ /** Append the given errors to the report buffer */
+ def updateBuffer(errors: Traversable[AbsTypeError]) = reportBuffer ++= errors
+ /** Clear all errors from the report buffer */
+ def flushBuffer() { reportBuffer.clearAllErrors() }
+ /** Return and clear all errors from the report buffer */
+ def flushAndReturnBuffer(): immutable.Seq[AbsTypeError] = {
+ val current = reportBuffer.errors
+ reportBuffer.clearAllErrors()
current
}
- def flushAndReturnWarningsBuffer(): mutable.Set[(Position, String)] = {
- val current = warningsBuffer.clone()
- warningsBuffer.clear()
- current
- }
-
- def withImplicitsEnabled[T](op: => T): T = {
- val saved = implicitsEnabled
- implicitsEnabled = true
- try op
- finally implicitsEnabled = saved
- }
- def withImplicitsDisabled[T](op: => T): T = {
- val saved = implicitsEnabled
- implicitsEnabled = false
- val savedP = enrichmentEnabled
- enrichmentEnabled = false
- try op
- finally {
- implicitsEnabled = saved
- enrichmentEnabled = savedP
+ /** Issue and clear all warnings from the report buffer */
+ def flushAndIssueWarnings() {
+ reportBuffer.warnings foreach {
+ case (pos, msg) => unit.warning(pos, msg)
}
+ reportBuffer.clearAllWarnings()
}
- def withImplicitsDisabledAllowEnrichment[T](op: => T): T = {
- val saved = implicitsEnabled
- implicitsEnabled = false
- val savedP = enrichmentEnabled
- enrichmentEnabled = true
- try op
- finally {
- implicitsEnabled = saved
- enrichmentEnabled = savedP
- }
- }
+ //
+ // Temporary mode adjustment
+ //
- def withMacrosEnabled[T](op: => T): T = {
- val saved = macrosEnabled
- macrosEnabled = true
+ @inline def withMode[T](enabled: ContextMode = NOmode, disabled: ContextMode = NOmode)(op: => T): T = {
+ val saved = contextMode
+ set(enabled, disabled)
try op
- finally macrosEnabled = saved
+ finally contextMode = saved
}
- def withMacrosDisabled[T](op: => T): T = {
- val saved = macrosEnabled
- macrosEnabled = false
- try op
- finally macrosEnabled = saved
+ def withImplicitsEnabled[T](op: => T): T = withMode(enabled = ImplicitsEnabled)(op)
+ def withImplicitsDisabled[T](op: => T): T = withMode(disabled = ImplicitsEnabled | EnrichmentEnabled)(op)
+ def withImplicitsDisabledAllowEnrichment[T](op: => T): T = withMode(enabled = EnrichmentEnabled, disabled = ImplicitsEnabled)(op)
+ def withMacrosEnabled[T](op: => T): T = withMode(enabled = MacrosEnabled)(op)
+ def withMacrosDisabled[T](op: => T): T = withMode(disabled = MacrosEnabled)(op)
+
+ /** @return true if the `expr` evaluates to true within a silent Context that incurs no errors */
+ @inline final def inSilentMode(expr: => Boolean): Boolean = {
+ withMode() { // withMode with no arguments to restore the mode mutated by `setBufferErrors`.
+ setBufferErrors()
+ try expr && !hasErrors
+ finally reportBuffer.clearAll()
+ }
}
- def make(unit: CompilationUnit, tree: Tree, owner: Symbol,
- scope: Scope, imports: List[ImportInfo]): Context = {
- val c = new Context
- c.unit = unit
- c.tree = tree
- c.owner = owner
- c.scope = scope
- c.outer = this
-
- tree match {
- case Template(_, _, _) | PackageDef(_, _) =>
- c.enclClass = c
- c.prefix = c.owner.thisType
- c.inConstructorSuffix = false
- case _ =>
- c.enclClass = this.enclClass
- c.prefix =
- if (c.owner != this.owner && c.owner.isTerm) NoPrefix
- else this.prefix
- c.inConstructorSuffix = this.inConstructorSuffix
+ //
+ // Child Context Creation
+ //
+
+ /**
+ * 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, unit: CompilationUnit = unit): Context = {
+ val isTemplateOrPackage = tree match {
+ case _: Template | _: PackageDef => true
+ case _ => false
+ }
+ val isDefDef = tree match {
+ case _: DefDef => true
+ case _ => false
}
- tree match {
- case DefDef(_, _, _, _, _, _) =>
- c.enclMethod = c
- case _ =>
- c.enclMethod = this.enclMethod
+ val isImport = tree match {
+ case _: Import => true
+ case _ => false
}
- c.variance = this.variance
- c.depth = if (scope == this.scope) this.depth else this.depth + 1
- c.imports = imports
- c.inSelfSuperCall = inSelfSuperCall
- c.restoreState(this.state)
- c.diagnostic = this.diagnostic
- c.typingIndentLevel = typingIndentLevel
- c.implicitsEnabled = this.implicitsEnabled
- c.macrosEnabled = this.macrosEnabled
- c.enrichmentEnabled = this.enrichmentEnabled
- c.checking = this.checking
- c.retyping = this.retyping
- c.openImplicits = this.openImplicits
- c.buffer = if (this.buffer == null) mutable.LinkedHashSet[AbsTypeError]() else this.buffer // need to initialize
- c.warningsBuffer = if (this.warningsBuffer == null) mutable.LinkedHashSet[(Position, String)]() else this.warningsBuffer
+ val sameOwner = owner == this.owner
+ val prefixInChild =
+ if (isTemplateOrPackage) owner.thisType
+ else if (!sameOwner && owner.isTerm) NoPrefix
+ else prefix
+
+ // The blank canvas
+ 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.variance = variance
+ 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.prefix = prefixInChild
+ c.enclClass = if (isTemplateOrPackage) c else enclClass
+ c(ConstructorSuffix) = !isTemplateOrPackage && c(ConstructorSuffix)
+ c.enclMethod = if (isDefDef) c else enclMethod
+
registerContext(c.asInstanceOf[analyzer.Context])
debuglog("[context] ++ " + c.unit + " / " + tree.summaryString)
c
}
- def makeNewImport(sym: Symbol): Context =
- makeNewImport(gen.mkWildcardImport(sym))
-
- 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(unit, imp, owner, scope, impInfo :: imports)
- }
-
def make(tree: Tree, owner: Symbol, scope: Scope): Context =
+ // 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 make0(tree, owner, scope)
-
- private def make0(tree: Tree, owner: Symbol, scope: Scope): Context =
- make(unit, tree, owner, scope, imports)
+ else make(tree, owner, scope, unit)
+ /** Make a child context that represents a new nested scope */
def makeNewScope(tree: Tree, owner: Symbol): Context =
make(tree, owner, newNestedScope(scope))
- // IDE stuff: distinguish between scopes created for typing and scopes created for naming.
-
- def make(tree: Tree, owner: Symbol): Context =
- make0(tree, owner, scope)
- def make(tree: Tree): Context =
- make(tree, owner)
-
- def makeSilent(reportAmbiguousErrors: Boolean, newtree: Tree = tree): Context = {
+ /** Make a child context that buffers errors and warnings into a fresh report buffer. */
+ def makeSilent(reportAmbiguousErrors: Boolean = ambiguousErrors, newtree: Tree = tree): Context = {
val c = make(newtree)
c.setBufferErrors()
c.setAmbiguousErrors(reportAmbiguousErrors)
- c.buffer = mutable.LinkedHashSet[AbsTypeError]()
+ c._reportBuffer = new ReportBuffer // A fresh buffer so as not to leak errors/warnings into `this`.
c
}
+ /** Make a silent child context does not allow implicits. Used to prevent chaining of implicit views. */
def makeImplicit(reportAmbiguousErrors: Boolean) = {
val c = makeSilent(reportAmbiguousErrors)
- c.implicitsEnabled = false
- c.enrichmentEnabled = false
+ c(ImplicitsEnabled | EnrichmentEnabled) = false
c
}
@@ -377,12 +451,10 @@ trait Contexts { self: Analyzer =>
* accessible.
*/
def makeConstructorContext = {
- var baseContext = enclClass.outer
- while (baseContext.tree.isInstanceOf[Template])
- baseContext = baseContext.outer
+ val baseContext = enclClass.outer.nextEnclosing(!_.tree.isInstanceOf[Template])
val argContext = baseContext.makeNewScope(tree, owner)
+ argContext.contextMode = contextMode
argContext.inSelfSuperCall = true
- argContext.restoreState(this.state)
def enterElems(c: Context) {
def enterLocalElems(e: ScopeEntry) {
if (e != null && e.owner == c.scope) {
@@ -390,7 +462,7 @@ trait Contexts { self: Analyzer =>
argContext.scope enter e.sym
}
}
- if (c.owner.isTerm && !c.owner.isLocalDummy) {
+ if (c.isLocal && !c.owner.isLocalDummy) {
enterElems(c.outer)
enterLocalElems(c.scope.elems)
}
@@ -401,6 +473,10 @@ trait Contexts { self: Analyzer =>
argContext
}
+ //
+ // Error and warning issuance
+ //
+
private def addDiagString(msg: String) = {
val ds =
if (diagnostic.isEmpty) ""
@@ -417,16 +493,18 @@ trait Contexts { self: Analyzer =>
(new Exception).printStackTrace()
}
if (pf isDefinedAt err) pf(err)
- else if (bufferErrors) { buffer += err }
+ else if (bufferErrors) { reportBuffer += err }
else throw new TypeError(err.errPos, err.errMsg)
}
+ /** Issue/buffer/throw the given type error according to the current mode for error reporting. */
def issue(err: AbsTypeError) {
issueCommon(err) { case _ if reportErrors =>
unitError(err.errPos, addDiagString(err.errMsg))
}
}
+ /** Issue/buffer/throw the given implicit ambiguity error according to the current mode for error reporting. */
def issueAmbiguousError(pre: Type, sym1: Symbol, sym2: Symbol, err: AbsTypeError) {
issueCommon(err) { case _ if ambiguousErrors =>
if (!pre.isErroneous && !sym1.isErroneous && !sym2.isErroneous)
@@ -434,35 +512,31 @@ trait Contexts { self: Analyzer =>
}
}
+ /** Issue/buffer/throw the given implicit ambiguity error according to the current mode for error reporting. */
def issueAmbiguousError(err: AbsTypeError) {
issueCommon(err) { case _ if ambiguousErrors => unitError(err.errPos, addDiagString(err.errMsg)) }
}
- // TODO remove
+ /** Issue/throw the given `err` according to the current mode for error reporting. */
def error(pos: Position, err: Throwable) =
if (reportErrors) unitError(pos, addDiagString(err.getMessage()))
else throw err
+ /** Issue/throw the given error message according to the current mode for error reporting. */
def error(pos: Position, msg: String) = {
val msg1 = addDiagString(msg)
if (reportErrors) unitError(pos, msg1)
else throw new TypeError(pos, msg1)
}
- def warning(pos: Position, msg: String): Unit = warning(pos, msg, force = false)
- def warning(pos: Position, msg: String, force: Boolean) {
+ /** Issue/throw the given error message according to the current mode for error reporting. */
+ def warning(pos: Position, msg: String, force: Boolean = false) {
if (reportErrors || force) unit.warning(pos, msg)
- else if (bufferErrors) warningsBuffer += ((pos, msg))
+ else if (bufferErrors) reportBuffer += (pos -> msg)
}
- def isLocal(): Boolean = tree match {
- case Block(_,_) => true
- case PackageDef(_, _) => false
- case EmptyTree => false
- case _ => outer.isLocal()
- }
-
- def isNameInScope(name: Name) = lookupSymbol(name, _ => true).isSuccess
+ /** Is the owning symbol of this context a term? */
+ final def isLocal: Boolean = owner.isTerm
// nextOuter determines which context is searched next for implicits
// (after `this`, which contributes `newImplicits` below.) In
@@ -488,14 +562,34 @@ trait Contexts { self: Analyzer =>
def enclosingContextChain: List[Context] = this :: outer.enclosingContextChain
- override def toString = "Context(%s@%s unit=%s scope=%s errors=%b, reportErrors=%b, throwErrors=%b)".format(
- owner.fullName, tree.shortClass, unit, scope.##, hasErrors, reportErrors, throwErrors
- )
- /** Is `sub` a subclass of `base` or a companion object of such a subclass?
- */
- def isSubClassOrCompanion(sub: Symbol, base: Symbol) =
+ private def treeTruncated = tree.toString.replaceAll("\\s+", " ").lines.mkString("\\n").take(70)
+ private def treeIdString = if (settings.uniqid.value) "#" + System.identityHashCode(tree).toString.takeRight(3) else ""
+ private def treeString = tree match {
+ case x: Import => "" + x
+ case Template(parents, `emptyValDef`, body) =>
+ val pstr = if ((parents eq null) || parents.isEmpty) "Nil" else parents mkString " "
+ val bstr = if (body eq null) "" else body.length + " stats"
+ s"""Template($pstr, _, $bstr)"""
+ case x => s"${tree.shortClass}${treeIdString}:${treeTruncated}"
+ }
+
+ override def toString =
+ sm"""|Context($unit) {
+ | owner = $owner
+ | tree = $treeString
+ | scope = ${scope.size} decls
+ | contextMode = $contextMode
+ | outer.owner = ${outer.owner}
+ |}"""
+
+ //
+ // Accessibility checking
+ //
+
+ /** Is `sub` a subclass of `base` or a companion object of such a subclass? */
+ private def isSubClassOrCompanion(sub: Symbol, base: Symbol) =
sub.isNonBottomSubClass(base) ||
- sub.isModuleClass && sub.linkedClassOfClass.isNonBottomSubClass(base)
+ sub.isModuleClass && sub.linkedClassOfClass.isNonBottomSubClass(base)
/** Return the closest enclosing context that defines a subclass of `clazz`
* or a companion object thereof, or `NoContext` if no such context exists.
@@ -507,8 +601,7 @@ trait Contexts { self: Analyzer =>
c
}
- /** Is `sym` accessible as a member of `pre` in current context?
- */
+ /** Is `sym` accessible as a member of `pre` in current context? */
def isAccessible(sym: Symbol, pre: Type, superAccess: Boolean = false): Boolean = {
lastAccessCheckDetails = ""
// Console.println("isAccessible(%s, %s, %s)".format(sym, pre, superAccess))
@@ -588,6 +681,10 @@ trait Contexts { self: Analyzer =>
}
}
+ //
+ // Type bound management
+ //
+
def pushTypeBounds(sym: Symbol) {
sym.info match {
case tb: TypeBounds => if (!tb.isEmptyBounds) log(s"Saving $sym info=$tb")
@@ -624,6 +721,10 @@ trait Contexts { self: Analyzer =>
}
}
+ //
+ // Implicit collection
+ //
+
private var implicitsCache: List[List[ImplicitInfo]] = null
private var implicitsRunId = NoRunId
@@ -683,6 +784,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()
@@ -699,8 +802,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.
@@ -712,6 +815,10 @@ trait Contexts { self: Analyzer =>
implicitsCache
}
+ //
+ // Imports and symbol lookup
+ //
+
/** It's possible that seemingly conflicting identifiers are
* identifiably the same after type normalization. In such cases,
* allow compilation to proceed. A typical example is:
@@ -821,6 +928,8 @@ trait Contexts { self: Analyzer =>
)
}
+ def isNameInScope(name: Name) = lookupSymbol(name, _ => true).isSuccess
+
/** Find the symbol of a simple name starting from this context.
* All names are filtered through the "qualifies" predicate,
* the search continuing as long as no qualifying name is found.
@@ -1027,6 +1136,82 @@ trait Contexts { self: Analyzer =>
}
} //class Context
+ /** A `Context` focussed on an `Import` tree */
+ trait ImportContext extends Context {
+ private def makeImpInfo = {
+ val info = new ImportInfo(tree.asInstanceOf[Import], outer.depth)
+ if (settings.lint && !info.isRootImport) // excludes java.lang/scala/Predef imports
+ allImportInfos(unit) ::= info
+ info
+ }
+
+ private var _impInfo: ImportInfo = null // hand rolled lazy val, we don't need/want synchronization.
+ private def impInfo: ImportInfo = {
+ if (_impInfo eq null) _impInfo = makeImpInfo
+ _impInfo
+ }
+
+ override final def imports = impInfo :: super.imports
+ override final def firstImport = Some(impInfo)
+ override final def isRootImport = impInfo.isRootImport
+ override final def toString = s"ImportContext { $impInfo; outer.owner = ${outer.owner} }"
+ }
+
+ /** A buffer for warnings and errors that are accumulated during speculative type checking. */
+ final class ReportBuffer {
+ type Error = AbsTypeError
+ type Warning = (Position, String)
+
+ private def newBuffer[A] = mutable.LinkedHashSet.empty[A] // Important to use LinkedHS for stable results.
+
+ // [JZ] Contexts, pre- the SI-7345 refactor, avoided allocating the buffers until needed. This
+ // is replicated here out of conservatism.
+ private var _errorBuffer: mutable.LinkedHashSet[Error] = _
+ private def errorBuffer = {if (_errorBuffer == null) _errorBuffer = newBuffer; _errorBuffer}
+ def errors: immutable.Seq[Error] = errorBuffer.toVector
+
+ private var _warningBuffer: mutable.LinkedHashSet[Warning] = _
+ private def warningBuffer = {if (_warningBuffer == null) _warningBuffer = newBuffer; _warningBuffer}
+ def warnings: immutable.Seq[Warning] = warningBuffer.toVector
+
+ def +=(error: AbsTypeError): this.type = {
+ errorBuffer += error
+ this
+ }
+ def ++=(errors: Traversable[AbsTypeError]): this.type = {
+ errorBuffer ++= errors
+ this
+ }
+ def +=(warning: Warning): this.type = {
+ warningBuffer += warning
+ this
+ }
+
+ def clearAll(): this.type = {
+ clearAllErrors(); clearAllWarnings();
+ }
+
+ def clearAllErrors(): this.type = {
+ errorBuffer.clear()
+ this
+ }
+ def clearErrors(kind: ErrorKinds.ErrorKind): this.type = {
+ errorBuffer.retain(_.kind != kind)
+ this
+ }
+ def retainErrors(kind: ErrorKinds.ErrorKind): this.type = {
+ errorBuffer.retain(_.kind == kind)
+ this
+ }
+ def clearAllWarnings(): this.type = {
+ warningBuffer.clear()
+ this
+ }
+
+ def hasErrors = errorBuffer.nonEmpty
+ def firstError = errorBuffer.headOption
+ }
+
class ImportInfo(val tree: Import, val depth: Int) {
def pos = tree.pos
def posOf(sel: ImportSelector) = tree.pos withPoint sel.namePos
@@ -1042,6 +1227,8 @@ trait Contexts { self: Analyzer =>
def isExplicitImport(name: Name): Boolean =
tree.selectors exists (_.rename == name.toTermName)
+ final def isRootImport: Boolean = !tree.pos.isDefined
+
/** The symbol with name `name` imported from import clause `tree`.
*/
def importedSymbol(name: Name): Symbol = importedSymbol(name, requireExplicit = false)
@@ -1113,3 +1300,75 @@ trait Contexts { self: Analyzer =>
override def safeToString = "ImportType("+expr+")"
}
}
+
+object ContextMode {
+ private implicit def liftIntBitsToContextState(bits: Int): ContextMode = apply(bits)
+ def apply(bits: Int): ContextMode = new ContextMode(bits)
+ final val NOmode: ContextMode = 0
+
+ final val ReportErrors: ContextMode = 1 << 0
+ final val BufferErrors: ContextMode = 1 << 1
+ final val AmbiguousErrors: ContextMode = 1 << 2
+
+ /** Are we in a secondary constructor after the this constructor call? */
+ final val ConstructorSuffix: ContextMode = 1 << 3
+
+ /** For method context: were returns encountered? */
+ final val ReturnsSeen: ContextMode = 1 << 4
+
+ /** Is this context (enclosed in) a constructor call?
+ * (the call to the super or self constructor in the first line of a constructor.)
+ * In such a context, the object's fields should not be in scope
+ */
+ final val SelfSuperCall: ContextMode = 1 << 5
+
+ // TODO harvest documentation for this
+ final val ImplicitsEnabled: ContextMode = 1 << 6
+
+ final val MacrosEnabled: ContextMode = 1 << 7
+
+ /** To selectively allow enrichment in patterns, where other kinds of implicit conversions are not allowed */
+ final val EnrichmentEnabled: ContextMode = 1 << 8
+
+ /** Are we in a run of [[scala.tools.nsc.typechecker.TreeCheckers]]? */
+ final val Checking: ContextMode = 1 << 9
+
+ /** Are we retypechecking arguments independently from the function applied to them? See `Typer.tryTypedApply` */
+ // TODO This seems to directly overlap with Mode.SNDTRYmode
+ final val ReTyping: ContextMode = 1 << 10
+
+ final val DefaultMode: ContextMode = MacrosEnabled
+
+ private val contextModeNameMap = Map(
+ ReportErrors -> "ReportErrors",
+ BufferErrors -> "BufferErrors",
+ AmbiguousErrors -> "AmbiguousErrors",
+ ConstructorSuffix -> "ConstructorSuffix",
+ SelfSuperCall -> "SelfSuperCall",
+ ImplicitsEnabled -> "ImplicitsEnabled",
+ MacrosEnabled -> "MacrosEnabled",
+ Checking -> "Checking",
+ ReTyping -> "ReTyping"
+ )
+}
+
+/**
+ * A value class to carry the boolean flags of a context, such as whether errors should
+ * be buffered or reported.
+ */
+final class ContextMode private (val bits: Int) extends AnyVal {
+ import ContextMode._
+
+ def &(other: ContextMode): ContextMode = new ContextMode(bits & other.bits)
+ def |(other: ContextMode): ContextMode = new ContextMode(bits | other.bits)
+ def &~(other: ContextMode): ContextMode = new ContextMode(bits & ~(other.bits))
+ def set(value: Boolean, mask: ContextMode) = if (value) |(mask) else &~(mask)
+
+ def inAll(required: ContextMode) = (this & required) == required
+ def inAny(required: ContextMode) = (this & required) != NOmode
+ def inNone(prohibited: ContextMode) = (this & prohibited) == NOmode
+
+ override def toString =
+ if (bits == 0) "NOmode"
+ else (contextModeNameMap filterKeys inAll).values.toList.sorted mkString " "
+}
diff --git a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala
index 85e31347be..b0c8baae20 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala
@@ -81,8 +81,8 @@ trait Implicits {
val implicitSearchContext = context.makeImplicit(reportAmbiguous)
val result = new ImplicitSearch(tree, pt, isView, implicitSearchContext, pos).bestImplicit
if (saveAmbiguousDivergent && implicitSearchContext.hasErrors) {
- context.updateBuffer(implicitSearchContext.errBuffer.filter(err => err.kind == ErrorKinds.Ambiguous || err.kind == ErrorKinds.Divergent))
- debuglog("update buffer: " + implicitSearchContext.errBuffer)
+ context.updateBuffer(implicitSearchContext.reportBuffer.errors.filter(err => err.kind == ErrorKinds.Ambiguous || err.kind == ErrorKinds.Divergent))
+ debuglog("update buffer: " + implicitSearchContext.reportBuffer.errors)
}
printInference("[infer implicit] inferred " + result)
context.undetparams = context.undetparams filterNot result.subst.from.contains
@@ -584,9 +584,11 @@ trait Implicits {
)
case _ => fallback
}
- if (context.hasErrors) {
- log("implicit adapt failed: " + context.errBuffer.head.errMsg)
- return fail(context.errBuffer.head.errMsg)
+ context.firstError match { // using match rather than foreach to avoid non local return.
+ case Some(err) =>
+ log("implicit adapt failed: " + err.errMsg)
+ return fail(err.errMsg)
+ case None =>
}
if (Statistics.canEnable) Statistics.incCounter(typedImplicits)
@@ -609,7 +611,7 @@ trait Implicits {
}
if (context.hasErrors)
- fail("hasMatchingSymbol reported error: " + context.errBuffer.head.errMsg)
+ fail("hasMatchingSymbol reported error: " + context.firstError.get.errMsg)
else if (isLocal && !hasMatchingSymbol(itree1))
fail("candidate implicit %s is shadowed by %s".format(
info.sym.fullLocationString, itree1.symbol.fullLocationString))
@@ -632,8 +634,11 @@ trait Implicits {
// #2421: check that we correctly instantiated type parameters outside of the implicit tree:
checkBounds(itree2, NoPrefix, NoSymbol, undetParams, targs, "inferred ")
- if (context.hasErrors)
- return fail("type parameters weren't correctly instantiated outside of the implicit tree: " + context.errBuffer.head.errMsg)
+ context.firstError match {
+ case Some(err) =>
+ return fail("type parameters weren't correctly instantiated outside of the implicit tree: " + err.errMsg)
+ case None =>
+ }
// filter out failures from type inference, don't want to remove them from undetParams!
// we must be conservative in leaving type params in undetparams
@@ -668,13 +673,14 @@ trait Implicits {
case t => t
}
- if (context.hasErrors)
- fail("typing TypeApply reported errors for the implicit tree: " + context.errBuffer.head.errMsg)
- else {
- val result = new SearchResult(itree2, subst)
- if (Statistics.canEnable) Statistics.incCounter(foundImplicits)
- printInference("[success] found %s for pt %s".format(result, ptInstantiated))
- result
+ context.firstError match {
+ case Some(err) =>
+ fail("typing TypeApply reported errors for the implicit tree: " + err.errMsg)
+ case None =>
+ val result = new SearchResult(itree2, subst)
+ if (Statistics.canEnable) Statistics.incCounter(foundImplicits)
+ printInference("[success] found %s for pt %s".format(result, ptInstantiated))
+ result
}
}
else fail("incompatible: %s does not match expected type %s".format(itree2.tpe, ptInstantiated))
@@ -828,7 +834,7 @@ trait Implicits {
case sr if sr.isFailure =>
// We don't want errors that occur during checking implicit info
// to influence the check of further infos.
- context.condBufferFlush(_.kind != ErrorKinds.Divergent)
+ context.reportBuffer.retainErrors(ErrorKinds.Divergent)
rankImplicits(is, acc)
case newBest =>
best = newBest
@@ -1085,8 +1091,10 @@ trait Implicits {
try {
val tree1 = typedPos(pos.focus)(arg)
- if (context.hasErrors) processMacroExpansionError(context.errBuffer.head.errPos, context.errBuffer.head.errMsg)
- else new SearchResult(tree1, EmptyTreeTypeSubstituter)
+ context.firstError match {
+ case Some(err) => processMacroExpansionError(err.errPos, err.errMsg)
+ case None => new SearchResult(tree1, EmptyTreeTypeSubstituter)
+ }
} catch {
case ex: TypeError =>
processMacroExpansionError(ex.pos, ex.msg)
diff --git a/src/compiler/scala/tools/nsc/typechecker/Infer.scala b/src/compiler/scala/tools/nsc/typechecker/Infer.scala
index 4913034d3f..49188d1deb 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Infer.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Infer.scala
@@ -914,19 +914,28 @@ trait Infer extends Checkable {
}
/**
- * Todo: Try to make isApplicable always safe (i.e. not cause TypeErrors).
- * The chance of TypeErrors should be reduced through context errors
+ * Are arguments of the given types applicable to `ftpe`? Type argument inference
+ * is tried twice: firstly with the given expected type, and secondly with `WildcardType`.
*/
+ // Todo: Try to make isApplicable always safe (i.e. not cause TypeErrors).
+ // The chance of TypeErrors should be reduced through context errors
private[typechecker] def isApplicableSafe(undetparams: List[Symbol], ftpe: Type,
argtpes0: List[Type], pt: Type): Boolean = {
- val silentContext = context.makeSilent(reportAmbiguousErrors = false)
- val typer0 = newTyper(silentContext)
- val res1 = typer0.infer.isApplicable(undetparams, ftpe, argtpes0, pt)
- if (pt != WildcardType && silentContext.hasErrors) {
- silentContext.flushBuffer()
- val res2 = typer0.infer.isApplicable(undetparams, ftpe, argtpes0, WildcardType)
- if (silentContext.hasErrors) false else res2
- } else res1
+ final case class Result(error: Boolean, applicable: Boolean)
+ def isApplicableWithExpectedType(pt0: Type): Result = {
+ val silentContext = context.makeSilent(reportAmbiguousErrors = false)
+ val applicable = newTyper(silentContext).infer.isApplicable(undetparams, ftpe, argtpes0, pt0)
+ Result(silentContext.hasErrors, applicable)
+ }
+ val canSecondTry = pt != WildcardType
+ val firstTry = isApplicableWithExpectedType(pt)
+ if (!firstTry.error || !canSecondTry)
+ firstTry.applicable
+ else {
+ val secondTry = isApplicableWithExpectedType(WildcardType)
+ // TODO `!secondTry.error &&` was faithfully replicated as part of the refactoring, but mayberedundant.
+ !secondTry.error && secondTry.applicable
+ }
}
/** Is type `ftpe1` strictly more specific than type `ftpe2`
@@ -1535,16 +1544,6 @@ trait Infer extends Checkable {
}
}
- @inline private def inSilentMode(context: Context)(expr: => Boolean): Boolean = {
- val oldState = context.state
- context.setBufferErrors()
- val res = expr
- val contextWithErrors = context.hasErrors
- context.flushBuffer()
- context.restoreState(oldState)
- res && !contextWithErrors
- }
-
// Checks against the name of the parameter and also any @deprecatedName.
private def paramMatchesName(param: Symbol, name: Name) =
param.name == name || param.deprecatedParamName.exists(_ == name)
@@ -1613,7 +1612,7 @@ trait Infer extends Checkable {
}
def followType(sym: Symbol) = followApply(pre memberType sym)
def bestForExpectedType(pt: Type, isLastTry: Boolean): Unit = {
- val applicable0 = alts filter (alt => inSilentMode(context)(isApplicable(undetparams, followType(alt), argtpes, pt)))
+ val applicable0 = alts filter (alt => context inSilentMode (isApplicable(undetparams, followType(alt), argtpes, pt)))
val applicable = overloadsToConsiderBySpecificity(applicable0, argtpes, varargsStar)
val ranked = bestAlternatives(applicable)((sym1, sym2) =>
isStrictlyMoreSpecific(followType(sym1), followType(sym2), sym1, sym2)
@@ -1623,8 +1622,8 @@ trait Infer extends Checkable {
case best :: Nil => tree setSymbol best setType (pre memberType best) // success
case Nil if pt eq WildcardType => NoBestMethodAlternativeError(tree, argtpes, pt, isLastTry) // failed
case Nil => bestForExpectedType(WildcardType, isLastTry) // failed, but retry with WildcardType
- }
- }
+ }
+ }
// This potentially makes up to four attempts: tryTwice may execute
// with and without views enabled, and bestForExpectedType will try again
// with pt = WildcardType if it fails with pt != WildcardType.
@@ -1640,7 +1639,7 @@ trait Infer extends Checkable {
*/
def tryTwice(infer: Boolean => Unit): Unit = {
if (context.implicitsEnabled) {
- val saved = context.state
+ val savedContextMode = context.contextMode
var fallback = false
context.setBufferErrors()
// We cache the current buffer because it is impossible to
@@ -1654,17 +1653,17 @@ trait Infer extends Checkable {
context.withImplicitsDisabled(infer(false))
if (context.hasErrors) {
fallback = true
- context.restoreState(saved)
+ context.contextMode = savedContextMode
context.flushBuffer()
infer(true)
}
} catch {
case ex: CyclicReference => throw ex
case ex: TypeError => // recoverable cyclic references
- context.restoreState(saved)
+ context.contextMode = savedContextMode
if (!fallback) infer(true) else ()
} finally {
- context.restoreState(saved)
+ context.contextMode = savedContextMode
context.updateBuffer(errorsToRestore)
}
}
diff --git a/src/compiler/scala/tools/nsc/typechecker/Macros.scala b/src/compiler/scala/tools/nsc/typechecker/Macros.scala
index 2dbfa1d0d3..d07297bb35 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Macros.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Macros.scala
@@ -769,12 +769,12 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces {
if (macroDebugVerbose) println(s"typecheck #1 (against expectedTpe = $expectedTpe): $expanded")
val expanded1 = typer.context.withImplicitsEnabled(typer.typed(expanded, mode, expectedTpe))
if (expanded1.isErrorTyped) {
- if (macroDebugVerbose) println(s"typecheck #1 has failed: ${typer.context.errBuffer}")
+ if (macroDebugVerbose) println(s"typecheck #1 has failed: ${typer.context.reportBuffer.errors}")
expanded1
} else {
if (macroDebugVerbose) println(s"typecheck #2 (against pt = $pt): $expanded1")
val expanded2 = typer.context.withImplicitsEnabled(super.onSuccess(expanded1))
- if (macroDebugVerbose && expanded2.isErrorTyped) println(s"typecheck #2 has failed: ${typer.context.errBuffer}")
+ if (macroDebugVerbose && expanded2.isErrorTyped) println(s"typecheck #2 has failed: ${typer.context.reportBuffer.errors}")
expanded2
}
}
diff --git a/src/compiler/scala/tools/nsc/typechecker/Namers.scala b/src/compiler/scala/tools/nsc/typechecker/Namers.scala
index f84a758281..541d60c16d 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Namers.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Namers.scala
@@ -48,7 +48,6 @@ trait Namers extends MethodSynthesis {
private class NormalNamer(context: Context) extends Namer(context)
def newNamer(context: Context): Namer = new NormalNamer(context)
- def newNamerFor(context: Context, tree: Tree): Namer = newNamer(context.makeNewScope(tree, tree.symbol))
abstract class Namer(val context: Context) extends MethodSynth with NamerContextErrors { thisNamer =>
// overridden by the presentation compiler
@@ -255,7 +254,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
@@ -1630,7 +1629,7 @@ trait Namers extends MethodSynthesis {
// @M an abstract type's type parameters are entered.
// TODO: change to isTypeMember ?
if (defnSym.isAbstractType)
- newNamerFor(ctx, tree) enterSyms tparams //@M
+ newNamer(ctx.makeNewScope(tree, tree.symbol)) enterSyms tparams //@M
restp complete sym
}
}
diff --git a/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala b/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala
index 6921f8ce27..e22e2c603a 100644
--- a/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala
@@ -19,6 +19,7 @@ trait NamesDefaults { self: Analyzer =>
import global._
import definitions._
import NamesDefaultsErrorsGen._
+ import treeInfo.WildcardStarArg
// Default getters of constructors are added to the companion object in the
// typeCompleter of the constructor (methodSig). To compute the signature,
@@ -278,8 +279,8 @@ trait NamesDefaults { self: Analyzer =>
val repeated = isScalaRepeatedParamType(paramTpe)
val argTpe = (
if (repeated) arg match {
- case Typed(expr, Ident(tpnme.WILDCARD_STAR)) => expr.tpe
- case _ => seqType(arg.tpe)
+ case WildcardStarArg(expr) => expr.tpe
+ case _ => seqType(arg.tpe)
}
else
// Note stabilizing can lead to a non-conformant argument when existentials are involved, e.g. neg/t3507-old.scala, hence the filter.
@@ -302,11 +303,8 @@ trait NamesDefaults { self: Analyzer =>
} else {
new ChangeOwnerTraverser(context.owner, sym) traverse arg // fixes #4502
if (repeated) arg match {
- case Typed(expr, Ident(tpnme.WILDCARD_STAR)) =>
- expr
- case _ =>
- val factory = Select(gen.mkAttributedRef(SeqModule), nme.apply)
- blockTyper.typed(Apply(factory, List(resetLocalAttrs(arg))))
+ case WildcardStarArg(expr) => expr
+ case _ => blockTyper typed gen.mkSeqApply(resetLocalAttrs(arg))
} else arg
}
Some(atPos(body.pos)(ValDef(sym, body).setType(NoType)))
@@ -451,20 +449,6 @@ trait NamesDefaults { self: Analyzer =>
} else NoSymbol
}
- private def savingUndeterminedTParams[T](context: Context)(fn: List[Symbol] => T): T = {
- val savedParams = context.extractUndetparams()
- val savedReporting = context.ambiguousErrors
-
- context.setAmbiguousErrors(false)
- try fn(savedParams)
- finally {
- context.setAmbiguousErrors(savedReporting)
- //@M note that we don't get here when an ambiguity was detected (during the computation of res),
- // as errorTree throws an exception
- context.undetparams = savedParams
- }
- }
-
/** A full type check is very expensive; let's make sure there's a name
* somewhere which could potentially be ambiguous before we go that route.
*/
@@ -479,7 +463,8 @@ trait NamesDefaults { self: Analyzer =>
// def f[T](x: T) = x
// var x = 0
// f(x = 1) << "x = 1" typechecks with expected type WildcardType
- savingUndeterminedTParams(context) { udp =>
+ val udp = context.undetparams
+ context.savingUndeterminedTypeParams(reportAmbiguous = false) {
val subst = new SubstTypeMap(udp, udp map (_ => WildcardType)) {
override def apply(tp: Type): Type = super.apply(tp match {
case TypeRef(_, ByNameParamClass, x :: Nil) => x
diff --git a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala
index efd4fd804f..4d8c9d84a5 100644
--- a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala
@@ -1591,7 +1591,7 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans
enterReference(tree.pos, tpt.tpe.typeSymbol)
tree
- case Typed(_, Ident(tpnme.WILDCARD_STAR)) if !isRepeatedParamArg(tree) =>
+ case treeInfo.WildcardStarArg(_) if !isRepeatedParamArg(tree) =>
unit.error(tree.pos, "no `: _*' annotation allowed here\n"+
"(such annotations are only allowed in arguments to *-parameters)")
tree
diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
index 0d619ea046..fbee228cb9 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
@@ -150,13 +150,13 @@ trait Typers extends Adaptations with Tags {
} else {
mkArg = gen.mkNamedArg // don't pass the default argument (if any) here, but start emitting named arguments for the following args
if (!param.hasDefault && !paramFailed) {
- context.errBuffer.find(_.kind == ErrorKinds.Divergent) match {
+ context.reportBuffer.errors.find(_.kind == ErrorKinds.Divergent) match {
case Some(divergentImplicit) =>
// DivergentImplicit error has higher priority than "no implicit found"
// no need to issue the problem again if we are still in silent mode
if (context.reportErrors) {
context.issue(divergentImplicit)
- context.condBufferFlush(_.kind == ErrorKinds.Divergent)
+ context.reportBuffer.clearErrors(ErrorKinds.Divergent)
}
case None =>
NoImplicitFoundError(fun, param)
@@ -679,17 +679,14 @@ trait Typers extends Adaptations with Tags {
context.undetparams = context1.undetparams
context.savedTypeBounds = context1.savedTypeBounds
context.namedApplyBlockInfo = context1.namedApplyBlockInfo
- if (context1.hasErrors) {
- stopStats()
- SilentTypeError(context1.errBuffer.head)
- } else {
- // If we have a successful result, emit any warnings it created.
- if (context1.hasWarnings) {
- context1.flushAndReturnWarningsBuffer() foreach {
- case (pos, msg) => unit.warning(pos, msg)
- }
- }
- SilentResultValue(result)
+ context1.firstError match {
+ case Some(err) =>
+ stopStats()
+ SilentTypeError(err)
+ case None =>
+ // If we have a successful result, emit any warnings it created.
+ context1.flushAndIssueWarnings()
+ SilentResultValue(result)
}
} else {
assert(context.bufferErrors || isPastTyper, "silent mode is not available past typer")
@@ -1169,7 +1166,10 @@ trait Typers extends Adaptations with Tags {
val silentContext = context.makeImplicit(context.ambiguousErrors)
val res = newTyper(silentContext).typed(
new ApplyImplicitView(coercion, List(tree)) setPos tree.pos, mode, pt)
- if (silentContext.hasErrors) context.issue(silentContext.errBuffer.head) else return res
+ silentContext.firstError match {
+ case Some(err) => context.issue(err)
+ case None => return res
+ }
}
}
}
@@ -2846,7 +2846,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 _ =>
@@ -3099,30 +3099,29 @@ trait Typers extends Adaptations with Tags {
fun.tpe match {
case OverloadedType(pre, alts) =>
def handleOverloaded = {
- val undetparams = context.extractUndetparams()
- val argtpes = new ListBuffer[Type]
- val amode = forArgMode(fun, mode)
- val args1 = args map {
- case arg @ AssignOrNamedArg(Ident(name), rhs) =>
- // named args: only type the righthand sides ("unknown identifier" errors otherwise)
- val rhs1 = typedArg(rhs, amode, BYVALmode, WildcardType)
- argtpes += NamedType(name, rhs1.tpe.deconst)
- // the assign is untyped; that's ok because we call doTypedApply
- treeCopy.AssignOrNamedArg(arg, arg.lhs, rhs1)
- case arg @ Typed(repeated, Ident(tpnme.WILDCARD_STAR)) =>
- val arg1 = typedArg(arg, amode, BYVALmode, WildcardType)
- argtpes += RepeatedType(arg1.tpe.deconst)
- arg1
- case arg =>
- val arg1 = typedArg(arg, amode, BYVALmode, WildcardType)
- argtpes += arg1.tpe.deconst
- arg1
+ val undetparams = context.undetparams
+ val (args1, argTpes) = context.savingUndeterminedTypeParams() {
+ val amode = forArgMode(fun, mode)
+ def typedArg0(tree: Tree) = typedArg(tree, amode, BYVALmode, WildcardType)
+ args.map {
+ case arg @ AssignOrNamedArg(Ident(name), rhs) =>
+ // named args: only type the righthand sides ("unknown identifier" errors otherwise)
+ val rhs1 = typedArg0(rhs)
+ // the assign is untyped; that's ok because we call doTypedApply
+ val arg1 = treeCopy.AssignOrNamedArg(arg, arg.lhs, rhs1)
+ (arg1, NamedType(name, rhs1.tpe.deconst))
+ case arg @ treeInfo.WildcardStarArg(repeated) =>
+ val arg1 = typedArg0(arg)
+ (arg1, RepeatedType(arg1.tpe.deconst))
+ case arg =>
+ val arg1 = typedArg0(arg)
+ (arg1, arg1.tpe.deconst)
+ }.unzip
}
- context.undetparams = undetparams
if (context.hasErrors)
setError(tree)
else {
- inferMethodAlternative(fun, undetparams, argtpes.toList, pt)
+ inferMethodAlternative(fun, undetparams, argTpes, pt)
doTypedApply(tree, adapt(fun, mode.forFunMode, WildcardType), args1, mode, pt)
}
}
@@ -4359,11 +4358,9 @@ trait Typers extends Adaptations with Tags {
case ex: CyclicReference =>
throw ex
case te: TypeError =>
- // @H some of typer erros can still leak,
+ // @H some of typer errors can still leak,
// for instance in continuations
None
- } finally {
- c.flushBuffer()
}
}
@@ -5001,7 +4998,7 @@ trait Typers extends Adaptations with Tags {
typedEta(checkDead(exprTyped))
}
- case Ident(tpnme.WILDCARD_STAR) =>
+ case t if treeInfo isWildcardStarType t =>
val exprTyped = typed(expr, mode.onlySticky, WildcardType)
def subArrayType(pt: Type) =
if (isPrimitiveValueClass(pt.typeSymbol) || !isFullyDefined(pt)) arrayType(pt)
@@ -5312,7 +5309,7 @@ trait Typers extends Adaptations with Tags {
}
def atOwner(owner: Symbol): Typer =
- newTyper(context.make(context.tree, owner))
+ newTyper(context.make(owner = owner))
def atOwner(tree: Tree, owner: Symbol): Typer =
newTyper(context.make(tree, owner))
diff --git a/src/compiler/scala/tools/reflect/ToolBoxFactory.scala b/src/compiler/scala/tools/reflect/ToolBoxFactory.scala
index f6ed5f8f1c..66dda2b530 100644
--- a/src/compiler/scala/tools/reflect/ToolBoxFactory.scala
+++ b/src/compiler/scala/tools/reflect/ToolBoxFactory.scala
@@ -184,7 +184,9 @@ abstract class ToolBoxFactory[U <: JavaUniverse](val u: U) { factorySelf =>
analyzer.inferImplicit(tree, pt, reportAmbiguous = true, isView = isView, context = context, saveAmbiguousDivergent = !silent, pos = pos) match {
case failure if failure.tree.isEmpty =>
trace("implicit search has failed. to find out the reason, turn on -Xlog-implicits: ")(failure.tree)
- if (context.hasErrors) throw ToolBoxError("reflective implicit search has failed: %s".format(context.errBuffer.head.errMsg))
+ context.firstError foreach { err =>
+ throw ToolBoxError("reflective implicit search has failed: %s".format(err.errMsg))
+ }
EmptyTree
case success =>
success.tree