From 624728e5c7f46c79968c80625d9db020c70e8b78 Mon Sep 17 00:00:00 2001 From: Nikolay Lunyak Date: Sat, 2 Apr 2022 00:58:47 +0300 Subject: [PATCH] [FIR JS] Support dynamic type in IR --- .../fir/backend/Fir2IrImplicitCastInserter.kt | 15 +- .../kotlin/fir/backend/Fir2IrTypeConverter.kt | 8 +- .../kotlin/fir/backend/Fir2IrVisitor.kt | 155 ++++++++++-- .../generators/CallAndReferenceGenerator.kt | 229 +++++++++++++++++- .../generators/OperatorExpressionGenerator.kt | 99 +++++++- 5 files changed, 467 insertions(+), 39 deletions(-) diff --git a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/Fir2IrImplicitCastInserter.kt b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/Fir2IrImplicitCastInserter.kt index 73c8d6b963a..389464bbf8a 100644 --- a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/Fir2IrImplicitCastInserter.kt +++ b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/Fir2IrImplicitCastInserter.kt @@ -205,6 +205,13 @@ class Fir2IrImplicitCastInserter( expectedType.isUnit -> { coerceToUnitIfNeeded(this, irBuiltIns) } + valueType.coneTypeSafe() != null -> { + if (expectedType.coneType !is ConeDynamicType && !expectedType.isNullableAny) { + implicitCast(this, expectedType.toIrType()) + } else { + this + } + } typeCanBeEnhancedOrFlexibleNullable(valueType) && !expectedType.acceptsNullValues() -> { insertImplicitNotNullCastIfNeeded(expression) } @@ -326,11 +333,17 @@ class Fir2IrImplicitCastInserter( companion object { private fun implicitCast(original: IrExpression, castType: IrType): IrExpression { + val typeOperator = if (original.type is IrDynamicType) { + IrTypeOperator.IMPLICIT_DYNAMIC_CAST + } else { + IrTypeOperator.IMPLICIT_CAST + } + return IrTypeOperatorCallImpl( original.startOffset, original.endOffset, castType, - IrTypeOperator.IMPLICIT_CAST, + typeOperator, castType, original ) diff --git a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/Fir2IrTypeConverter.kt b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/Fir2IrTypeConverter.kt index 3758b1fce6b..5ae50c5ff1c 100644 --- a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/Fir2IrTypeConverter.kt +++ b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/Fir2IrTypeConverter.kt @@ -20,9 +20,7 @@ import org.jetbrains.kotlin.ir.symbols.IrClassSymbol import org.jetbrains.kotlin.ir.types.IrType import org.jetbrains.kotlin.ir.types.IrTypeArgument import org.jetbrains.kotlin.ir.types.IrTypeProjection -import org.jetbrains.kotlin.ir.types.impl.IrSimpleTypeImpl -import org.jetbrains.kotlin.ir.types.impl.IrStarProjectionImpl -import org.jetbrains.kotlin.ir.types.impl.makeTypeProjection +import org.jetbrains.kotlin.ir.types.impl.* import org.jetbrains.kotlin.ir.types.makeNotNull import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.StandardClassIds @@ -168,6 +166,10 @@ class Fir2IrTypeConverter( addRawTypeAnnotation = true ) } + is ConeDynamicType -> { + val typeAnnotations = with(annotationGenerator) { annotations.toIrAnnotations() } + return IrDynamicTypeImpl(null, typeAnnotations, Variance.INVARIANT) + } is ConeFlexibleType -> with(session.typeContext) { if (upperBound is ConeClassLikeType) { val upper = upperBound as ConeClassLikeType diff --git a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/Fir2IrVisitor.kt b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/Fir2IrVisitor.kt index 3dfe1f31473..37e789615e6 100644 --- a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/Fir2IrVisitor.kt +++ b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/Fir2IrVisitor.kt @@ -6,10 +6,7 @@ package org.jetbrains.kotlin.fir.backend import com.intellij.psi.tree.IElementType -import org.jetbrains.kotlin.KtFakeSourceElementKind -import org.jetbrains.kotlin.KtNodeTypes -import org.jetbrains.kotlin.KtPsiSourceElement -import org.jetbrains.kotlin.KtSourceElement +import org.jetbrains.kotlin.* import org.jetbrains.kotlin.descriptors.ClassKind import org.jetbrains.kotlin.descriptors.Modality import org.jetbrains.kotlin.descriptors.Visibilities @@ -20,14 +17,9 @@ import org.jetbrains.kotlin.fir.backend.generators.OperatorExpressionGenerator import org.jetbrains.kotlin.fir.declarations.* import org.jetbrains.kotlin.fir.declarations.builder.buildProperty import org.jetbrains.kotlin.fir.declarations.impl.FirDeclarationStatusImpl -import org.jetbrains.kotlin.fir.declarations.utils.expandedConeType -import org.jetbrains.kotlin.fir.declarations.utils.isSealed -import org.jetbrains.kotlin.fir.declarations.utils.isSynthetic -import org.jetbrains.kotlin.fir.declarations.utils.visibility +import org.jetbrains.kotlin.fir.declarations.utils.* import org.jetbrains.kotlin.fir.expressions.* -import org.jetbrains.kotlin.fir.expressions.impl.FirElseIfTrueCondition -import org.jetbrains.kotlin.fir.expressions.impl.FirStubStatement -import org.jetbrains.kotlin.fir.expressions.impl.FirUnitExpression +import org.jetbrains.kotlin.fir.expressions.impl.* import org.jetbrains.kotlin.fir.references.FirReference import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference import org.jetbrains.kotlin.fir.references.FirSuperReference @@ -43,7 +35,7 @@ import org.jetbrains.kotlin.ir.builders.* 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.IrClassSymbol +import org.jetbrains.kotlin.ir.symbols.* import org.jetbrains.kotlin.ir.types.* import org.jetbrains.kotlin.ir.util.constructors import org.jetbrains.kotlin.ir.util.defaultType @@ -54,6 +46,7 @@ import org.jetbrains.kotlin.name.SpecialNames import org.jetbrains.kotlin.psi.KtBinaryExpression import org.jetbrains.kotlin.psi.KtForExpression import org.jetbrains.kotlin.util.OperatorNameConventions +import org.jetbrains.kotlin.utils.addToStdlib.safeAs class Fir2IrVisitor( private val components: Fir2IrComponents, @@ -373,10 +366,50 @@ class Fir2IrVisitor( } else convertToIrExpression(this, annotationMode) private fun convertToIrCall(functionCall: FirFunctionCall, annotationMode: Boolean): IrExpression { - val explicitReceiverExpression = convertToIrReceiverExpression( - functionCall.explicitReceiver, functionCall.calleeReference + if (functionCall.isCalleeDynamic && + functionCall.calleeReference.name == OperatorNameConventions.SET && + functionCall.calleeReference.source?.kind == KtFakeSourceElementKind.ArrayAccessNameReference + ) { + return convertToIrArrayAccessDynamicCall(functionCall, annotationMode) + } + return convertToIrCall(functionCall, annotationMode, dynamicOperator = null) + } + + private fun convertToIrCall( + functionCall: FirFunctionCall, + annotationMode: Boolean, + dynamicOperator: IrDynamicOperator? + ): IrExpression { + val explicitReceiverExpression = convertToIrReceiverExpression(functionCall.explicitReceiver, functionCall.calleeReference) + return callGenerator.convertToIrCall( + functionCall, + functionCall.typeRef, + explicitReceiverExpression, + annotationMode, + dynamicOperator ) - return callGenerator.convertToIrCall(functionCall, functionCall.typeRef, explicitReceiverExpression, annotationMode) + } + + private fun convertToIrArrayAccessDynamicCall(functionCall: FirFunctionCall, annotationMode: Boolean): IrExpression { + val explicitReceiverExpression = convertToIrCall( + functionCall, annotationMode, dynamicOperator = IrDynamicOperator.ARRAY_ACCESS + ) + if (explicitReceiverExpression is IrDynamicOperatorExpression) { + explicitReceiverExpression.arguments.removeLast() + } + val result = callGenerator.convertToIrCall( + functionCall, functionCall.typeRef, explicitReceiverExpression, + annotationMode = annotationMode, + dynamicOperator = IrDynamicOperator.EQ + ) + if (result is IrDynamicOperatorExpression) { + val arguments = result.arguments + arguments[0] = arguments[arguments.lastIndex] + while (arguments.size > 1) { + arguments.removeLast() + } + } + return result } override fun visitFunctionCall(functionCall: FirFunctionCall, data: Any?): IrExpression { @@ -709,7 +742,82 @@ class Fir2IrVisitor( return this == IrStatementOrigin.DO_WHILE_LOOP || this == IrStatementOrigin.WHILE_LOOP || this == IrStatementOrigin.FOR_LOOP } + private inline fun List<*>.findFirst() = firstOrNull { it is K } as? K + + private inline fun List<*>.findLast() = lastOrNull { it is K } as? K + + private fun extractOperationFromDynamicSetCall(functionCall: FirFunctionCall) = + functionCall.dynamicVarargArguments?.lastOrNull() as? FirFunctionCall + + private val FirExpression.isIncrementOrDecrementCall: Boolean + get() { + val name = safeAs()?.calleeReference?.resolved?.name + return name == OperatorNameConventions.INC || name == OperatorNameConventions.DEC + } + + private fun FirBlock.tryConvertDynamicIncrementOrDecrementToIr(): IrExpression? { + val receiver = statements.findFirst() ?: return null + val receiverValue = receiver.initializer ?: return null + + if (receiverValue.typeRef.coneType !is ConeDynamicType) { + return null + } + + val savedValue = statements.findLast()?.initializer ?: return null + val isPrefix = savedValue.isIncrementOrDecrementCall + + val (operationReceiver, operationCall) = if (isPrefix) { + val operation = savedValue as? FirFunctionCall ?: return null + val operationReceiver = operation.explicitReceiver ?: return null + operationReceiver to operation + } else { + val operation = statements.findLast()?.rValue as? FirFunctionCall + ?: statements.findLast()?.let { extractOperationFromDynamicSetCall(it) } + ?: return null + savedValue to operation + } + + val isArrayAccess = receiver.name == SpecialNames.ARRAY + + val explicitReceiverExpression = if (isArrayAccess) { + val arrayAccess = operationReceiver as? FirFunctionCall ?: return null + val originalVararg = arrayAccess.resolvedArgumentMapping?.keys?.filterIsInstance()?.firstOrNull() + (callGenerator.convertToIrCall( + arrayAccess, arrayAccess.typeRef, + convertToIrReceiverExpression(receiverValue, arrayAccess.calleeReference), + noArguments = true + ) as IrDynamicOperatorExpression).apply { + originalVararg?.arguments?.forEach { + val that = (it as? FirPropertyAccessExpression)?.calleeReference?.resolvedSymbol?.fir as? FirProperty + val initializer = that?.initializer ?: return@forEach + arguments.add(convertToIrExpression(initializer)) + } + } + } else { + val qualifiedAccess = operationReceiver as? FirQualifiedAccessExpression ?: return null + val receiverExpression = if (receiverValue != qualifiedAccess) { + receiverValue + } else { + null + } + callGenerator.convertToIrCall( + qualifiedAccess, + qualifiedAccess.typeRef, + convertToIrReceiverExpression(receiverExpression, qualifiedAccess.calleeReference), + annotationMode = false + ) + } + return callGenerator.convertToIrCall( + operationCall, operationCall.typeRef, explicitReceiverExpression, annotationMode = false + ) + } + private fun FirBlock.convertToIrExpressionOrBlock(origin: IrStatementOrigin? = null): IrExpression { + if (this.source?.kind == KtFakeSourceElementKind.DesugaredIncrementOrDecrement) { + tryConvertDynamicIncrementOrDecrementToIr()?.let { + return it + } + } return statements.convertToIrExpressionOrBlock(source, origin) } @@ -1205,11 +1313,26 @@ class Fir2IrVisitor( return callGenerator.convertToGetObject(resolvedQualifier) } + private fun LogicOperationKind.toIrDynamicOperator() = when (this) { + LogicOperationKind.AND -> IrDynamicOperator.ANDAND + LogicOperationKind.OR -> IrDynamicOperator.OROR + } + override fun visitBinaryLogicExpression(binaryLogicExpression: FirBinaryLogicExpression, data: Any?): IrElement { return binaryLogicExpression.convertWithOffsets { startOffset, endOffset -> val leftOperand = binaryLogicExpression.leftOperand.accept(this, data) as IrExpression val rightOperand = binaryLogicExpression.rightOperand.accept(this, data) as IrExpression - when (binaryLogicExpression.kind) { + if (leftOperand.type is IrDynamicType) { + IrDynamicOperatorExpressionImpl( + startOffset, + endOffset, + irBuiltIns.booleanType, + binaryLogicExpression.kind.toIrDynamicOperator(), + ).apply { + receiver = leftOperand + arguments.add(rightOperand) + } + } else when (binaryLogicExpression.kind) { LogicOperationKind.AND -> { IrIfThenElseImpl(startOffset, endOffset, irBuiltIns.booleanType, IrStatementOrigin.ANDAND).apply { branches.add(IrBranchImpl(leftOperand, rightOperand)) diff --git a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/generators/CallAndReferenceGenerator.kt b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/generators/CallAndReferenceGenerator.kt index 311d98aae30..8a0a245c26f 100644 --- a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/generators/CallAndReferenceGenerator.kt +++ b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/generators/CallAndReferenceGenerator.kt @@ -28,6 +28,7 @@ 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 @@ -43,10 +44,13 @@ 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, @@ -269,12 +273,98 @@ class CallAndReferenceGenerator( 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, - variableAsFunctionMode: Boolean = false + dynamicOperator: IrDynamicOperator? = null, + variableAsFunctionMode: Boolean = false, + noArguments: Boolean = false ): IrExpression { try { val type = typeRef.toIrType() @@ -283,10 +373,45 @@ class CallAndReferenceGenerator( 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) { @@ -339,15 +464,17 @@ class CallAndReferenceGenerator( 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 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, explicitReceiverExpression) + }.applyTypeArguments(qualifiedAccess).applyReceivers(qualifiedAccess, convertedExplicitReceiver) .applyCallArguments(qualifiedAccess, annotationMode) } catch (e: Throwable) { throw IllegalStateException( @@ -357,16 +484,58 @@ class CallAndReferenceGenerator( } } + 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 -> - val assignedValue = visitor.convertToIrExpression(variableAssignment.rValue) when (symbol) { is IrFieldSymbol -> IrSetFieldImpl(startOffset, endOffset, symbol, type, origin).apply { value = assignedValue @@ -421,7 +590,9 @@ class CallAndReferenceGenerator( putValueArgument(0, assignedValue) } } - is IrVariableSymbol -> IrSetValueImpl(startOffset, endOffset, type, symbol, assignedValue, origin) + is IrVariableSymbol -> { + IrSetValueImpl(startOffset, endOffset, type, symbol, assignedValue, origin) + } else -> generateErrorCallExpression(startOffset, endOffset, calleeReference) } }.applyTypeArguments(variableAssignment).applyReceivers(variableAssignment, explicitReceiverExpression) @@ -551,6 +722,28 @@ class CallAndReferenceGenerator( return ConeSubstitutorByMap(map, session) } + private fun extractArgumentsMapping( + call: FirCall + ): Triple?, Map?, 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() + val firClass = (coneClassLikeType?.lookupTag?.toSymbol(session) as? FirRegularClassSymbol)?.fir + firClass?.declarations?.filterIsInstance()?.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 @@ -613,6 +806,21 @@ class CallAndReferenceGenerator( } } } + 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() + ?: 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)) @@ -857,6 +1065,9 @@ class CallAndReferenceGenerator( receiver = qualifiedAccess.findIrDispatchReceiver(explicitReceiverExpression) } } + is IrDynamicOperatorExpression -> { + receiver = explicitReceiverExpression ?: error("No receiver for dynamic") + } } return this } diff --git a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/generators/OperatorExpressionGenerator.kt b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/generators/OperatorExpressionGenerator.kt index 38e78934f2e..73620a3708e 100644 --- a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/generators/OperatorExpressionGenerator.kt +++ b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/generators/OperatorExpressionGenerator.kt @@ -7,17 +7,14 @@ package org.jetbrains.kotlin.fir.backend.generators import org.jetbrains.kotlin.fir.backend.* import org.jetbrains.kotlin.fir.expressions.* -import org.jetbrains.kotlin.fir.types.isMarkedNullable -import org.jetbrains.kotlin.fir.types.isNullable +import org.jetbrains.kotlin.fir.types.* import org.jetbrains.kotlin.ir.builders.primitiveOp1 import org.jetbrains.kotlin.ir.builders.primitiveOp2 import org.jetbrains.kotlin.ir.expressions.* -import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl -import org.jetbrains.kotlin.ir.expressions.impl.IrConstImpl -import org.jetbrains.kotlin.ir.expressions.impl.IrGetValueImpl -import org.jetbrains.kotlin.ir.expressions.impl.IrTypeOperatorCallImpl +import org.jetbrains.kotlin.ir.expressions.impl.* import org.jetbrains.kotlin.ir.symbols.IrClassifierSymbol import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol +import org.jetbrains.kotlin.ir.types.IrDynamicType import org.jetbrains.kotlin.ir.types.* import org.jetbrains.kotlin.ir.types.impl.IrSimpleTypeImpl import org.jetbrains.kotlin.ir.util.getSimpleFunction @@ -45,6 +42,24 @@ internal class OperatorExpressionGenerator( comparisonExpression: FirComparisonExpression ): IrExpression { val operation = comparisonExpression.operation + val receiver = comparisonExpression.compareToCall.explicitReceiver + + if (receiver?.typeRef?.coneType is ConeDynamicType) { + val dynamicOperator = operation.toIrDynamicOperator() + ?: throw Exception("Can't convert to the corresponding IrDynamicOperator") + val argument = comparisonExpression.compareToCall.dynamicVarargArguments?.firstOrNull() + ?: throw Exception("Comparison with a dynamic function should have a vararg with the rhs-argument") + + return IrDynamicOperatorExpressionImpl( + startOffset, + endOffset, + irBuiltIns.booleanType, + dynamicOperator, + ).apply { + this.receiver = receiver.accept(visitor, null) as IrExpression + arguments.add(argument.accept(visitor, null) as IrExpression) + } + } fun fallbackToRealCall(): IrExpression { val (symbol, origin) = getSymbolAndOriginForComparison(operation, irBuiltIns.intType.classifierOrFail) @@ -92,6 +107,14 @@ internal class OperatorExpressionGenerator( } } + private fun FirOperation.toIrDynamicOperator() = when (this) { + FirOperation.LT -> IrDynamicOperator.LT + FirOperation.LT_EQ -> IrDynamicOperator.LE + FirOperation.GT -> IrDynamicOperator.GT + FirOperation.GT_EQ -> IrDynamicOperator.GE + else -> null + } + private fun generateEqualityOperatorCall( startOffset: Int, endOffset: Int, operation: FirOperation, arguments: List ): IrExpression = when (operation) { @@ -100,6 +123,37 @@ internal class OperatorExpressionGenerator( else -> error("Unexpected operation: $operation") } + private fun IrStatementOrigin.toIrDynamicOperator() = when (this) { + is IrStatementOrigin.EQEQ -> IrDynamicOperator.EQEQ + is IrStatementOrigin.EXCLEQ -> IrDynamicOperator.EXCLEQ + is IrStatementOrigin.EQEQEQ -> IrDynamicOperator.EQEQEQ + is IrStatementOrigin.EXCLEQEQ -> IrDynamicOperator.EXCLEQEQ + else -> null + } + + private fun tryGenerateDynamicOperatorCall( + startOffset: Int, + endOffset: Int, + firstArgument: IrExpression, + secondArgument: IrExpression, + origin: IrStatementOrigin, + ) = if (firstArgument.type is IrDynamicType) { + val dynamicOperator = origin.toIrDynamicOperator() + ?: throw Exception("Couldn't convert to the corresponding IrDynamicOperator") + + IrDynamicOperatorExpressionImpl( + startOffset, + endOffset, + irBuiltIns.booleanType, + dynamicOperator, + ).apply { + receiver = firstArgument + arguments.add(secondArgument) + } + } else { + null + } + private fun transformEqualityOperatorCall( startOffset: Int, endOffset: Int, operation: FirOperation, arguments: List ): IrExpression { @@ -109,6 +163,20 @@ internal class OperatorExpressionGenerator( else -> error("Not an equality operation: $operation") } val comparisonInfo = inferPrimitiveNumericComparisonInfo(arguments[0], arguments[1]) + + val convertedLeft = arguments[0].convertToIrExpression(comparisonInfo, isLeftType = true) + val convertedRight = arguments[1].convertToIrExpression(comparisonInfo, isLeftType = false) + + tryGenerateDynamicOperatorCall( + startOffset, + endOffset, + convertedLeft, + convertedRight, + origin, + )?.let { + return it + } + val comparisonType = comparisonInfo?.comparisonType val eqeqSymbol = comparisonType?.let { typeConverter.classIdToSymbolMap[it.lookupTag.classId] } ?.let { irBuiltIns.ieee754equalsFunByOperandType[it] } ?: irBuiltIns.eqeqSymbol @@ -119,8 +187,8 @@ internal class OperatorExpressionGenerator( eqeqSymbol, irBuiltIns.booleanType, origin, - arguments[0].convertToIrExpression(comparisonInfo, isLeftType = true), - arguments[1].convertToIrExpression(comparisonInfo, isLeftType = false) + convertedLeft, + convertedRight ) return if (operation == FirOperation.EQ) { equalsCall @@ -137,13 +205,24 @@ internal class OperatorExpressionGenerator( FirOperation.NOT_IDENTITY -> IrStatementOrigin.EXCLEQEQ else -> error("Not an identity operation: $operation") } + val convertedLeft = visitor.convertToIrExpression(arguments[0]) + val convertedRight = visitor.convertToIrExpression(arguments[1]) + tryGenerateDynamicOperatorCall( + startOffset, + endOffset, + convertedLeft, + convertedRight, + origin, + )?.let { + return it + } val identityCall = primitiveOp2( startOffset, endOffset, irBuiltIns.eqeqeqSymbol, irBuiltIns.booleanType, origin, - visitor.convertToIrExpression(arguments[0]), - visitor.convertToIrExpression(arguments[1]) + convertedLeft, + convertedRight ) return if (operation == FirOperation.IDENTITY) { identityCall