Skip to content

Commit

Permalink
Drop all but one usages of @uncheckedCaptures from stdlib
Browse files Browse the repository at this point in the history
  • Loading branch information
odersky committed Nov 12, 2023
1 parent e54c2ac commit 667b757
Show file tree
Hide file tree
Showing 32 changed files with 100 additions and 131 deletions.
6 changes: 3 additions & 3 deletions tests/pos-special/stdlib/collection/IterableOnce.scala
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ package scala
package collection

import scala.annotation.tailrec
import scala.annotation.unchecked.{uncheckedVariance, uncheckedCaptures}
import scala.annotation.unchecked.uncheckedVariance
import scala.collection.mutable.StringBuilder
import scala.language.implicitConversions
import scala.math.{Numeric, Ordering}
Expand Down Expand Up @@ -1341,8 +1341,8 @@ object IterableOnceOps:
// Moved out of trait IterableOnceOps to here, since universal traits cannot
// have nested classes in Scala 3
private class Maximized[X, B](descriptor: String)(f: X -> B)(cmp: (B, B) -> Boolean) extends AbstractFunction2[Maximized[X, B], X, Maximized[X, B]] {
var maxElem: X @uncheckedCaptures = null.asInstanceOf[X]
var maxF: B @uncheckedCaptures = null.asInstanceOf[B]
var maxElem: X = null.asInstanceOf[X]
var maxF: B = null.asInstanceOf[B]
var nonEmpty = false
def toOption: Option[X] = if (nonEmpty) Some(maxElem) else None
def result: X = if (nonEmpty) maxElem else throw new UnsupportedOperationException(s"empty.$descriptor")
Expand Down
34 changes: 16 additions & 18 deletions tests/pos-special/stdlib/collection/Iterator.scala
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,9 @@ package scala.collection

import scala.collection.mutable.{ArrayBuffer, ArrayBuilder, Builder, ImmutableBuilder}
import scala.annotation.tailrec
import scala.annotation.unchecked.{uncheckedVariance, uncheckedCaptures}
import scala.annotation.unchecked.uncheckedVariance
import scala.runtime.Statics
import language.experimental.captureChecking
import annotation.unchecked.uncheckedCaptures


/** Iterators are data structures that allow to iterate over a sequence
Expand Down Expand Up @@ -161,12 +160,12 @@ trait Iterator[+A] extends IterableOnce[A] with IterableOnceOps[A, Iterator, Ite

require(size >= 1 && step >= 1, f"size=$size%d and step=$step%d, but both must be positive")

private[this] var buffer: Array[B @uncheckedCaptures] = null // current result
private[this] var prev: Array[B @uncheckedCaptures] = null // if sliding, overlap from previous result
private[this] var buffer: Array[B] = null // current result
private[this] var prev: Array[B] = null // if sliding, overlap from previous result
private[this] var first = true // if !first, advancing may skip ahead
private[this] var filled = false // whether the buffer is "hot"
private[this] var partial = true // whether to emit partial sequence
private[this] var padding: () -> B @uncheckedCaptures = null // what to pad short sequences with
private[this] var padding: () -> B = null // what to pad short sequences with
private[this] def pad = padding != null // irrespective of partial flag
private[this] def newBuilder = {
val b = ArrayBuilder.make[Any]
Expand Down Expand Up @@ -258,7 +257,7 @@ trait Iterator[+A] extends IterableOnce[A] with IterableOnceOps[A, Iterator, Ite
}
// segment must have data, and must be complete unless they allow partial
val ok = index > 0 && (partial || index == size)
if (ok) buffer = builder.result().asInstanceOf[Array[B @uncheckedCaptures]]
if (ok) buffer = builder.result().asInstanceOf[Array[B]]
else prev = null
ok
}
Expand Down Expand Up @@ -417,8 +416,7 @@ trait Iterator[+A] extends IterableOnce[A] with IterableOnceOps[A, Iterator, Ite

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

def indexWhere(p: A => Boolean, from: Int = 0): Int = {
var i = math.max(from, 0)
Expand Down Expand Up @@ -561,7 +559,7 @@ trait Iterator[+A] extends IterableOnce[A] with IterableOnceOps[A, Iterator, Ite
*/
def distinctBy[B](f: A -> B): Iterator[A]^{this} = new AbstractIterator[A] {

private[this] val traversedValues = mutable.HashSet.empty[B @uncheckedCaptures]
private[this] val traversedValues = mutable.HashSet.empty[B]
private[this] var nextElementDefined: Boolean = false
private[this] var nextElement: A = _

Expand Down Expand Up @@ -704,7 +702,7 @@ trait Iterator[+A] extends IterableOnce[A] with IterableOnceOps[A, Iterator, Ite
*/
private[this] var status = 0
private def store(a: A): Unit = {
if (lookahead == null) lookahead = new mutable.Queue[A @uncheckedCaptures]
if (lookahead == null) lookahead = new mutable.Queue[A]
lookahead += a
}
def hasNext = {
Expand Down Expand Up @@ -867,8 +865,8 @@ trait Iterator[+A] extends IterableOnce[A] with IterableOnceOps[A, Iterator, Ite
* @note Reuse: $consumesOneAndProducesTwoIterators
*/
def duplicate: (Iterator[A]^{this}, Iterator[A]^{this}) = {
val gap = new scala.collection.mutable.Queue[A @uncheckedCaptures]
var ahead: Iterator[A @uncheckedCaptures] = null // ahead is captured by Partner, so A is not recognized as parametric
val gap = new scala.collection.mutable.Queue[A]
var ahead: Iterator[A] = null // ahead is captured by Partner, so A is not recognized as parametric
class Partner extends AbstractIterator[A] {
override def knownSize: Int = self.synchronized {
val thisSize = self.knownSize
Expand Down Expand Up @@ -1145,9 +1143,9 @@ object Iterator extends IterableFactory[Iterator] {
* Nested ConcatIterators are merged to avoid blowing the stack.
*/
private final class ConcatIterator[+A](val from: Iterator[A]^) extends AbstractIterator[A] {
private var current: Iterator[A @uncheckedCaptures]^{from*} = from
private var tail: ConcatIteratorCell[A @uncheckedVariance @uncheckedCaptures] = null
private var last: ConcatIteratorCell[A @uncheckedVariance @uncheckedCaptures] = null
private var current: Iterator[A]^{from*} = from
private var tail: ConcatIteratorCell[A @uncheckedVariance] = null
private var last: ConcatIteratorCell[A @uncheckedVariance] = null
private var currentHasNextChecked = false

def hasNext =
Expand Down Expand Up @@ -1216,7 +1214,7 @@ object Iterator extends IterableFactory[Iterator] {
}
}

private[this] final class ConcatIteratorCell[A](head: => IterableOnce[A]^, var tail: ConcatIteratorCell[A @uncheckedCaptures]) {
private[this] final class ConcatIteratorCell[A](head: => IterableOnce[A]^, var tail: ConcatIteratorCell[A]) {
def headIterator: Iterator[A]^{this} = head.iterator // CC todo: can't use {head} as capture set, gives "cannot establish a reference"
}

Expand Down Expand Up @@ -1277,8 +1275,8 @@ object Iterator extends IterableFactory[Iterator] {
* type `A` and update an internal state of type `S`.
*/
private final class UnfoldIterator[A, S](init: S)(f: S => Option[(A, S)])extends AbstractIterator[A] {
private[this] var state: S @uncheckedCaptures = init
private[this] var nextResult: Option[(A, S)] @uncheckedCaptures = null
private[this] var state: S = init
private[this] var nextResult: Option[(A, S)] = null

override def hasNext: Boolean = {
if (nextResult eq null) {
Expand Down
8 changes: 3 additions & 5 deletions tests/pos-special/stdlib/collection/Seq.scala
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ import Searching.{Found, InsertionPoint, SearchResult}
import scala.annotation.nowarn
import language.experimental.captureChecking
import caps.unsafe.unsafeAssumePure
import scala.annotation.unchecked.uncheckedCaptures

/** Base trait for sequence collections
*
Expand Down Expand Up @@ -601,8 +600,7 @@ trait SeqOps[+A, +CC[_], +C] extends Any with SeqViewOps[A, CC, C] { self =>
if (!hasNext)
Iterator.empty.next()

val forcedElms = new mutable.ArrayBuffer[A @uncheckedCaptures](elms.size) ++= elms
// uncheckedCaptures OK since used only locally
val forcedElms = new mutable.ArrayBuffer[A](elms.size) ++= elms
val result = (newSpecificBuilder ++= forcedElms).result()
var i = idxs.length - 2
while(i >= 0 && idxs(i) >= idxs(i+1))
Expand Down Expand Up @@ -893,7 +891,7 @@ trait SeqOps[+A, +CC[_], +C] extends Any with SeqViewOps[A, CC, C] { self =>
* part of the result, but any following occurrences will.
*/
def diff[B >: A](that: Seq[B]): C = {
val occ = occCounts[B @uncheckedCaptures](that)
val occ = occCounts[B](that)
fromSpecific(iterator.filter { x =>
var include = false
occ.updateWith(x) {
Expand All @@ -918,7 +916,7 @@ trait SeqOps[+A, +CC[_], +C] extends Any with SeqViewOps[A, CC, C] { self =>
* in the result, but any following occurrences will be omitted.
*/
def intersect[B >: A](that: Seq[B]): C = {
val occ = occCounts[B @uncheckedCaptures](that)
val occ = occCounts[B](that)
fromSpecific(iterator.filter { x =>
var include = true
occ.updateWith(x) {
Expand Down
2 changes: 1 addition & 1 deletion tests/pos-special/stdlib/collection/SeqView.scala
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ object SeqView {
// contains items of another type, we'd get a CCE anyway)
// - the cast doesn't actually do anything in the runtime because the
// type of A is not known and Array[_] is Array[AnyRef]
immutable.ArraySeq.unsafeWrapArray(arr.asInstanceOf[Array[A @uncheckedCaptures]])
immutable.ArraySeq.unsafeWrapArray(arr.asInstanceOf[Array[A]])
}
}
evaluated = true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@

package scala.collection
import language.experimental.captureChecking
import scala.annotation.unchecked.uncheckedCaptures

/**
* Trait that overrides operations on sequences in order
Expand All @@ -25,7 +24,7 @@ trait StrictOptimizedSeqOps [+A, +CC[_], +C]

override def distinctBy[B](f: A -> B): C = {
val builder = newSpecificBuilder
val seen = mutable.HashSet.empty[B @uncheckedCaptures]
val seen = mutable.HashSet.empty[B]
val it = this.iterator
while (it.hasNext) {
val next = it.next()
Expand Down Expand Up @@ -80,7 +79,7 @@ trait StrictOptimizedSeqOps [+A, +CC[_], +C]
override def diff[B >: A](that: Seq[B]): C =
if (isEmpty || that.isEmpty) coll
else {
val occ = occCounts[B @uncheckedCaptures](that)
val occ = occCounts[B](that)
val b = newSpecificBuilder
for (x <- this) {
occ.updateWith(x) {
Expand All @@ -98,7 +97,7 @@ trait StrictOptimizedSeqOps [+A, +CC[_], +C]
override def intersect[B >: A](that: Seq[B]): C =
if (isEmpty || that.isEmpty) empty
else {
val occ = occCounts[B @uncheckedCaptures](that)
val occ = occCounts[B](that)
val b = newSpecificBuilder
for (x <- this) {
occ.updateWith(x) {
Expand Down
3 changes: 1 addition & 2 deletions tests/pos-special/stdlib/collection/View.scala
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ package scala.collection
import scala.annotation.{nowarn, tailrec}
import scala.collection.mutable.{ArrayBuffer, Builder}
import scala.collection.immutable.LazyList
import scala.annotation.unchecked.uncheckedCaptures
import language.experimental.captureChecking

/** Views are collections whose transformation operations are non strict: the resulting elements
Expand Down Expand Up @@ -449,7 +448,7 @@ object View extends IterableFactory[View] {
}

private final class TakeRightIterator[A](underlying: Iterator[A]^, maxlen: Int) extends AbstractIterator[A] {
private[this] var current: Iterator[A @uncheckedCaptures]^{underlying} = underlying
private[this] var current: Iterator[A]^{underlying} = underlying
private[this] var len: Int = -1
private[this] var pos: Int = 0
private[this] var buf: ArrayBuffer[AnyRef] = _
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ import java.io.{ObjectInputStream, ObjectOutputStream}
import scala.collection.{Factory, Iterable}
import scala.collection.mutable.Builder
import language.experimental.captureChecking
import scala.annotation.unchecked.uncheckedCaptures

/** The default serialization proxy for collection implementations.
*
Expand All @@ -29,8 +28,7 @@ import scala.annotation.unchecked.uncheckedCaptures
@SerialVersionUID(3L)
final class DefaultSerializationProxy[A](factory: Factory[A, Any], @transient private[this] val coll: Iterable[A]) extends Serializable {

@transient protected var builder: Builder[A @uncheckedCaptures, Any] = _
// @uncheckedCaptures OK since builder is used only locally when reading objects
@transient protected var builder: Builder[A, Any] = _

private[this] def writeObject(out: ObjectOutputStream): Unit = {
out.defaultWriteObject()
Expand Down
3 changes: 1 addition & 2 deletions tests/pos-special/stdlib/collection/generic/IsSeq.scala
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ package generic
import scala.reflect.ClassTag
import language.experimental.captureChecking
import language.experimental.captureChecking
import scala.annotation.unchecked.uncheckedCaptures

/** Type class witnessing that a collection representation type `Repr` has
* elements of type `A` and has a conversion to `SeqOps[A, Iterable, C]`, for
Expand Down Expand Up @@ -100,7 +99,7 @@ object IsSeq {
new SeqOps[A, mutable.ArraySeq, Array[A]] {
def apply(i: Int): A = a(i)
def length: Int = a.length
def toIterable: Iterable[A] = mutable.ArraySeq.make[A @uncheckedCaptures](a)
def toIterable: Iterable[A] = mutable.ArraySeq.make[A](a)
protected def coll: Array[A] = a
protected def fromSpecific(coll: IterableOnce[A]^): Array[A] = Array.from(coll)
def iterableFactory: FreeSeqFactory[mutable.ArraySeq] = mutable.ArraySeq.untagged
Expand Down
19 changes: 9 additions & 10 deletions tests/pos-special/stdlib/collection/immutable/ArraySeq.scala
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ import scala.runtime.ScalaRunTime
import scala.util.Sorting
import scala.util.hashing.MurmurHash3
import language.experimental.captureChecking
import scala.annotation.unchecked.uncheckedCaptures

/**
* An immutable array.
Expand Down Expand Up @@ -59,7 +58,7 @@ sealed abstract class ArraySeq[+A]
def unsafeArrayAsAnyArray = unsafeArray.asInstanceOf[Array[Any]]

protected def evidenceIterableFactory: ArraySeq.type = ArraySeq
protected def iterableEvidence: ClassTag[A @uncheckedVariance @uncheckedCaptures] = elemTag.asInstanceOf[ClassTag[A]]
protected def iterableEvidence: ClassTag[A @uncheckedVariance] = elemTag.asInstanceOf[ClassTag[A]]

def stepper[S <: Stepper[_]](implicit shape: StepperShape[A, S]): S with EfficientSplit

Expand Down Expand Up @@ -109,17 +108,17 @@ sealed abstract class ArraySeq[+A]
null
else if (thisIsObj) {
// A and B are objects
val ax = this.unsafeArray.asInstanceOf[Array[A @uncheckedCaptures]]
val ay = that.unsafeArray.asInstanceOf[Array[B @uncheckedCaptures]]
val ax = this.unsafeArray.asInstanceOf[Array[A]]
val ay = that.unsafeArray.asInstanceOf[Array[B]]
val len = ax.length + ay.length
val a = new Array[AnyRef](len)
System.arraycopy(ax, 0, a, 0, ax.length)
System.arraycopy(ay, 0, a, ax.length, ay.length)
ArraySeq.unsafeWrapArray(a).asInstanceOf[ArraySeq[B]]
} else {
// A is a primative and B = A. Use this instance's protected ClassTag.
val ax = this.unsafeArray.asInstanceOf[Array[A @uncheckedCaptures]]
val ay = that.unsafeArray.asInstanceOf[Array[A @uncheckedCaptures]]
val ax = this.unsafeArray.asInstanceOf[Array[A]]
val ay = that.unsafeArray.asInstanceOf[Array[A]]
val len = ax.length + ay.length
val a = iterableEvidence.newArray(len)
System.arraycopy(ax, 0, a, 0, ax.length)
Expand Down Expand Up @@ -186,7 +185,7 @@ sealed abstract class ArraySeq[+A]
strictOptimizedZip[B, ArraySeq[(A, B)]](that, iterableFactory.newBuilder)
}

private inline def ops[A](xs: Array[A @uncheckedCaptures]): ArrayOps[A] = new ArrayOps[A @uncheckedCaptures](xs)
private inline def ops[A](xs: Array[A]): ArrayOps[A] = new ArrayOps[A](xs)

override def take(n: Int): ArraySeq[A] =
if (unsafeArray.length <= n)
Expand Down Expand Up @@ -290,12 +289,12 @@ object ArraySeq extends StrictOptimizedClassTagSeqFactory[ArraySeq] { self =>
}

def newBuilder[A : ClassTag]: Builder[A, ArraySeq[A]] =
ArrayBuffer.newBuilder[A @uncheckedCaptures].mapResult(b => unsafeWrapArray[A](b.toArray))
ArrayBuffer.newBuilder[A].mapResult(b => unsafeWrapArray[A](b.toArray))

override def fill[A : ClassTag](n: Int)(elem: => A): ArraySeq[A] = tabulate(n)(_ => elem)

override def tabulate[A : ClassTag](n: Int)(f: Int => A): ArraySeq[A] = {
val elements = Array.ofDim[A @uncheckedCaptures](scala.math.max(n, 0))
val elements = Array.ofDim[A](scala.math.max(n, 0))
var i = 0
while (i < n) {
ScalaRunTime.array_update(elements, i, f(i))
Expand All @@ -316,7 +315,7 @@ object ArraySeq extends StrictOptimizedClassTagSeqFactory[ArraySeq] { self =>
* `ArraySeq.unsafeWrapArray(a.asInstanceOf[Array[Int]])` does not work, it throws a
* `ClassCastException` at runtime.
*/
def unsafeWrapArray[T](x: Array[T @uncheckedCaptures]): ArraySeq[T] = ((x: @unchecked) match {
def unsafeWrapArray[T](x: Array[T]): ArraySeq[T] = ((x: @unchecked) match {
case null => null
case x: Array[AnyRef] => new ofRef[AnyRef](x)
case x: Array[Int] => new ofInt(x)
Expand Down
11 changes: 5 additions & 6 deletions tests/pos-special/stdlib/collection/immutable/HashMap.scala
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ import scala.runtime.AbstractFunction2
import scala.runtime.Statics.releaseFence
import scala.util.hashing.MurmurHash3
import language.experimental.captureChecking
import scala.annotation.unchecked.uncheckedCaptures

/** This class implements immutable maps using a Compressed Hash-Array Mapped Prefix-tree.
* See paper https://michael.steindorfer.name/publications/oopsla15.pdf for more details.
Expand Down Expand Up @@ -1768,7 +1767,7 @@ private final class BitmapIndexedMapNode[K, +V](
} else {
mapOfNewNodes |= bitpos
if (newNodes eq null) {
newNodes = mutable.Queue.empty[MapNode[K, V] @uncheckedCaptures]
newNodes = mutable.Queue.empty[MapNode[K, V]]
}
newNodes += newSubNode
}
Expand Down Expand Up @@ -1853,7 +1852,7 @@ private final class BitmapIndexedMapNode[K, +V](
private final class HashCollisionMapNode[K, +V ](
val originalHash: Int,
val hash: Int,
var content: Vector[(K, V @uV) @uncheckedCaptures]
var content: Vector[(K, V @uV)]
) extends MapNode[K, V] {

import Node._
Expand Down Expand Up @@ -2157,7 +2156,7 @@ private final class MapKeyValueTupleReverseIterator[K, V](rootNode: MapNode[K, V
private final class MapKeyValueTupleHashIterator[K, V](rootNode: MapNode[K, V])
extends ChampBaseReverseIterator[MapNode[K, V]](rootNode) with Iterator[Any] {
private[this] var hash = 0
private[this] var value: V @uncheckedCaptures = _
private[this] var value: V = _
override def hashCode(): Int = MurmurHash3.tuple2Hash(hash, value.##, MurmurHash3.productSeed)
def next() = {
if (!hasNext)
Expand Down Expand Up @@ -2229,12 +2228,12 @@ private[immutable] final class HashMapBuilder[K, V] extends ReusableBuilder[(K,
/** The last given out HashMap as a return value of `result()`, if any, otherwise null.
* Indicates that on next add, the elements should be copied to an identical structure, before continuing
* mutations. */
private var aliased: HashMap[K, V] @uncheckedCaptures = _
private var aliased: HashMap[K, V] = _

private def isAliased: Boolean = aliased != null

/** The root node of the partially build hashmap */
private var rootNode: BitmapIndexedMapNode[K, V] @uncheckedCaptures = newEmptyRootNode
private var rootNode: BitmapIndexedMapNode[K, V] = newEmptyRootNode

private[immutable] def getOrElse[V0 >: V](key: K, value: V0): V0 =
if (rootNode.size == 0) value
Expand Down
Loading

0 comments on commit 667b757

Please sign in to comment.