diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java index 12bbca949a0..b2adac2797c 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java @@ -3373,31 +3373,33 @@ public class ExpressionCodegen extends KtVisitor impleme TypeAndNullability left754Type = calcTypeForIeee754ArithmeticIfNeeded(left, getLeftOperandType(primitiveNumericComparisonInfo)); TypeAndNullability right754Type = calcTypeForIeee754ArithmeticIfNeeded(right, getRightOperandType(primitiveNumericComparisonInfo)); + if (left754Type != null && right754Type != null) { - if (left754Type.type.equals(right754Type.type)) { - //check nullability cause there is some optimizations in codegen for non-nullable case - if (left754Type.isNullable || right754Type.isNullable) { - if (state.getLanguageVersionSettings().getApiVersion().compareTo(ApiVersion.KOTLIN_1_1) >= 0) { - return StackValue.operation(Type.BOOLEAN_TYPE, v -> { - generate754EqualsForNullableTypesViaIntrinsic(v, opToken, pregeneratedLeft, left, left754Type, right, right754Type); - return Unit.INSTANCE; - }); + Type comparisonType = comparisonOperandType(left754Type.type, right754Type.type); + if (comparisonType == Type.FLOAT_TYPE || comparisonType == Type.DOUBLE_TYPE) { + if (left754Type.type.equals(right754Type.type)) { + //check nullability cause there is some optimizations in codegen for non-nullable case + if (left754Type.isNullable || right754Type.isNullable) { + if (state.getLanguageVersionSettings().getApiVersion().compareTo(ApiVersion.KOTLIN_1_1) >= 0) { + return StackValue.operation(Type.BOOLEAN_TYPE, v -> { + generate754EqualsForNullableTypesViaIntrinsic(v, opToken, pregeneratedLeft, left, left754Type, right, + right754Type); + return Unit.INSTANCE; + }); + } + else { + return StackValue.operation(Type.BOOLEAN_TYPE, v -> { + generate754EqualsForNullableTypes(v, opToken, pregeneratedLeft, left, left754Type, right, right754Type); + return Unit.INSTANCE; + }); + } } else { - return StackValue.operation(Type.BOOLEAN_TYPE, v -> { - generate754EqualsForNullableTypes(v, opToken, pregeneratedLeft, left, left754Type, right, right754Type); - return Unit.INSTANCE; - }); + leftType = left754Type.type; + rightType = right754Type.type; } } - else { - leftType = left754Type.type; - rightType = right754Type.type; - } - } - else if (shouldUseProperIeee754Comparisons()) { - Type comparisonType = comparisonOperandType(left754Type.type, right754Type.type); - if (comparisonType == Type.FLOAT_TYPE || comparisonType == Type.DOUBLE_TYPE) { + else if (shouldUseProperIeee754Comparisons()) { return Ieee754Equality.create( myFrameMap, genLazy(left, boxIfNullable(left754Type)), diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/ieee754.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/ieee754.kt index b93421af520..35a820ecfc3 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/ieee754.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/ieee754.kt @@ -35,6 +35,9 @@ fun calcProperTypeForIeee754ArithmeticIfNeeded( return TypeAndNullability(asmType, isNullable) } +private fun KotlinType.isFloatingPointOrNullable() = + KotlinBuiltIns.isDoubleOrNullableDouble(this) || KotlinBuiltIns.isFloatOrNullableFloat(this) + private fun KtExpression.getKotlinTypeForComparison(bindingContext: BindingContext): KotlinType? = when { this is KtProperty -> bindingContext[BindingContext.VARIABLE, this]?.type diff --git a/compiler/testData/codegen/box/primitiveTypes/equalityWithObject/intEqualsNullableInt.kt b/compiler/testData/codegen/box/primitiveTypes/equalityWithObject/intEqualsNullableInt.kt new file mode 100644 index 00000000000..70d3240e137 --- /dev/null +++ b/compiler/testData/codegen/box/primitiveTypes/equalityWithObject/intEqualsNullableInt.kt @@ -0,0 +1,15 @@ +// !LANGUAGE: +ProperIeee754Comparisons + +fun eq1(a: Int?, b: Int) = a == b + +fun eq2(a: Int, b: Int?) = a == b + +fun eq3(a: Int?, b: Int?) = a == b + +fun box(): String = + when { + !eq1(1, 1) -> "Fail 1" + !eq2(1, 1) -> "Fail 2" + !eq3(1, 1) -> "Fail 3" + else -> "OK" + } \ No newline at end of file diff --git a/compiler/testData/codegen/box/primitiveTypes/equalityWithObject/intEqualsNullableIntWithSmartCasts.kt b/compiler/testData/codegen/box/primitiveTypes/equalityWithObject/intEqualsNullableIntWithSmartCasts.kt new file mode 100644 index 00000000000..71ddca79d42 --- /dev/null +++ b/compiler/testData/codegen/box/primitiveTypes/equalityWithObject/intEqualsNullableIntWithSmartCasts.kt @@ -0,0 +1,22 @@ +// !LANGUAGE: +ProperIeee754Comparisons + +fun eq1(a: Any?, b: Any?) = + a is Int? && b is Int && a == b + +fun eq2(a: Any?, b: Any?) = + a is Int && b is Int? && a == b + +fun eq3(a: Any?, b: Any?) = + a is Int && b is Int && a == b + +fun eq4(a: Any?, b: Any?) = + a is Int? && b is Int? && a == b + +fun box(): String = + when { + !eq1(1, 1) -> "Fail 1" + !eq2(1, 1) -> "Fail 2" + !eq3(1, 1) -> "Fail 3" + !eq4(1, 1) -> "Fail 4" + else -> "OK" + } \ No newline at end of file diff --git a/compiler/testData/codegen/box/primitiveTypes/equalityWithObject/whenIntAsNullableAny.kt b/compiler/testData/codegen/box/primitiveTypes/equalityWithObject/whenIntAsNullableAny.kt new file mode 100644 index 00000000000..2c05525f33b --- /dev/null +++ b/compiler/testData/codegen/box/primitiveTypes/equalityWithObject/whenIntAsNullableAny.kt @@ -0,0 +1,11 @@ +// !LANGUAGE: +ProperIeee754Comparisons + +fun test(x: Any?): String { + if (x !is Int) return "Fail 1" + when (x) { + 0 -> return "OK" + else -> return "Fail 2" + } +} + +fun box(): String = test(0) \ No newline at end of file diff --git a/compiler/testData/ir/irText/stubs/builtinMap.txt b/compiler/testData/ir/irText/stubs/builtinMap.txt index 5c7823a1c05..e2471d8df00 100644 --- a/compiler/testData/ir/irText/stubs/builtinMap.txt +++ b/compiler/testData/ir/irText/stubs/builtinMap.txt @@ -1,11 +1,7 @@ FILE fqName: fileName:/builtinMap.kt - FUN name:plus visibility:public modality:FINAL ($receiver:kotlin.collections.Map, pair:kotlin.Pair) returnType:Map flags: - TYPE_PARAMETER name:K1 index:0 variance: upperBounds:[kotlin.Any?] - superClassifiers: - CLASS IR_EXTERNAL_DECLARATION_STUB CLASS name:Any modality:OPEN visibility:public flags: - TYPE_PARAMETER name:V1 index:1 variance: upperBounds:[kotlin.Any?] - superClassifiers: - CLASS IR_EXTERNAL_DECLARATION_STUB CLASS name:Any modality:OPEN visibility:public flags: + FUN name:plus visibility:public modality:FINAL ($receiver:kotlin.collections.Map, pair:kotlin.Pair) returnType:kotlin.collections.Map flags: + TYPE_PARAMETER name:K1 index:0 variance: superTypes:[kotlin.Any?] + TYPE_PARAMETER name:V1 index:1 variance: superTypes:[kotlin.Any?] $receiver: VALUE_PARAMETER name: type:kotlin.collections.Map flags: VALUE_PARAMETER name:pair index:0 type:kotlin.Pair flags: BLOCK_BODY @@ -21,18 +17,18 @@ FILE fqName: fileName:/builtinMap.kt BRANCH if: CONST Boolean type=kotlin.Boolean value=true then: CALL 'apply(LinkedHashMap /* = LinkedHashMap */.() -> Unit) on LinkedHashMap /* = LinkedHashMap */: LinkedHashMap /* = LinkedHashMap */' type=kotlin.collections.LinkedHashMap /* = java.util.LinkedHashMap */ origin=null - : LinkedHashMap /* = LinkedHashMap */ + : kotlin.collections.LinkedHashMap /* = java.util.LinkedHashMap */ $receiver: CALL 'constructor LinkedHashMap((MutableMap..Map?))' type=java.util.LinkedHashMap origin=null - : K1! - : V1! + : K1? + : V1? p0: GET_VAR 'this@plus: Map' type=kotlin.collections.Map origin=null block: BLOCK type=kotlin.collections.LinkedHashMap /* = java.util.LinkedHashMap */.() -> kotlin.Unit origin=LAMBDA - FUN LOCAL_FUNCTION_FOR_LAMBDA name: visibility:local modality:FINAL <> ($receiver:kotlin.collections.LinkedHashMap /* = java.util.LinkedHashMap */) returnType:Unit flags: + FUN LOCAL_FUNCTION_FOR_LAMBDA name: visibility:local modality:FINAL <> ($receiver:kotlin.collections.LinkedHashMap /* = java.util.LinkedHashMap */) returnType:kotlin.Unit flags: $receiver: VALUE_PARAMETER name: type:kotlin.collections.LinkedHashMap /* = java.util.LinkedHashMap */ flags: BLOCK_BODY RETURN type=kotlin.Nothing from='() on LinkedHashMap /* = LinkedHashMap */: Unit' TYPE_OP type=kotlin.Unit origin=IMPLICIT_COERCION_TO_UNIT typeOperand=kotlin.Unit - typeOperand: CLASS IR_EXTERNAL_DECLARATION_STUB OBJECT name:Unit modality:FINAL visibility:public flags: + typeOperand: CLASS IR_EXTERNAL_DECLARATION_STUB OBJECT name:Unit modality:FINAL visibility:public flags: superTypes:[kotlin.Any] CALL 'put(K1!, V1!): V1?' type=V1? origin=null $this: GET_VAR 'this@: LinkedHashMap<(K1..K1?), (V1..V1?)>' type=kotlin.collections.LinkedHashMap /* = java.util.LinkedHashMap */ origin=null key: CALL '(): K1' type=K1 origin=GET_PROPERTY @@ -40,4 +36,3 @@ FILE fqName: fileName:/builtinMap.kt value: CALL '(): V1' type=V1 origin=GET_PROPERTY $this: GET_VAR 'value-parameter pair: Pair' type=kotlin.Pair origin=null FUNCTION_REFERENCE '() on LinkedHashMap /* = LinkedHashMap */: Unit' type=kotlin.collections.LinkedHashMap /* = java.util.LinkedHashMap */.() -> kotlin.Unit origin=LAMBDA - diff --git a/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java b/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java index d63a707689d..cd73880263a 100644 --- a/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java +++ b/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java @@ -15014,11 +15014,26 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes runTest("compiler/testData/codegen/box/primitiveTypes/equalityWithObject/boxedLongEqualsLong.kt"); } + @TestMetadata("intEqualsNullableInt.kt") + public void testIntEqualsNullableInt() throws Exception { + runTest("compiler/testData/codegen/box/primitiveTypes/equalityWithObject/intEqualsNullableInt.kt"); + } + + @TestMetadata("intEqualsNullableIntWithSmartCasts.kt") + public void testIntEqualsNullableIntWithSmartCasts() throws Exception { + runTest("compiler/testData/codegen/box/primitiveTypes/equalityWithObject/intEqualsNullableIntWithSmartCasts.kt"); + } + @TestMetadata("objectWithAsymmetricEqualsEqPrimitive.kt") public void testObjectWithAsymmetricEqualsEqPrimitive() throws Exception { runTest("compiler/testData/codegen/box/primitiveTypes/equalityWithObject/objectWithAsymmetricEqualsEqPrimitive.kt"); } + @TestMetadata("whenIntAsNullableAny.kt") + public void testWhenIntAsNullableAny() throws Exception { + runTest("compiler/testData/codegen/box/primitiveTypes/equalityWithObject/whenIntAsNullableAny.kt"); + } + @TestMetadata("whenNullableBoxed.kt") public void testWhenNullableBoxed() throws Exception { runTest("compiler/testData/codegen/box/primitiveTypes/equalityWithObject/whenNullableBoxed.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java index 1768259a06f..a20ed45420f 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java @@ -15014,11 +15014,26 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { runTest("compiler/testData/codegen/box/primitiveTypes/equalityWithObject/boxedLongEqualsLong.kt"); } + @TestMetadata("intEqualsNullableInt.kt") + public void testIntEqualsNullableInt() throws Exception { + runTest("compiler/testData/codegen/box/primitiveTypes/equalityWithObject/intEqualsNullableInt.kt"); + } + + @TestMetadata("intEqualsNullableIntWithSmartCasts.kt") + public void testIntEqualsNullableIntWithSmartCasts() throws Exception { + runTest("compiler/testData/codegen/box/primitiveTypes/equalityWithObject/intEqualsNullableIntWithSmartCasts.kt"); + } + @TestMetadata("objectWithAsymmetricEqualsEqPrimitive.kt") public void testObjectWithAsymmetricEqualsEqPrimitive() throws Exception { runTest("compiler/testData/codegen/box/primitiveTypes/equalityWithObject/objectWithAsymmetricEqualsEqPrimitive.kt"); } + @TestMetadata("whenIntAsNullableAny.kt") + public void testWhenIntAsNullableAny() throws Exception { + runTest("compiler/testData/codegen/box/primitiveTypes/equalityWithObject/whenIntAsNullableAny.kt"); + } + @TestMetadata("whenNullableBoxed.kt") public void testWhenNullableBoxed() throws Exception { runTest("compiler/testData/codegen/box/primitiveTypes/equalityWithObject/whenNullableBoxed.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java index 0e34106fe51..b51cfc09c11 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java @@ -15014,11 +15014,26 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes runTest("compiler/testData/codegen/box/primitiveTypes/equalityWithObject/boxedLongEqualsLong.kt"); } + @TestMetadata("intEqualsNullableInt.kt") + public void testIntEqualsNullableInt() throws Exception { + runTest("compiler/testData/codegen/box/primitiveTypes/equalityWithObject/intEqualsNullableInt.kt"); + } + + @TestMetadata("intEqualsNullableIntWithSmartCasts.kt") + public void testIntEqualsNullableIntWithSmartCasts() throws Exception { + runTest("compiler/testData/codegen/box/primitiveTypes/equalityWithObject/intEqualsNullableIntWithSmartCasts.kt"); + } + @TestMetadata("objectWithAsymmetricEqualsEqPrimitive.kt") public void testObjectWithAsymmetricEqualsEqPrimitive() throws Exception { runTest("compiler/testData/codegen/box/primitiveTypes/equalityWithObject/objectWithAsymmetricEqualsEqPrimitive.kt"); } + @TestMetadata("whenIntAsNullableAny.kt") + public void testWhenIntAsNullableAny() throws Exception { + runTest("compiler/testData/codegen/box/primitiveTypes/equalityWithObject/whenIntAsNullableAny.kt"); + } + @TestMetadata("whenNullableBoxed.kt") public void testWhenNullableBoxed() throws Exception { runTest("compiler/testData/codegen/box/primitiveTypes/equalityWithObject/whenNullableBoxed.kt"); diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/IrJsCodegenBoxTestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/IrJsCodegenBoxTestGenerated.java index a4208fc62f2..ee925789526 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/IrJsCodegenBoxTestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/IrJsCodegenBoxTestGenerated.java @@ -13279,11 +13279,26 @@ public class IrJsCodegenBoxTestGenerated extends AbstractIrJsCodegenBoxTest { runTest("compiler/testData/codegen/box/primitiveTypes/equalityWithObject/boxedLongEqualsLong.kt"); } + @TestMetadata("intEqualsNullableInt.kt") + public void testIntEqualsNullableInt() throws Exception { + runTest("compiler/testData/codegen/box/primitiveTypes/equalityWithObject/intEqualsNullableInt.kt"); + } + + @TestMetadata("intEqualsNullableIntWithSmartCasts.kt") + public void testIntEqualsNullableIntWithSmartCasts() throws Exception { + runTest("compiler/testData/codegen/box/primitiveTypes/equalityWithObject/intEqualsNullableIntWithSmartCasts.kt"); + } + @TestMetadata("objectWithAsymmetricEqualsEqPrimitive.kt") public void testObjectWithAsymmetricEqualsEqPrimitive() throws Exception { runTest("compiler/testData/codegen/box/primitiveTypes/equalityWithObject/objectWithAsymmetricEqualsEqPrimitive.kt"); } + @TestMetadata("whenIntAsNullableAny.kt") + public void testWhenIntAsNullableAny() throws Exception { + runTest("compiler/testData/codegen/box/primitiveTypes/equalityWithObject/whenIntAsNullableAny.kt"); + } + @TestMetadata("whenNullableBoxed.kt") public void testWhenNullableBoxed() throws Exception { runTest("compiler/testData/codegen/box/primitiveTypes/equalityWithObject/whenNullableBoxed.kt"); diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java index d074f69d1bd..6b1c206c88e 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java @@ -14274,11 +14274,26 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest { runTest("compiler/testData/codegen/box/primitiveTypes/equalityWithObject/boxedLongEqualsLong.kt"); } + @TestMetadata("intEqualsNullableInt.kt") + public void testIntEqualsNullableInt() throws Exception { + runTest("compiler/testData/codegen/box/primitiveTypes/equalityWithObject/intEqualsNullableInt.kt"); + } + + @TestMetadata("intEqualsNullableIntWithSmartCasts.kt") + public void testIntEqualsNullableIntWithSmartCasts() throws Exception { + runTest("compiler/testData/codegen/box/primitiveTypes/equalityWithObject/intEqualsNullableIntWithSmartCasts.kt"); + } + @TestMetadata("objectWithAsymmetricEqualsEqPrimitive.kt") public void testObjectWithAsymmetricEqualsEqPrimitive() throws Exception { runTest("compiler/testData/codegen/box/primitiveTypes/equalityWithObject/objectWithAsymmetricEqualsEqPrimitive.kt"); } + @TestMetadata("whenIntAsNullableAny.kt") + public void testWhenIntAsNullableAny() throws Exception { + runTest("compiler/testData/codegen/box/primitiveTypes/equalityWithObject/whenIntAsNullableAny.kt"); + } + @TestMetadata("whenNullableBoxed.kt") public void testWhenNullableBoxed() throws Exception { runTest("compiler/testData/codegen/box/primitiveTypes/equalityWithObject/whenNullableBoxed.kt");