diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java index e3be799847d..c32d2d2b760 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java @@ -3871,12 +3871,11 @@ public class ExpressionCodegen extends KtVisitor impleme Type type = expressionType(exp); StackValue argument = pregeneratedExpr != null ? pregeneratedExpr : gen(exp); - if (kotlinType == null || TypeUtils.isNullableType(kotlinType)) { + if (kotlinType == null || + !AsmUtil.isPrimitive(type) && (TypeUtils.isNullableType(kotlinType) || !InlineClassesUtilsKt.isInlineClassType(kotlinType))) { return StackValue.compareWithNull(argument, (KtTokens.EQEQ == opToken || KtTokens.EQEQEQ == opToken) ? IFNONNULL : IFNULL); } else { - // If exp has a non-nullable type, the comparison is vacuous. - // For inline classes we cannot necessarily compare with null at all. - // In this case the code below is necessary for correctness, not just an optimization. + // If exp is an unboxed inline class value the comparison is vacuous return StackValue.operation(Type.BOOLEAN_TYPE, v -> { argument.put(type, kotlinType, v); AsmUtil.pop(v, type); diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/intrinsics/Equals.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/intrinsics/Equals.kt index b8055c231fb..576f6c53218 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/intrinsics/Equals.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/intrinsics/Equals.kt @@ -17,7 +17,9 @@ import org.jetbrains.kotlin.codegen.intrinsics.IntrinsicMethods import org.jetbrains.kotlin.codegen.pseudoInsns.fakeAlwaysFalseIfeq import org.jetbrains.kotlin.codegen.pseudoInsns.fakeAlwaysTrueIfeq import org.jetbrains.kotlin.config.LanguageFeature -import org.jetbrains.kotlin.ir.expressions.* +import org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression +import org.jetbrains.kotlin.ir.expressions.IrStatementOrigin +import org.jetbrains.kotlin.ir.types.classOrNull import org.jetbrains.kotlin.ir.types.isNullable import org.jetbrains.kotlin.ir.types.toKotlinType import org.jetbrains.kotlin.ir.util.isNullConst @@ -46,8 +48,9 @@ class Equals(val operator: IElementType) : IntrinsicMethod() { override fun invoke(expression: IrFunctionAccessExpression, codegen: ExpressionCodegen, data: BlockInfo): PromisedValue? { val (a, b) = expression.receiverAndArgs() if (a.isNullConst() || b.isNullConst()) { - val value = if (a.isNullConst()) b.accept(codegen, data) else a.accept(codegen, data) - return if (a.type.isNullable() && b.type.isNullable()) + val irValue = if (a.isNullConst()) b else a + val value = irValue.accept(codegen, data) + return if (!isPrimitive(value.type) && (irValue.type.classOrNull?.owner?.isInline != true || irValue.type.isNullable())) BooleanNullCheck(value) else BooleanConstantFalseCheck(value) diff --git a/compiler/testData/codegen/box/platformTypes/unsafeNullCheck.kt b/compiler/testData/codegen/box/platformTypes/unsafeNullCheck.kt new file mode 100644 index 00000000000..c6f7ce64d84 --- /dev/null +++ b/compiler/testData/codegen/box/platformTypes/unsafeNullCheck.kt @@ -0,0 +1,18 @@ +// TARGET_BACKEND: JVM +// FILE: Unsound.java + +import test.Wrap; + +public class Unsound { + public static Wrap get() { + return new Wrap(null); + } +} + +// FILE: 1.kt + +package test + +class Wrap(val x: T) + +fun box(): String = if ((Unsound.get() as Wrap).x == null) "OK" else "Fail" diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java index 047314e5429..783eba95473 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java @@ -17920,6 +17920,11 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/platformTypes"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM, true); } + @TestMetadata("unsafeNullCheck.kt") + public void testUnsafeNullCheck() throws Exception { + runTest("compiler/testData/codegen/box/platformTypes/unsafeNullCheck.kt"); + } + @TestMetadata("compiler/testData/codegen/box/platformTypes/primitives") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java index bc2be130af6..8ce6d849ffa 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java @@ -17920,6 +17920,11 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/platformTypes"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM, true); } + @TestMetadata("unsafeNullCheck.kt") + public void testUnsafeNullCheck() throws Exception { + runTest("compiler/testData/codegen/box/platformTypes/unsafeNullCheck.kt"); + } + @TestMetadata("compiler/testData/codegen/box/platformTypes/primitives") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java index 476e0f5fc72..80ecc808ce5 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java @@ -16480,6 +16480,11 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/platformTypes"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM_IR, true); } + @TestMetadata("unsafeNullCheck.kt") + public void testUnsafeNullCheck() throws Exception { + runTest("compiler/testData/codegen/box/platformTypes/unsafeNullCheck.kt"); + } + @TestMetadata("compiler/testData/codegen/box/platformTypes/primitives") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class)