[FIR] Check for subtyping during actualization of supertypes of expect class

^KT-59356 Fixed
This commit is contained in:
Dmitriy Novozhilov
2023-06-16 17:35:01 +03:00
committed by Space Team
parent ac92c129bf
commit 1b24b95cde
26 changed files with 323 additions and 15 deletions
@@ -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,
@@ -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
/*