diff --git a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/OperatorExpressionGenerator.kt b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/OperatorExpressionGenerator.kt index 43891189930..d831bc57078 100644 --- a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/OperatorExpressionGenerator.kt +++ b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/OperatorExpressionGenerator.kt @@ -30,7 +30,9 @@ import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.psiUtil.endOffset import org.jetbrains.kotlin.psi.psiUtil.startOffset +import org.jetbrains.kotlin.psi2ir.containsNull import org.jetbrains.kotlin.psi2ir.findSingleFunction +import org.jetbrains.kotlin.psi2ir.intermediate.safeCallOnDispatchReceiver import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall import org.jetbrains.kotlin.resolve.checkers.PrimitiveNumericComparisonInfo @@ -277,6 +279,15 @@ class OperatorExpressionGenerator(statementGenerator: StatementGenerator) : Stat operandType == targetType || operandNNType == targetType -> this + type.containsNull() -> + safeCallOnDispatchReceiver(this@OperatorExpressionGenerator, startOffset, endOffset) { dispatchReceiver -> + invokeConversionFunction( + startOffset, endOffset, + conversionFunction ?: throw AssertionError("No conversion function for $type ~> $targetType"), + dispatchReceiver + ) + } + else -> invokeConversionFunction( startOffset, endOffset, diff --git a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/intermediate/SafeCallReceiver.kt b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/intermediate/SafeCallReceiver.kt index 416c8bbdae7..bfbfbcaf8e6 100644 --- a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/intermediate/SafeCallReceiver.kt +++ b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/intermediate/SafeCallReceiver.kt @@ -67,4 +67,20 @@ class SafeCallReceiver( return irBlock } -} \ No newline at end of file +} + + +fun IrExpression.safeCallOnDispatchReceiver( + generator: GeneratorWithScope, + startOffset: Int, + endOffset: Int, + ifNotNull: (IrExpression) -> IrExpression +) = + SafeCallReceiver( + generator, startOffset, endOffset, + extensionReceiver = null, + dispatchReceiver = OnceExpressionValue(this), + isAssignmentReceiver = false + ).call { dispatchReceiverValue, _ -> + ifNotNull(dispatchReceiverValue!!.load()) + } \ No newline at end of file diff --git a/compiler/testData/ir/irText/expressions/floatingPointComparisons/nullableAnyAsIntToDouble.txt b/compiler/testData/ir/irText/expressions/floatingPointComparisons/nullableAnyAsIntToDouble.txt index ed9d9209221..a78ccae14ad 100644 --- a/compiler/testData/ir/irText/expressions/floatingPointComparisons/nullableAnyAsIntToDouble.txt +++ b/compiler/testData/ir/irText/expressions/floatingPointComparisons/nullableAnyAsIntToDouble.txt @@ -9,9 +9,20 @@ FILE fqName: fileName:/nullableAnyAsIntToDouble.kt if: TYPE_OP type=kotlin.Boolean origin=INSTANCEOF typeOperand=kotlin.Int GET_VAR 'value-parameter x: Any?' type=kotlin.Any? origin=null then: CALL 'less(Double, Double): Boolean' type=kotlin.Boolean origin=LT - arg0: CALL 'toDouble(): Double' type=kotlin.Double origin=null - $this: TYPE_OP type=kotlin.Int origin=IMPLICIT_CAST typeOperand=kotlin.Int + arg0: BLOCK type=kotlin.Double? origin=SAFE_CALL + VAR IR_TEMPORARY_VARIABLE name:tmp0_safe_receiver type:kotlin.Any? flags:val GET_VAR 'value-parameter x: Any?' type=kotlin.Any? origin=null + WHEN type=kotlin.Double? origin=SAFE_CALL + BRANCH + if: CALL 'EQEQ(Any?, Any?): Boolean' type=kotlin.Boolean origin=EQEQ + arg0: GET_VAR 'tmp0_safe_receiver: Any?' type=kotlin.Any? origin=null + arg1: CONST Null type=kotlin.Nothing? value=null + then: CONST Null type=kotlin.Nothing? value=null + BRANCH + if: CONST Boolean type=kotlin.Boolean value=true + then: CALL 'toDouble(): Double' type=kotlin.Double origin=null + $this: TYPE_OP type=kotlin.Int origin=IMPLICIT_CAST typeOperand=kotlin.Int + GET_VAR 'tmp0_safe_receiver: Any?' type=kotlin.Any? origin=null arg1: GET_VAR 'value-parameter y: Double' type=kotlin.Double origin=null BRANCH if: CONST Boolean type=kotlin.Boolean value=true diff --git a/compiler/testData/ir/irText/expressions/floatingPointComparisons/nullableFloatingPointEqeq.kt b/compiler/testData/ir/irText/expressions/floatingPointComparisons/nullableFloatingPointEqeq.kt index 325db51921a..25709fcb151 100644 --- a/compiler/testData/ir/irText/expressions/floatingPointComparisons/nullableFloatingPointEqeq.kt +++ b/compiler/testData/ir/irText/expressions/floatingPointComparisons/nullableFloatingPointEqeq.kt @@ -1,3 +1,4 @@ fun testDD(x: Double?, y: Double?) = x == y fun testDF(x: Double?, y: Any?) = y is Float? && x == y -fun testDI(x: Double?, y: Any?) = y is Int? && x == y \ No newline at end of file +fun testDI(x: Double?, y: Any?) = y is Int? && x == y +fun testDI2(x: Any?, y: Any?) = x is Int? && y is Double && x == y \ No newline at end of file diff --git a/compiler/testData/ir/irText/expressions/floatingPointComparisons/nullableFloatingPointEqeq.txt b/compiler/testData/ir/irText/expressions/floatingPointComparisons/nullableFloatingPointEqeq.txt index 6f913e6b43d..31fc4e2cb09 100644 --- a/compiler/testData/ir/irText/expressions/floatingPointComparisons/nullableFloatingPointEqeq.txt +++ b/compiler/testData/ir/irText/expressions/floatingPointComparisons/nullableFloatingPointEqeq.txt @@ -18,9 +18,20 @@ FILE fqName: fileName:/nullableFloatingPointEqeq.kt GET_VAR 'value-parameter y: Any?' type=kotlin.Any? origin=null then: CALL 'ieee754equals(Double?, Double?): Boolean' type=kotlin.Boolean origin=EQEQ arg0: GET_VAR 'value-parameter x: Double?' type=kotlin.Double? origin=null - arg1: CALL 'toDouble(): Double' type=kotlin.Double origin=null - $this: TYPE_OP type=kotlin.Float origin=IMPLICIT_CAST typeOperand=kotlin.Float + arg1: BLOCK type=kotlin.Double? origin=SAFE_CALL + VAR IR_TEMPORARY_VARIABLE name:tmp0_safe_receiver type:kotlin.Any? flags:val GET_VAR 'value-parameter y: Any?' type=kotlin.Any? origin=null + WHEN type=kotlin.Double? origin=SAFE_CALL + BRANCH + if: CALL 'EQEQ(Any?, Any?): Boolean' type=kotlin.Boolean origin=EQEQ + arg0: GET_VAR 'tmp0_safe_receiver: Any?' type=kotlin.Any? origin=null + arg1: CONST Null type=kotlin.Nothing? value=null + then: CONST Null type=kotlin.Nothing? value=null + BRANCH + if: CONST Boolean type=kotlin.Boolean value=true + then: CALL 'toDouble(): Double' type=kotlin.Double origin=null + $this: TYPE_OP type=kotlin.Float origin=IMPLICIT_CAST typeOperand=kotlin.Float + GET_VAR 'tmp0_safe_receiver: Any?' type=kotlin.Any? origin=null BRANCH if: CONST Boolean type=kotlin.Boolean value=true then: CONST Boolean type=kotlin.Boolean value=false @@ -35,9 +46,56 @@ FILE fqName: fileName:/nullableFloatingPointEqeq.kt GET_VAR 'value-parameter y: Any?' type=kotlin.Any? origin=null then: CALL 'ieee754equals(Double?, Double?): Boolean' type=kotlin.Boolean origin=EQEQ arg0: GET_VAR 'value-parameter x: Double?' type=kotlin.Double? origin=null - arg1: CALL 'toDouble(): Double' type=kotlin.Double origin=null - $this: TYPE_OP type=kotlin.Int origin=IMPLICIT_CAST typeOperand=kotlin.Int + arg1: BLOCK type=kotlin.Double? origin=SAFE_CALL + VAR IR_TEMPORARY_VARIABLE name:tmp0_safe_receiver type:kotlin.Any? flags:val GET_VAR 'value-parameter y: Any?' type=kotlin.Any? origin=null + WHEN type=kotlin.Double? origin=SAFE_CALL + BRANCH + if: CALL 'EQEQ(Any?, Any?): Boolean' type=kotlin.Boolean origin=EQEQ + arg0: GET_VAR 'tmp0_safe_receiver: Any?' type=kotlin.Any? origin=null + arg1: CONST Null type=kotlin.Nothing? value=null + then: CONST Null type=kotlin.Nothing? value=null + BRANCH + if: CONST Boolean type=kotlin.Boolean value=true + then: CALL 'toDouble(): Double' type=kotlin.Double origin=null + $this: TYPE_OP type=kotlin.Int origin=IMPLICIT_CAST typeOperand=kotlin.Int + GET_VAR 'tmp0_safe_receiver: Any?' type=kotlin.Any? origin=null + BRANCH + if: CONST Boolean type=kotlin.Boolean value=true + then: CONST Boolean type=kotlin.Boolean value=false + FUN name:testDI2 visibility:public modality:FINAL <> (x:kotlin.Any?, y:kotlin.Any?) returnType:Boolean flags: + VALUE_PARAMETER name:x index:0 type:kotlin.Any? flags: + VALUE_PARAMETER name:y index:1 type:kotlin.Any? flags: + BLOCK_BODY + RETURN type=kotlin.Nothing from='testDI2(Any?, Any?): Boolean' + WHEN type=kotlin.Boolean origin=ANDAND + BRANCH + if: WHEN type=kotlin.Boolean origin=ANDAND + BRANCH + if: TYPE_OP type=kotlin.Boolean origin=INSTANCEOF typeOperand=kotlin.Int? + GET_VAR 'value-parameter x: Any?' type=kotlin.Any? origin=null + then: TYPE_OP type=kotlin.Boolean origin=INSTANCEOF typeOperand=kotlin.Double + GET_VAR 'value-parameter y: Any?' type=kotlin.Any? origin=null + BRANCH + if: CONST Boolean type=kotlin.Boolean value=true + then: CONST Boolean type=kotlin.Boolean value=false + then: CALL 'ieee754equals(Double?, Double?): Boolean' type=kotlin.Boolean origin=EQEQ + arg0: BLOCK type=kotlin.Double? origin=SAFE_CALL + VAR IR_TEMPORARY_VARIABLE name:tmp0_safe_receiver type:kotlin.Any? flags:val + GET_VAR 'value-parameter x: Any?' type=kotlin.Any? origin=null + WHEN type=kotlin.Double? origin=SAFE_CALL + BRANCH + if: CALL 'EQEQ(Any?, Any?): Boolean' type=kotlin.Boolean origin=EQEQ + arg0: GET_VAR 'tmp0_safe_receiver: Any?' type=kotlin.Any? origin=null + arg1: CONST Null type=kotlin.Nothing? value=null + then: CONST Null type=kotlin.Nothing? value=null + BRANCH + if: CONST Boolean type=kotlin.Boolean value=true + then: CALL 'toDouble(): Double' type=kotlin.Double origin=null + $this: TYPE_OP type=kotlin.Int origin=IMPLICIT_CAST typeOperand=kotlin.Int + GET_VAR 'tmp0_safe_receiver: Any?' type=kotlin.Any? origin=null + arg1: TYPE_OP type=kotlin.Double? origin=IMPLICIT_CAST typeOperand=kotlin.Double? + GET_VAR 'value-parameter y: Any?' type=kotlin.Any? origin=null BRANCH if: CONST Boolean type=kotlin.Boolean value=true then: CONST Boolean type=kotlin.Boolean value=false