Skip to content

Commit dbdf38f

Browse files
committed
Further clean-up of new lazy vals
1 parent ab52310 commit dbdf38f

File tree

10 files changed

+37
-91
lines changed

10 files changed

+37
-91
lines changed

Diff for: compiler/src/dotty/tools/dotc/core/Definitions.scala

+5-18
Original file line numberDiff line numberDiff line change
@@ -2,33 +2,20 @@ package dotty.tools
22
package dotc
33
package core
44

5-
import scala.annotation.threadUnsafe as tu
6-
import Types.*
7-
import Contexts.*
8-
import Symbols.*
9-
import SymDenotations.*
10-
import StdNames.*
11-
import Names.*
12-
import Phases.*
13-
import Flags.*
14-
import Scopes.*
15-
import Decorators.*
16-
import NameOps.*
17-
import Periods.*
18-
import NullOpsDecorator.*
5+
import scala.annotation.{threadUnsafe => tu}
6+
import Types._, Contexts._, Symbols._, SymDenotations._, StdNames._, Names._, Phases._
7+
import Flags._, Scopes._, Decorators._, NameOps._, Periods._, NullOpsDecorator._
198
import unpickleScala2.Scala2Unpickler.ensureConstructor
20-
219
import scala.collection.mutable
2210
import collection.mutable
2311
import Denotations.{SingleDenotation, staticRef}
24-
import util.{NoSource, SimpleIdentityMap, SourceFile}
12+
import util.{SimpleIdentityMap, SourceFile, NoSource}
2513
import typer.ImportInfo.RootRef
2614
import Comments.CommentsContext
2715
import Comments.Comment
2816
import util.Spans.NoSpan
2917
import Symbols.requiredModuleRef
30-
import cc.{CaptureSet, CapturingType, EventuallyCapturingType}
31-
import dotty.tools.dotc.transform.LazyVals.lazyNme
18+
import cc.{CapturingType, CaptureSet, EventuallyCapturingType}
3219

3320
import scala.annotation.tailrec
3421

Diff for: compiler/src/dotty/tools/dotc/transform/LazyVals.scala

+26-55
Original file line numberDiff line numberDiff line change
@@ -104,8 +104,8 @@ class LazyVals extends MiniPhase with IdentityDenotTransformer {
104104
}
105105
}
106106

