Skip to content

Commit b664a48

Browse files
committed
Merge pull request scala#3797 from retronym/topic/exhaust-compound
SI-8631 Treat `A with Sealed` as enumerable for pattern matching
2 parents a704678 + 78caf29 commit b664a48

File tree

4 files changed

+59
-4
lines changed

4 files changed

+59
-4
lines changed

src/compiler/scala/tools/nsc/transform/patmat/MatchAnalysis.scala

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -104,11 +104,18 @@ trait TreeAndTypeAnalysis extends Debugging {
104104
// TODO case _ if tp.isTupleType => // recurse into component types
105105
case modSym: ModuleClassSymbol =>
106106
Some(List(tp))
107+
case sym: RefinementClassSymbol =>
108+
val parentSubtypes: List[Option[List[Type]]] = tp.parents.map(parent => enumerateSubtypes(parent))
109+
if (parentSubtypes exists (_.isDefined))
110+
// If any of the parents is enumerable, then the refinement type is enumerable.
111+
Some(
112+
// We must only include subtypes of the parents that conform to `tp`.
113+
// See neg/virtpatmat_exhaust_compound.scala for an example.
114+
parentSubtypes flatMap (_.getOrElse(Nil)) filter (_ <:< tp)
115+
)
116+
else None
107117
// make sure it's not a primitive, else (5: Byte) match { case 5 => ... } sees no Byte
108-
case sym if !sym.isSealed || isPrimitiveValueClass(sym) =>
109-
debug.patmat("enum unsealed "+ ((tp, sym, sym.isSealed, isPrimitiveValueClass(sym))))
110-
None
111-
case sym =>
118+
case sym if sym.isSealed =>
112119
val subclasses = debug.patmatResult(s"enum $sym sealed, subclasses")(
113120
// symbols which are both sealed and abstract need not be covered themselves, because
114121
// all of their children must be and they cannot otherwise be created.
@@ -136,6 +143,9 @@ trait TreeAndTypeAnalysis extends Debugging {
136143
else None
137144
}
138145
})
146+
case sym =>
147+
debug.patmat("enum unsealed "+ ((tp, sym, sym.isSealed, isPrimitiveValueClass(sym))))
148+
None
139149
}
140150

141151
// approximate a type to the static type that is fully checkable at run time,
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
virtpatmat_exhaust_compound.scala:14: warning: match may not be exhaustive.
2+
It would fail on the following inputs: O1, O2, O4
3+
a match {
4+
^
5+
virtpatmat_exhaust_compound.scala:18: warning: match may not be exhaustive.
6+
It would fail on the following input: O4
7+
def t1(a: Product with Base with Base2) = a match {
8+
^
9+
virtpatmat_exhaust_compound.scala:22: warning: match may not be exhaustive.
10+
It would fail on the following input: O2
11+
def t2(a: Product with Base { def foo: Int }) = a match {
12+
^
13+
error: No warnings can be incurred under -Xfatal-warnings.
14+
three warnings found
15+
one error found
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
-Xfatal-warnings
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
sealed trait Base
2+
case object O1 extends Base
3+
case object O2 extends Base {
4+
def foo: Int = 0
5+
}
6+
7+
sealed trait Base2
8+
case object O3 extends Base2
9+
10+
case object O4 extends Base with Base2
11+
12+
object Test {
13+
val a /*: Product with Serialiable with Base */ = if (true) O1 else O2
14+
a match {
15+
case null =>
16+
}
17+
18+
def t1(a: Product with Base with Base2) = a match {
19+
case null => // O1..O3 should *not* be possible here
20+
}
21+
22+
def t2(a: Product with Base { def foo: Int }) = a match {
23+
case null => // O2 in the domain
24+
}
25+
26+
def t3(a: Product with Base { def bar: Int }) = a match {
27+
case null => // nothing in the domain
28+
}
29+
}

0 commit comments

Comments
 (0)