Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Perform usage analysis on refenced binding aliases in function signatures. #55683

Merged
merged 6 commits into from Dec 13, 2023

Conversation

dragomirtitian
Copy link
Contributor

@dragomirtitian dragomirtitian commented Sep 8, 2023

Fix #55654 by marking binding aliases that need to be preserved.

This is done in a two step process:

  1. We first remove all existing aliases (assuming they will not be used as this is the common case)
  2. After the signature types are processed in the declaration emit, if any alias is marked as being used (should be rare), then we go though the parameters again to add back the referenced aliases.

Fixes #55654

@andrewbranch
Copy link
Member

Maybe I missed it somewhere, but I feel like I still haven’t heard an explanation as to why #55654 is worth spending 1500 lines and extra CPU cycles to fix and not just a harmless aesthetic quirk.

@jakebailey
Copy link
Member

The focus of this is isolatedDelarations where that emitter is currently not emitting renamed type variables and so the two differ where tsc's current emit is the suboptimal one.

@dragomirtitian
Copy link
Contributor Author

@andrewbranch Most of the 1.5k are tests 😅. The actual implementation is about 350 lines, most of which is just a switch to call the appropriate node factory to recreate the node.

As for CPU cycles, that was one of my worries too. As far as I can tell the only extra CPU cycles that are spent in the common case of not using the type alias in the signature is a call to isBindingElement(declaration) in the checker and a lookup on empty map in the declaration emitter, so hopefully no real impact.

While it's not a design goal to 100% reproduce TS declaration files with an external emitter, it would be ideal to limit cases where the two differ. This would be one example of a difference taht we can eliminate without. Always keeping the alias in is also a way to achieve this.

@andrewbranch
Copy link
Member

I guess I’m surprised that the more complicated behavior is the one we’re pursuing if the goal is consistency between tsc and a third-party isolatedDeclarations emitter.

@dragomirtitian
Copy link
Contributor Author

@andrewbranch Most of the machinery to track references is needed for tracking the usage of variables and imports anyway. This extra analysis for binding elements shouldn't make a third-party emitter much more complicated IMO.

Someone obviously cared enough to implement the removal of aliases, even if it did have some bugs, fixing the bugs seems the better way forward. They are just unused implementation details that should probably not be exposed unless absolutely necessary.

@typescript-bot typescript-bot added For Backlog Bug PRs that fix a backlog bug and removed For Uncommitted Bug PR for untriaged, rejected, closed or missing bug labels Sep 14, 2023
@jakebailey
Copy link
Member

This PR theoretically looks good to me, though the repetition in the d.ts emitter makes me feel like there must be a better way still that I'm missing here. Maybe @weswigham or @rbuckton know a better trick.

@sandersn sandersn added this to Not started in PR Backlog Sep 21, 2023
@sandersn sandersn moved this from Not started to Waiting on reviewers in PR Backlog Sep 21, 2023
Copy link
Member

@weswigham weswigham left a comment

Choose a reason for hiding this comment

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

Does this handle a binding element merged with a var well? eg,

function a({e1: t1}: {e1: {}})/*: typeof t1*/ {
    var t1: {} = 1;
    return t1;
}

@dragomirtitian
Copy link
Contributor Author

Does this handle a binding element merged with a var well? eg,

function a({e1: t1}: {e1: {}})/*: typeof t1*/ {
    var t1: {} = 1;
    return t1;
}

That is already an error: Return type of exported function has or is using private name 't1'. This PR does not attempt to change that. My only goal here was to not keep the binding alias if it was not used in declarations.

@jakebailey
Copy link
Member

So, as far as I can tell, this looks good to me, though I'm not a super fan of the repetition I don't think there's much of a way to do better. @weswigham do you have any other feedback here? I'm hoping to get the list of differences for isolatedDeclarations down to as small as possible (I think think this is the only one left??)

Copy link
Member

@weswigham weswigham left a comment

Choose a reason for hiding this comment

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

