[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? {
|
internal fun ConeKotlinType.approximateForIrOrNull(): ConeKotlinType? {
|
||||||
return session.typeApproximator.approximateToSuperType(this, TypeApproximatorConfiguration.FrontendToBackendTypesApproximation)
|
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(
|
private fun castArgumentToFunctionalInterfaceForSamType(
|
||||||
argument: IrExpression,
|
argument: IrExpression,
|
||||||
argumentConeType: ConeKotlinType,
|
argumentConeType: ConeKotlinType,
|
||||||
samType: ConeKotlinType
|
samType: ConeKotlinType,
|
||||||
): IrExpression {
|
): 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
|
// 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
|
// 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.
|
// tests from FirBlackBoxCodegenTestGenerated relevant to INDY start failing once this line is removed.
|
||||||
if (argumentConeType.isSubtypeOf(coneKotlinFunctionType, session)) return argument
|
val approximateArgumentConeType = argumentConeType.approximateForIrOrSelf()
|
||||||
val irFunctionType = coneKotlinFunctionType.toIrType()
|
|
||||||
|
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)
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
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>
|
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>) {
|
fun test7(a: Function1<Int, Int>) {
|
||||||
a as Function0<Unit> /*~> Unit */
|
a as Function0<Unit> /*~> Unit */
|
||||||
run1(r = a /*-> KRunnable */)
|
run1(r = a /*as Function0<Unit> */ /*-> KRunnable */)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun <T : Function1<Int, Int>> test7a(a: T) {
|
fun <T : Function1<Int, Int>> test7a(a: T) {
|
||||||
a as Function0<Unit> /*~> Unit */
|
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> {
|
fun <T> test7b(a: T) where T : Function1<Int, Unit>, T : Function0<Unit> {
|
||||||
@@ -97,3 +97,14 @@ fun test9() {
|
|||||||
run1(r = ::test9 /*-> KRunnable */)
|
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
|
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
|
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>
|
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() {
|
fun test9() {
|
||||||
run1(::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() {
|
fun test9() {
|
||||||
run1(r = ::test9 /*-> KRunnable */)
|
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>){}
|
// Public signature debug description: test1(kotlin.Function0<kotlin.Unit>){}
|
||||||
fun test1(a: Function0<Unit>): 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:
|
// CHECK:
|
||||||
// Mangled name: #test2(KRunnable){}
|
// Mangled name: #test2(KRunnable){}
|
||||||
// Public signature: /test2|-6262171320756539088[0]
|
// 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
|
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
|
$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
|
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
|
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>
|
VALUE_PARAMETER name:a index:0 type:kotlin.Function0<kotlin.Unit>
|
||||||
BLOCK_BODY
|
BLOCK_BODY
|
||||||
|
|||||||
+1
-1
@@ -54,7 +54,7 @@ fun test6(a: Any) {
|
|||||||
|
|
||||||
fun test7(a: Function1<Int, Int>) {
|
fun test7(a: Function1<Int, Int>) {
|
||||||
a as Function0<Unit> /*~> Unit */
|
a as Function0<Unit> /*~> Unit */
|
||||||
J().run1(r = a /*-> Runnable */)
|
J().run1(r = a /*as Function0<Unit> */ /*-> Runnable */)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun test8(a: Function0<Unit>) {
|
fun test8(a: Function0<Unit>) {
|
||||||
|
|||||||
Reference in New Issue
Block a user