From 182262381548f3a51df6ea7dfa32bc6e9348bf70 Mon Sep 17 00:00:00 2001 From: Dmitry Petrashko Date: Mon, 9 Jan 2017 15:58:46 +0100 Subject: [PATCH 1/4] Fixes #1856: mimic Scalac sematic of recursive lazy vals. --- compiler/src/dotty/tools/dotc/transform/LazyVals.scala | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/LazyVals.scala b/compiler/src/dotty/tools/dotc/transform/LazyVals.scala index a6ac71286543..29f2b69270d6 100644 --- a/compiler/src/dotty/tools/dotc/transform/LazyVals.scala +++ b/compiler/src/dotty/tools/dotc/transform/LazyVals.scala @@ -186,8 +186,9 @@ class LazyVals extends MiniPhaseTransform with IdentityDenotTransformer { def mkNonThreadSafeDef(target: Tree, flag: Tree, rhs: Tree)(implicit ctx: Context) = { val setFlag = flag.becomes(Literal(Constants.Constant(true))) - val setTargets = if (isWildcardArg(rhs)) Nil else target.becomes(rhs) :: Nil - val init = Block(setFlag :: setTargets, target.ensureApplied) + val flagSet = setFlag :: Nil + val targetSet = if (isWildcardArg(rhs)) flagSet else target.becomes(rhs) :: flagSet + val init = Block(targetSet, target.ensureApplied) If(flag.ensureApplied, target.ensureApplied, init) } From 8b8fc178bd94ff7996ff340113660f3abb7f45c0 Mon Sep 17 00:00:00 2001 From: Dmitry Petrashko Date: Mon, 9 Jan 2017 16:04:40 +0100 Subject: [PATCH 2/4] test that #1856 is fixed. --- tests/run/i1856.check | 12 ++++++++++++ tests/run/i1856.scala | 14 ++++++++++++++ 2 files changed, 26 insertions(+) create mode 100644 tests/run/i1856.check create mode 100644 tests/run/i1856.scala diff --git a/tests/run/i1856.check b/tests/run/i1856.check new file mode 100644 index 000000000000..736619a9daca --- /dev/null +++ b/tests/run/i1856.check @@ -0,0 +1,12 @@ +Iteration 0 +Iteration 1 +Iteration 2 +Iteration 3 +Iteration 4 +Iteration 5 +Iteration 6 +Iteration 7 +Iteration 8 +Iteration 9 +42 + diff --git a/tests/run/i1856.scala b/tests/run/i1856.scala new file mode 100644 index 000000000000..b50fb51862e9 --- /dev/null +++ b/tests/run/i1856.scala @@ -0,0 +1,14 @@ +object Test { + var count: Int = 0 + lazy val lzy: Int = { + if (count < 10) { + println(s"Iteration $count") + count += 1 + lzy + } else 42 + } + + def main(args: Array[String]): Unit = { + println(lzy) + } +} From 0a6bc93e572296b2adf264b093d97804f7baf473 Mon Sep 17 00:00:00 2001 From: Dmitry Petrashko Date: Mon, 9 Jan 2017 16:26:24 +0100 Subject: [PATCH 3/4] Add a tricky example for #1856 --- tests/run/i1856.check | 1 - tests/run/i1856b.check | 22 ++++++++++++++++++++++ tests/run/i1856b.scala | 21 +++++++++++++++++++++ 3 files changed, 43 insertions(+), 1 deletion(-) create mode 100644 tests/run/i1856b.check create mode 100644 tests/run/i1856b.scala diff --git a/tests/run/i1856.check b/tests/run/i1856.check index 736619a9daca..5ebf09a88014 100644 --- a/tests/run/i1856.check +++ b/tests/run/i1856.check @@ -9,4 +9,3 @@ Iteration 7 Iteration 8 Iteration 9 42 - diff --git a/tests/run/i1856b.check b/tests/run/i1856b.check new file mode 100644 index 000000000000..d7556d459565 --- /dev/null +++ b/tests/run/i1856b.check @@ -0,0 +1,22 @@ +Iteration 0 +Iteration 1 +Iteration 2 +Iteration 3 +Iteration 4 +Iteration 5 +Iteration 6 +Iteration 7 +Iteration 8 +Iteration 9 +Lzy has been read as: 42 +Lzy has been read as: 43 +Lzy has been read as: 44 +Lzy has been read as: 45 +Lzy has been read as: 46 +Lzy has been read as: 47 +Lzy has been read as: 48 +Lzy has been read as: 49 +Lzy has been read as: 50 +Lzy has been read as: 51 +52 +Lzy has been read as: 52 diff --git a/tests/run/i1856b.scala b/tests/run/i1856b.scala new file mode 100644 index 000000000000..1a208600798b --- /dev/null +++ b/tests/run/i1856b.scala @@ -0,0 +1,21 @@ +object Test { + var count: Int = 0 + def getLzy = { + val read = lzy + println("Lzy has been read as: " +read) + read + } + + lazy val lzy: Int = { + if (count < 10) { + println(s"Iteration $count") + count += 1 + getLzy + 1 + } else 42 + } + + def main(args: Array[String]): Unit = { + println(lzy) + getLzy + } +} From 6c7234bd275d553f4133c62a29af10c6d5b07637 Mon Sep 17 00:00:00 2001 From: Dmitry Petrashko Date: Mon, 9 Jan 2017 17:09:56 +0100 Subject: [PATCH 4/4] This version is additionally able to _throw_ in case a lazy val would be assigned twice. --- .../dotty/tools/dotc/transform/LazyVals.scala | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/LazyVals.scala b/compiler/src/dotty/tools/dotc/transform/LazyVals.scala index 29f2b69270d6..899ee82c9033 100644 --- a/compiler/src/dotty/tools/dotc/transform/LazyVals.scala +++ b/compiler/src/dotty/tools/dotc/transform/LazyVals.scala @@ -187,8 +187,21 @@ class LazyVals extends MiniPhaseTransform with IdentityDenotTransformer { def mkNonThreadSafeDef(target: Tree, flag: Tree, rhs: Tree)(implicit ctx: Context) = { val setFlag = flag.becomes(Literal(Constants.Constant(true))) val flagSet = setFlag :: Nil - val targetSet = if (isWildcardArg(rhs)) flagSet else target.becomes(rhs) :: flagSet - val init = Block(targetSet, target.ensureApplied) + + val init = evalOnce(rhs) { rhsRef => + val targetSet = if (isWildcardArg(rhs)) flagSet else target.becomes(rhsRef) :: flagSet + val checkValidity = + tpd.If( + target.select(defn.Any_!=).appliedTo(tpd.defaultValue(target.tpe.widenDealias)).select(defn.Boolean_&&). + appliedTo( + target.select(defn.Any_!=).appliedTo(rhsRef) + ), + tpd.Throw(tpd.New(ctx.requiredClass("java.lang.IllegalStateException").namedType, Nil)), + tpd.EmptyTree + ) + Block(checkValidity :: targetSet, rhsRef) + } + If(flag.ensureApplied, target.ensureApplied, init) }