Skip to content

Commit cb40a49

Browse files
committed
Always freeze GADTs when comparing type member infos
1 parent 63344e7 commit cb40a49

File tree

5 files changed

+42
-4
lines changed

5 files changed

+42
-4
lines changed

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

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1923,8 +1923,6 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
19231923
|| symInfo.isInstanceOf[MethodType]
19241924
&& symInfo.signature.consistentParams(info2.signature)
19251925

1926-
def tp1IsSingleton: Boolean = tp1.isInstanceOf[SingletonType]
1927-
19281926
// A relaxed version of isSubType, which compares method types
19291927
// under the standard arrow rule which is contravarient in the parameter types,
19301928
// but under the condition that signatures might have to match (see sigsOK)
@@ -1939,8 +1937,8 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
19391937
matchingMethodParams(info1, info2, precise = false)
19401938
&& isSubInfo(info1.resultType, info2.resultType.subst(info2, info1), symInfo1.resultType)
19411939
&& sigsOK(symInfo1, info2)
1942-
case _ => inFrozenGadtIf(tp1IsSingleton) { isSubType(info1, info2) }
1943-
case _ => inFrozenGadtIf(tp1IsSingleton) { isSubType(info1, info2) }
1940+
case _ => inFrozenGadtIf(!info2.isTypeAlias) { isSubType(info1, info2) }
1941+
case _ => inFrozenGadtIf(!info2.isTypeAlias) { isSubType(info1, info2) }
19441942

19451943
def qualifies(m: SingleDenotation): Boolean =
19461944
val info1 = m.info.widenExpr

tests/neg/i15485.scala

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
enum SUB[-A, +B]:
2+
case Refl[C]() extends SUB[C, C]
3+
4+
trait Tag { type T }
5+
6+
def foo[A, B, X <: Tag { type T <: A } ](
7+
e: SUB[X, Tag { type T <: B } ],
8+
x: A,
9+
): B = e match {
10+
case SUB.Refl() =>
11+
// unsound GADT constr because of approx state resetting:
12+
// A <: B
13+
x // error: Found: (x: A) Required: B
14+
}
15+
16+
def bad(x: Int): String =
17+
foo[Int, String, Tag { type T = Nothing } ](SUB.Refl(), x) // cast Int to String
18+
19+
object Test:
20+
def main(args: Array[String]): Unit = bad(1) // was: ClassCastException: class java.lang.Integer cannot be cast to class java.lang.String
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import scala.deriving.*, scala.quoted.*
2+
3+
trait Foo[T]:
4+
def foo: Int
5+
6+
// A minimisation of monocle's GenIsoSpec.scala
7+
// which broke when fixing soundness in infering GADT constraints on refined types
8+
object Foo:
9+
inline given derived[T](using inline m: Mirror.Of[T]): Foo[T] = ${ impl('m) }
10+
11+
private def impl[T](m: Expr[Mirror.Of[T]])(using qctx: Quotes, tpe: Type[T]): Expr[Foo[T]] = m match
12+
case '{ $m : Mirror.Product { type MirroredElemTypes = EmptyTuple } } => '{ FooN[T](0) }
13+
case '{ $m : Mirror.Product { type MirroredElemTypes = a *: EmptyTuple } } => '{ FooN[T](1) }
14+
case '{ $m : Mirror.Product { type MirroredElemTypes = mirroredElemTypes } } => '{ FooN[T](9) }
15+
16+
class FooN[T](val foo: Int) extends Foo[T]
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
final case class Box(value: Int) derives Foo
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
@main def Test =
2+
val foo = summon[Foo[Box]].foo
3+
assert(foo == 1, foo)

0 commit comments

Comments
 (0)