diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/actualizer/ExpectActualCollector.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/actualizer/ExpectActualCollector.kt index 8a9c86379c7..741d26c7d0f 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/actualizer/ExpectActualCollector.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/actualizer/ExpectActualCollector.kt @@ -6,15 +6,12 @@ package org.jetbrains.kotlin.backend.common.actualizer import org.jetbrains.kotlin.KtDiagnosticReporterWithImplicitIrBasedContext -import org.jetbrains.kotlin.backend.common.ir.isExpect -import org.jetbrains.kotlin.backend.common.ir.isProperExpect 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.classifierOrFail import org.jetbrains.kotlin.ir.util.kotlinFqName -import org.jetbrains.kotlin.ir.visitors.IrElementVisitor import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid import org.jetbrains.kotlin.name.FqName @@ -26,110 +23,119 @@ internal class ExpectActualCollector( ) { fun collect(): Pair, Map> { val result = mutableMapOf() - // Collect and link classifiers at first to make it possible to expand type aliases on the callables linking - val (allActualDeclarations, typeAliasMap) = result.appendExpectActualClassifiersMap() - result.appendExpectActualCallablesMap(allActualDeclarations, typeAliasMap) - return result to typeAliasMap + // 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 to expectActualTypeAliasMap } - private fun MutableMap.appendExpectActualClassifiersMap(): Pair, Map> { - val actualClassifiers = mutableMapOf() + private fun MutableMap.appendExpectActualClassMap(): Pair, Map> { + val actualClasses = mutableMapOf() // There is no list for builtins declarations; that's why they are being collected from typealiases - val allActualDeclarations = mutableSetOf() - val typeAliasMap = mutableMapOf() // It's used to link members from expect class that have typealias actual + val actualMembers = mutableListOf() + val expectActualTypeAliasMap = mutableMapOf() // It's used to link members from expect class that have typealias actual - val fragmentsWithActuals = (dependentFragments.drop(1) + mainFragment) - for (fragment in fragmentsWithActuals) { - ActualClassifiersCollector(actualClassifiers, allActualDeclarations, typeAliasMap).visitModuleFragment(fragment, false) - } + val fragmentsWithActuals = dependentFragments.drop(1) + mainFragment + val actualClassesAndMembersCollector = ActualClassesAndMembersCollector(actualClasses, actualMembers, expectActualTypeAliasMap) + fragmentsWithActuals.forEach { actualClassesAndMembersCollector.collect(it) } - val linkCollector = ClassifiersLinkCollector(this, actualClassifiers, typeAliasMap, diagnosticsReporter) + val linkCollector = ClassLinksCollector(this, actualClasses, expectActualTypeAliasMap, diagnosticsReporter) dependentFragments.forEach { linkCollector.visitModuleFragment(it) } - return allActualDeclarations to typeAliasMap + return actualMembers to expectActualTypeAliasMap } - private fun MutableMap.appendExpectActualCallablesMap( - allActualDeclarations: Set, - typeAliasMap: Map + private fun MutableMap.appendExpectActualMemberMap( + actualMembers: List, + expectActualTypeAliasMap: Map ) { - val actualMembers = mutableMapOf() - - collectActualCallables(this, actualMembers, typeAliasMap, allActualDeclarations) - val collector = CallablesLinkCollector(this, actualMembers, typeAliasMap, diagnosticsReporter) + val actualMembersMap = mutableMapOf>() + for (actualMember in actualMembers) { + actualMembersMap.getOrPut(generateIrElementFullNameFromExpect(actualMember, expectActualTypeAliasMap)) { mutableListOf() } + .add(actualMember) + } + val collector = MemberLinksCollector(this, actualMembersMap, expectActualTypeAliasMap, diagnosticsReporter) dependentFragments.forEach { collector.visitModuleFragment(it) } } } -private class ActualClassifiersCollector( - private val actualClassifiers: MutableMap, - private val allActualClassifiers: MutableSet, - private val typeAliasMap: MutableMap -) : IrElementVisitor { - override fun visitTypeAlias(declaration: IrTypeAlias, data: Boolean) { - if (declaration.isActual) { - val expandedTypeSymbol = declaration.expandedType.classifierOrFail - actualClassifiers[generateIrElementFullName(declaration)] = expandedTypeSymbol - if (expandedTypeSymbol is IrClassSymbol) { - allActualClassifiers.add(expandedTypeSymbol.owner) - typeAliasMap[declaration.kotlinFqName] = expandedTypeSymbol.owner.kotlinFqName +private class ActualClassesAndMembersCollector( + private val actualClasses: MutableMap, + private val actualMembers: MutableList, + private val expectActualTypeAliasMap: MutableMap +) { + private val visitedActualClasses = mutableSetOf() + + fun collect(element: IrElement) { + when (element) { + is IrModuleFragment -> { + for (file in element.files) { + collect(file) + } + } + is IrTypeAlias -> { + if (!element.isActual) return + + val expandedTypeSymbol = element.expandedType.classifierOrFail as IrClassSymbol + addActualClass(element, expandedTypeSymbol) + collect(expandedTypeSymbol.owner) + + expectActualTypeAliasMap[element.kotlinFqName] = expandedTypeSymbol.owner.kotlinFqName + } + is IrClass -> { + if (element.isExpect || !visitedActualClasses.add(element)) return + + addActualClass(element, element.symbol) + for (declaration in element.declarations) { + collect(declaration) + } + } + is IrDeclarationContainer -> { + for (declaration in element.declarations) { + collect(declaration) + } + } + is IrEnumEntry -> { + actualMembers.add(element) // If enum entry is located inside expect enum, then this code is not executed + } + is IrProperty -> { + if (element.isExpect) return + actualMembers.add(element) + } + is IrFunction -> { + if (element.isExpect) return + actualMembers.add(element) } } - visitElement(declaration, data) } - override fun visitClass(declaration: IrClass, data: Boolean) { - if (!data && !declaration.isExpect) { - actualClassifiers[generateIrElementFullName(declaration)] = declaration.symbol - } - visitDeclaration(declaration, data) - } - - override fun visitEnumEntry(declaration: IrEnumEntry, data: Boolean) { - if (!data && !declaration.isExpect) { - actualClassifiers[generateIrElementFullName(declaration)] = declaration.symbol - } - visitDeclaration(declaration, data) - } - - override fun visitDeclaration(declaration: IrDeclarationBase, data: Boolean) { - if (!data && !declaration.isExpect) { - allActualClassifiers.add(declaration) - } - visitElement(declaration, data || declaration.isExpect) - } - - override fun visitElement(element: IrElement, data: Boolean) { - element.acceptChildren(this, data) + private fun addActualClass(classOrTypeAlias: IrElement, classSymbol: IrClassSymbol) { + actualClasses[generateActualIrClassOrTypeAliasFullName(classOrTypeAlias)] = classSymbol } } -private class ClassifiersLinkCollector( +private class ClassLinksCollector( private val expectActualMap: MutableMap, - private val actualClassifiers: Map, - private val typeAliasMap: Map, + private val actualClasses: Map, + private val expectActualTypeAliasMap: Map, private val diagnosticsReporter: KtDiagnosticReporterWithImplicitIrBasedContext ) : IrElementVisitorVoid { - private fun addLinkOrReportMissing(expectElement: IrDeclaration) { - val actualClassifier = actualClassifiers[generateIrElementFullName(expectElement, expectActualMap, typeAliasMap)] - if (actualClassifier != null) { - expectActualMap[expectElement.symbol] = actualClassifier - } else if (!expectElement.containsOptionalExpectation()) { - diagnosticsReporter.reportMissingActual(expectElement) - } - } - override fun visitClass(declaration: IrClass) { - if (declaration.isExpect) { - addLinkOrReportMissing(declaration) - } - visitElement(declaration) - } + if (!declaration.isExpect) return - override fun visitEnumEntry(declaration: IrEnumEntry) { - if (declaration.isProperExpect) { - addLinkOrReportMissing(declaration) + val actualClassSymbol = actualClasses[generateIrElementFullNameFromExpect(declaration, expectActualTypeAliasMap)] + if (actualClassSymbol != null) { + expectActualMap[declaration.symbol] = actualClassSymbol + val actualClass = actualClassSymbol.owner + for (expectTypeParameter in declaration.typeParameters) { + actualClass.typeParameters.firstOrNull { it.name == expectTypeParameter.name }?.let { actualTypeParameter -> + expectActualMap[expectTypeParameter.symbol] = actualTypeParameter.symbol + } + } + } else if (!declaration.containsOptionalExpectation()) { + diagnosticsReporter.reportMissingActual(declaration) } + visitElement(declaration) } @@ -138,44 +144,26 @@ private class ClassifiersLinkCollector( } } -private fun collectActualCallables( - expectActualMap: MutableMap, - actualMembers: MutableMap, - typeAliasMap: Map, - allActualDeclarations: Set, -) { - fun collectActualsCallables(declaration: IrDeclaration) { - when (declaration) { - is IrFunction, - is IrProperty -> { - actualMembers[generateIrElementFullName(declaration, expectActualMap, typeAliasMap)] = declaration as IrDeclarationBase - } - is IrClass -> { - for (member in declaration.declarations) { - collectActualsCallables(member) - } - } - } - } - - for (actualDeclaration in allActualDeclarations) { - collectActualsCallables(actualDeclaration) - } -} - -private class CallablesLinkCollector( +private class MemberLinksCollector( private val expectActualMap: MutableMap, - private val actualMembers: Map, + private val actualMembers: Map>, private val typeAliasMap: Map, private val diagnosticsReporter: KtDiagnosticReporterWithImplicitIrBasedContext ) : IrElementVisitorVoid { - override fun visitFunction(declaration: IrFunction) = addLink(declaration) + override fun visitFunction(declaration: IrFunction) { + if (declaration.isExpect) addLink(declaration) + } - override fun visitProperty(declaration: IrProperty) = addLink(declaration) + override fun visitProperty(declaration: IrProperty) { + if (declaration.isExpect) addLink(declaration) + } + + override fun visitEnumEntry(declaration: IrEnumEntry) { + if ((declaration.parent as IrClass).isExpect) addLink(declaration) + } private fun addLink(declaration: IrDeclarationBase) { - if (!declaration.isExpect) return - val actualMember = actualMembers[generateIrElementFullName(declaration, expectActualMap, typeAliasMap)] + val actualMember = actualMembers.getMatch(declaration, expectActualMap, typeAliasMap) if (actualMember != null) { expectActualMap[declaration.symbol] = actualMember.symbol if (declaration is IrProperty) { diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/actualizer/IrActualizer.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/actualizer/IrActualizer.kt index f3495b11bcf..6dfc6dda036 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/actualizer/IrActualizer.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/actualizer/IrActualizer.kt @@ -20,10 +20,10 @@ object IrActualizer { languageVersionSettings: LanguageVersionSettings ) { val ktDiagnosticReporter = KtDiagnosticReporterWithImplicitIrBasedContext(diagnosticReporter, languageVersionSettings) - val (expectActualMap, typeAliasMap) = ExpectActualCollector(mainFragment, dependentFragments, ktDiagnosticReporter).collect() + val (expectActualMap, expectActualTypeAliasMap) = ExpectActualCollector(mainFragment, dependentFragments, ktDiagnosticReporter).collect() FunctionDefaultParametersActualizer(expectActualMap).actualize() removeExpectDeclarations(dependentFragments, expectActualMap) - addMissingFakeOverrides(expectActualMap, dependentFragments, typeAliasMap, ktDiagnosticReporter) + addMissingFakeOverrides(expectActualMap, dependentFragments, expectActualTypeAliasMap, ktDiagnosticReporter) linkExpectToActual(expectActualMap, dependentFragments) mergeIrFragments(mainFragment, dependentFragments) } @@ -48,10 +48,14 @@ object IrActualizer { private fun addMissingFakeOverrides( expectActualMap: Map, dependentFragments: List, - typeAliasMap: Map, + expectActualTypeAliasMap: Map, diagnosticsReporter: KtDiagnosticReporterWithImplicitIrBasedContext ) { - MissingFakeOverridesAdder(expectActualMap, typeAliasMap, diagnosticsReporter).apply { dependentFragments.forEach { visitModuleFragment(it) } } + MissingFakeOverridesAdder( + expectActualMap, + expectActualTypeAliasMap, + diagnosticsReporter + ).apply { dependentFragments.forEach { visitModuleFragment(it) } } } private fun linkExpectToActual(expectActualMap: Map, dependentFragments: List) { diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/actualizer/IrActualizerUtils.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/actualizer/IrActualizerUtils.kt index f47979040cd..53fbbabe657 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/actualizer/IrActualizerUtils.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/actualizer/IrActualizerUtils.kt @@ -12,27 +12,95 @@ import org.jetbrains.kotlin.ir.IrElement import org.jetbrains.kotlin.ir.ObsoleteDescriptorBasedAPI import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.symbols.IrSymbol -import org.jetbrains.kotlin.ir.types.IrType +import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol import org.jetbrains.kotlin.ir.types.classifierOrFail import org.jetbrains.kotlin.ir.util.hasAnnotation import org.jetbrains.kotlin.ir.util.kotlinFqName import org.jetbrains.kotlin.ir.util.module import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.resolve.multiplatform.OptionalAnnotationUtil +import org.jetbrains.kotlin.utils.addToStdlib.runIf -fun generateIrElementFullName( +fun Map>.getMatch( + expectDeclaration: IrDeclaration, + expectActualTypesMap: Map, + expectActualTypeAliasMap: Map +): IrDeclaration? { + val members = this[generateIrElementFullNameFromExpect(expectDeclaration, expectActualTypeAliasMap)] ?: return null + return if (expectDeclaration is IrFunction) { + members.firstNotNullOfOrNull { runIf(expectDeclaration.match(it as IrFunction, expectActualTypesMap)) { it } } + } else { + members.singleOrNull() + } +} + +private fun IrFunction.match(actualFunction: IrFunction, expectActualTypesMap: Map): Boolean { + fun checkParameter( + expectParameter: IrValueParameter?, + actualParameter: IrValueParameter?, + typeParametersMap: Map + ): Boolean { + if (expectParameter == null) { + return actualParameter == null + } + if (actualParameter == null) { + return false + } + + val actualizedParameterTypeSymbol = expectParameter.type.classifierOrFail.let { + var mappedSymbol: IrSymbol? = null + if (it is IrTypeParameterSymbol) { + mappedSymbol = typeParametersMap[it] + } + if (mappedSymbol == null) { + mappedSymbol = expectActualTypesMap[it] ?: it + } + mappedSymbol + } + if (actualizedParameterTypeSymbol != actualParameter.type.classifierOrFail) { + return false + } + return true + } + + if (valueParameters.size != actualFunction.valueParameters.size || typeParameters.size != actualFunction.typeParameters.size) { + return false + } + + val typeParametersMap = mutableMapOf() + for ((expectTypeParameter, actualTypeParameter) in typeParameters.zip(actualFunction.typeParameters)) { + if (expectTypeParameter.name != actualTypeParameter.name) { + return false + } + typeParametersMap[expectTypeParameter.symbol] = actualTypeParameter.symbol + } + + if (!checkParameter(extensionReceiverParameter, actualFunction.extensionReceiverParameter, typeParametersMap)) { + return false + } + + for ((expectParameter, actualParameter) in valueParameters.zip(actualFunction.valueParameters)) { + if (!checkParameter(expectParameter, actualParameter, typeParametersMap)) { + return false + } + } + + return true +} + +fun generateActualIrClassOrTypeAliasFullName(declaration: IrElement) = generateIrElementFullNameFromExpect(declaration, emptyMap()) + +fun generateIrElementFullNameFromExpect( declaration: IrElement, - expectActualTypesMap: Map? = null, - typeAliasMap: Map? = null + expectActualTypeAliasMap: Map ): String { - return StringBuilder().apply { appendElementFullName(declaration, this, expectActualTypesMap, typeAliasMap) }.toString() + return StringBuilder().apply { appendElementFullName(declaration, this, expectActualTypeAliasMap) }.toString() } private fun appendElementFullName( declaration: IrElement, result: StringBuilder, - expectActualTypesMap: Map? = null, - expectActualTypeAliasMap: Map? = null + expectActualTypeAliasMap: Map ) { if (declaration !is IrDeclarationBase) return @@ -47,7 +115,7 @@ private fun appendElementFullName( continue } } - val parentString = parent.kotlinFqName.let { (expectActualTypeAliasMap?.get(it) ?: it).asString() } + val parentString = parent.kotlinFqName.let { (expectActualTypeAliasMap[it] ?: it).asString() } if (parentString.isNotEmpty()) { parents.add(parentString) } @@ -64,27 +132,7 @@ private fun appendElementFullName( } if (declaration is IrFunction) { - fun appendType(type: IrType) { - val typeClassifier = type.classifierOrFail - val actualizedTypeSymbol = expectActualTypesMap?.get(typeClassifier) ?: typeClassifier - appendElementFullName(actualizedTypeSymbol.owner, result, expectActualTypesMap) - } - - val extensionReceiverType = declaration.extensionReceiverParameter?.type - if (extensionReceiverType != null) { - result.append('[') - appendType(extensionReceiverType) - result.append(']') - } - - result.append('(') - for ((index, parameter) in declaration.valueParameters.withIndex()) { - appendType(parameter.type) - if (index < declaration.valueParameters.size - 1) { - result.append(',') - } - } - result.append(')') + result.append("()") } } diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/actualizer/MissingFakeOverridesAdder.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/actualizer/MissingFakeOverridesAdder.kt index 59492b231a3..09620da67c6 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/actualizer/MissingFakeOverridesAdder.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/actualizer/MissingFakeOverridesAdder.kt @@ -39,9 +39,12 @@ class MissingFakeOverridesAdder( private fun processSupertypes(declaration: IrClass) { val members by lazy(LazyThreadSafetyMode.NONE) { - declaration.declarations.filter { !it.isBuiltinMember() }.associateBy { - generateIrElementFullName(it, expectActualMap, typeAliasMap) + val notBuiltinMembers = declaration.declarations.filter { !it.isBuiltinMember() } + val result = mutableMapOf>() + for (member in notBuiltinMembers) { + result.getOrPut(generateIrElementFullNameFromExpect(member, typeAliasMap)) { mutableListOf() }.add(member) } + result } for (superType in declaration.superTypes) { @@ -68,15 +71,11 @@ class MissingFakeOverridesAdder( } } - private fun addFakeOverride( - actualMember: IrDeclaration, - members: Map, - declaration: IrClass - ) { + private fun addFakeOverride(actualMember: IrDeclaration, members: Map>, declaration: IrClass) { when (actualMember) { is IrFunctionImpl, is IrPropertyImpl -> { - if (members[generateIrElementFullName(actualMember, expectActualMap, typeAliasMap)] != null) { + if (members.getMatch(actualMember, expectActualMap, typeAliasMap) != null) { diagnosticsReporter.reportManyInterfacesMembersNotImplemented(declaration, actualMember as IrDeclarationWithName) return } diff --git a/compiler/testData/diagnostics/tests/multiplatform/enum/javaEnum.fir.kt b/compiler/testData/diagnostics/tests/multiplatform/enum/javaEnum.fir.kt index 4c20547e9b6..4fb63ae58e1 100644 --- a/compiler/testData/diagnostics/tests/multiplatform/enum/javaEnum.fir.kt +++ b/compiler/testData/diagnostics/tests/multiplatform/enum/javaEnum.fir.kt @@ -2,7 +2,7 @@ // FILE: common.kt expect enum class Foo { - ENTRY + ENTRY } expect enum class _TimeUnit diff --git a/compiler/testData/diagnostics/tests/multiplatform/java/flexibleTypes.fir.kt b/compiler/testData/diagnostics/tests/multiplatform/java/flexibleTypes.fir.kt index a022c3e79db..9580ce80604 100644 --- a/compiler/testData/diagnostics/tests/multiplatform/java/flexibleTypes.fir.kt +++ b/compiler/testData/diagnostics/tests/multiplatform/java/flexibleTypes.fir.kt @@ -8,7 +8,7 @@ expect class Foo { fun f2(s: List?): MutableMap - fun > f3(t: T): T? + fun > f3(t: T): T? } // MODULE: m2-jvm()()(m1-common)