[IR] Moved actualizer and related things to separate module

^KT-62292
This commit is contained in:
Pavel Kunyavskiy
2023-11-14 12:33:39 +01:00
committed by Space Team
parent 62700c3ec3
commit 649a7bd66b
22 changed files with 28 additions and 0 deletions
@@ -1,174 +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.backend.common.actualizer
import org.jetbrains.kotlin.KtDiagnosticReporterWithImplicitIrBasedContext
import org.jetbrains.kotlin.backend.common.CommonBackendErrors
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.overrides.IrOverrideChecker
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
import org.jetbrains.kotlin.ir.symbols.IrPropertySymbol
import org.jetbrains.kotlin.ir.symbols.IrSymbol
import org.jetbrains.kotlin.ir.types.IrTypeSystemContext
import org.jetbrains.kotlin.ir.types.classifierOrFail
import org.jetbrains.kotlin.ir.util.isInterface
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.OverridingUtil
/**
* It adds fake overrides to non-expect classes inside common or multi-platform module,
* taken from these non-expect classes actualized super classes.
*
* In case when a non-expect class has direct or indirect expect supertypes,
* it may happen that the actual classes for these supertypes contain additional (non-actual) members that don't exist in their expect counterparts.
* We still should have fake overrides generated for these members, but FIR2IR isn't able to see their base members in common or multi-platform module.
* This class is intended to search for such situations and generate such fake overrides.
*/
internal class ActualFakeOverridesAdder(
private val expectActualMap: Map<IrSymbol, IrSymbol>,
private val expectToActualClassMap: Map<ClassId, IrClassSymbol>,
private val diagnosticsReporter: KtDiagnosticReporterWithImplicitIrBasedContext,
private val typeSystemContext: IrTypeSystemContext
) : IrElementVisitorVoid {
private val overrideChecker = IrOverrideChecker(typeSystemContext, emptyList())
private val missingActualMembersMap = mutableMapOf<IrClass, FakeOverrideInfo>()
override fun visitClass(declaration: IrClass) {
extractMissingActualMembersFromSupertypes(declaration)
visitElement(declaration)
}
override fun visitElement(element: IrElement) {
element.acceptChildrenVoid(this)
}
private fun extractMissingActualMembersFromSupertypes(klass: IrClass): FakeOverrideInfo {
missingActualMembersMap[klass]?.let { return it }
val missingActualMembers = FakeOverrideInfo()
missingActualMembersMap[klass] = missingActualMembers
// New members from supertypes are only relevant for not expect (ordinary) classes
// New members from the current class are only relevant for actualized expect classes
val processedMembers = FakeOverrideInfo()
for (superType in klass.superTypes) {
val membersFromSupertype = extractMissingActualMembersFromSupertypes(superType.classifierOrFail.owner as IrClass)
if (!klass.isExpect) {
appendMissingMembersToNotExpectClass(missingActualMembers, klass, membersFromSupertype.allSymbols(), processedMembers)
}
}
val actualClass = expectActualMap[klass.symbol]?.owner as? IrClass ?: return missingActualMembers
missingActualMembers.appendMissingMembersFromActualizedExpectClass(klass, actualClass)
return missingActualMembers
}
private fun appendMissingMembersToNotExpectClass(
fakeOverrideInfo: FakeOverrideInfo,
klass: IrClass,
membersFromSupertype: List<IrSymbol>,
processedMembers: FakeOverrideInfo
) {
for (symbolFromSupertype in membersFromSupertype) {
val memberFromSupertype = symbolFromSupertype.owner as IrDeclaration
if (memberFromSupertype is IrOverridableMember) {
// We can land here because of a hierarchy like
// actual A -> common B -> actual C
// where C defines a member x and A overrides the member x.
// We will first add a fake-override x to B and then land here.
// In this case we don't want to add a fake-override on top of the real override to A.
// Instead, we add the fake-override x to the overridden symbols of A.x.
@Suppress("UNCHECKED_CAST")
val override = klass.declarations.firstOrNull {
it is IrOverridableMember &&
overrideChecker.isOverridableBy(
superMember = memberFromSupertype,
subMember = it,
checkIsInlineFlag = false,
).result == OverridingUtil.OverrideCompatibilityInfo.Result.OVERRIDABLE
} as? IrOverridableDeclaration<IrSymbol>
if (override != null) {
override.overriddenSymbols += symbolFromSupertype
if (override is IrProperty && symbolFromSupertype is IrPropertySymbol) {
override.getter?.let { getter -> symbolFromSupertype.owner.getter?.symbol?.let { getter.overriddenSymbols += it } }
override.setter?.let { setter -> symbolFromSupertype.owner.setter?.symbol?.let { setter.overriddenSymbols += it } }
}
continue
}
}
val newMember = createFakeOverrideMember(listOf(memberFromSupertype), klass)
val matchingFakeOverrides = collectActualCallablesMatchingToSpecificExpect(
newMember.symbol,
fakeOverrideInfo.getMembersForActual(newMember),
expectToActualClassMap,
typeSystemContext
)
if (matchingFakeOverrides.isEmpty()) {
processedMembers.addMember(memberFromSupertype as IrOverridableDeclaration<*>)
fakeOverrideInfo.addMember(newMember)
klass.addMember(newMember)
}
}
}
private fun FakeOverrideInfo.appendMissingMembersFromActualizedExpectClass(
expectClass: IrClass,
actualClass: IrClass,
) {
val actualWithCorrespondingExpectMembers = hashSetOf<IrSymbol>().apply {
expectClass.declarations.mapNotNullTo(this) { expectActualMap[(it as? IrOverridableDeclaration<*>)?.symbol] }
}
for (actualMember in actualClass.declarations) {
if (actualMember is IrOverridableDeclaration<*> &&
(actualMember as? IrDeclarationWithVisibility)?.visibility != DescriptorVisibilities.PRIVATE &&
!actualWithCorrespondingExpectMembers.contains(actualMember.symbol)
) {
addMember(actualMember)
}
}
}
private class FakeOverrideInfo {
val functionsByName: MutableMap<Name, MutableList<IrSymbol>> = mutableMapOf()
val propertiesByName: MutableMap<Name, MutableList<IrSymbol>> = mutableMapOf()
fun allSymbols(): List<IrSymbol> {
return buildList {
functionsByName.values.flatMapTo(this) { it }
propertiesByName.values.flatMapTo(this) { it }
}
}
private fun getCorrespondingMap(member: IrOverridableDeclaration<*>): MutableMap<Name, MutableList<IrSymbol>> {
return when (member) {
is IrFunction -> functionsByName
is IrProperty -> propertiesByName
else -> error("Unsupported declaration type: $member")
}
}
fun addMember(member: IrOverridableDeclaration<*>) {
getCorrespondingMap(member).getOrPut(member.name) { mutableListOf() } += member.symbol
}
fun getMembersForActual(actualMember: IrOverridableDeclaration<*>): List<IrSymbol> {
return getCorrespondingMap(actualMember)[actualMember.name].orEmpty()
}
}
}
@@ -1,322 +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.backend.common.actualizer
import org.jetbrains.kotlin.KtDiagnosticReporterWithImplicitIrBasedContext
import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.incremental.components.ExpectActualTracker
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.PsiIrFileEntry
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
import org.jetbrains.kotlin.ir.symbols.IrSymbol
import org.jetbrains.kotlin.ir.symbols.IrTypeAliasSymbol
import org.jetbrains.kotlin.ir.types.IrTypeSystemContext
import org.jetbrains.kotlin.ir.types.classifierOrFail
import org.jetbrains.kotlin.ir.util.callableId
import org.jetbrains.kotlin.ir.util.classIdOrFail
import org.jetbrains.kotlin.ir.visitors.IrElementVisitor
import org.jetbrains.kotlin.mpp.DeclarationSymbolMarker
import org.jetbrains.kotlin.mpp.RegularClassSymbolMarker
import org.jetbrains.kotlin.name.CallableId
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.resolve.calls.mpp.AbstractExpectActualChecker
import org.jetbrains.kotlin.resolve.calls.mpp.AbstractExpectActualMatcher
import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCheckingCompatibility
import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualMatchingCompatibility
import java.io.File
/**
* This class is used to collect mapping between all expect and actuals declarations, which are declared in passed module fragments
* It does not process any declarations which may appear after actualization of supertypes (like fake-overrides in classes with
* expect supertypes)
*
* The main method of this class, `collect` returns a pair of two values:
* - `expectToActualMap` is the main storage of all mapped declarations. Key is symbol of expect declaration and the value is symbol of
* corresponding actual declaration
* - `classActualizationInfo` is a storage which keeps information about types (class) actualization, which can be used later for type
* refinement if needed
*
* If some declarations didn't match (or there was missing/actual) then corresponding declarations won't be stored in `expectToActualMap`.
* Instead of that an error will be reported to `diagnosticReporter`
*/
internal class ExpectActualCollector(
private val mainFragment: IrModuleFragment,
private val dependentFragments: List<IrModuleFragment>,
private val typeSystemContext: IrTypeSystemContext,
private val diagnosticsReporter: KtDiagnosticReporterWithImplicitIrBasedContext,
private val expectActualTracker: ExpectActualTracker?,
) {
data class Result(val expectToActualMap: MutableMap<IrSymbol, IrSymbol>, val classActualizationInfo: ClassActualizationInfo)
fun collect(): Result {
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)
}
private fun collectActualDeclarations(): ClassActualizationInfo {
val fragmentsWithActuals = dependentFragments.drop(1) + mainFragment
return ActualDeclarationsCollector.collectActualsFromFragments(fragmentsWithActuals)
}
private fun matchAllExpectDeclarations(
destination: MutableMap<IrSymbol, IrSymbol>,
classActualizationInfo: ClassActualizationInfo,
) {
val linkCollector = ExpectActualLinkCollector()
val linkCollectorContext =
ExpectActualLinkCollector.MatchingContext(
typeSystemContext, destination, diagnosticsReporter, expectActualTracker, classActualizationInfo, null
)
dependentFragments.forEach { linkCollector.visitModuleFragment(it, linkCollectorContext) }
// It doesn't make sense to link expects from the last module because actuals always should be located in another module
// Thus relevant actuals are always missing for the last module
// But the collector should be run anyway to detect and report "hanging" expect declarations
linkCollector.visitModuleFragment(mainFragment, linkCollectorContext)
}
}
internal data class ClassActualizationInfo(
// mapping from classId of actual class/typealias to itself/typealias expansion
val actualClasses: Map<ClassId, IrClassSymbol>,
// mapping from classId to actual typealias
val actualTypeAliases: Map<ClassId, IrTypeAliasSymbol>,
val actualTopLevels: Map<CallableId, List<IrSymbol>>,
val actualSymbolsToFile: Map<IrSymbol, IrFile?>,
) {
fun getActualWithoutExpansion(classId: ClassId): IrSymbol? {
return actualTypeAliases[classId] ?: actualClasses[classId]
}
}
private class ActualDeclarationsCollector {
companion object {
fun collectActualsFromFragments(fragments: List<IrModuleFragment>): ClassActualizationInfo {
val collector = ActualDeclarationsCollector()
for (fragment in fragments) {
collector.collect(fragment)
}
return ClassActualizationInfo(
collector.actualClasses,
collector.actualTypeAliasesWithoutExpansion,
collector.actualTopLevels,
collector.actualSymbolsToFile
)
}
}
private val actualClasses: MutableMap<ClassId, IrClassSymbol> = mutableMapOf()
private val actualTypeAliasesWithoutExpansion: MutableMap<ClassId, IrTypeAliasSymbol> = mutableMapOf()
private val actualTopLevels: MutableMap<CallableId, MutableList<IrSymbol>> = mutableMapOf()
private val actualSymbolsToFile: MutableMap<IrSymbol, IrFile?> = mutableMapOf()
private val visitedActualClasses = mutableSetOf<IrClass>()
private var currentFile: IrFile? = null
private fun collect(element: IrElement) {
when (element) {
is IrModuleFragment -> {
for (file in element.files) {
collect(file)
}
}
is IrFile -> {
currentFile = element
for (declaration in element.declarations) {
collect(declaration)
}
}
is IrTypeAlias -> {
if (!element.isActual) return
val classId = element.classIdOrFail
val expandedTypeSymbol = element.expandedType.classifierOrFail as IrClassSymbol
actualClasses[classId] = expandedTypeSymbol
actualTypeAliasesWithoutExpansion[classId] = element.symbol
actualSymbolsToFile[expandedTypeSymbol] = currentFile
actualSymbolsToFile[element.symbol] = currentFile
collect(expandedTypeSymbol.owner)
}
is IrClass -> {
if (element.isExpect || !visitedActualClasses.add(element)) return
actualClasses[element.classIdOrFail] = element.symbol
actualSymbolsToFile[element.symbol] = currentFile
for (declaration in element.declarations) {
collect(declaration)
}
}
is IrDeclarationContainer -> {
for (declaration in element.declarations) {
collect(declaration)
}
}
is IrEnumEntry -> {
recordActualCallable(element, element.callableId) // If enum entry is located inside expect enum, then this code is not executed
}
is IrProperty -> {
if (element.isExpect) return
recordActualCallable(element, element.callableId)
}
is IrFunction -> {
if (element.isExpect) return
recordActualCallable(element, element.callableId)
}
}
}
private fun recordActualCallable(callableDeclaration: IrDeclarationWithName, callableId: CallableId) {
if (callableId.classId == null) {
actualTopLevels
.getOrPut(callableId) { mutableListOf() }
.add(callableDeclaration.symbol)
actualSymbolsToFile[callableDeclaration.symbol] = currentFile
}
}
}
private class ExpectActualLinkCollector : IrElementVisitor<Unit, ExpectActualLinkCollector.MatchingContext> {
override fun visitFile(declaration: IrFile, data: MatchingContext) {
super.visitFile(declaration, data.withNewCurrentFile(declaration))
}
override fun visitFunction(declaration: IrFunction, data: MatchingContext) {
if (declaration.isExpect) {
matchExpectCallable(declaration, declaration.callableId, data)
}
}
override fun visitProperty(declaration: IrProperty, data: MatchingContext) {
if (declaration.isExpect) {
matchExpectCallable(declaration, declaration.callableId, data)
}
}
private fun matchExpectCallable(declaration: IrDeclarationWithName, callableId: CallableId, context: MatchingContext) {
matchAndCheckExpectDeclaration(
declaration.symbol,
context.classActualizationInfo.actualTopLevels[callableId].orEmpty(),
context,
)
}
override fun visitClass(declaration: IrClass, data: MatchingContext) {
if (!declaration.isExpect) return
val classId = declaration.classIdOrFail
val expectClassSymbol = declaration.symbol
val actualClassLikeSymbol = data.classActualizationInfo.getActualWithoutExpansion(classId)
matchAndCheckExpectDeclaration(expectClassSymbol, listOfNotNull(actualClassLikeSymbol), data)
}
private fun matchAndCheckExpectDeclaration(
expectSymbol: IrSymbol,
actualSymbols: List<IrSymbol>,
context: MatchingContext,
) {
val matched = AbstractExpectActualMatcher.matchSingleExpectTopLevelDeclarationAgainstPotentialActuals(
expectSymbol,
actualSymbols,
context,
)
if (matched != null) {
AbstractExpectActualChecker.checkSingleExpectTopLevelDeclarationAgainstMatchedActual(
expectSymbol,
matched,
context,
context.languageVersionSettings,
)
}
}
override fun visitElement(element: IrElement, data: MatchingContext) {
element.acceptChildren(this, data)
}
class MatchingContext(
typeSystemContext: IrTypeSystemContext,
private val destination: MutableMap<IrSymbol, IrSymbol>,
private val diagnosticsReporter: KtDiagnosticReporterWithImplicitIrBasedContext,
private val expectActualTracker: ExpectActualTracker?,
internal val classActualizationInfo: ClassActualizationInfo,
private val currentExpectFile: IrFile?,
) : IrExpectActualMatchingContext(typeSystemContext, classActualizationInfo.actualClasses) {
private val currentExpectIoFile by lazy(LazyThreadSafetyMode.PUBLICATION) { currentExpectFile?.toIoFile() }
internal val languageVersionSettings: LanguageVersionSettings get() = diagnosticsReporter.languageVersionSettings
fun withNewCurrentFile(newCurrentFile: IrFile) =
MatchingContext(
typeContext, destination, diagnosticsReporter, expectActualTracker, classActualizationInfo, newCurrentFile
)
override fun onMatchedClasses(expectClassSymbol: IrClassSymbol, actualClassSymbol: IrClassSymbol) {
destination[expectClassSymbol] = actualClassSymbol
expectActualTracker?.reportWithCurrentFile(actualClassSymbol)
recordActualForExpectDeclaration(expectClassSymbol, actualClassSymbol, destination)
}
override fun onMatchedCallables(expectSymbol: IrSymbol, actualSymbol: IrSymbol) {
expectActualTracker?.reportWithCurrentFile(actualSymbol)
recordActualForExpectDeclaration(expectSymbol, actualSymbol, destination)
}
override fun onIncompatibleMembersFromClassScope(
expectSymbol: DeclarationSymbolMarker,
actualSymbolsByIncompatibility: Map<ExpectActualCheckingCompatibility.Incompatible<*>, List<DeclarationSymbolMarker>>,
containingExpectClassSymbol: RegularClassSymbolMarker?,
containingActualClassSymbol: RegularClassSymbolMarker?
) {
require(expectSymbol is IrSymbol)
for ((incompatibility, actualMemberSymbols) in actualSymbolsByIncompatibility) {
for (actualSymbol in actualMemberSymbols) {
require(actualSymbol is IrSymbol)
diagnosticsReporter.reportExpectActualIncompatibility(expectSymbol, actualSymbol, incompatibility)
}
}
}
override fun onMismatchedMembersFromClassScope(
expectSymbol: DeclarationSymbolMarker,
actualSymbolsByIncompatibility: Map<ExpectActualMatchingCompatibility.Mismatch, List<DeclarationSymbolMarker>>,
containingExpectClassSymbol: RegularClassSymbolMarker?,
containingActualClassSymbol: RegularClassSymbolMarker?,
) {
require(expectSymbol is IrSymbol)
if (actualSymbolsByIncompatibility.isEmpty() && !expectSymbol.owner.containsOptionalExpectation()) {
diagnosticsReporter.reportMissingActual(expectSymbol)
}
for ((incompatibility, actualMemberSymbols) in actualSymbolsByIncompatibility) {
for (actualSymbol in actualMemberSymbols) {
require(actualSymbol is IrSymbol)
diagnosticsReporter.reportExpectActualMismatch(expectSymbol, actualSymbol, incompatibility)
}
}
}
private fun ExpectActualTracker.reportWithCurrentFile(actualSymbol: IrSymbol) {
if (currentExpectFile != null) {
val actualIoFile = classActualizationInfo.actualSymbolsToFile[actualSymbol]?.toIoFile()
if (actualIoFile != null) {
report(currentExpectIoFile!!, actualIoFile)
}
}
}
}
}
private fun IrFile.toIoFile(): File? = when (val fe = fileEntry) {
is PsiIrFileEntry -> fe.psiFile.virtualFile.let { it.fileSystem.getNioPath(it)?.toFile() }
else -> File(fileEntry.name).takeIf { it.exists() }
}
@@ -1,179 +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.backend.common.actualizer
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.symbols.*
import org.jetbrains.kotlin.ir.util.DeepCopyIrTreeWithSymbols
import org.jetbrains.kotlin.ir.util.SymbolRemapper
import org.jetbrains.kotlin.ir.util.SymbolRenamer
import org.jetbrains.kotlin.ir.util.TypeRemapper
import org.jetbrains.kotlin.utils.memoryOptimizedMap
internal class ActualizerSymbolRemapper(private val expectActualMap: Map<IrSymbol, IrSymbol>) : SymbolRemapper {
override fun getDeclaredClass(symbol: IrClassSymbol) = symbol
override fun getDeclaredScript(symbol: IrScriptSymbol) = symbol
override fun getDeclaredFunction(symbol: IrSimpleFunctionSymbol) = symbol
override fun getDeclaredProperty(symbol: IrPropertySymbol) = symbol
override fun getDeclaredField(symbol: IrFieldSymbol) = symbol
override fun getDeclaredFile(symbol: IrFileSymbol) = symbol
override fun getDeclaredConstructor(symbol: IrConstructorSymbol) = symbol
override fun getDeclaredEnumEntry(symbol: IrEnumEntrySymbol) = symbol
override fun getDeclaredExternalPackageFragment(symbol: IrExternalPackageFragmentSymbol) = symbol
override fun getDeclaredVariable(symbol: IrVariableSymbol) = symbol
override fun getDeclaredLocalDelegatedProperty(symbol: IrLocalDelegatedPropertySymbol) = symbol
override fun getDeclaredTypeParameter(symbol: IrTypeParameterSymbol) = symbol
override fun getDeclaredValueParameter(symbol: IrValueParameterSymbol) = symbol
override fun getDeclaredTypeAlias(symbol: IrTypeAliasSymbol) = symbol
override fun getReferencedClass(symbol: IrClassSymbol) = symbol.actualizeSymbol()
override fun getReferencedScript(symbol: IrScriptSymbol) = symbol.actualizeSymbol()
override fun getReferencedClassOrNull(symbol: IrClassSymbol?) = symbol?.actualizeSymbol()
override fun getReferencedEnumEntry(symbol: IrEnumEntrySymbol) = symbol.actualizeSymbol()
override fun getReferencedVariable(symbol: IrVariableSymbol) = symbol.actualizeSymbol()
override fun getReferencedLocalDelegatedProperty(symbol: IrLocalDelegatedPropertySymbol) = symbol.actualizeSymbol()
override fun getReferencedField(symbol: IrFieldSymbol) = symbol.actualizeSymbol()
override fun getReferencedConstructor(symbol: IrConstructorSymbol) = symbol.actualizeSymbol()
override fun getReferencedValue(symbol: IrValueSymbol) = symbol.actualizeSymbol()
override fun getReferencedFunction(symbol: IrFunctionSymbol) = symbol.actualizeSymbol()
override fun getReferencedProperty(symbol: IrPropertySymbol) = symbol.actualizeSymbol()
override fun getReferencedSimpleFunction(symbol: IrSimpleFunctionSymbol) = symbol.actualizeSymbol()
override fun getReferencedReturnableBlock(symbol: IrReturnableBlockSymbol) = symbol.actualizeSymbol()
override fun getReferencedClassifier(symbol: IrClassifierSymbol) = symbol.actualizeSymbol()
override fun getReferencedTypeAlias(symbol: IrTypeAliasSymbol) = symbol.actualizeSymbol()
private inline fun <reified S : IrSymbol> S.actualizeSymbol(): S = (expectActualMap[this] as? S) ?: this
}
internal open class ActualizerVisitor(private val symbolRemapper: SymbolRemapper, typeRemapper: TypeRemapper) :
DeepCopyIrTreeWithSymbols(symbolRemapper, typeRemapper, SymbolRenamer.DEFAULT) {
override fun visitModuleFragment(declaration: IrModuleFragment) =
declaration.also { it.transformChildren(this, null) }
override fun visitExternalPackageFragment(declaration: IrExternalPackageFragment) =
declaration.also { it.transformChildren(this, null) }
override fun visitFile(declaration: IrFile) =
declaration.also {
it.transformChildren(this, null)
it.transformAnnotations(declaration)
}
override fun visitScript(declaration: IrScript) =
declaration.also {
it.baseClass = it.baseClass?.remapType()
it.transformChildren(this, null)
}
override fun visitClass(declaration: IrClass) =
declaration.also {
it.superTypes = it.superTypes.map { superType -> superType.remapType() }
it.transformChildren(this, null)
it.transformAnnotations(declaration)
}
override fun visitSimpleFunction(declaration: IrSimpleFunction) = (visitFunction(declaration) as IrSimpleFunction).also {
it.overriddenSymbols = it.overriddenSymbols.memoryOptimizedMap { symbol ->
symbolRemapper.getReferencedFunction(symbol) as IrSimpleFunctionSymbol
}
}
override fun visitConstructor(declaration: IrConstructor) = visitFunction(declaration) as IrConstructor
override fun visitFunction(declaration: IrFunction) =
declaration.also {
it.returnType = it.returnType.remapType()
it.transformChildren(this, null)
it.transformAnnotations(declaration)
}
override fun visitProperty(declaration: IrProperty) =
declaration.also {
it.transformChildren(this, null)
it.overriddenSymbols = it.overriddenSymbols.memoryOptimizedMap { symbol ->
symbolRemapper.getReferencedProperty(symbol)
}
it.transformAnnotations(declaration)
}
override fun visitField(declaration: IrField) =
declaration.also {
it.type = it.type.remapType()
it.transformChildren(this, null)
it.transformAnnotations(declaration)
}
override fun visitLocalDelegatedProperty(declaration: IrLocalDelegatedProperty) =
declaration.also {
it.type = it.type.remapType()
it.transformChildren(this, null)
}
override fun visitEnumEntry(declaration: IrEnumEntry) =
declaration.also {
it.transformChildren(this, null)
it.transformAnnotations(declaration)
}
override fun visitTypeParameter(declaration: IrTypeParameter) =
declaration.also {
it.superTypes = it.superTypes.map { superType -> superType.remapType() }
it.transformChildren(this, null)
it.transformAnnotations(declaration)
}
override fun visitValueParameter(declaration: IrValueParameter) =
declaration.also {
it.type = it.type.remapType()
it.varargElementType = it.varargElementType?.remapType()
it.transformChildren(this, null)
it.transformAnnotations(declaration)
}
override fun visitAnonymousInitializer(declaration: IrAnonymousInitializer) =
declaration.also { it.transformChildren(this, null) }
override fun visitVariable(declaration: IrVariable) =
declaration.also {
it.type = it.type.remapType()
it.transformChildren(this, null)
it.transformAnnotations(declaration)
}
override fun visitTypeAlias(declaration: IrTypeAlias) =
declaration.also {
it.expandedType = it.expandedType.remapType()
it.transformChildren(this, null)
it.transformAnnotations(declaration)
}
}
@@ -1,242 +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.backend.common.actualizer
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.overrides.IrFakeOverrideBuilder
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
}
@@ -1,57 +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.backend.common.actualizer
import org.jetbrains.kotlin.backend.common.ir.isExpect
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrDeclaration
import org.jetbrains.kotlin.ir.declarations.IrDeclarationBase
import org.jetbrains.kotlin.ir.declarations.IrOverridableDeclaration
import org.jetbrains.kotlin.ir.symbols.IrSymbol
import org.jetbrains.kotlin.ir.util.isFakeOverride
import org.jetbrains.kotlin.ir.util.transformInPlace
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
/**
* It actualizes expect fake overrides in non-expect classes inside common or multi-platform module.
*
* When some non-expect class inside a common or multi-platform module has an expect base class,
* FIR2IR generates expect fake overrides overriding the expect base class members for this non-expect class which is not correct for the backend.
* This Actualizer processes expect overridable declarations in non-expect classes and replaces them with the associated actual overridable
* declarations overriding the actual base class members.The newly created actual fake overrides are stored in expectActualMap.
*/
internal class FakeOverridesActualizer(private val expectActualMap: MutableMap<IrSymbol, IrSymbol>) : IrElementVisitorVoid {
override fun visitClass(declaration: IrClass) {
if (!declaration.isExpect) {
actualizeFakeOverrides(declaration)
}
visitElement(declaration)
}
override fun visitElement(element: IrElement) {
element.acceptChildrenVoid(this)
}
private fun actualizeFakeOverrides(klass: IrClass) {
fun IrDeclaration.actualize(): IrDeclaration {
if (!isExpect) return this
(expectActualMap[symbol]?.owner as? IrDeclaration)?.let { return it }
require(this is IrOverridableDeclaration<*>)
val actualizedOverrides = overriddenSymbols.map { (it.owner as IrDeclaration).actualize() }
val actualFakeOverride = createFakeOverrideMember(actualizedOverrides, parent as IrClass)
recordActualForExpectDeclaration(this.symbol, actualFakeOverride.symbol, expectActualMap)
return actualFakeOverride
}
klass.declarations.transformInPlace { if (it.isExpect && it.isFakeOverride) it.actualize() else it }
}
}
@@ -1,54 +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.backend.common.actualizer
import org.jetbrains.kotlin.backend.common.lower.actualize
import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.declarations.copyAttributes
import org.jetbrains.kotlin.ir.expressions.IrGetValue
import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
import org.jetbrains.kotlin.ir.symbols.IrSymbol
import org.jetbrains.kotlin.ir.util.DeepCopyTypeRemapper
import org.jetbrains.kotlin.ir.util.SymbolRemapper
import org.jetbrains.kotlin.ir.util.TypeRemapper
import org.jetbrains.kotlin.ir.util.deepCopyWithSymbols
internal class FunctionDefaultParametersActualizer(
symbolRemapper: ActualizerSymbolRemapper,
typeRemapper: DeepCopyTypeRemapper,
private val expectActualMap: Map<IrSymbol, IrSymbol>
) {
private val visitor = FunctionDefaultParametersActualizerVisitor(symbolRemapper, typeRemapper)
fun actualize() {
for ((expect, actual) in expectActualMap) {
if (expect is IrFunctionSymbol) {
actualize(expect.owner, (actual as IrFunctionSymbol).owner)
}
}
}
private fun actualize(expectFunction: IrFunction, actualFunction: IrFunction) {
expectFunction.valueParameters.zip(actualFunction.valueParameters).forEach { (expectParameter, actualParameter) ->
val expectDefaultValue = expectParameter.defaultValue
if (actualParameter.defaultValue == null && expectDefaultValue != null) {
actualParameter.defaultValue = expectDefaultValue.deepCopyWithSymbols(actualFunction).transform(visitor, null)
}
}
}
}
private class FunctionDefaultParametersActualizerVisitor(private val symbolRemapper: SymbolRemapper, typeRemapper: TypeRemapper) :
ActualizerVisitor(symbolRemapper, typeRemapper) {
override fun visitGetValue(expression: IrGetValue): IrGetValue {
// It performs actualization of dispatch/extension receivers
// It's actual only for default parameter values of expect functions because expect functions don't have bodies
return expression.actualize(
classActualizer = { symbolRemapper.getReferencedClass(it.symbol).owner },
functionActualizer = { symbolRemapper.getReferencedFunction(it.symbol).owner }
).copyAttributes(expression)
}
}
@@ -1,127 +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.backend.common.actualizer
import org.jetbrains.kotlin.KtDiagnosticReporterWithImplicitIrBasedContext
import org.jetbrains.kotlin.backend.common.actualizer.checker.IrExpectActualCheckers
import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
import org.jetbrains.kotlin.incremental.components.ExpectActualTracker
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.overrides.IrFakeOverrideBuilder
import org.jetbrains.kotlin.ir.symbols.IrSymbol
import org.jetbrains.kotlin.ir.types.IrTypeSystemContext
import org.jetbrains.kotlin.ir.util.*
data class IrActualizedResult(val actualizedExpectDeclarations: List<IrDeclaration>)
/**
* IrActualizer is responsible for performing actualization.
*
* The main action is the replacement of references to expect declarations with corresponding actuals on the IR level.
*
* See `/docs/fir/k2_kmp.md`
*/
object IrActualizer {
fun actualize(
mainFragment: IrModuleFragment,
dependentFragments: List<IrModuleFragment>,
diagnosticReporter: DiagnosticReporter,
typeSystemContext: IrTypeSystemContext,
languageVersionSettings: LanguageVersionSettings,
symbolTable: SymbolTable,
fakeOverrideBuilder: IrFakeOverrideBuilder,
useIrFakeOverrideBuilder: Boolean,
expectActualTracker: ExpectActualTracker?
): IrActualizedResult {
val ktDiagnosticReporter = KtDiagnosticReporterWithImplicitIrBasedContext(diagnosticReporter, languageVersionSettings)
// 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()
IrExpectActualCheckers(expectActualMap, actualDeclarations, typeSystemContext, ktDiagnosticReporter).check()
// 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)
if (!useIrFakeOverrideBuilder) {
// 3. Actualize expect fake overrides in non-expect classes inside common or multi-platform module.
// It's probably important to run FakeOverridesActualizer before ActualFakeOverridesAdder
FakeOverridesActualizer(expectActualMap).apply { dependentFragments.forEach { visitModuleFragment(it) } }
// 4. Add fake overrides to non-expect classes inside common or multi-platform module,
// taken from these non-expect classes actualized super classes.
ActualFakeOverridesAdder(
expectActualMap,
actualDeclarations.actualClasses,
ktDiagnosticReporter,
typeSystemContext
).apply { dependentFragments.forEach { visitModuleFragment(it) } }
}
// 5. Copy and actualize function parameter default values from expect functions
val symbolRemapper = ActualizerSymbolRemapper(expectActualMap)
val typeRemapper = DeepCopyTypeRemapper(symbolRemapper)
FunctionDefaultParametersActualizer(symbolRemapper, typeRemapper, expectActualMap).actualize()
// 6. Actualize expect calls in dependent fragments using info obtained in the previous steps
val actualizerVisitor = ActualizerVisitor(symbolRemapper, typeRemapper)
dependentFragments.forEach { it.transform(actualizerVisitor, null) }
// 7. Merge dependent fragments into the main one
mergeIrFragments(mainFragment, dependentFragments)
if (useIrFakeOverrideBuilder) {
// 8. Rebuild fake overrides from stretch, as they could become invalid during actualization
FakeOverrideRebuilder(symbolTable, fakeOverrideBuilder).rebuildFakeOverrides(mainFragment)
}
return IrActualizedResult(removedExpectDeclarations)
}
private fun removeExpectDeclarations(dependentFragments: List<IrModuleFragment>, expectActualMap: Map<IrSymbol, IrSymbol>): List<IrDeclaration> {
val removedExpectDeclarations = mutableListOf<IrDeclaration>()
for (fragment in dependentFragments) {
for (file in fragment.files) {
file.declarations.removeIf {
if (shouldRemoveExpectDeclaration(it, expectActualMap)) {
removedExpectDeclarations.add(it)
true
} else {
false
}
}
}
}
return removedExpectDeclarations
}
private fun shouldRemoveExpectDeclaration(irDeclaration: IrDeclaration, expectActualMap: Map<IrSymbol, IrSymbol>): Boolean {
return when (irDeclaration) {
is IrClass -> irDeclaration.isExpect && (!irDeclaration.containsOptionalExpectation() || expectActualMap.containsKey(irDeclaration.symbol))
is IrProperty -> irDeclaration.isExpect
is IrFunction -> irDeclaration.isExpect
else -> false
}
}
private fun mergeIrFragments(mainFragment: IrModuleFragment, dependentFragments: List<IrModuleFragment>) {
val newFiles = dependentFragments.flatMap { it.files }
for (file in newFiles) file.module = mainFragment
mainFragment.files.addAll(0, newFiles)
}
}
@@ -1,233 +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.backend.common.actualizer
import org.jetbrains.kotlin.KtDiagnosticReporterWithImplicitIrBasedContext
import org.jetbrains.kotlin.backend.common.CommonBackendErrors
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.ObsoleteDescriptorBasedAPI
import org.jetbrains.kotlin.ir.builders.declarations.buildFun
import org.jetbrains.kotlin.ir.builders.declarations.buildProperty
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.IrConstructorCall
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
import org.jetbrains.kotlin.ir.symbols.IrPropertySymbol
import org.jetbrains.kotlin.ir.symbols.IrSymbol
import org.jetbrains.kotlin.ir.types.IrTypeSystemContext
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.resolve.calls.mpp.AbstractExpectActualMatcher
import org.jetbrains.kotlin.resolve.multiplatform.*
import org.jetbrains.kotlin.utils.addToStdlib.runIf
import org.jetbrains.kotlin.utils.addToStdlib.shouldNotBeCalled
internal fun collectActualCallablesMatchingToSpecificExpect(
expectSymbol: IrSymbol,
actualSymbols: List<IrSymbol>,
expectToActualClassMap: Map<ClassId, IrClassSymbol>,
typeSystemContext: IrTypeSystemContext,
): List<IrSymbol> {
val baseExpectSymbol = expectSymbol
val matchingActuals = mutableListOf<IrSymbol>()
val context = object : IrExpectActualMatchingContext(typeSystemContext, expectToActualClassMap) {
override fun onMatchedClasses(expectClassSymbol: IrClassSymbol, actualClassSymbol: IrClassSymbol) {
shouldNotBeCalled()
}
override fun onMatchedCallables(expectSymbol: IrSymbol, actualSymbol: IrSymbol) {
require(expectSymbol == baseExpectSymbol)
matchingActuals += actualSymbol
}
}
AbstractExpectActualMatcher.matchSingleExpectTopLevelDeclarationAgainstPotentialActuals(
expectSymbol,
actualSymbols,
context,
)
return matchingActuals
}
internal fun recordActualForExpectDeclaration(
expectSymbol: IrSymbol,
actualSymbol: IrSymbol,
destination: MutableMap<IrSymbol, IrSymbol>,
) {
val expectDeclaration = expectSymbol.owner as IrDeclarationBase
val actualDeclaration = actualSymbol.owner as IrDeclaration
val registeredActual = destination.put(expectSymbol, actualSymbol)
require(registeredActual == null || registeredActual == actualSymbol) {
"""
Expect symbol already has registered mapping
Expect declaration: ${expectDeclaration.render()}
Actual declaration: ${actualDeclaration.render()}
Already registered: ${registeredActual!!.owner.render()}
""".trimIndent()
}
if (expectDeclaration is IrTypeParametersContainer) {
recordTypeParametersMapping(destination, expectDeclaration, actualDeclaration as IrTypeParametersContainer)
}
if (expectDeclaration is IrProperty) {
val actualProperty = actualDeclaration as IrProperty
expectDeclaration.getter!!.let {
val getter = actualProperty.getter!!
destination[it.symbol] = getter.symbol
recordTypeParametersMapping(destination, it, getter)
}
expectDeclaration.setter?.symbol?.let { destination[it] = actualProperty.setter!!.symbol }
}
}
private fun recordTypeParametersMapping(
destination: MutableMap<IrSymbol, IrSymbol>,
expectTypeParametersContainer: IrTypeParametersContainer,
actualTypeParametersContainer: IrTypeParametersContainer
) {
expectTypeParametersContainer.typeParameters
.zip(actualTypeParametersContainer.typeParameters)
.forEach { (expectTypeParameter, actualTypeParameter) ->
destination[expectTypeParameter.symbol] = actualTypeParameter.symbol
}
}
internal fun KtDiagnosticReporterWithImplicitIrBasedContext.reportMissingActual(expectSymbol: IrSymbol) {
reportMissingActual(expectSymbol.owner as IrDeclaration)
}
@OptIn(ObsoleteDescriptorBasedAPI::class)
internal fun KtDiagnosticReporterWithImplicitIrBasedContext.reportMissingActual(irDeclaration: IrDeclaration) {
at(irDeclaration).report(
CommonBackendErrors.NO_ACTUAL_FOR_EXPECT,
(irDeclaration as? IrDeclarationWithName)?.name?.asString().orEmpty(),
irDeclaration.module
)
}
internal fun KtDiagnosticReporterWithImplicitIrBasedContext.reportExpectActualIncompatibility(
expectSymbol: IrSymbol,
actualSymbol: IrSymbol,
incompatibility: ExpectActualCheckingCompatibility.Incompatible<*>,
) {
val expectDeclaration = expectSymbol.owner as IrDeclaration
val actualDeclaration = actualSymbol.owner as IrDeclaration
at(expectDeclaration).report(
CommonBackendErrors.EXPECT_ACTUAL_INCOMPATIBILITY,
expectDeclaration.getNameWithAssert().asString(),
actualDeclaration.getNameWithAssert().asString(),
incompatibility
)
}
internal fun KtDiagnosticReporterWithImplicitIrBasedContext.reportExpectActualMismatch(
expectSymbol: IrSymbol,
actualSymbol: IrSymbol,
incompatibility: ExpectActualMatchingCompatibility.Mismatch,
) {
val expectDeclaration = expectSymbol.owner as IrDeclaration
val actualDeclaration = actualSymbol.owner as IrDeclaration
at(expectDeclaration).report(
CommonBackendErrors.EXPECT_ACTUAL_MISMATCH,
expectDeclaration.getNameWithAssert().asString(),
actualDeclaration.getNameWithAssert().asString(),
incompatibility
)
}
internal fun KtDiagnosticReporterWithImplicitIrBasedContext.reportActualAnnotationsNotMatchExpect(
expectSymbol: IrSymbol,
actualSymbol: IrSymbol,
incompatibilityType: ExpectActualAnnotationsIncompatibilityType<IrConstructorCall>,
reportOn: IrSymbol,
) {
at(reportOn.owner as IrDeclaration).report(
CommonBackendErrors.ACTUAL_ANNOTATIONS_NOT_MATCH_EXPECT,
expectSymbol,
actualSymbol,
incompatibilityType,
)
}
internal fun KtDiagnosticReporterWithImplicitIrBasedContext.reportActualAnnotationConflictingDefaultArgumentValue(
reportOn: IrElement,
file: IrFile,
actualParam: IrValueParameter,
) {
at(reportOn, file).report(
CommonBackendErrors.ACTUAL_ANNOTATION_CONFLICTING_DEFAULT_ARGUMENT_VALUE,
actualParam,
)
}
internal fun IrElement.containsOptionalExpectation(): Boolean {
return this is IrClass &&
this.kind == ClassKind.ANNOTATION_CLASS &&
this.hasAnnotation(OptionalAnnotationUtil.OPTIONAL_EXPECTATION_FQ_NAME)
}
@Suppress("UNCHECKED_CAST")
internal fun createFakeOverrideMember(actualMembers: List<IrDeclaration>, declaration: IrClass): IrOverridableDeclaration<*> {
return when (actualMembers.first()) {
is IrSimpleFunction -> createFakeOverrideFunction(actualMembers as List<IrSimpleFunction>, declaration)
is IrProperty -> createFakeOverrideProperty(actualMembers as List<IrProperty>, declaration)
else -> error("Only function or property can be overridden")
}
}
private fun createFakeOverrideProperty(actualProperties: List<IrProperty>, parent: IrClass): IrProperty {
val actualProperty = actualProperties.first() // TODO: Currently FIR2IR works in the similar way but it looks incorrect
return parent.factory.buildProperty {
updateFrom(actualProperty)
name = actualProperty.name
origin = IrDeclarationOrigin.FAKE_OVERRIDE
isFakeOverride = true
isExpect = false
}.apply {
this.parent = parent
annotations = actualProperty.annotations
backingField = null
getter = createFakeOverrideFunction(actualProperties.map { it.getter as IrSimpleFunction }, parent, symbol)
setter = runIf(actualProperty.setter != null) {
createFakeOverrideFunction(actualProperties.map { it.setter as IrSimpleFunction }, parent, symbol)
}
overriddenSymbols = actualProperties.map { it.symbol }
}
}
private fun createFakeOverrideFunction(
actualFunctions: List<IrSimpleFunction>,
parent: IrDeclarationParent,
correspondingPropertySymbol: IrPropertySymbol? = null
): IrSimpleFunction {
val actualFunction = actualFunctions.first() // TODO: Currently FIR2IR works in the similar way but it looks incorrect
return actualFunction.factory.buildFun {
updateFrom(actualFunction)
name = actualFunction.name
returnType = actualFunction.returnType
origin = IrDeclarationOrigin.FAKE_OVERRIDE
isFakeOverride = true
isExpect = false
}.also {
it.parent = parent
it.annotations = actualFunction.annotations.map { p -> p.deepCopyWithSymbols(it) }
it.typeParameters = actualFunction.typeParameters.map { p -> p.deepCopyWithSymbols(it) }
val typeRemapper = IrTypeParameterRemapper(actualFunction.typeParameters.zip(it.typeParameters).toMap())
fun IrValueParameter.deepCopyWithTypeParameters(): IrValueParameter = deepCopyWithSymbols(it) { symbolRemapper, _ ->
DeepCopyIrTreeWithSymbols(symbolRemapper, typeRemapper)
}
it.dispatchReceiverParameter = actualFunction.dispatchReceiverParameter?.deepCopyWithTypeParameters()
it.extensionReceiverParameter = actualFunction.extensionReceiverParameter?.deepCopyWithTypeParameters()
it.valueParameters = actualFunction.valueParameters.map { p -> p.deepCopyWithTypeParameters() }
it.contextReceiverParametersCount = actualFunction.contextReceiverParametersCount
it.metadata = actualFunction.metadata
it.overriddenSymbols = actualFunctions.map { f -> f.symbol }
it.attributeOwnerId = it
it.correspondingPropertySymbol = correspondingPropertySymbol
}
}
@@ -1,586 +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.backend.common.actualizer
import org.jetbrains.kotlin.backend.common.actualizer.checker.areIrExpressionConstValuesEqual
import org.jetbrains.kotlin.backend.common.sourceElement
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.descriptors.Visibility
import org.jetbrains.kotlin.descriptors.annotations.KotlinRetention
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.IrConstructorCall
import org.jetbrains.kotlin.ir.symbols.*
import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.types.impl.IrSimpleTypeImpl
import org.jetbrains.kotlin.ir.types.impl.IrTypeBase
import org.jetbrains.kotlin.ir.types.impl.IrTypeProjectionImpl
import org.jetbrains.kotlin.ir.types.impl.makeTypeProjection
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.mpp.*
import org.jetbrains.kotlin.name.CallableId
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.calls.mpp.ExpectActualCollectionArgumentsCompatibilityCheckStrategy
import org.jetbrains.kotlin.resolve.calls.mpp.ExpectActualMatchingContext
import org.jetbrains.kotlin.resolve.calls.mpp.ExpectActualMatchingContext.AnnotationCallInfo
import org.jetbrains.kotlin.resolve.checkers.OptInNames
import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualMatchingCompatibility
import org.jetbrains.kotlin.types.AbstractTypeChecker
import org.jetbrains.kotlin.types.TypeCheckerState
import org.jetbrains.kotlin.types.Variance
import org.jetbrains.kotlin.types.model.KotlinTypeMarker
import org.jetbrains.kotlin.types.model.TypeSubstitutorMarker
import org.jetbrains.kotlin.types.model.TypeSystemContext
import org.jetbrains.kotlin.utils.addToStdlib.shouldNotBeCalled
internal abstract class IrExpectActualMatchingContext(
val typeContext: IrTypeSystemContext,
val expectToActualClassMap: Map<ClassId, IrClassSymbol>
) : ExpectActualMatchingContext<IrSymbol>, TypeSystemContext by typeContext {
override val allowClassActualizationWithWiderVisibility: Boolean
get() = true
override val allowTransitiveSupertypesActualization: Boolean
get() = true
// This incompatibility is often suppressed in the source code (e.g. in kotlin-stdlib).
// The backend must be able to do expect-actual matching to emit bytecode
// That's why we disable the checker here. Probably, this checker can be enabled once KT-60426 is fixed
override val shouldCheckAbsenceOfDefaultParamsInActual: Boolean
get() = false
private inline fun <R> CallableSymbolMarker.processIr(
onFunction: (IrFunction) -> R,
onProperty: (IrProperty) -> R,
onField: (IrField) -> R,
onValueParameter: (IrValueParameter) -> R,
onEnumEntry: (IrEnumEntry) -> R,
): R {
return when (this) {
is IrFunctionSymbol -> onFunction(owner)
is IrPropertySymbol -> onProperty(owner)
is IrFieldSymbol -> onField(owner)
is IrValueParameterSymbol -> onValueParameter(owner)
is IrEnumEntrySymbol -> onEnumEntry(owner)
else -> error("Unsupported declaration: $this")
}
}
private inline fun <R> ClassLikeSymbolMarker.processIr(
onClass: (IrClass) -> R,
onTypeAlias: (IrTypeAlias) -> R,
): R {
return when (this) {
is IrClassSymbol -> onClass(owner)
is IrTypeAliasSymbol -> onTypeAlias(owner)
else -> error("Unsupported declaration: $this")
}
}
private fun DeclarationSymbolMarker.asIr(): IrDeclaration = (this as IrSymbol).owner as IrDeclaration
private fun FunctionSymbolMarker.asIr(): IrFunction = (this as IrFunctionSymbol).owner
private fun PropertySymbolMarker.asIr(): IrProperty = (this as IrPropertySymbol).owner
private fun ValueParameterSymbolMarker.asIr(): IrValueParameter = (this as IrValueParameterSymbol).owner
private fun TypeParameterSymbolMarker.asIr(): IrTypeParameter = (this as IrTypeParameterSymbol).owner
private fun RegularClassSymbolMarker.asIr(): IrClass = (this as IrClassSymbol).owner
private fun TypeAliasSymbolMarker.asIr(): IrTypeAlias = (this as IrTypeAliasSymbol).owner
private inline fun <reified T : IrDeclaration> DeclarationSymbolMarker.safeAsIr(): T? = (this as IrSymbol).owner as? T
override val innerClassesCapturesOuterTypeParameters: Boolean
get() = false
override val RegularClassSymbolMarker.classId: ClassId
get() = asIr().classIdOrFail
override val TypeAliasSymbolMarker.classId: ClassId
get() = asIr().classIdOrFail
override val CallableSymbolMarker.callableId: CallableId
get() = processIr(
onFunction = { it.callableId },
onProperty = { it.callableId },
onField = { it.callableId },
onValueParameter = { shouldNotBeCalled() },
onEnumEntry = { it.callableId }
)
override val TypeParameterSymbolMarker.parameterName: Name
get() = asIr().name
override val ValueParameterSymbolMarker.parameterName: Name
get() = asIr().name
override fun TypeAliasSymbolMarker.expandToRegularClass(): RegularClassSymbolMarker? {
return asIr().expandedType.getClass()?.symbol
}
override val RegularClassSymbolMarker.classKind: ClassKind
get() = asIr().kind
override val RegularClassSymbolMarker.isCompanion: Boolean
get() = asIr().isCompanion
override val RegularClassSymbolMarker.isInner: Boolean
get() = asIr().isInner
override val RegularClassSymbolMarker.isInline: Boolean
get() = asIr().isValue
override val RegularClassSymbolMarker.isValue: Boolean
get() = asIr().isValue
override val RegularClassSymbolMarker.isFun: Boolean
get() = asIr().isFun
override val ClassLikeSymbolMarker.typeParameters: List<TypeParameterSymbolMarker>
get() {
val parameters = processIr(
onClass = { it.typeParameters },
onTypeAlias = { it.typeParameters },
)
return parameters.map { it.symbol }
}
override val ClassLikeSymbolMarker.modality: Modality
get() = processIr(
onClass = {
// For some reason kotlin annotations in IR have open modality and java annotations have final modality
// But since it's legal to actualize kotlin annotation class with java annotation class
// and effectively all annotation classes have the same modality, it's ok to always return one
// modality for all annotation classes (doesn't matter final or open)
if (it.isAnnotationClass) Modality.OPEN else it.modality
},
onTypeAlias = { Modality.FINAL }
)
override val ClassLikeSymbolMarker.visibility: Visibility
get() = safeAsIr<IrDeclarationWithVisibility>()!!.visibility.delegate
override val CallableSymbolMarker.modality: Modality?
get() = when (this) {
is IrConstructorSymbol -> Modality.FINAL
is IrSymbol -> (owner as? IrOverridableMember)?.modality
else -> shouldNotBeCalled()
}
override val CallableSymbolMarker.visibility: Visibility
get() = when (this) {
is IrEnumEntrySymbol -> Visibilities.Public
is IrSymbol -> (owner as IrDeclarationWithVisibility).visibility.delegate
else -> shouldNotBeCalled()
}
override val RegularClassSymbolMarker.superTypes: List<IrType>
get() = asIr().superTypes
override val RegularClassSymbolMarker.superTypesRefs: List<TypeRefMarker>
get() = superTypes
override val RegularClassSymbolMarker.defaultType: KotlinTypeMarker
get() = asIr().defaultType
override val CallableSymbolMarker.isExpect: Boolean
get() = processIr(
onFunction = { it.isExpect },
onProperty = { it.isExpect },
onField = { false },
onValueParameter = { false },
onEnumEntry = { false }
)
override val CallableSymbolMarker.isInline: Boolean
get() = processIr(
onFunction = { it.isInline },
onProperty = { false }, // property can not be inline in IR. Its getter is inline instead
onField = { false },
onValueParameter = { false },
onEnumEntry = { false }
)
override val CallableSymbolMarker.isSuspend: Boolean
get() = processIr(
onFunction = { it.isSuspend },
onProperty = { false }, // property can not be suspend in IR. Its getter is suspend instead
onField = { false },
onValueParameter = { false },
onEnumEntry = { false }
)
override val CallableSymbolMarker.isExternal: Boolean
get() = safeAsIr<IrPossiblyExternalDeclaration>()?.isExternal ?: false
override val CallableSymbolMarker.isInfix: Boolean
get() = safeAsIr<IrSimpleFunction>()?.isInfix ?: false
override val CallableSymbolMarker.isOperator: Boolean
get() = safeAsIr<IrSimpleFunction>()?.isOperator ?: false
override val CallableSymbolMarker.isTailrec: Boolean
get() = safeAsIr<IrSimpleFunction>()?.isTailrec ?: false
override val PropertySymbolMarker.isVar: Boolean
get() = asIr().isVar
override val PropertySymbolMarker.isLateinit: Boolean
get() = asIr().isLateinit
override val PropertySymbolMarker.isConst: Boolean
get() = asIr().isConst
override val PropertySymbolMarker.getter: FunctionSymbolMarker?
get() = asIr().getter?.symbol
override val PropertySymbolMarker.setter: FunctionSymbolMarker?
get() = asIr().setter?.symbol
override fun createExpectActualTypeParameterSubstitutor(
expectActualTypeParameters: List<Pair<TypeParameterSymbolMarker, TypeParameterSymbolMarker>>,
parentSubstitutor: TypeSubstitutorMarker?,
): TypeSubstitutorMarker {
val typeParametersToArguments = expectActualTypeParameters.associate { (expect, actual) ->
(expect as IrTypeParameterSymbol) to (actual as IrTypeParameterSymbol).owner.defaultType
}
val substitutor = IrTypeSubstitutor(typeParametersToArguments, allowEmptySubstitution = true)
return when (parentSubstitutor) {
null -> substitutor
is AbstractIrTypeSubstitutor -> IrChainedSubstitutor(parentSubstitutor, substitutor)
else -> shouldNotBeCalled()
}
}
/*
* [isActualDeclaration] flag is needed to correctly determine scope for specific class
* In IR there are no scopes, all declarations are stored inside IrClass itself, so this flag
* has no sense in IR context
*/
override fun RegularClassSymbolMarker.collectAllMembers(isActualDeclaration: Boolean): List<DeclarationSymbolMarker> {
return asIr().declarations.filterNot { it is IrAnonymousInitializer }.map { it.symbol }
}
override fun RegularClassSymbolMarker.getMembersForExpectClass(name: Name): List<DeclarationSymbolMarker> {
return asIr().declarations.filter { it.getNameWithAssert() == name }.map { it.symbol }
}
override fun RegularClassSymbolMarker.collectEnumEntryNames(): List<Name> {
return asIr().declarations.filterIsInstance<IrEnumEntry>().map { it.name }
}
override fun RegularClassSymbolMarker.collectEnumEntries(): List<DeclarationSymbolMarker> {
return asIr().declarations.filterIsInstance<IrEnumEntry>().map { it.symbol }
}
override val CallableSymbolMarker.dispatchReceiverType: KotlinTypeMarker?
get() = (asIr().parent as? IrClass)?.defaultType
override val CallableSymbolMarker.extensionReceiverType: IrType?
get() = safeAsIr<IrFunction>()?.extensionReceiverParameter?.type
override val CallableSymbolMarker.extensionReceiverTypeRef: TypeRefMarker?
get() = extensionReceiverType
override val CallableSymbolMarker.returnType: IrType
get() = processIr(
onFunction = { it.returnType },
onProperty = { it.getter?.returnType ?: it.backingField?.type ?: error("No type for property: $it") },
onField = { it.type },
onValueParameter = { it.type },
onEnumEntry = { it.parentAsClass.defaultType }
)
override val CallableSymbolMarker.returnTypeRef: TypeRefMarker
get() = returnType
override val CallableSymbolMarker.typeParameters: List<TypeParameterSymbolMarker>
get() = processIr(
onFunction = { it.typeParameters.map { parameter -> parameter.symbol } },
onProperty = { it.getter?.symbol?.typeParameters.orEmpty() },
onField = { emptyList() },
onValueParameter = { emptyList() },
onEnumEntry = { emptyList() }
)
override fun FunctionSymbolMarker.allOverriddenDeclarationsRecursive(): Sequence<CallableSymbolMarker> =
throw NotImplementedError("Not implemented because it's unused")
override val FunctionSymbolMarker.valueParameters: List<ValueParameterSymbolMarker>
get() = asIr().valueParameters.map { it.symbol }
override val ValueParameterSymbolMarker.isVararg: Boolean
get() = asIr().isVararg
override val ValueParameterSymbolMarker.isNoinline: Boolean
get() = asIr().isNoinline
override val ValueParameterSymbolMarker.isCrossinline: Boolean
get() = asIr().isCrossinline
override val ValueParameterSymbolMarker.hasDefaultValue: Boolean
get() = asIr().hasDefaultValue()
override fun CallableSymbolMarker.isAnnotationConstructor(): Boolean {
val irConstructor = safeAsIr<IrConstructor>() ?: return false
return irConstructor.constructedClass.isAnnotationClass
}
override val TypeParameterSymbolMarker.bounds: List<IrType>
get() = asIr().superTypes
override val TypeParameterSymbolMarker.boundsTypeRefs: List<TypeRefMarker>
get() = bounds
override val TypeParameterSymbolMarker.variance: Variance
get() = asIr().variance
override val TypeParameterSymbolMarker.isReified: Boolean
get() = asIr().isReified
override fun areCompatibleExpectActualTypes(
expectType: KotlinTypeMarker?,
actualType: KotlinTypeMarker?,
parameterOfAnnotationComparisonMode: Boolean,
dynamicTypesEqualToAnything: Boolean
): Boolean {
if (expectType == null) return actualType == null
if (actualType == null) return false
/*
* Here we need to actualize both types, because of following situation:
*
* // MODULE: common
* expect fun foo(): S // (1)
* expect class S
*
* // MODULE: intermediate
* actual fun foo(): S = null!! // (2)
*
* // MODULE: platform
* actual typealias S = String
*
* When we match return types of (1) and (2) they both will have original type `S`, but from
* perspective of module `platform` it should be replaced with `String`
*/
val actualizedExpectType = expectType.actualize()
val actualizedActualType = actualType.actualize()
if (parameterOfAnnotationComparisonMode && actualizedExpectType is IrSimpleType && actualizedExpectType.isArray() &&
actualizedActualType is IrSimpleType && actualizedActualType.isArray()
) {
return AbstractTypeChecker.equalTypes(
createTypeCheckerState(),
actualizedExpectType.convertToArrayWithOutProjections(),
actualizedActualType.convertToArrayWithOutProjections()
)
}
return AbstractTypeChecker.equalTypes(
createTypeCheckerState(),
actualizedExpectType,
actualizedActualType
)
}
private fun IrSimpleType.convertToArrayWithOutProjections(): IrSimpleType {
val argumentsWithOutProjection = List(arguments.size) { i ->
val typeArgument = arguments[i]
if (typeArgument !is IrSimpleType) typeArgument
else makeTypeProjection(typeArgument, Variance.OUT_VARIANCE)
}
return IrSimpleTypeImpl(classifier, isNullable(), argumentsWithOutProjection, annotations)
}
private fun createTypeCheckerState(): TypeCheckerState {
return typeContext.newTypeCheckerState(errorTypesEqualToAnything = true, stubTypesEqualToAnything = false)
}
override fun actualTypeIsSubtypeOfExpectType(expectType: KotlinTypeMarker, actualType: KotlinTypeMarker): Boolean {
return AbstractTypeChecker.isSubtypeOf(
createTypeCheckerState(),
subType = actualType.actualize(),
superType = expectType.actualize()
)
}
private fun KotlinTypeMarker.actualize(): IrType {
return actualizingSubstitutor.substitute(this as IrType)
}
private val actualizingSubstitutor = ActualizingSubstitutor()
private inner class ActualizingSubstitutor : AbstractIrTypeSubstitutor() {
override fun substitute(type: IrType): IrType {
return substituteOrNull(type) ?: type
}
private fun substituteOrNull(type: IrType): IrType? {
if (type !is IrSimpleTypeImpl) return null
val newClassifier = (type.classifier.owner as? IrClass)?.let { expectToActualClassMap[it.classIdOrFail] }
val newArguments = ArrayList<IrTypeArgument>(type.arguments.size)
var argumentsChanged = false
for (argument in type.arguments) {
val newArgument = substituteArgumentOrNull(argument)
if (newArgument != null) {
newArguments += newArgument
argumentsChanged = true
} else {
newArguments += argument
}
}
if (newClassifier == null && !argumentsChanged) {
return null
}
return IrSimpleTypeImpl(
classifier = newClassifier ?: type.classifier,
type.nullability,
newArguments,
type.annotations,
type.abbreviation
)
}
private fun substituteArgumentOrNull(argument: IrTypeArgument): IrTypeArgument? {
return when (argument) {
is IrStarProjection -> null
is IrTypeProjection -> when (argument) {
is IrTypeProjectionImpl -> {
val newType = substituteOrNull(argument.type) ?: return null
makeTypeProjection(newType, argument.variance)
}
is IrTypeBase -> substituteOrNull(argument) as IrTypeBase?
else -> shouldNotBeCalled()
}
}
}
}
override fun RegularClassSymbolMarker.isNotSamInterface(): Boolean {
/*
* This is incorrect for java classes (because all java interfaces are considered as fun interfaces),
* but it's fine to not to check if some java interfaces is really SAM or not, because if one
* tries to actualize `expect fun interface` with typealias to non-SAM java interface, frontend
* will report an error and IR matching won't be invoked
*/
return !asIr().isFun
}
override fun CallableSymbolMarker.shouldSkipMatching(containingExpectClass: RegularClassSymbolMarker): Boolean {
return false
}
override val CallableSymbolMarker.hasStableParameterNames: Boolean
get() {
var ir = asIr()
if (ir.isFakeOverride && ir is IrOverridableDeclaration<*>) {
ir.resolveFakeOverride()?.let { ir = it }
}
return when (ir.origin) {
IrDeclarationOrigin.IR_EXTERNAL_DECLARATION_STUB,
IrDeclarationOrigin.IR_EXTERNAL_JAVA_DECLARATION_STUB,
-> false
else -> true
}
}
override fun onMatchedMembers(
expectSymbol: DeclarationSymbolMarker,
actualSymbol: DeclarationSymbolMarker,
containingExpectClassSymbol: RegularClassSymbolMarker?,
containingActualClassSymbol: RegularClassSymbolMarker?,
) {
require(expectSymbol is IrSymbol)
require(actualSymbol is IrSymbol)
when (expectSymbol) {
is IrClassSymbol -> {
val actualClassSymbol = when (actualSymbol) {
is IrClassSymbol -> actualSymbol
is IrTypeAliasSymbol -> actualSymbol.owner.expandedType.getClass()!!.symbol
else -> actualSymbol.unexpectedSymbolKind<IrClassifierSymbol>()
}
onMatchedClasses(expectSymbol, actualClassSymbol)
}
else -> onMatchedCallables(expectSymbol, actualSymbol)
}
}
abstract fun onMatchedClasses(expectClassSymbol: IrClassSymbol, actualClassSymbol: IrClassSymbol)
abstract fun onMatchedCallables(expectSymbol: IrSymbol, actualSymbol: IrSymbol)
override val DeclarationSymbolMarker.annotations: List<AnnotationCallInfo>
get() = asIr().annotations.map(::AnnotationCallInfoImpl)
override fun areAnnotationArgumentsEqual(
expectAnnotation: AnnotationCallInfo, actualAnnotation: AnnotationCallInfo,
collectionArgumentsCompatibilityCheckStrategy: ExpectActualCollectionArgumentsCompatibilityCheckStrategy,
): Boolean {
fun AnnotationCallInfo.getIrElement(): IrConstructorCall = (this as AnnotationCallInfoImpl).irElement
return areIrExpressionConstValuesEqual(
expectAnnotation.getIrElement(),
actualAnnotation.getIrElement(),
collectionArgumentsCompatibilityCheckStrategy,
)
}
internal fun getClassIdAfterActualization(classId: ClassId): ClassId {
return expectToActualClassMap[classId]?.classId ?: classId
}
private inner class AnnotationCallInfoImpl(val irElement: IrConstructorCall) : AnnotationCallInfo {
override val annotationSymbol: IrConstructorCall = irElement
override val classId: ClassId?
get() = getAnnotationClass()?.classId
override val isRetentionSource: Boolean
get() = getAnnotationClass()?.getAnnotationRetention() == KotlinRetention.SOURCE
override val isOptIn: Boolean
get() = getAnnotationClass()?.hasAnnotation(OptInNames.REQUIRES_OPT_IN_FQ_NAME) ?: false
private fun getAnnotationClass(): IrClass? {
val annotationClass = irElement.type.getClass() ?: return null
return expectToActualClassMap[annotationClass.classId]?.owner ?: annotationClass
}
}
override val DeclarationSymbolMarker.hasSourceAnnotationsErased: Boolean
get() {
val ir = asIr()
return ir.sourceElement() == null && ir.origin !is IrDeclarationOrigin.GeneratedByPlugin
}
// IR checker traverses member scope itself and collects mappings
override val checkClassScopesForAnnotationCompatibility = false
override fun skipCheckingAnnotationsOfActualClassMember(actualMember: DeclarationSymbolMarker): Boolean = error("Should not be called")
override fun findPotentialExpectClassMembersForActual(
expectClass: RegularClassSymbolMarker,
actualClass: RegularClassSymbolMarker,
actualMember: DeclarationSymbolMarker,
): Map<out DeclarationSymbolMarker, ExpectActualMatchingCompatibility> = error("Should not be called")
// It's a stub, because not needed anywhere
private object IrSourceElementStub : SourceElementMarker
override fun DeclarationSymbolMarker.getSourceElement(): SourceElementMarker = IrSourceElementStub
override fun TypeRefMarker.getClassId(): ClassId? = (this as IrType).getClass()?.classId
override fun checkAnnotationsOnTypeRefAndArguments(
expectContainingSymbol: DeclarationSymbolMarker,
actualContainingSymbol: DeclarationSymbolMarker,
expectTypeRef: TypeRefMarker,
actualTypeRef: TypeRefMarker,
checker: ExpectActualMatchingContext.AnnotationsCheckerCallback
) {
check(expectTypeRef is IrType && actualTypeRef is IrType)
fun IrType.getAnnotations() = annotations.map(::AnnotationCallInfoImpl)
checker.check(expectTypeRef.getAnnotations(), actualTypeRef.getAnnotations(), IrSourceElementStub)
if (expectTypeRef !is IrSimpleType || actualTypeRef !is IrSimpleType) return
if (expectTypeRef.arguments.size != actualTypeRef.arguments.size) return
for ((expectArg, actualArg) in expectTypeRef.arguments.zip(actualTypeRef.arguments)) {
val expectArgType = expectArg.typeOrNull ?: continue
val actualArgType = actualArg.typeOrNull ?: continue
checkAnnotationsOnTypeRefAndArguments(expectContainingSymbol, actualContainingSymbol, expectArgType, actualArgType, checker)
}
}
}
@@ -1,63 +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.backend.common.actualizer.checker
import org.jetbrains.kotlin.backend.common.actualizer.reportActualAnnotationConflictingDefaultArgumentValue
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrValueParameter
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.symbols.IrConstructorSymbol
import org.jetbrains.kotlin.ir.util.file
import org.jetbrains.kotlin.ir.util.parentAsClass
import org.jetbrains.kotlin.resolve.calls.mpp.ExpectActualCollectionArgumentsCompatibilityCheckStrategy
internal object IrExpectActualAnnotationConflictingDefaultArgumentValueChecker : IrExpectActualChecker {
override fun check(context: IrExpectActualChecker.Context) = with(context) {
for ((expectSymbol, actualSymbol) in matchedExpectToActual) {
if (expectSymbol !is IrConstructorSymbol || actualSymbol !is IrConstructorSymbol) continue
val expectClass = expectSymbol.owner.parentAsClass
if (expectClass.kind != ClassKind.ANNOTATION_CLASS) continue
val expectValueParams = expectSymbol.owner.valueParameters
val actualValueParams = actualSymbol.owner.valueParameters
if (expectValueParams.size != actualValueParams.size) continue
for ((expectParam, actualParam) in expectValueParams.zip(actualValueParams)) {
val expectDefaultValue = expectParam.defaultValue?.expression ?: continue
val actualDefaultValue = actualParam.defaultValue?.expression ?: continue
with(matchingContext) {
if (!areIrExpressionConstValuesEqual(
expectDefaultValue, actualDefaultValue,
ExpectActualCollectionArgumentsCompatibilityCheckStrategy.Default
)
) {
reportError(expectClass, actualDefaultValue, actualParam)
}
}
}
}
}
private fun IrExpectActualChecker.Context.reportError(
expectAnnotationClass: IrClass,
actualDefaultValue: IrExpression,
actualParam: IrValueParameter,
) {
val actualTypealias = getTypealiasSymbolIfActualizedViaTypealias(expectAnnotationClass, classActualizationInfo)?.owner
if (actualTypealias != null) {
diagnosticsReporter.reportActualAnnotationConflictingDefaultArgumentValue(
actualTypealias, actualTypealias.file, actualParam
)
return
}
diagnosticsReporter.reportActualAnnotationConflictingDefaultArgumentValue(
actualDefaultValue, actualParam.file, actualParam
)
}
}
@@ -1,55 +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.backend.common.actualizer.checker
import org.jetbrains.kotlin.backend.common.actualizer.reportActualAnnotationsNotMatchExpect
import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.ir.declarations.IrDeclaration
import org.jetbrains.kotlin.ir.expressions.IrConstructorCall
import org.jetbrains.kotlin.ir.symbols.IrSymbol
import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol
import org.jetbrains.kotlin.ir.util.isFakeOverride
import org.jetbrains.kotlin.resolve.calls.mpp.AbstractExpectActualAnnotationMatchChecker
internal object IrExpectActualAnnotationMatchingChecker : IrExpectActualChecker {
override fun check(context: IrExpectActualChecker.Context) = with(context) {
if (!diagnosticsReporter.languageVersionSettings.supportsFeature(LanguageFeature.MultiplatformRestrictions)) {
return
}
for ((expectSymbol, actualSymbol) in matchedExpectToActual.entries) {
if (expectSymbol is IrTypeParameterSymbol) {
continue
}
if (expectSymbol.isFakeOverride) {
continue
}
val incompatibility =
AbstractExpectActualAnnotationMatchChecker.areAnnotationsCompatible(expectSymbol, actualSymbol, matchingContext) ?: continue
val reportOn = getTypealiasSymbolIfActualizedViaTypealias(expectSymbol.owner as IrDeclaration, classActualizationInfo)
?: getContainingActualClassIfFakeOverride(actualSymbol)
?: actualSymbol
diagnosticsReporter.reportActualAnnotationsNotMatchExpect(
incompatibility.expectSymbol as IrSymbol,
incompatibility.actualSymbol as IrSymbol,
incompatibility.type.mapAnnotationType { it.annotationSymbol as IrConstructorCall },
reportOn,
)
}
}
private val IrSymbol.isFakeOverride: Boolean
get() = (owner as IrDeclaration).isFakeOverride
private fun getContainingActualClassIfFakeOverride(actualSymbol: IrSymbol): IrSymbol? {
if (!actualSymbol.isFakeOverride) {
return null
}
return getContainingTopLevelClass(actualSymbol.owner as IrDeclaration)?.symbol
}
}
@@ -1,24 +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.backend.common.actualizer.checker
import org.jetbrains.kotlin.KtDiagnosticReporterWithImplicitIrBasedContext
import org.jetbrains.kotlin.backend.common.actualizer.ClassActualizationInfo
import org.jetbrains.kotlin.backend.common.actualizer.IrExpectActualMatchingContext
import org.jetbrains.kotlin.ir.symbols.IrSymbol
import org.jetbrains.kotlin.ir.types.IrTypeSystemContext
internal interface IrExpectActualChecker {
interface Context {
val matchedExpectToActual: Map<IrSymbol, IrSymbol>
val classActualizationInfo: ClassActualizationInfo
val typeSystemContext: IrTypeSystemContext
val diagnosticsReporter: KtDiagnosticReporterWithImplicitIrBasedContext
val matchingContext: IrExpectActualMatchingContext
}
fun check(context: Context)
}
@@ -1,42 +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.backend.common.actualizer.checker
import org.jetbrains.kotlin.KtDiagnosticReporterWithImplicitIrBasedContext
import org.jetbrains.kotlin.backend.common.actualizer.ClassActualizationInfo
import org.jetbrains.kotlin.backend.common.actualizer.IrExpectActualMatchingContext
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
import org.jetbrains.kotlin.ir.symbols.IrSymbol
import org.jetbrains.kotlin.ir.types.IrTypeSystemContext
internal class IrExpectActualCheckers(
override val matchedExpectToActual: Map<IrSymbol, IrSymbol>,
override val classActualizationInfo: ClassActualizationInfo,
override val typeSystemContext: IrTypeSystemContext,
override val diagnosticsReporter: KtDiagnosticReporterWithImplicitIrBasedContext,
) : IrExpectActualChecker.Context {
private val checkers: Set<IrExpectActualChecker> = setOf(
IrExpectActualAnnotationMatchingChecker,
IrExpectActualAnnotationConflictingDefaultArgumentValueChecker,
)
override val matchingContext = object : IrExpectActualMatchingContext(typeSystemContext, classActualizationInfo.actualClasses) {
override fun onMatchedClasses(expectClassSymbol: IrClassSymbol, actualClassSymbol: IrClassSymbol) {
error("Must not be called")
}
override fun onMatchedCallables(expectSymbol: IrSymbol, actualSymbol: IrSymbol) {
error("Must not be called")
}
}
fun check() {
for (checker in checkers) {
checker.check(context = this)
}
}
}
@@ -1,57 +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.backend.common.actualizer.checker
import org.jetbrains.kotlin.backend.common.actualizer.IrExpectActualMatchingContext
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.types.classOrFail
import org.jetbrains.kotlin.resolve.calls.mpp.ExpectActualCollectionArgumentsCompatibilityCheckStrategy
internal fun IrExpectActualMatchingContext.areIrExpressionConstValuesEqual(
a: IrElement?,
b: IrElement?,
collectionArgumentsCompatibilityCheckStrategy: ExpectActualCollectionArgumentsCompatibilityCheckStrategy,
): Boolean {
return when {
a == null || b == null -> (a == null) == (b == null)
a::class != b::class -> false
a is IrConst<*> && b is IrConst<*> -> a.value == b.value
a is IrClassReference && b is IrClassReference -> equalBy(a, b) {
val classId = it.classType.classOrFail.classId
getClassIdAfterActualization(classId)
}
a is IrGetEnumValue && b is IrGetEnumValue ->
areCompatibleExpectActualTypes(a.type, b.type) && equalBy(a, b) { it.symbol.owner.name }
a is IrVararg && b is IrVararg -> {
collectionArgumentsCompatibilityCheckStrategy.areCompatible(a.elements, b.elements) { f, s ->
areIrExpressionConstValuesEqual(f, s, collectionArgumentsCompatibilityCheckStrategy)
}
}
a is IrConstructorCall && b is IrConstructorCall -> {
equalBy(a, b) { it.valueArgumentsCount } &&
areCompatibleExpectActualTypes(a.type, b.type) &&
(0..<a.valueArgumentsCount).all { i ->
areIrExpressionConstValuesEqual(
a.getValueArgument(i),
b.getValueArgument(i),
collectionArgumentsCompatibilityCheckStrategy
)
}
}
else -> error("Not handled expression types $a $b")
}
}
private inline fun <T : Any> equalBy(first: T, second: T, selector: (T) -> Any?): Boolean =
selector(first) == selector(second)
@@ -1,27 +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.backend.common.actualizer.checker
import org.jetbrains.kotlin.backend.common.actualizer.ClassActualizationInfo
import org.jetbrains.kotlin.backend.common.lower.parents
import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrDeclaration
import org.jetbrains.kotlin.ir.symbols.IrTypeAliasSymbol
import org.jetbrains.kotlin.ir.util.classIdOrFail
internal fun getTypealiasSymbolIfActualizedViaTypealias(
expectDeclaration: IrDeclaration,
classActualizationInfo: ClassActualizationInfo,
): IrTypeAliasSymbol? {
val topLevelExpectClass = getContainingTopLevelClass(expectDeclaration) ?: return null
val classId = topLevelExpectClass.classIdOrFail
return classActualizationInfo.actualTypeAliases[classId]
}
internal fun getContainingTopLevelClass(expectDeclaration: IrDeclaration): IrClass? {
val parentsWithSelf = sequenceOf(expectDeclaration) + expectDeclaration.parents
return parentsWithSelf.filterIsInstance<IrClass>().lastOrNull()
}