mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
Fix Array#at and similar cases to capture if receiver is mutable
The previous PR helped me realize we weren't handling Array#at correctly. If the receiver is a mutable value its effect should be Capture and the lvalue effect needs to be Store. This PR updates the definition for Array#at to make the receiver Capture, and then updates inference to automatically set the lvalue effect to Store if _any_ argument (or the receiver) was Capture.
This commit is contained in:
@@ -203,7 +203,7 @@ addObject(BUILTIN_SHAPES, BuiltInArrayId, [
|
||||
positionalParams: [Effect.Read],
|
||||
restParam: null,
|
||||
returnType: { kind: "Poly" },
|
||||
calleeEffect: Effect.Read,
|
||||
calleeEffect: Effect.Capture,
|
||||
returnValueKind: ValueKind.Mutable,
|
||||
}),
|
||||
],
|
||||
|
||||
@@ -809,6 +809,7 @@ function inferBlock(
|
||||
signature !== null ? getFunctionEffects(instrValue, signature) : null;
|
||||
const returnValueKind =
|
||||
signature !== null ? signature.returnValueKind : ValueKind.Mutable;
|
||||
let hasCaptureArgument = false;
|
||||
for (let i = 0; i < instrValue.args.length; i++) {
|
||||
const arg = instrValue.args[i];
|
||||
const place = arg.kind === "Identifier" ? arg : arg.place;
|
||||
@@ -817,16 +818,20 @@ function inferBlock(
|
||||
} else {
|
||||
state.reference(place, Effect.ConditionallyMutate);
|
||||
}
|
||||
hasCaptureArgument ||= place.effect === Effect.Capture;
|
||||
}
|
||||
if (signature !== null) {
|
||||
state.reference(instrValue.callee, signature.calleeEffect);
|
||||
} else {
|
||||
state.reference(instrValue.callee, Effect.ConditionallyMutate);
|
||||
}
|
||||
hasCaptureArgument ||= instrValue.callee.effect === Effect.Capture;
|
||||
|
||||
state.initialize(instrValue, returnValueKind);
|
||||
state.define(instr.lvalue, instrValue);
|
||||
instr.lvalue.effect = Effect.ConditionallyMutate;
|
||||
instr.lvalue.effect = hasCaptureArgument
|
||||
? Effect.Store
|
||||
: Effect.ConditionallyMutate;
|
||||
continue;
|
||||
}
|
||||
case "MethodCall": {
|
||||
@@ -871,6 +876,7 @@ function inferBlock(
|
||||
signature !== null ? getFunctionEffects(instrValue, signature) : null;
|
||||
const returnValueKind =
|
||||
signature !== null ? signature.returnValueKind : ValueKind.Mutable;
|
||||
let hasCaptureArgument = false;
|
||||
for (let i = 0; i < instrValue.args.length; i++) {
|
||||
const arg = instrValue.args[i];
|
||||
const place = arg.kind === "Identifier" ? arg : arg.place;
|
||||
@@ -883,16 +889,20 @@ function inferBlock(
|
||||
} else {
|
||||
state.reference(place, Effect.ConditionallyMutate);
|
||||
}
|
||||
hasCaptureArgument ||= place.effect === Effect.Capture;
|
||||
}
|
||||
if (signature !== null) {
|
||||
state.reference(instrValue.receiver, signature.calleeEffect);
|
||||
} else {
|
||||
state.reference(instrValue.receiver, Effect.ConditionallyMutate);
|
||||
}
|
||||
hasCaptureArgument ||= instrValue.receiver.effect === Effect.Capture;
|
||||
|
||||
state.initialize(instrValue, returnValueKind);
|
||||
state.define(instr.lvalue, instrValue);
|
||||
instr.lvalue.effect = Effect.ConditionallyMutate;
|
||||
instr.lvalue.effect = hasCaptureArgument
|
||||
? Effect.Store
|
||||
: Effect.ConditionallyMutate;
|
||||
continue;
|
||||
}
|
||||
case "PropertyStore": {
|
||||
|
||||
+10
-10
@@ -21,18 +21,18 @@ function Component(props) {
|
||||
import { unstable_useMemoCache as useMemoCache } from "react"; // x's mutable range should extend to `mutate(y)`
|
||||
|
||||
function Component(props) {
|
||||
const $ = useMemoCache(1);
|
||||
let t0;
|
||||
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t0 = [42, {}];
|
||||
$[0] = t0;
|
||||
const $ = useMemoCache(2);
|
||||
let x;
|
||||
if ($[0] !== props.b) {
|
||||
x = [42, {}];
|
||||
const idx = foo(props.b);
|
||||
const y = x.at(idx);
|
||||
mutate(y);
|
||||
$[0] = props.b;
|
||||
$[1] = x;
|
||||
} else {
|
||||
t0 = $[0];
|
||||
x = $[1];
|
||||
}
|
||||
const x = t0;
|
||||
const idx = foo(props.b);
|
||||
const y = x.at(idx);
|
||||
mutate(y);
|
||||
return x;
|
||||
}
|
||||
|
||||
|
||||
+6
-7
@@ -23,16 +23,15 @@ export const FIXTURE_ENTRYPOINT = {
|
||||
import { unstable_useMemoCache as useMemoCache } from "react";
|
||||
function foo() {
|
||||
const $ = useMemoCache(1);
|
||||
let t0;
|
||||
let a;
|
||||
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t0 = [[1]];
|
||||
$[0] = t0;
|
||||
a = [[1]];
|
||||
const first = a.at(0);
|
||||
first.set(0, 2);
|
||||
$[0] = a;
|
||||
} else {
|
||||
t0 = $[0];
|
||||
a = $[0];
|
||||
}
|
||||
const a = t0;
|
||||
const first = a.at(0);
|
||||
first.set(0, 2);
|
||||
return a;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user