From 5a85bf36a54cab0ab4c53495d643dc1e67c8ab9b Mon Sep 17 00:00:00 2001 From: Dmitry Petrov Date: Fri, 9 Feb 2018 12:40:52 +0300 Subject: [PATCH] Check for type parameter supertypes in primitive numeric comparisons --- .../PrimitiveNumericComparisonCallChecker.kt | 14 +- ...eParameterWithPrimitiveNumericSupertype.kt | 11 ++ ...ParameterWithPrimitiveNumericSupertype.txt | 155 ++++++++++++++++++ .../kotlin/ir/IrTextTestCaseGenerated.java | 6 + 4 files changed, 185 insertions(+), 1 deletion(-) create mode 100644 compiler/testData/ir/irText/expressions/floatingPointComparisons/typeParameterWithPrimitiveNumericSupertype.kt create mode 100644 compiler/testData/ir/irText/expressions/floatingPointComparisons/typeParameterWithPrimitiveNumericSupertype.txt 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 c1633727b9f..4b73347c8ce 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/PrimitiveNumericComparisonCallChecker.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/PrimitiveNumericComparisonCallChecker.kt @@ -6,6 +6,7 @@ package org.jetbrains.kotlin.resolve.checkers import com.intellij.psi.PsiElement +import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.psi.KtBinaryExpression import org.jetbrains.kotlin.psi.KtExpression @@ -17,6 +18,7 @@ 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 class PrimitiveNumericComparisonInfo( val comparisonType: KotlinType, @@ -90,5 +92,15 @@ object PrimitiveNumericComparisonCallChecker : CallChecker { } private fun List.findPrimitiveType() = - find { it.isPrimitiveNumberOrNullableType() }?.makeNotNullable() + firstNotNullResult { it.getPrimitiveTypeOrSupertype() } + + private fun KotlinType.getPrimitiveTypeOrSupertype(): KotlinType? = + when { + constructor.declarationDescriptor is TypeParameterDescriptor -> + immediateSupertypes().firstNotNullResult { it.getPrimitiveTypeOrSupertype() } + isPrimitiveNumberOrNullableType() -> + makeNotNullable() + else -> + null + } } \ No newline at end of file diff --git a/compiler/testData/ir/irText/expressions/floatingPointComparisons/typeParameterWithPrimitiveNumericSupertype.kt b/compiler/testData/ir/irText/expressions/floatingPointComparisons/typeParameterWithPrimitiveNumericSupertype.kt new file mode 100644 index 00000000000..51bcdb79777 --- /dev/null +++ b/compiler/testData/ir/irText/expressions/floatingPointComparisons/typeParameterWithPrimitiveNumericSupertype.kt @@ -0,0 +1,11 @@ +fun test0(x: Any, y: T) = x is Int && x == y +fun test1(x: Any, y: T) = x is Float && x == y +fun test2(x: Any, y: T) = x is Float && x == y +fun test3(x: Any, y: T) = x is Int && x == y +fun test4(x: Any, y: T) = x is Int && x == y +fun test5(x: Any, y: R) = x is Int && x == y +fun test6(x: Any, y: T) = x is Int && x == y + +class F { + fun testCapturedType(x: T, y: Any) = y is Double && x == y +} \ No newline at end of file diff --git a/compiler/testData/ir/irText/expressions/floatingPointComparisons/typeParameterWithPrimitiveNumericSupertype.txt b/compiler/testData/ir/irText/expressions/floatingPointComparisons/typeParameterWithPrimitiveNumericSupertype.txt new file mode 100644 index 00000000000..405de555ae1 --- /dev/null +++ b/compiler/testData/ir/irText/expressions/floatingPointComparisons/typeParameterWithPrimitiveNumericSupertype.txt @@ -0,0 +1,155 @@ +FILE fqName: fileName:/typeParameterWithPrimitiveNumericSupertype.kt + FUN name:test0 visibility:public modality:FINAL (x:kotlin.Any, y:T) returnType:Boolean flags: + TYPE_PARAMETER name:T index:0 variance: upperBounds:[kotlin.Any?] + VALUE_PARAMETER name:x index:0 type:kotlin.Any flags: + VALUE_PARAMETER name:y index:1 type:T flags: + BLOCK_BODY + RETURN type=kotlin.Nothing from='test0(Any, T): Boolean' + 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: 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: T' type=T origin=null + BRANCH + if: CONST Boolean type=kotlin.Boolean value=true + then: CONST Boolean type=kotlin.Boolean value=false + FUN name:test1 visibility:public modality:FINAL (x:kotlin.Any, y:T) returnType:Boolean flags: + TYPE_PARAMETER name:T index:0 variance: upperBounds:[kotlin.Float] + VALUE_PARAMETER name:x index:0 type:kotlin.Any flags: + VALUE_PARAMETER name:y index:1 type:T flags: + BLOCK_BODY + RETURN type=kotlin.Nothing from='test1(Any, T): Boolean' + WHEN type=kotlin.Boolean origin=ANDAND + BRANCH + if: TYPE_OP type=kotlin.Boolean origin=INSTANCEOF typeOperand=kotlin.Float + GET_VAR 'value-parameter x: Any' type=kotlin.Any origin=null + then: CALL 'ieee754equals(Float?, Float?): Boolean' type=kotlin.Boolean origin=EQEQ + arg0: TYPE_OP type=kotlin.Float origin=IMPLICIT_CAST typeOperand=kotlin.Float + GET_VAR 'value-parameter x: Any' type=kotlin.Any origin=null + arg1: GET_VAR 'value-parameter y: T' type=T origin=null + BRANCH + if: CONST Boolean type=kotlin.Boolean value=true + then: CONST Boolean type=kotlin.Boolean value=false + FUN name:test2 visibility:public modality:FINAL (x:kotlin.Any, y:T) returnType:Boolean flags: + TYPE_PARAMETER name:T index:0 variance: upperBounds:[kotlin.Double] + VALUE_PARAMETER name:x index:0 type:kotlin.Any flags: + VALUE_PARAMETER name:y index:1 type:T flags: + BLOCK_BODY + RETURN type=kotlin.Nothing from='test2(Any, T): Boolean' + WHEN type=kotlin.Boolean origin=ANDAND + BRANCH + if: TYPE_OP type=kotlin.Boolean origin=INSTANCEOF typeOperand=kotlin.Float + GET_VAR 'value-parameter x: Any' type=kotlin.Any origin=null + then: CALL 'ieee754equals(Double?, Double?): Boolean' type=kotlin.Boolean origin=EQEQ + arg0: CALL 'toDouble(): Double' type=kotlin.Double origin=null + $this: TYPE_OP type=kotlin.Float origin=IMPLICIT_CAST typeOperand=kotlin.Float + GET_VAR 'value-parameter x: Any' type=kotlin.Any origin=null + arg1: GET_VAR 'value-parameter y: T' type=T origin=null + BRANCH + if: CONST Boolean type=kotlin.Boolean value=true + then: CONST Boolean type=kotlin.Boolean value=false + FUN name:test3 visibility:public modality:FINAL (x:kotlin.Any, y:T) returnType:Boolean flags: + TYPE_PARAMETER name:T index:0 variance: upperBounds:[kotlin.Float] + VALUE_PARAMETER name:x index:0 type:kotlin.Any flags: + VALUE_PARAMETER name:y index:1 type:T flags: + BLOCK_BODY + RETURN type=kotlin.Nothing from='test3(Any, T): Boolean' + 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: CALL 'ieee754equals(Float?, Float?): Boolean' type=kotlin.Boolean origin=EQEQ + arg0: CALL 'toFloat(): Float' type=kotlin.Float origin=null + $this: TYPE_OP type=kotlin.Int origin=IMPLICIT_CAST typeOperand=kotlin.Int + GET_VAR 'value-parameter x: Any' type=kotlin.Any origin=null + arg1: GET_VAR 'value-parameter y: T' type=T origin=null + BRANCH + if: CONST Boolean type=kotlin.Boolean value=true + then: CONST Boolean type=kotlin.Boolean value=false + FUN name:test4 visibility:public modality:FINAL (x:kotlin.Any, y:T) returnType:Boolean flags: + TYPE_PARAMETER name:T index:0 variance: upperBounds:[kotlin.Float?] + VALUE_PARAMETER name:x index:0 type:kotlin.Any flags: + VALUE_PARAMETER name:y index:1 type:T flags: + BLOCK_BODY + RETURN type=kotlin.Nothing from='test4(Any, T): Boolean' + 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: CALL 'ieee754equals(Float?, Float?): Boolean' type=kotlin.Boolean origin=EQEQ + arg0: CALL 'toFloat(): Float' type=kotlin.Float origin=null + $this: TYPE_OP type=kotlin.Int origin=IMPLICIT_CAST typeOperand=kotlin.Int + GET_VAR 'value-parameter x: Any' type=kotlin.Any origin=null + arg1: GET_VAR 'value-parameter y: T' type=T origin=null + BRANCH + if: CONST Boolean type=kotlin.Boolean value=true + then: CONST Boolean type=kotlin.Boolean value=false + FUN name:test5 visibility:public modality:FINAL (x:kotlin.Any, y:R) returnType:Boolean flags: + TYPE_PARAMETER name:T index:0 variance: upperBounds:[kotlin.Float?] + TYPE_PARAMETER name:R index:1 variance: upperBounds:[T] + VALUE_PARAMETER name:x index:0 type:kotlin.Any flags: + VALUE_PARAMETER name:y index:1 type:R flags: + BLOCK_BODY + RETURN type=kotlin.Nothing from='test5(Any, R): Boolean' + 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: CALL 'ieee754equals(Float?, Float?): Boolean' type=kotlin.Boolean origin=EQEQ + arg0: CALL 'toFloat(): Float' type=kotlin.Float origin=null + $this: TYPE_OP type=kotlin.Int origin=IMPLICIT_CAST typeOperand=kotlin.Int + GET_VAR 'value-parameter x: Any' type=kotlin.Any origin=null + arg1: GET_VAR 'value-parameter y: R' type=R origin=null + BRANCH + if: CONST Boolean type=kotlin.Boolean value=true + then: CONST Boolean type=kotlin.Boolean value=false + FUN name:test6 visibility:public modality:FINAL (x:kotlin.Any, y:T) returnType:Boolean flags: + TYPE_PARAMETER name:T index:0 variance: upperBounds:[kotlin.Number] + VALUE_PARAMETER name:x index:0 type:kotlin.Any flags: + VALUE_PARAMETER name:y index:1 type:T flags: + BLOCK_BODY + RETURN type=kotlin.Nothing from='test6(Any, T): Boolean' + 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: 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: T' type=T origin=null + BRANCH + if: CONST Boolean type=kotlin.Boolean value=true + then: CONST Boolean type=kotlin.Boolean value=false + CLASS CLASS name:F modality:FINAL visibility:public flags: + $this: VALUE_PARAMETER INSTANCE_RECEIVER name: type:F flags: + TYPE_PARAMETER name:T index:0 variance: upperBounds:[kotlin.Float] + CONSTRUCTOR visibility:public <> () returnType:F flags: + BLOCK_BODY + DELEGATING_CONSTRUCTOR_CALL 'constructor Any()' + INSTANCE_INITIALIZER_CALL classDescriptor='F' + FUN name:testCapturedType visibility:public modality:FINAL <> ($this:F, x:T, y:kotlin.Any) returnType:Boolean flags: + $this: VALUE_PARAMETER name: type:F flags: + VALUE_PARAMETER name:x index:0 type:T flags: + VALUE_PARAMETER name:y index:1 type:kotlin.Any flags: + BLOCK_BODY + RETURN type=kotlin.Nothing from='testCapturedType(T, Any): Boolean' + WHEN type=kotlin.Boolean origin=ANDAND + BRANCH + if: TYPE_OP type=kotlin.Boolean origin=INSTANCEOF typeOperand=kotlin.Double + GET_VAR 'value-parameter y: Any' type=kotlin.Any origin=null + then: CALL 'ieee754equals(Double?, Double?): Boolean' type=kotlin.Boolean origin=EQEQ + arg0: CALL 'toDouble(): Double' type=kotlin.Double origin=null + $this: GET_VAR 'value-parameter x: T' type=T 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 + 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: \ No newline at end of file diff --git a/compiler/tests/org/jetbrains/kotlin/ir/IrTextTestCaseGenerated.java b/compiler/tests/org/jetbrains/kotlin/ir/IrTextTestCaseGenerated.java index f32bbb4dc7e..144fd0679ad 100644 --- a/compiler/tests/org/jetbrains/kotlin/ir/IrTextTestCaseGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/ir/IrTextTestCaseGenerated.java @@ -1058,6 +1058,12 @@ public class IrTextTestCaseGenerated extends AbstractIrTextTestCase { doTest(fileName); } + @TestMetadata("typeParameterWithPrimitiveNumericSupertype.kt") + public void testTypeParameterWithPrimitiveNumericSupertype() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/ir/irText/expressions/floatingPointComparisons/typeParameterWithPrimitiveNumericSupertype.kt"); + doTest(fileName); + } + @TestMetadata("whenByFloatingPoint.kt") public void testWhenByFloatingPoint() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/ir/irText/expressions/floatingPointComparisons/whenByFloatingPoint.kt");