Skip to content

Commit a759714

Browse files
committed
Merge pull request #346 from dotty-staging/local-lazy-vals
Bring back lazy vals. Changed encoding scheme of local(non-field) lazy vals
2 parents 3ce904f + 76ce7c7 commit a759714

File tree

6 files changed

+98
-53
lines changed

6 files changed

+98
-53
lines changed

src/dotty/runtime/LazyHolders.scala

Lines changed: 24 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -3,38 +3,42 @@ package dotty.runtime
33
/**
44
* Classes used as holders for local lazy vals
55
*/
6-
class LazyInt(init: => Int) {
7-
lazy val value = init
6+
class LazyInt {
7+
var value: Int = _
8+
@volatile var initialized: Boolean = false
89
}
910

10-
class LazyLong(init: => Long) {
11-
lazy val value = init
11+
class LazyLong {
12+
var value: Long = _
13+
@volatile var initialized: Boolean = false
1214
}
1315

14-
class LazyBoolean(init: => Boolean) {
15-
lazy val value = init
16+
class LazyBoolean {
17+
var value: Boolean = _
18+
@volatile var initialized: Boolean = false
1619
}
1720

18-
class LazyDouble(init: => Double) {
19-
lazy val value = init
21+
class LazyDouble {
22+
var value: Double = _
23+
@volatile var initialized: Boolean = false
2024
}
2125

22-
class LazyFloat(init: => Float) {
23-
lazy val value = init
26+
class LazyByte {
27+
var value: Byte = _
28+
@volatile var initialized: Boolean = false
2429
}
2530

26-
class LazyByte(init: => Byte) {
27-
lazy val value = init
31+
class LazyRef {
32+
var value: AnyRef = _
33+
@volatile var initialized: Boolean = false
2834
}
2935

30-
class LazyRef(init: => AnyRef) {
31-
lazy val value = init
36+
class LazyShort {
37+
var value: Short = _
38+
@volatile var initialized: Boolean = false
3239
}
3340

34-
class LazyShort(init: => Short) {
35-
lazy val value = init
36-
}
37-
38-
class LazyChar(init: => Char) {
39-
lazy val value = init
41+
class LazyChar {
42+
var value: Char = _
43+
@volatile var initialized: Boolean = false
4044
}

src/dotty/tools/dotc/Compiler.scala

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,15 +52,16 @@ class Compiler {
5252
List(new PatternMatcher,
5353
new ExplicitOuter,
5454
new Splitter),
55-
List(new ElimByName,
55+
List(new LazyVals,
56+
new ElimByName,
5657
new SeqLiterals,
5758
new InterceptedMethods,
5859
new Literalize,
5960
new Getters,
6061
new ResolveSuper),
6162
List(new Erasure),
6263
List(new Mixin,
63-
new Memoize, // TODO: Make LazyVals a part of this phase
64+
new Memoize,
6465
new CapturedVars,
6566
new Constructors),
6667
List(new LambdaLift,

src/dotty/tools/dotc/core/StdNames.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,7 @@ object StdNames {
226226
val FAKE_LOCAL_THIS: N = "this$"
227227
val IMPLCLASS_CONSTRUCTOR: N = "$init$"
228228
val LAZY_LOCAL: N = "$lzy"
229+
val LAZY_LOCAL_INIT: N = "$lzyINIT"
229230
val LAZY_FIELD_OFFSET: N = "OFFSET$"
230231
val LAZY_SLOW_SUFFIX: N = "$lzycompute"
231232
val LOCAL_SUFFIX: N = "$$local"

src/dotty/tools/dotc/transform/Getters.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ class Getters extends MiniPhaseTransform with SymTransformer { thisTransform =>
5858
}
5959
else d
6060
}
61-
private val NoGetterNeeded = Method | Param | JavaDefined | JavaStatic
61+
private val NoGetterNeeded = Method | Param | JavaDefined | JavaStatic | Lazy
6262

6363
override def transformValDef(tree: ValDef)(implicit ctx: Context, info: TransformerInfo): Tree =
6464
if (tree.symbol is Method) DefDef(tree.symbol.asTerm, tree.rhs) else tree

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

Lines changed: 53 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -12,23 +12,34 @@ import dotty.tools.dotc.transform.TreeTransforms.{TransformerInfo, TreeTransform
1212
import dotty.tools.dotc.ast.Trees._
1313
import dotty.tools.dotc.ast.{untpd, tpd}
1414
import dotty.tools.dotc.core.Constants.Constant
15-
import dotty.tools.dotc.core.Types.MethodType
15+
import dotty.tools.dotc.core.Types.{ExprType, NoType, MethodType}
1616
import dotty.tools.dotc.core.Names.Name
17-
import dotty.runtime.LazyVals
17+
import dotty.runtime.{LazyVals => RLazyVals} // dotty deviation
1818
import SymUtils._
1919
import scala.collection.mutable.ListBuffer
2020
import dotty.tools.dotc.core.Denotations.SingleDenotation
2121
import dotty.tools.dotc.core.SymDenotations.SymDenotation
22-
import dotty.tools.dotc.core.DenotTransformers.{IdentityDenotTransformer, DenotTransformer}
22+
import dotty.tools.dotc.core.DenotTransformers.{SymTransformer, IdentityDenotTransformer, DenotTransformer}
2323

24-
class LazyValsTransform extends MiniPhaseTransform with IdentityDenotTransformer {
24+
class LazyVals extends MiniPhaseTransform with SymTransformer {
2525

2626
import tpd._
2727

2828

29-
def transformer = new LazyValsTransform
29+
def transformSym(d: SymDenotation)(implicit ctx: Context): SymDenotation = {
30+
if(d is(Flags.Lazy, butNot = Flags.ModuleVal | Flags.Method)) {
31+
// Method flag is set on lazy vals coming from Unpickler. They are already methods and shouldn't be transformed twice
32+
d.copySymDenotation(
33+
initFlags = d.flags | Flags.Method,
34+
info = ExprType(d.info))
35+
}
36+
else d
37+
}
38+
39+
def transformer = new LazyVals
3040

31-
val containerFlags = Flags.Synthetic | Flags.Mutable
41+
val containerFlags = Flags.Synthetic | Flags.Mutable | Flags.Lazy
42+
val initFlags = Flags.Synthetic | Flags.Method
3243

3344
/** this map contains mutable state of transformation: OffsetDefs to be appended to companion object definitions,
3445
* and number of bits currently used */
@@ -72,9 +83,9 @@ class LazyValsTransform extends MiniPhaseTransform with IdentityDenotTransformer
7283
* dotty.runtime(eg dotty.runtime.LazyInt)
7384
*/
7485
def transformLocalValDef(x: ValDef)(implicit ctx: Context) = x match {
75-
case x@ValDef(name, tpt, rhs) =>
76-
val valueInitter = rhs
86+
case x@ValDef(name, tpt, valueInitter) =>
7787
val holderName = ctx.freshName(name.toString + StdNames.nme.LAZY_LOCAL).toTermName
88+
val initName = ctx.freshName(name.toString + StdNames.nme.LAZY_LOCAL_INIT).toTermName
7889
val tpe = x.tpe.widen
7990

8091
val holderType =
@@ -88,21 +99,35 @@ class LazyValsTransform extends MiniPhaseTransform with IdentityDenotTransformer
8899
else if (tpe isRef defn.ShortClass) "LazyShort"
89100
else "LazyRef"
90101

102+
91103
val holderImpl = ctx.requiredClass("dotty.runtime." + holderType)
92104

93-
val holderSymbol = ctx.newSymbol(x.symbol.owner, holderName, containerFlags, holderImpl.typeRef, coord = x.symbol.coord)
94-
val holderTree = ValDef(holderSymbol, New(holderImpl.typeRef, List(valueInitter.changeOwner(x.symbol, holderSymbol))))
105+
val holderSymbol = ctx.newSymbol(x.symbol.owner, holderName, containerFlags, holderImpl.typeRef, coord = x.pos)
106+
val initSymbol = ctx.newSymbol(x.symbol.owner, initName, initFlags, MethodType(Nil, tpe), coord = x.pos)
107+
val result = ref(holderSymbol).select("value".toTermName)
108+
val flag = ref(holderSymbol).select("initialized".toTermName)
109+
val initer = valueInitter.changeOwner(x.symbol, initSymbol)
110+
val initBody =
111+
ref(holderSymbol).select(defn.Object_synchronized).appliedToType(tpe).appliedTo(
112+
mkNonThreadSafeDef(result, flag, initer).ensureConforms(tpe))
113+
val initTree = DefDef(initSymbol, initBody)
114+
val holderTree = ValDef(holderSymbol, New(holderImpl.typeRef, List()))
95115
val methodBody = {
96-
val prefix = ref(holderSymbol).select("value".toTermName)
97-
if (holderType != "LazyRef") prefix
98-
else prefix.select(defn.Any_asInstanceOf).appliedToType(tpe)
116+
tpd.If(flag, EmptyTree, ref(initSymbol))
117+
result.ensureConforms(tpe)
99118
}
100119
val methodTree = DefDef(x.symbol.asTerm, methodBody)
101120
ctx.debuglog(s"found a lazy val ${x.show},\n rewrote with ${holderTree.show}")
102-
Thicket(holderTree, methodTree)
121+
Thicket(holderTree, initTree, methodTree)
103122
}
104123

105-
/** Create non-threadsafe lazy accessor equivalent to such code
124+
125+
override def transformStats(trees: List[tpd.Tree])(implicit ctx: Context, info: TransformerInfo): List[tpd.Tree] = {
126+
val (holders, stats) = trees.partition { _.symbol.flags == containerFlags}
127+
holders:::stats
128+
}
129+
130+
/** Create non-threadsafe lazy accessor equivalent to such code
106131
* def methodSymbol() = {
107132
* if (flag) target
108133
* else {
@@ -113,13 +138,11 @@ class LazyValsTransform extends MiniPhaseTransform with IdentityDenotTransformer
113138
* }
114139
*/
115140

116-
def mkNonThreadSafeDef(target: Symbol, flag: Symbol, rhs: Tree)(implicit ctx: Context) = {
117-
val cond = ref(flag)
118-
val exp = ref(target)
119-
val setFlag = Assign(cond, Literal(Constants.Constant(true)))
120-
val setTarget = Assign(exp, rhs)
121-
val init = Block(List(setFlag, setTarget), exp)
122-
If(cond, exp, init)
141+
def mkNonThreadSafeDef(target: Tree, flag: Tree, rhs: Tree)(implicit ctx: Context) = {
142+
val setFlag = Assign(flag, Literal(Constants.Constant(true)))
143+
val setTarget = Assign(target, rhs)
144+
val init = Block(List(setFlag, setTarget), target)
145+
If(flag, target, init)
123146
}
124147

125148
/** Create non-threadsafe lazy accessor for not-nullable types equivalent to such code
@@ -155,7 +178,7 @@ class LazyValsTransform extends MiniPhaseTransform with IdentityDenotTransformer
155178
val flagName = ctx.freshName(name.toString + StdNames.nme.BITMAP_PREFIX).toTermName
156179
val flagSymbol = ctx.newSymbol(x.symbol.owner, flagName, containerFlags, defn.BooleanType)
157180
val flag = ValDef(flagSymbol, Literal(Constants.Constant(false)))
158-
val slowPath = DefDef(x.symbol.asTerm, mkNonThreadSafeDef(containerSymbol, flagSymbol, rhs))
181+
val slowPath = DefDef(x.symbol.asTerm, mkNonThreadSafeDef(ref(containerSymbol), ref(flagSymbol), rhs))
159182
Thicket(List(containerTree, flag, slowPath))
160183
}
161184

@@ -266,15 +289,15 @@ class LazyValsTransform extends MiniPhaseTransform with IdentityDenotTransformer
266289
val thiz = This(claz)(ctx.fresh.setOwner(claz))
267290
val companion = claz.companionModule
268291
val helperModule = ctx.requiredModule("dotty.runtime.LazyVals")
269-
val getOffset = Select(ref(helperModule), LazyVals.Names.getOffset.toTermName)
292+
val getOffset = Select(ref(helperModule), RLazyVals.Names.getOffset.toTermName)
270293
var offsetSymbol: TermSymbol = null
271294
var flag: Tree = EmptyTree
272295
var ord = 0
273296

274297
// compute or create appropriate offsetSymol, bitmap and bits used by current ValDef
275298
appendOffsetDefs.get(companion.name.moduleClassName) match {
276299
case Some(info) =>
277-
val flagsPerLong = 64 / LazyVals.BITS_PER_LAZY_VAL
300+
val flagsPerLong = 64 / RLazyVals.BITS_PER_LAZY_VAL
278301
info.ord += 1
279302
ord = info.ord % flagsPerLong
280303
val id = info.ord / flagsPerLong
@@ -305,11 +328,11 @@ class LazyValsTransform extends MiniPhaseTransform with IdentityDenotTransformer
305328
val containerTree = ValDef(containerSymbol, initValue(tpe))
306329

307330
val offset = Select(ref(companion), offsetSymbol.name)
308-
val getFlag = Select(ref(helperModule), LazyVals.Names.get.toTermName)
309-
val setFlag = Select(ref(helperModule), LazyVals.Names.setFlag.toTermName)
310-
val wait = Select(ref(helperModule), LazyVals.Names.wait4Notification.toTermName)
311-
val state = Select(ref(helperModule), LazyVals.Names.state.toTermName)
312-
val cas = Select(ref(helperModule), LazyVals.Names.cas.toTermName)
331+
val getFlag = Select(ref(helperModule), RLazyVals.Names.get.toTermName)
332+
val setFlag = Select(ref(helperModule), RLazyVals.Names.setFlag.toTermName)
333+
val wait = Select(ref(helperModule), RLazyVals.Names.wait4Notification.toTermName)
334+
val state = Select(ref(helperModule), RLazyVals.Names.state.toTermName)
335+
val cas = Select(ref(helperModule), RLazyVals.Names.cas.toTermName)
313336

314337
val accessor = mkThreadSafeDef(x.symbol.asTerm, claz, ord, containerSymbol, rhs, tpe, offset, getFlag, state, cas, setFlag, wait)
315338
if(flag eq EmptyTree)

tests/pos/lazyValsSepComp.scala

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package dotty.tools
2+
package io
3+
4+
import java.io.{ InputStream }
5+
import java.util.jar.JarEntry
6+
import dotty.tools.dotc.core.Definitions
7+
import language.postfixOps
8+
import dotty.tools.dotc.core.Contexts._
9+
10+
11+
/** A test to trigger issue with separate compilation between Dotty and Scalac and lazy vals */
12+
object Foo {
13+
val definitions: Definitions = null
14+
def defn = definitions
15+
def go = defn.FunctionClass(0)
16+
}

0 commit comments

Comments
 (0)