summaryrefslogtreecommitdiff
path: root/src/compiler/scala/tools/nsc/typechecker
diff options
context:
space:
mode:
authorJason Zaugg <jzaugg@gmail.com>2013-04-09 21:47:05 +0200
committerJason Zaugg <jzaugg@gmail.com>2013-04-21 13:01:01 +0200
commitc9f5ab031a881812149908491217b531b6e29e06 (patch)
treee9db1c0432956e80e1d87b2b321a8834005564b1 /src/compiler/scala/tools/nsc/typechecker
parentff5dde125a9a88395f52a898a26aabb28f81f5e9 (diff)
downloadscala-c9f5ab031a881812149908491217b531b6e29e06.tar.gz
scala-c9f5ab031a881812149908491217b531b6e29e06.tar.bz2
scala-c9f5ab031a881812149908491217b531b6e29e06.zip
SI-7345 Encapsulate warning and error buffers in ReportBuffer.
- merge `Context#{buffer, warningBuffer}` into `Context#reportBuffer`. - only expose immutable copies of the error and warnings buffers - Introduce a convenience method, `firstError`. - replace `condBufferFlush` more specific methods to retain or clear errors by error kind.
Diffstat (limited to 'src/compiler/scala/tools/nsc/typechecker')
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Contexts.scala99
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Implicits.scala22
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Macros.scala4
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Typers.scala10
4 files changed, 95 insertions, 40 deletions
diff --git a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala
index bc7fb48571..0a269c6723 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
@@ -173,8 +173,8 @@ trait Contexts { self: Analyzer =>
var typingIndentLevel: Int = 0
def typingIndent = " " * typingIndentLevel
- var buffer: mutable.Set[AbsTypeError] = _
- var warningsBuffer: mutable.Set[(Position, String)] = _
+ var _reportBuffer: ReportBuffer = new ReportBuffer
+ def reportBuffer = _reportBuffer
def enclClassOrMethod: Context =
if ((owner eq NoSymbol) || (owner.isClass) || (owner.isMethod)) this
@@ -193,9 +193,11 @@ trait Contexts { self: Analyzer =>
tparams
}
- def errBuffer = buffer
- def hasErrors = buffer.nonEmpty
- def hasWarnings = warningsBuffer.nonEmpty
+ 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
def state: ContextMode = mode
def restoreState(state0: ContextMode) = mode = state0
@@ -212,23 +214,19 @@ trait Contexts { self: Analyzer =>
private def warnIfBufferNotClean() {
if (!bufferErrors && hasErrors)
- devWarning("When entering the buffer state, context has to be clean. Current buffer: " + buffer)
+ devWarning("When entering the buffer state, context has to be clean. Current buffer: " + reportBuffer.errors)
}
- 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()
+ def updateBuffer(errors: Traversable[AbsTypeError]) = reportBuffer ++= errors
+ def flushBuffer() { reportBuffer.clearAllErrors() }
+ def flushAndReturnBuffer(): immutable.Seq[AbsTypeError] = {
+ val current = reportBuffer.errors
+ reportBuffer.clearAllErrors()
current
}
- def flushAndReturnWarningsBuffer(): mutable.Set[(Position, String)] = {
- val current = warningsBuffer.clone()
- warningsBuffer.clear()
+ def flushAndReturnWarningsBuffer(): immutable.Seq[(Position, String)] = {
+ val current = reportBuffer.warnings
+ reportBuffer.clearAllWarnings()
current
}
@@ -281,8 +279,8 @@ trait Contexts { self: Analyzer =>
c.diagnostic = this.diagnostic
c.typingIndentLevel = typingIndentLevel
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
+ // Usually, the parent and child contexts share the report buffer. Exception: `makeSilent` will use a fresh buffer.
+ c._reportBuffer = this.reportBuffer
registerContext(c.asInstanceOf[analyzer.Context])
debuglog("[context] ++ " + c.unit + " / " + tree.summaryString)
c
@@ -308,7 +306,7 @@ trait Contexts { self: Analyzer =>
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
}
@@ -367,7 +365,7 @@ 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)
}
@@ -402,7 +400,7 @@ trait Contexts { self: Analyzer =>
def warning(pos: Position, msg: String): Unit = warning(pos, msg, force = false)
def warning(pos: Position, msg: String, force: Boolean) {
if (reportErrors || force) unit.warning(pos, msg)
- else if (bufferErrors) warningsBuffer += ((pos, msg))
+ else if (bufferErrors) reportBuffer += (pos -> msg)
}
/** Is the owning symbol of this context a term? */
@@ -973,6 +971,58 @@ trait Contexts { self: Analyzer =>
}
} //class Context
+ /** 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 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 hasWarnings = warningBuffer.nonEmpty
+ def firstError = errorBuffer.head
+ }
+
class ImportInfo(val tree: Import, val depth: Int) {
def pos = tree.pos
def posOf(sel: ImportSelector) = tree.pos withPoint sel.namePos
@@ -1130,3 +1180,4 @@ final class ContextMode private (val bits: Int) extends AnyVal {
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..f7f6970c14 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
@@ -585,8 +585,9 @@ trait Implicits {
case _ => fallback
}
if (context.hasErrors) {
- log("implicit adapt failed: " + context.errBuffer.head.errMsg)
- return fail(context.errBuffer.head.errMsg)
+ val err = context.firstError
+ log("implicit adapt failed: " + err.errMsg)
+ return fail(err.errMsg)
}
if (Statistics.canEnable) Statistics.incCounter(typedImplicits)
@@ -609,7 +610,7 @@ trait Implicits {
}
if (context.hasErrors)
- fail("hasMatchingSymbol reported error: " + context.errBuffer.head.errMsg)
+ fail("hasMatchingSymbol reported error: " + context.firstError.errMsg)
else if (isLocal && !hasMatchingSymbol(itree1))
fail("candidate implicit %s is shadowed by %s".format(
info.sym.fullLocationString, itree1.symbol.fullLocationString))
@@ -633,7 +634,7 @@ 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)
+ return fail("type parameters weren't correctly instantiated outside of the implicit tree: " + context.firstError.errMsg)
// 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,7 +670,7 @@ trait Implicits {
}
if (context.hasErrors)
- fail("typing TypeApply reported errors for the implicit tree: " + context.errBuffer.head.errMsg)
+ 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)
@@ -828,7 +829,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,7 +1086,10 @@ trait Implicits {
try {
val tree1 = typedPos(pos.focus)(arg)
- if (context.hasErrors) processMacroExpansionError(context.errBuffer.head.errPos, context.errBuffer.head.errMsg)
+ if (context.hasErrors) {
+ val err = context.firstError
+ processMacroExpansionError(err.errPos, err.errMsg)
+ }
else new SearchResult(tree1, EmptyTreeTypeSubstituter)
} catch {
case ex: TypeError =>
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/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
index 692c24fd20..69cd802170 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)
@@ -493,7 +493,7 @@ trait Typers extends Adaptations with Tags {
final def typerReportAnyContextErrors[T](c: Context)(f: Typer => T): T = {
val res = f(newTyper(c))
if (c.hasErrors)
- context.issue(c.errBuffer.head)
+ context.issue(c.firstError)
res
}
@@ -689,7 +689,7 @@ trait Typers extends Adaptations with Tags {
context.namedApplyBlockInfo = context1.namedApplyBlockInfo
if (context1.hasErrors) {
stopStats()
- SilentTypeError(context1.errBuffer.head)
+ SilentTypeError(context1.firstError)
} else {
// If we have a successful result, emit any warnings it created.
if (context1.hasWarnings) {
@@ -1174,7 +1174,7 @@ 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
+ if (silentContext.hasErrors) context.issue(silentContext.firstError) else return res
}
}
}