Skip to content

Commit 5b7a1a0

Browse files
committed
Fix #5340: Improve runtime implementation of local lazy vals
Reduce the amount of code generated for local lazy val by moving some of the generated code to the runtime library. This is a port of scala/scala#5374
1 parent 4385494 commit 5b7a1a0

File tree

5 files changed

+100
-89
lines changed

5 files changed

+100
-89
lines changed

compiler/src/dotty/tools/dotc/core/Definitions.scala

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -897,10 +897,25 @@ class Definitions {
897897
// ----- Symbol sets ---------------------------------------------------
898898

899899
lazy val AbstractFunctionType: Array[TypeRef] = mkArityArray("scala.runtime.AbstractFunction", MaxImplementedFunctionArity, 0)
900-
val AbstractFunctionClassPerRun: PerRun[Array[Symbol]] = new PerRun[Array[Symbol]](implicit ctx => AbstractFunctionType.map(_.symbol.asClass))
900+
val AbstractFunctionClassPerRun: PerRun[Array[Symbol]] = new PerRun(implicit ctx => AbstractFunctionType.map(_.symbol.asClass))
901901
def AbstractFunctionClass(n: Int)(implicit ctx: Context): Symbol = AbstractFunctionClassPerRun()(ctx)(n)
902902
private lazy val ImplementedFunctionType = mkArityArray("scala.Function", MaxImplementedFunctionArity, 0)
903-
def FunctionClassPerRun: PerRun[Array[Symbol]] = new PerRun[Array[Symbol]](implicit ctx => ImplementedFunctionType.map(_.symbol.asClass))
903+
def FunctionClassPerRun: PerRun[Array[Symbol]] = new PerRun(implicit ctx => ImplementedFunctionType.map(_.symbol.asClass))
904+
905+
val LazyHolder: PerRun[Map[Symbol, Symbol]] = new PerRun({ implicit ctx =>
906+
def holderImpl(holderType: String) = ctx.requiredClass("scala.runtime." + holderType)
907+
Map[Symbol, Symbol](
908+
IntClass -> holderImpl("LazyInt"),
909+
LongClass -> holderImpl("LazyLong"),
910+
BooleanClass -> holderImpl("LazyBoolean"),
911+
FloatClass -> holderImpl("LazyFloat"),
912+
DoubleClass -> holderImpl("LazyDouble"),
913+
ByteClass -> holderImpl("LazyByte"),
914+
CharClass -> holderImpl("LazyChar"),
915+
ShortClass -> holderImpl("LazyShort")
916+
)
917+
.withDefaultValue(holderImpl("LazyRef"))
918+
})
904919

905920
lazy val TupleType: Array[TypeRef] = mkArityArray("scala.Tuple", MaxTupleArity, 1)
906921

compiler/src/dotty/tools/dotc/transform/LazyVals.scala

Lines changed: 52 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -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},\nrewrote 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},\nrewrote 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
}

library/src/dotty/runtime/LazyHolders.scala

Lines changed: 0 additions & 44 deletions
This file was deleted.

tests/run/i5340.check

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
Int
2+
Long
3+
Float
4+
Double
5+
Byte
6+
Char
7+
Short
8+
String

tests/run/i5340.scala

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
object Test {
2+
def main(args: Array[String]): Unit = {
3+
lazy val a: Int = { println("Int"); 1 }
4+
lazy val b: Long = { println("Long"); 1L }
5+
lazy val c: Float = { println("Float"); 1F }
6+
lazy val d: Double = { println("Double"); 1.0 }
7+
lazy val e: Byte = { println("Byte"); 1 }
8+
lazy val f: Char = { println("Char"); '1' }
9+
lazy val g: Short = { println("Short"); 1 }
10+
lazy val h: String = { println("String"); "1" }
11+
lazy val i: Unit = { println("Unit"); () }
12+
13+
a
14+
b
15+
c
16+
d
17+
e
18+
f
19+
g
20+
h
21+
// i // FIXME: See #5350
22+
}
23+
}

0 commit comments

Comments
 (0)