-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Use unitialized
for wildcard initializers
#11231
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 7 commits
33501d3
eab08cf
f073d15
c1b17aa
60f7f69
5543550
493082b
90907ed
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -10,6 +10,7 @@ import Symbols._ | |
import Types._ | ||
import typer.RefChecks | ||
import MegaPhase.MiniPhase | ||
import StdNames.nme | ||
import ast.tpd | ||
|
||
/** This phase makes all erased term members of classes private so that they cannot | ||
|
@@ -18,6 +19,10 @@ import ast.tpd | |
* The phase also replaces all expressions that appear in an erased context by | ||
* default values. This is necessary so that subsequent checking phases such | ||
* as IsInstanceOfChecker don't give false negatives. | ||
* Finally, the phase replaces `compiletime.uninitialized` on the right hand side | ||
* of a mutable field definition by `_`. This avoids a "is declared erased, but is | ||
* in fact used" error in Erasure and communicates to Constructors that the | ||
* variable does not have an initializer. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
*/ | ||
class PruneErasedDefs extends MiniPhase with SymTransformer { thisTransform => | ||
import tpd._ | ||
|
@@ -38,10 +43,25 @@ class PruneErasedDefs extends MiniPhase with SymTransformer { thisTransform => | |
cpy.Apply(tree)(tree.fun, tree.args.map(trivialErasedTree)) | ||
else tree | ||
|
||
private def hasUninitializedRHS(tree: ValOrDefDef)(using Context): Boolean = | ||
def recur(rhs: Tree): Boolean = rhs match | ||
case rhs: RefTree => | ||
rhs.symbol == defn.Compiletime_uninitialized | ||
&& tree.symbol.is(Mutable) && tree.symbol.owner.isClass | ||
case closureDef(ddef) if defn.isContextFunctionType(tree.tpt.tpe.dealias) => | ||
recur(ddef.rhs) | ||
case _ => | ||
false | ||
recur(tree.rhs) | ||
|
||
override def transformValDef(tree: ValDef)(using Context): Tree = | ||
if (tree.symbol.isEffectivelyErased && !tree.rhs.isEmpty) | ||
val sym = tree.symbol | ||
if tree.symbol.isEffectivelyErased && !tree.rhs.isEmpty then | ||
cpy.ValDef(tree)(rhs = trivialErasedTree(tree)) | ||
else tree | ||
else if hasUninitializedRHS(tree) then | ||
cpy.ValDef(tree)(rhs = cpy.Ident(tree.rhs)(nme.WILDCARD).withType(tree.tpt.tpe)) | ||
else | ||
tree | ||
|
||
override def transformDefDef(tree: DefDef)(using Context): Tree = | ||
if (tree.symbol.isEffectivelyErased && !tree.rhs.isEmpty) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
--- | ||
layout: doc-page | ||
title: "Dropped: wildcard initializer" | ||
--- | ||
|
||
The syntax | ||
```scala | ||
var x: A = _ | ||
``` | ||
that was used to indicate an uninitialized field, has been dropped. | ||
At its place there is a special value `uninitialized` in the `scala.compiletime` package. To get an uninitialized field, you now write | ||
nicolasstucki marked this conversation as resolved.
Show resolved
Hide resolved
|
||
```scala | ||
import scala.compiletime.uninitialized | ||
|
||
var x: A = uninitialized | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Currently, we also support var a = uninitialized[A] This seems to be leaking implementation details. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Right. We it's better to define it like this: @compileTimeOnly("`uninitialized` can only be used as the right hand side of a mutable field definition")
def uninitialized: Nothing = ??? |
||
``` | ||
To enable cross-compilation, `_` is still supported, but it will be dropped in a future 3.x version. | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
import compiletime.uninitialized | ||
|
||
class Memo[A](x: => A): | ||
private var cached: A = _ // error | ||
private var known: Boolean = false | ||
def force = | ||
if !known then | ||
known = true | ||
cached = x | ||
cached |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
-- Error: tests/neg/i11225.scala:5:16 ---------------------------------------------------------------------------------- | ||
5 | val x1: Int = uninitialized // error | ||
| ^^^^^^^^^^^^^ | ||
| `uninitialized` can only be used as the right hand side of a mutable field definition | ||
-- Error: tests/neg/i11225.scala:6:28 ---------------------------------------------------------------------------------- | ||
6 | var x2: Int = if ??? then uninitialized else uninitialized // error // error | ||
| ^^^^^^^^^^^^^ | ||
| `uninitialized` can only be used as the right hand side of a mutable field definition | ||
-- Error: tests/neg/i11225.scala:6:47 ---------------------------------------------------------------------------------- | ||
6 | var x2: Int = if ??? then uninitialized else uninitialized // error // error | ||
| ^^^^^^^^^^^^^ | ||
| `uninitialized` can only be used as the right hand side of a mutable field definition | ||
-- Error: tests/neg/i11225.scala:9:28 ---------------------------------------------------------------------------------- | ||
9 | var x5: () => Int = () => uninitialized // error | ||
| ^^^^^^^^^^^^^ | ||
| `uninitialized` can only be used as the right hand side of a mutable field definition | ||
-- Error: tests/neg/i11225.scala:10:18 --------------------------------------------------------------------------------- | ||
10 | var x6: Int = { uninitialized } // error | ||
| ^^^^^^^^^^^^^ | ||
| `uninitialized` can only be used as the right hand side of a mutable field definition | ||
nicolasstucki marked this conversation as resolved.
Show resolved
Hide resolved
|
||
-- Error: tests/neg/i11225.scala:13:22 --------------------------------------------------------------------------------- | ||
13 | var cached: Int = uninitialized // error | ||
| ^^^^^^^^^^^^^ | ||
| `uninitialized` can only be used as the right hand side of a mutable field definition | ||
-- Error: tests/neg/i11225.scala:14:30 --------------------------------------------------------------------------------- | ||
14 | cached = if x then 1 else uninitialized // error | ||
| ^^^^^^^^^^^^^ | ||
| `uninitialized` can only be used as the right hand side of a mutable field definition | ||
-- Error: tests/neg/i11225.scala:17:4 ---------------------------------------------------------------------------------- | ||
17 | uninitialized // error | ||
| ^^^^^^^^^^^^^ | ||
| `uninitialized` can only be used as the right hand side of a mutable field definition | ||
-- Error: tests/neg/i11225.scala:18:4 ---------------------------------------------------------------------------------- | ||
18 | uninitialized // error | ||
| ^^^^^^^^^^^^^ | ||
| `uninitialized` can only be used as the right hand side of a mutable field definition | ||
-- Error: tests/neg/i11225.scala:23:4 ---------------------------------------------------------------------------------- | ||
23 | uninitialized // error | ||
| ^^^^^^^^^^^^^ | ||
| `uninitialized` can only be used as the right hand side of a mutable field definition | ||
-- Error: tests/neg/i11225.scala:30:16 --------------------------------------------------------------------------------- | ||
30 | var x7: Int = uni // error | ||
| ^^^ | ||
| `uninitialized` can only be used as the right hand side of a mutable field definition | ||
| This location contains code that was inlined from i11225.scala:25 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
import compiletime.uninitialized | ||
|
||
class Test: | ||
|
||
val x1: Int = uninitialized // error | ||
var x2: Int = if ??? then uninitialized else uninitialized // error // error | ||
var x3: Int = if true then uninitialized else 1 // ok | ||
var x4: Int = if false then uninitialized else 1 // ok | ||
var x5: () => Int = () => uninitialized // error | ||
var x6: Int = { uninitialized } // error | ||
|
||
def f(x: Boolean) = | ||
var cached: Int = uninitialized // error | ||
cached = if x then 1 else uninitialized // error | ||
|
||
var c: Int = | ||
uninitialized // error | ||
uninitialized // error | ||
2 | ||
|
||
var d: Int = | ||
println("pseudo init") | ||
uninitialized // error | ||
|
||
transparent inline def uni = uninitialized | ||
|
||
inline def g(inline x: Int): Unit = () | ||
def f2 = g(uninitialized) // this one is ok since `uninitialized` is inlined away | ||
|
||
var x7: Int = uni // error | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This should be ok as the code we end up with is var x7: Int = uni // ok
// after inlining
var x7: Int = uninitialized // still ok There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Otherwise, we would need to make There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's an error since we end up with There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Then it would be inconsistent with There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we need not overthink this. The intention is that you should not use |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,6 @@ | ||
|
||
class Regress { | ||
var v: Int = _ | ||
var v: Int = compiletime.uninitialized | ||
def f = 42 | ||
var w: Int = (_) // error: not default value syntax | ||
} |
Uh oh!
There was an error while loading. Please reload this page.