org.jetbrains.kotlin.resolve.calls.mpp.AbstractExpectActualMatcher.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of kotlin-compiler-embeddable Show documentation
Show all versions of kotlin-compiler-embeddable Show documentation
the Kotlin compiler embeddable
/*
* 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.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.keysToMap
import org.jetbrains.kotlin.utils.zipIfSizesAreEqual
/**
* This object is responsible for matching of expect-actual pairs.
*
* - If you want to report the diagnostics then the declarations needs to be checked after they are matched ([AbstractExpectActualChecker]
* is responsible for the checking)
* - In all other cases you only need the "matching" data
*
* See `/docs/fir/k2_kmp.md` for details
*/
object AbstractExpectActualMatcher {
fun getCallablesMatchingCompatibility(
expectDeclaration: CallableSymbolMarker,
actualDeclaration: CallableSymbolMarker,
expectContainingClass: RegularClassSymbolMarker?,
actualContainingClass: RegularClassSymbolMarker?,
context: ExpectActualMatchingContext<*>,
): ExpectActualMatchingCompatibility = with(context) {
val expectTypeParameters = expectContainingClass?.typeParameters.orEmpty()
val actualTypeParameters = actualContainingClass?.typeParameters.orEmpty()
val parentSubstitutor = (expectTypeParameters zipIfSizesAreEqual actualTypeParameters)
?.let { createExpectActualTypeParameterSubstitutor(it, parentSubstitutor = null) }
getCallablesCompatibility(
expectDeclaration,
actualDeclaration,
parentSubstitutor,
expectContainingClass,
actualContainingClass
)
}
fun matchSingleExpectTopLevelDeclarationAgainstPotentialActuals(
expectDeclaration: DeclarationSymbolMarker,
actualDeclarations: List,
context: ExpectActualMatchingContext,
): DeclarationSymbolMarker? = with(context) {
matchSingleExpectAgainstPotentialActuals(
expectDeclaration,
actualDeclarations,
substitutor = null,
expectClassSymbol = null,
actualClassSymbol = null,
mismatchedMembers = null,
)
}
fun matchClassifiers(
expectClassSymbol: RegularClassSymbolMarker,
actualClassLikeSymbol: ClassLikeSymbolMarker,
context: ExpectActualMatchingContext<*>,
): ExpectActualMatchingCompatibility = with(context) {
// Can't check FQ names here because nested expected class may be implemented via actual typealias's expansion with the other FQ name
check(nameOf(expectClassSymbol) == nameOf(actualClassLikeSymbol)) {
"This function should be invoked only for declarations with the same name: $expectClassSymbol, $actualClassLikeSymbol"
}
check(actualClassLikeSymbol is RegularClassSymbolMarker || actualClassLikeSymbol is TypeAliasSymbolMarker) {
"Incorrect actual classifier for $expectClassSymbol: $actualClassLikeSymbol"
}
ExpectActualMatchingCompatibility.MatchedSuccessfully
}
/**
* Besides returning the matched declaration
*
* The function has an additional side effects:
* - It adds mismatched members to `mismatchedMembers`
* - It calls `onMatchedMembers` and `onMismatchedMembersFromClassScope` callbacks
*/
internal fun ExpectActualMatchingContext<*>.matchSingleExpectAgainstPotentialActuals(
expectMember: DeclarationSymbolMarker,
actualMembers: List,
substitutor: TypeSubstitutorMarker?,
expectClassSymbol: RegularClassSymbolMarker?,
actualClassSymbol: RegularClassSymbolMarker?,
mismatchedMembers: MutableList>>>?,
): DeclarationSymbolMarker? {
val mapping = actualMembers.keysToMap { actualMember ->
when (expectMember) {
is CallableSymbolMarker -> getCallablesCompatibility(
expectMember,
actualMember as CallableSymbolMarker,
substitutor,
expectClassSymbol,
actualClassSymbol
)
is RegularClassSymbolMarker -> {
matchClassifiers(expectMember, actualMember as ClassLikeSymbolMarker, this)
}
else -> error("Unsupported declaration: $expectMember ($actualMembers)")
}
}
val matched = ArrayList()
val mismatched = HashMap>()
for ((actualMember, compatibility) in mapping) {
when (compatibility) {
ExpectActualMatchingCompatibility.MatchedSuccessfully -> {
onMatchedMembers(expectMember, actualMember, expectClassSymbol, actualClassSymbol)
matched.add(actualMember)
}
is ExpectActualMatchingCompatibility.Mismatch -> mismatched.getOrPut(compatibility) { SmartList() }.add(actualMember)
}
}
matched.singleOrNull()?.let { return it }
mismatchedMembers?.add(expectMember to mismatched)
onMismatchedMembersFromClassScope(expectMember, mismatched, expectClassSymbol, actualClassSymbol)
return null
}
private fun ExpectActualMatchingContext<*>.getCallablesCompatibility(
expectDeclaration: CallableSymbolMarker,
actualDeclaration: CallableSymbolMarker,
parentSubstitutor: TypeSubstitutorMarker?,
expectContainingClass: RegularClassSymbolMarker?,
actualContainingClass: RegularClassSymbolMarker?,
): ExpectActualMatchingCompatibility {
checkCallablesInvariants(expectDeclaration, actualDeclaration)
if (areEnumConstructors(expectDeclaration, actualDeclaration, expectContainingClass, actualContainingClass)) {
return ExpectActualMatchingCompatibility.MatchedSuccessfully
}
val insideAnnotationClass = expectContainingClass?.classKind == ClassKind.ANNOTATION_CLASS
if (expectDeclaration is FunctionSymbolMarker != actualDeclaration is FunctionSymbolMarker) {
return ExpectActualMatchingCompatibility.CallableKind
}
if (actualDeclaration.isJavaField) {
return ExpectActualMatchingCompatibility.ActualJavaField
}
val expectedReceiverType = expectDeclaration.extensionReceiverType
val actualReceiverType = actualDeclaration.extensionReceiverType
if ((expectedReceiverType != null) != (actualReceiverType != null)) {
return ExpectActualMatchingCompatibility.ParameterShape
}
val expectedValueParameters = expectDeclaration.valueParameters
val actualValueParameters = actualDeclaration.valueParameters
if (!valueParametersCountCompatible(expectDeclaration, actualDeclaration, expectedValueParameters, actualValueParameters)) {
return ExpectActualMatchingCompatibility.ParameterCount
}
val expectedTypeParameters = expectDeclaration.typeParameters
val actualTypeParameters = actualDeclaration.typeParameters
if (expectedTypeParameters.size != actualTypeParameters.size) {
return ExpectActualMatchingCompatibility.FunctionTypeParameterCount
}
val substitutor = createExpectActualTypeParameterSubstitutor(
(expectedTypeParameters zipIfSizesAreEqual actualTypeParameters)
?: error("expect/actual type parameters sizes are checked earlier"),
parentSubstitutor
)
if (
!areCompatibleTypeLists(
toTypeList(expectedValueParameters, substitutor),
toTypeList(actualValueParameters, createEmptySubstitutor()),
insideAnnotationClass
) || !areCompatibleExpectActualTypes(
expectedReceiverType?.let { substitutor.safeSubstitute(it) },
actualReceiverType,
parameterOfAnnotationComparisonMode = false
)
) {
return ExpectActualMatchingCompatibility.ParameterTypes
}
if (!areCompatibleTypeParameterUpperBounds(expectedTypeParameters, actualTypeParameters, substitutor)) {
return ExpectActualMatchingCompatibility.FunctionTypeParameterUpperBounds
}
return ExpectActualMatchingCompatibility.MatchedSuccessfully
}
private fun ExpectActualMatchingContext<*>.valueParametersCountCompatible(
expectDeclaration: CallableSymbolMarker,
actualDeclaration: CallableSymbolMarker,
expectValueParameters: List,
actualValueParameters: List,
): Boolean {
if (expectValueParameters.size == actualValueParameters.size) return true
return if (expectDeclaration.isAnnotationConstructor() && actualDeclaration.isAnnotationConstructor()) {
expectValueParameters.isEmpty() && actualValueParameters.all { it.hasDefaultValue }
} else {
false
}
}
// ---------------------------------------- Utils ----------------------------------------
private fun ExpectActualMatchingContext<*>.toTypeList(
parameterSymbolMarkers: List,
substitutor: TypeSubstitutorMarker,
): List {
return parameterSymbolMarkers.map { substitutor.safeSubstitute(it.returnType) }
}
}