Skip to content

Commit 36159bb

Browse files
authored
Merge pull request #11231 from dotty-staging/fix-11225
Use `unitialized` for wildcard initializers
2 parents 49389b3 + 90907ed commit 36159bb

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

68 files changed

+282
-118
lines changed

bench/tests/Vector.scala

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import scala.annotation.unchecked.uncheckedVariance
1414
import scala.compat.Platform
1515
import scala.collection.generic._
1616
import scala.collection.mutable.Builder
17+
import compiletime.uninitialized
1718

1819
/** Companion object to the Vector class
1920
*/
@@ -741,13 +742,13 @@ final class VectorBuilder[A]() extends Builder[A,Vector[A]] with VectorPointer[A
741742

742743

743744
private[immutable] trait VectorPointer[T] {
744-
private[immutable] var depth: Int = _
745-
private[immutable] var display0: Array[AnyRef] = _
746-
private[immutable] var display1: Array[AnyRef] = _
747-
private[immutable] var display2: Array[AnyRef] = _
748-
private[immutable] var display3: Array[AnyRef] = _
749-
private[immutable] var display4: Array[AnyRef] = _
750-
private[immutable] var display5: Array[AnyRef] = _
745+
private[immutable] var depth: Int = uninitialized
746+
private[immutable] var display0: Array[AnyRef] = uninitialized
747+
private[immutable] var display1: Array[AnyRef] = uninitialized
748+
private[immutable] var display2: Array[AnyRef] = uninitialized
749+
private[immutable] var display3: Array[AnyRef] = uninitialized
750+
private[immutable] var display4: Array[AnyRef] = uninitialized
751+
private[immutable] var display5: Array[AnyRef] = uninitialized
751752

752753
// used
753754
private[immutable] final def initFrom[U](that: VectorPointer[U]): Unit = initFrom(that, that.depth)

compiler/src/dotty/tools/dotc/core/Definitions.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -226,8 +226,9 @@ class Definitions {
226226
@tu lazy val CompiletimePackageObject: Symbol = requiredModule("scala.compiletime.package")
227227
@tu lazy val Compiletime_codeOf: Symbol = CompiletimePackageObject.requiredMethod("codeOf")
228228
@tu lazy val Compiletime_erasedValue : Symbol = CompiletimePackageObject.requiredMethod("erasedValue")
229+
@tu lazy val Compiletime_uninitialized: Symbol = CompiletimePackageObject.requiredMethod("uninitialized")
229230
@tu lazy val Compiletime_error : Symbol = CompiletimePackageObject.requiredMethod(nme.error)
230-
@tu lazy val Compiletime_requireConst: Symbol = CompiletimePackageObject.requiredMethod("requireConst")
231+
@tu lazy val Compiletime_requireConst : Symbol = CompiletimePackageObject.requiredMethod("requireConst")
231232
@tu lazy val Compiletime_constValue : Symbol = CompiletimePackageObject.requiredMethod("constValue")
232233
@tu lazy val Compiletime_constValueOpt: Symbol = CompiletimePackageObject.requiredMethod("constValueOpt")
233234
@tu lazy val Compiletime_summonFrom : Symbol = CompiletimePackageObject.requiredMethod("summonFrom")

compiler/src/dotty/tools/dotc/parsing/Parsers.scala

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3201,7 +3201,8 @@ object Parsers {
32013201

32023202
/** PatDef ::= ids [‘:’ Type] ‘=’ Expr
32033203
* | Pattern2 [‘:’ Type] ‘=’ Expr
3204-
* VarDef ::= PatDef | id {`,' id} `:' Type `=' `_'
3204+
* VarDef ::= PatDef
3205+
* | id {`,' id} `:' Type `=' `_' (deprecated in 3.1)
32053206
* ValDcl ::= id {`,' id} `:' Type
32063207
* VarDcl ::= id {`,' id} `:' Type
32073208
*/
@@ -3224,9 +3225,14 @@ object Parsers {
32243225
val rhs =
32253226
if tpt.isEmpty || in.token == EQUALS then
32263227
accept(EQUALS)
3228+
val rhsOffset = in.offset
32273229
subExpr() match
32283230
case rhs0 @ Ident(name) if placeholderParams.nonEmpty && name == placeholderParams.head.name
32293231
&& !tpt.isEmpty && mods.is(Mutable) && lhs.forall(_.isInstanceOf[Ident]) =>
3232+
if sourceVersion.isAtLeast(`3.1`) then
3233+
deprecationWarning(
3234+
em"""`= _` has been deprecated; use `= uninitialized` instead.
3235+
|`uninitialized` can be imported with `scala.compiletime.uninitialized`.""", rhsOffset)
32303236
placeholderParams = placeholderParams.tail
32313237
atSpan(rhs0.span) { Ident(nme.WILDCARD) }
32323238
case rhs0 => rhs0

compiler/src/dotty/tools/dotc/transform/PruneErasedDefs.scala

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import Symbols._
1010
import Types._
1111
import typer.RefChecks
1212
import MegaPhase.MiniPhase
13+
import StdNames.nme
1314
import ast.tpd
1415

1516
/** This phase makes all erased term members of classes private so that they cannot
@@ -18,6 +19,10 @@ import ast.tpd
1819
* The phase also replaces all expressions that appear in an erased context by
1920
* default values. This is necessary so that subsequent checking phases such
2021
* as IsInstanceOfChecker don't give false negatives.
22+
* Finally, the phase replaces `compiletime.uninitialized` on the right hand side
23+
* of a mutable field definition by `_`. This avoids a "is declared erased, but is
24+
* in fact used" error in Erasure and communicates to Constructors that the
25+
* variable does not have an initializer.
2126
*/
2227
class PruneErasedDefs extends MiniPhase with SymTransformer { thisTransform =>
2328
import tpd._
@@ -38,10 +43,25 @@ class PruneErasedDefs extends MiniPhase with SymTransformer { thisTransform =>
3843
cpy.Apply(tree)(tree.fun, tree.args.map(trivialErasedTree))
3944
else tree
4045

46+
private def hasUninitializedRHS(tree: ValOrDefDef)(using Context): Boolean =
47+
def recur(rhs: Tree): Boolean = rhs match
48+
case rhs: RefTree =>
49+
rhs.symbol == defn.Compiletime_uninitialized
50+
&& tree.symbol.is(Mutable) && tree.symbol.owner.isClass
51+
case closureDef(ddef) if defn.isContextFunctionType(tree.tpt.tpe.dealias) =>
52+
recur(ddef.rhs)
53+
case _ =>
54+
false
55+
recur(tree.rhs)
56+
4157
override def transformValDef(tree: ValDef)(using Context): Tree =
42-
if (tree.symbol.isEffectivelyErased && !tree.rhs.isEmpty)
58+
val sym = tree.symbol
59+
if tree.symbol.isEffectivelyErased && !tree.rhs.isEmpty then
4360
cpy.ValDef(tree)(rhs = trivialErasedTree(tree))
44-
else tree
61+
else if hasUninitializedRHS(tree) then
62+
cpy.ValDef(tree)(rhs = cpy.Ident(tree.rhs)(nme.WILDCARD).withType(tree.tpt.tpe))
63+
else
64+
tree
4565

4666
override def transformDefDef(tree: DefDef)(using Context): Tree =
4767
if (tree.symbol.isEffectivelyErased && !tree.rhs.isEmpty)

compiler/test/dotty/tools/dotc/CompilationTests.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ class CompilationTests {
119119
aggregateTests(
120120
compileFilesInDir("tests/neg", defaultOptions),
121121
compileFilesInDir("tests/neg-tailcall", defaultOptions),
122-
compileFilesInDir("tests/neg-strict", defaultOptions.and("-source", "3.1", "-Xfatal-warnings")),
122+
compileFilesInDir("tests/neg-strict", defaultOptions.and("-source", "3.1", "-deprecation", "-Xfatal-warnings")),
123123
compileFilesInDir("tests/neg-no-kind-polymorphism", defaultOptions and "-Yno-kind-polymorphism"),
124124
compileFilesInDir("tests/neg-custom-args/deprecation", defaultOptions.and("-Xfatal-warnings", "-deprecation")),
125125
compileFilesInDir("tests/neg-custom-args/fatal-warnings", defaultOptions.and("-Xfatal-warnings")),

docs/docs/internals/syntax.md

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -384,14 +384,12 @@ TypeDcl ::= id [TypeParamClause] {FunParamClause} TypeBounds
384384
[‘=’ Type]
385385
386386
Def ::= ‘val’ PatDef
387-
| ‘var’ VarDef
387+
| ‘var’ PatDef
388388
| ‘def’ DefDef
389389
| ‘type’ {nl} TypeDcl
390390
| TmplDef
391391
PatDef ::= ids [‘:’ Type] ‘=’ Expr
392-
| Pattern2 [‘:’ Type] ‘=’ Expr PatDef(_, pats, tpe?, expr)
393-
VarDef ::= PatDef
394-
| ids ‘:’ Type ‘=’ ‘_’
392+
| Pattern2 [‘:’ Type] ‘=’ Expr PatDef(_, pats, tpe?, expr)
395393
DefDef ::= DefSig [‘:’ Type] ‘=’ Expr DefDef(_, name, tparams, vparamss, tpe, expr)
396394
| ‘this’ DefParamClause DefParamClauses ‘=’ ConstrExpr DefDef(_, <init>, Nil, vparamss, EmptyTree, expr | Block)
397395
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
---
2+
layout: doc-page
3+
title: "Dropped: wildcard initializer"
4+
---
5+
6+
The syntax
7+
```scala
8+
var x: A = _
9+
```
10+
that was used to indicate an uninitialized field, has been dropped.
11+
At its place there is a special value `uninitialized` in the `scala.compiletime` package. To get an uninitialized field, you now write
12+
```scala
13+
import scala.compiletime.uninitialized
14+
15+
var x: A = uninitialized
16+
```
17+
To enable cross-compilation, `_` is still supported, but it will be dropped in a future 3.x version.
18+

docs/docs/reference/syntax.md

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -374,14 +374,12 @@ DefSig ::= id [DefTypeParamClause] DefParamClauses
374374
TypeDcl ::= id [TypeParamClause] {FunParamClause} TypeBounds [‘=’ Type]
375375
376376
Def ::= ‘val’ PatDef
377-
| ‘var’ VarDef
377+
| ‘var’ PatDef
378378
| ‘def’ DefDef
379379
| ‘type’ {nl} TypeDcl
380380
| TmplDef
381381
PatDef ::= ids [‘:’ Type] ‘=’ Expr
382382
| Pattern2 [‘:’ Type] ‘=’ Expr
383-
VarDef ::= PatDef
384-
| ids ‘:’ Type ‘=’ ‘_’
385383
DefDef ::= DefSig [‘:’ Type] ‘=’ Expr
386384
| ‘this’ DefParamClause DefParamClauses ‘=’ ConstrExpr
387385

docs/sidebar.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,8 @@ sidebar:
189189
url: docs/reference/dropped-features/nonlocal-returns.html
190190
- title: "[this] Qualifier"
191191
url: docs/reference/dropped-features/this-qualifier.html
192+
- title: Wildcard initializers
193+
url: docs/reference/dropped-features/wildcard-init.html
192194
- title: Syntax Summary
193195
url: docs/reference/syntax.html
194196
- title: Contributing

library/src/scala/compiletime/package.scala

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package scala
2-
3-
import scala.quoted._
2+
import annotation.compileTimeOnly
43

54
package object compiletime {
65

@@ -18,6 +17,17 @@ package object compiletime {
1817
*/
1918
erased def erasedValue[T]: T = ???
2019

20+
/** Used as the initializer of a mutable class or object field, like this:
21+
*
22+
* var x: T = uninitialized
23+
*
24+
* This signifies that the field is not initialized on its own. It is still initialized
25+
* as part of the bulk initialization of the object it belongs to, which assigns zero
26+
* values such as `null`, `0`, `0.0`, `false` to all object fields.
27+
*/
28+
@compileTimeOnly("`uninitialized` can only be used as the right hand side of a mutable field definition")
29+
def uninitialized: Nothing = ???
30+
2131
/** The error method is used to produce user-defined compile errors during inline expansion.
2232
* If an inline expansion results in a call error(msgStr) the compiler produces an error message containing the given msgStr.
2333
*

tests/fuzzy/471d33abf565d5dd3691679237f148638f4ff115.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ def i10(i2: i4): i3 = new i4(i5)
2424
object i10 {
2525
def main(i12: Array[String]): Unit = {
2626
val i10: Array[String] = null
27-
var i2 = _
27+
var i2 = compiletime.uninitialized
2828
def i3(i2: Int) = i2
2929
}
3030
object i0 {

tests/init/crash/i2468.scala

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11

2+
import compiletime.uninitialized
23
object Test {
34

45
class A {
5-
private[this] var x: String = _
6+
private[this] var x: String = uninitialized
67
}
78

89
class B {
@@ -11,16 +12,16 @@ object Test {
1112
}
1213

1314
class C {
14-
private[this] var x1: Int = _
15-
private[this] var x2: Unit = _
16-
private[this] var x3: Char = _
17-
private[this] var x4: Boolean = _
18-
private[this] var x5: Float = _
19-
private[this] var x6: Double = _
20-
private[this] var x7: Char = _
21-
private[this] var x8: Byte = _
22-
private[this] var x9: AnyVal = _
23-
private[this] var x10: D = _
15+
private[this] var x1: Int = uninitialized
16+
private[this] var x2: Unit = uninitialized
17+
private[this] var x3: Char = uninitialized
18+
private[this] var x4: Boolean = uninitialized
19+
private[this] var x5: Float = uninitialized
20+
private[this] var x6: Double = uninitialized
21+
private[this] var x7: Char = uninitialized
22+
private[this] var x8: Byte = uninitialized
23+
private[this] var x9: AnyVal = uninitialized
24+
private[this] var x10: D = uninitialized
2425
}
2526

2627
class D(x: Int) extends AnyVal

tests/init/crash/opassign.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ object opassign {
1212
}
1313

1414
class Ref {
15-
var x: Int = _
15+
var x: Int = compiletime.uninitialized
1616
}
1717
val r = new Ref
1818
r.x += 1

tests/neg-strict/i1050.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ object Tiark3 {
7474
def brand(x: Any): p.L = x // error: underlying not concrete
7575
}
7676
trait V extends U {
77-
type X = B with A
77+
type X = B & A
7878
def p2: X = ???
7979
}
8080
val v = new V {}

tests/neg-strict/i11225.scala

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import compiletime.uninitialized
2+
3+
class Memo[A](x: => A):
4+
private var cached: A = _ // error
5+
private var known: Boolean = false
6+
def force =
7+
if !known then
8+
known = true
9+
cached = x
10+
cached

tests/neg-strict/nullless.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,8 @@ object bar {
2929
type U = UU
3030
}
3131
final lazy val nothing: Nothing = nothing
32-
final lazy val sub: S2 with Sub = nothing
33-
final lazy val box : Box[S2 with Sub] = new Box(nothing)
32+
final lazy val sub: S2 & Sub = nothing
33+
final lazy val box : Box[S2 & Sub] = new Box(nothing)
3434
def upcast(t: box.v.M2): box.v.M2 = t // error // error under -strict
3535
}
3636
def main(args : Array[String]) : Unit = {

tests/neg/i11225.check

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
-- Error: tests/neg/i11225.scala:5:16 ----------------------------------------------------------------------------------
2+
5 | val x1: Int = uninitialized // error
3+
| ^^^^^^^^^^^^^
4+
| `uninitialized` can only be used as the right hand side of a mutable field definition
5+
-- Error: tests/neg/i11225.scala:6:28 ----------------------------------------------------------------------------------
6+
6 | var x2: Int = if ??? then uninitialized else uninitialized // error // error
7+
| ^^^^^^^^^^^^^
8+
| `uninitialized` can only be used as the right hand side of a mutable field definition
9+
-- Error: tests/neg/i11225.scala:6:47 ----------------------------------------------------------------------------------
10+
6 | var x2: Int = if ??? then uninitialized else uninitialized // error // error
11+
| ^^^^^^^^^^^^^
12+
| `uninitialized` can only be used as the right hand side of a mutable field definition
13+
-- Error: tests/neg/i11225.scala:9:28 ----------------------------------------------------------------------------------
14+
9 | var x5: () => Int = () => uninitialized // error
15+
| ^^^^^^^^^^^^^
16+
| `uninitialized` can only be used as the right hand side of a mutable field definition
17+
-- Error: tests/neg/i11225.scala:10:18 ---------------------------------------------------------------------------------
18+
10 | var x6: Int = { uninitialized } // error
19+
| ^^^^^^^^^^^^^
20+
| `uninitialized` can only be used as the right hand side of a mutable field definition
21+
-- Error: tests/neg/i11225.scala:13:22 ---------------------------------------------------------------------------------
22+
13 | var cached: Int = uninitialized // error
23+
| ^^^^^^^^^^^^^
24+
| `uninitialized` can only be used as the right hand side of a mutable field definition
25+
-- Error: tests/neg/i11225.scala:14:30 ---------------------------------------------------------------------------------
26+
14 | cached = if x then 1 else uninitialized // error
27+
| ^^^^^^^^^^^^^
28+
| `uninitialized` can only be used as the right hand side of a mutable field definition
29+
-- Error: tests/neg/i11225.scala:17:4 ----------------------------------------------------------------------------------
30+
17 | uninitialized // error
31+
| ^^^^^^^^^^^^^
32+
| `uninitialized` can only be used as the right hand side of a mutable field definition
33+
-- Error: tests/neg/i11225.scala:18:4 ----------------------------------------------------------------------------------
34+
18 | uninitialized // error
35+
| ^^^^^^^^^^^^^
36+
| `uninitialized` can only be used as the right hand side of a mutable field definition
37+
-- Error: tests/neg/i11225.scala:23:4 ----------------------------------------------------------------------------------
38+
23 | uninitialized // error
39+
| ^^^^^^^^^^^^^
40+
| `uninitialized` can only be used as the right hand side of a mutable field definition
41+
-- Error: tests/neg/i11225.scala:30:16 ---------------------------------------------------------------------------------
42+
30 | var x7: Int = uni // error
43+
| ^^^
44+
| `uninitialized` can only be used as the right hand side of a mutable field definition
45+
| This location contains code that was inlined from i11225.scala:25

tests/neg/i11225.scala

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import compiletime.uninitialized
2+
3+
class Test:
4+
5+
val x1: Int = uninitialized // error
6+
var x2: Int = if ??? then uninitialized else uninitialized // error // error
7+
var x3: Int = if true then uninitialized else 1 // ok
8+
var x4: Int = if false then uninitialized else 1 // ok
9+
var x5: () => Int = () => uninitialized // error
10+
var x6: Int = { uninitialized } // error
11+
12+
def f(x: Boolean) =
13+
var cached: Int = uninitialized // error
14+
cached = if x then 1 else uninitialized // error
15+
16+
var c: Int =
17+
uninitialized // error
18+
uninitialized // error
19+
2
20+
21+
var d: Int =
22+
println("pseudo init")
23+
uninitialized // error
24+
25+
transparent inline def uni = uninitialized
26+
27+
inline def g(inline x: Int): Unit = ()
28+
def f2 = g(uninitialized) // this one is ok since `uninitialized` is inlined away
29+
30+
var x7: Int = uni // error

tests/neg/i8427.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,6 @@
22
trait T
33

44
object Test {
5-
var t: T = _
5+
var t: T = compiletime.uninitialized
66
def main(args: Array[String]) = println("hi")
77
}

tests/neg/refinedSubtyping.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ class Test3 {
1313
type U1 = C { type T <: B }
1414
type U2 = C { type T <: A }
1515

16-
var x: T2 = _
16+
var x: T2 = compiletime.uninitialized
1717
val y1: U1 = ???
1818
val y2: U2 = ???
1919

tests/neg/t11437.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11

22
class Regress {
3-
var v: Int = _
3+
var v: Int = compiletime.uninitialized
44
def f = 42
55
var w: Int = (_) // error: not default value syntax
66
}

0 commit comments

Comments
 (0)