-
Notifications
You must be signed in to change notification settings - Fork 1.2k
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] Track root cause of why a type is inferred as Unknown
#12986
Conversation
} | ||
} | ||
} | ||
|
||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] |
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 derived Ord
and PartialOrd
here because the Type
enum derives them, but I'm not sure it makes sense to do so. I don't really understand why the Type
enum derives them in the first place.
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.
Yeah, me neither. I would remove Ord
from Type
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 removed the PartialOrd
and Ord
implementations in ff6b148
|
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.
How does this new type work when unifying or intersecting types? Do we keep all different variants?
Good question. Currently yes, you could end up with a union such as I suppose we should flatten them into a single |
I would love to wait with this PR to get @carljm opinion. I feel uncertain about the type's definition because there are no "obvious" definitions for unification and intersection (unless we make it a bit set, at least for unification). It's further unclear if we still need to distinguish between the two types, now that the check for unresolved imports moves into the type checker. I had a quick look at pyright and from what i understand is that it uses |
Agreed, I'm definitely not going to merge until we've heard Carl's thoughts here! There's no rush on this. |
232789d
to
d4a194b
Compare
FWIW, I think simplifying unions is inevitably going to become more complex than what we currently have, whether we go with this PR or not, because of the fact that we will want to simplify
Hmm, yeah, not sure. I'll try to pull out some of the improvements here into a separate PR that doesn't introduce the new |
d4a194b
to
698de48
Compare
#[ignore = "\ | ||
A spurious second 'Unresolved import' diagnostic message is emitted on `b.py`, \ | ||
despite the symbol existing in the symbol table for `a.py`"] |
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.
Following ecd9e6a, the key benefit of this PR is now that this test can be unskipped. This improvement is also reflected in the fact that six spurious "unresolved import" diagnostics go away in the benchmarks.
CodSpeed Performance ReportMerging #12986 will degrade performances by 5.41%Comparing Summary
Benchmarks breakdown
|
I'm open to the idea that we may need to do this eventually, but there are complexity and efficiency disadvantages to smuggling additional context through the type system this way, so I'd rather not do it until it's really clear exactly why we need it. And I don't think that's currently clear, given that the issue with the unresolved-imports check in main branch that this PR fixes is also easily fixable with a much smaller change: #13007 I don't think we should ever need this for the sake of deciding whether or not to emit a diagnostic. I would be interested in knowing more about precisely how mypy uses its extra information about the origin of |
Thanks, I think this is correct; treating |
Much of it seems to be used for precise diagnostics when strict mode is enabled. Mypy E.g. here the type of |
I still think there's a good chance that we'll need something like this eventually, but I agree that with the fixes in #13055, there are few immediate benefits to this right now. Thanks both! |
Yeah, that makes sense. I agree, we'll probably need something like this for the same reason, when we want to have an Any-forbidding strict mode and emit useful diagnostics for it. |
This variant shows inference that is not yet implemented.. ## Summary PR #13500 reopened the idea of adding a new type variant to keep track of not-implemented features in Red Knot. It was based off of #12986 with a more generic approach of keeping track of different kind of unknowns. Discussion in #13500 agreed that keeping track of different `Unknown` is complicated for now, and this feature is better achieved through a new variant of `Type`. ### Requirements Requirements for this implementation can be summed up with some extracts of comment from @carljm on the previous PR > So at the moment we are leaning towards simplifying this PR to just use a new top-level variant, which behaves like Any and Unknown but represents inference that is not yet implemented in red-knot. > I think the general rule should be that Todo should propagate only when the presence of the input Todo caused the output to be unknown. > > To take a specific example, the inferred result of addition must be Unknown if either operand is Unknown. That is, Unknown + X will always be Unknown regardless of what X is. (Same for X + Unknown.) In this case, I believe that Unknown + Todo (or Todo + Unknown) should result in Unknown, not result in Todo. If we fix the upstream source of the Todo, the result would still be Unknown, so it's not useful to propagate the Todo in this case: it wrongly suggests that the output is unknown because of a todo item. ## Test Plan This PR does not introduce new tests, but it did required to edit some tests with the display of `[Type::Todo]` (currently `@Todo`), which suggests that those test are placeholders requirements for features we don't support yet.
Summary
This PR adds detailed information to our type inference so we can track exactly why a symbol has been inferred as
Unknown
. This allows us to restore theunresolved-import
red-knot lint to the level of accuracy it had prior to a9847af, because we can now distinguish betweenUnknown
types that were caused by unresolved imports and other kinds ofUnknown
types. The approach is similar to the one mypy uses.Test Plan
There are two ways in which this PR is tested:
*
imports yet, andIterable
is defined in the typeshed stub forcollections.abc
here.red_knot_workspace/src/lint.rs
asserting that a project structure (where thefoo
module does not exist) like this only triggers an "unresolved import" diagnostic ina.py
, and not inb.py
:/src/a.py
:import foo as foo
/src/b.py
:from a import foo