diff options
-rw-r--r-- | src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala | 38 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/typechecker/Infer.scala | 40 | ||||
-rw-r--r-- | test/files/neg/t5735.check | 6 | ||||
-rw-r--r-- | test/files/neg/t5735.scala | 7 |
4 files changed, 55 insertions, 36 deletions
diff --git a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala index eb3a1ffb5b..affa9cd63b 100644 --- a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala +++ b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala @@ -690,34 +690,44 @@ 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 + @inline + 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) } 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) + issueAmbiguousTypeError(pre, best, firstCompeting, AmbiguousTypeError(tree, pos, msg)) + setErrorOnLastTry(lastTry, tree) + } 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)))) + 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) - setError(tree) issueAmbiguousTypeError(pre, best, firstCompeting, AmbiguousTypeError(tree, pos, msg)) + setErrorOnLastTry(lastTry, tree) } // checkBounds diff --git a/src/compiler/scala/tools/nsc/typechecker/Infer.scala b/src/compiler/scala/tools/nsc/typechecker/Infer.scala index 1c1adee343..839cdee301 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Infer.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Infer.scala @@ -1449,10 +1449,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)) - 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 = @@ -1480,10 +1480,10 @@ trait Infer { } } // todo: missing test case - NoBestExprAlternativeError(tree, pt) + NoBestExprAlternativeError(tree, pt, isSecondTry) } else if (!competing.isEmpty) { - if (secondTry) { NoBestExprAlternativeError(tree, pt); setError(tree) } - 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)) @@ -1562,10 +1562,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) @@ -1587,13 +1587,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)) @@ -1607,29 +1604,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 diff --git a/test/files/neg/t5735.check b/test/files/neg/t5735.check new file mode 100644 index 0000000000..f6e0028044 --- /dev/null +++ b/test/files/neg/t5735.check @@ -0,0 +1,6 @@ +t5735.scala:6: error: type mismatch; + found : (x: Int)Int <and> => String + required: Int + val z: Int = a + ^ +one error found diff --git a/test/files/neg/t5735.scala b/test/files/neg/t5735.scala new file mode 100644 index 0000000000..fde71ff962 --- /dev/null +++ b/test/files/neg/t5735.scala @@ -0,0 +1,7 @@ +abstract class Base { + def a: String = "one" +} +class Clazz extends Base { + def a(x: Int): Int = 2 + val z: Int = a +} |