Description
Originally posted by @Linyxus in #15175 (comment)
Regarding the soundness holes caused by resetting approx state: yes, we have more soundness holes caused by this. Each time isSubType(S, T)
(instead of recur(S, T)
) is called inside isSubType
, the approx state is reset, and it will be a potential soundness hole. For example, I find another callsite of isSubType
here, and we can make a counter-example showing the unsoundness brought by this:
enum SUB[-A, +B]:
case Refl[C]() extends SUB[C, C]
trait Tag { type T }
def foo[A, B, X <: Tag{type T <: A}](e: SUB[X, Tag{type T <: B}], x: A): B = e match {
case SUB.Refl() =>
// unsound GADT constr because of approx state resetting: A <: B
x
}
def bad(x: Int): String =
foo[Int, String, Tag{type T = Nothing}](SUB.Refl(), x) // cast Int to String
In this example, we are trying to extract necessary constraint from X <: Tag{type T <: B}
, where X
is known to be a subtype of Tag{type T <: A}
. During this we will try upcasting LHS and compare Tag{type T <: A} <: Tag{type T <: B}
with LHS approximated. Since when comparing the refinement info the approx state is reset, we derive the unsound constraint A <: B
.