JVM, JVM IR: Don't optimize null-checks based on nullability information
Since Java interop allows us to circumvent the Kotlin type system we cannot rely on nullability information.
This commit is contained in:
committed by
Alexander Udalov
parent
de082543f1
commit
b80e157381
@@ -3871,12 +3871,11 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
|
|||||||
Type type = expressionType(exp);
|
Type type = expressionType(exp);
|
||||||
StackValue argument = pregeneratedExpr != null ? pregeneratedExpr : gen(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);
|
return StackValue.compareWithNull(argument, (KtTokens.EQEQ == opToken || KtTokens.EQEQEQ == opToken) ? IFNONNULL : IFNULL);
|
||||||
} else {
|
} else {
|
||||||
// If exp has a non-nullable type, the comparison is vacuous.
|
// If exp is an unboxed inline class value 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.
|
|
||||||
return StackValue.operation(Type.BOOLEAN_TYPE, v -> {
|
return StackValue.operation(Type.BOOLEAN_TYPE, v -> {
|
||||||
argument.put(type, kotlinType, v);
|
argument.put(type, kotlinType, v);
|
||||||
AsmUtil.pop(v, type);
|
AsmUtil.pop(v, type);
|
||||||
|
|||||||
@@ -17,7 +17,9 @@ import org.jetbrains.kotlin.codegen.intrinsics.IntrinsicMethods
|
|||||||
import org.jetbrains.kotlin.codegen.pseudoInsns.fakeAlwaysFalseIfeq
|
import org.jetbrains.kotlin.codegen.pseudoInsns.fakeAlwaysFalseIfeq
|
||||||
import org.jetbrains.kotlin.codegen.pseudoInsns.fakeAlwaysTrueIfeq
|
import org.jetbrains.kotlin.codegen.pseudoInsns.fakeAlwaysTrueIfeq
|
||||||
import org.jetbrains.kotlin.config.LanguageFeature
|
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.isNullable
|
||||||
import org.jetbrains.kotlin.ir.types.toKotlinType
|
import org.jetbrains.kotlin.ir.types.toKotlinType
|
||||||
import org.jetbrains.kotlin.ir.util.isNullConst
|
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? {
|
override fun invoke(expression: IrFunctionAccessExpression, codegen: ExpressionCodegen, data: BlockInfo): PromisedValue? {
|
||||||
val (a, b) = expression.receiverAndArgs()
|
val (a, b) = expression.receiverAndArgs()
|
||||||
if (a.isNullConst() || b.isNullConst()) {
|
if (a.isNullConst() || b.isNullConst()) {
|
||||||
val value = if (a.isNullConst()) b.accept(codegen, data) else a.accept(codegen, data)
|
val irValue = if (a.isNullConst()) b else a
|
||||||
return if (a.type.isNullable() && b.type.isNullable())
|
val value = irValue.accept(codegen, data)
|
||||||
|
return if (!isPrimitive(value.type) && (irValue.type.classOrNull?.owner?.isInline != true || irValue.type.isNullable()))
|
||||||
BooleanNullCheck(value)
|
BooleanNullCheck(value)
|
||||||
else
|
else
|
||||||
BooleanConstantFalseCheck(value)
|
BooleanConstantFalseCheck(value)
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
// TARGET_BACKEND: JVM
|
||||||
|
// FILE: Unsound.java
|
||||||
|
|
||||||
|
import test.Wrap;
|
||||||
|
|
||||||
|
public class Unsound {
|
||||||
|
public static <T> Wrap<T> get() {
|
||||||
|
return new Wrap<T>(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FILE: 1.kt
|
||||||
|
|
||||||
|
package test
|
||||||
|
|
||||||
|
class Wrap<T>(val x: T)
|
||||||
|
|
||||||
|
fun box(): String = if ((Unsound.get<String>() as Wrap<String>).x == null) "OK" else "Fail"
|
||||||
+5
@@ -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);
|
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")
|
@TestMetadata("compiler/testData/codegen/box/platformTypes/primitives")
|
||||||
@TestDataPath("$PROJECT_ROOT")
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
@RunWith(JUnit3RunnerWithInners.class)
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
|
|||||||
+5
@@ -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);
|
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")
|
@TestMetadata("compiler/testData/codegen/box/platformTypes/primitives")
|
||||||
@TestDataPath("$PROJECT_ROOT")
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
@RunWith(JUnit3RunnerWithInners.class)
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
|
|||||||
+5
@@ -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);
|
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")
|
@TestMetadata("compiler/testData/codegen/box/platformTypes/primitives")
|
||||||
@TestDataPath("$PROJECT_ROOT")
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
@RunWith(JUnit3RunnerWithInners.class)
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
|
|||||||
Reference in New Issue
Block a user