[FIR] Add effective visibility to resolved status

This commit includes:
- introduce FirResolvedDeclarationStatus.effectiveVisibility
- replace FirEffectiveVisibility with EffectiveVisibility
- move calculation of effective visibility from FirEffectiveVisibilityResolver
  (which is deleted) to FirStatusResolver and FirDeserializer
This commit is contained in:
Dmitriy Novozhilov
2021-04-02 14:57:30 +03:00
committed by TeamCityServer
parent a600d18396
commit 3ef87d0265
33 changed files with 327 additions and 679 deletions
@@ -6,6 +6,7 @@
package org.jetbrains.kotlin.fir
import org.jetbrains.kotlin.contracts.description.EventOccurrencesRange
import org.jetbrains.kotlin.descriptors.EffectiveVisibility
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.descriptors.Visibility
import org.jetbrains.kotlin.fir.declarations.*
@@ -171,12 +172,17 @@ fun FirDeclarationStatus.copy(
isExpect: Boolean = this.isExpect,
newModality: Modality? = null,
newVisibility: Visibility? = null,
newEffectiveVisibility: EffectiveVisibility? = null
): FirDeclarationStatus {
return if (this.isExpect == isExpect && newModality == null && newVisibility == null) {
this
} else {
require(this is FirDeclarationStatusImpl) { "Unexpected class ${this::class}" }
this.resolved(newVisibility ?: visibility, newModality ?: modality!!).apply {
this.resolved(
newVisibility ?: visibility,
newModality ?: modality!!,
newEffectiveVisibility ?: EffectiveVisibility.Public
).apply {
this.isExpect = isExpect
}
}
@@ -0,0 +1,37 @@
/*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.fir
import org.jetbrains.kotlin.descriptors.EffectiveVisibility
import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.descriptors.Visibility
import org.jetbrains.kotlin.fir.symbols.ConeClassLikeLookupTag
import org.jetbrains.kotlin.fir.symbols.impl.FirClassLikeSymbol
fun Visibility.toEffectiveVisibility(
ownerSymbol: FirClassLikeSymbol<*>?,
checkPublishedApi: Boolean = false
): EffectiveVisibility {
return toEffectiveVisibility(ownerSymbol?.toLookupTag(), checkPublishedApi)
}
fun Visibility.toEffectiveVisibility(
owner: ConeClassLikeLookupTag?,
checkPublishedApi: Boolean = false
): EffectiveVisibility {
customEffectiveVisibility()?.let { return it }
return when (this.normalize()) {
Visibilities.Private, Visibilities.PrivateToThis, Visibilities.InvisibleFake -> EffectiveVisibility.Private
Visibilities.Protected -> EffectiveVisibility.Protected(owner)
Visibilities.Internal -> when (!checkPublishedApi /*|| !owner.isPublishedApi()*/) { // TODO
true -> EffectiveVisibility.Internal
false -> EffectiveVisibility.Public
}
Visibilities.Public -> EffectiveVisibility.Public
Visibilities.Local -> EffectiveVisibility.Local
else -> error("Unknown visibility: $this")
}
}
@@ -1,234 +0,0 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.fir
import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.fir.FirEffectiveVisibility.Permissiveness
import org.jetbrains.kotlin.fir.declarations.FirDeclaration
import org.jetbrains.kotlin.fir.declarations.FirMemberDeclaration
import org.jetbrains.kotlin.fir.resolve.ScopeSession
import org.jetbrains.kotlin.fir.resolve.effectiveVisibilityResolver
import org.jetbrains.kotlin.fir.resolve.lookupSuperTypes
import org.jetbrains.kotlin.fir.symbols.impl.FirClassLikeSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirClassifierSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol
import org.jetbrains.kotlin.fir.types.classId
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
sealed class FirEffectiveVisibilityImpl(
override val name: String,
override val publicApi: Boolean = false,
override val privateApi: Boolean = false
) : FirEffectiveVisibility {
override fun toString() = name
// Public
// /--/ | \-------------\
// Protected(Base) | \
// | Protected(Other) Internal = PackagePrivate
// Protected(Derived) | / \
// | | / InternalProtected(Base)
// ProtectedBound / \
// \ / /InternalProtected(Derived)
// \InternalProtectedBound/
// |
// Private = Local
object Private : FirEffectiveVisibilityImpl("private", privateApi = true) {
override fun relation(other: FirEffectiveVisibility) =
if (this == other || Local == other) Permissiveness.SAME else Permissiveness.LESS
override fun toVisibility() = Visibilities.Private
}
// Effectively same as Private
object Local : FirEffectiveVisibilityImpl("local") {
override fun relation(other: FirEffectiveVisibility) =
if (this == other || Private == other) Permissiveness.SAME else Permissiveness.LESS
override fun toVisibility() = Visibilities.Local
}
object Public : FirEffectiveVisibilityImpl("public", publicApi = true) {
override fun relation(other: FirEffectiveVisibility) =
if (this == other) Permissiveness.SAME else Permissiveness.MORE
override fun toVisibility() = Visibilities.Public
}
abstract class InternalOrPackage protected constructor(internal: Boolean) : FirEffectiveVisibilityImpl(
if (internal) "internal" else "public/*package*/"
) {
override fun relation(other: FirEffectiveVisibility) = when (other as FirEffectiveVisibilityImpl) {
Public -> Permissiveness.LESS
Private, Local, InternalProtectedBound, is InternalProtected -> Permissiveness.MORE
is InternalOrPackage -> Permissiveness.SAME
ProtectedBound, is Protected -> Permissiveness.UNKNOWN
}
override fun lowerBound(other: FirEffectiveVisibility) = when (val o = other as FirEffectiveVisibilityImpl) {
Public -> this
Private, Local, InternalProtectedBound, is InternalOrPackage, is InternalProtected -> other
is Protected -> InternalProtected(o.containerSymbol, o.session)
ProtectedBound -> InternalProtectedBound
}
}
object Internal : InternalOrPackage(true) {
override fun toVisibility() = Visibilities.Internal
}
object PackagePrivate : InternalOrPackage(false) {
override fun toVisibility() = Visibilities.Private
}
class Protected(
val containerSymbol: FirClassLikeSymbol<*>?,
internal val session: FirSession
) : FirEffectiveVisibilityImpl("protected", publicApi = true) {
override fun equals(other: Any?) = (other is Protected && containerSymbol == other.containerSymbol)
override fun hashCode() = containerSymbol?.hashCode() ?: 0
override fun toString() = "${super.toString()} (in ${containerSymbol.safeAs<FirRegularClassSymbol>()?.fir?.name ?: '?'})"
override fun relation(other: FirEffectiveVisibility) = when (val o = other as FirEffectiveVisibilityImpl) {
Public -> Permissiveness.LESS
Private, Local, ProtectedBound, InternalProtectedBound -> Permissiveness.MORE
is Protected -> containerRelation(containerSymbol, o.containerSymbol, session)
is InternalProtected -> when (containerRelation(containerSymbol, o.containerSymbol, session)) {
// Protected never can be less permissive than internal & protected
Permissiveness.SAME, Permissiveness.MORE -> Permissiveness.MORE
Permissiveness.UNKNOWN, Permissiveness.LESS -> Permissiveness.UNKNOWN
}
is InternalOrPackage -> Permissiveness.UNKNOWN
}
override fun lowerBound(other: FirEffectiveVisibility) = when (other as FirEffectiveVisibilityImpl) {
Public -> this
Private, Local, ProtectedBound, InternalProtectedBound -> other
is Protected -> when (relation(other)) {
Permissiveness.SAME, Permissiveness.MORE -> this
Permissiveness.LESS -> other
Permissiveness.UNKNOWN -> ProtectedBound
}
is InternalProtected -> when (relation(other)) {
Permissiveness.LESS -> other
else -> InternalProtectedBound
}
is InternalOrPackage -> InternalProtected(containerSymbol, session)
}
override fun toVisibility() = Visibilities.Protected
}
// Lower bound for all protected visibilities
object ProtectedBound : FirEffectiveVisibilityImpl("protected (in different classes)", publicApi = true) {
override fun relation(other: FirEffectiveVisibility) = when (other as FirEffectiveVisibilityImpl) {
Public, is Protected -> Permissiveness.LESS
Private, Local, InternalProtectedBound -> Permissiveness.MORE
ProtectedBound -> Permissiveness.SAME
is InternalOrPackage, is InternalProtected -> Permissiveness.UNKNOWN
}
override fun lowerBound(other: FirEffectiveVisibility) = when (other as FirEffectiveVisibilityImpl) {
Public, is Protected -> this
Private, Local, ProtectedBound, InternalProtectedBound -> other
is InternalOrPackage, is InternalProtected -> InternalProtectedBound
}
override fun toVisibility() = Visibilities.Protected
}
// Lower bound for internal and protected(C)
class InternalProtected(
val containerSymbol: FirClassLikeSymbol<*>?,
private val session: FirSession
) : FirEffectiveVisibilityImpl("internal & protected") {
override fun equals(other: Any?) = (other is InternalProtected && containerSymbol == other.containerSymbol)
override fun hashCode() = containerSymbol?.hashCode() ?: 0
override fun toString() = "${super.toString()} (in ${containerSymbol.safeAs<FirRegularClassSymbol>()?.fir?.name ?: '?'})"
override fun relation(other: FirEffectiveVisibility) = when (val o = other as FirEffectiveVisibilityImpl) {
Public, is InternalOrPackage -> Permissiveness.LESS
Private, Local, InternalProtectedBound -> Permissiveness.MORE
is InternalProtected -> containerRelation(containerSymbol, o.containerSymbol, session)
is Protected -> when (containerRelation(containerSymbol, o.containerSymbol, session)) {
// Internal & protected never can be more permissive than just protected
Permissiveness.SAME, Permissiveness.LESS -> Permissiveness.LESS
Permissiveness.UNKNOWN, Permissiveness.MORE -> Permissiveness.UNKNOWN
}
ProtectedBound -> Permissiveness.UNKNOWN
}
override fun lowerBound(other: FirEffectiveVisibility) = when (other as FirEffectiveVisibilityImpl) {
Public, is InternalOrPackage -> this
Private, Local, InternalProtectedBound -> other
is Protected, is InternalProtected -> when (relation(other)) {
Permissiveness.SAME, Permissiveness.MORE -> this
Permissiveness.LESS -> other
Permissiveness.UNKNOWN -> InternalProtectedBound
}
ProtectedBound -> InternalProtectedBound
}
override fun toVisibility() = Visibilities.Private
}
// Lower bound for internal and protected lower bound
object InternalProtectedBound : FirEffectiveVisibilityImpl("internal & protected (in different classes)") {
override fun relation(other: FirEffectiveVisibility) = when (other as FirEffectiveVisibilityImpl) {
Public, is Protected, is InternalProtected, ProtectedBound, is InternalOrPackage -> Permissiveness.LESS
Private, Local -> Permissiveness.MORE
InternalProtectedBound -> Permissiveness.SAME
}
override fun toVisibility() = Visibilities.Private
}
override fun lowerBound(other: FirEffectiveVisibility): FirEffectiveVisibility {
if (this == Local || other == Local) return Local
return when (relation(other)) {
Permissiveness.SAME, Permissiveness.LESS -> this
Permissiveness.MORE -> other
Permissiveness.UNKNOWN -> Private
}
}
companion object {
private fun containerRelation(first: FirClassLikeSymbol<*>?, second: FirClassLikeSymbol<*>?, session: FirSession): Permissiveness =
if (first == null || second == null) {
Permissiveness.UNKNOWN
} else if (first == second) {
Permissiveness.SAME
} else {
when {
first.collectSupertypes(session).any { it.classId == second.classId } -> Permissiveness.LESS
second.collectSupertypes(session).any { it.classId == first.classId } -> Permissiveness.MORE
else -> Permissiveness.UNKNOWN
}
}
private fun FirClassifierSymbol<*>.collectSupertypes(session: FirSession) = lookupSuperTypes(
this,
lookupInterfaces = true,
deep = true,
useSiteSession = session
)
}
}
fun FirMemberDeclaration.getEffectiveVisibility(
session: FirSession,
containingDeclarations: List<FirDeclaration>,
scopeSession: ScopeSession
): FirEffectiveVisibility = session.effectiveVisibilityResolver.resolveFor(this, containingDeclarations, scopeSession)
@@ -1,156 +0,0 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.fir
import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.descriptors.Visibility
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.resolve.ScopeSession
import org.jetbrains.kotlin.fir.resolve.symbolProvider
import org.jetbrains.kotlin.fir.symbols.impl.FirClassLikeSymbol
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
@ThreadSafeMutableState
open class FirEffectiveVisibilityResolverImpl(private val session: FirSession) : FirEffectiveVisibilityResolver() {
private val cache = mutableMapOf<FirSourceElement, FirEffectiveVisibility>()
final override fun resolveFor(
declaration: FirMemberDeclaration,
containingDeclarations: List<FirDeclaration>?,
scopeSession: ScopeSession
): FirEffectiveVisibility {
if (declaration.source != null) {
cache[declaration.source]?.let {
return it
}
}
val (parentSymbol, parentEffectiveVisibility) =
declaration.getParentInfo(containingDeclarations, scopeSession)
var visibility = declaration.visibility
// this is a temporary solution until
// visibility starts to understand
// overrides itself.
if (declaration is FirSimpleFunction) {
parentSymbol?.fir.safeAs<FirClass<*>>()?.let {
visibility = declaration.lowestVisibilityAmongOverrides(it, session, scopeSession)
}
}
var selfEffectiveVisibility = computeEffectiveVisibility(visibility, parentSymbol)
selfEffectiveVisibility = parentEffectiveVisibility.lowerBound(selfEffectiveVisibility)
return declaration.remember(selfEffectiveVisibility)
}
protected open fun computeEffectiveVisibility(visibility: Visibility, containerSymbol: FirClassLikeSymbol<*>?): FirEffectiveVisibility {
return visibility.normalize().forVisibility(containerSymbol)
}
protected fun Visibility.forVisibility(
containerSymbol: FirClassLikeSymbol<*>?
): FirEffectiveVisibility =
when (this) {
Visibilities.Private, Visibilities.PrivateToThis, Visibilities.InvisibleFake -> FirEffectiveVisibilityImpl.Private
Visibilities.Protected -> FirEffectiveVisibilityImpl.Protected(containerSymbol, session)
Visibilities.Internal -> FirEffectiveVisibilityImpl.Internal
Visibilities.Public -> FirEffectiveVisibilityImpl.Public
Visibilities.Local -> FirEffectiveVisibilityImpl.Local
Visibilities.Unknown -> FirEffectiveVisibilityImpl.Private
// NB: visibility must be already normalized here, so e.g. no JavaVisibilities are possible at this point
else -> error("Visibility $name is not allowed in forVisibility")
}
private fun FirElement.remember(effectiveVisibility: FirEffectiveVisibility): FirEffectiveVisibility {
val source = source
if (source != null) {
cache[source] = effectiveVisibility
}
return effectiveVisibility
}
private fun FirMemberDeclaration.getParentInfo(
containingDeclarations: List<FirDeclaration>?,
scopeSession: ScopeSession
): Pair<FirClassLikeSymbol<*>?, FirEffectiveVisibility> {
var parentEffectiveVisibility: FirEffectiveVisibility = FirEffectiveVisibilityImpl.Public
// because for now effective visibility
// only works with FirClassLikeSymbol's
var parentSymbol: FirClassLikeSymbol<*>? = null
val parentClassId = this.getParentClassId()
// for some reason ClassId for "/<anonymous>"
// has local = false but still returns
// null instead of a symbol
// TODO: fix
var succeededToGetSymbol = false
// look for the containing class
// in the containingDeclarations or
// try using the firSymbolProvider
if (parentClassId != null && containingDeclarations != null) {
parentClassId.findOuterContainerInfo(containingDeclarations, scopeSession)?.let {
parentSymbol = it.first
parentEffectiveVisibility = it.second
succeededToGetSymbol = true
}
}
if (!succeededToGetSymbol) {
if (parentClassId?.isLocal == false) {
// ?: is needed to get enum from enum entry
parentSymbol = session.symbolProvider.getClassLikeSymbolByFqName(parentClassId)
?: parentClassId.outerClassId?.let { session.symbolProvider.getClassLikeSymbolByFqName(it) }
parentSymbol?.fir.safeAs<FirMemberDeclaration>()?.let {
parentEffectiveVisibility = resolveFor(it, null, scopeSession)
}
} else if (parentClassId?.isLocal == true) {
parentEffectiveVisibility = FirEffectiveVisibilityImpl.Local
}
}
return parentSymbol to parentEffectiveVisibility
}
private fun FirDeclaration.getParentClassId(): ClassId? = when (this) {
is FirCallableMemberDeclaration<*> -> this.containingClass()?.classId
is FirClassLikeDeclaration<*> -> this.symbol.classId.outerClassId
else -> null
}
private fun FirDeclaration.getClassId(): ClassId? = when (this) {
is FirCallableMemberDeclaration<*> -> this.containingClass()?.classId
is FirClassLikeDeclaration<*> -> this.symbol.classId
else -> null
}
private fun ClassId.findOuterContainerInfo(
containingDeclarations: List<FirDeclaration>,
scopeSession: ScopeSession
): Pair<FirClassLikeSymbol<*>, FirEffectiveVisibility>? {
for (index in containingDeclarations.indices) {
val declaration = containingDeclarations[index]
val declarationClassId = declaration.getClassId()
if (this.relativeClassName == declarationClassId?.relativeClassName) {
return when (declaration) {
is FirRegularClass -> {
declaration.symbol to resolveFor(declaration, containingDeclarations.subList(0, index), scopeSession)
}
is FirAnonymousObject -> {
declaration.symbol to declaration.remember(FirEffectiveVisibilityImpl.Local)
}
else -> {
null
}
}
}
}
return null
}
}
@@ -5,7 +5,6 @@
package org.jetbrains.kotlin.fir.resolve
import org.jetbrains.kotlin.fir.FirEffectiveVisibilityResolver
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.resolve.providers.FirProvider
import org.jetbrains.kotlin.fir.resolve.providers.FirSymbolProvider
@@ -18,4 +17,3 @@ val FirSession.correspondingSupertypesCache: FirCorrespondingSupertypesCache by
val FirSession.declaredMemberScopeProvider: FirDeclaredMemberScopeProvider by FirSession.sessionComponentAccessor()
val FirSession.qualifierResolver: FirQualifierResolver by FirSession.sessionComponentAccessor()
val FirSession.typeResolver: FirTypeResolver by FirSession.sessionComponentAccessor()
val FirSession.effectiveVisibilityResolver: FirEffectiveVisibilityResolver by FirSession.sessionComponentAccessor()
@@ -5,10 +5,7 @@
package org.jetbrains.kotlin.fir.resolve.transformers
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.descriptors.Visibility
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.declarations.impl.FirDeclarationStatusImpl
@@ -17,6 +14,8 @@ import org.jetbrains.kotlin.fir.render
import org.jetbrains.kotlin.fir.resolve.ScopeSession
import org.jetbrains.kotlin.fir.scopes.ProcessorAction
import org.jetbrains.kotlin.fir.scopes.unsubstitutedScope
import org.jetbrains.kotlin.fir.toEffectiveVisibility
import org.jetbrains.kotlin.fir.typeContext
class FirStatusResolver(
val session: FirSession,
@@ -162,7 +161,16 @@ class FirStatusResolver(
}
}
}
return status.resolved(visibility, modality)
val parentEffectiveVisibility = when {
containingProperty != null -> containingProperty.effectiveVisibility
containingClass is FirRegularClass -> containingClass.effectiveVisibility
containingClass is FirAnonymousObject -> EffectiveVisibility.Local
else -> EffectiveVisibility.Public
}
val selfEffectiveVisibility = visibility.toEffectiveVisibility(containingClass?.symbol?.toLookupTag())
val effectiveVisibility = parentEffectiveVisibility.lowerBound(selfEffectiveVisibility, session.typeContext)
return status.resolved(visibility, modality, effectiveVisibility)
}
private fun resolveVisibility(