diff options
author | Hubert Plociniczak <hubert.plociniczak@gmail.com> | 2012-05-22 00:20:11 +0200 |
---|---|---|
committer | Hubert Plociniczak <hubert.plociniczak@gmail.com> | 2012-05-22 11:04:57 +0200 |
commit | 1ddc9358f551f4586adb1a540e0255fd0e5a33c9 (patch) | |
tree | 9e773c703d1512f49c6703a1f0172927367cd5cb | |
parent | bbfbd6694d564552a6a8d903dc5d6e34f5976c2b (diff) | |
download | scala-1ddc9358f551f4586adb1a540e0255fd0e5a33c9.tar.gz scala-1ddc9358f551f4586adb1a540e0255fd0e5a33c9.tar.bz2 scala-1ddc9358f551f4586adb1a540e0255fd0e5a33c9.zip |
More consistent solution for errorenous situations when infering the alternative.
From now on we specify explicity which attempt is tried and setError on the tree
depending on that. Relying on the context was a nice idea to avoid that but was
fragile when we were in the silent mode (like checking named arguments).
-rw-r--r-- | src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala | 38 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/typechecker/Infer.scala | 42 |
2 files changed, 41 insertions, 39 deletions
diff --git a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala index 7fa421e353..9aa1b1b9c2 100644 --- a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala +++ b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala @@ -690,35 +690,43 @@ trait ContextErrors { setError(tree) } - def NoBestMethodAlternativeError(tree: Tree, argtpes: List[Type], pt: Type) = { + // side-effect on the tree, break the overloaded type cycle in infer + private def setErrorOnLastTry(lastTry: Boolean, tree: Tree) = if (lastTry) setError(tree) + + def NoBestMethodAlternativeError(tree: Tree, argtpes: List[Type], pt: Type, lastTry: Boolean) = { issueNormalTypeError(tree, applyErrorMsg(tree, " cannot be applied to ", argtpes, pt)) // since inferMethodAlternative modifies the state of the tree // we have to set the type of tree to ErrorType only in the very last - // fallback action that is done in the inference (tracking it manually is error prone). + // fallback action that is done in the inference. // This avoids entering infinite loop in doTypeApply. - if (implicitly[Context].reportErrors) setError(tree) + setErrorOnLastTry(lastTry, tree) + //if (implicitly[Context].reportErrors) setError(tree) } def AmbiguousMethodAlternativeError(tree: Tree, pre: Type, best: Symbol, - firstCompeting: Symbol, argtpes: List[Type], pt: Type) = { - val msg0 = - "argument types " + argtpes.mkString("(", ",", ")") + - (if (pt == WildcardType) "" else " and expected result type " + pt) - val (pos, msg) = ambiguousErrorMsgPos(tree.pos, pre, best, firstCompeting, msg0) - // discover last attempt in a similar way as for NoBestMethodAlternativeError - if (implicitly[Context].ambiguousErrors) setError(tree) - issueAmbiguousTypeError(pre, best, firstCompeting, AmbiguousTypeError(tree, pos, msg)) + firstCompeting: Symbol, argtpes: List[Type], pt: Type, lastTry: Boolean) = { + + if (!(argtpes exists (_.isErroneous)) && !pt.isErroneous) { + val msg0 = + "argument types " + argtpes.mkString("(", ",", ")") + + (if (pt == WildcardType) "" else " and expected result type " + pt) + val (pos, msg) = ambiguousErrorMsgPos(tree.pos, pre, best, firstCompeting, msg0) + setErrorOnLastTry(lastTry, tree) + issueAmbiguousTypeError(pre, best, firstCompeting, AmbiguousTypeError(tree, pos, msg)) + } else setError(tree) // do not even try further attempts because they should all fail + // even if this is not the last attempt (because of the SO's possibility on the horizon) + } - def NoBestExprAlternativeError(tree: Tree, pt: Type) = { + def NoBestExprAlternativeError(tree: Tree, pt: Type, lastTry: Boolean) = { issueNormalTypeError(tree, withAddendum(tree.pos)(typeErrorMsg(tree.symbol.tpe, pt, isPossiblyMissingArgs(tree.symbol.tpe, pt)))) - if (implicitly[Context].reportErrors) setError(tree) + setErrorOnLastTry(lastTry, tree) } - def AmbiguousExprAlternativeError(tree: Tree, pre: Type, best: Symbol, firstCompeting: Symbol, pt: Type) = { + def AmbiguousExprAlternativeError(tree: Tree, pre: Type, best: Symbol, firstCompeting: Symbol, pt: Type, lastTry: Boolean) = { val (pos, msg) = ambiguousErrorMsgPos(tree.pos, pre, best, firstCompeting, "expected type " + pt) - if (implicitly[Context].ambiguousErrors) setError(tree) + setErrorOnLastTry(lastTry, tree) issueAmbiguousTypeError(pre, best, firstCompeting, AmbiguousTypeError(tree, pos, msg)) } diff --git a/src/compiler/scala/tools/nsc/typechecker/Infer.scala b/src/compiler/scala/tools/nsc/typechecker/Infer.scala index ea0758e155..daddd64eb2 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Infer.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Infer.scala @@ -1448,12 +1448,10 @@ trait Infer { * If no alternative matches `pt`, take the parameterless one anyway. */ def inferExprAlternative(tree: Tree, pt: Type) = tree.tpe match { - case OverloadedType(pre, alts) => tryTwice { - val alts0 = alts filter (alt => isWeaklyCompatible(pre.memberType(alt), pt)) - // secondTry is not a sufficient condition to decide whether that was our final attempt. - // This is because it doesn't take into account tryTwice implicits search option. - val secondTry = alts0.isEmpty - val alts1 = if (secondTry) alts else alts0 + case OverloadedType(pre, alts) => tryTwice { isSecondTry => + val alts0 = alts filter (alt => isWeaklyCompatible(pre.memberType(alt), pt)) + val noAlternatives = alts0.isEmpty + val alts1 = if (noAlternatives) alts else alts0 //println("trying "+alts1+(alts1 map (_.tpe))+(alts1 map (_.locationString))+" for "+pt) def improves(sym1: Symbol, sym2: Symbol): Boolean = @@ -1481,10 +1479,10 @@ trait Infer { } } // todo: missing test case - NoBestExprAlternativeError(tree, pt) + NoBestExprAlternativeError(tree, pt, isSecondTry) } else if (!competing.isEmpty) { - if (secondTry) NoBestExprAlternativeError(tree, pt) - else if (!pt.isErroneous) AmbiguousExprAlternativeError(tree, pre, best, competing.head, pt) + if (noAlternatives) NoBestExprAlternativeError(tree, pt, isSecondTry) + else if (!pt.isErroneous) AmbiguousExprAlternativeError(tree, pre, best, competing.head, pt, isSecondTry) } else { // val applicable = alts1 filter (alt => // global.typer.infer.isWeaklyCompatible(pre.memberType(alt), pt)) @@ -1563,10 +1561,10 @@ trait Infer { * assignment expression. */ def inferMethodAlternative(tree: Tree, undetparams: List[Symbol], - argtpes: List[Type], pt0: Type, varArgsOnly: Boolean = false): Unit = tree.tpe match { + argtpes: List[Type], pt0: Type, varArgsOnly: Boolean = false, lastInferAttempt: Boolean = true): Unit = tree.tpe match { case OverloadedType(pre, alts) => val pt = if (pt0.typeSymbol == UnitClass) WildcardType else pt0 - tryTwice { + tryTwice { isSecondTry => debuglog("infer method alt "+ tree.symbol +" with alternatives "+ (alts map pre.memberType) +", argtpes = "+ argtpes +", pt = "+ pt) @@ -1588,13 +1586,10 @@ trait Infer { if (improves(alt, best)) alt else best) val competing = applicable.dropWhile(alt => best == alt || improves(best, alt)) if (best == NoSymbol) { - if (pt == WildcardType) NoBestMethodAlternativeError(tree, argtpes, pt) - else inferMethodAlternative(tree, undetparams, argtpes, WildcardType) + if (pt == WildcardType) NoBestMethodAlternativeError(tree, argtpes, pt, isSecondTry && lastInferAttempt) + else inferMethodAlternative(tree, undetparams, argtpes, WildcardType, lastInferAttempt = isSecondTry) } else if (!competing.isEmpty) { - if (!(argtpes exists (_.isErroneous)) && !pt.isErroneous) - AmbiguousMethodAlternativeError(tree, pre, best, competing.head, argtpes, pt) - else setError(tree) - () + AmbiguousMethodAlternativeError(tree, pre, best, competing.head, argtpes, pt, isSecondTry && lastInferAttempt) } else { // checkNotShadowed(tree.pos, pre, best, applicable) tree.setSymbol(best).setType(pre.memberType(best)) @@ -1608,29 +1603,28 @@ trait Infer { * * @param infer ... */ - def tryTwice(infer: => Unit): Unit = { + def tryTwice(infer: Boolean => Unit): Unit = { if (context.implicitsEnabled) { val saved = context.state var fallback = false context.setBufferErrors() - val res = try { - context.withImplicitsDisabled(infer) + try { + context.withImplicitsDisabled(infer(false)) if (context.hasErrors) { fallback = true context.restoreState(saved) context.flushBuffer() - infer + infer(true) } } catch { case ex: CyclicReference => throw ex case ex: TypeError => // recoverable cyclic references context.restoreState(saved) - if (!fallback) infer else () + if (!fallback) infer(true) else () } context.restoreState(saved) - res } - else infer + else infer(true) } /** Assign <code>tree</code> the type of all polymorphic alternatives |