Skip to content

Incorrect narrowing of class/global variables in nested scopes #916

@mtshiba

Description

@mtshiba

Summary

In the following code, g in the class scope D should be inferred as str | None, but is instead inferred as str.

g: str | None = None

def f(flag: bool):
    class C:
        if flag:
            g = 1

        if g is not None:  # this `g` may refer to a class variable, or a global variable
            class D:
                # refers to the global `g`
                reveal_type(g)  # should be: str | None

Here, it appears that we have mistakenly applied the narrowing constraint g is not None to the global variable g.
However, simply checking the symbol table and not recording an eager snapshot if the class variable C.g is bound is not sufficient. In the following example, we can safely narrow the global g because we can assume that C.g is undefined in class D.

g: str | None = None

def f(flag: bool):
    class C:
        if flag:
            g = None

        if g is not None:  # if this evaluates to `True`, `g` must refer to a global variable
            class D:
                reveal_type(g)  # revealed: str

This is already checked in mdtest/narrow/conditionals/nested.md.

Version

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    narrowingrelated to flow-sensitive type narrowing

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions