diff --git a/compiler/ir/backend.jvm/entrypoint/src/org/jetbrains/kotlin/backend/jvm/SymbolTableWithBuiltInsDeduplication.kt b/compiler/ir/backend.jvm/entrypoint/src/org/jetbrains/kotlin/backend/jvm/SymbolTableWithBuiltInsDeduplication.kt index d248fc4e3e7..9e1c7eb3dd1 100644 --- a/compiler/ir/backend.jvm/entrypoint/src/org/jetbrains/kotlin/backend/jvm/SymbolTableWithBuiltInsDeduplication.kt +++ b/compiler/ir/backend.jvm/entrypoint/src/org/jetbrains/kotlin/backend/jvm/SymbolTableWithBuiltInsDeduplication.kt @@ -31,8 +31,8 @@ class SymbolTableWithBuiltInsDeduplication( irFactory: IrFactory, ) : SymbolTable(signaturer, irFactory) { /** - * As long as [IrBuiltIns] aren't bound, the symbol table will operate like [SymbolTable], as it must be assumed that built-ins are - * still being created. + * As long as [IrBuiltIns] aren't bound, the symbol table will operate like [SymbolTable], as the initialization of built-ins requires + * a symbol table. */ private var irBuiltIns: IrBuiltInsOverDescriptors? = null @@ -44,6 +44,14 @@ class SymbolTableWithBuiltInsDeduplication( } } + /** + * Gets or creates the [IrClassSymbol] for [descriptor], or for the built-in descriptor with the same name if [descriptor] is a + * duplicate built-in. + * + * Note that not all built-in symbols may have been bound or created by the time [irBuiltIns] has been bound. However, [referenceClass] + * will create a symbol in such a case (via `super.referenceClass`) and [org.jetbrains.kotlin.ir.util.DeclarationStubGenerator] will + * create a stub for the symbol if [referenceClass] was invoked from the stub generator. + */ @ObsoleteDescriptorBasedAPI override fun referenceClass(descriptor: ClassDescriptor): IrClassSymbol { val irBuiltIns = this.irBuiltIns ?: return super.referenceClass(descriptor) diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/DeclarationStubGenerator.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/DeclarationStubGenerator.kt index b055902eb9b..eb203f6a2ef 100644 --- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/DeclarationStubGenerator.kt +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/DeclarationStubGenerator.kt @@ -254,26 +254,33 @@ abstract class DeclarationStubGenerator( classDescriptor.modality fun generateClassStub(descriptor: ClassDescriptor): IrClass { - val referenceClass = symbolTable.referenceClass(descriptor) - if (referenceClass.isBound) { - return referenceClass.owner + val irClassSymbol = symbolTable.referenceClass(descriptor) + if (irClassSymbol.isBound) { + return irClassSymbol.owner } - val origin = computeOrigin(descriptor) - return symbolTable.declareClass(descriptor) { - IrLazyClass( - UNDEFINED_OFFSET, UNDEFINED_OFFSET, origin, - it, descriptor, - descriptor.name, descriptor.kind, descriptor.visibility, getEffectiveModality(descriptor), - isCompanion = descriptor.isCompanionObject, - isInner = descriptor.isInner, - isData = descriptor.isData, - isExternal = descriptor.isEffectivelyExternal(), - isValue = descriptor.isValueClass(), - isExpect = descriptor.isExpect, - isFun = descriptor.isFun, - stubGenerator = this, - typeTranslator = typeTranslator - ) + + // `irClassSymbol` may have a different descriptor than `descriptor`. For example, a `SymbolTableWithBuiltInsDeduplication` might + // return a built-in symbol that hasn't been bound yet (e.g. `OptIn`). If the stub generator calls `declareClass` with the incorrect + // `descriptor`, a symbol created for `descriptor` will be bound, not the built-in symbol which should be. If `generateClassStub` is + // called twice for such a `descriptor`, an exception will occur because `descriptor`'s symbol will already have been bound. + with(irClassSymbol.descriptor) { + val origin = computeOrigin(this) + return symbolTable.declareClass(this) { + IrLazyClass( + UNDEFINED_OFFSET, UNDEFINED_OFFSET, origin, + it, this, + name, kind, visibility, getEffectiveModality(this), + isCompanion = isCompanionObject, + isInner = isInner, + isData = isData, + isExternal = isEffectivelyExternal(), + isValue = isValueClass(), + isExpect = isExpect, + isFun = isFun, + stubGenerator = this@DeclarationStubGenerator, + typeTranslator = typeTranslator + ) + } } }