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

[red-knot] Handle multiple comprehension targets #13213

Merged
merged 2 commits into from
Sep 4, 2024

Conversation

dhruvmanila
Copy link
Member

@dhruvmanila dhruvmanila commented Sep 2, 2024

Summary

Part of #13085, this PR updates the comprehension definition to handle multiple targets.

Test Plan

Update existing semantic index test case for comprehension with multiple targets. Running corpus tests shouldn't panic.

@dhruvmanila dhruvmanila added the red-knot Multi-file analysis & type inference label Sep 2, 2024
@dhruvmanila dhruvmanila changed the title [red-knot] Handle multiple for comprehension targets [red-knot] Handle multiple comprehension targets Sep 2, 2024
@dhruvmanila dhruvmanila marked this pull request as ready for review September 2, 2024 12:48
Copy link
Contributor

github-actions bot commented Sep 2, 2024

ruff-ecosystem results

Linter (stable)

✅ ecosystem check detected no linter changes.

Linter (preview)

✅ ecosystem check detected no linter changes.

@AlexWaygood AlexWaygood removed their request for review September 2, 2024 13:41
Copy link
Contributor

@carljm carljm left a comment

Choose a reason for hiding this comment

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

Looks good! My only issue is with the name(s) infer_comprehension(s)_scope, otherwise this makes sense. Thanks for taking care of this!

@@ -156,7 +156,8 @@ pub(crate) struct ForStmtDefinitionNodeRef<'a> {

#[derive(Copy, Clone, Debug)]
pub(crate) struct ComprehensionDefinitionNodeRef<'a> {
pub(crate) node: &'a ast::Comprehension,
pub(crate) iterable: &'a ast::Expr,
Copy link
Contributor

Choose a reason for hiding this comment

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

I was wondering if we'll also need access to the if portion of the generator here, but I think we won't; that expression should be handled separately as a Constraint, it's not part of the Definition.

Copy link
Member Author

Choose a reason for hiding this comment

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

Yeah, that's what I thought as well which is the reason to limit this. It can easily be reverted in the future if there's a need.

self.infer_definition(comprehension);
for expr in &comprehension.ifs {
self.infer_expression(expr);
fn infer_comprehensions_scope(&mut self, comprehensions: &[ast::Comprehension]) {
Copy link
Contributor

Choose a reason for hiding this comment

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

We've inherited some awfully confusing naming from the CPython grammar and AST :( If we have [y for x in [] for y in x], that's a single "list comprehension", whose AST node has a generators attribute containing two... Comprehensions, for x in [] and for y in x. So not only is the term "generator" overloaded here from its usual meaning, but we're also using the term "comprehension" itself to mean two entirely different things, one of which is part of the other 🤯

(To be clear, this is not a critique of this PR, just a lament for the state of naming in this part of the AST. We could fix this in our AST, but then we'd be inventing our own naming scheme that doesn't match the CPython AST, and that doesn't seem great either.)

One way to maybe clarify this naming a bit would be to switch from list_comprehension, dict_comprehension, etc all through these method names to instead use listcomp, dictcomp, etc when we are referring to the outer expression, and reserve the full word "comprehension" for the thing actually named Comprehension in the AST, which is just the inner for x in y part. I wouldn't exactly call that clear, but given what we're working with, it might at least be better? It at least matches the names used in the AST and avoids using the word "comprehension" to mean two different things.

In any case, I don't like the names infer_comprehensions_scope and infer_comprehension_scope for these two methods. The individual "Comprehension" nodes inside a list/set/dict/gen-comp are not different scopes, so the use of "scope" here is misleading.

If we go with my suggestion above to rename e.g. infer_list_comprehension_expression to infer_listcomp_expression, then I think we could name these two just infer_comprehensions and infer_comprehension.

Copy link
Member Author

Choose a reason for hiding this comment

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

I agree with the sentiment, thanks for sharing that.

(To be clear, this is not a critique of this PR, just a lament for the state of naming in this part of the AST. We could fix this in our AST, but then we'd be inventing our own naming scheme that doesn't match the CPython AST, and that doesn't seem great either.)

I think, if we want, we can change the names in our AST as we've done so in the past as well (#8064, #6379, #6253, etc.). There are other recommendations here: #6183

One way to maybe clarify this naming a bit would be to switch from list_comprehension, dict_comprehension, etc all through these method names to instead use listcomp, dictcomp, etc when we are referring to the outer expression, and reserve the full word "comprehension" for the thing actually named Comprehension in the AST, which is just the inner for x in y part.

From my perspective, both listcomp and list_comprehension seems equivalent as the former seems like a short version of the latter. Is the reason for recommending listcomp because of it's presence in the CPython AST?

In any case, I don't like the names infer_comprehensions_scope and infer_comprehension_scope for these two methods. The individual "Comprehension" nodes inside a list/set/dict/gen-comp are not different scopes, so the use of "scope" here is misleading.

Makes sense. I can change this.

Copy link
Member Author

Choose a reason for hiding this comment

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

I've not renamed the list_comprehension to listcomp as I feel like the "expression" prefix in list_comprehension_expression gives a signal that this is different than a simple "comprehension". Regardless, I don't have a strong opinion, so I'm happy to rename it if you think listcomp is better.

Copy link
Contributor

Choose a reason for hiding this comment

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

Yeah, mostly what my suggestion was aiming for was to hew very closely to the AST naming, in hopes that this would at least give some guideposts to readers. But I'm OK with your approach and relying on _expression to distinguish.

crates/red_knot_python_semantic/src/types/infer.rs Outdated Show resolved Hide resolved
Copy link

codspeed-hq bot commented Sep 4, 2024

CodSpeed Performance Report

Merging #13213 will improve performances by 4.66%

Comparing dhruv/comprehension-multiple-target (b72afe7) with main (3c4ec82)

Summary

⚡ 1 improvements
✅ 31 untouched benchmarks

Benchmarks breakdown

Benchmark main dhruv/comprehension-multiple-target Change
linter/default-rules[pydantic/types.py] 1.9 ms 1.8 ms +4.66%

@dhruvmanila dhruvmanila merged commit e1e9143 into main Sep 4, 2024
20 checks passed
@dhruvmanila dhruvmanila deleted the dhruv/comprehension-multiple-target branch September 4, 2024 05:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
red-knot Multi-file analysis & type inference
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants