Skip to content

Conversation

@tyralla
Copy link
Collaborator

@tyralla tyralla commented Oct 30, 2025

Completes #14771

The mentioned issue's except: assert_never examples are already fixed by #15995. This PR only targets the "no unreachable else" examples.

Introducing the special-purpose attribute IfStmt.else_irrelevant_for_possibly_undefined is not super nice, but it appears more straightforward and less risky than starting to introduce empty else blocks with is_unreachable = True (as discussed here).

@github-actions

This comment has been minimized.

mypy/checker.py Outdated

if_map, else_map = self.find_isinstance_check(e)

s.else_irrelevant_for_possibly_undefined = else_map is None
Copy link
Collaborator

@sterliakov sterliakov Oct 31, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So else_irrelevant_for_possibly_undefined is essentially equivalent to else_unreachable or is_exhaustive? I'm a bit confused by this naming

Copy link
Collaborator Author

@tyralla tyralla Oct 31, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh yes, I should have changed this. When I started working on this PR, I thought that the assert_never cases and similar ones would also need similar treatment. So else_irrelevant_for_possibly_undefined meant "unreachable or does not return" to me. I will change it later. Thanks!

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

@sterliakov
Copy link
Collaborator

sterliakov commented Oct 31, 2025

That's a nice idea overall. Can I suggest setting the same flag in reachability::infer_reachability_of_if_statement if the if is true and there is no else? Now we mark all the remaining blocks unreachable, so this flag should also become True there. And then _get_executable_if_block_with_overloads in fastparse.py can make use of it...

Now that was one path I found that checks for "unreachable else". There may be more, so maybe introducing a dummy empty else is a reasonable alternative to consider?

I tried grepping for else_body\.is_unreachable and there are only two hits, one mentioned above and one in dataclasses plugin (where this flag won't be helpful?), so maybe _get_executable_if_block_with_overloads is indeed the only other usage site. Or maybe not if there is some other access pattern...

@github-actions

This comment has been minimized.

@github-actions
Copy link
Contributor

According to mypy_primer, this change doesn't affect type check results on a corpus of open source code. ✅

@tyralla
Copy link
Collaborator Author

tyralla commented Nov 1, 2025

That's a nice idea overall. Can I suggest setting the same flag in reachability::infer_reachability_of_if_statement if the if is true and there is no else? Now we mark all the remaining blocks unreachable, so this flag should also become True there. And then _get_executable_if_block_with_overloads in fastparse.py can make use of it...

Now that was one path I found that checks for "unreachable else". There may be more, so maybe introducing a dummy empty else is a reasonable alternative to consider?

I am not sure about this.

Semantic analysis already adds fake else_body blocks:

# Make sure else body always exists and is marked as
# unreachable so the type checker always knows that
# all control flow paths will flow through the if
# statement body.
if not s.else_body:
s.else_body = Block([])
mark_block_unreachable(s.else_body)

is_unreachable is a pure product of the semantic analysis step, while unreachable_else is a type checking result that can (at least I think so) frequently change its value (e.g. in repeatedly visited loops). My feeling is that we should keep things separated.

Copy link
Collaborator

@sterliakov sterliakov left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, makes sense! I'd maybe consider a separate PR refactoring reachability.py to no longer add fake else blocks, but that might also be too much of effort for little gain. Could you please add a backlink comment to reachability.py to mention that these two strategies coexist now? It might be surprising later to see that one place adds fake else blocks and another place only uses a flag to do the same

expr: list[Expression]
body: list[Block]
else_body: Block | None
unreachable_else: bool
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you add a brief comment explaining the attribute here? It would be nice to immediately see a notice with this attr's meaning and "volatile" nature

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants