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:
@@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+1
@@ -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
|
||||||
|
|||||||
+4
-4
@@ -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)
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-3
@@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user