[valueblocks] Convert logical terminal to ReactiveValue
Implements the conversion from LogicalTerminal into a ReactiveLogicalValue (and ReactiveSequenveValue if necessary). The implementation is a bit rough, i clean it up in subsequent PRs which revealed parts of the logic that could be shared w ternaries.
Joe Savona committed
Jan 31, 2023 at 13:39 UTC
d4acc7efa6aa10d03cc2eda97345da12a60c6cbe
8 files changed
+261
-73
compiler/forget/src/HIR/HIR.ts
+11
-1
@@ -93,7 +93,10 @@ export type ReactiveInstruction = {
93
loc: SourceLocation;
94
};
95
96
-export type ReactiveValue = InstructionValue | ReactiveLogicalValue;
96
+export type ReactiveValue =
97
+ | InstructionValue
98
+ | ReactiveLogicalValue
99
+ | ReactiveSequenceValue;
100
101
export type ReactiveLogicalValue = {
102
kind: "LogicalExpression";
@@ -103,6 +106,13 @@ export type ReactiveLogicalValue = {
106
loc: SourceLocation;
107
};
108
109
+export type ReactiveSequenceValue = {
110
+ kind: "SequenceExpression";
111
+ instructions: Array<ReactiveInstruction>;
112
+ value: ReactiveValue;
113
+ loc: SourceLocation;
114
+};
115
+
116
export type ReactiveTerminal =
117
| ReactiveBreakTerminal
118
| ReactiveContinueTerminal
compiler/forget/src/HIR/PrintHIR.ts
+10
@@ -339,6 +339,16 @@ export function printInstructionValue(instrValue: ReactiveValue): string {
339
} ${printInstructionValue(instrValue.right)}`;
340
break;
341
}
342
+ case "SequenceExpression": {
343
+ value = [
344
+ `Sequence`,
345
+ ...instrValue.instructions.map(
346
+ (instr) => ` ${printInstruction(instr)}`
347
+ ),
348
+ ` ${printInstructionValue(instrValue.value)}`,
349
+ ].join("\n");
350
+ break;
351
+ }
352
default: {
353
assertExhaustive(
354
instrValue,
compiler/forget/src/ReactiveScopes/BuildReactiveFunction.ts
+136
-1
@@ -19,11 +19,24 @@ import {
19
} from "../HIR";
20
import {
21
HIRFunction,
22
+ Instruction,
23
+ InstructionKind,
24
ReactiveBreakTerminal,
25
ReactiveContinueTerminal,
26
ReactiveFunction,
27
+ ReactiveInstruction,
28
+ ReactiveLogicalValue,
29
+ ReactiveSequenceValue,
30
ReactiveTerminalStatement,
31
+ ReactiveValue,
32
+ Terminal,
33
} from "../HIR/HIR";
34
+import {
35
+ printInstructionValue,
36
+ printPlace,
37
+ printTerminal,
38
+} from "../HIR/PrintHIR";
39
+import { mapInstructionOperands } from "../HIR/visitors";
40
import { assertExhaustive } from "../Utils/utils";
41
42
/**
@@ -407,7 +420,16 @@ class Driver {
420
const scheduleId = this.cx.schedule(fallthroughId, "if");
421
scheduleIds.push(scheduleId);
422
410
- this.visitBlock(this.cx.ir.blocks.get(terminal.test)!, blockValue);
423
+ const { place, value } = this.visitValueTerminal(terminal);
424
+ blockValue.push({
425
+ kind: "instruction",
426
+ instruction: {
427
+ id: terminal.id,
428
+ lvalue: { kind: InstructionKind.Const, place },
429
+ value,
430
+ loc: terminal.loc,
431
+ },
432
+ });
433
434
this.cx.unschedule(scheduleId);
435
this.visitBlock(this.cx.ir.blocks.get(fallthroughId)!, blockValue);
@@ -447,6 +469,119 @@ class Driver {
469
}
470
}
471
472
+ visitValueTerminal(terminal: Terminal): {
473
+ value: ReactiveValue;
474
+ place: Place;
475
+ fallthrough: BlockId;
476
+ } {
477
+ switch (terminal.kind) {
478
+ case "logical": {
479
+ let testBlock: BasicBlock;
480
+ let leftValue: ReactiveValue | null = null;
481
+ let leftPlace: Place | null = null;
482
+ const defaultTestBlock = this.cx.ir.blocks.get(terminal.test)!;
483
+ if (defaultTestBlock.terminal.kind === "branch") {
484
+ testBlock = defaultTestBlock;
485
+ } else {
486
+ const leftResult = this.visitValueTerminal(defaultTestBlock.terminal);
487
+ testBlock = this.cx.ir.blocks.get(leftResult.fallthrough)!;
488
+ leftValue = leftResult.value;
489
+ leftPlace = leftResult.place;
490
+ }
491
+
492
+ invariant(
493
+ testBlock.terminal.kind === "branch",
494
+ "Unexpected terminal kind '%s' for logical test block",
495
+ testBlock.terminal.kind
496
+ );
497
+ const leftInstructions: Array<ReactiveInstruction> =
498
+ testBlock.instructions;
499
+ const leftBlock = this.cx.ir.blocks.get(testBlock.terminal.consequent)!;
500
+ leftInstructions.push(...leftBlock.instructions);
501
+ // TODO: If right block ends in a value terminal, recursively process with visitValueTerminal
502
+ // similar to handling for the compound lhs case.
503
+ const rightBlock = this.cx.ir.blocks.get(testBlock.terminal.alternate)!;
504
+ const rightInstructions: Array<ReactiveInstruction> =
505
+ rightBlock.instructions;
506
+ const place = leftInstructions.at(-1)!.lvalue!.place;
507
+ invariant(
508
+ place.identifier ===
509
+ rightInstructions.at(-1)!.lvalue!.place.identifier,
510
+ "Expected both branches of a logical expression to store to the same temporary"
511
+ );
512
+ if (leftPlace !== null) {
513
+ leftInstructions.forEach((instr) =>
514
+ mapInstructionOperands(instr as Instruction, (place) => {
515
+ return place.identifier === leftPlace!.identifier
516
+ ? (leftValue! as Place)
517
+ : place;
518
+ })
519
+ );
520
+ rightInstructions.forEach((instr) =>
521
+ mapInstructionOperands(instr as Instruction, (place) => {
522
+ return place.identifier === leftPlace!.identifier
523
+ ? (leftValue! as Place)
524
+ : place;
525
+ })
526
+ );
527
+ }
528
+
529
+ let left: ReactiveValue;
530
+ if (leftInstructions.length === 1) {
531
+ left = leftInstructions[0]!.value;
532
+ } else {
533
+ const sequence: ReactiveSequenceValue = {
534
+ kind: "SequenceExpression",
535
+ instructions: leftInstructions.slice(0, -1),
536
+ value: leftInstructions.at(-1)!.value,
537
+ loc: terminal.loc,
538
+ };
539
+ left = sequence;
540
+ }
541
+ let right: ReactiveValue;
542
+ if (rightInstructions.length === 1) {
543
+ right = rightInstructions[0]!.value;
544
+ } else {
545
+ const sequence: ReactiveSequenceValue = {
546
+ kind: "SequenceExpression",
547
+ instructions: rightInstructions.slice(0, -1),
548
+ value: rightInstructions.at(-1)!.value,
549
+ loc: terminal.loc,
550
+ };
551
+ right = sequence;
552
+ }
553
+ const value: ReactiveLogicalValue = {
554
+ kind: "LogicalExpression",
555
+ operator: terminal.operator,
556
+ left,
557
+ right,
558
+ loc: terminal.loc,
559
+ };
560
+ console.log(
561
+ printTerminal(terminal) +
562
+ " testBlock=" +
563
+ testBlock.id +
564
+ " " +
565
+ printPlace(place) +
566
+ "=" +
567
+ printInstructionValue(value)
568
+ );
569
+ return {
570
+ place: { ...place },
571
+ value,
572
+ fallthrough: terminal.fallthrough,
573
+ };
574
+ }
575
+ default: {
576
+ invariant(
577
+ false,
578
+ "Unexpected value block terminal kind '%s'",
579
+ terminal.kind
580
+ );
581
+ }
582
+ }
583
+ }
584
+
585
visitInitBlock(parent: ReactiveBlock, block: BasicBlock): ReactiveValueBlock {
586
const initBlock: ReactiveValueBlock = {
587
kind: "value-block",
compiler/forget/src/ReactiveScopes/CodegenReactiveFunction.ts
+79
-53
@@ -93,7 +93,7 @@ function codegenBlock(cx: Context, block: ReactiveBlock): t.BlockStatement {
93
const statement = codegenInstructionNullable(
94
cx,
95
item.instruction,
96
- codegenInstructionValue(cx.temp, item.instruction.value)
96
+ codegenInstructionValue(cx, item.instruction.value)
97
);
98
if (statement !== null) {
99
statements.push(statement);
@@ -272,7 +272,7 @@ function codegenTerminal(
272
}
273
case "if": {
274
return t.ifStatement(
275
- codegenPlace(cx.temp, terminal.test),
275
+ codegenPlace(cx, terminal.test),
276
codegenBlock(cx, terminal.consequent),
277
terminal.alternate !== null
278
? codegenBlock(cx, terminal.alternate)
@@ -281,22 +281,22 @@ function codegenTerminal(
281
}
282
case "return": {
283
return t.returnStatement(
284
- terminal.value !== null ? codegenPlace(cx.temp, terminal.value) : null
284
+ terminal.value !== null ? codegenPlace(cx, terminal.value) : null
285
);
286
}
287
case "switch": {
288
return t.switchStatement(
289
- codegenPlace(cx.temp, terminal.test),
289
+ codegenPlace(cx, terminal.test),
290
terminal.cases.map((case_) => {
291
const test =
292
- case_.test !== null ? codegenPlace(cx.temp, case_.test) : null;
292
+ case_.test !== null ? codegenPlace(cx, case_.test) : null;
293
const block = codegenBlock(cx, case_.block!);
294
return t.switchCase(test, [block]);
295
})
296
);
297
}
298
case "throw": {
299
- return t.throwStatement(codegenPlace(cx.temp, terminal.value));
299
+ return t.throwStatement(codegenPlace(cx, terminal.value));
300
}
301
case "while": {
302
const test = codegenValueBlock(cx, terminal.test);
@@ -319,7 +319,7 @@ function codegenInstructionNullable(
319
let statement;
320
if (instr.lvalue !== null && cx.declared(instr.lvalue.place.identifier)) {
321
statement = codegenInstruction(
322
- cx.temp,
322
+ cx,
323
{
324
...instr,
325
lvalue: {
@@ -330,7 +330,7 @@ function codegenInstructionNullable(
330
value
331
);
332
} else {
333
- statement = codegenInstruction(cx.temp, instr, value);
333
+ statement = codegenInstruction(cx, instr, value);
334
}
335
if (statement.type === "EmptyStatement") {
336
return null;
@@ -348,7 +348,7 @@ function codegenForInit(
348
body.length === 0,
349
"Expected for init block to produce only temporaries"
350
);
351
- return codegenInstructionValue(cx.temp, init.last.value);
351
+ return codegenInstructionValue(cx, init.last.value);
352
} else {
353
invariant(
354
body.length === 1,
@@ -376,7 +376,7 @@ function codegenValueBlock(
376
}
377
});
378
if (block.last !== null) {
379
- const value = codegenInstructionValue(cx.temp, block.last.value);
379
+ const value = codegenInstructionValue(cx, block.last.value);
380
expressions.push(value);
381
}
382
invariant(
@@ -431,6 +431,7 @@ const createVariableDeclaration = withLoc(t.variableDeclaration);
431
const createWhileStatement = withLoc(t.whileStatement);
432
const createTaggedTemplateExpression = withLoc(t.taggedTemplateExpression);
433
const createLogicalExpression = withLoc(t.logicalExpression);
434
+const createSequenceExpression = withLoc(t.sequenceExpression);
435
436
type Temporaries = Map<IdentifierId, t.Expression>;
437
@@ -439,7 +440,7 @@ function codegenLabel(id: BlockId): string {
440
}
441
442
function codegenInstruction(
442
- temp: Temporaries,
443
+ cx: Context,
444
instr: ReactiveInstruction,
445
value: t.Expression
446
): t.Statement {
@@ -451,7 +452,7 @@ function codegenInstruction(
452
}
453
if (instr.lvalue.place.identifier.name === null) {
454
// temporary
454
- temp.set(instr.lvalue.place.identifier.id, value);
455
+ cx.temp.set(instr.lvalue.place.identifier.id, value);
456
return t.emptyStatement();
457
} else {
458
switch (instr.lvalue.kind) {
@@ -482,21 +483,21 @@ function codegenInstruction(
483
}
484
485
function codegenInstructionValue(
485
- temp: Temporaries,
486
+ cx: Context,
487
instrValue: ReactiveValue
488
): t.Expression {
489
let value: t.Expression;
490
switch (instrValue.kind) {
491
case "ArrayExpression": {
492
const elements = instrValue.elements.map((element) =>
492
- codegenPlace(temp, element)
493
+ codegenPlace(cx, element)
494
);
495
value = t.arrayExpression(elements);
496
break;
497
}
498
case "BinaryExpression": {
498
- const left = codegenPlace(temp, instrValue.left);
499
- const right = codegenPlace(temp, instrValue.right);
499
+ const left = codegenPlace(cx, instrValue.left);
500
+ const right = codegenPlace(cx, instrValue.right);
501
value = createBinaryExpression(
502
instrValue.loc,
503
instrValue.operator,
@@ -508,41 +509,41 @@ function codegenInstructionValue(
509
case "UnaryExpression": {
510
value = t.unaryExpression(
511
instrValue.operator as "throw", // todo
511
- codegenPlace(temp, instrValue.value)
512
+ codegenPlace(cx, instrValue.value)
513
);
514
break;
515
}
516
case "Primitive": {
516
- value = codegenValue(temp, instrValue.value);
517
+ value = codegenValue(cx, instrValue.value);
518
break;
519
}
520
case "CallExpression": {
520
- const callee = codegenPlace(temp, instrValue.callee);
521
- const args = instrValue.args.map((arg) => codegenPlace(temp, arg));
521
+ const callee = codegenPlace(cx, instrValue.callee);
522
+ const args = instrValue.args.map((arg) => codegenPlace(cx, arg));
523
value = createCallExpression(instrValue.loc, callee, args);
524
break;
525
}
526
case "PropertyCall": {
526
- const receiver = codegenPlace(temp, instrValue.receiver);
527
+ const receiver = codegenPlace(cx, instrValue.receiver);
528
const callee = t.memberExpression(
529
receiver,
530
t.identifier(instrValue.property)
531
);
531
- const args = instrValue.args.map((arg) => codegenPlace(temp, arg));
532
+ const args = instrValue.args.map((arg) => codegenPlace(cx, arg));
533
value = createCallExpression(instrValue.loc, callee, args);
534
break;
535
}
536
case "ComputedCall": {
536
- const receiver = codegenPlace(temp, instrValue.receiver);
537
- const property = codegenPlace(temp, instrValue.property);
537
+ const receiver = codegenPlace(cx, instrValue.receiver);
538
+ const property = codegenPlace(cx, instrValue.property);
539
const callee = t.memberExpression(receiver, property, true);
539
- const args = instrValue.args.map((arg) => codegenPlace(temp, arg));
540
+ const args = instrValue.args.map((arg) => codegenPlace(cx, arg));
541
value = createCallExpression(instrValue.loc, callee, args);
542
break;
543
}
544
case "NewExpression": {
544
- const callee = codegenPlace(temp, instrValue.callee);
545
- const args = instrValue.args.map((arg) => codegenPlace(temp, arg));
545
+ const callee = codegenPlace(cx, instrValue.callee);
546
+ const args = instrValue.args.map((arg) => codegenPlace(cx, arg));
547
value = t.newExpression(callee, args);
548
break;
549
}
@@ -551,10 +552,7 @@ function codegenInstructionValue(
552
if (instrValue.properties !== null) {
553
for (const [property, value] of instrValue.properties) {
554
properties.push(
554
- t.objectProperty(
555
- t.stringLiteral(property),
556
- codegenPlace(temp, value)
557
- )
555
+ t.objectProperty(t.stringLiteral(property), codegenPlace(cx, value))
556
);
557
}
558
}
@@ -571,11 +569,11 @@ function codegenInstructionValue(
569
attributes.push(
570
t.jsxAttribute(
571
t.jsxIdentifier(prop),
574
- t.jsxExpressionContainer(codegenPlace(temp, value))
572
+ t.jsxExpressionContainer(codegenPlace(cx, value))
573
)
574
);
575
}
578
- let tagValue = codegenPlace(temp, instrValue.tag);
576
+ let tagValue = codegenPlace(cx, instrValue.tag);
577
let tag: string;
578
if (tagValue.type === "Identifier") {
579
tag = tagValue.name;
@@ -588,7 +586,7 @@ function codegenInstructionValue(
586
}
587
const children =
588
instrValue.children !== null
591
- ? instrValue.children.map((child) => codegenJsxElement(temp, child))
589
+ ? instrValue.children.map((child) => codegenJsxElement(cx, child))
590
: [];
591
value = t.jsxElement(
592
t.jsxOpeningElement(
@@ -608,7 +606,7 @@ function codegenInstructionValue(
606
value = t.jsxFragment(
607
t.jsxOpeningFragment(),
608
t.jsxClosingFragment(),
611
- instrValue.children.map((child) => codegenJsxElement(temp, child))
609
+ instrValue.children.map((child) => codegenJsxElement(cx, child))
610
);
611
break;
612
}
@@ -624,24 +622,24 @@ function codegenInstructionValue(
622
value = t.assignmentExpression(
623
"=",
624
t.memberExpression(
627
- codegenPlace(temp, instrValue.object),
625
+ codegenPlace(cx, instrValue.object),
626
t.identifier(instrValue.property)
627
),
630
- codegenPlace(temp, instrValue.value)
628
+ codegenPlace(cx, instrValue.value)
629
);
630
break;
631
}
632
case "PropertyLoad": {
633
if (instrValue.optional) {
634
value = t.optionalMemberExpression(
637
- codegenPlace(temp, instrValue.object),
635
+ codegenPlace(cx, instrValue.object),
636
t.identifier(instrValue.property),
637
undefined,
638
true
639
);
640
} else {
641
value = t.memberExpression(
644
- codegenPlace(temp, instrValue.object),
642
+ codegenPlace(cx, instrValue.object),
643
t.identifier(instrValue.property)
644
);
645
}
@@ -651,24 +649,24 @@ function codegenInstructionValue(
649
value = t.assignmentExpression(
650
"=",
651
t.memberExpression(
654
- codegenPlace(temp, instrValue.object),
655
- codegenPlace(temp, instrValue.property),
652
+ codegenPlace(cx, instrValue.object),
653
+ codegenPlace(cx, instrValue.property),
654
true
655
),
658
- codegenPlace(temp, instrValue.value)
656
+ codegenPlace(cx, instrValue.value)
657
);
658
break;
659
}
660
case "ComputedLoad": {
661
value = t.memberExpression(
664
- codegenPlace(temp, instrValue.object),
665
- codegenPlace(temp, instrValue.property),
662
+ codegenPlace(cx, instrValue.object),
663
+ codegenPlace(cx, instrValue.property),
664
true
665
);
666
break;
667
}
668
case "Identifier": {
671
- value = codegenPlace(temp, instrValue);
669
+ value = codegenPlace(cx, instrValue);
670
break;
671
}
672
case "FunctionExpression": {
@@ -681,7 +679,7 @@ function codegenInstructionValue(
679
case "TaggedTemplateExpression": {
680
value = createTaggedTemplateExpression(
681
instrValue.loc,
684
- codegenPlace(temp, instrValue.tag),
682
+ codegenPlace(cx, instrValue.tag),
683
t.templateLiteral([t.templateElement(instrValue.value)], [])
684
);
685
break;
@@ -690,11 +688,39 @@ function codegenInstructionValue(
688
value = createLogicalExpression(
689
instrValue.loc,
690
instrValue.operator,
693
- codegenInstructionValue(temp, instrValue.left),
694
- codegenInstructionValue(temp, instrValue.right)
691
+ codegenInstructionValue(cx, instrValue.left),
692
+ codegenInstructionValue(cx, instrValue.right)
693
);
694
break;
695
}
696
+ case "SequenceExpression": {
697
+ const body = codegenBlock(
698
+ cx,
699
+ instrValue.instructions.map((instruction) => ({
700
+ kind: "instruction",
701
+ instruction,
702
+ }))
703
+ ).body;
704
+ const expressions = body.map((stmt) => {
705
+ if (stmt.type === "ExpressionStatement") {
706
+ return stmt.expression;
707
+ } else {
708
+ todoInvariant(
709
+ false,
710
+ `Handle conversion of ${stmt.type} to expression`
711
+ );
712
+ }
713
+ });
714
+ if (expressions.length === 0) {
715
+ value = codegenInstructionValue(cx, instrValue.value);
716
+ } else {
717
+ value = createSequenceExpression(instrValue.loc, [
718
+ ...expressions,
719
+ codegenInstructionValue(cx, instrValue.value),
720
+ ]);
721
+ }
722
+ break;
723
+ }
724
default: {
725
assertExhaustive(
726
instrValue,
@@ -706,7 +732,7 @@ function codegenInstructionValue(
732
}
733
734
function codegenJsxElement(
709
- temp: Temporaries,
735
+ cx: Context,
736
place: Place
737
):
738
| t.JSXText
@@ -714,7 +740,7 @@ function codegenJsxElement(
740
| t.JSXSpreadChild
741
| t.JSXElement
742
| t.JSXFragment {
717
- const value = codegenPlace(temp, place);
743
+ const value = codegenPlace(cx, place);
744
switch (value.type) {
745
case "StringLiteral": {
746
return t.jsxText(value.value);
@@ -730,7 +756,7 @@ function codegenLVal(lval: LValue): t.LVal {
756
}
757
758
function codegenValue(
733
- temp: Temporaries,
759
+ cx: Context,
760
value: boolean | number | string | null | undefined
761
): t.Expression {
762
if (typeof value === "number") {
@@ -748,9 +774,9 @@ function codegenValue(
774
}
775
}
776
751
-function codegenPlace(temp: Temporaries, place: Place): t.Expression {
777
+function codegenPlace(cx: Context, place: Place): t.Expression {
778
todoInvariant(place.kind === "Identifier", "support scope values");
753
- let tmp = temp.get(place.identifier.id);
779
+ let tmp = cx.temp.get(place.identifier.id);
780
if (tmp != null) {
781
return tmp;
782
}
compiler/forget/src/ReactiveScopes/visitors.ts
+7
@@ -91,6 +91,13 @@ export function* eachReactiveValueOperand(
91
yield* eachReactiveValueOperand(instrValue.right);
92
break;
93
}
94
+ case "SequenceExpression": {
95
+ for (const instr of instrValue.instructions) {
96
+ yield* eachReactiveValueOperand(instr.value);
97
+ }
98
+ yield* eachReactiveValueOperand(instrValue.value);
99
+ break;
100
+ }
101
default: {
102
yield* eachInstructionValueOperand(instrValue);
103
}
compiler/forget/src/SSA/LeaveSSA.ts
+5
-1
@@ -123,7 +123,6 @@ export function leaveSSA(fn: HIRFunction) {
123
(terminal.kind === "if" ||
124
terminal.kind === "switch" ||
125
terminal.kind === "while" ||
126
- terminal.kind === "logical" ||
126
terminal.kind === "for") &&
127
terminal.fallthrough !== null
128
) {
@@ -149,6 +148,11 @@ export function leaveSSA(fn: HIRFunction) {
148
pushPhis(rewritePhis, update);
149
update.phis.clear();
150
}
151
+ if (terminal.kind === "logical") {
152
+ const fallthrough = fn.body.blocks.get(terminal.fallthrough)!;
153
+ pushPhis(rewritePhis, fallthrough);
154
+ fallthrough.phis.clear();
155
+ }
156
157
for (const { phi, block: phiBlock } of reassignmentPhis) {
158
// In some cases one of the phi operands can be defined *before* the let binding
compiler/forget/src/__tests__/fixtures/hir/logical-expression.expect.md
+11
-16
@@ -2,8 +2,9 @@
2
## Input
3
4
```javascript
5
+// @only
6
function component(props) {
6
- let a = props.a && props.b;
7
+ let a = (props.a && props.b && props.c) || props.d;
8
return a;
9
// let b = props.c || props.d;
10
// let c = props.e ?? props.f;
@@ -15,25 +16,19 @@ function component(props) {
16
## Code
17
18
```javascript
19
+// @only
20
function component(props) {
21
const $ = React.useMemoCache();
20
- const c_0 = $[0] !== props.a;
21
- const c_1 = $[1] !== props.b;
22
- let t2;
23
- if (c_0 || c_1) {
24
- t2 = undefined;
25
- if (props.a) {
26
- t2 = props.a;
27
- } else {
28
- t2 = props.b;
29
- }
30
- $[0] = props.a;
31
- $[1] = props.b;
32
- $[2] = t2;
22
+ const c_0 = $[0] !== props;
23
+ let t1;
24
+ if (c_0) {
25
+ t1 = (props.a && props.b && props.c) || props.d;
26
+ $[0] = props;
27
+ $[1] = t1;
28
} else {
34
- t2 = $[2];
29
+ t1 = $[1];
30
}
36
- const a = t2;
31
+ const a = t1;
32
return a;
33
}
34
compiler/forget/src/__tests__/fixtures/hir/logical-expression.js
+2
-1
@@ -1,5 +1,6 @@
1
+// @only
2
function component(props) {
2
- let a = props.a && props.b;
3
+ let a = (props.a && props.b && props.c) || props.d;
4
return a;
5
// let b = props.c || props.d;
6
// let c = props.e ?? props.f;