Hmm, this looks fine to me, implementation-wise. I think I'd have a minor preference for merging the nodes from bindingElementToMakeVisible and aliasesToMakeVisible into a single nodesToMakeVisible list, but it's not strictly required if nobody else feels strongly about it.

PR Backlog automation moved this from Waiting on reviewers to Needs merge Dec 12, 2023
@jakebailey
Copy link
Member

Hmm, this looks fine to me, implementation-wise. I think I'd have a minor preference for merging the nodes from bindingElementToMakeVisible and aliasesToMakeVisible into a single nodesToMakeVisible list, but it's not strictly required if nobody else feels strongly about it.

As in, make it nodesToMakeVisible?: (LateVisibilityPaintedStatement | BindingElement)[]; and then check the kind?

@weswigham
Copy link
Member

As in, make it nodesToMakeVisible?: (LateVisibilityPaintedStatement | BindingElement)[]; and then check the kind?

Aye.

@jakebailey
Copy link
Member

I think the awkward thing is that there's only one bindingElementToMakeVisible... unless this is an indicator that the PR is missing something because there could be more than one like aliases?

@dragomirtitian
Copy link
Contributor Author

Hmm, this looks fine to me, implementation-wise. I think I'd have a minor preference for merging the nodes from bindingElementToMakeVisible and aliasesToMakeVisible into a single nodesToMakeVisible list, but it's not strictly required if nobody else feels strongly about it.

I could merge those, the problem is that will change the common case to something like:

if (symbolAccessibilityResult.aliasesToMakeVisible) {
    if(!lateMarkedStatements) {
        lateMarkedStatements = []
    }
    for (const ref of symbolAccessibilityResult.aliasesToMakeVisible) {
        if(ref.kind === SyntaxKind.BindingElement) {
            const bindingElement = ref;
            const parameter = findAncestor(bindingElement, isParameter);
            Debug.assert(parameter !== undefined);
            const parent = getOriginalNode(parameter.parent);
            let aliases = usedBindingElementAliases.get(parent);
            if (!aliases) {
                usedBindingElementAliases.set(parent, aliases = new Map());
            }
            aliases.set(getOriginalNode(bindingElement), bindingElement.name);
        } else {
            pushIfUnique(lateMarkedStatements, ref);
        }
    }
}

Which would mean some extra work filtering out the binding elements first time lateMarkedStatements is assigned as well as using pushIfUnique that first time. Previously it could just do if(!lateMarkedStatements) lateMarkedStatements = symbolAccessibilityResult.aliasesToMakeVisible). Not sure if this significant. I would guess not.

I think the awkward thing is that there's only one bindingElementToMakeVisible... unless this is an indicator that the PR is missing something because there could be more than one like aliases?

@jakebailey I don't think there is any way to have multiple binding elements to make visible if the code is valid. You can get it if with duplicate identifiers but I don't think that is a case we need to think to deeply about as it's a syntax error (function foo({a: v, b: v}: {a: number, b: string}): typeof v)

@jakebailey
Copy link
Member

I think I'd personally prefer to leave it as is, in that case. I don't feel strongly, but it does seem like an extra loop to find one thing...

@jakebailey
Copy link
Member

I'll quick run the extended suite, though I expect it to not really do anything since it can't detect emit changes.

@typescript-bot test top200
@typescript-bot user test this
@typescript-bot run dt
@typescript-bot perf test this

@typescript-bot
Copy link
Collaborator

typescript-bot commented Dec 13, 2023

Heya @jakebailey, I've started to run the regular perf test suite on this PR at ca275ec. You can monitor the build here.

Update: The results are in!

@typescript-bot
Copy link
Collaborator

typescript-bot commented Dec 13, 2023

Heya @jakebailey, I've started to run the diff-based user code test suite on this PR at ca275ec. You can monitor the build here.

Update: The results are in!

@typescript-bot
Copy link
Collaborator

typescript-bot commented Dec 13, 2023

Heya @jakebailey, I've started to run the parallelized Definitely Typed test suite on this PR at ca275ec. You can monitor the build here.

Update: The results are in!

