From 687a58843feeb470f961f6850f5cece8ee21c674 Mon Sep 17 00:00:00 2001 From: Denis Zharkov Date: Wed, 6 May 2020 16:10:32 +0300 Subject: [PATCH] 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) --- .../jetbrains/kotlin/fir/FirCallResolver.kt | 6 +- .../fir/resolve/BodyResolveComponents.kt | 1 + .../kotlin/fir/resolve/calls/Candidate.kt | 4 +- .../kotlin/fir/resolve/calls/ResolverParts.kt | 99 +++++++++++-------- .../transformers/FirSyntheticCallGenerator.kt | 4 +- .../FirAbstractBodyResolveTransformer.kt | 1 + .../codegen/box/statics/protectedStatic.kt | 1 - .../typeAliasConstructorInSuperCall.fir.kt | 8 +- .../CallCompanionProtectedNonStatic.fir.kt | 6 +- 9 files changed, 75 insertions(+), 55 deletions(-) diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/FirCallResolver.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/FirCallResolver.kt index e8dc9f5a174..c8f7dbde425 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/FirCallResolver.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/FirCallResolver.kt @@ -121,7 +121,7 @@ class FirCallResolver( typeArguments, session, file, - transformer.components.implicitReceiverStack, + transformer.components.containingDeclarations, ) towerResolver.reset() val result = towerResolver.runResolver( @@ -279,7 +279,7 @@ class FirCallResolver( typeArguments = typeArguments, session, file, - implicitReceiverStack, + containingDeclarations, ) towerResolver.reset() val result = towerResolver.runResolverForDelegatingConstructor( @@ -334,7 +334,7 @@ class FirCallResolver( emptyList(), session, file, - transformer.components.implicitReceiverStack, + transformer.components.containingDeclarations, candidateForCommonInvokeReceiver = null, // Additional things for callable reference resolve expectedType, diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/BodyResolveComponents.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/BodyResolveComponents.kt index b011274b815..8bf672c5879 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/BodyResolveComponents.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/BodyResolveComponents.kt @@ -31,6 +31,7 @@ interface SessionHolder { interface BodyResolveComponents : SessionHolder { val returnTypeCalculator: ReturnTypeCalculator val implicitReceiverStack: ImplicitReceiverStack + val containingDeclarations: List val fileImportsScope: List val typeParametersScopes: List val localScopes: FirLocalScopes diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/Candidate.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/Candidate.kt index 71c5506583f..7da3bd1f8a4 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/Candidate.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/Candidate.kt @@ -6,6 +6,7 @@ package org.jetbrains.kotlin.fir.resolve.calls 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.FirValueParameter 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.resolve.BodyResolveComponents 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.substitution.ConeSubstitutor import org.jetbrains.kotlin.fir.symbols.AbstractFirBasedSymbol @@ -42,7 +42,7 @@ data class CallInfo( val typeArguments: List, val session: FirSession, val containingFile: FirFile, - val implicitReceiverStack: ImplicitReceiverStack, + val containingDeclarations: List, val candidateForCommonInvokeReceiver: Candidate? = null, diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/ResolverParts.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/ResolverParts.kt index 615d8b7f45f..08c6866757e 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/ResolverParts.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/ResolverParts.kt @@ -335,54 +335,72 @@ internal object CheckVisibility : CheckerStage() { private fun ClassId.isSame(other: ClassId): Boolean = packageFqName == other.packageFqName && relativeClassName == other.relativeClassName - private fun ImplicitReceiverStack.canSeePrivateMemberOf(ownerId: ClassId): Boolean { - for (implicitReceiverValue in receiversAsReversed()) { - if (implicitReceiverValue !is ImplicitDispatchReceiverValue) continue - if (implicitReceiverValue.companionFromSupertype) continue - val boundSymbol = implicitReceiverValue.boundSymbol + private fun canSeePrivateMemberOf( + containingDeclarationOfUseSite: List, + ownerId: ClassId, + session: FirSession + ): 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)) { return true } } + return false } - private fun FirRegularClassSymbol.canSeeProtectedMemberOf( - ownerId: ClassId, - session: FirSession, - visited: MutableSet - ): Boolean { - if (classId in visited) return false - visited += classId + private fun ClassId.ownerIfCompanion(session: FirSession): ClassId? { + if (outerClassId == null || isLocal) return null + val ownerSymbol = session.firSymbolProvider.getClassLikeSymbolByFqName(this) as? FirRegularClassSymbol + + return outerClassId.takeIf { ownerSymbol?.fir?.isCompanion == true } + } + + private fun FirClass<*>.isSubClass(ownerId: ClassId, session: FirSession): Boolean { if (classId.isSame(ownerId)) return true - fir.companionObject?.let { companion -> - if (companion.classId.isSame(ownerId)) return true + return lookupSuperTypes(this, lookupInterfaces = true, deep = true, session).any { superType -> + (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 - for (superType in superTypes) { - val superTypeSymbol = superType.lookupTag.toSymbol(session) as? FirRegularClassSymbol ?: continue - if (superTypeSymbol.canSeeProtectedMemberOf(ownerId, session, visited)) return true + // TODO: Add check for receiver, see org.jetbrains.kotlin.descriptors.Visibility#doesReceiverFitForProtectedVisibility + return containingUseSiteClass.isSubClass(ownerId, session) + } + + private fun canSeeProtectedMemberOf( + containingDeclarationOfUseSite: List, + 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 } - private fun ImplicitReceiverStack.canSeeProtectedMemberOf(ownerId: ClassId, session: FirSession): Boolean { - if (canSeePrivateMemberOf(ownerId)) return true - val visited = mutableSetOf() - 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 ReceiverValue?.ownerIfCompanion(session: FirSession): ClassId? = + (this?.type as? ConeClassLikeType)?.lookupTag?.classId?.ownerIfCompanion(session) private fun AbstractFirBasedSymbol<*>.getOwnerId(): ClassId? { return when (this) { @@ -409,10 +427,11 @@ internal object CheckVisibility : CheckerStage() { declaration: FirMemberDeclaration, symbol: AbstractFirBasedSymbol<*>, sink: CheckerSink, - callInfo: CallInfo + candidate: Candidate ): Boolean { + val callInfo = candidate.callInfo val useSiteFile = callInfo.containingFile - val implicitReceiverStack = callInfo.implicitReceiverStack + val containingDeclarations = callInfo.containingDeclarations val session = callInfo.session val provider = session.firProvider val candidateFile = when (symbol) { @@ -435,20 +454,20 @@ internal object CheckVisibility : CheckerStage() { candidateFile == useSiteFile } else { // Member: visible inside parent class, including all its member classes - implicitReceiverStack.canSeePrivateMemberOf(ownerId) + canSeePrivateMemberOf(containingDeclarations, ownerId, session) } } else { false } } Visibilities.PROTECTED -> { - ownerId != null && implicitReceiverStack.canSeeProtectedMemberOf(ownerId, session) + ownerId != null && canSeeProtectedMemberOf(containingDeclarations, candidate.dispatchReceiverValue, ownerId, session) } JavaVisibilities.PROTECTED_AND_PACKAGE, JavaVisibilities.PROTECTED_STATIC_VISIBILITY -> { if (symbol.packageFqName() == useSiteFile.packageFqName) { true } else { - ownerId != null && implicitReceiverStack.canSeeProtectedMemberOf(ownerId, session) + ownerId != null && canSeeProtectedMemberOf(containingDeclarations, candidate.dispatchReceiverValue, ownerId, session) } } else -> true @@ -465,7 +484,7 @@ internal object CheckVisibility : CheckerStage() { val symbol = candidate.symbol val declaration = symbol.fir if (declaration is FirMemberDeclaration) { - if (!checkVisibility(declaration, symbol, sink, callInfo)) { + if (!checkVisibility(declaration, symbol, sink, candidate)) { return } } @@ -479,7 +498,7 @@ internal object CheckVisibility : CheckerStage() { if (classSymbol.fir.classKind.isSingleton) { sink.yieldApplicability(CandidateApplicability.HIDDEN) } - checkVisibility(classSymbol.fir, classSymbol, sink, callInfo) + checkVisibility(classSymbol.fir, classSymbol, sink, candidate) } } } diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirSyntheticCallGenerator.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirSyntheticCallGenerator.kt index 6c7fb7d395b..6427bf2ddc2 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirSyntheticCallGenerator.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirSyntheticCallGenerator.kt @@ -158,7 +158,7 @@ class FirSyntheticCallGenerator( typeArguments = emptyList(), session = session, containingFile = file, - implicitReceiverStack = implicitReceiverStack + containingDeclarations = containingDeclarations ) private fun generateSyntheticSelectTypeParameter(): Pair { @@ -276,4 +276,4 @@ private object UpdateReference : FirTransformer( override fun transformReference(reference: FirReference, data: FirNamedReferenceWithCandidate): CompositeTransformResult { return data.compose() } -} \ No newline at end of file +} diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirAbstractBodyResolveTransformer.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirAbstractBodyResolveTransformer.kt index bf6aab21a7a..6a4d87d9d70 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirAbstractBodyResolveTransformer.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirAbstractBodyResolveTransformer.kt @@ -215,6 +215,7 @@ abstract class FirAbstractBodyResolveTransformer(phase: FirResolvePhase) : FirAb override val localScopes: FirLocalScopes get() = context.localScopes override val file: FirFile get() = context.file override val implicitReceiverStack: ImplicitReceiverStack get() = context.implicitReceiverStack + override val containingDeclarations: List get() = context.containers override val localContextForAnonymousFunctions: LocalContextForAnonymousFunctions get() = context.localContextForAnonymousFunctions override val returnTypeCalculator: ReturnTypeCalculator get() = context.returnTypeCalculator override val container: FirDeclaration get() = context.containerIfAny!! diff --git a/compiler/testData/codegen/box/statics/protectedStatic.kt b/compiler/testData/codegen/box/statics/protectedStatic.kt index 9af99d32f82..b025f912426 100644 --- a/compiler/testData/codegen/box/statics/protectedStatic.kt +++ b/compiler/testData/codegen/box/statics/protectedStatic.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND_FIR: JVM_IR // TARGET_BACKEND: JVM // FILE: First.java diff --git a/compiler/testData/diagnostics/tests/typealias/typeAliasConstructorInSuperCall.fir.kt b/compiler/testData/diagnostics/tests/typealias/typeAliasConstructorInSuperCall.fir.kt index 71c53d03a77..5eab095aca7 100644 --- a/compiler/testData/diagnostics/tests/typealias/typeAliasConstructorInSuperCall.fir.kt +++ b/compiler/testData/diagnostics/tests/typealias/typeAliasConstructorInSuperCall.fir.kt @@ -3,12 +3,12 @@ open class MyBase protected constructor() { } typealias MyAlias = MyBase -class MyDerived1 : MyAlias() +class MyDerived1 : MyAlias() class MyDerived1a : MyBase() -class MyDerived2 : MyAlias(null) +class MyDerived2 : MyAlias(null) class MyDerived2a : MyBase(null) class MyDerived3 : MyAlias { - constructor(x: Nothing?) : super(x) -} \ No newline at end of file + constructor(x: Nothing?) : super(x) +} diff --git a/compiler/testData/diagnostics/testsWithStdLib/CallCompanionProtectedNonStatic.fir.kt b/compiler/testData/diagnostics/testsWithStdLib/CallCompanionProtectedNonStatic.fir.kt index 949c96d4cdf..ca67e5f5c76 100644 --- a/compiler/testData/diagnostics/testsWithStdLib/CallCompanionProtectedNonStatic.fir.kt +++ b/compiler/testData/diagnostics/testsWithStdLib/CallCompanionProtectedNonStatic.fir.kt @@ -48,7 +48,7 @@ class Derived : Base() { foo() // Ok gav() // Ok bar() - baz() + baz() prop = 0 } @@ -57,7 +57,7 @@ class Derived : Base() { foo() // Ok gav() // Ok bar() - baz() + baz() prop = 0 } } @@ -66,7 +66,7 @@ class Derived : Base() { fun test2() { gav() // Ok bar() - baz() + baz() prop = 0 } }