diff --git a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/ir2wasm/BodyGenerator.kt b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/ir2wasm/BodyGenerator.kt index 99e03e4a12e..5fa03e20cae 100644 --- a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/ir2wasm/BodyGenerator.kt +++ b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/ir2wasm/BodyGenerator.kt @@ -754,6 +754,19 @@ class BodyGenerator( return } + // Type? -> Nothing? -> ref.cast null (none/noextern) + if (actualType.isNullable() && expectedType.isNullableNothing()) { + val type = + if (expectedType.getClass()?.isExternal == true) + WasmHeapType.Simple.NoExtern + else + WasmHeapType.Simple.None + + body.buildInstr(WasmOp.REF_CAST_NULL, location, WasmImmediate.HeapType(type)) + + return + } + val expectedClassErased = expectedType.getRuntimeClass(irBuiltIns) // TYPE -> EXTERNAL -> TRUE @@ -792,7 +805,11 @@ class BodyGenerator( // REF -> REF -> REF_CAST if (!expectedIsPrimitive) { if (expectedClassErased.isSubclassOf(actualClassErased)) { - generateRefCast(actualTypeErased, expectedTypeErased, location) + if (expectedType.isNullable()) + generateRefNullCast(actualTypeErased, expectedTypeErased, location) + else + generateRefCast(actualTypeErased, expectedTypeErased, location) + body.commentPreviousInstr { "to make verifier happy" } } else { body.buildUnreachableForVerifier() } diff --git a/compiler/testData/codegen/boxInline/complex/nestedCallsWithGeneric.kt b/compiler/testData/codegen/boxInline/complex/nestedCallsWithGeneric.kt new file mode 100644 index 00000000000..82213cce967 --- /dev/null +++ b/compiler/testData/codegen/boxInline/complex/nestedCallsWithGeneric.kt @@ -0,0 +1,125 @@ +// FILE: 1.kt +package foo + +fun fail(s: String): Nothing = throw Error(s) + +inline fun depth1(a: Any?): T = a as T + +inline fun depth2(a: Any?): T = depth1(a) + +fun test() { + val t11 = depth1(null) + if (t11 != null) fail("t11") + val t12 = depth2(null) + if (t12 != null) fail("t12") + + val t21 = depth1(null) + if (t21 != null) fail("t21") + val t22 = depth2(null) + if (t22 != null) fail("t22") + + val t31 = depth1("str1") + if (t31 != "str1") fail("t31") + val t32 = depth2("str2") + if (t32 != "str2") fail("t32") +} + + +// From KT-60113 +fun kt60113() { + stupidRemembered = ComposerEmpty + crashes1_illegalcast() + stupidRemembered = ComposerEmpty + crashes2_unreachable() + stupidRemembered = ComposerEmpty + crash3_illegal_cast() + nocrash() + + val expectedInL = "BEFORE_CAST - null;Text1 = null;BEFORE_CAST - null;Text2 = null;BEFORE_CAST - asdasd;Text3 = asdasd;BEFORE_CAST - asdasd;Text0 = asdasd;" + if (l != expectedInL) fail("Unexpected value for l:$l") +} + +var l = "" +fun log(t: String?) { + l += t + ";" +} + +fun crashes1_illegalcast() { + val text = remember { + if (false) { + "asdasd" + } else { + null + } + } + + log("Text1 = $text") +} + +fun crashes2_unreachable() { + val text = remember { + if (false) { + null + } else { + null + } + } + + log("Text2 = $text") +} + +fun crash3_illegal_cast() { + val text = remember { + if (true) { + "asdasd" + } else { + "asda324234" + } + } + + log("Text3 = $text") +} + +fun nocrash() { + val text = cache(false) { + if (false) { + "asdasd" + } else { + null + } + } + + log("Text0 = $text") +} + +inline fun remember(crossinline calculation: () -> T): T = + cache(false, calculation) + +inline fun cache(invalid: Boolean, block: () -> T): T { + @Suppress("UNCHECKED_CAST") + return rememberedValue().let { + if (invalid || it === ComposerEmpty) { + val value = block() + stupidRemembered = value + value + } else it + }.also { log("BEFORE_CAST - $it") } as T +} + +fun rememberedValue(): Any? { + return stupidRemembered +} + +val ComposerEmpty = Any() + +var stupidRemembered: Any? = ComposerEmpty + +// FILE: 2.kt +package foo + +fun box(): String { + test() + kt60113() + + return "OK" +} \ No newline at end of file