[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:
committed by
Space Team
parent
dd1d36816b
commit
b857c28ab3
@@ -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
|
||||
}
|
||||
|
||||
+16
-4
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
Vendored
+24
-2
@@ -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
|
||||
|
||||
Vendored
+13
-2
@@ -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 */)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+19
@@ -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
|
||||
|
||||
+8
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
+11
@@ -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 */)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Vendored
+6
@@ -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]
|
||||
|
||||
+2
-1
@@ -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
|
||||
|
||||
+1
-1
@@ -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>) {
|
||||
|
||||
Reference in New Issue
Block a user