Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit ca72b94

Browse files
olsdavisszymon-rd
authored andcommittedMay 17, 2022
New lazy vals implementation
1 parent ee5bab9 commit ca72b94

File tree

11 files changed

+394
-25
lines changed

11 files changed

+394
-25
lines changed
 

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

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

‎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)

‎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

‎library/src/scala/runtime/LazyVals.scala

+57
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package scala.runtime
22

3+
import java.lang.reflect.Modifier
34
import scala.annotation.since
45

56
/**
@@ -24,6 +25,7 @@ object LazyVals {
2425
val processors = java.lang.Runtime.getRuntime.nn.availableProcessors()
2526
8 * processors * processors
2627
}
28+
2729
private[this] val monitors: Array[Object] =
2830
Array.tabulate(base)(_ => new Object)
2931

@@ -39,6 +41,41 @@ object LazyVals {
3941

4042
/* ------------- Start of public API ------------- */
4143

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+
4279
final val BITS_PER_LAZY_VAL = 2L
4380

4481
def STATE(cur: Long, ord: Int): Long = {
@@ -56,6 +93,12 @@ object LazyVals {
5693
unsafe.compareAndSwapLong(t, offset, e, n)
5794
}
5895

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+
59102
def setFlag(t: Object, offset: Long, v: Int, ord: Int): Unit = {
60103
if (debug)
61104
println(s"setFlag($t, $offset, $v, $ord)")
@@ -108,6 +151,13 @@ object LazyVals {
108151
r
109152
}
110153

154+
def getStaticOffset(clz: Class[_], name: String): Long = {
155+
val r = unsafe.staticFieldOffset(clz.getDeclaredField(name))
156+
if (debug)
157+
println(s"getStaticOffset($clz, $name) = $r")
158+
r
159+
}
160+
111161
@since("3.2")
112162
def getOffsetStatic(field: java.lang.reflect.Field) =
113163
val r = unsafe.objectFieldOffset(field)
@@ -117,11 +167,18 @@ object LazyVals {
117167

118168

119169
object Names {
170+
final val waiting = "Waiting"
171+
final val evaluating = "Evaluating"
172+
final val nullValued = "NULL"
173+
final val waitingAwaitRelease = "awaitRelease"
174+
final val waitingRelease = "release"
120175
final val state = "STATE"
121176
final val cas = "CAS"
177+
final val objCas = "objCAS"
122178
final val setFlag = "setFlag"
123179
final val wait4Notification = "wait4Notification"
124180
final val get = "get"
125181
final val getOffset = "getOffset"
182+
final val getStaticOffset = "getStaticOffset"
126183
}
127184
}

‎project/MiMaFilters.scala

+22
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,32 @@ object MiMaFilters {
99
ProblemFilters.exclude[ReversedMissingMethodProblem]("scala.quoted.runtime.QuoteUnpickler.unpickleTypeV2"),
1010
ProblemFilters.exclude[DirectMissingMethodProblem]("scala.quoted.runtime.QuoteUnpickler.unpickleTypeV2"),
1111

12+
13+
// Private to the compiler - needed for forward binary compatibility
14+
ProblemFilters.exclude[MissingClassProblem]("scala.annotation.since"),
15+
16+
ProblemFilters.exclude[DirectMissingMethodProblem]("scala.runtime.LazyVals.getStaticOffset"),
17+
ProblemFilters.exclude[DirectMissingMethodProblem]("scala.runtime.LazyVals.getStaticOffset"),
18+
ProblemFilters.exclude[DirectMissingMethodProblem]("scala.runtime.LazyVals.objCAS"),
19+
ProblemFilters.exclude[DirectMissingMethodProblem]("scala.runtime.LazyVals.objCAS"),
20+
ProblemFilters.exclude[DirectMissingMethodProblem]("scala.runtime.LazyVals#Names.evaluating"),
21+
ProblemFilters.exclude[DirectMissingMethodProblem]("scala.runtime.LazyVals#Names.getStaticOffset"),
22+
ProblemFilters.exclude[DirectMissingMethodProblem]("scala.runtime.LazyVals#Names.nullValued"),
23+
ProblemFilters.exclude[DirectMissingMethodProblem]("scala.runtime.LazyVals#Names.objCas"),
24+
ProblemFilters.exclude[DirectMissingMethodProblem]("scala.runtime.LazyVals#Names.waiting"),
25+
ProblemFilters.exclude[DirectMissingMethodProblem]("scala.runtime.LazyVals#Names.waitingAwaitRelease"),
26+
ProblemFilters.exclude[DirectMissingMethodProblem]("scala.runtime.LazyVals#Names.waitingRelease"),
27+
ProblemFilters.exclude[MissingClassProblem]("scala.runtime.LazyVals$Evaluating$"),
28+
ProblemFilters.exclude[MissingClassProblem]("scala.runtime.LazyVals$NULL$"),
29+
ProblemFilters.exclude[MissingClassProblem]("scala.runtime.LazyVals$Waiting"),
30+
ProblemFilters.exclude[MissingFieldProblem]("scala.runtime.LazyVals.Evaluating"),
31+
ProblemFilters.exclude[MissingFieldProblem]("scala.runtime.LazyVals.NULL"),
32+
1233
// Experimental `MainAnnotation` APIs. Can be added in 3.3.0 or later.
1334
// MiMa bug: classes nested in an experimental object should be ignored
1435
ProblemFilters.exclude[MissingClassProblem]("scala.annotation.MainAnnotation$Info"),
1536
ProblemFilters.exclude[MissingClassProblem]("scala.annotation.MainAnnotation$Parameter"),
1637
ProblemFilters.exclude[MissingClassProblem]("scala.annotation.MainAnnotation$ParameterAnnotation"),
38+
1739
)
1840
}

‎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.

‎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:

‎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

‎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)

‎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

‎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)
Please sign in to comment.