diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/PrimitiveNumericComparisonCallChecker.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/PrimitiveNumericComparisonCallChecker.kt index 9364471d1d0..9be5755f94b 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/PrimitiveNumericComparisonCallChecker.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/PrimitiveNumericComparisonCallChecker.kt @@ -15,7 +15,6 @@ import org.jetbrains.kotlin.resolve.BindingTrace import org.jetbrains.kotlin.resolve.calls.checkers.CallChecker import org.jetbrains.kotlin.resolve.calls.checkers.CallCheckerContext import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall -import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValueFactory import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.typeUtil.* import org.jetbrains.kotlin.utils.addToStdlib.firstNotNullResult @@ -77,7 +76,7 @@ object PrimitiveNumericComparisonCallChecker : CallChecker { private fun KotlinType.promoteIntegerTypeToIntIfRequired() = when { !isPrimitiveNumberType() -> throw AssertionError("Primitive number type expected: $this") - isByte() || isChar() || isShort() -> builtIns.intType + isByte() || isShort() -> builtIns.intType else -> this } diff --git a/compiler/testData/codegen/box/binaryOp/kt11163_properIeee754comparisons.kt b/compiler/testData/codegen/box/binaryOp/kt11163_properIeee754comparisons.kt new file mode 100644 index 00000000000..b89e8ac39ce --- /dev/null +++ b/compiler/testData/codegen/box/binaryOp/kt11163_properIeee754comparisons.kt @@ -0,0 +1,13 @@ +// !LANGUAGE: +ProperIeee754Comparisons +operator fun Int.compareTo(c: Char) = 0 + +fun foo(x: Int, y: Char): String { + if (x < y) { + throw Error() + } + return "${y}K" +} + +fun box(): String { + return foo(42, 'O') +} \ No newline at end of file diff --git a/compiler/testData/codegen/box/binaryOp/kt23030_properIeee754comparisons.kt b/compiler/testData/codegen/box/binaryOp/kt23030_properIeee754comparisons.kt new file mode 100644 index 00000000000..6b80dab5ba0 --- /dev/null +++ b/compiler/testData/codegen/box/binaryOp/kt23030_properIeee754comparisons.kt @@ -0,0 +1,15 @@ +// !LANGUAGE: +ProperIeee754Comparisons +class C { + operator fun Int.compareTo(c: Char) = 0 + + fun foo(x: Int, y: Char): String { + if (x < y) { + throw Error() + } + return "${y}K" + } +} + +fun box(): String { + return C().foo(42, 'O') +} \ No newline at end of file diff --git a/compiler/testData/ir/irText/expressions/kt23030.kt b/compiler/testData/ir/irText/expressions/kt23030.kt new file mode 100644 index 00000000000..84f2c286203 --- /dev/null +++ b/compiler/testData/ir/irText/expressions/kt23030.kt @@ -0,0 +1,20 @@ +operator fun Int.compareTo(c: Char) = 0 + +fun testOverloadedCompareToCall(x: Int, y: Char) = + x < y + +fun testOverloadedCompareToCallWithSmartCast(x: Any, y: Any) = + x is Int && y is Char && x < y + +fun testEqualsWithSmartCast(x: Any, y: Any) = + x is Int && y is Char && x == y + +class C { + operator fun Int.compareTo(c: Char) = 0 + + fun testMemberExtensionCompareToCall(x: Int, y: Char) = + x < y + + fun testMemberExtensionCompareToCallWithSmartCast(x: Any, y: Any) = + x is Int && y is Char && x < y +} \ No newline at end of file diff --git a/compiler/testData/ir/irText/expressions/kt23030.txt b/compiler/testData/ir/irText/expressions/kt23030.txt new file mode 100644 index 00000000000..e78832e50a1 --- /dev/null +++ b/compiler/testData/ir/irText/expressions/kt23030.txt @@ -0,0 +1,125 @@ +FILE fqName: fileName:/kt23030.kt + FUN name:compareTo visibility:public modality:FINAL <> ($receiver:kotlin.Int, c:kotlin.Char) returnType:Int flags: + $receiver: VALUE_PARAMETER name: type:kotlin.Int flags: + VALUE_PARAMETER name:c index:0 type:kotlin.Char flags: + BLOCK_BODY + RETURN type=kotlin.Nothing from='compareTo(Char) on Int: Int' + CONST Int type=kotlin.Int value=0 + FUN name:testOverloadedCompareToCall visibility:public modality:FINAL <> (x:kotlin.Int, y:kotlin.Char) returnType:Boolean flags: + VALUE_PARAMETER name:x index:0 type:kotlin.Int flags: + VALUE_PARAMETER name:y index:1 type:kotlin.Char flags: + BLOCK_BODY + RETURN type=kotlin.Nothing from='testOverloadedCompareToCall(Int, Char): Boolean' + CALL 'less(Int, Int): Boolean' type=kotlin.Boolean origin=LT + arg0: CALL 'compareTo(Char) on Int: Int' type=kotlin.Int origin=LT + $receiver: GET_VAR 'value-parameter x: Int' type=kotlin.Int origin=null + c: GET_VAR 'value-parameter y: Char' type=kotlin.Char origin=null + arg1: CONST Int type=kotlin.Int value=0 + FUN name:testOverloadedCompareToCallWithSmartCast 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='testOverloadedCompareToCallWithSmartCast(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.Char + 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 'less(Int, Int): Boolean' type=kotlin.Boolean origin=LT + arg0: CALL 'compareTo(Char) on Int: Int' type=kotlin.Int origin=LT + $receiver: TYPE_OP type=kotlin.Int origin=IMPLICIT_CAST typeOperand=kotlin.Int + GET_VAR 'value-parameter x: Any' type=kotlin.Any origin=null + c: TYPE_OP type=kotlin.Char origin=IMPLICIT_CAST typeOperand=kotlin.Char + GET_VAR 'value-parameter y: Any' type=kotlin.Any origin=null + arg1: CONST Int type=kotlin.Int value=0 + BRANCH + if: CONST Boolean type=kotlin.Boolean value=true + then: CONST Boolean type=kotlin.Boolean value=false + FUN name:testEqualsWithSmartCast 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='testEqualsWithSmartCast(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.Char + 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 'EQEQ(Any?, Any?): Boolean' type=kotlin.Boolean origin=EQEQ + arg0: GET_VAR 'value-parameter x: Any' type=kotlin.Any origin=null + arg1: 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 + CLASS CLASS name:C modality:FINAL visibility:public flags: + $this: VALUE_PARAMETER INSTANCE_RECEIVER name: type:C flags: + CONSTRUCTOR visibility:public <> () returnType:C flags: + BLOCK_BODY + DELEGATING_CONSTRUCTOR_CALL 'constructor Any()' + INSTANCE_INITIALIZER_CALL classDescriptor='C' + FUN name:compareTo visibility:public modality:FINAL <> ($this:C, $receiver:kotlin.Int, c:kotlin.Char) returnType:Int flags: + $this: VALUE_PARAMETER name: type:C flags: + $receiver: VALUE_PARAMETER name: type:kotlin.Int flags: + VALUE_PARAMETER name:c index:0 type:kotlin.Char flags: + BLOCK_BODY + RETURN type=kotlin.Nothing from='compareTo(Char) on Int: Int' + CONST Int type=kotlin.Int value=0 + FUN name:testMemberExtensionCompareToCall visibility:public modality:FINAL <> ($this:C, x:kotlin.Int, y:kotlin.Char) returnType:Boolean flags: + $this: VALUE_PARAMETER name: type:C flags: + VALUE_PARAMETER name:x index:0 type:kotlin.Int flags: + VALUE_PARAMETER name:y index:1 type:kotlin.Char flags: + BLOCK_BODY + RETURN type=kotlin.Nothing from='testMemberExtensionCompareToCall(Int, Char): Boolean' + CALL 'less(Int, Int): Boolean' type=kotlin.Boolean origin=LT + arg0: CALL 'compareTo(Char) on Int: Int' type=kotlin.Int origin=LT + $this: GET_VAR 'this@C: C' type=C origin=null + $receiver: GET_VAR 'value-parameter x: Int' type=kotlin.Int origin=null + c: GET_VAR 'value-parameter y: Char' type=kotlin.Char origin=null + arg1: CONST Int type=kotlin.Int value=0 + FUN name:testMemberExtensionCompareToCallWithSmartCast visibility:public modality:FINAL <> ($this:C, x:kotlin.Any, y:kotlin.Any) returnType:Boolean flags: + $this: VALUE_PARAMETER name: type:C 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='testMemberExtensionCompareToCallWithSmartCast(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.Char + 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 'less(Int, Int): Boolean' type=kotlin.Boolean origin=LT + arg0: CALL 'compareTo(Char) on Int: Int' type=kotlin.Int origin=LT + $this: GET_VAR 'this@C: C' type=C origin=null + $receiver: TYPE_OP type=kotlin.Int origin=IMPLICIT_CAST typeOperand=kotlin.Int + GET_VAR 'value-parameter x: Any' type=kotlin.Any origin=null + c: TYPE_OP type=kotlin.Char origin=IMPLICIT_CAST typeOperand=kotlin.Char + GET_VAR 'value-parameter y: Any' type=kotlin.Any origin=null + arg1: CONST Int type=kotlin.Int value=0 + BRANCH + if: CONST Boolean type=kotlin.Boolean value=true + then: CONST Boolean type=kotlin.Boolean value=false + FUN FAKE_OVERRIDE name:equals visibility:public modality:OPEN <> ($this:kotlin.Any, other:kotlin.Any?) returnType:Boolean flags: + $this: VALUE_PARAMETER name: type:kotlin.Any flags: + VALUE_PARAMETER name:other index:0 type:kotlin.Any? flags: + FUN FAKE_OVERRIDE name:hashCode visibility:public modality:OPEN <> ($this:kotlin.Any) returnType:Int flags: + $this: VALUE_PARAMETER name: type:kotlin.Any flags: + FUN FAKE_OVERRIDE name:toString visibility:public modality:OPEN <> ($this:kotlin.Any) returnType:String flags: + $this: VALUE_PARAMETER name: type:kotlin.Any flags: 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 9a2e5ca0bad..686a7ec60c9 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 @@ -882,6 +882,18 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes doTest(fileName); } + @TestMetadata("kt11163_properIeee754comparisons.kt") + public void testKt11163_properIeee754comparisons() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/binaryOp/kt11163_properIeee754comparisons.kt"); + doTest(fileName); + } + + @TestMetadata("kt23030_properIeee754comparisons.kt") + public void testKt23030_properIeee754comparisons() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/binaryOp/kt23030_properIeee754comparisons.kt"); + doTest(fileName); + } + @TestMetadata("kt6747_identityEquals.kt") public void testKt6747_identityEquals() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/binaryOp/kt6747_identityEquals.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java index 50b74681c91..0b0c6e5c9d9 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java @@ -882,6 +882,18 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { doTest(fileName); } + @TestMetadata("kt11163_properIeee754comparisons.kt") + public void testKt11163_properIeee754comparisons() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/binaryOp/kt11163_properIeee754comparisons.kt"); + doTest(fileName); + } + + @TestMetadata("kt23030_properIeee754comparisons.kt") + public void testKt23030_properIeee754comparisons() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/binaryOp/kt23030_properIeee754comparisons.kt"); + doTest(fileName); + } + @TestMetadata("kt6747_identityEquals.kt") public void testKt6747_identityEquals() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/binaryOp/kt6747_identityEquals.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java index d9a795e6081..757f97a2be1 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java @@ -882,6 +882,18 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes doTest(fileName); } + @TestMetadata("kt11163_properIeee754comparisons.kt") + public void testKt11163_properIeee754comparisons() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/binaryOp/kt11163_properIeee754comparisons.kt"); + doTest(fileName); + } + + @TestMetadata("kt23030_properIeee754comparisons.kt") + public void testKt23030_properIeee754comparisons() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/binaryOp/kt23030_properIeee754comparisons.kt"); + doTest(fileName); + } + @TestMetadata("kt6747_identityEquals.kt") public void testKt6747_identityEquals() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/binaryOp/kt6747_identityEquals.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/ir/IrTextTestCaseGenerated.java b/compiler/tests/org/jetbrains/kotlin/ir/IrTextTestCaseGenerated.java index a3ed0e77471..0cc72017bdf 100644 --- a/compiler/tests/org/jetbrains/kotlin/ir/IrTextTestCaseGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/ir/IrTextTestCaseGenerated.java @@ -801,6 +801,12 @@ public class IrTextTestCaseGenerated extends AbstractIrTextTestCase { doTest(fileName); } + @TestMetadata("kt23030.kt") + public void testKt23030() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/ir/irText/expressions/kt23030.kt"); + doTest(fileName); + } + @TestMetadata("lambdaInCAO.kt") public void testLambdaInCAO() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/ir/irText/expressions/lambdaInCAO.kt"); diff --git a/core/descriptors/src/org/jetbrains/kotlin/types/TypeUtils.kt b/core/descriptors/src/org/jetbrains/kotlin/types/TypeUtils.kt index 0ab6bc44f9d..3cc66395b81 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/types/TypeUtils.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/types/TypeUtils.kt @@ -71,7 +71,8 @@ fun KotlinType.isDouble() = KotlinBuiltIns.isDouble(this) fun KotlinType.isPrimitiveNumberOrNullableType(): Boolean = KotlinBuiltIns.isPrimitiveTypeOrNullablePrimitiveType(this) && - !KotlinBuiltIns.isBooleanOrNullableBoolean(this) + !KotlinBuiltIns.isBooleanOrNullableBoolean(this) && + !KotlinBuiltIns.isCharOrNullableChar(this) fun KotlinType.isTypeParameter(): Boolean = TypeUtils.isTypeParameter(this) 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 2b6b449a4d3..051b2987c14 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 @@ -1062,6 +1062,18 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest { doTest(fileName); } + @TestMetadata("kt11163_properIeee754comparisons.kt") + public void testKt11163_properIeee754comparisons() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/binaryOp/kt11163_properIeee754comparisons.kt"); + doTest(fileName); + } + + @TestMetadata("kt23030_properIeee754comparisons.kt") + public void testKt23030_properIeee754comparisons() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/binaryOp/kt23030_properIeee754comparisons.kt"); + doTest(fileName); + } + @TestMetadata("kt6747_identityEquals.kt") public void testKt6747_identityEquals() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/binaryOp/kt6747_identityEquals.kt");