From c485382e802399e3b143cdbdcd2767ae675f3943 Mon Sep 17 00:00:00 2001 From: Pavel Kunyavskiy Date: Thu, 23 Nov 2023 15:44:35 +0100 Subject: [PATCH] [FIR2IR] Rework actualization pipeline There are a lot of restrictions between different parts of the pipeline. 1. Fake overrides can't be built before classes are actualized 2. Constants can't be evaluated before callables are actualized 3. Callables can't be actulaized before fake overrides are built 4. Checkers can't run before constants are evaluated This commit reorders things to make all these restrictions happy. ^KT-63644 --- .../kotlin/fir/pipeline/convertToIr.kt | 121 ++++----- .../kotlin/fir/backend/Fir2IrConverter.kt | 11 +- .../actualizer/ExpectActualCollector.kt | 8 +- .../backend/common/actualizer/IrActualizer.kt | 97 ++++--- .../ir/overrides/FakeOverrideRebuilder.kt | 241 ------------------ .../ir/overrides/IrFakeOverrideBuilder.kt | 29 +++ .../codegen/box/multiplatform/hmpp/simple.kt | 1 - .../k2/basic/expectInterfaceInSupertypes.kt | 1 - .../k2/basic/expectInterfaceInSupertypes2.kt | 1 - 9 files changed, 144 insertions(+), 366 deletions(-) delete mode 100644 compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/overrides/FakeOverrideRebuilder.kt diff --git a/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/pipeline/convertToIr.kt b/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/pipeline/convertToIr.kt index 9c43eab03c1..be5871b59ba 100644 --- a/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/pipeline/convertToIr.kt +++ b/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/pipeline/convertToIr.kt @@ -21,9 +21,11 @@ import org.jetbrains.kotlin.ir.IrBuiltIns import org.jetbrains.kotlin.ir.backend.jvm.serialization.JvmDescriptorMangler import org.jetbrains.kotlin.ir.declarations.IrModuleFragment import org.jetbrains.kotlin.ir.declarations.impl.IrFactoryImpl +import org.jetbrains.kotlin.ir.overrides.buildForAll import org.jetbrains.kotlin.ir.types.IrTypeSystemContext import org.jetbrains.kotlin.ir.util.IdSignatureComposer import org.jetbrains.kotlin.ir.util.KotlinMangler +import org.jetbrains.kotlin.ir.visitors.acceptVoid data class FirResult(val outputs: List) @@ -61,85 +63,58 @@ fun FirResult.convertToIrAndActualize( actualizerTypeContextProvider: (IrBuiltIns) -> IrTypeSystemContext, fir2IrResultPostCompute: (ModuleCompilerAnalyzedOutput, Fir2IrResult) -> Unit = { _, _ -> }, ): Fir2IrActualizedResult { - val fir2IrResult: Fir2IrResult - val actualizationResult: IrActualizedResult? - val commonMemberStorage = Fir2IrCommonMemberStorage(signatureComposer, firMangler) - when (outputs.size) { - 0 -> error("No modules found") - 1 -> { - val output = outputs.single() - fir2IrResult = convertToIr( - output, - fir2IrExtensions, - fir2IrConfiguration, - commonMemberStorage = commonMemberStorage, - irBuiltIns = null, - irMangler, - visibilityConverter, - kotlinBuiltIns, - actualizerTypeContextProvider, - ).also { result -> - fir2IrResultPostCompute(output, result) - } - actualizationResult = null - } - else -> { - val platformOutput = outputs.last() - val commonOutputs = outputs.dropLast(1) - var irBuiltIns: IrBuiltInsOverFir? = null - val commonIrOutputs = commonOutputs.map { - convertToIr( - it, - // We need to build all modules before rebuilding fake overrides - // to avoid fixing declaration storages - fir2IrExtensions, - fir2IrConfiguration.copy(useIrFakeOverrideBuilder = false), - commonMemberStorage = commonMemberStorage, - irBuiltIns = irBuiltIns, - irMangler, - visibilityConverter, - kotlinBuiltIns, - actualizerTypeContextProvider, - ).also { result -> - fir2IrResultPostCompute(it, result) - if (irBuiltIns == null) { - irBuiltIns = result.components.irBuiltIns - } - } - } - fir2IrResult = convertToIr( - platformOutput, - fir2IrExtensions, - fir2IrConfiguration, - commonMemberStorage = commonMemberStorage, - irBuiltIns = irBuiltIns!!, - irMangler, - visibilityConverter, - kotlinBuiltIns, - actualizerTypeContextProvider, - ).also { - fir2IrResultPostCompute(platformOutput, it) - } + require(outputs.isNotEmpty()) { "No modules found" } - actualizationResult = IrActualizer.actualize( - fir2IrResult.irModuleFragment, - commonIrOutputs.map { it.irModuleFragment }, - KtDiagnosticReporterWithImplicitIrBasedContext( - fir2IrConfiguration.diagnosticReporter, - fir2IrConfiguration.languageVersionSettings - ), - actualizerTypeContextProvider(fir2IrResult.irModuleFragment.irBuiltins), - commonMemberStorage.symbolTable, - fir2IrResult.components.fakeOverrideBuilder, - fir2IrConfiguration.useIrFakeOverrideBuilder, - fir2IrConfiguration.expectActualTracker, - ) + var irBuiltIns: IrBuiltInsOverFir? = null + val irOutputs = outputs.map { + convertToIr( + it, + // We need to build all modules before rebuilding fake overrides + // to avoid fixing declaration storages + fir2IrExtensions, + fir2IrConfiguration, + commonMemberStorage = commonMemberStorage, + irBuiltIns = irBuiltIns, + irMangler, + visibilityConverter, + kotlinBuiltIns, + actualizerTypeContextProvider, + ).also { result -> + fir2IrResultPostCompute(it, result) + if (irBuiltIns == null) { + irBuiltIns = result.components.irBuiltIns + } } } - val (irModuleFragment, components, pluginContext) = fir2IrResult + val (irModuleFragment, components, pluginContext) = irOutputs.last() + val allIrModules = irOutputs.map { it.irModuleFragment } + + val irActualizer = if (allIrModules.size == 1) null else IrActualizer( + KtDiagnosticReporterWithImplicitIrBasedContext( + fir2IrConfiguration.diagnosticReporter, + fir2IrConfiguration.languageVersionSettings + ), + actualizerTypeContextProvider(irModuleFragment.irBuiltins), + fir2IrConfiguration.expectActualTracker, + fir2IrConfiguration.useIrFakeOverrideBuilder, + irModuleFragment, + allIrModules.dropLast(1), + ) + + if (fir2IrConfiguration.useIrFakeOverrideBuilder) { + // actualizeCallablesAndMergeModules call below in fact can also actualize classifiers. + // So to avoid even more changes, when this mode is disabled, we don't run classifiers + // actualization separately. This should go away, after useIrFakeOverrideBuilder becomes + // always enabled + irActualizer?.actualizeClassifiers() + components.fakeOverrideBuilder.buildForAll(allIrModules) + } + val expectActualMap = irActualizer?.actualizeCallablesAndMergeModules() ?: emptyMap() + Fir2IrConverter.evaluateConstants(irModuleFragment, components) + val actualizationResult = irActualizer?.runChecksAndFinalize(expectActualMap) pluginContext.applyIrGenerationExtensions(irModuleFragment, irGeneratorExtensions) return Fir2IrActualizedResult(irModuleFragment, components, pluginContext, actualizationResult) } diff --git a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/Fir2IrConverter.kt b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/Fir2IrConverter.kt index a0a1027cefb..ab5bf82e10f 100644 --- a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/Fir2IrConverter.kt +++ b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/Fir2IrConverter.kt @@ -11,7 +11,6 @@ import org.jetbrains.kotlin.KtFakeSourceElementKind import org.jetbrains.kotlin.KtPsiSourceFileLinesMapping import org.jetbrains.kotlin.KtSourceFileLinesMappingFromLineStartOffsets import org.jetbrains.kotlin.backend.common.CommonBackendErrors -import org.jetbrains.kotlin.ir.overrides.FakeOverrideRebuilder import org.jetbrains.kotlin.backend.common.sourceElement import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.config.LanguageFeature @@ -129,8 +128,6 @@ class Fir2IrConverter( @OptIn(LeakedDeclarationCaches::class) declarationStorage.fillUnboundSymbols() } - - evaluateConstants(irModuleFragment, components) } fun bindFakeOverridesOrPostpone(declarations: List) { @@ -618,7 +615,8 @@ class Fir2IrConverter( } companion object { - private fun evaluateConstants(irModuleFragment: IrModuleFragment, components: Fir2IrComponents) { + // TODO: move to compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/pipeline/convertToIr.kt (KT-64201) + fun evaluateConstants(irModuleFragment: IrModuleFragment, components: Fir2IrComponents) { val fir2IrConfiguration = components.configuration val firModuleDescriptor = irModuleFragment.descriptor as? FirModuleDescriptor val targetPlatform = firModuleDescriptor?.platform @@ -745,11 +743,6 @@ class Fir2IrConverter( components.fir2IrVisitor ) - if (fir2IrConfiguration.useIrFakeOverrideBuilder) { - val rebuilder = FakeOverrideRebuilder(commonMemberStorage.symbolTable, components.fakeOverrideBuilder) - rebuilder.rebuildFakeOverrides(irModuleFragment) - } - return Fir2IrResult(irModuleFragment, components, moduleDescriptor) } } diff --git a/compiler/ir/ir.actualization/src/main/kotlin/org/jetbrains/kotlin/backend/common/actualizer/ExpectActualCollector.kt b/compiler/ir/ir.actualization/src/main/kotlin/org/jetbrains/kotlin/backend/common/actualizer/ExpectActualCollector.kt index 94cc581507a..86b49710bb9 100644 --- a/compiler/ir/ir.actualization/src/main/kotlin/org/jetbrains/kotlin/backend/common/actualizer/ExpectActualCollector.kt +++ b/compiler/ir/ir.actualization/src/main/kotlin/org/jetbrains/kotlin/backend/common/actualizer/ExpectActualCollector.kt @@ -49,17 +49,15 @@ internal class ExpectActualCollector( private val diagnosticsReporter: IrDiagnosticReporter, private val expectActualTracker: ExpectActualTracker?, ) { - data class Result(val expectToActualMap: MutableMap, val classActualizationInfo: ClassActualizationInfo) - fun collect(): Result { + fun collect(actualDeclarations: ClassActualizationInfo): MutableMap { val result = mutableMapOf() // Collect and link classes at first to make it possible to expand type aliases on the members linking - val actualDeclarations = collectActualDeclarations() matchAllExpectDeclarations(result, actualDeclarations) - return Result(result, actualDeclarations) + return result } - private fun collectActualDeclarations(): ClassActualizationInfo { + fun collectClassActualizationInfo(): ClassActualizationInfo { val expectTopLevelClasses = ExpectTopLevelClassesCollector.collect(dependentFragments) val fragmentsWithActuals = dependentFragments.drop(1) + mainFragment return ActualDeclarationsCollector.collectActualsFromFragments(fragmentsWithActuals, expectTopLevelClasses) diff --git a/compiler/ir/ir.actualization/src/main/kotlin/org/jetbrains/kotlin/backend/common/actualizer/IrActualizer.kt b/compiler/ir/ir.actualization/src/main/kotlin/org/jetbrains/kotlin/backend/common/actualizer/IrActualizer.kt index 490bf5d9ba0..cfa500ff9b4 100644 --- a/compiler/ir/ir.actualization/src/main/kotlin/org/jetbrains/kotlin/backend/common/actualizer/IrActualizer.kt +++ b/compiler/ir/ir.actualization/src/main/kotlin/org/jetbrains/kotlin/backend/common/actualizer/IrActualizer.kt @@ -9,13 +9,18 @@ import org.jetbrains.kotlin.ir.IrDiagnosticReporter import org.jetbrains.kotlin.backend.common.actualizer.checker.IrExpectActualCheckers import org.jetbrains.kotlin.incremental.components.ExpectActualTracker import org.jetbrains.kotlin.ir.declarations.* -import org.jetbrains.kotlin.ir.overrides.FakeOverrideRebuilder -import org.jetbrains.kotlin.ir.overrides.IrFakeOverrideBuilder +import org.jetbrains.kotlin.ir.symbols.IrClassSymbol +import org.jetbrains.kotlin.ir.symbols.IrClassifierSymbol import org.jetbrains.kotlin.ir.symbols.IrSymbol import org.jetbrains.kotlin.ir.types.IrTypeSystemContext +import org.jetbrains.kotlin.ir.types.classOrFail import org.jetbrains.kotlin.ir.util.* +import org.jetbrains.kotlin.utils.addToStdlib.shouldNotBeCalled -data class IrActualizedResult(val actualizedExpectDeclarations: List) +data class IrActualizedResult( + val actualizedExpectDeclarations: List, + val expectActualMap: Map +) /** * IrActualizer is responsible for performing actualization. @@ -24,34 +29,53 @@ data class IrActualizedResult(val actualizedExpectDeclarations: List, - ktDiagnosticReporter: IrDiagnosticReporter, - typeSystemContext: IrTypeSystemContext, - symbolTable: SymbolTable, - fakeOverrideBuilder: IrFakeOverrideBuilder, - useIrFakeOverrideBuilder: Boolean, - expectActualTracker: ExpectActualTracker? - ): IrActualizedResult { +class IrActualizer( + val ktDiagnosticReporter: IrDiagnosticReporter, + val typeSystemContext: IrTypeSystemContext, + expectActualTracker: ExpectActualTracker?, + val useIrFakeOverrideBuilder: Boolean, + val mainFragment: IrModuleFragment, + val dependentFragments: List +) { + private val collector = ExpectActualCollector( + mainFragment, + dependentFragments, + typeSystemContext, + ktDiagnosticReporter, + expectActualTracker, + ) - // The ir modules processing is performed phase-to-phase: - // 1. Collect expect-actual links for classes and their members from dependent fragments - val (expectActualMap, actualDeclarations) = ExpectActualCollector( - mainFragment, - dependentFragments, - typeSystemContext, - ktDiagnosticReporter, - expectActualTracker - ).collect() + private val classActualizationInfo = collector.collectClassActualizationInfo() - IrExpectActualCheckers(expectActualMap, actualDeclarations, typeSystemContext, ktDiagnosticReporter).check() + fun actualizeClassifiers() { + val classSymbolRemapper = object : SymbolRemapper.Empty() { + override fun getReferencedClass(symbol: IrClassSymbol): IrClassSymbol { + if (!symbol.owner.isExpect) return symbol + if (symbol.owner.containsOptionalExpectation()) return symbol + val classId = symbol.owner.classIdOrFail + classActualizationInfo.actualTypeAliases[classId]?.let { return it.owner.expandedType.classOrFail } + classActualizationInfo.actualClasses[classId]?.let { return it } + shouldNotBeCalled("There is no actual class for ${classId}, but this should be already rejected by frontend upto this point") + } - // 2. Remove top-only expect declarations since they are not needed anymore and should not be presented in the final IrFragment - // Expect fake-overrides from non-expect classes remain untouched since they will be actualized in the next phase. - // Also, it doesn't remove unactualized expect declarations marked with @OptionalExpectation - val removedExpectDeclarations = removeExpectDeclarations(dependentFragments, expectActualMap) + override fun getReferencedClassOrNull(symbol: IrClassSymbol?): IrClassSymbol? { + if (symbol == null) return null + return getReferencedClass(symbol) + } + + override fun getReferencedClassifier(symbol: IrClassifierSymbol): IrClassifierSymbol { + if (symbol !is IrClassSymbol) return symbol + return getReferencedClass(symbol) + } + } + val classTypeRemapper = DeepCopyTypeRemapper(classSymbolRemapper) + val classActualizerVisitor = ActualizerVisitor(classSymbolRemapper, classTypeRemapper) + dependentFragments.forEach { it.transform(classActualizerVisitor, null) } + } + + fun actualizeCallablesAndMergeModules(): Map { + // 1. Collect expect-actual links for members of classes found on step 1. + val expectActualMap = collector.collect(classActualizationInfo) if (!useIrFakeOverrideBuilder) { // 3. Actualize expect fake overrides in non-expect classes inside common or multi-platform module. @@ -62,7 +86,7 @@ object IrActualizer { // taken from these non-expect classes actualized super classes. ActualFakeOverridesAdder( expectActualMap, - actualDeclarations.actualClasses, + classActualizationInfo.actualClasses, typeSystemContext ).apply { dependentFragments.forEach { visitModuleFragment(it) } } } @@ -76,15 +100,18 @@ object IrActualizer { val actualizerVisitor = ActualizerVisitor(symbolRemapper, typeRemapper) dependentFragments.forEach { it.transform(actualizerVisitor, null) } - // 7. Merge dependent fragments into the main one + // 8. Move all declarations to mainFragment mergeIrFragments(mainFragment, dependentFragments) + return expectActualMap + } - if (useIrFakeOverrideBuilder) { - // 8. Rebuild fake overrides from stretch, as they could become invalid during actualization - FakeOverrideRebuilder(symbolTable, fakeOverrideBuilder).rebuildFakeOverrides(mainFragment) - } + fun runChecksAndFinalize(expectActualMap: Map) : IrActualizedResult { + // Remove top-only expect declarations since they are not needed anymore and should not be presented in the final IrFragment + // Also, it doesn't remove unactualized expect declarations marked with @OptionalExpectation + val removedExpectDeclarations = removeExpectDeclarations(dependentFragments, expectActualMap) - return IrActualizedResult(removedExpectDeclarations) + IrExpectActualCheckers(expectActualMap, classActualizationInfo, typeSystemContext, ktDiagnosticReporter).check() + return IrActualizedResult(removedExpectDeclarations, expectActualMap) } private fun removeExpectDeclarations(dependentFragments: List, expectActualMap: Map): List { diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/overrides/FakeOverrideRebuilder.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/overrides/FakeOverrideRebuilder.kt deleted file mode 100644 index 662a7a7422a..00000000000 --- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/overrides/FakeOverrideRebuilder.kt +++ /dev/null @@ -1,241 +0,0 @@ -/* - * 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.ir.overrides - -import org.jetbrains.kotlin.ir.IrElement -import org.jetbrains.kotlin.ir.declarations.* -import org.jetbrains.kotlin.ir.declarations.lazy.IrLazyDeclarationBase -import org.jetbrains.kotlin.ir.expressions.IrCall -import org.jetbrains.kotlin.ir.expressions.IrFunctionReference -import org.jetbrains.kotlin.ir.expressions.IrLocalDelegatedPropertyReference -import org.jetbrains.kotlin.ir.expressions.IrPropertyReference -import org.jetbrains.kotlin.ir.symbols.IrClassSymbol -import org.jetbrains.kotlin.ir.symbols.IrSymbol -import org.jetbrains.kotlin.ir.types.getClass -import org.jetbrains.kotlin.ir.util.* -import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid -import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid -import org.jetbrains.kotlin.ir.visitors.acceptVoid -import org.jetbrains.kotlin.utils.addToStdlib.shouldNotBeCalled -import org.jetbrains.kotlin.utils.newHashSetWithExpectedSize - -/** - * After actualization, fake overrides can be incorrect. - * - * Fake overrides can change even in classes, which don't have expect/actuals in their superclasses. - * And this change can be more non-trivial than just substituting expect class with actual. - * - * For example, if an expect class is actualized to a common class, or several expect classes are actualized to the same class, - * several fake overrides can be merged with each other, or with real functions. - * - * To fix that, we are just rebuilding fake overrides from scratch, after actualization is done. - * - * This is done in 3 steps: - * 1. Remove all fake overrides from all declarations, and remove their symbols from the symbol table. - * 2. Build new fake overrides and map the old one to some function now existing in the class (which can also be a real function). - * 3. Remap call-sites of all functions using the map collected on step 2. - * - * Steps 1 and 3 are kinda trivial, except we should remove all fake overrides from symbol table. - * - * For matching in step 2, classes are processed in such order, that the superclass is processed before each class. - * This allows us to match using `overriddenSymbols`. In particular, f/o is mapped to only function - * overriding any of functions (possibly already mapped), which was overridden by initial fake override. - * - * Here we assume that functions can only be merged, not split, i.e. if the same fake override was overriding several - * super-functions, it's impossible to have different function overriding them after actualization. - */ -class FakeOverrideRebuilder( - val symbolTable: SymbolTable, - val fakeOverrideBuilder: IrFakeOverrideBuilder, -) { - private val removedFakeOverrides = mutableMapOf>() - private val processedClasses = hashSetOf() - - // Map from the old fake override symbol to the new (rebuilt) symbol. - private val fakeOverrideMap = hashMapOf() - - - fun rebuildFakeOverrides(irModule: IrModuleFragment) { - irModule.acceptVoid(RemoveFakeOverridesVisitor(removedFakeOverrides, symbolTable)) - for (clazz in removedFakeOverrides.keys) { - rebuildClassFakeOverrides(clazz.owner) - } - irModule.acceptVoid(RemapFakeOverridesVisitor(fakeOverrideMap)) - } - - private fun collectOverriddenDeclarations( - member: IrOverridableDeclaration<*>, - result: MutableSet>, - visited: MutableSet>, - stopAtReal: Boolean, - ) { - if (!visited.add(member)) return - if (member.isReal) { - result.add(member) - if (stopAtReal) return - } else { - require(member.overriddenSymbols.isNotEmpty()) { "No overridden symbols found for (fake override) ${member.render()}" } - } - for (overridden in member.overriddenSymbols) { - collectOverriddenDeclarations(overridden.owner as IrOverridableDeclaration<*>, result, visited, stopAtReal) - } - } - - private fun IrOverridableDeclaration<*>.getOverriddenDeclarations(stopAtReal: Boolean): Set> = - mutableSetOf>().also { result -> - collectOverriddenDeclarations(this, result, hashSetOf(), stopAtReal) - } - - private fun MutableMap.processDeclaration(declaration: IrOverridableDeclaration<*>, irClass: IrClass) { - for (overridden in declaration.getOverriddenDeclarations(false)) { - val previousSymbol = put(overridden.symbol, declaration.symbol) - if (previousSymbol != null) { - val previous = previousSymbol.owner as IrDeclaration - error( - "Multiple overrides in class ${irClass.fqNameWhenAvailable} for ${overridden.render()}:\n" + - " previous: ${previous.render()}\n" + - " declared in ${previous.parentAsClass.fqNameWhenAvailable}\n" + - " new: ${declaration.render()}\n" + - " declared in ${declaration.parentAsClass.fqNameWhenAvailable}" - ) - } - } - } - - private fun rebuildClassFakeOverrides(irClass: IrClass) { - if (irClass is IrLazyDeclarationBase) return - if (!processedClasses.add(irClass)) return - val oldList = removedFakeOverrides[irClass.symbol] ?: return - for (c in irClass.superTypes) { - c.getClass()?.let { rebuildClassFakeOverrides(it) } - } - fakeOverrideBuilder.buildFakeOverridesForClass(irClass, false) - - val overriddenMap = mutableMapOf() - - for (declaration in irClass.declarations) { - when (declaration) { - is IrSimpleFunction -> overriddenMap.processDeclaration(declaration, irClass) - is IrProperty -> { - overriddenMap.processDeclaration(declaration, irClass) - declaration.getter?.let { overriddenMap.processDeclaration(it, irClass) } - declaration.setter?.let { overriddenMap.processDeclaration(it, irClass) } - } - } - } - - for (old in oldList) { - val overridden = mutableSetOf>() - collectOverriddenDeclarations(old.owner as IrOverridableDeclaration<*>, overridden, hashSetOf(), true) - val newSymbols = overridden.mapTo(newHashSetWithExpectedSize(1)) { - overriddenMap[it.symbol] - ?: error("No new fake override recorded for declaration in class ${irClass.fqNameWhenAvailable}: ${it.render()}") - } - when (newSymbols.size) { - 0 -> error("No overridden found for declaration in class ${irClass.fqNameWhenAvailable}: ${old.owner.render()}") - 1 -> fakeOverrideMap[old] = newSymbols.single() - else -> error( - "Multiple overridden found for declaration in class ${irClass.fqNameWhenAvailable}: ${old.owner.render()}\n" + - newSymbols.joinToString("\n") { - " - ${it.owner.render()} (declared in ${(it.owner as IrDeclaration).parentAsClass.fqNameWhenAvailable}" - } - ) - } - } - } -} - -private class RemoveFakeOverridesVisitor( - val removedOverrides: MutableMap>, - val symbolTable: SymbolTable -) : IrElementVisitorVoid { - - override fun visitElement(element: IrElement) { - element.acceptChildrenVoid(this) - } - - @OptIn(DelicateSymbolTableApi::class) - override fun visitClass(declaration: IrClass) { - val curList = mutableListOf() - declaration.declarations.removeIf { - if (it is IrOverridableDeclaration<*> && it.isFakeOverride) { - when (it) { - is IrProperty -> { - curList.add(it.symbol) - symbolTable.removeProperty(it.symbol) - it.getter?.symbol?.let { getter -> - curList.add(getter) - symbolTable.removeSimpleFunction(getter) - } - it.setter?.symbol?.let { setter -> - curList.add(setter) - symbolTable.removeSimpleFunction(setter) - } - } - is IrSimpleFunction -> { - curList.add(it.symbol) - symbolTable.removeSimpleFunction(it.symbol) - } - else -> shouldNotBeCalled("Only simple functions and properties can be overridden") - } - true - } else { - false - } - } - if (curList.isNotEmpty()) { - removedOverrides[declaration.symbol] = curList - } - declaration.acceptChildrenVoid(this) - } -} - - -// TODO: KT-61561 it seems this class is too generic to be here. -// For some reason, we don't have utility class doing that, but we probably should, as we have DeepCopy util classes -// Probably, it should also be generated, to avoid missing some places where symbols can happen. -private class RemapFakeOverridesVisitor(val fakeOverridesMap: Map) : IrElementVisitorVoid { - override fun visitElement(element: IrElement) { - element.acceptChildrenVoid(this) - } - - override fun visitSimpleFunction(declaration: IrSimpleFunction) { - declaration.overriddenSymbols = declaration.overriddenSymbols.map { it.remap() } - declaration.acceptChildrenVoid(this) - } - - override fun visitProperty(declaration: IrProperty) { - declaration.overriddenSymbols = declaration.overriddenSymbols.map { it.remap() } - declaration.acceptChildrenVoid(this) - } - - override fun visitCall(expression: IrCall) { - expression.symbol = expression.symbol.remap() - expression.acceptChildrenVoid(this) - } - - override fun visitFunctionReference(expression: IrFunctionReference) { - expression.symbol = expression.symbol.remap() - expression.reflectionTarget = expression.reflectionTarget?.remap() - expression.acceptChildrenVoid(this) - } - - override fun visitPropertyReference(expression: IrPropertyReference) { - expression.symbol = expression.symbol.remap() - expression.getter = expression.getter?.remap() - expression.setter = expression.setter?.remap() - expression.acceptChildrenVoid(this) - } - - override fun visitLocalDelegatedPropertyReference(expression: IrLocalDelegatedPropertyReference) { - expression.getter = expression.getter.remap() - expression.setter = expression.setter?.remap() - expression.acceptChildrenVoid(this) - } - - private inline fun S.remap(): S = - fakeOverridesMap[this] as S? ?: this -} diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/overrides/IrFakeOverrideBuilder.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/overrides/IrFakeOverrideBuilder.kt index 8351297eb55..e6fac575419 100644 --- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/overrides/IrFakeOverrideBuilder.kt +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/overrides/IrFakeOverrideBuilder.kt @@ -8,7 +8,9 @@ package org.jetbrains.kotlin.ir.overrides import org.jetbrains.kotlin.descriptors.DescriptorVisibilities import org.jetbrains.kotlin.descriptors.DescriptorVisibility import org.jetbrains.kotlin.descriptors.Modality +import org.jetbrains.kotlin.ir.IrElement import org.jetbrains.kotlin.ir.declarations.* +import org.jetbrains.kotlin.ir.declarations.lazy.IrLazyDeclarationBase import org.jetbrains.kotlin.ir.symbols.IrPropertySymbol import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol import org.jetbrains.kotlin.ir.symbols.IrSymbol @@ -16,6 +18,9 @@ import org.jetbrains.kotlin.ir.types.* import org.jetbrains.kotlin.ir.util.collectAndFilterRealOverrides import org.jetbrains.kotlin.ir.util.fileOrNull import org.jetbrains.kotlin.ir.util.render +import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid +import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid +import org.jetbrains.kotlin.ir.visitors.acceptVoid import org.jetbrains.kotlin.resolve.OverridingUtil.OverrideCompatibilityInfo import org.jetbrains.kotlin.types.AbstractTypeChecker import org.jetbrains.kotlin.types.TypeCheckerState @@ -515,3 +520,27 @@ fun IrDeclaration.isOverridableMemberOrAccessor(): Boolean = when (this) { is IrProperty -> isOverridableProperty() else -> false } + +fun IrFakeOverrideBuilder.buildForAll(modules: List) { + val builtFakeOverridesClasses = mutableSetOf() + fun buildFakeOverrides(clazz: IrClass) { + if (clazz is IrLazyDeclarationBase) return + if (!builtFakeOverridesClasses.add(clazz)) return + for (c in clazz.superTypes) { + c.getClass()?.let { buildFakeOverrides(it) } + } + buildFakeOverridesForClass(clazz, false) + } + class ClassVisitor : IrElementVisitorVoid { + override fun visitElement(element: IrElement) { + element.acceptChildrenVoid(this) + } + override fun visitClass(declaration: IrClass) { + buildFakeOverrides(declaration) + declaration.acceptChildrenVoid(this) + } + } + for (module in modules) { + module.acceptVoid(ClassVisitor()) + } +} \ No newline at end of file diff --git a/compiler/testData/codegen/box/multiplatform/hmpp/simple.kt b/compiler/testData/codegen/box/multiplatform/hmpp/simple.kt index 11228af140a..ddcd9c537f9 100644 --- a/compiler/testData/codegen/box/multiplatform/hmpp/simple.kt +++ b/compiler/testData/codegen/box/multiplatform/hmpp/simple.kt @@ -5,7 +5,6 @@ // WITH_STDLIB // !LANGUAGE: +MultiPlatformProjects -// IGNORE_CODEGEN_WITH_IR_FAKE_OVERRIDE_GENERATION: KT-62535 // TARGET_BACKEND: JVM // MODULE: common // TARGET_PLATFORM: Common diff --git a/compiler/testData/codegen/box/multiplatform/k2/basic/expectInterfaceInSupertypes.kt b/compiler/testData/codegen/box/multiplatform/k2/basic/expectInterfaceInSupertypes.kt index 04b1457ecfe..05dc206d8c4 100644 --- a/compiler/testData/codegen/box/multiplatform/k2/basic/expectInterfaceInSupertypes.kt +++ b/compiler/testData/codegen/box/multiplatform/k2/basic/expectInterfaceInSupertypes.kt @@ -1,6 +1,5 @@ // WITH_STDLIB // LANGUAGE: +MultiPlatformProjects -// IGNORE_CODEGEN_WITH_IR_FAKE_OVERRIDE_GENERATION: KT-62535 // MODULE: common // TARGET_PLATFORM: Common // FILE: common.kt diff --git a/compiler/testData/codegen/box/multiplatform/k2/basic/expectInterfaceInSupertypes2.kt b/compiler/testData/codegen/box/multiplatform/k2/basic/expectInterfaceInSupertypes2.kt index e5f8168d5a6..946505b2b95 100644 --- a/compiler/testData/codegen/box/multiplatform/k2/basic/expectInterfaceInSupertypes2.kt +++ b/compiler/testData/codegen/box/multiplatform/k2/basic/expectInterfaceInSupertypes2.kt @@ -1,5 +1,4 @@ // WITH_STDLIB -// IGNORE_CODEGEN_WITH_IR_FAKE_OVERRIDE_GENERATION: KT-62535 // LANGUAGE: +MultiPlatformProjects // MODULE: common