Skip to content

False positive unreachable when variable is set and reset inside loop #14987

Closed
@adebrecht661

Description

@adebrecht661

Bug Report

mypy warns about the right side of a condition being unreachable when a variable is reset in an outer loop, then set again inside the inner loop.

To Reproduce

from typing import Optional

test: Optional[float] = None
reveal_type(test)  # Revealed type: Union[builtins.float, None]

for i in range(10):
    test = None
    reveal_type(test)  # Revealed type: None (???)
    for k in range(10):
        if test is None or k > test:
           test = k

Expected Behavior

No error.

Actual Behavior

mypy outputs error: Right operand of "or" is never evaluated. This is clearly because the type of test is re-evaluated at some point, even though it was declared with a type and no statement narrows that type.

This appears related to #7204, #8721, #8865, #13973, #14120, except it can't be solved by declaring the type of the variable before the loop in which it's updated (because that violates no-redef).

Note: I'm not sure why anyone would want to actually do this in practice. But I thought it might provide a useful test case.

Your Environment

  • Mypy version used: 1.1.1
  • Mypy command-line flags: --disable-error-code name-defined, plus a few others that I don't believe could possibly have any relevance.
  • Mypy configuration options from mypy.ini (and other config files): None
  • Python version used: 3.10.10

Activity

Melebius

Melebius commented on Jun 15, 2023

@Melebius

Similar problem here while using mypy==1.3.0.

My example code:

data = """
{foo}
bar >
baz
"""

prompt = None
reponse = None
lines = data.splitlines()
for line in lines:
    if prompt and reponse:  # mypy error: Right operand of "and" is never evaluated  [unreachable]
        print("Break reached!")  # mypy error: Statement is unreachable  [unreachable]
        break
    if not prompt and line.endswith(">"):
        prompt = line
        continue
    if not reponse and line.endswith("}"):
        reponse = line
        continue

Run output:

$ python example.py
Break reached!
$
patrickelectric

patrickelectric commented on Aug 4, 2023

@patrickelectric
grihabor

grihabor commented on Jun 9, 2024

@grihabor

+1

Here is a smaller example:

state = None
for x in 'ab':
    if state is not None:
        print('reachable')
    state = 'x'

The script output:

reachable

Mypy error:

main.py:4:9: error: Statement is unreachable  [unreachable]
            print('reachable')
            ^~~~~~~~~~~~~~~~~~
Found 1 error in 1 file (checked 1 source file)

✕ mypy failed.
A5rocks

A5rocks commented on Jan 22, 2025

@A5rocks
Collaborator

@grihabor and @Melebius your issues were fixed by #18433, though the initial example was not.

grihabor

grihabor commented on Jan 23, 2025

@grihabor

Nice, thanks for the ping

tyralla

tyralla commented on Jun 3, 2025

@tyralla
Collaborator

I tend to think the problem underlying the initial example has nothing to do with loops but with promotions, because both float and int are involved.

So, maybe this is a simpler repro:

x: float | None
y: int | None
z: int
reveal_type(x)  # float | None
x = y
reveal_type(x)  # None   !!!!!!!!!
x = z
reveal_type(x)  # int

With "normal" subtypes, the same type of type narrowing works:

class A: ...
class B(A): ...

a: A | None
b: B | None
c: B
reveal_type(a)  # A | None
a = b
reveal_type(a)  # B | None
a = c
reveal_type(a)  # B

Likely, an attempt to solve this could start here:

https://github.com/python/mypy/blob/5a0fa556be741270e25c95a923ef8450d28dd448/mypy/meet.py#L130C1-L145C10

self-assigned this
on Jun 3, 2025
added a commit that references this issue on Jul 7, 2025
cb3bddd
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

bugmypy got something wrong

Projects

No projects

Milestone

No milestone

Relationships

None yet

    Development

    Participants

    @patrickelectric@Melebius@grihabor@tyralla@A5rocks

    Issue actions

      False positive `unreachable` when variable is set and reset inside loop · Issue #14987 · python/mypy