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 7b5e8f31a46..bd050cf8720 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 @@ -3950,6 +3950,12 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT runTest("compiler/testData/codegen/box/casts/nullableSafeCastToTypeParameterWithInterfaceUpperBound.kt"); } + @Test + @TestMetadata("objectToPrimitiveWithAssertion.kt") + public void testObjectToPrimitiveWithAssertion() throws Exception { + runTest("compiler/testData/codegen/box/casts/objectToPrimitiveWithAssertion.kt"); + } + @Test @TestMetadata("unitAsAny.kt") public void testUnitAsAny() throws Exception { diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmLower.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmLower.kt index 6d5713ad716..aac40d055c7 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmLower.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmLower.kt @@ -326,7 +326,6 @@ private val jvmFilePhases = listOf( polymorphicSignaturePhase, varargPhase, arrayConstructorPhase, - checkNotNullPhase, lateinitNullableFieldsPhase, lateinitDeclarationLoweringPhase, diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/intrinsics/IrCheckNotNull.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/intrinsics/IrCheckNotNull.kt new file mode 100644 index 00000000000..e79c83b380d --- /dev/null +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/intrinsics/IrCheckNotNull.kt @@ -0,0 +1,40 @@ +/* + * Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.backend.jvm.intrinsics + +import org.jetbrains.kotlin.backend.jvm.codegen.* +import org.jetbrains.kotlin.codegen.AsmUtil +import org.jetbrains.kotlin.codegen.intrinsics.IntrinsicMethods +import org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression +import org.jetbrains.kotlin.ir.types.IrType +import org.jetbrains.org.objectweb.asm.Label +import org.jetbrains.org.objectweb.asm.Type + +private fun ExpressionCodegen.checkTopValueForNull() { + mv.dup() + if (state.unifiedNullChecks) { + mv.invokestatic(IntrinsicMethods.INTRINSICS_CLASS_NAME, "checkNotNull", "(Ljava/lang/Object;)V", false) + } else { + val ifNonNullLabel = Label() + mv.ifnonnull(ifNonNullLabel) + mv.invokestatic(IntrinsicMethods.INTRINSICS_CLASS_NAME, "throwNpe", "()V", false) + mv.mark(ifNonNullLabel) + } +} + +object IrCheckNotNull : IntrinsicMethod() { + override fun invoke(expression: IrFunctionAccessExpression, codegen: ExpressionCodegen, data: BlockInfo): PromisedValue? { + val arg0 = expression.getValueArgument(0)!!.accept(codegen, data) + if (AsmUtil.isPrimitive(arg0.type)) return arg0 + return object : PromisedValue(codegen, arg0.type, expression.type) { + override fun materializeAt(target: Type, irTarget: IrType, castForReified: Boolean) = + arg0.materialized().also { codegen.checkTopValueForNull() }.materializeAt(target, irTarget, castForReified) + + override fun discard() = + arg0.materialized().also { codegen.checkTopValueForNull() }.discard() + } + } +} diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/intrinsics/IrIntrinsicMethods.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/intrinsics/IrIntrinsicMethods.kt index bce051b46e4..221e0d86f47 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/intrinsics/IrIntrinsicMethods.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/intrinsics/IrIntrinsicMethods.kt @@ -110,6 +110,7 @@ class IrIntrinsicMethods(val irBuiltIns: IrBuiltIns, val symbols: JvmSymbols) { irBuiltIns.booleanNotSymbol.toKey()!! to Not, irBuiltIns.noWhenBranchMatchedExceptionSymbol.toKey()!! to IrNoWhenBranchMatchedException, irBuiltIns.illegalArgumentExceptionSymbol.toKey()!! to IrIllegalArgumentException, + irBuiltIns.checkNotNullSymbol.toKey()!! to IrCheckNotNull, irBuiltIns.andandSymbol.toKey()!! to AndAnd, irBuiltIns.ororSymbol.toKey()!! to OrOr, irBuiltIns.dataClassArrayMemberHashCodeSymbol.toKey()!! to IrDataClassArrayMemberHashCode, diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/CheckNotNullLowering.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/CheckNotNullLowering.kt deleted file mode 100644 index 9205857095f..00000000000 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/CheckNotNullLowering.kt +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors. - * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. - */ - -package org.jetbrains.kotlin.backend.jvm.lower - -import org.jetbrains.kotlin.backend.common.FileLoweringPass -import org.jetbrains.kotlin.backend.common.IrElementTransformerVoidWithContext -import org.jetbrains.kotlin.backend.common.lower.createIrBuilder -import org.jetbrains.kotlin.backend.common.lower.irIfThen -import org.jetbrains.kotlin.backend.common.phaser.makeIrFilePhase -import org.jetbrains.kotlin.backend.jvm.JvmBackendContext -import org.jetbrains.kotlin.ir.builders.* -import org.jetbrains.kotlin.ir.declarations.IrFile -import org.jetbrains.kotlin.ir.expressions.IrCall -import org.jetbrains.kotlin.ir.expressions.IrExpression -import org.jetbrains.kotlin.ir.expressions.IrGetValue - -internal val checkNotNullPhase = makeIrFilePhase( - ::CheckNotNullLowering, - name = "CheckNotNullLowering", - description = "Lower calls to the CHECK_NOT_NULL intrinsic, which are generated by psi2ir for \"!!\" expressions." -) - -private class CheckNotNullLowering(private val backendContext: JvmBackendContext) : FileLoweringPass, IrElementTransformerVoidWithContext() { - override fun lower(irFile: IrFile) = irFile.transformChildrenVoid() - - override fun visitCall(expression: IrCall): IrExpression { - if (expression.symbol != backendContext.irBuiltIns.checkNotNullSymbol) - return super.visitCall(expression) - - expression.transformChildrenVoid() - return backendContext.createIrBuilder(currentScope!!.scope.scopeOwnerSymbol, expression.startOffset, expression.endOffset).irBlock { - val valueArgument = expression.getValueArgument(0)!! - - // For a null-check on a variable we should not introduce a temporary, since null checks are - // optimized by RedundantNullCheckMethodTransformer, which remembers that the argument variable - // is non-nullable if this call succeeds. - val argument = if (valueArgument is IrGetValue) valueArgument.symbol.owner else irTemporary(valueArgument) - - // Starting with Kotlin 1.4 null-checks are lowered to calls to the "checkNotNull" intrinsic, which - // throws a NullPointerException on failure. Prior to Kotlin 1.4 we instead inline the null-check and - // call the "throwNpe" intrinsic on failure which throws a KotlinNullPointerException. - if (backendContext.state.unifiedNullChecks) { - +irCall(backendContext.ir.symbols.checkNotNull).apply { - putValueArgument(0, irGet(argument)) - } - } else { - +irIfThen(irEqualsNull(irGet(argument)), irCall(backendContext.ir.symbols.throwNpe)) - } - - +irImplicitCast(irGet(argument), expression.type) - } - } -} diff --git a/compiler/testData/codegen/box/casts/objectToPrimitiveWithAssertion.kt b/compiler/testData/codegen/box/casts/objectToPrimitiveWithAssertion.kt new file mode 100644 index 00000000000..1ef8e0033e4 --- /dev/null +++ b/compiler/testData/codegen/box/casts/objectToPrimitiveWithAssertion.kt @@ -0,0 +1,8 @@ +// IGNORE_BACKEND: JS, JS_IR, WASM +@Suppress("UNCHECKED_CAST") +fun f() = 1L as T + +fun box(): String { + val x: Int = f()!! // T = Int?, but the cast succeeds because it's immediately upcasted to Number + return if (x == 1) "OK" else "fail: $x" +} diff --git a/compiler/testData/codegen/bytecodeText/inline/inlineReturnsNothing1.kt b/compiler/testData/codegen/bytecodeText/inline/inlineReturnsNothing1.kt index 6917d999aea..896b784b78c 100644 --- a/compiler/testData/codegen/bytecodeText/inline/inlineReturnsNothing1.kt +++ b/compiler/testData/codegen/bytecodeText/inline/inlineReturnsNothing1.kt @@ -1,4 +1,5 @@ -// NB '!!' uses Intrinsics.throwNpe/checkNotNull +// NB '!!' uses Intrinsics.throwNpe/checkNotNull, but IR follows it with ATHROW +// while the old backend returns null from `exit` inline fun exit(): Nothing = null!! fun box(): String { @@ -13,4 +14,7 @@ fun box(): String { return a } +// JVM_TEMPLATES // 1 ATHROW +// JVM_IR_TEMPLATES +// 2 ATHROW diff --git a/compiler/testData/codegen/bytecodeText/inline/inlineReturnsNothing3.kt b/compiler/testData/codegen/bytecodeText/inline/inlineReturnsNothing3.kt index f39c50f633f..0bdfb85c061 100644 --- a/compiler/testData/codegen/bytecodeText/inline/inlineReturnsNothing3.kt +++ b/compiler/testData/codegen/bytecodeText/inline/inlineReturnsNothing3.kt @@ -1,3 +1,5 @@ +// NB '!!' uses Intrinsics.throwNpe/checkNotNull, but IR follows it with ATHROW +// while the old backend returns null from `exit` inline fun exit(): Nothing = null!! inline fun exita(): Nothing = exit() // ATHROW inline fun exitb(): Nothing = exita() // ATHROW @@ -14,4 +16,7 @@ fun box(): String { return a } -// 4 ATHROW \ No newline at end of file +// JVM_TEMPLATES +// 4 ATHROW +// JVM_IR_TEMPLATES +// 5 ATHROW 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 90aca518941..815cbdc35cf 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 @@ -3950,6 +3950,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { runTest("compiler/testData/codegen/box/casts/nullableSafeCastToTypeParameterWithInterfaceUpperBound.kt"); } + @Test + @TestMetadata("objectToPrimitiveWithAssertion.kt") + public void testObjectToPrimitiveWithAssertion() throws Exception { + runTest("compiler/testData/codegen/box/casts/objectToPrimitiveWithAssertion.kt"); + } + @Test @TestMetadata("unitAsAny.kt") public void testUnitAsAny() 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 25715caf6a5..ed6db17f630 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 @@ -3950,6 +3950,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes runTest("compiler/testData/codegen/box/casts/nullableSafeCastToTypeParameterWithInterfaceUpperBound.kt"); } + @Test + @TestMetadata("objectToPrimitiveWithAssertion.kt") + public void testObjectToPrimitiveWithAssertion() throws Exception { + runTest("compiler/testData/codegen/box/casts/objectToPrimitiveWithAssertion.kt"); + } + @Test @TestMetadata("unitAsAny.kt") public void testUnitAsAny() 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 7204ccccc58..f6f0a36327d 100644 --- a/compiler/tests-gen/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java +++ b/compiler/tests-gen/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java @@ -3450,6 +3450,11 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes runTest("compiler/testData/codegen/box/casts/nullableSafeCastToTypeParameterWithInterfaceUpperBound.kt"); } + @TestMetadata("objectToPrimitiveWithAssertion.kt") + public void testObjectToPrimitiveWithAssertion() throws Exception { + runTest("compiler/testData/codegen/box/casts/objectToPrimitiveWithAssertion.kt"); + } + @TestMetadata("unitAsAny.kt") public void testUnitAsAny() throws Exception { runTest("compiler/testData/codegen/box/casts/unitAsAny.kt"); diff --git a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/es6/semantics/IrJsCodegenBoxES6TestGenerated.java b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/es6/semantics/IrJsCodegenBoxES6TestGenerated.java index 0dd027f1bd1..8229dae8eda 100644 --- a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/es6/semantics/IrJsCodegenBoxES6TestGenerated.java +++ b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/es6/semantics/IrJsCodegenBoxES6TestGenerated.java @@ -2625,6 +2625,11 @@ public class IrJsCodegenBoxES6TestGenerated extends AbstractIrJsCodegenBoxES6Tes runTest("compiler/testData/codegen/box/casts/nullableSafeCastToTypeParameterWithInterfaceUpperBound.kt"); } + @TestMetadata("objectToPrimitiveWithAssertion.kt") + public void testObjectToPrimitiveWithAssertion() throws Exception { + runTest("compiler/testData/codegen/box/casts/objectToPrimitiveWithAssertion.kt"); + } + @TestMetadata("unitAsAny.kt") public void testUnitAsAny() throws Exception { runTest("compiler/testData/codegen/box/casts/unitAsAny.kt"); diff --git a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/semantics/IrJsCodegenBoxTestGenerated.java b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/semantics/IrJsCodegenBoxTestGenerated.java index 5b54c57e440..13371c5f6c6 100644 --- a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/semantics/IrJsCodegenBoxTestGenerated.java +++ b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/semantics/IrJsCodegenBoxTestGenerated.java @@ -2625,6 +2625,11 @@ public class IrJsCodegenBoxTestGenerated extends AbstractIrJsCodegenBoxTest { runTest("compiler/testData/codegen/box/casts/nullableSafeCastToTypeParameterWithInterfaceUpperBound.kt"); } + @TestMetadata("objectToPrimitiveWithAssertion.kt") + public void testObjectToPrimitiveWithAssertion() throws Exception { + runTest("compiler/testData/codegen/box/casts/objectToPrimitiveWithAssertion.kt"); + } + @TestMetadata("unitAsAny.kt") public void testUnitAsAny() throws Exception { runTest("compiler/testData/codegen/box/casts/unitAsAny.kt"); diff --git a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java index f254c22f8b3..9d6d4e65113 100644 --- a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java +++ b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java @@ -2625,6 +2625,11 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest { runTest("compiler/testData/codegen/box/casts/nullableSafeCastToTypeParameterWithInterfaceUpperBound.kt"); } + @TestMetadata("objectToPrimitiveWithAssertion.kt") + public void testObjectToPrimitiveWithAssertion() throws Exception { + runTest("compiler/testData/codegen/box/casts/objectToPrimitiveWithAssertion.kt"); + } + @TestMetadata("unitAsAny.kt") public void testUnitAsAny() throws Exception { runTest("compiler/testData/codegen/box/casts/unitAsAny.kt"); diff --git a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/wasm/semantics/IrCodegenBoxWasmTestGenerated.java b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/wasm/semantics/IrCodegenBoxWasmTestGenerated.java index e200bf51802..377cd120450 100644 --- a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/wasm/semantics/IrCodegenBoxWasmTestGenerated.java +++ b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/wasm/semantics/IrCodegenBoxWasmTestGenerated.java @@ -1820,6 +1820,11 @@ public class IrCodegenBoxWasmTestGenerated extends AbstractIrCodegenBoxWasmTest runTest("compiler/testData/codegen/box/casts/lambdaToUnitCast.kt"); } + @TestMetadata("objectToPrimitiveWithAssertion.kt") + public void testObjectToPrimitiveWithAssertion() throws Exception { + runTest("compiler/testData/codegen/box/casts/objectToPrimitiveWithAssertion.kt"); + } + @TestMetadata("compiler/testData/codegen/box/casts/functions") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class)