Skip to content

Commit bbedc51

Browse files
committed
Always freeze GADTs when comparing type member infos
1 parent e560c2d commit bbedc51

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
@@ -1849,8 +1849,6 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
18491849
|| symInfo.isInstanceOf[MethodType]
18501850
&& symInfo.signature.consistentParams(info2.signature)
18511851

1852-
def tp1IsSingleton: Boolean = tp1.isInstanceOf[SingletonType]
1853-
18541852
// A relaxed version of isSubType, which compares method types
18551853
// under the standard arrow rule which is contravarient in the parameter types,
18561854
// but under the condition that signatures might have to match (see sigsOK)
@@ -1865,8 +1863,8 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
18651863
matchingMethodParams(info1, info2, precise = false)
18661864
&& isSubInfo(info1.resultType, info2.resultType.subst(info2, info1), symInfo1.resultType)
18671865
&& sigsOK(symInfo1, info2)
1868-
case _ => inFrozenGadtIf(tp1IsSingleton) { isSubType(info1, info2) }
1869-
case _ => inFrozenGadtIf(tp1IsSingleton) { isSubType(info1, info2) }
1866+
case _ => inFrozenGadtIf(!info2.isTypeAlias) { isSubType(info1, info2) }
1867+
case _ => inFrozenGadtIf(!info2.isTypeAlias) { isSubType(info1, info2) }
18701868

18711869
val info1 = m.info.widenExpr
18721870
isSubInfo(info1, tp2.refinedInfo.widenExpr, m.symbol.info.orElse(info1))

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)