[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
This commit is contained in:
Pavel Kunyavskiy
2023-11-23 15:44:35 +01:00
committed by Space Team
parent 9609ac6908
commit c485382e80
9 changed files with 144 additions and 366 deletions
@@ -21,9 +21,11 @@ import org.jetbrains.kotlin.ir.IrBuiltIns
import org.jetbrains.kotlin.ir.backend.jvm.serialization.JvmDescriptorMangler import org.jetbrains.kotlin.ir.backend.jvm.serialization.JvmDescriptorMangler
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
import org.jetbrains.kotlin.ir.declarations.impl.IrFactoryImpl 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.types.IrTypeSystemContext
import org.jetbrains.kotlin.ir.util.IdSignatureComposer import org.jetbrains.kotlin.ir.util.IdSignatureComposer
import org.jetbrains.kotlin.ir.util.KotlinMangler import org.jetbrains.kotlin.ir.util.KotlinMangler
import org.jetbrains.kotlin.ir.visitors.acceptVoid
data class FirResult(val outputs: List<ModuleCompilerAnalyzedOutput>) data class FirResult(val outputs: List<ModuleCompilerAnalyzedOutput>)
@@ -61,85 +63,58 @@ fun FirResult.convertToIrAndActualize(
actualizerTypeContextProvider: (IrBuiltIns) -> IrTypeSystemContext, actualizerTypeContextProvider: (IrBuiltIns) -> IrTypeSystemContext,
fir2IrResultPostCompute: (ModuleCompilerAnalyzedOutput, Fir2IrResult) -> Unit = { _, _ -> }, fir2IrResultPostCompute: (ModuleCompilerAnalyzedOutput, Fir2IrResult) -> Unit = { _, _ -> },
): Fir2IrActualizedResult { ): Fir2IrActualizedResult {
val fir2IrResult: Fir2IrResult
val actualizationResult: IrActualizedResult?
val commonMemberStorage = Fir2IrCommonMemberStorage(signatureComposer, firMangler) val commonMemberStorage = Fir2IrCommonMemberStorage(signatureComposer, firMangler)
when (outputs.size) { require(outputs.isNotEmpty()) { "No modules found" }
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)
}
actualizationResult = IrActualizer.actualize( var irBuiltIns: IrBuiltInsOverFir? = null
fir2IrResult.irModuleFragment, val irOutputs = outputs.map {
commonIrOutputs.map { it.irModuleFragment }, convertToIr(
KtDiagnosticReporterWithImplicitIrBasedContext( it,
fir2IrConfiguration.diagnosticReporter, // We need to build all modules before rebuilding fake overrides
fir2IrConfiguration.languageVersionSettings // to avoid fixing declaration storages
), fir2IrExtensions,
actualizerTypeContextProvider(fir2IrResult.irModuleFragment.irBuiltins), fir2IrConfiguration,
commonMemberStorage.symbolTable, commonMemberStorage = commonMemberStorage,
fir2IrResult.components.fakeOverrideBuilder, irBuiltIns = irBuiltIns,
fir2IrConfiguration.useIrFakeOverrideBuilder, irMangler,
fir2IrConfiguration.expectActualTracker, 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) pluginContext.applyIrGenerationExtensions(irModuleFragment, irGeneratorExtensions)
return Fir2IrActualizedResult(irModuleFragment, components, pluginContext, actualizationResult) return Fir2IrActualizedResult(irModuleFragment, components, pluginContext, actualizationResult)
} }
@@ -11,7 +11,6 @@ import org.jetbrains.kotlin.KtFakeSourceElementKind
import org.jetbrains.kotlin.KtPsiSourceFileLinesMapping import org.jetbrains.kotlin.KtPsiSourceFileLinesMapping
import org.jetbrains.kotlin.KtSourceFileLinesMappingFromLineStartOffsets import org.jetbrains.kotlin.KtSourceFileLinesMappingFromLineStartOffsets
import org.jetbrains.kotlin.backend.common.CommonBackendErrors 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.backend.common.sourceElement
import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.config.LanguageFeature import org.jetbrains.kotlin.config.LanguageFeature
@@ -129,8 +128,6 @@ class Fir2IrConverter(
@OptIn(LeakedDeclarationCaches::class) @OptIn(LeakedDeclarationCaches::class)
declarationStorage.fillUnboundSymbols() declarationStorage.fillUnboundSymbols()
} }
evaluateConstants(irModuleFragment, components)
} }
fun bindFakeOverridesOrPostpone(declarations: List<IrDeclaration>) { fun bindFakeOverridesOrPostpone(declarations: List<IrDeclaration>) {
@@ -618,7 +615,8 @@ class Fir2IrConverter(
} }
companion object { 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 fir2IrConfiguration = components.configuration
val firModuleDescriptor = irModuleFragment.descriptor as? FirModuleDescriptor val firModuleDescriptor = irModuleFragment.descriptor as? FirModuleDescriptor
val targetPlatform = firModuleDescriptor?.platform val targetPlatform = firModuleDescriptor?.platform
@@ -745,11 +743,6 @@ class Fir2IrConverter(
components.fir2IrVisitor components.fir2IrVisitor
) )
if (fir2IrConfiguration.useIrFakeOverrideBuilder) {
val rebuilder = FakeOverrideRebuilder(commonMemberStorage.symbolTable, components.fakeOverrideBuilder)
rebuilder.rebuildFakeOverrides(irModuleFragment)
}
return Fir2IrResult(irModuleFragment, components, moduleDescriptor) return Fir2IrResult(irModuleFragment, components, moduleDescriptor)
} }
} }
@@ -49,17 +49,15 @@ internal class ExpectActualCollector(
private val diagnosticsReporter: IrDiagnosticReporter, private val diagnosticsReporter: IrDiagnosticReporter,
private val expectActualTracker: ExpectActualTracker?, private val expectActualTracker: ExpectActualTracker?,
) { ) {
data class Result(val expectToActualMap: MutableMap<IrSymbol, IrSymbol>, val classActualizationInfo: ClassActualizationInfo)
fun collect(): Result { fun collect(actualDeclarations: ClassActualizationInfo): MutableMap<IrSymbol, IrSymbol> {
val result = mutableMapOf<IrSymbol, IrSymbol>() val result = mutableMapOf<IrSymbol, IrSymbol>()
// Collect and link classes at first to make it possible to expand type aliases on the members linking // Collect and link classes at first to make it possible to expand type aliases on the members linking
val actualDeclarations = collectActualDeclarations()
matchAllExpectDeclarations(result, actualDeclarations) matchAllExpectDeclarations(result, actualDeclarations)
return Result(result, actualDeclarations) return result
} }
private fun collectActualDeclarations(): ClassActualizationInfo { fun collectClassActualizationInfo(): ClassActualizationInfo {
val expectTopLevelClasses = ExpectTopLevelClassesCollector.collect(dependentFragments) val expectTopLevelClasses = ExpectTopLevelClassesCollector.collect(dependentFragments)
val fragmentsWithActuals = dependentFragments.drop(1) + mainFragment val fragmentsWithActuals = dependentFragments.drop(1) + mainFragment
return ActualDeclarationsCollector.collectActualsFromFragments(fragmentsWithActuals, expectTopLevelClasses) return ActualDeclarationsCollector.collectActualsFromFragments(fragmentsWithActuals, expectTopLevelClasses)
@@ -9,13 +9,18 @@ import org.jetbrains.kotlin.ir.IrDiagnosticReporter
import org.jetbrains.kotlin.backend.common.actualizer.checker.IrExpectActualCheckers import org.jetbrains.kotlin.backend.common.actualizer.checker.IrExpectActualCheckers
import org.jetbrains.kotlin.incremental.components.ExpectActualTracker import org.jetbrains.kotlin.incremental.components.ExpectActualTracker
import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.overrides.FakeOverrideRebuilder import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
import org.jetbrains.kotlin.ir.overrides.IrFakeOverrideBuilder import org.jetbrains.kotlin.ir.symbols.IrClassifierSymbol
import org.jetbrains.kotlin.ir.symbols.IrSymbol import org.jetbrains.kotlin.ir.symbols.IrSymbol
import org.jetbrains.kotlin.ir.types.IrTypeSystemContext import org.jetbrains.kotlin.ir.types.IrTypeSystemContext
import org.jetbrains.kotlin.ir.types.classOrFail
import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.utils.addToStdlib.shouldNotBeCalled
data class IrActualizedResult(val actualizedExpectDeclarations: List<IrDeclaration>) data class IrActualizedResult(
val actualizedExpectDeclarations: List<IrDeclaration>,
val expectActualMap: Map<IrSymbol, IrSymbol>
)
/** /**
* IrActualizer is responsible for performing actualization. * IrActualizer is responsible for performing actualization.
@@ -24,34 +29,53 @@ data class IrActualizedResult(val actualizedExpectDeclarations: List<IrDeclarati
* *
* See `/docs/fir/k2_kmp.md` * See `/docs/fir/k2_kmp.md`
*/ */
object IrActualizer { class IrActualizer(
fun actualize( val ktDiagnosticReporter: IrDiagnosticReporter,
mainFragment: IrModuleFragment, val typeSystemContext: IrTypeSystemContext,
dependentFragments: List<IrModuleFragment>, expectActualTracker: ExpectActualTracker?,
ktDiagnosticReporter: IrDiagnosticReporter, val useIrFakeOverrideBuilder: Boolean,
typeSystemContext: IrTypeSystemContext, val mainFragment: IrModuleFragment,
symbolTable: SymbolTable, val dependentFragments: List<IrModuleFragment>
fakeOverrideBuilder: IrFakeOverrideBuilder, ) {
useIrFakeOverrideBuilder: Boolean, private val collector = ExpectActualCollector(
expectActualTracker: ExpectActualTracker? mainFragment,
): IrActualizedResult { dependentFragments,
typeSystemContext,
ktDiagnosticReporter,
expectActualTracker,
)
// The ir modules processing is performed phase-to-phase: private val classActualizationInfo = collector.collectClassActualizationInfo()
// 1. Collect expect-actual links for classes and their members from dependent fragments
val (expectActualMap, actualDeclarations) = ExpectActualCollector(
mainFragment,
dependentFragments,
typeSystemContext,
ktDiagnosticReporter,
expectActualTracker
).collect()
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 override fun getReferencedClassOrNull(symbol: IrClassSymbol?): IrClassSymbol? {
// Expect fake-overrides from non-expect classes remain untouched since they will be actualized in the next phase. if (symbol == null) return null
// Also, it doesn't remove unactualized expect declarations marked with @OptionalExpectation return getReferencedClass(symbol)
val removedExpectDeclarations = removeExpectDeclarations(dependentFragments, expectActualMap) }
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<IrSymbol, IrSymbol> {
// 1. Collect expect-actual links for members of classes found on step 1.
val expectActualMap = collector.collect(classActualizationInfo)
if (!useIrFakeOverrideBuilder) { if (!useIrFakeOverrideBuilder) {
// 3. Actualize expect fake overrides in non-expect classes inside common or multi-platform module. // 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. // taken from these non-expect classes actualized super classes.
ActualFakeOverridesAdder( ActualFakeOverridesAdder(
expectActualMap, expectActualMap,
actualDeclarations.actualClasses, classActualizationInfo.actualClasses,
typeSystemContext typeSystemContext
).apply { dependentFragments.forEach { visitModuleFragment(it) } } ).apply { dependentFragments.forEach { visitModuleFragment(it) } }
} }
@@ -76,15 +100,18 @@ object IrActualizer {
val actualizerVisitor = ActualizerVisitor(symbolRemapper, typeRemapper) val actualizerVisitor = ActualizerVisitor(symbolRemapper, typeRemapper)
dependentFragments.forEach { it.transform(actualizerVisitor, null) } dependentFragments.forEach { it.transform(actualizerVisitor, null) }
// 7. Merge dependent fragments into the main one // 8. Move all declarations to mainFragment
mergeIrFragments(mainFragment, dependentFragments) mergeIrFragments(mainFragment, dependentFragments)
return expectActualMap
}
if (useIrFakeOverrideBuilder) { fun runChecksAndFinalize(expectActualMap: Map<IrSymbol, IrSymbol>) : IrActualizedResult {
// 8. Rebuild fake overrides from stretch, as they could become invalid during actualization // Remove top-only expect declarations since they are not needed anymore and should not be presented in the final IrFragment
FakeOverrideRebuilder(symbolTable, fakeOverrideBuilder).rebuildFakeOverrides(mainFragment) // 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<IrModuleFragment>, expectActualMap: Map<IrSymbol, IrSymbol>): List<IrDeclaration> { private fun removeExpectDeclarations(dependentFragments: List<IrModuleFragment>, expectActualMap: Map<IrSymbol, IrSymbol>): List<IrDeclaration> {
@@ -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<IrClassSymbol, List<IrSymbol>>()
private val processedClasses = hashSetOf<IrClass>()
// Map from the old fake override symbol to the new (rebuilt) symbol.
private val fakeOverrideMap = hashMapOf<IrSymbol, IrSymbol>()
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<IrOverridableDeclaration<*>>,
visited: MutableSet<IrOverridableDeclaration<*>>,
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<IrOverridableDeclaration<*>> =
mutableSetOf<IrOverridableDeclaration<*>>().also { result ->
collectOverriddenDeclarations(this, result, hashSetOf(), stopAtReal)
}
private fun MutableMap<IrSymbol, IrSymbol>.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<IrSymbol, IrSymbol>()
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<IrOverridableDeclaration<*>>()
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<IrClassSymbol, List<IrSymbol>>,
val symbolTable: SymbolTable
) : IrElementVisitorVoid {
override fun visitElement(element: IrElement) {
element.acceptChildrenVoid(this)
}
@OptIn(DelicateSymbolTableApi::class)
override fun visitClass(declaration: IrClass) {
val curList = mutableListOf<IrSymbol>()
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<IrSymbol, IrSymbol>) : 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 <reified S : IrSymbol> S.remap(): S =
fakeOverridesMap[this] as S? ?: this
}
@@ -8,7 +8,9 @@ package org.jetbrains.kotlin.ir.overrides
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
import org.jetbrains.kotlin.descriptors.DescriptorVisibility import org.jetbrains.kotlin.descriptors.DescriptorVisibility
import org.jetbrains.kotlin.descriptors.Modality import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.declarations.* 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.IrPropertySymbol
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
import org.jetbrains.kotlin.ir.symbols.IrSymbol 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.collectAndFilterRealOverrides
import org.jetbrains.kotlin.ir.util.fileOrNull import org.jetbrains.kotlin.ir.util.fileOrNull
import org.jetbrains.kotlin.ir.util.render 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.resolve.OverridingUtil.OverrideCompatibilityInfo
import org.jetbrains.kotlin.types.AbstractTypeChecker import org.jetbrains.kotlin.types.AbstractTypeChecker
import org.jetbrains.kotlin.types.TypeCheckerState import org.jetbrains.kotlin.types.TypeCheckerState
@@ -515,3 +520,27 @@ fun IrDeclaration.isOverridableMemberOrAccessor(): Boolean = when (this) {
is IrProperty -> isOverridableProperty() is IrProperty -> isOverridableProperty()
else -> false else -> false
} }
fun IrFakeOverrideBuilder.buildForAll(modules: List<IrModuleFragment>) {
val builtFakeOverridesClasses = mutableSetOf<IrClass>()
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())
}
}
@@ -5,7 +5,6 @@
// WITH_STDLIB // WITH_STDLIB
// !LANGUAGE: +MultiPlatformProjects // !LANGUAGE: +MultiPlatformProjects
// IGNORE_CODEGEN_WITH_IR_FAKE_OVERRIDE_GENERATION: KT-62535
// TARGET_BACKEND: JVM // TARGET_BACKEND: JVM
// MODULE: common // MODULE: common
// TARGET_PLATFORM: Common // TARGET_PLATFORM: Common
@@ -1,6 +1,5 @@
// WITH_STDLIB // WITH_STDLIB
// LANGUAGE: +MultiPlatformProjects // LANGUAGE: +MultiPlatformProjects
// IGNORE_CODEGEN_WITH_IR_FAKE_OVERRIDE_GENERATION: KT-62535
// MODULE: common // MODULE: common
// TARGET_PLATFORM: Common // TARGET_PLATFORM: Common
// FILE: common.kt // FILE: common.kt
@@ -1,5 +1,4 @@
// WITH_STDLIB // WITH_STDLIB
// IGNORE_CODEGEN_WITH_IR_FAKE_OVERRIDE_GENERATION: KT-62535
// LANGUAGE: +MultiPlatformProjects // LANGUAGE: +MultiPlatformProjects
// MODULE: common // MODULE: common