Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
2beaae9
chore: enable explicit-nulls in the stdlib
hamzaremmal Jul 18, 2025
8820357
Port stdlib to explicit nulls
noti0na1 Aug 26, 2025
6c5f4c5
Adjust some type parameters
noti0na1 Aug 29, 2025
0909858
More changes
noti0na1 Aug 30, 2025
70041ed
Migrate RBT
noti0na1 Aug 31, 2025
af6fd65
Another RBT
noti0na1 Sep 1, 2025
8fe03d9
Update TrieMap
noti0na1 Sep 1, 2025
d461b14
Fix some cc errors
noti0na1 Sep 1, 2025
8d626c3
Fix remaining cc errors
noti0na1 Sep 1, 2025
d2e09c8
Clean some unnecessary .nn
noti0na1 Sep 1, 2025
d0d0d98
Removing remaining unnecessary .nn
noti0na1 Sep 1, 2025
c56501e
Fix crush
noti0na1 Sep 1, 2025
e097b2c
Always apply asInstanceOf with arguments
noti0na1 Sep 2, 2025
b129ced
Refine .nn usages
noti0na1 Sep 2, 2025
8a74a5b
Address some review comments
noti0na1 Sep 8, 2025
5cec392
Make wrappers and convertors non-nullable
noti0na1 Sep 11, 2025
1c4c83b
Fix NPEs revealed by tests
noti0na1 Sep 15, 2025
4eeaf7a
Fix a missing comment
noti0na1 Sep 15, 2025
d56985c
Remove some unnecessary .nn
noti0na1 Sep 15, 2025
91f90d8
Try to migrate sjs library
noti0na1 Sep 15, 2025
57a3f43
Update tests about Regex
noti0na1 Sep 15, 2025
c8d9b93
Update required type signatures in sjs
noti0na1 Sep 18, 2025
8f99266
NO IDEA FOR NOW :)
noti0na1 Sep 18, 2025
e315680
Refactor type erasure logic for Scala 2 pseudo-unions in Scalajs
noti0na1 Sep 22, 2025
0f61340
Fix MiMa issue
noti0na1 Sep 23, 2025
a0abd36
Keep Yflexify-tasty tests disabled
noti0na1 Sep 24, 2025
558d143
Resolve some review comments
noti0na1 Sep 28, 2025
1d326c5
Update comment in LazyListIterable
noti0na1 Sep 28, 2025
20c9504
Migrate scaladoc-new
noti0na1 Sep 30, 2025
670cbb6
Re-enable explicit nulls in compiler
noti0na1 Sep 30, 2025
c7100a8
Update test code
noti0na1 Sep 30, 2025
5d03653
Use hack to compile scala3-library for now
noti0na1 Sep 30, 2025
01a8a85
Add comments
noti0na1 Sep 30, 2025
b7cb5de
Address review comments; avoid inferred flexible types
noti0na1 Oct 1, 2025
68d0fb9
Address review comments
noti0na1 Oct 1, 2025
3929722
Update Option.apply
noti0na1 Oct 1, 2025
4db2859
Revert "Update Option.apply"
noti0na1 Oct 2, 2025
1598582
Remove experimental annotation from Option.fromNullable
noti0na1 Oct 2, 2025
8f628ba
Address comments
noti0na1 Oct 3, 2025
da79ae2
Add mapNull and nullForGC to ScalaRunTime
noti0na1 Oct 3, 2025
4344b3d
Address some comments
noti0na1 Oct 3, 2025
a96f739
Re-apply nullable to Option.apply
noti0na1 Oct 5, 2025
542c9d7
Fix tests
noti0na1 Oct 5, 2025
7e4d698
Update Promise; update build
noti0na1 Oct 5, 2025
9b657fb
Fix build; remove fromNullable usage
noti0na1 Oct 5, 2025
4678e50
Address comments; polish Prop to disallow setting null string value
noti0na1 Oct 6, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions compiler/src/dotty/tools/MainGenericCompiler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/MainGenericRunner.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
8 changes: 4 additions & 4 deletions compiler/src/dotty/tools/backend/sjs/JSDefinitions.scala
Original file line number Diff line number Diff line change
Expand Up @@ -281,15 +281,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
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/core/Comments.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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+\\*+$", "")
}
}
Expand Down
16 changes: 10 additions & 6 deletions compiler/src/dotty/tools/dotc/core/TypeErasure.scala
Original file line number Diff line number Diff line change
Expand Up @@ -681,7 +681,15 @@ class TypeErasure(sourceLanguage: SourceLanguage, semiEraseVCs: Boolean, isConst
if e1.isInstanceOf[WildcardType] || e2.isInstanceOf[WildcardType] then WildcardType
else erasedGlb(e1, e2)
case OrType(tp1, tp2) =>
if isSymbol && sourceLanguage.isScala2 && ctx.settings.scalajs.value then
val e1 = this(tp1)
val e2 = this(tp2)
val result = if e1.isInstanceOf[WildcardType] || e2.isInstanceOf[WildcardType]
then WildcardType
else TypeComparer.orType(e1, e2, isErased = true)
def isNullStripped =
tp2.isNullType && e1.derivesFrom(defn.ObjectClass)
|| tp1.isNullType && e2.derivesFrom(defn.ObjectClass)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like these changes deserve a separate PR, as they actually change the compiler's behavior; not just the library types.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Created #24130
Do you have any suggestion how to test this in sjs?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hopefully once #24115 is in, we will have enough coverage for this by construction.

if isSymbol && sourceLanguage.isScala2 && ctx.settings.scalajs.value && !isNullStripped then
// In Scala2Unpickler we unpickle Scala.js pseudo-unions as if they were
// real unions, but we must still erase them as Scala 2 would to emit
// the correct signatures in SJSIR.
Expand All @@ -692,11 +700,7 @@ class TypeErasure(sourceLanguage: SourceLanguage, semiEraseVCs: Boolean, isConst
// impact on overriding relationships so it's best to leave them
// alone (and this doesn't impact the SJSIR we generate).
JSDefinitions.jsdefn.PseudoUnionType
else
val e1 = this(tp1)
val e2 = this(tp2)
if e1.isInstanceOf[WildcardType] || e2.isInstanceOf[WildcardType] then WildcardType
else TypeComparer.orType(e1, e2, isErased = true)
else result
case tp: MethodType =>
def paramErasure(tpToErase: Type) =
erasureFn(sourceLanguage, semiEraseVCs, isConstructor, isSymbol, inSigName = false)(tpToErase)
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/reporting/messages.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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}"
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/semanticdb/Scala3.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/repl/ParseResult.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
4 changes: 2 additions & 2 deletions compiler/src/dotty/tools/scripting/Main.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/scripting/Util.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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

12 changes: 6 additions & 6 deletions compiler/test/dotty/tools/debug/DebugStepAssert.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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)
Expand Down
35 changes: 16 additions & 19 deletions compiler/test/dotty/tools/dotc/CompilationTests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,6 @@ class CompilationTests {
).checkCompile()

// Explicit nulls tests
@Ignore
@Test def explicitNullsNeg: Unit = {
implicit val testGroup: TestGroup = TestGroup("explicitNullsNeg")
aggregateTests(
Expand All @@ -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(
Expand All @@ -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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
2 changes: 1 addition & 1 deletion compiler/test/dotty/tools/io/ClasspathTest.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
2 changes: 1 addition & 1 deletion library-js/src/scala/Console.scala
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,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
Expand Down
14 changes: 7 additions & 7 deletions library-js/src/scala/Enumeration.scala
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,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
Expand All @@ -119,7 +119,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. */
Expand All @@ -128,7 +128,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
Expand Down Expand Up @@ -190,7 +190,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`.
Expand All @@ -200,7 +200,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)
Expand Down Expand Up @@ -229,9 +229,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)
Expand Down
Loading
Loading