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 9e0fd813ad4..423976b052f 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 @@ -21,6 +21,7 @@ import org.jetbrains.kotlin.builtins.isFunctionType import org.jetbrains.kotlin.builtins.isSuspendFunctionType import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.impl.AnonymousFunctionDescriptor +import org.jetbrains.kotlin.incremental.components.NoLookupLocation import org.jetbrains.kotlin.ir.IrElement import org.jetbrains.kotlin.ir.IrStatement import org.jetbrains.kotlin.ir.declarations.IrClass @@ -29,20 +30,26 @@ import org.jetbrains.kotlin.ir.declarations.IrFunction import org.jetbrains.kotlin.ir.declarations.IrVariable import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns import org.jetbrains.kotlin.ir.expressions.* +import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl +import org.jetbrains.kotlin.ir.expressions.impl.IrConstImpl import org.jetbrains.kotlin.ir.expressions.impl.IrTypeOperatorCallImpl import org.jetbrains.kotlin.ir.symbols.IrConstructorSymbol import org.jetbrains.kotlin.ir.types.IrType import org.jetbrains.kotlin.ir.types.impl.originalKotlinType +import org.jetbrains.kotlin.ir.util.SymbolTable import org.jetbrains.kotlin.ir.util.TypeTranslator import org.jetbrains.kotlin.ir.util.coerceToUnit import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid +import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.psi2ir.containsNull +import org.jetbrains.kotlin.psi2ir.findSingleFunction import org.jetbrains.kotlin.psi2ir.generators.GeneratorContext import org.jetbrains.kotlin.psi2ir.generators.GeneratorExtensions import org.jetbrains.kotlin.psi2ir.generators.getSubstitutedFunctionTypeForSamType +import org.jetbrains.kotlin.resolve.descriptorUtil.module import org.jetbrains.kotlin.types.* import org.jetbrains.kotlin.types.checker.KotlinTypeChecker import org.jetbrains.kotlin.types.typeUtil.* @@ -53,7 +60,8 @@ fun insertImplicitCasts(element: IrElement, context: GeneratorContext) { context.irBuiltIns, context.typeTranslator, context.callToSubstitutedDescriptorMap, - context.extensions + context.extensions, + context.symbolTable ).run(element) } @@ -62,7 +70,8 @@ internal class InsertImplicitCasts( private val irBuiltIns: IrBuiltIns, private val typeTranslator: TypeTranslator, private val callToSubstitutedDescriptorMap: Map, - private val generatorExtensions: GeneratorExtensions + private val generatorExtensions: GeneratorExtensions, + private val symbolTable: SymbolTable ) : IrElementTransformerVoid() { private val expectedFunctionExpressionReturnType = hashMapOf() @@ -354,7 +363,7 @@ internal class InsertImplicitCasts( this KotlinBuiltIns.isInt(valueType) && notNullableExpectedType.isBuiltInIntegerType() -> - implicitCast(notNullableExpectedType, IrTypeOperator.IMPLICIT_INTEGER_COERCION) + coerceIntToAnotherIntegerType(notNullableExpectedType) else -> { val targetType = if (!valueType.containsNull()) notNullableExpectedType else expectedType @@ -389,19 +398,73 @@ internal class InsertImplicitCasts( return implicitCast(nonNullValueType, IrTypeOperator.IMPLICIT_NOTNULL).cast(expectedType) } - private fun IrExpression.implicitCast( - targetType: KotlinType, - typeOperator: IrTypeOperator - ): IrExpression { + private fun IrExpression.implicitCast(targetType: KotlinType, typeOperator: IrTypeOperator): IrExpression { val irType = targetType.toIrType() - return IrTypeOperatorCallImpl( - startOffset, - endOffset, - irType, - typeOperator, - irType, - this - ) + return IrTypeOperatorCallImpl(startOffset, endOffset, irType, typeOperator, irType, this) + } + + private fun IrExpression.coerceIntToAnotherIntegerType(targetType: KotlinType): IrExpression { + if (!type.originalKotlinType!!.isInt()) throw AssertionError("Expression of type 'kotlin.Int' expected: $this") + if (targetType.isInt()) return this + return if (this is IrConst<*>) { + val value = this.value as Int + val irType = targetType.toIrType() + when { + targetType.isByte() -> IrConstImpl.byte(startOffset, endOffset, irType, value.toByte()) + targetType.isShort() -> IrConstImpl.short(startOffset, endOffset, irType, value.toShort()) + targetType.isLong() -> IrConstImpl.long(startOffset, endOffset, irType, value.toLong()) + KotlinBuiltIns.isUByte(targetType) -> IrConstImpl.byte(startOffset, endOffset, irType, value.toByte()) + KotlinBuiltIns.isUShort(targetType) -> IrConstImpl.short(startOffset, endOffset, irType, value.toShort()) + KotlinBuiltIns.isUInt(targetType) -> IrConstImpl.int(startOffset, endOffset, irType, value) + KotlinBuiltIns.isULong(targetType) -> IrConstImpl.long(startOffset, endOffset, irType, value.toLong()) + else -> throw AssertionError("Unexpected target type for integer coercion: $targetType") + } + } else { + when { + targetType.isByte() -> invokeIntegerCoercionFunction(targetType, "toByte") + targetType.isShort() -> invokeIntegerCoercionFunction(targetType, "toShort") + targetType.isLong() -> invokeIntegerCoercionFunction(targetType, "toLong") + KotlinBuiltIns.isUByte(targetType) -> invokeUnsignedIntegerCoercionFunction(targetType, "toUByte") + KotlinBuiltIns.isUShort(targetType) -> invokeUnsignedIntegerCoercionFunction(targetType, "toUShort") + KotlinBuiltIns.isUInt(targetType) -> invokeUnsignedIntegerCoercionFunction(targetType, "toUInt") + KotlinBuiltIns.isULong(targetType) -> invokeUnsignedIntegerCoercionFunction(targetType, "toULong") + else -> throw AssertionError("Unexpected target type for integer coercion: $targetType") + } + } + } + + private fun IrExpression.invokeIntegerCoercionFunction(targetType: KotlinType, coercionFunName: String): IrExpression { + val coercionFunction = builtIns.int.unsubstitutedMemberScope.findSingleFunction(Name.identifier(coercionFunName)) + return IrCallImpl( + startOffset, endOffset, + targetType.toIrType(), + symbolTable.referenceSimpleFunction(coercionFunction), + typeArgumentsCount = 0, valueArgumentsCount = 0 + ).also { irCall -> + irCall.dispatchReceiver = this + } + } + + private fun IrExpression.invokeUnsignedIntegerCoercionFunction(targetType: KotlinType, coercionFunName: String): IrExpression { + // 'toUByte', 'toUShort', 'toUInt', 'toULong' are top-level extension functions in 'kotlin' package. + // There are several such functions (one for each built-in integer type: Byte, Short, Int, Long), + // we need one that takes Int. + val coercionFunction = targetType.constructor.declarationDescriptor!!.module + .getPackage(KotlinBuiltIns.BUILT_INS_PACKAGE_FQ_NAME) + .memberScope.getContributedFunctions(Name.identifier(coercionFunName), NoLookupLocation.FROM_BACKEND) + .find { + val extensionReceiver = it.extensionReceiverParameter + extensionReceiver != null && extensionReceiver.type.isInt() + } + ?: throw AssertionError("Coercion function '$coercionFunName' not found") + return IrCallImpl( + startOffset, endOffset, + targetType.toIrType(), + symbolTable.referenceSimpleFunction(coercionFunction), + typeArgumentsCount = 0, valueArgumentsCount = 0 + ).also { irCall -> + irCall.extensionReceiver = this + } } private fun KotlinType.isBuiltInIntegerType(): Boolean = diff --git a/compiler/testData/ir/irText/expressions/genericConstructorCallWithTypeArguments.txt b/compiler/testData/ir/irText/expressions/genericConstructorCallWithTypeArguments.txt index 78afc2f2b68..156ba677768 100644 --- a/compiler/testData/ir/irText/expressions/genericConstructorCallWithTypeArguments.txt +++ b/compiler/testData/ir/irText/expressions/genericConstructorCallWithTypeArguments.txt @@ -4,8 +4,8 @@ FILE fqName: fileName:/genericConstructorCallWithTypeArguments.kt RETURN type=kotlin.Nothing from='public final fun testSimple (): .Box declared in ' CONSTRUCTOR_CALL 'public constructor (value: T of .Box) [primary] declared in .Box' type=.Box origin=null : kotlin.Long - value: TYPE_OP type=kotlin.Long origin=IMPLICIT_INTEGER_COERCION typeOperand=kotlin.Long - CALL 'public final fun times (other: kotlin.Int): kotlin.Int [operator] declared in kotlin.Int' type=kotlin.Int origin=MUL + value: CALL 'public open fun toLong (): kotlin.Long declared in kotlin.Int' type=kotlin.Long origin=null + $this: CALL 'public final fun times (other: kotlin.Int): kotlin.Int [operator] declared in kotlin.Int' type=kotlin.Int origin=MUL $this: CONST Int type=kotlin.Int value=2 other: CONST Int type=kotlin.Int value=3 FUN name:testArray visibility:public modality:FINAL (n:kotlin.Int, block:kotlin.Function0.testArray>) returnType:kotlin.Array.testArray> [inline] diff --git a/compiler/testData/ir/irText/expressions/primitivesImplicitConversions.txt b/compiler/testData/ir/irText/expressions/primitivesImplicitConversions.txt index 9bd064593be..1ad672cde02 100644 --- a/compiler/testData/ir/irText/expressions/primitivesImplicitConversions.txt +++ b/compiler/testData/ir/irText/expressions/primitivesImplicitConversions.txt @@ -29,8 +29,8 @@ FILE fqName: fileName:/primitivesImplicitConversions.kt PROPERTY name:test4 visibility:public modality:FINAL [val] FIELD PROPERTY_BACKING_FIELD name:test4 type:kotlin.Long visibility:private [final,static] EXPRESSION_BODY - TYPE_OP type=kotlin.Long origin=IMPLICIT_INTEGER_COERCION typeOperand=kotlin.Long - CALL 'public final fun unaryMinus (): kotlin.Int [operator] declared in kotlin.Int' type=kotlin.Int origin=null + CALL 'public open fun toLong (): kotlin.Long declared in kotlin.Int' type=kotlin.Long origin=null + $this: CALL 'public final fun unaryMinus (): kotlin.Int [operator] declared in kotlin.Int' type=kotlin.Int origin=null $this: CONST Int type=kotlin.Int value=42 FUN DEFAULT_PROPERTY_ACCESSOR name: visibility:public modality:FINAL <> () returnType:kotlin.Long correspondingProperty: PROPERTY name:test4 visibility:public modality:FINAL [val] @@ -40,8 +40,8 @@ FILE fqName: fileName:/primitivesImplicitConversions.kt PROPERTY name:test5 visibility:public modality:FINAL [val] FIELD PROPERTY_BACKING_FIELD name:test5 type:kotlin.Short visibility:private [final,static] EXPRESSION_BODY - TYPE_OP type=kotlin.Short origin=IMPLICIT_INTEGER_COERCION typeOperand=kotlin.Short - CALL 'public final fun unaryMinus (): kotlin.Int [operator] declared in kotlin.Int' type=kotlin.Int origin=null + CALL 'public open fun toShort (): kotlin.Short declared in kotlin.Int' type=kotlin.Short origin=null + $this: CALL 'public final fun unaryMinus (): kotlin.Int [operator] declared in kotlin.Int' type=kotlin.Int origin=null $this: CONST Int type=kotlin.Int value=42 FUN DEFAULT_PROPERTY_ACCESSOR name: visibility:public modality:FINAL <> () returnType:kotlin.Short correspondingProperty: PROPERTY name:test5 visibility:public modality:FINAL [val] @@ -51,8 +51,8 @@ FILE fqName: fileName:/primitivesImplicitConversions.kt PROPERTY name:test6 visibility:public modality:FINAL [val] FIELD PROPERTY_BACKING_FIELD name:test6 type:kotlin.Byte visibility:private [final,static] EXPRESSION_BODY - TYPE_OP type=kotlin.Byte origin=IMPLICIT_INTEGER_COERCION typeOperand=kotlin.Byte - CALL 'public final fun unaryMinus (): kotlin.Int [operator] declared in kotlin.Int' type=kotlin.Int origin=null + CALL 'public open fun toByte (): kotlin.Byte declared in kotlin.Int' type=kotlin.Byte origin=null + $this: CALL 'public final fun unaryMinus (): kotlin.Int [operator] declared in kotlin.Int' type=kotlin.Int origin=null $this: CONST Int type=kotlin.Int value=42 FUN DEFAULT_PROPERTY_ACCESSOR name: visibility:public modality:FINAL <> () returnType:kotlin.Byte correspondingProperty: PROPERTY name:test6 visibility:public modality:FINAL [val] @@ -70,22 +70,22 @@ FILE fqName: fileName:/primitivesImplicitConversions.kt VAR name:test4 type:kotlin.Long? [val] CONST Long type=kotlin.Long value=-1 VAR name:test5 type:kotlin.Long? [val] - TYPE_OP type=kotlin.Long origin=IMPLICIT_INTEGER_COERCION typeOperand=kotlin.Long - CALL 'public final fun unaryMinus (): kotlin.Int [operator] declared in kotlin.Int' type=kotlin.Int origin=null + CALL 'public open fun toLong (): kotlin.Long declared in kotlin.Int' type=kotlin.Long origin=null + $this: CALL 'public final fun unaryMinus (): kotlin.Int [operator] declared in kotlin.Int' type=kotlin.Int origin=null $this: CONST Int type=kotlin.Int value=1 VAR name:test6 type:kotlin.Short? [val] - TYPE_OP type=kotlin.Short origin=IMPLICIT_INTEGER_COERCION typeOperand=kotlin.Short - CALL 'public final fun unaryMinus (): kotlin.Int [operator] declared in kotlin.Int' type=kotlin.Int origin=null + CALL 'public open fun toShort (): kotlin.Short declared in kotlin.Int' type=kotlin.Short origin=null + $this: CALL 'public final fun unaryMinus (): kotlin.Int [operator] declared in kotlin.Int' type=kotlin.Int origin=null $this: CONST Int type=kotlin.Int value=1 VAR name:test7 type:kotlin.Byte? [val] - TYPE_OP type=kotlin.Byte origin=IMPLICIT_INTEGER_COERCION typeOperand=kotlin.Byte - CALL 'public final fun unaryMinus (): kotlin.Int [operator] declared in kotlin.Int' type=kotlin.Int origin=null + CALL 'public open fun toByte (): kotlin.Byte declared in kotlin.Int' type=kotlin.Byte origin=null + $this: CALL 'public final fun unaryMinus (): kotlin.Int [operator] declared in kotlin.Int' type=kotlin.Int origin=null $this: CONST Int type=kotlin.Int value=1 FUN name:testImplicitArguments visibility:public modality:FINAL <> (x:kotlin.Long) returnType:kotlin.Unit VALUE_PARAMETER name:x index:0 type:kotlin.Long EXPRESSION_BODY - TYPE_OP type=kotlin.Long origin=IMPLICIT_INTEGER_COERCION typeOperand=kotlin.Long - CALL 'public final fun unaryMinus (): kotlin.Int [operator] declared in kotlin.Int' type=kotlin.Int origin=null + CALL 'public open fun toLong (): kotlin.Long declared in kotlin.Int' type=kotlin.Long origin=null + $this: CALL 'public final fun unaryMinus (): kotlin.Int [operator] declared in kotlin.Int' type=kotlin.Int origin=null $this: CONST Int type=kotlin.Int value=1 BLOCK_BODY CLASS CLASS name:TestImplicitArguments modality:FINAL visibility:public superTypes:[kotlin.Any] @@ -93,8 +93,8 @@ FILE fqName: fileName:/primitivesImplicitConversions.kt CONSTRUCTOR visibility:public <> (x:kotlin.Long) returnType:.TestImplicitArguments [primary] VALUE_PARAMETER name:x index:0 type:kotlin.Long EXPRESSION_BODY - TYPE_OP type=kotlin.Long origin=IMPLICIT_INTEGER_COERCION typeOperand=kotlin.Long - CALL 'public final fun unaryMinus (): kotlin.Int [operator] declared in kotlin.Int' type=kotlin.Int origin=null + CALL 'public open fun toLong (): kotlin.Long declared in kotlin.Int' type=kotlin.Long origin=null + $this: CALL 'public final fun unaryMinus (): kotlin.Int [operator] declared in kotlin.Int' type=kotlin.Int origin=null $this: CONST Int type=kotlin.Int value=1 BLOCK_BODY DELEGATING_CONSTRUCTOR_CALL 'public constructor () [primary] declared in kotlin.Any' diff --git a/compiler/testData/ir/irText/expressions/signedToUnsignedConversions_test.txt b/compiler/testData/ir/irText/expressions/signedToUnsignedConversions_test.txt index 9b9457f751b..1e1e6e7e94f 100644 --- a/compiler/testData/ir/irText/expressions/signedToUnsignedConversions_test.txt +++ b/compiler/testData/ir/irText/expressions/signedToUnsignedConversions_test.txt @@ -98,27 +98,27 @@ FILE fqName: fileName:/signedToUnsignedConversions_test.kt FUN name:test visibility:public modality:FINAL <> () returnType:kotlin.Unit BLOCK_BODY CALL 'public final fun takeUByte (u: kotlin.UByte): kotlin.Unit declared in ' type=kotlin.Unit origin=null - u: TYPE_OP type=kotlin.UByte origin=IMPLICIT_INTEGER_COERCION typeOperand=kotlin.UByte - CALL 'public final fun (): kotlin.Int declared in ' type=kotlin.Int origin=GET_PROPERTY + u: CALL 'public final fun toUByte (): kotlin.UByte [inline] declared in kotlin' type=kotlin.UByte origin=null + $receiver: CALL 'public final fun (): kotlin.Int declared in ' type=kotlin.Int origin=GET_PROPERTY CALL 'public final fun takeUByte (u: kotlin.UByte): kotlin.Unit declared in ' type=kotlin.Unit origin=null - u: TYPE_OP type=kotlin.UByte origin=IMPLICIT_INTEGER_COERCION typeOperand=kotlin.UByte - CALL 'public final fun (): kotlin.Int declared in ' type=kotlin.Int origin=GET_PROPERTY + u: CALL 'public final fun toUByte (): kotlin.UByte [inline] declared in kotlin' type=kotlin.UByte origin=null + $receiver: CALL 'public final fun (): kotlin.Int declared in ' type=kotlin.Int origin=GET_PROPERTY CALL 'public final fun takeUShort (u: kotlin.UShort): kotlin.Unit declared in ' type=kotlin.Unit origin=null - u: TYPE_OP type=kotlin.UShort origin=IMPLICIT_INTEGER_COERCION typeOperand=kotlin.UShort - CALL 'public final fun (): kotlin.Int declared in ' type=kotlin.Int origin=GET_PROPERTY + u: CALL 'public final fun toUShort (): kotlin.UShort [inline] declared in kotlin' type=kotlin.UShort origin=null + $receiver: CALL 'public final fun (): kotlin.Int declared in ' type=kotlin.Int origin=GET_PROPERTY CALL 'public final fun takeUShort (u: kotlin.UShort): kotlin.Unit declared in ' type=kotlin.Unit origin=null - u: TYPE_OP type=kotlin.UShort origin=IMPLICIT_INTEGER_COERCION typeOperand=kotlin.UShort - CALL 'public final fun (): kotlin.Int declared in ' type=kotlin.Int origin=GET_PROPERTY + u: CALL 'public final fun toUShort (): kotlin.UShort [inline] declared in kotlin' type=kotlin.UShort origin=null + $receiver: CALL 'public final fun (): kotlin.Int declared in ' type=kotlin.Int origin=GET_PROPERTY CALL 'public final fun takeUInt (u: kotlin.UInt): kotlin.Unit declared in ' type=kotlin.Unit origin=null - u: TYPE_OP type=kotlin.UInt origin=IMPLICIT_INTEGER_COERCION typeOperand=kotlin.UInt - CALL 'public final fun (): kotlin.Int declared in ' type=kotlin.Int origin=GET_PROPERTY + u: CALL 'public final fun toUInt (): kotlin.UInt [inline] declared in kotlin' type=kotlin.UInt origin=null + $receiver: CALL 'public final fun (): kotlin.Int declared in ' type=kotlin.Int origin=GET_PROPERTY CALL 'public final fun takeULong (u: kotlin.ULong): kotlin.Unit declared in ' type=kotlin.Unit origin=null - u: TYPE_OP type=kotlin.ULong origin=IMPLICIT_INTEGER_COERCION typeOperand=kotlin.ULong - CALL 'public final fun (): kotlin.Int declared in ' type=kotlin.Int origin=GET_PROPERTY + u: CALL 'public final fun toULong (): kotlin.ULong [inline] declared in kotlin' type=kotlin.ULong origin=null + $receiver: CALL 'public final fun (): kotlin.Int declared in ' type=kotlin.Int origin=GET_PROPERTY CALL 'public final fun takeUBytes (vararg u: kotlin.UByte): kotlin.Unit declared in ' type=kotlin.Unit origin=null u: VARARG type=kotlin.UByteArray varargElementType=kotlin.UByte - TYPE_OP type=kotlin.UByte origin=IMPLICIT_INTEGER_COERCION typeOperand=kotlin.UByte - CALL 'public final fun (): kotlin.Int declared in ' type=kotlin.Int origin=GET_PROPERTY - TYPE_OP type=kotlin.UByte origin=IMPLICIT_INTEGER_COERCION typeOperand=kotlin.UByte - CALL 'public final fun (): kotlin.Int declared in ' type=kotlin.Int origin=GET_PROPERTY + CALL 'public final fun toUByte (): kotlin.UByte [inline] declared in kotlin' type=kotlin.UByte origin=null + $receiver: CALL 'public final fun (): kotlin.Int declared in ' type=kotlin.Int origin=GET_PROPERTY + CALL 'public final fun toUByte (): kotlin.UByte [inline] declared in kotlin' type=kotlin.UByte origin=null + $receiver: CALL 'public final fun (): kotlin.Int declared in ' type=kotlin.Int origin=GET_PROPERTY CONST Byte type=kotlin.UByte value=42