@@ -39,7 +39,7 @@ class LazyVals extends MiniPhase with IdentityDenotTransformer {
39
39
40
40
/** List of names of phases that should have finished processing of tree
41
41
* before this phase starts processing same tree */
42
- override def runsAfter = Set (Mixin .name)
42
+ override def runsAfter = Set (Mixin .name, CollectNullableFields .name )
43
43
44
44
override def changesMembers = true // the phase adds lazy val accessors
45
45
@@ -50,6 +50,18 @@ class LazyVals extends MiniPhase with IdentityDenotTransformer {
50
50
51
51
val containerFlagsMask = Flags .Method | Flags .Lazy | Flags .Accessor | Flags .Module
52
52
53
+ /** A map of lazy values to the fields they should null after initialization. */
54
+ private [this ] var lazyValNullables : Map [Symbol , List [Symbol ]] = _
55
+ private def nullableFor (sym : Symbol )(implicit ctx : Context ) =
56
+ if (sym.is(Flags .Module )) Nil
57
+ else lazyValNullables.getOrElse(sym, Nil )
58
+
59
+
60
+ override def prepareForUnit (tree : Tree )(implicit ctx : Context ) = {
61
+ lazyValNullables = ctx.collectNullableFieldsPhase.asInstanceOf [CollectNullableFields ].lazyValNullables
62
+ ctx
63
+ }
64
+
53
65
override def transformDefDef (tree : tpd.DefDef )(implicit ctx : Context ): tpd.Tree =
54
66
transformLazyVal(tree)
55
67
@@ -150,7 +162,7 @@ class LazyVals extends MiniPhase with IdentityDenotTransformer {
150
162
val initBody =
151
163
adaptToType(
152
164
ref(holderSymbol).select(defn.Object_synchronized ).appliedTo(
153
- adaptToType(mkNonThreadSafeDef(result, flag, initer), defn.ObjectType )),
165
+ adaptToType(mkNonThreadSafeDef(result, flag, initer, nullables = Nil ), defn.ObjectType )),
154
166
tpe)
155
167
val initTree = DefDef (initSymbol, initBody)
156
168
val holderTree = ValDef (holderSymbol, New (holderImpl.typeRef, List ()))
@@ -176,37 +188,51 @@ class LazyVals extends MiniPhase with IdentityDenotTransformer {
176
188
holders::: stats
177
189
}
178
190
191
+ private def nullOut (nullables : List [Symbol ])(implicit ctx : Context ): List [Tree ] = {
192
+ val nullConst = Literal (Constants .Constant (null ))
193
+ nullables.map { sym =>
194
+ val field = if (sym.isGetter) sym.field else sym
195
+ assert(field.isField)
196
+ field.setFlag(Flags .Mutable )
197
+ ref(field).becomes(nullConst)
198
+ }
199
+ }
200
+
179
201
/** Create non-threadsafe lazy accessor equivalent to such code
180
202
* def methodSymbol() = {
181
203
* if (flag) target
182
204
* else {
183
205
* target = rhs
184
206
* flag = true
207
+ * nullable = null
185
208
* target
186
209
* }
187
210
* }
188
211
*/
189
212
190
- def mkNonThreadSafeDef (target : Tree , flag : Tree , rhs : Tree )(implicit ctx : Context ) = {
213
+ def mkNonThreadSafeDef (target : Tree , flag : Tree , rhs : Tree , nullables : List [ Symbol ] )(implicit ctx : Context ) = {
191
214
val setFlag = flag.becomes(Literal (Constants .Constant (true )))
192
- val setTargets = if (isWildcardArg(rhs)) Nil else target.becomes(rhs) :: Nil
193
- val init = Block (setFlag :: setTargets, target.ensureApplied)
215
+ val setNullables = nullOut(nullables)
216
+ val setTargetAndNullable = if (isWildcardArg(rhs)) setNullables else target.becomes(rhs) :: setNullables
217
+ val init = Block (setFlag :: setTargetAndNullable, target.ensureApplied)
194
218
If (flag.ensureApplied, target.ensureApplied, init)
195
219
}
196
220
197
221
/** Create non-threadsafe lazy accessor for not-nullable types equivalent to such code
198
222
* def methodSymbol() = {
199
223
* if (target eq null) {
200
224
* target = rhs
225
+ * nullable = null
201
226
* target
202
227
* } else target
203
228
* }
204
229
*/
205
- def mkDefNonThreadSafeNonNullable (target : Symbol , rhs : Tree )(implicit ctx : Context ) = {
230
+ def mkDefNonThreadSafeNonNullable (target : Symbol , rhs : Tree , nullables : List [ Symbol ] )(implicit ctx : Context ) = {
206
231
val cond = ref(target).select(nme.eq).appliedTo(Literal (Constant (null )))
207
232
val exp = ref(target)
208
233
val setTarget = exp.becomes(rhs)
209
- val init = Block (List (setTarget), exp)
234
+ val setNullables = nullOut(nullables)
235
+ val init = Block (setTarget :: setNullables, exp)
210
236
If (cond, init, exp)
211
237
}
212
238
@@ -222,14 +248,14 @@ class LazyVals extends MiniPhase with IdentityDenotTransformer {
222
248
223
249
val containerTree = ValDef (containerSymbol, defaultValue(tpe))
224
250
if (x.tpe.isNotNull && tpe <:< defn.ObjectType ) { // can use 'null' value instead of flag
225
- val slowPath = DefDef (x.symbol.asTerm, mkDefNonThreadSafeNonNullable(containerSymbol, x.rhs))
251
+ val slowPath = DefDef (x.symbol.asTerm, mkDefNonThreadSafeNonNullable(containerSymbol, x.rhs, nullableFor(x.symbol) ))
226
252
Thicket (containerTree, slowPath)
227
253
}
228
254
else {
229
255
val flagName = LazyBitMapName .fresh(x.name.asTermName)
230
256
val flagSymbol = ctx.newSymbol(x.symbol.owner, flagName, containerFlags | Flags .Private , defn.BooleanType ).enteredAfter(this )
231
257
val flag = ValDef (flagSymbol, Literal (Constants .Constant (false )))
232
- val slowPath = DefDef (x.symbol.asTerm, mkNonThreadSafeDef(ref(containerSymbol), ref(flagSymbol), x.rhs))
258
+ val slowPath = DefDef (x.symbol.asTerm, mkNonThreadSafeDef(ref(containerSymbol), ref(flagSymbol), x.rhs, nullableFor(x.symbol) ))
233
259
Thicket (containerTree, flag, slowPath)
234
260
}
235
261
}
@@ -263,10 +289,23 @@ class LazyVals extends MiniPhase with IdentityDenotTransformer {
263
289
* result = $target
264
290
* }
265
291
* }
292
+ * nullable = null
266
293
* result
267
294
* }
268
295
*/
269
- def mkThreadSafeDef (methodSymbol : TermSymbol , claz : ClassSymbol , ord : Int , target : Symbol , rhs : Tree , tp : Types .Type , offset : Tree , getFlag : Tree , stateMask : Tree , casFlag : Tree , setFlagState : Tree , waitOnLock : Tree )(implicit ctx : Context ) = {
296
+ def mkThreadSafeDef (methodSymbol : TermSymbol ,
297
+ claz : ClassSymbol ,
298
+ ord : Int ,
299
+ target : Symbol ,
300
+ rhs : Tree ,
301
+ tp : Types .Type ,
302
+ offset : Tree ,
303
+ getFlag : Tree ,
304
+ stateMask : Tree ,
305
+ casFlag : Tree ,
306
+ setFlagState : Tree ,
307
+ waitOnLock : Tree ,
308
+ nullables : List [Symbol ])(implicit ctx : Context ) = {
270
309
val initState = Literal (Constants .Constant (0 ))
271
310
val computeState = Literal (Constants .Constant (1 ))
272
311
val notifyState = Literal (Constants .Constant (2 ))
@@ -330,7 +369,8 @@ class LazyVals extends MiniPhase with IdentityDenotTransformer {
330
369
331
370
val whileBody = List (ref(flagSymbol).becomes(getFlag.appliedTo(thiz, offset)), cases)
332
371
val cycle = WhileDo (methodSymbol, whileCond, whileBody)
333
- DefDef (methodSymbol, Block (resultDef :: retryDef :: flagDef :: cycle :: Nil , ref(resultSymbol)))
372
+ val setNullables = nullOut(nullables)
373
+ DefDef (methodSymbol, Block (resultDef :: retryDef :: flagDef :: cycle :: setNullables, ref(resultSymbol)))
334
374
}
335
375
336
376
def transformMemberDefVolatile (x : ValOrDefDef )(implicit ctx : Context ) = {
@@ -390,8 +430,9 @@ class LazyVals extends MiniPhase with IdentityDenotTransformer {
390
430
val wait = Select (ref(helperModule), lazyNme.RLazyVals .wait4Notification)
391
431
val state = Select (ref(helperModule), lazyNme.RLazyVals .state)
392
432
val cas = Select (ref(helperModule), lazyNme.RLazyVals .cas)
433
+ val nullables = nullableFor(x.symbol)
393
434
394
- val accessor = mkThreadSafeDef(x.symbol.asTerm, claz, ord, containerSymbol, x.rhs, tpe, offset, getFlag, state, cas, setFlag, wait)
435
+ val accessor = mkThreadSafeDef(x.symbol.asTerm, claz, ord, containerSymbol, x.rhs, tpe, offset, getFlag, state, cas, setFlag, wait, nullables )
395
436
if (flag eq EmptyTree )
396
437
Thicket (containerTree, accessor)
397
438
else Thicket (containerTree, flag, accessor)
0 commit comments