Skip to content

Commit 435080b

Browse files
demiurg906Space Team
authored and
Space Team
committed
[FIR] Consider effective visibility of parent class during deserialization
For effective visibility calculation, it's essential to know the effective visibility of the containing declaration, which was completely missed in the implementation of the metadata deserializer Also, make the effective visibility of deserialized declarations lazy, as its computation requires subtyping, which is illegal to call during deserialization. ^KT-74040
1 parent 05e92d4 commit 435080b

21 files changed

+284
-75
lines changed

compiler/fir/fir-deserialization/src/org/jetbrains/kotlin/fir/deserialization/ClassDeserialization.kt

+9-6
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import org.jetbrains.kotlin.fir.*
1313
import org.jetbrains.kotlin.fir.declarations.*
1414
import org.jetbrains.kotlin.fir.declarations.builder.*
1515
import org.jetbrains.kotlin.fir.declarations.impl.FirResolvedDeclarationStatusImpl
16+
import org.jetbrains.kotlin.fir.declarations.impl.FirResolvedDeclarationStatusWithLazyEffectiveVisibility
1617
import org.jetbrains.kotlin.fir.declarations.utils.addDeclarations
1718
import org.jetbrains.kotlin.fir.declarations.utils.isCompanion
1819
import org.jetbrains.kotlin.fir.declarations.utils.moduleName
@@ -62,11 +63,12 @@ fun deserializeClassToSymbol(
6263
val kind = Flags.CLASS_KIND.get(flags)
6364
val modality = ProtoEnumFlags.modality(Flags.MODALITY.get(flags))
6465
val visibility = ProtoEnumFlags.visibility(Flags.VISIBILITY.get(flags))
65-
val status = FirResolvedDeclarationStatusImpl(
66-
visibility,
67-
modality,
68-
visibility.toEffectiveVisibility(parentContext?.outerClassSymbol, forClass = true)
69-
).apply {
66+
val effectiveVisibility = visibility.toLazyEffectiveVisibility(
67+
owner = parentContext?.outerClassSymbol,
68+
session,
69+
forClass = true
70+
)
71+
val status = FirResolvedDeclarationStatusWithLazyEffectiveVisibility(visibility, modality, effectiveVisibility).apply {
7072
isExpect = Flags.IS_EXPECT_CLASS.get(flags)
7173
isActual = false
7274
isCompanion = kind == ProtoBuf.Class.Kind.COMPANION_OBJECT
@@ -105,7 +107,8 @@ fun deserializeClassToSymbol(
105107
flexibleTypeFactory,
106108
constDeserializer,
107109
containerSource,
108-
symbol
110+
symbol,
111+
status.effectiveVisibility
109112
)
110113
if (status.isCompanion) {
111114
parentContext?.let {

compiler/fir/fir-deserialization/src/org/jetbrains/kotlin/fir/deserialization/FirMemberDeserializer.kt

+64-21
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@
55

66
package org.jetbrains.kotlin.fir.deserialization
77

8+
import org.jetbrains.kotlin.descriptors.EffectiveVisibility
89
import org.jetbrains.kotlin.descriptors.Modality
10+
import org.jetbrains.kotlin.descriptors.Visibility
911
import org.jetbrains.kotlin.fir.FirModuleData
1012
import org.jetbrains.kotlin.fir.FirSession
1113
import org.jetbrains.kotlin.fir.containingClassForStaticMemberAttr
@@ -14,7 +16,8 @@ import org.jetbrains.kotlin.fir.declarations.builder.*
1416
import org.jetbrains.kotlin.fir.declarations.impl.FirDefaultPropertyBackingField
1517
import org.jetbrains.kotlin.fir.declarations.impl.FirDefaultPropertyGetter
1618
import org.jetbrains.kotlin.fir.declarations.impl.FirDefaultPropertySetter
17-
import org.jetbrains.kotlin.fir.declarations.impl.FirResolvedDeclarationStatusImpl
19+
import org.jetbrains.kotlin.fir.declarations.impl.FirResolvedDeclarationStatusWithLazyEffectiveVisibility
20+
import org.jetbrains.kotlin.fir.declarations.utils.effectiveVisibility
1821
import org.jetbrains.kotlin.fir.declarations.utils.sourceElement
1922
import org.jetbrains.kotlin.fir.expressions.FirAnnotation
2023
import org.jetbrains.kotlin.fir.expressions.FirExpression
@@ -114,6 +117,7 @@ class FirDeserializationContext(
114117
typeParameterProtos = emptyList(),
115118
containerSource,
116119
outerClassSymbol = null,
120+
outerClassEffectiveVisibility = EffectiveVisibility.Public,
117121
containingDeclarationSymbol = null
118122
)
119123

@@ -126,7 +130,8 @@ class FirDeserializationContext(
126130
flexibleTypeFactory: FirTypeDeserializer.FlexibleTypeFactory,
127131
constDeserializer: FirConstDeserializer,
128132
containerSource: DeserializedContainerSource?,
129-
outerClassSymbol: FirRegularClassSymbol
133+
outerClassSymbol: FirRegularClassSymbol,
134+
outerClassEffectiveVisibility: EffectiveVisibility,
130135
): FirDeserializationContext = createRootContext(
131136
nameResolver,
132137
TypeTable(classProto.typeTable),
@@ -140,6 +145,7 @@ class FirDeserializationContext(
140145
classProto.typeParameterList,
141146
containerSource,
142147
outerClassSymbol,
148+
outerClassEffectiveVisibility,
143149
outerClassSymbol
144150
)
145151

@@ -156,6 +162,7 @@ class FirDeserializationContext(
156162
typeParameterProtos: List<ProtoBuf.TypeParameter>,
157163
containerSource: DeserializedContainerSource?,
158164
outerClassSymbol: FirRegularClassSymbol?,
165+
outerClassEffectiveVisibility: EffectiveVisibility,
159166
containingDeclarationSymbol: FirBasedSymbol<*>?
160167
): FirDeserializationContext {
161168
return FirDeserializationContext(
@@ -209,10 +216,10 @@ class FirMemberDeserializer(private val c: FirDeserializationContext) {
209216
origin = FirDeclarationOrigin.Library
210217
this.name = name
211218
val visibility = ProtoEnumFlags.visibility(Flags.VISIBILITY.get(flags))
212-
status = FirResolvedDeclarationStatusImpl(
219+
status = FirResolvedDeclarationStatusWithLazyEffectiveVisibility(
213220
visibility,
214221
Modality.FINAL,
215-
visibility.toEffectiveVisibility(owner = null)
222+
visibility.toLazyEffectiveVisibility(owner = null),
216223
).apply {
217224
isExpect = Flags.IS_EXPECT_CLASS.get(flags)
218225
isActual = false
@@ -242,15 +249,15 @@ class FirMemberDeserializer(private val c: FirDeserializationContext) {
242249
val getterFlags = if (proto.hasGetterFlags()) proto.getterFlags else defaultAccessorFlags
243250
val visibility = ProtoEnumFlags.visibility(Flags.VISIBILITY.get(getterFlags))
244251
val accessorModality = ProtoEnumFlags.modality(Flags.MODALITY.get(getterFlags))
245-
val effectiveVisibility = visibility.toEffectiveVisibility(classSymbol)
252+
val effectiveVisibility = visibility.toLazyEffectiveVisibility(classSymbol)
246253
return if (Flags.IS_NOT_DEFAULT.get(getterFlags)) {
247254
buildPropertyAccessor {
248255
moduleData = c.moduleData
249256
origin = FirDeclarationOrigin.Library
250257
this.returnTypeRef = returnTypeRef
251258
resolvePhase = FirResolvePhase.ANALYZED_DEPENDENCIES
252259
isGetter = true
253-
status = FirResolvedDeclarationStatusImpl(visibility, accessorModality, effectiveVisibility).apply {
260+
status = FirResolvedDeclarationStatusWithLazyEffectiveVisibility(visibility, accessorModality, effectiveVisibility).apply {
254261
isInline = Flags.IS_INLINE_ACCESSOR.get(getterFlags)
255262
isExternal = Flags.IS_EXTERNAL_ACCESSOR.get(getterFlags)
256263
}
@@ -262,14 +269,12 @@ class FirMemberDeserializer(private val c: FirDeserializationContext) {
262269
}
263270
} else {
264271
FirDefaultPropertyGetter(
265-
null,
272+
source = null,
266273
c.moduleData,
267274
FirDeclarationOrigin.Library,
268275
returnTypeRef,
269-
visibility,
270276
propertySymbol,
271-
propertyModality,
272-
effectiveVisibility,
277+
status = FirResolvedDeclarationStatusWithLazyEffectiveVisibility(visibility, propertyModality, effectiveVisibility),
273278
resolvePhase = FirResolvePhase.ANALYZED_DEPENDENCIES,
274279
)
275280
}.apply {
@@ -295,15 +300,15 @@ class FirMemberDeserializer(private val c: FirDeserializationContext) {
295300
val setterFlags = if (proto.hasSetterFlags()) proto.setterFlags else defaultAccessorFlags
296301
val visibility = ProtoEnumFlags.visibility(Flags.VISIBILITY.get(setterFlags))
297302
val accessorModality = ProtoEnumFlags.modality(Flags.MODALITY.get(setterFlags))
298-
val effectiveVisibility = visibility.toEffectiveVisibility(classSymbol)
303+
val effectiveVisibility = visibility.toLazyEffectiveVisibility(classSymbol)
299304
return if (Flags.IS_NOT_DEFAULT.get(setterFlags)) {
300305
buildPropertyAccessor {
301306
moduleData = c.moduleData
302307
origin = FirDeclarationOrigin.Library
303308
this.returnTypeRef = FirImplicitUnitTypeRef(source)
304309
resolvePhase = FirResolvePhase.ANALYZED_DEPENDENCIES
305310
isGetter = false
306-
status = FirResolvedDeclarationStatusImpl(visibility, accessorModality, effectiveVisibility).apply {
311+
status = FirResolvedDeclarationStatusWithLazyEffectiveVisibility(visibility, accessorModality, effectiveVisibility).apply {
307312
isInline = Flags.IS_INLINE_ACCESSOR.get(setterFlags)
308313
isExternal = Flags.IS_EXTERNAL_ACCESSOR.get(setterFlags)
309314
}
@@ -322,14 +327,12 @@ class FirMemberDeserializer(private val c: FirDeserializationContext) {
322327
}
323328
} else {
324329
FirDefaultPropertySetter(
325-
null,
330+
source = null,
326331
c.moduleData,
327332
FirDeclarationOrigin.Library,
328333
returnTypeRef,
329-
visibility,
330334
propertySymbol,
331-
propertyModality,
332-
effectiveVisibility,
335+
status = FirResolvedDeclarationStatusWithLazyEffectiveVisibility(visibility, propertyModality, effectiveVisibility),
333336
resolvePhase = FirResolvePhase.ANALYZED_DEPENDENCIES,
334337
)
335338
}.apply {
@@ -394,7 +397,11 @@ class FirMemberDeserializer(private val c: FirDeserializationContext) {
394397
dispatchReceiverType = c.dispatchReceiver
395398
isLocal = false
396399
val visibility = ProtoEnumFlags.visibility(Flags.VISIBILITY.get(flags))
397-
status = FirResolvedDeclarationStatusImpl(visibility, propertyModality, visibility.toEffectiveVisibility(classSymbol)).apply {
400+
status = FirResolvedDeclarationStatusWithLazyEffectiveVisibility(
401+
visibility,
402+
propertyModality,
403+
visibility.toLazyEffectiveVisibility(classSymbol)
404+
).apply {
398405
isExpect = Flags.IS_EXPECT_PROPERTY.get(flags)
399406
isActual = false
400407
isOverride = false
@@ -546,10 +553,10 @@ class FirMemberDeserializer(private val c: FirDeserializationContext) {
546553

547554
name = callableName
548555
val visibility = ProtoEnumFlags.visibility(Flags.VISIBILITY.get(flags))
549-
status = FirResolvedDeclarationStatusImpl(
556+
status = FirResolvedDeclarationStatusWithLazyEffectiveVisibility(
550557
visibility,
551558
ProtoEnumFlags.modality(Flags.MODALITY.get(flags)),
552-
visibility.toEffectiveVisibility(classSymbol)
559+
visibility.toLazyEffectiveVisibility(classSymbol)
553560
).apply {
554561
isExpect = Flags.IS_EXPECT_FUNCTION.get(flags)
555562
isActual = false
@@ -625,10 +632,10 @@ class FirMemberDeserializer(private val c: FirDeserializationContext) {
625632
returnTypeRef = delegatedSelfType
626633
val visibility = ProtoEnumFlags.visibility(Flags.VISIBILITY.get(flags))
627634
val isInner = classBuilder.status.isInner
628-
status = FirResolvedDeclarationStatusImpl(
635+
status = FirResolvedDeclarationStatusWithLazyEffectiveVisibility(
629636
visibility,
630637
Modality.FINAL,
631-
visibility.toEffectiveVisibility(classBuilder.symbol)
638+
visibility.toLazyEffectiveVisibility(classBuilder.symbol)
632639
).apply {
633640
// We don't store information about expect modifier on constructors
634641
// It is inherited from containing class
@@ -718,4 +725,40 @@ class FirMemberDeserializer(private val c: FirDeserializationContext) {
718725

719726
private fun ProtoBuf.Type.toTypeRef(context: FirDeserializationContext): FirResolvedTypeRef =
720727
context.typeDeserializer.typeRef(this)
728+
729+
private fun Visibility.toLazyEffectiveVisibility(owner: FirClassLikeSymbol<*>?): Lazy<EffectiveVisibility> {
730+
return this.toLazyEffectiveVisibility(owner, c.session, forClass = false)
731+
}
732+
}
733+
734+
fun Visibility.toLazyEffectiveVisibility(
735+
owner: FirClassLikeSymbol<*>?,
736+
session: FirSession,
737+
forClass: Boolean
738+
): Lazy<EffectiveVisibility> {
739+
/*
740+
* `lowerBound` operation for `EffectiveVisibility.Protected` involves subtyping between container classes.
741+
* In some cases, during deserialization, this subtyping might lead to the infinite recursion.
742+
* Consider the following example:
743+
*
744+
* ```
745+
* class Outer {
746+
* protected class Inner(protected val x: Any)
747+
* }
748+
* ```
749+
*
750+
* Here `Inner` class has effective visibility `protected (in Outer)` and `x` has `protected (in Inner)`.
751+
* So to perform the `lowerBound` operation between these two visibilities, the compiler needs to check the
752+
* subtyping between the `Outer` and `Inner`. BUT this happens during the deserialization in the following chain:
753+
* `deserialize Outer -> deserialize Inner -> deserialize x`, and no class symbols are published yet (neither
754+
* FIR element for them is created). So when subtyping tries to access supertypes of any of these classes, it triggers
755+
* deserialization once again which leads to stack overflow eventually.
756+
*
757+
* Due to this situation, we cannot compute the effective visibility eagerly, so we postpone its computation
758+
*/
759+
return lazy(LazyThreadSafetyMode.PUBLICATION) l@{
760+
val selfEffectiveVisibility = this.toEffectiveVisibility(owner, forClass = forClass)
761+
val parentEffectiveVisibility = owner?.effectiveVisibility ?: EffectiveVisibility.Public
762+
parentEffectiveVisibility.lowerBound(selfEffectiveVisibility, session.typeContext)
763+
}
721764
}

compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirStatusResolver.kt

+2-6
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import org.jetbrains.kotlin.descriptors.*
1010
import org.jetbrains.kotlin.fir.FirSession
1111
import org.jetbrains.kotlin.fir.declarations.*
1212
import org.jetbrains.kotlin.fir.declarations.impl.FirDeclarationStatusImpl
13-
import org.jetbrains.kotlin.fir.declarations.impl.FirResolvedDeclarationStatusImpl
1413
import org.jetbrains.kotlin.fir.declarations.utils.*
1514
import org.jetbrains.kotlin.fir.extensions.FirStatusTransformerExtension
1615
import org.jetbrains.kotlin.fir.extensions.extensionService
@@ -241,9 +240,6 @@ class FirStatusResolver(
241240
): FirResolvedDeclarationStatus {
242241
if (status is FirResolvedDeclarationStatus) return status
243242
require(status is FirDeclarationStatusImpl)
244-
245-
@Suppress("UNCHECKED_CAST")
246-
overriddenStatuses as List<FirResolvedDeclarationStatusImpl>
247243
val visibility = when (status.visibility) {
248244
Visibilities.Unknown -> when {
249245
isLocal -> Visibilities.Local
@@ -262,7 +258,7 @@ class FirStatusResolver(
262258
if (overriddenStatuses.isNotEmpty()) {
263259
for (modifier in MODIFIERS_FROM_OVERRIDDEN) {
264260
status[modifier] = status[modifier] || overriddenStatuses.fold(false) { acc, overriddenStatus ->
265-
acc || overriddenStatus[modifier]
261+
acc || (overriddenStatus as FirDeclarationStatusImpl)[modifier]
266262
}
267263
}
268264
status[FirDeclarationStatusImpl.Modifier.OVERRIDE] = true
@@ -312,7 +308,7 @@ class FirStatusResolver(
312308
declaration: FirDeclaration,
313309
containingClass: FirClass?,
314310
containingProperty: FirProperty?,
315-
overriddenStatuses: List<FirResolvedDeclarationStatusImpl>
311+
overriddenStatuses: List<FirResolvedDeclarationStatus>
316312
): Visibility {
317313
if (declaration is FirConstructor && containingClass?.hasPrivateConstructor() == true) return Visibilities.Private
318314

compiler/fir/tree/src/org/jetbrains/kotlin/fir/EnumClassUtils.kt

+14-5
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import org.jetbrains.kotlin.fir.declarations.builder.buildValueParameter
2222
import org.jetbrains.kotlin.fir.declarations.impl.FirDeclarationStatusImpl
2323
import org.jetbrains.kotlin.fir.declarations.impl.FirDefaultPropertyGetter
2424
import org.jetbrains.kotlin.fir.declarations.impl.FirResolvedDeclarationStatusImpl
25+
import org.jetbrains.kotlin.fir.declarations.impl.FirResolvedDeclarationStatusWithLazyEffectiveVisibility
2526
import org.jetbrains.kotlin.fir.expressions.builder.buildEmptyExpressionBlock
2627
import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol
2728
import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol
@@ -257,11 +258,19 @@ fun generateEntriesGetter(
257258
}
258259
}
259260

261+
@OptIn(FirImplementationDetail::class)
260262
private fun createStatus(parentStatus: FirDeclarationStatus): FirDeclarationStatusImpl {
261-
val parentEffectiveVisibility = (parentStatus as? FirResolvedDeclarationStatusImpl)?.effectiveVisibility
262-
return if (parentEffectiveVisibility != null) {
263-
FirResolvedDeclarationStatusImpl(Visibilities.Public, Modality.FINAL, parentEffectiveVisibility)
264-
} else {
265-
FirDeclarationStatusImpl(Visibilities.Public, Modality.FINAL)
263+
return when (parentStatus) {
264+
is FirResolvedDeclarationStatusImpl -> FirResolvedDeclarationStatusImpl(
265+
Visibilities.Public,
266+
Modality.FINAL,
267+
parentStatus.effectiveVisibility
268+
)
269+
is FirResolvedDeclarationStatusWithLazyEffectiveVisibility -> FirResolvedDeclarationStatusWithLazyEffectiveVisibility(
270+
Visibilities.Public,
271+
Modality.FINAL,
272+
parentStatus.lazyEffectiveVisibility,
273+
)
274+
else -> FirDeclarationStatusImpl(Visibilities.Public, Modality.FINAL)
266275
}
267276
}

0 commit comments

Comments
 (0)