@@ -129,50 +129,69 @@ 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 holderType =
156+ if (tpe isRef defn.IntClass ) " LazyInt"
157+ else if (tpe isRef defn.LongClass ) " LazyLong"
158+ else if (tpe isRef defn.BooleanClass ) " LazyBoolean"
159+ else if (tpe isRef defn.FloatClass ) " LazyFloat"
160+ else if (tpe isRef defn.DoubleClass ) " LazyDouble"
161+ else if (tpe isRef defn.ByteClass ) " LazyByte"
162+ else if (tpe isRef defn.CharClass ) " LazyChar"
163+ else if (tpe isRef defn.ShortClass ) " LazyShort"
164+ else " LazyRef"
165+
166+ // val x$lzy = new scala.runtime.LazyInt()
167+ val holderImpl = ctx.requiredClass(" scala.runtime." + holderType)
168+ val holderSymbol = ctx.newSymbol(x.symbol.owner, holderName, containerFlags, holderImpl.typeRef, coord = x.pos)
169+ val holderTree = ValDef (holderSymbol, New (holderImpl.typeRef, List ()))
170+
171+ val holderRef = ref(holderSymbol)
172+ val getValue = holderRef.select(lazyNme.value).ensureApplied.withPos(x.pos)
173+ val initialized = holderRef.select(lazyNme.initialized).ensureApplied
174+
175+ // def x$lzycompute(): Int = x$lzy.synchronized {
176+ // if (x$lzy.initialized()) x$lzy.value()
177+ // else x$lzy.initialize(<RHS>)
178+ // }
179+ val initSymbol = ctx.newSymbol(x.symbol.owner, initName, initFlags, MethodType (Nil , tpe), coord = x.pos)
180+ val rhs = x.rhs.changeOwnerAfter(x.symbol, initSymbol, this )
181+ val initialize = holderRef.select(lazyNme.initialize).appliedTo(rhs)
182+ val initBody =
183+ adaptToType(
184+ holderRef.select(defn.Object_synchronized ).appliedTo(
185+ adaptToType(If (initialized, getValue, initialize), defn.ObjectType )),
186+ tpe)
187+ val initTree = DefDef (initSymbol, initBody)
188+
189+ // def x(): Int = if (x$lzy.initialized()) x$lzy.value() else x$lzycompute()
190+ val accessorBody = If (initialized, getValue, ref(initSymbol).ensureApplied).ensureConforms(tpe)
191+ val accessor = DefDef (x.symbol.asTerm, accessorBody)
192+
193+ ctx.debuglog(s " found a lazy val ${x.show}, \n rewrote with ${holderTree.show}" )
194+ Thicket (holderTree, initTree, accessor)
176195 }
177196
178197
@@ -458,6 +477,7 @@ object LazyVals {
458477 val result : TermName = " result" .toTermName
459478 val value : TermName = " value" .toTermName
460479 val initialized : TermName = " initialized" .toTermName
480+ val initialize : TermName = " initialize" .toTermName
461481 val retry : TermName = " retry" .toTermName
462482 }
463483}
0 commit comments