From ba41e8ec3885b34cd18e2d41d65dd3baf66278a4 Mon Sep 17 00:00:00 2001 From: Dmitriy Novozhilov Date: Tue, 6 Jun 2023 17:27:03 +0300 Subject: [PATCH] [IR] Use common expect/actual matching algorithm in IR actualizer ^KT-58578 Fixed --- .../kotlin/cli/js/klib/compilerPipeline.kt | 3 +- .../kotlin/fir/pipeline/convertToIr.kt | 6 + .../kotlin/diagnostics/DiagnosticReporter.kt | 7 + .../backend/common/CommonBackendErrors.kt | 19 + .../actualizer/ActualFakeOverridesAdder.kt | 84 +++- .../actualizer/ExpectActualCollector.kt | 249 ++++++----- .../actualizer/FakeOverridesActualizer.kt | 4 +- .../backend/common/actualizer/IrActualizer.kt | 11 +- .../common/actualizer/IrActualizerUtils.kt | 244 ++++------- .../IrExpectActualMatchingContext.kt | 401 ++++++++++++++++++ .../jetbrains/kotlin/ir/symbols/IrSymbol.kt | 26 +- .../kotlin/ir/types/IrTypeSubstitutor.kt | 24 +- .../org/jetbrains/kotlin/ir/types/irTypes.kt | 2 +- .../kotlin/ir/util/AdditionalIrUtils.kt | 42 +- .../ir/util/DeepCopyIrTreeWithSymbols.kt | 2 +- ...bstractExpectActualCompatibilityChecker.kt | 108 +++-- .../calls/mpp/ExpectActualMatchingContext.kt | 25 +- .../box/multiplatform/callToJavaSuper.kt | 1 + ...methodDefaultArgsViaActualTypealias.fir.kt | 49 +++ .../methodDefaultArgsViaActualTypealias.kt | 1 - .../tests/multiplatform/enum/javaEnum.fir.kt | 34 ++ .../tests/multiplatform/enum/javaEnum.kt | 1 - .../multiplatform/enum/simpleEnum.fir.kt | 17 + .../tests/multiplatform/enum/simpleEnum.kt | 1 - .../expectInterfaceApplicability.fir.kt | 8 +- ...erBoundsDifferentOrderActualMissing.fir.kt | 2 +- ...faultValuesInAnnotationViaTypealias.fir.kt | 2 +- ...ctualClassWithDifferentConstructors.fir.kt | 6 +- ...larationWithStrongIncompatibilities.fir.kt | 6 +- .../headerClass/expectFunInterface.fir.kt | 8 +- .../genericClassImplTypeAlias.fir.kt | 37 ++ .../headerClass/genericClassImplTypeAlias.kt | 1 - .../morePermissiveVisibilityOnActual.fir.kt | 12 +- .../nestedClassesWithErrors.fir.kt | 4 +- .../headerClass/noImplKeywordOnMember.fir.kt | 6 +- .../headerClass/superClass.fir.kt | 2 +- .../headerFunInNonHeaderClass.fir.kt | 2 +- .../tests/multiplatform/hmpp/kt57320.fir.kt | 4 +- .../expectAndActualInTheSameModule.fir.kt | 8 +- ...ualInTheSameModuleIncompatibilities.fir.kt | 4 +- .../multiplatform/java/flexibleTypes.fir.kt | 31 -- .../tests/multiplatform/java/flexibleTypes.kt | 1 + .../tests/multiplatform/kt54827.fir.kt | 4 +- .../topLevelFun/inlineFun.fir.kt | 2 +- .../valueParameterModifiers.fir.kt | 8 +- .../multiplatform/varSetterVisibility.fir.kt | 18 +- .../test/backend/ir/ActualizerOnlyFacade.kt | 11 +- .../test/backend/ir/JvmIrBackendFacade.kt | 3 + .../kotlin/mpp/DeclarationSymbolMarkers.kt | 2 + .../ExpectActualCompatibility.kt | 11 + .../test/converters/FirJsKlibBackendFacade.kt | 4 +- .../jetbrains/kotlin/backend/konan/Fir2Ir.kt | 2 + 52 files changed, 1130 insertions(+), 440 deletions(-) create mode 100644 compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/actualizer/IrExpectActualMatchingContext.kt create mode 100644 compiler/testData/diagnostics/tests/multiplatform/defaultArguments/methodDefaultArgsViaActualTypealias.fir.kt create mode 100644 compiler/testData/diagnostics/tests/multiplatform/enum/javaEnum.fir.kt create mode 100644 compiler/testData/diagnostics/tests/multiplatform/enum/simpleEnum.fir.kt create mode 100644 compiler/testData/diagnostics/tests/multiplatform/headerClass/genericClassImplTypeAlias.fir.kt delete mode 100644 compiler/testData/diagnostics/tests/multiplatform/java/flexibleTypes.fir.kt diff --git a/compiler/cli/cli-js/src/org/jetbrains/kotlin/cli/js/klib/compilerPipeline.kt b/compiler/cli/cli-js/src/org/jetbrains/kotlin/cli/js/klib/compilerPipeline.kt index c933a933c2b..55a6f9f7105 100644 --- a/compiler/cli/cli-js/src/org/jetbrains/kotlin/cli/js/klib/compilerPipeline.kt +++ b/compiler/cli/cli-js/src/org/jetbrains/kotlin/cli/js/klib/compilerPipeline.kt @@ -14,7 +14,6 @@ import org.jetbrains.kotlin.cli.common.fir.FirDiagnosticsCompilerResultsReporter import org.jetbrains.kotlin.cli.common.messages.AnalyzerWithCompilerReport import org.jetbrains.kotlin.cli.common.messages.MessageCollector import org.jetbrains.kotlin.config.CommonConfigurationKeys -import org.jetbrains.kotlin.config.CompilerConfiguration import org.jetbrains.kotlin.config.languageVersionSettings import org.jetbrains.kotlin.constant.EvaluatedConstTracker import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl @@ -36,6 +35,7 @@ import org.jetbrains.kotlin.incremental.js.IncrementalDataProvider import org.jetbrains.kotlin.ir.backend.js.* import org.jetbrains.kotlin.ir.backend.js.lower.serialization.ir.JsManglerDesc import org.jetbrains.kotlin.ir.backend.js.lower.serialization.ir.JsManglerIr +import org.jetbrains.kotlin.ir.types.IrTypeSystemContextImpl import org.jetbrains.kotlin.ir.util.IrMessageLogger import org.jetbrains.kotlin.js.config.JSConfigurationKeys import org.jetbrains.kotlin.js.resolve.JsPlatformAnalyzerServices @@ -217,6 +217,7 @@ fun transformFirToIr( visibilityConverter = Fir2IrVisibilityConverter.Default, kotlinBuiltIns = builtInsModule ?: DefaultBuiltIns.Instance, diagnosticReporter = diagnosticsReporter, + actualizerTypeContextProvider = ::IrTypeSystemContextImpl, fir2IrResultPostCompute = { (this.irModuleFragment.descriptor as? FirModuleDescriptor)?.let { it.allDependencyModules = librariesDescriptors } } diff --git a/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/pipeline/convertToIr.kt b/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/pipeline/convertToIr.kt index 4aa33416a09..32b3711673a 100644 --- a/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/pipeline/convertToIr.kt +++ b/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/pipeline/convertToIr.kt @@ -8,6 +8,7 @@ package org.jetbrains.kotlin.fir.pipeline import org.jetbrains.kotlin.backend.common.actualizer.IrActualizedResult import org.jetbrains.kotlin.backend.common.actualizer.IrActualizer import org.jetbrains.kotlin.backend.common.extensions.IrGenerationExtension +import org.jetbrains.kotlin.backend.jvm.JvmIrTypeSystemContext import org.jetbrains.kotlin.backend.jvm.serialization.JvmIdSignatureDescriptor import org.jetbrains.kotlin.builtins.DefaultBuiltIns import org.jetbrains.kotlin.builtins.KotlinBuiltIns @@ -20,10 +21,12 @@ import org.jetbrains.kotlin.fir.backend.jvm.FirJvmVisibilityConverter import org.jetbrains.kotlin.fir.declarations.FirFile import org.jetbrains.kotlin.fir.resolve.ScopeSession import org.jetbrains.kotlin.fir.signaturer.FirMangler +import org.jetbrains.kotlin.ir.IrBuiltIns import org.jetbrains.kotlin.ir.backend.jvm.serialization.JvmDescriptorMangler import org.jetbrains.kotlin.ir.backend.jvm.serialization.JvmIrMangler import org.jetbrains.kotlin.ir.declarations.IrModuleFragment import org.jetbrains.kotlin.ir.declarations.impl.IrFactoryImpl +import org.jetbrains.kotlin.ir.types.IrTypeSystemContext import org.jetbrains.kotlin.ir.util.IdSignatureComposer import org.jetbrains.kotlin.ir.util.KotlinMangler @@ -57,6 +60,7 @@ fun FirResult.convertToIrAndActualizeForJvm( visibilityConverter = FirJvmVisibilityConverter, diagnosticReporter = diagnosticReporter, kotlinBuiltIns = DefaultBuiltIns.Instance, + actualizerTypeContextProvider = ::JvmIrTypeSystemContext, ) fun signatureComposerForJvmFir2Ir(generateSignatures: Boolean): IdSignatureComposer { @@ -78,6 +82,7 @@ fun FirResult.convertToIrAndActualize( visibilityConverter: Fir2IrVisibilityConverter, kotlinBuiltIns: KotlinBuiltIns, diagnosticReporter: DiagnosticReporter, + actualizerTypeContextProvider: (IrBuiltIns) -> IrTypeSystemContext, fir2IrResultPostCompute: Fir2IrResult.() -> Unit = {}, ): Fir2IrActualizedResult { val fir2IrResult: Fir2IrResult @@ -140,6 +145,7 @@ fun FirResult.convertToIrAndActualize( fir2IrResult.irModuleFragment, commonIrOutputs.map { it.irModuleFragment }, diagnosticReporter, + actualizerTypeContextProvider(fir2IrResult.irModuleFragment.irBuiltins), fir2IrConfiguration.languageVersionSettings ) } diff --git a/compiler/frontend.common/src/org/jetbrains/kotlin/diagnostics/DiagnosticReporter.kt b/compiler/frontend.common/src/org/jetbrains/kotlin/diagnostics/DiagnosticReporter.kt index 55ac3626eb0..901866cbff2 100644 --- a/compiler/frontend.common/src/org/jetbrains/kotlin/diagnostics/DiagnosticReporter.kt +++ b/compiler/frontend.common/src/org/jetbrains/kotlin/diagnostics/DiagnosticReporter.kt @@ -70,6 +70,13 @@ open class KtDiagnosticReporterWithContext( } } + fun report(factory: KtDiagnosticFactory3, a: A, b: B, c: C) { + sourceElement?.let { + reportOn(it, factory, a, b, c, this) + checkAndCommitReportsOn(it, this) + } + } + override fun equals(other: Any?): Boolean { if (this === other) return true if (other !is DiagnosticContextImpl) return false diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/CommonBackendErrors.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/CommonBackendErrors.kt index 44c4e70eee5..7ad75ad7339 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/CommonBackendErrors.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/CommonBackendErrors.kt @@ -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() val MANY_INTERFACES_MEMBER_NOT_IMPLEMENTED by error2() val MANY_IMPL_MEMBER_NOT_IMPLEMENTED by error2() + val INCOMPATIBLE_MATCHING by error3>() 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> { + it.reason ?: "" } } diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/actualizer/ActualFakeOverridesAdder.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/actualizer/ActualFakeOverridesAdder.kt index 96d2b0b5f08..e48f15cd2fe 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/actualizer/ActualFakeOverridesAdder.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/actualizer/ActualFakeOverridesAdder.kt @@ -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, - private val diagnosticsReporter: KtDiagnosticReporterWithImplicitIrBasedContext + private val expectToActualClassMap: Map, + private val diagnosticsReporter: KtDiagnosticReporterWithImplicitIrBasedContext, + private val typeSystemContext: IrTypeSystemContext ) : IrElementVisitorVoid { - private val missingActualMembersMap = mutableMapOf>>() + private val missingActualMembersMap = mutableMapOf() override fun visitClass(declaration: IrClass) { extractMissingActualMembersFromSupertypes(declaration) @@ -40,20 +47,20 @@ internal class ActualFakeOverridesAdder( element.acceptChildrenVoid(this) } - private fun extractMissingActualMembersFromSupertypes(klass: IrClass): Map> { + private fun extractMissingActualMembersFromSupertypes(klass: IrClass): FakeOverrideInfo { missingActualMembersMap[klass]?.let { return it } - val missingActualMembers = mutableMapOf>() + 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>() + 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>.appendMissingMembersToNotExpectClass( + private fun appendMissingMembersToNotExpectClass( + fakeOverrideInfo: FakeOverrideInfo, klass: IrClass, - membersFromSupertype: Map>, - processedMembers: MutableMap> + membersFromSupertype: List, + 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>.appendMissingMembersFromActualizedExpectClass( + private fun FakeOverrideInfo.appendMissingMembersFromActualizedExpectClass( expectClass: IrClass, actualClass: IrClass, ) { @@ -110,11 +130,31 @@ internal class ActualFakeOverridesAdder( } } - private fun MutableMap>.addMember(member: IrOverridableDeclaration<*>) { - getOrPut(member.getFunctionOrPropertyName()) { mutableListOf() }.add(member) - } + private class FakeOverrideInfo { + val functionsByName: MutableMap> = mutableMapOf() + val propertiesByName: MutableMap> = mutableMapOf() - private fun IrOverridableDeclaration<*>.getFunctionOrPropertyName(): String { - return (this as IrDeclarationWithName).name.asString() + (if (this is IrSimpleFunction) "()" else "") + fun allSymbols(): List { + return buildList { + functionsByName.values.flatMapTo(this) { it } + propertiesByName.values.flatMapTo(this) { it } + } + } + + private fun getCorrespondingMap(member: IrOverridableDeclaration<*>): MutableMap> { + 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 { + return getCorrespondingMap(actualMember)[actualMember.name].orEmpty() + } } -} \ No newline at end of file +} 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 d0696009c13..3f4e380daab 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 @@ -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, - private val diagnosticsReporter: KtDiagnosticReporterWithImplicitIrBasedContext + private val typeSystemContext: IrTypeSystemContext, + private val diagnosticsReporter: KtDiagnosticReporterWithImplicitIrBasedContext, ) { - fun collect(): MutableMap { + data class Result(val expectToActualMap: MutableMap, val classActualizationInfo: ClassActualizationInfo) + + fun collect(): Result { val result = mutableMapOf() // 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.appendExpectActualClassMap(): Pair, Map> { - val actualClasses = mutableMapOf() - // There is no list for builtins declarations; that's why they are being collected from typealiases - val actualMembers = mutableListOf() - val expectActualTypeAliasMap = mutableMapOf() // 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.appendExpectActualMemberMap( - actualMembers: List, - expectActualTypeAliasMap: Map + private fun matchAllExpectDeclarations( + destination: MutableMap, + classActualizationInfo: ClassActualizationInfo, ) { - 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) } + val linkCollector = ExpectActualLinkCollector(destination, classActualizationInfo, typeSystemContext, diagnosticsReporter) + dependentFragments.forEach { linkCollector.visitModuleFragment(it) } } } -private class ActualClassesAndMembersCollector( - private val actualClasses: MutableMap, - private val actualMembers: MutableList, - private val expectActualTypeAliasMap: MutableMap +internal data class ClassActualizationInfo( + // mapping from classId of actual class/typealias to itself/typealias expansion + val actualClasses: Map, + // mapping from classId to actual typealias + val actualTypeAliases: Map, + val actualTopLevels: Map>, ) { + fun getActualWithoutExpansion(classId: ClassId): IrSymbol? { + return actualTypeAliases[classId] ?: actualClasses[classId] + } +} + +private class ActualDeclarationsCollector { + companion object { + fun collectActualsFromFragments(fragments: List): ClassActualizationInfo { + val collector = ActualDeclarationsCollector() + for (fragment in fragments) { + collector.collect(fragment) + } + return ClassActualizationInfo( + collector.actualClasses, + collector.actualTypeAliasesWithoutExpansion, + collector.actualTopLevels + ) + } + } + + private val actualClasses: MutableMap = mutableMapOf() + private val actualTypeAliasesWithoutExpansion: MutableMap = mutableMapOf() + private val actualTopLevels: MutableMap> = mutableMapOf() + private val visitedActualClasses = mutableSetOf() - 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, - private val actualClasses: Map, - private val expectActualTypeAliasMap: Map, - private val diagnosticsReporter: KtDiagnosticReporterWithImplicitIrBasedContext +private class ExpectActualLinkCollector( + private val destination: MutableMap, + 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) { + AbstractExpectActualCompatibilityChecker.matchSingleExpectTopLevelDeclarationAgainstPotentialActuals( + expectSymbol, + actualSymbols, + context + ) } override fun visitElement(element: IrElement) { element.acceptChildrenVoid(this) } -} -private class MemberLinksCollector( - private val expectActualMap: MutableMap, - private val actualMembers: Map>, - private val typeAliasMap: Map, - 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, List>, + ) { + 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.appendTypeParametersMap( - expectTypeParametersContainer: IrTypeParametersContainer, - actualTypeParametersContainer: IrTypeParametersContainer -) { - expectTypeParametersContainer.typeParameters.zip(actualTypeParametersContainer.typeParameters) - .forEach { (expectTypeParameter, actualTypeParameter) -> this[expectTypeParameter.symbol] = actualTypeParameter.symbol } -} \ No newline at end of file diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/actualizer/FakeOverridesActualizer.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/actualizer/FakeOverridesActualizer.kt index f1b4887b39b..e96aa67cc92 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/actualizer/FakeOverridesActualizer.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/actualizer/FakeOverridesActualizer.kt @@ -47,11 +47,11 @@ internal class FakeOverridesActualizer(private val expectActualMap: MutableMap) @@ -19,15 +20,17 @@ object IrActualizer { mainFragment: IrModuleFragment, dependentFragments: List, 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) { mainFragment.files.addAll(0, dependentFragments.flatMap { it.files }) } -} \ No newline at end of file +} 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 433643fd18a..44d1459d4c4 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 @@ -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, + expectToActualClassMap: Map, + typeSystemContext: IrTypeSystemContext, +): List { + val baseExpectSymbol = expectSymbol + val matchingActuals = mutableListOf() + val context = object : IrExpectActualMatchingContext(typeSystemContext, expectToActualClassMap) { + override fun onMatchedClasses(expectClassSymbol: IrClassSymbol, actualClassSymbol: IrClassSymbol) { + shouldNotBeCalled() + } -internal fun Map>.getMatches( - expectDeclaration: IrDeclaration, - expectActualTypesMap: Map, - expectActualTypeAliasMap: Map -): List = - getMatches(generateIrElementFullNameFromExpect(expectDeclaration, expectActualTypeAliasMap), expectDeclaration, expectActualTypesMap) - -internal fun Map>.getMatches( - mainSignature: String, - expectDeclaration: IrDeclaration, - expectActualTypesMap: Map -): List { - 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.getMatches( - expect: IrDeclaration, - expectActualTypesMap: Map, - functionExtractor: (IrDeclaration) -> IrFunction -): List { - val expectFunction = functionExtractor(expect) - return filter { expectFunction.match(functionExtractor(it), expectActualTypesMap) } -} - -private fun IrFunction.match(actualFunction: IrFunction, expectActualTypesMap: Map): Boolean { - fun getActualizedTypeClassifierSymbol( - expectParameter: IrValueParameter, - localTypeParametersMap: Map? = 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 - ): 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() - 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 -): String { - return buildString { appendElementFullName(declaration, this, expectActualTypeAliasMap) } -} - -private fun appendElementFullName( - declaration: IrElement, - result: StringBuilder, - expectActualTypeAliasMap: Map +internal fun recordActualForExpectDeclaration( + expectSymbol: IrSymbol, + actualSymbol: IrSymbol, + destination: MutableMap, ) { - if (declaration !is IrDeclarationBase) return - - val parents = mutableListOf() - 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.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, + 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 } -} \ No newline at end of file +} diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/actualizer/IrExpectActualMatchingContext.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/actualizer/IrExpectActualMatchingContext.kt new file mode 100644 index 00000000000..442dcf1f77f --- /dev/null +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/actualizer/IrExpectActualMatchingContext.kt @@ -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 +) : ExpectActualMatchingContext, TypeSystemContext by typeContext { + private inline fun 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 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 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 + 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()!!.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 + 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()?.isExternal ?: false + + override val CallableSymbolMarker.isInfix: Boolean + get() = safeAsIr()?.isInfix ?: false + + override val CallableSymbolMarker.isOperator: Boolean + get() = safeAsIr()?.isOperator ?: false + + override val CallableSymbolMarker.isTailrec: Boolean + get() = safeAsIr()?.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, + actualTypeParameters: List, + parentSubstitutor: TypeSubstitutorMarker?, + ): TypeSubstitutorMarker { + val expectParameters = expectTypeParameters.castAll() + val actualParameters = actualTypeParameters.castAll() + 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 { + return asIr().declarations.filterNot { it is IrAnonymousInitializer }.map { it.symbol } + } + + override fun RegularClassSymbolMarker.getMembersForExpectClass(name: Name): List { + return asIr().declarations.filter { it.getNameWithAssert() == name }.map { it.symbol } + } + + override fun RegularClassSymbolMarker.collectEnumEntryNames(): List { + return asIr().declarations.filterIsInstance().map { it.name } + } + + override val CallableSymbolMarker.dispatchReceiverType: KotlinTypeMarker? + get() = (asIr().parent as? IrClass)?.defaultType + + override val CallableSymbolMarker.extensionReceiverType: KotlinTypeMarker? + get() = safeAsIr()?.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 + 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 + 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() ?: return false + return irConstructor.constructedClass.isAnnotationClass + } + + override val TypeParameterSymbolMarker.bounds: List + 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(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() + } + onMatchedClasses(expectSymbol, actualClassSymbol) + } + else -> onMatchedCallables(expectSymbol, actualSymbol) + } + } + + abstract fun onMatchedClasses(expectClassSymbol: IrClassSymbol, actualClassSymbol: IrClassSymbol) + abstract fun onMatchedCallables(expectSymbol: IrSymbol, actualSymbol: IrSymbol) +} diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/symbols/IrSymbol.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/symbols/IrSymbol.kt index c1efa0ebe70..5f5acc456bf 100644 --- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/symbols/IrSymbol.kt +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/symbols/IrSymbol.kt @@ -25,6 +25,7 @@ import org.jetbrains.kotlin.ir.types.IrSimpleType import org.jetbrains.kotlin.ir.types.IrTypeAbbreviation import org.jetbrains.kotlin.ir.util.IdSignature import org.jetbrains.kotlin.ir.util.SymbolTable +import org.jetbrains.kotlin.mpp.* import org.jetbrains.kotlin.types.model.TypeConstructorMarker import org.jetbrains.kotlin.types.model.TypeParameterMarker @@ -46,7 +47,7 @@ import org.jetbrains.kotlin.types.model.TypeParameterMarker * @see IdSignature * @see SymbolTable */ -interface IrSymbol { +interface IrSymbol : DeclarationSymbolMarker { /** * The declaration that this symbol refers to if it's bound. @@ -167,7 +168,7 @@ interface IrAnonymousInitializerSymbol : IrBindableSymbol +interface IrEnumEntrySymbol : IrBindableSymbol, EnumEntrySymbolMarker /** * A symbol whose [owner] is [IrField]. @@ -175,7 +176,7 @@ interface IrEnumEntrySymbol : IrBindableSymbol * @see IrGetField * @see IrSetField */ -interface IrFieldSymbol : IrBindableSymbol +interface IrFieldSymbol : IrBindableSymbol, FieldSymbolMarker /** * A symbol whose [owner] is [IrClass], [IrScript] or [IrTypeParameter]. @@ -195,7 +196,7 @@ sealed interface IrClassifierSymbol : IrSymbol, TypeConstructorMarker { * @see IrCall.superQualifierSymbol * @see IrFieldAccessExpression.superQualifierSymbol */ -interface IrClassSymbol : IrClassifierSymbol, IrBindableSymbol +interface IrClassSymbol : IrClassifierSymbol, IrBindableSymbol, RegularClassSymbolMarker /** * A symbol whose [owner] is [IrScript]. @@ -205,7 +206,10 @@ interface IrScriptSymbol : IrClassifierSymbol, IrBindableSymbol, TypeParameterMarker +interface IrTypeParameterSymbol : IrClassifierSymbol, + IrBindableSymbol, + TypeParameterMarker, + TypeParameterSymbolMarker /** * A symbol whose [owner] is [IrValueParameter] or [IrVariable]. @@ -223,7 +227,7 @@ sealed interface IrValueSymbol : IrSymbol { /** * A symbol whose [owner] is [IrValueParameter]. */ -interface IrValueParameterSymbol : IrValueSymbol, IrBindableSymbol +interface IrValueParameterSymbol : IrValueSymbol, IrBindableSymbol, ValueParameterSymbolMarker /** * A symbol whose [owner] is [IrVariable]. @@ -247,7 +251,7 @@ sealed interface IrReturnTargetSymbol : IrSymbol { * * @see IrFunctionReference */ -sealed interface IrFunctionSymbol : IrReturnTargetSymbol { +sealed interface IrFunctionSymbol : IrReturnTargetSymbol, FunctionSymbolMarker { override val owner: IrFunction } @@ -256,14 +260,14 @@ sealed interface IrFunctionSymbol : IrReturnTargetSymbol { * * @see IrConstructorCall */ -interface IrConstructorSymbol : IrFunctionSymbol, IrBindableSymbol +interface IrConstructorSymbol : IrFunctionSymbol, IrBindableSymbol, ConstructorSymbolMarker /** * A symbol whose [owner] is [IrSimpleFunction]. * * @see IrCall */ -interface IrSimpleFunctionSymbol : IrFunctionSymbol, IrBindableSymbol +interface IrSimpleFunctionSymbol : IrFunctionSymbol, IrBindableSymbol, SimpleFunctionSymbolMarker /** * A symbol whose [owner] is [IrReturnableBlock]. @@ -273,7 +277,7 @@ interface IrReturnableBlockSymbol : IrReturnTargetSymbol, IrBindableSymbol +interface IrPropertySymbol : IrBindableSymbol, PropertySymbolMarker /** * A symbol whose [owner] is [IrLocalDelegatedProperty]. @@ -285,4 +289,4 @@ interface IrLocalDelegatedPropertySymbol : IrBindableSymbol +interface IrTypeAliasSymbol : IrBindableSymbol, TypeAliasSymbolMarker diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/types/IrTypeSubstitutor.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/types/IrTypeSubstitutor.kt index 61214c9e63b..aec1aa75d48 100644 --- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/types/IrTypeSubstitutor.kt +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/types/IrTypeSubstitutor.kt @@ -13,8 +13,11 @@ import org.jetbrains.kotlin.types.Variance import org.jetbrains.kotlin.types.model.TypeSubstitutorMarker import org.jetbrains.kotlin.utils.memoryOptimizedMap +abstract class AbstractIrTypeSubstitutor : TypeSubstitutorMarker { + abstract fun substitute(type: IrType): IrType +} -abstract class AbstractIrTypeSubstitutor(private val irBuiltIns: IrBuiltIns) : TypeSubstitutorMarker { +abstract class BaseIrTypeSubstitutor(private val irBuiltIns: IrBuiltIns) : AbstractIrTypeSubstitutor() { private fun IrType.typeParameterConstructor(): IrTypeParameterSymbol? { return if (this is IrSimpleType) classifier as? IrTypeParameterSymbol @@ -25,7 +28,7 @@ abstract class AbstractIrTypeSubstitutor(private val irBuiltIns: IrBuiltIns) : T abstract fun isEmptySubstitution(): Boolean - fun substitute(type: IrType): IrType { + final override fun substitute(type: IrType): IrType { if (isEmptySubstitution()) return type return substituteType(type) } @@ -63,8 +66,9 @@ abstract class AbstractIrTypeSubstitutor(private val irBuiltIns: IrBuiltIns) : T class IrTypeSubstitutor( typeParameters: List, typeArguments: List, - irBuiltIns: IrBuiltIns -) : AbstractIrTypeSubstitutor(irBuiltIns) { + irBuiltIns: IrBuiltIns, + private val allowEmptySubstitution: Boolean = false +) : BaseIrTypeSubstitutor(irBuiltIns) { init { assert(typeParameters.size == typeArguments.size) { @@ -80,7 +84,8 @@ class IrTypeSubstitutor( override fun getSubstitutionArgument(typeParameter: IrTypeParameterSymbol): IrTypeArgument = substitution[typeParameter] - ?: throw AssertionError("Unsubstituted type parameter: ${typeParameter.owner.render()}") + ?: typeParameter.takeIf { allowEmptySubstitution }?.owner?.defaultType + ?: error("Unsubstituted type parameter: ${typeParameter.owner.render()}") override fun isEmptySubstitution(): Boolean = substitution.isEmpty() } @@ -90,7 +95,7 @@ class IrCapturedTypeSubstitutor( typeArguments: List, capturedTypes: List, irBuiltIns: IrBuiltIns -) : AbstractIrTypeSubstitutor(irBuiltIns) { +) : BaseIrTypeSubstitutor(irBuiltIns) { init { assert(typeArguments.size == typeParameters.size) @@ -108,3 +113,10 @@ class IrCapturedTypeSubstitutor( override fun isEmptySubstitution(): Boolean = oldSubstitution.isEmpty() } + +class IrChainedSubstitutor(val first: AbstractIrTypeSubstitutor, val second: AbstractIrTypeSubstitutor) : AbstractIrTypeSubstitutor() { + override fun substitute(type: IrType): IrType { + val firstResult = first.substitute(type) + return second.substitute(firstResult) + } +} diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/types/irTypes.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/types/irTypes.kt index 16e07873514..48299012573 100644 --- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/types/irTypes.kt +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/types/irTypes.kt @@ -169,7 +169,7 @@ val IrClassifierSymbol.defaultType: IrType is IrScriptSymbol -> unexpectedSymbolKind() } -val IrTypeParameter.defaultType: IrType +val IrTypeParameter.defaultType: IrSimpleType get() = IrSimpleTypeImpl( symbol, SimpleTypeNullability.NOT_SPECIFIED, diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/AdditionalIrUtils.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/AdditionalIrUtils.kt index 8e090661e74..1c9fab0601f 100644 --- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/AdditionalIrUtils.kt +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/AdditionalIrUtils.kt @@ -15,10 +15,7 @@ import org.jetbrains.kotlin.ir.symbols.IrClassifierSymbol import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol import org.jetbrains.kotlin.ir.symbols.IrSymbol import org.jetbrains.kotlin.ir.symbols.impl.IrClassPublicSymbolImpl -import org.jetbrains.kotlin.name.ClassId -import org.jetbrains.kotlin.name.FqName -import org.jetbrains.kotlin.name.Name -import org.jetbrains.kotlin.name.SpecialNames +import org.jetbrains.kotlin.name.* import org.jetbrains.kotlin.resolve.descriptorUtil.module import org.jetbrains.kotlin.utils.filterIsInstanceAnd import java.io.File @@ -57,12 +54,49 @@ val IrDeclarationParent.kotlinFqName: FqName } val IrClass.classId: ClassId? + get() = classIdImpl + +val IrTypeAlias.classId: ClassId? + get() = classIdImpl + +private val IrDeclarationWithName.classIdImpl: ClassId? get() = when (val parent = this.parent) { is IrClass -> parent.classId?.createNestedClassId(this.name) is IrPackageFragment -> ClassId.topLevel(parent.packageFqName.child(this.name)) else -> null } +val IrClass.classIdOrFail: ClassId + get() = classIdOrFailImpl + +val IrTypeAlias.classIdOrFail: ClassId + get() = classIdOrFailImpl + +private val IrDeclarationWithName.classIdOrFailImpl: ClassId + get() = classIdImpl ?: error("No classId for $this") + +val IrFunction.callableId: CallableId + get() = callableIdImpl + +val IrProperty.callableId: CallableId + get() = callableIdImpl + +val IrField.callableId: CallableId + get() = callableIdImpl + +val IrEnumEntry.callableId: CallableId + get() = callableIdImpl + +private val IrDeclarationWithName.callableIdImpl: CallableId + get() { + if (this.symbol is IrClassifierSymbol) error("Classifiers can not have callableId. Got $this") + return when (val parent = this.parent) { + is IrClass -> parent.classId?.let { CallableId(it, name) } + is IrPackageFragment -> CallableId(parent.packageFqName, name) + else -> null + } ?: error("$this has no callableId") + } + @Suppress("unused") @Deprecated( "This function is deprecated because it has confusing name and behavior. " + diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/DeepCopyIrTreeWithSymbols.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/DeepCopyIrTreeWithSymbols.kt index 32175c74c5a..8412250ea9a 100644 --- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/DeepCopyIrTreeWithSymbols.kt +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/DeepCopyIrTreeWithSymbols.kt @@ -802,7 +802,7 @@ open class DeepCopyIrTreeWithSymbols( expression.value.transform() ).copyAttributes(expression) - private fun SymbolRemapper.getReferencedReturnTarget(returnTarget: IrReturnTargetSymbol) = + private fun SymbolRemapper.getReferencedReturnTarget(returnTarget: IrReturnTargetSymbol): IrReturnTargetSymbol = when (returnTarget) { is IrFunctionSymbol -> getReferencedFunction(returnTarget) is IrReturnableBlockSymbol -> getReferencedReturnableBlock(returnTarget) diff --git a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/mpp/AbstractExpectActualCompatibilityChecker.kt b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/mpp/AbstractExpectActualCompatibilityChecker.kt index f35c5403d5f..de69bfb6da6 100644 --- a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/mpp/AbstractExpectActualCompatibilityChecker.kt +++ b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/mpp/AbstractExpectActualCompatibilityChecker.kt @@ -29,7 +29,7 @@ object AbstractExpectActualCompatibilityChecker { context: ExpectActualMatchingContext, ): ExpectActualCompatibility { val result = with(context) { - areCompatibleClassifiers(expectClassSymbol, actualClassLikeSymbol) + areCompatibleClassifiers(expectClassSymbol, actualClassLikeSymbol, parentSubstitutor = null) } @Suppress("UNCHECKED_CAST") return result as ExpectActualCompatibility @@ -50,11 +50,29 @@ object AbstractExpectActualCompatibilityChecker { return result as ExpectActualCompatibility } + fun matchSingleExpectTopLevelDeclarationAgainstPotentialActuals( + expectDeclaration: DeclarationSymbolMarker, + actualDeclarations: List, + context: ExpectActualMatchingContext, + ) { + with(context) { + matchSingleExpectAgainstPotentialActuals( + expectDeclaration, + actualDeclarations, + substitutor = null, + expectClassSymbol = null, + actualClassSymbol = null, + unfulfilled = null + ) + } + } + context(ExpectActualMatchingContext<*>) @Suppress("warnings") private fun areCompatibleClassifiers( expectClassSymbol: RegularClassSymbolMarker, actualClassLikeSymbol: ClassLikeSymbolMarker, + parentSubstitutor: TypeSubstitutorMarker? ): ExpectActualCompatibility<*> { // Can't check FQ names here because nested expected class may be implemented via actual typealias's expansion with the other FQ name require(expectClassSymbol.name == actualClassLikeSymbol.name) { @@ -95,7 +113,7 @@ object AbstractExpectActualCompatibilityChecker { val substitutor = createExpectActualTypeParameterSubstitutor( expectTypeParameterSymbols, actualTypeParameterSymbols, - parentSubstitutor = null + parentSubstitutor ) areCompatibleTypeParameters(expectTypeParameterSymbols, actualTypeParameterSymbols, substitutor).let { @@ -133,7 +151,7 @@ object AbstractExpectActualCompatibilityChecker { actualClassSymbol: RegularClassSymbolMarker, substitutor: TypeSubstitutorMarker, ): ExpectActualCompatibility<*> { - val unfulfilled = arrayListOf, MutableCollection>>>() + val unfulfilled = arrayListOf, List>>>() val actualMembersByName = actualClassSymbol.collectAllMembers(isActualDeclaration = true).groupBy { it.name } @@ -145,31 +163,14 @@ object AbstractExpectActualCompatibilityChecker { expectMember is RegularClassSymbolMarker && actualMember is RegularClassSymbolMarker }.orEmpty() - val mapping = actualMembers.keysToMap { actualMember -> - when (expectMember) { - is CallableSymbolMarker -> areCompatibleCallables( - expectMember, - actualMember as CallableSymbolMarker, - substitutor, - expectClassSymbol, - actualClassSymbol - ) - - is RegularClassSymbolMarker -> areCompatibleClassifiers(expectMember, actualMember as RegularClassSymbolMarker) - else -> error("Unsupported declaration: $expectMember ($actualMembers)") - } - } - if (mapping.values.any { it == ExpectActualCompatibility.Compatible }) continue - - val incompatibilityMap = mutableMapOf, MutableCollection>() - for ((declaration, compatibility) in mapping) { - when (compatibility) { - ExpectActualCompatibility.Compatible -> continue@outer - is Incompatible -> incompatibilityMap.getOrPut(compatibility) { SmartList() }.add(declaration) - } - } - - unfulfilled.add(expectMember to incompatibilityMap) + matchSingleExpectAgainstPotentialActuals( + expectMember, + actualMembers, + substitutor, + expectClassSymbol, + actualClassSymbol, + unfulfilled + ) } if (expectClassSymbol.classKind == ClassKind.ENUM_CLASS) { @@ -186,6 +187,53 @@ object AbstractExpectActualCompatibilityChecker { return Incompatible.ClassScopes(unfulfilled) } + context(ExpectActualMatchingContext<*>) + private fun matchSingleExpectAgainstPotentialActuals( + expectMember: DeclarationSymbolMarker, + actualMembers: List, + substitutor: TypeSubstitutorMarker?, + expectClassSymbol: RegularClassSymbolMarker?, + actualClassSymbol: RegularClassSymbolMarker?, + unfulfilled: MutableList, List>>>? + ) { + val mapping = actualMembers.keysToMap { actualMember -> + when (expectMember) { + is CallableSymbolMarker -> areCompatibleCallables( + expectMember, + actualMember as CallableSymbolMarker, + substitutor, + expectClassSymbol, + actualClassSymbol + ) + + is RegularClassSymbolMarker -> { + val parentSubstitutor = substitutor?.takeIf { !innerClassesCapturesOuterTypeParameters } + areCompatibleClassifiers( + expectMember, + actualMember as ClassLikeSymbolMarker, + parentSubstitutor + ) + } + else -> error("Unsupported declaration: $expectMember ($actualMembers)") + } + } + + val incompatibilityMap = mutableMapOf, MutableList>() + for ((actualMember, compatibility) in mapping) { + when (compatibility) { + ExpectActualCompatibility.Compatible -> { + onMatchedMembers(expectMember, actualMember) + return + } + + is Incompatible -> incompatibilityMap.getOrPut(compatibility) { SmartList() }.add(actualMember) + } + } + + unfulfilled?.add(expectMember to incompatibilityMap) + onMismatchedMembersFromClassScope(expectMember, incompatibilityMap) + } + context(ExpectActualMatchingContext<*>) private fun areCompatibleCallables( expectDeclaration: CallableSymbolMarker, @@ -307,6 +355,10 @@ object AbstractExpectActualCompatibilityChecker { actualDeclaration ).let { if (it != ExpectActualCompatibility.Compatible) return it } + expectDeclaration is EnumEntrySymbolMarker && actualDeclaration is EnumEntrySymbolMarker -> { + // do nothing, entries are matched only by name + } + else -> error("Unsupported declarations: $expectDeclaration, $actualDeclaration") } diff --git a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/mpp/ExpectActualMatchingContext.kt b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/mpp/ExpectActualMatchingContext.kt index 8319d710137..e2bf664601a 100644 --- a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/mpp/ExpectActualMatchingContext.kt +++ b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/mpp/ExpectActualMatchingContext.kt @@ -12,6 +12,7 @@ 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.multiplatform.ExpectActualCompatibility import org.jetbrains.kotlin.types.Variance import org.jetbrains.kotlin.types.model.KotlinTypeMarker import org.jetbrains.kotlin.types.model.TypeSubstitutorMarker @@ -20,6 +21,21 @@ import org.jetbrains.kotlin.types.model.TypeSystemContext interface ExpectActualMatchingContext : TypeSystemContext { val shouldCheckReturnTypesOfCallables: Boolean + /* + * This flag indicates how are type parameters of inner classes stored in the specific implementation of RegularClassSymbolMarker + * + * class Outer { + * inner class Inner + * } + * + * If flag is set to `true` then `typeParameters` for class `Outer.Inner` contains both parameters: [U, R] + * Otherwise it contains only parameters of itself: [U] + * + * This flag is needed for proper calculation of substitutions for components of inner classes + */ + val innerClassesCapturesOuterTypeParameters: Boolean + get() = true + val RegularClassSymbolMarker.classId: ClassId val TypeAliasSymbolMarker.classId: ClassId val CallableSymbolMarker.callableId: CallableId @@ -100,10 +116,17 @@ interface ExpectActualMatchingContext : TypeSystemC /* * Determines should some declaration from expect class scope be checked * - FE 1.0: skip fake overrides - * - FIR: skip nothing + * - FIR: skip fake overrides * - IR: skip nothing */ fun CallableSymbolMarker.shouldSkipMatching(containingExpectClass: RegularClassSymbolMarker): Boolean val CallableSymbolMarker.hasStableParameterNames: Boolean + + fun onMatchedMembers(expectSymbol: DeclarationSymbolMarker, actualSymbol: DeclarationSymbolMarker) {} + + fun onMismatchedMembersFromClassScope( + expectSymbol: DeclarationSymbolMarker, + actualSymbolsByIncompatibility: Map, List> + ) {} } diff --git a/compiler/testData/codegen/box/multiplatform/callToJavaSuper.kt b/compiler/testData/codegen/box/multiplatform/callToJavaSuper.kt index 4be5acd5a08..4cbc72a1000 100644 --- a/compiler/testData/codegen/box/multiplatform/callToJavaSuper.kt +++ b/compiler/testData/codegen/box/multiplatform/callToJavaSuper.kt @@ -1,5 +1,6 @@ // LANGUAGE: +MultiPlatformProjects // WITH_STDLIB +// FULL_JDK // TARGET_BACKEND: JVM_IR // IGNORE_BACKEND_K1: JVM_IR // Ignore reason: expect/actual are not supported in K1 box tests diff --git a/compiler/testData/diagnostics/tests/multiplatform/defaultArguments/methodDefaultArgsViaActualTypealias.fir.kt b/compiler/testData/diagnostics/tests/multiplatform/defaultArguments/methodDefaultArgsViaActualTypealias.fir.kt new file mode 100644 index 00000000000..f0471169b53 --- /dev/null +++ b/compiler/testData/diagnostics/tests/multiplatform/defaultArguments/methodDefaultArgsViaActualTypealias.fir.kt @@ -0,0 +1,49 @@ +// !DIAGNOSTICS: -ACTUAL_WITHOUT_EXPECT +// MODULE: m1-common +// FILE: common.kt +expect class A { + fun foo(p1: String = "common", p2: String = "common", p3: String) +} + +expect class B { + fun foo(s: String) +} + +interface I { + fun methodWithDefaultArg(s: String = "common") +} + +expect class WithDefaultArgFromSuper : I { + override fun methodWithDefaultArg(s: String) +} + +expect open class WithIncompatibility { + fun foo(p: String = "common") +} + +// MODULE: m2-jvm()()(m1-common) +// FILE: jvm.kt + +class AImpl { + fun foo(p1: String = "impl", p2: String = "impl", p3: String) {} +} + +actual typealias A = AImpl + +class BImpl { + fun foo(s: String = "impl") {} +} + +actual typealias B = BImpl + +class WithDefaultArgFromSuperImpl : I { + override fun methodWithDefaultArg(s: String) {} +} + +actual typealias WithDefaultArgFromSuper = WithDefaultArgFromSuperImpl + +class WithIncompatibilityImpl { + fun foo(p: String) {} +} + +actual typealias WithIncompatibility = WithIncompatibilityImpl diff --git a/compiler/testData/diagnostics/tests/multiplatform/defaultArguments/methodDefaultArgsViaActualTypealias.kt b/compiler/testData/diagnostics/tests/multiplatform/defaultArguments/methodDefaultArgsViaActualTypealias.kt index 03418a5a091..2aff9ad6a38 100644 --- a/compiler/testData/diagnostics/tests/multiplatform/defaultArguments/methodDefaultArgsViaActualTypealias.kt +++ b/compiler/testData/diagnostics/tests/multiplatform/defaultArguments/methodDefaultArgsViaActualTypealias.kt @@ -1,4 +1,3 @@ -// FIR_IDENTICAL // !DIAGNOSTICS: -ACTUAL_WITHOUT_EXPECT // MODULE: m1-common // FILE: common.kt diff --git a/compiler/testData/diagnostics/tests/multiplatform/enum/javaEnum.fir.kt b/compiler/testData/diagnostics/tests/multiplatform/enum/javaEnum.fir.kt new file mode 100644 index 00000000000..3bb2c1601ea --- /dev/null +++ b/compiler/testData/diagnostics/tests/multiplatform/enum/javaEnum.fir.kt @@ -0,0 +1,34 @@ +// MODULE: m1-common +// FILE: common.kt + +expect enum class Foo { + ENTRY +} + +expect enum class _TimeUnit + +// MODULE: m2-jvm()()(m1-common) +// FILE: jvm.kt + +actual typealias Foo = FooImpl + +actual typealias _TimeUnit = java.util.concurrent.TimeUnit + +// FILE: FooImpl.java + +public enum FooImpl { + ENTRY("OK") { + @Override + public String getResult() { + return value; + } + }; + + protected final String value; + + public FooImpl(String value) { + this.value = value; + } + + public abstract String getResult(); +} diff --git a/compiler/testData/diagnostics/tests/multiplatform/enum/javaEnum.kt b/compiler/testData/diagnostics/tests/multiplatform/enum/javaEnum.kt index ab192066c68..727c918af54 100644 --- a/compiler/testData/diagnostics/tests/multiplatform/enum/javaEnum.kt +++ b/compiler/testData/diagnostics/tests/multiplatform/enum/javaEnum.kt @@ -1,4 +1,3 @@ -// FIR_IDENTICAL // MODULE: m1-common // FILE: common.kt diff --git a/compiler/testData/diagnostics/tests/multiplatform/enum/simpleEnum.fir.kt b/compiler/testData/diagnostics/tests/multiplatform/enum/simpleEnum.fir.kt new file mode 100644 index 00000000000..eb4776a61fa --- /dev/null +++ b/compiler/testData/diagnostics/tests/multiplatform/enum/simpleEnum.fir.kt @@ -0,0 +1,17 @@ +// MODULE: m1-common +// FILE: common.kt + +expect enum class Foo { + ENTRY1, + ENTRY2, + ENTRY3; +} + +// MODULE: m2-jvm()()(m1-common) +// FILE: jvm.kt + +actual enum class Foo(val x: String) { + ENTRY1("1"), + ENTRY2("2"), + ENTRY3("3"); +} diff --git a/compiler/testData/diagnostics/tests/multiplatform/enum/simpleEnum.kt b/compiler/testData/diagnostics/tests/multiplatform/enum/simpleEnum.kt index e465c204f82..5261c676cf1 100644 --- a/compiler/testData/diagnostics/tests/multiplatform/enum/simpleEnum.kt +++ b/compiler/testData/diagnostics/tests/multiplatform/enum/simpleEnum.kt @@ -1,4 +1,3 @@ -// FIR_IDENTICAL // MODULE: m1-common // FILE: common.kt diff --git a/compiler/testData/diagnostics/tests/multiplatform/expectInterfaceApplicability.fir.kt b/compiler/testData/diagnostics/tests/multiplatform/expectInterfaceApplicability.fir.kt index 6b2ab5ced75..b43742bdc00 100644 --- a/compiler/testData/diagnostics/tests/multiplatform/expectInterfaceApplicability.fir.kt +++ b/compiler/testData/diagnostics/tests/multiplatform/expectInterfaceApplicability.fir.kt @@ -1,15 +1,15 @@ // MODULE: m1-common // FILE: common.kt // TODO: .fir.kt version is just a stub. -expect interface My { +expect interface My { open fun openFunPositive() - open fun openFunNegative() + open fun openFunNegative() abstract fun abstractFun() open val openValPositive: Int - open val openValNegative: Int + open val openValNegative: Int abstract val abstractVal: Int -} +} // MODULE: m1-jvm()()(m1-common) // FILE: jvm.kt diff --git a/compiler/testData/diagnostics/tests/multiplatform/generic/typeParameterBoundsDifferentOrderActualMissing.fir.kt b/compiler/testData/diagnostics/tests/multiplatform/generic/typeParameterBoundsDifferentOrderActualMissing.fir.kt index de2dfec1596..3be85a7a66f 100644 --- a/compiler/testData/diagnostics/tests/multiplatform/generic/typeParameterBoundsDifferentOrderActualMissing.fir.kt +++ b/compiler/testData/diagnostics/tests/multiplatform/generic/typeParameterBoundsDifferentOrderActualMissing.fir.kt @@ -4,7 +4,7 @@ interface A interface B -expect fun List.foo() where T : A, T : B +expect fun List.foo() where T : A, T : B // MODULE: m2-jvm()()(m1-common) // FILE: jvm.kt diff --git a/compiler/testData/diagnostics/tests/multiplatform/headerClass/actualClassWithDefaultValuesInAnnotationViaTypealias.fir.kt b/compiler/testData/diagnostics/tests/multiplatform/headerClass/actualClassWithDefaultValuesInAnnotationViaTypealias.fir.kt index 119fd39e8bf..79722e91845 100644 --- a/compiler/testData/diagnostics/tests/multiplatform/headerClass/actualClassWithDefaultValuesInAnnotationViaTypealias.fir.kt +++ b/compiler/testData/diagnostics/tests/multiplatform/headerClass/actualClassWithDefaultValuesInAnnotationViaTypealias.fir.kt @@ -7,7 +7,7 @@ expect annotation class Foo3 expect annotation class Foo4 expect annotation class Foo5() expect annotation class Foo6() -expect annotation class Foo7() +expect annotation class Foo7() @Foo1 fun foo() {} diff --git a/compiler/testData/diagnostics/tests/multiplatform/headerClass/actualClassWithDifferentConstructors.fir.kt b/compiler/testData/diagnostics/tests/multiplatform/headerClass/actualClassWithDifferentConstructors.fir.kt index 649563e8234..3e0480365f3 100644 --- a/compiler/testData/diagnostics/tests/multiplatform/headerClass/actualClassWithDifferentConstructors.fir.kt +++ b/compiler/testData/diagnostics/tests/multiplatform/headerClass/actualClassWithDifferentConstructors.fir.kt @@ -5,9 +5,9 @@ expect class Foo1 expect class Foo2 expect class Foo3 -expect class Bar1() -expect class Bar2() -expect class Bar3() +expect class Bar1() +expect class Bar2() +expect class Bar3() expect class Bar4() expect class Bar5() expect class Bar6() diff --git a/compiler/testData/diagnostics/tests/multiplatform/headerClass/expectDeclarationWithStrongIncompatibilities.fir.kt b/compiler/testData/diagnostics/tests/multiplatform/headerClass/expectDeclarationWithStrongIncompatibilities.fir.kt index 81e53a1036a..98dd465a6bf 100644 --- a/compiler/testData/diagnostics/tests/multiplatform/headerClass/expectDeclarationWithStrongIncompatibilities.fir.kt +++ b/compiler/testData/diagnostics/tests/multiplatform/headerClass/expectDeclarationWithStrongIncompatibilities.fir.kt @@ -3,12 +3,12 @@ // FILE: common.kt expect fun foo1(x: Int) -expect fun foo2(x: Int) +expect fun foo2(x: Int) expect class NoArgConstructor() -expect fun foo3(): Int -expect fun foo4(): Int +expect fun foo3(): Int +expect fun foo4(): Int // MODULE: m2-jvm()()(m1-common) diff --git a/compiler/testData/diagnostics/tests/multiplatform/headerClass/expectFunInterface.fir.kt b/compiler/testData/diagnostics/tests/multiplatform/headerClass/expectFunInterface.fir.kt index 8720243d056..c52d8a335f4 100644 --- a/compiler/testData/diagnostics/tests/multiplatform/headerClass/expectFunInterface.fir.kt +++ b/compiler/testData/diagnostics/tests/multiplatform/headerClass/expectFunInterface.fir.kt @@ -5,9 +5,9 @@ expect fun interface F1 { fun run() } -expect fun interface F2 { +expect fun interface F2 { fun run() -} +} expect fun interface F3 { fun run() @@ -21,9 +21,9 @@ expect fun interface F5 { fun run() } -expect fun interface F6 { +expect fun interface F6 { fun run() -} +} // MODULE: m2-jvm()()(m1-common) diff --git a/compiler/testData/diagnostics/tests/multiplatform/headerClass/genericClassImplTypeAlias.fir.kt b/compiler/testData/diagnostics/tests/multiplatform/headerClass/genericClassImplTypeAlias.fir.kt new file mode 100644 index 00000000000..3549d34fdac --- /dev/null +++ b/compiler/testData/diagnostics/tests/multiplatform/headerClass/genericClassImplTypeAlias.fir.kt @@ -0,0 +1,37 @@ +// !DIAGNOSTICS: -ACTUAL_WITHOUT_EXPECT +// MODULE: m1-common +// FILE: common.kt + +expect class C1 +expect interface C2 +expect interface C3 +expect interface C4 +expect interface C5 +expect interface C6 +expect interface C7 +expect interface C8 +expect interface C9 +expect interface C10 + +// MODULE: m2-jvm()()(m1-common) +// FILE: jvm.kt + +class A> +class B + +actual typealias C1 = String +actual typealias C2 = List +actual typealias C3 = List +actual typealias C4 = MutableMap +actual typealias C5 = MutableMap +actual typealias C51 = MutableMap +actual typealias C52 = MutableMap +actual typealias C53 = A> +actual typealias C54 = B> +actual typealias C6 = MutableList +actual typealias C7 = MutableList +actual typealias C8 = MutableList<*> +actual typealias C9 = MutableList + +typealias Tmp = MutableList +actual typealias C10 = Tmp diff --git a/compiler/testData/diagnostics/tests/multiplatform/headerClass/genericClassImplTypeAlias.kt b/compiler/testData/diagnostics/tests/multiplatform/headerClass/genericClassImplTypeAlias.kt index 65153d15164..804c086a1ae 100644 --- a/compiler/testData/diagnostics/tests/multiplatform/headerClass/genericClassImplTypeAlias.kt +++ b/compiler/testData/diagnostics/tests/multiplatform/headerClass/genericClassImplTypeAlias.kt @@ -1,4 +1,3 @@ -// FIR_IDENTICAL // !DIAGNOSTICS: -ACTUAL_WITHOUT_EXPECT // MODULE: m1-common // FILE: common.kt diff --git a/compiler/testData/diagnostics/tests/multiplatform/headerClass/morePermissiveVisibilityOnActual.fir.kt b/compiler/testData/diagnostics/tests/multiplatform/headerClass/morePermissiveVisibilityOnActual.fir.kt index f60649f095f..533880d54aa 100644 --- a/compiler/testData/diagnostics/tests/multiplatform/headerClass/morePermissiveVisibilityOnActual.fir.kt +++ b/compiler/testData/diagnostics/tests/multiplatform/headerClass/morePermissiveVisibilityOnActual.fir.kt @@ -2,20 +2,20 @@ // MODULE: m1-common // FILE: common.kt -expect open class Container { +expect open class Container { fun publicFun() internal fun internalFun1() internal fun internalFun2() - internal fun internalFun3() + internal fun internalFun3() protected fun protectedFun1() protected fun protectedFun2() - protected fun protectedFun3() + protected fun protectedFun3() - open internal fun openInternalFun() - open fun openPublicFun() -} + open internal fun openInternalFun() + open fun openPublicFun() +} // MODULE: m2-jvm()()(m1-common) diff --git a/compiler/testData/diagnostics/tests/multiplatform/headerClass/nestedClassesWithErrors.fir.kt b/compiler/testData/diagnostics/tests/multiplatform/headerClass/nestedClassesWithErrors.fir.kt index e43fb12bdaf..9c44aaae50a 100644 --- a/compiler/testData/diagnostics/tests/multiplatform/headerClass/nestedClassesWithErrors.fir.kt +++ b/compiler/testData/diagnostics/tests/multiplatform/headerClass/nestedClassesWithErrors.fir.kt @@ -14,9 +14,9 @@ expect class C { expect inner class I } -expect class D { +expect class D { class N -} +} expect class E { class N diff --git a/compiler/testData/diagnostics/tests/multiplatform/headerClass/noImplKeywordOnMember.fir.kt b/compiler/testData/diagnostics/tests/multiplatform/headerClass/noImplKeywordOnMember.fir.kt index bd34df5eb05..f25d953ab1d 100644 --- a/compiler/testData/diagnostics/tests/multiplatform/headerClass/noImplKeywordOnMember.fir.kt +++ b/compiler/testData/diagnostics/tests/multiplatform/headerClass/noImplKeywordOnMember.fir.kt @@ -1,10 +1,10 @@ // MODULE: m1-common // FILE: common.kt -expect class Foo { +expect class Foo { fun bar(): String - fun bas(f: Int) -} + fun bas(f: Int) +} // MODULE: m2-jvm()()(m1-common) // FILE: jvm.kt diff --git a/compiler/testData/diagnostics/tests/multiplatform/headerClass/superClass.fir.kt b/compiler/testData/diagnostics/tests/multiplatform/headerClass/superClass.fir.kt index 3e1c7edda7f..1d82857c7a1 100644 --- a/compiler/testData/diagnostics/tests/multiplatform/headerClass/superClass.fir.kt +++ b/compiler/testData/diagnostics/tests/multiplatform/headerClass/superClass.fir.kt @@ -7,7 +7,7 @@ interface J expect class Foo : I, C, J -expect class Bar : C() +expect class Bar : C() // MODULE: m2-jvm()()(m1-common) // FILE: jvm.kt diff --git a/compiler/testData/diagnostics/tests/multiplatform/headerFunInNonHeaderClass.fir.kt b/compiler/testData/diagnostics/tests/multiplatform/headerFunInNonHeaderClass.fir.kt index 14fd83841bc..5745558b1fb 100644 --- a/compiler/testData/diagnostics/tests/multiplatform/headerFunInNonHeaderClass.fir.kt +++ b/compiler/testData/diagnostics/tests/multiplatform/headerFunInNonHeaderClass.fir.kt @@ -2,7 +2,7 @@ // FILE: common.kt class Foo { - expect fun bar(): String + expect fun bar(): String } // MODULE: m1-jvm()()(m1-common) diff --git a/compiler/testData/diagnostics/tests/multiplatform/hmpp/kt57320.fir.kt b/compiler/testData/diagnostics/tests/multiplatform/hmpp/kt57320.fir.kt index cb24a89785e..2bdb9b24101 100644 --- a/compiler/testData/diagnostics/tests/multiplatform/hmpp/kt57320.fir.kt +++ b/compiler/testData/diagnostics/tests/multiplatform/hmpp/kt57320.fir.kt @@ -26,7 +26,7 @@ interface KotlinXStringDemoInterface { val value: String } -expect fun StringDemoInterface.plusK(): String +expect fun StringDemoInterface.plusK(): String // MODULE: js()()(common, intermediate) // TARGET_PLATFORM: JS @@ -34,7 +34,7 @@ expect fun StringDemoInterface.plusK(): String // FILE: StringDemoInterface.kt actual typealias StringDemoInterface = KotlinXStringDemoInterface -; The following declaration is incompatible: expect fun StringDemoInterface.plusK(): String")!>actual fun StringDemoInterface.plusK() = StringValue(value).plus("K").value +; The following declaration is incompatible: expect fun StringDemoInterface.plusK(): String")!>actual fun StringDemoInterface.plusK() = StringValue(value).plus("K").value // FILE: main.kt class StringDemo(override val value: String) : StringDemoInterface diff --git a/compiler/testData/diagnostics/tests/multiplatform/hmpp/multiplatformCompositeAnalysis/expectAndActualInTheSameModule.fir.kt b/compiler/testData/diagnostics/tests/multiplatform/hmpp/multiplatformCompositeAnalysis/expectAndActualInTheSameModule.fir.kt index 35032665242..2835fe5ef79 100644 --- a/compiler/testData/diagnostics/tests/multiplatform/hmpp/multiplatformCompositeAnalysis/expectAndActualInTheSameModule.fir.kt +++ b/compiler/testData/diagnostics/tests/multiplatform/hmpp/multiplatformCompositeAnalysis/expectAndActualInTheSameModule.fir.kt @@ -3,10 +3,10 @@ // TARGET_PLATFORM: Common expect class CommonClass { - fun memberFun() - val memberProp: Int - class Nested - inner class Inner + fun memberFun() + val memberProp: Int + class Nested + inner class Inner } actual class CommonClass { actual fun memberFun() {} diff --git a/compiler/testData/diagnostics/tests/multiplatform/hmpp/multiplatformCompositeAnalysis/expectAndActualInTheSameModuleIncompatibilities.fir.kt b/compiler/testData/diagnostics/tests/multiplatform/hmpp/multiplatformCompositeAnalysis/expectAndActualInTheSameModuleIncompatibilities.fir.kt index 798233848b2..6a90ee7255a 100644 --- a/compiler/testData/diagnostics/tests/multiplatform/hmpp/multiplatformCompositeAnalysis/expectAndActualInTheSameModuleIncompatibilities.fir.kt +++ b/compiler/testData/diagnostics/tests/multiplatform/hmpp/multiplatformCompositeAnalysis/expectAndActualInTheSameModuleIncompatibilities.fir.kt @@ -13,8 +13,8 @@ val callableKind: Int = 1 expect fun typeParameterCount() fun typeParameterCount() {} -expect enum class EnumEntries { - ONE, TWO; +expect enum class EnumEntries { + ONE, TWO; } actual enum class EnumEntries { ONE; diff --git a/compiler/testData/diagnostics/tests/multiplatform/java/flexibleTypes.fir.kt b/compiler/testData/diagnostics/tests/multiplatform/java/flexibleTypes.fir.kt deleted file mode 100644 index 9580ce80604..00000000000 --- a/compiler/testData/diagnostics/tests/multiplatform/java/flexibleTypes.fir.kt +++ /dev/null @@ -1,31 +0,0 @@ -// MODULE: m1-common -// FILE: common.kt - -expect class Foo { - constructor(p: Any) - - fun f1(s: String): Int - - fun f2(s: List?): MutableMap - - fun > f3(t: T): T? -} - -// MODULE: m2-jvm()()(m1-common) -// FILE: FooImpl.java - -import java.util.*; - -public class FooImpl { - public FooImpl(Object p) {} - - public final int f1(String s) { return 0; } - - public final Map f2(List s) { return null; } - - public final > T f3(T t) { return null; } -} - -// FILE: jvm.kt - -actual typealias Foo = FooImpl diff --git a/compiler/testData/diagnostics/tests/multiplatform/java/flexibleTypes.kt b/compiler/testData/diagnostics/tests/multiplatform/java/flexibleTypes.kt index 8c344f239ed..78871917df9 100644 --- a/compiler/testData/diagnostics/tests/multiplatform/java/flexibleTypes.kt +++ b/compiler/testData/diagnostics/tests/multiplatform/java/flexibleTypes.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL // MODULE: m1-common // FILE: common.kt diff --git a/compiler/testData/diagnostics/tests/multiplatform/kt54827.fir.kt b/compiler/testData/diagnostics/tests/multiplatform/kt54827.fir.kt index 015fdeb55c7..34818d4ec5b 100644 --- a/compiler/testData/diagnostics/tests/multiplatform/kt54827.fir.kt +++ b/compiler/testData/diagnostics/tests/multiplatform/kt54827.fir.kt @@ -1,8 +1,8 @@ // MODULE: m1-common // FILE: common.kt -expect class SomeClass { +expect class SomeClass { fun foo() -} +} // MODULE: m1-jvm()()(m1-common) // FILE: jvm.kt diff --git a/compiler/testData/diagnostics/tests/multiplatform/topLevelFun/inlineFun.fir.kt b/compiler/testData/diagnostics/tests/multiplatform/topLevelFun/inlineFun.fir.kt index 94d0de397a4..b2975eb0e8c 100644 --- a/compiler/testData/diagnostics/tests/multiplatform/topLevelFun/inlineFun.fir.kt +++ b/compiler/testData/diagnostics/tests/multiplatform/topLevelFun/inlineFun.fir.kt @@ -1,7 +1,7 @@ // MODULE: m1-common // FILE: common.kt -inline expect fun inlineFun() +inline expect fun inlineFun() expect fun nonInlineFun() // MODULE: m2-jvm()()(m1-common) diff --git a/compiler/testData/diagnostics/tests/multiplatform/topLevelFun/valueParameterModifiers.fir.kt b/compiler/testData/diagnostics/tests/multiplatform/topLevelFun/valueParameterModifiers.fir.kt index 37868b85876..0ccc96ccf1d 100644 --- a/compiler/testData/diagnostics/tests/multiplatform/topLevelFun/valueParameterModifiers.fir.kt +++ b/compiler/testData/diagnostics/tests/multiplatform/topLevelFun/valueParameterModifiers.fir.kt @@ -3,15 +3,15 @@ // FILE: common.kt expect fun f1(s: () -> String) -expect inline fun f2(s: () -> String) +expect inline fun f2(s: () -> String) expect inline fun f3(noinline s: () -> String) expect fun f4(s: () -> String) -expect inline fun f5(s: () -> String) +expect inline fun f5(s: () -> String) expect inline fun f6(crossinline s: () -> String) -expect fun f7(x: Any) -expect fun f8(vararg x: Any) +expect fun f7(x: Any) +expect fun f8(vararg x: Any) // MODULE: m2-jvm()()(m1-common) // FILE: jvm.kt diff --git a/compiler/testData/diagnostics/tests/multiplatform/varSetterVisibility.fir.kt b/compiler/testData/diagnostics/tests/multiplatform/varSetterVisibility.fir.kt index a574d3eeebd..99e67edb724 100644 --- a/compiler/testData/diagnostics/tests/multiplatform/varSetterVisibility.fir.kt +++ b/compiler/testData/diagnostics/tests/multiplatform/varSetterVisibility.fir.kt @@ -1,20 +1,20 @@ // MODULE: m1-common // FILE: common.kt -expect var v1: Boolean +expect var v1: Boolean expect var v2: Boolean internal set -expect var v3: Boolean - internal set +expect var v3: Boolean + internal set -expect open class C { - var foo: Boolean -} +expect open class C { + var foo: Boolean +} -expect open class C2 { - var foo: Boolean -} +expect open class C2 { + var foo: Boolean +} // MODULE: m1-jvm()()(m1-common) // FILE: jvm.kt diff --git a/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/backend/ir/ActualizerOnlyFacade.kt b/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/backend/ir/ActualizerOnlyFacade.kt index 77b140beb00..067f65e5449 100644 --- a/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/backend/ir/ActualizerOnlyFacade.kt +++ b/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/backend/ir/ActualizerOnlyFacade.kt @@ -6,8 +6,11 @@ package org.jetbrains.kotlin.test.backend.ir import org.jetbrains.kotlin.backend.common.actualizer.IrActualizer +import org.jetbrains.kotlin.backend.jvm.JvmIrTypeSystemContext import org.jetbrains.kotlin.config.LanguageFeature import org.jetbrains.kotlin.config.languageVersionSettings +import org.jetbrains.kotlin.ir.types.IrTypeSystemContextImpl +import org.jetbrains.kotlin.platform.jvm.isJvm import org.jetbrains.kotlin.test.model.* import org.jetbrains.kotlin.test.services.TestServices import org.jetbrains.kotlin.test.services.compilerConfigurationProvider @@ -17,10 +20,16 @@ class ActualizerOnlyFacade( ) : AbstractTestFacade() { override fun transform(module: TestModule, inputArtifact: IrBackendInput): IrBackendInput { if (module.useIrActualizer()) { + val builtins = inputArtifact.irModuleFragment.irBuiltins + val typeSystemContext = when (module.targetPlatform.isJvm()) { + true -> JvmIrTypeSystemContext(builtins) + false -> IrTypeSystemContextImpl(builtins) + } IrActualizer.actualize( inputArtifact.irModuleFragment, inputArtifact.dependentIrModuleFragments, inputArtifact.diagnosticReporter, + typeSystemContext, testServices.compilerConfigurationProvider.getCompilerConfiguration(module).languageVersionSettings ) } @@ -35,4 +44,4 @@ class ActualizerOnlyFacade( override val outputKind: TestArtifactKind = BackendKinds.IrBackend override fun shouldRunAnalysis(module: TestModule): Boolean = true -} \ No newline at end of file +} diff --git a/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/backend/ir/JvmIrBackendFacade.kt b/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/backend/ir/JvmIrBackendFacade.kt index e4f74da819e..b083947d9fc 100644 --- a/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/backend/ir/JvmIrBackendFacade.kt +++ b/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/backend/ir/JvmIrBackendFacade.kt @@ -8,12 +8,14 @@ package org.jetbrains.kotlin.test.backend.ir import org.jetbrains.kotlin.KtPsiSourceFile import org.jetbrains.kotlin.backend.common.BackendException import org.jetbrains.kotlin.backend.common.actualizer.IrActualizer +import org.jetbrains.kotlin.backend.jvm.JvmIrTypeSystemContext import org.jetbrains.kotlin.backend.jvm.MultifileFacadeFileEntry import org.jetbrains.kotlin.backend.jvm.lower.getFileClassInfoFromIrFile import org.jetbrains.kotlin.config.LanguageFeature import org.jetbrains.kotlin.fileClasses.JvmFileClassUtil import org.jetbrains.kotlin.ir.PsiIrFileEntry import org.jetbrains.kotlin.ir.declarations.IrFile +import org.jetbrains.kotlin.ir.types.IrTypeSystemContextImpl import org.jetbrains.kotlin.ir.util.NaiveSourceBasedFileEntryImpl import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.test.backend.classic.JavaCompilerFacade @@ -40,6 +42,7 @@ class JvmIrBackendFacade( inputArtifact.irModuleFragment, inputArtifact.dependentIrModuleFragments, inputArtifact.state.diagnosticReporter, + JvmIrTypeSystemContext(inputArtifact.irModuleFragment.irBuiltins), inputArtifact.state.languageVersionSettings ) } diff --git a/core/compiler.common/src/org/jetbrains/kotlin/mpp/DeclarationSymbolMarkers.kt b/core/compiler.common/src/org/jetbrains/kotlin/mpp/DeclarationSymbolMarkers.kt index 1b45d03ef79..801e090af17 100644 --- a/core/compiler.common/src/org/jetbrains/kotlin/mpp/DeclarationSymbolMarkers.kt +++ b/core/compiler.common/src/org/jetbrains/kotlin/mpp/DeclarationSymbolMarkers.kt @@ -17,6 +17,8 @@ interface ConstructorSymbolMarker : FunctionSymbolMarker interface SimpleFunctionSymbolMarker : FunctionSymbolMarker interface PropertySymbolMarker : CallableSymbolMarker interface ValueParameterSymbolMarker : CallableSymbolMarker +interface FieldSymbolMarker : CallableSymbolMarker +interface EnumEntrySymbolMarker : CallableSymbolMarker interface ClassifierSymbolMarker : DeclarationSymbolMarker interface TypeParameterSymbolMarker : ClassifierSymbolMarker diff --git a/core/compiler.common/src/org/jetbrains/kotlin/resolve/multiplatform/ExpectActualCompatibility.kt b/core/compiler.common/src/org/jetbrains/kotlin/resolve/multiplatform/ExpectActualCompatibility.kt index 637aced0cfb..45aabd08d35 100644 --- a/core/compiler.common/src/org/jetbrains/kotlin/resolve/multiplatform/ExpectActualCompatibility.kt +++ b/core/compiler.common/src/org/jetbrains/kotlin/resolve/multiplatform/ExpectActualCompatibility.kt @@ -5,6 +5,9 @@ package org.jetbrains.kotlin.resolve.multiplatform +import kotlin.contracts.ExperimentalContracts +import kotlin.contracts.contract + sealed class ExpectActualCompatibility { // For IncompatibilityKind.STRONG `actual` declaration is considered as overload and error reports on expected declaration enum class IncompatibilityKind { @@ -95,3 +98,11 @@ sealed class ExpectActualCompatibility { val ExpectActualCompatibility<*>.compatible: Boolean get() = this == ExpectActualCompatibility.Compatible + +@OptIn(ExperimentalContracts::class) +fun ExpectActualCompatibility<*>.isIncompatible(): Boolean { + contract { + returns(true) implies (this@isIncompatible is ExpectActualCompatibility.Incompatible<*>) + } + return !compatible +} diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/converters/FirJsKlibBackendFacade.kt b/js/js.tests/test/org/jetbrains/kotlin/js/test/converters/FirJsKlibBackendFacade.kt index a3a1db75061..1dbfaaf428e 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/converters/FirJsKlibBackendFacade.kt +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/converters/FirJsKlibBackendFacade.kt @@ -16,6 +16,7 @@ import org.jetbrains.kotlin.incremental.components.LookupTracker import org.jetbrains.kotlin.ir.backend.js.JsFactories import org.jetbrains.kotlin.ir.backend.js.resolverLogger import org.jetbrains.kotlin.ir.backend.js.serializeModuleIntoKlib +import org.jetbrains.kotlin.ir.types.IrTypeSystemContextImpl import org.jetbrains.kotlin.ir.util.IrMessageLogger import org.jetbrains.kotlin.js.test.utils.JsIrIncrementalDataProvider import org.jetbrains.kotlin.js.test.utils.jsIrIncrementalDataProvider @@ -71,6 +72,7 @@ class FirJsKlibBackendFacade( inputArtifact.irModuleFragment, inputArtifact.dependentIrModuleFragments, diagnosticReporter, + IrTypeSystemContextImpl(inputArtifact.irModuleFragment.irBuiltins), configuration.languageVersionSettings ) } else { @@ -124,4 +126,4 @@ class FirJsKlibBackendFacade( return BinaryArtifacts.KLib(File(outputFile)) } -} \ No newline at end of file +} diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Fir2Ir.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Fir2Ir.kt index 390a4810c49..5f400527a29 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Fir2Ir.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Fir2Ir.kt @@ -31,6 +31,7 @@ import org.jetbrains.kotlin.fir.signaturer.Ir2FirManglerAdapter import org.jetbrains.kotlin.incremental.components.LookupTracker import org.jetbrains.kotlin.ir.declarations.IrDeclaration import org.jetbrains.kotlin.ir.declarations.IrExternalPackageFragment +import org.jetbrains.kotlin.ir.types.IrTypeSystemContextImpl import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.ir.visitors.acceptVoid import org.jetbrains.kotlin.library.metadata.KlibMetadataFactories @@ -87,6 +88,7 @@ internal fun PhaseContext.fir2Ir( visibilityConverter = Fir2IrVisibilityConverter.Default, diagnosticReporter = diagnosticsReporter, kotlinBuiltIns = builtInsModule ?: DefaultBuiltIns.Instance, + actualizerTypeContextProvider = ::IrTypeSystemContextImpl, fir2IrResultPostCompute = { // it's important to compare manglers before actualization, since IR will be actualized, while FIR won't irModuleFragment.acceptVoid(