diff options
Diffstat (limited to 'src')
33 files changed, 657 insertions, 221 deletions
diff --git a/src/build/maven/scala-library-pom.xml b/src/build/maven/scala-library-pom.xml index 673c3dfada..c3f8a4531c 100644 --- a/src/build/maven/scala-library-pom.xml +++ b/src/build/maven/scala-library-pom.xml @@ -30,17 +30,24 @@ <system>JIRA</system> <url>https://issues.scala-lang.org/</url> </issueManagement> - <distributionManagement> - <repository> - <id>scala-tools.org</id> - <url>@RELEASE_REPOSITORY@</url> - </repository> - <snapshotRepository> - <id>scala-tools.org</id> - <url>@SNAPSHOT_REPOSITORY@</url> - <uniqueVersion>false</uniqueVersion> - </snapshotRepository> - </distributionManagement> + <dependencies> + <dependency> + <groupId>org.skife.com.typesafe.config</groupId> + <artifactId>typesafe-config</artifactId> + <version>0.3.0</version> + </dependency> + </dependencies> + <distributionManagement> + <repository> + <id>scala-tools.org</id> + <url>@RELEASE_REPOSITORY@</url> + </repository> + <snapshotRepository> + <id>scala-tools.org</id> + <url>@SNAPSHOT_REPOSITORY@</url> + <uniqueVersion>false</uniqueVersion> + </snapshotRepository> + </distributionManagement> <developers> <developer> <id>lamp</id> diff --git a/src/compiler/scala/reflect/internal/SymbolTable.scala b/src/compiler/scala/reflect/internal/SymbolTable.scala index bb11ca634a..256c1a6ced 100644 --- a/src/compiler/scala/reflect/internal/SymbolTable.scala +++ b/src/compiler/scala/reflect/internal/SymbolTable.scala @@ -62,6 +62,11 @@ abstract class SymbolTable extends api.Universe result } + final val traceSymbolActivity = sys.props contains "scalac.debug.syms" + object traceSymbols extends { + val global: SymbolTable.this.type = SymbolTable.this + } with util.TraceSymbolActivity + /** Are we compiling for Java SE? */ // def forJVM: Boolean diff --git a/src/compiler/scala/reflect/internal/Symbols.scala b/src/compiler/scala/reflect/internal/Symbols.scala index 4842d47e4d..63d030531d 100644 --- a/src/compiler/scala/reflect/internal/Symbols.scala +++ b/src/compiler/scala/reflect/internal/Symbols.scala @@ -3,7 +3,6 @@ * @author Martin Odersky */ - package scala.reflect package internal @@ -166,9 +165,11 @@ trait Symbols extends api.Symbols { self: SymbolTable => private var rawpos = initPos val id = nextId() // identity displayed when -uniqid - private[this] var _validTo: Period = NoPeriod + if (traceSymbolActivity) + traceSymbols.recordNewSymbol(this) + def validTo = _validTo def validTo_=(x: Period) { _validTo = x} @@ -863,6 +864,8 @@ trait Symbols extends api.Symbols { self: SymbolTable => else originalOwner(this) = rawowner } assert(!inReflexiveMirror, "owner_= is not thread-safe; cannot be run in reflexive code") + if (traceSymbolActivity) + traceSymbols.recordNewSymbolOwner(this, owner) _rawowner = owner } diff --git a/src/compiler/scala/reflect/internal/Types.scala b/src/compiler/scala/reflect/internal/Types.scala index 5afa5343ed..786b680ff8 100644 --- a/src/compiler/scala/reflect/internal/Types.scala +++ b/src/compiler/scala/reflect/internal/Types.scala @@ -5059,6 +5059,8 @@ trait Types extends api.Types { self: SymbolTable => case (_, tv @ TypeVar(_,_)) => tv.registerTypeSelection(tr2.sym, tr1) case _ => false }) + case _: SingleType => + return isSameType2(tp2, tp1) // put singleton type on the left, caught below case _ => } case tt1: ThisType => @@ -5071,6 +5073,8 @@ trait Types extends api.Types { self: SymbolTable => tp2 match { case st2: SingleType => if (equalSymsAndPrefixes(st1.sym, st1.pre, st2.sym, st2.pre)) return true + case TypeRef(pre2, sym2, Nil) => + if (sym2.isModuleClass && equalSymsAndPrefixes(st1.sym, st1.pre, sym2.sourceModule, pre2)) return true case _ => } case ct1: ConstantType => @@ -5481,7 +5485,7 @@ trait Types extends api.Types { self: SymbolTable => * - handle typerefs, refined types, notnull and singleton types. */ def fourthTry = tp1 match { - case tr1 @ TypeRef(_, sym1, _) => + case tr1 @ TypeRef(pre1, sym1, _) => sym1 match { case NothingClass => true case NullClass => @@ -5495,8 +5499,8 @@ trait Types extends api.Types { self: SymbolTable => if (isRaw(sym1, tr1.args)) isSubType(rawToExistential(tp1), tp2, depth) else if (sym1.isModuleClass) tp2 match { - case SingleType(_, sym2) => sym1 == sym2 - case _ => false + case SingleType(pre2, sym2) => equalSymsAndPrefixes(sym1.sourceModule, pre1, sym2, pre2) + case _ => false } else if (sym1.isRefinementClass) isSubType(sym1.info, tp2, depth) diff --git a/src/compiler/scala/reflect/internal/util/TraceSymbolActivity.scala b/src/compiler/scala/reflect/internal/util/TraceSymbolActivity.scala new file mode 100644 index 0000000000..eb384f9a85 --- /dev/null +++ b/src/compiler/scala/reflect/internal/util/TraceSymbolActivity.scala @@ -0,0 +1,168 @@ +package scala.reflect.internal +package util + +import scala.collection.{ mutable, immutable } + +trait TraceSymbolActivity { + val global: SymbolTable + import global._ + + if (traceSymbolActivity) + scala.sys addShutdownHook showAllSymbols() + + private type Set[T] = scala.collection.immutable.Set[T] + private val Set = scala.collection.immutable.Set + + val allSymbols = mutable.Map[Int, Symbol]() + val allChildren = mutable.Map[Int, List[Int]]() withDefaultValue Nil + val prevOwners = mutable.Map[Int, List[(Int, Phase)]]() withDefaultValue Nil + val symsCaused = mutable.Map[Int, Int]() withDefaultValue 0 + val allTrees = mutable.Set[Tree]() + + def recordSymbolsInTree(tree: Tree) { + allTrees += tree + } + + def recordNewSymbol(sym: Symbol) { + if (sym.id > 1) { + allSymbols(sym.id) = sym + allChildren(sym.owner.id) ::= sym.id + } + } + def recordNewSymbolOwner(sym: Symbol, newOwner: Symbol) { + val sid = sym.id + val oid = sym.owner.id + val nid = newOwner.id + + prevOwners(sid) ::= (oid -> phase) + allChildren(oid) = allChildren(oid) filterNot (_ == sid) + allChildren(nid) ::= sid + } + + /** TODO. + */ + private def reachableDirectlyFromSymbol(sym: Symbol): List[Symbol] = ( + List(sym.owner, sym.alias, sym.thisSym) + ++ sym.children + ++ sym.info.parents.map(_.typeSymbol) + ++ sym.typeParams + ++ sym.paramss.flatten + ) + private def reachable[T](inputs: Traversable[T], mkSymbol: T => Symbol): Set[Symbol] = { + def loop(seen: Set[Symbol], remaining: List[Symbol]): Set[Symbol] = { + remaining match { + case Nil => seen + case head :: rest => + if ((head eq null) || (head eq NoSymbol) || seen(head)) loop(seen, rest) + else loop(seen + head, rest ++ reachableDirectlyFromSymbol(head).filterNot(seen)) + } + } + loop(immutable.Set(), inputs.toList map mkSymbol filterNot (_ eq null) distinct) + } + private def treeList(t: Tree) = { + val buf = mutable.ListBuffer[Tree]() + t foreach (buf += _) + buf.toList + } + + private def reachableFromSymbol(root: Symbol): Set[Symbol] = + reachable[Symbol](List(root, root.info.typeSymbol), x => x) + + private def reachableFromTree(tree: Tree): Set[Symbol] = + reachable[Tree](treeList(tree), _.symbol) + + private def signature(id: Int) = runBeforeErasure(allSymbols(id).defString) + + private def dashes(s: Any): String = ("" + s) map (_ => '-') + private def show(s1: Any, ss: Any*) { + println("%-12s".format(s1) +: ss mkString " ") + } + private def showHeader(s1: Any, ss: Any*) { + show(s1, ss: _*) + show(dashes(s1), ss map dashes: _*) + } + private def showSym(sym: Symbol) { + def prefix = (" " * (sym.ownerChain.length - 1)) + sym.id + try println("%s#%s %s".format(prefix, sym.accurateKindString, sym.name.decode)) + catch { + case x => println(prefix + " failed: " + x) + } + allChildren(sym.id).sorted foreach showIdAndRemove + } + private def showIdAndRemove(id: Int) { + allSymbols remove id foreach showSym + } + private def symbolStr(id: Int): String = { + if (id == 1) "NoSymbol" else { + val sym = allSymbols(id) + sym.accurateKindString + " " + sym.name.decode + } + } + private def ownerStr(id: Int): String = { + val sym = allSymbols(id) + sym.name.decode + "#" + sym.id + } + + private def freq[T, U](xs: collection.Traversable[T])(fn: T => U): List[(U, Int)] = { + val ys = xs groupBy fn mapValues (_.size) + ys.toList sortBy (-_._2) + } + + private def showMapFreq[T](xs: collection.Map[T, Traversable[_]])(showFn: T => String) { + xs.mapValues(_.size).toList.sortBy(-_._2) take 100 foreach { case (k, size) => + show(size, showFn(k)) + } + println("\n") + } + private def showFreq[T, U](xs: Traversable[T])(groupFn: T => U, showFn: U => String = (x: U) => "" + x) = { + showMapFreq(xs.toList groupBy groupFn)(showFn) + } + private lazy val findErasurePhase: Phase = { + var ph = phase + while (ph != NoPhase && ph.name != "erasure") { + ph = ph.prev + } + ph + } + private def runBeforeErasure[T](body: => T): T = atPhase(findErasurePhase)(body) + + def showAllSymbols() { + if (!traceSymbolActivity) return + allSymbols(1) = NoSymbol + + println("" + allSymbols.size + " symbols created.") + println("") + + showHeader("descendants", "symbol") + showFreq(allSymbols.values flatMap (_.ownerChain drop 1))(_.id, symbolStr) + + showHeader("children", "symbol") + showMapFreq(allChildren)(symbolStr) + + if (prevOwners.nonEmpty) { + showHeader("prev owners", "symbol") + showMapFreq(prevOwners) { k => + val owners = (((allSymbols(k).owner.id, NoPhase)) :: prevOwners(k)) map { + case (oid, NoPhase) => "-> owned by " + ownerStr(oid) + case (oid, ph) => "-> owned by %s (until %s)".format(ownerStr(oid), ph) + } + signature(k) :: owners mkString "\n " + } + } + + val nameFreq = allSymbols.values.toList groupBy (_.name) + showHeader("frequency", "%-15s".format("name"), "owners") + showMapFreq(nameFreq) { name => + "%-15s %s".format(name.decode, { + val owners = freq(nameFreq(name))(_.owner) + + "%4s owners (%s)".format( + owners.size, + owners.take(3).map({ case (k, v) => v + "/" + k }).mkString(", ") + ", ..." + ) + }) + } + + allSymbols.keys.toList.sorted foreach showIdAndRemove + } +} diff --git a/src/compiler/scala/tools/nsc/Global.scala b/src/compiler/scala/tools/nsc/Global.scala index 9ccd0c28db..6a30dc2a0f 100644 --- a/src/compiler/scala/tools/nsc/Global.scala +++ b/src/compiler/scala/tools/nsc/Global.scala @@ -1356,6 +1356,9 @@ class Global(var currentSettings: Settings, var reporter: Reporter) extends Symb inform(unitTimingsFormatted) } + if (traceSymbolActivity) + units map (_.body) foreach (traceSymbols recordSymbolsInTree _) + // In case no phase was specified for -Xshow-class/object, show it now for sure. if (opt.noShow) showMembers() diff --git a/src/compiler/scala/tools/nsc/interactive/tests/InteractiveTest.scala b/src/compiler/scala/tools/nsc/interactive/tests/InteractiveTest.scala index 7be115e777..9dc2a8de10 100644 --- a/src/compiler/scala/tools/nsc/interactive/tests/InteractiveTest.scala +++ b/src/compiler/scala/tools/nsc/interactive/tests/InteractiveTest.scala @@ -55,6 +55,7 @@ abstract class InteractiveTest with AskShutdown with AskReload with AskLoadedTyped + with AskType with PresentationCompilerInstance with CoreTestDefs with InteractiveTestSettings { self => diff --git a/src/compiler/scala/tools/nsc/interactive/tests/core/AskCommand.scala b/src/compiler/scala/tools/nsc/interactive/tests/core/AskCommand.scala index 35d6723818..657ef23eed 100644 --- a/src/compiler/scala/tools/nsc/interactive/tests/core/AskCommand.scala +++ b/src/compiler/scala/tools/nsc/interactive/tests/core/AskCommand.scala @@ -101,13 +101,13 @@ trait AskTypeAt extends AskCommand { trait AskType extends AskCommand { import compiler.Tree - private[tests] def askType(source: SourceFile, forceReload: Boolean)(implicit reporter: Reporter): Response[Tree] = { + protected def askType(source: SourceFile, forceReload: Boolean)(implicit reporter: Reporter): Response[Tree] = { ask { compiler.askType(source, forceReload, _) } } - private[tests] def askType(sources: Seq[SourceFile], forceReload: Boolean)(implicit reporter: Reporter): Seq[Response[Tree]] = { + protected def askType(sources: Seq[SourceFile], forceReload: Boolean)(implicit reporter: Reporter): Seq[Response[Tree]] = { for(source <- sources) yield askType(source, forceReload) } diff --git a/src/compiler/scala/tools/nsc/typechecker/Infer.scala b/src/compiler/scala/tools/nsc/typechecker/Infer.scala index a1ca4904f4..dba31f7bca 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Infer.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Infer.scala @@ -484,8 +484,7 @@ trait Infer { else Some( if (targ.typeSymbol == RepeatedParamClass) targ.baseType(SeqClass) else if (targ.typeSymbol == JavaRepeatedParamClass) targ.baseType(ArrayClass) - // this infers Foo.type instead of "object Foo" (see also widenIfNecessary) - else if (targ.typeSymbol.isModuleClass || ((opt.experimental || opt.virtPatmat) && tvar.constr.avoidWiden)) targ + else if ((opt.experimental || opt.virtPatmat) && tvar.constr.avoidWiden) targ else targ.widen ) )) diff --git a/src/compiler/scala/tools/nsc/typechecker/Namers.scala b/src/compiler/scala/tools/nsc/typechecker/Namers.scala index 8604366bf2..1b505d1e5d 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Namers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Namers.scala @@ -793,10 +793,7 @@ trait Namers extends MethodSynthesis { val tpe1 = dropRepeatedParamType(tpe.deconst) val tpe2 = tpe1.widen - // This infers Foo.type instead of "object Foo" - // See Infer#adjustTypeArgs for the polymorphic case. - if (tpe.typeSymbolDirect.isModuleClass) tpe1 - else if (sym.isVariable || sym.isMethod && !sym.hasAccessorFlag) + if (sym.isVariable || sym.isMethod && !sym.hasAccessorFlag) if (tpe2 <:< pt) tpe2 else tpe1 else if (isHidden(tpe)) tpe2 // In an attempt to make pattern matches involving method local vals diff --git a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala index 045614e773..54b711cebc 100644 --- a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala +++ b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala @@ -951,17 +951,20 @@ abstract class RefChecks extends InfoTransform with reflect.internal.transform.R override def traverse(tree: Tree) { tree match { - case ClassDef(_, _, _, _) | - TypeDef(_, _, _, _) => + case ClassDef(_, _, _, _) | TypeDef(_, _, _, _) => validateVariance(tree.symbol) super.traverse(tree) // ModuleDefs need not be considered because they have been eliminated already case ValDef(_, _, _, _) => - validateVariance(tree.symbol) + if (!tree.symbol.hasLocalFlag) + validateVariance(tree.symbol) case DefDef(_, _, tparams, vparamss, _, _) => - validateVariance(tree.symbol) - traverseTrees(tparams) - traverseTreess(vparamss) + // No variance check for object-private/protected methods/values. + if (!tree.symbol.hasLocalFlag) { + validateVariance(tree.symbol) + traverseTrees(tparams) + traverseTreess(vparamss) + } case Template(_, _, _) => super.traverse(tree) case _ => diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index 2aff00f6a5..36c81b09cd 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -2089,7 +2089,10 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { for (Apply(_, xs) <- cdef.pat ; x <- xs dropRight 1 ; if treeInfo isStar x) StarPositionInPatternError(x) - val pat1 = typedPattern(cdef.pat, pattpe) + // withoutAnnotations - see continuations-run/z1673.scala + // This adjustment is awfully specific to continuations, but AFAICS the + // whole AnnotationChecker framework is. + val pat1 = typedPattern(cdef.pat, pattpe.withoutAnnotations) // When case classes have more than two parameter lists, the pattern ends // up typed as a method. We only pattern match on the first parameter // list, so substitute the final result type of the method, i.e. the type @@ -2199,7 +2202,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { vparams map {p => if(p.tpt.tpe == null) typedType(p.tpt).tpe else p.tpt.tpe} } - def mkParams(methodSym: Symbol, formals: List[Type] = deriveFormals) = { + def mkParams(methodSym: Symbol, formals: List[Type]/* = deriveFormals*/) = { selOverride match { case None if targs.isEmpty => MissingParameterTypeAnonMatchError(tree, pt); (Nil, EmptyTree) case None => @@ -2225,7 +2228,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { // rig the show so we can get started typing the method body -- later we'll correct the infos... anonClass setInfo ClassInfoType(List(ObjectClass.tpe, pt, SerializableClass.tpe), newScope, anonClass) val methodSym = anonClass.newMethod(nme.apply, tree.pos, FINAL) - val (paramSyms, selector) = mkParams(methodSym) + val (paramSyms, selector) = mkParams(methodSym, deriveFormals) if (selector eq EmptyTree) EmptyTree else { @@ -2289,7 +2292,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { def isDefinedAtMethod = { val methodSym = anonClass.newMethod(nme.isDefinedAt, tree.pos, FINAL) - val (paramSyms, selector) = mkParams(methodSym) + val (paramSyms, selector) = mkParams(methodSym, deriveFormals) if (selector eq EmptyTree) EmptyTree else { val methodBodyTyper = newTyper(context.makeNewScope(context.tree, methodSym)) // should use the DefDef for the context's tree, but it doesn't exist yet (we need the typer we're creating to create it) diff --git a/src/continuations/plugin/scala/tools/selectivecps/CPSUtils.scala b/src/continuations/plugin/scala/tools/selectivecps/CPSUtils.scala index 075009ce5e..67ea6e15f0 100644 --- a/src/continuations/plugin/scala/tools/selectivecps/CPSUtils.scala +++ b/src/continuations/plugin/scala/tools/selectivecps/CPSUtils.scala @@ -24,6 +24,7 @@ trait CPSUtils { val shift = newTermName("shift") val shiftR = newTermName("shiftR") val shiftSuffix = newTermName("$shift") + val shiftUnit0 = newTermName("shiftUnit0") val shiftUnit = newTermName("shiftUnit") val shiftUnitR = newTermName("shiftUnitR") } @@ -38,6 +39,7 @@ trait CPSUtils { lazy val ModCPS = definitions.getRequiredModule("scala.util.continuations") lazy val MethShiftUnit = definitions.getMember(ModCPS, cpsNames.shiftUnit) + lazy val MethShiftUnit0 = definitions.getMember(ModCPS, cpsNames.shiftUnit0) lazy val MethShiftUnitR = definitions.getMember(ModCPS, cpsNames.shiftUnitR) lazy val MethShift = definitions.getMember(ModCPS, cpsNames.shift) lazy val MethShiftR = definitions.getMember(ModCPS, cpsNames.shiftR) diff --git a/src/continuations/plugin/scala/tools/selectivecps/SelectiveANFTransform.scala b/src/continuations/plugin/scala/tools/selectivecps/SelectiveANFTransform.scala index 1189cc2e38..a6737573ea 100644 --- a/src/continuations/plugin/scala/tools/selectivecps/SelectiveANFTransform.scala +++ b/src/continuations/plugin/scala/tools/selectivecps/SelectiveANFTransform.scala @@ -310,15 +310,23 @@ abstract class SelectiveANFTransform extends PluginComponent with Transform with try { val Some((a, b)) = cpsR - - val res = localTyper.typed(atPos(tree.pos) { - Apply(TypeApply(gen.mkAttributedRef(MethShiftUnit), - List(TypeTree(plainTpe), TypeTree(a), TypeTree(b))), - List(expr)) - }) - return (stms, res) - - } catch { + /** Since shiftUnit is bounded [A,B,C>:B] this may not typecheck + * if C is overly specific. So if !(B <:< C), call shiftUnit0 + * instead, which takes only two type arguments. + */ + val conforms = a <:< b + val call = localTyper.typedPos(tree.pos)( + Apply( + TypeApply( + gen.mkAttributedRef( if (conforms) MethShiftUnit else MethShiftUnit0 ), + List(TypeTree(plainTpe), TypeTree(a)) ++ ( if (conforms) List(TypeTree(b)) else Nil ) + ), + List(expr) + ) + ) + return ((stms, call)) + } + catch { case ex:TypeError => unit.error(ex.pos, "cannot cps-transform expression " + tree + ": " + ex.msg) } diff --git a/src/library/scala/Option.scala b/src/library/scala/Option.scala index 6db4904b93..2d87ccb261 100644 --- a/src/library/scala/Option.scala +++ b/src/library/scala/Option.scala @@ -130,6 +130,18 @@ sealed abstract class Option[+A] extends Product with Serializable { @inline final def map[B](f: A => B): Option[B] = if (isEmpty) None else Some(f(this.get)) + /** Returns the result of applying $f to this $option's + * value if the $option is nonempty. Otherwise, evaluates + * expression $ifEmpty. + * + * @note This is equivalent to `$option map f getOrElse ifEmpty`. + * + * @param ifEmpty the expression to evaluate if empty. + * @param f the function to apply if nonempty. + */ + @inline final def fold[B](ifEmpty: => B)(f: A => B): B = + if (isEmpty) ifEmpty else f(this.get) + /** Returns the result of applying $f to this $option's value if * this $option is nonempty. * Returns $none if this $option is empty. diff --git a/src/library/scala/collection/JavaConversions.scala b/src/library/scala/collection/JavaConversions.scala index 75ab3f28f5..a978a9a783 100644 --- a/src/library/scala/collection/JavaConversions.scala +++ b/src/library/scala/collection/JavaConversions.scala @@ -127,3 +127,5 @@ object JavaConversions extends WrapAsScala with WrapAsJava { @deprecated("use propertiesAsScalaMap instead", "2.9.0") def asScalaMap(p: ju.Properties): mutable.Map[String, String] = propertiesAsScalaMap(p) } + + diff --git a/src/library/scala/collection/JavaConverters.scala b/src/library/scala/collection/JavaConverters.scala index 13f1f19f81..f8a9466caf 100755 --- a/src/library/scala/collection/JavaConverters.scala +++ b/src/library/scala/collection/JavaConverters.scala @@ -100,4 +100,4 @@ object JavaConverters extends DecorateAsJava with DecorateAsScala { @deprecated("Use propertiesAsScalaMapConverter instead", "2.9.0") def asScalaMapConverter(p: ju.Properties): AsScala[mutable.Map[String, String]] = propertiesAsScalaMapConverter(p) -}
\ No newline at end of file +} diff --git a/src/library/scala/collection/convert/DecorateAsJava.scala b/src/library/scala/collection/convert/DecorateAsJava.scala index 76837d937c..e05bfc41cd 100644 --- a/src/library/scala/collection/convert/DecorateAsJava.scala +++ b/src/library/scala/collection/convert/DecorateAsJava.scala @@ -291,6 +291,26 @@ trait DecorateAsJava { * @return An object with an `asJava` method that returns a Java * `ConcurrentMap` view of the argument. */ + @deprecated("Use `concurrent.Map` instead of `ConcurrentMap`.", "2.10.0") implicit def asJavaConcurrentMapConverter[A, B](m: mutable.ConcurrentMap[A, B]): AsJava[juc.ConcurrentMap[A, B]] = new AsJava(asJavaConcurrentMap(m)) + + /** + * Adds an `asJava` method that implicitly converts a Scala mutable + * `concurrent.Map` to a Java `ConcurrentMap`. + * + * The returned Java `ConcurrentMap` is backed by the provided Scala + * `concurrent.Map` and any side-effects of using it via the Java interface + * will be visible via the Scala interface and vice versa. + * + * If the Scala `concurrent.Map` was previously obtained from an implicit or + * explicit call of `asConcurrentMap(java.util.concurrect.ConcurrentMap)` + * then the original Java `ConcurrentMap` will be returned. + * + * @param m The Scala `concurrent.Map` to be converted. + * @return An object with an `asJava` method that returns a Java + * `ConcurrentMap` view of the argument. + */ + implicit def asJavaConcurrentMapConverter[A, B](m: concurrent.Map[A, B]): AsJava[juc.ConcurrentMap[A, B]] = + new AsJava(asJavaConcurrentMap(m)) } diff --git a/src/library/scala/collection/convert/DecorateAsScala.scala b/src/library/scala/collection/convert/DecorateAsScala.scala index bb14228e67..722f0b9af9 100644 --- a/src/library/scala/collection/convert/DecorateAsScala.scala +++ b/src/library/scala/collection/convert/DecorateAsScala.scala @@ -156,10 +156,29 @@ trait DecorateAsScala { * @return An object with an `asScala` method that returns a Scala mutable * `ConcurrentMap` view of the argument. */ - implicit def asScalaConcurrentMapConverter[A, B](m: juc.ConcurrentMap[A, B]): AsScala[mutable.ConcurrentMap[A, B]] = + @deprecated("Use `mapAsScalaConcurrentMapConverter` instead, and use `concurrent.Map` instead of `ConcurrentMap`.", "2.10.0") + def asScalaConcurrentMapConverter[A, B](m: juc.ConcurrentMap[A, B]): AsScala[mutable.ConcurrentMap[A, B]] = new AsScala(asScalaConcurrentMap(m)) /** + * Adds an `asScala` method that implicitly converts a Java `ConcurrentMap` + * to a Scala mutable `concurrent.Map`. The returned Scala `concurrent.Map` is + * backed by the provided Java `ConcurrentMap` and any side-effects of using + * it via the Scala interface will be visible via the Java interface and + * vice versa. + * + * If the Java `ConcurrentMap` was previously obtained from an implicit or + * explicit call of `mapAsScalaConcurrentMap(scala.collection.mutable.ConcurrentMap)` + * then the original Scala `concurrent.Map` will be returned. + * + * @param m The `ConcurrentMap` to be converted. + * @return An object with an `asScala` method that returns a Scala mutable + * `concurrent.Map` view of the argument. + */ + implicit def mapAsScalaConcurrentMapConverter[A, B](m: juc.ConcurrentMap[A, B]): AsScala[concurrent.Map[A, B]] = + new AsScala(mapAsScalaConcurrentMap(m)) + + /** * Adds an `asScala` method that implicitly converts a Java `Dictionary` * to a Scala mutable `Map[String, String]`. The returned Scala * `Map[String, String]` is backed by the provided Java `Dictionary` and diff --git a/src/library/scala/collection/convert/WrapAsJava.scala b/src/library/scala/collection/convert/WrapAsJava.scala index 6274518d1a..cdec72b9fe 100644 --- a/src/library/scala/collection/convert/WrapAsJava.scala +++ b/src/library/scala/collection/convert/WrapAsJava.scala @@ -241,16 +241,45 @@ trait WrapAsJava { * will be visible via the Scala interface and vice versa. * * If the Scala `ConcurrentMap` was previously obtained from an implicit or - * explicit call of `asConcurrentMap(java.util.concurrect.ConcurrentMap)` + * explicit call of `asScalaConcurrentMap(java.util.concurrect.ConcurrentMap)` * then the original Java ConcurrentMap will be returned. * * @param m The `ConcurrentMap` to be converted. * @return A Java `ConcurrentMap` view of the argument. */ + @deprecated("Use `concurrent.Map` instead of `ConcurrentMap`.", "2.10.0") implicit def asJavaConcurrentMap[A, B](m: mutable.ConcurrentMap[A, B]): juc.ConcurrentMap[A, B] = m match { + case JConcurrentMapDeprecatedWrapper(wrapped) => wrapped + case _ => new ConcurrentMapDeprecatedWrapper(m) + } + + /** + * Implicitly converts a Scala mutable `concurrent.Map` to a Java + * `ConcurrentMap`. + * + * The returned Java `ConcurrentMap` is backed by the provided Scala + * `concurrent.Map` and any side-effects of using it via the Java interface + * will be visible via the Scala interface and vice versa. + * + * If the Scala `concurrent.Map` was previously obtained from an implicit or + * explicit call of `mapAsScalaConcurrentMap(java.util.concurrect.ConcurrentMap)` + * then the original Java ConcurrentMap will be returned. + * + * @param m The Scala `concurrent.Map` to be converted. + * @return A Java `ConcurrentMap` view of the argument. + */ + implicit def asJavaConcurrentMap[A, B](m: concurrent.Map[A, B]): juc.ConcurrentMap[A, B] = m match { case JConcurrentMapWrapper(wrapped) => wrapped case _ => new ConcurrentMapWrapper(m) } } object WrapAsJava extends WrapAsJava { } + + + + + + + + diff --git a/src/library/scala/collection/convert/WrapAsScala.scala b/src/library/scala/collection/convert/WrapAsScala.scala index 02b58f55a4..56e13b2105 100644 --- a/src/library/scala/collection/convert/WrapAsScala.scala +++ b/src/library/scala/collection/convert/WrapAsScala.scala @@ -154,9 +154,28 @@ trait WrapAsScala { * @param m The ConcurrentMap to be converted. * @return A Scala mutable ConcurrentMap view of the argument. */ - implicit def asScalaConcurrentMap[A, B](m: juc.ConcurrentMap[A, B]): mutable.ConcurrentMap[A, B] = m match { - case cmw: ConcurrentMapWrapper[a, b] => cmw.underlying - case _ => new JConcurrentMapWrapper(m) + @deprecated("Use `mapAsScalaConcurrentMap` instead, and use `concurrent.Map` instead of `ConcurrentMap`.", "2.10.0") + def asScalaConcurrentMap[A, B](m: juc.ConcurrentMap[A, B]): mutable.ConcurrentMap[A, B] = m match { + case cmw: ConcurrentMapDeprecatedWrapper[a, b] => cmw.underlying + case _ => new JConcurrentMapDeprecatedWrapper(m) + } + + /** + * Implicitly converts a Java ConcurrentMap to a Scala mutable ConcurrentMap. + * The returned Scala ConcurrentMap is backed by the provided Java + * ConcurrentMap and any side-effects of using it via the Scala interface will + * be visible via the Java interface and vice versa. + * + * If the Java ConcurrentMap was previously obtained from an implicit or + * explicit call of `asConcurrentMap(scala.collection.mutable.ConcurrentMap)` + * then the original Scala ConcurrentMap will be returned. + * + * @param m The ConcurrentMap to be converted. + * @return A Scala mutable ConcurrentMap view of the argument. + */ + implicit def mapAsScalaConcurrentMap[A, B](m: juc.ConcurrentMap[A, B]): concurrent.Map[A, B] = m match { + case cmw: ConcurrentMapWrapper[a, b] => cmw.underlying + case _ => new JConcurrentMapWrapper(m) } /** diff --git a/src/library/scala/collection/convert/Wrappers.scala b/src/library/scala/collection/convert/Wrappers.scala index 8136e462cb..b1b48b760f 100644 --- a/src/library/scala/collection/convert/Wrappers.scala +++ b/src/library/scala/collection/convert/Wrappers.scala @@ -268,7 +268,7 @@ private[collection] trait Wrappers { override def empty = JMapWrapper(new ju.HashMap[A, B]) } - class ConcurrentMapWrapper[A, B](override val underlying: mutable.ConcurrentMap[A, B]) extends MutableMapWrapper[A, B](underlying) with juc.ConcurrentMap[A, B] { + class ConcurrentMapDeprecatedWrapper[A, B](override val underlying: mutable.ConcurrentMap[A, B]) extends MutableMapWrapper[A, B](underlying) with juc.ConcurrentMap[A, B] { def putIfAbsent(k: A, v: B) = underlying.putIfAbsent(k, v) match { case Some(v) => v @@ -290,7 +290,54 @@ private[collection] trait Wrappers { def replace(k: A, oldval: B, newval: B) = underlying.replace(k, oldval, newval) } - case class JConcurrentMapWrapper[A, B](val underlying: juc.ConcurrentMap[A, B]) extends mutable.AbstractMap[A, B] with JMapWrapperLike[A, B, JConcurrentMapWrapper[A, B]] with mutable.ConcurrentMap[A, B] { + class ConcurrentMapWrapper[A, B](override val underlying: concurrent.Map[A, B]) extends MutableMapWrapper[A, B](underlying) with juc.ConcurrentMap[A, B] { + + def putIfAbsent(k: A, v: B) = underlying.putIfAbsent(k, v) match { + case Some(v) => v + case None => null.asInstanceOf[B] + } + + def remove(k: AnyRef, v: AnyRef) = try { + underlying.remove(k.asInstanceOf[A], v.asInstanceOf[B]) + } catch { + case ex: ClassCastException => + false + } + + def replace(k: A, v: B): B = underlying.replace(k, v) match { + case Some(v) => v + case None => null.asInstanceOf[B] + } + + def replace(k: A, oldval: B, newval: B) = underlying.replace(k, oldval, newval) + } + + case class JConcurrentMapDeprecatedWrapper[A, B](val underlying: juc.ConcurrentMap[A, B]) extends mutable.AbstractMap[A, B] with JMapWrapperLike[A, B, JConcurrentMapDeprecatedWrapper[A, B]] with mutable.ConcurrentMap[A, B] { + override def get(k: A) = { + val v = underlying get k + if (v != null) Some(v) + else None + } + + override def empty = new JConcurrentMapDeprecatedWrapper(new juc.ConcurrentHashMap[A, B]) + + def putIfAbsent(k: A, v: B): Option[B] = { + val r = underlying.putIfAbsent(k, v) + if (r != null) Some(r) else None + } + + def remove(k: A, v: B): Boolean = underlying.remove(k, v) + + def replace(k: A, v: B): Option[B] = { + val prev = underlying.replace(k, v) + if (prev != null) Some(prev) else None + } + + def replace(k: A, oldvalue: B, newvalue: B): Boolean = + underlying.replace(k, oldvalue, newvalue) + } + + case class JConcurrentMapWrapper[A, B](val underlying: juc.ConcurrentMap[A, B]) extends mutable.AbstractMap[A, B] with JMapWrapperLike[A, B, JConcurrentMapWrapper[A, B]] with concurrent.Map[A, B] { override def get(k: A) = { val v = underlying get k if (v != null) Some(v) diff --git a/src/library/scala/collection/mutable/HashTable.scala b/src/library/scala/collection/mutable/HashTable.scala index cc0aed6963..06b7d40bfc 100644 --- a/src/library/scala/collection/mutable/HashTable.scala +++ b/src/library/scala/collection/mutable/HashTable.scala @@ -366,7 +366,7 @@ private[collection] object HashTable { private[collection] final def newThreshold(_loadFactor: Int, size: Int) = ((size.toLong * _loadFactor) / loadFactorDenum).toInt - private[collection] final def sizeForThreshold(_loadFactor: Int, thr: Int) = thr * loadFactorDenum / _loadFactor + private[collection] final def sizeForThreshold(_loadFactor: Int, thr: Int) = ((thr.toLong * loadFactorDenum) / _loadFactor).toInt private[collection] final def capacity(expectedSize: Int) = if (expectedSize == 0) 1 else powerOfTwo(expectedSize) diff --git a/src/library/scala/concurrent/ConcurrentPackageObject.scala b/src/library/scala/concurrent/ConcurrentPackageObject.scala index ba98757906..0f882540f2 100644 --- a/src/library/scala/concurrent/ConcurrentPackageObject.scala +++ b/src/library/scala/concurrent/ConcurrentPackageObject.scala @@ -8,11 +8,8 @@ package scala.concurrent - - -import java.util.concurrent.{ Executors, ExecutorService } -import scala.concurrent.forkjoin.ForkJoinPool -import scala.util.{ Try, Success, Failure } +import java.util.concurrent.{ Executors, ExecutorService, ThreadFactory } +import scala.concurrent.forkjoin.{ ForkJoinPool, ForkJoinWorkerThread } import scala.concurrent.util.Duration import ConcurrentPackageObject._ @@ -24,14 +21,9 @@ abstract class ConcurrentPackageObject { /** A global execution environment for executing lightweight tasks. */ lazy val executionContext = - new impl.ExecutionContextImpl(getExecutorService) - - private[concurrent] def getExecutorService: AnyRef = - if (scala.util.Properties.isJavaAtLeast("1.6")) { - val vendor = scala.util.Properties.javaVmVendor - if ((vendor contains "Oracle") || (vendor contains "Sun") || (vendor contains "Apple")) new ForkJoinPool - else Executors.newCachedThreadPool() - } else Executors.newCachedThreadPool() + new impl.ExecutionContextImpl() + + private val currentExecutionContext = new ThreadLocal[ExecutionContext] val handledFutureException: PartialFunction[Throwable, Throwable] = { case t: Throwable if isFutureThrowable(t) => t @@ -45,23 +37,23 @@ abstract class ConcurrentPackageObject { 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)) + private[concurrent] def resolve[T](source: Either[Throwable, T]): Either[Throwable, T] = source match { + case Left(t: scala.runtime.NonLocalReturnControl[_]) => Right(t.value.asInstanceOf[T]) + case Left(t: scala.util.control.ControlThrowable) => Left(new ExecutionException("Boxed ControlThrowable", t)) + case Left(t: InterruptedException) => Left(new ExecutionException("Boxed InterruptedException", t)) + case Left(e: Error) => Left(new ExecutionException("Boxed Error", e)) case _ => source } private[concurrent] def resolver[T] = - resolverFunction.asInstanceOf[PartialFunction[Throwable, Try[T]]] + resolverFunction.asInstanceOf[PartialFunction[Throwable, Either[Throwable, T]]] /* concurrency constructs */ - def future[T](body: =>T)(implicit execCtx: ExecutionContext = executionContext): Future[T] = + def future[T](body: =>T)(implicit execctx: ExecutionContext = executionContext): Future[T] = Future[T](body) - def promise[T]()(implicit execCtx: ExecutionContext = executionContext): Promise[T] = + def promise[T]()(implicit execctx: ExecutionContext = executionContext): Promise[T] = Promise[T]() /** Wraps a block of code into an awaitable object. */ @@ -82,8 +74,8 @@ abstract class ConcurrentPackageObject { * - 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](body: =>T)(implicit execCtx: ExecutionContext): T = - executionContext.blocking(body) + def blocking[T](body: =>T): T = + blocking(body2awaitable(body), Duration.fromNanos(0)) /** Blocks on an awaitable object. * @@ -94,8 +86,11 @@ abstract class ConcurrentPackageObject { * - 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) + def blocking[T](awaitable: Awaitable[T], atMost: Duration): T = + currentExecutionContext.get match { + case null => Await.result(awaitable, atMost) + case ec => ec.internalBlockingCall(awaitable, atMost) + } @inline implicit final def int2durationops(x: Int): DurationOps = new DurationOps(x) } @@ -106,11 +101,11 @@ private[concurrent] object ConcurrentPackageObject { // 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) + private val resolverFunction: PartialFunction[Throwable, Either[Throwable, _]] = { + case t: scala.runtime.NonLocalReturnControl[_] => Right(t.value) + case t: scala.util.control.ControlThrowable => Left(new ExecutionException("Boxed ControlThrowable", t)) + case t: InterruptedException => Left(new ExecutionException("Boxed InterruptedException", t)) + case e: Error => Left(new ExecutionException("Boxed Error", e)) + case t => Left(t) } } diff --git a/src/library/scala/concurrent/ExecutionContext.scala b/src/library/scala/concurrent/ExecutionContext.scala index a206a2d4ea..16d9a1f980 100644 --- a/src/library/scala/concurrent/ExecutionContext.scala +++ b/src/library/scala/concurrent/ExecutionContext.scala @@ -13,7 +13,6 @@ package scala.concurrent import java.util.concurrent.atomic.{ AtomicInteger } import java.util.concurrent.{ Executors, Future => JFuture, Callable } import scala.concurrent.util.Duration -import scala.util.{ Try, Success, Failure } import scala.concurrent.forkjoin.{ ForkJoinPool, RecursiveTask => FJTask, RecursiveAction, ForkJoinWorkerThread } import scala.collection.generic.CanBuildFrom import collection._ @@ -26,10 +25,8 @@ trait ExecutionContext { def execute[U](body: () => U): Unit - def blocking[T](body: =>T): T - - def blocking[T](awaitable: Awaitable[T], atMost: Duration): T - + def internalBlockingCall[T](awaitable: Awaitable[T], atMost: Duration): T + def reportFailure(t: Throwable): Unit /* implementations follow */ diff --git a/src/library/scala/concurrent/Future.scala b/src/library/scala/concurrent/Future.scala index d73801aa90..5f703ac23b 100644 --- a/src/library/scala/concurrent/Future.scala +++ b/src/library/scala/concurrent/Future.scala @@ -17,7 +17,6 @@ import java.util.{ LinkedList => JLinkedList } import java.{ lang => jl } import java.util.concurrent.atomic.{ AtomicReferenceFieldUpdater, AtomicInteger, AtomicBoolean } -import scala.util.{ Try, Success, Failure } import scala.concurrent.util.Duration import scala.Option @@ -97,8 +96,8 @@ self => * $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*/ } + case Left(t) => // do nothing + case Right(v) => if (pf isDefinedAt v) pf(v) else { /*do nothing*/ } } /** When this future is completed with a failure (i.e. with a throwable), @@ -114,8 +113,8 @@ self => * $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 + case Left(t) => if (isFutureThrowable(t) && callback.isDefinedAt(t)) callback(t) else { /*do nothing*/ } + case Right(v) => // do nothing } /** When this future is completed, either through an exception, a timeout, or a value, @@ -126,7 +125,7 @@ self => * * $multipleCallbacks */ - def onComplete[U](func: Try[T] => U): this.type + def onComplete[U](func: Either[Throwable, T] => U): this.type /* Miscellaneous */ @@ -151,7 +150,7 @@ self => * if it contains a valid result, or `Some(Failure(error))` if it contains * an exception. */ - def value: Option[Try[T]] + def value: Option[Either[Throwable, T]] /* Projections */ @@ -175,8 +174,8 @@ self => val p = newPromise[Throwable] onComplete { - case Failure(t) => p success t - case Success(v) => p failure noSuchElem(v) + case Left(t) => p success t + case Right(v) => p failure noSuchElem(v) } p.future @@ -190,8 +189,8 @@ self => * 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 + case Right(r) => f(r) + case Left(_) => // do nothing } /** Creates a new future by applying a function to the successful result of @@ -204,8 +203,8 @@ self => val p = newPromise[S] onComplete { - case Failure(t) => p failure t - case Success(v) => + case Left(t) => p failure t + case Right(v) => try p success f(v) catch { case t => p complete resolver(t) @@ -226,12 +225,12 @@ self => val p = newPromise[S] onComplete { - case Failure(t) => p failure t - case Success(v) => + case Left(t) => p failure t + case Right(v) => try { f(v) onComplete { - case Failure(t) => p failure t - case Success(v) => p success v + case Left(t) => p failure t + case Right(v) => p success v } } catch { case t: Throwable => p complete resolver(t) @@ -261,8 +260,8 @@ self => val p = newPromise[T] onComplete { - case Failure(t) => p failure t - case Success(v) => + case Left(t) => p failure t + case Right(v) => try { if (pred(v)) p success v else p failure new NoSuchElementException("Future.filter predicate is not satisfied by: " + v) @@ -274,6 +273,18 @@ self => p.future } + /** Used by for-comprehensions. + */ + final def withFilter(p: T => Boolean): Future[T] = filter(p) + // final def withFilter(p: T => Boolean) = new FutureWithFilter[T](this, p) + + // final class FutureWithFilter[+S](self: Future[S], p: S => Boolean) { + // def foreach(f: S => Unit): Unit = self filter p foreach f + // def map[R](f: S => R) = self filter p map f + // def flatMap[R](f: S => Future[R]) = self filter p flatMap f + // def withFilter(q: S => Boolean): FutureWithFilter[S] = new FutureWithFilter[S](self, x => p(x) && q(x)) + // } + /** 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. @@ -298,8 +309,8 @@ self => val p = newPromise[S] onComplete { - case Failure(t) => p failure t - case Success(v) => + case Left(t) => p failure t + case Right(v) => try { if (pf.isDefinedAt(v)) p success pf(v) else p failure new NoSuchElementException("Future.collect partial function is not defined at: " + v) @@ -327,7 +338,7 @@ self => val p = newPromise[U] onComplete { - case Failure(t) if pf isDefinedAt t => + case Left(t) if pf isDefinedAt t => try { p success pf(t) } catch { case t: Throwable => p complete resolver(t) } case otherwise => p complete otherwise @@ -353,7 +364,7 @@ self => val p = newPromise[U] onComplete { - case Failure(t) if pf isDefinedAt t => + case Left(t) if pf isDefinedAt t => try { p completeWith pf(t) } catch { @@ -377,8 +388,8 @@ self => val p = newPromise[(T, U)] this onComplete { - case Failure(t) => p failure t - case Success(r) => that onSuccess { + case Left(t) => p failure t + case Right(r) => that onSuccess { case r2 => p success ((r, r2)) } } @@ -408,16 +419,35 @@ self => val p = newPromise[U] onComplete { - case Failure(t) => that onComplete { - case Failure(_) => p failure t - case Success(v) => p success v + case Left(t) => that onComplete { + case Left(_) => p failure t + case Right(v) => p success v } - case Success(v) => p success v + case Right(v) => p success v } p.future } - + + /** Creates a new `Future[S]` which is completed with this `Future`'s result if + * that conforms to `S`'s erased type or a `ClassCastException` otherwise. + */ + def mapTo[S](implicit m: Manifest[S]): Future[S] = { + val p = newPromise[S] + + onComplete { + case l: Left[Throwable, _] => p complete l.asInstanceOf[Either[Throwable, S]] + case Right(t) => + p complete (try { + Right(impl.Future.boxedType(m.erasure).cast(t).asInstanceOf[S]) + } catch { + case e: ClassCastException => Left(e) + }) + } + + p.future + } + /** Applies the side-effecting function to the result of this future, and returns * a new future with the result of this future. * @@ -441,7 +471,7 @@ self => * } * }}} */ - def andThen[U](pf: PartialFunction[Try[T], U]): Future[T] = { + def andThen[U](pf: PartialFunction[Either[Throwable, T], U]): Future[T] = { val p = newPromise[T] onComplete { @@ -469,9 +499,9 @@ self => 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 + val completePromise: PartialFunction[Either[Throwable, U], _] = { + case Left(t) => p tryFailure t + case Right(v) => p trySuccess v } self onComplete completePromise @@ -510,7 +540,7 @@ object Future { def firstCompletedOf[T](futures: Traversable[Future[T]])(implicit executor: ExecutionContext): Future[T] = { val p = Promise[T]() - val completeFirst: Try[T] => Unit = p tryComplete _ + val completeFirst: Either[Throwable, T] => Unit = p tryComplete _ futures.foreach(_ onComplete completeFirst) p.future @@ -523,14 +553,14 @@ object Future { else { val result = Promise[Option[T]]() val ref = new AtomicInteger(futures.size) - val search: Try[T] => Unit = v => try { + val search: Either[Throwable, T] => Unit = v => try { v match { - case Success(r) => if (predicate(r)) result tryComplete Success(Some(r)) + case Right(r) => if (predicate(r)) result tryComplete Right(Some(r)) case _ => } } finally { if (ref.decrementAndGet == 0) - result tryComplete Success(None) + result tryComplete Right(None) } futures.foreach(_ onComplete search) diff --git a/src/library/scala/concurrent/Promise.scala b/src/library/scala/concurrent/Promise.scala index 61e21606e6..8f2bce5d1a 100644 --- a/src/library/scala/concurrent/Promise.scala +++ b/src/library/scala/concurrent/Promise.scala @@ -8,11 +8,6 @@ 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. * @@ -40,7 +35,7 @@ trait Promise[T] { * * $promiseCompletion */ - def complete(result:Try[T]): this.type = if (tryComplete(result)) this else throwCompleted + def complete(result: Either[Throwable, T]): this.type = if (tryComplete(result)) this else throwCompleted /** Tries to complete the promise with either a value or the exception. * @@ -48,7 +43,7 @@ trait Promise[T] { * * @return If the promise has already been completed returns `false`, or `true` otherwise. */ - def tryComplete(result: Try[T]): Boolean + def tryComplete(result: Either[Throwable, T]): Boolean /** Completes this promise with the specified future, once that future is completed. * @@ -75,7 +70,7 @@ trait Promise[T] { * * @return If the promise has already been completed returns `false`, or `true` otherwise. */ - def trySuccess(value: T): Boolean = tryComplete(Success(value)) + def trySuccess(value: T): Boolean = tryComplete(Right(value)) /** Completes the promise with an exception. * @@ -93,7 +88,7 @@ trait Promise[T] { * * @return If the promise has already been completed returns `false`, or `true` otherwise. */ - def tryFailure(t: Throwable): Boolean = tryComplete(Failure(t)) + def tryFailure(t: Throwable): Boolean = tryComplete(Left(t)) /** Wraps a `Throwable` in an `ExecutionException` if necessary. TODO replace with `resolver` from scala.concurrent * @@ -118,11 +113,11 @@ object Promise { /** Creates an already completed Promise with the specified exception */ - def failed[T](exception: Throwable)(implicit executor: ExecutionContext): Promise[T] = new impl.Promise.KeptPromise[T](Failure(exception)) + def failed[T](exception: Throwable)(implicit executor: ExecutionContext): Promise[T] = new impl.Promise.KeptPromise[T](Left(exception)) /** Creates an already completed Promise with the specified result */ - def successful[T](result: T)(implicit executor: ExecutionContext): Promise[T] = new impl.Promise.KeptPromise[T](Success(result)) + def successful[T](result: T)(implicit executor: ExecutionContext): Promise[T] = new impl.Promise.KeptPromise[T](Right(result)) } diff --git a/src/library/scala/concurrent/impl/ExecutionContextImpl.scala b/src/library/scala/concurrent/impl/ExecutionContextImpl.scala index 5dc440f42b..9a94bfca4f 100644 --- a/src/library/scala/concurrent/impl/ExecutionContextImpl.scala +++ b/src/library/scala/concurrent/impl/ExecutionContextImpl.scala @@ -10,16 +10,49 @@ package scala.concurrent.impl -import java.util.concurrent.{Callable, ExecutorService} +import java.util.concurrent.{Callable, ExecutorService, Executors, ThreadFactory} import scala.concurrent.forkjoin._ import scala.concurrent.{ExecutionContext, resolver, Awaitable, body2awaitable} -import scala.util.{ Try, Success, Failure } import scala.concurrent.util.{ Duration } -private[scala] class ExecutionContextImpl(val executorService: AnyRef) extends ExecutionContext { +private[scala] class ExecutionContextImpl() extends ExecutionContext { import ExecutionContextImpl._ + + val executorService: AnyRef = getExecutorService + + // to ensure that the current execution context thread local is properly set + private def executorsThreadFactory = new ThreadFactory { + def newThread(r: Runnable) = new Thread(new Runnable { + override def run() { + currentExecutionContext.set(ExecutionContextImpl.this) + r.run() + } + }) + } + + // to ensure that the current execution context thread local is properly set + private def forkJoinPoolThreadFactory = new ForkJoinPool.ForkJoinWorkerThreadFactory { + def newThread(fjp: ForkJoinPool) = new ForkJoinWorkerThread(fjp) { + override def onStart() { + currentExecutionContext.set(ExecutionContextImpl.this) + } + } + } + + private def getExecutorService: AnyRef = + if (scala.util.Properties.isJavaAtLeast("1.6")) { + val vendor = scala.util.Properties.javaVmVendor + if ((vendor contains "Oracle") || (vendor contains "Sun") || (vendor contains "Apple")) + new ForkJoinPool( + Runtime.getRuntime.availableProcessors(), + forkJoinPoolThreadFactory, + null, + false) + else + Executors.newCachedThreadPool(executorsThreadFactory) + } else Executors.newCachedThreadPool(executorsThreadFactory) def execute(runnable: Runnable): Unit = executorService match { case fj: ForkJoinPool => @@ -37,9 +70,7 @@ private[scala] class ExecutionContextImpl(val executorService: AnyRef) extends E def run() = body() }) - def blocking[T](body: =>T): T = blocking(body2awaitable(body), Duration.fromNanos(0)) - - def blocking[T](awaitable: Awaitable[T], atMost: Duration): T = { + def internalBlockingCall[T](awaitable: Awaitable[T], atMost: Duration): T = { Future.releaseStack(this) awaitable.result(atMost)(scala.concurrent.Await.canAwaitEvidence) diff --git a/src/library/scala/concurrent/impl/Future.scala b/src/library/scala/concurrent/impl/Future.scala index 6833b2467f..615ab061a5 100644 --- a/src/library/scala/concurrent/impl/Future.scala +++ b/src/library/scala/concurrent/impl/Future.scala @@ -11,11 +11,8 @@ package scala.concurrent.impl import scala.concurrent.{Awaitable, ExecutionContext} -import scala.util.{ Try, Success, Failure } import scala.collection.mutable.Stack - - private[concurrent] trait Future[+T] extends scala.concurrent.Future[T] with Awaitable[T] { implicit def executor: ExecutionContext @@ -36,39 +33,9 @@ private[concurrent] trait Future[+T] extends scala.concurrent.Future[T] with Awa * 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 = new Promise.DefaultPromise[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) - }) - } + def value: Option[Either[Throwable, T]] - 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)) - } + def onComplete[U](func: Either[Throwable, T] => U): this.type } @@ -97,7 +64,7 @@ object Future { def run = { promise complete { try { - Success(body) + Right(body) } catch { case e => scala.concurrent.resolver(e) } diff --git a/src/library/scala/concurrent/impl/Promise.scala b/src/library/scala/concurrent/impl/Promise.scala index c79b0d02cc..f7e073cb78 100644 --- a/src/library/scala/concurrent/impl/Promise.scala +++ b/src/library/scala/concurrent/impl/Promise.scala @@ -15,7 +15,6 @@ import java.util.concurrent.atomic.AtomicReferenceFieldUpdater import scala.concurrent.{Awaitable, ExecutionContext, resolve, resolver, blocking, CanAwait, TimeoutException} //import scala.util.continuations._ import scala.concurrent.util.Duration -import scala.util.Try import scala.util import scala.annotation.tailrec //import scala.concurrent.NonDeterministic @@ -84,18 +83,18 @@ object Promise { * * [adriaan] it's unsound to make FState covariant (tryComplete won't type check) */ - sealed trait FState[T] { def value: Option[Try[T]] } + sealed trait FState[T] { def value: Option[Either[Throwable, T]] } - case class Pending[T](listeners: List[Try[T] => Any] = Nil) extends FState[T] { - def value: Option[Try[T]] = None + case class Pending[T](listeners: List[Either[Throwable, T] => Any] = Nil) extends FState[T] { + def value: Option[Either[Throwable, T]] = None } - case class Success[T](value: Option[util.Success[T]] = None) extends FState[T] { - def result: T = value.get.get + case class Success[T](value: Option[Either[Throwable, T]] = None) extends FState[T] { + def result: T = value.get.right.get } - case class Failure[T](value: Option[util.Failure[T]] = None) extends FState[T] { - def exception: Throwable = value.get.exception + case class Failure[T](value: Option[Either[Throwable, T]] = None) extends FState[T] { + def exception: Throwable = value.get.left.get } private val emptyPendingValue = Pending[Nothing](Nil) @@ -127,7 +126,7 @@ object Promise { value.isDefined } - executor.blocking(concurrent.body2awaitable(awaitUnsafe(dur2long(atMost))), atMost) + blocking(concurrent.body2awaitable(awaitUnsafe(dur2long(atMost))), atMost) } def ready(atMost: Duration)(implicit permit: CanAwait): this.type = @@ -136,11 +135,11 @@ object Promise { def result(atMost: Duration)(implicit permit: CanAwait): T = ready(atMost).value.get match { - case util.Failure(e) => throw e - case util.Success(r) => r + case Left(e) => throw e + case Right(r) => r } - def value: Option[Try[T]] = getState.value + def value: Option[Either[Throwable, T]] = getState.value @inline private[this] final def updater = AbstractPromise.updater.asInstanceOf[AtomicReferenceFieldUpdater[AbstractPromise, FState[T]]] @@ -151,16 +150,16 @@ object Promise { @inline protected final def getState: FState[T] = updater.get(this) - def tryComplete(value: Try[T]): Boolean = { - val callbacks: List[Try[T] => Any] = { + def tryComplete(value: Either[Throwable, T]): Boolean = { + val callbacks: List[Either[Throwable, T] => Any] = { try { @tailrec - def tryComplete(v: Try[T]): List[Try[T] => Any] = { + def tryComplete(v: Either[Throwable, T]): List[Either[Throwable, T] => Any] = { getState match { case cur @ Pending(listeners) => val newState = - if (v.isFailure) Failure(Some(v.asInstanceOf[util.Failure[T]])) - else Success(Some(v.asInstanceOf[util.Success[T]])) + if (v.isLeft) Failure(Some(v.asInstanceOf[Left[Throwable, T]])) + else Success(Some(v.asInstanceOf[Right[Throwable, T]])) if (updateState(cur, newState)) listeners else tryComplete(v) @@ -184,7 +183,7 @@ object Promise { } } - def onComplete[U](func: Try[T] => U): this.type = { + def onComplete[U](func: Either[Throwable, T] => U): this.type = { @tailrec // Returns whether the future has already been completed or not def tryAddCallback(): Boolean = { val cur = getState @@ -206,7 +205,7 @@ object Promise { this } - private final def notifyCompleted(func: Try[T] => Any, result: Try[T]) { + private final def notifyCompleted(func: Either[Throwable, T] => Any, result: Either[Throwable, T]) { try { func(result) } catch { @@ -219,12 +218,13 @@ object Promise { * * Useful in Future-composition when a value to contribute is already available. */ - final class KeptPromise[T](suppliedValue: Try[T])(implicit val executor: ExecutionContext) extends Promise[T] { + final class KeptPromise[T](suppliedValue: Either[Throwable, T])(implicit val executor: ExecutionContext) extends Promise[T] { + val value = Some(resolve(suppliedValue)) - def tryComplete(value: Try[T]): Boolean = false + def tryComplete(value: Either[Throwable, T]): Boolean = false - def onComplete[U](func: Try[T] => U): this.type = { + def onComplete[U](func: Either[Throwable, T] => U): this.type = { val completedAs = value.get Future.dispatchFuture(executor, { () => func(completedAs) @@ -235,8 +235,8 @@ object Promise { def ready(atMost: Duration)(implicit permit: CanAwait): this.type = this def result(atMost: Duration)(implicit permit: CanAwait): T = value.get match { - case util.Failure(e) => throw e - case util.Success(r) => r + case Left(e) => throw e + case Right(r) => r } } diff --git a/src/library/scala/math/BigDecimal.scala b/src/library/scala/math/BigDecimal.scala index c1f45eccfb..cb42b76b51 100644 --- a/src/library/scala/math/BigDecimal.scala +++ b/src/library/scala/math/BigDecimal.scala @@ -183,7 +183,8 @@ extends ScalaNumber with ScalaNumericConversions with Serializable { override def equals (that: Any): Boolean = that match { case that: BigDecimal => this equals that case that: BigInt => this.toBigIntExact exists (that equals _) - case _: Float | _: Double => unifiedPrimitiveEquals(that) + case that: Double => isValidDouble && toDouble == that + case that: Float => isValidFloat && toFloat == that case _ => isValidLong && unifiedPrimitiveEquals(that) } override def isValidByte = noArithmeticException(toByteExact) @@ -191,6 +192,18 @@ extends ScalaNumber with ScalaNumericConversions with Serializable { override def isValidChar = isValidInt && toIntExact >= Char.MinValue && toIntExact <= Char.MaxValue override def isValidInt = noArithmeticException(toIntExact) def isValidLong = noArithmeticException(toLongExact) + /** Returns `true` iff this can be represented exactly by [[scala.Float]]; otherwise returns `false`. + */ + def isValidFloat = { + val f = toFloat + !f.isInfinity && bigDecimal.compareTo(new java.math.BigDecimal(f)) == 0 + } + /** Returns `true` iff this can be represented exactly by [[scala.Double]]; otherwise returns `false`. + */ + def isValidDouble = { + val d = toDouble + !d.isInfinity && bigDecimal.compareTo(new java.math.BigDecimal(d)) == 0 + } private def noArithmeticException(body: => Unit): Boolean = { try { body ; true } diff --git a/src/library/scala/math/BigInt.scala b/src/library/scala/math/BigInt.scala index 8a53afaa62..dbec30b2fe 100644 --- a/src/library/scala/math/BigInt.scala +++ b/src/library/scala/math/BigInt.scala @@ -20,6 +20,7 @@ object BigInt { private val minCached = -1024 private val maxCached = 1024 private val cache = new Array[BigInt](maxCached - minCached + 1) + private val minusOne = BigInteger.valueOf(-1) @deprecated("Use Long.MinValue", "2.9.0") val MinLong = BigInt(Long.MinValue) @@ -122,6 +123,8 @@ class BigInt(val bigInteger: BigInteger) extends ScalaNumber with ScalaNumericCo override def equals(that: Any): Boolean = that match { case that: BigInt => this equals that case that: BigDecimal => that.toBigIntExact exists (this equals _) + case that: Double => isValidDouble && toDouble == that + case that: Float => isValidFloat && toFloat == that case x => isValidLong && unifiedPrimitiveEquals(x) } override def isValidByte = this >= Byte.MinValue && this <= Byte.MaxValue @@ -129,6 +132,41 @@ class BigInt(val bigInteger: BigInteger) extends ScalaNumber with ScalaNumericCo override def isValidChar = this >= Char.MinValue && this <= Char.MaxValue override def isValidInt = this >= Int.MinValue && this <= Int.MaxValue def isValidLong = this >= Long.MinValue && this <= Long.MaxValue + /** Returns `true` iff this can be represented exactly by [[scala.Float]]; otherwise returns `false`. + */ + def isValidFloat = { + val bitLen = bitLength + (bitLen <= 24 || + { + val lowest = lowestSetBit + bitLen <= java.lang.Float.MAX_EXPONENT + 1 && // exclude this < -2^128 && this >= 2^128 + lowest >= bitLen - 24 && + lowest < java.lang.Float.MAX_EXPONENT + 1 // exclude this == -2^128 + } + ) && !bitLengthOverflow + } + /** Returns `true` iff this can be represented exactly by [[scala.Double]]; otherwise returns `false`. + */ + def isValidDouble = { + val bitLen = bitLength + (bitLen <= 53 || + { + val lowest = lowestSetBit + bitLen <= java.lang.Double.MAX_EXPONENT + 1 && // exclude this < -2^1024 && this >= 2^1024 + lowest >= bitLen - 53 && + lowest < java.lang.Double.MAX_EXPONENT + 1 // exclude this == -2^1024 + } + ) && !bitLengthOverflow + } + /** Some implementations of java.math.BigInteger allow huge values with bit length greater than Int.MaxValue . + * The BigInteger.bitLength method returns truncated bit length in this case . + * This method tests if result of bitLength is valid. + * This method will become unnecessary if BigInt constructors reject huge BigIntegers. + */ + private def bitLengthOverflow = { + val shifted = bigInteger.shiftRight(Int.MaxValue) + (shifted.signum != 0) && !(shifted equals BigInt.minusOne) + } protected[math] def isWhole = true def underlying = bigInteger diff --git a/src/library/scala/util/Try.scala b/src/library/scala/util/Try.scala index c9bde81317..efa2fcabb8 100644 --- a/src/library/scala/util/Try.scala +++ b/src/library/scala/util/Try.scala @@ -101,9 +101,9 @@ sealed abstract class Try[+T] { } -final case class Failure[+T](val exception: Throwable) extends Try[T] { - def isFailure = true - def isSuccess = false +final class Failure[+T](val exception: Throwable) extends Try[T] { + def isFailure: Boolean = true + def isSuccess: Boolean = false def rescue[U >: T](rescueException: PartialFunction[Throwable, Try[U]]): Try[U] = { try { if (rescueException.isDefinedAt(exception)) rescueException(exception) else this @@ -129,30 +129,49 @@ final case class Failure[+T](val exception: Throwable) extends Try[T] { } -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 +final class Success[+T](value: T) extends Try[T] { + def isFailure: Boolean = false + def isSuccess: Boolean = true + def rescue[U >: T](rescueException: PartialFunction[Throwable, Try[U]]): Try[U] = Success(value) + def get = value def flatMap[U](f: T => Try[U]): Try[U] = - try f(r) + try f(value) 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 flatten[U](implicit ev: T <:< Try[U]): Try[U] = value + def foreach[U](f: T => U): Unit = f(value) + def map[U](f: T => U): Try[U] = Try[U](f(value)) 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)) + if (pf isDefinedAt value) Success(pf(value)) + else Failure[U](new NoSuchElementException("Partial function not defined at " + value)) def filter(p: T => Boolean): Try[T] = - if (p(r)) this - else Failure(new NoSuchElementException("Predicate does not hold for " + r)) + if (p(value)) this + else Failure(new NoSuchElementException("Predicate does not hold for " + value)) def recover[U >: T](rescueException: PartialFunction[Throwable, U]): Try[U] = this - def exists(p: T => Boolean): Boolean = p(r) + def exists(p: T => Boolean): Boolean = p(value) def failed: Try[Throwable] = Failure(new UnsupportedOperationException("Success.failed")) } +object Failure { + def apply[T](e: Throwable): Failure[T] = new Failure(e) + def unapply(scrutinizee: Any): Option[Throwable] = scrutinizee match { + case Right(_) => None + case Left(e) => Some(e.asInstanceOf[Throwable]) + case s: Success[_] => None + case f: Failure[_] => Some(f.exception) + } +} + +object Success { + def apply[T](value: T): Success[T] = new Success(value) + def unapply[T](scrutinizee: Any): Option[T] = scrutinizee match { + case Right(v) => Some(v.asInstanceOf[T]) + case Left(_) => None + case s: Success[_] => Some(s.get.asInstanceOf[T]) + case f: Failure[Throwable] => None + } +} object Try { |