Repository navigation
fix(resolution): what a walk answers doesn't depend on who asked first - #3215
Open
colbymchenry wants to merge 1 commit into
Open
colbymchenry wants to merge 1 commit into
colbymchenry wants to merge 1 commit into
Conversation
Three walks stopped after a few steps and kept what they had: asked from the
top of a chain of type aliases or Lua re-exports, the walk stopped short and
stored a null for the middle of the chain, which the next asker took. The
TypeScript and Lua walks have one successor at each step, so they now follow a
chain to its end, with a visited set to end a cycle. Pascal's can branch, an
alias declared in each arm of an {$IFDEF}, so it walks with a seen set and
keeps only the answer for where it started.
cMacroRename's index grew as source files asked, so a rename in a file another
includes counted only once that file had asked, per worker. Sources something
includes now go in the index up front within the budget, the index is fixed
once built, and any other source's renames count for itself alone.
A config name that isn't a regular file now counts as absent and is never read:
a FIFO would park readFileSync, and #3214 took that to manifests and databases.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Four lookups kept an answer that depended on who asked first. The memo audit made each one flip on a small fixture with the reversed-batch probe. Now a chain of aliases answers the same from any point on it, however long it is, and a C rename answers for the asking file alone.
Root cause
tsAliasedType,luaModuleMember, Pascal'sclassNamed. Each stopped after a few steps and stored what it had, under a key without the depth. Taketype A = B; B = C; C = D; D = E; E = F; class F. Asked fromA, the walk gives up atE, 4 steps down, and stores null forE, so a value typedEgets nothing. Asked fromEfirst,EfindsFandAthen reads that answer and getsF. So one project's calls came and went by which reference resolved first.cMacroRename. The answer is cached per file and name, but readmemo.byName, which grew as each source file first asked. That growth exists because a source file's own#definematters to that file, and reading every source up front is too much. A source another file includes was caught by it by accident: it counted for its includer only once it had asked, on that worker.The fix
tsAliasedTypeluaModuleMemberclassNamed{$IFDEF}, and the first that leads to a class winsA cycle gives nothing in all three, from anywhere on it.
cMacroRename: the index is now fixed once built. Headers go in as before, then the source files something#includes by a source name (impl.c,tables.inc), which reach their includers as a header does. They share the existing 16 MB budget, read after the headers so no header's place in it moves, and it's a per-thread read, outside #3182's main-thread scan. A source past the budget gives its includers no rename, the same in every order. Any other source reaches itself alone, so its renames sit in a per-file set that no other file reads.A config name that isn't a regular file.
moduleConfigEntryread whateverstatSyncfound, so a FIFO under one of these names would parkreadFileSync, and #3214 took that hazard from tsconfig and go.mod to Cargo manifests and compile databases. Such a name now counts as absent and is never read.loadCppIncludeDirsFromCompileDBand the resolver'sdiskContextread the same names and had the same gap, so they get the same check, and a folder namedcompile_commands.jsonno longer hides the one inbuild/. A FIFO can't be made on this machine, so the test puts a folder under both names, and 2 mutations turn it red. It isn't in the CHANGELOG, since #3214 hasn't shipped.Validation
Windows 11.
Both orders.
resolution-order-independence.test.tsasks each pair in both orders, each on a fresh resolver, as #3194's test does: 10 tests, 6 red on main, and the other 4 guard that a cycle names nothing and a file nothing includes renames for itself alone. 8 mutations all turn a test red: each cap put back, each cycle guard removed, the first alias only, included sources unread, own renames unread.Reversed-batch probe, on one thread, each batch resolved last to first:
#include "impl.c"renameA/B against main, every dump compared byte for byte. No edge moved on any of them, so no chain or rename on these repos reaches a different answer:
Cost. 8 interleaved rounds against main, medians, wall s and CPU s:
All within this box's noise, which is wider than 1%. Those rounds ran before the last small tidy of
cMacroRenameand the regular-file check; neither adds work on these repos beyond the stat already made.Suite on this head, with a kernel built from it: 881 files passed, 6 skipped; 11,053 passed, 2 expected fail, 87 skipped, 0 failed; 2 workers, by the memory floor, Vitest 4.1.11.
🤖 Generated with Claude Code