Skip to content

Commit b9d5565

Browse files
olsdavisszymon-rd
authored andcommitted
New lazy vals implementation
1 parent 1ae85eb commit b9d5565

File tree

10 files changed

+381
-26
lines changed

10 files changed

+381
-26
lines changed

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

Lines changed: 272 additions & 22 deletions
Large diffs are not rendered by default.

compiler/test-resources/scripting/scriptPath.sc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
val pathEntries = System.getenv("PATH").split(psep).toList
1717
System.err.printf("sun.java.command: %s\n", sys.props("sun.java.command"))
1818
System.err.printf("first 5 PATH entries:\n%s\n",pathEntries.take(5).mkString("\n"))
19+
printf("script.path: %s\n",path.norm)
20+
assert(path.endsWith("scriptPath.sc"),s"actual path [$path]")
1921
}
2022

2123
extension(s: String)

compiler/test/dotty/tools/backend/jvm/DottyBytecodeTests.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -597,7 +597,7 @@ class DottyBytecodeTests extends DottyBytecodeTest {
597597
val clsIn = dir.lookupName("Test.class", directory = false).input
598598
val clsNode = loadClassNode(clsIn)
599599
val method = getMethod(clsNode, "test")
600-
assertEquals(88, instructionsFromMethod(method).size)
600+
assertEquals(122, instructionsFromMethod(method).size)
601601
}
602602
}
603603

library/src/scala/runtime/LazyVals.scala

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ object LazyVals {
2525
val processors = java.lang.Runtime.getRuntime.nn.availableProcessors()
2626
8 * processors * processors
2727
}
28+
2829
private[this] val monitors: Array[Object] =
2930
Array.tabulate(base)(_ => new Object)
3031

