summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAdriaan Moors <adriaan.moors@typesafe.com>2016-09-27 16:58:25 -0700
committerAdriaan Moors <adriaan.moors@typesafe.com>2016-09-29 11:49:36 -0700
commit493e22f321d6e7774e74419242b6e3d61eff6bad (patch)
tree5f7ae7fcf7563213a98a6aeb7d43f80ff55796f3
parentd04cda14d722ff365b4c3b543de008d93772410b (diff)
downloadscala-493e22f321d6e7774e74419242b6e3d61eff6bad.tar.gz
scala-493e22f321d6e7774e74419242b6e3d61eff6bad.tar.bz2
scala-493e22f321d6e7774e74419242b6e3d61eff6bad.zip
Emit local module like lazy val
The motivation is to use the new fine-grained lock scoping that local lazies have since #5294. Fixes scala/scala-dev#235 Co-Authored-By: Jason Zaugg <jzaugg@gmail.com>
-rw-r--r--src/compiler/scala/tools/nsc/transform/Fields.scala42
-rw-r--r--test/files/run/SD-235.scala39
-rw-r--r--test/files/run/delambdafy_t6028.check17
-rw-r--r--test/files/run/local_obj.scala9
-rw-r--r--test/files/run/t6028.check17
-rw-r--r--test/junit/scala/tools/nsc/backend/jvm/opt/ScalaInlineInfoTest.scala4
6 files changed, 88 insertions, 40 deletions
diff --git a/src/compiler/scala/tools/nsc/transform/Fields.scala b/src/compiler/scala/tools/nsc/transform/Fields.scala
index 6cf6a5abce..0c6982384d 100644
--- a/src/compiler/scala/tools/nsc/transform/Fields.scala
+++ b/src/compiler/scala/tools/nsc/transform/Fields.scala
@@ -193,20 +193,6 @@ abstract class Fields extends InfoTransform with ast.TreeDSL with TypingTransfor
// not be emitted as ACC_FINAL. They are FINAL in the Scala sense, though: cannot be overridden.
private final val ModuleOrLazyFieldFlags = FINAL | PrivateLocal | SYNTHETIC | NEEDS_TREES
- private def newModuleVarSymbol(owner: Symbol, module: Symbol, tp: Type): TermSymbol = {
-// println(s"new module var in $site for $module of type $tp")
- val flags = MODULEVAR | (if (owner.isClass) ModuleOrLazyFieldFlags else 0)
-
- val moduleVar =
- (owner.newVariable(nme.moduleVarName(module.name.toTermName), module.pos.focus, flags)
- setInfo tp
- addAnnotation VolatileAttr)
-
- moduleOrLazyVarOf(module) = moduleVar
-
- moduleVar
- }
-
private def moduleInit(module: Symbol, moduleVar: Symbol) = {
// println(s"moduleInit for $module in ${module.ownerChain} --> ${moduleVarOf.get(module)}")
def moduleVarRef = gen.mkAttributedRef(moduleVar)
@@ -380,8 +366,16 @@ abstract class Fields extends InfoTransform with ast.TreeDSL with TypingTransfor
(existingGetter ne NoSymbol) && (tp matches (site memberInfo existingGetter).resultType) // !existingGetter.isDeferred && -- see (3)
}
- def newModuleVarMember(member: Symbol): TermSymbol =
- newModuleVarSymbol(clazz, member, site.memberType(member).resultType)
+ def newModuleVarMember(module: Symbol): TermSymbol = {
+ val moduleVar =
+ (clazz.newVariable(nme.moduleVarName(module.name.toTermName), module.pos.focus, MODULEVAR | ModuleOrLazyFieldFlags)
+ setInfo site.memberType(module).resultType
+ addAnnotation VolatileAttr)
+
+ moduleOrLazyVarOf(module) = moduleVar
+
+ moduleVar
+ }
def newLazyVarMember(member: Symbol): TermSymbol =
Fields.this.newLazyVarMember(clazz, member, site.memberType(member).resultType)
@@ -531,7 +525,8 @@ abstract class Fields extends InfoTransform with ast.TreeDSL with TypingTransfor
def mkTypedValDef(sym: Symbol, rhs: Tree = EmptyTree) = typedPos(sym.pos)(ValDef(sym, rhs)).asInstanceOf[ValDef]
/**
- * Desugar a local `lazy val x: Int = rhs` into
+ * Desugar a local `lazy val x: Int = rhs` or a local object into
+ *
* ```
* val x$lzy = new scala.runtime.LazyInt()
* def x$lzycompute(): Int =
@@ -541,10 +536,13 @@ abstract class Fields extends InfoTransform with ast.TreeDSL with TypingTransfor
* }
* def x(): Int = if (x$lzy.initialized()) x$lzy.value() else x$lzycompute()
* ```
+ *
+ * The expansion is the same for local lazy vals and local objects,
+ * except for the name of the val ($lzy or
*/
private def mkLazyLocalDef(lazyVal: Symbol, rhs: Tree): Tree = {
import CODE._
- import scala.reflect.NameTransformer.LAZY_LOCAL_SUFFIX_STRING
+ import scala.reflect.{NameTransformer => nx}
val owner = lazyVal.owner
val lazyValType = lazyVal.tpe.resultType
@@ -555,8 +553,7 @@ abstract class Fields extends InfoTransform with ast.TreeDSL with TypingTransfor
val lazyName = lazyVal.name.toTermName
val pos = lazyVal.pos.focus
- // used twice: once in the same owner as the lazy val, another time inside the compute method
- val localLazyName = lazyName append LAZY_LOCAL_SUFFIX_STRING
+ val localLazyName = lazyName append (if (lazyVal.isModule) nx.MODULE_VAR_SUFFIX_STRING else nx.LAZY_LOCAL_SUFFIX_STRING)
// The lazy holder val need not be mutable, as we write to its field.
// In fact, it MUST not be mutable to avoid capturing it as an ObjectRef in lambdalift
@@ -730,8 +727,9 @@ abstract class Fields extends InfoTransform with ast.TreeDSL with TypingTransfor
val cd = super.transform(ClassDef(statSym.moduleClass, impl) setType NoType)
if (currOwner.isClass) cd
else { // local module -- symbols cannot be generated by info transformer, so do it all here
- val moduleVar = newModuleVarSymbol(currOwner, statSym, statSym.info.resultType)
- Thicket(cd :: mkTypedValDef(moduleVar) :: mkAccessor(statSym)(moduleInit(statSym, moduleVar)) :: Nil)
+ val Block(stats, _) = mkLazyLocalDef(statSym, gen.newModule(statSym, statSym.info.resultType))
+
+ Thicket(cd :: stats)
}
case tree =>
diff --git a/test/files/run/SD-235.scala b/test/files/run/SD-235.scala
new file mode 100644
index 0000000000..eb79c6fe71
--- /dev/null
+++ b/test/files/run/SD-235.scala
@@ -0,0 +1,39 @@
+class C {
+ var ORef: Object = null
+ def test = {
+ object O {
+ assert(!Thread.holdsLock(C.this))
+ assert(Thread.holdsLock(ORef))
+ }
+ val captor = new { def oh = O }
+ val refField = captor.getClass.getDeclaredFields.last
+ refField.setAccessible(true)
+ assert(refField.getType.toString.contains("LazyRef"), refField)
+ ORef = refField.get(captor)
+ O
+ }
+}
+
+class D {
+ var ORef: Object = null
+ def test = {
+ lazy val O = {
+ assert(!Thread.holdsLock(D.this))
+ assert(Thread.holdsLock(ORef))
+ "O"
+ }
+ val captor = new { def oh = O }
+ val refField = captor.getClass.getDeclaredFields.last
+ refField.setAccessible(true)
+ assert(refField.getType.toString.contains("LazyRef"), refField)
+ ORef = refField.get(captor)
+ O
+ }
+}
+
+object Test {
+ def main(args: Array[String]): Unit = {
+ new C().test
+ new D().test
+ }
+}
diff --git a/test/files/run/delambdafy_t6028.check b/test/files/run/delambdafy_t6028.check
index 7b319c92dd..86cb1d5e97 100644
--- a/test/files/run/delambdafy_t6028.check
+++ b/test/files/run/delambdafy_t6028.check
@@ -15,7 +15,7 @@ package <empty> {
}
};
def bar(barParam: String): Object = {
- @volatile var MethodLocalObject$module: runtime.VolatileObjectRef = scala.runtime.VolatileObjectRef.zero();
+ lazy <artifact> val MethodLocalObject$module: scala.runtime.LazyRef = new scala.runtime.LazyRef();
T.this.MethodLocalObject$1(barParam, MethodLocalObject$module)
};
def tryy(tryyParam: String): Function0 = {
@@ -42,13 +42,14 @@ package <empty> {
<synthetic> <stable> <artifact> def $outer(): T = MethodLocalObject$2.this.$outer;
<synthetic> <stable> <artifact> def $outer(): T = MethodLocalObject$2.this.$outer
};
- final private[this] def MethodLocalObject$lzycompute$1(barParam$1: String, MethodLocalObject$module$1: runtime.VolatileObjectRef): Unit = T.this.synchronized[Unit](if (MethodLocalObject$module$1.elem.$asInstanceOf[T#MethodLocalObject$2.type]().eq(null))
- MethodLocalObject$module$1.elem = new T#MethodLocalObject$2.type(T.this, barParam$1));
- final <stable> private[this] def MethodLocalObject$1(barParam$1: String, MethodLocalObject$module$1: runtime.VolatileObjectRef): T#MethodLocalObject$2.type = {
- if (MethodLocalObject$module$1.elem.$asInstanceOf[T#MethodLocalObject$2.type]().eq(null))
- T.this.MethodLocalObject$lzycompute$1(barParam$1, MethodLocalObject$module$1);
- (MethodLocalObject$module$1.elem.$asInstanceOf[T#MethodLocalObject$2.type](): T#MethodLocalObject$2.type)
- };
+ final <artifact> private[this] def MethodLocalObject$lzycompute$1(barParam$1: String, MethodLocalObject$module$1: scala.runtime.LazyRef): T#MethodLocalObject$2.type = MethodLocalObject$module$1.synchronized[T#MethodLocalObject$2.type](if (MethodLocalObject$module$1.initialized())
+ MethodLocalObject$module$1.value().$asInstanceOf[T#MethodLocalObject$2.type]()
+ else
+ MethodLocalObject$module$1.initialize(new T#MethodLocalObject$2.type(T.this, barParam$1)).$asInstanceOf[T#MethodLocalObject$2.type]());
+ final private[this] def MethodLocalObject$1(barParam$1: String, MethodLocalObject$module$1: scala.runtime.LazyRef): T#MethodLocalObject$2.type = if (MethodLocalObject$module$1.initialized())
+ MethodLocalObject$module$1.value().$asInstanceOf[T#MethodLocalObject$2.type]()
+ else
+ T.this.MethodLocalObject$lzycompute$1(barParam$1, MethodLocalObject$module$1);
final <artifact> private[this] def $anonfun$tryy$1(tryyParam$1: String, tryyLocal$1: runtime.ObjectRef): Unit = try {
tryyLocal$1.elem = tryyParam$1
} finally ()
diff --git a/test/files/run/local_obj.scala b/test/files/run/local_obj.scala
new file mode 100644
index 0000000000..25123f7078
--- /dev/null
+++ b/test/files/run/local_obj.scala
@@ -0,0 +1,9 @@
+class C {
+ val z = 2
+ def mod = { object x { val y = z } ; x.y }
+}
+
+object Test extends App {
+ val c = new C
+ assert(c.mod == c.z, s"${c.mod} != ${c.z}")
+}
diff --git a/test/files/run/t6028.check b/test/files/run/t6028.check
index 903ea3b753..05634fa8eb 100644
--- a/test/files/run/t6028.check
+++ b/test/files/run/t6028.check
@@ -15,7 +15,7 @@ package <empty> {
}
};
def bar(barParam: Int): Object = {
- @volatile var MethodLocalObject$module: runtime.VolatileObjectRef = scala.runtime.VolatileObjectRef.zero();
+ lazy <artifact> val MethodLocalObject$module: scala.runtime.LazyRef = new scala.runtime.LazyRef();
T.this.MethodLocalObject$1(barParam, MethodLocalObject$module)
};
def tryy(tryyParam: Int): Function0 = {
@@ -54,13 +54,14 @@ package <empty> {
<synthetic> <stable> <artifact> def $outer(): T = MethodLocalObject$2.this.$outer;
<synthetic> <stable> <artifact> def $outer(): T = MethodLocalObject$2.this.$outer
};
- final private[this] def MethodLocalObject$lzycompute$1(barParam$1: Int, MethodLocalObject$module$1: runtime.VolatileObjectRef): Unit = T.this.synchronized[Unit](if (MethodLocalObject$module$1.elem.$asInstanceOf[T#MethodLocalObject$2.type]().eq(null))
- MethodLocalObject$module$1.elem = new T#MethodLocalObject$2.type(T.this, barParam$1));
- final <stable> private[this] def MethodLocalObject$1(barParam$1: Int, MethodLocalObject$module$1: runtime.VolatileObjectRef): T#MethodLocalObject$2.type = {
- if (MethodLocalObject$module$1.elem.$asInstanceOf[T#MethodLocalObject$2.type]().eq(null))
- T.this.MethodLocalObject$lzycompute$1(barParam$1, MethodLocalObject$module$1);
- (MethodLocalObject$module$1.elem.$asInstanceOf[T#MethodLocalObject$2.type](): T#MethodLocalObject$2.type)
- };
+ final <artifact> private[this] def MethodLocalObject$lzycompute$1(barParam$1: Int, MethodLocalObject$module$1: scala.runtime.LazyRef): T#MethodLocalObject$2.type = MethodLocalObject$module$1.synchronized[T#MethodLocalObject$2.type](if (MethodLocalObject$module$1.initialized())
+ MethodLocalObject$module$1.value().$asInstanceOf[T#MethodLocalObject$2.type]()
+ else
+ MethodLocalObject$module$1.initialize(new T#MethodLocalObject$2.type(T.this, barParam$1)).$asInstanceOf[T#MethodLocalObject$2.type]());
+ final private[this] def MethodLocalObject$1(barParam$1: Int, MethodLocalObject$module$1: scala.runtime.LazyRef): T#MethodLocalObject$2.type = if (MethodLocalObject$module$1.initialized())
+ MethodLocalObject$module$1.value().$asInstanceOf[T#MethodLocalObject$2.type]()
+ else
+ T.this.MethodLocalObject$lzycompute$1(barParam$1, MethodLocalObject$module$1);
@SerialVersionUID(value = 0) final <synthetic> class $anonfun$tryy$1 extends scala.runtime.AbstractFunction0$mcV$sp with Serializable {
def <init>($outer: T, tryyParam$1: Int, tryyLocal$1: runtime.IntRef): <$anon: Function0> = {
$anonfun$tryy$1.super.<init>();
diff --git a/test/junit/scala/tools/nsc/backend/jvm/opt/ScalaInlineInfoTest.scala b/test/junit/scala/tools/nsc/backend/jvm/opt/ScalaInlineInfoTest.scala
index 8861577366..5cedc483cd 100644
--- a/test/junit/scala/tools/nsc/backend/jvm/opt/ScalaInlineInfoTest.scala
+++ b/test/junit/scala/tools/nsc/backend/jvm/opt/ScalaInlineInfoTest.scala
@@ -105,10 +105,10 @@ class ScalaInlineInfoTest extends BytecodeTesting {
("x4$(LT;)I", MethodInlineInfo(true ,false,false)),
("x5()I", MethodInlineInfo(true, false,false)),
("x5$(LT;)I", MethodInlineInfo(true ,false,false)),
- ("L$1(Lscala/runtime/VolatileObjectRef;)LT$L$2$;", MethodInlineInfo(true, false,false)),
+ ("L$1(Lscala/runtime/LazyRef;)LT$L$2$;", MethodInlineInfo(true, false,false)),
("nest$1()I", MethodInlineInfo(true, false,false)),
("$init$(LT;)V", MethodInlineInfo(true,false,false)),
- ("L$lzycompute$1(Lscala/runtime/VolatileObjectRef;)V", MethodInlineInfo(true,false,false))
+ ("L$lzycompute$1(Lscala/runtime/LazyRef;)LT$L$2$;", MethodInlineInfo(true,false,false))
),
None // warning
)