Partial support for update expressions
Adds limited support for UpdateExpressions (`x++`). We now support the postfix form (`x++` ok, `++x` is a todo) and only when the argument is an identifier. We can relax these restrictions with more work, but this PR should be sufficient for the examples we've seen so far.
Joe Savona committed
Feb 2, 2023 at 07:53 UTC
de929fcc40a8a975e11d839746d7b55b0e36b1b1
7 files changed
+142
-4
compiler/forget/src/HIR/BuildHIR.ts
+49
@@ -1405,6 +1405,55 @@ function lowerExpression(
1405
loc: exprLoc,
1406
};
1407
}
1408
+ case "UpdateExpression": {
1409
+ let expr = exprPath as NodePath<t.UpdateExpression>;
1410
+ const argument = expr.get("argument");
1411
+ if (!argument.isIdentifier()) {
1412
+ builder.errors.push({
1413
+ reason: `(BuildHIR::lowerExpression) Handle UpdateExpression with ${argument.type} argument`,
1414
+ severity: ErrorSeverity.Todo,
1415
+ nodePath: exprPath,
1416
+ });
1417
+ return { kind: "UnsupportedNode", node: exprNode, loc: exprLoc };
1418
+ }
1419
+ if (expr.node.prefix) {
1420
+ builder.errors.push({
1421
+ reason: `(BuildHIR::lowerExpression) Handle prefix UpdateExpression`,
1422
+ severity: ErrorSeverity.Todo,
1423
+ nodePath: exprPath,
1424
+ });
1425
+ return { kind: "UnsupportedNode", node: exprNode, loc: exprLoc };
1426
+ }
1427
+ const temp = buildTemporaryPlace(
1428
+ builder,
1429
+ expr.node.loc ?? GeneratedSource
1430
+ );
1431
+ builder.push({
1432
+ id: makeInstructionId(0),
1433
+ lvalue: { place: { ...temp }, kind: InstructionKind.Const },
1434
+ value: {
1435
+ kind: "Primitive",
1436
+ value: 1,
1437
+ loc: expr.node.loc ?? GeneratedSource,
1438
+ },
1439
+ loc: expr.node.loc ?? GeneratedSource,
1440
+ });
1441
+ const identifier = argument as NodePath<t.Identifier>;
1442
+ const place = lowerExpressionToPlace(builder, identifier);
1443
+ builder.push({
1444
+ id: makeInstructionId(0),
1445
+ lvalue: { place: { ...place }, kind: InstructionKind.Reassign },
1446
+ value: {
1447
+ kind: "BinaryExpression",
1448
+ operator: expr.node.operator === "++" ? "+" : "-",
1449
+ left: { ...place },
1450
+ right: { ...temp },
1451
+ loc: exprLoc,
1452
+ },
1453
+ loc: exprLoc,
1454
+ });
1455
+ return place;
1456
+ }
1457
default: {
1458
builder.errors.push({
1459
reason: `(BuildHIR::lowerExpression) Handle ${exprPath.type} expressions`,
compiler/forget/src/__tests__/fixtures/hir/error.todo-kitchensink.expect.md
+44
-2
@@ -52,6 +52,12 @@ function foo([a, b], { c, d, e = "e" }, f = "f", ...args) {
52
}
53
54
do {} while (i < 3);
55
+
56
+ let updateIdentifier = 0;
57
+ --updateIdentifier;
58
+ ++updateIdentifier;
59
+ updateIdentifier.y++;
60
+ updateIdentifier.y--;
61
}
62
63
```
@@ -327,8 +333,44 @@ function foo([a, b], { c, d, e = "e" }, f = "f", ...args) {
333
49 |
334
> 50 | do {} while (i < 3);
335
| ^^^^^^^^^^^^^^^^^^^^
330
- 51 | }
331
- 52 |
336
+ 51 |
337
+ 52 | let updateIdentifier = 0;
338
+ 53 | --updateIdentifier;
339
+
340
+[ReactForget] TodoError: (BuildHIR::lowerExpression) Handle prefix UpdateExpression
341
+ 51 |
342
+ 52 | let updateIdentifier = 0;
343
+> 53 | --updateIdentifier;
344
+ | ^^^^^^^^^^^^^^^^^^
345
+ 54 | ++updateIdentifier;
346
+ 55 | updateIdentifier.y++;
347
+ 56 | updateIdentifier.y--;
348
+
349
+[ReactForget] TodoError: (BuildHIR::lowerExpression) Handle prefix UpdateExpression
350
+ 52 | let updateIdentifier = 0;
351
+ 53 | --updateIdentifier;
352
+> 54 | ++updateIdentifier;
353
+ | ^^^^^^^^^^^^^^^^^^
354
+ 55 | updateIdentifier.y++;
355
+ 56 | updateIdentifier.y--;
356
+ 57 | }
357
+
358
+[ReactForget] TodoError: (BuildHIR::lowerExpression) Handle UpdateExpression with MemberExpression argument
359
+ 53 | --updateIdentifier;
360
+ 54 | ++updateIdentifier;
361
+> 55 | updateIdentifier.y++;
362
+ | ^^^^^^^^^^^^^^^^^^^^
363
+ 56 | updateIdentifier.y--;
364
+ 57 | }
365
+ 58 |
366
+
367
+[ReactForget] TodoError: (BuildHIR::lowerExpression) Handle UpdateExpression with MemberExpression argument
368
+ 54 | ++updateIdentifier;
369
+ 55 | updateIdentifier.y++;
370
+> 56 | updateIdentifier.y--;
371
+ | ^^^^^^^^^^^^^^^^^^^^
372
+ 57 | }
373
+ 58 |
374
```
375
376
\ No newline at end of file
compiler/forget/src/__tests__/fixtures/hir/error.todo-kitchensink.js
+6
@@ -48,4 +48,10 @@ function foo([a, b], { c, d, e = "e" }, f = "f", ...args) {
48
}
49
50
do {} while (i < 3);
51
+
52
+ let updateIdentifier = 0;
53
+ --updateIdentifier;
54
+ ++updateIdentifier;
55
+ updateIdentifier.y++;
56
+ updateIdentifier.y--;
57
}
compiler/forget/src/__tests__/fixtures/hir/ssa-for.expect.md
+1
-1
@@ -4,7 +4,7 @@
4
```javascript
5
function foo() {
6
let x = 1;
7
- for (let i = 0; i < 10; i += 1) {
7
+ for (let i = 0; i < 10; i++) {
8
x += 1;
9
}
10
return x;
compiler/forget/src/__tests__/fixtures/hir/ssa-for.js
+1
-1
@@ -1,6 +1,6 @@
1
function foo() {
2
let x = 1;
3
- for (let i = 0; i < 10; i += 1) {
3
+ for (let i = 0; i < 10; i++) {
4
x += 1;
5
}
6
return x;
compiler/forget/src/__tests__/fixtures/hir/update-expression.expect.md
new
+35
@@ -0,0 +1,35 @@
1
+
2
+## Input
3
+
4
+```javascript
5
+function foo(props) {
6
+ let x = props.x;
7
+ let y = x++;
8
+ let z = x--;
9
+ return { x, y, z };
10
+}
11
+
12
+```
13
+
14
+## Code
15
+
16
+```javascript
17
+function foo(props) {
18
+ const $ = React.useMemoCache();
19
+ const x = props.x;
20
+ const x$0 = x + 1;
21
+ const y = x$0;
22
+ const x$1 = x$0 - 1;
23
+ const z = x$1;
24
+ let t0;
25
+ if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
26
+ t0 = { x: x$1, y: y, z: z };
27
+ $[0] = t0;
28
+ } else {
29
+ t0 = $[0];
30
+ }
31
+ return t0;
32
+}
33
+
34
+```
35
+
\ No newline at end of file
compiler/forget/src/__tests__/fixtures/hir/update-expression.js
new
+6
@@ -0,0 +1,6 @@
1
+function foo(props) {
2
+ let x = props.x;
3
+ let y = x++;
4
+ let z = x--;
5
+ return { x, y, z };
6
+}