diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/CoroutineTransformerMethodVisitor.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/CoroutineTransformerMethodVisitor.kt index 8f00ce1fa87..def31a40544 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/CoroutineTransformerMethodVisitor.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/CoroutineTransformerMethodVisitor.kt @@ -643,7 +643,7 @@ class CoroutineTransformerMethodVisitor( val value = frame.getLocal(slot) if (value.type == null || !livenessFrame.isAlive(slot)) continue - if (value == StrictBasicValue.NULL_VALUE) { + if (value == StrictBasicValue.NULL_VALUE || value is TypedNullValue) { referencesToSpill += slot to null continue } diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/SpilledVariableFieldTypesAnalysis.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/SpilledVariableFieldTypesAnalysis.kt index 8ac216224b9..fcb282ab2a3 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/SpilledVariableFieldTypesAnalysis.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/SpilledVariableFieldTypesAnalysis.kt @@ -8,6 +8,7 @@ package org.jetbrains.kotlin.codegen.coroutines import org.jetbrains.kotlin.codegen.StackValue import org.jetbrains.kotlin.codegen.optimization.common.FastMethodAnalyzer import org.jetbrains.kotlin.codegen.optimization.common.OptimizationBasicInterpreter +import org.jetbrains.kotlin.codegen.optimization.common.StrictBasicValue import org.jetbrains.org.objectweb.asm.Label import org.jetbrains.org.objectweb.asm.Opcodes import org.jetbrains.org.objectweb.asm.Type @@ -134,7 +135,7 @@ internal fun performSpilledVariableFieldTypesAnalysis( for ((insn, type) in interpreter.needsToBeCoerced) { methodNode.instructions.insert(insn, withInstructionAdapter { coerceInt(type, this) }) } - return FastMethodAnalyzer(thisName, methodNode, OptimizationBasicInterpreter()).analyze() + return FastMethodAnalyzer(thisName, methodNode, NullCheckcastAwareOptimizationBasicInterpreter()).analyze() } private fun coerceInt(to: Type, v: InstructionAdapter) { @@ -157,4 +158,17 @@ private fun coerceInt(to: Type, v: InstructionAdapter) { private fun Type.isIntLike(): Boolean = when (sort) { Type.BOOLEAN, Type.BYTE, Type.CHAR, Type.SHORT -> true else -> false +} + +// Represents [ACONST_NULL, CHECKCAST Type] sequence result. +internal class TypedNullValue(type: Type) : BasicValue(type) + +// Preserves nulls through CHECKCASTS. +private class NullCheckcastAwareOptimizationBasicInterpreter : OptimizationBasicInterpreter() { + override fun unaryOperation(insn: AbstractInsnNode, value: BasicValue?): BasicValue? { + if (insn.opcode == Opcodes.CHECKCAST && (value == StrictBasicValue.NULL_VALUE || value is TypedNullValue)) { + return TypedNullValue(Type.getObjectType((insn as TypeInsnNode).desc)) + } + return super.unaryOperation(insn, value) + } } \ No newline at end of file diff --git a/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirBlackBoxCodegenTestGenerated.java b/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirBlackBoxCodegenTestGenerated.java index f42afd1c4b0..e7b2f5fd503 100644 --- a/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirBlackBoxCodegenTestGenerated.java +++ b/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirBlackBoxCodegenTestGenerated.java @@ -10191,6 +10191,12 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT runTest("compiler/testData/codegen/box/coroutines/kt50277.kt"); } + @Test + @TestMetadata("kt51718.kt") + public void testKt51718() throws Exception { + runTest("compiler/testData/codegen/box/coroutines/kt51718.kt"); + } + @Test @TestMetadata("lastExpressionIsLoop.kt") public void testLastExpressionIsLoop() throws Exception { diff --git a/compiler/ir/backend.jvm/codegen/src/org/jetbrains/kotlin/backend/jvm/codegen/ExpressionCodegen.kt b/compiler/ir/backend.jvm/codegen/src/org/jetbrains/kotlin/backend/jvm/codegen/ExpressionCodegen.kt index 646f1e3d025..b49cd7f6721 100644 --- a/compiler/ir/backend.jvm/codegen/src/org/jetbrains/kotlin/backend/jvm/codegen/ExpressionCodegen.kt +++ b/compiler/ir/backend.jvm/codegen/src/org/jetbrains/kotlin/backend/jvm/codegen/ExpressionCodegen.kt @@ -45,8 +45,7 @@ import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.ir.visitors.IrElementVisitor import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.resolve.jvm.AsmTypes -import org.jetbrains.kotlin.resolve.jvm.AsmTypes.JAVA_STRING_TYPE -import org.jetbrains.kotlin.resolve.jvm.AsmTypes.OBJECT_TYPE +import org.jetbrains.kotlin.resolve.jvm.AsmTypes.* import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodParameterKind import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature import org.jetbrains.kotlin.types.TypeSystemCommonBackendContext @@ -653,6 +652,18 @@ class ExpressionCodegen( val value = initializer.accept(this, data) initializer.markLineNumber(startOffset = true) value.materializeAt(varType, declaration.type) + // We need to generate CHECKCAST from ACONST_NULL here for coroutines, + // since otherwise, upon spilling and then unspilling, we will get VerifyError, + // because state-machine builder does not know the type of the ACONST_NULL + // and assumes it to be Ljava/lang/Object;, which is incorrect. + // Generating CHECKCAST hints the state-machine builder the type of the variable + // avoiding the issue of VerifyError. See KT-51718 + // Exception is Ljava/lang/Object;, since CHECKCAST Ljava/lang/Object; is effectively no-op. + if (initializer.isNullConst() && varType != OBJECT_TYPE && + (irFunction.isSuspend || irFunction.isInvokeSuspendOfLambda()) + ) { + mv.checkcast(varType) + } declaration.markLineNumber(startOffset = true) mv.store(index, varType) } else if (declaration.isVisibleInLVT) { diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/ir/JvmIrCoroutineUtils.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/ir/JvmIrCoroutineUtils.kt index 5fdb7b12fb9..8b67bb97398 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/ir/JvmIrCoroutineUtils.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/ir/JvmIrCoroutineUtils.kt @@ -23,7 +23,7 @@ fun IrFunction.continuationParameter(): IrValueParameter? = when { else -> valueParameters.singleOrNull { it.origin == JvmLoweredDeclarationOrigin.CONTINUATION_CLASS } } -internal fun IrFunction.isInvokeSuspendOfLambda(): Boolean = +fun IrFunction.isInvokeSuspendOfLambda(): Boolean = name.asString() == INVOKE_SUSPEND_METHOD_NAME && parentAsClass.origin == JvmLoweredDeclarationOrigin.SUSPEND_LAMBDA private fun IrFunction.isInvokeSuspendForInlineOfLambda(): Boolean = diff --git a/compiler/testData/codegen/box/coroutines/kt51718.kt b/compiler/testData/codegen/box/coroutines/kt51718.kt new file mode 100644 index 00000000000..fb07e9f8597 --- /dev/null +++ b/compiler/testData/codegen/box/coroutines/kt51718.kt @@ -0,0 +1,60 @@ +// WITH_STDLIB + +import kotlin.coroutines.* + +fun builder(c: suspend () -> Unit) { + c.startCoroutine(Continuation(EmptyCoroutineContext) { + it.getOrThrow() + }) +} + +class Service { + suspend fun getCanalConfig() { + var start: Start? = null + var configuration: String? = null + val starts: Array? = null + if (starts != null && starts.isNotEmpty()) { + start = starts[0] + configuration = call2() + } + //start = start as Start // kotlin 1.5.0 need for produce correct bytecode + call(start!!) + } + + val canalConfig = suspend { + var start: Start? = null + var configuration: String? = null + val starts: Array? = null + if (starts != null && starts.isNotEmpty()) { + start = starts[0] + configuration = call2() + } + //start = start as Start // kotlin 1.5.0 need for produce correct bytecode + call(start!!) + } + + fun call(start: Start) { + + } + + suspend fun call2( + ): String? { + return null + } +} + +class Start() + +fun box(): String { + builder { + try { + Service().getCanalConfig() + error("FAIL 1") + } catch (ignored: NullPointerException) {} + try { + Service().canalConfig() + error("FAIL 2") + } catch (ignored: NullPointerException) {} + } + return "OK" +} \ No newline at end of file diff --git a/compiler/testData/codegen/box/coroutines/tailCallOptimizations/crossinline.txt b/compiler/testData/codegen/box/coroutines/tailCallOptimizations/crossinline.txt index c8d45008644..b26384243d5 100644 --- a/compiler/testData/codegen/box/coroutines/tailCallOptimizations/crossinline.txt +++ b/compiler/testData/codegen/box/coroutines/tailCallOptimizations/crossinline.txt @@ -4,7 +4,6 @@ public final class CrossinlineKt$box$1$filter$$inlined$source$1$1 { // source: 'crossinline.kt' enclosing method CrossinlineKt$box$1$filter$$inlined$source$1.consume(LSink;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; field L$0: java.lang.Object - field L$1: java.lang.Object field label: int synthetic field result: java.lang.Object synthetic final field this$0: CrossinlineKt$box$1$filter$$inlined$source$1 @@ -89,7 +88,6 @@ public final class CrossinlineKt$box$1$invokeSuspend$$inlined$filter$1$1 { // source: 'crossinline.kt' enclosing method CrossinlineKt$box$1$invokeSuspend$$inlined$filter$1.consume(LSink;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; field L$0: java.lang.Object - field L$1: java.lang.Object field label: int synthetic field result: java.lang.Object synthetic final field this$0: CrossinlineKt$box$1$invokeSuspend$$inlined$filter$1 @@ -181,7 +179,6 @@ public final class CrossinlineKt$filter$$inlined$source$1$1 { // source: 'crossinline.kt' enclosing method CrossinlineKt$filter$$inlined$source$1.consume(LSink;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; field L$0: java.lang.Object - field L$1: java.lang.Object field label: int synthetic field result: java.lang.Object synthetic final field this$0: CrossinlineKt$filter$$inlined$source$1 @@ -282,7 +279,6 @@ public final class CrossinlineKt$range$$inlined$source$1$1 { field I$1: int field L$0: java.lang.Object field L$1: java.lang.Object - field L$2: java.lang.Object field label: int synthetic field result: java.lang.Object synthetic final field this$0: CrossinlineKt$range$$inlined$source$1 @@ -310,7 +306,6 @@ public final class CrossinlineKt$source$1$consume$1 { // source: 'crossinline.kt' enclosing method CrossinlineKt$source$1.consume(LSink;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; field L$0: java.lang.Object - field L$1: java.lang.Object field label: int synthetic field result: java.lang.Object synthetic final field this$0: CrossinlineKt$source$1 diff --git a/compiler/testData/codegen/bytecodeText/coroutines/cleanup/nullCleanup.kt b/compiler/testData/codegen/bytecodeText/coroutines/cleanup/nullCleanup.kt index 45604c69da6..66b1e5d4abe 100644 --- a/compiler/testData/codegen/bytecodeText/coroutines/cleanup/nullCleanup.kt +++ b/compiler/testData/codegen/bytecodeText/coroutines/cleanup/nullCleanup.kt @@ -14,10 +14,5 @@ suspend fun test() { // 2 PUTFIELD .*L\$0 : Ljava/lang/Object; -// JVM_TEMPLATES: -// just before suspension point -// 1 ACONST_NULL - -// JVM_IR_TEMPLATES: // two stores to initialize the `a` variable and one null constant to store in the spill slot. // 3 ACONST_NULL diff --git a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/BlackBoxCodegenTestGenerated.java b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/BlackBoxCodegenTestGenerated.java index e4e9a1ceb47..566bb8a1eef 100644 --- a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/BlackBoxCodegenTestGenerated.java @@ -10065,6 +10065,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { runTest("compiler/testData/codegen/box/coroutines/kt50277.kt"); } + @Test + @TestMetadata("kt51718.kt") + public void testKt51718() throws Exception { + runTest("compiler/testData/codegen/box/coroutines/kt51718.kt"); + } + @Test @TestMetadata("lastExpressionIsLoop.kt") public void testLastExpressionIsLoop() throws Exception { diff --git a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBlackBoxCodegenTestGenerated.java b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBlackBoxCodegenTestGenerated.java index f8cc9dae48e..f2fc91d9dce 100644 --- a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBlackBoxCodegenTestGenerated.java +++ b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBlackBoxCodegenTestGenerated.java @@ -10191,6 +10191,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes runTest("compiler/testData/codegen/box/coroutines/kt50277.kt"); } + @Test + @TestMetadata("kt51718.kt") + public void testKt51718() throws Exception { + runTest("compiler/testData/codegen/box/coroutines/kt51718.kt"); + } + @Test @TestMetadata("lastExpressionIsLoop.kt") public void testLastExpressionIsLoop() throws Exception { diff --git a/compiler/tests-gen/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java b/compiler/tests-gen/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java index 4411f8997e1..514dbede35e 100644 --- a/compiler/tests-gen/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java +++ b/compiler/tests-gen/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java @@ -7919,6 +7919,11 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes runTest("compiler/testData/codegen/box/coroutines/kt50277.kt"); } + @TestMetadata("kt51718.kt") + public void testKt51718() throws Exception { + runTest("compiler/testData/codegen/box/coroutines/kt51718.kt"); + } + @TestMetadata("lastExpressionIsLoop.kt") public void testLastExpressionIsLoop() throws Exception { runTest("compiler/testData/codegen/box/coroutines/lastExpressionIsLoop.kt"); diff --git a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/JsCodegenBoxTestGenerated.java b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/JsCodegenBoxTestGenerated.java index b1516aadd33..1a4f9418c69 100644 --- a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/JsCodegenBoxTestGenerated.java +++ b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/JsCodegenBoxTestGenerated.java @@ -7157,6 +7157,12 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest { runTest("compiler/testData/codegen/box/coroutines/kt49168.kt"); } + @Test + @TestMetadata("kt51718.kt") + public void testKt51718() throws Exception { + runTest("compiler/testData/codegen/box/coroutines/kt51718.kt"); + } + @Test @TestMetadata("lastExpressionIsLoop.kt") public void testLastExpressionIsLoop() throws Exception { diff --git a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/IrJsCodegenBoxTestGenerated.java b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/IrJsCodegenBoxTestGenerated.java index 33c9fbcb50e..d273b6e9beb 100644 --- a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/IrJsCodegenBoxTestGenerated.java +++ b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/IrJsCodegenBoxTestGenerated.java @@ -7199,6 +7199,12 @@ public class IrJsCodegenBoxTestGenerated extends AbstractIrJsCodegenBoxTest { runTest("compiler/testData/codegen/box/coroutines/kt49168.kt"); } + @Test + @TestMetadata("kt51718.kt") + public void testKt51718() throws Exception { + runTest("compiler/testData/codegen/box/coroutines/kt51718.kt"); + } + @Test @TestMetadata("lastExpressionIsLoop.kt") public void testLastExpressionIsLoop() throws Exception { diff --git a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/testOld/wasm/semantics/IrCodegenBoxWasmTestGenerated.java b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/testOld/wasm/semantics/IrCodegenBoxWasmTestGenerated.java index 8e27e27e935..356fc313349 100644 --- a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/testOld/wasm/semantics/IrCodegenBoxWasmTestGenerated.java +++ b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/testOld/wasm/semantics/IrCodegenBoxWasmTestGenerated.java @@ -6241,6 +6241,11 @@ public class IrCodegenBoxWasmTestGenerated extends AbstractIrCodegenBoxWasmTest runTest("compiler/testData/codegen/box/coroutines/kt49168.kt"); } + @TestMetadata("kt51718.kt") + public void testKt51718() throws Exception { + runTest("compiler/testData/codegen/box/coroutines/kt51718.kt"); + } + @TestMetadata("lastExpressionIsLoop.kt") public void testLastExpressionIsLoop() throws Exception { runTest("compiler/testData/codegen/box/coroutines/lastExpressionIsLoop.kt"); diff --git a/native/native.tests/tests-gen/org/jetbrains/kotlin/konan/blackboxtest/NativeCodegenBoxTestGenerated.java b/native/native.tests/tests-gen/org/jetbrains/kotlin/konan/blackboxtest/NativeCodegenBoxTestGenerated.java index ae4e24f0350..d83be705a19 100644 --- a/native/native.tests/tests-gen/org/jetbrains/kotlin/konan/blackboxtest/NativeCodegenBoxTestGenerated.java +++ b/native/native.tests/tests-gen/org/jetbrains/kotlin/konan/blackboxtest/NativeCodegenBoxTestGenerated.java @@ -8041,6 +8041,12 @@ public class NativeCodegenBoxTestGenerated extends AbstractNativeCodegenBoxTest runTest("compiler/testData/codegen/box/coroutines/kt49168.kt"); } + @Test + @TestMetadata("kt51718.kt") + public void testKt51718() throws Exception { + runTest("compiler/testData/codegen/box/coroutines/kt51718.kt"); + } + @Test @TestMetadata("lastExpressionIsLoop.kt") public void testLastExpressionIsLoop() throws Exception {