Skip to content

[red-knot] Inverse narrowing without explicit else branches #14593

@sharkdp

Description

@sharkdp

Consider the following code:

def optional_int() -> int | None: ...
x = optional_int()

if x is None:
    x = 0
else:
    pass

reveal_type(x)  # revealed: int

We correctly infer int for x in the final line. This works because we union the types of both branches. The if branch has an explicit definition of x with type Literal[0]. And the empty else branch has a type of (int | None) & ~None = int for x, thanks to narrowing. The union of these two, Literal[0] | int, then simplifies to int.

However, if we remove the empty else branch, we get a type of x: int | None in the final line. This should be fixed. We should not union Literal[0] from the if branch with what we had before (int | None), but rather apply the inverted narrowing condition to the pre-if type of x, just as if we had an empty else branch.

def optional_int() -> int | None: ...
x = optional_int()

if x is None:
    x = 0

reveal_type(x)  # revealed: int | None

Metadata

Metadata

Assignees

No one assigned

    Labels

    help wantedContributions especially welcometyMulti-file analysis & type inference

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions