summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSom Snytt <som.snytt@gmail.com>2012-05-21 11:41:23 -0700
committerSom Snytt <som.snytt@gmail.com>2012-05-21 12:31:27 -0700
commitf6a4d945698bac9b64a2d2ddaf44eb7302336670 (patch)
tree746ddbb88e76085c95348bcb715490a6adb028ee
parentf406550146250f5a6036d3d778582efa6d68252a (diff)
downloadscala-f6a4d945698bac9b64a2d2ddaf44eb7302336670.tar.gz
scala-f6a4d945698bac9b64a2d2ddaf44eb7302336670.tar.bz2
scala-f6a4d945698bac9b64a2d2ddaf44eb7302336670.zip
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.
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Infer.scala21
-rw-r--r--test/files/run/t3761-overload-byname.check4
-rw-r--r--test/files/run/t3761-overload-byname.scala18
3 files changed, 39 insertions, 4 deletions
diff --git a/src/compiler/scala/tools/nsc/typechecker/Infer.scala b/src/compiler/scala/tools/nsc/typechecker/Infer.scala
index 1c1adee343..34b951a797 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Infer.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Infer.scala
@@ -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)
diff --git a/test/files/run/t3761-overload-byname.check b/test/files/run/t3761-overload-byname.check
new file mode 100644
index 0000000000..9410a4fe65
--- /dev/null
+++ b/test/files/run/t3761-overload-byname.check
@@ -0,0 +1,4 @@
+hello!
+hello working world
+goodnight!
+goodnight moon, nobody, noises everywhere
diff --git a/test/files/run/t3761-overload-byname.scala b/test/files/run/t3761-overload-byname.scala
new file mode 100644
index 0000000000..0e2c9b1059
--- /dev/null
+++ b/test/files/run/t3761-overload-byname.scala
@@ -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"))
+ }
+}