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 096fc968bdf..7f779762cf7 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 @@ -10,11 +10,9 @@ import org.jetbrains.kotlin.descriptors.Modality import org.jetbrains.kotlin.descriptors.Visibilities 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.ExpectActualMatchingCompatibility -import org.jetbrains.kotlin.types.model.KotlinTypeMarker import org.jetbrains.kotlin.types.model.TypeSubstitutorMarker import org.jetbrains.kotlin.utils.SmartList import org.jetbrains.kotlin.utils.addToStdlib.enumMapOf @@ -225,10 +223,7 @@ object AbstractExpectActualChecker { outer@ for (expectMember in expectClassSymbol.collectAllMembers(isActualDeclaration = false)) { if (expectMember is CallableSymbolMarker && expectMember.shouldSkipMatching(expectClassSymbol)) continue - val actualMembers = actualMembersByName[expectMember.name]?.filter { actualMember -> - expectMember is CallableSymbolMarker && actualMember is CallableSymbolMarker || - expectMember is RegularClassSymbolMarker && actualMember is RegularClassSymbolMarker - }.orEmpty() + val actualMembers = getPossibleActualsByExpectName(expectMember, actualMembersByName) val matched = AbstractExpectActualMatcher.matchSingleExpectAgainstPotentialActuals( expectMember, @@ -317,22 +312,9 @@ object AbstractExpectActualChecker { expectContainingClass: RegularClassSymbolMarker?, actualContainingClass: RegularClassSymbolMarker?, ): ExpectActualCheckingCompatibility<*> { - require( - (expectDeclaration is ConstructorSymbolMarker && actualDeclaration is ConstructorSymbolMarker) || - expectDeclaration.callableId.callableName == actualDeclaration.callableId.callableName - ) { - "This function should be invoked only for declarations with the same name: $expectDeclaration, $actualDeclaration" - } - require((expectDeclaration.dispatchReceiverType == null) == (actualDeclaration.dispatchReceiverType == null)) { - "This function should be invoked only for declarations in the same kind of container (both members or both top level): $expectDeclaration, $actualDeclaration" - } + checkCallablesInvariants(expectDeclaration, actualDeclaration) - if ( - expectContainingClass?.classKind == ClassKind.ENUM_CLASS && - actualContainingClass?.classKind == ClassKind.ENUM_CLASS && - expectDeclaration is ConstructorSymbolMarker && - actualDeclaration is ConstructorSymbolMarker - ) { + if (areEnumConstructors(expectDeclaration, actualDeclaration, expectContainingClass, actualContainingClass)) { return ExpectActualCheckingCompatibility.Compatible } @@ -451,23 +433,6 @@ object AbstractExpectActualChecker { return null } - context(ExpectActualMatchingContext<*>) - private fun areCompatibleTypeLists( - expectedTypes: List, - actualTypes: List, - insideAnnotationClass: Boolean, - ): Boolean { - for (i in expectedTypes.indices) { - if (!areCompatibleExpectActualTypes( - expectedTypes[i], actualTypes[i], parameterOfAnnotationComparisonMode = insideAnnotationClass - ) - ) { - return false - } - } - return true - } - context(ExpectActualMatchingContext<*>) private fun areCompatibleClassKinds( expectClass: RegularClassSymbolMarker, @@ -542,26 +507,6 @@ object AbstractExpectActualChecker { return result != null && result > 0 } - context(ExpectActualMatchingContext<*>) - private fun areCompatibleTypeParameterUpperBounds( - expectTypeParameterSymbols: List, - actualTypeParameterSymbols: List, - substitutor: TypeSubstitutorMarker, - ): Boolean { - for (i in expectTypeParameterSymbols.indices) { - val expectBounds = expectTypeParameterSymbols[i].bounds - val actualBounds = actualTypeParameterSymbols[i].bounds - if ( - expectBounds.size != actualBounds.size || - !areCompatibleTypeLists(expectBounds.map { substitutor.safeSubstitute(it) }, actualBounds, insideAnnotationClass = false) - ) { - return false - } - } - - return true - } - context(ExpectActualMatchingContext<*>) private fun getTypeParametersVarianceOrReifiedIncompatibility( expectTypeParameterSymbols: List, @@ -645,18 +590,6 @@ object AbstractExpectActualChecker { private inline fun equalBy(first: T, second: T, selector: (T) -> K): Boolean = selector(first) == selector(second) - context(ExpectActualMatchingContext<*>) - private val DeclarationSymbolMarker.name: Name - get() = when (this) { - is ConstructorSymbolMarker -> SpecialNames.INIT - is ValueParameterSymbolMarker -> parameterName - is CallableSymbolMarker -> callableId.callableName - is RegularClassSymbolMarker -> classId.shortClassName - is TypeAliasSymbolMarker -> classId.shortClassName - is TypeParameterSymbolMarker -> parameterName - else -> error("Unsupported declaration: $this") - } - context(ExpectActualMatchingContext<*>) private val RegularClassSymbolMarker.isCtorless: Boolean get() = getMembersForExpectClass(SpecialNames.INIT).isEmpty() 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 2f7b9892c3a..6046292ec61 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 @@ -7,8 +7,6 @@ package org.jetbrains.kotlin.resolve.calls.mpp import org.jetbrains.kotlin.descriptors.ClassKind import org.jetbrains.kotlin.mpp.* -import org.jetbrains.kotlin.name.Name -import org.jetbrains.kotlin.name.SpecialNames import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualMatchingCompatibility import org.jetbrains.kotlin.types.model.KotlinTypeMarker import org.jetbrains.kotlin.types.model.TypeSubstitutorMarker @@ -93,10 +91,7 @@ object AbstractExpectActualMatcher { outer@ for (expectMember in expectClassSymbol.collectAllMembers(isActualDeclaration = false)) { if (expectMember is CallableSymbolMarker && expectMember.shouldSkipMatching(expectClassSymbol)) continue - val actualMembers = actualMembersByName[expectMember.name]?.filter { actualMember -> - expectMember is CallableSymbolMarker && actualMember is CallableSymbolMarker || - expectMember is RegularClassSymbolMarker && actualMember is RegularClassSymbolMarker - }.orEmpty() + val actualMembers = getPossibleActualsByExpectName(expectMember, actualMembersByName) matchSingleExpectAgainstPotentialActuals( expectMember, @@ -169,22 +164,9 @@ object AbstractExpectActualMatcher { expectContainingClass: RegularClassSymbolMarker?, actualContainingClass: RegularClassSymbolMarker?, ): ExpectActualMatchingCompatibility { - require( - (expectDeclaration is ConstructorSymbolMarker && actualDeclaration is ConstructorSymbolMarker) || - expectDeclaration.callableId.callableName == actualDeclaration.callableId.callableName - ) { - "This function should be invoked only for declarations with the same name: $expectDeclaration, $actualDeclaration" - } - require((expectDeclaration.dispatchReceiverType == null) == (actualDeclaration.dispatchReceiverType == null)) { - "This function should be invoked only for declarations in the same kind of container (both members or both top level): $expectDeclaration, $actualDeclaration" - } + checkCallablesInvariants(expectDeclaration, actualDeclaration) - if ( - expectContainingClass?.classKind == ClassKind.ENUM_CLASS && - actualContainingClass?.classKind == ClassKind.ENUM_CLASS && - expectDeclaration is ConstructorSymbolMarker && - actualDeclaration is ConstructorSymbolMarker - ) { + if (areEnumConstructors(expectDeclaration, actualDeclaration, expectContainingClass, actualContainingClass)) { return ExpectActualMatchingCompatibility.MatchedSuccessfully } @@ -265,57 +247,8 @@ object AbstractExpectActualMatcher { } } - context(ExpectActualMatchingContext<*>) - private fun areCompatibleTypeLists( - expectedTypes: List, - actualTypes: List, - insideAnnotationClass: Boolean, - ): Boolean { - for (i in expectedTypes.indices) { - if (!areCompatibleExpectActualTypes( - expectedTypes[i], actualTypes[i], parameterOfAnnotationComparisonMode = insideAnnotationClass - ) - ) { - return false - } - } - return true - } - - context(ExpectActualMatchingContext<*>) - private fun areCompatibleTypeParameterUpperBounds( - expectTypeParameterSymbols: List, - actualTypeParameterSymbols: List, - substitutor: TypeSubstitutorMarker, - ): Boolean { - for (i in expectTypeParameterSymbols.indices) { - val expectBounds = expectTypeParameterSymbols[i].bounds - val actualBounds = actualTypeParameterSymbols[i].bounds - if ( - expectBounds.size != actualBounds.size || - !areCompatibleTypeLists(expectBounds.map { substitutor.safeSubstitute(it) }, actualBounds, insideAnnotationClass = false) - ) { - return false - } - } - - return true - } - // ---------------------------------------- Utils ---------------------------------------- - context(ExpectActualMatchingContext<*>) - private val DeclarationSymbolMarker.name: Name - get() = when (this) { - is ConstructorSymbolMarker -> SpecialNames.INIT - is ValueParameterSymbolMarker -> parameterName - is CallableSymbolMarker -> callableId.callableName - is RegularClassSymbolMarker -> classId.shortClassName - is TypeAliasSymbolMarker -> classId.shortClassName - is TypeParameterSymbolMarker -> parameterName - else -> error("Unsupported declaration: $this") - } - context(ExpectActualMatchingContext<*>) private fun List.toTypeList(substitutor: TypeSubstitutorMarker): List { return this.map { substitutor.safeSubstitute(it.returnType) } diff --git a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/mpp/matcherCheckerCommonUtils.kt b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/mpp/matcherCheckerCommonUtils.kt new file mode 100644 index 00000000000..20980e6b704 --- /dev/null +++ b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/mpp/matcherCheckerCommonUtils.kt @@ -0,0 +1,109 @@ +/* + * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.resolve.calls.mpp + +import org.jetbrains.kotlin.descriptors.ClassKind +import org.jetbrains.kotlin.mpp.* +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.name.SpecialNames +import org.jetbrains.kotlin.types.model.KotlinTypeMarker +import org.jetbrains.kotlin.types.model.TypeSubstitutorMarker + +context(ExpectActualMatchingContext<*>) +internal fun getPossibleActualsByExpectName( + expectMember: DeclarationSymbolMarker, + actualMembersByName: Map>, +): List { + val actualMembers = actualMembersByName[expectMember.name]?.filter { actualMember -> + expectMember is CallableSymbolMarker && actualMember is CallableSymbolMarker || + expectMember is RegularClassSymbolMarker && actualMember is RegularClassSymbolMarker + }.orEmpty() + return actualMembers +} + +context(ExpectActualMatchingContext<*>) +internal val DeclarationSymbolMarker.name: Name + get() = when (this) { + is ConstructorSymbolMarker -> SpecialNames.INIT + is ValueParameterSymbolMarker -> parameterName + is CallableSymbolMarker -> callableId.callableName + is RegularClassSymbolMarker -> classId.shortClassName + is TypeAliasSymbolMarker -> classId.shortClassName + is TypeParameterSymbolMarker -> parameterName + else -> error("Unsupported declaration: $this") + } + +context(ExpectActualMatchingContext<*>) +internal fun areCompatibleTypeParameterUpperBounds( + expectTypeParameterSymbols: List, + actualTypeParameterSymbols: List, + substitutor: TypeSubstitutorMarker, +): Boolean { + for (i in expectTypeParameterSymbols.indices) { + val expectBounds = expectTypeParameterSymbols[i].bounds + val actualBounds = actualTypeParameterSymbols[i].bounds + if ( + expectBounds.size != actualBounds.size || + !areCompatibleTypeLists( + expectBounds.map { substitutor.safeSubstitute(it) }, + actualBounds, + insideAnnotationClass = false + ) + ) { + return false + } + } + + return true +} + +context(ExpectActualMatchingContext<*>) +internal fun areCompatibleTypeLists( + expectedTypes: List, + actualTypes: List, + insideAnnotationClass: Boolean, +): Boolean { + for (i in expectedTypes.indices) { + if (!areCompatibleExpectActualTypes( + expectedTypes[i], actualTypes[i], parameterOfAnnotationComparisonMode = insideAnnotationClass + ) + ) { + return false + } + } + return true +} + +/** + * In terms of KMP, there is no such thing as `expect constructor` for enums, + * but they are physically exist in FIR and IR, so we need to skip matching and checking for them + */ +context(ExpectActualMatchingContext<*>) +internal fun areEnumConstructors( + expectDeclaration: CallableSymbolMarker, + actualDeclaration: CallableSymbolMarker, + expectContainingClass: RegularClassSymbolMarker?, + actualContainingClass: RegularClassSymbolMarker?, +): Boolean = expectContainingClass?.classKind == ClassKind.ENUM_CLASS && + actualContainingClass?.classKind == ClassKind.ENUM_CLASS && + expectDeclaration is ConstructorSymbolMarker && + actualDeclaration is ConstructorSymbolMarker + +context(ExpectActualMatchingContext<*>) +internal fun checkCallablesInvariants( + expectDeclaration: CallableSymbolMarker, + actualDeclaration: CallableSymbolMarker, +) { + require( + (expectDeclaration is ConstructorSymbolMarker && actualDeclaration is ConstructorSymbolMarker) || + expectDeclaration.callableId.callableName == actualDeclaration.callableId.callableName + ) { + "This function should be invoked only for declarations with the same name: $expectDeclaration, $actualDeclaration" + } + require((expectDeclaration.dispatchReceiverType == null) == (actualDeclaration.dispatchReceiverType == null)) { + "This function should be invoked only for declarations in the same kind of container (both members or both top level): $expectDeclaration, $actualDeclaration" + } +}