Skip to content

Improvements to the Scala 3 crossbuild #240

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Oct 18, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 18 additions & 3 deletions build.sbt
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
ThisBuild / scalaVersion := "3.3.3"
ThisBuild / crossScalaVersions := Seq((ThisBuild / scalaVersion).value, "2.13.13")
ThisBuild / crossScalaVersions := Seq((ThisBuild / scalaVersion).value, "2.13.14")

lazy val root = project.in(file("."))
.aggregate(collectionContrib.jvm, collectionContrib.js, collectionContrib.native)
Expand All @@ -20,8 +20,23 @@ lazy val collectionContrib = crossProject(JVMPlatform, JSPlatform, NativePlatfor
scalaModuleAutomaticModuleName := Some("scala.collection.contrib"),
Compile / compile / scalacOptions ++= {
CrossVersion.partialVersion(scalaVersion.value) match {
case Some((2, _)) => Seq("-opt-warnings", "-Werror", "-Wconf:origin=scala.collection.IterableOps.toIterable:s")
case _ => Seq("-Xfatal-warnings", "-Wconf:cat=deprecation:s")
case Some((2, _)) => Seq(
"-Wconf:origin=scala.collection.IterableOps.toIterable:s", // internal usage; annotating @nowarn is clutter
"-Werror",
"-Wnonunit-statement",
"-Wopt",
"-opt:inline:<sources>",
"-opt:inline:scala.util.package$chaining$,scala.util.ChainingSyntax,scala.util.ChainingOps$",
"-Wvalue-discard",
"-Xlint",
"-Xsource:3-cross",
)
case _ => Seq(
"-Wconf:cat=deprecation:s", // Scala 3 lacks origin, src arrives in 3.3.4 & 3.5
"-Wconf:id=E175:s",
"-Werror",
"-Wnonunit-statement", // warns on uni-limbed if
)
}
},
Compile / doc / scalacOptions ++= {
Expand Down
2 changes: 1 addition & 1 deletion project/build.properties
Original file line number Diff line number Diff line change
@@ -1 +1 @@
sbt.version=1.9.9
sbt.version=1.10.0
17 changes: 7 additions & 10 deletions src/main/scala/scala/collection/MultiDict.scala
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ trait MultiDict[K, V]
with MultiDictOps[K, V, MultiDict, MultiDict[K, V]]
with Equals {

override protected[this] def className: String = "MultiDict"
override protected def className: String = "MultiDict"

def multiDictFactory: MapFactory[MultiDict] = MultiDict
override protected def fromSpecific(coll: IterableOnce[(K, V)]): MultiDict[K, V] = multiDictFactory.from(coll)
Expand All @@ -26,14 +26,11 @@ trait MultiDict[K, V]
override def equals(o: Any): Boolean = o match {
case that: MultiDict[K @unchecked, _] =>
(this eq that) ||
(that canEqual this) &&
(this.size == that.size) && {
try {
sets forall { case (k, vs) => that.sets.get(k).contains(vs) }
} catch {
case _: ClassCastException => false
}
}
that.canEqual(this) &&
this.size == that.size && (
try sets.forall { case (k, vs) => that.sets.get(k).contains(vs) }
catch { case _: ClassCastException => false }
)
case _ => false
}

Expand Down Expand Up @@ -205,7 +202,7 @@ trait MultiDictOps[K, V, +CC[X, Y] <: MultiDict[X, Y], +C <: MultiDict[K, V]]
object MultiDictOps {

class WithFilter[K, V, +IterableCC[_], +CC[X, Y] <: MultiDict[X, Y]](
`this`: MultiDictOps[K, V, CC, _] with IterableOps[(K, V), IterableCC, _],
`this`: MultiDictOps[K, V, CC, ?] & IterableOps[(K, V), IterableCC, ?],
p: ((K, V)) => Boolean
) extends IterableOps.WithFilter[(K, V), IterableCC](`this`, p) {

Expand Down
19 changes: 8 additions & 11 deletions src/main/scala/scala/collection/MultiSet.scala
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ trait MultiSet[A]
with MultiSetOps[A, MultiSet, MultiSet[A]]
with Equals {

override protected[this] def className: String = "MultiSet"
override protected def className: String = "MultiSet"

override def iterableFactory: IterableFactory[MultiSet] = MultiSet
override protected def fromSpecific(coll: IterableOnce[A]): MultiSet[A] = iterableFactory.from(coll)
Expand All @@ -24,14 +24,11 @@ trait MultiSet[A]
override def equals(o: Any): Boolean = o match {
case that: MultiSet[A @unchecked] =>
(this eq that) ||
(that canEqual this) &&
(this.size == that.size) && {
try {
occurrences forall { case (elem, n) => that.get(elem) == n }
} catch {
case _: ClassCastException => false
}
}
that.canEqual(this) &&
this.size == that.size && (
try occurrences.forall { case (elem, n) => that.get(elem) == n }
catch { case _: ClassCastException => false }
)
case _ => false
}

Expand All @@ -42,10 +39,10 @@ trait MultiSet[A]
trait MultiSetOps[A, +CC[X] <: MultiSet[X], +C <: MultiSet[A]]
extends IterableOps[A, CC, C] {

protected[this] def fromSpecificOccurrences(it: Iterable[(A, Int)]): C =
protected def fromSpecificOccurrences(it: Iterable[(A, Int)]): C =
fromSpecific(it.view.flatMap { case (e, n) => new View.Fill(n)(e) })

protected[this] def fromOccurrences[E](it: Iterable[(E, Int)]): CC[E] =
protected def fromOccurrences[E](it: Iterable[(E, Int)]): CC[E] =
// Note new MultiSet(it.to(Map)) would be more efficient but would also loose duplicates
iterableFactory.from(it.view.flatMap { case (e, n) => new View.Fill(n)(e) })

Expand Down
6 changes: 3 additions & 3 deletions src/main/scala/scala/collection/SortedMultiDict.scala
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ trait SortedMultiDictOps[K, V, +CC[X, Y] <: MultiDict[X, Y], +C <: MultiDict[K,

def sortedMultiDictFactory: SortedMapFactory[CC]

protected[this] def sortedFromIterable[L : Ordering, W](it: Iterable[(L, W)]): CC[L, W] = sortedMultiDictFactory.from(it)
protected[this] def sortedFromSets[L : Ordering, W](it: Iterable[(L, Set[W])]): CC[L, W] =
protected def sortedFromIterable[L : Ordering, W](it: Iterable[(L, W)]): CC[L, W] = sortedMultiDictFactory.from(it)
protected def sortedFromSets[L : Ordering, W](it: Iterable[(L, Set[W])]): CC[L, W] =
sortedFromIterable(it.view.flatMap { case (l, ws) => ws.map(w => (l, w)) })

/** `this` sorted multidict upcasted to an unsorted multidict */
Expand Down Expand Up @@ -126,7 +126,7 @@ trait SortedMultiDictOps[K, V, +CC[X, Y] <: MultiDict[X, Y], +C <: MultiDict[K,
object SortedMultiDictOps {

class WithFilter[K, V, +IterableCC[_], +MultiDictCC[X, Y] <: MultiDict[X, Y], +CC[X, Y] <: MultiDict[X, Y]](
`this`: SortedMultiDictOps[K, V, CC, _] with MultiDictOps[K, V, MultiDictCC, _] with IterableOps[(K, V), IterableCC, _],
`this`: SortedMultiDictOps[K, V, CC, ?] & MultiDictOps[K, V, MultiDictCC, ?] & IterableOps[(K, V), IterableCC, ?],
p: ((K, V)) => Boolean
) extends MultiDictOps.WithFilter[K, V, IterableCC, MultiDictCC](`this`, p) {

Expand Down
6 changes: 3 additions & 3 deletions src/main/scala/scala/collection/SortedMultiSet.scala
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ trait SortedMultiSetOps[A, +CC[X] <: MultiSet[X], +C <: MultiSet[A]]
* is the minimum of the lengths of `this` and `that`
*/
def zip[B](that: Iterable[B])(implicit ev: Ordering[B]): CC[(A @uncheckedVariance, B)] = // sound bcs of VarianceNote
sortedFromIterable(new View.Zip(toIterable, that))(Ordering.Tuple2(ordering, implicitly))
sortedFromIterable(new View.Zip(toIterable, that))(using Ordering.Tuple2(ordering, implicitly))

/**
* @return a new collection resulting from applying the given partial
Expand Down Expand Up @@ -147,7 +147,7 @@ trait SortedMultiSetOps[A, +CC[X] <: MultiSet[X], +C <: MultiSet[A]]
// --- Override return type of methods that returned an unsorted MultiSet

override def zipWithIndex: CC[(A, Int)] =
sortedFromIterable(new View.ZipWithIndex(toIterable))(Ordering.Tuple2(ordering, implicitly))
sortedFromIterable(new View.ZipWithIndex(toIterable))(using Ordering.Tuple2(ordering, implicitly))

}

Expand All @@ -158,7 +158,7 @@ object SortedMultiSetOps {
* @define coll sorted collection
*/
class WithFilter[A, +IterableCC[_], +CC[X] <: MultiSet[X]](
`this`: SortedMultiSetOps[A, CC, _] with IterableOps[A, IterableCC, _],
`this`: SortedMultiSetOps[A, CC, ?] & IterableOps[A, IterableCC, ?],
p: A => Boolean
) extends IterableOps.WithFilter[A, IterableCC](`this`, p) {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package scala.collection.decorators

import scala.collection.{BitSet, BitSetOps}

class BitSetDecorator[+C <: BitSet with BitSetOps[C]](protected val bs: C) {
class BitSetDecorator[+C <: BitSet & BitSetOps[C]](protected val bs: C) {

import BitSetDecorator._
import BitSetOps._
Expand Down
35 changes: 15 additions & 20 deletions src/main/scala/scala/collection/decorators/IteratorDecorator.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package scala.collection
package decorators

import scala.annotation.tailrec
import scala.util.chaining.*
import scala.util.control.NonFatal

/** Enriches Iterator with additional methods.
Expand Down Expand Up @@ -188,8 +189,8 @@ class IteratorDecorator[A](val `this`: Iterator[A]) extends AnyVal {
*/
def splitBy[K](f: A => K): Iterator[immutable.Seq[A]] =
new AbstractIterator[immutable.Seq[A]] {
private var hd: A = _
private var hdKey: K = _
private var hd: A = null.asInstanceOf[A] // todo uninitialized
private var hdKey: K = null.asInstanceOf[K] // todo uninitialized
private var hdDefined: Boolean = false

override def hasNext: Boolean = hdDefined || `this`.hasNext
Expand Down Expand Up @@ -240,32 +241,26 @@ class IteratorDecorator[A](val `this`: Iterator[A]) extends AnyVal {
}

/** Gives elements from the source iterator until the source iterator ends or throws a NonFatal exception.
*
* @param exceptionCaught a callback invoked from `hasNext` when the source iterator throws a NonFatal exception
* @return an iterator that takes items until the wrapped iterator ends or throws a NonFatal exception
* @see scala.util.control.NonFatal
* @note Reuse: $consumesAndProducesIterator
*/
def takeUntilException(exceptionCaught: Throwable => Unit): Iterator[A] = {
*
* @param exceptionCaught a callback invoked from `hasNext` when the source iterator throws a NonFatal exception
* @return an iterator that takes items until the wrapped iterator ends or throws a NonFatal exception
* @see scala.util.control.NonFatal
* @note Reuse: $consumesAndProducesIterator
*/
def takeUntilException(exceptionCaught: Throwable => Unit): Iterator[A] =
new AbstractIterator[A] {
private val wrapped = `this`.buffered

override def hasNext: Boolean = {
try {
val n = wrapped.hasNext
// By already invoking `head` (and therefore also `next` on `this`),
// we are sure that `wrapped.next` will not throw when it is used from
// `next`.
if (n) wrapped.head
n
} catch {
override def hasNext: Boolean =
// By already invoking `head` (and therefore also `next` on `this`),
// we are sure that `wrapped.next` will not throw when it is used from `next`.
try wrapped.hasNext.tap(if(_) wrapped.head)
catch {
case NonFatal(t) =>
exceptionCaught(t)
false
}
}

override def next(): A = wrapped.next()
}
}
}
2 changes: 1 addition & 1 deletion src/main/scala/scala/collection/decorators/package.scala
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ package object decorators {
implicit def mapDecorator[C](coll: C)(implicit map: IsMap[C]): MapDecorator[C, map.type] =
new MapDecorator(coll)(map)

implicit def bitSetDecorator[C <: BitSet with BitSetOps[C]](bs: C): BitSetDecorator[C] =
implicit def bitSetDecorator[C <: BitSet & BitSetOps[C]](bs: C): BitSetDecorator[C] =
new BitSetDecorator(bs)

implicit def mutableBitSetDecorator(bs: mutable.BitSet): MutableBitSetDecorator =
Expand Down
4 changes: 2 additions & 2 deletions src/main/scala/scala/collection/decorators/views.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ package decorators
/** Views used by decorators */
object View {

type SomeIterableOps[+A] = IterableOps[A, AnyConstr, _]
type SomeIterableOps[+A] = IterableOps[A, AnyConstr, ?]

class Intersperse[A](underlying: SomeIterableOps[A], sep: A) extends View[A] {
def iterator: Iterator[A] = underlying.iterator.intersperse(sep)
Expand All @@ -21,4 +21,4 @@ object View {
else underlying.knownSize
}

}
}
27 changes: 12 additions & 15 deletions src/main/scala/scala/collection/mutable/MultiDict.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,10 @@ package scala
package collection
package mutable

/**
* A mutable multidict
* @tparam K the type of keys
* @tparam V the type of values
*/
/** A mutable multidict.
* @tparam K the type of keys
* @tparam V the type of values
*/
class MultiDict[K, V] private (elems: Map[K, Set[V]])
extends collection.MultiDict[K, V]
with Iterable[(K, V)]
Expand All @@ -27,20 +26,18 @@ class MultiDict[K, V] private (elems: Map[K, Set[V]])

def addOne(elem: (K, V)): this.type = {
val (k, v) = elem
elems.updateWith(k) {
case None => Some(Set(v))
case Some(vs) => Some(vs += v)
}
val vs = elems.getOrElseUpdate(k, Set.empty[V])
vs.addOne(v)
this
}

def subtractOne(elem: (K, V)): this.type = {
val (k, v) = elem
elems.updateWith(k) {
elems.get(k) match {
case Some(vs) =>
vs -= v
if (vs.nonEmpty) Some(vs) else None
case None => None
vs.subtractOne(v)
if (vs.isEmpty) elems.subtractOne(k)
case _ =>
}
this
}
Expand All @@ -50,7 +47,7 @@ class MultiDict[K, V] private (elems: Map[K, Set[V]])
* @return the collection itself
*/
def removeKey(key: K): this.type = {
elems -= key
elems.subtractOne(key)
this
}

Expand All @@ -69,4 +66,4 @@ object MultiDict extends MapFactory[MultiDict] {

def newBuilder[K, V]: Builder[(K, V), MultiDict[K, V]] = new GrowableBuilder[(K, V), MultiDict[K, V]](empty)

}
}
19 changes: 9 additions & 10 deletions src/main/scala/scala/collection/mutable/MultiSet.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ package scala
package collection
package mutable

import java.util.concurrent.atomic.AtomicInteger

/**
* A mutable multiset.
*/
Expand All @@ -19,22 +21,19 @@ trait MultiSet[A]
override def knownSize: Int = super[Growable].knownSize
}

class MultiSetImpl[A] private[mutable] (val elems: Map[A, Int]) extends MultiSet[A] {
class MultiSetImpl[A] private[mutable] (elems: Map[A, AtomicInteger]) extends MultiSet[A] {

def occurrences: collection.Map[A, Int] = elems
def occurrences: collection.Map[A, Int] = elems.map { case (k, v) => (k, v.get) }

def addOne(elem: A): this.type = {
elems.updateWith(elem) {
case None => Some(1)
case Some(n) => Some(n + 1)
}
elems.getOrElseUpdate(elem, new AtomicInteger).getAndIncrement
this
}

def subtractOne(elem: A): this.type = {
elems.updateWith(elem) {
case Some(n) => if (n > 1) Some(n - 1) else None
case None => None
elems.get(elem) match {
case Some(n) => if (n.decrementAndGet <= 0) elems.subtractOne(elem)
case _ =>
}
this
}
Expand All @@ -50,4 +49,4 @@ object MultiSet extends IterableFactory[MultiSet] {

def newBuilder[A]: Builder[A, MultiSet[A]] = new GrowableBuilder[A, MultiSet[A]](empty)

}
}
Loading