Skip to content

Commit

Permalink
Capturecheck all files in generic
Browse files Browse the repository at this point in the history
  • Loading branch information
odersky committed Nov 3, 2023
1 parent 0f9a032 commit d97d2c4
Show file tree
Hide file tree
Showing 8 changed files with 610 additions and 13 deletions.
51 changes: 51 additions & 0 deletions tests/pos-special/stdlib/collection/generic/BitOperations.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/*
* Scala (https://www.scala-lang.org)
*
* Copyright EPFL and Lightbend, Inc.
*
* Licensed under Apache License 2.0
* (http://www.apache.org/licenses/LICENSE-2.0).
*
* See the NOTICE file distributed with this work for
* additional information regarding copyright ownership.
*/

package scala.collection
package generic
import language.experimental.captureChecking


/** Some bit operations.
*
* See [[https://www.drmaciver.com/2008/08/unsigned-comparison-in-javascala/]] for
* an explanation of unsignedCompare.
*/
private[collection] object BitOperations {
trait Int {
type Int = scala.Int
def zero(i: Int, mask: Int) = (i & mask) == 0
def mask(i: Int, mask: Int) = i & (complement(mask - 1) ^ mask)
def hasMatch(key: Int, prefix: Int, m: Int) = mask(key, m) == prefix
def unsignedCompare(i: Int, j: Int) = (i < j) ^ (i < 0) ^ (j < 0)
def shorter(m1: Int, m2: Int) = unsignedCompare(m2, m1)
def complement(i: Int) = (-1) ^ i
def bits(num: Int) = 31 to 0 by -1 map (i => (num >>> i & 1) != 0)
def bitString(num: Int, sep: String = "") = bits(num) map (b => if (b) "1" else "0") mkString sep
def highestOneBit(j: Int) = java.lang.Integer.highestOneBit(j)
}
object Int extends Int

trait Long {
type Long = scala.Long
def zero(i: Long, mask: Long) = (i & mask) == 0L
def mask(i: Long, mask: Long) = i & (complement(mask - 1) ^ mask)
def hasMatch(key: Long, prefix: Long, m: Long) = mask(key, m) == prefix
def unsignedCompare(i: Long, j: Long) = (i < j) ^ (i < 0L) ^ (j < 0L)
def shorter(m1: Long, m2: Long) = unsignedCompare(m2, m1)
def complement(i: Long) = (-1L) ^ i
def bits(num: Long) = 63L to 0L by -1L map (i => (num >>> i & 1L) != 0L)
def bitString(num: Long, sep: String = "") = bits(num) map (b => if (b) "1" else "0") mkString sep
def highestOneBit(j: Long) = java.lang.Long.highestOneBit(j)
}
object Long extends Long
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
/*
* Scala (https://www.scala-lang.org)
*
* Copyright EPFL and Lightbend, Inc.
*
* Licensed under Apache License 2.0
* (http://www.apache.org/licenses/LICENSE-2.0).
*
* See the NOTICE file distributed with this work for
* additional information regarding copyright ownership.
*/

package scala.collection.generic

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.
*
* This class is `final` and requires an extra `Factory` object rather than leaving the details of creating a `Builder`
* to an abstract method that could be implemented by a subclass. This is necessary because the factory is needed
* for deserializing this class's private state, which happens before any subclass fields would be deserialized. Any
* additional state required to create the proper `Builder` needs to be captured by the `factory`.
*/
@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

private[this] def writeObject(out: ObjectOutputStream): Unit = {
out.defaultWriteObject()
val k = coll.knownSize
out.writeInt(k)
var count = 0
coll.foreach { x =>
out.writeObject(x)
count += 1
}
if(k >= 0) {
if(count != k) throw new IllegalStateException(s"Illegal size $count of collection, expected $k")
} else out.writeObject(SerializeEnd)
}

private[this] def readObject(in: ObjectInputStream): Unit = {
in.defaultReadObject()
builder = factory.newBuilder
val k = in.readInt()
if(k >= 0) {
builder.sizeHint(k)
var count = 0
while(count < k) {
builder += in.readObject().asInstanceOf[A]
count += 1
}
} else {
while (true) in.readObject match {
case SerializeEnd => return
case a => builder += a.asInstanceOf[A]
}
}
}

protected[this] def readResolve(): Any = builder.result()
}

