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:
Tianyu Geng
2021-07-26 10:07:29 -07:00
committed by teamcityserver
parent b5ea811b9f
commit e242ad955b
3 changed files with 24 additions and 10 deletions
@@ -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
@@ -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 -> {}
}
}