FIR: initial support of suspend conversion for function reference

This commit is contained in:
Jinseong Jeon
2020-07-24 15:19:28 -07:00
committed by Mikhail Glukhikh
parent b9243aad24
commit 5a3367e09c
21 changed files with 230 additions and 68 deletions
@@ -5,6 +5,9 @@
package org.jetbrains.kotlin.fir.backend.generators
import org.jetbrains.kotlin.builtins.functions.FunctionClassDescriptor
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.fir.backend.*
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.expressions.*
@@ -18,22 +21,28 @@ import org.jetbrains.kotlin.fir.render
import org.jetbrains.kotlin.fir.resolve.calls.isFunctional
import org.jetbrains.kotlin.fir.resolve.getCorrespondingConstructorReferenceOrNull
import org.jetbrains.kotlin.fir.resolve.inference.isBuiltinFunctionalType
import org.jetbrains.kotlin.fir.resolve.inference.isSuspendFunctionType
import org.jetbrains.kotlin.fir.resolve.toSymbol
import org.jetbrains.kotlin.fir.symbols.impl.ConeClassLikeLookupTagImpl
import org.jetbrains.kotlin.fir.symbols.impl.FirClassSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirFunctionSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol
import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrDeclaration
import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.declarations.IrProperty
import org.jetbrains.kotlin.fir.types.impl.ConeClassLikeTypeImpl
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.descriptors.WrappedSimpleFunctionDescriptor
import org.jetbrains.kotlin.ir.descriptors.WrappedValueParameterDescriptor
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.impl.*
import org.jetbrains.kotlin.ir.symbols.*
import org.jetbrains.kotlin.ir.types.IrSimpleType
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.types.classifierOrNull
import org.jetbrains.kotlin.ir.types.toArrayOrPrimitiveArrayType
import org.jetbrains.kotlin.ir.util.parentClassOrNull
import org.jetbrains.kotlin.ir.types.typeOrNull
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.KtPropertyDelegate
import org.jetbrains.kotlin.psi2ir.generators.hasNoSideEffects
@@ -87,12 +96,18 @@ class CallAndReferenceGenerator(
}
is IrFunctionSymbol -> {
val function = symbol.owner
IrFunctionReferenceImpl(
startOffset, endOffset, type, symbol,
typeArgumentsCount = function.typeParameters.size,
valueArgumentsCount = function.valueParameters.size,
reflectionTarget = symbol
)
// TODO: should refer to LanguageVersionSettings.SuspendConversion
if (requiresSuspendConversion(type, function)) {
val adaptedType = callableReferenceAccess.typeRef.coneType.kFunctionTypeToFunctionType()
generateAdaptedCallableReference(callableReferenceAccess, symbol, adaptedType)
} else {
IrFunctionReferenceImpl(
startOffset, endOffset, type, symbol,
typeArgumentsCount = function.typeParameters.size,
valueArgumentsCount = function.valueParameters.size,
reflectionTarget = symbol
)
}
}
else -> {
IrErrorCallExpressionImpl(
@@ -103,6 +118,156 @@ class CallAndReferenceGenerator(
}.applyTypeArguments(callableReferenceAccess).applyReceivers(callableReferenceAccess, explicitReceiverExpression)
}
private fun requiresSuspendConversion(type: IrType, function: IrFunction): Boolean =
type.isKSuspendFunction() && !function.isSuspend
private fun ConeKotlinType.kFunctionTypeToFunctionType(): IrSimpleType {
val kind =
if (isSuspendFunctionType(session)) FunctionClassDescriptor.Kind.SuspendFunction
else FunctionClassDescriptor.Kind.Function
val functionalTypeId = ClassId(kind.packageFqName, kind.numberedClassName(typeArguments.size - 1))
val coneType = ConeClassLikeTypeImpl(ConeClassLikeLookupTagImpl(functionalTypeId), typeArguments, isNullable = false)
return coneType.toIrType() as IrSimpleType
}
private fun generateAdaptedCallableReference(
callableReferenceAccess: FirCallableReferenceAccess,
adapteeSymbol: IrFunctionSymbol,
type: IrSimpleType
): IrExpression {
val firAdaptee = callableReferenceAccess.toResolvedCallableReference()?.resolvedSymbol?.fir as? FirSimpleFunction
val adaptee = adapteeSymbol.owner
// TODO: handle bound receiver, e.g., c::foo
return callableReferenceAccess.convertWithOffsets { startOffset, endOffset ->
val irAdapterFunction = createAdapterFunctionForSuspendConversion(startOffset, endOffset, firAdaptee!!, adaptee, type)
val irCall = createAdapteeCall(callableReferenceAccess, adapteeSymbol, irAdapterFunction)
irAdapterFunction.body = irFactory.createBlockBody(startOffset, endOffset) {
if (firAdaptee.returnTypeRef.isUnit) {
statements.add(irCall)
} else {
statements.add(IrReturnImpl(startOffset, endOffset, irBuiltIns.nothingType, irAdapterFunction.symbol, irCall))
}
}
IrFunctionExpressionImpl(startOffset, endOffset, type, irAdapterFunction, IrStatementOrigin.ADAPTED_FUNCTION_REFERENCE)
}
}
private fun createAdapterFunctionForSuspendConversion(
startOffset: Int,
endOffset: Int,
firAdaptee: FirSimpleFunction,
adaptee: IrFunction,
type: IrSimpleType,
): IrSimpleFunction {
val returnType = type.arguments.last().typeOrNull!!
val parameterTypes = type.arguments.dropLast(1).map { it.typeOrNull!! }
val adapterFunctionDescriptor = WrappedSimpleFunctionDescriptor()
return symbolTable.declareSimpleFunction(adapterFunctionDescriptor) { irAdapterSymbol ->
irFactory.createFunction(
startOffset, endOffset,
IrDeclarationOrigin.ADAPTER_FOR_CALLABLE_REFERENCE,
irAdapterSymbol,
adaptee.name,
Visibilities.LOCAL,
Modality.FINAL,
returnType,
isInline = firAdaptee.isInline,
isExternal = firAdaptee.isExternal,
isTailrec = firAdaptee.isTailRec,
isSuspend = true,
isOperator = firAdaptee.isOperator,
isInfix = firAdaptee.isInfix,
isExpect = firAdaptee.isExpect,
isFakeOverride = false
).also { irAdapterFunction ->
adapterFunctionDescriptor.bind(irAdapterFunction)
irAdapterFunction.metadata = FirMetadataSource.Function(firAdaptee)
symbolTable.enterScope(irAdapterFunction)
irAdapterFunction.valueParameters += parameterTypes.mapIndexed { index, parameterType ->
createAdapterParameter(irAdapterFunction, Name.identifier("p$index"), index, parameterType)
}
symbolTable.leaveScope(irAdapterFunction)
irAdapterFunction.parent = conversionScope.parent()!!
}
}
}
private fun createAdapterParameter(
adapterFunction: IrFunction,
name: Name,
index: Int,
type: IrType
): IrValueParameter {
val startOffset = adapterFunction.startOffset
val endOffset = adapterFunction.endOffset
val descriptor = WrappedValueParameterDescriptor()
return symbolTable.declareValueParameter(
startOffset, endOffset, IrDeclarationOrigin.ADAPTER_PARAMETER_FOR_CALLABLE_REFERENCE, descriptor, type
) { irAdapterParameterSymbol ->
irFactory.createValueParameter(
startOffset, endOffset,
IrDeclarationOrigin.ADAPTER_PARAMETER_FOR_CALLABLE_REFERENCE,
irAdapterParameterSymbol,
name,
index,
type,
varargElementType = null,
isCrossinline = false,
isNoinline = false
).also { irAdapterValueParameter ->
descriptor.bind(irAdapterValueParameter)
irAdapterValueParameter.parent = adapterFunction
}
}
}
private fun createAdapteeCall(
callableReferenceAccess: FirCallableReferenceAccess,
adapteeSymbol: IrFunctionSymbol,
adapterFunction: IrFunction
): IrExpression {
val adapteeFunction = adapteeSymbol.owner
val startOffset = adapteeFunction.startOffset
val endOffset = adapteeFunction.endOffset
val type = adapteeFunction.returnType
val irCall =
if (adapteeSymbol is IrConstructorSymbol) {
IrConstructorCallImpl.fromSymbolOwner(startOffset, endOffset, type, adapteeSymbol)
} else {
IrCallImpl(
startOffset,
endOffset,
type,
adapteeSymbol,
callableReferenceAccess.typeArguments.size,
adapteeFunction.valueParameters.size,
origin = null,
superQualifierSymbol = null
)
}
// TODO: handle non-transient, bound dispatch/extension receiver
adapteeFunction.valueParameters.mapIndexed { index, valueParameter ->
when {
valueParameter.hasDefaultValue() -> {
irCall.putValueArgument(index, null)
}
valueParameter.isVararg -> {
// TODO: handle vararg and spread
irCall.putValueArgument(index, null)
}
else -> {
val irValueArgument = adapterFunction.valueParameters[index]
irCall.putValueArgument(index, IrGetValueImpl(startOffset, endOffset, irValueArgument.type, irValueArgument.symbol))
}
}
}
return irCall.applyTypeArguments(callableReferenceAccess)
}
private fun FirQualifiedAccess.tryConvertToSamConstructorCall(type: IrType): IrTypeOperatorCall? {
val calleeReference = calleeReference as? FirResolvedNamedReference ?: return null
val fir = calleeReference.resolvedSymbol.fir
@@ -220,12 +220,18 @@ internal object CheckCallableReferenceExpectedType : CheckerStage() {
}
val returnTypeRef = candidate.bodyResolveComponents.returnTypeCalculator.tryCalculateReturnType(fir)
// If the expected type is a suspend function type and the current argument of interest is a function reference, we need to do
// "suspend conversion." Here, during resolution, we bypass constraint system by making resulting type be KSuspendFunction.
// Then, during conversion, we need to create an adapter function and replace the function reference created here with an adapted
// callable reference.
// TODO: should refer to LanguageVersionSettings.SuspendConversion
val requireSuspendConversion = expectedType?.isSuspendFunctionType(callInfo.session) == true
// TODO: handle callable reference with vararg
val resultingType: ConeKotlinType = when (fir) {
is FirFunction -> callInfo.session.createKFunctionType(
fir, resultingReceiverType, returnTypeRef,
expectedParameterNumberWithReceiver = expectedType?.let { it.typeArguments.size - 1 },
isSuspend = (fir as? FirSimpleFunction)?.isSuspend == true,
isSuspend = (fir as? FirSimpleFunction)?.isSuspend == true || requireSuspendConversion,
expectedReturnType = extractInputOutputTypesFromCallableReferenceExpectedType(expectedType, callInfo.session)?.outputType
)
is FirVariable<*> -> createKPropertyType(fir, resultingReceiverType, returnTypeRef)
@@ -1,7 +1,6 @@
// !LANGUAGE: +SuspendConversion
// WITH_RUNTIME
// WITH_COROUTINES
// IGNORE_BACKEND_FIR: JVM_IR
import helpers.*
import kotlin.coroutines.*
@@ -1,7 +1,6 @@
// !LANGUAGE: +SuspendConversion
// WITH_RUNTIME
// WITH_COROUTINES
// IGNORE_BACKEND_FIR: JVM_IR
import helpers.*
import kotlin.coroutines.*
@@ -1,7 +1,6 @@
// !LANGUAGE: +SuspendConversion
// WITH_RUNTIME
// WITH_COROUTINES
// IGNORE_BACKEND_FIR: JVM_IR
import helpers.*
import kotlin.coroutines.*
@@ -1,7 +1,6 @@
// !LANGUAGE: +SuspendConversion
// WITH_RUNTIME
// WITH_COROUTINES
// IGNORE_BACKEND_FIR: JVM_IR
import helpers.*
import kotlin.coroutines.*
@@ -1,7 +1,6 @@
// !LANGUAGE: +SuspendConversion
// WITH_RUNTIME
// WITH_COROUTINES
// IGNORE_BACKEND_FIR: JVM_IR
import helpers.*
import kotlin.coroutines.*
@@ -1,7 +1,6 @@
// !LANGUAGE: +SuspendConversion
// WITH_RUNTIME
// WITH_COROUTINES
// IGNORE_BACKEND_FIR: JVM_IR
import helpers.*
import kotlin.coroutines.*
@@ -1,7 +1,6 @@
// !LANGUAGE: +SuspendConversion
// WITH_RUNTIME
// WITH_COROUTINES
// IGNORE_BACKEND_FIR: JVM_IR
import helpers.*
import kotlin.coroutines.*
@@ -1,7 +1,6 @@
// !LANGUAGE: +SuspendConversion
// WITH_RUNTIME
// WITH_COROUTINES
// IGNORE_BACKEND_FIR: JVM_IR
import helpers.*
import kotlin.coroutines.*
@@ -1,7 +1,6 @@
// !LANGUAGE: +SuspendConversion
// WITH_RUNTIME
// WITH_COROUTINES
// IGNORE_BACKEND_FIR: JVM_IR
import helpers.*
import kotlin.coroutines.*
@@ -9,10 +9,10 @@ fun bar2(x: Int) {}
fun bar2(s: String) {}
fun test() {
<!INAPPLICABLE_CANDIDATE!>foo1<!>(::bar1)
foo1(::bar1)
<!INAPPLICABLE_CANDIDATE!>foo2<!>(42, <!UNRESOLVED_REFERENCE!>::bar2<!>)
<!INAPPLICABLE_CANDIDATE!>foo2<!>("str", <!UNRESOLVED_REFERENCE!>::bar2<!>)
foo2(42, ::bar2)
foo2("str", ::bar2)
<!INAPPLICABLE_CANDIDATE!>foo2<!>(42, ::bar1)
}
@@ -22,7 +22,7 @@ object Test2 {
fun test() {
val result = foo(::bar)
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Int")!>result<!>
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.String")!>result<!>
}
}
}
@@ -11,7 +11,7 @@ object Test1 {
fun test() {
val result = foo(::bar)
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Int")!>result<!>
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.String")!>result<!>
}
}
}
@@ -29,7 +29,7 @@ object Test2 {
fun test() {
val result = foo(::bar)
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Double")!>result<!>
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.String")!>result<!>
}
}
}
@@ -20,7 +20,7 @@ fun test(
<!INAPPLICABLE_CANDIDATE!>foo2<!>(f2)
<!INAPPLICABLE_CANDIDATE!>foo3<!>(f3)
<!INAPPLICABLE_CANDIDATE!>foo1<!>(::bar)
foo1(::bar)
<!INAPPLICABLE_CANDIDATE!>foo1<!>(f2)
<!INAPPLICABLE_CANDIDATE!>foo1<!>(f3)
@@ -1,21 +0,0 @@
// !LANGUAGE: +SuspendConversion
// !DIAGNOSTICS: -UNUSED_PARAMETER
fun interface SuspendRunnable {
suspend fun invoke()
}
fun foo1(s: SuspendRunnable) {}
fun bar1() {}
fun bar2(s: String = ""): Int = 0
fun bar3() {}
suspend fun bar3(s: String = ""): Int = 0
fun test() {
<!INAPPLICABLE_CANDIDATE!>foo1<!>(::bar1)
<!INAPPLICABLE_CANDIDATE!>foo1<!>(::bar2)
foo1(::bar3) // Should be ambiguity
}
@@ -1,3 +1,4 @@
// FIR_IDENTICAL
// !LANGUAGE: +SuspendConversion
// !DIAGNOSTICS: -UNUSED_PARAMETER
@@ -14,8 +14,8 @@ fun unitCoercionAndDefaults(f: suspend () -> Unit) {}
fun all(s: String = ""): Int = 0
fun test() {
<!INAPPLICABLE_CANDIDATE!>unitCoercion<!>(::foo)
<!INAPPLICABLE_CANDIDATE!>defaults<!>(::bar)
unitCoercion(::foo)
defaults(::bar)
<!INAPPLICABLE_CANDIDATE!>varargs<!>(::baz)
<!INAPPLICABLE_CANDIDATE!>unitCoercionAndDefaults<!>(::all)
unitCoercionAndDefaults(::all)
}
@@ -14,5 +14,5 @@ abstract class SubInt : () -> Int
fun test(f: () -> String, s: SubInt) {
<!INAPPLICABLE_CANDIDATE!>foo<!>(f)
<!INAPPLICABLE_CANDIDATE!>foo<!>(s)
<!INAPPLICABLE_CANDIDATE!>foo<!>(::bar)
foo(::bar)
}
@@ -8,7 +8,7 @@ fun bar(): String = ""
abstract class SubInt : () -> Int
fun test(g: () -> Double, s: SubInt) {
<!INAPPLICABLE_CANDIDATE!>foo<!>(::bar)
foo(::bar)
<!INAPPLICABLE_CANDIDATE!>foo<!>(g)
<!INAPPLICABLE_CANDIDATE!>foo<!>(s)
}
@@ -59,30 +59,50 @@ FILE fqName:<root> fileName:/suspendConversion.kt
fn: FUNCTION_REFERENCE 'public final fun foo0 (): kotlin.Unit [suspend] declared in <root>' type=kotlin.reflect.KSuspendFunction0<kotlin.Unit> origin=null reflectionTarget=<same>
FUN name:testSuspendPlain visibility:public modality:FINAL <> () returnType:kotlin.Unit
BLOCK_BODY
ERROR_CALL 'Unresolved reference: <Inapplicable(INAPPLICABLE): [/useSuspend]>#' type=IrErrorType
FUNCTION_REFERENCE 'public final fun foo1 (): kotlin.Unit declared in <root>' type=kotlin.reflect.KFunction0<kotlin.Unit> origin=null reflectionTarget=<same>
CALL 'public final fun useSuspend (fn: kotlin.coroutines.SuspendFunction0<kotlin.Unit>): kotlin.Unit declared in <root>' type=kotlin.Unit origin=null
fn: FUN_EXPR type=kotlin.coroutines.SuspendFunction0<kotlin.Unit> origin=ADAPTED_FUNCTION_REFERENCE
FUN ADAPTER_FOR_CALLABLE_REFERENCE name:foo1 visibility:local modality:FINAL <> () returnType:kotlin.Unit [suspend]
BLOCK_BODY
CALL 'public final fun foo1 (): kotlin.Unit declared in <root>' type=kotlin.Unit origin=null
FUN name:testSuspendWithArgs visibility:public modality:FINAL <> () returnType:kotlin.Unit
BLOCK_BODY
ERROR_CALL 'Unresolved reference: <Inapplicable(INAPPLICABLE): [/useSuspendInt]>#' type=IrErrorType
FUNCTION_REFERENCE 'public final fun fooInt (x: kotlin.Int): kotlin.Unit declared in <root>' type=kotlin.reflect.KFunction1<kotlin.Int, kotlin.Unit> origin=null reflectionTarget=<same>
CALL 'public final fun useSuspendInt (fn: kotlin.coroutines.SuspendFunction1<kotlin.Int, kotlin.Unit>): kotlin.Unit declared in <root>' type=kotlin.Unit origin=null
fn: FUN_EXPR type=kotlin.coroutines.SuspendFunction1<kotlin.Int, kotlin.Unit> origin=ADAPTED_FUNCTION_REFERENCE
FUN ADAPTER_FOR_CALLABLE_REFERENCE name:fooInt visibility:local modality:FINAL <> (p0:kotlin.Int) returnType:kotlin.Unit [suspend]
VALUE_PARAMETER ADAPTER_PARAMETER_FOR_CALLABLE_REFERENCE name:p0 index:0 type:kotlin.Int
BLOCK_BODY
CALL 'public final fun fooInt (x: kotlin.Int): kotlin.Unit declared in <root>' type=kotlin.Unit origin=null
x: GET_VAR 'p0: kotlin.Int declared in <root>.testSuspendWithArgs.fooInt' type=kotlin.Int origin=null
FUN name:testWithVararg visibility:public modality:FINAL <> () returnType:kotlin.Unit
BLOCK_BODY
ERROR_CALL 'Unresolved reference: <Inapplicable(INAPPLICABLE): [/useSuspend]>#' type=IrErrorType
FUNCTION_REFERENCE 'public final fun foo2 (vararg xs: kotlin.Int): kotlin.Unit declared in <root>' type=kotlin.reflect.KFunction1<kotlin.IntArray, kotlin.Unit> origin=null reflectionTarget=<same>
CALL 'public final fun useSuspend (fn: kotlin.coroutines.SuspendFunction0<kotlin.Unit>): kotlin.Unit declared in <root>' type=kotlin.Unit origin=null
fn: FUN_EXPR type=kotlin.coroutines.SuspendFunction0<kotlin.Unit> origin=ADAPTED_FUNCTION_REFERENCE
FUN ADAPTER_FOR_CALLABLE_REFERENCE name:foo2 visibility:local modality:FINAL <> () returnType:kotlin.Unit [suspend]
BLOCK_BODY
CALL 'public final fun foo2 (vararg xs: kotlin.Int): kotlin.Unit declared in <root>' type=kotlin.Unit origin=null
FUN name:testWithVarargMapped visibility:public modality:FINAL <> () returnType:kotlin.Unit
BLOCK_BODY
ERROR_CALL 'Unresolved reference: <Inapplicable(INAPPLICABLE): [/useSuspendInt]>#' type=IrErrorType
FUNCTION_REFERENCE 'public final fun foo2 (vararg xs: kotlin.Int): kotlin.Unit declared in <root>' type=kotlin.reflect.KFunction1<kotlin.IntArray, kotlin.Unit> origin=null reflectionTarget=<same>
FUN name:testWithCoercionToUnit visibility:public modality:FINAL <> () returnType:kotlin.Unit
BLOCK_BODY
ERROR_CALL 'Unresolved reference: <Inapplicable(INAPPLICABLE): [/useSuspend]>#' type=IrErrorType
FUNCTION_REFERENCE 'public final fun foo3 (): kotlin.Int declared in <root>' type=kotlin.reflect.KFunction0<kotlin.Int> origin=null reflectionTarget=<same>
CALL 'public final fun useSuspend (fn: kotlin.coroutines.SuspendFunction0<kotlin.Unit>): kotlin.Unit declared in <root>' type=kotlin.Unit origin=null
fn: FUN_EXPR type=kotlin.coroutines.SuspendFunction0<kotlin.Unit> origin=ADAPTED_FUNCTION_REFERENCE
FUN ADAPTER_FOR_CALLABLE_REFERENCE name:foo3 visibility:local modality:FINAL <> () returnType:kotlin.Unit [suspend]
BLOCK_BODY
RETURN type=kotlin.Nothing from='local final fun foo3 (): kotlin.Unit [suspend] declared in <root>.testWithCoercionToUnit'
CALL 'public final fun foo3 (): kotlin.Int declared in <root>' type=kotlin.Int origin=null
FUN name:testWithDefaults visibility:public modality:FINAL <> () returnType:kotlin.Unit
BLOCK_BODY
ERROR_CALL 'Unresolved reference: <Inapplicable(INAPPLICABLE): [/useSuspend]>#' type=IrErrorType
FUNCTION_REFERENCE 'public final fun foo4 (i: kotlin.Int): kotlin.Unit declared in <root>' type=kotlin.reflect.KFunction1<kotlin.Int, kotlin.Unit> origin=null reflectionTarget=<same>
CALL 'public final fun useSuspend (fn: kotlin.coroutines.SuspendFunction0<kotlin.Unit>): kotlin.Unit declared in <root>' type=kotlin.Unit origin=null
fn: FUN_EXPR type=kotlin.coroutines.SuspendFunction0<kotlin.Unit> origin=ADAPTED_FUNCTION_REFERENCE
FUN ADAPTER_FOR_CALLABLE_REFERENCE name:foo4 visibility:local modality:FINAL <> () returnType:kotlin.Unit [suspend]
BLOCK_BODY
CALL 'public final fun foo4 (i: kotlin.Int): kotlin.Unit declared in <root>' type=kotlin.Unit origin=null
FUN name:testWithBoundReceiver visibility:public modality:FINAL <> () returnType:kotlin.Unit
BLOCK_BODY
ERROR_CALL 'Unresolved reference: <Inapplicable(INAPPLICABLE): [/useSuspend]>#' type=IrErrorType
FUNCTION_REFERENCE 'public final fun bar (): kotlin.Unit declared in <root>.C' type=kotlin.reflect.KFunction0<kotlin.Unit> origin=null reflectionTarget=<same>
$this: CONSTRUCTOR_CALL 'public constructor <init> () [primary] declared in <root>.C' type=<root>.C origin=null
CALL 'public final fun useSuspend (fn: kotlin.coroutines.SuspendFunction0<kotlin.Unit>): kotlin.Unit declared in <root>' type=kotlin.Unit origin=null
fn: FUN_EXPR type=kotlin.coroutines.SuspendFunction0<kotlin.Unit> origin=ADAPTED_FUNCTION_REFERENCE
FUN ADAPTER_FOR_CALLABLE_REFERENCE name:bar visibility:local modality:FINAL <> () returnType:kotlin.Unit [suspend]
BLOCK_BODY
CALL 'public final fun bar (): kotlin.Unit declared in <root>.C' type=kotlin.Unit origin=null