Skip to content

Commit 95d4018

Browse files
Fix #971 by updating usages when inlining (#973)
Fixes #971 by: Before inlining, if the block variable for the function is used more than once, change usages for all free variables that are used once to Many. This should be a conservative approximation, then.
1 parent 0d03a87 commit 95d4018

File tree

9 files changed

+86
-0
lines changed

9 files changed

+86
-0
lines changed

effekt/jvm/src/test/scala/effekt/ChezSchemeTests.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ abstract class ChezSchemeTests extends EffektTests {
2626
examplesDir / "pos" / "bidirectional",
2727
examplesDir / "pos" / "object",
2828
examplesDir / "pos" / "type_omission_op.effekt",
29+
examplesDir / "pos" / "issue972.effekt",
2930

3031
// filesystem operations and bytearrays are not yet supported in our Chez backend
3132
examplesDir / "benchmarks" / "input_output" / "word_count_ascii.effekt",

effekt/shared/src/main/scala/effekt/core/optimizer/Normalizer.scala

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,15 @@ object Normalizer { normal =>
187187
case Stmt.App(b, targs, vargs, bargs) =>
188188
active(b) match {
189189
case NormalizedBlock.Known(b: BlockLit, boundBy) if shouldInline(b, boundBy, bargs) =>
190+
val blockUsage = boundBy.flatMap { bv => C.usage.get(bv.id) }.getOrElse(Usage.Once)
191+
if (blockUsage == Usage.Many) {
192+
// This is a conservative approximation:
193+
// Since the block is used more than once, we will use the free variables multiple times
194+
// after inlining.
195+
Variables.free(b).toSet.foreach { v =>
196+
C.usage.put(v.id, C.usage.getOrElse(v.id, Usage.Never) * Usage.Many)
197+
}
198+
}
190199
reduce(b, targs, vargs.map(normalize), bargs.map(normalize))
191200
case normalized =>
192201
Stmt.App(normalized.shared, targs, vargs.map(normalize), bargs.map(normalize))

effekt/shared/src/main/scala/effekt/core/optimizer/Reachable.scala

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,16 @@ enum Usage {
149149
case (Usage.Once, Usage.Many) => Usage.Many
150150
}
151151

152+
def *(other: Usage): Usage = (this, other) match {
153+
case (Usage.Never, other) => Usage.Never
154+
case (other, Usage.Never) => Usage.Never
155+
case (Usage.Once, other) => other
156+
case (other, Usage.Once) => other
157+
case (Usage.Recursive, other) => Usage.Recursive
158+
case (other, Usage.Recursive) => Usage.Recursive
159+
case _ => Usage.Many
160+
}
161+
152162
// -1
153163
def decrement: Usage = this match {
154164
case Usage.Never => Usage.Never

examples/pos/issue971.check

Whitespace-only changes.

examples/pos/issue971.effekt

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
effect bdeff{ body: => Unit }: Unit
2+
3+
def hnd{ body: => Unit / bdeff }: Unit =
4+
try {
5+
body()
6+
} with bdeff {
7+
resume { {b} => b() }
8+
resume { {b} => () }
9+
}
10+
11+
def main() = {
12+
hnd{
13+
do bdeff{
14+
()
15+
}
16+
}
17+
}

examples/pos/issue971_2.check

Whitespace-only changes.

examples/pos/issue971_2.effekt

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
def hnd{ body: => Unit }: Unit = {
2+
def resum{ bod: {=> Unit} => Unit }: Unit = {
3+
bod{body}
4+
}
5+
resum { {b} => b() }
6+
resum { {b} => () }
7+
}
8+
def main() = {
9+
hnd{
10+
()
11+
}
12+
}

examples/pos/issue972.check

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
12
2+
5
3+
7
4+
5
5+
12

examples/pos/issue972.effekt

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
effect exit(): Unit
2+
effect locally { body: => Unit }: Unit
3+
4+
def scope { body: => Unit / locally }: Unit =
5+
try {
6+
body()
7+
} with locally {
8+
try {
9+
resume { {b} =>
10+
b()
11+
exit.exit()
12+
}
13+
} with exit: exit { () }
14+
resume { {b} => if (false) b() } // should be just (), blocked on #971
15+
}
16+
17+
def main() = {
18+
scope {
19+
var x = 12
20+
println(x)
21+
do locally {
22+
x = 5
23+
println(x)
24+
do locally {
25+
x = 7
26+
println(x)
27+
}
28+
println(x)
29+
}
30+
println(x)
31+
}
32+
}

0 commit comments

Comments
 (0)