in compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/generators/CallAndReferenceGenerator.kt [485:702]
fun convertToIrCall(
qualifiedAccess: FirQualifiedAccessExpression,
type: ConeKotlinType,
explicitReceiverExpression: IrExpression?,
dynamicOperator: IrDynamicOperator? = null,
variableAsFunctionMode: Boolean = false,
noArguments: Boolean = false,
): IrExpression = convertCatching(qualifiedAccess, conversionScope) {
injectGetValueCall(qualifiedAccess, qualifiedAccess.calleeReference)?.let { return it }
convertSubstitutedInlineLambda(qualifiedAccess)?.let {
return it.patchDeclarationParents(conversionScope.parent())
}
val irType = type.toIrType()
val samConstructorCall = qualifiedAccess.tryConvertToSamConstructorCall(irType)
if (samConstructorCall != null) return samConstructorCall
val dispatchReceiver = qualifiedAccess.dispatchReceiver
val calleeReference = qualifiedAccess.calleeReference
if (qualifiedAccess is FirSuperReceiverExpression && dispatchReceiver != null) {
return qualifiedAccess.convertWithOffsets { startOffset, endOffset ->
visitor.convertToIrExpression(dispatchReceiver).apply {
this.startOffset = startOffset
this.endOffset = endOffset
}
}
}
val firSymbol = calleeReference.extractDeclarationSiteSymbol()
val isDynamicAccess = firSymbol?.origin == FirDeclarationOrigin.DynamicScope
if (isDynamicAccess) {
return convertToIrCallForDynamic(
qualifiedAccess,
explicitReceiverExpression,
irType,
calleeReference,
firSymbol,
dynamicOperator,
noArguments,
)
}
// We might have had a dynamic receiver, but resolved
// into a non-fake member. For example, we can
// resolve into members of `Any`.
val convertedExplicitReceiver = if (explicitReceiverExpression?.type is IrDynamicType) {
qualifiedAccess.convertWithOffsets { startOffset, endOffset ->
// we should use a dispatch receiver type from the raw resolved symbol, without unwrapping fake-overrides
// to get proper use-site dispatch receiver
val callableDeclaration = calleeReference.toResolvedCallableSymbol()?.fir
val targetType = callableDeclaration?.dispatchReceiverType?.toIrType()
?: callableDeclaration?.receiverParameter?.typeRef?.toIrType()
?: error("Couldn't get the proper receiver for call ${qualifiedAccess.render()}")
IrTypeOperatorCallImpl(
startOffset, endOffset, targetType,
IrTypeOperator.IMPLICIT_DYNAMIC_CAST,
targetType, explicitReceiverExpression,
)
}
} else {
explicitReceiverExpression
}
return qualifiedAccess.convertWithOffsets { startOffset, endOffset ->
val irSymbol = firSymbol?.toIrSymbolForCall(
dispatchReceiver,
explicitReceiver = qualifiedAccess.explicitReceiver
)
when (irSymbol) {
is IrConstructorSymbol -> {
require(firSymbol is FirConstructorSymbol)
val constructor = firSymbol.unwrapCallRepresentative().fir as FirConstructor
val totalTypeParametersCount = constructor.typeParameters.size
val constructorTypeParametersCount = constructor.typeParameters.count { it is FirTypeParameter }
if (firSymbol.isAnnotationConstructor(session)) {
IrAnnotationImplWithShape(
startOffset,
endOffset,
irType,
irSymbol,
typeArgumentsCount = totalTypeParametersCount,
valueArgumentsCount = firSymbol.valueParametersSize(),
contextParameterCount = constructor.contextParameters.size,
constructorTypeArgumentsCount = constructorTypeParametersCount,
hasDispatchReceiver = firSymbol.dispatchReceiverType != null,
hasExtensionReceiver = firSymbol.isExtension,
)
} else {
IrConstructorCallImplWithShape(
startOffset,
endOffset,
irType,
irSymbol,
typeArgumentsCount = totalTypeParametersCount,
valueArgumentsCount = firSymbol.valueParametersSize(),
contextParameterCount = constructor.contextParameters.size,
constructorTypeArgumentsCount = constructorTypeParametersCount,
hasDispatchReceiver = firSymbol.dispatchReceiverType != null,
hasExtensionReceiver = firSymbol.isExtension,
)
}
}
is IrSimpleFunctionSymbol -> {
val callOrigin = calleeReference.statementOrigin()
/*
* For `x += y` -> `x = x.plus(y)` receiver of call `plus` should also have an augmented assignment origin.
* But it's hard to detect this origin during conversion the receiver itself, so we update it afterward.
*/
if (
explicitReceiverExpression != null &&
calleeReference.source?.kind is KtFakeSourceElementKind.DesugaredAugmentedAssign &&
callOrigin != null &&
// This is to reproduce K1 behavior. K1 does not set origin for augmented assignment-originated `get` and `set`
firSymbol.name != OperatorNameConventions.GET && firSymbol.name != OperatorNameConventions.SET
) {
explicitReceiverExpression.updateStatementOrigin(callOrigin)
}
IrCallImplWithShape(
startOffset, endOffset, irType, irSymbol,
typeArgumentsCount = firSymbol.typeParameterSymbols.size,
valueArgumentsCount = firSymbol.valueParametersSize(),
contextParameterCount = firSymbol.fir.contextParameters.size,
hasDispatchReceiver = firSymbol.dispatchReceiverType != null,
hasExtensionReceiver = firSymbol.isExtension,
origin = callOrigin,
superQualifierSymbol = dispatchReceiver?.superQualifierSymbolForFunctionAndPropertyAccess()
).apply {
if (qualifiedAccess is FirImplicitInvokeCall) {
implicitInvoke = true
}
}
}
is IrLocalDelegatedPropertySymbol -> {
IrCallImpl(
startOffset, endOffset, irType,
declarationStorage.findGetterOfProperty(irSymbol),
typeArgumentsCount = calleeReference.toResolvedCallableSymbol()!!.fir.typeParameters.size,
origin = IrStatementOrigin.GET_LOCAL_PROPERTY,
superQualifierSymbol = dispatchReceiver?.superQualifierSymbolForFunctionAndPropertyAccess()
)
}
is IrPropertySymbol -> {
val property = calleeReference.toResolvedPropertySymbol()!!.fir
val getterSymbol = declarationStorage.findGetterOfProperty(irSymbol)
val backingFieldSymbol = declarationStorage.findBackingFieldOfProperty(irSymbol)
when {
getterSymbol != null -> {
// In case the receiver is an intersection type containing a value class and the property is an intersection
// override, the return type might be approximated to Any.
// Native and Wasm are sensitive regarding the expression type of value class property access,
// that's why we unwrap the intersection override and use the type of the value class property.
// See compiler/testData/codegen/box/inlineClasses/kt70461.kt
val finalIrType =
if (firSymbol.isInlineClassProperty &&
property.isIntersectionOverride &&
property.dispatchReceiverType is ConeIntersectionType
) {
property.baseForIntersectionOverride!!.returnTypeRef.toIrType()
} else {
irType
}
IrCallImplWithShape(
startOffset, endOffset,
finalIrType,
getterSymbol,
typeArgumentsCount = property.typeParameters.size,
valueArgumentsCount = property.contextParameters.size,
contextParameterCount = property.contextParameters.size,
hasDispatchReceiver = property.dispatchReceiverType != null,
hasExtensionReceiver = property.isExtension,
origin = incOrDecSourceKindToIrStatementOrigin[qualifiedAccess.source?.kind]
?: augmentedAssignSourceKindToIrStatementOrigin[qualifiedAccess.source?.kind]
?: IrStatementOrigin.GET_PROPERTY,
superQualifierSymbol = dispatchReceiver?.superQualifierSymbolForFunctionAndPropertyAccess()
)
}
backingFieldSymbol != null -> IrGetFieldImpl(
startOffset, endOffset, backingFieldSymbol, irType,
superQualifierSymbol = dispatchReceiver?.superQualifierSymbolForFieldAccess(firSymbol)
)
else -> IrErrorCallExpressionImpl(
startOffset, endOffset, irType,
description = "No getter or backing field found for ${calleeReference.render()}"
)
}
}
is IrFieldSymbol -> IrGetFieldImpl(
startOffset, endOffset, irSymbol, irType,
superQualifierSymbol = dispatchReceiver?.superQualifierSymbolForFieldAccess(firSymbol)
)
is IrValueSymbol -> {
val variable = calleeReference.toResolvedVariableSymbol()!!.fir
IrGetValueImpl(
startOffset, endOffset,
// Note: there is a case with componentN function when IR type of variable differs from FIR type
variable.irTypeForPotentiallyComponentCall(predefinedType = irType),
irSymbol,
origin = if (variableAsFunctionMode) IrStatementOrigin.VARIABLE_AS_FUNCTION
else incOrDecSourceKindToIrStatementOrigin[qualifiedAccess.source?.kind] ?: calleeReference.statementOrigin()
)
}
is IrEnumEntrySymbol -> IrGetEnumValueImpl(startOffset, endOffset, irType, irSymbol)
else -> generateErrorCallExpression(startOffset, endOffset, calleeReference, irType)
}
}.applyTypeArguments(qualifiedAccess)
.applyReceiversAndArguments(qualifiedAccess, firSymbol, convertedExplicitReceiver)
}