Skip to content

Don't ignore all generic self tail calls when collecting the return type of a function - #63999

Open
Mateusz Burzyński (Andarist) wants to merge 2 commits into
microsoft:mainfrom
Andarist:fix-63990
Open

Don't ignore all generic self tail calls when collecting the return type of a function#63999
Mateusz Burzyński (Andarist) wants to merge 2 commits into
microsoft:mainfrom
Andarist:fix-63990

Conversation

@Andarist

Copy link
Copy Markdown
Contributor

fixes #63990 (a bug introduced with #53995 )

This PR avoids ignoring recursive self calls when the arguments list is not assignable to the parameter list of the signature declaration.

So this errors:

const f = <T>(a: T, p: number) => {
    if (!p) return a;
    return f(p, p - 1);
};

But this continues to work:

function h<T>(value: T, count: number) {
    if (!count) return value;
    return h(value, count - 1);
}

It also ignores generic calls with type argument lists because that can lead to other instantiations of the signature declaration.

Copilot AI balanced review requested due to automatic review settings August 25, 2026 09:53
@github-project-automation github-project-automation Bot moved this to Not started in PR Backlog Aug 25, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Tightens generic recursive return-type inference so only same-instantiation tail calls are ignored.

Changes:

  • Checks recursive arguments against the current generic signature.
  • Adds regression coverage and generated baselines.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
tsc/internal/checker/checker.go Refines recursive-call inference handling.
tsc/testdata/tests/cases/compiler/genericRecursiveFunctionReturn.ts Adds regression scenarios.
tsc/testdata/baselines/reference/compiler/genericRecursiveFunctionReturn.errors.txt Records expected diagnostics.
tsc/testdata/baselines/reference/compiler/genericRecursiveFunctionReturn.types Records inferred types.
tsc/testdata/baselines/reference/compiler/genericRecursiveFunctionReturn.symbols Records symbols.
tsc/testdata/baselines/reference/compiler/genericRecursiveFunctionReturn.js Records emitted JavaScript.

💡 Add a code-review agent skill for context-aware, tailored reviews. Learn more in the docs.

Comment thread tsc/internal/checker/checker.go Outdated
@RyanCavanaugh

Copy link
Copy Markdown
Member

TypeScript Bot (@typescript-bot) test it

@typescript-automation

typescript-automation Bot commented Aug 25, 2026

Copy link
Copy Markdown

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

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

@RyanCavanaugh

Copy link
Copy Markdown
Member

when the arguments list is not assignable to the parameter list of the signature declaration

What's an intuition for why this is the correct rule?

@typescript-automation

Copy link
Copy Markdown

Ryan Cavanaugh (@RyanCavanaugh) Here are the results of running the user tests with tsc comparing main and refs/pull/63999/merge:

There were infrastructure failures potentially unrelated to your change:

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

Otherwise...

Everything looks good!

@typescript-automation

Copy link
Copy Markdown

Ryan Cavanaugh (@RyanCavanaugh)
The results of the perf run you requested are in!

Here they are:

tsc

