summaryrefslogtreecommitdiff
path: root/src/compiler/scala/tools/nsc/transform/LazyVals.scala
diff options
context:
space:
mode:
Diffstat (limited to 'src/compiler/scala/tools/nsc/transform/LazyVals.scala')
-rw-r--r--src/compiler/scala/tools/nsc/transform/LazyVals.scala95
1 files changed, 60 insertions, 35 deletions
diff --git a/src/compiler/scala/tools/nsc/transform/LazyVals.scala b/src/compiler/scala/tools/nsc/transform/LazyVals.scala
index b6695efb0b..bc9f70679c 100644
--- a/src/compiler/scala/tools/nsc/transform/LazyVals.scala
+++ b/src/compiler/scala/tools/nsc/transform/LazyVals.scala
@@ -1,14 +1,14 @@
package scala.tools.nsc
package transform
-import scala.collection.{ mutable, immutable }
+import scala.collection.mutable
abstract class LazyVals extends Transform with TypingTransformers with ast.TreeDSL {
// inherits abstract value `global` and class `Phase` from Transform
import global._ // the global environment
import definitions._ // standard classes and methods
- import typer.{typed, atOwner} // methods to type trees
+ import typer.typed // methods to type trees
import CODE._
val phaseName: String = "lazyvals"
@@ -38,6 +38,7 @@ abstract class LazyVals extends Transform with TypingTransformers with ast.TreeD
case ClassDef(_, _, _, _) | DefDef(_, _, _, _, _, _) | ModuleDef(_, _, _) =>
+ // Avoid adding bitmaps when they are fully overshadowed by those that are added inside loops
case LabelDef(name, _, _) if nme.isLoopHeaderLabel(name) =>
case _ =>
@@ -54,9 +55,28 @@ abstract class LazyVals extends Transform with TypingTransformers with ast.TreeD
private val lazyVals = perRunCaches.newMap[Symbol, Int]() withDefaultValue 0
import symtab.Flags._
+ private def flattenThickets(stats: List[Tree]): List[Tree] = stats.flatMap(_ match {
+ case b @ Block(List(d1@DefDef(_, n1, _, _, _, _)), d2@DefDef(_, n2, _, _, _, _)) if b.tpe == null && n1.endsWith(nme.LAZY_SLOW_SUFFIX) =>
+ List(d1, d2)
+ case stat =>
+ List(stat)
+ })
/** Perform the following transformations:
* - for a lazy accessor inside a method, make it check the initialization bitmap
+ * - implement double checked locking of member modules for non-trait owners (trait just have the abstract accessor)
+ * ```
+ * // typer
+ * class C { object x }
+ * // refchecks
+ * class C { var x$module; def x() = { x$module = new x; x$module }
+ * // lazyvals
+ * class C {
+ * var x$module // module var
+ * def x() = { if (x$module == null) x$lzycompute() else x$module // fast path
+ * def x$lzycompute() = { synchronized { if (x$module == null) x$module = new x }; x$module } // slow path
+ * }
+ * ```
* - for all methods, add enough int vars to allow one flag per lazy local value
* - blocks in template bodies behave almost like methods. A single bitmaps section is
* added in the first block, for all lazy values defined in such blocks.
@@ -72,20 +92,14 @@ abstract class LazyVals extends Transform with TypingTransformers with ast.TreeD
case Block(_, _) =>
val block1 = super.transform(tree)
val Block(stats, expr) = block1
- val stats1 = stats.flatMap(_ match {
- case Block(List(d1@DefDef(_, n1, _, _, _, _)), d2@DefDef(_, n2, _, _, _, _)) if (nme.newLazyValSlowComputeName(n2) == n1) =>
- List(d1, d2)
- case stat =>
- List(stat)
- })
- treeCopy.Block(block1, stats1, expr)
+ treeCopy.Block(block1, flattenThickets(stats), expr)
case DefDef(_, _, _, _, _, rhs) => atOwner(tree.symbol) {
val (res, slowPathDef) = if (!sym.owner.isClass && sym.isLazy) {
val enclosingClassOrDummyOrMethod = {
val enclMethod = sym.enclMethod
- if (enclMethod != NoSymbol ) {
+ if (enclMethod != NoSymbol) {
val enclClass = sym.enclClass
if (enclClass != NoSymbol && enclMethod == enclClass.enclMethod)
enclClass
@@ -100,30 +114,39 @@ abstract class LazyVals extends Transform with TypingTransformers with ast.TreeD
val (rhs1, sDef) = mkLazyDef(enclosingClassOrDummyOrMethod, transform(rhs), idx, sym)
sym.resetFlag((if (lazyUnit(sym)) 0 else LAZY) | ACCESSOR)
(rhs1, sDef)
- } else
+ } else if (sym.hasAllFlags(MODULE | METHOD) && !sym.owner.isTrait) {
+ rhs match {
+ case b @ Block((assign @ Assign(moduleRef, _)) :: Nil, expr) =>
+ def cond = Apply(Select(moduleRef, Object_eq), List(Literal(Constant(null))))
+ val (fastPath, slowPath) = mkFastPathBody(sym.owner.enclClass, moduleRef.symbol, cond, transform(assign) :: Nil, Nil, transform(expr))
+ (localTyper.typedPos(tree.pos)(fastPath), localTyper.typedPos(tree.pos)(slowPath))
+ case rhs =>
+ global.reporter.error(tree.pos, "Unexpected tree on the RHS of a module accessor: " + rhs)
+ (rhs, EmptyTree)
+ }
+ } else {
(transform(rhs), EmptyTree)
+ }
val ddef1 = deriveDefDef(tree)(_ => if (LocalLazyValFinder.find(res)) typed(addBitmapDefs(sym, res)) else res)
- if (slowPathDef != EmptyTree) Block(slowPathDef, ddef1) else ddef1
+ if (slowPathDef != EmptyTree) {
+ // The contents of this block are flattened into the enclosing statement sequence, see flattenThickets
+ // This is a poor man's version of dotty's Thicket: https://github.com/lampepfl/dotty/blob/d5280358d1/src/dotty/tools/dotc/ast/Trees.scala#L707
+ Block(slowPathDef, ddef1)
+ } else ddef1
}
case Template(_, _, body) => atOwner(currentOwner) {
- val body1 = super.transformTrees(body)
+ // TODO: shady business... can this logic be encapsulated in LocalLazyValFinder?
var added = false
- val stats =
- for (stat <- body1) yield stat match {
- case Block(_, _) | Apply(_, _) | If(_, _, _) | Try(_, _, _) if !added =>
- // Avoid adding bitmaps when they are fully overshadowed by those
- // that are added inside loops
- if (LocalLazyValFinder.find(stat)) {
- added = true
- typed(addBitmapDefs(sym, stat))
- } else stat
- case ValDef(_, _, _, _) =>
- typed(deriveValDef(stat)(addBitmapDefs(stat.symbol, _)))
- case _ =>
- stat
+ val stats = super.transformTrees(body) mapConserve {
+ case stat: ValDef => typed(deriveValDef(stat)(addBitmapDefs(stat.symbol, _)))
+ case stat: TermTree if !added && (LocalLazyValFinder find stat) =>
+ added = true
+ typed(addBitmapDefs(sym, stat))
+ case stat => stat
}
+
val innerClassBitmaps = if (!added && currentOwner.isClass && bitmaps.contains(currentOwner)) {
// add bitmap to inner class if necessary
val toAdd0 = bitmaps(currentOwner).map(s => typed(ValDef(s, ZERO)))
@@ -135,7 +158,7 @@ abstract class LazyVals extends Transform with TypingTransformers with ast.TreeD
})
toAdd0
} else List()
- deriveTemplate(tree)(_ => innerClassBitmaps ++ stats)
+ deriveTemplate(tree)(_ => innerClassBitmaps ++ flattenThickets(stats))
}
case ValDef(_, _, _, _) if !sym.owner.isModule && !sym.owner.isClass =>
@@ -192,21 +215,23 @@ abstract class LazyVals extends Transform with TypingTransformers with ast.TreeD
def mkSlowPathDef(clazz: Symbol, lzyVal: Symbol, cond: Tree, syncBody: List[Tree],
stats: List[Tree], retVal: Tree): Tree = {
- // Q: is there a reason to first set owner to `clazz` (by using clazz.newMethod), and then
- // changing it to lzyVal.owner very soon after? Could we just do lzyVal.owner.newMethod?
- val defSym = clazz.newMethod(nme.newLazyValSlowComputeName(lzyVal.name.toTermName), lzyVal.pos, STABLE | PRIVATE)
+ val owner = lzyVal.owner
+ val defSym = owner.newMethod(nme.newLazyValSlowComputeName(lzyVal.name.toTermName), lzyVal.pos, STABLE | PRIVATE)
defSym setInfo MethodType(List(), lzyVal.tpe.resultType)
- defSym.owner = lzyVal.owner
+ if (owner.isClass) owner.info.decls.enter(defSym)
debuglog(s"crete slow compute path $defSym with owner ${defSym.owner} for lazy val $lzyVal")
- if (bitmaps.contains(lzyVal))
- bitmaps(lzyVal).map(_.owner = defSym)
+ // this is a hack i don't understand for lazy vals nested in a lazy val, introduced in 3769f4d,
+ // tested in pos/t3670 (add9be64). class A { val n = { lazy val b = { lazy val dd = 3; dd }; b } }
+ // bitmaps has an entry bMethodSym -> List(bitmap$0), where bitmap$0.owner == bMethodSym.
+ // now we set bitmap$0.owner = b$lzycomputeMethodSym.
+ for (bitmap <- bitmaps(lzyVal)) bitmap.owner = defSym
val rhs: Tree = gen.mkSynchronizedCheck(clazz, cond, syncBody, stats).changeOwner(currentOwner -> defSym)
DefDef(defSym, addBitmapDefs(lzyVal, BLOCK(rhs, retVal)))
}
- def mkFastPathBody(clazz: Symbol, lzyVal: Symbol, cond: Tree, syncBody: List[Tree],
+ def mkFastPathBody(clazz: Symbol, lzyVal: Symbol, cond: => Tree, syncBody: List[Tree],
stats: List[Tree], retVal: Tree): (Tree, Tree) = {
val slowPathDef: Tree = mkSlowPathDef(clazz, lzyVal, cond, syncBody, stats, retVal)
(If(cond, Apply(Ident(slowPathDef.symbol), Nil), retVal), slowPathDef)
@@ -261,7 +286,7 @@ abstract class LazyVals extends Transform with TypingTransformers with ast.TreeD
(mkBlock(rhs), UNIT)
}
- val cond = (bitmapRef GEN_& (mask, bitmapKind)) GEN_== (ZERO, bitmapKind)
+ def cond = (bitmapRef GEN_& (mask, bitmapKind)) GEN_== (ZERO, bitmapKind)
val lazyDefs = mkFastPathBody(methOrClass.enclClass, lazyVal, cond, List(block), Nil, res)
(atPos(tree.pos)(localTyper.typed {lazyDefs._1 }), atPos(tree.pos)(localTyper.typed {lazyDefs._2 }))
}