FIR: Rewrite visibility checking

Unbound it from implicit receiver stack as it only needs scope structure/declaration nestedness
Semantics for protected has been changed in a way it works in old FE

NB: We should report additional diagnostic in case of CallCompanionProtectedNonStatic.fir.kt
(see KT-38814)
This commit is contained in:
Denis Zharkov
2020-05-06 16:10:32 +03:00
parent 825cdd3841
commit 687a58843f
9 changed files with 75 additions and 55 deletions
@@ -121,7 +121,7 @@ class FirCallResolver(
typeArguments, typeArguments,
session, session,
file, file,
transformer.components.implicitReceiverStack, transformer.components.containingDeclarations,
) )
towerResolver.reset() towerResolver.reset()
val result = towerResolver.runResolver( val result = towerResolver.runResolver(
@@ -279,7 +279,7 @@ class FirCallResolver(
typeArguments = typeArguments, typeArguments = typeArguments,
session, session,
file, file,
implicitReceiverStack, containingDeclarations,
) )
towerResolver.reset() towerResolver.reset()
val result = towerResolver.runResolverForDelegatingConstructor( val result = towerResolver.runResolverForDelegatingConstructor(
@@ -334,7 +334,7 @@ class FirCallResolver(
emptyList(), emptyList(),
session, session,
file, file,
transformer.components.implicitReceiverStack, transformer.components.containingDeclarations,
candidateForCommonInvokeReceiver = null, candidateForCommonInvokeReceiver = null,
// Additional things for callable reference resolve // Additional things for callable reference resolve
expectedType, expectedType,
@@ -31,6 +31,7 @@ interface SessionHolder {
interface BodyResolveComponents : SessionHolder { interface BodyResolveComponents : SessionHolder {
val returnTypeCalculator: ReturnTypeCalculator val returnTypeCalculator: ReturnTypeCalculator
val implicitReceiverStack: ImplicitReceiverStack val implicitReceiverStack: ImplicitReceiverStack
val containingDeclarations: List<FirDeclaration>
val fileImportsScope: List<FirScope> val fileImportsScope: List<FirScope>
val typeParametersScopes: List<FirScope> val typeParametersScopes: List<FirScope>
val localScopes: FirLocalScopes val localScopes: FirLocalScopes
@@ -6,6 +6,7 @@
package org.jetbrains.kotlin.fir.resolve.calls package org.jetbrains.kotlin.fir.resolve.calls
import org.jetbrains.kotlin.fir.FirSession import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.declarations.FirDeclaration
import org.jetbrains.kotlin.fir.declarations.FirFile import org.jetbrains.kotlin.fir.declarations.FirFile
import org.jetbrains.kotlin.fir.declarations.FirValueParameter import org.jetbrains.kotlin.fir.declarations.FirValueParameter
import org.jetbrains.kotlin.fir.expressions.FirArgumentList import org.jetbrains.kotlin.fir.expressions.FirArgumentList
@@ -16,7 +17,6 @@ import org.jetbrains.kotlin.fir.expressions.impl.FirExpressionStub
import org.jetbrains.kotlin.fir.expressions.impl.FirNoReceiverExpression import org.jetbrains.kotlin.fir.expressions.impl.FirNoReceiverExpression
import org.jetbrains.kotlin.fir.resolve.BodyResolveComponents import org.jetbrains.kotlin.fir.resolve.BodyResolveComponents
import org.jetbrains.kotlin.fir.resolve.DoubleColonLHS import org.jetbrains.kotlin.fir.resolve.DoubleColonLHS
import org.jetbrains.kotlin.fir.resolve.ImplicitReceiverStack
import org.jetbrains.kotlin.fir.resolve.inference.PostponedResolvedAtom import org.jetbrains.kotlin.fir.resolve.inference.PostponedResolvedAtom
import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor
import org.jetbrains.kotlin.fir.symbols.AbstractFirBasedSymbol import org.jetbrains.kotlin.fir.symbols.AbstractFirBasedSymbol
@@ -42,7 +42,7 @@ data class CallInfo(
val typeArguments: List<FirTypeProjection>, val typeArguments: List<FirTypeProjection>,
val session: FirSession, val session: FirSession,
val containingFile: FirFile, val containingFile: FirFile,
val implicitReceiverStack: ImplicitReceiverStack, val containingDeclarations: List<FirDeclaration>,
val candidateForCommonInvokeReceiver: Candidate? = null, val candidateForCommonInvokeReceiver: Candidate? = null,
@@ -335,54 +335,72 @@ internal object CheckVisibility : CheckerStage() {
private fun ClassId.isSame(other: ClassId): Boolean = private fun ClassId.isSame(other: ClassId): Boolean =
packageFqName == other.packageFqName && relativeClassName == other.relativeClassName packageFqName == other.packageFqName && relativeClassName == other.relativeClassName
private fun ImplicitReceiverStack.canSeePrivateMemberOf(ownerId: ClassId): Boolean { private fun canSeePrivateMemberOf(
for (implicitReceiverValue in receiversAsReversed()) { containingDeclarationOfUseSite: List<FirDeclaration>,
if (implicitReceiverValue !is ImplicitDispatchReceiverValue) continue ownerId: ClassId,
if (implicitReceiverValue.companionFromSupertype) continue session: FirSession
val boundSymbol = implicitReceiverValue.boundSymbol ): Boolean {
ownerId.ownerIfCompanion(session)?.let { companionOwnerClassId ->
return canSeePrivateMemberOf(containingDeclarationOfUseSite, companionOwnerClassId, session)
}
for (declaration in containingDeclarationOfUseSite) {
if (declaration !is FirClass<*>) continue
val boundSymbol = declaration.symbol
if (boundSymbol.classId.isSame(ownerId)) { if (boundSymbol.classId.isSame(ownerId)) {
return true return true
} }
} }
return false return false
} }
private fun FirRegularClassSymbol.canSeeProtectedMemberOf( private fun ClassId.ownerIfCompanion(session: FirSession): ClassId? {
ownerId: ClassId, if (outerClassId == null || isLocal) return null
session: FirSession, val ownerSymbol = session.firSymbolProvider.getClassLikeSymbolByFqName(this) as? FirRegularClassSymbol
visited: MutableSet<ClassId>
): Boolean { return outerClassId.takeIf { ownerSymbol?.fir?.isCompanion == true }
if (classId in visited) return false }
visited += classId
private fun FirClass<*>.isSubClass(ownerId: ClassId, session: FirSession): Boolean {
if (classId.isSame(ownerId)) return true if (classId.isSame(ownerId)) return true
fir.companionObject?.let { companion -> return lookupSuperTypes(this, lookupInterfaces = true, deep = true, session).any { superType ->
if (companion.classId.isSame(ownerId)) return true (superType as? ConeClassLikeType)?.fullyExpandedType(session)?.lookupTag?.classId?.isSame(ownerId) == true
}
}
private fun canSeeProtectedMemberOf(
containingUseSiteClass: FirClass<*>,
dispatchReceiver: ReceiverValue?,
ownerId: ClassId, session: FirSession
): Boolean {
dispatchReceiver?.ownerIfCompanion(session)?.let { companionOwnerClassId ->
if (containingUseSiteClass.isSubClass(companionOwnerClassId, session)) return true
} }
val superTypes = fir.superConeTypes // TODO: Add check for receiver, see org.jetbrains.kotlin.descriptors.Visibility#doesReceiverFitForProtectedVisibility
for (superType in superTypes) { return containingUseSiteClass.isSubClass(ownerId, session)
val superTypeSymbol = superType.lookupTag.toSymbol(session) as? FirRegularClassSymbol ?: continue }
if (superTypeSymbol.canSeeProtectedMemberOf(ownerId, session, visited)) return true
private fun canSeeProtectedMemberOf(
containingDeclarationOfUseSite: List<FirDeclaration>,
dispatchReceiver: ReceiverValue?,
ownerId: ClassId, session: FirSession
): Boolean {
if (canSeePrivateMemberOf(containingDeclarationOfUseSite, ownerId, session)) return true
for (containingDeclaration in containingDeclarationOfUseSite) {
if (containingDeclaration !is FirClass<*>) continue
val boundSymbol = containingDeclaration.symbol
if (canSeeProtectedMemberOf(boundSymbol.fir, dispatchReceiver, ownerId, session)) return true
} }
return false return false
} }
private fun ImplicitReceiverStack.canSeeProtectedMemberOf(ownerId: ClassId, session: FirSession): Boolean { private fun ReceiverValue?.ownerIfCompanion(session: FirSession): ClassId? =
if (canSeePrivateMemberOf(ownerId)) return true (this?.type as? ConeClassLikeType)?.lookupTag?.classId?.ownerIfCompanion(session)
val visited = mutableSetOf<ClassId>()
for (implicitReceiverValue in receiversAsReversed()) {
if (implicitReceiverValue !is ImplicitDispatchReceiverValue) continue
if (implicitReceiverValue.companionFromSupertype) continue
val boundSymbol = implicitReceiverValue.boundSymbol
val superTypes = boundSymbol.fir.superConeTypes
for (superType in superTypes) {
val superTypeSymbol = superType.lookupTag.toSymbol(session) as? FirRegularClassSymbol ?: continue
if (superTypeSymbol.canSeeProtectedMemberOf(ownerId, session, visited)) return true
}
}
return false
}
private fun AbstractFirBasedSymbol<*>.getOwnerId(): ClassId? { private fun AbstractFirBasedSymbol<*>.getOwnerId(): ClassId? {
return when (this) { return when (this) {
@@ -409,10 +427,11 @@ internal object CheckVisibility : CheckerStage() {
declaration: FirMemberDeclaration, declaration: FirMemberDeclaration,
symbol: AbstractFirBasedSymbol<*>, symbol: AbstractFirBasedSymbol<*>,
sink: CheckerSink, sink: CheckerSink,
callInfo: CallInfo candidate: Candidate
): Boolean { ): Boolean {
val callInfo = candidate.callInfo
val useSiteFile = callInfo.containingFile val useSiteFile = callInfo.containingFile
val implicitReceiverStack = callInfo.implicitReceiverStack val containingDeclarations = callInfo.containingDeclarations
val session = callInfo.session val session = callInfo.session
val provider = session.firProvider val provider = session.firProvider
val candidateFile = when (symbol) { val candidateFile = when (symbol) {
@@ -435,20 +454,20 @@ internal object CheckVisibility : CheckerStage() {
candidateFile == useSiteFile candidateFile == useSiteFile
} else { } else {
// Member: visible inside parent class, including all its member classes // Member: visible inside parent class, including all its member classes
implicitReceiverStack.canSeePrivateMemberOf(ownerId) canSeePrivateMemberOf(containingDeclarations, ownerId, session)
} }
} else { } else {
false false
} }
} }
Visibilities.PROTECTED -> { Visibilities.PROTECTED -> {
ownerId != null && implicitReceiverStack.canSeeProtectedMemberOf(ownerId, session) ownerId != null && canSeeProtectedMemberOf(containingDeclarations, candidate.dispatchReceiverValue, ownerId, session)
} }
JavaVisibilities.PROTECTED_AND_PACKAGE, JavaVisibilities.PROTECTED_STATIC_VISIBILITY -> { JavaVisibilities.PROTECTED_AND_PACKAGE, JavaVisibilities.PROTECTED_STATIC_VISIBILITY -> {
if (symbol.packageFqName() == useSiteFile.packageFqName) { if (symbol.packageFqName() == useSiteFile.packageFqName) {
true true
} else { } else {
ownerId != null && implicitReceiverStack.canSeeProtectedMemberOf(ownerId, session) ownerId != null && canSeeProtectedMemberOf(containingDeclarations, candidate.dispatchReceiverValue, ownerId, session)
} }
} }
else -> true else -> true
@@ -465,7 +484,7 @@ internal object CheckVisibility : CheckerStage() {
val symbol = candidate.symbol val symbol = candidate.symbol
val declaration = symbol.fir val declaration = symbol.fir
if (declaration is FirMemberDeclaration) { if (declaration is FirMemberDeclaration) {
if (!checkVisibility(declaration, symbol, sink, callInfo)) { if (!checkVisibility(declaration, symbol, sink, candidate)) {
return return
} }
} }
@@ -479,7 +498,7 @@ internal object CheckVisibility : CheckerStage() {
if (classSymbol.fir.classKind.isSingleton) { if (classSymbol.fir.classKind.isSingleton) {
sink.yieldApplicability(CandidateApplicability.HIDDEN) sink.yieldApplicability(CandidateApplicability.HIDDEN)
} }
checkVisibility(classSymbol.fir, classSymbol, sink, callInfo) checkVisibility(classSymbol.fir, classSymbol, sink, candidate)
} }
} }
} }
@@ -158,7 +158,7 @@ class FirSyntheticCallGenerator(
typeArguments = emptyList(), typeArguments = emptyList(),
session = session, session = session,
containingFile = file, containingFile = file,
implicitReceiverStack = implicitReceiverStack containingDeclarations = containingDeclarations
) )
private fun generateSyntheticSelectTypeParameter(): Pair<FirTypeParameter, FirResolvedTypeRef> { private fun generateSyntheticSelectTypeParameter(): Pair<FirTypeParameter, FirResolvedTypeRef> {
@@ -276,4 +276,4 @@ private object UpdateReference : FirTransformer<FirNamedReferenceWithCandidate>(
override fun transformReference(reference: FirReference, data: FirNamedReferenceWithCandidate): CompositeTransformResult<FirReference> { override fun transformReference(reference: FirReference, data: FirNamedReferenceWithCandidate): CompositeTransformResult<FirReference> {
return data.compose() return data.compose()
} }
} }
@@ -215,6 +215,7 @@ abstract class FirAbstractBodyResolveTransformer(phase: FirResolvePhase) : FirAb
override val localScopes: FirLocalScopes get() = context.localScopes override val localScopes: FirLocalScopes get() = context.localScopes
override val file: FirFile get() = context.file override val file: FirFile get() = context.file
override val implicitReceiverStack: ImplicitReceiverStack get() = context.implicitReceiverStack override val implicitReceiverStack: ImplicitReceiverStack get() = context.implicitReceiverStack
override val containingDeclarations: List<FirDeclaration> get() = context.containers
override val localContextForAnonymousFunctions: LocalContextForAnonymousFunctions get() = context.localContextForAnonymousFunctions override val localContextForAnonymousFunctions: LocalContextForAnonymousFunctions get() = context.localContextForAnonymousFunctions
override val returnTypeCalculator: ReturnTypeCalculator get() = context.returnTypeCalculator override val returnTypeCalculator: ReturnTypeCalculator get() = context.returnTypeCalculator
override val container: FirDeclaration get() = context.containerIfAny!! override val container: FirDeclaration get() = context.containerIfAny!!
@@ -1,4 +1,3 @@
// IGNORE_BACKEND_FIR: JVM_IR
// TARGET_BACKEND: JVM // TARGET_BACKEND: JVM
// FILE: First.java // FILE: First.java
@@ -3,12 +3,12 @@ open class MyBase protected constructor() {
} }
typealias MyAlias = MyBase typealias MyAlias = MyBase
class MyDerived1 : <!INAPPLICABLE_CANDIDATE!>MyAlias<!>() class MyDerived1 : MyAlias()
class MyDerived1a : MyBase() class MyDerived1a : MyBase()
class MyDerived2 : <!INAPPLICABLE_CANDIDATE!>MyAlias<!>(null) class MyDerived2 : MyAlias(null)
class MyDerived2a : MyBase(null) class MyDerived2a : MyBase(null)
class MyDerived3 : MyAlias { class MyDerived3 : MyAlias {
constructor(x: Nothing?) : <!INAPPLICABLE_CANDIDATE!>super<!>(x) constructor(x: Nothing?) : super(x)
} }
@@ -48,7 +48,7 @@ class Derived : Base() {
foo() // Ok foo() // Ok
gav() // Ok gav() // Ok
bar() bar()
<!INAPPLICABLE_CANDIDATE!>baz<!>() baz()
prop = 0 prop = 0
} }
@@ -57,7 +57,7 @@ class Derived : Base() {
foo() // Ok foo() // Ok
gav() // Ok gav() // Ok
bar() bar()
<!INAPPLICABLE_CANDIDATE!>baz<!>() baz()
prop = 0 prop = 0
} }
} }
@@ -66,7 +66,7 @@ class Derived : Base() {
fun test2() { fun test2() {
gav() // Ok gav() // Ok
bar() bar()
<!INAPPLICABLE_CANDIDATE!>baz<!>() baz()
prop = 0 prop = 0
} }
} }