[FIR] Calculate property setters visibilities for intersections properly

Basically, just calculate them the same
way it's done for other members.

`chooseIntersectionVisibilityForSymbolsOrNull`
is named like this to prevent a JVM clash.

^KT-66046 Fixed
This commit is contained in:
Nikolay Lunyak
2024-03-06 15:14:44 +02:00
committed by Space Team
parent b7926b68ab
commit 7ecbaf7d1e
12 changed files with 88 additions and 84 deletions
@@ -12,7 +12,6 @@ import org.jetbrains.kotlin.fir.declarations.FirDeclarationOrigin
import org.jetbrains.kotlin.fir.declarations.FirProperty
import org.jetbrains.kotlin.fir.declarations.FirSimpleFunction
import org.jetbrains.kotlin.fir.java.JavaTypeParameterStack
import org.jetbrains.kotlin.fir.scopes.MemberWithBaseScope
import org.jetbrains.kotlin.fir.scopes.PlatformSpecificOverridabilityRules
import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol
@@ -51,8 +50,8 @@ class JavaOverridabilityRules(session: FirSession) : PlatformSpecificOverridabil
private fun FirCallableDeclaration.isOriginallyFromJava(): Boolean = unwrapFakeOverrides().origin == FirDeclarationOrigin.Enhancement
override fun <D : FirCallableSymbol<*>> chooseIntersectionVisibility(
extractedOverrides: Collection<MemberWithBaseScope<D>>,
override fun chooseIntersectionVisibility(
overrides: Collection<FirCallableSymbol<*>>,
dispatchClassSymbol: FirRegularClassSymbol?,
): Visibility = javaOverrideChecker.chooseIntersectionVisibility(extractedOverrides, dispatchClassSymbol)
): Visibility = javaOverrideChecker.chooseIntersectionVisibility(overrides, dispatchClassSymbol)
}
@@ -25,11 +25,8 @@ import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor
import org.jetbrains.kotlin.fir.resolve.substitution.substitutorByMap
import org.jetbrains.kotlin.fir.resolve.toSymbol
import org.jetbrains.kotlin.fir.scopes.FirTypeScope
import org.jetbrains.kotlin.fir.scopes.MemberWithBaseScope
import org.jetbrains.kotlin.fir.scopes.ProcessorAction
import org.jetbrains.kotlin.fir.scopes.impl.FirAbstractOverrideChecker
import org.jetbrains.kotlin.fir.scopes.impl.chooseIntersectionVisibilityOrNull
import org.jetbrains.kotlin.fir.scopes.impl.isAbstract
import org.jetbrains.kotlin.fir.scopes.impl.*
import org.jetbrains.kotlin.fir.scopes.jvm.computeJvmDescriptorRepresentation
import org.jetbrains.kotlin.fir.scopes.processOverriddenFunctions
import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
@@ -370,25 +367,23 @@ class JavaOverrideChecker internal constructor(
return overridesMutableCollectionRemove
}
override fun <D : FirCallableSymbol<*>> chooseIntersectionVisibility(
extractedOverrides: Collection<MemberWithBaseScope<D>>,
override fun chooseIntersectionVisibility(
overrides: Collection<FirCallableSymbol<*>>,
dispatchClassSymbol: FirRegularClassSymbol?,
): Visibility {
val nonSubsumed = extractedOverrides.getNonSubsumedNonPhantomOverriddenSymbols()
// In Java it's OK to inherit multiple implementations of the same function
// from the supertypes as long as there's an implementation from a class.
// We shouldn't reject green Java code.
if (dispatchClassSymbol?.fir is FirJavaClass) {
val nonAbstractFromClass = nonSubsumed.find {
!it.isAbstract && it.member.dispatchReceiverClassLookupTagOrNull()
val nonAbstractFromClass = overrides.find {
!it.isAbstractAccordingToRawStatus && it.dispatchReceiverClassLookupTagOrNull()
?.toSymbol(session)?.classKind == ClassKind.CLASS
}
if (nonAbstractFromClass != null) {
return nonAbstractFromClass.member.rawStatus.visibility
return nonAbstractFromClass.rawStatus.visibility
}
}
return chooseIntersectionVisibilityOrNull(nonSubsumed) ?: Visibilities.Unknown
return chooseIntersectionVisibilityOrNull(overrides) ?: Visibilities.Unknown
}
}
@@ -14,7 +14,6 @@ import org.jetbrains.kotlin.fir.containingClassLookupTag
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.resolve.toSymbol
import org.jetbrains.kotlin.fir.scopes.FirOverrideChecker
import org.jetbrains.kotlin.fir.scopes.MemberWithBaseScope
import org.jetbrains.kotlin.fir.scopes.impl.*
import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirClassSymbol
@@ -38,12 +37,11 @@ class FirNativeOverrideChecker(private val session: FirSession) : FirOverrideChe
// KT-57640: There's no necessity to implement platform-dependent overridability check for properties
standardOverrideChecker.isOverriddenProperty(overrideCandidate, baseDeclaration)
override fun <D : FirCallableSymbol<*>> chooseIntersectionVisibility(
extractedOverrides: Collection<MemberWithBaseScope<D>>,
override fun chooseIntersectionVisibility(
overrides: Collection<FirCallableSymbol<*>>,
dispatchClassSymbol: FirRegularClassSymbol?,
): Visibility {
val nonSubsumed = extractedOverrides.getNonSubsumedNonPhantomOverriddenSymbols()
return chooseIntersectionVisibilityOrNull(nonSubsumed) { it.isAbstract || it.member.isObjCClassProperty(session) }
return chooseIntersectionVisibilityOrNull(overrides) { it.isAbstractAccordingToRawStatus || it.isObjCClassProperty(session) }
?: Visibilities.Unknown
}
@@ -35,11 +35,11 @@ class FirIntersectionScopeOverrideChecker(session: FirSession) : FirOverrideChec
return standardOverrideChecker.isOverriddenProperty(overrideCandidate, baseDeclaration)
}
override fun <D : FirCallableSymbol<*>> chooseIntersectionVisibility(
extractedOverrides: Collection<MemberWithBaseScope<D>>,
override fun chooseIntersectionVisibility(
overrides: Collection<FirCallableSymbol<*>>,
dispatchClassSymbol: FirRegularClassSymbol?,
): Visibility {
platformSpecificOverridabilityRules?.chooseIntersectionVisibility(extractedOverrides, dispatchClassSymbol)?.let { return it }
return standardOverrideChecker.chooseIntersectionVisibility(extractedOverrides, dispatchClassSymbol)
platformSpecificOverridabilityRules?.chooseIntersectionVisibility(overrides, dispatchClassSymbol)?.let { return it }
return standardOverrideChecker.chooseIntersectionVisibility(overrides, dispatchClassSymbol)
}
}
@@ -27,8 +27,8 @@ interface FirOverrideChecker : FirSessionComponent {
baseDeclaration: FirProperty
): Boolean
fun <D : FirCallableSymbol<*>> chooseIntersectionVisibility(
extractedOverrides: Collection<MemberWithBaseScope<D>>,
fun chooseIntersectionVisibility(
overrides: Collection<FirCallableSymbol<*>>,
dispatchClassSymbol: FirRegularClassSymbol?,
): Visibility
}
@@ -26,8 +26,8 @@ interface PlatformSpecificOverridabilityRules : FirSessionComponent {
baseDeclaration: FirProperty
): Boolean?
fun <D : FirCallableSymbol<*>> chooseIntersectionVisibility(
extractedOverrides: Collection<MemberWithBaseScope<D>>,
fun chooseIntersectionVisibility(
overrides: Collection<FirCallableSymbol<*>>,
dispatchClassSymbol: FirRegularClassSymbol?,
): Visibility?
}
@@ -399,6 +399,7 @@ object FirFakeOverrideGenerator {
newReturnType: ConeKotlinType? = null,
newModality: Modality? = null,
newVisibility: Visibility? = null,
newSetterVisibility: Visibility? = null,
deferredReturnTypeCalculation: DeferredCallableCopyReturnType? = null,
newSource: KtSourceElement? = derivedClassLookupTag?.toSymbol(session)?.source,
): FirProperty = buildProperty {
@@ -436,16 +437,19 @@ object FirFakeOverrideGenerator {
newSource = newSource ?: baseProperty.getter?.source,
)
setter = baseProperty.setter?.buildCopyIfNeeded(
moduleData = session.nullableModuleData ?: baseProperty.moduleData,
origin = origin,
propertyReturnTypeRef = this@buildProperty.returnTypeRef,
propertySymbol = newSymbol,
dispatchReceiverType = dispatchReceiverType,
derivedClassLookupTag = derivedClassLookupTag,
baseProperty = baseProperty,
newSource = newSource ?: baseProperty.setter?.source,
)
setter = baseProperty.setter?.let { setter ->
setter.buildCopyIfNeeded(
moduleData = session.nullableModuleData ?: baseProperty.moduleData,
origin = origin,
propertyReturnTypeRef = this@buildProperty.returnTypeRef,
propertySymbol = newSymbol,
dispatchReceiverType = dispatchReceiverType,
derivedClassLookupTag = derivedClassLookupTag,
baseProperty = baseProperty,
newSource = newSource ?: baseProperty.setter?.source,
newSetterVisibility ?: setter.visibility,
)
}
}.apply {
containingClassForStaticMemberAttr = derivedClassLookupTag.takeIf { shouldOverrideSetContainingClass(baseProperty) }
}
@@ -459,8 +463,9 @@ object FirFakeOverrideGenerator {
derivedClassLookupTag: ConeClassLikeLookupTag?,
baseProperty: FirProperty,
newSource: KtSourceElement? = source,
newVisibility: Visibility = visibility,
) = when {
annotations.isNotEmpty() || visibility != baseProperty.visibility -> buildCopy(
annotations.isNotEmpty() || newVisibility != baseProperty.visibility -> buildCopy(
moduleData,
origin,
propertyReturnTypeRef,
@@ -469,6 +474,7 @@ object FirFakeOverrideGenerator {
derivedClassLookupTag,
baseProperty,
newSource,
newVisibility,
)
else -> null
}
@@ -482,13 +488,14 @@ object FirFakeOverrideGenerator {
derivedClassLookupTag: ConeClassLikeLookupTag?,
baseProperty: FirProperty,
newSource: KtSourceElement? = source,
newVisibility: Visibility = visibility,
) = when (this) {
is FirDefaultPropertyGetter -> FirDefaultPropertyGetter(
source = newSource,
moduleData = moduleData,
origin = origin,
propertyTypeRef = propertyReturnTypeRef,
visibility = visibility,
visibility = newVisibility,
propertySymbol = propertySymbol,
modality = modality ?: Modality.FINAL,
effectiveVisibility = effectiveVisibility,
@@ -501,7 +508,7 @@ object FirFakeOverrideGenerator {
moduleData = moduleData,
origin = origin,
propertyTypeRef = propertyReturnTypeRef,
visibility = visibility,
visibility = newVisibility,
propertySymbol = propertySymbol,
modality = modality ?: Modality.FINAL,
effectiveVisibility = effectiveVisibility,
@@ -518,6 +525,7 @@ object FirFakeOverrideGenerator {
this.dispatchReceiverType = dispatchReceiverType
this.body = null
resolvePhase = origin.resolvePhaseForCopy
this.status = status.copy(visibility = newVisibility)
}.also {
if (it.isSetter) {
val originalParameter = it.valueParameters.first()
@@ -14,6 +14,7 @@ import org.jetbrains.kotlin.fir.declarations.utils.visibility
import org.jetbrains.kotlin.fir.scopes.*
import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirPropertyAccessorSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol
import org.jetbrains.kotlin.name.StandardClassIds
@@ -64,40 +65,54 @@ fun <D : FirCallableSymbol<*>> overrides(
}
inline fun chooseIntersectionVisibilityOrNull(
nonSubsumedOverrides: List<MemberWithBaseScope<FirCallableSymbol<*>>>,
isAbstract: (MemberWithBaseScope<FirCallableSymbol<*>>) -> Boolean = MemberWithBaseScope<FirCallableSymbol<*>>::isAbstract,
nonSubsumedOverrides: Collection<FirCallableSymbol<*>>,
isAbstract: (FirCallableSymbol<*>) -> Boolean = FirCallableSymbol<*>::isAbstractAccordingToRawStatus,
): Visibility? = chooseIntersectionVisibilityOrNull(
nonSubsumedOverrides,
toSymbol = { it },
isAbstract,
)
inline fun <D> chooseIntersectionVisibilityOrNull(
nonSubsumedOverrides: Collection<D>,
toSymbol: (D) -> FirCallableSymbol<*>,
isAbstract: (D) -> Boolean,
): Visibility? {
val nonAbstract = nonSubsumedOverrides.filter {
// Kotlin's Cloneable interface contains phantom `protected open fun clone()`.
!isAbstract(it) && it.member.callableId != StandardClassIds.Callables.clone
!isAbstract(it) && toSymbol(it).callableId != StandardClassIds.Callables.clone
}
val allAreAbstract = nonAbstract.isEmpty()
if (allAreAbstract) {
return findMaxVisibilityOrNull(nonSubsumedOverrides)
return findMaxVisibilityOrNull(nonSubsumedOverrides, toSymbol)
}
return nonAbstract.singleOrNull()?.member?.rawStatus?.visibility
return nonAbstract.singleOrNull()?.let(toSymbol)?.rawStatus?.visibility
}
val MemberWithBaseScope<FirCallableSymbol<*>>.isAbstract: Boolean
val FirCallableSymbol<*>.isAbstractAccordingToRawStatus: Boolean
get() {
val responsibleDeclaration = when (this) {
!is FirPropertyAccessorSymbol -> this
else -> propertySymbol
}
// This function is expected to be called during FirResolvePhase.STATUS,
// meaning we can't yet access `resolvedStatus`, because it would require
// the same phase, but by this time we expect the statuses to have been
// calculated de-facto.
require(member.rawStatus is FirResolvedDeclarationStatus)
// Kotlin's Cloneable interface contains phantom `protected open fun clone()`.
return member.rawStatus.modality == Modality.ABSTRACT
require(responsibleDeclaration.rawStatus is FirResolvedDeclarationStatus)
return responsibleDeclaration.rawStatus.modality == Modality.ABSTRACT
}
fun <D : FirCallableSymbol<*>> findMaxVisibilityOrNull(
extractedOverrides: Collection<MemberWithBaseScope<D>>
inline fun <D> findMaxVisibilityOrNull(
extractedOverrides: Collection<D>,
toSymbol: (D) -> FirCallableSymbol<*>,
): Visibility? {
var maxVisibility: Visibility = Visibilities.Private
for ((override) in extractedOverrides) {
val visibility = (override.fir as FirMemberDeclaration).visibility
for (override in extractedOverrides) {
val visibility = (toSymbol(override).fir as FirMemberDeclaration).visibility
val compare = Visibilities.compare(visibility, maxVisibility) ?: return null
if (compare > 0) {
@@ -13,7 +13,6 @@ import org.jetbrains.kotlin.fir.declarations.utils.isSuspend
import org.jetbrains.kotlin.fir.declarations.utils.visibility
import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor
import org.jetbrains.kotlin.fir.resolve.transformers.ensureResolvedTypeDeclaration
import org.jetbrains.kotlin.fir.scopes.MemberWithBaseScope
import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol
import org.jetbrains.kotlin.fir.symbols.lazyResolveToPhase
@@ -162,11 +161,10 @@ class FirStandardOverrideChecker(private val session: FirSession) : FirAbstractO
}
}
override fun <D : FirCallableSymbol<*>> chooseIntersectionVisibility(
extractedOverrides: Collection<MemberWithBaseScope<D>>,
override fun chooseIntersectionVisibility(
overrides: Collection<FirCallableSymbol<*>>,
dispatchClassSymbol: FirRegularClassSymbol?,
): Visibility {
val nonSubsumed = extractedOverrides.getNonSubsumedNonPhantomOverriddenSymbols()
return chooseIntersectionVisibilityOrNull(nonSubsumed) ?: Visibilities.Unknown
return chooseIntersectionVisibilityOrNull(overrides) ?: Visibilities.Unknown
}
}
@@ -215,7 +215,8 @@ class FirTypeIntersectionScopeContext(
containsMultipleNonSubsumed: Boolean,
): MemberWithBaseScope<FirCallableSymbol<*>> {
val newModality = chooseIntersectionOverrideModality(extractedOverrides.flatMap { it.flattenIntersectionsRecursively() }.nonSubsumed())
val newVisibility = overrideChecker.chooseIntersectionVisibility(extractedOverrides, dispatchClassSymbol)
val nonSubsumedNonPhantomOverrides = extractedOverrides.getNonSubsumedNonPhantomOverriddenSymbols().map { it.member }
val newVisibility = overrideChecker.chooseIntersectionVisibility(nonSubsumedNonPhantomOverrides, dispatchClassSymbol)
val mostSpecificSymbols = mostSpecific.map { it.member }
val extractedOverridesSymbols = extractedOverrides.map { it.member }
val key = mostSpecific.first()
@@ -227,7 +228,8 @@ class FirTypeIntersectionScopeContext(
is FirPropertySymbol ->
createIntersectionOverrideProperty(
mostSpecificSymbols, extractedOverridesSymbols, newModality, newVisibility, containsMultipleNonSubsumed
mostSpecificSymbols, extractedOverridesSymbols, nonSubsumedNonPhantomOverrides, newModality,
newVisibility, containsMultipleNonSubsumed,
)
is FirFieldSymbol -> {
@@ -368,6 +370,7 @@ class FirTypeIntersectionScopeContext(
private fun createIntersectionOverrideProperty(
mostSpecific: Collection<FirCallableSymbol<*>>,
overrides: Collection<FirCallableSymbol<*>>,
nonSubsumedNonPhantomOverrides: List<FirCallableSymbol<*>>,
newModality: Modality?,
newVisibility: Visibility,
containsMultipleNonSubsumed: Boolean,
@@ -378,6 +381,14 @@ class FirTypeIntersectionScopeContext(
containsMultipleNonSubsumed,
::FirIntersectionOverridePropertySymbol,
) { symbol, fir, deferredReturnTypeCalculation, returnType ->
// Only setters's visibilities are calculated properly, because
// getters' visibilities must be the same as the ones of their
// properties and those we've already calculated.
val setters = nonSubsumedNonPhantomOverrides.mapNotNull {
(it as? FirPropertySymbol)?.unwrapSubstitutionOverrides()?.setterSymbol
}
val setterVisibility = chooseIntersectionVisibilityOrNull(setters) ?: Visibilities.Unknown
FirFakeOverrideGenerator.createCopyForFirProperty(
symbol, fir, derivedClassLookupTag = null, session,
FirDeclarationOrigin.IntersectionOverride,
@@ -390,6 +401,7 @@ class FirTypeIntersectionScopeContext(
// anyway and their uses should result in an overload resolution error.
newReturnType = returnType,
newSource = dispatchReceiverType.toSymbol(session)?.source,
newSetterVisibility = setterVisibility,
)
}
}
@@ -1,22 +0,0 @@
// ISSUE: KT-66046
abstract class I1 {
abstract var a: Int
protected set
}
interface I2 {
var a: Int
}
abstract class <!CANNOT_WEAKEN_ACCESS_PRIVILEGE!>C<!> : I1(), I2
abstract class I3 {
protected abstract fun foo(): Int
}
interface I4 {
fun foo(): Int
}
abstract class B : I3(), I4
@@ -1,3 +1,4 @@
// FIR_IDENTICAL
// ISSUE: KT-66046
abstract class I1 {