[FIR, IR] Cleanup: deduplicate common code in AbstractExpectActualMatcher & AbstractExpectActualChecker

Review: https://jetbrains.team/p/kt/reviews/12750/timeline
This commit is contained in:
Nikita Bobko
2023-10-26 01:01:39 +02:00
committed by teamcity
parent b331726c6e
commit fd39795f41
3 changed files with 115 additions and 140 deletions
@@ -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<KotlinTypeMarker?>,
actualTypes: List<KotlinTypeMarker?>,
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<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, insideAnnotationClass = false)
) {
return false
}
}
return true
}
context(ExpectActualMatchingContext<*>)
private fun getTypeParametersVarianceOrReifiedIncompatibility(
expectTypeParameterSymbols: List<TypeParameterSymbolMarker>,
@@ -645,18 +590,6 @@ object AbstractExpectActualChecker {
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()
@@ -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<KotlinTypeMarker?>,
actualTypes: List<KotlinTypeMarker?>,
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<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, 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<ValueParameterSymbolMarker>.toTypeList(substitutor: TypeSubstitutorMarker): List<KotlinTypeMarker> {
return this.map { substitutor.safeSubstitute(it.returnType) }
@@ -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<Name, List<DeclarationSymbolMarker>>,
): List<DeclarationSymbolMarker> {
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<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,
insideAnnotationClass = false
)
) {
return false
}
}
return true
}
context(ExpectActualMatchingContext<*>)
internal fun areCompatibleTypeLists(
expectedTypes: List<KotlinTypeMarker?>,
actualTypes: List<KotlinTypeMarker?>,
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"
}
}