K2: add dispatchReceiverForStatics to ScopeTowerLevel to avoid receiver hacks

Related to KT-55116
This commit is contained in:
Mikhail Glukhikh
2023-01-20 18:07:50 +01:00
committed by Space Team
parent d436d56871
commit 8aed6d3c4c
8 changed files with 84 additions and 64 deletions
@@ -27,6 +27,10 @@ import org.jetbrains.kotlin.resolve.calls.tower.isSuccess
class Candidate(
override val symbol: FirBasedSymbol<*>,
// Here we may have an ExpressionReceiverValue
// - in case a use-site receiver is explicit
// - in some cases with static entities, no matter is a use-site receiver explicit or not
// OR we may have here a kind of ImplicitReceiverValue (non-statics only)
override var dispatchReceiverValue: ReceiverValue?,
// In most cases, it contains zero or single element
// More than one, only in case of context receiver group
@@ -10,7 +10,6 @@ import org.jetbrains.kotlin.fir.declarations.FirClassLikeDeclaration
import org.jetbrains.kotlin.fir.declarations.FirRegularClass
import org.jetbrains.kotlin.fir.declarations.FirTypeAlias
import org.jetbrains.kotlin.fir.declarations.utils.expandedConeType
import org.jetbrains.kotlin.fir.expressions.FirExpression
import org.jetbrains.kotlin.fir.expressions.FirResolvedQualifier
import org.jetbrains.kotlin.fir.resolve.ScopeSession
import org.jetbrains.kotlin.fir.resolve.toSymbol
@@ -42,7 +41,9 @@ fun createQualifierReceiver(
}
}
abstract class QualifierReceiver(final override val explicitReceiver: FirExpression) : AbstractExplicitReceiver<FirResolvedQualifier>() {
abstract class QualifierReceiver(
final override val explicitReceiver: FirResolvedQualifier
) : AbstractExplicitReceiver<FirResolvedQualifier>() {
abstract fun classifierScope(): FirScope?
abstract fun callableScope(): FirScope?
}
@@ -382,7 +382,7 @@ private class InvokeFunctionResolveTask(
)
enumerateTowerLevels(
onScope = { scope, group ->
onScope = { scope, _, group ->
processLevelForRegularInvoke(
scope.toScopeTowerLevel(extensionReceiver = invokeReceiverValue),
info, group,
@@ -5,17 +5,22 @@
package org.jetbrains.kotlin.fir.resolve.calls.tower
import org.jetbrains.kotlin.KtFakeSourceElementKind
import org.jetbrains.kotlin.KtSourceElement
import org.jetbrains.kotlin.fakeElement
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
import org.jetbrains.kotlin.fir.expressions.FirResolvedQualifier
import org.jetbrains.kotlin.fir.expressions.builder.buildExpressionStub
import org.jetbrains.kotlin.fir.resolve.BodyResolveComponents
import org.jetbrains.kotlin.fir.resolve.DoubleColonLHS
import org.jetbrains.kotlin.fir.expressions.builder.buildResolvedQualifier
import org.jetbrains.kotlin.fir.resolve.*
import org.jetbrains.kotlin.fir.resolve.calls.*
import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.resultType
import org.jetbrains.kotlin.fir.scopes.FirScope
import org.jetbrains.kotlin.fir.scopes.impl.FirWhenSubjectImportingScope
import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol
import org.jetbrains.kotlin.fir.types.builder.buildResolvedTypeRef
import org.jetbrains.kotlin.fir.types.impl.FirImplicitBuiltinTypeRef
import org.jetbrains.kotlin.fir.util.asReversedFrozen
@@ -52,9 +57,6 @@ internal class TowerDataElementsForName(
towerDataElement.contextReceiverGroup?.let { receiver -> IndexedValue(index, receiver) }
}
}
val emptyScopes = mutableSetOf<FirScope>()
val implicitReceiverValuesWithEmptyScopes = mutableSetOf<ImplicitReceiverValue<*>>()
}
internal abstract class FirBaseTowerResolveTask(
@@ -88,10 +90,32 @@ internal abstract class FirBaseTowerResolveTask(
withHideMembersOnly: Boolean = false,
includeInnerConstructors: Boolean = extensionReceiver != null,
contextReceiverGroup: ContextReceiverGroup? = null,
dispatchReceiverForStatics: ExpressionReceiverValue? = null
): ScopeTowerLevel = ScopeTowerLevel(
components, this,
givenExtensionReceiverOptions = contextReceiverGroup ?: listOfNotNull(extensionReceiver),
withHideMembersOnly, includeInnerConstructors
withHideMembersOnly, includeInnerConstructors, dispatchReceiverForStatics
)
protected fun FirScope.toScopeTowerLevelForStaticWithImplicitDispatchReceiver(
staticOwnerOwnerSymbol: FirRegularClassSymbol? = null,
source: KtSourceElement? = null
): ScopeTowerLevel = toScopeTowerLevel(
extensionReceiver = null,
withHideMembersOnly = false,
includeInnerConstructors = false,
contextReceiverGroup = null,
staticOwnerOwnerSymbol?.let {
val resolvedQualifier = buildResolvedQualifier {
packageFqName = it.classId.packageFqName
relativeClassFqName = it.classId.relativeClassName
this.symbol = it
this.source = source?.fakeElement(KtFakeSourceElementKind.ImplicitReceiver)
}.apply {
resultType = components.typeForQualifier(this)
}
ExpressionReceiverValue(resolvedQualifier)
}
)
protected fun ReceiverValue.toMemberScopeTowerLevel(
@@ -112,12 +136,12 @@ internal abstract class FirBaseTowerResolveTask(
protected inline fun enumerateTowerLevels(
parentGroup: TowerGroup = TowerGroup.EmptyRoot,
onScope: (FirScope, TowerGroup) -> Unit,
onScope: (FirScope, FirRegularClassSymbol?, TowerGroup) -> Unit,
onImplicitReceiver: (ImplicitReceiverValue<*>, TowerGroup) -> Unit,
onContextReceiverGroup: (ContextReceiverGroup, TowerGroup) -> Unit,
) {
for ((index, localScope) in towerDataElementsForName.reversedFilteredLocalScopes) {
onScope(localScope, parentGroup.Local(index))
onScope(localScope, null, parentGroup.Local(index))
}
for ((depth, lexical) in towerDataElementsForName.nonLocalTowerDataElements.withIndex()) {
@@ -125,6 +149,7 @@ internal abstract class FirBaseTowerResolveTask(
if (!lexical.isLocal && scope != null) {
onScope(
scope,
lexical.staticScopeOwnerSymbol,
if (scope is FirWhenSubjectImportingScope) TowerGroup.UnqualifiedEnum(depth)
else parentGroup.NonLocal(depth)
)
@@ -220,7 +245,13 @@ internal open class FirTowerResolveTask(
if (qualifierReceiver == null) return
val callableScope = qualifierReceiver.callableScope() ?: return
processLevel(
callableScope.toScopeTowerLevel(includeInnerConstructors = false),
callableScope.toScopeTowerLevel(
includeInnerConstructors = false,
dispatchReceiverForStatics = when (qualifierReceiver) {
is ClassQualifierReceiver -> ExpressionReceiverValue(qualifierReceiver.explicitReceiver)
else -> null
}
),
info, TowerGroup.Qualifier
)
}
@@ -256,7 +287,7 @@ internal open class FirTowerResolveTask(
enumerateTowerLevels(
parentGroup = parentGroup,
onScope = { scope, group ->
onScope = { scope, _, group ->
processScopeForExplicitReceiver(
scope,
explicitReceiverValue,
@@ -289,13 +320,16 @@ internal open class FirTowerResolveTask(
val implicitReceiverValuesWithEmptyScopes = mutableSetOf<ImplicitReceiverValue<*>>()
enumerateTowerLevels(
onScope = l@{ scope, group ->
onScope = l@{ scope, staticScopeOwnerSymbol, group ->
// NB: this check does not work for variables
// because we do not search for objects if we have extension receiver
if (info.callKind != CallKind.VariableAccess && scope in emptyScopes) return@l
processLevel(
scope.toScopeTowerLevel(), info, group,
scope.toScopeTowerLevelForStaticWithImplicitDispatchReceiver(
staticScopeOwnerSymbol, source = info.callSite.source
),
info, group,
onEmptyLevel = {
emptyScopes += scope
}
@@ -386,7 +420,7 @@ internal open class FirTowerResolveTask(
enumerateTowerLevels(
parentGroup,
onScope = l@{ scope, group ->
onScope = l@{ scope, _, group ->
if (scope in emptyScopes) return@l
processLevel(
@@ -425,7 +459,7 @@ internal open class FirTowerResolveTask(
enumerateTowerLevels(
parentGroup,
onScope = { scope, towerGroup ->
onScope = { scope, _, towerGroup ->
processLevel(
scope.toScopeTowerLevel(contextReceiverGroup = contextReceiverGroup),
info, towerGroup,
@@ -7,7 +7,6 @@ package org.jetbrains.kotlin.fir.resolve.calls.tower
import org.jetbrains.kotlin.KtFakeSourceElementKind
import org.jetbrains.kotlin.KtSourceElement
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.fakeElement
import org.jetbrains.kotlin.fir.*
import org.jetbrains.kotlin.fir.declarations.ContextReceiverGroup
@@ -15,7 +14,6 @@ import org.jetbrains.kotlin.fir.declarations.FirConstructor
import org.jetbrains.kotlin.fir.declarations.getAnnotationByClassId
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.declarations.utils.isInner
import org.jetbrains.kotlin.fir.expressions.FirResolvedQualifier
import org.jetbrains.kotlin.fir.expressions.FirSmartCastExpression
import org.jetbrains.kotlin.fir.expressions.builder.buildResolvedQualifier
import org.jetbrains.kotlin.fir.resolve.*
@@ -326,7 +324,8 @@ class ScopeTowerLevel(
val scope: FirScope,
private val givenExtensionReceiverOptions: List<ReceiverValue>,
private val withHideMembersOnly: Boolean,
private val includeInnerConstructors: Boolean
private val includeInnerConstructors: Boolean,
private val dispatchReceiverForStatics: ExpressionReceiverValue?
) : TowerScopeLevel() {
private val session: FirSession get() = bodyResolveComponents.session
@@ -344,22 +343,7 @@ class ScopeTowerLevel(
return ExpressionReceiverValue(resolvedQualifier)
}
private fun FirRegularClassSymbol.findJavaSuperClassSymbol(): FirRegularClassSymbol? {
var currentSymbol = this
while (currentSymbol.origin !is FirDeclarationOrigin.Java) {
var baseClassSymbol: FirRegularClassSymbol? = null
for (superType in currentSymbol.resolvedSuperTypes) {
val symbol = superType.fullyExpandedType(session).toSymbol(session)
if (symbol is FirRegularClassSymbol && symbol.classKind == ClassKind.CLASS) {
baseClassSymbol = symbol
break
}
}
currentSymbol = baseClassSymbol ?: return null
}
return currentSymbol
}
// For static entries we may return here FirResolvedQualifier, wrapped in ExpressionReceiverValue
private fun dispatchReceiverValue(candidate: FirCallableSymbol<*>, callInfo: CallInfo): ReceiverValue? {
candidate.fir.importedFromObjectOrStaticData?.let { data ->
val objectClassId = data.objectClassId
@@ -369,36 +353,25 @@ class ScopeTowerLevel(
}
}
if (candidate is FirBackingFieldSymbol) {
val lookupTag = candidate.fir.propertySymbol.dispatchReceiverClassLookupTagOrNull()
return when {
lookupTag != null -> {
bodyResolveComponents.implicitReceiverStack.lastDispatchReceiver { implicitReceiverValue ->
(implicitReceiverValue.type as? ConeClassLikeType)?.fullyExpandedType(session)?.lookupTag == lookupTag
when {
candidate is FirBackingFieldSymbol -> {
val lookupTag = candidate.fir.propertySymbol.dispatchReceiverClassLookupTagOrNull()
return when {
lookupTag != null -> {
bodyResolveComponents.implicitReceiverStack.lastDispatchReceiver { implicitReceiverValue ->
(implicitReceiverValue.type as? ConeClassLikeType)?.fullyExpandedType(session)?.lookupTag == lookupTag
}
}
else -> {
bodyResolveComponents.implicitReceiverStack.lastDispatchReceiver()
}
}
else -> {
bodyResolveComponents.implicitReceiverStack.lastDispatchReceiver()
}
}
}
if (candidate.isStatic && candidate.isJavaOrEnhancement) {
val explicitReceiver = callInfo.explicitReceiver
if (explicitReceiver is FirResolvedQualifier) {
return ExpressionReceiverValue(explicitReceiver)
candidate.isStatic -> {
return dispatchReceiverForStatics
}
val lookupTag = candidate.fir.containingClassLookupTag() ?: return null
val implicitDispatchReceiverValue = bodyResolveComponents.implicitReceiverStack.lastDispatchReceiver { implicitReceiverValue ->
implicitReceiverValue is ImplicitDispatchReceiverValue && implicitReceiverValue.boundSymbol.fir.isSubclassOf(
lookupTag, session, isStrict = false, lookupInterfaces = false
)
} ?: return null
// Note: we know that candidate is from Java, so normally we should find this class and its Java superclass
val regularClassSymbol = implicitDispatchReceiverValue.boundSymbol as? FirRegularClassSymbol ?: return null
return regularClassSymbol.findJavaSuperClassSymbol()?.toResolvedQualifierExpressionReceiver(callInfo.callSite.source)
else -> return null
}
return null
}
private fun shouldSkipCandidateWithInconsistentExtensionReceiver(candidate: FirCallableSymbol<*>): Boolean {
@@ -16,6 +16,7 @@ import org.jetbrains.kotlin.fir.scopes.FirContainingNamesAwareScope
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.symbols.impl.FirRegularClassSymbol
import org.jetbrains.kotlin.fir.types.ConeKotlinType
import org.jetbrains.kotlin.fir.types.coneType
import org.jetbrains.kotlin.name.Name
@@ -78,7 +79,7 @@ fun SessionHolder.collectTowerDataElementsForClass(owner: FirClass, defaultType:
superClass.staticScope(this)
?.wrapNestedClassifierScopeWithSubstitutionForSuperType(expandedType, session)
?.asTowerDataElement(isLocal = false)
?.asTowerDataElementForStaticScope(staticScopeOwnerSymbol = superClass.symbol)
?.let(superClassesStaticsAndCompanionReceivers::add)
(superClass as? FirRegularClass)?.companionObjectSymbol?.let {
@@ -223,6 +224,7 @@ class FirTowerDataElement(
val implicitReceiver: ImplicitReceiverValue<*>?,
val contextReceiverGroup: ContextReceiverGroup? = null,
val isLocal: Boolean,
val staticScopeOwnerSymbol: FirRegularClassSymbol? = null
) {
fun createSnapshot(): FirTowerDataElement =
FirTowerDataElement(
@@ -230,6 +232,7 @@ class FirTowerDataElement(
implicitReceiver?.createSnapshot(),
contextReceiverGroup?.map { it.createSnapshot() },
isLocal,
staticScopeOwnerSymbol
)
}
@@ -242,6 +245,9 @@ fun ContextReceiverGroup.asTowerDataElement(): FirTowerDataElement =
fun FirScope.asTowerDataElement(isLocal: Boolean): FirTowerDataElement =
FirTowerDataElement(scope = this, implicitReceiver = null, isLocal = isLocal)
fun FirScope.asTowerDataElementForStaticScope(staticScopeOwnerSymbol: FirRegularClassSymbol?): FirTowerDataElement =
FirTowerDataElement(scope = this, implicitReceiver = null, isLocal = false, staticScopeOwnerSymbol = staticScopeOwnerSymbol)
fun FirClass.staticScope(sessionHolder: SessionHolder): FirContainingNamesAwareScope? =
staticScope(sessionHolder.session, sessionHolder.scopeSession)
@@ -150,7 +150,9 @@ fun FirRegularClassBuilder.generateEntriesGetter(
moduleData, FirDeclarationOrigin.Source, returnTypeRef.copyWithNewSourceKind(KtFakeSourceElementKind.EnumGeneratedDeclaration),
Visibilities.Public, symbol
).apply {
(status as FirDeclarationStatusImpl).isStatic = true
this.status = createStatus(this@generateEntriesGetter.status).apply {
isStatic = true
}
}
}.apply {
containingClassForStaticMemberAttr = this@generateEntriesGetter.symbol.toLookupTag()
@@ -14,7 +14,7 @@ FILE: Child.kt
}
public final fun foo(): R|kotlin/Unit| {
lval x: R|kotlin/String| = R|javapackage/PackagePrivateGrandparentInterface.publicStaticField|
lval x: R|kotlin/String| = Q|javapackage/PublicParentClass|.R|javapackage/PackagePrivateGrandparentInterface.publicStaticField|
lval y: R|kotlin/String| = Q|javapackage/PublicParentClass|.R|javapackage/PackagePrivateGrandparentInterface.publicStaticField|
lval z: <ERROR TYPE REF: Unresolved name: publicStaticField> = Q|javapackage/KotlinParentClass|.<Unresolved name: publicStaticField>#
}