Skip to content

[ty] Fix various overflows with recursive type aliases#24683

Open
charliermarsh wants to merge 6 commits intomainfrom
charlie/recursive-type-aliases
Open

[ty] Fix various overflows with recursive type aliases#24683
charliermarsh wants to merge 6 commits intomainfrom
charlie/recursive-type-aliases

Conversation

@charliermarsh
Copy link
Copy Markdown
Member

@charliermarsh charliermarsh commented Apr 17, 2026

Summary

This PR fixes a variety of panics that arise from recursive PEP 695 type aliases. (Codex estimates that it fixes 40-50 user-visible panics stemming from 8-12 underlying codepaths.)

Previously, a bunch of type operations handled aliases by directly expanding the alias RHS with alias.value_type(db) and then continuing the same operation on the expanded type. That works for acyclic aliases, but recursive aliases can re-enter the same alias expansion before the outer operation completes, leading to stack overflows or cycle non-convergence.

For example:

type RecursiveList[T] = T | list[RecursiveList[T]]

r1: RecursiveList[int] = [1, [1, 2, 3]]
r2: RecursiveList[int] = [1, ["a"]]

The core fix is to make alias visitation a guarded operation. Alias expansion now goes through Type::visit_type_alias_value(...), backed by an active-alias stack keyed by the alias identity. When an operation reaches the same active alias again, it stops expanding that recursive edge and returns an operation-specific conservative fallback (like Unknown, or a default). We have to take this into account anywhere that we try to access an alias value.

I initially implemented this by threading a visitor through all type operations (first commit). That worked, but it required adding _impl variants all across the API, and remembering to thread the visitor through in the correct places. So I did a second pass to use a thread-local guard, which significantly reduces the diff at the cost of some implicit magic. I am fine either approach, but figured this was much easier to review.

Closes astral-sh/ty#3195.

Closes astral-sh/ty#3196.

@charliermarsh charliermarsh added bug Something isn't working ty Multi-file analysis & type inference labels Apr 17, 2026
@astral-sh-bot
Copy link
Copy Markdown

astral-sh-bot Bot commented Apr 17, 2026

Typing conformance results

No changes detected ✅

Current numbers
The percentage of diagnostics emitted that were expected errors held steady at 89.36%. The percentage of expected errors that received a diagnostic held steady at 85.49%. The number of fully passing files held steady at 88/134.

@astral-sh-bot
Copy link
Copy Markdown

astral-sh-bot Bot commented Apr 17, 2026

Memory usage report

Summary

Project Old New Diff Outcome
trio 117.45MB 117.45MB +0.00% (4.02kB)
flake8 47.81MB 47.81MB -0.00% (1.57kB) ⬇️
sphinx 261.76MB 261.75MB -0.00% (5.01kB) ⬇️
prefect 693.95MB 693.92MB -0.01% (36.39kB) ⬇️

Significant changes

Click to expand detailed breakdown

trio

Name Old New Diff Outcome
Type<'db>::apply_specialization_ 615.80kB 612.43kB -0.55% (3.36kB)
is_redundant_with_impl::interned_arguments 283.42kB 284.97kB +0.55% (1.55kB)
infer_expression_types_impl 6.97MB 6.98MB +0.01% (1.04kB)
is_redundant_with_impl 241.12kB 242.11kB +0.41% (1008.00B)
UnionType 142.73kB 143.66kB +0.65% (944.00B)
all_narrowing_constraints_for_expression 606.11kB 606.81kB +0.12% (720.00B)
all_negative_narrowing_constraints_for_expression 560.01kB 560.71kB +0.13% (720.00B)
loop_header_reachability 129.54kB 130.19kB +0.51% (672.00B)
infer_expression_type_impl 1.31MB 1.31MB +0.05% (672.00B)
infer_scope_types_impl 4.73MB 4.73MB +0.01% (480.00B)
Type<'db>::try_call_dunder_get_ 1.34MB 1.34MB -0.03% (360.00B)
IntersectionType 138.68kB 138.92kB +0.17% (248.00B)
infer_definition_types 7.80MB 7.80MB -0.00% (192.00B)

flake8

Name Old New Diff Outcome
infer_definition_types 1.87MB 1.87MB -0.04% (864.00B) ⬇️
Type<'db>::apply_specialization_ 189.02kB 188.55kB -0.25% (480.00B) ⬇️
StaticClassLiteral<'db>::try_mro_ 309.86kB 309.69kB -0.06% (180.00B) ⬇️
StaticClassLiteral<'db>::try_mro_::interned_arguments 70.73kB 70.66kB -0.10% (72.00B) ⬇️
try_call_bin_op_return_type_impl 6.23kB 6.22kB -0.19% (12.00B) ⬇️

sphinx

