[FIR] Remove enforcesEmptyIntersection

It's confusing + it's not really
correct. It checks properties of
LUB types rather than the original
ones, but if we want to replicate
K1, we should check the originals.
This commit is contained in:
Nikolay Lunyak
2023-10-20 17:58:39 +03:00
committed by Space Team
parent ed8935899c
commit cb4183bec6
@@ -57,7 +57,7 @@ object FirEqualityCompatibilityChecker : FirEqualityOperatorCallChecker() {
// In either case K1 may not report a diagnostic // In either case K1 may not report a diagnostic
// due to some reasons, and we need to // due to some reasons, and we need to
// account for them. // account for them.
val isCaseMissedByK1 = isCaseMissedByK1Intersector(l.originalType, r.originalType, context.session) val isCaseMissedByK1 = isCaseMissedByK1Intersector(l.originalTypeInfo, r.originalTypeInfo)
&& isCaseMissedByAdditionalK1IncompatibleEnumsCheck(l.originalType, r.originalType, context.session) && isCaseMissedByAdditionalK1IncompatibleEnumsCheck(l.originalType, r.originalType, context.session)
val replicateK1Behavior = !context.languageVersionSettings.supportsFeature(LanguageFeature.ReportErrorsForComparisonOperators) val replicateK1Behavior = !context.languageVersionSettings.supportsFeature(LanguageFeature.ReportErrorsForComparisonOperators)
@@ -111,11 +111,12 @@ object FirEqualityCompatibilityChecker : FirEqualityOperatorCallChecker() {
} }
private fun getInapplicabilityFor(l: TypeInfo, r: TypeInfo): Applicability { private fun getInapplicabilityFor(l: TypeInfo, r: TypeInfo): Applicability {
val isIntersectionEmpty = l.enforcesEmptyIntersection || r.enforcesEmptyIntersection val isNonEmptyIntersectionInK1 = isCaseMissedByK1Intersector(l, r)
val isOneEnum = l.isEnumClass || r.isEnumClass val isOneEnum = l.isEnumClass || r.isEnumClass
return when { return when {
!isIntersectionEmpty && isOneEnum -> Applicability.INAPPLICABLE_AS_ENUMS // This code aims to replicate the K1's choice of diagnostics
isNonEmptyIntersectionInK1 && isOneEnum -> Applicability.INAPPLICABLE_AS_ENUMS
else -> Applicability.GENERALLY_INAPPLICABLE else -> Applicability.GENERALLY_INAPPLICABLE
} }
} }
@@ -134,6 +135,15 @@ object FirEqualityCompatibilityChecker : FirEqualityOperatorCallChecker() {
private fun TypeInfo.isSubtypeOf(other: TypeInfo, context: CheckerContext) = private fun TypeInfo.isSubtypeOf(other: TypeInfo, context: CheckerContext) =
notNullType.isSubtypeOf(other.notNullType, context.session) notNullType.isSubtypeOf(other.notNullType, context.session)
/**
* K1 reports different diagnostics for different
* cases, and this enum helps to replicate the K1's
* choice of diagnostics.
*
* Should the K2's diagnostic severity differ,
* the proper version will be picked later
* when reporting the diagnostic.
*/
private enum class Applicability { private enum class Applicability {
APPLICABLE, APPLICABLE,
GENERALLY_INAPPLICABLE, GENERALLY_INAPPLICABLE,
@@ -284,11 +294,11 @@ object FirEqualityCompatibilityChecker : FirEqualityOperatorCallChecker() {
private class TypeInfo( private class TypeInfo(
val type: ConeKotlinType, val type: ConeKotlinType,
val notNullType: ConeKotlinType, val notNullType: ConeKotlinType,
val isFinalClass: Boolean,
val isEnumClass: Boolean, val isEnumClass: Boolean,
val isPrimitive: Boolean, val isPrimitive: Boolean,
val isBuiltin: Boolean, val isBuiltin: Boolean,
val isValueClass: Boolean, val isValueClass: Boolean,
val canHaveSubtypesAccordingToK1: Boolean,
) { ) {
override fun toString() = "$type" override fun toString() = "$type"
} }
@@ -309,10 +319,6 @@ private fun ConeKotlinType.canHaveSubtypesAccordingToK1(session: FirSession): Bo
} }
} }
// This property is used to replicate K1 behavior, and it
// tries to predict empty intersections from the K1 point-of-view.
private val TypeInfo.enforcesEmptyIntersection get() = isFinalClass && !isEnumClass
private val TypeInfo.isNullableEnum get() = isEnumClass && type.isNullable private val TypeInfo.isNullableEnum get() = isEnumClass && type.isNullable
private val TypeInfo.isIdentityLess get() = isPrimitive || isValueClass private val TypeInfo.isIdentityLess get() = isPrimitive || isValueClass
@@ -336,11 +342,11 @@ private fun ConeKotlinType.toTypeInfo(session: FirSession): TypeInfo {
return TypeInfo( return TypeInfo(
type, notNullType, type, notNullType,
isFinalClass = bounds.any { it.toClassSymbol(session)?.isFinalClass == true },
isEnumClass = bounds.any { it.isEnum(session) }, isEnumClass = bounds.any { it.isEnum(session) },
isPrimitive = bounds.any { it.isPrimitiveOrNullablePrimitive }, isPrimitive = bounds.any { it.isPrimitiveOrNullablePrimitive },
isBuiltin = bounds.any { it.toClassSymbol(session)?.isBuiltin == true }, isBuiltin = bounds.any { it.toClassSymbol(session)?.isBuiltin == true },
isValueClass = bounds.any { it.toClassSymbol(session)?.isInline == true }, isValueClass = bounds.any { it.toClassSymbol(session)?.isInline == true },
canHaveSubtypesAccordingToK1(session),
) )
} }
@@ -378,8 +384,8 @@ private fun FirExpression.toArgumentInfo(context: CheckerContext) =
this, resolvedType, mostOriginalTypeIfSmartCast.fullyExpandedType(context.session), context.session, this, resolvedType, mostOriginalTypeIfSmartCast.fullyExpandedType(context.session), context.session,
) )
private fun isCaseMissedByK1Intersector(a: ConeKotlinType, b: ConeKotlinType, session: FirSession) = private fun isCaseMissedByK1Intersector(a: TypeInfo, b: TypeInfo) =
a.canHaveSubtypesAccordingToK1(session) && b.canHaveSubtypesAccordingToK1(session) a.canHaveSubtypesAccordingToK1 && b.canHaveSubtypesAccordingToK1
private fun isCaseMissedByAdditionalK1IncompatibleEnumsCheck(a: ConeKotlinType, b: ConeKotlinType, session: FirSession): Boolean { private fun isCaseMissedByAdditionalK1IncompatibleEnumsCheck(a: ConeKotlinType, b: ConeKotlinType, session: FirSession): Boolean {
return when { return when {