blob: 99ef5da05e4336a728b9cd3fd316ed2fda3f4e03 [file] [log] [blame]
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.fir.backend.generators
import org.jetbrains.kotlin.KtFakeSourceElementKind
import org.jetbrains.kotlin.fir.*
import org.jetbrains.kotlin.fir.backend.*
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.declarations.utils.isCompanion
import org.jetbrains.kotlin.fir.dispatchReceiverClassOrNull
import org.jetbrains.kotlin.fir.expressions.*
import org.jetbrains.kotlin.fir.expressions.builder.buildAnnotationCall
import org.jetbrains.kotlin.fir.expressions.impl.FirNoReceiverExpression
import org.jetbrains.kotlin.fir.languageVersionSettings
import org.jetbrains.kotlin.fir.references.FirDelegateFieldReference
import org.jetbrains.kotlin.fir.references.FirReference
import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference
import org.jetbrains.kotlin.fir.references.FirSuperReference
import org.jetbrains.kotlin.fir.references.builder.buildResolvedNamedReference
import org.jetbrains.kotlin.fir.references.impl.FirReferencePlaceholderForResolvedAnnotations
import org.jetbrains.kotlin.fir.render
import org.jetbrains.kotlin.fir.resolve.calls.FirSyntheticFunctionSymbol
import org.jetbrains.kotlin.fir.resolve.fullyExpandedType
import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor
import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutorByMap
import org.jetbrains.kotlin.fir.resolve.toSymbol
import org.jetbrains.kotlin.fir.scopes.unsubstitutedScope
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
import org.jetbrains.kotlin.fir.symbols.impl.*
import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.fir.types.impl.ConeClassLikeTypeImpl
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
import org.jetbrains.kotlin.ir.builders.declarations.UNDEFINED_PARAMETER_INDEX
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.impl.*
import org.jetbrains.kotlin.ir.symbols.*
import org.jetbrains.kotlin.ir.symbols.impl.IrFieldSymbolImpl
import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.util.dump
import org.jetbrains.kotlin.ir.util.isFunctionTypeOrSubtype
import org.jetbrains.kotlin.ir.util.isInterface
import org.jetbrains.kotlin.ir.util.isMethodOfAny
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi2ir.generators.hasNoSideEffects
import org.jetbrains.kotlin.psi2ir.generators.isUnchanging
import org.jetbrains.kotlin.resolve.calls.NewCommonSuperTypeCalculator.commonSuperType
import org.jetbrains.kotlin.util.OperatorNameConventions
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
class CallAndReferenceGenerator(
private val components: Fir2IrComponents,
private val visitor: Fir2IrVisitor,
private val conversionScope: Fir2IrConversionScope
) : Fir2IrComponents by components {
private val approximator = ConeTypeApproximator(session.typeContext, session.languageVersionSettings)
private val adapterGenerator = AdapterGenerator(components, conversionScope)
private fun FirTypeRef.toIrType(): IrType = with(typeConverter) { toIrType() }
private fun ConeKotlinType.toIrType(conversionTypeContext: ConversionTypeContext = ConversionTypeContext.DEFAULT): IrType =
with(typeConverter) { toIrType(conversionTypeContext) }
fun convertToIrCallableReference(
callableReferenceAccess: FirCallableReferenceAccess,
explicitReceiverExpression: IrExpression?,
isDelegate: Boolean
): IrExpression {
val type = approximateFunctionReferenceType(callableReferenceAccess.typeRef.coneType).toIrType()
val callableSymbol = callableReferenceAccess.calleeReference.toResolvedCallableSymbol()
if (callableSymbol?.origin == FirDeclarationOrigin.SamConstructor) {
assert(explicitReceiverExpression == null) {
"Fun interface constructor reference should be unbound: ${explicitReceiverExpression?.dump()}"
}
return adapterGenerator.generateFunInterfaceConstructorReference(
callableReferenceAccess,
callableSymbol as FirSyntheticFunctionSymbol,
type
)
}
val symbol = callableReferenceAccess.calleeReference.toSymbolForCall(
callableReferenceAccess.dispatchReceiver,
conversionScope,
explicitReceiver = callableReferenceAccess.explicitReceiver,
isDelegate = isDelegate
)
// val x by y ->
// val `x$delegate` = y
// val x get() = `x$delegate`.getValue(this, ::x)
// The reference here (like the rest of the accessor) has DefaultAccessor source kind.
val isForDelegate = callableReferenceAccess.source?.kind == KtFakeSourceElementKind.DelegatedPropertyAccessor
val origin = if (isForDelegate) IrStatementOrigin.PROPERTY_REFERENCE_FOR_DELEGATE else null
return callableReferenceAccess.convertWithOffsets { startOffset, endOffset ->
when (symbol) {
is IrPropertySymbol -> {
val referencedProperty = symbol.owner
val referencedPropertyGetter = referencedProperty.getter
val referencedPropertySetterSymbol =
if (callableReferenceAccess.typeRef.coneType.isKMutableProperty(session)) referencedProperty.setter?.symbol
else null
val backingFieldSymbol = when {
referencedPropertyGetter != null -> null
else -> referencedProperty.backingField?.symbol
}
IrPropertyReferenceImpl(
startOffset, endOffset, type, symbol,
typeArgumentsCount = referencedPropertyGetter?.typeParameters?.size ?: 0,
field = backingFieldSymbol,
getter = referencedPropertyGetter?.symbol,
setter = referencedPropertySetterSymbol,
origin = origin
).applyTypeArguments(callableReferenceAccess).applyReceivers(callableReferenceAccess, explicitReceiverExpression)
}
is IrLocalDelegatedPropertySymbol -> {
IrLocalDelegatedPropertyReferenceImpl(
startOffset, endOffset, type, symbol,
delegate = symbol.owner.delegate.symbol,
getter = symbol.owner.getter.symbol,
setter = symbol.owner.setter?.symbol,
origin = origin
)
}
is IrFieldSymbol -> {
val fieldSymbol = computeFieldSymbolForCallableReference(callableReferenceAccess, symbol)
val referencedField = fieldSymbol.owner
val propertySymbol = referencedField.correspondingPropertySymbol
?: run {
// In case of [IrField] without the corresponding property, we've created it directly from [FirField].
// Since it's used as a field reference, we need a bogus property as a placeholder.
val firSymbol =
(callableReferenceAccess.calleeReference as FirResolvedNamedReference).resolvedSymbol as FirFieldSymbol
declarationStorage.getOrCreateIrPropertyByPureField(firSymbol.fir, referencedField.parent).symbol
}
IrPropertyReferenceImpl(
startOffset, endOffset, type,
propertySymbol,
typeArgumentsCount = (type as? IrSimpleType)?.arguments?.size ?: 0,
field = fieldSymbol,
getter = if (referencedField.isStatic) null else propertySymbol.owner.getter?.symbol,
setter = if (referencedField.isStatic) null else propertySymbol.owner.setter?.symbol,
origin
).applyReceivers(callableReferenceAccess, explicitReceiverExpression)
}
is IrFunctionSymbol -> {
assert(type.isFunctionTypeOrSubtype()) {
"Callable reference whose symbol refers to a function should be of functional type."
}
type as IrSimpleType
val function = symbol.owner
if (adapterGenerator.needToGenerateAdaptedCallableReference(callableReferenceAccess, type, function)) {
// Receivers are being applied inside
with(adapterGenerator) {
val adaptedType = callableReferenceAccess.typeRef.coneType.kFunctionTypeToFunctionType()
generateAdaptedCallableReference(callableReferenceAccess, explicitReceiverExpression, symbol, adaptedType)
}
} else {
val klass = function.parent as? IrClass
val typeArgumentCount = function.typeParameters.size +
if (function is IrConstructor) klass?.typeParameters?.size ?: 0 else 0
IrFunctionReferenceImpl(
startOffset, endOffset, type, symbol,
typeArgumentsCount = typeArgumentCount,
valueArgumentsCount = function.valueParameters.size,
reflectionTarget = symbol
).applyTypeArguments(callableReferenceAccess)
.applyReceivers(callableReferenceAccess, explicitReceiverExpression)
}
}
else -> {
IrErrorCallExpressionImpl(
startOffset, endOffset, type, "Unsupported callable reference: ${callableReferenceAccess.render()}"
)
}
}
}
}
private fun approximateFunctionReferenceType(kotlinType: ConeKotlinType): ConeKotlinType {
// This is a hack to support intersection types in function references on JVM.
// Function reference type KFunctionN<T1, ..., TN, R> might contain intersection types in its top-level arguments.
// Intersection types in expressions and local variable declarations usually don't bother us.
// However, in case of function references type mapping affects behavior:
// resulting function reference class will have a bridge method, which will downcast its arguments to the expected types.
// This would cause ClassCastException in case of usual type approximation,
// because '{ X1 & ... & Xm }' would be approximated to 'Nothing'.
// JVM_OLD just relies on type mapping for generic argument types in such case.
if (!kotlinType.isKFunctionType(session))
return kotlinType
if (kotlinType !is ConeSimpleKotlinType)
return kotlinType
if (kotlinType.typeArguments.none { it.type is ConeIntersectionType })
return kotlinType
val functionParameterTypes = kotlinType.typeArguments.take(kotlinType.typeArguments.size - 1)
val functionReturnType = kotlinType.typeArguments.last()
return ConeClassLikeTypeImpl(
(kotlinType as ConeClassLikeType).lookupTag,
(functionParameterTypes.map { approximateFunctionReferenceParameterType(it) } + functionReturnType).toTypedArray(),
kotlinType.isNullable,
kotlinType.attributes
)
}
private fun approximateFunctionReferenceParameterType(typeProjection: ConeTypeProjection): ConeTypeProjection {
if (typeProjection.isStarProjection) return typeProjection
val intersectionType = typeProjection as? ConeIntersectionType ?: return typeProjection
val newType = intersectionType.alternativeType
?: session.typeContext.commonSuperType(intersectionType.intersectedTypes.toList()) as? ConeKotlinType
?: return typeProjection
return newType.toTypeProjection(typeProjection.kind)
}
private fun computeFieldSymbolForCallableReference(
callableReferenceAccess: FirCallableReferenceAccess,
symbol: IrFieldSymbol
): IrFieldSymbol {
val receiverClass = when (val receiverExpression = callableReferenceAccess.explicitReceiver) {
is FirResolvedQualifier -> receiverExpression.symbol
null -> null
else -> receiverExpression.typeRef.coneType.toSymbol(session)
} as? FirClassSymbol<*>
val newParent = receiverClass?.let { classifierStorage.getIrClassSymbol(it) }?.owner ?: return symbol
return IrFieldSymbolImpl().also { newSymbol ->
val field = symbol.owner
irFactory.createField(
startOffset = -1,
endOffset = -1,
field.origin,
newSymbol,
field.name,
field.type,
field.visibility,
field.isFinal,
field.isExternal,
field.isStatic,
).also { it.parent = newParent }
}
}
private fun FirQualifiedAccess.tryConvertToSamConstructorCall(type: IrType): IrTypeOperatorCall? {
val calleeReference = calleeReference as? FirResolvedNamedReference ?: return null
val fir = calleeReference.resolvedSymbol.fir
if (this is FirFunctionCall && fir is FirSimpleFunction && fir.origin == FirDeclarationOrigin.SamConstructor) {
return convertWithOffsets { startOffset, endOffset ->
IrTypeOperatorCallImpl(
startOffset, endOffset, type, IrTypeOperator.SAM_CONVERSION, type, visitor.convertToIrExpression(argument)
)
}
}
return null
}
private fun FirExpression.superQualifierSymbol(): IrClassSymbol? {
if (this !is FirQualifiedAccess) {
return null
}
val dispatchReceiverReference = calleeReference
if (dispatchReceiverReference !is FirSuperReference) {
return null
}
val superTypeRef = dispatchReceiverReference.superTypeRef
val coneSuperType = superTypeRef.coneTypeSafe<ConeClassLikeType>() ?: return null
val firClassSymbol = coneSuperType.fullyExpandedType(session).lookupTag.toSymbol(session) as? FirClassSymbol<*>
if (firClassSymbol != null) {
return classifierStorage.getIrClassSymbol(firClassSymbol)
}
return null
}
private val Name.dynamicOperator
get() = when (this) {
OperatorNameConventions.UNARY_PLUS -> IrDynamicOperator.UNARY_PLUS
OperatorNameConventions.UNARY_MINUS -> IrDynamicOperator.UNARY_MINUS
OperatorNameConventions.NOT -> IrDynamicOperator.EXCL
OperatorNameConventions.PLUS -> IrDynamicOperator.BINARY_PLUS
OperatorNameConventions.MINUS -> IrDynamicOperator.BINARY_MINUS
OperatorNameConventions.TIMES -> IrDynamicOperator.MUL
OperatorNameConventions.DIV -> IrDynamicOperator.DIV
OperatorNameConventions.REM -> IrDynamicOperator.MOD
OperatorNameConventions.AND -> IrDynamicOperator.ANDAND
OperatorNameConventions.OR -> IrDynamicOperator.OROR
OperatorNameConventions.EQUALS -> IrDynamicOperator.EQEQ
OperatorNameConventions.PLUS_ASSIGN -> IrDynamicOperator.PLUSEQ
OperatorNameConventions.MINUS_ASSIGN -> IrDynamicOperator.MINUSEQ
OperatorNameConventions.TIMES_ASSIGN -> IrDynamicOperator.MULEQ
OperatorNameConventions.DIV_ASSIGN -> IrDynamicOperator.DIVEQ
OperatorNameConventions.REM_ASSIGN -> IrDynamicOperator.MODEQ
else -> null
}
private val FirQualifiedAccess.dynamicOperator
get() = when (calleeReference.source?.kind) {
is KtFakeSourceElementKind.ArrayAccessNameReference -> when (calleeReference.resolved?.name) {
OperatorNameConventions.SET -> IrDynamicOperator.EQ
OperatorNameConventions.GET -> IrDynamicOperator.ARRAY_ACCESS
else -> error("Unexpected name")
}
is KtFakeSourceElementKind.DesugaredPrefixNameReference -> when (calleeReference.resolved?.name) {
OperatorNameConventions.INC -> IrDynamicOperator.PREFIX_INCREMENT
OperatorNameConventions.DEC -> IrDynamicOperator.PREFIX_DECREMENT
else -> error("Unexpected name")
}
is KtFakeSourceElementKind.DesugaredPostfixNameReference -> when (calleeReference.resolved?.name) {
OperatorNameConventions.INC -> IrDynamicOperator.POSTFIX_INCREMENT
OperatorNameConventions.DEC -> IrDynamicOperator.POSTFIX_DECREMENT
else -> error("Unexpected name")
}
else -> null
}
private fun convertToIrCallForDynamic(
qualifiedAccess: FirQualifiedAccess,
explicitReceiverExpression: IrExpression,
type: IrType,
calleeReference: FirReference,
symbol: FirBasedSymbol<*>,
annotationMode: Boolean = false,
dynamicOperator: IrDynamicOperator? = null,
noArguments: Boolean = false,
): IrExpression {
var convertedExplicitReceiver = explicitReceiverExpression
return qualifiedAccess.convertWithOffsets { startOffset, endOffset ->
when (symbol) {
is FirFunctionSymbol<*> -> {
val name = calleeReference.resolved?.name
?: error("Must have a name")
val operator = dynamicOperator
?: name.dynamicOperator
?: qualifiedAccess.dynamicOperator
?: IrDynamicOperator.INVOKE
val theType = if (name == OperatorNameConventions.COMPARE_TO) {
typeConverter.irBuiltIns.booleanType
} else {
type
}
if (operator == IrDynamicOperator.INVOKE && qualifiedAccess !is FirImplicitInvokeCall) {
convertedExplicitReceiver = IrDynamicMemberExpressionImpl(
startOffset, endOffset, type, name.identifier, explicitReceiverExpression
)
}
IrDynamicOperatorExpressionImpl(startOffset, endOffset, theType, operator)
}
is FirPropertySymbol -> {
val name = calleeReference.resolved?.name ?: error("There must be a name")
IrDynamicMemberExpressionImpl(startOffset, endOffset, type, name.identifier, explicitReceiverExpression)
}
else -> generateErrorCallExpression(startOffset, endOffset, calleeReference, type)
}
}.applyTypeArguments(qualifiedAccess).applyReceivers(qualifiedAccess, convertedExplicitReceiver)
.applyCallArguments((qualifiedAccess as? FirCall)?.takeIf { !noArguments }, annotationMode)
}
fun convertToIrCall(
qualifiedAccess: FirQualifiedAccess,
typeRef: FirTypeRef,
explicitReceiverExpression: IrExpression?,
annotationMode: Boolean = false,
dynamicOperator: IrDynamicOperator? = null,
variableAsFunctionMode: Boolean = false,
noArguments: Boolean = false
): IrExpression {
try {
val type = typeRef.toIrType()
val samConstructorCall = qualifiedAccess.tryConvertToSamConstructorCall(type)
if (samConstructorCall != null) return samConstructorCall
val dispatchReceiver = qualifiedAccess.dispatchReceiver
val calleeReference = qualifiedAccess.calleeReference
val firSymbol = calleeReference.resolvedSymbol
val isDynamicAccess = firSymbol?.origin == FirDeclarationOrigin.DynamicScope
if (isDynamicAccess) {
return convertToIrCallForDynamic(
qualifiedAccess,
explicitReceiverExpression ?: error("Must've had a receiver"),
type,
calleeReference,
firSymbol ?: error("Must have had a symbol"),
annotationMode,
dynamicOperator,
noArguments,
)
}
val symbol = calleeReference.toSymbolForCall(
dispatchReceiver,
conversionScope
)
// 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 ->
val targetType = (firSymbol?.fir as? FirCallableDeclaration)?.dispatchReceiverType?.toIrType()
?: error("Couldn't get the proper receiver")
IrTypeOperatorCallImpl(
startOffset, endOffset, targetType,
IrTypeOperator.IMPLICIT_DYNAMIC_CAST,
targetType, explicitReceiverExpression,
)
}
} else {
explicitReceiverExpression
}
return qualifiedAccess.convertWithOffsets { startOffset, endOffset ->
if (calleeReference is FirSuperReference) {
if (dispatchReceiver !is FirNoReceiverExpression) {
return@convertWithOffsets visitor.convertToIrExpression(dispatchReceiver)
}
}
when (symbol) {
is IrConstructorSymbol -> IrConstructorCallImpl.fromSymbolOwner(startOffset, endOffset, type, symbol)
is IrSimpleFunctionSymbol -> {
IrCallImpl(
startOffset, endOffset, type, symbol,
typeArgumentsCount = symbol.owner.typeParameters.size,
valueArgumentsCount = symbol.owner.valueParameters.size,
origin = calleeReference.statementOrigin(),
superQualifierSymbol = dispatchReceiver.superQualifierSymbol()
)
}
is IrLocalDelegatedPropertySymbol -> {
IrCallImpl(
startOffset, endOffset, type, symbol.owner.getter.symbol,
typeArgumentsCount = symbol.owner.getter.typeParameters.size,
valueArgumentsCount = 0,
origin = IrStatementOrigin.GET_LOCAL_PROPERTY,
superQualifierSymbol = dispatchReceiver.superQualifierSymbol()
)
}
is IrPropertySymbol -> {
val getter = symbol.owner.getter
val backingField = symbol.owner.backingField
when {
getter != null -> IrCallImpl(
startOffset, endOffset, type, getter.symbol,
typeArgumentsCount = getter.typeParameters.size,
valueArgumentsCount = getter.valueParameters.size,
origin = IrStatementOrigin.GET_PROPERTY,
superQualifierSymbol = dispatchReceiver.superQualifierSymbol()
)
backingField != null -> IrGetFieldImpl(
startOffset, endOffset, backingField.symbol, type,
superQualifierSymbol = dispatchReceiver.superQualifierSymbol()
)
else -> IrErrorCallExpressionImpl(
startOffset, endOffset, type,
description = "No getter or backing field found for ${calleeReference.render()}"
)
}
}
is IrFieldSymbol -> if (annotationMode) {
val resolvedSymbol = calleeReference.resolvedSymbol ?: error("should have resolvedSymbol")
val returnType = (resolvedSymbol as FirCallableSymbol<*>).resolvedReturnTypeRef.toIrType()
val firConstExpression = (resolvedSymbol.fir as FirVariable).initializer as? FirConstExpression<*>
?: error("should be FirConstExpression")
firConstExpression.toIrConst(returnType)
} else {
IrGetFieldImpl(
startOffset, endOffset, symbol, type,
origin = IrStatementOrigin.GET_PROPERTY.takeIf { calleeReference !is FirDelegateFieldReference },
superQualifierSymbol = dispatchReceiver.superQualifierSymbol()
)
}
is IrValueSymbol -> {
IrGetValueImpl(
startOffset, endOffset, type, symbol,
origin = if (variableAsFunctionMode) IrStatementOrigin.VARIABLE_AS_FUNCTION
else calleeReference.statementOrigin()
)
}
is IrEnumEntrySymbol -> IrGetEnumValueImpl(startOffset, endOffset, type, symbol)
else -> generateErrorCallExpression(startOffset, endOffset, calleeReference, type)
}
}.applyTypeArguments(qualifiedAccess).applyReceivers(qualifiedAccess, convertedExplicitReceiver)
.applyCallArguments(qualifiedAccess, annotationMode)
} catch (e: Throwable) {
throw IllegalStateException(
"Error while translating ${qualifiedAccess.render()} " +
"from file ${conversionScope.containingFileIfAny()?.name ?: "???"} to BE IR", e
)
}
}
private fun convertToIrSetCallForDynamic(
variableAssignment: FirVariableAssignment,
explicitReceiverExpression: IrExpression,
type: IrType,
calleeReference: FirReference,
symbol: FirBasedSymbol<*>,
assignedValue: IrExpression,
): IrExpression {
var convertedExplicitReceiver = explicitReceiverExpression
return variableAssignment.convertWithOffsets { startOffset, endOffset ->
when (symbol) {
is FirPropertySymbol -> {
val name = calleeReference.resolved?.name ?: error("There must be a name")
convertedExplicitReceiver = IrDynamicMemberExpressionImpl(
startOffset, endOffset, type, name.identifier, explicitReceiverExpression
)
IrDynamicOperatorExpressionImpl(startOffset, endOffset, type, IrDynamicOperator.EQ).apply {
arguments.add(assignedValue)
}
}
else -> generateErrorCallExpression(startOffset, endOffset, calleeReference)
}
}.applyTypeArguments(variableAssignment).applyReceivers(variableAssignment, convertedExplicitReceiver)
}
fun convertToIrSetCall(variableAssignment: FirVariableAssignment, explicitReceiverExpression: IrExpression?): IrExpression {
try {
val type = irBuiltIns.unitType
val calleeReference = variableAssignment.calleeReference
val assignedValue = visitor.convertToIrExpression(variableAssignment.rValue)
val firSymbol = calleeReference.resolvedSymbol
val isDynamicAccess = firSymbol?.origin == FirDeclarationOrigin.DynamicScope
if (isDynamicAccess) {
return convertToIrSetCallForDynamic(
variableAssignment,
explicitReceiverExpression ?: error("Must've had a receiver"),
type,
calleeReference,
firSymbol ?: error("Must've had a symbol"),
assignedValue,
)
}
val symbol = calleeReference.toSymbolForCall(
variableAssignment.dispatchReceiver, conversionScope, preferGetter = false
)
val origin = variableAssignment.getIrAssignmentOrigin()
return variableAssignment.convertWithOffsets { startOffset, endOffset ->
when (symbol) {
is IrFieldSymbol -> IrSetFieldImpl(startOffset, endOffset, symbol, type, origin).apply {
value = assignedValue
}
is IrLocalDelegatedPropertySymbol -> {
val setter = symbol.owner.setter
when {
setter != null -> IrCallImpl(
startOffset, endOffset, type, setter.symbol,
typeArgumentsCount = setter.typeParameters.size,
valueArgumentsCount = setter.valueParameters.size,
origin = origin,
superQualifierSymbol = variableAssignment.dispatchReceiver.superQualifierSymbol()
).apply {
putContextReceiverArguments(variableAssignment)
putValueArgument(0, assignedValue)
}
else -> generateErrorCallExpression(startOffset, endOffset, calleeReference)
}
}
is IrPropertySymbol -> {
val irProperty = symbol.owner
val setter = irProperty.setter
val backingField = irProperty.backingField
when {
setter != null -> IrCallImpl(
startOffset, endOffset, type, setter.symbol,
typeArgumentsCount = setter.typeParameters.size,
valueArgumentsCount = setter.valueParameters.size,
origin = origin,
superQualifierSymbol = variableAssignment.dispatchReceiver.superQualifierSymbol()
).apply {
putValueArgument(putContextReceiverArguments(variableAssignment), assignedValue)
}
backingField != null -> IrSetFieldImpl(
startOffset, endOffset, backingField.symbol, type,
origin = null, // NB: to be consistent with PSI2IR, origin should be null here
superQualifierSymbol = variableAssignment.dispatchReceiver.superQualifierSymbol()
).apply {
value = assignedValue
}
else -> generateErrorCallExpression(startOffset, endOffset, calleeReference)
}
}
is IrSimpleFunctionSymbol -> {
IrCallImpl(
startOffset, endOffset, type, symbol,
typeArgumentsCount = symbol.owner.typeParameters.size,
valueArgumentsCount = 1,
origin = origin
).apply {
putValueArgument(0, assignedValue)
}
}
is IrVariableSymbol -> {
IrSetValueImpl(startOffset, endOffset, type, symbol, assignedValue, origin)
}
else -> generateErrorCallExpression(startOffset, endOffset, calleeReference)
}
}.applyTypeArguments(variableAssignment).applyReceivers(variableAssignment, explicitReceiverExpression)
} catch (e: Throwable) {
throw IllegalStateException(
"Error while translating ${variableAssignment.render()} " +
"from file ${conversionScope.containingFileIfAny()?.name ?: "???"} to BE IR", e
)
}
}
fun convertToIrConstructorCall(annotation: FirAnnotation): IrExpression {
val coneType = annotation.annotationTypeRef.coneTypeSafe<ConeLookupTagBasedType>()
val type = coneType?.toIrType()
val symbol = type?.classifierOrNull
return annotation.convertWithOffsets { startOffset, endOffset ->
when (symbol) {
is IrClassSymbol -> {
val irClass = symbol.owner
val irConstructor = (annotation.toResolvedCallableSymbol() as? FirConstructorSymbol)?.let {
this.declarationStorage.getIrConstructorSymbol(it)
} ?: run {
// Fallback for FirReferencePlaceholderForResolvedAnnotations from jar
val fir = coneType.lookupTag.toSymbol(session)?.fir as? FirClass
var constructorSymbol: FirConstructorSymbol? = null
fir?.unsubstitutedScope(session, scopeSession, withForcedTypeCalculator = true)?.processDeclaredConstructors {
if (it.fir.isPrimary && constructorSymbol == null) {
constructorSymbol = it
}
}
constructorSymbol?.let {
this.declarationStorage.getIrConstructorSymbol(it)
}
}
if (irConstructor == null) {
IrErrorCallExpressionImpl(startOffset, endOffset, type, "No annotation constructor found: ${irClass.name}")
} else {
IrConstructorCallImpl(
startOffset, endOffset, type, irConstructor,
valueArgumentsCount = irConstructor.owner.valueParameters.size,
typeArgumentsCount = 0,
constructorTypeArgumentsCount = 0
)
}
}
else -> {
IrErrorCallExpressionImpl(
startOffset,
endOffset,
type ?: createErrorType(),
"Unresolved reference: ${annotation.render()}"
)
}
}
}.applyCallArguments(annotation.toAnnotationCall(), annotationMode = true)
}
private fun FirAnnotation.toAnnotationCall(): FirAnnotationCall? {
if (this is FirAnnotationCall) return this
return buildAnnotationCall {
useSiteTarget = this@toAnnotationCall.useSiteTarget
annotationTypeRef = this@toAnnotationCall.annotationTypeRef
val symbol = annotationTypeRef.coneType.toSymbol(session) as? FirRegularClassSymbol ?: return null
val constructorSymbol = symbol.declarationSymbols.firstIsInstanceOrNull<FirConstructorSymbol>() ?: return null
val argumentToParameterToMapping = constructorSymbol.valueParameterSymbols.mapNotNull {
val parameter = it.fir
val argument = this@toAnnotationCall.argumentMapping.mapping[parameter.name] ?: return@mapNotNull null
argument to parameter
}.toMap(LinkedHashMap())
argumentList = buildResolvedArgumentList(argumentToParameterToMapping)
calleeReference = buildResolvedNamedReference {
name = symbol.classId.shortClassName
resolvedSymbol = constructorSymbol
}
}
}
internal fun convertToGetObject(qualifier: FirResolvedQualifier): IrExpression {
return convertToGetObject(qualifier, null)!!
}
internal fun convertToGetObject(
qualifier: FirResolvedQualifier,
callableReferenceAccess: FirCallableReferenceAccess?
): IrExpression? {
val classSymbol = (qualifier.typeRef.coneType as? ConeClassLikeType)?.lookupTag?.toSymbol(session)
if (callableReferenceAccess != null && classSymbol is FirRegularClassSymbol) {
val classIdMatched = classSymbol.classId == qualifier.classId
if (!classIdMatched) {
// Check whether we need get companion object as dispatch receiver
if (!classSymbol.fir.isCompanion || classSymbol.classId.outerClassId != qualifier.classId) {
return null
}
val resolvedReference = callableReferenceAccess.calleeReference as FirResolvedNamedReference
val firCallableSymbol = resolvedReference.resolvedSymbol as FirCallableSymbol<*>
// Make sure the reference indeed refers to a member of that companion
if (firCallableSymbol.dispatchReceiverClassOrNull() != classSymbol.toLookupTag()) {
return null
}
}
}
val irType = qualifier.typeRef.toIrType()
return qualifier.convertWithOffsets { startOffset, endOffset ->
if (classSymbol != null) {
IrGetObjectValueImpl(
startOffset, endOffset, irType,
classSymbol.toSymbol() as IrClassSymbol
)
} else {
IrErrorCallExpressionImpl(
startOffset, endOffset, irType,
"Resolved qualifier ${qualifier.render()} does not have correctly resolved type"
)
}
}
}
private fun FirFunctionCall.buildSubstitutorByCalledFunction(function: FirFunction?): ConeSubstitutor? {
if (function == null) return null
val map = mutableMapOf<FirTypeParameterSymbol, ConeKotlinType>()
for ((index, typeParameter) in function.typeParameters.withIndex()) {
val typeProjection = typeArguments.getOrNull(index) as? FirTypeProjectionWithVariance ?: continue
map[typeParameter.symbol] = typeProjection.typeRef.coneType
}
return ConeSubstitutorByMap(map, session)
}
private fun extractArgumentsMapping(
call: FirCall
): Triple<List<FirValueParameter>?, Map<FirExpression, FirValueParameter>?, ConeSubstitutor> {
val calleeReference = when (call) {
is FirFunctionCall -> call.calleeReference
is FirDelegatedConstructorCall -> call.calleeReference
is FirAnnotationCall -> call.calleeReference
else -> null
}
val function = if (calleeReference == FirReferencePlaceholderForResolvedAnnotations) {
val coneClassLikeType = (call as FirAnnotation).annotationTypeRef.coneTypeSafe<ConeClassLikeType>()
val firClass = (coneClassLikeType?.lookupTag?.toSymbol(session) as? FirRegularClassSymbol)?.fir
firClass?.declarations?.filterIsInstance<FirConstructor>()?.firstOrNull()
} else {
((calleeReference as? FirResolvedNamedReference)?.resolvedSymbol as? FirFunctionSymbol<*>)?.fir
}
val valueParameters = function?.valueParameters
val argumentMapping = call.resolvedArgumentMapping
val substitutor = (call as? FirFunctionCall)?.buildSubstitutorByCalledFunction(function) ?: ConeSubstitutor.Empty
return Triple(valueParameters, argumentMapping, substitutor)
}
internal fun IrExpression.applyCallArguments(
statement: FirStatement?,
annotationMode: Boolean
): IrExpression {
val call = statement as? FirCall
return when (this) {
is IrMemberAccessExpression<*> -> {
val contextReceiverCount = putContextReceiverArguments(statement)
if (call == null) return this
val argumentsCount = call.arguments.size
if (argumentsCount <= valueArgumentsCount) {
apply {
val calleeReference = when (call) {
is FirFunctionCall -> call.calleeReference
is FirDelegatedConstructorCall -> call.calleeReference
is FirAnnotationCall -> call.calleeReference
else -> null
}
val function = if (calleeReference == FirReferencePlaceholderForResolvedAnnotations) {
val coneClassLikeType = (call as FirAnnotation).annotationTypeRef.coneTypeSafe<ConeClassLikeType>()
val firClass = (coneClassLikeType?.lookupTag?.toSymbol(session) as? FirRegularClassSymbol)?.fir
firClass?.declarations?.filterIsInstance<FirConstructor>()?.firstOrNull()
} else {
((calleeReference as? FirResolvedNamedReference)?.resolvedSymbol as? FirFunctionSymbol<*>)?.fir
}
val valueParameters = function?.valueParameters
val argumentMapping = call.resolvedArgumentMapping
val substitutor = (call as? FirFunctionCall)?.buildSubstitutorByCalledFunction(function) ?: ConeSubstitutor.Empty
if (argumentMapping != null && (annotationMode || argumentMapping.isNotEmpty())) {
if (valueParameters != null) {
return applyArgumentsWithReorderingIfNeeded(
argumentMapping, valueParameters, substitutor, annotationMode,
contextReceiverCount,
)
}
}
// Case without argument mapping (deserialized annotation)
// TODO: support argument mapping in deserialized annotations and remove me
for ((index, argument) in call.arguments.withIndex()) {
val valueParameter = when (argument) {
is FirNamedArgumentExpression -> valueParameters?.find { it.name == argument.name }
else -> null
} ?: valueParameters?.get(index)
val argumentExpression = convertArgument(argument, valueParameter, substitutor)
putValueArgument(
(valueParameters?.indexOf(valueParameter)?.takeIf { it >= 0 } ?: index) + contextReceiverCount,
argumentExpression
)
}
}
} else {
val name = if (this is IrCallImpl) symbol.owner.name else "???"
IrErrorCallExpressionImpl(
startOffset, endOffset, type,
"Cannot bind $argumentsCount arguments to $name call with $valueArgumentsCount parameters"
).apply {
for (argument in call.arguments) {
addArgument(visitor.convertToIrExpression(argument))
}
}
}
}
is IrDynamicOperatorExpression -> apply {
if (call == null) return@apply
val (valueParameters, argumentMapping, substitutor) = extractArgumentsMapping(call)
if (argumentMapping != null && (annotationMode || argumentMapping.isNotEmpty())) {
if (valueParameters != null) {
val dynamicCallVarargArgument = argumentMapping.keys.firstOrNull()
?.safeAs<FirVarargArgumentsExpression>()
?: error("Dynamic call must have a single vararg argument")
for (argument in dynamicCallVarargArgument.arguments) {
val irArgument = convertArgument(argument, null, substitutor, annotationMode)
arguments.add(irArgument)
}
}
}
}
is IrErrorCallExpressionImpl -> apply {
for (argument in call?.arguments.orEmpty()) {
addArgument(visitor.convertToIrExpression(argument))
}
}
else -> this
}
}
private fun IrMemberAccessExpression<*>.putContextReceiverArguments(statement: FirStatement?): Int {
if (statement !is FirContextReceiverArgumentListOwner) return 0
val contextReceiverCount = statement.contextReceiverArguments.size
if (contextReceiverCount > 0) {
for (index in 0 until contextReceiverCount) {
putValueArgument(
index,
visitor.convertToIrExpression(statement.contextReceiverArguments[index]),
)
}
}
return contextReceiverCount
}
private fun IrMemberAccessExpression<*>.applyArgumentsWithReorderingIfNeeded(
argumentMapping: Map<FirExpression, FirValueParameter>,
valueParameters: List<FirValueParameter>,
substitutor: ConeSubstitutor,
annotationMode: Boolean,
contextReceiverCount: Int,
): IrExpression {
val converted = argumentMapping.entries.map { (argument, parameter) ->
parameter to convertArgument(argument, parameter, substitutor, annotationMode)
}
// If none of the parameters have side effects, the evaluation order doesn't matter anyway.
// For annotations, this is always true, since arguments have to be compile-time constants.
if (!annotationMode && !converted.all { (_, irArgument) -> irArgument.hasNoSideEffects() } &&
needArgumentReordering(argumentMapping.values, valueParameters)
) {
return IrBlockImpl(startOffset, endOffset, type, IrStatementOrigin.ARGUMENTS_REORDERING_FOR_CALL).apply {
fun IrExpression.freeze(nameHint: String): IrExpression {
if (isUnchanging()) return this
val (variable, symbol) = createTemporaryVariable(this, conversionScope, nameHint)
statements.add(variable)
return IrGetValueImpl(startOffset, endOffset, symbol, null)
}
dispatchReceiver = dispatchReceiver?.freeze("\$this")
extensionReceiver = extensionReceiver?.freeze("\$receiver")
for ((parameter, irArgument) in converted) {
putValueArgument(
valueParameters.indexOf(parameter) + contextReceiverCount,
irArgument.freeze(parameter.name.asString())
)
}
statements.add(this@applyArgumentsWithReorderingIfNeeded)
}
} else {
for ((parameter, irArgument) in converted) {
putValueArgument(valueParameters.indexOf(parameter) + contextReceiverCount, irArgument)
}
if (annotationMode) {
for ((index, parameter) in valueParameters.withIndex()) {
if (parameter.isVararg && !argumentMapping.containsValue(parameter)) {
val defaultValue = parameter.defaultValue
val value = if (defaultValue != null) {
convertArgument(defaultValue, parameter, ConeSubstitutor.Empty, annotationMode)
} else {
val elementType = parameter.returnTypeRef.toIrType()
IrVarargImpl(
UNDEFINED_OFFSET,
UNDEFINED_OFFSET,
elementType,
elementType.toArrayOrPrimitiveArrayType(irBuiltIns)
)
}
putValueArgument(index, value)
}
}
}
return this
}
}
private fun needArgumentReordering(
parametersInActualOrder: Collection<FirValueParameter>,
valueParameters: List<FirValueParameter>
): Boolean {
var lastValueParameterIndex = UNDEFINED_PARAMETER_INDEX
for (parameter in parametersInActualOrder) {
val index = valueParameters.indexOf(parameter)
if (index < lastValueParameterIndex) {
return true
}
lastValueParameterIndex = index
}
return false
}
private fun convertArgument(
argument: FirExpression,
parameter: FirValueParameter?,
substitutor: ConeSubstitutor,
annotationMode: Boolean = false
): IrExpression {
var irArgument = visitor.convertToIrExpression(argument, annotationMode)
if (parameter != null) {
with(visitor.implicitCastInserter) {
irArgument = irArgument.cast(argument, argument.typeRef, parameter.returnTypeRef)
}
}
with(adapterGenerator) {
if (parameter?.returnTypeRef is FirResolvedTypeRef) {
// Java type case (from annotations)
val parameterType = parameter.returnTypeRef.coneType
val unwrappedParameterType = if (parameter.isVararg) parameterType.arrayElementType()!! else parameterType
val samFunctionType = getFunctionTypeForPossibleSamType(unwrappedParameterType)
irArgument = irArgument.applySuspendConversionIfNeeded(argument, samFunctionType ?: unwrappedParameterType)
irArgument = irArgument.applySamConversionIfNeeded(argument, parameter, substitutor)
}
}
return irArgument.applyAssigningArrayElementsToVarargInNamedForm(argument, parameter)
}
private fun IrExpression.applyAssigningArrayElementsToVarargInNamedForm(
argument: FirExpression,
parameter: FirValueParameter?
): IrExpression {
// TODO: Need to refer to language feature: AllowAssigningArrayElementsToVarargsInNamedFormForFunctions
if (this !is IrVarargImpl ||
parameter?.isVararg != true ||
argument !is FirVarargArgumentsExpression ||
argument.arguments.none { it is FirNamedArgumentExpression }
) {
return this
}
elements.forEachIndexed { i, irVarargElement ->
if (irVarargElement !is IrSpreadElement &&
argument.arguments[i] is FirNamedArgumentExpression &&
irVarargElement is IrExpression &&
irVarargElement.type.isArray()
) {
elements[i] = IrSpreadElementImpl(irVarargElement.startOffset, irVarargElement.endOffset, irVarargElement)
}
}
return this
}
internal fun IrExpression.applyTypeArguments(access: FirQualifiedAccess): IrExpression {
if (this !is IrMemberAccessExpression<*>) return this
val argumentsCount = access.typeArguments.size
if (argumentsCount <= typeArgumentsCount) {
for ((index, argument) in access.typeArguments.withIndex()) {
val typeParameter = access.findTypeParameter(index)
val argumentFirType = (argument as FirTypeProjectionWithVariance).typeRef
val argumentIrType = if (typeParameter?.isReified == true) {
argumentFirType.approximatedForPublicPosition(approximator).toIrType()
} else {
argumentFirType.toIrType()
}
putTypeArgument(index, argumentIrType)
}
return this
} else {
val name = if (this is IrCallImpl) symbol.owner.name else "???"
return IrErrorExpressionImpl(
startOffset, endOffset, type,
"Cannot bind $argumentsCount type arguments to $name call with $typeArgumentsCount type parameters"
)
}
}
private fun FirQualifiedAccess.findTypeParameter(index: Int): FirTypeParameter? =
((calleeReference as? FirResolvedNamedReference)?.resolvedSymbol?.fir as? FirTypeParametersOwner)?.typeParameters?.get(index)
private fun FirQualifiedAccess.findIrDispatchReceiver(explicitReceiverExpression: IrExpression?): IrExpression? =
findIrReceiver(explicitReceiverExpression, isDispatch = true)
private fun FirQualifiedAccess.findIrExtensionReceiver(explicitReceiverExpression: IrExpression?): IrExpression? =
findIrReceiver(explicitReceiverExpression, isDispatch = false)
internal fun FirQualifiedAccess.findIrReceiver(explicitReceiverExpression: IrExpression?, isDispatch: Boolean): IrExpression? {
val firReceiver = if (isDispatch) dispatchReceiver else extensionReceiver
if (firReceiver == explicitReceiver) {
return explicitReceiverExpression
}
return firReceiver.takeIf { it !is FirNoReceiverExpression }
?.let { visitor.convertToIrReceiverExpression(it, calleeReference, this as? FirCallableReferenceAccess) }
?: explicitReceiverExpression
?: run {
if (this is FirCallableReferenceAccess) return null
val name = if (isDispatch) "Dispatch" else "Extension"
error("$name receiver expected: ${render()} to ${calleeReference.render()}")
}
}
private fun IrExpression.applyReceivers(qualifiedAccess: FirQualifiedAccess, explicitReceiverExpression: IrExpression?): IrExpression {
when (this) {
is IrMemberAccessExpression<*> -> {
val ownerFunction =
symbol.owner as? IrFunction
?: (symbol.owner as? IrProperty)?.getter
if (ownerFunction?.dispatchReceiverParameter != null) {
val baseDispatchReceiver = qualifiedAccess.findIrDispatchReceiver(explicitReceiverExpression)
dispatchReceiver =
if (!ownerFunction.isMethodOfAny() || baseDispatchReceiver?.type?.classOrNull?.owner?.isInterface != true) {
baseDispatchReceiver
} else {
// NB: for FE 1.0, this type cast is added by InterfaceObjectCallsLowering
// However, it doesn't work for FIR due to different f/o structure
// (FIR calls Any method directly, but FE 1.0 calls its interface f/o instead)
IrTypeOperatorCallImpl(
baseDispatchReceiver.startOffset,
baseDispatchReceiver.endOffset,
irBuiltIns.anyType,
IrTypeOperator.IMPLICIT_CAST,
irBuiltIns.anyType,
baseDispatchReceiver
)
}
}
if (ownerFunction?.extensionReceiverParameter != null) {
extensionReceiver = qualifiedAccess.findIrExtensionReceiver(explicitReceiverExpression)?.let {
((qualifiedAccess.calleeReference as FirResolvedNamedReference)
.resolvedSymbol.fir as? FirCallableDeclaration)?.receiverTypeRef?.let { receiverType ->
with(visitor.implicitCastInserter) {
it.cast(
qualifiedAccess.extensionReceiver,
qualifiedAccess.extensionReceiver.typeRef,
receiverType
)
}
} ?: it
}
}
}
is IrFieldAccessExpression -> {
val ownerField = symbol.owner
if (!ownerField.isStatic) {
receiver = qualifiedAccess.findIrDispatchReceiver(explicitReceiverExpression)
}
}
is IrDynamicOperatorExpression -> {
receiver = explicitReceiverExpression ?: error("No receiver for dynamic")
}
}
return this
}
private fun generateErrorCallExpression(
startOffset: Int,
endOffset: Int,
calleeReference: FirReference,
type: IrType? = null
): IrErrorCallExpression {
return IrErrorCallExpressionImpl(
startOffset, endOffset, type ?: createErrorType(),
"Unresolved reference: ${calleeReference.render()}"
)
}
}