diff --git a/compiler/src/dotty/tools/dotc/typer/Applications.scala b/compiler/src/dotty/tools/dotc/typer/Applications.scala index efa047df787e..97f8ad98c417 100644 --- a/compiler/src/dotty/tools/dotc/typer/Applications.scala +++ b/compiler/src/dotty/tools/dotc/typer/Applications.scala @@ -671,6 +671,21 @@ trait Applications extends Compatibility { self: Typer with Dynamic => def harmonizeArgs(args: List[Type]): List[Type] = harmonizeTypes(args) } + /** Special-case of ApplicableToTypes for checking overload specificity. */ + class IsAsSpecific(methRef: TermRef, args: List[Type], resultType: Type)(implicit ctx: Context) + extends ApplicableToTypes(methRef, args, resultType) { + override def argOK(arg: TypedArg, formal: Type): Boolean = { + // Simulate RepeatedClass[X] not being in subtyping relationship with any other type. + // This is necessary in cases when the last parameter of one method is repeated, + // and another is, for instance, Any - see neg/overload_repeated.scala. + // Note: + // 1) isSubType cannot special-case RepeatedClass[X] - it'd interfere with typing method bodies. + // 2) arg and formal cannot both be repeated - if both methods have varargs, we strip them in isAsSpecific + !(arg.isRepeatedParam || formal.isRepeatedParam) && super.argOK(arg, formal) + } + } + + /** Subclass of Application for type checking an Apply node, where * types of arguments are either known or unknown. */ @@ -1293,7 +1308,8 @@ trait Applications extends Compatibility { self: Typer with Dynamic => val formals1 = if (tp1.isVarArgsMethod && tp2.isVarArgsMethod) tp1.paramInfos.map(_.repeatedToSingle) else tp1.paramInfos - isApplicable(alt2, formals1, WildcardType) || + + ctx.test(ctx => new IsAsSpecific(alt2, formals1, WildcardType)(ctx).success) || tp1.paramInfos.isEmpty && tp2.isInstanceOf[LambdaType] case tp1: PolyType => // (2) val nestedCtx = ctx.fresh.setExploreTyperState() diff --git a/tests/neg/overload_repeated.scala b/tests/neg/overload_repeated.scala new file mode 100644 index 000000000000..a69276092149 --- /dev/null +++ b/tests/neg/overload_repeated.scala @@ -0,0 +1,5 @@ +object Test { + def foo(a: Any) = a + def foo(s: String*) = s + foo("") // error +} diff --git a/tests/neg/t4775/JavaClass.java b/tests/neg/t4775/JavaClass.java new file mode 100644 index 000000000000..bef2a1ccb170 --- /dev/null +++ b/tests/neg/t4775/JavaClass.java @@ -0,0 +1,25 @@ +public class JavaClass { + public static class Element { + + } + + public static int foo(Element a, Class b, boolean c, Class... d) { + return 1; + } + + public static int foo(Element a, Class b, boolean c) { + return 2; + } + + public static int foo(Element a, Class... b) { + return 3; + } + + public static int foo(Element a, boolean b, Class... c) { + return 4; + } + + static { + foo(new Element(), Element.class, false); + } +} diff --git a/tests/neg/t4775/Test.scala b/tests/neg/t4775/Test.scala new file mode 100644 index 000000000000..c9c5c88ebd58 --- /dev/null +++ b/tests/neg/t4775/Test.scala @@ -0,0 +1,24 @@ +class Test { + import JavaClass._ + // in Scala2, this is not actually an error (see https://github.com/scala/bug/issues/4775) + // overloading selection needs to decide between: + // + // def foo[T <: Element](a: Element, b: Class[T], c: Boolean, d: Class[_ <: T]*) + // def foo[T <: Element](a: Element, b: Class[_ <: T], c: Boolean) + // + // The first is not as specific as the second, since it has more arguments. + // The second _should_ be as specific as the first, + // but the T parameter would need to capture the wildcard of b. + // Since we don't allow wildcard capture anymore, + // we cannot infer anything for T + // and just say the second is not as specific as the first either. + // + // As a note to whomever will be revisiting this in the future, + // we can select the same overload as Scala2 by selecting the one + // without vararg parameter when neither is more specific. + // The argument is that since one overload has varargs and the other doesn't, + // the callsite must be trying to pass no varargs, + // so we should select the overload that doesn't actually have any. + // This breaks no tests, but potentially allows too much. + foo(new Element, classOf[Element], false) // error +} diff --git a/tests/pos/overload_poly_repeated.scala b/tests/pos/overload_poly_repeated.scala new file mode 100644 index 000000000000..ee977920c972 --- /dev/null +++ b/tests/pos/overload_poly_repeated.scala @@ -0,0 +1,14 @@ +class C { + def foo(xs: Int*): Int = xs.toSeq.head + def foo(x: Int): Int = x + foo(2) + + def fooT[T](xs: T*): T = xs.toSeq.head + def fooT[T](x: T): T = x + fooT(2) + + def f[T](x: T): T = x + def f[T](x: T, xs: T*): T = x + + f(5) +}