diff --git a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/ConversionUtils.kt b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/ConversionUtils.kt index e2d388061b0..c0677e6485b 100644 --- a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/ConversionUtils.kt +++ b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/ConversionUtils.kt @@ -459,3 +459,10 @@ fun FirClass<*>.irOrigin(firProvider: FirProvider): IrDeclarationOrigin = when { origin == FirDeclarationOrigin.Java -> IrDeclarationOrigin.IR_EXTERNAL_JAVA_DECLARATION_STUB else -> IrDeclarationOrigin.IR_EXTERNAL_DECLARATION_STUB } + +val IrType.isSamType: Boolean + get() { + val irClass = classOrNull ?: return false + val am = irClass.functions.singleOrNull { it.owner.modality == Modality.ABSTRACT } + return am != null + } diff --git a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/generators/CallAndReferenceGenerator.kt b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/generators/CallAndReferenceGenerator.kt index 6c72b8f3c1d..bdcee655bc4 100644 --- a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/generators/CallAndReferenceGenerator.kt +++ b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/generators/CallAndReferenceGenerator.kt @@ -6,6 +6,7 @@ package org.jetbrains.kotlin.fir.backend.generators import org.jetbrains.kotlin.fir.backend.* +import org.jetbrains.kotlin.fir.declarations.FirAnonymousFunction import org.jetbrains.kotlin.fir.declarations.FirClass import org.jetbrains.kotlin.fir.declarations.FirValueParameter import org.jetbrains.kotlin.fir.expressions.* @@ -16,6 +17,7 @@ import org.jetbrains.kotlin.fir.references.FirReference import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference import org.jetbrains.kotlin.fir.references.FirSuperReference import org.jetbrains.kotlin.fir.render +import org.jetbrains.kotlin.fir.resolve.inference.isBuiltinFunctionalType import org.jetbrains.kotlin.fir.resolve.toSymbol import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol import org.jetbrains.kotlin.fir.symbols.impl.FirClassSymbol @@ -28,6 +30,7 @@ import org.jetbrains.kotlin.ir.declarations.IrProperty import org.jetbrains.kotlin.ir.expressions.IrErrorCallExpression import org.jetbrains.kotlin.ir.expressions.IrExpression import org.jetbrains.kotlin.ir.expressions.IrStatementOrigin +import org.jetbrains.kotlin.ir.expressions.IrTypeOperator import org.jetbrains.kotlin.ir.expressions.impl.* import org.jetbrains.kotlin.ir.symbols.* import org.jetbrains.kotlin.ir.types.* @@ -325,22 +328,22 @@ internal class CallAndReferenceGenerator( val argumentsCount = call.arguments.size if (argumentsCount <= valueArgumentsCount) { apply { + val calleeReference = when (call) { + is FirFunctionCall -> call.calleeReference + is FirDelegatedConstructorCall -> call.calleeReference + else -> null + } as? FirResolvedNamedReference + val function = (calleeReference?.resolvedSymbol as? FirFunctionSymbol<*>)?.fir + val valueParameters = function?.valueParameters val argumentMapping = call.argumentMapping if (argumentMapping != null && argumentMapping.isNotEmpty()) { - val calleeReference = when (call) { - is FirFunctionCall -> call.calleeReference - is FirDelegatedConstructorCall -> call.calleeReference - else -> throw IllegalArgumentException("Unsupported call: ${call.render()}") - } as? FirResolvedNamedReference - val function = (calleeReference?.resolvedSymbol as? FirFunctionSymbol<*>)?.fir - val valueParameters = function?.valueParameters - if (valueParameters != null) { return applyArgumentsWithReorderingIfNeeded(argumentMapping, valueParameters) } } for ((index, argument) in call.arguments.withIndex()) { - val argumentExpression = visitor.convertToIrExpression(argument) + val argumentExpression = + visitor.convertToIrExpression(argument).applySamConversionIfNeeded(argument, valueParameters?.get(index)) putValueArgument(index, argumentExpression) } } @@ -373,7 +376,7 @@ internal class CallAndReferenceGenerator( return IrBlockImpl(startOffset, endOffset, type, IrStatementOrigin.ARGUMENTS_REORDERING_FOR_CALL).apply { for ((argument, parameter) in argumentMapping) { val parameterIndex = valueParameters.indexOf(parameter) - val irArgument = visitor.convertToIrExpression(argument) + val irArgument = visitor.convertToIrExpression(argument).applySamConversionIfNeeded(argument, parameter) if (irArgument.hasNoSideEffects()) { putValueArgument(parameterIndex, irArgument) } else { @@ -388,7 +391,7 @@ internal class CallAndReferenceGenerator( } } else { for ((argument, parameter) in argumentMapping) { - val argumentExpression = visitor.convertToIrExpression(argument) + val argumentExpression = visitor.convertToIrExpression(argument).applySamConversionIfNeeded(argument, parameter) putValueArgument(valueParameters.indexOf(parameter), argumentExpression) } return this @@ -410,6 +413,21 @@ internal class CallAndReferenceGenerator( return false } + private fun IrExpression.applySamConversionIfNeeded( + argument: FirExpression, + parameter: FirValueParameter? + ): IrExpression { + if (parameter == null || + parameter.returnTypeRef.coneTypeSafe()?.isBuiltinFunctionalType(session) == true + ) return this + if (argument !is FirLambdaArgumentExpression) return this + if (argument.expression !is FirAnonymousFunction) return this + if (argument.expression.typeRef == parameter.returnTypeRef) return this + val samType = parameter.returnTypeRef.toIrType() + if (!samType.isSamType) return this + return IrTypeOperatorCallImpl(this.startOffset, this.endOffset, samType, IrTypeOperator.SAM_CONVERSION, samType, this) + } + private fun IrExpression.applyTypeArguments(access: FirQualifiedAccess): IrExpression { return when (this) { is IrMemberAccessExpressionBase -> { diff --git a/compiler/testData/codegen/box/funInterface/basicFunInterfaceConversion.kt b/compiler/testData/codegen/box/funInterface/basicFunInterfaceConversion.kt index 6e454d357aa..8baf88358f2 100644 --- a/compiler/testData/codegen/box/funInterface/basicFunInterfaceConversion.kt +++ b/compiler/testData/codegen/box/funInterface/basicFunInterfaceConversion.kt @@ -1,5 +1,4 @@ // !LANGUAGE: +NewInference +FunctionalInterfaceConversion +SamConversionPerArgument +SamConversionForKotlinFunctions -// IGNORE_BACKEND_FIR: JVM_IR // SKIP_DCE_DRIVEN fun interface Foo { diff --git a/compiler/testData/codegen/box/funInterface/equality/simpleLambdas.kt b/compiler/testData/codegen/box/funInterface/equality/simpleLambdas.kt index 4ae798dbaf4..1e1bd2bcdbd 100644 --- a/compiler/testData/codegen/box/funInterface/equality/simpleLambdas.kt +++ b/compiler/testData/codegen/box/funInterface/equality/simpleLambdas.kt @@ -1,5 +1,4 @@ // IGNORE_BACKEND: JS_IR -// IGNORE_BACKEND_FIR: JVM_IR fun interface FunInterface { fun invoke() diff --git a/compiler/testData/ir/irText/expressions/funInterface/basicFunInterfaceConversion.fir.txt b/compiler/testData/ir/irText/expressions/funInterface/basicFunInterfaceConversion.fir.txt index e13ace076c9..4a72567999d 100644 --- a/compiler/testData/ir/irText/expressions/funInterface/basicFunInterfaceConversion.fir.txt +++ b/compiler/testData/ir/irText/expressions/funInterface/basicFunInterfaceConversion.fir.txt @@ -26,8 +26,9 @@ FILE fqName: fileName:/basicFunInterfaceConversion.kt BLOCK_BODY RETURN type=kotlin.Nothing from='public final fun test (): kotlin.String declared in ' CALL 'public final fun foo (f: .Foo): kotlin.String declared in ' type=kotlin.String origin=null - f: FUN_EXPR type=kotlin.Function0 origin=LAMBDA - FUN LOCAL_FUNCTION_FOR_LAMBDA name: visibility:local modality:FINAL <> () returnType:kotlin.String - BLOCK_BODY - RETURN type=kotlin.Nothing from='local final fun (): kotlin.String declared in .test' - CONST String type=kotlin.String value="OK" + f: TYPE_OP type=.Foo origin=SAM_CONVERSION typeOperand=.Foo + FUN_EXPR type=kotlin.Function0 origin=LAMBDA + FUN LOCAL_FUNCTION_FOR_LAMBDA name: visibility:local modality:FINAL <> () returnType:kotlin.String + BLOCK_BODY + RETURN type=kotlin.Nothing from='local final fun (): kotlin.String declared in .test' + CONST String type=kotlin.String value="OK" diff --git a/compiler/testData/ir/irText/expressions/funInterface/partialSam.fir.txt b/compiler/testData/ir/irText/expressions/funInterface/partialSam.fir.txt index 064a619fb53..9360156a158 100644 --- a/compiler/testData/ir/irText/expressions/funInterface/partialSam.fir.txt +++ b/compiler/testData/ir/irText/expressions/funInterface/partialSam.fir.txt @@ -139,14 +139,15 @@ FILE fqName: fileName:/partialSam.kt CALL 'public final fun runConversion (f1: .Fn, f2: .Fn): kotlin.Int declared in .J' type=kotlin.Int origin=null $this: GET_VAR 'j: .J declared in .test' type=.J origin=null f1: CALL 'public final fun (): .fsi. declared in ' type=.fsi. origin=GET_PROPERTY - f2: FUN_EXPR type=kotlin.Function3 origin=LAMBDA - FUN LOCAL_FUNCTION_FOR_LAMBDA name: visibility:local modality:FINAL <> (s:kotlin.String, i:kotlin.Int, ti:kotlin.Int) returnType:kotlin.String - VALUE_PARAMETER name:s index:0 type:kotlin.String - VALUE_PARAMETER name:i index:1 type:kotlin.Int - VALUE_PARAMETER name:ti index:2 type:kotlin.Int - BLOCK_BODY - RETURN type=kotlin.Nothing from='local final fun (s: kotlin.String, i: kotlin.Int, ti: kotlin.Int): kotlin.String declared in .test' - CONST String type=kotlin.String value="" + f2: TYPE_OP type=.Fn origin=SAM_CONVERSION typeOperand=.Fn + FUN_EXPR type=kotlin.Function3 origin=LAMBDA + FUN LOCAL_FUNCTION_FOR_LAMBDA name: visibility:local modality:FINAL <> (s:kotlin.String, i:kotlin.Int, ti:kotlin.Int) returnType:kotlin.String + VALUE_PARAMETER name:s index:0 type:kotlin.String + VALUE_PARAMETER name:i index:1 type:kotlin.Int + VALUE_PARAMETER name:ti index:2 type:kotlin.Int + BLOCK_BODY + RETURN type=kotlin.Nothing from='local final fun (s: kotlin.String, i: kotlin.Int, ti: kotlin.Int): kotlin.String declared in .test' + CONST String type=kotlin.String value="" CALL 'public final fun runConversion (f1: .Fn, f2: .Fn): kotlin.Int declared in .J' type=kotlin.Int origin=null $this: GET_VAR 'j: .J declared in .test' type=.J origin=null f1: FUN_EXPR type=kotlin.Function3 origin=LAMBDA diff --git a/compiler/testData/ir/irText/types/castsInsideCoroutineInference.fir.txt b/compiler/testData/ir/irText/types/castsInsideCoroutineInference.fir.txt index b8b88a92610..b2263411a89 100644 --- a/compiler/testData/ir/irText/types/castsInsideCoroutineInference.fir.txt +++ b/compiler/testData/ir/irText/types/castsInsideCoroutineInference.fir.txt @@ -94,26 +94,27 @@ FILE fqName: fileName:/castsInsideCoroutineInference.kt $this: GET_VAR ': .ProducerScope declared in .asFairChannel.' type=.ProducerScope origin=null CALL 'public abstract fun collect (collector: .FlowCollector.Flow>): kotlin.Unit [suspend] declared in .Flow' type=kotlin.Unit origin=null $this: GET_VAR 'flow: .Flow<*> declared in .asFairChannel' type=.Flow<*> origin=null - collector: FUN_EXPR type=kotlin.Function1 origin=LAMBDA - FUN LOCAL_FUNCTION_FOR_LAMBDA name: visibility:local modality:FINAL <> (value:kotlin.Any?) returnType:kotlin.Unit - VALUE_PARAMETER name:value index:0 type:kotlin.Any? - BLOCK_BODY - RETURN type=kotlin.Nothing from='local final fun (value: kotlin.Any?): kotlin.Unit declared in .asFairChannel.' - CALL 'public final fun sendFair (element: E of .ChannelCoroutine): kotlin.Unit [suspend] declared in .ChannelCoroutine' type=kotlin.Unit origin=null - $this: GET_VAR 'val channel: .ChannelCoroutine [val] declared in .asFairChannel.' type=.ChannelCoroutine origin=null - element: BLOCK type=kotlin.Any origin=ELVIS - VAR IR_TEMPORARY_VARIABLE name:tmp_0 type:kotlin.Any? [val] - GET_VAR 'value: kotlin.Any? declared in .asFairChannel..' type=kotlin.Any? origin=null - WHEN type=kotlin.Any origin=ELVIS - BRANCH - if: CALL 'public final fun EQEQ (arg0: kotlin.Any?, arg1: kotlin.Any?): kotlin.Boolean declared in kotlin.internal.ir' type=kotlin.Boolean origin=EQEQ - arg0: GET_VAR 'val tmp_0: kotlin.Any? [val] declared in .asFairChannel..' type=kotlin.Any? origin=null - arg1: CONST Null type=kotlin.Nothing? value=null - then: CONSTRUCTOR_CALL 'public constructor () [primary] declared in kotlin.Any' type=kotlin.Any origin=null - BRANCH - if: CONST Boolean type=kotlin.Boolean value=true - then: TYPE_OP type=kotlin.Any origin=IMPLICIT_CAST typeOperand=kotlin.Any - GET_VAR 'val tmp_0: kotlin.Any? [val] declared in .asFairChannel..' type=kotlin.Any? origin=null + collector: TYPE_OP type=.FlowCollector origin=SAM_CONVERSION typeOperand=.FlowCollector + FUN_EXPR type=kotlin.Function1 origin=LAMBDA + FUN LOCAL_FUNCTION_FOR_LAMBDA name: visibility:local modality:FINAL <> (value:kotlin.Any?) returnType:kotlin.Unit + VALUE_PARAMETER name:value index:0 type:kotlin.Any? + BLOCK_BODY + RETURN type=kotlin.Nothing from='local final fun (value: kotlin.Any?): kotlin.Unit declared in .asFairChannel.' + CALL 'public final fun sendFair (element: E of .ChannelCoroutine): kotlin.Unit [suspend] declared in .ChannelCoroutine' type=kotlin.Unit origin=null + $this: GET_VAR 'val channel: .ChannelCoroutine [val] declared in .asFairChannel.' type=.ChannelCoroutine origin=null + element: BLOCK type=kotlin.Any origin=ELVIS + VAR IR_TEMPORARY_VARIABLE name:tmp_0 type:kotlin.Any? [val] + GET_VAR 'value: kotlin.Any? declared in .asFairChannel..' type=kotlin.Any? origin=null + WHEN type=kotlin.Any origin=ELVIS + BRANCH + if: CALL 'public final fun EQEQ (arg0: kotlin.Any?, arg1: kotlin.Any?): kotlin.Boolean declared in kotlin.internal.ir' type=kotlin.Boolean origin=EQEQ + arg0: GET_VAR 'val tmp_0: kotlin.Any? [val] declared in .asFairChannel..' type=kotlin.Any? origin=null + arg1: CONST Null type=kotlin.Nothing? value=null + then: CONSTRUCTOR_CALL 'public constructor () [primary] declared in kotlin.Any' type=kotlin.Any origin=null + BRANCH + if: CONST Boolean type=kotlin.Boolean value=true + then: TYPE_OP type=kotlin.Any origin=IMPLICIT_CAST typeOperand=kotlin.Any + GET_VAR 'val tmp_0: kotlin.Any? [val] declared in .asFairChannel..' type=kotlin.Any? origin=null FUN name:asChannel visibility:private modality:FINAL <> ($receiver:.CoroutineScope, flow:.Flow<*>) returnType:.ReceiveChannel $receiver: VALUE_PARAMETER name: type:.CoroutineScope VALUE_PARAMETER name:flow index:0 type:.Flow<*> @@ -128,27 +129,28 @@ FILE fqName: fileName:/castsInsideCoroutineInference.kt BLOCK_BODY CALL 'public abstract fun collect (collector: .FlowCollector.Flow>): kotlin.Unit [suspend] declared in .Flow' type=kotlin.Unit origin=null $this: GET_VAR 'flow: .Flow<*> declared in .asChannel' type=.Flow<*> origin=null - collector: FUN_EXPR type=kotlin.Function1 origin=LAMBDA - FUN LOCAL_FUNCTION_FOR_LAMBDA name: visibility:local modality:FINAL <> (value:kotlin.Any?) returnType:kotlin.Unit - VALUE_PARAMETER name:value index:0 type:kotlin.Any? - BLOCK_BODY - RETURN type=kotlin.Nothing from='local final fun (value: kotlin.Any?): kotlin.Unit declared in .asChannel.' - CALL 'public abstract fun send (e: E of .SendChannel): kotlin.Unit [suspend] declared in .SendChannel' type=kotlin.Unit origin=null - $this: CALL 'public abstract fun (): .SendChannel.ProducerScope> declared in .ProducerScope' type=.SendChannel origin=GET_PROPERTY - $this: GET_VAR ': .ProducerScope declared in .asChannel.' type=.ProducerScope origin=null - e: BLOCK type=kotlin.Any origin=ELVIS - VAR IR_TEMPORARY_VARIABLE name:tmp_1 type:kotlin.Any? [val] - GET_VAR 'value: kotlin.Any? declared in .asChannel..' type=kotlin.Any? origin=null - WHEN type=kotlin.Any origin=ELVIS - BRANCH - if: CALL 'public final fun EQEQ (arg0: kotlin.Any?, arg1: kotlin.Any?): kotlin.Boolean declared in kotlin.internal.ir' type=kotlin.Boolean origin=EQEQ - arg0: GET_VAR 'val tmp_1: kotlin.Any? [val] declared in .asChannel..' type=kotlin.Any? origin=null - arg1: CONST Null type=kotlin.Nothing? value=null - then: CONSTRUCTOR_CALL 'public constructor () [primary] declared in kotlin.Any' type=kotlin.Any origin=null - BRANCH - if: CONST Boolean type=kotlin.Boolean value=true - then: TYPE_OP type=kotlin.Any origin=IMPLICIT_CAST typeOperand=kotlin.Any - GET_VAR 'val tmp_1: kotlin.Any? [val] declared in .asChannel..' type=kotlin.Any? origin=null + collector: TYPE_OP type=.FlowCollector origin=SAM_CONVERSION typeOperand=.FlowCollector + FUN_EXPR type=kotlin.Function1 origin=LAMBDA + FUN LOCAL_FUNCTION_FOR_LAMBDA name: visibility:local modality:FINAL <> (value:kotlin.Any?) returnType:kotlin.Unit + VALUE_PARAMETER name:value index:0 type:kotlin.Any? + BLOCK_BODY + RETURN type=kotlin.Nothing from='local final fun (value: kotlin.Any?): kotlin.Unit declared in .asChannel.' + CALL 'public abstract fun send (e: E of .SendChannel): kotlin.Unit [suspend] declared in .SendChannel' type=kotlin.Unit origin=null + $this: CALL 'public abstract fun (): .SendChannel.ProducerScope> declared in .ProducerScope' type=.SendChannel origin=GET_PROPERTY + $this: GET_VAR ': .ProducerScope declared in .asChannel.' type=.ProducerScope origin=null + e: BLOCK type=kotlin.Any origin=ELVIS + VAR IR_TEMPORARY_VARIABLE name:tmp_1 type:kotlin.Any? [val] + GET_VAR 'value: kotlin.Any? declared in .asChannel..' type=kotlin.Any? origin=null + WHEN type=kotlin.Any origin=ELVIS + BRANCH + if: CALL 'public final fun EQEQ (arg0: kotlin.Any?, arg1: kotlin.Any?): kotlin.Boolean declared in kotlin.internal.ir' type=kotlin.Boolean origin=EQEQ + arg0: GET_VAR 'val tmp_1: kotlin.Any? [val] declared in .asChannel..' type=kotlin.Any? origin=null + arg1: CONST Null type=kotlin.Nothing? value=null + then: CONSTRUCTOR_CALL 'public constructor () [primary] declared in kotlin.Any' type=kotlin.Any origin=null + BRANCH + if: CONST Boolean type=kotlin.Boolean value=true + then: TYPE_OP type=kotlin.Any origin=IMPLICIT_CAST typeOperand=kotlin.Any + GET_VAR 'val tmp_1: kotlin.Any? [val] declared in .asChannel..' type=kotlin.Any? origin=null CLASS CLASS name:SafeCollector modality:FINAL visibility:public superTypes:[.FlowCollector.SafeCollector>] $this: VALUE_PARAMETER INSTANCE_RECEIVER name: type:.SafeCollector.SafeCollector> TYPE_PARAMETER name:T index:0 variance: superTypes:[kotlin.Any?]