Skip to content

Commit e5b0763

Browse files
committed
Use notInitialized for wildcard initializers
Fixes scala#11225
1 parent a3b7ed6 commit e5b0763

File tree

7 files changed

+68
-9
lines changed

7 files changed

+68
-9
lines changed

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

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -225,12 +225,13 @@ class Definitions {
225225

226226
@tu lazy val CompiletimePackageObject: Symbol = requiredModule("scala.compiletime.package")
227227
@tu lazy val Compiletime_codeOf: Symbol = CompiletimePackageObject.requiredMethod("codeOf")
228-
@tu lazy val Compiletime_erasedValue : Symbol = CompiletimePackageObject.requiredMethod("erasedValue")
229-
@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_constValue : Symbol = CompiletimePackageObject.requiredMethod("constValue")
232-
@tu lazy val Compiletime_constValueOpt: Symbol = CompiletimePackageObject.requiredMethod("constValueOpt")
233-
@tu lazy val Compiletime_summonFrom : Symbol = CompiletimePackageObject.requiredMethod("summonFrom")
228+
@tu lazy val Compiletime_erasedValue : Symbol = CompiletimePackageObject.requiredMethod("erasedValue")
229+
@tu lazy val Compiletime_notInitialized: Symbol = CompiletimePackageObject.requiredMethod("notInitialized")
230+
@tu lazy val Compiletime_error : Symbol = CompiletimePackageObject.requiredMethod(nme.error)
231+
@tu lazy val Compiletime_requireConst : Symbol = CompiletimePackageObject.requiredMethod("requireConst")
232+
@tu lazy val Compiletime_constValue : Symbol = CompiletimePackageObject.requiredMethod("constValue")
233+
@tu lazy val Compiletime_constValueOpt : Symbol = CompiletimePackageObject.requiredMethod("constValueOpt")
234+
@tu lazy val Compiletime_summonFrom : Symbol = CompiletimePackageObject.requiredMethod("summonFrom")
234235
@tu lazy val CompiletimeTestingPackage: Symbol = requiredPackage("scala.compiletime.testing")
235236
@tu lazy val CompiletimeTesting_typeChecks: Symbol = CompiletimeTestingPackage.requiredMethod("typeChecks")
236237
@tu lazy val CompiletimeTesting_typeCheckErrors: Symbol = CompiletimeTestingPackage.requiredMethod("typeCheckErrors")

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

Lines changed: 14 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.notInitialized` 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._
@@ -39,9 +44,16 @@ class PruneErasedDefs extends MiniPhase with SymTransformer { thisTransform =>
3944
else tree
4045

4146
override def transformValDef(tree: ValDef)(using Context): Tree =
42-
if (tree.symbol.isEffectivelyErased && !tree.rhs.isEmpty)
47+
val sym = tree.symbol
48+
if sym.isEffectivelyErased && !tree.rhs.isEmpty then
4349
cpy.ValDef(tree)(rhs = trivialErasedTree(tree))
44-
else tree
50+
else tree.rhs match
51+
case rhs: TypeApply
52+
if rhs.symbol == defn.Compiletime_notInitialized
53+
&& sym.is(Mutable) && sym.owner.isClass =>
54+
cpy.ValDef(tree)(rhs = cpy.Ident(rhs)(nme.WILDCARD))
55+
case _ =>
56+
tree
4557

4658
override def transformDefDef(tree: DefDef)(using Context): Tree =
4759
if (tree.symbol.isEffectivelyErased && !tree.rhs.isEmpty)
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 `notInitialized` in the `scala.compiletime` package. To get an uninitialized field, you now write
12+
```scala
13+
import scala.compiletime.notInitialized
14+
15+
var x: A = notInitialized
16+
```
17+
To enable cross-compilation, `_` is still supported, but it will be dropped in a future 3.x version.
18+

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: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
package scala
22

3-
import scala.quoted._
3+
import scala.quoted
44

55
package object compiletime {
66

@@ -18,6 +18,15 @@ package object compiletime {
1818
*/
1919
erased def erasedValue[T]: T = ???
2020

21+
/** Used as the initializer of a class or object field, like this:
22+
*
23+
* val x: T = notInitialized
24+
*
25+
* This signifies that the field is not initialized by its own (On the JVM
26+
* it is still bulk-initialized to a zero bitfield).
27+
*/
28+
erased def notInitialized[T]: T = ???
29+
2130
/** The error method is used to produce user-defined compile errors during inline expansion.
2231
* If an inline expansion results in a call error(msgStr) the compiler produces an error message containing the given msgStr.
2332
*

tests/neg/i11225.scala

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import compiletime.notInitialized
2+
3+
@main def Test(x: Boolean) =
4+
var cached: Int = notInitialized // error
5+
cached = if x then 1 else notInitialized // error

tests/pos/i11225.scala

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

0 commit comments

Comments
 (0)