diff options
-rw-r--r-- | src/dotty/runtime/LazyHolders.scala | 44 | ||||
-rw-r--r-- | src/dotty/tools/dotc/Compiler.scala | 5 | ||||
-rw-r--r-- | src/dotty/tools/dotc/core/StdNames.scala | 1 | ||||
-rw-r--r-- | src/dotty/tools/dotc/transform/Getters.scala | 2 | ||||
-rw-r--r-- | src/dotty/tools/dotc/transform/LazyVals.scala | 83 | ||||
-rw-r--r-- | tests/pos/lazyValsSepComp.scala | 16 |
6 files changed, 98 insertions, 53 deletions
diff --git a/src/dotty/runtime/LazyHolders.scala b/src/dotty/runtime/LazyHolders.scala index 86c5d34ec..1e31cda66 100644 --- a/src/dotty/runtime/LazyHolders.scala +++ b/src/dotty/runtime/LazyHolders.scala @@ -3,38 +3,42 @@ package dotty.runtime /** * Classes used as holders for local lazy vals */ -class LazyInt(init: => Int) { - lazy val value = init +class LazyInt { + var value: Int = _ + @volatile var initialized: Boolean = false } -class LazyLong(init: => Long) { - lazy val value = init +class LazyLong { + var value: Long = _ + @volatile var initialized: Boolean = false } -class LazyBoolean(init: => Boolean) { - lazy val value = init +class LazyBoolean { + var value: Boolean = _ + @volatile var initialized: Boolean = false } -class LazyDouble(init: => Double) { - lazy val value = init +class LazyDouble { + var value: Double = _ + @volatile var initialized: Boolean = false } -class LazyFloat(init: => Float) { - lazy val value = init +class LazyByte { + var value: Byte = _ + @volatile var initialized: Boolean = false } -class LazyByte(init: => Byte) { - lazy val value = init +class LazyRef { + var value: AnyRef = _ + @volatile var initialized: Boolean = false } -class LazyRef(init: => AnyRef) { - lazy val value = init +class LazyShort { + var value: Short = _ + @volatile var initialized: Boolean = false } -class LazyShort(init: => Short) { - lazy val value = init -} - -class LazyChar(init: => Char) { - lazy val value = init +class LazyChar { + var value: Char = _ + @volatile var initialized: Boolean = false } diff --git a/src/dotty/tools/dotc/Compiler.scala b/src/dotty/tools/dotc/Compiler.scala index c5c1d8713..721b52b2e 100644 --- a/src/dotty/tools/dotc/Compiler.scala +++ b/src/dotty/tools/dotc/Compiler.scala @@ -52,7 +52,8 @@ class Compiler { List(new PatternMatcher, new ExplicitOuter, new Splitter), - List(new ElimByName, + List(new LazyVals, + new ElimByName, new SeqLiterals, new InterceptedMethods, new Literalize, @@ -60,7 +61,7 @@ class Compiler { new ResolveSuper), List(new Erasure), List(new Mixin, - new Memoize, // TODO: Make LazyVals a part of this phase + new Memoize, new CapturedVars, new Constructors), List(new LambdaLift, diff --git a/src/dotty/tools/dotc/core/StdNames.scala b/src/dotty/tools/dotc/core/StdNames.scala index 382ae47de..b7cc1e03f 100644 --- a/src/dotty/tools/dotc/core/StdNames.scala +++ b/src/dotty/tools/dotc/core/StdNames.scala @@ -226,6 +226,7 @@ object StdNames { val FAKE_LOCAL_THIS: N = "this$" val IMPLCLASS_CONSTRUCTOR: N = "$init$" val LAZY_LOCAL: N = "$lzy" + val LAZY_LOCAL_INIT: N = "$lzyINIT" val LAZY_FIELD_OFFSET: N = "OFFSET$" val LAZY_SLOW_SUFFIX: N = "$lzycompute" val LOCAL_SUFFIX: N = "$$local" diff --git a/src/dotty/tools/dotc/transform/Getters.scala b/src/dotty/tools/dotc/transform/Getters.scala index e018a45cc..918a92a04 100644 --- a/src/dotty/tools/dotc/transform/Getters.scala +++ b/src/dotty/tools/dotc/transform/Getters.scala @@ -58,7 +58,7 @@ class Getters extends MiniPhaseTransform with SymTransformer { thisTransform => } else d } - private val NoGetterNeeded = Method | Param | JavaDefined | JavaStatic + private val NoGetterNeeded = Method | Param | JavaDefined | JavaStatic | Lazy override def transformValDef(tree: ValDef)(implicit ctx: Context, info: TransformerInfo): Tree = if (tree.symbol is Method) DefDef(tree.symbol.asTerm, tree.rhs) else tree diff --git a/src/dotty/tools/dotc/transform/LazyVals.scala b/src/dotty/tools/dotc/transform/LazyVals.scala index 7b6135dc0..dde086089 100644 --- a/src/dotty/tools/dotc/transform/LazyVals.scala +++ b/src/dotty/tools/dotc/transform/LazyVals.scala @@ -12,23 +12,34 @@ import dotty.tools.dotc.transform.TreeTransforms.{TransformerInfo, TreeTransform import dotty.tools.dotc.ast.Trees._ import dotty.tools.dotc.ast.{untpd, tpd} import dotty.tools.dotc.core.Constants.Constant -import dotty.tools.dotc.core.Types.MethodType +import dotty.tools.dotc.core.Types.{ExprType, NoType, MethodType} import dotty.tools.dotc.core.Names.Name -import dotty.runtime.LazyVals +import dotty.runtime.{LazyVals => RLazyVals} // dotty deviation import SymUtils._ import scala.collection.mutable.ListBuffer import dotty.tools.dotc.core.Denotations.SingleDenotation import dotty.tools.dotc.core.SymDenotations.SymDenotation -import dotty.tools.dotc.core.DenotTransformers.{IdentityDenotTransformer, DenotTransformer} +import dotty.tools.dotc.core.DenotTransformers.{SymTransformer, IdentityDenotTransformer, DenotTransformer} -class LazyValsTransform extends MiniPhaseTransform with IdentityDenotTransformer { +class LazyVals extends MiniPhaseTransform with SymTransformer { import tpd._ - def transformer = new LazyValsTransform + def transformSym(d: SymDenotation)(implicit ctx: Context): SymDenotation = { + if(d is(Flags.Lazy, butNot = Flags.ModuleVal | Flags.Method)) { + // Method flag is set on lazy vals coming from Unpickler. They are already methods and shouldn't be transformed twice + d.copySymDenotation( + initFlags = d.flags | Flags.Method, + info = ExprType(d.info)) + } + else d + } + + def transformer = new LazyVals - val containerFlags = Flags.Synthetic | Flags.Mutable + val containerFlags = Flags.Synthetic | Flags.Mutable | Flags.Lazy + val initFlags = Flags.Synthetic | Flags.Method /** this map contains mutable state of transformation: OffsetDefs to be appended to companion object definitions, * and number of bits currently used */ @@ -72,9 +83,9 @@ class LazyValsTransform extends MiniPhaseTransform with IdentityDenotTransformer * dotty.runtime(eg dotty.runtime.LazyInt) */ def transformLocalValDef(x: ValDef)(implicit ctx: Context) = x match { - case x@ValDef(name, tpt, rhs) => - val valueInitter = rhs + case x@ValDef(name, tpt, valueInitter) => val holderName = ctx.freshName(name.toString + StdNames.nme.LAZY_LOCAL).toTermName + val initName = ctx.freshName(name.toString + StdNames.nme.LAZY_LOCAL_INIT).toTermName val tpe = x.tpe.widen val holderType = @@ -88,21 +99,35 @@ class LazyValsTransform extends MiniPhaseTransform with IdentityDenotTransformer else if (tpe isRef defn.ShortClass) "LazyShort" else "LazyRef" + val holderImpl = ctx.requiredClass("dotty.runtime." + holderType) - val holderSymbol = ctx.newSymbol(x.symbol.owner, holderName, containerFlags, holderImpl.typeRef, coord = x.symbol.coord) - val holderTree = ValDef(holderSymbol, New(holderImpl.typeRef, List(valueInitter.changeOwner(x.symbol, holderSymbol)))) + val holderSymbol = ctx.newSymbol(x.symbol.owner, holderName, containerFlags, holderImpl.typeRef, coord = x.pos) + val initSymbol = ctx.newSymbol(x.symbol.owner, initName, initFlags, MethodType(Nil, tpe), coord = x.pos) + val result = ref(holderSymbol).select("value".toTermName) + val flag = ref(holderSymbol).select("initialized".toTermName) + val initer = valueInitter.changeOwner(x.symbol, initSymbol) + val initBody = + ref(holderSymbol).select(defn.Object_synchronized).appliedToType(tpe).appliedTo( + mkNonThreadSafeDef(result, flag, initer).ensureConforms(tpe)) + val initTree = DefDef(initSymbol, initBody) + val holderTree = ValDef(holderSymbol, New(holderImpl.typeRef, List())) val methodBody = { - val prefix = ref(holderSymbol).select("value".toTermName) - if (holderType != "LazyRef") prefix - else prefix.select(defn.Any_asInstanceOf).appliedToType(tpe) + tpd.If(flag, EmptyTree, ref(initSymbol)) + result.ensureConforms(tpe) } val methodTree = DefDef(x.symbol.asTerm, methodBody) ctx.debuglog(s"found a lazy val ${x.show},\n rewrote with ${holderTree.show}") - Thicket(holderTree, methodTree) + Thicket(holderTree, initTree, methodTree) } - /** Create non-threadsafe lazy accessor equivalent to such code + + override def transformStats(trees: List[tpd.Tree])(implicit ctx: Context, info: TransformerInfo): List[tpd.Tree] = { + val (holders, stats) = trees.partition { _.symbol.flags == containerFlags} + holders:::stats + } + + /** Create non-threadsafe lazy accessor equivalent to such code * def methodSymbol() = { * if (flag) target * else { @@ -113,13 +138,11 @@ class LazyValsTransform extends MiniPhaseTransform with IdentityDenotTransformer * } */ - def mkNonThreadSafeDef(target: Symbol, flag: Symbol, rhs: Tree)(implicit ctx: Context) = { - val cond = ref(flag) - val exp = ref(target) - val setFlag = Assign(cond, Literal(Constants.Constant(true))) - val setTarget = Assign(exp, rhs) - val init = Block(List(setFlag, setTarget), exp) - If(cond, exp, init) + def mkNonThreadSafeDef(target: Tree, flag: Tree, rhs: Tree)(implicit ctx: Context) = { + val setFlag = Assign(flag, Literal(Constants.Constant(true))) + val setTarget = Assign(target, rhs) + val init = Block(List(setFlag, setTarget), target) + If(flag, target, init) } /** Create non-threadsafe lazy accessor for not-nullable types equivalent to such code @@ -155,7 +178,7 @@ class LazyValsTransform extends MiniPhaseTransform with IdentityDenotTransformer val flagName = ctx.freshName(name.toString + StdNames.nme.BITMAP_PREFIX).toTermName val flagSymbol = ctx.newSymbol(x.symbol.owner, flagName, containerFlags, defn.BooleanType) val flag = ValDef(flagSymbol, Literal(Constants.Constant(false))) - val slowPath = DefDef(x.symbol.asTerm, mkNonThreadSafeDef(containerSymbol, flagSymbol, rhs)) + val slowPath = DefDef(x.symbol.asTerm, mkNonThreadSafeDef(ref(containerSymbol), ref(flagSymbol), rhs)) Thicket(List(containerTree, flag, slowPath)) } @@ -266,7 +289,7 @@ class LazyValsTransform extends MiniPhaseTransform with IdentityDenotTransformer val thiz = This(claz)(ctx.fresh.setOwner(claz)) val companion = claz.companionModule val helperModule = ctx.requiredModule("dotty.runtime.LazyVals") - val getOffset = Select(ref(helperModule), LazyVals.Names.getOffset.toTermName) + val getOffset = Select(ref(helperModule), RLazyVals.Names.getOffset.toTermName) var offsetSymbol: TermSymbol = null var flag: Tree = EmptyTree var ord = 0 @@ -274,7 +297,7 @@ class LazyValsTransform extends MiniPhaseTransform with IdentityDenotTransformer // compute or create appropriate offsetSymol, bitmap and bits used by current ValDef appendOffsetDefs.get(companion.name.moduleClassName) match { case Some(info) => - val flagsPerLong = 64 / LazyVals.BITS_PER_LAZY_VAL + val flagsPerLong = 64 / RLazyVals.BITS_PER_LAZY_VAL info.ord += 1 ord = info.ord % flagsPerLong val id = info.ord / flagsPerLong @@ -305,11 +328,11 @@ class LazyValsTransform extends MiniPhaseTransform with IdentityDenotTransformer val containerTree = ValDef(containerSymbol, initValue(tpe)) val offset = Select(ref(companion), offsetSymbol.name) - val getFlag = Select(ref(helperModule), LazyVals.Names.get.toTermName) - val setFlag = Select(ref(helperModule), LazyVals.Names.setFlag.toTermName) - val wait = Select(ref(helperModule), LazyVals.Names.wait4Notification.toTermName) - val state = Select(ref(helperModule), LazyVals.Names.state.toTermName) - val cas = Select(ref(helperModule), LazyVals.Names.cas.toTermName) + val getFlag = Select(ref(helperModule), RLazyVals.Names.get.toTermName) + val setFlag = Select(ref(helperModule), RLazyVals.Names.setFlag.toTermName) + val wait = Select(ref(helperModule), RLazyVals.Names.wait4Notification.toTermName) + val state = Select(ref(helperModule), RLazyVals.Names.state.toTermName) + val cas = Select(ref(helperModule), RLazyVals.Names.cas.toTermName) val accessor = mkThreadSafeDef(x.symbol.asTerm, claz, ord, containerSymbol, rhs, tpe, offset, getFlag, state, cas, setFlag, wait) if(flag eq EmptyTree) diff --git a/tests/pos/lazyValsSepComp.scala b/tests/pos/lazyValsSepComp.scala new file mode 100644 index 000000000..337447280 --- /dev/null +++ b/tests/pos/lazyValsSepComp.scala @@ -0,0 +1,16 @@ +package dotty.tools +package io + +import java.io.{ InputStream } +import java.util.jar.JarEntry +import dotty.tools.dotc.core.Definitions +import language.postfixOps +import dotty.tools.dotc.core.Contexts._ + + +/** A test to trigger issue with separate compilation between Dotty and Scalac and lazy vals */ +object Foo { + val definitions: Definitions = null + def defn = definitions + def go = defn.FunctionClass(0) +} |