@@ -40,6 +41,41 @@ object LazyVals {
4041

4142
/* ------------- Start of public API ------------- */
4243

44+
/**
45+
* Used to indicate the state of a lazy val that is being
46+
* evaluated and of which other threads await the result.
47+
*/
48+
final class Waiting:
49+
private var done = false
50+
51+
/**
52+
* Wakes up waiting threads. Called on completion of the evaluation
53+
* of lazy val's right-hand side.
54+
*/
55+
def release(): Unit = synchronized {
56+
done = true
57+
notifyAll()
58+
}
59+
60+
/**
61+
* Awaits the completion of the evaluation of lazy val's right-hand side.
62+
*/
63+
def awaitRelease(): Unit = synchronized {
64+
while !done do wait()
65+
}
66+
67+
/**
68+
* Used to indicate the state of a lazy val that is currently being
69+
* evaluated with no other thread awaiting its result.
70+
*/
71+
object Evaluating
72+
73+
/**
74+
* Used to indicate the state of a lazy val that has been evaluated to
75+
* `null`.
76+
*/
77+
object NULL
78+
4379
final val BITS_PER_LAZY_VAL = 2L
4480

4581
def STATE(cur: Long, ord: Int): Long = {
@@ -57,6 +93,12 @@ object LazyVals {
5793
unsafe.compareAndSwapLong(t, offset, e, n)
5894
}
5995

96+
def objCAS(t: Object, offset: Long, exp: Object, n: Object): Boolean = {
97+
if (debug)
98+
println(s"objCAS($t, $exp, $n)")
99+
unsafe.compareAndSwapObject(t, offset, exp, n)
100+
}
101+
60102
def setFlag(t: Object, offset: Long, v: Int, ord: Int): Unit = {
61103
if (debug)
62104
println(s"setFlag($t, $offset, $v, $ord)")
@@ -110,6 +152,13 @@ object LazyVals {
110152
r
111153
}
112154

155+
def getStaticOffset(clz: Class[_], name: String): Long = {
156+
val r = unsafe.staticFieldOffset(clz.getDeclaredField(name))
157+
if (debug)
158+
println(s"getStaticOffset($clz, $name) = $r")
159+
r
160+
}
161+
113162
def getOffsetStatic(field: java.lang.reflect.Field) =
114163
@nowarn
115164
val r = unsafe.objectFieldOffset(field)
@@ -119,11 +168,18 @@ object LazyVals {
119168

120169

121170
object Names {
171+
final val waiting = "Waiting"
172+
final val evaluating = "Evaluating"
173+
final val nullValue = "NULL"
174+
final val waitingAwaitRelease = "awaitRelease"
175+
final val waitingRelease = "release"
122176
final val state = "STATE"
123177
final val cas = "CAS"
178+
final val objCas = "objCAS"
124179
final val setFlag = "setFlag"
125180
final val wait4Notification = "wait4Notification"
126181
final val get = "get"
127182
final val getOffset = "getOffset"
183+
final val getStaticOffset = "getStaticOffset"
128184
}
129185
}

project/MiMaFilters.scala

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,22 @@ import com.typesafe.tools.mima.core._
44
object MiMaFilters {
55
val Library: Seq[ProblemFilter] = Seq(
66
ProblemFilters.exclude[MissingClassProblem]("scala.annotation.internal.MappedAlternative"),
7+
8+
ProblemFilters.exclude[DirectMissingMethodProblem]("scala.runtime.LazyVals.getStaticOffset"),
9+
ProblemFilters.exclude[DirectMissingMethodProblem]("scala.runtime.LazyVals.objCAS"),
10+
ProblemFilters.exclude[DirectMissingMethodProblem]("scala.runtime.LazyVals#Names.evaluating"),
11+
ProblemFilters.exclude[DirectMissingMethodProblem]("scala.runtime.LazyVals#Names.getStaticOffset"),
12+
ProblemFilters.exclude[DirectMissingMethodProblem]("scala.runtime.LazyVals#Names.nullValue"),
13+
ProblemFilters.exclude[DirectMissingMethodProblem]("scala.runtime.LazyVals#Names.objCas"),
14+
ProblemFilters.exclude[DirectMissingMethodProblem]("scala.runtime.LazyVals#Names.waiting"),
15+
ProblemFilters.exclude[DirectMissingMethodProblem]("scala.runtime.LazyVals#Names.waitingAwaitRelease"),
16+
ProblemFilters.exclude[DirectMissingMethodProblem]("scala.runtime.LazyVals#Names.waitingRelease"),
17+
ProblemFilters.exclude[MissingClassProblem]("scala.runtime.LazyVals$Evaluating$"),
18+
ProblemFilters.exclude[MissingClassProblem]("scala.runtime.LazyVals$NULL$"),
19+
ProblemFilters.exclude[MissingClassProblem]("scala.runtime.LazyVals$Waiting"),
20+
ProblemFilters.exclude[MissingFieldProblem]("scala.runtime.LazyVals.Evaluating"),
21+
ProblemFilters.exclude[MissingFieldProblem]("scala.runtime.LazyVals.NULL"),
22+
723
ProblemFilters.exclude[MissingFieldProblem]("scala.runtime.stdLibPatches.language#experimental.pureFunctions"),
824
ProblemFilters.exclude[MissingFieldProblem]("scala.runtime.stdLibPatches.language#experimental.captureChecking"),
925
ProblemFilters.exclude[MissingClassProblem]("scala.runtime.stdLibPatches.language$experimental$pureFunctions$"),

tests/init/neg/t3273.check

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,6 @@
1212
| Access non-initialized value num1. Promotion trace:
1313
| -> val num1: LazyList[Int] = 1 #:: num1.map(_ + 1) // error [ t3273.scala:4 ]
1414
| ^^^^
15-
-- Error: tests/init/neg/t3273.scala:5:61 ------------------------------------------------------------------------------
16-
5 | val num2: LazyList[Int] = 1 #:: num2.iterator.map(_ + 1).to(LazyList) // error
17-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
1815
| Cannot prove the method argument is hot. Only hot values are safe to leak.
1916
| Found = Fun { this = ThisRef[object Test], owner = object Test }. Calling trace:
2017
| -> object Test { [ t3273.scala:3 ]

tests/run/lazyVals_c3.0.0.check

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
computing x
2+
x
3+
computing y
4+
y

tests/run/lazyVals_c3.0.0.scala

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// Compiled with 3.0.0 and run with current compiler
2+
class Foo:
3+
lazy val x =
4+
println("computing x")
5+
"x"
6+
lazy val y =
7+
println("computing y")
8+
"y"
9+
10+
@main def Test =
11+
val foo = new Foo
12+
println(foo.x)
13+
println(foo.y)

tests/run/lazyVals_c3.1.0.check

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
computing x
2+
x
3+
computing y
4+
y

tests/run/lazyVals_c3.1.0.scala

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// Compiled with 3.1.0 and run with current compiler
2+
class Foo:
3+
lazy val x =
4+
println("computing x")
5+
"x"
6+
lazy val y =
7+
println("computing y")
8+
"y"
9+
10+
@main def Test =
11+
val foo = new Foo
12+
println(foo.x)
13+
println(foo.y)

0 commit comments

Comments
 (0)