[IR] Moved actualizer and related things to separate module
^KT-62292
This commit is contained in:
committed by
Space Team
parent
62700c3ec3
commit
649a7bd66b
-174
@@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
-322
@@ -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() }
|
||||
}
|
||||
-179
@@ -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)
|
||||
}
|
||||
}
|
||||
-242
@@ -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
|
||||
}
|
||||
-57
@@ -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 }
|
||||
}
|
||||
}
|
||||
-54
@@ -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)
|
||||
}
|
||||
}
|
||||
-127
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
-233
@@ -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
|
||||
}
|
||||
}
|
||||
-586
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
-63
@@ -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
|
||||
)
|
||||
}
|
||||
}
|
||||
-55
@@ -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
|
||||
}
|
||||
}
|
||||
-24
@@ -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)
|
||||
}
|
||||
-42
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
-57
@@ -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)
|
||||
-27
@@ -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()
|
||||
}
|
||||
Reference in New Issue
Block a user