Skip to content

Commit 0407d6a

Browse files
committed
Downwards comparisons for implicit search and overloading resolution
This is a backport of the following Dotty change to scalac, scala/scala3@8954026 As in the Dotty implementation the specificity comparison which is used for overload resolution and implicit selection is now performed as-if all contravariant positions in top level type constructors were covariant. Currently this breaks test/files/run/t2030.scala in exactly the same way as in Dotty as reported here, scala/scala3#1246 (comment) and in this PR it has been changed to a neg test. However, test/files/run/t2030.scala passes when this PR is combined with, scala#6139 so if/when that is merged it can be switched back to a pos test. Fixes scala/bug#2509. Fixes scala/bug#7768.
1 parent 8741bd9 commit 0407d6a

File tree

18 files changed

+416
-1
lines changed

18 files changed

+416
-1
lines changed

src/compiler/scala/tools/nsc/typechecker/Infer.scala

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -837,13 +837,33 @@ trait Infer extends Checkable {
837837
case _ => onRight
838838
}
839839
}
840+
840841
private def isAsSpecificValueType(tpe1: Type, tpe2: Type, undef1: List[Symbol], undef2: List[Symbol]): Boolean = tpe1 match {
841842
case PolyType(tparams1, rtpe1) =>
842843
isAsSpecificValueType(rtpe1, tpe2, undef1 ::: tparams1, undef2)
843844
case _ =>
844845
tpe2 match {
845846
case PolyType(tparams2, rtpe2) => isAsSpecificValueType(tpe1, rtpe2, undef1, undef2 ::: tparams2)
846-
case _ => existentialAbstraction(undef1, tpe1) <:< existentialAbstraction(undef2, tpe2)
847+
case _ =>
848+
849+
val e1 = existentialAbstraction(undef1, tpe1)
850+
val e2 = existentialAbstraction(undef2, tpe2)
851+
852+
// Backport of fix for https://github.com/scala/bug/issues/2509
853+
// from Dotty https://github.com/lampepfl/dotty/commit/89540268e6c49fb92b9ca61249e46bb59981bf5a
854+
val flip = new TypeMap(trackVariance = true) {
855+
def apply(tp: Type): Type = tp match {
856+
case TypeRef(pre, sym, args) if variance > 0 && sym.typeParams.exists(_.isContravariant) =>
857+
TypeRef(pre, sym.flipped, args)
858+
case _: TypeRef => tp
859+
case _ =>
860+
mapOver(tp)
861+
}
862+
}
863+
864+
val bt = e1.baseType(e2.typeSymbol)
865+
val lhs = if(bt != NoType) bt else e1
866+
flip(lhs) <:< flip(e2)
847867
}
848868
}
849869

