From f051252c28d22b9bb4f5a5219a7bfade99421ccf Mon Sep 17 00:00:00 2001 From: zielinsky Date: Sun, 24 Aug 2025 15:48:44 +0200 Subject: [PATCH] add guard for doing rhs decompostion in computing subspaces [Cherry-picked f3db07a2937b4db2c47e87194363c55e99951726] --- .../dotty/tools/dotc/transform/patmat/Space.scala | 10 +++++++++- tests/pos/i20225.scala | 12 ++++++++++++ tests/pos/i20395.scala | 13 +++++++++++++ tests/run/i20225.check | 1 + tests/run/i20225.scala | 12 ++++++++++++ tests/run/i20395.check | 1 + tests/run/i20395.scala | 13 +++++++++++++ 7 files changed, 61 insertions(+), 1 deletion(-) create mode 100644 tests/pos/i20225.scala create mode 100644 tests/pos/i20395.scala create mode 100644 tests/run/i20225.check create mode 100644 tests/run/i20225.scala create mode 100644 tests/run/i20395.check create mode 100644 tests/run/i20395.scala diff --git a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala index b7e1f349a377..40054d73a357 100644 --- a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala +++ b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala @@ -171,6 +171,14 @@ object SpaceEngine { /** Is `a` a subspace of `b`? Equivalent to `simplify(simplify(a) - simplify(b)) == Empty`, but faster */ def computeIsSubspace(a: Space, b: Space)(using Context): Boolean = trace(i"isSubspace($a, $b)") { + /** Is decomposition allowed on the right-hand side of a pattern? */ + /** We only allow decomposition on the right-hand side of a pattern if the type is not a type parameter, a type parameter reference, or a deferred type reference */ + /** This is because decomposition on the right-hand side of a pattern can lead to false positive warnings */ + inline def rhsDecompositionAllowed(tp: Type): Boolean = tp.dealias match + case _: TypeParamRef => false + case tr: TypeRef if tr.symbol.is(TypeParam) || (tr.symbol.is(Deferred) && !tr.symbol.isClass) => false + case _ => true + val a2 = simplify(a) val b2 = simplify(b) if (a ne a2) || (b ne b2) then isSubspace(a2, b2) @@ -185,7 +193,7 @@ object SpaceEngine { case (a @ Typ(tp1, _), b @ Typ(tp2, _)) => isSubType(tp1, tp2) || canDecompose(a) && isSubspace(Or(decompose(a)), b) - || canDecompose(b) && isSubspace(a, Or(decompose(b))) + || (canDecompose(b) && rhsDecompositionAllowed(tp2)) && isSubspace(a, Or(decompose(b))) case (Prod(tp1, _, _), Typ(tp2, _)) => isSubType(tp1, tp2) case (a @ Typ(tp1, _), Prod(tp2, fun, ss)) => diff --git a/tests/pos/i20225.scala b/tests/pos/i20225.scala new file mode 100644 index 000000000000..3704aa2034b5 --- /dev/null +++ b/tests/pos/i20225.scala @@ -0,0 +1,12 @@ +sealed abstract class Parent +class A extends Parent +class B extends Parent + +inline def matchAs[T <: Parent](p: Parent): Unit = p match + case _: T => () + case _ => () + +object Test: + def main(args: Array[String]): Unit = + matchAs[A](new B) + diff --git a/tests/pos/i20395.scala b/tests/pos/i20395.scala new file mode 100644 index 000000000000..3b0be1e31b5a --- /dev/null +++ b/tests/pos/i20395.scala @@ -0,0 +1,13 @@ +sealed trait NodeId +case object Hi extends NodeId +case object Hello extends NodeId + +extension (value: NodeId) + inline def parse[T <: NodeId] = value match + case _: T => () + case _ => () + +object Test: + def main(args: Array[String]): Unit = + Hi.parse[Hello.type] + diff --git a/tests/run/i20225.check b/tests/run/i20225.check new file mode 100644 index 000000000000..0f06315755de --- /dev/null +++ b/tests/run/i20225.check @@ -0,0 +1 @@ +unreachable case reached diff --git a/tests/run/i20225.scala b/tests/run/i20225.scala new file mode 100644 index 000000000000..ad9babf832f7 --- /dev/null +++ b/tests/run/i20225.scala @@ -0,0 +1,12 @@ +sealed abstract class Parent +class A extends Parent +class B extends Parent + +inline def matchAs[T <: Parent](p: Parent): Unit = p match + case _: T => () + case _ => println("unreachable case reached") + +object Test: + def main(args: Array[String]): Unit = + matchAs[A](new B) + diff --git a/tests/run/i20395.check b/tests/run/i20395.check new file mode 100644 index 000000000000..60653bdaa7f2 --- /dev/null +++ b/tests/run/i20395.check @@ -0,0 +1 @@ +not match diff --git a/tests/run/i20395.scala b/tests/run/i20395.scala new file mode 100644 index 000000000000..e432ea4bca6b --- /dev/null +++ b/tests/run/i20395.scala @@ -0,0 +1,13 @@ +sealed trait NodeId +case object Hi extends NodeId +case object Hello extends NodeId + +extension (value: NodeId) + inline def parse[T <: NodeId]: Unit = value match + case _: T => println("match") + case _ => println("not match") + +object Test: + def main(args: Array[String]): Unit = + Hi.parse[Hello.type] +