diff options
author | Paul Phillips <paulp@improving.org> | 2009-03-16 17:46:58 +0000 |
---|---|---|
committer | Paul Phillips <paulp@improving.org> | 2009-03-16 17:46:58 +0000 |
commit | 67c3c68da5de6284c5e37ef4b5f907d0079f0490 (patch) | |
tree | 0db5a40f781f2f9edb21fd4c20ce6608c8a5c147 | |
parent | 00d196adeeb3aa88f2afedcb5d7eb51f0fc19896 (diff) | |
download | scala-67c3c68da5de6284c5e37ef4b5f907d0079f0490.tar.gz scala-67c3c68da5de6284c5e37ef4b5f907d0079f0490.tar.bz2 scala-67c3c68da5de6284c5e37ef4b5f907d0079f0490.zip |
The birth of the @switch and @tailrec annotations.
They are located in package scala.annotation. Also in this patch:
* numerous test cases for both annotations
* addition of @tailrec and @switch in a few strategic locations
* fixes for critical section NewScanners methods which were not being
compiled into switches, immediately proving the value of @switch
* tail recursive implementations for Iterator.{ dropWhile, drop}
and List.dropWhile tagged with @tailrec, closing bug #1376
19 files changed, 301 insertions, 79 deletions
diff --git a/src/compiler/scala/tools/nsc/ast/parser/MarkupParsers.scala b/src/compiler/scala/tools/nsc/ast/parser/MarkupParsers.scala index 7a725bd82e..55695f725a 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/MarkupParsers.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/MarkupParsers.scala @@ -10,6 +10,7 @@ import scala.collection.mutable import scala.tools.nsc.util.{Position,NoPosition,SourceFile} import scala.xml.{Text, TextBuffer} import SourceFile.{SU,LF} +import scala.annotation.switch /** This trait ... * @@ -238,7 +239,7 @@ trait MarkupParsers {self: Parsers => val base = if (hex) 16 else 10 var i = 0 while (ch != ';') { - ch match { + (ch: @switch) match { case '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' => i = i * base + ch.asDigit case 'a' | 'b' | 'c' | 'd' | 'e' | 'f' diff --git a/src/compiler/scala/tools/nsc/ast/parser/NewScanners.scala b/src/compiler/scala/tools/nsc/ast/parser/NewScanners.scala index f9151a4b8d..db547b81db 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/NewScanners.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/NewScanners.scala @@ -8,6 +8,7 @@ package scala.tools.nsc.ast.parser import scala.tools.nsc.util.SourceFile._ import scala.tools.nsc.util._ +import scala.annotation.switch trait NewScanners { val global : Global @@ -586,10 +587,25 @@ trait NewScanners { } def isDigit(c : Char) : Boolean = digit2int(c, 10) >= 0 - def isIdentifierStart(c: Char): Boolean = c match { - case 'A' | 'B' | 'C' | 'D' | 'E' | - 'F' | 'G' | 'H' | 'I' | 'J' | - 'K' | 'L' | 'M' | 'N' | 'O' | + def isIdentifierStart(c: Char): Boolean = (c: @switch) match { + case 'A' | 'B' | 'C' | 'D' | 'E' | + 'F' | 'G' | 'H' | 'I' | 'J' | + 'K' | 'L' | 'M' | 'N' | 'O' | + 'P' | 'Q' | 'R' | 'S' | 'T' | + 'U' | 'V' | 'W' | 'X' | 'Y' | + 'Z' | '$' | '_' | + 'a' | 'b' | 'c' | 'd' | 'e' | + 'f' | 'g' | 'h' | 'i' | 'j' | + 'k' | 'l' | 'm' | 'n' | 'o' | + 'p' | 'q' | 'r' | 's' | 't' | + 'u' | 'v' | 'w' | 'x' | 'y' | // scala-mode: need to understand multi-line case patterns + 'z' => true + case _ => false + } + def isIdentifierPart(c: Char) : Boolean = (c: @switch) match { + case ('A' | 'B' | 'C' | 'D' | 'E' | + 'F' | 'G' | 'H' | 'I' | 'J' | + 'K' | 'L' | 'M' | 'N' | 'O' | 'P' | 'Q' | 'R' | 'S' | 'T' | 'U' | 'V' | 'W' | 'X' | 'Y' | 'Z' | '$' | '_' | @@ -597,35 +613,18 @@ trait NewScanners { 'f' | 'g' | 'h' | 'i' | 'j' | 'k' | 'l' | 'm' | 'n' | 'o' | 'p' | 'q' | 'r' | 's' | 't' | - 'u' | 'v' | 'w' | 'x' | 'y' | // scala-mode: need to understand multi-line case patterns - 'z' => true - case _ => false - } - def isIdentifierPart(c: Char) : Boolean = c match { - case ('A' | 'B' | 'C' | 'D' | 'E' | - 'F' | 'G' | 'H' | 'I' | 'J' | - 'K' | 'L' | 'M' | 'N' | 'O' | - 'P' | 'Q' | 'R' | 'S' | 'T' | - 'U' | 'V' | 'W' | 'X' | 'Y' | - 'Z' | '$' | '_' | - 'a' | 'b' | 'c' | 'd' | 'e' | - 'f' | 'g' | 'h' | 'i' | 'j' | - 'k' | 'l' | 'm' | 'n' | 'o' | - 'p' | 'q' | 'r' | 's' | 't' | - 'u' | 'v' | 'w' | 'x' | 'y' | - 'z') => true - case '0'|'1'|'2'|'3'|'4'|'5'|'6'|'7'|'8'|'9' => true - case c if Character.isUnicodeIdentifierPart(c) => true - case _ => false + 'u' | 'v' | 'w' | 'x' | 'y' | + 'z') => true + case '0'|'1'|'2'|'3'|'4'|'5'|'6'|'7'|'8'|'9' => true + case c => Character.isUnicodeIdentifierPart(c) } //isIdentifierStart(c) || isDigit(c) || isUnicodeIdentifierPart(c) - def isOperatorPart(c : Char) : Boolean = c match { - case '~' | '!' | '@' | '#' | '%' | - '^' | '*' | '+' | '-' | '<' | - '>' | '?' | ':' | '=' | '&' | - '|' | '/' | '\\' => true - case c if isSpecial(c) => true - case _ => false + def isOperatorPart(c : Char) : Boolean = (c: @switch) match { + case '~' | '!' | '@' | '#' | '%' | + '^' | '*' | '+' | '-' | '<' | + '>' | '?' | ':' | '=' | '&' | + '|' | '/' | '\\' => true + case c => isSpecial(c) } private def getOperatorRest(implicit in : CoreScannerInput) : Unit = { @@ -719,18 +718,18 @@ trait NewScanners { in.error(offset, "Invalid literal number") code } - def inFirstOfStat(token: Int) = token match { - case EOF | /*CASE |*/ CATCH | ELSE | EXTENDS | FINALLY | MATCH | REQUIRES | WITH | YIELD | - COMMA | SEMI | NEWLINE | NEWLINES | DOT | COLON | EQUALS | ARROW | /* | USCORE - bug #756 */ - LARROW | SUBTYPE | VIEWBOUND | SUPERTYPE | HASH | // todo: add LBRACKET - RPAREN | RBRACKET | RBRACE => false - case _ => true + // todo: add LBRACKET + def inFirstOfStat(token: Int) = (token: @switch) match { + case EOF | CATCH | ELSE | EXTENDS | FINALLY | MATCH | REQUIRES | WITH | YIELD | + COMMA | SEMI | NEWLINE | NEWLINES | DOT | COLON | EQUALS | ARROW | LARROW | + SUBTYPE | VIEWBOUND | SUPERTYPE | HASH | RPAREN | RBRACKET | RBRACE => false + case _ => true } - def inLastOfStat(token: Int) = token match { - case CHARLIT | INTLIT | LONGLIT | FLOATLIT | DOUBLELIT | STRINGLIT | SYMBOLLIT | - IDENTIFIER | BACKQUOTED_IDENT | THIS | NULL | TRUE | FALSE | RETURN | USCORE | - TYPE | XMLSTART | RPAREN | RBRACKET | RBRACE => true - case _ => false + def inLastOfStat(token: Int) = (token: @switch) match { + case CHARLIT | INTLIT | LONGLIT | FLOATLIT | DOUBLELIT | STRINGLIT | SYMBOLLIT | + IDENTIFIER | BACKQUOTED_IDENT | THIS | NULL | TRUE | FALSE | RETURN | USCORE | + TYPE | XMLSTART | RPAREN | RBRACKET | RBRACE => true + case _ => false } def digit(c : Char, radix : Int) = c match { diff --git a/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala b/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala index 5de7e88300..4722210eb6 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala @@ -217,10 +217,9 @@ abstract class GenJVM extends SubComponent { addGenericSignature(jclass, c.symbol) addAnnotations(jclass, c.symbol.attributes) - emitClass(jclass, c.symbol) - if (c.symbol.attributes.exists(_.atp.typeSymbol == BeanInfoAttr)) + if (c.symbol hasAttribute BeanInfoAttr) genBeanInfoClass(c) } diff --git a/src/compiler/scala/tools/nsc/javac/JavaScanners.scala b/src/compiler/scala/tools/nsc/javac/JavaScanners.scala index cddbfcfd60..61443ebcdc 100755 --- a/src/compiler/scala/tools/nsc/javac/JavaScanners.scala +++ b/src/compiler/scala/tools/nsc/javac/JavaScanners.scala @@ -8,6 +8,7 @@ package scala.tools.nsc.javac import scala.tools.nsc.util._ import SourceFile.{LF, FF, CR, SU} import JavaTokens._ +import scala.annotation.switch trait JavaScanners { val global : Global @@ -347,7 +348,7 @@ trait JavaScanners { in.next case _ => pos = in.cpos // Position.encode(in.cline, in.ccol) - in.ch match { + (in.ch: @switch) match { case 'A' | 'B' | 'C' | 'D' | 'E' | 'F' | 'G' | 'H' | 'I' | 'J' | 'K' | 'L' | 'M' | 'N' | 'O' | @@ -702,7 +703,7 @@ trait JavaScanners { private def getIdentRest { while (true) { - in.ch match { + (in.ch: @switch) match { case 'A' | 'B' | 'C' | 'D' | 'E' | 'F' | 'G' | 'H' | 'I' | 'J' | 'K' | 'L' | 'M' | 'N' | 'O' | diff --git a/src/compiler/scala/tools/nsc/matching/TransMatcher.scala b/src/compiler/scala/tools/nsc/matching/TransMatcher.scala index ba35cc79c9..967c0938a6 100644 --- a/src/compiler/scala/tools/nsc/matching/TransMatcher.scala +++ b/src/compiler/scala/tools/nsc/matching/TransMatcher.scala @@ -38,7 +38,14 @@ trait TransMatcher { self: transform.ExplicitOuter with PatternNodes with Parall /** handles all translation of pattern matching */ - def handlePattern(selector: Tree, cases: List[CaseDef], doCheckExhaustive: Boolean, owner: Symbol, handleOuter: Tree => Tree)(implicit typer : Typer): Tree = { + def handlePattern( + selector: Tree, + cases: List[CaseDef], + doCheckExhaustive: Boolean, + owner: Symbol, + handleOuter: Tree => Tree) + (implicit typer : Typer): Tree = + { DBG("****") DBG("**** initalize, selector = "+selector+" selector.tpe = "+selector.tpe) DBG("**** doCheckExhaustive == "+doCheckExhaustive) diff --git a/src/compiler/scala/tools/nsc/symtab/Definitions.scala b/src/compiler/scala/tools/nsc/symtab/Definitions.scala index 0c37a9574b..0cecbc03c4 100644 --- a/src/compiler/scala/tools/nsc/symtab/Definitions.scala +++ b/src/compiler/scala/tools/nsc/symtab/Definitions.scala @@ -151,6 +151,8 @@ trait Definitions { var ByNameParamClass: Symbol = _ //var UnsealedClass: Symbol = _ lazy val UncheckedClass: Symbol = getClass("scala.unchecked") + lazy val TailrecClass: Symbol = getClass("scala.annotation.tailrec") + lazy val SwitchClass: Symbol = getClass("scala.annotation.switch") var EqualsPatternClass: Symbol = _ diff --git a/src/compiler/scala/tools/nsc/symtab/Symbols.scala b/src/compiler/scala/tools/nsc/symtab/Symbols.scala index 391dd69059..a0dd86c79f 100644 --- a/src/compiler/scala/tools/nsc/symtab/Symbols.scala +++ b/src/compiler/scala/tools/nsc/symtab/Symbols.scala @@ -91,16 +91,12 @@ trait Symbols { else -1 } - var attributes: List[AnnotationInfo] = List() - + // XXX the attributes logic is essentially duplicated across Symbols and Types + var attributes: List[AnnotationInfo] = Nil def setAttributes(attrs: List[AnnotationInfo]): this.type = { this.attributes = attrs; this } /** Does this symbol have an attribute of the given class? */ - def hasAttribute(cls: Symbol): Boolean = - attributes.exists { - case AnnotationInfo(tp, _, _) if tp.typeSymbol == cls => true - case _ => false - } + def hasAttribute(cls: Symbol) = attributes exists { _.atp.typeSymbol == cls } var privateWithin: Symbol = _ // set when symbol has a modifier of the form private[X], NoSymbol otherwise. @@ -347,8 +343,7 @@ trait Symbols { } } - def isDeprecated = - attributes exists (attr => attr.atp.typeSymbol == DeprecatedAttr) + def isDeprecated = hasAttribute(DeprecatedAttr) /** Does this symbol denote a wrapper object of the interpreter or its class? */ final def isInterpreterWrapper = diff --git a/src/compiler/scala/tools/nsc/symtab/Types.scala b/src/compiler/scala/tools/nsc/symtab/Types.scala index 4909628f4a..828a59f35a 100644 --- a/src/compiler/scala/tools/nsc/symtab/Types.scala +++ b/src/compiler/scala/tools/nsc/symtab/Types.scala @@ -775,6 +775,8 @@ trait Types { /** Return the attributes on this type */ val attributes: List[AnnotationInfo] = Nil + /** Test for the presence of an attribute */ + def hasAttribute(clazz: Symbol) = attributes exists { _.atp.typeSymbol == clazz } /** Add an attribute to this type */ def withAttribute(attrib: AnnotationInfo) = withAttributes(List(attrib)) diff --git a/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala b/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala index b7f904fbbd..db5845c94a 100644 --- a/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala +++ b/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala @@ -454,16 +454,19 @@ abstract class ExplicitOuter extends InfoTransform with TransMatcher with Patter } var checkExhaustive = true - def isUncheckedAnnotation(tpe: Type) = tpe match { - case AnnotatedType(List(AnnotationInfo(atp, _, _)), _, _) if atp.typeSymbol == UncheckedClass => - true - case _ => - false - } + var requireSwitch = false + + def isUncheckedAnnotation(tpe: Type) = tpe hasAttribute UncheckedClass + def isSwitchAnnotation(tpe: Type) = tpe hasAttribute SwitchClass + nselector match { - case Typed(nselector1, tpt) if isUncheckedAnnotation(tpt.tpe) => - nselector = nselector1 - checkExhaustive = false + case Typed(nselector1, tpt) => + if (isUncheckedAnnotation(tpt.tpe)) { + nselector = nselector1 + checkExhaustive = false + } + if (isSwitchAnnotation(tpt.tpe)) + requireSwitch = true case _ => } @@ -471,13 +474,22 @@ abstract class ExplicitOuter extends InfoTransform with TransMatcher with Patter val t = atPos(tree.pos) { val t_untyped = handlePattern(nselector, ncases.toList, checkExhaustive, currentOwner, transform)(localTyper) + /* if @switch annotation is present, verify the resulting tree is a Match */ + if (requireSwitch) t_untyped match { + case Block(_, Match(_, _)) => // ok + case _ => + unit.error(tree.pos, "could not emit switch for @switch annotated match") + } + localTyper.typed(t_untyped, resultType) } if (settings.debug.value) Console.println("finished translation of " + tid) - if(nguard.isEmpty) {t} else Block(nguard.toList, t) setType t.tpe + if (nguard.isEmpty) t + else Block(nguard.toList, t) setType t.tpe + case _ => val x = super.transform(tree) diff --git a/src/compiler/scala/tools/nsc/transform/TailCalls.scala b/src/compiler/scala/tools/nsc/transform/TailCalls.scala index 31b8665b74..d2fd0bec42 100644 --- a/src/compiler/scala/tools/nsc/transform/TailCalls.scala +++ b/src/compiler/scala/tools/nsc/transform/TailCalls.scala @@ -39,6 +39,9 @@ abstract class TailCalls extends Transform } } + /** The @tailrec annotation indicates TCO is mandatory */ + private def tailrecRequired(defdef: DefDef) = defdef.symbol hasAttribute TailrecClass + /** * A Tail Call Transformer * @@ -149,8 +152,9 @@ abstract class TailCalls extends Transform override def transform(tree: Tree): Tree = { tree match { - case DefDef(mods, name, tparams, vparams, tpt, rhs) => + case dd @ DefDef(mods, name, tparams, vparams, tpt, rhs) => log("Entering DefDef: " + name) + var isTransformed = false val newCtx = mkContext(ctx) newCtx.currentMethod = tree.symbol newCtx.makeLabel() @@ -173,6 +177,7 @@ abstract class TailCalls extends Transform var newRHS = transform(rhs, newCtx); if (newCtx.accessed) { log("Rewrote def " + newCtx.currentMethod) + isTransformed = true val newThis = newCtx.currentMethod.newValue(tree.pos, nme.THIS) .setInfo(currentClass.tpe) @@ -189,6 +194,10 @@ abstract class TailCalls extends Transform } else { copy.DefDef(tree, mods, name, tparams, vparams, tpt, transform(rhs, newCtx)) } + + if (!isTransformed && tailrecRequired(dd)) + unit.error(dd.pos, "could not optimize @tailrec annotated method") + log("Leaving DefDef: " + name) t1 diff --git a/src/library/scala/Iterator.scala b/src/library/scala/Iterator.scala index d04d32393a..b5092a2430 100644 --- a/src/library/scala/Iterator.scala +++ b/src/library/scala/Iterator.scala @@ -14,6 +14,7 @@ package scala import Predef._ import collection.mutable.{Buffer, ListBuffer, ArrayBuffer} +import annotation.tailrec /** The <code>Iterator</code> object provides various functions for * creating specialized iterators. @@ -248,8 +249,14 @@ trait Iterator[+A] { * @param n the number of elements to drop * @return the new iterator */ - def drop(n: Int): Iterator[A] = - if (n > 0 && hasNext) { next; drop(n - 1) } else this + def drop(n: Int): Iterator[A] = { + @tailrec + def loop(left: Int): Iterator[A] = + if (left > 0 && hasNext) { next; loop(left - 1) } + else this + + loop(n) + } /** A sub-iterator of <code>until - from elements * starting at index <code>from</code> @@ -366,12 +373,18 @@ trait Iterator[+A] { * @param p the predicate used to skip elements. * @return an iterator consisting of the remaining elements */ - def dropWhile(p: A => Boolean): Iterator[A] = - if (hasNext) { - val x = next - if (p(x)) dropWhile(p) - else Iterator.single(x) append this - } else this + def dropWhile(p: A => Boolean): Iterator[A] = { + @tailrec + def loop(): Iterator[A] = + if (hasNext) { + val x = next + if (p(x)) loop() + else Iterator.single(x) append this + } + else this + + loop() + } /** Return an iterator formed from this iterator and the specified iterator * <code>that</code> by associating each element of the former with diff --git a/src/library/scala/List.scala b/src/library/scala/List.scala index eda39f9ec9..5482deabf4 100644 --- a/src/library/scala/List.scala +++ b/src/library/scala/List.scala @@ -12,6 +12,7 @@ package scala import scala.collection.mutable.ListBuffer +import annotation.tailrec import Predef._ /** This object provides methods for creating specialized lists, and for @@ -702,6 +703,7 @@ sealed abstract class List[+A] extends Seq[A] with Product { * @return the suffix of length <code>n</code> of the list */ def takeRight(n: Int): List[A] = { + @tailrec def loop(lead: List[A], lag: List[A]): List[A] = lead match { case Nil => lag case _ :: tail => loop(tail, lag.tail) @@ -765,9 +767,14 @@ sealed abstract class List[+A] extends Seq[A] with Product { * @return the longest suffix of the list whose first element * does not satisfy the predicate <code>p</code>. */ - override def dropWhile(p: A => Boolean): List[A] = - if (isEmpty || !p(head)) this - else tail dropWhile p + override def dropWhile(p: A => Boolean): List[A] = { + @tailrec + def loop(xs: List[A]): List[A] = + if (xs.isEmpty || !p(xs.head)) xs + else loop(xs.tail) + + loop(this) + } /** Returns the longest prefix of the list whose elements all satisfy * the given predicate, and the rest of the list. @@ -823,6 +830,7 @@ sealed abstract class List[+A] extends Seq[A] with Product { * @return the reversed list of results. */ def reverseMap[B](f: A => B): List[B] = { + @tailrec def loop(l: List[A], res: List[B]): List[B] = l match { case Nil => res case head :: tail => loop(tail, f(head) :: res) diff --git a/src/library/scala/annotation/switch.scala b/src/library/scala/annotation/switch.scala new file mode 100644 index 0000000000..5ecbf73905 --- /dev/null +++ b/src/library/scala/annotation/switch.scala @@ -0,0 +1,17 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2002-2009, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ +package scala.annotation + +/** <p> + * An annotation to be applied to a match expression. If present, + * 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. + * </p> + */ +final class switch extends StaticAnnotation
\ No newline at end of file diff --git a/src/library/scala/annotation/tailrec.scala b/src/library/scala/annotation/tailrec.scala new file mode 100644 index 0000000000..6ce23a92fa --- /dev/null +++ b/src/library/scala/annotation/tailrec.scala @@ -0,0 +1,16 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2002-2009, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ +package scala.annotation + +/** <p> + * A method annotation which verifies that the method will be compiled + * with tail call optimization. If it is present, the compiler will + * issue an error if the method cannot be optimized into a loop. + * </p> + */ +final class tailrec extends StaticAnnotation diff --git a/src/library/scalax/collection/immutable/Stream.scala b/src/library/scalax/collection/immutable/Stream.scala index 036fefe70c..4055be40bc 100755 --- a/src/library/scalax/collection/immutable/Stream.scala +++ b/src/library/scalax/collection/immutable/Stream.scala @@ -14,6 +14,7 @@ package scalax.collection.immutable import mutable.ListBuffer import generic.{SequenceTemplate, SequenceFactory, EmptyIterableFactory, Builder, LazyBuilder} import annotation.unchecked.uncheckedVariance +import annotation.tailrec /** * The object <code>Stream</code> provides helper functions @@ -486,6 +487,7 @@ self => * @param sep The separator string printed between consecutive elements. */ def print(sep: String) { + @tailrec def loop(these: Stream[A], start: String) { Console.print(start) if (isEmpty) Console.print("empty") diff --git a/test/files/neg/switch.check b/test/files/neg/switch.check new file mode 100644 index 0000000000..7212c1a22b --- /dev/null +++ b/test/files/neg/switch.check @@ -0,0 +1,10 @@ +switch.scala:28: error: could not emit switch for @switch annotated match + def fail1(c: Char) = (c: @switch) match { + ^ +switch.scala:38: error: could not emit switch for @switch annotated match + def fail2(c: Char) = (c: @switch @unchecked) match { + ^ +switch.scala:45: error: could not emit switch for @switch annotated match + def fail3(c: Char) = (c: @unchecked @switch) match { + ^ +three errors found diff --git a/test/files/neg/switch.scala b/test/files/neg/switch.scala new file mode 100644 index 0000000000..a5a6f9e789 --- /dev/null +++ b/test/files/neg/switch.scala @@ -0,0 +1,66 @@ +import scala.annotation.switch + +// this is testing not so much how things ought to be but how they are; +// the test is supposed to start failing if the behavior changes at all. +object Other { + val C1 = 'P' // fails: not final + final val C2 = 'Q' // succeeds: singleton type Char('Q') inferred + final val C3: Char = 'R' // fails: type Char specified + final val C4 = '\u000A' // succeeds like C2 but more unicodey +} + +object Main { + def succ1(c: Char) = (c: @switch) match { + case 'A' | 'B' | 'C' => true + case 'd' => true + case 'f' | 'g' => true + case _ => false + } + + def succ2(c: Char) = (c: @switch) match { + case 'A' | 'B' | 'C' => true + case Other.C2 => true + case Other.C4 => true + case _ => false + } + + // has a guard + def fail1(c: Char) = (c: @switch) match { + case 'A' | 'B' | 'C' => true + case x if x == 'A' => true + case _ => false + } + + // throwing in @unchecked on the next two to make sure + // multiple annotations are processed correctly + + // thinks a val in an object is constant... so naive + def fail2(c: Char) = (c: @switch @unchecked) match { + case 'A' => true + case Other.C1 => true + case _ => false + } + + // more naivete + def fail3(c: Char) = (c: @unchecked @switch) match { + case 'A' => true + case Other.C3 => true + case _ => false + } + + // guard case done correctly + def succ3(c: Char) = (c: @switch) match { + case 'A' | 'B' | 'C' => true + case x => x == 'A' + } + + // some ints just to mix it up a bit + def succ4(x: Int, y: Int) = ((x+y): @switch) match { + case 1 => 5 + case 2 => 10 + case 3 => 20 + case 4 => 50 + case 5|6|7|8 => 100 + case _ => -1 + } +}
\ No newline at end of file diff --git a/test/files/neg/tailrec.check b/test/files/neg/tailrec.check new file mode 100644 index 0000000000..22d70e82a0 --- /dev/null +++ b/test/files/neg/tailrec.check @@ -0,0 +1,10 @@ +tailrec.scala:6: error: could not optimize @tailrec annotated method + def facfail(n: Int): Int = + ^ +tailrec.scala:42: error: could not optimize @tailrec annotated method + @tailrec def fail1(x: Int): Int = fail1(x) + ^ +tailrec.scala:45: error: could not optimize @tailrec annotated method + @tailrec def fail2[T](xs: List[T]): List[T] = xs match { + ^ +three errors found diff --git a/test/files/neg/tailrec.scala b/test/files/neg/tailrec.scala new file mode 100644 index 0000000000..4c45672f93 --- /dev/null +++ b/test/files/neg/tailrec.scala @@ -0,0 +1,53 @@ +import scala.annotation.tailrec + +// putting @tailrec through the paces +object Main { + @tailrec + def facfail(n: Int): Int = + if (n == 0) 1 + else n * facfail(n - 1) + + @tailrec + def facsucc(n: Int, acc: Int): Int = + if (n == 0) acc + else facsucc(n - 1, n * acc) + + @tailrec def loopy1(x: Int): Int = loopy1(x - 1) + + def ding { + object dong { + @tailrec def loopy2(x: Int): Int = loopy2(x) + } + () + } + + def inner(q: Int) = { + @tailrec + def loopy3(x: Int): Int = loopy3(x + 1) + + loopy3(q) + } +} + +class Bob { + // these should work + @tailrec private def succ1(x: Int): Int = succ1(x) + @tailrec final def succ2(x: Int): Int = succ2(x) + @tailrec final def succ3[T](in: List[T], acc: List[T]): List[T] = in match { + case Nil => Nil + case x :: xs => succ3(xs, x :: acc) + } + + // not private, not final + @tailrec def fail1(x: Int): Int = fail1(x) + + // a typical between-chair-and-keyboard error + @tailrec def fail2[T](xs: List[T]): List[T] = xs match { + case Nil => Nil + case x :: xs => x :: fail2(xs) + } + + object innerBob { + @tailrec def succ4(x: Int): Int = succ4(x) + } +} |