src/reflect/scala/reflect/internal/Symbols.scala

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2003,6 +2003,8 @@ trait Symbols extends api.Symbols { self: SymbolTable =>
20032003
/** Internal method to clone a symbol's implementation with the given flags and no info. */
20042004
def cloneSymbolImpl(owner: Symbol, newFlags: Long): TypeOfClonedSymbol
20052005

2006+
def flipped: Symbol = this
2007+
20062008
// ------ access to related symbols --------------------------------------------------
20072009

20082010
/** The next enclosing class. */
@@ -3369,6 +3371,14 @@ trait Symbols extends api.Symbols { self: SymbolTable =>
33693371
clone
33703372
}
33713373

3374+
override lazy val flipped: ClassSymbol = {
3375+
val clone = cloneSymbol(owner)
3376+
clone.rawInfo.typeParams.foreach { sym =>
3377+
if (sym.isContravariant) sym.resetFlag(Flag.CONTRAVARIANT).setFlag(Flag.COVARIANT)
3378+
}
3379+
clone
3380+
}
3381+
33723382
override def derivedValueClassUnbox =
33733383
// (info.decl(nme.unbox)) orElse uncomment once we accept unbox methods
33743384
(info.decls.find(_ hasAllFlags PARAMACCESSOR | METHOD) getOrElse

test/files/neg/t2030.check

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
t2030.scala:5: error: ambiguous implicit values:
2+
both method newCanBuildFrom in object SortedSet of type [A](implicit ord: Ordering[A])scala.collection.generic.CanBuildFrom[scala.collection.immutable.SortedSet.Coll,A,scala.collection.immutable.SortedSet[A]]
3+
and method newCanBuildFrom in class SortedSetFactory of type [A](implicit ord: Ordering[A])scala.collection.generic.CanBuildFrom[scala.collection.immutable.TreeSet.Coll,A,scala.collection.immutable.TreeSet[A]]
4+
match expected type scala.collection.generic.CanBuildFrom[scala.collection.immutable.TreeSet[Int],Int,That]
5+
val res1 = res0.map(x => x)
6+
^
7+
one error found
File renamed without changes.

test/files/neg/t2509-2.check

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
t2509-2.scala:26: error: ambiguous implicit values:
2+
both value xb in object Test of type => X[B,Int]
3+
and value xa in object Test of type => X[A,Boolean]
4+
match expected type X[B,U]
5+
val fb = f(new B)
6+
^
7+
one error found

test/files/neg/t2509-2.scala

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
class A
2+
class B extends A
3+
class C extends B
4+
5+
trait X[-T, U] {
6+
val u: U
7+
}
8+
9+
object XA extends X[A, Boolean] {
10+
val u = true
11+
}
12+
13+
object XB extends X[B, Int] {
14+
val u = 23
15+
}
16+
17+
object Test {
18+
implicit def f[T, U](t: T)(implicit x: X[T, U]): U = x.u
19+
implicit val xa: X[A, Boolean] = XA
20+
implicit val xb: X[B, Int] = XB
21+
22+
val fa = f(new A)
23+
val ffa: Boolean = fa
24+
25+
// Should be ambiguous
26+
val fb = f(new B)
27+
val ffb: Int = fb
28+
}

test/files/neg/t2509-3.check

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
t2509-3.scala:31: error: value value is not a member of B
2+
println("B: " + b.value)
3+
^
4+
one error found

test/files/neg/t2509-3.scala

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
class A
2+
class B extends A
3+
4+
trait Y {
5+
def value: String
6+
}
7+
8+
trait X[-T] {
9+
def y(t: T): Y
10+
}
11+
12+
trait Z[-T] extends X[T]
13+
14+
object ZA extends Z[A] {
15+
def y(a: A) = new Y { def value = a.getClass + ": AValue" }
16+
}
17+
18+
object XB extends X[B] {
19+
def y(b: B) = new Y { def value = b.getClass + ": BValue" }
20+
}
21+
22+
object Test {
23+
implicit def f[T](t: T)(implicit x: X[T]): Y = x.y(t)
24+
implicit val za: Z[A] = ZA
25+
implicit val xb: X[B] = XB
26+
27+
def main(argv: Array[String]): Unit = {
28+
val a = new A
29+
val b = new B
30+
println("A: " + a.value)
31+
println("B: " + b.value)
32+
}
33+
}
34+

test/files/neg/t2509-4.check

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
t2509-4.scala:31: error: value value is not a member of B
2+
println("B: " + b.value)
3+
^
4+
one error found

test/files/pos/t2509-5.scala

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// See https://github.com/lampepfl/dotty/issues/2974
2+
trait Foo[-T]
3+
4+
trait Bar[-T] extends Foo[T]
5+
6+
object Test {
7+
implicit val fa: Foo[Any] = ???
8+
implicit val ba: Bar[Int] = ???
9+
10+
def test: Unit = {
11+
implicitly[Foo[Int]]
12+
}
13+
}

test/files/pos/t2509-6.scala

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
class A
2+
class B extends A
3+
4+
trait Y {
5+
def value: String
6+
}
7+
8+
trait X[-T] {
9+
def y(t: T): Y
10+
}
11+
12+
trait Z[-T] extends X[T]
13+
14+
object XA extends X[A] {
15+
def y(a: A) = new Y { def value = a.getClass + ": AValue" }
16+
}
17+
18+
object ZB extends Z[B] {
19+
def y(b: B) = new Y { def value = b.getClass + ": BValue" }
20+
}
21+
22+
object Test {
23+
implicit def f[T](t: T)(implicit x: X[T]): Y = x.y(t)
24+
implicit val za: X[A] = XA
25+
implicit val xb: Z[B] = ZB
26+
27+
def main(argv: Array[String]): Unit = {
28+
val a = new A
29+
val b = new B
30+
println("A: " + a.value)
31+
println("B: " + b.value)
32+
}
33+
}

test/files/run/t2509-1.check

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
warning: there was one feature warning; re-run with -feature for details
2+
A: class A: AValue
3+
B: class B: BValue

test/files/run/t2509-1.scala

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
class A
2+
class B extends A
3+
4+
trait Y {
5+
def value: String
6+
}
7+
8+
trait X[-T] {
9+
def y(t: T): Y
10+
}
11+
12+
object XA extends X[A] {
13+
def y(a: A) = new Y { def value = a.getClass + ": AValue" }
14+
}
15+
16+
object XB extends X[B] {
17+
def y(b: B) = new Y { def value = b.getClass + ": BValue" }
18+
}
19+
20+
object Test {
21+
implicit def f[T](t: T)(implicit x: X[T]): Y = x.y(t)
22+
implicit val xa: X[A] = XA
23+
implicit val xb: X[B] = XB
24+
25+
def main(argv: Array[String]) {
26+
val a = new A
27+
val b = new B
28+
println("A: " + a.value)
29+
println("B: " + b.value)
30+
}
31+
}

test/files/run/t2509-4.check

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
warning: there was one feature warning; re-run with -feature for details
2+
A: class A: AValue
3+
B: class B: BValue

test/files/run/t2509-4.scala

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
class A
2+
class B extends A
3+
4+
trait Y {
5+
def value: String
6+
}
7+
8+
trait X[-T] {
9+
def y(t: T): Y
10+
}
11+
12+
trait Z[-T] extends X[T]
13+
14+
object XA extends Z[A] {
15+
def y(a: A) = new Y { def value = a.getClass + ": AValue" }
16+
}
17+
18+
object ZB extends Z[B] {
19+
def y(b: B) = new Y { def value = b.getClass + ": BValue" }
20+
}
21+
22+
object Test {
23+
implicit def f[T](t: T)(implicit x: X[T]): Y = x.y(t)
24+
implicit val za: X[A] = XA
25+
implicit val xb: Z[B] = ZB
26+
27+
def main(argv: Array[String]): Unit = {
28+
val a = new A
29+
val b = new B
30+
println("A: " + a.value)
31+
println("B: " + b.value)
32+
}
33+
}
34+

test/files/run/t7768.check

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Inv[A] Inv[B] Inv[C] Inv[D] Inv[E]
2+
Con[A] Con[A] Con[C] Con[C] Con[E]
3+
Cov[E] Cov[E] Cov[E] Cov[E] Cov[E]

test/files/run/t7768.scala

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
class A
2+
class B extends A
3+
class C extends B
4+
class D extends C
5+
class E extends D
6+
7+
class Inv[T](override val toString: String)
8+
class Con[-T](override val toString: String)
9+
class Cov[+T](override val toString: String)
10+
11+
object InvTest {
12+
implicit val a : Inv[A] = new Inv[A]("Inv[A]")
13+
implicit val b : Inv[B] = new Inv[B]("Inv[B]")
14+
implicit val c : Inv[C] = new Inv[C]("Inv[C]")
15+
implicit val d : Inv[D] = new Inv[D]("Inv[D]")
16+
implicit val e : Inv[E] = new Inv[E]("Inv[E]")
17+
}
18+
object ConTest {
19+
implicit val a : Con[A] = new Con[A]("Con[A]")
20+
implicit val c : Con[C] = new Con[C]("Con[C]")
21+
implicit val e : Con[E] = new Con[E]("Con[E]")
22+
}
23+
object CovTest {
24+
implicit val a : Cov[A] = new Cov[A]("Cov[A]")
25+
implicit val c : Cov[C] = new Cov[C]("Cov[C]")
26+
implicit val e : Cov[E] = new Cov[E]("Cov[E]")
27+
}
28+
29+
object Test {
30+
def f0(): Unit = {
31+
import InvTest._
32+
println(List(
33+
implicitly[Inv[A]],
34+
implicitly[Inv[B]],
35+
implicitly[Inv[C]],
36+
implicitly[Inv[D]],
37+
implicitly[Inv[E]]
38+
) mkString " ")
39+
}
40+
def f1(): Unit = {
41+
import ConTest._
42+
println(List(
43+
implicitly[Con[A]],
44+
implicitly[Con[B]],
45+
implicitly[Con[C]],
46+
implicitly[Con[D]],
47+
implicitly[Con[E]]
48+
) mkString " ")
49+
}
50+
def f2(): Unit = {
51+
import CovTest._
52+
println(List(
53+
implicitly[Cov[A]],
54+
implicitly[Cov[B]],
55+
implicitly[Cov[C]],
56+
implicitly[Cov[D]],
57+
implicitly[Cov[E]]
58+
) mkString " ")
59+
}
60+
61+
def main(args: Array[String]): Unit = {
62+
f0()
63+
f1()
64+
f2()
65+
}
66+
}
67+
68+
/***
69+
70+
Previously:
71+
72+
Inv[A] Inv[B] Inv[C] Inv[D] Inv[E]
73+
Con[A] Con[A] Con[A] Con[A] Con[A]
74+
Cov[E] Cov[E] Cov[E] Cov[E] Cov[E]
75+
76+
Currently (and in Dotty):
77+
78+
Inv[A] Inv[B] Inv[C] Inv[D] Inv[E]
79+
Con[A] Con[A] Con[C] Con[C] Con[E]
80+
Cov[E] Cov[E] Cov[E] Cov[E] Cov[E]
81+
82+
Note that @paulp thinks that f2 should produce,
83+
84+
Cov[A] Cov[C] Cov[C] Cov[E] Cov[E]
85+
86+
I don't think that behaviour would be desirable: in the covariant case the
87+
expectation that the most derived should be selected as the most specific
88+
seems reasonable.
89+
***/

0 commit comments

Comments
 (0)