summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--build.xml2
-rw-r--r--src/compiler/scala/reflect/internal/Definitions.scala7
-rw-r--r--src/compiler/scala/reflect/internal/StdNames.scala1
-rw-r--r--src/compiler/scala/reflect/internal/Symbols.scala7
-rw-r--r--src/compiler/scala/reflect/internal/Types.scala15
-rw-r--r--src/compiler/scala/tools/nsc/ast/TreeGen.scala83
-rw-r--r--src/compiler/scala/tools/nsc/ast/parser/Parsers.scala2
-rw-r--r--src/compiler/scala/tools/nsc/backend/icode/GenICode.scala14
-rw-r--r--src/compiler/scala/tools/nsc/backend/icode/analysis/DataFlowAnalysis.scala19
-rw-r--r--src/compiler/scala/tools/nsc/backend/icode/analysis/TypeFlowAnalysis.scala639
-rw-r--r--src/compiler/scala/tools/nsc/backend/opt/Inliners.scala313
-rw-r--r--src/compiler/scala/tools/nsc/interpreter/MemberHandlers.scala5
-rw-r--r--src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala2
-rw-r--r--src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala58
-rw-r--r--src/compiler/scala/tools/nsc/transform/UnCurry.scala125
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala16
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Infer.scala16
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Macros.scala77
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/PatMatVirtualiser.scala324
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Typers.scala36
-rw-r--r--src/continuations/plugin/scala/tools/selectivecps/SelectiveCPSTransform.scala2
-rw-r--r--src/library/scala/concurrent/Awaitable.scala24
-rw-r--r--src/library/scala/concurrent/Channel.scala5
-rw-r--r--src/library/scala/concurrent/ConcurrentPackageObject.scala103
-rw-r--r--src/library/scala/concurrent/DelayedLazyVal.scala11
-rw-r--r--src/library/scala/concurrent/ExecutionContext.scala132
-rw-r--r--src/library/scala/concurrent/Future.scala492
-rw-r--r--src/library/scala/concurrent/FutureTaskRunner.scala1
-rw-r--r--src/library/scala/concurrent/JavaConversions.scala7
-rw-r--r--src/library/scala/concurrent/ManagedBlocker.scala1
-rw-r--r--src/library/scala/concurrent/Promise.scala132
-rw-r--r--src/library/scala/concurrent/Scheduler.scala54
-rw-r--r--src/library/scala/concurrent/Task.scala13
-rw-r--r--src/library/scala/concurrent/TaskRunner.scala1
-rw-r--r--src/library/scala/concurrent/TaskRunners.scala1
-rw-r--r--src/library/scala/concurrent/ThreadPoolRunner.scala1
-rw-r--r--src/library/scala/concurrent/default/SchedulerImpl.scala.disabled44
-rw-r--r--src/library/scala/concurrent/default/TaskImpl.scala.disabled313
-rw-r--r--src/library/scala/concurrent/impl/AbstractPromise.java21
-rw-r--r--src/library/scala/concurrent/impl/ExecutionContextImpl.scala134
-rw-r--r--src/library/scala/concurrent/impl/Future.scala89
-rw-r--r--src/library/scala/concurrent/impl/Promise.scala252
-rw-r--r--src/library/scala/concurrent/ops.scala1
-rw-r--r--src/library/scala/concurrent/package.scala57
-rw-r--r--src/library/scala/concurrent/package.scala.disabled108
-rw-r--r--src/library/scala/package.scala6
-rw-r--r--src/library/scala/sys/process/BasicIO.scala5
-rw-r--r--src/library/scala/util/Duration.scala485
-rw-r--r--src/library/scala/util/Timeout.scala33
-rw-r--r--src/library/scala/util/Try.scala165
-rw-r--r--test/disabled/jvm/scala-concurrent-tck-akka.scala391
-rw-r--r--test/files/buildmanager/t2652/t2652.check2
-rw-r--r--test/files/jvm/concurrent-future.check16
-rw-r--r--test/files/jvm/concurrent-future.scala122
-rw-r--r--test/files/jvm/scala-concurrent-tck.scala413
-rw-r--r--test/files/neg/macro-argtype-mismatch.check6
-rw-r--r--test/files/neg/macro-argtype-mismatch.flags1
-rw-r--r--test/files/neg/macro-argtype-mismatch/Macros_1.scala3
-rw-r--r--test/files/neg/macro-argtype-mismatch/Test_2.scala4
-rw-r--r--test/files/neg/macro-noexpand.check4
-rw-r--r--test/files/neg/macro-noexpand.flags1
-rw-r--r--test/files/neg/macro-noexpand/Macros_1.scala3
-rw-r--r--test/files/neg/macro-noexpand/Test_2.scala4
-rw-r--r--test/files/neg/macro-noncompilertree.check6
-rw-r--r--test/files/neg/macro-noncompilertree.flags1
-rw-r--r--test/files/neg/macro-noncompilertree/Macros_1.scala3
-rw-r--r--test/files/neg/macro-nontree.check6
-rw-r--r--test/files/neg/macro-nontree.flags1
-rw-r--r--test/files/neg/macro-nontree/Macros_1.scala3
-rw-r--r--test/files/neg/t5452.check (renamed from test/disabled/neg/t5452.check)0
-rw-r--r--test/files/neg/t5452.scala (renamed from test/disabled/neg/t5452.scala)0
-rw-r--r--test/files/pos/Transactions.scala2
-rw-r--r--test/files/pos/spurious-overload.scala (renamed from test/disabled/pos/spurious-overload.scala)0
-rw-r--r--test/files/run/macro-basic.check1
-rw-r--r--test/files/run/macro-basic.flags1
-rw-r--r--test/files/run/macro-basic/Macros_1.scala10
-rw-r--r--test/files/run/macro-basic/Test_2.scala4
-rw-r--r--test/files/run/macro-repl-basic.check25
-rw-r--r--test/files/run/macro-repl-basic.scala18
-rw-r--r--test/files/run/macro-repl-dontexpand.check9
-rw-r--r--test/files/run/macro-repl-dontexpand.scala8
-rw-r--r--test/files/run/macro-rettype-mismatch.check5
-rw-r--r--test/files/run/macro-rettype-mismatch.flags1
-rw-r--r--test/files/run/macro-rettype-mismatch/Macros_1.scala3
-rw-r--r--test/files/run/macro-rettype-mismatch/Test_2.scala16
-rw-r--r--test/files/run/t4794.check2
-rw-r--r--test/files/run/t5488-fn.check17
-rw-r--r--test/files/run/t5488-fn.scala49
-rw-r--r--test/files/run/t5488.check14
-rw-r--r--test/files/run/t5488.scala26
-rw-r--r--test/files/run/virtpatmat_partial.check4
-rw-r--r--test/files/run/virtpatmat_partial.scala23
-rw-r--r--test/files/run/virtpatmat_switch.scala8
-rw-r--r--test/files/run/virtpatmat_try.check2
-rw-r--r--test/files/run/virtpatmat_try.flags1
-rw-r--r--test/files/run/virtpatmat_try.scala47
-rw-r--r--test/files/specialized/SI-5005.scala6
-rw-r--r--test/pending/run/macro-overload.check4
-rw-r--r--test/pending/run/macro-overload.flags1
-rw-r--r--test/pending/run/macro-overload/Macros_1.scala9
-rw-r--r--test/pending/run/macro-overload/Test_2.scala6
-rw-r--r--test/pending/run/t4770.check (renamed from test/files/run/t4770.check)0
-rw-r--r--test/pending/run/t4770.scala (renamed from test/files/run/t4770.scala)0
103 files changed, 5022 insertions, 744 deletions
diff --git a/build.xml b/build.xml
index 79b36fd4a6..314ce379e1 100644
--- a/build.xml
+++ b/build.xml
@@ -1535,6 +1535,7 @@ DOCUMENTATION
docUncompilable="${src.dir}/library-aux"
sourcepath="${src.dir}"
classpathref="pack.classpath"
+ addparams="${scalac.args.all}"
docRootContent="${src.dir}/library/rootdoc.txt">
<src>
<files includes="${src.dir}/actors"/>
@@ -1620,6 +1621,7 @@ DOCUMENTATION
classpathref="pack.classpath"
srcdir="${src.dir}/compiler"
docRootContent="${src.dir}/compiler/rootdoc.txt">
+ addparams="${scalac.args.all}"
<include name="**/*.scala"/>
</scaladoc>
<touch file="${build-docs.dir}/compiler.complete" verbose="no"/>
diff --git a/src/compiler/scala/reflect/internal/Definitions.scala b/src/compiler/scala/reflect/internal/Definitions.scala
index b4b0a7335d..d043230e48 100644
--- a/src/compiler/scala/reflect/internal/Definitions.scala
+++ b/src/compiler/scala/reflect/internal/Definitions.scala
@@ -70,8 +70,7 @@ trait Definitions extends reflect.api.StandardDefinitions {
tpnme.Float -> FLOAT_TAG,
tpnme.Double -> DOUBLE_TAG,
tpnme.Boolean -> BOOL_TAG,
- tpnme.Unit -> VOID_TAG,
- tpnme.Object -> OBJECT_TAG
+ tpnme.Unit -> VOID_TAG
)
private def classesMap[T](f: Name => T) = symbolsMap(ScalaValueClassesNoUnit, f)
@@ -80,7 +79,7 @@ trait Definitions extends reflect.api.StandardDefinitions {
private def boxedName(name: Name) = sn.Boxed(name.toTypeName)
- lazy val abbrvTag = symbolsMap(ObjectClass :: ScalaValueClasses, nameToTag)
+ lazy val abbrvTag = symbolsMap(ScalaValueClasses, nameToTag) withDefaultValue OBJECT_TAG
lazy val numericWeight = symbolsMapFilt(ScalaValueClasses, nameToWeight.keySet, nameToWeight)
lazy val boxedModule = classesMap(x => getModule(boxedName(x)))
lazy val boxedClass = classesMap(x => getClass(boxedName(x)))
@@ -213,7 +212,7 @@ trait Definitions extends reflect.api.StandardDefinitions {
// Note: this is not the type alias AnyRef, it's a companion-like
// object used by the @specialize annotation.
- def AnyRefModule = getMember(ScalaPackageClass, nme.AnyRef)
+ lazy val AnyRefModule = getMember(ScalaPackageClass, nme.AnyRef)
@deprecated("Use AnyRefModule", "2.10.0")
def Predef_AnyRef = AnyRefModule
diff --git a/src/compiler/scala/reflect/internal/StdNames.scala b/src/compiler/scala/reflect/internal/StdNames.scala
index 1f67bbc0ac..b4c404a80c 100644
--- a/src/compiler/scala/reflect/internal/StdNames.scala
+++ b/src/compiler/scala/reflect/internal/StdNames.scala
@@ -330,6 +330,7 @@ trait StdNames extends NameManglers { self: SymbolTable =>
val freeValue : NameType = "freeValue"
val genericArrayOps: NameType = "genericArrayOps"
val get: NameType = "get"
+ val getOrElse: NameType = "getOrElse"
val hasNext: NameType = "hasNext"
val hashCode_ : NameType = if (forMSIL) "GetHashCode" else "hashCode"
val hash_ : NameType = "hash"
diff --git a/src/compiler/scala/reflect/internal/Symbols.scala b/src/compiler/scala/reflect/internal/Symbols.scala
index 77ed2f6a1b..b3ff2c6329 100644
--- a/src/compiler/scala/reflect/internal/Symbols.scala
+++ b/src/compiler/scala/reflect/internal/Symbols.scala
@@ -1023,8 +1023,11 @@ trait Symbols extends api.Symbols { self: SymbolTable =>
/** Modifies this symbol's info in place. */
def modifyInfo(f: Type => Type): this.type = setInfo(f(info))
/** Substitute second list of symbols for first in current info. */
- def substInfo(syms0: List[Symbol], syms1: List[Symbol]) = modifyInfo(_.substSym(syms0, syms1))
- def setInfoOwnerAdjusted(info: Type): this.type = setInfo(info atOwner this)
+ def substInfo(syms0: List[Symbol], syms1: List[Symbol]): this.type =
+ if (syms0.isEmpty) this
+ else modifyInfo(_.substSym(syms0, syms1))
+
+ def setInfoOwnerAdjusted(info: Type): this.type = setInfo(info atOwner this)
/** Set the info and enter this symbol into the owner's scope. */
def setInfoAndEnter(info: Type): this.type = {
diff --git a/src/compiler/scala/reflect/internal/Types.scala b/src/compiler/scala/reflect/internal/Types.scala
index adf9df185a..04efe04636 100644
--- a/src/compiler/scala/reflect/internal/Types.scala
+++ b/src/compiler/scala/reflect/internal/Types.scala
@@ -680,7 +680,7 @@ trait Types extends api.Types { self: SymbolTable =>
* symbol.
*/
def substSym(from: List[Symbol], to: List[Symbol]): Type =
- if (from eq to) this
+ if ((from eq to) || from.isEmpty) this
else new SubstSymMap(from, to) apply this
/** Substitute all occurrences of `ThisType(from)` in this type by `to`.
@@ -5381,9 +5381,9 @@ trait Types extends api.Types { self: SymbolTable =>
val params2 = mt2.params
val res2 = mt2.resultType
(sameLength(params1, params2) &&
+ mt1.isImplicit == mt2.isImplicit &&
matchingParams(params1, params2, mt1.isJava, mt2.isJava) &&
- (res1 <:< res2.substSym(params2, params1)) &&
- mt1.isImplicit == mt2.isImplicit)
+ (res1 <:< res2.substSym(params2, params1)))
// TODO: if mt1.params.isEmpty, consider NullaryMethodType?
case _ =>
false
@@ -5503,9 +5503,9 @@ trait Types extends api.Types { self: SymbolTable =>
tp2 match {
case mt2 @ MethodType(params2, res2) =>
// sameLength(params1, params2) was used directly as pre-screening optimization (now done by matchesQuantified -- is that ok, performancewise?)
- matchesQuantified(params1, params2, res1, res2) &&
+ mt1.isImplicit == mt2.isImplicit &&
matchingParams(params1, params2, mt1.isJava, mt2.isJava) &&
- mt1.isImplicit == mt2.isImplicit
+ matchesQuantified(params1, params2, res1, res2)
case NullaryMethodType(res2) =>
if (params1.isEmpty) matchesType(res1, res2, alwaysMatchSimple)
else matchesType(tp1, res2, alwaysMatchSimple)
@@ -5532,7 +5532,10 @@ trait Types extends api.Types { self: SymbolTable =>
case PolyType(tparams1, res1) =>
tp2 match {
case PolyType(tparams2, res2) =>
- matchesQuantified(tparams1, tparams2, res1, res2)
+ if ((tparams1 corresponds tparams2)(_ eq _))
+ matchesType(res1, res2, alwaysMatchSimple)
+ else
+ matchesQuantified(tparams1, tparams2, res1, res2)
case ExistentialType(_, res2) =>
alwaysMatchSimple && matchesType(tp1, res2, true)
case _ =>
diff --git a/src/compiler/scala/tools/nsc/ast/TreeGen.scala b/src/compiler/scala/tools/nsc/ast/TreeGen.scala
index 265d017653..3467292f28 100644
--- a/src/compiler/scala/tools/nsc/ast/TreeGen.scala
+++ b/src/compiler/scala/tools/nsc/ast/TreeGen.scala
@@ -13,7 +13,7 @@ import symtab.SymbolTable
/** XXX to resolve: TreeGen only assumes global is a SymbolTable, but
* TreeDSL at the moment expects a Global. Can we get by with SymbolTable?
*/
-abstract class TreeGen extends reflect.internal.TreeGen {
+abstract class TreeGen extends reflect.internal.TreeGen with TreeDSL {
val global: Global
import global._
@@ -66,18 +66,81 @@ abstract class TreeGen extends reflect.internal.TreeGen {
case _ => tree
}
- def withDefaultCase(matchExpr: Tree, defaultAction: Tree/*scrutinee*/ => Tree): Tree = matchExpr match {
- case Match(scrutinee, cases) =>
- if (cases exists treeInfo.isDefaultCase) matchExpr
- else {
- val defaultCase = CaseDef(Ident(nme.WILDCARD), EmptyTree, defaultAction(scrutinee))
- Match(scrutinee, cases :+ defaultCase)
+ // must be kept in synch with the codegen in PatMatVirtualiser
+ object VirtualCaseDef {
+ def unapply(b: Block): Option[(Assign, Tree, Tree)] = b match {
+ case Block(List(assign@Assign(keepGoingLhs, falseLit), matchRes), zero) => Some((assign, matchRes, zero)) // TODO: check tree annotation
+ case _ => None
+ }
+ }
+
+ // TODO: would be so much nicer if we would know during match-translation (i.e., type checking)
+ // whether we should emit missingCase-style apply (and isDefinedAt), instead of transforming trees post-factum
+ class MatchMatcher {
+ def caseMatch(orig: Tree, selector: Tree, cases: List[CaseDef], wrap: Tree => Tree): Tree = unknownTree(orig)
+ def caseVirtualizedMatch(orig: Tree, _match: Tree, targs: List[Tree], scrut: Tree, matcher: Tree): Tree = unknownTree(orig)
+ def caseVirtualizedMatchOpt(orig: Tree, zero: ValDef, x: ValDef, matchRes: ValDef, keepGoing: ValDef, stats: List[Tree], epilogue: Tree, wrap: Tree => Tree): Tree = unknownTree(orig)
+
+ def apply(matchExpr: Tree): Tree = (matchExpr: @unchecked) match {
+ // old-style match or virtpatmat switch
+ case Match(selector, cases) => // println("simple match: "+ (selector, cases) + "for:\n"+ matchExpr )
+ caseMatch(matchExpr, selector, cases, identity)
+ // old-style match or virtpatmat switch
+ case Block((vd: ValDef) :: Nil, orig@Match(selector, cases)) => // println("block match: "+ (selector, cases, vd) + "for:\n"+ matchExpr )
+ caseMatch(matchExpr, selector, cases, m => copyBlock(matchExpr, List(vd), m))
+ // virtpatmat
+ case Apply(Apply(TypeApply(Select(tgt, nme.runOrElse), targs), List(scrut)), List(matcher)) if opt.virtPatmat => // println("virt match: "+ (tgt, targs, scrut, matcher) + "for:\n"+ matchExpr )
+ caseVirtualizedMatch(matchExpr, tgt, targs, scrut, matcher)
+ // optimized version of virtpatmat
+ case Block((zero: ValDef) :: (x: ValDef) :: (matchRes: ValDef) :: (keepGoing: ValDef) :: stats, epilogue) if opt.virtPatmat => // TODO: check tree annotation // println("virtopt match: "+ (zero, x, matchRes, keepGoing, stats) + "for:\n"+ matchExpr )
+ caseVirtualizedMatchOpt(matchExpr, zero, x, matchRes, keepGoing, stats, epilogue, identity)
+ // optimized version of virtpatmat
+ case Block(outerStats, orig@Block((zero: ValDef) :: (x: ValDef) :: (matchRes: ValDef) :: (keepGoing: ValDef) :: stats, epilogue)) if opt.virtPatmat => // TODO: check tree annotation // println("virt opt block match: "+ (zero, x, matchRes, keepGoing, stats, outerStats) + "for:\n"+ matchExpr )
+ caseVirtualizedMatchOpt(matchExpr, zero, x, matchRes, keepGoing, stats, epilogue, m => copyBlock(matchExpr, outerStats, m))
+ case other =>
+ unknownTree(other)
+ }
+
+ def unknownTree(t: Tree): Tree = throw new MatchError(t)
+ def copyBlock(orig: Tree, stats: List[Tree], expr: Tree): Block = Block(stats, expr)
+
+ def dropSyntheticCatchAll(cases: List[CaseDef]): List[CaseDef] =
+ if (!opt.virtPatmat) cases
+ else cases filter {
+ case CaseDef(pat, EmptyTree, Throw(Apply(Select(New(exTpt), nme.CONSTRUCTOR), _))) if (treeInfo.isWildcardArg(pat) && (exTpt.tpe.typeSymbol eq MatchErrorClass)) => false
+ case CaseDef(pat, guard, body) => true
+ }
+ }
+
+ def withDefaultCase(matchExpr: Tree, defaultAction: Tree/*scrutinee*/ => Tree): Tree = {
+ object withDefaultTransformer extends MatchMatcher {
+ override def caseMatch(orig: Tree, selector: Tree, cases: List[CaseDef], wrap: Tree => Tree): Tree = {
+ val casesNoSynthCatchAll = dropSyntheticCatchAll(cases)
+ if (casesNoSynthCatchAll exists treeInfo.isDefaultCase) orig
+ else {
+ val defaultCase = CaseDef(Ident(nme.WILDCARD), EmptyTree, defaultAction(selector.duplicate))
+ wrap(Match(selector, casesNoSynthCatchAll :+ defaultCase))
+ }
+ }
+ override def caseVirtualizedMatch(orig: Tree, _match: Tree, targs: List[Tree], scrut: Tree, matcher: Tree): Tree = { import CODE._
+ ((matcher APPLY (scrut)) DOT nme.getOrElse) APPLY (defaultAction(scrut.duplicate)) // TODO: pass targs
}
- case _ =>
- matchExpr
- // [Martin] Adriaan: please fill in virtpatmat transformation here
+ override def caseVirtualizedMatchOpt(orig: Tree, zero: ValDef, x: ValDef, matchRes: ValDef, keepGoing: ValDef, stats: List[Tree], epilogue: Tree, wrap: Tree => Tree): Tree = { import CODE._
+ wrap(Block(
+ zero ::
+ x ::
+ matchRes ::
+ keepGoing ::
+ stats,
+ // replace `if (keepGoing) throw new MatchError(...) else matchRes` by `if (keepGoing) ${defaultAction(`x`)} else matchRes`
+ (IF (REF(keepGoing.symbol)) THEN defaultAction(x.rhs.duplicate) ELSE REF(matchRes.symbol))
+ ))
+ }
+ }
+ withDefaultTransformer(matchExpr)
}
+
def mkCached(cvar: Symbol, expr: Tree): Tree = {
val cvarRef = mkUnattributedRef(cvar)
Block(
diff --git a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala
index fe6dcc9138..e3a59058a3 100644
--- a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala
+++ b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala
@@ -1205,7 +1205,7 @@ self =>
*/
def wildcardType(start: Int) = {
val pname = freshTypeName("_$")
- val t = atPos(start) { Ident(pname) }
+ val t = atPos(start)(Ident(pname))
val bounds = typeBounds()
val param = atPos(t.pos union bounds.pos) { makeSyntheticTypeParam(pname, bounds) }
placeholderTypes = param :: placeholderTypes
diff --git a/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala b/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala
index 6aee52a354..f2c725a5c2 100644
--- a/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala
+++ b/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala
@@ -393,15 +393,15 @@ abstract class GenICode extends SubComponent {
for (CaseDef(pat, _, body) <- catches.reverse) yield {
def genWildcardHandler(sym: Symbol): (Symbol, TypeKind, Context => Context) =
(sym, kind, ctx => {
- ctx.bb.emit(DROP(REFERENCE(sym)))
+ ctx.bb.emit(DROP(REFERENCE(sym))) // drop the loaded exception
genLoad(body, ctx, kind)
})
pat match {
case Typed(Ident(nme.WILDCARD), tpt) => genWildcardHandler(tpt.tpe.typeSymbol)
case Ident(nme.WILDCARD) => genWildcardHandler(ThrowableClass)
- case Bind(name, _) =>
- val exception = ctx.method addLocal new Local(pat.symbol, toTypeKind(pat.symbol.tpe), false)
+ case Bind(_, _) =>
+ val exception = ctx.method addLocal new Local(pat.symbol, toTypeKind(pat.symbol.tpe), false) // the exception will be loaded and stored into this local
(pat.symbol.tpe.typeSymbol, kind, {
ctx: Context =>
@@ -1054,7 +1054,7 @@ abstract class GenICode extends SubComponent {
case Match(selector, cases) =>
debuglog("Generating SWITCH statement.");
- var ctx1 = genLoad(selector, ctx, INT)
+ var ctx1 = genLoad(selector, ctx, INT) // TODO: Java 7 allows strings in switches (so, don't assume INT and don't convert the literals using intValue)
val afterCtx = ctx1.newBlock
var caseCtx: Context = null
generatedType = toTypeKind(tree.tpe)
@@ -2086,12 +2086,12 @@ abstract class GenICode extends SubComponent {
exh
}) else None
- val exhs = handlers.map { handler =>
- val exh = this.newExceptionHandler(handler._1, handler._2, tree.pos)
+ val exhs = handlers.map { case (sym, kind, handler) => // def genWildcardHandler(sym: Symbol): (Symbol, TypeKind, Context => Context) =
+ val exh = this.newExceptionHandler(sym, kind, tree.pos)
var ctx1 = outerCtx.enterExceptionHandler(exh)
ctx1.addFinalizer(finalizer, finalizerCtx)
loadException(ctx1, exh, tree.pos)
- ctx1 = handler._3(ctx1)
+ ctx1 = handler(ctx1)
// emit finalizer
val ctx2 = emitFinalizer(ctx1)
ctx2.bb.closeWith(JUMP(afterCtx.bb))
diff --git a/src/compiler/scala/tools/nsc/backend/icode/analysis/DataFlowAnalysis.scala b/src/compiler/scala/tools/nsc/backend/icode/analysis/DataFlowAnalysis.scala
index 60cb679782..9f43e1b84c 100644
--- a/src/compiler/scala/tools/nsc/backend/icode/analysis/DataFlowAnalysis.scala
+++ b/src/compiler/scala/tools/nsc/backend/icode/analysis/DataFlowAnalysis.scala
@@ -60,20 +60,17 @@ trait DataFlowAnalysis[L <: SemiLattice] {
val output = f(point, in(point))
if ((lattice.bottom == out(point)) || output != out(point)) {
-// Console.println("Output changed at " + point
-// + " from: " + out(point) + " to: " + output
-// + " for input: " + in(point) + " and they are different: " + (output != out(point)))
+ // Console.println("Output changed at " + point
+ // + " from: " + out(point) + " to: " + output
+ // + " for input: " + in(point) + " and they are different: " + (output != out(point)))
out(point) = output
val succs = point.successors
succs foreach { p =>
- if (!worklist(p))
- worklist += p;
- if (!in.isDefinedAt(p))
- assert(false, "Invalid successor for: " + point + " successor " + p + " does not exist")
-// if (!p.exceptionHandlerHeader) {
-// println("lubbing " + p.predecessors + " outs: " + p.predecessors.map(out.apply).mkString("\n", "\n", ""))
- in(p) = lattice.lub(in(p) :: (p.predecessors map out.apply), p.exceptionHandlerStart)
-// }
+ val updated = lattice.lub(in(p) :: (p.predecessors map out.apply), p.exceptionHandlerStart)
+ if(updated != in(p)) {
+ in(p) = updated
+ if (!worklist(p)) { worklist += p; }
+ }
}
}
}
diff --git a/src/compiler/scala/tools/nsc/backend/icode/analysis/TypeFlowAnalysis.scala b/src/compiler/scala/tools/nsc/backend/icode/analysis/TypeFlowAnalysis.scala
index 6421d6c8ef..877c51ebc1 100644
--- a/src/compiler/scala/tools/nsc/backend/icode/analysis/TypeFlowAnalysis.scala
+++ b/src/compiler/scala/tools/nsc/backend/icode/analysis/TypeFlowAnalysis.scala
@@ -127,34 +127,6 @@ abstract class TypeFlowAnalysis {
}
}
- /** reinitialize the analysis, keeping around solutions from a previous run. */
- def reinit(m: icodes.IMethod) {
- if (this.method == null || this.method.symbol != m.symbol)
- init(m)
- else reinit {
- m foreachBlock { b =>
- if (!in.contains(b)) {
- for (p <- b.predecessors) {
- if (out.isDefinedAt(p)) {
- in(b) = out(p)
- worklist += p
- }
- /* else
- in(b) = typeFlowLattice.bottom
- */ }
- out(b) = typeFlowLattice.bottom
- }
- }
- for (handler <- m.exh) {
- val start = handler.startBlock
- if (!in.contains(start)) {
- worklist += start
- in(start) = lattice.IState(in(start).vars, typeStackLattice.exceptionHandlerStack)
- }
- }
- }
- }
-
def this(m: icodes.IMethod) {
this()
init(m)
@@ -162,7 +134,7 @@ abstract class TypeFlowAnalysis {
def run = {
timer.start
-// icodes.lubs0 = 0
+ // icodes.lubs0 = 0
forwardAnalysis(blockTransfer)
val t = timer.stop
if (settings.debug.value) {
@@ -170,216 +142,35 @@ abstract class TypeFlowAnalysis {
assert(visited.contains(b),
"Block " + b + " in " + this.method + " has input equal to bottom -- not visited? .." + visited));
}
-// log("" + method.symbol.fullName + " [" + method.code.blocks.size + " blocks] "
-// + "\n\t" + iterations + " iterations: " + t + " ms."
-// + "\n\tlubs: " + typeFlowLattice.lubs + " out of which " + icodes.lubs0 + " typer lubs")
+ // log("" + method.symbol.fullName + " [" + method.code.blocks.size + " blocks] "
+ // + "\n\t" + iterations + " iterations: " + t + " ms."
+ // + "\n\tlubs: " + typeFlowLattice.lubs + " out of which " + icodes.lubs0 + " typer lubs")
}
def blockTransfer(b: BasicBlock, in: lattice.Elem): lattice.Elem = {
- b.iterator.foldLeft(in)(interpret)
- }
- /** The flow function of a given basic block. */
- /* var flowFun: immutable.Map[BasicBlock, TransferFunction] = new immutable.HashMap */
-
- /** Fill flowFun with a transfer function per basic block. */
-/*
- private def buildFlowFunctions(blocks: List[BasicBlock]) {
- def transfer(b: BasicBlock): TransferFunction = {
- var gens: List[Gen] = Nil
- var consumed: Int = 0
- val stack = new SimulatedStack
-
- for (instr <- b) instr match {
- case THIS(clasz) =>
- stack push toTypeKind(clasz.tpe)
-
- case CONSTANT(const) =>
- stack push toTypeKind(const.tpe)
-
- case LOAD_ARRAY_ITEM(kind) =>
- stack.pop2
- stack.push(kind)
-
- case LOAD_LOCAL(local) =>
- val t = bindings(local)
- stack push (if (t == typeLattice.bottom) local.kind else t)
-
- case LOAD_FIELD(field, isStatic) =>
- if (!isStatic)
- stack.pop
- stack push toTypeKind(field.tpe)
-
- case LOAD_MODULE(module) =>
- stack push toTypeKind(module.tpe)
-
- case STORE_ARRAY_ITEM(kind) =>
- stack.pop3
-
- case STORE_LOCAL(local) =>
- val t = stack.pop
- bindings += (local -> t)
-
- case STORE_THIS(_) =>
- stack.pop
-
- case STORE_FIELD(field, isStatic) =>
- if (isStatic)
- stack.pop
- else
- stack.pop2
-
- case CALL_PRIMITIVE(primitive) =>
- primitive match {
- case Negation(kind) =>
- stack.pop; stack.push(kind)
- case Test(_, kind, zero) =>
- stack.pop
- if (!zero) stack.pop
- stack push BOOL;
- case Comparison(_, _) =>
- stack.pop2
- stack push INT
-
- case Arithmetic(op, kind) =>
- stack.pop
- if (op != NOT)
- stack.pop
- val k = kind match {
- case BYTE | SHORT | CHAR => INT
- case _ => kind
- }
- stack push k
-
- case Logical(op, kind) =>
- stack.pop2
- stack push kind
-
- case Shift(op, kind) =>
- stack.pop2
- stack push kind
-
- case Conversion(src, dst) =>
- stack.pop
- stack push dst
-
- case ArrayLength(kind) =>
- stack.pop
- stack push INT
-
- case StartConcat =>
- stack.push(ConcatClass)
-
- case EndConcat =>
- stack.pop
- stack.push(STRING)
-
- case StringConcat(el) =>
- stack.pop2
- stack push ConcatClass
- }
-
- case CALL_METHOD(method, style) => style match {
- case Dynamic =>
- stack.pop(1 + method.info.paramTypes.length)
- stack.push(toTypeKind(method.info.resultType))
-
- case Static(onInstance) =>
- if (onInstance) {
- stack.pop(1 + method.info.paramTypes.length)
- if (!method.isConstructor)
- stack.push(toTypeKind(method.info.resultType));
- } else {
- stack.pop(method.info.paramTypes.length)
- stack.push(toTypeKind(method.info.resultType))
- }
-
- case SuperCall(mix) =>
- stack.pop(1 + method.info.paramTypes.length)
- stack.push(toTypeKind(method.info.resultType))
- }
-
- case BOX(kind) =>
- stack.pop
- stack.push(BOXED(kind))
-
- case UNBOX(kind) =>
- stack.pop
- stack.push(kind)
-
- case NEW(kind) =>
- stack.push(kind)
-
- case CREATE_ARRAY(elem, dims) =>
- stack.pop(dims)
- stack.push(ARRAY(elem))
-
- case IS_INSTANCE(tpe) =>
- stack.pop
- stack.push(BOOL)
-
- case CHECK_CAST(tpe) =>
- stack.pop
- stack.push(tpe)
-
- case SWITCH(tags, labels) =>
- stack.pop
-
- case JUMP(whereto) =>
- ()
-
- case CJUMP(success, failure, cond, kind) =>
- stack.pop2
-
- case CZJUMP(success, failure, cond, kind) =>
- stack.pop
-
- case RETURN(kind) =>
- if (kind != UNIT)
- stack.pop;
-
- case THROW() =>
- stack.pop
-
- case DROP(kind) =>
- stack.pop
-
- case DUP(kind) =>
- stack.push(stack.head)
-
- case MONITOR_ENTER() =>
- stack.pop
-
- case MONITOR_EXIT() =>
- stack.pop
-
- case SCOPE_ENTER(_) | SCOPE_EXIT(_) =>
- ()
-
- case LOAD_EXCEPTION(_) =>
- stack.pop(stack.length)
- stack.push(typeLattice.Object)
-
- case _ =>
- dumpClassesAndAbort("Unknown instruction: " + i)
- }
-
- new TransferFunction(consumed, gens)
- }
-
- for (b <- blocks) {
- flowFun = flowFun + (b -> transfer(b))
+ var result = lattice.IState(new VarBinding(in.vars), new TypeStack(in.stack))
+ var instrs = b.toList
+ while(!instrs.isEmpty) {
+ val i = instrs.head
+ result = mutatingInterpret(result, i)
+ instrs = instrs.tail
}
+ result
}
-*/
+
/** Abstract interpretation for one instruction. */
def interpret(in: typeFlowLattice.Elem, i: Instruction): typeFlowLattice.Elem = {
val out = lattice.IState(new VarBinding(in.vars), new TypeStack(in.stack))
+ mutatingInterpret(out, i)
+ }
+
+ def mutatingInterpret(out: typeFlowLattice.Elem, i: Instruction): typeFlowLattice.Elem = {
val bindings = out.vars
val stack = out.stack
if (settings.debug.value) {
-// Console.println("[before] Stack: " + stack);
-// Console.println(i);
+ // Console.println("[before] Stack: " + stack);
+ // Console.println(i);
}
i match {
@@ -619,11 +410,292 @@ abstract class TypeFlowAnalysis {
}
}
+ case class CallsiteInfo(bb: icodes.BasicBlock, receiver: Symbol, stackLength: Int, concreteMethod: Symbol)
+
+ /**
+
+ A full type-flow analysis on a method computes in- and out-flows for each basic block (that's what MethodTFA does).
+
+ For the purposes of Inliner, doing so guarantees that an abstract typestack-slot is available by the time an inlining candidate (a CALL_METHOD instruction) is visited.
+ This subclass (MTFAGrowable) of MethodTFA also aims at performing such analysis on CALL_METHOD instructions, with some differences:
+
+ (a) early screening is performed while the type-flow is being computed (in an override of `blockTransfer`) by testing a subset of the conditions that Inliner checks later.
+ The reasoning here is: if the early check fails at some iteration, there's no chance a follow-up iteration (with a yet more lub-ed typestack-slot) will succeed.
+ Failure is sufficient to remove that particular CALL_METHOD from the typeflow's `remainingCALLs`.
+ A forward note: in case inlining occurs at some basic block B, all blocks reachable from B get their CALL_METHOD instructions considered again as candidates
+ (because of the more precise types that -- perhaps -- can be computed).
+
+ (b) in case the early check does not fail, no conclusive decision can be made, thus the CALL_METHOD stays `isOnwatchlist`.
+
+ In other words, `remainingCALLs` tracks those callsites that still remain as candidates for inlining, so that Inliner can focus on those.
+ `remainingCALLs` also caches info about the typestack just before the callsite, so as to spare computing them again at inlining time.
+
+ Besides caching, a further optimization involves skipping those basic blocks whose in-flow and out-flow isn't needed anyway (as explained next).
+ A basic block lacking a callsite in `remainingCALLs`, when visisted by the standard algorithm, won't cause any inlining.
+ But as we know from the way type-flows are computed, computing the in- and out-flow for a basic block relies in general on those of other basic blocks.
+ In detail, we want to focus on that sub-graph of the CFG such that control flow may reach a remaining candidate callsite.
+ Those basic blocks not in that subgraph can be skipped altogether. That's why:
+ - `forwardAnalysis()` in `MTFAGrowable` now checks for inclusion of a basic block in `relevantBBs`
+ - same check is performed before adding a block to the worklist, and as part of choosing successors.
+ The bookkeeping supporting on-the-fly pruning of irrelevant blocks requires overridding most methods of the dataflow-analysis.
+
+ The rest of the story takes place in Inliner, which does not visit all of the method's basic blocks but only on those represented in `remainingCALLs`.
+
+ @author Miguel Garcia, http://lampwww.epfl.ch/~magarcia/ScalaCompilerCornerReloaded/
+
+ */
class MTFAGrowable extends MethodTFA {
import icodes._
- /** discards what must be discarded, blanks what needs to be blanked out, and keeps the rest. */
+ val remainingCALLs = mutable.Map.empty[opcodes.CALL_METHOD, CallsiteInfo]
+
+ val preCandidates = mutable.Set.empty[BasicBlock]
+
+ var callerLin: Traversable[BasicBlock] = null
+
+ override def run {
+
+ timer.start
+ forwardAnalysis(blockTransfer)
+ val t = timer.stop
+
+ /* Now that `forwardAnalysis(blockTransfer)` has finished, all inlining candidates can be found in `remainingCALLs`,
+ whose keys are callsites and whose values are pieces of information about the typestack just before the callsite in question.
+ In order to keep `analyzeMethod()` simple, we collect in `preCandidates` those basic blocks containing at least one candidate. */
+ preCandidates.clear()
+ for(rc <- remainingCALLs) {
+ preCandidates += rc._2.bb
+ }
+
+ if (settings.debug.value) {
+ for(b <- callerLin; if (b != method.startBlock) && preCandidates(b)) {
+ assert(visited.contains(b),
+ "Block " + b + " in " + this.method + " has input equal to bottom -- not visited? .." + visited)
+ }
+ }
+
+ }
+
+ var shrinkedWatchlist = false
+
+ /*
+ This is the method where information cached elsewhere is put to use. References are given those other places that populate those caches.
+
+ The goal is avoiding computing type-flows for blocks we don't need (ie blocks not tracked in `relevantBBs`). The method used to add to `relevantBBs` is `putOnRadar`.
+
+ Moreover, it's often the case that the last CALL_METHOD of interest ("of interest" equates to "being tracked in `isOnWatchlist`) isn't the last instruction on the block.
+ There are cases where the typeflows computed past this `lastInstruction` are needed, and cases when they aren't.
+ The reasoning behind this decsision is described in `populatePerimeter()`. All `blockTransfer()` needs to do (in order to know at which instruction it can stop)
+ is querying `isOnPerimeter`.
+
+ Upon visiting a CALL_METHOD that's an inlining candidate, the relevant pieces of information about the pre-instruction typestack are collected for future use.
+ That is, unless the candidacy test fails. The reasoning here is: if such early check fails at some iteration, there's no chance a follow-up iteration
+ (with a yet more lub-ed typestack-slot) will succeed. In case of failure we can safely remove the CALL_METHOD from both `isOnWatchlist` and `remainingCALLs`.
+
+ */
+ override def blockTransfer(b: BasicBlock, in: lattice.Elem): lattice.Elem = {
+ var result = lattice.IState(new VarBinding(in.vars), new TypeStack(in.stack))
+
+ val stopAt = if(isOnPerimeter(b)) lastInstruction(b) else null;
+ var isPastLast = false
+
+ var instrs = b.toList
+ while(!isPastLast && !instrs.isEmpty) {
+ val i = instrs.head
+
+ if(isOnWatchlist(i)) {
+ val cm = i.asInstanceOf[opcodes.CALL_METHOD]
+ val msym = cm.method
+ val paramsLength = msym.info.paramTypes.size
+ val receiver = result.stack.types.drop(paramsLength).head match {
+ case REFERENCE(s) => s
+ case _ => NoSymbol // e.g. the scrutinee is BOX(s) or ARRAY
+ }
+ val concreteMethod = inliner.lookupImplFor(msym, receiver)
+ val isCandidate = {
+ ( inliner.isClosureClass(receiver) || concreteMethod.isEffectivelyFinal || receiver.isEffectivelyFinal ) &&
+ !blackballed(concreteMethod)
+ }
+ if(isCandidate) {
+ remainingCALLs += Pair(cm, CallsiteInfo(b, receiver, result.stack.length, concreteMethod))
+ } else {
+ remainingCALLs.remove(cm)
+ isOnWatchlist.remove(cm)
+ shrinkedWatchlist = true
+ }
+ }
+
+ isPastLast = (i eq stopAt)
+
+ if(!isPastLast) {
+ result = mutatingInterpret(result, i)
+ instrs = instrs.tail
+ }
+ }
+
+ result
+ } // end of method blockTransfer
+
+ val isOnWatchlist = mutable.Set.empty[Instruction]
+
+ /* Each time CallerCalleeInfo.isSafeToInline determines a concrete callee is unsafe to inline in the current caller,
+ the fact is recorded in this TFA instance for the purpose of avoiding devoting processing to that callsite next time.
+ The condition of "being unsafe to inline in the current caller" sticks across inlinings and TFA re-inits
+ because it depends on the instructions of the callee, which stay unchanged during the course of `analyzeInc(caller)`
+ (with the caveat of the side-effecting `makePublic` in `helperIsSafeToInline`).*/
+ val knownUnsafe = mutable.Set.empty[Symbol]
+ val knownSafe = mutable.Set.empty[Symbol]
+ val knownNever = mutable.Set.empty[Symbol] // `knownNever` needs be cleared only at the very end of the inlining phase (unlike `knownUnsafe` and `knownSafe`)
+ @inline final def blackballed(msym: Symbol): Boolean = { knownUnsafe(msym) || knownNever(msym) }
+
+ val relevantBBs = mutable.Set.empty[BasicBlock]
+
+ private def isPreCandidate(cm: opcodes.CALL_METHOD): Boolean = {
+ val msym = cm.method
+ val style = cm.style
+ // Dynamic == normal invocations
+ // Static(true) == calls to private members
+ !msym.isConstructor && !blackballed(msym) &&
+ (style.isDynamic || (style.hasInstance && style.isStatic))
+ // && !(msym hasAnnotation definitions.ScalaNoInlineClass)
+ }
+
+ override def init(m: icodes.IMethod) {
+ super.init(m)
+ remainingCALLs.clear()
+ knownUnsafe.clear()
+ knownSafe.clear()
+ // initially populate the watchlist with all callsites standing a chance of being inlined
+ isOnWatchlist.clear()
+ relevantBBs.clear()
+ /* TODO Do we want to perform inlining in non-finally exception handlers?
+ * Seems counterproductive (the larger the method the less likely it will be JITed.
+ * It's not that putting on radar only `linearizer linearizeAt (m, m.startBlock)` makes for much shorter inlining times (a minor speedup nonetheless)
+ * but the effect on method size could be explored. */
+ putOnRadar(m.linearizedBlocks(linearizer))
+ populatePerimeter()
+ assert(relevantBBs.isEmpty || relevantBBs.contains(m.startBlock), "you gave me dead code")
+ }
+
+ def conclusives(b: BasicBlock): List[opcodes.CALL_METHOD] = {
+ knownBeforehand(b) filter { cm => inliner.isMonadicMethod(cm.method) || inliner.hasInline(cm.method) }
+ }
+
+ def knownBeforehand(b: BasicBlock): List[opcodes.CALL_METHOD] = {
+ b.toList collect { case c : opcodes.CALL_METHOD => c } filter { cm => isPreCandidate(cm) && isReceiverKnown(cm) }
+ }
+
+ private def isReceiverKnown(cm: opcodes.CALL_METHOD): Boolean = {
+ cm.method.isEffectivelyFinal && cm.method.owner.isEffectivelyFinal
+ }
+
+ private def putOnRadar(blocks: Traversable[BasicBlock]) {
+ for(bb <- blocks) {
+ val preCands = bb.toList collect {
+ case cm : opcodes.CALL_METHOD
+ if isPreCandidate(cm) /* && !isReceiverKnown(cm) */
+ => cm
+ }
+ isOnWatchlist ++= preCands
+ }
+ relevantBBs ++= blocks
+ }
+
+ /* the argument is also included in the result */
+ private def transitivePreds(b: BasicBlock): Set[BasicBlock] = { transitivePreds(List(b)) }
+
+ /* those BBs in the argument are also included in the result */
+ private def transitivePreds(starters: Traversable[BasicBlock]): Set[BasicBlock] = {
+ val result = mutable.Set.empty[BasicBlock]
+ var toVisit: List[BasicBlock] = starters.toList.distinct
+ while(toVisit.nonEmpty) {
+ val h = toVisit.head
+ toVisit = toVisit.tail
+ result += h
+ for(p <- h.predecessors; if !result(p) && !toVisit.contains(p)) { toVisit = p :: toVisit }
+ }
+ result.toSet
+ }
+
+ /* those BBs in the argument are also included in the result */
+ private def transitiveSuccs(starters: Traversable[BasicBlock]): Set[BasicBlock] = {
+ val result = mutable.Set.empty[BasicBlock]
+ var toVisit: List[BasicBlock] = starters.toList.distinct
+ while(toVisit.nonEmpty) {
+ val h = toVisit.head
+ toVisit = toVisit.tail
+ result += h
+ for(p <- h.successors; if !result(p) && !toVisit.contains(p)) { toVisit = p :: toVisit }
+ }
+ result.toSet
+ }
+
+ /* A basic block B is "on the perimeter" of the current control-flow subgraph if none of its successors belongs to that subgraph.
+ * In that case, for the purposes of inlining, we're interested in the typestack right before the last inline candidate in B, not in those afterwards.
+ * In particular we can do without computing the outflow at B. */
+ private def populatePerimeter() {
+ isOnPerimeter.clear()
+ var done = true
+ do {
+ val (frontier, toPrune) = (relevantBBs filter hasNoRelevantSuccs) partition isWatching
+ isOnPerimeter ++= frontier
+ relevantBBs --= toPrune
+ done = toPrune.isEmpty
+ } while(!done)
+
+ lastInstruction.clear()
+ for(b <- isOnPerimeter; val lastIns = b.toList.reverse find isOnWatchlist) {
+ lastInstruction += (b -> lastIns.get.asInstanceOf[opcodes.CALL_METHOD])
+ }
+
+ // assertion: "no relevant block can have a predecessor that is on perimeter"
+ assert((for (b <- relevantBBs; if transitivePreds(b.predecessors) exists isOnPerimeter) yield b).isEmpty)
+ }
+
+ private val isOnPerimeter = mutable.Set.empty[BasicBlock]
+ private val lastInstruction = mutable.Map.empty[BasicBlock, opcodes.CALL_METHOD]
+
+ def hasNoRelevantSuccs(x: BasicBlock): Boolean = { !(x.successors exists relevantBBs) }
+
+ def isWatching(x: BasicBlock): Boolean = (x.toList exists isOnWatchlist)
+
+
+
+
+ /**
+
+ This method is invoked after one or more inlinings have been performed in basic blocks whose in-flow is non-bottom (this makes a difference later).
+ What we know about those inlinings is given by:
+
+ - `staleOut`: These are the blocks where a callsite was inlined.
+ For each callsite, all instructions in that block before the callsite were left in the block, and the rest moved to an `afterBlock`.
+ The out-flow of these basic blocks is thus in general stale, that's why we'll add them to the TFA worklist.
+
+ - `inlined` : These blocks were spliced into the method's CFG as part of inlining. Being new blocks, they haven't been visited yet by the typeflow analysis.
+
+ - `staleIn` : These blocks are what `doInline()` calls `afterBlock`s, ie the new home for instructions that previously appearead
+ after a callsite in a `staleOut` block.
+
+ Based on the above information, we have to bring up-to-date the caches that `forwardAnalysis` and `blockTransfer` use to skip blocks and instructions.
+ Those caches are `relevantBBs` and `isOnPerimeter` (for blocks) and `isOnWatchlist` and `lastInstruction` (for CALL_METHODs).
+ Please notice that all `inlined` and `staleIn` blocks are reachable from `staleOut` blocks.
+
+ The update takes place in two steps:
+
+ (1) `staleOut foreach { so => putOnRadar(linearizer linearizeAt (m, so)) }`
+ This results in initial populations for `relevantBBs` and `isOnWatchlist`.
+ Because of the way `isPreCandidate` reuses previous decision-outcomes that are still valid,
+ this already prunes some candidates standing no chance of being inlined.
+
+ (2) `populatePerimeter()`
+ Based on the CFG-subgraph determined in (1) as reflected in `relevantBBs`,
+ this method detects some blocks whose typeflows aren't needed past a certain CALL_METHOD
+ (not needed because none of its successors is relevant for the purposes of inlining, see `hasNoRelevantSuccs`).
+ The blocks thus chosen are said to be "on the perimeter" of the CFG-subgraph.
+ For each of them, its `lastInstruction` (after which no more typeflows are needed) is found.
+
+ */
def reinit(m: icodes.IMethod, staleOut: List[BasicBlock], inlined: collection.Set[BasicBlock], staleIn: collection.Set[BasicBlock]) {
if (this.method == null || this.method.symbol != m.symbol) {
init(m)
@@ -633,31 +705,102 @@ abstract class TypeFlowAnalysis {
return;
}
- reinit {
- // asserts conveying an idea what CFG shapes arrive here.
- // staleIn foreach (p => assert( !in.isDefinedAt(p), p))
- // staleIn foreach (p => assert(!out.isDefinedAt(p), p))
- // inlined foreach (p => assert( !in.isDefinedAt(p), p))
- // inlined foreach (p => assert(!out.isDefinedAt(p), p))
- // inlined foreach (p => assert(!p.successors.isEmpty || p.lastInstruction.isInstanceOf[icodes.opcodes.THROW], p))
- // staleOut foreach (p => assert( in.isDefinedAt(p), p))
-
- // never rewrite in(m.startBlock)
- staleOut foreach { b =>
- if(!inlined.contains(b)) { worklist += b }
- out(b) = typeFlowLattice.bottom
- }
- // nothing else is added to the worklist, bb's reachable via succs will be tfa'ed
- blankOut(inlined)
- blankOut(staleIn)
- // no need to add startBlocks from m.exh
+ worklist.clear // calling reinit(f: => Unit) would also clear visited, thus forgetting about blocks visited before reinit.
+
+ // asserts conveying an idea what CFG shapes arrive here:
+ // staleIn foreach (p => assert( !in.isDefinedAt(p), p))
+ // staleIn foreach (p => assert(!out.isDefinedAt(p), p))
+ // inlined foreach (p => assert( !in.isDefinedAt(p), p))
+ // inlined foreach (p => assert(!out.isDefinedAt(p), p))
+ // inlined foreach (p => assert(!p.successors.isEmpty || p.lastInstruction.isInstanceOf[icodes.opcodes.THROW], p))
+ // staleOut foreach (p => assert( in.isDefinedAt(p), p))
+
+ // remainingCALLs.clear()
+ isOnWatchlist.clear()
+ relevantBBs.clear()
+
+ // never rewrite in(m.startBlock)
+ staleOut foreach { b =>
+ enqueue(b)
+ out(b) = typeFlowLattice.bottom
}
+ // nothing else is added to the worklist, bb's reachable via succs will be tfa'ed
+ blankOut(inlined)
+ blankOut(staleIn)
+ // no need to add startBlocks from m.exh
+
+ staleOut foreach { so => putOnRadar(linearizer linearizeAt (m, so)) }
+ populatePerimeter()
+
+ } // end of method reinit
+
+ /* this is not a general purpose method to add to the worklist,
+ * because the assert is expected to hold only when called from MTFAGrowable.reinit() */
+ private def enqueue(b: BasicBlock) {
+ assert(in(b) ne typeFlowLattice.bottom)
+ if(!worklist.contains(b)) { worklist += b }
+ }
+
+ /* this is not a general purpose method to add to the worklist,
+ * because the assert is expected to hold only when called from MTFAGrowable.reinit() */
+ private def enqueue(bs: Traversable[BasicBlock]) {
+ bs foreach enqueue
}
private def blankOut(blocks: collection.Set[BasicBlock]) {
blocks foreach { b =>
- in(b) = typeFlowLattice.bottom
- out(b) = typeFlowLattice.bottom
+ in(b) = typeFlowLattice.bottom
+ out(b) = typeFlowLattice.bottom
+ }
+ }
+
+ /*
+ This is basically the plain-old forward-analysis part of a dataflow algorithm,
+ adapted to skip non-relevant blocks (as determined by `reinit()` via `populatePerimeter()`).
+
+ The adaptations are:
+
+ - only relevant blocks dequeued from the worklist move on to have the transfer function applied
+
+ - `visited` now means the transfer function was applied to the block,
+ but please notice that this does not imply anymore its out-flow to be different from bottom,
+ because a block on the perimeter will have per-instruction typeflows computed only up to its `lastInstruction`.
+ In case you need to know whether a visted block `v` has been "fully visited", evaluate `out(v) ne typeflowLattice.bottom`
+
+ - given that the transfer function may remove callsite-candidates from the watchlist (thus, they are not candidates anymore)
+ there's an opportunity to detect whether a previously relevant block has been left without candidates.
+ That's what `shrinkedWatchlist` detects. Provided the block was on the perimeter, we know we can skip it from now now,
+ and we can also constrain the CFG-subgraph by finding a new perimeter (thus the invocation to `populatePerimeter()`).
+ */
+ override def forwardAnalysis(f: (P, lattice.Elem) => lattice.Elem): Unit = {
+ while (!worklist.isEmpty && relevantBBs.nonEmpty) {
+ if (stat) iterations += 1
+ val point = worklist.iterator.next; worklist -= point;
+ if(relevantBBs(point)) {
+ shrinkedWatchlist = false
+ val output = f(point, in(point))
+ visited += point;
+ if(isOnPerimeter(point)) {
+ if(shrinkedWatchlist && !isWatching(point)) {
+ relevantBBs -= point;
+ populatePerimeter()
+ }
+ } else {
+ val propagate = ((lattice.bottom == out(point)) || output != out(point))
+ if (propagate) {
+ out(point) = output
+ val succs = point.successors filter relevantBBs
+ succs foreach { p =>
+ assert((p.predecessors filter isOnPerimeter).isEmpty)
+ val updated = lattice.lub(List(output, in(p)), p.exceptionHandlerStart)
+ if(updated != in(p)) {
+ in(p) = updated
+ enqueue(p)
+ }
+ }
+ }
+ }
+ }
}
}
diff --git a/src/compiler/scala/tools/nsc/backend/opt/Inliners.scala b/src/compiler/scala/tools/nsc/backend/opt/Inliners.scala
index 66f802f74f..3d7f08cebe 100644
--- a/src/compiler/scala/tools/nsc/backend/opt/Inliners.scala
+++ b/src/compiler/scala/tools/nsc/backend/opt/Inliners.scala
@@ -38,6 +38,33 @@ abstract class Inliners extends SubComponent {
res
}
+ /** Look up implementation of method 'sym in 'clazz'.
+ */
+ def lookupImplFor(sym: Symbol, clazz: Symbol): Symbol = {
+ // TODO: verify that clazz.superClass is equivalent here to clazz.tpe.parents(0).typeSymbol (.tpe vs .info)
+ def needsLookup = (
+ (clazz != NoSymbol)
+ && (clazz != sym.owner)
+ && !sym.isEffectivelyFinal
+ && clazz.isEffectivelyFinal
+ )
+ def lookup(clazz: Symbol): Symbol = {
+ // println("\t\tlooking up " + meth + " in " + clazz.fullName + " meth.owner = " + meth.owner)
+ if (sym.owner == clazz || isBottomType(clazz)) sym
+ else sym.overridingSymbol(clazz) match {
+ case NoSymbol => if (sym.owner.isTrait) sym else lookup(clazz.superClass)
+ case imp => imp
+ }
+ }
+ if (needsLookup) {
+ val concreteMethod = lookup(clazz)
+ debuglog("\tlooked up method: " + concreteMethod.fullName)
+
+ concreteMethod
+ }
+ else sym
+ }
+
/* A warning threshold */
private final val MAX_INLINE_MILLIS = 2000
@@ -67,8 +94,7 @@ abstract class Inliners extends SubComponent {
try {
super.run()
} finally {
- inliner.NonPublicRefs.usesNonPublics.clear()
- inliner.recentTFAs.clear
+ inliner.clearCaches()
}
}
}
@@ -80,6 +106,21 @@ abstract class Inliners extends SubComponent {
def isClosureClass(cls: Symbol): Boolean =
cls.isFinal && cls.isSynthetic && !cls.isModuleClass && cls.isAnonymousFunction
+ /*
+ TODO now that Inliner runs faster we could consider additional "monadic methods" (in the limit, all those taking a closure as last arg)
+ Any "monadic method" occurring in a given caller C that is not `isMonadicMethod()` will prevent CloseElim from eliminating
+ any anonymous-closure-class any whose instances are given as argument to C invocations.
+ */
+ def isMonadicMethod(sym: Symbol) = {
+ nme.unspecializedName(sym.name) match {
+ case nme.foreach | nme.filter | nme.withFilter | nme.map | nme.flatMap => true
+ case _ => false
+ }
+ }
+
+ def hasInline(sym: Symbol) = sym hasAnnotation ScalaInlineClass
+ def hasNoInline(sym: Symbol) = sym hasAnnotation ScalaNoInlineClass
+
/**
* Simple inliner.
*/
@@ -92,9 +133,6 @@ abstract class Inliners extends SubComponent {
}
import NonPublicRefs._
- private def hasInline(sym: Symbol) = sym hasAnnotation ScalaInlineClass
- private def hasNoInline(sym: Symbol) = sym hasAnnotation ScalaNoInlineClass
-
/** The current iclass */
private var currentIClazz: IClass = _
private def warn(pos: Position, msg: String) = currentIClazz.cunit.warning(pos, msg)
@@ -121,6 +159,21 @@ abstract class Inliners extends SubComponent {
(hasRETURN, a)
}
+ def clearCaches() {
+ // methods
+ NonPublicRefs.usesNonPublics.clear()
+ recentTFAs.clear
+ tfa.knownUnsafe.clear()
+ tfa.knownSafe.clear()
+ tfa.knownNever.clear()
+ // basic blocks
+ tfa.preCandidates.clear()
+ tfa.relevantBBs.clear()
+ // callsites
+ tfa.remainingCALLs.clear()
+ tfa.isOnWatchlist.clear()
+ }
+
def analyzeClass(cls: IClass): Unit =
if (settings.inline.value) {
debuglog("Analyzing " + cls)
@@ -142,7 +195,38 @@ abstract class Inliners extends SubComponent {
val splicedBlocks = mutable.Set.empty[BasicBlock]
val staleIn = mutable.Set.empty[BasicBlock]
+ /**
+ * A transformation local to the body of the argument.
+ * An linining decision consists in replacing a callsite with the body of the callee.
+ * Please notice that, because `analyzeMethod()` itself may modify a method body,
+ * the particular callee bodies that end up being inlined depend on the particular order in which methods are visited
+ * (no topological ordering over the call-graph is attempted).
+ *
+ * Making an inlining decision requires type-flow information for both caller and callee.
+ * Regarding the caller, such information is needed only for basic blocks containing inlining candidates
+ * (and their transitive predecessors). This observation leads to using a custom type-flow analysis (MTFAGrowable)
+ * that can be re-inited, i.e. that reuses lattice elements (type-flow information) computed in a previous iteration
+ * as starting point for faster convergence in a new iteration.
+ *
+ * The mechanics of inlining are iterative for a given invocation of `analyzeMethod(m)`,
+ * thus considering the basic blocks that successful inlining added in a previous iteration:
+ *
+ * (1) before the iterations proper start, so-called preinlining is performed.
+ * Those callsites whose (receiver, concreteMethod) are both known statically
+ * can be analyzed for inlining before computing a type-flow. Details in `preInline()`
+ *
+ * (2) the first iteration computes type-flow information for basic blocks containing inlining candidates
+ * (and their transitive predecessors), so called `relevantBBs`.
+ * The ensuing analysis of each candidate (performed by `analyzeInc()`)
+ * may result in a CFG isomorphic to that of the callee being inserted where the callsite was
+ * (i.e. a CALL_METHOD instruction is replaced with a single-entry single-exit CFG, which we call "successful inlining").
+ *
+ * (3) following iterations have their relevant basic blocks updated to focus
+ * on the inlined basic blocks and their successors only. Details in `MTFAGrowable.reinit()`
+ * */
def analyzeMethod(m: IMethod): Unit = {
+ // m.normalize
+
var sizeBeforeInlining = m.code.blockCount
var instrBeforeInlining = m.code.instructionCount
var retry = false
@@ -154,17 +238,53 @@ abstract class Inliners extends SubComponent {
val inlinedMethodCount = mutable.HashMap.empty[Symbol, Int] withDefaultValue 0
val caller = new IMethodInfo(m)
- var info: tfa.lattice.Elem = null
- def analyzeInc(msym: Symbol, i: Instruction, bb: BasicBlock): Boolean = {
- var inlined = false
- def paramTypes = msym.info.paramTypes
- val receiver = (info.stack.types drop paramTypes.length) match {
- case Nil => log("analyzeInc(" + msym + "), no type on the stack!") ; NoSymbol
- case REFERENCE(s) :: _ => s
- case _ => NoSymbol
+ def preInline(isFirstRound: Boolean): Int = {
+ val inputBlocks = caller.m.linearizedBlocks()
+ val callsites: Function1[BasicBlock, List[opcodes.CALL_METHOD]] = {
+ if(isFirstRound) tfa.conclusives else tfa.knownBeforehand
}
- val concreteMethod = lookupImplFor(msym, receiver)
+ inlineWithoutTFA(inputBlocks, callsites)
+ }
+
+ /**
+ * Inline straightforward callsites (those that can be inlined without a TFA).
+ *
+ * To perform inlining, all we need to know is listed as formal params in `analyzeInc()`:
+ * - callsite and block containing it
+ * - actual (ie runtime) class of the receiver
+ * - actual (ie runtime) method being invoked
+ * - stack length just before the callsite (to check whether enough arguments have been pushed).
+ * The assert below lists the conditions under which "no TFA is needed"
+ * (the statically known receiver and method are both final, thus, at runtime they can't be any others than those).
+ *
+ */
+ def inlineWithoutTFA(inputBlocks: Traversable[BasicBlock], callsites: Function1[BasicBlock, List[opcodes.CALL_METHOD]]): Int = {
+ var inlineCount = 0
+ import scala.util.control.Breaks._
+ for(x <- inputBlocks; val easyCake = callsites(x); if easyCake.nonEmpty) {
+ breakable {
+ for(ocm <- easyCake) {
+ assert(ocm.method.isEffectivelyFinal && ocm.method.owner.isEffectivelyFinal)
+ if(analyzeInc(ocm, x, ocm.method.owner, -1, ocm.method)) {
+ inlineCount += 1
+ break
+ }
+ }
+ }
+ }
+
+ inlineCount
+ }
+
+ /**
+ Decides whether it's feasible and desirable to inline the body of the method given by `concreteMethod`
+ at the program point given by `i` (a callsite). The boolean result indicates whether inlining was performed.
+
+ */
+ def analyzeInc(i: CALL_METHOD, bb: BasicBlock, receiver: Symbol, stackLength: Int, concreteMethod: Symbol): Boolean = {
+ var inlined = false
+ val msym = i.method
def warnNoInline(reason: String) = {
if (hasInline(msym) && !caller.isBridge)
@@ -209,7 +329,7 @@ abstract class Inliners extends SubComponent {
val inc = new IMethodInfo(callee)
val pair = new CallerCalleeInfo(caller, inc, fresh, inlinedMethodCount)
- if (pair isStampedForInlining info.stack) {
+ if (pair isStampedForInlining stackLength) {
retry = true
inlined = true
if (isCountable)
@@ -228,9 +348,9 @@ abstract class Inliners extends SubComponent {
}
else {
if (settings.debug.value)
- pair logFailure info.stack
+ pair logFailure stackLength
- warnNoInline(pair failureReason info.stack)
+ warnNoInline(pair failureReason stackLength)
}
case None =>
warnNoInline("bytecode was not available")
@@ -241,38 +361,96 @@ abstract class Inliners extends SubComponent {
if (!isAvailable) "bytecode was not available"
else "it can be overridden"
)
+
inlined
}
- import scala.util.control.Breaks._
+ /* Pre-inlining consists in invoking the usual inlining subroutine with (receiver class, concrete method) pairs as input
+ * where both method and receiver are final, which implies that the receiver computed via TFA will always match `concreteMethod.owner`.
+ *
+ * As with any invocation of `analyzeInc()` the inlining outcome is based on heuristics which favor inlining an isMonadicMethod before other methods.
+ * That's why preInline() is invoked twice: any inlinings downplayed by the heuristics during the first round get an opportunity to rank higher during the second.
+ *
+ * As a whole, both `preInline()` invocations amount to priming the inlining process,
+ * so that the first TFA run afterwards is able to gain more information as compared to a cold-start.
+ */
+ val totalPreInlines = {
+ val firstRound = preInline(true)
+ if(firstRound == 0) 0 else (firstRound + preInline(false))
+ }
+ staleOut.clear()
+ splicedBlocks.clear()
+ staleIn.clear()
+
do {
retry = false
log("Analyzing " + m + " count " + count + " with " + caller.length + " blocks")
+
+ /* it's important not to inline in unreachable basic blocks. linearizedBlocks() returns only reachable ones. */
+ tfa.callerLin = caller.m.linearizedBlocks()
+ /* TODO Do we want to perform inlining in non-finally exception handlers?
+ * Seems counterproductive (the larger the method the less likely it will be JITed).
+ * The alternative above would be `linearizer.linearizeAt(caller.m, caller.m.startBlock)`.
+ * See also comment on the same topic in TypeFlowAnalysis. */
+
tfa.reinit(m, staleOut.toList, splicedBlocks, staleIn)
tfa.run
staleOut.clear()
splicedBlocks.clear()
staleIn.clear()
- caller.m.linearizedBlocks() foreach { bb =>
- info = tfa in bb
-
+ import scala.util.control.Breaks._
+ for(bb <- tfa.callerLin; if tfa.preCandidates(bb)) {
+ val cms = bb.toList collect { case cm : CALL_METHOD => cm }
breakable {
- for (i <- bb) {
- i match {
- // Dynamic == normal invocations
- // Static(true) == calls to private members
- case CALL_METHOD(msym, Dynamic | Static(true)) if !msym.isConstructor =>
- if (analyzeInc(msym, i, bb)) {
- break
- }
- case _ => ()
+ for (cm <- cms; if tfa.remainingCALLs.isDefinedAt(cm)) {
+ val analysis.CallsiteInfo(_, receiver, stackLength, concreteMethod) = tfa.remainingCALLs(cm)
+ if (analyzeInc(cm, bb, receiver, stackLength, concreteMethod)) {
+ break
}
- info = tfa.interpret(info, i)
}
}
+ }
+
+ /* As part of inlining, some instructions are moved to a new block.
+ * In detail: the instructions moved to a new block originally appeared after a (by now inlined) callsite.
+ * Their new home is an `afterBlock` created by `doInline()` to that effect.
+ * Each block in staleIn is one such `afterBlock`.
+ *
+ * Some of those instructions may be CALL_METHOD possibly tracked in `remainingCALLs`
+ * (with an entry still noting the old containing block). However, that causes no problem:
+ *
+ * (1) such callsites won't be analyzed for inlining by `analyzeInc()` (*in this iteration*)
+ * because of the `break` that abandons the original basic block where it was contained.
+ *
+ * (2) Additionally, its new containing block won't be visited either (*in this iteration*)
+ * because the new blocks don't show up in the linearization computed before inlinings started:
+ * `for(bb <- tfa.callerLin; if tfa.preCandidates(bb)) {`
+ *
+ * For a next iteration, the new home of any instructions that have moved
+ * will be tracked properly in `remainingCALLs` after `MTFAGrowable.reinit()` puts on radar their new homes.
+ *
+ */
+ if(retry) {
+ for(afterBlock <- staleIn) {
+ val justCALLsAfter = afterBlock.toList collect { case c : opcodes.CALL_METHOD => c }
+ for(ia <- justCALLsAfter) { tfa.remainingCALLs.remove(ia) }
+ }
+ }
+ /*
+ if(splicedBlocks.nonEmpty) { // TODO explore (saves time but leads to slightly different inlining decisions)
+ // opportunistically perform straightforward inlinings before the next typeflow round
+ val savedRetry = retry
+ val savedStaleOut = staleOut.toSet; staleOut.clear()
+ val savedStaleIn = staleIn.toSet ; staleIn.clear()
+ val howmany = inlineWithoutTFA(splicedBlocks, tfa.knownBeforehand)
+ splicedBlocks ++= staleIn
+ staleOut.clear(); staleOut ++= savedStaleOut;
+ staleIn.clear(); staleIn ++= savedStaleIn;
+ retry = savedRetry
}
+ */
if (tfa.stat)
log(m.symbol.fullName + " iterations: " + tfa.iterations + " (size: " + caller.length + ")")
@@ -288,13 +466,6 @@ abstract class Inliners extends SubComponent {
}
}
- private def isMonadicMethod(sym: Symbol) = {
- nme.unspecializedName(sym.name) match {
- case nme.foreach | nme.filter | nme.withFilter | nme.map | nme.flatMap => true
- case _ => false
- }
- }
-
private def isHigherOrderMethod(sym: Symbol) =
sym.isMethod && atPhase(currentRun.erasurePhase.prev)(sym.info.paramTypes exists isFunctionType)
@@ -308,33 +479,6 @@ abstract class Inliners extends SubComponent {
res
}
- /** Look up implementation of method 'sym in 'clazz'.
- */
- def lookupImplFor(sym: Symbol, clazz: Symbol): Symbol = {
- // TODO: verify that clazz.superClass is equivalent here to clazz.tpe.parents(0).typeSymbol (.tpe vs .info)
- def needsLookup = (
- (clazz != NoSymbol)
- && (clazz != sym.owner)
- && !sym.isEffectivelyFinal
- && clazz.isEffectivelyFinal
- )
- def lookup(clazz: Symbol): Symbol = {
- // println("\t\tlooking up " + meth + " in " + clazz.fullName + " meth.owner = " + meth.owner)
- if (sym.owner == clazz || isBottomType(clazz)) sym
- else sym.overridingSymbol(clazz) match {
- case NoSymbol => if (sym.owner.isTrait) sym else lookup(clazz.superClass)
- case imp => imp
- }
- }
- if (needsLookup) {
- val concreteMethod = lookup(clazz)
- debuglog("\tlooked up method: " + concreteMethod.fullName)
-
- concreteMethod
- }
- else sym
- }
-
class IMethodInfo(val m: IMethod) {
val sym = m.symbol
val name = sym.name
@@ -386,10 +530,13 @@ abstract class Inliners extends SubComponent {
/** Inline 'inc' into 'caller' at the given block and instruction.
* The instruction must be a CALL_METHOD.
*/
- def doInline(block: BasicBlock, instr: Instruction) {
+ def doInline(block: BasicBlock, instr: CALL_METHOD) {
staleOut += block
+ tfa.remainingCALLs.remove(instr) // this bookkpeeping is done here and not in MTFAGrowable.reinit due to (1st) convenience and (2nd) necessity.
+ tfa.isOnWatchlist.remove(instr) // ditto
+
val targetPos = instr.pos
log("Inlining " + inc.m + " in " + caller.m + " at pos: " + posToStr(targetPos))
@@ -557,10 +704,10 @@ abstract class Inliners extends SubComponent {
if (settings.debug.value) icodes.checkValid(caller.m)
}
- def isStampedForInlining(stack: TypeStack) =
- !sameSymbols && inc.m.hasCode && shouldInline && isSafeToInline(stack)
+ def isStampedForInlining(stackLength: Int) =
+ !sameSymbols && inc.m.hasCode && shouldInline && isSafeToInline(stackLength)
- def logFailure(stack: TypeStack) = log(
+ def logFailure(stackLength: Int) = log(
"""|inline failed for %s:
| pair.sameSymbols: %s
| inc.numInlined < 2: %s
@@ -569,13 +716,13 @@ abstract class Inliners extends SubComponent {
| shouldInline: %s
""".stripMargin.format(
inc.m, sameSymbols, inlinedMethodCount(inc.sym) < 2,
- inc.m.hasCode, isSafeToInline(stack), shouldInline
+ inc.m.hasCode, isSafeToInline(stackLength), shouldInline
)
)
- def failureReason(stack: TypeStack) =
+ def failureReason(stackLength: Int) =
if (!inc.m.hasCode) "bytecode was unavailable"
- else if (!isSafeToInline(stack)) "it is unsafe (target may reference private fields)"
+ else if (!isSafeToInline(stackLength)) "it is unsafe (target may reference private fields)"
else "of a bug (run with -Ylog:inline -Ydebug for more information)"
def canAccess(level: NonPublicRefs.Value) = level match {
@@ -587,15 +734,26 @@ abstract class Inliners extends SubComponent {
private def sameOwner = caller.owner == inc.owner
/** A method is safe to inline when:
- * - it does not contain calls to private methods when
- * called from another class
+ * - it does not contain calls to private methods when called from another class
* - it is not inlined into a position with non-empty stack,
* while having a top-level finalizer (see liftedTry problem)
* - it is not recursive
* Note:
* - synthetic private members are made public in this pass.
*/
- def isSafeToInline(stack: TypeStack): Boolean = {
+ def isSafeToInline(stackLength: Int): Boolean = {
+
+ if(tfa.blackballed(inc.sym)) { return false }
+ if(tfa.knownSafe(inc.sym)) { return true }
+
+ if(helperIsSafeToInline(stackLength)) {
+ tfa.knownSafe += inc.sym; true
+ } else {
+ tfa.knownUnsafe += inc.sym; false
+ }
+ }
+
+ private def helperIsSafeToInline(stackLength: Int): Boolean = {
def makePublic(f: Symbol): Boolean =
(inc.m.sourceFile ne NoSourceFile) && (f.isSynthetic || f.isParamAccessor) && {
debuglog("Making not-private symbol out of synthetic: " + f)
@@ -642,9 +800,10 @@ abstract class Inliners extends SubComponent {
})
canAccess(accessNeeded) && {
- val isIllegalStack = (stack.length > inc.minimumStack && inc.hasNonFinalizerHandler)
+ val isIllegalStack = (stackLength > inc.minimumStack && inc.hasNonFinalizerHandler)
+
!isIllegalStack || {
- debuglog("method " + inc.sym + " is used on a non-empty stack with finalizer. Stack: " + stack)
+ debuglog("method " + inc.sym + " is used on a non-empty stack with finalizer.")
false
}
}
diff --git a/src/compiler/scala/tools/nsc/interpreter/MemberHandlers.scala b/src/compiler/scala/tools/nsc/interpreter/MemberHandlers.scala
index c742ab89c0..48a5fa9e34 100644
--- a/src/compiler/scala/tools/nsc/interpreter/MemberHandlers.scala
+++ b/src/compiler/scala/tools/nsc/interpreter/MemberHandlers.scala
@@ -118,8 +118,9 @@ trait MemberHandlers {
class DefHandler(member: DefDef) extends MemberDefHandler(member) {
private def vparamss = member.vparamss
- // true if 0-arity
- override def definesValue = vparamss.isEmpty || vparamss.head.isEmpty
+ private def isMacro = member.mods.hasFlag(scala.reflect.internal.Flags.MACRO)
+ // true if not a macro and 0-arity
+ override def definesValue = !isMacro && (vparamss.isEmpty || vparamss.head.isEmpty)
override def resultExtractionCode(req: Request) =
if (mods.isPublic) codegenln(name, ": ", req.typeOf(name)) else ""
}
diff --git a/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala b/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala
index 7f7f7e7b65..252b3ccffc 100644
--- a/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala
+++ b/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala
@@ -517,7 +517,7 @@ abstract class ExplicitOuter extends InfoTransform
super.transform(treeCopy.Apply(tree, sel, outerVal :: args))
// entry point for pattern matcher translation
- case mch: Match =>
+ case mch: Match if (!opt.virtPatmat) => // don't use old pattern matcher as fallback when the user wants the virtualizing one
matchTranslation(mch)
case _ =>
diff --git a/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala b/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala
index 8c34a9139d..8b1d5b3295 100644
--- a/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala
+++ b/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala
@@ -121,9 +121,11 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers {
// then pos/spec-List.scala fails - why? Does this kind of check fail
// for similar reasons? Does `sym.isAbstractType` make a difference?
private def isSpecializedAnyRefSubtype(tp: Type, sym: Symbol) = (
- (specializedOn(sym) contains AnyRefModule)
+ // !!! Come back to this, not sure it's recognizing AnyRefModule
+ (specializedOn(sym) exists (s => !isValueClass(s)))
&& !isValueClass(tp.typeSymbol)
&& isBoundedGeneric(tp)
+ // && (tp <:< AnyRefClass.tpe)
)
private def isBoundedGeneric(tp: Type) = tp match {
case TypeRef(_, sym, _) if sym.isAbstractType => (tp <:< AnyRefClass.tpe)
@@ -279,10 +281,10 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers {
val pre1 = this(pre)
// when searching for a specialized class, take care to map all
// type parameters that are subtypes of AnyRef to AnyRef
- val args1 = map2(args, sym.typeParams) {
- case (tp, orig) if isSpecializedAnyRefSubtype(tp, orig) => AnyRefClass.tpe
- case (tp, _) => tp
- }
+ val args1 = map2(args, sym.info.typeParams)((tp, orig) =>
+ if (isSpecializedAnyRefSubtype(tp, orig)) AnyRefClass.tpe
+ else tp
+ )
specializedClass.get((sym, TypeEnv.fromSpecialization(sym, args1))) match {
case Some(sym1) => typeRef(pre1, sym1, survivingArgs(sym, args))
case None => typeRef(pre1, sym, args)
@@ -305,7 +307,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers {
else specializedTypeVars(sym).intersect(env.keySet)
)
val (methparams, others) = tvars.toList sortBy ("" + _.name) partition (_.owner.isMethod)
- debuglog("specName(" + sym + ") env: " + env + " tvars: " + tvars)
+ log("specName(" + sym + ") env: " + env + " tvars: " + tvars)
specializedName(sym.name, methparams map env, others map env)
}
@@ -322,10 +324,9 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers {
nme.getterToLocal(specializedName(nme.localToGetter(name), types1, types2))
else {
val (base, cs, ms) = nme.splitSpecializedName(name)
- val abbrevs = definitions.abbrvTag withDefaultValue definitions.abbrvTag(ObjectClass)
newTermName(base.toString + "$"
- + "m" + ms + types1.map(t => abbrevs(t.typeSymbol)).mkString("", "", "")
- + "c" + cs + types2.map(t => abbrevs(t.typeSymbol)).mkString("", "", "$sp"))
+ + "m" + ms + types1.map(t => definitions.abbrvTag(t.typeSymbol)).mkString("", "", "")
+ + "c" + cs + types2.map(t => definitions.abbrvTag(t.typeSymbol)).mkString("", "", "$sp"))
}
}
@@ -370,8 +371,10 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers {
case set :: Nil => set map (x => List(x))
case set :: sets => for (x <- set ; xs <- loop(sets)) yield x :: xs
}
- // zip the keys with each permutation to create a TypeEnv
- loop(keys map concreteTypes) map (xss => Map(keys zip xss: _*))
+ // zip the keys with each permutation to create a TypeEnv.
+ // If we don't exclude the "all AnyRef" specialization, we will
+ // incur duplicate members and crash during mixin.
+ loop(keys map concreteTypes) filterNot (_ forall (_ <:< AnyRefClass.tpe)) map (xss => Map(keys zip xss: _*))
}
/** Does the given 'sym' need to be specialized in the environment 'env'?
@@ -962,6 +965,10 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers {
}
case object UnifyError extends scala.util.control.ControlThrowable
+ private[this] def unifyError(tp1: Any, tp2: Any): Nothing = {
+ log("unifyError" + ((tp1, tp2)))
+ throw UnifyError
+ }
/** Return the most general type environment that specializes tp1 to tp2.
* It only allows binding of type parameters annotated with @specialized.
@@ -972,29 +979,34 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers {
private def unify(tp1: Type, tp2: Type, env: TypeEnv, strict: Boolean): TypeEnv = (tp1, tp2) match {
case (TypeRef(_, sym1, _), _) if isSpecialized(sym1) =>
debuglog("Unify - basic case: " + tp1 + ", " + tp2)
- if (isValueClass(tp2.typeSymbol) || isSpecializedAnyRefSubtype(tp2, sym1))
+ if (isValueClass(tp2.typeSymbol))
env + ((sym1, tp2))
+ else if (isSpecializedAnyRefSubtype(tp2, sym1))
+ env + ((sym1, tp2)) // env + ((sym1, AnyRefClass.tpe))
+ else if (strict)
+ unifyError(tp1, tp2)
else
- if (strict) throw UnifyError else env
+ env
case (TypeRef(_, sym1, args1), TypeRef(_, sym2, args2)) =>
debuglog("Unify TypeRefs: " + tp1 + " and " + tp2 + " with args " + (args1, args2) + " - ")
- if (strict && args1.length != args2.length) throw UnifyError
+ if (strict && args1.length != args2.length) unifyError(tp1, tp2)
val e = unify(args1, args2, env, strict)
debuglog("unified to: " + e)
e
case (TypeRef(_, sym1, _), _) if sym1.isTypeParameterOrSkolem =>
env
case (MethodType(params1, res1), MethodType(params2, res2)) =>
- if (strict && params1.length != params2.length) throw UnifyError
+ if (strict && params1.length != params2.length) unifyError(tp1, tp2)
debuglog("Unify MethodTypes: " + tp1 + " and " + tp2)
unify(res1 :: (params1 map (_.tpe)), res2 :: (params2 map (_.tpe)), env, strict)
case (PolyType(tparams1, res1), PolyType(tparams2, res2)) =>
- if (strict && tparams1.length != tparams2.length) throw UnifyError
debuglog("Unify PolyTypes: " + tp1 + " and " + tp2)
- unify(res1, res2, env, strict)
- case (PolyType(_, res), other) =>
- unify(res, other, env, strict)
- case (ThisType(_), ThisType(_)) => env
+ if (strict && tparams1.length != tparams2.length)
+ unifyError(tp1, tp2)
+ else
+ unify(res1, res2, env, strict)
+ case (PolyType(_, res), other) => unify(res, other, env, strict)
+ case (ThisType(_), ThisType(_)) => env
case (_, SingleType(_, _)) => unify(tp1, tp2.underlying, env, strict)
case (SingleType(_, _), _) => unify(tp1.underlying, tp2, env, strict)
case (ThisType(_), _) => unify(tp1.widen, tp2, env, strict)
@@ -1016,7 +1028,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers {
if (env.keySet intersect nenv.keySet isEmpty) env ++ nenv
else {
debuglog("could not unify: u(" + args._1 + ", " + args._2 + ") yields " + nenv + ", env: " + env)
- throw UnifyError
+ unifyError(tp1, tp2)
}
}
}
@@ -1242,7 +1254,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers {
}
}
- def doesConform(origSymbol: Symbol, treeType: Type, memberType: Type, env: TypeEnv) =
+ def doesConform(origSymbol: Symbol, treeType: Type, memberType: Type, env: TypeEnv) = {
(treeType =:= memberType) || { // anyref specialization
memberType match {
case PolyType(_, resTpe) =>
@@ -1259,6 +1271,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers {
case _ => false
}
}
+ }
override def transform(tree: Tree): Tree = {
val symbol = tree.symbol
@@ -1289,6 +1302,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers {
curTree = tree
tree match {
case Apply(Select(New(tpt), nme.CONSTRUCTOR), args) =>
+ // log("Attempting to specialize new %s(%s)".format(tpt, args.mkString(", ")))
if (findSpec(tpt.tpe).typeSymbol ne tpt.tpe.typeSymbol) {
// the ctor can be specialized
log("** instantiated specialized type: " + findSpec(tpt.tpe))
diff --git a/src/compiler/scala/tools/nsc/transform/UnCurry.scala b/src/compiler/scala/tools/nsc/transform/UnCurry.scala
index ab4a2141a9..841cd4a204 100644
--- a/src/compiler/scala/tools/nsc/transform/UnCurry.scala
+++ b/src/compiler/scala/tools/nsc/transform/UnCurry.scala
@@ -228,9 +228,9 @@ abstract class UnCurry extends InfoTransform
* case P_1 if G_1 => E_1
* ...
* case P_n if G_n => true
- * case _ => this.missingCase(x)
+ * case _ => this.missingCase(expr)
* }
- * def isDefinedAtCurrent(x: T): boolean = (x: @unchecked) match {
+ * def _isDefinedAt(x: T): boolean = (x: @unchecked) match {
* case P_1 if G_1 => true
* ...
* case P_n if G_n => true
@@ -240,7 +240,7 @@ abstract class UnCurry extends InfoTransform
* new $anon()
*
* However, if one of the patterns P_i if G_i is a default pattern,
- * drop the last default clause in tghe definition of `apply` and generate for `isDefinedAtCurrent` instead
+ * drop the last default clause in the definition of `apply` and generate for `_isDefinedAt` instead
*
* def isDefinedAtCurrent(x: T): boolean = true
*/
@@ -291,73 +291,26 @@ abstract class UnCurry extends InfoTransform
val substParam = new TreeSymSubstituter(fun.vparams map (_.symbol), params)
def substTree[T <: Tree](t: T): T = substParam(resetLocalAttrs(t))
- // waiting here until we can mix case classes and extractors reliably (i.e., when virtpatmat becomes the default)
- // object VirtPatmatOpt {
- // object Last {
- // def unapply[T](xs: List[T]) = xs.lastOption
- // }
- // // keep this in synch by what's generated by combineCases/runOrElse
- // object MatcherBlock {
- // def unapply(matcher: Tree): Option[(ValDef, ValDef, ValDef, ValDef, List[Tree])] = matcher match { // TODO: BUG the unapplySeq version of the case below does not seem to work in virtpatmat??
- // case Block((zero: ValDef) :: (x: ValDef) :: (matchRes: ValDef) :: (keepGoing: ValDef) :: stats, _) => Some(zero, x, matchRes, keepGoing, stats)
- // case _ => None
- // }
- // }
- // // TODO: virtpatmat use case: would be nice if could abstract over the repeated pattern more easily
- // // case Block(Last(P)) =>
- // // case P =>
- // def unapply(matcher: Tree): Option[(ValDef, ValDef, ValDef, ValDef, List[Tree], Tree => Tree)] = matcher match {
- // case MatcherBlock(zero, x, matchRes, keepGoing, stats) => Some(zero, x, matchRes, keepGoing, stats, identity[Tree])
- // case Block(outerStats, MatcherBlock(zero, x, matchRes, keepGoing, stats)) => Some(zero, x, matchRes, keepGoing, stats, inner => Block(outerStats, inner))
- // case b => treeBrowser browse b; None
- // }
- // }
-
- // TODO: optimize duplication, but make sure ValDef's introduced by wrap are treated correctly
- def dupMatch(selector: Tree, cases: List[CaseDef], wrap: Match => Tree = identity) = {
- def transformCase(cdef: CaseDef): CaseDef =
- CaseDef(cdef.pat, cdef.guard, Literal(Constant(true)))
- def defaultCase = CaseDef(Ident(nme.WILDCARD), EmptyTree, Literal(Constant(false)))
-
- gen.mkUncheckedMatch(
- if (cases exists treeInfo.isDefaultCase) Literal(Constant(true))
- else substTree(wrap(Match(selector, (cases map transformCase) :+ defaultCase)).duplicate)
- )
- }
+ object isDefinedAtTransformer extends gen.MatchMatcher {
+ // TODO: optimize duplication, but make sure ValDef's introduced by wrap are treated correctly
+ override def caseMatch(orig: Tree, selector: Tree, cases: List[CaseDef], wrap: Tree => Tree): Tree = {
+ def transformCase(cdef: CaseDef): CaseDef =
+ CaseDef(cdef.pat, cdef.guard, Literal(Constant(true)))
- def dupVirtMatch(zero: ValDef, x: ValDef, matchRes: ValDef, keepGoing: ValDef, stats: List[Tree], wrap: Block => Tree = identity) = {
- object dropMatchResAssign extends Transformer {
- // override val treeCopy = newStrictTreeCopier // will duplicate below
- override def transform(tree: Tree): Tree = tree match {
- // don't compute the result of the match -- remove the block for the RHS (emitted by pmgen.one), except for the assignment to keepGoing
- case Block(List(matchRes, ass@Assign(keepGoingLhs, falseLit)), zero) if keepGoingLhs.symbol eq keepGoing.symbol =>
- Block(List(ass), zero)
- case _ =>
- super.transform(tree)
- }
+ def defaultCase = CaseDef(Ident(nme.WILDCARD), EmptyTree, Literal(Constant(false)))
+
+ val casesNoSynthCatchAll = dropSyntheticCatchAll(cases)
+
+ gen.mkUncheckedMatch(
+ if (casesNoSynthCatchAll exists treeInfo.isDefaultCase) Literal(Constant(true))
+ else substTree(wrap(Match(selector, (casesNoSynthCatchAll map transformCase) :+ defaultCase)).duplicate)
+ )
}
- val statsNoMatchRes: List[Tree] = stats map (dropMatchResAssign.transform) toList
- val idaBlock = wrap(Block(
- zero ::
- x ::
- /* drop matchRes def */
- keepGoing ::
- statsNoMatchRes,
- NOT(REF(keepGoing.symbol)) // replace `if (keepGoing) throw new MatchError(...) else matchRes` by `!keepGoing`
- ))
- substTree(idaBlock.duplicate) // duplicate on block as a whole to ensure valdefs are properly cloned and substed
- }
- DefDef(m, (fun.body: @unchecked) match {
- case Match(selector, cases) =>
- dupMatch(selector, cases)
- case Block((vd: ValDef) :: Nil, Match(selector, cases)) => // can't factor this out using an extractor due to bugs in the old pattern matcher
- dupMatch(selector, cases, m => Block(List(vd), m))
- // virtpatmat -- TODO: find a better way to keep this in synch with the code generated by patmatvirtualizer
- case Apply(Apply(TypeApply(Select(tgt, nme.runOrElse), targs), args_scrut), args_pm) if opt.virtPatmat =>
+ override def caseVirtualizedMatch(orig: Tree, _match: Tree, targs: List[Tree], scrut: Tree, matcher: Tree): Tree = {
object noOne extends Transformer {
override val treeCopy = newStrictTreeCopier // must duplicate everything
- val one = tgt.tpe member newTermName("one")
+ val one = _match.tpe member newTermName("one")
override def transform(tree: Tree): Tree = tree match {
case Apply(fun, List(a)) if fun.symbol == one =>
// blow one's argument away since all we want to know is whether the match succeeds or not
@@ -367,15 +320,34 @@ abstract class UnCurry extends InfoTransform
super.transform(tree)
}
}
- substTree(Apply(Apply(TypeApply(Select(tgt.duplicate, tgt.tpe.member(newTermName("isSuccess"))), targs map (_.duplicate)), args_scrut map (_.duplicate)), args_pm map (noOne.transform)))
- // for the optimized version of virtpatmat
- case Block((zero: ValDef) :: (x: ValDef) :: (matchRes: ValDef) :: (keepGoing: ValDef) :: stats, _) if opt.virtPatmat =>
- dupVirtMatch(zero, x, matchRes, keepGoing, stats)
- case Block(outerStats, Block((zero: ValDef) :: (x: ValDef) :: (matchRes: ValDef) :: (keepGoing: ValDef) :: stats, _)) if opt.virtPatmat => // can't factor this out using an extractor due to bugs in the old pattern matcher
- dupVirtMatch(zero, x, matchRes, keepGoing, stats, m => Block(outerStats, m))
- // case other =>
- // treeBrowser browse other
- })
+ substTree(Apply(Apply(TypeApply(Select(_match.duplicate, _match.tpe.member(newTermName("isSuccess"))), targs map (_.duplicate)), List(scrut.duplicate)), List(noOne.transform(matcher))))
+ }
+
+ override def caseVirtualizedMatchOpt(orig: Tree, zero: ValDef, x: ValDef, matchRes: ValDef, keepGoing: ValDef, stats: List[Tree], epilogue: Tree, wrap: Tree => Tree) = {
+ object dropMatchResAssign extends Transformer {
+ // override val treeCopy = newStrictTreeCopier // will duplicate below
+ override def transform(tree: Tree): Tree = tree match {
+ // don't compute the result of the match -- remove the block for the RHS (emitted by pmgen.one), except for the assignment to keepGoing
+ case gen.VirtualCaseDef(assignKeepGoing, matchRes, zero) if assignKeepGoing.lhs.symbol eq keepGoing.symbol =>
+ Block(List(assignKeepGoing), zero)
+ case _ =>
+ super.transform(tree)
+ }
+ }
+ val statsNoMatchRes: List[Tree] = stats map (dropMatchResAssign.transform) toList
+ val idaBlock = wrap(Block(
+ zero ::
+ x ::
+ /* drop matchRes def */
+ keepGoing ::
+ statsNoMatchRes,
+ NOT(REF(keepGoing.symbol)) // replace `if (keepGoing) throw new MatchError(...) else matchRes` epilogue by `!keepGoing`
+ ))
+ substTree(idaBlock.duplicate) // duplicate on block as a whole to ensure valdefs are properly cloned and substed
+ }
+ }
+
+ DefDef(m, isDefinedAtTransformer(fun.body))
}
val members =
@@ -679,7 +651,8 @@ abstract class UnCurry extends InfoTransform
if (dd.symbol hasAnnotation VarargsClass) addJavaVarargsForwarders(dd, flatdd, tree)
flatdd
case Try(body, catches, finalizer) =>
- if (catches forall treeInfo.isCatchCase) tree
+ if (opt.virtPatmat) { if(catches exists (cd => !treeInfo.isCatchCase(cd))) debugwarn("VPM BUG! illegal try/catch "+ catches); tree }
+ else if (catches forall treeInfo.isCatchCase) tree
else {
val exname = unit.freshTermName("ex$")
val cases =
@@ -701,7 +674,7 @@ abstract class UnCurry extends InfoTransform
}
debuglog("rewrote try: " + catches + " ==> " + catchall);
val catches1 = localTyper.typedCases(
- tree, List(catchall), ThrowableClass.tpe, WildcardType)
+ List(catchall), ThrowableClass.tpe, WildcardType)
treeCopy.Try(tree, body, catches1, finalizer)
}
case Apply(Apply(fn, args), args1) =>
diff --git a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala
index 6ee09d064f..ea5223e32f 100644
--- a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala
@@ -636,11 +636,6 @@ trait ContextErrors {
def CyclicReferenceError(errPos: Position, lockedSym: Symbol) =
issueTypeError(PosAndMsgTypeError(errPos, "illegal cyclic reference involving " + lockedSym))
-
- def MacroExpandError(tree: Tree, t: Any) = {
- issueNormalTypeError(tree, "macros must return a compiler-specific tree; returned class is: " + t.getClass)
- setError(tree)
- }
}
}
@@ -713,10 +708,17 @@ trait ContextErrors {
"constructor cannot be instantiated to expected type" + foundReqMsg(restpe, pt))
setError(tree)
}
-
- def NoBestMethodAlternativeError(tree: Tree, argtpes: List[Type], pt: Type) =
+
+ def NoBestMethodAlternativeError(tree: Tree, argtpes: List[Type], pt: Type) = {
issueNormalTypeError(tree,
applyErrorMsg(tree, " cannot be applied to ", argtpes, pt))
+ // since inferMethodAlternative modifies the state of the tree
+ // we have to set the type of tree to ErrorType only in the very last
+ // fallback action that is done in the inference (tracking it manually is error prone).
+ // This avoids entering infinite loop in doTypeApply.
+ // TODO: maybe we should do the same thing with inferExprAlternative.
+ if (implicitly[Context].reportErrors) setError(tree)
+ }
def AmbiguousMethodAlternativeError(tree: Tree, pre: Type, best: Symbol,
firstCompeting: Symbol, argtpes: List[Type], pt: Type) = {
diff --git a/src/compiler/scala/tools/nsc/typechecker/Infer.scala b/src/compiler/scala/tools/nsc/typechecker/Infer.scala
index b97fbebec2..e1aa8b46eb 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Infer.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Infer.scala
@@ -1388,7 +1388,7 @@ trait Infer {
NoBestExprAlternativeError(tree, pt)
} else if (!competing.isEmpty) {
if (secondTry) NoBestExprAlternativeError(tree, pt)
- else { if (!pt.isErroneous) AmbiguousExprAlternativeError(tree, pre, best, competing.head, pt) }
+ else if (!pt.isErroneous) AmbiguousExprAlternativeError(tree, pre, best, competing.head, pt)
} else {
// val applicable = alts1 filter (alt =>
// global.typer.infer.isWeaklyCompatible(pre.memberType(alt), pt))
@@ -1398,10 +1398,14 @@ trait Infer {
}
}
- @inline private def inSilentMode(expr: Typer => Boolean): Boolean = {
- val silentContext = context.makeSilent(context.ambiguousErrors)
- val res = expr(newTyper(silentContext))
- if (silentContext.hasErrors) false else res
+ @inline private def inSilentMode(context: Context)(expr: => Boolean): Boolean = {
+ val oldState = context.state
+ context.setBufferErrors()
+ val res = expr
+ val contextWithErrors = context.hasErrors
+ context.flushBuffer()
+ context.restoreState(oldState)
+ res && !contextWithErrors
}
// Checks against the name of the parameter and also any @deprecatedName.
@@ -1472,7 +1476,7 @@ trait Infer {
val applicable = resolveOverloadedMethod(argtpes, {
alts filter { alt =>
- inSilentMode(typer0 => typer0.infer.isApplicable(undetparams, followApply(pre.memberType(alt)), argtpes, pt)) &&
+ inSilentMode(context)(isApplicable(undetparams, followApply(pre.memberType(alt)), argtpes, pt)) &&
(!varArgsOnly || isVarArgsList(alt.tpe.params))
}
})
diff --git a/src/compiler/scala/tools/nsc/typechecker/Macros.scala b/src/compiler/scala/tools/nsc/typechecker/Macros.scala
index 470f3e7117..161e4b7a9a 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Macros.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Macros.scala
@@ -90,8 +90,19 @@ trait Macros { self: Analyzer =>
lazy val mirror = new scala.reflect.runtime.Mirror {
lazy val libraryClassLoader = {
+ // todo. this is more or less okay, but not completely correct
+ // see https://issues.scala-lang.org/browse/SI-5433 for more info
val classpath = global.classPath.asURLs
- ScalaClassLoader.fromURLs(classpath, self.getClass.getClassLoader)
+ var loader: ClassLoader = ScalaClassLoader.fromURLs(classpath, self.getClass.getClassLoader)
+
+ // an heuristic to detect REPL
+ if (global.settings.exposeEmptyPackage.value) {
+ import scala.tools.nsc.interpreter._
+ val virtualDirectory = global.settings.outputDirs.getSingleOutput.get
+ loader = new AbstractFileClassLoader(virtualDirectory, loader) {}
+ }
+
+ loader
}
override def defaultReflectiveClassLoader() = libraryClassLoader
@@ -112,19 +123,42 @@ trait Macros { self: Analyzer =>
if (mmeth == NoSymbol) None
else {
- val receiverClass: mirror.Symbol = mirror.symbolForName(mmeth.owner.fullName)
+ trace("loading implementation class: ")(mmeth.owner.fullName)
+ trace("classloader is: ")("%s of type %s".format(mirror.libraryClassLoader, mirror.libraryClassLoader.getClass))
+ def inferClasspath(cl: ClassLoader) = cl match {
+ case cl: java.net.URLClassLoader => "[" + (cl.getURLs mkString ",") + "]"
+ case _ => "<unknown>"
+ }
+ trace("classpath is: ")(inferClasspath(mirror.libraryClassLoader))
+
+ // @xeno.by: relies on the fact that macros can only be defined in static classes
+ def classfile(sym: Symbol): String = {
+ def recur(sym: Symbol): String = sym match {
+ case sym if sym.owner.isPackageClass =>
+ val suffix = if (sym.isModuleClass) "$" else ""
+ sym.fullName + suffix
+ case sym =>
+ val separator = if (sym.owner.isModuleClass) "" else "$"
+ recur(sym.owner) + separator + sym.javaSimpleName
+ }
+
+ if (sym.isClass || sym.isModule) recur(sym)
+ else recur(sym.enclClass)
+ }
+
+ // @xeno.by: this doesn't work for inner classes
+ // neither does mmeth.owner.javaClassName, so I had to roll my own implementation
+ //val receiverName = mmeth.owner.fullName
+ val receiverName = classfile(mmeth.owner)
+ val receiverClass: mirror.Symbol = mirror.symbolForName(receiverName)
+
if (debug) {
println("receiverClass is: " + receiverClass.fullNameString)
val jreceiverClass = mirror.classToJava(receiverClass)
val jreceiverSource = jreceiverClass.getProtectionDomain.getCodeSource
println("jreceiverClass is %s from %s".format(jreceiverClass, jreceiverSource))
-
- val jreceiverClasspath = jreceiverClass.getClassLoader match {
- case cl: java.net.URLClassLoader => "[" + (cl.getURLs mkString ",") + "]"
- case _ => "<unknown>"
- }
- println("jreceiverClassLoader is %s with classpath %s".format(jreceiverClass.getClassLoader, jreceiverClasspath))
+ println("jreceiverClassLoader is %s with classpath %s".format(jreceiverClass.getClassLoader, inferClasspath(jreceiverClass.getClassLoader)))
}
val receiverObj = receiverClass.companionModule
@@ -132,7 +166,11 @@ trait Macros { self: Analyzer =>
if (receiverObj == mirror.NoSymbol) None
else {
- val receiver = mirror.companionInstance(receiverClass)
+ // @xeno.by: yet another reflection method that doesn't work for inner classes
+ //val receiver = mirror.companionInstance(receiverClass)
+ val clazz = java.lang.Class.forName(receiverName, true, mirror.libraryClassLoader)
+ val receiver = clazz getField "MODULE$" get null
+
val rmeth = receiverObj.info.member(mirror.newTermName(mmeth.name.toString))
if (debug) {
println("rmeth is: " + rmeth.fullNameString)
@@ -147,6 +185,7 @@ trait Macros { self: Analyzer =>
}
} catch {
case ex: ClassNotFoundException =>
+ trace("implementation class failed to load: ")(ex.toString)
None
}
}
@@ -155,7 +194,7 @@ trait Macros { self: Analyzer =>
* Or, if that fails, and the macro overrides a method return
* tree that calls this method instead of the macro.
*/
- def macroExpand(tree: Tree, context: Context): Option[Any] = {
+ def macroExpand(tree: Tree, typer: Typer): Option[Any] = {
val trace = scala.tools.nsc.util.trace when settings.Ymacrodebug.value
trace("macroExpand: ")(tree)
@@ -180,7 +219,19 @@ trait Macros { self: Analyzer =>
// if one of those children involves macro expansion, things might get nasty
// that's why I'm temporarily turning this behavior off
nodePrinters.infolevel = nodePrinters.InfoLevel.Quiet
- Some(mirror.invoke(receiver, rmeth)(rawArgs: _*))
+ val expanded = mirror.invoke(receiver, rmeth)(rawArgs: _*)
+ expanded match {
+ case expanded: Tree =>
+ val expectedTpe = tree.tpe
+ val typed = typer.typed(expanded, EXPRmode, expectedTpe)
+ Some(typed)
+ case expanded if expanded.isInstanceOf[Tree] =>
+ typer.context.unit.error(tree.pos, "macro must return a compiler-specific tree; returned value is Tree, but it doesn't belong to this compiler's universe")
+ None
+ case expanded =>
+ typer.context.unit.error(tree.pos, "macro must return a compiler-specific tree; returned value is of class: " + expanded.getClass)
+ None
+ }
} catch {
case ex =>
val realex = ReflectionUtils.unwrapThrowable(ex)
@@ -191,14 +242,14 @@ trait Macros { self: Analyzer =>
} else {
realex.getMessage
}
- context.unit.error(tree.pos, "exception during macro expansion: " + msg)
+ typer.context.unit.error(tree.pos, "exception during macro expansion: " + msg)
None
} finally {
nodePrinters.infolevel = savedInfolevel
}
case None =>
def notFound() = {
- context.unit.error(tree.pos, "macro implementation not found: " + macroDef.name)
+ typer.context.unit.error(tree.pos, "macro implementation not found: " + macroDef.name)
None
}
def fallBackToOverridden(tree: Tree): Option[Tree] = {
diff --git a/src/compiler/scala/tools/nsc/typechecker/PatMatVirtualiser.scala b/src/compiler/scala/tools/nsc/typechecker/PatMatVirtualiser.scala
index 6d31243fd0..25dcc302ce 100644
--- a/src/compiler/scala/tools/nsc/typechecker/PatMatVirtualiser.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/PatMatVirtualiser.scala
@@ -43,7 +43,7 @@ trait PatMatVirtualiser extends ast.TreeDSL { self: Analyzer =>
val outer = newTermName("<outer>")
val runOrElse = newTermName("runOrElse")
val zero = newTermName("zero")
- val __match = newTermName("__match")
+ val _match = newTermName("__match") // don't call it __match, since that will trigger virtual pattern matching...
def counted(str: String, i: Int) = newTermName(str+i)
}
@@ -51,8 +51,8 @@ trait PatMatVirtualiser extends ast.TreeDSL { self: Analyzer =>
object MatchTranslator {
def apply(typer: Typer): MatchTranslation = {
import typer._
- // typing `__match` to decide which MatchTranslator to create adds 4% to quick.comp.timer
- newTyper(context.makeImplicit(reportAmbiguousErrors = false)).silent(_.typed(Ident(vpmName.__match), EXPRmode, WildcardType), reportAmbiguousErrors = false) match {
+ // typing `_match` to decide which MatchTranslator to create adds 4% to quick.comp.timer
+ newTyper(context.makeImplicit(reportAmbiguousErrors = false)).silent(_.typed(Ident(vpmName._match), EXPRmode, WildcardType), reportAmbiguousErrors = false) match {
case SilentResultValue(ms) => new PureMatchTranslator(typer, ms)
case _ => new OptimizingMatchTranslator(typer)
}
@@ -116,6 +116,10 @@ trait PatMatVirtualiser extends ast.TreeDSL { self: Analyzer =>
trait MatchTranslation extends MatchMonadInterface { self: TreeMakers with CodegenCore =>
import typer.{typed, context, silent, reallyExists}
+ private def repeatedToSeq(tp: Type): Type = (tp baseType RepeatedParamClass) match {
+ case TypeRef(_, RepeatedParamClass, args) => appliedType(SeqClass.typeConstructor, args)
+ case _ => tp
+ }
/** Implement a pattern match by turning its cases (including the implicit failure case)
* into the corresponding (monadic) extractors, and combining them with the `orElse` combinator.
@@ -133,11 +137,6 @@ trait PatMatVirtualiser extends ast.TreeDSL { self: Analyzer =>
// and the only place that emits Matches after typers is for exception handling anyway)
assert(phase.id <= currentRun.typerPhase.id, phase)
- def repeatedToSeq(tp: Type): Type = (tp baseType RepeatedParamClass) match {
- case TypeRef(_, RepeatedParamClass, args) => appliedType(SeqClass.typeConstructor, args)
- case _ => tp
- }
-
val scrutType = repeatedToSeq(elimAnonymousClass(scrut.tpe.widen))
val scrutSym = freshSym(scrut.pos, pureType(scrutType))
@@ -146,6 +145,47 @@ trait PatMatVirtualiser extends ast.TreeDSL { self: Analyzer =>
combineCases(scrut, scrutSym, cases map translateCase(scrutSym, okPt), okPt, matchOwner)
}
+ // return list of typed CaseDefs that are supported by the backend (typed/bind/wildcard)
+ // we don't have a global scrutinee -- the caught exception must be bound in each of the casedefs
+ // there's no need to check the scrutinee for null -- "throw null" becomes "throw new NullPointerException"
+ // try to simplify to a type-based switch, or fall back to a catch-all case that runs a normal pattern match
+ // unlike translateMatch, we type our result before returning it
+ def translateTry(caseDefs: List[CaseDef], pt: Type, pos: Position): List[CaseDef] =
+ // if they're already simple enough to be handled by the back-end, we're done
+ if (caseDefs forall treeInfo.isCatchCase) caseDefs
+ else {
+ val okPt = repeatedToSeq(pt)
+ val switch = {
+ val bindersAndCases = caseDefs map { caseDef =>
+ // generate a fresh symbol for each case, hoping we'll end up emitting a type-switch (we don't have a global scrut there)
+ // if we fail to emit a fine-grained switch, have to do translateCase again with a single scrutSym (TODO: uniformize substitution on treemakers so we can avoid this)
+ val caseScrutSym = freshSym(pos, pureType(ThrowableClass.tpe))
+ (caseScrutSym, propagateSubstitution(translateCase(caseScrutSym, okPt)(caseDef), EmptySubstitution))
+ }
+
+ (emitTypeSwitch(bindersAndCases, pt) map (_.map(fixerUpper(matchOwner, pos).apply(_).asInstanceOf[CaseDef])))
+ }
+
+ val catches = switch getOrElse {
+ val scrutSym = freshSym(pos, pureType(ThrowableClass.tpe))
+ val casesNoSubstOnly = caseDefs map { caseDef => (propagateSubstitution(translateCase(scrutSym, okPt)(caseDef), EmptySubstitution))}
+
+ val exSym = freshSym(pos, pureType(ThrowableClass.tpe), "ex")
+
+ List(
+ atPos(pos) {
+ CaseDef(
+ Bind(exSym, Ident(nme.WILDCARD)), // TODO: does this need fixing upping?
+ EmptyTree,
+ combineCasesNoSubstOnly(CODE.REF(exSym), scrutSym, casesNoSubstOnly, pt, matchOwner, scrut => Throw(CODE.REF(exSym)))
+ )
+ })
+ }
+
+ typer.typedCases(catches, ThrowableClass.tpe, WildcardType)
+ }
+
+
/** The translation of `pat if guard => body` has two aspects:
* 1) the substitution due to the variables bound by patterns
@@ -668,6 +708,10 @@ class Foo(x: Other) { x._1 } // no error in this order
def emitSwitch(scrut: Tree, scrutSym: Symbol, cases: List[List[TreeMaker]], pt: Type): Option[Tree] =
None
+ // for catch
+ def emitTypeSwitch(bindersAndCases: List[(Symbol, List[TreeMaker])], pt: Type): Option[List[CaseDef]] =
+ None
+
abstract class TreeMaker {
/** captures the scope and the value of the bindings in patterns
* important *when* the substitution happens (can't accumulate and do at once after the full matcher has been constructed)
@@ -788,6 +832,7 @@ class Foo(x: Other) { x._1 } // no error in this order
}
// implements the run-time aspects of (§8.2) (typedPattern has already done the necessary type transformations)
+ // TODO: normalize construction, which yields a combination of a EqualityTestTreeMaker (when necessary) and a TypeTestTreeMaker
case class TypeAndEqualityTestTreeMaker(prevBinder: Symbol, patBinder: Symbol, pt: Type, pos: Position) extends CondTreeMaker {
val nextBinderTp = glb(List(patBinder.info.widen, pt))
@@ -843,6 +888,10 @@ class Foo(x: Other) { x._1 } // no error in this order
val cond = typeAndEqualityTest(patBinder, pt)
val res = codegen._asInstanceOf(patBinder, nextBinderTp)
+
+ // TODO: remove this
+ def isStraightTypeTest = cond match { case TypeApply(_, _) => cond.symbol == Any_isInstanceOf case _ => false }
+
override def toString = "TET"+(patBinder, pt)
}
@@ -926,25 +975,30 @@ class Foo(x: Other) { x._1 } // no error in this order
}
// calls propagateSubstitution on the treemakers
- def combineCases(scrut: Tree, scrutSym: Symbol, casesRaw: List[List[TreeMaker]], pt: Type, owner: Symbol): Tree = fixerUpper(owner, scrut.pos){
- val casesUnOpt = casesRaw map (propagateSubstitution(_, EmptySubstitution)) // drops SubstOnlyTreeMakers, since their effect is now contained in the TreeMakers that follow them
+ def combineCases(scrut: Tree, scrutSym: Symbol, casesRaw: List[List[TreeMaker]], pt: Type, owner: Symbol): Tree = {
+ val casesNoSubstOnly = casesRaw map (propagateSubstitution(_, EmptySubstitution)) // drops SubstOnlyTreeMakers, since their effect is now contained in the TreeMakers that follow them
+ combineCasesNoSubstOnly(scrut, scrutSym, casesNoSubstOnly, pt, owner, CODE.MATCHERROR(_))
+ }
- emitSwitch(scrut, scrutSym, casesUnOpt, pt).getOrElse{
+ def combineCasesNoSubstOnly(scrut: Tree, scrutSym: Symbol, casesNoSubstOnly: List[List[TreeMaker]], pt: Type, owner: Symbol, matchFail: Tree => Tree): Tree = fixerUpper(owner, scrut.pos){
+ emitSwitch(scrut, scrutSym, casesNoSubstOnly, pt).getOrElse{
val (matcher, hasDefault, toHoist) =
- if (casesUnOpt nonEmpty) {
+ if (casesNoSubstOnly nonEmpty) {
// when specified, need to propagate pt explicitly (type inferencer can't handle it)
val optPt =
if (isFullyDefined(pt)) inMatchMonad(pt)
else NoType
- // do this check on casesUnOpt, since DCE will eliminate trivial cases like `case _ =>`, even if they're the last one
+ // do this check on casesNoSubstOnly, since DCE will eliminate trivial cases like `case _ =>`, even if they're the last one
// exhaustivity and reachability must be checked before optimization as well
- val hasDefault = casesUnOpt.nonEmpty && {
- val nonTrivLast = casesUnOpt.last
+ // TODO: improve, a trivial type test before the body still makes for a default case
+ // ("trivial" depends on whether we're emitting a straight match or an exception, or more generally, any supertype of scrutSym.tpe is a no-op)
+ val hasDefault = casesNoSubstOnly.nonEmpty && {
+ val nonTrivLast = casesNoSubstOnly.last
nonTrivLast.nonEmpty && nonTrivLast.head.isInstanceOf[BodyTreeMaker]
}
- val (cases, toHoist) = optimizeCases(scrutSym, casesUnOpt, pt)
+ val (cases, toHoist) = optimizeCases(scrutSym, casesNoSubstOnly, pt)
val combinedCases =
cases.map(combineExtractors(_, pt)).reduceLeft(codegen.typedOrElse(optPt))
@@ -952,7 +1006,11 @@ class Foo(x: Other) { x._1 } // no error in this order
(combinedCases, hasDefault, toHoist)
} else (codegen.zero, false, Nil)
- val expr = codegen.runOrElse(scrut, scrutSym, matcher, if (isFullyDefined(pt)) pt else NoType, hasDefault)
+ // catch-all
+ val catchAll =
+ if (hasDefault) None // no need for a catch-all when there's already a default
+ else Some(matchFail)
+ val expr = codegen.runOrElse(scrut, scrutSym, matcher, if (isFullyDefined(pt)) pt else NoType, catchAll)
if (toHoist isEmpty) expr
else Block(toHoist, expr)
}
@@ -966,7 +1024,7 @@ class Foo(x: Other) { x._1 } // no error in this order
// TODO: do this during tree construction, but that will require tracking the current owner in treemakers
// TODO: assign more fine-grained positions
// fixes symbol nesting, assigns positions
- private def fixerUpper(origOwner: Symbol, pos: Position) = new Traverser {
+ protected def fixerUpper(origOwner: Symbol, pos: Position) = new Traverser {
currentOwner = origOwner
override def traverse(t: Tree) {
@@ -1019,7 +1077,7 @@ class Foo(x: Other) { x._1 } // no error in this order
// codegen relevant to the structure of the translation (how extractors are combined)
trait AbsCodegen {
- def runOrElse(scrut: Tree, scrutSym: Symbol, matcher: Tree, resTp: Type, hasDefault: Boolean): Tree
+ def runOrElse(scrut: Tree, scrutSym: Symbol, matcher: Tree, resTp: Type, catchAll: Option[Tree => Tree]): Tree
def one(res: Tree, bodyPt: Type, matchPt: Type): Tree
def zero: Tree
def flatMap(prev: Tree, b: Symbol, next: Tree): Tree
@@ -1098,10 +1156,10 @@ class Foo(x: Other) { x._1 } // no error in this order
protected def matchMonadSym = oneSig.finalResultType.typeSymbol
import CODE._
- def __match(n: Name): SelectStart = matchStrategy DOT n
+ def _match(n: Name): SelectStart = matchStrategy DOT n
private lazy val oneSig: Type =
- typer.typed(__match(vpmName.one), EXPRmode | POLYmode | TAPPmode | FUNmode, WildcardType).tpe // TODO: error message
+ typer.typed(_match(vpmName.one), EXPRmode | POLYmode | TAPPmode | FUNmode, WildcardType).tpe // TODO: error message
}
trait PureCodegen extends CodegenCore with PureMatchMonadInterface {
@@ -1110,14 +1168,15 @@ class Foo(x: Other) { x._1 } // no error in this order
object pureCodegen extends CommonCodegen { import CODE._
//// methods in MatchingStrategy (the monad companion) -- used directly in translation
// __match.runOrElse(`scrut`)(`scrutSym` => `matcher`)
- def runOrElse(scrut: Tree, scrutSym: Symbol, matcher: Tree, resTp: Type, hasDefault: Boolean): Tree
- = __match(vpmName.runOrElse) APPLY (scrut) APPLY (fun(scrutSym, matcher))
+ // TODO: consider catchAll, or virtualized matching will break in exception handlers
+ def runOrElse(scrut: Tree, scrutSym: Symbol, matcher: Tree, resTp: Type, catchAll: Option[Tree => Tree]): Tree
+ = _match(vpmName.runOrElse) APPLY (scrut) APPLY (fun(scrutSym, matcher))
// __match.one(`res`)
- def one(res: Tree, bodyPt: Type, matchPt: Type): Tree = (__match(vpmName.one)) (res)
+ def one(res: Tree, bodyPt: Type, matchPt: Type): Tree = (_match(vpmName.one)) (res)
// __match.zero
- def zero: Tree = __match(vpmName.zero)
+ def zero: Tree = _match(vpmName.zero)
// __match.guard(`c`, `then`)
- def guard(c: Tree, then: Tree, tp: Type): Tree = __match(vpmName.guard) APPLY (c, then)
+ def guard(c: Tree, then: Tree, tp: Type): Tree = _match(vpmName.guard) APPLY (c, then)
//// methods in the monad instance -- used directly in translation
// `prev`.flatMap(`b` => `next`)
@@ -1437,94 +1496,145 @@ class Foo(x: Other) { x._1 } // no error in this order
}
}
- //// SWITCHES
+ //// SWITCHES -- TODO: operate on Tests rather than TreeMakers
trait SwitchEmission extends TreeMakers with OptimizedMatchMonadInterface { self: CodegenCore =>
- object SwitchablePattern { def unapply(pat: Tree) = pat match {
- case Literal(Constant((_: Byte ) | (_: Short) | (_: Int ) | (_: Char ))) => true // TODO: Java 7 allows strings in switches
- case _ => false
- }}
-
- // def isSwitchable(cases: List[(List[TreeMaker], Tree)]): Boolean = {
- // def isSwitchableTreeMaker(tm: TreeMaker) = tm match {
- // case tm@EqualityTestTreeMaker(_, SwitchablePattern(), _) => true
- // case SubstOnlyTreeMaker(_) => true
- // case AlternativesTreeMaker(_, altss, _) => altss forall (_.forall(isSwitchableTreeMaker))
- // case _ => false
- // }
- // }
+ abstract class SwitchMaker {
+ abstract class SwitchableTreeMakerExtractor { def unapply(x: TreeMaker): Option[Tree] }
+ val SwitchableTreeMaker: SwitchableTreeMakerExtractor
+
+ def alternativesSupported: Boolean
- private val switchableTpes = Set(ByteClass.tpe, ShortClass.tpe, IntClass.tpe, CharClass.tpe)
+ def isDefault(x: CaseDef): Boolean
+ def defaultSym: Symbol
+ def defaultBody: Tree
+ def defaultCase(scrutSym: Symbol = defaultSym, body: Tree = defaultBody): CaseDef
- override def emitSwitch(scrut: Tree, scrutSym: Symbol, cases: List[List[TreeMaker]], pt: Type): Option[Tree] = {
- def sequence[T](xs: List[Option[T]]): Option[List[T]] =
+ private def sequence[T](xs: List[Option[T]]): Option[List[T]] =
if (xs exists (_.isEmpty)) None else Some(xs.flatten)
- def isSwitchableTpe(tpe: Type): Boolean =
- switchableTpes contains tpe
- def switchableConstToInt(x: Tree): Tree = {
- val Literal(const) = x
- const.tag match {
- case IntTag => x
- case ByteTag | ShortTag | CharTag => Literal(Constant(const.intValue))
+ // empty list ==> failure
+ def apply(cases: List[(Symbol, List[TreeMaker])], pt: Type): List[CaseDef] = {
+ val caseDefs = cases map { case (scrutSym, makers) =>
+ makers match {
+ // default case
+ case (btm@BodyTreeMaker(body, _)) :: Nil =>
+ Some(defaultCase(scrutSym, btm.substitution(body)))
+ // constant (or typetest for typeSwitch)
+ case SwitchableTreeMaker(pattern) :: (btm@BodyTreeMaker(body, _)) :: Nil =>
+ Some(CaseDef(pattern, EmptyTree, btm.substitution(body)))
+ // alternatives
+ case AlternativesTreeMaker(_, altss, _) :: (btm@BodyTreeMaker(body, _)) :: Nil if alternativesSupported =>
+ val casePatterns = altss map {
+ case SwitchableTreeMaker(pattern) :: Nil =>
+ Some(pattern)
+ case _ =>
+ None
+ }
+
+ sequence(casePatterns) map { patterns =>
+ val substedBody = btm.substitution(body)
+ CaseDef(Alternative(patterns), EmptyTree, substedBody)
+ }
+ case _ => //println("can't emit switch for "+ makers)
+ None //failure (can't translate pattern to a switch)
+ }
}
- }
- val caseDefs = cases map { makers =>
- removeSubstOnly(makers) match {
- // default case (don't move this to unfold, as it may only occur on the top level, not as an alternative -- well, except in degenerate matches)
- case (btm@BodyTreeMaker(body, _)) :: Nil =>
- Some(CaseDef(Ident(nme.WILDCARD), EmptyTree, btm.substitution(body)))
- // constant
- case (EqualityTestTreeMaker(_, const@SwitchablePattern(), _)) :: (btm@BodyTreeMaker(body, _)) :: Nil =>
- Some(CaseDef(switchableConstToInt(const), EmptyTree, btm.substitution(body)))
- // alternatives
- case AlternativesTreeMaker(_, altss, _) :: (btm@BodyTreeMaker(body, _)) :: Nil => // assert(currLabel.isEmpty && nextLabel.isEmpty)
- val caseConstants = altss map {
- case EqualityTestTreeMaker(_, const@SwitchablePattern(), _) :: Nil =>
- Some(switchableConstToInt(const))
- case _ =>
- None
+ (for(
+ caseDefs <- sequence(caseDefs)) yield
+ if (caseDefs exists isDefault) caseDefs
+ else {
+ caseDefs :+ defaultCase()
}
+ ) getOrElse Nil
+ }
+ }
- sequence(caseConstants) map { contants =>
- val substedBody = btm.substitution(body)
- CaseDef(Alternative(contants), EmptyTree, substedBody)
- }
- case _ =>
- None //failure (can't translate pattern to a switch)
+ class RegularSwitchMaker(scrutSym: Symbol) extends SwitchMaker {
+ val switchableTpe = Set(ByteClass.tpe, ShortClass.tpe, IntClass.tpe, CharClass.tpe)
+ val alternativesSupported = true
+
+ object SwitchablePattern { def unapply(pat: Tree): Option[Tree] = pat match {
+ case Literal(const@Constant((_: Byte ) | (_: Short) | (_: Int ) | (_: Char ))) =>
+ Some(Literal(Constant(const.intValue))) // TODO: Java 7 allows strings in switches
+ case _ => None
+ }}
+
+ object SwitchableTreeMaker extends SwitchableTreeMakerExtractor {
+ def unapply(x: TreeMaker): Option[Tree] = x match {
+ case EqualityTestTreeMaker(_, SwitchablePattern(const), _) => Some(const)
+ case _ => None
}
}
- if (!isSwitchableTpe(scrut.tpe))
- None // TODO: emit a cast of the scrutinee and a switch on the cast scrutinee if patterns allow switch but the type of the scrutinee doesn't
- else {
- sequence(caseDefs) map { caseDefs =>
- import CODE._
- val caseDefsWithDefault = {
- def isDefault(x: CaseDef): Boolean = x match {
- case CaseDef(Ident(nme.WILDCARD), EmptyTree, _) => true
- case _ => false
- }
- val hasDefault = caseDefs exists isDefault
- if (hasDefault) caseDefs else {
- val default = atPos(scrut.pos) { DEFAULT ==> MATCHERROR(REF(scrutSym)) }
- caseDefs :+ default
- }
- }
- val matcher = BLOCK(
- if (scrut.tpe != IntClass.tpe) {
- scrutSym setInfo IntClass.tpe
- VAL(scrutSym) === (scrut DOT nme.toInt)
- } else {
- VAL(scrutSym) === scrut
- },
- Match(REF(scrutSym), caseDefsWithDefault) // match on scrutSym, not scrut to avoid duplicating scrut
- )
- // matcher filter (tree => tree.tpe == null) foreach println
- // treeBrowser browse matcher
- matcher // set type to avoid recursion in typedMatch
+ def isDefault(x: CaseDef): Boolean = x match {
+ case CaseDef(Ident(nme.WILDCARD), EmptyTree, _) => true
+ case _ => false
+ }
+
+ def defaultSym: Symbol = scrutSym
+ def defaultBody: Tree = { import CODE._; MATCHERROR(REF(scrutSym)) }
+ def defaultCase(scrutSym: Symbol = defaultSym, body: Tree = defaultBody): CaseDef = { import CODE._; atPos(body.pos) {
+ DEFAULT ==> body
+ }}
+ }
+
+ override def emitSwitch(scrut: Tree, scrutSym: Symbol, cases: List[List[TreeMaker]], pt: Type): Option[Tree] = { import CODE._
+ val regularSwitchMaker = new RegularSwitchMaker(scrutSym)
+ // TODO: if patterns allow switch but the type of the scrutinee doesn't, cast (type-test) the scrutinee to the corresponding switchable type and switch on the result
+ if (regularSwitchMaker.switchableTpe(scrutSym.tpe)) {
+ val caseDefsWithDefault = regularSwitchMaker(cases map {c => (scrutSym, c)}, pt)
+ if (caseDefsWithDefault isEmpty) None
+ else {
+ // match on scrutSym -- converted to an int if necessary -- not on scrut directly (to avoid duplicating scrut)
+ val scrutToInt: Tree =
+ if(scrutSym.tpe =:= IntClass.tpe) REF(scrutSym)
+ else (REF(scrutSym) DOT (nme.toInt))
+ Some(BLOCK(
+ VAL(scrutSym) === scrut,
+ Match(scrutToInt, caseDefsWithDefault)
+ ))
+ }
+ } else None
+ }
+
+ // for the catch-cases in a try/catch
+ private object typeSwitchMaker extends SwitchMaker {
+ def switchableTpe(tp: Type) = true
+ val alternativesSupported = false // TODO: needs either back-end support of flattening of alternatives during typers
+
+ // TODO: there are more treemaker-sequences that can be handled by type tests
+ // analyze the result of approximateTreeMaker rather than the TreeMaker itself
+ object SwitchableTreeMaker extends SwitchableTreeMakerExtractor {
+ def unapply(x: TreeMaker): Option[Tree] = x match {
+ case tm@TypeTestTreeMaker(_, _, _) =>
+ Some(Bind(tm.nextBinder, Typed(Ident(nme.WILDCARD), TypeTree(tm.nextBinderTp)) /* not used by back-end */)) // -- TODO: use this if binder does not occur in the body
+ case tm@TypeAndEqualityTestTreeMaker(_, patBinder, pt, _) if tm.isStraightTypeTest =>
+ Some(Bind(tm.nextBinder, Typed(Ident(nme.WILDCARD), TypeTree(tm.nextBinderTp)) /* not used by back-end */))
+ case _ =>
+ None
}
}
+
+ def isDefault(x: CaseDef): Boolean = x match {
+ case CaseDef(Typed(Ident(nme.WILDCARD), tpt), EmptyTree, _) if (tpt.tpe =:= ThrowableClass.tpe) => true
+ case CaseDef(Bind(_, Typed(Ident(nme.WILDCARD), tpt)), EmptyTree, _) if (tpt.tpe =:= ThrowableClass.tpe) => true
+ case CaseDef(Ident(nme.WILDCARD), EmptyTree, _) => true
+ case _ => false
+ }
+
+ lazy val defaultSym: Symbol = freshSym(NoPosition, ThrowableClass.tpe)
+ def defaultBody: Tree = Throw(CODE.REF(defaultSym))
+ def defaultCase(scrutSym: Symbol = defaultSym, body: Tree = defaultBody): CaseDef = { import CODE._; atPos(body.pos) {
+ CASE (Bind(scrutSym, Typed(Ident(nme.WILDCARD), TypeTree(ThrowableClass.tpe)))) ==> body
+ }}
+ }
+
+ // TODO: drop null checks
+ override def emitTypeSwitch(bindersAndCases: List[(Symbol, List[TreeMaker])], pt: Type): Option[List[CaseDef]] = {
+ val caseDefsWithDefault = typeSwitchMaker(bindersAndCases, pt)
+ if (caseDefsWithDefault isEmpty) None
+ else Some(caseDefsWithDefault)
}
}
@@ -1551,33 +1661,31 @@ class Foo(x: Other) { x._1 } // no error in this order
/** Inline runOrElse and get rid of Option allocations
*
- * runOrElse(scrut: scrutTp)(matcher): resTp = matcher(scrut) getOrElse (throw new MatchError(x))
+ * runOrElse(scrut: scrutTp)(matcher): resTp = matcher(scrut) getOrElse ${catchAll(`scrut`)}
* the matcher's optional result is encoded as a flag, keepGoing, where keepGoing == true encodes result.isEmpty,
* if keepGoing is false, the result Some(x) of the naive translation is encoded as matchRes == x
*/
@inline private def dontStore(tp: Type) = (tp.typeSymbol eq UnitClass) || (tp.typeSymbol eq NothingClass)
lazy val keepGoing = freshSym(NoPosition, BooleanClass.tpe, "keepGoing") setFlag MUTABLE
lazy val matchRes = freshSym(NoPosition, AnyClass.tpe, "matchRes") setFlag MUTABLE
- def runOrElse(scrut: Tree, scrutSym: Symbol, matcher: Tree, resTp: Type, hasDefault: Boolean) = {
+ def runOrElse(scrut: Tree, scrutSym: Symbol, matcher: Tree, resTp: Type, catchAll: Option[Tree => Tree]) = {
matchRes.info = if (resTp ne NoType) resTp.widen else AnyClass.tpe // we don't always know resTp, and it might be AnyVal, in which case we can't assign NULL
if (dontStore(resTp)) matchRes resetFlag MUTABLE // don't assign to Unit-typed var's, in fact, make it a val -- conveniently also works around SI-5245
BLOCK(
VAL(zeroSym) === REF(NoneModule), // TODO: can we just get rid of explicitly emitted zero? don't know how to do that as a local rewrite...
- VAL(scrutSym) === scrut, // reuse the symbol of the function's argument to avoid creating a fresh one and substituting it for scrutSym in `matcher` -- the owner structure is repaired by fixerUpper
+ VAL(scrutSym) === scrut,
VAL(matchRes) === mkZero(matchRes.info), // must cast to deal with GADT typing, hence the private mkZero above
VAL(keepGoing) === TRUE,
matcher,
- if(hasDefault) REF(matchRes)
- else (IF (REF(keepGoing)) THEN MATCHERROR(REF(scrutSym)) ELSE REF(matchRes))
+ catchAll map { catchAllGen => (IF (REF(keepGoing)) THEN catchAllGen(REF(scrutSym)) ELSE REF(matchRes)) } getOrElse REF(matchRes)
)
}
// only used to wrap the RHS of a body
def one(res: Tree, bodyPt: Type, matchPt: Type): Tree = {
BLOCK(
- if (dontStore(matchPt)) res // runOrElse hasn't been called yet, so matchRes.isMutable is irrelevant, also, tp may be a subtype of resTp used in runOrElse...
- else (REF(matchRes) === res), // _asInstanceOf(res, tp.widen, force = true)
- REF(keepGoing) === FALSE,
+ REF(keepGoing) === FALSE, // comes before assignment to matchRes, so the latter is in tail positions (can ignore the trailing zero -- will disappear when we flatten blocks, which is TODO)
+ if (dontStore(matchPt)) res else (REF(matchRes) === res), // runOrElse hasn't been called yet, so matchRes.isMutable is irrelevant, also, tp may be a subtype of resTp used in runOrElse...
zero // to have a nice lub for lubs -- otherwise we'll get a boxed unit here -- TODO: get rid of all those dangling else zero's
)
}
diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
index ef69e1525e..17c0bdf304 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
@@ -941,10 +941,14 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser {
}
if (tree.isType)
adaptType()
- else if (inExprModeButNot(mode, FUNmode) && tree.symbol != null && tree.symbol.isMacro && !tree.isDef) {
- val tree1 = expandMacro(tree)
- if (tree1.isErroneous) tree1 else typed(tree1, mode, pt)
- } else if ((mode & (PATTERNmode | FUNmode)) == (PATTERNmode | FUNmode))
+ else if (inExprModeButNot(mode, FUNmode) && tree.symbol != null && tree.symbol.isMacro && !tree.isDef && !(tree exists (_.isErroneous)))
+ macroExpand(tree, this) match {
+ case Some(expanded: Tree) =>
+ typed(expanded, mode, pt)
+ case None =>
+ setError(tree) // error already reported
+ }
+ else if ((mode & (PATTERNmode | FUNmode)) == (PATTERNmode | FUNmode))
adaptConstrPattern()
else if (inAllModes(mode, EXPRmode | FUNmode) &&
!tree.tpe.isInstanceOf[MethodType] &&
@@ -1987,7 +1991,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser {
treeCopy.CaseDef(cdef, pat1, guard1, body1) setType body1.tpe
}
- def typedCases(tree: Tree, cases: List[CaseDef], pattp: Type, pt: Type): List[CaseDef] =
+ def typedCases(cases: List[CaseDef], pattp: Type, pt: Type): List[CaseDef] =
cases mapConserve { cdef =>
newTyper(context.makeNewScope(cdef, context.owner)).typedCase(cdef, pattp, pt)
}
@@ -2169,6 +2173,10 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser {
// error for this is issued in RefChecks.checkDefaultsInOverloaded
if (!e.sym.isErroneous && !e1.sym.isErroneous && !e.sym.hasDefaultFlag &&
!e.sym.hasAnnotation(BridgeClass) && !e1.sym.hasAnnotation(BridgeClass)) {
+ log("Double definition detected:\n " +
+ ((e.sym.getClass, e.sym.info, e.sym.ownerChain)) + "\n " +
+ ((e1.sym.getClass, e1.sym.info, e1.sym.ownerChain)))
+
DefDefinedTwiceError(e.sym, e1.sym)
scope.unlink(e1) // need to unlink to avoid later problems with lub; see #2779
}
@@ -3318,7 +3326,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser {
typed1(atPos(tree.pos) { Function(params, body) }, mode, pt)
} else {
val selector1 = checkDead(typed(selector, EXPRmode | BYVALmode, WildcardType))
- var cases1 = typedCases(tree, cases, packCaptured(selector1.tpe.widen), pt)
+ var cases1 = typedCases(cases, packCaptured(selector1.tpe.widen), pt)
if (isPastTyper || !opt.virtPatmat) {
val (owntype, needAdapt) = ptOrLub(cases1 map (_.tpe))
@@ -3334,7 +3342,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser {
(MatchTranslator(this)).translateMatch(selector1, cases1, owntype) match {
case Block(vd :: Nil, tree@Match(selector, cases)) =>
val selector1 = checkDead(typed(selector, EXPRmode | BYVALmode, WildcardType))
- var cases1 = typedCases(tree, cases, packCaptured(selector1.tpe.widen), pt)
+ var cases1 = typedCases(cases, packCaptured(selector1.tpe.widen), pt)
val (owntype, needAdapt) = ptOrLub(cases1 map (_.tpe))
if (needAdapt)
cases1 = cases1 map (adaptCase(_, owntype))
@@ -4242,7 +4250,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser {
case Try(block, catches, finalizer) =>
var block1 = typed(block, pt)
- var catches1 = typedCases(tree, catches, ThrowableClass.tpe, pt)
+ var catches1 = typedCases(catches, ThrowableClass.tpe, pt)
val finalizer1 = if (finalizer.isEmpty) finalizer
else typed(finalizer, UnitClass.tpe)
val (owntype, needAdapt) = ptOrLub(block1.tpe :: (catches1 map (_.tpe)))
@@ -4250,6 +4258,11 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser {
block1 = adapt(block1, mode, owntype)
catches1 = catches1 map (adaptCase(_, owntype))
}
+
+ if(!isPastTyper && opt.virtPatmat) {
+ catches1 = (MatchTranslator(this)).translateTry(catches1, owntype, tree.pos)
+ }
+
treeCopy.Try(tree, block1, catches1, finalizer1) setType owntype
case Throw(expr) =>
@@ -4536,13 +4549,6 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser {
}
}
- def expandMacro(tree: Tree): Tree =
- macroExpand(tree, context) match {
- case Some(t: Tree) => t
- case Some(t) => MacroExpandError(tree, t)
- case None => setError(tree) // error already reported
- }
-
def atOwner(owner: Symbol): Typer =
newTyper(context.make(context.tree, owner))
diff --git a/src/continuations/plugin/scala/tools/selectivecps/SelectiveCPSTransform.scala b/src/continuations/plugin/scala/tools/selectivecps/SelectiveCPSTransform.scala
index b2a1546b4e..a90dc36639 100644
--- a/src/continuations/plugin/scala/tools/selectivecps/SelectiveCPSTransform.scala
+++ b/src/continuations/plugin/scala/tools/selectivecps/SelectiveCPSTransform.scala
@@ -203,7 +203,7 @@ abstract class SelectiveCPSTransform extends PluginComponent with
rhs.changeOwner(currentOwner -> fun.symbol)
val exSym = currentOwner.newValueParameter(cpsNames.ex, pos).setInfo(ThrowableClass.tpe)
- val catch2 = { localTyper.typedCases(tree, List(
+ val catch2 = { localTyper.typedCases(List(
CaseDef(Bind(exSym, Typed(Ident("_"), TypeTree(ThrowableClass.tpe))),
Apply(Select(Ident(funSym), nme.isDefinedAt), List(Ident(exSym))),
Apply(Ident(funSym), List(Ident(exSym))))
diff --git a/src/library/scala/concurrent/Awaitable.scala b/src/library/scala/concurrent/Awaitable.scala
new file mode 100644
index 0000000000..c38e668f30
--- /dev/null
+++ b/src/library/scala/concurrent/Awaitable.scala
@@ -0,0 +1,24 @@
+/* __ *\
+** ________ ___ / / ___ Scala API **
+** / __/ __// _ | / / / _ | (c) 2003-2011, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ **
+** /____/\___/_/ |_/____/_/ | | **
+** |/ **
+\* */
+
+package scala.concurrent
+
+
+
+import scala.annotation.implicitNotFound
+import scala.util.Duration
+
+
+
+trait Awaitable[+T] {
+ @implicitNotFound(msg = "Waiting must be done by calling `blocking(timeout) b`, where `b` is the `Awaitable` object or a potentially blocking piece of code.")
+ def await(atMost: Duration)(implicit canawait: CanAwait): T
+}
+
+
+
diff --git a/src/library/scala/concurrent/Channel.scala b/src/library/scala/concurrent/Channel.scala
index 43d684641e..e79f76430f 100644
--- a/src/library/scala/concurrent/Channel.scala
+++ b/src/library/scala/concurrent/Channel.scala
@@ -23,7 +23,7 @@ class Channel[A] {
private var written = new LinkedList[A] // FIFO buffer, realized through
private var lastWritten = written // aliasing of a linked list
private var nreaders = 0
-
+
/**
* @param x ...
*/
@@ -33,7 +33,7 @@ class Channel[A] {
lastWritten = lastWritten.next
if (nreaders > 0) notify()
}
-
+
def read: A = synchronized {
while (written.next == null) {
try {
@@ -46,4 +46,5 @@ class Channel[A] {
written = written.next
x
}
+
}
diff --git a/src/library/scala/concurrent/ConcurrentPackageObject.scala b/src/library/scala/concurrent/ConcurrentPackageObject.scala
new file mode 100644
index 0000000000..6aacd53de2
--- /dev/null
+++ b/src/library/scala/concurrent/ConcurrentPackageObject.scala
@@ -0,0 +1,103 @@
+/* __ *\
+** ________ ___ / / ___ Scala API **
+** / __/ __// _ | / / / _ | (c) 2003-2011, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ **
+** /____/\___/_/ |_/____/_/ | | **
+** |/ **
+\* */
+
+package scala.concurrent
+
+import scala.util.{ Duration, Try, Success, Failure }
+import ConcurrentPackageObject._
+
+/** This package object contains primitives for concurrent and parallel programming.
+ */
+abstract class ConcurrentPackageObject {
+ /** A global execution environment for executing lightweight tasks.
+ */
+ lazy val executionContext =
+ new impl.ExecutionContextImpl(java.util.concurrent.Executors.newCachedThreadPool())
+
+ /** A global service for scheduling tasks for execution.
+ */
+ // lazy val scheduler =
+ // new default.SchedulerImpl
+
+ val handledFutureException: PartialFunction[Throwable, Throwable] = {
+ case t: Throwable if isFutureThrowable(t) => t
+ }
+
+ // TODO rename appropriately and make public
+ private[concurrent] def isFutureThrowable(t: Throwable) = t match {
+ case e: Error => false
+ case t: scala.util.control.ControlThrowable => false
+ case i: InterruptedException => false
+ case _ => true
+ }
+
+ private[concurrent] def resolve[T](source: Try[T]): Try[T] = source match {
+ case Failure(t: scala.runtime.NonLocalReturnControl[_]) => Success(t.value.asInstanceOf[T])
+ case Failure(t: scala.util.control.ControlThrowable) => Failure(new ExecutionException("Boxed ControlThrowable", t))
+ case Failure(t: InterruptedException) => Failure(new ExecutionException("Boxed InterruptedException", t))
+ case Failure(e: Error) => Failure(new ExecutionException("Boxed Error", e))
+ case _ => source
+ }
+
+ private[concurrent] def resolver[T] =
+ resolverFunction.asInstanceOf[PartialFunction[Throwable, Try[T]]]
+
+ /* concurrency constructs */
+
+ def future[T](body: =>T)(implicit execCtx: ExecutionContext = executionContext): Future[T] =
+ execCtx future body
+
+ def promise[T]()(implicit execCtx: ExecutionContext = executionContext): Promise[T] =
+ execCtx promise
+
+ /** Wraps a block of code into an awaitable object. */
+ def body2awaitable[T](body: =>T) = new Awaitable[T] {
+ def await(atMost: Duration)(implicit cb: CanAwait) = body
+ }
+
+ /** Used to block on a piece of code which potentially blocks.
+ *
+ * @param body A piece of code which contains potentially blocking or long running calls.
+ *
+ * Calling this method may throw the following exceptions:
+ * - CancellationException - if the computation was cancelled
+ * - InterruptedException - in the case that a wait within the blockable object was interrupted
+ * - TimeoutException - in the case that the blockable object timed out
+ */
+ def blocking[T](atMost: Duration)(body: =>T)(implicit execCtx: ExecutionContext): T =
+ executionContext.blocking(atMost)(body)
+
+ /** Blocks on an awaitable object.
+ *
+ * @param awaitable An object with a `block` method which runs potentially blocking or long running calls.
+ *
+ * Calling this method may throw the following exceptions:
+ * - CancellationException - if the computation was cancelled
+ * - InterruptedException - in the case that a wait within the blockable object was interrupted
+ * - TimeoutException - in the case that the blockable object timed out
+ */
+ def blocking[T](awaitable: Awaitable[T], atMost: Duration)(implicit execCtx: ExecutionContext = executionContext): T =
+ executionContext.blocking(awaitable, atMost)
+
+ @inline implicit final def int2durationops(x: Int): DurationOps = new DurationOps(x)
+}
+
+private[concurrent] object ConcurrentPackageObject {
+ // TODO, docs, return type
+ // Note that having this in the package object led to failures when
+ // compiling a subset of sources; it seems that the wildcard is not
+ // properly handled, and you get messages like "type _$1 defined twice".
+ // This is consistent with other package object breakdowns.
+ private val resolverFunction: PartialFunction[Throwable, Try[_]] = {
+ case t: scala.runtime.NonLocalReturnControl[_] => Success(t.value)
+ case t: scala.util.control.ControlThrowable => Failure(new ExecutionException("Boxed ControlThrowable", t))
+ case t: InterruptedException => Failure(new ExecutionException("Boxed InterruptedException", t))
+ case e: Error => Failure(new ExecutionException("Boxed Error", e))
+ case t => Failure(t)
+ }
+}
diff --git a/src/library/scala/concurrent/DelayedLazyVal.scala b/src/library/scala/concurrent/DelayedLazyVal.scala
index e308c3b5a6..0b7f54a27a 100644
--- a/src/library/scala/concurrent/DelayedLazyVal.scala
+++ b/src/library/scala/concurrent/DelayedLazyVal.scala
@@ -8,7 +8,6 @@
package scala.concurrent
-import ops.future
/** A `DelayedLazyVal` is a wrapper for lengthy computations which have a
* valid partially computed result.
@@ -27,21 +26,23 @@ import ops.future
class DelayedLazyVal[T](f: () => T, body: => Unit) {
@volatile private[this] var _isDone = false
private[this] lazy val complete = f()
-
+
/** Whether the computation is complete.
*
* @return true if the computation is complete.
*/
def isDone = _isDone
-
+
/** The current result of f(), or the final result if complete.
*
* @return the current value
*/
def apply(): T = if (isDone) complete else f()
-
- future {
+
+ // TODO replace with scala.concurrent.future { ... }
+ ops.future {
body
_isDone = true
}
+
}
diff --git a/src/library/scala/concurrent/ExecutionContext.scala b/src/library/scala/concurrent/ExecutionContext.scala
new file mode 100644
index 0000000000..99cd264ac5
--- /dev/null
+++ b/src/library/scala/concurrent/ExecutionContext.scala
@@ -0,0 +1,132 @@
+/* __ *\
+** ________ ___ / / ___ Scala API **
+** / __/ __// _ | / / / _ | (c) 2003-2011, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ **
+** /____/\___/_/ |_/____/_/ | | **
+** |/ **
+\* */
+
+package scala.concurrent
+
+
+
+import java.util.concurrent.atomic.{ AtomicInteger }
+import java.util.concurrent.{ Executors, Future => JFuture, Callable }
+import scala.util.Duration
+import scala.util.{ Try, Success, Failure }
+import scala.concurrent.forkjoin.{ ForkJoinPool, RecursiveTask => FJTask, RecursiveAction, ForkJoinWorkerThread }
+import scala.collection.generic.CanBuildFrom
+import collection._
+
+
+
+trait ExecutionContext {
+
+ protected implicit object CanAwaitEvidence extends CanAwait
+
+ def execute(runnable: Runnable): Unit
+
+ def execute[U](body: () => U): Unit
+
+ def promise[T]: Promise[T]
+
+ def future[T](body: Callable[T]): Future[T] = future(body.call())
+
+ def future[T](body: => T): Future[T]
+
+ def blocking[T](atMost: Duration)(body: =>T): T
+
+ def blocking[T](awaitable: Awaitable[T], atMost: Duration): T
+
+ def reportFailure(t: Throwable): Unit
+
+ /* implementations follow */
+
+ private implicit val executionContext = this
+
+ def keptPromise[T](result: T): Promise[T] = {
+ val p = promise[T]
+ p success result
+ }
+
+ def brokenPromise[T](t: Throwable): Promise[T] = {
+ val p = promise[T]
+ p failure t
+ }
+
+ /** TODO some docs
+ *
+ */
+ def all[T, Coll[X] <: Traversable[X]](futures: Coll[Future[T]])(implicit cbf: CanBuildFrom[Coll[_], T, Coll[T]]): Future[Coll[T]] = {
+ import nondeterministic._
+ val buffer = new mutable.ArrayBuffer[T]
+ val counter = new AtomicInteger(1) // how else could we do this?
+ val p: Promise[Coll[T]] = promise[Coll[T]] // we need an implicit execctx in the signature
+ var idx = 0
+
+ def tryFinish() = if (counter.decrementAndGet() == 0) {
+ val builder = cbf(futures)
+ builder ++= buffer
+ p success builder.result
+ }
+
+ for (f <- futures) {
+ val currentIndex = idx
+ buffer += null.asInstanceOf[T]
+ counter.incrementAndGet()
+ f onComplete {
+ case Failure(t) =>
+ p tryFailure t
+ case Success(v) =>
+ buffer(currentIndex) = v
+ tryFinish()
+ }
+ idx += 1
+ }
+
+ tryFinish()
+
+ p.future
+ }
+
+ /** TODO some docs
+ *
+ */
+ def any[T](futures: Traversable[Future[T]]): Future[T] = {
+ val p = promise[T]
+ val completeFirst: Try[T] => Unit = elem => p tryComplete elem
+
+ futures foreach (_ onComplete completeFirst)
+
+ p.future
+ }
+
+ /** TODO some docs
+ *
+ */
+ def find[T](futures: Traversable[Future[T]])(predicate: T => Boolean): Future[Option[T]] = {
+ if (futures.isEmpty) Promise.kept[Option[T]](None).future
+ else {
+ val result = promise[Option[T]]
+ val count = new AtomicInteger(futures.size)
+ val search: Try[T] => Unit = {
+ v => v match {
+ case Success(r) => if (predicate(r)) result trySuccess Some(r)
+ case _ =>
+ }
+ if (count.decrementAndGet() == 0) result trySuccess None
+ }
+
+ futures.foreach(_ onComplete search)
+
+ result.future
+ }
+ }
+
+}
+
+
+sealed trait CanAwait
+
+
+
diff --git a/src/library/scala/concurrent/Future.scala b/src/library/scala/concurrent/Future.scala
new file mode 100644
index 0000000000..73f76bbbfb
--- /dev/null
+++ b/src/library/scala/concurrent/Future.scala
@@ -0,0 +1,492 @@
+/* __ *\
+** ________ ___ / / ___ Scala API **
+** / __/ __// _ | / / / _ | (c) 2003-2011, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ **
+** /____/\___/_/ |_/____/_/ | | **
+** |/ **
+\* */
+
+package scala.concurrent
+
+
+
+import java.util.concurrent.{ ConcurrentLinkedQueue, TimeUnit, Callable }
+import java.util.concurrent.TimeUnit.{ NANOSECONDS => NANOS, MILLISECONDS ⇒ MILLIS }
+import java.lang.{ Iterable => JIterable }
+import java.util.{ LinkedList => JLinkedList }
+import java.{ lang => jl }
+import java.util.concurrent.atomic.{ AtomicReferenceFieldUpdater, AtomicInteger, AtomicBoolean }
+
+import scala.util.{ Timeout, Duration, Try, Success, Failure }
+import scala.Option
+
+import scala.annotation.tailrec
+import scala.collection.mutable.Stack
+import scala.collection.mutable.Builder
+import scala.collection.generic.CanBuildFrom
+
+
+
+/** The trait that represents futures.
+ *
+ * Asynchronous computations that yield futures are created with the `future` call:
+ *
+ * {{{
+ * val s = "Hello"
+ * val f: Future[String] = future {
+ * s + " future!"
+ * }
+ * f onSuccess {
+ * case msg => println(msg)
+ * }
+ * }}}
+ *
+ * @author Philipp Haller, Heather Miller, Aleksandar Prokopec, Viktor Klang
+ *
+ * @define multipleCallbacks
+ * Multiple callbacks may be registered; there is no guarantee that they will be
+ * executed in a particular order.
+ *
+ * @define caughtThrowables
+ * The future may contain a throwable object and this means that the future failed.
+ * Futures obtained through combinators have the same exception as the future they were obtained from.
+ * The following throwable objects are not contained in the future:
+ * - `Error` - errors are not contained within futures
+ * - `InterruptedException` - not contained within futures
+ * - all `scala.util.control.ControlThrowable` except `NonLocalReturnControl` - not contained within futures
+ *
+ * Instead, the future is completed with a ExecutionException with one of the exceptions above
+ * as the cause.
+ * If a future is failed with a `scala.runtime.NonLocalReturnControl`,
+ * it is completed with a value instead from that throwable instead instead.
+ *
+ * @define nonDeterministic
+ * Note: using this method yields nondeterministic dataflow programs.
+ *
+ * @define forComprehensionExamples
+ * Example:
+ *
+ * {{{
+ * val f = future { 5 }
+ * val g = future { 3 }
+ * val h = for {
+ * x: Int <- f // returns Future(5)
+ * y: Int <- g // returns Future(5)
+ * } yield x + y
+ * }}}
+ *
+ * is translated to:
+ *
+ * {{{
+ * f flatMap { (x: Int) => g map { (y: Int) => x + y } }
+ * }}}
+ */
+trait Future[+T] extends Awaitable[T] {
+self =>
+
+ /* Callbacks */
+
+ /** When this future is completed successfully (i.e. with a value),
+ * apply the provided partial function to the value if the partial function
+ * is defined at that value.
+ *
+ * If the future has already been completed with a value,
+ * this will either be applied immediately or be scheduled asynchronously.
+ *
+ * $multipleCallbacks
+ */
+ def onSuccess[U](pf: PartialFunction[T, U]): this.type = onComplete {
+ case Failure(t) => // do nothing
+ case Success(v) => if (pf isDefinedAt v) pf(v) else { /*do nothing*/ }
+ }
+
+ /** When this future is completed with a failure (i.e. with a throwable),
+ * apply the provided callback to the throwable.
+ *
+ * $caughtThrowables
+ *
+ * If the future has already been completed with a failure,
+ * this will either be applied immediately or be scheduled asynchronously.
+ *
+ * Will not be called in case that the future is completed with a value.
+ *
+ * $multipleCallbacks
+ */
+ def onFailure[U](callback: PartialFunction[Throwable, U]): this.type = onComplete {
+ case Failure(t) => if (isFutureThrowable(t) && callback.isDefinedAt(t)) callback(t) else { /*do nothing*/ }
+ case Success(v) => // do nothing
+ }
+
+ /** When this future is completed, either through an exception, a timeout, or a value,
+ * apply the provided function.
+ *
+ * If the future has already been completed,
+ * this will either be applied immediately or be scheduled asynchronously.
+ *
+ * $multipleCallbacks
+ */
+ def onComplete[U](func: Try[T] => U): this.type
+
+
+ /* Miscellaneous */
+
+ /** Creates a new promise.
+ */
+ def newPromise[S]: Promise[S]
+
+
+ /* Projections */
+
+ /** Returns a failed projection of this future.
+ *
+ * The failed projection is a future holding a value of type `Throwable`.
+ *
+ * It is completed with a value which is the throwable of the original future
+ * in case the original future is failed.
+ *
+ * It is failed with a `NoSuchElementException` if the original future is completed successfully.
+ *
+ * Blocking on this future returns a value if the original future is completed with an exception
+ * and throws a corresponding exception if the original future fails.
+ */
+ def failed: Future[Throwable] = {
+ def noSuchElem(v: T) =
+ new NoSuchElementException("Future.failed not completed with a throwable. Instead completed with: " + v)
+
+ val p = newPromise[Throwable]
+
+ onComplete {
+ case Failure(t) => p success t
+ case Success(v) => p failure noSuchElem(v)
+ }
+
+ p.future
+ }
+
+
+ /* Monadic operations */
+
+ /** Asynchronously processes the value in the future once the value becomes available.
+ *
+ * Will not be called if the future fails.
+ */
+ def foreach[U](f: T => U): Unit = onComplete {
+ case Success(r) => f(r)
+ case Failure(_) => // do nothing
+ }
+
+ /** Creates a new future by applying a function to the successful result of
+ * this future. If this future is completed with an exception then the new
+ * future will also contain this exception.
+ *
+ * $forComprehensionExample
+ */
+ def map[S](f: T => S): Future[S] = {
+ val p = newPromise[S]
+
+ onComplete {
+ case Failure(t) => p failure t
+ case Success(v) =>
+ try p success f(v)
+ catch {
+ case t => p complete resolver(t)
+ }
+ }
+
+ p.future
+ }
+
+ /** Creates a new future by applying a function to the successful result of
+ * this future, and returns the result of the function as the new future.
+ * If this future is completed with an exception then the new future will
+ * also contain this exception.
+ *
+ * $forComprehensionExample
+ */
+ def flatMap[S](f: T => Future[S]): Future[S] = {
+ val p = newPromise[S]
+
+ onComplete {
+ case Failure(t) => p failure t
+ case Success(v) =>
+ try {
+ f(v) onComplete {
+ case Failure(t) => p failure t
+ case Success(v) => p success v
+ }
+ } catch {
+ case t: Throwable => p complete resolver(t)
+ }
+ }
+
+ p.future
+ }
+
+ /** Creates a new future by filtering the value of the current future with a predicate.
+ *
+ * If the current future contains a value which satisfies the predicate, the new future will also hold that value.
+ * Otherwise, the resulting future will fail with a `NoSuchElementException`.
+ *
+ * If the current future fails or times out, the resulting future also fails or times out, respectively.
+ *
+ * Example:
+ * {{{
+ * val f = future { 5 }
+ * val g = f filter { _ % 2 == 1 }
+ * val h = f filter { _ % 2 == 0 }
+ * await(0) g // evaluates to 5
+ * await(0) h // throw a NoSuchElementException
+ * }}}
+ */
+ def filter(pred: T => Boolean): Future[T] = {
+ val p = newPromise[T]
+
+ onComplete {
+ case Failure(t) => p failure t
+ case Success(v) =>
+ try {
+ if (pred(v)) p success v
+ else p failure new NoSuchElementException("Future.filter predicate is not satisfied by: " + v)
+ } catch {
+ case t: Throwable => p complete resolver(t)
+ }
+ }
+
+ p.future
+ }
+
+ /** Creates a new future by mapping the value of the current future if the given partial function is defined at that value.
+ *
+ * If the current future contains a value for which the partial function is defined, the new future will also hold that value.
+ * Otherwise, the resulting future will fail with a `NoSuchElementException`.
+ *
+ * If the current future fails or times out, the resulting future also fails or times out, respectively.
+ *
+ * Example:
+ * {{{
+ * val f = future { -5 }
+ * val g = f collect {
+ * case x if x < 0 => -x
+ * }
+ * val h = f collect {
+ * case x if x > 0 => x * 2
+ * }
+ * await(0) g // evaluates to 5
+ * await(0) h // throw a NoSuchElementException
+ * }}}
+ */
+ def collect[S](pf: PartialFunction[T, S]): Future[S] = {
+ val p = newPromise[S]
+
+ onComplete {
+ case Failure(t) => p failure t
+ case Success(v) =>
+ try {
+ if (pf.isDefinedAt(v)) p success pf(v)
+ else p failure new NoSuchElementException("Future.collect partial function is not defined at: " + v)
+ } catch {
+ case t: Throwable => p complete resolver(t)
+ }
+ }
+
+ p.future
+ }
+
+ /** Creates a new future that will handle any matching throwable that this
+ * future might contain. If there is no match, or if this future contains
+ * a valid result then the new future will contain the same.
+ *
+ * Example:
+ *
+ * {{{
+ * future (6 / 0) recover { case e: ArithmeticException ⇒ 0 } // result: 0
+ * future (6 / 0) recover { case e: NotFoundException ⇒ 0 } // result: exception
+ * future (6 / 2) recover { case e: ArithmeticException ⇒ 0 } // result: 3
+ * }}}
+ */
+ def recover[U >: T](pf: PartialFunction[Throwable, U]): Future[U] = {
+ val p = newPromise[U]
+
+ onComplete {
+ case Failure(t) if pf isDefinedAt t =>
+ try { p success pf(t) }
+ catch { case t: Throwable => p complete resolver(t) }
+ case otherwise => p complete otherwise
+ }
+
+ p.future
+ }
+
+ /** Creates a new future that will handle any matching throwable that this
+ * future might contain by assigning it a value of another future.
+ *
+ * If there is no match, or if this future contains
+ * a valid result then the new future will contain the same result.
+ *
+ * Example:
+ *
+ * {{{
+ * val f = future { Int.MaxValue }
+ * future (6 / 0) recoverWith { case e: ArithmeticException => f } // result: Int.MaxValue
+ * }}}
+ */
+ def recoverWith[U >: T](pf: PartialFunction[Throwable, Future[U]]): Future[U] = {
+ val p = newPromise[U]
+
+ onComplete {
+ case Failure(t) if pf isDefinedAt t =>
+ try {
+ p completeWith pf(t)
+ } catch {
+ case t: Throwable => p complete resolver(t)
+ }
+ case otherwise => p complete otherwise
+ }
+
+ p.future
+ }
+
+ /** Zips the values of `this` and `that` future, and creates
+ * a new future holding the tuple of their results.
+ *
+ * If `this` future fails, the resulting future is failed
+ * with the throwable stored in `this`.
+ * Otherwise, if `that` future fails, the resulting future is failed
+ * with the throwable stored in `that`.
+ */
+ def zip[U](that: Future[U]): Future[(T, U)] = {
+ val p = newPromise[(T, U)]
+
+ this onComplete {
+ case Failure(t) => p failure t
+ case Success(r) => that onSuccess {
+ case r2 => p success ((r, r2))
+ }
+ }
+
+ that onFailure {
+ case f => p failure f
+ }
+
+ p.future
+ }
+
+ /** Creates a new future which holds the result of this future if it was completed successfully, or, if not,
+ * the result of the `that` future if `that` is completed successfully.
+ * If both futures are failed, the resulting future holds the throwable object of the first future.
+ *
+ * Using this method will not cause concurrent programs to become nondeterministic.
+ *
+ * Example:
+ * {{{
+ * val f = future { sys.error("failed") }
+ * val g = future { 5 }
+ * val h = f orElse g
+ * await(0) h // evaluates to 5
+ * }}}
+ */
+ def fallbackTo[U >: T](that: Future[U]): Future[U] = {
+ val p = newPromise[U]
+
+ onComplete {
+ case Failure(t) => that onComplete {
+ case Failure(_) => p failure t
+ case Success(v) => p success v
+ }
+ case Success(v) => p success v
+ }
+
+ p.future
+ }
+
+ /** Applies the side-effecting function to the result of this future, and returns
+ * a new future with the result of this future.
+ *
+ * This method allows one to enforce that the callbacks are executed in a
+ * specified order.
+ *
+ * Note that if one of the chained `andThen` callbacks throws
+ * an exception, that exception is not propagated to the subsequent `andThen`
+ * callbacks. Instead, the subsequent `andThen` callbacks are given the original
+ * value of this future.
+ *
+ * The following example prints out `5`:
+ *
+ * {{{
+ * val f = future { 5 }
+ * f andThen {
+ * case r => sys.error("runtime exception")
+ * } andThen {
+ * case Failure(t) => println(t)
+ * case Success(v) => println(v)
+ * }
+ * }}}
+ */
+ def andThen[U](pf: PartialFunction[Try[T], U]): Future[T] = {
+ val p = newPromise[T]
+
+ onComplete {
+ case r =>
+ try if (pf isDefinedAt r) pf(r)
+ finally p complete r
+ }
+
+ p.future
+ }
+
+ /** Creates a new future which holds the result of either this future or `that` future, depending on
+ * which future was completed first.
+ *
+ * $nonDeterministic
+ *
+ * Example:
+ * {{{
+ * val f = future { sys.error("failed") }
+ * val g = future { 5 }
+ * val h = f either g
+ * await(0) h // evaluates to either 5 or throws a runtime exception
+ * }}}
+ */
+ def either[U >: T](that: Future[U]): Future[U] = {
+ val p = self.newPromise[U]
+
+ val completePromise: PartialFunction[Try[U], _] = {
+ case Failure(t) => p tryFailure t
+ case Success(v) => p trySuccess v
+ }
+
+ self onComplete completePromise
+ that onComplete completePromise
+
+ p.future
+ }
+
+}
+
+
+
+/** TODO some docs
+ *
+ * @define nonDeterministic
+ * Note: using this method yields nondeterministic dataflow programs.
+ */
+object Future {
+
+ // TODO make more modular by encoding all other helper methods within the execution context
+ /** TODO some docs
+ */
+ def all[T, Coll[X] <: Traversable[X]](futures: Coll[Future[T]])(implicit cbf: CanBuildFrom[Coll[_], T, Coll[T]], ec: ExecutionContext): Future[Coll[T]] =
+ ec.all[T, Coll](futures)
+
+ // move this to future companion object
+ @inline def apply[T](body: =>T)(implicit executor: ExecutionContext): Future[T] = executor.future(body)
+
+ def any[T](futures: Traversable[Future[T]])(implicit ec: ExecutionContext): Future[T] = ec.any(futures)
+
+ def find[T](futures: Traversable[Future[T]])(predicate: T => Boolean)(implicit ec: ExecutionContext): Future[Option[T]] = ec.find(futures)(predicate)
+
+}
+
+
+
+
diff --git a/src/library/scala/concurrent/FutureTaskRunner.scala b/src/library/scala/concurrent/FutureTaskRunner.scala
index c5fcde2d19..75e6299ad9 100644
--- a/src/library/scala/concurrent/FutureTaskRunner.scala
+++ b/src/library/scala/concurrent/FutureTaskRunner.scala
@@ -13,6 +13,7 @@ package scala.concurrent
*
* @author Philipp Haller
*/
+@deprecated("Use `ExecutionContext`s instead.", "2.10.0")
trait FutureTaskRunner extends TaskRunner {
/** The type of the futures that the underlying task runner supports.
diff --git a/src/library/scala/concurrent/JavaConversions.scala b/src/library/scala/concurrent/JavaConversions.scala
index db3c490882..bac9d4f558 100644
--- a/src/library/scala/concurrent/JavaConversions.scala
+++ b/src/library/scala/concurrent/JavaConversions.scala
@@ -17,6 +17,7 @@ import java.util.concurrent.{ExecutorService, Executor}
*/
object JavaConversions {
+ @deprecated("Use `asExecutionContext` instead.", "2.10.0")
implicit def asTaskRunner(exec: ExecutorService): FutureTaskRunner =
new ThreadPoolRunner {
override protected def executor =
@@ -26,6 +27,7 @@ object JavaConversions {
exec.shutdown()
}
+ @deprecated("Use `asExecutionContext` instead.", "2.10.0")
implicit def asTaskRunner(exec: Executor): TaskRunner =
new TaskRunner {
type Task[T] = Runnable
@@ -46,4 +48,9 @@ object JavaConversions {
// do nothing
}
}
+
+ implicit def asExecutionContext(exec: ExecutorService): ExecutionContext = null // TODO
+
+ implicit def asExecutionContext(exec: Executor): ExecutionContext = null // TODO
+
}
diff --git a/src/library/scala/concurrent/ManagedBlocker.scala b/src/library/scala/concurrent/ManagedBlocker.scala
index 9c6f4d51d6..0b6d82e76f 100644
--- a/src/library/scala/concurrent/ManagedBlocker.scala
+++ b/src/library/scala/concurrent/ManagedBlocker.scala
@@ -12,6 +12,7 @@ package scala.concurrent
*
* @author Philipp Haller
*/
+@deprecated("Not used.", "2.10.0")
trait ManagedBlocker {
/**
diff --git a/src/library/scala/concurrent/Promise.scala b/src/library/scala/concurrent/Promise.scala
new file mode 100644
index 0000000000..f26deb77ab
--- /dev/null
+++ b/src/library/scala/concurrent/Promise.scala
@@ -0,0 +1,132 @@
+/* __ *\
+** ________ ___ / / ___ Scala API **
+** / __/ __// _ | / / / _ | (c) 2003-2011, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ **
+** /____/\___/_/ |_/____/_/ | | **
+** |/ **
+\* */
+
+package scala.concurrent
+
+import scala.util.{ Try, Success, Failure }
+
+
+
+
+/** Promise is an object which can be completed with a value or failed
+ * with an exception.
+ *
+ * @define promiseCompletion
+ * If the promise has already been fulfilled, failed or has timed out,
+ * calling this method will throw an IllegalStateException.
+ *
+ * @define allowedThrowables
+ * If the throwable used to fail this promise is an error, a control exception
+ * or an interrupted exception, it will be wrapped as a cause within an
+ * `ExecutionException` which will fail the promise.
+ *
+ * @define nonDeterministic
+ * Note: Using this method may result in non-deterministic concurrent programs.
+ */
+trait Promise[T] {
+
+ import nondeterministic._
+
+ /** Future containing the value of this promise.
+ */
+ def future: Future[T]
+
+ /** Completes the promise with either an exception or a value.
+ *
+ * @param result Either the value or the exception to complete the promise with.
+ *
+ * $promiseCompletion
+ */
+ def complete(result:Try[T]): this.type = if (tryComplete(result)) this else throwCompleted
+
+ /** Tries to complete the promise with either a value or the exception.
+ *
+ * $nonDeterministic
+ *
+ * @return If the promise has already been completed returns `false`, or `true` otherwise.
+ */
+ def tryComplete(result: Try[T]): Boolean
+
+ /** Completes this promise with the specified future, once that future is completed.
+ *
+ * @return This promise
+ */
+ final def completeWith(other: Future[T]): this.type = {
+ other onComplete {
+ this complete _
+ }
+ this
+ }
+
+ /** Completes the promise with a value.
+ *
+ * @param value The value to complete the promise with.
+ *
+ * $promiseCompletion
+ */
+ def success(v: T): this.type = if (trySuccess(v)) this else throwCompleted
+
+ /** Tries to complete the promise with a value.
+ *
+ * $nonDeterministic
+ *
+ * @return If the promise has already been completed returns `false`, or `true` otherwise.
+ */
+ def trySuccess(value: T): Boolean = tryComplete(Success(value))
+
+ /** Completes the promise with an exception.
+ *
+ * @param t The throwable to complete the promise with.
+ *
+ * $allowedThrowables
+ *
+ * $promiseCompletion
+ */
+ def failure(t: Throwable): this.type = if (tryFailure(t)) this else throwCompleted
+
+ /** Tries to complete the promise with an exception.
+ *
+ * $nonDeterministic
+ *
+ * @return If the promise has already been completed returns `false`, or `true` otherwise.
+ */
+ def tryFailure(t: Throwable): Boolean = tryComplete(Failure(t))
+
+ /** Wraps a `Throwable` in an `ExecutionException` if necessary. TODO replace with `resolver` from scala.concurrent
+ *
+ * $allowedThrowables
+ */
+ protected def wrap(t: Throwable): Throwable = t match {
+ case t: Throwable if isFutureThrowable(t) => t
+ case _ => new ExecutionException(t)
+ }
+
+ private def throwCompleted = throw new IllegalStateException("Promise already completed.")
+
+}
+
+
+
+object Promise {
+
+ def kept[T](result: T)(implicit execctx: ExecutionContext): Promise[T] =
+ execctx keptPromise result
+
+ def broken[T](t: Throwable)(implicit execctx: ExecutionContext): Promise[T] =
+ execctx brokenPromise t
+
+}
+
+
+
+
+
+
+
+
+
diff --git a/src/library/scala/concurrent/Scheduler.scala b/src/library/scala/concurrent/Scheduler.scala
new file mode 100644
index 0000000000..39d798e6b4
--- /dev/null
+++ b/src/library/scala/concurrent/Scheduler.scala
@@ -0,0 +1,54 @@
+/* __ *\
+** ________ ___ / / ___ Scala API **
+** / __/ __// _ | / / / _ | (c) 2003-2011, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ **
+** /____/\___/_/ |_/____/_/ | | **
+** |/ **
+\* */
+
+package scala.concurrent
+
+import scala.util.Duration
+
+/** A service for scheduling tasks and thunks for one-time, or periodic execution.
+ */
+trait Scheduler {
+
+ /** Schedules a thunk for repeated execution with an initial delay and a frequency.
+ *
+ * @param delay the initial delay after which the thunk should be executed
+ * the first time
+ * @param frequency the frequency with which the thunk should be executed,
+ * as a time period between subsequent executions
+ */
+ def schedule(delay: Duration, frequency: Duration)(thunk: => Unit): Cancellable
+
+ /** Schedules a task for execution after a given delay.
+ *
+ * @param delay the duration after which the task should be executed
+ * @param task the task that is scheduled for execution
+ * @return a `Cancellable` that may be used to cancel the execution
+ * of the task
+ */
+ def scheduleOnce(delay: Duration, task: Runnable): Cancellable
+
+ /** Schedules a thunk for execution after a given delay.
+ *
+ * @param delay the duration after which the thunk should be executed
+ * @param thunk the thunk that is scheduled for execution
+ * @return a `Cancellable` that may be used to cancel the execution
+ * of the thunk
+ */
+ def scheduleOnce(delay: Duration)(task: => Unit): Cancellable
+
+}
+
+
+
+trait Cancellable {
+
+ /** Cancels the underlying task.
+ */
+ def cancel(): Unit
+
+}
diff --git a/src/library/scala/concurrent/Task.scala b/src/library/scala/concurrent/Task.scala
new file mode 100644
index 0000000000..d6f86bac31
--- /dev/null
+++ b/src/library/scala/concurrent/Task.scala
@@ -0,0 +1,13 @@
+package scala.concurrent
+
+
+
+trait Task[+T] {
+
+ def start(): Unit
+
+ def future: Future[T]
+
+}
+
+
diff --git a/src/library/scala/concurrent/TaskRunner.scala b/src/library/scala/concurrent/TaskRunner.scala
index 64e62adfd3..500d79e07f 100644
--- a/src/library/scala/concurrent/TaskRunner.scala
+++ b/src/library/scala/concurrent/TaskRunner.scala
@@ -12,6 +12,7 @@ package scala.concurrent
*
* @author Philipp Haller
*/
+@deprecated("Use `ExecutionContext`s instead.", "2.10.0")
trait TaskRunner {
type Task[T]
diff --git a/src/library/scala/concurrent/TaskRunners.scala b/src/library/scala/concurrent/TaskRunners.scala
index 588073dc5e..7994255b25 100644
--- a/src/library/scala/concurrent/TaskRunners.scala
+++ b/src/library/scala/concurrent/TaskRunners.scala
@@ -14,6 +14,7 @@ import java.util.concurrent.{ThreadPoolExecutor, LinkedBlockingQueue, TimeUnit}
*
* @author Philipp Haller
*/
+@deprecated("Use `ExecutionContext`s instead.", "2.10.0")
object TaskRunners {
implicit val threadRunner: FutureTaskRunner =
diff --git a/src/library/scala/concurrent/ThreadPoolRunner.scala b/src/library/scala/concurrent/ThreadPoolRunner.scala
index 27d8f2cc32..a3e0253634 100644
--- a/src/library/scala/concurrent/ThreadPoolRunner.scala
+++ b/src/library/scala/concurrent/ThreadPoolRunner.scala
@@ -15,6 +15,7 @@ import java.util.concurrent.{ExecutorService, Callable, TimeUnit}
*
* @author Philipp Haller
*/
+@deprecated("Use `ExecutionContext`s instead.", "2.10.0")
trait ThreadPoolRunner extends FutureTaskRunner {
type Task[T] = Callable[T] with Runnable
diff --git a/src/library/scala/concurrent/default/SchedulerImpl.scala.disabled b/src/library/scala/concurrent/default/SchedulerImpl.scala.disabled
new file mode 100644
index 0000000000..745d2d1a15
--- /dev/null
+++ b/src/library/scala/concurrent/default/SchedulerImpl.scala.disabled
@@ -0,0 +1,44 @@
+/* __ *\
+** ________ ___ / / ___ Scala API **
+** / __/ __// _ | / / / _ | (c) 2003-2011, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ **
+** /____/\___/_/ |_/____/_/ | | **
+** |/ **
+\* */
+
+package scala.concurrent
+package default
+
+import scala.util.Duration
+
+private[concurrent] final class SchedulerImpl extends Scheduler {
+ private val timer =
+ new java.util.Timer(true) // the associated thread runs as a daemon
+
+ def schedule(delay: Duration, frequency: Duration)(thunk: => Unit): Cancellable = ???
+
+ def scheduleOnce(delay: Duration, task: Runnable): Cancellable = {
+ val timerTask = new java.util.TimerTask {
+ def run(): Unit =
+ task.run()
+ }
+ timer.schedule(timerTask, delay.toMillis)
+ new Cancellable {
+ def cancel(): Unit =
+ timerTask.cancel()
+ }
+ }
+
+ def scheduleOnce(delay: Duration)(task: => Unit): Cancellable = {
+ val timerTask = new java.util.TimerTask {
+ def run(): Unit =
+ task
+ }
+ timer.schedule(timerTask, delay.toMillis)
+ new Cancellable {
+ def cancel(): Unit =
+ timerTask.cancel()
+ }
+ }
+
+}
diff --git a/src/library/scala/concurrent/default/TaskImpl.scala.disabled b/src/library/scala/concurrent/default/TaskImpl.scala.disabled
new file mode 100644
index 0000000000..94e54cb372
--- /dev/null
+++ b/src/library/scala/concurrent/default/TaskImpl.scala.disabled
@@ -0,0 +1,313 @@
+package scala.concurrent
+package default
+
+
+
+import java.util.concurrent.atomic.AtomicReferenceFieldUpdater
+import scala.concurrent.forkjoin.{ ForkJoinPool, RecursiveAction, ForkJoinWorkerThread }
+import scala.util.Try
+import scala.util
+import scala.util.Duration
+import scala.annotation.tailrec
+
+
+
+private[concurrent] trait Completable[T] {
+self: Future[T] =>
+
+ val executor: ExecutionContextImpl
+
+ def newPromise[S]: Promise[S] = executor promise
+
+ type Callback = Try[T] => Any
+
+ def getState: State[T]
+
+ def casState(oldv: State[T], newv: State[T]): Boolean
+
+ protected def dispatch[U](r: Runnable) = executionContext execute r
+
+ protected def processCallbacks(cbs: List[Callback], r: Try[T]) =
+ for (cb <- cbs) dispatch(new Runnable {
+ override def run() = cb(r)
+ })
+
+ def future: Future[T] = self
+
+ def onComplete[U](callback: Try[T] => U): this.type = {
+ @tailrec def tryAddCallback(): Try[T] = {
+ getState match {
+ case p @ Pending(lst) =>
+ val pt = p.asInstanceOf[Pending[T]]
+ if (casState(pt, Pending(callback :: pt.callbacks))) null
+ else tryAddCallback()
+ case Success(res) => util.Success(res)
+ case Failure(t) => util.Failure(t)
+ }
+ }
+
+ val res = tryAddCallback()
+ if (res != null) dispatch(new Runnable {
+ override def run() =
+ try callback(res)
+ catch handledFutureException andThen {
+ t => Console.err.println(t)
+ }
+ })
+
+ this
+ }
+
+ def isTimedout: Boolean = getState match {
+ case Failure(ft: FutureTimeoutException) => true
+ case _ => false
+ }
+
+}
+
+private[concurrent] class PromiseImpl[T](context: ExecutionContextImpl)
+extends Promise[T] with Future[T] with Completable[T] {
+
+ val executor: scala.concurrent.default.ExecutionContextImpl = context
+
+ @volatile private var state: State[T] = _
+
+ val updater = AtomicReferenceFieldUpdater.newUpdater(classOf[PromiseImpl[T]], classOf[State[T]], "state")
+
+ updater.set(this, Pending(List()))
+
+ def casState(oldv: State[T], newv: State[T]): Boolean = {
+ updater.compareAndSet(this, oldv, newv)
+ }
+
+ def getState: State[T] = {
+ updater.get(this)
+ }
+
+ @tailrec private def tryCompleteState(completed: State[T]): List[Callback] = (getState: @unchecked) match {
+ case p @ Pending(cbs) => if (!casState(p, completed)) tryCompleteState(completed) else cbs
+ case _ => null
+ }
+
+ def tryComplete(r: Try[T]) = r match {
+ case util.Failure(t) => tryFailure(t)
+ case util.Success(v) => trySuccess(v)
+ }
+
+ override def trySuccess(value: T): Boolean = {
+ val cbs = tryCompleteState(Success(value))
+ if (cbs == null)
+ false
+ else {
+ processCallbacks(cbs, util.Success(value))
+ this.synchronized {
+ this.notifyAll()
+ }
+ true
+ }
+ }
+
+ override def tryFailure(t: Throwable): Boolean = {
+ val wrapped = wrap(t)
+ val cbs = tryCompleteState(Failure(wrapped))
+ if (cbs == null)
+ false
+ else {
+ processCallbacks(cbs, util.Failure(wrapped))
+ this.synchronized {
+ this.notifyAll()
+ }
+ true
+ }
+ }
+
+ def await(atMost: Duration)(implicit canawait: scala.concurrent.CanAwait): T = getState match {
+ case Success(res) => res
+ case Failure(t) => throw t
+ case _ =>
+ this.synchronized {
+ while (true)
+ getState match {
+ case Pending(_) => this.wait()
+ case Success(res) => return res
+ case Failure(t) => throw t
+ }
+ }
+ sys.error("unreachable")
+ }
+
+}
+
+private[concurrent] class TaskImpl[T](context: ExecutionContextImpl, body: => T)
+extends RecursiveAction with Task[T] with Future[T] with Completable[T] {
+
+ val executor: ExecutionContextImpl = context
+
+ @volatile private var state: State[T] = _
+
+ val updater = AtomicReferenceFieldUpdater.newUpdater(classOf[TaskImpl[T]], classOf[State[T]], "state")
+
+ updater.set(this, Pending(List()))
+
+ def casState(oldv: State[T], newv: State[T]): Boolean = {
+ updater.compareAndSet(this, oldv, newv)
+ }
+
+ def getState: State[T] = {
+ updater.get(this)
+ }
+
+ @tailrec private def tryCompleteState(completed: State[T]): List[Callback] = (getState: @unchecked) match {
+ case p @ Pending(cbs) => if (!casState(p, completed)) tryCompleteState(completed) else cbs
+ }
+
+ def compute(): Unit = {
+ var cbs: List[Callback] = null
+ try {
+ val res = body
+ processCallbacks(tryCompleteState(Success(res)), util.Success(res))
+ } catch {
+ case t if isFutureThrowable(t) =>
+ processCallbacks(tryCompleteState(Failure(t)), util.Failure(t))
+ case t =>
+ val ee = new ExecutionException(t)
+ processCallbacks(tryCompleteState(Failure(ee)), util.Failure(ee))
+ throw t
+ }
+ }
+
+ def start(): Unit = {
+ Thread.currentThread match {
+ case fj: ForkJoinWorkerThread if fj.getPool eq executor.pool => fork()
+ case _ => executor.pool.execute(this)
+ }
+ }
+
+ // TODO FIXME: handle timeouts
+ def await(atMost: Duration): this.type =
+ await
+
+ def await: this.type = {
+ this.join()
+ this
+ }
+
+ def tryCancel(): Unit =
+ tryUnfork()
+
+ def await(atMost: Duration)(implicit canawait: CanAwait): T = {
+ join() // TODO handle timeout also
+ (updater.get(this): @unchecked) match {
+ case Success(r) => r
+ case Failure(t) => throw t
+ }
+ }
+
+}
+
+
+private[concurrent] sealed abstract class State[T]
+
+
+case class Pending[T](callbacks: List[Try[T] => Any]) extends State[T]
+
+
+case class Success[T](result: T) extends State[T]
+
+
+case class Failure[T](throwable: Throwable) extends State[T]
+
+
+private[concurrent] final class ExecutionContextImpl extends ExecutionContext {
+ import ExecutionContextImpl._
+
+ val pool = {
+ val p = new ForkJoinPool
+ p.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler {
+ def uncaughtException(t: Thread, throwable: Throwable) {
+ Console.err.println(throwable.getMessage)
+ throwable.printStackTrace(Console.err)
+ }
+ })
+ p
+ }
+
+ @inline
+ private def executeTask(task: RecursiveAction) {
+ if (Thread.currentThread.isInstanceOf[ForkJoinWorkerThread])
+ task.fork()
+ else
+ pool execute task
+ }
+
+ def execute(task: Runnable) {
+ val action = new RecursiveAction { def compute() { task.run() } }
+ executeTask(action)
+ }
+
+ def execute[U](body: () => U) {
+ val action = new RecursiveAction { def compute() { body() } }
+ executeTask(action)
+ }
+
+ def task[T](body: => T): Task[T] = {
+ new TaskImpl(this, body)
+ }
+
+ def future[T](body: => T): Future[T] = {
+ val t = task(body)
+ t.start()
+ t.future
+ }
+
+ def promise[T]: Promise[T] =
+ new PromiseImpl[T](this)
+
+ def blocking[T](atMost: Duration)(body: =>T): T = blocking(body2awaitable(body), atMost)
+
+ def blocking[T](awaitable: Awaitable[T], atMost: Duration): T = {
+ currentExecutionContext.get match {
+ case null => awaitable.await(atMost)(null) // outside - TODO - fix timeout case
+ case x if x eq this => this.blockingCall(awaitable) // inside an execution context thread on this executor
+ case x => x.blocking(awaitable, atMost)
+ }
+ }
+
+ private def blockingCall[T](b: Awaitable[T]): T = b match {
+ case fj: TaskImpl[_] if fj.executor.pool eq pool =>
+ fj.await(Duration.fromNanos(0))
+ case _ =>
+ var res: T = null.asInstanceOf[T]
+ @volatile var blockingDone = false
+ // TODO add exception handling here!
+ val mb = new ForkJoinPool.ManagedBlocker {
+ def block() = {
+ res = b.await(Duration.fromNanos(0))(CanAwaitEvidence)
+ blockingDone = true
+ true
+ }
+ def isReleasable = blockingDone
+ }
+ ForkJoinPool.managedBlock(mb, true)
+ res
+ }
+
+ def reportFailure(t: Throwable): Unit = {}
+
+}
+
+
+object ExecutionContextImpl {
+
+ private[concurrent] def currentExecutionContext: ThreadLocal[ExecutionContext] = new ThreadLocal[ExecutionContext] {
+ override protected def initialValue = null
+ }
+
+}
+
+
+
+
+
+
+
diff --git a/src/library/scala/concurrent/impl/AbstractPromise.java b/src/library/scala/concurrent/impl/AbstractPromise.java
new file mode 100644
index 0000000000..5280d67854
--- /dev/null
+++ b/src/library/scala/concurrent/impl/AbstractPromise.java
@@ -0,0 +1,21 @@
+/* __ *\
+** ________ ___ / / ___ Scala API **
+** / __/ __// _ | / / / _ | (c) 2003-2011, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ **
+** /____/\___/_/ |_/____/_/ | | **
+** |/ **
+\* */
+
+package scala.concurrent.impl;
+
+
+
+import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
+
+
+
+abstract class AbstractPromise {
+ private volatile Object _ref = null;
+ protected final static AtomicReferenceFieldUpdater<AbstractPromise, Object> updater =
+ AtomicReferenceFieldUpdater.newUpdater(AbstractPromise.class, Object.class, "_ref");
+}
diff --git a/src/library/scala/concurrent/impl/ExecutionContextImpl.scala b/src/library/scala/concurrent/impl/ExecutionContextImpl.scala
new file mode 100644
index 0000000000..af0eb66292
--- /dev/null
+++ b/src/library/scala/concurrent/impl/ExecutionContextImpl.scala
@@ -0,0 +1,134 @@
+/* __ *\
+** ________ ___ / / ___ Scala API **
+** / __/ __// _ | / / / _ | (c) 2003-2011, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ **
+** /____/\___/_/ |_/____/_/ | | **
+** |/ **
+\* */
+
+package scala.concurrent.impl
+
+
+
+import java.util.concurrent.{Callable, ExecutorService}
+import scala.concurrent.{ExecutionContext, resolver, Awaitable, body2awaitable}
+import scala.util.{ Duration, Try, Success, Failure }
+import scala.collection.mutable.Stack
+
+
+
+class ExecutionContextImpl(executorService: ExecutorService) extends ExecutionContext {
+ import ExecutionContextImpl._
+
+ def execute(runnable: Runnable): Unit = executorService match {
+ // case fj: ForkJoinPool =>
+ // TODO fork if more applicable
+ // executorService execute runnable
+ case _ =>
+ executorService execute runnable
+ }
+
+ def execute[U](body: () => U): Unit = execute(new Runnable {
+ def run() = body()
+ })
+
+ def promise[T]: Promise[T] = new Promise.DefaultPromise[T]()(this)
+
+ def future[T](body: =>T): Future[T] = {
+ val p = promise[T]
+
+ dispatchFuture {
+ () =>
+ p complete {
+ try {
+ Success(body)
+ } catch {
+ case e => resolver(e)
+ }
+ }
+ }
+
+ p.future
+ }
+
+ def blocking[T](atMost: Duration)(body: =>T): T = blocking(body2awaitable(body), atMost)
+
+ def blocking[T](awaitable: Awaitable[T], atMost: Duration): T = {
+ currentExecutionContext.get match {
+ case null => awaitable.await(atMost)(null) // outside - TODO - fix timeout case
+ case x => x.blockingCall(awaitable) // inside an execution context thread
+ }
+ }
+
+ def reportFailure(t: Throwable) = t match {
+ case e: Error => throw e // rethrow serious errors
+ case t => t.printStackTrace()
+ }
+
+ /** Only callable from the tasks running on the same execution context. */
+ private def blockingCall[T](body: Awaitable[T]): T = {
+ releaseStack()
+
+ // TODO see what to do with timeout
+ body.await(Duration.fromNanos(0))(CanAwaitEvidence)
+ }
+
+ // an optimization for batching futures
+ // TODO we should replace this with a public queue,
+ // so that it can be stolen from
+ // OR: a push to the local task queue should be so cheap that this is
+ // not even needed, but stealing is still possible
+ private val _taskStack = new ThreadLocal[Stack[() => Unit]]()
+
+ private def releaseStack(): Unit =
+ _taskStack.get match {
+ case stack if (stack ne null) && stack.nonEmpty =>
+ val tasks = stack.elems
+ stack.clear()
+ _taskStack.remove()
+ dispatchFuture(() => _taskStack.get.elems = tasks, true)
+ case null =>
+ // do nothing - there is no local batching stack anymore
+ case _ =>
+ _taskStack.remove()
+ }
+
+ private[impl] def dispatchFuture(task: () => Unit, force: Boolean = false): Unit =
+ _taskStack.get match {
+ case stack if (stack ne null) && !force => stack push task
+ case _ => this.execute(
+ new Runnable {
+ def run() {
+ try {
+ val taskStack = Stack[() => Unit](task)
+ _taskStack set taskStack
+ while (taskStack.nonEmpty) {
+ val next = taskStack.pop()
+ try {
+ next.apply()
+ } catch {
+ case e =>
+ // TODO catching all and continue isn't good for OOME
+ reportFailure(e)
+ }
+ }
+ } finally {
+ _taskStack.remove()
+ }
+ }
+ }
+ )
+ }
+
+}
+
+
+object ExecutionContextImpl {
+
+ private[concurrent] def currentExecutionContext: ThreadLocal[ExecutionContextImpl] = new ThreadLocal[ExecutionContextImpl] {
+ override protected def initialValue = null
+ }
+
+}
+
+
diff --git a/src/library/scala/concurrent/impl/Future.scala b/src/library/scala/concurrent/impl/Future.scala
new file mode 100644
index 0000000000..24d0258cc8
--- /dev/null
+++ b/src/library/scala/concurrent/impl/Future.scala
@@ -0,0 +1,89 @@
+/* __ *\
+** ________ ___ / / ___ Scala API **
+** / __/ __// _ | / / / _ | (c) 2003-2011, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ **
+** /____/\___/_/ |_/____/_/ | | **
+** |/ **
+\* */
+
+package scala.concurrent.impl
+
+import scala.concurrent.{Awaitable, ExecutionContext}
+import scala.util.{ Try, Success, Failure }
+//import scala.util.continuations._
+
+trait Future[+T] extends scala.concurrent.Future[T] with Awaitable[T] {
+
+ implicit def executor: ExecutionContextImpl
+
+ /** For use only within a Future.flow block or another compatible Delimited Continuations reset block.
+ *
+ * Returns the result of this Future without blocking, by suspending execution and storing it as a
+ * continuation until the result is available.
+ */
+ //def apply(): T @cps[Future[Any]] = shift(this flatMap (_: T => Future[Any]))
+
+ /** Tests whether this Future has been completed.
+ */
+ final def isCompleted: Boolean = value.isDefined
+
+ /** The contained value of this Future. Before this Future is completed
+ * the value will be None. After completion the value will be Some(Right(t))
+ * if it contains a valid result, or Some(Left(error)) if it contains
+ * an exception.
+ */
+ def value: Option[Try[T]]
+
+ def onComplete[U](func: Try[T] => U): this.type
+
+ /** Creates a new Future[A] which is completed with this Future's result if
+ * that conforms to A's erased type or a ClassCastException otherwise.
+ */
+ final def mapTo[T](implicit m: Manifest[T]) = {
+ val p = executor.promise[T]
+
+ onComplete {
+ case f @ Failure(t) => p complete f.asInstanceOf[Try[T]]
+ case Success(v) =>
+ p complete (try {
+ Success(Future.boxedType(m.erasure).cast(v).asInstanceOf[T])
+ } catch {
+ case e: ClassCastException ⇒ Failure(e)
+ })
+ }
+
+ p.future
+ }
+
+ /** Used by for-comprehensions.
+ */
+ final def withFilter(p: T => Boolean) = new FutureWithFilter[T](this, p)
+
+ final class FutureWithFilter[+A](self: Future[A], p: A => Boolean) {
+ def foreach(f: A => Unit): Unit = self filter p foreach f
+ def map[B](f: A => B) = self filter p map f
+ def flatMap[B](f: A => Future[B]) = self filter p flatMap f
+ def withFilter(q: A => Boolean): FutureWithFilter[A] = new FutureWithFilter[A](self, x ⇒ p(x) && q(x))
+ }
+
+}
+
+object Future {
+ import java.{ lang => jl }
+
+ private val toBoxed = Map[Class[_], Class[_]](
+ classOf[Boolean] -> classOf[jl.Boolean],
+ classOf[Byte] -> classOf[jl.Byte],
+ classOf[Char] -> classOf[jl.Character],
+ classOf[Short] -> classOf[jl.Short],
+ classOf[Int] -> classOf[jl.Integer],
+ classOf[Long] -> classOf[jl.Long],
+ classOf[Float] -> classOf[jl.Float],
+ classOf[Double] -> classOf[jl.Double],
+ classOf[Unit] -> classOf[scala.runtime.BoxedUnit]
+ )
+
+ def boxedType(c: Class[_]): Class[_] = {
+ if (c.isPrimitive) toBoxed(c) else c
+ }
+}
diff --git a/src/library/scala/concurrent/impl/Promise.scala b/src/library/scala/concurrent/impl/Promise.scala
new file mode 100644
index 0000000000..7ef76e1501
--- /dev/null
+++ b/src/library/scala/concurrent/impl/Promise.scala
@@ -0,0 +1,252 @@
+/* __ *\
+** ________ ___ / / ___ Scala API **
+** / __/ __// _ | / / / _ | (c) 2003-2011, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ **
+** /____/\___/_/ |_/____/_/ | | **
+** |/ **
+\* */
+
+package scala.concurrent.impl
+
+
+
+import java.util.concurrent.TimeUnit.{ NANOSECONDS, MILLISECONDS }
+import java.util.concurrent.atomic.AtomicReferenceFieldUpdater
+import scala.concurrent.{Awaitable, ExecutionContext, resolve, resolver, blocking, CanAwait, TimeoutException}
+//import scala.util.continuations._
+import scala.util.Duration
+import scala.util.Try
+import scala.util
+import scala.annotation.tailrec
+//import scala.concurrent.NonDeterministic
+
+
+
+trait Promise[T] extends scala.concurrent.Promise[T] with Future[T] {
+
+ def future = this
+
+ def newPromise[S]: Promise[S] = executor promise
+
+ // TODO refine answer and return types here from Any to type parameters
+ // then move this up in the hierarchy
+ /*
+ final def <<(value: T): Future[T] @cps[Future[Any]] = shift {
+ cont: (Future[T] => Future[Any]) =>
+ cont(complete(Right(value)))
+ }
+
+ final def <<(other: Future[T]): Future[T] @cps[Future[Any]] = shift {
+ cont: (Future[T] => Future[Any]) =>
+ val p = executor.promise[Any]
+ val thisPromise = this
+
+ thisPromise completeWith other
+ thisPromise onComplete { v =>
+ try {
+ p completeWith cont(thisPromise)
+ } catch {
+ case e => p complete resolver(e)
+ }
+ }
+
+ p.future
+ }
+ */
+ // TODO finish this once we introduce something like dataflow streams
+
+ /*
+ final def <<(stream: PromiseStreamOut[T]): Future[T] @cps[Future[Any]] = shift { cont: (Future[T] => Future[Any]) =>
+ val fr = executor.promise[Any]
+ val f = stream.dequeue(this)
+ f.onComplete { _ =>
+ try {
+ fr completeWith cont(f)
+ } catch {
+ case e =>
+ fr failure e
+ }
+ }
+ fr
+ }
+ */
+
+}
+
+
+object Promise {
+ def dur2long(dur: Duration): Long = if (dur.isFinite) dur.toNanos else Long.MaxValue
+
+ def EmptyPending[T](): FState[T] = emptyPendingValue.asInstanceOf[FState[T]]
+
+ /** Represents the internal state.
+ */
+ sealed trait FState[+T] { def value: Option[Try[T]] }
+
+ case class Pending[T](listeners: List[Try[T] => Any] = Nil) extends FState[T] {
+ def value: Option[Try[T]] = None
+ }
+
+ case class Success[T](value: Option[util.Success[T]] = None) extends FState[T] {
+ def result: T = value.get.get
+ }
+
+ case class Failure[T](value: Option[util.Failure[T]] = None) extends FState[T] {
+ def exception: Throwable = value.get.exception
+ }
+
+ private val emptyPendingValue = Pending[Nothing](Nil)
+
+ /** Default promise implementation.
+ */
+ class DefaultPromise[T](implicit val executor: ExecutionContextImpl) extends AbstractPromise with Promise[T] {
+ self =>
+
+ updater.set(this, Promise.EmptyPending())
+
+ protected final def tryAwait(atMost: Duration): Boolean = {
+ @tailrec
+ def awaitUnsafe(waitTimeNanos: Long): Boolean = {
+ if (value.isEmpty && waitTimeNanos > 0) {
+ val ms = NANOSECONDS.toMillis(waitTimeNanos)
+ val ns = (waitTimeNanos % 1000000l).toInt // as per object.wait spec
+ val start = System.nanoTime()
+ try {
+ synchronized {
+ while (value.isEmpty) wait(ms, ns)
+ }
+ } catch {
+ case e: InterruptedException =>
+ }
+
+ awaitUnsafe(waitTimeNanos - (System.nanoTime() - start))
+ } else
+ value.isDefined
+ }
+
+ executor.blocking(concurrent.body2awaitable(awaitUnsafe(dur2long(atMost))), Duration.fromNanos(0))
+ }
+
+ private def ready(atMost: Duration)(implicit permit: CanAwait): this.type =
+ if (value.isDefined || tryAwait(atMost)) this
+ else throw new TimeoutException("Futures timed out after [" + atMost.toMillis + "] milliseconds")
+
+ def await(atMost: Duration)(implicit permit: CanAwait): T =
+ ready(atMost).value.get match {
+ case util.Failure(e) => throw e
+ case util.Success(r) => r
+ }
+
+ def value: Option[Try[T]] = getState.value
+
+ @inline
+ private[this] final def updater = AbstractPromise.updater.asInstanceOf[AtomicReferenceFieldUpdater[AbstractPromise, FState[T]]]
+
+ @inline
+ protected final def updateState(oldState: FState[T], newState: FState[T]): Boolean = updater.compareAndSet(this, oldState, newState)
+
+ @inline
+ protected final def getState: FState[T] = updater.get(this)
+
+ def tryComplete(value: Try[T]): Boolean = {
+ val callbacks: List[Try[T] => Any] = {
+ try {
+ @tailrec
+ def tryComplete(v: Try[T]): List[Try[T] => Any] = {
+ getState match {
+ case cur @ Pending(listeners) =>
+ if (updateState(cur, if (v.isFailure) Failure(Some(v.asInstanceOf[util.Failure[T]])) else Success(Some(v.asInstanceOf[util.Success[T]])))) listeners
+ else tryComplete(v)
+ case _ => null
+ }
+ }
+ tryComplete(resolve(value))
+ } finally {
+ synchronized { notifyAll() } // notify any blockers from `tryAwait`
+ }
+ }
+
+ callbacks match {
+ case null => false
+ case cs if cs.isEmpty => true
+ case cs =>
+ executor dispatchFuture {
+ () => cs.foreach(f => notifyCompleted(f, value))
+ }
+ true
+ }
+ }
+
+ def onComplete[U](func: Try[T] => U): this.type = {
+ @tailrec // Returns whether the future has already been completed or not
+ def tryAddCallback(): Boolean = {
+ val cur = getState
+ cur match {
+ case _: Success[_] | _: Failure[_] => true
+ case p: Pending[_] =>
+ val pt = p.asInstanceOf[Pending[T]]
+ if (updateState(pt, pt.copy(listeners = func :: pt.listeners))) false else tryAddCallback()
+ }
+ }
+
+ if (tryAddCallback()) {
+ val result = value.get
+ executor dispatchFuture {
+ () => notifyCompleted(func, result)
+ }
+ }
+
+ this
+ }
+
+ private final def notifyCompleted(func: Try[T] => Any, result: Try[T]) {
+ try {
+ func(result)
+ } catch {
+ case e => executor.reportFailure(e)
+ }
+ }
+ }
+
+ /** An already completed Future is given its result at creation.
+ *
+ * Useful in Future-composition when a value to contribute is already available.
+ */
+ final class KeptPromise[T](suppliedValue: Try[T])(implicit val executor: ExecutionContextImpl) extends Promise[T] {
+ val value = Some(resolve(suppliedValue))
+
+ def tryComplete(value: Try[T]): Boolean = false
+
+ def onComplete[U](func: Try[T] => U): this.type = {
+ val completedAs = value.get
+ executor dispatchFuture {
+ () => func(completedAs)
+ }
+ this
+ }
+
+ private def ready(atMost: Duration)(implicit permit: CanAwait): this.type = this
+
+ def await(atMost: Duration)(implicit permit: CanAwait): T = value.get match {
+ case util.Failure(e) => throw e
+ case util.Success(r) => r
+ }
+ }
+
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/library/scala/concurrent/ops.scala b/src/library/scala/concurrent/ops.scala
index 92220a8313..2cea29aefe 100644
--- a/src/library/scala/concurrent/ops.scala
+++ b/src/library/scala/concurrent/ops.scala
@@ -15,6 +15,7 @@ import scala.util.control.Exception.allCatch
*
* @author Martin Odersky, Stepan Koltsov, Philipp Haller
*/
+@deprecated("Use `future` instead.", "2.10.0")
object ops
{
val defaultRunner: FutureTaskRunner = TaskRunners.threadRunner
diff --git a/src/library/scala/concurrent/package.scala b/src/library/scala/concurrent/package.scala
new file mode 100644
index 0000000000..6a98fd50c2
--- /dev/null
+++ b/src/library/scala/concurrent/package.scala
@@ -0,0 +1,57 @@
+/* __ *\
+** ________ ___ / / ___ Scala API **
+** / __/ __// _ | / / / _ | (c) 2003-2011, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ **
+** /____/\___/_/ |_/____/_/ | | **
+** |/ **
+\* */
+
+package scala
+
+import scala.util.{ Duration, Try, Success, Failure }
+
+/** This package object contains primitives for concurrent and parallel programming.
+ */
+package object concurrent extends scala.concurrent.ConcurrentPackageObject {
+ type ExecutionException = java.util.concurrent.ExecutionException
+ type CancellationException = java.util.concurrent.CancellationException
+ type TimeoutException = java.util.concurrent.TimeoutException
+}
+
+package concurrent {
+ object await {
+ def ready[T](atMost: Duration)(awaitable: Awaitable[T])(implicit execCtx: ExecutionContext = executionContext): Awaitable[T] = {
+ try blocking(awaitable, atMost)
+ catch { case _ => }
+ awaitable
+ }
+
+ def result[T](atMost: Duration)(awaitable: Awaitable[T])(implicit execCtx: ExecutionContext = executionContext): T = {
+ blocking(awaitable, atMost)
+ }
+ }
+
+ /** Importing this object allows using some concurrency primitives
+ * on futures and promises that can yield nondeterministic programs.
+ *
+ * While program determinism is broken when using these primitives,
+ * some programs cannot be written without them (e.g. multiple client threads
+ * cannot send requests to a server thread through regular promises and futures).
+ */
+ object nondeterministic { }
+
+ /** A timeout exception.
+ *
+ * Futures are failed with a timeout exception when their timeout expires.
+ *
+ * Each timeout exception contains an origin future which originally timed out.
+ */
+ class FutureTimeoutException(origin: Future[_], message: String) extends TimeoutException(message) {
+ def this(origin: Future[_]) = this(origin, "Future timed out.")
+ }
+
+ final class DurationOps private[concurrent] (x: Int) {
+ // TODO ADD OTHERS
+ def ns = util.Duration.fromNanos(0)
+ }
+}
diff --git a/src/library/scala/concurrent/package.scala.disabled b/src/library/scala/concurrent/package.scala.disabled
deleted file mode 100644
index 42b4bf954c..0000000000
--- a/src/library/scala/concurrent/package.scala.disabled
+++ /dev/null
@@ -1,108 +0,0 @@
-/* __ *\
-** ________ ___ / / ___ Scala API **
-** / __/ __// _ | / / / _ | (c) 2003-2011, LAMP/EPFL **
-** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ **
-** /____/\___/_/ |_/____/_/ | | **
-** |/ **
-\* */
-
-
-
-package scala
-
-
-
-
-/** This package object contains primitives for parallel programming.
- */
-package object concurrent {
-
- /** Performs a call which can potentially block execution.
- *
- * Example:
- * {{{
- * val lock = new ReentrantLock
- *
- * // ... do something ...
- *
- * blocking {
- * if (!lock.hasLock) lock.lock()
- * }
- * }}}
- *
- * '''Note:''' calling methods that wait arbitrary amounts of time
- * (e.g. for I/O operations or locks) may severely decrease performance
- * or even result in deadlocks. This does not include waiting for
- * results of futures.
- *
- * @tparam T the result type of the blocking operation
- * @param body the blocking operation
- * @param runner the runner used for parallel computations
- * @return the result of the potentially blocking operation
- */
- def blocking[T](body: =>T)(implicit runner: TaskRunner): T = {
- null.asInstanceOf[T]
- }
-
- /** Invokes a computation asynchronously. Does not wait for the computation
- * to finish.
- *
- * @tparam U the result type of the operation
- * @param p the computation to be invoked asynchronously
- * @param runner the runner used for parallel computations
- */
- def spawn[U](p: =>U)(implicit runner: TaskRunner): Unit = {
- }
-
- /** Starts 2 parallel computations and returns once they are completed.
- *
- * $invokingPar
- *
- * @tparam T1 the type of the result of 1st the parallel computation
- * @tparam T2 the type of the result of 2nd the parallel computation
- * @param b1 the 1st computation to be invoked in parallel
- * @param b2 the 2nd computation to be invoked in parallel
- * @param runner the runner used for parallel computations
- * @return a tuple of results corresponding to parallel computations
- */
- def par[T1, T2](b1: =>T1)(b2: =>T2)(implicit runner: TaskRunner): (T1, T2) = {
- null
- }
-
- /** Starts 3 parallel computations and returns once they are completed.
- *
- * $invokingPar
- *
- * @tparam T1 the type of the result of 1st the parallel computation
- * @tparam T2 the type of the result of 2nd the parallel computation
- * @tparam T3 the type of the result of 3rd the parallel computation
- * @param b1 the 1st computation to be invoked in parallel
- * @param b2 the 2nd computation to be invoked in parallel
- * @param b3 the 3rd computation to be invoked in parallel
- * @param runner the runner used for parallel computations
- * @return a tuple of results corresponding to parallel computations
- */
- def par[T1, T2, T3](b1: =>T1)(b2: =>T2)(b3: =>T3)(implicit runner: TaskRunner): (T1, T2, T3) = {
- null
- }
-
- /** Starts 4 parallel computations and returns once they are completed.
- *
- * $invokingPar
- *
- * @tparam T1 the type of the result of 1st the parallel computation
- * @tparam T2 the type of the result of 2nd the parallel computation
- * @tparam T3 the type of the result of 3rd the parallel computation
- * @tparam T4 the type of the result of 4th the parallel computation
- * @param b1 the 1st computation to be invoked in parallel
- * @param b2 the 2nd computation to be invoked in parallel
- * @param b3 the 3rd computation to be invoked in parallel
- * @param b4 the 4th computation to be invoked in parallel
- * @param runner the runner used for parallel computations
- * @return a tuple of results corresponding to parallel computations
- */
- def par[T1, T2, T3, T4](b1: =>T1)(b2: =>T2)(b3: =>T3)(b4: =>T4)(implicit runner: TaskRunner): (T1, T2, T3, T4) = {
- null
- }
-
-}
diff --git a/src/library/scala/package.scala b/src/library/scala/package.scala
index 9425eba232..366af34ee9 100644
--- a/src/library/scala/package.scala
+++ b/src/library/scala/package.scala
@@ -27,12 +27,10 @@ package object scala {
type NoSuchElementException = java.util.NoSuchElementException
type NumberFormatException = java.lang.NumberFormatException
type AbstractMethodError = java.lang.AbstractMethodError
+ type InterruptedException = java.lang.InterruptedException
// A dummy used by the specialization annotation.
- // Normally it's bad juju to place objects inside package objects,
- // but there's no choice here as we'd have to be AnyRef's companion
- // and defined in the same file - except there is no such file.
- object AnyRef extends Specializable {
+ val AnyRef = new Specializable {
override def toString = "object AnyRef"
}
diff --git a/src/library/scala/sys/process/BasicIO.scala b/src/library/scala/sys/process/BasicIO.scala
index 5b7244e98e..edc60a1bb5 100644
--- a/src/library/scala/sys/process/BasicIO.scala
+++ b/src/library/scala/sys/process/BasicIO.scala
@@ -227,9 +227,10 @@ object BasicIO {
out.write(buffer, 0, byteCount)
// flush() will throw an exception once the process has terminated
val available = try { out.flush(); true } catch { case _: IOException => false }
- if (available) loop() else in.close()
- } else in.close()
+ if (available) loop()
+ }
}
loop()
+ in.close()
}
}
diff --git a/src/library/scala/util/Duration.scala b/src/library/scala/util/Duration.scala
new file mode 100644
index 0000000000..4c118f8b3b
--- /dev/null
+++ b/src/library/scala/util/Duration.scala
@@ -0,0 +1,485 @@
+/**
+ * Copyright (C) 2009-2011 Typesafe Inc. <http://www.typesafe.com>
+ */
+
+package scala.util
+
+import java.util.concurrent.TimeUnit
+import TimeUnit._
+import java.lang.{ Long ⇒ JLong, Double ⇒ JDouble }
+//import akka.actor.ActorSystem (commented methods)
+
+class TimerException(message: String) extends RuntimeException(message)
+
+/**
+ * Simple timer class.
+ * Usage:
+ * <pre>
+ * import akka.util.duration._
+ * import akka.util.Timer
+ *
+ * val timer = Timer(30.seconds)
+ * while (timer.isTicking) { ... }
+ * </pre>
+ */
+case class Timer(duration: Duration, throwExceptionOnTimeout: Boolean = false) {
+ val startTimeInMillis = System.currentTimeMillis
+ val timeoutInMillis = duration.toMillis
+
+ /**
+ * Returns true while the timer is ticking. After that it either throws and exception or
+ * returns false. Depending on if the 'throwExceptionOnTimeout' argument is true or false.
+ */
+ def isTicking: Boolean = {
+ if (!(timeoutInMillis > (System.currentTimeMillis - startTimeInMillis))) {
+ if (throwExceptionOnTimeout) throw new TimerException("Time out after " + duration)
+ else false
+ } else true
+ }
+}
+
+object Duration {
+ def apply(length: Long, unit: TimeUnit): Duration = new FiniteDuration(length, unit)
+ def apply(length: Double, unit: TimeUnit): Duration = fromNanos(unit.toNanos(1) * length)
+ def apply(length: Long, unit: String): Duration = new FiniteDuration(length, timeUnit(unit))
+
+ def fromNanos(nanos: Long): Duration = {
+ if (nanos % 86400000000000L == 0) {
+ Duration(nanos / 86400000000000L, DAYS)
+ } else if (nanos % 3600000000000L == 0) {
+ Duration(nanos / 3600000000000L, HOURS)
+ } else if (nanos % 60000000000L == 0) {
+ Duration(nanos / 60000000000L, MINUTES)
+ } else if (nanos % 1000000000L == 0) {
+ Duration(nanos / 1000000000L, SECONDS)
+ } else if (nanos % 1000000L == 0) {
+ Duration(nanos / 1000000L, MILLISECONDS)
+ } else if (nanos % 1000L == 0) {
+ Duration(nanos / 1000L, MICROSECONDS)
+ } else {
+ Duration(nanos, NANOSECONDS)
+ }
+ }
+
+ def fromNanos(nanos: Double): Duration = fromNanos((nanos + 0.5).asInstanceOf[Long])
+
+ /**
+ * Construct a Duration by parsing a String. In case of a format error, a
+ * RuntimeException is thrown. See `unapply(String)` for more information.
+ */
+ def apply(s: String): Duration = unapply(s) getOrElse sys.error("format error")
+
+ /**
+ * Deconstruct a Duration into length and unit if it is finite.
+ */
+ def unapply(d: Duration): Option[(Long, TimeUnit)] = {
+ if (d.finite_?) {
+ Some((d.length, d.unit))
+ } else {
+ None
+ }
+ }
+
+ private val RE = ("""^\s*(\d+(?:\.\d+)?)\s*""" + // length part
+ "(?:" + // units are distinguished in separate match groups
+ "(d|day|days)|" +
+ "(h|hour|hours)|" +
+ "(min|minute|minutes)|" +
+ "(s|sec|second|seconds)|" +
+ "(ms|milli|millis|millisecond|milliseconds)|" +
+ "(µs|micro|micros|microsecond|microseconds)|" +
+ "(ns|nano|nanos|nanosecond|nanoseconds)" +
+ """)\s*$""").r // close the non-capturing group
+ private val REinf = """^\s*Inf\s*$""".r
+ private val REminf = """^\s*(?:-\s*|Minus)Inf\s*""".r
+
+ /**
+ * Parse String, return None if no match. Format is `"<length><unit>"`, where
+ * whitespace is allowed before, between and after the parts. Infinities are
+ * designated by `"Inf"` and `"-Inf"` or `"MinusInf"`.
+ */
+ def unapply(s: String): Option[Duration] = s match {
+ case RE(length, d, h, m, s, ms, mus, ns) ⇒
+ if (d ne null) Some(Duration(JDouble.parseDouble(length), DAYS)) else if (h ne null) Some(Duration(JDouble.parseDouble(length), HOURS)) else if (m ne null) Some(Duration(JDouble.parseDouble(length), MINUTES)) else if (s ne null) Some(Duration(JDouble.parseDouble(length), SECONDS)) else if (ms ne null) Some(Duration(JDouble.parseDouble(length), MILLISECONDS)) else if (mus ne null) Some(Duration(JDouble.parseDouble(length), MICROSECONDS)) else if (ns ne null) Some(Duration(JDouble.parseDouble(length), NANOSECONDS)) else
+ sys.error("made some error in regex (should not be possible)")
+ case REinf() ⇒ Some(Inf)
+ case REminf() ⇒ Some(MinusInf)
+ case _ ⇒ None
+ }
+
+ /**
+ * Parse TimeUnit from string representation.
+ */
+ def timeUnit(unit: String) = unit.toLowerCase match {
+ case "d" | "day" | "days" ⇒ DAYS
+ case "h" | "hour" | "hours" ⇒ HOURS
+ case "min" | "minute" | "minutes" ⇒ MINUTES
+ case "s" | "sec" | "second" | "seconds" ⇒ SECONDS
+ case "ms" | "milli" | "millis" | "millisecond" | "milliseconds" ⇒ MILLISECONDS
+ case "µs" | "micro" | "micros" | "microsecond" | "microseconds" ⇒ MICROSECONDS
+ case "ns" | "nano" | "nanos" | "nanosecond" | "nanoseconds" ⇒ NANOSECONDS
+ }
+
+ val Zero: Duration = new FiniteDuration(0, NANOSECONDS)
+ val Undefined: Duration = new Duration with Infinite {
+ override def toString = "Duration.Undefined"
+ override def equals(other: Any) = other.asInstanceOf[AnyRef] eq this
+ override def +(other: Duration): Duration = throw new IllegalArgumentException("cannot add Undefined duration")
+ override def -(other: Duration): Duration = throw new IllegalArgumentException("cannot subtract Undefined duration")
+ override def *(factor: Double): Duration = throw new IllegalArgumentException("cannot multiply Undefined duration")
+ override def /(factor: Double): Duration = throw new IllegalArgumentException("cannot divide Undefined duration")
+ override def /(other: Duration): Double = throw new IllegalArgumentException("cannot divide Undefined duration")
+ def >(other: Duration) = throw new IllegalArgumentException("cannot compare Undefined duration")
+ def >=(other: Duration) = throw new IllegalArgumentException("cannot compare Undefined duration")
+ def <(other: Duration) = throw new IllegalArgumentException("cannot compare Undefined duration")
+ def <=(other: Duration) = throw new IllegalArgumentException("cannot compare Undefined duration")
+ def unary_- : Duration = throw new IllegalArgumentException("cannot negate Undefined duration")
+ }
+
+ trait Infinite {
+ this: Duration ⇒
+
+ override def equals(other: Any) = false
+
+ def +(other: Duration): Duration =
+ other match {
+ case _: this.type ⇒ this
+ case _: Infinite ⇒ throw new IllegalArgumentException("illegal addition of infinities")
+ case _ ⇒ this
+ }
+ def -(other: Duration): Duration =
+ other match {
+ case _: this.type ⇒ throw new IllegalArgumentException("illegal subtraction of infinities")
+ case _ ⇒ this
+ }
+ def *(factor: Double): Duration = this
+ def /(factor: Double): Duration = this
+ def /(other: Duration): Double =
+ other match {
+ case _: Infinite ⇒ throw new IllegalArgumentException("illegal division of infinities")
+ // maybe questionable but pragmatic: Inf / 0 => Inf
+ case x ⇒ Double.PositiveInfinity * (if ((this > Zero) ^ (other >= Zero)) -1 else 1)
+ }
+
+ def finite_? = false
+
+ def length: Long = throw new IllegalArgumentException("length not allowed on infinite Durations")
+ def unit: TimeUnit = throw new IllegalArgumentException("unit not allowed on infinite Durations")
+ def toNanos: Long = throw new IllegalArgumentException("toNanos not allowed on infinite Durations")
+ def toMicros: Long = throw new IllegalArgumentException("toMicros not allowed on infinite Durations")
+ def toMillis: Long = throw new IllegalArgumentException("toMillis not allowed on infinite Durations")
+ def toSeconds: Long = throw new IllegalArgumentException("toSeconds not allowed on infinite Durations")
+ def toMinutes: Long = throw new IllegalArgumentException("toMinutes not allowed on infinite Durations")
+ def toHours: Long = throw new IllegalArgumentException("toHours not allowed on infinite Durations")
+ def toDays: Long = throw new IllegalArgumentException("toDays not allowed on infinite Durations")
+ def toUnit(unit: TimeUnit): Double = throw new IllegalArgumentException("toUnit not allowed on infinite Durations")
+
+ def printHMS = toString
+ }
+
+ /**
+ * Infinite duration: greater than any other and not equal to any other,
+ * including itself.
+ */
+ val Inf: Duration = new Duration with Infinite {
+ override def toString = "Duration.Inf"
+ def >(other: Duration) = true
+ def >=(other: Duration) = true
+ def <(other: Duration) = false
+ def <=(other: Duration) = false
+ def unary_- : Duration = MinusInf
+ }
+
+ /**
+ * Infinite negative duration: lesser than any other and not equal to any other,
+ * including itself.
+ */
+ val MinusInf: Duration = new Duration with Infinite {
+ override def toString = "Duration.MinusInf"
+ def >(other: Duration) = false
+ def >=(other: Duration) = false
+ def <(other: Duration) = true
+ def <=(other: Duration) = true
+ def unary_- : Duration = Inf
+ }
+
+ // Java Factories
+ def create(length: Long, unit: TimeUnit): Duration = apply(length, unit)
+ def create(length: Double, unit: TimeUnit): Duration = apply(length, unit)
+ def create(length: Long, unit: String): Duration = apply(length, unit)
+ def parse(s: String): Duration = unapply(s).get
+}
+
+/**
+ * Utility for working with java.util.concurrent.TimeUnit durations.
+ *
+ * <p/>
+ * Examples of usage from Java:
+ * <pre>
+ * import akka.util.FiniteDuration;
+ * import java.util.concurrent.TimeUnit;
+ *
+ * Duration duration = new FiniteDuration(100, MILLISECONDS);
+ * Duration duration = new FiniteDuration(5, "seconds");
+ *
+ * duration.toNanos();
+ * </pre>
+ *
+ * <p/>
+ * Examples of usage from Scala:
+ * <pre>
+ * import akka.util.Duration
+ * import java.util.concurrent.TimeUnit
+ *
+ * val duration = Duration(100, MILLISECONDS)
+ * val duration = Duration(100, "millis")
+ *
+ * duration.toNanos
+ * duration < 1.second
+ * duration <= Duration.Inf
+ * </pre>
+ *
+ * <p/>
+ * Implicits are also provided for Int, Long and Double. Example usage:
+ * <pre>
+ * import akka.util.duration._
+ *
+ * val duration = 100 millis
+ * </pre>
+ *
+ * Extractors, parsing and arithmetic are also included:
+ * <pre>
+ * val d = Duration("1.2 µs")
+ * val Duration(length, unit) = 5 millis
+ * val d2 = d * 2.5
+ * val d3 = d2 + 1.millisecond
+ * </pre>
+ */
+abstract class Duration extends Serializable {
+ def length: Long
+ def unit: TimeUnit
+ def toNanos: Long
+ def toMicros: Long
+ def toMillis: Long
+ def toSeconds: Long
+ def toMinutes: Long
+ def toHours: Long
+ def toDays: Long
+ def toUnit(unit: TimeUnit): Double
+ def printHMS: String
+ def <(other: Duration): Boolean
+ def <=(other: Duration): Boolean
+ def >(other: Duration): Boolean
+ def >=(other: Duration): Boolean
+ def +(other: Duration): Duration
+ def -(other: Duration): Duration
+ def *(factor: Double): Duration
+ def /(factor: Double): Duration
+ def /(other: Duration): Double
+ def unary_- : Duration
+ def finite_? : Boolean
+// def dilated(implicit system: ActorSystem): Duration = this * system.settings.TestTimeFactor
+ def min(other: Duration): Duration = if (this < other) this else other
+ def max(other: Duration): Duration = if (this > other) this else other
+ def sleep(): Unit = Thread.sleep(toMillis)
+
+ // Java API
+ def lt(other: Duration) = this < other
+ def lteq(other: Duration) = this <= other
+ def gt(other: Duration) = this > other
+ def gteq(other: Duration) = this >= other
+ def plus(other: Duration) = this + other
+ def minus(other: Duration) = this - other
+ def mul(factor: Double) = this * factor
+ def div(factor: Double) = this / factor
+ def div(other: Duration) = this / other
+ def neg() = -this
+ def isFinite() = finite_?
+}
+
+class FiniteDuration(val length: Long, val unit: TimeUnit) extends Duration {
+ import Duration._
+
+ def this(length: Long, unit: String) = this(length, Duration.timeUnit(unit))
+
+ def toNanos = unit.toNanos(length)
+ def toMicros = unit.toMicros(length)
+ def toMillis = unit.toMillis(length)
+ def toSeconds = unit.toSeconds(length)
+ def toMinutes = unit.toMinutes(length)
+ def toHours = unit.toHours(length)
+ def toDays = unit.toDays(length)
+ def toUnit(u: TimeUnit) = long2double(toNanos) / NANOSECONDS.convert(1, u)
+
+ override def toString = this match {
+ case Duration(1, DAYS) ⇒ "1 day"
+ case Duration(x, DAYS) ⇒ x + " days"
+ case Duration(1, HOURS) ⇒ "1 hour"
+ case Duration(x, HOURS) ⇒ x + " hours"
+ case Duration(1, MINUTES) ⇒ "1 minute"
+ case Duration(x, MINUTES) ⇒ x + " minutes"
+ case Duration(1, SECONDS) ⇒ "1 second"
+ case Duration(x, SECONDS) ⇒ x + " seconds"
+ case Duration(1, MILLISECONDS) ⇒ "1 millisecond"
+ case Duration(x, MILLISECONDS) ⇒ x + " milliseconds"
+ case Duration(1, MICROSECONDS) ⇒ "1 microsecond"
+ case Duration(x, MICROSECONDS) ⇒ x + " microseconds"
+ case Duration(1, NANOSECONDS) ⇒ "1 nanosecond"
+ case Duration(x, NANOSECONDS) ⇒ x + " nanoseconds"
+ }
+
+ def printHMS = "%02d:%02d:%06.3f".format(toHours, toMinutes % 60, toMillis / 1000.0 % 60)
+
+ def <(other: Duration) = {
+ if (other.finite_?) {
+ toNanos < other.asInstanceOf[FiniteDuration].toNanos
+ } else {
+ other > this
+ }
+ }
+
+ def <=(other: Duration) = {
+ if (other.finite_?) {
+ toNanos <= other.asInstanceOf[FiniteDuration].toNanos
+ } else {
+ other >= this
+ }
+ }
+
+ def >(other: Duration) = {
+ if (other.finite_?) {
+ toNanos > other.asInstanceOf[FiniteDuration].toNanos
+ } else {
+ other < this
+ }
+ }
+
+ def >=(other: Duration) = {
+ if (other.finite_?) {
+ toNanos >= other.asInstanceOf[FiniteDuration].toNanos
+ } else {
+ other <= this
+ }
+ }
+
+ def +(other: Duration) = {
+ if (!other.finite_?) {
+ other
+ } else {
+ val nanos = toNanos + other.asInstanceOf[FiniteDuration].toNanos
+ fromNanos(nanos)
+ }
+ }
+
+ def -(other: Duration) = {
+ if (!other.finite_?) {
+ other
+ } else {
+ val nanos = toNanos - other.asInstanceOf[FiniteDuration].toNanos
+ fromNanos(nanos)
+ }
+ }
+
+ def *(factor: Double) = fromNanos(long2double(toNanos) * factor)
+
+ def /(factor: Double) = fromNanos(long2double(toNanos) / factor)
+
+ def /(other: Duration) = if (other.finite_?) long2double(toNanos) / other.toNanos else 0
+
+ def unary_- = Duration(-length, unit)
+
+ def finite_? = true
+
+ override def equals(other: Any) =
+ other.isInstanceOf[FiniteDuration] &&
+ toNanos == other.asInstanceOf[FiniteDuration].toNanos
+
+ override def hashCode = toNanos.asInstanceOf[Int]
+}
+
+class DurationInt(n: Int) {
+ def nanoseconds = Duration(n, NANOSECONDS)
+ def nanos = Duration(n, NANOSECONDS)
+ def nanosecond = Duration(n, NANOSECONDS)
+ def nano = Duration(n, NANOSECONDS)
+
+ def microseconds = Duration(n, MICROSECONDS)
+ def micros = Duration(n, MICROSECONDS)
+ def microsecond = Duration(n, MICROSECONDS)
+ def micro = Duration(n, MICROSECONDS)
+
+ def milliseconds = Duration(n, MILLISECONDS)
+ def millis = Duration(n, MILLISECONDS)
+ def millisecond = Duration(n, MILLISECONDS)
+ def milli = Duration(n, MILLISECONDS)
+
+ def seconds = Duration(n, SECONDS)
+ def second = Duration(n, SECONDS)
+
+ def minutes = Duration(n, MINUTES)
+ def minute = Duration(n, MINUTES)
+
+ def hours = Duration(n, HOURS)
+ def hour = Duration(n, HOURS)
+
+ def days = Duration(n, DAYS)
+ def day = Duration(n, DAYS)
+}
+
+class DurationLong(n: Long) {
+ def nanoseconds = Duration(n, NANOSECONDS)
+ def nanos = Duration(n, NANOSECONDS)
+ def nanosecond = Duration(n, NANOSECONDS)
+ def nano = Duration(n, NANOSECONDS)
+
+ def microseconds = Duration(n, MICROSECONDS)
+ def micros = Duration(n, MICROSECONDS)
+ def microsecond = Duration(n, MICROSECONDS)
+ def micro = Duration(n, MICROSECONDS)
+
+ def milliseconds = Duration(n, MILLISECONDS)
+ def millis = Duration(n, MILLISECONDS)
+ def millisecond = Duration(n, MILLISECONDS)
+ def milli = Duration(n, MILLISECONDS)
+
+ def seconds = Duration(n, SECONDS)
+ def second = Duration(n, SECONDS)
+
+ def minutes = Duration(n, MINUTES)
+ def minute = Duration(n, MINUTES)
+
+ def hours = Duration(n, HOURS)
+ def hour = Duration(n, HOURS)
+
+ def days = Duration(n, DAYS)
+ def day = Duration(n, DAYS)
+}
+
+class DurationDouble(d: Double) {
+ def nanoseconds = Duration(d, NANOSECONDS)
+ def nanos = Duration(d, NANOSECONDS)
+ def nanosecond = Duration(d, NANOSECONDS)
+ def nano = Duration(d, NANOSECONDS)
+
+ def microseconds = Duration(d, MICROSECONDS)
+ def micros = Duration(d, MICROSECONDS)
+ def microsecond = Duration(d, MICROSECONDS)
+ def micro = Duration(d, MICROSECONDS)
+
+ def milliseconds = Duration(d, MILLISECONDS)
+ def millis = Duration(d, MILLISECONDS)
+ def millisecond = Duration(d, MILLISECONDS)
+ def milli = Duration(d, MILLISECONDS)
+
+ def seconds = Duration(d, SECONDS)
+ def second = Duration(d, SECONDS)
+
+ def minutes = Duration(d, MINUTES)
+ def minute = Duration(d, MINUTES)
+
+ def hours = Duration(d, HOURS)
+ def hour = Duration(d, HOURS)
+
+ def days = Duration(d, DAYS)
+ def day = Duration(d, DAYS)
+}
diff --git a/src/library/scala/util/Timeout.scala b/src/library/scala/util/Timeout.scala
new file mode 100644
index 0000000000..0190675344
--- /dev/null
+++ b/src/library/scala/util/Timeout.scala
@@ -0,0 +1,33 @@
+/**
+ * Copyright (C) 2009-2011 Typesafe Inc. <http://www.typesafe.com>
+ */
+package scala.util
+
+import java.util.concurrent.TimeUnit
+
+case class Timeout(duration: Duration) {
+ def this(timeout: Long) = this(Duration(timeout, TimeUnit.MILLISECONDS))
+ def this(length: Long, unit: TimeUnit) = this(Duration(length, unit))
+}
+
+object Timeout {
+ /**
+ * A timeout with zero duration, will cause most requests to always timeout.
+ */
+ val zero = new Timeout(Duration.Zero)
+
+ /**
+ * A Timeout with infinite duration. Will never timeout. Use extreme caution with this
+ * as it may cause memory leaks, blocked threads, or may not even be supported by
+ * the receiver, which would result in an exception.
+ */
+ val never = new Timeout(Duration.Inf)
+
+ def apply(timeout: Long) = new Timeout(timeout)
+ def apply(length: Long, unit: TimeUnit) = new Timeout(length, unit)
+
+ implicit def durationToTimeout(duration: Duration) = new Timeout(duration)
+ implicit def intToTimeout(timeout: Int) = new Timeout(timeout)
+ implicit def longToTimeout(timeout: Long) = new Timeout(timeout)
+ //implicit def defaultTimeout(implicit system: ActorSystem) = system.settings.ActorTimeout (have to introduce this in ActorSystem)
+}
diff --git a/src/library/scala/util/Try.scala b/src/library/scala/util/Try.scala
new file mode 100644
index 0000000000..a05a75e0b7
--- /dev/null
+++ b/src/library/scala/util/Try.scala
@@ -0,0 +1,165 @@
+/* __ *\
+** ________ ___ / / ___ Scala API **
+** / __/ __// _ | / / / _ | (c) 2008-2011, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ **
+** /____/\___/_/ |_/____/_/ | | **
+** |/ **
+\* */
+
+package scala.util
+
+
+
+import collection.Seq
+
+
+
+/**
+ * The `Try` type represents a computation that may either result in an exception,
+ * or return a success value. It's analagous to the `Either` type.
+ */
+sealed abstract class Try[+T] {
+ /**
+ * Returns true if the `Try` is a `Failure`, false otherwise.
+ */
+ def isFailure: Boolean
+
+ /**
+ * Returns true if the `Try` is a `Success`, false otherwise.
+ */
+ def isSuccess: Boolean
+
+ /**
+ * Returns the value from this `Success` or the given argument if this is a `Failure`.
+ */
+ def getOrElse[U >: T](default: => U) = if (isSuccess) get else default
+
+ /**
+ * Returns the value from this `Success` or throws the exception if this is a `Failure`.
+ */
+ def get: T
+
+ /**
+ * Applies the given function f if this is a Result.
+ */
+ def foreach[U](f: T => U): Unit
+
+ /**
+ * Returns the given function applied to the value from this `Success` or returns this if this is a `Failure`.
+ */
+ def flatMap[U](f: T => Try[U]): Try[U]
+
+ /**
+ * Maps the given function to the value from this `Success` or returns this if this is a `Failure`.
+ */
+ def map[U](f: T => U): Try[U]
+
+ def collect[U](pf: PartialFunction[T, U]): Try[U]
+
+ def exists(p: T => Boolean): Boolean
+
+ /**
+ * Converts this to a `Failure` if the predicate is not satisfied.
+ */
+ def filter(p: T => Boolean): Try[T]
+
+ /**
+ * Converts this to a `Failure` if the predicate is not satisfied.
+ */
+ def filterNot(p: T => Boolean): Try[T] = filter(x => !p(x))
+
+ /**
+ * Calls the exceptionHandler with the exception if this is a `Failure`. This is like `flatMap` for the exception.
+ */
+ def rescue[U >: T](rescueException: PartialFunction[Throwable, Try[U]]): Try[U]
+
+ /**
+ * Calls the exceptionHandler with the exception if this is a `Failure`. This is like map for the exception.
+ */
+ def recover[U >: T](rescueException: PartialFunction[Throwable, U]): Try[U]
+
+ /**
+ * Returns `None` if this is a `Failure` or a `Some` containing the value if this is a `Success`.
+ */
+ def toOption = if (isSuccess) Some(get) else None
+
+ def toSeq = if (isSuccess) Seq(get) else Seq()
+
+ /**
+ * Returns the given function applied to the value from this Success or returns this if this is a `Failure`.
+ * Alias for `flatMap`.
+ */
+ def andThen[U](f: T => Try[U]): Try[U] = flatMap(f)
+
+ /**
+ * Transforms a nested `Try`, i.e., a `Try` of type `Try[Try[T]]`,
+ * into an un-nested `Try`, i.e., a `Try` of type `Try[T]`.
+ */
+ def flatten[U](implicit ev: T <:< Try[U]): Try[U]
+
+ def failed: Try[Throwable]
+}
+
+
+final case class Failure[+T](val exception: Throwable) extends Try[T] {
+ def isFailure = true
+ def isSuccess = false
+ def rescue[U >: T](rescueException: PartialFunction[Throwable, Try[U]]): Try[U] = {
+ try {
+ if (rescueException.isDefinedAt(exception)) rescueException(exception) else this
+ } catch {
+ case e2 => Failure(e2)
+ }
+ }
+ def get: T = throw exception
+ def flatMap[U](f: T => Try[U]): Try[U] = Failure[U](exception)
+ def flatten[U](implicit ev: T <:< Try[U]): Try[U] = Failure[U](exception)
+ def foreach[U](f: T => U): Unit = {}
+ def map[U](f: T => U): Try[U] = Failure[U](exception)
+ def collect[U](pf: PartialFunction[T, U]): Try[U] = Failure[U](exception)
+ def filter(p: T => Boolean): Try[T] = this
+ def recover[U >: T](rescueException: PartialFunction[Throwable, U]): Try[U] =
+ if (rescueException.isDefinedAt(exception)) {
+ Try(rescueException(exception))
+ } else {
+ this
+ }
+ def exists(p: T => Boolean): Boolean = false
+ def failed: Try[Throwable] = Success(exception)
+}
+
+
+final case class Success[+T](r: T) extends Try[T] {
+ def isFailure = false
+ def isSuccess = true
+ def rescue[U >: T](rescueException: PartialFunction[Throwable, Try[U]]): Try[U] = Success(r)
+ def get = r
+ def flatMap[U](f: T => Try[U]): Try[U] =
+ try f(r)
+ catch {
+ case e => Failure(e)
+ }
+ def flatten[U](implicit ev: T <:< Try[U]): Try[U] = r
+ def foreach[U](f: T => U): Unit = f(r)
+ def map[U](f: T => U): Try[U] = Try[U](f(r))
+ def collect[U](pf: PartialFunction[T, U]): Try[U] =
+ if (pf isDefinedAt r) Success(pf(r))
+ else Failure[U](new NoSuchElementException("Partial function not defined at " + r))
+ def filter(p: T => Boolean): Try[T] =
+ if (p(r)) this
+ else Failure(new NoSuchElementException("Predicate does not hold for " + r))
+ def recover[U >: T](rescueException: PartialFunction[Throwable, U]): Try[U] = this
+ def exists(p: T => Boolean): Boolean = p(r)
+ def failed: Try[Throwable] = Failure(new UnsupportedOperationException("Success.failed"))
+}
+
+
+object Try {
+
+ def apply[T](r: => T): Try[T] = {
+ try { Success(r) } catch {
+ case e => Failure(e)
+ }
+ }
+
+}
diff --git a/test/disabled/jvm/scala-concurrent-tck-akka.scala b/test/disabled/jvm/scala-concurrent-tck-akka.scala
new file mode 100644
index 0000000000..dfd906e59e
--- /dev/null
+++ b/test/disabled/jvm/scala-concurrent-tck-akka.scala
@@ -0,0 +1,391 @@
+
+
+import akka.dispatch.{
+ Future => future,
+ Promise => promise
+}
+import akka.dispatch.Await.{result => await}
+
+// Duration required for await
+import akka.util.Duration
+import java.util.concurrent.TimeUnit
+import TimeUnit._
+
+import scala.concurrent.{
+ TimeoutException,
+ SyncVar,
+ ExecutionException
+}
+//import scala.concurrent.future
+//import scala.concurrent.promise
+//import scala.concurrent.await
+
+
+
+trait TestBase {
+
+ implicit val disp = akka.actor.ActorSystem().dispatcher
+
+ def once(body: (() => Unit) => Unit) {
+ val sv = new SyncVar[Boolean]
+ body(() => sv put true)
+ sv.take()
+ }
+
+}
+
+
+trait FutureCallbacks extends TestBase {
+
+ def testOnSuccess(): Unit = once {
+ done =>
+ var x = 0
+ val f = future {
+ x = 1
+ }
+ f onSuccess { case any =>
+ done()
+ assert(x == 1)
+ }
+ }
+
+ def testOnSuccessWhenCompleted(): Unit = once {
+ done =>
+ var x = 0
+ val f = future {
+ x = 1
+ }
+ f onSuccess { case any =>
+ assert(x == 1)
+ x = 2
+ f onSuccess { case any =>
+ assert(x == 2)
+ done()
+ }
+ }
+ }
+
+ def testOnSuccessWhenFailed(): Unit = once {
+ done =>
+ val f = future[Unit] {
+ done()
+ throw new Exception
+ }
+ f onSuccess { case any =>
+ assert(false)
+ }
+ }
+
+ def testOnFailure(): Unit = once {
+ done =>
+ var x = 0
+ val f = future[Unit] {
+ x = 1
+ throw new Exception
+ }
+ f onSuccess { case any =>
+ done()
+ assert(false)
+ }
+ f onFailure {
+ case _ =>
+ done()
+ assert(x == 1)
+ }
+ }
+
+ def testOnFailureWhenSpecialThrowable(num: Int, cause: Throwable): Unit = once {
+ done =>
+ val f = future[Unit] {
+ throw cause
+ }
+ f onSuccess { case any =>
+ done()
+ assert(false)
+ }
+ f onFailure {
+ case e: ExecutionException if (e.getCause == cause) =>
+ done()
+ case _ =>
+ done()
+ assert(false)
+ }
+ }
+
+ def testOnFailureWhenTimeoutException(): Unit = once {
+ done =>
+ val f = future[Unit] {
+ throw new TimeoutException()
+ }
+ f onSuccess { case any =>
+ done()
+ assert(false)
+ }
+ f onFailure {
+ case e: TimeoutException =>
+ done()
+ case other =>
+ done()
+ assert(false)
+ }
+ }
+
+ testOnSuccess()
+ testOnSuccessWhenCompleted()
+ testOnSuccessWhenFailed()
+ testOnFailure()
+// testOnFailureWhenSpecialThrowable(5, new Error)
+// testOnFailureWhenSpecialThrowable(6, new scala.util.control.ControlThrowable { })
+// testOnFailureWhenSpecialThrowable(7, new InterruptedException)
+// testOnFailureWhenTimeoutException()
+
+}
+
+
+trait FutureCombinators extends TestBase {
+
+ // map: stub
+ def testMapSuccess(): Unit = once {
+ done =>
+ done()
+ }
+
+ def testMapFailure(): Unit = once {
+ done =>
+ done()
+ }
+
+ // flatMap: stub
+ def testFlatMapSuccess(): Unit = once {
+ done =>
+ done()
+ }
+
+ def testFlatMapFailure(): Unit = once {
+ done =>
+ done()
+ }
+
+ // filter: stub
+ def testFilterSuccess(): Unit = once {
+ done =>
+ done()
+ }
+
+ def testFilterFailure(): Unit = once {
+ done =>
+ done()
+ }
+
+ // foreach: stub
+ def testForeachSuccess(): Unit = once {
+ done =>
+ done()
+ }
+
+ def testForeachFailure(): Unit = once {
+ done =>
+ done()
+ }
+
+ def testRecoverSuccess(): Unit = once {
+ done =>
+ val cause = new RuntimeException
+ val f = future {
+ throw cause
+ } recover {
+ case re: RuntimeException =>
+ "recovered"
+ } onSuccess { case x =>
+ done()
+ assert(x == "recovered")
+ } onFailure { case any =>
+ done()
+ assert(false)
+ }
+ }
+
+ def testRecoverFailure(): Unit = once {
+ done =>
+ val cause = new RuntimeException
+ val f = future {
+ throw cause
+ } recover {
+ case te: TimeoutException => "timeout"
+ } onSuccess { case x =>
+ done()
+ assert(false)
+ } onFailure { case any =>
+ done()
+ assert(any == cause)
+ }
+ }
+
+ testMapSuccess()
+ testMapFailure()
+ testFlatMapSuccess()
+ testFlatMapFailure()
+ testFilterSuccess()
+ testFilterFailure()
+ testForeachSuccess()
+ testForeachFailure()
+ testRecoverSuccess()
+ testRecoverFailure()
+
+}
+
+/*
+trait FutureProjections extends TestBase {
+
+ def testFailedFailureOnComplete(): Unit = once {
+ done =>
+ val cause = new RuntimeException
+ val f = future {
+ throw cause
+ }
+ f.failed onComplete {
+ case Right(t) =>
+ assert(t == cause)
+ done()
+ case Left(t) =>
+ assert(false)
+ }
+ }
+
+ def testFailedFailureOnSuccess(): Unit = once {
+ done =>
+ val cause = new RuntimeException
+ val f = future {
+ throw cause
+ }
+ f.failed onSuccess {
+ t =>
+ assert(t == cause)
+ done()
+ }
+ }
+
+ def testFailedSuccessOnComplete(): Unit = once {
+ done =>
+ val f = future { 0 }
+ f.failed onComplete {
+ case Right(t) =>
+ assert(false)
+ case Left(t) =>
+ assert(t.isInstanceOf[NoSuchElementException])
+ done()
+ }
+ }
+
+ def testFailedSuccessOnFailure(): Unit = once {
+ done =>
+ val f = future { 0 }
+ f.failed onFailure {
+ case nsee: NoSuchElementException =>
+ done()
+ }
+ }
+
+ def testFailedFailureAwait(): Unit = once {
+ done =>
+ val cause = new RuntimeException
+ val f = future {
+ throw cause
+ }
+ assert(await(0, f.failed) == cause)
+ done()
+ }
+
+ def testFailedSuccessAwait(): Unit = once {
+ done =>
+ val f = future { 0 }
+ try {
+ println(await(0, f.failed))
+ assert(false)
+ } catch {
+ case nsee: NoSuchElementException => done()
+ }
+ }
+
+ testFailedFailureOnComplete()
+ testFailedFailureOnSuccess()
+ testFailedSuccessOnComplete()
+ testFailedSuccessOnFailure()
+ testFailedFailureAwait()
+ //testFailedSuccessAwait()
+
+}
+*/
+
+trait Blocking extends TestBase {
+
+ def testAwaitSuccess(): Unit = once {
+ done =>
+ val f = future { 0 }
+ await(f, Duration(500, "ms"))
+ done()
+ }
+
+ def testAwaitFailure(): Unit = once {
+ done =>
+ val cause = new RuntimeException
+ val f = future {
+ throw cause
+ }
+ try {
+ await(f, Duration(500, "ms"))
+ assert(false)
+ } catch {
+ case t =>
+ assert(t == cause)
+ done()
+ }
+ }
+
+ testAwaitSuccess()
+ testAwaitFailure()
+
+}
+
+/*
+trait Promises extends TestBase {
+
+ def testSuccess(): Unit = once {
+ done =>
+ val p = promise[Int]()
+ val f = p.future
+
+ f.onSuccess { x =>
+ done()
+ assert(x == 5)
+ } onFailure { case any =>
+ done()
+ assert(false)
+ }
+
+ p.success(5)
+ }
+
+ testSuccess()
+
+}
+*/
+
+trait Exceptions extends TestBase {
+
+}
+
+
+object Test
+extends App
+with FutureCallbacks
+with FutureCombinators
+/*with FutureProjections*/
+/*with Promises*/
+with Blocking
+with Exceptions
+{
+ System.exit(0)
+}
+
+
diff --git a/test/files/buildmanager/t2652/t2652.check b/test/files/buildmanager/t2652/t2652.check
index b84c80205e..071281c6ff 100644
--- a/test/files/buildmanager/t2652/t2652.check
+++ b/test/files/buildmanager/t2652/t2652.check
@@ -3,7 +3,7 @@ compiling Set(A.scala, B.scala)
Changes: Map()
builder > A.scala
compiling Set(A.scala)
-Changes: Map(class A -> List(Added(Definition(A.x$mBc$sp)), Added(Definition(A.x$mCc$sp)), Added(Definition(A.x$mDc$sp)), Added(Definition(A.x$mFc$sp)), Added(Definition(A.x$mIc$sp)), Added(Definition(A.x$mJc$sp)), Added(Definition(A.x$mLc$sp)), Added(Definition(A.x$mSc$sp)), Added(Definition(A.x$mVc$sp)), Added(Definition(A.x$mZc$sp)), Changed(Definition(A.x))[method x changed from [T](t: T)T to [T](t: T)T flags: <method> <triedcooking>]))
+Changes: Map(class A -> List(Added(Definition(A.x$mBc$sp)), Added(Definition(A.x$mCc$sp)), Added(Definition(A.x$mDc$sp)), Added(Definition(A.x$mFc$sp)), Added(Definition(A.x$mIc$sp)), Added(Definition(A.x$mJc$sp)), Added(Definition(A.x$mSc$sp)), Added(Definition(A.x$mVc$sp)), Added(Definition(A.x$mZc$sp)), Changed(Definition(A.x))[method x changed from [T](t: T)T to [T](t: T)T flags: <method> <triedcooking>]))
invalidate B.scala because it references changed definition [Changed(Definition(A.x))[method x changed from [T](t: T)T to [T](t: T)T flags: <method> <triedcooking>]]
compiling Set(B.scala)
Changes: Map(object B -> List())
diff --git a/test/files/jvm/concurrent-future.check b/test/files/jvm/concurrent-future.check
new file mode 100644
index 0000000000..c55e824818
--- /dev/null
+++ b/test/files/jvm/concurrent-future.check
@@ -0,0 +1,16 @@
+test1: hai world
+test1: kthxbye
+test2: hai world
+test2: awsum thx
+test2: kthxbye
+test3: hai world
+test4: hai world
+test4: kthxbye
+test5: hai world
+test5: kthxbye
+test6: hai world
+test6: kthxbye
+test7: hai world
+test7: kthxbye
+test8: hai world
+test8: im in yr loop
diff --git a/test/files/jvm/concurrent-future.scala b/test/files/jvm/concurrent-future.scala
new file mode 100644
index 0000000000..b44d054219
--- /dev/null
+++ b/test/files/jvm/concurrent-future.scala
@@ -0,0 +1,122 @@
+
+
+
+import scala.concurrent._
+
+
+
+object Test extends App {
+
+ def once(body: (() => Unit) => Unit) {
+ val sv = new SyncVar[Boolean]
+ body(() => sv put true)
+ sv.take()
+ }
+
+ def output(num: Int, msg: String) {
+ println("test" + num + ": " + msg)
+ }
+
+ def testOnSuccess(): Unit = once {
+ done =>
+ val f = future {
+ output(1, "hai world")
+ }
+ f onSuccess { case _ =>
+ output(1, "kthxbye")
+ done()
+ }
+ }
+
+ def testOnSuccessWhenCompleted(): Unit = once {
+ done =>
+ val f = future {
+ output(2, "hai world")
+ }
+ f onSuccess { case _ =>
+ output(2, "awsum thx")
+ f onSuccess { case _ =>
+ output(2, "kthxbye")
+ done()
+ }
+ }
+ }
+
+ def testOnSuccessWhenFailed(): Unit = once {
+ done =>
+ val f = future[Unit] {
+ output(3, "hai world")
+ done()
+ throw new Exception
+ }
+ f onSuccess { case _ =>
+ output(3, "onoes")
+ }
+ }
+
+ def testOnFailure(): Unit = once {
+ done =>
+ val f = future[Unit] {
+ output(4, "hai world")
+ throw new Exception
+ }
+ f onSuccess { case _ =>
+ output(4, "onoes")
+ done()
+ }
+ f onFailure { case _ =>
+ output(4, "kthxbye")
+ done()
+ }
+ }
+
+ def testOnFailureWhenSpecialThrowable(num: Int, cause: Throwable): Unit = once {
+ done =>
+ val f = future[Unit] {
+ output(num, "hai world")
+ throw cause
+ }
+ f onSuccess { case _ =>
+ output(num, "onoes")
+ done()
+ }
+ f onFailure {
+ case e: ExecutionException if (e.getCause == cause) =>
+ output(num, "kthxbye")
+ done()
+ case _ =>
+ output(num, "onoes")
+ done()
+ }
+ }
+
+ def testOnFailureWhenFutureTimeoutException(): Unit = once {
+ done =>
+ val f = future[Unit] {
+ output(8, "hai world")
+ throw new FutureTimeoutException(null)
+ }
+ f onSuccess { case _ =>
+ output(8, "onoes")
+ done()
+ }
+ f onFailure {
+ case e: FutureTimeoutException =>
+ output(8, "im in yr loop")
+ done()
+ case other =>
+ output(8, "onoes: " + other)
+ done()
+ }
+ }
+
+ testOnSuccess()
+ testOnSuccessWhenCompleted()
+ testOnSuccessWhenFailed()
+ testOnFailure()
+ testOnFailureWhenSpecialThrowable(5, new Error)
+ testOnFailureWhenSpecialThrowable(6, new scala.util.control.ControlThrowable { })
+ testOnFailureWhenSpecialThrowable(7, new InterruptedException)
+ testOnFailureWhenFutureTimeoutException()
+
+}
diff --git a/test/files/jvm/scala-concurrent-tck.scala b/test/files/jvm/scala-concurrent-tck.scala
new file mode 100644
index 0000000000..244ff02da7
--- /dev/null
+++ b/test/files/jvm/scala-concurrent-tck.scala
@@ -0,0 +1,413 @@
+
+
+
+import scala.concurrent.{
+ Future,
+ Promise,
+ TimeoutException,
+ SyncVar,
+ ExecutionException
+}
+import scala.concurrent.future
+import scala.concurrent.promise
+import scala.concurrent.blocking
+import scala.util.{ Try, Success, Failure }
+
+import scala.util.Duration
+
+
+trait TestBase {
+
+ def once(body: (() => Unit) => Unit) {
+ val sv = new SyncVar[Boolean]
+ body(() => sv put true)
+ sv.take()
+ }
+
+ // def assert(cond: => Boolean) {
+ // try {
+ // Predef.assert(cond)
+ // } catch {
+ // case e => e.printStackTrace()
+ // }
+ // }
+
+}
+
+
+trait FutureCallbacks extends TestBase {
+
+ def testOnSuccess(): Unit = once {
+ done =>
+ var x = 0
+ val f = future {
+ x = 1
+ }
+ f onSuccess {
+ case _ =>
+ done()
+ assert(x == 1)
+ }
+ }
+
+ def testOnSuccessWhenCompleted(): Unit = once {
+ done =>
+ var x = 0
+ val f = future {
+ x = 1
+ }
+ f onSuccess {
+ case _ =>
+ assert(x == 1)
+ x = 2
+ f onSuccess {
+ case _ =>
+ assert(x == 2)
+ done()
+ }
+ }
+ }
+
+ def testOnSuccessWhenFailed(): Unit = once {
+ done =>
+ val f = future[Unit] {
+ done()
+ throw new Exception
+ }
+ f onSuccess {
+ case _ => assert(false)
+ }
+ }
+
+ def testOnFailure(): Unit = once {
+ done =>
+ var x = 0
+ val f = future[Unit] {
+ x = 1
+ throw new Exception
+ }
+ f onSuccess {
+ case _ =>
+ done()
+ assert(false)
+ }
+ f onFailure {
+ case _ =>
+ done()
+ assert(x == 1)
+ }
+ }
+
+ def testOnFailureWhenSpecialThrowable(num: Int, cause: Throwable): Unit = once {
+ done =>
+ val f = future[Unit] {
+ throw cause
+ }
+ f onSuccess {
+ case _ =>
+ done()
+ assert(false)
+ }
+ f onFailure {
+ case e: ExecutionException if (e.getCause == cause) =>
+ done()
+ case _ =>
+ done()
+ assert(false)
+ }
+ }
+
+ def testOnFailureWhenTimeoutException(): Unit = once {
+ done =>
+ val f = future[Unit] {
+ throw new TimeoutException()
+ }
+ f onSuccess {
+ case _ =>
+ done()
+ assert(false)
+ }
+ f onFailure {
+ case e: TimeoutException =>
+ done()
+ case other =>
+ done()
+ assert(false)
+ }
+ }
+
+ testOnSuccess()
+ testOnSuccessWhenCompleted()
+ testOnSuccessWhenFailed()
+ testOnFailure()
+ testOnFailureWhenSpecialThrowable(5, new Error)
+ testOnFailureWhenSpecialThrowable(6, new scala.util.control.ControlThrowable { })
+ testOnFailureWhenSpecialThrowable(7, new InterruptedException)
+ testOnFailureWhenTimeoutException()
+
+}
+
+
+trait FutureCombinators extends TestBase {
+
+ // map: stub
+ def testMapSuccess(): Unit = once {
+ done =>
+ done()
+ }
+
+ def testMapFailure(): Unit = once {
+ done =>
+ done()
+ }
+
+ // flatMap: stub
+ def testFlatMapSuccess(): Unit = once {
+ done =>
+ done()
+ }
+
+ def testFlatMapFailure(): Unit = once {
+ done =>
+ done()
+ }
+
+ // filter: stub
+ def testFilterSuccess(): Unit = once {
+ done =>
+ done()
+ }
+
+ def testFilterFailure(): Unit = once {
+ done =>
+ done()
+ }
+
+ // collect: stub
+ def testCollectSuccess(): Unit = once {
+ done =>
+ done()
+ }
+
+ def testCollectFailure(): Unit = once {
+ done =>
+ done()
+ }
+
+ // foreach: stub
+ def testForeachSuccess(): Unit = once {
+ done =>
+ done()
+ }
+
+ def testForeachFailure(): Unit = once {
+ done =>
+ done()
+ }
+
+ def testRecoverSuccess(): Unit = once {
+ done =>
+ val cause = new RuntimeException
+ val f = future {
+ throw cause
+ } recover {
+ case re: RuntimeException =>
+ "recovered"
+ } onSuccess {
+ case x =>
+ done()
+ assert(x == "recovered")
+ } onFailure { case any =>
+ done()
+ assert(false)
+ }
+ }
+
+ def testRecoverFailure(): Unit = once {
+ done =>
+ val cause = new RuntimeException
+ val f = future {
+ throw cause
+ } recover {
+ case te: TimeoutException => "timeout"
+ } onSuccess {
+ case x =>
+ done()
+ assert(false)
+ } onFailure { case any =>
+ done()
+ assert(any == cause)
+ }
+ }
+
+ testMapSuccess()
+ testMapFailure()
+ testFlatMapSuccess()
+ testFlatMapFailure()
+ testFilterSuccess()
+ testFilterFailure()
+ testCollectSuccess()
+ testCollectFailure()
+ testForeachSuccess()
+ testForeachFailure()
+ testRecoverSuccess()
+ testRecoverFailure()
+
+}
+
+
+trait FutureProjections extends TestBase {
+
+ def testFailedFailureOnComplete(): Unit = once {
+ done =>
+ val cause = new RuntimeException
+ val f = future {
+ throw cause
+ }
+ f.failed onComplete {
+ case Success(t) =>
+ assert(t == cause)
+ done()
+ case Failure(t) =>
+ assert(false)
+ }
+ }
+
+ def testFailedFailureOnSuccess(): Unit = once {
+ done =>
+ val cause = new RuntimeException
+ val f = future {
+ throw cause
+ }
+ f.failed onSuccess {
+ case t =>
+ assert(t == cause)
+ done()
+ }
+ }
+
+ def testFailedSuccessOnComplete(): Unit = once {
+ done =>
+ val f = future { 0 }
+ f.failed onComplete {
+ case Success(t) =>
+ assert(false)
+ case Failure(t) =>
+ assert(t.isInstanceOf[NoSuchElementException])
+ done()
+ }
+ }
+
+ def testFailedSuccessOnFailure(): Unit = once {
+ done =>
+ val f = future { 0 }
+ f.failed onFailure {
+ case nsee: NoSuchElementException =>
+ done()
+ }
+ }
+
+ def testFailedFailureAwait(): Unit = once {
+ done =>
+ val cause = new RuntimeException
+ val f = future {
+ throw cause
+ }
+ assert(blocking(f.failed, Duration(500, "ms")) == cause)
+ done()
+ }
+
+ def testFailedSuccessAwait(): Unit = once {
+ done =>
+ val f = future { 0 }
+ try {
+ blocking(f.failed, Duration(0, "ms"))
+ assert(false)
+ } catch {
+ case nsee: NoSuchElementException => done()
+ }
+ }
+
+ testFailedFailureOnComplete()
+ testFailedFailureOnSuccess()
+ testFailedSuccessOnComplete()
+ testFailedSuccessOnFailure()
+ testFailedFailureAwait()
+ testFailedSuccessAwait()
+
+}
+
+
+trait Blocking extends TestBase {
+
+ def testAwaitSuccess(): Unit = once {
+ done =>
+ val f = future { 0 }
+ blocking(f, Duration(500, "ms"))
+ done()
+ }
+
+ def testAwaitFailure(): Unit = once {
+ done =>
+ val cause = new RuntimeException
+ val f = future {
+ throw cause
+ }
+ try {
+ blocking(f, Duration(500, "ms"))
+ assert(false)
+ } catch {
+ case t =>
+ assert(t == cause)
+ done()
+ }
+ }
+
+ testAwaitSuccess()
+ testAwaitFailure()
+
+}
+
+
+trait Promises extends TestBase {
+
+ def testSuccess(): Unit = once {
+ done =>
+ val p = promise[Int]()
+ val f = p.future
+
+ f.onSuccess {
+ case x =>
+ done()
+ assert(x == 5)
+ } onFailure {
+ case any =>
+ done()
+ assert(false)
+ }
+
+ p.success(5)
+ }
+
+ testSuccess()
+
+}
+
+
+trait Exceptions extends TestBase {
+
+}
+
+
+object Test
+extends App
+with FutureCallbacks
+with FutureCombinators
+with FutureProjections
+with Promises
+with Exceptions
+{
+ System.exit(0)
+}
+
+
diff --git a/test/files/neg/macro-argtype-mismatch.check b/test/files/neg/macro-argtype-mismatch.check
new file mode 100644
index 0000000000..dd867be804
--- /dev/null
+++ b/test/files/neg/macro-argtype-mismatch.check
@@ -0,0 +1,6 @@
+Test_2.scala:3: error: type mismatch;
+ found : String("2")
+ required: Int
+ foo("2")
+ ^
+one error found
diff --git a/test/files/neg/macro-argtype-mismatch.flags b/test/files/neg/macro-argtype-mismatch.flags
new file mode 100644
index 0000000000..7fea2ff901
--- /dev/null
+++ b/test/files/neg/macro-argtype-mismatch.flags
@@ -0,0 +1 @@
+-Xmacros \ No newline at end of file
diff --git a/test/files/neg/macro-argtype-mismatch/Macros_1.scala b/test/files/neg/macro-argtype-mismatch/Macros_1.scala
new file mode 100644
index 0000000000..4b5f98ba37
--- /dev/null
+++ b/test/files/neg/macro-argtype-mismatch/Macros_1.scala
@@ -0,0 +1,3 @@
+object Macros {
+ def macro foo(x: Int) = x
+} \ No newline at end of file
diff --git a/test/files/neg/macro-argtype-mismatch/Test_2.scala b/test/files/neg/macro-argtype-mismatch/Test_2.scala
new file mode 100644
index 0000000000..18feb69425
--- /dev/null
+++ b/test/files/neg/macro-argtype-mismatch/Test_2.scala
@@ -0,0 +1,4 @@
+object Test extends App {
+ import Macros._
+ foo("2")
+} \ No newline at end of file
diff --git a/test/files/neg/macro-noexpand.check b/test/files/neg/macro-noexpand.check
new file mode 100644
index 0000000000..c15d54bb32
--- /dev/null
+++ b/test/files/neg/macro-noexpand.check
@@ -0,0 +1,4 @@
+Test_2.scala:3: error: not found: value x
+ foo(x)
+ ^
+one error found
diff --git a/test/files/neg/macro-noexpand.flags b/test/files/neg/macro-noexpand.flags
new file mode 100644
index 0000000000..7fea2ff901
--- /dev/null
+++ b/test/files/neg/macro-noexpand.flags
@@ -0,0 +1 @@
+-Xmacros \ No newline at end of file
diff --git a/test/files/neg/macro-noexpand/Macros_1.scala b/test/files/neg/macro-noexpand/Macros_1.scala
new file mode 100644
index 0000000000..7a6aadf6a1
--- /dev/null
+++ b/test/files/neg/macro-noexpand/Macros_1.scala
@@ -0,0 +1,3 @@
+object Macros {
+ def macro foo(x: Any) = ???
+} \ No newline at end of file
diff --git a/test/files/neg/macro-noexpand/Test_2.scala b/test/files/neg/macro-noexpand/Test_2.scala
new file mode 100644
index 0000000000..0bed592883
--- /dev/null
+++ b/test/files/neg/macro-noexpand/Test_2.scala
@@ -0,0 +1,4 @@
+object Test extends App {
+ import Macros._
+ foo(x)
+} \ No newline at end of file
diff --git a/test/files/neg/macro-noncompilertree.check b/test/files/neg/macro-noncompilertree.check
new file mode 100644
index 0000000000..616765a39e
--- /dev/null
+++ b/test/files/neg/macro-noncompilertree.check
@@ -0,0 +1,6 @@
+Macros_1.scala:2: error: type mismatch;
+ found : reflect.mirror.Literal
+ required: _context.Tree
+ def macro foo = scala.reflect.mirror.Literal(scala.reflect.mirror.Constant(2))
+ ^
+one error found
diff --git a/test/files/neg/macro-noncompilertree.flags b/test/files/neg/macro-noncompilertree.flags
new file mode 100644
index 0000000000..7fea2ff901
--- /dev/null
+++ b/test/files/neg/macro-noncompilertree.flags
@@ -0,0 +1 @@
+-Xmacros \ No newline at end of file
diff --git a/test/files/neg/macro-noncompilertree/Macros_1.scala b/test/files/neg/macro-noncompilertree/Macros_1.scala
new file mode 100644
index 0000000000..eb1253e5e9
--- /dev/null
+++ b/test/files/neg/macro-noncompilertree/Macros_1.scala
@@ -0,0 +1,3 @@
+object Macros {
+ def macro foo = scala.reflect.mirror.Literal(scala.reflect.mirror.Constant(2))
+} \ No newline at end of file
diff --git a/test/files/neg/macro-nontree.check b/test/files/neg/macro-nontree.check
new file mode 100644
index 0000000000..a1c7139580
--- /dev/null
+++ b/test/files/neg/macro-nontree.check
@@ -0,0 +1,6 @@
+Macros_1.scala:2: error: type mismatch;
+ found : Int(2)
+ required: _context.Tree
+ def macro foo = 2
+ ^
+one error found
diff --git a/test/files/neg/macro-nontree.flags b/test/files/neg/macro-nontree.flags
new file mode 100644
index 0000000000..7fea2ff901
--- /dev/null
+++ b/test/files/neg/macro-nontree.flags
@@ -0,0 +1 @@
+-Xmacros \ No newline at end of file
diff --git a/test/files/neg/macro-nontree/Macros_1.scala b/test/files/neg/macro-nontree/Macros_1.scala
new file mode 100644
index 0000000000..2433974a85
--- /dev/null
+++ b/test/files/neg/macro-nontree/Macros_1.scala
@@ -0,0 +1,3 @@
+object Macros {
+ def macro foo = 2
+} \ No newline at end of file
diff --git a/test/disabled/neg/t5452.check b/test/files/neg/t5452.check
index 2f35a45509..2f35a45509 100644
--- a/test/disabled/neg/t5452.check
+++ b/test/files/neg/t5452.check
diff --git a/test/disabled/neg/t5452.scala b/test/files/neg/t5452.scala
index 1032db7a4b..1032db7a4b 100644
--- a/test/disabled/neg/t5452.scala
+++ b/test/files/neg/t5452.scala
diff --git a/test/files/pos/Transactions.scala b/test/files/pos/Transactions.scala
index 9b4388300b..525eff7514 100644
--- a/test/files/pos/Transactions.scala
+++ b/test/files/pos/Transactions.scala
@@ -1,4 +1,4 @@
-package scala.concurrent
+package scala.concurrent1
class AbortException extends RuntimeException
diff --git a/test/disabled/pos/spurious-overload.scala b/test/files/pos/spurious-overload.scala
index 9767a44eee..9767a44eee 100644
--- a/test/disabled/pos/spurious-overload.scala
+++ b/test/files/pos/spurious-overload.scala
diff --git a/test/files/run/macro-basic.check b/test/files/run/macro-basic.check
new file mode 100644
index 0000000000..d434014897
--- /dev/null
+++ b/test/files/run/macro-basic.check
@@ -0,0 +1 @@
+10
diff --git a/test/files/run/macro-basic.flags b/test/files/run/macro-basic.flags
new file mode 100644
index 0000000000..06a7b31f11
--- /dev/null
+++ b/test/files/run/macro-basic.flags
@@ -0,0 +1 @@
+-Xmacros
diff --git a/test/files/run/macro-basic/Macros_1.scala b/test/files/run/macro-basic/Macros_1.scala
new file mode 100644
index 0000000000..c2ea183abe
--- /dev/null
+++ b/test/files/run/macro-basic/Macros_1.scala
@@ -0,0 +1,10 @@
+object Macros {
+ object Shmacros {
+ def macro foo(x: Int): Int = x
+ }
+ def macro bar(x: Int): Int = x
+}
+
+class Macros {
+ def macro quux(x: Int): Int = x
+} \ No newline at end of file
diff --git a/test/files/run/macro-basic/Test_2.scala b/test/files/run/macro-basic/Test_2.scala
new file mode 100644
index 0000000000..e9a10e20c9
--- /dev/null
+++ b/test/files/run/macro-basic/Test_2.scala
@@ -0,0 +1,4 @@
+object Test extends App {
+ import Macros.Shmacros._
+ println(foo(2) + Macros.bar(2) * new Macros().quux(4))
+} \ No newline at end of file
diff --git a/test/files/run/macro-repl-basic.check b/test/files/run/macro-repl-basic.check
new file mode 100644
index 0000000000..f8f0d3ad29
--- /dev/null
+++ b/test/files/run/macro-repl-basic.check
@@ -0,0 +1,25 @@
+Type in expressions to have them evaluated.
+Type :help for more information.
+
+scala>
+
+scala> object Macros {
+ object Shmacros {
+ def macro foo(x: Int): Int = x
+ }
+ def macro bar(x: Int): Int = x
+}; class Macros {
+ def macro quux(x: Int): Int = x
+}
+defined module Macros
+defined class Macros
+
+scala>
+
+scala> import Macros.Shmacros._
+import Macros.Shmacros._
+
+scala> println(foo(2) + Macros.bar(2) * new Macros().quux(4))
+10
+
+scala>
diff --git a/test/files/run/macro-repl-basic.scala b/test/files/run/macro-repl-basic.scala
new file mode 100644
index 0000000000..9b1a53343b
--- /dev/null
+++ b/test/files/run/macro-repl-basic.scala
@@ -0,0 +1,18 @@
+import scala.tools.partest.ReplTest
+
+object Test extends ReplTest {
+ override def extraSettings = "-Xmacros"
+ def code = """
+ |object Macros {
+ | object Shmacros {
+ | def macro foo(x: Int): Int = x
+ | }
+ | def macro bar(x: Int): Int = x
+ |}; class Macros {
+ | def macro quux(x: Int): Int = x
+ |}
+ |
+ |import Macros.Shmacros._
+ |println(foo(2) + Macros.bar(2) * new Macros().quux(4))
+ |""".stripMargin
+} \ No newline at end of file
diff --git a/test/files/run/macro-repl-dontexpand.check b/test/files/run/macro-repl-dontexpand.check
new file mode 100644
index 0000000000..d2bb89b6f7
--- /dev/null
+++ b/test/files/run/macro-repl-dontexpand.check
@@ -0,0 +1,9 @@
+Type in expressions to have them evaluated.
+Type :help for more information.
+
+scala>
+
+scala> def macro foo = ???
+foo: Any
+
+scala>
diff --git a/test/files/run/macro-repl-dontexpand.scala b/test/files/run/macro-repl-dontexpand.scala
new file mode 100644
index 0000000000..254bce894c
--- /dev/null
+++ b/test/files/run/macro-repl-dontexpand.scala
@@ -0,0 +1,8 @@
+import scala.tools.partest.ReplTest
+
+object Test extends ReplTest {
+ override def extraSettings = "-Xmacros"
+ def code = """
+ |def macro foo = ???
+ |""".stripMargin
+} \ No newline at end of file
diff --git a/test/files/run/macro-rettype-mismatch.check b/test/files/run/macro-rettype-mismatch.check
new file mode 100644
index 0000000000..f6e4bc09fd
--- /dev/null
+++ b/test/files/run/macro-rettype-mismatch.check
@@ -0,0 +1,5 @@
+error: type mismatch;
+ found : Int(2)
+ required: String
+
+java.lang.Error: reflective compilation has failed
diff --git a/test/files/run/macro-rettype-mismatch.flags b/test/files/run/macro-rettype-mismatch.flags
new file mode 100644
index 0000000000..7fea2ff901
--- /dev/null
+++ b/test/files/run/macro-rettype-mismatch.flags
@@ -0,0 +1 @@
+-Xmacros \ No newline at end of file
diff --git a/test/files/run/macro-rettype-mismatch/Macros_1.scala b/test/files/run/macro-rettype-mismatch/Macros_1.scala
new file mode 100644
index 0000000000..64e5b93468
--- /dev/null
+++ b/test/files/run/macro-rettype-mismatch/Macros_1.scala
@@ -0,0 +1,3 @@
+object Macros {
+ def macro foo(x: Int): String = x
+} \ No newline at end of file
diff --git a/test/files/run/macro-rettype-mismatch/Test_2.scala b/test/files/run/macro-rettype-mismatch/Test_2.scala
new file mode 100644
index 0000000000..39a7c7ad1a
--- /dev/null
+++ b/test/files/run/macro-rettype-mismatch/Test_2.scala
@@ -0,0 +1,16 @@
+import scala.tools.nsc.reporters._
+import scala.tools.nsc.Settings
+import reflect.runtime.Mirror.ToolBox
+
+object Test extends App {
+ import scala.reflect.mirror._
+ val tree = Apply(Select(Ident("Macros"), newTermName("foo")), List(Literal(Constant(2))))
+
+ val stderr = new java.io.ByteArrayOutputStream()
+ Console.setErr(new java.io.PrintStream(stderr))
+
+ val reporter = new ConsoleReporter(new Settings)
+ val toolbox = new ToolBox(reporter)
+ try { toolbox.runExpr(tree) }
+ catch { case ex: Throwable => println(stderr); println(ex) }
+}
diff --git a/test/files/run/t4794.check b/test/files/run/t4794.check
index b4de394767..f599e28b8a 100644
--- a/test/files/run/t4794.check
+++ b/test/files/run/t4794.check
@@ -1 +1 @@
-11
+10
diff --git a/test/files/run/t5488-fn.check b/test/files/run/t5488-fn.check
new file mode 100644
index 0000000000..196f58d063
--- /dev/null
+++ b/test/files/run/t5488-fn.check
@@ -0,0 +1,17 @@
+B$mcII$sp
+B
+B$mcIV$sp
+B$mcLI$sp
+B
+B$mcLV$sp
+B$mcVI$sp
+B
+B$mcVV$sp
+C$mcIII$sp
+C
+C$mcILI$sp
+C
+C$mcLII$sp
+C
+C$mcLLI$sp
+C
diff --git a/test/files/run/t5488-fn.scala b/test/files/run/t5488-fn.scala
new file mode 100644
index 0000000000..9e4f60002b
--- /dev/null
+++ b/test/files/run/t5488-fn.scala
@@ -0,0 +1,49 @@
+class B[@specialized(Int, AnyRef, Unit) A, @specialized(Int, Unit) B](f: A => B)
+class C[@specialized(Int, AnyRef) A, @specialized(Int, AnyRef) B, @specialized(Int) C](f: (A, B) => C)
+// Not yet:
+// class B[@specialized(Int, AnyRef, Unit) A, @specialized(Int, AnyRef, Unit) B](f: A => B)
+// class C[@specialized(Int, AnyRef) A, @specialized(Int, AnyRef) B, @specialized(Int, AnyRef) C](f: (A, B) => C)
+
+object Test {
+ def main(args:Array[String]) {
+ def show(x: Any) = println(x.getClass.getName)
+
+ show(new B((x: Int) => 1))
+ show(new B((x: Int) => "abc"))
+ show(new B((x: Int) => ()))
+ show(new B((x: AnyRef) => 1))
+ show(new B((x: AnyRef) => "abc"))
+ show(new B((x: AnyRef) => ()))
+ show(new B((x: Unit) => 1))
+ show(new B((x: Unit) => "abc"))
+ show(new B((x: Unit) => ()))
+
+ show(new C((x: Int, y: Int) => 1))
+ show(new C((x: Int, y: Int) => "abc"))
+ show(new C((x: Int, y: AnyRef) => 1))
+ show(new C((x: Int, y: AnyRef) => "abc"))
+ show(new C((x: AnyRef, y: Int) => 1))
+ show(new C((x: AnyRef, y: Int) => "abc"))
+ show(new C((x: AnyRef, y: AnyRef) => 1))
+ show(new C((x: AnyRef, y: AnyRef) => "abc"))
+ }
+}
+/** If the return types are specialized on AnyRef as well:
+
+files/run/t5488-fn.scala:18: error: type mismatch;
+ found : Unit => String
+ required: Unit => B$sp
+ show(new B((x: Unit) => "abc"))
+ ^
+files/run/t5488-fn.scala:24: error: type mismatch;
+ found : (Int, Object) => String
+ required: (Int, B$sp) => C$sp
+ show(new C((x: Int, y: AnyRef) => "abc"))
+ ^
+files/run/t5488-fn.scala:26: error: type mismatch;
+ found : (Object, Int) => String
+ required: (A$sp, Int) => C$sp
+ show(new C((x: AnyRef, y: Int) => "abc"))
+ ^
+three errors found
+**/
diff --git a/test/files/run/t5488.check b/test/files/run/t5488.check
new file mode 100644
index 0000000000..ccd98c4dbc
--- /dev/null
+++ b/test/files/run/t5488.check
@@ -0,0 +1,14 @@
+A0$mcI$sp
+A0
+B0$mcII$sp
+B0$mcIL$sp
+B0$mcLI$sp
+B0
+C0$mcIII$sp
+C0$mcIIL$sp
+C0$mcILI$sp
+C0$mcILL$sp
+C0$mcLII$sp
+C0$mcLIL$sp
+C0$mcLLI$sp
+C0
diff --git a/test/files/run/t5488.scala b/test/files/run/t5488.scala
new file mode 100644
index 0000000000..7bab0cdc3c
--- /dev/null
+++ b/test/files/run/t5488.scala
@@ -0,0 +1,26 @@
+class A0[@specialized(Int, AnyRef) A]()
+class B0[@specialized(Int, AnyRef) A, @specialized(Int, AnyRef) B]()
+class C0[@specialized(Int, AnyRef) A, @specialized(Int, AnyRef) B, @specialized(Int, AnyRef) C]()
+
+object Test {
+ def main(args:Array[String]) {
+ def show(x: Any) = println(x.getClass.getName)
+
+ show(new A0[Int]())
+ show(new A0[AnyRef]())
+
+ show(new B0[Int, Int]())
+ show(new B0[Int, AnyRef]())
+ show(new B0[AnyRef, Int]())
+ show(new B0[AnyRef, AnyRef]())
+
+ show(new C0[Int, Int, Int]())
+ show(new C0[Int, Int, AnyRef]())
+ show(new C0[Int, AnyRef, Int]())
+ show(new C0[Int, AnyRef, AnyRef]())
+ show(new C0[AnyRef, Int, Int]())
+ show(new C0[AnyRef, Int, AnyRef]())
+ show(new C0[AnyRef, AnyRef, Int]())
+ show(new C0[AnyRef, AnyRef, AnyRef]())
+ }
+}
diff --git a/test/files/run/virtpatmat_partial.check b/test/files/run/virtpatmat_partial.check
index 093020ce05..1555eca82b 100644
--- a/test/files/run/virtpatmat_partial.check
+++ b/test/files/run/virtpatmat_partial.check
@@ -1,2 +1,4 @@
Map(a -> Some(1), b -> None)
-Map(a -> 1) \ No newline at end of file
+79
+undefined
+Map(a -> 1)
diff --git a/test/files/run/virtpatmat_partial.scala b/test/files/run/virtpatmat_partial.scala
index c408b31983..6597f2f5ae 100644
--- a/test/files/run/virtpatmat_partial.scala
+++ b/test/files/run/virtpatmat_partial.scala
@@ -4,6 +4,29 @@ object Test extends App {
val res = a collect {case (p, Some(a)) => (p, a)}
+ final val GT = 79
+ final val GTGT = 93
+ final val GTGTGT = 94
+ final val GTEQ = 81
+ final val GTGTEQ = 113
+ final val GTGTGTEQ = 114
+ final val ASSIGN = 75
+
+ def acceptClosingAngle(in: Int) {
+ val closers: PartialFunction[Int, Int] = {
+ case GTGTGTEQ => GTGTEQ
+ case GTGTGT => GTGT
+ case GTGTEQ => GTEQ
+ case GTGT => GT
+ case GTEQ => ASSIGN
+ }
+ if (closers isDefinedAt in) println(closers(in))
+ else println("undefined")
+ }
+
+ acceptClosingAngle(GTGT)
+ acceptClosingAngle(ASSIGN)
+
// should uncurry to:
// val res: Map[String,Int] = a.collect[(String, Int), Map[String,Int]](
// new PartialFunction[(String, Option[Int]),(String, Int)] {
diff --git a/test/files/run/virtpatmat_switch.scala b/test/files/run/virtpatmat_switch.scala
index 2e2c31e8e5..1329c19d0f 100644
--- a/test/files/run/virtpatmat_switch.scala
+++ b/test/files/run/virtpatmat_switch.scala
@@ -14,9 +14,15 @@ object Test extends App {
case 'b' => "got b"
case _ => "got some letter"
}
+
+ def byteSwitch(x: Byte) = x match {
+ case 'a' => "got a"
+ case 'b' => "got b"
+ case _ => "got some letter"
+ }
println(charSwitch('a'))
- println(charSwitch('b'))
+ println(byteSwitch('b'))
println(charSwitch('z'))
def implicitDefault(x: Int) = x match {
diff --git a/test/files/run/virtpatmat_try.check b/test/files/run/virtpatmat_try.check
new file mode 100644
index 0000000000..80ebbf494a
--- /dev/null
+++ b/test/files/run/virtpatmat_try.check
@@ -0,0 +1,2 @@
+meh
+B
diff --git a/test/files/run/virtpatmat_try.flags b/test/files/run/virtpatmat_try.flags
new file mode 100644
index 0000000000..9769db9257
--- /dev/null
+++ b/test/files/run/virtpatmat_try.flags
@@ -0,0 +1 @@
+ -Yvirtpatmat -Xexperimental
diff --git a/test/files/run/virtpatmat_try.scala b/test/files/run/virtpatmat_try.scala
new file mode 100644
index 0000000000..46e67cb72e
--- /dev/null
+++ b/test/files/run/virtpatmat_try.scala
@@ -0,0 +1,47 @@
+object Test extends App {
+ case class A(val x: String) extends Throwable
+ class B extends Exception { override def toString = "B" }
+ def bla = 0
+
+ try {
+ throw new A("meh")
+ } catch { // this should emit a "catch-switch"
+ case y: A => println(y.x)
+ case (_ : A | _ : B) => println("B")
+ case _ => println("other")
+ }
+
+ try {
+ throw new B()
+ } catch { // case classes and alternative flattening aren't supported yet, but could be in principle
+ // case A(x) => println(x)
+ case y: A => println(y.x)
+ case x@((_ : A) | (_ : B)) => println(x)
+ case _ => println("other")
+ }
+
+ def simpleTry {
+ try {
+ bla
+ } catch {
+ case x: Exception if x.getMessage == "test" => println("first case " + x)
+ case x: Exception => println("second case " + x)
+ }
+ }
+
+ def typedWildcardTry {
+ try { bla } catch { case _: ClassCastException => bla }
+ }
+
+ def wildcardTry {
+ try { bla } catch { case _ => bla }
+ }
+
+ def tryPlusFinally {
+ try { bla } finally { println("finally") }
+ }
+
+ def catchAndPassToLambda {
+ try { bla } catch { case ex: Exception => val f = () => ex }
+ }
+} \ No newline at end of file
diff --git a/test/files/specialized/SI-5005.scala b/test/files/specialized/SI-5005.scala
index cc9d327b08..3d1ada49e2 100644
--- a/test/files/specialized/SI-5005.scala
+++ b/test/files/specialized/SI-5005.scala
@@ -17,7 +17,11 @@ object Test extends DirectTest {
override def show(): Unit = {
// redirect err to out, for inliner log
- System.setErr(new PrintStream(System.out));
+ val prevErr = System.err
+ System.setErr(System.out)
compile()
+ System.setErr(prevErr)
}
+
+ override def isDebug = false // so we don't get the newSettings warning
}
diff --git a/test/pending/run/macro-overload.check b/test/pending/run/macro-overload.check
new file mode 100644
index 0000000000..764f914e48
--- /dev/null
+++ b/test/pending/run/macro-overload.check
@@ -0,0 +1,4 @@
+object-Int
+object-String
+class-Int
+class-String \ No newline at end of file
diff --git a/test/pending/run/macro-overload.flags b/test/pending/run/macro-overload.flags
new file mode 100644
index 0000000000..7fea2ff901
--- /dev/null
+++ b/test/pending/run/macro-overload.flags
@@ -0,0 +1 @@
+-Xmacros \ No newline at end of file
diff --git a/test/pending/run/macro-overload/Macros_1.scala b/test/pending/run/macro-overload/Macros_1.scala
new file mode 100644
index 0000000000..f24c69ea7b
--- /dev/null
+++ b/test/pending/run/macro-overload/Macros_1.scala
@@ -0,0 +1,9 @@
+object Macros {
+ def macro bar(x: Int): Int = Apply(Select(Select(Ident("scala"), newTermName("Predef")), newTermName("println")), List(Literal(Constant("object-Int"))))
+ def macro bar(x: String): String = Apply(Select(Select(Ident("scala"), newTermName("Predef")), newTermName("println")), List(Literal(Constant("object-String"))))
+}
+
+class Macros {
+ def macro bar(x: Int): Int = Apply(Select(Select(Ident("scala"), newTermName("Predef")), newTermName("println")), List(Literal(Constant("class-Int"))))
+ def macro bar(x: String): String = Apply(Select(Select(Ident("scala"), newTermName("Predef")), newTermName("println")), List(Literal(Constant("class-String"))))
+} \ No newline at end of file
diff --git a/test/pending/run/macro-overload/Test_2.scala b/test/pending/run/macro-overload/Test_2.scala
new file mode 100644
index 0000000000..75f6572e03
--- /dev/null
+++ b/test/pending/run/macro-overload/Test_2.scala
@@ -0,0 +1,6 @@
+object Test extends App {
+ Macros.bar(2)
+ Macros.bar("2")
+ new Macros.bar(2)
+ new Macros.bar("2")
+} \ No newline at end of file
diff --git a/test/files/run/t4770.check b/test/pending/run/t4770.check
index 38e5a831fa..38e5a831fa 100644
--- a/test/files/run/t4770.check
+++ b/test/pending/run/t4770.check
diff --git a/test/files/run/t4770.scala b/test/pending/run/t4770.scala
index 25bf3050c3..25bf3050c3 100644
--- a/test/files/run/t4770.scala
+++ b/test/pending/run/t4770.scala