IR: check whether local declarations need to throw after Nothing return

^KT-48982 Fixed
This commit is contained in:
pyos
2021-10-06 13:56:04 +02:00
committed by Ilmir Usmanov
parent a213dad9ab
commit 226306ac83
2 changed files with 24 additions and 4 deletions
@@ -7,8 +7,10 @@ package org.jetbrains.kotlin.backend.common.lower
import org.jetbrains.kotlin.backend.common.BodyLoweringPass
import org.jetbrains.kotlin.backend.common.CommonBackendContext
import org.jetbrains.kotlin.backend.common.runOnFilePostfix
import org.jetbrains.kotlin.ir.builders.irCall
import org.jetbrains.kotlin.ir.declarations.IrDeclaration
import org.jetbrains.kotlin.ir.declarations.IrFile
import org.jetbrains.kotlin.ir.expressions.IrBody
import org.jetbrains.kotlin.ir.expressions.IrCall
import org.jetbrains.kotlin.ir.expressions.IrExpression
@@ -20,6 +22,9 @@ import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
class KotlinNothingValueExceptionLowering(
val backendContext: CommonBackendContext, val skip: (IrDeclaration) -> Boolean = { false }
) : BodyLoweringPass {
override fun lower(irFile: IrFile) =
runOnFilePostfix(irFile, withLocalDeclarations = true)
override fun lower(irBody: IrBody, container: IrDeclaration) {
if (!skip(container)) {
irBody.transformChildrenVoid(Transformer(container.symbol))
@@ -27,6 +32,8 @@ class KotlinNothingValueExceptionLowering(
}
private inner class Transformer(val parent: IrSymbol) : IrElementTransformerVoid() {
override fun visitBody(body: IrBody): IrBody = body
override fun visitCall(expression: IrCall): IrExpression =
if (expression.type.isNothing()) {
// Replace call 'foo' of type 'kotlin.Nothing' with a block:
@@ -5,12 +5,14 @@ import helpers.*
import kotlin.coroutines.*
import kotlin.coroutines.intrinsics.*
class Success : RuntimeException()
suspend fun suspendThenThrow(): Nothing {
suspendCoroutineUninterceptedOrReturn<Unit> {
it.resume(Unit)
COROUTINE_SUSPENDED
}
throw RuntimeException()
throw Success()
}
suspend fun foo(x: Int = 1): Nothing = suspendThenThrow()
@@ -26,9 +28,20 @@ class C : I by (object : I {
var result = ""
fun box(): String {
var counter = 0
suspend {
try { foo() } catch (e: RuntimeException) { result += "O" }
try { C().bar() } catch (e: RuntimeException) { result += "K" }
// 1. foo$default is a bridge to foo, should not throw after foo returns
try { foo() } catch (e: Success) { counter++ }
// 2. C.bar is a delegation to another method, so same
try { C().bar() } catch (e: Success) { counter++ }
// 3. object.foo$default is the same as 1 but in a local object
try {
object {
suspend fun foo(x: Int = 1): Nothing = suspendThenThrow()
}.foo()
} catch (e: Success) { counter++ }
// 4. this point should be reached exactly once
counter++
}.startCoroutine(EmptyContinuation)
return result
return if (counter == 4) "OK" else "counter incremented $counter times"
}