Permalink
Browse files

SI-3761: Overload resolution fails on by-name parameter

When isAsSpecific checks if method m applies to args of types of
formal params of m1, a by-name parameter was converted to its
underlying result type for the params (of m) but not the args (of m1).
This had the useful effect of making m(A) more specific than m(=>A),
which is the specified prioritization for implicit views,
but also made m(=>A) and m(=>A, B*) ambiguous.  To handle this edge
case, the isCompatible test for A and =>A is made explicit, and
by-name params are no longer converted.
  • Loading branch information...
som-snytt committed May 21, 2012
1 parent f406550 commit f6a4d945698bac9b64a2d2ddaf44eb7302336670
@@ -305,9 +305,21 @@ trait Infer {
}
def isCompatible(tp: Type, pt: Type): Boolean = {
/** "Compatible" means conforming after conversions.
* "Raising to a thunk" is not implicit; therefore, for purposes of applicability and
* specificity, an arg type `A` is considered compatible with cbn formal parameter type `=>A`.
* For this behavior, the type `pt` must have cbn params preserved; for instance, `formalTypes(removeByName = false)`.
*
* `isAsSpecific` no longer prefers A by testing applicability to A for both m(A) and m(=>A)
* since that induces a tie between m(=>A) and m(=>A,B*) [SI-3761]
*/
private def isCompatible(tp: Type, pt: Type): Boolean = {
def isCompatibleByName(tp: Type, pt: Type): Boolean = pt match {
case TypeRef(_, ByNameParamClass, List(res)) if !isByNameParamType(tp) => isCompatible(tp, res)
case _ => false
}
val tp1 = normalize(tp)
(tp1 weak_<:< pt) || isCoercible(tp1, pt)
(tp1 weak_<:< pt) || isCoercible(tp1, pt) || isCompatibleByName(tp, pt)
}
def isCompatibleArgs(tps: List[Type], pts: List[Type]) =
(tps corresponds pts)(isCompatible)
@@ -662,7 +674,7 @@ trait Infer {
case ExistentialType(tparams, qtpe) =>
isApplicable(undetparams, qtpe, argtpes0, pt)
case MethodType(params, _) =>
val formals = formalTypes(params map { _.tpe }, argtpes0.length)
val formals = formalTypes(params map { _.tpe }, argtpes0.length, removeByName = false)
def tryTupleApply: Boolean = {
// if 1 formal, 1 argtpe (a tuple), otherwise unmodified argtpes0
@@ -682,7 +694,8 @@ trait Infer {
isCompatibleArgs(argtpes, formals) && isWeaklyCompatible(restpe, pt)
} else {
try {
val AdjustedTypeArgs.Undets(okparams, okargs, leftUndet) = methTypeArgs(undetparams, formals, restpe, argtpes, pt)
val blackTie = formalTypes(params map { _.tpe }, argtpes0.length)
val AdjustedTypeArgs.Undets(okparams, okargs, leftUndet) = methTypeArgs(undetparams, blackTie, restpe, argtpes, pt)
// #2665: must use weak conformance, not regular one (follow the monomorphic case above)
(exprTypeArgs(leftUndet, restpe.instantiateTypeParams(okparams, okargs), pt, useWeaklyCompatible = true)._1 ne null) &&
isWithinBounds(NoPrefix, NoSymbol, okparams, okargs)
@@ -0,0 +1,4 @@
hello!
hello working world
goodnight!
goodnight moon, nobody, noises everywhere
@@ -0,0 +1,18 @@
class OverTheTop {
def info0(m: String) = m + "!"
def info0(m: String, args: Any*) = m +" "+ args.mkString(" ")
// as reported
def info1(m: =>String) = m + "!"
def info1(m: =>String, args: Any*) = m +" "+ args.mkString(", ")
}
object Test {
def main(args: Array[String]) {
val top = new OverTheTop
println(top.info0("hello"))
println(top.info0("hello","working","world"))
println(top.info1("goodnight"))
println(top.info1("goodnight", "moon", "nobody", "noises everywhere"))
}
}

0 comments on commit f6a4d94

Please sign in to comment.