@typescript-bot
Copy link
Collaborator

typescript-bot commented Dec 13, 2023

Heya @jakebailey, I've started to run the diff-based top-repos suite on this PR at ca275ec. You can monitor the build here.

Update: The results are in!

@typescript-bot
Copy link
Collaborator

@jakebailey
The results of the perf run you requested are in!

Here they are:

Compiler

Comparison Report - baseline..pr
Metric baseline pr Delta Best Worst p-value
Angular - node (v18.15.0, x64)
Memory used 295,401k (± 0.01%) 295,417k (± 0.01%) ~ 295,403k 295,454k p=0.298 n=6
Parse Time 2.65s (± 0.19%) 2.65s (± 0.24%) ~ 2.64s 2.66s p=0.386 n=6
Bind Time 0.82s (± 0.77%) 0.82s (± 0.00%) ~ 0.82s 0.82s p=1.000 n=6
Check Time 8.14s (± 0.39%) 8.15s (± 0.25%) ~ 8.11s 8.17s p=1.000 n=6
Emit Time 7.11s (± 0.26%) 7.12s (± 0.51%) ~ 7.09s 7.19s p=0.459 n=6
Total Time 18.71s (± 0.18%) 18.74s (± 0.17%) ~ 18.71s 18.80s p=0.332 n=6
Compiler-Unions - node (v18.15.0, x64)
Memory used 193,355k (± 1.54%) 194,851k (± 1.51%) ~ 191,428k 197,542k p=0.128 n=6
Parse Time 1.36s (± 1.35%) 1.35s (± 0.73%) ~ 1.33s 1.36s p=0.303 n=6
Bind Time 0.72s (± 0.57%) 0.72s (± 0.00%) ~ 0.72s 0.72s p=0.405 n=6
Check Time 9.23s (± 0.55%) 9.26s (± 0.40%) ~ 9.19s 9.29s p=0.747 n=6
Emit Time 2.62s (± 1.07%) 2.64s (± 0.88%) ~ 2.61s 2.67s p=0.418 n=6
Total Time 13.94s (± 0.43%) 13.96s (± 0.32%) ~ 13.88s 14.00s p=0.630 n=6
Monaco - node (v18.15.0, x64)
Memory used 347,375k (± 0.01%) 347,379k (± 0.01%) ~ 347,350k 347,411k p=0.810 n=6
Parse Time 2.46s (± 0.17%) 2.46s (± 0.61%) ~ 2.44s 2.48s p=0.498 n=6
Bind Time 0.93s (± 0.56%) 0.92s (± 0.56%) ~ 0.92s 0.93s p=0.311 n=6
Check Time 6.91s (± 0.57%) 6.89s (± 0.56%) ~ 6.85s 6.96s p=0.195 n=6
Emit Time 4.06s (± 1.24%) 4.05s (± 0.49%) ~ 4.04s 4.09s p=0.677 n=6
Total Time 14.36s (± 0.50%) 14.33s (± 0.35%) ~ 14.27s 14.42s p=0.748 n=6
TFS - node (v18.15.0, x64)
Memory used 302,638k (± 0.00%) 302,646k (± 0.00%) ~ 302,618k 302,655k p=0.470 n=6
Parse Time 2.01s (± 1.08%) 2.00s (± 0.71%) ~ 1.98s 2.02s p=0.685 n=6
Bind Time 1.00s (± 1.36%) 1.01s (± 1.20%) ~ 0.99s 1.02s p=0.676 n=6
Check Time 6.29s (± 0.33%) 6.27s (± 0.56%) ~ 6.21s 6.31s p=0.293 n=6
Emit Time 3.59s (± 0.49%) 3.58s (± 0.27%) ~ 3.56s 3.59s p=0.345 n=6
Total Time 12.89s (± 0.12%) 12.86s (± 0.19%) -0.03s (- 0.25%) 12.83s 12.89s p=0.043 n=6
material-ui - node (v18.15.0, x64)
Memory used 506,798k (± 0.01%) 506,795k (± 0.01%) ~ 506,746k 506,816k p=0.936 n=6
Parse Time 2.57s (± 0.43%) 2.58s (± 0.84%) ~ 2.57s 2.62s p=0.531 n=6
Bind Time 0.99s (± 1.52%) 0.99s (± 1.64%) ~ 0.97s 1.01s p=0.934 n=6
Check Time 16.93s (± 0.50%) 16.91s (± 0.53%) ~ 16.77s 17.01s p=0.689 n=6
Emit Time 0.00s (± 0.00%) 0.00s (± 0.00%) ~ 0.00s 0.00s p=1.000 n=6
Total Time 20.50s (± 0.45%) 20.48s (± 0.47%) ~ 20.35s 20.61s p=0.873 n=6
xstate - node (v18.15.0, x64)
Memory used 512,748k (± 0.01%) 512,802k (± 0.01%) ~ 512,723k 512,871k p=0.066 n=6
Parse Time 3.27s (± 0.12%) 3.27s (± 0.32%) ~ 3.25s 3.28s p=0.390 n=6
Bind Time 1.53s (± 0.34%) 1.53s (± 0.34%) ~ 1.53s 1.54s p=1.000 n=6
Check Time 2.81s (± 0.53%) 2.81s (± 0.49%) ~ 2.79s 2.82s p=0.514 n=6
Emit Time 0.07s (± 0.00%) 0.07s (± 0.00%) ~ 0.07s 0.07s p=1.000 n=6
Total Time 7.69s (± 0.21%) 7.68s (± 0.05%) -0.01s (- 0.17%) 7.67s 7.68s p=0.042 n=6
System info unknown
Hosts
  • node (v18.15.0, x64)
