Skip to content

[ty] Allow recursive type expansion a limited number of times during type relation checking - #26989

Closed
mtshiba wants to merge 4 commits into
mainfrom
cycle-detector-visit-limit
Closed

[ty] Allow recursive type expansion a limited number of times during type relation checking#26989
mtshiba wants to merge 4 commits into
mainfrom
cycle-detector-visit-limit

Conversation

@mtshiba

@mtshiba mtshiba commented Jul 19, 2026

Copy link
Copy Markdown
Collaborator

Summary

This PR addresses #26503 (comment)

As described in the comment, instead of immediately returning the Cycle state when a same identity is found, we expect the recursive expansion to stabilize by Pending several times. If it does not stabilize even after expanding 10 times, it will be determined as a cycle.

This makes DefinitionReferenceVisitor completely unnecessary. In other words, even if we see signs of a growing pattern, we will try expanding recursive types up to 10 times. At first glance, it might seem better to keep the DefinitionReferenceVisitor guard in place, but doing so would actually cause us to miss patterns that look like growing patterns but ultimately converge in a finite number of steps. For example:

type Left[A, B, C, D] = Left[B, C, D, D]
type Right[A, B, C, D] = Right[B, C, D, D]

Now consider these type relations:

Left[
    int,
    list[int],
    list[list[int]],
    list[list[list[int]]],
]

Right[
    int,
    list[int],
    list[list[int]],
    list[list[list[int]]],
]

CycleDetector observes the increase in specialization as follows. However, this will eventually converge after a finite number of steps. In other words, it is not possible to conclude that "this type relation check will not stop" just from the increasing structure of specialization observed by CycleDetector.

[int, list[int],  list[list[int]], list[list[list[int]]]]
↓
[list[int], list[list[int]], list[list[list[int]]], list[list[list[int]]]]
↓
[list[list[int]], list[list[list[int]]], list[list[list[int]]], list[list[list[int]]]]
↓
[list[list[list[int]]], list[list[list[int]]], list[list[list[int]]], list[list[list[int]]]]
↓
[list[list[list[int]]], list[list[list[int]]], list[list[list[int]]], list[list[list[int]]]] (converged)

Test Plan

mdtest updated

@mtshiba mtshiba added the ty Multi-file analysis & type inference label Jul 19, 2026
@astral-sh-bot

astral-sh-bot Bot commented Jul 19, 2026

Copy link
Copy Markdown

Typing conformance results

No changes detected ✅

Current numbers
The percentage of diagnostics emitted that were expected errors held steady at 96.95%. The percentage of expected errors that received a diagnostic held steady at 92.58%. The number of fully passing files held steady at 104/133.

@astral-sh-bot

astral-sh-bot Bot commented Jul 19, 2026

Copy link
Copy Markdown

Memory usage report

Memory usage unchanged ✅

@astral-sh-bot

astral-sh-bot Bot commented Jul 19, 2026

Copy link
Copy Markdown

ecosystem-analyzer results

No diagnostic changes detected ✅

Flaky changes detected. This PR summary excludes flaky changes; see the HTML report for details.

Full report with detailed diff (timing results)

@mtshiba
mtshiba force-pushed the cycle-detector-visit-limit branch 2 times, most recently from 9591845 to 7d9d11b Compare July 20, 2026 14:35
@mtshiba
mtshiba force-pushed the cycle-detector-visit-limit branch from 7d9d11b to ec031ea Compare August 5, 2026 14:08
@mtshiba mtshiba changed the title [ty] allow limited recursive relation expansion [ty] Allow recursive type expansion a limited number of times during type relation checking Aug 5, 2026
@codspeed-hq

codspeed-hq Bot commented Aug 5, 2026

Copy link
Copy Markdown

Merging this PR will not alter performance

✅ 111 untouched benchmarks
⏩ 84 skipped benchmarks1


Comparing cycle-detector-visit-limit (f507e20) with main (8c30182)

Open in CodSpeed

Footnotes

  1. 84 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

@mtshiba

mtshiba commented Aug 5, 2026

Copy link
Copy Markdown
Collaborator Author

This approach has the undesirable side effect that the union type reduction results depend on the order. Therefore, it cannot be adopted.

type Left12[A, B, C, D, E, F, G, H, I, J, K] = tuple[A, Left12[B, C, D, E, F, G, H, I, J, K, L, None]]
type Right12[A, B, C, D, E, F, G, H, I, J, K] = tuple[A, Right12[B, C, D, E, F, G, H, I, J, K, L, None]]

type Source = Left12[int, int, int, int, int, int, int, int, int, int, int, int]
type RightAfterTwo = Right12[int, int, int, int, int, int, int, int, int, int, None, None]
type Shortcut = tuple[int, tuple[int, RightAfterTwo]]
# Shortcut is equivalent to LongPath
type LongPath = Right12[int, int, int, int, int, int, int, int, int, int, int, int]

static_assert(is_subtype_of(Source, Shortcut))
static_assert(is_subtype_of(Source, Shortcut | LongPath))
static_assert(is_subtype_of(Source, LongPath | Shortcut))  # failure!

Instead, it is necessary to redesign DefinitionReferenceVisitor so that the detector does not immediately return Cycle for recursive-but-saturate patterns.

@mtshiba mtshiba closed this Aug 5, 2026
@mtshiba
mtshiba deleted the cycle-detector-visit-limit branch August 5, 2026 17:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ty Multi-file analysis & type inference

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant