Skip to content

Commit

Permalink
[compiler] Prune all unused array destructure items during DCE (#31619)
Browse files Browse the repository at this point in the history
We didn't originally support holes within array patterns, so DCE was
only able to prune unused items from the end of an array pattern. Now
that we support holes we can replace any unused item with a hole, and
then just prune the items to the last identifier/spread entry.

Note: this was motivated by finding useState where either the state or
setState go unused — both are strong indications that you're violating
the rules in some way. By DCE-ing the unused portions of the useState
destructuring we can easily check if you're ignoring either value.

closes #31603 

This is a redo of that PR not using ghstack
  • Loading branch information
josephsavona authored Nov 22, 2024
1 parent aba370f commit eee5ca2
Show file tree
Hide file tree
Showing 12 changed files with 27 additions and 31 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
*/

import {
ArrayPattern,
BlockId,
HIRFunction,
Identifier,
Expand Down Expand Up @@ -184,29 +183,28 @@ function rewriteInstruction(instr: Instruction, state: State): void {
switch (instr.value.lvalue.pattern.kind) {
case 'ArrayPattern': {
/*
* For arrays, we can only eliminate unused items from the end of the array,
* so we iterate from the end and break once we find a used item. Note that
* we already know at least one item is used, from the pruneableValue check.
* For arrays, we can prune items prior to the end by replacing
* them with a hole. Items at the end can simply be dropped.
*/
let nextItems: ArrayPattern['items'] | null = null;
const originalItems = instr.value.lvalue.pattern.items;
for (let i = originalItems.length - 1; i >= 0; i--) {
const item = originalItems[i];
let lastEntryIndex = 0;
const items = instr.value.lvalue.pattern.items;
for (let i = 0; i < items.length; i++) {
const item = items[i];
if (item.kind === 'Identifier') {
if (state.isIdOrNameUsed(item.identifier)) {
nextItems = originalItems.slice(0, i + 1);
break;
if (!state.isIdOrNameUsed(item.identifier)) {
items[i] = {kind: 'Hole'};
} else {
lastEntryIndex = i;
}
} else if (item.kind === 'Spread') {
if (state.isIdOrNameUsed(item.place.identifier)) {
nextItems = originalItems.slice(0, i + 1);
break;
if (!state.isIdOrNameUsed(item.place.identifier)) {
items[i] = {kind: 'Hole'};
} else {
lastEntryIndex = i;
}
}
}
if (nextItems !== null) {
instr.value.lvalue.pattern.items = nextItems;
}
items.length = lastEntryIndex + 1;
break;
}
case 'ObjectPattern': {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ function component() {
import { c as _c } from "react/compiler-runtime";
function component() {
const $ = _c(1);
const [x, setX] = useState(0);
const [, setX] = useState(0);
let t0;
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
const handler = (v) => setX(v);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,7 @@ function Component(props) {
}
let d;
if ($[2] !== props.c) {
const [c, ...t0] = props.c;
d = t0;
[, ...d] = props.c;
$[2] = props.c;
$[3] = d;
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ function Component(props) {
import { c as _c } from "react/compiler-runtime";
function Component(props) {
const $ = _c(2);
const [value, setValue] = useState(null);
const [, setValue] = useState(null);
let t0;
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
t0 = (e) => setValue((value_0) => value_0 + e.target.value);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ import { useState } from "react";

function Component(props) {
const $ = _c(1);
const [_state, setState] = useState();
const [, setState] = useState();
let t0;
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
const a = () => b();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import { useCallback, useTransition } from "react";

function useFoo() {
const $ = _c(1);
const [t, start] = useTransition();
const [, start] = useTransition();
let t0;
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
t0 = () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ function Component(props) {
const $ = _c(5);
React.useContext(FooContext);
const ref = React.useRef();
const [x, setX] = React.useState(false);
const [, setX] = React.useState(false);
let t0;
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
t0 = () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,8 @@ import { useState } from "react";

function Component(props) {
const $ = _c(5);
const [x, setX] = useState(false);
const [y, setY] = useState(false);
const [, setX] = useState(false);
const [, setY] = useState(false);
let setState;
if (props.cond) {
setState = setX;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,7 @@ function Component(props) {
const { buttons } = props;
let nonPrimaryButtons;
if ($[0] !== buttons) {
const [primaryButton, ...t0] = buttons;
nonPrimaryButtons = t0;
[, ...nonPrimaryButtons] = buttons;
$[0] = buttons;
$[1] = nonPrimaryButtons;
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export const FIXTURE_ENTRYPOINT = {

```javascript
function foo(props) {
const [x, unused, y] = props.a;
const [x, , y] = props.a;
return x + y;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ import { useActionState } from "react";

function Component() {
const $ = _c(1);
const [actionState, dispatchAction] = useActionState();
const [, dispatchAction] = useActionState();
let t0;
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
const onSubmitAction = () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ import { useReducer } from "react";

function f() {
const $ = _c(1);
const [state, dispatch] = useReducer();
const [, dispatch] = useReducer();
let t0;
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
const onClick = () => {
Expand Down

0 comments on commit eee5ca2

Please sign in to comment.