Comparison Report - baseline..pr
Metric baseline pr Delta Best Worst p-value
Compiler-Unions - native
Errors 41 41 ~ ~ ~ p=1.000 n=12
Symbols 115,480 115,480 ~ ~ ~ p=1.000 n=12
Types 96,945 96,945 ~ ~ ~ p=1.000 n=12
Memory Used 148,598k (± 0.44%) 148,947k (± 0.49%) ~ 147,206k 151,072k p=0.478 n=12
Memory Allocs 2,271,289 (± 0.01%) 2,271,356 (± 0.01%) ~ 2,271,045 2,272,304 p=0.932 n=12
Config Time 0.000s 0.000s ~ ~ ~ p=1.000 n=12
Parse Time 0.051s (± 2.68%) 0.052s (± 2.53%) +0.002s (+ 3.62%) 0.049s 0.056s p=0.042 n=12
Bind Time 0.016s (±12.44%) 0.015s (±10.32%) ~ 0.013s 0.020s p=0.381 n=12
Check Time 0.594s (± 0.95%) 0.599s (± 0.94%) ~ 0.586s 0.612s p=0.164 n=12
Emit Time 0.330s (± 1.38%) 0.333s (± 2.10%) ~ 0.315s 0.352s p=0.485 n=12
Total Time 0.997s (± 0.62%) 1.006s (± 0.90%) ~ 0.990s 1.033s p=0.131 n=12
angular-1 - native
Errors 3 3 ~ ~ ~ p=1.000 n=12
Symbols 846,939 (± 0.06%) 846,888 (± 0.08%) ~ 845,015 848,107 p=0.977 n=12
Types 249,859 (± 0.00%) 249,859 (± 0.00%) ~ 249,856 249,870 p=0.381 n=12
Memory Used 793,470k (± 0.06%) 793,201k (± 0.04%) ~ 792,455k 794,104k p=0.478 n=12
Memory Allocs 12,959,768 (± 0.07%) 12,963,741 (± 0.12%) ~ 12,950,176 13,017,020 p=0.843 n=12
Config Time 0.020s 0.020s (± 3.57%) ~ 0.020s 0.024s p=1.000 n=12
Parse Time 0.349s (± 1.98%) 0.345s (± 4.09%) ~ 0.312s 0.388s p=0.723 n=12
Bind Time 0.077s (±11.09%) 0.097s (±18.99%) 🔻+0.020s (+25.49%) 0.073s 0.152s p=0.010 n=12
Check Time 0s 0s ~ ~ ~ p=1.000 n=12
Emit Time 1.951s (± 0.68%) 2.035s (± 1.46%) 🔻+0.085s (+ 4.34%) 1.974s 2.102s p=0.000 n=12
Total Time 2.414s (± 0.65%) 2.521s (± 1.80%) 🔻+0.107s (+ 4.43%) 2.423s 2.610s p=0.000 n=12
mui-docs - native
Errors 11,276 (± 0.04%) 11,272 (± 0.05%) ~ 11,247 11,282 p=0.256 n=12
Symbols 4,456,324 4,456,324 ~ ~ ~ p=1.000 n=12
Types 1,669,869 1,669,869 ~ ~ ~ p=1.000 n=12
Memory Used 5,405,086k (± 0.03%) 5,404,335k (± 0.03%) ~ 5,400,834k 5,411,836k p=0.478 n=12
Memory Allocs 50,709,606 (± 0.03%) 50,690,782 (± 0.03%) ~ 50,651,767 50,746,249 p=0.101 n=12
Config Time 0.019s 0.019s ~ ~ ~ p=1.000 n=12
Parse Time 0.684s (± 2.06%) 0.685s (± 1.56%) ~ 0.648s 0.710s p=0.967 n=12
Bind Time 0.002s (±12.64%) 0.003s (±28.35%) ~ 0.002s 0.006s p=0.730 n=12
Check Time 20.053s (± 0.11%) 19.942s (± 0.10%) -0.111s (- 0.55%) 19.902s 20.009s p=0.000 n=12
Emit Time 0.516s (± 0.12%) 0.518s (± 0.24%) +0.002s (+ 0.39%) 0.515s 0.523s p=0.001 n=12
Total Time 22.147s (± 0.15%) 22.070s (± 0.13%) -0.078s (- 0.35%) 21.995s 22.143s p=0.002 n=12
strada-build-src - native
Errors 0 0 ~ ~ ~ p=1.000 n=12
Symbols 1,394,339 1,394,339 ~ ~ ~ p=1.000 n=12
Types 443,465 443,465 ~ ~ ~ p=1.000 n=12
Memory Used 1,645,209k (± 0.20%) 1,648,134k (± 0.26%) ~ 1,638,539k 1,658,171k p=0.291 n=12
Memory Allocs 97,061,639 (± 0.03%) 97,080,241 (± 0.04%) ~ 97,004,612 97,230,246 p=0.713 n=12
Config Time 0.006s (±16.00%) 0.006s (±17.24%) ~ 0.003s 0.008s p=0.465 n=12
Parse Time 0.240s (± 1.97%) 0.240s (± 2.05%) ~ 0.230s 0.252s p=0.898 n=12
Bind Time 0.000s 0.000s ~ ~ ~ p=1.000 n=12
Check Time 2.149s (± 0.40%) 2.150s (± 0.44%) ~ 2.131s 2.187s p=0.876 n=12
Emit Time 0.324s (± 6.25%) 0.354s (± 3.94%) 🔻+0.030s (+ 9.37%) 0.308s 0.378s p=0.015 n=12
Total Time 29.666s (± 0.42%) 29.893s (± 0.68%) ~ 29.491s 30.589s p=0.068 n=12
strada-compiler - native
Errors 0 0 ~ ~ ~ p=1.000 n=12
Symbols 337,586 337,586 ~ ~ ~ p=1.000 n=12
Types 198,869 198,869 ~ ~ ~ p=1.000 n=12
Memory Used 319,504k (± 0.04%) 319,494k (± 0.04%) ~ 319,070k 319,857k p=0.887 n=12
Memory Allocs 4,673,092 (± 0.01%) 4,673,082 (± 0.01%) ~ 4,671,231 4,674,406 p=1.000 n=12
Config Time 0.001s 0.001s ~ ~ ~ p=1.000 n=12
Parse Time 0.113s (± 4.83%) 0.118s (± 3.63%) ~ 0.107s 0.128s p=0.123 n=12
Bind Time 0.000s 0.000s ~ ~ ~ p=1.000 n=12
Check Time 1.042s (± 0.51%) 1.045s (± 0.85%) ~ 1.031s 1.085s p=0.876 n=12
Emit Time 0.140s (±11.05%) 0.152s (± 8.57%) ~ 0.102s 0.176s p=0.235 n=12
Total Time 1.347s (± 1.03%) 1.366s (± 1.12%) ~ 1.328s 1.418s p=0.088 n=12
ts-pre-modules - native
Errors 87 87 ~ ~ ~ p=1.000 n=12
Symbols 305,317 305,317 ~ ~ ~ p=1.000 n=12
Types 181,984 181,984 ~ ~ ~ p=1.000 n=12
Memory Used 277,286k (± 0.01%) 277,274k (± 0.01%) ~ 277,156k 277,315k p=0.478 n=12
Memory Allocs 1,642,277 (± 0.02%) 1,642,276 (± 0.01%) ~ 1,641,881 1,642,917 p=0.843 n=12
Config Time 0.000s (±146.91%) 0.000s (±113.79%) ~ 0.000s 0.001s p=1.000 n=12
Parse Time 0.098s (± 3.86%) 0.100s (± 3.79%) ~ 0.089s 0.108s p=0.468 n=12
Bind Time 0.042s (± 8.69%) 0.041s (±14.15%) ~ 0.027s 0.052s p=0.989 n=12
Check Time 0.822s (± 0.69%) 0.828s (± 0.95%) ~ 0.808s 0.858s p=0.311 n=12
Emit Time 0.000s 0.000s ~ ~ ~ p=1.000 n=12
Total Time 0.977s (± 0.68%) 0.982s (± 1.07%) ~ 0.960s 1.009s p=0.599 n=12
vscode - native
Errors 347 347 ~ ~ ~ p=1.000 n=12
Symbols 7,934,940 7,934,940 ~ ~ ~ p=1.000 n=12
Types 2,734,507 2,734,507 ~ ~ ~ p=1.000 n=12
Memory Used 5,356,456k (± 0.01%) 5,355,814k (± 0.01%) ~ 5,354,307k 5,357,881k p=0.178 n=12
Memory Allocs 41,369,638 (± 0.01%) 41,372,148 (± 0.03%) ~ 41,356,822 41,400,974 p=0.713 n=12
Config Time 0.063s (± 0.51%) 0.063s (± 0.58%) ~ 0.062s 0.064s p=0.856 n=12
Parse Time 1.632s (± 2.46%) 1.629s (± 2.31%) ~ 1.491s 1.696s p=0.898 n=12
Bind Time 0.362s (±13.45%) 0.361s (±12.79%) ~ 0.336s 0.594s p=0.783 n=12
Check Time 9.961s (± 1.15%) 10.005s (± 1.08%) ~ 9.462s 10.099s p=0.147 n=12
Emit Time 3.166s (± 7.32%) 3.158s (± 7.79%) ~ 2.987s 4.393s p=0.486 n=12
Total Time 15.274s (± 1.04%) 15.305s (± 1.10%) ~ 15.114s 16.115s p=0.744 n=12
webpack - native
Errors 530 530 ~ ~ ~ p=1.000 n=12
Symbols 843,220 843,220 ~ ~ ~ p=1.000 n=12
Types 378,778 378,778 ~ ~ ~ p=1.000 n=12
Memory Used 647,461k (± 0.02%) 647,234k (± 0.02%) -227k (- 0.04%) 646,923k 647,697k p=0.039 n=12
Memory Allocs 4,286,283 (± 0.01%) 4,287,428 (± 0.04%) ~ 4,284,887 4,295,597 p=0.291 n=12
Config Time 0.008s 0.008s (± 5.88%) ~ 0.008s 0.010s p=0.478 n=12
Parse Time 0.209s (± 2.46%) 0.211s (± 2.29%) ~ 0.202s 0.228s p=0.967 n=12
Bind Time 0.045s (±13.86%) 0.048s (±19.99%) ~ 0.038s 0.091s p=0.986 n=12
Check Time 1.285s (± 0.74%) 1.303s (± 2.36%) ~ 1.257s 1.443s p=0.370 n=12
Emit Time 0.000s 0.000s ~ ~ ~ p=1.000 n=12
Total Time 1.572s (± 0.82%) 1.596s (± 2.45%) ~ 1.542s 1.775s p=0.418 n=12
xstate-main - native
Errors 0 0 ~ ~ ~ p=1.000 n=12
Symbols 1,022,508 1,022,508 ~ ~ ~ p=1.000 n=12
Types 376,977 376,977 ~ ~ ~ p=1.000 n=12
Memory Used 603,512k (± 0.01%) 603,499k (± 0.01%) ~ 603,359k 603,602k p=0.468 n=12
Memory Allocs 4,755,524 (± 0.05%) 4,754,275 (± 0.04%) ~ 4,749,433 4,760,987 p=0.443 n=12
Config Time 0.002s 0.002s ~ ~ ~ p=1.000 n=12
Parse Time 0.113s (± 2.63%) 0.115s (± 3.62%) ~ 0.107s 0.128s p=0.485 n=12
Bind Time 0.038s (±16.17%) 0.033s (±14.63%) ~ 0.026s 0.049s p=0.143 n=12
Check Time 0.854s (± 0.60%) 0.853s (± 1.11%) ~ 0.839s 0.888s p=0.339 n=12
Emit Time 0.000s 0.000s ~ ~ ~ p=1.000 n=12
Total Time 1.017s (± 0.83%) 1.012s (± 1.14%) ~ 0.988s 1.050s p=0.523 n=12
System info unknown
Hosts
  • native
Scenarios
  • Compiler-Unions - native
  • angular-1 - native
  • mui-docs - native
  • strada-build-src - native
  • strada-compiler - native
  • ts-pre-modules - native
  • vscode - native
  • webpack - native
  • xstate-main - native
Benchmark Name Iterations
Current pr 12
Baseline baseline 12

Developer Information:

Download Benchmarks

@typescript-automation

Copy link
Copy Markdown

Ryan Cavanaugh (@RyanCavanaugh) Here are the results of running the top 400 repos with tsc comparing main and refs/pull/63999/merge:

Everything looks good!

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

Labels

None yet

Projects

Status: Not started

Development

Successfully merging this pull request may close these issues.

Generic recursive function infers incorrect return type

3 participants