IR: check whether local declarations need to throw after Nothing return
^KT-48982 Fixed
This commit is contained in:
+7
@@ -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"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user