diff --git a/compiler/src/dotty/tools/MainGenericCompiler.scala b/compiler/src/dotty/tools/MainGenericCompiler.scala index 98bd9078c397..ad522a9f6810 100644 --- a/compiler/src/dotty/tools/MainGenericCompiler.scala +++ b/compiler/src/dotty/tools/MainGenericCompiler.scala @@ -131,9 +131,9 @@ object MainGenericCompiler { val tStopAtLvl="-XX:TieredStopAtLevel=1" println(s"ignoring deprecated -Oshort flag, please add `-J$addTC` and `-J$tStopAtLvl` flags manually") process(tail, settings) - case javaOption(stripped) :: tail => + case javaOption(stripped: String) :: tail => process(tail, settings.withJavaArgs(stripped)) - case javaPropOption(opt, value) :: tail => + case javaPropOption(opt: String, value: String) :: tail => process(tail, settings.withJavaProps(opt -> value)) case arg :: tail => process(tail, settings.withResidualArgs(arg)) diff --git a/compiler/src/dotty/tools/MainGenericRunner.scala b/compiler/src/dotty/tools/MainGenericRunner.scala index 9ff96f812bca..3f645ea803a4 100644 --- a/compiler/src/dotty/tools/MainGenericRunner.scala +++ b/compiler/src/dotty/tools/MainGenericRunner.scala @@ -143,7 +143,7 @@ object MainGenericRunner { processArgs(tail, settings.noSave) case "-with-compiler" :: tail => processArgs(tail, settings.withCompiler) - case (o @ javaOption(striped)) :: tail => + case (o @ javaOption(striped: String)) :: tail => processArgs(tail, settings.withJavaArgs(striped).withScalaArgs(o)) case (o @ scalaOption(_*)) :: tail => val remainingArgs = CommandLineParser.expandArg(o) ++ tail diff --git a/compiler/src/dotty/tools/backend/sjs/JSDefinitions.scala b/compiler/src/dotty/tools/backend/sjs/JSDefinitions.scala index e42502fe715b..ca8c93d9c4e0 100644 --- a/compiler/src/dotty/tools/backend/sjs/JSDefinitions.scala +++ b/compiler/src/dotty/tools/backend/sjs/JSDefinitions.scala @@ -289,15 +289,15 @@ final class JSDefinitions()(using Context) { @threadUnsafe lazy val EnumerationClass = requiredClass("scala.Enumeration") @threadUnsafe lazy val Enumeration_Value_NoArg = EnumerationClass.requiredValue(nmeValue) @threadUnsafe lazy val Enumeration_Value_IntArg = EnumerationClass.requiredMethod(nmeValue, List(defn.IntType)) - @threadUnsafe lazy val Enumeration_Value_StringArg = EnumerationClass.requiredMethod(nmeValue, List(defn.StringType)) - @threadUnsafe lazy val Enumeration_Value_IntStringArg = EnumerationClass.requiredMethod(nmeValue, List(defn.IntType, defn.StringType)) + @threadUnsafe lazy val Enumeration_Value_StringArg = EnumerationClass.requiredMethod(nmeValue, List(OrNull(defn.StringType))) + @threadUnsafe lazy val Enumeration_Value_IntStringArg = EnumerationClass.requiredMethod(nmeValue, List(defn.IntType, OrNull(defn.StringType))) @threadUnsafe lazy val Enumeration_nextName = EnumerationClass.requiredMethod(termName("nextName")) @threadUnsafe lazy val EnumerationValClass = EnumerationClass.requiredClass("Val") @threadUnsafe lazy val Enumeration_Val_NoArg = EnumerationValClass.requiredMethod(nme.CONSTRUCTOR, Nil) @threadUnsafe lazy val Enumeration_Val_IntArg = EnumerationValClass.requiredMethod(nme.CONSTRUCTOR, List(defn.IntType)) - @threadUnsafe lazy val Enumeration_Val_StringArg = EnumerationValClass.requiredMethod(nme.CONSTRUCTOR, List(defn.StringType)) - @threadUnsafe lazy val Enumeration_Val_IntStringArg = EnumerationValClass.requiredMethod(nme.CONSTRUCTOR, List(defn.IntType, defn.StringType)) + @threadUnsafe lazy val Enumeration_Val_StringArg = EnumerationValClass.requiredMethod(nme.CONSTRUCTOR, List(OrNull(defn.StringType))) + @threadUnsafe lazy val Enumeration_Val_IntStringArg = EnumerationValClass.requiredMethod(nme.CONSTRUCTOR, List(defn.IntType, OrNull(defn.StringType))) def isValueMethod(sym: Symbol)(using Context): Boolean = sym.name == nmeValue && sym.owner == EnumerationClass diff --git a/compiler/src/dotty/tools/dotc/core/Comments.scala b/compiler/src/dotty/tools/dotc/core/Comments.scala index cafe09673684..b742e3cf8f32 100644 --- a/compiler/src/dotty/tools/dotc/core/Comments.scala +++ b/compiler/src/dotty/tools/dotc/core/Comments.scala @@ -404,7 +404,7 @@ object Comments { val raw = ctx.docCtx.flatMap(_.docstring(sym).map(_.raw)).getOrElse("") defs(sym) ++= defines(raw).map { str => val start = skipWhitespace(str, "@define".length) - val (key, Trim(value)) = str.splitAt(skipVariable(str, start)): @unchecked + val (key, Trim(value: String)) = str.splitAt(skipVariable(str, start)): @unchecked variableName(key.drop(start)) -> value.replaceAll("\\s+\\*+$", "") } } diff --git a/compiler/src/dotty/tools/dotc/reporting/messages.scala b/compiler/src/dotty/tools/dotc/reporting/messages.scala index 9c4ee9926bf2..efbe9c405d26 100644 --- a/compiler/src/dotty/tools/dotc/reporting/messages.scala +++ b/compiler/src/dotty/tools/dotc/reporting/messages.scala @@ -3000,7 +3000,7 @@ class MissingImplicitArgument( val idx = paramNames.indexOf(name) if (idx >= 0) Some(i"${args(idx)}") else None """\$\{\s*([^}\s]+)\s*\}""".r.replaceAllIn(raw, (_: Regex.Match) match - case Regex.Groups(v) => quoteReplacement(translate(v).getOrElse("?" + v)).nn + case Regex.Groups(v: String) => quoteReplacement(translate(v).getOrElse("?" + v)).nn ) /** @param rawMsg Message template with variables, e.g. "Variable A is ${A}" diff --git a/compiler/src/dotty/tools/dotc/semanticdb/Scala3.scala b/compiler/src/dotty/tools/dotc/semanticdb/Scala3.scala index 3d81408e8482..6ef42b6f9775 100644 --- a/compiler/src/dotty/tools/dotc/semanticdb/Scala3.scala +++ b/compiler/src/dotty/tools/dotc/semanticdb/Scala3.scala @@ -382,7 +382,7 @@ object Scala3: object LocalSymbol: def unapply(symbolInfo: SymbolInformation): Option[Int] = symbolInfo.symbol match - case locals(ints) => + case locals(ints: String) => val bi = BigInt(ints) if bi.isValidInt then Some(bi.toInt) diff --git a/compiler/src/dotty/tools/repl/ParseResult.scala b/compiler/src/dotty/tools/repl/ParseResult.scala index 2b7740152fa4..24aa07c707a7 100644 --- a/compiler/src/dotty/tools/repl/ParseResult.scala +++ b/compiler/src/dotty/tools/repl/ParseResult.scala @@ -180,7 +180,7 @@ object ParseResult { val sourceCode = source.content().mkString sourceCode match { case "" => Newline - case CommandExtract(cmd, arg) => { + case CommandExtract(cmd: String, arg: String) => { val matchingCommands = commands.filter((command, _) => command.startsWith(cmd)) matchingCommands match { case Nil => UnknownCommand(cmd) diff --git a/compiler/src/dotty/tools/scripting/Main.scala b/compiler/src/dotty/tools/scripting/Main.scala index 16e2fe35e6f1..daf69e8384a9 100755 --- a/compiler/src/dotty/tools/scripting/Main.scala +++ b/compiler/src/dotty/tools/scripting/Main.scala @@ -78,7 +78,7 @@ object Main: writer.close() end writeJarfile - def pathsep = sys.props("path.separator") + def pathsep: String = sys.props("path.separator").nn extension(path: String) { // Normalize path separator, convert relative path to absolute @@ -102,4 +102,4 @@ object Main: def secondChar: String = path.take(2).drop(1).mkString("") } - lazy val userDir = sys.props("user.dir").norm + lazy val userDir: String = sys.props("user.dir").nn.norm diff --git a/compiler/src/dotty/tools/scripting/Util.scala b/compiler/src/dotty/tools/scripting/Util.scala index 99af967143d3..5ce71aa7c130 100755 --- a/compiler/src/dotty/tools/scripting/Util.scala +++ b/compiler/src/dotty/tools/scripting/Util.scala @@ -56,7 +56,7 @@ object Util: end match end detectMainClassAndMethod - def pathsep = sys.props("path.separator") + def pathsep: String = sys.props("path.separator").nn end Util diff --git a/compiler/test/dotty/tools/debug/DebugStepAssert.scala b/compiler/test/dotty/tools/debug/DebugStepAssert.scala index f3c3d8af405f..bc9d67d7d3f5 100644 --- a/compiler/test/dotty/tools/debug/DebugStepAssert.scala +++ b/compiler/test/dotty/tools/debug/DebugStepAssert.scala @@ -49,17 +49,17 @@ private[debug] object DebugStepAssert: given location: CheckFileLocation = CheckFileLocation(checkFile, allLines.size - lines.size + 1) lines match case Nil => acc.reverse - case break(className , lineStr) :: tail => + case break(className: String, lineStr: String) :: tail => val breakpointLine = lineStr.toInt val step = DebugStepAssert(Break(className, breakpointLine), checkClassAndLine(className, breakpointLine)) loop(tail, step :: acc) - case step(pattern) :: tail => + case step(pattern: String) :: tail => val step = DebugStepAssert(Step, checkLineOrMethod(pattern)) loop(tail, step :: acc) - case next(pattern) :: tail => + case next(pattern: String) :: tail => val step = DebugStepAssert(Next, checkLineOrMethod(pattern)) loop(tail, step :: acc) - case eval(expr) :: tail0 => + case eval(expr: String) :: tail0 => val (assertion, tail1) = parseEvalAssertion(tail0) val step = DebugStepAssert(Eval(expr), assertion) loop(tail1, step :: acc) @@ -78,8 +78,8 @@ private[debug] object DebugStepAssert: lines match case Nil => throw new Exception(s"Missing result or error") case trailing() :: tail => parseEvalAssertion(tail) - case result(expected) :: tail => (checkResult(expected), tail) - case error(expected) :: tail => (checkError(Seq(expected)), tail) + case result(expected: String) :: tail => (checkResult(expected), tail) + case error(expected: String) :: tail => (checkError(Seq(expected)), tail) case multiLineError() :: tail0 => val (expected, tail1) = tail0.span(_.startsWith(" ")) (checkError(expected.map(_.stripPrefix(" "))), tail1) diff --git a/compiler/test/dotty/tools/dotc/CompilationTests.scala b/compiler/test/dotty/tools/dotc/CompilationTests.scala index baf1b4d66306..8626d823aa4f 100644 --- a/compiler/test/dotty/tools/dotc/CompilationTests.scala +++ b/compiler/test/dotty/tools/dotc/CompilationTests.scala @@ -205,7 +205,6 @@ class CompilationTests { ).checkCompile() // Explicit nulls tests - @Ignore @Test def explicitNullsNeg: Unit = { implicit val testGroup: TestGroup = TestGroup("explicitNullsNeg") aggregateTests( @@ -214,18 +213,18 @@ class CompilationTests { compileFilesInDir("tests/explicit-nulls/unsafe-common", explicitNullsOptions `and` "-Yno-flexible-types", FileFilter.exclude(TestSources.negExplicitNullsScala2LibraryTastyExcludelisted)), ).checkExpectedErrors() - locally { - val unsafeFile = compileFile("tests/explicit-nulls/flexible-unpickle/neg/Unsafe_1.scala", explicitNullsOptions without "-Yexplicit-nulls") - val flexibleFile = compileFile("tests/explicit-nulls/flexible-unpickle/neg/Flexible_2.scala", - explicitNullsOptions.and("-Yflexify-tasty").withClasspath(defaultOutputDir + testGroup + "/Unsafe_1/neg/Unsafe_1")) + // locally { + // val unsafeFile = compileFile("tests/explicit-nulls/flexible-unpickle/neg/Unsafe_1.scala", explicitNullsOptions without "-Yexplicit-nulls") + // val flexibleFile = compileFile("tests/explicit-nulls/flexible-unpickle/neg/Flexible_2.scala", + // explicitNullsOptions.and("-Yflexify-tasty").withClasspath(defaultOutputDir + testGroup + "/Unsafe_1/neg/Unsafe_1")) - flexibleFile.keepOutput.checkExpectedErrors() + // unsafeFile.keepOutput.checkCompile() + // flexibleFile.keepOutput.checkExpectedErrors() - List(unsafeFile, flexibleFile).foreach(_.delete()) - } + // List(unsafeFile, flexibleFile).foreach(_.delete()) + // } } - @Ignore @Test def explicitNullsPos: Unit = { implicit val testGroup: TestGroup = TestGroup("explicitNullsPos") aggregateTests( @@ -234,24 +233,22 @@ class CompilationTests { compileFilesInDir("tests/explicit-nulls/unsafe-common", explicitNullsOptions `and` "-language:unsafeNulls" `and` "-Yno-flexible-types"), ).checkCompile() - locally { - val tests = List( - compileFile("tests/explicit-nulls/flexible-unpickle/pos/Unsafe_1.scala", explicitNullsOptions `without` "-Yexplicit-nulls"), - compileFile("tests/explicit-nulls/flexible-unpickle/pos/Flexible_2.scala", - explicitNullsOptions.and("-Yflexify-tasty").withClasspath(defaultOutputDir + testGroup + "/Unsafe_1/pos/Unsafe_1")), - ).map(_.keepOutput.checkCompile()) + // locally { + // val tests = List( + // compileFile("tests/explicit-nulls/flexible-unpickle/pos/Unsafe_1.scala", explicitNullsOptions without "-Yexplicit-nulls"), + // compileFile("tests/explicit-nulls/flexible-unpickle/pos/Flexible_2.scala", + // explicitNullsOptions.and("-Yflexify-tasty").withClasspath(defaultOutputDir + testGroup + "/Unsafe_1/pos/Unsafe_1")), + // ).map(_.keepOutput.checkCompile()) - tests.foreach(_.delete()) - } + // tests.foreach(_.delete()) + // } } - @Ignore @Test def explicitNullsWarn: Unit = { implicit val testGroup: TestGroup = TestGroup("explicitNullsWarn") compileFilesInDir("tests/explicit-nulls/warn", explicitNullsOptions) }.checkWarnings() - @Ignore @Test def explicitNullsRun: Unit = { implicit val testGroup: TestGroup = TestGroup("explicitNullsRun") compileFilesInDir("tests/explicit-nulls/run", explicitNullsOptions) diff --git a/compiler/test/dotty/tools/dotc/profile/TraceNameManglingTest.scala b/compiler/test/dotty/tools/dotc/profile/TraceNameManglingTest.scala index f1f570cc85d4..30915682cd16 100644 --- a/compiler/test/dotty/tools/dotc/profile/TraceNameManglingTest.scala +++ b/compiler/test/dotty/tools/dotc/profile/TraceNameManglingTest.scala @@ -27,7 +27,7 @@ class TraceNameManglingTest extends DottyTest { } @Test def escapeBackslashes(): Unit = { - val isWindows = sys.props("os.name").toLowerCase(Locale.ROOT).nn.contains("windows") + val isWindows = sys.props("os.name").nn.toLowerCase(Locale.ROOT).nn.contains("windows") // It is not possible to create a file with backslash in name on Windows val filename = if isWindows then "test.scala" else "\\.scala" checkTraceEvents( diff --git a/compiler/test/dotty/tools/io/ClasspathTest.scala b/compiler/test/dotty/tools/io/ClasspathTest.scala index 333f2b8062b0..7af68e47b3b8 100755 --- a/compiler/test/dotty/tools/io/ClasspathTest.scala +++ b/compiler/test/dotty/tools/io/ClasspathTest.scala @@ -13,7 +13,7 @@ import dotty.tools.io.{ PlainDirectory, Directory, ClassPath } class ClasspathTest { - def pathsep = sys.props("path.separator") + def pathsep: String = sys.props("path.separator").nn def isWindows: Boolean = scala.util.Properties.isWin diff --git a/library-js/src/scala/Console.scala b/library-js/src/scala/Console.scala index 7eb19b1012ac..bb8aa8f699c8 100644 --- a/library-js/src/scala/Console.scala +++ b/library-js/src/scala/Console.scala @@ -131,7 +131,7 @@ import scala.util.DynamicVariable object Console extends AnsiColor { private[this] val outVar = new DynamicVariable[PrintStream](java.lang.System.out) private[this] val errVar = new DynamicVariable[PrintStream](java.lang.System.err) - private[this] val inVar = new DynamicVariable[BufferedReader](null) + private[this] val inVar = new DynamicVariable[BufferedReader](null.asInstanceOf[BufferedReader]) //new BufferedReader(new InputStreamReader(java.lang.System.in))) protected def setOutDirect(out: PrintStream): Unit = outVar.value = out diff --git a/library-js/src/scala/Enumeration.scala b/library-js/src/scala/Enumeration.scala index 94aec25a60f2..2408cfb2af04 100644 --- a/library-js/src/scala/Enumeration.scala +++ b/library-js/src/scala/Enumeration.scala @@ -107,7 +107,7 @@ abstract class Enumeration (initial: Int) extends Serializable { private val vmap: mutable.Map[Int, Value] = new mutable.HashMap /** The cache listing all values of this enumeration. */ - @transient private var vset: ValueSet = null + @transient private var vset: ValueSet | Null = null @transient @volatile private var vsetDefined = false /** The mapping from the integer used to identify values to their @@ -121,7 +121,7 @@ abstract class Enumeration (initial: Int) extends Serializable { vset = (ValueSet.newBuilder ++= vmap.values).result() vsetDefined = true } - vset + vset.nn } /** The integer to use to identify the next created value. */ @@ -130,7 +130,7 @@ abstract class Enumeration (initial: Int) extends Serializable { /** The string to use to name the next created value. */ protected var nextName: Iterator[String] = _ - private def nextNameOrNull = + private def nextNameOrNull: String | Null = if (nextName != null && nextName.hasNext) nextName.next() else null /** The highest integer amongst those used to identify values in this @@ -192,7 +192,7 @@ abstract class Enumeration (initial: Int) extends Serializable { * @param name A human-readable name for that value. * @return Fresh value called `name`. */ - protected final def Value(name: String): Value = Value(nextId, name) + protected final def Value(name: String | Null): Value = Value(nextId, name) /** Creates a fresh value, part of this enumeration, called `name` * and identified by the integer `i`. @@ -202,7 +202,7 @@ abstract class Enumeration (initial: Int) extends Serializable { * @param name A human-readable name for that value. * @return Fresh value with the provided identifier `i` and name `name`. */ - protected final def Value(i: Int, name: String): Value = new Val(i, name) + protected final def Value(i: Int, name: String | Null): Value = new Val(i, name) /** The type of the enumerated values. */ @SerialVersionUID(7091335633555234129L) @@ -231,9 +231,9 @@ abstract class Enumeration (initial: Int) extends Serializable { * identification behaviour. */ @SerialVersionUID(0 - 3501153230598116017L) - protected class Val(i: Int, name: String) extends Value with Serializable { + protected class Val(i: Int, name: String | Null) extends Value with Serializable { def this(i: Int) = this(i, nextNameOrNull) - def this(name: String) = this(nextId, name) + def this(name: String | Null) = this(nextId, name) def this() = this(nextId) assert(!vmap.isDefinedAt(i), "Duplicate id: " + i) diff --git a/library-js/src/scala/Symbol.scala b/library-js/src/scala/Symbol.scala index 2d9ac33c8480..a5bbd615ef36 100644 --- a/library-js/src/scala/Symbol.scala +++ b/library-js/src/scala/Symbol.scala @@ -46,7 +46,7 @@ object Symbol extends UniquenessCache[String, Symbol] { } // Modified to use Scala.js specific cache -private[scala] abstract class UniquenessCache[K, V >: Null] { +private[scala] abstract class UniquenessCache[K, V] { private val cache = js.Dictionary.empty[V] protected def valueFromKey(k: String): V diff --git a/library-js/src/scala/collection/mutable/ArrayBuilder.scala b/library-js/src/scala/collection/mutable/ArrayBuilder.scala index 4a6f81502474..f8a5b6abc47f 100644 --- a/library-js/src/scala/collection/mutable/ArrayBuilder.scala +++ b/library-js/src/scala/collection/mutable/ArrayBuilder.scala @@ -32,7 +32,7 @@ sealed abstract class ArrayBuilder[T] extends ReusableBuilder[T, Array[T]] with Serializable { protected[this] var capacity: Int = 0 - protected[this] def elems: Array[T] + protected[this] def elems: Array[T] | Null // may not be allocated at size = capacity = 0 protected var size: Int = 0 def length: Int = size @@ -60,7 +60,7 @@ sealed abstract class ArrayBuilder[T] /** Add a slice of an array */ def addAll(xs: Array[_ <: T], offset: Int, length: Int): this.type = { ensureSize(this.size + length) - Array.copy(xs, offset, elems, this.size, length) + Array.copy(xs, offset, elems.nn, this.size, length) size += length this } @@ -70,8 +70,8 @@ sealed abstract class ArrayBuilder[T] if(k > 0) { ensureSize(this.size + k) xs match { - case xs: Iterable[T] => xs.copyToArray(elems, this.size) - case _ => xs.iterator.copyToArray(elems, this.size) + case xs: Iterable[T] => xs.copyToArray(elems.nn, this.size) + case _ => xs.iterator.copyToArray(elems.nn, this.size) } size += k } else if(k < 0) super.addAll(xs) @@ -227,12 +227,12 @@ object ArrayBuilder { * @tparam T type of elements for the array builder, subtype of `AnyRef` with a `ClassTag` context bound. */ @SerialVersionUID(3L) - final class ofRef[T <: AnyRef](implicit ct: ClassTag[T]) extends ArrayBuilder[T] { + final class ofRef[T <: AnyRef | Null](implicit ct: ClassTag[T]) extends ArrayBuilder[T] { - protected var elems: Array[T] = _ + protected var elems: Array[T] | Null = null private def mkArray(size: Int): Array[T] = { - if (capacity == size && capacity > 0) elems + if (capacity == size && capacity > 0) elems.nn else if (elems eq null) new Array[T](size) else java.util.Arrays.copyOf[T](elems, size) } @@ -244,7 +244,7 @@ object ArrayBuilder { def addOne(elem: T): this.type = { ensureSize(size + 1) - elems(size) = elem + elems.nn(size) = elem size += 1 this } @@ -252,7 +252,7 @@ object ArrayBuilder { def result() = { if (capacity != 0 && capacity == size) { capacity = 0 - val res = elems + val res = elems.nn elems = null res } @@ -276,11 +276,11 @@ object ArrayBuilder { @SerialVersionUID(3L) final class ofByte extends ArrayBuilder[Byte] { - protected var elems: Array[Byte] = _ + protected var elems: Array[Byte] | Null = null private def mkArray(size: Int): Array[Byte] = { val newelems = new Array[Byte](size) - if (this.size > 0) Array.copy(elems, 0, newelems, 0, this.size) + if (this.size > 0) Array.copy(elems.nn, 0, newelems, 0, this.size) newelems } @@ -291,7 +291,7 @@ object ArrayBuilder { def addOne(elem: Byte): this.type = { ensureSize(size + 1) - elems(size) = elem + elems.nn(size) = elem size += 1 this } @@ -299,7 +299,7 @@ object ArrayBuilder { def result() = { if (capacity != 0 && capacity == size) { capacity = 0 - val res = elems + val res = elems.nn elems = null res } @@ -318,11 +318,11 @@ object ArrayBuilder { @SerialVersionUID(3L) final class ofShort extends ArrayBuilder[Short] { - protected var elems: Array[Short] = _ + protected var elems: Array[Short] | Null = null private def mkArray(size: Int): Array[Short] = { val newelems = new Array[Short](size) - if (this.size > 0) Array.copy(elems, 0, newelems, 0, this.size) + if (this.size > 0) Array.copy(elems.nn, 0, newelems, 0, this.size) newelems } @@ -333,7 +333,7 @@ object ArrayBuilder { def addOne(elem: Short): this.type = { ensureSize(size + 1) - elems(size) = elem + elems.nn(size) = elem size += 1 this } @@ -341,7 +341,7 @@ object ArrayBuilder { def result() = { if (capacity != 0 && capacity == size) { capacity = 0 - val res = elems + val res = elems.nn elems = null res } @@ -360,11 +360,11 @@ object ArrayBuilder { @SerialVersionUID(3L) final class ofChar extends ArrayBuilder[Char] { - protected var elems: Array[Char] = _ + protected var elems: Array[Char] | Null = null private def mkArray(size: Int): Array[Char] = { val newelems = new Array[Char](size) - if (this.size > 0) Array.copy(elems, 0, newelems, 0, this.size) + if (this.size > 0) Array.copy(elems.nn, 0, newelems, 0, this.size) newelems } @@ -375,7 +375,7 @@ object ArrayBuilder { def addOne(elem: Char): this.type = { ensureSize(size + 1) - elems(size) = elem + elems.nn(size) = elem size += 1 this } @@ -383,7 +383,7 @@ object ArrayBuilder { def result() = { if (capacity != 0 && capacity == size) { capacity = 0 - val res = elems + val res = elems.nn elems = null res } @@ -402,11 +402,11 @@ object ArrayBuilder { @SerialVersionUID(3L) final class ofInt extends ArrayBuilder[Int] { - protected var elems: Array[Int] = _ + protected var elems: Array[Int] | Null = null private def mkArray(size: Int): Array[Int] = { val newelems = new Array[Int](size) - if (this.size > 0) Array.copy(elems, 0, newelems, 0, this.size) + if (this.size > 0) Array.copy(elems.nn, 0, newelems, 0, this.size) newelems } @@ -417,7 +417,7 @@ object ArrayBuilder { def addOne(elem: Int): this.type = { ensureSize(size + 1) - elems(size) = elem + elems.nn(size) = elem size += 1 this } @@ -425,7 +425,7 @@ object ArrayBuilder { def result() = { if (capacity != 0 && capacity == size) { capacity = 0 - val res = elems + val res = elems.nn elems = null res } @@ -444,11 +444,11 @@ object ArrayBuilder { @SerialVersionUID(3L) final class ofLong extends ArrayBuilder[Long] { - protected var elems: Array[Long] = _ + protected var elems: Array[Long] | Null = null private def mkArray(size: Int): Array[Long] = { val newelems = new Array[Long](size) - if (this.size > 0) Array.copy(elems, 0, newelems, 0, this.size) + if (this.size > 0) Array.copy(elems.nn, 0, newelems, 0, this.size) newelems } @@ -459,7 +459,7 @@ object ArrayBuilder { def addOne(elem: Long): this.type = { ensureSize(size + 1) - elems(size) = elem + elems.nn(size) = elem size += 1 this } @@ -467,7 +467,7 @@ object ArrayBuilder { def result() = { if (capacity != 0 && capacity == size) { capacity = 0 - val res = elems + val res = elems.nn elems = null res } @@ -486,11 +486,11 @@ object ArrayBuilder { @SerialVersionUID(3L) final class ofFloat extends ArrayBuilder[Float] { - protected var elems: Array[Float] = _ + protected var elems: Array[Float] | Null = null private def mkArray(size: Int): Array[Float] = { val newelems = new Array[Float](size) - if (this.size > 0) Array.copy(elems, 0, newelems, 0, this.size) + if (this.size > 0) Array.copy(elems.nn, 0, newelems, 0, this.size) newelems } @@ -501,7 +501,7 @@ object ArrayBuilder { def addOne(elem: Float): this.type = { ensureSize(size + 1) - elems(size) = elem + elems.nn(size) = elem size += 1 this } @@ -509,7 +509,7 @@ object ArrayBuilder { def result() = { if (capacity != 0 && capacity == size) { capacity = 0 - val res = elems + val res = elems.nn elems = null res } @@ -528,11 +528,11 @@ object ArrayBuilder { @SerialVersionUID(3L) final class ofDouble extends ArrayBuilder[Double] { - protected var elems: Array[Double] = _ + protected var elems: Array[Double] | Null = null private def mkArray(size: Int): Array[Double] = { val newelems = new Array[Double](size) - if (this.size > 0) Array.copy(elems, 0, newelems, 0, this.size) + if (this.size > 0) Array.copy(elems.nn, 0, newelems, 0, this.size) newelems } @@ -543,7 +543,7 @@ object ArrayBuilder { def addOne(elem: Double): this.type = { ensureSize(size + 1) - elems(size) = elem + elems.nn(size) = elem size += 1 this } @@ -551,7 +551,7 @@ object ArrayBuilder { def result() = { if (capacity != 0 && capacity == size) { capacity = 0 - val res = elems + val res = elems.nn elems = null res } @@ -570,11 +570,11 @@ object ArrayBuilder { @SerialVersionUID(3L) class ofBoolean extends ArrayBuilder[Boolean] { - protected var elems: Array[Boolean] = _ + protected var elems: Array[Boolean] | Null = null private def mkArray(size: Int): Array[Boolean] = { val newelems = new Array[Boolean](size) - if (this.size > 0) Array.copy(elems, 0, newelems, 0, this.size) + if (this.size > 0) Array.copy(elems.nn, 0, newelems, 0, this.size) newelems } @@ -585,7 +585,7 @@ object ArrayBuilder { def addOne(elem: Boolean): this.type = { ensureSize(size + 1) - elems(size) = elem + elems.nn(size) = elem size += 1 this } @@ -593,7 +593,7 @@ object ArrayBuilder { def result() = { if (capacity != 0 && capacity == size) { capacity = 0 - val res = elems + val res = elems.nn elems = null res } @@ -612,7 +612,7 @@ object ArrayBuilder { @SerialVersionUID(3L) final class ofUnit extends ArrayBuilder[Unit] { - protected def elems: Array[Unit] = throw new UnsupportedOperationException() + protected def elems: Array[Unit] | Null = throw new UnsupportedOperationException() def addOne(elem: Unit): this.type = { size += 1 diff --git a/library-js/src/scala/runtime/ScalaRunTime.scala b/library-js/src/scala/runtime/ScalaRunTime.scala index 4247002cce6b..28aba9dea39e 100644 --- a/library-js/src/scala/runtime/ScalaRunTime.scala +++ b/library-js/src/scala/runtime/ScalaRunTime.scala @@ -254,22 +254,32 @@ object ScalaRunTime { case s => s + "\n" } + + // For backward compatibility with code compiled without -Yexplicit-nulls. + // If `a` is null, return null; otherwise, return `f`. + private[scala] inline def mapNull[A, B](a: A, inline f: B): B = + if ((a: A | Null) == null) null.asInstanceOf[B] else f + + // Use `null` in places where we want to make sure the reference is cleared. + private[scala] inline def nullForGC[T]: T = null.asInstanceOf[T] + + // For the following functions, both the parameter and the return type are non-nullable. + // However, if a null reference is passed explicitly, this method will still return null. + // We intentionally keep this signature to discourage passing nulls implicitly while + // preserving the previous behavior for backward compatibility. + // Convert arrays to immutable.ArraySeq for use with Java varargs: def genericWrapArray[T](xs: Array[T]): ArraySeq[T] = - if (xs eq null) null - else ArraySeq.unsafeWrapArray(xs) - def wrapRefArray[T <: AnyRef](xs: Array[T]): ArraySeq[T] = { - if (xs eq null) null - else if (xs.length == 0) ArraySeq.empty[AnyRef].asInstanceOf[ArraySeq[T]] - else new ArraySeq.ofRef[T](xs) - } - def wrapIntArray(xs: Array[Int]): ArraySeq[Int] = if (xs ne null) new ArraySeq.ofInt(xs) else null - def wrapDoubleArray(xs: Array[Double]): ArraySeq[Double] = if (xs ne null) new ArraySeq.ofDouble(xs) else null - def wrapLongArray(xs: Array[Long]): ArraySeq[Long] = if (xs ne null) new ArraySeq.ofLong(xs) else null - def wrapFloatArray(xs: Array[Float]): ArraySeq[Float] = if (xs ne null) new ArraySeq.ofFloat(xs) else null - def wrapCharArray(xs: Array[Char]): ArraySeq[Char] = if (xs ne null) new ArraySeq.ofChar(xs) else null - def wrapByteArray(xs: Array[Byte]): ArraySeq[Byte] = if (xs ne null) new ArraySeq.ofByte(xs) else null - def wrapShortArray(xs: Array[Short]): ArraySeq[Short] = if (xs ne null) new ArraySeq.ofShort(xs) else null - def wrapBooleanArray(xs: Array[Boolean]): ArraySeq[Boolean] = if (xs ne null) new ArraySeq.ofBoolean(xs) else null - def wrapUnitArray(xs: Array[Unit]): ArraySeq[Unit] = if (xs ne null) new ArraySeq.ofUnit(xs) else null + mapNull(xs, ArraySeq.unsafeWrapArray(xs)) + def wrapRefArray[T <: AnyRef | Null](xs: Array[T]): ArraySeq[T] = + mapNull(xs, if (xs.length == 0) ArraySeq.empty[AnyRef].asInstanceOf[ArraySeq[T]] else new ArraySeq.ofRef[T](xs)) + def wrapIntArray(xs: Array[Int]): ArraySeq[Int] = mapNull(xs, new ArraySeq.ofInt(xs)) + def wrapDoubleArray(xs: Array[Double]): ArraySeq[Double] = mapNull(xs, new ArraySeq.ofDouble(xs)) + def wrapLongArray(xs: Array[Long]): ArraySeq[Long] = mapNull(xs, new ArraySeq.ofLong(xs)) + def wrapFloatArray(xs: Array[Float]): ArraySeq[Float] = mapNull(xs, new ArraySeq.ofFloat(xs)) + def wrapCharArray(xs: Array[Char]): ArraySeq[Char] = mapNull(xs, new ArraySeq.ofChar(xs)) + def wrapByteArray(xs: Array[Byte]): ArraySeq[Byte] = mapNull(xs, new ArraySeq.ofByte(xs)) + def wrapShortArray(xs: Array[Short]): ArraySeq[Short] = mapNull(xs, new ArraySeq.ofShort(xs)) + def wrapBooleanArray(xs: Array[Boolean]): ArraySeq[Boolean] = mapNull(xs, new ArraySeq.ofBoolean(xs)) + def wrapUnitArray(xs: Array[Unit]): ArraySeq[Unit] = mapNull(xs, new ArraySeq.ofUnit(xs)) } diff --git a/library/src/scala/Enumeration.scala b/library/src/scala/Enumeration.scala index b527fd3fc2fb..2e5347472826 100644 --- a/library/src/scala/Enumeration.scala +++ b/library/src/scala/Enumeration.scala @@ -107,7 +107,7 @@ abstract class Enumeration (initial: Int) extends Serializable { private val vmap: mutable.Map[Int, Value] = new mutable.HashMap /** The cache listing all values of this enumeration. */ - @transient private var vset: ValueSet = null + @transient private var vset: ValueSet | Null = null @transient @volatile private var vsetDefined = false /** The mapping from the integer used to identify values to their @@ -121,7 +121,7 @@ abstract class Enumeration (initial: Int) extends Serializable { vset = (ValueSet.newBuilder ++= vmap.values).result() vsetDefined = true } - vset + vset.nn } /** The integer to use to identify the next created value. */ @@ -130,7 +130,7 @@ abstract class Enumeration (initial: Int) extends Serializable { /** The string to use to name the next created value. */ protected var nextName: Iterator[String] = _ - private def nextNameOrNull = + private def nextNameOrNull: String | Null = if (nextName != null && nextName.hasNext) nextName.next() else null /** The highest integer amongst those used to identify values in this @@ -177,7 +177,7 @@ abstract class Enumeration (initial: Int) extends Serializable { * @param name A human-readable name for that value. * @return Fresh value called `name`. */ - protected final def Value(name: String): Value = Value(nextId, name) + protected final def Value(name: String | Null): Value = Value(nextId, name) /** Creates a fresh value, part of this enumeration, called `name` * and identified by the integer `i`. @@ -187,10 +187,10 @@ abstract class Enumeration (initial: Int) extends Serializable { * @param name A human-readable name for that value. * @return Fresh value with the provided identifier `i` and name `name`. */ - protected final def Value(i: Int, name: String): Value = new Val(i, name) + protected final def Value(i: Int, name: String | Null): Value = new Val(i, name) private def populateNameMap(): Unit = { - @tailrec def getFields(clazz: Class[_], acc: Array[JField]): Array[JField] = { + @tailrec def getFields(clazz: Class[_] | Null, acc: Array[JField]): Array[JField] = { if (clazz == null) acc else @@ -248,9 +248,9 @@ abstract class Enumeration (initial: Int) extends Serializable { * identification behaviour. */ @SerialVersionUID(0 - 3501153230598116017L) - protected class Val(i: Int, name: String) extends Value with Serializable { + protected class Val(i: Int, name: String | Null) extends Value with Serializable { def this(i: Int) = this(i, nextNameOrNull) - def this(name: String) = this(nextId, name) + def this(name: String | Null) = this(nextId, name) def this() = this(nextId) assert(!vmap.isDefinedAt(i), "Duplicate id: " + i) diff --git a/library/src/scala/IArray.scala b/library/src/scala/IArray.scala index 281d1ef78a89..275eaccd7325 100644 --- a/library/src/scala/IArray.scala +++ b/library/src/scala/IArray.scala @@ -330,22 +330,30 @@ object IArray: extension [T, U >: T: ClassTag](x: T) def +:(arr: IArray[U]): IArray[U] = genericArrayOps(arr).prepended(x) - // For backwards compatibility with code compiled without -Yexplicit-nulls + // For backward compatibility with code compiled without -Yexplicit-nulls. + // If `a` is null, return null; otherwise, return `f`. + // Have to add a private mapNull here to avoid errors, use `ScalaRunTime.mapNull` in the future. private inline def mapNull[A, B](a: A, inline f: B): B = - if((a: A|Null) == null) null.asInstanceOf[B] else f + if ((a: A | Null) == null) null.asInstanceOf[B] else f + + // For the following functions, both the parameter and the return type are non-nullable. + // However, if a null reference is passed explicitly, this method will still return null. + // We intentionally keep this signature to discourage passing nulls implicitly while + // preserving the previous behavior for backward compatibility. /** Conversion from IArray to immutable.ArraySeq */ implicit def genericWrapArray[T](arr: IArray[T]): ArraySeq[T] = mapNull(arr, ArraySeq.unsafeWrapArray(arr)) /** Conversion from IArray to immutable.ArraySeq */ - implicit def wrapRefArray[T <: AnyRef](arr: IArray[T]): ArraySeq.ofRef[T] = - // Since the JVM thinks arrays are covariant, one 0-length Array[AnyRef] - // is as good as another for all T <: AnyRef. Instead of creating 100,000,000 + import scala.language.unsafeNulls // TODO!!! only for stdlib migration! + implicit def wrapRefArray[T <: AnyRef | Null](arr: IArray[T]): ArraySeq.ofRef[T] = + // Since the JVM thinks arrays are covariant, one 0-length Array[AnyRef | Null] + // is as good as another for all T <: AnyRef | Null. Instead of creating 100,000,000 // unique ones by way of this implicit, let's share one. mapNull(arr, - if (arr.length == 0) ArraySeq.empty[AnyRef].asInstanceOf[ArraySeq.ofRef[T]] - else ArraySeq.ofRef(arr.asInstanceOf[Array[T]]) + if (arr.length == 0) ArraySeq.empty[AnyRef | Null].asInstanceOf[ArraySeq.ofRef[T]] + else ArraySeq.ofRef[AnyRef](arr.asInstanceOf[Array[AnyRef]]).asInstanceOf[ArraySeq.ofRef[T]] ) /** Conversion from IArray to immutable.ArraySeq */ diff --git a/library/src/scala/MatchError.scala b/library/src/scala/MatchError.scala index 7e6bcc480d8c..2053d2cc6f9c 100644 --- a/library/src/scala/MatchError.scala +++ b/library/src/scala/MatchError.scala @@ -22,7 +22,7 @@ final class MatchError(@transient obj: Any) extends RuntimeException { /** There's no reason we need to call toString eagerly, * so defer it until getMessage is called or object is serialized */ - private[this] lazy val objString = { + private[this] lazy val objString: String = { def ofClass = "of class " + obj.getClass.getName if (obj == null) "null" else @@ -38,5 +38,5 @@ final class MatchError(@transient obj: Any) extends RuntimeException { this } - override def getMessage() = objString + override def getMessage(): String = objString } diff --git a/library/src/scala/Option.scala b/library/src/scala/Option.scala index 894eade2445a..b2eadf2c1be3 100644 --- a/library/src/scala/Option.scala +++ b/library/src/scala/Option.scala @@ -28,7 +28,7 @@ object Option { * @param x the value * @return Some(value) if value != null, None if value == null */ - def apply[A](x: A): Option[A] = if (x == null) None else Some(x) + def apply[A](x: A | Null): Option[A] = if (x == null) None else Some(x) /** An Option factory which returns `None` in a manner consistent with * the collections hierarchy. diff --git a/library/src/scala/Predef.scala b/library/src/scala/Predef.scala index 552a491470ae..48030f2f1232 100644 --- a/library/src/scala/Predef.scala +++ b/library/src/scala/Predef.scala @@ -20,6 +20,7 @@ import scala.annotation.{elidable, experimental, implicitNotFound, publicInBinar import scala.annotation.meta.{ companionClass, companionMethod } import scala.annotation.internal.{ RuntimeChecked } import scala.compiletime.summonFrom +import scala.runtime.ScalaRunTime.mapNull /** The `Predef` object provides definitions that are accessible in all Scala * compilation units without explicit qualification. @@ -122,7 +123,7 @@ object Predef extends LowPriorityImplicits { * @return The runtime [[Class]] representation of type `T`. * @group utilities */ - def classOf[T]: Class[T] = null // This is a stub method. The actual implementation is filled in by the compiler. + def classOf[T]: Class[T] = null.asInstanceOf[Class[T]] // This is a stub method. The actual implementation is filled in by the compiler. /** * Retrieve the single value of a type with a unique inhabitant. @@ -517,7 +518,7 @@ object Predef extends LowPriorityImplicits { @inline implicit def floatArrayOps(xs: Array[Float]): ArrayOps[Float] = new ArrayOps(xs) @inline implicit def intArrayOps(xs: Array[Int]): ArrayOps[Int] = new ArrayOps(xs) @inline implicit def longArrayOps(xs: Array[Long]): ArrayOps[Long] = new ArrayOps(xs) - @inline implicit def refArrayOps[T <: AnyRef](xs: Array[T]): ArrayOps[T] = new ArrayOps(xs) + @inline implicit def refArrayOps[T <: AnyRef | Null](xs: Array[T]): ArrayOps[T] = new ArrayOps(xs) @inline implicit def shortArrayOps(xs: Array[Short]): ArrayOps[Short] = new ArrayOps(xs) @inline implicit def unitArrayOps(xs: Array[Unit]): ArrayOps[Unit] = new ArrayOps(xs) @@ -658,45 +659,42 @@ private[scala] abstract class LowPriorityImplicits extends LowPriorityImplicits2 /** @group conversions-array-to-wrapped-array */ implicit def genericWrapArray[T](xs: Array[T]): ArraySeq[T] = - if (xs eq null) null - else ArraySeq.make(xs) + mapNull(xs, ArraySeq.make(xs)) // Since the JVM thinks arrays are covariant, one 0-length Array[AnyRef] // is as good as another for all T <: AnyRef. Instead of creating 100,000,000 // unique ones by way of this implicit, let's share one. /** @group conversions-array-to-wrapped-array */ - implicit def wrapRefArray[T <: AnyRef](xs: Array[T]): ArraySeq.ofRef[T] = { - if (xs eq null) null - else if (xs.length == 0) ArraySeq.empty[AnyRef].asInstanceOf[ArraySeq.ofRef[T]] - else new ArraySeq.ofRef[T](xs) - } + implicit def wrapRefArray[T <: AnyRef | Null](xs: Array[T]): ArraySeq.ofRef[T] = + mapNull(xs, + if (xs.length == 0) ArraySeq.empty[AnyRef].asInstanceOf[ArraySeq.ofRef[T]] + else new ArraySeq.ofRef[T](xs)) /** @group conversions-array-to-wrapped-array */ - implicit def wrapIntArray(xs: Array[Int]): ArraySeq.ofInt = if (xs ne null) new ArraySeq.ofInt(xs) else null + implicit def wrapIntArray(xs: Array[Int]): ArraySeq.ofInt = mapNull(xs, new ArraySeq.ofInt(xs)) /** @group conversions-array-to-wrapped-array */ - implicit def wrapDoubleArray(xs: Array[Double]): ArraySeq.ofDouble = if (xs ne null) new ArraySeq.ofDouble(xs) else null + implicit def wrapDoubleArray(xs: Array[Double]): ArraySeq.ofDouble = mapNull(xs, new ArraySeq.ofDouble(xs)) /** @group conversions-array-to-wrapped-array */ - implicit def wrapLongArray(xs: Array[Long]): ArraySeq.ofLong = if (xs ne null) new ArraySeq.ofLong(xs) else null + implicit def wrapLongArray(xs: Array[Long]): ArraySeq.ofLong = mapNull(xs, new ArraySeq.ofLong(xs)) /** @group conversions-array-to-wrapped-array */ - implicit def wrapFloatArray(xs: Array[Float]): ArraySeq.ofFloat = if (xs ne null) new ArraySeq.ofFloat(xs) else null + implicit def wrapFloatArray(xs: Array[Float]): ArraySeq.ofFloat = mapNull(xs, new ArraySeq.ofFloat(xs)) /** @group conversions-array-to-wrapped-array */ - implicit def wrapCharArray(xs: Array[Char]): ArraySeq.ofChar = if (xs ne null) new ArraySeq.ofChar(xs) else null + implicit def wrapCharArray(xs: Array[Char]): ArraySeq.ofChar = mapNull(xs, new ArraySeq.ofChar(xs)) /** @group conversions-array-to-wrapped-array */ - implicit def wrapByteArray(xs: Array[Byte]): ArraySeq.ofByte = if (xs ne null) new ArraySeq.ofByte(xs) else null + implicit def wrapByteArray(xs: Array[Byte]): ArraySeq.ofByte = mapNull(xs, new ArraySeq.ofByte(xs)) /** @group conversions-array-to-wrapped-array */ - implicit def wrapShortArray(xs: Array[Short]): ArraySeq.ofShort = if (xs ne null) new ArraySeq.ofShort(xs) else null + implicit def wrapShortArray(xs: Array[Short]): ArraySeq.ofShort = mapNull(xs, new ArraySeq.ofShort(xs)) /** @group conversions-array-to-wrapped-array */ - implicit def wrapBooleanArray(xs: Array[Boolean]): ArraySeq.ofBoolean = if (xs ne null) new ArraySeq.ofBoolean(xs) else null + implicit def wrapBooleanArray(xs: Array[Boolean]): ArraySeq.ofBoolean = mapNull(xs, new ArraySeq.ofBoolean(xs)) /** @group conversions-array-to-wrapped-array */ - implicit def wrapUnitArray(xs: Array[Unit]): ArraySeq.ofUnit = if (xs ne null) new ArraySeq.ofUnit(xs) else null + implicit def wrapUnitArray(xs: Array[Unit]): ArraySeq.ofUnit = mapNull(xs, new ArraySeq.ofUnit(xs)) /** @group conversions-string */ - implicit def wrapString(s: String): WrappedString = if (s ne null) new WrappedString(s) else null + implicit def wrapString(s: String): WrappedString = mapNull(s, new WrappedString(s)) } private[scala] abstract class LowPriorityImplicits2 { @deprecated("implicit conversions from Array to immutable.IndexedSeq are implemented by copying; use `toIndexedSeq` explicitly if you want to copy, or use the more efficient non-copying ArraySeq.unsafeWrapArray", since="2.13.0") implicit def copyArrayToImmutableIndexedSeq[T](xs: Array[T]): IndexedSeq[T] = - if (xs eq null) null - else new ArrayOps(xs).toIndexedSeq + mapNull(xs, new ArrayOps(xs).toIndexedSeq) } diff --git a/library/src/scala/Specializable.scala b/library/src/scala/Specializable.scala index 51822f9f6446..5728d8c4f03d 100644 --- a/library/src/scala/Specializable.scala +++ b/library/src/scala/Specializable.scala @@ -24,17 +24,17 @@ object Specializable { trait SpecializedGroup // Smuggle a list of types by way of a tuple upon which Group is parameterized. - class Group[T >: Null](value: T) extends SpecializedGroup + class Group[T](value: T) extends SpecializedGroup - final val Primitives: Group[(Byte, Short, Int, Long, Char, Float, Double, Boolean, Unit)] = null - final val Everything: Group[(Byte, Short, Int, Long, Char, Float, Double, Boolean, Unit, AnyRef)] = null - final val Bits32AndUp: Group[(Int, Long, Float, Double)] = null - final val Integral: Group[(Byte, Short, Int, Long, Char)] = null - final val AllNumeric: Group[(Byte, Short, Int, Long, Char, Float, Double)] = null - final val BestOfBreed: Group[(Int, Double, Boolean, Unit, AnyRef)] = null - final val Unit: Group[Tuple1[Unit]] = null + final val Primitives: Group[(Byte, Short, Int, Long, Char, Float, Double, Boolean, Unit)] = null.asInstanceOf[Group[(Byte, Short, Int, Long, Char, Float, Double, Boolean, Unit)]] + final val Everything: Group[(Byte, Short, Int, Long, Char, Float, Double, Boolean, Unit, AnyRef)] = null.asInstanceOf[Group[(Byte, Short, Int, Long, Char, Float, Double, Boolean, Unit, AnyRef)]] + final val Bits32AndUp: Group[(Int, Long, Float, Double)] = null.asInstanceOf[Group[(Int, Long, Float, Double)]] + final val Integral: Group[(Byte, Short, Int, Long, Char)] = null.asInstanceOf[Group[(Byte, Short, Int, Long, Char)]] + final val AllNumeric: Group[(Byte, Short, Int, Long, Char, Float, Double)] = null.asInstanceOf[Group[(Byte, Short, Int, Long, Char, Float, Double)]] + final val BestOfBreed: Group[(Int, Double, Boolean, Unit, AnyRef)] = null.asInstanceOf[Group[(Int, Double, Boolean, Unit, AnyRef)]] + final val Unit: Group[Tuple1[Unit]] = null.asInstanceOf[Group[Tuple1[Unit]]] - final val Arg: Group[(Int, Long, Float, Double)] = null - final val Args: Group[(Int, Long, Double)] = null - final val Return: Group[(Int, Long, Float, Double, Boolean, Unit)] = null + final val Arg: Group[(Int, Long, Float, Double)] = null.asInstanceOf[Group[(Int, Long, Float, Double)]] + final val Args: Group[(Int, Long, Double)] = null.asInstanceOf[Group[(Int, Long, Double)]] + final val Return: Group[(Int, Long, Float, Double, Boolean, Unit)] = null.asInstanceOf[Group[(Int, Long, Float, Double, Boolean, Unit)]] } diff --git a/library/src/scala/Symbol.scala b/library/src/scala/Symbol.scala index f865f2ce4262..922082cc0f62 100644 --- a/library/src/scala/Symbol.scala +++ b/library/src/scala/Symbol.scala @@ -36,7 +36,7 @@ object Symbol extends UniquenessCache[String, Symbol] { /** This is private so it won't appear in the library API, but * abstracted to offer some hope of reusability. */ -private[scala] abstract class UniquenessCache[K, V >: Null] { +private[scala] abstract class UniquenessCache[K, V] { import java.lang.ref.WeakReference import java.util.WeakHashMap import java.util.concurrent.locks.ReentrantReadWriteLock @@ -50,7 +50,7 @@ private[scala] abstract class UniquenessCache[K, V >: Null] { protected def keyFromValue(v: V): Option[K] def apply(name: K): V = { - def cached(): V = { + def cached(): V | Null = { rlock.lock try { val reference = map get name diff --git a/library/src/scala/collection/ArrayOps.scala b/library/src/scala/collection/ArrayOps.scala index 40570e5c261b..056dd1e9e5c2 100644 --- a/library/src/scala/collection/ArrayOps.scala +++ b/library/src/scala/collection/ArrayOps.scala @@ -429,7 +429,7 @@ final class ArrayOps[A](private val xs: Array[A]) extends AnyVal { val s = (shape.shape: @unchecked) match { case StepperShape.ReferenceShape => (xs: Any) match { case bs: Array[Boolean] => new BoxedBooleanArrayStepper(bs, 0, xs.length) - case _ => new ObjectArrayStepper[AnyRef](xs.asInstanceOf[Array[AnyRef ]], 0, xs.length) + case _ => new ObjectArrayStepper[AnyRef](xs.asInstanceOf[Array[AnyRef]], 0, xs.length) } case StepperShape.IntShape => new IntArrayStepper (xs.asInstanceOf[Array[Int ]], 0, xs.length) case StepperShape.LongShape => new LongArrayStepper (xs.asInstanceOf[Array[Long ]], 0, xs.length) diff --git a/library/src/scala/collection/Iterator.scala b/library/src/scala/collection/Iterator.scala index 4ddef7dcb18f..66faf001cac0 100644 --- a/library/src/scala/collection/Iterator.scala +++ b/library/src/scala/collection/Iterator.scala @@ -17,6 +17,7 @@ import language.experimental.captureChecking import scala.collection.mutable.{ArrayBuffer, ArrayBuilder, Builder, ImmutableBuilder} import scala.annotation.tailrec import scala.annotation.unchecked.uncheckedVariance +import scala.runtime.ScalaRunTime.nullForGC import scala.runtime.Statics /** Iterators are data structures that allow to iterate over a sequence @@ -124,7 +125,6 @@ trait Iterator[+A] extends IterableOnce[A] with IterableOnceOps[A, Iterator, Ite private[this] var hd: A = _ private[this] var hdDefined: Boolean = false - def head: A = { if (!hdDefined) { hd = next() @@ -161,12 +161,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] = null // current result - private[this] var prev: Array[B] = null // if sliding, overlap from previous result + private[this] var buffer: Array[B] | Null = null // current result + private[this] var prev: Array[B] | Null = 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 = null // what to pad short sequences with + private[this] var padding: (() -> B) | Null = 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] @@ -230,7 +230,7 @@ trait Iterator[+A] extends IterableOnce[A] with IterableOnceOps[A, Iterator, Ite val builder = newBuilder var done = false // keep prefix of previous buffer if stepping - if (prev != null) builder.addAll(prev) + if (prev != null) builder.addAll(prev.nn) // skip ahead if (!first && step > size) { var dropping = step - size @@ -251,7 +251,7 @@ trait Iterator[+A] extends IterableOnce[A] with IterableOnceOps[A, Iterator, Ite if (index < size && pad) { builder.sizeHint(size) while (index < size) { - builder.addOne(padding()) + builder.addOne(padding.nn()) index += 1 } } @@ -273,14 +273,15 @@ trait Iterator[+A] extends IterableOnce[A] with IterableOnceOps[A, Iterator, Ite if (!fill()) Iterator.empty.next() else { filled = false + val buffer = this.buffer.nn // if stepping, retain overlap in prev if (step < size) { if (first) prev = buffer.drop(step) - else if (buffer.length == size) Array.copy(src = buffer, srcPos = step, dest = prev, destPos = 0, length = size - step) + else if (buffer.length == size) Array.copy(src = buffer, srcPos = step, dest = prev.nn, destPos = 0, length = size - step) else prev = null } val res = immutable.ArraySeq.unsafeWrapArray(buffer).asInstanceOf[immutable.ArraySeq[B]] - buffer = null + this.buffer = null first = false res } @@ -702,7 +703,7 @@ trait Iterator[+A] extends IterableOnce[A] with IterableOnceOps[A, Iterator, Ite * handling of structural calls. It's not what's intended here. */ final class Leading extends AbstractIterator[A] { - private[this] var lookahead: mutable.Queue[A] = null + private[this] var lookahead: mutable.Queue[A] | Null = null private[this] var hd: A = _ /* Status is kept with magic numbers * 1 means next element is in hd and we're still reading into this iterator @@ -713,10 +714,10 @@ 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] - lookahead += a + lookahead.nn += a } def hasNext = { - if (status < 0) (lookahead ne null) && lookahead.nonEmpty + if (status < 0) (lookahead ne null) && lookahead.nn.nonEmpty else if (status > 0) true else { if (self.hasNext) { @@ -730,7 +731,7 @@ trait Iterator[+A] extends IterableOnce[A] with IterableOnceOps[A, Iterator, Ite def next() = { if (hasNext) { if (status == 1) { status = 0; hd } - else lookahead.dequeue() + else lookahead.nn.dequeue() } else Iterator.empty.next() } @@ -772,14 +773,14 @@ trait Iterator[+A] extends IterableOnce[A] with IterableOnceOps[A, Iterator, Ite case 1 => if (self.hasNext) { status = 2 ; true } else { status = 3 ; false } case 0 => true case _ => - if (myLeading.finish()) { status = 0 ; true } else { status = 1 ; myLeading = null ; hasNext } + if (myLeading.finish()) { status = 0 ; true } else { status = 1 ; myLeading = nullForGC[Leading]; hasNext } } def next() = { if (hasNext) { if (status == 0) { status = 1 val res = myLeading.trailer - myLeading = null + myLeading = nullForGC[Leading] res } else { status = 1 @@ -873,7 +874,7 @@ trait Iterator[+A] extends IterableOnce[A] with IterableOnceOps[A, Iterator, Ite */ def duplicate: (Iterator[A]^{this}, Iterator[A]^{this}) = { val gap = new scala.collection.mutable.Queue[A] - var ahead: Iterator[A]^ = null + var ahead: (Iterator[A]^) | Null = null class Partner extends AbstractIterator[A] { override def knownSize: Int = self.synchronized { val thisSize = self.knownSize @@ -1149,10 +1150,13 @@ object Iterator extends IterableFactory[Iterator] { /** Creates an iterator to which other iterators can be appended efficiently. * Nested ConcatIterators are merged to avoid blowing the stack. */ - private final class ConcatIterator[+A](val from: Iterator[A @uncheckedVariance]^) extends AbstractIterator[A] { - private var current: Iterator[A]^{from} = from - private var tail: ConcatIteratorCell[A @uncheckedVariance] = null - private var last: ConcatIteratorCell[A @uncheckedVariance] = null + private final class ConcatIterator[+A](val from: (Iterator[A @uncheckedVariance]^) | Null) extends AbstractIterator[A] { + @annotation.stableNull + private var current: Iterator[A]^{from} | Null = from + @annotation.stableNull + private var tail: ConcatIteratorCell[A @uncheckedVariance] | Null = null + @annotation.stableNull + private var last: ConcatIteratorCell[A @uncheckedVariance] | Null = null private var currentHasNextChecked = false def hasNext = @@ -1171,7 +1175,7 @@ object Iterator extends IterableFactory[Iterator] { currentHasNextChecked = c.currentHasNextChecked if (c.tail != null) { if (last == null) last = c.last - c.last.tail = tail + c.last.nn.tail = tail tail = c.tail } merge() @@ -1187,7 +1191,7 @@ object Iterator extends IterableFactory[Iterator] { } else { current = tail.headIterator - if (last eq tail) last = last.tail + if (last eq tail) last = last.nn.tail tail = tail.tail merge() if (currentHasNextChecked) true @@ -1203,7 +1207,7 @@ object Iterator extends IterableFactory[Iterator] { def next() = if (hasNext) { currentHasNextChecked = false - current.next() + current.nn.next() } else Iterator.empty.next() override def concat[B >: A](that: => IterableOnce[B]^): Iterator[B]^{this, that} = { @@ -1213,7 +1217,7 @@ object Iterator extends IterableFactory[Iterator] { last = c } else { - last.tail = c + last.nn.tail = c last = c } if (current == null) current = Iterator.empty @@ -1221,7 +1225,7 @@ object Iterator extends IterableFactory[Iterator] { } } - private[this] final class ConcatIteratorCell[A](head: => IterableOnce[A]^, var tail: ConcatIteratorCell[A]^) { + private[this] final class ConcatIteratorCell[A](head: => IterableOnce[A]^, var tail: (ConcatIteratorCell[A]^) | Null) { def headIterator: Iterator[A]^{this} = head.iterator } @@ -1289,7 +1293,7 @@ object Iterator extends IterableFactory[Iterator] { */ private final class UnfoldIterator[A, S](init: S)(f: S => Option[(A, S)]) extends AbstractIterator[A] { private[this] var state: S = init - private[this] var nextResult: Option[(A, S)] = null + private[this] var nextResult: Option[(A, S)] | Null = null override def hasNext: Boolean = { if (nextResult eq null) { @@ -1298,14 +1302,14 @@ object Iterator extends IterableFactory[Iterator] { if (res eq null) throw new NullPointerException("null during unfold") res } - state = null.asInstanceOf[S] // allow GC + state = nullForGC[S] } - nextResult.isDefined + nextResult.nn.isDefined } override def next(): A = { if (hasNext) { - val (value, newState) = nextResult.get + val (value, newState) = nextResult.nn.get state = newState nextResult = null value diff --git a/library/src/scala/collection/LazyZipOps.scala b/library/src/scala/collection/LazyZipOps.scala index 5849de61448b..7e2e6a5ed66f 100644 --- a/library/src/scala/collection/LazyZipOps.scala +++ b/library/src/scala/collection/LazyZipOps.scala @@ -72,7 +72,7 @@ final class LazyZip2[+El1, +El2, C1] private[collection](src: C1, coll1: Iterabl def iterator: AbstractIterator[(El1, El2)]^{this, p} = new AbstractIterator[(El1, El2)] { private[this] val elems1 = coll1.iterator private[this] val elems2 = coll2.iterator - private[this] var _current: (El1, El2) = _ + private[this] var _current: (El1, El2) | Null = _ private def current = { while ((_current eq null) && elems1.hasNext && elems2.hasNext) { val e1 = elems1.next() @@ -203,7 +203,7 @@ final class LazyZip3[+El1, +El2, +El3, C1] private[collection](src: C1, private[this] val elems1 = coll1.iterator private[this] val elems2 = coll2.iterator private[this] val elems3 = coll3.iterator - private[this] var _current: (El1, El2, El3) = _ + private[this] var _current: (El1, El2, El3) | Null = _ private def current = { while ((_current eq null) && elems1.hasNext && elems2.hasNext && elems3.hasNext) { val e1 = elems1.next() @@ -338,7 +338,7 @@ final class LazyZip4[+El1, +El2, +El3, +El4, C1] private[collection](src: C1, private[this] val elems2 = coll2.iterator private[this] val elems3 = coll3.iterator private[this] val elems4 = coll4.iterator - private[this] var _current: (El1, El2, El3, El4) = _ + private[this] var _current: (El1, El2, El3, El4) | Null = _ private def current = { while ((_current eq null) && elems1.hasNext && elems2.hasNext && elems3.hasNext && elems4.hasNext) { val e1 = elems1.next() diff --git a/library/src/scala/collection/SeqView.scala b/library/src/scala/collection/SeqView.scala index 3928a20aef70..21b771d4e13c 100644 --- a/library/src/scala/collection/SeqView.scala +++ b/library/src/scala/collection/SeqView.scala @@ -18,6 +18,7 @@ import language.experimental.captureChecking import scala.annotation.nowarn import scala.collection.generic.CommonErrors +import scala.runtime.ScalaRunTime.nullForGC trait SeqView[+A] extends SeqOps[A, View, View[A]] with View[A] { @@ -187,13 +188,12 @@ object SeqView { } } evaluated = true - underlying = null + underlying = nullForGC[SomeSeqOps[A]] res } private[this] def elems: SomeSeqOps[A]^{this} = { - val orig: SomeSeqOps[A]^{this} = underlying - if (evaluated) _sorted else orig + if (evaluated) _sorted else underlying } def apply(i: Int): A = _sorted.apply(i) diff --git a/library/src/scala/collection/Stepper.scala b/library/src/scala/collection/Stepper.scala index 7685c2304c7c..d3f8c0d84518 100644 --- a/library/src/scala/collection/Stepper.scala +++ b/library/src/scala/collection/Stepper.scala @@ -54,7 +54,7 @@ trait Stepper[@specialized(Double, Int, Long) +A] { * * See method `trySplit` in [[java.util.Spliterator]]. */ - def trySplit(): Stepper[A]^{this} + def trySplit(): Stepper[A]^{this} | Null /** Returns an estimate of the number of elements of this Stepper, or [[Long.MaxValue]]. See * method `estimateSize` in [[java.util.Spliterator]]. @@ -111,7 +111,7 @@ object Stepper { def nextStep(): Double = st.nextStep() def estimateSize: Long = st.estimateSize def characteristics: Int = st.characteristics - def trySplit(): DoubleStepper^{this} = { + def trySplit(): DoubleStepper^{this} | Null = { val s = st.trySplit() if (s == null) null else new UnboxingDoubleStepper(s) } @@ -122,7 +122,7 @@ object Stepper { def nextStep(): Int = st.nextStep() def estimateSize: Long = st.estimateSize def characteristics: Int = st.characteristics - def trySplit(): IntStepper^{this} = { + def trySplit(): IntStepper^{this} | Null = { val s = st.trySplit() if (s == null) null else new UnboxingIntStepper(s) } @@ -133,7 +133,7 @@ object Stepper { def nextStep(): Long = st.nextStep() def estimateSize: Long = st.estimateSize def characteristics: Int = st.characteristics - def trySplit(): LongStepper^{this} = { + def trySplit(): LongStepper^{this} | Null = { val s = st.trySplit() if (s == null) null else new UnboxingLongStepper(s) } @@ -144,7 +144,7 @@ object Stepper { def nextStep(): Int = st.nextStep() def estimateSize: Long = st.estimateSize def characteristics: Int = st.characteristics - def trySplit(): IntStepper^{this} = { + def trySplit(): IntStepper^{this} | Null = { val s = st.trySplit() if (s == null) null else new UnboxingByteStepper(s) } @@ -155,7 +155,7 @@ object Stepper { def nextStep(): Int = st.nextStep() def estimateSize: Long = st.estimateSize def characteristics: Int = st.characteristics - def trySplit(): IntStepper^{this} = { + def trySplit(): IntStepper^{this} | Null = { val s = st.trySplit() if (s == null) null else new UnboxingCharStepper(s) } @@ -166,7 +166,7 @@ object Stepper { def nextStep(): Int = st.nextStep() def estimateSize: Long = st.estimateSize def characteristics: Int = st.characteristics - def trySplit(): IntStepper^{this} = { + def trySplit(): IntStepper^{this} | Null = { val s = st.trySplit() if (s == null) null else new UnboxingShortStepper(s) } @@ -177,7 +177,7 @@ object Stepper { def nextStep(): Double = st.nextStep() def estimateSize: Long = st.estimateSize def characteristics: Int = st.characteristics - def trySplit(): DoubleStepper^{this} = { + def trySplit(): DoubleStepper^{this} | Null = { val s = st.trySplit() if (s == null) null else new UnboxingFloatStepper(s) } @@ -186,7 +186,7 @@ object Stepper { /** A Stepper for arbitrary element types. See [[Stepper]]. */ trait AnyStepper[+A] extends Stepper[A] { - def trySplit(): AnyStepper[A]^{this} + def trySplit(): AnyStepper[A]^{this} | Null def spliterator[B >: A]: Spliterator[B]^{this} = new AnyStepper.AnyStepperSpliterator(this) @@ -200,7 +200,7 @@ object AnyStepper { class AnyStepperSpliterator[A](s: AnyStepper[A]^) extends Spliterator[A] { def tryAdvance(c: Consumer[_ >: A]): Boolean = if (s.hasStep) { c.accept(s.nextStep()); true } else false - def trySplit(): Spliterator[A]^{this} = { + def trySplit(): Spliterator[A]^{this} | Null = { val sp = s.trySplit() if (sp == null) null else sp.spliterator } @@ -225,7 +225,7 @@ object AnyStepper { def nextStep(): Double = st.nextStep() def estimateSize: Long = st.estimateSize def characteristics: Int = st.characteristics - def trySplit(): AnyStepper[Double] = { + def trySplit(): AnyStepper[Double] | Null = { val s = st.trySplit() if (s == null) null else new BoxedDoubleStepper(s) } @@ -236,7 +236,7 @@ object AnyStepper { def nextStep(): Int = st.nextStep() def estimateSize: Long = st.estimateSize def characteristics: Int = st.characteristics - def trySplit(): AnyStepper[Int] = { + def trySplit(): AnyStepper[Int] | Null = { val s = st.trySplit() if (s == null) null else new BoxedIntStepper(s) } @@ -247,7 +247,7 @@ object AnyStepper { def nextStep(): Long = st.nextStep() def estimateSize: Long = st.estimateSize def characteristics: Int = st.characteristics - def trySplit(): AnyStepper[Long] = { + def trySplit(): AnyStepper[Long] | Null = { val s = st.trySplit() if (s == null) null else new BoxedLongStepper(s) } @@ -256,7 +256,7 @@ object AnyStepper { /** A Stepper for Ints. See [[Stepper]]. */ trait IntStepper extends Stepper[Int] { - def trySplit(): IntStepper^{this} + def trySplit(): IntStepper^{this} | Null def spliterator[B >: Int]: Spliterator.OfInt^{this} = new IntStepper.IntStepperSpliterator(this) @@ -275,7 +275,7 @@ object IntStepper { case _ => if (s.hasStep) { c.accept(jl.Integer.valueOf(s.nextStep())); true } else false } // override required for dotty#6152 - override def trySplit(): Spliterator.OfInt^{this} = { + override def trySplit(): Spliterator.OfInt^{this} | Null = { val sp = s.trySplit() if (sp == null) null else sp.spliterator } @@ -294,7 +294,7 @@ object IntStepper { /** A Stepper for Doubles. See [[Stepper]]. */ trait DoubleStepper extends Stepper[Double] { - def trySplit(): DoubleStepper^{this} + def trySplit(): DoubleStepper^{this} | Null def spliterator[B >: Double]: Spliterator.OfDouble^{this} = new DoubleStepper.DoubleStepperSpliterator(this) @@ -314,7 +314,7 @@ object DoubleStepper { case _ => if (s.hasStep) { c.accept(java.lang.Double.valueOf(s.nextStep())); true } else false } // override required for dotty#6152 - override def trySplit(): Spliterator.OfDouble^{this} = { + override def trySplit(): Spliterator.OfDouble^{this} | Null = { val sp = s.trySplit() if (sp == null) null else sp.spliterator } @@ -333,7 +333,7 @@ object DoubleStepper { /** A Stepper for Longs. See [[Stepper]]. */ trait LongStepper extends Stepper[Long] { - def trySplit(): LongStepper^{this} + def trySplit(): LongStepper^{this} | Null def spliterator[B >: Long]: Spliterator.OfLong^{this} = new LongStepper.LongStepperSpliterator(this) @@ -353,7 +353,7 @@ object LongStepper { case _ => if (s.hasStep) { c.accept(java.lang.Long.valueOf(s.nextStep())); true } else false } // override required for dotty#6152 - override def trySplit(): Spliterator.OfLong^{this} = { + override def trySplit(): Spliterator.OfLong^{this} | Null = { val sp = s.trySplit() if (sp == null) null else sp.spliterator } diff --git a/library/src/scala/collection/StringOps.scala b/library/src/scala/collection/StringOps.scala index 17800cb1d98b..ffc69210d1c0 100644 --- a/library/src/scala/collection/StringOps.scala +++ b/library/src/scala/collection/StringOps.scala @@ -731,14 +731,14 @@ final class StringOps(private val s: String) extends AnyVal { self => /** Returns this string with the given `prefix` stripped. If this string does not * start with `prefix`, it is returned unchanged. */ - def stripPrefix(prefix: String) = + def stripPrefix(prefix: String): String = if (s startsWith prefix) s.substring(prefix.length) else s /** Returns this string with the given `suffix` stripped. If this string does not * end with `suffix`, it is returned unchanged. */ - def stripSuffix(suffix: String) = + def stripSuffix(suffix: String): String = if (s endsWith suffix) s.substring(0, s.length - suffix.length) else s diff --git a/library/src/scala/collection/View.scala b/library/src/scala/collection/View.scala index 78a3f49b1f24..0ea0459664bd 100644 --- a/library/src/scala/collection/View.scala +++ b/library/src/scala/collection/View.scala @@ -18,6 +18,7 @@ import language.experimental.captureChecking import scala.annotation.{nowarn, tailrec} import scala.collection.mutable.{ArrayBuffer, Builder} import scala.collection.immutable.LazyList +import scala.runtime.ScalaRunTime.nullForGC import caps.unsafe.unsafeAssumePure /** Views are collections whose transformation operations are non strict: the resulting elements @@ -452,7 +453,8 @@ object View extends IterableFactory[View] { private final class TakeRightIterator[A](private[this] var underlying: Iterator[A]^, maxlen: Int) extends AbstractIterator[A] { private[this] var len: Int = -1 private[this] var pos: Int = 0 - private[this] var buf: ArrayBuffer[AnyRef] = _ + @annotation.stableNull + private[this] var buf: ArrayBuffer[AnyRef] | Null = _ def init(): Unit = if(buf eq null) { buf = new ArrayBuffer[AnyRef](maxlen min 256) len = 0 @@ -464,7 +466,7 @@ object View extends IterableFactory[View] { if(pos == maxlen) pos = 0 len += 1 } - underlying = null + underlying = nullForGC[Iterator[A]] if(len > maxlen) len = maxlen pos = pos - len if(pos < 0) pos += maxlen @@ -478,7 +480,7 @@ object View extends IterableFactory[View] { init() if(len == 0) Iterator.empty.next() else { - val x = buf(pos).asInstanceOf[A] + val x = buf.nn(pos).asInstanceOf[A] pos += 1 if(pos == maxlen) pos = 0 len -= 1 @@ -507,7 +509,8 @@ object View extends IterableFactory[View] { private final class DropRightIterator[A](private[this] var underlying: Iterator[A]^, maxlen: Int) extends AbstractIterator[A] { private[this] var len: Int = -1 // known size or -1 if the end of `underlying` has not been seen yet private[this] var pos: Int = 0 - private[this] var buf: ArrayBuffer[AnyRef] = _ + @annotation.stableNull + private[this] var buf: ArrayBuffer[AnyRef] | Null = _ def init(): Unit = if(buf eq null) { buf = new ArrayBuffer[AnyRef](maxlen min 256) while(pos < maxlen && underlying.hasNext) { @@ -525,9 +528,9 @@ object View extends IterableFactory[View] { def next(): A = { if(!hasNext) Iterator.empty.next() else { - val x = buf(pos).asInstanceOf[A] + val x = buf.nn(pos).asInstanceOf[A] if(len == -1) { - buf(pos) = underlying.next().asInstanceOf[AnyRef] + buf.nn(pos) = underlying.next().asInstanceOf[AnyRef] if(!underlying.hasNext) len = 0 } else len -= 1 pos += 1 diff --git a/library/src/scala/collection/concurrent/TrieMap.scala b/library/src/scala/collection/concurrent/TrieMap.scala index a29eb902afb4..875ba45c9f49 100644 --- a/library/src/scala/collection/concurrent/TrieMap.scala +++ b/library/src/scala/collection/concurrent/TrieMap.scala @@ -27,29 +27,29 @@ import scala.collection.mutable.GrowableBuilder import scala.util.Try import scala.util.hashing.Hashing -private[collection] final class INode[K, V](bn: MainNode[K, V], g: Gen, equiv: Equiv[K]) extends INodeBase[K, V](g) { +private[collection] final class INode[K, V](bn: MainNode[K, V] | Null, g: Gen, equiv: Equiv[K]) extends INodeBase[K, V](g) { import INodeBase._ WRITE(bn) def this(g: Gen, equiv: Equiv[K]) = this(null, g, equiv) - def WRITE(nval: MainNode[K, V]) = INodeBase.updater.set(this, nval) + def WRITE(nval: MainNode[K, V] | Null) = INodeBase.updater.set(this, nval) def CAS(old: MainNode[K, V], n: MainNode[K, V]) = INodeBase.updater.compareAndSet(this, old, n) - def gcasRead(ct: TrieMap[K, V]): MainNode[K, V] = GCAS_READ(ct) + def gcasRead(ct: TrieMap[K, V]): MainNode[K, V] | Null = GCAS_READ(ct) - def GCAS_READ(ct: TrieMap[K, V]): MainNode[K, V] = { + def GCAS_READ(ct: TrieMap[K, V]): MainNode[K, V] | Null = { val m = /*READ*/mainnode - val prevval = /*READ*/m.prev + val prevval: MainNode[K, V] | Null = /*READ*/m.prev if (prevval eq null) m else GCAS_Complete(m, ct) } - @tailrec private def GCAS_Complete(m: MainNode[K, V], ct: TrieMap[K, V]): MainNode[K, V] = if (m eq null) null else { + @tailrec private def GCAS_Complete(m: MainNode[K, V] | Null, ct: TrieMap[K, V]): MainNode[K, V] | Null = if (m eq null) null else { // complete the GCAS - val prev = /*READ*/m.prev + val prev: MainNode[K, V] | Null = /*READ*/m.prev val ctr = ct.readRoot(abort = true) prev match { @@ -73,7 +73,8 @@ private[collection] final class INode[K, V](bn: MainNode[K, V], g: Gen, equiv: E else GCAS_Complete(m, ct) } else { // try to abort - m.CAS_PREV(prev, new FailedNode(prev)) + // we know `vn eq prev` and `prev ne null` + m.CAS_PREV(prev, new FailedNode(vn)) GCAS_Complete(/*READ*/mainnode, ct) } } @@ -106,7 +107,7 @@ private[collection] final class INode[K, V](bn: MainNode[K, V], g: Gen, equiv: E * * @return true if successful, false otherwise */ - @tailrec def rec_insert(k: K, v: V, hc: Int, lev: Int, parent: INode[K, V], startgen: Gen, ct: TrieMap[K, V]): Boolean = { + @tailrec def rec_insert(k: K, v: V, hc: Int, lev: Int, parent: INode[K, V] | Null, startgen: Gen, ct: TrieMap[K, V]): Boolean = { val m = GCAS_READ(ct) // use -Yinline! m match { @@ -140,7 +141,7 @@ private[collection] final class INode[K, V](bn: MainNode[K, V], g: Gen, equiv: E GCAS(cn, ncnode, ct) } case tn: TNode[K, V] => - clean(parent, ct, lev - 5) + clean(parent.nn, ct, lev - 5) false case ln: LNode[K, V] => // 3) an l-node val nn = ln.inserted(k, v) @@ -162,7 +163,7 @@ private[collection] final class INode[K, V](bn: MainNode[K, V], g: Gen, equiv: E * * @return null if unsuccessful, Option[V] otherwise (indicating previous value bound to the key) */ - @tailrec def rec_insertif(k: K, v: V, hc: Int, cond: AnyRef, fullEquals: Boolean, lev: Int, parent: INode[K, V], startgen: Gen, ct: TrieMap[K, V]): Option[V] = { + @tailrec def rec_insertif(k: K, v: V, hc: Int, cond: AnyRef, fullEquals: Boolean, lev: Int, parent: INode[K, V] | Null, startgen: Gen, ct: TrieMap[K, V]): Option[V] | Null = { val m = GCAS_READ(ct) // use -Yinline! m match { @@ -219,7 +220,7 @@ private[collection] final class INode[K, V](bn: MainNode[K, V], g: Gen, equiv: E case otherv => None } case sn: TNode[K, V] => - clean(parent, ct, lev - 5) + clean(parent.nn, ct, lev - 5) null case ln: LNode[K, V] => // 3) an l-node def insertln() = { @@ -258,7 +259,7 @@ private[collection] final class INode[K, V](bn: MainNode[K, V], g: Gen, equiv: E * @return NO_SUCH_ELEMENT_SENTINEL if no value has been found, RESTART if the operation wasn't successful, * or any other value otherwise */ - @tailrec def rec_lookup(k: K, hc: Int, lev: Int, parent: INode[K, V], startgen: Gen, ct: TrieMap[K, V]): AnyRef = { + @tailrec def rec_lookup(k: K, hc: Int, lev: Int, parent: INode[K, V] | Null, startgen: Gen, ct: TrieMap[K, V]): AnyRef = { val m = GCAS_READ(ct) // use -Yinline! m match { @@ -285,7 +286,7 @@ private[collection] final class INode[K, V](bn: MainNode[K, V], g: Gen, equiv: E } case tn: TNode[_, _] => // 3) non-live node def cleanReadOnly(tn: TNode[K, V]) = if (ct.nonReadOnly) { - clean(parent, ct, lev - 5) + clean(parent.nn, ct, lev - 5) RESTART } else { if (tn.hc == hc && tn.k == k) tn.v.asInstanceOf[AnyRef] @@ -313,9 +314,9 @@ private[collection] final class INode[K, V](bn: MainNode[K, V], g: Gen, equiv: E removalPolicy: Int, hc: Int, lev: Int, - parent: INode[K, V], + parent: INode[K, V] | Null, startgen: Gen, - ct: TrieMap[K, V]): Option[V] = { + ct: TrieMap[K, V]): Option[V] | Null = { GCAS_READ(ct) match { case cn: CNode[K, V] => @@ -343,8 +344,8 @@ private[collection] final class INode[K, V](bn: MainNode[K, V], g: Gen, equiv: E if (res == None || (res eq null)) res else { - @tailrec def cleanParent(nonlive: AnyRef): Unit = { - val cn = parent.GCAS_READ(ct) + @tailrec def cleanParent(nonlive: AnyRef | Null): Unit = { + val cn = parent.nn.GCAS_READ(ct) cn match { case cn: CNode[K, V] => val idx = (hc >>> (lev - 5)) & 0x1f @@ -357,7 +358,7 @@ private[collection] final class INode[K, V](bn: MainNode[K, V], g: Gen, equiv: E if (sub eq this) (nonlive: @uc) match { case tn: TNode[K, V] @uc => val ncn = cn.updatedAt(pos, tn.copyUntombed, gen).toContracted(lev - 5) - if (!parent.GCAS(cn, ncn, ct)) + if (!parent.nn.GCAS(cn, ncn, ct)) if (ct.readRoot().gen == startgen) cleanParent(nonlive) } } @@ -375,7 +376,7 @@ private[collection] final class INode[K, V](bn: MainNode[K, V], g: Gen, equiv: E } } case tn: TNode[K, V] => - clean(parent, ct, lev - 5) + clean(parent.nn, ct, lev - 5) null case ln: LNode[K, V] => if (removalPolicy == RemovalPolicy.Always) { @@ -403,13 +404,13 @@ private[collection] final class INode[K, V](bn: MainNode[K, V], g: Gen, equiv: E def isNullInode(ct: TrieMap[K, V]) = GCAS_READ(ct) eq null def cachedSize(ct: TrieMap[K, V]): Int = - GCAS_READ(ct).cachedSize(ct) + GCAS_READ(ct).nn.cachedSize(ct) def knownSize(ct: TrieMap[K, V]): Int = - GCAS_READ(ct).knownSize() + GCAS_READ(ct).nn.knownSize() /* this is a quiescent method! */ - def string(lev: Int) = "%sINode -> %s".format(" " * lev, mainnode match { + def string(lev: Int): String = "%sINode -> %s".format(" " * lev, mainnode match { case null => "" case tn: TNode[_, _] => "TNode(%s, %s, %d, !)".format(tn.k, tn.v, tn.hc) case cn: CNode[_, _] => cn.string(lev) @@ -445,7 +446,7 @@ private[concurrent] final class FailedNode[K, V](p: MainNode[K, V]) extends Main def knownSize: Int = throw new UnsupportedOperationException - override def toString = "FailedNode(%s)".format(p) + override def toString: String = "FailedNode(%s)".format(p) } @@ -460,7 +461,7 @@ private[collection] final class SNode[K, V](final val k: K, final val v: V, fina def copyTombed = new TNode(k, v, hc) def copyUntombed = new SNode(k, v, hc) def kvPair = (k, v) - def string(lev: Int) = (" " * lev) + "SNode(%s, %s, %x)".format(k, v, hc) + def string(lev: Int): String = (" " * lev) + "SNode(%s, %s, %x)".format(k, v, hc) } // Tomb Node, used to ensure proper ordering during removals @@ -472,7 +473,7 @@ private[collection] final class TNode[K, V](final val k: K, final val v: V, fina def kvPair = (k, v) def cachedSize(ct: AnyRef): Int = 1 def knownSize: Int = 1 - def string(lev: Int) = (" " * lev) + "TNode(%s, %s, %x, !)".format(k, v, hc) + def string(lev: Int): String = (" " * lev) + "TNode(%s, %s, %x, !)".format(k, v, hc) } // List Node, leaf node that handles hash collisions @@ -513,7 +514,7 @@ private[collection] final class LNode[K, V](val entries: List[(K, V)], equiv: Eq def knownSize: Int = -1 // shouldn't ever be empty, and the size of a list is not known - def string(lev: Int) = (" " * lev) + "LNode(%s)".format(entries.mkString(", ")) + def string(lev: Int): String = (" " * lev) + "LNode(%s)".format(entries.mkString(", ")) } @@ -673,7 +674,7 @@ private[concurrent] object CNode { } -private[concurrent] case class RDCSS_Descriptor[K, V](old: INode[K, V], expectedmain: MainNode[K, V], nv: INode[K, V]) { +private[concurrent] case class RDCSS_Descriptor[K, V](old: INode[K, V], expectedmain: MainNode[K, V] | Null, nv: INode[K, V]) { @volatile var committed = false } @@ -689,7 +690,7 @@ private[concurrent] case class RDCSS_Descriptor[K, V](old: INode[K, V], expected * For details, see: [[http://lampwww.epfl.ch/~prokopec/ctries-snapshot.pdf]] */ @SerialVersionUID(-5212455458703321708L) -final class TrieMap[K, V] private (r: AnyRef, rtupd: AtomicReferenceFieldUpdater[TrieMap[K, V], AnyRef], hashf: Hashing[K], ef: Equiv[K]) +final class TrieMap[K, V] private (r: AnyRef, rtupd: AtomicReferenceFieldUpdater[TrieMap[K, V], AnyRef] | Null, hashf: Hashing[K], ef: Equiv[K]) extends scala.collection.mutable.AbstractMap[K, V] with scala.collection.concurrent.Map[K, V] with scala.collection.mutable.MapOps[K, V, TrieMap, TrieMap[K, V]] @@ -699,7 +700,7 @@ final class TrieMap[K, V] private (r: AnyRef, rtupd: AtomicReferenceFieldUpdater private[this] var hashingobj = if (hashf.isInstanceOf[Hashing.Default[_]]) new TrieMap.MangledHashing[K] else hashf private[this] var equalityobj = ef @transient - private[this] var rootupdater = rtupd + private[this] var rootupdater: AtomicReferenceFieldUpdater[TrieMap[K, V], AnyRef] | Null = rtupd def hashing = hashingobj def equality = equalityobj @volatile private var root = r @@ -749,7 +750,7 @@ final class TrieMap[K, V] private (r: AnyRef, rtupd: AtomicReferenceFieldUpdater } } - private def CAS_ROOT(ov: AnyRef, nv: AnyRef) = rootupdater.compareAndSet(this, ov, nv) + private def CAS_ROOT(ov: AnyRef, nv: AnyRef) = rootupdater.nn.compareAndSet(this, ov, nv) private[collection] def readRoot(abort: Boolean = false): INode[K, V] = RDCSS_READ_ROOT(abort) @@ -787,7 +788,7 @@ final class TrieMap[K, V] private (r: AnyRef, rtupd: AtomicReferenceFieldUpdater } } - private def RDCSS_ROOT(ov: INode[K, V], expectedmain: MainNode[K, V], nv: INode[K, V]): Boolean = { + private def RDCSS_ROOT(ov: INode[K, V], expectedmain: MainNode[K, V] | Null, nv: INode[K, V]): Boolean = { val desc = RDCSS_Descriptor(ov, expectedmain, nv) if (CAS_ROOT(ov, desc)) { RDCSS_Complete(abort = false) @@ -838,7 +839,7 @@ final class TrieMap[K, V] private (r: AnyRef, rtupd: AtomicReferenceFieldUpdater } - def string = RDCSS_READ_ROOT().string(0) + def string: String = RDCSS_READ_ROOT().string(0) /* public methods */ @@ -1074,24 +1075,24 @@ private[collection] class TrieMapIterator[K, V](var level: Int, private var ct: private val stack = new Array[Array[BasicNode]](7) private val stackpos = new Array[Int](7) private var depth = -1 - private var subiter: Iterator[(K, V)] = null - private var current: KVNode[K, V] = null + @annotation.stableNull private var subiter: Iterator[(K, V)] | Null = null + @annotation.stableNull private var current: KVNode[K, V] | Null = null if (mustInit) initialize() def hasNext = (current ne null) || (subiter ne null) - def next() = if (hasNext) { - var r: (K, V) = null + def next(): (K, V) = { if (subiter ne null) { - r = subiter.next() + val r = subiter.next() checkSubiter() - } else { - r = current.kvPair + r + } else if (current ne null) { + val r = current.kvPair advance() - } - r - } else Iterator.empty.next() + r + } else Iterator.empty.next() + } private def readin(in: INode[K, V]) = in.gcasRead(ct) match { case cn: CNode[K, V] => @@ -1109,7 +1110,7 @@ private[collection] class TrieMapIterator[K, V](var level: Int, private var ct: case mainNode => throw new MatchError(mainNode) } - private def checkSubiter() = if (!subiter.hasNext) { + private def checkSubiter() = if (!subiter.nn.hasNext) { subiter = null advance() } diff --git a/library/src/scala/collection/convert/AsJavaConverters.scala b/library/src/scala/collection/convert/AsJavaConverters.scala index 079847776368..07775f678fdd 100644 --- a/library/src/scala/collection/convert/AsJavaConverters.scala +++ b/library/src/scala/collection/convert/AsJavaConverters.scala @@ -28,6 +28,11 @@ import scala.{unchecked => uc} trait AsJavaConverters { import JavaCollectionWrappers._ + // Note: Both the parameter and the return type of the methods in this class are non-nullable. + // However, if a null reference is passed explicitly, the method will still return null. + // We intentionally keep this signature to discourage passing nulls implicitly while preserving the + // previous behavior for backward compatibility. + /** * Converts a Scala `Iterator` to a Java `Iterator`. * @@ -40,8 +45,8 @@ trait AsJavaConverters { * @param i The Scala `Iterator` to be converted. * @return A Java `Iterator` view of the argument. */ - def asJava[A](i: Iterator[A]): ju.Iterator[A] = i match { - case null => null + def asJava[A](i: Iterator[A]): ju.Iterator[A] = (i: Iterator[A] | Null) match { + case null => null.asInstanceOf[ju.Iterator[A]] case wrapper: JIteratorWrapper[A @uc] => wrapper.underlying case _ => new IteratorWrapper(i) } @@ -58,8 +63,8 @@ trait AsJavaConverters { * @param i The Scala `Iterator` to be converted. * @return A Java `Enumeration` view of the argument. */ - def asJavaEnumeration[A](i: Iterator[A]): ju.Enumeration[A] = i match { - case null => null + def asJavaEnumeration[A](i: Iterator[A]): ju.Enumeration[A] = (i: Iterator[A] | Null) match { + case null => null.asInstanceOf[ju.Enumeration[A]] case wrapper: JEnumerationWrapper[A @uc] => wrapper.underlying case _ => new IteratorWrapper(i) } @@ -76,8 +81,8 @@ trait AsJavaConverters { * @param i The Scala `Iterable` to be converted. * @return A Java `Iterable` view of the argument. */ - def asJava[A](i: Iterable[A]): jl.Iterable[A] = i match { - case null => null + def asJava[A](i: Iterable[A]): jl.Iterable[A] = (i: Iterable[A] | Null) match { + case null => null.asInstanceOf[jl.Iterable[A]] case wrapper: JIterableWrapper[A @uc] => wrapper.underlying case _ => new IterableWrapper(i) } @@ -91,8 +96,8 @@ trait AsJavaConverters { * @param i The Scala `Iterable` to be converted. * @return A Java `Collection` view of the argument. */ - def asJavaCollection[A](i: Iterable[A]): ju.Collection[A] = i match { - case null => null + def asJavaCollection[A](i: Iterable[A]): ju.Collection[A] = (i: Iterable[A] | Null) match { + case null => null.asInstanceOf[ju.Collection[A]] case wrapper: JCollectionWrapper[A @uc] => wrapper.underlying case _ => new IterableWrapper(i) } @@ -109,8 +114,8 @@ trait AsJavaConverters { * @param b The Scala `Buffer` to be converted. * @return A Java `List` view of the argument. */ - def asJava[A](b: mutable.Buffer[A]): ju.List[A] = b match { - case null => null + def asJava[A](b: mutable.Buffer[A]): ju.List[A] = (b: mutable.Buffer[A] | Null) match { + case null => null.asInstanceOf[ju.List[A]] case wrapper: JListWrapper[A @uc] => wrapper.underlying case _ => new MutableBufferWrapper(b) } @@ -127,8 +132,8 @@ trait AsJavaConverters { * @param s The Scala `Seq` to be converted. * @return A Java `List` view of the argument. */ - def asJava[A](s: mutable.Seq[A]): ju.List[A] = s match { - case null => null + def asJava[A](s: mutable.Seq[A]): ju.List[A] = (s: mutable.Seq[A] | Null) match { + case null => null.asInstanceOf[ju.List[A]] case wrapper: JListWrapper[A @uc] => wrapper.underlying case _ => new MutableSeqWrapper(s) } @@ -145,8 +150,8 @@ trait AsJavaConverters { * @param s The Scala `Seq` to be converted. * @return A Java `List` view of the argument. */ - def asJava[A](s: Seq[A]): ju.List[A] = s match { - case null => null + def asJava[A](s: Seq[A]): ju.List[A] = (s: Seq[A] | Null) match { + case null => null.asInstanceOf[ju.List[A]] case wrapper: JListWrapper[A @uc] => wrapper.underlying case _ => new SeqWrapper(s) } @@ -163,8 +168,8 @@ trait AsJavaConverters { * @param s The Scala mutable `Set` to be converted. * @return A Java `Set` view of the argument. */ - def asJava[A](s: mutable.Set[A]): ju.Set[A] = s match { - case null => null + def asJava[A](s: mutable.Set[A]): ju.Set[A] = (s: mutable.Set[A] | Null) match { + case null => null.asInstanceOf[ju.Set[A]] case wrapper: JSetWrapper[A @uc] => wrapper.underlying case _ => new MutableSetWrapper(s) } @@ -181,8 +186,8 @@ trait AsJavaConverters { * @param s The Scala `Set` to be converted. * @return A Java `Set` view of the argument. */ - def asJava[A](s: Set[A]): ju.Set[A] = s match { - case null => null + def asJava[A](s: Set[A]): ju.Set[A] = (s: Set[A] | Null) match { + case null => null.asInstanceOf[ju.Set[A]] case wrapper: JSetWrapper[A @uc] => wrapper.underlying case _ => new SetWrapper(s) } @@ -199,8 +204,8 @@ trait AsJavaConverters { * @param m The Scala mutable `Map` to be converted. * @return A Java `Map` view of the argument. */ - def asJava[K, V](m: mutable.Map[K, V]): ju.Map[K, V] = m match { - case null => null + def asJava[K, V](m: mutable.Map[K, V]): ju.Map[K, V] = (m: mutable.Map[K, V] | Null) match { + case null => null.asInstanceOf[ju.Map[K, V]] case wrapper: JMapWrapper[K @uc, V @uc] => wrapper.underlying case _ => new MutableMapWrapper(m) } @@ -218,8 +223,8 @@ trait AsJavaConverters { * @param m The Scala `Map` to be converted. * @return A Java `Dictionary` view of the argument. */ - def asJavaDictionary[K, V](m: mutable.Map[K, V]): ju.Dictionary[K, V] = m match { - case null => null + def asJavaDictionary[K, V](m: mutable.Map[K, V]): ju.Dictionary[K, V] = (m: mutable.Map[K, V] | Null) match { + case null => null.asInstanceOf[ju.Dictionary[K, V]] case wrapper: JDictionaryWrapper[K @uc, V @uc] => wrapper.underlying case _ => new DictionaryWrapper(m) } @@ -236,8 +241,8 @@ trait AsJavaConverters { * @param m The Scala `Map` to be converted. * @return A Java `Map` view of the argument. */ - def asJava[K, V](m: Map[K, V]): ju.Map[K, V] = m match { - case null => null + def asJava[K, V](m: Map[K, V]): ju.Map[K, V] = (m: Map[K, V] | Null) match { + case null => null.asInstanceOf[ju.Map[K, V]] case wrapper: JMapWrapper[K @uc, V @uc] => wrapper.underlying case _ => new MapWrapper(m) } @@ -255,8 +260,8 @@ trait AsJavaConverters { * @param m The Scala `concurrent.Map` to be converted. * @return A Java `ConcurrentMap` view of the argument. */ - def asJava[K, V](m: concurrent.Map[K, V]): juc.ConcurrentMap[K, V] = m match { - case null => null + def asJava[K, V](m: concurrent.Map[K, V]): juc.ConcurrentMap[K, V] = (m: concurrent.Map[K, V] | Null) match { + case null => null.asInstanceOf[juc.ConcurrentMap[K, V]] case wrapper: JConcurrentMapWrapper[K @uc, V @uc] => wrapper.underlying case _ => new ConcurrentMapWrapper(m) } diff --git a/library/src/scala/collection/convert/AsScalaConverters.scala b/library/src/scala/collection/convert/AsScalaConverters.scala index e2fa0f39bfef..fa39ff1d8d37 100644 --- a/library/src/scala/collection/convert/AsScalaConverters.scala +++ b/library/src/scala/collection/convert/AsScalaConverters.scala @@ -29,6 +29,11 @@ import scala.{unchecked => uc} trait AsScalaConverters { import JavaCollectionWrappers._ + // Note: Both the parameter and the return type of the methods in this class are non-nullable. + // However, if a null reference is passed explicitly, the method will still return null. + // We intentionally keep this signature to discourage passing nulls implicitly while preserving the + // previous behavior for backward compatibility. + /** * Converts a Java `Iterator` to a Scala `Iterator`. * @@ -41,8 +46,8 @@ trait AsScalaConverters { * @param i The Java `Iterator` to be converted. * @return A Scala `Iterator` view of the argument. */ - def asScala[A](i: ju.Iterator[A]): Iterator[A] = i match { - case null => null + def asScala[A](i: ju.Iterator[A]): Iterator[A] = (i: ju.Iterator[A] | Null) match { + case null => null.asInstanceOf[Iterator[A]] case wrapper: IteratorWrapper[A @uc] => wrapper.underlying.unsafeAssumePure // TODO: Remove when pattern matching recognizes this is safe case _ => new JIteratorWrapper(i) } @@ -59,8 +64,8 @@ trait AsScalaConverters { * @param e The Java `Enumeration` to be converted. * @return A Scala `Iterator` view of the argument. */ - def asScala[A](e: ju.Enumeration[A]): Iterator[A] = e match { - case null => null + def asScala[A](e: ju.Enumeration[A]): Iterator[A] = (e: ju.Enumeration[A] | Null) match { + case null => null.asInstanceOf[Iterator[A]] case wrapper: IteratorWrapper[A @uc] => wrapper.underlying.unsafeAssumePure // TODO: Remove when pattern matching recognizes this is safe case _ => new JEnumerationWrapper(e) } @@ -77,8 +82,8 @@ trait AsScalaConverters { * @param i The Java `Iterable` to be converted. * @return A Scala `Iterable` view of the argument. */ - def asScala[A](i: jl.Iterable[A]): Iterable[A] = i match { - case null => null + def asScala[A](i: jl.Iterable[A]): Iterable[A] = (i: jl.Iterable[A] | Null) match { + case null => null.asInstanceOf[Iterable[A]] case wrapper: IterableWrapper[A @uc] => wrapper.underlying.unsafeAssumePure // TODO: Remove when pattern matching recognizes this is safe case _ => new JIterableWrapper(i) } @@ -92,8 +97,8 @@ trait AsScalaConverters { * @param c The Java `Collection` to be converted. * @return A Scala `Iterable` view of the argument. */ - def asScala[A](c: ju.Collection[A]): Iterable[A] = c match { - case null => null + def asScala[A](c: ju.Collection[A]): Iterable[A] = (c: ju.Collection[A] | Null) match { + case null => null.asInstanceOf[Iterable[A]] case wrapper: IterableWrapper[A @uc] => wrapper.underlying.unsafeAssumePure // TODO: Remove when pattern matching recognizes this is safe case _ => new JCollectionWrapper(c) } @@ -110,8 +115,8 @@ trait AsScalaConverters { * @param l The Java `List` to be converted. * @return A Scala mutable `Buffer` view of the argument. */ - def asScala[A](l: ju.List[A]): mutable.Buffer[A] = l match { - case null => null + def asScala[A](l: ju.List[A]): mutable.Buffer[A] = (l: ju.List[A] | Null) match { + case null => null.asInstanceOf[mutable.Buffer[A]] case wrapper: MutableBufferWrapper[A @uc] => wrapper.underlying case _ => new JListWrapper(l) } @@ -128,8 +133,8 @@ trait AsScalaConverters { * @param s The Java `Set` to be converted. * @return A Scala mutable `Set` view of the argument. */ - def asScala[A](s: ju.Set[A]): mutable.Set[A] = s match { - case null => null + def asScala[A](s: ju.Set[A]): mutable.Set[A] = (s: ju.Set[A] | Null) match { + case null => null.asInstanceOf[mutable.Set[A]] case wrapper: MutableSetWrapper[A @uc] => wrapper.underlying case _ => new JSetWrapper(s) } @@ -151,8 +156,8 @@ trait AsScalaConverters { * @param m The Java `Map` to be converted. * @return A Scala mutable `Map` view of the argument. */ - def asScala[K, V](m: ju.Map[K, V]): mutable.Map[K, V] = m match { - case null => null + def asScala[K, V](m: ju.Map[K, V]): mutable.Map[K, V] = (m: ju.Map[K, V] | Null) match { + case null => null.asInstanceOf[mutable.Map[K, V]] case wrapper: MutableMapWrapper[K @uc, V @uc] => wrapper.underlying case _ => new JMapWrapper(m) } @@ -170,8 +175,8 @@ trait AsScalaConverters { * @param m The Java `ConcurrentMap` to be converted. * @return A Scala mutable `ConcurrentMap` view of the argument. */ - def asScala[K, V](m: juc.ConcurrentMap[K, V]): concurrent.Map[K, V] = m match { - case null => null + def asScala[K, V](m: juc.ConcurrentMap[K, V]): concurrent.Map[K, V] = (m: juc.ConcurrentMap[K, V] | Null) match { + case null => null.asInstanceOf[concurrent.Map[K, V]] case wrapper: ConcurrentMapWrapper[K @uc, V @uc] => wrapper.underlyingConcurrentMap case _ => new JConcurrentMapWrapper(m) } @@ -188,8 +193,8 @@ trait AsScalaConverters { * @param d The Java `Dictionary` to be converted. * @return A Scala mutable `Map` view of the argument. */ - def asScala[K, V](d: ju.Dictionary[K, V]): mutable.Map[K, V] = d match { - case null => null + def asScala[K, V](d: ju.Dictionary[K, V]): mutable.Map[K, V] = (d: ju.Dictionary[K, V] | Null) match { + case null => null.asInstanceOf[mutable.Map[K, V]] case wrapper: DictionaryWrapper[K @uc, V @uc] => wrapper.underlying case _ => new JDictionaryWrapper(d) } @@ -204,8 +209,8 @@ trait AsScalaConverters { * @param p The Java `Properties` to be converted. * @return A Scala mutable `Map[String, String]` view of the argument. */ - def asScala(p: ju.Properties): mutable.Map[String, String] = p match { - case null => null + def asScala(p: ju.Properties): mutable.Map[String, String] = (p: ju.Properties | Null) match { + case null => null.asInstanceOf[mutable.Map[String, String]] case _ => new JPropertiesWrapper(p) } } diff --git a/library/src/scala/collection/convert/JavaCollectionWrappers.scala b/library/src/scala/collection/convert/JavaCollectionWrappers.scala index 5058f93fc48f..d131a185c736 100644 --- a/library/src/scala/collection/convert/JavaCollectionWrappers.scala +++ b/library/src/scala/collection/convert/JavaCollectionWrappers.scala @@ -33,9 +33,9 @@ private[collection] object JavaCollectionWrappers extends Serializable { @SerialVersionUID(3L) class IteratorWrapper[A](val underlying: Iterator[A]^) extends ju.Iterator[A] with ju.Enumeration[A] with Serializable { def hasNext = underlying.hasNext - def next() = underlying.next() + def next(): A = underlying.next() def hasMoreElements = underlying.hasNext - def nextElement() = underlying.next() + def nextElement(): A = underlying.next() override def remove(): Nothing = throw new UnsupportedOperationException override def equals(other: Any): Boolean = other match { case that: IteratorWrapper[_] => this.underlying == that.underlying @@ -47,7 +47,7 @@ private[collection] object JavaCollectionWrappers extends Serializable { @SerialVersionUID(3L) class JIteratorWrapper[A](val underlying: ju.Iterator[A]) extends AbstractIterator[A] with Serializable { def hasNext = underlying.hasNext - def next() = underlying.next + def next(): A = underlying.next override def equals(other: Any): Boolean = other match { case that: JIteratorWrapper[_] => this.underlying == that.underlying case _ => false @@ -58,7 +58,7 @@ private[collection] object JavaCollectionWrappers extends Serializable { @SerialVersionUID(3L) class JEnumerationWrapper[A](val underlying: ju.Enumeration[A]) extends AbstractIterator[A] with Serializable { def hasNext = underlying.hasMoreElements - def next() = underlying.nextElement + def next(): A = underlying.nextElement override def equals(other: Any): Boolean = other match { case that: JEnumerationWrapper[_] => this.underlying == that.underlying case _ => false @@ -102,7 +102,7 @@ private[collection] object JavaCollectionWrappers extends Serializable { extends AbstractIterable[A] with StrictOptimizedIterableOps[A, Iterable, Iterable[A]] with Serializable { - def iterator = underlying.iterator.asScala + def iterator: Iterator[A] = underlying.iterator.asScala override def size = underlying.size override def knownSize: Int = if (underlying.isEmpty) 0 else super.knownSize override def isEmpty = underlying.isEmpty @@ -116,13 +116,13 @@ private[collection] object JavaCollectionWrappers extends Serializable { @SerialVersionUID(3L) class SeqWrapper[A](val underlying: Seq[A]) extends ju.AbstractList[A] with IterableWrapperTrait[A] with Serializable { - def get(i: Int) = underlying(i) + def get(i: Int): A = underlying(i) } @SerialVersionUID(3L) class MutableSeqWrapper[A](val underlying: mutable.Seq[A]) extends ju.AbstractList[A] with IterableWrapperTrait[A] with Serializable { - def get(i: Int) = underlying(i) - override def set(i: Int, elem: A) = { + def get(i: Int): A = underlying(i) + override def set(i: Int, elem: A): A = { val p = underlying(i) underlying(i) = elem p @@ -131,10 +131,10 @@ private[collection] object JavaCollectionWrappers extends Serializable { @SerialVersionUID(3L) class MutableBufferWrapper[A](val underlying: mutable.Buffer[A]) extends ju.AbstractList[A] with IterableWrapperTrait[A] with Serializable { - def get(i: Int) = underlying(i) - override def set(i: Int, elem: A) = { val p = underlying(i); underlying(i) = elem; p } + def get(i: Int): A = underlying(i) + override def set(i: Int, elem: A): A = { val p = underlying(i); underlying(i) = elem; p } override def add(elem: A) = { underlying += elem; true } - override def remove(i: Int) = underlying remove i + override def remove(i: Int): A = underlying remove i } @SerialVersionUID(3L) @@ -148,7 +148,7 @@ private[collection] object JavaCollectionWrappers extends Serializable { override def knownSize: Int = if (underlying.isEmpty) 0 else super.knownSize override def isEmpty = underlying.isEmpty override def iterator: Iterator[A] = underlying.iterator.asScala - def apply(i: Int) = underlying.get(i) + def apply(i: Int): A = underlying.get(i) def update(i: Int, elem: A) = underlying.set(i, elem) def prepend(elem: A) = { underlying.subList(0, 0) add elem; this } def addOne(elem: A): this.type = { underlying add elem; this } @@ -185,7 +185,7 @@ private[collection] object JavaCollectionWrappers extends Serializable { val ui = underlying.iterator var prev: Option[A] = None def hasNext = ui.hasNext - def next = { val e = ui.next(); prev = Some(e); e } + def next: A = { val e = ui.next(); prev = Some(e); e } override def remove() = prev match { case Some(e) => underlying match { @@ -326,7 +326,7 @@ private[collection] object JavaCollectionWrappers extends Serializable { @SerialVersionUID(3L) class MutableMapWrapper[K, V](val underlying: mutable.Map[K, V]) extends MapWrapper[K, V](underlying) { - override def put(k: K, v: V) = underlying.put(k, v) match { + override def put(k: K, v: V): V = underlying.put(k, v) match { case Some(v1) => v1 case None => null.asInstanceOf[V] } @@ -459,7 +459,7 @@ private[collection] object JavaCollectionWrappers extends Serializable { def underlyingConcurrentMap: concurrent.Map[K, V] = underlying - override def putIfAbsent(k: K, v: V) = underlying.putIfAbsent(k, v).getOrElse(null.asInstanceOf[V]) + override def putIfAbsent(k: K, v: V): V = underlying.putIfAbsent(k, v).getOrElse(null.asInstanceOf[V]) override def remove(k: AnyRef, v: AnyRef) = try underlying.remove(k.asInstanceOf[K], v.asInstanceOf[V]) @@ -526,7 +526,7 @@ private[collection] object JavaCollectionWrappers extends Serializable { def isEmpty: Boolean = underlying.isEmpty def keys: ju.Enumeration[K] = underlying.keysIterator.asJavaEnumeration def elements: ju.Enumeration[V] = underlying.valuesIterator.asJavaEnumeration - def get(key: AnyRef) = try { + def get(key: AnyRef): V = try { underlying get key.asInstanceOf[K] match { case None => null.asInstanceOf[V] case Some(v) => v @@ -538,7 +538,7 @@ private[collection] object JavaCollectionWrappers extends Serializable { case Some(v) => v case None => null.asInstanceOf[V] } - override def remove(key: AnyRef) = try { + override def remove(key: AnyRef): V = try { underlying remove key.asInstanceOf[K] match { case None => null.asInstanceOf[V] case Some(v) => v @@ -622,12 +622,11 @@ private[collection] object JavaCollectionWrappers extends Serializable { override def empty: JPropertiesWrapper = new JPropertiesWrapper(new ju.Properties) - def getProperty(key: String) = underlying.getProperty(key) + def getProperty(key: String): String | Null = underlying.getProperty(key) - def getProperty(key: String, defaultValue: String) = - underlying.getProperty(key, defaultValue) + def getProperty(key: String, defaultValue: String): String = underlying.getProperty(key, defaultValue) - def setProperty(key: String, value: String) = + def setProperty(key: String, value: String): AnyRef | Null = underlying.setProperty(key, value) override def mapFactory: mutable.HashMap.type = mutable.HashMap diff --git a/library/src/scala/collection/convert/StreamExtensions.scala b/library/src/scala/collection/convert/StreamExtensions.scala index 28b762b425f0..ca161d45d83a 100644 --- a/library/src/scala/collection/convert/StreamExtensions.scala +++ b/library/src/scala/collection/convert/StreamExtensions.scala @@ -450,31 +450,31 @@ object StreamExtensions { * `noAccumulatorFactoryInfo` is passed. */ trait AccumulatorFactoryInfo[A, C] { - val companion: AnyRef + val companion: AnyRef | Null } trait LowPriorityAccumulatorFactoryInfo { implicit def noAccumulatorFactoryInfo[A, C]: AccumulatorFactoryInfo[A, C] = noAccumulatorFactoryInfoPrototype.asInstanceOf[AccumulatorFactoryInfo[A, C]] private val noAccumulatorFactoryInfoPrototype: AccumulatorFactoryInfo[AnyRef, AnyRef] = new AccumulatorFactoryInfo[AnyRef, AnyRef] { - val companion: AnyRef = null + val companion: AnyRef | Null = null } } object AccumulatorFactoryInfo extends LowPriorityAccumulatorFactoryInfo { implicit def anyAccumulatorFactoryInfo[A]: AccumulatorFactoryInfo[A, AnyAccumulator[A]] = anyAccumulatorFactoryInfoPrototype.asInstanceOf[AccumulatorFactoryInfo[A, AnyAccumulator[A]]] private object anyAccumulatorFactoryInfoPrototype extends AccumulatorFactoryInfo[AnyRef, AnyAccumulator[AnyRef]] { - val companion: AnyRef = AnyAccumulator + val companion: AnyRef | Null = AnyAccumulator } implicit val intAccumulatorFactoryInfo: AccumulatorFactoryInfo[Int, IntAccumulator] = new AccumulatorFactoryInfo[Int, IntAccumulator] { - val companion: AnyRef = IntAccumulator + val companion: AnyRef | Null = IntAccumulator } implicit val longAccumulatorFactoryInfo: AccumulatorFactoryInfo[Long, LongAccumulator] = new AccumulatorFactoryInfo[Long, LongAccumulator] { - val companion: AnyRef = LongAccumulator + val companion: AnyRef | Null = LongAccumulator } implicit val doubleAccumulatorFactoryInfo: AccumulatorFactoryInfo[Double, DoubleAccumulator] = new AccumulatorFactoryInfo[Double, DoubleAccumulator] { - val companion: AnyRef = DoubleAccumulator + val companion: AnyRef | Null = DoubleAccumulator } implicit val jIntegerAccumulatorFactoryInfo: AccumulatorFactoryInfo[jl.Integer, IntAccumulator] = intAccumulatorFactoryInfo.asInstanceOf[AccumulatorFactoryInfo[jl.Integer, IntAccumulator]] diff --git a/library/src/scala/collection/convert/impl/ArrayStepper.scala b/library/src/scala/collection/convert/impl/ArrayStepper.scala index b53dab574909..bed14aec8739 100644 --- a/library/src/scala/collection/convert/impl/ArrayStepper.scala +++ b/library/src/scala/collection/convert/impl/ArrayStepper.scala @@ -16,7 +16,7 @@ package impl import scala.language.`2.13` import scala.collection._ -private[collection] class ObjectArrayStepper[A <: Object](underlying: Array[A], _i0: Int, _iN: Int) +private[collection] class ObjectArrayStepper[A <: Object | Null](underlying: Array[A], _i0: Int, _iN: Int) extends IndexedStepperBase[AnyStepper[A], ObjectArrayStepper[A]](_i0, _iN) with AnyStepper[A] { def nextStep(): A = if (hasStep) { val j = i0; i0 += 1; underlying(j) } else Stepper.throwNSEE() diff --git a/library/src/scala/collection/convert/impl/BinaryTreeStepper.scala b/library/src/scala/collection/convert/impl/BinaryTreeStepper.scala index 87823ced9cee..d736044f4f90 100644 --- a/library/src/scala/collection/convert/impl/BinaryTreeStepper.scala +++ b/library/src/scala/collection/convert/impl/BinaryTreeStepper.scala @@ -14,6 +14,7 @@ package scala.collection.convert package impl import scala.language.`2.13` + import java.util.Spliterator import annotation.tailrec @@ -22,7 +23,7 @@ import scala.collection._ private[collection] object BinaryTreeStepper { - val emptyStack = new Array[AnyRef](0) + val emptyStack = new Array[AnyRef | Null](0) } @@ -45,9 +46,9 @@ private[collection] object BinaryTreeStepper { * Subclasses should allow this class to do all the work of maintaining state; `next` should simply * reduce `maxLength` by one, and consume `myCurrent` and set it to `null` if `hasNext` is true. */ -private[collection] abstract class BinaryTreeStepperBase[A, T >: Null <: AnyRef, Sub >: Null, Semi <: Sub with BinaryTreeStepperBase[A, T, _, _]]( - protected var maxLength: Int, protected var myCurrent: T, protected var stack: Array[AnyRef], protected var index: Int, - protected val left: T => T, protected val right: T => T +private[collection] abstract class BinaryTreeStepperBase[A, T <: AnyRef, Sub, Semi <: Sub with BinaryTreeStepperBase[A, T, _, _]]( + protected var maxLength: Int, protected var myCurrent: T | Null, protected var stack: Array[AnyRef | Null], protected var index: Int, + protected val left: T => T | Null, protected val right: T => T | Null ) extends EfficientSplit { /** Unrolls a subtree onto the stack starting from a particular node, returning @@ -88,7 +89,7 @@ extends EfficientSplit { * * Right now overwrites everything so could allow reuse, but isn't used for it. */ - private[impl] final def initialize(root: T, size: Int): Unit = + private[impl] final def initialize(root: T | Null, size: Int): Unit = if (root eq null) { maxLength = 0 myCurrent = null @@ -101,7 +102,7 @@ extends EfficientSplit { myCurrent = detach(unroll(root)) } - protected def semiclone(maxL: Int, myC: T, stk: Array[AnyRef], ix: Int): Semi + protected def semiclone(maxL: Int, myC: T | Null, stk: Array[AnyRef | Null], ix: Int): Semi def characteristics: Int = Spliterator.ORDERED @@ -122,11 +123,11 @@ extends EfficientSplit { * * If the tree is empty or only has one element left, it returns `null` instead of splitting. */ - def trySplit(): Sub = + def trySplit(): Sub | Null = if (!hasStep || index < 0) null else { val root = stack(0).asInstanceOf[T] - val leftStack = + val leftStack = if (index > 0) java.util.Arrays.copyOfRange(stack, 1, index+1) else BinaryTreeStepper.emptyStack val leftIndex = index - 1 @@ -142,25 +143,25 @@ extends EfficientSplit { } -private[collection] final class AnyBinaryTreeStepper[A, T >: Null <: AnyRef]( - _maxLength: Int, _myCurrent: T, _stack: Array[AnyRef], _index: Int, _left: T => T, _right: T => T, protected val extract: T => A +private[collection] final class AnyBinaryTreeStepper[A, T <: AnyRef]( + _maxLength: Int, _myCurrent: T | Null, _stack: Array[AnyRef | Null], _index: Int, _left: T => T | Null, _right: T => T | Null, protected val extract: T => A ) extends BinaryTreeStepperBase[A, T, AnyStepper[A], AnyBinaryTreeStepper[A, T]](_maxLength, _myCurrent, _stack, _index, _left, _right) with AnyStepper[A] { def nextStep(): A = if (hasStep) { - val ans = extract(myCurrent) + val ans = extract(myCurrent.nn) myCurrent = null maxLength -= 1 ans } else Stepper.throwNSEE() - def semiclone(maxL: Int, myC: T, stk: Array[AnyRef], ix: Int): AnyBinaryTreeStepper[A, T] = + def semiclone(maxL: Int, myC: T | Null, stk: Array[AnyRef | Null], ix: Int): AnyBinaryTreeStepper[A, T] = new AnyBinaryTreeStepper[A, T](maxL, myC, stk, ix, left, right, extract) } private[collection] object AnyBinaryTreeStepper { - def from[A, T >: Null <: AnyRef](maxLength: Int, root: T, left: T => T, right: T => T, extract: T => A): AnyBinaryTreeStepper[A, T] = { + def from[A, T <: AnyRef](maxLength: Int, root: T | Null, left: T => T | Null, right: T => T | Null, extract: T => A): AnyBinaryTreeStepper[A, T] = { val ans = new AnyBinaryTreeStepper(0, null, BinaryTreeStepper.emptyStack, -1, left, right, extract) ans.initialize(root, maxLength) ans @@ -168,25 +169,25 @@ private[collection] object AnyBinaryTreeStepper { } -private[collection] final class DoubleBinaryTreeStepper[T >: Null <: AnyRef]( - _maxLength: Int, _myCurrent: T, _stack: Array[AnyRef], _index: Int, _left: T => T, _right: T => T, protected val extract: T => Double +private[collection] final class DoubleBinaryTreeStepper[T <: AnyRef]( + _maxLength: Int, _myCurrent: T | Null, _stack: Array[AnyRef | Null], _index: Int, _left: T => T | Null, _right: T => T | Null, protected val extract: T => Double ) extends BinaryTreeStepperBase[Double, T, DoubleStepper, DoubleBinaryTreeStepper[T]](_maxLength, _myCurrent, _stack, _index, _left, _right) with DoubleStepper { def nextStep(): Double = if (hasStep) { - val ans = extract(myCurrent) + val ans = extract(myCurrent.nn) myCurrent = null maxLength -= 1 ans } else Stepper.throwNSEE() - def semiclone(maxL: Int, myC: T, stk: Array[AnyRef], ix: Int): DoubleBinaryTreeStepper[T] = + def semiclone(maxL: Int, myC: T | Null, stk: Array[AnyRef | Null], ix: Int): DoubleBinaryTreeStepper[T] = new DoubleBinaryTreeStepper[T](maxL, myC, stk, ix, left, right, extract) } private [collection] object DoubleBinaryTreeStepper { - def from[T >: Null <: AnyRef](maxLength: Int, root: T, left: T => T, right: T => T, extract: T => Double): DoubleBinaryTreeStepper[T] = { + def from[T <: AnyRef](maxLength: Int, root: T | Null, left: T => T | Null, right: T => T | Null, extract: T => Double): DoubleBinaryTreeStepper[T] = { val ans = new DoubleBinaryTreeStepper(0, null, BinaryTreeStepper.emptyStack, -1, left, right, extract) ans.initialize(root, maxLength) ans @@ -194,25 +195,25 @@ private [collection] object DoubleBinaryTreeStepper { } -private[collection] final class IntBinaryTreeStepper[T >: Null <: AnyRef]( - _maxLength: Int, _myCurrent: T, _stack: Array[AnyRef], _index: Int, _left: T => T, _right: T => T, protected val extract: T => Int +private[collection] final class IntBinaryTreeStepper[T <: AnyRef]( + _maxLength: Int, _myCurrent: T | Null, _stack: Array[AnyRef | Null], _index: Int, _left: T => T | Null, _right: T => T | Null, protected val extract: T => Int ) extends BinaryTreeStepperBase[Int, T, IntStepper, IntBinaryTreeStepper[T]](_maxLength, _myCurrent, _stack, _index, _left, _right) with IntStepper { def nextStep(): Int = if (hasStep) { - val ans = extract(myCurrent) + val ans = extract(myCurrent.nn) myCurrent = null maxLength -= 1 ans } else Stepper.throwNSEE() - def semiclone(maxL: Int, myC: T, stk: Array[AnyRef], ix: Int): IntBinaryTreeStepper[T] = + def semiclone(maxL: Int, myC: T | Null, stk: Array[AnyRef | Null], ix: Int): IntBinaryTreeStepper[T] = new IntBinaryTreeStepper[T](maxL, myC, stk, ix, left, right, extract) } private [collection] object IntBinaryTreeStepper { - def from[T >: Null <: AnyRef](maxLength: Int, root: T, left: T => T, right: T => T, extract: T => Int): IntBinaryTreeStepper[T] = { + def from[T <: AnyRef](maxLength: Int, root: T | Null, left: T => T | Null, right: T => T | Null, extract: T => Int): IntBinaryTreeStepper[T] = { val ans = new IntBinaryTreeStepper(0, null, BinaryTreeStepper.emptyStack, -1, left, right, extract) ans.initialize(root, maxLength) ans @@ -221,25 +222,25 @@ private [collection] object IntBinaryTreeStepper { -private[collection] final class LongBinaryTreeStepper[T >: Null <: AnyRef]( - _maxLength: Int, _myCurrent: T, _stack: Array[AnyRef], _index: Int, _left: T => T, _right: T => T, protected val extract: T => Long +private[collection] final class LongBinaryTreeStepper[T <: AnyRef]( + _maxLength: Int, _myCurrent: T | Null, _stack: Array[AnyRef | Null], _index: Int, _left: T => T | Null, _right: T => T | Null, protected val extract: T => Long ) extends BinaryTreeStepperBase[Long, T, LongStepper, LongBinaryTreeStepper[T]](_maxLength, _myCurrent, _stack, _index, _left, _right) with LongStepper { def nextStep(): Long = if (hasStep) { - val ans = extract(myCurrent) + val ans = extract(myCurrent.nn) myCurrent = null maxLength -= 1 ans } else Stepper.throwNSEE() - def semiclone(maxL: Int, myC: T, stk: Array[AnyRef], ix: Int): LongBinaryTreeStepper[T] = + def semiclone(maxL: Int, myC: T | Null, stk: Array[AnyRef | Null], ix: Int): LongBinaryTreeStepper[T] = new LongBinaryTreeStepper[T](maxL, myC, stk, ix, left, right, extract) } private [collection] object LongBinaryTreeStepper { - def from[T >: Null <: AnyRef](maxLength: Int, root: T, left: T => T, right: T => T, extract: T => Long): LongBinaryTreeStepper[T] = { + def from[T <: AnyRef](maxLength: Int, root: T | Null, left: T => T | Null, right: T => T | Null, extract: T => Long): LongBinaryTreeStepper[T] = { val ans = new LongBinaryTreeStepper(0, null, BinaryTreeStepper.emptyStack, -1, left, right, extract) ans.initialize(root, maxLength) ans diff --git a/library/src/scala/collection/convert/impl/BitSetStepper.scala b/library/src/scala/collection/convert/impl/BitSetStepper.scala index a7fa44685d80..70d59d2d2555 100644 --- a/library/src/scala/collection/convert/impl/BitSetStepper.scala +++ b/library/src/scala/collection/convert/impl/BitSetStepper.scala @@ -19,8 +19,9 @@ import scala.collection.{BitSetOps, IntStepper, Stepper} private[collection] final class BitSetStepper( - private var underlying: BitSetOps[_], - private var cache0: Long, private var cache1: Long, + @annotation.stableNull + private var underlying: BitSetOps[_] | Null, + private var cache0: Long, private var cache1: Long, _i0: Int, _iN: Int, private var cacheIndex: Int ) @@ -48,7 +49,7 @@ with IntStepper { findNext() } } - else if (underlying eq null) { + else if (underlying eq null) { i0 = iN found = false found @@ -97,7 +98,7 @@ with IntStepper { else scanLong(bits, from + 1) def nextStep(): Int = - if (found || findNext()) { + if (found || findNext()) { found = false val ans = i0 i0 += 1 diff --git a/library/src/scala/collection/convert/impl/ChampStepper.scala b/library/src/scala/collection/convert/impl/ChampStepper.scala index 2973cd1ba9d2..05589f1ee04d 100644 --- a/library/src/scala/collection/convert/impl/ChampStepper.scala +++ b/library/src/scala/collection/convert/impl/ChampStepper.scala @@ -23,7 +23,7 @@ import scala.collection.immutable.Node * to the end of all trees. */ private[collection] abstract class ChampStepperBase[ - A, T <: Node[T], Sub >: Null, Semi <: Sub with ChampStepperBase[A, T, _, _] + A, T <: Node[T], Sub, Semi <: Sub with ChampStepperBase[A, T, _, _] ](protected var maxSize: Int) extends EfficientSplit { import Node.MaxDepth @@ -109,7 +109,7 @@ extends EfficientSplit { ans } - final def trySplit(): Sub = + final def trySplit(): Sub | Null = if (!hasStep) null else { var fork = 0 @@ -157,7 +157,7 @@ extends EfficientSplit { } -private[collection] final class AnyChampStepper[A, T >: Null <: Node[T]](_maxSize: Int, protected val extract: (T, Int) => A) +private[collection] final class AnyChampStepper[A, T <: Node[T]](_maxSize: Int, protected val extract: (T, Int) => A) extends ChampStepperBase[A, T, AnyStepper[A], AnyChampStepper[A, T]](_maxSize) with AnyStepper[A] { def nextStep(): A = @@ -172,14 +172,14 @@ with AnyStepper[A] { def semiclone(): AnyChampStepper[A, T] = new AnyChampStepper[A, T](0, extract) } private[collection] object AnyChampStepper { - def from[A, T >: Null <: Node[T]](maxSize: Int, root: T, extract: (T, Int) => A): AnyChampStepper[A, T] = { + def from[A, T <: Node[T]](maxSize: Int, root: T, extract: (T, Int) => A): AnyChampStepper[A, T] = { val ans = new AnyChampStepper[A, T](maxSize, extract) ans.initRoot(root) ans } } -private[collection] final class DoubleChampStepper[T >: Null <: Node[T]](_maxSize: Int, protected val extract: (T, Int) => Double) +private[collection] final class DoubleChampStepper[T <: Node[T]](_maxSize: Int, protected val extract: (T, Int) => Double) extends ChampStepperBase[Double, T, DoubleStepper, DoubleChampStepper[T]](_maxSize) with DoubleStepper { def nextStep(): Double = @@ -194,14 +194,14 @@ with DoubleStepper { def semiclone(): DoubleChampStepper[T] = new DoubleChampStepper[T](0, extract) } private[collection] object DoubleChampStepper { - def from[T >: Null <: Node[T]](maxSize: Int, root: T, extract: (T, Int) => Double): DoubleChampStepper[T] = { + def from[T <: Node[T]](maxSize: Int, root: T, extract: (T, Int) => Double): DoubleChampStepper[T] = { val ans = new DoubleChampStepper[T](maxSize, extract) ans.initRoot(root) ans } } -private[collection] final class IntChampStepper[T >: Null <: Node[T]](_maxSize: Int, protected val extract: (T, Int) => Int) +private[collection] final class IntChampStepper[T <: Node[T]](_maxSize: Int, protected val extract: (T, Int) => Int) extends ChampStepperBase[Int, T, IntStepper, IntChampStepper[T]](_maxSize) with IntStepper { def nextStep(): Int = @@ -216,14 +216,14 @@ with IntStepper { def semiclone(): IntChampStepper[T] = new IntChampStepper[T](0, extract) } private[collection] object IntChampStepper { - def from[T >: Null <: Node[T]](maxSize: Int, root: T, extract: (T, Int) => Int): IntChampStepper[T] = { + def from[T <: Node[T]](maxSize: Int, root: T, extract: (T, Int) => Int): IntChampStepper[T] = { val ans = new IntChampStepper[T](maxSize, extract) ans.initRoot(root) ans } } -private[collection] final class LongChampStepper[T >: Null <: Node[T]](_maxSize: Int, protected val extract: (T, Int) => Long) +private[collection] final class LongChampStepper[T <: Node[T]](_maxSize: Int, protected val extract: (T, Int) => Long) extends ChampStepperBase[Long, T, LongStepper, LongChampStepper[T]](_maxSize) with LongStepper { def nextStep(): Long = @@ -238,7 +238,7 @@ with LongStepper { def semiclone(): LongChampStepper[T] = new LongChampStepper[T](0, extract) } private[collection] object LongChampStepper { - def from[T >: Null <: Node[T]](maxSize: Int, root: T, extract: (T, Int) => Long): LongChampStepper[T] = { + def from[T <: Node[T]](maxSize: Int, root: T, extract: (T, Int) => Long): LongChampStepper[T] = { val ans = new LongChampStepper[T](maxSize, extract) ans.initRoot(root) ans diff --git a/library/src/scala/collection/convert/impl/InOrderStepperBase.scala b/library/src/scala/collection/convert/impl/InOrderStepperBase.scala index 544bfff010d8..92706e95d004 100644 --- a/library/src/scala/collection/convert/impl/InOrderStepperBase.scala +++ b/library/src/scala/collection/convert/impl/InOrderStepperBase.scala @@ -23,7 +23,7 @@ import scala.collection.Stepper.EfficientSplit * * For collections that are guaranteed to not have gaps, use `IndexedStepperBase` instead. */ -private[convert] abstract class InOrderStepperBase[Sub >: Null, Semi <: Sub](protected var i0: Int, protected var iN: Int) +private[convert] abstract class InOrderStepperBase[Sub, Semi <: Sub](protected var i0: Int, protected var iN: Int) extends EfficientSplit { /** Set `true` if the element at `i0` is known to be there. `false` if either not known or is a gap. */ @@ -42,7 +42,7 @@ extends EfficientSplit { def estimateSize: Long = iN - i0 - def trySplit(): Sub = { + def trySplit(): Sub | Null = { if (iN-1 > i0) { val half = (i0 + iN) >>> 1 val ans = semiclone(half) diff --git a/library/src/scala/collection/convert/impl/IndexedStepperBase.scala b/library/src/scala/collection/convert/impl/IndexedStepperBase.scala index 3acb743e7c57..cc649bf09901 100644 --- a/library/src/scala/collection/convert/impl/IndexedStepperBase.scala +++ b/library/src/scala/collection/convert/impl/IndexedStepperBase.scala @@ -19,7 +19,7 @@ import java.util.Spliterator import scala.collection.Stepper.EfficientSplit /** Abstracts all the generic operations of stepping over an indexable collection */ -private[convert] abstract class IndexedStepperBase[Sub >: Null, Semi <: Sub](protected var i0: Int, protected var iN: Int) +private[convert] abstract class IndexedStepperBase[Sub, Semi <: Sub](protected var i0: Int, protected var iN: Int) extends EfficientSplit { protected def semiclone(half: Int): Semi @@ -29,7 +29,7 @@ private[convert] abstract class IndexedStepperBase[Sub >: Null, Semi <: Sub](pro def estimateSize: Long = iN - i0 - def trySplit(): Sub = { + def trySplit(): Sub | Null = { if (iN-1 > i0) { val half = (i0+iN) >>> 1 val ans = semiclone(half) diff --git a/library/src/scala/collection/convert/impl/IteratorStepper.scala b/library/src/scala/collection/convert/impl/IteratorStepper.scala index 9f8aab9c2165..2e8e35e29205 100644 --- a/library/src/scala/collection/convert/impl/IteratorStepper.scala +++ b/library/src/scala/collection/convert/impl/IteratorStepper.scala @@ -19,19 +19,19 @@ import java.util.Spliterator import scala.collection.{AnyStepper, DoubleStepper, IntStepper, LongStepper, Stepper} import scala.jdk.{AnyAccumulator, DoubleAccumulator, IntAccumulator, LongAccumulator} -private[collection] class AnyIteratorStepper[A](_underlying: Iterator[A]) +private[collection] class AnyIteratorStepper[A](_underlying: Iterator[A] | Null) extends IteratorStepperBase[A, AnyStepper[A], AnyIteratorStepper[A]](_underlying) with AnyStepper[A] { protected def semiclone(): AnyIteratorStepper[A] = new AnyIteratorStepper(null) - def nextStep(): A = if (proxied ne null) proxied.nextStep() else underlying.next() + def nextStep(): A = if (proxied ne null) proxied.nextStep() else underlying.nn.next() - def trySplit(): AnyStepper[A] = if (proxied ne null) proxied.trySplit() else { + def trySplit(): AnyStepper[A] | Null = if (proxied ne null) proxied.trySplit() else { val acc = new AnyAccumulator[A] var i = 0 val n = nextChunkSize & 0xFFFFFFFC - while (i < n && underlying.hasNext) { acc += underlying.next(); i += 1 } - if (i < n || !underlying.hasNext) { + while (i < n && underlying.nn.hasNext) { acc += underlying.nn.next(); i += 1 } + if (i < n || !underlying.nn.hasNext) { proxied = acc.stepper proxied.trySplit() } @@ -44,19 +44,19 @@ private[collection] class AnyIteratorStepper[A](_underlying: Iterator[A]) } } -private[collection] class DoubleIteratorStepper(_underlying: Iterator[Double]) +private[collection] class DoubleIteratorStepper(_underlying: Iterator[Double] | Null) extends IteratorStepperBase[Double, DoubleStepper, DoubleIteratorStepper](_underlying) with DoubleStepper { protected def semiclone(): DoubleIteratorStepper = new DoubleIteratorStepper(null) - def nextStep(): Double = if (proxied ne null) proxied.nextStep() else underlying.next() + def nextStep(): Double = if (proxied ne null) proxied.nextStep() else underlying.nn.next() - def trySplit(): DoubleStepper = if (proxied ne null) proxied.trySplit() else { + def trySplit(): DoubleStepper | Null = if (proxied ne null) proxied.trySplit() else { val acc = new DoubleAccumulator var i = 0 val n = nextChunkSize & 0xFFFFFFFC - while (i < n && underlying.hasNext) { acc += underlying.next(); i += 1 } - if (i < n || !underlying.hasNext) { + while (i < n && underlying.nn.hasNext) { acc += underlying.nn.next(); i += 1 } + if (i < n || !underlying.nn.hasNext) { proxied = acc.stepper proxied.trySplit() } @@ -69,19 +69,19 @@ private[collection] class DoubleIteratorStepper(_underlying: Iterator[Double]) } } -private[collection] class IntIteratorStepper(_underlying: Iterator[Int]) +private[collection] class IntIteratorStepper(_underlying: Iterator[Int] | Null) extends IteratorStepperBase[Int, IntStepper, IntIteratorStepper](_underlying) with IntStepper { protected def semiclone(): IntIteratorStepper = new IntIteratorStepper(null) - def nextStep(): Int = if (proxied ne null) proxied.nextStep() else underlying.next() + def nextStep(): Int = if (proxied ne null) proxied.nextStep() else underlying.nn.next() - def trySplit(): IntStepper = if (proxied ne null) proxied.trySplit() else { + def trySplit(): IntStepper | Null = if (proxied ne null) proxied.trySplit() else { val acc = new IntAccumulator var i = 0 val n = nextChunkSize & 0xFFFFFFFC - while (i < n && underlying.hasNext) { acc += underlying.next(); i += 1 } - if (i < n || !underlying.hasNext) { + while (i < n && underlying.nn.hasNext) { acc += underlying.nn.next(); i += 1 } + if (i < n || !underlying.nn.hasNext) { proxied = acc.stepper proxied.trySplit() } @@ -94,19 +94,19 @@ private[collection] class IntIteratorStepper(_underlying: Iterator[Int]) } } -private[collection] class LongIteratorStepper(_underlying: Iterator[Long]) +private[collection] class LongIteratorStepper(_underlying: Iterator[Long] | Null) extends IteratorStepperBase[Long, LongStepper, LongIteratorStepper](_underlying) with LongStepper { protected def semiclone(): LongIteratorStepper = new LongIteratorStepper(null) - def nextStep(): Long = if (proxied ne null) proxied.nextStep() else underlying.next() + def nextStep(): Long = if (proxied ne null) proxied.nextStep() else underlying.nn.next() - def trySplit(): LongStepper = if (proxied ne null) proxied.trySplit() else { + def trySplit(): LongStepper | Null = if (proxied ne null) proxied.trySplit() else { val acc = new LongAccumulator var i = 0 val n = nextChunkSize & 0xFFFFFFFC - while (i < n && underlying.hasNext) { acc += underlying.next(); i += 1 } - if (i < n || !underlying.hasNext) { + while (i < n && underlying.nn.hasNext) { acc += underlying.nn.next(); i += 1 } + if (i < n || !underlying.nn.hasNext) { proxied = acc.stepper proxied.trySplit() } @@ -120,11 +120,12 @@ private[collection] class LongIteratorStepper(_underlying: Iterator[Long]) } /** Common functionality for Steppers that step through an Iterator, caching the results as needed when a split is requested. */ -private[convert] abstract class IteratorStepperBase[A, SP >: Null <: Stepper[A], Semi <: SP](final protected val underlying: Iterator[A]) { +private[convert] abstract class IteratorStepperBase[A, SP <: Stepper[A], Semi <: SP](final protected val underlying: Iterator[A] | Null) { final protected var nextChunkSize = 16 - final protected var proxied: SP = null + @annotation.stableNull + final protected var proxied: SP | Null = null protected def semiclone(): Semi // Must initialize with null iterator! def characteristics: Int = if (proxied ne null) Spliterator.ORDERED | Spliterator.SIZED | Spliterator.SUBSIZED else Spliterator.ORDERED def estimateSize: Long = if (proxied ne null) proxied.estimateSize else Long.MaxValue - def hasStep: Boolean = if (proxied ne null) proxied.hasStep else underlying.hasNext + def hasStep: Boolean = if (proxied ne null) proxied.hasStep else underlying.nn.hasNext } diff --git a/library/src/scala/collection/convert/impl/RangeStepper.scala b/library/src/scala/collection/convert/impl/RangeStepper.scala index 283975ff0332..ca600a3cf5a1 100644 --- a/library/src/scala/collection/convert/impl/RangeStepper.scala +++ b/library/src/scala/collection/convert/impl/RangeStepper.scala @@ -28,11 +28,11 @@ with IntStepper { val ans = myNext myNext += myStep i0 += 1 - ans + ans } else Stepper.throwNSEE() protected def semiclone(half: Int): RangeStepper = new RangeStepper(myNext, myStep, i0, half) - override def trySplit(): IntStepper = { + override def trySplit(): IntStepper | Null = { val old_i0 = i0 val ans = super.trySplit() myNext += (i0 - old_i0) * myStep diff --git a/library/src/scala/collection/convert/impl/StringStepper.scala b/library/src/scala/collection/convert/impl/StringStepper.scala index 72d4e67ef1cb..f824375b668d 100644 --- a/library/src/scala/collection/convert/impl/StringStepper.scala +++ b/library/src/scala/collection/convert/impl/StringStepper.scala @@ -47,7 +47,7 @@ extends IntStepper with EfficientSplit { } else Stepper.throwNSEE() } - def trySplit(): CodePointStringStepper = + def trySplit(): CodePointStringStepper | Null = if (iN - 3 > i0) { var half = (i0 + iN) >>> 1 if (isLowSurrogate(underlying.charAt(half))) half -= 1 diff --git a/library/src/scala/collection/convert/impl/TableStepper.scala b/library/src/scala/collection/convert/impl/TableStepper.scala index 324732a0c2d1..acdd1c57ec64 100644 --- a/library/src/scala/collection/convert/impl/TableStepper.scala +++ b/library/src/scala/collection/convert/impl/TableStepper.scala @@ -17,12 +17,12 @@ import scala.language.`2.13` import scala.collection.Stepper.EfficientSplit import scala.collection._ -private[collection] abstract class TableStepperBase[A, I >: Null <: AnyRef, Sub >: Null, Semi <: Sub with TableStepperBase[A, I, _, _]]( - protected var maxLength: Int, protected val table: Array[I], protected var i0: Int, protected val iN: Int +private[collection] abstract class TableStepperBase[A, I <: AnyRef, Sub, Semi <: Sub with TableStepperBase[A, I, _, _]]( + protected var maxLength: Int, protected val table: Array[I | Null], protected var i0: Int, protected val iN: Int ) extends EfficientSplit { // Always holds table(i0); if `null` it is time to switch to the next element - protected var myCurrent: I = if (i0 < iN) table(i0) else null + protected var myCurrent: I | Null = if (i0 < iN) table(i0) else null // Only call this when `myCurrent` is null (meaning we need to advance) @annotation.tailrec @@ -46,7 +46,7 @@ extends EfficientSplit { def hasStep: Boolean = (myCurrent ne null) || findNextCurrent() - def trySplit(): Sub = { + def trySplit(): Sub | Null = { if (iN-1 > i0 && maxLength > 0) { val half = (i0 + iN) >>> 1 val ans = semiclone(half) @@ -70,15 +70,15 @@ extends EfficientSplit { } -private[collection] final class AnyTableStepper[A, I >: Null <: AnyRef]( - _maxLength: Int, _table: Array[I], iterate: I => I, extract: I => A, _i0: Int, _iN: Int +private[collection] final class AnyTableStepper[A, I <: AnyRef]( + _maxLength: Int, _table: Array[I | Null], iterate: I => I, extract: I => A, _i0: Int, _iN: Int ) extends TableStepperBase[A, I, AnyStepper[A], AnyTableStepper[A, I]](_maxLength, _table, _i0, _iN) with AnyStepper[A] { def nextStep(): A = if (hasStep) { - val ans = extract(myCurrent) - myCurrent = iterate(myCurrent) + val ans = extract(myCurrent.nn) + myCurrent = iterate(myCurrent.nn) ans } else Stepper.throwNSEE() @@ -87,15 +87,15 @@ with AnyStepper[A] { } -private[collection] final class DoubleTableStepper[I >: Null <: AnyRef]( - _maxLength: Int, _table: Array[I], iterate: I => I, extract: I => Double, _i0: Int, _iN: Int +private[collection] final class DoubleTableStepper[I <: AnyRef]( + _maxLength: Int, _table: Array[I | Null], iterate: I => I, extract: I => Double, _i0: Int, _iN: Int ) extends TableStepperBase[Double, I, DoubleStepper, DoubleTableStepper[I]](_maxLength, _table, _i0, _iN) with DoubleStepper { def nextStep(): Double = if (hasStep) { - val ans = extract(myCurrent) - myCurrent = iterate(myCurrent) + val ans = extract(myCurrent.nn) + myCurrent = iterate(myCurrent.nn) ans } else Stepper.throwNSEE() @@ -104,15 +104,15 @@ with DoubleStepper { } -private[collection] final class IntTableStepper[I >: Null <: AnyRef]( - _maxLength: Int, _table: Array[I], iterate: I => I, extract: I => Int, _i0: Int, _iN: Int +private[collection] final class IntTableStepper[I <: AnyRef]( + _maxLength: Int, _table: Array[I | Null], iterate: I => I, extract: I => Int, _i0: Int, _iN: Int ) extends TableStepperBase[Int, I, IntStepper, IntTableStepper[I]](_maxLength, _table, _i0, _iN) with IntStepper { def nextStep(): Int = if (hasStep) { - val ans = extract(myCurrent) - myCurrent = iterate(myCurrent) + val ans = extract(myCurrent.nn) + myCurrent = iterate(myCurrent.nn) ans } else Stepper.throwNSEE() @@ -121,15 +121,15 @@ with IntStepper { } -private[collection] final class LongTableStepper[I >: Null <: AnyRef]( - _maxLength: Int, _table: Array[I], iterate: I => I, extract: I => Long, _i0: Int, _iN: Int +private[collection] final class LongTableStepper[I <: AnyRef]( + _maxLength: Int, _table: Array[I | Null], iterate: I => I, extract: I => Long, _i0: Int, _iN: Int ) extends TableStepperBase[Long, I, LongStepper, LongTableStepper[I]](_maxLength, _table, _i0, _iN) with LongStepper { def nextStep(): Long = if (hasStep) { - val ans = extract(myCurrent) - myCurrent = iterate(myCurrent) + val ans = extract(myCurrent.nn) + myCurrent = iterate(myCurrent.nn) ans } else Stepper.throwNSEE() diff --git a/library/src/scala/collection/convert/impl/VectorStepper.scala b/library/src/scala/collection/convert/impl/VectorStepper.scala index 85f349922f48..750b3cd55e97 100644 --- a/library/src/scala/collection/convert/impl/VectorStepper.scala +++ b/library/src/scala/collection/convert/impl/VectorStepper.scala @@ -16,7 +16,7 @@ package impl import scala.language.`2.13` import scala.collection._ -private[convert] abstract class VectorStepperBase[Sub >: Null, Semi <: Sub]( +private[convert] abstract class VectorStepperBase[Sub, Semi <: Sub]( _i0: Int, _iN: Int, protected val displayN: Int, @@ -24,9 +24,9 @@ private[convert] abstract class VectorStepperBase[Sub >: Null, Semi <: Sub]( ) extends IndexedStepperBase[Sub, Semi](_i0, _iN) { protected var index: Int = 32 // Force an advanceData on the first element - protected var leaves: Array[AnyRef] = null + protected var leaves: Array[AnyRef] = _ protected var index1: Int = 32 // Force advanceData to defer to initTo on the first element - protected var twigs: Array[AnyRef] = null + protected var twigs: Array[AnyRef] = _ protected final def advanceData(iX: Int): Unit = { index1 += 1 @@ -92,7 +92,7 @@ with DoubleStepper { index1 = 32 i0 = half ans - } + } } private[collection] class IntVectorStepper(_i0: Int, _iN: Int, _displayN: Int, _trunk: Array[AnyRef]) @@ -110,7 +110,7 @@ with IntStepper { index1 = 32 i0 = half ans - } + } } private[collection] class LongVectorStepper(_i0: Int, _iN: Int, _displayN: Int, _trunk: Array[AnyRef]) @@ -128,5 +128,5 @@ with LongStepper { index1 = 32 i0 = half ans - } + } } diff --git a/library/src/scala/collection/immutable/ArraySeq.scala b/library/src/scala/collection/immutable/ArraySeq.scala index d3e21515b95e..88ec33e729d8 100644 --- a/library/src/scala/collection/immutable/ArraySeq.scala +++ b/library/src/scala/collection/immutable/ArraySeq.scala @@ -92,7 +92,7 @@ sealed abstract class ArraySeq[+A] * * @return null if optimisation not possible. */ - private def appendedAllArraySeq[B >: A](that: ArraySeq[B]): ArraySeq[B] = { + private def appendedAllArraySeq[B >: A](that: ArraySeq[B]): ArraySeq[B] | Null = { // Optimise concatenation of two ArraySeqs // For ArraySeqs with sizes of [100, 1000, 10000] this is [3.5, 4.1, 5.2]x as fast if (isEmpty) @@ -328,7 +328,7 @@ object ArraySeq extends StrictOptimizedClassTagSeqFactory[ArraySeq] { self => }).asInstanceOf[ArraySeq[T]] @SerialVersionUID(3L) - final class ofRef[T <: AnyRef](val unsafeArray: Array[T]) extends ArraySeq[T] { + final class ofRef[T <: AnyRef | Null](val unsafeArray: Array[T]) extends ArraySeq[T] { def elemTag: ClassTag[T] = ClassTag[T](unsafeArray.getClass.getComponentType) def length: Int = unsafeArray.length @throws[ArrayIndexOutOfBoundsException] diff --git a/library/src/scala/collection/immutable/HashMap.scala b/library/src/scala/collection/immutable/HashMap.scala index 63f8b8a97006..2a4774a75c3c 100644 --- a/library/src/scala/collection/immutable/HashMap.scala +++ b/library/src/scala/collection/immutable/HashMap.scala @@ -1175,7 +1175,7 @@ private final class BitmapIndexedMapNode[K, +V]( } override def transform[W](f: (K, V) => W): BitmapIndexedMapNode[K, W] = { - var newContent: Array[Any] = null + @annotation.stableNull var newContent: Array[Any] | Null = null val iN = payloadArity // arity doesn't change during this operation val jN = nodeArity // arity doesn't change during this operation val newContentLength = content.length @@ -1720,7 +1720,7 @@ private final class BitmapIndexedMapNode[K, +V]( // bitmap of nodes which, when filtered, returned a single-element node. These must be migrated to data var nodeMigrateToDataTargetMap = 0 // the queue of single-element, post-filter nodes - var nodesToMigrateToData: mutable.Queue[MapNode[K, V]] = null + @annotation.stableNull var nodesToMigrateToData: mutable.Queue[MapNode[K, V]] | Null = null // bitmap of all nodes which, when filtered, returned themselves. They are passed forward to the returned node var nodesToPassThroughMap = 0 @@ -1730,7 +1730,7 @@ private final class BitmapIndexedMapNode[K, +V]( // not named `newNodesMap` (plural) to avoid confusion with `newNodeMap` (singular) var mapOfNewNodes = 0 // each bit in `mapOfNewNodes` corresponds to one element in this queue - var newNodes: mutable.Queue[MapNode[K, V]] = null + @annotation.stableNull var newNodes: mutable.Queue[MapNode[K, V]] | Null = null var newDataMap = 0 var newNodeMap = 0 @@ -1770,7 +1770,7 @@ private final class BitmapIndexedMapNode[K, +V]( nodesToPassThroughMap |= bitpos } else { mapOfNewNodes |= bitpos - if (newNodes eq null) { + if (newNodes == null) { newNodes = mutable.Queue.empty } newNodes += newSubNode @@ -1778,7 +1778,7 @@ private final class BitmapIndexedMapNode[K, +V]( } else if (newSubNode.size == 1) { newDataMap |= bitpos nodeMigrateToDataTargetMap |= bitpos - if (nodesToMigrateToData eq null) { + if (nodesToMigrateToData == null) { nodesToMigrateToData = mutable.Queue() } nodesToMigrateToData += newSubNode @@ -1828,14 +1828,14 @@ private final class BitmapIndexedMapNode[K, +V]( oldNodeIndex += 1 } else if ((bitpos & nodeMigrateToDataTargetMap) != 0) { // we need not check for null here. If nodeMigrateToDataTargetMap != 0, then nodesMigrateToData must not be null - val node = nodesToMigrateToData.dequeue() + val node = nodesToMigrateToData.nn.dequeue() newContent(TupleLength * newDataIndex) = node.getKey(0) newContent(TupleLength * newDataIndex + 1) = node.getValue(0) newOriginalHashes(newDataIndex) = node.getHash(0) newDataIndex += 1 oldNodeIndex += 1 } else if ((bitpos & mapOfNewNodes) != 0) { - newContent(newContentSize - newNodeIndex - 1) = newNodes.dequeue() + newContent(newContentSize - newNodeIndex - 1) = newNodes.nn.dequeue() newNodeIndex += 1 oldNodeIndex += 1 } else if ((bitpos & dataMap) != 0) { @@ -2010,12 +2010,12 @@ private final class HashCollisionMapNode[K, +V ]( if (hc eq this) { this } else { - var newContent: VectorBuilder[(K, V1)] = null + var newContent: VectorBuilder[(K, V1)] | Null = null val iter = content.iterator while (iter.hasNext) { val nextPayload = iter.next() if (hc.indexOf(nextPayload._1) < 0) { - if (newContent eq null) { + if (newContent == null) { newContent = new VectorBuilder[(K, V1)]() newContent.addAll(hc.content) } @@ -2033,7 +2033,7 @@ private final class HashCollisionMapNode[K, +V ]( override def mergeInto[V1 >: V](that: MapNode[K, V1], builder: HashMapBuilder[K, V1], shift: Int)(mergef: ((K, V), (K, V1)) => (K, V1)): Unit = that match { case hc: HashCollisionMapNode[K, V1] => val iter = content.iterator - val rightArray = hc.content.toArray[AnyRef] // really Array[(K, V1)] + val rightArray: Array[AnyRef | Null] = hc.content.toArray[AnyRef | Null] // really Array[(K, V1)] def rightIndexOf(key: K): Int = { var i = 0 @@ -2231,7 +2231,8 @@ 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] = _ + @annotation.stableNull + private var aliased: HashMap[K, V] | Null = _ private def isAliased: Boolean = aliased != null diff --git a/library/src/scala/collection/immutable/HashSet.scala b/library/src/scala/collection/immutable/HashSet.scala index 65dca3dbba79..c052dee5506d 100644 --- a/library/src/scala/collection/immutable/HashSet.scala +++ b/library/src/scala/collection/immutable/HashSet.scala @@ -1097,7 +1097,7 @@ private final class BitmapIndexedSetNode[A]( // return at runtime a SetNode[A], or a tuple of (A, Int, Int) // the queue of single-element, post-filter nodes - var nodesToMigrateToData: mutable.Queue[SetNode[A]] = null + var nodesToMigrateToData: mutable.Queue[SetNode[A]] | Null = null // bitmap of all nodes which, when filtered, returned themselves. They are passed forward to the returned node var nodesToPassThroughMap = 0 @@ -1107,7 +1107,7 @@ private final class BitmapIndexedSetNode[A]( // not named `newNodesMap` (plural) to avoid confusion with `newNodeMap` (singular) var mapOfNewNodes = 0 // each bit in `mapOfNewNodes` corresponds to one element in this queue - var newNodes: mutable.Queue[SetNode[A]] = null + var newNodes: mutable.Queue[SetNode[A]] | Null = null var newDataMap = 0 var newNodeMap = 0 @@ -1199,7 +1199,7 @@ private final class BitmapIndexedSetNode[A]( // bitmap of nodes which, when filtered, returned a single-element node. These must be migrated to data var nodeMigrateToDataTargetMap = 0 // the queue of single-element, post-filter nodes - var nodesToMigrateToData: mutable.Queue[SetNode[A]] = null + var nodesToMigrateToData: mutable.Queue[SetNode[A]] | Null = null // bitmap of all nodes which, when filtered, returned themselves. They are passed forward to the returned node var nodesToPassThroughMap = 0 @@ -1209,7 +1209,7 @@ private final class BitmapIndexedSetNode[A]( // not named `newNodesMap` (plural) to avoid confusion with `newNodeMap` (singular) var mapOfNewNodes = 0 // each bit in `mapOfNewNodes` corresponds to one element in this queue - var newNodes: mutable.Queue[SetNode[A]] = null + var newNodes: mutable.Queue[SetNode[A]] | Null = null var newDataMap = 0 var newNodeMap = 0 @@ -1327,9 +1327,9 @@ private final class BitmapIndexedSetNode[A]( oldDataPassThrough: Int, nodesToPassThroughMap: Int, nodeMigrateToDataTargetMap: Int, - nodesToMigrateToData: mutable.Queue[SetNode[A]], + nodesToMigrateToData: mutable.Queue[SetNode[A]] | Null, mapOfNewNodes: Int, - newNodes: mutable.Queue[SetNode[A]], + newNodes: mutable.Queue[SetNode[A]] | Null, newCachedHashCode: Int): BitmapIndexedSetNode[A] = { if (newSize == 0) { SetNode.empty @@ -1368,14 +1368,14 @@ private final class BitmapIndexedSetNode[A]( oldNodeIndex += 1 } else if ((bitpos & nodeMigrateToDataTargetMap) != 0) { // we need not check for null here. If nodeMigrateToDataTargetMap != 0, then nodesMigrateToData must not be null - val node = nodesToMigrateToData.dequeue() + val node = nodesToMigrateToData.nn.dequeue() newContent(newDataIndex) = node.getPayload(0) newOriginalHashes(newDataIndex) = node.getHash(0) newDataIndex += 1 oldNodeIndex += 1 } else if ((bitpos & mapOfNewNodes) != 0) { // we need not check for null here. If mapOfNewNodes != 0, then newNodes must not be null - newContent(newContentSize - newNodeIndex - 1) = newNodes.dequeue() + newContent(newContentSize - newNodeIndex - 1) = newNodes.nn.dequeue() newNodeIndex += 1 oldNodeIndex += 1 } else if ((bitpos & dataMap) != 0) { @@ -1842,7 +1842,7 @@ private final class HashCollisionSetNode[A](val originalHash: Int, val hash: Int if (hc eq this) { this } else { - var newContent: VectorBuilder[A] = null + var newContent: VectorBuilder[A] | Null = null val iter = hc.content.iterator while (iter.hasNext) { val nextPayload = iter.next() @@ -1964,7 +1964,8 @@ private[collection] final class HashSetBuilder[A] extends ReusableBuilder[A, Has /** The last given out HashSet 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: HashSet[A] = _ + @annotation.stableNull + private var aliased: HashSet[A] | Null = null private def isAliased: Boolean = aliased != null diff --git a/library/src/scala/collection/immutable/LazyList.scala b/library/src/scala/collection/immutable/LazyList.scala index 823144a19338..82c1402999f4 100644 --- a/library/src/scala/collection/immutable/LazyList.scala +++ b/library/src/scala/collection/immutable/LazyList.scala @@ -296,11 +296,11 @@ final class LazyList[+A] private (lazyState: AnyRef /* EmptyMarker.type | () => // when `_head ne Uninitialized` // - `null` if this is an empty lazy list // - `tail: LazyList[A]` otherwise - private[this] var _tail: AnyRef /* () => LazyList[A] | MidEvaluation.type | LazyList[A] */ = + private[this] var _tail: AnyRef | Null /* () => LazyList[A] | MidEvaluation.type | LazyList[A] | Null */ = if (lazyState eq EmptyMarker) null else lazyState private def rawHead: Any = _head - private def rawTail: AnyRef = _tail + private def rawTail: AnyRef | Null = _tail @inline private def isEvaluated: Boolean = _head.asInstanceOf[AnyRef] ne Uninitialized @@ -1112,7 +1112,7 @@ object LazyList extends SeqFactory[LazyList] { // DO NOT REFERENCE `ll` ANYWHERE ELSE, OR IT WILL LEAK THE HEAD var restRef = ll // val restRef = new ObjectRef(ll) newLL { - var it: Iterator[B] = null + var it: Iterator[B] | Null = null var itHasNext = false var rest = restRef // var rest = restRef.elem while (!itHasNext && !rest.isEmpty) { @@ -1124,10 +1124,10 @@ object LazyList extends SeqFactory[LazyList] { } } if (itHasNext) { - val head = it.next() + val head = it.nn.next() rest = rest.tail restRef = rest // restRef.elem = rest - eagerCons(head, newLL(eagerHeadPrependIterator(it)(flatMapImpl(rest, f)))) + eagerCons(head, newLL(eagerHeadPrependIterator(it.nn)(flatMapImpl(rest, f)))) } else Empty } } @@ -1449,3 +1449,4 @@ object LazyList extends SeqFactory[LazyList] { private[this] def readResolve(): Any = coll } } + diff --git a/library/src/scala/collection/immutable/LazyListIterable.scala b/library/src/scala/collection/immutable/LazyListIterable.scala index f1d14bbb1fad..da64bfa51e15 100644 --- a/library/src/scala/collection/immutable/LazyListIterable.scala +++ b/library/src/scala/collection/immutable/LazyListIterable.scala @@ -294,11 +294,11 @@ final class LazyListIterable[+A] private (lazyState: LazyListIterable.EmptyMarke // when `_head ne Uninitialized` // - `null` if this is an empty lazy list // - `tail: LazyListIterable[A]` otherwise - private[this] var _tail: AnyRef^{this} /* () => LazyListIterable[A] | MidEvaluation.type | LazyListIterable[A] */ = + private[this] var _tail: AnyRef^{this} | Null /* () => LazyListIterable[A] | MidEvaluation.type | LazyListIterable[A] | Null */ = if (lazyState eq EmptyMarker) null else lazyState private def rawHead: Any = _head - private def rawTail: AnyRef^{this} = _tail + private def rawTail: AnyRef^{this} | Null = _tail @inline private def isEvaluated: Boolean = _head.asInstanceOf[AnyRef] ne Uninitialized @@ -1134,7 +1134,7 @@ object LazyListIterable extends IterableFactory[LazyListIterable] { // DO NOT REFERENCE `ll` ANYWHERE ELSE, OR IT WILL LEAK THE HEAD var restRef: LazyListIterable[A]^{ll} = ll // val restRef = new ObjectRef(ll) newLL { - var it: Iterator[B]^{f} = null + var it: Iterator[B]^{f} | Null = null var itHasNext = false var rest: LazyListIterable[A]^{ll} = restRef // var rest = restRef.elem while (!itHasNext && !rest.isEmpty) { @@ -1146,10 +1146,10 @@ object LazyListIterable extends IterableFactory[LazyListIterable] { } } if (itHasNext) { - val head = it.next() + val head = it.nn.next() rest = rest.tail restRef = rest // restRef.elem = rest - eagerCons(head, newLL(eagerHeadPrependIterator(it)(flatMapImpl(rest, f)))) + eagerCons(head, newLL(eagerHeadPrependIterator(it.nn)(flatMapImpl(rest, f)))) } else Empty } } diff --git a/library/src/scala/collection/immutable/List.scala b/library/src/scala/collection/immutable/List.scala index 66c2ce524f3e..a26e63078b24 100644 --- a/library/src/scala/collection/immutable/List.scala +++ b/library/src/scala/collection/immutable/List.scala @@ -254,7 +254,7 @@ sealed abstract class List[+A] final override def collect[B](pf: PartialFunction[A, B]^): List[B] = { if (this eq Nil) Nil else { var rest = this - var h: ::[B] = null + var h: ::[B] | Null = null var x: Any = null // Special case for first element while (h eq null) { @@ -281,8 +281,8 @@ sealed abstract class List[+A] final override def flatMap[B](f: A => IterableOnce[B]^): List[B] = { var rest = this - var h: ::[B] = null - var t: ::[B] = null + var h: ::[B] | Null = null + var t: ::[B] | Null = null while (rest ne Nil) { val it = f(rest.head).iterator while (it.hasNext) { @@ -453,11 +453,11 @@ sealed abstract class List[+A] // Note to developers: there exists a duplication between this function and `reflect.internal.util.Collections#map2Conserve`. // If any successful optimization attempts or other changes are made, please rehash them there too. @tailrec - def loop(mappedHead: List[B], mappedLast: ::[B], unchanged: List[A], pending: List[A]): List[B] = { + def loop(mappedHead: List[B] | Null, mappedLast: ::[B] | Null, unchanged: List[A], pending: List[A]): List[B] = { if (pending.isEmpty) { if (mappedHead eq null) unchanged else { - mappedLast.next = (unchanged: List[B]) + mappedLast.nn.next = (unchanged: List[B]) mappedHead } } @@ -469,8 +469,8 @@ sealed abstract class List[+A] loop(mappedHead, mappedLast, unchanged, pending.tail) else { var xc = unchanged - var mappedHead1: List[B] = mappedHead - var mappedLast1: ::[B] = mappedLast + var mappedHead1: List[B] | Null = mappedHead + var mappedLast1: ::[B] | Null = mappedLast while (xc ne pending) { val next = new ::[B](xc.head, Nil) if (mappedHead1 eq null) mappedHead1 = next diff --git a/library/src/scala/collection/immutable/ListMap.scala b/library/src/scala/collection/immutable/ListMap.scala index 0d6ef12296e2..b6599369a1bd 100644 --- a/library/src/scala/collection/immutable/ListMap.scala +++ b/library/src/scala/collection/immutable/ListMap.scala @@ -136,7 +136,7 @@ object ListMap extends MapFactory[ListMap] { private[immutable] final class Node[K, V]( override private[immutable] val key: K, private[immutable] var _value: V, - private[immutable] var _init: ListMap[K, V] + private[immutable] var _init: ListMap[K, V] | Null ) extends ListMap[K, V] { releaseFence() @@ -195,28 +195,28 @@ object ListMap extends MapFactory[ListMap] { if (found) { if (isDifferent) { - var newHead: ListMap.Node[K, V1] = null - var prev: ListMap.Node[K, V1] = null + var newHead: ListMap.Node[K, V1] | Null = null + var prev: ListMap.Node[K, V1] | Null = null var curr: ListMap[K, V1] = this var i = 0 while (i < index) { val temp = new ListMap.Node(curr.key, curr.value, null) - if (prev ne null) { + if (prev != null) { prev._init = temp } prev = temp curr = curr.init - if (newHead eq null) { + if (newHead == null) { newHead = prev } i += 1 } val newNode = new ListMap.Node(curr.key, v, curr.init) - if (prev ne null) { + if (prev != null) { prev._init = newNode } releaseFence() - if (newHead eq null) newNode else newHead + if (newHead == null) newNode else newHead } else { this } @@ -232,7 +232,7 @@ object ListMap extends MapFactory[ListMap] { override def removed(k: K): ListMap[K, V] = removeInternal(k, this, Nil) - override private[immutable] def next: ListMap[K, V] = _init + override private[immutable] def next: ListMap[K, V] = _init.nn override def last: (K, V) = (key, value) override def init: ListMap[K, V] = next diff --git a/library/src/scala/collection/immutable/RedBlackTree.scala b/library/src/scala/collection/immutable/RedBlackTree.scala index 973c64826993..eb55757accb2 100644 --- a/library/src/scala/collection/immutable/RedBlackTree.scala +++ b/library/src/scala/collection/immutable/RedBlackTree.scala @@ -29,15 +29,15 @@ import scala.runtime.Statics.releaseFence * optimizations behind a reasonably clean API. */ private[collection] object RedBlackTree { - def validate[A](tree: Tree[A, _])(implicit ordering: Ordering[A]): tree.type = { + def validate[A](tree: Tree[A, _] | Null)(implicit ordering: Ordering[A]): tree.type = { def impl(tree: Tree[A, _], keyProp: A => Boolean): Int = { assert(keyProp(tree.key), s"key check failed: $tree") if (tree.isRed) { - assert(tree.left == null || tree.left.isBlack, s"red-red left $tree") - assert(tree.right == null || tree.right.isBlack, s"red-red right $tree") + assert(tree.left == null || tree.left.nn.isBlack, s"red-red left $tree") + assert(tree.right == null || tree.right.nn.isBlack, s"red-red right $tree") } - val leftBlacks = if (tree.left == null) 0 else impl(tree.left, k => keyProp(k) && ordering.compare(k, tree.key) < 0) - val rightBlacks = if (tree.right == null) 0 else impl(tree.right, k => keyProp(k) && ordering.compare(k, tree.key) > 0) + val leftBlacks = if (tree.left == null) 0 else impl(tree.left.nn, k => keyProp(k) && ordering.compare(k, tree.key) < 0) + val rightBlacks = if (tree.right == null) 0 else impl(tree.right.nn, k => keyProp(k) && ordering.compare(k, tree.key) > 0) assert(leftBlacks == rightBlacks, s"not balanced: $tree") leftBlacks + (if (tree.isBlack) 1 else 0) } @@ -45,23 +45,23 @@ private[collection] object RedBlackTree { tree } - def isEmpty(tree: Tree[_, _]): Boolean = tree eq null + def isEmpty(tree: Tree[_, _] | Null): Boolean = tree eq null - def contains[A: Ordering](tree: Tree[A, _], x: A): Boolean = lookup(tree, x) ne null - def get[A: Ordering, B](tree: Tree[A, B], x: A): Option[B] = lookup(tree, x) match { + def contains[A: Ordering](tree: Tree[A, _] | Null, x: A): Boolean = lookup(tree, x) ne null + def get[A: Ordering, B](tree: Tree[A, B] | Null, x: A): Option[B] = lookup(tree, x) match { case null => None case found => Some(found.value) } @tailrec - def lookup[A, B](tree: Tree[A, B], x: A)(implicit ordering: Ordering[A]): Tree[A, B] = if (tree eq null) null else { + def lookup[A, B](tree: Tree[A, B] | Null, x: A)(implicit ordering: Ordering[A]): Tree[A, B] | Null = if (tree eq null) null else { val cmp = ordering.compare(x, tree.key) if (cmp < 0) lookup(tree.left, x) else if (cmp > 0) lookup(tree.right, x) else tree } private[immutable] abstract class Helper[A](implicit val ordering: Ordering[A]) { - def beforePublish[B](tree: Tree[A, B]): Tree[A, B] = { + def beforePublish[B](tree: Tree[A, B] | Null): Tree[A, B] | Null = { if (tree eq null) tree else if (tree.isMutable) { val res = tree.mutableBlack.makeImmutable @@ -86,7 +86,7 @@ private[collection] object RedBlackTree { // RED // black(nl.L) nl.KV black // nl.R KV R - val resultLeft = newLeft_left.mutableBlack + val resultLeft = newLeft_left.nn.mutableBlack val resultRight = tree.mutableBlackWithLeft(newLeft_right) newLeft.mutableWithLeftRight(resultLeft, resultRight) @@ -95,12 +95,12 @@ private[collection] object RedBlackTree { // black nl.R.KV black // nl.L nl.KV nl.R.L nl.R.R KV R - val newLeft_right_right = newLeft_right.right + val newLeft_right_right = newLeft_right.nn.right - val resultLeft = newLeft.mutableBlackWithRight(newLeft_right.left) + val resultLeft = newLeft.mutableBlackWithRight(newLeft_right.nn.left) val resultRight = tree.mutableBlackWithLeft(newLeft_right_right) - newLeft_right.mutableWithLeftRight(resultLeft, resultRight) + newLeft_right.nn.mutableWithLeftRight(resultLeft, resultRight) } else { // tree // newLeft KV R @@ -129,10 +129,10 @@ private[collection] object RedBlackTree { // black nr.L.KV black // L KV nr.L.L nr.L.R nr.KV nr.R - val resultLeft = tree.mutableBlackWithRight(newRight_left.left) - val resultRight = newRight.mutableBlackWithLeft(newRight_left.right) + val resultLeft = tree.mutableBlackWithRight(newRight_left.nn.left) + val resultRight = newRight.mutableBlackWithLeft(newRight_left.nn.right) - newRight_left.mutableWithLeftRight(resultLeft, resultRight) + newRight_left.nn.mutableWithLeftRight(resultLeft, resultRight) } else { val newRight_right = newRight.right @@ -142,7 +142,7 @@ private[collection] object RedBlackTree { // L KV nr.L val resultLeft = tree.mutableBlackWithRight(newRight_left) - val resultRight = newRight_right.mutableBlack + val resultRight = newRight_right.nn.mutableBlack newRight.mutableWithLeftRight(resultLeft, resultRight) } else { @@ -159,7 +159,7 @@ private[collection] object RedBlackTree { } } private[immutable] class SetHelper[A](implicit ordering: Ordering[A]) extends Helper[A] { - protected[this] final def mutableUpd(tree: Tree[A, Any], k: A): Tree[A, Any] = + protected[this] final def mutableUpd(tree: Tree[A, Any] | Null, k: A): Tree[A, Any] = if (tree eq null) { mutableRedTree(k, (), null, null) } else if (k.asInstanceOf[AnyRef] eq tree.key.asInstanceOf[AnyRef]) { @@ -174,7 +174,7 @@ private[collection] object RedBlackTree { } } private[immutable] class MapHelper[A, B](implicit ordering: Ordering[A]) extends Helper[A] { - protected[this] final def mutableUpd[B1 >: B](tree: Tree[A, B], k: A, v: B1): Tree[A, B1] = + protected[this] final def mutableUpd[B1 >: B](tree: Tree[A, B] | Null, k: A, v: B1): Tree[A, B1] = if (tree eq null) { mutableRedTree(k, v, null, null) } else if (k.asInstanceOf[AnyRef] eq tree.key.asInstanceOf[AnyRef]) { @@ -189,39 +189,39 @@ private[collection] object RedBlackTree { } } - def count(tree: Tree[_, _]) = if (tree eq null) 0 else tree.count - def update[A: Ordering, B, B1 >: B](tree: Tree[A, B], k: A, v: B1, overwrite: Boolean): Tree[A, B1] = blacken(upd(tree, k, v, overwrite)) - def delete[A: Ordering, B](tree: Tree[A, B], k: A): Tree[A, B] = blacken(del(tree, k)) - def rangeImpl[A: Ordering, B](tree: Tree[A, B], from: Option[A], until: Option[A]): Tree[A, B] = (from, until) match { + def count(tree: Tree[_, _] | Null) = if (tree eq null) 0 else tree.count + def update[A: Ordering, B, B1 >: B](tree: Tree[A, B] | Null, k: A, v: B1, overwrite: Boolean): Tree[A, B1] | Null = blacken(upd(tree, k, v, overwrite)) + def delete[A: Ordering, B](tree: Tree[A, B] | Null, k: A): Tree[A, B] | Null = blacken(del(tree, k)) + def rangeImpl[A: Ordering, B](tree: Tree[A, B] | Null, from: Option[A], until: Option[A]): Tree[A, B] | Null = (from, until) match { case (Some(from), Some(until)) => this.range(tree, from, until) case (Some(from), None) => this.from(tree, from) case (None, Some(until)) => this.until(tree, until) case (None, None) => tree } - def range[A: Ordering, B](tree: Tree[A, B], from: A, until: A): Tree[A, B] = blacken(doRange(tree, from, until)) - def from[A: Ordering, B](tree: Tree[A, B], from: A): Tree[A, B] = blacken(doFrom(tree, from)) - def to[A: Ordering, B](tree: Tree[A, B], to: A): Tree[A, B] = blacken(doTo(tree, to)) - def until[A: Ordering, B](tree: Tree[A, B], key: A): Tree[A, B] = blacken(doUntil(tree, key)) + def range[A: Ordering, B](tree: Tree[A, B] | Null, from: A, until: A): Tree[A, B] | Null = blacken(doRange(tree, from, until)) + def from[A: Ordering, B](tree: Tree[A, B] | Null, from: A): Tree[A, B] | Null = blacken(doFrom(tree, from)) + def to[A: Ordering, B](tree: Tree[A, B] | Null, to: A): Tree[A, B] | Null = blacken(doTo(tree, to)) + def until[A: Ordering, B](tree: Tree[A, B] | Null, key: A): Tree[A, B] | Null = blacken(doUntil(tree, key)) - def drop[A: Ordering, B](tree: Tree[A, B], n: Int): Tree[A, B] = blacken(doDrop(tree, n)) - def take[A: Ordering, B](tree: Tree[A, B], n: Int): Tree[A, B] = blacken(doTake(tree, n)) - def slice[A: Ordering, B](tree: Tree[A, B], from: Int, until: Int): Tree[A, B] = blacken(doSlice(tree, from, until)) + def drop[A: Ordering, B](tree: Tree[A, B] | Null, n: Int): Tree[A, B] | Null = blacken(doDrop(tree, n)) + def take[A: Ordering, B](tree: Tree[A, B] | Null, n: Int): Tree[A, B] | Null = blacken(doTake(tree, n)) + def slice[A: Ordering, B](tree: Tree[A, B] | Null, from: Int, until: Int): Tree[A, B] | Null = blacken(doSlice(tree, from, until)) - def smallest[A, B](tree: Tree[A, B]): Tree[A, B] = { + def smallest[A, B](tree: Tree[A, B] | Null): Tree[A, B] = { if (tree eq null) throw new NoSuchElementException("empty tree") var result = tree - while (result.left ne null) result = result.left + while (result.left ne null) result = result.left.nn result } - def greatest[A, B](tree: Tree[A, B]): Tree[A, B] = { + def greatest[A, B](tree: Tree[A, B] | Null): Tree[A, B] = { if (tree eq null) throw new NoSuchElementException("empty tree") var result = tree - while (result.right ne null) result = result.right + while (result.right ne null) result = result.right.nn result } - def tail[A, B](tree: Tree[A, B]): Tree[A, B] = { - def _tail(tree: Tree[A, B]): Tree[A, B] = + def tail[A, B](tree: Tree[A, B] | Null): Tree[A, B] | Null = { + def _tail(tree: Tree[A, B] | Null): Tree[A, B] | Null= if (tree eq null) throw new NoSuchElementException("empty tree") else { val tl = tree.left @@ -232,8 +232,8 @@ private[collection] object RedBlackTree { blacken(_tail(tree)) } - def init[A, B](tree: Tree[A, B]): Tree[A, B] = { - def _init(tree: Tree[A, B]): Tree[A, B] = + def init[A, B](tree: Tree[A, B] | Null): Tree[A, B] | Null = { + def _init(tree: Tree[A, B] | Null): Tree[A, B] | Null = if (tree eq null) throw new NoSuchElementException("empty tree") else { val tr = tree.right @@ -247,7 +247,7 @@ private[collection] object RedBlackTree { /** * Returns the smallest node with a key larger than or equal to `x`. Returns `null` if there is no such node. */ - def minAfter[A, B](tree: Tree[A, B], x: A)(implicit ordering: Ordering[A]): Tree[A, B] = if (tree eq null) null else { + def minAfter[A, B](tree: Tree[A, B] | Null, x: A)(implicit ordering: Ordering[A]): Tree[A, B] | Null = if (tree eq null) null else { val cmp = ordering.compare(x, tree.key) if (cmp == 0) tree else if (cmp < 0) { @@ -259,7 +259,7 @@ private[collection] object RedBlackTree { /** * Returns the largest node with a key smaller than `x`. Returns `null` if there is no such node. */ - def maxBefore[A, B](tree: Tree[A, B], x: A)(implicit ordering: Ordering[A]): Tree[A, B] = if (tree eq null) null else { + def maxBefore[A, B](tree: Tree[A, B] | Null, x: A)(implicit ordering: Ordering[A]): Tree[A, B] | Null = if (tree eq null) null else { val cmp = ordering.compare(x, tree.key) if (cmp <= 0) maxBefore(tree.left, x) else { @@ -268,21 +268,21 @@ private[collection] object RedBlackTree { } } - def foreach[A,B,U](tree:Tree[A,B], f:((A,B)) => U):Unit = if (tree ne null) _foreach(tree,f) + def foreach[A,B,U](tree:Tree[A,B] | Null, f:((A,B)) => U):Unit = if (tree ne null) _foreach(tree,f) - def keysEqual[A: Ordering, X, Y](a: Tree[A, X], b: Tree[A, Y]): Boolean = { + def keysEqual[A: Ordering, X, Y](a: Tree[A, X] | Null, b: Tree[A, Y] | Null): Boolean = { if (a eq b) true else if (a eq null) false else if (b eq null) false else a.count == b.count && (new EqualsIterator(a)).sameKeys(new EqualsIterator(b)) } - def valuesEqual[A: Ordering, X, Y](a: Tree[A, X], b: Tree[A, Y]): Boolean = { + def valuesEqual[A: Ordering, X, Y](a: Tree[A, X] | Null, b: Tree[A, Y] | Null): Boolean = { if (a eq b) true else if (a eq null) false else if (b eq null) false else a.count == b.count && (new EqualsIterator(a)).sameValues(new EqualsIterator(b)) } - def entriesEqual[A: Ordering, X, Y](a: Tree[A, X], b: Tree[A, Y]): Boolean = { + def entriesEqual[A: Ordering, X, Y](a: Tree[A, X] | Null, b: Tree[A, Y] | Null): Boolean = { if (a eq b) true else if (a eq null) false else if (b eq null) false @@ -290,52 +290,52 @@ private[collection] object RedBlackTree { } private[this] def _foreach[A, B, U](tree: Tree[A, B], f: ((A, B)) => U): Unit = { - if (tree.left ne null) _foreach(tree.left, f) + if (tree.left ne null) _foreach(tree.left.nn, f) f((tree.key, tree.value)) - if (tree.right ne null) _foreach(tree.right, f) + if (tree.right ne null) _foreach(tree.right.nn, f) } - def foreachKey[A, U](tree:Tree[A,_], f: A => U):Unit = if (tree ne null) _foreachKey(tree,f) + def foreachKey[A, U](tree: Tree[A,_] | Null, f: A => U):Unit = if (tree ne null) _foreachKey(tree,f) private[this] def _foreachKey[A, U](tree: Tree[A, _], f: A => U): Unit = { - if (tree.left ne null) _foreachKey(tree.left, f) + if (tree.left ne null) _foreachKey(tree.left.nn, f) f((tree.key)) - if (tree.right ne null) _foreachKey(tree.right, f) + if (tree.right ne null) _foreachKey(tree.right.nn, f) } - def foreachEntry[A, B, U](tree:Tree[A,B], f: (A, B) => U):Unit = if (tree ne null) _foreachEntry(tree,f) + def foreachEntry[A, B, U](tree: Tree[A,B] | Null, f: (A, B) => U):Unit = if (tree ne null) _foreachEntry(tree,f) private[this] def _foreachEntry[A, B, U](tree: Tree[A, B], f: (A, B) => U): Unit = { - if (tree.left ne null) _foreachEntry(tree.left, f) + if (tree.left ne null) _foreachEntry(tree.left.nn, f) f(tree.key, tree.value) - if (tree.right ne null) _foreachEntry(tree.right, f) + if (tree.right ne null) _foreachEntry(tree.right.nn, f) } - def iterator[A: Ordering, B](tree: Tree[A, B], start: Option[A] = None): Iterator[(A, B)] = new EntriesIterator(tree, start) - def keysIterator[A: Ordering](tree: Tree[A, _], start: Option[A] = None): Iterator[A] = new KeysIterator(tree, start) - def valuesIterator[A: Ordering, B](tree: Tree[A, B], start: Option[A] = None): Iterator[B] = new ValuesIterator(tree, start) + def iterator[A: Ordering, B](tree: Tree[A, B] | Null, start: Option[A] = None): Iterator[(A, B)] = new EntriesIterator(tree, start) + def keysIterator[A: Ordering](tree: Tree[A, _] | Null, start: Option[A] = None): Iterator[A] = new KeysIterator(tree, start) + def valuesIterator[A: Ordering, B](tree: Tree[A, B] | Null, start: Option[A] = None): Iterator[B] = new ValuesIterator(tree, start) @tailrec def nth[A, B](tree: Tree[A, B], n: Int): Tree[A, B] = { val count = this.count(tree.left) - if (n < count) nth(tree.left, n) - else if (n > count) nth(tree.right, n - count - 1) + if (n < count) nth(tree.left.nn, n) + else if (n > count) nth(tree.right.nn, n - count - 1) else tree } - def isBlack(tree: Tree[_, _]) = (tree eq null) || tree.isBlack + def isBlack(tree: Tree[_, _] | Null) = (tree eq null) || tree.isBlack - @`inline` private[this] def isRedTree(tree: Tree[_, _]) = (tree ne null) && tree.isRed - @`inline` private[this] def isBlackTree(tree: Tree[_, _]) = (tree ne null) && tree.isBlack + @`inline` private[this] def isRedTree(tree: Tree[_, _] | Null) = (tree ne null) && tree.isRed + @`inline` private[this] def isBlackTree(tree: Tree[_, _] | Null) = (tree ne null) && tree.isBlack - private[this] def blacken[A, B](t: Tree[A, B]): Tree[A, B] = if (t eq null) null else t.black + private[this] def blacken[A, B](t: Tree[A, B] | Null): Tree[A, B] | Null = if (t eq null) null else t.black // Blacken if the tree is red and has a red child. This is necessary when using methods such as `upd` or `updNth` // for building subtrees. Use `blacken` instead when building top-level trees. private[this] def maybeBlacken[A, B](t: Tree[A, B]): Tree[A, B] = if(isBlack(t)) t else if(isRedTree(t.left) || isRedTree(t.right)) t.black else t - private[this] def mkTree[A, B](isBlack: Boolean, key: A, value: B, left: Tree[A, B], right: Tree[A, B]) = { + private[this] def mkTree[A, B](isBlack: Boolean, key: A, value: B, left: Tree[A, B] | Null, right: Tree[A, B] | Null) = { val sizeAndColour = sizeOf(left) + sizeOf(right) + 1 | (if(isBlack) initialBlackCount else initialRedCount) new Tree(key, value.asInstanceOf[AnyRef], left, right, sizeAndColour) } @@ -355,7 +355,7 @@ private[collection] object RedBlackTree { // RED // black(nl.L) nl.KV black // nl.R KV R - val resultLeft = newLeft_left.black + val resultLeft = newLeft_left.nn.black val resultRight = tree.blackWithLeft(newLeft_right) newLeft.withLeftRight(resultLeft, resultRight) @@ -363,12 +363,12 @@ private[collection] object RedBlackTree { // RED // black nl.R.KV black // nl.L nl.KV nl.R.L nl.R.R KV R - val newLeft_right_right = newLeft_right.right + val newLeft_right_right = newLeft_right.nn.right - val resultLeft = newLeft.blackWithRight(newLeft_right.left) + val resultLeft = newLeft.blackWithRight(newLeft_right.nn.left) val resultRight = tree.blackWithLeft(newLeft_right_right) - newLeft_right.withLeftRight(resultLeft, resultRight) + newLeft_right.nn.withLeftRight(resultLeft, resultRight) } else { // tree // newLeft KV R @@ -395,10 +395,10 @@ private[collection] object RedBlackTree { // RED // black nr.L.KV black // L KV nr.L.L nr.L.R nr.KV nr.R - val resultLeft = tree.blackWithRight(newRight_left.left) - val resultRight = newRight.blackWithLeft(newRight_left.right) + val resultLeft = tree.blackWithRight(newRight_left.nn.left) + val resultRight = newRight.blackWithLeft(newRight_left.nn.right) - newRight_left.withLeftRight(resultLeft, resultRight) + newRight_left.nn.withLeftRight(resultLeft, resultRight) } else { val newRight_right = newRight.right if (isRedTree(newRight_right)) { @@ -406,7 +406,7 @@ private[collection] object RedBlackTree { // black nr.KV black(nr.R) // L KV nr.L val resultLeft = tree.blackWithRight(newRight_left) - val resultRight = newRight_right.black + val resultRight = newRight_right.nn.black newRight.withLeftRight(resultLeft, resultRight) } else { @@ -423,8 +423,8 @@ private[collection] object RedBlackTree { } } - private[this] def upd[A, B, B1 >: B](tree: Tree[A, B], k: A, v: B1, overwrite: Boolean)(implicit ordering: Ordering[A]): Tree[A, B1] = if (tree eq null) { - RedTree(k, v, null, null) + private[this] def upd[A, B, B1 >: B](tree: Tree[A, B] | Null, k: A, v: B1, overwrite: Boolean)(implicit ordering: Ordering[A]): Tree[A, B1] = if (tree eq null) { + RedTree[A, B1](k, v, null, null) } else if (k.asInstanceOf[AnyRef] eq tree.key.asInstanceOf[AnyRef]) { if (overwrite) tree.withV(v) @@ -439,8 +439,8 @@ private[collection] object RedBlackTree { tree.withV(v) else tree } - private[this] def updNth[A, B, B1 >: B](tree: Tree[A, B], idx: Int, k: A, v: B1): Tree[A, B1] = if (tree eq null) { - RedTree(k, v, null, null) + private[this] def updNth[A, B, B1 >: B](tree: Tree[A, B] | Null, idx: Int, k: A, v: B1): Tree[A, B1] = if (tree eq null) { + RedTree(k, v, null: Tree[A, B1] | Null, null: Tree[A, B1] | Null) } else { val rank = count(tree.left) + 1 if (idx < rank) @@ -450,7 +450,7 @@ private[collection] object RedBlackTree { else tree } - private[this] def doFrom[A, B](tree: Tree[A, B], from: A)(implicit ordering: Ordering[A]): Tree[A, B] = { + private[this] def doFrom[A, B](tree: Tree[A, B] | Null, from: A)(implicit ordering: Ordering[A]): Tree[A, B] | Null = { if (tree eq null) return null if (ordering.lt(tree.key, from)) return doFrom(tree.right, from) val newLeft = doFrom(tree.left, from) @@ -458,7 +458,7 @@ private[collection] object RedBlackTree { else if (newLeft eq null) maybeBlacken(upd(tree.right, tree.key, tree.value, overwrite = false)) else join(newLeft, tree.key, tree.value, tree.right) } - private[this] def doTo[A, B](tree: Tree[A, B], to: A)(implicit ordering: Ordering[A]): Tree[A, B] = { + private[this] def doTo[A, B](tree: Tree[A, B] | Null, to: A)(implicit ordering: Ordering[A]): Tree[A, B] | Null = { if (tree eq null) return null if (ordering.lt(to, tree.key)) return doTo(tree.left, to) val newRight = doTo(tree.right, to) @@ -466,7 +466,7 @@ private[collection] object RedBlackTree { else if (newRight eq null) maybeBlacken(upd(tree.left, tree.key, tree.value, overwrite = false)) else join(tree.left, tree.key, tree.value, newRight) } - private[this] def doUntil[A, B](tree: Tree[A, B], until: A)(implicit ordering: Ordering[A]): Tree[A, B] = { + private[this] def doUntil[A, B](tree: Tree[A, B] | Null, until: A)(implicit ordering: Ordering[A]): Tree[A, B] | Null = { if (tree eq null) return null if (ordering.lteq(until, tree.key)) return doUntil(tree.left, until) val newRight = doUntil(tree.right, until) @@ -475,7 +475,7 @@ private[collection] object RedBlackTree { else join(tree.left, tree.key, tree.value, newRight) } - private[this] def doRange[A, B](tree: Tree[A, B], from: A, until: A)(implicit ordering: Ordering[A]): Tree[A, B] = { + private[this] def doRange[A, B](tree: Tree[A, B] | Null, from: A, until: A)(implicit ordering: Ordering[A]): Tree[A, B] | Null = { if (tree eq null) return null if (ordering.lt(tree.key, from)) return doRange(tree.right, from, until) if (ordering.lteq(until, tree.key)) return doRange(tree.left, from, until) @@ -487,7 +487,7 @@ private[collection] object RedBlackTree { else join(newLeft, tree.key, tree.value, newRight) } - private[this] def doDrop[A, B](tree: Tree[A, B], n: Int): Tree[A, B] = + private[this] def doDrop[A, B](tree: Tree[A, B] | Null, n: Int): Tree[A, B] | Null = if((tree eq null) || (n <= 0)) tree else if(n >= tree.count) null else { @@ -497,7 +497,7 @@ private[collection] object RedBlackTree { else join(doDrop(tree.left, n), tree.key, tree.value, tree.right) } - private[this] def doTake[A, B](tree: Tree[A, B], n: Int): Tree[A, B] = + private[this] def doTake[A, B](tree: Tree[A, B] | Null, n: Int): Tree[A, B] | Null = if((tree eq null) || (n <= 0)) null else if(n >= tree.count) tree else { @@ -507,7 +507,7 @@ private[collection] object RedBlackTree { else join(tree.left, tree.key, tree.value, doTake(tree.right, n-l-1)) } - private[this] def doSlice[A, B](tree: Tree[A, B], from: Int, until: Int): Tree[A, B] = + private[this] def doSlice[A, B](tree: Tree[A, B] | Null, from: Int, until: Int): Tree[A, B] | Null = if((tree eq null) || (from >= until) || (from >= tree.count) || (until <= 0)) null else if((from <= 0) && (until >= tree.count)) tree else { @@ -557,9 +557,9 @@ private[collection] object RedBlackTree { */ private[immutable] final class Tree[A, +B]( @(`inline` @getter @setter) private var _key: A, - @(`inline` @getter @setter) private var _value: AnyRef, - @(`inline` @getter @setter) private var _left: Tree[A, _], - @(`inline` @getter @setter) private var _right: Tree[A, _], + @(`inline` @getter @setter) private var _value: AnyRef | Null, + @(`inline` @getter @setter) private var _left: Tree[A, _] | Null, + @(`inline` @getter @setter) private var _right: Tree[A, _] | Null, @(`inline` @getter @setter) private var _count: Int) { @`inline` private[RedBlackTree] def isMutable: Boolean = (_count & colourMask) == 0 @@ -572,11 +572,11 @@ private[collection] object RedBlackTree { @`inline` private def mutableRetainingColour = _count & colourBit //inlined here to avoid outer object null checks - @`inline` private[RedBlackTree] final def sizeOf(tree:Tree[_,_]) = if (tree eq null) 0 else tree.count + @`inline` private[RedBlackTree] final def sizeOf(tree: Tree[_,_] | Null) = if (tree eq null) 0 else tree.count @`inline` private[immutable] final def key = _key @`inline` private[immutable] final def value = _value.asInstanceOf[B] - @`inline` private[immutable] final def left = _left.asInstanceOf[Tree[A, B]] - @`inline` private[immutable] final def right = _right.asInstanceOf[Tree[A, B]] + @`inline` private[immutable] final def left = _left.asInstanceOf[Tree[A, B] | Null] + @`inline` private[immutable] final def right = _right.asInstanceOf[Tree[A, B] | Null] //Note - only used in tests outside RedBlackTree @`inline` private[immutable] final def isBlack = _count < 0 //Note - only used in tests outside RedBlackTree @@ -590,12 +590,12 @@ private[collection] object RedBlackTree { if (isMutable) { var size = 1 if (_left ne null) { - _left.makeImmutable - size += _left.count + _left.nn.makeImmutable + size += _left.nn.count } if (_right ne null) { - _right.makeImmutable - size += _right.count + _right.nn.makeImmutable + size += _right.nn.count } _count |= size //retains colour } @@ -629,21 +629,21 @@ private[collection] object RedBlackTree { } else new Tree(_key, newValue.asInstanceOf[AnyRef], _left, _right, mutableRetainingColour) } - private[RedBlackTree] def mutableWithLeft[B1 >: B](newLeft: Tree[A, B1]): Tree[A, B1] = { + private[RedBlackTree] def mutableWithLeft[B1 >: B](newLeft: Tree[A, B1] | Null): Tree[A, B1] = { if (_left eq newLeft) this else if (isMutable) { _left = newLeft this } else new Tree(_key, _value, newLeft, _right, mutableRetainingColour) } - private[RedBlackTree] def mutableWithRight[B1 >: B](newRight: Tree[A, B1]): Tree[A, B1] = { + private[RedBlackTree] def mutableWithRight[B1 >: B](newRight: Tree[A, B1] | Null): Tree[A, B1] = { if (_right eq newRight) this else if (isMutable) { _right = newRight this } else new Tree(_key, _value, _left, newRight, mutableRetainingColour) } - private[RedBlackTree] def mutableWithLeftRight[B1 >: B](newLeft: Tree[A, B1], newRight: Tree[A, B1]): Tree[A, B1] = { + private[RedBlackTree] def mutableWithLeftRight[B1 >: B](newLeft: Tree[A, B1] | Null, newRight: Tree[A, B1] | Null): Tree[A, B1] = { if ((_left eq newLeft) && (_right eq newRight)) this else if (isMutable) { _left = newLeft @@ -651,7 +651,7 @@ private[collection] object RedBlackTree { this } else new Tree(_key, _value, newLeft, newRight, mutableRetainingColour) } - private[RedBlackTree] def mutableBlackWithLeft[B1 >: B](newLeft: Tree[A, B1]): Tree[A, B1] = { + private[RedBlackTree] def mutableBlackWithLeft[B1 >: B](newLeft: Tree[A, B1] | Null): Tree[A, B1] = { if ((_left eq newLeft) && isBlack) this else if (isMutable) { _count = initialBlackCount @@ -659,7 +659,7 @@ private[collection] object RedBlackTree { this } else new Tree(_key, _value, newLeft, _right, initialBlackCount) } - private[RedBlackTree] def mutableBlackWithRight[B1 >: B](newRight: Tree[A, B1]): Tree[A, B1] = { + private[RedBlackTree] def mutableBlackWithRight[B1 >: B](newRight: Tree[A, B1] | Null): Tree[A, B1] = { if ((_right eq newRight) && isBlack) this else if (isMutable) { _count = initialBlackCount @@ -690,7 +690,7 @@ private[collection] object RedBlackTree { else new Tree(_key, newValue.asInstanceOf[AnyRef], _left, _right, _count) } - private[RedBlackTree] def withLeft[B1 >: B](newLeft: Tree[A, B1]): Tree[A, B1] = { + private[RedBlackTree] def withLeft[B1 >: B](newLeft: Tree[A, B1] | Null): Tree[A, B1] = { //assertNotMutable(this) //assertNotMutable(newLeft) if (newLeft eq _left) this @@ -699,7 +699,7 @@ private[collection] object RedBlackTree { new Tree(key, value.asInstanceOf[AnyRef], newLeft, _right, (_count & colourBit) | size) } } - private[RedBlackTree] def withRight[B1 >: B](newRight: Tree[A, B1]): Tree[A, B1] = { + private[RedBlackTree] def withRight[B1 >: B](newRight: Tree[A, B1] | Null): Tree[A, B1] = { //assertNotMutable(this) //assertNotMutable(newRight) if (newRight eq _right) this @@ -708,7 +708,7 @@ private[collection] object RedBlackTree { new Tree(key, value.asInstanceOf[AnyRef], _left, newRight, (_count & colourBit) | size) } } - private[RedBlackTree] def blackWithLeft[B1 >: B](newLeft: Tree[A, B1]): Tree[A, B1] = { + private[RedBlackTree] def blackWithLeft[B1 >: B](newLeft: Tree[A, B1] | Null): Tree[A, B1] = { //assertNotMutable(this) //assertNotMutable(newLeft) if ((newLeft eq _left) && isBlack) this @@ -717,7 +717,7 @@ private[collection] object RedBlackTree { new Tree(key, value.asInstanceOf[AnyRef], newLeft, _right, initialBlackCount | size) } } - private[RedBlackTree] def redWithLeft[B1 >: B](newLeft: Tree[A, B1]): Tree[A, B1] = { + private[RedBlackTree] def redWithLeft[B1 >: B](newLeft: Tree[A, B1] | Null): Tree[A, B1] = { //assertNotMutable(this) //assertNotMutable(newLeft) if ((newLeft eq _left) && isRed) this @@ -726,7 +726,7 @@ private[collection] object RedBlackTree { new Tree(key, value.asInstanceOf[AnyRef], newLeft, _right, initialRedCount | size) } } - private[RedBlackTree] def blackWithRight[B1 >: B](newRight: Tree[A, B1]): Tree[A, B1] = { + private[RedBlackTree] def blackWithRight[B1 >: B](newRight: Tree[A, B1] | Null): Tree[A, B1] = { //assertNotMutable(this) //assertNotMutable(newRight) if ((newRight eq _right) && isBlack) this @@ -735,7 +735,7 @@ private[collection] object RedBlackTree { new Tree(key, value.asInstanceOf[AnyRef], _left, newRight, initialBlackCount | size) } } - private[RedBlackTree] def redWithRight[B1 >: B](newRight: Tree[A, B1]): Tree[A, B1] = { + private[RedBlackTree] def redWithRight[B1 >: B](newRight: Tree[A, B1] | Null): Tree[A, B1] = { //assertNotMutable(this) //assertNotMutable(newLeft) if ((newRight eq _right) && isRed) this @@ -744,7 +744,7 @@ private[collection] object RedBlackTree { new Tree(key, value.asInstanceOf[AnyRef], _left, newRight, initialRedCount | size) } } - private[RedBlackTree] def withLeftRight[B1 >: B](newLeft: Tree[A, B1], newRight: Tree[A, B1]): Tree[A, B1] = { + private[RedBlackTree] def withLeftRight[B1 >: B](newLeft: Tree[A, B1] | Null, newRight: Tree[A, B1] | Null): Tree[A, B1] = { //assertNotMutable(this) //assertNotMutable(newLeft) //assertNotMutable(newRight) @@ -754,7 +754,7 @@ private[collection] object RedBlackTree { new Tree(key, value.asInstanceOf[AnyRef], newLeft, newRight, (_count & colourBit) | size) } } - private[RedBlackTree] def redWithLeftRight[B1 >: B](newLeft: Tree[A, B1], newRight: Tree[A, B1]): Tree[A, B1] = { + private[RedBlackTree] def redWithLeftRight[B1 >: B](newLeft: Tree[A, B1] | Null, newRight: Tree[A, B1] | Null): Tree[A, B1] = { //assertNotMutable(this) //assertNotMutable(newLeft) //assertNotMutable(newRight) @@ -764,7 +764,7 @@ private[collection] object RedBlackTree { new Tree(key, value.asInstanceOf[AnyRef], newLeft, newRight, initialRedCount | size) } } - private[RedBlackTree] def blackWithLeftRight[B1 >: B](newLeft: Tree[A, B1], newRight: Tree[A, B1]): Tree[A, B1] = { + private[RedBlackTree] def blackWithLeftRight[B1 >: B](newLeft: Tree[A, B1] | Null, newRight: Tree[A, B1] | Null): Tree[A, B1] = { //assertNotMutable(this) //assertNotMutable(newLeft) //assertNotMutable(newRight) @@ -782,25 +782,25 @@ private[collection] object RedBlackTree { private[RedBlackTree] final val initialBlackCount = colourBit private[RedBlackTree] final val initialRedCount = 0 - @`inline` private[RedBlackTree] def mutableRedTree[A, B](key: A, value: B, left: Tree[A, B], right: Tree[A, B]) = new Tree[A,B](key, value.asInstanceOf[AnyRef], left, right, initialRedCount) - @`inline` private[RedBlackTree] def mutableBlackTree[A, B](key: A, value: B, left: Tree[A, B], right: Tree[A, B]) = new Tree[A,B](key, value.asInstanceOf[AnyRef], left, right, initialBlackCount) + @`inline` private[RedBlackTree] def mutableRedTree[A, B](key: A, value: B, left: Tree[A, B] | Null, right: Tree[A, B] | Null) = new Tree[A,B](key, value.asInstanceOf[AnyRef], left, right, initialRedCount) + @`inline` private[RedBlackTree] def mutableBlackTree[A, B](key: A, value: B, left: Tree[A, B] | Null, right: Tree[A, B] | Null) = new Tree[A,B](key, value.asInstanceOf[AnyRef], left, right, initialBlackCount) /** create a new immutable red tree. * left and right may be null */ - private[immutable] def RedTree[A, B](key: A, value: B, left: Tree[A, B], right: Tree[A, B]): Tree[A, B] = { + private[immutable] def RedTree[A, B](key: A, value: B, left: Tree[A, B] | Null, right: Tree[A, B] | Null): Tree[A, B] = { //assertNotMutable(left) //assertNotMutable(right) val size = sizeOf(left) + sizeOf(right) + 1 new Tree(key, value.asInstanceOf[AnyRef], left, right, initialRedCount | size) } - private[immutable] def BlackTree[A, B](key: A, value: B, left: Tree[A, B], right: Tree[A, B]): Tree[A, B] = { + private[immutable] def BlackTree[A, B](key: A, value: B, left: Tree[A, B] | Null, right: Tree[A, B] | Null): Tree[A, B] = { //assertNotMutable(left) //assertNotMutable(right) val size = sizeOf(left) + sizeOf(right) + 1 new Tree(key, value.asInstanceOf[AnyRef], left, right, initialBlackCount | size) } - @`inline` private def sizeOf(tree:Tree[_,_]) = if (tree eq null) 0 else tree.count + @`inline` private def sizeOf(tree:Tree[_,_] | Null) = if (tree eq null) 0 else tree.count //immutable APIs //assertions - uncomment decls and callers when changing functionality // private def devTimeAssert(assertion: Boolean) = { @@ -810,7 +810,7 @@ private[collection] object RedBlackTree { // private def assertNotMutable(t:Tree[_,_]) = { // devTimeAssert ((t eq null) || t.count > 0) // } - private[this] abstract class TreeIterator[A, B, R](root: Tree[A, B], start: Option[A])(protected implicit val ordering: Ordering[A]) extends AbstractIterator[R] { + private[this] abstract class TreeIterator[A, B, R](root: Tree[A, B] | Null, start: Option[A])(protected implicit val ordering: Ordering[A]) extends AbstractIterator[R] { protected[this] def nextResult(tree: Tree[A, B]): R override def hasNext: Boolean = lookahead ne null @@ -825,21 +825,21 @@ private[collection] object RedBlackTree { } @tailrec - protected final def findLeftMostOrPopOnEmpty(tree: Tree[A, B]): Tree[A, B] = + protected final def findLeftMostOrPopOnEmpty(tree: Tree[A, B] | Null): Tree[A, B] | Null = if (tree eq null) popNext() else if (tree.left eq null) tree else findLeftMostOrPopOnEmpty(goLeft(tree)) @`inline` private[this] def pushNext(tree: Tree[A, B]): Unit = { - stackOfNexts(index) = tree + stackOfNexts.nn(index) = tree index += 1 } - @`inline` protected final def popNext(): Tree[A, B] = if (index == 0) null else { + @`inline` protected final def popNext(): Tree[A, B] | Null = if (index == 0) null else { index -= 1 - stackOfNexts(index) + stackOfNexts.nn(index) } - protected[this] val stackOfNexts = if (root eq null) null else { + protected[this] val stackOfNexts: Array[Tree[A, B] | Null] | Null = if (root eq null) null else { /* * According to "Ralf Hinze. Constructing red-black trees" [https://www.cs.ox.ac.uk/ralf.hinze/publications/#P5] * the maximum height of a red-black tree is 2*log_2(n + 2) - 2. @@ -850,10 +850,10 @@ private[collection] object RedBlackTree { * we potentially do so in `startFrom`. */ val maximumHeight = 2 * (32 - Integer.numberOfLeadingZeros(root.count + 2 - 1)) - 2 - new Array[Tree[A, B]](maximumHeight) + new Array[Tree[A, B] | Null](maximumHeight) } private[this] var index = 0 - protected var lookahead: Tree[A, B] = if (start.isDefined) startFrom(start.get) else findLeftMostOrPopOnEmpty(root) + protected var lookahead: Tree[A, B] | Null = if (start.isDefined) startFrom(start.get) else findLeftMostOrPopOnEmpty(root) /** * Find the leftmost subtree whose key is equal to the given key, or if no such thing, @@ -861,8 +861,8 @@ private[collection] object RedBlackTree { * to the ordering. Along the way build up the iterator's path stack so that "next" * functionality works. */ - private[this] def startFrom(key: A) : Tree[A,B] = if (root eq null) null else { - @tailrec def find(tree: Tree[A, B]): Tree[A, B] = + private[this] def startFrom(key: A) : Tree[A,B] | Null = if (root eq null) null else { + @tailrec def find(tree: Tree[A, B] | Null): Tree[A, B] | Null = if (tree eq null) popNext() else find( if (ordering.lteq(key, tree.key)) goLeft(tree) @@ -889,10 +889,10 @@ private[collection] object RedBlackTree { this.lookahead = this.popNext() that.lookahead = that.popNext() } else { - equal = (this.lookahead.key.asInstanceOf[AnyRef] eq that.lookahead.key.asInstanceOf[AnyRef]) || - ordering.equiv(this.lookahead.key, that.lookahead.key) - this.lookahead = this.findLeftMostOrPopOnEmpty(this.goRight(this.lookahead)) - that.lookahead = that.findLeftMostOrPopOnEmpty(that.goRight(that.lookahead)) + equal = (this.lookahead.nn.key.asInstanceOf[AnyRef] eq that.lookahead.nn.key.asInstanceOf[AnyRef]) || + ordering.equiv(this.lookahead.nn.key, that.lookahead.nn.key) + this.lookahead = this.findLeftMostOrPopOnEmpty(this.goRight(this.lookahead.nn)) + that.lookahead = that.findLeftMostOrPopOnEmpty(that.goRight(that.lookahead.nn)) } } equal && (this.lookahead eq null) && (that.lookahead eq null) @@ -904,9 +904,9 @@ private[collection] object RedBlackTree { this.lookahead = this.popNext() that.lookahead = that.popNext() } else { - equal = this.lookahead.value == that.lookahead.value - this.lookahead = this.findLeftMostOrPopOnEmpty(this.goRight(this.lookahead)) - that.lookahead = that.findLeftMostOrPopOnEmpty(that.goRight(that.lookahead)) + equal = this.lookahead.nn.value == that.lookahead.nn.value + this.lookahead = this.findLeftMostOrPopOnEmpty(this.goRight(this.lookahead.nn)) + that.lookahead = that.findLeftMostOrPopOnEmpty(that.goRight(that.lookahead.nn)) } } equal && (this.lookahead eq null) && (that.lookahead eq null) @@ -918,31 +918,31 @@ private[collection] object RedBlackTree { this.lookahead = this.popNext() that.lookahead = that.popNext() } else { - equal = ((this.lookahead.key.asInstanceOf[AnyRef] eq that.lookahead.key.asInstanceOf[AnyRef]) || - ordering.equiv(this.lookahead.key, that.lookahead.key)) && this.lookahead.value == that.lookahead.value - this.lookahead = this.findLeftMostOrPopOnEmpty(this.goRight(this.lookahead)) - that.lookahead = that.findLeftMostOrPopOnEmpty(that.goRight(that.lookahead)) + equal = ((this.lookahead.nn.key.asInstanceOf[AnyRef] eq that.lookahead.nn.key.asInstanceOf[AnyRef]) || + ordering.equiv(this.lookahead.nn.key, that.lookahead.nn.key)) && this.lookahead.nn.value == that.lookahead.nn.value + this.lookahead = this.findLeftMostOrPopOnEmpty(this.goRight(this.lookahead.nn)) + that.lookahead = that.findLeftMostOrPopOnEmpty(that.goRight(that.lookahead.nn)) } } equal && (this.lookahead eq null) && (that.lookahead eq null) } } - private[this] class EntriesIterator[A: Ordering, B](tree: Tree[A, B], focus: Option[A]) extends TreeIterator[A, B, (A, B)](tree, focus) { + private[this] class EntriesIterator[A: Ordering, B](tree: Tree[A, B] | Null, focus: Option[A]) extends TreeIterator[A, B, (A, B)](tree, focus) { override def nextResult(tree: Tree[A, B]) = (tree.key, tree.value) } - private[this] class KeysIterator[A: Ordering, B](tree: Tree[A, B], focus: Option[A]) extends TreeIterator[A, B, A](tree, focus) { + private[this] class KeysIterator[A: Ordering, B](tree: Tree[A, B] | Null, focus: Option[A]) extends TreeIterator[A, B, A](tree, focus) { override def nextResult(tree: Tree[A, B]) = tree.key } - private[this] class ValuesIterator[A: Ordering, B](tree: Tree[A, B], focus: Option[A]) extends TreeIterator[A, B, B](tree, focus) { + private[this] class ValuesIterator[A: Ordering, B](tree: Tree[A, B] | Null, focus: Option[A]) extends TreeIterator[A, B, B](tree, focus) { override def nextResult(tree: Tree[A, B]) = tree.value } /** Build a Tree suitable for a TreeSet from an ordered sequence of keys */ - def fromOrderedKeys[A](xs: Iterator[A]^, size: Int): Tree[A, Null] = { + def fromOrderedKeys[A](xs: Iterator[A]^, size: Int): Tree[A, Null] | Null = { val maxUsedDepth = 32 - Integer.numberOfLeadingZeros(size) // maximum depth of non-leaf nodes - def f(level: Int, size: Int): Tree[A, Null] = size match { + def f(level: Int, size: Int): Tree[A, Null] | Null = size match { case 0 => null case 1 => mkTree(level != maxUsedDepth || level == 1, xs.next(), null, null, null) case n => @@ -956,9 +956,9 @@ private[collection] object RedBlackTree { } /** Build a Tree suitable for a TreeMap from an ordered sequence of key/value pairs */ - def fromOrderedEntries[A, B](xs: Iterator[(A, B)]^, size: Int): Tree[A, B] = { + def fromOrderedEntries[A, B](xs: Iterator[(A, B)]^, size: Int): Tree[A, B] | Null = { val maxUsedDepth = 32 - Integer.numberOfLeadingZeros(size) // maximum depth of non-leaf nodes - def f(level: Int, size: Int): Tree[A, B] = size match { + def f(level: Int, size: Int): Tree[A, B] | Null = size match { case 0 => null case 1 => val (k, v) = xs.next() @@ -973,7 +973,7 @@ private[collection] object RedBlackTree { f(1, size) } - def transform[A, B, C](t: Tree[A, B], f: (A, B) => C): Tree[A, C] = + def transform[A, B, C](t: Tree[A, B] | Null, f: (A, B) => C): Tree[A, C] | Null = if(t eq null) null else { val k = t.key @@ -989,8 +989,8 @@ private[collection] object RedBlackTree { else mkTree(t.isBlack, k, v2, l2, r2) } - def filterEntries[A, B](t: Tree[A, B], f: (A, B) => Boolean): Tree[A, B] = if(t eq null) null else { - def fk(t: Tree[A, B]): Tree[A, B] = { + def filterEntries[A, B](t: Tree[A, B] | Null, f: (A, B) => Boolean): Tree[A, B] | Null = if(t eq null) null else { + def fk(t: Tree[A, B]): Tree[A, B] | Null = { val k = t.key val v = t.value val l = t.left @@ -1007,17 +1007,17 @@ private[collection] object RedBlackTree { private[this] val null2 = (null, null) - def partitionEntries[A, B](t: Tree[A, B], p: (A, B) => Boolean): (Tree[A, B], Tree[A, B]) = if(t eq null) (null, null) else { + def partitionEntries[A, B](t: Tree[A, B] | Null, p: (A, B) => Boolean): (Tree[A, B] | Null, Tree[A, B] | Null) = if(t eq null) (null, null) else { if (t eq null) null2 else { object partitioner { - var tmpk, tmpd = null: Tree[A, B] // shared vars to avoid returning tuples from fk + var tmpk, tmpd = null: Tree[A, B] | Null // shared vars to avoid returning tuples from fk def fk(t: Tree[A, B]): Unit = { val k = t.key val v = t.value - val l = t.left - val r = t.right - var l2k, l2d, r2k, r2d = null: Tree[A, B] + var l = t.left + var r = t.right + var l2k, l2d, r2k, r2d = null: Tree[A, B] | Null if (l ne null) { fk(l) l2k = tmpk @@ -1051,7 +1051,7 @@ private[collection] object RedBlackTree { // Constructing Red-Black Trees, Ralf Hinze: [[https://www.cs.ox.ac.uk/ralf.hinze/publications/WAAAPL99b.ps.gz]] // Red-Black Trees in a Functional Setting, Chris Okasaki: [[https://wiki.rice.edu/confluence/download/attachments/2761212/Okasaki-Red-Black.pdf]] */ - private[this] def del[A, B](tree: Tree[A, B], k: A)(implicit ordering: Ordering[A]): Tree[A, B] = if (tree eq null) null else { + private[this] def del[A, B](tree: Tree[A, B] | Null, k: A)(implicit ordering: Ordering[A]): Tree[A, B] | Null = if (tree eq null) null else { val cmp = ordering.compare(k, tree.key) if (cmp < 0) { val newLeft = del(tree.left, k) @@ -1066,57 +1066,57 @@ private[collection] object RedBlackTree { } else append(tree.left, tree.right) } - private[this] def balance[A, B](tree: Tree[A,B], tl: Tree[A, B], tr: Tree[A, B]): Tree[A, B] = + private[this] def balance[A, B](tree: Tree[A,B], tl: Tree[A, B] | Null, tr: Tree[A, B] | Null): Tree[A, B] = if (isRedTree(tl)) { - if (isRedTree(tr)) tree.redWithLeftRight(tl.black, tr.black) - else if (isRedTree(tl.left)) tl.withLeftRight(tl.left.black, tree.blackWithLeftRight(tl.right, tr)) - else if (isRedTree(tl.right)) tl.right.withLeftRight(tl.blackWithRight(tl.right.left), tree.blackWithLeftRight(tl.right.right, tr)) + if (isRedTree(tr)) tree.redWithLeftRight(tl.nn.black, tr.nn.black) + else if (isRedTree(tl.nn.left)) tl.nn.withLeftRight(tl.nn.left.nn.black, tree.blackWithLeftRight(tl.nn.right, tr)) + else if (isRedTree(tl.nn.right)) tl.nn.right.nn.withLeftRight(tl.nn.blackWithRight(tl.nn.right.nn.left), tree.blackWithLeftRight(tl.nn.right.nn.right, tr)) else tree.blackWithLeftRight(tl, tr) } else if (isRedTree(tr)) { - if (isRedTree(tr.right)) tr.withLeftRight(tree.blackWithLeftRight(tl, tr.left), tr.right.black) - else if (isRedTree(tr.left)) tr.left.withLeftRight(tree.blackWithLeftRight(tl, tr.left.left), tr.blackWithLeftRight(tr.left.right, tr.right)) + if (isRedTree(tr.nn.right)) tr.nn.withLeftRight(tree.blackWithLeftRight(tl, tr.nn.left), tr.nn.right.nn.black) + else if (isRedTree(tr.nn.left)) tr.nn.left.nn.withLeftRight(tree.blackWithLeftRight(tl, tr.nn.left.nn.left), tr.nn.blackWithLeftRight(tr.nn.left.nn.right, tr.nn.right)) else tree.blackWithLeftRight(tl, tr) } else tree.blackWithLeftRight(tl, tr) - private[this] def balLeft[A, B](tree: Tree[A,B], tl: Tree[A, B], tr: Tree[A, B]): Tree[A, B] = - if (isRedTree(tl)) tree.redWithLeftRight(tl.black, tr) - else if (isBlackTree(tr)) balance(tree, tl, tr.red) - else if (isRedTree(tr) && isBlackTree(tr.left)) - tr.left.redWithLeftRight(tree.blackWithLeftRight(tl, tr.left.left), balance(tr, tr.left.right, tr.right.red)) + private[this] def balLeft[A, B](tree: Tree[A,B], tl: Tree[A, B] | Null, tr: Tree[A, B] | Null): Tree[A, B] = + if (isRedTree(tl)) tree.redWithLeftRight(tl.nn.black, tr) + else if (isBlackTree(tr)) balance(tree, tl, tr.nn.red) + else if (isRedTree(tr) && isBlackTree(tr.nn.left)) + tr.nn.left.nn.redWithLeftRight(tree.blackWithLeftRight(tl, tr.nn.left.nn.left), balance(tr.nn, tr.nn.left.nn.right, tr.nn.right.nn.red)) else sys.error("Defect: invariance violation") - private[this] def balRight[A, B](tree: Tree[A,B], tl: Tree[A, B], tr: Tree[A, B]): Tree[A, B] = - if (isRedTree(tr)) tree.redWithLeftRight(tl, tr.black) - else if (isBlackTree(tl)) balance(tree, tl.red, tr) - else if (isRedTree(tl) && isBlackTree(tl.right)) - tl.right.redWithLeftRight(balance(tl, tl.left.red, tl.right.left), tree.blackWithLeftRight(tl.right.right, tr)) + private[this] def balRight[A, B](tree: Tree[A,B], tl: Tree[A, B] | Null, tr: Tree[A, B] | Null): Tree[A, B] = + if (isRedTree(tr)) tree.redWithLeftRight(tl, tr.nn.black) + else if (isBlackTree(tl)) balance(tree, tl.nn.red, tr) + else if (isRedTree(tl) && isBlackTree(tl.nn.right)) + tl.nn.right.nn.redWithLeftRight(balance(tl.nn, tl.nn.left.nn.red, tl.nn.right.nn.left), tree.blackWithLeftRight(tl.nn.right.nn.right, tr)) else sys.error("Defect: invariance violation") /** `append` is similar to `join2` but requires that both subtrees have the same black height */ - private[this] def append[A, B](tl: Tree[A, B], tr: Tree[A, B]): Tree[A, B] = { + private[this] def append[A, B](tl: Tree[A, B] | Null, tr: Tree[A, B] | Null): Tree[A, B] | Null = { if (tl eq null) tr else if (tr eq null) tl else if (tl.isRed) { - if (tr.isRed) { - //tl is red, tr is red - val bc = append(tl.right, tr.left) - if (isRedTree(bc)) bc.withLeftRight(tl.withRight(bc.left), tr.withLeft(bc.right)) - else tl.withRight(tr.withLeft(bc)) - } else { - //tl is red, tr is black - tl.withRight(append(tl.right, tr)) - } - } else { - if (tr.isBlack) { - //tl is black tr is black - val bc = append(tl.right, tr.left) - if (isRedTree(bc)) bc.withLeftRight(tl.withRight(bc.left), tr.withLeft(bc.right)) - else balLeft(tl, tl.left, tr.withLeft(bc)) - } else { - //tl is black tr is red - tr.withLeft(append(tl, tr.left)) - } - } + if (tr.isRed) { + //tl is red, tr is red + val bc = append(tl.right, tr.left) + if (isRedTree(bc)) bc.nn.withLeftRight(tl.withRight(bc.nn.left), tr.withLeft(bc.nn.right)) + else tl.withRight(tr.withLeft(bc)) + } else { + //tl is red, tr is black + tl.withRight(append(tl.right, tr)) + } + } else { + if (tr.isBlack) { + //tl is black tr is black + val bc = append(tl.right, tr.left) + if (isRedTree(bc)) bc.nn.withLeftRight(tl.withRight(bc.nn.left), tr.withLeft(bc.nn.right)) + else balLeft(tl, tl.left, tr.withLeft(bc)) + } else { + //tl is black tr is red + tr.withLeft(append(tl, tr.left)) + } + } } @@ -1125,52 +1125,54 @@ private[collection] object RedBlackTree { // of child nodes from it. Where possible the black height is used directly instead of deriving the rank from it. // Our trees are supposed to have a black root so we always blacken as the last step of union/intersect/difference. - def union[A, B](t1: Tree[A, B], t2: Tree[A, B])(implicit ordering: Ordering[A]): Tree[A, B] = blacken(_union(t1, t2)) + def union[A, B](t1: Tree[A, B] | Null, t2: Tree[A, B] | Null)(implicit ordering: Ordering[A]): Tree[A, B] | Null = blacken(_union(t1, t2)) - def intersect[A, B](t1: Tree[A, B], t2: Tree[A, B])(implicit ordering: Ordering[A]): Tree[A, B] = blacken(_intersect(t1, t2)) + def intersect[A, B](t1: Tree[A, B] | Null, t2: Tree[A, B] | Null)(implicit ordering: Ordering[A]): Tree[A, B] | Null = blacken(_intersect(t1, t2)) - def difference[A, B](t1: Tree[A, B], t2: Tree[A, _])(implicit ordering: Ordering[A]): Tree[A, B] = + def difference[A, B](t1: Tree[A, B] | Null, t2: Tree[A, _] | Null)(implicit ordering: Ordering[A]): Tree[A, B] | Null = blacken(_difference(t1, t2.asInstanceOf[Tree[A, B]])) /** Compute the rank from a tree and its black height */ - @`inline` private[this] def rank(t: Tree[_, _], bh: Int): Int = { + @`inline` private[this] def rank(t: Tree[_, _] | Null, bh: Int): Int = { if(t eq null) 0 else if(t.isBlack) 2*(bh-1) else 2*bh-1 } - private[this] def joinRight[A, B](tl: Tree[A, B], k: A, v: B, tr: Tree[A, B], bhtl: Int, rtr: Int): Tree[A, B] = { + private[this] def joinRight[A, B](tl: Tree[A, B] | Null, k: A, v: B, tr: Tree[A, B] | Null, bhtl: Int, rtr: Int): Tree[A, B] = { val rtl = rank(tl, bhtl) if(rtl == (rtr/2)*2) RedTree(k, v, tl, tr) else { + val tlnn = tl.nn val tlBlack = isBlackTree(tl) val bhtlr = if(tlBlack) bhtl-1 else bhtl - val ttr = joinRight(tl.right, k, v, tr, bhtlr, rtr) + val ttr = joinRight(tlnn.right, k, v, tr, bhtlr, rtr) if(tlBlack && isRedTree(ttr) && isRedTree(ttr.right)) RedTree(ttr.key, ttr.value, - BlackTree(tl.key, tl.value, tl.left, ttr.left), - ttr.right.black) - else mkTree(tlBlack, tl.key, tl.value, tl.left, ttr) + BlackTree(tlnn.key, tlnn.value, tlnn.left, ttr.left), + ttr.right.nn.black) + else mkTree(tlBlack, tlnn.key, tlnn.value, tlnn.left, ttr) } } - private[this] def joinLeft[A, B](tl: Tree[A, B], k: A, v: B, tr: Tree[A, B], rtl: Int, bhtr: Int): Tree[A, B] = { + private[this] def joinLeft[A, B](tl: Tree[A, B] | Null, k: A, v: B, tr: Tree[A, B] | Null, rtl: Int, bhtr: Int): Tree[A, B] = { val rtr = rank(tr, bhtr) if(rtr == (rtl/2)*2) RedTree(k, v, tl, tr) else { + val trnn = tr.nn val trBlack = isBlackTree(tr) val bhtrl = if(trBlack) bhtr-1 else bhtr - val ttl = joinLeft(tl, k, v, tr.left, rtl, bhtrl) + val ttl = joinLeft(tl, k, v, trnn.left, rtl, bhtrl) if(trBlack && isRedTree(ttl) && isRedTree(ttl.left)) RedTree(ttl.key, ttl.value, - ttl.left.black, - BlackTree(tr.key, tr.value, ttl.right, tr.right)) - else mkTree(trBlack, tr.key, tr.value, ttl, tr.right) + ttl.left.nn.black, + BlackTree(trnn.key, trnn.value, ttl.right, trnn.right)) + else mkTree(trBlack, trnn.key, trnn.value, ttl, trnn.right) } } - private[this] def join[A, B](tl: Tree[A, B], k: A, v: B, tr: Tree[A, B]): Tree[A, B] = { - @tailrec def h(t: Tree[_, _], i: Int): Int = + private[this] def join[A, B](tl: Tree[A, B] | Null, k: A, v: B, tr: Tree[A, B] | Null): Tree[A, B] = { + @tailrec def h(t: Tree[_, _] | Null, i: Int): Int = if(t eq null) i+1 else h(t.left, if(t.isBlack) i+1 else i) val bhtl = h(tl, 0) val bhtr = h(tr, 0) @@ -1185,7 +1187,7 @@ private[collection] object RedBlackTree { } else mkTree(isRedTree(tl) || isRedTree(tr), k, v, tl, tr) } - private[this] def split[A, B](t: Tree[A, B], k2: A)(implicit ordering: Ordering[A]): (Tree[A, B], Tree[A, B], Tree[A, B], A) = + private[this] def split[A, B](t: Tree[A, B] | Null, k2: A)(implicit ordering: Ordering[A]): (Tree[A, B] | Null, Tree[A, B] | Null, Tree[A, B] | Null, A) = if(t eq null) (null, null, null, k2) else { val cmp = ordering.compare(k2, t.key) @@ -1200,13 +1202,13 @@ private[collection] object RedBlackTree { } private[this] def splitLast[A, B](t: Tree[A, B]): (Tree[A, B], A, B) = - if(t.right eq null) (t.left, t.key, t.value) + if(t.right eq null) (t.left.nn, t.key, t.value) else { - val (tt, kk, vv) = splitLast(t.right) + val (tt, kk, vv) = splitLast(t.right.nn) (join(t.left, t.key, t.value, tt), kk, vv) } - private[this] def join2[A, B](tl: Tree[A, B], tr: Tree[A, B]): Tree[A, B] = + private[this] def join2[A, B](tl: Tree[A, B] | Null, tr: Tree[A, B] | Null): Tree[A, B] | Null = if(tl eq null) tr else if(tr eq null) tl else { @@ -1214,7 +1216,7 @@ private[collection] object RedBlackTree { join(ttl, k, v, tr) } - private[this] def _union[A, B](t1: Tree[A, B], t2: Tree[A, B])(implicit ordering: Ordering[A]): Tree[A, B] = + private[this] def _union[A, B](t1: Tree[A, B] | Null, t2: Tree[A, B] | Null)(implicit ordering: Ordering[A]): Tree[A, B] | Null = if((t1 eq null) || (t1 eq t2)) t2 else if(t2 eq null) t1 else { @@ -1224,7 +1226,7 @@ private[collection] object RedBlackTree { join(tl, k1, t2.value, tr) } - private[this] def _intersect[A, B](t1: Tree[A, B], t2: Tree[A, B])(implicit ordering: Ordering[A]): Tree[A, B] = + private[this] def _intersect[A, B](t1: Tree[A, B] | Null, t2: Tree[A, B] | Null)(implicit ordering: Ordering[A]): Tree[A, B] | Null = if((t1 eq null) || (t2 eq null)) null else if (t1 eq t2) t1 else { @@ -1235,7 +1237,7 @@ private[collection] object RedBlackTree { else join2(tl, tr) } - private[this] def _difference[A, B](t1: Tree[A, B], t2: Tree[A, B])(implicit ordering: Ordering[A]): Tree[A, B] = + private[this] def _difference[A, B](t1: Tree[A, B] | Null, t2: Tree[A, B] | Null)(implicit ordering: Ordering[A]): Tree[A, B] | Null = if((t1 eq null) || (t2 eq null)) t1 else if (t1 eq t2) null else { @@ -1245,3 +1247,4 @@ private[collection] object RedBlackTree { join2(tl, tr) } } + diff --git a/library/src/scala/collection/immutable/Stream.scala b/library/src/scala/collection/immutable/Stream.scala index b31c5c8018c6..941e4ff5d158 100644 --- a/library/src/scala/collection/immutable/Stream.scala +++ b/library/src/scala/collection/immutable/Stream.scala @@ -23,6 +23,8 @@ import scala.annotation.unchecked.uncheckedVariance import scala.collection.generic.SerializeEnd import scala.collection.mutable.{ArrayBuffer, StringBuilder} import scala.language.implicitConversions +import scala.runtime.ScalaRunTime.nullForGC + import Stream.cons @deprecated("Use LazyListIterable (which is fully lazy) instead of Stream (which has a lazy tail only)", "2.13.0") @@ -396,13 +398,13 @@ object Stream extends SeqFactory[Stream] { final class Cons[A](override val head: A, tl: => Stream[A]) extends Stream[A] { override def isEmpty: Boolean = false @volatile private[this] var tlVal: Stream[A] = _ - @volatile private[this] var tlGen = () => tl + @volatile private[this] var tlGen: (() => Stream[A]) | Null = () => tl protected def tailDefined: Boolean = tlGen eq null override def tail: Stream[A] = { if (!tailDefined) synchronized { if (!tailDefined) { - tlVal = tlGen() + tlVal = tlGen.nn() tlGen = null } } @@ -479,8 +481,8 @@ object Stream extends SeqFactory[Stream] { new WithFilter[A](l, p) private[this] final class WithFilter[A](l: Stream[A] @uncheckedVariance, p: A => Boolean) extends collection.WithFilter[A, Stream] { - private[this] var s = l // set to null to allow GC after filtered - private[this] lazy val filtered: Stream[A] = { val f = s.filter(p); s = null.asInstanceOf[Stream[A]]; f } // don't set to null if throw during filter + private[this] var s: Stream[A] = l // set to null to allow GC after filtered + private[this] lazy val filtered: Stream[A] = { val f = s.filter(p); s = nullForGC[Stream[A]]; f } // don't set to null if throw during filter def map[B](f: A => B): Stream[B] = filtered.map(f) def flatMap[B](f: A => IterableOnce[B]): Stream[B] = filtered.flatMap(f) def foreach[U](f: A => U): Unit = filtered.foreach(f) diff --git a/library/src/scala/collection/immutable/TreeMap.scala b/library/src/scala/collection/immutable/TreeMap.scala index 84ca78c48b75..810c01ca2e5f 100644 --- a/library/src/scala/collection/immutable/TreeMap.scala +++ b/library/src/scala/collection/immutable/TreeMap.scala @@ -73,7 +73,7 @@ import scala.runtime.AbstractFunction2 * @define mayNotTerminateInf * @define willNotTerminateInf */ -final class TreeMap[K, +V] private (private val tree: RB.Tree[K, V])(implicit val ordering: Ordering[K]) +final class TreeMap[K, +V] private (private val tree: RB.Tree[K, V] | Null)(implicit val ordering: Ordering[K]) extends AbstractMap[K, V] with SortedMap[K, V] with StrictOptimizedSortedMapOps[K, V, TreeMap, TreeMap[K, V]] @@ -81,9 +81,9 @@ final class TreeMap[K, +V] private (private val tree: RB.Tree[K, V])(implicit va with DefaultSerializable { def this()(implicit ordering: Ordering[K]) = this(null)(ordering) - private[immutable] def tree0: RB.Tree[K, V] = tree + private[immutable] def tree0: RB.Tree[K, V] | Null = tree - private[this] def newMapOrSelf[V1 >: V](t: RB.Tree[K, V1]): TreeMap[K, V1] = if(t eq tree) this else new TreeMap[K, V1](t) + private[this] def newMapOrSelf[V1 >: V](t: RB.Tree[K, V1] | Null): TreeMap[K, V1] = if(t eq tree) this else new TreeMap[K, V1](t) override def sortedMapFactory: SortedMapFactory[TreeMap] = TreeMap @@ -282,7 +282,7 @@ final class TreeMap[K, +V] private (private val tree: RB.Tree[K, V])(implicit va private final class Adder[B1 >: V] extends RB.MapHelper[K, B1] with Function1[(K, B1), Unit] { - private var currentMutableTree: RB.Tree[K,B1] = tree0 + private var currentMutableTree: RB.Tree[K,B1] | Null = tree0 def finalTree = beforePublish(currentMutableTree) override def apply(kv: (K, B1)): Unit = { currentMutableTree = mutableUpd(currentMutableTree, kv._1, kv._2) @@ -318,7 +318,7 @@ object TreeMap extends SortedMapFactory[TreeMap] { case sm: scala.collection.SortedMap[K, V] if ordering == sm.ordering => new TreeMap[K, V](RB.fromOrderedEntries(sm.iterator, sm.size)) case _ => - var t: RB.Tree[K, V] = null + var t: RB.Tree[K, V] | Null = null val i = it.iterator while (i.hasNext) { val (k, v) = i.next() @@ -333,7 +333,7 @@ object TreeMap extends SortedMapFactory[TreeMap] { extends RB.MapHelper[K, V] with ReusableBuilder[(K, V), TreeMap[K, V]] { type Tree = RB.Tree[K, V] - private var tree:Tree = null + private var tree: Tree | Null = null def addOne(elem: (K, V)): this.type = { tree = mutableUpd(tree, elem._1, elem._2) @@ -342,7 +342,7 @@ object TreeMap extends SortedMapFactory[TreeMap] { private object adder extends AbstractFunction2[K, V, Unit] { // we cache tree to avoid the outer access to tree // in the hot path (apply) - private[this] var accumulator :Tree = null + private[this] var accumulator: Tree | Null = null def addForEach(hasForEach: collection.Map[K, V]): Unit = { accumulator = tree hasForEach.foreachEntry(this) diff --git a/library/src/scala/collection/immutable/TreeSeqMap.scala b/library/src/scala/collection/immutable/TreeSeqMap.scala index 0741bcbaec58..76e39cbc8c2e 100644 --- a/library/src/scala/collection/immutable/TreeSeqMap.scala +++ b/library/src/scala/collection/immutable/TreeSeqMap.scala @@ -320,7 +320,8 @@ object TreeSeqMap extends MapFactory[TreeSeqMap] { private[this] val bdr = new MapBuilderImpl[K, (Int, V)] private[this] var ong = Ordering.empty[K] private[this] var ord = 0 - private[this] var aliased: TreeSeqMap[K, V] = _ + @annotation.stableNull + private[this] var aliased: TreeSeqMap[K, V] | Null = null override def addOne(elem: (K, V)): this.type = addOne(elem._1, elem._2) def addOne(key: K, value: V): this.type = { @@ -574,7 +575,7 @@ object TreeSeqMap extends MapFactory[TreeSeqMap] { } @inline private[collection] final def appendInPlace[S >: T](ordinal: Int, value: S): Ordering[S] = appendInPlace1(null, ordinal, value) - private[collection] final def appendInPlace1[S >: T](parent: Bin[S], ordinal: Int, value: S): Ordering[S] = this match { + private[collection] final def appendInPlace1[S >: T](parent: Bin[S] | Null, ordinal: Int, value: S): Ordering[S] = this match { case Zero => Tip(ordinal, value) case Tip(o, _) if o >= ordinal => @@ -582,8 +583,8 @@ object TreeSeqMap extends MapFactory[TreeSeqMap] { case Tip(o, _) if parent == null => join(ordinal, Tip(ordinal, value), o, this) case Tip(o, _) => - parent.right = join(ordinal, Tip(ordinal, value), o, this) - parent + parent.nn.right = join(ordinal, Tip(ordinal, value), o, this) + parent.nn case b @ Bin(p, m, _, r) => if (!hasMatch(ordinal, p, m)) { val b2 = join(ordinal, Tip(ordinal, value), p, this) diff --git a/library/src/scala/collection/immutable/TreeSet.scala b/library/src/scala/collection/immutable/TreeSet.scala index d30380b88101..6b1a5561935d 100644 --- a/library/src/scala/collection/immutable/TreeSet.scala +++ b/library/src/scala/collection/immutable/TreeSet.scala @@ -39,7 +39,7 @@ import scala.runtime.AbstractFunction1 * @define mayNotTerminateInf * @define willNotTerminateInf */ -final class TreeSet[A] private[immutable] (private[immutable] val tree: RB.Tree[A, Any])(implicit val ordering: Ordering[A]) +final class TreeSet[A] private[immutable] (private[immutable] val tree: RB.Tree[A, Any] | Null)(implicit val ordering: Ordering[A]) extends AbstractSet[A] with SortedSet[A] with SortedSetOps[A, TreeSet, TreeSet[A]] @@ -53,7 +53,7 @@ final class TreeSet[A] private[immutable] (private[immutable] val tree: RB.Tree[ override def sortedIterableFactory: TreeSet.type = TreeSet - private[this] def newSetOrSelf(t: RB.Tree[A, Any]) = if(t eq tree) this else new TreeSet[A](t) + private[this] def newSetOrSelf(t: RB.Tree[A, Any] | Null) = if(t eq tree) this else new TreeSet[A](t) override def size: Int = RB.count(tree) @@ -254,7 +254,7 @@ object TreeSet extends SortedIterableFactory[TreeSet] { // Dotty doesn't infer that E =:= Int, since instantiation of covariant GADTs is unsound new TreeSet[E](tree) case _ => - var t: RB.Tree[E, Null] = null + var t: RB.Tree[E, Null] | Null = null val i = it.iterator while (i.hasNext) t = RB.update(t, i.next(), null, overwrite = false) new TreeSet[E](t) @@ -265,7 +265,7 @@ object TreeSet extends SortedIterableFactory[TreeSet] { extends RB.SetHelper[A] with ReusableBuilder[A, TreeSet[A]] { type Tree = RB.Tree[A, Any] - private [this] var tree:RB.Tree[A, Any] = null + private [this] var tree:RB.Tree[A, Any] | Null = null override def addOne(elem: A): this.type = { tree = mutableUpd(tree, elem) diff --git a/library/src/scala/collection/immutable/Vector.scala b/library/src/scala/collection/immutable/Vector.scala index 78768577b99b..3af7c7d6a9be 100644 --- a/library/src/scala/collection/immutable/Vector.scala +++ b/library/src/scala/collection/immutable/Vector.scala @@ -27,7 +27,7 @@ import scala.collection.generic.{CommonErrors, DefaultSerializable} import scala.collection.immutable.VectorInline._ import scala.collection.immutable.VectorStatics._ import scala.collection.mutable.ReusableBuilder - +import scala.runtime.ScalaRunTime.nullForGC /** $factoryInfo * @define Coll `Vector` @@ -265,7 +265,7 @@ sealed abstract class Vector[+A] private[immutable] (private[immutable] final va /** Number of slices */ protected[immutable] def vectorSliceCount: Int /** Slice at index */ - protected[immutable] def vectorSlice(idx: Int): Array[_ <: AnyRef] + protected[immutable] def vectorSlice(idx: Int): Array[_ <: AnyRef | Null] /** Length of all slices up to and including index */ protected[immutable] def vectorSlicePrefixLength(idx: Int): Int @@ -364,7 +364,7 @@ private object Vector0 extends BigVector[Nothing](empty1, empty1, 0) { protected[this] def slice0(lo: Int, hi: Int): Vector[Nothing] = this protected[immutable] def vectorSliceCount: Int = 0 - protected[immutable] def vectorSlice(idx: Int): Array[_ <: AnyRef] = null + protected[immutable] def vectorSlice(idx: Int): Array[_ <: AnyRef | Null] = null.asInstanceOf[Array[_ <: AnyRef | Null]] protected[immutable] def vectorSlicePrefixLength(idx: Int): Int = 0 override def equals(o: Any): Boolean = { @@ -426,7 +426,7 @@ private final class Vector1[+A](_data1: Arr1) extends VectorImpl[A](_data1) { else new Vector1(copyInit(prefix1)) protected[immutable] def vectorSliceCount: Int = 1 - protected[immutable] def vectorSlice(idx: Int): Array[_ <: AnyRef] = prefix1 + protected[immutable] def vectorSlice(idx: Int): Array[_ <: AnyRef | Null] = prefix1 protected[immutable] def vectorSlicePrefixLength(idx: Int): Int = prefix1.length override protected[this] def prependedAll0[B >: A](prefix: collection.IterableOnce[B]^, k: Int): Vector[B] = @@ -513,7 +513,7 @@ private final class Vector2[+A](_prefix1: Arr1, private[immutable] val len1: Int else slice0(0, length0-1) protected[immutable] def vectorSliceCount: Int = 3 - protected[immutable] def vectorSlice(idx: Int): Array[_ <: AnyRef] = (idx: @switch) match { + protected[immutable] def vectorSlice(idx: Int): Array[_ <: AnyRef | Null] = (idx: @switch) match { case 0 => prefix1 case 1 => data2 case 2 => suffix1 @@ -631,7 +631,7 @@ private final class Vector3[+A](_prefix1: Arr1, private[immutable] val len1: Int else slice0(0, length0-1) protected[immutable] def vectorSliceCount: Int = 5 - protected[immutable] def vectorSlice(idx: Int): Array[_ <: AnyRef] = (idx: @switch) match { + protected[immutable] def vectorSlice(idx: Int): Array[_ <: AnyRef | Null] = (idx: @switch) match { case 0 => prefix1 case 1 => prefix2 case 2 => data3 @@ -770,7 +770,7 @@ private final class Vector4[+A](_prefix1: Arr1, private[immutable] val len1: Int else slice0(0, length0-1) protected[immutable] def vectorSliceCount: Int = 7 - protected[immutable] def vectorSlice(idx: Int): Array[_ <: AnyRef] = (idx: @switch) match { + protected[immutable] def vectorSlice(idx: Int): Array[_ <: AnyRef | Null] = (idx: @switch) match { case 0 => prefix1 case 1 => prefix2 case 2 => prefix3 @@ -930,7 +930,7 @@ private final class Vector5[+A](_prefix1: Arr1, private[immutable] val len1: Int else slice0(0, length0-1) protected[immutable] def vectorSliceCount: Int = 9 - protected[immutable] def vectorSlice(idx: Int): Array[_ <: AnyRef] = (idx: @switch) match { + protected[immutable] def vectorSlice(idx: Int): Array[_ <: AnyRef | Null] = (idx: @switch) match { case 0 => prefix1 case 1 => prefix2 case 2 => prefix3 @@ -1111,7 +1111,7 @@ private final class Vector6[+A](_prefix1: Arr1, private[immutable] val len1: Int else slice0(0, length0-1) protected[immutable] def vectorSliceCount: Int = 11 - protected[immutable] def vectorSlice(idx: Int): Array[_ <: AnyRef] = (idx: @switch) match { + protected[immutable] def vectorSlice(idx: Int): Array[_ <: AnyRef | Null] = (idx: @switch) match { case 0 => prefix1 case 1 => prefix2 case 2 => prefix3 @@ -1170,7 +1170,7 @@ private final class Vector6[+A](_prefix1: Arr1, private[immutable] val len1: Int private final class VectorSliceBuilder(lo: Int, hi: Int) { //println(s"***** VectorSliceBuilder($lo, $hi)") - private[this] val slices = new Array[Array[AnyRef]](11) + private[this] val slices = new Array[Array[AnyRef] | Null](11) private[this] var len, pos, maxDim = 0 @inline private[this] def prefixIdx(n: Int) = n-1 @@ -1272,11 +1272,11 @@ private final class VectorSliceBuilder(lo: Int, hi: Int) { // A single highest-dimensional slice could have length WIDTH-1 if it came from a prefix or suffix but we // only allow WIDTH-2 for the main data, so increase the dimension in this case val one = if(pre ne null) pre else suf - if(one.length > WIDTH-2) resultDim += 1 + if(one.nn.length > WIDTH-2) resultDim += 1 } } - val prefix1 = slices(prefixIdx(1)) - val suffix1 = slices(suffixIdx(1)) + val prefix1 = slices(prefixIdx(1)).nn + val suffix1 = slices(suffixIdx(1)).nn val len1 = prefix1.length val res = (resultDim: @switch) match { case 2 => @@ -1393,7 +1393,7 @@ private final class VectorSliceBuilder(lo: Int, hi: Int) { override def toString: String = s"VectorSliceBuilder(lo=$lo, hi=$hi, len=$len, pos=$pos, maxDim=$maxDim)" - private[immutable] def getSlices: Array[Array[AnyRef]] = slices + private[immutable] def getSlices: Array[Array[AnyRef] | Null] = slices } @@ -1421,11 +1421,11 @@ final class VectorBuilder[A] extends ReusableBuilder[A, Vector[A]] { @inline def nonEmpty: Boolean = knownSize != 0 def clear(): Unit = { - a6 = null - a5 = null - a4 = null - a3 = null - a2 = null + a6 = nullForGC[Arr6] + a5 = nullForGC[Arr5] + a4 = nullForGC[Arr4] + a3 = nullForGC[Arr3] + a2 = nullForGC[Arr2] a1 = new Arr1(WIDTH) len1 = 0 lenRest = 0 @@ -1614,8 +1614,8 @@ final class VectorBuilder[A] extends ReusableBuilder[A, Vector[A]] { lenRest -= offset - newOffset offset = newOffset } - var a: Array[AnyRef] = null // the array we modify - var aParent: Array[AnyRef] = null // a's parent, so aParent(0) == a + var a: Array[AnyRef] = null.asInstanceOf[Array[AnyRef]] // the array we modify + var aParent: Array[AnyRef] = null.asInstanceOf[Array[AnyRef]] // a's parent, so aParent(0) == a if (depth >= 6) { a = a6.asInstanceOf[Array[AnyRef]] val i = offset >>> BITS5 @@ -2120,7 +2120,7 @@ private object VectorStatics { final val empty5: Arr5 = new Array(0) final val empty6: Arr6 = new Array(0) - final def foreachRec[T <: AnyRef, A, U](level: Int, a: Array[T], f: A => U): Unit = { + final def foreachRec[T <: AnyRef | Null, A, U](level: Int, a: Array[T], f: A => U): Unit = { var i = 0 val len = a.length if(level == 0) { @@ -2131,7 +2131,7 @@ private object VectorStatics { } else { val l = level-1 while(i < len) { - foreachRec(l, a(i).asInstanceOf[Array[AnyRef]], f) + foreachRec(l, a(i).asInstanceOf[Array[AnyRef | Null]], f) i += 1 } } @@ -2189,7 +2189,7 @@ private object VectorStatics { ac.asInstanceOf[Array[T]] } - final def prepend1IfSpace(prefix1: Arr1, xs: IterableOnce[_]^): Arr1 = xs match { + final def prepend1IfSpace(prefix1: Arr1, xs: IterableOnce[_]^): Arr1 | Null = xs match { case it: Iterable[_] => if(it.sizeCompare(WIDTH-prefix1.length) <= 0) { it.size match { @@ -2214,7 +2214,7 @@ private object VectorStatics { } else null } - final def append1IfSpace(suffix1: Arr1, xs: IterableOnce[_]^): Arr1 = xs match { + final def append1IfSpace(suffix1: Arr1, xs: IterableOnce[_]^): Arr1 | Null = xs match { case it: Iterable[_] => if(it.sizeCompare(WIDTH-suffix1.length) <= 0) { it.size match { @@ -2274,7 +2274,7 @@ private final class NewVectorIterator[A](v: Vector[A], private[this] var totalLe private[this] def advanceSlice(): Unit = { if(!hasNext) Iterator.empty.next() sliceIdx += 1 - var slice: Array[_ <: AnyRef] = v.vectorSlice(sliceIdx) + var slice: Array[_ <: AnyRef | Null] = v.vectorSlice(sliceIdx) while(slice.length == 0) { sliceIdx += 1 slice = v.vectorSlice(sliceIdx) @@ -2431,7 +2431,7 @@ private final class NewVectorIterator[A](v: Vector[A], private[this] var totalLe } -private abstract class VectorStepperBase[A, Sub >: Null <: Stepper[A], Semi <: Sub](it: NewVectorIterator[A]) +private abstract class VectorStepperBase[A, Sub <: Stepper[A], Semi <: Sub](it: NewVectorIterator[A]) extends Stepper[A] with EfficientSplit { protected[this] def build(it: NewVectorIterator[A]): Semi @@ -2442,7 +2442,7 @@ private abstract class VectorStepperBase[A, Sub >: Null <: Stepper[A], Semi <: S final def estimateSize: Long = it.knownSize - def trySplit(): Sub = { + def trySplit(): Sub | Null = { val len = it.knownSize if(len > 1) build(it.split(len >>> 1)) else null diff --git a/library/src/scala/collection/immutable/VectorMap.scala b/library/src/scala/collection/immutable/VectorMap.scala index bf257fd7759e..ffd87448e97d 100644 --- a/library/src/scala/collection/immutable/VectorMap.scala +++ b/library/src/scala/collection/immutable/VectorMap.scala @@ -247,7 +247,8 @@ object VectorMap extends MapFactory[VectorMap] { private[immutable] final class VectorMapBuilder[K, V] extends mutable.Builder[(K, V), VectorMap[K, V]] { private[this] val vectorBuilder = new VectorBuilder[K] private[this] val mapBuilder = new MapBuilderImpl[K, (Int, V)] - private[this] var aliased: VectorMap[K, V] = _ + @annotation.stableNull + private[this] var aliased: VectorMap[K, V] | Null = null override def clear(): Unit = { vectorBuilder.clear() diff --git a/library/src/scala/collection/mutable/AnyRefMap.scala b/library/src/scala/collection/mutable/AnyRefMap.scala index 984fb1ff781e..8228254ddfe2 100644 --- a/library/src/scala/collection/mutable/AnyRefMap.scala +++ b/library/src/scala/collection/mutable/AnyRefMap.scala @@ -70,9 +70,9 @@ class AnyRefMap[K <: AnyRef, V] private[collection] (defaultEntry: K -> V, initi private[this] var mask = 0 private[this] var _size = 0 private[this] var _vacant = 0 - private[this] var _hashes: Array[Int] = null - private[this] var _keys: Array[AnyRef] = null - private[this] var _values: Array[AnyRef] = null + private[this] var _hashes: Array[Int] = _ + private[this] var _keys: Array[AnyRef | Null] = _ + private[this] var _values: Array[AnyRef | Null] = _ if (initBlank) defaultInitialize(initialBufferSize) @@ -81,12 +81,12 @@ class AnyRefMap[K <: AnyRef, V] private[collection] (defaultEntry: K -> V, initi if (n<0) 0x7 else (((1 << (32 - java.lang.Integer.numberOfLeadingZeros(n-1))) - 1) & 0x3FFFFFFF) | 0x7 _hashes = new Array[Int](mask+1) - _keys = new Array[AnyRef](mask+1) - _values = new Array[AnyRef](mask+1) + _keys = new Array[AnyRef | Null](mask+1) + _values = new Array[AnyRef | Null](mask+1) } private[collection] def initializeTo( - m: Int, sz: Int, vc: Int, hz: Array[Int], kz: Array[AnyRef], vz: Array[AnyRef] + m: Int, sz: Int, vc: Int, hz: Array[Int], kz: Array[AnyRef | Null], vz: Array[AnyRef | Null] ): Unit = { mask = m; _size = sz; _vacant = vc; _hashes = hz; _keys = kz; _values = vz } @@ -200,9 +200,9 @@ class AnyRefMap[K <: AnyRef, V] private[collection] (defaultEntry: K -> V, initi * may not exist, if the default null/zero is acceptable. For key/value * pairs that do exist, `apply` (i.e. `map(key)`) is equally fast. */ - def getOrNull(key: K): V = { + def getOrNull(key: K): V | Null = { val i = seekEntry(hashOf(key), key) - (if (i < 0) null else _values(i)).asInstanceOf[V] + if (i < 0) null else _values(i).asInstanceOf[V] } /** Retrieves the value associated with a key. @@ -226,8 +226,8 @@ class AnyRefMap[K <: AnyRef, V] private[collection] (defaultEntry: K -> V, initi val ov = _values mask = newMask _hashes = new Array[Int](mask+1) - _keys = new Array[AnyRef](mask+1) - _values = new Array[AnyRef](mask+1) + _keys = new Array[AnyRef | Null](mask+1) + _values = new Array[AnyRef | Null](mask+1) _vacant = 0 var i = 0 while (i < oh.length) { @@ -340,7 +340,7 @@ class AnyRefMap[K <: AnyRef, V] private[collection] (defaultEntry: K -> V, initi private[this] var index = 0 - def hasNext: Boolean = index V, initi override def clone(): AnyRefMap[K, V] = { val hz = java.util.Arrays.copyOf(_hashes, _hashes.length) val kz = java.util.Arrays.copyOf(_keys, _keys.length) - val vz = java.util.Arrays.copyOf(_values, _values.length) + val vz = java.util.Arrays.copyOf(_values, _values.length) val arm = new AnyRefMap[K, V](defaultEntry, 1, initBlank = false) - arm.initializeTo(mask, _size, _vacant, hz, kz, vz) + arm.initializeTo(mask, _size, _vacant, hz, kz, vz) arm } @@ -422,7 +422,7 @@ class AnyRefMap[K <: AnyRef, V] private[collection] (defaultEntry: K -> V, initi override def updated[V1 >: V](key: K, value: V1): AnyRefMap[K, V1] = clone().asInstanceOf[AnyRefMap[K, V1]].addOne(key, value) - private[this] def foreachElement[A,B](elems: Array[AnyRef], f: A => B): Unit = { + private[this] def foreachElement[A,B](elems: Array[AnyRef | Null], f: A => B): Unit = { var i,j = 0 while (i < _hashes.length & j < _size) { val h = _hashes(i) @@ -445,10 +445,10 @@ class AnyRefMap[K <: AnyRef, V] private[collection] (defaultEntry: K -> V, initi * collection immediately. */ def mapValuesNow[V1](f: V => V1): AnyRefMap[K, V1] = { - val arm = new AnyRefMap[K,V1](AnyRefMap.exceptionDefault, 1, initBlank = false) + val arm = new AnyRefMap[K,V1](AnyRefMap.exceptionDefault, 1, initBlank = false) val hz = java.util.Arrays.copyOf(_hashes, _hashes.length) val kz = java.util.Arrays.copyOf(_keys, _keys.length) - val vz = new Array[AnyRef](_values.length) + val vz = new Array[AnyRef | Null](_values.length) var i,j = 0 while (i < _hashes.length & j < _size) { val h = _hashes(i) @@ -628,3 +628,4 @@ object AnyRefMap { implicit def iterableFactory[K <: AnyRef, V]: Factory[(K, V), AnyRefMap[K, V]] = toFactory[K, V](this) implicit def buildFromAnyRefMap[K <: AnyRef, V]: BuildFrom[AnyRefMap[_, _], (K, V), AnyRefMap[K, V]] = toBuildFrom(this) } + diff --git a/library/src/scala/collection/mutable/ArrayBuilder.scala b/library/src/scala/collection/mutable/ArrayBuilder.scala index 608fb85564a3..c1af54f73af0 100644 --- a/library/src/scala/collection/mutable/ArrayBuilder.scala +++ b/library/src/scala/collection/mutable/ArrayBuilder.scala @@ -27,7 +27,7 @@ sealed abstract class ArrayBuilder[T] extends ReusableBuilder[T, Array[T]] with Serializable { protected[this] var capacity: Int = 0 - protected[this] def elems: Array[T] // may not be allocated at size = capacity = 0 + protected[this] def elems: Array[T] | Null // may not be allocated at size = capacity = 0 protected var size: Int = 0 /** Current number of elements. */ @@ -61,7 +61,7 @@ sealed abstract class ArrayBuilder[T] private def doAddAll(xs: Array[_ <: T], offset: Int, length: Int): this.type = { if (length > 0) { ensureSize(this.size + length) - Array.copy(xs, offset, elems, this.size, length) + Array.copy(xs, offset, elems.nn, this.size, length) size += length } this @@ -71,7 +71,7 @@ sealed abstract class ArrayBuilder[T] val k = xs.knownSize if (k > 0) { ensureSize(this.size + k) - val actual = IterableOnce.copyElemsToArray(xs, elems, this.size) + val actual = IterableOnce.copyElemsToArray(xs, elems.nn, this.size) if (actual != k) throw new IllegalStateException(s"Copied $actual of $k") size += k } else if (k < 0) super.addAll(xs) @@ -111,12 +111,12 @@ object ArrayBuilder { * @tparam T type of elements for the array builder, subtype of `AnyRef` with a `ClassTag` context bound. */ @SerialVersionUID(3L) - final class ofRef[T <: AnyRef](implicit ct: ClassTag[T]) extends ArrayBuilder[T] { + final class ofRef[T <: AnyRef | Null](implicit ct: ClassTag[T]) extends ArrayBuilder[T] { - protected var elems: Array[T] = _ + protected var elems: Array[T] | Null = null private def mkArray(size: Int): Array[T] = { - if (capacity == size && capacity > 0) elems + if (capacity == size && capacity > 0) elems.nn else if (elems eq null) new Array[T](size) else java.util.Arrays.copyOf[T](elems, size) } @@ -128,7 +128,7 @@ object ArrayBuilder { def addOne(elem: T): this.type = { ensureSize(size + 1) - elems(size) = elem + elems.nn(size) = elem size += 1 this } @@ -136,7 +136,7 @@ object ArrayBuilder { def result(): Array[T] = { if (capacity != 0 && capacity == size) { capacity = 0 - val res = elems + val res = elems.nn elems = null res } @@ -160,11 +160,11 @@ object ArrayBuilder { @SerialVersionUID(3L) final class ofByte extends ArrayBuilder[Byte] { - protected var elems: Array[Byte] = _ + protected var elems: Array[Byte] | Null = null private def mkArray(size: Int): Array[Byte] = { val newelems = new Array[Byte](size) - if (this.size > 0) Array.copy(elems, 0, newelems, 0, this.size) + if (this.size > 0) Array.copy(elems.nn, 0, newelems, 0, this.size) newelems } @@ -175,7 +175,7 @@ object ArrayBuilder { def addOne(elem: Byte): this.type = { ensureSize(size + 1) - elems(size) = elem + elems.nn(size) = elem size += 1 this } @@ -183,7 +183,7 @@ object ArrayBuilder { def result(): Array[Byte] = { if (capacity != 0 && capacity == size) { capacity = 0 - val res = elems + val res = elems.nn elems = null res } @@ -202,11 +202,11 @@ object ArrayBuilder { @SerialVersionUID(3L) final class ofShort extends ArrayBuilder[Short] { - protected var elems: Array[Short] = _ + protected var elems: Array[Short] | Null = null private def mkArray(size: Int): Array[Short] = { val newelems = new Array[Short](size) - if (this.size > 0) Array.copy(elems, 0, newelems, 0, this.size) + if (this.size > 0) Array.copy(elems.nn, 0, newelems, 0, this.size) newelems } @@ -217,7 +217,7 @@ object ArrayBuilder { def addOne(elem: Short): this.type = { ensureSize(size + 1) - elems(size) = elem + elems.nn(size) = elem size += 1 this } @@ -225,7 +225,7 @@ object ArrayBuilder { def result(): Array[Short] = { if (capacity != 0 && capacity == size) { capacity = 0 - val res = elems + val res = elems.nn elems = null res } @@ -244,11 +244,11 @@ object ArrayBuilder { @SerialVersionUID(3L) final class ofChar extends ArrayBuilder[Char] { - protected var elems: Array[Char] = _ + protected var elems: Array[Char] | Null = null private def mkArray(size: Int): Array[Char] = { val newelems = new Array[Char](size) - if (this.size > 0) Array.copy(elems, 0, newelems, 0, this.size) + if (this.size > 0) Array.copy(elems.nn, 0, newelems, 0, this.size) newelems } @@ -259,7 +259,7 @@ object ArrayBuilder { def addOne(elem: Char): this.type = { ensureSize(size + 1) - elems(size) = elem + elems.nn(size) = elem size += 1 this } @@ -267,7 +267,7 @@ object ArrayBuilder { def result(): Array[Char] = { if (capacity != 0 && capacity == size) { capacity = 0 - val res = elems + val res = elems.nn elems = null res } @@ -286,11 +286,11 @@ object ArrayBuilder { @SerialVersionUID(3L) final class ofInt extends ArrayBuilder[Int] { - protected var elems: Array[Int] = _ + protected var elems: Array[Int] | Null = null private def mkArray(size: Int): Array[Int] = { val newelems = new Array[Int](size) - if (this.size > 0) Array.copy(elems, 0, newelems, 0, this.size) + if (this.size > 0) Array.copy(elems.nn, 0, newelems, 0, this.size) newelems } @@ -301,7 +301,7 @@ object ArrayBuilder { def addOne(elem: Int): this.type = { ensureSize(size + 1) - elems(size) = elem + elems.nn(size) = elem size += 1 this } @@ -309,7 +309,7 @@ object ArrayBuilder { def result(): Array[Int] = { if (capacity != 0 && capacity == size) { capacity = 0 - val res = elems + val res = elems.nn elems = null res } @@ -328,11 +328,11 @@ object ArrayBuilder { @SerialVersionUID(3L) final class ofLong extends ArrayBuilder[Long] { - protected var elems: Array[Long] = _ + protected var elems: Array[Long] | Null = null private def mkArray(size: Int): Array[Long] = { val newelems = new Array[Long](size) - if (this.size > 0) Array.copy(elems, 0, newelems, 0, this.size) + if (this.size > 0) Array.copy(elems.nn, 0, newelems, 0, this.size) newelems } @@ -343,7 +343,7 @@ object ArrayBuilder { def addOne(elem: Long): this.type = { ensureSize(size + 1) - elems(size) = elem + elems.nn(size) = elem size += 1 this } @@ -351,7 +351,7 @@ object ArrayBuilder { def result(): Array[Long] = { if (capacity != 0 && capacity == size) { capacity = 0 - val res = elems + val res = elems.nn elems = null res } @@ -370,11 +370,11 @@ object ArrayBuilder { @SerialVersionUID(3L) final class ofFloat extends ArrayBuilder[Float] { - protected var elems: Array[Float] = _ + protected var elems: Array[Float] | Null = null private def mkArray(size: Int): Array[Float] = { val newelems = new Array[Float](size) - if (this.size > 0) Array.copy(elems, 0, newelems, 0, this.size) + if (this.size > 0) Array.copy(elems.nn, 0, newelems, 0, this.size) newelems } @@ -385,7 +385,7 @@ object ArrayBuilder { def addOne(elem: Float): this.type = { ensureSize(size + 1) - elems(size) = elem + elems.nn(size) = elem size += 1 this } @@ -393,7 +393,7 @@ object ArrayBuilder { def result(): Array[Float] = { if (capacity != 0 && capacity == size) { capacity = 0 - val res = elems + val res = elems.nn elems = null res } @@ -412,11 +412,11 @@ object ArrayBuilder { @SerialVersionUID(3L) final class ofDouble extends ArrayBuilder[Double] { - protected var elems: Array[Double] = _ + protected var elems: Array[Double] | Null = null private def mkArray(size: Int): Array[Double] = { val newelems = new Array[Double](size) - if (this.size > 0) Array.copy(elems, 0, newelems, 0, this.size) + if (this.size > 0) Array.copy(elems.nn, 0, newelems, 0, this.size) newelems } @@ -427,7 +427,7 @@ object ArrayBuilder { def addOne(elem: Double): this.type = { ensureSize(size + 1) - elems(size) = elem + elems.nn(size) = elem size += 1 this } @@ -435,7 +435,7 @@ object ArrayBuilder { def result(): Array[Double] = { if (capacity != 0 && capacity == size) { capacity = 0 - val res = elems + val res = elems.nn elems = null res } @@ -455,11 +455,11 @@ object ArrayBuilder { class ofBoolean extends ArrayBuilder[Boolean] { this: ofBoolean^{} => - protected var elems: Array[Boolean] = _ + protected var elems: Array[Boolean] | Null = null private def mkArray(size: Int): Array[Boolean] = { val newelems = new Array[Boolean](size) - if (this.size > 0) Array.copy(elems, 0, newelems, 0, this.size) + if (this.size > 0) Array.copy(elems.nn, 0, newelems, 0, this.size) newelems } @@ -470,7 +470,7 @@ object ArrayBuilder { def addOne(elem: Boolean): this.type = { ensureSize(size + 1) - elems(size) = elem + elems.nn(size) = elem size += 1 this } @@ -478,7 +478,7 @@ object ArrayBuilder { def result(): Array[Boolean] = { if (capacity != 0 && capacity == size) { capacity = 0 - val res = elems + val res = elems.nn elems = null res } @@ -497,7 +497,7 @@ object ArrayBuilder { @SerialVersionUID(3L) final class ofUnit extends ArrayBuilder[Unit] { - protected def elems: Array[Unit] = throw new UnsupportedOperationException() + protected def elems: Array[Unit] | Null = throw new UnsupportedOperationException() def addOne(elem: Unit): this.type = { val newSize = size + 1 diff --git a/library/src/scala/collection/mutable/ArrayDeque.scala b/library/src/scala/collection/mutable/ArrayDeque.scala index e395a651778c..08a89754720a 100644 --- a/library/src/scala/collection/mutable/ArrayDeque.scala +++ b/library/src/scala/collection/mutable/ArrayDeque.scala @@ -39,7 +39,7 @@ import scala.reflect.ClassTag * @define willNotTerminateInf */ class ArrayDeque[A] protected ( - protected var array: Array[AnyRef], + protected var array: Array[AnyRef | Null], private[ArrayDeque] var start: Int, private[ArrayDeque] var end: Int ) extends AbstractBuffer[A] @@ -53,7 +53,7 @@ class ArrayDeque[A] protected ( reset(array, start, end) - private[this] def reset(array: Array[AnyRef], start: Int, end: Int) = { + private[this] def reset(array: Array[AnyRef | Null], start: Int, end: Int) = { assert((array.length & (array.length - 1)) == 0, s"Array.length must be power of 2") requireBounds(idx = start, until = array.length) requireBounds(idx = end, until = array.length) @@ -463,7 +463,7 @@ class ArrayDeque[A] protected ( this } - protected def ofArray(array: Array[AnyRef], end: Int): ArrayDeque[A] = + protected def ofArray(array: Array[AnyRef | Null], end: Int): ArrayDeque[A] = new ArrayDeque[A](array, start = 0, end) override def copyToArray[B >: A](dest: Array[B], destStart: Int, len: Int): Int = { @@ -566,18 +566,18 @@ object ArrayDeque extends StrictOptimizedSeqFactory[ArrayDeque] { require(len >= 0, s"Non-negative array size required") val size = (1 << 31) >>> java.lang.Integer.numberOfLeadingZeros(len) << 1 require(size >= 0, s"ArrayDeque too big - cannot allocate ArrayDeque of length $len") - new Array[AnyRef](Math.max(size, DefaultInitialSize)) + new Array[AnyRef | Null](Math.max(size, DefaultInitialSize)) } } transparent trait ArrayDequeOps[A, +CC[_] <: caps.Pure, +C <: AnyRef] extends StrictOptimizedSeqOps[A, CC, C] { - protected def array: Array[AnyRef] + protected def array: Array[AnyRef | Null] final override def clone(): C = klone() protected def klone(): C - protected def ofArray(array: Array[AnyRef], end: Int): C + protected def ofArray(array: Array[AnyRef | Null], end: Int): C protected def start_+(idx: Int): Int diff --git a/library/src/scala/collection/mutable/ArraySeq.scala b/library/src/scala/collection/mutable/ArraySeq.scala index 9f74959232eb..695c9567b597 100644 --- a/library/src/scala/collection/mutable/ArraySeq.scala +++ b/library/src/scala/collection/mutable/ArraySeq.scala @@ -138,7 +138,7 @@ object ArraySeq extends StrictOptimizedClassTagSeqFactory[ArraySeq] { self => }).asInstanceOf[ArraySeq[T]] @SerialVersionUID(3L) - final class ofRef[T <: AnyRef](val array: Array[T]) extends ArraySeq[T] { + final class ofRef[T <: AnyRef | Null](val array: Array[T]) extends ArraySeq[T] { def elemTag: ClassTag[T] = ClassTag[T](array.getClass.getComponentType) def length: Int = array.length def apply(index: Int): T = array(index) diff --git a/library/src/scala/collection/mutable/BitSet.scala b/library/src/scala/collection/mutable/BitSet.scala index 03f81bf64907..cc622acbe566 100644 --- a/library/src/scala/collection/mutable/BitSet.scala +++ b/library/src/scala/collection/mutable/BitSet.scala @@ -329,7 +329,7 @@ class BitSet(protected[collection] final var elems: Array[Long]) // * over-allocating -- the resulting array will be exactly the right size // * multiple resizing allocations -- the array is allocated one time, not log(n) times. var i = nwords - 1 - var newArray: Array[Long] = null + var newArray: Array[Long] | Null = null while (i >= 0) { val w = BitSetOps.computeWordForFilter(pred, isFlipped, word(i), i) if (w != 0L) { diff --git a/library/src/scala/collection/mutable/CollisionProofHashMap.scala b/library/src/scala/collection/mutable/CollisionProofHashMap.scala index 35fa6393deab..69f2ac4af202 100644 --- a/library/src/scala/collection/mutable/CollisionProofHashMap.scala +++ b/library/src/scala/collection/mutable/CollisionProofHashMap.scala @@ -15,6 +15,7 @@ package mutable import scala.language.`2.13` import language.experimental.captureChecking + import scala.{unchecked => uc} import scala.annotation.{implicitNotFound, tailrec, unused} import scala.annotation.unchecked.uncheckedVariance @@ -49,7 +50,7 @@ final class CollisionProofHashMap[K, V](initialCapacity: Int, loadFactor: Double private[this] type LLNode = CollisionProofHashMap.LLNode[K, V] /** The actual hash table. */ - private[this] var table: Array[Node] = new Array[Node](tableSizeFor(initialCapacity)) + private[this] var table: Array[Node | Null] = new Array[Node | Null](tableSizeFor(initialCapacity)) /** The next size value at which to resize (capacity * load factor). */ private[this] var threshold: Int = newThreshold(table.length) @@ -97,7 +98,7 @@ final class CollisionProofHashMap[K, V](initialCapacity: Int, loadFactor: Double } } - @`inline` private[this] def findNode(elem: K): Node = { + @`inline` private[this] def findNode(elem: K): Node | Null = { val hash = computeHash(elem) table(index(hash)) match { case null => null @@ -123,25 +124,25 @@ final class CollisionProofHashMap[K, V](initialCapacity: Int, loadFactor: Double def addOne(elem: (K, V)): this.type = { put0(elem._1, elem._2, getOld = false); this } - @`inline` private[this] def put0(key: K, value: V, getOld: Boolean): Some[V] = { + @`inline` private[this] def put0(key: K, value: V, getOld: Boolean): Some[V] | Null = { if(contentSize + 1 >= threshold) growTable(table.length * 2) val hash = computeHash(key) val idx = index(hash) put0(key, value, getOld, hash, idx) } - private[this] def put0(key: K, value: V, getOld: Boolean, hash: Int, idx: Int): Some[V] = { + private[this] def put0(key: K, value: V, getOld: Boolean, hash: Int, idx: Int): Some[V] | Null = { val res = table(idx) match { case n: RBNode @uc => insert(n, idx, key, hash, value) case _old => - val old: LLNode = _old.asInstanceOf[LLNode] + val old: LLNode | Null = _old.asInstanceOf[LLNode | Null] if(old eq null) { table(idx) = new LLNode(key, hash, value, null) } else { var remaining = CollisionProofHashMap.treeifyThreshold - var prev: LLNode = null - var n = old + var prev: LLNode | Null = null + var n: LLNode | Null = old while((n ne null) && n.hash <= hash && remaining > 0) { if(n.hash == hash && key == n.key) { val old = n.value @@ -167,7 +168,7 @@ final class CollisionProofHashMap[K, V](initialCapacity: Int, loadFactor: Double private[this] def treeify(old: LLNode, idx: Int): Unit = { table(idx) = CollisionProofHashMap.leaf(old.key, old.hash, old.value, red = false, null) - var n: LLNode = old.next + var n: LLNode | Null = old.next while(n ne null) { val root = table(idx).asInstanceOf[RBNode] insertIntoExisting(root, idx, n.key, n.hash, n.value, root) @@ -217,7 +218,7 @@ final class CollisionProofHashMap[K, V](initialCapacity: Int, loadFactor: Double protected[this] def extract(node: RBNode): R private[this] var i = 0 - private[this] var node: Node = null + private[this] var node: Node | Null = null private[this] val len = table.length def hasNext: Boolean = { @@ -286,13 +287,14 @@ final class CollisionProofHashMap[K, V](initialCapacity: Int, loadFactor: Double } @`inline` private[this] def reallocTable(newlen: Int) = { - table = new Array(newlen) + table = new Array[Node | Null](newlen) threshold = newThreshold(table.length) } - @`inline` private[this] def splitBucket(tree: Node, lowBucket: Int, highBucket: Int, mask: Int): Unit = tree match { + @`inline` private[this] def splitBucket(tree: Node | Null, lowBucket: Int, highBucket: Int, mask: Int): Unit = tree match { case t: LLNode @uc => splitBucket(t, lowBucket, highBucket, mask) case t: RBNode @uc => splitBucket(t, lowBucket, highBucket, mask) + case null => } private[this] def splitBucket(list: LLNode, lowBucket: Int, highBucket: Int, mask: Int): Unit = { @@ -302,7 +304,7 @@ final class CollisionProofHashMap[K, V](initialCapacity: Int, loadFactor: Double //preHigh.next = null var lastLow: LLNode = preLow var lastHigh: LLNode = preHigh - var n = list + var n: LLNode | Null = list while(n ne null) { val next = n.next if((n.hash & mask) == 0) { // keep low @@ -461,8 +463,8 @@ final class CollisionProofHashMap[K, V](initialCapacity: Int, loadFactor: Double ///////////////////// RedBlackTree code derived from mutable.RedBlackTree: - @`inline` private[this] def isRed(node: RBNode) = (node ne null) && node.red - @`inline` private[this] def isBlack(node: RBNode) = (node eq null) || !node.red + @`inline` private[this] def isRed(node: RBNode | Null) = (node ne null) && node.red + @`inline` private[this] def isBlack(node: RBNode | Null) = (node eq null) || !node.red @unused @`inline` private[this] def compare(key: K, hash: Int, node: LLNode): Int = { val i = hash - node.hash @@ -493,7 +495,7 @@ final class CollisionProofHashMap[K, V](initialCapacity: Int, loadFactor: Double } } - private[this] final def insert(tree: RBNode, bucket: Int, key: K, hash: Int, value: V): Boolean = { + private[this] final def insert(tree: RBNode | Null, bucket: Int, key: K, hash: Int, value: V): Boolean = { if(tree eq null) { table(bucket) = CollisionProofHashMap.leaf(key, hash, value, red = false, null) true @@ -504,37 +506,37 @@ final class CollisionProofHashMap[K, V](initialCapacity: Int, loadFactor: Double var root = _root var z = node while (isRed(z.parent)) { - if (z.parent eq z.parent.parent.left) { - val y = z.parent.parent.right + if (z.parent eq z.parent.nn.parent.nn.left) { + val y = z.parent.nn.parent.nn.right if (isRed(y)) { - z.parent.red = false - y.red = false - z.parent.parent.red = true - z = z.parent.parent + z.parent.nn.red = false + y.nn.red = false + z.parent.nn.parent.nn.red = true + z = z.parent.nn.parent.nn } else { - if (z eq z.parent.right) { - z = z.parent + if (z eq z.parent.nn.right) { + z = z.parent.nn root = rotateLeft(root, z) } - z.parent.red = false - z.parent.parent.red = true - root = rotateRight(root, z.parent.parent) + z.parent.nn.red = false + z.parent.nn.parent.nn.red = true + root = rotateRight(root, z.parent.nn.parent.nn) } } else { // symmetric cases - val y = z.parent.parent.left + val y = z.parent.nn.parent.nn.left if (isRed(y)) { - z.parent.red = false - y.red = false - z.parent.parent.red = true - z = z.parent.parent + z.parent.nn.red = false + y.nn.red = false + z.parent.nn.parent.nn.red = true + z = z.parent.nn.parent.nn } else { - if (z eq z.parent.left) { - z = z.parent + if (z eq z.parent.nn.left) { + z = z.parent.nn root = rotateRight(root, z) } - z.parent.red = false - z.parent.parent.red = true - root = rotateLeft(root, z.parent.parent) + z.parent.nn.red = false + z.parent.nn.parent.nn.red = true + root = rotateLeft(root, z.parent.nn.parent.nn) } } } @@ -552,12 +554,12 @@ final class CollisionProofHashMap[K, V](initialCapacity: Int, loadFactor: Double val oldValue = z.value var y = z var yIsRed = y.red - var x: RBNode = null - var xParent: RBNode = null + var x: RBNode | Null = null + var xParent: RBNode | Null = null if (z.left eq null) { x = z.right - root = transplant(root, z, z.right) + root = transplant(root, z, z.right.nn) xParent = z.parent } else if (z.right eq null) { @@ -573,13 +575,13 @@ final class CollisionProofHashMap[K, V](initialCapacity: Int, loadFactor: Double if (y.parent eq z) xParent = y else { xParent = y.parent - root = transplant(root, y, y.right) + root = transplant(root, y, y.right.nn) y.right = z.right - y.right.parent = y + y.right.nn.parent = y } root = transplant(root, z, y) y.left = z.left - y.left.parent = y + y.left.nn.parent = y y.red = z.red } @@ -589,65 +591,65 @@ final class CollisionProofHashMap[K, V](initialCapacity: Int, loadFactor: Double } else Statics.pfMarker } - private[this] def fixAfterDelete(_root: RBNode, node: RBNode, parent: RBNode): RBNode = { + private[this] def fixAfterDelete(_root: RBNode, node: RBNode | Null, parent: RBNode | Null): RBNode = { var root = _root var x = node var xParent = parent while ((x ne root) && isBlack(x)) { - if (x eq xParent.left) { - var w = xParent.right + if (x eq xParent.nn.left) { + var w = xParent.nn.right // assert(w ne null) - if (w.red) { - w.red = false - xParent.red = true - root = rotateLeft(root, xParent) - w = xParent.right + if (w.nn.red) { + w.nn.red = false + xParent.nn.red = true + root = rotateLeft(root, xParent.nn) + w = xParent.nn.right } - if (isBlack(w.left) && isBlack(w.right)) { - w.red = true + if (isBlack(w.nn.left) && isBlack(w.nn.right)) { + w.nn.red = true x = xParent } else { - if (isBlack(w.right)) { - w.left.red = false - w.red = true - root = rotateRight(root, w) - w = xParent.right + if (isBlack(w.nn.right)) { + w.nn.left.nn.red = false + w.nn.red = true + root = rotateRight(root, w.nn) + w = xParent.nn.right } - w.red = xParent.red - xParent.red = false - w.right.red = false - root = rotateLeft(root, xParent) + w.nn.red = xParent.nn.red + xParent.nn.red = false + w.nn.right.nn.red = false + root = rotateLeft(root, xParent.nn) x = root } } else { // symmetric cases - var w = xParent.left + var w = xParent.nn.left // assert(w ne null) - if (w.red) { - w.red = false - xParent.red = true - root = rotateRight(root, xParent) - w = xParent.left + if (w.nn.red) { + w.nn.red = false + xParent.nn.red = true + root = rotateRight(root, xParent.nn) + w = xParent.nn.left } - if (isBlack(w.right) && isBlack(w.left)) { - w.red = true + if (isBlack(w.nn.right) && isBlack(w.nn.left)) { + w.nn.red = true x = xParent } else { - if (isBlack(w.left)) { - w.right.red = false - w.red = true - root = rotateLeft(root, w) - w = xParent.left + if (isBlack(w.nn.left)) { + w.nn.right.nn.red = false + w.nn.red = true + root = rotateLeft(root, w.nn) + w = xParent.nn.left } - w.red = xParent.red - xParent.red = false - w.left.red = false - root = rotateRight(root, xParent) + w.nn.red = xParent.nn.red + xParent.nn.red = false + w.nn.left.nn.red = false + root = rotateRight(root, xParent.nn) x = root } } - xParent = x.parent + xParent = x.nn.parent } if (x ne null) x.red = false root @@ -657,7 +659,7 @@ final class CollisionProofHashMap[K, V](initialCapacity: Int, loadFactor: Double @`inline` private[this] def rotateLeft(_root: RBNode, x: RBNode): RBNode = { var root = _root - val y = x.right + val y = x.right.nn x.right = y.left val xp = x.parent @@ -675,7 +677,7 @@ final class CollisionProofHashMap[K, V](initialCapacity: Int, loadFactor: Double @`inline` private[this] def rotateRight(_root: RBNode, x: RBNode): RBNode = { var root = _root - val y = x.left + val y = x.left.nn x.left = y.right val xp = x.parent @@ -706,9 +708,9 @@ final class CollisionProofHashMap[K, V](initialCapacity: Int, loadFactor: Double // building - def fromNodes(xs: Iterator[Node], size: Int): RBNode = { + def fromNodes(xs: Iterator[Node], size: Int): RBNode | Null = { val maxUsedDepth = 32 - Integer.numberOfLeadingZeros(size) // maximum depth of non-leaf nodes - def f(level: Int, size: Int): RBNode = size match { + def f(level: Int, size: Int): RBNode | Null = size match { case 0 => null case 1 => val nn = xs.next() @@ -728,7 +730,7 @@ final class CollisionProofHashMap[K, V](initialCapacity: Int, loadFactor: Double } val n = new RBNode(key, hash, value, red = false, left, right, null) if(left ne null) left.parent = n - right.parent = n + if(right ne null) right.parent = n n } f(1, size) @@ -767,7 +769,7 @@ object CollisionProofHashMap extends SortedMapFactory[CollisionProofHashMap] { @SerialVersionUID(3L) private final class DeserializationFactory[K, V](val tableLength: Int, val loadFactor: Double, val ordering: Ordering[K]) extends Factory[(K, V), CollisionProofHashMap[K, V]] with Serializable { - def fromSpecific(it: IterableOnce[(K, V)]^): CollisionProofHashMap[K, V] = new CollisionProofHashMap[K, V](tableLength, loadFactor)(ordering) ++= it + def fromSpecific(it: IterableOnce[(K, V)]^): CollisionProofHashMap[K, V] = new CollisionProofHashMap[K, V](tableLength, loadFactor)(using ordering) ++= it def newBuilder: Builder[(K, V), CollisionProofHashMap[K, V]] = CollisionProofHashMap.newBuilder(tableLength, loadFactor)(using ordering) } @@ -789,10 +791,18 @@ object CollisionProofHashMap extends SortedMapFactory[CollisionProofHashMap] { /////////////////////////// Red-Black Tree Node - final class RBNode[K, V](var key: K, var hash: Int, var value: V, var red: Boolean, var left: RBNode[K, V], var right: RBNode[K, V], var parent: RBNode[K, V]) extends Node { + final class RBNode[K, V]( + var key: K, var hash: Int, var value: V, var red: Boolean, + @annotation.stableNull + var left: RBNode[K, V] | Null, + @annotation.stableNull + var right: RBNode[K, V] | Null, + @annotation.stableNull + var parent: RBNode[K, V] | Null + ) extends Node { override def toString: String = "RBNode(" + key + ", " + hash + ", " + value + ", " + red + ", " + left + ", " + right + ")" - @tailrec def getNode(k: K, h: Int)(implicit ord: Ordering[K]): RBNode[K, V] = { + @tailrec def getNode(k: K, h: Int)(implicit ord: Ordering[K]): RBNode[K, V] | Null = { val cmp = compare(k, h, this) if (cmp < 0) { if(left ne null) left.getNode(k, h) else null @@ -820,7 +830,7 @@ object CollisionProofHashMap extends SortedMapFactory[CollisionProofHashMap] { } } - @`inline` private def leaf[A, B](key: A, hash: Int, value: B, red: Boolean, parent: RBNode[A, B]): RBNode[A, B] = + @`inline` private def leaf[A, B](key: A, hash: Int, value: B, red: Boolean, parent: RBNode[A, B] | Null): RBNode[A, B] = new RBNode(key, hash, value, red, null, null, parent) @tailrec private def minNodeNonNull[A, B](node: RBNode[A, B]): RBNode[A, B] = @@ -830,7 +840,7 @@ object CollisionProofHashMap extends SortedMapFactory[CollisionProofHashMap] { * Returns the node that follows `node` in an in-order tree traversal. If `node` has the maximum key (and is, * therefore, the last node), this method returns `null`. */ - private def successor[A, B](node: RBNode[A, B]): RBNode[A, B] = { + private def successor[A, B](node: RBNode[A, B]): RBNode[A, B] | Null = { if (node.right ne null) minNodeNonNull(node.right) else { var x = node @@ -843,8 +853,8 @@ object CollisionProofHashMap extends SortedMapFactory[CollisionProofHashMap] { } } - private final class RBNodesIterator[A, B](tree: RBNode[A, B])(implicit @unused ord: Ordering[A]) extends AbstractIterator[RBNode[A, B]] { - private[this] var nextNode: RBNode[A, B] = if(tree eq null) null else minNodeNonNull(tree) + private final class RBNodesIterator[A, B](tree: RBNode[A, B] | Null)(implicit @unused ord: Ordering[A]) extends AbstractIterator[RBNode[A, B]] { + private[this] var nextNode: RBNode[A, B] | Null = if(tree eq null) null else minNodeNonNull(tree) def hasNext: Boolean = nextNode ne null @@ -859,13 +869,13 @@ object CollisionProofHashMap extends SortedMapFactory[CollisionProofHashMap] { /////////////////////////// Linked List Node - private final class LLNode[K, V](var key: K, var hash: Int, var value: V, var next: LLNode[K, V]) extends Node { + private final class LLNode[K, V](var key: K, var hash: Int, var value: V, @annotation.stableNull var next: LLNode[K, V] | Null) extends Node { override def toString = s"LLNode($key, $value, $hash) -> $next" private[this] def eq(a: Any, b: Any): Boolean = if(a.asInstanceOf[AnyRef] eq null) b.asInstanceOf[AnyRef] eq null else a.asInstanceOf[AnyRef].equals(b) - @tailrec def getNode(k: K, h: Int)(implicit ord: Ordering[K]): LLNode[K, V] = { + @tailrec def getNode(k: K, h: Int)(implicit ord: Ordering[K]): LLNode[K, V] | Null = { if(h == hash && eq(k, key) /*ord.compare(k, key) == 0*/) this else if((next eq null) || (hash > h)) null else next.getNode(k, h) @@ -887,3 +897,4 @@ object CollisionProofHashMap extends SortedMapFactory[CollisionProofHashMap] { } } } + diff --git a/library/src/scala/collection/mutable/HashMap.scala b/library/src/scala/collection/mutable/HashMap.scala index 3204d0924110..0e3c8e7ce2a5 100644 --- a/library/src/scala/collection/mutable/HashMap.scala +++ b/library/src/scala/collection/mutable/HashMap.scala @@ -15,6 +15,7 @@ package mutable import scala.language.`2.13` import language.experimental.captureChecking + import scala.annotation.{nowarn, tailrec} import scala.collection.Stepper.EfficientSplit import scala.collection.generic.DefaultSerializationProxy @@ -52,7 +53,7 @@ class HashMap[K, V](initialCapacity: Int, loadFactor: Double) import HashMap.Node /** The actual hash table. */ - private[this] var table = new Array[Node[K, V]](tableSizeFor(initialCapacity)) + private[this] var table = new Array[Node[K, V] | Null](tableSizeFor(initialCapacity)) /** The next size value at which to resize (capacity * load factor). */ private[this] var threshold: Int = newThreshold(table.length) @@ -83,7 +84,7 @@ class HashMap[K, V](initialCapacity: Int, loadFactor: Double) override def contains(key: K): Boolean = findNode(key) ne null - @`inline` private[this] def findNode(key: K): Node[K, V] = { + @`inline` private[this] def findNode(key: K): Node[K, V] | Null = { val hash = computeHash(key) table(index(hash)) match { case null => null @@ -137,19 +138,22 @@ class HashMap[K, V](initialCapacity: Int, loadFactor: Double) val hash = computeHash(key) val indexedHash = index(hash) - var foundNode: Node[K, V] = null - var previousNode: Node[K, V] = null + var foundNode: Node[K, V] | Null = null + var previousNode: Node[K, V] | Null = null table(indexedHash) match { case null => case nd => @tailrec - def findNode(prev: Node[K, V], nd: Node[K, V], k: K, h: Int): Unit = { + def findNode(prev: Node[K, V] | Null, nd: Node[K, V], k: K, h: Int): Unit = { if (h == nd.hash && k == nd.key) { previousNode = prev foundNode = nd } - else if ((nd.next eq null) || (nd.hash > h)) () - else findNode(nd, nd.next, k, h) + else { + val ndNext = nd.next + if ((ndNext eq null) || (nd.hash > h)) () + else findNode(nd, ndNext, k, h) + } } findNode(null, nd, key, hash) @@ -166,8 +170,8 @@ class HashMap[K, V](initialCapacity: Int, loadFactor: Double) case (None, None) => // do nothing case (Some(_), None) => - if (previousNode != null) previousNode.next = foundNode.next - else table(indexedHash) = foundNode.next + if (previousNode != null) previousNode.nn.next = foundNode.nn.next + else table(indexedHash) = foundNode.nn.next contentSize -= 1 case (None, Some(value)) => @@ -178,7 +182,7 @@ class HashMap[K, V](initialCapacity: Int, loadFactor: Double) } else indexedHash put0(key, value, getOld = false, hash, newIndexedHash) - case (Some(_), Some(newValue)) => foundNode.value = newValue + case (Some(_), Some(newValue)) => foundNode.nn.value = newValue } nextValue } @@ -223,13 +227,13 @@ class HashMap[K, V](initialCapacity: Int, loadFactor: Double) * @param hash the **improved** hashcode of `key` (see computeHash) * @param getOld if true, then the previous value for `key` will be returned, otherwise, false */ - private[this] def put0(key: K, value: V, hash: Int, getOld: Boolean): Some[V] = { + private[this] def put0(key: K, value: V, hash: Int, getOld: Boolean): Some[V] | Null = { if(contentSize + 1 >= threshold) growTable(table.length * 2) val idx = index(hash) put0(key, value, getOld, hash, idx) } - private[this] def put0(key: K, value: V, getOld: Boolean): Some[V] = { + private[this] def put0(key: K, value: V, getOld: Boolean): Some[V] | Null = { if(contentSize + 1 >= threshold) growTable(table.length * 2) val hash = computeHash(key) val idx = index(hash) @@ -237,13 +241,13 @@ class HashMap[K, V](initialCapacity: Int, loadFactor: Double) } - private[this] def put0(key: K, value: V, getOld: Boolean, hash: Int, idx: Int): Some[V] = { + private[this] def put0(key: K, value: V, getOld: Boolean, hash: Int, idx: Int): Some[V] | Null = { table(idx) match { case null => table(idx) = new Node[K, V](key, hash, value, null) case old => - var prev: Node[K, V] = null - var n = old + var prev: Node[K, V] | Null = null + var n: Node[K, V] | Null = old while((n ne null) && n.hash <= hash) { if(n.hash == hash && key == n.key) { val old = n.value @@ -260,7 +264,7 @@ class HashMap[K, V](initialCapacity: Int, loadFactor: Double) null } - private def remove0(elem: K) : Node[K, V] = remove0(elem, computeHash(elem)) + private def remove0(elem: K) : Node[K, V] | Null = remove0(elem, computeHash(elem)) /** Removes a key from this map if it exists * @@ -268,7 +272,7 @@ class HashMap[K, V](initialCapacity: Int, loadFactor: Double) * @param hash the **improved** hashcode of `element` (see computeHash) * @return the node that contained element if it was present, otherwise null */ - private[this] def remove0(elem: K, hash: Int) : Node[K, V] = { + private[this] def remove0(elem: K, hash: Int) : Node[K, V] | Null = { val idx = index(hash) table(idx) match { case null => null @@ -296,7 +300,7 @@ class HashMap[K, V](initialCapacity: Int, loadFactor: Double) private[this] abstract class HashMapIterator[A] extends AbstractIterator[A] { private[this] var i = 0 - private[this] var node: Node[K, V] = null + private[this] var node: Node[K, V] | Null = null private[this] val len = table.length protected[this] def extract(nd: Node[K, V]): A @@ -316,8 +320,8 @@ class HashMap[K, V](initialCapacity: Int, loadFactor: Double) def next(): A = if(!hasNext) Iterator.empty.next() else { - val r = extract(node) - node = node.next + val r = extract(node.nn) + node = node.nn.next r } } @@ -350,16 +354,16 @@ class HashMap[K, V](initialCapacity: Int, loadFactor: Double) override def stepper[S <: Stepper[_]](implicit shape: StepperShape[(K, V), S]): S with EfficientSplit = shape. - parUnbox(new convert.impl.AnyTableStepper[(K, V), Node[K, V]](size, table, _.next, node => (node.key, node.value), 0, table.length)). + parUnbox(new convert.impl.AnyTableStepper[(K, V), Node[K, V]](size, table, _.next.nn, node => (node.key, node.value), 0, table.length)). asInstanceOf[S with EfficientSplit] override def keyStepper[S <: Stepper[_]](implicit shape: StepperShape[K, S]): S with EfficientSplit = { import convert.impl._ val s = shape.shape match { - case StepperShape.IntShape => new IntTableStepper[Node[K, V]] (size, table, _.next, _.key.asInstanceOf[Int], 0, table.length) - case StepperShape.LongShape => new LongTableStepper[Node[K, V]] (size, table, _.next, _.key.asInstanceOf[Long], 0, table.length) - case StepperShape.DoubleShape => new DoubleTableStepper[Node[K, V]](size, table, _.next, _.key.asInstanceOf[Double], 0, table.length) - case _ => shape.parUnbox(new AnyTableStepper[K, Node[K, V]](size, table, _.next, _.key, 0, table.length)) + case StepperShape.IntShape => new IntTableStepper[Node[K, V]] (size, table, _.next.nn, _.key.asInstanceOf[Int], 0, table.length) + case StepperShape.LongShape => new LongTableStepper[Node[K, V]] (size, table, _.next.nn, _.key.asInstanceOf[Long], 0, table.length) + case StepperShape.DoubleShape => new DoubleTableStepper[Node[K, V]](size, table, _.next.nn, _.key.asInstanceOf[Double], 0, table.length) + case _ => shape.parUnbox(new AnyTableStepper[K, Node[K, V]](size, table, _.next.nn, _.key, 0, table.length)) } s.asInstanceOf[S with EfficientSplit] } @@ -367,10 +371,10 @@ class HashMap[K, V](initialCapacity: Int, loadFactor: Double) override def valueStepper[S <: Stepper[_]](implicit shape: StepperShape[V, S]): S with EfficientSplit = { import convert.impl._ val s = shape.shape match { - case StepperShape.IntShape => new IntTableStepper[Node[K, V]] (size, table, _.next, _.value.asInstanceOf[Int], 0, table.length) - case StepperShape.LongShape => new LongTableStepper[Node[K, V]] (size, table, _.next, _.value.asInstanceOf[Long], 0, table.length) - case StepperShape.DoubleShape => new DoubleTableStepper[Node[K, V]](size, table, _.next, _.value.asInstanceOf[Double], 0, table.length) - case _ => shape.parUnbox(new AnyTableStepper[V, Node[K, V]](size, table, _.next, _.value, 0, table.length)) + case StepperShape.IntShape => new IntTableStepper[Node[K, V]] (size, table, _.next.nn, _.value.asInstanceOf[Int], 0, table.length) + case StepperShape.LongShape => new LongTableStepper[Node[K, V]] (size, table, _.next.nn, _.value.asInstanceOf[Long], 0, table.length) + case StepperShape.DoubleShape => new DoubleTableStepper[Node[K, V]](size, table, _.next.nn, _.value.asInstanceOf[Double], 0, table.length) + case _ => shape.parUnbox(new AnyTableStepper[V, Node[K, V]](size, table, _.next.nn, _.value, 0, table.length)) } s.asInstanceOf[S with EfficientSplit] } @@ -380,7 +384,7 @@ class HashMap[K, V](initialCapacity: Int, loadFactor: Double) throw new RuntimeException(s"new HashMap table size $newlen exceeds maximum") var oldlen = table.length threshold = newThreshold(newlen) - if(size == 0) table = new Array(newlen) + if(size == 0) table = new Array[Node[K, V] | Null](newlen) else { table = java.util.Arrays.copyOf(table, newlen) val preLow: Node[K, V] = new Node(null.asInstanceOf[K], 0, null.asInstanceOf[V], null) @@ -396,7 +400,7 @@ class HashMap[K, V](initialCapacity: Int, loadFactor: Double) preHigh.next = null var lastLow: Node[K, V] = preLow var lastHigh: Node[K, V] = preHigh - var n = old + var n: Node[K, V] | Null = old while(n ne null) { val next = n.next if((n.hash & oldlen) == 0) { // keep low @@ -625,16 +629,16 @@ object HashMap extends MapFactory[HashMap] { def newBuilder: Builder[(K, V), HashMap[K, V]] = HashMap.newBuilder(tableLength, loadFactor) } - private[collection] final class Node[K, V](_key: K, _hash: Int, private[this] var _value: V, private[this] var _next: Node[K, V]) { + private[collection] final class Node[K, V](_key: K, _hash: Int, private[this] var _value: V, @annotation.stableNull private[this] var _next: Node[K, V] | Null) { def key: K = _key def hash: Int = _hash def value: V = _value def value_= (v: V): Unit = _value = v - def next: Node[K, V] = _next - def next_= (n: Node[K, V]): Unit = _next = n + def next: Node[K, V] | Null = _next + def next_= (n: Node[K, V] | Null): Unit = _next = n @tailrec - def findNode(k: K, h: Int): Node[K, V] = + def findNode(k: K, h: Int): Node[K, V] | Null = if(h == _hash && k == _key) this else if((_next eq null) || (_hash > h)) null else _next.findNode(k, h) @@ -654,3 +658,4 @@ object HashMap extends MapFactory[HashMap] { override def toString = s"Node($key, $value, $hash) -> $next" } } + diff --git a/library/src/scala/collection/mutable/HashSet.scala b/library/src/scala/collection/mutable/HashSet.scala index a92db177c110..5dff2155b385 100644 --- a/library/src/scala/collection/mutable/HashSet.scala +++ b/library/src/scala/collection/mutable/HashSet.scala @@ -15,6 +15,7 @@ package mutable import scala.language.`2.13` import language.experimental.captureChecking + import scala.annotation.tailrec import scala.collection.Stepper.EfficientSplit import scala.collection.generic.DefaultSerializationProxy @@ -47,7 +48,7 @@ final class HashSet[A](initialCapacity: Int, loadFactor: Double) * - The sum of the lengths of all buckets is equal to contentSize. */ /** The actual hash table. */ - private[this] var table = new Array[Node[A]](tableSizeFor(initialCapacity)) + private[this] var table = new Array[Node[A] | Null](tableSizeFor(initialCapacity)) /** The next size value at which to resize (capacity * load factor). */ private[this] var threshold: Int = newThreshold(table.length) @@ -74,7 +75,7 @@ final class HashSet[A](initialCapacity: Int, loadFactor: Double) override def contains(elem: A): Boolean = findNode(elem) ne null - @`inline` private[this] def findNode(elem: A): Node[A] = { + @`inline` private[this] def findNode(elem: A): Node[A] | Null = { val hash = computeHash(elem) table(index(hash)) match { case null => null @@ -158,8 +159,8 @@ final class HashSet[A](initialCapacity: Int, loadFactor: Double) case null => table(idx) = new Node(elem, hash, null) case old => - var prev: Node[A] = null - var n = old + var prev: Node[A] | Null = null + var n: Node[A] | Null = old while((n ne null) && n.hash <= hash) { if(n.hash == hash && elem == n.key) return false prev = n @@ -204,7 +205,7 @@ final class HashSet[A](initialCapacity: Int, loadFactor: Double) private[this] abstract class HashSetIterator[B] extends AbstractIterator[B] { private[this] var i = 0 - private[this] var node: Node[A] = null + private[this] var node: Node[A] | Null = null private[this] val len = table.length protected[this] def extract(nd: Node[A]): B @@ -224,8 +225,8 @@ final class HashSet[A](initialCapacity: Int, loadFactor: Double) def next(): B = if(!hasNext) Iterator.empty.next() else { - val r = extract(node) - node = node.next + val r = extract(node.nn) + node = node.nn.next r } } @@ -242,10 +243,10 @@ final class HashSet[A](initialCapacity: Int, loadFactor: Double) override def stepper[S <: Stepper[_]](implicit shape: StepperShape[A, S]): S with EfficientSplit = { import convert.impl._ val s = shape.shape match { - case StepperShape.IntShape => new IntTableStepper[Node[A]] (size, table, _.next, _.key.asInstanceOf[Int], 0, table.length) - case StepperShape.LongShape => new LongTableStepper[Node[A]] (size, table, _.next, _.key.asInstanceOf[Long], 0, table.length) - case StepperShape.DoubleShape => new DoubleTableStepper[Node[A]](size, table, _.next, _.key.asInstanceOf[Double], 0, table.length) - case _ => shape.parUnbox(new AnyTableStepper[A, Node[A]](size, table, _.next, _.key, 0, table.length)) + case StepperShape.IntShape => new IntTableStepper[Node[A]] (size, table, _.next.nn, _.key.asInstanceOf[Int], 0, table.length) + case StepperShape.LongShape => new LongTableStepper[Node[A]] (size, table, _.next.nn, _.key.asInstanceOf[Long], 0, table.length) + case StepperShape.DoubleShape => new DoubleTableStepper[Node[A]](size, table, _.next.nn, _.key.asInstanceOf[Double], 0, table.length) + case _ => shape.parUnbox(new AnyTableStepper[A, Node[A]](size, table, _.next.nn, _.key, 0, table.length)) } s.asInstanceOf[S with EfficientSplit] } @@ -253,7 +254,7 @@ final class HashSet[A](initialCapacity: Int, loadFactor: Double) private[this] def growTable(newlen: Int) = { var oldlen = table.length threshold = newThreshold(newlen) - if(size == 0) table = new Array(newlen) + if(size == 0) table = new Array[Node[A] | Null](newlen) else { table = java.util.Arrays.copyOf(table, newlen) val preLow: Node[A] = new Node(null.asInstanceOf[A], 0, null) @@ -269,7 +270,7 @@ final class HashSet[A](initialCapacity: Int, loadFactor: Double) preHigh.next = null var lastLow: Node[A] = preLow var lastHigh: Node[A] = preHigh - var n = old + var n: Node[A] | Null = old while(n ne null) { val next = n.next if((n.hash & oldlen) == 0) { // keep low @@ -435,14 +436,14 @@ object HashSet extends IterableFactory[HashSet] { def newBuilder: Builder[A, HashSet[A]] = HashSet.newBuilder(tableLength, loadFactor) } - private[collection] final class Node[K](_key: K, _hash: Int, private[this] var _next: Node[K]) { + private[collection] final class Node[K](_key: K, _hash: Int, @annotation.stableNull private[this] var _next: Node[K] | Null) { def key: K = _key def hash: Int = _hash - def next: Node[K] = _next - def next_= (n: Node[K]): Unit = _next = n + def next: Node[K] | Null = _next + def next_= (n: Node[K] | Null): Unit = _next = n @tailrec - def findNode(k: K, h: Int): Node[K] = + def findNode(k: K, h: Int): Node[K] | Null = if(h == _hash && k == _key) this else if((_next eq null) || (_hash > h)) null else _next.findNode(k, h) diff --git a/library/src/scala/collection/mutable/HashTable.scala b/library/src/scala/collection/mutable/HashTable.scala index 5d0d1f35bdca..0d6d01837abb 100644 --- a/library/src/scala/collection/mutable/HashTable.scala +++ b/library/src/scala/collection/mutable/HashTable.scala @@ -38,7 +38,7 @@ import java.lang.Integer * @tparam A type of the elements contained in this hash table. */ // Not used in the standard library, but used in scala-parallel-collections -private[collection] trait HashTable[A, B, Entry >: Null <: HashEntry[A, Entry]] extends HashTable.HashUtils[A] { +private[collection] trait HashTable[A, B, Entry <: HashEntry[A, Entry]] extends HashTable.HashUtils[A] { // Replacing Entry type parameter by abstract type member here allows to not expose to public // implementation-specific entry classes such as `DefaultEntry` or `LinkedEntry`. // However, I'm afraid it's too late now for such breaking change. @@ -48,7 +48,7 @@ private[collection] trait HashTable[A, B, Entry >: Null <: HashEntry[A, Entry]] /** The actual hash table. */ - protected[collection] var table: Array[HashEntry[A, Entry]] = new Array(initialCapacity) + protected[collection] var table: Array[HashEntry[A, Entry] | Null] = new Array(initialCapacity) /** The number of mappings contained in this hash table. */ @@ -62,7 +62,8 @@ private[collection] trait HashTable[A, B, Entry >: Null <: HashEntry[A, Entry]] /** The array keeping track of the number of elements in 32 element blocks. */ - protected var sizemap: Array[Int] = null + @annotation.stableNull + protected var sizemap: Array[Int] | Null = null protected var seedvalue: Int = tableSizeSeed @@ -132,11 +133,11 @@ private[collection] trait HashTable[A, B, Entry >: Null <: HashEntry[A, Entry]] /** Find entry with given key in table, null if not found. */ - final def findEntry(key: A): Entry = + final def findEntry(key: A): Entry | Null = findEntry0(key, index(elemHashCode(key))) - protected[collection] final def findEntry0(key: A, h: Int): Entry = { - var e = table(h).asInstanceOf[Entry] + protected[collection] final def findEntry0(key: A, h: Int): Entry | Null = { + var e = table(h).asInstanceOf[Entry | Null] while (e != null && !elemEquals(e.key, key)) e = e.next e } @@ -149,7 +150,7 @@ private[collection] trait HashTable[A, B, Entry >: Null <: HashEntry[A, Entry]] } protected[collection] final def addEntry0(e: Entry, h: Int): Unit = { - e.next = table(h).asInstanceOf[Entry] + e.next = table(h).asInstanceOf[Entry | Null] table(h) = e tableSize = tableSize + 1 nnSizeMapAdd(h) @@ -163,7 +164,7 @@ private[collection] trait HashTable[A, B, Entry >: Null <: HashEntry[A, Entry]] * Returns entry found in table or null. * New entries are created by calling `createNewEntry` method. */ - def findOrAddEntry(key: A, value: B): Entry = { + def findOrAddEntry(key: A, value: B): Entry | Null = { val h = index(elemHashCode(key)) val e = findEntry0(key, h) if (e ne null) e else { addEntry0(createNewEntry(key, value), h); null } @@ -177,13 +178,13 @@ private[collection] trait HashTable[A, B, Entry >: Null <: HashEntry[A, Entry]] /** Remove entry from table if present. */ - final def removeEntry(key: A) : Entry = { + final def removeEntry(key: A) : Entry | Null = { removeEntry0(key, index(elemHashCode(key))) } /** Remove entry from table if present. */ - private[collection] final def removeEntry0(key: A, h: Int) : Entry = { - var e = table(h).asInstanceOf[Entry] + private[collection] final def removeEntry0(key: A, h: Int) : Entry | Null = { + var e = table(h).asInstanceOf[Entry | Null] if (e != null) { if (elemEquals(e.key, key)) { table(h) = e.next @@ -219,7 +220,7 @@ private[collection] trait HashTable[A, B, Entry >: Null <: HashEntry[A, Entry]] def hasNext = es != null def next() = { val res = es - es = es.next + es = es.nn.next while (es == null && idx > 0) { idx = idx - 1 es = iterTable(idx) @@ -265,7 +266,7 @@ private[collection] trait HashTable[A, B, Entry >: Null <: HashEntry[A, Entry]] while (e != null) { val h = index(elemHashCode(e.key)) val e1 = e.next - e.next = table(h).asInstanceOf[Entry] + e.next = table(h).asInstanceOf[Entry | Null] table(h) = e e = e1 nnSizeMapAdd(h) @@ -338,14 +339,14 @@ private[collection] trait HashTable[A, B, Entry >: Null <: HashEntry[A, Entry]] } tableidx += 1 } - sizemap(bucketidx) = currbucketsize + sizemap.nn(bucketidx) = currbucketsize tableuntil += sizeMapBucketSize bucketidx += 1 } } private[collection] def printSizeMap() = { - println(sizemap.to(collection.immutable.List)) + println(sizemap.nn.to(collection.immutable.List)) } protected final def sizeMapDisable() = sizemap = null @@ -415,5 +416,5 @@ private[collection] object HashTable { */ private[collection] trait HashEntry[A, E <: HashEntry[A, E]] { val key: A - var next: E = _ + var next: E | Null = _ } diff --git a/library/src/scala/collection/mutable/LinkedHashMap.scala b/library/src/scala/collection/mutable/LinkedHashMap.scala index a2b1b0e8f94b..8fd1f38fca5c 100644 --- a/library/src/scala/collection/mutable/LinkedHashMap.scala +++ b/library/src/scala/collection/mutable/LinkedHashMap.scala @@ -51,37 +51,39 @@ class LinkedHashMap[K, V] private[collection] type Entry = LinkedHashMap.LinkedEntry[K, V] - private[collection] def _firstEntry: Entry = firstEntry + private[collection] def _firstEntry: Entry | Null = firstEntry - protected var firstEntry: Entry = null + @annotation.stableNull + protected var firstEntry: Entry | Null = null - protected var lastEntry: Entry = null + @annotation.stableNull + protected var lastEntry: Entry | Null = null /* Uses the same implementation as mutable.HashMap. The hashtable holds the following invariant: * - For each i between 0 and table.length, the bucket at table(i) only contains keys whose hash-index is i. * - Every bucket is sorted in ascendant hash order * - The sum of the lengths of all buckets is equal to contentSize. */ - private[this] var table = new Array[Entry](tableSizeFor(LinkedHashMap.defaultinitialSize)) + private[this] var table = new Array[Entry | Null](tableSizeFor(LinkedHashMap.defaultinitialSize)) private[this] var threshold: Int = newThreshold(table.length) private[this] var contentSize = 0 override def last: (K, V) = - if (size > 0) (lastEntry.key, lastEntry.value) + if (size > 0) (lastEntry.nn.key, lastEntry.nn.value) else throw new NoSuchElementException("Cannot call .last on empty LinkedHashMap") override def lastOption: Option[(K, V)] = - if (size > 0) Some((lastEntry.key, lastEntry.value)) + if (size > 0) Some((lastEntry.nn.key, lastEntry.nn.value)) else None override def head: (K, V) = - if (size > 0) (firstEntry.key, firstEntry.value) + if (size > 0) (firstEntry.nn.key, firstEntry.nn.value) else throw new NoSuchElementException("Cannot call .head on empty LinkedHashMap") override def headOption: Option[(K, V)] = - if (size > 0) Some((firstEntry.key, firstEntry.value)) + if (size > 0) Some((firstEntry.nn.key, firstEntry.nn.value)) else None override def size = contentSize @@ -152,7 +154,7 @@ class LinkedHashMap[K, V] } } - private[this] def removeEntry0(elem: K): Entry = removeEntry0(elem, computeHash(elem)) + private[this] def removeEntry0(elem: K): Entry | Null = removeEntry0(elem, computeHash(elem)) /** Removes a key from this map if it exists * @@ -160,7 +162,7 @@ class LinkedHashMap[K, V] * @param hash the **improved** hashcode of `element` (see computeHash) * @return the node that contained element if it was present, otherwise null */ - private[this] def removeEntry0(elem: K, hash: Int): Entry = { + private[this] def removeEntry0(elem: K, hash: Int): Entry | Null = { val idx = index(hash) table(idx) match { case null => null @@ -199,7 +201,7 @@ class LinkedHashMap[K, V] @`inline` private[this] def index(hash: Int) = hash & (table.length - 1) - @`inline` private[this] def findEntry(key: K): Entry = { + @`inline` private[this] def findEntry(key: K): Entry | Null = { val hash = computeHash(key) table(index(hash)) match { case null => null @@ -218,11 +220,11 @@ class LinkedHashMap[K, V] } private[this] abstract class LinkedHashMapIterator[T] extends AbstractIterator[T] { - private[this] var cur = firstEntry + private[this] var cur: Entry | Null = firstEntry def extract(nd: Entry): T def hasNext: Boolean = cur ne null def next(): T = - if (hasNext) { val r = extract(cur); cur = cur.later; r } + if (hasNext) { val r = extract(cur.nn); cur = cur.nn.later; r } else Iterator.empty.next() } @@ -261,19 +263,19 @@ class LinkedHashMap[K, V] val hash = computeHash(key) val indexedHash = index(hash) - var foundEntry: Entry = null - var previousEntry: Entry = null + var foundEntry: Entry | Null = null + var previousEntry: Entry | Null = null table(indexedHash) match { case null => case nd => @tailrec - def findEntry(prev: Entry, nd: Entry, k: K, h: Int): Unit = { + def findEntry(prev: Entry | Null, nd: Entry, k: K, h: Int): Unit = { if (h == nd.hash && k == nd.key) { previousEntry = prev foundEntry = nd } else if ((nd.next eq null) || (nd.hash > h)) () - else findEntry(nd, nd.next, k, h) + else findEntry(nd, nd.next.nn, k, h) } findEntry(null, nd, key, hash) @@ -290,9 +292,9 @@ class LinkedHashMap[K, V] case (None, None) => // do nothing case (Some(_), None) => - if (previousEntry != null) previousEntry.next = foundEntry.next - else table(indexedHash) = foundEntry.next - deleteEntry(foundEntry) + if (previousEntry != null) previousEntry.nn.next = foundEntry.nn.next + else table(indexedHash) = foundEntry.nn.next + deleteEntry(foundEntry.nn) contentSize -= 1 case (None, Some(value)) => @@ -303,7 +305,7 @@ class LinkedHashMap[K, V] } else indexedHash put0(key, value, getOld = false, hash, newIndexedHash) - case (Some(_), Some(newValue)) => foundEntry.value = newValue + case (Some(_), Some(newValue)) => foundEntry.nn.value = newValue } nextValue } @@ -352,7 +354,7 @@ class LinkedHashMap[K, V] val e = new Entry(key, hash, value) if (firstEntry eq null) firstEntry = e else { - lastEntry.later = e + lastEntry.nn.later = e e.earlier = lastEntry } lastEntry = e @@ -362,28 +364,28 @@ class LinkedHashMap[K, V] /** Delete the entry from the LinkedHashMap, set the `earlier` and `later` pointers correctly */ private[this] def deleteEntry(e: Entry): Unit = { if (e.earlier eq null) firstEntry = e.later - else e.earlier.later = e.later + else e.earlier.nn.later = e.later if (e.later eq null) lastEntry = e.earlier - else e.later.earlier = e.earlier + else e.later.nn.earlier = e.earlier e.earlier = null e.later = null e.next = null } - private[this] def put0(key: K, value: V, getOld: Boolean): Some[V] = { + private[this] def put0(key: K, value: V, getOld: Boolean): Some[V] | Null = { if (contentSize + 1 >= threshold) growTable(table.length * 2) val hash = computeHash(key) val idx = index(hash) put0(key, value, getOld, hash, idx) } - private[this] def put0(key: K, value: V, getOld: Boolean, hash: Int, idx: Int): Some[V] = { + private[this] def put0(key: K, value: V, getOld: Boolean, hash: Int, idx: Int): Some[V] | Null = { table(idx) match { case null => table(idx) = createNewEntry(key, hash, value) case old => - var prev: Entry = null - var n = old + var prev: Entry | Null = null + var n: Entry | Null = old while ((n ne null) && n.hash <= hash) { if (n.hash == hash && key == n.key) { val old = n.value @@ -427,7 +429,7 @@ class LinkedHashMap[K, V] preHigh.next = null var lastLow = preLow var lastHigh = preHigh - var n = old + var n: Entry | Null = old while (n ne null) { val next = n.next if ((n.hash & oldlen) == 0) { // keep low @@ -492,15 +494,15 @@ object LinkedHashMap extends MapFactory[LinkedHashMap] { /** Class for the linked hash map entry, used internally. */ private[mutable] final class LinkedEntry[K, V](val key: K, val hash: Int, var value: V) { - var earlier: LinkedEntry[K, V] = null - var later: LinkedEntry[K, V] = null - var next: LinkedEntry[K, V] = null + var earlier: LinkedEntry[K, V] | Null = null + var later: LinkedEntry[K, V] | Null = null + var next: LinkedEntry[K, V] | Null = null @tailrec - final def findEntry(k: K, h: Int): LinkedEntry[K, V] = + final def findEntry(k: K, h: Int): LinkedEntry[K, V] | Null = if (h == hash && k == key) this else if ((next eq null) || (hash > h)) null - else next.findEntry(k, h) + else next.nn.findEntry(k, h) } /** The default load factor for the hash table */ diff --git a/library/src/scala/collection/mutable/LinkedHashSet.scala b/library/src/scala/collection/mutable/LinkedHashSet.scala index f0c2d2d843c3..e31cdceb0053 100644 --- a/library/src/scala/collection/mutable/LinkedHashSet.scala +++ b/library/src/scala/collection/mutable/LinkedHashSet.scala @@ -47,35 +47,37 @@ class LinkedHashSet[A] /*private*/ type Entry = LinkedHashSet.Entry[A] - protected var firstEntry: Entry = null + @annotation.stableNull + protected var firstEntry: Entry | Null = null - protected var lastEntry: Entry = null + @annotation.stableNull + protected var lastEntry: Entry | Null = null /* Uses the same implementation as mutable.HashSet. The hashtable holds the following invariant: * - For each i between 0 and table.length, the bucket at table(i) only contains keys whose hash-index is i. * - Every bucket is sorted in ascendant hash order * - The sum of the lengths of all buckets is equal to contentSize. */ - private[this] var table = new Array[Entry](tableSizeFor(LinkedHashSet.defaultinitialSize)) + private[this] var table = new Array[Entry | Null](tableSizeFor(LinkedHashSet.defaultinitialSize)) private[this] var threshold: Int = newThreshold(table.length) private[this] var contentSize = 0 override def last: A = - if (size > 0) lastEntry.key + if (size > 0) lastEntry.nn.key else throw new NoSuchElementException("Cannot call .last on empty LinkedHashSet") override def lastOption: Option[A] = - if (size > 0) Some(lastEntry.key) + if (size > 0) Some(lastEntry.nn.key) else None override def head: A = - if (size > 0) firstEntry.key + if (size > 0) firstEntry.nn.key else throw new NoSuchElementException("Cannot call .head on empty LinkedHashSet") override def headOption: Option[A] = - if (size > 0) Some(firstEntry.key) + if (size > 0) Some(firstEntry.nn.key) else None override def size: Int = contentSize @@ -108,11 +110,11 @@ class LinkedHashSet[A] override def remove(elem: A): Boolean = remove0(elem, computeHash(elem)) private[this] abstract class LinkedHashSetIterator[T] extends AbstractIterator[T] { - private[this] var cur = firstEntry + private[this] var cur: Entry | Null = firstEntry def extract(nd: Entry): T def hasNext: Boolean = cur ne null def next(): T = - if (hasNext) { val r = extract(cur); cur = cur.later; r } + if (hasNext) { val r = extract(cur.nn); cur = cur.nn.later; r } else Iterator.empty.next() } @@ -125,7 +127,7 @@ class LinkedHashSet[A] } override def foreach[U](f: A => U): Unit = { - var cur = firstEntry + var cur: Entry | Null = firstEntry while (cur ne null) { f(cur.key) cur = cur.later @@ -155,7 +157,7 @@ class LinkedHashSet[A] @`inline` private[this] def index(hash: Int) = hash & (table.length - 1) - @`inline` private[this] def findEntry(key: A): Entry = { + @`inline` private[this] def findEntry(key: A): Entry | Null = { val hash = computeHash(key) table(index(hash)) match { case null => null @@ -171,7 +173,7 @@ class LinkedHashSet[A] val e = new Entry(key, hash) if (firstEntry eq null) firstEntry = e else { - lastEntry.later = e + lastEntry.nn.later = e e.earlier = lastEntry } lastEntry = e @@ -194,8 +196,8 @@ class LinkedHashSet[A] case null => table(idx) = createNewEntry(elem, hash) case old => - var prev: Entry = null - var n = old + var prev: Entry | Null = null + var n: Entry | Null = old while ((n ne null) && n.hash <= hash) { if (n.hash == hash && elem == n.key) return false prev = n @@ -227,7 +229,7 @@ class LinkedHashSet[A] case nd => // find an element that matches var prev = nd - var next = nd.next + var next: Entry | Null = nd.next while ((next ne null) && next.hash <= hash) { if (next.hash == hash && next.key == elem) { prev.next = next.next @@ -257,13 +259,13 @@ class LinkedHashSet[A] while (oldlen < newlen) { var i = 0 while (i < oldlen) { - val old = table(i) + val old: Entry | Null = table(i) if (old ne null) { preLow.next = null preHigh.next = null var lastLow = preLow var lastHigh = preHigh - var n = old + var n: Entry | Null = old while (n ne null) { val next = n.next if ((n.hash & oldlen) == 0) { // keep low @@ -306,7 +308,7 @@ class LinkedHashSet[A] } @nowarn("""cat=deprecation&origin=scala\.collection\.Iterable\.stringPrefix""") - override protected[this] def stringPrefix = "LinkedHashSet" + override protected[this] def stringPrefix: String = "LinkedHashSet" } /** $factoryInfo @@ -330,12 +332,12 @@ object LinkedHashSet extends IterableFactory[LinkedHashSet] { /** Class for the linked hash set entry, used internally. */ private[mutable] final class Entry[A](val key: A, val hash: Int) { - var earlier: Entry[A] = null - var later: Entry[A] = null - var next: Entry[A] = null + @annotation.stableNull var earlier: Entry[A] | Null = null + @annotation.stableNull var later: Entry[A] | Null = null + @annotation.stableNull var next: Entry[A] | Null = null @tailrec - final def findEntry(k: A, h: Int): Entry[A] = + final def findEntry(k: A, h: Int): Entry[A] | Null = if (h == hash && k == key) this else if ((next eq null) || (hash > h)) null else next.findEntry(k, h) @@ -347,4 +349,3 @@ object LinkedHashSet extends IterableFactory[LinkedHashSet] { /** The default initial capacity for the hash table */ private[collection] final def defaultinitialSize: Int = 16 } - diff --git a/library/src/scala/collection/mutable/ListBuffer.scala b/library/src/scala/collection/mutable/ListBuffer.scala index c81dae108427..cf0730ef0cd4 100644 --- a/library/src/scala/collection/mutable/ListBuffer.scala +++ b/library/src/scala/collection/mutable/ListBuffer.scala @@ -49,11 +49,12 @@ class ListBuffer[A] @transient private[this] var mutationCount: Int = 0 private var first: List[A] = Nil - private var last0: ::[A] = null // last element (`last0` just because the name `last` is already taken) + @annotation.stableNull + private var last0: ::[A] | Null = null // last element (`last0` just because the name `last` is already taken) private[this] var aliased = false private[this] var len = 0 - private type Predecessor = ::[A] /*| Null*/ + private type Predecessor = ::[A] | Null def iterator: Iterator[A] = new MutationTracker.CheckedIterator(first.iterator, mutationCount) @@ -101,7 +102,7 @@ class ListBuffer[A] if (isEmpty) xs else { ensureUnaliased() - last0.next = xs + last0.nn.next = xs toList } } @@ -117,7 +118,7 @@ class ListBuffer[A] final def addOne(elem: A): this.type = { ensureUnaliased() val last1 = new ::[A](elem, Nil) - if (len == 0) first = last1 else last0.next = last1 + if (len == 0) first = last1 else last0.nn.next = last1 last0 = last1 len += 1 this @@ -149,7 +150,7 @@ class ListBuffer[A] val fresh = new ListBuffer[A].freshFrom(it) ensureUnaliased() if (len == 0) first = fresh.first - else last0.next = fresh.first + else last0.nn.next = fresh.first last0 = fresh.last0 len += fresh.length } @@ -216,7 +217,7 @@ class ListBuffer[A] first = newElem } else { // `p` can not be `null` because the case where `idx == 0` is handled above - val p = predecessor(idx) + val p = predecessor(idx).nn val newElem = new :: (elem, p.tail.tail) if (last0 eq p.tail) { last0 = newElem @@ -247,7 +248,7 @@ class ListBuffer[A] if (!fresh.isEmpty) { val follow = getNext(prev) if (prev eq null) first = fresh.first else prev.next = fresh.first - fresh.last0.next = follow + fresh.last0.nn.next = follow if (follow.isEmpty) last0 = fresh.last0 len += fresh.length } @@ -323,14 +324,14 @@ class ListBuffer[A] def flatMapInPlace(f: A => IterableOnce[A]^): this.type = { mutationCount += 1 var src = first - var dst: List[A] = null + var dst: List[A] | Null = null last0 = null len = 0 while(!src.isEmpty) { val it = f(src.head).iterator while(it.hasNext) { val v = new ::(it.next(), Nil) - if(dst eq null) dst = v else last0.next = v + if(dst eq null) dst = v else last0.nn.next = v last0 = v len += 1 } diff --git a/library/src/scala/collection/mutable/LongMap.scala b/library/src/scala/collection/mutable/LongMap.scala index fe9dab81dc24..171c9d44351a 100644 --- a/library/src/scala/collection/mutable/LongMap.scala +++ b/library/src/scala/collection/mutable/LongMap.scala @@ -72,12 +72,12 @@ final class LongMap[V] private[collection] (defaultEntry: Long -> V, initialBuff private[this] var mask = 0 private[this] var extraKeys: Int = 0 - private[this] var zeroValue: AnyRef = null - private[this] var minValue: AnyRef = null + @annotation.stableNull private[this] var zeroValue: AnyRef | Null = null + @annotation.stableNull private[this] var minValue: AnyRef | Null = null private[this] var _size = 0 private[this] var _vacant = 0 - private[this] var _keys: Array[Long] = null - private[this] var _values: Array[AnyRef] = null + private[this] var _keys: Array[Long] = _ + private[this] var _values: Array[AnyRef | Null] = _ if (initBlank) defaultInitialize(initialBufferSize) @@ -86,11 +86,11 @@ final class LongMap[V] private[collection] (defaultEntry: Long -> V, initialBuff if (n<0) 0x7 else (((1 << (32 - java.lang.Integer.numberOfLeadingZeros(n-1))) - 1) & 0x3FFFFFFF) | 0x7 _keys = new Array[Long](mask+1) - _values = new Array[AnyRef](mask+1) + _values = new Array[AnyRef | Null](mask+1) } private[collection] def initializeTo( - m: Int, ek: Int, zv: AnyRef, mv: AnyRef, sz: Int, vc: Int, kz: Array[Long], vz: Array[AnyRef] + m: Int, ek: Int, zv: AnyRef | Null, mv: AnyRef | Null, sz: Int, vc: Int, kz: Array[Long], vz: Array[AnyRef | Null] ): Unit = { mask = m; extraKeys = ek; zeroValue = zv; minValue = mv; _size = sz; _vacant = vc; _keys = kz; _values = vz } @@ -222,15 +222,15 @@ final class LongMap[V] private[collection] (defaultEntry: Long -> V, initialBuff * may not exist, if the default null/zero is acceptable. For key/value * pairs that do exist, `apply` (i.e. `map(key)`) is equally fast. */ - def getOrNull(key: Long): V = { + def getOrNull(key: Long): V | Null = { if (key == -key) { - if ((((key>>>63).toInt+1) & extraKeys) == 0) null.asInstanceOf[V] + if ((((key>>>63).toInt+1) & extraKeys) == 0) null else if (key == 0) zeroValue.asInstanceOf[V] else minValue.asInstanceOf[V] } else { val i = seekEntry(key) - if (i < 0) null.asInstanceOf[V] else _values(i).asInstanceOf[V] + if (i < 0) null else _values(i).asInstanceOf[V | Null] } } @@ -260,7 +260,7 @@ final class LongMap[V] private[collection] (defaultEntry: Long -> V, initialBuff val ov = _values mask = newMask _keys = new Array[Long](mask+1) - _values = new Array[AnyRef](mask+1) + _values = new Array[AnyRef | Null](mask+1) _vacant = 0 var i = 0 while (i < ok.length) { @@ -387,12 +387,12 @@ final class LongMap[V] private[collection] (defaultEntry: Long -> V, initialBuff private[this] val kz = _keys private[this] val vz = _values - private[this] var nextPair: (Long, V) = + private[this] var nextPair: (Long, V) | Null = if (extraKeys==0) null else if ((extraKeys&1)==1) (0L, zeroValue.asInstanceOf[V]) else (Long.MinValue, minValue.asInstanceOf[V]) - private[this] var anotherPair: (Long, V) = + private[this] var anotherPair: (Long, V) | Null = if (extraKeys==3) (Long.MinValue, minValue.asInstanceOf[V]) else null @@ -417,7 +417,7 @@ final class LongMap[V] private[collection] (defaultEntry: Long -> V, initialBuff anotherPair = null } else nextPair = null - ans + ans.nn } } @@ -522,17 +522,17 @@ final class LongMap[V] private[collection] (defaultEntry: Long -> V, initialBuff * collection immediately. */ def mapValuesNow[V1](f: V => V1): LongMap[V1] = { - val zv = if ((extraKeys & 1) == 1) f(zeroValue.asInstanceOf[V]).asInstanceOf[AnyRef] else null - val mv = if ((extraKeys & 2) == 2) f(minValue.asInstanceOf[V]).asInstanceOf[AnyRef] else null + val zv = if ((extraKeys & 1) == 1) f(zeroValue.asInstanceOf[V]).asInstanceOf[AnyRef | Null] else null + val mv = if ((extraKeys & 2) == 2) f(minValue.asInstanceOf[V]).asInstanceOf[AnyRef | Null] else null val lm = new LongMap[V1](LongMap.exceptionDefault, 1, initBlank = false) val kz = java.util.Arrays.copyOf(_keys, _keys.length) - val vz = new Array[AnyRef](_values.length) + val vz = new Array[AnyRef | Null](_values.length) var i,j = 0 while (i < _keys.length & j < _size) { val k = _keys(i) if (k != -k) { j += 1 - vz(i) = f(_values(i).asInstanceOf[V]).asInstanceOf[AnyRef] + vz(i) = f(_values(i).asInstanceOf[V]).asInstanceOf[AnyRef | Null] } i += 1 } @@ -550,14 +550,14 @@ final class LongMap[V] private[collection] (defaultEntry: Long -> V, initialBuff * Note: the default, if any, is not transformed. */ def transformValuesInPlace(f: V => V): this.type = { - if ((extraKeys & 1) == 1) zeroValue = f(zeroValue.asInstanceOf[V]).asInstanceOf[AnyRef] - if ((extraKeys & 2) == 2) minValue = f(minValue.asInstanceOf[V]).asInstanceOf[AnyRef] + if ((extraKeys & 1) == 1) zeroValue = f(zeroValue.asInstanceOf[V]).asInstanceOf[AnyRef | Null] + if ((extraKeys & 2) == 2) minValue = f(minValue.asInstanceOf[V]).asInstanceOf[AnyRef | Null] var i,j = 0 while (i < _keys.length & j < _size) { val k = _keys(i) if (k != -k) { j += 1 - _values(i) = f(_values(i).asInstanceOf[V]).asInstanceOf[AnyRef] + _values(i) = f(_values(i).asInstanceOf[V]).asInstanceOf[AnyRef | Null] } i += 1 } diff --git a/library/src/scala/collection/mutable/PriorityQueue.scala b/library/src/scala/collection/mutable/PriorityQueue.scala index 050fa5159ee6..2c1e1671b215 100644 --- a/library/src/scala/collection/mutable/PriorityQueue.scala +++ b/library/src/scala/collection/mutable/PriorityQueue.scala @@ -89,7 +89,7 @@ sealed class PriorityQueue[A](implicit val ord: Ordering[A]) def p_size0 = size0 def p_size0_=(s: Int) = size0 = s - def p_array = array + def p_array: Array[AnyRef | Null] = array.asInstanceOf[Array[AnyRef | Null]] def p_ensureSize(n: Int) = super.ensureSize(n) def p_ensureAdditionalSize(n: Int) = super.ensureSize(size0 + n) def p_swap(a: Int, b: Int): Unit = { @@ -130,8 +130,8 @@ sealed class PriorityQueue[A](implicit val ord: Ordering[A]) def result() = this - private def toA(x: AnyRef): A = x.asInstanceOf[A] - protected def fixUp(as: Array[AnyRef], m: Int): Unit = { + private def toA(x: AnyRef | Null): A = x.asInstanceOf[A] + protected def fixUp(as: Array[AnyRef | Null], m: Int): Unit = { var k: Int = m // use `ord` directly to avoid allocating `OrderingOps` while (k > 1 && ord.lt(toA(as(k / 2)), toA(as(k)))) { @@ -140,7 +140,7 @@ sealed class PriorityQueue[A](implicit val ord: Ordering[A]) } } - protected def fixDown(as: Array[AnyRef], m: Int, n: Int): Boolean = { + protected def fixDown(as: Array[AnyRef | Null], m: Int, n: Int): Boolean = { // returns true if any swaps were done (used in heapify) var k: Int = m while (n >= 2 * k) { diff --git a/library/src/scala/collection/mutable/Queue.scala b/library/src/scala/collection/mutable/Queue.scala index 91c2fdd81334..a4b087e068c8 100644 --- a/library/src/scala/collection/mutable/Queue.scala +++ b/library/src/scala/collection/mutable/Queue.scala @@ -29,7 +29,7 @@ import scala.collection.generic.DefaultSerializable * @define mayNotTerminateInf * @define willNotTerminateInf */ -class Queue[A] protected (array: Array[AnyRef], start: Int, end: Int) +class Queue[A] protected (array: Array[AnyRef | Null], start: Int, end: Int) extends ArrayDeque[A](array, start, end) with IndexedSeqOps[A, Queue, Queue[A]] with StrictOptimizedSeqOps[A, Queue, Queue[A]] @@ -118,7 +118,7 @@ class Queue[A] protected (array: Array[AnyRef], start: Int, end: Int) bf.result() } - override protected def ofArray(array: Array[AnyRef], end: Int): Queue[A] = + override protected def ofArray(array: Array[AnyRef | Null], end: Int): Queue[A] = new Queue(array, start = 0, end) } diff --git a/library/src/scala/collection/mutable/RedBlackTree.scala b/library/src/scala/collection/mutable/RedBlackTree.scala index 87f922be4ef0..b8347b5984c7 100644 --- a/library/src/scala/collection/mutable/RedBlackTree.scala +++ b/library/src/scala/collection/mutable/RedBlackTree.scala @@ -33,11 +33,19 @@ private[collection] object RedBlackTree { // Therefore, while obtaining the size of the whole tree is O(1), knowing the number of entries inside a range is O(n) // on the size of the range. - final class Tree[A, B](var root: Node[A, B], var size: Int) { + final class Tree[A, B](var root: Node[A, B] | Null, var size: Int) { def treeCopy(): Tree[A, B] = new Tree(copyTree(root), size) } - final class Node[A, B](var key: A, var value: B, var red: Boolean, var left: Node[A, B], var right: Node[A, B], var parent: Node[A, B]) { + final class Node[A, B]( + var key: A, var value: B, var red: Boolean, + @annotation.stableNull + var left: Node[A, B] | Null, + @annotation.stableNull + var right: Node[A, B] | Null, + @annotation.stableNull + var parent: Node[A, B] | Null + ) { override def toString: String = "Node(" + key + ", " + value + ", " + red + ", " + left + ", " + right + ")" } @@ -48,10 +56,10 @@ private[collection] object RedBlackTree { object Node { @`inline` def apply[A, B](key: A, value: B, red: Boolean, - left: Node[A, B], right: Node[A, B], parent: Node[A, B]): Node[A, B] = + left: Node[A, B], right: Node[A, B], parent: Node[A, B] | Null): Node[A, B] = new Node(key, value, red, left, right, parent) - @`inline` def leaf[A, B](key: A, value: B, red: Boolean, parent: Node[A, B]): Node[A, B] = + @`inline` def leaf[A, B](key: A, value: B, red: Boolean, parent: Node[A, B] | Null): Node[A, B] = new Node(key, value, red, null, null, parent) def unapply[A, B](t: Node[A, B]) = Some((t.key, t.value, t.left, t.right, t.parent)) @@ -59,12 +67,12 @@ private[collection] object RedBlackTree { // ---- getters ---- - def isRed(node: Node[_, _]) = (node ne null) && node.red - def isBlack(node: Node[_, _]) = (node eq null) || !node.red + def isRed(node: Node[_, _] | Null) = (node ne null) && node.red + def isBlack(node: Node[_, _] | Null) = (node eq null) || !node.red // ---- size ---- - def size(node: Node[_, _]): Int = if (node eq null) 0 else 1 + size(node.left) + size(node.right) + def size(node: Node[_, _] | Null): Int = if (node eq null) 0 else 1 + size(node.left) + size(node.right) def size(tree: Tree[_, _]): Int = tree.size def isEmpty(tree: Tree[_, _]) = tree.root eq null def clear(tree: Tree[_, _]): Unit = { tree.root = null; tree.size = 0 } @@ -76,7 +84,7 @@ private[collection] object RedBlackTree { case node => Some(node.value) } - @tailrec private[this] def getNode[A, B](node: Node[A, B], key: A)(implicit ord: Ordering[A]): Node[A, B] = + @tailrec private[this] def getNode[A, B](node: Node[A, B] | Null, key: A)(implicit ord: Ordering[A]): Node[A, B] | Null = if (node eq null) null else { val cmp = ord.compare(key, node.key) @@ -97,7 +105,7 @@ private[collection] object RedBlackTree { case node => Some(node.key) } - private def minNode[A, B](node: Node[A, B]): Node[A, B] = + private def minNode[A, B](node: Node[A, B] | Null): Node[A, B] | Null = if (node eq null) null else minNodeNonNull(node) @tailrec def minNodeNonNull[A, B](node: Node[A, B]): Node[A, B] = @@ -113,7 +121,7 @@ private[collection] object RedBlackTree { case node => Some(node.key) } - private def maxNode[A, B](node: Node[A, B]): Node[A, B] = + private def maxNode[A, B](node: Node[A, B] | Null): Node[A, B] | Null = if (node eq null) null else maxNodeNonNull(node) @tailrec def maxNodeNonNull[A, B](node: Node[A, B]): Node[A, B] = @@ -135,11 +143,12 @@ private[collection] object RedBlackTree { case node => Some(node.key) } - private[this] def minNodeAfter[A, B](node: Node[A, B], key: A)(implicit ord: Ordering[A]): Node[A, B] = { + private[this] def minNodeAfter[A, B](node: Node[A, B] | Null, key: A)(implicit ord: Ordering[A]): Node[A, B] | Null = { if (node eq null) null else { - var y: Node[A, B] = null - var x = node + // We know x is not null initially, so y will only be null before the first iteration of the loop. + var y: Node[A, B] = null.asInstanceOf[Node[A, B]] + var x: Node[A, B] | Null = node var cmp = 1 while ((x ne null) && cmp != 0) { y = x @@ -165,11 +174,12 @@ private[collection] object RedBlackTree { case node => Some(node.key) } - private[this] def maxNodeBefore[A, B](node: Node[A, B], key: A)(implicit ord: Ordering[A]): Node[A, B] = { + private[this] def maxNodeBefore[A, B](node: Node[A, B] | Null, key: A)(implicit ord: Ordering[A]): Node[A, B] | Null = { if (node eq null) null else { - var y: Node[A, B] = null - var x = node + // We know x is not null initially, so y will only be null before the first iteration of the loop. + var y: Node[A, B] = null.asInstanceOf[Node[A, B]] + var x: Node[A, B] | Null = node var cmp = 1 while ((x ne null) && cmp != 0) { y = x @@ -183,7 +193,7 @@ private[collection] object RedBlackTree { // ---- insertion ---- def insert[A, B](tree: Tree[A, B], key: A, value: B)(implicit ord: Ordering[A]): Unit = { - var y: Node[A, B] = null + var y: Node[A, B] | Null = null var x = tree.root var cmp = 1 while ((x ne null) && cmp != 0) { @@ -192,7 +202,7 @@ private[collection] object RedBlackTree { x = if (cmp < 0) x.left else x.right } - if (cmp == 0) y.value = value + if (cmp == 0) y.nn.value = value else { val z = Node.leaf(key, value, red = true, y) @@ -208,41 +218,41 @@ private[collection] object RedBlackTree { private[this] def fixAfterInsert[A, B](tree: Tree[A, B], node: Node[A, B]): Unit = { var z = node while (isRed(z.parent)) { - if (z.parent eq z.parent.parent.left) { - val y = z.parent.parent.right + if (z.parent eq z.parent.nn.parent.nn.left) { + val y = z.parent.nn.parent.nn.right if (isRed(y)) { - z.parent.red = false - y.red = false - z.parent.parent.red = true - z = z.parent.parent + z.parent.nn.red = false + y.nn.red = false + z.parent.nn.parent.nn.red = true + z = z.parent.nn.parent.nn } else { - if (z eq z.parent.right) { - z = z.parent + if (z eq z.parent.nn.right) { + z = z.parent.nn rotateLeft(tree, z) } - z.parent.red = false - z.parent.parent.red = true - rotateRight(tree, z.parent.parent) + z.parent.nn.red = false + z.parent.nn.parent.nn.red = true + rotateRight(tree, z.parent.nn.parent.nn) } } else { // symmetric cases - val y = z.parent.parent.left + val y = z.parent.nn.parent.nn.left if (isRed(y)) { - z.parent.red = false - y.red = false - z.parent.parent.red = true - z = z.parent.parent + z.parent.nn.red = false + y.nn.red = false + z.parent.nn.parent.nn.red = true + z = z.parent.nn.parent.nn } else { - if (z eq z.parent.left) { - z = z.parent + if (z eq z.parent.nn.left) { + z = z.parent.nn rotateRight(tree, z) } - z.parent.red = false - z.parent.parent.red = true - rotateLeft(tree, z.parent.parent) + z.parent.nn.red = false + z.parent.nn.parent.nn.red = true + rotateLeft(tree, z.parent.nn.parent.nn) } } } - tree.root.red = false + tree.root.nn.red = false } // ---- deletion ---- @@ -252,8 +262,8 @@ private[collection] object RedBlackTree { if (z ne null) { var y = z var yIsRed = y.red - var x: Node[A, B] = null - var xParent: Node[A, B] = null + var x: Node[A, B] | Null = null + var xParent: Node[A, B] | Null = null if (z.left eq null) { x = z.right @@ -275,11 +285,11 @@ private[collection] object RedBlackTree { xParent = y.parent transplant(tree, y, y.right) y.right = z.right - y.right.parent = y + y.right.nn.parent = y } transplant(tree, z, y) y.left = z.left - y.left.parent = y + y.left.nn.parent = y y.red = z.red } @@ -288,64 +298,64 @@ private[collection] object RedBlackTree { } } - private[this] def fixAfterDelete[A, B](tree: Tree[A, B], node: Node[A, B], parent: Node[A, B]): Unit = { + private[this] def fixAfterDelete[A, B](tree: Tree[A, B], node: Node[A, B] | Null, parent: Node[A, B] | Null): Unit = { var x = node var xParent = parent while ((x ne tree.root) && isBlack(x)) { - if (x eq xParent.left) { - var w = xParent.right + if (x eq xParent.nn.left) { + var w = xParent.nn.right // assert(w ne null) - if (w.red) { - w.red = false - xParent.red = true - rotateLeft(tree, xParent) - w = xParent.right + if (w.nn.red) { + w.nn.red = false + xParent.nn.red = true + rotateLeft(tree, xParent.nn) + w = xParent.nn.right } - if (isBlack(w.left) && isBlack(w.right)) { - w.red = true + if (isBlack(w.nn.left) && isBlack(w.nn.right)) { + w.nn.red = true x = xParent } else { - if (isBlack(w.right)) { - w.left.red = false - w.red = true - rotateRight(tree, w) - w = xParent.right + if (isBlack(w.nn.right)) { + w.nn.left.nn.red = false + w.nn.red = true + rotateRight(tree, w.nn) + w = xParent.nn.right } - w.red = xParent.red - xParent.red = false - w.right.red = false - rotateLeft(tree, xParent) + w.nn.red = xParent.nn.red + xParent.nn.red = false + w.nn.right.nn.red = false + rotateLeft(tree, xParent.nn) x = tree.root } } else { // symmetric cases - var w = xParent.left + var w = xParent.nn.left // assert(w ne null) - if (w.red) { - w.red = false - xParent.red = true - rotateRight(tree, xParent) - w = xParent.left + if (w.nn.red) { + w.nn.red = false + xParent.nn.red = true + rotateRight(tree, xParent.nn) + w = xParent.nn.left } - if (isBlack(w.right) && isBlack(w.left)) { - w.red = true + if (isBlack(w.nn.right) && isBlack(w.nn.left)) { + w.nn.red = true x = xParent } else { - if (isBlack(w.left)) { - w.right.red = false - w.red = true - rotateLeft(tree, w) - w = xParent.left + if (isBlack(w.nn.left)) { + w.nn.right.nn.red = false + w.nn.red = true + rotateLeft(tree, w.nn) + w = xParent.nn.left } - w.red = xParent.red - xParent.red = false - w.left.red = false - rotateRight(tree, xParent) + w.nn.red = xParent.nn.red + xParent.nn.red = false + w.nn.left.nn.red = false + rotateRight(tree, xParent.nn) x = tree.root } } - xParent = x.parent + xParent = x.nn.parent } if (x ne null) x.red = false } @@ -356,12 +366,12 @@ private[collection] object RedBlackTree { * Returns the node that follows `node` in an in-order tree traversal. If `node` has the maximum key (and is, * therefore, the last node), this method returns `null`. */ - private[this] def successor[A, B](node: Node[A, B]): Node[A, B] = { + private[this] def successor[A, B](node: Node[A, B]): Node[A, B] | Null = { if (node.right ne null) minNodeNonNull(node.right) else { var x = node var y = x.parent - while ((y ne null) && (x eq y.right)) { + while ((y ne null) && (x eq y.nn.right)) { x = y y = y.parent } @@ -373,7 +383,7 @@ private[collection] object RedBlackTree { * Returns the node that precedes `node` in an in-order tree traversal. If `node` has the minimum key (and is, * therefore, the first node), this method returns `null`. */ - private[this] def predecessor[A, B](node: Node[A, B]): Node[A, B] = { + private[this] def predecessor[A, B](node: Node[A, B]): Node[A, B] | Null = { if (node.left ne null) maxNodeNonNull(node.left) else { var x = node @@ -388,14 +398,14 @@ private[collection] object RedBlackTree { private[this] def rotateLeft[A, B](tree: Tree[A, B], x: Node[A, B]): Unit = if (x ne null) { // assert(x.right ne null) - val y = x.right + val y = x.right.nn x.right = y.left if (y.left ne null) y.left.parent = x y.parent = x.parent if (x.parent eq null) tree.root = y - else if (x eq x.parent.left) x.parent.left = y + else if (x eq x.parent.nn.left) x.parent.left = y else x.parent.right = y y.left = x @@ -404,7 +414,7 @@ private[collection] object RedBlackTree { private[this] def rotateRight[A, B](tree: Tree[A, B], x: Node[A, B]): Unit = if (x ne null) { // assert(x.left ne null) - val y = x.left + val y = x.left.nn x.left = y.right if (y.right ne null) y.right.parent = x @@ -422,9 +432,9 @@ private[collection] object RedBlackTree { * Transplant the node `from` to the place of node `to`. This is done by setting `from` as a child of `to`'s previous * parent and setting `from`'s parent to the `to`'s previous parent. The children of `from` are left unchanged. */ - private[this] def transplant[A, B](tree: Tree[A, B], to: Node[A, B], from: Node[A, B]): Unit = { + private[this] def transplant[A, B](tree: Tree[A, B], to: Node[A, B], from: Node[A, B] | Null): Unit = { if (to.parent eq null) tree.root = from - else if (to eq to.parent.left) to.parent.left = from + else if (to eq to.parent.nn.left) to.parent.left = from else to.parent.right = from if (from ne null) from.parent = to.parent @@ -434,7 +444,7 @@ private[collection] object RedBlackTree { def foreach[A, B, U](tree: Tree[A, B], f: ((A, B)) => U): Unit = foreachNode(tree.root, f) - private[this] def foreachNode[A, B, U](node: Node[A, B], f: ((A, B)) => U): Unit = + private[this] def foreachNode[A, B, U](node: Node[A, B] | Null, f: ((A, B)) => U): Unit = if (node ne null) foreachNodeNonNull(node, f) private[this] def foreachNodeNonNull[A, B, U](node: Node[A, B], f: ((A, B)) => U): Unit = { @@ -469,7 +479,7 @@ private[collection] object RedBlackTree { def transform[A, B](tree: Tree[A, B], f: (A, B) => B): Unit = transformNode(tree.root, f) - private[this] def transformNode[A, B, U](node: Node[A, B], f: (A, B) => B): Unit = + private[this] def transformNode[A, B, U](node: Node[A, B] | Null, f: (A, B) => B): Unit = if (node ne null) transformNodeNonNull(node, f) private[this] def transformNodeNonNull[A, B, U](node: Node[A, B], f: (A, B) => B): Unit = { @@ -503,18 +513,17 @@ private[collection] object RedBlackTree { nextResult(node) } - private[this] var nextNode: Node[A, B] = start match { + private[this] var nextNode: Node[A, B] | Null = start match { case None => minNode(tree.root) case Some(from) => minNodeAfter(tree.root, from) } private[this] def setNullIfAfterEnd(): Unit = - if (end.isDefined && (nextNode ne null) && ord.compare(nextNode.key, end.get) >= 0) + if (end.isDefined && (nextNode ne null) && ord.compare(nextNode.nn.key, end.get) >= 0) nextNode = null setNullIfAfterEnd() } - private[this] final class EntriesIterator[A: Ordering, B](tree: Tree[A, B], start: Option[A], end: Option[A]) extends TreeIterator[A, B, (A, B)](tree, start, end) { @@ -550,7 +559,7 @@ private[collection] object RedBlackTree { */ private[this] def hasProperParentRefs[A, B](tree: Tree[A, B]): Boolean = { - def hasProperParentRefs(node: Node[A, B]): Boolean = { + def hasProperParentRefs(node: Node[A, B] | Null): Boolean = { if (node eq null) true else { if ((node.left ne null) && (node.left.parent ne node) || @@ -560,13 +569,13 @@ private[collection] object RedBlackTree { } if(tree.root eq null) true - else (tree.root.parent eq null) && hasProperParentRefs(tree.root) + else (tree.root.nn.parent eq null) && hasProperParentRefs(tree.root) } /** * Returns true if this node follows the properties of a binary search tree. */ - private[this] def isValidBST[A, B](node: Node[A, B])(implicit ord: Ordering[A]): Boolean = { + private[this] def isValidBST[A, B](node: Node[A, B] | Null)(implicit ord: Ordering[A]): Boolean = { if (node eq null) true else { if ((node.left ne null) && (ord.compare(node.key, node.left.key) <= 0) || @@ -581,13 +590,13 @@ private[collection] object RedBlackTree { */ private[this] def isValidRedBlackTree[A, B](tree: Tree[A, B]): Boolean = { - def noRedAfterRed(node: Node[A, B]): Boolean = { + def noRedAfterRed(node: Node[A, B] | Null): Boolean = { if (node eq null) true else if (node.red && (isRed(node.left) || isRed(node.right))) false else noRedAfterRed(node.left) && noRedAfterRed(node.right) } - def blackHeight(node: Node[A, B]): Int = { + def blackHeight(node: Node[A, B] | Null): Int = { if (node eq null) 1 else { val lh = blackHeight(node.left) @@ -607,7 +616,7 @@ private[collection] object RedBlackTree { /** Build a Tree suitable for a TreeSet from an ordered sequence of keys */ def fromOrderedKeys[A](xs: Iterator[A]^, size: Int): Tree[A, Null] = { val maxUsedDepth = 32 - Integer.numberOfLeadingZeros(size) // maximum depth of non-leaf nodes - def f(level: Int, size: Int): Node[A, Null] = size match { + def f(level: Int, size: Int): Node[A, Null] | Null = size match { case 0 => null case 1 => new Node(xs.next(), null, level == maxUsedDepth && level != 1, null, null, null) case n => @@ -617,7 +626,7 @@ private[collection] object RedBlackTree { val right = f(level+1, size-1-leftSize) val n = new Node(x, null, red = false, left, right, null) if(left ne null) left.parent = n - right.parent = n + right.nn.parent = n n } new Tree(f(1, size), size) @@ -626,7 +635,7 @@ private[collection] object RedBlackTree { /** Build a Tree suitable for a TreeMap from an ordered sequence of key/value pairs */ def fromOrderedEntries[A, B](xs: Iterator[(A, B)]^, size: Int): Tree[A, B] = { val maxUsedDepth = 32 - Integer.numberOfLeadingZeros(size) // maximum depth of non-leaf nodes - def f(level: Int, size: Int): Node[A, B] = size match { + def f(level: Int, size: Int): Node[A, B] | Null = size match { case 0 => null case 1 => val (k, v) = xs.next() @@ -638,13 +647,13 @@ private[collection] object RedBlackTree { val right = f(level+1, size-1-leftSize) val n = new Node(k, v, red = false, left, right, null) if(left ne null) left.parent = n - right.parent = n + right.nn.parent = n n } new Tree(f(1, size), size) } - def copyTree[A, B](n: Node[A, B]): Node[A, B] = + def copyTree[A, B](n: Node[A, B] | Null): Node[A, B] | Null = if(n eq null) null else { val c = new Node(n.key, n.value, n.red, copyTree(n.left), copyTree(n.right), null) if(c.left != null) c.left.parent = c diff --git a/library/src/scala/collection/mutable/Stack.scala b/library/src/scala/collection/mutable/Stack.scala index 8ca4b58a7af6..26cda88ecd4f 100644 --- a/library/src/scala/collection/mutable/Stack.scala +++ b/library/src/scala/collection/mutable/Stack.scala @@ -35,7 +35,7 @@ import scala.collection.{IterableFactoryDefaults, IterableOnce, SeqFactory, Stri * @define willNotTerminateInf */ @migration("Stack is now based on an ArrayDeque instead of a linked list", "2.13.0") -class Stack[A] protected (array: Array[AnyRef], start: Int, end: Int) +class Stack[A] protected (array: Array[AnyRef | Null], start: Int, end: Int) extends ArrayDeque[A](array, start, end) with IndexedSeqOps[A, Stack, Stack[A]] with StrictOptimizedSeqOps[A, Stack, Stack[A]] @@ -122,7 +122,7 @@ class Stack[A] protected (array: Array[AnyRef], start: Int, end: Int) bf.result() } - override protected def ofArray(array: Array[AnyRef], end: Int): Stack[A] = + override protected def ofArray(array: Array[AnyRef | Null], end: Int): Stack[A] = new Stack(array, start = 0, end) } diff --git a/library/src/scala/collection/mutable/UnrolledBuffer.scala b/library/src/scala/collection/mutable/UnrolledBuffer.scala index aee908369f0f..a3d34d4d06a3 100644 --- a/library/src/scala/collection/mutable/UnrolledBuffer.scala +++ b/library/src/scala/collection/mutable/UnrolledBuffer.scala @@ -130,20 +130,20 @@ sealed class UnrolledBuffer[T](implicit val tag: ClassTag[T]) def iterator: Iterator[T] = new AbstractIterator[T] { var pos: Int = -1 - var node: Unrolled[T] = headptr + var node: Unrolled[T] | Null = headptr scan() private def scan(): Unit = { pos += 1 - while (pos >= node.size) { + while (pos >= node.nn.size) { pos = 0 - node = node.next + node = node.nn.next if (node eq null) return } } def hasNext = node ne null def next() = if (hasNext) { - val r = node.array(pos) + val r = node.nn.array(pos) scan() r } else Iterator.empty.next() @@ -264,10 +264,10 @@ object UnrolledBuffer extends StrictOptimizedClassTagSeqFactory[UnrolledBuffer] /** Unrolled buffer node. */ - class Unrolled[T: ClassTag] private[collection] (var size: Int, var array: Array[T], var next: Unrolled[T], val buff: UnrolledBuffer[T] = null) { + class Unrolled[T: ClassTag] private[collection] (var size: Int, var array: Array[T], var next: Unrolled[T] | Null, val buff: UnrolledBuffer[T] | Null = null) { this: Unrolled[T]^{} => private[collection] def this() = this(0, new Array[T](unrolledlength), null, null) - private[collection] def this(b: UnrolledBuffer[T]) = this(0, new Array[T](unrolledlength), null, b) + private[collection] def this(b: UnrolledBuffer[T] | Null) = this(0, new Array[T](unrolledlength), null, b) private def nextlength = if (buff eq null) unrolledlength else buff.calcNextLength(array.length) @@ -278,10 +278,10 @@ object UnrolledBuffer extends StrictOptimizedClassTagSeqFactory[UnrolledBuffer] this } else { next = new Unrolled[T](0, new Array[T](nextlength), null, buff) - next append elem + next.nn append elem } def foreach[U](f: T => U): Unit = { - var unrolled = this + var unrolled: Unrolled[T] | Null = this var i = 0 while (unrolled ne null) { val chunkarr = unrolled.array @@ -296,7 +296,7 @@ object UnrolledBuffer extends StrictOptimizedClassTagSeqFactory[UnrolledBuffer] } } def mapInPlace(f: T => T): Unit = { - var unrolled = this + var unrolled: Unrolled[T] | Null = this var i = 0 while (unrolled ne null) { val chunkarr = unrolled.array @@ -311,11 +311,11 @@ object UnrolledBuffer extends StrictOptimizedClassTagSeqFactory[UnrolledBuffer] } } @tailrec final def apply(idx: Int): T = - if (idx < size) array(idx) else next.apply(idx - size) + if (idx < size) array(idx) else next.nn.apply(idx - size) @tailrec final def update(idx: Int, newelem: T): Unit = - if (idx < size) array(idx) = newelem else next.update(idx - size, newelem) + if (idx < size) array(idx) = newelem else next.nn.update(idx - size, newelem) @tailrec final def locate(idx: Int): Unrolled[T] = - if (idx < size) this else next.locate(idx - size) + if (idx < size) this else next.nn.locate(idx - size) def prepend(elem: T) = if (size < array.length) { // shift the elements of the array right // then insert the element @@ -349,7 +349,7 @@ object UnrolledBuffer extends StrictOptimizedClassTagSeqFactory[UnrolledBuffer] size -= 1 if (tryMergeWithNext()) buffer.lastPtr = this r - } else next.remove(idx - size, buffer) + } else next.nn.remove(idx - size, buffer) @tailrec final def subtractOne(elem: T, buffer: UnrolledBuffer[T]): Boolean = { var i = 0 @@ -360,7 +360,7 @@ object UnrolledBuffer extends StrictOptimizedClassTagSeqFactory[UnrolledBuffer] } i += 1 } - if(next ne null) next.subtractOne(elem, buffer) else false + if(next ne null) next.nn.subtractOne(elem, buffer) else false } // shifts left elements after `leftb` (overwrites `leftb`) @@ -372,11 +372,11 @@ object UnrolledBuffer extends StrictOptimizedClassTagSeqFactory[UnrolledBuffer] } nullout(i, i + 1) } - protected def tryMergeWithNext() = if (next != null && (size + next.size) < (array.length * waterline / waterlineDenom)) { + protected def tryMergeWithNext() = if (next != null && (size + next.nn.size) < (array.length * waterline / waterlineDenom)) { // copy the next array, then discard the next node - Array.copy(next.array, 0, array, size, next.size) - size = size + next.size - next = next.next + Array.copy(next.nn.array, 0, array, size, next.nn.size) + size = size + next.nn.size + next = next.nn.next if (next eq null) true else false // checks if last node was thrown out } else false @@ -417,7 +417,7 @@ object UnrolledBuffer extends StrictOptimizedClassTagSeqFactory[UnrolledBuffer] } appended } - else next.insertAll(idx - size, t, buffer) + else next.nn.insertAll(idx - size, t, buffer) } private def nullout(from: Int, until: Int): Unit = { diff --git a/library/src/scala/concurrent/BatchingExecutor.scala b/library/src/scala/concurrent/BatchingExecutor.scala index b108af7c3eca..b69dc86fd181 100644 --- a/library/src/scala/concurrent/BatchingExecutor.scala +++ b/library/src/scala/concurrent/BatchingExecutor.scala @@ -26,7 +26,7 @@ trait Batchable { } private[concurrent] object BatchingExecutorStatics { - final val emptyBatchArray: Array[Runnable] = new Array[Runnable](0) + final val emptyBatchArray: Array[Runnable | Null] = new Array[Runnable | Null](0) // Max number of Runnables executed nested before starting to batch (to prevent stack exhaustion) final val syncPreBatchDepth = 16 @@ -98,9 +98,13 @@ private[concurrent] trait BatchingExecutor extends Executor { * In order to conserve allocations, the first element in the batch is stored "unboxed" in * the `first` field. Subsequent Runnables are stored in the array called `other`. */ - private[this] sealed abstract class AbstractBatch protected (protected final var first: Runnable, protected final var other: Array[Runnable], protected final var size: Int) { + private[this] sealed abstract class AbstractBatch protected ( + @annotation.stableNull protected final var first: Runnable | Null, + protected final var other: Array[Runnable | Null], + protected final var size: Int + ) { - private[this] final def ensureCapacity(curSize: Int): Array[Runnable] = { + private[this] final def ensureCapacity(curSize: Int): Array[Runnable | Null] = { val curOther = this.other val curLen = curOther.length if (curSize <= curLen) curOther @@ -108,7 +112,7 @@ private[concurrent] trait BatchingExecutor extends Executor { val newLen = if (curLen == 0) 4 else curLen << 1 if (newLen <= curLen) throw new StackOverflowError("Space limit of asynchronous stack reached: " + curLen) - val newOther = new Array[Runnable](newLen) + val newOther = new Array[Runnable | Null](newLen) System.arraycopy(curOther, 0, newOther, 0, curLen) this.other = newOther newOther @@ -129,14 +133,14 @@ private[concurrent] trait BatchingExecutor extends Executor { (this.size: @switch) match { case 0 => case 1 => - val next = this.first + val next = this.first.nn this.first = null this.size = 0 next.run() runN(n - 1) case sz => val o = this.other - val next = o(sz - 2) + val next = o(sz - 2).nn o(sz - 2) = null this.size = sz - 1 next.run() @@ -144,7 +148,7 @@ private[concurrent] trait BatchingExecutor extends Executor { } } - private[this] final class AsyncBatch private(_first: Runnable, _other: Array[Runnable], _size: Int) extends AbstractBatch(_first, _other, _size) with Runnable with BlockContext with (BlockContext => Throwable) { + private[this] final class AsyncBatch private(_first: Runnable | Null, _other: Array[Runnable | Null], _size: Int) extends AbstractBatch(_first, _other, _size) with Runnable with BlockContext with (BlockContext => Throwable | Null) { private[this] final var parentBlockContext: BlockContext = BatchingExecutorStatics.MissingParentBlockContext final def this(runnable: Runnable) = this(runnable, BatchingExecutorStatics.emptyBatchArray, 1) @@ -159,7 +163,7 @@ private[concurrent] trait BatchingExecutor extends Executor { } /* LOGIC FOR ASYNCHRONOUS BATCHES */ - override final def apply(prevBlockContext: BlockContext): Throwable = try { + override final def apply(prevBlockContext: BlockContext): Throwable | Null = try { parentBlockContext = prevBlockContext runN(BatchingExecutorStatics.runLimit) null @@ -175,7 +179,7 @@ private[concurrent] trait BatchingExecutor extends Executor { * Only attempt to resubmit when there are `Runnables` left to process. * Note that `cause` can be `null`. */ - private[this] final def resubmit(cause: Throwable): Throwable = + private[this] final def resubmit(cause: Throwable | Null): Throwable | Null = if (this.size > 0) { try { submitForExecution(this); cause } catch { case inner: Throwable => diff --git a/library/src/scala/concurrent/ExecutionContext.scala b/library/src/scala/concurrent/ExecutionContext.scala index 703b962a0f17..625a0c3cbeba 100644 --- a/library/src/scala/concurrent/ExecutionContext.scala +++ b/library/src/scala/concurrent/ExecutionContext.scala @@ -39,7 +39,7 @@ import scala.annotation.implicitNotFound * `scala.concurrent.ExecutionContext.Implicits.global`. * The recommended approach is to add `(implicit ec: ExecutionContext)` to methods, * or class constructor parameters, which need an `ExecutionContext`. - * + * * Then locally import a specific `ExecutionContext` in one place for the entire * application or module, passing it implicitly to individual methods. * Alternatively define a local implicit val with the required `ExecutionContext`. @@ -197,7 +197,7 @@ object ExecutionContext { * * @return the global [[ExecutionContext]] */ - final lazy val global: ExecutionContextExecutor = impl.ExecutionContextImpl.fromExecutor(null: Executor) + final lazy val global: ExecutionContextExecutor = impl.ExecutionContextImpl.fromExecutor(null: Executor | Null) /** * WARNING: Only ever execute logic which will quickly return control to the caller. @@ -212,7 +212,7 @@ object ExecutionContext { * * Do *not* call any blocking code in the `Runnable`s submitted to this `ExecutionContext` * as it will prevent progress by other enqueued `Runnable`s and the calling `Thread`. - * + * * Symptoms of misuse of this `ExecutionContext` include, but are not limited to, deadlocks * and severe performance problems. * @@ -253,7 +253,7 @@ object ExecutionContext { * @param reporter a function for error reporting * @return the `ExecutionContext` using the given `ExecutorService` */ - def fromExecutorService(e: ExecutorService, reporter: Throwable => Unit): ExecutionContextExecutorService = + def fromExecutorService(e: ExecutorService | Null, reporter: Throwable => Unit): ExecutionContextExecutorService = impl.ExecutionContextImpl.fromExecutorService(e, reporter) /** Creates an `ExecutionContext` from the given `ExecutorService` with the [[scala.concurrent.ExecutionContext$.defaultReporter default reporter]]. @@ -269,7 +269,7 @@ object ExecutionContext { * @param e the `ExecutorService` to use. If `null`, a new `ExecutorService` is created with [[scala.concurrent.ExecutionContext$.global default configuration]]. * @return the `ExecutionContext` using the given `ExecutorService` */ - def fromExecutorService(e: ExecutorService): ExecutionContextExecutorService = fromExecutorService(e, defaultReporter) + def fromExecutorService(e: ExecutorService | Null): ExecutionContextExecutorService = fromExecutorService(e, defaultReporter) /** Creates an `ExecutionContext` from the given `Executor`. * @@ -277,7 +277,7 @@ object ExecutionContext { * @param reporter a function for error reporting * @return the `ExecutionContext` using the given `Executor` */ - def fromExecutor(e: Executor, reporter: Throwable => Unit): ExecutionContextExecutor = + def fromExecutor(e: Executor | Null, reporter: Throwable => Unit): ExecutionContextExecutor = impl.ExecutionContextImpl.fromExecutor(e, reporter) /** Creates an `ExecutionContext` from the given `Executor` with the [[scala.concurrent.ExecutionContext$.defaultReporter default reporter]]. @@ -285,7 +285,7 @@ object ExecutionContext { * @param e the `Executor` to use. If `null`, a new `Executor` is created with [[scala.concurrent.ExecutionContext$.global default configuration]]. * @return the `ExecutionContext` using the given `Executor` */ - def fromExecutor(e: Executor): ExecutionContextExecutor = fromExecutor(e, defaultReporter) + def fromExecutor(e: Executor | Null): ExecutionContextExecutor = fromExecutor(e, defaultReporter) /** The default reporter simply prints the stack trace of the `Throwable` to [[java.lang.System#err System.err]]. * diff --git a/library/src/scala/concurrent/JavaConversions.scala b/library/src/scala/concurrent/JavaConversions.scala index eab15c2610b9..046c14ff063e 100644 --- a/library/src/scala/concurrent/JavaConversions.scala +++ b/library/src/scala/concurrent/JavaConversions.scala @@ -26,14 +26,14 @@ object JavaConversions { * Creates a new `ExecutionContext` which uses the provided `ExecutorService`. */ @deprecated("Use `ExecutionContext.fromExecutorService` instead", "2.13.0") - implicit def asExecutionContext(exec: ExecutorService): ExecutionContextExecutorService = + implicit def asExecutionContext(exec: ExecutorService | Null): ExecutionContextExecutorService = ExecutionContext.fromExecutorService(exec) /** * Creates a new `ExecutionContext` which uses the provided `Executor`. */ @deprecated("Use `ExecutionContext.fromExecutor` instead", "2.13.0") - implicit def asExecutionContext(exec: Executor): ExecutionContextExecutor = + implicit def asExecutionContext(exec: Executor | Null): ExecutionContextExecutor = ExecutionContext.fromExecutor(exec) } diff --git a/library/src/scala/concurrent/impl/ExecutionContextImpl.scala b/library/src/scala/concurrent/impl/ExecutionContextImpl.scala index 3096297f620a..54fd2dc4795b 100644 --- a/library/src/scala/concurrent/impl/ExecutionContextImpl.scala +++ b/library/src/scala/concurrent/impl/ExecutionContextImpl.scala @@ -109,16 +109,17 @@ private[concurrent] object ExecutionContextImpl { } } - def fromExecutor(e: Executor, reporter: Throwable => Unit = ExecutionContext.defaultReporter): ExecutionContextExecutor = + def fromExecutor(e: Executor | Null, reporter: Throwable => Unit = ExecutionContext.defaultReporter): ExecutionContextExecutor = e match { case null => createDefaultExecutorService(reporter) case some => new ExecutionContextImpl(some, reporter) } - def fromExecutorService(es: ExecutorService, reporter: Throwable => Unit = ExecutionContext.defaultReporter): + def fromExecutorService(es: ExecutorService | Null, reporter: Throwable => Unit = ExecutionContext.defaultReporter): ExecutionContextExecutorService = es match { case null => createDefaultExecutorService(reporter) case some => + // This is a anonymous class extending a Java class, so we left inferred flexible types in the signatures. new ExecutionContextImpl(some, reporter) with ExecutionContextExecutorService { private[this] final def asExecutorService: ExecutorService = executor.asInstanceOf[ExecutorService] final override def shutdown() = asExecutorService.shutdown() diff --git a/library/src/scala/concurrent/impl/Promise.scala b/library/src/scala/concurrent/impl/Promise.scala index 58cb130a6603..2ca2228d159f 100644 --- a/library/src/scala/concurrent/impl/Promise.scala +++ b/library/src/scala/concurrent/impl/Promise.scala @@ -34,8 +34,8 @@ import java.io.{IOException, NotSerializableException, ObjectInputStream, Object */ private[impl] final class CompletionLatch[T] extends AbstractQueuedSynchronizer with (Try[T] => Unit) { //@volatie not needed since we use acquire/release - /*@volatile*/ private[this] var _result: Try[T] = null - final def result: Try[T] = _result + /*@volatile*/ @annotation.stableNull private[this] var _result: Try[T] | Null = null + final def result: Try[T] | Null = _result override protected def tryAcquireShared(ignored: Int): Int = if (getState != 0) 1 else -1 override protected def tryReleaseShared(ignore: Int): Boolean = { setState(1) @@ -238,7 +238,7 @@ private[concurrent] object Promise { else /*if (state.isInstanceOf[Callbacks[T]]) */ "Future()" } - private[this] final def tryAwait0(atMost: Duration): Try[T] = + private[this] final def tryAwait0(atMost: Duration): Try[T] | Null = if (atMost ne Duration.Undefined) { val v = value0 if (v ne null) v @@ -270,14 +270,14 @@ private[concurrent] object Promise { @throws(classOf[Exception]) final def result(atMost: Duration)(implicit permit: CanAwait): T = - tryAwait0(atMost).get // returns the value, or throws the contained exception + tryAwait0(atMost).nn.get // returns the value, or throws the contained exception override final def isCompleted: Boolean = value0 ne null override final def value: Option[Try[T]] = Option(value0) @tailrec // returns null if not completed - private final def value0: Try[T] = { + private final def value0: Try[T] | Null = { val state = get() if (state.isInstanceOf[Try[_]]) state.asInstanceOf[Try[T]] else if (state.isInstanceOf[Link[_]]) state.asInstanceOf[Link[T]].promise(this).value0 @@ -348,7 +348,7 @@ private[concurrent] object Promise { concatCallbacks(m.rest, new ManyCallbacks(m.first, right)) } - @tailrec private[this] final def removeCallback(cs: Callbacks[T], t: Transformation[_, _], result: Callbacks[T] = null): AnyRef = + @tailrec private[this] final def removeCallback(cs: Callbacks[T], t: Transformation[_, _], result: Callbacks[T] | Null = null): AnyRef = if (cs eq t) { if (result == null) Noop else result @@ -375,7 +375,7 @@ private[concurrent] object Promise { /** Link this promise to the root of another promise. */ - @tailrec private[concurrent] final def linkRootOf(target: DefaultPromise[T], link: Link[T]): Unit = + @tailrec private[concurrent] final def linkRootOf(target: DefaultPromise[T], link: Link[T] | Null): Unit = if (this ne target) { val state = get() if (state.isInstanceOf[Try[_]]) { @@ -435,7 +435,7 @@ private[concurrent] object Promise { override final def toString: String = "ManyCallbacks" } - private[this] final val Noop = new Transformation[Nothing, Nothing](Xform_noop, null, ExecutionContext.parasitic) + private[this] final val Noop = new Transformation[Nothing, Nothing](Xform_noop, null: (Any => Any) | Null, ExecutionContext.parasitic) /** * A Transformation[F, T] receives an F (it is a Callback[F]) and applies a transformation function to that F, @@ -444,13 +444,13 @@ private[concurrent] object Promise { * function's type parameters are erased, and the _xform tag will be used to reify them. **/ final class Transformation[-F, T] private[this] ( - private[this] final var _fun: Any => Any, - private[this] final var _ec: ExecutionContext, - private[this] final var _arg: Try[F], + @annotation.stableNull private[this] final var _fun: (Any => Any) | Null, + @annotation.stableNull private[this] final var _ec: ExecutionContext | Null, + @annotation.stableNull private[this] final var _arg: Try[F] | Null, private[this] final val _xform: Int ) extends DefaultPromise[T]() with Callbacks[F] with Runnable with Batchable { - final def this(xform: Int, f: _ => _, ec: ExecutionContext) = - this(f.asInstanceOf[Any => Any], ec.prepare(): @nowarn("cat=deprecation"), null, xform) + final def this(xform: Int, f: (_ => _) | Null, ec: ExecutionContext) = + this(f.asInstanceOf[(Any => Any) | Null], ec.prepare(): @nowarn("cat=deprecation"), null, xform) final def benefitsFromBatching: Boolean = _xform != Xform_onComplete && _xform != Xform_foreach @@ -461,13 +461,13 @@ private[concurrent] object Promise { final def submitWithValue(resolved: Try[F]): this.type = { _arg = resolved val e = _ec - try e.execute(this) /* Safe publication of _arg, _fun, _ec */ + try e.nn.execute(this) /* Safe publication of _arg, _fun, _ec */ catch { case t: Throwable => _fun = null // allow to GC _arg = null // see above _ec = null // see above again - handleFailure(t, e) + handleFailure(t, e.nn) } this @@ -487,14 +487,14 @@ private[concurrent] object Promise { // Gets invoked by the ExecutionContext, when we have a value to transform. override final def run(): Unit = { - val v = _arg - val fun = _fun - val ec = _ec + val v = _arg.nn + val fun = _fun.nn + val ec = _ec.nn _fun = null // allow to GC _arg = null // see above _ec = null // see above try { - val resolvedResult: Try[_] = + val resolvedResult: Try[_] | Null = (_xform: @switch) match { case Xform_noop => null diff --git a/library/src/scala/io/BufferedSource.scala b/library/src/scala/io/BufferedSource.scala index b569cce8d797..d4cfee2db477 100644 --- a/library/src/scala/io/BufferedSource.scala +++ b/library/src/scala/io/BufferedSource.scala @@ -67,7 +67,7 @@ class BufferedSource(inputStream: InputStream, bufferSize: Int)(implicit val cod class BufferedLineIterator extends AbstractIterator[String] with Iterator[String] { private[this] val lineReader = decachedReader - var nextLine: String = null + @annotation.stableNull var nextLine: String | Null = null override def hasNext = { if (nextLine == null) diff --git a/library/src/scala/io/Codec.scala b/library/src/scala/io/Codec.scala index 44b5becd7d78..62c875cf2b27 100644 --- a/library/src/scala/io/Codec.scala +++ b/library/src/scala/io/Codec.scala @@ -39,14 +39,14 @@ class Codec(val charSet: Charset) { // these variables allow configuring the Codec object, and then // all decoders and encoders retrieved from it will use these settings. - private[this] var _onMalformedInput: Action = null - private[this] var _onUnmappableCharacter: Action = null - private[this] var _encodingReplacement: Array[Byte] = null - private[this] var _decodingReplacement: String = null + private[this] var _onMalformedInput: Action | Null = null + private[this] var _onUnmappableCharacter: Action | Null = null + private[this] var _encodingReplacement: Array[Byte] | Null = null + private[this] var _decodingReplacement: String | Null = null private[this] var _onCodingException: Handler = e => throw e /** The name of the Codec. */ - override def toString = name + override def toString: String = name // these methods can be chained to configure the variables above def onMalformedInput(newAction: Action): this.type = { _onMalformedInput = newAction ; this } @@ -55,7 +55,7 @@ class Codec(val charSet: Charset) { def encodingReplaceWith(newReplacement: Array[Byte]): this.type = { _encodingReplacement = newReplacement ; this } def onCodingException(handler: Handler): this.type = { _onCodingException = handler ; this } - def name = charSet.name + def name: String = charSet.name def encoder: CharsetEncoder = { val enc = charSet.newEncoder() if (_onMalformedInput ne null) enc onMalformedInput _onMalformedInput diff --git a/library/src/scala/io/Source.scala b/library/src/scala/io/Source.scala index c66c12d3c8e1..49c965d46bc8 100644 --- a/library/src/scala/io/Source.scala +++ b/library/src/scala/io/Source.scala @@ -158,8 +158,8 @@ object Source { def createBufferedSource( inputStream: InputStream, bufferSize: Int = DefaultBufSize, - reset: () => Source = null, - close: () => Unit = null + reset: (() => Source) | Null = null, + close: (() => Unit) | Null = null )(implicit codec: Codec): BufferedSource = { // workaround for default arguments being unable to refer to other parameters val resetFn = if (reset == null) () => createBufferedSource(inputStream, bufferSize, reset, close)(codec) else reset @@ -344,15 +344,17 @@ abstract class Source extends Iterator[Char] with Closeable { report(pos, "warning! " + msg, out) } - private[this] var resetFunction: () => Source = null - private[this] var closeFunction: () => Unit = null + @annotation.stableNull + private[this] var resetFunction: (() => Source) | Null = null + @annotation.stableNull + private[this] var closeFunction: (() => Unit) | Null = null private[this] var positioner: Positioner = RelaxedPositioner - def withReset(f: () => Source): this.type = { + def withReset(f: (() => Source) | Null): this.type = { resetFunction = f this } - def withClose(f: () => Unit): this.type = { + def withClose(f: (() => Unit) | Null): this.type = { closeFunction = f this } diff --git a/library/src/scala/jdk/AnyAccumulator.scala b/library/src/scala/jdk/AnyAccumulator.scala index 94814594008c..f2aa79b82853 100644 --- a/library/src/scala/jdk/AnyAccumulator.scala +++ b/library/src/scala/jdk/AnyAccumulator.scala @@ -326,7 +326,7 @@ private[jdk] class AnyAccumulatorStepper[A](private[this] val acc: AnyAccumulato ans } - def trySplit(): AnyStepper[A] = + def trySplit(): AnyStepper[A] | Null = if (N <= 1) null else { val half = N >> 1 diff --git a/library/src/scala/jdk/DoubleAccumulator.scala b/library/src/scala/jdk/DoubleAccumulator.scala index 403f877364c4..8a1aa8c88c88 100644 --- a/library/src/scala/jdk/DoubleAccumulator.scala +++ b/library/src/scala/jdk/DoubleAccumulator.scala @@ -406,7 +406,7 @@ private[jdk] class DoubleAccumulatorStepper(private val acc: DoubleAccumulator) ans } - def trySplit(): DoubleStepper = + def trySplit(): DoubleStepper | Null = if (N <= 1) null else { val half = N >> 1 diff --git a/library/src/scala/jdk/FunctionWrappers.scala b/library/src/scala/jdk/FunctionWrappers.scala index d6a4d071144d..674922d5d60a 100644 --- a/library/src/scala/jdk/FunctionWrappers.scala +++ b/library/src/scala/jdk/FunctionWrappers.scala @@ -57,7 +57,7 @@ object FunctionWrappers { } case class AsJavaBiFunction[T, U, R](sf: scala.Function2[T, U, R]) extends java.util.function.BiFunction[T, U, R] { - def apply(x1: T, x2: U) = sf.apply(x1, x2) + def apply(x1: T, x2: U): R = sf.apply(x1, x2) } class RichFunction2AsBiFunction[T, U, R](private val underlying: scala.Function2[T, U, R]) extends AnyVal { @@ -111,7 +111,7 @@ object FunctionWrappers { } case class AsJavaBinaryOperator[T](sf: scala.Function2[T, T, T]) extends java.util.function.BinaryOperator[T] { - def apply(x1: T, x2: T) = sf.apply(x1, x2) + def apply(x1: T, x2: T): T = sf.apply(x1, x2) } class RichFunction2AsBinaryOperator[T](private val underlying: scala.Function2[T, T, T]) extends AnyVal { @@ -234,7 +234,7 @@ object FunctionWrappers { } case class AsJavaDoubleFunction[R](sf: scala.Function1[Double, R]) extends java.util.function.DoubleFunction[R] { - def apply(x1: scala.Double) = sf.apply(x1) + def apply(x1: scala.Double): R = sf.apply(x1) } class RichFunction1AsDoubleFunction[R](private val underlying: scala.Function1[Double, R]) extends AnyVal { @@ -376,7 +376,7 @@ object FunctionWrappers { } case class AsJavaFunction[T, R](sf: scala.Function1[T, R]) extends java.util.function.Function[T, R] { - def apply(x1: T) = sf.apply(x1) + def apply(x1: T): R = sf.apply(x1) } class RichFunction1AsFunction[T, R](private val underlying: scala.Function1[T, R]) extends AnyVal { @@ -449,7 +449,7 @@ object FunctionWrappers { } case class AsJavaIntFunction[R](sf: scala.Function1[Int, R]) extends java.util.function.IntFunction[R] { - def apply(x1: scala.Int) = sf.apply(x1) + def apply(x1: scala.Int): R = sf.apply(x1) } class RichFunction1AsIntFunction[R](private val underlying: scala.Function1[Int, R]) extends AnyVal { @@ -637,7 +637,7 @@ object FunctionWrappers { } case class AsJavaLongFunction[R](sf: scala.Function1[Long, R]) extends java.util.function.LongFunction[R] { - def apply(x1: scala.Long) = sf.apply(x1) + def apply(x1: scala.Long): R = sf.apply(x1) } class RichFunction1AsLongFunction[R](private val underlying: scala.Function1[Long, R]) extends AnyVal { @@ -887,7 +887,7 @@ object FunctionWrappers { } case class AsJavaSupplier[T](sf: scala.Function0[T]) extends java.util.function.Supplier[T] { - def get() = sf.apply() + def get(): T = sf.apply() } class RichFunction0AsSupplier[T](private val underlying: scala.Function0[T]) extends AnyVal { @@ -1065,7 +1065,7 @@ object FunctionWrappers { case class FromJavaUnaryOperator[T](jf: java.util.function.UnaryOperator[T]) extends scala.Function1[T, T] { - def apply(x1: T) = jf.apply(x1) + def apply(x1: T): T = jf.apply(x1) } class RichUnaryOperatorAsFunction1[T](private val underlying: java.util.function.UnaryOperator[T]) extends AnyVal { @@ -1076,7 +1076,7 @@ object FunctionWrappers { } case class AsJavaUnaryOperator[T](sf: scala.Function1[T, T]) extends java.util.function.UnaryOperator[T] { - def apply(x1: T) = sf.apply(x1) + def apply(x1: T): T = sf.apply(x1) } class RichFunction1AsUnaryOperator[T](private val underlying: scala.Function1[T, T]) extends AnyVal { diff --git a/library/src/scala/jdk/IntAccumulator.scala b/library/src/scala/jdk/IntAccumulator.scala index 2b507940ec2e..0a85b519ae63 100644 --- a/library/src/scala/jdk/IntAccumulator.scala +++ b/library/src/scala/jdk/IntAccumulator.scala @@ -412,7 +412,7 @@ private[jdk] class IntAccumulatorStepper(private val acc: IntAccumulator) extend ans } - def trySplit(): IntStepper = + def trySplit(): IntStepper | Null = if (N <= 1) null else { val half = N >> 1 diff --git a/library/src/scala/jdk/LongAccumulator.scala b/library/src/scala/jdk/LongAccumulator.scala index dd39c6e05a4d..ee9ce79b0ba5 100644 --- a/library/src/scala/jdk/LongAccumulator.scala +++ b/library/src/scala/jdk/LongAccumulator.scala @@ -407,7 +407,7 @@ private[jdk] class LongAccumulatorStepper(private val acc: LongAccumulator) exte ans } - def trySplit(): LongStepper = + def trySplit(): LongStepper | Null = if (N <= 1) null else { val half = N >> 1 diff --git a/library/src/scala/math/BigDecimal.scala b/library/src/scala/math/BigDecimal.scala index 7e0cb6434401..25d5f55a55d9 100644 --- a/library/src/scala/math/BigDecimal.scala +++ b/library/src/scala/math/BigDecimal.scala @@ -29,7 +29,7 @@ object BigDecimal { private final val deci2binary = 3.3219280948873626 // Ratio of log(10) to log(2) private[this] val minCached = -512 private[this] val maxCached = 512 - val defaultMathContext = MathContext.DECIMAL128 + val defaultMathContext: MathContext = MathContext.DECIMAL128 /** Cache only for defaultMathContext using BigDecimals in a small range. */ private[this] lazy val cache = new Array[BigDecimal](maxCached - minCached + 1) @@ -305,7 +305,7 @@ object BigDecimal { implicit def double2bigDecimal(d: Double): BigDecimal = decimal(d) /** Implicit conversion from `java.math.BigDecimal` to `scala.BigDecimal`. */ - implicit def javaBigDecimal2bigDecimal(x: BigDec): BigDecimal = if (x == null) null else apply(x) + implicit def javaBigDecimal2bigDecimal(x: BigDec | Null): BigDecimal | Null = if (x == null) null else apply(x) } /** diff --git a/library/src/scala/math/BigInt.scala b/library/src/scala/math/BigInt.scala index cf7c45d5682a..76c4a7caf67f 100644 --- a/library/src/scala/math/BigInt.scala +++ b/library/src/scala/math/BigInt.scala @@ -19,6 +19,7 @@ import scala.language.`2.13` import scala.annotation.nowarn import scala.language.implicitConversions import scala.collection.immutable.NumericRange +import scala.runtime.ScalaRunTime.mapNull object BigInt { @@ -122,9 +123,14 @@ object BigInt { */ implicit def long2bigInt(l: Long): BigInt = apply(l) + // For the following function, both the parameter and the return type are non-nullable. + // However, if a null reference is passed explicitly, this method will still return null. + // We intentionally keep this signature to discourage passing nulls implicitly while + // preserving the previous behavior for backward compatibility. + /** Implicit conversion from `java.math.BigInteger` to `scala.BigInt`. */ - implicit def javaBigInteger2bigInt(x: BigInteger): BigInt = if (x eq null) null else apply(x) + implicit def javaBigInteger2bigInt(x: BigInteger): BigInt = mapNull(x, apply(x)) // this method is adapted from Google Guava's version at // https://github.com/google/guava/blob/master/guava/src/com/google/common/math/LongMath.java @@ -177,8 +183,10 @@ object BigInt { * * It wraps `java.math.BigInteger`, with optimization for small values that can be encoded in a `Long`. */ -final class BigInt private (private var _bigInteger: BigInteger, private val _long: Long) - extends ScalaNumber +final class BigInt private ( + @annotation.stableNull private var _bigInteger: BigInteger | Null, + private val _long: Long +) extends ScalaNumber with ScalaNumericConversions with Serializable with Ordered[BigInt] @@ -295,9 +303,9 @@ final class BigInt private (private var _bigInteger: BigInteger, private val _lo */ def compare(that: BigInt): Int = if (this.longEncoding) { - if (that.longEncoding) java.lang.Long.compare(this._long, that._long) else -that._bigInteger.signum() + if (that.longEncoding) java.lang.Long.compare(this._long, that._long) else -that._bigInteger.nn.signum() } else { - if (that.longEncoding) _bigInteger.signum() else this._bigInteger.compareTo(that._bigInteger) + if (that.longEncoding) _bigInteger.nn.signum() else this._bigInteger.nn.compareTo(that._bigInteger) } /** Addition of BigInts @@ -426,7 +434,7 @@ final class BigInt private (private var _bigInteger: BigInteger, private val _lo if (that.longEncoding) { if (that._long == 0) return this.abs // if (that._long == Long.MinValue) return this gcd (-that) - val red = (this._bigInteger mod BigInteger.valueOf(that._long.abs)).longValue() + val red = (this._bigInteger.nn mod BigInteger.valueOf(that._long.abs)).longValue() if (red == 0) return that.abs BigInt(BigInt.longGcd(that._long.abs, red)) } else BigInt(this.bigInteger.gcd(that.bigInteger)) @@ -479,7 +487,7 @@ final class BigInt private (private var _bigInteger: BigInteger, private val _lo * +1 if it is greater than 0, * 0 if it is equal to 0. */ - def signum: Int = if (longEncoding) java.lang.Long.signum(_long) else _bigInteger.signum() + def signum: Int = if (longEncoding) java.lang.Long.signum(_long) else _bigInteger.nn.signum() /** Returns the sign of this BigInt; * -1 if it is less than 0, @@ -536,7 +544,7 @@ final class BigInt private (private var _bigInteger: BigInteger, private val _lo if (longEncoding) { if (_long < 0) 64 - java.lang.Long.numberOfLeadingZeros(-(_long + 1)) // takes care of Long.MinValue else 64 - java.lang.Long.numberOfLeadingZeros(_long) - } else _bigInteger.bitLength() + } else _bigInteger.nn.bitLength() /** Returns the number of bits in the two's complement representation of this BigInt * that differ from its sign bit. @@ -590,7 +598,7 @@ final class BigInt private (private var _bigInteger: BigInteger, private val _lo * overall magnitude of the BigInt value as well as return a result with * the opposite sign. */ - def longValue: Long = if (longEncoding) _long else _bigInteger.longValue + def longValue: Long = if (longEncoding) _long else _bigInteger.nn.longValue /** Converts this `BigInt` to a `float`. * If this `BigInt` has too great a magnitude to represent as a float, diff --git a/library/src/scala/quoted/FromExpr.scala b/library/src/scala/quoted/FromExpr.scala index 2d9e3ebc5ab6..fb230c6d22b9 100644 --- a/library/src/scala/quoted/FromExpr.scala +++ b/library/src/scala/quoted/FromExpr.scala @@ -104,7 +104,7 @@ object FromExpr { */ given OptionFromExpr[T](using Type[T], FromExpr[T]): FromExpr[Option[T]] with { def unapply(x: Expr[Option[T]])(using Quotes) = x match { - case '{ Option[T](${Expr(y)}) } => Some(Option(y)) + case '{ Option[T](${Expr(y)}: T) } => Some(Option(y)) case '{ None } => Some(None) case '{ ${Expr(opt)} : Some[T] } => Some(opt) case _ => None diff --git a/library/src/scala/ref/ReferenceQueue.scala b/library/src/scala/ref/ReferenceQueue.scala index 2fb8e646b444..339d26b05ae2 100644 --- a/library/src/scala/ref/ReferenceQueue.scala +++ b/library/src/scala/ref/ReferenceQueue.scala @@ -19,7 +19,7 @@ class ReferenceQueue[+T <: AnyRef] { private[ref] val underlying: java.lang.ref.ReferenceQueue[_ <: T] = new java.lang.ref.ReferenceQueue[T] override def toString: String = underlying.toString - protected def Wrapper(jref: java.lang.ref.Reference[_]): Option[Reference[T]] = + protected def Wrapper(jref: java.lang.ref.Reference[_] | Null): Option[Reference[T]] = jref match { case null => None case ref => Some(ref.asInstanceOf[ReferenceWithWrapper[T]].wrapper) diff --git a/library/src/scala/ref/SoftReference.scala b/library/src/scala/ref/SoftReference.scala index dd79863ff03b..fbe505d32314 100644 --- a/library/src/scala/ref/SoftReference.scala +++ b/library/src/scala/ref/SoftReference.scala @@ -14,7 +14,7 @@ package scala.ref import scala.language.`2.13` -class SoftReference[+T <: AnyRef](value : T, queue : ReferenceQueue[T]) extends ReferenceWrapper[T] { +class SoftReference[+T <: AnyRef](value : T, queue : ReferenceQueue[T] | Null) extends ReferenceWrapper[T] { def this(value : T) = this(value, null) val underlying: java.lang.ref.SoftReference[_ <: T] = @@ -33,5 +33,5 @@ object SoftReference { def unapply[T <: AnyRef](sr: SoftReference[T]): Option[T] = Option(sr.underlying.get) } -private class SoftReferenceWithWrapper[T <: AnyRef](value: T, queue: ReferenceQueue[T], val wrapper: SoftReference[T]) +private class SoftReferenceWithWrapper[T <: AnyRef](value: T, queue: ReferenceQueue[T] | Null, val wrapper: SoftReference[T]) extends java.lang.ref.SoftReference[T](value, if (queue == null) null else queue.underlying.asInstanceOf[java.lang.ref.ReferenceQueue[T]]) with ReferenceWithWrapper[T] diff --git a/library/src/scala/ref/WeakReference.scala b/library/src/scala/ref/WeakReference.scala index 196b79131a04..b28a1ab8d3f2 100644 --- a/library/src/scala/ref/WeakReference.scala +++ b/library/src/scala/ref/WeakReference.scala @@ -19,7 +19,7 @@ import scala.language.`2.13` * The new functionality is (1) results are Option values, instead of using null. * (2) There is an extractor that maps the weak reference itself into an option. */ -class WeakReference[+T <: AnyRef](value: T, queue: ReferenceQueue[T]) extends ReferenceWrapper[T] { +class WeakReference[+T <: AnyRef](value: T, queue: ReferenceQueue[T] | Null) extends ReferenceWrapper[T] { def this(value: T) = this(value, null) val underlying: java.lang.ref.WeakReference[_ <: T] = new WeakReferenceWithWrapper[T](value, queue, this) @@ -35,5 +35,5 @@ object WeakReference { def unapply[T <: AnyRef](wr: WeakReference[T]): Option[T] = Option(wr.underlying.get) } -private class WeakReferenceWithWrapper[T <: AnyRef](value: T, queue: ReferenceQueue[T], val wrapper: WeakReference[T]) +private class WeakReferenceWithWrapper[T <: AnyRef](value: T, queue: ReferenceQueue[T] | Null, val wrapper: WeakReference[T]) extends java.lang.ref.WeakReference[T](value, if (queue == null) null else queue.underlying.asInstanceOf[java.lang.ref.ReferenceQueue[T]]) with ReferenceWithWrapper[T] diff --git a/library/src/scala/reflect/ClassManifestDeprecatedApis.scala b/library/src/scala/reflect/ClassManifestDeprecatedApis.scala index 17e05050132a..f3b866f42858 100644 --- a/library/src/scala/reflect/ClassManifestDeprecatedApis.scala +++ b/library/src/scala/reflect/ClassManifestDeprecatedApis.scala @@ -137,7 +137,8 @@ trait ClassManifestDeprecatedApis[T] extends OptManifest[T] { protected def argString = if (typeArguments.nonEmpty) typeArguments.mkString("[", ", ", "]") - else if (runtimeClass.isArray) "["+ClassManifest.fromClass(runtimeClass.getComponentType)+"]" + // TODO: remove .nn here after 3.8. See #24070 + else if (runtimeClass.isArray) "["+ClassManifest.fromClass(runtimeClass.getComponentType.nn)+"]" else "" } diff --git a/library/src/scala/reflect/NameTransformer.scala b/library/src/scala/reflect/NameTransformer.scala index 15d2af96f15e..a78afcb13b6b 100644 --- a/library/src/scala/reflect/NameTransformer.scala +++ b/library/src/scala/reflect/NameTransformer.scala @@ -33,10 +33,10 @@ object NameTransformer { private[this] val nops = 128 private[this] val ncodes = 26 * 26 - private class OpCodes(val op: Char, val code: String, val next: OpCodes) + private class OpCodes(val op: Char, val code: String, val next: OpCodes | Null) - private[this] val op2code = new Array[String](nops) - private[this] val code2op = new Array[OpCodes](ncodes) + private[this] val op2code = new Array[String | Null](nops) + private[this] val code2op = new Array[OpCodes | Null](ncodes) private def enterOp(op: Char, code: String) = { op2code(op.toInt) = code val c = (code.charAt(1) - 'a') * 26 + code.charAt(2) - 'a' @@ -69,7 +69,7 @@ object NameTransformer { * @return the string with all recognized opchars replaced with their encoding */ def encode(name: String): String = { - var buf: StringBuilder = null + var buf: StringBuilder | Null = null val len = name.length() var i = 0 while (i < len) { @@ -79,7 +79,7 @@ object NameTransformer { buf = new StringBuilder() buf.append(name.substring(0, i)) } - buf.append(op2code(c.toInt)) + buf.append(op2code(c.toInt).nn) /* Handle glyphs that are not valid Java/JVM identifiers */ } else if (!Character.isJavaIdentifierPart(c)) { @@ -106,11 +106,11 @@ object NameTransformer { //System.out.println("decode: " + name);//DEBUG val name = if (name0.endsWith("")) name0.stripSuffix("") + "this" else name0 - var buf: StringBuilder = null + var buf: StringBuilder | Null = null val len = name.length() var i = 0 while (i < len) { - var ops: OpCodes = null + var ops: OpCodes | Null = null var unicode = false val c = name charAt i if (c == '$' && i + 2 < len) { diff --git a/library/src/scala/runtime/LambdaDeserializer.scala b/library/src/scala/runtime/LambdaDeserializer.scala index fa2a038f1f3f..98ba8a3da983 100644 --- a/library/src/scala/runtime/LambdaDeserializer.scala +++ b/library/src/scala/runtime/LambdaDeserializer.scala @@ -51,14 +51,14 @@ object LambdaDeserializer { } def deserializeLambdaOrNull(lookup: MethodHandles.Lookup, cache: java.util.Map[String, MethodHandle], - targetMethodMap: java.util.Map[String, MethodHandle], serialized: SerializedLambda): AnyRef = { + targetMethodMap: java.util.Map[String, MethodHandle], serialized: SerializedLambda): AnyRef | Null = { assert(targetMethodMap != null) def slashDot(name: String) = name.replaceAll("/", ".") val loader = lookup.lookupClass().getClassLoader val implClass = loader.loadClass(slashDot(serialized.getImplClass)) val key = LambdaDeserialize.nameAndDescriptorKey(serialized.getImplMethodName, serialized.getImplMethodSignature) - def makeCallSite: CallSite = { + def makeCallSite: CallSite | Null = { import serialized._ def parseDescriptor(s: String) = MethodType.fromMethodDescriptorString(s, loader) diff --git a/library/src/scala/runtime/MethodCache.scala b/library/src/scala/runtime/MethodCache.scala index 9cd59a1cf4d3..d50daa770cd0 100644 --- a/library/src/scala/runtime/MethodCache.scala +++ b/library/src/scala/runtime/MethodCache.scala @@ -32,13 +32,13 @@ private[scala] sealed abstract class MethodCache { * `null` is returned. If `null` is returned, find's caller should look- * up the right method using whichever means it prefers, and add it to * the cache for later use. */ - def find(forReceiver: JClass[_]): JMethod + def find(forReceiver: JClass[_]): JMethod | Null def add(forReceiver: JClass[_], forMethod: JMethod): MethodCache } private[scala] final class EmptyMethodCache extends MethodCache { - def find(forReceiver: JClass[_]): JMethod = null + def find(forReceiver: JClass[_]): JMethod | Null = null def add(forReceiver: JClass[_], forMethod: JMethod): MethodCache = new PolyMethodCache(this, forReceiver, forMethod, 1) @@ -50,7 +50,7 @@ private[scala] final class MegaMethodCache( private[this] val forParameterTypes: Array[JClass[_]] ) extends MethodCache { - def find(forReceiver: JClass[_]): JMethod = + def find(forReceiver: JClass[_]): JMethod | Null = forReceiver.getMethod(forName, forParameterTypes:_*) def add(forReceiver: JClass[_], forMethod: JMethod): MethodCache = this @@ -67,14 +67,14 @@ private[scala] final class PolyMethodCache( /** To achieve tail recursion this must be a separate method * from `find`, because the type of next is not `PolyMethodCache`. */ - @tailrec private def findInternal(forReceiver: JClass[_]): JMethod = + @tailrec private def findInternal(forReceiver: JClass[_]): JMethod | Null = if (forReceiver eq receiver) method else next match { case x: PolyMethodCache => x findInternal forReceiver case _ => next find forReceiver } - def find(forReceiver: JClass[_]): JMethod = findInternal(forReceiver) + def find(forReceiver: JClass[_]): JMethod | Null = findInternal(forReceiver) // TODO: come up with a more realistic number final private val MaxComplexity = 160 diff --git a/library/src/scala/runtime/ScalaRunTime.scala b/library/src/scala/runtime/ScalaRunTime.scala index d63056b372c0..e288fa7ffc5e 100644 --- a/library/src/scala/runtime/ScalaRunTime.scala +++ b/library/src/scala/runtime/ScalaRunTime.scala @@ -281,13 +281,21 @@ object ScalaRunTime { case s => s + "\n" } + // For backward compatibility with code compiled without -Yexplicit-nulls. + // If `a` is null, return null; otherwise, return `f`. + private[scala] inline def mapNull[A, B](a: A, inline f: B): B = + if ((a: A | Null) == null) null.asInstanceOf[B] else f + + // Use `null` in places where we want to make sure the reference is cleared. + private[scala] inline def nullForGC[T]: T = null.asInstanceOf[T] + // Convert arrays to immutable.ArraySeq for use with Scala varargs. // By construction, calls to these methods always receive a fresh (and non-null), non-empty array. // In cases where an empty array would appear, the compiler uses a direct reference to Nil instead. // Synthetic Java varargs forwarders (@annotation.varargs or varargs bridges when overriding) may pass // `null` to these methods; but returning `null` or `ArraySeq(null)` makes little difference in practice. def genericWrapArray[T](xs: Array[T]): ArraySeq[T] = ArraySeq.unsafeWrapArray(xs) - def wrapRefArray[T <: AnyRef](xs: Array[T]): ArraySeq[T] = new ArraySeq.ofRef[T](xs) + def wrapRefArray[T <: AnyRef | Null](xs: Array[T]): ArraySeq[T] = new ArraySeq.ofRef[T](xs) def wrapIntArray(xs: Array[Int]): ArraySeq[Int] = new ArraySeq.ofInt(xs) def wrapDoubleArray(xs: Array[Double]): ArraySeq[Double] = new ArraySeq.ofDouble(xs) def wrapLongArray(xs: Array[Long]): ArraySeq[Long] = new ArraySeq.ofLong(xs) diff --git a/library/src/scala/runtime/StructuralCallSite.scala b/library/src/scala/runtime/StructuralCallSite.scala index 29a0febec3f0..26e51c518cd4 100644 --- a/library/src/scala/runtime/StructuralCallSite.scala +++ b/library/src/scala/runtime/StructuralCallSite.scala @@ -31,7 +31,7 @@ final class StructuralCallSite private (callType: MethodType) { cache } - def find(receiver: Class[_]): Method = get.find(receiver) + def find(receiver: Class[_]): Method | Null = get.find(receiver) def add(receiver: Class[_], m: Method): Method = { cache = new SoftReference(get.add(receiver, m)) diff --git a/library/src/scala/sys/Prop.scala b/library/src/scala/sys/Prop.scala index 0a7c8b678299..6ed7db6f84b8 100644 --- a/library/src/scala/sys/Prop.scala +++ b/library/src/scala/sys/Prop.scala @@ -44,7 +44,7 @@ trait Prop[+T] { * @param newValue the new string value * @return the old value, or null if it was unset. */ - def set(newValue: String): String + def set(newValue: String): String | Null /** Sets the property with a value of the represented type. */ diff --git a/library/src/scala/sys/PropImpl.scala b/library/src/scala/sys/PropImpl.scala index 37f0bac5c02a..455fb65f9a18 100644 --- a/library/src/scala/sys/PropImpl.scala +++ b/library/src/scala/sys/PropImpl.scala @@ -21,19 +21,19 @@ import scala.collection.mutable private[sys] class PropImpl[+T](val key: String, valueFn: String => T) extends Prop[T] { def value: T = if (isSet) valueFn(get) else zero def isSet = underlying contains key - def set(newValue: String): String = { - val old = if (isSet) get else null + def set(newValue: String): String | Null = { + val old: String | Null = if (isSet) get else null underlying(key) = newValue old } def setValue[T1 >: T](newValue: T1): T = { val old = value - if (newValue == null) set(null) + if (newValue == null) set(null.asInstanceOf[String]) // will cause NPE in java.lang.System.setProperty else set("" + newValue) old } def get: String = - if (isSet) underlying.getOrElse(key, "") + if (isSet) underlying.getOrElse(key, "").nn else "" def clear(): Unit = underlying -= key @@ -41,7 +41,7 @@ private[sys] class PropImpl[+T](val key: String, valueFn: String => T) extends P def or[T1 >: T](alt: => T1): T1 = if (isSet) value else alt /** The underlying property map, in our case always sys.props */ - protected def underlying: mutable.Map[String, String] = scala.sys.props + protected def underlying: mutable.Map[String, String | Null] = scala.sys.props protected def zero: T = null.asInstanceOf[T] private def getString = if (isSet) "currently: " + get else "unset" override def toString = "%s (%s)".format(key, getString) @@ -50,4 +50,3 @@ private[sys] class PropImpl[+T](val key: String, valueFn: String => T) extends P private[sys] abstract class CreatorImpl[+T](f: String => T) extends Prop.Creator[T] { def apply(key: String): Prop[T] = new PropImpl[T](key, f) } - diff --git a/library/src/scala/sys/SystemProperties.scala b/library/src/scala/sys/SystemProperties.scala index d1a5326e66f7..31d7ea3edc55 100644 --- a/library/src/scala/sys/SystemProperties.scala +++ b/library/src/scala/sys/SystemProperties.scala @@ -29,10 +29,10 @@ import scala.language.implicitConversions * @define coll mutable map */ class SystemProperties -extends mutable.AbstractMap[String, String] { +extends mutable.AbstractMap[String, String | Null] { - override def empty: mutable.Map[String, String] = mutable.Map[String, String]() - override def default(key: String): String = null + override def empty: mutable.Map[String, String | Null] = mutable.Map[String, String | Null]() + override def default(key: String): String | Null = null def iterator: Iterator[(String, String)] = wrapAccess { val ps = System.getProperties() @@ -51,7 +51,7 @@ extends mutable.AbstractMap[String, String] { override def clear(): Unit = wrapAccess(System.getProperties().clear()) def subtractOne (key: String): this.type = { wrapAccess(System.clearProperty(key)) ; this } - def addOne (kv: (String, String)): this.type = { wrapAccess(System.setProperty(kv._1, kv._2)) ; this } + def addOne (kv: (String, String | Null)): this.type = { wrapAccess(System.setProperty(kv._1, kv._2)) ; this } @annotation.nowarn("cat=deprecation") // AccessControlException is deprecated on JDK 17 def wrapAccess[T](body: => T): Option[T] = @@ -90,4 +90,3 @@ object SystemProperties { lazy val preferIPv6Addresses: BooleanProp = BooleanProp.keyExists(PreferIPv6AddressesKey) lazy val noTraceSuppression: BooleanProp = BooleanProp.valueIsTrue(NoTraceSuppressionKey) } - diff --git a/library/src/scala/sys/process/BasicIO.scala b/library/src/scala/sys/process/BasicIO.scala index 57710609451d..3a2b99b58c5c 100644 --- a/library/src/scala/sys/process/BasicIO.scala +++ b/library/src/scala/sys/process/BasicIO.scala @@ -37,7 +37,7 @@ object BasicIO { final val BufferSize = 8192 /** Used to separate lines in the `processFully` function that takes `Appendable`. */ - final val Newline = System.lineSeparator + final val Newline: String = System.lineSeparator private[process] final class LazilyListed[T]( val process: T => Unit, diff --git a/library/src/scala/sys/process/ProcessBuilderImpl.scala b/library/src/scala/sys/process/ProcessBuilderImpl.scala index 41b284dbd0bb..7dd74c67bbec 100644 --- a/library/src/scala/sys/process/ProcessBuilderImpl.scala +++ b/library/src/scala/sys/process/ProcessBuilderImpl.scala @@ -86,7 +86,7 @@ private[process] trait ProcessBuilderImpl { val process = p.start() // start the external process // spawn threads that process the input, output, and error streams using the functions defined in `io` - val inThread = + val inThread: Thread | Null = if (inherit || (writeInput eq BasicIO.connectNoOp)) null else Spawn("Simple-input", daemon = true)(writeInput(process.getOutputStream)) val outThread = Spawn("Simple-output", daemonizeThreads)(processOutput(process.getInputStream())) diff --git a/library/src/scala/sys/process/ProcessImpl.scala b/library/src/scala/sys/process/ProcessImpl.scala index 7e5a742d9b5b..c120b6607a69 100644 --- a/library/src/scala/sys/process/ProcessImpl.scala +++ b/library/src/scala/sys/process/ProcessImpl.scala @@ -261,7 +261,7 @@ private[process] trait ProcessImpl { * The implementation of `exitValue` interrupts `inputThread` * and then waits until all I/O threads die before returning. */ - private[process] class SimpleProcess(p: JProcess, inputThread: Thread, outputThreads: List[Thread]) extends Process { + private[process] class SimpleProcess(p: JProcess, inputThread: Thread | Null, outputThreads: List[Thread]) extends Process { override def isAlive() = p.isAlive() override def exitValue() = { try p.waitFor() // wait for the process to terminate diff --git a/library/src/scala/sys/process/package.scala b/library/src/scala/sys/process/package.scala index bf0e534e2284..9314bc176813 100644 --- a/library/src/scala/sys/process/package.scala +++ b/library/src/scala/sys/process/package.scala @@ -211,11 +211,11 @@ import scala.language.`2.13` @annotation.nowarn("msg=package object inheritance") object `package` extends ProcessImplicits { /** The input stream of this process */ - def stdin = java.lang.System.in + def stdin: java.io.InputStream = java.lang.System.in /** The output stream of this process */ - def stdout = java.lang.System.out + def stdout: java.io.PrintStream = java.lang.System.out /** The error stream of this process */ - def stderr = java.lang.System.err + def stderr: java.io.PrintStream = java.lang.System.err } // private val shell: String => Array[String] = // if (isWin) Array("cmd.exe", "/C", _) diff --git a/library/src/scala/util/Properties.scala b/library/src/scala/util/Properties.scala index 364a66caa1e8..2bc065d5cddf 100644 --- a/library/src/scala/util/Properties.scala +++ b/library/src/scala/util/Properties.scala @@ -52,18 +52,18 @@ private[scala] trait PropertiesTrait { catch { case _: IOException => } } - def propIsSet(name: String) = System.getProperty(name) != null - def propIsSetTo(name: String, value: String) = propOrNull(name) == value - def propOrElse(name: String, alt: => String) = Option(System.getProperty(name)).getOrElse(alt) - def propOrEmpty(name: String) = propOrElse(name, "") - def propOrNull(name: String) = propOrElse(name, null) - def propOrNone(name: String) = Option(propOrNull(name)) - def propOrFalse(name: String) = propOrNone(name) exists (x => List("yes", "on", "true") contains x.toLowerCase) - def setProp(name: String, value: String) = System.setProperty(name, value) - def clearProp(name: String) = System.clearProperty(name) - - def envOrElse(name: String, alt: => String) = Option(System getenv name) getOrElse alt - def envOrNone(name: String) = Option(System getenv name) + def propIsSet(name: String): Boolean = System.getProperty(name) != null + def propIsSetTo(name: String, value: String) = propOrNull(name) == value + def propOrNone(name: String): Option[String] = Option[String](System.getProperty(name)) + def propOrElse(name: String, alt: => String): String = propOrNone(name).getOrElse(alt) + def propOrEmpty(name: String): String = propOrElse(name, "") + def propOrNull(name: String): String | Null = propOrNone(name).orNull + def propOrFalse(name: String): Boolean = propOrNone(name) exists (x => List("yes", "on", "true") contains x.toLowerCase) + def setProp(name: String, value: String): String = System.setProperty(name, value) + def clearProp(name: String): String = System.clearProperty(name) + + def envOrElse(name: String, alt: => String): String = Option(System getenv name) getOrElse alt + def envOrNone(name: String): Option[String] = Option(System getenv name) def envOrSome(name: String, alt: => Option[String]) = envOrNone(name) orElse alt @@ -108,7 +108,7 @@ private[scala] trait PropertiesTrait { /** The default end of line character. */ - def lineSeparator = System.lineSeparator() + def lineSeparator: String = System.lineSeparator() /* Various well-known properties. */ def javaClassPath = propOrEmpty("java.class.path") diff --git a/library/src/scala/util/Sorting.scala b/library/src/scala/util/Sorting.scala index b1083664b336..3c8da267ccad 100644 --- a/library/src/scala/util/Sorting.scala +++ b/library/src/scala/util/Sorting.scala @@ -178,7 +178,7 @@ object Sorting { } // Caller is required to pass iN >= i0, else math will fail. Also, i0 >= 0. - private def mergeSort[@specialized T: ClassTag](a: Array[T], i0: Int, iN: Int, ord: Ordering[T], scratch: Array[T] = null): Unit = { + private def mergeSort[@specialized T: ClassTag](a: Array[T], i0: Int, iN: Int, ord: Ordering[T], scratch: Array[T] | Null = null): Unit = { if (iN - i0 < mergeThreshold) insertionSort(a, i0, iN, ord) else { val iK = (i0 + iN) >>> 1 // Bit shift equivalent to unsigned math, no overflow @@ -235,7 +235,7 @@ object Sorting { // TODO: add upper bound: T <: AnyRef, propagate to callers below (not binary compatible) // Maybe also rename all these methods to `sort`. @inline private def sort[T](a: Array[T], from: Int, until: Int, ord: Ordering[T]): Unit = (a: @unchecked) match { - case _: Array[AnyRef] => + case a: Array[AnyRef] => // Note that runtime matches are covariant, so could actually be any Array[T] s.t. T is not primitive (even boxed value classes) if (a.length > 1 && (ord eq null)) throw new NullPointerException("Ordering") java.util.Arrays.sort(a, from, until, ord) diff --git a/library/src/scala/util/Using.scala b/library/src/scala/util/Using.scala index 90ec6dc71d40..d0c6bdbde4fa 100644 --- a/library/src/scala/util/Using.scala +++ b/library/src/scala/util/Using.scala @@ -14,6 +14,7 @@ package scala.util import scala.language.`2.13` import scala.util.control.{ControlThrowable, NonFatal} +import scala.runtime.ScalaRunTime.nullForGC /** A utility for performing automatic resource management. It can be used to perform an * operation using resources, after which it releases the resources in reverse order @@ -198,7 +199,7 @@ object Using { } private def manage[A](op: Manager => A): A = { - var toThrow: Throwable = null + var toThrow: Throwable | Null = null try { op(this) } catch { @@ -208,8 +209,8 @@ object Using { } finally { closed = true var rs = resources - resources = null // allow GC, in case something is holding a reference to `this` - while (rs.nonEmpty) { + resources = nullForGC[List[Resource[_]]] // allow GC, in case something is holding a reference to `this` + while (rs != null && rs.nonEmpty) { val resource = rs.head rs = rs.tail try resource.release() @@ -292,7 +293,7 @@ object Using { def resource[R, A](resource: R)(body: R => A)(implicit releasable: Releasable[R]): A = { if (resource == null) throw new NullPointerException("null resource") - var toThrow: Throwable = null + var toThrow: Throwable | Null = null try { body(resource) } catch { diff --git a/library/src/scala/util/control/ControlThrowable.scala b/library/src/scala/util/control/ControlThrowable.scala index b3a3bf1006e2..dee0b5dae168 100644 --- a/library/src/scala/util/control/ControlThrowable.scala +++ b/library/src/scala/util/control/ControlThrowable.scala @@ -42,7 +42,7 @@ import scala.language.`2.13` * Instances of `ControlThrowable` should not normally have a cause. * Legacy subclasses may set a cause using `initCause`. */ -abstract class ControlThrowable(message: String) extends Throwable( +abstract class ControlThrowable(message: String | Null) extends Throwable( message, /*cause*/ null, /*enableSuppression=*/ false, /*writableStackTrace*/ false) { def this() = this(message = null) diff --git a/library/src/scala/util/hashing/MurmurHash3.scala b/library/src/scala/util/hashing/MurmurHash3.scala index e828ff771819..a65a0e2c2771 100644 --- a/library/src/scala/util/hashing/MurmurHash3.scala +++ b/library/src/scala/util/hashing/MurmurHash3.scala @@ -83,7 +83,7 @@ private[hashing] class MurmurHash3 { } /** See the [[MurmurHash3.caseClassHash(x:Product,caseClassName:String)]] overload */ - final def caseClassHash(x: Product, seed: Int, caseClassName: String): Int = { + final def caseClassHash(x: Product, seed: Int, caseClassName: String | Null): Int = { val arr = x.productArity val aye = (if (caseClassName != null) caseClassName else x.productPrefix).hashCode if (arr == 0) aye @@ -395,7 +395,7 @@ object MurmurHash3 extends MurmurHash3 { * val res2: Int = -668012062 * }}} */ - def caseClassHash(x: Product, caseClassName: String = null): Int = caseClassHash(x, productSeed, caseClassName) + def caseClassHash(x: Product, caseClassName: String | Null = null): Int = caseClassHash(x, productSeed, caseClassName) private[scala] def arraySeqHash[@specialized T](a: Array[T]): Int = arrayHash(a, seqSeed) private[scala] def tuple2Hash(x: Any, y: Any): Int = tuple2Hash(x.##, y.##, productSeed) diff --git a/library/src/scala/util/matching/Regex.scala b/library/src/scala/util/matching/Regex.scala index 25cb2f86dea3..826d2ad9b604 100644 --- a/library/src/scala/util/matching/Regex.scala +++ b/library/src/scala/util/matching/Regex.scala @@ -284,7 +284,7 @@ class Regex private[matching](val pattern: Pattern, groupNames: String*) extends * @param s The string to match * @return The matches */ - def unapplySeq(s: CharSequence): Option[List[String]] = { + def unapplySeq(s: CharSequence): Option[List[String | Null]] = { val m = pattern.matcher(s) if (runMatcher(m)) Some(List.tabulate(m.groupCount) { i => m.group(i + 1) }) else None @@ -338,10 +338,10 @@ class Regex private[matching](val pattern: Pattern, groupNames: String*) extends * Otherwise, this Regex is applied to the previously matched input, * and the result of that match is used. */ - def unapplySeq(m: Match): Option[List[String]] = + def unapplySeq(m: Match): Option[List[String | Null]] = if (m.matched == null) None else if (m.matcher.pattern == this.pattern) Regex.extractGroupsFromMatch(m) - else unapplySeq(m.matched) + else unapplySeq(m.matched.nn) // @see UnanchoredRegex protected def runMatcher(m: Matcher): Boolean = m.matches() @@ -650,45 +650,45 @@ object Regex { def end(i: Int): Int /** The matched string, or `null` if nothing was matched. */ - def matched: String = + def matched: String | Null = if (start >= 0) source.subSequence(start, end).toString else null /** The matched string in group `i`, * or `null` if nothing was matched. */ - def group(i: Int): String = + def group(i: Int): String | Null = if (start(i) >= 0) source.subSequence(start(i), end(i)).toString else null /** All capturing groups, i.e., not including group(0). */ - def subgroups: List[String] = (1 to groupCount).toList map group + def subgroups: List[String | Null] = (1 to groupCount).toList map group /** The char sequence before first character of match, * or `null` if nothing was matched. */ - def before: CharSequence = + def before: CharSequence | Null = if (start >= 0) source.subSequence(0, start) else null /** The char sequence before first character of match in group `i`, * or `null` if nothing was matched for that group. */ - def before(i: Int): CharSequence = + def before(i: Int): CharSequence | Null = if (start(i) >= 0) source.subSequence(0, start(i)) else null /** Returns char sequence after last character of match, * or `null` if nothing was matched. */ - def after: CharSequence = + def after: CharSequence | Null = if (end >= 0) source.subSequence(end, source.length) else null /** The char sequence after last character of match in group `i`, * or `null` if nothing was matched for that group. */ - def after(i: Int): CharSequence = + def after(i: Int): CharSequence | Null = if (end(i) >= 0) source.subSequence(end(i), source.length) else null @@ -708,7 +708,7 @@ object Regex { * @return The requested group * @throws IllegalArgumentException if the requested group name is not defined */ - def group(id: String): String = ( + def group(id: String): String | Null = ( if (groupNamesNowarn.isEmpty) matcher group id else @@ -719,7 +719,7 @@ object Regex { ) /** The matched string; equivalent to `matched.toString`. */ - override def toString: String = matched + override def toString: String = matched.nn } /** Provides information about a successful match. */ @@ -768,7 +768,7 @@ object Regex { * */ object Match { - def unapply(m: Match): Some[String] = Some(m.matched) + def unapply(m: Match): Some[String] = Some(m.matched.nn) } /** An extractor object that yields the groups in the match. Using this extractor @@ -783,12 +783,12 @@ object Regex { * }}} */ object Groups { - def unapplySeq(m: Match): Option[Seq[String]] = { + def unapplySeq(m: Match): Option[Seq[String | Null]] = { if (m.groupCount > 0) extractGroupsFromMatch(m) else None } } - @inline private def extractGroupsFromMatch(m: Match): Option[List[String]] = + @inline private def extractGroupsFromMatch(m: Match): Option[List[String | Null]] = Some(List.tabulate(m.groupCount) { i => m.group(i + 1) }) /** A class to step through a sequence of regex matches. @@ -895,7 +895,7 @@ object Regex { } // Appends the input prefix and the replacement text. - def replace(replacement: String) = matcher.appendReplacement(sb, replacement) + def replace(replacement: String): Matcher = matcher.appendReplacement(sb, replacement) } /** Quotes strings to be used literally in regex patterns. diff --git a/project/Build.scala b/project/Build.scala index 1222613de823..c81821cf80d0 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -686,6 +686,9 @@ object Build { // Note: bench/profiles/projects.yml should be updated accordingly. Compile / scalacOptions ++= Seq("-Yexplicit-nulls", "-Wsafe-init"), + // TODO: Enable fatal warnings after 3.8 because old stdlib has different nullability. + Compile / scalacOptions -= "-Werror", + // Use source 3.3 to avoid fatal migration warnings on scalajs-ir scalacOptions ++= Seq("-source", "3.3"), @@ -1796,6 +1799,7 @@ object Build { // NOTE: The only difference here is that we drop `-Werror` and semanticDB for now Compile / scalacOptions := Seq("-deprecation", "-feature", "-unchecked", "-encoding", "UTF8", "-language:implicitConversions"), Compile / scalacOptions += "-Yno-stdlib-patches", + Compile / scalacOptions += "-Yexplicit-nulls", Compile / scalacOptions ++= Seq( // Needed so that the library sources are visible when `dotty.tools.dotc.core.Definitions#init` is called "-sourcepath", (Compile / sourceDirectories).value.map(_.getCanonicalPath).distinct.mkString(File.pathSeparator), @@ -1890,6 +1894,7 @@ object Build { // NOTE: The only difference here is that we drop `-Werror` and semanticDB for now Compile / scalacOptions := Seq("-deprecation", "-feature", "-unchecked", "-encoding", "UTF8", "-language:implicitConversions"), Compile / scalacOptions += "-Yno-stdlib-patches", + Compile / scalacOptions += "-Yexplicit-nulls", Compile / scalacOptions ++= Seq( // Needed so that the library sources are visible when `dotty.tools.dotc.core.Definitions#init` is called "-sourcepath", (Compile / sourceDirectories).value.map(_.getCanonicalPath).distinct.mkString(File.pathSeparator), @@ -2026,6 +2031,7 @@ object Build { // NOTE: The only difference here is that we drop `-Werror` and semanticDB for now Compile / scalacOptions := Seq("-deprecation", "-feature", "-unchecked", "-encoding", "UTF8", "-language:implicitConversions", "-nowarn"), Compile / scalacOptions += "-Yno-stdlib-patches", + Compile / scalacOptions += "-Yexplicit-nulls", Compile / scalacOptions += "-scalajs", // Packaging configuration of the stdlib Compile / packageBin / publishArtifact := true, @@ -2335,7 +2341,7 @@ object Build { // NOTE: The only difference here is that we drop `-Werror` and semanticDB for now Compile / scalacOptions := Seq("-deprecation", "-feature", "-unchecked", "-encoding", "UTF8", "-language:implicitConversions"), // TODO: Enable these flags when the new stdlib is explicitelly null checked - //Compile / scalacOptions ++= Seq("-Yexplicit-nulls", "-Wsafe-init"), + Compile / scalacOptions ++= Seq("-Yexplicit-nulls", "-Wsafe-init"), // Make sure that the produced artifacts have the minimum JVM version in the bytecode Compile / javacOptions ++= Seq("--release", Versions.minimumJVMVersion), Compile / scalacOptions ++= Seq("--java-output-version", Versions.minimumJVMVersion), @@ -2509,7 +2515,7 @@ object Build { // NOTE: The only difference here is that we drop `-Werror` and semanticDB for now Compile / scalacOptions := Seq("-deprecation", "-feature", "-unchecked", "-encoding", "UTF8", "-language:implicitConversions"), // TODO: Enable these flags when the new stdlib is explicitelly null checked - //Compile / scalacOptions ++= Seq("-Yexplicit-nulls", "-Wsafe-init"), + Compile / scalacOptions ++= Seq("-Yexplicit-nulls", "-Wsafe-init"), // Make sure that the produced artifacts have the minimum JVM version in the bytecode Compile / javacOptions ++= Seq("--release", Versions.minimumJVMVersion), Compile / scalacOptions ++= Seq("--java-output-version", Versions.minimumJVMVersion), @@ -2688,7 +2694,7 @@ object Build { Compile / scalacOptions := Seq("-deprecation", "-feature", "-unchecked", "-encoding", "UTF8", "-language:implicitConversions"), Compile / scalacOptions += "-experimental", // TODO: Enable these flags when the new stdlib is explicitelly null checked - //Compile / scalacOptions ++= Seq("-Yexplicit-nulls", "-Wsafe-init"), + Compile / scalacOptions ++= Seq("-Yexplicit-nulls", "-Wsafe-init"), // Make sure that the produced artifacts have the minimum JVM version in the bytecode Compile / javacOptions ++= Seq("--release", Versions.minimumJVMVersion), Compile / scalacOptions ++= Seq("--java-output-version", Versions.minimumJVMVersion), diff --git a/sbt-bridge/src/xsbt/ConsoleInterface.java b/sbt-bridge/src/xsbt/ConsoleInterface.java index 49194fdf53d2..3ba4e011c8e3 100644 --- a/sbt-bridge/src/xsbt/ConsoleInterface.java +++ b/sbt-bridge/src/xsbt/ConsoleInterface.java @@ -5,7 +5,7 @@ import java.util.ArrayList; -import scala.Some; +import scala.Option; import xsbti.Logger; @@ -40,7 +40,7 @@ public void run( completeArgsList.add(classpathString); String[] completeArgs = completeArgsList.toArray(args); - ReplDriver driver = new ReplDriver(completeArgs, System.out, Some.apply(loader), ReplDriver.pprintImport()); + ReplDriver driver = new ReplDriver(completeArgs, System.out, Option.apply(loader), ReplDriver.pprintImport()); State state = driver.initialState(); assert bindNames.length == bindValues.length; diff --git a/scaladoc/src/dotty/tools/scaladoc/PathBased.scala b/scaladoc/src/dotty/tools/scaladoc/PathBased.scala index 5e24623b43d5..a3ff29ba5269 100644 --- a/scaladoc/src/dotty/tools/scaladoc/PathBased.scala +++ b/scaladoc/src/dotty/tools/scaladoc/PathBased.scala @@ -24,7 +24,7 @@ object PathBased: def parse[T](args: Seq[String], projectRoot: Path = Paths.get("").toAbsolutePath())(using parser: ArgParser[T]): ParsingResult[T] = { val parsed = args.map { - case PathExtractor(path, arg) => parser.parse(arg).map(elem => Entry(Some(Paths.get(path)), elem)) + case PathExtractor(path: String, arg: String) => parser.parse(arg).map(elem => Entry(Some(Paths.get(path)), elem)) case arg => parser.parse(arg).map(elem => Entry(None, elem)) } val errors = parsed.collect { diff --git a/scaladoc/src/dotty/tools/scaladoc/Scaladoc.scala b/scaladoc/src/dotty/tools/scaladoc/Scaladoc.scala index 50e7589d92fe..abf073aa53aa 100644 --- a/scaladoc/src/dotty/tools/scaladoc/Scaladoc.scala +++ b/scaladoc/src/dotty/tools/scaladoc/Scaladoc.scala @@ -19,7 +19,7 @@ object Scaladoc: tastyFiles: Seq[File] = Nil, classpath: String = "", bootclasspath: String = "", - output: File, + output: File | Null, docsRoot: Option[String] = None, projectVersion: Option[String] = None, projectLogo: Option[String] = None, @@ -66,13 +66,13 @@ object Scaladoc: if !ctx.reporter.hasErrors then val updatedArgs = parsedArgs.copy(tastyDirs = parsedArgs.tastyDirs, tastyFiles = tastyFiles) - if (parsedArgs.output.exists()) util.IO.delete(parsedArgs.output) + if (parsedArgs.output.nn.exists()) util.IO.delete(parsedArgs.output) run(updatedArgs) report.inform("Done") else report.error("Failure") - if parsedArgs.generateInkuire then dumpInkuireDB(parsedArgs.output.getAbsolutePath, parsedArgs) + if parsedArgs.generateInkuire then dumpInkuireDB(parsedArgs.output.nn.getAbsolutePath, parsedArgs) } ctx.reporter diff --git a/scaladoc/src/dotty/tools/scaladoc/SourceLinks.scala b/scaladoc/src/dotty/tools/scaladoc/SourceLinks.scala index b3732bcbc946..00344aa5ad75 100644 --- a/scaladoc/src/dotty/tools/scaladoc/SourceLinks.scala +++ b/scaladoc/src/dotty/tools/scaladoc/SourceLinks.scala @@ -73,7 +73,7 @@ class SourceLinkParser(revision: Option[String]) extends ArgParser[SourceLink]: if unsupported.nonEmpty then Left(s"Unsupported patterns from scaladoc format are used: ${unsupported.mkString(" ")}") else Right(TemplateSourceLink(supported.foldLeft(string)((template, pattern) => template.replace(pattern, SupportedScalaDocPatternReplacements(pattern))))) - case KnownProvider(name, organization, repo, rawRevision, rawSubPath) => + case KnownProvider(name: String, organization: String, repo: String, rawRevision, rawSubPath) => val subPath = Option(rawSubPath).fold("")("/" + _.drop(1)) val pathRev = Option(rawRevision).map(_.drop(1)).orElse(revision) diff --git a/scaladoc/src/dotty/tools/scaladoc/compat.scala b/scaladoc/src/dotty/tools/scaladoc/compat.scala index d2095b9cc98c..50c2686d8dd6 100644 --- a/scaladoc/src/dotty/tools/scaladoc/compat.scala +++ b/scaladoc/src/dotty/tools/scaladoc/compat.scala @@ -17,10 +17,10 @@ type JMap[K, V] = java.util.Map[K, V] type JHashMap[K, V] = java.util.HashMap[K, V] type JMapEntry[K, V] = java.util.Map.Entry[K, V] -private val emptyListInst = Collections.emptyList +private val emptyListInst: JList[AnyRef] = Collections.emptyList def JNil[A] = emptyListInst.asInstanceOf[JList[A]] -private val emptyMapInst = Collections.emptyMap +private val emptyMapInst: JMap[AnyRef, AnyRef] = Collections.emptyMap def emptyJMap[A, B] = emptyMapInst.asInstanceOf[JMap[A, B]] enum DocLink: diff --git a/scaladoc/src/dotty/tools/scaladoc/parsers/WikiCodeBlockParser.scala b/scaladoc/src/dotty/tools/scaladoc/parsers/WikiCodeBlockParser.scala index e6ebe0d2cc7a..2f47046bf81a 100644 --- a/scaladoc/src/dotty/tools/scaladoc/parsers/WikiCodeBlockParser.scala +++ b/scaladoc/src/dotty/tools/scaladoc/parsers/WikiCodeBlockParser.scala @@ -55,10 +55,9 @@ object WikiCodeBlockParser { def tryStart(state: ParserState, matchedBlockParser: MatchedBlockParser): BlockStart = { val nextNonSpace = state.getNextNonSpaceIndex val line = state.getLine - var matcher: Matcher = null if state.getIndent < 4 then { val trySequence = line.subSequence(nextNonSpace, line.length) - matcher = OPENING_FENCE.matcher(trySequence) + val matcher = OPENING_FENCE.matcher(trySequence) if matcher.find then { val fenceLength = matcher.group(0).length val blockParser = @@ -93,14 +92,13 @@ class WikiCodeBlockParser( val nextNonSpace = state.getNextNonSpaceIndex var newIndex = state.getIndex val line = state.getLine - var matcher: Matcher = null val matches = state.getIndent <= 3 && nextNonSpace < line.length if matches then { val trySequence = line.subSequence(nextNonSpace, line.length) - matcher = WikiCodeBlockParser.CLOSING_FENCE.matcher(trySequence) + val matcher = WikiCodeBlockParser.CLOSING_FENCE.matcher(trySequence) if matcher.find then { val foundFenceLength = matcher.group(0).length if (foundFenceLength >= fenceLength) { // closing fence - we're at end of line, so we can finalize now @@ -150,7 +148,7 @@ class WikiCodeBlockParser( } else block.setContent(content) block.setCharsFromContent - content = null + content = null.asInstanceOf[BlockContent] // release for GC } } diff --git a/scaladoc/src/dotty/tools/scaladoc/renderers/SiteRenderer.scala b/scaladoc/src/dotty/tools/scaladoc/renderers/SiteRenderer.scala index 71b0a1b572ac..f15e13e4a443 100644 --- a/scaladoc/src/dotty/tools/scaladoc/renderers/SiteRenderer.scala +++ b/scaladoc/src/dotty/tools/scaladoc/renderers/SiteRenderer.scala @@ -33,7 +33,7 @@ trait SiteRenderer(using DocContext) extends Locations: def tryAsDriPlain(str: String): Option[String] = val (path, prefix) = str match - case HashRegex(path, prefix) => (path, prefix) + case HashRegex(path: String, prefix: String) => (path, prefix) case _ => (str, "") val res = ctx.driForLink(content.template.file, path).filter(driExists) res.headOption.map(pathToPage(pageDri, _) + prefix) @@ -46,7 +46,7 @@ trait SiteRenderer(using DocContext) extends Locations: case _ => str val (path, prefix) = newStr match - case HashRegex(path, prefix) => (path, prefix) + case HashRegex(path: String, prefix: String) => (path, prefix) case _ => (newStr, "") val res = ctx.driForLink(content.template.file, path).filter(driExists) diff --git a/scaladoc/src/dotty/tools/scaladoc/renderers/Writer.scala b/scaladoc/src/dotty/tools/scaladoc/renderers/Writer.scala index 029da21bdda7..af81b05aadc5 100644 --- a/scaladoc/src/dotty/tools/scaladoc/renderers/Writer.scala +++ b/scaladoc/src/dotty/tools/scaladoc/renderers/Writer.scala @@ -14,7 +14,7 @@ trait Writer(using ctx: DocContext) extends Locations: private val args = summon[DocContext].args private def dest(path: String) = - val absPath = args.output.toPath.resolve(path) + val absPath = args.output.nn.toPath.resolve(path) if !Files.exists(absPath.getParent) then Files.createDirectories(absPath.getParent) absPath diff --git a/scaladoc/src/dotty/tools/scaladoc/site/BlogParser.scala b/scaladoc/src/dotty/tools/scaladoc/site/BlogParser.scala index 68e709a339b2..04a9ee7d9084 100644 --- a/scaladoc/src/dotty/tools/scaladoc/site/BlogParser.scala +++ b/scaladoc/src/dotty/tools/scaladoc/site/BlogParser.scala @@ -8,8 +8,8 @@ import scala.beans.{BooleanBeanProperty, BeanProperty} import scala.util.Try case class BlogConfig( - @BeanProperty input: String, - @BeanProperty output: String, + @BeanProperty input: String | Null, + @BeanProperty output: String | Null, @BooleanBeanProperty hidden: Boolean ): def this() = this(null, null, false) diff --git a/scaladoc/src/dotty/tools/scaladoc/site/StaticSiteContext.scala b/scaladoc/src/dotty/tools/scaladoc/site/StaticSiteContext.scala index a610e41f12f0..6e2e21c089d0 100644 --- a/scaladoc/src/dotty/tools/scaladoc/site/StaticSiteContext.scala +++ b/scaladoc/src/dotty/tools/scaladoc/site/StaticSiteContext.scala @@ -19,8 +19,8 @@ class StaticSiteContext( var memberLinkResolver: String => Option[DRI] = _ => None - val docsPath = root.toPath.resolve("_docs") - val blogPath = root.toPath.resolve("_blog") + val docsPath: Path = root.toPath.resolve("_docs") + val blogPath: Path = root.toPath.resolve("_blog") def resolveNewBlogPath(stringPath: String): Path = if stringPath.nonEmpty then root.toPath.resolve(stringPath) @@ -114,7 +114,7 @@ class StaticSiteContext( DRI.forPath(relativePath) - def pathFromRoot(myTemplate: LoadedTemplate) = root.toPath.relativize(myTemplate.file.toPath) + def pathFromRoot(myTemplate: LoadedTemplate): Path = root.toPath.relativize(myTemplate.file.toPath) val projectWideProperties = Seq("projectName" -> args.name) ++ diff --git a/scaladoc/src/dotty/tools/scaladoc/site/StaticSiteLoader.scala b/scaladoc/src/dotty/tools/scaladoc/site/StaticSiteLoader.scala index e25639c36183..af0d1208c5cb 100644 --- a/scaladoc/src/dotty/tools/scaladoc/site/StaticSiteLoader.scala +++ b/scaladoc/src/dotty/tools/scaladoc/site/StaticSiteLoader.scala @@ -138,7 +138,7 @@ class StaticSiteLoader(val root: File, val args: Scaladoc.Args)(using StaticSite val indexDest = ctx.docsPath.resolve(defaultDirectory).resolve("index.html") val regex = raw"(\d*)-(\d*)-(\d*)-(.*)".r def splitDateName(tf: TemplateFile): (Date, String) = tf.file.getName match - case regex(year, month, day, name) => ((year, month, day), name) + case regex(year: String, month: String, day: String, name: String) => ((year, month, day), name) case name => report.warn("Incorrect file name for blog post. Post file name should be in format ---", tf.file) (("1900","01","01"), name) diff --git a/scaladoc/src/dotty/tools/scaladoc/snippets/SnippetCompiler.scala b/scaladoc/src/dotty/tools/scaladoc/snippets/SnippetCompiler.scala index 1648dbe2917b..514155165ea7 100644 --- a/scaladoc/src/dotty/tools/scaladoc/snippets/SnippetCompiler.scala +++ b/scaladoc/src/dotty/tools/scaladoc/snippets/SnippetCompiler.scala @@ -48,7 +48,7 @@ class SnippetCompiler( private def newRun(using ctx: Context): Run = scala3Compiler.newRun - private def nullableMessage(msgOrNull: String): String = + private def nullableMessage(msgOrNull: String | Null): String = if (msgOrNull == null) "" else msgOrNull private def createReportMessage(wrappedSnippet: WrappedSnippet, arg: SnippetCompilerArg, diagnostics: Seq[Diagnostic], sourceFile: SourceFile): Seq[SnippetCompilerMessage] = { diff --git a/scaladoc/src/dotty/tools/scaladoc/tasty/SymOps.scala b/scaladoc/src/dotty/tools/scaladoc/tasty/SymOps.scala index 969b1d6462c2..0b38af38a3b3 100644 --- a/scaladoc/src/dotty/tools/scaladoc/tasty/SymOps.scala +++ b/scaladoc/src/dotty/tools/scaladoc/tasty/SymOps.scala @@ -214,7 +214,7 @@ end SymOps class SymOpsWithLinkCache: import SymOps.* - private val externalLinkCache: scala.collection.mutable.Map[AbstractFile, Option[ExternalDocLink]] = MMap() + private val externalLinkCache: scala.collection.mutable.Map[AbstractFile | Null, Option[ExternalDocLink]] = MMap() extension (using Quotes)(sym: reflect.Symbol) diff --git a/scaladoc/src/dotty/tools/scaladoc/tasty/comments/Cleaner.scala b/scaladoc/src/dotty/tools/scaladoc/tasty/comments/Cleaner.scala index e495e90018cd..88ca3534fcee 100644 --- a/scaladoc/src/dotty/tools/scaladoc/tasty/comments/Cleaner.scala +++ b/scaladoc/src/dotty/tools/scaladoc/tasty/comments/Cleaner.scala @@ -13,7 +13,7 @@ object Cleaner { def cleanLine(line: String): String = { // Remove trailing whitespaces TrailingWhitespace.replaceAllIn(line, "") match { - case CleanCommentLine(ctl) => ctl + case CleanCommentLine(ctl: String) => ctl case tl => tl } } diff --git a/scaladoc/src/dotty/tools/scaladoc/tasty/comments/Comments.scala b/scaladoc/src/dotty/tools/scaladoc/tasty/comments/Comments.scala index 44a1c3630a5f..5b72e9eb5c42 100644 --- a/scaladoc/src/dotty/tools/scaladoc/tasty/comments/Comments.scala +++ b/scaladoc/src/dotty/tools/scaladoc/tasty/comments/Comments.scala @@ -83,7 +83,7 @@ abstract class MarkupConversion[T](val repr: Repr)(using dctx: DocContext) { lazy val snippetChecker = dctx.snippetChecker - val qctx: repr.qctx.type = if repr == null then null else repr.qctx // TODO why we do need null? + val qctx: repr.qctx.type = if repr == null then null.asInstanceOf[repr.qctx.type] else repr.qctx // TODO why we do need null? val owner: qctx.reflect.Symbol = if repr == null then null.asInstanceOf[qctx.reflect.Symbol] else repr.sym private given qctx.type = qctx diff --git a/scaladoc/src/dotty/tools/scaladoc/tasty/comments/Preparser.scala b/scaladoc/src/dotty/tools/scaladoc/tasty/comments/Preparser.scala index 9fad9e22eeb9..22873d8c424c 100644 --- a/scaladoc/src/dotty/tools/scaladoc/tasty/comments/Preparser.scala +++ b/scaladoc/src/dotty/tools/scaladoc/tasty/comments/Preparser.scala @@ -34,7 +34,7 @@ object Preparser { remaining: List[String], inCodeBlock: Boolean, )(using strippedLinesBeforeNo: Int = 0): PreparsedComment = remaining match { - case CodeBlockStartRegex(before, marker, after) :: ls if !inCodeBlock => + case CodeBlockStartRegex(before: String, marker: String, after: String) :: ls if !inCodeBlock => if (!before.trim.isEmpty && !after.trim.isEmpty && marker == "```") go(docBody, tags, lastTagKey, before :: (marker + after) :: ls, inCodeBlock = false) else if (!before.trim.isEmpty && !after.trim.isEmpty) @@ -55,7 +55,7 @@ object Preparser { go(docBody append endOfLine append (marker + after), tags, lastTagKey, ls, inCodeBlock = true) } - case CodeBlockEndRegex(before, marker, after) :: ls => + case CodeBlockEndRegex(before: String, marker: String, after: String) :: ls => if (!before.trim.isEmpty && !after.trim.isEmpty) go(docBody, tags, lastTagKey, before :: marker :: after :: ls, inCodeBlock = true) else if (!before.trim.isEmpty) @@ -75,18 +75,18 @@ object Preparser { } - case SymbolTagRegex(name, sym, body) :: ls if !inCodeBlock => + case SymbolTagRegex(name: String, sym: String, body: String) :: ls if !inCodeBlock => val key = SymbolTagKey(name, sym) val value = body :: tags.getOrElse(key, Nil) go(docBody, tags + (key -> value), Some(key), ls, inCodeBlock) - case SimpleTagRegex(name, body) :: ls if !inCodeBlock => + case SimpleTagRegex(name: String, body: String) :: ls if !inCodeBlock => val key = SimpleTagKey(name) val value = body :: tags.getOrElse(key, Nil) go(docBody, tags + (key -> value), Some(key), ls, inCodeBlock) - case SingleTagRegex(name) :: ls if !inCodeBlock => + case SingleTagRegex(name: String) :: ls if !inCodeBlock => val key = SimpleTagKey(name) val value = "" :: tags.getOrElse(key, Nil) go(docBody, tags + (key -> value), Some(key), ls, inCodeBlock) diff --git a/scaladoc/src/dotty/tools/scaladoc/tasty/comments/markdown/SnippetRenderer.scala b/scaladoc/src/dotty/tools/scaladoc/tasty/comments/markdown/SnippetRenderer.scala index 312d30a6ca19..d8cf57c2f1b9 100644 --- a/scaladoc/src/dotty/tools/scaladoc/tasty/comments/markdown/SnippetRenderer.scala +++ b/scaladoc/src/dotty/tools/scaladoc/tasty/comments/markdown/SnippetRenderer.scala @@ -44,7 +44,7 @@ object SnippetRenderer: private def wrapImportedSection(snippetLines: Seq[SnippetLine]): Seq[SnippetLine] = val mRes = cutBetweenSymbols(importedStartSymbol, importedEndSymbol, snippetLines) { case (begin, mid, end) => - val name = importedRegex.findFirstMatchIn(mid.head.content).fold("")(_.group(1)) + val name = importedRegex.findFirstMatchIn(mid.head.content).fold("")(_.group(1)).nn begin ++ mid.drop(1).dropRight(1).map(_.withClass("hideable").withClass("include").withAttribute("name", name)) ++ wrapImportedSection(end) } mRes.getOrElse(snippetLines) diff --git a/scaladoc/src/dotty/tools/scaladoc/tasty/comments/wiki/Entities.scala b/scaladoc/src/dotty/tools/scaladoc/tasty/comments/wiki/Entities.scala index 86e7298226ea..229fb2840d33 100644 --- a/scaladoc/src/dotty/tools/scaladoc/tasty/comments/wiki/Entities.scala +++ b/scaladoc/src/dotty/tools/scaladoc/tasty/comments/wiki/Entities.scala @@ -85,7 +85,7 @@ object RepresentationLink { final case class HtmlTag(data: String) extends Inline { private val Pattern = """(?ms)\A<(/?)(.*?)[\s>].*\z""".r private val (isEnd, tagName) = data match { - case Pattern(s1, s2) => + case Pattern(s1: String, s2: String) => (! s1.isEmpty, Some(s2.toLowerCase)) case _ => (false, None) diff --git a/scaladoc/src/dotty/tools/scaladoc/tasty/comments/wiki/Parser.scala b/scaladoc/src/dotty/tools/scaladoc/tasty/comments/wiki/Parser.scala index 125bca102fba..f1d628ca8929 100644 --- a/scaladoc/src/dotty/tools/scaladoc/tasty/comments/wiki/Parser.scala +++ b/scaladoc/src/dotty/tools/scaladoc/tasty/comments/wiki/Parser.scala @@ -429,7 +429,7 @@ final class Parser( val CLOSE_TAG = "^$".r private def readHTMLFrom(begin: HtmlTag): String = { val list = mutable.ListBuffer.empty[String] - val stack = mutable.ListBuffer.empty[String] + val stack = mutable.ListBuffer.empty[String | Null] begin.close match { case Some(HtmlTag(CLOSE_TAG(s))) => diff --git a/scaladoc/src/dotty/tools/scaladoc/util/escape.scala b/scaladoc/src/dotty/tools/scaladoc/util/escape.scala index 5d4bf02e8b38..129b62720fb1 100644 --- a/scaladoc/src/dotty/tools/scaladoc/util/escape.scala +++ b/scaladoc/src/dotty/tools/scaladoc/util/escape.scala @@ -1,10 +1,10 @@ package dotty.tools.scaladoc.util object Escape: - def escapeUrl(url: String) = url + def escapeUrl(url: String): String = url .replace("#","%23") - def escapeFilename(filename: String) = + def escapeFilename(filename: String): String = // from compiler/src/dotty/tools/dotc/util/NameTransformer.scala val escaped = filename .replace("~", "$tilde") diff --git a/tests/explicit-nulls/flexible-types-common/i7883.scala b/tests/explicit-nulls/flexible-types-common/i7883.scala index 9ee92553b60d..df7f7cd696ef 100644 --- a/tests/explicit-nulls/flexible-types-common/i7883.scala +++ b/tests/explicit-nulls/flexible-types-common/i7883.scala @@ -1,7 +1,7 @@ import scala.util.matching.Regex object Test extends App { - def head(s: String, r: Regex): Option[(String, String)] = + def head(s: String, r: Regex): Option[(String | Null, String | Null)] = s.trim match { case r(hd, tl) => Some((hd, tl)) // error // error // error case _ => None diff --git a/tests/explicit-nulls/flexible-unpickle/pos/Flexible_2.scala b/tests/explicit-nulls/flexible-unpickle/pos/Flexible_2.scala index 55c5dc90ac78..6ee339ab3507 100644 --- a/tests/explicit-nulls/flexible-unpickle/pos/Flexible_2.scala +++ b/tests/explicit-nulls/flexible-unpickle/pos/Flexible_2.scala @@ -1,6 +1,7 @@ import unsafeNulls.Foo.* import unsafeNulls.Unsafe_1 import unsafeNulls.{A, B, C, F, G, H, I, J, L, M, N, S, T, U, expects} +import unsafeNulls.ZIO import scala.reflect.Selectable.reflectiveSelectable import scala.quoted.* diff --git a/tests/explicit-nulls/neg/i7883.check b/tests/explicit-nulls/neg/i7883.check index f14e5d4e7481..3cedc51eb274 100644 --- a/tests/explicit-nulls/neg/i7883.check +++ b/tests/explicit-nulls/neg/i7883.check @@ -2,9 +2,9 @@ 8 | case r(hd, tl) => Some((hd, tl)) // error // error // error | ^ | None of the overloaded alternatives of method unapplySeq in class Regex with types - | (m: scala.util.matching.Regex.Match): Option[List[String]] + | (m: scala.util.matching.Regex.Match): Option[List[String | Null]] | (c: Char): Option[List[Char]] - | (s: CharSequence): Option[List[String]] + | (s: CharSequence): Option[List[String | Null]] | match arguments (String | Null) -- [E006] Not Found Error: tests/explicit-nulls/neg/i7883.scala:8:30 --------------------------------------------------- 8 | case r(hd, tl) => Some((hd, tl)) // error // error // error diff --git a/tests/neg-macros/annot-crash.check b/tests/neg-macros/annot-crash.check index 63fd77de0ec5..30a02c6f9286 100644 --- a/tests/neg-macros/annot-crash.check +++ b/tests/neg-macros/annot-crash.check @@ -3,5 +3,5 @@ |^^^^^^ |Failed to evaluate macro annotation '@crash'. | Caused by class scala.NotImplementedError: an implementation is missing - | scala.Predef$.$qmark$qmark$qmark(Predef.scala:400) + | scala.Predef$.$qmark$qmark$qmark(Predef.scala:401) | crash.transform(Macro_1.scala:7) diff --git a/tests/neg-macros/i23008.check b/tests/neg-macros/i23008.check index f69edff330a0..d661e59fb74b 100644 --- a/tests/neg-macros/i23008.check +++ b/tests/neg-macros/i23008.check @@ -3,7 +3,7 @@ | ^^^^^^^^^^^^^^^^^^ | Exception occurred while executing macro expansion. | java.lang.IllegalArgumentException: requirement failed: value of StringConstant cannot be `null` - | at scala.Predef$.require(Predef.scala:393) + | at scala.Predef$.require(Predef.scala:394) | at scala.quoted.runtime.impl.QuotesImpl$reflect$StringConstant$.apply(QuotesImpl.scala:2542) | at scala.quoted.runtime.impl.QuotesImpl$reflect$StringConstant$.apply(QuotesImpl.scala:2541) | at scala.quoted.ToExpr$StringToExpr.apply(ToExpr.scala:82) diff --git a/tests/warn/i15503f.scala b/tests/warn/i15503f.scala index 70d0b9182eb2..d4ef9709ffe6 100644 --- a/tests/warn/i15503f.scala +++ b/tests/warn/i15503f.scala @@ -28,7 +28,7 @@ object Example: import scala.quoted.* given OptionFromExpr[T](using Type[T], FromExpr[T]): FromExpr[Option[T]] with def unapply(x: Expr[Option[T]])(using Quotes) = x match - case '{ Option[T](${Expr(y)}) } => Some(Option(y)) + case '{ Option[T](${Expr(y)}: T) } => Some(Option(y)) case '{ None } => Some(None) case '{ ${Expr(opt)} : Some[T] } => Some(opt) case _ => None @@ -37,7 +37,7 @@ object ExampleWithoutWith: import scala.quoted.* given [T] => (Type[T], FromExpr[T]) => FromExpr[Option[T]]: def unapply(x: Expr[Option[T]])(using Quotes) = x match - case '{ Option[T](${Expr(y)}) } => Some(Option(y)) + case '{ Option[T](${Expr(y)}: T) } => Some(Option(y)) case '{ None } => Some(None) case '{ ${Expr(opt)} : Some[T] } => Some(opt) case _ => None