@SerialVersionUID(3L)
private[collection] case object SerializeEnd

/** Mix-in trait to enable DefaultSerializationProxy for the standard collection types. Depending on the type
* it is mixed into, it will dynamically choose `iterableFactory`, `mapFactory`, `sortedIterableFactory` or
* `sortedMapFactory` for deserialization into the respective `CC` type. Override `writeReplace` or implement
* it directly without using this trait if you need a non-standard factory or if you want to use a different
* serialization scheme.
*/
trait DefaultSerializable extends Serializable { this: scala.collection.Iterable[_] =>
protected[this] def writeReplace(): AnyRef = {
val f: Factory[Any, Any] = this match {
case it: scala.collection.SortedMap[_, _] => it.sortedMapFactory.sortedMapFactory[Any, Any](it.ordering.asInstanceOf[Ordering[Any]]).asInstanceOf[Factory[Any, Any]]
case it: scala.collection.Map[_, _] => it.mapFactory.mapFactory[Any, Any].asInstanceOf[Factory[Any, Any]]
case it: scala.collection.SortedSet[_] => it.sortedIterableFactory.evidenceIterableFactory[Any](it.ordering.asInstanceOf[Ordering[Any]])
case it => it.iterableFactory.iterableFactory
}
new DefaultSerializationProxy(f, this)
}
}
165 changes: 165 additions & 0 deletions tests/pos-special/stdlib/collection/generic/IsIterable.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
/*
* Scala (https://www.scala-lang.org)
*
* Copyright EPFL and Lightbend, Inc.
*
* Licensed under Apache License 2.0
* (http://www.apache.org/licenses/LICENSE-2.0).
*
* See the NOTICE file distributed with this work for
* additional information regarding copyright ownership.
*/

package scala.collection
package generic
import language.experimental.captureChecking

