FIR: tolerate comparison of an intersection of incompatible types
Previously, if user compares an `it<String, Int>` with `String`, the checker reports it since the flattened types `[String, Int, Int]` are incompatible. But technically, before flattening, the intersection type actually contain the other side, so they should really be compatible.
This commit is contained in:
committed by
teamcityserver
parent
b5ea811b9f
commit
e242ad955b
+17
-2
@@ -7,7 +7,6 @@ package org.jetbrains.kotlin.fir.analysis.checkers
|
||||
|
||||
import org.jetbrains.kotlin.config.LanguageFeature
|
||||
import org.jetbrains.kotlin.descriptors.Modality
|
||||
import org.jetbrains.kotlin.fir.declarations.*
|
||||
import org.jetbrains.kotlin.fir.declarations.utils.*
|
||||
import org.jetbrains.kotlin.fir.isPrimitiveType
|
||||
import org.jetbrains.kotlin.fir.languageVersionSettings
|
||||
@@ -63,7 +62,23 @@ internal object ConeTypeCompatibilityChecker {
|
||||
HARD_INCOMPATIBLE,
|
||||
}
|
||||
|
||||
fun Collection<ConeKotlinType>.areCompatible(ctx: ConeInferenceContext): Compatibility {
|
||||
fun ConeInferenceContext.isCompatible(a: ConeKotlinType, b: ConeKotlinType): Compatibility {
|
||||
val aUnwrap = unwrap(a)
|
||||
val bUnwrap = unwrap(b)
|
||||
if (aUnwrap.containsAll(bUnwrap) || bUnwrap.containsAll(aUnwrap)) {
|
||||
return Compatibility.COMPATIBLE
|
||||
}
|
||||
|
||||
val intersectionType = intersectTypesOrNull(listOf(a, b)) as? ConeIntersectionType ?: return Compatibility.COMPATIBLE
|
||||
return intersectionType.intersectedTypes.areCompatible(this)
|
||||
}
|
||||
|
||||
private fun unwrap(type: ConeKotlinType): Collection<ConeKotlinType> = when (type) {
|
||||
is ConeIntersectionType -> type.intersectedTypes
|
||||
else -> listOf(type)
|
||||
}
|
||||
|
||||
private fun Collection<ConeKotlinType>.areCompatible(ctx: ConeInferenceContext): Compatibility {
|
||||
// If all types are nullable, then `null` makes the given types compatible.
|
||||
if (all { with(ctx) { it.isNullableType() } }) return Compatibility.COMPATIBLE
|
||||
|
||||
|
||||
+5
-6
@@ -7,18 +7,18 @@ package org.jetbrains.kotlin.fir.analysis.checkers.expression
|
||||
|
||||
import org.jetbrains.kotlin.fir.FirRealSourceElementKind
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.ConeTypeCompatibilityChecker
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.ConeTypeCompatibilityChecker.isCompatible
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.reportOn
|
||||
import org.jetbrains.kotlin.fir.declarations.FirFile
|
||||
import org.jetbrains.kotlin.fir.declarations.utils.isEnumClass
|
||||
import org.jetbrains.kotlin.fir.expressions.FirEqualityOperatorCall
|
||||
import org.jetbrains.kotlin.fir.render
|
||||
import org.jetbrains.kotlin.fir.resolve.inference.inferenceComponents
|
||||
import org.jetbrains.kotlin.fir.resolve.toFirRegularClass
|
||||
import org.jetbrains.kotlin.fir.types.*
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.ConeTypeCompatibilityChecker.areCompatible
|
||||
import org.jetbrains.kotlin.fir.declarations.FirFile
|
||||
import org.jetbrains.kotlin.fir.render
|
||||
|
||||
object FirEqualityCompatibilityChecker : FirEqualityOperatorCallChecker() {
|
||||
override fun check(expression: FirEqualityOperatorCall, context: CheckerContext, reporter: DiagnosticReporter) {
|
||||
@@ -30,13 +30,12 @@ object FirEqualityCompatibilityChecker : FirEqualityOperatorCallChecker() {
|
||||
// type `Nothing?`
|
||||
if (lType.isNullableNothing || rType.isNullableNothing) return
|
||||
val inferenceContext = context.session.inferenceComponents.ctx
|
||||
val intersectionType = inferenceContext.intersectTypesOrNull(listOf(lType, rType)) as? ConeIntersectionType ?: return
|
||||
|
||||
val compatibility = try {
|
||||
intersectionType.intersectedTypes.areCompatible(inferenceContext)
|
||||
inferenceContext.isCompatible(lType, rType)
|
||||
} catch (e: Throwable) {
|
||||
throw IllegalStateException(
|
||||
"Exception while determining type compatibility: type ${intersectionType.render()}, " +
|
||||
"Exception while determining type compatibility: lType: $lType, rType: $rType, " +
|
||||
"equality ${expression.render()}, " +
|
||||
"file ${context.containingDeclarations.filterIsInstance<FirFile>().firstOrNull()?.name}",
|
||||
e
|
||||
|
||||
@@ -32,8 +32,8 @@ fun useEn2(x: En2) = x
|
||||
fun bar(x: Any) {
|
||||
if (x is En && x is En2) {
|
||||
when (x) {
|
||||
<!INCOMPATIBLE_TYPES!>En.A<!> -> useEn(x)
|
||||
<!INCOMPATIBLE_TYPES!>En2.D<!> -> useEn2(x)
|
||||
En.A -> useEn(x)
|
||||
En2.D -> useEn2(x)
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user