[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:
committed by
Space Team
parent
9609ac6908
commit
c485382e80
@@ -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<ModuleCompilerAnalyzedOutput>)
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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<IrDeclaration>) {
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
+3
-5
@@ -49,17 +49,15 @@ internal class ExpectActualCollector(
|
||||
private val diagnosticsReporter: IrDiagnosticReporter,
|
||||
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>()
|
||||
// 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)
|
||||
|
||||
+62
-35
@@ -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<IrDeclaration>)
|
||||
data class IrActualizedResult(
|
||||
val actualizedExpectDeclarations: List<IrDeclaration>,
|
||||
val expectActualMap: Map<IrSymbol, IrSymbol>
|
||||
)
|
||||
|
||||
/**
|
||||
* IrActualizer is responsible for performing actualization.
|
||||
@@ -24,34 +29,53 @@ data class IrActualizedResult(val actualizedExpectDeclarations: List<IrDeclarati
|
||||
*
|
||||
* See `/docs/fir/k2_kmp.md`
|
||||
*/
|
||||
object IrActualizer {
|
||||
fun actualize(
|
||||
mainFragment: IrModuleFragment,
|
||||
dependentFragments: List<IrModuleFragment>,
|
||||
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<IrModuleFragment>
|
||||
) {
|
||||
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<IrSymbol, IrSymbol> {
|
||||
// 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<IrSymbol, IrSymbol>) : 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<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.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<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
|
||||
// !LANGUAGE: +MultiPlatformProjects
|
||||
// IGNORE_CODEGEN_WITH_IR_FAKE_OVERRIDE_GENERATION: KT-62535
|
||||
// TARGET_BACKEND: JVM
|
||||
// MODULE: common
|
||||
// TARGET_PLATFORM: Common
|
||||
|
||||
-1
@@ -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
|
||||
|
||||
-1
@@ -1,5 +1,4 @@
|
||||
// WITH_STDLIB
|
||||
// IGNORE_CODEGEN_WITH_IR_FAKE_OVERRIDE_GENERATION: KT-62535
|
||||
// LANGUAGE: +MultiPlatformProjects
|
||||
|
||||
// MODULE: common
|
||||
|
||||
Reference in New Issue
Block a user