/** A trait which can be used to avoid code duplication when defining extension
* methods that should be applicable both to existing Scala collections (i.e.,
* types extending `Iterable`) as well as other (potentially user-defined)
* types that could be converted to a Scala collection type. This trait
* makes it possible to treat Scala collections and types that can be implicitly
* converted to a collection type uniformly. For example, one can provide
* extension methods that work both on collection types and on `String`s (`String`s
* do not extend `Iterable`, but can be converted to `Iterable`)
*
* `IsIterable` provides three members:
*
* 1. type member `A`, which represents the element type of the target `Iterable[A]`
* 1. type member `C`, which represents the type returned by transformation operations that preserve the collection’s elements type
* 1. method `apply`, which provides a way to convert between the type we wish to add extension methods to, `Repr`, and `IterableOps[A, Iterable, C]`.
*
* ===Usage===
*
* One must provide `IsIterable` as an implicit parameter type of an implicit
* conversion. Its usage is shown below. Our objective in the following example
* is to provide a generic extension method `mapReduce` to any type that extends
* or can be converted to `Iterable`. In our example, this includes
* `String`.
*
* {{{
* import scala.collection.{Iterable, IterableOps}
* import scala.collection.generic.IsIterable
*
* class ExtensionMethods[Repr, I <: IsIterable[Repr]](coll: Repr, it: I) {
* def mapReduce[B](mapper: it.A => B)(reducer: (B, B) => B): B = {
* val iter = it(coll).iterator
* var res = mapper(iter.next())
* while (iter.hasNext)
* res = reducer(res, mapper(iter.next()))
* res
* }
* }
*
* implicit def withExtensions[Repr](coll: Repr)(implicit it: IsIterable[Repr]): ExtensionMethods[Repr, it.type] =
* new ExtensionMethods(coll, it)
*
* // See it in action!
* List(1, 2, 3).mapReduce(_ * 2)(_ + _) // res0: Int = 12
* "Yeah, well, you know, that's just, like, your opinion, man.".mapReduce(x => 1)(_ + _) // res1: Int = 59
*}}}
*
* Here, we begin by creating a class `ExtensionMethods` which contains our
* `mapReduce` extension method.
*
* Note that `ExtensionMethods` takes a constructor argument `coll` of type `Repr`, where
* `Repr` represents (typically) the collection type, and an argument `it` of a subtype of `IsIterable[Repr]`.
* The body of the method starts by converting the `coll` argument to an `IterableOps` in order to
* call the `iterator` method on it.
* The remaining of the implementation is straightforward.
*
* The `withExtensions` implicit conversion makes the `mapReduce` operation available
* on any type `Repr` for which it exists an implicit `IsIterable[Repr]` instance.
* Note how we keep track of the precise type of the implicit `it` argument by using the
* `it.type` singleton type, rather than the wider `IsIterable[Repr]` type. We do that
* so that the information carried by the type members `A` and `C` of the `it` argument
* is not lost.
*
* When the `mapReduce` method is called on some type of which it is not
* a member, implicit search is triggered. Because implicit conversion
* `withExtensions` is generic, it will be applied as long as an implicit
* value of type `IsIterable[Repr]` can be found. Given that the
* `IsIterable` companion object contains implicit members that return values of type
* `IsIterable`, this requirement is typically satisfied, and the chain
* of interactions described in the previous paragraph is set into action.
* (See the `IsIterable` companion object, which contains a precise
* specification of the available implicits.)
*
* ''Note'': Currently, it's not possible to combine the implicit conversion and
* the class with the extension methods into an implicit class due to
* limitations of type inference.
*
* ===Implementing `IsIterable` for New Types===
*
* One must simply provide an implicit value of type `IsIterable`
* specific to the new type, or an implicit conversion which returns an
* instance of `IsIterable` specific to the new type.
*
* Below is an example of an implementation of the `IsIterable` trait
* where the `Repr` type is `Range`.
*
*{{{
* implicit val rangeRepr: IsIterable[Range] { type A = Int; type C = IndexedSeq[Int] } =
* new IsIterable[Range] {
* type A = Int
* type C = IndexedSeq[Int]
* def apply(coll: Range): IterableOps[Int, IndexedSeq, IndexedSeq[Int]] = coll
* }
*}}}
*
* (Note that in practice the `IsIterable[Range]` instance is already provided by
* the standard library, and it is defined as an `IsSeq[Range]` instance)
*/
trait IsIterable[Repr] extends IsIterableOnce[Repr] {

/** The type returned by transformation operations that preserve the same elements
* type (e.g. `filter`, `take`).
*
* In practice, this type is often `Repr` itself, excepted in the case
* of `SeqView[A]` (and other `View[A]` subclasses), where it is “only” `View[A]`.
*/
type C

@deprecated("'conversion' is now a method named 'apply'", "2.13.0")
override val conversion: Repr => IterableOps[A, Iterable, C] = apply(_)

/** A conversion from the type `Repr` to `IterableOps[A, Iterable, C]` */
def apply(coll: Repr): IterableOps[A, Iterable, C]

}