Scenarios
  • Angular - node (v18.15.0, x64)
  • Compiler-Unions - node (v18.15.0, x64)
  • Monaco - node (v18.15.0, x64)
  • TFS - node (v18.15.0, x64)
  • material-ui - node (v18.15.0, x64)
  • xstate - node (v18.15.0, x64)
Benchmark Name Iterations
Current pr 6
Baseline baseline 6

tsserver

Comparison Report - baseline..pr
Metric baseline pr Delta Best Worst p-value
Compiler-UnionsTSServer - node (v18.15.0, x64)
Req 1 - updateOpen 2,348ms (± 0.77%) 2,349ms (± 0.63%) ~ 2,328ms 2,364ms p=0.810 n=6
Req 2 - geterr 5,427ms (± 1.32%) 5,419ms (± 1.54%) ~ 5,343ms 5,546ms p=0.689 n=6
Req 3 - references 325ms (± 1.17%) 324ms (± 0.93%) ~ 320ms 327ms p=0.686 n=6
Req 4 - navto 277ms (± 1.20%) 277ms (± 1.18%) ~ 273ms 281ms p=0.745 n=6
Req 5 - completionInfo count 1,356 (± 0.00%) 1,356 (± 0.00%) ~ 1,356 1,356 p=1.000 n=6
Req 5 - completionInfo 87ms (± 8.02%) 89ms (± 5.20%) ~ 83ms 93ms p=0.935 n=6
CompilerTSServer - node (v18.15.0, x64)
Req 1 - updateOpen 2,465ms (± 1.01%) 2,482ms (± 0.51%) ~ 2,463ms 2,498ms p=0.298 n=6
Req 2 - geterr 4,135ms (± 1.89%) 4,139ms (± 2.20%) ~ 4,048ms 4,227ms p=1.000 n=6
Req 3 - references 342ms (± 1.61%) 341ms (± 1.78%) ~ 335ms 347ms p=0.808 n=6
Req 4 - navto 286ms (± 1.14%) 287ms (± 1.29%) ~ 283ms 292ms p=0.623 n=6
Req 5 - completionInfo count 1,518 (± 0.00%) 1,518 (± 0.00%) ~ 1,518 1,518 p=1.000 n=6
Req 5 - completionInfo 83ms (± 8.37%) 83ms (± 8.16%) ~ 76ms 89ms p=0.870 n=6
xstateTSServer - node (v18.15.0, x64)
Req 1 - updateOpen 2,605ms (± 0.74%) 2,608ms (± 0.40%) ~ 2,593ms 2,621ms p=0.936 n=6
Req 2 - geterr 1,684ms (± 2.31%) 1,707ms (± 2.21%) ~ 1,656ms 1,734ms p=0.092 n=6
Req 3 - references 120ms (± 7.21%) 114ms (± 8.23%) ~ 102ms 123ms p=0.168 n=6
Req 4 - navto 365ms (± 0.33%) 365ms (± 0.32%) ~ 364ms 367ms p=0.563 n=6
Req 5 - completionInfo count 2,073 (± 0.00%) 2,073 (± 0.00%) ~ 2,073 2,073 p=1.000 n=6
Req 5 - completionInfo 309ms (± 1.73%) 306ms (± 3.00%) ~ 289ms 314ms p=0.747 n=6
System info unknown
Hosts
  • node (v18.15.0, x64)
