[IR] Use common expect/actual matching algorithm in IR actualizer
^KT-58578 Fixed
This commit is contained in:
committed by
Space Team
parent
62534f43c9
commit
ba41e8ec38
+80
-28
@@ -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")
|
||||
}
|
||||
|
||||
|
||||
+24
-1
@@ -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>>
|
||||
) {}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user