Skip to content

Commit 897116f

Browse files
author
Adriaan Moors
committed
Merge pull request scala#874 from adriaanm/ticket-6022
SI-6022 model type-test-implication better
2 parents a569148 + d852c9b commit 897116f

File tree

3 files changed

+44
-8
lines changed

3 files changed

+44
-8
lines changed

src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala

Lines changed: 36 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1616,7 +1616,16 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL
16161616

16171617
type Const <: AbsConst
16181618
trait AbsConst {
1619+
// when we know V = C, which other equalities must hold
1620+
// in general, equality to some type implies equality to its supertypes
1621+
// (this multi-valued kind of equality is necessary for unreachability)
1622+
// note that we use subtyping as a model for implication between instanceof tests
1623+
// i.e., when S <:< T we assume x.isInstanceOf[S] implies x.isInstanceOf[T]
1624+
// unfortunately this is not true in general (see e.g. SI-6022)
16191625
def implies(other: Const): Boolean
1626+
1627+
// does V = C preclude V having value `other`? V = null is an exclusive assignment,
1628+
// but V = 1 does not preclude V = Int, or V = Any
16201629
def excludes(other: Const): Boolean
16211630
}
16221631

@@ -2164,11 +2173,30 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL
21642173

21652174
def isAny = wideTp.typeSymbol == AnyClass
21662175

2176+
// we use subtyping as a model for implication between instanceof tests
2177+
// i.e., when S <:< T we assume x.isInstanceOf[S] implies x.isInstanceOf[T]
2178+
// unfortunately this is not true in general:
2179+
// SI-6022 expects instanceOfTpImplies(ProductClass.tpe, AnyRefClass.tpe)
2180+
private def instanceOfTpImplies(tp: Type, tpImplied: Type) = {
2181+
val tpValue = tp.typeSymbol.isPrimitiveValueClass
2182+
2183+
// pretend we're comparing to Any when we're actually comparing to AnyVal or AnyRef
2184+
// (and the subtype is respectively a value type or not a value type)
2185+
// this allows us to reuse subtyping as a model for implication between instanceOf tests
2186+
// the latter don't see a difference between AnyRef, Object or Any when comparing non-value types -- SI-6022
2187+
val tpImpliedNormalizedToAny =
2188+
if ((tpValue && tpImplied =:= AnyValClass.tpe) ||
2189+
(!tpValue && tpImplied =:= AnyRefClass.tpe)) AnyClass.tpe
2190+
else tpImplied
2191+
2192+
tp <:< tpImpliedNormalizedToAny
2193+
}
2194+
21672195
final def implies(other: Const): Boolean = {
21682196
val r = (this, other) match {
21692197
case (_: ValueConst, _: ValueConst) => this == other // hashconsed
2170-
case (_: ValueConst, _: TypeConst) => tp <:< other.tp
2171-
case (_: TypeConst, _) => tp <:< other.tp
2198+
case (_: ValueConst, _: TypeConst) => instanceOfTpImplies(tp, other.tp)
2199+
case (_: TypeConst, _) => instanceOfTpImplies(tp, other.tp)
21722200
case _ => false
21732201
}
21742202
// if(r) patmatDebug("implies : "+(this, other))
@@ -2185,12 +2213,12 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL
21852213
// this causes false negative for unreachability, but that's ok:
21862214
// example: val X = 1; val Y = 1; (2: Int) match { case X => case Y => /* considered reachable */ }
21872215
case (_: ValueConst, _: ValueConst) => this != other
2188-
case (_: ValueConst, _: TypeConst) => !((tp <:< other.tp) || (other.tp <:< wideTp))
2189-
case (_: TypeConst, _: ValueConst) => !((other.tp <:< tp) || (tp <:< other.wideTp))
2190-
case (_: TypeConst, _: TypeConst) => !((tp <:< other.tp) || (other.tp <:< tp))
2216+
case (_: ValueConst, _: TypeConst) => !(instanceOfTpImplies(tp, other.tp) || instanceOfTpImplies(other.tp, wideTp))
2217+
case (_: TypeConst, _: ValueConst) => !(instanceOfTpImplies(other.tp, tp) || instanceOfTpImplies(tp, other.wideTp))
2218+
case (_: TypeConst, _: TypeConst) => !(instanceOfTpImplies(tp, other.tp) || instanceOfTpImplies(other.tp, tp))
21912219
case _ => false
21922220
}
2193-
// if(r) patmatDebug("excludes : "+(this, this.tp, other, other.tp, (tp <:< other.tp), (tp <:< other.wideTp), (other.tp <:< tp), (other.tp <:< wideTp)))
2221+
// if(r) patmatDebug("excludes : "+(this, this.tp, other, other.tp))
21942222
// else patmatDebug("NOT excludes: "+(this, other))
21952223
r
21962224
}
@@ -2752,7 +2780,7 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL
27522780
def simplify(c: Cond): Set[Cond] = c match {
27532781
case AndCond(a, b) => simplify(a) ++ simplify(b)
27542782
case OrCond(_, _) => Set(FalseCond) // TODO: make more precise
2755-
case NonNullCond(_) => Set(TrueCond) // not worth remembering
2783+
case NonNullCond(_) => Set(TrueCond) // not worth remembering
27562784
case _ => Set(c)
27572785
}
27582786
val conds = simplify(cond)
@@ -2768,7 +2796,7 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL
27682796
case (priorTest, deps) =>
27692797
((simplify(priorTest.cond) == nonTrivial) || // our conditions are implied by priorTest if it checks the same thing directly
27702798
(nonTrivial subsetOf deps) // or if it depends on a superset of our conditions
2771-
) && (deps subsetOf tested) // the conditions we've tested when we are here in the match satisfy the prior test, and hence what it tested
2799+
) && (deps subsetOf tested) // the conditions we've tested when we are here in the match satisfy the prior test, and hence what it tested
27722800
} foreach {
27732801
case (priorTest, _) =>
27742802
// if so, note the dependency in both tests

test/files/pos/t6022.flags

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
-Xfatal-warnings

test/files/pos/t6022.scala

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
class Test {
2+
(null: Any) match {
3+
case x: AnyRef if false =>
4+
case list: Option[_] =>
5+
case product: Product => // change Product to String and it's all good
6+
}
7+
}

0 commit comments

Comments
 (0)