Skip to content

Commit 55f29cc

Browse files
cypressiousSpace Team
authored andcommitted
[FIR] Implement explicit passing of context arguments by name
#KT-81684
1 parent dac1450 commit 55f29cc

File tree

12 files changed

+195
-90
lines changed

12 files changed

+195
-90
lines changed

compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/generators/CallAndReferenceGenerator.kt

Lines changed: 78 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,19 @@ import org.jetbrains.kotlin.descriptors.ClassKind
1313
import org.jetbrains.kotlin.fir.*
1414
import org.jetbrains.kotlin.fir.backend.*
1515
import org.jetbrains.kotlin.fir.backend.utils.*
16+
import org.jetbrains.kotlin.fir.backend.utils.buildSubstitutorByCalledCallable
1617
import org.jetbrains.kotlin.fir.declarations.*
1718
import org.jetbrains.kotlin.fir.declarations.utils.*
1819
import org.jetbrains.kotlin.fir.diagnostics.ConeSimpleDiagnostic
1920
import org.jetbrains.kotlin.fir.expressions.*
21+
import org.jetbrains.kotlin.fir.expressions.FirAnnotationCall
22+
import org.jetbrains.kotlin.fir.expressions.FirDelegatedConstructorCall
23+
import org.jetbrains.kotlin.fir.expressions.FirFunctionCall
2024
import org.jetbrains.kotlin.fir.expressions.builder.buildAnnotationCall
25+
import org.jetbrains.kotlin.fir.expressions.impl.FirResolvedArgumentList
2126
import org.jetbrains.kotlin.fir.java.declarations.FirJavaField
2227
import org.jetbrains.kotlin.fir.references.*
28+
import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference
2329
import org.jetbrains.kotlin.fir.references.builder.buildResolvedNamedReference
2430
import org.jetbrains.kotlin.fir.resolve.*
2531
import org.jetbrains.kotlin.fir.resolve.calls.FirSimpleSyntheticPropertySymbol
@@ -32,6 +38,7 @@ import org.jetbrains.kotlin.fir.scopes.impl.toConeType
3238
import org.jetbrains.kotlin.fir.scopes.impl.typeAliasConstructorInfo
3339
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
3440
import org.jetbrains.kotlin.fir.symbols.impl.*
41+
import org.jetbrains.kotlin.fir.symbols.impl.FirFunctionSymbol
3542
import org.jetbrains.kotlin.fir.symbols.lazyResolveToPhase
3643
import org.jetbrains.kotlin.fir.types.*
3744
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
@@ -222,7 +229,7 @@ class CallAndReferenceGenerator(
222229
val calleeReference = calleeReference as? FirResolvedNamedReference ?: return null
223230
val fir = calleeReference.resolvedSymbol.fir
224231
if (this is FirFunctionCall && fir is FirNamedFunction && fir.origin == FirDeclarationOrigin.SamConstructor) {
225-
val (_, _, substitutor) = extractArgumentsMapping(this)
232+
val substitutor = buildSubstitutorByCalledCallable()
226233
val irArgument = convertArgument(argument, fir.valueParameters.first(), substitutor)
227234
return convertWithOffsets { startOffset, endOffset ->
228235
IrTypeOperatorCallImpl(
@@ -425,13 +432,13 @@ class CallAndReferenceGenerator(
425432

426433
if (noArguments || qualifiedAccess !is FirCall) return@apply
427434

428-
val (valueParameters, argumentMapping, substitutor) = extractArgumentsMapping(qualifiedAccess)
429-
if (valueParameters == null || argumentMapping == null || !visitor.annotationMode && argumentMapping.isEmpty()) return@apply
435+
val argumentMapping = qualifiedAccess.resolvedArgumentMapping
436+
if (argumentMapping == null || !visitor.annotationMode && argumentMapping.isEmpty()) return@apply
430437

431438
val dynamicCallVarargArgument = argumentMapping.keys.firstOrNull() as? FirVarargArgumentsExpression
432439
?: error("Dynamic call must have a single vararg argument: ${qualifiedAccess.render()}")
433440
for (argument in dynamicCallVarargArgument.arguments) {
434-
val irArgument = convertArgument(argument, null, substitutor)
441+
val irArgument = convertArgument(argument, null, ConeSubstitutor.Empty)
435442
arguments.add(irArgument)
436443
}
437444
}
@@ -1019,22 +1026,6 @@ class CallAndReferenceGenerator(
10191026
}
10201027
}
10211028

1022-
private fun extractArgumentsMapping(
1023-
call: FirCall,
1024-
): Triple<List<FirValueParameter>?, Map<FirExpression, FirValueParameter>?, ConeSubstitutor> {
1025-
val calleeReference = when (call) {
1026-
is FirFunctionCall -> call.calleeReference
1027-
is FirDelegatedConstructorCall -> call.calleeReference
1028-
is FirAnnotationCall -> call.calleeReference
1029-
else -> null
1030-
}
1031-
val function = ((calleeReference as? FirResolvedNamedReference)?.resolvedSymbol as? FirFunctionSymbol<*>)?.fir
1032-
val valueParameters = function?.valueParameters
1033-
val argumentMapping = call.resolvedArgumentMapping
1034-
val substitutor = (call as? FirFunctionCall)?.buildSubstitutorByCalledCallable() ?: ConeSubstitutor.Empty
1035-
return Triple(valueParameters, argumentMapping, substitutor)
1036-
}
1037-
10381029
private fun convertArgument(
10391030
argument: FirExpression,
10401031
parameter: FirValueParameter?,
@@ -1444,19 +1435,19 @@ class CallAndReferenceGenerator(
14441435
val call = statement as? FirCall
14451436
return when (this) {
14461437
is IrMemberAccessExpression<*> -> {
1447-
val contextArgumentCount = putContextArguments(statement, receiverInfo)
1448-
if (call == null) return this
1438+
if (call == null) {
1439+
// Property access has implicit context arguments but no explicit arguments.
1440+
putContextArguments(statement, receiverInfo)
1441+
return this
1442+
}
14491443
val argumentsCount = call.arguments.size
14501444
if (declarationSiteSymbol != null && argumentsCount <= declarationSiteSymbol.valueParametersSize()) {
1451-
apply {
1452-
val (valueParameters, argumentMapping, substitutor) = extractArgumentsMapping(call)
1453-
if (argumentMapping != null && (visitor.annotationMode || argumentMapping.isNotEmpty()) && valueParameters != null) {
1454-
return applyArgumentsWithReorderingIfNeeded(
1455-
argumentMapping, valueParameters, substitutor, receiverInfo, contextArgumentCount, call,
1456-
)
1457-
}
1458-
check(argumentsCount == 0) { "Non-empty unresolved argument list." }
1445+
applyArgumentsWithReorderingIfNeeded(receiverInfo, call)?.let {
1446+
return it
14591447
}
1448+
1449+
check(argumentsCount == 0) { "Non-empty unresolved argument list." }
1450+
this
14601451
} else {
14611452
val calleeSymbol = (this as? IrCallImpl)?.symbol
14621453

@@ -1486,18 +1477,56 @@ class CallAndReferenceGenerator(
14861477
}
14871478

14881479
private fun IrMemberAccessExpression<*>.applyArgumentsWithReorderingIfNeeded(
1489-
argumentMapping: Map<FirExpression, FirValueParameter>,
1490-
valueParameters: List<FirValueParameter>,
1491-
substitutor: ConeSubstitutor,
14921480
receiverInfo: ReceiverInfo,
1493-
contextArgumentCount: Int,
14941481
call: FirCall,
1495-
): IrExpression {
1496-
val converted = convertArguments(argumentMapping, substitutor)
1482+
): IrExpression? {
1483+
val function = (call as? FirResolvable)?.calleeReference?.toResolvedFunctionSymbol()?.fir
1484+
val argumentList = call.argumentList as? FirResolvedArgumentList
1485+
if (function == null || argumentList == null || !visitor.annotationMode && argumentList.mappingIncludingContextArguments.isEmpty()) {
1486+
putContextArguments(call, receiverInfo)
1487+
return null
1488+
}
1489+
1490+
val contextParameters = function.contextParameters
1491+
val valueParameters = function.valueParameters
1492+
val substitutor = (call as? FirFunctionCall)?.buildSubstitutorByCalledCallable() ?: ConeSubstitutor.Empty
1493+
val contextArgumentCount = contextParameters.size
1494+
1495+
data class ArgumentInfo(val parameter: FirValueParameter, val expression: IrExpression, val parameterIndex: Int)
1496+
1497+
// Convert all context and value arguments.
1498+
// It's important to preserve the order of the explicit arguments including explicit context arguments
1499+
// because they can have side effects.
1500+
// Implicit context arguments are also in the list for convenience but their order doesn't matter because they can't have
1501+
// side effects.
1502+
val converted = buildList {
1503+
(call as? FirContextArgumentListOwner)?.contextArguments?.forEachIndexed { index, contextArgument ->
1504+
// Only convert implicit context arguments here, explicit ones will be converted below to preserve the order.
1505+
if (contextArgument in argumentList.mappingIncludingContextArguments) return@forEachIndexed
1506+
val parameter = contextParameters[index]
1507+
val parameterIndex = receiverInfo.contextArgumentOffset() + index
1508+
1509+
val irExpression = convertArgument(contextArgument, parameter, substitutor)
1510+
add(ArgumentInfo(parameter, irExpression, parameterIndex))
1511+
}
1512+
1513+
argumentList.mappingIncludingContextArguments.entries.forEach { (argument, parameter) ->
1514+
if (!visitor.isGetClassOfUnresolvedTypeInAnnotation(argument)) {
1515+
val parameterIndex = if (parameter.valueParameterKind == FirValueParameterKind.Regular) {
1516+
receiverInfo.valueArgumentOffset(contextArgumentCount) + valueParameters.indexOf(parameter)
1517+
} else {
1518+
receiverInfo.contextArgumentOffset() + contextParameters.indexOf(parameter)
1519+
}
1520+
val irExpression = convertArgument(argument, parameter, substitutor)
1521+
add(ArgumentInfo(parameter, irExpression, parameterIndex))
1522+
}
1523+
}
1524+
}
1525+
14971526
// If none of the parameters have side effects, the evaluation order doesn't matter anyway.
14981527
// For annotations, this is always true, since arguments have to be compile-time constants.
14991528
if (!visitor.annotationMode && !converted.all { (_, irArgument) -> irArgument.hasNoSideEffects() } &&
1500-
needArgumentReordering(argumentMapping.values, valueParameters)
1529+
needArgumentReordering(argumentList.mappingIncludingContextArguments.values, contextParameters + valueParameters)
15011530
) {
15021531
return IrBlockImpl(startOffset, endOffset, type, IrStatementOrigin.ARGUMENTS_REORDERING_FOR_CALL).apply {
15031532
fun IrExpression.freeze(nameHint: String): IrExpression {
@@ -1507,6 +1536,7 @@ class CallAndReferenceGenerator(
15071536
return IrGetValueImpl(startOffset, endOffset, symbol, null)
15081537
}
15091538

1539+
// Freeze receivers first
15101540
if (receiverInfo.hasDispatchReceiver) {
15111541
arguments[0] = arguments[0]?.freeze($$"$this")
15121542
}
@@ -1516,21 +1546,20 @@ class CallAndReferenceGenerator(
15161546
arguments[extensionReceiverIndex] = arguments[extensionReceiverIndex]?.freeze($$"$receiver")
15171547
}
15181548

1519-
val valueArgumentOffset = receiverInfo.valueArgumentOffset(contextArgumentCount)
1520-
for ((parameter, irArgument) in converted) {
1521-
arguments[valueArgumentOffset + valueParameters.indexOf(parameter)] = irArgument.freeze(parameter.name.asString())
1549+
// Add and freeze context and value arguments in source order
1550+
for ((parameter, irArgument, parameterIndex) in converted) {
1551+
arguments[parameterIndex] = irArgument.freeze(parameter.name.asString())
15221552
}
15231553
statements.add(this@applyArgumentsWithReorderingIfNeeded)
15241554
}
15251555
} else {
1526-
val valueArgumentOffset = receiverInfo.valueArgumentOffset(contextArgumentCount)
1527-
for ((parameter, irArgument) in converted) {
1528-
arguments[valueArgumentOffset + valueParameters.indexOf(parameter)] = irArgument
1556+
for ((_, irArgument, parameterIndex) in converted) {
1557+
arguments[parameterIndex] = irArgument
15291558
}
15301559
if (visitor.annotationMode) {
15311560
val function = call.toReference(session)?.toResolvedCallableSymbol()?.fir as? FirFunction
15321561
for ((index, parameter) in valueParameters.withIndex()) {
1533-
if (parameter.isVararg && !argumentMapping.containsValue(parameter)) {
1562+
if (parameter.isVararg && !argumentList.mapping.containsValue(parameter)) {
15341563
val value = if (function?.itOrExpectHasDefaultParameterValue(index) == true) {
15351564
null
15361565
} else {
@@ -1542,30 +1571,21 @@ class CallAndReferenceGenerator(
15421571
varargType.getArrayElementType(builtins)
15431572
)
15441573
}
1545-
arguments[valueArgumentOffset + index] = value
1574+
arguments[receiverInfo.valueArgumentOffset(contextArgumentCount) + index] = value
15461575
}
15471576
}
15481577
}
15491578
return this
15501579
}
15511580
}
15521581

1553-
private fun convertArguments(
1554-
argumentMapping: Map<FirExpression, FirValueParameter>,
1555-
substitutor: ConeSubstitutor,
1556-
): List<Pair<FirValueParameter, IrExpression>> =
1557-
argumentMapping.entries.mapNotNull { (argument, parameter) ->
1558-
if (visitor.isGetClassOfUnresolvedTypeInAnnotation(argument)) null
1559-
else (parameter to convertArgument(argument, parameter, substitutor))
1560-
}
1561-
15621582
private fun needArgumentReordering(
1563-
parametersInActualOrder: Collection<FirValueParameter>,
1564-
valueParameters: List<FirValueParameter>,
1583+
parametersInArgumentOrder: Collection<FirValueParameter>,
1584+
contextAndValueParameters: List<FirValueParameter>,
15651585
): Boolean {
15661586
var lastValueParameterIndex = UNDEFINED_PARAMETER_INDEX
1567-
for (parameter in parametersInActualOrder) {
1568-
val index = valueParameters.indexOf(parameter)
1587+
for (parameter in parametersInArgumentOrder) {
1588+
val index = contextAndValueParameters.indexOf(parameter)
15691589
if (index < lastValueParameterIndex) {
15701590
return true
15711591
}

compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/candidate/Candidate.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import org.jetbrains.kotlin.fir.expressions.FirThisReceiverExpression
1919
import org.jetbrains.kotlin.fir.expressions.builder.buildPropertyAccessExpressionCopy
2020
import org.jetbrains.kotlin.fir.expressions.builder.buildThisReceiverExpressionCopy
2121
import org.jetbrains.kotlin.fir.expressions.impl.FirExpressionStub
22+
import org.jetbrains.kotlin.fir.expressions.unwrapArgument
2223
import org.jetbrains.kotlin.fir.resolve.FirSamResolver
2324
import org.jetbrains.kotlin.fir.resolve.calls.*
2425
import org.jetbrains.kotlin.fir.resolve.calls.stages.TypeArgumentMapping
@@ -314,7 +315,7 @@ class Candidate(
314315
}
315316

316317
fun contextArguments(): List<FirExpression> {
317-
return contextArguments?.map { it.expression } ?: emptyList()
318+
return contextArguments?.map { it.expression.unwrapArgument() } ?: emptyList()
318319
}
319320

320321
private var sourcesWereUpdated = false

compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/overloads/ConeEquivalentCallConflictResolver.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,9 @@ class ConeEquivalentCallConflictResolver(private val session: FirSession) : Cone
8080
private val Candidate.mappedArgumentsOrderRepresentation: IntArray?
8181
get() {
8282
val function = symbol.fir as? FirFunction ?: return null
83-
val parametersToIndices = function.valueParameters.mapIndexed { index, it -> it to index }.toMap()
83+
val parametersToIndices = (function.valueParameters + function.contextParameters)
84+
.mapIndexed { index, it -> it to index }
85+
.toMap()
8486
if (!argumentMappingInitialized) return null
8587
val mapping = argumentMapping
8688
val result = IntArray(mapping.size + 1) { function.valueParameters.size }

compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/overloads/ConeOverloadConflictResolver.kt

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -565,8 +565,10 @@ class ConeOverloadConflictResolver(
565565
called.contextParameters.mapTo(this) { TypeWithConversion(it.returnTypeRef.coneType.prepareType(session, call)) }
566566
}
567567
if (call.argumentMappingInitialized) {
568-
call.argumentMapping.mapTo(this) { (argument, parameter) ->
569-
parameter.toTypeWithConversion(argument, session, call)
568+
call.argumentMapping.mapNotNullTo(this) { (argument, parameter) ->
569+
runIf(parameter.valueParameterKind == FirValueParameterKind.Regular) {
570+
parameter.toTypeWithConversion(argument, session, call)
571+
}
570572
}
571573
}
572574
}

compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/stages/CheckCallableReferenceExpectedType.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,7 @@ private fun BodyResolveComponents.getCallableReferenceAdaptation(
209209
requiredMembersPhase = FirResolvePhase.STATUS,
210210
)
211211

212-
val argumentMapping = mapArguments(fakeArguments, function, originScope = originScope, callSiteIsOperatorCall = false)
212+
val argumentMapping = mapArguments(fakeArguments, function, originScope = originScope, callSiteIsOperatorCall = false, lookInContextParameters = false)
213213
if (argumentMapping.diagnostics.anyUnsuccessful) return null
214214

215215
/**

0 commit comments

Comments
 (0)