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

Don't special-case class instances in unary expression inference #15045

Merged
merged 7 commits into from
Dec 18, 2024

Conversation

dcreager
Copy link
Member

We have a handy to_meta_type that does the right thing for class instances, and also works for all of the other types that are “instances of” something. Unless I'm missing something, this should let us get rid of the catch-all clause in one fell swoop.

cf #14548

We have a handy `to_meta_type` that does the right thing for class
instances, and also works for all of the other types that are “instances
of” something.
@dcreager dcreager added the red-knot Multi-file analysis & type inference label Dec 18, 2024
Copy link
Contributor

github-actions bot commented Dec 18, 2024

ruff-ecosystem results

Linter (stable)

✅ ecosystem check detected no linter changes.

Linter (preview)

✅ ecosystem check detected no linter changes.

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.

Very nice!

Copy link
Member

@AlexWaygood AlexWaygood left a comment

Choose a reason for hiding this comment

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

Nice!!

op @ (UnaryOp::UAdd | UnaryOp::USub | UnaryOp::Invert),
Type::Instance(InstanceType { class }),
) => {
(op @ (UnaryOp::UAdd | UnaryOp::USub | UnaryOp::Invert), _) => {
Copy link
Member

Choose a reason for hiding this comment

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

nit: I know there's a lot of variants in the Type enum, but I'd prefer it if we could explicitly list all the remaining ones in this match arm rather than using _ for the second tuple element. The advantage is that if we add another Type variant in the future that we want to special-case in unary expressions (similar to the special-casing we already do for Type::IntLiteral and Type::BooleanLiteral above), there's no chance of us forgetting to add that special-casing. We'll be forced to explicitly consider whether the new variant should be handled in its own match arm or be handled as part of the catch-all fallback case here.

Copy link
Contributor

Choose a reason for hiding this comment

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

I'm not so sure explicitly listing all variants is always the best answer. In this case, the handling in this arm really is a generic implementation of the semantics in terms of other type semantics. It should always be correct for any type (even the ones we special case above here), assuming a correct implementation of calling dunder methods (to_meta_type, member, etc -- all of which do use exhaustive matches and we'll be forced to consider for any new Type variant.) Thus, a special case for a new Type variant here would not be required for correctness, it would just potentially offer more precision. I kind of think it's fine, possibly even good, for that "more precision" to be driven only by actual user needs, and not something we are forced to consider.

But I'm really fine with it either way.

Copy link
Member

@AlexWaygood AlexWaygood Dec 18, 2024

Choose a reason for hiding this comment

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

Yes, you're right that there isn't a correctness issue here. But I would expect more precision here to be both trivial to accomplish for any new variants and simpler for us to compute. The IntLiteral() branches above really involve much less work for us than doing the full lookup for an instance type's dunder method in typeshed or user code (which could involve materialising an MRO!). I don't really see a reason not for us to infer the more precise type whenever possible if it's trivial to do so, and I'd at least like us to be forced to consider whether it would be better to do so

Copy link
Member Author

Choose a reason for hiding this comment

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

Sounds like there's a -0 vote from Carl and a +1 from Alex, so I went ahead and expanded the clauses out. We can always put it back to _ in the future if we find this more unwieldy than the benefit.

@dcreager dcreager merged commit 2802cbd into main Dec 18, 2024
21 checks passed
@dcreager dcreager deleted the dcreager/unary branch December 18, 2024 19:37
dcreager added a commit that referenced this pull request Jan 6, 2025
…5161)

Just like in #15045 for unary expressions: In binary expressions, we
were only looking for dunder expressions for `Type::Instance` types. We
had some special cases for coercing the various `Literal` types into
their corresponding `Instance` types before doing the lookup. But we can
side-step all of that by using the existing `Type::to_meta_type` and
`Type::to_instance` methods.
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.

3 participants