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
@@ -8,14 +8,14 @@ package org.jetbrains.kotlin.fir.declarations
import kotlinx.collections.immutable.PersistentList
import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.toPersistentList
import org.jetbrains.kotlin.fir.labelName
import org.jetbrains.kotlin.fir.resolve.*
import org.jetbrains.kotlin.fir.resolve.calls.ImplicitDispatchReceiverValue
import org.jetbrains.kotlin.fir.resolve.calls.ImplicitExtensionReceiverValue
import org.jetbrains.kotlin.fir.resolve.calls.ImplicitReceiverValue
import org.jetbrains.kotlin.fir.resolve.calls.*
import org.jetbrains.kotlin.fir.scopes.FirScope
import org.jetbrains.kotlin.fir.scopes.impl.FirLocalScope
import org.jetbrains.kotlin.fir.scopes.impl.wrapNestedClassifierScopeWithSubstitutionForSuperType
import org.jetbrains.kotlin.fir.types.ConeKotlinType
import org.jetbrains.kotlin.fir.types.coneType
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.utils.addIfNotNull
@@ -23,32 +23,39 @@ fun SessionHolder.collectImplicitReceivers(
type: ConeKotlinType?,
owner: FirDeclaration
): ImplicitReceivers {
if (type == null) return ImplicitReceivers(null, emptyList())
val implicitCompanionValues = mutableListOf<ImplicitReceiverValue<*>>()
val contextReceiverValues = mutableListOf<ContextReceiverValue<*>>()
val implicitReceiverValue = when (owner) {
is FirClass -> {
val towerElementsForClass = collectTowerDataElementsForClass(owner, type)
val towerElementsForClass = collectTowerDataElementsForClass(owner, type!!)
implicitCompanionValues.addAll(towerElementsForClass.implicitCompanionValues)
contextReceiverValues.addAll(towerElementsForClass.contextReceivers)
towerElementsForClass.thisReceiver
}
is FirFunction -> {
ImplicitExtensionReceiverValue(owner.symbol, type, session, scopeSession)
contextReceiverValues.addAll(owner.createContextReceiverValues(this))
type?.let { ImplicitExtensionReceiverValue(owner.symbol, type, session, scopeSession) }
}
is FirVariable -> {
ImplicitExtensionReceiverValue(owner.symbol, type, session, scopeSession)
contextReceiverValues.addAll(owner.createContextReceiverValues(this))
type?.let { ImplicitExtensionReceiverValue(owner.symbol, type, session, scopeSession) }
}
else -> {
throw IllegalArgumentException("Incorrect label & receiver owner: ${owner.javaClass}")
if (type != null) {
throw IllegalArgumentException("Incorrect label & receiver owner: ${owner.javaClass}")
}
null
}
}
return ImplicitReceivers(implicitReceiverValue, implicitCompanionValues)
return ImplicitReceivers(implicitReceiverValue, implicitCompanionValues, contextReceiverValues)
}
data class ImplicitReceivers(
val implicitReceiverValue: ImplicitReceiverValue<*>?,
val implicitCompanionValues: List<ImplicitReceiverValue<*>>
val implicitCompanionValues: List<ImplicitReceiverValue<*>>,
val contextReceivers: List<ContextReceiverValue<*>>,
)
fun SessionHolder.collectTowerDataElementsForClass(owner: FirClass, defaultType: ConeKotlinType): TowerElementsForClass {
@@ -83,9 +90,15 @@ fun SessionHolder.collectTowerDataElementsForClass(owner: FirClass, defaultType:
}
val thisReceiver = ImplicitDispatchReceiverValue(owner.symbol, defaultType, session, scopeSession)
val contextReceivers = (owner as? FirRegularClass)?.contextReceivers?.map {
ContextReceiverValueForClass(
owner.symbol, it.typeRef.coneType, it.labelName, session, scopeSession,
)
}.orEmpty()
return TowerElementsForClass(
thisReceiver,
contextReceivers,
owner.staticScope(this),
companionReceiver,
companionObject?.staticScope(this),
@@ -96,6 +109,7 @@ fun SessionHolder.collectTowerDataElementsForClass(owner: FirClass, defaultType:
class TowerElementsForClass(
val thisReceiver: ImplicitReceiverValue<*>,
val contextReceivers: List<ContextReceiverValueForClass>,
val staticScope: FirScope?,
val companionReceiver: ImplicitReceiverValue<*>?,
val companionStaticScope: FirScope?,
@@ -137,7 +151,9 @@ class FirTowerDataContext private constructor(
fun addNonLocalTowerDataElements(newElements: List<FirTowerDataElement>): FirTowerDataContext {
return FirTowerDataContext(
towerDataElements.addAll(newElements),
implicitReceiverStack.addAll(newElements.mapNotNull { it.implicitReceiver }),
implicitReceiverStack
.addAll(newElements.mapNotNull { it.implicitReceiver })
.addAllContextReceivers(newElements.flatMap { it.contextReceiverGroup.orEmpty() }),
localScopes,
nonLocalTowerDataElements.addAll(newElements)
)
@@ -162,6 +178,18 @@ class FirTowerDataContext private constructor(
)
}
fun addContextReceiverGroup(contextReceiverGroup: ContextReceiverGroup): FirTowerDataContext {
if (contextReceiverGroup.isEmpty()) return this
val element = contextReceiverGroup.asTowerDataElement()
return FirTowerDataContext(
towerDataElements.add(element),
contextReceiverGroup.fold(implicitReceiverStack, PersistentImplicitReceiverStack::addContextReceiver),
localScopes,
nonLocalTowerDataElements.add(element)
)
}
fun addNonLocalScopeIfNotNull(scope: FirScope?): FirTowerDataContext {
if (scope == null) return this
return addNonLocalScope(scope)
@@ -179,23 +207,47 @@ class FirTowerDataContext private constructor(
fun createSnapshot(): FirTowerDataContext {
return FirTowerDataContext(
towerDataElements.map { FirTowerDataElement(it.scope, it.implicitReceiver?.createSnapshot(), it.isLocal) }.toPersistentList(),
towerDataElements.map(FirTowerDataElement::createSnapshot).toPersistentList(),
implicitReceiverStack.createSnapshot(),
localScopes.toPersistentList(),
nonLocalTowerDataElements.map { FirTowerDataElement(it.scope, it.implicitReceiver?.createSnapshot(), it.isLocal) }
.toPersistentList()
nonLocalTowerDataElements.map(FirTowerDataElement::createSnapshot).toPersistentList()
)
}
}
class FirTowerDataElement(val scope: FirScope?, val implicitReceiver: ImplicitReceiverValue<*>?, val isLocal: Boolean)
class FirTowerDataElement(
val scope: FirScope?,
val implicitReceiver: ImplicitReceiverValue<*>?,
val contextReceiverGroup: ContextReceiverGroup? = null,
val isLocal: Boolean,
) {
fun createSnapshot(): FirTowerDataElement =
FirTowerDataElement(
scope,
implicitReceiver?.createSnapshot(),
contextReceiverGroup?.map { it.createSnapshot() },
isLocal,
)
}
fun ImplicitReceiverValue<*>.asTowerDataElement(): FirTowerDataElement =
FirTowerDataElement(scope = null, this, isLocal = false)
FirTowerDataElement(scope = null, implicitReceiver = this, isLocal = false)
fun ContextReceiverGroup.asTowerDataElement(): FirTowerDataElement =
FirTowerDataElement(scope = null, implicitReceiver = null, contextReceiverGroup = this, isLocal = false)
fun FirScope.asTowerDataElement(isLocal: Boolean): FirTowerDataElement =
FirTowerDataElement(this, implicitReceiver = null, isLocal)
FirTowerDataElement(scope = this, implicitReceiver = null, isLocal = isLocal)
fun FirClass.staticScope(sessionHolder: SessionHolder) =
scopeProvider.getStaticScope(this, sessionHolder.session, sessionHolder.scopeSession)
typealias ContextReceiverGroup = List<ContextReceiverValue<*>>
typealias FirLocalScopes = PersistentList<FirLocalScope>
fun FirCallableDeclaration.createContextReceiverValues(
sessionHolder: SessionHolder,
): List<ContextReceiverValueForCallable> =
contextReceivers.map {
ContextReceiverValueForCallable(symbol, it.typeRef.coneType, it.labelName, sessionHolder.session, sessionHolder.scopeSession)
}
@@ -6,6 +6,7 @@
package org.jetbrains.kotlin.fir.resolve
import kotlinx.collections.immutable.*
import org.jetbrains.kotlin.fir.resolve.calls.ContextReceiverValue
import org.jetbrains.kotlin.fir.resolve.calls.ImplicitDispatchReceiverValue
import org.jetbrains.kotlin.fir.resolve.calls.ImplicitReceiverValue
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
@@ -16,7 +17,7 @@ import org.jetbrains.kotlin.name.Name
class PersistentImplicitReceiverStack private constructor(
private val stack: PersistentList<ImplicitReceiverValue<*>>,
// This multi-map holds indexes of the stack ^
private val indexesPerLabel: PersistentSetMultimap<Name, Int>,
private val receiversPerLabel: PersistentSetMultimap<Name, ImplicitReceiverValue<*>>,
private val indexesPerSymbol: PersistentMap<FirBasedSymbol<*>, Int>,
private val originalTypes: PersistentList<ConeKotlinType>,
) : ImplicitReceiverStack(), Iterable<ImplicitReceiverValue<*>> {
@@ -33,16 +34,42 @@ class PersistentImplicitReceiverStack private constructor(
return receivers.fold(this) { acc, value -> acc.add(name = null, value) }
}
fun add(name: Name?, value: ImplicitReceiverValue<*>): PersistentImplicitReceiverStack {
fun addAllContextReceivers(receivers: List<ContextReceiverValue<*>>): PersistentImplicitReceiverStack {
return receivers.fold(this) { acc, value -> acc.addContextReceiver(value) }
}
fun add(name: Name?, value: ImplicitReceiverValue<*>, aliasLabel: Name? = null): PersistentImplicitReceiverStack {
val stack = stack.add(value)
val originalTypes = originalTypes.add(value.originalType)
val index = stack.size - 1
val indexesPerLabel = name?.let { indexesPerLabel.put(it, index) } ?: indexesPerLabel
val receiversPerLabel = name?.let { receiversPerLabel.put(it, value) } ?: receiversPerLabel
val indexesPerSymbol = indexesPerSymbol.put(value.boundSymbol, index)
return PersistentImplicitReceiverStack(
stack,
indexesPerLabel,
receiversPerLabel,
indexesPerSymbol,
originalTypes
)
}
fun addContextReceiver(value: ContextReceiverValue<*>): PersistentImplicitReceiverStack {
val labelName = value.labelName ?: return this
val receiversPerLabel = receiversPerLabel.put(labelName, value)
return PersistentImplicitReceiverStack(
stack,
receiversPerLabel,
indexesPerSymbol,
originalTypes
)
}
fun addReceiverLabelAlias(aliasLabel: Name, value: ImplicitReceiverValue<*>): PersistentImplicitReceiverStack {
val receiversPerLabel = receiversPerLabel.put(aliasLabel, value)
return PersistentImplicitReceiverStack(
stack,
receiversPerLabel,
indexesPerSymbol,
originalTypes
)
@@ -50,7 +77,7 @@ class PersistentImplicitReceiverStack private constructor(
override operator fun get(name: String?): ImplicitReceiverValue<*>? {
if (name == null) return stack.lastOrNull()
return indexesPerLabel[Name.identifier(name)].lastOrNull()?.let { stack[it] }
return receiversPerLabel[Name.identifier(name)].lastOrNull()
}
override fun lastDispatchReceiver(): ImplicitDispatchReceiverValue? {
@@ -85,7 +112,7 @@ class PersistentImplicitReceiverStack private constructor(
fun createSnapshot(): PersistentImplicitReceiverStack {
return PersistentImplicitReceiverStack(
stack.map { it.createSnapshot() }.toPersistentList(),
indexesPerLabel,
receiversPerLabel,
indexesPerSymbol,
originalTypes
)
@@ -13,7 +13,7 @@ import org.jetbrains.kotlin.resolve.calls.tower.CandidateApplicability
abstract class AbstractCandidate {
abstract val symbol: FirBasedSymbol<*>
abstract val dispatchReceiverValue: ReceiverValue?
abstract val extensionReceiverValue: ReceiverValue?
abstract val chosenExtensionReceiverValue: ReceiverValue?
abstract val explicitReceiverKind: ExplicitReceiverKind
abstract val callInfo: AbstractCallInfo
abstract val diagnostics: List<ResolutionDiagnostic>
@@ -82,7 +82,8 @@ object LowerPriorityToPreserveCompatibilityDiagnostic : ResolutionDiagnostic(RES
object CandidateChosenUsingOverloadResolutionByLambdaAnnotation : ResolutionDiagnostic(RESOLVED)
class UnstableSmartCast(val argument: FirExpressionWithSmartcast, val targetType: ConeKotlinType, val isCastToNotNull: Boolean) : ResolutionDiagnostic(UNSTABLE_SMARTCAST)
class UnstableSmartCast(val argument: FirExpressionWithSmartcast, val targetType: ConeKotlinType, val isCastToNotNull: Boolean) :
ResolutionDiagnostic(UNSTABLE_SMARTCAST)
class ArgumentTypeMismatch(
val expectedType: ConeKotlinType,
@@ -107,4 +108,14 @@ class Unsupported(val message: String, val source: KtSourceElement? = null) : Re
object PropertyAsOperator : ResolutionDiagnostic(PROPERTY_AS_OPERATOR)
class DslScopeViolation(val calleeSymbol: FirBasedSymbol<*>) : ResolutionDiagnostic(DSL_SCOPE_VIOLATION)
class DslScopeViolation(val calleeSymbol: FirBasedSymbol<*>) : ResolutionDiagnostic(DSL_SCOPE_VIOLATION)
class MultipleContextReceiversApplicableForExtensionReceivers : ResolutionDiagnostic(INAPPLICABLE)
class NoApplicableValueForContextReceiver(
val expectedContextReceiverType: ConeKotlinType
) : ResolutionDiagnostic(INAPPLICABLE)
class AmbiguousValuesForContextReceiverParameter(
val expectedContextReceiverType: ConeKotlinType,
) : ResolutionDiagnostic(INAPPLICABLE)