Skip to content

Commit 591154d

Browse files
authored
Merge pull request #12377 from dotty-staging/fix-12337
Fix #12337: Enable exhaustivity check for case classes with checkable components
2 parents 83ab627 + 682a596 commit 591154d

File tree

8 files changed

+39
-8
lines changed

8 files changed

+39
-8
lines changed

compiler/src/dotty/tools/backend/jvm/BCodeBodyBuilder.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -228,7 +228,7 @@ trait BCodeBodyBuilder extends BCodeSkelBuilder {
228228
resKind
229229
}
230230

231-
def genPrimitiveOp(tree: Apply, expectedType: BType): BType = tree match {
231+
def genPrimitiveOp(tree: Apply, expectedType: BType): BType = (tree: @unchecked) match {
232232
case Apply(fun @ DesugaredSelect(receiver, _), _) =>
233233
val sym = tree.symbol
234234

@@ -610,7 +610,7 @@ trait BCodeBodyBuilder extends BCodeSkelBuilder {
610610
}
611611
}
612612

613-
def genTypeApply(t: TypeApply): BType = t match {
613+
def genTypeApply(t: TypeApply): BType = (t: @unchecked) match {
614614
case TypeApply(fun@DesugaredSelect(obj, _), targs) =>
615615

616616
val sym = fun.symbol

compiler/src/dotty/tools/backend/jvm/BCodeSyncAndTry.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ trait BCodeSyncAndTry extends BCodeBodyBuilder {
2727
*/
2828
abstract class SyncAndTryBuilder(cunit: CompilationUnit) extends PlainBodyBuilder(cunit) {
2929

30-
def genSynchronized(tree: Apply, expectedType: BType): BType = tree match {
30+
def genSynchronized(tree: Apply, expectedType: BType): BType = (tree: @unchecked) match {
3131
case Apply(TypeApply(fun, _), args) =>
3232
val monitor = locals.makeLocal(ObjectReference, "monitor", defn.ObjectType, tree.span)
3333
val monCleanup = new asm.Label

compiler/src/dotty/tools/dotc/transform/localopt/StringInterpolatorOpt.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ class StringInterpolatorOpt extends MiniPhase {
121121
(sym.name == nme.f && sym.eq(defn.StringContext_f)) ||
122122
(sym.name == nme.s && sym.eq(defn.StringContext_s))
123123
if (isInterpolatedMethod)
124-
tree match {
124+
(tree: @unchecked) match {
125125
case StringContextIntrinsic(strs: List[Literal], elems: List[Tree]) =>
126126
val stri = strs.iterator
127127
val elemi = elems.iterator

compiler/src/dotty/tools/dotc/transform/patmat/Space.scala

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -791,16 +791,18 @@ class SpaceEngine(using Context) extends SpaceLogic {
791791
def isCheckable(tp: Type): Boolean =
792792
!tp.hasAnnotation(defn.UncheckedAnnot) && {
793793
val tpw = tp.widen.dealias
794+
val classSym = tpw.classSymbol
794795
ctx.settings.YcheckAllPatmat.value ||
795-
tpw.typeSymbol.is(Sealed) ||
796+
classSym.is(Sealed) ||
796797
tpw.isInstanceOf[OrType] ||
797798
(tpw.isInstanceOf[AndType] && {
798799
val and = tpw.asInstanceOf[AndType]
799800
isCheckable(and.tp1) || isCheckable(and.tp2)
800801
}) ||
801802
tpw.isRef(defn.BooleanClass) ||
802-
tpw.typeSymbol.isAllOf(JavaEnumTrait) ||
803-
(defn.isTupleType(tpw) && tpw.argInfos.exists(isCheckable(_)))
803+
classSym.isAllOf(JavaEnumTrait) ||
804+
(defn.isProductSubType(tpw) && classSym.is(Case)
805+
&& productSelectorTypes(tpw, sel.srcPos).exists(isCheckable(_)))
804806
}
805807

806808
val res = isCheckable(sel.tpe)

compiler/src/dotty/tools/dotc/typer/Applications.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -990,7 +990,7 @@ trait Applications extends Compatibility {
990990
* { val xs = es; e' = e' + args }
991991
*/
992992
def typedOpAssign(using Context): Tree = {
993-
val (lhs1, name, rhss) = tree match
993+
val (lhs1, name, rhss) = (tree: @unchecked) match
994994
case Apply(Select(lhs, name), rhss) => (typedExpr(lhs), name, rhss)
995995
case Apply(untpd.TypedSplice(Select(lhs1, name)), rhss) => (lhs1, name, rhss)
996996
val liftedDefs = new mutable.ListBuffer[Tree]

tests/neg-custom-args/isInstanceOf/enum-approx2.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,6 @@ class Test {
55
def eval(e: Fun[Int, Int]) = e match {
66
case Fun(x: Fun[Int, Double]) => ??? // error
77
case Fun(x: Exp[Int => String]) => ??? // error
8+
case _ =>
89
}
910
}

tests/patmat/i12337.check

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
8: Pattern Match Exhaustivity: Foo(Inactive)
2+
17: Pattern Match Exhaustivity: Foo(Status.Active(_))

tests/patmat/i12337.scala

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
sealed trait Status
2+
object Status {
3+
case class Active(since: Int) extends Status
4+
case object Inactive extends Status
5+
}
6+
7+
case class Foo(status: Status)
8+
def bar(foo: Foo): Unit = foo match {
9+
case Foo(Status.Active(since)) =>
10+
println(s"active since $since")
11+
}
12+
// Expected:
13+
// warning: match may not be exhaustive.
14+
// It would fail on the following input: Foo(Inactive)
15+
// def bar(foo: Foo): Unit = foo match {
16+
17+
def baz(foo: Foo): Unit = foo match {
18+
case Foo(Status.Active(2000)) =>
19+
println("active since 2000")
20+
case Foo(Status.Inactive) =>
21+
println("inactive")
22+
}
23+
// Expected:
24+
// warning: match may not be exhaustive.
25+
// It would fail on the following input: Foo(Active((x: Int forSome x not in 2000)))
26+
// def baz(foo: Foo): Unit = foo match {

0 commit comments

Comments
 (0)