Skip to content

Commit 5af15bc

Browse files
committed
Add ArrayBuffer and GrowableBuilder to stdlib
1 parent 73ecde2 commit 5af15bc

File tree

8 files changed

+78
-21
lines changed

8 files changed

+78
-21
lines changed
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
def foo[sealed B](x: B): B = x
2+
3+
def bar[B, sealed A >: B](x: A): A = foo[A](x)
4+
5+
class C[sealed A]
6+
7+
class CV[sealed A](x: Int):
8+
def this() = this:
9+
val x = new C[A]:
10+
println("foo")
11+
0
12+

tests/pos-special/stdlib/collection/IterableOnce.scala

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,7 @@ final class IterableOnceExtensionMethods[A](private val it: IterableOnce[A]) ext
162162
def to[C1](factory: Factory[A, C1]): C1 = factory.fromSpecific(it)
163163

164164
@deprecated("Use .iterator.to(ArrayBuffer) instead", "2.13.0")
165-
def toBuffer[B >: A]: mutable.Buffer[B] = mutable.ArrayBuffer.from(it)
165+
def toBuffer[sealed B >: A]: mutable.Buffer[B] = mutable.ArrayBuffer.from(it)
166166

167167
@deprecated("Use .iterator.toArray", "2.13.0")
168168
def toArray[B >: A: ClassTag]: Array[B] = it match {
@@ -272,7 +272,7 @@ object IterableOnce {
272272
math.max(math.min(math.min(len, srcLen), destLen - start), 0)
273273

274274
/** Calls `copyToArray` on the given collection, regardless of whether or not it is an `Iterable`. */
275-
@inline private[collection] def copyElemsToArray[A, B >: A](elems: IterableOnce[A],
275+
@inline private[collection] def copyElemsToArray[A, B >: A](elems: IterableOnce[A]^,
276276
xs: Array[B],
277277
start: Int = 0,
278278
len: Int = Int.MaxValue): Int =
@@ -1312,7 +1312,7 @@ trait IterableOnceOps[+A, +CC[_], +C] extends Any { this: IterableOnce[A]^ =>
13121312
@deprecated("Use .to(LazyList) instead of .toStream", "2.13.0")
13131313
@`inline` final def toStream: immutable.Stream[A] = to(immutable.Stream)
13141314

1315-
@`inline` final def toBuffer[B >: A]: mutable.Buffer[B] = mutable.Buffer.from(this)
1315+
@`inline` final def toBuffer[sealed B >: A]: mutable.Buffer[B] = mutable.Buffer.from(this)
13161316

13171317
/** Convert collection to array.
13181318
*

tests/pos-special/stdlib/collection/Iterator.scala

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import scala.annotation.unchecked.{uncheckedVariance, uncheckedCaptures}
1818
import scala.runtime.Statics
1919
import language.experimental.captureChecking
2020
import caps.unsafe.unsafeAssumePure
21+
import annotation.unchecked.uncheckedCaptures
2122

2223

2324
/** Iterators are data structures that allow to iterate over a sequence
@@ -416,7 +417,9 @@ trait Iterator[+A] extends IterableOnce[A] with IterableOnceOps[A, Iterator, Ite
416417
}
417418

418419
@deprecated("Call scanRight on an Iterable instead.", "2.13.0")
419-
def scanRight[B](z: B)(op: (A, B) => B): Iterator[B]^{this} = ArrayBuffer.from(this).scanRight(z)(op).iterator
420+
def scanRight[B](z: B)(op: (A, B) => B): Iterator[B]^{this} =
421+
ArrayBuffer.from[A @uncheckedCaptures](this).scanRight(z)(op).iterator
422+
// @uncheckedCaptures is safe since the ArrayBuffer is local temporrary storage
420423

421424
def indexWhere(p: A => Boolean, from: Int = 0): Int = {
422425
var i = math.max(from, 0)

tests/pos-special/stdlib/collection/Seq.scala

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import Searching.{Found, InsertionPoint, SearchResult}
1818
import scala.annotation.nowarn
1919
import language.experimental.captureChecking
2020
import caps.unsafe.unsafeAssumePure
21+
import scala.annotation.unchecked.uncheckedCaptures
2122

2223
/** Base trait for sequence collections
2324
*
@@ -598,7 +599,8 @@ trait SeqOps[+A, +CC[_], +C] extends Any with SeqViewOps[A, CC, C] { self =>
598599
if (!hasNext)
599600
Iterator.empty.next()
600601

601-
val forcedElms = new mutable.ArrayBuffer[A](elms.size) ++= elms
602+
val forcedElms = new mutable.ArrayBuffer[A @uncheckedCaptures](elms.size) ++= elms
603+
// uncheckedCaptures OK since used only locally
602604
val result = (newSpecificBuilder ++= forcedElms).result()
603605
var i = idxs.length - 2
604606
while(i >= 0 && idxs(i) >= idxs(i+1))

tests/pos-special/stdlib/collection/View.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ object View extends IterableFactory[View] {
7878

7979
def empty[A]: View[A] = Empty
8080

81-
def newBuilder[A]: Builder[A, View[A]] = ArrayBuffer.newBuilder[A].mapResult(from)
81+
def newBuilder[sealed A]: Builder[A, View[A]] = ArrayBuffer.newBuilder[A].mapResult(from)
8282

8383
override def apply[A](xs: A*): View[A] = new Elems(xs: _*)
8484

tests/pos-special/stdlib/collection/mutable/ArrayBuffer.scala

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ import scala.annotation.nowarn
2020
import scala.annotation.tailrec
2121
import scala.collection.Stepper.EfficientSplit
2222
import scala.collection.generic.DefaultSerializable
23+
import language.experimental.captureChecking
24+
import scala.annotation.unchecked.uncheckedCaptures
2325

2426
/** An implementation of the `Buffer` class using an array to
2527
* represent the assembled sequence internally. Append, update and random
@@ -40,7 +42,7 @@ import scala.collection.generic.DefaultSerializable
4042
* @define willNotTerminateInf
4143
*/
4244
@SerialVersionUID(-1582447879429021880L)
43-
class ArrayBuffer[A] private (initialElements: Array[AnyRef], initialSize: Int)
45+
class ArrayBuffer[sealed A] private (initialElements: Array[AnyRef], initialSize: Int)
4446
extends AbstractBuffer[A]
4547
with IndexedBuffer[A]
4648
with IndexedSeqOps[A, ArrayBuffer, ArrayBuffer[A]]
@@ -151,7 +153,7 @@ class ArrayBuffer[A] private (initialElements: Array[AnyRef], initialSize: Int)
151153
}
152154

153155
// Overridden to use array copying for efficiency where possible.
154-
override def addAll(elems: IterableOnce[A]): this.type = {
156+
override def addAll(elems: IterableOnce[A]^): this.type = {
155157
elems match {
156158
case elems: ArrayBuffer[_] =>
157159
val elemsLength = elems.size0
@@ -180,7 +182,7 @@ class ArrayBuffer[A] private (initialElements: Array[AnyRef], initialSize: Int)
180182
this
181183
}
182184

183-
def insertAll(@deprecatedName("n", "2.13.0") index: Int, elems: IterableOnce[A]): Unit = {
185+
def insertAll(@deprecatedName("n", "2.13.0") index: Int, elems: IterableOnce[A]^): Unit = {
184186
checkWithinBounds(index, index)
185187
elems match {
186188
case elems: collection.Iterable[A] =>
@@ -234,7 +236,7 @@ class ArrayBuffer[A] private (initialElements: Array[AnyRef], initialSize: Int)
234236

235237
@deprecated("Use 'new GrowableBuilder(this).mapResult(f)' instead", "2.13.0")
236238
@deprecatedOverriding("ArrayBuffer[A] no longer extends Builder[A, ArrayBuffer[A]]", "2.13.0")
237-
@inline def mapResult[NewTo](f: (ArrayBuffer[A]) => NewTo): Builder[A, NewTo] = new GrowableBuilder[A, ArrayBuffer[A]](this).mapResult(f)
239+
@inline def mapResult[NewTo](f: (ArrayBuffer[A]) => NewTo): Builder[A, NewTo]^{f} = new GrowableBuilder[A, ArrayBuffer[A]](this).mapResult(f)
238240

239241
@nowarn("""cat=deprecation&origin=scala\.collection\.Iterable\.stringPrefix""")
240242
override protected[this] def stringPrefix = "ArrayBuffer"
@@ -291,7 +293,7 @@ object ArrayBuffer extends StrictOptimizedSeqFactory[ArrayBuffer] {
291293
final val DefaultInitialSize = 16
292294
private[this] val emptyArray = new Array[AnyRef](0)
293295

294-
def from[B](coll: collection.IterableOnce[B]): ArrayBuffer[B] = {
296+
def from[sealed B](coll: collection.IterableOnce[B]^): ArrayBuffer[B] = {
295297
val k = coll.knownSize
296298
if (k >= 0) {
297299
// Avoid reallocation of buffer if length is known
@@ -303,12 +305,12 @@ object ArrayBuffer extends StrictOptimizedSeqFactory[ArrayBuffer] {
303305
else new ArrayBuffer[B] ++= coll
304306
}
305307

306-
def newBuilder[A]: Builder[A, ArrayBuffer[A]] =
308+
def newBuilder[sealed A]: Builder[A, ArrayBuffer[A]] =
307309
new GrowableBuilder[A, ArrayBuffer[A]](empty) {
308310
override def sizeHint(size: Int): Unit = elems.ensureSize(size)
309311
}
310312

311-
def empty[A]: ArrayBuffer[A] = new ArrayBuffer[A]()
313+
def empty[sealed A]: ArrayBuffer[A] = new ArrayBuffer[A]()
312314

313315
/**
314316
* @param arrayLen the length of the backing array
@@ -357,22 +359,23 @@ object ArrayBuffer extends StrictOptimizedSeqFactory[ArrayBuffer] {
357359
}
358360

359361
// TODO: use `CheckedIndexedSeqView.Id` once we can change the return type of `ArrayBuffer#view`
360-
final class ArrayBufferView[A] private[mutable](underlying: ArrayBuffer[A], mutationCount: () => Int)
361-
extends AbstractIndexedSeqView[A] {
362+
final class ArrayBufferView[sealed A] private[mutable](underlying: ArrayBuffer[A], mutationCount: () -> Int)
363+
extends AbstractIndexedSeqView[A], Pure {
364+
/* Removed since it poses problems for capture checking
362365
@deprecated("never intended to be public; call ArrayBuffer#view instead", since = "2.13.7")
363366
def this(array: Array[AnyRef], length: Int) = {
364367
// this won't actually track mutation, but it would be a pain to have the implementation
365368
// check if we have a method to get the current mutation count or not on every method and
366369
// change what it does based on that. hopefully no one ever calls this.
367370
this({
368-
val _array = array
371+
val _array: Array[Object] = array
369372
val _length = length
370373
new ArrayBuffer[A](0) {
371374
this.array = _array
372375
this.size0 = _length
373-
}
376+
}: ArrayBuffer[A]
374377
}, () => 0)
375-
}
378+
}*/
376379

377380
@deprecated("never intended to be public", since = "2.13.7")
378381
def array: Array[AnyRef] = underlying.toArray[Any].asInstanceOf[Array[AnyRef]]
@@ -392,10 +395,10 @@ final class ArrayBufferView[A] private[mutable](underlying: ArrayBuffer[A], muta
392395
override def takeRight(n: Int): IndexedSeqView[A] = new CheckedIndexedSeqView.TakeRight(this, n)(mutationCount)
393396
override def drop(n: Int): IndexedSeqView[A] = new CheckedIndexedSeqView.Drop(this, n)(mutationCount)
394397
override def dropRight(n: Int): IndexedSeqView[A] = new CheckedIndexedSeqView.DropRight(this, n)(mutationCount)
395-
override def map[B](f: A => B): IndexedSeqView[B] = new CheckedIndexedSeqView.Map(this, f)(mutationCount)
398+
override def map[B](f: A => B): IndexedSeqView[B]^{f} = new CheckedIndexedSeqView.Map(this, f)(mutationCount)
396399
override def reverse: IndexedSeqView[A] = new CheckedIndexedSeqView.Reverse(this)(mutationCount)
397400
override def slice(from: Int, until: Int): IndexedSeqView[A] = new CheckedIndexedSeqView.Slice(this, from, until)(mutationCount)
398-
override def tapEach[U](f: A => U): IndexedSeqView[A] = new CheckedIndexedSeqView.Map(this, { (a: A) => f(a); a})(mutationCount)
401+
override def tapEach[U](f: A => U): IndexedSeqView[A]^{f} = new CheckedIndexedSeqView.Map(this, { (a: A) => f(a); a})(mutationCount)
399402

400403
override def concat[B >: A](suffix: IndexedSeqView.SomeIndexedSeqOps[B]): IndexedSeqView[B] = new CheckedIndexedSeqView.Concat(this, suffix)(mutationCount)
401404
override def appendedAll[B >: A](suffix: IndexedSeqView.SomeIndexedSeqOps[B]): IndexedSeqView[B] = new CheckedIndexedSeqView.Concat(this, suffix)(mutationCount)

tests/pos-special/stdlib/collection/mutable/Buffer.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import language.experimental.captureChecking
1818

1919

2020
/** A `Buffer` is a growable and shrinkable `Seq`. */
21-
trait Buffer[A]
21+
trait Buffer[sealed A]
2222
extends Seq[A]
2323
with SeqOps[A, Buffer, Buffer[A]]
2424
with Growable[A]
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/*
2+
* Scala (https://www.scala-lang.org)
3+
*
4+
* Copyright EPFL and Lightbend, Inc.
5+
*
6+
* Licensed under Apache License 2.0
7+
* (http://www.apache.org/licenses/LICENSE-2.0).
8+
*
9+
* See the NOTICE file distributed with this work for
10+
* additional information regarding copyright ownership.
11+
*/
12+
13+
package scala
14+
package collection.mutable
15+
import language.experimental.captureChecking
16+
17+
/** The canonical builder for collections that are growable, i.e. that support an
18+
* efficient `+=` method which adds an element to the collection.
19+
*
20+
* GrowableBuilders can produce only a single instance of the collection they are growing.
21+
*
22+
* @define Coll `GrowingBuilder`
23+
* @define coll growing builder
24+
*/
25+
class GrowableBuilder[Elem, To <: Growable[Elem]](protected val elems: To)
26+
extends Builder[Elem, To] {
27+
28+
def clear(): Unit = elems.clear()
29+
30+
def result(): To = elems
31+
32+
def addOne(elem: Elem): this.type = { elems += elem; this }
33+
34+
override def addAll(xs: IterableOnce[Elem]^): this.type = { elems.addAll(xs); this }
35+
36+
override def knownSize: Int = elems.knownSize
37+
}

0 commit comments

Comments
 (0)