diff --git a/compiler/src/dotty/tools/dotc/reporting/diagnostic/ErrorMessageID.java b/compiler/src/dotty/tools/dotc/reporting/diagnostic/ErrorMessageID.java index 4f5ed0ef4ab6..32dac326b93d 100644 --- a/compiler/src/dotty/tools/dotc/reporting/diagnostic/ErrorMessageID.java +++ b/compiler/src/dotty/tools/dotc/reporting/diagnostic/ErrorMessageID.java @@ -118,6 +118,7 @@ public enum ErrorMessageID { StaticFieldsOnlyAllowedInObjectsID, CyclicInheritanceID, UnableToExtendSealedClassID, + UnableToEmitSwitchID, ; public int errorNumber() { diff --git a/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala b/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala index 94192d13ee57..eb2947893753 100644 --- a/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala +++ b/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala @@ -1972,4 +1972,33 @@ object messages { val msg = hl"Cannot extend ${"sealed"} $pclazz in a different source file" val explanation = "A sealed class or trait can only be extended in the same file as its declaration" } + + case class UnableToEmitSwitch()(implicit ctx: Context) + extends Message(UnableToEmitSwitchID) { + val kind = "Syntax" + val msg = hl"Could not emit switch for ${"@switch"} annotated match" + val explanation = { + val codeExample = + """val ConstantB = 'B' + |final val ConstantC = 'C' + |def tokenMe(ch: Char) = (ch: @switch) match { + | case '\t' | '\n' => 1 + | case 'A' => 2 + | case ConstantB => 3 // a non-literal may prevent switch generation: this would not compile + | case ConstantC => 4 // a constant value is allowed + | case _ => 5 + |}""".stripMargin + + hl"""If annotated with ${"@switch"}, the compiler will verify that the match has been compiled to a + |tableswitch or lookupswitch and issue an error if it instead compiles into a series of conditional + |expressions. Example usage: + | + |$codeExample + | + |The compiler will not apply the optimisation if: + |- the matched value is not of type ${"Int"}, ${"Byte"}, ${"Short"} or ${"Char"} + |- the matched value is not a constant literal + |- there are less than three cases""" + } + } } diff --git a/compiler/src/dotty/tools/dotc/transform/PatternMatcher.scala b/compiler/src/dotty/tools/dotc/transform/PatternMatcher.scala index a16f7ab427f7..8d217cf96e0d 100644 --- a/compiler/src/dotty/tools/dotc/transform/PatternMatcher.scala +++ b/compiler/src/dotty/tools/dotc/transform/PatternMatcher.scala @@ -14,6 +14,7 @@ import Decorators._ import patmat.Space import NameKinds.{UniqueNameKind, PatMatStdBinderName, PatMatCaseName} import config.Printers.patmatch +import reporting.diagnostic.messages._ /** The pattern matching transform. * After this phase, the only Match nodes remaining in the code are simple switches @@ -908,7 +909,7 @@ object PatternMatcher { tpes.toSet.size: Int // without the type ascription, testPickling fails because of #2840. } if (numConsts(resultCases) < numConsts(original.cases)) - ctx.warning(i"could not emit switch for @switch annotated match", original.pos) + ctx.warning(UnableToEmitSwitch(), original.pos) case _ => }