FIR: Add preliminary support for context receivers in resolution

This commit is contained in:
Denis.Zharkov
2022-03-09 14:36:20 +03:00
committed by teamcity
parent 588dd23fd9
commit ef84dddc88
39 changed files with 688 additions and 133 deletions
@@ -119,7 +119,7 @@ class FirCallResolver(
functionCall.copyAsImplicitInvokeCall {
explicitReceiver = candidate.callInfo.explicitReceiver
dispatchReceiver = candidate.dispatchReceiverExpression()
extensionReceiver = candidate.extensionReceiverExpression()
extensionReceiver = candidate.chosenExtensionReceiverExpression()
argumentList = candidate.callInfo.argumentList
}
} else {
@@ -381,7 +381,7 @@ class FirCallResolver(
if (reducedCandidates.size == 1) {
val candidate = reducedCandidates.single()
resultExpression = resultExpression.transformDispatchReceiver(StoreReceiver, candidate.dispatchReceiverExpression())
resultExpression = resultExpression.transformExtensionReceiver(StoreReceiver, candidate.extensionReceiverExpression())
resultExpression = resultExpression.transformExtensionReceiver(StoreReceiver, candidate.chosenExtensionReceiverExpression())
}
if (resultExpression is FirExpression) transformer.storeTypeFromCallee(resultExpression)
return resultExpression
@@ -47,9 +47,10 @@ import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.name.StandardClassIds
import org.jetbrains.kotlin.resolve.ForbiddenNamedArgumentsTarget
import org.jetbrains.kotlin.types.ConstantValueKind
import org.jetbrains.kotlin.resolve.calls.tower.CandidateApplicability
import org.jetbrains.kotlin.types.ConstantValueKind
import org.jetbrains.kotlin.types.SmartcastStability
import org.jetbrains.kotlin.utils.addIfNotNull
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
import kotlin.contracts.ExperimentalContracts
import kotlin.contracts.contract
@@ -73,7 +74,10 @@ fun FirFunction.constructFunctionalType(isSuspend: Boolean = false): ConeLookupT
}
val rawReturnType = (this as FirCallableDeclaration).returnTypeRef.coneType
return createFunctionalType(parameters, receiverTypeRef?.coneType, rawReturnType, isSuspend = isSuspend)
return createFunctionalType(
parameters, receiverTypeRef?.coneType, rawReturnType, isSuspend = isSuspend,
contextReceivers = contextReceivers.map { it.typeRef.coneType }
)
}
fun FirFunction.constructFunctionalTypeRef(isSuspend: Boolean = false): FirResolvedTypeRef {
@@ -88,9 +92,16 @@ fun createFunctionalType(
receiverType: ConeKotlinType?,
rawReturnType: ConeKotlinType,
isSuspend: Boolean,
contextReceivers: List<ConeKotlinType> = emptyList(),
isKFunctionType: Boolean = false
): ConeLookupTagBasedType {
val receiverAndParameterTypes = listOfNotNull(receiverType) + parameters + listOf(rawReturnType)
val receiverAndParameterTypes =
buildList {
addAll(contextReceivers)
addIfNotNull(receiverType)
addAll(parameters)
add(rawReturnType)
}
val kind = if (isSuspend) {
if (isKFunctionType) FunctionClassKind.KSuspendFunction else FunctionClassKind.SuspendFunction
@@ -99,7 +110,18 @@ fun createFunctionalType(
}
val functionalTypeId = ClassId(kind.packageFqName, kind.numberedClassName(receiverAndParameterTypes.size - 1))
val attributes = if (receiverType != null) ConeAttributes.WithExtensionFunctionType else ConeAttributes.Empty
val attributes = when {
contextReceivers.isNotEmpty() -> ConeAttributes.create(
buildList {
add(CompilerConeAttributes.ContextFunctionTypeParams(contextReceivers.size))
if (receiverType != null) {
add(CompilerConeAttributes.ExtensionFunctionType)
}
}
)
receiverType != null -> ConeAttributes.WithExtensionFunctionType
else -> ConeAttributes.Empty
}
return ConeClassLikeTypeImpl(
ConeClassLikeLookupTagImpl(functionalTypeId),
receiverAndParameterTypes.toTypedArray(),
@@ -16,6 +16,7 @@ sealed class CallKind(vararg resolutionSequence: ResolutionStage) {
CollectTypeVariableUsagesInfo,
CheckDispatchReceiver,
CheckExtensionReceiver,
CheckContextReceivers,
CheckDslScopeViolation,
CheckLowPriorityInOverloadResolution,
PostponedVariablesInitializerResolutionStage,
@@ -43,6 +44,7 @@ sealed class CallKind(vararg resolutionSequence: ResolutionStage) {
CollectTypeVariableUsagesInfo,
CheckDispatchReceiver,
CheckExtensionReceiver,
CheckContextReceivers,
CheckDslScopeViolation,
CheckArguments,
CheckCallModifiers,
@@ -113,6 +115,7 @@ class ResolutionSequenceBuilder(
var mapTypeArguments: Boolean = false,
var resolveCallableReferenceArguments: Boolean = false,
var checkCallableReferenceExpectedType: Boolean = false,
val checkContextReceivers: Boolean = false,
) {
fun build(): CallKind {
val stages = mutableListOf<ResolutionStage>().apply {
@@ -125,6 +128,7 @@ class ResolutionSequenceBuilder(
if (checkDispatchReceiver) add(CheckDispatchReceiver)
if (checkExtensionReceiver) add(CheckExtensionReceiver)
if (checkArguments) add(CheckArguments)
if (checkContextReceivers) add(CheckContextReceivers)
if (resolveCallableReferenceArguments) add(EagerResolveOfCallableReferences)
if (checkLowPriorityInOverloadResolution) add(CheckLowPriorityInOverloadResolution)
if (initializePostponedVariables) add(PostponedVariablesInitializerResolutionStage)
@@ -28,9 +28,11 @@ import org.jetbrains.kotlin.resolve.calls.tower.isSuccess
class Candidate(
override val symbol: FirBasedSymbol<*>,
override val dispatchReceiverValue: ReceiverValue?,
override val extensionReceiverValue: ReceiverValue?,
// In most cases, it contains zero or single element
// More than one, only in case of context receiver group
val givenExtensionReceiverOptions: List<ReceiverValue>,
override val explicitReceiverKind: ExplicitReceiverKind,
val constraintSystemFactory: InferenceComponents.ConstraintSystemFactory,
private val constraintSystemFactory: InferenceComponents.ConstraintSystemFactory,
private val baseSystem: ConstraintStorage,
override val callInfo: CallInfo,
val originScope: FirScope?,
@@ -73,6 +75,10 @@ class Candidate(
var currentApplicability = CandidateApplicability.RESOLVED
private set
override var chosenExtensionReceiverValue: ReceiverValue? = givenExtensionReceiverOptions.singleOrNull()
var contextReceiverArguments: List<FirExpression>? = null
override val applicability: CandidateApplicability
get() = currentApplicability
@@ -95,8 +101,8 @@ class Candidate(
fun dispatchReceiverExpression(): FirExpression =
dispatchReceiverValue?.receiverExpression?.takeIf { it !is FirExpressionStub } ?: FirNoReceiverExpression
fun extensionReceiverExpression(): FirExpression =
extensionReceiverValue?.receiverExpression?.takeIf { it !is FirExpressionStub } ?: FirNoReceiverExpression
fun chosenExtensionReceiverExpression(): FirExpression =
chosenExtensionReceiverValue?.receiverExpression?.takeIf { it !is FirExpressionStub } ?: FirNoReceiverExpression
var hasVisibleBackingField = false
@@ -49,19 +49,20 @@ class CandidateFactory private constructor(
explicitReceiverKind: ExplicitReceiverKind,
scope: FirScope?,
dispatchReceiverValue: ReceiverValue? = null,
extensionReceiverValue: ReceiverValue? = null,
givenExtensionReceiverOptions: List<ReceiverValue> = emptyList(),
objectsByName: Boolean = false
): Candidate {
@Suppress("NAME_SHADOWING")
val symbol = symbol.unwrapIntegerOperatorSymbolIfNeeded(callInfo)
val result = Candidate(
symbol, dispatchReceiverValue, extensionReceiverValue,
symbol, dispatchReceiverValue, givenExtensionReceiverOptions,
explicitReceiverKind, context.inferenceComponents.constraintSystemFactory, baseSystem,
callInfo,
scope,
isFromCompanionObjectTypeScope = when (explicitReceiverKind) {
ExplicitReceiverKind.EXTENSION_RECEIVER -> extensionReceiverValue.isCandidateFromCompanionObjectTypeScope()
ExplicitReceiverKind.EXTENSION_RECEIVER ->
givenExtensionReceiverOptions.singleOrNull().isCandidateFromCompanionObjectTypeScope()
ExplicitReceiverKind.DISPATCH_RECEIVER -> dispatchReceiverValue.isCandidateFromCompanionObjectTypeScope()
// The following cases are not applicable for companion objects.
ExplicitReceiverKind.NO_EXPLICIT_RECEIVER, ExplicitReceiverKind.BOTH_RECEIVERS -> false
@@ -121,7 +122,7 @@ class CandidateFactory private constructor(
return Candidate(
symbol,
dispatchReceiverValue = null,
extensionReceiverValue = null,
givenExtensionReceiverOptions = emptyList(),
explicitReceiverKind = ExplicitReceiverKind.NO_EXPLICIT_RECEIVER,
context.inferenceComponents.constraintSystemFactory,
baseSystem,
@@ -34,6 +34,8 @@ import org.jetbrains.kotlin.fir.visibilityChecker
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.calls.inference.isSubtypeConstraintCompatible
import org.jetbrains.kotlin.resolve.calls.inference.model.SimpleConstraintSystemConstraintPosition
import org.jetbrains.kotlin.resolve.calls.tasks.ExplicitReceiverKind.*
import org.jetbrains.kotlin.resolve.calls.tower.CandidateApplicability
import org.jetbrains.kotlin.resolve.deprecation.DeprecationLevelValue
@@ -76,18 +78,43 @@ internal object CheckExplicitReceiverConsistency : ResolutionStage() {
object CheckExtensionReceiver : ResolutionStage() {
override suspend fun check(candidate: Candidate, callInfo: CallInfo, sink: CheckerSink, context: ResolutionContext) {
val expectedReceiverType = candidate.getReceiverType(context) ?: return
val argumentExtensionReceiverValue = candidate.extensionReceiverValue ?: return
val expectedType = candidate.substitutor.substituteOrSelf(expectedReceiverType.type)
val argumentType = captureFromTypeParameterUpperBoundIfNeeded(
argumentType = argumentExtensionReceiverValue.type,
expectedType = expectedType,
session = context.session
)
// Probably, we should add an assertion here since we check consistency on the level of scope tower levels
if (candidate.givenExtensionReceiverOptions.isEmpty()) return
val preparedReceivers = candidate.givenExtensionReceiverOptions.map {
candidate.prepareReceivers(it, expectedType, context)
}
if (preparedReceivers.size == 1) {
resolveExtensionReceiver(preparedReceivers, candidate, expectedType, sink, context)
return
}
val successfulReceivers = preparedReceivers.filter {
candidate.system.isSubtypeConstraintCompatible(it.type, expectedType, SimpleConstraintSystemConstraintPosition)
}
when (successfulReceivers.size) {
0 -> sink.yieldDiagnostic(InapplicableWrongReceiver())
1 -> resolveExtensionReceiver(successfulReceivers, candidate, expectedType, sink, context)
else -> sink.yieldDiagnostic(MultipleContextReceiversApplicableForExtensionReceivers())
}
}
private suspend fun resolveExtensionReceiver(
receivers: List<ReceiverDescription>,
candidate: Candidate,
expectedType: ConeKotlinType,
sink: CheckerSink,
context: ResolutionContext
) {
val receiver = receivers.single()
candidate.resolvePlainArgumentType(
candidate.csBuilder,
argumentExtensionReceiverValue.receiverExpression,
argumentType = argumentType,
receiver.expression,
argumentType = receiver.type,
expectedType = expectedType,
sink = sink,
context = context,
@@ -109,6 +136,25 @@ object CheckExtensionReceiver : ResolutionStage() {
}
}
private fun Candidate.prepareReceivers(
argumentExtensionReceiverValue: ReceiverValue,
expectedType: ConeKotlinType,
context: ResolutionContext,
): ReceiverDescription {
val argumentType = captureFromTypeParameterUpperBoundIfNeeded(
argumentType = argumentExtensionReceiverValue.type,
expectedType = expectedType,
session = context.session
).let { prepareCapturedType(it, context) }
return ReceiverDescription(argumentExtensionReceiverValue.receiverExpression, argumentType)
}
private class ReceiverDescription(
val expression: FirExpression,
val type: ConeKotlinType,
)
object CheckDispatchReceiver : ResolutionStage() {
@OptIn(SymbolInternals::class)
override suspend fun check(candidate: Candidate, callInfo: CallInfo, sink: CheckerSink, context: ResolutionContext) {
@@ -156,6 +202,58 @@ object CheckDispatchReceiver : ResolutionStage() {
}
}
object CheckContextReceivers : ResolutionStage() {
override suspend fun check(candidate: Candidate, callInfo: CallInfo, sink: CheckerSink, context: ResolutionContext) {
val contextReceiverExpectedTypes = (candidate.symbol as? FirCallableSymbol<*>)?.fir?.contextReceivers?.map {
candidate.substitutor.substituteOrSelf(it.typeRef.coneType)
}?.takeUnless { it.isEmpty() } ?: return
val receiverGroups: List<List<ImplicitReceiverValue<*>>> =
context.bodyResolveContext.towerDataContext.towerDataElements.mapNotNull { towerDataElement ->
towerDataElement.implicitReceiver?.let(::listOf) ?: towerDataElement.contextReceiverGroup
}
val resultingContextReceiverArguments = mutableListOf<FirExpression>()
for (expectedType in contextReceiverExpectedTypes) {
val matchingReceivers = candidate.findClosestMatchingReceivers(expectedType, receiverGroups, context)
when (matchingReceivers.size) {
0 -> {
sink.reportDiagnostic(NoApplicableValueForContextReceiver(expectedType))
return
}
1 -> {
val matchingReceiver = matchingReceivers.single()
resultingContextReceiverArguments.add(matchingReceiver.expression)
candidate.system.addSubtypeConstraint(matchingReceiver.type, expectedType, SimpleConstraintSystemConstraintPosition)
}
else -> {
sink.reportDiagnostic(AmbiguousValuesForContextReceiverParameter(expectedType))
return
}
}
}
candidate.contextReceiverArguments = resultingContextReceiverArguments
}
}
private fun Candidate.findClosestMatchingReceivers(
expectedType: ConeKotlinType,
receiverGroups: List<List<ImplicitReceiverValue<*>>>,
context: ResolutionContext,
): List<ReceiverDescription> {
for (receiverGroup in receiverGroups) {
val currentResult =
receiverGroup
.map { prepareReceivers(it, expectedType, context) }
.filter { system.isSubtypeConstraintCompatible(it.type, expectedType, SimpleConstraintSystemConstraintPosition) }
if (currentResult.isNotEmpty()) return currentResult
}
return emptyList()
}
/**
* See https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-dsl-marker/ for more details and
* /compiler/testData/diagnostics/tests/resolve/dslMarker for the test files.
@@ -176,7 +274,7 @@ object CheckDslScopeViolation : ResolutionStage() {
}
}
checkReceiverValue(candidate.dispatchReceiverValue)
checkReceiverValue(candidate.extensionReceiverValue)
checkReceiverValue(candidate.chosenExtensionReceiverValue)
// For value of builtin functional type with implicit extension receiver, the receiver is passed as the first argument rather than
// an extension receiver of the `invoke` call. Hence, we need to specially handle this case.
@@ -162,7 +162,7 @@ internal class FirInvokeResolveTowerExtension(
continue
}
val extensionReceiverExpression = invokeReceiverCandidate.extensionReceiverExpression()
val extensionReceiverExpression = invokeReceiverCandidate.chosenExtensionReceiverExpression()
val useImplicitReceiverAsBuiltinInvokeArgument =
!invokeBuiltinExtensionMode && isExtensionFunctionType &&
invokeReceiverCandidate.explicitReceiverKind == ExplicitReceiverKind.NO_EXPLICIT_RECEIVER
@@ -391,6 +391,13 @@ private class InvokeFunctionResolveTask(
info, group.Member,
ExplicitReceiverKind.EXTENSION_RECEIVER
)
},
onContextReceiverGroup = { contextReceiverGroup, towerGroup ->
processLevelForRegularInvoke(
contextReceiverGroup.toMemberScopeTowerLevel(extensionReceiver = invokeReceiverValue),
info, towerGroup.Member,
ExplicitReceiverKind.EXTENSION_RECEIVER
)
}
)
}
@@ -5,6 +5,7 @@
package org.jetbrains.kotlin.fir.resolve.calls.tower
import org.jetbrains.kotlin.fir.declarations.ContextReceiverGroup
import org.jetbrains.kotlin.fir.declarations.FirTowerDataContext
import org.jetbrains.kotlin.fir.expressions.FirExpression
import org.jetbrains.kotlin.fir.expressions.FirQualifiedAccessExpression
@@ -47,6 +48,12 @@ internal class TowerDataElementsForName(
}
}
val contextReceiverGroups by lazy(LazyThreadSafetyMode.NONE) {
nonLocalTowerDataElements.mapIndexedNotNull { index, towerDataElement ->
towerDataElement.contextReceiverGroup?.let { receiver -> IndexedValue(index, receiver) }
}
}
val emptyScopes = mutableSetOf<FirScope>()
val implicitReceiverValuesWithEmptyScopes = mutableSetOf<ImplicitReceiverValue<*>>()
}
@@ -81,22 +88,34 @@ internal abstract class FirBaseTowerResolveTask(
extensionReceiver: ReceiverValue? = null,
withHideMembersOnly: Boolean = false,
includeInnerConstructors: Boolean = extensionReceiver != null,
contextReceiverGroup: ContextReceiverGroup? = null,
): ScopeTowerLevel = ScopeTowerLevel(
components, this,
extensionReceiver, withHideMembersOnly, includeInnerConstructors
givenExtensionReceiverOptions = contextReceiverGroup ?: listOfNotNull(extensionReceiver),
withHideMembersOnly, includeInnerConstructors
)
protected fun ReceiverValue.toMemberScopeTowerLevel(
extensionReceiver: ReceiverValue? = null
extensionReceiver: ReceiverValue? = null,
contextReceiverGroup: ContextReceiverGroup? = null,
) = MemberScopeTowerLevel(
components, this,
extensionReceiver,
givenExtensionReceiverOptions = contextReceiverGroup ?: listOfNotNull(extensionReceiver),
)
protected fun ContextReceiverGroup.toMemberScopeTowerLevel(
extensionReceiver: ReceiverValue? = null,
otherContextReceiverGroup: ContextReceiverGroup? = null,
) = ContextReceiverGroupMemberScopeTowerLevel(
components, this,
givenExtensionReceiverOptions = otherContextReceiverGroup ?: listOfNotNull(extensionReceiver),
)
protected inline fun enumerateTowerLevels(
parentGroup: TowerGroup = TowerGroup.EmptyRoot,
onScope: (FirScope, TowerGroup) -> Unit,
onImplicitReceiver: (ImplicitReceiverValue<*>, TowerGroup) -> Unit,
onContextReceiverGroup: (ContextReceiverGroup, TowerGroup) -> Unit,
) {
for ((index, localScope) in towerDataElementsForName.reversedFilteredLocalScopes) {
onScope(localScope, parentGroup.Local(index))
@@ -118,6 +137,10 @@ internal abstract class FirBaseTowerResolveTask(
onImplicitReceiver(receiver, parentGroup.Implicit(depth))
}
}
for ((depth, contextReceiverGroup) in towerDataElementsForName.contextReceiverGroups) {
onContextReceiverGroup(contextReceiverGroup, parentGroup.ContextReceiverGroup(depth))
}
}
/**
@@ -243,7 +266,17 @@ internal open class FirTowerResolveTask(
)
},
onImplicitReceiver = { implicitReceiverValue, group ->
processCombinationOfReceivers(implicitReceiverValue, explicitReceiverValue, info, group)
// Member extensions
processLevel(
implicitReceiverValue.toMemberScopeTowerLevel(extensionReceiver = explicitReceiverValue),
info, group.Member, ExplicitReceiverKind.EXTENSION_RECEIVER
)
},
onContextReceiverGroup = { contextReceiverGroup, towerGroup ->
processLevel(
contextReceiverGroup.toMemberScopeTowerLevel(extensionReceiver = explicitReceiverValue),
info, towerGroup, ExplicitReceiverKind.EXTENSION_RECEIVER,
)
}
)
}
@@ -277,7 +310,13 @@ internal open class FirTowerResolveTask(
implicitReceiverValuesWithEmptyScopes,
emptyScopes
)
}
},
onContextReceiverGroup = { contextReceiverGroup, towerGroup ->
processCandidatesWithGivenContextReceiverGroup(
contextReceiverGroup,
info, towerGroup,
)
},
)
}
@@ -308,6 +347,7 @@ internal open class FirTowerResolveTask(
ExplicitReceiverKind.EXTENSION_RECEIVER, parentGroup
)
} else {
// context?
for ((depth, implicitReceiverValue) in towerDataElementsForName.implicitReceivers) {
processHideMembersLevel(
implicitReceiverValue, topLevelScope, info, index, depth,
@@ -331,19 +371,6 @@ internal open class FirTowerResolveTask(
}
private suspend fun processCombinationOfReceivers(
implicitReceiverValue: ImplicitReceiverValue<*>,
explicitReceiverValue: ExpressionReceiverValue,
info: CallInfo,
parentGroup: TowerGroup
) {
// Member extensions
processLevel(
implicitReceiverValue.toMemberScopeTowerLevel(extensionReceiver = explicitReceiverValue),
info, parentGroup.Member, ExplicitReceiverKind.EXTENSION_RECEIVER
)
}
private suspend fun processCandidatesWithGivenImplicitReceiverAsValue(
receiver: ImplicitReceiverValue<*>,
info: CallInfo,
@@ -377,11 +404,49 @@ internal open class FirTowerResolveTask(
implicitReceiverValue.toMemberScopeTowerLevel(extensionReceiver = receiver),
info, group
)
}
},
onContextReceiverGroup = { contextReceiverGroup, towerGroup ->
processLevel(
contextReceiverGroup.toMemberScopeTowerLevel(extensionReceiver = receiver),
info, towerGroup
)
},
)
}
private suspend fun processCandidatesWithGivenContextReceiverGroup(
contextReceiverGroup: ContextReceiverGroup,
info: CallInfo,
parentGroup: TowerGroup,
) {
processLevel(
contextReceiverGroup.toMemberScopeTowerLevel(), info, parentGroup.Member,
)
enumerateTowerLevels(
parentGroup,
onScope = { scope, towerGroup ->
processLevel(
scope.toScopeTowerLevel(contextReceiverGroup = contextReceiverGroup),
info, towerGroup,
)
},
onImplicitReceiver = { implicitReceiverValue, towerGroup ->
processLevel(
implicitReceiverValue.toMemberScopeTowerLevel(contextReceiverGroup = contextReceiverGroup),
info, towerGroup
)
},
onContextReceiverGroup = { otherContextReceiverGroup, towerGroup ->
processLevel(
contextReceiverGroup.toMemberScopeTowerLevel(otherContextReceiverGroup = otherContextReceiverGroup),
info, towerGroup,
)
}
)
}
private suspend fun processHideMembersLevel(
receiverValue: ReceiverValue,
topLevelScope: FirScope,
@@ -116,7 +116,7 @@ class FirTowerResolver(
ExplicitReceiverKind.NO_EXPLICIT_RECEIVER,
scope,
dispatchReceiver,
extensionReceiverValue = null
givenExtensionReceiverOptions = emptyList()
),
context
)
@@ -40,9 +40,11 @@ sealed class TowerGroupKind(val index: Byte) : Comparable<TowerGroupKind> {
class ImplicitOrNonLocal(depth: Int, val kindForDebugSake: String) : WithDepth(6, depth)
object InvokeExtension : TowerGroupKind(7)
class ContextReceiverGroup(depth: Int) : WithDepth(7, depth)
object QualifierValue : TowerGroupKind(8)
object InvokeExtension : TowerGroupKind(8)
object QualifierValue : TowerGroupKind(9)
class UnqualifiedEnum(depth: Int) : WithDepth(9, depth)
@@ -166,6 +168,8 @@ private constructor(
fun Implicit(depth: Int) = kindOf(TowerGroupKind.Implicit(depth))
fun NonLocal(depth: Int) = kindOf(TowerGroupKind.NonLocal(depth))
fun ContextReceiverGroup(depth: Int) = kindOf(TowerGroupKind.ContextReceiverGroup(depth))
fun TopPrioritized(depth: Int) = kindOf(TowerGroupKind.TopPrioritized(depth))
val Last = kindOf(TowerGroupKind.Last)
@@ -180,6 +184,8 @@ private constructor(
fun Implicit(depth: Int) = kindOf(TowerGroupKind.Implicit(depth))
fun NonLocal(depth: Int) = kindOf(TowerGroupKind.NonLocal(depth))
fun ContextReceiverGroup(depth: Int) = kindOf(TowerGroupKind.ContextReceiverGroup(depth))
val InvokeExtension get() = kindOf(TowerGroupKind.InvokeExtension)
fun TopPrioritized(depth: Int) = kindOf(TowerGroupKind.TopPrioritized(depth))
@@ -45,7 +45,7 @@ internal class TowerLevelHandler {
CallKind.VariableAccess -> {
processResult += towerLevel.processPropertiesByName(info, processor)
if (!collector.isSuccess() && towerLevel is ScopeTowerLevel && towerLevel.extensionReceiver == null) {
if (!collector.isSuccess() && towerLevel is ScopeTowerLevel && !towerLevel.areThereExtensionReceiverOptions()) {
processResult += towerLevel.processObjectsByName(info, processor)
}
}
@@ -74,7 +74,7 @@ private class TowerScopeLevelProcessor(
override fun consumeCandidate(
symbol: FirBasedSymbol<*>,
dispatchReceiverValue: ReceiverValue?,
extensionReceiverValue: ReceiverValue?,
givenExtensionReceiverOptions: List<ReceiverValue>,
scope: FirScope,
objectsByName: Boolean
) {
@@ -85,7 +85,7 @@ private class TowerScopeLevelProcessor(
explicitReceiverKind,
scope,
dispatchReceiverValue,
extensionReceiverValue,
givenExtensionReceiverOptions,
objectsByName
), candidateFactory.context
)
@@ -6,6 +6,7 @@
package org.jetbrains.kotlin.fir.resolve.calls.tower
import org.jetbrains.kotlin.fir.*
import org.jetbrains.kotlin.fir.declarations.ContextReceiverGroup
import org.jetbrains.kotlin.fir.declarations.FirConstructor
import org.jetbrains.kotlin.fir.declarations.getAnnotationByClassId
import org.jetbrains.kotlin.fir.declarations.utils.isInner
@@ -56,7 +57,7 @@ abstract class TowerScopeLevel {
fun consumeCandidate(
symbol: T,
dispatchReceiverValue: ReceiverValue?,
extensionReceiverValue: ReceiverValue?,
givenExtensionReceiverOptions: List<ReceiverValue>,
scope: FirScope,
objectsByName: Boolean = false
)
@@ -73,7 +74,7 @@ abstract class TowerScopeLevel {
class MemberScopeTowerLevel(
private val bodyResolveComponents: BodyResolveComponents,
val dispatchReceiverValue: ReceiverValue,
private val extensionReceiver: ReceiverValue? = null,
private val givenExtensionReceiverOptions: List<ReceiverValue>,
) : TowerScopeLevel() {
private val scopeSession: ScopeSession get() = bodyResolveComponents.scopeSession
private val session: FirSession get() = bodyResolveComponents.session
@@ -86,11 +87,11 @@ class MemberScopeTowerLevel(
var (empty, candidates) = scope.collectCandidates(processScopeMembers)
consumeCandidates(output, candidates.map { scope to it })
if (extensionReceiver == null) {
if (givenExtensionReceiverOptions.isEmpty()) {
val withSynthetic = FirSyntheticPropertiesScope(session, scope)
withSynthetic.processScopeMembers { symbol ->
empty = false
output.consumeCandidate(symbol, dispatchReceiverValue, null, scope)
output.consumeCandidate(symbol, dispatchReceiverValue, givenExtensionReceiverOptions = emptyList(), scope)
}
}
return if (empty) ProcessResult.SCOPE_EMPTY else ProcessResult.FOUND
@@ -103,7 +104,7 @@ class MemberScopeTowerLevel(
val result = mutableListOf<T>()
processScopeMembers { candidate ->
empty = false
if (candidate is FirCallableSymbol<*> && candidate.hasConsistentExtensionReceiver(extensionReceiver)) {
if (candidate is FirCallableSymbol<*> && candidate.hasConsistentExtensionReceiver(givenExtensionReceiverOptions)) {
val fir = candidate.fir
if ((fir as? FirConstructor)?.isInner == false) {
return@processScopeMembers
@@ -121,14 +122,14 @@ class MemberScopeTowerLevel(
candidatesWithScope: List<Pair<FirScope, T>>
) {
for ((scope, candidate) in candidatesWithScope) {
if (candidate is FirCallableSymbol<*> && candidate.hasConsistentExtensionReceiver(extensionReceiver)) {
if (candidate is FirCallableSymbol<*> && candidate.hasConsistentExtensionReceiver(givenExtensionReceiverOptions)) {
output.consumeCandidate(
candidate, dispatchReceiverValue,
extensionReceiverValue = extensionReceiver,
givenExtensionReceiverOptions,
scope
)
} else if (candidate is FirClassLikeSymbol<*>) {
output.consumeCandidate(candidate, null, extensionReceiver, scope)
output.consumeCandidate(candidate, null, givenExtensionReceiverOptions, scope)
}
}
}
@@ -201,8 +202,30 @@ class MemberScopeTowerLevel(
}
}
private fun FirCallableSymbol<*>.hasConsistentExtensionReceiver(extensionReceiver: Receiver?): Boolean {
return (extensionReceiver != null) == hasExtensionReceiver()
private fun FirCallableSymbol<*>.hasConsistentExtensionReceiver(givenExtensionReceivers: List<ReceiverValue>): Boolean {
return givenExtensionReceivers.isNotEmpty() == hasExtensionReceiver()
}
}
class ContextReceiverGroupMemberScopeTowerLevel(
bodyResolveComponents: BodyResolveComponents,
contextReceiverGroup: ContextReceiverGroup,
givenExtensionReceiverOptions: List<ReceiverValue> = emptyList(),
) : TowerScopeLevel() {
private val memberScopeLevels = contextReceiverGroup.map {
MemberScopeTowerLevel(bodyResolveComponents, it, givenExtensionReceiverOptions)
}
override fun processFunctionsByName(info: CallInfo, processor: TowerScopeLevelProcessor<FirFunctionSymbol<*>>): ProcessResult {
return memberScopeLevels.minOf { it.processFunctionsByName(info, processor) }
}
override fun processPropertiesByName(info: CallInfo, processor: TowerScopeLevelProcessor<FirVariableSymbol<*>>): ProcessResult {
return memberScopeLevels.minOf { it.processPropertiesByName(info, processor) }
}
override fun processObjectsByName(info: CallInfo, processor: TowerScopeLevelProcessor<FirBasedSymbol<*>>): ProcessResult {
return memberScopeLevels.minOf { it.processObjectsByName(info, processor) }
}
}
@@ -216,12 +239,14 @@ class MemberScopeTowerLevel(
class ScopeTowerLevel(
private val bodyResolveComponents: BodyResolveComponents,
val scope: FirScope,
val extensionReceiver: ReceiverValue?,
private val givenExtensionReceiverOptions: List<ReceiverValue>,
private val withHideMembersOnly: Boolean,
private val includeInnerConstructors: Boolean
) : TowerScopeLevel() {
private val session: FirSession get() = bodyResolveComponents.session
fun areThereExtensionReceiverOptions(): Boolean = givenExtensionReceiverOptions.isNotEmpty()
private fun dispatchReceiverValue(candidate: FirCallableSymbol<*>): ReceiverValue? {
candidate.fir.importedFromObjectData?.let { data ->
val objectClassId = data.objectClassId
@@ -257,26 +282,25 @@ class ScopeTowerLevel(
private fun shouldSkipCandidateWithInconsistentExtensionReceiver(candidate: FirCallableSymbol<*>): Boolean {
// Pre-check explicit extension receiver for default package top-level members
if (scope is FirDefaultStarImportingScope && extensionReceiver != null) {
if (scope !is FirDefaultStarImportingScope || !areThereExtensionReceiverOptions()) return false
val declarationReceiverType = candidate.resolvedReceiverTypeRef?.coneType as? ConeClassLikeType ?: return false
val startProjectedDeclarationReceiverType = declarationReceiverType.lookupTag.constructClassType(
declarationReceiverType.typeArguments.map { ConeStarProjection }.toTypedArray(),
isNullable = true
)
return givenExtensionReceiverOptions.none { extensionReceiver ->
val extensionReceiverType = extensionReceiver.type
if (extensionReceiverType is ConeClassLikeType) {
val declarationReceiverType = candidate.resolvedReceiverTypeRef?.coneType
if (declarationReceiverType is ConeClassLikeType) {
if (!AbstractTypeChecker.isSubtypeOf(
session.typeContext,
extensionReceiverType,
declarationReceiverType.lookupTag.constructClassType(
declarationReceiverType.typeArguments.map { ConeStarProjection }.toTypedArray(),
isNullable = true
)
)
) {
return true
}
}
}
// If some receiver is non class like, we should not skip it
if (extensionReceiverType !is ConeClassLikeType) return@none true
AbstractTypeChecker.isSubtypeOf(
session.typeContext,
extensionReceiverType,
startProjectedDeclarationReceiverType
)
}
return false
}
private fun <T : FirBasedSymbol<*>> consumeCallableCandidate(
@@ -287,7 +311,7 @@ class ScopeTowerLevel(
if (withHideMembersOnly && candidate.getAnnotationByClassId(HidesMembers) == null) {
return
}
val receiverExpected = withHideMembersOnly || extensionReceiver != null
val receiverExpected = withHideMembersOnly || areThereExtensionReceiverOptions()
if (candidateReceiverTypeRef == null == receiverExpected) return
val dispatchReceiverValue = dispatchReceiverValue(candidate)
if (dispatchReceiverValue == null && shouldSkipCandidateWithInconsistentExtensionReceiver(candidate)) {
@@ -297,7 +321,7 @@ class ScopeTowerLevel(
@Suppress("UNCHECKED_CAST")
processor.consumeCandidate(
unwrappedCandidate as T, dispatchReceiverValue,
extensionReceiverValue = extensionReceiver,
givenExtensionReceiverOptions,
scope
)
}
@@ -343,7 +367,7 @@ class ScopeTowerLevel(
empty = false
processor.consumeCandidate(
it, dispatchReceiverValue = null,
extensionReceiverValue = null,
givenExtensionReceiverOptions = emptyList(),
scope = scope,
objectsByName = true
)
@@ -64,7 +64,7 @@ class FirBuilderInferenceSession(
}
private fun Candidate.isSuitableForBuilderInference(): Boolean {
val extensionReceiver = extensionReceiverValue
val extensionReceiver = chosenExtensionReceiverValue
val dispatchReceiver = dispatchReceiverValue
return when {
extensionReceiver == null && dispatchReceiver == null -> false
@@ -11,6 +11,7 @@ import org.jetbrains.kotlin.fir.*
import org.jetbrains.kotlin.fir.declarations.FirAnonymousFunction
import org.jetbrains.kotlin.fir.declarations.FirDeclarationOrigin
import org.jetbrains.kotlin.fir.declarations.FirFunction
import org.jetbrains.kotlin.fir.declarations.builder.buildContextReceiver
import org.jetbrains.kotlin.fir.declarations.builder.buildValueParameter
import org.jetbrains.kotlin.fir.expressions.*
import org.jetbrains.kotlin.fir.resolve.ResolutionMode
@@ -28,6 +29,7 @@ import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.resultType
import org.jetbrains.kotlin.fir.resolve.typeFromCallee
import org.jetbrains.kotlin.fir.symbols.impl.FirValueParameterSymbol
import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.fir.types.builder.buildResolvedTypeRef
import org.jetbrains.kotlin.fir.types.impl.ConeClassLikeTypeImpl
import org.jetbrains.kotlin.fir.visitors.transformSingle
import org.jetbrains.kotlin.name.Name
@@ -259,6 +261,7 @@ class FirCallCompleter(
override fun analyzeAndGetLambdaReturnArguments(
lambdaAtom: ResolvedLambdaAtom,
receiverType: ConeKotlinType?,
contextReceivers: List<ConeKotlinType>,
parameters: List<ConeKotlinType>,
expectedReturnType: ConeKotlinType?,
stubsForPostponedVariables: Map<TypeVariableMarker, StubTypeMarker>,
@@ -307,6 +310,18 @@ class FirCallCompleter(
}
)
if (contextReceivers.isNotEmpty()) {
lambdaArgument.replaceContextReceivers(
contextReceivers.map { contextReceiverType ->
buildContextReceiver {
typeRef = buildResolvedTypeRef {
type = contextReceiverType
}
}
}
)
}
val lookupTracker = session.lookupTracker
val fileSource = components.file.source
lambdaArgument.valueParameters.forEachIndexed { index, parameter ->
@@ -19,7 +19,6 @@ import org.jetbrains.kotlin.resolve.calls.inference.buildAbstractResultingSubsti
import org.jetbrains.kotlin.resolve.calls.inference.components.ConstraintSystemCompletionContext
import org.jetbrains.kotlin.resolve.calls.inference.components.ConstraintSystemCompletionMode
import org.jetbrains.kotlin.resolve.calls.inference.model.BuilderInferencePosition
import org.jetbrains.kotlin.resolve.calls.inference.model.ConstraintKind
import org.jetbrains.kotlin.resolve.calls.inference.model.ConstraintStorage
import org.jetbrains.kotlin.resolve.calls.inference.model.NewConstraintSystemImpl
import org.jetbrains.kotlin.resolve.calls.inference.registerTypeVariableIfNotPresent
@@ -74,7 +73,7 @@ class FirDelegatedPropertyInferenceSession(
if (callee.candidate.system.hasContradiction) return true
val hasStubType =
callee.candidate.extensionReceiverValue?.type?.containsStubType() ?: false
callee.candidate.chosenExtensionReceiverValue?.type?.containsStubType() ?: false
|| callee.candidate.dispatchReceiverValue?.type?.containsStubType() ?: false
if (!hasStubType) {
@@ -51,10 +51,16 @@ fun extractLambdaInfoFromFunctionalType(
// For lambdas, the existence of the receiver is always implied by the expected type, and a value parameter
// can never fill its role.
val receiverType = if (argument.isLambda) expectedType.receiverType(session) else argument.receiverType
val contextReceiversNumber =
if (argument.isLambda) expectedType.contextReceiversNumberForFunctionType else argument.contextReceivers.size
val valueParametersTypesIncludingReceiver = expectedType.valueParameterTypesIncludingReceiver(session)
val isExtensionFunctionType = expectedType.isExtensionFunctionType(session)
val expectedParameters = valueParametersTypesIncludingReceiver.let {
if (receiverType != null && isExtensionFunctionType) it.drop(1) else it
val forExtension = if (receiverType != null && isExtensionFunctionType) 1 else 0
val toDrop = forExtension + contextReceiversNumber
if (toDrop > 0) it.drop(toDrop) else it
}
var coerceFirstParameterToExtensionReceiver = false
@@ -84,11 +90,19 @@ fun extractLambdaInfoFromFunctionalType(
}
}
val contextReceivers =
when {
contextReceiversNumber == 0 -> emptyList()
argument.isLambda -> valueParametersTypesIncludingReceiver.subList(0, contextReceiversNumber)
else -> argument.contextReceivers.map { it.typeRef.coneType }
}
return ResolvedLambdaAtom(
argument,
expectedType,
expectedType.isSuspendFunctionType(session),
receiverType,
contextReceivers,
parameters,
returnType,
typeVariableForLambdaReturnType = returnTypeVariable,
@@ -66,7 +66,8 @@ fun Candidate.preprocessLambdaArgument(
if (resolvedArgument.coerceFirstParameterToExtensionReceiver) parameters.drop(1) else parameters,
resolvedArgument.receiver,
resolvedArgument.returnType,
isSuspend = resolvedArgument.isSuspend
isSuspend = resolvedArgument.isSuspend,
contextReceivers = resolvedArgument.contextReceivers,
)
val position = ConeArgumentConstraintPosition(resolvedArgument.atom)
@@ -124,6 +125,10 @@ private fun extractLambdaInfo(
it.returnTypeRef.coneTypeSafe<ConeKotlinType>() ?: nothingType
}
val contextReceivers = argument.contextReceivers.map {
it.typeRef.coneTypeSafe<ConeKotlinType>() ?: nothingType
}
val newTypeVariableUsed = returnType == typeVariable.defaultType
if (newTypeVariableUsed) csBuilder.registerVariable(typeVariable)
@@ -132,6 +137,7 @@ private fun extractLambdaInfo(
expectedType,
isSuspend,
receiverType,
contextReceivers,
parameters,
returnType,
typeVariable.takeIf { newTypeVariableUsed },
@@ -37,6 +37,7 @@ interface LambdaAnalyzer {
fun analyzeAndGetLambdaReturnArguments(
lambdaAtom: ResolvedLambdaAtom,
receiverType: ConeKotlinType?,
contextReceivers: List<ConeKotlinType>,
parameters: List<ConeKotlinType>,
expectedReturnType: ConeKotlinType?, // null means, that return type is not proper i.e. it depends on some type variables
stubsForPostponedVariables: Map<TypeVariableMarker, StubTypeMarker>,
@@ -122,6 +123,7 @@ class PostponedArgumentsAnalyzer(
fun substitute(type: ConeKotlinType) = currentSubstitutor.safeSubstitute(c, type) as ConeKotlinType
val receiver = lambda.receiver?.let(::substitute)
val contextReceivers = lambda.contextReceivers.map(::substitute)
val parameters = lambda.parameters.map(::substitute)
val rawReturnType = lambda.returnType
@@ -137,6 +139,7 @@ class PostponedArgumentsAnalyzer(
val results = lambdaAnalyzer.analyzeAndGetLambdaReturnArguments(
lambda,
receiver,
contextReceivers,
parameters,
expectedTypeForReturnArguments,
stubsForPostponedVariables,
@@ -41,6 +41,7 @@ class ResolvedLambdaAtom(
expectedType: ConeKotlinType?,
val isSuspend: Boolean,
val receiver: ConeKotlinType?,
val contextReceivers: List<ConeKotlinType>,
val parameters: List<ConeKotlinType>,
var returnType: ConeKotlinType,
typeVariableForLambdaReturnType: ConeTypeVariableForLambdaReturnType?,
@@ -467,8 +467,8 @@ class FirTypeResolverImpl(private val session: FirSession) : FirTypeResolver() {
private fun createFunctionalType(typeRef: FirFunctionTypeRef): ConeClassLikeType {
val parameters =
listOfNotNull(typeRef.receiverTypeRef?.coneType) +
typeRef.contextReceiverTypeRefs.map { it.coneType } +
typeRef.contextReceiverTypeRefs.map { it.coneType } +
listOfNotNull(typeRef.receiverTypeRef?.coneType) +
typeRef.valueParameters.map { it.returnTypeRef.coneType.withParameterNameAnnotation(it) } +
listOf(typeRef.returnTypeRef.coneType)
val classId = if (typeRef.isSuspend) {
@@ -550,7 +550,7 @@ class FirTypeResolverImpl(private val session: FirSession) : FirTypeResolver() {
override val dispatchReceiverValue: ReceiverValue?
get() = null
override val extensionReceiverValue: ReceiverValue?
override val chosenExtensionReceiverValue: ReceiverValue?
get() = null
override val explicitReceiverKind: ExplicitReceiverKind
@@ -131,7 +131,7 @@ class FirCallCompletionResultsWriterTransformer(
}
var dispatchReceiver = subCandidate.dispatchReceiverExpression()
var extensionReceiver = subCandidate.extensionReceiverExpression()
var extensionReceiver = subCandidate.chosenExtensionReceiverExpression()
if (!declaration.isWrappedIntegerOperator()) {
val expectedDispatchReceiverType = (declaration as? FirCallableDeclaration)?.dispatchReceiverType
val expectedExtensionReceiverType = (declaration as? FirCallableDeclaration)?.receiverTypeRef?.coneType
@@ -403,7 +403,7 @@ class FirCallCompletionResultsWriterTransformer(
StoreCalleeReference,
resolvedReference,
).transformDispatchReceiver(StoreReceiver, subCandidate.dispatchReceiverExpression())
.transformExtensionReceiver(StoreReceiver, subCandidate.extensionReceiverExpression())
.transformExtensionReceiver(StoreReceiver, subCandidate.chosenExtensionReceiverExpression())
}
override fun transformVariableAssignment(
@@ -13,7 +13,6 @@ import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.declarations.impl.FirDefaultPropertyAccessor
import org.jetbrains.kotlin.fir.declarations.utils.isCompanion
import org.jetbrains.kotlin.fir.declarations.utils.isInner
import org.jetbrains.kotlin.fir.declarations.primaryConstructorIfAny
import org.jetbrains.kotlin.fir.expressions.FirCallableReferenceAccess
import org.jetbrains.kotlin.fir.expressions.FirWhenExpression
import org.jetbrains.kotlin.fir.resolve.*
@@ -228,6 +227,8 @@ class BodyResolveContext(
holder: SessionHolder,
f: () -> T
): T = withTowerDataCleanup {
replaceTowerDataContext(towerDataContext.addContextReceiverGroup(owner.createContextReceiverValues(holder)))
if (type != null) {
val receiver = ImplicitExtensionReceiverValue(
owner.symbol,
@@ -450,6 +451,7 @@ class BodyResolveContext(
val forMembersResolution =
staticsAndCompanion
.addReceiver(labelName, towerElementsForClass.thisReceiver)
.addContextReceiverGroup(towerElementsForClass.contextReceivers)
.addNonLocalScopeIfNotNull(typeParameterScope)
val scopeForConstructorHeader =
@@ -12,6 +12,7 @@ import org.jetbrains.kotlin.fakeElement
import org.jetbrains.kotlin.fir.*
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.declarations.builder.buildAnonymousFunctionCopy
import org.jetbrains.kotlin.fir.declarations.builder.buildContextReceiver
import org.jetbrains.kotlin.fir.declarations.builder.buildValueParameter
import org.jetbrains.kotlin.fir.declarations.impl.FirDeclarationStatusImpl
import org.jetbrains.kotlin.fir.declarations.impl.FirDefaultPropertyAccessor
@@ -34,7 +35,6 @@ import org.jetbrains.kotlin.fir.resolve.diagnostics.ConeLocalVariableNoTypeOrIni
import org.jetbrains.kotlin.fir.resolve.inference.FirStubTypeTransformer
import org.jetbrains.kotlin.fir.resolve.inference.ResolvedLambdaAtom
import org.jetbrains.kotlin.fir.resolve.inference.extractLambdaInfoFromFunctionalType
import org.jetbrains.kotlin.fir.types.isSuspendFunctionType
import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor
import org.jetbrains.kotlin.fir.resolve.substitution.createTypeSubstitutorByTypeConstructor
import org.jetbrains.kotlin.fir.resolve.transformers.*
@@ -809,6 +809,19 @@ open class FirDeclarationsResolveTransformer(transformer: FirBodyResolveTransfor
lambda = buildAnonymousFunctionCopy(lambda) {
receiverTypeRef = lambda.receiverTypeRef?.takeIf { it !is FirImplicitTypeRef }
?: resolvedLambdaAtom?.receiver?.let { lambda.receiverTypeRef?.resolvedTypeFromPrototype(it) }
contextReceivers.clear()
contextReceivers.addAll(
lambda.contextReceivers.takeIf { it.isNotEmpty() }
?: resolvedLambdaAtom?.contextReceivers?.map { receiverType ->
buildContextReceiver {
this.typeRef = buildResolvedTypeRef {
type = receiverType
}
}
}.orEmpty()
)
this.valueParameters.clear()
this.valueParameters.addAll(valueParameters)
returnTypeRef = (lambda.returnTypeRef as? FirResolvedTypeRef)