Skip to content

Commit

Permalink
Exhaustivity: don't widen & then warn on singleton types
Browse files Browse the repository at this point in the history
  • Loading branch information
dwijnand committed Sep 22, 2020
1 parent 431994b commit 2fe421a
Show file tree
Hide file tree
Showing 10 changed files with 53 additions and 26 deletions.
21 changes: 15 additions & 6 deletions src/compiler/scala/tools/nsc/transform/CleanUp.scala
Expand Up @@ -398,12 +398,15 @@ abstract class CleanUp extends Statics with Transform with ast.TreeDSL {

// transform scrutinee of all matches to ints
def transformSwitch(sw: Match): Tree = { import CODE._
sw.selector.tpe match {
sw.selector.tpe.widen match {
case IntTpe => sw // can switch directly on ints
case StringTpe =>
// these assumptions about the shape of the tree are justified by the codegen in MatchOptimization
val Match(Typed(selTree: Ident, _), cases) = sw
val sel = selTree.symbol
val Match(Typed(selTree, _), cases) = sw
val selArg = selTree match {
case x: Ident => REF(x.symbol)
case x: Literal => x
}
val restpe = sw.tpe
val swPos = sw.pos.focus

Expand All @@ -429,7 +432,7 @@ abstract class CleanUp extends Statics with Transform with ast.TreeDSL {
*/

val stats = mutable.ListBuffer.empty[Tree]
var failureBody = Throw(New(definitions.MatchErrorClass.tpe_*, REF(sel))) : Tree
var failureBody = Throw(New(definitions.MatchErrorClass.tpe_*, selArg)) : Tree

// genbcode isn't thrilled about seeing labels with Unit arguments, so `success`'s type is one of
// `${sw.tpe} => ${sw.tpe}` or `() => Unit` depending.
Expand All @@ -447,7 +450,13 @@ abstract class CleanUp extends Statics with Transform with ast.TreeDSL {
val failure = currentOwner.newLabel(unit.freshTermName("matchEnd"), swPos).setInfo(MethodType(Nil, restpe))
def fail(): Tree = atPos(swPos) { Apply(REF(failure), Nil) }

val newSel = atPos(sel.pos) { IF (sel OBJ_EQ NULL) THEN LIT(0) ELSE (Apply(REF(sel) DOT Object_hashCode, Nil)) }
val ifNull = LIT(0)
val noNull = Apply(selArg DOT Object_hashCode, Nil)

val newSel = selTree match {
case _: Ident => atPos(selTree.symbol.pos) { IF(selTree.symbol OBJ_EQ NULL) THEN ifNull ELSE noNull }
case x: Literal => atPos(selTree.pos) { if (x.value.value == null) ifNull else noNull }
}
val casesByHash =
cases.flatMap {
case cd@CaseDef(StringsPattern(strs), _, body) =>
Expand All @@ -465,7 +474,7 @@ abstract class CleanUp extends Statics with Transform with ast.TreeDSL {
case (next, (pat, jump, pos)) =>
val comparison = if (pat == null) Object_eq else Object_equals
atPos(pos) {
IF(LIT(pat) DOT comparison APPLY REF(sel)) THEN (REF(jump) APPLY Nil) ELSE next
IF(LIT(pat) DOT comparison APPLY selArg) THEN (REF(jump) APPLY Nil) ELSE next
}
}
CaseDef(LIT(hash), EmptyTree, newBody)
Expand Down
Expand Up @@ -67,7 +67,7 @@ trait MatchTranslation {
private lazy val extractor = ExtractorCall(tree)

def pos = tree.pos
def tpe = binder.info.dealiasWiden // the type of the variable bound to the pattern
def tpe = binder.info.dealias // the type of the variable bound to the pattern
def pt = unbound match {
case Star(tpt) => this glbWith seqType(tpt.tpe)
case TypeBound(tpe) => tpe
Expand Down Expand Up @@ -217,7 +217,7 @@ trait MatchTranslation {

val start = if (StatisticsStatics.areSomeColdStatsEnabled) statistics.startTimer(statistics.patmatNanos) else null

val selectorTp = repeatedToSeq(elimAnonymousClass(selector.tpe.widen.withoutAnnotations))
val selectorTp = repeatedToSeq(elimAnonymousClass(selector.tpe.withoutAnnotations))

// when one of the internal cps-type-state annotations is present, strip all CPS annotations
val origPt = removeCPSFromPt(match_.tpe)
Expand Down
6 changes: 6 additions & 0 deletions test/files/pos/t11603.scala
@@ -0,0 +1,6 @@
// scalac: -Werror
class C {
def m(x: true) = x match {
case true => println("the one true path")
}
}
3 changes: 3 additions & 0 deletions test/files/run/blame_eye_triple_eee-double.check
@@ -1,3 +1,6 @@
blame_eye_triple_eee-double.scala:49: warning: unreachable code
case _ => println("NaN matching was good")
^
if (NaN == NaN) is good
if (x == x) is good
if (x == NaN) is good
Expand Down
3 changes: 3 additions & 0 deletions test/files/run/blame_eye_triple_eee-float.check
@@ -1,3 +1,6 @@
blame_eye_triple_eee-float.scala:49: warning: unreachable code
case _ => println("NaN matching was good")
^
if (NaN == NaN) is good
if (x == x) is good
if (x == NaN) is good
Expand Down
8 changes: 4 additions & 4 deletions test/files/run/patmat-seq.check
Expand Up @@ -63,7 +63,7 @@ package <empty> {
()
};
def t: Any = {
case <synthetic> val x1: Int = 2;
case <synthetic> val x1: Int(2) = 2;
case16(){
<synthetic> val o18: collection.SeqFactory.UnapplySeqWrapper[Int] = A.unapplySeq(x1);
if (o18.isEmpty.unary_!)
Expand Down Expand Up @@ -193,7 +193,7 @@ package <empty> {
case39()
};
case39(){
matchEnd15(throw new MatchError(x1))
matchEnd15(throw new MatchError(2))
};
matchEnd15(x: Any){
x
Expand Down Expand Up @@ -264,7 +264,7 @@ package <empty> {
()
};
def t(): Object = {
case <synthetic> val x1: Int = 2;
case <synthetic> val x1: Int(2) = 2;
case16(){
<synthetic> val o18: scala.collection.SeqOps = A.unapplySeq(x1);
if (scala.collection.SeqFactory.UnapplySeqWrapper.isEmpty$extension(o18).unary_!())
Expand Down Expand Up @@ -394,7 +394,7 @@ package <empty> {
case39()
};
case39(){
matchEnd15(throw new MatchError(scala.Int.box(x1)))
matchEnd15(throw new MatchError(scala.Int.box(2)))
};
matchEnd15(x: Object){
x
Expand Down
6 changes: 3 additions & 3 deletions test/files/run/string-switch-pos.check
Expand Up @@ -30,15 +30,15 @@
[56]0
else
[56][56]x1.hashCode() match {
[75:81]case [56]2031744 => [75:81]if ([75][75][75]"AaAa".equals([75]x1))
[75:81]case [56]2031744 => [75:81]if ([75][75][75]"AaAa".equals([56]x1))
[75][75]case1()
else
[56][56]matchEnd2()
[133:139]case [56]2062528 => [133:139]if ([133][133][133]"BbBb".equals([133]x1))
[133:139]case [56]2062528 => [133:139]if ([133][133][133]"BbBb".equals([56]x1))
[133][133]case3()
else
[56][56]matchEnd2()
[104:110]case [56]3003444 => [104:110]if ([104][104][104]"asdf".equals([104]x1))
[104:110]case [56]3003444 => [104:110]if ([104][104][104]"asdf".equals([56]x1))
[104][104]case2()
else
[56][56]matchEnd2()
Expand Down
22 changes: 11 additions & 11 deletions test/files/run/t6288.check
Expand Up @@ -7,9 +7,9 @@
};
[17:60]def unapply([29:35]z: [32:35]<type: [32:35]scala.Any>): [21]Option[Int] = [52:60][52:56][52:56]new [52:56]Some[Int]([57:59]-1);
[64:66]{
[64:66]case <synthetic> val x1: [64]String = [64:66]"";
[64:66]case <synthetic> val x1: [64]String("") = [64:66]"";
[64:66]case5(){
[84:93]if ([89][89]x1.ne([89]null))
[84:93]if ([89][89][89]"".ne([89]null))
[84:89]{
[84:89]<synthetic> val o7: [84]Option[Int] = [84:89][84:89]Case3.unapply([84]x1);
[97:99]if ([84]o7.isEmpty.unary_!)
Expand All @@ -21,7 +21,7 @@
[89][89]case6()
};
[64:66]case6(){
[64:66][64:66]matchEnd4([64:66]throw [64:66][64:66][64:66]new [64:66]MatchError([64:66]x1))
[64:66][64:66]matchEnd4([64:66]throw [64:66][64:66][64:66]new [64:66]MatchError([64:66]""))
};
[64:66]matchEnd4(x: [NoPosition]Unit){
[64:66]x
Expand All @@ -35,9 +35,9 @@
};
[123:171]def unapplySeq([138:144]z: [141:144]<type: [141:144]scala.Any>): [127]Option[List[Int]] = [167:171]scala.None;
[175:177]{
[175:177]case <synthetic> val x1: [175]String = [175:177]"";
[175:177]case <synthetic> val x1: [175]String("") = [175:177]"";
[175:177]case5(){
[195:204]if ([200][200]x1.ne([200]null))
[195:204]if ([200][200][200]"".ne([200]null))
[195:200]{
[195:200]<synthetic> val o7: [195]Option[List[Int]] = [195:200][195:200]Case4.unapplySeq([195]x1);
[208:210]if ([195][195]o7.isEmpty.unary_!.&&([195][195][195][195]o7.get.!=([195]null).&&([195][195][195][195]o7.get.lengthCompare([195]1).==([195]0))))
Expand All @@ -49,7 +49,7 @@
[200][200]case6()
};
[175:177]case6(){
[175:177][175:177]matchEnd4([175:177]throw [175:177][175:177][175:177]new [175:177]MatchError([175:177]x1))
[175:177][175:177]matchEnd4([175:177]throw [175:177][175:177][175:177]new [175:177]MatchError([175:177]""))
};
[175:177]matchEnd4(x: [NoPosition]Unit){
[175:177]x
Expand All @@ -63,9 +63,9 @@
};
[234:269]def unapply([246:252]z: [249:252]<type: [249:252]scala.Any>): [238]Boolean = [265:269]true;
[273:275]{
[273:275]case <synthetic> val x1: [273]String = [273:275]"";
[273:275]case <synthetic> val x1: [273]String("") = [273:275]"";
[273:275]case5(){
[293:300]if ([298][298]x1.ne([298]null))
[293:300]if ([298][298][298]"".ne([298]null))
[293:298]{
[293:298]<synthetic> val o7: [293]Option[List[Int]] = [293:298][293:298]Case4.unapplySeq([293]x1);
[304:306]if ([293][293]o7.isEmpty.unary_!.&&([293][293][293][293]o7.get.!=([293]null).&&([293][293][293][293]o7.get.lengthCompare([293]0).==([293]0))))
Expand All @@ -77,7 +77,7 @@
[298][298]case6()
};
[273:275]case6(){
[273:275][273:275]matchEnd4([273:275]throw [273:275][273:275][273:275]new [273:275]MatchError([273:275]x1))
[273:275][273:275]matchEnd4([273:275]throw [273:275][273:275][273:275]new [273:275]MatchError([273:275]""))
};
[273:275]matchEnd4(x: [NoPosition]Unit){
[273:275]x
Expand All @@ -91,7 +91,7 @@
};
[330:373]def unapply([342:348]z: [345:348]<type: [345:348]scala.Int>): [334]Option[Int] = [365:373][365:369][365:369]new [365:369]Some[Int]([370:372]-1);
[377:378]{
[377:378]case <synthetic> val x1: [377]Int = [377:378]0;
[377:378]case <synthetic> val x1: [377]Int(0) = [377:378]0;
[377:378]case5()[396:401]{
[396:401]<synthetic> val o7: [396]Option[Int] = [396:401][396:401]Case6.unapply([396]x1);
[409:411]if ([396]o7.isEmpty.unary_!)
Expand All @@ -100,7 +100,7 @@
[396][396]case6()
};
[377:378]case6(){
[377:378][377:378]matchEnd4([377:378]throw [377:378][377:378][377:378]new [377:378]MatchError([377:378]x1))
[377:378][377:378]matchEnd4([377:378]throw [377:378][377:378][377:378]new [377:378]MatchError([377:378]0))
};
[377:378]matchEnd4(x: [NoPosition]Unit){
[377:378]x
Expand Down
3 changes: 3 additions & 0 deletions test/files/run/t7039.check
@@ -1 +1,4 @@
t7039.scala:8: warning: unreachable code
case UnapplySeqTest(5, 1) => println("Matched!") // compiles
^
null matched
3 changes: 3 additions & 0 deletions test/files/run/virtpatmat_literal.check
@@ -1,3 +1,6 @@
virtpatmat_literal.scala:6: warning: unreachable code
case `a` => println("FAILED")
^
OK
OK
OK

0 comments on commit 2fe421a

Please sign in to comment.