summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorPaul Phillips <paulp@improving.org>2009-03-16 17:46:58 +0000
committerPaul Phillips <paulp@improving.org>2009-03-16 17:46:58 +0000
commit67c3c68da5de6284c5e37ef4b5f907d0079f0490 (patch)
tree0db5a40f781f2f9edb21fd4c20ce6608c8a5c147 /src
parent00d196adeeb3aa88f2afedcb5d7eb51f0fc19896 (diff)
downloadscala-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
Diffstat (limited to 'src')
-rw-r--r--src/compiler/scala/tools/nsc/ast/parser/MarkupParsers.scala3
-rw-r--r--src/compiler/scala/tools/nsc/ast/parser/NewScanners.scala83
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala3
-rwxr-xr-xsrc/compiler/scala/tools/nsc/javac/JavaScanners.scala5
-rw-r--r--src/compiler/scala/tools/nsc/matching/TransMatcher.scala9
-rw-r--r--src/compiler/scala/tools/nsc/symtab/Definitions.scala2
-rw-r--r--src/compiler/scala/tools/nsc/symtab/Symbols.scala13
-rw-r--r--src/compiler/scala/tools/nsc/symtab/Types.scala2
-rw-r--r--src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala32
-rw-r--r--src/compiler/scala/tools/nsc/transform/TailCalls.scala11
-rw-r--r--src/library/scala/Iterator.scala29
-rw-r--r--src/library/scala/List.scala14
-rw-r--r--src/library/scala/annotation/switch.scala17
-rw-r--r--src/library/scala/annotation/tailrec.scala16
-rwxr-xr-xsrc/library/scalax/collection/immutable/Stream.scala2
15 files changed, 162 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")