Skip to content

function call that returns Never incorrectly marked as unreachable when function that's defined later with multiple argument decorator is called #13043

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

Closed
DetachHead opened this issue Jun 30, 2022 · 2 comments · Fixed by #13575
Labels
bug mypy got something wrong topic-reachability Detecting unreachable code

Comments

@DetachHead
Copy link
Contributor

sorry for the wacky title

from typing import TypeVar, Literal
from typing_extensions import assert_never

Fn = TypeVar("Fn")


def foo(fn: Fn, value: bool = False) -> Fn:
    ...


def bar(value: Literal[True]) -> None:
    if value:
        baz()
    else:
        assert_never(value) # error: Statement is unreachable  [unreachable]


@foo
def baz() -> None:
    ...

playground

functions that return Never are not supposed to be marked as unreachable - see #10916 (comment)

@DetachHead DetachHead added the bug mypy got something wrong label Jun 30, 2022
@KotlinIsland
Copy link
Contributor

KotlinIsland commented Jun 30, 2022

Looks similar to #8900

minified:

from typing import _T as T

def foo() -> None:
    return bar()
    exit()  # error: Statement is unreachable  [unreachable]

def deco(fn: T, a: bool = False) -> T: ...

@deco
def bar() -> None: ...

@AlexWaygood AlexWaygood added the topic-reachability Detecting unreachable code label Jun 30, 2022
@DetachHead
Copy link
Contributor Author

DetachHead commented Aug 11, 2022

also causes an invalid 'Expression has type "Any"' error:

from typing import TypeVar

Fn = TypeVar("Fn")

def foo(fn: Fn, value: bool = False) -> Fn:
    ...


def f() -> None:
    baz()
    try:
        ...
    except Exception as ex:  # error: Expression has type "Any"  [misc]
        pass

@foo
def baz() -> None:
    ...

playground

very related (probably the same issue?): #13167

Michael0x2a added a commit to Michael0x2a/mypy that referenced this issue Sep 1, 2022
This diff:

- Fixes python#8129
- Fixes python#13043
- Fixes python#13167

For more concise repros of these various issues, see the modified
test files. But in short, there were two broad categories of errors:

1.  Within the deferred pass, we tend to infer 'Any' for the types of
    different variables instead of the actual type. This interacts
    badly with our unreachable and disallow-any checks and causes
    spurious errors.

    Arguably, the better way of handling this error is to only collect
    errors during the final pass. I briefly experimented with this
    approach, but was unable to find a clean, efficient, and
    non-disruptive way of implementing this. So, I settled for
    sprinkling in a few more `not self.current_node_deferred` checks.

2.  The 'self.msg.disallowed_any_type(...)` call is normally guarded
    behind a `not self.chk.current_node_deferred` check. However, we
    were bypassing this check for `except` block assignments because
    we were deliberately setting that flag to False to work around some
    bug. For more context, see python#2290.

    It appears we no longer need this patch anymore. I'm not entirely
    sure why, but I'm guessing we tightened and fixed the underlying
    problem with deferred passes some time during the past half-decade.
Michael0x2a added a commit to Michael0x2a/mypy that referenced this issue Sep 2, 2022
This diff:

- Fixes python#8129
- Fixes python#13043
- Fixes python#13167

For more concise repros of these various issues, see the modified
test files. But in short, there were two broad categories of errors:

1.  Within the deferred pass, we tend to infer 'Any' for the types of
    different variables instead of the actual type. This interacts
    badly with our unreachable and disallow-any checks and causes
    spurious errors.

    Arguably, the better way of handling this error is to only collect
    errors during the final pass. I briefly experimented with this
    approach, but was unable to find a clean, efficient, and
    non-disruptive way of implementing this. So, I settled for
    sprinkling in a few more `not self.current_node_deferred` checks.

2.  The 'self.msg.disallowed_any_type(...)` call is normally guarded
    behind a `not self.chk.current_node_deferred` check. However, we
    were bypassing this check for `except` block assignments because
    we were deliberately setting that flag to False to work around some
    bug. For more context, see python#2290.

    It appears we no longer need this patch anymore. I'm not entirely
    sure why, but I'm guessing we tightened and fixed the underlying
    problem with deferred passes some time during the past half-decade.
hauntsaninja pushed a commit that referenced this issue Sep 2, 2022
…#13575)

This diff:

- Fixes #8129
- Fixes #13043
- Fixes #13167

For more concise repros of these various issues, see the modified test files. But in short, there were two broad categories of errors:

1.  Within the deferred pass, we tend to infer 'Any' for the types of different variables instead of the actual type. This interacts badly with our unreachable and disallow-any checks and causes spurious errors.

    Arguably, the better way of handling this error is to only collect errors during the final pass. I briefly experimented with this
    approach, but was unable to find a clean, efficient, and non-disruptive way of implementing this. So, I settled for sprinkling in a few more `not self.current_node_deferred` checks.

2.  The `self.msg.disallowed_any_type(...)` call is normally guarded behind a `not self.chk.current_node_deferred` check. However, we were bypassing this check for `except` block assignments because we were deliberately setting that flag to False to work around some bug. For more context, see #2290.

    It appears we no longer need this patch anymore. I'm not entirely sure why, but I'm guessing we tightened and fixed the underlying problem with deferred passes some time during the past half-decade.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug mypy got something wrong topic-reachability Detecting unreachable code
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants