Fix last(?) order of evaluation bug

Not to get ahead of myself (sorry i had to), but i think this is the last order 
of evaluation bug. At least it's the last one we know of[1]. Per the previous 
PR, the issue is that constant propagation can copy the last value of a sequence 
expression to where the sequence is used, leaving the original sequence 
expression out of order after other instructions are moved around. We fix that 
here by explicitly skipping constant propagation for the last value of a 
sequence block. 

[1] There are some places where we _would_ have evaluation order bugs if we 
allowed arbitrary expressions, but we explicitly limit the expressions we allow 
in those places. For the curious: switch test case values and destructuring 
default values.
This commit is contained in:
Joe Savona
2023-06-04 21:41:27 -04:00
parent 1e0eb25cd0
commit 3744930728
5 changed files with 12 additions and 5 deletions
+1 -1
View File
@@ -1171,7 +1171,7 @@ function lowerExpression(
const continuationBlock = builder.reserve(builder.currentBlockKind());
const place = buildTemporaryPlace(builder, exprLoc);
const sequenceBlock = builder.enter("value", (_) => {
const sequenceBlock = builder.enter("sequence", (_) => {
let last: Place | null = null;
for (const item of expr.get("expressions")) {
last = lowerExpressionToTemporary(builder, item);
+1 -1
View File
@@ -250,7 +250,7 @@ export type HIR = {
* an exception occurs, therefore the block model only represents explicit throw
* statements and not implicit exceptions which may occur.
*/
export type BlockKind = "block" | "value" | "loop";
export type BlockKind = "block" | "value" | "loop" | "sequence";
export type BasicBlock = {
kind: BlockKind;
id: BlockId;
@@ -113,7 +113,13 @@ function applyConstantPropagation(fn: HIRFunction): boolean {
}
}
for (const instr of block.instructions) {
for (let i = 0; i < block.instructions.length; i++) {
if (block.kind === "sequence" && i === block.instructions.length - 1) {
// evaluating the last value of a value block can break order of evaluation,
// skip these instructions
continue;
}
const instr = block.instructions[i]!;
const value = evaluateInstruction(constants, instr);
if (value !== null) {
constants.set(instr.lvalue.identifier.id, value);
@@ -46,8 +46,9 @@ function Component() {
if ($[2] === Symbol.for("react.memo_cache_sentinel")) {
x = { f: t1 };
console.log("B"), "f";
(console.log("A"), x).f((changeF(x), console.log("arg"), 1));
(console.log("A"), x)[(console.log("B"), "f")](
(changeF(x), console.log("arg"), 1)
);
$[2] = x;
} else {
x = $[2];