Skip to content

Commit 01b11f7

Browse files
committed
Avoid zero-ing out dead fields of primitive value class type
- Zero out fields of type Any - Zero out fields of value class type
1 parent 62f22d4 commit 01b11f7

File tree

3 files changed

+126
-14
lines changed

3 files changed

+126
-14
lines changed

src/main/scala/scala/async/internal/AsyncTransform.scala

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -70,12 +70,11 @@ trait AsyncTransform {
7070
for ((state, flds) <- assignsOf) {
7171
val assigns = flds.map { fld =>
7272
val fieldSym = fld.symbol
73-
val zero = gen.mkZero(fieldSym.info)
7473
Block(
7574
List(
76-
asyncBase.nullOut(global)(Expr[String](Literal(Constant(fieldSym.name.toString))), Expr[Any](zero)).tree
75+
asyncBase.nullOut(global)(Expr[String](Literal(Constant(fieldSym.name.toString))), Expr[Any](Ident(fieldSym))).tree
7776
),
78-
Assign(gen.mkAttributedStableRef(fieldSym.owner.thisType, fieldSym), zero)
77+
Assign(gen.mkAttributedStableRef(fieldSym.owner.thisType, fieldSym), gen.mkZero(fieldSym.info))
7978
)
8079
}
8180
val asyncState = asyncBlock.asyncStates.find(_.state == state).get

src/main/scala/scala/async/internal/LiveVariables.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,10 +49,11 @@ trait LiveVariables {
4949

5050
// determine which fields should be live also at the end (will not be nulled out)
5151
val noNull: Set[Symbol] = liftedSyms.filter { sym =>
52-
liftables.exists { tree =>
52+
sym.tpe.typeSymbol.isPrimitiveValueClass || liftables.exists { tree =>
5353
!liftedSyms.contains(tree.symbol) && tree.exists(_.symbol == sym)
5454
}
5555
}
56+
AsyncUtils.vprintln(s"fields never zero-ed out: ${noNull.mkString(", ")}")
5657

5758
/**
5859
* Traverse statements of an `AsyncState`, collect `Ident`-s refering to lifted fields.

src/test/scala/scala/async/run/live/LiveVariablesSpec.scala

Lines changed: 122 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,30 +11,142 @@ import org.junit.Test
1111
import internal.AsyncTestLV
1212
import AsyncTestLV._
1313

14+
case class Cell[T](v: T)
15+
16+
class Meter(val len: Long) extends AnyVal
17+
18+
case class MCell[T](var v: T)
19+
20+
1421
class LiveVariablesSpec {
1522

1623
@Test
17-
def liveVars1() {
24+
def `zero out fields of reference type`() {
25+
val f = async { Cell(1) }
26+
27+
def m1(x: Cell[Int]): Cell[Int] =
28+
async { Cell(x.v + 1) }
29+
30+
def m2(x: Cell[Int]): String =
31+
async { x.v.toString }
32+
33+
def m3() = async {
34+
val a: Cell[Int] = await(f) // await$1$1
35+
// a == Cell(1)
36+
val b: Cell[Int] = await(m1(a)) // await$2$1
37+
// b == Cell(2)
38+
assert(AsyncTestLV.log.exists(_ == ("await$1$1" -> Cell(1))))
39+
val res = await(m2(b)) // await$3$1
40+
assert(AsyncTestLV.log.exists(_ == ("await$2$1" -> Cell(2))))
41+
res
42+
}
43+
44+
assert(m3() == "2")
45+
}
46+
47+
@Test
48+
def `zero out fields of type Any`() {
49+
val f = async { Cell(1) }
50+
51+
def m1(x: Cell[Int]): Cell[Int] =
52+
async { Cell(x.v + 1) }
53+
54+
def m2(x: Any): String =
55+
async { x.toString }
56+
57+
def m3() = async {
58+
val a: Cell[Int] = await(f) // await$4$1
59+
// a == Cell(1)
60+
val b: Any = await(m1(a)) // await$5$1
61+
// b == Cell(2)
62+
assert(AsyncTestLV.log.exists(_ == ("await$4$1" -> Cell(1))))
63+
val res = await(m2(b)) // await$6$1
64+
assert(AsyncTestLV.log.exists(_ == ("await$5$1" -> Cell(2))))
65+
res
66+
}
67+
68+
assert(m3() == "Cell(2)")
69+
}
70+
71+
@Test
72+
def `do not zero out fields of primitive type`() {
1873
val f = async { 1 }
1974

20-
def m1(x: Int): Int =
21-
async { x + 1 }
75+
def m1(x: Int): Cell[Int] =
76+
async { Cell(x + 1) }
2277

23-
def m2(x: Int): String =
78+
def m2(x: Any): String =
2479
async { x.toString }
2580

2681
def m3() = async {
27-
val a = await(f) // await$1$1
82+
val a: Int = await(f) // await$7$1
2883
// a == 1
29-
val b = await(m1(a)) // await$2$1
30-
// b == 2
31-
assert(AsyncTestLV.log.exists(_ == ("await$1$1" -> 0)))
32-
val res = await(m2(b)) // await$3$1
33-
assert(AsyncTestLV.log.exists(_ == ("await$2$1" -> 0)))
84+
val b: Any = await(m1(a)) // await$8$1
85+
// b == Cell(2)
86+
assert(!AsyncTestLV.log.exists(p => p._1 == "await$7$1"))
87+
val res = await(m2(b)) // await$9$1
88+
assert(AsyncTestLV.log.exists(_ == ("await$8$1" -> Cell(2))))
89+
res
90+
}
91+
92+
assert(m3() == "Cell(2)")
93+
}
94+
95+
@Test
96+
def `zero out fields of value class type`() {
97+
val f = async { Cell(1) }
98+
99+
def m1(x: Cell[Int]): Meter =
100+
async { new Meter(x.v + 1) }
101+
102+
def m2(x: Any): String =
103+
async { x.toString }
104+
105+
def m3() = async {
106+
val a: Cell[Int] = await(f) // await$10$1
107+
// a == Cell(1)
108+
val b: Meter = await(m1(a)) // await$11$1
109+
// b == Meter(2)
110+
assert(AsyncTestLV.log.exists(_ == ("await$10$1" -> Cell(1))))
111+
val res = await(m2(b.len)) // await$12$1
112+
assert(AsyncTestLV.log.exists(entry => entry._1 == "await$11$1" && entry._2.asInstanceOf[Meter].len == 2L))
34113
res
35114
}
36115

37116
assert(m3() == "2")
38117
}
39118

119+
@Test
120+
def `zero out fields after use in loop`() {
121+
val f = async { MCell(1) }
122+
123+
def m1(x: MCell[Int], y: Int): Int =
124+
async { x.v + y }
125+
126+
def m3() = async {
127+
// state #1
128+
val a: MCell[Int] = await(f) // await$13$1
129+
// state #2
130+
var y = MCell(0)
131+
132+
while (a.v < 10) {
133+
// state #4
134+
a.v = a.v + 1
135+
y = MCell(await(a).v + 1) // await$14$1
136+
// state #7
137+
}
138+
139+
// state #3
140+
assert(AsyncTestLV.log.exists(entry => entry._1 == "await$14$1"))
141+
142+
val b = await(m1(a, y.v)) // await$15$1
143+
// state #8
144+
assert(AsyncTestLV.log.exists(_ == ("a$1" -> MCell(10))))
145+
assert(AsyncTestLV.log.exists(_ == ("y$1" -> MCell(11))))
146+
b
147+
}
148+
149+
assert(m3() == 21)
150+
}
151+
40152
}

0 commit comments

Comments
 (0)