diff options
author | Felix Mulder <felix.mulder@gmail.com> | 2016-05-17 17:39:55 +0200 |
---|---|---|
committer | Felix Mulder <felix.mulder@gmail.com> | 2016-05-17 17:39:55 +0200 |
commit | d5147288df4cc05ac4d761bd0e451f724c474ae1 (patch) | |
tree | b2135b22adb44929fe8f4499665c0099432b9c0b | |
parent | 3aa22cc24f601cb70dcff7247af95ad9fa403de5 (diff) | |
download | dotty-d5147288df4cc05ac4d761bd0e451f724c474ae1.tar.gz dotty-d5147288df4cc05ac4d761bd0e451f724c474ae1.tar.bz2 dotty-d5147288df4cc05ac4d761bd0e451f724c474ae1.zip |
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.
-rw-r--r-- | src/dotty/tools/dotc/core/Definitions.scala | 2 | ||||
-rw-r--r-- | src/dotty/tools/dotc/transform/PatternMatcher.scala | 18 | ||||
-rw-r--r-- | test/test/DottyBytecodeTest.scala | 2 | ||||
-rw-r--r-- | test/test/DottyBytecodeTests.scala | 52 |
4 files changed, 50 insertions, 24 deletions
diff --git a/src/dotty/tools/dotc/core/Definitions.scala b/src/dotty/tools/dotc/core/Definitions.scala index 7f59cbed0..e96636282 100644 --- a/src/dotty/tools/dotc/core/Definitions.scala +++ b/src/dotty/tools/dotc/core/Definitions.scala @@ -479,6 +479,8 @@ class Definitions { def TASTYLongSignatureAnnot(implicit ctx: Context) = TASTYLongSignatureAnnotType.symbol.asClass lazy val TailrecAnnotType = ctx.requiredClassRef("scala.annotation.tailrec") def TailrecAnnot(implicit ctx: Context) = TailrecAnnotType.symbol.asClass + lazy val SwitchAnnotType = ctx.requiredClassRef("scala.annotation.switch") + def SwitchAnnot(implicit ctx: Context) = SwitchAnnotType.symbol.asClass lazy val ThrowsAnnotType = ctx.requiredClassRef("scala.throws") def ThrowsAnnot(implicit ctx: Context) = ThrowsAnnotType.symbol.asClass lazy val TransientAnnotType = ctx.requiredClassRef("scala.transient") diff --git a/src/dotty/tools/dotc/transform/PatternMatcher.scala b/src/dotty/tools/dotc/transform/PatternMatcher.scala index 740fd5460..f8f32131b 100644 --- a/src/dotty/tools/dotc/transform/PatternMatcher.scala +++ b/src/dotty/tools/dotc/transform/PatternMatcher.scala @@ -307,10 +307,15 @@ class PatternMatcher extends MiniPhaseTransform with DenotTransformer {thisTrans // TODO Deal with guards? def isSwitchableType(tpe: Type): Boolean = { - (tpe isRef defn.IntClass) || - (tpe isRef defn.ByteClass) || - (tpe isRef defn.ShortClass) || - (tpe isRef defn.CharClass) + val actualTpe = tpe match { + case t @ AnnotatedType(inner, _) => inner + case x => x + } + + (actualTpe isRef defn.IntClass) || + (actualTpe isRef defn.ByteClass) || + (actualTpe isRef defn.ShortClass) || + (actualTpe isRef defn.CharClass) } object IntEqualityTestTreeMaker { @@ -423,7 +428,8 @@ class PatternMatcher extends MiniPhaseTransform with DenotTransformer {thisTrans Match(intScrut, newCases :+ defaultCase) } - if (isSwitchableType(scrut.tpe.widenDealias) && cases.forall(isSwitchCase)) { + val dealiased = scrut.tpe.widenDealias + if (isSwitchableType(dealiased) && cases.forall(isSwitchCase)) { val valuesToCases = cases.map(extractSwitchCase) val values = valuesToCases.map(_._1) if (values.tails.exists { tail => tail.nonEmpty && tail.tail.exists(doOverlap(_, tail.head)) }) { @@ -433,6 +439,8 @@ class PatternMatcher extends MiniPhaseTransform with DenotTransformer {thisTrans Some(makeSwitch(valuesToCases)) } } else { + if (dealiased hasAnnotation defn.SwitchAnnot) + ctx.warning("failed to emit switch for `@switch` annotated match") None } } diff --git a/test/test/DottyBytecodeTest.scala b/test/test/DottyBytecodeTest.scala index 3814b03bd..1acb85cbf 100644 --- a/test/test/DottyBytecodeTest.scala +++ b/test/test/DottyBytecodeTest.scala @@ -107,7 +107,7 @@ trait DottyBytecodeTest extends DottyTest { if (debug || !succ && !shouldFail || succ && shouldFail) instructions.foreach(Console.err.println) - succ + succ && !shouldFail || shouldFail && !succ } def sameBytecode(methA: MethodNode, methB: MethodNode) = { diff --git a/test/test/DottyBytecodeTests.scala b/test/test/DottyBytecodeTests.scala index 6b1562741..5b79772c4 100644 --- a/test/test/DottyBytecodeTests.scala +++ b/test/test/DottyBytecodeTests.scala @@ -46,24 +46,40 @@ class TestBCode extends DottyBytecodeTest { /** This test verifies that simple matches with `@switch` annotations are * indeed transformed to a switch - * - * FIXME: once issue#1258 is resolved, this should be enabled! */ - //@Test def basicTransfromAnnotated = { - // val source = """ - // |object Foo { - // | import scala.annotation.switch - // | def foo(i: Int) = (i: @switch) match { - // | case 2 => println(2) - // | case 1 => println(1) - // | } - // |}""".stripMargin + @Test def basicTransfromAnnotated = { + val source = """ + |object Foo { + | import scala.annotation.switch + | def foo(i: Int) = (i: @switch) match { + | case 2 => println(2) + | case 1 => println(1) + | } + |}""".stripMargin - // checkBCode(source) { dir => - // val moduleIn = dir.lookupName("Foo$.class", directory = false) - // val moduleNode = loadClassNode(moduleIn.input) - // val methodNode = getMethod(moduleNode, "foo") - // assert(verifySwitch(methodNode)) - // } - //} + checkBCode(source) { dir => + val moduleIn = dir.lookupName("Foo$.class", directory = false) + val moduleNode = loadClassNode(moduleIn.input) + val methodNode = getMethod(moduleNode, "foo") + assert(verifySwitch(methodNode)) + } + } + + @Test def failTransform = { + val source = """ + |object Foo { + | import scala.annotation.switch + | def foo(i: Any) = (i: @switch) match { + | case x: String => println("string!") + | case x :: xs => println("list!") + | } + |}""".stripMargin + checkBCode(source) { dir => + val moduleIn = dir.lookupName("Foo$.class", directory = false) + val moduleNode = loadClassNode(moduleIn.input) + val methodNode = getMethod(moduleNode, "foo") + + assert(verifySwitch(methodNode, shouldFail = true)) + } + } } |