Skip to content

Commit fb1a124

Browse files
olsdavisszymon-rd
authored andcommitted
New lazy vals implementation
1 parent a293d7d commit fb1a124

File tree

11 files changed

+394
-25
lines changed

11 files changed

+394
-25
lines changed

Diff for: compiler/src/dotty/tools/dotc/transform/LazyVals.scala

+272-22
Large diffs are not rendered by default.

Diff for: compiler/test-resources/scripting/scriptPath.sc

+2
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)

Diff for: compiler/test/dotty/tools/backend/jvm/DottyBytecodeTests.scala

+1-1
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

Diff for: library/src/scala/runtime/LazyVals.scala

+56
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ object LazyVals {
2222
val processors = java.lang.Runtime.getRuntime.nn.availableProcessors()
2323
8 * processors * processors
2424
}
25+
2526
private[this] val monitors: Array[Object] =
2627
Array.tabulate(base)(_ => new Object)
2728

@@ -37,6 +38,41 @@ object LazyVals {
3738

3839
/* ------------- Start of public API ------------- */
3940

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

4278
def STATE(cur: Long, ord: Int): Long = {
@@ -54,6 +90,12 @@ object LazyVals {
5490
unsafe.compareAndSwapLong(t, offset, e, n)
5591
}
5692

93+
def objCAS(t: Object, offset: Long, exp: Object, n: Object): Boolean = {
94+
if (debug)
95+
println(s"objCAS($t, $exp, $n)")
96+
unsafe.compareAndSwapObject(t, offset, exp, n)
97+
}
98+
5799
def setFlag(t: Object, offset: Long, v: Int, ord: Int): Unit = {
58100
if (debug)
59101
println(s"setFlag($t, $offset, $v, $ord)")
@@ -106,6 +148,13 @@ object LazyVals {
106148
r
107149
}
108150

151+
def getStaticOffset(clz: Class[_], name: String): Long = {
152+
val r = unsafe.staticFieldOffset(clz.getDeclaredField(name))
153+
if (debug)
154+
println(s"getStaticOffset($clz, $name) = $r")
155+
r
156+
}
157+
109158
def getOffsetStatic(field: java.lang.reflect.Field) =
110159
val r = unsafe.objectFieldOffset(field)
111160
if (debug)
@@ -114,11 +163,18 @@ object LazyVals {
114163

115164

116165
object Names {
166+
final val waiting = "Waiting"
167+
final val evaluating = "Evaluating"
168+
final val nullValued = "NULL"
169+
final val waitingAwaitRelease = "awaitRelease"
170+
final val waitingRelease = "release"
117171
final val state = "STATE"
118172
final val cas = "CAS"
173+
final val objCas = "objCAS"
119174
final val setFlag = "setFlag"
120175
final val wait4Notification = "wait4Notification"
121176
final val get = "get"
122177
final val getOffset = "getOffset"
178+
final val getStaticOffset = "getStaticOffset"
123179
}
124180
}

Diff for: project/MiMaFilters.scala

+23
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,28 @@ object MiMaFilters {
1010
ProblemFilters.exclude[DirectMissingMethodProblem]("scala.quoted.runtime.QuoteUnpickler.unpickleTypeV2"),
1111

1212
ProblemFilters.exclude[MissingClassProblem]("scala.annotation.since"),
13+
14+
ProblemFilters.exclude[DirectMissingMethodProblem]("scala.runtime.LazyVals.getStaticOffset"),
15+
ProblemFilters.exclude[DirectMissingMethodProblem]("scala.runtime.LazyVals.getStaticOffset"),
16+
ProblemFilters.exclude[DirectMissingMethodProblem]("scala.runtime.LazyVals.objCAS"),
17+
ProblemFilters.exclude[DirectMissingMethodProblem]("scala.runtime.LazyVals.objCAS"),
18+
ProblemFilters.exclude[DirectMissingMethodProblem]("scala.runtime.LazyVals#Names.evaluating"),
19+
ProblemFilters.exclude[DirectMissingMethodProblem]("scala.runtime.LazyVals#Names.getStaticOffset"),
20+
ProblemFilters.exclude[DirectMissingMethodProblem]("scala.runtime.LazyVals#Names.nullValued"),
21+
ProblemFilters.exclude[DirectMissingMethodProblem]("scala.runtime.LazyVals#Names.objCas"),
22+
ProblemFilters.exclude[DirectMissingMethodProblem]("scala.runtime.LazyVals#Names.waiting"),
23+
ProblemFilters.exclude[DirectMissingMethodProblem]("scala.runtime.LazyVals#Names.waitingAwaitRelease"),
24+
ProblemFilters.exclude[DirectMissingMethodProblem]("scala.runtime.LazyVals#Names.waitingRelease"),
25+
ProblemFilters.exclude[MissingClassProblem]("scala.runtime.LazyVals$Evaluating$"),
26+
ProblemFilters.exclude[MissingClassProblem]("scala.runtime.LazyVals$NULL$"),
27+
ProblemFilters.exclude[MissingClassProblem]("scala.runtime.LazyVals$Waiting"),
28+
ProblemFilters.exclude[MissingFieldProblem]("scala.runtime.LazyVals.Evaluating"),
29+
ProblemFilters.exclude[MissingFieldProblem]("scala.runtime.LazyVals.NULL"),
30+
31+
// Experimental `MainAnnotation` APIs. Can be added in 3.3.0 or later.
32+
// MiMa bug: classes nested in an experimental object should be ignored
33+
ProblemFilters.exclude[MissingClassProblem]("scala.annotation.MainAnnotation$Info"),
34+
ProblemFilters.exclude[MissingClassProblem]("scala.annotation.MainAnnotation$Parameter"),
35+
ProblemFilters.exclude[MissingClassProblem]("scala.annotation.MainAnnotation$ParameterAnnotation"),
1336
)
1437
}

Diff for: tests/init/neg/promotion-loop.check

+4
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
-- Error: tests/init/neg/promotion-loop.scala:16:10 --------------------------------------------------------------------
22
16 | println(b) // error
33
| ^
4+
<<<<<<< HEAD
5+
| Cannot prove that the value is fully-initialized. Only initialized values may be used as arguments.
6+
=======
47
| Cannot prove that the value is fully initialized. Only initialized values may be used as arguments.
8+
>>>>>>> 91c03b1f7835d574290eba1ecf236e0f59fc2bf0
59
|
610
| The unsafe promotion may cause the following problem:
711
| Cannot prove that the value is fully initialized. Only initialized values may be used as arguments.

Diff for: tests/init/neg/t3273.check

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
-- Error: tests/init/neg/t3273.scala:4:42 ------------------------------------------------------------------------------
22
4 | val num1: LazyList[Int] = 1 #:: num1.map(_ + 1) // error
33
| ^^^^^^^^^^^^^^^
4-
| Cannot prove that the value is fully initialized. Only initialized values may be used as arguments.
4+
| Cannot prove that the value is fully-initialized. Only initialized values may be used as arguments.
55
|
66
| The unsafe promotion may cause the following problem:
77
| Access non-initialized value num1. Calling trace:
@@ -10,7 +10,7 @@
1010
-- Error: tests/init/neg/t3273.scala:5:61 ------------------------------------------------------------------------------
1111
5 | val num2: LazyList[Int] = 1 #:: num2.iterator.map(_ + 1).to(LazyList) // error
1212
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
13-
| Cannot prove that the value is fully initialized. Only initialized values may be used as arguments.
13+
| Cannot prove that the value is fully-initialized. Only initialized values may be used as arguments.
1414
|
1515
| The unsafe promotion may cause the following problem:
1616
| Access non-initialized value num2. Calling trace:

Diff for: tests/run/lazyVals_c3.0.0.check

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
computing x
2+
x
3+
computing y
4+
y

Diff for: tests/run/lazyVals_c3.0.0.scala

+13
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)

Diff for: tests/run/lazyVals_c3.1.0.check

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
computing x
2+
x
3+
computing y
4+
y

Diff for: tests/run/lazyVals_c3.1.0.scala

+13
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)