Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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
[red-knot] Consider all definitions after terminal statements unreachable #15676
[red-knot] Consider all definitions after terminal statements unreachable #15676
Changes from 21 commits
e0d2130
f45af24
70b681b
81a3164
93b149c
912ed62
4102c89
60eccc9
4190b5b
641ccb8
efdf6c3
57ea7ca
db98926
a434762
b9f91fb
1388eb0
79de3eb
c4d1599
82e6e67
bfe76da
0b7da66
e4771bf
9431fb9
08b0522
ee3ac28
add8a57
a0e6980
9fb8d2a
3684739
7fbec5f
5c45e88
d2b21fc
2f62eb0
32e8131
2932bb4
1cf65f5
681fae2
File filter
Filter by extension
Conversations
Jump to
There are no files selected for viewing
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.
A return at the end of the function isn't really an "early return", contrary to the test title.
Is this test testing some code that was added in this PR? It doesn't clearly seem to test anything about terminality of
return
.This test intersects with two known-incorrect areas (closed-over vars in scopes with
return
statements, modeling of eagerly-executing nested scopes), has noreveal_type
(so asserts nothing more than "no diagnostics here") and doesn't demonstrate any TODOs. This makes me question its value as a test.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.
This is a minimal reproduction of an error I was getting in the
tomllib
benchmark test. I thought to put it here to catch it earlier in the CI process, but since it's redundant with thetomllib
test I'll remove it. (Maybe a better way to handle this is to move the assertions out of the benchmark and into a new test case that also analyzestomllib
? That way the benchmark is only concerned with performance, and the test with correctness.)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.
I think it's fine (even good) to take cases that we find from tomllib (or any other testing on real code), distill them down to their simplest form that illustrates a potential regression, and include them as mdtests. So that's not an issue. I think my question here really is trying to understand what the regression was (what did we do wrong in this example in some earlier version of this PR?) and clarify what behavior the test is trying to demonstrate (maybe just with some prose, maybe by adding a
reveal_type
, maybe both).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.
When I was using visibility constraints, this was a regression because:
return
statement would mark thex
parameter as non-visible for the remainder of the flow;return
statement, and so the body of the list comprehension wouldn't see thex
formal parameter as a visible definition.It's the a lack of an
unresolved-reference
error that shows that the regression isn't there anymore.Talking through it in detail like this, I think this is superfluous with the "Early returns and nested functions" tests, because it was the "closed-over vars in scopes with
return
statements" part that was relevant, and the "modeling of eagerly-executing nested scopes" was a red herring.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.
There is also the possibility that neither
self
norsnapshot
is reachable. This code will correctly result inself
still being marked unreachable in that case, but it seems a little odd that we keep the visible definitions state fromself
in that case. The logical extension of the idea that an unreachable state takes no part in a merge should be that in case neither are reachable, we resetself
to a state withreachable: false
and no visible definitions, right?Not sure if it practically makes a difference, though; since the new state is still unreachable its visible definitions shouldn't "go" anywhere anyway, even if
self
is later merged into another state. I guess it will make a difference to the types we reveal in the following unreachable code?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.
If we want this, I think it would be best to add an invariant that marking a flow as unreachable clears out all of its definitions, and update
mark_unreachable
andrestore
to maintain that invariant. Then this code inmerge
would do the right thing as you describe.Yes, that's exactly right. (In the sense that that's what the code does, not necessarily that that's what we want it to do 😅) For now, I was punting on this, because this PR isn't currently addressing what we want to do for unreachable code. (Note that in the mdtests I've tried to not put in any
reveal_type
s in unreachable positions.)I think there are a couple of issues at play here. One is that, not even considering the merge, what do we want to report in the unreachable code within the same block after a terminal statement?
Should it be an
unresolved-reference
error? Or should it act as if the terminal statement weren't there, and show whatx
would be if control flow could somehow make it to that point? Or should we silence all diagnostics completely in unreachable code?Whatever we choose, we should have the same result for
If we decide that we want the first case to reveal
Literal[2]
, then we'd want this case to revealLiteral[2, 3]
— which means that we actually do want to merge all of the flows, even if they're unreachable, and it's just the visibility of the relevant symbols that needs to be tracked/adjusted somehow.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.
Yes, that all makes sense, thanks for clarifying!
I think we should tackle this separately as a later problem; maybe file an issue for it? I don't think it's urgent, and I'm happy with doing the "least work" for now, even if it's less consistent (that is, neither eagerly clearing definitions when a branch becomes unreachable, nor merging definitions from two unreachable branches just so we can have fully consistent types in unreachable code).
Whatever we do for unreachable code should look consistent whether the origin of that unreachability is in terminals or in statically-known branches (that is, code under an
if False
should behave similarly to code after areturn
), which may place some additional constraints on how we handle it.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.
Top-of-head thoughts on what behaviors we do/don't want (not for action now, just for consideration in writing up the issue):
Never
is the "right" type for all expressions in unreachable code?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.
Mypy has terrible UX if it sees a
reveal_type
in a block of code it considers unreachable: it just doesn't emit any diagnostic for thereveal_type
call at all. This has been the source of many bug reports at mypy over the years, because the rule that tells you off for having unreachable code in the first place is disabled by default, even if you opt into mypy's--strict
flag. We shouldn't do what mypy does!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.
#15797