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

Tree-shake parameter defaults (replaces #4498) #4510

Merged
merged 12 commits into from May 27, 2022
Merged

Conversation

lukastaegert
Copy link
Member

@lukastaegert lukastaegert commented May 25, 2022

This PR contains:

  • bugfix
  • feature
  • refactor
  • documentation
  • other

Are tests included?

  • yes (bugfixes and features will not be merged without tests)
  • no

Breaking Changes?

  • yes (breaking changes will not be merged unless absolutely necessary)
  • no

List any relevant issue numbers:
Resolves #4498
Resolves #4503
Resolves #4504
Resolves #4505
Resolves #4506
Resolves #4507

Description

This replaces the previous PR #4498 which has been rolled back. In the previous implementation, I had a hard time finding a way to make the feature "non-optimized by default" which meant an unprecedented amount of different bugs crept in. As it was feeling increasingly like playing whack-a-mole, I decided to revisit the general assumptions and greatly reduced the scope of the feature.
Now, parameter default tree-shaking will only be applied to top-level functions and IIFEs as in those cases, we can be fairly certain we can either track every possible call of a function or know when it is not possible.
In the previous implementation, tree-shaking was also possible for objects and static class methods, but I found there were sneaky bugs because here it is possible to call a function via this, or worse, via super, and those entirely depend on call context.

Still, all examples of the previous PR still work, which is why I will copy them here:

This allows tree-shaking for unnecessary default parameters in functions. This includes arrow functions but does not include nested defaults in destructuring patterns REPL

// input
const foo = (a = 'removed', { b = 'unused but included' } = 'removed') =>
  console.log(a,b);
foo('a', 'b');

// output
const foo = (a, { b = 'unused but included' }) =>
  console.log(a,b);
foo('a', 'b');

While the feature may sound simple at first glance, it was actually quite complex. The main problem is that JavaScript does not make a difference between not passing a parameter to a function and passing the value undefined: In both cases, the default value will be used.

Therefore, there are quite some subtleties involved which in the end I resolved by extending the mechanism I created for omitting unneeded parameters. This mechanism was removing arguments from function calls if the corresponding parameter was unused. Now, it will also see if parameter defaults need to be included. Care was taken to ensure no side effects are omitted REPL

// input
const foo = (a = 'removed because unused') => effect();
foo();

const bar = (a = effect('included because triggered')) => {};
// It does not make a difference if you pass nothing or undefined
bar(undefined);

const baz = (a = effect('removed')) => {};
baz('value');

// output
const foo = (a) => effect();
foo();

const bar = (a = effect('included because triggered')) => {};
// It does not make a difference if you pass nothing or undefined
bar();

const baz = (a) => {};
baz();

It is very important to track the instance where we "lose track" of how a function is used, e.g. because it is exported, passed to an unknown function etc. In those cases, all necessary defaults are included REPL

// input
export const foo = (a = 'included') => console.log(a);

// output
const foo = (a = 'included') => console.log(a);

export { foo };

@github-actions
Copy link

github-actions bot commented May 25, 2022

Thank you for your contribution! ❤️

You can try out this pull request locally by installing Rollup via

npm install rollup/rollup#parameter-defaults

or load it into the REPL:
https://rollupjs.org/repl/?pr=4510

@codecov
Copy link

codecov bot commented May 25, 2022

Codecov Report

Merging #4510 (b5cd46e) into master (e823ede) will increase coverage by 0.03%.
The diff coverage is 98.44%.

@@            Coverage Diff             @@
##           master    #4510      +/-   ##
==========================================
+ Coverage   98.74%   98.78%   +0.03%     
==========================================
  Files         207      208       +1     
  Lines        7357     7425      +68     
  Branches     2084     2118      +34     
==========================================
+ Hits         7265     7335      +70     
- Misses         33       34       +1     
+ Partials       59       56       -3     
Impacted Files Coverage Δ
src/ast/nodes/ArrowFunctionExpression.ts 100.00% <ø> (ø)
src/ast/nodes/AwaitExpression.ts 100.00% <ø> (ø)
src/ast/nodes/shared/MethodTypes.ts 88.88% <ø> (+6.13%) ⬆️
src/ast/nodes/shared/ObjectMember.ts 100.00% <ø> (+10.00%) ⬆️
src/ast/scopes/ClassBodyScope.ts 100.00% <ø> (ø)
src/ast/scopes/FunctionScope.ts 100.00% <ø> (ø)
src/ast/scopes/ParameterScope.ts 100.00% <ø> (ø)
src/ast/values.ts 100.00% <ø> (ø)
src/ast/variables/LocalVariable.ts 93.15% <ø> (ø)
src/ast/nodes/shared/Node.ts 95.83% <76.92%> (-4.17%) ⬇️
... and 37 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update e823ede...b5cd46e. Read the comment docs.

@lukastaegert
Copy link
Member Author

To everyone following this, I decided to roll back the feature for good as it turned out to be far too brittle with far too much overhead for my liking at the moment, see #4520

pos777 pushed a commit to pos777/rollup that referenced this pull request Jun 18, 2022
* Recreate parameter tree-shaking but keep boolean return values

* Properly include parameters when tree-shaking is disabled

Also improve side effect detection for unused parameter defaults

* Make null checks more compact

* Include super class parameter defaults

* Include return value parameter defaults

* Limit feature to top-level functions

* Remove path from deoptimization signature

* Ensure class methods are deoptimized

* Add comment

* Class fields need Node 12

* Improve coverage

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