Chars are not promoted to Int on comparisons

This commit is contained in:
Dmitry Petrov
2018-02-26 15:30:11 +03:00
parent 4bb6c61a5a
commit 8fbdf52d34
11 changed files with 230 additions and 3 deletions
@@ -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
}
@@ -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')
}
@@ -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')
}
+20
View File
@@ -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
}
+125
View File
@@ -0,0 +1,125 @@
FILE fqName:<root> fileName:/kt23030.kt
FUN name:compareTo visibility:public modality:FINAL <> ($receiver:kotlin.Int, c:kotlin.Char) returnType:Int flags:
$receiver: VALUE_PARAMETER name:<this> 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:<this> 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:<this> type:C flags:
$receiver: VALUE_PARAMETER name:<this> 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:<this> 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:<this> 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:<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:
@@ -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");
@@ -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");
@@ -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");
@@ -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");
@@ -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)
@@ -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");