diff --git a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala index 21c58374a342..1d529664c99d 100644 --- a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala +++ b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala @@ -687,6 +687,8 @@ object SpaceEngine { else NoType }.filter(_.exists) parts + case tref: TypeRef if tref.isUpperBoundedAbstract => + rec(tref.info.hiBound, mixins) case _ => ListOfNoType end rec @@ -702,6 +704,10 @@ object SpaceEngine { && !cls.hasAnonymousChild // can't name anonymous classes as counter-examples && cls.children.nonEmpty // can't decompose without children + extension (tref: TypeRef) + def isUpperBoundedAbstract(using Context): Boolean = + tref.symbol.isAbstractOrAliasType && !tref.info.hiBound.isNothingType + val ListOfNoType = List(NoType) val ListOfTypNoType = ListOfNoType.map(Typ(_, decomposed = true)) @@ -826,7 +832,11 @@ object SpaceEngine { classSym.is(Case) && { if seen.add(classSym) then productSelectorTypes(tpw, sel.srcPos).exists(isCheckable(_)) else true // recursive case class: return true and other members can still fail the check - } + } || + (tpw.isInstanceOf[TypeRef] && { + val tref = tpw.asInstanceOf[TypeRef] + tref.isUpperBoundedAbstract && isCheckable(tref.info.hiBound) + }) !sel.tpe.hasAnnotation(defn.UncheckedAnnot) && { diff --git a/tests/pos/i23620.scala b/tests/pos/i23620.scala new file mode 100644 index 000000000000..aa81f09ee182 --- /dev/null +++ b/tests/pos/i23620.scala @@ -0,0 +1,18 @@ +trait Foo +trait Bar + +type FooOrBar = FooOrBar.Type +object FooOrBar: + opaque type Type <: (Foo | Bar) = Foo | Bar + + def bar: FooOrBar = new Bar {} + +trait Buz + +@main def main = + val p: FooOrBar | Buz = FooOrBar.bar + + p match + case _: Foo => println("foo") + case _: Buz => println("buz") + case _: Bar => println("bar") diff --git a/tests/warn/i23620b.check b/tests/warn/i23620b.check new file mode 100644 index 000000000000..556448701026 --- /dev/null +++ b/tests/warn/i23620b.check @@ -0,0 +1,24 @@ +-- [E029] Pattern Match Exhaustivity Warning: tests/warn/i23620b.scala:20:2 -------------------------------------------- +20 | p match // warn + | ^ + | match may not be exhaustive. + | + | It would fail on pattern case: _: Bar + | + | longer explanation available when compiling with `-explain` +-- [E029] Pattern Match Exhaustivity Warning: tests/warn/i23620b.scala:23:2 -------------------------------------------- +23 | p2 match { // warn + | ^^ + | match may not be exhaustive. + | + | It would fail on pattern case: _: Bar + | + | longer explanation available when compiling with `-explain` +-- [E029] Pattern Match Exhaustivity Warning: tests/warn/i23620b.scala:37:2 -------------------------------------------- +37 | x match // warn + | ^ + | match may not be exhaustive. + | + | It would fail on pattern case: B + | + | longer explanation available when compiling with `-explain` diff --git a/tests/warn/i23620b.scala b/tests/warn/i23620b.scala new file mode 100644 index 000000000000..dfdac45347a2 --- /dev/null +++ b/tests/warn/i23620b.scala @@ -0,0 +1,38 @@ +trait Foo +trait Bar + +type FooOrBar = FooOrBar.Type +object FooOrBar: + opaque type Type <: (Foo | Bar) = Foo | Bar + + def bar: FooOrBar = new Bar {} + +type OnlyFoo = OnlyFoo.Type +object OnlyFoo: + opaque type Type <: (Foo | Bar) = Foo + + def foo: OnlyFoo = new Foo {} + +@main def main = + val p: FooOrBar= FooOrBar.bar + val p2: OnlyFoo = OnlyFoo.foo + + p match // warn + case _: Foo => println("foo") + + p2 match { // warn + case _: Foo => println("foo") + } + +sealed trait S +trait Z + +case object A extends S, Z +case object B extends S, Z + +trait HasT: + type T <: S & Z + +def nonExhaustive(h: HasT, x: h.T) = + x match // warn + case A => () diff --git a/tests/warn/i24246.check b/tests/warn/i24246.check new file mode 100644 index 000000000000..fa197419d86d --- /dev/null +++ b/tests/warn/i24246.check @@ -0,0 +1,8 @@ +-- [E029] Pattern Match Exhaustivity Warning: tests/warn/i24246.scala:8:2 ---------------------------------------------- +8 | x match { // warn + | ^ + | match may not be exhaustive. + | + | It would fail on pattern case: ZZ + | + | longer explanation available when compiling with `-explain` diff --git a/tests/warn/i24246.scala b/tests/warn/i24246.scala new file mode 100644 index 000000000000..f5bd02692774 --- /dev/null +++ b/tests/warn/i24246.scala @@ -0,0 +1,10 @@ +trait X + +sealed trait Y +case object YY extends Y, X +case object ZZ extends Y, X + +def foo[A <: X & Y](x: A): Unit = + x match { // warn + case YY => () + }