diff --git a/compiler/src/dotty/tools/dotc/typer/Applications.scala b/compiler/src/dotty/tools/dotc/typer/Applications.scala index b8dbfc3786fe..6b5f5e44829f 100644 --- a/compiler/src/dotty/tools/dotc/typer/Applications.scala +++ b/compiler/src/dotty/tools/dotc/typer/Applications.scala @@ -883,7 +883,27 @@ trait Applications extends Compatibility { case SAMType(samMeth, samParent) => argtpe <:< samMeth.toFunctionType(isJava = samParent.classSymbol.is(JavaDefined)) case _ => false - isCompatible(argtpe, formal) + // For overload resolution, allow wildcard upper bounds to match their bound type. + // For example, given: + // def blub[T](a: Class[? <: T]): String = "a" + // def blub[T](a: Class[T], ints: Int*): String = "b" + // blub(classOf[Object]) + // + // The non-varargs overload should be preferred. While Class[? <: T] is not a + // subtype of Class[T] (Class is invariant), for overload resolution we consider + // Class[? <: T] "applicable" where Class[T] is expected by checking if the + // wildcard's upper bound is a subtype of the formal type parameter. + def wildcardArgOK = + (argtpe, formal) match + case (AppliedType(tycon1, args1), AppliedType(tycon2, args2)) + if tycon1 =:= tycon2 && args1.length == args2.length => + args1.lazyZip(args2).forall { + case (TypeBounds(_, hi), formal) => hi relaxed_<:< formal + case (arg, formal) => arg =:= formal + } + case _ => false + + isCompatible(argtpe, formal) || wildcardArgOK // Only allow SAM-conversion to PartialFunction if implicit conversions // are enabled. This is necessary to avoid ambiguity between an overload // taking a PartialFunction and one taking a Function1 because diff --git a/tests/neg/overload_repeated.scala b/tests/neg/overload_repeated.scala index 8e2b34bc411f..6d6fca2c84a1 100644 --- a/tests/neg/overload_repeated.scala +++ b/tests/neg/overload_repeated.scala @@ -2,5 +2,11 @@ object Test { def bar1(x: Any) = 1 def bar1(x: String*) = 2 + // (b) isn't as good as (a) because of ints + // (a) isn't as good as (b) because T in Class[? <: T] may not subtype of Number + def bar2[T](a: Class[? <: T]): Int = 1 // (a) + def bar2[T <: Number](a: Class[T], ints: Int*): Int = 2 // (b) + assert(bar1("") == 1) // error: ambiguous in Scala 2 and Scala 3 + assert(bar2(classOf[Integer]) == 1) // error: ambiguous in Scala 2 and Scala 3 } diff --git a/tests/run/overload_repeated/A_1.java b/tests/run/overload_repeated/A_1.java index 8ef89f1aaf3f..2b59040f6d69 100644 --- a/tests/run/overload_repeated/A_1.java +++ b/tests/run/overload_repeated/A_1.java @@ -11,6 +11,22 @@ class A_1 { public static int foo4(T x) { return 1; } public static int foo4(T x, T... y) { return 2; } + // https://github.com/scala/scala3/issues/24072 + public static int foo5(Class a) { return 1; } + public static int foo5(Class a, int... ints) { return 2; } + + public static int foo6(Class a, int... ints) { return 1; } + public static int foo6(int a) { return 2; } + + public static int foo7(Class a) { return 1; } + public static int foo7(Class a, int... ints) { return 2; } + + public static int foo8(Class a) { return 1; } // (a) + public static int foo8(Class a, int... ints) { return 2; } // (b) + + public static int foo9(Class a) { return 1; } // (a) + public static int foo9(Class a, int... ints) { return 2; } // (b) + public static boolean check() { // Java prefers non-varargs to varargs: // https://docs.oracle.com/javase/specs/jls/se8/html/jls-15.html#jls-15.12.2 @@ -18,6 +34,11 @@ public static boolean check() { foo1("") == 1 && foo2("") == 1 && foo3("") == 1 && - foo4("") == 1; + foo4("") == 1 && + foo5(Object.class) == 1 && + foo6(Object.class) == 1 && + foo7(Integer.class) == 1 && + foo8(Integer.class) == 1 && + foo9(Integer.class) == 1; } } diff --git a/tests/run/overload_repeated/B_2.scala b/tests/run/overload_repeated/B_2.scala index 89452a2ed92d..d8eda3d26f92 100644 --- a/tests/run/overload_repeated/B_2.scala +++ b/tests/run/overload_repeated/B_2.scala @@ -13,6 +13,22 @@ object Test { def bar4[T](x: T): Int = 1 def bar4[T](x: T, xs: T*): Int = 2 + // https://github.com/scala/scala3/issues/24072 + def bar5[T](a: Class[? <: T]): Int = 1 + def bar5[T](a: Class[T], ints: Int*): Int = 2 + + def bar6[T](a: Class[T], ints: Int*): Int = 1 + def bar6[T](a: Int): Int = 2 + + def bar7[T <: Number](a: Class[? <: T]): Int = 1 + def bar7[T <: Number](a: Class[T], ints: Int*): Int = 2 + + def bar8[T <: Number](a: Class[? <: T]): Int = 1 // (a) + def bar8[T](a: Class[T], ints: Int*): Int = 2 // (b) + + def bar9[T](a: Class[? <: T]): Int = 1 // (a) + def bar9[T <: Number](a: Class[T], ints: Int*): Int = 2 // (b) + def main(args: Array[String]): Unit = { // In Java, varargs are always less specific than non-varargs (see // https://docs.oracle.com/javase/specs/jls/se8/html/jls-15.html#jls-15.12.2), @@ -22,11 +38,21 @@ object Test { assert(A_1.foo2("") == 1) // Same as in Java and Scala 2 assert(A_1.foo3("") == 1) // Same as in Java and Scala 2 assert(A_1.foo4("") == 1) // Same as in Java and Scala 2 + assert(A_1.foo5(classOf[Object]) == 1) // Same as in Java and Scala 2 + assert(A_1.foo6(classOf[Object]) == 1) // Same as in Java and Scala 2 + assert(A_1.foo7(classOf[Integer]) == 1) // Same as in Java and Scala 2 + assert(A_1.foo8(classOf[Integer]) == 1) // Same as in Java and Scala 2 + // assert(A_1.foo9(classOf[Integer]) == 1) // Works in Java, ambiguous in Scala 2 and 3 // Same with Scala varargs: // assert(bar1("") == 1) // Works in Java, ambiguous in Scala 2 and Dotty assert(bar2("") == 1) // same in Scala 2 assert(bar3("") == 1) // same in Scala 2 assert(bar4("") == 1) // same in Scala 2 + assert(bar5(classOf[Object]) == 1) // same in Scala2 + assert(bar6(classOf[Object]) == 1) // same in Scala2 + assert(bar7(classOf[Integer]) == 1) // same in Scala2 + assert(bar8(classOf[Integer]) == 1) // same in Scala2 + // assert(bar9(classOf[Integer]) == 1) Works in Java, ambiguous in Scala 2 and 3 } }