Skip to content

Commit d514728

Browse files
committed
Fix #1258: correct behavior for annotated values
Annotated values are encapsulated in a `ConcreteAnnotation`, as such, the statement `tpe isRef defn.IntClass` would yield false despite the annotated reference being an Int. The tpe is now unwrapped if it has an annotation. If the transformation fails despite having the annotation the compiler will warn.
1 parent 3aa22cc commit d514728

File tree

4 files changed

+50
-24
lines changed

4 files changed

+50
-24
lines changed

src/dotty/tools/dotc/core/Definitions.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -479,6 +479,8 @@ class Definitions {
479479
def TASTYLongSignatureAnnot(implicit ctx: Context) = TASTYLongSignatureAnnotType.symbol.asClass
480480
lazy val TailrecAnnotType = ctx.requiredClassRef("scala.annotation.tailrec")
481481
def TailrecAnnot(implicit ctx: Context) = TailrecAnnotType.symbol.asClass
482+
lazy val SwitchAnnotType = ctx.requiredClassRef("scala.annotation.switch")
483+
def SwitchAnnot(implicit ctx: Context) = SwitchAnnotType.symbol.asClass
482484
lazy val ThrowsAnnotType = ctx.requiredClassRef("scala.throws")
483485
def ThrowsAnnot(implicit ctx: Context) = ThrowsAnnotType.symbol.asClass
484486
lazy val TransientAnnotType = ctx.requiredClassRef("scala.transient")

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

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -307,10 +307,15 @@ class PatternMatcher extends MiniPhaseTransform with DenotTransformer {thisTrans
307307
// TODO Deal with guards?
308308

309309
def isSwitchableType(tpe: Type): Boolean = {
310-
(tpe isRef defn.IntClass) ||
311-
(tpe isRef defn.ByteClass) ||
312-
(tpe isRef defn.ShortClass) ||
313-
(tpe isRef defn.CharClass)
310+
val actualTpe = tpe match {
311+
case t @ AnnotatedType(inner, _) => inner
312+
case x => x
313+
}
314+
315+
(actualTpe isRef defn.IntClass) ||
316+
(actualTpe isRef defn.ByteClass) ||
317+
(actualTpe isRef defn.ShortClass) ||
318+
(actualTpe isRef defn.CharClass)
314319
}
315320

316321
object IntEqualityTestTreeMaker {
@@ -423,7 +428,8 @@ class PatternMatcher extends MiniPhaseTransform with DenotTransformer {thisTrans
423428
Match(intScrut, newCases :+ defaultCase)
424429
}
425430

426-
if (isSwitchableType(scrut.tpe.widenDealias) && cases.forall(isSwitchCase)) {
431+
val dealiased = scrut.tpe.widenDealias
432+
if (isSwitchableType(dealiased) && cases.forall(isSwitchCase)) {
427433
val valuesToCases = cases.map(extractSwitchCase)
428434
val values = valuesToCases.map(_._1)
429435
if (values.tails.exists { tail => tail.nonEmpty && tail.tail.exists(doOverlap(_, tail.head)) }) {
@@ -433,6 +439,8 @@ class PatternMatcher extends MiniPhaseTransform with DenotTransformer {thisTrans
433439
Some(makeSwitch(valuesToCases))
434440
}
435441
} else {
442+
if (dealiased hasAnnotation defn.SwitchAnnot)
443+
ctx.warning("failed to emit switch for `@switch` annotated match")
436444
None
437445
}
438446
}

test/test/DottyBytecodeTest.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ trait DottyBytecodeTest extends DottyTest {
107107
if (debug || !succ && !shouldFail || succ && shouldFail)
108108
instructions.foreach(Console.err.println)
109109

110-
succ
110+
succ && !shouldFail || shouldFail && !succ
111111
}
112112

113113
def sameBytecode(methA: MethodNode, methB: MethodNode) = {

test/test/DottyBytecodeTests.scala

Lines changed: 34 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -46,24 +46,40 @@ class TestBCode extends DottyBytecodeTest {
4646

4747
/** This test verifies that simple matches with `@switch` annotations are
4848
* indeed transformed to a switch
49-
*
50-
* FIXME: once issue#1258 is resolved, this should be enabled!
5149
*/
52-
//@Test def basicTransfromAnnotated = {
53-
// val source = """
54-
// |object Foo {
55-
// | import scala.annotation.switch
56-
// | def foo(i: Int) = (i: @switch) match {
57-
// | case 2 => println(2)
58-
// | case 1 => println(1)
59-
// | }
60-
// |}""".stripMargin
50+
@Test def basicTransfromAnnotated = {
51+
val source = """
52+
|object Foo {
53+
| import scala.annotation.switch
54+
| def foo(i: Int) = (i: @switch) match {
55+
| case 2 => println(2)
56+
| case 1 => println(1)
57+
| }
58+
|}""".stripMargin
6159

62-
// checkBCode(source) { dir =>
63-
// val moduleIn = dir.lookupName("Foo$.class", directory = false)
64-
// val moduleNode = loadClassNode(moduleIn.input)
65-
// val methodNode = getMethod(moduleNode, "foo")
66-
// assert(verifySwitch(methodNode))
67-
// }
68-
//}
60+
checkBCode(source) { dir =>
61+
val moduleIn = dir.lookupName("Foo$.class", directory = false)
62+
val moduleNode = loadClassNode(moduleIn.input)
63+
val methodNode = getMethod(moduleNode, "foo")
64+
assert(verifySwitch(methodNode))
65+
}
66+
}
67+
68+
@Test def failTransform = {
69+
val source = """
70+
|object Foo {
71+
| import scala.annotation.switch
72+
| def foo(i: Any) = (i: @switch) match {
73+
| case x: String => println("string!")
74+
| case x :: xs => println("list!")
75+
| }
76+
|}""".stripMargin
77+
checkBCode(source) { dir =>
78+
val moduleIn = dir.lookupName("Foo$.class", directory = false)
79+
val moduleNode = loadClassNode(moduleIn.input)
80+
val methodNode = getMethod(moduleNode, "foo")
81+
82+
assert(verifySwitch(methodNode, shouldFail = true))
83+
}
84+
}
6985
}

0 commit comments

Comments
 (0)