FIR2IR: properly support combinations of SAM/suspend conversions

This commit is contained in:
Mikhail Glukhikh
2021-12-07 16:49:25 +03:00
committed by TeamCityServer
parent e574d3be27
commit 9b42fab9de
9 changed files with 42 additions and 24 deletions
@@ -414,12 +414,10 @@ internal class AdapterGenerator(
if (!needSamConversion(argument, parameter)) { if (!needSamConversion(argument, parameter)) {
return this return this
} }
val samFirType = parameter.returnTypeRef.coneTypeSafe<ConeKotlinType>()?.let { val parameterType = parameter.returnTypeRef.coneType
var substituted = substitutor.substituteOrSelf(it) val substitutedParameterType = starProjectionApproximator.substituteOrSelf(substitutor.substituteOrSelf(parameterType))
substituted = starProjectionApproximator.substituteOrSelf(substituted) val samFirType = if (substitutedParameterType is ConeRawType) substitutedParameterType.lowerBound else substitutedParameterType
if (substituted is ConeRawType) substituted.lowerBound else substituted var samType = samFirType.toIrType(ConversionTypeContext.WITH_INVARIANT)
}
var samType = samFirType?.toIrType(ConversionTypeContext.WITH_INVARIANT) ?: createErrorType()
if (shouldUnwrapVarargType) { if (shouldUnwrapVarargType) {
samType = samType.getArrayElementType(irBuiltIns) samType = samType.getArrayElementType(irBuiltIns)
} }
@@ -448,10 +446,14 @@ internal class AdapterGenerator(
return false return false
} }
// On the other hand, the actual type should be either a functional type or a subtype of a class that has a contributed `invoke`. // On the other hand, the actual type should be either a functional type or a subtype of a class that has a contributed `invoke`.
val expectedFunctionType = samResolver.getFunctionTypeForPossibleSamType(parameter.returnTypeRef.coneType) val expectedFunctionType = getFunctionTypeForPossibleSamType(parameter.returnTypeRef.coneType)
return argument.isFunctional(session, scopeSession, expectedFunctionType) return argument.isFunctional(session, scopeSession, expectedFunctionType)
} }
internal fun getFunctionTypeForPossibleSamType(parameterType: ConeKotlinType): ConeKotlinType? {
return samResolver.getFunctionTypeForPossibleSamType(parameterType)
}
/** /**
* For example, * For example,
* fun consumer(f: suspend () -> Unit) = ... * fun consumer(f: suspend () -> Unit) = ...
@@ -468,21 +470,22 @@ internal class AdapterGenerator(
*/ */
internal fun IrExpression.applySuspendConversionIfNeeded( internal fun IrExpression.applySuspendConversionIfNeeded(
argument: FirExpression, argument: FirExpression,
parameter: FirValueParameter? parameterType: ConeKotlinType,
samFunctionType: ConeKotlinType?
): IrExpression { ): IrExpression {
// TODO: should refer to LanguageVersionSettings.SuspendConversion // TODO: should refer to LanguageVersionSettings.SuspendConversion
if (this is IrBlock && origin == IrStatementOrigin.ADAPTED_FUNCTION_REFERENCE) { if (this is IrBlock && origin == IrStatementOrigin.ADAPTED_FUNCTION_REFERENCE) {
return this return this
} }
val expectedType = parameter?.returnTypeRef?.coneType ?: return this
// Expect the expected type to be a suspend functional type. // Expect the expected type to be a suspend functional type.
if (!expectedType.isSuspendFunctionType(session)) { val potentialFunctionType = samFunctionType ?: parameterType
if (!potentialFunctionType.isSuspendFunctionType(session)) {
return this return this
} }
val expectedFunctionalType = expectedType.suspendFunctionTypeToFunctionType(session) val expectedFunctionalType = potentialFunctionType.suspendFunctionTypeToFunctionType(session)
val invokeSymbol = findInvokeSymbol(expectedFunctionalType, argument) ?: return this val invokeSymbol = findInvokeSymbol(expectedFunctionalType, argument) ?: return this
val suspendConvertedType = expectedType.toIrType() as IrSimpleType val suspendConvertedType = potentialFunctionType.toIrType() as IrSimpleType
return argument.convertWithOffsets { startOffset, endOffset -> return argument.convertWithOffsets { startOffset, endOffset ->
val irAdapterFunction = createAdapterFunctionForArgument(startOffset, endOffset, suspendConvertedType, type, invokeSymbol) val irAdapterFunction = createAdapterFunctionForArgument(startOffset, endOffset, suspendConvertedType, type, invokeSymbol)
// TODO add a bound receiver property to IrFunctionExpressionImpl? // TODO add a bound receiver property to IrFunctionExpressionImpl?
@@ -657,7 +657,9 @@ class CallAndReferenceGenerator(
with(adapterGenerator) { with(adapterGenerator) {
if (parameter?.returnTypeRef is FirResolvedTypeRef) { if (parameter?.returnTypeRef is FirResolvedTypeRef) {
// Java type case (from annotations) // Java type case (from annotations)
irArgument = irArgument.applySuspendConversionIfNeeded(argument, parameter) val parameterType = parameter.returnTypeRef.coneType
val samFunctionType = getFunctionTypeForPossibleSamType(parameterType)
irArgument = irArgument.applySuspendConversionIfNeeded(argument, parameterType, samFunctionType)
irArgument = irArgument.applySamConversionIfNeeded(argument, parameter, substitutor) irArgument = irArgument.applySamConversionIfNeeded(argument, parameter, substitutor)
} }
} }
@@ -1,7 +1,5 @@
// !LANGUAGE: +SuspendConversion // !LANGUAGE: +SuspendConversion
// IGNORE_BACKEND: JVM // IGNORE_BACKEND: JVM
// IGNORE_BACKEND_FIR: JVM_IR
// FIR status: ChainedFunSuspendConversionForSimpleExpressionKt$box$1 cannot be cast to kotlin.jvm.functions.Function1
fun interface SuspendRunnable { fun interface SuspendRunnable {
suspend fun invoke() suspend fun invoke()
@@ -1,4 +1,3 @@
// FIR_IDENTICAL
// !LANGUAGE: +SuspendConversion // !LANGUAGE: +SuspendConversion
// !DIAGNOSTICS: -UNUSED_PARAMETER // !DIAGNOSTICS: -UNUSED_PARAMETER
// IGNORE_BACKEND: JVM // IGNORE_BACKEND: JVM
@@ -1,8 +1,6 @@
// !LANGUAGE: +SuspendConversion // !LANGUAGE: +SuspendConversion
// !DIAGNOSTICS: -UNUSED_PARAMETER -UNUSED_EXPRESSION // !DIAGNOSTICS: -UNUSED_PARAMETER -UNUSED_EXPRESSION
// IGNORE_BACKEND: JVM // IGNORE_BACKEND: JVM
// IGNORE_BACKEND_FIR: JVM_IR
// FIR status: SuspendAndFunConversionInDisabledModeKt$box$1 cannot be cast to kotlin.jvm.functions.Function1
fun interface Runnable { fun interface Runnable {
fun run() fun run()
@@ -1,4 +1,3 @@
// FIR_IDENTICAL
// !LANGUAGE: +SuspendConversion // !LANGUAGE: +SuspendConversion
// !DIAGNOSTICS: -UNUSED_PARAMETER // !DIAGNOSTICS: -UNUSED_PARAMETER
// IGNORE_BACKEND: JVM // IGNORE_BACKEND: JVM
@@ -1,4 +1,3 @@
// FIR_IDENTICAL
// !LANGUAGE: +SuspendConversion // !LANGUAGE: +SuspendConversion
// !DIAGNOSTICS: -UNUSED_PARAMETER // !DIAGNOSTICS: -UNUSED_PARAMETER
// IGNORE_BACKEND: JVM // IGNORE_BACKEND: JVM
@@ -1,4 +1,3 @@
// FIR_IDENTICAL
// !LANGUAGE: +SuspendConversion // !LANGUAGE: +SuspendConversion
// !DIAGNOSTICS: -UNUSED_PARAMETER // !DIAGNOSTICS: -UNUSED_PARAMETER
@@ -32,12 +32,33 @@ FILE fqName:<root> fileName:/chainedFunSuspendConversionForSimpleExpression.kt
BLOCK_BODY BLOCK_BODY
CALL 'public final fun foo (s: <root>.SuspendRunnable): kotlin.Unit declared in <root>' type=kotlin.Unit origin=null CALL 'public final fun foo (s: <root>.SuspendRunnable): kotlin.Unit declared in <root>' type=kotlin.Unit origin=null
s: TYPE_OP type=<root>.SuspendRunnable origin=SAM_CONVERSION typeOperand=<root>.SuspendRunnable s: TYPE_OP type=<root>.SuspendRunnable origin=SAM_CONVERSION typeOperand=<root>.SuspendRunnable
GET_VAR 'f: kotlin.Function0<kotlin.Unit> declared in <root>.test' type=kotlin.Function0<kotlin.Unit> origin=null BLOCK type=kotlin.coroutines.SuspendFunction0<kotlin.Unit> origin=SUSPEND_CONVERSION
FUN ADAPTER_FOR_SUSPEND_CONVERSION name:suspendConversion visibility:local modality:FINAL <> ($receiver:kotlin.Function0<kotlin.Unit>) returnType:kotlin.Unit [suspend]
$receiver: VALUE_PARAMETER ADAPTER_PARAMETER_FOR_SUSPEND_CONVERSION name:callee type:kotlin.Function0<kotlin.Unit>
BLOCK_BODY
CALL 'public abstract fun invoke (): R of kotlin.Function0 [operator] declared in kotlin.Function0' type=kotlin.Unit origin=null
$this: GET_VAR 'callee: kotlin.Function0<kotlin.Unit> declared in <root>.test.suspendConversion' type=kotlin.Function0<kotlin.Unit> origin=null
FUNCTION_REFERENCE 'local final fun suspendConversion (): kotlin.Unit [suspend] declared in <root>.test' type=kotlin.coroutines.SuspendFunction0<kotlin.Unit> origin=SUSPEND_CONVERSION reflectionTarget=null
$receiver: GET_VAR 'f: kotlin.Function0<kotlin.Unit> declared in <root>.test' type=kotlin.Function0<kotlin.Unit> origin=null
CALL 'public final fun foo (s: <root>.SuspendRunnable): kotlin.Unit declared in <root>' type=kotlin.Unit origin=null CALL 'public final fun foo (s: <root>.SuspendRunnable): kotlin.Unit declared in <root>' type=kotlin.Unit origin=null
s: TYPE_OP type=<root>.SuspendRunnable origin=SAM_CONVERSION typeOperand=<root>.SuspendRunnable s: TYPE_OP type=<root>.SuspendRunnable origin=SAM_CONVERSION typeOperand=<root>.SuspendRunnable
CALL 'public final fun bar (): kotlin.Function0<kotlin.Unit> declared in <root>' type=kotlin.Function0<kotlin.Unit> origin=null BLOCK type=kotlin.coroutines.SuspendFunction0<kotlin.Unit> origin=SUSPEND_CONVERSION
FUN ADAPTER_FOR_SUSPEND_CONVERSION name:suspendConversion visibility:local modality:FINAL <> ($receiver:kotlin.Function0<kotlin.Unit>) returnType:kotlin.Unit [suspend]
$receiver: VALUE_PARAMETER ADAPTER_PARAMETER_FOR_SUSPEND_CONVERSION name:callee type:kotlin.Function0<kotlin.Unit>
BLOCK_BODY
CALL 'public abstract fun invoke (): R of kotlin.Function0 [operator] declared in kotlin.Function0' type=kotlin.Unit origin=null
$this: GET_VAR 'callee: kotlin.Function0<kotlin.Unit> declared in <root>.test.suspendConversion' type=kotlin.Function0<kotlin.Unit> origin=null
FUNCTION_REFERENCE 'local final fun suspendConversion (): kotlin.Unit [suspend] declared in <root>.test' type=kotlin.coroutines.SuspendFunction0<kotlin.Unit> origin=SUSPEND_CONVERSION reflectionTarget=null
$receiver: CALL 'public final fun bar (): kotlin.Function0<kotlin.Unit> declared in <root>' type=kotlin.Function0<kotlin.Unit> origin=null
VAR name:t type:kotlin.Function0<kotlin.Unit> [var] VAR name:t type:kotlin.Function0<kotlin.Unit> [var]
GET_VAR 'f: kotlin.Function0<kotlin.Unit> declared in <root>.test' type=kotlin.Function0<kotlin.Unit> origin=null GET_VAR 'f: kotlin.Function0<kotlin.Unit> declared in <root>.test' type=kotlin.Function0<kotlin.Unit> origin=null
CALL 'public final fun foo (s: <root>.SuspendRunnable): kotlin.Unit declared in <root>' type=kotlin.Unit origin=null CALL 'public final fun foo (s: <root>.SuspendRunnable): kotlin.Unit declared in <root>' type=kotlin.Unit origin=null
s: TYPE_OP type=<root>.SuspendRunnable origin=SAM_CONVERSION typeOperand=<root>.SuspendRunnable s: TYPE_OP type=<root>.SuspendRunnable origin=SAM_CONVERSION typeOperand=<root>.SuspendRunnable
GET_VAR 'var t: kotlin.Function0<kotlin.Unit> [var] declared in <root>.test' type=kotlin.Function0<kotlin.Unit> origin=null BLOCK type=kotlin.coroutines.SuspendFunction0<kotlin.Unit> origin=SUSPEND_CONVERSION
FUN ADAPTER_FOR_SUSPEND_CONVERSION name:suspendConversion visibility:local modality:FINAL <> ($receiver:kotlin.Function0<kotlin.Unit>) returnType:kotlin.Unit [suspend]
$receiver: VALUE_PARAMETER ADAPTER_PARAMETER_FOR_SUSPEND_CONVERSION name:callee type:kotlin.Function0<kotlin.Unit>
BLOCK_BODY
CALL 'public abstract fun invoke (): R of kotlin.Function0 [operator] declared in kotlin.Function0' type=kotlin.Unit origin=null
$this: GET_VAR 'callee: kotlin.Function0<kotlin.Unit> declared in <root>.test.suspendConversion' type=kotlin.Function0<kotlin.Unit> origin=null
FUNCTION_REFERENCE 'local final fun suspendConversion (): kotlin.Unit [suspend] declared in <root>.test' type=kotlin.coroutines.SuspendFunction0<kotlin.Unit> origin=SUSPEND_CONVERSION reflectionTarget=null
$receiver: GET_VAR 'var t: kotlin.Function0<kotlin.Unit> [var] declared in <root>.test' type=kotlin.Function0<kotlin.Unit> origin=null