From d0b34fe378e0674d762f255b21e2b7437eebb470 Mon Sep 17 00:00:00 2001 From: Nikita Bobko Date: Mon, 16 Oct 2023 15:27:57 +0200 Subject: [PATCH] [FIR, IR] Refactor: restructure ExpectActualCompatibility class hierarchy KT-62590 is in progress This commit is pure refactoring. - No tests changed their behaviour - Semantics isn't changed Review: https://jetbrains.team/p/kt/reviews/12750/timeline --- .../fir/generator/HLDiagnosticConverter.kt | 2 +- .../diagnostics/KtFirDataClassConverters.kt | 4 +- .../api/fir/diagnostics/KtFirDiagnostics.kt | 4 +- .../fir/diagnostics/KtFirDiagnosticsImpl.kt | 4 +- .../diagnostics/FirDiagnosticsList.kt | 5 +- .../fir/analysis/diagnostics/FirErrors.kt | 6 +- .../FirExpectActualDeclarationChecker.kt | 26 +-- ...atformIncompatibilityDiagnosticRenderer.kt | 15 +- .../mpp/FirExpectActualMatchingContextImpl.kt | 9 +- .../mpp/FirExpectActualResolver.kt | 6 +- .../backend/common/CommonBackendErrors.kt | 4 +- .../actualizer/ExpectActualCollector.kt | 4 +- .../common/actualizer/IrActualizerUtils.kt | 2 +- .../IrExpectActualMatchingContext.kt | 2 +- ...tractExpectActualAnnotationMatchChecker.kt | 3 +- .../calls/mpp/AbstractExpectActualChecker.kt | 106 +++++----- .../calls/mpp/AbstractExpectActualMatcher.kt | 43 ++-- .../calls/mpp/ExpectActualMatchingContext.kt | 6 +- .../ExpectActualCompatibility.kt | 185 +++++++++--------- .../multiplatform/ExpectActualMemberDiff.kt | 64 +++--- 20 files changed, 256 insertions(+), 244 deletions(-) diff --git a/analysis/analysis-api-fir/analysis-api-fir-generator/src/org/jetbrains/kotlin/analysis/api/fir/generator/HLDiagnosticConverter.kt b/analysis/analysis-api-fir/analysis-api-fir-generator/src/org/jetbrains/kotlin/analysis/api/fir/generator/HLDiagnosticConverter.kt index 7a9f064e4c0..ce107929262 100644 --- a/analysis/analysis-api-fir/analysis-api-fir-generator/src/org/jetbrains/kotlin/analysis/api/fir/generator/HLDiagnosticConverter.kt +++ b/analysis/analysis-api-fir/analysis-api-fir-generator/src/org/jetbrains/kotlin/analysis/api/fir/generator/HLDiagnosticConverter.kt @@ -373,7 +373,7 @@ internal object FirToKtConversionCreator { ClassId::class, FirModuleData::class, ExpectActualCompatibility::class, - ExpectActualCompatibility.Incompatible::class, + ExpectActualCompatibility.MismatchOrIncompatible::class, ExpectActualAnnotationsIncompatibilityType::class, DeprecationInfo::class, ApiVersion::class, diff --git a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDataClassConverters.kt b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDataClassConverters.kt index 55852250390..a6984e0deba 100644 --- a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDataClassConverters.kt +++ b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDataClassConverters.kt @@ -3763,8 +3763,8 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert NoActualClassMemberForExpectedClassImpl( firSymbolBuilder.buildSymbol(firDiagnostic.a), firDiagnostic.b.map { pair -> - firSymbolBuilder.buildSymbol(pair.first) to pair.second.mapKeys { (incompatible, _) -> - incompatible + firSymbolBuilder.buildSymbol(pair.first) to pair.second.mapKeys { (mismatchOrIncompatible, _) -> + mismatchOrIncompatible }.mapValues { (_, collection) -> collection.map { firBasedSymbol -> firSymbolBuilder.buildSymbol(firBasedSymbol) diff --git a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnostics.kt b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnostics.kt index 51d5948a2ea..58c15a07b90 100644 --- a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnostics.kt +++ b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnostics.kt @@ -84,7 +84,7 @@ import org.jetbrains.kotlin.resolve.ForbiddenNamedArgumentsTarget import org.jetbrains.kotlin.resolve.deprecation.DeprecationInfo import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualAnnotationsIncompatibilityType import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility -import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility.Incompatible +import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility.MismatchOrIncompatible import org.jetbrains.kotlin.serialization.deserialization.IncompatibleVersionErrorData import org.jetbrains.kotlin.types.Variance @@ -2630,7 +2630,7 @@ sealed interface KtFirDiagnostic : KtDiagnosticWithPsi { interface NoActualClassMemberForExpectedClass : KtFirDiagnostic { override val diagnosticClass get() = NoActualClassMemberForExpectedClass::class val declaration: KtSymbol - val members: List>, List>>> + val members: List>, List>>> } interface ActualMissing : KtFirDiagnostic { diff --git a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnosticsImpl.kt b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnosticsImpl.kt index 3f48e824345..7850ba5f2d4 100644 --- a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnosticsImpl.kt +++ b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnosticsImpl.kt @@ -85,7 +85,7 @@ import org.jetbrains.kotlin.resolve.ForbiddenNamedArgumentsTarget import org.jetbrains.kotlin.resolve.deprecation.DeprecationInfo import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualAnnotationsIncompatibilityType import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility -import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility.Incompatible +import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility.MismatchOrIncompatible import org.jetbrains.kotlin.serialization.deserialization.IncompatibleVersionErrorData import org.jetbrains.kotlin.types.Variance @@ -3166,7 +3166,7 @@ internal class AmbiguousExpectsImpl( internal class NoActualClassMemberForExpectedClassImpl( override val declaration: KtSymbol, - override val members: List>, List>>>, + override val members: List>, List>>>, firDiagnostic: KtPsiDiagnostic, token: KtLifetimeToken, ) : KtAbstractFirDiagnostic(firDiagnostic, token), KtFirDiagnostic.NoActualClassMemberForExpectedClass diff --git a/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirDiagnosticsList.kt b/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirDiagnosticsList.kt index dc34509dd97..1a165a09495 100644 --- a/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirDiagnosticsList.kt +++ b/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirDiagnosticsList.kt @@ -37,7 +37,6 @@ import org.jetbrains.kotlin.resolve.ForbiddenNamedArgumentsTarget import org.jetbrains.kotlin.resolve.deprecation.DeprecationInfo import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualAnnotationsIncompatibilityType import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility -import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility.Incompatible import org.jetbrains.kotlin.serialization.deserialization.IncompatibleVersionErrorData import org.jetbrains.kotlin.types.Variance import org.jetbrains.kotlin.util.PrivateForInline @@ -1270,7 +1269,7 @@ object DIAGNOSTICS_LIST : DiagnosticList("FirErrors") { val ACTUAL_WITHOUT_EXPECT by error(PositioningStrategy.DECLARATION_NAME_ONLY) { parameter("declaration") - parameter, Collection>>("compatibility") + parameter, Collection>>("compatibility") } val AMBIGUOUS_ACTUALS by error(PositioningStrategy.INCOMPATIBLE_DECLARATION) { @@ -1285,7 +1284,7 @@ object DIAGNOSTICS_LIST : DiagnosticList("FirErrors") { val NO_ACTUAL_CLASS_MEMBER_FOR_EXPECTED_CLASS by error(PositioningStrategy.ACTUAL_DECLARATION_NAME) { parameter("declaration") - parameter, Collection>>>>("members") + parameter, Collection>>>>("members") } val ACTUAL_MISSING by error(PositioningStrategy.ACTUAL_DECLARATION_NAME) diff --git a/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt b/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt index 25269b86c24..f1cbd506635 100644 --- a/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt +++ b/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt @@ -110,7 +110,7 @@ import org.jetbrains.kotlin.resolve.ForbiddenNamedArgumentsTarget import org.jetbrains.kotlin.resolve.deprecation.DeprecationInfo import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualAnnotationsIncompatibilityType import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility -import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility.Incompatible +import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility.MismatchOrIncompatible import org.jetbrains.kotlin.serialization.deserialization.IncompatibleVersionErrorData import org.jetbrains.kotlin.types.Variance @@ -663,10 +663,10 @@ object FirErrors { val DEFAULT_ARGUMENTS_IN_EXPECT_ACTUALIZED_BY_FAKE_OVERRIDE by error2>(SourceElementPositioningStrategies.SUPERTYPES_LIST) val EXPECTED_FUNCTION_SOURCE_WITH_DEFAULT_ARGUMENTS_NOT_FOUND by error0() val NO_ACTUAL_FOR_EXPECT by error3, FirModuleData, Map>, Collection>>>(SourceElementPositioningStrategies.INCOMPATIBLE_DECLARATION) - val ACTUAL_WITHOUT_EXPECT by error2, Map>, Collection>>>(SourceElementPositioningStrategies.DECLARATION_NAME_ONLY) + val ACTUAL_WITHOUT_EXPECT by error2, Map>, Collection>>>(SourceElementPositioningStrategies.DECLARATION_NAME_ONLY) val AMBIGUOUS_ACTUALS by error2, Collection>>(SourceElementPositioningStrategies.INCOMPATIBLE_DECLARATION) val AMBIGUOUS_EXPECTS by error2, Collection>(SourceElementPositioningStrategies.INCOMPATIBLE_DECLARATION) - val NO_ACTUAL_CLASS_MEMBER_FOR_EXPECTED_CLASS by error2, List, Map>, Collection>>>>>(SourceElementPositioningStrategies.ACTUAL_DECLARATION_NAME) + val NO_ACTUAL_CLASS_MEMBER_FOR_EXPECTED_CLASS by error2, List, Map>, Collection>>>>>(SourceElementPositioningStrategies.ACTUAL_DECLARATION_NAME) val ACTUAL_MISSING by error0(SourceElementPositioningStrategies.ACTUAL_DECLARATION_NAME) val EXPECT_ACTUAL_CLASSIFIERS_ARE_IN_BETA_WARNING by warning0(SourceElementPositioningStrategies.EXPECT_ACTUAL_MODIFIER) val NOT_A_MULTIPLATFORM_COMPILATION by error0() diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirExpectActualDeclarationChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirExpectActualDeclarationChecker.kt index 7c7a6289559..ca9832db2f3 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirExpectActualDeclarationChecker.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirExpectActualDeclarationChecker.kt @@ -30,9 +30,10 @@ import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.name.StandardClassIds import org.jetbrains.kotlin.resolve.calls.mpp.AbstractExpectActualAnnotationMatchChecker import org.jetbrains.kotlin.resolve.checkers.OptInNames +import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCheckingCompatibility import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility -import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility.Compatible -import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility.Incompatible +import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility.MismatchOrIncompatible +import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualMatchingCompatibility import org.jetbrains.kotlin.resolve.multiplatform.isCompatibleOrWeaklyIncompatible @Suppress("DuplicatedCode") @@ -128,9 +129,11 @@ object FirExpectActualDeclarationChecker : FirBasicDeclarationChecker() { val source = declaration.source if (!declaration.isActual) { - if (compatibilityToMembersMap.allStrongIncompatibilities()) return + if (compatibilityToMembersMap.allMismatches()) return - if (Compatible in compatibilityToMembersMap) { + if (ExpectActualCheckingCompatibility.Compatible in compatibilityToMembersMap || + ExpectActualMatchingCompatibility.MatchedSuccessfully in compatibilityToMembersMap + ) { if (checkActual) { reporter.reportOn(source, FirErrors.ACTUAL_MISSING, context) } @@ -140,7 +143,7 @@ object FirExpectActualDeclarationChecker : FirBasicDeclarationChecker() { val singleIncompatibility = compatibilityToMembersMap.keys.singleOrNull() when { - singleIncompatibility is Incompatible.ClassScopes -> { + singleIncompatibility is ExpectActualCheckingCompatibility.ClassScopes -> { require(symbol is FirRegularClassSymbol || symbol is FirTypeAliasSymbol) { "Incompatible.ClassScopes is only possible for a class or a typealias: $declaration" } @@ -150,13 +153,13 @@ object FirExpectActualDeclarationChecker : FirBasicDeclarationChecker() { // This is needed only to reduce the number of errors. Incompatibility errors for those members will be reported // later when this checker is called for them fun hasSingleActualSuspect( - expectedWithIncompatibility: Pair, Map>, Collection>>> + expectedWithIncompatibility: Pair, Map>, Collection>>> ): Boolean { val (expectedMember, incompatibility) = expectedWithIncompatibility val actualMember = incompatibility.values.singleOrNull()?.singleOrNull() @OptIn(SymbolInternals::class) return actualMember != null && - !incompatibility.allStrongIncompatibilities() && + !incompatibility.allMismatches() && actualMember.fir.expectForActual?.values?.singleOrNull()?.singleOrNull() == expectedMember } @@ -167,9 +170,10 @@ object FirExpectActualDeclarationChecker : FirBasicDeclarationChecker() { } } - Compatible !in compatibilityToMembersMap -> { + ExpectActualCheckingCompatibility.Compatible !in compatibilityToMembersMap && + ExpectActualMatchingCompatibility.MatchedSuccessfully !in compatibilityToMembersMap -> { // A nicer diagnostic for functions with default params - if (declaration is FirFunction && compatibilityToMembersMap.keys.any { it is Incompatible.ActualFunctionWithDefaultParameters }) { + if (declaration is FirFunction && compatibilityToMembersMap.keys.any { it is ExpectActualCheckingCompatibility.ActualFunctionWithDefaultParameters }) { reporter.reportOn(declaration.source, FirErrors.ACTUAL_FUNCTION_WITH_DEFAULT_ARGUMENTS, context) } else if (requireActualModifier(declaration.symbol, context.session)) { reporter.reportOn( @@ -247,8 +251,8 @@ object FirExpectActualDeclarationChecker : FirBasicDeclarationChecker() { ) } - fun Map, *>.allStrongIncompatibilities(): Boolean { - return keys.all { it is Incompatible.StrongIncompatible } + fun Map, *>.allMismatches(): Boolean { + return keys.all { it is ExpectActualMatchingCompatibility.Mismatch } } // we don't require `actual` modifier on diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirPlatformIncompatibilityDiagnosticRenderer.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirPlatformIncompatibilityDiagnosticRenderer.kt index 4a0f62a7145..70a8c863ee0 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirPlatformIncompatibilityDiagnosticRenderer.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirPlatformIncompatibilityDiagnosticRenderer.kt @@ -18,14 +18,15 @@ package org.jetbrains.kotlin.fir.analysis.diagnostics import org.jetbrains.kotlin.diagnostics.rendering.ContextIndependentParameterRenderer import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol +import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCheckingCompatibility import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility -import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility.Incompatible +import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility.MismatchOrIncompatible class FirPlatformIncompatibilityDiagnosticRenderer( private val mode: MultiplatformDiagnosticRenderingMode -) : ContextIndependentParameterRenderer>, Collection>>> { +) : ContextIndependentParameterRenderer>, Collection>>> { override fun render( - obj: Map>, Collection>>, + obj: Map>, Collection>>, ): String { if (obj.isEmpty()) return "" @@ -43,9 +44,9 @@ class FirPlatformIncompatibilityDiagnosticRenderer( class FirIncompatibleExpectedActualClassScopesRenderer( private val mode: MultiplatformDiagnosticRenderingMode -) : ContextIndependentParameterRenderer, Map>, Collection>>>>> { +) : ContextIndependentParameterRenderer, Map>, Collection>>>>> { override fun render( - obj: List, Map>, Collection>>>> + obj: List, Map>, Collection>>>> ): String { if (obj.isEmpty()) return "" @@ -90,14 +91,14 @@ private fun StringBuilder.renderIncompatibilityInformation( append("The following declaration") if (descriptors.size == 1) append(" is") else append("s are") append(" incompatible") - (compatibility as? Incompatible)?.reason?.let { append(" because $it") } + (compatibility as? MismatchOrIncompatible)?.reason?.let { append(" because $it") } append(":") mode.renderList(this, descriptors.map { descriptor -> { mode.renderSymbol(this, descriptor, indent) } }) - if (compatibility is Incompatible.ClassScopes) { + if (compatibility is ExpectActualCheckingCompatibility.ClassScopes) { append(indent) append("No actual members are found for expected members listed below:") mode.newLine(this) diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/mpp/FirExpectActualMatchingContextImpl.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/mpp/FirExpectActualMatchingContextImpl.kt index f3c99a5bb1c..e6410271d34 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/mpp/FirExpectActualMatchingContextImpl.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/mpp/FirExpectActualMatchingContextImpl.kt @@ -29,6 +29,7 @@ import org.jetbrains.kotlin.resolve.calls.mpp.ExpectActualMatchingContext import org.jetbrains.kotlin.resolve.calls.mpp.ExpectActualMatchingContext.AnnotationCallInfo import org.jetbrains.kotlin.resolve.checkers.OptInNames import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility +import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualMatchingCompatibility import org.jetbrains.kotlin.types.AbstractTypeChecker import org.jetbrains.kotlin.types.TypeCheckerState import org.jetbrains.kotlin.types.Variance @@ -450,7 +451,7 @@ class FirExpectActualMatchingContextImpl private constructor( return symbol.source == null && symbol.origin !is FirDeclarationOrigin.Plugin } - override fun onMatchedMembers( + override fun onMatchedOrCompatibleMembers( expectSymbol: DeclarationSymbolMarker, actualSymbol: DeclarationSymbolMarker, containingExpectClassSymbol: RegularClassSymbolMarker?, @@ -462,13 +463,13 @@ class FirExpectActualMatchingContextImpl private constructor( expectSymbol.asSymbol(), actualSymbol.asSymbol(), containingExpectClassSymbol.asSymbol(), - ExpectActualCompatibility.Compatible + ExpectActualMatchingCompatibility.MatchedSuccessfully ) } - override fun onMismatchedMembersFromClassScope( + override fun onMismatchedOrIncompatibleMembersFromClassScope( expectSymbol: DeclarationSymbolMarker, - actualSymbolsByIncompatibility: Map, List>, + actualSymbolsByIncompatibility: Map, List>, containingExpectClassSymbol: RegularClassSymbolMarker?, containingActualClassSymbol: RegularClassSymbolMarker? ) { diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/mpp/FirExpectActualResolver.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/mpp/FirExpectActualResolver.kt index 64d0ffb3199..f3bc34b1678 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/mpp/FirExpectActualResolver.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/mpp/FirExpectActualResolver.kt @@ -18,6 +18,8 @@ import org.jetbrains.kotlin.fir.types.* import org.jetbrains.kotlin.mpp.CallableSymbolMarker import org.jetbrains.kotlin.resolve.calls.mpp.AbstractExpectActualChecker import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility +import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualMatchingCompatibility +import org.jetbrains.kotlin.resolve.multiplatform.getMatchedAndChecked object FirExpectActualResolver { fun findExpectForActual( @@ -67,9 +69,9 @@ object FirExpectActualResolver { ) }.let { // If there is a compatible entry, return a map only containing it - when (val compatibleSymbols = it[ExpectActualCompatibility.Compatible]) { + when (val compatibleSymbols = it.getMatchedAndChecked()) { null -> it - else -> mapOf>, _>(ExpectActualCompatibility.Compatible to compatibleSymbols) + else -> mapOf>, _>(ExpectActualMatchingCompatibility.MatchedSuccessfully to compatibleSymbols) } } } 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 04038bc4c53..31affdc0dfb 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 @@ -29,7 +29,7 @@ import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility object CommonBackendErrors { val NO_ACTUAL_FOR_EXPECT by error2() - val INCOMPATIBLE_MATCHING by error3>() + val INCOMPATIBLE_MATCHING by error3>() val ACTUAL_ANNOTATIONS_NOT_MATCH_EXPECT by warning3>() val EVALUATION_ERROR by error1() val ACTUAL_ANNOTATION_CONFLICTING_DEFAULT_ARGUMENT_VALUE by error1() @@ -76,7 +76,7 @@ object KtDefaultCommonBackendErrorMessages : BaseDiagnosticRendererFactory() { } object BackendDiagnosticRenderers { - val INCOMPATIBILITY = Renderer> { + val INCOMPATIBILITY = Renderer> { it.reason ?: "" } val SYMBOL_OWNER_DECLARATION_FQ_NAME = Renderer { 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 aa33e9882d6..96ec4c4ca93 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 @@ -258,9 +258,9 @@ private class ExpectActualLinkCollector : IrElementVisitor, List>, + actualSymbolsByIncompatibility: Map, List>, containingExpectClassSymbol: RegularClassSymbolMarker?, containingActualClassSymbol: RegularClassSymbolMarker?, ) { 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 7352409749f..2c8b046c622 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 @@ -113,7 +113,7 @@ internal fun KtDiagnosticReporterWithImplicitIrBasedContext.reportMissingActual( internal fun KtDiagnosticReporterWithImplicitIrBasedContext.reportIncompatibleExpectActual( expectSymbol: IrSymbol, actualSymbol: IrSymbol, - incompatibility: ExpectActualCompatibility.Incompatible<*> + incompatibility: ExpectActualCompatibility.MismatchOrIncompatible<*> ) { val expectDeclaration = expectSymbol.owner as IrDeclaration val actualDeclaration = actualSymbol.owner as IrDeclaration 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 index 1e1eb8f0023..4850877537c 100644 --- 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 @@ -480,7 +480,7 @@ internal abstract class IrExpectActualMatchingContext( } } - override fun onMatchedMembers( + override fun onMatchedOrCompatibleMembers( expectSymbol: DeclarationSymbolMarker, actualSymbol: DeclarationSymbolMarker, containingExpectClassSymbol: RegularClassSymbolMarker?, diff --git a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/mpp/AbstractExpectActualAnnotationMatchChecker.kt b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/mpp/AbstractExpectActualAnnotationMatchChecker.kt index ff48164ef0d..4b610da9259 100644 --- a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/mpp/AbstractExpectActualAnnotationMatchChecker.kt +++ b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/mpp/AbstractExpectActualAnnotationMatchChecker.kt @@ -315,7 +315,8 @@ object AbstractExpectActualAnnotationMatchChecker { // so we are sure that we found the right member. checkClassScopesCompatibility = false, ) - val expectMember = expectToCompatibilityMap.filter { it.value == ExpectActualCompatibility.Compatible }.keys.singleOrNull() + val expectMember = expectToCompatibilityMap + .filter { it.value is ExpectActualCompatibility.MatchedOrCompatible }.keys.singleOrNull() // Check also incompatible members if only one is found ?: expectToCompatibilityMap.keys.singleOrNull() ?: continue diff --git a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/mpp/AbstractExpectActualChecker.kt b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/mpp/AbstractExpectActualChecker.kt index dd89d56ceba..d473cc9a9a2 100644 --- a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/mpp/AbstractExpectActualChecker.kt +++ b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/mpp/AbstractExpectActualChecker.kt @@ -12,8 +12,10 @@ import org.jetbrains.kotlin.descriptors.Visibility import org.jetbrains.kotlin.mpp.* import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.SpecialNames +import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCheckingCompatibility import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility -import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility.Incompatible +import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility.MismatchOrIncompatible +import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualMatchingCompatibility import org.jetbrains.kotlin.types.model.KotlinTypeMarker import org.jetbrains.kotlin.types.model.TypeSubstitutorMarker import org.jetbrains.kotlin.utils.SmartList @@ -91,8 +93,8 @@ object AbstractExpectActualChecker { actualClassLikeSymbol: ClassLikeSymbolMarker, parentSubstitutor: TypeSubstitutorMarker?, checkClassScopes: Boolean, - ): ExpectActualCompatibility<*> = getClassifiersIncompatibility(expectClassSymbol, actualClassLikeSymbol, parentSubstitutor, checkClassScopes) - ?: ExpectActualCompatibility.Compatible + ): ExpectActualCheckingCompatibility<*> = getClassifiersIncompatibility(expectClassSymbol, actualClassLikeSymbol, parentSubstitutor, checkClassScopes) + ?: ExpectActualCheckingCompatibility.Compatible context(ExpectActualMatchingContext<*>) @Suppress("warnings") @@ -101,7 +103,7 @@ object AbstractExpectActualChecker { actualClassLikeSymbol: ClassLikeSymbolMarker, parentSubstitutor: TypeSubstitutorMarker?, checkClassScopesCompatibility: Boolean, - ): ExpectActualCompatibility.Incompatible.WeakIncompatible<*>? { + ): ExpectActualCheckingCompatibility.Incompatible<*>? { // 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) { "This function should be invoked only for declarations with the same name: $expectClassSymbol, $actualClassLikeSymbol" @@ -114,28 +116,28 @@ object AbstractExpectActualChecker { else -> error("Incorrect actual classifier for $expectClassSymbol: $actualClassLikeSymbol") } - if (!areCompatibleClassKinds(expectClassSymbol, actualClass)) return Incompatible.ClassKind + if (!areCompatibleClassKinds(expectClassSymbol, actualClass)) return ExpectActualCheckingCompatibility.ClassKind if (!equalBy(expectClassSymbol, actualClass) { listOf(it.isCompanion, it.isInner, it.isInline || it.isValue) }) { - return Incompatible.ClassModifiers + return ExpectActualCheckingCompatibility.ClassModifiers } if (expectClassSymbol.isFun && !actualClass.isFun && actualClass.isNotSamInterface()) { - return Incompatible.FunInterfaceModifier + return ExpectActualCheckingCompatibility.FunInterfaceModifier } val expectTypeParameterSymbols = expectClassSymbol.typeParameters val actualTypeParameterSymbols = actualClass.typeParameters if (expectTypeParameterSymbols.size != actualTypeParameterSymbols.size) { - return Incompatible.ClassTypeParameterCount + return ExpectActualCheckingCompatibility.ClassTypeParameterCount } if (!areCompatibleModalities(expectClassSymbol.modality, actualClass.modality)) { - return Incompatible.Modality + return ExpectActualCheckingCompatibility.Modality } if (!areCompatibleClassVisibilities(expectClassSymbol, actualClass)) { - return Incompatible.Visibility + return ExpectActualCheckingCompatibility.Visibility } val substitutor = createExpectActualTypeParameterSubstitutor( @@ -145,14 +147,14 @@ object AbstractExpectActualChecker { ) if (!areCompatibleTypeParameterUpperBounds(expectTypeParameterSymbols, actualTypeParameterSymbols, substitutor)) { - return Incompatible.ClassTypeParameterUpperBounds + return ExpectActualCheckingCompatibility.ClassTypeParameterUpperBounds } getTypeParametersVarianceOrReifiedIncompatibility(expectTypeParameterSymbols, actualTypeParameterSymbols) ?.let { return it } if (!areCompatibleSupertypes(expectClassSymbol, actualClass, substitutor)) { - return Incompatible.Supertypes + return ExpectActualCheckingCompatibility.Supertypes } if (checkClassScopesCompatibility) { @@ -213,8 +215,8 @@ object AbstractExpectActualChecker { expectClassSymbol: RegularClassSymbolMarker, actualClassSymbol: RegularClassSymbolMarker, substitutor: TypeSubstitutorMarker, - ): Incompatible.WeakIncompatible<*>? { - val unfulfilled = arrayListOf, List>>>() + ): ExpectActualCheckingCompatibility.Incompatible<*>? { + val unfulfilled = arrayListOf, List>>>() val actualMembersByName = actualClassSymbol.collectAllMembers(isActualDeclaration = true).groupBy { it.name } @@ -241,14 +243,14 @@ object AbstractExpectActualChecker { val aEntries = expectClassSymbol.collectEnumEntryNames() val bEntries = actualClassSymbol.collectEnumEntryNames() - if (!bEntries.containsAll(aEntries)) return Incompatible.EnumEntries + if (!bEntries.containsAll(aEntries)) return ExpectActualCheckingCompatibility.EnumEntries } // TODO: check static scope? if (unfulfilled.isEmpty()) return null - return Incompatible.ClassScopes(unfulfilled) + return ExpectActualCheckingCompatibility.ClassScopes(unfulfilled) } context(ExpectActualMatchingContext<*>) @@ -258,7 +260,7 @@ object AbstractExpectActualChecker { substitutor: TypeSubstitutorMarker?, expectClassSymbol: RegularClassSymbolMarker?, actualClassSymbol: RegularClassSymbolMarker?, - unfulfilled: MutableList, List>>>?, + unfulfilled: MutableList, List>>>?, checkClassScopesCompatibility: Boolean, ) { val mapping = actualMembers.keysToMap { actualMember -> @@ -284,20 +286,20 @@ object AbstractExpectActualChecker { } } - val incompatibilityMap = mutableMapOf, MutableList>() + val incompatibilityMap = mutableMapOf, MutableList>() for ((actualMember, compatibility) in mapping) { when (compatibility) { - ExpectActualCompatibility.Compatible -> { - onMatchedMembers(expectMember, actualMember, expectClassSymbol, actualClassSymbol) + is ExpectActualCompatibility.MatchedOrCompatible<*> -> { + onMatchedOrCompatibleMembers(expectMember, actualMember, expectClassSymbol, actualClassSymbol) return } - is Incompatible -> incompatibilityMap.getOrPut(compatibility) { SmartList() }.add(actualMember) + is MismatchOrIncompatible<*> -> incompatibilityMap.getOrPut(compatibility) { SmartList() }.add(actualMember) } } unfulfilled?.add(expectMember to incompatibilityMap) - onMismatchedMembersFromClassScope(expectMember, incompatibilityMap, expectClassSymbol, actualClassSymbol) + onMismatchedOrIncompatibleMembersFromClassScope(expectMember, incompatibilityMap, expectClassSymbol, actualClassSymbol) } context(ExpectActualMatchingContext<*>) @@ -325,7 +327,7 @@ object AbstractExpectActualChecker { expectDeclaration is ConstructorSymbolMarker && actualDeclaration is ConstructorSymbolMarker ) { - return ExpectActualCompatibility.Compatible + return ExpectActualCheckingCompatibility.Compatible } // We must prioritize to return STRONG incompatible over WEAK incompatible (because STRONG incompatibility allows to search for overloads) @@ -339,7 +341,7 @@ object AbstractExpectActualChecker { expectContainingClass, actualContainingClass ) - ?: ExpectActualCompatibility.Compatible + ?: ExpectActualCheckingCompatibility.Compatible } context(ExpectActualMatchingContext<*>) @@ -348,27 +350,27 @@ object AbstractExpectActualChecker { actualDeclaration: CallableSymbolMarker, insideAnnotationClass: Boolean, parentSubstitutor: TypeSubstitutorMarker?, - ): Incompatible.StrongIncompatible<*>? { + ): ExpectActualMatchingCompatibility.Mismatch? { if (expectDeclaration is FunctionSymbolMarker != actualDeclaration is FunctionSymbolMarker) { - return Incompatible.CallableKind + return ExpectActualMatchingCompatibility.CallableKind } val expectedReceiverType = expectDeclaration.extensionReceiverType val actualReceiverType = actualDeclaration.extensionReceiverType if ((expectedReceiverType != null) != (actualReceiverType != null)) { - return Incompatible.ParameterShape + return ExpectActualMatchingCompatibility.ParameterShape } val expectedValueParameters = expectDeclaration.valueParameters val actualValueParameters = actualDeclaration.valueParameters if (!valueParametersCountCompatible(expectDeclaration, actualDeclaration, expectedValueParameters, actualValueParameters)) { - return Incompatible.ParameterCount + return ExpectActualMatchingCompatibility.ParameterCount } val expectedTypeParameters = expectDeclaration.typeParameters val actualTypeParameters = actualDeclaration.typeParameters if (expectedTypeParameters.size != actualTypeParameters.size) { - return Incompatible.FunctionTypeParameterCount + return ExpectActualMatchingCompatibility.FunctionTypeParameterCount } val substitutor = createExpectActualTypeParameterSubstitutor( @@ -388,11 +390,11 @@ object AbstractExpectActualChecker { parameterOfAnnotationComparisonMode = false ) ) { - return Incompatible.ParameterTypes + return ExpectActualMatchingCompatibility.ParameterTypes } if (!areCompatibleTypeParameterUpperBounds(expectedTypeParameters, actualTypeParameters, substitutor)) { - return Incompatible.FunctionTypeParameterUpperBounds + return ExpectActualMatchingCompatibility.FunctionTypeParameterUpperBounds } return null @@ -406,7 +408,7 @@ object AbstractExpectActualChecker { parentSubstitutor: TypeSubstitutorMarker?, expectContainingClass: RegularClassSymbolMarker?, actualContainingClass: RegularClassSymbolMarker?, - ): Incompatible.WeakIncompatible<*>? { + ): ExpectActualCheckingCompatibility<*>? { val expectedTypeParameters = expectDeclaration.typeParameters val actualTypeParameters = actualDeclaration.typeParameters val expectedValueParameters = expectDeclaration.valueParameters @@ -425,16 +427,16 @@ object AbstractExpectActualChecker { parameterOfAnnotationComparisonMode = insideAnnotationClass ) ) { - return Incompatible.ReturnType + return ExpectActualCheckingCompatibility.ReturnType } } if (actualDeclaration.hasStableParameterNames && !equalsBy(expectedValueParameters, actualValueParameters) { it.name }) { - return Incompatible.ParameterNames + return ExpectActualCheckingCompatibility.ParameterNames } if (!equalsBy(expectedTypeParameters, actualTypeParameters) { it.name }) { - return Incompatible.TypeParameterNames + return ExpectActualCheckingCompatibility.TypeParameterNames } val expectModality = expectDeclaration.modality @@ -447,11 +449,11 @@ object AbstractExpectActualChecker { actualContainingClass?.modality ) ) { - return Incompatible.Modality + return ExpectActualCheckingCompatibility.Modality } if (!areCompatibleCallableVisibilities(expectDeclaration.visibility, expectModality, actualDeclaration.visibility)) { - return Incompatible.Visibility + return ExpectActualCheckingCompatibility.Visibility } getTypeParametersVarianceOrReifiedIncompatibility(expectedTypeParameters, actualTypeParameters)?.let { return it } @@ -467,22 +469,22 @@ object AbstractExpectActualChecker { (actualDeclaration.allOverriddenDeclarationsRecursive() - expectDeclaration.allOverriddenDeclarationsRecursive().toSet()) .flatMap { it.valueParameters }.any { it.hasDefaultValue } ) { - return Incompatible.ActualFunctionWithDefaultParameters + return ExpectActualCheckingCompatibility.ActualFunctionWithDefaultParameters } } } if (!equalsBy(expectedValueParameters, actualValueParameters) { it.isVararg }) { - return Incompatible.ValueParameterVararg + return ExpectActualCheckingCompatibility.ValueParameterVararg } // Adding noinline/crossinline to parameters is disallowed, except if the expected declaration was not inline at all if (expectDeclaration is SimpleFunctionSymbolMarker && expectDeclaration.isInline) { if (expectedValueParameters.indices.any { i -> !expectedValueParameters[i].isNoinline && actualValueParameters[i].isNoinline }) { - return Incompatible.ValueParameterNoinline + return ExpectActualCheckingCompatibility.ValueParameterNoinline } if (expectedValueParameters.indices.any { i -> !expectedValueParameters[i].isCrossinline && actualValueParameters[i].isCrossinline }) { - return Incompatible.ValueParameterCrossinline + return ExpectActualCheckingCompatibility.ValueParameterCrossinline } } @@ -634,9 +636,9 @@ object AbstractExpectActualChecker { private fun getTypeParametersVarianceOrReifiedIncompatibility( expectTypeParameterSymbols: List, actualTypeParameterSymbols: List, - ): Incompatible.WeakIncompatible<*>? { + ): ExpectActualCheckingCompatibility.Incompatible<*>? { if (!equalsBy(expectTypeParameterSymbols, actualTypeParameterSymbols) { it.variance }) { - return Incompatible.TypeParameterVariance + return ExpectActualCheckingCompatibility.TypeParameterVariance } // Removing "reified" from an expected function's type parameter is fine @@ -645,7 +647,7 @@ object AbstractExpectActualChecker { !expectTypeParameterSymbols[i].isReified && actualTypeParameterSymbols[i].isReified } ) { - return Incompatible.TypeParameterReified + return ExpectActualCheckingCompatibility.TypeParameterReified } return null @@ -655,9 +657,9 @@ object AbstractExpectActualChecker { private fun getFunctionsIncompatibility( expectFunction: CallableSymbolMarker, actualFunction: CallableSymbolMarker, - ): Incompatible.WeakIncompatible<*>? { + ): ExpectActualCheckingCompatibility<*>? { if (!equalBy(expectFunction, actualFunction) { f -> f.isSuspend }) { - return Incompatible.FunctionModifiersDifferent + return ExpectActualCheckingCompatibility.FunctionModifiersDifferent } if ( @@ -665,7 +667,7 @@ object AbstractExpectActualChecker { expectFunction.isInline && !actualFunction.isInline || expectFunction.isOperator && !actualFunction.isOperator ) { - return Incompatible.FunctionModifiersNotSubset + return ExpectActualCheckingCompatibility.FunctionModifiersNotSubset } return null @@ -675,12 +677,12 @@ object AbstractExpectActualChecker { private fun getPropertiesIncompatibility( expected: PropertySymbolMarker, actual: PropertySymbolMarker, - ): Incompatible.WeakIncompatible<*>? { + ): ExpectActualCheckingCompatibility<*>? { return when { - !equalBy(expected, actual) { p -> p.isVar } -> Incompatible.PropertyKind - !equalBy(expected, actual) { p -> p.isLateinit } -> Incompatible.PropertyLateinitModifier - expected.isConst && !actual.isConst -> Incompatible.PropertyConstModifier - !arePropertySettersWithCompatibleVisibilities(expected, actual) -> Incompatible.PropertySetterVisibility + !equalBy(expected, actual) { p -> p.isVar } -> ExpectActualCheckingCompatibility.PropertyKind + !equalBy(expected, actual) { p -> p.isLateinit } -> ExpectActualCheckingCompatibility.PropertyLateinitModifier + expected.isConst && !actual.isConst -> ExpectActualCheckingCompatibility.PropertyConstModifier + !arePropertySettersWithCompatibleVisibilities(expected, actual) -> ExpectActualCheckingCompatibility.PropertySetterVisibility else -> null } } diff --git a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/mpp/AbstractExpectActualMatcher.kt b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/mpp/AbstractExpectActualMatcher.kt index c151fd6baef..9b883f91a36 100644 --- a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/mpp/AbstractExpectActualMatcher.kt +++ b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/mpp/AbstractExpectActualMatcher.kt @@ -10,7 +10,8 @@ import org.jetbrains.kotlin.mpp.* import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.SpecialNames import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility -import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility.Incompatible +import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility.MismatchOrIncompatible +import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualMatchingCompatibility import org.jetbrains.kotlin.types.model.KotlinTypeMarker import org.jetbrains.kotlin.types.model.TypeSubstitutorMarker import org.jetbrains.kotlin.utils.SmartList @@ -66,11 +67,11 @@ object AbstractExpectActualMatcher { } } - fun matchClassifiers( + fun matchClassifiers( expectClassSymbol: RegularClassSymbolMarker, actualClassLikeSymbol: ClassLikeSymbolMarker, - context: ExpectActualMatchingContext, - ): ExpectActualCompatibility = with(context) { + context: ExpectActualMatchingContext<*>, + ): ExpectActualMatchingCompatibility = with(context) { // Can't check FQ names here because nested expected class may be implemented via actual typealias's expansion with the other FQ name check(expectClassSymbol.name == actualClassLikeSymbol.name) { "This function should be invoked only for declarations with the same name: $expectClassSymbol, $actualClassLikeSymbol" @@ -78,7 +79,7 @@ object AbstractExpectActualMatcher { check(actualClassLikeSymbol is RegularClassSymbolMarker || actualClassLikeSymbol is TypeAliasSymbolMarker) { "Incorrect actual classifier for $expectClassSymbol: $actualClassLikeSymbol" } - ExpectActualCompatibility.Compatible + ExpectActualMatchingCompatibility.MatchedSuccessfully } context(ExpectActualMatchingContext<*>) @@ -117,7 +118,7 @@ object AbstractExpectActualMatcher { substitutor: TypeSubstitutorMarker?, expectClassSymbol: RegularClassSymbolMarker?, actualClassSymbol: RegularClassSymbolMarker?, - unfulfilled: MutableList, List>>>?, + unfulfilled: MutableList, List>>>?, ) { val mapping = actualMembers.keysToMap { actualMember -> when (expectMember) { @@ -136,20 +137,20 @@ object AbstractExpectActualMatcher { } } - val incompatibilityMap = mutableMapOf, MutableList>() + val incompatibilityMap = mutableMapOf, MutableList>() for ((actualMember, compatibility) in mapping) { when (compatibility) { - ExpectActualCompatibility.Compatible -> { - onMatchedMembers(expectMember, actualMember, expectClassSymbol, actualClassSymbol) + ExpectActualMatchingCompatibility.MatchedSuccessfully -> { + onMatchedOrCompatibleMembers(expectMember, actualMember, expectClassSymbol, actualClassSymbol) return } - is Incompatible -> incompatibilityMap.getOrPut(compatibility) { SmartList() }.add(actualMember) + is ExpectActualMatchingCompatibility.Mismatch -> incompatibilityMap.getOrPut(compatibility) { SmartList() }.add(actualMember) } } unfulfilled?.add(expectMember to incompatibilityMap) - onMismatchedMembersFromClassScope(expectMember, incompatibilityMap, expectClassSymbol, actualClassSymbol) + onMismatchedOrIncompatibleMembersFromClassScope(expectMember, incompatibilityMap, expectClassSymbol, actualClassSymbol) } context(ExpectActualMatchingContext<*>) @@ -159,7 +160,7 @@ object AbstractExpectActualMatcher { parentSubstitutor: TypeSubstitutorMarker?, expectContainingClass: RegularClassSymbolMarker?, actualContainingClass: RegularClassSymbolMarker?, - ): ExpectActualCompatibility<*> { + ): ExpectActualMatchingCompatibility { require( (expectDeclaration is ConstructorSymbolMarker && actualDeclaration is ConstructorSymbolMarker) || expectDeclaration.callableId.callableName == actualDeclaration.callableId.callableName @@ -177,12 +178,12 @@ object AbstractExpectActualMatcher { expectDeclaration is ConstructorSymbolMarker && actualDeclaration is ConstructorSymbolMarker ) { - return ExpectActualCompatibility.Compatible + return ExpectActualMatchingCompatibility.MatchedSuccessfully } val annotationMode = expectContainingClass?.classKind == ClassKind.ANNOTATION_CLASS return getCallablesStrongIncompatibility(expectDeclaration, actualDeclaration, annotationMode, parentSubstitutor) - ?: ExpectActualCompatibility.Compatible + ?: ExpectActualMatchingCompatibility.MatchedSuccessfully } context(ExpectActualMatchingContext<*>) @@ -191,27 +192,27 @@ object AbstractExpectActualMatcher { actualDeclaration: CallableSymbolMarker, insideAnnotationClass: Boolean, parentSubstitutor: TypeSubstitutorMarker?, - ): Incompatible.StrongIncompatible<*>? { + ): ExpectActualMatchingCompatibility.Mismatch? { if (expectDeclaration is FunctionSymbolMarker != actualDeclaration is FunctionSymbolMarker) { - return Incompatible.CallableKind + return ExpectActualMatchingCompatibility.CallableKind } val expectedReceiverType = expectDeclaration.extensionReceiverType val actualReceiverType = actualDeclaration.extensionReceiverType if ((expectedReceiverType != null) != (actualReceiverType != null)) { - return Incompatible.ParameterShape + return ExpectActualMatchingCompatibility.ParameterShape } val expectedValueParameters = expectDeclaration.valueParameters val actualValueParameters = actualDeclaration.valueParameters if (!valueParametersCountCompatible(expectDeclaration, actualDeclaration, expectedValueParameters, actualValueParameters)) { - return Incompatible.ParameterCount + return ExpectActualMatchingCompatibility.ParameterCount } val expectedTypeParameters = expectDeclaration.typeParameters val actualTypeParameters = actualDeclaration.typeParameters if (expectedTypeParameters.size != actualTypeParameters.size) { - return Incompatible.FunctionTypeParameterCount + return ExpectActualMatchingCompatibility.FunctionTypeParameterCount } val substitutor = createExpectActualTypeParameterSubstitutor( @@ -231,11 +232,11 @@ object AbstractExpectActualMatcher { parameterOfAnnotationComparisonMode = false ) ) { - return Incompatible.ParameterTypes + return ExpectActualMatchingCompatibility.ParameterTypes } if (!areCompatibleTypeParameterUpperBounds(expectedTypeParameters, actualTypeParameters, substitutor)) { - return Incompatible.FunctionTypeParameterUpperBounds + return ExpectActualMatchingCompatibility.FunctionTypeParameterUpperBounds } return null 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 5c3d7e5d61d..9942527bed3 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 @@ -165,16 +165,16 @@ interface ExpectActualMatchingContext : TypeSystemC val CallableSymbolMarker.hasStableParameterNames: Boolean - fun onMatchedMembers( + fun onMatchedOrCompatibleMembers( expectSymbol: DeclarationSymbolMarker, actualSymbol: DeclarationSymbolMarker, containingExpectClassSymbol: RegularClassSymbolMarker?, containingActualClassSymbol: RegularClassSymbolMarker?, ) {} - fun onMismatchedMembersFromClassScope( + fun onMismatchedOrIncompatibleMembersFromClassScope( expectSymbol: DeclarationSymbolMarker, - actualSymbolsByIncompatibility: Map, List>, + actualSymbolsByIncompatibility: Map, List>, containingExpectClassSymbol: RegularClassSymbolMarker?, containingActualClassSymbol: RegularClassSymbolMarker?, ) {} 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 239e394a3b1..6eeb9a2d8df 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,103 +5,104 @@ package org.jetbrains.kotlin.resolve.multiplatform -import kotlin.contracts.ExperimentalContracts -import kotlin.contracts.contract +private const val TYPE_PARAMETER_COUNT = "number of type parameters is different" -sealed class ExpectActualCompatibility { - - // Note that the reason is used in the diagnostic output, see PlatformIncompatibilityDiagnosticRenderer - sealed class Incompatible(val reason: String?) : ExpectActualCompatibility() { - - sealed class WeakIncompatible(reason: String?) : Incompatible(reason) - - // For StrongIncompatible `actual` declaration is considered as overload and error reports on expected declaration - sealed class StrongIncompatible(reason: String?) : Incompatible(reason) - - // Callables - - object CallableKind : StrongIncompatible("callable kinds are different (function vs property)") - - object ParameterShape : StrongIncompatible("parameter shapes are different (extension vs non-extension)") - - object ParameterCount : StrongIncompatible("number of value parameters is different") - - // FunctionTypeParameterCount is strong because functions can be overloaded by type parameter count - object FunctionTypeParameterCount : StrongIncompatible("number of type parameters is different") - - // ClassTypeParameterCount is weak because classes cannot be overloaded - object ClassTypeParameterCount : WeakIncompatible(FunctionTypeParameterCount.reason) - - object ParameterTypes : StrongIncompatible("parameter types are different") - object ReturnType : WeakIncompatible("return type is different") - - object ParameterNames : WeakIncompatible("parameter names are different") - object TypeParameterNames : WeakIncompatible("names of type parameters are different") - - object ValueParameterVararg : WeakIncompatible("some value parameter is vararg in one declaration and non-vararg in the other") - object ValueParameterNoinline : WeakIncompatible( - "some value parameter is noinline in one declaration and not noinline in the other" - ) - - object ValueParameterCrossinline : WeakIncompatible( - "some value parameter is crossinline in one declaration and not crossinline in the other" - ) - - // Functions - - object FunctionModifiersDifferent : WeakIncompatible("modifiers are different (suspend)") - object FunctionModifiersNotSubset : WeakIncompatible( - "some modifiers on expected declaration are missing on the actual one (infix, inline, operator)" - ) - object ActualFunctionWithDefaultParameters : - WeakIncompatible("actual function cannot have default argument values, they should be declared in the expected function") - - // Properties - - object PropertyKind : WeakIncompatible("property kinds are different (val vs var)") - object PropertyLateinitModifier : WeakIncompatible("modifiers are different (lateinit)") - object PropertyConstModifier : WeakIncompatible("modifiers are different (const)") - object PropertySetterVisibility : WeakIncompatible("setter visibility is different") - - // Classifiers - - object ClassKind : WeakIncompatible("class kinds are different (class, interface, object, enum, annotation)") - - object ClassModifiers : WeakIncompatible("modifiers are different (companion, inner, inline, value)") - - object FunInterfaceModifier : WeakIncompatible("actual declaration for fun expect interface is not a functional interface") - - object Supertypes : WeakIncompatible("some supertypes are missing in the actual declaration") - - class ClassScopes( - val unfulfilled: List, Collection>>> - ) : WeakIncompatible("some expected members have no actual ones") - - object EnumEntries : WeakIncompatible("some entries from expected enum are missing in the actual enum") - - // Common - - object Modality : WeakIncompatible("modality is different") - object Visibility : WeakIncompatible("visibility is different") - - // FunctionTypeParameterUpperBounds is weak because functions can be overloaded by type parameter upper bounds - object FunctionTypeParameterUpperBounds : StrongIncompatible("upper bounds of type parameters are different") - - // ClassTypeParameterUpperBounds is strong because classes cannot be overloaded - object ClassTypeParameterUpperBounds : WeakIncompatible(FunctionTypeParameterUpperBounds.reason) - - object TypeParameterVariance : WeakIncompatible("declaration-site variances of type parameters are different") - object TypeParameterReified : WeakIncompatible( - "some type parameter is reified in one declaration and non-reified in the other" - ) +// Note that the reason is used in the diagnostic output, see PlatformIncompatibilityDiagnosticRenderer +/** + * DON'T USE THIS CLASS. This class is currently used only in diagnostics. Eventually, it will go away KT-62631 + */ +sealed interface ExpectActualCompatibility { + /** + * DON'T USE THIS CLASS. This class is currently used only in diagnostics. Eventually, it will go away KT-62631 + */ + sealed interface MismatchOrIncompatible : ExpectActualCompatibility { + val reason: String? } - object Compatible : ExpectActualCompatibility() + // It's temporary class. KT-62590 is in progress + sealed interface MatchedOrCompatible : ExpectActualCompatibility +} + +/** + * All mismatches that can be fixed by introducing an overload without this mismatch. + * In other words: "overloadable" mismatches + * + * @see ExpectActualCheckingCompatibility + */ +sealed class ExpectActualMatchingCompatibility : ExpectActualCompatibility { + sealed class Mismatch(override val reason: String?) : ExpectActualMatchingCompatibility(), + ExpectActualCompatibility.MismatchOrIncompatible + + object CallableKind : Mismatch("callable kinds are different (function vs property)") + object ParameterShape : Mismatch("parameter shapes are different (extension vs non-extension)") + object ParameterCount : Mismatch("number of value parameters is different") + object FunctionTypeParameterCount : Mismatch(TYPE_PARAMETER_COUNT) + object ParameterTypes : Mismatch("parameter types are different") + object FunctionTypeParameterUpperBounds : Mismatch("upper bounds of type parameters are different") + object MatchedSuccessfully : ExpectActualMatchingCompatibility(), ExpectActualCompatibility.MatchedOrCompatible +} + +/** + * "Non-overloadable" compatibilities + * + * @see ExpectActualMatchingCompatibility + */ +sealed class ExpectActualCheckingCompatibility : ExpectActualCompatibility { + sealed class Incompatible(override val reason: String?) : ExpectActualCheckingCompatibility(), + ExpectActualCompatibility.MismatchOrIncompatible + + object ClassTypeParameterCount : Incompatible(TYPE_PARAMETER_COUNT) + + // Callables + object ReturnType : Incompatible("return type is different") + object ParameterNames : Incompatible("parameter names are different") + object TypeParameterNames : Incompatible("names of type parameters are different") + object ValueParameterVararg : Incompatible("some value parameter is vararg in one declaration and non-vararg in the other") + object ValueParameterNoinline : + Incompatible("some value parameter is noinline in one declaration and not noinline in the other") + object ValueParameterCrossinline : + Incompatible("some value parameter is crossinline in one declaration and not crossinline in the other") + + // Functions + object FunctionModifiersDifferent : Incompatible("modifiers are different (suspend)") + object FunctionModifiersNotSubset : + Incompatible("some modifiers on expected declaration are missing on the actual one (infix, inline, operator)") + object ActualFunctionWithDefaultParameters : + Incompatible("actual function cannot have default argument values, they should be declared in the expected function") + + // Properties + object PropertyKind : Incompatible("property kinds are different (val vs var)") + object PropertyLateinitModifier : Incompatible("modifiers are different (lateinit)") + object PropertyConstModifier : Incompatible("modifiers are different (const)") + object PropertySetterVisibility : Incompatible("setter visibility is different") + + // Classifiers + object ClassKind : Incompatible("class kinds are different (class, interface, object, enum, annotation)") + object ClassModifiers : Incompatible("modifiers are different (companion, inner, inline, value)") + object FunInterfaceModifier : Incompatible("actual declaration for fun expect interface is not a functional interface") + object Supertypes : Incompatible("some supertypes are missing in the actual declaration") + class ClassScopes( + val unfulfilled: List, Collection>>> + ) : Incompatible("some expected members have no actual ones") + object EnumEntries : Incompatible("some entries from expected enum are missing in the actual enum") + + // Common + object Modality : Incompatible("modality is different") + object Visibility : Incompatible("visibility is different") + + object ClassTypeParameterUpperBounds : Incompatible(ExpectActualMatchingCompatibility.FunctionTypeParameterUpperBounds.reason) + object TypeParameterVariance : Incompatible("declaration-site variances of type parameters are different") + object TypeParameterReified : Incompatible("some type parameter is reified in one declaration and non-reified in the other") + object Compatible : ExpectActualCheckingCompatibility(), ExpectActualCompatibility.MatchedOrCompatible } val ExpectActualCompatibility<*>.isCompatibleOrWeaklyIncompatible: Boolean - get() = this is ExpectActualCompatibility.Compatible - || this is ExpectActualCompatibility.Incompatible.WeakIncompatible + get() = this is ExpectActualCompatibility.MatchedOrCompatible || this is ExpectActualCheckingCompatibility val ExpectActualCompatibility<*>.compatible: Boolean - get() = this == ExpectActualCompatibility.Compatible + get() = this is ExpectActualCompatibility.MatchedOrCompatible + +// It's temporary function. KT-62590 is in progress +fun Map, List>.getMatchedAndChecked(): List? = + (get(ExpectActualMatchingCompatibility.MatchedSuccessfully).orEmpty() + get(ExpectActualCheckingCompatibility.Compatible).orEmpty()) + .takeIf { it.isNotEmpty() } diff --git a/core/compiler.common/src/org/jetbrains/kotlin/resolve/multiplatform/ExpectActualMemberDiff.kt b/core/compiler.common/src/org/jetbrains/kotlin/resolve/multiplatform/ExpectActualMemberDiff.kt index 1a02c185a0a..90a44aa99a0 100644 --- a/core/compiler.common/src/org/jetbrains/kotlin/resolve/multiplatform/ExpectActualMemberDiff.kt +++ b/core/compiler.common/src/org/jetbrains/kotlin/resolve/multiplatform/ExpectActualMemberDiff.kt @@ -56,39 +56,39 @@ data class ExpectActualMemberDiff(val kind: Kind, val actualMember } } -fun ExpectActualCompatibility.Incompatible<*>.toMemberDiffKind(): ExpectActualMemberDiff.Kind? = when (this) { - ExpectActualCompatibility.Incompatible.CallableKind, - ExpectActualCompatibility.Incompatible.ParameterCount, - ExpectActualCompatibility.Incompatible.ParameterShape, - ExpectActualCompatibility.Incompatible.ParameterTypes, - ExpectActualCompatibility.Incompatible.FunctionTypeParameterCount, - ExpectActualCompatibility.Incompatible.FunctionTypeParameterUpperBounds, +fun ExpectActualCompatibility.MismatchOrIncompatible<*>.toMemberDiffKind(): ExpectActualMemberDiff.Kind? = when (this) { + ExpectActualMatchingCompatibility.CallableKind, + ExpectActualMatchingCompatibility.ParameterCount, + ExpectActualMatchingCompatibility.ParameterShape, + ExpectActualMatchingCompatibility.ParameterTypes, + ExpectActualMatchingCompatibility.FunctionTypeParameterCount, + ExpectActualMatchingCompatibility.FunctionTypeParameterUpperBounds, // It's an awful API. It will be fixed in KT-62752 -> error("It's not allowed to call this function with receiver: $this") - ExpectActualCompatibility.Incompatible.ReturnType -> ExpectActualMemberDiff.Kind.ReturnTypeChangedInOverride - ExpectActualCompatibility.Incompatible.ClassTypeParameterCount -> error("Not applicable because ExpectActualMemberDiff is about members") - ExpectActualCompatibility.Incompatible.ClassTypeParameterUpperBounds -> error("Not applicable because ExpectActualMemberDiff is about members") - ExpectActualCompatibility.Incompatible.ActualFunctionWithDefaultParameters -> null // It's not possible to add default parameters in override - ExpectActualCompatibility.Incompatible.ClassKind -> error("Not applicable because ExpectActualMemberDiff is about members") - ExpectActualCompatibility.Incompatible.ClassModifiers -> error("Not applicable because ExpectActualMemberDiff is about members") - is ExpectActualCompatibility.Incompatible.ClassScopes -> error("Not applicable because ExpectActualMemberDiff is about members") - ExpectActualCompatibility.Incompatible.EnumEntries -> error("Not applicable because ExpectActualMemberDiff is about members") - ExpectActualCompatibility.Incompatible.FunInterfaceModifier -> error("Not applicable because ExpectActualMemberDiff is about members") - ExpectActualCompatibility.Incompatible.FunctionModifiersDifferent -> null // It's not possible to override with different function modifier (suspend) - ExpectActualCompatibility.Incompatible.FunctionModifiersNotSubset -> null // It's not possible to override with different function modifier (infix, inline, operator) - ExpectActualCompatibility.Incompatible.Modality -> ExpectActualMemberDiff.Kind.ModalityChangedInOverride - ExpectActualCompatibility.Incompatible.ParameterNames -> ExpectActualMemberDiff.Kind.ParameterNameChangedInOverride - ExpectActualCompatibility.Incompatible.PropertyConstModifier -> null // const fun can't be overridden - ExpectActualCompatibility.Incompatible.PropertyKind -> ExpectActualMemberDiff.Kind.PropertyKindChangedInOverride - ExpectActualCompatibility.Incompatible.PropertyLateinitModifier -> ExpectActualMemberDiff.Kind.LateinitChangedInOverride - ExpectActualCompatibility.Incompatible.PropertySetterVisibility -> ExpectActualMemberDiff.Kind.SetterVisibilityChangedInOverride - ExpectActualCompatibility.Incompatible.Supertypes -> error("Not applicable because ExpectActualMemberDiff is about members") - ExpectActualCompatibility.Incompatible.TypeParameterNames -> ExpectActualMemberDiff.Kind.TypeParameterNamesChangedInOverride - ExpectActualCompatibility.Incompatible.TypeParameterReified -> null // inline fun can't be overridden - ExpectActualCompatibility.Incompatible.TypeParameterVariance -> null // Members are not allowed to have variance - ExpectActualCompatibility.Incompatible.ValueParameterCrossinline -> null // inline fun can't be overridden - ExpectActualCompatibility.Incompatible.ValueParameterNoinline -> null // inline fun can't be overridden - ExpectActualCompatibility.Incompatible.ValueParameterVararg -> ExpectActualMemberDiff.Kind.VarargChangedInOverride - ExpectActualCompatibility.Incompatible.Visibility -> ExpectActualMemberDiff.Kind.VisibilityChangedInOverride + ExpectActualCheckingCompatibility.ReturnType -> ExpectActualMemberDiff.Kind.ReturnTypeChangedInOverride + ExpectActualCheckingCompatibility.ClassTypeParameterCount -> error("Not applicable because ExpectActualMemberDiff is about members") + ExpectActualCheckingCompatibility.ClassTypeParameterUpperBounds -> error("Not applicable because ExpectActualMemberDiff is about members") + ExpectActualCheckingCompatibility.ActualFunctionWithDefaultParameters -> null // It's not possible to add default parameters in override + ExpectActualCheckingCompatibility.ClassKind -> error("Not applicable because ExpectActualMemberDiff is about members") + ExpectActualCheckingCompatibility.ClassModifiers -> error("Not applicable because ExpectActualMemberDiff is about members") + is ExpectActualCheckingCompatibility.ClassScopes -> error("Not applicable because ExpectActualMemberDiff is about members") + ExpectActualCheckingCompatibility.EnumEntries -> error("Not applicable because ExpectActualMemberDiff is about members") + ExpectActualCheckingCompatibility.FunInterfaceModifier -> error("Not applicable because ExpectActualMemberDiff is about members") + ExpectActualCheckingCompatibility.FunctionModifiersDifferent -> null // It's not possible to override with different function modifier (suspend) + ExpectActualCheckingCompatibility.FunctionModifiersNotSubset -> null // It's not possible to override with different function modifier (infix, inline, operator) + ExpectActualCheckingCompatibility.Modality -> ExpectActualMemberDiff.Kind.ModalityChangedInOverride + ExpectActualCheckingCompatibility.ParameterNames -> ExpectActualMemberDiff.Kind.ParameterNameChangedInOverride + ExpectActualCheckingCompatibility.PropertyConstModifier -> null // const fun can't be overridden + ExpectActualCheckingCompatibility.PropertyKind -> ExpectActualMemberDiff.Kind.PropertyKindChangedInOverride + ExpectActualCheckingCompatibility.PropertyLateinitModifier -> ExpectActualMemberDiff.Kind.LateinitChangedInOverride + ExpectActualCheckingCompatibility.PropertySetterVisibility -> ExpectActualMemberDiff.Kind.SetterVisibilityChangedInOverride + ExpectActualCheckingCompatibility.Supertypes -> error("Not applicable because ExpectActualMemberDiff is about members") + ExpectActualCheckingCompatibility.TypeParameterNames -> ExpectActualMemberDiff.Kind.TypeParameterNamesChangedInOverride + ExpectActualCheckingCompatibility.TypeParameterReified -> null // inline fun can't be overridden + ExpectActualCheckingCompatibility.TypeParameterVariance -> null // Members are not allowed to have variance + ExpectActualCheckingCompatibility.ValueParameterCrossinline -> null // inline fun can't be overridden + ExpectActualCheckingCompatibility.ValueParameterNoinline -> null // inline fun can't be overridden + ExpectActualCheckingCompatibility.ValueParameterVararg -> ExpectActualMemberDiff.Kind.VarargChangedInOverride + ExpectActualCheckingCompatibility.Visibility -> ExpectActualMemberDiff.Kind.VisibilityChangedInOverride }