Skip to content

Port TypeScript#62676: Fix slow sync iteration types spoiling cached async iteration types#2728

Merged
jakebailey merged 2 commits intomainfrom
copilot/fix-slow-sync-iteration-types
Feb 18, 2026
Merged

Port TypeScript#62676: Fix slow sync iteration types spoiling cached async iteration types#2728
jakebailey merged 2 commits intomainfrom
copilot/fix-slow-sync-iteration-types

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Feb 10, 2026

When a type implements both Iterable<T_Sync> and AsyncIterable<T_Async>, the sync slow resolution path could cache results before the async slow path ran, causing for await loops to incorrectly yield the sync type parameter instead of the async one.

type Stream<T_Sync, T_Async> = Iterable<T_Sync> & AsyncIterable<T_Async>;

async function loop(stream: Stream<A, B>) {
  for await (const b of stream) {} // b was incorrectly typed as A instead of B
}
  • Move getIterationTypesOfIterableSlow calls inside their respective fast-check blocks so async slow resolution always completes before sync slow resolution runs, preventing cache poisoning

💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.

…hed value for async ones

Move getIterationTypesOfIterableSlow calls inside their respective
fast-check blocks to prevent sync slow iteration types from being
resolved and cached before async slow types, which was causing
incorrect async iteration types.

Co-authored-by: jakebailey <5341706+jakebailey@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix issue with slow sync iteration types Port TypeScript#62676: Fix slow sync iteration types spoiling cached async iteration types Feb 10, 2026
Copilot AI requested a review from jakebailey February 10, 2026 22:12
@jakebailey
Copy link
Copy Markdown
Member

Hm, this does nothing. Maybe we avoid the problem entirely now?

@jakebailey
Copy link
Copy Markdown
Member

microsoft/TypeScript#62676

Probably worth pulling, just because this probably is an ordering thing we now just so happen to avoid in this case, but not others

@jakebailey jakebailey marked this pull request as ready for review February 13, 2026 00:50
Copilot AI review requested due to automatic review settings February 13, 2026 00:50
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Ports TypeScript#62676 to the Go checker to ensure for await...of prefers AsyncIterable iteration types over sync fallbacks, avoiding incorrect cached results when a type supports both sync and async iteration.

Changes:

  • Reorders getIterationTypesOfIterableWorker resolution so async slow-path resolution completes before any sync-path fallback.
  • Shares a single diags slice across the async/sync slow paths for related diagnostic info.

Copy link
Copy Markdown
Member

@DanielRosenwasser DanielRosenwasser left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No test?

@jakebailey
Copy link
Copy Markdown
Member

jakebailey commented Feb 17, 2026

See above; this is type order dependent. I don't think I can effectively come up with something that walks this in just the right order to cause the problem again without a lot of effort...

@DanielRosenwasser
Copy link
Copy Markdown
Member

DanielRosenwasser commented Feb 17, 2026

Probably worth pulling, just because this probably is an ordering thing we now just so happen to avoid in this case, but not others

But I don't understand how it's union-order dependent, but

  1. Had no diffs with Strada
  2. Doesn't introduce diffs with Strada

I also don't know which union it is depending on - this seems to be more about intersections, right?

@jakebailey
Copy link
Copy Markdown
Member

@copilot come up with a test that fails in the Go code before this fix (revert the change locally and keep testing, restore afterward), based on the upstream PR.

Work on this for a while, but if you can't find something, you can give up and say so.

Copy link
Copy Markdown
Contributor Author

Copilot AI commented Feb 17, 2026

@copilot come up with a test that fails in the Go code before this fix (revert the change locally and keep testing, restore afterward), based on the upstream PR.

Work on this for a while, but if ...

I spent a while trying to construct a failing test and wasn't able to find one. Here's why:

The original TS bug is caused by type-level caching via setCachedIterationTypes/getCachedIterationTypes, which caches iteration types directly on the Type object under keys like "iterationTypesOfIterable" and "iterationTypesOfAsyncIterable". The sync slow path would call setCachedIterationTypes(type, resolver.iterableCacheKey, ...) and cache sync iteration types under the sync key. Then when the async slow path ran, it could find stale cached types on the type from a previous sync-only resolution (e.g., a for...of call before a for await...of call on the same type).

The Go code never ported this type-level caching mechanism. Instead, it uses a single centralized iterationTypesCache map keyed by (typeId, use & CacheFlags). Since for...of (AllowsSync|ForOf) and for await...of (AllowsSync|AllowsAsync|ForOf) produce different cache keys, they never share cached results. The getIterationTypesOfIterableFast and getIterationTypesOfIterableSlow functions in Go are pure — they don't read or write any per-type cache.

Because neither the fast nor slow paths in Go have side effects that could affect each other, the ordering change from this PR is a no-op for all current code paths. I verified this by reverting the fix, running the upstream forAwaitForIntersection1 test, and regenerating baselines — the output is identical with and without the fix.

The structural change is still correct to port for parity with TS, but I cannot produce a test that fails before the fix in the current Go codebase.

@DanielRosenwasser
Copy link
Copy Markdown
Member

DanielRosenwasser commented Feb 18, 2026

So is there no test diff removal because we haven't updated the submodule? Therefore, there's nothing being tested?

@jakebailey
Copy link
Copy Markdown
Member

No, the test exists, it just doesn't have a diff because the differing caching system prevented it entirely.

So now it's just down to whether or not the code style is better / if it should match. Which, I think it should.

@jakebailey jakebailey added this pull request to the merge queue Feb 18, 2026
Merged via the queue into main with commit 7e8d282 Feb 18, 2026
27 checks passed
@jakebailey jakebailey deleted the copilot/fix-slow-sync-iteration-types branch February 18, 2026 00:24
Copilot AI added a commit that referenced this pull request Feb 25, 2026
…async iteration types (#2728)

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: jakebailey <5341706+jakebailey@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants