[FIR2IR] Extract logic of IR declarations generation into separate component. Part 14

Decompose creation, processing, and caching local classes between classifier
  storage and classifiers generator
This commit is contained in:
Dmitriy Novozhilov
2023-08-29 15:06:00 +03:00
committed by Space Team
parent 41fa276022
commit eb9c732527
3 changed files with 81 additions and 41 deletions
@@ -5,11 +5,15 @@
package org.jetbrains.kotlin.fir.backend
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.descriptors.Visibility
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.declarations.utils.*
import org.jetbrains.kotlin.fir.declarations.utils.visibility
import org.jetbrains.kotlin.fir.expressions.FirAnonymousObjectExpression
import org.jetbrains.kotlin.fir.lazy.Fir2IrLazyClass
import org.jetbrains.kotlin.fir.resolve.providers.firProvider
import org.jetbrains.kotlin.fir.resolve.providers.symbolProvider
import org.jetbrains.kotlin.fir.resolve.providers.toSymbol
import org.jetbrains.kotlin.fir.resolve.toSymbol
import org.jetbrains.kotlin.fir.symbols.ConeClassLikeLookupTag
@@ -19,8 +23,12 @@ import org.jetbrains.kotlin.fir.types.FirTypeRef
import org.jetbrains.kotlin.fir.types.toLookupTag
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.symbols.*
import org.jetbrains.kotlin.ir.symbols.impl.*
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
import org.jetbrains.kotlin.ir.symbols.IrSymbol
import org.jetbrains.kotlin.ir.symbols.IrSymbolInternals
import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol
import org.jetbrains.kotlin.ir.symbols.impl.IrFieldSymbolImpl
import org.jetbrains.kotlin.ir.symbols.impl.IrTypeParameterSymbolImpl
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.name.SpecialNames
@@ -49,6 +57,10 @@ class Fir2IrClassifierStorage(
commonMemberStorage.localClassCache
)
private val localClassesCreatedOnTheFly: MutableMap<FirClass, IrClass> = mutableMapOf()
private var processMembersOfClassesOnTheFlyImmediately = false
private fun FirTypeRef.toIrType(typeOrigin: ConversionTypeOrigin = ConversionTypeOrigin.DEFAULT): IrType =
with(typeConverter) { toIrType(typeOrigin) }
@@ -268,7 +280,57 @@ class Fir2IrClassifierStorage(
}
fun getIrClassSymbol(firClassSymbol: FirClassSymbol<*>): IrClassSymbol {
return classifiersGenerator.createIrClassSymbolByFirSymbol(firClassSymbol)
val firClass = firClassSymbol.fir
classifierStorage.getCachedIrClass(firClass)?.let { return it.symbol }
if (firClass is FirAnonymousObject || firClass is FirRegularClass && firClass.visibility == Visibilities.Local) {
return createAndCacheLocalIrClassOnTheFly(firClass).symbol
}
firClass as FirRegularClass
val classId = firClassSymbol.classId
val parentId = classId.outerClassId
val parentClass = parentId?.let { session.symbolProvider.getClassLikeSymbolByClassId(it) }
val irParent = declarationStorage.findIrParent(classId.packageFqName, parentClass?.toLookupTag(), firClassSymbol, firClass.origin)!!
// firClass may be referenced by some parent's type parameters as a bound. In that case, getIrClassSymbol will be called recursively.
classifierStorage.getCachedIrClass(firClass)?.let { return it.symbol }
val irClass = lazyDeclarationsGenerator.createIrLazyClass(firClass, irParent)
classCache[firClass] = irClass
// NB: this is needed to prevent recursions in case of self bounds
(irClass as Fir2IrLazyClass).prepareTypeParameters()
return irClass.symbol
}
private fun createAndCacheLocalIrClassOnTheFly(klass: FirClass): IrClass {
val (irClass, firClassOrLocalParent, irClassOrLocalParent) = classifiersGenerator.createLocalIrClassOnTheFly(klass, processMembersOfClassesOnTheFlyImmediately)
if (!processMembersOfClassesOnTheFlyImmediately) {
localClassesCreatedOnTheFly[firClassOrLocalParent] = irClassOrLocalParent
}
return irClass
}
// Note: this function is called exactly once, right after Fir2IrConverter finished f/o binding for regular classes
fun processMembersOfClassesCreatedOnTheFly() {
// After the call of this function, members of local classes may be processed immediately
// Before the call it's not possible, because f/o binding for regular classes isn't done yet
processMembersOfClassesOnTheFlyImmediately = true
for ((klass, irClass) in localClassesCreatedOnTheFly) {
converter.processClassMembers(klass, irClass)
// See the problem from KT-57441
// class Wrapper {
// private val dummy = object: Bar {}
// private val bar = object: Bar by dummy {}
// }
// interface Bar {
// val foo: String
// get() = ""
// }
// When we are building bar.foo fake override, we should call dummy.foo,
// so we should have object : Bar.foo fake override to be built and bound.
converter.bindFakeOverridesInClass(irClass)
}
localClassesCreatedOnTheFly.clear()
}
fun getIrTypeParameterSymbol(
@@ -92,7 +92,7 @@ class Fir2IrConverter(
postponedDeclarationsForFakeOverridesBinding.clear()
// Do (3) and (4) for local classes encountered during (3)
classifiersGenerator.processMembersOfClassesCreatedOnTheFly()
classifierStorage.processMembersOfClassesCreatedOnTheFly()
// 5. Body processing
// If we encounter local class / anonymous object here, then we perform all (1)-(5) stages immediately
@@ -14,12 +14,10 @@ import org.jetbrains.kotlin.fir.expressions.FirAnonymousObjectExpression
import org.jetbrains.kotlin.fir.lazy.Fir2IrLazyClass
import org.jetbrains.kotlin.fir.resolve.getSymbolByLookupTag
import org.jetbrains.kotlin.fir.resolve.providers.firProvider
import org.jetbrains.kotlin.fir.resolve.providers.symbolProvider
import org.jetbrains.kotlin.fir.symbols.ConeClassLikeLookupTag
import org.jetbrains.kotlin.fir.symbols.Fir2IrClassSymbol
import org.jetbrains.kotlin.fir.symbols.Fir2IrEnumEntrySymbol
import org.jetbrains.kotlin.fir.symbols.Fir2IrTypeAliasSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirClassSymbol
import org.jetbrains.kotlin.fir.types.toLookupTag
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
import org.jetbrains.kotlin.ir.declarations.*
@@ -34,10 +32,6 @@ import org.jetbrains.kotlin.name.SpecialNames
import org.jetbrains.kotlin.utils.addToStdlib.runUnless
class Fir2IrClassifiersGenerator(val components: Fir2IrComponents) : Fir2IrComponents by components {
private val localClassesCreatedOnTheFly: MutableMap<FirClass, IrClass> = mutableMapOf()
private var processMembersOfClassesOnTheFlyImmediately = false
internal fun setTypeParameters(
irOwner: IrTypeParametersContainer,
owner: FirTypeParameterRefsOwner,
@@ -113,10 +107,15 @@ class Fir2IrClassifiersGenerator(val components: Fir2IrComponents) : Fir2IrCompo
return false
}
// TODO: it's worth to move this onTheFly stuff back to classifier storage
data class LocalIrClassInfo(
val irClass: IrClass,
val firClassOrLocalParent: FirClass,
val irClassOrLocalParent: IrClass,
)
// This function is called when we refer local class earlier than we reach its declaration
// This can happen e.g. when implicit return type has a local class constructor
private fun createLocalIrClassOnTheFly(klass: FirClass): IrClass {
fun createLocalIrClassOnTheFly(klass: FirClass, processMembersOfClassesOnTheFlyImmediately: Boolean): LocalIrClassInfo {
// finding the parent class that actually contains the [klass] in the tree - it is the root one that should be created on the fly
val classOrLocalParent = generateSequence(klass) { c ->
(c as? FirRegularClass)?.containingClassForLocalAttr?.let { lookupTag ->
@@ -132,35 +131,14 @@ class Fir2IrClassifiersGenerator(val components: Fir2IrComponents) : Fir2IrCompo
if (processMembersOfClassesOnTheFlyImmediately) {
converter.processClassMembers(classOrLocalParent, result)
converter.bindFakeOverridesInClass(result)
}
val irClass = if (classOrLocalParent === klass) {
result
} else {
localClassesCreatedOnTheFly[classOrLocalParent] = result
classifierStorage.getCachedIrClass(klass)
?: error("Assuming that all nested classes of ${classOrLocalParent.classId.asString()} should already be cached")
}
return if (classOrLocalParent === klass) result
else (classifierStorage.getCachedIrClass(klass)
?: error("Assuming that all nested classes of ${classOrLocalParent.classId.asString()} should already be cached"))
}
// Note: this function is called exactly once, right after Fir2IrConverter finished f/o binding for regular classes
fun processMembersOfClassesCreatedOnTheFly() {
// After the call of this function, members of local classes may be processed immediately
// Before the call it's not possible, because f/o binding for regular classes isn't done yet
processMembersOfClassesOnTheFlyImmediately = true
for ((klass, irClass) in localClassesCreatedOnTheFly) {
converter.processClassMembers(klass, irClass)
// See the problem from KT-57441
// class Wrapper {
// private val dummy = object: Bar {}
// private val bar = object: Bar by dummy {}
// }
// interface Bar {
// val foo: String
// get() = ""
// }
// When we are building bar.foo fake override, we should call dummy.foo,
// so we should have object : Bar.foo fake override to be built and bound.
converter.bindFakeOverridesInClass(irClass)
}
localClassesCreatedOnTheFly.clear()
return LocalIrClassInfo(irClass, classOrLocalParent, result)
}
fun processClassHeader(klass: FirClass, irClass: IrClass = classifierStorage.getCachedIrClass(klass)!!): IrClass {