Skip to content

Commit 83eedaf

Browse files
committed
Drop capture conversion in type comparer
1 parent 6161eea commit 83eedaf

File tree

4 files changed

+92
-56
lines changed

4 files changed

+92
-56
lines changed

compiler/src/dotty/tools/dotc/core/TypeComparer.scala

Lines changed: 44 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1001,44 +1001,53 @@ class TypeComparer(initctx: Context) extends ConstraintHandling[AbsentContext] {
10011001
}
10021002

10031003
/** Subtype test for corresponding arguments in `args1`, `args2` according to
1004-
* variances in type parameters `tparams`.
1004+
* variances in type parameters `tparams2`.
1005+
* @param tp1 The applied type containing `args1`
1006+
* @param tparams2 The type parameters of the type constructor applied to `args2`
10051007
*/
1006-
def isSubArgs(args1: List[Type], args2: List[Type], tp1: Type, tparams: List[ParamInfo]): Boolean =
1007-
if (args1.isEmpty) args2.isEmpty
1008-
else args2.nonEmpty && {
1009-
val tparam = tparams.head
1010-
val v = tparam.paramVariance
1011-
1012-
def compareCaptured(arg1: Type, arg2: Type): Boolean = arg1 match {
1013-
case arg1: TypeBounds =>
1014-
val captured = TypeRef(tp1, tparam.asInstanceOf[TypeSymbol])
1015-
isSubArg(captured, arg2)
1016-
case _ =>
1017-
false
1018-
}
1008+
def isSubArgs(args1: List[Type], args2: List[Type], tp1: Type, tparams2: List[ParamInfo]): Boolean = {
10191009

1020-
def isSubArg(arg1: Type, arg2: Type): Boolean = arg2 match {
1021-
case arg2: TypeBounds =>
1022-
arg2.contains(arg1) || compareCaptured(arg1, arg2)
1023-
case _ =>
1024-
arg1 match {
1025-
case arg1: TypeBounds =>
1026-
compareCaptured(arg1, arg2)
1027-
case _ =>
1028-
(v > 0 || isSubType(arg2, arg1)) &&
1029-
(v < 0 || isSubType(arg1, arg2))
1030-
}
1031-
}
1010+
def paramBounds(tparam: Symbol): TypeBounds =
1011+
tparam.info.substApprox(tparams2.asInstanceOf[List[Symbol]], args2).bounds
10321012

1033-
val arg1 = args1.head
1034-
val arg2 = args2.head
1035-
isSubArg(arg1, arg2) || {
1036-
// last effort: try to adapt variances of higher-kinded types if this is sound.
1037-
// TODO: Move this to eta-expansion?
1038-
val adapted2 = arg2.adaptHkVariances(tparam.paramInfo)
1039-
adapted2.ne(arg2) && isSubArg(arg1, adapted2)
1040-
}
1041-
} && isSubArgs(args1.tail, args2.tail, tp1, tparams.tail)
1013+
def recur(args1: List[Type], args2: List[Type], tparams2: List[ParamInfo]): Boolean =
1014+
if (args1.isEmpty) args2.isEmpty
1015+
else args2.nonEmpty && {
1016+
val tparam = tparams2.head
1017+
val v = tparam.paramVariance
1018+
1019+
def isSubArg(arg1: Type, arg2: Type): Boolean = arg2 match {
1020+
case arg2: TypeBounds =>
1021+
val arg1norm = arg1 match {
1022+
case arg1: TypeBounds =>
1023+
tparam match {
1024+
case tparam: Symbol => arg1 & paramBounds(tparam)
1025+
case _ => arg1 // This case can only arise when a hk-type is illegally instantiated with a wildcard
1026+
}
1027+
case _ => arg1
1028+
}
1029+
arg2.contains(arg1norm)
1030+
case _ =>
1031+
arg1 match {
1032+
case arg1: TypeBounds => false
1033+
case _ =>
1034+
(v > 0 || isSubType(arg2, arg1)) &&
1035+
(v < 0 || isSubType(arg1, arg2))
1036+
}
1037+
}
1038+
1039+
val arg1 = args1.head
1040+
val arg2 = args2.head
1041+
isSubArg(arg1, arg2) || {
1042+
// last effort: try to adapt variances of higher-kinded types if this is sound.
1043+
// TODO: Move this to eta-expansion?
1044+
val adapted2 = arg2.adaptHkVariances(tparam.paramInfo)
1045+
adapted2.ne(arg2) && isSubArg(arg1, adapted2)
1046+
}
1047+
} && recur(args1.tail, args2.tail, tparams2.tail)
1048+
1049+
recur(args1, args2, tparams2)
1050+
}
10421051

10431052
/** Test whether `tp1` has a base type of the form `B[T1, ..., Tn]` where
10441053
* - `B` derives from one of the class symbols of `tp2`,

tests/neg/i5948b.scala

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
class Foo[T](var elem: T) { type TT = T }
2+
3+
object Test {
4+
def setFirstInPair[T](pair: (Foo[T], Foo[T])) = {
5+
pair._1.elem = pair._2.elem
6+
}
7+
8+
def setFirstInList[T](list: List[Foo[T]]) = {
9+
list(0).elem = list(1).elem
10+
}
11+
12+
def test1(): Unit = {
13+
val fooInt = new Foo(1)
14+
val fooString = new Foo("")
15+
val pair: (Foo[_], Foo[_]) = (fooInt, fooString)
16+
setFirstInPair(pair) // error
17+
println(fooInt.elem + 1)
18+
}
19+
20+
def test2(): Unit = {
21+
val fooInt = new Foo(1)
22+
val fooString = new Foo("")
23+
val list: List[Foo[_]] = List(fooInt, fooString)
24+
setFirstInList(list) // error
25+
println(fooInt.elem + 1)
26+
}
27+
28+
def main(args: Array[String]): Unit = {
29+
test1()
30+
test2()
31+
}
32+
}

tests/neg/t5729.scala

Lines changed: 0 additions & 21 deletions
This file was deleted.

tests/run/t5729.scala

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
trait T[X]
2+
object Test extends App {
3+
def join(in: Seq[T[_]]): Int = 1
4+
def join[S](in: Seq[T[S]]): String = "x"
5+
val x = join(null: Seq[T[_]])
6+
assert(x == 1) // first alt chosen, since second requires a capture conversion in adapt
7+
C
8+
}
9+
10+
object C {
11+
def join(in: Seq[List[_]]): Int = 1
12+
def join[S](in: Seq[List[S]]): String = "x"
13+
14+
val x= join(Seq[List[Int]]()) // second alt chosen, since it is more specific
15+
assert(x == "x")
16+
}

0 commit comments

Comments
 (0)