[compiler][optim] more shapes for mixedreadonly (#32594)
- Add `at`, `indexOf`, and `includes` - Optimize MixedReadOnly which is currently only used by hook return values. Hook return values are typed as Frozen, this change propagates that to return values of aliasing function calls (such as `at`). One potential issue is that developers may pass `enableAssumeHooksFollowRulesOfReact:false` and set `transitiveMixedData`, expecting their transitive mixed data to be mutable. This is a bit of an edge case and already doesn't have clear semantics. --- [//]: # (BEGIN SAPLING FOOTER) Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/facebook/react/pull/32594). * #32596 * #32595 * __->__ #32594 * #32593 * #32522 * #32521
mofeiZ committed
Mar 13, 2025 at 11:59 UTC
89a46a57df3a3cc309fff937794341ab215e01e9
1 file changed
+56
-2
compiler/packages/babel-plugin-react-compiler/src/HIR/ObjectShape.ts
+56
-2
@@ -535,6 +535,30 @@ addObject(BUILTIN_SHAPES, BuiltInRefValueId, [
535
['*', {kind: 'Object', shapeId: BuiltInRefValueId}],
536
]);
537
538
+/**
539
+ * MixedReadOnly =
540
+ * | primitive
541
+ * | simple objects (Record<string, MixedReadOnly>)
542
+ * | Array<MixedReadOnly>
543
+ *
544
+ * APIs such as Relay — but also Flux and other data stores — often return a
545
+ * union of types with some interesting properties in terms of analysis.
546
+ *
547
+ * Given this constraint, if data came from Relay, then we should be able to
548
+ * infer things like `data.items.map(): Array`. That may seem like a leap at
549
+ * first but remember, we assume you're not patching builtins. Thus the only way
550
+ * data.items.map can exist and be a function, given the above set of data types
551
+ * and builtin JS methods, is if `data.items` was an Array, and `data.items.map`
552
+ * is therefore calling Array.prototype.map. Then we know that function returns
553
+ * an Array as well. This relies on the fact that map() is being called, so if
554
+ * data.items was some other type it would error at runtime - so it's sound.
555
+ *
556
+ * Note that this shape is currently only used for hook return values, which
557
+ * means that it's safe to type aliasing method-call return kinds as `Frozen`.
558
+ *
559
+ * Also note that all newly created arrays from method-calls (e.g. `.map`)
560
+ * have the appropriate mutable `BuiltInArray` shape
561
+ */
562
addObject(BUILTIN_SHAPES, BuiltInMixedReadonlyId, [
563
[
564
'toString',
@@ -546,6 +570,36 @@ addObject(BUILTIN_SHAPES, BuiltInMixedReadonlyId, [
570
returnValueKind: ValueKind.Primitive,
571
}),
572
],
573
+ [
574
+ 'indexOf',
575
+ addFunction(BUILTIN_SHAPES, [], {
576
+ positionalParams: [],
577
+ restParam: Effect.Read,
578
+ returnType: {kind: 'Primitive'},
579
+ calleeEffect: Effect.Read,
580
+ returnValueKind: ValueKind.Primitive,
581
+ }),
582
+ ],
583
+ [
584
+ 'includes',
585
+ addFunction(BUILTIN_SHAPES, [], {
586
+ positionalParams: [],
587
+ restParam: Effect.Read,
588
+ returnType: {kind: 'Primitive'},
589
+ calleeEffect: Effect.Read,
590
+ returnValueKind: ValueKind.Primitive,
591
+ }),
592
+ ],
593
+ [
594
+ 'at',
595
+ addFunction(BUILTIN_SHAPES, [], {
596
+ positionalParams: [Effect.Read],
597
+ restParam: null,
598
+ returnType: {kind: 'Object', shapeId: BuiltInMixedReadonlyId},
599
+ calleeEffect: Effect.Capture,
600
+ returnValueKind: ValueKind.Frozen,
601
+ }),
602
+ ],
603
[
604
'map',
605
addFunction(BUILTIN_SHAPES, [], {
@@ -642,9 +696,9 @@ addObject(BUILTIN_SHAPES, BuiltInMixedReadonlyId, [
696
addFunction(BUILTIN_SHAPES, [], {
697
positionalParams: [],
698
restParam: Effect.ConditionallyMutate,
645
- returnType: {kind: 'Poly'},
699
+ returnType: {kind: 'Object', shapeId: BuiltInMixedReadonlyId},
700
calleeEffect: Effect.ConditionallyMutate,
647
- returnValueKind: ValueKind.Mutable,
701
+ returnValueKind: ValueKind.Frozen,
702
noAlias: true,
703
mutableOnlyIfOperandsAreMutable: true,
704
}),