diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index 327f97a3cc9d..878b74a4e9fe 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -3665,11 +3665,14 @@ object Parsers { subExpr() match case rhs0 @ Ident(name) if placeholderParams.nonEmpty && name == placeholderParams.head.name && !tpt.isEmpty && mods.is(Mutable) && lhs.forall(_.isInstanceOf[Ident]) => - if sourceVersion.isAtLeast(future) then - deprecationWarning( - em"""`= _` has been deprecated; use `= uninitialized` instead. - |`uninitialized` can be imported with `scala.compiletime.uninitialized`.""", - rhsOffset) + report.gradualErrorOrMigrationWarning( + em"""`= _` has been deprecated; use `= uninitialized` instead. + |`uninitialized` can be imported with `scala.compiletime.uninitialized`.${rewriteNotice(`3.4-migration`)}""", + in.sourcePos(rhsOffset), + warnFrom = `3.4`, + errorFrom = future) + if sourceVersion.isMigrating && sourceVersion.isAtLeast(`3.4-migration`) then + patch(source, Span(rhsOffset, rhsOffset + 1), "scala.compiletime.uninitialized") placeholderParams = placeholderParams.tail atSpan(rhs0.span) { Ident(nme.WILDCARD) } case rhs0 => rhs0 diff --git a/compiler/test/dotty/tools/dotc/CompilationTests.scala b/compiler/test/dotty/tools/dotc/CompilationTests.scala index 0491660219b2..21235ac048e2 100644 --- a/compiler/test/dotty/tools/dotc/CompilationTests.scala +++ b/compiler/test/dotty/tools/dotc/CompilationTests.scala @@ -60,6 +60,7 @@ class CompilationTests { compileFile("tests/rewrites/rewrites.scala", defaultOptions.and("-source", "3.0-migration").and("-rewrite", "-indent")), compileFile("tests/rewrites/rewrites3x.scala", defaultOptions.and("-rewrite", "-source", "future-migration")), compileFile("tests/rewrites/rewrites3x-fatal-warnings.scala", defaultOptions.and("-rewrite", "-source", "future-migration", "-Xfatal-warnings")), + compileFile("tests/rewrites/uninitialized-var.scala", defaultOptions.and("-rewrite", "-source", "future-migration")), compileFile("tests/rewrites/with-type-operator.scala", defaultOptions.and("-rewrite", "-source", "future-migration")), compileFile("tests/rewrites/private-this.scala", defaultOptions.and("-rewrite", "-source", "future-migration")), compileFile("tests/rewrites/filtering-fors.scala", defaultOptions.and("-rewrite", "-source", "3.2-migration")), diff --git a/language-server/src/dotty/tools/languageserver/DottyLanguageServer.scala b/language-server/src/dotty/tools/languageserver/DottyLanguageServer.scala index e7f9c332aeeb..c58b17e49559 100644 --- a/language-server/src/dotty/tools/languageserver/DottyLanguageServer.scala +++ b/language-server/src/dotty/tools/languageserver/DottyLanguageServer.scala @@ -15,6 +15,7 @@ import scala.collection._ import scala.jdk.CollectionConverters._ import scala.util.control.NonFatal import scala.io.Codec +import scala.compiletime.uninitialized import dotc._ import ast.{Trees, tpd, untpd} @@ -54,14 +55,14 @@ class DottyLanguageServer extends LanguageServer import lsp4j.jsonrpc.messages.{Either => JEither} import lsp4j._ - private var rootUri: String = _ + private var rootUri: String = uninitialized - private var myClient: DottyClient = _ + private var myClient: DottyClient = uninitialized def client: DottyClient = myClient - private var myDrivers: mutable.Map[ProjectConfig, InteractiveDriver] = _ + private var myDrivers: mutable.Map[ProjectConfig, InteractiveDriver] = uninitialized - private var myDependentProjects: mutable.Map[ProjectConfig, mutable.Set[ProjectConfig]] = _ + private var myDependentProjects: mutable.Map[ProjectConfig, mutable.Set[ProjectConfig]] = uninitialized def drivers: Map[ProjectConfig, InteractiveDriver] = thisServer.synchronized { if myDrivers == null then diff --git a/language-server/test/dotty/tools/languageserver/util/PositionContext.scala b/language-server/test/dotty/tools/languageserver/util/PositionContext.scala index 10629d900c92..1f73be867c06 100644 --- a/language-server/test/dotty/tools/languageserver/util/PositionContext.scala +++ b/language-server/test/dotty/tools/languageserver/util/PositionContext.scala @@ -3,9 +3,11 @@ package dotty.tools.languageserver.util import dotty.tools.languageserver.util.embedded.CodeMarker import dotty.tools.languageserver.util.server.TestFile +import scala.compiletime.uninitialized + class PositionContext(positionMap: Map[CodeMarker, (TestFile, Int, Int)]) { - private var lastKey: CodeMarker = _ - private var lastValue: (TestFile, Int, Int) = _ + private var lastKey: CodeMarker = uninitialized + private var lastValue: (TestFile, Int, Int) = uninitialized def positionOf(pos: CodeMarker): (TestFile, Int, Int) = { if (lastKey eq pos) lastValue else { diff --git a/language-server/test/dotty/tools/languageserver/util/server/TestServer.scala b/language-server/test/dotty/tools/languageserver/util/server/TestServer.scala index 749b92daba8c..473c14324860 100644 --- a/language-server/test/dotty/tools/languageserver/util/server/TestServer.scala +++ b/language-server/test/dotty/tools/languageserver/util/server/TestServer.scala @@ -7,6 +7,8 @@ import java.nio.file.{Files, Path} import java.nio.charset.StandardCharsets import java.util +import scala.compiletime.uninitialized + import dotty.tools.dotc.Main import dotty.tools.dotc.reporting.{Reporter, ThrowingReporter} import dotty.tools.io.Directory @@ -17,7 +19,7 @@ import org.eclipse.lsp4j.{ DidOpenTextDocumentParams, InitializeParams, Initiali class TestServer(testFolder: Path, projects: List[Project]) { val server = new DottyLanguageServer - var client: TestClient = _ + var client: TestClient = uninitialized init() diff --git a/library/src/scala/util/control/NonLocalReturns.scala b/library/src/scala/util/control/NonLocalReturns.scala index ad4dc05f36ac..c7e600b4c028 100644 --- a/library/src/scala/util/control/NonLocalReturns.scala +++ b/library/src/scala/util/control/NonLocalReturns.scala @@ -1,5 +1,7 @@ package scala.util.control +import scala.compiletime.uninitialized + /** Library implementation of nonlocal return. * * Usage: @@ -21,7 +23,7 @@ package scala.util.control object NonLocalReturns { @deprecated("Use scala.util.boundary.Break instead", "3.3") class ReturnThrowable[T] extends ControlThrowable { - private var myResult: T = _ + private var myResult: T = uninitialized def throwReturn(result: T): Nothing = { myResult = result throw this diff --git a/presentation-compiler/src/main/dotty/tools/pc/MetalsDriver.scala b/presentation-compiler/src/main/dotty/tools/pc/MetalsDriver.scala index 55504db7a11a..819c3f2fc9c9 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/MetalsDriver.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/MetalsDriver.scala @@ -7,6 +7,8 @@ import dotty.tools.dotc.interactive.InteractiveDriver import dotty.tools.dotc.reporting.Diagnostic import dotty.tools.dotc.util.SourceFile +import scala.compiletime.uninitialized + /** * MetalsDriver is a wrapper class that provides a compilation cache for InteractiveDriver. * MetalsDriver skips running compilation if @@ -29,7 +31,7 @@ class MetalsDriver( override val settings: List[String] ) extends InteractiveDriver(settings): - @volatile private var lastCompiledURI: URI = _ + @volatile private var lastCompiledURI: URI = uninitialized private def alreadyCompiled(uri: URI, content: Array[Char]): Boolean = compilationUnits.get(uri) match diff --git a/project/Build.scala b/project/Build.scala index 3ac19a0f7b35..ab4cfe19377b 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -1267,6 +1267,7 @@ object Build { ivyConfigurations += SourceDeps.hide, transitiveClassifiers := Seq("sources"), Compile / scalacOptions ++= Seq("-Yexplicit-nulls", "-Ysafe-init"), + Compile / scalacOptions ++= Seq("-source", "3.3"), // To avoid fatal migration warnings Compile / sourceGenerators += Def.task { val s = streams.value val cacheDir = s.cacheDirectory diff --git a/sbt-test/compilerReporter/i14576/Test.scala b/sbt-test/compilerReporter/i14576/Test.scala index d94a49145f81..8be852b846a0 100644 --- a/sbt-test/compilerReporter/i14576/Test.scala +++ b/sbt-test/compilerReporter/i14576/Test.scala @@ -10,8 +10,5 @@ object Test: def f(x: Text) = println(x.str) f("abc") - // private[this] and = _ are deprecated under -source:future - private[this] var x: AnyRef = _ - // under -source:future, `_` is deprecated for wildcard arguments of types: use `?` instead val xs: List[_] = Nil diff --git a/sbt-test/compilerReporter/i14576/build.sbt b/sbt-test/compilerReporter/i14576/build.sbt index f9f211b24977..cc0402a7ba5e 100644 --- a/sbt-test/compilerReporter/i14576/build.sbt +++ b/sbt-test/compilerReporter/i14576/build.sbt @@ -24,7 +24,7 @@ lazy val root = (project in file(".")) }, assertDeprecationSummary := { assert { - FakePrintWriter.messages.exists(_.contains("there were 2 deprecation warnings; re-run with -deprecation for details")) + FakePrintWriter.messages.exists(_.contains("there was 1 deprecation warning; re-run with -deprecation for details")) } }, assertNoDeprecationSummary := { diff --git a/tests/neg/i4812.scala b/tests/neg/i4812.scala index 8d518107825c..c6f6dafc656c 100644 --- a/tests/neg/i4812.scala +++ b/tests/neg/i4812.scala @@ -1,6 +1,6 @@ //> using options -Werror object Test: - var prev: Any = _ + var prev: Any = scala.compiletime.uninitialized def test[T](x: T): T = class A(val elem: (T, Boolean)) @@ -55,7 +55,7 @@ object Test: def test6[T](x: T): T = class A { var b: B = null } - class B { var a: A = null; var elem: T = _ } + class B { var a: A = null; var elem: T = scala.compiletime.uninitialized } prev match case prev: A => // error: the type test for A cannot be checked at runtime prev.b.elem @@ -88,7 +88,7 @@ object Test: case x: B => x sealed class A - var prevA: A = _ + var prevA: A = scala.compiletime.uninitialized def test10: A = val methodCallId = System.nanoTime() class B(val id: Long) extends A diff --git a/tests/neg/uninitialized-3.4.check b/tests/neg/uninitialized-3.4.check new file mode 100644 index 000000000000..1c7b985072d0 --- /dev/null +++ b/tests/neg/uninitialized-3.4.check @@ -0,0 +1,6 @@ +-- Error: tests/neg/uninitialized-3.4.scala:7:15 ----------------------------------------------------------------------- +7 | var a: Int = _ // error: migration warning + | ^ + | `= _` has been deprecated; use `= uninitialized` instead. + | `uninitialized` can be imported with `scala.compiletime.uninitialized`. + | This construct can be rewritten automatically under -rewrite -source 3.4-migration. diff --git a/tests/neg/uninitialized-3.4.scala b/tests/neg/uninitialized-3.4.scala new file mode 100644 index 000000000000..174a95ae6c54 --- /dev/null +++ b/tests/neg/uninitialized-3.4.scala @@ -0,0 +1,8 @@ +//> using options -Werror + +import scala.language.`3.4` +import scala.compiletime.uninitialized + +class Foo: + var a: Int = _ // error: migration warning + var b: Int = uninitialized diff --git a/tests/neg/uninitialized-future-migration.scala b/tests/neg/uninitialized-future-migration.scala new file mode 100644 index 000000000000..05f7c6b67f38 --- /dev/null +++ b/tests/neg/uninitialized-future-migration.scala @@ -0,0 +1,8 @@ +//> using options -Werror + +import scala.language.`future-migration` +import scala.compiletime.uninitialized + +class Foo: + var a: Int = _ // error: migration warning + var b: Int = uninitialized diff --git a/tests/neg/uninitialized-future.scala b/tests/neg/uninitialized-future.scala new file mode 100644 index 000000000000..8882b70ed48b --- /dev/null +++ b/tests/neg/uninitialized-future.scala @@ -0,0 +1,6 @@ +import scala.language.future +import scala.compiletime.uninitialized + +class Foo: + var a: Int = _ // error + var b: Int = uninitialized diff --git a/tests/patmat/i12805-fallout.scala b/tests/patmat/i12805-fallout.scala index b598b36159ea..66d9c0d598fd 100644 --- a/tests/patmat/i12805-fallout.scala +++ b/tests/patmat/i12805-fallout.scala @@ -1,4 +1,5 @@ import scala.annotation.unchecked.uncheckedVariance +import scala.compiletime.uninitialized type Untyped = Null @@ -7,7 +8,7 @@ class Type abstract class Tree[-T >: Untyped] { type ThisTree[T >: Untyped] <: Tree[T] - protected var myTpe: T @uncheckedVariance = _ + protected var myTpe: T @uncheckedVariance = uninitialized def withType(tpe: Type): ThisTree[Type] = { val tree = this.asInstanceOf[ThisTree[Type]] diff --git a/tests/pos/uninitialized-future-migration.scala b/tests/pos/uninitialized-future-migration.scala new file mode 100644 index 000000000000..a1e606dc90fb --- /dev/null +++ b/tests/pos/uninitialized-future-migration.scala @@ -0,0 +1,6 @@ +import scala.language.`future-migration` +import scala.compiletime.uninitialized + +class Foo: + var a: Int = _ // warn + var b: Int = uninitialized diff --git a/tests/rewrites/uninitialized-var.check b/tests/rewrites/uninitialized-var.check new file mode 100644 index 000000000000..3809938512a7 --- /dev/null +++ b/tests/rewrites/uninitialized-var.check @@ -0,0 +1,2 @@ +class Foo: + var a: Int = scala.compiletime.uninitialized diff --git a/tests/rewrites/uninitialized-var.scala b/tests/rewrites/uninitialized-var.scala new file mode 100644 index 000000000000..910734b33350 --- /dev/null +++ b/tests/rewrites/uninitialized-var.scala @@ -0,0 +1,2 @@ +class Foo: + var a: Int = _