[FIR] Check for subtyping during actualization of supertypes of expect class
^KT-59356 Fixed
This commit is contained in:
committed by
Space Team
parent
ac92c129bf
commit
1b24b95cde
+47
-11
@@ -122,17 +122,7 @@ object AbstractExpectActualCompatibilityChecker {
|
||||
}
|
||||
}
|
||||
|
||||
// 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 = actualClass.superTypes.filterNot { it.typeConstructor().isAnyConstructor() }
|
||||
if (
|
||||
expectSupertypes.map { substitutor.safeSubstitute(it) }.any { expectSupertype ->
|
||||
actualSupertypes.none { actualSupertype ->
|
||||
areCompatibleExpectActualTypes(expectSupertype, actualSupertype)
|
||||
}
|
||||
}
|
||||
) {
|
||||
if (!areCompatibleSupertypes(expectClassSymbol, actualClass, substitutor)) {
|
||||
return Incompatible.Supertypes
|
||||
}
|
||||
|
||||
@@ -145,6 +135,52 @@ object AbstractExpectActualCompatibilityChecker {
|
||||
return ExpectActualCompatibility.Compatible
|
||||
}
|
||||
|
||||
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 areCompatibleClassScopes(
|
||||
expectClassSymbol: RegularClassSymbolMarker,
|
||||
|
||||
+14
@@ -50,6 +50,14 @@ interface ExpectActualMatchingContext<T : DeclarationSymbolMarker> : TypeSystemC
|
||||
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
|
||||
@@ -74,6 +82,7 @@ interface ExpectActualMatchingContext<T : DeclarationSymbolMarker> : TypeSystemC
|
||||
val CallableSymbolMarker.visibility: Visibility
|
||||
|
||||
val RegularClassSymbolMarker.superTypes: List<KotlinTypeMarker>
|
||||
val RegularClassSymbolMarker.defaultType: KotlinTypeMarker
|
||||
|
||||
val CallableSymbolMarker.isExpect: Boolean
|
||||
val CallableSymbolMarker.isInline: Boolean
|
||||
@@ -125,6 +134,11 @@ interface ExpectActualMatchingContext<T : DeclarationSymbolMarker> : TypeSystemC
|
||||
actualType: KotlinTypeMarker?,
|
||||
): Boolean
|
||||
|
||||
fun actualTypeIsSubtypeOfExpectType(
|
||||
expectType: KotlinTypeMarker,
|
||||
actualType: KotlinTypeMarker
|
||||
): Boolean
|
||||
|
||||
fun RegularClassSymbolMarker.isNotSamInterface(): Boolean
|
||||
|
||||
/*
|
||||
|
||||
Reference in New Issue
Block a user