From 9cf1d434652f1c4ddb3839465a3292e1e16e2d6f Mon Sep 17 00:00:00 2001 From: Dmitriy Novozhilov Date: Thu, 31 Aug 2023 12:10:34 +0300 Subject: [PATCH] [FIR2IR] Properly treat synthetic properties in Fir2IrDeclarationStorage There is a difference in how FIR and IR treat synthetic properties (properties built upon java getter + optional java setter) For FIR they are really synthetic and exist only during call resolution, so FIR creates a new instance of FirSyntheticProperty each time it resolves some call to such property In IR synthetic properties are fair properties that are present in IR Java classes This leads to the situation when synthetic property does not have a stable key (because FIR instance is new each time) and the only source of truth is a symbol table. To fix it (and avoid using symbol table as a storage), a pair of original getter and setter is used as a key for storage IR for synthetic properties. And to avoid introducing special cache of FirSyntheticPropertyKey -> IrProperty additional mapping level is introduced - FirSyntheticPropertyKey is mapped to the first FIR synthetic property which was processed by FIR2IR - this property is mapped to IrProperty using regular propertyCache ^KT-61637 --- .../fir/backend/Fir2IrDeclarationStorage.kt | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/Fir2IrDeclarationStorage.kt b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/Fir2IrDeclarationStorage.kt index fee84c47cce..8e80ea17cb7 100644 --- a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/Fir2IrDeclarationStorage.kt +++ b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/Fir2IrDeclarationStorage.kt @@ -12,6 +12,7 @@ import org.jetbrains.kotlin.fileClasses.JvmFileClassUtil import org.jetbrains.kotlin.fir.* import org.jetbrains.kotlin.fir.declarations.* import org.jetbrains.kotlin.fir.declarations.builder.buildProperty +import org.jetbrains.kotlin.fir.declarations.synthetic.FirSyntheticProperty import org.jetbrains.kotlin.fir.declarations.utils.isExpect import org.jetbrains.kotlin.fir.declarations.utils.isStatic import org.jetbrains.kotlin.fir.declarations.utils.visibility @@ -409,6 +410,43 @@ class Fir2IrDeclarationStorage( // ------------------------------------ properties ------------------------------------ + /** + * There is a difference in how FIR and IR treat synthetic properties (properties built upon java getter + optional java setter) + * For FIR they are really synthetic and exist only during call resolution, so FIR creates a new instance of FirSyntheticProperty + * each time it resolves some call to such property + * In IR synthetic properties are fair properties that are present in IR Java classes + * + * This leads to the situation when synthetic property does not have a stable key (because FIR instance is new each time) and the only + * source of truth is a symbol table. To fix it (and avoid using symbol table as a storage), a pair of original getter and setter is + * used as a key for storage IR for synthetic properties. And to avoid introducing special cache of FirSyntheticPropertyKey -> IrProperty + * additional mapping level is introduced + * + * - FirSyntheticPropertyKey is mapped to the first FIR synthetic property which was processed by FIR2IR + * - this property is mapped to IrProperty using regular propertyCache + * + * IMPORTANT: this whole story requires to call [prepareProperty] or [preparePropertySymbol] in the beginning of any public method + * which accepts arbitary FirProperty or FirPropertySymbol + */ + private data class FirSyntheticPropertyKey( + val originalForGetter: FirSimpleFunction, + val originalForSetter: FirSimpleFunction?, + ) { + constructor(property: FirSyntheticProperty) : this(property.getter.delegate, property.setter?.delegate) + } + + private val originalForSyntheticProperty: ConcurrentHashMap = ConcurrentHashMap() + + private fun prepareProperty(property: FirProperty): FirProperty { + return when (property) { + is FirSyntheticProperty -> originalForSyntheticProperty.getOrPut(FirSyntheticPropertyKey(property)) { property } + else -> property + } + } + + private fun preparePropertySymbol(symbol: FirPropertySymbol): FirPropertySymbol { + return prepareProperty(symbol.fir).symbol + } + fun getOrCreateIrProperty( property: FirProperty, irParent: IrDeclarationParent?, @@ -416,6 +454,8 @@ class Fir2IrDeclarationStorage( isLocal: Boolean = false, fakeOverrideOwnerLookupTag: ConeClassLikeLookupTag? = null ): IrProperty { + @Suppress("NAME_SHADOWING") + val property = prepareProperty(property) getCachedIrProperty(property)?.let { return it } return createAndCacheIrProperty(property, irParent, predefinedOrigin, isLocal, fakeOverrideOwnerLookupTag) } @@ -442,6 +482,8 @@ class Fir2IrDeclarationStorage( isLocal: Boolean = false, fakeOverrideOwnerLookupTag: ConeClassLikeLookupTag? = null ): IrProperty { + @Suppress("NAME_SHADOWING") + val property = prepareProperty(property) val irProperty = callablesGenerator.createIrProperty(property, irParent, predefinedOrigin, isLocal, fakeOverrideOwnerLookupTag) cacheIrProperty(property, irProperty, fakeOverrideOwnerLookupTag) return irProperty @@ -499,6 +541,8 @@ class Fir2IrDeclarationStorage( firPropertySymbol: FirPropertySymbol, fakeOverrideOwnerLookupTag: ConeClassLikeLookupTag? = null, ): IrSymbol { + @Suppress("NAME_SHADOWING") + val firPropertySymbol = preparePropertySymbol(firPropertySymbol) val fir = firPropertySymbol.fir if (fir.isLocal) { return localStorage.getDelegatedProperty(fir)?.symbol ?: getIrVariableSymbol(fir) @@ -561,6 +605,8 @@ class Fir2IrDeclarationStorage( fakeOverrideOwnerLookupTag: ConeClassLikeLookupTag?, signatureCalculator: () -> IdSignature? ): IrProperty? { + @Suppress("NAME_SHADOWING") + val property = prepareProperty(property) return getCachedIrCallable( property, fakeOverrideOwnerLookupTag,