[FIR] Relax incompatibleEnumAndUnrelatedInterface
This commit is contained in:
committed by
Space Team
parent
8ec7d128f5
commit
ed8935899c
+39
-14
@@ -26,6 +26,7 @@ import org.jetbrains.kotlin.fir.expressions.FirSmartCastExpression
|
|||||||
import org.jetbrains.kotlin.fir.isPrimitiveType
|
import org.jetbrains.kotlin.fir.isPrimitiveType
|
||||||
import org.jetbrains.kotlin.fir.resolve.fullyExpandedType
|
import org.jetbrains.kotlin.fir.resolve.fullyExpandedType
|
||||||
import org.jetbrains.kotlin.fir.symbols.impl.FirClassSymbol
|
import org.jetbrains.kotlin.fir.symbols.impl.FirClassSymbol
|
||||||
|
import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol
|
||||||
import org.jetbrains.kotlin.fir.types.*
|
import org.jetbrains.kotlin.fir.types.*
|
||||||
import org.jetbrains.kotlin.fir.types.impl.ConeClassLikeTypeImpl
|
import org.jetbrains.kotlin.fir.types.impl.ConeClassLikeTypeImpl
|
||||||
import org.jetbrains.kotlin.name.StandardClassIds
|
import org.jetbrains.kotlin.name.StandardClassIds
|
||||||
@@ -49,12 +50,15 @@ object FirEqualityCompatibilityChecker : FirEqualityOperatorCallChecker() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
checkApplicability(l.originalTypeInfo, r.originalTypeInfo, context).ifInapplicable {
|
checkApplicability(l.originalTypeInfo, r.originalTypeInfo, context).ifInapplicable {
|
||||||
// Ideally this should match cases when K1
|
// K1 checks consist of 2 parts: reporting a
|
||||||
// sees a non-empty intersection and none of the
|
// diagnostic if the intersection is empty,
|
||||||
// types is an enum, but intersections in K1
|
// and otherwise reporting a diagnostic if
|
||||||
// work differently from intersections in K2.
|
// `isIncompatibleEnums` returns true.
|
||||||
val isCaseMissedByK1 = areBothTypeParameters(l.originalTypeInfo, r.originalTypeInfo)
|
// In either case K1 may not report a diagnostic
|
||||||
|| areTypeParameterAndEnumClass(l.originalTypeInfo, r.originalTypeInfo)
|
// due to some reasons, and we need to
|
||||||
|
// account for them.
|
||||||
|
val isCaseMissedByK1 = isCaseMissedByK1Intersector(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)
|
||||||
|
|
||||||
return reporter.reportInapplicabilityDiagnostic(
|
return reporter.reportInapplicabilityDiagnostic(
|
||||||
@@ -285,16 +289,28 @@ private class TypeInfo(
|
|||||||
val isPrimitive: Boolean,
|
val isPrimitive: Boolean,
|
||||||
val isBuiltin: Boolean,
|
val isBuiltin: Boolean,
|
||||||
val isValueClass: Boolean,
|
val isValueClass: Boolean,
|
||||||
val isLiterallyTypeParameter: Boolean,
|
|
||||||
) {
|
) {
|
||||||
override fun toString() = "$type"
|
override fun toString() = "$type"
|
||||||
}
|
}
|
||||||
|
|
||||||
private val FirClassSymbol<*>.isBuiltin get() = isPrimitiveType() || classId == StandardClassIds.String || isEnumClass
|
private val FirClassSymbol<*>.isBuiltin get() = isPrimitiveType() || classId == StandardClassIds.String || isEnumClass
|
||||||
|
|
||||||
|
// This property is used to replicate K1 behavior, and it
|
||||||
|
// tries to match the `TypeUtils.canHaveSubtypes(typeChecker, type)`
|
||||||
|
// check in the K1 intersector.
|
||||||
|
// In K2 enum classes are final, though enum entries are their subclasses.
|
||||||
|
private fun ConeKotlinType.canHaveSubtypesAccordingToK1(session: FirSession): Boolean {
|
||||||
|
val symbol = toSymbol(session)
|
||||||
|
|
||||||
|
return when {
|
||||||
|
symbol is FirRegularClassSymbol && symbol.isEnumClass -> true
|
||||||
|
symbol is FirClassSymbol<*> && symbol.isFinalClass -> false
|
||||||
|
else -> true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// This property is used to replicate K1 behavior, and it
|
// This property is used to replicate K1 behavior, and it
|
||||||
// tries to predict empty intersections from the K1 point-of-view.
|
// tries to predict empty intersections from the K1 point-of-view.
|
||||||
// Enum classes are final, but enum entries are their subclasses.
|
|
||||||
private val TypeInfo.enforcesEmptyIntersection get() = isFinalClass && !isEnumClass
|
private val TypeInfo.enforcesEmptyIntersection get() = isFinalClass && !isEnumClass
|
||||||
|
|
||||||
private val TypeInfo.isNullableEnum get() = isEnumClass && type.isNullable
|
private val TypeInfo.isNullableEnum get() = isEnumClass && type.isNullable
|
||||||
@@ -308,6 +324,10 @@ private val FirClassSymbol<*>.isFinalClass get() = isClass && isFinal
|
|||||||
// NB: This is what RULES1 means then it says "class".
|
// NB: This is what RULES1 means then it says "class".
|
||||||
private val FirClassSymbol<*>.isClass get() = !isInterface
|
private val FirClassSymbol<*>.isClass get() = !isInterface
|
||||||
|
|
||||||
|
private fun ConeKotlinType.isEnum(session: FirSession) = toRegularClassSymbol(session)?.isEnumClass == true
|
||||||
|
|
||||||
|
private fun ConeKotlinType.isClass(session: FirSession) = toRegularClassSymbol(session) != null
|
||||||
|
|
||||||
private fun ConeKotlinType.toTypeInfo(session: FirSession): TypeInfo {
|
private fun ConeKotlinType.toTypeInfo(session: FirSession): TypeInfo {
|
||||||
val bounds = collectUpperBounds().map { type -> toKotlinType(type).replaceArgumentsWithStarProjections() }
|
val bounds = collectUpperBounds().map { type -> toKotlinType(type).replaceArgumentsWithStarProjections() }
|
||||||
val type = bounds.ifNotEmpty { ConeTypeIntersector.intersectTypes(session.typeContext, this) }
|
val type = bounds.ifNotEmpty { ConeTypeIntersector.intersectTypes(session.typeContext, this) }
|
||||||
@@ -317,11 +337,10 @@ private fun ConeKotlinType.toTypeInfo(session: FirSession): TypeInfo {
|
|||||||
return TypeInfo(
|
return TypeInfo(
|
||||||
type, notNullType,
|
type, notNullType,
|
||||||
isFinalClass = bounds.any { it.toClassSymbol(session)?.isFinalClass == true },
|
isFinalClass = bounds.any { it.toClassSymbol(session)?.isFinalClass == true },
|
||||||
isEnumClass = bounds.any { it.toClassSymbol(session)?.isEnumClass == true },
|
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 },
|
||||||
isLiterallyTypeParameter = this.lowerBoundIfFlexible() is ConeTypeParameterType,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -359,8 +378,14 @@ private fun FirExpression.toArgumentInfo(context: CheckerContext) =
|
|||||||
this, resolvedType, mostOriginalTypeIfSmartCast.fullyExpandedType(context.session), context.session,
|
this, resolvedType, mostOriginalTypeIfSmartCast.fullyExpandedType(context.session), context.session,
|
||||||
)
|
)
|
||||||
|
|
||||||
private fun areBothTypeParameters(a: TypeInfo, b: TypeInfo) =
|
private fun isCaseMissedByK1Intersector(a: ConeKotlinType, b: ConeKotlinType, session: FirSession) =
|
||||||
a.isLiterallyTypeParameter && b.isLiterallyTypeParameter
|
a.canHaveSubtypesAccordingToK1(session) && b.canHaveSubtypesAccordingToK1(session)
|
||||||
|
|
||||||
private fun areTypeParameterAndEnumClass(a: TypeInfo, b: TypeInfo) =
|
private fun isCaseMissedByAdditionalK1IncompatibleEnumsCheck(a: ConeKotlinType, b: ConeKotlinType, session: FirSession): Boolean {
|
||||||
a.isLiterallyTypeParameter && b.isEnumClass || b.isLiterallyTypeParameter && a.isEnumClass
|
return when {
|
||||||
|
!a.isEnum(session) && !b.isEnum(session) -> true
|
||||||
|
a.isNullable && b.isNullable -> true
|
||||||
|
a.isNothingOrNullableNothing || b.isNothingOrNullableNothing -> true
|
||||||
|
else -> !a.isClass(session) || !b.isClass(session)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+1
-1
@@ -111,5 +111,5 @@ fun <I : A> incompatibleEnumAndUnrelatedInterfaceThroughTypeParameter(x: G?, i:
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun incompatibleEnumAndUnrelatedInterface(x: G?, i: A?) {
|
fun incompatibleEnumAndUnrelatedInterface(x: G?, i: A?) {
|
||||||
<!INCOMPATIBLE_ENUM_COMPARISON_ERROR!>x == i<!>
|
<!INCOMPATIBLE_ENUM_COMPARISON!>x == i<!>
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user