Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Binder loses narrowed type of a variable if variable may be uninitialized #18619

Open
JukkaL opened this issue Feb 6, 2025 · 5 comments
Open
Assignees
Labels
bug mypy got something wrong false-positive mypy gave an error on correct code priority-0-high topic-type-narrowing Conditional type narrowing / binder

Comments

@JukkaL
Copy link
Collaborator

JukkaL commented Feb 6, 2025

Bug Report

Mypy sometimes loses track of the narrowed type of a variable, at least when the variable can be uninitialized in some code paths. This can happen when variables is both initialized and narrowed in a conditional block.

To Reproduce

if int():
    x: int | str
    x = 0
    x = str(x)
reveal_type(x) # int | str

Expected Behavior

Revealed type is str.

Actual Behavior

Revealed type is int | str.

Discussion

Here are some more realistic examples:

def cond() -> bool:
    return True

if cond():
    x: int | str
    x = 0
    x = str(x)
if cond():
    reveal_type(x) # int | str

for i in [1]:
    y: int | bytes
    y = 0
    y = bytes(y)
reveal_type(y) # int | bytes

def f(x: int | None) -> int:
    return x or 1

def g() -> None:
    if cond():
        if int():
            z = None
        else:
            z = 1
        z = f(z)
    if cond():
        reveal_type(z) # int | None

def h() -> None:
    if cond():
        if int():
            z = 1
        else:
            z = None
        z = f(z)
    if cond():
        reveal_type(z) # int | None

This issue interferes with inferring union types from assignments (#18568).

@ilevkivskyi Do you have an idea of how to fix this, since you did some work on the binder recently? If a variable hasn't been assigned in some conditional code path, maybe the type should be treated as Never when merging types from different code paths?

@JukkaL JukkaL added bug mypy got something wrong false-positive mypy gave an error on correct code priority-0-high topic-type-narrowing Conditional type narrowing / binder labels Feb 6, 2025
@ilevkivskyi
Copy link
Member

First of all I am far from binder expert :-), but unfortunately I don't think there is an easy/clean fix for this. The problem is that the textually first assignment is the one that sets the declared type. So these two are identical from the point of view of the binder

if cond():
    x: int | str = ...
    x = 0

and

x: int | str = ...
if cond():
    x = 0

while for the second one we obviously want to keep the type as union. We would need to track somehow where the variable was first declared and act accordingly. I however don't have any simple/clear idea on this.

@ilevkivskyi
Copy link
Member

The only relatively clean solution I see is we would need to track in which frames a variable is readable, and ignore frames without information when popping them if a variable is not readable in that frame (now if there is at least one frame without information, we erase all information so far, see update_from_options()). But IIUC this will essentially duplicate the work done in PossiblyUndefinedVariableVisitor.

@JukkaL
Copy link
Collaborator Author

JukkaL commented Feb 7, 2025

The only relatively clean solution I see is we would need to track in which frames a variable is readable, and ignore frames without information when popping them if a variable is not readable in that frame

This looks promising. In my prototype implementation I now update binder on initial assignment, so that if there is nothing in binder, the variable is uninitialized in that frame. This broke a bunch of things, however, but I hope I can fix all the resulting issues. This will possibly be enabled only when the new type inference logic is enabled (inferring unions from assignments) to reduce potential for backward compatibility breaks.

@Saunakghosh10
Copy link

trying to fix this

@JukkaL
Copy link
Collaborator Author

JukkaL commented Feb 11, 2025

@Saunakghosh10 I'm working on this and have a prototype implementation.

@JukkaL JukkaL self-assigned this Feb 11, 2025
# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
bug mypy got something wrong false-positive mypy gave an error on correct code priority-0-high topic-type-narrowing Conditional type narrowing / binder
Projects
None yet
Development

No branches or pull requests

3 participants