IR: add IrClass.hasEnumEntries

This flag will be used on JVM to determine whether or not to generate
external enum entries mappings ("$EntriesMappings") classes. Note that
from the frontend's point of view, every enum has `entries`, so for
backend purposes we have to reach out to the underlying deserialized
data to read the flag from the metadata.
This commit is contained in:
Alexander Udalov
2023-08-17 12:54:59 +02:00
committed by Space Team
parent db31f1f926
commit c33c918bd4
19 changed files with 68 additions and 10 deletions
@@ -11,6 +11,7 @@ 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.hasEnumEntries
import org.jetbrains.kotlin.fir.lazy.Fir2IrLazyClass
import org.jetbrains.kotlin.fir.moduleData
import org.jetbrains.kotlin.fir.resolve.getSymbolByLookupTag
@@ -138,7 +139,8 @@ class Fir2IrClassifiersGenerator(val components: Fir2IrComponents) : Fir2IrCompo
isData = regularClass.isData,
isValue = regularClass.isInline,
isExpect = regularClass.isExpect,
isFun = regularClass.isFun
isFun = regularClass.isFun,
hasEnumEntries = regularClass.hasEnumEntries,
).apply {
metadata = FirMetadataSource.Class(regularClass)
}
@@ -376,7 +378,8 @@ class Fir2IrClassifiersGenerator(val components: Fir2IrComponents) : Fir2IrCompo
isData = false,
isValue = false,
isExpect = false,
isFun = false
isFun = false,
hasEnumEntries = false,
).apply {
metadata = FirMetadataSource.CodeFragment(codeFragment)
parent = containingFile
@@ -10,6 +10,7 @@ import org.jetbrains.kotlin.fir.backend.*
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.declarations.utils.*
import org.jetbrains.kotlin.fir.dispatchReceiverClassLookupTagOrNull
import org.jetbrains.kotlin.fir.hasEnumEntries
import org.jetbrains.kotlin.fir.isNewPlaceForBodyGeneration
import org.jetbrains.kotlin.fir.isSubstitutionOrIntersectionOverride
import org.jetbrains.kotlin.fir.scopes.FirContainingNamesAwareScope
@@ -109,6 +110,10 @@ class Fir2IrLazyClass(
get() = fir.isFun
set(_) = mutationNotSupported()
override var hasEnumEntries: Boolean
get() = fir.hasEnumEntries
set(_) = mutationNotSupported()
override var superTypes: List<IrType> by lazyVar(lock) {
fir.superTypeRefs.map { it.toIrType(typeConverter) }
}
@@ -51,6 +51,17 @@ abstract class IrClass : IrDeclarationBase(), IrPossiblyExternalDeclaration,
abstract var isFun: Boolean
/**
* Returns true iff this is a class loaded from dependencies which has the `HAS_ENUM_ENTRIES`
* metadata flag set.
* This flag is useful for Kotlin/JVM to determine whether an enum class from dependency
* actually has the `entries` property
* in its bytecode, as opposed to whether it has it in its member scope, which is true even for
* enum classes compiled by
* old versions of Kotlin which did not support the EnumEntries language feature.
*/
abstract var hasEnumEntries: Boolean
abstract val source: SourceElement
abstract var superTypes: List<IrType>
@@ -60,6 +60,7 @@ interface IrFactory {
isValue: Boolean = false,
isExpect: Boolean = false,
isFun: Boolean = false,
hasEnumEntries: Boolean = false,
source: SourceElement = SourceElement.NO_SOURCE,
): IrClass
@@ -300,6 +301,7 @@ interface IrFactory {
isValue,
isExpect,
isFun,
false,
source,
)
@@ -20,6 +20,7 @@ class IrClassBuilder : IrDeclarationBuilder() {
var isValue: Boolean = false
var isExpect: Boolean = false
var isFun: Boolean = false
var hasEnumEntries: Boolean = false
fun updateFrom(from: IrClass) {
super.updateFrom(from)
@@ -33,5 +34,6 @@ class IrClassBuilder : IrDeclarationBuilder() {
isValue = from.isValue
isExpect = from.isExpect
isFun = from.isFun
hasEnumEntries = from.hasEnumEntries
}
}
@@ -42,6 +42,7 @@ internal fun IrFactory.buildClass(builder: IrClassBuilder): IrClass = with(build
isValue = isValue,
isExpect = isExpect,
isFun = isFun,
hasEnumEntries = hasEnumEntries,
)
}
@@ -30,6 +30,7 @@ open class IrClassImpl(
override var isValue: Boolean = false,
override var isExpect: Boolean = false,
override var isFun: Boolean = false,
override var hasEnumEntries: Boolean = false,
override val source: SourceElement = SourceElement.NO_SOURCE,
override val factory: IrFactory = IrFactoryImpl
) : IrClass() {
@@ -50,11 +50,12 @@ abstract class AbstractIrFactoryImpl : IrFactory {
isValue: Boolean,
isExpect: Boolean,
isFun: Boolean,
hasEnumEntries: Boolean,
source: SourceElement,
): IrClass =
IrClassImpl(
startOffset, endOffset, origin, symbol, name, kind, visibility, modality,
isCompanion, isInner, isData, isExternal, isValue, isExpect, isFun, source,
isCompanion, isInner, isData, isExternal, isValue, isExpect, isFun, hasEnumEntries, source,
factory = this
)
@@ -62,6 +62,7 @@ class IrFactoryImplForJsIC(override val stageController: StageController) : Abst
isValue: Boolean,
isExpect: Boolean,
isFun: Boolean,
hasEnumEntries: Boolean,
source: SourceElement
): IrClass {
return super.createClass(
@@ -80,6 +81,7 @@ class IrFactoryImplForJsIC(override val stageController: StageController) : Abst
isValue,
isExpect,
isFun,
hasEnumEntries,
source,
).register()
}
@@ -37,6 +37,7 @@ class IrLazyClass(
override var isValue: Boolean,
override var isExpect: Boolean,
override var isFun: Boolean,
override var hasEnumEntries: Boolean,
override val stubGenerator: DeclarationStubGenerator,
override val typeTranslator: TypeTranslator
) : IrClass(), IrLazyDeclarationBase, DeserializableClass {
@@ -36,6 +36,7 @@ class LazyIrFactory(
isValue: Boolean,
isExpect: Boolean,
isFun: Boolean,
hasEnumEntries: Boolean,
source: SourceElement
): IrClass = if (symbol.isBound)
symbol.owner
@@ -56,6 +57,7 @@ class LazyIrFactory(
isValue,
isExpect,
isFun,
hasEnumEntries,
source,
)
@@ -24,11 +24,14 @@ import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.declarations.lazy.*
import org.jetbrains.kotlin.ir.linkage.IrProvider
import org.jetbrains.kotlin.ir.symbols.*
import org.jetbrains.kotlin.ir.symbols.IrFieldSymbol
import org.jetbrains.kotlin.ir.symbols.IrSymbol
import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol
import org.jetbrains.kotlin.ir.symbols.impl.IrValueParameterSymbolImpl
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.descriptorUtil.isEffectivelyExternal
import org.jetbrains.kotlin.resolve.isValueClass
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedClassDescriptor
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedContainerSource
import org.jetbrains.kotlin.types.KotlinType
@@ -277,6 +280,7 @@ abstract class DeclarationStubGenerator(
isValue = isValueClass(),
isExpect = isExpect,
isFun = isFun,
hasEnumEntries = descriptor is DeserializedClassDescriptor && descriptor.hasEnumEntriesMetadataFlag,
stubGenerator = this@DeclarationStubGenerator,
typeTranslator = typeTranslator
)
@@ -5,7 +5,9 @@
package org.jetbrains.kotlin.ir.util
import org.jetbrains.kotlin.ir.*
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.IrStatement
import org.jetbrains.kotlin.ir.ObsoleteDescriptorBasedAPI
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.declarations.impl.*
import org.jetbrains.kotlin.ir.expressions.*
@@ -157,6 +159,7 @@ open class DeepCopyIrTreeWithSymbols(
isValue = declaration.isValue,
isExpect = declaration.isExpect,
isFun = declaration.isFun,
hasEnumEntries = declaration.hasEnumEntries,
).apply {
transformAnnotations(declaration)
copyTypeParametersFrom(declaration)
@@ -13,6 +13,7 @@ import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.descriptorUtil.isEffectivelyExternal
import org.jetbrains.kotlin.resolve.isValueClass
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedClassDescriptor
import org.jetbrains.kotlin.types.KotlinType
val ParameterDescriptor.indexOrMinusOne: Int
@@ -52,5 +53,6 @@ fun IrFactory.createIrClassFromDescriptor(
isValue = descriptor.isValueClass(),
isExpect = descriptor.isExpect,
isFun = descriptor.isFun,
hasEnumEntries = descriptor is DeserializedClassDescriptor && descriptor.hasEnumEntriesMetadataFlag,
source = descriptor.source,
)
@@ -273,6 +273,15 @@ object IrTree : AbstractTreeBuilder() {
+field("isFun", boolean) {
useFieldInIrFactory(defaultValue = false)
}
+field("hasEnumEntries", boolean) {
useFieldInIrFactory(defaultValue = false)
kdoc = """
Returns true iff this is a class loaded from dependencies which has the `HAS_ENUM_ENTRIES` metadata flag set.
This flag is useful for Kotlin/JVM to determine whether an enum class from dependency actually has the `entries` property
in its bytecode, as opposed to whether it has it in its member scope, which is true even for enum classes compiled by
old versions of Kotlin which did not support the EnumEntries language feature.
""".trimIndent()
}
+field("source", type<SourceElement>(), mutable = false) {
useFieldInIrFactory(defaultValue = code("%T.NO_SOURCE", SourceElement::class))
}
@@ -104,6 +104,7 @@ internal fun printFactory(generationPath: File, model: Model): GeneratedFile {
parameter("isExpect")
parameter("isFun")
parameter("source")
defaultValue["hasEnumEntries"] = false
}
addDeprecatedFunction(replacement("createConstructor")) {
@@ -272,14 +273,11 @@ internal fun printFactory(generationPath: File, model: Model): GeneratedFile {
}
private class DeprecatedFunctionBuilder(private val replacement: FunSpec) {
val deprecatedFunctionParameterSpecs = mutableListOf<ParameterSpec>()
var oldName = replacement.name
var returnType = replacement.returnType
var deprecationMessage: String? = null
val defaultValue = mutableMapOf<String, Any?>()
fun parameter(name: String, removeDefaultValue: Boolean = false) {
val replacementParameter =
@@ -322,6 +320,13 @@ private fun TypeSpec.Builder.addDeprecatedFunction(
indent()
add("%N,\n", parameter)
unindent()
} else {
val value = builder.defaultValue[parameter.name]
if (value != null) {
indent()
add("$value,\n")
unindent()
}
}
}
}
@@ -374,6 +374,7 @@ class IrDeclarationDeserializer(
isValue = flags.isValue,
isExpect = flags.isExpect,
isFun = flags.isFun,
hasEnumEntries = flags.hasEnumEntries,
)
}.usingParent {
typeParameters = deserializeTypeParameters(proto.typeParameterList, true)
@@ -33,6 +33,7 @@ value class ClassFlags(val flags: Long) {
val isExpect: Boolean get() = IrFlags.IS_EXPECT_CLASS.get(flags.toInt())
val isExternal: Boolean get() = IrFlags.IS_EXTERNAL_CLASS.get(flags.toInt())
val isFun: Boolean get() = IrFlags.IS_FUN_INTERFACE.get(flags.toInt())
val hasEnumEntries: Boolean get() = IrFlags.HAS_ENUM_ENTRIES.get(flags.toInt())
companion object {
fun encode(clazz: IrClass, languageVersionSettings: LanguageVersionSettings): Long {
@@ -52,9 +52,11 @@ class DeserializedClassDescriptor(
VersionRequirementTable.create(classProto.versionRequirementTable), metadataVersion
)
val hasEnumEntriesMetadataFlag: Boolean = Flags.HAS_ENUM_ENTRIES.get(classProto.flags)
private val staticScope =
if (kind == ClassKind.ENUM_CLASS) {
val enumEntriesCanBeUsed = Flags.HAS_ENUM_ENTRIES.get(classProto.flags) ||
val enumEntriesCanBeUsed = hasEnumEntriesMetadataFlag ||
c.components.enumEntriesDeserializationSupport.canSynthesizeEnumEntries() == true
StaticScopeForKotlinEnum(c.storageManager, this, enumEntriesCanBeUsed)
} else {