mirror of
https://github.com/astral-sh/ruff.git
synced 2025-07-24 05:25:17 +00:00
[ty] Fix standalone expression type retrieval in presence of cycles (#17849)
## Summary
When entering an `infer_expression_types` cycle from
`TypeInferenceBuilder::infer_standalone_expression`, we might get back a
`TypeInference::cycle_fallback(…)` that doesn't actually contain any new
types, but instead it contains a `cycle_fallback_type` which is set to
`Some(Type::Never)`. When calling `self.extend(…)`, we therefore don't
really pull in a type for the expression we're interested in. This
caused us to panic if we tried to call `self.expression_type(…)` after
`self.extend(…)`.
The proposed fix here is to retrieve that type from the nested
`TypeInferenceBuilder` directly, which will correctly fall back to
`cycle_fallback_type`.
## Details
I minimized the second example from #17792 a bit further and used this
example for debugging:
```py
from __future__ import annotations
class C: ...
def f(arg: C):
pass
x, _ = f(1)
assert x
```
This is self-referential because when we check the assignment statement
`x, _ = f(1)`, we need to look up the signature of `f`. Since evaluation
of annotations is deferred, we look up the public type of `C` for the
`arg` parameter. The public use of `C` is visibility-constraint by "`x`"
via the `assert` statement. While evaluating this constraint, we need to
look up the type of `x`, which in turn leads us back to the `x, _ =
f(1)` definition.
The reason why this only showed up in the relatively peculiar case with
unpack assignments is the code here:
78b4c3ccf1/crates/ty_python_semantic/src/types/infer.rs (L2709-L2718)
For a non-unpack assignment like `x = f(1)`, we would not try to infer
the right-hand side eagerly. Instead, we would enter a
`infer_definition_types` cycle that handles the situation correctly. For
unpack assignments, however, we try to infer the type of `value`
(`f(1)`) and therefore enter the cycle via `standalone_expression_type
=> infer_expression_type`.
closes #17792
## Test Plan
* New regression test
* Made sure that we can now run successfully on scipy => see #17850
This commit is contained in:
parent
a95c73d5d0
commit
1945bfdb84
2 changed files with 21 additions and 1 deletions
|
@ -0,0 +1,15 @@
|
|||
# Regression test for https://github.com/astral-sh/ruff/issues/17792
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
|
||||
class C: ...
|
||||
|
||||
|
||||
def f(arg: C):
|
||||
pass
|
||||
|
||||
|
||||
x, _ = f(1)
|
||||
|
||||
assert x
|
|
@ -4002,7 +4002,12 @@ impl<'db> TypeInferenceBuilder<'db> {
|
|||
let standalone_expression = self.index.expression(expression);
|
||||
let types = infer_expression_types(self.db(), standalone_expression);
|
||||
self.extend(types);
|
||||
self.expression_type(expression)
|
||||
|
||||
// Instead of calling `self.expression_type(expr)` after extending here, we get
|
||||
// the result from `types` directly because we might be in cycle recovery where
|
||||
// `types.cycle_fallback_type` is `Some(fallback_ty)`, which we can retrieve by
|
||||
// using `expression_type` on `types`:
|
||||
types.expression_type(expression.scoped_expression_id(self.db(), self.scope()))
|
||||
}
|
||||
|
||||
fn infer_expression_impl(&mut self, expression: &ast::Expr) -> Type<'db> {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue