-
-
Notifications
You must be signed in to change notification settings - Fork 2.8k
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
check_boolean_op: clean up semanal/type-based reachability #10566
Conversation
d95234e
to
9c34a35
Compare
if e.op == 'and': | ||
left_map: mypy.checker.TypeMap | ||
right_map: mypy.checker.TypeMap | ||
if e.right_always: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For always-true/false cases, find_isinstance_check
returns a {}, None
or a None, {}
pair. We do the same here, but without even calling find_isinstance_check
.
@@ -2822,6 +2818,13 @@ def check_boolean_op(self, e: OpExpr, context: Context) -> Type: | |||
assert right_map is not None # find_isinstance_check guarantees this | |||
return right_type | |||
|
|||
if e.op == 'and': |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can safely move it down here since nothing uses restricted_left_type
and result_is_left
until this point.
@hauntsaninja @JukkaL want to take a look? |
@hauntsaninja @JukkaL gentle ping |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry for the delay and thanks for this well explained PR that fixes a crash!
However, the reveal types in your test cases aren't quite what I'd expect. E.g., in your first test case I'd expect reveal_type(b)
to be bool (Literal[True]
and None
shouldn't really be appearing anywhere, since mypy should always think the COMPILE_TIME_* are True or False).
I think we should maybe set left_type
and right_type
if e.right_always
or e.right_unreachable
? Also happy to merge as is!
You're saying you'd expect: indeterminate: bool
COMPILE_TIME_FALSE: Literal[True]
b = indeterminate or COMPILE_TIME_FALSE
-reveal_type(b) # N: Revealed type is "Union[builtins.bool, Literal[True]]"
+reveal_type(b) # N: Revealed type is "builtins.bool" but we don't know if Or in this case you'd expect: indeterminate: bool
COMPILE_TIME_TRUE: None
b = indeterminate or COMPILE_TIME_TRUE
-reveal_type(b) # N: Revealed type is "Union[builtins.bool, None]"
+reveal_type(b) # N: Revealed type is "builtins.bool" Same logic. |
which I interpreted as the variable should be interpreted as that literal boolean value, not just something that's truthy or falsey (TYPE_CHECKING and MYPY are often used to make bold lies to the type checker, so just wiping out the type seemed like a valid interpretation). |
That's what I'd assume too, and maybe that'd be a better way to implement it. As for now, those options manifest in the - # Is the right side going to be evaluated every time?
+ # Per static analysis only: Is the right side going to be evaluated every time?
right_always = False
- # Is the right side unreachable?
+ # Per static analysis only: Is the right side unreachable?
right_unreachable = False |
ExpressionChecker.check_boolean_op
supports two potentially conflicting ways of determining whether an expression is always true/false: name-based checks from semantic analysis (e.g.name in options.always_true
) and type narrowing inTypeChecker.find_isinstance_check
(*). The two ways can be set to conflict, e.g.In the above case,
COMPILE_TIME_TRUE
would be always true per semanal and always false perfind_isinstance_check
, which results in an assertion failure here:mypy/mypy/checkexpr.py
Line 2828 in 5eb4de4
and here:
mypy/mypy/checkexpr.py
Line 2832 in 5eb4de4
This change reconciles the two, by prioritizing the semanal flags (e.right_always, e.right_unreachable) over type-narrowing (if_map, else_map). Then we explicitly avoid flagging reachability errors when the semanal flags are set, since they're obviously used intentionally.
(*) which seems to do slightly more than
isinstance
checks