Check for type parameter supertypes in primitive numeric comparisons
This commit is contained in:
+13
-1
@@ -6,6 +6,7 @@
|
|||||||
package org.jetbrains.kotlin.resolve.checkers
|
package org.jetbrains.kotlin.resolve.checkers
|
||||||
|
|
||||||
import com.intellij.psi.PsiElement
|
import com.intellij.psi.PsiElement
|
||||||
|
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
|
||||||
import org.jetbrains.kotlin.lexer.KtTokens
|
import org.jetbrains.kotlin.lexer.KtTokens
|
||||||
import org.jetbrains.kotlin.psi.KtBinaryExpression
|
import org.jetbrains.kotlin.psi.KtBinaryExpression
|
||||||
import org.jetbrains.kotlin.psi.KtExpression
|
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.resolve.calls.smartcasts.DataFlowValueFactory
|
||||||
import org.jetbrains.kotlin.types.KotlinType
|
import org.jetbrains.kotlin.types.KotlinType
|
||||||
import org.jetbrains.kotlin.types.typeUtil.*
|
import org.jetbrains.kotlin.types.typeUtil.*
|
||||||
|
import org.jetbrains.kotlin.utils.addToStdlib.firstNotNullResult
|
||||||
|
|
||||||
class PrimitiveNumericComparisonInfo(
|
class PrimitiveNumericComparisonInfo(
|
||||||
val comparisonType: KotlinType,
|
val comparisonType: KotlinType,
|
||||||
@@ -90,5 +92,15 @@ object PrimitiveNumericComparisonCallChecker : CallChecker {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun List<KotlinType>.findPrimitiveType() =
|
private fun List<KotlinType>.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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+11
@@ -0,0 +1,11 @@
|
|||||||
|
fun <T> test0(x: Any, y: T) = x is Int && x == y
|
||||||
|
fun <T : Float> test1(x: Any, y: T) = x is Float && x == y
|
||||||
|
fun <T : Double> test2(x: Any, y: T) = x is Float && x == y
|
||||||
|
fun <T : Float> test3(x: Any, y: T) = x is Int && x == y
|
||||||
|
fun <T : Float?> test4(x: Any, y: T) = x is Int && x == y
|
||||||
|
fun <T : Float?, R : T> test5(x: Any, y: R) = x is Int && x == y
|
||||||
|
fun <T : Number> test6(x: Any, y: T) = x is Int && x == y
|
||||||
|
|
||||||
|
class F<T : Float> {
|
||||||
|
fun testCapturedType(x: T, y: Any) = y is Double && x == y
|
||||||
|
}
|
||||||
+155
@@ -0,0 +1,155 @@
|
|||||||
|
FILE fqName:<root> fileName:/typeParameterWithPrimitiveNumericSupertype.kt
|
||||||
|
FUN name:test0 visibility:public modality:FINAL <T> (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 <T> (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 <T> (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 <T> (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 <T> (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 <T, R> (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 <T> (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:<this> type:F<T> flags:
|
||||||
|
TYPE_PARAMETER name:T index:0 variance: upperBounds:[kotlin.Float]
|
||||||
|
CONSTRUCTOR visibility:public <> () returnType:F<T> flags:
|
||||||
|
BLOCK_BODY
|
||||||
|
DELEGATING_CONSTRUCTOR_CALL 'constructor Any()'
|
||||||
|
INSTANCE_INITIALIZER_CALL classDescriptor='F'
|
||||||
|
FUN name:testCapturedType visibility:public modality:FINAL <> ($this:F<T>, x:T, y:kotlin.Any) returnType:Boolean flags:
|
||||||
|
$this: VALUE_PARAMETER name:<this> type:F<T> 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:<this> 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:<this> type:kotlin.Any flags:
|
||||||
|
FUN FAKE_OVERRIDE name:toString visibility:public modality:OPEN <> ($this:kotlin.Any) returnType:String flags:
|
||||||
|
$this: VALUE_PARAMETER name:<this> type:kotlin.Any flags:
|
||||||
@@ -1058,6 +1058,12 @@ public class IrTextTestCaseGenerated extends AbstractIrTextTestCase {
|
|||||||
doTest(fileName);
|
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")
|
@TestMetadata("whenByFloatingPoint.kt")
|
||||||
public void testWhenByFloatingPoint() throws Exception {
|
public void testWhenByFloatingPoint() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/ir/irText/expressions/floatingPointComparisons/whenByFloatingPoint.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/ir/irText/expressions/floatingPointComparisons/whenByFloatingPoint.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user