summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAdriaan Moors <adriaan.moors@typesafe.com>2014-07-08 16:44:23 +0200
committerAdriaan Moors <adriaan.moors@typesafe.com>2014-07-17 15:47:21 +0200
commit543bb3e0b60cd1e1d4a3e3be19ec90256cc13151 (patch)
tree43e0b921c4dc9eef88100820392d91d4c6834d11
parentac6dcad89a0121d5c5c78e373fe691a56f1103d4 (diff)
downloadscala-543bb3e0b60cd1e1d4a3e3be19ec90256cc13151.tar.gz
scala-543bb3e0b60cd1e1d4a3e3be19ec90256cc13151.tar.bz2
scala-543bb3e0b60cd1e1d4a3e3be19ec90256cc13151.zip
Encapsulate `TryTwice` as a class, move to `Context`.
All functionality that's closely tied to the error buffer should be in `Context`'s reporting infrastructure. (called `ContextReporter`, soon to follow.)
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Contexts.scala40
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Infer.scala138
2 files changed, 96 insertions, 82 deletions
diff --git a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala
index 695932c870..199209c5fa 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala
@@ -365,6 +365,46 @@ trait Contexts { self: Analyzer =>
reportBuffer.clearAllWarnings()
}
+
+ /** Try inference twice, once without views and once with views,
+ * unless views are already disabled.
+ */
+ abstract class TryTwice {
+ def tryOnce(isLastTry: Boolean): Unit
+
+ def apply(): Unit =
+ if (implicitsEnabled) {
+ val savedContextMode = contextMode
+ var fallback = false
+ setBufferErrors()
+ // We cache the current buffer because it is impossible to
+ // distinguish errors that occurred before entering tryTwice
+ // and our first attempt in 'withImplicitsDisabled'. If the
+ // first attempt fails we try with implicits on *and* clean
+ // buffer but that would also flush any pre-tryTwice valid
+ // errors, hence some manual buffer tweaking is necessary.
+ val errorsToRestore = flushAndReturnBuffer()
+ try {
+ withImplicitsDisabled(tryOnce(false))
+ if (hasErrors) {
+ fallback = true
+ contextMode = savedContextMode
+ flushBuffer()
+ tryOnce(true)
+ }
+ } catch {
+ case ex: CyclicReference => throw ex
+ case ex: TypeError => // recoverable cyclic references
+ contextMode = savedContextMode
+ if (!fallback) tryOnce(true) else ()
+ } finally {
+ contextMode = savedContextMode
+ updateBuffer(errorsToRestore)
+ }
+ }
+ else tryOnce(true)
+ }
+
//
// Temporary mode adjustment
//
diff --git a/src/compiler/scala/tools/nsc/typechecker/Infer.scala b/src/compiler/scala/tools/nsc/typechecker/Infer.scala
index 04657c908b..cb985ddaf6 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Infer.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Infer.scala
@@ -1264,33 +1264,36 @@ trait Infer extends Checkable {
* If no alternative matches `pt`, take the parameterless one anyway.
*/
def inferExprAlternative(tree: Tree, pt: Type): Tree = {
- def tryOurBests(pre: Type, alts: List[Symbol], isSecondTry: Boolean): Unit = {
- val alts0 = alts filter (alt => isWeaklyCompatible(pre memberType alt, pt))
- val alts1 = if (alts0.isEmpty) alts else alts0
- val bests = bestAlternatives(alts1) { (sym1, sym2) =>
- val tp1 = pre memberType sym1
- val tp2 = pre memberType sym2
-
- ( (tp2 eq ErrorType)
- || isWeaklyCompatible(tp1, pt) && !isWeaklyCompatible(tp2, pt)
- || isStrictlyMoreSpecific(tp1, tp2, sym1, sym2)
- )
- }
- // todo: missing test case for bests.isEmpty
- bests match {
- case best :: Nil => tree setSymbol best setType (pre memberType best)
- case best :: competing :: _ if alts0.nonEmpty =>
- // SI-6912 Don't give up and leave an OverloadedType on the tree.
- // Originally I wrote this as `if (secondTry) ... `, but `tryTwice` won't attempt the second try
- // unless an error is issued. We're not issuing an error, in the assumption that it would be
- // spurious in light of the erroneous expected type
- if (pt.isErroneous) setError(tree)
- else AmbiguousExprAlternativeError(tree, pre, best, competing, pt, isSecondTry)
- case _ => if (bests.isEmpty || alts0.isEmpty) NoBestExprAlternativeError(tree, pt, isSecondTry)
+ val c = context
+ class InferTwice(pre: Type, alts: List[Symbol]) extends c.TryTwice {
+ def tryOnce(isSecondTry: Boolean): Unit = {
+ val alts0 = alts filter (alt => isWeaklyCompatible(pre memberType alt, pt))
+ val alts1 = if (alts0.isEmpty) alts else alts0
+ val bests = bestAlternatives(alts1) { (sym1, sym2) =>
+ val tp1 = pre memberType sym1
+ val tp2 = pre memberType sym2
+
+ ( (tp2 eq ErrorType)
+ || isWeaklyCompatible(tp1, pt) && !isWeaklyCompatible(tp2, pt)
+ || isStrictlyMoreSpecific(tp1, tp2, sym1, sym2)
+ )
+ }
+ // todo: missing test case for bests.isEmpty
+ bests match {
+ case best :: Nil => tree setSymbol best setType (pre memberType best)
+ case best :: competing :: _ if alts0.nonEmpty =>
+ // SI-6912 Don't give up and leave an OverloadedType on the tree.
+ // Originally I wrote this as `if (secondTry) ... `, but `tryTwice` won't attempt the second try
+ // unless an error is issued. We're not issuing an error, in the assumption that it would be
+ // spurious in light of the erroneous expected type
+ if (pt.isErroneous) setError(tree)
+ else AmbiguousExprAlternativeError(tree, pre, best, competing, pt, isSecondTry)
+ case _ => if (bests.isEmpty || alts0.isEmpty) NoBestExprAlternativeError(tree, pt, isSecondTry)
+ }
}
}
tree.tpe match {
- case OverloadedType(pre, alts) => tryTwice(tryOurBests(pre, alts, _)) ; tree
+ case OverloadedType(pre, alts) => (new InferTwice(pre, alts)).apply() ; tree
case _ => tree
}
}
@@ -1368,70 +1371,41 @@ trait Infer extends Checkable {
* @pre tree.tpe is an OverloadedType.
*/
def inferMethodAlternative(tree: Tree, undetparams: List[Symbol], argtpes0: List[Type], pt0: Type): Unit = {
- val OverloadedType(pre, alts) = tree.tpe
- var varargsStar = false
- val argtpes = argtpes0 mapConserve {
- case RepeatedType(tp) => varargsStar = true ; tp
- case tp => tp
- }
- def followType(sym: Symbol) = followApply(pre memberType sym)
- def bestForExpectedType(pt: Type, isLastTry: Boolean): Unit = {
- 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)
- )
- ranked match {
- case best :: competing :: _ => AmbiguousMethodAlternativeError(tree, pre, best, competing, argtpes, pt, isLastTry) // ambiguous
- case best :: Nil => tree setSymbol best setType (pre memberType best) // success
- case Nil if pt.isWildcard => 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
+ // This potentially makes up to four attempts: tryOnce may execute
// with and without views enabled, and bestForExpectedType will try again
// with pt = WildcardType if it fails with pt != WildcardType.
- tryTwice { isLastTry =>
- val pt = if (pt0.typeSymbol == UnitClass) WildcardType else pt0
- debuglog(s"infer method alt ${tree.symbol} with alternatives ${alts map pre.memberType} argtpes=$argtpes pt=$pt")
- bestForExpectedType(pt, isLastTry)
- }
- }
+ val c = context
+ class InferTwice extends c.TryTwice {
+ private[this] val OverloadedType(pre, alts) = tree.tpe
+ private[this] var varargsStar = false
+ private[this] val argtpes = argtpes0 mapConserve {
+ case RepeatedType(tp) => varargsStar = true ; tp
+ case tp => tp
+ }
- /** Try inference twice, once without views and once with views,
- * unless views are already disabled.
- */
- def tryTwice(infer: Boolean => Unit): Unit = {
- if (context.implicitsEnabled) {
- val savedContextMode = context.contextMode
- var fallback = false
- context.setBufferErrors()
- // We cache the current buffer because it is impossible to
- // distinguish errors that occurred before entering tryTwice
- // and our first attempt in 'withImplicitsDisabled'. If the
- // first attempt fails we try with implicits on *and* clean
- // buffer but that would also flush any pre-tryTwice valid
- // errors, hence some manual buffer tweaking is necessary.
- val errorsToRestore = context.flushAndReturnBuffer()
- try {
- context.withImplicitsDisabled(infer(false))
- if (context.hasErrors) {
- fallback = true
- context.contextMode = savedContextMode
- context.flushBuffer()
- infer(true)
+ private def followType(sym: Symbol) = followApply(pre memberType sym)
+ private def bestForExpectedType(pt: Type, isLastTry: Boolean): Unit = {
+ 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)
+ )
+ ranked match {
+ case best :: competing :: _ => AmbiguousMethodAlternativeError(tree, pre, best, competing, argtpes, pt, isLastTry) // ambiguous
+ case best :: Nil => tree setSymbol best setType (pre memberType best) // success
+ case Nil if pt.isWildcard => NoBestMethodAlternativeError(tree, argtpes, pt, isLastTry) // failed
+ case Nil => bestForExpectedType(WildcardType, isLastTry) // failed, but retry with WildcardType
}
- } catch {
- case ex: CyclicReference => throw ex
- case ex: TypeError => // recoverable cyclic references
- context.contextMode = savedContextMode
- if (!fallback) infer(true) else ()
- } finally {
- context.contextMode = savedContextMode
- context.updateBuffer(errorsToRestore)
+ }
+
+ private[this] val pt = if (pt0.typeSymbol == UnitClass) WildcardType else pt0
+ def tryOnce(isLastTry: Boolean): Unit = {
+ debuglog(s"infer method alt ${tree.symbol} with alternatives ${alts map pre.memberType} argtpes=$argtpes pt=$pt")
+ bestForExpectedType(pt, isLastTry)
}
}
- else infer(true)
+
+ (new InferTwice).apply()
}
/** Assign `tree` the type of all polymorphic alternatives