From ae1d7d9c55df373962a49468bcf0f2a957ae6fee Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Mon, 9 Dec 2019 17:18:20 +0100 Subject: [PATCH 1/3] Fix #7630: Use weak conformance in overloading resolution Following the spec, use weak conformance when determining whether an overloaded variant is applicable to arguments. --- .../dotty/tools/dotc/typer/Applications.scala | 3 ++- tests/run/i7630.check | 1 + tests/run/i7630.scala | 19 +++++++++++++++++++ 3 files changed, 22 insertions(+), 1 deletion(-) create mode 100644 tests/run/i7630.check create mode 100644 tests/run/i7630.scala diff --git a/compiler/src/dotty/tools/dotc/typer/Applications.scala b/compiler/src/dotty/tools/dotc/typer/Applications.scala index 2f04f7f17a8f..4c4ec903c090 100644 --- a/compiler/src/dotty/tools/dotc/typer/Applications.scala +++ b/compiler/src/dotty/tools/dotc/typer/Applications.scala @@ -666,7 +666,8 @@ trait Applications extends Compatibility { * argument trees. */ class ApplicableToTreesDirectly(methRef: TermRef, targs: List[Type], args: List[Tree], resultType: Type)(implicit ctx: Context) extends ApplicableToTrees(methRef, targs, args, resultType)(ctx) { - override def argOK(arg: TypedArg, formal: Type): Boolean = argType(arg, formal) <:< formal.widenExpr + override def argOK(arg: TypedArg, formal: Type): Boolean = + argType(arg, formal) relaxed_<:< formal.widenExpr } /** Subclass of Application for applicability tests with value argument types. */ diff --git a/tests/run/i7630.check b/tests/run/i7630.check new file mode 100644 index 000000000000..0cfbf08886fc --- /dev/null +++ b/tests/run/i7630.check @@ -0,0 +1 @@ +2 diff --git a/tests/run/i7630.scala b/tests/run/i7630.scala new file mode 100644 index 000000000000..8aad29cdbde8 --- /dev/null +++ b/tests/run/i7630.scala @@ -0,0 +1,19 @@ +object Asserts { + def assertEquals(expected: Any, actual: Any): Unit = { + println(1) + assert(expected.equals(actual), s"expected $expected but got $actual") + } + + def assertEquals(expected: Long, actual: Long): Unit = { + println(2) + assert(expected == actual, s"expected $expected but got $actual") + } +} + +object Test { + def main(args: Array[String]): Unit = { + def foo(): Long = 42L + + Asserts.assertEquals(42, foo()) // an Int and a Long + } +} \ No newline at end of file From 9fe541ef92831109a751b54e5c7ad3f21c102243 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Mon, 9 Dec 2019 18:22:47 +0100 Subject: [PATCH 2/3] Fix tests --- tests/neg/harmonize.scala | 12 ++++++------ tests/run/weak-conformance.scala | 14 ++++++++------ 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/tests/neg/harmonize.scala b/tests/neg/harmonize.scala index 236308fceaef..5bdea2894d41 100644 --- a/tests/neg/harmonize.scala +++ b/tests/neg/harmonize.scala @@ -53,17 +53,17 @@ object Test { inline val b = 33 def f(): Int = b + 1 val a1 = Array(b, 33, 'a') - val b1: Array[Int] = a1 // error: no widening + val b1: Array[Int] = a1 // OK, Array constructor selection uses weak conformance val a2 = Array(b, 33, 'a', f()) - val b2: Array[Int] = a2 // error: no widening + val b2: Array[Int] = a2 // OK, Array constructor selection uses weak conformance val a3 = Array(1.0f, 'a', 0) - val b3: Array[Float] = a3 // error: no widening + val b3: Array[Float] = a3 // OK, Array constructor selection uses weak conformance val a4 = Array(1.0f, 1L) - val b4: Array[Double] = a4 // error: no widening + val b4: Array[Double] = a4 // error: Array[Float] is picked val a5 = Array(1.0f, 1L, f()) - val b5: Array[AnyVal] = a5 + val b5: Array[AnyVal] = a5 // error: Array[Float] is picked val a6 = Array(1.0f, 1234567890) - val b6: Array[AnyVal] = a6 + val b6: Array[AnyVal] = a6 // error: Array[Float] is picked def totalDuration(results: List[Long], cond: Boolean): Long = results.map(r => if (cond) r else 0).sum diff --git a/tests/run/weak-conformance.scala b/tests/run/weak-conformance.scala index 5e7350bf3044..a02294d1a2fe 100644 --- a/tests/run/weak-conformance.scala +++ b/tests/run/weak-conformance.scala @@ -21,15 +21,17 @@ object Test extends App { } locally { + // Arrays behave differently from lists since they have overloaded constructors, and weak + // conformance does apply for selecting one. See Issue #7630. def f(): Int = b + 1 - val x1 = Array(b, 33, 5.5) ; x1: Array[Double] // b is an inline val - val x2 = Array(f(), 33, 5.5) ; x2: Array[AnyVal] // f() is not a constant + val x1 = Array(b, 33, 5.5) ; x1: Array[Double] + val x2 = Array(f(), 33, 5.5) ; x2: Array[Double] val x3 = Array(5, 11L) ; x3: Array[Long] - val x4 = Array(5, 11L, 5.5) ; x4: Array[AnyVal] // Long and Double found + val x4 = Array(5, 11L, 5.5) ; x4: Array[Double] val x5 = Array(1.0f, 2) ; x5: Array[Float] - val x6 = Array(1.0f, 1234567890); x6: Array[AnyVal] // loss of precision - val x7 = Array(b, 33, 'a') ; x7: Array[Char] - val x8 = Array(5.toByte, 11) ; x8: Array[Byte] + val x6 = Array(1.0f, 1234567890); x6: Array[Float] + val x7 = Array(b, 33, 'a') ; x7: Array[Int] + val x8 = Array(5.toByte, 11) ; x8: Array[Int] val x9: Array[AnyVal] = Array(1.0f, 0) assert(x9(0).getClass == classOf[java.lang.Float]) From 1b5049374f6998cac6a6c70fa324785179b7bf40 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 10 Dec 2019 22:14:30 +0100 Subject: [PATCH 3/3] Bring back tests for nonvariant collections --- tests/neg/harmonize.scala | 46 ++++++++++++++++++++++---------- tests/run/weak-conformance.scala | 20 ++++++++++++++ 2 files changed, 52 insertions(+), 14 deletions(-) diff --git a/tests/neg/harmonize.scala b/tests/neg/harmonize.scala index 5bdea2894d41..0fe03d2d7600 100644 --- a/tests/neg/harmonize.scala +++ b/tests/neg/harmonize.scala @@ -1,3 +1,4 @@ +import collection.mutable.ArrayBuffer object Test { def main(args: Array[String]) = { @@ -50,20 +51,37 @@ object Test { } - inline val b = 33 - def f(): Int = b + 1 - val a1 = Array(b, 33, 'a') - val b1: Array[Int] = a1 // OK, Array constructor selection uses weak conformance - val a2 = Array(b, 33, 'a', f()) - val b2: Array[Int] = a2 // OK, Array constructor selection uses weak conformance - val a3 = Array(1.0f, 'a', 0) - val b3: Array[Float] = a3 // OK, Array constructor selection uses weak conformance - val a4 = Array(1.0f, 1L) - val b4: Array[Double] = a4 // error: Array[Float] is picked - val a5 = Array(1.0f, 1L, f()) - val b5: Array[AnyVal] = a5 // error: Array[Float] is picked - val a6 = Array(1.0f, 1234567890) - val b6: Array[AnyVal] = a6 // error: Array[Float] is picked + def arraytest = + inline val b = 33 + def f(): Int = b + 1 + val a1 = Array(b, 33, 'a') + val b1: Array[Int] = a1 // OK, Array constructor selection uses weak conformance + val a2 = Array(b, 33, 'a', f()) + val b2: Array[Int] = a2 // OK, Array constructor selection uses weak conformance + val a3 = Array(1.0f, 'a', 0) + val b3: Array[Float] = a3 // OK, Array constructor selection uses weak conformance + val a4 = Array(1.0f, 1L) + val b4: Array[Double] = a4 // error: Array[Float] is picked + val a5 = Array(1.0f, 1L, f()) + val b5: Array[AnyVal] = a5 // error: Array[Float] is picked + val a6 = Array(1.0f, 1234567890) + val b6: Array[AnyVal] = a6 // error: Array[Float] is picked + + def arrayBufferTest = + inline val b = 33 + def f(): Int = b + 1 + val a1 = ArrayBuffer(b, 33, 'a') + val b1: ArrayBuffer[Int] = a1 // error: no widening + val a2 = ArrayBuffer(b, 33, 'a', f()) + val b2: ArrayBuffer[Int] = a2 // error: no widening + val a3 = ArrayBuffer(1.0f, 'a', 0) + val b3: ArrayBuffer[Float] = a3 // error: no widening + val a4 = ArrayBuffer(1.0f, 1L) + val b4: ArrayBuffer[Double] = a4 // error: no widening + val a5 = ArrayBuffer(1.0f, 1L, f()) + val b5: ArrayBuffer[AnyVal] = a5 + val a6 = ArrayBuffer(1.0f, 1234567890) + val b6: ArrayBuffer[AnyVal] = a6 def totalDuration(results: List[Long], cond: Boolean): Long = results.map(r => if (cond) r else 0).sum diff --git a/tests/run/weak-conformance.scala b/tests/run/weak-conformance.scala index a02294d1a2fe..a9025b4a58cb 100644 --- a/tests/run/weak-conformance.scala +++ b/tests/run/weak-conformance.scala @@ -1,3 +1,4 @@ +import collection.mutable.ArrayBuffer object Test extends App { inline val b = 33 @@ -20,6 +21,25 @@ object Test extends App { assert(x10(1).getClass == classOf[java.lang.Integer]) } + locally { + def f(): Int = b + 1 + val x1 = ArrayBuffer(b, 33, 5.5) ; x1: ArrayBuffer[Double] // b is an inline val + val x2 = ArrayBuffer(f(), 33, 5.5) ; x2: ArrayBuffer[AnyVal] // f() is not a constant + val x3 = ArrayBuffer(5, 11L) ; x3: ArrayBuffer[Long] + val x4 = ArrayBuffer(5, 11L, 5.5) ; x4: ArrayBuffer[AnyVal] // Long and Double found + val x5 = ArrayBuffer(1.0f, 2) ; x5: ArrayBuffer[Float] + val x6 = ArrayBuffer(1.0f, 1234567890); x6: ArrayBuffer[AnyVal] // loss of precision + val x7 = ArrayBuffer(b, 33, 'a') ; x7: ArrayBuffer[Char] + val x8 = ArrayBuffer(5.toByte, 11) ; x8: ArrayBuffer[Byte] + + val x9: ArrayBuffer[AnyVal] = ArrayBuffer(1.0f, 0) + assert(x9(0).getClass == classOf[java.lang.Float]) + assert(x9(1).getClass == classOf[java.lang.Integer]) // expected type fully defined since ArrayBuffer is nonvariant + val x10 = ArrayBuffer[Any](1.0f, 0) + assert(x10(0).getClass == classOf[java.lang.Float]) + assert(x10(1).getClass == classOf[java.lang.Integer]) + } + locally { // Arrays behave differently from lists since they have overloaded constructors, and weak // conformance does apply for selecting one. See Issue #7630.