summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJason Zaugg <jzaugg@gmail.com>2013-04-10 11:52:01 +0200
committerJason Zaugg <jzaugg@gmail.com>2013-04-21 13:01:03 +0200
commitec5eaee3ec27b614c3ffe0496a755623c912cfdd (patch)
treed08a45df201ee77532b0f9b790315c3a114ffc5c /src
parent190aea9f015280d0f48cc2928515d11eccce4e33 (diff)
downloadscala-ec5eaee3ec27b614c3ffe0496a755623c912cfdd.tar.gz
scala-ec5eaee3ec27b614c3ffe0496a755623c912cfdd.tar.bz2
scala-ec5eaee3ec27b614c3ffe0496a755623c912cfdd.zip
SI-7345 More refactoring and documentation in Contexts
- Use `firstError match {}` rather than `if (c.hasErrors) c.firstError ..` - Convert implementation comments to doc comments - Add new doc comments - Refactor `Context#make` for better readability. - Note a few TODOs. - Roughly delineate the internal structure of Contexts with comments. - Combine `mode` and `state` methods into `contextMode`. - Move `isNameInScope` closer to its compadres. - Improving alignment, tightening access.
Diffstat (limited to 'src')
-rw-r--r--src/compiler/scala/reflect/macros/runtime/Typers.scala7
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Contexts.scala260
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Implicits.scala40
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Infer.scala19
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Typers.scala38
-rw-r--r--src/compiler/scala/tools/reflect/ToolBoxFactory.scala4
6 files changed, 238 insertions, 130 deletions
diff --git a/src/compiler/scala/reflect/macros/runtime/Typers.scala b/src/compiler/scala/reflect/macros/runtime/Typers.scala
index 99b934787d..79833d155e 100644
--- a/src/compiler/scala/reflect/macros/runtime/Typers.scala
+++ b/src/compiler/scala/reflect/macros/runtime/Typers.scala
@@ -54,11 +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) {
- val err = context.firstError
- throw new TypecheckException(err.errPos, err.errMsg)
+ context.firstError match {
+ case Some(err) => throw new TypecheckException(err.errPos, err.errMsg)
+ case None => universe.EmptyTree
}
- 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 22cda50591..4b9d3fda01 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala
@@ -111,37 +111,58 @@ trait Contexts { self: Analyzer =>
class Context private[typechecker] {
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 = {
+ /** 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 = _
+ /** The next outer context whose tree is a template or package definition */
+ var enclClass: Context = _
+
+ @inline private def savingEnclClass[A](c: Context)(a: => A): A = {
val saved = enclClass
enclClass = c
try a finally enclClass = saved
}
- private var mode: ContextMode = ContextMode.DefaultMode
+ /** 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) {
- mode = mode.set(value, mask)
+ 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 = {
- mode = mode.set(true, enable).set(false, disable)
- this
+ contextMode = contextMode.set(true, enable).set(false, disable)
+ this
}
- def apply(mask: ContextMode): Boolean = mode.inAll(mask)
- 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
+ /** 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
+
+ /** Undetermined type parameters. Not inherited to child contexts */
+ private var _undetparams: List[Symbol] = List()
+
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
+
+ /** The currently visible imports */
+ var imports: List[ImportInfo] = List()
+
+ /** 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
@@ -151,9 +172,6 @@ trait Contexts { self: Analyzer =>
def returnsSeen = this(ReturnsSeen)
def inSelfSuperCall_=(value: Boolean) = this(SelfSuperCall) = value
def inSelfSuperCall = this(SelfSuperCall)
-
- var diagnostic: List[String] = Nil // these messages are printed when issuing an error
-
def implicitsEnabled_=(value: Boolean) = this(ImplicitsEnabled) = value
def implicitsEnabled = this(ImplicitsEnabled)
def macrosEnabled_=(value: Boolean) = this(MacrosEnabled) = value
@@ -165,45 +183,61 @@ trait Contexts { self: Analyzer =>
def retyping_=(value: Boolean) = this(ReTyping) = value
def retyping = this(ReTyping)
- var savedTypeBounds: List[(Symbol, Type)] = List() // saved type bounds
- // for type parameters which are narrowed in a GADT
+ /** 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 _reportBuffer: ReportBuffer = new ReportBuffer
- def reportBuffer = _reportBuffer
-
+ /** 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
+ /** The next enclosing context (potentially `this`) that has a `CaseDef` as a tree */
def enclosingCaseDef = nextEnclosing(_.tree.isInstanceOf[CaseDef])
+
def undetparamsString =
if (undetparams.isEmpty) ""
else undetparams.mkString("undetparams=", ", ", "")
- def undetparams = _undetparams
+ /** Undetermined type parameters. See `Infer#{inferExprInstance, adjustTypeArgs}`. */
+ 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
}
- def errBuffer = reportBuffer.errors
- // TODO use Option[Error] this rather then `if (c.hasErrors) c.firstError._`
- def firstError: AbsTypeError = reportBuffer.firstError
- def hasErrors = reportBuffer.hasErrors
- def hasWarnings = reportBuffer.hasWarnings
+ //
+ // Error reporting policies and buffer.
+ //
- def state: ContextMode = mode
- def restoreState(state0: ContextMode) = mode = state0
+ 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.firstErrorOpt
+ /** The first warning, if any, in the report buffer */
+ def firstWarning: Option[(Position, String)] = reportBuffer.firstWarningOpt
+ /** 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 = mode.inNone(ReportErrors | BufferErrors)
+ def throwErrors = contextMode.inNone(ReportErrors | BufferErrors)
def setReportErrors(): Unit = set(enable = ReportErrors | AmbiguousErrors, disable = BufferErrors)
def setBufferErrors(): Unit = {set(enable = BufferErrors, disable = ReportErrors | AmbiguousErrors); warnIfBufferNotClean()}
@@ -215,24 +249,34 @@ trait Contexts { self: Analyzer =>
devWarning("When entering the buffer state, context has to be clean. Current buffer: " + reportBuffer.errors)
}
+ // TODO SI-7345 refactor this part of the API.
+
+ /** 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
}
+ /** Return and clear all warnings from the report buffer */
def flushAndReturnWarningsBuffer(): immutable.Seq[(Position, String)] = {
val current = reportBuffer.warnings
reportBuffer.clearAllWarnings()
current
}
+ //
+ // Temporary mode adjustment
+ //
+
private def withMode[T](enabled: ContextMode = NOmode, disabled: ContextMode = NOmode)(op: => T): T = {
- val saved = mode
+ val saved = contextMode
set(enabled, disabled)
try op
- finally mode = saved
+ finally contextMode = saved
}
def withImplicitsEnabled[T](op: => T): T = withMode(enabled = ImplicitsEnabled)(op)
@@ -241,19 +285,17 @@ trait Contexts { self: Analyzer =>
def withMacrosEnabled[T](op: => T): T = withMode(enabled = MacrosEnabled)(op)
def withMacrosDisabled[T](op: => T): T = withMode(disabled = MacrosEnabled)(op)
+ //
+ // 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.
+ */
def make(tree: Tree = tree, owner: Symbol = owner,
scope: Scope = scope, imports: List[ImportInfo] = imports,
unit: CompilationUnit = unit): Context = {
-
- val c = new Context
- c.unit = unit
- c.tree = tree
- c.owner = owner
- c.scope = scope
- c.outer = this
-
- c.restoreState(this.state) // note: ConstructorSuffix conditionally overwritten below.
-
val isTemplateOrPackage = tree match {
case _: Template | _: PackageDef => true
case _ => false
@@ -262,28 +304,49 @@ trait Contexts { self: Analyzer =>
case _: DefDef => true
case _ => false
}
-
- c.prefix =
- if (isTemplateOrPackage) c.owner.thisType
- else if (c.owner != this.owner && c.isLocal) NoPrefix
+ val sameOwner = owner == this.owner
+ val sameScope = scope == this.scope
+ val prefixInChild =
+ if (isTemplateOrPackage) owner.thisType
+ else if (!sameOwner && owner.isTerm) NoPrefix
else prefix
- c.enclClass = if (isTemplateOrPackage) c else enclClass
+
+ // The blank canvas
+ val c = new Context
+
+ // 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)
- c.enclMethod = if (isDefDef) c else enclMethod
-
- c.variance = this.variance
- c.depth = this.depth + (if (scope == this.scope) 0 else 1)
- c.imports = imports
- c.diagnostic = this.diagnostic
- c.typingIndentLevel = typingIndentLevel
- c.openImplicits = this.openImplicits
- // Usually, the parent and child contexts share the report buffer. Exception: `makeSilent` will use a fresh buffer.
- c._reportBuffer = this.reportBuffer
+ c.enclMethod = if (isDefDef) c else enclMethod
+ c.depth = depth + (if (sameScope) 0 else 1)
+
registerContext(c.asInstanceOf[analyzer.Context])
debuglog("[context] ++ " + c.unit + " / " + tree.summaryString)
c
}
+ 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 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
@@ -292,14 +355,11 @@ trait Contexts { self: Analyzer =>
make(imp, imports = impInfo :: imports)
}
- def make(tree: Tree, owner: Symbol, scope: Scope): Context =
- // TODO Moving this optimization into the main overload of `make` causes all tests to fail. Why?
- if (tree == this.tree && owner == this.owner && scope == this.scope) this
- else make(tree, owner, scope, imports, unit)
-
+ /** Make a child context that represents a new nested scope */
def makeNewScope(tree: Tree, owner: Symbol): Context =
make(tree, owner, newNestedScope(scope))
+ /** Make a child context that buffers errors and warnings into a fresh report buffer. */
def makeSilent(reportAmbiguousErrors: Boolean, newtree: Tree = tree): Context = {
val c = make(newtree)
c.setBufferErrors()
@@ -308,6 +368,7 @@ trait Contexts { self: Analyzer =>
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 | EnrichmentEnabled) = false
@@ -327,7 +388,7 @@ trait Contexts { self: Analyzer =>
def makeConstructorContext = {
val baseContext = enclClass.outer.nextEnclosing(!_.tree.isInstanceOf[Template])
val argContext = baseContext.makeNewScope(tree, owner)
- argContext.restoreState(state)
+ argContext.contextMode = contextMode
argContext.inSelfSuperCall = true
def enterElems(c: Context) {
def enterLocalElems(e: ScopeEntry) {
@@ -347,6 +408,10 @@ trait Contexts { self: Analyzer =>
argContext
}
+ //
+ // Error and warning issuance
+ //
+
private def addDiagString(msg: String) = {
val ds =
if (diagnostic.isEmpty) ""
@@ -367,12 +432,14 @@ trait Contexts { self: Analyzer =>
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)
@@ -380,23 +447,25 @@ 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) reportBuffer += (pos -> msg)
}
@@ -404,8 +473,6 @@ trait Contexts { self: Analyzer =>
/** Is the owning symbol of this context a term? */
final def isLocal: Boolean = owner.isTerm
- def isNameInScope(name: Name) = lookupSymbol(name, _ => true).isSuccess
-
// nextOuter determines which context is searched next for implicits
// (after `this`, which contributes `newImplicits` below.) In
// most cases, it is simply the outer context: if we're owned by
@@ -433,11 +500,15 @@ trait Contexts { self: Analyzer =>
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) =
+
+ //
+ // 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.
@@ -449,8 +520,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))
@@ -530,6 +600,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")
@@ -566,6 +640,10 @@ trait Contexts { self: Analyzer =>
}
}
+ //
+ // Implicit collection
+ //
+
private var implicitsCache: List[List[ImplicitInfo]] = null
private var implicitsRunId = NoRunId
@@ -654,6 +732,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:
@@ -763,6 +845,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.
@@ -999,6 +1083,10 @@ trait Contexts { self: Analyzer =>
this
}
+ def clearAll(): this.type = {
+ clearAllErrors(); clearAllWarnings();
+ }
+
def clearAllErrors(): this.type = {
errorBuffer.clear()
this
@@ -1016,9 +1104,11 @@ trait Contexts { self: Analyzer =>
this
}
- def hasErrors = errorBuffer.nonEmpty
- def hasWarnings = warningBuffer.nonEmpty
- def firstError = errorBuffer.head
+ def hasErrors = errorBuffer.nonEmpty
+ def hasWarnings = warningBuffer.nonEmpty
+ def firstError = errorBuffer.head
+ def firstErrorOpt = errorBuffer.headOption
+ def firstWarningOpt = warningBuffer.headOption
}
class ImportInfo(val tree: Import, val depth: Int) {
diff --git a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala
index f7f6970c14..b0c8baae20 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala
@@ -584,10 +584,11 @@ trait Implicits {
)
case _ => fallback
}
- if (context.hasErrors) {
- val err = context.firstError
- log("implicit adapt failed: " + err.errMsg)
- return fail(err.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)
@@ -610,7 +611,7 @@ trait Implicits {
}
if (context.hasErrors)
- fail("hasMatchingSymbol reported error: " + context.firstError.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))
@@ -633,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.firstError.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
@@ -669,13 +673,14 @@ trait Implicits {
case t => t
}
- if (context.hasErrors)
- fail("typing TypeApply reported errors for the implicit tree: " + context.firstError.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))
@@ -1086,11 +1091,10 @@ trait Implicits {
try {
val tree1 = typedPos(pos.focus)(arg)
- if (context.hasErrors) {
- val err = context.firstError
- processMacroExpansionError(err.errPos, err.errMsg)
+ context.firstError match {
+ case Some(err) => processMacroExpansionError(err.errPos, err.errMsg)
+ case None => new SearchResult(tree1, EmptyTreeTypeSubstituter)
}
- else 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 8d7830897d..8c87cf51d3 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Infer.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Infer.scala
@@ -1531,14 +1531,15 @@ trait Infer extends Checkable {
}
}
+ /** @return true if the `expr` evaluates to true within a silent Context that incurs no errors */
@inline private def inSilentMode(context: Context)(expr: => Boolean): Boolean = {
- val oldState = context.state
+ val savedContextMode = context.contextMode
context.setBufferErrors()
val res = expr
- val contextWithErrors = context.hasErrors
- context.flushBuffer()
- context.restoreState(oldState)
- res && !contextWithErrors
+ val contextHadErrors = context.hasErrors
+ context.reportBuffer.clearAll()
+ context.contextMode = savedContextMode
+ res && !contextHadErrors
}
// Checks against the name of the parameter and also any @deprecatedName.
@@ -1636,7 +1637,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
@@ -1650,17 +1651,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/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
index f57352b11e..3736380954 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
@@ -492,8 +492,16 @@ trait Typers extends Adaptations with Tags {
@inline
final def typerReportAnyContextErrors[T](c: Context)(f: Typer => T): T = {
val res = f(newTyper(c))
- if (c.hasErrors)
- context.issue(c.firstError)
+ c.firstError match {
+ case Some(err) =>
+ context.issue(err)
+ c.reportBuffer.warnings foreach {
+ case (pos, msg) => context.warning(pos, msg)
+ }
+ // Seemingly we should perform `c.reportBuffer.clearAll()` here. But
+ // `context` might share the ReportBuffer with `c`.
+ case None =>
+ }
res
}
@@ -687,17 +695,18 @@ trait Typers extends Adaptations with Tags {
context.undetparams = context1.undetparams
context.savedTypeBounds = context1.savedTypeBounds
context.namedApplyBlockInfo = context1.namedApplyBlockInfo
- if (context1.hasErrors) {
- stopStats()
- SilentTypeError(context1.firstError)
- } 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)
+ context1.firstError match {
+ case Some(err) =>
+ stopStats()
+ SilentTypeError(err)
+ case None =>
+ // If we have a successful result, emit any warnings it created.
+ if (context1.reportBuffer.hasWarnings) {
+ context1.flushAndReturnWarningsBuffer() foreach {
+ case (pos, msg) => unit.warning(pos, msg)
+ }
}
- }
- SilentResultValue(result)
+ SilentResultValue(result)
}
} else {
assert(context.bufferErrors || isPastTyper, "silent mode is not available past typer")
@@ -1174,7 +1183,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.firstError) else return res
+ silentContext.firstError match {
+ case Some(err) => context.issue(err)
+ case None => return res
+ }
}
}
}
diff --git a/src/compiler/scala/tools/reflect/ToolBoxFactory.scala b/src/compiler/scala/tools/reflect/ToolBoxFactory.scala
index 132dd251db..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.firstError.errMsg))
+ context.firstError foreach { err =>
+ throw ToolBoxError("reflective implicit search has failed: %s".format(err.errMsg))
+ }
EmptyTree
case success =>
success.tree