summaryrefslogtreecommitdiff
path: root/src/compiler
diff options
context:
space:
mode:
authorJason Zaugg <jzaugg@gmail.com>2013-04-23 23:55:05 +0200
committerJason Zaugg <jzaugg@gmail.com>2013-04-24 10:09:25 +0200
commit62713964b96f64f9c0fd0070c89aa6571679856d (patch)
tree36141e60bec629274a7b716386672f8e3d897509 /src/compiler
parentd484c17dbc47559dad8a7ae544d14a285cdfb8b2 (diff)
downloadscala-62713964b96f64f9c0fd0070c89aa6571679856d.tar.gz
scala-62713964b96f64f9c0fd0070c89aa6571679856d.tar.bz2
scala-62713964b96f64f9c0fd0070c89aa6571679856d.zip
SI-7369 Avoid spurious unreachable warnings in patterns
Unreachability analysis draws on the enumerated domain of types (e.g sealed subclasses + null, or true/false), and also looks at all stable identifier patterns tested for equality against the same 'slot' in a pattern. It was drawing the wrong conclusions about stable identifier patterns. Unlike the domain constants, two such values may hold the same value, so we can't assume that matching X precludes matching Y in the same slot in a subsequent case. For example: val X: Boolean = true; val Y: Boolean = true def m1(t1: Tuple1[Boolean]) = t1 match { case Tuple1(true) => case Tuple1(false) => case Tuple1(false) => // correctly unreachable } def m2(t1: Tuple1[Boolean]) = t1 match { case Tuple1(X) => case Tuple1(Y) => // spurious unreachable warning } // // Before // reachability, vars: V2: Boolean ::= true | false// Set(false, Y, X, true) // = x1._1 V1: (Boolean,) ::= null | ... // = x1 equality axioms: V2=true#4 \/ V2=false#5 /\ -V2=false#5 \/ -V2=Y#3 /\ -V2=false#5 \/ -V2=X#2 /\ -V2=false#5 \/ -V2=true#4 /\ -V2=Y#3 \/ -V2=X#2 /\ -V2=Y#3 \/ -V2=true#4 /\ -V2=X#2 \/ -V2=true#4 // // After // reachability, vars: V2: Boolean ::= true | false// Set(false, Y, X, true) // = x1._1 V1: (Boolean,) ::= null | ... // = x1 equality axioms: V2=true#4 \/ V2=false#5 /\ -V2=false#5 \/ -V2=true#4
Diffstat (limited to 'src/compiler')
-rw-r--r--src/compiler/scala/tools/nsc/transform/patmat/Logic.scala39
1 files changed, 28 insertions, 11 deletions
diff --git a/src/compiler/scala/tools/nsc/transform/patmat/Logic.scala b/src/compiler/scala/tools/nsc/transform/patmat/Logic.scala
index 0fab48028e..dbe08315f4 100644
--- a/src/compiler/scala/tools/nsc/transform/patmat/Logic.scala
+++ b/src/compiler/scala/tools/nsc/transform/patmat/Logic.scala
@@ -385,18 +385,35 @@ trait ScalaLogic extends Interface with Logic with TreeAndTypeAnalysis {
// else debug.patmat("NOT implies: "+(lower, upper))
- /** does V = C preclude V having value `other`?
- (1) V = null is an exclusive assignment,
- (2) V = A and V = B, for A and B value constants, are mutually exclusive unless A == B
- we err on the safe side, for example:
- - assume `val X = 1; val Y = 1`, then
- (2: Int) match { case X => case Y => <falsely considered reachable> }
- - V = 1 does not preclude V = Int, or V = Any, it could be said to preclude V = String, but we don't model that
-
- (3) for types we could try to do something fancy, but be conservative and just say no
+ /** Does V=A preclude V=B?
+ *
+ * (0) A or B must be in the domain to draw any conclusions.
+ *
+ * For example, knowing the the scrutinee is *not* true does not
+ * statically exclude it from being `X`, because that is an opaque
+ * Boolean.
+ *
+ * val X = true
+ * (true: Boolean) match { case true => case X <reachable> }
+ *
+ * (1) V = null excludes assignment to any other constant (modulo point #0). This includes
+ * both values and type tests (which are both modelled here as `Const`)
+ * (2) V = A and V = B, for A and B domain constants, are mutually exclusive unless A == B
+ *
+ * (3) We only reason about test tests as being excluded by null assignments, otherwise we
+ * only consider value assignments.
+ * TODO: refine this, a == 0 excludes a: String, or `a: Int` excludes `a: String`
+ * (since no value can be of both types. See also SI-7211)
+ *
+ * NOTE: V = 1 does not preclude V = Int, or V = Any, it could be said to preclude
+ * V = String, but we don't model that.
*/
- def excludes(a: Const, b: Const): Boolean =
- a != b && ((a == NullConst || b == NullConst) || (a.isValue && b.isValue))
+ def excludes(a: Const, b: Const): Boolean = {
+ val bothInDomain = domain exists (d => d(a) && d(b))
+ val eitherIsNull = a == NullConst || b == NullConst
+ val bothAreValues = a.isValue && b.isValue
+ bothInDomain && (eitherIsNull || bothAreValues) && (a != b)
+ }
// if(r) debug.patmat("excludes : "+(a, a.tp, b, b.tp))
// else debug.patmat("NOT excludes: "+(a, b))