From 1fbccff1bdec4a5ab7d8199fcb310a2035bf02d6 Mon Sep 17 00:00:00 2001 From: Ivan Kochurkin Date: Thu, 16 Sep 2021 21:52:12 +0300 Subject: [PATCH] [FIR] Rename isCastPossible to checkCasting, return CastingType instead of boolean --- .../checkers/FirCastDiagnosticsHelpers.kt | 42 +++++++++++-------- .../smartCasts/castchecks/variables.fir.kt | 4 +- 2 files changed, 27 insertions(+), 19 deletions(-) diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/FirCastDiagnosticsHelpers.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/FirCastDiagnosticsHelpers.kt index f6d3ed7d2d7..51995f9bbd3 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/FirCastDiagnosticsHelpers.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/FirCastDiagnosticsHelpers.kt @@ -19,12 +19,18 @@ import org.jetbrains.kotlin.types.AbstractTypeChecker import org.jetbrains.kotlin.types.AbstractTypeChecker.findCorrespondingSupertypes import org.jetbrains.kotlin.types.model.typeConstructor -fun isCastPossible( +enum class CastingType { + Possible, + Impossible, + Always +} + +fun checkCasting( lhsType: ConeKotlinType, rhsType: ConeKotlinType, isSafeCase: Boolean, context: CheckerContext -): Boolean { +): CastingType { val lhsLowerType = lhsType.lowerBoundIfFlexible() val rhsLowerType = rhsType.lowerBoundIfFlexible() val session = context.session @@ -32,36 +38,38 @@ fun isCastPossible( if (lhsLowerType is ConeIntersectionType) { var result = false for (intersectedType in lhsLowerType.intersectedTypes) { - val isIntersectedCastPossible = isCastPossible(intersectedType, rhsLowerType, isSafeCase, context) + val isIntersectedCastPossible = checkCasting(intersectedType, rhsLowerType, isSafeCase, context) val intersectedTypeSymbol = intersectedType.toRegularClassSymbol(context.session) - if (intersectedTypeSymbol?.isInterface == false && !isIntersectedCastPossible) { - return false // Any class type in intersection type should be subtype of RHS + if (intersectedTypeSymbol?.isInterface == false && isIntersectedCastPossible == CastingType.Impossible) { + return CastingType.Impossible // Any class type in intersection type should be subtype of RHS } - result = result or isIntersectedCastPossible + result = result or (isIntersectedCastPossible != CastingType.Impossible) } - return result + return if (result) CastingType.Possible else CastingType.Impossible } val lhsNullable = lhsLowerType.canBeNull val rhsNullable = rhsLowerType.canBeNull - if (lhsLowerType.isNothing) return true + if (lhsLowerType.isNothing) return CastingType.Possible if (lhsLowerType.isNullableNothing && !rhsNullable) { - return isSafeCase + return if (isSafeCase) CastingType.Always else CastingType.Impossible } - if (rhsLowerType.isNothing) return false - if (rhsLowerType.isNullableNothing) return lhsNullable - if (lhsNullable && rhsNullable) return true + if (rhsLowerType.isNothing) return CastingType.Impossible + if (rhsLowerType.isNullableNothing) { + return if (lhsNullable) CastingType.Possible else CastingType.Impossible + } + if (lhsNullable && rhsNullable) return CastingType.Possible val lhsClassSymbol = lhsLowerType.toRegularClassSymbol(context.session) val rhsClassSymbol = rhsLowerType.toRegularClassSymbol(context.session) - if (isRelated(lhsLowerType, rhsLowerType, lhsClassSymbol, rhsClassSymbol, context)) return true + if (isRelated(lhsLowerType, rhsLowerType, lhsClassSymbol, rhsClassSymbol, context)) return CastingType.Possible // This is an oversimplification (which does not render the method incomplete): // we consider any type parameter capable of taking any value, which may be made more precise if we considered bounds - if (lhsLowerType is ConeTypeParameterType || rhsLowerType is ConeTypeParameterType) return true + if (lhsLowerType is ConeTypeParameterType || rhsLowerType is ConeTypeParameterType) return CastingType.Possible - if (isFinal(lhsLowerType, session) || isFinal(rhsLowerType, session)) return false - if (lhsClassSymbol?.isInterface == true || rhsClassSymbol?.isInterface == true) return true - return false + if (isFinal(lhsLowerType, session) || isFinal(rhsLowerType, session)) return CastingType.Impossible + if (lhsClassSymbol?.isInterface == true || rhsClassSymbol?.isInterface == true) return CastingType.Possible + return CastingType.Impossible } /** diff --git a/compiler/testData/diagnostics/tests/smartCasts/castchecks/variables.fir.kt b/compiler/testData/diagnostics/tests/smartCasts/castchecks/variables.fir.kt index 0333aef8a17..c0c4a2e291a 100644 --- a/compiler/testData/diagnostics/tests/smartCasts/castchecks/variables.fir.kt +++ b/compiler/testData/diagnostics/tests/smartCasts/castchecks/variables.fir.kt @@ -43,7 +43,7 @@ fun f(a: SomeClass?) { aa.hashCode() aa.foo (aa as? SomeSubClass).foo - (aa as SomeSubClass).foo + (aa as SomeSubClass).foo } val b = (aa as? SomeSubClass)?.foo aa = null @@ -52,7 +52,7 @@ fun f(a: SomeClass?) { aa.hashCode() aa.foo (aa as? SomeSubClass).foo - (aa as SomeSubClass).foo + (aa as SomeSubClass).foo } aa = a val c = aa as? SomeSubClass