[ty] Normalize recursive TypeOf growth during cycle recovery#26163
Conversation
Typing conformance resultsNo changes detected ✅Current numbersThe percentage of diagnostics emitted that were expected errors held steady at 94.37%. The percentage of expected errors that received a diagnostic held steady at 89.00%. The number of fully passing files held steady at 94/134. |
Memory usage reportSummary
Significant changesClick to expand detailed breakdownflake8
sphinx
prefect
trio
|
dbf2027 to
c8296d8
Compare
|
c8296d8 to
5026d67
Compare
TypeOf growth during cycle recovery
| case 0: | ||
| async def name_4(): | ||
| pass | ||
| ``` |
There was a problem hiding this comment.
The initial fix introduced a panic caught by our fuzzer; this captures it.
There was a problem hiding this comment.
It might be worth minimizing this example but if that's not possible it would be useful to expand the prose to contain the inference path / symbol name that's caused this panic.
| case 0: | ||
| async def name_4(): | ||
| pass | ||
| ``` |
There was a problem hiding this comment.
It might be worth minimizing this example but if that's not possible it would be useful to expand the prose to contain the inference path / symbol name that's caused this panic.
| // Only align unions when each iteration has exactly one changed arm. With multiple | ||
| // unmatched arms, pairing is ambiguous and can destabilize otherwise-convergent | ||
| // recursive cycles. |
There was a problem hiding this comment.
Does this mean that something like the following might still hang?
from ty_extensions import TypeOf
class Container[T]: ...
x: list[TypeOf[x]] | Container[TypeOf[x]]
x = [1]Regardless, I don't think it's worth prioritizing given that TypeOf is internal.
5026d67 to
442d7cc
Compare
## Summary This reverts #26163 and its follow-up changes in #26230, #26246, #26274, #26275, and #26316. The recursive nominal-growth recovery replaces a previous cycle result nested inside a specialization with `Divergent`. The follow-up regressions show that this local replacement is not structurally safe: multi-arm union recovery can lose stable arms, and variadic tuple growth can continue after replacement. Addressing those cases required increasingly shape-specific alignment and guards without a general way to establish correspondence between fixed-point iterations. This restores the cycle-recovery behavior from before #26163. The direct self-referential `TypeOf` case and the regression coverage for astral-sh/ty#3835 and astral-sh/ty#3838 continue to pass without the heuristic and are retained. We remove only the cases that no longer converge: `TypeOf` references nested in nominal specializations and the nested-iterable case from astral-sh/ty#3827. The independent `Divergent` constraint fixes from #26288 and #26334, including the regression coverage for astral-sh/ty#3836, are also retained.
Summary
A
TypeOfself-reference nested in a generic specialization can cause each fixed-point iteration to wrap the previous inferred result:Prior to this change, we repeatedly inferred a deeper specialization such as
list[int],list[list[int]], and so on, eventually exhausting Salsa's cycle iterations.This detects structurally growing nominal specializations after the tainted cycle iterations, replaces the previous cycle result with the cycle's
Divergentmarker, and then reuses the existing recursive-type normalization to converge.Closes astral-sh/ty#3802.