Name Old New Diff Outcome
Type<'db>::apply_specialization_ 1.42MB 1.40MB -1.43% (20.80kB) ⬇️
infer_expression_types_impl 20.48MB 20.48MB +0.02% (3.68kB) ⬇️
UnionType 447.06kB 448.70kB +0.37% (1.64kB) ⬇️
is_redundant_with_impl 954.22kB 955.82kB +0.17% (1.59kB) ⬇️
is_redundant_with_impl::interned_arguments 1.12MB 1.13MB +0.13% (1.46kB) ⬇️
infer_expression_type_impl 2.90MB 2.90MB +0.05% (1.39kB) ⬇️
infer_definition_types 23.88MB 23.88MB +0.00% (960.00B) ⬇️
Type<'db>::class_member_with_policy_ 7.63MB 7.63MB +0.01% (852.00B) ⬇️
Type<'db>::expand_eagerly__ 11.67kB 12.42kB +6.46% (772.00B) ⬇️
loop_header_reachability 364.13kB 364.88kB +0.21% (768.00B) ⬇️
Type<'db>::member_lookup_with_policy_ 6.84MB 6.85MB +0.01% (660.00B) ⬇️
infer_scope_types_impl 15.47MB 15.47MB +0.00% (636.00B) ⬇️
UnionType<'db>::from_two_elements_ 100.65kB 101.11kB +0.45% (468.00B) ⬇️
Type<'db>::member_lookup_with_policy_::interned_arguments 2.67MB 2.67MB +0.01% (312.00B) ⬇️
Type<'db>::class_member_with_policy_::interned_arguments 4.03MB 4.04MB +0.01% (312.00B) ⬇️
... 14 more

prefect

Name Old New Diff Outcome
Type<'db>::apply_specialization_ 3.08MB 3.04MB -1.18% (37.12kB) ⬇️
UnionType 900.98kB 906.28kB +0.59% (5.30kB) ⬇️
infer_expression_type_impl 7.59MB 7.58MB -0.06% (4.43kB) ⬇️
infer_definition_types 87.25MB 87.25MB -0.00% (2.73kB) ⬇️
try_call_bin_op_return_type_impl 285.95kB 288.12kB +0.76% (2.18kB) ⬇️
all_negative_narrowing_constraints_for_expression 6.08MB 6.08MB -0.02% (1.20kB) ⬇️
all_narrowing_constraints_for_expression 6.28MB 6.28MB -0.02% (1.17kB) ⬇️
is_redundant_with_impl::interned_arguments 2.16MB 2.16MB +0.05% (1.12kB) ⬇️
infer_expression_types_impl 57.31MB 57.31MB -0.00% (924.00B) ⬇️
try_call_bin_op_return_type_impl::interned_arguments 63.16kB 64.02kB +1.36% (880.00B) ⬇️
Type<'db>::try_call_dunder_get_ 10.83MB 10.83MB +0.01% (752.00B) ⬇️
infer_scope_types_impl 56.67MB 56.67MB +0.00% (516.00B) ⬇️
is_redundant_with_impl 1.91MB 1.91MB +0.02% (444.00B) ⬇️
Type<'db>::class_member_with_policy_ 17.21MB 17.21MB +0.00% (392.00B) ⬇️
when_constraint_set_assignable_to_owned_impl 1.51MB 1.51MB -0.02% (348.00B) ⬇️
... 11 more

@astral-sh-bot
Copy link
Copy Markdown

astral-sh-bot Bot commented Apr 17, 2026

ecosystem-analyzer results

No diagnostic changes detected ✅

Full report with detailed diff (timing results)

@charliermarsh charliermarsh force-pushed the charlie/recursive-type-aliases branch 5 times, most recently from 755d446 to 2721ed3 Compare April 21, 2026 16:38
Comment thread crates/ty_python_semantic/src/types.rs Outdated
Comment thread crates/ty_python_semantic/src/types.rs Outdated
@charliermarsh
Copy link
Copy Markdown
Member Author

We may just want to remove the MaterializationPolarityDetector stuff, since I still can't find a real reproduction.

@charliermarsh
Copy link
Copy Markdown
Member Author

A subset of these are fixed by #24773, so we can wait for that to land first.

@charliermarsh charliermarsh force-pushed the charlie/recursive-type-aliases branch 7 times, most recently from 8d8d48e to 136a861 Compare May 3, 2026 19:21
@charliermarsh charliermarsh marked this pull request as ready for review May 4, 2026 05:41
@charliermarsh
Copy link
Copy Markdown
Member Author

Much of the process here was: fixing the initial issues, then prompting Codex to find another, similar panic that wasn't yet solved, then fixing that, and repeating until there were no more findings. Then, I did an additional few passes to clean up.

@charliermarsh charliermarsh marked this pull request as draft May 4, 2026 15:59
@charliermarsh charliermarsh force-pushed the charlie/recursive-type-aliases branch 2 times, most recently from 5e6c3ab to af9aba1 Compare May 4, 2026 16:28
@charliermarsh charliermarsh force-pushed the charlie/recursive-type-aliases branch 6 times, most recently from cca5571 to 0681c47 Compare May 4, 2026 22:43
@charliermarsh charliermarsh marked this pull request as ready for review May 4, 2026 23:10
@charliermarsh charliermarsh force-pushed the charlie/recursive-type-aliases branch 3 times, most recently from f2a1995 to 7719f4c Compare May 8, 2026 01:20
@charliermarsh charliermarsh force-pushed the charlie/recursive-type-aliases branch from 7719f4c to cac8ea5 Compare May 8, 2026 05:04
@charliermarsh charliermarsh marked this pull request as draft May 8, 2026 05:04
@charliermarsh charliermarsh marked this pull request as ready for review May 8, 2026 05:13
@sharkdp
Copy link
Copy Markdown
Contributor

sharkdp commented May 8, 2026

@mtshiba I know you did some work in this area recently. If you find the time (and if you are interested), would you mind taking a look at the general approach here to see if it aligns with what you had planned? I know that we took a few stabs at trying to fix panics/overflows in this area, so I'm wondering why previous attempts have fallen short and if we're really on the right trajectory here, or if we maybe need an entirely different approach?

@charliermarsh Sorry that I didn't find the time to take a closer look so far

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working ty Multi-file analysis & type inference

Projects

None yet

2 participants