Skip to content

Fixed a crash caused by circularly-reentrant getEffectsSignature - #63026

Merged
Gabriela Araujo Britto (gabritto) merged 7 commits into
microsoft:mainfrom
Andarist:fix/reentrant-getEffectsSignature
Feb 2, 2026
Merged

Fixed a crash caused by circularly-reentrant getEffectsSignature#63026
Gabriela Araujo Britto (gabritto) merged 7 commits into
microsoft:mainfrom
Andarist:fix/reentrant-getEffectsSignature

Conversation

@Andarist

@AndaristMateusz Burzyński (Andarist) commented Jan 21, 2026

Copy link
Copy Markdown
Contributor

fixes#63015
caused by https://github.com/microsoft/TypeScript/pull/62243/files#r2264931395

With the change above, some of the function-like type-level nodes lostContainerFlags.IsControlFlowContainer. It turns out that, despite them being type-level nodes, it was still important for them to be treated as flow containers. Or rather, it was important for them to get their own detached~ flow node as currentFlow in the bindContainer's branch handling ContainerFlags.IsControlFlowContainer.

Without that flag on them the nodes received .flowNode that was attached to the flow graph of the containing control flow container. Where with the flag they received FlowFlags.Start node that usually no other flow nodes would be attached to, unless one would write an incorrect program with narrowing expressions within type level nodes (those would attach to that FlowFlags.Start as that was set as currentFlow) in situations akin to this one:

typeSomeTypeAlias={[Math.random() ? foo : bar]: string}// error

So all of that caused typeof x within those function-like type-level nodes to get narrowed by the containing container's flow and the code managed to infinitely recurse in the reported code:

unionOfDifferentReturnType1(true);constunionOfDifferentReturnType1: {(a: any): number;}|{(a: number): typeofDate;};

In here, the Date in the typeof query was being narrowed by FlowFlags.Call node but that depends on the annotated type of unionOfDifferentReturnType1 (it depends on getEffectsSignature and that depends on explicitly annotated return types).

It seemed to me it would be possible to create a similar crash in 5.9 and I tried weird things (I added them as tests) but couldn't make it happen. The closest thing I got was this somewhat surprising error:

