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:
Steven Schäfer
2019-11-06 16:16:29 +01:00
committed by Alexander Udalov
parent de082543f1
commit b80e157381
6 changed files with 42 additions and 7 deletions
@@ -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"
@@ -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)
@@ -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)
@@ -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)