Skip to content

Commit 75b08ac

Browse files
authored
Merge pull request #9852 from griggt/fix-#9776
Fix #9776: Don't issue a @switch warning when fewer than 4 cases
2 parents e08fd59 + f8acad1 commit 75b08ac

File tree

13 files changed

+144
-39
lines changed

13 files changed

+144
-39
lines changed

compiler/src/dotty/tools/dotc/reporting/messages.scala

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1998,10 +1998,9 @@ import transform.SymUtils._
19981998
|whose behavior may have changed since version change."""
19991999
}
20002000

2001-
class UnableToEmitSwitch(tooFewCases: Boolean)(using Context)
2001+
class UnableToEmitSwitch()(using Context)
20022002
extends SyntaxMsg(UnableToEmitSwitchID) {
2003-
def tooFewStr: String = if (tooFewCases) " since there are not enough cases" else ""
2004-
def msg = em"Could not emit switch for ${hl("@switch")} annotated match$tooFewStr"
2003+
def msg = em"Could not emit switch for ${hl("@switch")} annotated match"
20052004
def explain = {
20062005
val codeExample =
20072006
"""val ConstantB = 'B'

compiler/src/dotty/tools/dotc/transform/PatternMatcher.scala

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -977,30 +977,32 @@ object PatternMatcher {
977977
/** If match is switch annotated, check that it translates to a switch
978978
* with at least as many cases as the original match.
979979
*/
980-
private def checkSwitch(original: Match, result: Tree) = original.selector match {
980+
private def checkSwitch(original: Match, result: Tree) = original.selector match
981981
case Typed(_, tpt) if tpt.tpe.hasAnnotation(defn.SwitchAnnot) =>
982-
val resultCases = result match {
982+
val resultCases = result match
983983
case Match(_, cases) => cases
984984
case Block(_, Match(_, cases)) => cases
985+
case Block((_: ValDef) :: Block(_, Match(_, cases)) :: Nil, _) => cases
985986
case _ => Nil
986-
}
987-
def typesInPattern(pat: Tree): List[Type] = pat match {
987+
val caseThreshold =
988+
if ValueClasses.isDerivedValueClass(tpt.tpe.typeSymbol) then 1
989+
else MinSwitchCases
990+
def typesInPattern(pat: Tree): List[Type] = pat match
988991
case Alternative(pats) => pats.flatMap(typesInPattern)
989992
case _ => pat.tpe :: Nil
990-
}
991993
def typesInCases(cdefs: List[CaseDef]): List[Type] =
992994
cdefs.flatMap(cdef => typesInPattern(cdef.pat))
993995
def numTypes(cdefs: List[CaseDef]): Int =
994996
typesInCases(cdefs).toSet.size: Int // without the type ascription, testPickling fails because of #2840.
995-
if (numTypes(resultCases) < numTypes(original.cases)) {
997+
val numTypesInOriginal = numTypes(original.cases)
998+
if numTypesInOriginal >= caseThreshold && numTypes(resultCases) < numTypesInOriginal then
996999
patmatch.println(i"switch warning for ${ctx.compilationUnit}")
9971000
patmatch.println(i"original types: ${typesInCases(original.cases)}%, %")
9981001
patmatch.println(i"switch types : ${typesInCases(resultCases)}%, %")
9991002
patmatch.println(i"tree = $result")
1000-
report.warning(UnableToEmitSwitch(numTypes(original.cases) < MinSwitchCases), original.srcPos)
1001-
}
1003+
report.warning(UnableToEmitSwitch(), original.srcPos)
10021004
case _ =>
1003-
}
1005+
end checkSwitch
10041006

10051007
val optimizations: List[(String, Plan => Plan)] = List(
10061008
"mergeTests" -> mergeTests,

tests/neg-custom-args/fatal-warnings/i3561.scala

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,4 @@ class Test {
1313
case '5' | Constant => 3
1414
case '4' => 4
1515
}
16-
17-
def test3(x: Any) = (x: @annotation.switch) match { // error: could not emit switch - too few cases
18-
case 1 => 1
19-
case 2 => 2
20-
case x: String => 4
21-
}
2216
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import scala.annotation.switch
2+
3+
sealed trait Fruit
4+
5+
object Fruit {
6+
case object Apple extends Fruit
7+
case object Banana extends Fruit
8+
case object Lemon extends Fruit
9+
case object Lime extends Fruit
10+
case object Orange extends Fruit
11+
12+
def isCitrus(fruit: Fruit): Boolean =
13+
(fruit: @switch) match { // error Could not emit switch for @switch annotated match
14+
case Orange => true
15+
case Lemon => true
16+
case Lime => true
17+
case _ => false
18+
}
19+
}
20+
21+
22+
sealed trait TaggedFruit {
23+
def tag: Int
24+
}
25+
26+
object TaggedFruit {
27+
case object Apple extends TaggedFruit {
28+
val tag = 1
29+
}
30+
case object Banana extends TaggedFruit {
31+
val tag = 2
32+
}
33+
case object Orange extends TaggedFruit {
34+
val tag = 3
35+
}
36+
37+
def isCitrus(fruit: TaggedFruit): Boolean =
38+
(fruit.tag: @switch) match { // error Could not emit switch for @switch annotated match
39+
case Apple.tag => true
40+
case 2 => true
41+
case 3 => true
42+
case _ => false
43+
}
44+
45+
// fewer than four cases, so no warning
46+
def succ1(fruit: TaggedFruit): Boolean =
47+
(fruit.tag: @switch) match {
48+
case 3 => false
49+
case 2 | Apple.tag => true
50+
}
51+
52+
// fewer than four cases, so no warning
53+
def succ2(fruit: TaggedFruit): Boolean =
54+
(fruit.tag: @switch) match {
55+
case 3 => false
56+
case 2 => true
57+
case Apple.tag => true
58+
}
59+
}

tests/neg-custom-args/fatal-warnings/switches.scala

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,13 +38,15 @@ object Main {
3838
// thinks a val in an object is constant... so naive
3939
def fail1(c: Char) = (c: @switch @unchecked) match { // error: Could not emit switch for @switch annotated match
4040
case 'A' => true
41+
case 'B' => true
4142
case Other.C1 => true
4243
case _ => false
4344
}
4445

4546
// more naivete
4647
def fail2(c: Char) = (c: @unchecked @switch) match { // error: Could not emit switch for @switch annotated match
4748
case 'A' => true
49+
case 'B' => true
4850
case Other.C3 => true
4951
case _ => false
5052
}
@@ -74,4 +76,25 @@ object Main {
7476
case 1 | 2 | 3 => true
7577
case _ => false
7678
}
79+
80+
case class IntAnyVal(x: Int) extends AnyVal
81+
82+
val Ten = IntAnyVal(10)
83+
def fail5(x: IntAnyVal) = (x: @switch) match { // error: Could not emit switch for @switch annotated match
84+
case IntAnyVal(1) => 0
85+
case Ten => 1
86+
case IntAnyVal(100) => 2
87+
case IntAnyVal(1000) => 3
88+
case IntAnyVal(10000) => 4
89+
}
90+
91+
// the generated lookupswitch covers only a subset of the cases
92+
final val One = IntAnyVal(1)
93+
def fail6(x: IntAnyVal) = (x: @switch) match { // error: Could not emit switch for @switch annotated match
94+
case One => 0
95+
case IntAnyVal(10) => 1
96+
case IntAnyVal(100) => 2
97+
case IntAnyVal(1000) => 3
98+
case IntAnyVal(10000) => 4
99+
}
77100
}

tests/untried/neg/t5830.scala renamed to tests/neg-custom-args/fatal-warnings/t5830.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import scala.annotation.switch
33
class Test {
44
def unreachable(ch: Char) = (ch: @switch) match {
55
case 'a' => println("b") // ok
6-
case 'a' => println("b") // unreachable
6+
case 'a' => println("b") // error: unreachable case
77
case 'c' =>
88
}
99
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import scala.annotation.switch
2+
3+
sealed trait Fruit
4+
5+
object Fruit {
6+
case object Apple extends Fruit
7+
case object Banana extends Fruit
8+
case object Orange extends Fruit
9+
10+
def isCitrus(fruit: Fruit): Boolean =
11+
(fruit: @switch) match {
12+
case Orange => true
13+
case _ => false
14+
}
15+
}
16+
17+
18+
sealed trait TaggedFruit {
19+
def tag: Int
20+
}
21+
22+
object TaggedFruit {
23+
case object Apple extends TaggedFruit {
24+
val tag = 1
25+
}
26+
case object Banana extends TaggedFruit {
27+
val tag = 2
28+
}
29+
case object Orange extends TaggedFruit {
30+
val tag = 3
31+
}
32+
33+
def isCitrus(fruit: TaggedFruit): Boolean =
34+
(fruit.tag: @switch) match {
35+
case 3 => true
36+
case _ => false
37+
}
38+
}

tests/pos-special/fatal-warnings/switches.scala

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,18 @@ class Test {
3333
case 1 | 2 | 3 => true
3434
case _ => false
3535
}
36+
37+
def test6(x: IntAnyVal) = (x: @switch) match {
38+
case IntAnyVal(1) => 0
39+
case IntAnyVal(10) => 1
40+
case IntAnyVal(100) => 2
41+
case IntAnyVal(1000) => 3
42+
case IntAnyVal(10000) => 4
43+
}
3644
}
3745

46+
case class IntAnyVal(x: Int) extends AnyVal
47+
3848
object Test {
3949
final val LF = '\u000A'
4050
final val CR = '\u000D'
File renamed without changes.

tests/untried/neg/switch.check

Lines changed: 0 additions & 9 deletions
This file was deleted.

tests/untried/neg/switch.flags

Lines changed: 0 additions & 1 deletion
This file was deleted.

tests/untried/neg/t5830.check

Lines changed: 0 additions & 9 deletions
This file was deleted.

tests/untried/neg/t5830.flags

Lines changed: 0 additions & 1 deletion
This file was deleted.

0 commit comments

Comments
 (0)