[FIR JS] Support dynamic type in IR

This commit is contained in:
Nikolay Lunyak
2022-04-02 00:58:47 +03:00
committed by teamcity
parent 6b94ba5804
commit 624728e5c7
5 changed files with 467 additions and 39 deletions
@@ -205,6 +205,13 @@ class Fir2IrImplicitCastInserter(
expectedType.isUnit -> {
coerceToUnitIfNeeded(this, irBuiltIns)
}
valueType.coneTypeSafe<ConeDynamicType>() != 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
)
@@ -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
@@ -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 <reified K> List<*>.findFirst() = firstOrNull { it is K } as? K
private inline fun <reified K> 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<FirFunctionCall>()?.calleeReference?.resolved?.name
return name == OperatorNameConventions.INC || name == OperatorNameConventions.DEC
}
private fun FirBlock.tryConvertDynamicIncrementOrDecrementToIr(): IrExpression? {
val receiver = statements.findFirst<FirProperty>() ?: return null
val receiverValue = receiver.initializer ?: return null
if (receiverValue.typeRef.coneType !is ConeDynamicType) {
return null
}
val savedValue = statements.findLast<FirProperty>()?.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<FirVariableAssignment>()?.rValue as? FirFunctionCall
?: statements.findLast<FirFunctionCall>()?.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<FirVarargArgumentsExpression>()?.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<IrElement> { 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))
@@ -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<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
@@ -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<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))
@@ -857,6 +1065,9 @@ class CallAndReferenceGenerator(
receiver = qualifiedAccess.findIrDispatchReceiver(explicitReceiverExpression)
}
}
is IrDynamicOperatorExpression -> {
receiver = explicitReceiverExpression ?: error("No receiver for dynamic")
}
}
return this
}
@@ -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<FirExpression>
): 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<FirExpression>
): 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