[JVM] Split the variable range for external finally blocks in inliner
When external finally blocks are inlined none of the current locals are in scope of the finally block.
This commit is contained in:
committed by
Mikhael Bogdanov
parent
6d9f02cfc6
commit
6095d8a7fa
@@ -320,8 +320,7 @@ abstract class InlineCodegen<out T : BaseExpressionCodegen>(
|
||||
|
||||
val splitBy = SimpleInterval(start.info as LabelNode, extension.finallyIntervalEnd)
|
||||
processor.tryBlocksMetaInfo.splitAndRemoveCurrentIntervals(splitBy, true)
|
||||
|
||||
//processor.getLocalVarsMetaInfo().splitAndRemoveIntervalsFromCurrents(splitBy);
|
||||
processor.localVarsMetaInfo.splitAndRemoveCurrentIntervals(splitBy, true);
|
||||
|
||||
mark.dropTo()
|
||||
}
|
||||
@@ -330,8 +329,7 @@ abstract class InlineCodegen<out T : BaseExpressionCodegen>(
|
||||
}
|
||||
|
||||
processor.substituteTryBlockNodes(intoNode)
|
||||
|
||||
//processor.substituteLocalVarTable(intoNode);
|
||||
processor.substituteLocalVarTable(intoNode);
|
||||
}
|
||||
|
||||
protected abstract fun generateAssertFieldIfNeeded(info: RootInliningContext)
|
||||
|
||||
+39
-20
@@ -87,9 +87,27 @@ class TryWithFinallyInfo(val onExit: IrExpression) : TryInfo()
|
||||
class BlockInfo(val parent: BlockInfo? = null) {
|
||||
val variables = mutableListOf<VariableInfo>()
|
||||
val infos: Stack<ExpressionInfo> = parent?.infos ?: Stack()
|
||||
var activeLocalGaps = 0
|
||||
|
||||
fun hasFinallyBlocks(): Boolean = infos.firstIsInstanceOrNull<TryWithFinallyInfo>() != null
|
||||
|
||||
internal inline fun forEachBlockUntil(tryWithFinallyInfo: TryWithFinallyInfo, onBlock: BlockInfo.() -> Unit) {
|
||||
var current: BlockInfo? = this
|
||||
while (current != null && current != tryWithFinallyInfo.blockInfo) {
|
||||
current.onBlock()
|
||||
current = current.parent
|
||||
}
|
||||
}
|
||||
|
||||
internal inline fun localGapScope(tryWithFinallyInfo: TryWithFinallyInfo, block: () -> Unit) {
|
||||
forEachBlockUntil(tryWithFinallyInfo) { ++activeLocalGaps }
|
||||
try {
|
||||
block()
|
||||
} finally {
|
||||
forEachBlockUntil(tryWithFinallyInfo) { --activeLocalGaps }
|
||||
}
|
||||
}
|
||||
|
||||
internal inline fun <T : ExpressionInfo, R> withBlock(info: T, f: (T) -> R): R {
|
||||
info.blockInfo = this
|
||||
infos.add(info)
|
||||
@@ -411,14 +429,15 @@ class ExpressionCodegen(
|
||||
gapStart: Label,
|
||||
restartLabel: Label
|
||||
) {
|
||||
var current: BlockInfo? = info
|
||||
while (current != null && current != tryWithFinallyInfo.blockInfo) {
|
||||
for (variable in current.variables) {
|
||||
if (variable.declaration.isVisibleInLVT) {
|
||||
variable.gaps.add(Gap(gapStart, restartLabel))
|
||||
info.forEachBlockUntil(tryWithFinallyInfo) {
|
||||
// If we are already in a gap do not add a new one.
|
||||
if (activeLocalGaps == 0) {
|
||||
for (variable in variables) {
|
||||
if (variable.declaration.isVisibleInLVT) {
|
||||
variable.gaps.add(Gap(gapStart, restartLabel))
|
||||
}
|
||||
}
|
||||
}
|
||||
current = current.parent
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1239,21 +1258,21 @@ class ExpressionCodegen(
|
||||
nestedTryWithoutFinally: MutableList<TryInfo> = arrayListOf()
|
||||
) {
|
||||
val gapStart = markNewLinkedLabel()
|
||||
|
||||
finallyDepth++
|
||||
if (isFinallyMarkerRequired()) {
|
||||
generateFinallyMarker(mv, finallyDepth, true)
|
||||
data.localGapScope(tryWithFinallyInfo) {
|
||||
finallyDepth++
|
||||
if (isFinallyMarkerRequired()) {
|
||||
generateFinallyMarker(mv, finallyDepth, true)
|
||||
}
|
||||
tryWithFinallyInfo.onExit.accept(this, data).discard()
|
||||
if (isFinallyMarkerRequired()) {
|
||||
generateFinallyMarker(mv, finallyDepth, false)
|
||||
}
|
||||
finallyDepth--
|
||||
if (tryCatchBlockEnd != null) {
|
||||
tryWithFinallyInfo.onExit.markLineNumber(startOffset = false)
|
||||
mv.goTo(tryCatchBlockEnd)
|
||||
}
|
||||
}
|
||||
tryWithFinallyInfo.onExit.accept(this, data).discard()
|
||||
if (isFinallyMarkerRequired()) {
|
||||
generateFinallyMarker(mv, finallyDepth, false)
|
||||
}
|
||||
finallyDepth--
|
||||
if (tryCatchBlockEnd != null) {
|
||||
tryWithFinallyInfo.onExit.markLineNumber(startOffset = false)
|
||||
mv.goTo(tryCatchBlockEnd)
|
||||
}
|
||||
|
||||
// Split the local variables for the blocks on the way to the finally. Variables introduced in these blocks do not
|
||||
// cover the finally block code.
|
||||
val endOfFinallyCode = markNewLinkedLabel()
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
// FILE: test.kt
|
||||
|
||||
inline fun f(block: () -> Unit) {
|
||||
try {
|
||||
val z = 32
|
||||
for (j in 0 until 1) {
|
||||
return
|
||||
}
|
||||
} finally {
|
||||
block()
|
||||
}
|
||||
}
|
||||
|
||||
var x: String = ""
|
||||
|
||||
fun compute(): String {
|
||||
try {
|
||||
val y = 42
|
||||
for (i in 0 until 1) {
|
||||
f {
|
||||
return "NON_LOCAL_RETURN"
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
val s2 = "OK"
|
||||
x = s2 // TODO: Why is `s2` not visible here?
|
||||
}
|
||||
return "FAIL"
|
||||
}
|
||||
|
||||
fun box() {
|
||||
val result = compute()
|
||||
val localX = x
|
||||
}
|
||||
|
||||
// The local variables `y` and `i` are visible in the finally block with old backend.
|
||||
// IGNORE_BACKEND: JVM
|
||||
|
||||
// LOCAL VARIABLES
|
||||
// test.kt:32 box:
|
||||
// test.kt:17 compute:
|
||||
// test.kt:18 compute:
|
||||
// test.kt:19 compute: y:int=42:int
|
||||
// test.kt:20 compute: y:int=42:int, i:int=0:int
|
||||
// test.kt:4 compute: y:int=42:int, i:int=0:int, $i$f$f:int=0:int
|
||||
// test.kt:5 compute: y:int=42:int, i:int=0:int, $i$f$f:int=0:int
|
||||
// test.kt:6 compute: y:int=42:int, i:int=0:int, $i$f$f:int=0:int, z$iv:int=32:int
|
||||
// test.kt:7 compute: y:int=42:int, i:int=0:int, $i$f$f:int=0:int, z$iv:int=32:int, j$iv:int=0:int
|
||||
// test.kt:10 compute: y:int=42:int, i:int=0:int, $i$f$f:int=0:int
|
||||
// test.kt:21 compute: y:int=42:int, i:int=0:int, $i$f$f:int=0:int, $i$a$-f-TestKt$compute$1:int=0:int
|
||||
// test.kt:25 compute:
|
||||
// test.kt:26 compute:
|
||||
// test.kt:32 box:
|
||||
// test.kt:33 box: result:java.lang.String="NON_LOCAL_RETURN":java.lang.String
|
||||
// test.kt:34 box: result:java.lang.String="NON_LOCAL_RETURN":java.lang.String, localX:java.lang.String="OK":java.lang.String
|
||||
+2
-6
@@ -26,13 +26,9 @@ fun box() {
|
||||
val localX = x
|
||||
}
|
||||
|
||||
// JVM_IR backend correctly gets rid of the `a` local in the finally, but the inliner
|
||||
// does not get rid of `b` or the inlining locals.
|
||||
// JVM backend has the `a` local covering the finally block. It shouldn't.
|
||||
|
||||
// JVM backend does not get rid of `a` and also the inliner does not get rid of `b`
|
||||
// or the inlining locals.
|
||||
|
||||
// IGNORE_BACKEND: JVM_IR, JVM
|
||||
// IGNORE_BACKEND: JVM
|
||||
|
||||
// LOCAL VARIABLES
|
||||
// test.kt:25 box:
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
// FILE: test.kt
|
||||
|
||||
inline fun h(block: () -> Unit) {
|
||||
try {
|
||||
val hLocal = "hLocal"
|
||||
block()
|
||||
} finally {
|
||||
val h = "h"
|
||||
}
|
||||
}
|
||||
|
||||
inline fun g(block: () -> Unit) {
|
||||
try {
|
||||
val gLocal = "gLocal"
|
||||
h(block)
|
||||
} finally {
|
||||
val g = "g"
|
||||
}
|
||||
}
|
||||
|
||||
var x: String? = null
|
||||
|
||||
fun compute(): String {
|
||||
try {
|
||||
for (a in listOf("a")) {
|
||||
g {
|
||||
for (b in listOf("b")) {
|
||||
return b
|
||||
}
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
x = "OK"
|
||||
}
|
||||
return "FAIL"
|
||||
}
|
||||
|
||||
fun box() {
|
||||
val result = compute()
|
||||
val localX = x
|
||||
}
|
||||
|
||||
// JVM backend has `a` visible in the `compute` finally block. It shouldn't be.
|
||||
// IGNORE_BACKEND: JVM
|
||||
|
||||
// LOCAL VARIABLES
|
||||
// test.kt:39 box:
|
||||
// test.kt:24 compute:
|
||||
// test.kt:25 compute:
|
||||
// test.kt:26 compute: a:java.lang.String="a":java.lang.String
|
||||
// test.kt:13 compute: a:java.lang.String="a":java.lang.String, $i$f$g:int=0:int
|
||||
// test.kt:14 compute: a:java.lang.String="a":java.lang.String, $i$f$g:int=0:int
|
||||
// test.kt:15 compute: a:java.lang.String="a":java.lang.String, $i$f$g:int=0:int, gLocal$iv:java.lang.String="gLocal":java.lang.String
|
||||
// test.kt:4 compute: a:java.lang.String="a":java.lang.String, $i$f$g:int=0:int, gLocal$iv:java.lang.String="gLocal":java.lang.String, $i$f$h:int=0:int
|
||||
// test.kt:5 compute: a:java.lang.String="a":java.lang.String, $i$f$g:int=0:int, gLocal$iv:java.lang.String="gLocal":java.lang.String, $i$f$h:int=0:int
|
||||
// test.kt:6 compute: a:java.lang.String="a":java.lang.String, $i$f$g:int=0:int, gLocal$iv:java.lang.String="gLocal":java.lang.String, $i$f$h:int=0:int, hLocal$iv$iv:java.lang.String="hLocal":java.lang.String
|
||||
// test.kt:27 compute: a:java.lang.String="a":java.lang.String, $i$f$g:int=0:int, gLocal$iv:java.lang.String="gLocal":java.lang.String, $i$f$h:int=0:int, hLocal$iv$iv:java.lang.String="hLocal":java.lang.String, $i$a$-g-TestKt$compute$1:int=0:int
|
||||
// test.kt:28 compute: a:java.lang.String="a":java.lang.String, $i$f$g:int=0:int, gLocal$iv:java.lang.String="gLocal":java.lang.String, $i$f$h:int=0:int, hLocal$iv$iv:java.lang.String="hLocal":java.lang.String, $i$a$-g-TestKt$compute$1:int=0:int, b:java.lang.String="b":java.lang.String
|
||||
// test.kt:8 compute: a:java.lang.String="a":java.lang.String
|
||||
// test.kt:17 compute: a:java.lang.String="a":java.lang.String
|
||||
// test.kt:33 compute:
|
||||
// test.kt:39 box:
|
||||
// test.kt:40 box: result:java.lang.String="b":java.lang.String
|
||||
// test.kt:41 box: result:java.lang.String="b":java.lang.String, localX:java.lang.String="OK":java.lang.String
|
||||
@@ -0,0 +1,51 @@
|
||||
// FILE: test.kt
|
||||
|
||||
inline fun f(block: () -> Unit) {
|
||||
block()
|
||||
}
|
||||
|
||||
var x: String = ""
|
||||
|
||||
fun compute(): String {
|
||||
try {
|
||||
val y = 42
|
||||
for (i in 0 until 1) {
|
||||
throw RuntimeException("$y $i")
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
val y = 32
|
||||
for (j in 0 until 1) {
|
||||
f {
|
||||
return "NON_LOCAL_RETURN"
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
x = "OK"
|
||||
}
|
||||
return "FAIL"
|
||||
}
|
||||
|
||||
fun box() {
|
||||
val result = compute()
|
||||
val localX = x
|
||||
}
|
||||
|
||||
// The old backend has `y` and `j` visible on the finally block.
|
||||
// IGNORE_BACKEND: JVM
|
||||
|
||||
// LOCAL VARIABLES
|
||||
// test.kt:29 box:
|
||||
// test.kt:10 compute:
|
||||
// test.kt:11 compute:
|
||||
// test.kt:12 compute: y:int=42:int
|
||||
// test.kt:13 compute: y:int=42:int, i:int=0:int
|
||||
// test.kt:15 compute:
|
||||
// test.kt:16 compute: e:java.lang.Exception=java.lang.RuntimeException
|
||||
// test.kt:17 compute: e:java.lang.Exception=java.lang.RuntimeException, y:int=32:int
|
||||
// test.kt:18 compute: e:java.lang.Exception=java.lang.RuntimeException, y:int=32:int, j:int=0:int
|
||||
// test.kt:4 compute: e:java.lang.Exception=java.lang.RuntimeException, y:int=32:int, j:int=0:int, $i$f$f:int=0:int
|
||||
// test.kt:19 compute: e:java.lang.Exception=java.lang.RuntimeException, y:int=32:int, j:int=0:int, $i$f$f:int=0:int, $i$a$-f-TestKt$compute$1:int=0:int
|
||||
// test.kt:23 compute: e:java.lang.Exception=java.lang.RuntimeException
|
||||
// test.kt:29 box:
|
||||
// test.kt:30 box: result:java.lang.String="NON_LOCAL_RETURN":java.lang.String
|
||||
// test.kt:31 box: result:java.lang.String="NON_LOCAL_RETURN":java.lang.String, localX:java.lang.String="OK":java.lang.String
|
||||
@@ -0,0 +1,54 @@
|
||||
// FILE: test.kt
|
||||
|
||||
inline fun f(block: () -> Unit) {
|
||||
try {
|
||||
val z = 32
|
||||
for (j in 0 until 1) {
|
||||
throw RuntimeException("$z $j")
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
block()
|
||||
}
|
||||
}
|
||||
|
||||
var x: String = ""
|
||||
|
||||
fun compute(): String {
|
||||
try {
|
||||
val y = 42
|
||||
for (i in 0 until 1) {
|
||||
f {
|
||||
return "NON_LOCAL_RETURN"
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
x = "OK"
|
||||
}
|
||||
return "FAIL"
|
||||
}
|
||||
|
||||
fun box() {
|
||||
val result = compute()
|
||||
val localX = x
|
||||
}
|
||||
|
||||
// The old backend has `y` and `i` visible on the finally block.
|
||||
// IGNORE_BACKEND: JVM
|
||||
|
||||
// LOCAL VARIABLES
|
||||
// test.kt:31 box:
|
||||
// test.kt:17 compute:
|
||||
// test.kt:18 compute:
|
||||
// test.kt:19 compute: y:int=42:int
|
||||
// test.kt:20 compute: y:int=42:int, i:int=0:int
|
||||
// test.kt:4 compute: y:int=42:int, i:int=0:int, $i$f$f:int=0:int
|
||||
// test.kt:5 compute: y:int=42:int, i:int=0:int, $i$f$f:int=0:int
|
||||
// test.kt:6 compute: y:int=42:int, i:int=0:int, $i$f$f:int=0:int, z$iv:int=32:int
|
||||
// test.kt:7 compute: y:int=42:int, i:int=0:int, $i$f$f:int=0:int, z$iv:int=32:int, j$iv:int=0:int
|
||||
// test.kt:9 compute: y:int=42:int, i:int=0:int, $i$f$f:int=0:int
|
||||
// test.kt:10 compute: y:int=42:int, i:int=0:int, $i$f$f:int=0:int, e$iv:java.lang.Exception=java.lang.RuntimeException
|
||||
// test.kt:21 compute: y:int=42:int, i:int=0:int, $i$f$f:int=0:int, e$iv:java.lang.Exception=java.lang.RuntimeException, $i$a$-f-TestKt$compute$1:int=0:int
|
||||
// test.kt:25 compute:
|
||||
// test.kt:31 box:
|
||||
// test.kt:32 box: result:java.lang.String="NON_LOCAL_RETURN":java.lang.String
|
||||
// test.kt:33 box: result:java.lang.String="NON_LOCAL_RETURN":java.lang.String, localX:java.lang.String="OK":java.lang.String
|
||||
@@ -0,0 +1,54 @@
|
||||
// FILE: test.kt
|
||||
|
||||
inline fun f(block: () -> Unit) {
|
||||
block()
|
||||
}
|
||||
|
||||
var x: String = ""
|
||||
|
||||
fun compute(): String {
|
||||
try {
|
||||
try {
|
||||
val y = 42
|
||||
for (i in 0 until 1) {
|
||||
return "NORMAL_RETURN"
|
||||
}
|
||||
} finally {
|
||||
var s = "NOPE"
|
||||
x = s
|
||||
f {
|
||||
return "NON_LOCAL_RETURN"
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
val s2 = "OK"
|
||||
x = s2 // TODO: Why is `s2` not visible here?
|
||||
}
|
||||
return "FAIL"
|
||||
}
|
||||
|
||||
fun box() {
|
||||
val result = compute()
|
||||
val localX = x
|
||||
}
|
||||
|
||||
// The local variables in the try and finally blocks are not removed for the finally block with the old backend.
|
||||
// IGNORE_BACKEND: JVM
|
||||
|
||||
// LOCAL VARIABLES
|
||||
// test.kt:31 box:
|
||||
// test.kt:10 compute:
|
||||
// test.kt:11 compute:
|
||||
// test.kt:12 compute:
|
||||
// test.kt:13 compute: y:int=42:int
|
||||
// test.kt:14 compute: y:int=42:int, i:int=0:int
|
||||
// test.kt:17 compute:
|
||||
// test.kt:18 compute: s:java.lang.String="NOPE":java.lang.String
|
||||
// test.kt:19 compute: s:java.lang.String="NOPE":java.lang.String
|
||||
// test.kt:4 compute: s:java.lang.String="NOPE":java.lang.String, $i$f$f:int=0:int
|
||||
// test.kt:20 compute: s:java.lang.String="NOPE":java.lang.String, $i$f$f:int=0:int, $i$a$-f-TestKt$compute$1:int=0:int
|
||||
// test.kt:24 compute:
|
||||
// test.kt:25 compute:
|
||||
// test.kt:31 box:
|
||||
// test.kt:32 box: result:java.lang.String="NON_LOCAL_RETURN":java.lang.String
|
||||
// test.kt:33 box: result:java.lang.String="NON_LOCAL_RETURN":java.lang.String, localX:java.lang.String="OK":java.lang.String
|
||||
Generated
+30
@@ -92,6 +92,12 @@ public class IrLocalVariableTestGenerated extends AbstractIrLocalVariableTest {
|
||||
runTest("compiler/testData/debug/localVariables/tryFinally.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("tryFinally10.kt")
|
||||
public void testTryFinally10() throws Exception {
|
||||
runTest("compiler/testData/debug/localVariables/tryFinally10.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("tryFinally2.kt")
|
||||
public void testTryFinally2() throws Exception {
|
||||
@@ -116,6 +122,30 @@ public class IrLocalVariableTestGenerated extends AbstractIrLocalVariableTest {
|
||||
runTest("compiler/testData/debug/localVariables/tryFinally5.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("tryFinally6.kt")
|
||||
public void testTryFinally6() throws Exception {
|
||||
runTest("compiler/testData/debug/localVariables/tryFinally6.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("tryFinally7.kt")
|
||||
public void testTryFinally7() throws Exception {
|
||||
runTest("compiler/testData/debug/localVariables/tryFinally7.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("tryFinally8.kt")
|
||||
public void testTryFinally8() throws Exception {
|
||||
runTest("compiler/testData/debug/localVariables/tryFinally8.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("tryFinally9.kt")
|
||||
public void testTryFinally9() throws Exception {
|
||||
runTest("compiler/testData/debug/localVariables/tryFinally9.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("underscoreNames.kt")
|
||||
public void testUnderscoreNames() throws Exception {
|
||||
|
||||
Generated
+30
@@ -92,6 +92,12 @@ public class LocalVariableTestGenerated extends AbstractLocalVariableTest {
|
||||
runTest("compiler/testData/debug/localVariables/tryFinally.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("tryFinally10.kt")
|
||||
public void testTryFinally10() throws Exception {
|
||||
runTest("compiler/testData/debug/localVariables/tryFinally10.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("tryFinally2.kt")
|
||||
public void testTryFinally2() throws Exception {
|
||||
@@ -116,6 +122,30 @@ public class LocalVariableTestGenerated extends AbstractLocalVariableTest {
|
||||
runTest("compiler/testData/debug/localVariables/tryFinally5.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("tryFinally6.kt")
|
||||
public void testTryFinally6() throws Exception {
|
||||
runTest("compiler/testData/debug/localVariables/tryFinally6.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("tryFinally7.kt")
|
||||
public void testTryFinally7() throws Exception {
|
||||
runTest("compiler/testData/debug/localVariables/tryFinally7.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("tryFinally8.kt")
|
||||
public void testTryFinally8() throws Exception {
|
||||
runTest("compiler/testData/debug/localVariables/tryFinally8.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("tryFinally9.kt")
|
||||
public void testTryFinally9() throws Exception {
|
||||
runTest("compiler/testData/debug/localVariables/tryFinally9.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("underscoreNames.kt")
|
||||
public void testUnderscoreNames() throws Exception {
|
||||
|
||||
Reference in New Issue
Block a user