diff --git a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala index 0dfa73035099..71d1283a6264 100644 --- a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala +++ b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala @@ -167,6 +167,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) @@ -181,7 +189,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 (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] +