diff --git a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/Fir2IrTypeConverter.kt b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/Fir2IrTypeConverter.kt index f0a3579b764..79349cd0bfc 100644 --- a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/Fir2IrTypeConverter.kt +++ b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/Fir2IrTypeConverter.kt @@ -363,3 +363,8 @@ context(Fir2IrComponents) internal fun ConeKotlinType.approximateForIrOrNull(): ConeKotlinType? { return session.typeApproximator.approximateToSuperType(this, TypeApproximatorConfiguration.FrontendToBackendTypesApproximation) } + +context(Fir2IrComponents) +internal fun ConeKotlinType.approximateForIrOrSelf(): ConeKotlinType { + return approximateForIrOrNull() ?: this +} 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 1c47bb80ed7..1ed478b8998 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 @@ -426,14 +426,26 @@ internal class AdapterGenerator( private fun castArgumentToFunctionalInterfaceForSamType( argument: IrExpression, argumentConeType: ConeKotlinType, - samType: ConeKotlinType + samType: ConeKotlinType, ): IrExpression { - val coneKotlinFunctionType = getFunctionTypeForPossibleSamType(samType) ?: return argument + // The rule for SAM conversions is: + // the argument must be a non-reflection function type and be a subtype of the required function type. + // We handle intersection types, captured types, etc. by approximating both expected and actual types. + val approximatedConeKotlinFunctionType = getFunctionTypeForPossibleSamType(samType)?.approximateForIrOrSelf() ?: 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() + val approximateArgumentConeType = argumentConeType.approximateForIrOrSelf() + + if (approximateArgumentConeType.isSubtypeOf(approximatedConeKotlinFunctionType, session) + // TODO uncomment when KT-63510 is fixed, probably fixes KT-62865 + // && approximateArgumentConeType.lowerBoundIfFlexible().functionTypeKind(session)?.isReflectType == false + ) { + return argument + } + + val irFunctionType = approximatedConeKotlinFunctionType.toIrType() return argument.implicitCastTo(irFunctionType) } diff --git a/compiler/testData/ir/irText/expressions/funInterface/samConversionsWithSmartCasts.fir.ir.txt b/compiler/testData/ir/irText/expressions/funInterface/samConversionsWithSmartCasts.fir.ir.txt index f6a2af9bdac..3c9aa904cc2 100644 --- a/compiler/testData/ir/irText/expressions/funInterface/samConversionsWithSmartCasts.fir.ir.txt +++ b/compiler/testData/ir/irText/expressions/funInterface/samConversionsWithSmartCasts.fir.ir.txt @@ -125,7 +125,8 @@ FILE fqName: fileName:/samConversionsWithSmartCasts.kt GET_VAR 'a: kotlin.Function1 declared in .test7' type=kotlin.Function1 origin=null CALL 'public final fun run1 (r: .KRunnable): kotlin.Unit declared in ' type=kotlin.Unit origin=null r: TYPE_OP type=.KRunnable origin=SAM_CONVERSION typeOperand=.KRunnable - GET_VAR 'a: kotlin.Function1 declared in .test7' type=kotlin.Function1 origin=null + TYPE_OP type=kotlin.Function0 origin=IMPLICIT_CAST typeOperand=kotlin.Function0 + GET_VAR 'a: kotlin.Function1 declared in .test7' type=kotlin.Function1 origin=null FUN name:test7a visibility:public modality:FINAL (a:T of .test7a) returnType:kotlin.Unit TYPE_PARAMETER name:T index:0 variance: superTypes:[kotlin.Function1] reified:false VALUE_PARAMETER name:a index:0 type:T of .test7a @@ -135,7 +136,8 @@ FILE fqName: fileName:/samConversionsWithSmartCasts.kt GET_VAR 'a: T of .test7a declared in .test7a' type=T of .test7a origin=null CALL 'public final fun run1 (r: .KRunnable): kotlin.Unit declared in ' type=kotlin.Unit origin=null r: TYPE_OP type=.KRunnable origin=SAM_CONVERSION typeOperand=.KRunnable - GET_VAR 'a: T of .test7a declared in .test7a' type=T of .test7a origin=null + TYPE_OP type=kotlin.Function0 origin=IMPLICIT_CAST typeOperand=kotlin.Function0 + GET_VAR 'a: T of .test7a declared in .test7a' type=T of .test7a origin=null FUN name:test7b visibility:public modality:FINAL (a:T of .test7b) returnType:kotlin.Unit TYPE_PARAMETER name:T index:0 variance: superTypes:[kotlin.Function1; kotlin.Function0] reified:false VALUE_PARAMETER name:a index:0 type:T of .test7b @@ -178,3 +180,23 @@ FILE fqName: fileName:/samConversionsWithSmartCasts.kt CALL 'public final fun run1 (r: .KRunnable): kotlin.Unit declared in ' type=kotlin.Unit origin=null r: TYPE_OP type=.KRunnable origin=SAM_CONVERSION typeOperand=.KRunnable FUNCTION_REFERENCE 'public final fun test9 (): kotlin.Unit declared in ' type=kotlin.reflect.KFunction0 origin=null reflectionTarget= + FUN name:test10 visibility:public modality:FINAL <> (a:kotlin.Any) returnType:kotlin.Unit + VALUE_PARAMETER name:a index:0 type:kotlin.Any + BLOCK_BODY + WHEN type=kotlin.Unit origin=IF + BRANCH + if: WHEN type=kotlin.Boolean origin=ANDAND + BRANCH + if: TYPE_OP type=kotlin.Boolean origin=INSTANCEOF typeOperand=.Unrelated + GET_VAR 'a: kotlin.Any declared in .test10' type=kotlin.Any origin=null + then: TYPE_OP type=kotlin.Boolean origin=INSTANCEOF typeOperand=kotlin.Function0 + TYPE_OP type=.Unrelated origin=IMPLICIT_CAST typeOperand=.Unrelated + GET_VAR 'a: kotlin.Any declared in .test10' type=kotlin.Any origin=null + BRANCH + if: CONST Boolean type=kotlin.Boolean value=true + then: CONST Boolean type=kotlin.Boolean value=false + then: BLOCK type=kotlin.Unit origin=null + CALL 'public final fun run1 (r: .KRunnable): kotlin.Unit declared in ' type=kotlin.Unit origin=null + r: TYPE_OP type=.KRunnable origin=SAM_CONVERSION typeOperand=.KRunnable + TYPE_OP type=kotlin.Function0 origin=IMPLICIT_CAST typeOperand=kotlin.Function0 + GET_VAR 'a: kotlin.Any declared in .test10' type=kotlin.Any origin=null diff --git a/compiler/testData/ir/irText/expressions/funInterface/samConversionsWithSmartCasts.fir.kt.txt b/compiler/testData/ir/irText/expressions/funInterface/samConversionsWithSmartCasts.fir.kt.txt index aa519312dbc..38ce376d9d8 100644 --- a/compiler/testData/ir/irText/expressions/funInterface/samConversionsWithSmartCasts.fir.kt.txt +++ b/compiler/testData/ir/irText/expressions/funInterface/samConversionsWithSmartCasts.fir.kt.txt @@ -70,12 +70,12 @@ fun test6(a: Any) { fun test7(a: Function1) { a as Function0 /*~> Unit */ - run1(r = a /*-> KRunnable */) + run1(r = a /*as Function0 */ /*-> KRunnable */) } fun > test7a(a: T) { a as Function0 /*~> Unit */ - run1(r = a /*-> KRunnable */) + run1(r = a /*as Function0 */ /*-> KRunnable */) } fun test7b(a: T) where T : Function1, T : Function0 { @@ -97,3 +97,14 @@ fun test9() { run1(r = ::test9 /*-> KRunnable */) } +fun test10(a: Any) { + when { + when { + a is Unrelated -> a /*as Unrelated */ is Function0 + else -> false + } -> { // BLOCK + run1(r = a /*as Function0 */ /*-> KRunnable */) + } + } +} + diff --git a/compiler/testData/ir/irText/expressions/funInterface/samConversionsWithSmartCasts.ir.txt b/compiler/testData/ir/irText/expressions/funInterface/samConversionsWithSmartCasts.ir.txt index 67d646c021a..b49994cf50e 100644 --- a/compiler/testData/ir/irText/expressions/funInterface/samConversionsWithSmartCasts.ir.txt +++ b/compiler/testData/ir/irText/expressions/funInterface/samConversionsWithSmartCasts.ir.txt @@ -179,3 +179,22 @@ FILE fqName: fileName:/samConversionsWithSmartCasts.kt CALL 'public final fun run1 (r: .KRunnable): kotlin.Unit declared in ' type=kotlin.Unit origin=null r: TYPE_OP type=.KRunnable origin=SAM_CONVERSION typeOperand=.KRunnable FUNCTION_REFERENCE 'public final fun test9 (): kotlin.Unit declared in ' type=kotlin.reflect.KFunction0 origin=null reflectionTarget= + FUN name:test10 visibility:public modality:FINAL <> (a:kotlin.Any) returnType:kotlin.Unit + VALUE_PARAMETER name:a index:0 type:kotlin.Any + BLOCK_BODY + WHEN type=kotlin.Unit origin=IF + BRANCH + if: WHEN type=kotlin.Boolean origin=ANDAND + BRANCH + if: TYPE_OP type=kotlin.Boolean origin=INSTANCEOF typeOperand=.Unrelated + GET_VAR 'a: kotlin.Any declared in .test10' type=kotlin.Any origin=null + then: TYPE_OP type=kotlin.Boolean origin=INSTANCEOF typeOperand=kotlin.Function0 + GET_VAR 'a: kotlin.Any declared in .test10' type=kotlin.Any origin=null + BRANCH + if: CONST Boolean type=kotlin.Boolean value=true + then: CONST Boolean type=kotlin.Boolean value=false + then: BLOCK type=kotlin.Unit origin=null + CALL 'public final fun run1 (r: .KRunnable): kotlin.Unit declared in ' type=kotlin.Unit origin=null + r: TYPE_OP type=.KRunnable origin=SAM_CONVERSION typeOperand=.KRunnable + TYPE_OP type=kotlin.Function0 origin=IMPLICIT_CAST typeOperand=kotlin.Function0 + GET_VAR 'a: kotlin.Any declared in .test10' type=kotlin.Any origin=null diff --git a/compiler/testData/ir/irText/expressions/funInterface/samConversionsWithSmartCasts.kt b/compiler/testData/ir/irText/expressions/funInterface/samConversionsWithSmartCasts.kt index e5e4a65a59d..bf0d5362034 100644 --- a/compiler/testData/ir/irText/expressions/funInterface/samConversionsWithSmartCasts.kt +++ b/compiler/testData/ir/irText/expressions/funInterface/samConversionsWithSmartCasts.kt @@ -79,3 +79,11 @@ fun test8(a: () -> Unit) { fun test9() { run1(::test9) } + +// KT-63345 +fun test10(a: Any) { + @Suppress("CANNOT_CHECK_FOR_ERASED") + if (a is Unrelated && a is (() -> Unit)) { + run1(a) + } +} diff --git a/compiler/testData/ir/irText/expressions/funInterface/samConversionsWithSmartCasts.kt.txt b/compiler/testData/ir/irText/expressions/funInterface/samConversionsWithSmartCasts.kt.txt index 84fbd888a6d..a60696ee3ed 100644 --- a/compiler/testData/ir/irText/expressions/funInterface/samConversionsWithSmartCasts.kt.txt +++ b/compiler/testData/ir/irText/expressions/funInterface/samConversionsWithSmartCasts.kt.txt @@ -96,3 +96,14 @@ fun test8(a: Function0) { fun test9() { run1(r = ::test9 /*-> KRunnable */) } + +fun test10(a: Any) { + when { + when { + a is Unrelated -> a is Function0 + else -> false + } -> { // BLOCK + run1(r = a /*as Function0 */ /*-> KRunnable */) + } + } +} diff --git a/compiler/testData/ir/irText/expressions/funInterface/samConversionsWithSmartCasts.sig.kt.txt b/compiler/testData/ir/irText/expressions/funInterface/samConversionsWithSmartCasts.sig.kt.txt index 817d0a41186..37de316b115 100644 --- a/compiler/testData/ir/irText/expressions/funInterface/samConversionsWithSmartCasts.sig.kt.txt +++ b/compiler/testData/ir/irText/expressions/funInterface/samConversionsWithSmartCasts.sig.kt.txt @@ -50,6 +50,12 @@ fun test0(a: T): Unit where T : KRunnable, T : Function0 // Public signature debug description: test1(kotlin.Function0){} fun test1(a: Function0): Unit +// CHECK: +// Mangled name: #test10(kotlin.Any){} +// Public signature: /test10|8673682583765656768[0] +// Public signature debug description: test10(kotlin.Any){} +fun test10(a: Any): Unit + // CHECK: // Mangled name: #test2(KRunnable){} // Public signature: /test2|-6262171320756539088[0] diff --git a/compiler/testData/ir/irText/expressions/sam/samConversionsWithSmartCasts.fir.ir.txt b/compiler/testData/ir/irText/expressions/sam/samConversionsWithSmartCasts.fir.ir.txt index a842958304f..544bfee590c 100644 --- a/compiler/testData/ir/irText/expressions/sam/samConversionsWithSmartCasts.fir.ir.txt +++ b/compiler/testData/ir/irText/expressions/sam/samConversionsWithSmartCasts.fir.ir.txt @@ -99,7 +99,8 @@ FILE fqName: fileName:/samConversionsWithSmartCasts.kt CALL 'public open fun run1 (r: @[FlexibleNullability] java.lang.Runnable?): kotlin.Unit declared in .J' type=kotlin.Unit origin=null $this: CONSTRUCTOR_CALL 'public constructor () declared in .J' type=.J origin=null r: TYPE_OP type=java.lang.Runnable origin=SAM_CONVERSION typeOperand=java.lang.Runnable - GET_VAR 'a: kotlin.Function1 declared in .test7' type=kotlin.Function1 origin=null + TYPE_OP type=kotlin.Function0 origin=IMPLICIT_CAST typeOperand=kotlin.Function0 + GET_VAR 'a: kotlin.Function1 declared in .test7' type=kotlin.Function1 origin=null FUN name:test8 visibility:public modality:FINAL <> (a:kotlin.Function0) returnType:kotlin.Unit VALUE_PARAMETER name:a index:0 type:kotlin.Function0 BLOCK_BODY diff --git a/compiler/testData/ir/irText/expressions/sam/samConversionsWithSmartCasts.fir.kt.txt b/compiler/testData/ir/irText/expressions/sam/samConversionsWithSmartCasts.fir.kt.txt index 01ff674e970..fcd384296f9 100644 --- a/compiler/testData/ir/irText/expressions/sam/samConversionsWithSmartCasts.fir.kt.txt +++ b/compiler/testData/ir/irText/expressions/sam/samConversionsWithSmartCasts.fir.kt.txt @@ -54,7 +54,7 @@ fun test6(a: Any) { fun test7(a: Function1) { a as Function0 /*~> Unit */ - J().run1(r = a /*-> Runnable */) + J().run1(r = a /*as Function0 */ /*-> Runnable */) } fun test8(a: Function0) {