fun convertToIrCall()

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)
    }