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);
|
||||
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);
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@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)
|
||||
|
||||
+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);
|
||||
}
|
||||
|
||||
@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)
|
||||
|
||||
+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);
|
||||
}
|
||||
|
||||
@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)
|
||||
|
||||
Reference in New Issue
Block a user