1/2 Extract K1 KMP matcher in its own subsystem. Copy files

This commit is literally this shell command:
```
cp compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/mpp/AbstractExpectActualAnnotationMatchChecker.kt compiler/resolution/src/org/jetbrains/kotlin/resolve/multiplatform/K1AbstractExpectActualAnnotationMatchChecker.kt
cp compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/mpp/AbstractExpectActualCompatibilityChecker.kt compiler/resolution/src/org/jetbrains/kotlin/resolve/multiplatform/K1AbstractExpectActualCompatibilityChecker.kt
cp compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/mpp/ExpectActualCollectionArgumentsCompatibilityCheckStrategy.kt compiler/resolution/src/org/jetbrains/kotlin/resolve/multiplatform/K1ExpectActualCollectionArgumentsCompatibilityCheckStrategy.kt
cp core/compiler.common/src/org/jetbrains/kotlin/resolve/multiplatform/ExpectActualCompatibility.kt compiler/resolution/src/org/jetbrains/kotlin/resolve/multiplatform/K1ExpectActualCompatibility.kt
cp compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/mpp/ExpectActualMatchingContext.kt compiler/resolution/src/org/jetbrains/kotlin/resolve/multiplatform/K1ExpectActualMatchingContext.kt
cp core/compiler.common/src/org/jetbrains/kotlin/resolve/multiplatform/ExpectActualMemberDiff.kt compiler/resolution/src/org/jetbrains/kotlin/resolve/multiplatform/K1ExpectActualMemberDiff.kt
```

