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

This commit just moves methods which create IR callable declarations
  from Fir2IrClassifierStorage to newly created Fir2IrClassifiersGenerator

Code in generators contains some errors (mostly because it refers to the
  logic of caching created declaration), and those errors will be fixed
  in the following commits
This commit is contained in:
Dmitriy Novozhilov
2023-08-29 13:28:19 +03:00
committed by Space Team
parent e2e8a9eb8f
commit 26211a0277
10 changed files with 627 additions and 552 deletions
@@ -11,8 +11,6 @@ import com.intellij.psi.tree.IElementType
import org.jetbrains.kotlin.*
import org.jetbrains.kotlin.backend.common.actualizer.IrActualizedResult
import org.jetbrains.kotlin.builtins.StandardNames.DATA_CLASS_COMPONENT_PREFIX
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.descriptors.ValueClassRepresentation
import org.jetbrains.kotlin.diagnostics.startOffsetSkippingComments
import org.jetbrains.kotlin.fir.*
@@ -32,7 +30,6 @@ import org.jetbrains.kotlin.fir.extensions.extensionService
import org.jetbrains.kotlin.fir.extensions.generatedDeclarationsSymbolProvider
import org.jetbrains.kotlin.fir.references.FirReference
import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference
import org.jetbrains.kotlin.fir.references.FirThisReference
import org.jetbrains.kotlin.fir.references.impl.FirPropertyFromParameterResolvedNamedReference
import org.jetbrains.kotlin.fir.references.toResolvedCallableSymbol
import org.jetbrains.kotlin.fir.resolve.*
@@ -153,7 +150,7 @@ fun FirClassifierSymbol<*>.toSymbol(
val coneClassLikeType = fir.expandedTypeRef.coneType as ConeClassLikeType
coneClassLikeType.lookupTag.toSymbol(session)
?.toSymbol(typeOrigin, handleAnnotations)
?: classifierStorage.getIrClassSymbolForNotFoundClass(coneClassLikeType.lookupTag)
?: classifiersGenerator.createIrClassSymbolForNotFoundClass(coneClassLikeType.lookupTag)
}
is FirClassSymbol -> {
@@ -6,38 +6,26 @@
package org.jetbrains.kotlin.fir.backend
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.fir.containingClassForLocalAttr
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.declarations.utils.*
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.resolve.providers.toSymbol
import org.jetbrains.kotlin.fir.resolve.toSymbol
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.symbols.impl.FirTypeParameterSymbol
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.declarations.impl.IrExternalPackageFragmentImpl
import org.jetbrains.kotlin.ir.symbols.*
import org.jetbrains.kotlin.ir.symbols.impl.*
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.types.impl.IrSimpleTypeImpl
import org.jetbrains.kotlin.ir.util.IdSignature
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.name.SpecialNames
import org.jetbrains.kotlin.name.StandardClassIds
import org.jetbrains.kotlin.utils.addToStdlib.runIf
import org.jetbrains.kotlin.utils.addToStdlib.runUnless
class Fir2IrClassifierStorage(
private val components: Fir2IrComponents,
@@ -45,10 +33,6 @@ class Fir2IrClassifierStorage(
) : Fir2IrComponents by components {
private val classCache: MutableMap<FirRegularClass, IrClass> = commonMemberStorage.classCache
private val localClassesCreatedOnTheFly: MutableMap<FirClass, IrClass> = mutableMapOf()
private var processMembersOfClassesOnTheFlyImmediately = false
private val typeAliasCache: MutableMap<FirTypeAlias, IrTypeAlias> = mutableMapOf()
private val typeParameterCache: MutableMap<FirTypeParameter, IrTypeParameter> = commonMemberStorage.typeParameterCache
@@ -76,7 +60,7 @@ class Fir2IrClassifierStorage(
val firClass = classId.toSymbol(session)?.fir as FirRegularClass? ?: continue
val irClass = irBuiltinSymbol.owner
classCache[firClass] = irClass
processClassHeader(firClass, irClass)
classifiersGenerator.processClassHeader(firClass, irClass)
declarationStorage.preCacheBuiltinClassMembers(firClass, irClass)
}
for ((primitiveClassId, primitiveArrayId) in StandardClassIds.primitiveArrayTypeByElementType) {
@@ -85,59 +69,32 @@ class Fir2IrClassifierStorage(
val irType = typeConverter.classIdToTypeMap[primitiveClassId]
val irClass = irBuiltIns.primitiveArrayForType[irType]!!.owner
classCache[firClass] = irClass
processClassHeader(firClass, irClass)
classifiersGenerator.processClassHeader(firClass, irClass)
declarationStorage.preCacheBuiltinClassMembers(firClass, irClass)
}
}
// Note: declareTypeParameters should be called before!
private fun IrClass.setThisReceiver(typeParameters: List<FirTypeParameterRef>) {
symbolTable.enterScope(this)
val typeArguments = typeParameters.map {
IrSimpleTypeImpl(getIrTypeParameterSymbol(it.symbol, ConversionTypeOrigin.DEFAULT), false, emptyList(), emptyList())
}
thisReceiver = declareThisReceiverParameter(
thisType = IrSimpleTypeImpl(symbol, false, typeArguments, emptyList()),
thisOrigin = IrDeclarationOrigin.INSTANCE_RECEIVER
)
symbolTable.leaveScope(this)
}
internal fun preCacheTypeParameters(owner: FirTypeParameterRefsOwner, irOwnerSymbol: IrSymbol) {
for ((index, typeParameter) in owner.typeParameters.withIndex()) {
val original = typeParameter.symbol.fir
getCachedIrTypeParameter(original)
?: createIrTypeParameterWithoutBounds(original, index, irOwnerSymbol)
?: classifiersGenerator.createIrTypeParameterWithoutBounds(original, index, irOwnerSymbol)
if (owner is FirProperty && owner.isVar) {
val context = ConversionTypeOrigin.SETTER
getCachedIrTypeParameter(original, context)
?: createIrTypeParameterWithoutBounds(original, index, irOwnerSymbol, context)
?: classifiersGenerator.createIrTypeParameterWithoutBounds(original, index, irOwnerSymbol, context)
}
}
}
internal fun IrTypeParametersContainer.setTypeParameters(
internal fun setTypeParameters(
irOwner: IrTypeParametersContainer,
owner: FirTypeParameterRefsOwner,
typeOrigin: ConversionTypeOrigin = ConversionTypeOrigin.DEFAULT
) {
typeParameters = owner.typeParameters.mapIndexedNotNull { index, typeParameter ->
if (typeParameter !is FirTypeParameter) return@mapIndexedNotNull null
getIrTypeParameter(typeParameter, index, symbol, typeOrigin).apply {
parent = this@setTypeParameters
if (superTypes.isEmpty()) {
superTypes = typeParameter.bounds.map { it.toIrType(typeOrigin) }
}
}
}
}
private fun IrClass.declareTypeParameters(klass: FirClass) {
preCacheTypeParameters(klass, symbol)
setTypeParameters(klass)
if (klass is FirRegularClass) {
val fieldsForContextReceiversOfCurrentClass = getFieldsWithContextReceiversForClass(this, klass)
declarations.addAll(fieldsForContextReceiversOfCurrentClass)
}
classifiersGenerator.setTypeParameters(irOwner, owner, typeOrigin)
}
fun getFieldsWithContextReceiversForClass(irClass: IrClass, klass: FirClass): List<IrField> {
@@ -163,16 +120,6 @@ class Fir2IrClassifierStorage(
}
}
private fun IrClass.declareSupertypes(klass: FirClass) {
superTypes = klass.superTypeRefs.map { superTypeRef -> superTypeRef.toIrType() }
}
private fun IrClass.declareValueClassRepresentation(klass: FirRegularClass) {
if (this !is Fir2IrLazyClass) {
valueClassRepresentation = computeValueClassRepresentation(klass)
}
}
fun getCachedIrClass(klass: FirClass): IrClass? {
return if (klass is FirAnonymousObject || klass is FirRegularClass && klass.visibility == Visibilities.Local) {
localStorage[klass]
@@ -189,337 +136,34 @@ class Fir2IrClassifierStorage(
return codeFragmentCache[codeFragment]
}
private fun FirRegularClass.enumClassModality(): Modality {
return when {
declarations.any { it is FirCallableDeclaration && it.modality == Modality.ABSTRACT } -> {
Modality.ABSTRACT
}
declarations.none {
it is FirEnumEntry && isEnumEntryWhichRequiresSubclass(it)
} -> {
Modality.FINAL
}
hasAbstractMembersInScope() -> {
Modality.ABSTRACT
}
else -> {
Modality.OPEN
}
}
}
private fun FirRegularClass.hasAbstractMembersInScope(): Boolean {
val scope = unsubstitutedScope()
val names = scope.getCallableNames()
var hasAbstract = false
for (name in names) {
scope.processFunctionsByName(name) {
if (it.isAbstract) {
hasAbstract = true
}
}
if (hasAbstract) return true
scope.processPropertiesByName(name) {
if (it.isAbstract) {
hasAbstract = true
}
}
if (hasAbstract) return true
}
return false
}
// 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 {
// 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 ->
(session.firProvider.symbolProvider.getSymbolByLookupTag(lookupTag)?.fir as? FirClass)?.takeIf {
it.declarations.contains(c)
}
}
}.last()
val result = converter.processLocalClassAndNestedClassesOnTheFly(classOrLocalParent, temporaryParent)
// Note: usually member creation and f/o binding is delayed till non-local classes are processed in Fir2IrConverter
// If non-local classes are already created (this means we are in body translation) we do everything immediately
// The last variant is possible for local variables like 'val a = object : Any() { ... }'
if (processMembersOfClassesOnTheFlyImmediately) {
converter.processClassMembers(classOrLocalParent, result)
converter.bindFakeOverridesInClass(result)
} else {
localClassesCreatedOnTheFly[classOrLocalParent] = result
}
return if (classOrLocalParent === klass) result
else (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()
}
fun processClassHeader(klass: FirClass, irClass: IrClass = getCachedIrClass(klass)!!): IrClass {
irClass.declareTypeParameters(klass)
irClass.setThisReceiver(klass.typeParameters)
irClass.declareSupertypes(klass)
if (klass is FirRegularClass) {
irClass.declareValueClassRepresentation(klass)
}
return irClass
}
private fun declareIrTypeAlias(signature: IdSignature?, factory: (IrTypeAliasSymbol) -> IrTypeAlias): IrTypeAlias =
if (signature == null)
factory(IrTypeAliasSymbolImpl())
else
symbolTable.declareTypeAlias(signature, { Fir2IrTypeAliasSymbol(signature) }, factory)
fun registerTypeAlias(
fun createAndCacheIrTypeAlias(
typeAlias: FirTypeAlias,
parent: IrDeclarationParent
): IrTypeAlias {
val signature = signatureComposer.composeSignature(typeAlias)
return typeAlias.convertWithOffsets { startOffset, endOffset ->
declareIrTypeAlias(signature) { symbol ->
preCacheTypeParameters(typeAlias, symbol)
val irTypeAlias = irFactory.createTypeAlias(
startOffset = startOffset,
endOffset = endOffset,
origin = IrDeclarationOrigin.DEFINED,
name = typeAlias.name,
visibility = components.visibilityConverter.convertToDescriptorVisibility(typeAlias.visibility),
symbol = symbol,
isActual = typeAlias.isActual,
expandedType = typeAlias.expandedTypeRef.toIrType(),
).apply {
this.parent = parent
setTypeParameters(typeAlias)
if (parent is IrFile) {
parent.declarations += this
}
}
typeAliasCache[typeAlias] = irTypeAlias
irTypeAlias
}
}
return classifiersGenerator.createIrTypeAlias(typeAlias, parent)
}
internal fun getCachedTypeAlias(firTypeAlias: FirTypeAlias): IrTypeAlias? = typeAliasCache[firTypeAlias]
private fun declareIrClass(signature: IdSignature?, factory: (IrClassSymbol) -> IrClass): IrClass =
if (signature == null)
factory(IrClassSymbolImpl())
else
symbolTable.declareClass(signature, { Fir2IrClassSymbol(signature) }, factory)
fun registerIrClass(
fun createAndCacheIrClass(
regularClass: FirRegularClass,
parent: IrDeclarationParent,
predefinedOrigin: IrDeclarationOrigin? = null
): IrClass {
val visibility = regularClass.visibility
val modality = when (regularClass.classKind) {
ClassKind.ENUM_CLASS -> regularClass.enumClassModality()
ClassKind.ANNOTATION_CLASS -> Modality.OPEN
else -> regularClass.modality ?: Modality.FINAL
}
val signature = runUnless(regularClass.isLocal || !configuration.linkViaSignatures) {
signatureComposer.composeSignature(regularClass)
}
val irClass = regularClass.convertWithOffsets { startOffset, endOffset ->
declareIrClass(signature) { symbol ->
irFactory.createClass(
startOffset = startOffset,
endOffset = endOffset,
origin = regularClass.computeIrOrigin(predefinedOrigin),
name = regularClass.name,
visibility = components.visibilityConverter.convertToDescriptorVisibility(visibility),
symbol = symbol,
kind = regularClass.classKind,
modality = modality,
isExternal = regularClass.isExternal,
isCompanion = regularClass.isCompanion,
isInner = regularClass.isInner,
isData = regularClass.isData,
isValue = regularClass.isInline,
isExpect = regularClass.isExpect,
isFun = regularClass.isFun
).apply {
metadata = FirMetadataSource.Class(regularClass)
}
}
}
irClass.parent = parent
if (regularClass.visibility == Visibilities.Local) {
localStorage[regularClass] = irClass
} else {
classCache[regularClass] = irClass
}
return irClass
return classifiersGenerator.createIrClass(regularClass, parent, predefinedOrigin)
}
fun registerIrAnonymousObject(
fun createAndCacheAnonymousObject(
anonymousObject: FirAnonymousObject,
visibility: Visibility = Visibilities.Local,
name: Name = SpecialNames.NO_NAME_PROVIDED,
irParent: IrDeclarationParent? = null
): IrClass {
val origin = IrDeclarationOrigin.DEFINED
val modality = Modality.FINAL
val irAnonymousObject = anonymousObject.convertWithOffsets { startOffset, endOffset ->
irFactory.createClass(
startOffset = startOffset,
endOffset = endOffset,
origin = origin,
name = name,
visibility = components.visibilityConverter.convertToDescriptorVisibility(visibility),
symbol = IrClassSymbolImpl(),
kind = anonymousObject.classKind,
modality = modality,
).apply {
metadata = FirMetadataSource.Class(anonymousObject)
}
}
if (irParent != null) {
irAnonymousObject.parent = irParent
}
localStorage[anonymousObject] = irAnonymousObject
return irAnonymousObject
return classifiersGenerator.createAnonymousObject(anonymousObject, visibility, name, irParent)
}
fun registerCodeFragmentClass(codeFragment: FirCodeFragment, containingFile: IrFile): IrClass {
val conversionData = codeFragment.conversionData
val signature = signatureComposer.composeSignature(codeFragment)
val irClass = codeFragment.convertWithOffsets { startOffset, endOffset ->
declareIrClass(signature) { symbol ->
irFactory.createClass(
startOffset,
endOffset,
IrDeclarationOrigin.DEFINED,
conversionData.classId.shortClassName,
DescriptorVisibilities.PUBLIC,
symbol,
ClassKind.CLASS,
Modality.FINAL,
isExternal = false,
isCompanion = false,
isInner = false,
isData = false,
isValue = false,
isExpect = false,
isFun = false
).apply {
metadata = FirMetadataSource.CodeFragment(codeFragment)
parent = containingFile
typeParameters = emptyList()
thisReceiver = declareThisReceiverParameter(
thisType = IrSimpleTypeImpl(symbol, false, emptyList(), emptyList()),
thisOrigin = IrDeclarationOrigin.INSTANCE_RECEIVER
)
superTypes = listOf(irBuiltIns.anyType)
}
}
}
codeFragmentCache[codeFragment] = irClass
return irClass
}
private fun getIrAnonymousObjectForEnumEntry(anonymousObject: FirAnonymousObject, name: Name, irParent: IrClass?): IrClass {
localStorage[anonymousObject]?.let { return it }
val irAnonymousObject = registerIrAnonymousObject(anonymousObject, Visibilities.Private, name, irParent)
processClassHeader(anonymousObject, irAnonymousObject)
return irAnonymousObject
}
private fun createIrTypeParameterWithoutBounds(
typeParameter: FirTypeParameter,
index: Int,
ownerSymbol: IrSymbol,
typeOrigin: ConversionTypeOrigin = ConversionTypeOrigin.DEFAULT,
): IrTypeParameter {
require(index >= 0)
val origin = typeParameter.computeIrOrigin()
val irTypeParameter = with(typeParameter) {
convertWithOffsets { startOffset, endOffset ->
signatureComposer.composeTypeParameterSignature(
index, ownerSymbol.signature
)?.let { signature ->
if (ownerSymbol is IrClassifierSymbol) {
symbolTable.declareGlobalTypeParameter(
signature,
symbolFactory = { IrTypeParameterPublicSymbolImpl(signature) }
) { symbol ->
irFactory.createTypeParameter(
startOffset = startOffset,
endOffset = endOffset,
origin = origin,
name = name,
symbol = symbol,
variance = variance,
index = if (index < 0) 0 else index,
isReified = isReified,
)
}
} else {
symbolTable.declareScopedTypeParameter(
signature,
symbolFactory = { IrTypeParameterPublicSymbolImpl(signature) }
) { symbol ->
irFactory.createTypeParameter(
startOffset = startOffset,
endOffset = endOffset,
origin = origin,
name = name,
symbol = symbol,
variance = variance,
index = if (index < 0) 0 else index,
isReified = isReified,
)
}
}
} ?: irFactory.createTypeParameter(
startOffset = startOffset,
endOffset = endOffset,
origin = origin,
name = name,
symbol = IrTypeParameterSymbolImpl(),
variance = variance,
index = if (index < 0) 0 else index,
isReified = isReified,
)
}
}
// Cache the type parameter BEFORE processing its bounds/supertypes, to properly handle recursive type bounds.
if (typeOrigin.forSetter) {
typeParameterCacheForSetter[typeParameter] = irTypeParameter
} else {
typeParameterCache[typeParameter] = irTypeParameter
}
annotationGenerator.generate(irTypeParameter, typeParameter)
return irTypeParameter
fun createAndCacheCodeFragmentClass(codeFragment: FirCodeFragment, containingFile: IrFile): IrClass {
return classifiersGenerator.createCodeFragmentClass(codeFragment, containingFile)
}
internal fun getCachedIrTypeParameter(
@@ -539,11 +183,7 @@ class Fir2IrClassifierStorage(
typeOrigin: ConversionTypeOrigin = ConversionTypeOrigin.DEFAULT
): IrTypeParameter {
getCachedIrTypeParameter(typeParameter, typeOrigin)?.let { return it }
return typeParameter.run {
val irTypeParameter = createIrTypeParameterWithoutBounds(typeParameter, index, ownerSymbol, typeOrigin)
irTypeParameter.superTypes = bounds.map { it.toIrType() }
irTypeParameter
}
return classifiersGenerator.createIrTypeParameterWithBounds(typeParameter, index, ownerSymbol, typeOrigin)
}
fun putEnumEntryClassInScope(enumEntry: FirEnumEntry, correspondingClass: IrClass) {
@@ -552,12 +192,6 @@ class Fir2IrClassifierStorage(
internal fun getCachedIrEnumEntry(enumEntry: FirEnumEntry): IrEnumEntry? = enumEntryCache[enumEntry]
private fun declareIrEnumEntry(signature: IdSignature?, factory: (IrEnumEntrySymbol) -> IrEnumEntry): IrEnumEntry =
if (signature == null)
factory(IrEnumEntrySymbolImpl())
else
symbolTable.declareEnumEntry(signature, { Fir2IrEnumEntrySymbol(signature) }, factory)
fun getIrEnumEntry(
enumEntry: FirEnumEntry,
irParent: IrClass,
@@ -574,7 +208,7 @@ class Fir2IrClassifierStorage(
} else {
irParent.origin
}
return createIrEnumEntry(
return classifiersGenerator.createIrEnumEntry(
enumEntry,
irParent = irParent,
predefinedOrigin = predefinedOrigin
@@ -595,110 +229,8 @@ class Fir2IrClassifierStorage(
}
}
fun createIrEnumEntry(
enumEntry: FirEnumEntry,
irParent: IrClass?,
predefinedOrigin: IrDeclarationOrigin? = null,
): IrEnumEntry {
return enumEntry.convertWithOffsets { startOffset, endOffset ->
val signature = signatureComposer.composeSignature(enumEntry)
val result = declareIrEnumEntry(signature) { symbol ->
val origin = enumEntry.computeIrOrigin(predefinedOrigin)
irFactory.createEnumEntry(
startOffset = startOffset,
endOffset = endOffset,
origin = origin,
name = enumEntry.name,
symbol = symbol,
).apply {
declarationStorage.enterScope(this.symbol)
if (irParent != null) {
this.parent = irParent
}
if (isEnumEntryWhichRequiresSubclass(enumEntry)) {
// An enum entry with its own members requires an anonymous object generated.
// Otherwise, this is a default-ish enum entry whose initializer would be a delegating constructor call,
// which will be translated via visitor later.
val klass = getIrAnonymousObjectForEnumEntry(
(enumEntry.initializer as FirAnonymousObjectExpression).anonymousObject, enumEntry.name, irParent
)
this.correspondingClass = klass
}
declarationStorage.leaveScope(this.symbol)
}
}
enumEntryCache[enumEntry] = result
result
}
}
private fun isEnumEntryWhichRequiresSubclass(enumEntry: FirEnumEntry): Boolean {
val initializer = enumEntry.initializer
return initializer is FirAnonymousObjectExpression && initializer.anonymousObject.declarations.any { it !is FirConstructor }
}
fun getIrClassSymbol(firClassSymbol: FirClassSymbol<*>): IrClassSymbol {
val firClass = firClassSymbol.fir
getCachedIrClass(firClass)?.let { return it.symbol }
if (firClass is FirAnonymousObject || firClass is FirRegularClass && firClass.visibility == Visibilities.Local) {
return createLocalIrClassOnTheFly(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.
getCachedIrClass(firClass)?.let { return it.symbol }
val signature = runIf(configuration.linkViaSignatures) {
signatureComposer.composeSignature(firClass)
}
val irClass = firClass.convertWithOffsets { startOffset, endOffset ->
declareIrClass(signature) { irClassSymbol ->
val firClassOrigin = firClass.irOrigin(session.firProvider)
Fir2IrLazyClass(components, startOffset, endOffset, firClassOrigin, firClass, irClassSymbol).apply {
parent = irParent
}
}
}
classCache[firClass] = irClass
// NB: this is needed to prevent recursions in case of self bounds
(irClass as Fir2IrLazyClass).prepareTypeParameters()
return irClass.symbol
}
@OptIn(IrSymbolInternals::class)
fun getIrClassSymbolForNotFoundClass(classLikeLookupTag: ConeClassLikeLookupTag): IrClassSymbol {
val classId = classLikeLookupTag.classId
val signature = IdSignature.CommonSignature(
packageFqName = classId.packageFqName.asString(),
declarationFqName = classId.relativeClassName.asString(),
id = 0,
mask = 0,
description = null,
)
val parentId = classId.outerClassId
val parentClass = parentId?.let { getIrClassSymbolForNotFoundClass(it.toLookupTag()) }
val irParent = parentClass?.owner ?: declarationStorage.getIrExternalPackageFragment(classId.packageFqName)
return symbolTable.referenceClass(signature, { Fir2IrClassSymbol(signature) }) {
irFactory.createClass(
startOffset = UNDEFINED_OFFSET,
endOffset = UNDEFINED_OFFSET,
origin = IrDeclarationOrigin.IR_EXTERNAL_DECLARATION_STUB,
name = classId.shortClassName,
visibility = DescriptorVisibilities.DEFAULT_VISIBILITY,
symbol = it,
kind = ClassKind.CLASS,
modality = Modality.FINAL,
).apply {
parent = irParent
}
}
return classifiersGenerator.createIrClassSymbolByFirSymbol(firClassSymbol)
}
fun getIrTypeParameterSymbol(
@@ -715,40 +247,9 @@ class Fir2IrClassifierStorage(
}
if (components.configuration.allowNonCachedDeclarations) {
val firTypeParameterOwnerSymbol = firTypeParameter.containingDeclarationSymbol
val firTypeParameterOwner = firTypeParameterOwnerSymbol.fir as FirTypeParameterRefsOwner
val index = firTypeParameterOwner.typeParameters.indexOf(firTypeParameter).also { check(it >= 0) }
val isSetter = firTypeParameterOwner is FirPropertyAccessor && firTypeParameterOwner.isSetter
val conversionTypeOrigin = if (isSetter) ConversionTypeOrigin.SETTER else ConversionTypeOrigin.DEFAULT
return createIrTypeParameterWithoutBounds(firTypeParameter, index, IrTypeParameterSymbolImpl(), conversionTypeOrigin).apply {
superTypes = firTypeParameter.bounds.map { it.toIrType(typeConverter) }
}.symbol
return classifiersGenerator.createIrTypeParameterForNonCachedDeclaration(firTypeParameter)
}
error("Cannot find cached type parameter by FIR symbol: ${firTypeParameterSymbol.name} of the owner: ${firTypeParameter.containingDeclarationSymbol}")
}
private val temporaryParent by lazy {
irFactory.createSimpleFunction(
startOffset = UNDEFINED_OFFSET,
endOffset = UNDEFINED_OFFSET,
origin = IrDeclarationOrigin.DEFINED,
name = Name.special("<stub>"),
visibility = DescriptorVisibilities.PRIVATE,
isInline = false,
isExpect = false,
returnType = irBuiltIns.unitType,
modality = Modality.FINAL,
symbol = IrSimpleFunctionSymbolImpl(),
isTailrec = false,
isSuspend = false,
isOperator = false,
isInfix = false,
isExternal = false,
).apply {
parent = IrExternalPackageFragmentImpl(IrExternalPackageFragmentSymbolImpl(), FqName.ROOT)
}
}
}
@@ -35,6 +35,7 @@ interface Fir2IrComponents {
val visibilityConverter: Fir2IrVisibilityConverter
val callablesGenerator: Fir2IrCallableDeclarationsGenerator
val classifiersGenerator: Fir2IrClassifiersGenerator
val lazyDeclarationsGenerator: Fir2IrLazyDeclarationsGenerator
val annotationGenerator: AnnotationGenerator
@@ -38,6 +38,7 @@ class Fir2IrComponentsStorage(
override val declarationStorage: Fir2IrDeclarationStorage = Fir2IrDeclarationStorage(this, moduleDescriptor, commonMemberStorage)
override val callablesGenerator: Fir2IrCallableDeclarationsGenerator = Fir2IrCallableDeclarationsGenerator(this)
override val classifiersGenerator: Fir2IrClassifiersGenerator = Fir2IrClassifiersGenerator(this)
override val lazyDeclarationsGenerator: Fir2IrLazyDeclarationsGenerator = Fir2IrLazyDeclarationsGenerator(this)
// builtins should go after storages and generators, because they use them during initialization
@@ -92,7 +92,7 @@ class Fir2IrConverter(
postponedDeclarationsForFakeOverridesBinding.clear()
// Do (3) and (4) for local classes encountered during (3)
classifierStorage.processMembersOfClassesCreatedOnTheFly()
classifiersGenerator.processMembersOfClassesCreatedOnTheFly()
// 5. Body processing
// If we encounter local class / anonymous object here, then we perform all (1)-(5) stages immediately
@@ -156,7 +156,7 @@ class Fir2IrConverter(
for (declaration in file.declarations) {
when (declaration) {
is FirRegularClass -> registerClassAndNestedClasses(declaration, irFile)
is FirCodeFragment -> classifierStorage.registerCodeFragmentClass(declaration, irFile)
is FirCodeFragment -> classifierStorage.createAndCacheCodeFragmentClass(declaration, irFile)
else -> {}
}
}
@@ -167,7 +167,7 @@ class Fir2IrConverter(
file.declarations.forEach {
when (it) {
is FirRegularClass -> processClassAndNestedClassHeaders(it)
is FirTypeAlias -> classifierStorage.registerTypeAlias(it, declarationStorage.getIrFile(file))
is FirTypeAlias -> classifierStorage.createAndCacheIrTypeAlias(it, declarationStorage.getIrFile(file))
else -> {}
}
}
@@ -373,8 +373,8 @@ class Fir2IrConverter(
classifierStorage.getCachedIrClass(klass)?.apply {
this.parent = parent
} ?: when (klass) {
is FirRegularClass -> classifierStorage.registerIrClass(klass, parent)
is FirAnonymousObject -> classifierStorage.registerIrAnonymousObject(klass, irParent = parent)
is FirRegularClass -> classifierStorage.createAndCacheIrClass(klass, parent)
is FirAnonymousObject -> classifierStorage.createAndCacheAnonymousObject(klass, irParent = parent)
}
registerNestedClasses(klass, irClass)
return irClass
@@ -396,7 +396,7 @@ class Fir2IrConverter(
}
private fun processClassAndNestedClassHeaders(klass: FirClass) {
classifierStorage.processClassHeader(klass)
classifiersGenerator.processClassHeader(klass)
processNestedClassHeaders(klass)
}
@@ -437,7 +437,7 @@ class Fir2IrConverter(
registerClassAndNestedClasses(scriptStatement, irScript)
processClassAndNestedClassHeaders(scriptStatement)
}
is FirTypeAlias -> classifierStorage.registerTypeAlias(scriptStatement, irScript)
is FirTypeAlias -> classifierStorage.createAndCacheIrTypeAlias(scriptStatement, irScript)
else -> {}
}
}
@@ -124,7 +124,7 @@ class Fir2IrTypeConverter(
?: lookupTag.toSymbol(session)?.toSymbol(typeOrigin) {
typeAnnotations += with(annotationGenerator) { it.toIrAnnotations() }
}
?: (lookupTag as? ConeClassLikeLookupTag)?.let(classifierStorage::getIrClassSymbolForNotFoundClass)
?: (lookupTag as? ConeClassLikeLookupTag)?.let(classifiersGenerator::createIrClassSymbolForNotFoundClass)
?: return createErrorType()
when {
@@ -452,11 +452,9 @@ class Fir2IrCallableDeclarationsGenerator(val components: Fir2IrComponents) : Fi
if (propertyAccessorForAnnotations != null) {
convertAnnotationsForNonDeclaredMembers(propertyAccessorForAnnotations, origin)
}
with(classifierStorage) {
setTypeParameters(
property, if (isSetter) ConversionTypeOrigin.SETTER else ConversionTypeOrigin.DEFAULT
)
}
classifiersGenerator.setTypeParameters(
this, property, if (isSetter) ConversionTypeOrigin.SETTER else ConversionTypeOrigin.DEFAULT
)
val dispatchReceiverType = computeDispatchReceiverType(this, property, irParent)
// NB: we should enter accessor' scope before declaring its parameters
// (both setter default and receiver ones, if any)
@@ -686,9 +684,7 @@ class Fir2IrCallableDeclarationsGenerator(val components: Fir2IrComponents) : Fi
val containingClass = computeContainingClass(irParent)
val parent = this
if (function is FirSimpleFunction || function is FirConstructor) {
with(classifierStorage) {
setTypeParameters(function)
}
classifiersGenerator.setTypeParameters(this, function)
}
val typeOrigin = if (forSetter) ConversionTypeOrigin.SETTER else ConversionTypeOrigin.DEFAULT
if (function is FirDefaultPropertySetter) {
@@ -0,0 +1,570 @@
/*
* Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.fir.backend.generators
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.fir.backend.*
import org.jetbrains.kotlin.fir.containingClassForLocalAttr
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.declarations.utils.*
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.*
import org.jetbrains.kotlin.ir.declarations.impl.IrExternalPackageFragmentImpl
import org.jetbrains.kotlin.ir.symbols.*
import org.jetbrains.kotlin.ir.symbols.impl.*
import org.jetbrains.kotlin.ir.types.impl.IrSimpleTypeImpl
import org.jetbrains.kotlin.ir.util.IdSignature
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
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,
typeOrigin: ConversionTypeOrigin = ConversionTypeOrigin.DEFAULT
) {
irOwner.typeParameters = owner.typeParameters.mapIndexedNotNull { index, typeParameter ->
if (typeParameter !is FirTypeParameter) return@mapIndexedNotNull null
classifierStorage.getIrTypeParameter(typeParameter, index, irOwner.symbol, typeOrigin).apply {
parent = irOwner
if (superTypes.isEmpty()) {
superTypes = typeParameter.bounds.map { it.toIrType(typeOrigin) }
}
}
}
}
private fun IrClass.declareTypeParameters(klass: FirClass) {
classifierStorage.preCacheTypeParameters(klass, symbol)
setTypeParameters(this, klass)
if (klass is FirRegularClass) {
val fieldsForContextReceiversOfCurrentClass = classifierStorage.getFieldsWithContextReceiversForClass(this, klass)
declarations.addAll(fieldsForContextReceiversOfCurrentClass)
}
}
private fun IrClass.declareSupertypes(klass: FirClass) {
superTypes = klass.superTypeRefs.map { superTypeRef -> superTypeRef.toIrType() }
}
private fun IrClass.declareValueClassRepresentation(klass: FirRegularClass) {
if (this !is Fir2IrLazyClass) {
valueClassRepresentation = computeValueClassRepresentation(klass)
}
}
private fun FirRegularClass.enumClassModality(): Modality {
return when {
declarations.any { it is FirCallableDeclaration && it.modality == Modality.ABSTRACT } -> {
Modality.ABSTRACT
}
declarations.none {
it is FirEnumEntry && isEnumEntryWhichRequiresSubclass(it)
} -> {
Modality.FINAL
}
hasAbstractMembersInScope() -> {
Modality.ABSTRACT
}
else -> {
Modality.OPEN
}
}
}
private fun FirRegularClass.hasAbstractMembersInScope(): Boolean {
val scope = unsubstitutedScope()
val names = scope.getCallableNames()
var hasAbstract = false
for (name in names) {
scope.processFunctionsByName(name) {
if (it.isAbstract) {
hasAbstract = true
}
}
if (hasAbstract) return true
scope.processPropertiesByName(name) {
if (it.isAbstract) {
hasAbstract = true
}
}
if (hasAbstract) return true
}
return false
}
// TODO: it's worth to move this onTheFly stuff back to classifier storage
// 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 {
// 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 ->
(session.firProvider.symbolProvider.getSymbolByLookupTag(lookupTag)?.fir as? FirClass)?.takeIf {
it.declarations.contains(c)
}
}
}.last()
val result = converter.processLocalClassAndNestedClassesOnTheFly(classOrLocalParent, temporaryParent)
// Note: usually member creation and f/o binding is delayed till non-local classes are processed in Fir2IrConverter
// If non-local classes are already created (this means we are in body translation) we do everything immediately
// The last variant is possible for local variables like 'val a = object : Any() { ... }'
if (processMembersOfClassesOnTheFlyImmediately) {
converter.processClassMembers(classOrLocalParent, result)
converter.bindFakeOverridesInClass(result)
} else {
localClassesCreatedOnTheFly[classOrLocalParent] = result
}
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()
}
fun processClassHeader(klass: FirClass, irClass: IrClass = classifierStorage.getCachedIrClass(klass)!!): IrClass {
irClass.declareTypeParameters(klass)
irClass.setThisReceiver(klass.typeParameters)
irClass.declareSupertypes(klass)
if (klass is FirRegularClass) {
irClass.declareValueClassRepresentation(klass)
}
return irClass
}
private fun IrClass.setThisReceiver(typeParameters: List<FirTypeParameterRef>) {
symbolTable.enterScope(this)
val typeArguments = typeParameters.map {
IrSimpleTypeImpl(classifierStorage.getIrTypeParameterSymbol(it.symbol, ConversionTypeOrigin.DEFAULT), false, emptyList(), emptyList())
}
thisReceiver = declareThisReceiverParameter(
thisType = IrSimpleTypeImpl(symbol, false, typeArguments, emptyList()),
thisOrigin = IrDeclarationOrigin.INSTANCE_RECEIVER
)
symbolTable.leaveScope(this)
}
private fun declareIrTypeAlias(signature: IdSignature?, factory: (IrTypeAliasSymbol) -> IrTypeAlias): IrTypeAlias =
if (signature == null)
factory(IrTypeAliasSymbolImpl())
else
symbolTable.declareTypeAlias(signature, { Fir2IrTypeAliasSymbol(signature) }, factory)
fun createIrTypeAlias(
typeAlias: FirTypeAlias,
parent: IrDeclarationParent
): IrTypeAlias = typeAlias.convertWithOffsets { startOffset, endOffset ->
val signature = signatureComposer.composeSignature(typeAlias)
declareIrTypeAlias(signature) { symbol ->
classifierStorage.preCacheTypeParameters(typeAlias, symbol)
val irTypeAlias = irFactory.createTypeAlias(
startOffset = startOffset,
endOffset = endOffset,
origin = IrDeclarationOrigin.DEFINED,
name = typeAlias.name,
visibility = components.visibilityConverter.convertToDescriptorVisibility(typeAlias.visibility),
symbol = symbol,
isActual = typeAlias.isActual,
expandedType = typeAlias.expandedTypeRef.toIrType(),
).apply {
this.parent = parent
setTypeParameters(this, typeAlias)
if (parent is IrFile) {
parent.declarations += this
}
}
typeAliasCache[typeAlias] = irTypeAlias
irTypeAlias
}
}
fun declareIrClass(signature: IdSignature?, factory: (IrClassSymbol) -> IrClass): IrClass {
return if (signature == null)
factory(IrClassSymbolImpl())
else
symbolTable.declareClass(signature, { Fir2IrClassSymbol(signature) }, factory)
}
fun createIrClass(
regularClass: FirRegularClass,
parent: IrDeclarationParent,
predefinedOrigin: IrDeclarationOrigin? = null
): IrClass {
val visibility = regularClass.visibility
val modality = when (regularClass.classKind) {
ClassKind.ENUM_CLASS -> regularClass.enumClassModality()
ClassKind.ANNOTATION_CLASS -> Modality.OPEN
else -> regularClass.modality ?: Modality.FINAL
}
val signature = runUnless(regularClass.isLocal || !configuration.linkViaSignatures) {
signatureComposer.composeSignature(regularClass)
}
val irClass = regularClass.convertWithOffsets { startOffset, endOffset ->
declareIrClass(signature) { symbol ->
irFactory.createClass(
startOffset = startOffset,
endOffset = endOffset,
origin = regularClass.computeIrOrigin(predefinedOrigin),
name = regularClass.name,
visibility = components.visibilityConverter.convertToDescriptorVisibility(visibility),
symbol = symbol,
kind = regularClass.classKind,
modality = modality,
isExternal = regularClass.isExternal,
isCompanion = regularClass.isCompanion,
isInner = regularClass.isInner,
isData = regularClass.isData,
isValue = regularClass.isInline,
isExpect = regularClass.isExpect,
isFun = regularClass.isFun
).apply {
metadata = FirMetadataSource.Class(regularClass)
}
}
}
irClass.parent = parent
if (regularClass.visibility == Visibilities.Local) {
localStorage[regularClass] = irClass
} else {
classCache[regularClass] = irClass
}
return irClass
}
fun createAnonymousObject(
anonymousObject: FirAnonymousObject,
visibility: Visibility = Visibilities.Local,
name: Name = SpecialNames.NO_NAME_PROVIDED,
irParent: IrDeclarationParent? = null
): IrClass {
val origin = IrDeclarationOrigin.DEFINED
val modality = Modality.FINAL
val irAnonymousObject = anonymousObject.convertWithOffsets { startOffset, endOffset ->
irFactory.createClass(
startOffset = startOffset,
endOffset = endOffset,
origin = origin,
name = name,
visibility = components.visibilityConverter.convertToDescriptorVisibility(visibility),
symbol = IrClassSymbolImpl(),
kind = anonymousObject.classKind,
modality = modality,
).apply {
metadata = FirMetadataSource.Class(anonymousObject)
}
}
if (irParent != null) {
irAnonymousObject.parent = irParent
}
localStorage[anonymousObject] = irAnonymousObject
return irAnonymousObject
}
fun createCodeFragmentClass(codeFragment: FirCodeFragment, containingFile: IrFile): IrClass {
val conversionData = codeFragment.conversionData
val signature = signatureComposer.composeSignature(codeFragment)
val irClass = codeFragment.convertWithOffsets { startOffset, endOffset ->
declareIrClass(signature) { symbol ->
irFactory.createClass(
startOffset,
endOffset,
IrDeclarationOrigin.DEFINED,
conversionData.classId.shortClassName,
DescriptorVisibilities.PUBLIC,
symbol,
ClassKind.CLASS,
Modality.FINAL,
isExternal = false,
isCompanion = false,
isInner = false,
isData = false,
isValue = false,
isExpect = false,
isFun = false
).apply {
metadata = FirMetadataSource.CodeFragment(codeFragment)
parent = containingFile
typeParameters = emptyList()
thisReceiver = declareThisReceiverParameter(
thisType = IrSimpleTypeImpl(symbol, false, emptyList(), emptyList()),
thisOrigin = IrDeclarationOrigin.INSTANCE_RECEIVER
)
superTypes = listOf(irBuiltIns.anyType)
}
}
}
codeFragmentCache[codeFragment] = irClass
return irClass
}
private fun getIrAnonymousObjectForEnumEntry(anonymousObject: FirAnonymousObject, name: Name, irParent: IrClass?): IrClass {
localStorage[anonymousObject]?.let { return it }
val irAnonymousObject = classifierStorage.createAndCacheAnonymousObject(anonymousObject, Visibilities.Private, name, irParent)
processClassHeader(anonymousObject, irAnonymousObject)
return irAnonymousObject
}
fun createIrTypeParameterWithBounds(
typeParameter: FirTypeParameter,
index: Int,
ownerSymbol: IrSymbol,
typeOrigin: ConversionTypeOrigin = ConversionTypeOrigin.DEFAULT,
): IrTypeParameter {
val irTypeParameter = createIrTypeParameterWithoutBounds(typeParameter, index, ownerSymbol, typeOrigin)
irTypeParameter.superTypes = typeParameter.bounds.map { it.toIrType() }
return irTypeParameter
}
fun createIrTypeParameterWithoutBounds(
typeParameter: FirTypeParameter,
index: Int,
ownerSymbol: IrSymbol,
typeOrigin: ConversionTypeOrigin = ConversionTypeOrigin.DEFAULT,
): IrTypeParameter {
require(index >= 0)
val origin = typeParameter.computeIrOrigin()
val irTypeParameter = with(typeParameter) {
convertWithOffsets { startOffset, endOffset ->
signatureComposer.composeTypeParameterSignature(
index, ownerSymbol.signature
)?.let { signature ->
if (ownerSymbol is IrClassifierSymbol) {
symbolTable.declareGlobalTypeParameter(
signature,
symbolFactory = { IrTypeParameterPublicSymbolImpl(signature) }
) { symbol ->
irFactory.createTypeParameter(
startOffset = startOffset,
endOffset = endOffset,
origin = origin,
name = name,
symbol = symbol,
variance = variance,
index = if (index < 0) 0 else index,
isReified = isReified,
)
}
} else {
symbolTable.declareScopedTypeParameter(
signature,
symbolFactory = { IrTypeParameterPublicSymbolImpl(signature) }
) { symbol ->
irFactory.createTypeParameter(
startOffset = startOffset,
endOffset = endOffset,
origin = origin,
name = name,
symbol = symbol,
variance = variance,
index = if (index < 0) 0 else index,
isReified = isReified,
)
}
}
} ?: irFactory.createTypeParameter(
startOffset = startOffset,
endOffset = endOffset,
origin = origin,
name = name,
symbol = IrTypeParameterSymbolImpl(),
variance = variance,
index = if (index < 0) 0 else index,
isReified = isReified,
)
}
}
// Cache the type parameter BEFORE processing its bounds/supertypes, to properly handle recursive type bounds.
if (typeOrigin.forSetter) {
typeParameterCacheForSetter[typeParameter] = irTypeParameter
} else {
typeParameterCache[typeParameter] = irTypeParameter
}
annotationGenerator.generate(irTypeParameter, typeParameter)
return irTypeParameter
}
private fun declareIrEnumEntry(signature: IdSignature?, factory: (IrEnumEntrySymbol) -> IrEnumEntry): IrEnumEntry {
return if (signature == null)
factory(IrEnumEntrySymbolImpl())
else
symbolTable.declareEnumEntry(signature, { Fir2IrEnumEntrySymbol(signature) }, factory)
}
fun createIrEnumEntry(
enumEntry: FirEnumEntry,
irParent: IrClass?,
predefinedOrigin: IrDeclarationOrigin? = null,
): IrEnumEntry {
return enumEntry.convertWithOffsets { startOffset, endOffset ->
val signature = signatureComposer.composeSignature(enumEntry)
val result = declareIrEnumEntry(signature) { symbol ->
val origin = enumEntry.computeIrOrigin(predefinedOrigin)
irFactory.createEnumEntry(
startOffset = startOffset,
endOffset = endOffset,
origin = origin,
name = enumEntry.name,
symbol = symbol,
).apply {
declarationStorage.enterScope(this.symbol)
if (irParent != null) {
this.parent = irParent
}
if (isEnumEntryWhichRequiresSubclass(enumEntry)) {
// An enum entry with its own members requires an anonymous object generated.
// Otherwise, this is a default-ish enum entry whose initializer would be a delegating constructor call,
// which will be translated via visitor later.
val klass = getIrAnonymousObjectForEnumEntry(
(enumEntry.initializer as FirAnonymousObjectExpression).anonymousObject, enumEntry.name, irParent
)
this.correspondingClass = klass
}
declarationStorage.leaveScope(this.symbol)
}
}
enumEntryCache[enumEntry] = result
result
}
}
private fun isEnumEntryWhichRequiresSubclass(enumEntry: FirEnumEntry): Boolean {
val initializer = enumEntry.initializer
return initializer is FirAnonymousObjectExpression && initializer.anonymousObject.declarations.any { it !is FirConstructor }
}
@OptIn(IrSymbolInternals::class)
fun createIrClassSymbolForNotFoundClass(classLikeLookupTag: ConeClassLikeLookupTag): IrClassSymbol {
val classId = classLikeLookupTag.classId
val signature = IdSignature.CommonSignature(
packageFqName = classId.packageFqName.asString(),
declarationFqName = classId.relativeClassName.asString(),
id = 0,
mask = 0,
description = null,
)
val parentId = classId.outerClassId
val parentClass = parentId?.let { createIrClassSymbolForNotFoundClass(it.toLookupTag()) }
val irParent = parentClass?.owner ?: declarationStorage.getIrExternalPackageFragment(classId.packageFqName)
return symbolTable.referenceClass(signature, { Fir2IrClassSymbol(signature) }) {
irFactory.createClass(
startOffset = UNDEFINED_OFFSET,
endOffset = UNDEFINED_OFFSET,
origin = IrDeclarationOrigin.IR_EXTERNAL_DECLARATION_STUB,
name = classId.shortClassName,
visibility = DescriptorVisibilities.DEFAULT_VISIBILITY,
symbol = it,
kind = ClassKind.CLASS,
modality = Modality.FINAL,
).apply {
parent = irParent
}
}
}
// TODO: should be decomposed
fun createIrClassSymbolByFirSymbol(firClassSymbol: FirClassSymbol<*>): IrClassSymbol {
val firClass = firClassSymbol.fir
classifierStorage.getCachedIrClass(firClass)?.let { return it.symbol }
if (firClass is FirAnonymousObject || firClass is FirRegularClass && firClass.visibility == Visibilities.Local) {
return createLocalIrClassOnTheFly(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
}
fun createIrTypeParameterForNonCachedDeclaration(firTypeParameter: FirTypeParameter): IrTypeParameterSymbol {
val firTypeParameterOwnerSymbol = firTypeParameter.containingDeclarationSymbol
val firTypeParameterOwner = firTypeParameterOwnerSymbol.fir as FirTypeParameterRefsOwner
val index = firTypeParameterOwner.typeParameters.indexOf(firTypeParameter).also { check(it >= 0) }
val isSetter = firTypeParameterOwner is FirPropertyAccessor && firTypeParameterOwner.isSetter
val conversionTypeOrigin = if (isSetter) ConversionTypeOrigin.SETTER else ConversionTypeOrigin.DEFAULT
return createIrTypeParameterWithoutBounds(firTypeParameter, index, IrTypeParameterSymbolImpl(), conversionTypeOrigin).apply {
superTypes = firTypeParameter.bounds.map { it.toIrType(typeConverter) }
}.symbol
}
private val temporaryParent by lazy {
irFactory.createSimpleFunction(
startOffset = UNDEFINED_OFFSET,
endOffset = UNDEFINED_OFFSET,
origin = IrDeclarationOrigin.DEFINED,
name = Name.special("<stub>"),
visibility = DescriptorVisibilities.PRIVATE,
isInline = false,
isExpect = false,
returnType = irBuiltIns.unitType,
modality = Modality.FINAL,
symbol = IrSimpleFunctionSymbolImpl(),
isTailrec = false,
isSuspend = false,
isOperator = false,
isInfix = false,
isExternal = false,
).apply {
parent = IrExternalPackageFragmentImpl(IrExternalPackageFragmentSymbolImpl(), FqName.ROOT)
}
}
}
@@ -7,8 +7,11 @@ package org.jetbrains.kotlin.fir.backend.generators
import org.jetbrains.kotlin.fir.backend.Fir2IrComponents
import org.jetbrains.kotlin.fir.backend.convertWithOffsets
import org.jetbrains.kotlin.fir.backend.irOrigin
import org.jetbrains.kotlin.fir.backend.isStubPropertyForPureField
import org.jetbrains.kotlin.fir.declarations.FirClass
import org.jetbrains.kotlin.fir.declarations.FirProperty
import org.jetbrains.kotlin.fir.declarations.FirRegularClass
import org.jetbrains.kotlin.fir.declarations.FirSimpleFunction
import org.jetbrains.kotlin.fir.dispatchReceiverClassLookupTagOrNull
import org.jetbrains.kotlin.fir.isSubstitutionOrIntersectionOverride
@@ -16,13 +19,13 @@ import org.jetbrains.kotlin.fir.lazy.Fir2IrLazyClass
import org.jetbrains.kotlin.fir.lazy.Fir2IrLazyProperty
import org.jetbrains.kotlin.fir.lazy.Fir2IrLazySimpleFunction
import org.jetbrains.kotlin.fir.originalForSubstitutionOverride
import org.jetbrains.kotlin.fir.resolve.providers.firProvider
import org.jetbrains.kotlin.fir.symbols.Fir2IrPropertySymbol
import org.jetbrains.kotlin.ir.declarations.IrDeclarationOrigin
import org.jetbrains.kotlin.ir.declarations.IrDeclarationParent
import org.jetbrains.kotlin.ir.declarations.IrProperty
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
import org.jetbrains.kotlin.ir.symbols.IrSymbolInternals
import org.jetbrains.kotlin.ir.util.IdSignature
import org.jetbrains.kotlin.utils.addToStdlib.runIf
class Fir2IrLazyDeclarationsGenerator(val components: Fir2IrComponents) : Fir2IrComponents by components {
internal fun createIrLazyFunction(
@@ -87,4 +90,18 @@ class Fir2IrLazyDeclarationsGenerator(val components: Fir2IrComponents) : Fir2Ir
return irProperty
}
fun createIrLazyClass(
firClass: FirRegularClass,
irParent: IrDeclarationParent,
): IrClass = firClass.convertWithOffsets { startOffset, endOffset ->
val firClassOrigin = firClass.irOrigin(session.firProvider)
val signature = runIf(configuration.linkViaSignatures) {
signatureComposer.composeSignature(firClass)
}
classifiersGenerator.declareIrClass(signature) { symbol ->
Fir2IrLazyClass(components, startOffset, endOffset, firClassOrigin, firClass, symbol).apply {
parent = irParent
}
}
}
}
@@ -191,11 +191,7 @@ class Fir2IrLazyProperty(
}.apply {
parent = this@Fir2IrLazyProperty.parent
correspondingPropertySymbol = this@Fir2IrLazyProperty.symbol
with(classifierStorage) {
setTypeParameters(
this@Fir2IrLazyProperty.fir, ConversionTypeOrigin.DEFAULT
)
}
classifiersGenerator.setTypeParameters(this, this@Fir2IrLazyProperty.fir, ConversionTypeOrigin.DEFAULT)
}
}
@@ -222,11 +218,7 @@ class Fir2IrLazyProperty(
).apply {
parent = this@Fir2IrLazyProperty.parent
correspondingPropertySymbol = this@Fir2IrLazyProperty.symbol
with(classifierStorage) {
setTypeParameters(
this@Fir2IrLazyProperty.fir, ConversionTypeOrigin.SETTER
)
}
classifiersGenerator.setTypeParameters(this, this@Fir2IrLazyProperty.fir, ConversionTypeOrigin.SETTER)
}
}
}