diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/ConeTypeCompatibilityChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/ConeTypeCompatibilityChecker.kt index a248e43aaf3..ddd59aafc5d 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/ConeTypeCompatibilityChecker.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/ConeTypeCompatibilityChecker.kt @@ -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.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 = when (type) { + is ConeIntersectionType -> type.intersectedTypes + else -> listOf(type) + } + + private fun Collection.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 diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirEqualityCompatibilityChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirEqualityCompatibilityChecker.kt index 4d53bf6d24e..54bcef7d384 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirEqualityCompatibilityChecker.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirEqualityCompatibilityChecker.kt @@ -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().firstOrNull()?.name}", e diff --git a/compiler/testData/diagnostics/tests/enum/enumSubjectTypeCheck.fir.kt b/compiler/testData/diagnostics/tests/enum/enumSubjectTypeCheck.fir.kt index 750c3bdf0e6..de45682c62d 100644 --- a/compiler/testData/diagnostics/tests/enum/enumSubjectTypeCheck.fir.kt +++ b/compiler/testData/diagnostics/tests/enum/enumSubjectTypeCheck.fir.kt @@ -32,8 +32,8 @@ fun useEn2(x: En2) = x fun bar(x: Any) { if (x is En && x is En2) { when (x) { - En.A -> useEn(x) - En2.D -> useEn2(x) + En.A -> useEn(x) + En2.D -> useEn2(x) else -> {} } }