Motivation: KMP is going to evolve in K2 a lot. But we don't want to
touch K1 version of KMP. That's why it's easeir to just copy-paste
expect-actual matcher into K1 flavor
This commit is contained in:
Nikita Bobko
2023-10-04 16:11:56 +02:00
committed by teamcity
parent 96c45c74e8
commit cc70f78027
6 changed files with 1437 additions and 0 deletions
@@ -0,0 +1,286 @@
/*
* 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.ClassId
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.name.StandardClassIds
import org.jetbrains.kotlin.resolve.checkers.OptInNames
import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility
import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualAnnotationsIncompatibilityType as IncompatibilityType
object AbstractExpectActualAnnotationMatchChecker {
private val SKIPPED_CLASS_IDS = setOf(
StandardClassIds.Annotations.Deprecated,
StandardClassIds.Annotations.DeprecatedSinceKotlin,
StandardClassIds.Annotations.ImplicitlyActualizedByJvmDeclaration,
StandardClassIds.Annotations.OptionalExpectation,
StandardClassIds.Annotations.RequireKotlin,
StandardClassIds.Annotations.SinceKotlin,
StandardClassIds.Annotations.Suppress,
StandardClassIds.Annotations.WasExperimental,
OptInNames.OPT_IN_CLASS_ID,
)
class Incompatibility(
/**
* [expectSymbol] and [actualSymbol] are declaration symbols where annotation been mismatched.
* They are needed for writing whole declarations in diagnostic text.
* They are not the same as symbols passed to checker as arguments in [areAnnotationsCompatible] in following cases:
* 1. If [actualSymbol] is typealias, it will be expanded.
* 2. If problem is in class member, [expectSymbol] will be mismatched member, not the original class.
* 3. If annotation mismatched on function value parameter, symbols will be whole functions, not value parameter symbols.
*/
val expectSymbol: DeclarationSymbolMarker,
val actualSymbol: DeclarationSymbolMarker,
/**
* Link to source code element (possibly holding null, if no source) from actual declaration
* where mismatched actual annotation is set (or should be set if it is missing).
* Needed for the implementation of IDE intention.
*/
val actualAnnotationTargetElement: SourceElementMarker,
val type: IncompatibilityType<ExpectActualMatchingContext.AnnotationCallInfo>,
)
fun areAnnotationsCompatible(
expectSymbol: DeclarationSymbolMarker,
actualSymbol: DeclarationSymbolMarker,
context: ExpectActualMatchingContext<*>,
): Incompatibility? = with(context) {
areAnnotationsCompatible(expectSymbol, actualSymbol)
}
context (ExpectActualMatchingContext<*>)
private fun areAnnotationsCompatible(
expectSymbol: DeclarationSymbolMarker,
actualSymbol: DeclarationSymbolMarker,
): Incompatibility? {
return when (expectSymbol) {
is CallableSymbolMarker -> {
areCallableAnnotationsCompatible(expectSymbol, actualSymbol as CallableSymbolMarker)
}
is RegularClassSymbolMarker -> {
areClassAnnotationsCompatible(expectSymbol, actualSymbol as ClassLikeSymbolMarker)
}
else -> error("Incorrect types: $expectSymbol $actualSymbol")
}
}
context (ExpectActualMatchingContext<*>)
private fun areCallableAnnotationsCompatible(
expectSymbol: CallableSymbolMarker,
actualSymbol: CallableSymbolMarker,
): Incompatibility? {
commonForClassAndCallableChecks(expectSymbol, actualSymbol)?.let { return it }
areAnnotationsOnValueParametersCompatible(expectSymbol, actualSymbol)?.let { return it }
if (expectSymbol is PropertySymbolMarker && actualSymbol is PropertySymbolMarker) {
arePropertyGetterAndSetterAnnotationsCompatible(expectSymbol, actualSymbol)?.let { return it }
}
return null
}
context (ExpectActualMatchingContext<*>)
private fun arePropertyGetterAndSetterAnnotationsCompatible(
expectSymbol: PropertySymbolMarker,
actualSymbol: PropertySymbolMarker,
): Incompatibility? {
listOf(
expectSymbol.getter to actualSymbol.getter,
expectSymbol.setter to actualSymbol.setter,
).forEach { (expectAccessor, actualAccessor) ->
if (expectAccessor != null && actualAccessor != null) {
areAnnotationsSetOnDeclarationsCompatible(expectAccessor, actualAccessor)?.let {
// Write containing declarations into diagnostic
return Incompatibility(expectSymbol, actualSymbol, actualAccessor.getSourceElement(), it.type)
}
}
}
return null
}
context (ExpectActualMatchingContext<*>)
private fun areClassAnnotationsCompatible(
expectSymbol: RegularClassSymbolMarker,
actualSymbol: ClassLikeSymbolMarker,
): Incompatibility? {
if (actualSymbol is TypeAliasSymbolMarker) {
val expanded = actualSymbol.expandToRegularClass() ?: return null
return areClassAnnotationsCompatible(expectSymbol, expanded)
}
check(actualSymbol is RegularClassSymbolMarker)
commonForClassAndCallableChecks(expectSymbol, actualSymbol)?.let { return it }
if (checkClassScopesForAnnotationCompatibility) {
checkAnnotationsInClassMemberScope(expectSymbol, actualSymbol)?.let { return it }
}
if (expectSymbol.classKind == ClassKind.ENUM_CLASS && actualSymbol.classKind == ClassKind.ENUM_CLASS) {
checkAnnotationsOnEnumEntries(expectSymbol, actualSymbol)?.let { return it }
}
return null
}
context (ExpectActualMatchingContext<*>)
private fun commonForClassAndCallableChecks(
expectSymbol: DeclarationSymbolMarker,
actualSymbol: DeclarationSymbolMarker,
): Incompatibility? {
areAnnotationsSetOnDeclarationsCompatible(expectSymbol, actualSymbol)?.let { return it }
areAnnotationsOnTypeParametersCompatible(expectSymbol, actualSymbol)?.let { return it }
return null
}
context (ExpectActualMatchingContext<*>)
private fun areAnnotationsOnValueParametersCompatible(
expectSymbol: CallableSymbolMarker,
actualSymbol: CallableSymbolMarker,
): Incompatibility? {
val expectParams = expectSymbol.valueParameters
val actualParams = actualSymbol.valueParameters
if (expectParams.size != actualParams.size) return null
return expectParams.zip(actualParams).firstNotNullOfOrNull { (expectParam, actualParam) ->
areAnnotationsSetOnDeclarationsCompatible(expectParam, actualParam)?.let {
// Write containing declarations into diagnostic
Incompatibility(expectSymbol, actualSymbol, actualParam.getSourceElement(), it.type)
}
}
}
context (ExpectActualMatchingContext<*>)
private fun areAnnotationsOnTypeParametersCompatible(
expectSymbol: DeclarationSymbolMarker,
actualSymbol: DeclarationSymbolMarker,
): Incompatibility? {
fun DeclarationSymbolMarker.getTypeParameters(): List<TypeParameterSymbolMarker>? {
return when (this) {
is FunctionSymbolMarker -> typeParameters
is RegularClassSymbolMarker -> typeParameters
else -> null
}
}
val expectParams = expectSymbol.getTypeParameters() ?: return null
val actualParams = actualSymbol.getTypeParameters() ?: return null
if (expectParams.size != actualParams.size) return null
return expectParams.zip(actualParams).firstNotNullOfOrNull { (expectParam, actualParam) ->
areAnnotationsSetOnDeclarationsCompatible(expectParam, actualParam)?.let {
// Write containing declarations into diagnostic
Incompatibility(expectSymbol, actualSymbol, actualParam.getSourceElement(), it.type)
}
}
}
context (ExpectActualMatchingContext<*>)
private fun areAnnotationsSetOnDeclarationsCompatible(
expectSymbol: DeclarationSymbolMarker,
actualSymbol: DeclarationSymbolMarker,
): Incompatibility? {
// TODO(Roman.Efremov, KT-60671): check annotations set on types
val skipSourceAnnotations = actualSymbol.hasSourceAnnotationsErased
val actualAnnotationsByName = actualSymbol.annotations.groupBy { it.classId }
for (expectAnnotation in expectSymbol.annotations) {
val expectClassId = expectAnnotation.classId ?: continue
if (expectClassId in SKIPPED_CLASS_IDS || expectAnnotation.isOptIn) {
continue
}
if (expectAnnotation.isRetentionSource && skipSourceAnnotations) {
continue
}
val actualAnnotationsWithSameClassId = actualAnnotationsByName[expectClassId] ?: emptyList()
if (actualAnnotationsWithSameClassId.isEmpty()) {
return Incompatibility(
expectSymbol,
actualSymbol,
actualSymbol.getSourceElement(),
IncompatibilityType.MissingOnActual(expectAnnotation)
)
}
val collectionCompatibilityChecker = getAnnotationCollectionArgumentsCompatibilityChecker(expectClassId)
if (actualAnnotationsWithSameClassId.none {
areAnnotationArgumentsEqual(expectAnnotation, it, collectionCompatibilityChecker)
}) {
val incompatibilityType = if (actualAnnotationsWithSameClassId.size == 1) {
IncompatibilityType.DifferentOnActual(expectAnnotation, actualAnnotationsWithSameClassId.single())
} else {
// In the case of repeatable annotations, we can't choose on which to report
IncompatibilityType.MissingOnActual(expectAnnotation)
}
return Incompatibility(expectSymbol, actualSymbol, actualSymbol.getSourceElement(), incompatibilityType)
}
}
return null
}
private fun getAnnotationCollectionArgumentsCompatibilityChecker(annotationClassId: ClassId):
ExpectActualCollectionArgumentsCompatibilityCheckStrategy {
return if (annotationClassId == StandardClassIds.Annotations.Target) {
ExpectActualCollectionArgumentsCompatibilityCheckStrategy.ExpectIsSubsetOfActual
} else {
ExpectActualCollectionArgumentsCompatibilityCheckStrategy.Default
}
}
context (ExpectActualMatchingContext<*>)
private fun checkAnnotationsInClassMemberScope(
expectClass: RegularClassSymbolMarker,
actualClass: RegularClassSymbolMarker,
): Incompatibility? {
for (actualMember in actualClass.collectAllMembers(isActualDeclaration = true)) {
if (skipCheckingAnnotationsOfActualClassMember(actualMember)) {
continue
}
val expectToCompatibilityMap = findPotentialExpectClassMembersForActual(
expectClass, actualClass, actualMember,
// Optimization: don't check class scopes, because:
// 1. Annotation checker runs no matter if found expect class is compatible or not.
// 2. Class always has at most one corresponding `expect` class (unlike for functions, which may have several overrides),
// so we are sure that we found the right member.
checkClassScopesCompatibility = false,
)
val expectMember = expectToCompatibilityMap.filter { it.value == ExpectActualCompatibility.Compatible }.keys.singleOrNull()
// Check also incompatible members if only one is found
?: expectToCompatibilityMap.keys.singleOrNull()
?: continue
areAnnotationsCompatible(expectMember, actualMember)?.let { return it }
}
return null
}
context (ExpectActualMatchingContext<*>)
private fun checkAnnotationsOnEnumEntries(
expectClassSymbol: RegularClassSymbolMarker,
actualClassSymbol: RegularClassSymbolMarker,
): Incompatibility? {
fun DeclarationSymbolMarker.getEnumEntryName(): Name =
when (this) {
is CallableSymbolMarker -> callableId.callableName
is RegularClassSymbolMarker -> classId.shortClassName
else -> error("Unexpected type $this")
}
val expectEnumEntries = expectClassSymbol.collectEnumEntries()
val actualEnumEntriesByName = actualClassSymbol.collectEnumEntries().associateBy { it.getEnumEntryName() }
for (expectEnumEntry in expectEnumEntries) {
val actualEnumEntry = actualEnumEntriesByName[expectEnumEntry.getEnumEntryName()] ?: continue
areAnnotationsSetOnDeclarationsCompatible(expectEnumEntry, actualEnumEntry)
?.let { return it }
}
return null
}
}
@@ -0,0 +1,693 @@
/*
* 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.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.ExpectActualCompatibility
import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility.Incompatible
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
import org.jetbrains.kotlin.utils.addToStdlib.enumSetOf
import org.jetbrains.kotlin.utils.keysToMap
import java.util.*
object AbstractExpectActualCompatibilityChecker {
fun <T : DeclarationSymbolMarker> getClassifiersCompatibility(
expectClassSymbol: RegularClassSymbolMarker,
actualClassLikeSymbol: ClassLikeSymbolMarker,
checkClassScopesCompatibility: Boolean,
context: ExpectActualMatchingContext<T>,
): ExpectActualCompatibility<T> {
val result = with(context) {
getClassifiersCompatibility(expectClassSymbol, actualClassLikeSymbol, parentSubstitutor = null, checkClassScopesCompatibility)
}
@Suppress("UNCHECKED_CAST")
return result as ExpectActualCompatibility<T>
}
fun <T : DeclarationSymbolMarker> getCallablesCompatibility(
expectDeclaration: CallableSymbolMarker,
actualDeclaration: CallableSymbolMarker,
parentSubstitutor: TypeSubstitutorMarker?,
expectContainingClass: RegularClassSymbolMarker?,
actualContainingClass: RegularClassSymbolMarker?,
context: ExpectActualMatchingContext<T>,
): ExpectActualCompatibility<T> {
val result = with(context) {
getCallablesCompatibility(expectDeclaration, actualDeclaration, parentSubstitutor, expectContainingClass, actualContainingClass)
}
@Suppress("UNCHECKED_CAST")
return result as ExpectActualCompatibility<T>
}
fun <T : DeclarationSymbolMarker> matchSingleExpectTopLevelDeclarationAgainstPotentialActuals(
expectDeclaration: DeclarationSymbolMarker,
actualDeclarations: List<DeclarationSymbolMarker>,
context: ExpectActualMatchingContext<T>,
) {
with(context) {
matchSingleExpectAgainstPotentialActuals(
expectDeclaration,
actualDeclarations,
substitutor = null,
expectClassSymbol = null,
actualClassSymbol = null,
unfulfilled = null,
checkClassScopesCompatibility = true,
)
}
}
context(ExpectActualMatchingContext<*>)
@Suppress("warnings")
private fun getClassifiersCompatibility(
expectClassSymbol: RegularClassSymbolMarker,
actualClassLikeSymbol: ClassLikeSymbolMarker,
parentSubstitutor: TypeSubstitutorMarker?,
checkClassScopes: Boolean,
): ExpectActualCompatibility<*> = getClassifiersIncompatibility(expectClassSymbol, actualClassLikeSymbol, parentSubstitutor, checkClassScopes)
?: ExpectActualCompatibility.Compatible
context(ExpectActualMatchingContext<*>)
@Suppress("warnings")
private fun getClassifiersIncompatibility(
expectClassSymbol: RegularClassSymbolMarker,
actualClassLikeSymbol: ClassLikeSymbolMarker,
parentSubstitutor: TypeSubstitutorMarker?,
checkClassScopesCompatibility: Boolean,
): ExpectActualCompatibility.Incompatible.WeakIncompatible<*>? {
// 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"
}
val actualClass = when (actualClassLikeSymbol) {
is RegularClassSymbolMarker -> actualClassLikeSymbol
is TypeAliasSymbolMarker -> actualClassLikeSymbol.expandToRegularClass()
?: return null // do not report extra error on erroneous typealias
else -> error("Incorrect actual classifier for $expectClassSymbol: $actualClassLikeSymbol")
}
if (!areCompatibleClassKinds(expectClassSymbol, actualClass)) return Incompatible.ClassKind
if (!equalBy(expectClassSymbol, actualClass) { listOf(it.isCompanion, it.isInner, it.isInline || it.isValue) }) {
return Incompatible.ClassModifiers
}
if (expectClassSymbol.isFun && !actualClass.isFun && actualClass.isNotSamInterface()) {
return Incompatible.FunInterfaceModifier
}
val expectTypeParameterSymbols = expectClassSymbol.typeParameters
val actualTypeParameterSymbols = actualClass.typeParameters
if (expectTypeParameterSymbols.size != actualTypeParameterSymbols.size) {
return Incompatible.ClassTypeParameterCount
}
if (!areCompatibleModalities(expectClassSymbol.modality, actualClass.modality)) {
return Incompatible.Modality
}
if (!areCompatibleClassVisibilities(expectClassSymbol, actualClass)) {
return Incompatible.Visibility
}
val substitutor = createExpectActualTypeParameterSubstitutor(
expectTypeParameterSymbols,
actualTypeParameterSymbols,
parentSubstitutor
)
if (!areCompatibleTypeParameterUpperBounds(expectTypeParameterSymbols, actualTypeParameterSymbols, substitutor)) {
return Incompatible.ClassTypeParameterUpperBounds
}
getTypeParametersVarianceOrReifiedIncompatibility(expectTypeParameterSymbols, actualTypeParameterSymbols)
?.let { return it }
if (!areCompatibleSupertypes(expectClassSymbol, actualClass, substitutor)) {
return Incompatible.Supertypes
}
if (checkClassScopesCompatibility) {
getClassScopesIncompatibility(expectClassSymbol, actualClass, substitutor)?.let { return it }
}
return null
}
context(ExpectActualMatchingContext<*>)
private fun areCompatibleSupertypes(
expectClassSymbol: RegularClassSymbolMarker,
actualClassSymbol: RegularClassSymbolMarker,
substitutor: TypeSubstitutorMarker,
): Boolean {
return when (allowTransitiveSupertypesActualization) {
false -> areCompatibleSupertypesOneByOne(expectClassSymbol, actualClassSymbol, substitutor)
true -> areCompatibleSupertypesTransitive(expectClassSymbol, actualClassSymbol, substitutor)
}
}
context(ExpectActualMatchingContext<*>)
private fun areCompatibleSupertypesOneByOne(
expectClassSymbol: RegularClassSymbolMarker,
actualClassSymbol: RegularClassSymbolMarker,
substitutor: TypeSubstitutorMarker,
): Boolean {
// Subtract kotlin.Any from supertypes because it's implicitly added if no explicit supertype is specified,
// and not added if an explicit supertype _is_ specified
val expectSupertypes = expectClassSymbol.superTypes.filterNot { it.typeConstructor().isAnyConstructor() }
val actualSupertypes = actualClassSymbol.superTypes.filterNot { it.typeConstructor().isAnyConstructor() }
return expectSupertypes.all { expectSupertype ->
val substitutedExpectType = substitutor.safeSubstitute(expectSupertype)
actualSupertypes.any { actualSupertype ->
areCompatibleExpectActualTypes(substitutedExpectType, actualSupertype)
}
}
}
context(ExpectActualMatchingContext<*>)
private fun areCompatibleSupertypesTransitive(
expectClassSymbol: RegularClassSymbolMarker,
actualClassSymbol: RegularClassSymbolMarker,
substitutor: TypeSubstitutorMarker,
): Boolean {
val expectSupertypes = expectClassSymbol.superTypes.filterNot { it.typeConstructor().isAnyConstructor() }
val actualType = actualClassSymbol.defaultType
return expectSupertypes.all { expectSupertype ->
actualTypeIsSubtypeOfExpectType(
expectType = substitutor.safeSubstitute(expectSupertype),
actualType = actualType
)
}
}
context(ExpectActualMatchingContext<*>)
private fun getClassScopesIncompatibility(
expectClassSymbol: RegularClassSymbolMarker,
actualClassSymbol: RegularClassSymbolMarker,
substitutor: TypeSubstitutorMarker,
): Incompatible.WeakIncompatible<*>? {
val unfulfilled = arrayListOf<Pair<DeclarationSymbolMarker, Map<Incompatible<*>, List<DeclarationSymbolMarker?>>>>()
val actualMembersByName = actualClassSymbol.collectAllMembers(isActualDeclaration = true).groupBy { it.name }
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()
matchSingleExpectAgainstPotentialActuals(
expectMember,
actualMembers,
substitutor,
expectClassSymbol,
actualClassSymbol,
unfulfilled,
checkClassScopesCompatibility = true,
)
}
if (expectClassSymbol.classKind == ClassKind.ENUM_CLASS) {
val aEntries = expectClassSymbol.collectEnumEntryNames()
val bEntries = actualClassSymbol.collectEnumEntryNames()
if (!bEntries.containsAll(aEntries)) return Incompatible.EnumEntries
}
// TODO: check static scope?
if (unfulfilled.isEmpty()) return null
return Incompatible.ClassScopes(unfulfilled)
}
context(ExpectActualMatchingContext<*>)
private fun matchSingleExpectAgainstPotentialActuals(
expectMember: DeclarationSymbolMarker,
actualMembers: List<DeclarationSymbolMarker>,
substitutor: TypeSubstitutorMarker?,
expectClassSymbol: RegularClassSymbolMarker?,
actualClassSymbol: RegularClassSymbolMarker?,
unfulfilled: MutableList<Pair<DeclarationSymbolMarker, Map<Incompatible<*>, List<DeclarationSymbolMarker?>>>>?,
checkClassScopesCompatibility: Boolean,
) {
val mapping = actualMembers.keysToMap { actualMember ->
when (expectMember) {
is CallableSymbolMarker -> getCallablesCompatibility(
expectMember,
actualMember as CallableSymbolMarker,
substitutor,
expectClassSymbol,
actualClassSymbol
)
is RegularClassSymbolMarker -> {
val parentSubstitutor = substitutor?.takeIf { !innerClassesCapturesOuterTypeParameters }
getClassifiersCompatibility(
expectMember,
actualMember as ClassLikeSymbolMarker,
parentSubstitutor,
checkClassScopesCompatibility,
)
}
else -> error("Unsupported declaration: $expectMember ($actualMembers)")
}
}
val incompatibilityMap = mutableMapOf<Incompatible<*>, MutableList<DeclarationSymbolMarker>>()
for ((actualMember, compatibility) in mapping) {
when (compatibility) {
ExpectActualCompatibility.Compatible -> {
onMatchedMembers(expectMember, actualMember, expectClassSymbol, actualClassSymbol)
return
}
is Incompatible -> incompatibilityMap.getOrPut(compatibility) { SmartList() }.add(actualMember)
}
}
unfulfilled?.add(expectMember to incompatibilityMap)
onMismatchedMembersFromClassScope(expectMember, incompatibilityMap, expectClassSymbol, actualClassSymbol)
}
context(ExpectActualMatchingContext<*>)
private fun getCallablesCompatibility(
expectDeclaration: CallableSymbolMarker,
actualDeclaration: CallableSymbolMarker,
parentSubstitutor: TypeSubstitutorMarker?,
expectContainingClass: RegularClassSymbolMarker?,
actualContainingClass: RegularClassSymbolMarker?,
): ExpectActualCompatibility<*> {
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"
}
if (
enumConstructorsAreAlwaysCompatible &&
expectContainingClass?.classKind == ClassKind.ENUM_CLASS &&
actualContainingClass?.classKind == ClassKind.ENUM_CLASS &&
expectDeclaration is ConstructorSymbolMarker &&
actualDeclaration is ConstructorSymbolMarker
) {
return ExpectActualCompatibility.Compatible
}
// We must prioritize to return STRONG incompatible over WEAK incompatible (because STRONG incompatibility allows to search for overloads)
return getCallablesStrongIncompatibility(expectDeclaration, actualDeclaration, parentSubstitutor)
?: getCallablesWeakIncompatibility(expectDeclaration, actualDeclaration, expectContainingClass, actualContainingClass)
?: ExpectActualCompatibility.Compatible
}
context(ExpectActualMatchingContext<*>)
private fun getCallablesStrongIncompatibility(
expectDeclaration: CallableSymbolMarker,
actualDeclaration: CallableSymbolMarker,
parentSubstitutor: TypeSubstitutorMarker?,
): Incompatible.StrongIncompatible<*>? {
if (expectDeclaration is FunctionSymbolMarker != actualDeclaration is FunctionSymbolMarker) {
return Incompatible.CallableKind
}
val expectedReceiverType = expectDeclaration.extensionReceiverType
val actualReceiverType = actualDeclaration.extensionReceiverType
if ((expectedReceiverType != null) != (actualReceiverType != null)) {
return Incompatible.ParameterShape
}
val expectedValueParameters = expectDeclaration.valueParameters
val actualValueParameters = actualDeclaration.valueParameters
if (!valueParametersCountCompatible(expectDeclaration, actualDeclaration, expectedValueParameters, actualValueParameters)) {
return Incompatible.ParameterCount
}
val expectedTypeParameters = expectDeclaration.typeParameters
val actualTypeParameters = actualDeclaration.typeParameters
if (expectedTypeParameters.size != actualTypeParameters.size) {
return Incompatible.FunctionTypeParameterCount
}
val substitutor = createExpectActualTypeParameterSubstitutor(
expectedTypeParameters,
actualTypeParameters,
parentSubstitutor
)
if (
!areCompatibleTypeLists(
expectedValueParameters.toTypeList(substitutor),
actualValueParameters.toTypeList(createEmptySubstitutor())
) ||
!areCompatibleExpectActualTypes(
expectedReceiverType?.let { substitutor.safeSubstitute(it) },
actualReceiverType
)
) {
return Incompatible.ParameterTypes
}
if (shouldCheckReturnTypesOfCallables) {
if (!areCompatibleExpectActualTypes(substitutor.safeSubstitute(expectDeclaration.returnType), actualDeclaration.returnType)) {
return Incompatible.ReturnType
}
}
if (!areCompatibleTypeParameterUpperBounds(expectedTypeParameters, actualTypeParameters, substitutor)) {
return Incompatible.FunctionTypeParameterUpperBounds
}
return null
}
context(ExpectActualMatchingContext<*>)
private fun getCallablesWeakIncompatibility(
expectDeclaration: CallableSymbolMarker,
actualDeclaration: CallableSymbolMarker,
expectContainingClass: RegularClassSymbolMarker?,
actualContainingClass: RegularClassSymbolMarker?,
): Incompatible.WeakIncompatible<*>? {
val expectedTypeParameters = expectDeclaration.typeParameters
val actualTypeParameters = actualDeclaration.typeParameters
val expectedValueParameters = expectDeclaration.valueParameters
val actualValueParameters = actualDeclaration.valueParameters
if (actualDeclaration.hasStableParameterNames && !equalsBy(expectedValueParameters, actualValueParameters) { it.name }) {
return Incompatible.ParameterNames
}
if (!equalsBy(expectedTypeParameters, actualTypeParameters) { it.name }) {
return Incompatible.TypeParameterNames
}
val expectModality = expectDeclaration.modality
val actualModality = actualDeclaration.modality
if (
!areCompatibleModalities(
expectModality,
actualModality,
expectContainingClass?.modality,
actualContainingClass?.modality
)
) {
return Incompatible.Modality
}
if (!areCompatibleCallableVisibilities(expectDeclaration.visibility, expectModality, actualDeclaration.visibility)) {
return Incompatible.Visibility
}
getTypeParametersVarianceOrReifiedIncompatibility(expectedTypeParameters, actualTypeParameters)?.let { return it }
if (shouldCheckAbsenceOfDefaultParamsInActual) {
// "Default parameters in actual" check is required only for functions, because only functions can have parameters
if (actualDeclaration is FunctionSymbolMarker && expectDeclaration is FunctionSymbolMarker) {
// Actual annotation constructors can have default argument values; their consistency with arguments in the expected annotation
// is checked in ExpectedActualDeclarationChecker.checkAnnotationConstructors
if (!actualDeclaration.isAnnotationConstructor() &&
// If default params came from common supertypes of actual class and expect class then it's a valid code.
// Here we filter out such default params.
(actualDeclaration.allOverriddenDeclarationsRecursive() - expectDeclaration.allOverriddenDeclarationsRecursive().toSet())
.flatMap { it.valueParameters }.any { it.hasDefaultValue }
) {
return Incompatible.ActualFunctionWithDefaultParameters
}
}
}
if (!equalsBy(expectedValueParameters, actualValueParameters) { it.isVararg }) {
return Incompatible.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
}
if (expectedValueParameters.indices.any { i -> !expectedValueParameters[i].isCrossinline && actualValueParameters[i].isCrossinline }) {
return Incompatible.ValueParameterCrossinline
}
}
when {
expectDeclaration is FunctionSymbolMarker && actualDeclaration is FunctionSymbolMarker ->
getFunctionsIncompatibility(expectDeclaration, actualDeclaration)?.let { return it }
expectDeclaration is PropertySymbolMarker && actualDeclaration is PropertySymbolMarker ->
getPropertiesIncompatibility(expectDeclaration, actualDeclaration)?.let { return it }
expectDeclaration is EnumEntrySymbolMarker && actualDeclaration is EnumEntrySymbolMarker -> {
// do nothing, entries are matched only by name
}
else -> error("Unsupported declarations: $expectDeclaration, $actualDeclaration")
}
return null
}
context(ExpectActualMatchingContext<*>)
private fun valueParametersCountCompatible(
expectDeclaration: CallableSymbolMarker,
actualDeclaration: CallableSymbolMarker,
expectValueParameters: List<ValueParameterSymbolMarker>,
actualValueParameters: List<ValueParameterSymbolMarker>,
): Boolean {
if (expectValueParameters.size == actualValueParameters.size) return true
return if (expectDeclaration.isAnnotationConstructor() && actualDeclaration.isAnnotationConstructor()) {
expectValueParameters.isEmpty() && actualValueParameters.all { it.hasDefaultValue }
} else {
false
}
}
context(ExpectActualMatchingContext<*>)
private fun areCompatibleTypeLists(
expectedTypes: List<KotlinTypeMarker?>,
actualTypes: List<KotlinTypeMarker?>,
): Boolean {
for (i in expectedTypes.indices) {
if (!areCompatibleExpectActualTypes(expectedTypes[i], actualTypes[i])) {
return false
}
}
return true
}
context(ExpectActualMatchingContext<*>)
private fun areCompatibleClassKinds(
expectClass: RegularClassSymbolMarker,
actualClass: RegularClassSymbolMarker,
): Boolean {
if (expectClass.classKind == actualClass.classKind) return true
if (expectClass.classKind == ClassKind.CLASS && expectClass.isFinal && expectClass.isCtorless) {
if (actualClass.classKind == ClassKind.OBJECT) return true
}
return false
}
private fun areCompatibleModalities(
expectModality: Modality?,
actualModality: Modality?,
expectContainingClassModality: Modality? = null,
actualContainingClassModality: Modality? = null,
): Boolean {
val expectEffectiveModality = effectiveModality(expectModality, expectContainingClassModality)
val actualEffectiveModality = effectiveModality(actualModality, actualContainingClassModality)
return actualEffectiveModality in compatibleModalityMap.getValue(expectEffectiveModality)
}
/*
* If containing class is final then all declarations in it effectively final
*/
private fun effectiveModality(declarationModality: Modality?, containingClassModality: Modality?): Modality? {
return when (containingClassModality) {
Modality.FINAL -> Modality.FINAL
else -> declarationModality
}
}
/*
* Key is expect modality, value is a set of compatible actual modalities
*/
private val compatibleModalityMap: EnumMap<Modality, EnumSet<Modality>> = enumMapOf(
Modality.ABSTRACT to enumSetOf(Modality.ABSTRACT),
Modality.OPEN to enumSetOf(Modality.OPEN),
Modality.FINAL to enumSetOf(Modality.OPEN, Modality.FINAL),
Modality.SEALED to enumSetOf(Modality.SEALED),
)
private fun areCompatibleCallableVisibilities(
expectVisibility: Visibility,
expectModality: Modality?,
actualVisibility: Visibility,
): Boolean {
val compare = Visibilities.compare(expectVisibility, actualVisibility)
return if (expectModality != Modality.FINAL) {
// For overridable declarations visibility should match precisely, see KT-19664
compare == 0
} else {
// For non-overridable declarations actuals are allowed to have more permissive visibility
compare != null && compare <= 0
}
}
context(ExpectActualMatchingContext<*>)
private fun areCompatibleClassVisibilities(
expectClassSymbol: RegularClassSymbolMarker,
actualClassSymbol: RegularClassSymbolMarker,
): Boolean {
val expectVisibility = expectClassSymbol.visibility
val actualVisibility = actualClassSymbol.visibility
if (expectVisibility == actualVisibility) return true
if (!allowClassActualizationWithWiderVisibility) return false
val result = Visibilities.compare(actualVisibility, expectVisibility)
return result != null && result > 0
}
context(ExpectActualMatchingContext<*>)
private fun areCompatibleTypeParameterUpperBounds(
expectTypeParameterSymbols: List<TypeParameterSymbolMarker>,
actualTypeParameterSymbols: List<TypeParameterSymbolMarker>,
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)
) {
return false
}
}
return true
}
context(ExpectActualMatchingContext<*>)
private fun getTypeParametersVarianceOrReifiedIncompatibility(
expectTypeParameterSymbols: List<TypeParameterSymbolMarker>,
actualTypeParameterSymbols: List<TypeParameterSymbolMarker>,
): Incompatible.WeakIncompatible<*>? {
if (!equalsBy(expectTypeParameterSymbols, actualTypeParameterSymbols) { it.variance }) {
return Incompatible.TypeParameterVariance
}
// Removing "reified" from an expected function's type parameter is fine
if (
expectTypeParameterSymbols.indices.any { i ->
!expectTypeParameterSymbols[i].isReified && actualTypeParameterSymbols[i].isReified
}
) {
return Incompatible.TypeParameterReified
}
return null
}
context(ExpectActualMatchingContext<*>)
private fun getFunctionsIncompatibility(
expectFunction: CallableSymbolMarker,
actualFunction: CallableSymbolMarker,
): Incompatible.WeakIncompatible<*>? {
if (!equalBy(expectFunction, actualFunction) { f -> f.isSuspend }) {
return Incompatible.FunctionModifiersDifferent
}
if (
expectFunction.isInfix && !actualFunction.isInfix ||
expectFunction.isInline && !actualFunction.isInline ||
expectFunction.isOperator && !actualFunction.isOperator
) {
return Incompatible.FunctionModifiersNotSubset
}
return null
}
context(ExpectActualMatchingContext<*>)
private fun getPropertiesIncompatibility(
expected: PropertySymbolMarker,
actual: PropertySymbolMarker,
): Incompatible.WeakIncompatible<*>? {
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
else -> null
}
}
context(ExpectActualMatchingContext<*>)
private fun arePropertySettersWithCompatibleVisibilities(
expected: PropertySymbolMarker,
actual: PropertySymbolMarker,
): Boolean {
val expectedSetter = expected.setter ?: return true
val actualSetter = actual.setter ?: return true
return areCompatibleCallableVisibilities(expectedSetter.visibility, expectedSetter.modality, actualSetter.visibility)
}
// ---------------------------------------- Utils ----------------------------------------
context(ExpectActualMatchingContext<*>)
private fun List<ValueParameterSymbolMarker>.toTypeList(substitutor: TypeSubstitutorMarker): List<KotlinTypeMarker> {
return this.map { substitutor.safeSubstitute(it.returnType) }
}
private inline fun <T, K> equalsBy(first: List<T>, second: List<T>, selector: (T) -> K): Boolean {
for (i in first.indices) {
if (selector(first[i]) != selector(second[i])) return false
}
return true
}
private inline fun <T, K> 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()
context(ExpectActualMatchingContext<*>)
private val RegularClassSymbolMarker.isFinal: Boolean
get() = modality == Modality.FINAL
}
@@ -0,0 +1,37 @@
/*
* 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
sealed class ExpectActualCollectionArgumentsCompatibilityCheckStrategy {
abstract fun <T> areCompatible(
expectArg: Collection<T>,
actualArg: Collection<T>,
elementsEqual: (T, T) -> Boolean,
): Boolean
data object Default : ExpectActualCollectionArgumentsCompatibilityCheckStrategy() {
override fun <T> areCompatible(
expectArg: Collection<T>,
actualArg: Collection<T>,
elementsEqual: (T, T) -> Boolean,
): Boolean {
return expectArg.size == actualArg.size && expectArg.zip(actualArg).all { (e1, e2) -> elementsEqual(e1, e2) }
}
}
internal data object ExpectIsSubsetOfActual : ExpectActualCollectionArgumentsCompatibilityCheckStrategy() {
override fun <T> areCompatible(
expectArg: Collection<T>,
actualArg: Collection<T>,
elementsEqual: (T, T) -> Boolean,
): Boolean {
return expectArg.all { e1 ->
actualArg.any { e2 -> elementsEqual(e1, e2) }
}
}
}
}
@@ -0,0 +1,115 @@
/*
* Copyright 2010-2022 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.multiplatform
import kotlin.contracts.ExperimentalContracts
import kotlin.contracts.contract
sealed class ExpectActualCompatibility<out D> {
// Note that the reason is used in the diagnostic output, see PlatformIncompatibilityDiagnosticRenderer
sealed class Incompatible<out D>(val reason: String?) : ExpectActualCompatibility<D>() {
sealed class WeakIncompatible<out D>(reason: String?) : Incompatible<D>(reason)
// For StrongIncompatible `actual` declaration is considered as overload and error reports on expected declaration
sealed class StrongIncompatible<out D>(reason: String?) : Incompatible<D>(reason)
// Callables
object CallableKind : StrongIncompatible<Nothing>("callable kinds are different (function vs property)")
object ParameterShape : StrongIncompatible<Nothing>("parameter shapes are different (extension vs non-extension)")
object ParameterCount : StrongIncompatible<Nothing>("number of value parameters is different")
// FunctionTypeParameterCount is strong because functions can be overloaded by type parameter count
object FunctionTypeParameterCount : StrongIncompatible<Nothing>("number of type parameters is different")
// ClassTypeParameterCount is weak because classes cannot be overloaded
object ClassTypeParameterCount : WeakIncompatible<Nothing>(FunctionTypeParameterCount.reason)
object ParameterTypes : StrongIncompatible<Nothing>("parameter types are different")
object ReturnType : StrongIncompatible<Nothing>("return type is different")
object ParameterNames : WeakIncompatible<Nothing>("parameter names are different")
object TypeParameterNames : WeakIncompatible<Nothing>("names of type parameters are different")
object ValueParameterVararg : WeakIncompatible<Nothing>("some value parameter is vararg in one declaration and non-vararg in the other")
object ValueParameterNoinline : WeakIncompatible<Nothing>(
"some value parameter is noinline in one declaration and not noinline in the other"
)
object ValueParameterCrossinline : WeakIncompatible<Nothing>(
"some value parameter is crossinline in one declaration and not crossinline in the other"
)
// Functions
object FunctionModifiersDifferent : WeakIncompatible<Nothing>("modifiers are different (suspend)")
object FunctionModifiersNotSubset : WeakIncompatible<Nothing>(
"some modifiers on expected declaration are missing on the actual one (infix, inline, operator)"
)
object ActualFunctionWithDefaultParameters :
WeakIncompatible<Nothing>("actual function cannot have default argument values, they should be declared in the expected function")
// Properties
object PropertyKind : WeakIncompatible<Nothing>("property kinds are different (val vs var)")
object PropertyLateinitModifier : WeakIncompatible<Nothing>("modifiers are different (lateinit)")
object PropertyConstModifier : WeakIncompatible<Nothing>("modifiers are different (const)")
object PropertySetterVisibility : WeakIncompatible<Nothing>("setter visibility is different")
// Classifiers
object ClassKind : WeakIncompatible<Nothing>("class kinds are different (class, interface, object, enum, annotation)")
object ClassModifiers : WeakIncompatible<Nothing>("modifiers are different (companion, inner, inline, value)")
object FunInterfaceModifier : WeakIncompatible<Nothing>("actual declaration for fun expect interface is not a functional interface")
object Supertypes : WeakIncompatible<Nothing>("some supertypes are missing in the actual declaration")
class ClassScopes<D>(
val unfulfilled: List<Pair<D, Map<Incompatible<D>, Collection<D>>>>
) : WeakIncompatible<D>("some expected members have no actual ones")
object EnumEntries : WeakIncompatible<Nothing>("some entries from expected enum are missing in the actual enum")
// Common
object Modality : WeakIncompatible<Nothing>("modality is different")
object Visibility : WeakIncompatible<Nothing>("visibility is different")
// FunctionTypeParameterUpperBounds is weak because functions can be overloaded by type parameter upper bounds
object FunctionTypeParameterUpperBounds : StrongIncompatible<Nothing>("upper bounds of type parameters are different")
// ClassTypeParameterUpperBounds is strong because classes cannot be overloaded
object ClassTypeParameterUpperBounds : WeakIncompatible<Nothing>(FunctionTypeParameterUpperBounds.reason)
object TypeParameterVariance : WeakIncompatible<Nothing>("declaration-site variances of type parameters are different")
object TypeParameterReified : WeakIncompatible<Nothing>(
"some type parameter is reified in one declaration and non-reified in the other"
)
}
object Compatible : ExpectActualCompatibility<Nothing>()
}
val ExpectActualCompatibility<*>.isCompatibleOrWeaklyIncompatible: Boolean
get() = this is ExpectActualCompatibility.Compatible
|| this is ExpectActualCompatibility.Incompatible.WeakIncompatible
val ExpectActualCompatibility<*>.compatible: Boolean
get() = this == ExpectActualCompatibility.Compatible
@OptIn(ExperimentalContracts::class)
fun ExpectActualCompatibility<*>.isIncompatible(): Boolean {
contract {
returns(true) implies (this@isIncompatible is ExpectActualCompatibility.Incompatible<*>)
}
return !compatible
}
@@ -0,0 +1,219 @@
/*
* 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.descriptors.Modality
import org.jetbrains.kotlin.descriptors.Visibility
import org.jetbrains.kotlin.mpp.*
import org.jetbrains.kotlin.name.CallableId
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility
import org.jetbrains.kotlin.types.Variance
import org.jetbrains.kotlin.types.model.KotlinTypeMarker
import org.jetbrains.kotlin.types.model.TypeSubstitutorMarker
import org.jetbrains.kotlin.types.model.TypeSystemContext
interface ExpectActualMatchingContext<T : DeclarationSymbolMarker> : TypeSystemContext {
val shouldCheckReturnTypesOfCallables: Boolean
/*
* This flag indicates how are type parameters of inner classes stored in the specific implementation of RegularClassSymbolMarker
*
* class Outer<T> {
* inner class Inner<U>
* }
*
* If flag is set to `true` then `typeParameters` for class `Outer.Inner` contains both parameters: [U, R]
* Otherwise it contains only parameters of itself: [U]
*
* This flag is needed for proper calculation of substitutions for components of inner classes
*/
val innerClassesCapturesOuterTypeParameters: Boolean
get() = true
val enumConstructorsAreAlwaysCompatible: Boolean
get() = false
// Try to drop it once KT-61105 is fixed
val shouldCheckAbsenceOfDefaultParamsInActual: Boolean
/**
* This flag determines, how visibilities for classes/typealiases will be matched
* - `false` means that visibilities should be identical
* - `true` means that visibility of actual class should be the same or wider comparing to expect visibility
* this means that following actualizations will be additionally allowed:
* - protected -> public
* - internal -> public
*/
val allowClassActualizationWithWiderVisibility: Boolean
get() = false
/**
* This flag determines strategy for matching supertypes between expect and actual class
* - `false` means that expect and actual supertypes are matched one by one
* - `true` means that type of actual class should be subtype of each expect supertype of the expect class
*/
val allowTransitiveSupertypesActualization: Boolean
get() = false
val RegularClassSymbolMarker.classId: ClassId
val TypeAliasSymbolMarker.classId: ClassId
val CallableSymbolMarker.callableId: CallableId
val TypeParameterSymbolMarker.parameterName: Name
val ValueParameterSymbolMarker.parameterName: Name
fun TypeAliasSymbolMarker.expandToRegularClass(): RegularClassSymbolMarker?
val RegularClassSymbolMarker.classKind: ClassKind
val RegularClassSymbolMarker.isCompanion: Boolean
val RegularClassSymbolMarker.isInner: Boolean
val RegularClassSymbolMarker.isInline: Boolean
val RegularClassSymbolMarker.isValue: Boolean
val RegularClassSymbolMarker.isFun: Boolean
val ClassLikeSymbolMarker.typeParameters: List<TypeParameterSymbolMarker>
val ClassLikeSymbolMarker.modality: Modality?
val ClassLikeSymbolMarker.visibility: Visibility
val CallableSymbolMarker.modality: Modality?
val CallableSymbolMarker.visibility: Visibility
val RegularClassSymbolMarker.superTypes: List<KotlinTypeMarker>
val RegularClassSymbolMarker.defaultType: KotlinTypeMarker
val CallableSymbolMarker.isExpect: Boolean
val CallableSymbolMarker.isInline: Boolean
val CallableSymbolMarker.isSuspend: Boolean
val CallableSymbolMarker.isExternal: Boolean
val CallableSymbolMarker.isInfix: Boolean
val CallableSymbolMarker.isOperator: Boolean
val CallableSymbolMarker.isTailrec: Boolean
val PropertySymbolMarker.isVar: Boolean
val PropertySymbolMarker.isLateinit: Boolean
val PropertySymbolMarker.isConst: Boolean
val PropertySymbolMarker.getter: FunctionSymbolMarker?
val PropertySymbolMarker.setter: FunctionSymbolMarker?
fun createExpectActualTypeParameterSubstitutor(
expectTypeParameters: List<TypeParameterSymbolMarker>,
actualTypeParameters: List<TypeParameterSymbolMarker>,
parentSubstitutor: TypeSubstitutorMarker?
): TypeSubstitutorMarker
fun RegularClassSymbolMarker.collectAllMembers(isActualDeclaration: Boolean): List<DeclarationSymbolMarker>
fun RegularClassSymbolMarker.getMembersForExpectClass(name: Name): List<DeclarationSymbolMarker>
fun RegularClassSymbolMarker.collectEnumEntryNames(): List<Name>
fun RegularClassSymbolMarker.collectEnumEntries(): List<DeclarationSymbolMarker>
val CallableSymbolMarker.dispatchReceiverType: KotlinTypeMarker?
val CallableSymbolMarker.extensionReceiverType: KotlinTypeMarker?
val CallableSymbolMarker.returnType: KotlinTypeMarker
val CallableSymbolMarker.typeParameters: List<TypeParameterSymbolMarker>
val FunctionSymbolMarker.valueParameters: List<ValueParameterSymbolMarker>
/**
* Returns all symbols that are overridden by [this] symbol
*/
fun FunctionSymbolMarker.allOverriddenDeclarationsRecursive(): Sequence<CallableSymbolMarker>
val CallableSymbolMarker.valueParameters: List<ValueParameterSymbolMarker>
get() = (this as? FunctionSymbolMarker)?.valueParameters ?: emptyList()
val ValueParameterSymbolMarker.isVararg: Boolean
val ValueParameterSymbolMarker.isNoinline: Boolean
val ValueParameterSymbolMarker.isCrossinline: Boolean
val ValueParameterSymbolMarker.hasDefaultValue: Boolean
fun CallableSymbolMarker.isAnnotationConstructor(): Boolean
val TypeParameterSymbolMarker.bounds: List<KotlinTypeMarker>
val TypeParameterSymbolMarker.variance: Variance
val TypeParameterSymbolMarker.isReified: Boolean
fun areCompatibleExpectActualTypes(
expectType: KotlinTypeMarker?,
actualType: KotlinTypeMarker?,
): Boolean
fun actualTypeIsSubtypeOfExpectType(
expectType: KotlinTypeMarker,
actualType: KotlinTypeMarker
): Boolean
fun RegularClassSymbolMarker.isNotSamInterface(): Boolean
/*
* Determines should some declaration from expect class scope be checked
* - FE 1.0: skip fake overrides
* - FIR: skip fake overrides
* - IR: skip nothing
*/
fun CallableSymbolMarker.shouldSkipMatching(containingExpectClass: RegularClassSymbolMarker): Boolean
val CallableSymbolMarker.hasStableParameterNames: Boolean
fun onMatchedMembers(
expectSymbol: DeclarationSymbolMarker,
actualSymbol: DeclarationSymbolMarker,
containingExpectClassSymbol: RegularClassSymbolMarker?,
containingActualClassSymbol: RegularClassSymbolMarker?,
) {}
fun onMismatchedMembersFromClassScope(
expectSymbol: DeclarationSymbolMarker,
actualSymbolsByIncompatibility: Map<ExpectActualCompatibility.Incompatible<*>, List<DeclarationSymbolMarker>>,
containingExpectClassSymbol: RegularClassSymbolMarker?,
containingActualClassSymbol: RegularClassSymbolMarker?,
) {}
val DeclarationSymbolMarker.annotations: List<AnnotationCallInfo>
fun areAnnotationArgumentsEqual(
expectAnnotation: AnnotationCallInfo,
actualAnnotation: AnnotationCallInfo,
collectionArgumentsCompatibilityCheckStrategy: ExpectActualCollectionArgumentsCompatibilityCheckStrategy,
): Boolean
val DeclarationSymbolMarker.hasSourceAnnotationsErased: Boolean
interface AnnotationCallInfo {
val annotationSymbol: Any
val classId: ClassId?
val isRetentionSource: Boolean
val isOptIn: Boolean
}
val checkClassScopesForAnnotationCompatibility: Boolean
/**
* Determines whether it is needed to skip checking annotations on class member in [AbstractExpectActualAnnotationMatchChecker].
*
* This is needed to prevent checking member twice if it is real `actual` member (not fake override or member of
* class being typealiased).
* Example:
* ```
* actual class A {
* actual fun foo() {} // 1: checked itself, 2: checked as member of A
* }
* ```
*/
fun skipCheckingAnnotationsOfActualClassMember(actualMember: DeclarationSymbolMarker): Boolean
fun findPotentialExpectClassMembersForActual(
expectClass: RegularClassSymbolMarker,
actualClass: RegularClassSymbolMarker,
actualMember: DeclarationSymbolMarker,
checkClassScopesCompatibility: Boolean,
): Map<out DeclarationSymbolMarker, ExpectActualCompatibility<*>>
fun DeclarationSymbolMarker.getSourceElement(): SourceElementMarker
}
@@ -0,0 +1,87 @@
/*
* 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.multiplatform
// This class will be later used by K2. That's why it's placed in compiler.common module
data class ExpectActualMemberDiff<out M, out C>(val kind: Kind, val actualMember: M, val expectClass: C) {
/**
* Diff kinds that are legal for fake-overrides in final `expect class`, but illegal for non-final `expect class`
*
* Also see: [toMemberDiffKind]
*/
enum class Kind(val rawMessage: String) {
NonPrivateCallableAdded(
"{0}: non-private member must be declared in both the actual class and the expect class. " +
"This error happens because the expect class ''{1}'' is non-final"
),
ReturnTypeChangedInOverride(
"{0}: the return type of this member must be the same in the expect class and the actual class. " +
"This error happens because the expect class ''{1}'' is non-final"
),
ModalityChangedInOverride(
"{0}: the modality of this member must be the same in the expect class and the actual class. " +
"This error happens because the expect class ''{1}'' is non-final"
),
VisibilityChangedInOverride(
"{0}: the visibility of this member must be the same in the expect class and the actual class. " +
"This error happens because the expect class ''{1}'' is non-final"
),
SetterVisibilityChangedInOverride(
"{0}: the setter visibility of this member must be the same in the expect class and the actual class. " +
"This error happens because the expect class ''{1}'' is non-final"
),
ParameterNameChangedInOverride(
"{0}: the parameter names of this member must be the same in the expect class and the actual class. " +
"This error happens because the expect class ''{1}'' is non-final"
),
PropertyKindChangedInOverride(
"{0}: the property kind (val vs var) of this member must be the same in the expect class and the actual class. " +
"This error happens because the expect class ''{1}'' is non-final"
),
LateinitChangedInOverride(
"{0}: the property modifiers (lateinit) of this member must be the same in the expect class and the actual class. " +
"This error happens because the expect class ''{1}'' is non-final"
),
TypeParameterNamesChangedInOverride(
"{0}: the type parameter names of this member must be the same in the expect class and the actual class. " +
"This error happens because the expect class ''{1}'' is non-final"
),
}
}
fun ExpectActualCompatibility.Incompatible<*>.toMemberDiffKind(): ExpectActualMemberDiff.Kind? = when (this) {
ExpectActualCompatibility.Incompatible.CallableKind -> ExpectActualMemberDiff.Kind.NonPrivateCallableAdded
ExpectActualCompatibility.Incompatible.ParameterCount -> ExpectActualMemberDiff.Kind.NonPrivateCallableAdded
ExpectActualCompatibility.Incompatible.ParameterShape -> ExpectActualMemberDiff.Kind.NonPrivateCallableAdded
ExpectActualCompatibility.Incompatible.ParameterTypes -> ExpectActualMemberDiff.Kind.NonPrivateCallableAdded
ExpectActualCompatibility.Incompatible.ReturnType -> ExpectActualMemberDiff.Kind.ReturnTypeChangedInOverride
ExpectActualCompatibility.Incompatible.FunctionTypeParameterCount -> ExpectActualMemberDiff.Kind.NonPrivateCallableAdded
ExpectActualCompatibility.Incompatible.ClassTypeParameterCount -> error("Not applicable because ExpectActualMemberDiff is about members")
ExpectActualCompatibility.Incompatible.FunctionTypeParameterUpperBounds -> ExpectActualMemberDiff.Kind.NonPrivateCallableAdded
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.NonPrivateCallableAdded
ExpectActualCompatibility.Incompatible.Visibility -> ExpectActualMemberDiff.Kind.VisibilityChangedInOverride
}