JVM IR: Avoid optimizing comparisons between boxed primitives and null

A comparison of the form `x == null` where `x` is of type `Int` might not
be vacuous if `x` is a boxed value coming from Java code.
This commit is contained in:
Steven Schäfer
2020-02-13 14:45:49 +01:00
committed by Dmitry Petrov
parent 32e1ec8e98
commit 4b954c347a
2 changed files with 1 additions and 27 deletions
@@ -10,7 +10,6 @@ import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
import org.jetbrains.kotlin.backend.common.lower.irBlock
import org.jetbrains.kotlin.backend.common.phaser.makeIrFilePhase
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
import org.jetbrains.kotlin.backend.jvm.lower.inlineclasses.unboxInlineClass
import org.jetbrains.kotlin.codegen.intrinsics.Not
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.ir.IrStatement
@@ -24,7 +23,6 @@ import org.jetbrains.kotlin.ir.expressions.impl.IrConstImpl
import org.jetbrains.kotlin.ir.types.isBoolean
import org.jetbrains.kotlin.ir.types.isNullableAny
import org.jetbrains.kotlin.ir.types.isPrimitiveType
import org.jetbrains.kotlin.ir.types.toKotlinType
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.ir.visitors.IrElementTransformer
@@ -110,33 +108,12 @@ class JvmOptimizationLowering(val context: JvmBackendContext) : FileLoweringPass
}
getOperandsIfCallToEqeqOrEquals(expression)?.let { (left, right) ->
val constFalse =
IrConstImpl.constFalse(expression.startOffset, expression.endOffset, context.irBuiltIns.booleanType)
return when {
left.isNullConst() && right.isNullConst() ->
IrConstImpl.constTrue(expression.startOffset, expression.endOffset, context.irBuiltIns.booleanType)
left.isNullConst() && right is IrConst<*> || right.isNullConst() && left is IrConst<*> ->
constFalse
right.isNullConst() && left.type.unboxInlineClass().isPrimitiveType() ||
left.isNullConst() && right.type.unboxInlineClass().isPrimitiveType() -> {
val nonNullArgument = if (left.isNullConst()) right else left
if (hasNoSideEffectsForNullCompare(nonNullArgument)) {
constFalse
} else {
IrBlockImpl(expression.startOffset, expression.endOffset, expression.type, expression.origin).apply {
statements.add(
nonNullArgument.coerceToUnitIfNeeded(
nonNullArgument.type.toKotlinType(),
context.irBuiltIns
)
)
statements.add(constFalse)
}
}
}
IrConstImpl.constFalse(expression.startOffset, expression.endOffset, context.irBuiltIns.booleanType)
else -> expression
}
@@ -1,6 +1,5 @@
// IGNORE_BACKEND_FIR: JVM_IR
// TARGET_BACKEND: JVM
// IGNORE_BACKEND: JVM_IR
// FILE: Unsound.java
import test.Wrap;
@@ -17,6 +16,4 @@ package test
class Wrap<T>(val x: T)
// JVM IR generates bytecode that fails with NPE because it unwraps the value with `Number.intValue()`,
// whereas JVM generates a simple null check without unwrapping the box.
fun box(): String = if ((Unsound.get<Int>() as Wrap<Int>).x == null) "OK" else "Fail"