object IsIterable extends IsIterableLowPriority {

// Straightforward case: IterableOps subclasses
implicit def iterableOpsIsIterable[A0, CC0[X] <: IterableOps[X, Iterable, CC0[X]]]: IsIterable[CC0[A0]] { type A = A0; type C = CC0[A0] } =
new IsIterable[CC0[A0]] {
type A = A0
type C = CC0[A0]
def apply(coll: CC0[A]): IterableOps[A, Iterable, C] = coll
}

// The `BitSet` type can not be unified with the `CC0` parameter of
// the above definition because it does not take a type parameter.
// Hence the need for a separate case:
implicit def bitSetOpsIsIterable[C0 <: BitSet with BitSetOps[C0]]: IsIterable[C0] { type A = Int; type C = C0 } =
new IsIterable[C0] {
type A = Int
type C = C0
def apply(coll: C0): IterableOps[Int, Iterable, C0] = coll
}

}

trait IsIterableLowPriority {

// Makes `IsSeq` instances visible in `IsIterable` companion
implicit def isSeqLikeIsIterable[Repr](implicit
isSeqLike: IsSeq[Repr]
): IsIterable[Repr] { type A = isSeqLike.A; type C = isSeqLike.C } = isSeqLike

// Makes `IsMap` instances visible in `IsIterable` companion
implicit def isMapLikeIsIterable[Repr](implicit
isMapLike: IsMap[Repr]
): IsIterable[Repr] { type A = isMapLike.A; type C = isMapLike.C } = isMapLike

}
72 changes: 72 additions & 0 deletions tests/pos-special/stdlib/collection/generic/IsIterableOnce.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/*
* Scala (https://www.scala-lang.org)
*
* Copyright EPFL and Lightbend, Inc.
*
* Licensed under Apache License 2.0
* (http://www.apache.org/licenses/LICENSE-2.0).
*
* See the NOTICE file distributed with this work for
* additional information regarding copyright ownership.
*/

package scala
package collection
package generic
import language.experimental.captureChecking

/** Type class witnessing that a collection representation type `Repr` has
* elements of type `A` and has a conversion to `IterableOnce[A]`.
*
* This type enables simple enrichment of `IterableOnce`s with extension
* methods which can make full use of the mechanics of the Scala collections
* framework in their implementation.
*
* Example usage,
* {{{
* class FilterMapImpl[Repr, I <: IsIterableOnce[Repr]](coll: Repr, it: I) {
* final def filterMap[B, That](f: it.A => Option[B])(implicit bf: BuildFrom[Repr, B, That]): That = {
* val b = bf.newBuilder(coll)
* for(e <- it(coll).iterator) f(e) foreach (b +=)
* b.result()
* }
* }
* implicit def filterMap[Repr](coll: Repr)(implicit it: IsIterableOnce[Repr]): FilterMapImpl[Repr, it.type] =
* new FilterMapImpl(coll, it)
*
* List(1, 2, 3, 4, 5) filterMap (i => if(i % 2 == 0) Some(i) else None)
* // == List(2, 4)
* }}}
*/
trait IsIterableOnce[Repr] {

/** The type of elements we can traverse over (e.g. `Int`). */
type A

@deprecated("'conversion' is now a method named 'apply'", "2.13.0")
val conversion: Repr => IterableOnce[A] = apply(_)

/** A conversion from the representation type `Repr` to a `IterableOnce[A]`. */
def apply(coll: Repr): IterableOnce[A]

}

object IsIterableOnce extends IsIterableOnceLowPriority {

// Straightforward case: IterableOnce subclasses
implicit def iterableOnceIsIterableOnce[CC0[A] <: IterableOnce[A], A0]: IsIterableOnce[CC0[A0]] { type A = A0 } =
new IsIterableOnce[CC0[A0]] {
type A = A0
def apply(coll: CC0[A0]): IterableOnce[A0] = coll
}

}

trait IsIterableOnceLowPriority {

// Makes `IsIterable` instance visible in `IsIterableOnce` companion
implicit def isIterableLikeIsIterableOnce[Repr](implicit
isIterableLike: IsIterable[Repr]
): IsIterableOnce[Repr] { type A = isIterableLike.A } = isIterableLike

}
Loading

0 comments on commit d97d2c4

Please sign in to comment.