From 1852a7ddf7f8c5fb4a85e64b73123d333e698932 Mon Sep 17 00:00:00 2001 From: Aleksandar Prokopec Date: Thu, 23 Feb 2012 16:34:34 +0100 Subject: Add tasksupport as a configurable field in parallel collections. This required a bit of refactoring in the tasks objects and implementations of various operations. Combiners now hold a reference to a tasksupport object and pass it on to their result if `resultWithTaskSupport` is called. Additionally, several bugs that have to do with CanBuildFrom and combiner resolution have been fixed. --- .../scala/collection/parallel/Combiner.scala | 25 ++- .../collection/parallel/ParIterableLike.scala | 191 ++++++++++++--------- .../collection/parallel/ParIterableViewLike.scala | 3 +- .../scala/collection/parallel/ParSeqLike.scala | 69 ++++---- .../scala/collection/parallel/ParSeqViewLike.scala | 3 +- src/library/scala/collection/parallel/Tasks.scala | 135 +++++++-------- .../collection/parallel/immutable/ParHashMap.scala | 14 +- .../collection/parallel/immutable/ParHashSet.scala | 10 +- .../collection/parallel/mutable/ParArray.scala | 12 +- .../collection/parallel/mutable/ParCtrie.scala | 4 +- .../collection/parallel/mutable/ParHashMap.scala | 7 +- .../collection/parallel/mutable/ParHashSet.scala | 10 +- .../mutable/ResizableParArrayCombiner.scala | 12 +- .../mutable/UnrolledParArrayCombiner.scala | 8 +- .../scala/collection/parallel/package.scala | 12 +- 15 files changed, 295 insertions(+), 220 deletions(-) (limited to 'src') diff --git a/src/library/scala/collection/parallel/Combiner.scala b/src/library/scala/collection/parallel/Combiner.scala index e304be92ae..69e3271d39 100644 --- a/src/library/scala/collection/parallel/Combiner.scala +++ b/src/library/scala/collection/parallel/Combiner.scala @@ -33,8 +33,21 @@ import scala.collection.generic.Sizing * @since 2.9 */ trait Combiner[-Elem, +To] extends Builder[Elem, To] with Sizing with Parallel { -//self: EnvironmentPassingCombiner[Elem, To] => - + + @transient + @volatile + var _combinerTaskSupport = defaultTaskSupport + + def combinerTaskSupport = { + val cts = _combinerTaskSupport + if (cts eq null) { + _combinerTaskSupport = defaultTaskSupport + defaultTaskSupport + } else cts + } + + def combinerTaskSupport_=(cts: TaskSupport) = _combinerTaskSupport = cts + /** Combines the contents of the receiver builder and the `other` builder, * producing a new builder containing both their elements. * @@ -69,6 +82,14 @@ trait Combiner[-Elem, +To] extends Builder[Elem, To] with Sizing with Parallel { */ def canBeShared: Boolean = false + /** Constructs the result and sets the appropriate tasksupport object to the resulting collection + * if this is applicable. + */ + def resultWithTaskSupport: To = { + val res = result + setTaskSupport(res, combinerTaskSupport) + } + } diff --git a/src/library/scala/collection/parallel/ParIterableLike.scala b/src/library/scala/collection/parallel/ParIterableLike.scala index 7c5a835e56..cffd3bfbcf 100644 --- a/src/library/scala/collection/parallel/ParIterableLike.scala +++ b/src/library/scala/collection/parallel/ParIterableLike.scala @@ -28,7 +28,7 @@ import immutable.HashMapCombiner import java.util.concurrent.atomic.AtomicBoolean import annotation.unchecked.uncheckedVariance - +import annotation.unchecked.uncheckedStable /** A template trait for parallel collections of type `ParIterable[T]`. @@ -155,7 +155,19 @@ extends GenIterableLike[T, Repr] { self: ParIterableLike[T, Repr, Sequential] => - import tasksupport._ + @transient + @volatile + private var _tasksupport = defaultTaskSupport + + def tasksupport = { + val ts = _tasksupport + if (ts eq null) { + _tasksupport = defaultTaskSupport + defaultTaskSupport + } else ts + } + + def tasksupport_=(ts: TaskSupport) = _tasksupport = ts def seq: Sequential @@ -306,7 +318,7 @@ self: ParIterableLike[T, Repr, Sequential] => * if this $coll is empty. */ def reduce[U >: T](op: (U, U) => U): U = { - executeAndWaitResult(new Reduce(op, splitter) mapResult { _.get }) + tasksupport.executeAndWaitResult(new Reduce(op, splitter) mapResult { _.get }) } /** Optionally reduces the elements of this sequence using the specified associative binary operator. @@ -341,7 +353,7 @@ self: ParIterableLike[T, Repr, Sequential] => * @return the result of applying fold operator `op` between all the elements and `z` */ def fold[U >: T](z: U)(op: (U, U) => U): U = { - executeAndWaitResult(new Fold(z, op, splitter)) + tasksupport.executeAndWaitResult(new Fold(z, op, splitter)) } /** Aggregates the results of applying an operator to subsequent elements. @@ -373,7 +385,7 @@ self: ParIterableLike[T, Repr, Sequential] => * @param combop an associative operator used to combine results from different partitions */ def aggregate[S](z: S)(seqop: (S, T) => S, combop: (S, S) => S): S = { - executeAndWaitResult(new Aggregate(z, seqop, combop, splitter)) + tasksupport.executeAndWaitResult(new Aggregate(z, seqop, combop, splitter)) } def foldLeft[S](z: S)(op: (S, T) => S): S = seq.foldLeft(z)(op) @@ -394,27 +406,27 @@ self: ParIterableLike[T, Repr, Sequential] => * @param f function applied to each element */ def foreach[U](f: T => U) = { - executeAndWaitResult(new Foreach(f, splitter)) + tasksupport.executeAndWaitResult(new Foreach(f, splitter)) } def count(p: T => Boolean): Int = { - executeAndWaitResult(new Count(p, splitter)) + tasksupport.executeAndWaitResult(new Count(p, splitter)) } def sum[U >: T](implicit num: Numeric[U]): U = { - executeAndWaitResult(new Sum[U](num, splitter)) + tasksupport.executeAndWaitResult(new Sum[U](num, splitter)) } def product[U >: T](implicit num: Numeric[U]): U = { - executeAndWaitResult(new Product[U](num, splitter)) + tasksupport.executeAndWaitResult(new Product[U](num, splitter)) } def min[U >: T](implicit ord: Ordering[U]): T = { - executeAndWaitResult(new Min(ord, splitter) mapResult { _.get }).asInstanceOf[T] + tasksupport.executeAndWaitResult(new Min(ord, splitter) mapResult { _.get }).asInstanceOf[T] } def max[U >: T](implicit ord: Ordering[U]): T = { - executeAndWaitResult(new Max(ord, splitter) mapResult { _.get }).asInstanceOf[T] + tasksupport.executeAndWaitResult(new Max(ord, splitter) mapResult { _.get }).asInstanceOf[T] } def maxBy[S](f: T => S)(implicit cmp: Ordering[S]): T = { @@ -430,24 +442,24 @@ self: ParIterableLike[T, Repr, Sequential] => } def map[S, That](f: T => S)(implicit bf: CanBuildFrom[Repr, S, That]): That = if (bf(repr).isCombiner) { - executeAndWaitResult(new Map[S, That](f, combinerFactory(() => bf(repr).asCombiner), splitter) mapResult { _.result }) - } else seq.map(f)(bf2seq(bf)) + tasksupport.executeAndWaitResult(new Map[S, That](f, combinerFactory(() => bf(repr).asCombiner), splitter) mapResult { _.resultWithTaskSupport }) + } else setTaskSupport(seq.map(f)(bf2seq(bf)), tasksupport) /*bf ifParallel { pbf => - executeAndWaitResult(new Map[S, That](f, pbf, splitter) mapResult { _.result }) + tasksupport.executeAndWaitResult(new Map[S, That](f, pbf, splitter) mapResult { _.result }) } otherwise seq.map(f)(bf2seq(bf))*/ def collect[S, That](pf: PartialFunction[T, S])(implicit bf: CanBuildFrom[Repr, S, That]): That = if (bf(repr).isCombiner) { - executeAndWaitResult(new Collect[S, That](pf, combinerFactory(() => bf(repr).asCombiner), splitter) mapResult { _.result }) - } else seq.collect(pf)(bf2seq(bf)) + tasksupport.executeAndWaitResult(new Collect[S, That](pf, combinerFactory(() => bf(repr).asCombiner), splitter) mapResult { _.resultWithTaskSupport }) + } else setTaskSupport(seq.collect(pf)(bf2seq(bf)), tasksupport) /*bf ifParallel { pbf => - executeAndWaitResult(new Collect[S, That](pf, pbf, splitter) mapResult { _.result }) + tasksupport.executeAndWaitResult(new Collect[S, That](pf, pbf, splitter) mapResult { _.result }) } otherwise seq.collect(pf)(bf2seq(bf))*/ def flatMap[S, That](f: T => GenTraversableOnce[S])(implicit bf: CanBuildFrom[Repr, S, That]): That = if (bf(repr).isCombiner) { - executeAndWaitResult(new FlatMap[S, That](f, combinerFactory(() => bf(repr).asCombiner), splitter) mapResult { _.result }) - } else seq.flatMap(f)(bf2seq(bf)) + tasksupport.executeAndWaitResult(new FlatMap[S, That](f, combinerFactory(() => bf(repr).asCombiner), splitter) mapResult { _.resultWithTaskSupport }) + } else setTaskSupport(seq.flatMap(f)(bf2seq(bf)), tasksupport) /*bf ifParallel { pbf => - executeAndWaitResult(new FlatMap[S, That](f, pbf, splitter) mapResult { _.result }) + tasksupport.executeAndWaitResult(new FlatMap[S, That](f, pbf, splitter) mapResult { _.result }) } otherwise seq.flatMap(f)(bf2seq(bf))*/ /** Tests whether a predicate holds for all elements of this $coll. @@ -458,7 +470,7 @@ self: ParIterableLike[T, Repr, Sequential] => * @return true if `p` holds for all elements, false otherwise */ def forall(pred: T => Boolean): Boolean = { - executeAndWaitResult(new Forall(pred, splitter assign new DefaultSignalling with VolatileAbort)) + tasksupport.executeAndWaitResult(new Forall(pred, splitter assign new DefaultSignalling with VolatileAbort)) } /** Tests whether a predicate holds for some element of this $coll. @@ -469,7 +481,7 @@ self: ParIterableLike[T, Repr, Sequential] => * @return true if `p` holds for some element, false otherwise */ def exists(pred: T => Boolean): Boolean = { - executeAndWaitResult(new Exists(pred, splitter assign new DefaultSignalling with VolatileAbort)) + tasksupport.executeAndWaitResult(new Exists(pred, splitter assign new DefaultSignalling with VolatileAbort)) } /** Finds some element in the collection for which the predicate holds, if such @@ -484,7 +496,7 @@ self: ParIterableLike[T, Repr, Sequential] => * @return an option value with the element if such an element exists, or `None` otherwise */ def find(pred: T => Boolean): Option[T] = { - executeAndWaitResult(new Find(pred, splitter assign new DefaultSignalling with VolatileAbort)) + tasksupport.executeAndWaitResult(new Find(pred, splitter assign new DefaultSignalling with VolatileAbort)) } /** Creates a combiner factory. Each combiner factory instance is used @@ -500,6 +512,7 @@ self: ParIterableLike[T, Repr, Sequential] => */ protected[this] def combinerFactory = { val combiner = newCombiner + combiner.combinerTaskSupport = tasksupport if (combiner.canBeShared) new CombinerFactory[T, Repr] { val shared = combiner def apply() = shared @@ -512,6 +525,7 @@ self: ParIterableLike[T, Repr, Sequential] => protected[this] def combinerFactory[S, That](cbf: () => Combiner[S, That]) = { val combiner = cbf() + combiner.combinerTaskSupport = tasksupport if (combiner.canBeShared) new CombinerFactory[S, That] { val shared = combiner def apply() = shared @@ -523,11 +537,11 @@ self: ParIterableLike[T, Repr, Sequential] => } def filter(pred: T => Boolean): Repr = { - executeAndWaitResult(new Filter(pred, combinerFactory, splitter) mapResult { _.result }) + tasksupport.executeAndWaitResult(new Filter(pred, combinerFactory, splitter) mapResult { _.resultWithTaskSupport }) } def filterNot(pred: T => Boolean): Repr = { - executeAndWaitResult(new FilterNot(pred, combinerFactory, splitter) mapResult { _.result }) + tasksupport.executeAndWaitResult(new FilterNot(pred, combinerFactory, splitter) mapResult { _.resultWithTaskSupport }) } def ++[U >: T, That](that: GenTraversableOnce[U])(implicit bf: CanBuildFrom[Repr, U, That]): That = { @@ -542,43 +556,47 @@ self: ParIterableLike[T, Repr, Sequential] => tasksupport.executeAndWaitResult(othtask) } val task = (copythis parallel copythat) { _ combine _ } mapResult { - _.result + _.resultWithTaskSupport } - executeAndWaitResult(task) - } else if (bf.isParallel) { + tasksupport.executeAndWaitResult(task) + } else if (bf(repr).isCombiner) { // println("case parallel builder, `that` not parallel") - val pbf = bf.asParallel - val copythis = new Copy(combinerFactory(() => pbf(repr)), splitter) + val copythis = new Copy(combinerFactory(() => bf(repr).asCombiner), splitter) val copythat = wrap { - val cb = pbf(repr) + val cb = bf(repr).asCombiner for (elem <- that.seq) cb += elem cb } - executeAndWaitResult((copythis parallel copythat) { _ combine _ } mapResult { _.result }) + tasksupport.executeAndWaitResult((copythis parallel copythat) { _ combine _ } mapResult { _.resultWithTaskSupport }) } else { // println("case not a parallel builder") val b = bf(repr) this.splitter.copy2builder[U, That, Builder[U, That]](b) for (elem <- that.seq) b += elem - b.result + setTaskSupport(b.result, tasksupport) } } def partition(pred: T => Boolean): (Repr, Repr) = { - executeAndWaitResult(new Partition(pred, combinerFactory, combinerFactory, splitter) mapResult { p => (p._1.result, p._2.result) }) + tasksupport.executeAndWaitResult( + new Partition(pred, combinerFactory, combinerFactory, splitter) mapResult { + p => (p._1.resultWithTaskSupport, p._2.resultWithTaskSupport) + } + ) } def groupBy[K](f: T => K): immutable.ParMap[K, Repr] = { - executeAndWaitResult(new GroupBy(f, () => HashMapCombiner[K, T], splitter) mapResult { + val r = tasksupport.executeAndWaitResult(new GroupBy(f, () => HashMapCombiner[K, T], splitter) mapResult { rcb => rcb.groupByKey(() => combinerFactory()) }) + setTaskSupport(r, tasksupport) } def take(n: Int): Repr = { val actualn = if (size > n) n else size if (actualn < MIN_FOR_COPY) take_sequential(actualn) - else executeAndWaitResult(new Take(actualn, combinerFactory, splitter) mapResult { - _.result + else tasksupport.executeAndWaitResult(new Take(actualn, combinerFactory, splitter) mapResult { + _.resultWithTaskSupport }) } @@ -591,13 +609,13 @@ self: ParIterableLike[T, Repr, Sequential] => cb += it.next left -= 1 } - cb.result + cb.resultWithTaskSupport } def drop(n: Int): Repr = { val actualn = if (size > n) n else size if ((size - actualn) < MIN_FOR_COPY) drop_sequential(actualn) - else executeAndWaitResult(new Drop(actualn, combinerFactory, splitter) mapResult { _.result }) + else tasksupport.executeAndWaitResult(new Drop(actualn, combinerFactory, splitter) mapResult { _.resultWithTaskSupport }) } private def drop_sequential(n: Int) = { @@ -605,14 +623,14 @@ self: ParIterableLike[T, Repr, Sequential] => val cb = newCombiner cb.sizeHint(size - n) while (it.hasNext) cb += it.next - cb.result + cb.resultWithTaskSupport } override def slice(unc_from: Int, unc_until: Int): Repr = { val from = unc_from min size max 0 val until = unc_until min size max from if ((until - from) <= MIN_FOR_COPY) slice_sequential(from, until) - else executeAndWaitResult(new Slice(from, until, combinerFactory, splitter) mapResult { _.result }) + else tasksupport.executeAndWaitResult(new Slice(from, until, combinerFactory, splitter) mapResult { _.resultWithTaskSupport }) } private def slice_sequential(from: Int, until: Int): Repr = { @@ -623,11 +641,15 @@ self: ParIterableLike[T, Repr, Sequential] => cb += it.next left -= 1 } - cb.result + cb.resultWithTaskSupport } def splitAt(n: Int): (Repr, Repr) = { - executeAndWaitResult(new SplitAt(n, combinerFactory, combinerFactory, splitter) mapResult { p => (p._1.result, p._2.result) }) + tasksupport.executeAndWaitResult( + new SplitAt(n, combinerFactory, combinerFactory, splitter) mapResult { + p => (p._1.resultWithTaskSupport, p._2.resultWithTaskSupport) + } + ) } /** Computes a prefix scan of the elements of the collection. @@ -645,20 +667,19 @@ self: ParIterableLike[T, Repr, Sequential] => * * @return a new $coll containing the prefix scan of the elements in this $coll */ - def scan[U >: T, That](z: U)(op: (U, U) => U)(implicit bf: CanBuildFrom[Repr, U, That]): That = if (bf.isParallel) { - val cbf = bf.asParallel - if (parallelismLevel > 1) { - if (size > 0) executeAndWaitResult(new CreateScanTree(0, size, z, op, splitter) mapResult { - tree => executeAndWaitResult(new FromScanTree(tree, z, op, combinerFactory(() => cbf(repr))) mapResult { - cb => cb.result + def scan[U >: T, That](z: U)(op: (U, U) => U)(implicit bf: CanBuildFrom[Repr, U, That]): That = if (bf(repr).isCombiner) { + if (tasksupport.parallelismLevel > 1) { + if (size > 0) tasksupport.executeAndWaitResult(new CreateScanTree(0, size, z, op, splitter) mapResult { + tree => tasksupport.executeAndWaitResult(new FromScanTree(tree, z, op, combinerFactory(() => bf(repr).asCombiner)) mapResult { + cb => cb.resultWithTaskSupport }) - }) else (cbf(self.repr) += z).result - } else seq.scan(z)(op)(bf2seq(bf)) - } else seq.scan(z)(op)(bf2seq(bf)) + }) else setTaskSupport((bf(repr) += z).result, tasksupport) + } else setTaskSupport(seq.scan(z)(op)(bf2seq(bf)), tasksupport) + } else setTaskSupport(seq.scan(z)(op)(bf2seq(bf)), tasksupport) - def scanLeft[S, That](z: S)(op: (S, T) => S)(implicit bf: CanBuildFrom[Repr, S, That]) = seq.scanLeft(z)(op)(bf2seq(bf)) + def scanLeft[S, That](z: S)(op: (S, T) => S)(implicit bf: CanBuildFrom[Repr, S, That]) = setTaskSupport(seq.scanLeft(z)(op)(bf2seq(bf)), tasksupport) - def scanRight[S, That](z: S)(op: (T, S) => S)(implicit bf: CanBuildFrom[Repr, S, That]) = seq.scanRight(z)(op)(bf2seq(bf)) + def scanRight[S, That](z: S)(op: (T, S) => S)(implicit bf: CanBuildFrom[Repr, S, That]) = setTaskSupport(seq.scanRight(z)(op)(bf2seq(bf)), tasksupport) /** Takes the longest prefix of elements that satisfy the predicate. * @@ -672,11 +693,15 @@ self: ParIterableLike[T, Repr, Sequential] => val cbf = combinerFactory if (cbf.doesShareCombiners) { val parseqspan = toSeq.takeWhile(pred) - executeAndWaitResult(new Copy(combinerFactory, parseqspan.splitter) mapResult { _.result }) + tasksupport.executeAndWaitResult(new Copy(combinerFactory, parseqspan.splitter) mapResult { + _.resultWithTaskSupport + }) } else { val cntx = new DefaultSignalling with AtomicIndexFlag cntx.setIndexFlag(Int.MaxValue) - executeAndWaitResult(new TakeWhile(0, pred, combinerFactory, splitter assign cntx) mapResult { _._1.result }) + tasksupport.executeAndWaitResult(new TakeWhile(0, pred, combinerFactory, splitter assign cntx) mapResult { + _._1.resultWithTaskSupport + }) } } @@ -693,17 +718,17 @@ self: ParIterableLike[T, Repr, Sequential] => val cbf = combinerFactory if (cbf.doesShareCombiners) { val (xs, ys) = toSeq.span(pred) - val copyxs = new Copy(combinerFactory, xs.splitter) mapResult { _.result } - val copyys = new Copy(combinerFactory, ys.splitter) mapResult { _.result } + val copyxs = new Copy(combinerFactory, xs.splitter) mapResult { _.resultWithTaskSupport } + val copyys = new Copy(combinerFactory, ys.splitter) mapResult { _.resultWithTaskSupport } val copyall = (copyxs parallel copyys) { (xr, yr) => (xr, yr) } - executeAndWaitResult(copyall) + tasksupport.executeAndWaitResult(copyall) } else { val cntx = new DefaultSignalling with AtomicIndexFlag cntx.setIndexFlag(Int.MaxValue) - executeAndWaitResult(new Span(0, pred, combinerFactory, combinerFactory, splitter assign cntx) mapResult { - p => (p._1.result, p._2.result) + tasksupport.executeAndWaitResult(new Span(0, pred, combinerFactory, combinerFactory, splitter assign cntx) mapResult { + p => (p._1.resultWithTaskSupport, p._2.resultWithTaskSupport) }) } } @@ -721,7 +746,11 @@ self: ParIterableLike[T, Repr, Sequential] => def dropWhile(pred: T => Boolean): Repr = { val cntx = new DefaultSignalling with AtomicIndexFlag cntx.setIndexFlag(Int.MaxValue) - executeAndWaitResult(new Span(0, pred, combinerFactory, combinerFactory, splitter assign cntx) mapResult { _._2.result }) + tasksupport.executeAndWaitResult( + new Span(0, pred, combinerFactory, combinerFactory, splitter assign cntx) mapResult { + _._2.resultWithTaskSupport + } + ) } def copyToArray[U >: T](xs: Array[U]) = copyToArray(xs, 0) @@ -729,31 +758,33 @@ self: ParIterableLike[T, Repr, Sequential] => def copyToArray[U >: T](xs: Array[U], start: Int) = copyToArray(xs, start, xs.length - start) def copyToArray[U >: T](xs: Array[U], start: Int, len: Int) = if (len > 0) { - executeAndWaitResult(new CopyToArray(start, len, xs, splitter)) + tasksupport.executeAndWaitResult(new CopyToArray(start, len, xs, splitter)) } def sameElements[U >: T](that: GenIterable[U]) = seq.sameElements(that) - def zip[U >: T, S, That](that: GenIterable[S])(implicit bf: CanBuildFrom[Repr, (U, S), That]): That = if (bf.isParallel && that.isParSeq) { - val pbf = bf.asParallel + def zip[U >: T, S, That](that: GenIterable[S])(implicit bf: CanBuildFrom[Repr, (U, S), That]): That = if (bf(repr).isCombiner && that.isParSeq) { val thatseq = that.asParSeq - executeAndWaitResult(new Zip(combinerFactory(() => pbf(repr)), splitter, thatseq.splitter) mapResult { _.result }); - } else seq.zip(that)(bf2seq(bf)) + tasksupport.executeAndWaitResult(new Zip(combinerFactory(() => bf(repr).asCombiner), splitter, thatseq.splitter) mapResult { _.resultWithTaskSupport }); + } else setTaskSupport(seq.zip(that)(bf2seq(bf)), tasksupport) def zipWithIndex[U >: T, That](implicit bf: CanBuildFrom[Repr, (U, Int), That]): That = this zip immutable.ParRange(0, size, 1, false) - def zipAll[S, U >: T, That](that: GenIterable[S], thisElem: U, thatElem: S)(implicit bf: CanBuildFrom[Repr, (U, S), That]): That = if (bf.isParallel && that.isParSeq) { - val pbf = bf.asParallel + def zipAll[S, U >: T, That](that: GenIterable[S], thisElem: U, thatElem: S)(implicit bf: CanBuildFrom[Repr, (U, S), That]): That = if (bf(repr).isCombiner && that.isParSeq) { val thatseq = that.asParSeq - executeAndWaitResult(new ZipAll(size max thatseq.length, thisElem, thatElem, combinerFactory(() => pbf(repr)), splitter, thatseq.splitter) mapResult { _.result }); - } else seq.zipAll(that, thisElem, thatElem)(bf2seq(bf)) + tasksupport.executeAndWaitResult( + new ZipAll(size max thatseq.length, thisElem, thatElem, combinerFactory(() => bf(repr).asCombiner), splitter, thatseq.splitter) mapResult { + _.resultWithTaskSupport + } + ); + } else setTaskSupport(seq.zipAll(that, thisElem, thatElem)(bf2seq(bf)), tasksupport) protected def toParCollection[U >: T, That](cbf: () => Combiner[U, That]): That = { - executeAndWaitResult(new ToParCollection(combinerFactory(cbf), splitter) mapResult { _.result }); + tasksupport.executeAndWaitResult(new ToParCollection(combinerFactory(cbf), splitter) mapResult { _.resultWithTaskSupport }); } protected def toParMap[K, V, That](cbf: () => Combiner[(K, V), That])(implicit ev: T <:< (K, V)): That = { - executeAndWaitResult(new ToParMap(combinerFactory(cbf), splitter)(ev) mapResult { _.result }) + tasksupport.executeAndWaitResult(new ToParMap(combinerFactory(cbf), splitter)(ev) mapResult { _.resultWithTaskSupport }) } def view = new ParIterableView[T, Repr, Sequential] { @@ -810,7 +841,7 @@ self: ParIterableLike[T, Repr, Sequential] => extends StrictSplitterCheckTask[R, Tp] { protected[this] val pit: IterableSplitter[T] protected[this] def newSubtask(p: IterableSplitter[T]): Accessor[R, Tp] - def shouldSplitFurther = pit.shouldSplitFurther(self.repr, parallelismLevel) + def shouldSplitFurther = pit.shouldSplitFurther(self.repr, tasksupport.parallelismLevel) def split = pit.splitWithSignalling.map(newSubtask(_)) // default split procedure private[parallel] override def signalAbort = pit.abort override def toString = this.getClass.getSimpleName + "(" + pit.toString + ")(" + result + ")(supername: " + super.toString + ")" @@ -844,8 +875,8 @@ self: ParIterableLike[T, Repr, Sequential] => (f: First, s: Second) extends Composite[FR, SR, R, First, Second](f, s) { def leaf(prevr: Option[R]) = { - executeAndWaitResult(ft) - executeAndWaitResult(st) + tasksupport.executeAndWaitResult(ft) + tasksupport.executeAndWaitResult(st) mergeSubtasks } } @@ -855,8 +886,8 @@ self: ParIterableLike[T, Repr, Sequential] => (f: First, s: Second) extends Composite[FR, SR, R, First, Second](f, s) { def leaf(prevr: Option[R]) = { - val ftfuture = execute(ft) - executeAndWaitResult(st) + val ftfuture = tasksupport.execute(ft) + tasksupport.executeAndWaitResult(st) ftfuture() mergeSubtasks } @@ -867,7 +898,7 @@ self: ParIterableLike[T, Repr, Sequential] => @volatile var result: R1 = null.asInstanceOf[R1] def map(r: R): R1 def leaf(prevr: Option[R1]) = { - val initialResult = executeAndWaitResult(inner) + val initialResult = tasksupport.executeAndWaitResult(inner) result = map(initialResult) } private[parallel] override def signalAbort() { @@ -1339,7 +1370,7 @@ self: ParIterableLike[T, Repr, Sequential] => /* scan tree */ - protected[this] def scanBlockSize = (thresholdFromSize(size, parallelismLevel) / 2) max 1 + protected[this] def scanBlockSize = (thresholdFromSize(size, tasksupport.parallelismLevel) / 2) max 1 protected[this] trait ScanTree[U >: T] { def beginsAt: Int diff --git a/src/library/scala/collection/parallel/ParIterableViewLike.scala b/src/library/scala/collection/parallel/ParIterableViewLike.scala index 1d7659922c..536139c812 100644 --- a/src/library/scala/collection/parallel/ParIterableViewLike.scala +++ b/src/library/scala/collection/parallel/ParIterableViewLike.scala @@ -47,7 +47,6 @@ extends GenIterableView[T, Coll] with ParIterableLike[T, This, ThisSeq] { self => - import tasksupport._ override def foreach[U](f: T => U): Unit = super[ParIterableLike].foreach(f) override protected[this] def newCombiner: Combiner[T, This] = throw new UnsupportedOperationException(this + ".newCombiner"); @@ -135,7 +134,7 @@ self => newZippedAllTryParSeq(that, thisElem, thatElem).asInstanceOf[That] override def force[U >: T, That](implicit bf: CanBuildFrom[Coll, U, That]) = bf ifParallel { pbf => - executeAndWaitResult(new Force(pbf, splitter).mapResult(_.result).asInstanceOf[Task[That, ResultMapping[_, Force[U, That], That]]]) + tasksupport.executeAndWaitResult(new Force(pbf, splitter).mapResult(_.result).asInstanceOf[Task[That, ResultMapping[_, Force[U, That], That]]]) } otherwise { val b = bf(underlying) b ++= this.iterator diff --git a/src/library/scala/collection/parallel/ParSeqLike.scala b/src/library/scala/collection/parallel/ParSeqLike.scala index 6a5ee5c69b..3d498ab41b 100644 --- a/src/library/scala/collection/parallel/ParSeqLike.scala +++ b/src/library/scala/collection/parallel/ParSeqLike.scala @@ -44,8 +44,7 @@ trait ParSeqLike[+T, +Repr <: ParSeq[T], +Sequential <: Seq[T] with SeqLike[T, S extends scala.collection.GenSeqLike[T, Repr] with ParIterableLike[T, Repr, Sequential] { self => - import tasksupport._ - + type SuperParIterator = IterableSplitter[T] /** A more refined version of the iterator found in the `ParallelIterable` trait, @@ -107,7 +106,7 @@ self => val realfrom = if (from < 0) 0 else from val ctx = new DefaultSignalling with AtomicIndexFlag ctx.setIndexFlag(Int.MaxValue) - executeAndWaitResult(new SegmentLength(p, 0, splitter.psplitWithSignalling(realfrom, length - realfrom)(1) assign ctx))._1 + tasksupport.executeAndWaitResult(new SegmentLength(p, 0, splitter.psplitWithSignalling(realfrom, length - realfrom)(1) assign ctx))._1 } /** Finds the first element satisfying some predicate. @@ -125,7 +124,7 @@ self => val realfrom = if (from < 0) 0 else from val ctx = new DefaultSignalling with AtomicIndexFlag ctx.setIndexFlag(Int.MaxValue) - executeAndWaitResult(new IndexWhere(p, realfrom, splitter.psplitWithSignalling(realfrom, length - realfrom)(1) assign ctx)) + tasksupport.executeAndWaitResult(new IndexWhere(p, realfrom, splitter.psplitWithSignalling(realfrom, length - realfrom)(1) assign ctx)) } /** Finds the last element satisfying some predicate. @@ -143,18 +142,20 @@ self => val until = if (end >= length) length else end + 1 val ctx = new DefaultSignalling with AtomicIndexFlag ctx.setIndexFlag(Int.MinValue) - executeAndWaitResult(new LastIndexWhere(p, 0, splitter.psplitWithSignalling(until, length - until)(0) assign ctx)) + tasksupport.executeAndWaitResult(new LastIndexWhere(p, 0, splitter.psplitWithSignalling(until, length - until)(0) assign ctx)) } def reverse: Repr = { - executeAndWaitResult(new Reverse(() => newCombiner, splitter) mapResult { _.result }) + tasksupport.executeAndWaitResult(new Reverse(() => newCombiner, splitter) mapResult { _.resultWithTaskSupport }) } def reverseMap[S, That](f: T => S)(implicit bf: CanBuildFrom[Repr, S, That]): That = if (bf(repr).isCombiner) { - executeAndWaitResult(new ReverseMap[S, That](f, () => bf(repr).asCombiner, splitter) mapResult { _.result }) - } else seq.reverseMap(f)(bf2seq(bf)) + tasksupport.executeAndWaitResult( + new ReverseMap[S, That](f, () => bf(repr).asCombiner, splitter) mapResult { _.resultWithTaskSupport } + ) + } else setTaskSupport(seq.reverseMap(f)(bf2seq(bf)), tasksupport) /*bf ifParallel { pbf => - executeAndWaitResult(new ReverseMap[S, That](f, pbf, splitter) mapResult { _.result }) + tasksupport.executeAndWaitResult(new ReverseMap[S, That](f, pbf, splitter) mapResult { _.result }) } otherwise seq.reverseMap(f)(bf2seq(bf))*/ /** Tests whether this $coll contains the given sequence at a given index. @@ -172,13 +173,15 @@ self => else if (pthat.length > length - offset) false else { val ctx = new DefaultSignalling with VolatileAbort - executeAndWaitResult(new SameElements(splitter.psplitWithSignalling(offset, pthat.length)(1) assign ctx, pthat.splitter)) + tasksupport.executeAndWaitResult( + new SameElements(splitter.psplitWithSignalling(offset, pthat.length)(1) assign ctx, pthat.splitter) + ) } } otherwise seq.startsWith(that, offset) override def sameElements[U >: T](that: GenIterable[U]): Boolean = that ifParSeq { pthat => val ctx = new DefaultSignalling with VolatileAbort - length == pthat.length && executeAndWaitResult(new SameElements(splitter assign ctx, pthat.splitter)) + length == pthat.length && tasksupport.executeAndWaitResult(new SameElements(splitter assign ctx, pthat.splitter)) } otherwise seq.sameElements(that) /** Tests whether this $coll ends with the given parallel sequence. @@ -195,25 +198,24 @@ self => else { val ctx = new DefaultSignalling with VolatileAbort val tlen = that.length - executeAndWaitResult(new SameElements(splitter.psplitWithSignalling(length - tlen, tlen)(1) assign ctx, pthat.splitter)) + tasksupport.executeAndWaitResult(new SameElements(splitter.psplitWithSignalling(length - tlen, tlen)(1) assign ctx, pthat.splitter)) } } otherwise seq.endsWith(that) def patch[U >: T, That](from: Int, patch: GenSeq[U], replaced: Int)(implicit bf: CanBuildFrom[Repr, U, That]): That = { val realreplaced = replaced min (length - from) - if (patch.isParSeq && bf.isParallel && (size - realreplaced + patch.size) > MIN_FOR_COPY) { + if (patch.isParSeq && bf(repr).isCombiner && (size - realreplaced + patch.size) > MIN_FOR_COPY) { val that = patch.asParSeq - val pbf = bf.asParallel val pits = splitter.psplitWithSignalling(from, replaced, length - from - realreplaced) - val cfactory = combinerFactory(() => pbf(repr)) + val cfactory = combinerFactory(() => bf(repr).asCombiner) val copystart = new Copy[U, That](cfactory, pits(0)) val copymiddle = wrap { val tsk = new that.Copy[U, That](cfactory, that.splitter) tasksupport.executeAndWaitResult(tsk) } val copyend = new Copy[U, That](cfactory, pits(2)) - executeAndWaitResult(((copystart parallel copymiddle) { _ combine _ } parallel copyend) { _ combine _ } mapResult { - _.result + tasksupport.executeAndWaitResult(((copystart parallel copymiddle) { _ combine _ } parallel copyend) { _ combine _ } mapResult { + _.resultWithTaskSupport }) } else patch_sequential(from, patch.seq, replaced) } @@ -226,14 +228,18 @@ self => b ++= pits(0) b ++= patch b ++= pits(2) - b.result + setTaskSupport(b.result, tasksupport) } def updated[U >: T, That](index: Int, elem: U)(implicit bf: CanBuildFrom[Repr, U, That]): That = if (bf(repr).isCombiner) { - executeAndWaitResult(new Updated(index, elem, () => bf(repr).asCombiner, splitter) mapResult { _.result }) - } else seq.updated(index, elem)(bf2seq(bf)) + tasksupport.executeAndWaitResult( + new Updated(index, elem, combinerFactory(() => bf(repr).asCombiner), splitter) mapResult { + _.resultWithTaskSupport + } + ) + } else setTaskSupport(seq.updated(index, elem)(bf2seq(bf)), tasksupport) /*bf ifParallel { pbf => - executeAndWaitResult(new Updated(index, elem, pbf, splitter) mapResult { _.result }) + tasksupport.executeAndWaitResult(new Updated(index, elem, pbf, splitter) mapResult { _.result }) } otherwise seq.updated(index, elem)(bf2seq(bf))*/ def +:[U >: T, That](elem: U)(implicit bf: CanBuildFrom[Repr, U, That]): That = { @@ -248,10 +254,13 @@ self => patch(length, new immutable.Repetition(elem, len - length), 0) } else patch(length, Nil, 0); - override def zip[U >: T, S, That](that: GenIterable[S])(implicit bf: CanBuildFrom[Repr, (U, S), That]): That = if (bf.isParallel && that.isParSeq) { - val pbf = bf.asParallel + override def zip[U >: T, S, That](that: GenIterable[S])(implicit bf: CanBuildFrom[Repr, (U, S), That]): That = if (bf(repr).isCombiner && that.isParSeq) { val thatseq = that.asParSeq - executeAndWaitResult(new Zip(length min thatseq.length, pbf, splitter, thatseq.splitter) mapResult { _.result }); + tasksupport.executeAndWaitResult( + new Zip(length min thatseq.length, combinerFactory(() => bf(repr).asCombiner), splitter, thatseq.splitter) mapResult { + _.resultWithTaskSupport + } + ); } else super.zip(that)(bf) /** Tests whether every element of this $coll relates to the @@ -268,7 +277,7 @@ self => */ def corresponds[S](that: GenSeq[S])(p: (T, S) => Boolean): Boolean = that ifParSeq { pthat => val ctx = new DefaultSignalling with VolatileAbort - length == pthat.length && executeAndWaitResult(new Corresponds(p, splitter assign ctx, pthat.splitter)) + length == pthat.length && tasksupport.executeAndWaitResult(new Corresponds(p, splitter assign ctx, pthat.splitter)) } otherwise seq.corresponds(that)(p) def diff[U >: T](that: GenSeq[U]): Repr = sequentially { @@ -424,7 +433,7 @@ self => override def requiresStrictSplitters = true } - protected[this] class Updated[U >: T, That](pos: Int, elem: U, pbf: () => Combiner[U, That], protected[this] val pit: SeqSplitter[T]) + protected[this] class Updated[U >: T, That](pos: Int, elem: U, pbf: CombinerFactory[U, That], protected[this] val pit: SeqSplitter[T]) extends Transformer[Combiner[U, That], Updated[U, That]] { @volatile var result: Combiner[U, That] = null def leaf(prev: Option[Combiner[U, That]]) = result = pit.updated2combiner(pos, elem, pbf()) @@ -437,10 +446,10 @@ self => override def requiresStrictSplitters = true } - protected[this] class Zip[U >: T, S, That](len: Int, pbf: CanCombineFrom[Repr, (U, S), That], protected[this] val pit: SeqSplitter[T], val otherpit: SeqSplitter[S]) + protected[this] class Zip[U >: T, S, That](len: Int, cf: CombinerFactory[(U, S), That], protected[this] val pit: SeqSplitter[T], val otherpit: SeqSplitter[S]) extends Transformer[Combiner[(U, S), That], Zip[U, S, That]] { @volatile var result: Result = null - def leaf(prev: Option[Result]) = result = pit.zip2combiner[U, S, That](otherpit, pbf(self.repr)) + def leaf(prev: Option[Result]) = result = pit.zip2combiner[U, S, That](otherpit, cf()) protected[this] def newSubtask(p: SuperParIterator) = unsupported override def split = { val fp = len / 2 @@ -448,8 +457,8 @@ self => val pits = pit.psplitWithSignalling(fp, sp) val opits = otherpit.psplitWithSignalling(fp, sp) Seq( - new Zip(fp, pbf, pits(0), opits(0)), - new Zip(sp, pbf, pits(1), opits(1)) + new Zip(fp, cf, pits(0), opits(0)), + new Zip(sp, cf, pits(1), opits(1)) ) } override def merge(that: Zip[U, S, That]) = result = result combine that.result diff --git a/src/library/scala/collection/parallel/ParSeqViewLike.scala b/src/library/scala/collection/parallel/ParSeqViewLike.scala index 6fdc181793..e0d1a7d6ff 100644 --- a/src/library/scala/collection/parallel/ParSeqViewLike.scala +++ b/src/library/scala/collection/parallel/ParSeqViewLike.scala @@ -38,7 +38,6 @@ extends GenSeqView[T, Coll] with ParSeqLike[T, This, ThisSeq] { self => - import tasksupport._ trait Transformed[+S] extends ParSeqView[S, Coll, CollSeq] with super[ParIterableView].Transformed[S] with super[GenSeqViewLike].Transformed[S] { @@ -170,7 +169,7 @@ self => override def scanRight[S, That](z: S)(op: (T, S) => S)(implicit bf: CanBuildFrom[This, S, That]): That = newForced(thisParSeq.scanRight(z)(op)).asInstanceOf[That] override def groupBy[K](f: T => K): immutable.ParMap[K, This] = thisParSeq.groupBy(f).map(kv => (kv._1, newForced(kv._2).asInstanceOf[This])) override def force[U >: T, That](implicit bf: CanBuildFrom[Coll, U, That]) = bf ifParallel { pbf => - executeAndWaitResult(new Force(pbf, splitter).mapResult(_.result).asInstanceOf[Task[That, _]]) + tasksupport.executeAndWaitResult(new Force(pbf, splitter).mapResult(_.result).asInstanceOf[Task[That, _]]) } otherwise { val b = bf(underlying) b ++= this.iterator diff --git a/src/library/scala/collection/parallel/Tasks.scala b/src/library/scala/collection/parallel/Tasks.scala index b705909cad..e32ac443ae 100644 --- a/src/library/scala/collection/parallel/Tasks.scala +++ b/src/library/scala/collection/parallel/Tasks.scala @@ -6,102 +6,99 @@ ** |/ ** \* */ - package scala.collection.parallel - import scala.concurrent.forkjoin._ import scala.util.control.Breaks._ - import annotation.unchecked.uncheckedVariance +trait Task[R, +Tp] { + type Result = R -/** A trait that declares task execution capabilities used - * by parallel collections. - */ -trait Tasks { - - private[parallel] val debugMessages = collection.mutable.ArrayBuffer[String]() + def repr = this.asInstanceOf[Tp] - private[parallel] def debuglog(s: String) = synchronized { - debugMessages += s - } - - trait Task[R, +Tp] { - type Result = R - - def repr = this.asInstanceOf[Tp] - - /** Body of the task - non-divisible unit of work done by this task. - * Optionally is provided with the result from the previous completed task - * or `None` if there was no previous task (or the previous task is uncompleted or unknown). - */ - def leaf(result: Option[R]) + /** Body of the task - non-divisible unit of work done by this task. + * Optionally is provided with the result from the previous completed task + * or `None` if there was no previous task (or the previous task is uncompleted or unknown). + */ + def leaf(result: Option[R]) - /** A result that can be accessed once the task is completed. */ - var result: R + /** A result that can be accessed once the task is completed. */ + var result: R - /** Decides whether or not this task should be split further. */ - def shouldSplitFurther: Boolean + /** Decides whether or not this task should be split further. */ + def shouldSplitFurther: Boolean - /** Splits this task into a list of smaller tasks. */ - private[parallel] def split: Seq[Task[R, Tp]] + /** Splits this task into a list of smaller tasks. */ + private[parallel] def split: Seq[Task[R, Tp]] - /** Read of results of `that` task and merge them into results of this one. */ - private[parallel] def merge(that: Tp @uncheckedVariance) {} + /** Read of results of `that` task and merge them into results of this one. */ + private[parallel] def merge(that: Tp @uncheckedVariance) {} - // exception handling mechanism - @volatile var throwable: Throwable = null - def forwardThrowable() = if (throwable != null) throw throwable + // exception handling mechanism + @volatile var throwable: Throwable = null + def forwardThrowable() = if (throwable != null) throw throwable - // tries to do the leaf computation, storing the possible exception - private[parallel] def tryLeaf(lastres: Option[R]) { - try { - tryBreakable { - leaf(lastres) - result = result // ensure that effects of `leaf` are visible to readers of `result` - } catchBreak { - signalAbort - } - } catch { - case thr: Exception => - result = result // ensure that effects of `leaf` are visible - throwable = thr - signalAbort + // tries to do the leaf computation, storing the possible exception + private[parallel] def tryLeaf(lastres: Option[R]) { + try { + tryBreakable { + leaf(lastres) + result = result // ensure that effects of `leaf` are visible to readers of `result` + } catchBreak { + signalAbort } + } catch { + case thr: Exception => + result = result // ensure that effects of `leaf` are visible + throwable = thr + signalAbort } + } - private[parallel] def tryMerge(t: Tp @uncheckedVariance) { - val that = t.asInstanceOf[Task[R, Tp]] - val local = result // ensure that any effects of modifying `result` are detected - // checkMerge(that) - if (this.throwable == null && that.throwable == null) merge(t) - mergeThrowables(that) - } + private[parallel] def tryMerge(t: Tp @uncheckedVariance) { + val that = t.asInstanceOf[Task[R, Tp]] + val local = result // ensure that any effects of modifying `result` are detected + // checkMerge(that) + if (this.throwable == null && that.throwable == null) merge(t) + mergeThrowables(that) + } - private def checkMerge(that: Task[R, Tp] @uncheckedVariance) { - if (this.throwable == null && that.throwable == null && (this.result == null || that.result == null)) { - println("This: " + this + ", thr=" + this.throwable + "; merged with " + that + ", thr=" + that.throwable) - } else if (this.throwable != null || that.throwable != null) { - println("merging this: " + this + " with thr: " + this.throwable + " with " + that + ", thr=" + that.throwable) - } + private def checkMerge(that: Task[R, Tp] @uncheckedVariance) { + if (this.throwable == null && that.throwable == null && (this.result == null || that.result == null)) { + println("This: " + this + ", thr=" + this.throwable + "; merged with " + that + ", thr=" + that.throwable) + } else if (this.throwable != null || that.throwable != null) { + println("merging this: " + this + " with thr: " + this.throwable + " with " + that + ", thr=" + that.throwable) } + } - private[parallel] def mergeThrowables(that: Task[_, _]) { - if (this.throwable != null && that.throwable != null) { - // merge exceptions, since there were multiple exceptions - this.throwable = this.throwable alongWith that.throwable - } else if (that.throwable != null) this.throwable = that.throwable + private[parallel] def mergeThrowables(that: Task[_, _]) { + if (this.throwable != null && that.throwable != null) { + // merge exceptions, since there were multiple exceptions + this.throwable = this.throwable alongWith that.throwable + } else if (that.throwable != null) this.throwable = that.throwable else this.throwable = this.throwable - } + } + + // override in concrete task implementations to signal abort to other tasks + private[parallel] def signalAbort() {} +} + - // override in concrete task implementations to signal abort to other tasks - private[parallel] def signalAbort() {} +/** A trait that declares task execution capabilities used + * by parallel collections. + */ +trait Tasks { + + private[parallel] val debugMessages = collection.mutable.ArrayBuffer[String]() + + private[parallel] def debuglog(s: String) = synchronized { + debugMessages += s } trait TaskImpl[R, +Tp] { diff --git a/src/library/scala/collection/parallel/immutable/ParHashMap.scala b/src/library/scala/collection/parallel/immutable/ParHashMap.scala index 7adf51cffb..266b179401 100644 --- a/src/library/scala/collection/parallel/immutable/ParHashMap.scala +++ b/src/library/scala/collection/parallel/immutable/ParHashMap.scala @@ -8,6 +8,8 @@ package scala.collection.parallel.immutable + + import scala.collection.parallel.ParMapLike import scala.collection.parallel.Combiner import scala.collection.parallel.IterableSplitter @@ -19,6 +21,9 @@ import scala.collection.generic.GenericParMapTemplate import scala.collection.generic.GenericParMapCompanion import scala.collection.immutable.{ HashMap, TrieIterator } import annotation.unchecked.uncheckedVariance +import collection.parallel.Task + + /** Immutable parallel hash map, based on hash tries. * @@ -153,7 +158,6 @@ private[parallel] abstract class HashMapCombiner[K, V] extends collection.parallel.BucketCombiner[(K, V), ParHashMap[K, V], (K, V), HashMapCombiner[K, V]](HashMapCombiner.rootsize) { //self: EnvironmentPassingCombiner[(K, V), ParHashMap[K, V]] => import HashMapCombiner._ - import collection.parallel.tasksupport._ val emptyTrie = HashMap.empty[K, V] def +=(elem: (K, V)) = { @@ -173,7 +177,7 @@ extends collection.parallel.BucketCombiner[(K, V), ParHashMap[K, V], (K, V), Has val bucks = buckets.filter(_ != null).map(_.headPtr) val root = new Array[HashMap[K, V]](bucks.length) - executeAndWaitResult(new CreateTrie(bucks, root, 0, bucks.length)) + combinerTaskSupport.executeAndWaitResult(new CreateTrie(bucks, root, 0, bucks.length)) var bitmap = 0 var i = 0 @@ -195,7 +199,7 @@ extends collection.parallel.BucketCombiner[(K, V), ParHashMap[K, V], (K, V), Has val bucks = buckets.filter(_ != null).map(_.headPtr) val root = new Array[HashMap[K, AnyRef]](bucks.length) - executeAndWaitResult(new CreateGroupedTrie(cbf, bucks, root, 0, bucks.length)) + combinerTaskSupport.executeAndWaitResult(new CreateGroupedTrie(cbf, bucks, root, 0, bucks.length)) var bitmap = 0 var i = 0 @@ -256,7 +260,7 @@ extends collection.parallel.BucketCombiner[(K, V), ParHashMap[K, V], (K, V), Has val fp = howmany / 2 List(new CreateTrie(bucks, root, offset, fp), new CreateTrie(bucks, root, offset + fp, howmany - fp)) } - def shouldSplitFurther = howmany > collection.parallel.thresholdFromSize(root.length, parallelismLevel) + def shouldSplitFurther = howmany > collection.parallel.thresholdFromSize(root.length, combinerTaskSupport.parallelismLevel) } class CreateGroupedTrie[Repr](cbf: () => Combiner[V, Repr], bucks: Array[Unrolled[(K, V)]], root: Array[HashMap[K, AnyRef]], offset: Int, howmany: Int) @@ -321,7 +325,7 @@ extends collection.parallel.BucketCombiner[(K, V), ParHashMap[K, V], (K, V), Has val fp = howmany / 2 List(new CreateGroupedTrie(cbf, bucks, root, offset, fp), new CreateGroupedTrie(cbf, bucks, root, offset + fp, howmany - fp)) } - def shouldSplitFurther = howmany > collection.parallel.thresholdFromSize(root.length, parallelismLevel) + def shouldSplitFurther = howmany > collection.parallel.thresholdFromSize(root.length, combinerTaskSupport.parallelismLevel) } } diff --git a/src/library/scala/collection/parallel/immutable/ParHashSet.scala b/src/library/scala/collection/parallel/immutable/ParHashSet.scala index 1cf0ccd391..0d7f04976e 100644 --- a/src/library/scala/collection/parallel/immutable/ParHashSet.scala +++ b/src/library/scala/collection/parallel/immutable/ParHashSet.scala @@ -8,6 +8,8 @@ package scala.collection.parallel.immutable + + import scala.collection.parallel.ParSetLike import scala.collection.parallel.Combiner import scala.collection.parallel.IterableSplitter @@ -19,6 +21,9 @@ import scala.collection.generic.GenericParTemplate import scala.collection.generic.GenericParCompanion import scala.collection.generic.GenericCompanion import scala.collection.immutable.{ HashSet, TrieIterator } +import collection.parallel.Task + + /** Immutable parallel hash set, based on hash tries. * @@ -127,7 +132,6 @@ private[immutable] abstract class HashSetCombiner[T] extends collection.parallel.BucketCombiner[T, ParHashSet[T], Any, HashSetCombiner[T]](HashSetCombiner.rootsize) { //self: EnvironmentPassingCombiner[T, ParHashSet[T]] => import HashSetCombiner._ - import collection.parallel.tasksupport._ val emptyTrie = HashSet.empty[T] def +=(elem: T) = { @@ -147,7 +151,7 @@ extends collection.parallel.BucketCombiner[T, ParHashSet[T], Any, HashSetCombine val bucks = buckets.filter(_ != null).map(_.headPtr) val root = new Array[HashSet[T]](bucks.length) - executeAndWaitResult(new CreateTrie(bucks, root, 0, bucks.length)) + combinerTaskSupport.executeAndWaitResult(new CreateTrie(bucks, root, 0, bucks.length)) var bitmap = 0 var i = 0 @@ -202,7 +206,7 @@ extends collection.parallel.BucketCombiner[T, ParHashSet[T], Any, HashSetCombine val fp = howmany / 2 List(new CreateTrie(bucks, root, offset, fp), new CreateTrie(bucks, root, offset + fp, howmany - fp)) } - def shouldSplitFurther = howmany > collection.parallel.thresholdFromSize(root.length, parallelismLevel) + def shouldSplitFurther = howmany > collection.parallel.thresholdFromSize(root.length, combinerTaskSupport.parallelismLevel) } } diff --git a/src/library/scala/collection/parallel/mutable/ParArray.scala b/src/library/scala/collection/parallel/mutable/ParArray.scala index 72a8184b10..5c3da66be0 100644 --- a/src/library/scala/collection/parallel/mutable/ParArray.scala +++ b/src/library/scala/collection/parallel/mutable/ParArray.scala @@ -21,6 +21,7 @@ import scala.collection.generic.Sizing import scala.collection.parallel.Combiner import scala.collection.parallel.SeqSplitter import scala.collection.parallel.ParSeqLike +import scala.collection.parallel.Task import scala.collection.parallel.CHECK_RATE import scala.collection.mutable.ArraySeq import scala.collection.mutable.Builder @@ -56,7 +57,6 @@ extends ParSeq[T] with Serializable { self => - import collection.parallel.tasksupport._ @transient private var array: Array[Any] = arrayseq.array.asInstanceOf[Array[Any]] @@ -584,22 +584,22 @@ self => val targetarr = targarrseq.array.asInstanceOf[Array[Any]] // fill it in parallel - executeAndWaitResult(new Map[S](f, targetarr, 0, length)) + tasksupport.executeAndWaitResult(new Map[S](f, targetarr, 0, length)) // wrap it into a parallel array (new ParArray[S](targarrseq)).asInstanceOf[That] } else super.map(f)(bf) override def scan[U >: T, That](z: U)(op: (U, U) => U)(implicit cbf: CanBuildFrom[ParArray[T], U, That]): That = - if (parallelismLevel > 1 && buildsArray(cbf(repr))) { + if (tasksupport.parallelismLevel > 1 && buildsArray(cbf(repr))) { // reserve an array val targarrseq = new ArraySeq[U](length + 1) val targetarr = targarrseq.array.asInstanceOf[Array[Any]] targetarr(0) = z // do a parallel prefix scan - if (length > 0) executeAndWaitResult(new CreateScanTree[U](0, size, z, op, splitter) mapResult { - tree => executeAndWaitResult(new ScanToArray(tree, z, op, targetarr)) + if (length > 0) tasksupport.executeAndWaitResult(new CreateScanTree[U](0, size, z, op, splitter) mapResult { + tree => tasksupport.executeAndWaitResult(new ScanToArray(tree, z, op, targetarr)) }) // wrap the array into a parallel array @@ -661,7 +661,7 @@ self => val fp = howmany / 2 List(new Map(f, targetarr, offset, fp), new Map(f, targetarr, offset + fp, howmany - fp)) } - def shouldSplitFurther = howmany > collection.parallel.thresholdFromSize(length, parallelismLevel) + def shouldSplitFurther = howmany > collection.parallel.thresholdFromSize(length, tasksupport.parallelismLevel) } /* serialization */ diff --git a/src/library/scala/collection/parallel/mutable/ParCtrie.scala b/src/library/scala/collection/parallel/mutable/ParCtrie.scala index cec2e6886d..178424decc 100644 --- a/src/library/scala/collection/parallel/mutable/ParCtrie.scala +++ b/src/library/scala/collection/parallel/mutable/ParCtrie.scala @@ -13,6 +13,7 @@ package scala.collection.parallel.mutable import scala.collection.generic._ import scala.collection.parallel.Combiner import scala.collection.parallel.IterableSplitter +import scala.collection.parallel.Task import scala.collection.mutable.BasicNode import scala.collection.mutable.TNode import scala.collection.mutable.LNode @@ -40,7 +41,6 @@ extends ParMap[K, V] with ParCtrieCombiner[K, V] with Serializable { - import collection.parallel.tasksupport._ def this() = this(new Ctrie) @@ -83,7 +83,7 @@ extends ParMap[K, V] case tn: TNode[_, _] => tn.cachedSize(ctrie) case ln: LNode[_, _] => ln.cachedSize(ctrie) case cn: CNode[_, _] => - executeAndWaitResult(new Size(0, cn.array.length, cn.array)) + tasksupport.executeAndWaitResult(new Size(0, cn.array.length, cn.array)) cn.cachedSize(ctrie) } } diff --git a/src/library/scala/collection/parallel/mutable/ParHashMap.scala b/src/library/scala/collection/parallel/mutable/ParHashMap.scala index 21a5b05749..6ce6c45460 100644 --- a/src/library/scala/collection/parallel/mutable/ParHashMap.scala +++ b/src/library/scala/collection/parallel/mutable/ParHashMap.scala @@ -17,6 +17,7 @@ import collection.mutable.DefaultEntry import collection.mutable.HashEntry import collection.mutable.HashTable import collection.mutable.UnrolledBuffer +import collection.parallel.Task @@ -156,8 +157,6 @@ private[mutable] abstract class ParHashMapCombiner[K, V](private val tableLoadFa extends collection.parallel.BucketCombiner[(K, V), ParHashMap[K, V], DefaultEntry[K, V], ParHashMapCombiner[K, V]](ParHashMapCombiner.numblocks) with collection.mutable.HashTable.HashUtils[K] { -//self: EnvironmentPassingCombiner[(K, V), ParHashMap[K, V]] => - import collection.parallel.tasksupport._ private var mask = ParHashMapCombiner.discriminantmask private var nonmasklen = ParHashMapCombiner.nonmasklength private var seedvalue = 27 @@ -179,7 +178,7 @@ extends collection.parallel.BucketCombiner[(K, V), ParHashMap[K, V], DefaultEntr // construct table val table = new AddingHashTable(size, tableLoadFactor, seedvalue) val bucks = buckets.map(b => if (b ne null) b.headPtr else null) - val insertcount = executeAndWaitResult(new FillBlocks(bucks, table, 0, bucks.length)) + val insertcount = combinerTaskSupport.executeAndWaitResult(new FillBlocks(bucks, table, 0, bucks.length)) table.setSize(insertcount) // TODO compare insertcount and size to see if compression is needed val c = table.hashTableContents @@ -300,7 +299,7 @@ extends collection.parallel.BucketCombiner[(K, V), ParHashMap[K, V], DefaultEntr override def merge(that: FillBlocks) { this.result += that.result } - def shouldSplitFurther = howmany > collection.parallel.thresholdFromSize(ParHashMapCombiner.numblocks, parallelismLevel) + def shouldSplitFurther = howmany > collection.parallel.thresholdFromSize(ParHashMapCombiner.numblocks, combinerTaskSupport.parallelismLevel) } } diff --git a/src/library/scala/collection/parallel/mutable/ParHashSet.scala b/src/library/scala/collection/parallel/mutable/ParHashSet.scala index 6c5f513ad0..811fc8bfe7 100644 --- a/src/library/scala/collection/parallel/mutable/ParHashSet.scala +++ b/src/library/scala/collection/parallel/mutable/ParHashSet.scala @@ -8,10 +8,15 @@ package scala.collection.parallel.mutable + + import collection.generic._ import collection.mutable.FlatHashTable import collection.parallel.Combiner import collection.mutable.UnrolledBuffer +import collection.parallel.Task + + /** A parallel hash set. * @@ -113,7 +118,6 @@ private[mutable] abstract class ParHashSetCombiner[T](private val tableLoadFacto extends collection.parallel.BucketCombiner[T, ParHashSet[T], Any, ParHashSetCombiner[T]](ParHashSetCombiner.numblocks) with collection.mutable.FlatHashTable.HashUtils[T] { //self: EnvironmentPassingCombiner[T, ParHashSet[T]] => - import collection.parallel.tasksupport._ private var mask = ParHashSetCombiner.discriminantmask private var nonmasklen = ParHashSetCombiner.nonmasklength private var seedvalue = 27 @@ -139,7 +143,7 @@ with collection.mutable.FlatHashTable.HashUtils[T] { private def parPopulate: FlatHashTable.Contents[T] = { // construct it in parallel val table = new AddingFlatHashTable(size, tableLoadFactor, seedvalue) - val (inserted, leftovers) = executeAndWaitResult(new FillBlocks(buckets, table, 0, buckets.length)) + val (inserted, leftovers) = combinerTaskSupport.executeAndWaitResult(new FillBlocks(buckets, table, 0, buckets.length)) var leftinserts = 0 for (elem <- leftovers) leftinserts += table.insertEntry(0, table.tableLength, elem.asInstanceOf[T]) table.setSize(leftinserts + inserted) @@ -304,7 +308,7 @@ with collection.mutable.FlatHashTable.HashUtils[T] { // the total number of successfully inserted elements is adjusted accordingly result = (this.result._1 + that.result._1 + inserted, remainingLeftovers concat that.result._2) } - def shouldSplitFurther = howmany > collection.parallel.thresholdFromSize(ParHashMapCombiner.numblocks, parallelismLevel) + def shouldSplitFurther = howmany > collection.parallel.thresholdFromSize(ParHashMapCombiner.numblocks, combinerTaskSupport.parallelismLevel) } } diff --git a/src/library/scala/collection/parallel/mutable/ResizableParArrayCombiner.scala b/src/library/scala/collection/parallel/mutable/ResizableParArrayCombiner.scala index eadc93d422..01eb17024e 100644 --- a/src/library/scala/collection/parallel/mutable/ResizableParArrayCombiner.scala +++ b/src/library/scala/collection/parallel/mutable/ResizableParArrayCombiner.scala @@ -8,18 +8,20 @@ package scala.collection.parallel.mutable + + import scala.collection.generic.Sizing import scala.collection.mutable.ArraySeq import scala.collection.mutable.ArrayBuffer import scala.collection.parallel.TaskSupport -//import scala.collection.parallel.EnvironmentPassingCombiner import scala.collection.parallel.unsupportedop import scala.collection.parallel.Combiner +import scala.collection.parallel.Task + + /** An array combiner that uses a chain of arraybuffers to store elements. */ trait ResizableParArrayCombiner[T] extends LazyCombiner[T, ParArray[T], ExposedArrayBuffer[T]] { -//self: EnvironmentPassingCombiner[T, ParArray[T]] => - import collection.parallel.tasksupport._ override def sizeHint(sz: Int) = if (chain.length == 1) chain(0).sizeHint(sz) @@ -30,7 +32,7 @@ trait ResizableParArrayCombiner[T] extends LazyCombiner[T, ParArray[T], ExposedA val arrayseq = new ArraySeq[T](size) val array = arrayseq.array.asInstanceOf[Array[Any]] - executeAndWaitResult(new CopyChainToArray(array, 0, size)) + combinerTaskSupport.executeAndWaitResult(new CopyChainToArray(array, 0, size)) new ParArray(arrayseq) } else { // optimisation if there is only 1 array @@ -79,7 +81,7 @@ trait ResizableParArrayCombiner[T] extends LazyCombiner[T, ParArray[T], ExposedA val fp = howmany / 2 List(new CopyChainToArray(array, offset, fp), new CopyChainToArray(array, offset + fp, howmany - fp)) } - def shouldSplitFurther = howmany > collection.parallel.thresholdFromSize(size, parallelismLevel) + def shouldSplitFurther = howmany > collection.parallel.thresholdFromSize(size, combinerTaskSupport.parallelismLevel) } } diff --git a/src/library/scala/collection/parallel/mutable/UnrolledParArrayCombiner.scala b/src/library/scala/collection/parallel/mutable/UnrolledParArrayCombiner.scala index dc583fb4e7..410b542a68 100644 --- a/src/library/scala/collection/parallel/mutable/UnrolledParArrayCombiner.scala +++ b/src/library/scala/collection/parallel/mutable/UnrolledParArrayCombiner.scala @@ -18,9 +18,9 @@ import scala.collection.mutable.ArrayBuffer import scala.collection.mutable.UnrolledBuffer import scala.collection.mutable.UnrolledBuffer.Unrolled import scala.collection.parallel.TaskSupport -//import scala.collection.parallel.EnvironmentPassingCombiner import scala.collection.parallel.unsupportedop import scala.collection.parallel.Combiner +import scala.collection.parallel.Task @@ -40,8 +40,6 @@ extends Combiner[T, ParArray[T]] { // because size is doubling, random access is O(logn)! val buff = new DoublingUnrolledBuffer[Any] - import collection.parallel.tasksupport._ - def +=(elem: T) = { buff += elem this @@ -51,7 +49,7 @@ extends Combiner[T, ParArray[T]] { val arrayseq = new ArraySeq[T](size) val array = arrayseq.array.asInstanceOf[Array[Any]] - executeAndWaitResult(new CopyUnrolledToArray(array, 0, size)) + combinerTaskSupport.executeAndWaitResult(new CopyUnrolledToArray(array, 0, size)) new ParArray(arrayseq) } @@ -109,7 +107,7 @@ extends Combiner[T, ParArray[T]] { val fp = howmany / 2 List(new CopyUnrolledToArray(array, offset, fp), new CopyUnrolledToArray(array, offset + fp, howmany - fp)) } - def shouldSplitFurther = howmany > collection.parallel.thresholdFromSize(size, parallelismLevel) + def shouldSplitFurther = howmany > collection.parallel.thresholdFromSize(size, combinerTaskSupport.parallelismLevel) override def toString = "CopyUnrolledToArray(" + offset + ", " + howmany + ")" } } diff --git a/src/library/scala/collection/parallel/package.scala b/src/library/scala/collection/parallel/package.scala index 8f19d0ecdb..8f49b80c93 100644 --- a/src/library/scala/collection/parallel/package.scala +++ b/src/library/scala/collection/parallel/package.scala @@ -46,8 +46,16 @@ package object parallel { else new ThreadPoolTaskSupport } else new ThreadPoolTaskSupport - val tasksupport = getTaskSupport - + val defaultTaskSupport: TaskSupport = getTaskSupport + + def setTaskSupport[Coll](c: Coll, t: TaskSupport): Coll = { + c match { + case pc: ParIterableLike[_, _, _] => pc.tasksupport = t + case _ => // do nothing + } + c + } + /* implicit conversions */ implicit def factory2ops[From, Elem, To](bf: CanBuildFrom[From, Elem, To]) = new FactoryOps[From, Elem, To] { -- cgit v1.2.3 From 02e260a8e67e2b2b6f876aafe76cd61248a89374 Mon Sep 17 00:00:00 2001 From: Szabolcs Berecz Date: Tue, 28 Feb 2012 22:35:53 +0100 Subject: Fixes SI-5380: non-local return of try expression --- src/compiler/scala/tools/nsc/transform/UnCurry.scala | 3 +++ test/files/run/si5380.scala | 6 ++++++ 2 files changed, 9 insertions(+) create mode 100644 test/files/run/si5380.scala (limited to 'src') diff --git a/src/compiler/scala/tools/nsc/transform/UnCurry.scala b/src/compiler/scala/tools/nsc/transform/UnCurry.scala index b9b115b7c8..0e6f96fb07 100644 --- a/src/compiler/scala/tools/nsc/transform/UnCurry.scala +++ b/src/compiler/scala/tools/nsc/transform/UnCurry.scala @@ -551,6 +551,9 @@ abstract class UnCurry extends InfoTransform case Assign(lhs, _) if lhs.symbol.owner != currentMethod || lhs.symbol.hasFlag(LAZY | ACCESSOR) => withNeedLift(true) { super.transform(tree) } + case Return(_) => + withNeedLift(true) { super.transform(tree) } + case Try(block, catches, finalizer) => if (needTryLift || shouldBeLiftedAnyway(tree)) transform(liftTree(tree)) else super.transform(tree) diff --git a/test/files/run/si5380.scala b/test/files/run/si5380.scala new file mode 100644 index 0000000000..6083161a9b --- /dev/null +++ b/test/files/run/si5380.scala @@ -0,0 +1,6 @@ +object Test { + def main(args: Array[String]) { + val f = () => return try { 1 } catch { case _ => 0 } + f() + } +} -- cgit v1.2.3 From edf3ae0b8c3688b5cacbe2f7e2ae826f5fbb7644 Mon Sep 17 00:00:00 2001 From: Szabolcs Berecz Date: Wed, 29 Feb 2012 21:49:16 +0100 Subject: Lift only *non-local* returns of try expressions. --- src/compiler/scala/tools/nsc/transform/UnCurry.scala | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/compiler/scala/tools/nsc/transform/UnCurry.scala b/src/compiler/scala/tools/nsc/transform/UnCurry.scala index 0e6f96fb07..0d563c4d7d 100644 --- a/src/compiler/scala/tools/nsc/transform/UnCurry.scala +++ b/src/compiler/scala/tools/nsc/transform/UnCurry.scala @@ -452,6 +452,8 @@ abstract class UnCurry extends InfoTransform } } + def isNonLocalReturn(ret: Return) = ret.symbol != currentOwner.enclMethod || currentOwner.isLazy + // ------ The tree transformers -------------------------------------------------------- def mainTransform(tree: Tree): Tree = { @@ -551,8 +553,8 @@ abstract class UnCurry extends InfoTransform case Assign(lhs, _) if lhs.symbol.owner != currentMethod || lhs.symbol.hasFlag(LAZY | ACCESSOR) => withNeedLift(true) { super.transform(tree) } - case Return(_) => - withNeedLift(true) { super.transform(tree) } + case ret @ Return(_) if (isNonLocalReturn(ret)) => + withNeedLift(true) { super.transform(ret) } case Try(block, catches, finalizer) => if (needTryLift || shouldBeLiftedAnyway(tree)) transform(liftTree(tree)) @@ -659,9 +661,9 @@ abstract class UnCurry extends InfoTransform applyUnary() case Select(_, _) | TypeApply(_, _) => applyUnary() - case Return(expr) if (tree.symbol != currentOwner.enclMethod || currentOwner.isLazy) => - debuglog("non local return in "+tree.symbol+" from "+currentOwner.enclMethod) - atPos(tree.pos)(nonLocalReturnThrow(expr, tree.symbol)) + case ret @ Return(expr) if (isNonLocalReturn(ret)) => + debuglog("non local return in "+ret.symbol+" from "+currentOwner.enclMethod) + atPos(ret.pos)(nonLocalReturnThrow(expr, ret.symbol)) case TypeTree() => tree case _ => -- cgit v1.2.3 From 8cc36cdfa17c26a55ab1d164105363f3492f72af Mon Sep 17 00:00:00 2001 From: Eugene Burmako Date: Wed, 29 Feb 2012 12:51:18 +0100 Subject: Fixes SI-5530 --- src/compiler/scala/tools/nsc/ast/parser/Scanners.scala | 13 ++++++++----- test/files/run/t5530.check | 2 ++ test/files/run/t5530.flags | 1 + test/files/run/t5530.scala | 4 ++++ 4 files changed, 15 insertions(+), 5 deletions(-) create mode 100644 test/files/run/t5530.check create mode 100644 test/files/run/t5530.flags create mode 100644 test/files/run/t5530.scala (limited to 'src') diff --git a/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala b/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala index 2626ca26a6..20360b547e 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala @@ -698,12 +698,15 @@ trait Scanners extends ScannersCommon { } else { syntaxError("invalid string interpolation") } - } else if ((ch == CR || ch == LF || ch == SU) && !isUnicodeEscape) { - syntaxError("unclosed string literal") } else { - putChar(ch) - nextRawChar() - getStringPart(multiLine) + val isUnclosedLiteral = !isUnicodeEscape && (ch == SU || (!multiLine && (ch == CR || ch == LF))) + if (isUnclosedLiteral) { + syntaxError(if (!multiLine) "unclosed string literal" else "unclosed multi-line string literal") + } else { + putChar(ch) + nextRawChar() + getStringPart(multiLine) + } } } diff --git a/test/files/run/t5530.check b/test/files/run/t5530.check new file mode 100644 index 0000000000..1013e3356f --- /dev/null +++ b/test/files/run/t5530.check @@ -0,0 +1,2 @@ +something like this + 7 now works!. diff --git a/test/files/run/t5530.flags b/test/files/run/t5530.flags new file mode 100644 index 0000000000..e1b37447c9 --- /dev/null +++ b/test/files/run/t5530.flags @@ -0,0 +1 @@ +-Xexperimental \ No newline at end of file diff --git a/test/files/run/t5530.scala b/test/files/run/t5530.scala new file mode 100644 index 0000000000..c8109a4004 --- /dev/null +++ b/test/files/run/t5530.scala @@ -0,0 +1,4 @@ +object Test extends App { + println(s"""something like this + ${3+4} now works!.""") +} \ No newline at end of file -- cgit v1.2.3 From c1e76ddb80cf7737bfe0e8c99e70ee2ace66e43e Mon Sep 17 00:00:00 2001 From: Eugene Burmako Date: Thu, 1 Mar 2012 09:59:42 +0100 Subject: Fixes SI-5532 --- src/compiler/scala/tools/nsc/ast/parser/Scanners.scala | 14 ++++++++++---- test/files/run/t5532.flags | 1 + test/files/run/t5532.scala | 4 ++++ 3 files changed, 15 insertions(+), 4 deletions(-) create mode 100644 test/files/run/t5532.flags create mode 100644 test/files/run/t5532.scala (limited to 'src') diff --git a/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala b/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala index 2626ca26a6..9d33e7e9e1 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala @@ -670,12 +670,18 @@ trait Scanners extends ScannersCommon { next.offset = charOffset - 1 } if (ch == '"') { - nextRawChar() - if (!multiLine || isTripleQuote()) { + if (multiLine) { + nextRawChar() + if (isTripleQuote()) { + setStrVal() + token = STRINGLIT + } else + getStringPart(multiLine) + } else { + nextChar() setStrVal() token = STRINGLIT - } else - getStringPart(multiLine) + } } else if (ch == '$') { nextRawChar() if (ch == '$') { diff --git a/test/files/run/t5532.flags b/test/files/run/t5532.flags new file mode 100644 index 0000000000..e1b37447c9 --- /dev/null +++ b/test/files/run/t5532.flags @@ -0,0 +1 @@ +-Xexperimental \ No newline at end of file diff --git a/test/files/run/t5532.scala b/test/files/run/t5532.scala new file mode 100644 index 0000000000..75004730bf --- /dev/null +++ b/test/files/run/t5532.scala @@ -0,0 +1,4 @@ +object Test extends App { + val x = s"1" + val y = s"2" +} \ No newline at end of file -- cgit v1.2.3 From c5ac8005e23fc533e464d86e2c4ba5a4457844fe Mon Sep 17 00:00:00 2001 From: Paul Phillips Date: Thu, 1 Mar 2012 15:45:42 -0800 Subject: Changes to appease the inliner. It's unfortunate the things one must do to see @inline go through. --- .../scala/reflect/internal/SymbolTable.scala | 3 ++- src/compiler/scala/tools/nsc/Global.scala | 21 +++++++++++---------- 2 files changed, 13 insertions(+), 11 deletions(-) (limited to 'src') diff --git a/src/compiler/scala/reflect/internal/SymbolTable.scala b/src/compiler/scala/reflect/internal/SymbolTable.scala index c8c1a51102..221fb46d12 100644 --- a/src/compiler/scala/reflect/internal/SymbolTable.scala +++ b/src/compiler/scala/reflect/internal/SymbolTable.scala @@ -79,7 +79,8 @@ abstract class SymbolTable extends api.Universe type RunId = Int final val NoRunId = 0 - private var phStack: List[Phase] = Nil + // sigh, this has to be public or atPhase doesn't inline. + var phStack: List[Phase] = Nil private var ph: Phase = NoPhase private var per = NoPeriod diff --git a/src/compiler/scala/tools/nsc/Global.scala b/src/compiler/scala/tools/nsc/Global.scala index 1470a43491..1676efef5d 100644 --- a/src/compiler/scala/tools/nsc/Global.scala +++ b/src/compiler/scala/tools/nsc/Global.scala @@ -210,22 +210,23 @@ class Global(var currentSettings: Settings, var reporter: Reporter) extends Symb def logError(msg: String, t: Throwable): Unit = () - private def atPhaseStackMessage = atPhaseStack match { - case Nil => "" - case ps => ps.reverseMap("->" + _).mkString("(", " ", ")") - } - private def shouldLogAtThisPhase = ( - (settings.log.isSetByUser) - && ((settings.log containsPhase globalPhase) || (settings.log containsPhase phase)) - ) - def logAfterEveryPhase[T](msg: String)(op: => T) { log("Running operation '%s' after every phase.\n".format(msg) + describeAfterEveryPhase(op)) } // Over 200 closure objects are eliminated by inlining this. - @inline final def log(msg: => AnyRef): Unit = + @inline final def log(msg: => AnyRef): Unit = { + def shouldLogAtThisPhase = ( + (settings.log.isSetByUser) + && ((settings.log containsPhase globalPhase) || (settings.log containsPhase phase)) + ) + def atPhaseStackMessage = atPhaseStack match { + case Nil => "" + case ps => ps.reverseMap("->" + _).mkString("(", " ", ")") + } + if (shouldLogAtThisPhase) inform("[log %s%s] %s".format(globalPhase, atPhaseStackMessage, msg)) + } @inline final override def debuglog(msg: => String) { if (settings.debug.value) -- cgit v1.2.3 From ee4fa5449e25bae11891f23907114ff5ea5e12b8 Mon Sep 17 00:00:00 2001 From: Paul Phillips Date: Thu, 1 Mar 2012 22:02:02 -0800 Subject: Another try to quiet the inliner. --- src/compiler/scala/tools/nsc/Global.scala | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) (limited to 'src') diff --git a/src/compiler/scala/tools/nsc/Global.scala b/src/compiler/scala/tools/nsc/Global.scala index 1676efef5d..f0f53ec315 100644 --- a/src/compiler/scala/tools/nsc/Global.scala +++ b/src/compiler/scala/tools/nsc/Global.scala @@ -213,17 +213,17 @@ class Global(var currentSettings: Settings, var reporter: Reporter) extends Symb def logAfterEveryPhase[T](msg: String)(op: => T) { log("Running operation '%s' after every phase.\n".format(msg) + describeAfterEveryPhase(op)) } + + def shouldLogAtThisPhase = ( + (settings.log.isSetByUser) + && ((settings.log containsPhase globalPhase) || (settings.log containsPhase phase)) + ) + def atPhaseStackMessage = atPhaseStack match { + case Nil => "" + case ps => ps.reverseMap("->" + _).mkString("(", " ", ")") + } // Over 200 closure objects are eliminated by inlining this. - @inline final def log(msg: => AnyRef): Unit = { - def shouldLogAtThisPhase = ( - (settings.log.isSetByUser) - && ((settings.log containsPhase globalPhase) || (settings.log containsPhase phase)) - ) - def atPhaseStackMessage = atPhaseStack match { - case Nil => "" - case ps => ps.reverseMap("->" + _).mkString("(", " ", ")") - } - + @inline final def log(msg: => AnyRef) { if (shouldLogAtThisPhase) inform("[log %s%s] %s".format(globalPhase, atPhaseStackMessage, msg)) } -- cgit v1.2.3 From 54b541b103f79bdfff96227eeeac1d92d68165d8 Mon Sep 17 00:00:00 2001 From: Paul Phillips Date: Thu, 1 Mar 2012 13:36:12 -0800 Subject: More consistent use of atPhase. --- src/compiler/scala/reflect/internal/Phase.scala | 4 +- .../scala/reflect/internal/SymbolTable.scala | 23 ++-- src/compiler/scala/reflect/internal/Symbols.scala | 10 +- .../reflect/internal/pickling/UnPickler.scala | 9 +- .../scala/reflect/internal/util/Collections.scala | 4 + src/compiler/scala/tools/nsc/Global.scala | 140 +++++++++++---------- src/compiler/scala/tools/nsc/SubComponent.scala | 4 +- .../scala/tools/nsc/backend/icode/Members.scala | 7 +- .../scala/tools/nsc/backend/opt/Inliners.scala | 1 + .../scala/tools/nsc/symtab/classfile/Pickler.scala | 2 +- .../scala/tools/nsc/transform/ExplicitOuter.scala | 4 +- src/compiler/scala/tools/nsc/transform/Mixin.scala | 6 +- .../tools/nsc/transform/SpecializeTypes.scala | 4 +- 13 files changed, 120 insertions(+), 98 deletions(-) (limited to 'src') diff --git a/src/compiler/scala/reflect/internal/Phase.scala b/src/compiler/scala/reflect/internal/Phase.scala index acd3360c4f..89d643aacf 100644 --- a/src/compiler/scala/reflect/internal/Phase.scala +++ b/src/compiler/scala/reflect/internal/Phase.scala @@ -26,6 +26,8 @@ abstract class Phase(val prev: Phase) { if ((prev ne null) && (prev ne NoPhase)) prev.nx = this def next: Phase = nx + def hasNext = next != this + def iterator = Iterator.iterate(this)(_.next) takeWhile (p => p.next != p) def name: String def description: String = name @@ -37,7 +39,7 @@ abstract class Phase(val prev: Phase) { def refChecked: Boolean = false /** This is used only in unsafeTypeParams, and at this writing is - * overridden to false in namer, typer, and erasure. (And NoPhase.) + * overridden to false in parser, namer, typer, and erasure. (And NoPhase.) */ def keepsTypeParams = true def run(): Unit diff --git a/src/compiler/scala/reflect/internal/SymbolTable.scala b/src/compiler/scala/reflect/internal/SymbolTable.scala index 221fb46d12..ce54c32273 100644 --- a/src/compiler/scala/reflect/internal/SymbolTable.scala +++ b/src/compiler/scala/reflect/internal/SymbolTable.scala @@ -93,6 +93,16 @@ abstract class SymbolTable extends api.Universe ph = p per = period(currentRunId, p.id) } + final def pushPhase(ph: Phase): Phase = { + val current = phase + phase = ph + phStack ::= ph + current + } + final def popPhase(ph: Phase) { + phStack = phStack.tail + phase = ph + } /** The current compiler run identifier. */ def currentRunId: RunId @@ -120,20 +130,19 @@ abstract class SymbolTable extends api.Universe /** Perform given operation at given phase. */ @inline final def atPhase[T](ph: Phase)(op: => T): T = { - val current = phase - phase = ph - phStack ::= ph + val saved = pushPhase(ph) try op - finally { - phase = current - phStack = phStack.tail - } + finally popPhase(saved) } + + /** Since when it is to be "at" a phase is inherently ambiguous, * a couple unambiguously named methods. */ @inline final def beforePhase[T](ph: Phase)(op: => T): T = atPhase(ph)(op) @inline final def afterPhase[T](ph: Phase)(op: => T): T = atPhase(ph.next)(op) + @inline final def afterCurrentPhase[T](op: => T): T = atPhase(phase.next)(op) + @inline final def beforePrevPhase[T](op: => T): T = atPhase(phase.prev)(op) @inline final def atPhaseNotLaterThan[T](target: Phase)(op: => T): T = if (target != NoPhase && phase.id > target.id) atPhase(target)(op) else op diff --git a/src/compiler/scala/reflect/internal/Symbols.scala b/src/compiler/scala/reflect/internal/Symbols.scala index 334436bfbe..62b0206c28 100644 --- a/src/compiler/scala/reflect/internal/Symbols.scala +++ b/src/compiler/scala/reflect/internal/Symbols.scala @@ -1166,7 +1166,13 @@ trait Symbols extends api.Symbols { self: SymbolTable => abort("typeConstructor inapplicable for " + this) /** The logic approximately boils down to finding the most recent phase - * which immediately follows any of namer, typer, or erasure. + * which immediately follows any of parser, namer, typer, or erasure. + * In effect that means this will return one of: + * + * - packageobjects (follows namer) + * - superaccessors (follows typer) + * - lazyvals (follows erasure) + * - null */ private def unsafeTypeParamPhase = { var ph = phase @@ -2752,5 +2758,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => assert(validFrom != NoPeriod) override def toString() = "TypeHistory(" + phaseOf(validFrom)+":"+runId(validFrom) + "," + info + "," + prev + ")" + + def toList: List[TypeHistory] = this :: ( if (prev eq null) Nil else prev.toList ) } } diff --git a/src/compiler/scala/reflect/internal/pickling/UnPickler.scala b/src/compiler/scala/reflect/internal/pickling/UnPickler.scala index 9f93108420..34163d54f8 100644 --- a/src/compiler/scala/reflect/internal/pickling/UnPickler.scala +++ b/src/compiler/scala/reflect/internal/pickling/UnPickler.scala @@ -846,10 +846,11 @@ abstract class UnPickler /*extends reflect.generic.UnPickler*/ { private val p = phase override def complete(sym: Symbol) : Unit = try { val tp = at(i, () => readType(sym.isTerm)) // after NMT_TRANSITION, revert `() => readType(sym.isTerm)` to `readType` - if (p != phase) atPhase(p) (sym setInfo tp) - else sym setInfo tp - if (currentRunId != definedAtRunId) sym.setInfo(adaptToNewRunMap(tp)) - } catch { + atPhase(p) (sym setInfo tp) + if (currentRunId != definedAtRunId) + sym.setInfo(adaptToNewRunMap(tp)) + } + catch { case e: MissingRequirementError => throw toTypeError(e) } override def load(sym: Symbol) { complete(sym) } diff --git a/src/compiler/scala/reflect/internal/util/Collections.scala b/src/compiler/scala/reflect/internal/util/Collections.scala index cc48be1684..d26a1abadb 100644 --- a/src/compiler/scala/reflect/internal/util/Collections.scala +++ b/src/compiler/scala/reflect/internal/util/Collections.scala @@ -74,6 +74,10 @@ trait Collections { index += 1 } } + + @inline final def findOrElse[A](xs: TraversableOnce[A])(p: A => Boolean)(orElse: => A): A = { + xs find p getOrElse orElse + } final def mapWithIndex[A, B](xs: List[A])(f: (A, Int) => B): List[B] = { val lb = new ListBuffer[B] diff --git a/src/compiler/scala/tools/nsc/Global.scala b/src/compiler/scala/tools/nsc/Global.scala index f0f53ec315..44dc2fe384 100644 --- a/src/compiler/scala/tools/nsc/Global.scala +++ b/src/compiler/scala/tools/nsc/Global.scala @@ -59,7 +59,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter) extends Symb type AbstractFileType = scala.tools.nsc.io.AbstractFile def mkAttributedQualifier(tpe: Type, termSym: Symbol): Tree = gen.mkAttributedQualifier(tpe, termSym) - + def picklerPhase: Phase = if (currentRun.isDefined) currentRun.picklerPhase else NoPhase // platform specific elements @@ -869,26 +869,26 @@ class Global(var currentSettings: Settings, var reporter: Reporter) extends Symb def currentSource: SourceFile = if (currentUnit.exists) currentUnit.source else lastSeenSourceFile // TODO - trim these to the absolute minimum. - @inline final def afterErasure[T](op: => T): T = afterPhase(currentRun.erasurePhase)(op) - @inline final def afterExplicitOuter[T](op: => T): T = afterPhase(currentRun.explicitouterPhase)(op) - @inline final def afterFlatten[T](op: => T): T = afterPhase(currentRun.flattenPhase)(op) - @inline final def afterIcode[T](op: => T): T = afterPhase(currentRun.icodePhase)(op) - @inline final def afterMixin[T](op: => T): T = afterPhase(currentRun.mixinPhase)(op) - @inline final def afterPickler[T](op: => T): T = afterPhase(currentRun.picklerPhase)(op) - @inline final def afterRefchecks[T](op: => T): T = afterPhase(currentRun.refchecksPhase)(op) - @inline final def afterSpecialize[T](op: => T): T = afterPhase(currentRun.specializePhase)(op) - @inline final def afterTyper[T](op: => T): T = afterPhase(currentRun.typerPhase)(op) - @inline final def afterUncurry[T](op: => T): T = afterPhase(currentRun.uncurryPhase)(op) - @inline final def beforeErasure[T](op: => T): T = beforePhase(currentRun.erasurePhase)(op) + @inline final def afterErasure[T](op: => T): T = afterPhase(currentRun.erasurePhase)(op) + @inline final def afterExplicitOuter[T](op: => T): T = afterPhase(currentRun.explicitouterPhase)(op) + @inline final def afterFlatten[T](op: => T): T = afterPhase(currentRun.flattenPhase)(op) + @inline final def afterIcode[T](op: => T): T = afterPhase(currentRun.icodePhase)(op) + @inline final def afterMixin[T](op: => T): T = afterPhase(currentRun.mixinPhase)(op) + @inline final def afterPickler[T](op: => T): T = afterPhase(currentRun.picklerPhase)(op) + @inline final def afterRefchecks[T](op: => T): T = afterPhase(currentRun.refchecksPhase)(op) + @inline final def afterSpecialize[T](op: => T): T = afterPhase(currentRun.specializePhase)(op) + @inline final def afterTyper[T](op: => T): T = afterPhase(currentRun.typerPhase)(op) + @inline final def afterUncurry[T](op: => T): T = afterPhase(currentRun.uncurryPhase)(op) + @inline final def beforeErasure[T](op: => T): T = beforePhase(currentRun.erasurePhase)(op) @inline final def beforeExplicitOuter[T](op: => T): T = beforePhase(currentRun.explicitouterPhase)(op) - @inline final def beforeFlatten[T](op: => T): T = beforePhase(currentRun.flattenPhase)(op) - @inline final def beforeIcode[T](op: => T): T = beforePhase(currentRun.icodePhase)(op) - @inline final def beforeMixin[T](op: => T): T = beforePhase(currentRun.mixinPhase)(op) - @inline final def beforePickler[T](op: => T): T = beforePhase(currentRun.picklerPhase)(op) - @inline final def beforeRefchecks[T](op: => T): T = beforePhase(currentRun.refchecksPhase)(op) - @inline final def beforeSpecialize[T](op: => T): T = beforePhase(currentRun.specializePhase)(op) - @inline final def beforeTyper[T](op: => T): T = beforePhase(currentRun.typerPhase)(op) - @inline final def beforeUncurry[T](op: => T): T = beforePhase(currentRun.uncurryPhase)(op) + @inline final def beforeFlatten[T](op: => T): T = beforePhase(currentRun.flattenPhase)(op) + @inline final def beforeIcode[T](op: => T): T = beforePhase(currentRun.icodePhase)(op) + @inline final def beforeMixin[T](op: => T): T = beforePhase(currentRun.mixinPhase)(op) + @inline final def beforePickler[T](op: => T): T = beforePhase(currentRun.picklerPhase)(op) + @inline final def beforeRefchecks[T](op: => T): T = beforePhase(currentRun.refchecksPhase)(op) + @inline final def beforeSpecialize[T](op: => T): T = beforePhase(currentRun.specializePhase)(op) + @inline final def beforeTyper[T](op: => T): T = beforePhase(currentRun.typerPhase)(op) + @inline final def beforeUncurry[T](op: => T): T = beforePhase(currentRun.uncurryPhase)(op) /** Don't want to introduce new errors trying to report errors, * so swallow exceptions. @@ -997,16 +997,18 @@ class Global(var currentSettings: Settings, var reporter: Reporter) extends Symb // Each subcomponent supplies a phase, which are chained together. // If -Ystop:phase is given, neither that phase nor any beyond it is added. // If -Yskip:phase is given, that phase will be skipped. - val lastPhase = phaseDescriptors.tail . - takeWhile (pd => !stopPhase(pd.phaseName)) . - filterNot (pd => skipPhase(pd.phaseName)) . - foldLeft (parserPhase) ((chain, ph) => ph newPhase chain) - - // Ensure there is a terminal phase at the end, since -Ystop may have limited the phases. - terminalPhase = - if (lastPhase.name == "terminal") lastPhase - else terminal newPhase lastPhase - + val phaseLinks = { + val phs = ( + phaseDescriptors.tail + takeWhile (pd => !stopPhase(pd.phaseName)) + filterNot (pd => skipPhase(pd.phaseName)) + ) + // Ensure there is a terminal phase at the end, since -Ystop may have limited the phases. + if (phs.isEmpty || (phs.last ne terminal)) phs :+ terminal + else phs + } + // Link them together. + phaseLinks.foldLeft(parserPhase)((chain, ph) => ph newPhase chain) parserPhase } @@ -1090,32 +1092,38 @@ class Global(var currentSettings: Settings, var reporter: Reporter) extends Symb // ----- finding phases -------------------------------------------- - def phaseNamed(name: String): Phase = { - var p: Phase = firstPhase - while (p.next != p && p.name != name) p = p.next - if (p.name != name) NoPhase else p - } - - val parserPhase = phaseNamed("parser") - val namerPhase = phaseNamed("namer") - // packageobjects - val typerPhase = phaseNamed("typer") - // superaccessors - val picklerPhase = phaseNamed("pickler") - val refchecksPhase = phaseNamed("refchecks") - val uncurryPhase = phaseNamed("uncurry") - // tailcalls, specialize - val specializePhase = phaseNamed("specialize") - val explicitouterPhase = phaseNamed("explicitouter") - val erasurePhase = phaseNamed("erasure") - // lazyvals, lambdalift, constructors - val lambdaLiftPhase = phaseNamed("lambdalift") - val flattenPhase = phaseNamed("flatten") - val mixinPhase = phaseNamed("mixin") - val cleanupPhase = phaseNamed("cleanup") - val icodePhase = phaseNamed("icode") - // inliner, closelim, dce - val jvmPhase = phaseNamed("jvm") + def phaseNamed(name: String): Phase = + findOrElse(firstPhase.iterator)(_.name == name)(NoPhase) + + /** All phases as of 3/2012 here for handiness; the ones in + * active use uncommented. + */ + val parserPhase = phaseNamed("parser") + val namerPhase = phaseNamed("namer") + // val packageobjectsPhase = phaseNamed("packageobjects") + val typerPhase = phaseNamed("typer") + // val superaccessorsPhase = phaseNamed("superaccessors") + val picklerPhase = phaseNamed("pickler") + val refchecksPhase = phaseNamed("refchecks") + // val selectiveanfPhase = phaseNamed("selectiveanf") + // val selectivecpsPhase = phaseNamed("selectivecps") + val uncurryPhase = phaseNamed("uncurry") + // val tailcallsPhase = phaseNamed("tailcalls") + val specializePhase = phaseNamed("specialize") + val explicitouterPhase = phaseNamed("explicitouter") + val erasurePhase = phaseNamed("erasure") + // val lazyvalsPhase = phaseNamed("lazyvals") + val lambdaliftPhase = phaseNamed("lambdalift") + // val constructorsPhase = phaseNamed("constructors") + val flattenPhase = phaseNamed("flatten") + val mixinPhase = phaseNamed("mixin") + val cleanupPhase = phaseNamed("cleanup") + val icodePhase = phaseNamed("icode") + // val inlinerPhase = phaseNamed("inliner") + // val inlineExceptionHandlersPhase = phaseNamed("inlineExceptionHandlers") + // val closelimPhase = phaseNamed("closelim") + // val dcePhase = phaseNamed("dce") + val jvmPhase = phaseNamed("jvm") def runIsAt(ph: Phase) = globalPhase.id == ph.id def runIsPast(ph: Phase) = globalPhase.id > ph.id @@ -1269,7 +1277,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter) extends Symb checkDeprecatedSettings(unitbuf.head) globalPhase = fromPhase - while (globalPhase != terminalPhase && !reporter.hasErrors) { + while (globalPhase.hasNext && !reporter.hasErrors) { val startTime = currentTime phase = globalPhase @@ -1390,19 +1398,13 @@ class Global(var currentSettings: Settings, var reporter: Reporter) extends Symb /** Compile abstract file until `globalPhase`, but at least to phase "namer". */ def compileLate(unit: CompilationUnit) { - def stop(ph: Phase) = ph == null || ph.id >= (globalPhase.id max typerPhase.id) - def loop(ph: Phase) { - if (stop(ph)) refreshProgress - else { - atPhase(ph)(ph.asInstanceOf[GlobalPhase] applyPhase unit) - loop(ph.next match { - case `ph` => null // ph == ph.next implies terminal, and null ends processing - case x => x - }) - } - } + val maxId = math.max(globalPhase.id, typerPhase.id) addUnit(unit) - loop(firstPhase) + + firstPhase.iterator takeWhile (_.id < maxId) foreach (ph => + atPhase(ph)(ph.asInstanceOf[GlobalPhase] applyPhase unit) + ) + refreshProgress } /** diff --git a/src/compiler/scala/tools/nsc/SubComponent.scala b/src/compiler/scala/tools/nsc/SubComponent.scala index df63035007..a3e451f32f 100644 --- a/src/compiler/scala/tools/nsc/SubComponent.scala +++ b/src/compiler/scala/tools/nsc/SubComponent.scala @@ -47,8 +47,8 @@ abstract class SubComponent { private var ownPhaseCache: WeakReference[Phase] = new WeakReference(null) private var ownPhaseRunId = global.NoRunId - @inline final def atOwnPhase[T](op: => T) = global.atPhase(ownPhase)(op) - @inline final def afterOwnPhase[T](op: => T) = global.afterPhase(ownPhase)(op) + @inline final def beforeOwnPhase[T](op: => T) = global.beforePhase(ownPhase)(op) + @inline final def afterOwnPhase[T](op: => T) = global.afterPhase(ownPhase)(op) /** The phase corresponding to this subcomponent in the current compiler run */ def ownPhase: Phase = { diff --git a/src/compiler/scala/tools/nsc/backend/icode/Members.scala b/src/compiler/scala/tools/nsc/backend/icode/Members.scala index 97247dd89b..36651541b2 100644 --- a/src/compiler/scala/tools/nsc/backend/icode/Members.scala +++ b/src/compiler/scala/tools/nsc/backend/icode/Members.scala @@ -183,12 +183,7 @@ trait Members { this } - def addLocal(l: Local): Local = - locals find (_ == l) getOrElse { - locals ::= l - l - } - + def addLocal(l: Local): Local = findOrElse(locals)(_ == l) { locals ::= l ; l } def addParam(p: Local): Unit = if (params contains p) () diff --git a/src/compiler/scala/tools/nsc/backend/opt/Inliners.scala b/src/compiler/scala/tools/nsc/backend/opt/Inliners.scala index 2398f8406c..e91bab8367 100644 --- a/src/compiler/scala/tools/nsc/backend/opt/Inliners.scala +++ b/src/compiler/scala/tools/nsc/backend/opt/Inliners.scala @@ -724,6 +724,7 @@ abstract class Inliners extends SubComponent { def failureReason(stackLength: Int) = if (!inc.m.hasCode) "bytecode was unavailable" + else if (inc.m.symbol.hasFlag(Flags.SYNCHRONIZED)) "method is synchronized" else if (!isSafeToInline(stackLength)) "it is unsafe (target may reference private fields)" else "of a bug (run with -Ylog:inline -Ydebug for more information)" diff --git a/src/compiler/scala/tools/nsc/symtab/classfile/Pickler.scala b/src/compiler/scala/tools/nsc/symtab/classfile/Pickler.scala index 25ae6f33d2..758f870d6b 100644 --- a/src/compiler/scala/tools/nsc/symtab/classfile/Pickler.scala +++ b/src/compiler/scala/tools/nsc/symtab/classfile/Pickler.scala @@ -77,7 +77,7 @@ abstract class Pickler extends SubComponent { private var entries = new Array[AnyRef](256) private var ep = 0 private val index = new LinkedHashMap[AnyRef, Int] - private lazy val nonClassRoot = root.ownersIterator.find(! _.isClass) getOrElse NoSymbol + private lazy val nonClassRoot = findOrElse(root.ownersIterator)(!_.isClass)(NoSymbol) private def isRootSym(sym: Symbol) = sym.name.toTermName == rootName && sym.owner == rootOwner diff --git a/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala b/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala index 45045b1909..595c1486b6 100644 --- a/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala +++ b/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala @@ -89,8 +89,8 @@ abstract class ExplicitOuter extends InfoTransform def outerAccessor(clazz: Symbol): Symbol = { val firstTry = clazz.info.decl(nme.expandedName(nme.OUTER, clazz)) if (firstTry != NoSymbol && firstTry.outerSource == clazz) firstTry - else clazz.info.decls find (_.outerSource == clazz) getOrElse NoSymbol - } + else findOrElse(clazz.info.decls)(_.outerSource == clazz)(NoSymbol) + } def newOuterAccessor(clazz: Symbol) = { val accFlags = SYNTHETIC | METHOD | STABLE | ( if (clazz.isTrait) DEFERRED else 0 ) val sym = clazz.newMethodSymbol(nme.OUTER, clazz.pos, accFlags) diff --git a/src/compiler/scala/tools/nsc/transform/Mixin.scala b/src/compiler/scala/tools/nsc/transform/Mixin.scala index 050425c558..c9794cc20f 100644 --- a/src/compiler/scala/tools/nsc/transform/Mixin.scala +++ b/src/compiler/scala/tools/nsc/transform/Mixin.scala @@ -147,7 +147,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { member.hasAccessorFlag && (!member.isDeferred || (member hasFlag lateDEFERRED)) /** Is member overridden (either directly or via a bridge) in base class sequence `bcs`? */ - def isOverriddenAccessor(member: Symbol, bcs: List[Symbol]): Boolean = atOwnPhase { + def isOverriddenAccessor(member: Symbol, bcs: List[Symbol]): Boolean = beforeOwnPhase { def hasOverridingAccessor(clazz: Symbol) = { clazz.info.nonPrivateDecl(member.name).alternatives.exists( sym => @@ -1160,7 +1160,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { def implSym = implClass(sym.owner).info.member(sym.name) assert(target ne NoSymbol, List(sym + ":", sym.tpe, sym.owner, implClass(sym.owner), implSym, - atPhase(phase.prev)(implSym.tpe), phase) mkString " " + beforePrevPhase(implSym.tpe), phase) mkString " " ) typedPos(tree.pos)(Apply(staticRef(target), transformSuper(qual) :: args)) } @@ -1185,7 +1185,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { val sym1 = sym.overridingSymbol(currentOwner.enclClass) typedPos(tree.pos)((transformSuper(qual) DOT sym1)()) } else { - staticCall(atPhase(phase.prev)(sym.overridingSymbol(implClass(sym.owner)))) + staticCall(beforePrevPhase(sym.overridingSymbol(implClass(sym.owner)))) } } else { assert(!currentOwner.enclClass.isImplClass, currentOwner.enclClass) diff --git a/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala b/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala index be6200b353..0851dad0c2 100644 --- a/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala +++ b/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala @@ -1505,10 +1505,10 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { deriveValDef(newValDef)(transform) case Apply(sel @ Select(sup @ Super(qual, name), name1), args) - if (sup.symbol.info.parents != atPhase(phase.prev)(sup.symbol.info.parents)) => + if (sup.symbol.info.parents != beforePrevPhase(sup.symbol.info.parents)) => def parents = sup.symbol.info.parents - debuglog(tree + " parents changed from: " + atPhase(phase.prev)(parents) + " to: " + parents) + debuglog(tree + " parents changed from: " + beforePrevPhase(parents) + " to: " + parents) val res = localTyper.typed( Apply(Select(Super(qual, name) setPos sup.pos, name1) setPos sel.pos, transformTrees(args)) setPos tree.pos) -- cgit v1.2.3 From d92bcb5302be04104abe2db6cf92620c4cd7255e Mon Sep 17 00:00:00 2001 From: Miguel Garcia Date: Fri, 2 Mar 2012 10:55:10 +0100 Subject: calling a side-effecting boolean-returning method last in condition --- src/compiler/scala/tools/nsc/backend/opt/Inliners.scala | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/compiler/scala/tools/nsc/backend/opt/Inliners.scala b/src/compiler/scala/tools/nsc/backend/opt/Inliners.scala index 2398f8406c..cc0c244695 100644 --- a/src/compiler/scala/tools/nsc/backend/opt/Inliners.scala +++ b/src/compiler/scala/tools/nsc/backend/opt/Inliners.scala @@ -707,7 +707,8 @@ abstract class Inliners extends SubComponent { } def isStampedForInlining(stackLength: Int) = - !sameSymbols && inc.m.hasCode && shouldInline && isSafeToInline(stackLength) && !inc.m.symbol.hasFlag(Flags.SYNCHRONIZED) + !sameSymbols && inc.m.hasCode && shouldInline && + isSafeToInline(stackLength) // `isSafeToInline()` must be invoked last in this AND expr bc it mutates the `knownSafe` and `knownUnsafe` maps for good. def logFailure(stackLength: Int) = log( """|inline failed for %s: @@ -764,8 +765,8 @@ abstract class Inliners extends SubComponent { true } - if (!inc.m.hasCode || inc.isRecursive) - return false + if (!inc.m.hasCode || inc.isRecursive) { return false } + if (inc.m.symbol.hasFlag(Flags.SYNCHRONIZED)) { return false } val accessNeeded = usesNonPublics.getOrElseUpdate(inc.m, { // Avoiding crashing the compiler if there are open blocks. -- cgit v1.2.3 From 6aca8a074ac7c05d3bb2429bffa7ce922f9c8bd9 Mon Sep 17 00:00:00 2001 From: Aleksandar Prokopec Date: Fri, 2 Mar 2012 17:47:38 +0100 Subject: Adding execution context based task support implementation. Parallel collections now get the execution context task support which by default picks the execution context from the scala concurrent package. This execution context task support forwards calls to either a fork join task support or the thread pool task support. Additionally, the default execution context now uses either a fork join pool or a thread pool executor, depending on the JVM vendor and version. --- .../scala/collection/parallel/TaskSupport.scala | 17 +++- src/library/scala/collection/parallel/Tasks.scala | 101 +++++++++++++-------- .../scala/concurrent/ConcurrentPackageObject.scala | 22 +++-- .../concurrent/impl/ExecutionContextImpl.scala | 15 ++- 4 files changed, 104 insertions(+), 51 deletions(-) (limited to 'src') diff --git a/src/library/scala/collection/parallel/TaskSupport.scala b/src/library/scala/collection/parallel/TaskSupport.scala index 20800250b4..fc99347316 100644 --- a/src/library/scala/collection/parallel/TaskSupport.scala +++ b/src/library/scala/collection/parallel/TaskSupport.scala @@ -11,15 +11,26 @@ package scala.collection.parallel - +import java.util.concurrent.ThreadPoolExecutor +import scala.concurrent.forkjoin.ForkJoinPool +import scala.concurrent.ExecutionContext trait TaskSupport extends Tasks -private[collection] class ForkJoinTaskSupport extends TaskSupport with AdaptiveWorkStealingForkJoinTasks -private[collection] class ThreadPoolTaskSupport extends TaskSupport with AdaptiveWorkStealingThreadPoolTasks +private[collection] class ForkJoinTaskSupport(val environment: ForkJoinPool = ForkJoinTasks.defaultForkJoinPool) +extends TaskSupport with AdaptiveWorkStealingForkJoinTasks + + +private[collection] class ThreadPoolTaskSupport(val environment: ThreadPoolExecutor = ThreadPoolTasks.defaultThreadPool) +extends TaskSupport with AdaptiveWorkStealingThreadPoolTasks + + +private[collection] class ExecutionContextTaskSupport(val environment: ExecutionContext = scala.concurrent.executionContext) +extends TaskSupport with ExecutionContextTasks + diff --git a/src/library/scala/collection/parallel/Tasks.scala b/src/library/scala/collection/parallel/Tasks.scala index a7f2c586a7..60a8bb1ed6 100644 --- a/src/library/scala/collection/parallel/Tasks.scala +++ b/src/library/scala/collection/parallel/Tasks.scala @@ -10,7 +10,10 @@ package scala.collection.parallel +import java.util.concurrent.ThreadPoolExecutor + import scala.concurrent.forkjoin._ +import scala.concurrent.ExecutionContext import scala.util.control.Breaks._ import annotation.unchecked.uncheckedVariance @@ -101,11 +104,11 @@ trait Tasks { debugMessages += s } - trait TaskImpl[R, +Tp] { + trait WrappedTask[R, +Tp] { /** the body of this task - what it executes, how it gets split and how results are merged. */ val body: Task[R, Tp] - def split: Seq[TaskImpl[R, Tp]] + def split: Seq[WrappedTask[R, Tp]] /** Code that gets called after the task gets started - it may spawn other tasks instead of calling `leaf`. */ def compute() /** Start task. */ @@ -126,13 +129,10 @@ trait Tasks { def release() {} } - protected def newTaskImpl[R, Tp](b: Task[R, Tp]): TaskImpl[R, Tp] - /* task control */ - // safe to assume it will always have the same type, - // because the `tasksupport` in parallel iterable is final - var environment: AnyRef + /** The type of the environment is more specific in the implementations. */ + val environment: AnyRef /** Executes a task and returns a future. Forwards an exception if some task threw it. */ def execute[R, Tp](fjtask: Task[R, Tp]): () => R @@ -152,11 +152,11 @@ trait Tasks { */ trait AdaptiveWorkStealingTasks extends Tasks { - trait TaskImpl[R, Tp] extends super.TaskImpl[R, Tp] { - @volatile var next: TaskImpl[R, Tp] = null + trait WrappedTask[R, Tp] extends super.WrappedTask[R, Tp] { + @volatile var next: WrappedTask[R, Tp] = null @volatile var shouldWaitFor = true - def split: Seq[TaskImpl[R, Tp]] + def split: Seq[WrappedTask[R, Tp]] def compute() = if (body.shouldSplitFurther) { internal() @@ -192,8 +192,8 @@ trait AdaptiveWorkStealingTasks extends Tasks { } def spawnSubtasks() = { - var last: TaskImpl[R, Tp] = null - var head: TaskImpl[R, Tp] = this + var last: WrappedTask[R, Tp] = null + var head: WrappedTask[R, Tp] = this do { val subtasks = head.split head = subtasks.head @@ -219,7 +219,7 @@ trait AdaptiveWorkStealingTasks extends Tasks { } // specialize ctor - protected def newTaskImpl[R, Tp](b: Task[R, Tp]): TaskImpl[R, Tp] + protected def newWrappedTask[R, Tp](b: Task[R, Tp]): WrappedTask[R, Tp] } @@ -228,7 +228,7 @@ trait AdaptiveWorkStealingTasks extends Tasks { trait ThreadPoolTasks extends Tasks { import java.util.concurrent._ - trait TaskImpl[R, +Tp] extends Runnable with super.TaskImpl[R, Tp] { + trait WrappedTask[R, +Tp] extends Runnable with super.WrappedTask[R, Tp] { // initially, this is null // once the task is started, this future is set and used for `sync` // utb: var future: Future[_] = null @@ -290,9 +290,9 @@ trait ThreadPoolTasks extends Tasks { } } - protected def newTaskImpl[R, Tp](b: Task[R, Tp]): TaskImpl[R, Tp] + protected def newWrappedTask[R, Tp](b: Task[R, Tp]): WrappedTask[R, Tp] - var environment: AnyRef = ThreadPoolTasks.defaultThreadPool + val environment: ThreadPoolExecutor def executor = environment.asInstanceOf[ThreadPoolExecutor] def queue = executor.getQueue.asInstanceOf[LinkedBlockingQueue[Runnable]] @volatile var totaltasks = 0 @@ -306,7 +306,7 @@ trait ThreadPoolTasks extends Tasks { } def execute[R, Tp](task: Task[R, Tp]): () => R = { - val t = newTaskImpl(task) + val t = newWrappedTask(task) // debuglog("-----------> Executing without wait: " + task) t.start() @@ -319,7 +319,7 @@ trait ThreadPoolTasks extends Tasks { } def executeAndWaitResult[R, Tp](task: Task[R, Tp]): R = { - val t = newTaskImpl(task) + val t = newWrappedTask(task) // debuglog("-----------> Executing with wait: " + task) t.start() @@ -359,10 +359,11 @@ object ThreadPoolTasks { /** An implementation of tasks objects based on the Java thread pooling API and synchronization using futures. */ +@deprecated("This implementation is not used.") trait FutureThreadPoolTasks extends Tasks { import java.util.concurrent._ - trait TaskImpl[R, +Tp] extends Runnable with super.TaskImpl[R, Tp] { + trait WrappedTask[R, +Tp] extends Runnable with super.WrappedTask[R, Tp] { @volatile var future: Future[_] = null def start() = { @@ -377,13 +378,13 @@ trait FutureThreadPoolTasks extends Tasks { } } - protected def newTaskImpl[R, Tp](b: Task[R, Tp]): TaskImpl[R, Tp] + protected def newWrappedTask[R, Tp](b: Task[R, Tp]): WrappedTask[R, Tp] - var environment: AnyRef = FutureThreadPoolTasks.defaultThreadPool + val environment: AnyRef = FutureThreadPoolTasks.defaultThreadPool def executor = environment.asInstanceOf[ThreadPoolExecutor] def execute[R, Tp](task: Task[R, Tp]): () => R = { - val t = newTaskImpl(task) + val t = newWrappedTask(task) // debuglog("-----------> Executing without wait: " + task) t.start @@ -396,7 +397,7 @@ trait FutureThreadPoolTasks extends Tasks { } def executeAndWaitResult[R, Tp](task: Task[R, Tp]): R = { - val t = newTaskImpl(task) + val t = newWrappedTask(task) // debuglog("-----------> Executing with wait: " + task) t.start @@ -438,26 +439,26 @@ trait HavingForkJoinPool { */ trait ForkJoinTasks extends Tasks with HavingForkJoinPool { - trait TaskImpl[R, +Tp] extends RecursiveAction with super.TaskImpl[R, Tp] { + trait WrappedTask[R, +Tp] extends RecursiveAction with super.WrappedTask[R, Tp] { def start() = fork def sync() = join def tryCancel = tryUnfork } // specialize ctor - protected def newTaskImpl[R, Tp](b: Task[R, Tp]): TaskImpl[R, Tp] + protected def newWrappedTask[R, Tp](b: Task[R, Tp]): WrappedTask[R, Tp] /** The fork/join pool of this collection. */ def forkJoinPool: ForkJoinPool = environment.asInstanceOf[ForkJoinPool] - var environment: AnyRef = ForkJoinTasks.defaultForkJoinPool + val environment: ForkJoinPool /** Executes a task and does not wait for it to finish - instead returns a future. * * $fjdispatch */ def execute[R, Tp](task: Task[R, Tp]): () => R = { - val fjtask = newTaskImpl(task) + val fjtask = newWrappedTask(task) if (Thread.currentThread.isInstanceOf[ForkJoinWorkerThread]) { fjtask.fork @@ -480,7 +481,7 @@ trait ForkJoinTasks extends Tasks with HavingForkJoinPool { * @return the result of the task */ def executeAndWaitResult[R, Tp](task: Task[R, Tp]): R = { - val fjtask = newTaskImpl(task) + val fjtask = newWrappedTask(task) if (Thread.currentThread.isInstanceOf[ForkJoinWorkerThread]) { fjtask.fork @@ -510,25 +511,50 @@ object ForkJoinTasks { */ trait AdaptiveWorkStealingForkJoinTasks extends ForkJoinTasks with AdaptiveWorkStealingTasks { - class TaskImpl[R, Tp](val body: Task[R, Tp]) - extends super[ForkJoinTasks].TaskImpl[R, Tp] with super[AdaptiveWorkStealingTasks].TaskImpl[R, Tp] { - def split = body.split.map(b => newTaskImpl(b)) + class WrappedTask[R, Tp](val body: Task[R, Tp]) + extends super[ForkJoinTasks].WrappedTask[R, Tp] with super[AdaptiveWorkStealingTasks].WrappedTask[R, Tp] { + def split = body.split.map(b => newWrappedTask(b)) } - def newTaskImpl[R, Tp](b: Task[R, Tp]) = new TaskImpl[R, Tp](b) + def newWrappedTask[R, Tp](b: Task[R, Tp]) = new WrappedTask[R, Tp](b) } trait AdaptiveWorkStealingThreadPoolTasks extends ThreadPoolTasks with AdaptiveWorkStealingTasks { - class TaskImpl[R, Tp](val body: Task[R, Tp]) - extends super[ThreadPoolTasks].TaskImpl[R, Tp] with super[AdaptiveWorkStealingTasks].TaskImpl[R, Tp] { - def split = body.split.map(b => newTaskImpl(b)) + class WrappedTask[R, Tp](val body: Task[R, Tp]) + extends super[ThreadPoolTasks].WrappedTask[R, Tp] with super[AdaptiveWorkStealingTasks].WrappedTask[R, Tp] { + def split = body.split.map(b => newWrappedTask(b)) } - def newTaskImpl[R, Tp](b: Task[R, Tp]) = new TaskImpl[R, Tp](b) + def newWrappedTask[R, Tp](b: Task[R, Tp]) = new WrappedTask[R, Tp](b) + +} + +trait ExecutionContextTasks extends Tasks { + + def executionContext = environment + + val environment: ExecutionContext + + // this part is a hack which allows switching + val driver: Tasks = executionContext match { + case eci: scala.concurrent.impl.ExecutionContextImpl => eci.executorService match { + case fjp: ForkJoinPool => new ForkJoinTaskSupport(fjp) + case tpe: ThreadPoolExecutor => new ThreadPoolTaskSupport(tpe) + case _ => ??? + } + case _ => ??? + } + + def execute[R, Tp](task: Task[R, Tp]): () => R = driver execute task + + def executeAndWaitResult[R, Tp](task: Task[R, Tp]): R = driver executeAndWaitResult task + + def parallelismLevel = driver.parallelismLevel + } @@ -538,3 +564,6 @@ trait AdaptiveWorkStealingThreadPoolTasks extends ThreadPoolTasks with AdaptiveW + + + diff --git a/src/library/scala/concurrent/ConcurrentPackageObject.scala b/src/library/scala/concurrent/ConcurrentPackageObject.scala index ae17c7e032..7d005838d3 100644 --- a/src/library/scala/concurrent/ConcurrentPackageObject.scala +++ b/src/library/scala/concurrent/ConcurrentPackageObject.scala @@ -8,22 +8,30 @@ package scala.concurrent + + +import java.util.concurrent.{ Executors, ExecutorService } +import scala.concurrent.forkjoin.ForkJoinPool 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 - + new impl.ExecutionContextImpl(getExecutorService) + + private[concurrent] def getExecutorService: AnyRef = + if (util.Properties.isJavaAtLeast("1.6")) { + val vendor = util.Properties.javaVmVendor + if ((vendor contains "Oracle") || (vendor contains "Sun") || (vendor contains "Apple")) new ForkJoinPool + else Executors.newCachedThreadPool() + } else Executors.newCachedThreadPool() + val handledFutureException: PartialFunction[Throwable, Throwable] = { case t: Throwable if isFutureThrowable(t) => t } diff --git a/src/library/scala/concurrent/impl/ExecutionContextImpl.scala b/src/library/scala/concurrent/impl/ExecutionContextImpl.scala index 7b44d02612..7984aa02b7 100644 --- a/src/library/scala/concurrent/impl/ExecutionContextImpl.scala +++ b/src/library/scala/concurrent/impl/ExecutionContextImpl.scala @@ -11,20 +11,25 @@ package scala.concurrent.impl import java.util.concurrent.{Callable, ExecutorService} +import scala.concurrent.forkjoin._ 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 { +class ExecutionContextImpl(val executorService: AnyRef) extends ExecutionContext { import ExecutionContextImpl._ def execute(runnable: Runnable): Unit = executorService match { - // case fj: ForkJoinPool => - // TODO fork if more applicable - // executorService execute runnable - case _ => + case fj: ForkJoinPool => + if (Thread.currentThread.isInstanceOf[ForkJoinWorkerThread]) { + val fjtask = ForkJoinTask.adapt(runnable) + fjtask.fork + } else { + fj.execute(runnable) + } + case executorService: ExecutorService => executorService execute runnable } -- cgit v1.2.3 From 9f5767dd31395cac0bc64c86c2cacf247b1924fa Mon Sep 17 00:00:00 2001 From: Tiark Rompf Date: Fri, 2 Mar 2012 23:23:56 +0100 Subject: Fix for cps regression. Closes 5538. Closes 5445. --- .../tools/selectivecps/CPSAnnotationChecker.scala | 22 +++++----- .../tools/selectivecps/SelectiveANFTransform.scala | 14 +++--- test/files/continuations-neg/t5445.check | 4 ++ test/files/continuations-neg/t5445.scala | 5 +++ test/files/continuations-run/t5538.check | 1 + test/files/continuations-run/t5538.scala | 50 ++++++++++++++++++++++ 6 files changed, 81 insertions(+), 15 deletions(-) create mode 100644 test/files/continuations-neg/t5445.check create mode 100644 test/files/continuations-neg/t5445.scala create mode 100644 test/files/continuations-run/t5538.check create mode 100644 test/files/continuations-run/t5538.scala (limited to 'src') diff --git a/src/continuations/plugin/scala/tools/selectivecps/CPSAnnotationChecker.scala b/src/continuations/plugin/scala/tools/selectivecps/CPSAnnotationChecker.scala index 0382304bad..af0d768607 100644 --- a/src/continuations/plugin/scala/tools/selectivecps/CPSAnnotationChecker.scala +++ b/src/continuations/plugin/scala/tools/selectivecps/CPSAnnotationChecker.scala @@ -336,29 +336,31 @@ abstract class CPSAnnotationChecker extends CPSUtils { def single(xs: List[AnnotationInfo]) = xs match { case List(x) => x case _ => - global.globalError("not a single cps annotation: " + xs)// FIXME: error message + global.globalError("not a single cps annotation: " + xs) xs(0) } + + def emptyOrSingleList(xs: List[AnnotationInfo]) = if (xs.isEmpty) Nil else List(single(xs)) def transChildrenInOrder(tree: Tree, tpe: Type, childTrees: List[Tree], byName: List[Tree]) = { def inspect(t: Tree): List[AnnotationInfo] = { if (t.tpe eq null) Nil else { val extra: List[AnnotationInfo] = t.tpe match { case _: MethodType | _: PolyType | _: OverloadedType => - // method types, poly types and overloaded types do not obtain cps annotions by propagat - // need to reconstruct transitively from their children. - t match { - case Select(qual, name) => inspect(qual) - case Apply(fun, args) => (fun::args) flatMap inspect - case TypeApply(fun, args) => (fun::args) flatMap inspect - case _ => Nil - } + // method types, poly types and overloaded types do not obtain cps annotions by propagation + // need to reconstruct transitively from their children. + t match { + case Select(qual, name) => inspect(qual) + case Apply(fun, args) => (fun::(transArgList(fun,args).flatten)) flatMap inspect + case TypeApply(fun, args) => (fun::(transArgList(fun,args).flatten)) flatMap inspect + case _ => Nil + } case _ => Nil } val types = cpsParamAnnotation(t.tpe) // TODO: check that it has been adapted and if so correctly - extra ++ (if (types.isEmpty) Nil else List(single(types))) + extra ++ emptyOrSingleList(types) } } val children = childTrees flatMap inspect diff --git a/src/continuations/plugin/scala/tools/selectivecps/SelectiveANFTransform.scala b/src/continuations/plugin/scala/tools/selectivecps/SelectiveANFTransform.scala index d98169f21a..1189cc2e38 100644 --- a/src/continuations/plugin/scala/tools/selectivecps/SelectiveANFTransform.scala +++ b/src/continuations/plugin/scala/tools/selectivecps/SelectiveANFTransform.scala @@ -97,13 +97,17 @@ abstract class SelectiveANFTransform extends PluginComponent with Transform with case vd @ ValDef(mods, name, tpt, rhs) => // object-level valdefs debuglog("transforming valdef " + vd.symbol) - atOwner(vd.symbol) { + if (getExternalAnswerTypeAnn(tpt.tpe).isEmpty) { + + atOwner(vd.symbol) { - assert(getExternalAnswerTypeAnn(tpt.tpe) == None) + val rhs1 = transExpr(rhs, None, None) - val rhs1 = transExpr(rhs, None, None) - - treeCopy.ValDef(vd, mods, name, transform(tpt), rhs1) + treeCopy.ValDef(vd, mods, name, transform(tpt), rhs1) + } + } else { + unit.error(tree.pos, "cps annotations not allowed on by-value parameters or value definitions") + super.transform(tree) } case TypeTree() => diff --git a/test/files/continuations-neg/t5445.check b/test/files/continuations-neg/t5445.check new file mode 100644 index 0000000000..eb2943b6a6 --- /dev/null +++ b/test/files/continuations-neg/t5445.check @@ -0,0 +1,4 @@ +t5445.scala:4: error: cps annotations not allowed on by-value parameters or value definitions + def foo(block: Unit @suspendable ): Unit @suspendable = {} + ^ +one error found diff --git a/test/files/continuations-neg/t5445.scala b/test/files/continuations-neg/t5445.scala new file mode 100644 index 0000000000..cb6f8f686d --- /dev/null +++ b/test/files/continuations-neg/t5445.scala @@ -0,0 +1,5 @@ +import scala.util.continuations._ + +object Test { + def foo(block: Unit @suspendable ): Unit @suspendable = {} +} diff --git a/test/files/continuations-run/t5538.check b/test/files/continuations-run/t5538.check new file mode 100644 index 0000000000..457721d5e0 --- /dev/null +++ b/test/files/continuations-run/t5538.check @@ -0,0 +1 @@ +Future(Future(Future(Future(Future(List(1, 2, 3, 4, 5)))))) diff --git a/test/files/continuations-run/t5538.scala b/test/files/continuations-run/t5538.scala new file mode 100644 index 0000000000..42f8163caf --- /dev/null +++ b/test/files/continuations-run/t5538.scala @@ -0,0 +1,50 @@ +import scala.util.continuations._ +import scala.collection.generic.CanBuildFrom + +object Test { + + class ExecutionContext + + implicit def defaultExecutionContext = new ExecutionContext + + case class Future[+T](x:T) { + final def map[A](f: T => A): Future[A] = new Future[A](f(x)) + final def flatMap[A](f: T => Future[A]): Future[A] = f(x) + } + + class PromiseStream[A] { + override def toString = xs.toString + + var xs: List[A] = Nil + + final def +=(elem: A): this.type = { xs :+= elem; this } + + final def ++=(elem: Traversable[A]): this.type = { xs ++= elem; this } + + final def <<(elem: Future[A]): PromiseStream[A] @cps[Future[Any]] = + shift { cont: (PromiseStream[A] => Future[Any]) => elem map (a => cont(this += a)) } + + final def <<(elem1: Future[A], elem2: Future[A], elems: Future[A]*): PromiseStream[A] @cps[Future[Any]] = + shift { cont: (PromiseStream[A] => Future[Any]) => Future.flow(this << elem1 << elem2 <<< Future.sequence(elems.toSeq)) map cont } + + final def <<<(elems: Traversable[A]): PromiseStream[A] @cps[Future[Any]] = + shift { cont: (PromiseStream[A] => Future[Any]) => cont(this ++= elems) } + + final def <<<(elems: Future[Traversable[A]]): PromiseStream[A] @cps[Future[Any]] = + shift { cont: (PromiseStream[A] => Future[Any]) => elems map (as => cont(this ++= as)) } + } + + object Future { + + def sequence[A, M[_] <: Traversable[_]](in: M[Future[A]])(implicit cbf: CanBuildFrom[M[Future[A]], A, M[A]], executor: ExecutionContext): Future[M[A]] = + new Future(in.asInstanceOf[Traversable[Future[A]]].map((f:Future[A])=>f.x)(cbf.asInstanceOf[CanBuildFrom[Traversable[Future[A]], A, M[A]]])) + + def flow[A](body: => A @cps[Future[Any]])(implicit executor: ExecutionContext): Future[A] = reset(Future(body)).asInstanceOf[Future[A]] + + } + + def main(args: Array[String]) = { + val p = new PromiseStream[Int] + println(Future.flow(p << (Future(1), Future(2), Future(3), Future(4), Future(5)))) + } +} \ No newline at end of file -- cgit v1.2.3