[FIR2IR] Fix cast insertion for SAM conversion

The rule is: no cast is required iff the argument type is a
non-reflection function type and a subtype of the expected
function type.
We approximate the cone types using the FIR2IR specific config
to account for intersection types, captured types, etc.

#KT-63345 Fixed
#KT-63510
#KT-62865
This commit is contained in:
Kirill Rakhman
2023-11-15 15:25:15 +01:00
committed by Space Team
parent dd1d36816b
commit b857c28ab3
10 changed files with 105 additions and 10 deletions
@@ -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
}
@@ -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)
}
@@ -125,7 +125,8 @@ FILE fqName:<root> fileName:/samConversionsWithSmartCasts.kt
GET_VAR 'a: kotlin.Function1<kotlin.Int, kotlin.Int> declared in <root>.test7' type=kotlin.Function1<kotlin.Int, kotlin.Int> origin=null
CALL 'public final fun run1 (r: <root>.KRunnable): kotlin.Unit declared in <root>' type=kotlin.Unit origin=null
r: TYPE_OP type=<root>.KRunnable origin=SAM_CONVERSION typeOperand=<root>.KRunnable
GET_VAR 'a: kotlin.Function1<kotlin.Int, kotlin.Int> declared in <root>.test7' type=kotlin.Function1<kotlin.Int, kotlin.Int> origin=null
TYPE_OP type=kotlin.Function0<kotlin.Unit> origin=IMPLICIT_CAST typeOperand=kotlin.Function0<kotlin.Unit>
GET_VAR 'a: kotlin.Function1<kotlin.Int, kotlin.Int> declared in <root>.test7' type=kotlin.Function1<kotlin.Int, kotlin.Int> origin=null
FUN name:test7a visibility:public modality:FINAL <T> (a:T of <root>.test7a) returnType:kotlin.Unit
TYPE_PARAMETER name:T index:0 variance: superTypes:[kotlin.Function1<kotlin.Int, kotlin.Int>] reified:false
VALUE_PARAMETER name:a index:0 type:T of <root>.test7a
@@ -135,7 +136,8 @@ FILE fqName:<root> fileName:/samConversionsWithSmartCasts.kt
GET_VAR 'a: T of <root>.test7a declared in <root>.test7a' type=T of <root>.test7a origin=null
CALL 'public final fun run1 (r: <root>.KRunnable): kotlin.Unit declared in <root>' type=kotlin.Unit origin=null
r: TYPE_OP type=<root>.KRunnable origin=SAM_CONVERSION typeOperand=<root>.KRunnable
GET_VAR 'a: T of <root>.test7a declared in <root>.test7a' type=T of <root>.test7a origin=null
TYPE_OP type=kotlin.Function0<kotlin.Unit> origin=IMPLICIT_CAST typeOperand=kotlin.Function0<kotlin.Unit>
GET_VAR 'a: T of <root>.test7a declared in <root>.test7a' type=T of <root>.test7a origin=null
FUN name:test7b visibility:public modality:FINAL <T> (a:T of <root>.test7b) returnType:kotlin.Unit
TYPE_PARAMETER name:T index:0 variance: superTypes:[kotlin.Function1<kotlin.Int, kotlin.Unit>; kotlin.Function0<kotlin.Unit>] reified:false
VALUE_PARAMETER name:a index:0 type:T of <root>.test7b
@@ -178,3 +180,23 @@ FILE fqName:<root> fileName:/samConversionsWithSmartCasts.kt
CALL 'public final fun run1 (r: <root>.KRunnable): kotlin.Unit declared in <root>' type=kotlin.Unit origin=null
r: TYPE_OP type=<root>.KRunnable origin=SAM_CONVERSION typeOperand=<root>.KRunnable
FUNCTION_REFERENCE 'public final fun test9 (): kotlin.Unit declared in <root>' type=kotlin.reflect.KFunction0<kotlin.Unit> origin=null reflectionTarget=<same>
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=<root>.Unrelated
GET_VAR 'a: kotlin.Any declared in <root>.test10' type=kotlin.Any origin=null
then: TYPE_OP type=kotlin.Boolean origin=INSTANCEOF typeOperand=kotlin.Function0<kotlin.Unit>
TYPE_OP type=<root>.Unrelated origin=IMPLICIT_CAST typeOperand=<root>.Unrelated
GET_VAR 'a: kotlin.Any declared in <root>.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: <root>.KRunnable): kotlin.Unit declared in <root>' type=kotlin.Unit origin=null
r: TYPE_OP type=<root>.KRunnable origin=SAM_CONVERSION typeOperand=<root>.KRunnable
TYPE_OP type=kotlin.Function0<kotlin.Unit> origin=IMPLICIT_CAST typeOperand=kotlin.Function0<kotlin.Unit>
GET_VAR 'a: kotlin.Any declared in <root>.test10' type=kotlin.Any origin=null
@@ -70,12 +70,12 @@ fun test6(a: Any) {
fun test7(a: Function1<Int, Int>) {
a as Function0<Unit> /*~> Unit */
run1(r = a /*-> KRunnable */)
run1(r = a /*as Function0<Unit> */ /*-> KRunnable */)
}
fun <T : Function1<Int, Int>> test7a(a: T) {
a as Function0<Unit> /*~> Unit */
run1(r = a /*-> KRunnable */)
run1(r = a /*as Function0<Unit> */ /*-> KRunnable */)
}
fun <T> test7b(a: T) where T : Function1<Int, Unit>, T : Function0<Unit> {
@@ -97,3 +97,14 @@ fun test9() {
run1(r = ::test9 /*-> KRunnable */)
}
fun test10(a: Any) {
when {
when {
a is Unrelated -> a /*as Unrelated */ is Function0<Unit>
else -> false
} -> { // BLOCK
run1(r = a /*as Function0<Unit> */ /*-> KRunnable */)
}
}
}
@@ -179,3 +179,22 @@ FILE fqName:<root> fileName:/samConversionsWithSmartCasts.kt
CALL 'public final fun run1 (r: <root>.KRunnable): kotlin.Unit declared in <root>' type=kotlin.Unit origin=null
r: TYPE_OP type=<root>.KRunnable origin=SAM_CONVERSION typeOperand=<root>.KRunnable
FUNCTION_REFERENCE 'public final fun test9 (): kotlin.Unit declared in <root>' type=kotlin.reflect.KFunction0<kotlin.Unit> origin=null reflectionTarget=<same>
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=<root>.Unrelated
GET_VAR 'a: kotlin.Any declared in <root>.test10' type=kotlin.Any origin=null
then: TYPE_OP type=kotlin.Boolean origin=INSTANCEOF typeOperand=kotlin.Function0<kotlin.Unit>
GET_VAR 'a: kotlin.Any declared in <root>.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: <root>.KRunnable): kotlin.Unit declared in <root>' type=kotlin.Unit origin=null
r: TYPE_OP type=<root>.KRunnable origin=SAM_CONVERSION typeOperand=<root>.KRunnable
TYPE_OP type=kotlin.Function0<kotlin.Unit> origin=IMPLICIT_CAST typeOperand=kotlin.Function0<kotlin.Unit>
GET_VAR 'a: kotlin.Any declared in <root>.test10' type=kotlin.Any origin=null
@@ -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)
}
}
@@ -96,3 +96,14 @@ fun test8(a: Function0<Unit>) {
fun test9() {
run1(r = ::test9 /*-> KRunnable */)
}
fun test10(a: Any) {
when {
when {
a is Unrelated -> a is Function0<Unit>
else -> false
} -> { // BLOCK
run1(r = a /*as Function0<Unit> */ /*-> KRunnable */)
}
}
}
@@ -50,6 +50,12 @@ fun <T> test0(a: T): Unit where T : KRunnable, T : Function0<Unit>
// Public signature debug description: test1(kotlin.Function0<kotlin.Unit>){}
fun test1(a: Function0<Unit>): 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]
@@ -99,7 +99,8 @@ FILE fqName:<root> fileName:/samConversionsWithSmartCasts.kt
CALL 'public open fun run1 (r: @[FlexibleNullability] java.lang.Runnable?): kotlin.Unit declared in <root>.J' type=kotlin.Unit origin=null
$this: CONSTRUCTOR_CALL 'public constructor <init> () declared in <root>.J' type=<root>.J origin=null
r: TYPE_OP type=java.lang.Runnable origin=SAM_CONVERSION typeOperand=java.lang.Runnable
GET_VAR 'a: kotlin.Function1<kotlin.Int, kotlin.Int> declared in <root>.test7' type=kotlin.Function1<kotlin.Int, kotlin.Int> origin=null
TYPE_OP type=kotlin.Function0<kotlin.Unit> origin=IMPLICIT_CAST typeOperand=kotlin.Function0<kotlin.Unit>
GET_VAR 'a: kotlin.Function1<kotlin.Int, kotlin.Int> declared in <root>.test7' type=kotlin.Function1<kotlin.Int, kotlin.Int> origin=null
FUN name:test8 visibility:public modality:FINAL <> (a:kotlin.Function0<kotlin.Unit>) returnType:kotlin.Unit
VALUE_PARAMETER name:a index:0 type:kotlin.Function0<kotlin.Unit>
BLOCK_BODY
@@ -54,7 +54,7 @@ fun test6(a: Any) {
fun test7(a: Function1<Int, Int>) {
a as Function0<Unit> /*~> Unit */
J().run1(r = a /*-> Runnable */)
J().run1(r = a /*as Function0<Unit> */ /*-> Runnable */)
}
fun test8(a: Function0<Unit>) {