Scenarios
  • CompilerTSServer - node (v18.15.0, x64)
  • Compiler-UnionsTSServer - node (v18.15.0, x64)
  • xstateTSServer - node (v18.15.0, x64)
Benchmark Name Iterations
Current pr 6
Baseline baseline 6

Startup

Comparison Report - baseline..pr
Metric baseline pr Delta Best Worst p-value
tsc-startup - node (v18.15.0, x64)
Execution time 152.93ms (± 0.20%) 152.81ms (± 0.19%) -0.12ms (- 0.08%) 151.75ms 155.91ms p=0.000 n=600
tsserver-startup - node (v18.15.0, x64)
Execution time 228.20ms (± 0.17%) 228.10ms (± 0.17%) -0.11ms (- 0.05%) 226.73ms 232.47ms p=0.001 n=600
tsserverlibrary-startup - node (v18.15.0, x64)
Execution time 229.81ms (± 0.19%) 229.74ms (± 0.19%) ~ 228.08ms 235.35ms p=0.068 n=600
typescript-startup - node (v18.15.0, x64)
Execution time 229.36ms (± 0.18%) 229.38ms (± 0.18%) ~ 227.71ms 234.62ms p=0.751 n=600
System info unknown
Hosts
  • node (v18.15.0, x64)
Scenarios
  • tsc-startup - node (v18.15.0, x64)
  • tsserver-startup - node (v18.15.0, x64)
  • tsserverlibrary-startup - node (v18.15.0, x64)
  • typescript-startup - node (v18.15.0, x64)
Benchmark Name Iterations
Current pr 6
Baseline baseline 6

Developer Information:

Download Benchmarks

@typescript-bot
Copy link
Collaborator

@jakebailey Here are the results of running the user test suite comparing main and refs/pull/55683/merge:

There were infrastructure failures potentially unrelated to your change:

  • 1 instance of "Package install failed"

Otherwise...

Something interesting changed - please have a look.

Details

puppeteer

packages/browsers/test/src/tsconfig.json

@typescript-bot
Copy link
Collaborator

Hey @jakebailey, the results of running the DT tests are ready.
Everything looks the same!
You can check the log here.

@typescript-bot
Copy link
Collaborator

@jakebailey Here are the results of running the top-repos suite comparing main and refs/pull/55683/merge:

Everything looks good!

@jakebailey jakebailey merged commit b527b90 into microsoft:main Dec 13, 2023
19 checks passed
PR Backlog automation moved this from Needs merge to Done Dec 13, 2023
c0sta pushed a commit to c0sta/TypeScript that referenced this pull request Dec 20, 2023
…ures. (microsoft#55683)

Co-authored-by: Jake Bailey <5341706+jakebailey@users.noreply.github.com>
@jakebailey
Copy link
Member

Just to note it, this is being reverted in #57020.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
For Backlog Bug PRs that fix a backlog bug
Projects
PR Backlog
  
Done
Development

Successfully merging this pull request may close these issues.

Binding element alias is kept even though it is unused in declaration files
5 participants