From e66c2621af0959910f6be60ef61905e99b27584b Mon Sep 17 00:00:00 2001 From: Dmitry Petrov Date: Mon, 27 Feb 2017 17:32:39 +0300 Subject: [PATCH] KT-16436 Incorrect primitive constants handling Expression '1.unaryMinus()' is resolved as Int::unaryMinus call with Int receiver. However, this expression is implicitly coerced to a different integral type (Byte, Short, Long) based on results of constant evaluation. Introduce IMPLICIT_INTEGER_COERCION type operator to handle such cases. TODO: should we use it for simple constant expressions like '1' and '-1'? --- .../transformations/InsertImplicitCasts.kt | 36 ++++++++++--------- .../ir/expressions/IrTypeOperatorCall.kt | 1 + .../primitivesImplicitConversions.kt | 9 +++++ .../primitivesImplicitConversions.txt | 23 ++++++++++++ .../kotlin/ir/IrTextTestCaseGenerated.java | 6 ++++ 5 files changed, 58 insertions(+), 17 deletions(-) create mode 100644 compiler/testData/ir/irText/expressions/primitivesImplicitConversions.kt create mode 100644 compiler/testData/ir/irText/expressions/primitivesImplicitConversions.txt diff --git a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/transformations/InsertImplicitCasts.kt b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/transformations/InsertImplicitCasts.kt index 19b9daca187..a48837440ee 100644 --- a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/transformations/InsertImplicitCasts.kt +++ b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/transformations/InsertImplicitCasts.kt @@ -93,7 +93,7 @@ class InsertImplicitCasts(val builtIns: KotlinBuiltIns): IrElementTransformerVoi override fun visitReturn(expression: IrReturn): IrExpression { expression.transformChildrenVoid(this) - expression.value = expression.value?.cast(expression.returnTarget.returnType) + expression.value = expression.value.cast(expression.returnTarget.returnType) return expression } @@ -186,27 +186,29 @@ class InsertImplicitCasts(val builtIns: KotlinBuiltIns): IrElementTransformerVoi private fun IrExpression.cast(expectedType: KotlinType?): IrExpression { if (expectedType == null) return this - if (expectedType.isError) return this - if (KotlinBuiltIns.isUnit(expectedType)) return coerceToUnit() - val valueType = this.type - if (valueType.isNullabilityFlexible() && valueType.containsNull() && !expectedType.containsNull()) { - val nonNullValueType = valueType.upperIfFlexible().makeNotNullable() - return IrTypeOperatorCallImpl( - this.startOffset, this.endOffset, nonNullValueType, - IrTypeOperator.IMPLICIT_NOTNULL, nonNullValueType, this - ).cast(expectedType) + return when { + KotlinBuiltIns.isUnit(expectedType) -> + coerceToUnit() + valueType.isNullabilityFlexible() && valueType.containsNull() && !expectedType.containsNull() -> { + val nonNullValueType = valueType.upperIfFlexible().makeNotNullable() + IrTypeOperatorCallImpl( + this.startOffset, this.endOffset, nonNullValueType, + IrTypeOperator.IMPLICIT_NOTNULL, nonNullValueType, this + ).cast(expectedType) + } + KotlinTypeChecker.DEFAULT.isSubtypeOf(valueType.makeNotNullable(), expectedType) -> + this + KotlinBuiltIns.isInt(valueType) -> + IrTypeOperatorCallImpl(this.startOffset, this.endOffset, expectedType, + IrTypeOperator.IMPLICIT_INTEGER_COERCION, expectedType.makeNotNullable(), this) + else -> + IrTypeOperatorCallImpl(this.startOffset, this.endOffset, expectedType, + IrTypeOperator.IMPLICIT_CAST, expectedType, this) } - - if (!KotlinTypeChecker.DEFAULT.isSubtypeOf(valueType.makeNotNullable(), expectedType)) { - return IrTypeOperatorCallImpl(this.startOffset, this.endOffset, expectedType, - IrTypeOperator.IMPLICIT_CAST, expectedType, this) - } - - return this } private fun IrExpression.coerceToUnit(): IrExpression { diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrTypeOperatorCall.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrTypeOperatorCall.kt index 6787bbd0c8b..30a42ed868f 100644 --- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrTypeOperatorCall.kt +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrTypeOperatorCall.kt @@ -23,6 +23,7 @@ enum class IrTypeOperator { IMPLICIT_CAST, IMPLICIT_NOTNULL, IMPLICIT_COERCION_TO_UNIT, + IMPLICIT_INTEGER_COERCION, SAFE_CAST, INSTANCEOF, NOT_INSTANCEOF; diff --git a/compiler/testData/ir/irText/expressions/primitivesImplicitConversions.kt b/compiler/testData/ir/irText/expressions/primitivesImplicitConversions.kt new file mode 100644 index 00000000000..37cfb06a536 --- /dev/null +++ b/compiler/testData/ir/irText/expressions/primitivesImplicitConversions.kt @@ -0,0 +1,9 @@ +fun test() { + val test1: Int? = 42 + val test2: Long = 42 + val test3: Long? = 42 + val test4: Long? = -1 + val test5: Long? = 1.unaryMinus() + val test6: Short? = 1.unaryMinus() + val test7: Byte? = 1.unaryMinus() +} \ No newline at end of file diff --git a/compiler/testData/ir/irText/expressions/primitivesImplicitConversions.txt b/compiler/testData/ir/irText/expressions/primitivesImplicitConversions.txt new file mode 100644 index 00000000000..8b5970fa100 --- /dev/null +++ b/compiler/testData/ir/irText/expressions/primitivesImplicitConversions.txt @@ -0,0 +1,23 @@ +FILE /primitivesImplicitConversions.kt + FUN public fun test(): kotlin.Unit + BLOCK_BODY + VAR val test1: kotlin.Int? = 42 + CONST Int type=kotlin.Int value='42' + VAR val test2: kotlin.Long = 42.toLong() + CONST Long type=kotlin.Long value='42' + VAR val test3: kotlin.Long? = 42.toLong() + CONST Long type=kotlin.Long value='42' + VAR val test4: kotlin.Long? = -1.toLong() + CONST Long type=kotlin.Long value='-1' + VAR val test5: kotlin.Long? = -1.toLong() + TYPE_OP origin=IMPLICIT_INTEGER_COERCION typeOperand=kotlin.Long + CALL 'unaryMinus(): Int' type=kotlin.Int origin=null + $this: CONST Int type=kotlin.Int value='1' + VAR val test6: kotlin.Short? = -1.toShort() + TYPE_OP origin=IMPLICIT_INTEGER_COERCION typeOperand=kotlin.Short + CALL 'unaryMinus(): Int' type=kotlin.Int origin=null + $this: CONST Int type=kotlin.Int value='1' + VAR val test7: kotlin.Byte? = -1.toByte() + TYPE_OP origin=IMPLICIT_INTEGER_COERCION typeOperand=kotlin.Byte + CALL 'unaryMinus(): Int' type=kotlin.Int origin=null + $this: CONST Int type=kotlin.Int value='1' diff --git a/compiler/tests/org/jetbrains/kotlin/ir/IrTextTestCaseGenerated.java b/compiler/tests/org/jetbrains/kotlin/ir/IrTextTestCaseGenerated.java index 1a627499c94..d5842f1dad9 100644 --- a/compiler/tests/org/jetbrains/kotlin/ir/IrTextTestCaseGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/ir/IrTextTestCaseGenerated.java @@ -545,6 +545,12 @@ public class IrTextTestCaseGenerated extends AbstractIrTextTestCase { doTest(fileName); } + @TestMetadata("primitivesImplicitConversions.kt") + public void testPrimitivesImplicitConversions() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/ir/irText/expressions/primitivesImplicitConversions.kt"); + doTest(fileName); + } + @TestMetadata("references.kt") public void testReferences() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/ir/irText/expressions/references.kt");