Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Regression with type narrowing of bool #11839

Open
cdce8p opened this issue Dec 23, 2021 · 2 comments
Open

Regression with type narrowing of bool #11839

cdce8p opened this issue Dec 23, 2021 · 2 comments
Labels
bug mypy got something wrong priority-0-high topic-type-narrowing Conditional type narrowing / binder

Comments

@cdce8p
Copy link
Collaborator

cdce8p commented Dec 23, 2021

Regression in 0.920 / 0.930 compared with 0.910.

from typing import Any

def func() -> Any:
    ...

var = func()
reveal_type(var)  # Any
assert isinstance(var, bool)
reveal_type(var)  # bool

if var:
    reveal_type(var)  # Literal[True]
    pass
reveal_type(var)  # Any -> should still be 'bool'!

The type should not be lost if it can't be narrowed to Literal[True] or Literal[False]. It's a side effect of #10389. The issue was already mentioned #10389 (comment) but hasn't been addressed yet.

/CC: @ethan-leba

@cdce8p cdce8p added the bug mypy got something wrong label Dec 23, 2021
@sobolevn
Copy link
Member

Ok, looks like I was able to debug this problem. It is not really related to #10389, but this PR just made this problem visible. But, theoretically you can come up with other examples. Like:

from typing import Any

var: Any
reveal_type(var)  # N: Revealed type is "Any"
assert isinstance(var, bool) or isinstance(var, str)
reveal_type(var)  # N: Revealed type is "Union[builtins.bool, builtins.str]"

if isinstance(var, bool):
    reveal_type(var)  # N: Revealed type is "builtins.bool"

# Should be `Union[builtins.bool, builtins.str]`, but:
reveal_type(var)  # N: Revealed type is "Any"

What is the cause of this problem?

mypy/mypy/binder.py

Lines 232 to 237 in f96446c

options = self.options_on_return.pop()
if can_skip:
options.insert(0, self.frames[-1])
self.last_pop_changed = self.update_from_options(options)

  1. When we use allow_jump, it ends up with two frame objects in self.options_on_return[index]
  2. Each type in self.options_on_return[index] is only a part of the original type
  3. So, when we run this check: if not all(is_same_type(type, cast(Type, t)) for t in resulting_values[1:]): it evaluates to True and Any is returned

I guess we should write proper type joiner for frame object. Two frames with Var(x): Literal[True] and Var(x): Literal[False] should be joined as Var(x): bool. The same with union types.

@orsinium
Copy link

orsinium commented Dec 27, 2021

I have a related issue. Not sure if the same cause, though. In this code:

https://github.com/life4/deal/blob/48c6b1dbc51781f95ebba19c618f875ca9199ad4/deal/_runtime/_contracts.py#L67-L74

contracts variable should be inferred as Contracts after contracts = cls(func) but now it is Optional[Any].

  • Explicitly annotating cls doesn't help.
  • If I explicitly annotate contracts: Optional[Contracts], it stays that way, no type refinement is done by contracts = cls(func)
  • If I add an explicit assert contracts is not None, it also doesn't help, contracts stays Optional[Contracts].

In mypy 0.910 everything was fine. In pyright everything is also correctly inferred.

@JelleZijlstra JelleZijlstra added the topic-type-narrowing Conditional type narrowing / binder label Mar 19, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug mypy got something wrong priority-0-high topic-type-narrowing Conditional type narrowing / binder
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants