From 99448b77f3aee6930ae91c2daf170ab450ae556b Mon Sep 17 00:00:00 2001 From: "Denis.Zharkov" Date: Wed, 15 Feb 2023 16:07:20 +0100 Subject: [PATCH] FIR2IR: Imitate K1 behavior when SAM type has a bit different shape See the relevant test at t/ estData/codegen/box/sam/contravariantIntersectionType.kt After the previous commit, when generating a call for `doOnSuccess {}`, we have a SAM type with removed projection Consumer, while real function type of the lambda is a bit different (Function1). While for me, that looks questionable, but in K1 it was effectively resolved via the same implicit cast that on JVM is generated to an INVOKEDYNAMIC that wraps initial function-typed value. ^KT-53552 Fixed --- .../backend/generators/AdapterGenerator.kt | 21 +++++++- ...ndConversionForSimpleExpression.fir.ir.txt | 51 ++++++++++--------- .../ir/irText/firProblems/kt19251.fir.ir.txt | 13 ++--- .../ir/irText/firProblems/kt19251.fir.kt.txt | 2 +- 4 files changed, 55 insertions(+), 32 deletions(-) diff --git a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/generators/AdapterGenerator.kt b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/generators/AdapterGenerator.kt index bc88167f326..b3ca25031a4 100644 --- a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/generators/AdapterGenerator.kt +++ b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/generators/AdapterGenerator.kt @@ -44,6 +44,7 @@ import org.jetbrains.kotlin.ir.types.impl.IrSimpleTypeImpl import org.jetbrains.kotlin.ir.types.impl.makeTypeProjection import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.psi2ir.generators.implicitCastTo import org.jetbrains.kotlin.types.AbstractTypeChecker import org.jetbrains.kotlin.types.Variance import org.jetbrains.kotlin.types.model.TypeVariance @@ -443,7 +444,25 @@ internal class AdapterGenerator( val samType = samFirType.toIrType(ConversionTypeContext.DEFAULT) // Make sure the converted IrType owner indeed has a single abstract method, since FunctionReferenceLowering relies on it. if (!samType.isSamType) return this - return IrTypeOperatorCallImpl(this.startOffset, this.endOffset, samType, IrTypeOperator.SAM_CONVERSION, samType, this) + return IrTypeOperatorCallImpl( + this.startOffset, this.endOffset, samType, IrTypeOperator.SAM_CONVERSION, samType, + castArgumentToFunctionalInterfaceForSamType(this, argument.typeRef.coneType, samFirType) + ) + } + + // See org.jetbrains.kotlin.psi2ir.generators.ArgumentsGenerationUtilsKt.castArgumentToFunctionalInterfaceForSamType (K1 counterpart) + private fun castArgumentToFunctionalInterfaceForSamType( + argument: IrExpression, + argumentConeType: ConeKotlinType, + samType: ConeKotlinType + ): IrExpression { + val coneKotlinFunctionType = getFunctionTypeForPossibleSamType(samType) ?: return argument + // This line is not present in the K1 counterpart because there is InsertImplicitCasts::cast that effectively removes + // such unnecessary casts. At the same time, many IR lowerings assume that there are no such redundant casts and many + // tests from FirBlackBoxCodegenTestGenerated relevant to INDY start failing once this line is removed. + if (argumentConeType.isSubtypeOf(coneKotlinFunctionType, session)) return argument + val irFunctionType = coneKotlinFunctionType.toIrType() + return argument.implicitCastTo(irFunctionType) } // This function is mostly a mirror of org.jetbrains.kotlin.backend.common.SamTypeApproximator.removeExternalProjections diff --git a/compiler/testData/ir/irText/expressions/chainedFunSuspendConversionForSimpleExpression.fir.ir.txt b/compiler/testData/ir/irText/expressions/chainedFunSuspendConversionForSimpleExpression.fir.ir.txt index da398e97deb..6772ebf733e 100644 --- a/compiler/testData/ir/irText/expressions/chainedFunSuspendConversionForSimpleExpression.fir.ir.txt +++ b/compiler/testData/ir/irText/expressions/chainedFunSuspendConversionForSimpleExpression.fir.ir.txt @@ -32,33 +32,36 @@ FILE fqName: fileName:/chainedFunSuspendConversionForSimpleExpression.kt BLOCK_BODY CALL 'public final fun foo (s: .SuspendRunnable): kotlin.Unit declared in ' type=kotlin.Unit origin=null s: TYPE_OP type=.SuspendRunnable origin=SAM_CONVERSION typeOperand=.SuspendRunnable - BLOCK type=kotlin.coroutines.SuspendFunction0 origin=SUSPEND_CONVERSION - FUN ADAPTER_FOR_SUSPEND_CONVERSION name:suspendConversion visibility:local modality:FINAL <> ($receiver:kotlin.Function0) returnType:kotlin.Unit [suspend] - $receiver: VALUE_PARAMETER ADAPTER_PARAMETER_FOR_SUSPEND_CONVERSION name:callee type:kotlin.Function0 - BLOCK_BODY - CALL 'public abstract fun invoke (): R of kotlin.Function0 [operator] declared in kotlin.Function0' type=kotlin.Unit origin=null - $this: GET_VAR 'callee: kotlin.Function0 declared in .test.suspendConversion' type=kotlin.Function0 origin=null - FUNCTION_REFERENCE 'local final fun suspendConversion (): kotlin.Unit [suspend] declared in .test' type=kotlin.coroutines.SuspendFunction0 origin=SUSPEND_CONVERSION reflectionTarget=null - $receiver: GET_VAR 'f: kotlin.Function0 declared in .test' type=kotlin.Function0 origin=null + TYPE_OP type=kotlin.coroutines.SuspendFunction0 origin=IMPLICIT_CAST typeOperand=kotlin.coroutines.SuspendFunction0 + BLOCK type=kotlin.coroutines.SuspendFunction0 origin=SUSPEND_CONVERSION + FUN ADAPTER_FOR_SUSPEND_CONVERSION name:suspendConversion visibility:local modality:FINAL <> ($receiver:kotlin.Function0) returnType:kotlin.Unit [suspend] + $receiver: VALUE_PARAMETER ADAPTER_PARAMETER_FOR_SUSPEND_CONVERSION name:callee type:kotlin.Function0 + BLOCK_BODY + CALL 'public abstract fun invoke (): R of kotlin.Function0 [operator] declared in kotlin.Function0' type=kotlin.Unit origin=null + $this: GET_VAR 'callee: kotlin.Function0 declared in .test.suspendConversion' type=kotlin.Function0 origin=null + FUNCTION_REFERENCE 'local final fun suspendConversion (): kotlin.Unit [suspend] declared in .test' type=kotlin.coroutines.SuspendFunction0 origin=SUSPEND_CONVERSION reflectionTarget=null + $receiver: GET_VAR 'f: kotlin.Function0 declared in .test' type=kotlin.Function0 origin=null CALL 'public final fun foo (s: .SuspendRunnable): kotlin.Unit declared in ' type=kotlin.Unit origin=null s: TYPE_OP type=.SuspendRunnable origin=SAM_CONVERSION typeOperand=.SuspendRunnable - BLOCK type=kotlin.coroutines.SuspendFunction0 origin=SUSPEND_CONVERSION - FUN ADAPTER_FOR_SUSPEND_CONVERSION name:suspendConversion visibility:local modality:FINAL <> ($receiver:kotlin.Function0) returnType:kotlin.Unit [suspend] - $receiver: VALUE_PARAMETER ADAPTER_PARAMETER_FOR_SUSPEND_CONVERSION name:callee type:kotlin.Function0 - BLOCK_BODY - CALL 'public abstract fun invoke (): R of kotlin.Function0 [operator] declared in kotlin.Function0' type=kotlin.Unit origin=null - $this: GET_VAR 'callee: kotlin.Function0 declared in .test.suspendConversion' type=kotlin.Function0 origin=null - FUNCTION_REFERENCE 'local final fun suspendConversion (): kotlin.Unit [suspend] declared in .test' type=kotlin.coroutines.SuspendFunction0 origin=SUSPEND_CONVERSION reflectionTarget=null - $receiver: CALL 'public final fun bar (): kotlin.Function0 declared in ' type=kotlin.Function0 origin=null + TYPE_OP type=kotlin.coroutines.SuspendFunction0 origin=IMPLICIT_CAST typeOperand=kotlin.coroutines.SuspendFunction0 + BLOCK type=kotlin.coroutines.SuspendFunction0 origin=SUSPEND_CONVERSION + FUN ADAPTER_FOR_SUSPEND_CONVERSION name:suspendConversion visibility:local modality:FINAL <> ($receiver:kotlin.Function0) returnType:kotlin.Unit [suspend] + $receiver: VALUE_PARAMETER ADAPTER_PARAMETER_FOR_SUSPEND_CONVERSION name:callee type:kotlin.Function0 + BLOCK_BODY + CALL 'public abstract fun invoke (): R of kotlin.Function0 [operator] declared in kotlin.Function0' type=kotlin.Unit origin=null + $this: GET_VAR 'callee: kotlin.Function0 declared in .test.suspendConversion' type=kotlin.Function0 origin=null + FUNCTION_REFERENCE 'local final fun suspendConversion (): kotlin.Unit [suspend] declared in .test' type=kotlin.coroutines.SuspendFunction0 origin=SUSPEND_CONVERSION reflectionTarget=null + $receiver: CALL 'public final fun bar (): kotlin.Function0 declared in ' type=kotlin.Function0 origin=null VAR name:t type:kotlin.Function0 [var] GET_VAR 'f: kotlin.Function0 declared in .test' type=kotlin.Function0 origin=null CALL 'public final fun foo (s: .SuspendRunnable): kotlin.Unit declared in ' type=kotlin.Unit origin=null s: TYPE_OP type=.SuspendRunnable origin=SAM_CONVERSION typeOperand=.SuspendRunnable - BLOCK type=kotlin.coroutines.SuspendFunction0 origin=SUSPEND_CONVERSION - FUN ADAPTER_FOR_SUSPEND_CONVERSION name:suspendConversion visibility:local modality:FINAL <> ($receiver:kotlin.Function0) returnType:kotlin.Unit [suspend] - $receiver: VALUE_PARAMETER ADAPTER_PARAMETER_FOR_SUSPEND_CONVERSION name:callee type:kotlin.Function0 - BLOCK_BODY - CALL 'public abstract fun invoke (): R of kotlin.Function0 [operator] declared in kotlin.Function0' type=kotlin.Unit origin=null - $this: GET_VAR 'callee: kotlin.Function0 declared in .test.suspendConversion' type=kotlin.Function0 origin=null - FUNCTION_REFERENCE 'local final fun suspendConversion (): kotlin.Unit [suspend] declared in .test' type=kotlin.coroutines.SuspendFunction0 origin=SUSPEND_CONVERSION reflectionTarget=null - $receiver: GET_VAR 'var t: kotlin.Function0 [var] declared in .test' type=kotlin.Function0 origin=null + TYPE_OP type=kotlin.coroutines.SuspendFunction0 origin=IMPLICIT_CAST typeOperand=kotlin.coroutines.SuspendFunction0 + BLOCK type=kotlin.coroutines.SuspendFunction0 origin=SUSPEND_CONVERSION + FUN ADAPTER_FOR_SUSPEND_CONVERSION name:suspendConversion visibility:local modality:FINAL <> ($receiver:kotlin.Function0) returnType:kotlin.Unit [suspend] + $receiver: VALUE_PARAMETER ADAPTER_PARAMETER_FOR_SUSPEND_CONVERSION name:callee type:kotlin.Function0 + BLOCK_BODY + CALL 'public abstract fun invoke (): R of kotlin.Function0 [operator] declared in kotlin.Function0' type=kotlin.Unit origin=null + $this: GET_VAR 'callee: kotlin.Function0 declared in .test.suspendConversion' type=kotlin.Function0 origin=null + FUNCTION_REFERENCE 'local final fun suspendConversion (): kotlin.Unit [suspend] declared in .test' type=kotlin.coroutines.SuspendFunction0 origin=SUSPEND_CONVERSION reflectionTarget=null + $receiver: GET_VAR 'var t: kotlin.Function0 [var] declared in .test' type=kotlin.Function0 origin=null diff --git a/compiler/testData/ir/irText/firProblems/kt19251.fir.ir.txt b/compiler/testData/ir/irText/firProblems/kt19251.fir.ir.txt index 707632c37af..952033b40fd 100644 --- a/compiler/testData/ir/irText/firProblems/kt19251.fir.ir.txt +++ b/compiler/testData/ir/irText/firProblems/kt19251.fir.ir.txt @@ -18,9 +18,10 @@ FILE fqName: fileName:/test.kt $this: GET_VAR 'val map: kotlin.collections.MutableMap<.Fun, kotlin.String> [val] declared in .box' type=kotlin.collections.MutableMap<.Fun, kotlin.String> origin=null p0: GET_VAR 'val fn: .Fun [val] declared in .box' type=.Fun origin=null p1: TYPE_OP type=@[EnhancedNullability] java.util.function.Function origin=SAM_CONVERSION typeOperand=@[EnhancedNullability] java.util.function.Function - FUN_EXPR type=kotlin.Function1<@[EnhancedNullability] .Fun, @[EnhancedNullability] kotlin.String> origin=LAMBDA - FUN LOCAL_FUNCTION_FOR_LAMBDA name: visibility:local modality:FINAL <> (it:@[EnhancedNullability] .Fun) returnType:@[EnhancedNullability] kotlin.String - VALUE_PARAMETER name:it index:0 type:@[EnhancedNullability] .Fun - BLOCK_BODY - RETURN type=kotlin.Nothing from='local final fun (it: @[EnhancedNullability] .Fun): @[EnhancedNullability] kotlin.String declared in .box' - CONST String type=kotlin.String value="OK" + TYPE_OP type=kotlin.Function1<@[EnhancedNullability] kotlin.Any?, @[EnhancedNullability] kotlin.String> origin=IMPLICIT_CAST typeOperand=kotlin.Function1<@[EnhancedNullability] kotlin.Any?, @[EnhancedNullability] kotlin.String> + FUN_EXPR type=kotlin.Function1<@[EnhancedNullability] .Fun, @[EnhancedNullability] kotlin.String> origin=LAMBDA + FUN LOCAL_FUNCTION_FOR_LAMBDA name: visibility:local modality:FINAL <> (it:@[EnhancedNullability] .Fun) returnType:@[EnhancedNullability] kotlin.String + VALUE_PARAMETER name:it index:0 type:@[EnhancedNullability] .Fun + BLOCK_BODY + RETURN type=kotlin.Nothing from='local final fun (it: @[EnhancedNullability] .Fun): @[EnhancedNullability] kotlin.String declared in .box' + CONST String type=kotlin.String value="OK" diff --git a/compiler/testData/ir/irText/firProblems/kt19251.fir.kt.txt b/compiler/testData/ir/irText/firProblems/kt19251.fir.kt.txt index 5425b0b218b..cf710bd0530 100644 --- a/compiler/testData/ir/irText/firProblems/kt19251.fir.kt.txt +++ b/compiler/testData/ir/irText/firProblems/kt19251.fir.kt.txt @@ -7,5 +7,5 @@ fun box(): String { return map.computeIfAbsent(p0 = fn, p1 = local fun (it: @EnhancedNullability Fun): @EnhancedNullability String { return "OK" } - /*-> @EnhancedNullability Function */) /*!! String */ + /*as Function1<@EnhancedNullability Any?, @EnhancedNullability String> */ /*-> @EnhancedNullability Function */) /*!! String */ }