Skip to content

Commit 97c3383

Browse files
author
Adriaan Moors
committed
better unreachability for selections
Consts are hashconsed modulo static-approximation-for-dynamic-value-equality thus, two value-equality tests in patterns should reuse the same ValueConst if and only if the tested values are guaranteed to be equal in all possible executions the implementation uses unique types to track unique consts for an Ident with a stable symbol, we simply use the corresponding singleton type for a Select, we have to indirect some more: we store all the unique trees we've encountered and a unique type for each of them this unique type is then used to find the uniqut const that approximates the run-time value this may seem roundabout, but we need to standardize on types for representing "value" tests, as a type test against a singleton type must give rise to the same ValueConst as a value test using a tree that refers to the same symbol as the singleton type test
1 parent 43ae825 commit 97c3383

File tree

4 files changed

+51
-12
lines changed

4 files changed

+51
-12
lines changed

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

Lines changed: 34 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1429,6 +1429,12 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL
14291429
// (Select(codegen._asInstanceOf(testedBinder, expectedTp), outer)) OBJ_EQ expectedOuter
14301430
// }
14311431

1432+
// TODO: improve, e.g., for constants
1433+
def sameValue(a: Tree, b: Tree): Boolean = (a eq b) || ((a, b) match {
1434+
case (_ : Ident, _ : Ident) => a.symbol eq b.symbol
1435+
case _ => false
1436+
})
1437+
14321438

14331439
// returns (tree, tests), where `tree` will be used to refer to `root` in `tests`
14341440
abstract class TreeMakersToConds(val root: Symbol) {
@@ -1476,13 +1482,6 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL
14761482
// patmatDebug("accumSubst: "+ accumSubst)
14771483
}
14781484

1479-
1480-
// TODO: improve, e.g., for constants
1481-
def sameValue(a: Tree, b: Tree): Boolean = (a eq b) || ((a, b) match {
1482-
case (_ : Ident, _ : Ident) => a.symbol eq b.symbol
1483-
case _ => false
1484-
})
1485-
14861485
// hashconsing trees (modulo value-equality)
14871486
def unique(t: Tree, tpOverride: Type = NoType): Tree =
14881487
trees find (a => a.correspondsStructure(t)(sameValue)) match {
@@ -2075,7 +2074,7 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL
20752074
// a literal constant becomes ConstantType(Constant(v)) when the type allows it (roughly, anyval + string + null)
20762075
// equality between variables: SingleType(x) (note that pattern variables cannot relate to each other -- it's always patternVar == nonPatternVar)
20772076
object Const {
2078-
def resetUniques() = {_nextTypeId = 0; _nextValueId = 0; uniques.clear()} // patmatDebug("RESET")
2077+
def resetUniques() = {_nextTypeId = 0; _nextValueId = 0; uniques.clear() ; trees.clear() } // patmatDebug("RESET")
20792078

20802079
private var _nextTypeId = 0
20812080
def nextTypeId = {_nextTypeId += 1; _nextTypeId}
@@ -2093,6 +2092,27 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL
20932092
uniques(tp) = fresh
20942093
fresh
20952094
})
2095+
2096+
private val trees = collection.mutable.HashSet.empty[Tree]
2097+
2098+
// hashconsing trees (modulo value-equality)
2099+
private[SymbolicMatchAnalysis] def uniqueTpForTree(t: Tree): Type =
2100+
// a new type for every unstable symbol -- only stable value are uniqued
2101+
// technically, an unreachable value may change between cases
2102+
// thus, the failure of a case that matches on a mutable value does not exclude the next case succeeding
2103+
// (and thuuuuus, the latter case must be considered reachable)
2104+
if (!t.symbol.isStable) t.tpe.narrow
2105+
else trees find (a => a.correspondsStructure(t)(sameValue)) match {
2106+
case Some(orig) =>
2107+
// patmatDebug("unique: "+ (orig, orig.tpe))
2108+
orig.tpe
2109+
case _ =>
2110+
// duplicate, don't mutate old tree (TODO: use a map tree -> type instead?)
2111+
val treeWithNarrowedType = t.duplicate setType t.tpe.narrow
2112+
// patmatDebug("uniqued: "+ (t, t.tpe, treeWithNarrowedType.tpe))
2113+
trees += treeWithNarrowedType
2114+
treeWithNarrowedType.tpe
2115+
}
20962116
}
20972117

20982118
sealed abstract class Const extends AbsConst {
@@ -2181,11 +2201,13 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL
21812201
case Literal(c) =>
21822202
if (c.tpe.typeSymbol == UnitClass) c.tpe
21832203
else ConstantType(c)
2184-
case p if p.symbol.isStable =>
2204+
case Ident(_) if p.symbol.isStable =>
2205+
// for Idents, can encode uniqueness of symbol as uniqueness of the corresponding singleton type
2206+
// for Selects, which are handled by the next case, the prefix of the select varies independently of the symbol (see pos/virtpatmat_unreach_select.scala)
21852207
singleType(tp.prefix, p.symbol)
2186-
case x =>
2187-
// TODO: better type
2188-
x.tpe.narrow
2208+
case _ =>
2209+
// patmatDebug("unique type for "+(p, Const.uniqueTpForTree(p)))
2210+
Const.uniqueTpForTree(p)
21892211
}
21902212

21912213
val toString =
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
virtpatmat_unreach_select.scala:10: error: unreachable code
2+
case WARNING.id => // unreachable
3+
^
4+
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: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
class Test {
2+
object severity extends Enumeration
3+
class Severity(val id: Int) extends severity.Value
4+
val INFO = new Severity(0)
5+
val WARNING = new Severity(1)
6+
7+
(0: Int) match {
8+
case WARNING.id =>
9+
case INFO.id => // reachable
10+
case WARNING.id => // unreachable
11+
}
12+
}

0 commit comments

Comments
 (0)