[IrActualizer] Update doc on how the actualization process works.
This commit is contained in:
committed by
Space Team
parent
7813bb35cf
commit
f50fab65cc
+5
-5
@@ -78,11 +78,11 @@ class IrActualizer(
|
|||||||
val expectActualMap = collector.collect(classActualizationInfo)
|
val expectActualMap = collector.collect(classActualizationInfo)
|
||||||
|
|
||||||
if (!useIrFakeOverrideBuilder) {
|
if (!useIrFakeOverrideBuilder) {
|
||||||
// 3. Actualize expect fake overrides in non-expect classes inside common or multi-platform module.
|
// 2. Actualize expect fake overrides in non-expect classes inside common or multi-platform module.
|
||||||
// It's probably important to run FakeOverridesActualizer before ActualFakeOverridesAdder
|
// It's probably important to run FakeOverridesActualizer before ActualFakeOverridesAdder
|
||||||
FakeOverridesActualizer(expectActualMap).apply { dependentFragments.forEach { visitModuleFragment(it) } }
|
FakeOverridesActualizer(expectActualMap).apply { dependentFragments.forEach { visitModuleFragment(it) } }
|
||||||
|
|
||||||
// 4. Add fake overrides to non-expect classes inside common or multi-platform module,
|
// 3. Add fake overrides to non-expect classes inside common or multi-platform module,
|
||||||
// taken from these non-expect classes actualized super classes.
|
// taken from these non-expect classes actualized super classes.
|
||||||
ActualFakeOverridesAdder(
|
ActualFakeOverridesAdder(
|
||||||
expectActualMap,
|
expectActualMap,
|
||||||
@@ -91,16 +91,16 @@ class IrActualizer(
|
|||||||
).apply { dependentFragments.forEach { visitModuleFragment(it) } }
|
).apply { dependentFragments.forEach { visitModuleFragment(it) } }
|
||||||
}
|
}
|
||||||
|
|
||||||
// 5. Copy and actualize function parameter default values from expect functions
|
// 4. Copy and actualize function parameter default values from expect functions
|
||||||
val symbolRemapper = ActualizerSymbolRemapper(expectActualMap)
|
val symbolRemapper = ActualizerSymbolRemapper(expectActualMap)
|
||||||
val typeRemapper = DeepCopyTypeRemapper(symbolRemapper)
|
val typeRemapper = DeepCopyTypeRemapper(symbolRemapper)
|
||||||
FunctionDefaultParametersActualizer(symbolRemapper, typeRemapper, expectActualMap).actualize()
|
FunctionDefaultParametersActualizer(symbolRemapper, typeRemapper, expectActualMap).actualize()
|
||||||
|
|
||||||
// 6. Actualize expect calls in dependent fragments using info obtained in the previous steps
|
// 5. Actualize expect calls in dependent fragments using info obtained in the previous steps
|
||||||
val actualizerVisitor = ActualizerVisitor(symbolRemapper, typeRemapper)
|
val actualizerVisitor = ActualizerVisitor(symbolRemapper, typeRemapper)
|
||||||
dependentFragments.forEach { it.transform(actualizerVisitor, null) }
|
dependentFragments.forEach { it.transform(actualizerVisitor, null) }
|
||||||
|
|
||||||
// 8. Move all declarations to mainFragment
|
// 6. Move all declarations to mainFragment
|
||||||
mergeIrFragments(mainFragment, dependentFragments)
|
mergeIrFragments(mainFragment, dependentFragments)
|
||||||
return expectActualMap
|
return expectActualMap
|
||||||
}
|
}
|
||||||
|
|||||||
+87
-38
@@ -10,8 +10,9 @@ This document describes the implementation of the KMP support in the K2 Compiler
|
|||||||
- [Metadata Compilation](#metadata-compilation)
|
- [Metadata Compilation](#metadata-compilation)
|
||||||
- [Platform Compilation](#platform-compilation)
|
- [Platform Compilation](#platform-compilation)
|
||||||
- [FIR2IR](#fir2ir)
|
- [FIR2IR](#fir2ir)
|
||||||
- [IRActualizer](#iractualizer)
|
|
||||||
- [Fake overrides](#fake-overrides)
|
- [Fake overrides](#fake-overrides)
|
||||||
|
- [Lazy classes](#lazy-classes)
|
||||||
|
- [IRActualizer](#iractualizer)
|
||||||
- [Frontend support for expect/actual](#frontend-support-for-expectactual)
|
- [Frontend support for expect/actual](#frontend-support-for-expectactual)
|
||||||
- [Type refinement](#type-refinement)
|
- [Type refinement](#type-refinement)
|
||||||
- [Default propagation](#default-propagation)
|
- [Default propagation](#default-propagation)
|
||||||
@@ -256,46 +257,12 @@ The same applies to the declarations from [binary dependencies](#platform-compil
|
|||||||
|
|
||||||
Therefore, FIR2IR uses shared storage for get-or-create operations for declarations.
|
Therefore, FIR2IR uses shared storage for get-or-create operations for declarations.
|
||||||
|
|
||||||
### IRActualizer
|
|
||||||
|
|
||||||
The IR actualizer is a component performing [actualization](#actualization) over IR
|
|
||||||
|
|
||||||
See: [`org.jetbrains.kotlin.backend.common.actualizer.IrActualizer`](../../compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/actualizer/IrActualizer.kt)
|
|
||||||
|
|
||||||
In the current model, IR actualizer is used during the [platform compilation](#platform-compilation), to produce complete IR that is correct
|
|
||||||
from the backend standpoint.
|
|
||||||
|
|
||||||
**Inputs:**
|
|
||||||
- IR for each of the modules.
|
|
||||||
- IR might contain expect declarations and references to it.
|
|
||||||
|
|
||||||
**Outputs:**
|
|
||||||
- Single IR module.
|
|
||||||
- No expect declarations in the IR.
|
|
||||||
- IR is in the usual state, as for non-multiplatform projects
|
|
||||||
|
|
||||||
**Constraints:**
|
|
||||||
- No access to FIR or frontend state allowed
|
|
||||||
- Must operate over IR
|
|
||||||
- Diagnostic reporting is complicated and won't work for the IDE without special support
|
|
||||||
|
|
||||||
The following actions are preformed:
|
|
||||||
- Expect -> actual binding is constructed
|
|
||||||
- We have all expects and that's why we can construct it as opposed to the [actual -> expect binding](#actual-expect-binding)
|
|
||||||
- We can report diagnostics at that moment
|
|
||||||
- Top-level expect declarations are detached from IrModuleFragment
|
|
||||||
- Member expect declarations can only be contained within top-level expects, and is detached together with its container
|
|
||||||
- Modulo [`@OptionalExpectation`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-optional-expectation/)
|
|
||||||
- IR for default values is copied from expects to actuals. See [default propagation](#default-propagation) for the frontend part of work.
|
|
||||||
- All module fragments are merged into one
|
|
||||||
- [Fake-override actualization is performed](#fake-overrides)
|
|
||||||
|
|
||||||
### Fake overrides
|
### Fake overrides
|
||||||
|
|
||||||
FIR doesn't use fake-overrides in the frontend, and that's why we construct them during FIR2IR in order to match backend expectations
|
FIR doesn't use fake-overrides in the frontend, and that's why we construct them during FIR2IR in order to match backend expectations
|
||||||
By that, we require special handling of fake-overrides during the [platform compilation](#platform-compilation).
|
|
||||||
|
|
||||||
during the analysis and couldn't construct fake-overrides properly.
|
Also, the following example shows that it is impossible to build valid fake overrides during common
|
||||||
|
module compilation.
|
||||||
|
|
||||||
Ex:
|
Ex:
|
||||||
```kotlin
|
```kotlin
|
||||||
@@ -328,7 +295,89 @@ class Impl : K {
|
|||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
In the given example during the actualization, we need to combine (1) and (2) into one fake-override.
|
***Note: Approach described below is a target. It's not yet enabled by default. See KT-61514 for details.***
|
||||||
|
|
||||||
|
In the given example during the actualization, common compilation would produce (1) and (2), which need to be combined
|
||||||
|
into one fake override.
|
||||||
|
|
||||||
|
There is a new scheme, which is enabled by -Xuse-ir-fake-override-builder flag, and should become default at some point.
|
||||||
|
|
||||||
|
In it, [Fir2Ir](#fir2ir) creates a special symbol for fake overrides calls, and no declarations for fake overrides
|
||||||
|
(except one in [Lazy classes](#lazy-classes)). A special symbol, represented by [org.jetbrains.kotlin.ir.symbols.impl.IrFakeOverrideSymbolBase](../../compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/symbols/impl/IrFakeOverrideSymbol.kt) and its inheritors,
|
||||||
|
is effectively a pair of real declaration symbol and dispatch receiver class. This symbol can't be bound,
|
||||||
|
so backend can't work with it. To fix it, there is a phase, which replaces them with normal ones
|
||||||
|
after [actualization](#actualization). The symbol is mapped to single one overriding corresponding
|
||||||
|
real symbol within corresponding dispatch receiver class. It can be both a fake override and real declared symbol.
|
||||||
|
|
||||||
|
In theory, doing the same with other synthetic declarations (delegated members, data class generated members, etc)
|
||||||
|
can lead to more consistent behaviour, but we don't do it know, as we are not aware of any problems
|
||||||
|
|
||||||
|
### Lazy classes
|
||||||
|
|
||||||
|
Lazy classes represent classes used from compiled sources, but not present in them. This can be classes
|
||||||
|
from dependencies, java sources, or sources already compiled on previous round of incremental compilation.
|
||||||
|
|
||||||
|
Lazy classes are a special case for fake override building. They are now handled with frontend builder.
|
||||||
|
It can be done correctly, as they can only exist in platform session.
|
||||||
|
|
||||||
|
There is a technical issue with them. It's possible to have a lazy class with normal class super-type,
|
||||||
|
so it can refer to a fake override symbol in overridden symbols of some methods.
|
||||||
|
But we can't fix them all in the same place, as normal ones, as it would trigger lazy computations, which should be avoided.
|
||||||
|
|
||||||
|
To fix this, the rebuild is delayed while possible, using [org.jetbrains.kotlin.fir.backend.Fir2IrSymbolsMappingForLazyClasses](../../compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/Fir2IrSymbolsMappingForLazyClasses.kt),
|
||||||
|
which stores delayed operations to happen, when some read lazy property.
|
||||||
|
|
||||||
|
### IRActualizer
|
||||||
|
|
||||||
|
The IR actualizer is a component performing [actualization](#actualization) over IR
|
||||||
|
|
||||||
|
See: [`org.jetbrains.kotlin.backend.common.actualizer.IrActualizer`](../../compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/actualizer/IrActualizer.kt)
|
||||||
|
|
||||||
|
In the current model, IR actualizer is used during the [platform compilation](#platform-compilation), to produce complete IR that is correct
|
||||||
|
from the backend standpoint.
|
||||||
|
|
||||||
|
**Inputs:**
|
||||||
|
- IR for each of the modules.
|
||||||
|
- IR might contain expect declarations and references to it.
|
||||||
|
- IR can contain unbound fake override symbols
|
||||||
|
|
||||||
|
**Outputs:**
|
||||||
|
- Single IR module.
|
||||||
|
- No expect declarations in the IR.
|
||||||
|
- No unbound symbols in the IR
|
||||||
|
- IR is in the usual state, as for non-multiplatform projects
|
||||||
|
|
||||||
|
**Constraints:**
|
||||||
|
- No access to FIR or frontend state allowed
|
||||||
|
- Must operate over IR
|
||||||
|
- Diagnostic reporting is complicated and won't work for the IDE without special support
|
||||||
|
|
||||||
|
The following actions are preformed:
|
||||||
|
- Expect -> actual binding for classifiers is constructed and applied
|
||||||
|
- Fake overrides are constructed
|
||||||
|
- Expect -> actual binding for classifiers is callables and applied
|
||||||
|
- We have all expects and that's why we can construct it as opposed to the [actual -> expect binding](#actual-expect-binding)
|
||||||
|
- We can report diagnostics at that moment
|
||||||
|
- IR for default values is copied from expects to actuals. See [default propagation](#default-propagation) for the frontend part of work.
|
||||||
|
- All module fragments are merged into one
|
||||||
|
- Fake override symbols are replaced with normal ones
|
||||||
|
- Constants are evaluated
|
||||||
|
- Top-level expect declarations are detached from IrModuleFragment
|
||||||
|
- Member expect declarations can only be contained within top-level expects, and is detached together with its container
|
||||||
|
- Modulo [`@OptionalExpectation`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-optional-expectation/)
|
||||||
|
- Errors can be emitted if some expect-actual matches are bad.
|
||||||
|
|
||||||
|
There are several restrictions, because of which this order of actions is required:
|
||||||
|
|
||||||
|
- Fake overrides can't be built before classes are actualized
|
||||||
|
- Because we need to know what is real supertype.
|
||||||
|
- Callables can't be actulaized before fake overrides are built
|
||||||
|
- Some of them can match with fake override
|
||||||
|
- Constants can't be evaluated before callables are actualized
|
||||||
|
- As some expect function can become possible to evaluate
|
||||||
|
- Checkers can't run before constants are evaluated
|
||||||
|
- They need to check of some value are equal
|
||||||
|
|
||||||
|
|
||||||
## Frontend support for expect/actual
|
## Frontend support for expect/actual
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user