Skip to content

Commit b59eced

Browse files
committed
Fix @switch warnings for matches on value classes
Matches on value classes that have an underlying switchable type may be emitted as tableswitch or lookupswitch, but the shape of the result tree differs from ordinary switch-compiled matches. Previously, this caused a spurious warning to be issued if such a match was annotated with @switch, as none of the result cases were discovered by the warning logic, and hence the match was warned as having too few cases. With the warning for too few cases now removed, we have the opposite issue: in no circumstance is a switch warning issued for @switch annotated matches on value classes. This commit attempts to address this issue and restore the @switch warnings for those matches on value classes where a tableswitch or lookupswitch is not emitted. A complicating factor is that the original case types for matches on value class extractors are not singleton types, and so counting the number of unique types is not useful for determining the number of original cases.
1 parent f2a2c04 commit b59eced

File tree

3 files changed

+36
-1
lines changed

3 files changed

+36
-1
lines changed

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

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -973,15 +973,19 @@ object PatternMatcher {
973973
val resultCases = result match
974974
case Match(_, cases) => cases
975975
case Block(_, Match(_, cases)) => cases
976+
case Block((_: ValDef) :: Block(_, Match(_, cases)) :: Nil, _) => cases
976977
case _ => Nil
978+
val caseThreshold =
979+
if ValueClasses.isDerivedValueClass(tpt.tpe.widen.typeSymbol) then 1
980+
else MinSwitchCases
977981
def typesInPattern(pat: Tree): List[Type] = pat match
978982
case Alternative(pats) => pats.flatMap(typesInPattern)
979983
case _ => pat.tpe :: Nil
980984
def typesInCases(cdefs: List[CaseDef]): List[Type] =
981985
cdefs.flatMap(cdef => typesInPattern(cdef.pat))
982986
def numTypes(cdefs: List[CaseDef]): Int =
983987
typesInCases(cdefs).toSet.size: Int // without the type ascription, testPickling fails because of #2840.
984-
if numTypes(original.cases) >= MinSwitchCases && numTypes(resultCases) < numTypes(original.cases) then
988+
if numTypes(original.cases) >= caseThreshold && numTypes(resultCases) < numTypes(original.cases) then
985989
patmatch.println(i"switch warning for ${ctx.compilationUnit}")
986990
patmatch.println(i"original types: ${typesInCases(original.cases)}%, %")
987991
patmatch.println(i"switch types : ${typesInCases(resultCases)}%, %")

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

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,4 +76,25 @@ object Main {
7676
case 1 | 2 | 3 => true
7777
case _ => false
7878
}
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+
}
79100
}

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'

0 commit comments

Comments
 (0)