[IR] Use common expect/actual matching algorithm in IR actualizer

^KT-58578 Fixed
This commit is contained in:
Dmitriy Novozhilov
2023-06-06 17:27:03 +03:00
committed by Space Team
parent 62534f43c9
commit ba41e8ec38
52 changed files with 1130 additions and 440 deletions
@@ -29,7 +29,7 @@ object AbstractExpectActualCompatibilityChecker {
context: ExpectActualMatchingContext<T>,
): ExpectActualCompatibility<T> {
val result = with(context) {
areCompatibleClassifiers(expectClassSymbol, actualClassLikeSymbol)
areCompatibleClassifiers(expectClassSymbol, actualClassLikeSymbol, parentSubstitutor = null)
}
@Suppress("UNCHECKED_CAST")
return result as ExpectActualCompatibility<T>
@@ -50,11 +50,29 @@ object AbstractExpectActualCompatibilityChecker {
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
)
}
}
context(ExpectActualMatchingContext<*>)
@Suppress("warnings")
private fun areCompatibleClassifiers(
expectClassSymbol: RegularClassSymbolMarker,
actualClassLikeSymbol: ClassLikeSymbolMarker,
parentSubstitutor: TypeSubstitutorMarker?
): ExpectActualCompatibility<*> {
// 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) {
@@ -95,7 +113,7 @@ object AbstractExpectActualCompatibilityChecker {
val substitutor = createExpectActualTypeParameterSubstitutor(
expectTypeParameterSymbols,
actualTypeParameterSymbols,
parentSubstitutor = null
parentSubstitutor
)
areCompatibleTypeParameters(expectTypeParameterSymbols, actualTypeParameterSymbols, substitutor).let {
@@ -133,7 +151,7 @@ object AbstractExpectActualCompatibilityChecker {
actualClassSymbol: RegularClassSymbolMarker,
substitutor: TypeSubstitutorMarker,
): ExpectActualCompatibility<*> {
val unfulfilled = arrayListOf<Pair<Any?, Map<Incompatible<Any?>, MutableCollection<Any?>>>>()
val unfulfilled = arrayListOf<Pair<DeclarationSymbolMarker, Map<Incompatible<*>, List<DeclarationSymbolMarker?>>>>()
val actualMembersByName = actualClassSymbol.collectAllMembers(isActualDeclaration = true).groupBy { it.name }
@@ -145,31 +163,14 @@ object AbstractExpectActualCompatibilityChecker {
expectMember is RegularClassSymbolMarker && actualMember is RegularClassSymbolMarker
}.orEmpty()
val mapping = actualMembers.keysToMap { actualMember ->
when (expectMember) {
is CallableSymbolMarker -> areCompatibleCallables(
expectMember,
actualMember as CallableSymbolMarker,
substitutor,
expectClassSymbol,
actualClassSymbol
)
is RegularClassSymbolMarker -> areCompatibleClassifiers(expectMember, actualMember as RegularClassSymbolMarker)
else -> error("Unsupported declaration: $expectMember ($actualMembers)")
}
}
if (mapping.values.any { it == ExpectActualCompatibility.Compatible }) continue
val incompatibilityMap = mutableMapOf<Incompatible<Any?>, MutableCollection<Any?>>()
for ((declaration, compatibility) in mapping) {
when (compatibility) {
ExpectActualCompatibility.Compatible -> continue@outer
is Incompatible -> incompatibilityMap.getOrPut(compatibility) { SmartList() }.add(declaration)
}
}
unfulfilled.add(expectMember to incompatibilityMap)
matchSingleExpectAgainstPotentialActuals(
expectMember,
actualMembers,
substitutor,
expectClassSymbol,
actualClassSymbol,
unfulfilled
)
}
if (expectClassSymbol.classKind == ClassKind.ENUM_CLASS) {
@@ -186,6 +187,53 @@ object AbstractExpectActualCompatibilityChecker {
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?>>>>?
) {
val mapping = actualMembers.keysToMap { actualMember ->
when (expectMember) {
is CallableSymbolMarker -> areCompatibleCallables(
expectMember,
actualMember as CallableSymbolMarker,
substitutor,
expectClassSymbol,
actualClassSymbol
)
is RegularClassSymbolMarker -> {
val parentSubstitutor = substitutor?.takeIf { !innerClassesCapturesOuterTypeParameters }
areCompatibleClassifiers(
expectMember,
actualMember as ClassLikeSymbolMarker,
parentSubstitutor
)
}
else -> error("Unsupported declaration: $expectMember ($actualMembers)")
}
}
val incompatibilityMap = mutableMapOf<Incompatible<*>, MutableList<DeclarationSymbolMarker>>()
for ((actualMember, compatibility) in mapping) {
when (compatibility) {
ExpectActualCompatibility.Compatible -> {
onMatchedMembers(expectMember, actualMember)
return
}
is Incompatible -> incompatibilityMap.getOrPut(compatibility) { SmartList() }.add(actualMember)
}
}
unfulfilled?.add(expectMember to incompatibilityMap)
onMismatchedMembersFromClassScope(expectMember, incompatibilityMap)
}
context(ExpectActualMatchingContext<*>)
private fun areCompatibleCallables(
expectDeclaration: CallableSymbolMarker,
@@ -307,6 +355,10 @@ object AbstractExpectActualCompatibilityChecker {
actualDeclaration
).let { if (it != ExpectActualCompatibility.Compatible) return it }
expectDeclaration is EnumEntrySymbolMarker && actualDeclaration is EnumEntrySymbolMarker -> {
// do nothing, entries are matched only by name
}
else -> error("Unsupported declarations: $expectDeclaration, $actualDeclaration")
}
@@ -12,6 +12,7 @@ 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
@@ -20,6 +21,21 @@ 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 RegularClassSymbolMarker.classId: ClassId
val TypeAliasSymbolMarker.classId: ClassId
val CallableSymbolMarker.callableId: CallableId
@@ -100,10 +116,17 @@ interface ExpectActualMatchingContext<T : DeclarationSymbolMarker> : TypeSystemC
/*
* Determines should some declaration from expect class scope be checked
* - FE 1.0: skip fake overrides
* - FIR: skip nothing
* - FIR: skip fake overrides
* - IR: skip nothing
*/
fun CallableSymbolMarker.shouldSkipMatching(containingExpectClass: RegularClassSymbolMarker): Boolean
val CallableSymbolMarker.hasStableParameterNames: Boolean
fun onMatchedMembers(expectSymbol: DeclarationSymbolMarker, actualSymbol: DeclarationSymbolMarker) {}
fun onMismatchedMembersFromClassScope(
expectSymbol: DeclarationSymbolMarker,
actualSymbolsByIncompatibility: Map<ExpectActualCompatibility.Incompatible<*>, List<DeclarationSymbolMarker>>
) {}
}