Skip to content

Commit 17b7f9d

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 9291e12 commit 17b7f9d

File tree

21 files changed

+479
-1
lines changed

21 files changed

+479
-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/library/scala/collection/immutable/TreeSet.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ object TreeSet extends ImmutableSortedSetFactory[TreeSet] {
2424
implicit def implicitBuilder[A](implicit ordering: Ordering[A]): Builder[A, TreeSet[A]] = newBuilder[A](ordering)
2525
override def newBuilder[A](implicit ordering: Ordering[A]): Builder[A, TreeSet[A]] =
2626
new SetBuilder(empty[A](ordering))
27+
override implicit def newCanBuildFrom[A](implicit ord : Ordering[A]) : CanBuildFrom[Coll, A, TreeSet[A]] = super.newCanBuildFrom
2728

2829
/** The empty set of this type
2930
*/

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

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

2008+
def flipped: Symbol = this
2009+
20082010
// ------ access to related symbols --------------------------------------------------
20092011

20102012
/** The next enclosing class. */
@@ -3375,6 +3377,14 @@ trait Symbols extends api.Symbols { self: SymbolTable =>
33753377
clone
33763378
}
33773379

3380+
override lazy val flipped: ClassSymbol = {
3381+
val clone = cloneSymbol(owner)
3382+
clone.rawInfo.typeParams.foreach { sym =>
3383+
if (sym.isContravariant) sym.resetFlag(Flag.CONTRAVARIANT).setFlag(Flag.COVARIANT)
3384+
}
3385+
clone
3386+
}
3387+
33783388
override def derivedValueClassUnbox =
33793389
// (info.decl(nme.unbox)) orElse uncomment once we accept unbox methods
33803390
(info.decls.find(_ hasAllFlags PARAMACCESSOR | METHOD) getOrElse

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/neg/t2509-7b.check

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
t2509-7b.scala:30: error: ambiguous implicit values:
2+
both method make in object X of type => Both[X,X]
3+
and method make in trait Factory of type => Both[Y,Y]
4+
match expected type Both[Y,X]
5+
get
6+
^
7+
one error found

test/files/neg/t2509-7b.scala

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
class Both[-A, +B]
2+
3+
trait Factory[A] {
4+
implicit def make: Both[A, A] = new Both[A, A]
5+
}
6+
7+
trait X
8+
object X extends Factory[X] {
9+
override implicit def make: Both[X, X] = super.make
10+
}
11+
12+
class Y extends X
13+
object Y extends Factory[Y] {
14+
// See test/files/pos/t2509-7a.scala ... discussion below
15+
// override implicit def make: Both[Y, Y] = super.make
16+
}
17+
18+
object Test {
19+
def get(implicit ev: Both[Y, X]) = ev
20+
21+
// There are two possible implicits here: X.make and Y.make, neither are
22+
// subtype of each other, so who wins?
23+
// - Under the old scheme it's X.make because `isAsGood` sees that X.make is defined
24+
// in X whereas Y.make is defined in Factory
25+
// - Under the new scheme it's ambiguous because we replace contravariance by covariance
26+
// in top-level type parameters so Y.make is treated as a subtype of X.make
27+
// In both schemes we can get Y.make to win by uncommenting the override for make in Y
28+
// (Y wins against X because `isDerived` also considers the subtyping relationships
29+
// of companion classes)
30+
get
31+
}
File renamed without changes.

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/pos/t2509-7a.scala

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
class Both[-A, +B]
2+
3+
trait Factory[A] {
4+
implicit def make: Both[A, A] = new Both[A, A]
5+
}
6+
7+
trait X
8+
object X extends Factory[X] {
9+
override implicit def make: Both[X, X] = super.make
10+
}
11+
12+
class Y extends X
13+
object Y extends Factory[Y] {
14+
// See test/files/neg/t2509-7b.scala ... discussion below
15+
override implicit def make: Both[Y, Y] = super.make
16+
}
17+
18+
object Test {
19+
def get(implicit ev: Both[Y, X]) = ev
20+
21+
// There are two possible implicits here: X.make and Y.make, neither are
22+
// subtype of each other, so who wins?
23+
// - Under the old scheme it's X.make because `isAsGood` sees that X.make is defined
24+
// in X whereas Y.make is defined in Factory
25+
// - Under the new scheme it's ambiguous because we replace contravariance by covariance
26+
// in top-level type parameters so Y.make is treated as a subtype of X.make
27+
// In both schemes we can get Y.make to win by uncommenting the override for make in Y
28+
// (Y wins against X because `isDerived` also considers the subtyping relationships
29+
// of companion classes)
30+
get
31+
}

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]

0 commit comments

Comments
 (0)