[ty] Promote None to None | Unknown in invariant contexts#23790
[ty] Promote None to None | Unknown in invariant contexts#23790AlexWaygood merged 3 commits intomainfrom
None to None | Unknown in invariant contexts#23790Conversation
539a18b to
af6edd9
Compare
Typing conformance resultsNo changes detected ✅Current numbersThe percentage of diagnostics emitted that were expected errors held steady at 85.29%. The percentage of expected errors that received a diagnostic held steady at 78.13%. The number of fully passing files held steady at 64/132. |
Memory usage reportSummary
Significant changesClick to expand detailed breakdownprefect
sphinx
trio
flake8
|
|
|
| Lint rule | Added | Removed | Changed |
|---|---|---|---|
invalid-assignment |
0 | 123 | 9 |
invalid-argument-type |
0 | 29 | 34 |
invalid-await |
40 | 0 | 0 |
unsupported-operator |
6 | 3 | 12 |
no-matching-overload |
0 | 7 | 0 |
unresolved-attribute |
2 | 1 | 4 |
unused-type-ignore-comment |
6 | 0 | 0 |
invalid-return-type |
1 | 2 | 1 |
type-assertion-failure |
2 | 0 | 0 |
| Total | 57 | 165 | 60 |
2f8c33c to
becce02
Compare
becce02 to
c89d091
Compare
| // `SingletonsOnly` promotion only recurses into `NominalInstance` types (tuples | ||
| // and specialized generics). For all other types, return early. |
There was a problem hiding this comment.
It might be interesting to also handle class literal types here? See astral-sh/ty#3013 (comment). The y2: list[type] = [float] * 3 thing might be an interesting test case that could be added here?
There was a problem hiding this comment.
hmmmmm that's indeed an interesting idea! I think you're right that a variable typed as list[<class 'float'>] is just as useless as a variable typed as list[None]
There was a problem hiding this comment.
but probably we'd also want list[<class 'float'> | <class 'str'>] to be promoted to list[type]? in which case our more normal type-promotion machinery would be a better fit here, rather than the new kind of type promotion I'm adding in this PR that doesn't recurse into unions
There was a problem hiding this comment.
I'm trying it out as a standalone change in #23872
There was a problem hiding this comment.
astral-sh/ty#3013 is only helped if we promote <class 'float'> all the way to type[object] (or builtins.type) - promoting it only as far as type[float] (which is what #23872 currently does) won't help that case.
There was a problem hiding this comment.
Hey, let's not have the same conversation on two separate PRs simultaneously 😆
carljm
left a comment
There was a problem hiding this comment.
I don't love having this one special edge case where gradual types are still unexpectedly inserted, and I think we should eventually offer an opt-in diagnostic whenever it happens. Or even better, remove it again when #1473 renders it unnecessary. But the ecosystem report suggests it fixes enough real-world false positives to be worth doing.
Approving with comments -- I think the "flip" issue at least does need to be fixed before landing. The other issue feels wrong and would be ideal to fix, but not sure how much it matters in practice.
As I mentioned in the PR description, I think there are quite a few cases in the ecosystem report that won't be fixed by astral-sh/ty#1473. But (as I also said in the PR description), I agree that we should add a config option to switch this behaviour off at some point. |
c89d091 to
867fe88
Compare
Summary
You basically never want a type checker to infer a variable as having type
list[None],dict[None, <value type>],dict[None, <value type>]or similar. There's no earthly reason why anybody would ever create a list or dictionary that is only ever intended to haveNoneelements in it. The same principle applies for any singleton values that are commonly used as sentinels in Python code. Without an explicit type annotation, we can't say for sure what type the user wants us to infer[None]or[...]as, but we can be very confident that they almost certainly don't want us to inferlist[None]orlist[EllipsisType].This PR therefore adds a new type promotion that turns
NoneintoNone | Unknownin the context of unannotated invariant generic specializations. If the user really did wantlist[None]to be inferred, they can still get that result by adding an explicit type annotation.A new method,
Type::promote_singletons()has been added to achieve this: unlike other type promotions, we don't want to arbitrarily recurse into unions, etc.[42, None]should still be inferred aslist[int | None]rather thanlist[int | None | Unknown]; we only want situations such as[None]to be inferred differently.In the future, we can add a strict mode that allows users to opt out of this promotion, with us instead rejecting their code and demanding they add a type annotation to clarify their intentions. For now, though, I'd prefer to avoid false positives in this common case where we can be very confident that the unpromoted type is not what the user intended.
Ecosystem impact
We see lots of false positives going away, mostly of the form:
Many of these will be fixed by astral-sh/ty#1473, but far from all of them. For example, we also see things like this in the ecosystem, which won't be helped by any amount of bidirectional inference:
Test Plan
Mdtests added.
Closes astral-sh/ty#2972