[FIR] Fully expand the bounds of type parameters for canBeNull check

This is required because a not-null bound `Foo` can resolve to a
`typealias Foo = Bar?` in which case we must return true.

#KT-64645 Fixed
This commit is contained in:
Kirill Rakhman
2024-01-02 15:45:41 +01:00
committed by Space Team
parent 438b2dd164
commit 1018ff280e
23 changed files with 100 additions and 54 deletions
@@ -197,7 +197,7 @@ object CheckDispatchReceiver : ResolutionStage() {
if (smartcastedReceiver != null &&
!smartcastedReceiver.isStable &&
(isCandidateFromUnstableSmartcast || (isReceiverNullable && !smartcastedReceiver.smartcastType.canBeNull))
(isCandidateFromUnstableSmartcast || (isReceiverNullable && !smartcastedReceiver.smartcastType.coneType.canBeNull(callInfo.session)))
) {
val dispatchReceiverType = (candidate.symbol as? FirCallableSymbol<*>)?.dispatchReceiverType?.let {
context.session.typeApproximator.approximateToSuperType(
@@ -118,7 +118,7 @@ class MemberScopeTowerLevel(
// with two exceptions:
// - When the smart-casted type is always null, we want to return it and report UNSAFE_CALL.
// - When the original type can be null, in this case the smart-case either makes it not-null or the call is red anyway.
if (existing == null || dispatchReceiverValue.type.isNullableNothing || receiverTypeWithoutSmartCast.canBeNull) {
if (existing == null || dispatchReceiverValue.type.isNullableNothing || receiverTypeWithoutSmartCast.canBeNull(session)) {
map[candidateFromSmartCast.member] = MemberFromSmartcastScope(candidateFromSmartCast, DispatchReceiverToUse.SmartcastWithoutUnwrapping)
} else {
existing.dispatchReceiverToUse = DispatchReceiverToUse.SmartcastIfUnwrappedInvisible
@@ -368,7 +368,7 @@ abstract class FirDataFlowAnalyzer(
if (operandVariable.isReal()) {
flow.addImplication((expressionVariable eq isType) implies (operandVariable typeEq type))
}
if (!type.canBeNull) {
if (!type.canBeNull(components.session)) {
// x is (T & Any) => x != null
flow.addImplication((expressionVariable eq isType) implies (operandVariable notEq null))
} else if (type.isMarkedNullable) {
@@ -383,7 +383,7 @@ abstract class FirDataFlowAnalyzer(
if (operandVariable.isReal()) {
flow.addTypeStatement(operandVariable typeEq type)
}
if (!type.canBeNull) {
if (!type.canBeNull(components.session)) {
flow.commitOperationStatement(operandVariable notEq null)
} else {
val expressionVariable = variableStorage.createSynthetic(typeOperatorCall)
@@ -998,9 +998,10 @@ abstract class FirDataFlowAnalyzer(
for (conditionalEffect in conditionalEffects) {
val effect = conditionalEffect.effect as? ConeReturnsEffectDeclaration ?: continue
val operation = effect.value.toOperation()
val statements = logicSystem.approveContractStatement(conditionalEffect.condition, argumentVariables, substitutor) {
logicSystem.approveOperationStatement(flow, it, removeApprovedOrImpossible = operation == null)
} ?: continue // TODO: do what if the result is known to be false?
val statements =
logicSystem.approveContractStatement(conditionalEffect.condition, argumentVariables, substitutor) {
logicSystem.approveOperationStatement(flow, it, removeApprovedOrImpossible = operation == null)
} ?: continue // TODO: do what if the result is known to be false?
if (operation == null) {
flow.addAllStatements(statements)
} else {