Compare commits

...

2 Commits

Author SHA1 Message Date
Joe Savona
4211a7c12f [compiler] Fix for inferring props-derived-value as mutable
Fix for the repro from the previous PR. A `Capture x -> y` effect should downgrade to `ImmutableCapture` when the source value is maybe-frozen. MaybeFrozen represents the union of a frozen value with a non-frozen value.
2025-11-14 11:32:16 -08:00
Joe Savona
8deecf5085 [compiler] Repro for false positive mutation of a value derived from props
Repro from the compiler WG (Thanks Cody!) of a case where the compiler incorrectly thinks a value is mutable.
2025-11-14 11:28:12 -08:00
3 changed files with 59 additions and 0 deletions

View File

@@ -954,6 +954,7 @@ function applyEffect(
case ValueKind.Primitive: {
break;
}
case ValueKind.MaybeFrozen:
case ValueKind.Frozen: {
sourceType = 'frozen';
break;

View File

@@ -0,0 +1,45 @@
## Input
```javascript
export function useFormatRelativeTime(opts = {}) {
const {timeZone, minimal} = opts;
const format = useCallback(function formatWithUnit() {}, [minimal]);
// We previously recorded `{timeZone}` as capturing timeZone into the object,
// then assumed that dateTimeFormat() mutates that object,
// which in turn could mutate timeZone and the object it came from,
// which meanteans that the value `minimal` is derived from can change.
//
// The fix was to record a Capture from a maybefrozen value as an ImmutableCapture
// which doesn't propagate mutations
dateTimeFormat({timeZone});
return format;
}
```
## Code
```javascript
import { c as _c } from "react/compiler-runtime";
export function useFormatRelativeTime(t0) {
const $ = _c(1);
const opts = t0 === undefined ? {} : t0;
const { timeZone, minimal } = opts;
let t1;
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
t1 = function formatWithUnit() {};
$[0] = t1;
} else {
t1 = $[0];
}
const format = t1;
dateTimeFormat({ timeZone });
return format;
}
```
### Eval output
(kind: exception) Fixture not implemented

View File

@@ -0,0 +1,13 @@
export function useFormatRelativeTime(opts = {}) {
const {timeZone, minimal} = opts;
const format = useCallback(function formatWithUnit() {}, [minimal]);
// We previously recorded `{timeZone}` as capturing timeZone into the object,
// then assumed that dateTimeFormat() mutates that object,
// which in turn could mutate timeZone and the object it came from,
// which meanteans that the value `minimal` is derived from can change.
//
// The fix was to record a Capture from a maybefrozen value as an ImmutableCapture
// which doesn't propagate mutations
dateTimeFormat({timeZone});
return format;
}