107-
/**
108-
* Append offset fields to companion objects.
107+
108+
/** Append offset fields to companion objects
109109
*/
110110
override def transformTemplate(template: Template)(using Context): Tree = {
111111
val cls = ctx.owner.asClass
@@ -122,10 +122,10 @@ class LazyVals extends MiniPhase with IdentityDenotTransformer {
122122
case _ => prefix ::: stats
123123
}
124124

125-
/**
126-
* Make an eager val that would implement synthetic module.
127-
* Eager val ensures thread safety and has less code generated.
128-
*/
125+
/** Make an eager val that would implement synthetic module.
126+
* Eager val ensures thread safety and has less code generated.
127+
*
128+
*/
129129
def transformSyntheticModule(tree: ValOrDefDef)(using Context): Thicket = {
130130
val sym = tree.symbol
131131
val holderSymbol = newSymbol(sym.owner, LazyLocalName.fresh(sym.asTerm.name),
@@ -135,8 +135,7 @@ class LazyVals extends MiniPhase with IdentityDenotTransformer {
135135
Thicket(field, getter)
136136
}
137137

138-
/**
139-
* Desugar a local `lazy val x: Int = <RHS>` into:
138+
/** Desugar a local `lazy val x: Int = <RHS>` into:
140139
*
141140
* ```
142141
* val x$lzy = new scala.runtime.LazyInt()
@@ -276,75 +275,47 @@ class LazyVals extends MiniPhase with IdentityDenotTransformer {
276275
}
277276

278277
/**
279-
* Create a threadsafe lazy accessor equivalent to the following code:
278+
* Create a threadsafe lazy accessor and function that computes the field's value. `Evaluating` and
279+
* `NullValue` are represented by `object`s and `Waiting` by a class that allows awaiting the completion
280+
* of the evaluation. Note that since tail-recursive functions are transformed *before* lazy-vals,
281+
* this implementation does involve explicit while loop. `PatternMatcher` is coming before `LazyVals`,
282+
* therefore the pattern matching is implemented using if-s.
283+
*
280284
* ```
281285
* private @volatile var _x: AnyRef = null
282-
* @tailrec def x: A =
283-
* _x match
284-
* case current: A =>
285-
* current
286-
* case NullValue => null
287-
* case null =>
288-
* if CAS(_x, null, Evaluating) then
289-
* var result: AnyRef = null // here, we need `AnyRef` to possibly assign `NullValue`
290-
* try
291-
* result = rhs
292-
* nullable = null // if the field is nullable; see `CollectNullableFields`
293-
* finally
294-
* if result == null then result = NullValue // drop if A is non-nullable
295-
* if !CAS(_x, Evaluating, result) then
296-
* val lock = _x.asInstanceOf[Waiting]
297-
* CAS(_x, lock, result)
298-
* lock.release()
299-
* x
300-
* case Evaluating =>
301-
* CAS(_x, Evaluating, new Waiting)
302-
* x
303-
* case current: Waiting =>
304-
* current.awaitRelease()
305-
* x
306-
* ```
307-
* Where `Evaluating` and `NullValue` are represented by `object`s and `Waiting` by a class that
308-
* allows awaiting the completion of the evaluation. Note that since tail-recursive
309-
* functions are transformed *before* lazy-vals, this implementation directly implements
310-
* the resulting loop. `PatternMatcher` coming before `LazyVals`, the pattern matching block
311-
* is implemented using if-s. Additionally, the code can be optimized to be better inlined by
312-
* introducing separate function for computing the lazy value and awaiting it. That is:
313-
*
314-
* ```
315-
* private var _x: AnyRef = null
316286
*
317287
* def x: A =
318-
* if !(_x == null || _x.isInstanceOf[LazyValControlState]) then
319-
* return _x.asInstanceOf[A]
320-
* else if _x == NullValue then
321-
* return null
288+
* val result = _x
289+
* if result.isInstanceOf[A] then
290+
* result // possible unboxing applied here
291+
* else if result.eq(NullValue) then
292+
* null // possible unboxing applied here
322293
* else
323-
* return x_compute()
294+
* return x_compute() // possible unboxing applied here
324295
*
325296
* private def x_compute() =
326297
* while <EmptyTree> do
327298
* val current: AnyRef = _x
328-
* if current == null then
299+
* if current.eq(null) then
329300
* if CAS(_x, null, Evaluating) then
330301
* var resultNullable: AnyRef = null
331302
* var result: AnyRef = null
332303
* try
333304
* resultNullable = rhs
334-
* nullable = null
335-
* if resultNullable == null then
305+
* nullable = null // nulls out the nullable fields used only in initialization
306+
* if resultNullable.eq(null) then
336307
* result = NullValue
337308
* else
338309
* result = resultNullable
339-
* return result
340310
* finally
341311
* if !CAS(_x, Evaluating, result) then
342312
* val lock = _x.asInstanceOf[Waiting]
343313
* CAS(_x, lock, result)
344314
* lock.release()
315+
* return resultNullable
345316
* else
346317
* if current.isInstanceOf[LazyValControlState] then
347-
* if current.isInstanceOf[Evaluating] then // To avoid creating Waiting instance
318+
* if current.eq(Evaluating) then // To avoid creating Waiting instance
348319
* CAS(current, Evaluating, new Waiting)
349320
* else if current.isInstanceOf[Waiting] then
350321
* current.asInstanceOf[Waiting].await()
@@ -353,7 +324,7 @@ class LazyVals extends MiniPhase with IdentityDenotTransformer {
353324
* return current.asInstanceOf[A]
354325
* end while
355326
* * ```
356-
*
327+
*
357328
* @param memberDef the transformed lazy field member definition
358329
* @param claz the class containing this lazy val field
359330
* @param target the target synthetic field
@@ -469,7 +440,7 @@ class LazyVals extends MiniPhase with IdentityDenotTransformer {
469440
Return(ref(current), lazyInitMethodSymbol)
470441
)
471442

472-
val initBody = Block(ValDef(current, ref(target)) :: Nil, If(ref(current).equal(nullLiteral), initialize, ifNotUninitialized).withType(defn.UnitType))
443+
val initBody = Block(ValDef(current, ref(target)) :: Nil, If(ref(current).select(defn.Object_eq).appliedTo(nullLiteral), initialize, ifNotUninitialized).withType(defn.UnitType))
473444
val initMainLoop = WhileDo(EmptyTree, initBody) // becomes: while (true) do { body }
474445
val initMethodDef = DefDef(lazyInitMethodSymbol, initMainLoop)
475446
(accessorDef, initMethodDef)

Diff for: compiler/test/dotc/pos-lazy-vals-tests.allowlist

+1-1
Original file line numberDiff line numberDiff line change
@@ -34,4 +34,4 @@ t6278-synth-def.scala
3434
t6925b.scala
3535
t7011.scala
3636
t8306.scala
37-
zipped.scala
37+
zipped.scala

Diff for: compiler/test/dotc/run-lazy-vals-tests.allowlist

+1-1
Original file line numberDiff line numberDiff line change
@@ -63,4 +63,4 @@ t7406.scala
6363
t8245.scala
6464
unapply.scala
6565
unit-lazy-val.scala
66-
view-iterator-stream.scala
66+
view-iterator-stream.scala

Diff for: library/src/scala/runtime/LazyVals.scala

-1
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,5 @@ object LazyVals {
169169
final val wait4Notification = "wait4Notification"
170170
final val get = "get"
171171
final val getOffset = "getOffset"
172-
final val getOffsetStatic = "getOffsetStatic"
173172
}
174173
}

Diff for: project/MiMaFilters.scala

-11
Original file line numberDiff line numberDiff line change
@@ -3,20 +3,9 @@ import com.typesafe.tools.mima.core._
33

44
object MiMaFilters {
55
val Library: Seq[ProblemFilter] = Seq(
6-
ProblemFilters.exclude[DirectMissingMethodProblem]("scala.runtime.LazyVals.getStaticOffset"),
7-
ProblemFilters.exclude[DirectMissingMethodProblem]("scala.runtime.LazyVals#Names.getOffsetStatic"),
86
ProblemFilters.exclude[DirectMissingMethodProblem]("scala.runtime.LazyVals.getStaticFieldOffset"),
9-
ProblemFilters.exclude[DirectMissingMethodProblem]("scala.runtime.LazyVals#Names.getStaticFieldOffset"),
107
ProblemFilters.exclude[DirectMissingMethodProblem]("scala.runtime.LazyVals.objCAS"),
11-
ProblemFilters.exclude[DirectMissingMethodProblem]("scala.runtime.LazyVals#Names.evaluating"),
12-
ProblemFilters.exclude[DirectMissingMethodProblem]("scala.runtime.LazyVals#Names.getStaticOffset"),
13-
ProblemFilters.exclude[DirectMissingMethodProblem]("scala.runtime.LazyVals#Names.nullValue"),
14-
ProblemFilters.exclude[DirectMissingMethodProblem]("scala.runtime.LazyVals#Names.objCas"),
15-
ProblemFilters.exclude[DirectMissingMethodProblem]("scala.runtime.LazyVals#Names.waiting"),
16-
ProblemFilters.exclude[DirectMissingMethodProblem]("scala.runtime.LazyVals#Names.waitingAwaitRelease"),
17-
ProblemFilters.exclude[DirectMissingMethodProblem]("scala.runtime.LazyVals#Names.waitingRelease"),
188
ProblemFilters.exclude[MissingClassProblem]("scala.runtime.LazyVals$LazyValControlState"),
19-
ProblemFilters.exclude[DirectMissingMethodProblem]("scala.runtime.LazyVals#Names.controlState"),
209
ProblemFilters.exclude[MissingClassProblem]("scala.runtime.LazyVals$Evaluating$"),
2110
ProblemFilters.exclude[MissingClassProblem]("scala.runtime.LazyVals$NullValue$"),
2211
ProblemFilters.exclude[MissingClassProblem]("scala.runtime.LazyVals$Waiting"),

Diff for: tests/printing/transformed/lazy-vals-legacy.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
object A {
22
lazy val x: Int = 2
3-
}
3+
}

Diff for: tests/printing/transformed/lazy-vals-new.check

+1-1
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ package <empty> {
3535
while <empty> do
3636
{
3737
val current: Object = A#x$lzy1
38-
if current.==(null) then
38+
if current.eq(null) then
3939
if
4040
scala.runtime.LazyVals.objCAS(classOf[A], A.OFFSET$_m_0, null,
4141
scala.runtime.LazyVals.Evaluating

Diff for: tests/printing/transformed/lazy-vals-new.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
object A {
22
lazy val x: Int = 2
3-
}
3+
}

Diff for: tests/run/lazyVals_c3.1.0.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,4 @@ class Foo:
1010
@main def Test =
1111
val foo = new Foo
1212
println(foo.x)
13-
println(foo.y)
13+
println(foo.y)

0 commit comments

Comments
 (0)