[FIR, IR] 1/2 Minimize usages of ExpectActualCompatibility, MismatchOrIncompatible and MatchedOrCompatible

- Migrate ExpectActualCompatibility -> ExpectActualCheckingCompatibility
  where the "checker" is expect
- Migrate ExpectActualCompatibility -> ExpectActualMatchingCompatibility
  where the "matching" is expect

KT-62590 in progress. A lot of tests start to fail now. I will fix them
in next commits

Review: https://jetbrains.team/p/kt/reviews/12750/timeline
This commit is contained in:
Nikita Bobko
2023-10-25 22:15:51 +02:00
committed by teamcity
parent 664b9cc3ba
commit eb0c8c00c3
16 changed files with 181 additions and 170 deletions
@@ -14,7 +14,7 @@ import org.jetbrains.kotlin.fir.declarations.expectForActual
import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirClassSymbol import org.jetbrains.kotlin.fir.symbols.impl.FirClassSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirTypeAliasSymbol import org.jetbrains.kotlin.fir.symbols.impl.FirTypeAliasSymbol
import org.jetbrains.kotlin.resolve.multiplatform.isCompatibleOrWeaklyIncompatible import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualMatchingCompatibility
internal class KtFirMultiplatformInfoProvider( internal class KtFirMultiplatformInfoProvider(
override val analysisSession: KtFirAnalysisSession, override val analysisSession: KtFirAnalysisSession,
@@ -31,10 +31,7 @@ internal class KtFirMultiplatformInfoProvider(
} }
if (status?.isActual != true) return emptyList() if (status?.isActual != true) return emptyList()
return firSymbol.expectForActual.orEmpty().asSequence() return firSymbol.expectForActual?.get(ExpectActualMatchingCompatibility.MatchedSuccessfully)
.filter { it.key.isCompatibleOrWeaklyIncompatible } ?.map { analysisSession.firSymbolBuilder.buildSymbol(it) as KtDeclarationSymbol }.orEmpty()
.flatMap { it.value }
.map { analysisSession.firSymbolBuilder.buildSymbol(it) as KtDeclarationSymbol }
.toList()
} }
} }
@@ -19,6 +19,7 @@ import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
import org.jetbrains.kotlin.fir.types.areCompatibleExpectActualTypes import org.jetbrains.kotlin.fir.types.areCompatibleExpectActualTypes
import org.jetbrains.kotlin.fir.types.createExpectActualTypeParameterSubstitutor import org.jetbrains.kotlin.fir.types.createExpectActualTypeParameterSubstitutor
import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility
import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualMatchingCompatibility
import org.jetbrains.kotlin.utils.zipIfSizesAreEqual import org.jetbrains.kotlin.utils.zipIfSizesAreEqual
object FirActualCallableDeclarationChecker : FirCallableDeclarationChecker() { object FirActualCallableDeclarationChecker : FirCallableDeclarationChecker() {
@@ -60,7 +61,7 @@ object FirActualCallableDeclarationChecker : FirCallableDeclarationChecker() {
callableDeclaration.source, callableDeclaration.source,
FirErrors.ACTUAL_WITHOUT_EXPECT, FirErrors.ACTUAL_WITHOUT_EXPECT,
actualFunctionSymbol, actualFunctionSymbol,
actualFunctionSymbol.expectForActual as Map<ExpectActualCompatibility<FirBasedSymbol<*>>, Collection<FirBasedSymbol<*>>>, actualFunctionSymbol.expectForActual as Map<ExpectActualMatchingCompatibility, Collection<FirBasedSymbol<*>>>,
context context
) )
} }
@@ -22,7 +22,7 @@ import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol
import org.jetbrains.kotlin.fir.types.classId import org.jetbrains.kotlin.fir.types.classId
import org.jetbrains.kotlin.mpp.DeclarationSymbolMarker import org.jetbrains.kotlin.mpp.DeclarationSymbolMarker
import org.jetbrains.kotlin.resolve.multiplatform.compatible import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualMatchingCompatibility
internal object FirDefaultArgumentsInExpectActualizedByFakeOverrideChecker : FirRegularClassChecker() { internal object FirDefaultArgumentsInExpectActualizedByFakeOverrideChecker : FirRegularClassChecker() {
override fun check(declaration: FirRegularClass, context: CheckerContext, reporter: DiagnosticReporter) { override fun check(declaration: FirRegularClass, context: CheckerContext, reporter: DiagnosticReporter) {
@@ -77,7 +77,8 @@ internal object FirDefaultArgumentsInExpectActualizedByFakeOverrideChecker : Fir
expectSymbol, actualSymbol, actualMember, expectSymbol, actualSymbol, actualMember,
checkClassScopesCompatibility = false checkClassScopesCompatibility = false
) )
val expectMember: DeclarationSymbolMarker = potentialExpects.entries.singleOrNull { it.value.compatible }?.key val expectMember: DeclarationSymbolMarker = potentialExpects.entries
.singleOrNull { it.value == ExpectActualMatchingCompatibility.MatchedSuccessfully }?.key
?: potentialExpects.keys.singleOrNull() ?: potentialExpects.keys.singleOrNull()
?: return null ?: return null
return expectMember as FirNamedFunctionSymbol return expectMember as FirNamedFunctionSymbol
@@ -9,8 +9,7 @@ import org.jetbrains.kotlin.KtFakeSourceElementKind
import org.jetbrains.kotlin.config.LanguageFeature import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.descriptors.ClassKind import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.diagnostics.* import org.jetbrains.kotlin.diagnostics.*
import org.jetbrains.kotlin.fir.FirSession import org.jetbrains.kotlin.fir.*
import org.jetbrains.kotlin.fir.FirSourceElement
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.analysis.checkers.getModifierList import org.jetbrains.kotlin.fir.analysis.checkers.getModifierList
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
@@ -19,22 +18,22 @@ import org.jetbrains.kotlin.fir.declarations.utils.isActual
import org.jetbrains.kotlin.fir.declarations.utils.isExpect import org.jetbrains.kotlin.fir.declarations.utils.isExpect
import org.jetbrains.kotlin.fir.declarations.utils.isExternal import org.jetbrains.kotlin.fir.declarations.utils.isExternal
import org.jetbrains.kotlin.fir.declarations.utils.isTailRec import org.jetbrains.kotlin.fir.declarations.utils.isTailRec
import org.jetbrains.kotlin.fir.expectActualMatchingContextFactory
import org.jetbrains.kotlin.fir.expressions.FirAnnotation import org.jetbrains.kotlin.fir.expressions.FirAnnotation
import org.jetbrains.kotlin.fir.languageVersionSettings
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
import org.jetbrains.kotlin.fir.symbols.SymbolInternals import org.jetbrains.kotlin.fir.symbols.SymbolInternals
import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirClassLikeSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirTypeAliasSymbol import org.jetbrains.kotlin.fir.symbols.impl.FirTypeAliasSymbol
import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.mpp.RegularClassSymbolMarker
import org.jetbrains.kotlin.name.StandardClassIds import org.jetbrains.kotlin.name.StandardClassIds
import org.jetbrains.kotlin.resolve.calls.mpp.AbstractExpectActualAnnotationMatchChecker import org.jetbrains.kotlin.resolve.calls.mpp.AbstractExpectActualAnnotationMatchChecker
import org.jetbrains.kotlin.resolve.calls.mpp.AbstractExpectActualChecker
import org.jetbrains.kotlin.resolve.checkers.OptInNames import org.jetbrains.kotlin.resolve.checkers.OptInNames
import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCheckingCompatibility import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCheckingCompatibility
import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility
import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility.MismatchOrIncompatible
import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualMatchingCompatibility import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualMatchingCompatibility
import org.jetbrains.kotlin.resolve.multiplatform.isCompatibleOrWeaklyIncompatible
@Suppress("DuplicatedCode") @Suppress("DuplicatedCode")
object FirExpectActualDeclarationChecker : FirBasicDeclarationChecker() { object FirExpectActualDeclarationChecker : FirBasicDeclarationChecker() {
@@ -120,20 +119,35 @@ object FirExpectActualDeclarationChecker : FirBasicDeclarationChecker() {
declaration: FirMemberDeclaration, declaration: FirMemberDeclaration,
context: CheckerContext, context: CheckerContext,
reporter: DiagnosticReporter, reporter: DiagnosticReporter,
checkActual: Boolean = true checkActual: Boolean = true,
) { ) {
val symbol = declaration.symbol val symbol = declaration.symbol
val compatibilityToMembersMap = symbol.expectForActual ?: return val matchingCompatibilityToMembersMap = symbol.expectForActual ?: return
val expectedSingleCandidate =
matchingCompatibilityToMembersMap[ExpectActualMatchingCompatibility.MatchedSuccessfully]?.singleOrNull()
val checkingCompatibility = if (expectedSingleCandidate != null) {
val expectActualMatchingContext = context.session.expectActualMatchingContextFactory.create(
context.session, context.scopeSession,
allowedWritingMemberExpectForActualMapping = true,
)
val actualContainingClass = context.containingDeclarations.lastOrNull()?.symbol as? FirRegularClassSymbol
val expectContainingClass = actualContainingClass?.getSingleExpectForActualOrNull() as? FirRegularClassSymbol
getCheckingCompatibility(
symbol,
expectedSingleCandidate,
actualContainingClass,
expectContainingClass,
expectActualMatchingContext,
)
} else null
checkAmbiguousExpects(symbol, compatibilityToMembersMap, symbol, context, reporter) checkAmbiguousExpects(symbol, matchingCompatibilityToMembersMap, symbol, context, reporter)
val source = declaration.source val source = declaration.source
if (!declaration.isActual) { if (!declaration.isActual) {
if (compatibilityToMembersMap.allMismatches()) return if (matchingCompatibilityToMembersMap.allMismatches()) return
if (ExpectActualCheckingCompatibility.Compatible in compatibilityToMembersMap || if (ExpectActualMatchingCompatibility.MatchedSuccessfully in matchingCompatibilityToMembersMap) {
ExpectActualMatchingCompatibility.MatchedSuccessfully in compatibilityToMembersMap
) {
if (checkActual) { if (checkActual) {
reporter.reportOn(source, FirErrors.ACTUAL_MISSING, context) reporter.reportOn(source, FirErrors.ACTUAL_MISSING, context)
} }
@@ -141,9 +155,8 @@ object FirExpectActualDeclarationChecker : FirBasicDeclarationChecker() {
} }
} }
val singleIncompatibility = compatibilityToMembersMap.keys.singleOrNull()
when { when {
singleIncompatibility is ExpectActualCheckingCompatibility.ClassScopes -> { checkingCompatibility is ExpectActualCheckingCompatibility.ClassScopes -> {
require(symbol is FirRegularClassSymbol || symbol is FirTypeAliasSymbol) { require(symbol is FirRegularClassSymbol || symbol is FirTypeAliasSymbol) {
"Incompatible.ClassScopes is only possible for a class or a typealias: $declaration" "Incompatible.ClassScopes is only possible for a class or a typealias: $declaration"
} }
@@ -153,7 +166,7 @@ object FirExpectActualDeclarationChecker : FirBasicDeclarationChecker() {
// This is needed only to reduce the number of errors. Incompatibility errors for those members will be reported // 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 // later when this checker is called for them
fun hasSingleActualSuspect( fun hasSingleActualSuspect(
expectedWithIncompatibility: Pair<FirBasedSymbol<*>, Map<out MismatchOrIncompatible<FirBasedSymbol<*>>, Collection<FirBasedSymbol<*>>>> expectedWithIncompatibility: Pair<FirBasedSymbol<*>, Map<out ExpectActualCompatibility.MismatchOrIncompatible<FirBasedSymbol<*>>, Collection<FirBasedSymbol<*>>>>,
): Boolean { ): Boolean {
val (expectedMember, incompatibility) = expectedWithIncompatibility val (expectedMember, incompatibility) = expectedWithIncompatibility
val actualMember = incompatibility.values.singleOrNull()?.singleOrNull() val actualMember = incompatibility.values.singleOrNull()?.singleOrNull()
@@ -163,8 +176,8 @@ object FirExpectActualDeclarationChecker : FirBasicDeclarationChecker() {
actualMember.fir.expectForActual?.values?.singleOrNull()?.singleOrNull() == expectedMember actualMember.fir.expectForActual?.values?.singleOrNull()?.singleOrNull() == expectedMember
} }
val nonTrivialIncompatibleMembers = singleIncompatibility.incompatibleMembers.filterNot(::hasSingleActualSuspect) val nonTrivialIncompatibleMembers = checkingCompatibility.incompatibleMembers.filterNot(::hasSingleActualSuspect)
val nonTrivialMismatchedMembers = singleIncompatibility.mismatchedMembers.filterNot(::hasSingleActualSuspect) val nonTrivialMismatchedMembers = checkingCompatibility.mismatchedMembers.filterNot(::hasSingleActualSuspect)
if (nonTrivialIncompatibleMembers.isNotEmpty()) { if (nonTrivialIncompatibleMembers.isNotEmpty()) {
reporter.reportOn(source, FirErrors.NO_ACTUAL_CLASS_MEMBER_FOR_EXPECTED_CLASS, symbol, nonTrivialIncompatibleMembers, context) reporter.reportOn(source, FirErrors.NO_ACTUAL_CLASS_MEMBER_FOR_EXPECTED_CLASS, symbol, nonTrivialIncompatibleMembers, context)
@@ -173,17 +186,28 @@ object FirExpectActualDeclarationChecker : FirBasicDeclarationChecker() {
} }
} }
ExpectActualCheckingCompatibility.Compatible !in compatibilityToMembersMap && ExpectActualMatchingCompatibility.MatchedSuccessfully !in matchingCompatibilityToMembersMap &&
ExpectActualMatchingCompatibility.MatchedSuccessfully !in compatibilityToMembersMap -> { requireActualModifier(declaration.symbol, context.session) -> {
reporter.reportOn(
source,
FirErrors.ACTUAL_WITHOUT_EXPECT,
symbol,
matchingCompatibilityToMembersMap,
context
)
}
checkingCompatibility != null && checkingCompatibility != ExpectActualCheckingCompatibility.Compatible -> {
check(expectedSingleCandidate != null) // It can't be null, because checkingCompatibility is not null
// A nicer diagnostic for functions with default params // A nicer diagnostic for functions with default params
if (declaration is FirFunction && compatibilityToMembersMap.keys.any { it is ExpectActualCheckingCompatibility.ActualFunctionWithDefaultParameters }) { if (declaration is FirFunction && checkingCompatibility == ExpectActualCheckingCompatibility.ActualFunctionWithDefaultParameters) {
reporter.reportOn(declaration.source, FirErrors.ACTUAL_FUNCTION_WITH_DEFAULT_ARGUMENTS, context) reporter.reportOn(declaration.source, FirErrors.ACTUAL_FUNCTION_WITH_DEFAULT_ARGUMENTS, context)
} else if (requireActualModifier(declaration.symbol, context.session)) { } else if (requireActualModifier(declaration.symbol, context.session)) {
reporter.reportOn( reporter.reportOn(
source, source,
FirErrors.ACTUAL_WITHOUT_EXPECT, FirErrors.ACTUAL_WITHOUT_EXPECT,
symbol, symbol,
compatibilityToMembersMap, mapOf(checkingCompatibility to listOf(expectedSingleCandidate)),
context context
) )
} }
@@ -191,27 +215,49 @@ object FirExpectActualDeclarationChecker : FirBasicDeclarationChecker() {
else -> {} else -> {}
} }
// We want to report errors even if a candidate is incompatible, but it's single
val expectedSingleCandidate = symbol.getSingleExpectForActualOrNull()
if (expectedSingleCandidate != null) { if (expectedSingleCandidate != null) {
checkOptInAnnotation(declaration, expectedSingleCandidate, context, reporter) checkOptInAnnotation(declaration, expectedSingleCandidate, context, reporter)
checkAnnotationsMatch(expectedSingleCandidate, symbol, context, reporter) checkAnnotationsMatch(expectedSingleCandidate, symbol, context, reporter)
} }
} }
private fun getCheckingCompatibility(
actualSymbol: FirBasedSymbol<*>,
expectSymbol: FirBasedSymbol<*>,
actualContainingClass: FirRegularClassSymbol?,
expectContainingClass: FirRegularClassSymbol?,
context: FirExpectActualMatchingContext,
): ExpectActualCompatibility<FirBasedSymbol<*>> =
when {
actualSymbol is FirCallableSymbol<*> && expectSymbol is FirCallableSymbol<*> -> {
AbstractExpectActualChecker.getCallablesCompatibility(
expectSymbol,
actualSymbol,
expectContainingClass,
actualContainingClass,
context,
)
}
actualSymbol is FirClassLikeSymbol<*> && expectSymbol is RegularClassSymbolMarker -> {
AbstractExpectActualChecker.getClassifiersCompatibility(
expectSymbol,
actualSymbol,
checkClassScopesCompatibility = true,
context,
)
}
else -> error("These expect/actual shouldn't have been matched by FirExpectActualResolver")
}
private fun checkAmbiguousExpects( private fun checkAmbiguousExpects(
actualDeclaration: FirBasedSymbol<*>, actualDeclaration: FirBasedSymbol<*>,
compatibility: Map<ExpectActualCompatibility<FirBasedSymbol<*>>, List<FirBasedSymbol<*>>>, compatibility: Map<ExpectActualMatchingCompatibility, List<FirBasedSymbol<*>>>,
symbol: FirBasedSymbol<*>, symbol: FirBasedSymbol<*>,
context: CheckerContext, context: CheckerContext,
reporter: DiagnosticReporter reporter: DiagnosticReporter
) { ) {
val filesWithAtLeastWeaklyCompatibleExpects = compatibility.asSequence() val filesWithAtLeastWeaklyCompatibleExpects = compatibility[ExpectActualMatchingCompatibility.MatchedSuccessfully]
.filter { (compatibility, _) -> .orEmpty()
compatibility.isCompatibleOrWeaklyIncompatible
}
.map { (_, members) -> members }
.flatten()
.map { it.moduleData } .map { it.moduleData }
.sortedBy { it.name.asString() } .sortedBy { it.name.asString() }
.toList() .toList()
@@ -451,7 +451,7 @@ class FirExpectActualMatchingContextImpl private constructor(
return symbol.source == null && symbol.origin !is FirDeclarationOrigin.Plugin return symbol.source == null && symbol.origin !is FirDeclarationOrigin.Plugin
} }
override fun onMatchedOrCompatibleMembers( override fun onMatchedMembers(
expectSymbol: DeclarationSymbolMarker, expectSymbol: DeclarationSymbolMarker,
actualSymbol: DeclarationSymbolMarker, actualSymbol: DeclarationSymbolMarker,
containingExpectClassSymbol: RegularClassSymbolMarker?, containingExpectClassSymbol: RegularClassSymbolMarker?,
@@ -469,7 +469,7 @@ class FirExpectActualMatchingContextImpl private constructor(
override fun onMismatchedOrIncompatibleMembersFromClassScope( override fun onMismatchedOrIncompatibleMembersFromClassScope(
expectSymbol: DeclarationSymbolMarker, expectSymbol: DeclarationSymbolMarker,
actualSymbolsByIncompatibility: Map<out ExpectActualCompatibility.MismatchOrIncompatible<*>, List<DeclarationSymbolMarker>>, actualSymbolsByIncompatibility: Map<ExpectActualMatchingCompatibility.Mismatch, List<DeclarationSymbolMarker>>,
containingExpectClassSymbol: RegularClassSymbolMarker?, containingExpectClassSymbol: RegularClassSymbolMarker?,
containingActualClassSymbol: RegularClassSymbolMarker? containingActualClassSymbol: RegularClassSymbolMarker?
) { ) {
@@ -489,7 +489,7 @@ class FirExpectActualMatchingContextImpl private constructor(
private fun FirRegularClassSymbol.addMemberExpectForActualMapping( private fun FirRegularClassSymbol.addMemberExpectForActualMapping(
expectMember: FirBasedSymbol<*>, actualMember: FirBasedSymbol<*>, expectMember: FirBasedSymbol<*>, actualMember: FirBasedSymbol<*>,
expectClassSymbol: FirRegularClassSymbol, compatibility: ExpectActualCompatibility<*>, expectClassSymbol: FirRegularClassSymbol, compatibility: ExpectActualMatchingCompatibility,
) { ) {
check(allowedWritingMemberExpectForActualMapping) { "Writing memberExpectForActual is not allowed in this context" } check(allowedWritingMemberExpectForActualMapping) { "Writing memberExpectForActual is not allowed in this context" }
val fir = fir val fir = fir
@@ -524,7 +524,7 @@ class FirExpectActualMatchingContextImpl private constructor(
actualClass: RegularClassSymbolMarker, actualClass: RegularClassSymbolMarker,
actualMember: DeclarationSymbolMarker, actualMember: DeclarationSymbolMarker,
checkClassScopesCompatibility: Boolean, checkClassScopesCompatibility: Boolean,
): Map<FirBasedSymbol<*>, ExpectActualCompatibility<*>> { ): Map<FirBasedSymbol<*>, ExpectActualMatchingCompatibility> {
val mapping = actualClass.asSymbol().fir.memberExpectForActual val mapping = actualClass.asSymbol().fir.memberExpectForActual
return mapping?.get(actualMember to expectClass) ?: emptyMap() return mapping?.get(actualMember to expectClass) ?: emptyMap()
} }
@@ -7,19 +7,20 @@ package org.jetbrains.kotlin.fir.resolve.transformers.mpp
import org.jetbrains.kotlin.fir.FirExpectActualMatchingContext import org.jetbrains.kotlin.fir.FirExpectActualMatchingContext
import org.jetbrains.kotlin.fir.FirSession import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.declarations.* import org.jetbrains.kotlin.fir.declarations.ExpectForActualMatchingData
import org.jetbrains.kotlin.fir.resolve.* import org.jetbrains.kotlin.fir.declarations.fullyExpandedClass
import org.jetbrains.kotlin.fir.resolve.ScopeSession
import org.jetbrains.kotlin.fir.resolve.providers.dependenciesSymbolProvider import org.jetbrains.kotlin.fir.resolve.providers.dependenciesSymbolProvider
import org.jetbrains.kotlin.fir.resolve.providers.symbolProvider import org.jetbrains.kotlin.fir.resolve.providers.symbolProvider
import org.jetbrains.kotlin.fir.scopes.impl.FirPackageMemberScope import org.jetbrains.kotlin.fir.scopes.impl.FirPackageMemberScope
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
import org.jetbrains.kotlin.fir.symbols.impl.* import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
import org.jetbrains.kotlin.fir.types.* import org.jetbrains.kotlin.fir.symbols.impl.FirClassLikeSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirConstructorSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol
import org.jetbrains.kotlin.mpp.CallableSymbolMarker import org.jetbrains.kotlin.mpp.CallableSymbolMarker
import org.jetbrains.kotlin.resolve.calls.mpp.AbstractExpectActualChecker import org.jetbrains.kotlin.resolve.calls.mpp.AbstractExpectActualMatcher
import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility
import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualMatchingCompatibility import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualMatchingCompatibility
import org.jetbrains.kotlin.resolve.multiplatform.getMatchedAndChecked
object FirExpectActualResolver { object FirExpectActualResolver {
fun findExpectForActual( fun findExpectForActual(
@@ -27,9 +28,9 @@ object FirExpectActualResolver {
useSiteSession: FirSession, useSiteSession: FirSession,
scopeSession: ScopeSession, scopeSession: ScopeSession,
context: FirExpectActualMatchingContext, context: FirExpectActualMatchingContext,
): ExpectForActualData { ): ExpectForActualMatchingData {
with(context) { with(context) {
val result = when (actualSymbol) { val result: Map<ExpectActualMatchingCompatibility, List<FirBasedSymbol<*>>> = when (actualSymbol) {
is FirCallableSymbol<*> -> { is FirCallableSymbol<*> -> {
val callableId = actualSymbol.callableId val callableId = actualSymbol.callableId
val classId = callableId.classId val classId = callableId.classId
@@ -60,7 +61,7 @@ object FirExpectActualResolver {
candidates.filter { expectSymbol -> candidates.filter { expectSymbol ->
actualSymbol != expectSymbol && expectSymbol.isExpect actualSymbol != expectSymbol && expectSymbol.isExpect
}.groupBy { expectDeclaration -> }.groupBy { expectDeclaration ->
AbstractExpectActualChecker.getCallablesCompatibility( AbstractExpectActualMatcher.getCallablesMatchingCompatibility(
expectDeclaration, expectDeclaration,
actualSymbol as CallableSymbolMarker, actualSymbol as CallableSymbolMarker,
expectContainingClass, expectContainingClass,
@@ -69,21 +70,16 @@ object FirExpectActualResolver {
) )
}.let { }.let {
// If there is a compatible entry, return a map only containing it // If there is a compatible entry, return a map only containing it
when (val compatibleSymbols = it.getMatchedAndChecked()) { when (val compatibleSymbols = it[ExpectActualMatchingCompatibility.MatchedSuccessfully]) {
null -> it null -> it
else -> mapOf<ExpectActualCompatibility<FirBasedSymbol<*>>, _>(ExpectActualMatchingCompatibility.MatchedSuccessfully to compatibleSymbols) else -> mapOf(ExpectActualMatchingCompatibility.MatchedSuccessfully to compatibleSymbols)
} }
} }
} }
is FirClassLikeSymbol<*> -> { is FirClassLikeSymbol<*> -> {
val expectClassSymbol = useSiteSession.dependenciesSymbolProvider val expectClassSymbol = useSiteSession.dependenciesSymbolProvider
.getClassLikeSymbolByClassId(actualSymbol.classId) as? FirRegularClassSymbol ?: return emptyMap() .getClassLikeSymbolByClassId(actualSymbol.classId) as? FirRegularClassSymbol ?: return emptyMap()
val compatibility = AbstractExpectActualChecker.getClassifiersCompatibility( val compatibility = AbstractExpectActualMatcher.matchClassifiers(expectClassSymbol, actualSymbol, context)
expectClassSymbol,
actualSymbol,
checkClassScopesCompatibility = true,
context
)
mapOf(compatibility to listOf(expectClassSymbol)) mapOf(compatibility to listOf(expectClassSymbol))
} }
else -> emptyMap() else -> emptyMap()
@@ -10,12 +10,11 @@ import org.jetbrains.kotlin.fir.symbols.SymbolInternals
import org.jetbrains.kotlin.fir.symbols.impl.FirFunctionSymbol import org.jetbrains.kotlin.fir.symbols.impl.FirFunctionSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol
import org.jetbrains.kotlin.fir.symbols.lazyResolveToPhase import org.jetbrains.kotlin.fir.symbols.lazyResolveToPhase
import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualMatchingCompatibility
import org.jetbrains.kotlin.resolve.multiplatform.isCompatibleOrWeaklyIncompatible
private object ExpectForActualAttributeKey : FirDeclarationDataKey() private object ExpectForActualAttributeKey : FirDeclarationDataKey()
typealias ExpectForActualData = Map<ExpectActualCompatibility<FirBasedSymbol<*>>, List<FirBasedSymbol<*>>> typealias ExpectForActualMatchingData = Map<ExpectActualMatchingCompatibility, List<FirBasedSymbol<*>>>
/** /**
* Actual declaration -> (many) expect declaration mapping. For top-level declarations. * Actual declaration -> (many) expect declaration mapping. For top-level declarations.
@@ -27,7 +26,7 @@ typealias ExpectForActualData = Map<ExpectActualCompatibility<FirBasedSymbol<*>>
* See `/docs/fir/k2_kmp.md` * See `/docs/fir/k2_kmp.md`
*/ */
@SymbolInternals @SymbolInternals
var FirDeclaration.expectForActual: ExpectForActualData? by FirDeclarationDataRegistry.data(ExpectForActualAttributeKey) var FirDeclaration.expectForActual: ExpectForActualMatchingData? by FirDeclarationDataRegistry.data(ExpectForActualAttributeKey)
/** /**
* @see expectForActual * @see expectForActual
@@ -38,17 +37,13 @@ fun FirFunctionSymbol<*>.getSingleExpectForActualOrNull(): FirFunctionSymbol<*>?
/** /**
* @see expectForActual * @see expectForActual
*/ */
fun FirBasedSymbol<*>.getSingleExpectForActualOrNull(): FirBasedSymbol<*>? { fun FirBasedSymbol<*>.getSingleExpectForActualOrNull(): FirBasedSymbol<*>? =
val expectForActual = expectForActual ?: return null expectForActual?.get(ExpectActualMatchingCompatibility.MatchedSuccessfully)?.singleOrNull()
val compatibleOrWeakCompatible: List<FirBasedSymbol<*>> =
expectForActual.entries.singleOrNull { it.key.isCompatibleOrWeaklyIncompatible }?.value ?: return null
return compatibleOrWeakCompatible.singleOrNull()
}
/** /**
* @see expectForActual * @see expectForActual
*/ */
val FirBasedSymbol<*>.expectForActual: ExpectForActualData? val FirBasedSymbol<*>.expectForActual: ExpectForActualMatchingData?
get() { get() {
lazyResolveToPhase(FirResolvePhase.EXPECT_ACTUAL_MATCHING) lazyResolveToPhase(FirResolvePhase.EXPECT_ACTUAL_MATCHING)
return fir.expectForActual return fir.expectForActual
@@ -61,7 +56,7 @@ private object MemberExpectForActualAttributeKey : FirDeclarationDataKey()
// in case when two `actual typealias` point to the same class. // in case when two `actual typealias` point to the same class.
typealias MemberExpectForActualData = typealias MemberExpectForActualData =
Map<Pair</* actual member */ FirBasedSymbol<*>, /* expect class */ FirRegularClassSymbol>, Map<Pair</* actual member */ FirBasedSymbol<*>, /* expect class */ FirRegularClassSymbol>,
Map</* expect member */ FirBasedSymbol<*>, ExpectActualCompatibility<*>>> Map</* expect member */ FirBasedSymbol<*>, ExpectActualMatchingCompatibility>>
/** /**
* Actual class + expect class + actual member declaration -> (many) expect member declaration mapping. * Actual class + expect class + actual member declaration -> (many) expect member declaration mapping.
@@ -10,7 +10,9 @@ import org.jetbrains.kotlin.incremental.components.ExpectActualTracker
import org.jetbrains.kotlin.ir.IrElement import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.PsiIrFileEntry import org.jetbrains.kotlin.ir.PsiIrFileEntry
import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.symbols.* import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
import org.jetbrains.kotlin.ir.symbols.IrSymbol
import org.jetbrains.kotlin.ir.symbols.IrTypeAliasSymbol
import org.jetbrains.kotlin.ir.types.IrTypeSystemContext import org.jetbrains.kotlin.ir.types.IrTypeSystemContext
import org.jetbrains.kotlin.ir.types.classifierOrFail import org.jetbrains.kotlin.ir.types.classifierOrFail
import org.jetbrains.kotlin.ir.util.callableId import org.jetbrains.kotlin.ir.util.callableId
@@ -20,8 +22,8 @@ import org.jetbrains.kotlin.mpp.DeclarationSymbolMarker
import org.jetbrains.kotlin.mpp.RegularClassSymbolMarker import org.jetbrains.kotlin.mpp.RegularClassSymbolMarker
import org.jetbrains.kotlin.name.CallableId import org.jetbrains.kotlin.name.CallableId
import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.resolve.calls.mpp.AbstractExpectActualChecker import org.jetbrains.kotlin.resolve.calls.mpp.AbstractExpectActualMatcher
import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualMatchingCompatibility
import java.io.File import java.io.File
/** /**
@@ -219,11 +221,10 @@ private class ExpectActualLinkCollector : IrElementVisitor<Unit, ExpectActualLin
actualSymbols: List<IrSymbol>, actualSymbols: List<IrSymbol>,
context: MatchingContext context: MatchingContext
) { ) {
AbstractExpectActualChecker.checkSingleExpectTopLevelDeclarationAgainstPotentialActuals( AbstractExpectActualMatcher.matchSingleExpectTopLevelDeclarationAgainstPotentialActuals(
expectSymbol, expectSymbol,
actualSymbols, actualSymbols,
context, context,
checkClassScopesCompatibility = true
) )
} }
@@ -260,7 +261,7 @@ private class ExpectActualLinkCollector : IrElementVisitor<Unit, ExpectActualLin
override fun onMismatchedOrIncompatibleMembersFromClassScope( override fun onMismatchedOrIncompatibleMembersFromClassScope(
expectSymbol: DeclarationSymbolMarker, expectSymbol: DeclarationSymbolMarker,
actualSymbolsByIncompatibility: Map<out ExpectActualCompatibility.MismatchOrIncompatible<*>, List<DeclarationSymbolMarker>>, actualSymbolsByIncompatibility: Map<ExpectActualMatchingCompatibility.Mismatch, List<DeclarationSymbolMarker>>,
containingExpectClassSymbol: RegularClassSymbolMarker?, containingExpectClassSymbol: RegularClassSymbolMarker?,
containingActualClassSymbol: RegularClassSymbolMarker?, containingActualClassSymbol: RegularClassSymbolMarker?,
) { ) {
@@ -20,7 +20,7 @@ import org.jetbrains.kotlin.ir.symbols.IrSymbol
import org.jetbrains.kotlin.ir.types.IrTypeSystemContext import org.jetbrains.kotlin.ir.types.IrTypeSystemContext
import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.resolve.calls.mpp.AbstractExpectActualChecker import org.jetbrains.kotlin.resolve.calls.mpp.AbstractExpectActualMatcher
import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualAnnotationsIncompatibilityType import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualAnnotationsIncompatibilityType
import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility
import org.jetbrains.kotlin.resolve.multiplatform.OptionalAnnotationUtil import org.jetbrains.kotlin.resolve.multiplatform.OptionalAnnotationUtil
@@ -45,11 +45,10 @@ internal fun collectActualCallablesMatchingToSpecificExpect(
matchingActuals += actualSymbol matchingActuals += actualSymbol
} }
} }
AbstractExpectActualChecker.checkSingleExpectTopLevelDeclarationAgainstPotentialActuals( AbstractExpectActualMatcher.matchSingleExpectTopLevelDeclarationAgainstPotentialActuals(
expectSymbol, expectSymbol,
actualSymbols, actualSymbols,
context, context,
checkClassScopesCompatibility = false
) )
return matchingActuals return matchingActuals
} }
@@ -29,7 +29,7 @@ import org.jetbrains.kotlin.resolve.calls.mpp.ExpectActualCollectionArgumentsCom
import org.jetbrains.kotlin.resolve.calls.mpp.ExpectActualMatchingContext import org.jetbrains.kotlin.resolve.calls.mpp.ExpectActualMatchingContext
import org.jetbrains.kotlin.resolve.calls.mpp.ExpectActualMatchingContext.AnnotationCallInfo import org.jetbrains.kotlin.resolve.calls.mpp.ExpectActualMatchingContext.AnnotationCallInfo
import org.jetbrains.kotlin.resolve.checkers.OptInNames 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.AbstractTypeChecker
import org.jetbrains.kotlin.types.TypeCheckerState import org.jetbrains.kotlin.types.TypeCheckerState
import org.jetbrains.kotlin.types.Variance import org.jetbrains.kotlin.types.Variance
@@ -480,7 +480,7 @@ internal abstract class IrExpectActualMatchingContext(
} }
} }
override fun onMatchedOrCompatibleMembers( override fun onMatchedMembers(
expectSymbol: DeclarationSymbolMarker, expectSymbol: DeclarationSymbolMarker,
actualSymbol: DeclarationSymbolMarker, actualSymbol: DeclarationSymbolMarker,
containingExpectClassSymbol: RegularClassSymbolMarker?, containingExpectClassSymbol: RegularClassSymbolMarker?,
@@ -558,7 +558,7 @@ internal abstract class IrExpectActualMatchingContext(
actualClass: RegularClassSymbolMarker, actualClass: RegularClassSymbolMarker,
actualMember: DeclarationSymbolMarker, actualMember: DeclarationSymbolMarker,
checkClassScopesCompatibility: Boolean, checkClassScopesCompatibility: Boolean,
): Map<out DeclarationSymbolMarker, ExpectActualCompatibility<*>> = error("Should not be called") ): Map<out DeclarationSymbolMarker, ExpectActualMatchingCompatibility> = error("Should not be called")
// It's a stub, because not needed anywhere // It's a stub, because not needed anywhere
private object IrSourceElementStub : SourceElementMarker private object IrSourceElementStub : SourceElementMarker
@@ -13,6 +13,7 @@ import org.jetbrains.kotlin.name.StandardClassIds
import org.jetbrains.kotlin.resolve.checkers.OptInNames import org.jetbrains.kotlin.resolve.checkers.OptInNames
import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility
import org.jetbrains.kotlin.utils.zipIfSizesAreEqual import org.jetbrains.kotlin.utils.zipIfSizesAreEqual
import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualMatchingCompatibility
import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualAnnotationsIncompatibilityType as IncompatibilityType import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualAnnotationsIncompatibilityType as IncompatibilityType
object AbstractExpectActualAnnotationMatchChecker { object AbstractExpectActualAnnotationMatchChecker {
@@ -316,7 +317,7 @@ object AbstractExpectActualAnnotationMatchChecker {
checkClassScopesCompatibility = false, checkClassScopesCompatibility = false,
) )
val expectMember = expectToCompatibilityMap val expectMember = expectToCompatibilityMap
.filter { it.value is ExpectActualCompatibility.MatchedOrCompatible }.keys.singleOrNull() .filter { it.value == ExpectActualMatchingCompatibility.MatchedSuccessfully }.keys.singleOrNull()
// Check also incompatible members if only one is found // Check also incompatible members if only one is found
?: expectToCompatibilityMap.keys.singleOrNull() ?: expectToCompatibilityMap.keys.singleOrNull()
?: continue ?: continue
@@ -13,8 +13,6 @@ import org.jetbrains.kotlin.mpp.*
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.name.SpecialNames import org.jetbrains.kotlin.name.SpecialNames
import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCheckingCompatibility import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCheckingCompatibility
import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility
import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility.MismatchOrIncompatible
import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualMatchingCompatibility import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualMatchingCompatibility
import org.jetbrains.kotlin.types.model.KotlinTypeMarker import org.jetbrains.kotlin.types.model.KotlinTypeMarker
import org.jetbrains.kotlin.types.model.TypeSubstitutorMarker import org.jetbrains.kotlin.types.model.TypeSubstitutorMarker
@@ -37,12 +35,17 @@ object AbstractExpectActualChecker {
actualClassLikeSymbol: ClassLikeSymbolMarker, actualClassLikeSymbol: ClassLikeSymbolMarker,
checkClassScopesCompatibility: Boolean, checkClassScopesCompatibility: Boolean,
context: ExpectActualMatchingContext<T>, context: ExpectActualMatchingContext<T>,
): ExpectActualCompatibility<T> { ): ExpectActualCheckingCompatibility<T> {
val result = with(context) { val result = with(context) {
getClassifiersCompatibility(expectClassSymbol, actualClassLikeSymbol, parentSubstitutor = null, checkClassScopesCompatibility) getClassifiersCompatibility(
expectClassSymbol,
actualClassLikeSymbol,
parentSubstitutor = null,
checkClassScopesCompatibility,
)
} }
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
return result as ExpectActualCompatibility<T> return result as ExpectActualCheckingCompatibility<T>
} }
fun <T : DeclarationSymbolMarker> getCallablesCompatibility( fun <T : DeclarationSymbolMarker> getCallablesCompatibility(
@@ -51,7 +54,7 @@ object AbstractExpectActualChecker {
expectContainingClass: RegularClassSymbolMarker?, expectContainingClass: RegularClassSymbolMarker?,
actualContainingClass: RegularClassSymbolMarker?, actualContainingClass: RegularClassSymbolMarker?,
context: ExpectActualMatchingContext<T>, context: ExpectActualMatchingContext<T>,
): ExpectActualCompatibility<T> = with (context) { ): ExpectActualCheckingCompatibility<T> = with (context) {
val expectTypeParameters = expectContainingClass?.typeParameters.orEmpty() val expectTypeParameters = expectContainingClass?.typeParameters.orEmpty()
val actualTypeParameters = actualContainingClass?.typeParameters.orEmpty() val actualTypeParameters = actualContainingClass?.typeParameters.orEmpty()
val parentSubstitutor = (expectTypeParameters zipIfSizesAreEqual actualTypeParameters) val parentSubstitutor = (expectTypeParameters zipIfSizesAreEqual actualTypeParameters)
@@ -61,10 +64,10 @@ object AbstractExpectActualChecker {
actualDeclaration, actualDeclaration,
parentSubstitutor, parentSubstitutor,
expectContainingClass, expectContainingClass,
actualContainingClass actualContainingClass,
) )
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
result as ExpectActualCompatibility<T> result as ExpectActualCheckingCompatibility<T>
} }
fun <T : DeclarationSymbolMarker> checkSingleExpectTopLevelDeclarationAgainstPotentialActuals( fun <T : DeclarationSymbolMarker> checkSingleExpectTopLevelDeclarationAgainstPotentialActuals(
@@ -93,7 +96,12 @@ object AbstractExpectActualChecker {
actualClassLikeSymbol: ClassLikeSymbolMarker, actualClassLikeSymbol: ClassLikeSymbolMarker,
parentSubstitutor: TypeSubstitutorMarker?, parentSubstitutor: TypeSubstitutorMarker?,
checkClassScopes: Boolean, checkClassScopes: Boolean,
): ExpectActualCheckingCompatibility<*> = getClassifiersIncompatibility(expectClassSymbol, actualClassLikeSymbol, parentSubstitutor, checkClassScopes) ): ExpectActualCheckingCompatibility<*> = getClassifiersIncompatibility(
expectClassSymbol,
actualClassLikeSymbol,
parentSubstitutor,
checkClassScopes,
)
?: ExpectActualCheckingCompatibility.Compatible ?: ExpectActualCheckingCompatibility.Compatible
context(ExpectActualMatchingContext<*>) context(ExpectActualMatchingContext<*>)
@@ -301,27 +309,17 @@ object AbstractExpectActualChecker {
} }
} }
val mismatchedOrIncompatibleMap = mutableMapOf<MismatchOrIncompatible<*>, MutableList<DeclarationSymbolMarker>>() val incompatibilityMap = mutableMapOf<ExpectActualCheckingCompatibility.Incompatible<*>, MutableList<DeclarationSymbolMarker>>()
for ((actualMember, compatibility) in mapping) { for ((actualMember, compatibility) in mapping) {
when (compatibility) { when (compatibility) {
is ExpectActualCompatibility.MatchedOrCompatible<*> -> { ExpectActualCheckingCompatibility.Compatible -> return
onMatchedOrCompatibleMembers(expectMember, actualMember, expectClassSymbol, actualClassSymbol) is ExpectActualCheckingCompatibility.Incompatible<*> -> incompatibilityMap.getOrPut(compatibility) { SmartList() }.add(actualMember)
return
}
is MismatchOrIncompatible<*> -> mismatchedOrIncompatibleMap.getOrPut(compatibility) { SmartList() }.add(actualMember)
} }
} }
// todo it's temporary code. KT-62590 in progress incompatibleMembers?.add(expectMember to incompatibilityMap)
val incompatibilities = mismatchedOrIncompatibleMap // Temporarily comment out reporting checking incompatibilities to backend. KT-62590 is in progress
.mapNotNull { (key, value) -> //onMismatchedOrIncompatibleMembersFromClassScope(expectMember, incompatibilityMap, expectClassSymbol, actualClassSymbol)
(key as? ExpectActualCheckingCompatibility.Incompatible)?.let { it to value }
}
.toMap()
incompatibleMembers?.add(expectMember to incompatibilities)
onMismatchedOrIncompatibleMembersFromClassScope(expectMember, mismatchedOrIncompatibleMap, expectClassSymbol, actualClassSymbol)
} }
context(ExpectActualMatchingContext<*>) context(ExpectActualMatchingContext<*>)
@@ -331,7 +329,7 @@ object AbstractExpectActualChecker {
parentSubstitutor: TypeSubstitutorMarker?, parentSubstitutor: TypeSubstitutorMarker?,
expectContainingClass: RegularClassSymbolMarker?, expectContainingClass: RegularClassSymbolMarker?,
actualContainingClass: RegularClassSymbolMarker?, actualContainingClass: RegularClassSymbolMarker?,
): ExpectActualCompatibility<*> { ): ExpectActualCheckingCompatibility<*> {
require( require(
(expectDeclaration is ConstructorSymbolMarker && actualDeclaration is ConstructorSymbolMarker) || (expectDeclaration is ConstructorSymbolMarker && actualDeclaration is ConstructorSymbolMarker) ||
expectDeclaration.callableId.callableName == actualDeclaration.callableId.callableName expectDeclaration.callableId.callableName == actualDeclaration.callableId.callableName
@@ -352,18 +350,15 @@ object AbstractExpectActualChecker {
return ExpectActualCheckingCompatibility.Compatible return ExpectActualCheckingCompatibility.Compatible
} }
// We must prioritize to return STRONG incompatible over WEAK incompatible (because STRONG incompatibility allows to search for overloads)
val annotationMode = expectContainingClass?.classKind == ClassKind.ANNOTATION_CLASS val annotationMode = expectContainingClass?.classKind == ClassKind.ANNOTATION_CLASS
return getCallablesStrongIncompatibility(expectDeclaration, actualDeclaration, annotationMode, parentSubstitutor) return getCallablesCheckingIncompatibility(
?: getCallablesWeakIncompatibility( expectDeclaration,
expectDeclaration, actualDeclaration,
actualDeclaration, annotationMode,
annotationMode, parentSubstitutor,
parentSubstitutor, expectContainingClass,
expectContainingClass, actualContainingClass,
actualContainingClass ) ?: ExpectActualCheckingCompatibility.Compatible
)
?: ExpectActualCheckingCompatibility.Compatible
} }
context(ExpectActualMatchingContext<*>) context(ExpectActualMatchingContext<*>)
@@ -423,14 +418,14 @@ object AbstractExpectActualChecker {
} }
context(ExpectActualMatchingContext<*>) context(ExpectActualMatchingContext<*>)
private fun getCallablesWeakIncompatibility( private fun getCallablesCheckingIncompatibility(
expectDeclaration: CallableSymbolMarker, expectDeclaration: CallableSymbolMarker,
actualDeclaration: CallableSymbolMarker, actualDeclaration: CallableSymbolMarker,
insideAnnotationClass: Boolean, insideAnnotationClass: Boolean,
parentSubstitutor: TypeSubstitutorMarker?, parentSubstitutor: TypeSubstitutorMarker?,
expectContainingClass: RegularClassSymbolMarker?, expectContainingClass: RegularClassSymbolMarker?,
actualContainingClass: RegularClassSymbolMarker?, actualContainingClass: RegularClassSymbolMarker?,
): ExpectActualCheckingCompatibility<*>? { ): ExpectActualCheckingCompatibility.Incompatible<*>? {
val expectedTypeParameters = expectDeclaration.typeParameters val expectedTypeParameters = expectDeclaration.typeParameters
val actualTypeParameters = actualDeclaration.typeParameters val actualTypeParameters = actualDeclaration.typeParameters
val expectedValueParameters = expectDeclaration.valueParameters val expectedValueParameters = expectDeclaration.valueParameters
@@ -679,7 +674,7 @@ object AbstractExpectActualChecker {
private fun getFunctionsIncompatibility( private fun getFunctionsIncompatibility(
expectFunction: CallableSymbolMarker, expectFunction: CallableSymbolMarker,
actualFunction: CallableSymbolMarker, actualFunction: CallableSymbolMarker,
): ExpectActualCheckingCompatibility<*>? { ): ExpectActualCheckingCompatibility.Incompatible<*>? {
if (!equalBy(expectFunction, actualFunction) { f -> f.isSuspend }) { if (!equalBy(expectFunction, actualFunction) { f -> f.isSuspend }) {
return ExpectActualCheckingCompatibility.FunctionModifiersDifferent return ExpectActualCheckingCompatibility.FunctionModifiersDifferent
} }
@@ -699,12 +694,13 @@ object AbstractExpectActualChecker {
private fun getPropertiesIncompatibility( private fun getPropertiesIncompatibility(
expected: PropertySymbolMarker, expected: PropertySymbolMarker,
actual: PropertySymbolMarker, actual: PropertySymbolMarker,
): ExpectActualCheckingCompatibility<*>? { ): ExpectActualCheckingCompatibility.Incompatible<*>? {
return when { return when {
!equalBy(expected, actual) { p -> p.isVar } -> ExpectActualCheckingCompatibility.PropertyKind !equalBy(expected, actual) { p -> p.isVar } -> ExpectActualCheckingCompatibility.PropertyKind
!equalBy(expected, actual) { p -> p.isLateinit } -> ExpectActualCheckingCompatibility.PropertyLateinitModifier !equalBy(expected, actual) { p -> p.isLateinit } -> ExpectActualCheckingCompatibility.PropertyLateinitModifier
expected.isConst && !actual.isConst -> ExpectActualCheckingCompatibility.PropertyConstModifier expected.isConst && !actual.isConst -> ExpectActualCheckingCompatibility.PropertyConstModifier
!arePropertySettersWithCompatibleVisibilities(expected, actual) -> ExpectActualCheckingCompatibility.PropertySetterVisibility !arePropertySettersWithCompatibleVisibilities(expected, actual) ->
ExpectActualCheckingCompatibility.PropertySetterVisibility
else -> null else -> null
} }
} }
@@ -716,7 +712,11 @@ object AbstractExpectActualChecker {
): Boolean { ): Boolean {
val expectedSetter = expected.setter ?: return true val expectedSetter = expected.setter ?: return true
val actualSetter = actual.setter ?: return true val actualSetter = actual.setter ?: return true
return areCompatibleCallableVisibilities(expectedSetter.visibility, expectedSetter.modality, actualSetter.visibility) return areCompatibleCallableVisibilities(
expectedSetter.visibility,
expectedSetter.modality,
actualSetter.visibility,
)
} }
// ---------------------------------------- Utils ---------------------------------------- // ---------------------------------------- Utils ----------------------------------------
@@ -9,7 +9,6 @@ import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.mpp.* import org.jetbrains.kotlin.mpp.*
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.name.SpecialNames import org.jetbrains.kotlin.name.SpecialNames
import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility
import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualMatchingCompatibility import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualMatchingCompatibility
import org.jetbrains.kotlin.types.model.KotlinTypeMarker import org.jetbrains.kotlin.types.model.KotlinTypeMarker
import org.jetbrains.kotlin.types.model.TypeSubstitutorMarker import org.jetbrains.kotlin.types.model.TypeSubstitutorMarker
@@ -27,26 +26,24 @@ import org.jetbrains.kotlin.utils.zipIfSizesAreEqual
* See `/docs/fir/k2_kmp.md` for details * See `/docs/fir/k2_kmp.md` for details
*/ */
object AbstractExpectActualMatcher { object AbstractExpectActualMatcher {
fun <T : DeclarationSymbolMarker> getCallablesCompatibility( fun getCallablesMatchingCompatibility(
expectDeclaration: CallableSymbolMarker, expectDeclaration: CallableSymbolMarker,
actualDeclaration: CallableSymbolMarker, actualDeclaration: CallableSymbolMarker,
expectContainingClass: RegularClassSymbolMarker?, expectContainingClass: RegularClassSymbolMarker?,
actualContainingClass: RegularClassSymbolMarker?, actualContainingClass: RegularClassSymbolMarker?,
context: ExpectActualMatchingContext<T>, context: ExpectActualMatchingContext<*>,
): ExpectActualCompatibility<T> = with (context) { ): ExpectActualMatchingCompatibility = with (context) {
val expectTypeParameters = expectContainingClass?.typeParameters.orEmpty() val expectTypeParameters = expectContainingClass?.typeParameters.orEmpty()
val actualTypeParameters = actualContainingClass?.typeParameters.orEmpty() val actualTypeParameters = actualContainingClass?.typeParameters.orEmpty()
val parentSubstitutor = (expectTypeParameters zipIfSizesAreEqual actualTypeParameters) val parentSubstitutor = (expectTypeParameters zipIfSizesAreEqual actualTypeParameters)
?.let { createExpectActualTypeParameterSubstitutor(it, parentSubstitutor = null) } ?.let { createExpectActualTypeParameterSubstitutor(it, parentSubstitutor = null) }
val result = getCallablesCompatibility( getCallablesCompatibility(
expectDeclaration, expectDeclaration,
actualDeclaration, actualDeclaration,
parentSubstitutor, parentSubstitutor,
expectContainingClass, expectContainingClass,
actualContainingClass actualContainingClass
) )
@Suppress("UNCHECKED_CAST")
result as ExpectActualCompatibility<T>
} }
fun <T : DeclarationSymbolMarker> matchSingleExpectTopLevelDeclarationAgainstPotentialActuals( fun <T : DeclarationSymbolMarker> matchSingleExpectTopLevelDeclarationAgainstPotentialActuals(
@@ -147,7 +144,7 @@ object AbstractExpectActualMatcher {
for ((actualMember, compatibility) in mapping) { for ((actualMember, compatibility) in mapping) {
when (compatibility) { when (compatibility) {
ExpectActualMatchingCompatibility.MatchedSuccessfully -> { ExpectActualMatchingCompatibility.MatchedSuccessfully -> {
onMatchedOrCompatibleMembers(expectMember, actualMember, expectClassSymbol, actualClassSymbol) onMatchedMembers(expectMember, actualMember, expectClassSymbol, actualClassSymbol)
return actualMember return actualMember
} }
@@ -189,12 +186,12 @@ object AbstractExpectActualMatcher {
} }
val annotationMode = expectContainingClass?.classKind == ClassKind.ANNOTATION_CLASS val annotationMode = expectContainingClass?.classKind == ClassKind.ANNOTATION_CLASS
return getCallablesStrongIncompatibility(expectDeclaration, actualDeclaration, annotationMode, parentSubstitutor) return getCallablesMatchingIncompatibility(expectDeclaration, actualDeclaration, annotationMode, parentSubstitutor)
?: ExpectActualMatchingCompatibility.MatchedSuccessfully ?: ExpectActualMatchingCompatibility.MatchedSuccessfully
} }
context(ExpectActualMatchingContext<*>) context(ExpectActualMatchingContext<*>)
private fun getCallablesStrongIncompatibility( private fun getCallablesMatchingIncompatibility(
expectDeclaration: CallableSymbolMarker, expectDeclaration: CallableSymbolMarker,
actualDeclaration: CallableSymbolMarker, actualDeclaration: CallableSymbolMarker,
insideAnnotationClass: Boolean, insideAnnotationClass: Boolean,
@@ -12,7 +12,7 @@ import org.jetbrains.kotlin.mpp.*
import org.jetbrains.kotlin.name.CallableId import org.jetbrains.kotlin.name.CallableId
import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualMatchingCompatibility
import org.jetbrains.kotlin.types.Variance import org.jetbrains.kotlin.types.Variance
import org.jetbrains.kotlin.types.model.KotlinTypeMarker import org.jetbrains.kotlin.types.model.KotlinTypeMarker
import org.jetbrains.kotlin.types.model.TypeSubstitutorMarker import org.jetbrains.kotlin.types.model.TypeSubstitutorMarker
@@ -165,7 +165,7 @@ interface ExpectActualMatchingContext<T : DeclarationSymbolMarker> : TypeSystemC
val CallableSymbolMarker.hasStableParameterNames: Boolean val CallableSymbolMarker.hasStableParameterNames: Boolean
fun onMatchedOrCompatibleMembers( fun onMatchedMembers(
expectSymbol: DeclarationSymbolMarker, expectSymbol: DeclarationSymbolMarker,
actualSymbol: DeclarationSymbolMarker, actualSymbol: DeclarationSymbolMarker,
containingExpectClassSymbol: RegularClassSymbolMarker?, containingExpectClassSymbol: RegularClassSymbolMarker?,
@@ -174,7 +174,7 @@ interface ExpectActualMatchingContext<T : DeclarationSymbolMarker> : TypeSystemC
fun onMismatchedOrIncompatibleMembersFromClassScope( fun onMismatchedOrIncompatibleMembersFromClassScope(
expectSymbol: DeclarationSymbolMarker, expectSymbol: DeclarationSymbolMarker,
actualSymbolsByIncompatibility: Map<out ExpectActualCompatibility.MismatchOrIncompatible<*>, List<DeclarationSymbolMarker>>, actualSymbolsByIncompatibility: Map<ExpectActualMatchingCompatibility.Mismatch, List<DeclarationSymbolMarker>>,
containingExpectClassSymbol: RegularClassSymbolMarker?, containingExpectClassSymbol: RegularClassSymbolMarker?,
containingActualClassSymbol: RegularClassSymbolMarker?, containingActualClassSymbol: RegularClassSymbolMarker?,
) {} ) {}
@@ -217,7 +217,7 @@ interface ExpectActualMatchingContext<T : DeclarationSymbolMarker> : TypeSystemC
actualClass: RegularClassSymbolMarker, actualClass: RegularClassSymbolMarker,
actualMember: DeclarationSymbolMarker, actualMember: DeclarationSymbolMarker,
checkClassScopesCompatibility: Boolean, checkClassScopesCompatibility: Boolean,
): Map<out DeclarationSymbolMarker, ExpectActualCompatibility<*>> ): Map<out DeclarationSymbolMarker, ExpectActualMatchingCompatibility>
fun DeclarationSymbolMarker.getSourceElement(): SourceElementMarker fun DeclarationSymbolMarker.getSourceElement(): SourceElementMarker
@@ -20,9 +20,6 @@ sealed interface ExpectActualCompatibility<out D> {
sealed interface MismatchOrIncompatible<out D> : ExpectActualCompatibility<D> { sealed interface MismatchOrIncompatible<out D> : ExpectActualCompatibility<D> {
val reason: String? val reason: String?
} }
// It's temporary class. KT-62590 is in progress
sealed interface MatchedOrCompatible<out D> : ExpectActualCompatibility<D>
} }
/** /**
@@ -41,7 +38,7 @@ sealed class ExpectActualMatchingCompatibility : ExpectActualCompatibility<Nothi
object FunctionTypeParameterCount : Mismatch(TYPE_PARAMETER_COUNT) object FunctionTypeParameterCount : Mismatch(TYPE_PARAMETER_COUNT)
object ParameterTypes : Mismatch("parameter types are different") object ParameterTypes : Mismatch("parameter types are different")
object FunctionTypeParameterUpperBounds : Mismatch("upper bounds of type parameters are different") object FunctionTypeParameterUpperBounds : Mismatch("upper bounds of type parameters are different")
object MatchedSuccessfully : ExpectActualMatchingCompatibility(), ExpectActualCompatibility.MatchedOrCompatible<Nothing> object MatchedSuccessfully : ExpectActualMatchingCompatibility()
} }
/** /**
@@ -96,16 +93,5 @@ sealed class ExpectActualCheckingCompatibility<out D> : ExpectActualCompatibilit
object ClassTypeParameterUpperBounds : Incompatible<Nothing>(ExpectActualMatchingCompatibility.FunctionTypeParameterUpperBounds.reason) object ClassTypeParameterUpperBounds : Incompatible<Nothing>(ExpectActualMatchingCompatibility.FunctionTypeParameterUpperBounds.reason)
object TypeParameterVariance : Incompatible<Nothing>("declaration-site variances of type parameters are different") object TypeParameterVariance : Incompatible<Nothing>("declaration-site variances of type parameters are different")
object TypeParameterReified : Incompatible<Nothing>("some type parameter is reified in one declaration and non-reified in the other") object TypeParameterReified : Incompatible<Nothing>("some type parameter is reified in one declaration and non-reified in the other")
object Compatible : ExpectActualCheckingCompatibility<Nothing>(), ExpectActualCompatibility.MatchedOrCompatible<Nothing> object Compatible : ExpectActualCheckingCompatibility<Nothing>()
} }
val ExpectActualCompatibility<*>.isCompatibleOrWeaklyIncompatible: Boolean
get() = this is ExpectActualCompatibility.MatchedOrCompatible || this is ExpectActualCheckingCompatibility
val ExpectActualCompatibility<*>.compatible: Boolean
get() = this is ExpectActualCompatibility.MatchedOrCompatible
// It's temporary function. KT-62590 is in progress
fun <T, R> Map<ExpectActualCompatibility<R>, List<T>>.getMatchedAndChecked(): List<T>? =
(get(ExpectActualMatchingCompatibility.MatchedSuccessfully).orEmpty() + get(ExpectActualCheckingCompatibility.Compatible).orEmpty())
.takeIf { it.isNotEmpty() }
@@ -56,16 +56,7 @@ data class ExpectActualMemberDiff<out M, out C>(val kind: Kind, val actualMember
} }
} }
fun ExpectActualCompatibility.MismatchOrIncompatible<*>.toMemberDiffKind(): ExpectActualMemberDiff.Kind? = when (this) { fun ExpectActualCheckingCompatibility.Incompatible<*>.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")
ExpectActualCheckingCompatibility.ReturnType -> ExpectActualMemberDiff.Kind.ReturnTypeChangedInOverride ExpectActualCheckingCompatibility.ReturnType -> ExpectActualMemberDiff.Kind.ReturnTypeChangedInOverride
ExpectActualCheckingCompatibility.ClassTypeParameterCount -> error("Not applicable because ExpectActualMemberDiff is about members") ExpectActualCheckingCompatibility.ClassTypeParameterCount -> error("Not applicable because ExpectActualMemberDiff is about members")
ExpectActualCheckingCompatibility.ClassTypeParameterUpperBounds -> error("Not applicable because ExpectActualMemberDiff is about members") ExpectActualCheckingCompatibility.ClassTypeParameterUpperBounds -> error("Not applicable because ExpectActualMemberDiff is about members")