Skip to content

Commit f67a1a0

Browse files
authored
Fix varargs overload resolution with wildcard types (#24669)
Fixes #24072 When comparing overloaded methods where one is non-varargs with wildcard types (e.g., `Class[? <: T]`) and another is varargs, the non-varargs method should be preferred. Previously, the compiler failed to distinguish these methods , and results in an ambiguity error. ```scala def blub[T](a: Class[? <: T]): Unit // m1 def blub[T](a: Class[T], ints: Int*): Unit // m2 blub(classOf[Object]) // m1 should be picked, but fails to resolve ```` The problem is `compare(m1, m2)` returned 0 because: - (1). `m2` (varargs) is correctly considered "not as good" as `m1`. - (2). `m1` (non-varargs) was also considered "not as good" as `m2`. (but `m1` should be as good as `m2`! because `Class[Concrete]` can be applied to both m1 and m2). The (2) occurred because `Class[? <: T]` is not a subtype of `Class[T]` (due to invariance). Consequently, `isApplicableMethodRef(m2, Class[? <: T])` returned `false` because `isCompatible(Class[? <: T], Class[T])` returned `false` during the applicability check against the method. This commit adds special handling in `TestApplication.argOK` to check if wildcard upper bounds are compatible with their formal types during overload resolution, in addition to `isCompatible`.
2 parents 96be092 + c538739 commit f67a1a0

File tree

4 files changed

+77
-2
lines changed

4 files changed

+77
-2
lines changed

compiler/src/dotty/tools/dotc/typer/Applications.scala

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -883,7 +883,29 @@ trait Applications extends Compatibility {
883883
case SAMType(samMeth, samParent) => argtpe <:< samMeth.toFunctionType(isJava = samParent.classSymbol.is(JavaDefined))
884884
case _ => false
885885

886-
isCompatible(argtpe, formal)
886+
// For overload resolution, allow wildcard upper bounds to match their bound type.
887+
// For example, given:
888+
// def blub[T](a: Class[? <: T]): String = "a"
889+
// def blub[T](a: Class[T], ints: Int*): String = "b"
890+
// blub(classOf[Object])
891+
//
892+
// The non-varargs overload should be preferred. While Class[? <: T] is not a
893+
// subtype of Class[T] (Class is invariant), for overload resolution we consider
894+
// Class[? <: T] "applicable" where Class[T] is expected by checking if the
895+
// wildcard's upper bound is a subtype of the formal type parameter.
896+
def wildcardArgOK =
897+
argtpe match
898+
case at @ AppliedType(tycon1, args1) if at.hasWildcardArg =>
899+
formal match
900+
case AppliedType(tycon2, args2)
901+
if tycon1 =:= tycon2 && args1.length == args2.length =>
902+
args1.lazyZip(args2).forall {
903+
case (TypeBounds(_, hi), formal) => hi relaxed_<:< formal
904+
}
905+
case _ => false
906+
case _ => false
907+
908+
isCompatible(argtpe, formal) || wildcardArgOK
887909
// Only allow SAM-conversion to PartialFunction if implicit conversions
888910
// are enabled. This is necessary to avoid ambiguity between an overload
889911
// taking a PartialFunction and one taking a Function1 because

tests/neg/overload_repeated.scala

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,11 @@ object Test {
22
def bar1(x: Any) = 1
33
def bar1(x: String*) = 2
44

5+
// (b) isn't as good as (a) because of ints
6+
// (a) isn't as good as (b) because T in Class[? <: T] may not subtype of Number
7+
def bar2[T](a: Class[? <: T]): Int = 1 // (a)
8+
def bar2[T <: Number](a: Class[T], ints: Int*): Int = 2 // (b)
9+
510
assert(bar1("") == 1) // error: ambiguous in Scala 2 and Scala 3
11+
assert(bar2(classOf[Integer]) == 1) // error: ambiguous in Scala 2 and Scala 3
612
}

tests/run/overload_repeated/A_1.java

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,34 @@ class A_1 {
1111
public static <T> int foo4(T x) { return 1; }
1212
public static <T> int foo4(T x, T... y) { return 2; }
1313

14+
// https://github.com/scala/scala3/issues/24072
15+
public static <T> int foo5(Class<? extends T> a) { return 1; }
16+
public static <T> int foo5(Class<T> a, int... ints) { return 2; }
17+
18+
public static <T> int foo6(Class<T> a, int... ints) { return 1; }
19+
public static <T> int foo6(int a) { return 2; }
20+
21+
public static <T extends Number> int foo7(Class<? extends T> a) { return 1; }
22+
public static <T extends Number> int foo7(Class<T> a, int... ints) { return 2; }
23+
24+
public static <T extends Number> int foo8(Class<? extends T> a) { return 1; } // (a)
25+
public static <T> int foo8(Class<T> a, int... ints) { return 2; } // (b)
26+
27+
public static <T> int foo9(Class<? extends T> a) { return 1; } // (a)
28+
public static <T extends Number> int foo9(Class<T> a, int... ints) { return 2; } // (b)
29+
1430
public static boolean check() {
1531
// Java prefers non-varargs to varargs:
1632
// https://docs.oracle.com/javase/specs/jls/se8/html/jls-15.html#jls-15.12.2
1733
return
1834
foo1("") == 1 &&
1935
foo2("") == 1 &&
2036
foo3("") == 1 &&
21-
foo4("") == 1;
37+
foo4("") == 1 &&
38+
foo5(Object.class) == 1 &&
39+
foo6(Object.class) == 1 &&
40+
foo7(Integer.class) == 1 &&
41+
foo8(Integer.class) == 1 &&
42+
foo9(Integer.class) == 1;
2243
}
2344
}

tests/run/overload_repeated/B_2.scala

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,22 @@ object Test {
1313
def bar4[T](x: T): Int = 1
1414
def bar4[T](x: T, xs: T*): Int = 2
1515

16+
// https://github.com/scala/scala3/issues/24072
17+
def bar5[T](a: Class[? <: T]): Int = 1
18+
def bar5[T](a: Class[T], ints: Int*): Int = 2
19+
20+
def bar6[T](a: Class[T], ints: Int*): Int = 1
21+
def bar6[T](a: Int): Int = 2
22+
23+
def bar7[T <: Number](a: Class[? <: T]): Int = 1
24+
def bar7[T <: Number](a: Class[T], ints: Int*): Int = 2
25+
26+
def bar8[T <: Number](a: Class[? <: T]): Int = 1 // (a)
27+
def bar8[T](a: Class[T], ints: Int*): Int = 2 // (b)
28+
29+
def bar9[T](a: Class[? <: T]): Int = 1 // (a)
30+
def bar9[T <: Number](a: Class[T], ints: Int*): Int = 2 // (b)
31+
1632
def main(args: Array[String]): Unit = {
1733
// In Java, varargs are always less specific than non-varargs (see
1834
// https://docs.oracle.com/javase/specs/jls/se8/html/jls-15.html#jls-15.12.2),
@@ -22,11 +38,21 @@ object Test {
2238
assert(A_1.foo2("") == 1) // Same as in Java and Scala 2
2339
assert(A_1.foo3("") == 1) // Same as in Java and Scala 2
2440
assert(A_1.foo4("") == 1) // Same as in Java and Scala 2
41+
assert(A_1.foo5(classOf[Object]) == 1) // Same as in Java and Scala 2
42+
assert(A_1.foo6(classOf[Object]) == 1) // Same as in Java and Scala 2
43+
assert(A_1.foo7(classOf[Integer]) == 1) // Same as in Java and Scala 2
44+
assert(A_1.foo8(classOf[Integer]) == 1) // Same as in Java and Scala 2
45+
// assert(A_1.foo9(classOf[Integer]) == 1) // Works in Java, ambiguous in Scala 2 and 3
2546

2647
// Same with Scala varargs:
2748
// assert(bar1("") == 1) // Works in Java, ambiguous in Scala 2 and Dotty
2849
assert(bar2("") == 1) // same in Scala 2
2950
assert(bar3("") == 1) // same in Scala 2
3051
assert(bar4("") == 1) // same in Scala 2
52+
assert(bar5(classOf[Object]) == 1) // same in Scala2
53+
assert(bar6(classOf[Object]) == 1) // same in Scala2
54+
assert(bar7(classOf[Integer]) == 1) // same in Scala2
55+
assert(bar8(classOf[Integer]) == 1) // same in Scala2
56+
// assert(bar9(classOf[Integer]) == 1) Works in Java, ambiguous in Scala 2 and 3
3157
}
3258
}

0 commit comments

Comments
 (0)