[ty] Fix narrowing of nonlocal variables with conditional assignments#22966
[ty] Fix narrowing of nonlocal variables with conditional assignments#22966charliermarsh merged 1 commit intomainfrom
Conversation
Typing conformance resultsNo changes detected ✅ |
|
| // this can only happen if the code is unreachable | ||
| // and therefore it is correct to set the result to `Never`. | ||
| let union = union.build(); | ||
| if union.is_assignable_to(db, ty) { |
There was a problem hiding this comment.
This was added in https://github.com/astral-sh/ruff/pull/17643/changes#diff-20b910c6e20faa962bb1642e111db1cbad8e66ace089bdd966ac9d7f9fa99ff2R6047, but I'm not totally sure why.
There was a problem hiding this comment.
@mtshiba, I don't suppose you can remember the reason why this was added?
|
| Lint rule | Added | Removed | Changed |
|---|---|---|---|
invalid-return-type |
3 | 4 | 4 |
invalid-argument-type |
0 | 6 | 1 |
possibly-missing-attribute |
0 | 5 | 1 |
invalid-assignment |
0 | 4 | 1 |
unresolved-attribute |
0 | 1 | 2 |
invalid-await |
0 | 2 | 0 |
type-assertion-failure |
2 | 0 | 0 |
no-matching-overload |
0 | 1 | 0 |
unused-type-ignore-comment |
1 | 0 | 0 |
| Total | 6 | 23 | 9 |
carljm
left a comment
There was a problem hiding this comment.
The removed guard makes the assumption that a narrowed type in a nested scope should never be wider than the type in the outer scope which binds the name. (Since we have nonlocal x in the nested scope, the outer scope here binds x.) This assumption seems reasonable, but is violated by the x = certain_int assignment in the nested scope here.
There's an open question here about how we should be reflecting the inner x = certain_int in the type of x in the outer scope. Because of nonlocal x, that assignment means that x can become int again in the outer scope, wherever the nested function is called. (Which in the simplified example in this test, is obviously "never"). I think probably the best balance here would be to consider that the definition of the nested scope itself should "apply" the inner nonlocal bindings in the outer scope, so anywhere in the outer scope after the def _() -> None: we treat x in the outer scope as could-be-int again. (The rationale here is that before the nested scope is defined, it can't possibly be called; after it is defined, it's not feasible to track every specific place where it might be called.) But all of this is a separate issue, and I don't think it removes the need for the change in this PR.
|
The ecosystem results all look correct here, with the exception of the new diagnostic in xarray -- but there we are triggering a pre-existing (and unrelated) issue where we narrow constrained typevars too eagerly. |
Summary
Given:
With the guard (removed in this PR),
assert x is not Nonewould have no effect, sinceint | (float & ~int)is not assignable to(float & ~int) | None.Closes astral-sh/ty#2649.