functiontest4(arg: string|number,whatever: any){if(typeofarg==="string"){b()typeFirst=typeofarg// Type alias 'First' circularly references itself.(2456)typeTest=()=>Firstconstb: Test=whateverreturnb}returnundefined;}

I think this means that type aliases have reentrancy protection already implemented and the reported code crashes because it manages to slip through codepaths that don't have it (anonymous type nodes don't implement pushTypeResolution+popTypeResolution etc).

All of that led me to exploring the inconsistencies around typeof queries, like here:

functiontest1(a: number|string){if(typeofa==="number"){constfn=(arg: typeofa)=>true;returnfn;}return;}test1(0)?.(100);// oktest1(0)?.("");// errorfunctiontest2(a: number|string){if(typeofa==="number"){constfn: {(arg: typeofa): boolean;}=()=>true;returnfn;}return;}test2(0)?.(100);// oktest2(0)?.("");// ok ?

So I figured out, I would prefer to keep their behavior change accidentally implemented in #62243 . It would be nice if they would behave in the same way in the example above. It seemed the easiest to just eagerly set a "zero value" links.effectsSignature before resolving the true value. That's a pretty common pattern used across the codebase.

FWIW, I have also briefly explored disabling control flow narrowing within type-only nodes but that broke a couple of existing explicit tests.

Alternative fixes I have considered:

  • reverting the ContainerFlags.IsControlFlowContainer changes + fine-tuning seenThisKeyword tracking
  • adding an extra codepath to bindContainer, smth like:
if(ContainerFlags.IsFunctionLike){constsaveCurrentFlow=currentFlow;currentFlow=createFlowNode(FlowFlags.Start,/*node*/undefined,/*antecedent*/undefined);bindChildren(node);currentFlow=saveCurrentFlow;}
  • using links.effectsSignature = resolvingEffectsSignature strategy

EDIT:// at the end, I have refactored the PR to choose the first option of"reverting the ContainerFlags.IsControlFlowContainer changes + fine-tuning seenThisKeyword tracking", as requested here

cc Gabriela Araujo Britto (@gabritto)

@ahejlsberg

Copy link
Copy Markdown
Member

I'm somewhat queasy about having to put in stopgap recursion limiter where we previously didn't need it. We use a similar pattern in some places, but it always comes with the downside of possibly observing different results from functions that are supposed to just lazily compute immutable values. My preference would be to restore the original ContainerFlags.IsControlFlowContainer values and get closer to where we were before.

@Andarist

Copy link
Copy Markdown
ContributorAuthor

Thanks for the feedback. I’ll work on the suggested solution

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Looks good except for the (leftover?) change in checker.ts.

Comment threadsrc/compiler/checker.ts Outdated
@github-project-automationgithub-project-automationBot moved this from Not started to Waiting on author in PR BacklogFeb 2, 2026
@gabritto

Copy link
Copy Markdown
Member

TypeScript Bot (@typescript-bot) test it

@typescript-bot

TypeScript Bot (typescript-bot) commented Feb 2, 2026

Copy link
Copy Markdown
Contributor

Starting jobs; this comment will be updated as builds start and complete.

CommandStatusResults
test top400✅ Started✅ Results
user test this✅ Started✅ Results
run dt✅ Started✅ Results
perf test this faster✅ Started👀 Results

@typescript-bot

Copy link
Copy Markdown
Contributor

Gabriela Araujo Britto (@gabritto) Here are the results of running the user tests with tsc comparing main and refs/pull/63026/merge:

There were infrastructure failures potentially unrelated to your change:

  • 1 instance of "Package install failed"
  • 1 instance of "Git clone failed"

Otherwise...

Everything looks good!

@typescript-bot

Copy link
Copy Markdown
Contributor

Hey Gabriela Araujo Britto (@gabritto), the results of running the DT tests are ready.

Everything looks the same!

You can check the log here.

@typescript-bot

Copy link
Copy Markdown
Contributor

Gabriela Araujo Britto (@gabritto)
The results of the perf run you requested are in!

Here they are:

tsc

Comparison Report - baseline..pr
MetricbaselineprDeltaBestWorstp-value
Compiler-Unions - node (v18.15.0, x64)
Errors33~~~p=1.000 n=6
Symbols62,37062,370~~~p=1.000 n=6
Types50,38750,387~~~p=1.000 n=6
Memory used194,994k (± 0.92%)196,232k (± 0.10%)~195,988k196,485kp=0.230 n=6
Parse Time1.60s (± 2.35%)1.59s (± 1.93%)~1.56s1.63sp=0.809 n=6
Bind Time0.92s (± 1.07%)0.90s (± 1.30%)-0.02s (- 2.18%)0.88s0.91sp=0.016 n=6
Check Time11.98s (± 0.45%)11.98s (± 0.21%)~11.95s12.02sp=0.748 n=6
Emit Time3.32s (± 3.80%)3.46s (± 4.33%)~3.23s3.58sp=0.336 n=6
Total Time17.83s (± 0.85%)17.93s (± 1.01%)~17.68s18.14sp=0.575 n=6
angular-1 - node (v18.15.0, x64)
Errors22~~~p=1.000 n=6
Symbols955,823955,823~~~p=1.000 n=6
Types415,853415,853~~~p=1.000 n=6
Memory used1,254,077k (± 0.00%)1,254,958k (± 0.00%)+881k (+ 0.07%)1,254,926k1,255,003kp=0.005 n=6
Parse Time8.01s (± 0.59%)8.02s (± 0.91%)~7.93s8.09sp=0.746 n=6
Bind Time2.39s (± 1.58%)2.38s (± 0.35%)~2.37s2.39sp=1.000 n=6
Check Time38.71s (± 0.34%)38.74s (± 0.50%)~38.51s39.04sp=1.000 n=6
Emit Time17.84s (± 0.86%)17.83s (± 0.69%)~17.61s17.96sp=0.936 n=6
Total Time66.95s (± 0.21%)66.98s (± 0.33%)~66.61s67.27sp=0.689 n=6
mui-docs - node (v18.15.0, x64)
Errors11,43911,439~~~p=1.000 n=6
Symbols2,701,9432,701,943~~~p=1.000 n=6
Types930,574930,574~~~p=1.000 n=6
Memory used3,037,297k (± 0.00%)3,038,463k (± 0.00%)+1,166k (+ 0.04%)3,038,302k3,038,624kp=0.005 n=6
Parse Time10.35s (± 0.44%)10.31s (± 0.25%)~10.28s10.35sp=0.077 n=6
Bind Time2.76s (± 0.61%)2.78s (± 0.78%)~2.76s2.82sp=0.070 n=6
Check Time110.90s (± 0.86%)110.88s (± 1.69%)~109.56s114.62sp=0.575 n=6
Emit Time0.38s (± 1.99%)0.39s (± 2.11%)~0.38s0.40sp=0.120 n=6
Total Time124.39s (± 0.76%)124.36s (± 1.50%)~123.08s128.10sp=0.575 n=6
self-build-src - node (v18.15.0, x64)
Errors00~~~p=1.000 n=6
Symbols1,252,4751,252,487+12 (+ 0.00%)~~p=0.001 n=6
Types259,933259,932-1 (- 0.00%)~~p=0.001 n=6
Memory used2,998,683k (± 9.91%)2,882,938k (±13.05%)~2,396,315k3,127,006kp=0.230 n=6
Parse Time6.63s (± 0.72%)6.66s (± 0.77%)~6.58s6.72sp=0.336 n=6
Bind Time2.24s (± 1.30%)2.22s (± 1.29%)~2.17s2.24sp=0.418 n=6
Check Time42.85s (± 0.74%)42.91s (± 0.39%)~42.74s43.12sp=0.810 n=6
Emit Time3.44s (± 3.31%)3.50s (± 2.76%)~3.32s3.60sp=0.230 n=6
Total Time55.17s (± 0.65%)55.30s (± 0.38%)~55.03s55.51sp=0.471 n=6
self-build-src-public-api - node (v18.15.0, x64)
Errors00~~~p=1.000 n=6
Symbols1,252,4751,252,487+12 (+ 0.00%)~~p=0.001 n=6
Types259,933259,932-1 (- 0.00%)~~p=0.001 n=6
Memory used3,062,042k (± 9.65%)3,189,398k (± 0.03%)🔻+127,356k (+ 4.16%)3,188,465k3,190,556kp=0.005 n=6
Parse Time6.73s (± 1.14%)6.81s (± 0.90%)+0.08s (+ 1.14%)6.69s6.86sp=0.045 n=6
Bind Time2.27s (± 0.65%)2.26s (± 1.55%)~2.23s2.33sp=0.226 n=6
Check Time43.08s (± 0.46%)43.13s (± 0.23%)~43.04s43.32sp=0.810 n=6
Emit Time3.48s (± 0.81%)3.51s (± 1.54%)~3.45s3.59sp=0.423 n=6
Total Time55.56s (± 0.46%)55.70s (± 0.20%)~55.56s55.87sp=0.336 n=6
self-compiler - node (v18.15.0, x64)
Errors00~~~p=1.000 n=6
Symbols264,630264,631+1 (+ 0.00%)~~p=0.001 n=6
Types104,073104,072-1 (- 0.00%)~~p=0.001 n=6
Memory used443,122k (± 0.01%)443,546k (± 0.01%)+424k (+ 0.10%)443,463k443,609kp=0.005 n=6
Parse Time3.50s (± 0.53%)3.50s (± 1.40%)~3.46s3.58sp=0.467 n=6
Bind Time1.39s (± 1.02%)1.37s (± 0.55%)-0.02s (- 1.32%)1.36s1.38sp=0.032 n=6
Check Time19.14s (± 0.50%)19.21s (± 0.23%)~19.16s19.29sp=0.229 n=6
Emit Time1.54s (± 0.68%)1.53s (± 0.96%)~1.51s1.55sp=0.073 n=6
Total Time25.58s (± 0.35%)25.62s (± 0.22%)~25.55s25.67sp=0.376 n=6
ts-pre-modules - node (v18.15.0, x64)
Errors7474~~~p=1.000 n=6
Symbols225,493225,493~~~p=1.000 n=6
Types94,37394,373~~~p=1.000 n=6
Memory used369,928k (± 0.04%)370,216k (± 0.04%)+288k (+ 0.08%)370,088k370,409kp=0.013 n=6
Parse Time2.82s (± 1.05%)2.84s (± 1.23%)~2.78s2.88sp=0.333 n=6
Bind Time1.65s (± 0.91%)1.65s (± 1.11%)~1.62s1.67sp=0.869 n=6
Check Time16.60s (± 0.26%)16.64s (± 0.50%)~16.55s16.78sp=0.420 n=6
Emit Time0.00s0.00s~~~p=1.000 n=6
Total Time21.07s (± 0.34%)21.14s (± 0.43%)~21.02s21.28sp=0.298 n=6
vscode - node (v18.15.0, x64)
Errors1111~~~p=1.000 n=6
Symbols4,190,4794,190,479~~~p=1.000 n=6
Types1,328,0971,328,097~~~p=1.000 n=6
Memory used3,983,933k (± 0.01%)3,985,145k (± 0.00%)+1,211k (+ 0.03%)3,985,044k3,985,256kp=0.005 n=6
Parse Time16.12s (± 0.44%)16.30s (± 2.38%)~16.02s17.07sp=0.689 n=6
Bind Time5.69s (± 1.90%)5.45s (± 0.38%)🟩-0.24s (- 4.16%)5.43s5.48sp=0.010 n=6
Check Time117.77s (± 4.08%)117.84s (± 3.33%)~113.60s123.02sp=0.810 n=6
Emit Time53.46s (± 9.80%)53.67s (± 8.78%)~48.18s60.32sp=0.810 n=6
Total Time193.04s (± 3.30%)193.26s (± 2.72%)~187.94s200.52sp=1.000 n=6
webpack - node (v18.15.0, x64)
Errors4141~~~p=1.000 n=6
Symbols396,269396,269~~~p=1.000 n=6
Types175,363175,363~~~p=1.000 n=6
Memory used548,943k (± 0.02%)549,613k (± 0.01%)+670k (+ 0.12%)549,511k549,700kp=0.005 n=6
Parse Time4.64s (± 0.52%)4.63s (± 0.93%)~4.55s4.67sp=0.628 n=6
Bind Time2.02s (± 1.51%)2.02s (± 1.39%)~1.99s2.06sp=0.871 n=6
Check Time24.18s (± 1.67%)24.23s (± 0.44%)~24.14s24.40sp=0.423 n=6
Emit Time0.01s (±48.94%)0.01s (±109.43%)~0.00s0.01sp=0.282 n=6
Total Time30.84s (± 1.25%)30.87s (± 0.41%)~30.73s31.04sp=0.378 n=6
xstate-main - node (v18.15.0, x64)
Errors3030~~~p=1.000 n=6
Symbols731,941731,941~~~p=1.000 n=6
Types219,279219,279~~~p=1.000 n=6
Memory used634,933k (± 0.01%)635,823k (± 0.01%)+890k (+ 0.14%)635,757k635,903kp=0.005 n=6
Parse Time3.71s (± 0.37%)3.71s (± 0.40%)~3.69s3.73sp=0.743 n=6
Bind Time1.26s (± 0.71%)1.26s~~~p=1.000 n=6
Check Time18.48s (± 0.47%)18.39s (± 0.22%)~18.33s18.45sp=0.078 n=6
Emit Time0.00s0.00s~~~p=1.000 n=6
Total Time23.45s (± 0.36%)23.36s (± 0.22%)~23.28s23.43sp=0.071 n=6
System info unknown
Hosts
  • node (v18.15.0, x64)
Scenarios
  • Compiler-Unions - node (v18.15.0, x64)
  • angular-1 - node (v18.15.0, x64)
  • mui-docs - node (v18.15.0, x64)
  • self-build-src - node (v18.15.0, x64)
  • self-build-src-public-api - node (v18.15.0, x64)
  • self-compiler - node (v18.15.0, x64)
  • ts-pre-modules - node (v18.15.0, x64)
  • vscode - node (v18.15.0, x64)
  • webpack - node (v18.15.0, x64)
  • xstate-main - node (v18.15.0, x64)
BenchmarkNameIterations
Currentpr6
Baselinebaseline6

Developer Information:

Download Benchmarks

@typescript-bot

Copy link
Copy Markdown
Contributor

Gabriela Araujo Britto (@gabritto) Here are the results of running the top 400 repos with tsc comparing main and refs/pull/63026/merge:

Everything looks good!

@github-project-automationgithub-project-automationBot moved this from Waiting on author to Needs merge in PR BacklogFeb 2, 2026
Merged via the queue into microsoft:main with commit b92ec99Feb 2, 2026
33 checks passed
@github-project-automationgithub-project-automationBot moved this from Needs merge to Done in PR BacklogFeb 2, 2026
@microsoftMicrosoft (microsoft) locked as resolved and limited conversation to collaborators Aug 3, 2026
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

For Milestone BugPRs that fix a bug with a specific milestone

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

Regression Crash: RangeError: Maximum call stack size exceeded in isThisInTypeQuery on Nightly

6 participants

@Andarist@ahejlsberg@gabritto@typescript-bot@andrewbranch@jakebailey