[IR] Use common expect/actual matching algorithm in IR actualizer
^KT-58578 Fixed
This commit is contained in:
committed by
Space Team
parent
62534f43c9
commit
ba41e8ec38
+19
@@ -6,18 +6,24 @@
|
||||
package org.jetbrains.kotlin.backend.common
|
||||
|
||||
import com.intellij.psi.PsiElement
|
||||
import org.jetbrains.kotlin.backend.common.BackendDiagnosticRenderers.INCOMPATIBILITY
|
||||
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
|
||||
import org.jetbrains.kotlin.diagnostics.KtDiagnosticFactoryToRendererMap
|
||||
import org.jetbrains.kotlin.diagnostics.error0
|
||||
import org.jetbrains.kotlin.diagnostics.error2
|
||||
import org.jetbrains.kotlin.diagnostics.error3
|
||||
import org.jetbrains.kotlin.diagnostics.rendering.BaseDiagnosticRendererFactory
|
||||
import org.jetbrains.kotlin.diagnostics.rendering.CommonRenderers.STRING
|
||||
import org.jetbrains.kotlin.diagnostics.rendering.Renderer
|
||||
import org.jetbrains.kotlin.diagnostics.rendering.Renderers.MODULE_WITH_PLATFORM
|
||||
import org.jetbrains.kotlin.diagnostics.rendering.RootDiagnosticRendererFactory
|
||||
import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility
|
||||
|
||||
object CommonBackendErrors {
|
||||
val NO_ACTUAL_FOR_EXPECT by error2<PsiElement, String, ModuleDescriptor>()
|
||||
val MANY_INTERFACES_MEMBER_NOT_IMPLEMENTED by error2<PsiElement, String, String>()
|
||||
val MANY_IMPL_MEMBER_NOT_IMPLEMENTED by error2<PsiElement, String, String>()
|
||||
val INCOMPATIBLE_MATCHING by error3<PsiElement, String, String, ExpectActualCompatibility.Incompatible<*>>()
|
||||
|
||||
init {
|
||||
RootDiagnosticRendererFactory.registerFactory(KtDefaultCommonBackendErrorMessages)
|
||||
@@ -44,5 +50,18 @@ object KtDefaultCommonBackendErrorMessages : BaseDiagnosticRendererFactory() {
|
||||
STRING,
|
||||
STRING,
|
||||
)
|
||||
map.put(
|
||||
CommonBackendErrors.INCOMPATIBLE_MATCHING,
|
||||
"Expect declaration `{0}` doesn''t match actual `{1}` because {2}",
|
||||
STRING,
|
||||
STRING,
|
||||
INCOMPATIBILITY
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
object BackendDiagnosticRenderers {
|
||||
val INCOMPATIBILITY = Renderer<ExpectActualCompatibility.Incompatible<*>> {
|
||||
it.reason ?: "<unknown>"
|
||||
}
|
||||
}
|
||||
|
||||
+62
-22
@@ -8,13 +8,18 @@ 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.IrBuiltIns
|
||||
import org.jetbrains.kotlin.ir.IrElement
|
||||
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.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
|
||||
|
||||
/**
|
||||
* It adds fake overrides to non-expect classes inside common or multi-platform module,
|
||||
@@ -27,9 +32,11 @@ import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
|
||||
*/
|
||||
internal class ActualFakeOverridesAdder(
|
||||
private val expectActualMap: Map<IrSymbol, IrSymbol>,
|
||||
private val diagnosticsReporter: KtDiagnosticReporterWithImplicitIrBasedContext
|
||||
private val expectToActualClassMap: Map<ClassId, IrClassSymbol>,
|
||||
private val diagnosticsReporter: KtDiagnosticReporterWithImplicitIrBasedContext,
|
||||
private val typeSystemContext: IrTypeSystemContext
|
||||
) : IrElementVisitorVoid {
|
||||
private val missingActualMembersMap = mutableMapOf<IrClass, MutableMap<String, MutableList<IrDeclaration>>>()
|
||||
private val missingActualMembersMap = mutableMapOf<IrClass, FakeOverrideInfo>()
|
||||
|
||||
override fun visitClass(declaration: IrClass) {
|
||||
extractMissingActualMembersFromSupertypes(declaration)
|
||||
@@ -40,20 +47,20 @@ internal class ActualFakeOverridesAdder(
|
||||
element.acceptChildrenVoid(this)
|
||||
}
|
||||
|
||||
private fun extractMissingActualMembersFromSupertypes(klass: IrClass): Map<String, MutableList<IrDeclaration>> {
|
||||
private fun extractMissingActualMembersFromSupertypes(klass: IrClass): FakeOverrideInfo {
|
||||
missingActualMembersMap[klass]?.let { return it }
|
||||
|
||||
val missingActualMembers = mutableMapOf<String, MutableList<IrDeclaration>>()
|
||||
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 = mutableMapOf<String, MutableList<IrDeclaration>>()
|
||||
val processedMembers = FakeOverrideInfo()
|
||||
for (superType in klass.superTypes) {
|
||||
val membersFromSupertype = extractMissingActualMembersFromSupertypes(superType.classifierOrFail.owner as IrClass)
|
||||
if (!klass.isExpect) {
|
||||
missingActualMembers.appendMissingMembersToNotExpectClass(klass, membersFromSupertype, processedMembers)
|
||||
appendMissingMembersToNotExpectClass(missingActualMembers, klass, membersFromSupertype.allSymbols(), processedMembers)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,22 +71,35 @@ internal class ActualFakeOverridesAdder(
|
||||
return missingActualMembers
|
||||
}
|
||||
|
||||
private fun MutableMap<String, MutableList<IrDeclaration>>.appendMissingMembersToNotExpectClass(
|
||||
private fun appendMissingMembersToNotExpectClass(
|
||||
fakeOverrideInfo: FakeOverrideInfo,
|
||||
klass: IrClass,
|
||||
membersFromSupertype: Map<String, MutableList<IrDeclaration>>,
|
||||
processedMembers: MutableMap<String, MutableList<IrDeclaration>>
|
||||
membersFromSupertype: List<IrSymbol>,
|
||||
processedMembers: FakeOverrideInfo
|
||||
) {
|
||||
for (memberFromSupertype in membersFromSupertype.flatMap { it.value }) {
|
||||
for (symbolFromSupertype in membersFromSupertype) {
|
||||
val memberFromSupertype = symbolFromSupertype.owner as IrDeclaration
|
||||
val newMember = createFakeOverrideMember(listOf(memberFromSupertype), klass)
|
||||
val name = newMember.getFunctionOrPropertyName()
|
||||
if (getMatches(name, newMember, expectActualMap).isEmpty()) {
|
||||
val matchingFakeOverrides = collectActualCallablesMatchingToSpecificExpect(
|
||||
newMember.symbol,
|
||||
fakeOverrideInfo.getMembersForActual(newMember),
|
||||
expectToActualClassMap,
|
||||
typeSystemContext
|
||||
)
|
||||
if (matchingFakeOverrides.isEmpty()) {
|
||||
processedMembers.addMember(memberFromSupertype as IrOverridableDeclaration<*>)
|
||||
addMember(newMember)
|
||||
fakeOverrideInfo.addMember(newMember)
|
||||
klass.addMember(newMember)
|
||||
} else {
|
||||
val baseMember = processedMembers.getMatches(name, newMember, expectActualMap).single()
|
||||
val baseMembers = collectActualCallablesMatchingToSpecificExpect(
|
||||
newMember.symbol,
|
||||
processedMembers.getMembersForActual(newMember),
|
||||
expectToActualClassMap,
|
||||
typeSystemContext
|
||||
)
|
||||
|
||||
val errorFactory =
|
||||
if ((baseMember.parent as IrClass).isInterface && (memberFromSupertype.parent as IrClass).isInterface)
|
||||
if (baseMembers.all { ((it.owner as IrDeclaration).parent as IrClass).isInterface } && (memberFromSupertype.parent as IrClass).isInterface)
|
||||
CommonBackendErrors.MANY_INTERFACES_MEMBER_NOT_IMPLEMENTED
|
||||
else
|
||||
CommonBackendErrors.MANY_IMPL_MEMBER_NOT_IMPLEMENTED
|
||||
@@ -92,7 +112,7 @@ internal class ActualFakeOverridesAdder(
|
||||
}
|
||||
}
|
||||
|
||||
private fun MutableMap<String, MutableList<IrDeclaration>>.appendMissingMembersFromActualizedExpectClass(
|
||||
private fun FakeOverrideInfo.appendMissingMembersFromActualizedExpectClass(
|
||||
expectClass: IrClass,
|
||||
actualClass: IrClass,
|
||||
) {
|
||||
@@ -110,11 +130,31 @@ internal class ActualFakeOverridesAdder(
|
||||
}
|
||||
}
|
||||
|
||||
private fun MutableMap<String, MutableList<IrDeclaration>>.addMember(member: IrOverridableDeclaration<*>) {
|
||||
getOrPut(member.getFunctionOrPropertyName()) { mutableListOf() }.add(member)
|
||||
}
|
||||
private class FakeOverrideInfo {
|
||||
val functionsByName: MutableMap<Name, MutableList<IrSymbol>> = mutableMapOf()
|
||||
val propertiesByName: MutableMap<Name, MutableList<IrSymbol>> = mutableMapOf()
|
||||
|
||||
private fun IrOverridableDeclaration<*>.getFunctionOrPropertyName(): String {
|
||||
return (this as IrDeclarationWithName).name.asString() + (if (this is IrSimpleFunction) "()" else "")
|
||||
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()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+140
-109
@@ -8,66 +8,97 @@ package org.jetbrains.kotlin.backend.common.actualizer
|
||||
import org.jetbrains.kotlin.KtDiagnosticReporterWithImplicitIrBasedContext
|
||||
import org.jetbrains.kotlin.ir.IrElement
|
||||
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.*
|
||||
import org.jetbrains.kotlin.ir.types.IrTypeSystemContext
|
||||
import org.jetbrains.kotlin.ir.types.classifierOrFail
|
||||
import org.jetbrains.kotlin.ir.util.isFakeOverride
|
||||
import org.jetbrains.kotlin.ir.util.kotlinFqName
|
||||
import org.jetbrains.kotlin.ir.util.callableId
|
||||
import org.jetbrains.kotlin.ir.util.classIdOrFail
|
||||
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
|
||||
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.mpp.DeclarationSymbolMarker
|
||||
import org.jetbrains.kotlin.name.CallableId
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
import org.jetbrains.kotlin.resolve.calls.mpp.AbstractExpectActualCompatibilityChecker
|
||||
import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility
|
||||
|
||||
/**
|
||||
* 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 diagnosticsReporter: KtDiagnosticReporterWithImplicitIrBasedContext
|
||||
private val typeSystemContext: IrTypeSystemContext,
|
||||
private val diagnosticsReporter: KtDiagnosticReporterWithImplicitIrBasedContext,
|
||||
) {
|
||||
fun collect(): MutableMap<IrSymbol, IrSymbol> {
|
||||
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 (actualMembers, expectActualTypeAliasMap) = result.appendExpectActualClassMap()
|
||||
result.appendExpectActualMemberMap(actualMembers, expectActualTypeAliasMap)
|
||||
return result
|
||||
val actualDeclarations = collectActualDeclarations()
|
||||
matchAllExpectDeclarations(result, actualDeclarations)
|
||||
return Result(result, actualDeclarations)
|
||||
}
|
||||
|
||||
private fun MutableMap<IrSymbol, IrSymbol>.appendExpectActualClassMap(): Pair<List<IrDeclarationBase>, Map<FqName, FqName>> {
|
||||
val actualClasses = mutableMapOf<String, IrClassSymbol>()
|
||||
// There is no list for builtins declarations; that's why they are being collected from typealiases
|
||||
val actualMembers = mutableListOf<IrDeclarationBase>()
|
||||
val expectActualTypeAliasMap = mutableMapOf<FqName, FqName>() // It's used to link members from expect class that have typealias actual
|
||||
|
||||
private fun collectActualDeclarations(): ClassActualizationInfo {
|
||||
val fragmentsWithActuals = dependentFragments.drop(1) + mainFragment
|
||||
val actualClassesAndMembersCollector = ActualClassesAndMembersCollector(actualClasses, actualMembers, expectActualTypeAliasMap)
|
||||
fragmentsWithActuals.forEach { actualClassesAndMembersCollector.collect(it) }
|
||||
|
||||
val linkCollector = ClassLinksCollector(this, actualClasses, expectActualTypeAliasMap, diagnosticsReporter)
|
||||
dependentFragments.forEach { linkCollector.visitModuleFragment(it) }
|
||||
|
||||
return actualMembers to expectActualTypeAliasMap
|
||||
return ActualDeclarationsCollector.collectActualsFromFragments(fragmentsWithActuals)
|
||||
}
|
||||
|
||||
private fun MutableMap<IrSymbol, IrSymbol>.appendExpectActualMemberMap(
|
||||
actualMembers: List<IrDeclarationBase>,
|
||||
expectActualTypeAliasMap: Map<FqName, FqName>
|
||||
private fun matchAllExpectDeclarations(
|
||||
destination: MutableMap<IrSymbol, IrSymbol>,
|
||||
classActualizationInfo: ClassActualizationInfo,
|
||||
) {
|
||||
val actualMembersMap = mutableMapOf<String, MutableList<IrDeclarationBase>>()
|
||||
for (actualMember in actualMembers) {
|
||||
actualMembersMap.getOrPut(generateIrElementFullNameFromExpect(actualMember, expectActualTypeAliasMap)) { mutableListOf() }
|
||||
.add(actualMember)
|
||||
}
|
||||
val collector = MemberLinksCollector(this, actualMembersMap, expectActualTypeAliasMap, diagnosticsReporter)
|
||||
dependentFragments.forEach { collector.visitModuleFragment(it) }
|
||||
val linkCollector = ExpectActualLinkCollector(destination, classActualizationInfo, typeSystemContext, diagnosticsReporter)
|
||||
dependentFragments.forEach { linkCollector.visitModuleFragment(it) }
|
||||
}
|
||||
}
|
||||
|
||||
private class ActualClassesAndMembersCollector(
|
||||
private val actualClasses: MutableMap<String, IrClassSymbol>,
|
||||
private val actualMembers: MutableList<IrDeclarationBase>,
|
||||
private val expectActualTypeAliasMap: MutableMap<FqName, FqName>
|
||||
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<IrDeclarationWithName>>,
|
||||
) {
|
||||
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
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private val actualClasses: MutableMap<ClassId, IrClassSymbol> = mutableMapOf()
|
||||
private val actualTypeAliasesWithoutExpansion: MutableMap<ClassId, IrTypeAliasSymbol> = mutableMapOf()
|
||||
private val actualTopLevels: MutableMap<CallableId, MutableList<IrDeclarationWithName>> = mutableMapOf()
|
||||
|
||||
private val visitedActualClasses = mutableSetOf<IrClass>()
|
||||
|
||||
fun collect(element: IrElement) {
|
||||
private fun collect(element: IrElement) {
|
||||
when (element) {
|
||||
is IrModuleFragment -> {
|
||||
for (file in element.files) {
|
||||
@@ -77,16 +108,18 @@ private class ActualClassesAndMembersCollector(
|
||||
is IrTypeAlias -> {
|
||||
if (!element.isActual) return
|
||||
|
||||
val classId = element.classIdOrFail
|
||||
val expandedTypeSymbol = element.expandedType.classifierOrFail as IrClassSymbol
|
||||
addActualClass(element, expandedTypeSymbol)
|
||||
collect(expandedTypeSymbol.owner)
|
||||
|
||||
expectActualTypeAliasMap[element.kotlinFqName] = expandedTypeSymbol.owner.kotlinFqName
|
||||
actualClasses[classId] = expandedTypeSymbol
|
||||
actualTypeAliasesWithoutExpansion[classId] = element.symbol
|
||||
|
||||
collect(expandedTypeSymbol.owner)
|
||||
}
|
||||
is IrClass -> {
|
||||
if (element.isExpect || !visitedActualClasses.add(element)) return
|
||||
|
||||
addActualClass(element, element.symbol)
|
||||
actualClasses[element.classIdOrFail] = element.symbol
|
||||
for (declaration in element.declarations) {
|
||||
collect(declaration)
|
||||
}
|
||||
@@ -97,101 +130,99 @@ private class ActualClassesAndMembersCollector(
|
||||
}
|
||||
}
|
||||
is IrEnumEntry -> {
|
||||
actualMembers.add(element) // If enum entry is located inside expect enum, then this code is not executed
|
||||
recordActualCallable(element, element.callableId) // If enum entry is located inside expect enum, then this code is not executed
|
||||
}
|
||||
is IrProperty -> {
|
||||
if (element.isExpect) return
|
||||
actualMembers.add(element)
|
||||
recordActualCallable(element, element.callableId)
|
||||
}
|
||||
is IrFunction -> {
|
||||
if (element.isExpect) return
|
||||
actualMembers.add(element)
|
||||
recordActualCallable(element, element.callableId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun addActualClass(classOrTypeAlias: IrElement, classSymbol: IrClassSymbol) {
|
||||
actualClasses[generateActualIrClassOrTypeAliasFullName(classOrTypeAlias)] = classSymbol
|
||||
private fun recordActualCallable(callableDeclaration: IrDeclarationWithName, callableId: CallableId) {
|
||||
if (callableId.classId == null) {
|
||||
actualTopLevels
|
||||
.getOrPut(callableId) { mutableListOf() }
|
||||
.add(callableDeclaration)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class ClassLinksCollector(
|
||||
private val expectActualMap: MutableMap<IrSymbol, IrSymbol>,
|
||||
private val actualClasses: Map<String, IrClassSymbol>,
|
||||
private val expectActualTypeAliasMap: Map<FqName, FqName>,
|
||||
private val diagnosticsReporter: KtDiagnosticReporterWithImplicitIrBasedContext
|
||||
private class ExpectActualLinkCollector(
|
||||
private val destination: MutableMap<IrSymbol, IrSymbol>,
|
||||
private val classActualizationInfo: ClassActualizationInfo,
|
||||
typeSystemContext: IrTypeSystemContext,
|
||||
private val diagnosticsReporter: KtDiagnosticReporterWithImplicitIrBasedContext,
|
||||
) : IrElementVisitorVoid {
|
||||
private val context = MatchingContext(typeSystemContext)
|
||||
|
||||
override fun visitFunction(declaration: IrFunction) {
|
||||
if (declaration.isExpect) {
|
||||
matchExpectCallable(declaration, declaration.callableId)
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitProperty(declaration: IrProperty) {
|
||||
if (declaration.isExpect) {
|
||||
matchExpectCallable(declaration, declaration.callableId)
|
||||
}
|
||||
}
|
||||
|
||||
private fun matchExpectCallable(declaration: IrDeclarationWithName, callableId: CallableId) {
|
||||
val actualSymbols = classActualizationInfo.actualTopLevels[callableId]?.map { it.symbol }.orEmpty()
|
||||
matchExpectDeclaration(declaration.symbol, actualSymbols)
|
||||
}
|
||||
|
||||
override fun visitClass(declaration: IrClass) {
|
||||
if (!declaration.isExpect) return
|
||||
val classId = declaration.classIdOrFail
|
||||
val expectClassSymbol = declaration.symbol
|
||||
val actualClassLikeSymbol = classActualizationInfo.getActualWithoutExpansion(classId)
|
||||
matchExpectDeclaration(expectClassSymbol, listOfNotNull(actualClassLikeSymbol))
|
||||
}
|
||||
|
||||
val actualClassSymbol = actualClasses[generateIrElementFullNameFromExpect(declaration, expectActualTypeAliasMap)]
|
||||
if (actualClassSymbol != null) {
|
||||
expectActualMap[declaration.symbol] = actualClassSymbol
|
||||
expectActualMap.appendTypeParametersMap(declaration, actualClassSymbol.owner)
|
||||
} else if (!declaration.containsOptionalExpectation()) {
|
||||
diagnosticsReporter.reportMissingActual(declaration)
|
||||
}
|
||||
|
||||
visitElement(declaration)
|
||||
private fun matchExpectDeclaration(expectSymbol: IrSymbol, actualSymbols: List<IrSymbol>) {
|
||||
AbstractExpectActualCompatibilityChecker.matchSingleExpectTopLevelDeclarationAgainstPotentialActuals(
|
||||
expectSymbol,
|
||||
actualSymbols,
|
||||
context
|
||||
)
|
||||
}
|
||||
|
||||
override fun visitElement(element: IrElement) {
|
||||
element.acceptChildrenVoid(this)
|
||||
}
|
||||
}
|
||||
|
||||
private class MemberLinksCollector(
|
||||
private val expectActualMap: MutableMap<IrSymbol, IrSymbol>,
|
||||
private val actualMembers: Map<String, List<IrDeclarationBase>>,
|
||||
private val typeAliasMap: Map<FqName, FqName>,
|
||||
private val diagnosticsReporter: KtDiagnosticReporterWithImplicitIrBasedContext
|
||||
) : IrElementVisitorVoid {
|
||||
override fun visitFunction(declaration: IrFunction) {
|
||||
if (declaration.isExpect) addLink(declaration)
|
||||
}
|
||||
private inner class MatchingContext(
|
||||
typeSystemContext: IrTypeSystemContext,
|
||||
) : IrExpectActualMatchingContext(typeSystemContext, classActualizationInfo.actualClasses) {
|
||||
override fun onMatchedClasses(expectClassSymbol: IrClassSymbol, actualClassSymbol: IrClassSymbol) {
|
||||
destination[expectClassSymbol] = actualClassSymbol
|
||||
recordActualForExpectDeclaration(expectClassSymbol, actualClassSymbol, destination)
|
||||
}
|
||||
|
||||
override fun visitProperty(declaration: IrProperty) {
|
||||
if (declaration.isExpect) addLink(declaration)
|
||||
}
|
||||
override fun onMatchedCallables(expectSymbol: IrSymbol, actualSymbol: IrSymbol) {
|
||||
recordActualForExpectDeclaration(expectSymbol, actualSymbol, destination)
|
||||
}
|
||||
|
||||
override fun visitEnumEntry(declaration: IrEnumEntry) {
|
||||
if ((declaration.parent as IrClass).isExpect) addLink(declaration)
|
||||
}
|
||||
|
||||
private fun addLink(expectDeclaration: IrDeclarationBase) {
|
||||
val actualMemberMatches = actualMembers.getMatches(expectDeclaration, expectActualMap, typeAliasMap)
|
||||
when {
|
||||
actualMemberMatches.size == 1 -> {
|
||||
expectActualMap.addLink(expectDeclaration, actualMemberMatches.single())
|
||||
override fun onMismatchedMembersFromClassScope(
|
||||
expectSymbol: DeclarationSymbolMarker,
|
||||
actualSymbolsByIncompatibility: Map<ExpectActualCompatibility.Incompatible<*>, List<DeclarationSymbolMarker>>,
|
||||
) {
|
||||
require(expectSymbol is IrSymbol)
|
||||
if (actualSymbolsByIncompatibility.isEmpty() && !expectSymbol.owner.containsOptionalExpectation()) {
|
||||
diagnosticsReporter.reportMissingActual(expectSymbol)
|
||||
}
|
||||
actualMemberMatches.size > 1 -> {
|
||||
// TODO: report AMBIGUOUS_ACTUALS here, see KT-57932
|
||||
}
|
||||
(expectDeclaration.parent as? IrClass)?.isExpect == false && expectDeclaration.isFakeOverride -> {
|
||||
// Remaining expect fake override members from not expect classes will be actualized in FakeOverridesTransformer
|
||||
// It occurs in a separated step because they require filled expectActualMap for members from expect classes
|
||||
}
|
||||
else -> {
|
||||
if (shouldReportMissingActual(expectDeclaration)) {
|
||||
diagnosticsReporter.reportMissingActual(expectDeclaration)
|
||||
for ((incompatibility, actualMemberSymbols) in actualSymbolsByIncompatibility) {
|
||||
for (actualSymbol in actualMemberSymbols) {
|
||||
require(actualSymbol is IrSymbol)
|
||||
diagnosticsReporter.reportIncompatibleExpectActual(expectSymbol, actualSymbol, incompatibility)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun shouldReportMissingActual(declaration: IrDeclarationBase): Boolean {
|
||||
return !declaration.parent.containsOptionalExpectation() && !(declaration is IrConstructor && declaration.isPrimary)
|
||||
}
|
||||
|
||||
override fun visitElement(element: IrElement) {
|
||||
element.acceptChildrenVoid(this)
|
||||
}
|
||||
}
|
||||
|
||||
fun MutableMap<IrSymbol, IrSymbol>.appendTypeParametersMap(
|
||||
expectTypeParametersContainer: IrTypeParametersContainer,
|
||||
actualTypeParametersContainer: IrTypeParametersContainer
|
||||
) {
|
||||
expectTypeParametersContainer.typeParameters.zip(actualTypeParametersContainer.typeParameters)
|
||||
.forEach { (expectTypeParameter, actualTypeParameter) -> this[expectTypeParameter.symbol] = actualTypeParameter.symbol }
|
||||
}
|
||||
+2
-2
@@ -47,11 +47,11 @@ internal class FakeOverridesActualizer(private val expectActualMap: MutableMap<I
|
||||
val actualizedOverrides = overriddenSymbols.map { (it.owner as IrDeclaration).actualize() }
|
||||
val actualFakeOverride = createFakeOverrideMember(actualizedOverrides, parent as IrClass)
|
||||
|
||||
expectActualMap.addLink(this as IrDeclarationBase, actualFakeOverride)
|
||||
recordActualForExpectDeclaration(this.symbol, actualFakeOverride.symbol, expectActualMap)
|
||||
|
||||
return actualFakeOverride
|
||||
}
|
||||
|
||||
klass.declarations.transformInPlace { if (it.isExpect && it.isFakeOverride) it.actualize() else it }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+8
-3
@@ -10,6 +10,7 @@ import org.jetbrains.kotlin.config.LanguageVersionSettings
|
||||
import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.symbols.IrSymbol
|
||||
import org.jetbrains.kotlin.ir.types.IrTypeSystemContext
|
||||
import org.jetbrains.kotlin.ir.util.DeepCopyTypeRemapper
|
||||
|
||||
data class IrActualizedResult(val actualizedExpectDeclarations: List<IrDeclaration>)
|
||||
@@ -19,15 +20,17 @@ object IrActualizer {
|
||||
mainFragment: IrModuleFragment,
|
||||
dependentFragments: List<IrModuleFragment>,
|
||||
diagnosticReporter: DiagnosticReporter,
|
||||
typeSystemContext: IrTypeSystemContext,
|
||||
languageVersionSettings: LanguageVersionSettings
|
||||
): 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 = ExpectActualCollector(
|
||||
val (expectActualMap, actualDeclarations) = ExpectActualCollector(
|
||||
mainFragment,
|
||||
dependentFragments,
|
||||
typeSystemContext,
|
||||
ktDiagnosticReporter
|
||||
).collect()
|
||||
|
||||
@@ -44,7 +47,9 @@ object IrActualizer {
|
||||
// taken from these non-expect classes actualized super classes.
|
||||
ActualFakeOverridesAdder(
|
||||
expectActualMap,
|
||||
ktDiagnosticReporter
|
||||
actualDeclarations.actualClasses,
|
||||
ktDiagnosticReporter,
|
||||
typeSystemContext
|
||||
).apply { dependentFragments.forEach { visitModuleFragment(it) } }
|
||||
|
||||
// 5. Copy and actualize function parameter default values from expect functions
|
||||
@@ -91,4 +96,4 @@ object IrActualizer {
|
||||
private fun mergeIrFragments(mainFragment: IrModuleFragment, dependentFragments: List<IrModuleFragment>) {
|
||||
mainFragment.files.addAll(0, dependentFragments.flatMap { it.files })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+83
-161
@@ -13,182 +13,89 @@ 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.symbols.IrClassSymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.IrPropertySymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.IrSymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol
|
||||
import org.jetbrains.kotlin.ir.types.IrDynamicType
|
||||
import org.jetbrains.kotlin.ir.types.classifierOrFail
|
||||
import org.jetbrains.kotlin.ir.types.isNullable
|
||||
import org.jetbrains.kotlin.ir.types.IrTypeSystemContext
|
||||
import org.jetbrains.kotlin.ir.util.*
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.StandardClassIds
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
import org.jetbrains.kotlin.resolve.calls.mpp.AbstractExpectActualCompatibilityChecker
|
||||
import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility
|
||||
import org.jetbrains.kotlin.resolve.multiplatform.OptionalAnnotationUtil
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.runIf
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.shouldNotBeCalled
|
||||
|
||||
private val FLEXIBLE_NULLABILITY_ANNOTATION_FQ_NAME = StandardClassIds.Annotations.FlexibleNullability.asSingleFqName()
|
||||
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()
|
||||
}
|
||||
|
||||
internal fun Map<String, List<IrDeclaration>>.getMatches(
|
||||
expectDeclaration: IrDeclaration,
|
||||
expectActualTypesMap: Map<IrSymbol, IrSymbol>,
|
||||
expectActualTypeAliasMap: Map<FqName, FqName>
|
||||
): List<IrDeclaration> =
|
||||
getMatches(generateIrElementFullNameFromExpect(expectDeclaration, expectActualTypeAliasMap), expectDeclaration, expectActualTypesMap)
|
||||
|
||||
internal fun Map<String, List<IrDeclaration>>.getMatches(
|
||||
mainSignature: String,
|
||||
expectDeclaration: IrDeclaration,
|
||||
expectActualTypesMap: Map<IrSymbol, IrSymbol>
|
||||
): List<IrDeclaration> {
|
||||
val members = this[mainSignature] ?: return emptyList()
|
||||
return when (expectDeclaration) {
|
||||
is IrFunction -> members.getMatches(expectDeclaration, expectActualTypesMap) { it as IrFunction }
|
||||
is IrProperty -> members.getMatches(expectDeclaration, expectActualTypesMap) { (it as IrProperty).getter!! }
|
||||
else -> members
|
||||
override fun onMatchedCallables(expectSymbol: IrSymbol, actualSymbol: IrSymbol) {
|
||||
require(expectSymbol == baseExpectSymbol)
|
||||
matchingActuals += actualSymbol
|
||||
}
|
||||
}
|
||||
AbstractExpectActualCompatibilityChecker.matchSingleExpectTopLevelDeclarationAgainstPotentialActuals(
|
||||
expectSymbol,
|
||||
actualSymbols,
|
||||
context
|
||||
)
|
||||
return matchingActuals
|
||||
}
|
||||
|
||||
private inline fun List<IrDeclaration>.getMatches(
|
||||
expect: IrDeclaration,
|
||||
expectActualTypesMap: Map<IrSymbol, IrSymbol>,
|
||||
functionExtractor: (IrDeclaration) -> IrFunction
|
||||
): List<IrDeclaration> {
|
||||
val expectFunction = functionExtractor(expect)
|
||||
return filter { expectFunction.match(functionExtractor(it), expectActualTypesMap) }
|
||||
}
|
||||
|
||||
private fun IrFunction.match(actualFunction: IrFunction, expectActualTypesMap: Map<IrSymbol, IrSymbol>): Boolean {
|
||||
fun getActualizedTypeClassifierSymbol(
|
||||
expectParameter: IrValueParameter,
|
||||
localTypeParametersMap: Map<IrTypeParameterSymbol, IrTypeParameterSymbol>? = null
|
||||
): IrSymbol {
|
||||
return expectParameter.type.classifierOrFail.let {
|
||||
val localMappedSymbol = if (localTypeParametersMap != null && it is IrTypeParameterSymbol) {
|
||||
localTypeParametersMap[it]
|
||||
} else {
|
||||
null
|
||||
}
|
||||
localMappedSymbol ?: expectActualTypesMap[it] ?: it
|
||||
}
|
||||
}
|
||||
|
||||
fun checkParameter(
|
||||
expectParameter: IrValueParameter?,
|
||||
actualParameter: IrValueParameter?,
|
||||
localTypeParametersMap: Map<IrTypeParameterSymbol, IrTypeParameterSymbol>
|
||||
): Boolean {
|
||||
if (expectParameter == null) {
|
||||
return actualParameter == null
|
||||
}
|
||||
if (actualParameter == null) {
|
||||
return false
|
||||
}
|
||||
|
||||
if (expectParameter.type is IrDynamicType || actualParameter.type is IrDynamicType) {
|
||||
return true
|
||||
}
|
||||
|
||||
if (expectParameter.type.isNullable() != actualParameter.type.isNullable() &&
|
||||
!expectParameter.type.annotations.hasAnnotation(FLEXIBLE_NULLABILITY_ANNOTATION_FQ_NAME) &&
|
||||
!actualParameter.type.annotations.hasAnnotation(FLEXIBLE_NULLABILITY_ANNOTATION_FQ_NAME)
|
||||
) {
|
||||
return false
|
||||
}
|
||||
|
||||
if (getActualizedTypeClassifierSymbol(expectParameter, localTypeParametersMap) !=
|
||||
getActualizedTypeClassifierSymbol(actualParameter)
|
||||
) {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
if (valueParameters.size != actualFunction.valueParameters.size || typeParameters.size != actualFunction.typeParameters.size) {
|
||||
return false
|
||||
}
|
||||
|
||||
val localTypeParametersMap = mutableMapOf<IrTypeParameterSymbol, IrTypeParameterSymbol>()
|
||||
for ((expectTypeParameter, actualTypeParameter) in typeParameters.zip(actualFunction.typeParameters)) {
|
||||
if (expectTypeParameter.name != actualTypeParameter.name) {
|
||||
return false
|
||||
}
|
||||
localTypeParametersMap[expectTypeParameter.symbol] = actualTypeParameter.symbol
|
||||
}
|
||||
|
||||
if (!checkParameter(extensionReceiverParameter, actualFunction.extensionReceiverParameter, localTypeParametersMap)) {
|
||||
return false
|
||||
}
|
||||
|
||||
for ((expectParameter, actualParameter) in valueParameters.zip(actualFunction.valueParameters)) {
|
||||
if (!checkParameter(expectParameter, actualParameter, localTypeParametersMap)) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
internal fun generateActualIrClassOrTypeAliasFullName(declaration: IrElement) = generateIrElementFullNameFromExpect(declaration, emptyMap())
|
||||
|
||||
internal fun generateIrElementFullNameFromExpect(
|
||||
declaration: IrElement,
|
||||
expectActualTypeAliasMap: Map<FqName, FqName>
|
||||
): String {
|
||||
return buildString { appendElementFullName(declaration, this, expectActualTypeAliasMap) }
|
||||
}
|
||||
|
||||
private fun appendElementFullName(
|
||||
declaration: IrElement,
|
||||
result: StringBuilder,
|
||||
expectActualTypeAliasMap: Map<FqName, FqName>
|
||||
internal fun recordActualForExpectDeclaration(
|
||||
expectSymbol: IrSymbol,
|
||||
actualSymbol: IrSymbol,
|
||||
destination: MutableMap<IrSymbol, IrSymbol>,
|
||||
) {
|
||||
if (declaration !is IrDeclarationBase) return
|
||||
|
||||
val parents = mutableListOf<String>()
|
||||
var parent: IrDeclarationParent? = declaration.parent
|
||||
while (parent != null) {
|
||||
if (parent is IrDeclarationWithName) {
|
||||
val parentParent = parent.parent
|
||||
if (parentParent is IrClass) {
|
||||
parents.add(parent.name.asString())
|
||||
parent = parentParent
|
||||
continue
|
||||
}
|
||||
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)
|
||||
}
|
||||
val parentString = parent.kotlinFqName.let { (expectActualTypeAliasMap[it] ?: it).asString() }
|
||||
if (parentString.isNotEmpty()) {
|
||||
parents.add(parentString)
|
||||
}
|
||||
parent = null
|
||||
}
|
||||
|
||||
if (parents.isNotEmpty()) {
|
||||
result.append(parents.asReversed().joinToString(separator = "."))
|
||||
result.append('.')
|
||||
}
|
||||
|
||||
if (declaration is IrDeclarationWithName) {
|
||||
result.append(declaration.name)
|
||||
}
|
||||
|
||||
if (declaration is IrFunction) {
|
||||
result.append("()")
|
||||
expectDeclaration.setter?.symbol?.let { destination[it] = actualProperty.setter!!.symbol }
|
||||
}
|
||||
}
|
||||
|
||||
internal fun MutableMap<IrSymbol, IrSymbol>.addLink(expectMember: IrDeclarationBase, actualMember: IrDeclaration) {
|
||||
this[expectMember.symbol] = actualMember.symbol
|
||||
if (expectMember is IrProperty) {
|
||||
val actualProperty = actualMember as IrProperty
|
||||
expectMember.getter!!.let {
|
||||
val getter = actualProperty.getter!!
|
||||
this[it.symbol] = getter.symbol
|
||||
this.appendTypeParametersMap(it, getter)
|
||||
private fun recordTypeParametersMapping(
|
||||
destination: MutableMap<IrSymbol, IrSymbol>,
|
||||
expectTypeParametersContainer: IrTypeParametersContainer,
|
||||
actualTypeParametersContainer: IrTypeParametersContainer
|
||||
) {
|
||||
expectTypeParametersContainer.typeParameters
|
||||
.zip(actualTypeParametersContainer.typeParameters)
|
||||
.forEach { (expectTypeParameter, actualTypeParameter) ->
|
||||
destination[expectTypeParameter.symbol] = actualTypeParameter.symbol
|
||||
}
|
||||
expectMember.setter?.symbol?.let { this[it] = actualProperty.setter!!.symbol }
|
||||
} else if (expectMember is IrFunction) {
|
||||
this.appendTypeParametersMap(expectMember, actualMember as IrFunction)
|
||||
}
|
||||
}
|
||||
|
||||
internal fun KtDiagnosticReporterWithImplicitIrBasedContext.reportMissingActual(expectSymbol: IrSymbol) {
|
||||
reportMissingActual(expectSymbol.owner as IrDeclaration)
|
||||
}
|
||||
|
||||
@OptIn(ObsoleteDescriptorBasedAPI::class)
|
||||
@@ -200,6 +107,21 @@ internal fun KtDiagnosticReporterWithImplicitIrBasedContext.reportMissingActual(
|
||||
)
|
||||
}
|
||||
|
||||
internal fun KtDiagnosticReporterWithImplicitIrBasedContext.reportIncompatibleExpectActual(
|
||||
expectSymbol: IrSymbol,
|
||||
actualSymbol: IrSymbol,
|
||||
incompatibility: ExpectActualCompatibility.Incompatible<*>
|
||||
) {
|
||||
val expectDeclaration = expectSymbol.owner as IrDeclaration
|
||||
val actualDeclaration = actualSymbol.owner as IrDeclaration
|
||||
at(expectDeclaration).report(
|
||||
CommonBackendErrors.INCOMPATIBLE_MATCHING,
|
||||
expectDeclaration.getNameWithAssert().asString(),
|
||||
actualDeclaration.getNameWithAssert().asString(),
|
||||
incompatibility
|
||||
)
|
||||
}
|
||||
|
||||
internal fun IrElement.containsOptionalExpectation(): Boolean {
|
||||
return this is IrClass &&
|
||||
this.kind == ClassKind.ANNOTATION_CLASS &&
|
||||
@@ -268,4 +190,4 @@ private fun createFakeOverrideFunction(
|
||||
it.attributeOwnerId = it
|
||||
it.correspondingPropertySymbol = correspondingPropertySymbol
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+401
@@ -0,0 +1,401 @@
|
||||
/*
|
||||
* 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.descriptors.ClassKind
|
||||
import org.jetbrains.kotlin.descriptors.Modality
|
||||
import org.jetbrains.kotlin.descriptors.Visibilities
|
||||
import org.jetbrains.kotlin.descriptors.Visibility
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
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.ExpectActualMatchingContext
|
||||
import org.jetbrains.kotlin.types.AbstractTypeChecker
|
||||
import org.jetbrains.kotlin.types.Variance
|
||||
import org.jetbrains.kotlin.types.model.*
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.UnsafeCastFunction
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.castAll
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.shouldNotBeCalled
|
||||
|
||||
internal abstract class IrExpectActualMatchingContext(
|
||||
val typeContext: IrTypeSystemContext,
|
||||
val expectToActualClassMap: Map<ClassId, IrClassSymbol>
|
||||
) : ExpectActualMatchingContext<IrSymbol>, TypeSystemContext by typeContext {
|
||||
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 shouldCheckReturnTypesOfCallables: Boolean
|
||||
get() = true
|
||||
|
||||
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<KotlinTypeMarker>
|
||||
get() = asIr().superTypes
|
||||
|
||||
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.setter: FunctionSymbolMarker?
|
||||
get() = asIr().setter?.symbol
|
||||
|
||||
@OptIn(UnsafeCastFunction::class)
|
||||
override fun createExpectActualTypeParameterSubstitutor(
|
||||
expectTypeParameters: List<TypeParameterSymbolMarker>,
|
||||
actualTypeParameters: List<TypeParameterSymbolMarker>,
|
||||
parentSubstitutor: TypeSubstitutorMarker?,
|
||||
): TypeSubstitutorMarker {
|
||||
val expectParameters = expectTypeParameters.castAll<IrTypeParameterSymbol>()
|
||||
val actualParameters = actualTypeParameters.castAll<IrTypeParameterSymbol>()
|
||||
val actualTypes = actualParameters.map { it.owner.defaultType }
|
||||
val substitutor = IrTypeSubstitutor(expectParameters, actualTypes, typeContext.irBuiltIns, 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 val CallableSymbolMarker.dispatchReceiverType: KotlinTypeMarker?
|
||||
get() = (asIr().parent as? IrClass)?.defaultType
|
||||
|
||||
override val CallableSymbolMarker.extensionReceiverType: KotlinTypeMarker?
|
||||
get() = safeAsIr<IrFunction>()?.extensionReceiverParameter?.type
|
||||
|
||||
override val CallableSymbolMarker.returnType: KotlinTypeMarker
|
||||
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.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 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<KotlinTypeMarker>
|
||||
get() = asIr().superTypes
|
||||
override val TypeParameterSymbolMarker.variance: Variance
|
||||
get() = asIr().variance
|
||||
override val TypeParameterSymbolMarker.isReified: Boolean
|
||||
get() = asIr().isReified
|
||||
|
||||
override fun areCompatibleExpectActualTypes(expectType: KotlinTypeMarker?, actualType: KotlinTypeMarker?): 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 = actualizingSubstitutor.substitute(expectType as IrType)
|
||||
val actualizedActualType = actualizingSubstitutor.substitute(actualType as IrType)
|
||||
return AbstractTypeChecker.equalTypes(
|
||||
typeContext.newTypeCheckerState(errorTypesEqualToAnything = true, stubTypesEqualToAnything = false),
|
||||
actualizedExpectType,
|
||||
actualizedActualType
|
||||
)
|
||||
}
|
||||
|
||||
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 {
|
||||
// TODO: is it correct for java classes?
|
||||
return !asIr().isFun
|
||||
}
|
||||
|
||||
override fun CallableSymbolMarker.shouldSkipMatching(containingExpectClass: RegularClassSymbolMarker): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override val CallableSymbolMarker.hasStableParameterNames: Boolean
|
||||
get() = when (asIr().origin) {
|
||||
IrDeclarationOrigin.IR_EXTERNAL_DECLARATION_STUB,
|
||||
IrDeclarationOrigin.IR_EXTERNAL_JAVA_DECLARATION_STUB,
|
||||
-> false
|
||||
else -> true
|
||||
}
|
||||
|
||||
override fun onMatchedMembers(expectSymbol: DeclarationSymbolMarker, actualSymbol: DeclarationSymbolMarker) {
|
||||
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)
|
||||
}
|
||||
Reference in New Issue
Block a user