[ty] Infer lambda expressions with Callable type context#22633
[ty] Infer lambda expressions with Callable type context#22633ibraheemdev wants to merge 1 commit intomainfrom
Callable type context#22633Conversation
Typing conformance resultsNo changes detected ✅ |
|
5581b57 to
5d17506
Compare
|
| Lint rule | Added | Removed | Changed |
|---|---|---|---|
invalid-argument-type |
29 | 18 | 102 |
invalid-assignment |
13 | 0 | 46 |
unused-ignore-comment |
0 | 23 | 0 |
possibly-missing-attribute |
4 | 5 | 9 |
invalid-return-type |
2 | 4 | 5 |
unresolved-attribute |
5 | 0 | 5 |
type-assertion-failure |
0 | 5 | 1 |
unsupported-operator |
5 | 0 | 0 |
not-subscriptable |
4 | 0 | 0 |
no-matching-overload |
3 | 0 | 0 |
invalid-await |
1 | 0 | 0 |
| Total | 66 | 55 | 168 |
| reveal_type(self.c) | ||
|
|
||
| # revealed: (*, kw_only=...) -> Unknown | ||
| # revealed: (*, kw_only=...) -> Unknown | ((*, kw_only=...) -> Unknown) | ((*, kw_only=...) -> Divergent) | ((*, kw_only=...) -> Divergent) |
There was a problem hiding this comment.
The revealed type here is a little unfortunate. Ideally it would just be () -> Unknown | (() -> Divergent), but I'm not sure that is an issue related to this PR.
The weird typing- conformance comment should be fixed if you rebase that PR on main, and then this PR on that PR (sorry for the teething problems there with the new workflow...) |
|
There seems to be a new fuzzer panic |
fa2b058 to
109d2a5
Compare
af62f6c to
7d33c41
Compare
Merging this PR will not alter performance
Comparing Footnotes
|
7d33c41 to
a8bb4fc
Compare
|
There is one new fuzzer panic: lambda: name_4
@lambda: name_5
class name_1:
pass
name_2 = [lambda: name_4, name_1]
if name_2:
@(*name_2,)
class name_3:
pass
assert unique_name_19
@lambda: name_3
class name_4[*name_2](0, name_1=name_3):
pass
try:
[name_5, name_4] = *name_4, = name_4
except* 0:
pass
else:
async def name_4():
pass
for name_3 in name_4:
passSalsa traceWe are now inferring the lambda bodies eagerly while inferring the outer scope, and so there is a cyclic dependency between |
a8bb4fc to
d050295
Compare
|
There is still a fuzzer panic on this. I'm going to move it to draft until that is resolved. |
Detailed analysis of the panic caused by eagerly inferring lambda bodies as part of their parent scope's inference. The cycle involves circular dependencies between name_4, name_2, name_3 through lambda body references, unpack assignments, and class decorators. Root cause: eager lambda body inference creates new dependency edges that form non-converging cycles when lambda bodies reference names involved in circular definition chains. https://claude.ai/code/session_01Unrae9uHg8UZ2um1EKH3py
…context When `infer_lambda_expression` eagerly infers the lambda body via `infer_scope_types`, it can introduce cycle edges: the lambda body may reference names whose definitions depend on this very expression (e.g. a lambda used as a decorator on a class that is referenced transitively by the lambda body). Without a Callable type context, the inferred return type participates in the cycle and can oscillate between iterations, causing a "too many cycle iterations" panic. Fix: only use the inferred body return type when a direct Callable type context is available (the main feature of PR #22633). Without a type context, fall back to `Unknown` for the return type while still inferring the body for diagnostics (reveal_type, etc.). https://claude.ai/code/session_0148KoTarLuZRPBttHzmZYEU
## Summary There are some issues with our current cycle handling, particularly with our implementation of `Type::cycle_normalized`. To prevent values from oscillating endlessly within a fixed-point iteration, `cycle_normalized` unions the type of the previous iteration with the current type. This ensures monotonicity of the type inference calculation. However, we've observed that always applying this union can sometimes produce undesirable results. Therefore, we make some exceptions where this union isn't applied (see #21909, #21910). However, these prescriptions are likely not exhaustive, and #22794 also confirmed the occurrence of "divergent pollution" when narrowing. The core issue in #22794 is that `Divergent` can inhibit narrowing. The result type itself does not contain `Divergent`, but `Divergent` indirectly affects it. This suggests that determining whether a type contains `Divergent` is not a perfect indicator of "taintedness". After carefully examining these cases, I found that the problematic "low-precision types" only appear in the first few iterations: `Divergent` itself and the type of the next iteration constructed by referencing it. Therefore, we shouldn't union these few iterations. I found that this simple change can remove all of the exceptional handling for `Divergent` introduced in #21909, #21910, and #22794. I also found that this change removes the too-many-cycle panic blocking #22633. ## Test Plan mdtest updated --------- Co-authored-by: Carl Meyer <carl@astral.sh>
Infer lambda expressions eagerly as part of their parent scope, and with type context. This allows us to infer more precise types for lambda expressions, as well as perform check assignability against
Callableannotations.Note that this does not change the inferred type of a lambda parameter with the body of the lambda, even if it is annotated. That part is a little more tricky, so will be addressed in a followup PR.
This PR is stacked on #22564.