@@ -129,50 +129,58 @@ class LazyVals extends MiniPhase with IdentityDenotTransformer {
129129 Thicket (field, getter)
130130 }
131131
132- /** Replace a local lazy val inside a method,
133- * with a LazyHolder from
134- * dotty.runtime(eg dotty.runtime.LazyInt)
135- */
132+ /** Desugar a local `lazy val x: Int = <RHS>` into:
133+ *
134+ * ```
135+ * val x$lzy = new scala.runtime.LazyInt()
136+ *
137+ * def x$lzycompute(): Int = x$lzy.synchronized {
138+ * if (x$lzy.initialized()) x$lzy.value()
139+ * else x$lzy.initialize(<RHS>)
140+ * // for a Unit-typed lazy val, this becomes `{ rhs ; x$lzy.initialize() }`
141+ * // to avoid passing around BoxedUnit
142+ * }
143+ *
144+ * def x(): Int = if (x$lzy.initialized()) x$lzy.value() else x$lzycompute()
145+ * ```
146+ *
147+ * TODO: Implement Unit-typed lazy val optimization described above
148+ */
136149 def transformLocalDef (x : ValOrDefDef )(implicit ctx : Context ): Thicket = {
137- val valueInitter = x.rhs
138- val xname = x.name.asTermName
139- val holderName = LazyLocalName .fresh(xname)
140- val initName = LazyLocalInitName .fresh(xname)
141- val tpe = x.tpe.widen.resultType.widen
142-
143- val holderType =
144- if (tpe isRef defn.IntClass ) " LazyInt"
145- else if (tpe isRef defn.LongClass ) " LazyLong"
146- else if (tpe isRef defn.BooleanClass ) " LazyBoolean"
147- else if (tpe isRef defn.FloatClass ) " LazyFloat"
148- else if (tpe isRef defn.DoubleClass ) " LazyDouble"
149- else if (tpe isRef defn.ByteClass ) " LazyByte"
150- else if (tpe isRef defn.CharClass ) " LazyChar"
151- else if (tpe isRef defn.ShortClass ) " LazyShort"
152- else " LazyRef"
153-
154-
155- val holderImpl = ctx.requiredClass(" dotty.runtime." + holderType)
156-
157- val holderSymbol = ctx.newSymbol(x.symbol.owner, holderName, containerFlags, holderImpl.typeRef, coord = x.pos)
158- val initSymbol = ctx.newSymbol(x.symbol.owner, initName, initFlags, MethodType (Nil , tpe), coord = x.pos)
159- val result = ref(holderSymbol).select(lazyNme.value).withPos(x.pos)
160- val flag = ref(holderSymbol).select(lazyNme.initialized)
161- val initer = valueInitter.changeOwnerAfter(x.symbol, initSymbol, this )
162- val initBody =
163- adaptToType(
164- ref(holderSymbol).select(defn.Object_synchronized ).appliedTo(
165- adaptToType(mkNonThreadSafeDef(result, flag, initer, nullables = Nil ), defn.ObjectType )),
166- tpe)
167- val initTree = DefDef (initSymbol, initBody)
168- val holderTree = ValDef (holderSymbol, New (holderImpl.typeRef, List ()))
169- val methodBody = tpd.If (flag.ensureApplied,
170- result.ensureApplied,
171- ref(initSymbol).ensureApplied).ensureConforms(tpe)
172-
173- val methodTree = DefDef (x.symbol.asTerm, methodBody)
174- ctx.debuglog(s " found a lazy val ${x.show}, \n rewrote with ${holderTree.show}" )
175- Thicket (holderTree, initTree, methodTree)
150+ val xname = x.name.asTermName
151+ val holderName = LazyLocalName .fresh(xname)
152+ val initName = LazyLocalInitName .fresh(xname)
153+ val tpe = x.tpe.widen.resultType.widen
154+
155+ // val x$lzy = new scala.runtime.LazyInt()
156+ val holderImpl = defn.LazyHolder ()(ctx)(tpe.typeSymbol)
157+ val holderSymbol = ctx.newSymbol(x.symbol.owner, holderName, containerFlags, holderImpl.typeRef, coord = x.pos)
158+ val holderTree = ValDef (holderSymbol, New (holderImpl.typeRef, List ()))
159+
160+ val holderRef = ref(holderSymbol)
161+ val getValue = holderRef.select(lazyNme.value).ensureApplied.withPos(x.pos)
162+ val initialized = holderRef.select(lazyNme.initialized).ensureApplied
163+
164+ // def x$lzycompute(): Int = x$lzy.synchronized {
165+ // if (x$lzy.initialized()) x$lzy.value()
166+ // else x$lzy.initialize(<RHS>)
167+ // }
168+ val initSymbol = ctx.newSymbol(x.symbol.owner, initName, initFlags, MethodType (Nil , tpe), coord = x.pos)
169+ val rhs = x.rhs.changeOwnerAfter(x.symbol, initSymbol, this )
170+ val initialize = holderRef.select(lazyNme.initialize).appliedTo(rhs)
171+ val initBody =
172+ adaptToType(
173+ holderRef.select(defn.Object_synchronized ).appliedTo(
174+ adaptToType(If (initialized, getValue, initialize), defn.ObjectType )),
175+ tpe)
176+ val initTree = DefDef (initSymbol, initBody)
177+
178+ // def x(): Int = if (x$lzy.initialized()) x$lzy.value() else x$lzycompute()
179+ val accessorBody = If (initialized, getValue, ref(initSymbol).ensureApplied).ensureConforms(tpe)
180+ val accessor = DefDef (x.symbol.asTerm, accessorBody)
181+
182+ ctx.debuglog(s " found a lazy val ${x.show}, \n rewrote with ${holderTree.show}" )
183+ Thicket (holderTree, initTree, accessor)
176184 }
177185
178186
@@ -458,6 +466,7 @@ object LazyVals {
458466 val result : TermName = " result" .toTermName
459467 val value : TermName = " value" .toTermName
460468 val initialized : TermName = " initialized" .toTermName
469+ val initialize : TermName = " initialize" .toTermName
461470 val retry : TermName = " retry" .toTermName
462471 }
463472}
0 commit comments