Compare commits
4 Commits
asserts-st
...
pr32594
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d2d5350e65 | ||
|
|
8ba22587b5 | ||
|
|
7606b29048 | ||
|
|
54e602d891 |
@@ -1126,9 +1126,32 @@ export class Environment {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getFallthroughPropertyType(
|
||||||
|
receiver: Type,
|
||||||
|
_property: Type,
|
||||||
|
): BuiltInType | PolyType | null {
|
||||||
|
let shapeId = null;
|
||||||
|
if (receiver.kind === 'Object' || receiver.kind === 'Function') {
|
||||||
|
shapeId = receiver.shapeId;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shapeId !== null) {
|
||||||
|
const shape = this.#shapes.get(shapeId);
|
||||||
|
|
||||||
|
CompilerError.invariant(shape !== undefined, {
|
||||||
|
reason: `[HIR] Forget internal error: cannot resolve shape ${shapeId}`,
|
||||||
|
description: null,
|
||||||
|
loc: null,
|
||||||
|
suggestions: null,
|
||||||
|
});
|
||||||
|
return shape.properties.get('*') ?? null;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
getPropertyType(
|
getPropertyType(
|
||||||
receiver: Type,
|
receiver: Type,
|
||||||
property: string,
|
property: string | number,
|
||||||
): BuiltInType | PolyType | null {
|
): BuiltInType | PolyType | null {
|
||||||
let shapeId = null;
|
let shapeId = null;
|
||||||
if (receiver.kind === 'Object' || receiver.kind === 'Function') {
|
if (receiver.kind === 'Object' || receiver.kind === 'Function') {
|
||||||
@@ -1146,17 +1169,19 @@ export class Environment {
|
|||||||
loc: null,
|
loc: null,
|
||||||
suggestions: null,
|
suggestions: null,
|
||||||
});
|
});
|
||||||
let value =
|
if (typeof property === 'string') {
|
||||||
shape.properties.get(property) ?? shape.properties.get('*') ?? null;
|
return (
|
||||||
if (value === null && isHookName(property)) {
|
shape.properties.get(property) ??
|
||||||
value = this.#getCustomHookType();
|
shape.properties.get('*') ??
|
||||||
|
(isHookName(property) ? this.#getCustomHookType() : null)
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return shape.properties.get('*') ?? null;
|
||||||
}
|
}
|
||||||
return value;
|
} else if (typeof property === 'string' && isHookName(property)) {
|
||||||
} else if (isHookName(property)) {
|
|
||||||
return this.#getCustomHookType();
|
return this.#getCustomHookType();
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
getFunctionSignature(type: FunctionType): FunctionSignature | null {
|
getFunctionSignature(type: FunctionType): FunctionSignature | null {
|
||||||
|
|||||||
@@ -119,8 +119,8 @@ const TYPED_GLOBALS: Array<[string, BuiltInType]> = [
|
|||||||
],
|
],
|
||||||
/*
|
/*
|
||||||
* https://tc39.es/ecma262/multipage/indexed-collections.html#sec-array.from
|
* https://tc39.es/ecma262/multipage/indexed-collections.html#sec-array.from
|
||||||
* Array.from(arrayLike, optionalFn, optionalThis) not added because
|
* Array.from(arrayLike, optionalFn, optionalThis)
|
||||||
* the Effect of `arrayLike` is polymorphic i.e.
|
* Note that the Effect of `arrayLike` is polymorphic i.e.
|
||||||
* - Effect.read if
|
* - Effect.read if
|
||||||
* - it does not have an @iterator property and is array-like
|
* - it does not have an @iterator property and is array-like
|
||||||
* (i.e. has a length property)
|
* (i.e. has a length property)
|
||||||
@@ -128,6 +128,20 @@ const TYPED_GLOBALS: Array<[string, BuiltInType]> = [
|
|||||||
* - Effect.mutate if it is a self-mutative iterator (e.g. a generator
|
* - Effect.mutate if it is a self-mutative iterator (e.g. a generator
|
||||||
* function)
|
* function)
|
||||||
*/
|
*/
|
||||||
|
[
|
||||||
|
'from',
|
||||||
|
addFunction(DEFAULT_SHAPES, [], {
|
||||||
|
positionalParams: [
|
||||||
|
Effect.ConditionallyMutate,
|
||||||
|
Effect.ConditionallyMutate,
|
||||||
|
Effect.ConditionallyMutate,
|
||||||
|
],
|
||||||
|
restParam: Effect.Read,
|
||||||
|
returnType: {kind: 'Object', shapeId: BuiltInArrayId},
|
||||||
|
calleeEffect: Effect.Read,
|
||||||
|
returnValueKind: ValueKind.Mutable,
|
||||||
|
}),
|
||||||
|
],
|
||||||
[
|
[
|
||||||
'of',
|
'of',
|
||||||
// Array.of(element0, ..., elementN)
|
// Array.of(element0, ..., elementN)
|
||||||
|
|||||||
@@ -535,6 +535,30 @@ addObject(BUILTIN_SHAPES, BuiltInRefValueId, [
|
|||||||
['*', {kind: 'Object', shapeId: BuiltInRefValueId}],
|
['*', {kind: 'Object', shapeId: BuiltInRefValueId}],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MixedReadOnly =
|
||||||
|
* | primitive
|
||||||
|
* | simple objects (Record<string, MixedReadOnly>)
|
||||||
|
* | Array<MixedReadOnly>
|
||||||
|
*
|
||||||
|
* APIs such as Relay — but also Flux and other data stores — often return a
|
||||||
|
* union of types with some interesting properties in terms of analysis.
|
||||||
|
*
|
||||||
|
* Given this constraint, if data came from Relay, then we should be able to
|
||||||
|
* infer things like `data.items.map(): Array`. That may seem like a leap at
|
||||||
|
* first but remember, we assume you're not patching builtins. Thus the only way
|
||||||
|
* data.items.map can exist and be a function, given the above set of data types
|
||||||
|
* and builtin JS methods, is if `data.items` was an Array, and `data.items.map`
|
||||||
|
* is therefore calling Array.prototype.map. Then we know that function returns
|
||||||
|
* an Array as well. This relies on the fact that map() is being called, so if
|
||||||
|
* data.items was some other type it would error at runtime - so it's sound.
|
||||||
|
*
|
||||||
|
* Note that this shape is currently only used for hook return values, which
|
||||||
|
* means that it's safe to type aliasing method-call return kinds as `Frozen`.
|
||||||
|
*
|
||||||
|
* Also note that all newly created arrays from method-calls (e.g. `.map`)
|
||||||
|
* have the appropriate mutable `BuiltInArray` shape
|
||||||
|
*/
|
||||||
addObject(BUILTIN_SHAPES, BuiltInMixedReadonlyId, [
|
addObject(BUILTIN_SHAPES, BuiltInMixedReadonlyId, [
|
||||||
[
|
[
|
||||||
'toString',
|
'toString',
|
||||||
@@ -546,6 +570,36 @@ addObject(BUILTIN_SHAPES, BuiltInMixedReadonlyId, [
|
|||||||
returnValueKind: ValueKind.Primitive,
|
returnValueKind: ValueKind.Primitive,
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
|
[
|
||||||
|
'indexOf',
|
||||||
|
addFunction(BUILTIN_SHAPES, [], {
|
||||||
|
positionalParams: [],
|
||||||
|
restParam: Effect.Read,
|
||||||
|
returnType: {kind: 'Primitive'},
|
||||||
|
calleeEffect: Effect.Read,
|
||||||
|
returnValueKind: ValueKind.Primitive,
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'includes',
|
||||||
|
addFunction(BUILTIN_SHAPES, [], {
|
||||||
|
positionalParams: [],
|
||||||
|
restParam: Effect.Read,
|
||||||
|
returnType: {kind: 'Primitive'},
|
||||||
|
calleeEffect: Effect.Read,
|
||||||
|
returnValueKind: ValueKind.Primitive,
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'at',
|
||||||
|
addFunction(BUILTIN_SHAPES, [], {
|
||||||
|
positionalParams: [Effect.Read],
|
||||||
|
restParam: null,
|
||||||
|
returnType: {kind: 'Object', shapeId: BuiltInMixedReadonlyId},
|
||||||
|
calleeEffect: Effect.Capture,
|
||||||
|
returnValueKind: ValueKind.Frozen,
|
||||||
|
}),
|
||||||
|
],
|
||||||
[
|
[
|
||||||
'map',
|
'map',
|
||||||
addFunction(BUILTIN_SHAPES, [], {
|
addFunction(BUILTIN_SHAPES, [], {
|
||||||
@@ -642,9 +696,9 @@ addObject(BUILTIN_SHAPES, BuiltInMixedReadonlyId, [
|
|||||||
addFunction(BUILTIN_SHAPES, [], {
|
addFunction(BUILTIN_SHAPES, [], {
|
||||||
positionalParams: [],
|
positionalParams: [],
|
||||||
restParam: Effect.ConditionallyMutate,
|
restParam: Effect.ConditionallyMutate,
|
||||||
returnType: {kind: 'Poly'},
|
returnType: {kind: 'Object', shapeId: BuiltInMixedReadonlyId},
|
||||||
calleeEffect: Effect.ConditionallyMutate,
|
calleeEffect: Effect.ConditionallyMutate,
|
||||||
returnValueKind: ValueKind.Mutable,
|
returnValueKind: ValueKind.Frozen,
|
||||||
noAlias: true,
|
noAlias: true,
|
||||||
mutableOnlyIfOperandsAreMutable: true,
|
mutableOnlyIfOperandsAreMutable: true,
|
||||||
}),
|
}),
|
||||||
|
|||||||
@@ -60,7 +60,15 @@ export type PropType = {
|
|||||||
kind: 'Property';
|
kind: 'Property';
|
||||||
objectType: Type;
|
objectType: Type;
|
||||||
objectName: string;
|
objectName: string;
|
||||||
propertyName: PropertyLiteral;
|
propertyName:
|
||||||
|
| {
|
||||||
|
kind: 'literal';
|
||||||
|
value: PropertyLiteral;
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
kind: 'computed';
|
||||||
|
value: Type;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export type ObjectMethod = {
|
export type ObjectMethod = {
|
||||||
|
|||||||
@@ -872,11 +872,33 @@ function inferBlock(
|
|||||||
reason: new Set([ValueReason.Other]),
|
reason: new Set([ValueReason.Other]),
|
||||||
context: new Set(),
|
context: new Set(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
for (const element of instrValue.elements) {
|
||||||
|
if (element.kind === 'Spread') {
|
||||||
|
state.referenceAndRecordEffects(
|
||||||
|
freezeActions,
|
||||||
|
element.place,
|
||||||
|
isArrayType(element.place.identifier)
|
||||||
|
? Effect.Capture
|
||||||
|
: Effect.ConditionallyMutate,
|
||||||
|
ValueReason.Other,
|
||||||
|
);
|
||||||
|
} else if (element.kind === 'Identifier') {
|
||||||
|
state.referenceAndRecordEffects(
|
||||||
|
freezeActions,
|
||||||
|
element,
|
||||||
|
Effect.Capture,
|
||||||
|
ValueReason.Other,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
let _: 'Hole' = element.kind;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
state.initialize(instrValue, valueKind);
|
||||||
|
state.define(instr.lvalue, instrValue);
|
||||||
|
instr.lvalue.effect = Effect.Store;
|
||||||
continuation = {
|
continuation = {
|
||||||
kind: 'initialize',
|
kind: 'funeffects',
|
||||||
valueKind,
|
|
||||||
effect: {kind: Effect.Capture, reason: ValueReason.Other},
|
|
||||||
lvalueEffect: Effect.Store,
|
|
||||||
};
|
};
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -1241,21 +1263,12 @@ function inferBlock(
|
|||||||
for (let i = 0; i < instrValue.args.length; i++) {
|
for (let i = 0; i < instrValue.args.length; i++) {
|
||||||
const arg = instrValue.args[i];
|
const arg = instrValue.args[i];
|
||||||
const place = arg.kind === 'Identifier' ? arg : arg.place;
|
const place = arg.kind === 'Identifier' ? arg : arg.place;
|
||||||
if (effects !== null) {
|
state.referenceAndRecordEffects(
|
||||||
state.referenceAndRecordEffects(
|
freezeActions,
|
||||||
freezeActions,
|
place,
|
||||||
place,
|
getArgumentEffect(effects != null ? effects[i] : null, arg),
|
||||||
effects[i],
|
ValueReason.Other,
|
||||||
ValueReason.Other,
|
);
|
||||||
);
|
|
||||||
} else {
|
|
||||||
state.referenceAndRecordEffects(
|
|
||||||
freezeActions,
|
|
||||||
place,
|
|
||||||
Effect.ConditionallyMutate,
|
|
||||||
ValueReason.Other,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
hasCaptureArgument ||= place.effect === Effect.Capture;
|
hasCaptureArgument ||= place.effect === Effect.Capture;
|
||||||
}
|
}
|
||||||
if (signature !== null) {
|
if (signature !== null) {
|
||||||
@@ -1307,7 +1320,10 @@ function inferBlock(
|
|||||||
signature !== null
|
signature !== null
|
||||||
? {
|
? {
|
||||||
kind: signature.returnValueKind,
|
kind: signature.returnValueKind,
|
||||||
reason: new Set([ValueReason.Other]),
|
reason: new Set([
|
||||||
|
signature.returnValueReason ??
|
||||||
|
ValueReason.KnownReturnSignature,
|
||||||
|
]),
|
||||||
context: new Set(),
|
context: new Set(),
|
||||||
}
|
}
|
||||||
: {
|
: {
|
||||||
@@ -1356,25 +1372,16 @@ function inferBlock(
|
|||||||
for (let i = 0; i < instrValue.args.length; i++) {
|
for (let i = 0; i < instrValue.args.length; i++) {
|
||||||
const arg = instrValue.args[i];
|
const arg = instrValue.args[i];
|
||||||
const place = arg.kind === 'Identifier' ? arg : arg.place;
|
const place = arg.kind === 'Identifier' ? arg : arg.place;
|
||||||
if (effects !== null) {
|
/*
|
||||||
/*
|
* If effects are inferred for an argument, we should fail invalid
|
||||||
* If effects are inferred for an argument, we should fail invalid
|
* mutating effects
|
||||||
* mutating effects
|
*/
|
||||||
*/
|
state.referenceAndRecordEffects(
|
||||||
state.referenceAndRecordEffects(
|
freezeActions,
|
||||||
freezeActions,
|
place,
|
||||||
place,
|
getArgumentEffect(effects != null ? effects[i] : null, arg),
|
||||||
effects[i],
|
ValueReason.Other,
|
||||||
ValueReason.Other,
|
);
|
||||||
);
|
|
||||||
} else {
|
|
||||||
state.referenceAndRecordEffects(
|
|
||||||
freezeActions,
|
|
||||||
place,
|
|
||||||
Effect.ConditionallyMutate,
|
|
||||||
ValueReason.Other,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
hasCaptureArgument ||= place.effect === Effect.Capture;
|
hasCaptureArgument ||= place.effect === Effect.Capture;
|
||||||
}
|
}
|
||||||
if (signature !== null) {
|
if (signature !== null) {
|
||||||
@@ -2049,3 +2056,31 @@ function areArgumentsImmutableAndNonMutating(
|
|||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getArgumentEffect(
|
||||||
|
signatureEffect: Effect | null,
|
||||||
|
arg: Place | SpreadPattern,
|
||||||
|
): Effect {
|
||||||
|
if (signatureEffect != null) {
|
||||||
|
if (arg.kind === 'Identifier') {
|
||||||
|
return signatureEffect;
|
||||||
|
} else if (
|
||||||
|
signatureEffect === Effect.Mutate ||
|
||||||
|
signatureEffect === Effect.ConditionallyMutate
|
||||||
|
) {
|
||||||
|
return signatureEffect;
|
||||||
|
} else {
|
||||||
|
// see call-spread-argument-mutable-iterator test fixture
|
||||||
|
if (signatureEffect === Effect.Freeze) {
|
||||||
|
CompilerError.throwTodo({
|
||||||
|
reason: 'Support spread syntax for hook arguments',
|
||||||
|
loc: arg.place.loc,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// effects[i] is Effect.Capture | Effect.Read | Effect.Store
|
||||||
|
return Effect.ConditionallyMutate;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return Effect.ConditionallyMutate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -307,11 +307,26 @@ function* generateInstructionTypes(
|
|||||||
kind: 'Property',
|
kind: 'Property',
|
||||||
objectType: value.object.identifier.type,
|
objectType: value.object.identifier.type,
|
||||||
objectName: getName(names, value.object.identifier.id),
|
objectName: getName(names, value.object.identifier.id),
|
||||||
propertyName: value.property,
|
propertyName: {
|
||||||
|
kind: 'literal',
|
||||||
|
value: value.property,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case 'ComputedLoad': {
|
||||||
|
yield equation(left, {
|
||||||
|
kind: 'Property',
|
||||||
|
objectType: value.object.identifier.type,
|
||||||
|
objectName: getName(names, value.object.identifier.id),
|
||||||
|
propertyName: {
|
||||||
|
kind: 'computed',
|
||||||
|
value: value.property.identifier.type,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
case 'MethodCall': {
|
case 'MethodCall': {
|
||||||
const returnType = makeType();
|
const returnType = makeType();
|
||||||
yield equation(value.property.identifier.type, {
|
yield equation(value.property.identifier.type, {
|
||||||
@@ -336,7 +351,10 @@ function* generateInstructionTypes(
|
|||||||
kind: 'Property',
|
kind: 'Property',
|
||||||
objectType: value.value.identifier.type,
|
objectType: value.value.identifier.type,
|
||||||
objectName: getName(names, value.value.identifier.id),
|
objectName: getName(names, value.value.identifier.id),
|
||||||
propertyName: makePropertyLiteral(propertyName),
|
propertyName: {
|
||||||
|
kind: 'literal',
|
||||||
|
value: makePropertyLiteral(propertyName),
|
||||||
|
},
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
@@ -353,7 +371,10 @@ function* generateInstructionTypes(
|
|||||||
kind: 'Property',
|
kind: 'Property',
|
||||||
objectType: value.value.identifier.type,
|
objectType: value.value.identifier.type,
|
||||||
objectName: getName(names, value.value.identifier.id),
|
objectName: getName(names, value.value.identifier.id),
|
||||||
propertyName: makePropertyLiteral(property.key.name),
|
propertyName: {
|
||||||
|
kind: 'literal',
|
||||||
|
value: makePropertyLiteral(property.key.name),
|
||||||
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -410,7 +431,6 @@ function* generateInstructionTypes(
|
|||||||
case 'RegExpLiteral':
|
case 'RegExpLiteral':
|
||||||
case 'MetaProperty':
|
case 'MetaProperty':
|
||||||
case 'ComputedStore':
|
case 'ComputedStore':
|
||||||
case 'ComputedLoad':
|
|
||||||
case 'Await':
|
case 'Await':
|
||||||
case 'GetIterator':
|
case 'GetIterator':
|
||||||
case 'IteratorNext':
|
case 'IteratorNext':
|
||||||
@@ -454,12 +474,13 @@ class Unifier {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const objectType = this.get(tB.objectType);
|
const objectType = this.get(tB.objectType);
|
||||||
let propertyType;
|
const propertyType =
|
||||||
if (typeof tB.propertyName === 'number') {
|
tB.propertyName.kind === 'literal'
|
||||||
propertyType = null;
|
? this.env.getPropertyType(objectType, tB.propertyName.value)
|
||||||
} else {
|
: this.env.getFallthroughPropertyType(
|
||||||
propertyType = this.env.getPropertyType(objectType, tB.propertyName);
|
objectType,
|
||||||
}
|
tB.propertyName.value,
|
||||||
|
);
|
||||||
if (propertyType !== null) {
|
if (propertyType !== null) {
|
||||||
this.unify(tA, propertyType);
|
this.unify(tA, propertyType);
|
||||||
}
|
}
|
||||||
@@ -677,7 +698,11 @@ class Unifier {
|
|||||||
const RefLikeNameRE = /^(?:[a-zA-Z$_][a-zA-Z$_0-9]*)Ref$|^ref$/;
|
const RefLikeNameRE = /^(?:[a-zA-Z$_][a-zA-Z$_0-9]*)Ref$|^ref$/;
|
||||||
|
|
||||||
function isRefLikeName(t: PropType): boolean {
|
function isRefLikeName(t: PropType): boolean {
|
||||||
return RefLikeNameRE.test(t.objectName) && t.propertyName === 'current';
|
return (
|
||||||
|
t.propertyName.kind === 'literal' &&
|
||||||
|
RefLikeNameRE.test(t.objectName) &&
|
||||||
|
t.propertyName.value === 'current'
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function tryUnionTypes(ty1: Type, ty2: Type): Type | null {
|
function tryUnionTypes(ty1: Type, ty2: Type): Type | null {
|
||||||
|
|||||||
@@ -0,0 +1,91 @@
|
|||||||
|
|
||||||
|
## Input
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
import {useIdentity, Stringify} from 'shared-runtime';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO: Note that this `Array.from` is inferred to be mutating its first
|
||||||
|
* argument. This is because React Compiler's typing system does not yet support
|
||||||
|
* annotating a function with a set of argument match cases + distinct
|
||||||
|
* definitions (polymorphism).
|
||||||
|
*
|
||||||
|
* In this case, we should be able to infer that the `Array.from` call is
|
||||||
|
* not mutating its 0th argument.
|
||||||
|
* The 0th argument should be typed as having `effect:Mutate` only when
|
||||||
|
* (1) it might be a mutable iterable or
|
||||||
|
* (2) the 1st argument might mutate its callee
|
||||||
|
*/
|
||||||
|
function Component({value}) {
|
||||||
|
const arr = [{value: 'foo'}, {value: 'bar'}, {value}];
|
||||||
|
useIdentity();
|
||||||
|
const derived = Array.from(arr, (x, idx) => ({...x, id: idx}));
|
||||||
|
return <Stringify>{derived.at(-1)}</Stringify>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const FIXTURE_ENTRYPOINT = {
|
||||||
|
fn: Component,
|
||||||
|
params: [{value: 5}],
|
||||||
|
sequentialRenders: [{value: 5}, {value: 6}, {value: 6}],
|
||||||
|
};
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
## Code
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
import { c as _c } from "react/compiler-runtime";
|
||||||
|
import { useIdentity, Stringify } from "shared-runtime";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO: Note that this `Array.from` is inferred to be mutating its first
|
||||||
|
* argument. This is because React Compiler's typing system does not yet support
|
||||||
|
* annotating a function with a set of argument match cases + distinct
|
||||||
|
* definitions (polymorphism).
|
||||||
|
*
|
||||||
|
* In this case, we should be able to infer that the `Array.from` call is
|
||||||
|
* not mutating its 0th argument.
|
||||||
|
* The 0th argument should be typed as having `effect:Mutate` only when
|
||||||
|
* (1) it might be a mutable iterable or
|
||||||
|
* (2) the 1st argument might mutate its callee
|
||||||
|
*/
|
||||||
|
function Component(t0) {
|
||||||
|
const $ = _c(4);
|
||||||
|
const { value } = t0;
|
||||||
|
const arr = [{ value: "foo" }, { value: "bar" }, { value }];
|
||||||
|
useIdentity();
|
||||||
|
const derived = Array.from(arr, _temp);
|
||||||
|
let t1;
|
||||||
|
if ($[0] !== derived) {
|
||||||
|
t1 = derived.at(-1);
|
||||||
|
$[0] = derived;
|
||||||
|
$[1] = t1;
|
||||||
|
} else {
|
||||||
|
t1 = $[1];
|
||||||
|
}
|
||||||
|
let t2;
|
||||||
|
if ($[2] !== t1) {
|
||||||
|
t2 = <Stringify>{t1}</Stringify>;
|
||||||
|
$[2] = t1;
|
||||||
|
$[3] = t2;
|
||||||
|
} else {
|
||||||
|
t2 = $[3];
|
||||||
|
}
|
||||||
|
return t2;
|
||||||
|
}
|
||||||
|
function _temp(x, idx) {
|
||||||
|
return { ...x, id: idx };
|
||||||
|
}
|
||||||
|
|
||||||
|
export const FIXTURE_ENTRYPOINT = {
|
||||||
|
fn: Component,
|
||||||
|
params: [{ value: 5 }],
|
||||||
|
sequentialRenders: [{ value: 5 }, { value: 6 }, { value: 6 }],
|
||||||
|
};
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
### Eval output
|
||||||
|
(kind: ok) <div>{"children":{"value":5,"id":2}}</div>
|
||||||
|
<div>{"children":{"value":6,"id":2}}</div>
|
||||||
|
<div>{"children":{"value":6,"id":2}}</div>
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
import {useIdentity, Stringify} from 'shared-runtime';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO: Note that this `Array.from` is inferred to be mutating its first
|
||||||
|
* argument. This is because React Compiler's typing system does not yet support
|
||||||
|
* annotating a function with a set of argument match cases + distinct
|
||||||
|
* definitions (polymorphism).
|
||||||
|
*
|
||||||
|
* In this case, we should be able to infer that the `Array.from` call is
|
||||||
|
* not mutating its 0th argument.
|
||||||
|
* The 0th argument should be typed as having `effect:Mutate` only when
|
||||||
|
* (1) it might be a mutable iterable or
|
||||||
|
* (2) the 1st argument might mutate its callee
|
||||||
|
*/
|
||||||
|
function Component({value}) {
|
||||||
|
const arr = [{value: 'foo'}, {value: 'bar'}, {value}];
|
||||||
|
useIdentity();
|
||||||
|
const derived = Array.from(arr, (x, idx) => ({...x, id: idx}));
|
||||||
|
return <Stringify>{derived.at(-1)}</Stringify>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const FIXTURE_ENTRYPOINT = {
|
||||||
|
fn: Component,
|
||||||
|
params: [{value: 5}],
|
||||||
|
sequentialRenders: [{value: 5}, {value: 6}, {value: 6}],
|
||||||
|
};
|
||||||
@@ -0,0 +1,88 @@
|
|||||||
|
|
||||||
|
## Input
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
import {useIdentity, Stringify} from 'shared-runtime';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO: Note that this `Array.from` is inferred to be mutating its first
|
||||||
|
* argument. This is because React Compiler's typing system does not yet support
|
||||||
|
* annotating a function with a set of argument match cases + distinct
|
||||||
|
* definitions (polymorphism)
|
||||||
|
*
|
||||||
|
* In this case, we should be able to infer that the `Array.from` call is
|
||||||
|
* not mutating its 0th argument.
|
||||||
|
* The 0th argument should be typed as having `effect:Mutate` only when
|
||||||
|
* (1) it might be a mutable iterable or
|
||||||
|
* (2) the 1st argument might mutate its callee
|
||||||
|
*/
|
||||||
|
function Component({value}) {
|
||||||
|
const arr = [{value: 'foo'}, {value: 'bar'}, {value}];
|
||||||
|
useIdentity();
|
||||||
|
const derived = Array.from(arr);
|
||||||
|
return <Stringify>{derived.at(-1)}</Stringify>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const FIXTURE_ENTRYPOINT = {
|
||||||
|
fn: Component,
|
||||||
|
params: [{value: 5}],
|
||||||
|
sequentialRenders: [{value: 5}, {value: 6}, {value: 6}],
|
||||||
|
};
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
## Code
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
import { c as _c } from "react/compiler-runtime";
|
||||||
|
import { useIdentity, Stringify } from "shared-runtime";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO: Note that this `Array.from` is inferred to be mutating its first
|
||||||
|
* argument. This is because React Compiler's typing system does not yet support
|
||||||
|
* annotating a function with a set of argument match cases + distinct
|
||||||
|
* definitions (polymorphism)
|
||||||
|
*
|
||||||
|
* In this case, we should be able to infer that the `Array.from` call is
|
||||||
|
* not mutating its 0th argument.
|
||||||
|
* The 0th argument should be typed as having `effect:Mutate` only when
|
||||||
|
* (1) it might be a mutable iterable or
|
||||||
|
* (2) the 1st argument might mutate its callee
|
||||||
|
*/
|
||||||
|
function Component(t0) {
|
||||||
|
const $ = _c(4);
|
||||||
|
const { value } = t0;
|
||||||
|
const arr = [{ value: "foo" }, { value: "bar" }, { value }];
|
||||||
|
useIdentity();
|
||||||
|
const derived = Array.from(arr);
|
||||||
|
let t1;
|
||||||
|
if ($[0] !== derived) {
|
||||||
|
t1 = derived.at(-1);
|
||||||
|
$[0] = derived;
|
||||||
|
$[1] = t1;
|
||||||
|
} else {
|
||||||
|
t1 = $[1];
|
||||||
|
}
|
||||||
|
let t2;
|
||||||
|
if ($[2] !== t1) {
|
||||||
|
t2 = <Stringify>{t1}</Stringify>;
|
||||||
|
$[2] = t1;
|
||||||
|
$[3] = t2;
|
||||||
|
} else {
|
||||||
|
t2 = $[3];
|
||||||
|
}
|
||||||
|
return t2;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const FIXTURE_ENTRYPOINT = {
|
||||||
|
fn: Component,
|
||||||
|
params: [{ value: 5 }],
|
||||||
|
sequentialRenders: [{ value: 5 }, { value: 6 }, { value: 6 }],
|
||||||
|
};
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
### Eval output
|
||||||
|
(kind: ok) <div>{"children":{"value":5}}</div>
|
||||||
|
<div>{"children":{"value":6}}</div>
|
||||||
|
<div>{"children":{"value":6}}</div>
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
import {useIdentity, Stringify} from 'shared-runtime';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO: Note that this `Array.from` is inferred to be mutating its first
|
||||||
|
* argument. This is because React Compiler's typing system does not yet support
|
||||||
|
* annotating a function with a set of argument match cases + distinct
|
||||||
|
* definitions (polymorphism)
|
||||||
|
*
|
||||||
|
* In this case, we should be able to infer that the `Array.from` call is
|
||||||
|
* not mutating its 0th argument.
|
||||||
|
* The 0th argument should be typed as having `effect:Mutate` only when
|
||||||
|
* (1) it might be a mutable iterable or
|
||||||
|
* (2) the 1st argument might mutate its callee
|
||||||
|
*/
|
||||||
|
function Component({value}) {
|
||||||
|
const arr = [{value: 'foo'}, {value: 'bar'}, {value}];
|
||||||
|
useIdentity();
|
||||||
|
const derived = Array.from(arr);
|
||||||
|
return <Stringify>{derived.at(-1)}</Stringify>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const FIXTURE_ENTRYPOINT = {
|
||||||
|
fn: Component,
|
||||||
|
params: [{value: 5}],
|
||||||
|
sequentialRenders: [{value: 5}, {value: 6}, {value: 6}],
|
||||||
|
};
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
|
||||||
|
## Input
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
import {mutateAndReturn, Stringify, useIdentity} from 'shared-runtime';
|
||||||
|
|
||||||
|
function Component({value}) {
|
||||||
|
const arr = [{value: 'foo'}, {value: 'bar'}, {value}];
|
||||||
|
useIdentity();
|
||||||
|
const derived = Array.from(arr, mutateAndReturn);
|
||||||
|
return <Stringify>{derived.at(-1)}</Stringify>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const FIXTURE_ENTRYPOINT = {
|
||||||
|
fn: Component,
|
||||||
|
params: [{value: 5}],
|
||||||
|
sequentialRenders: [{value: 5}, {value: 6}, {value: 6}],
|
||||||
|
};
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
## Code
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
import { c as _c } from "react/compiler-runtime";
|
||||||
|
import { mutateAndReturn, Stringify, useIdentity } from "shared-runtime";
|
||||||
|
|
||||||
|
function Component(t0) {
|
||||||
|
const $ = _c(4);
|
||||||
|
const { value } = t0;
|
||||||
|
const arr = [{ value: "foo" }, { value: "bar" }, { value }];
|
||||||
|
useIdentity();
|
||||||
|
const derived = Array.from(arr, mutateAndReturn);
|
||||||
|
let t1;
|
||||||
|
if ($[0] !== derived) {
|
||||||
|
t1 = derived.at(-1);
|
||||||
|
$[0] = derived;
|
||||||
|
$[1] = t1;
|
||||||
|
} else {
|
||||||
|
t1 = $[1];
|
||||||
|
}
|
||||||
|
let t2;
|
||||||
|
if ($[2] !== t1) {
|
||||||
|
t2 = <Stringify>{t1}</Stringify>;
|
||||||
|
$[2] = t1;
|
||||||
|
$[3] = t2;
|
||||||
|
} else {
|
||||||
|
t2 = $[3];
|
||||||
|
}
|
||||||
|
return t2;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const FIXTURE_ENTRYPOINT = {
|
||||||
|
fn: Component,
|
||||||
|
params: [{ value: 5 }],
|
||||||
|
sequentialRenders: [{ value: 5 }, { value: 6 }, { value: 6 }],
|
||||||
|
};
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
### Eval output
|
||||||
|
(kind: ok) <div>{"children":{"value":5,"wat0":"joe"}}</div>
|
||||||
|
<div>{"children":{"value":6,"wat0":"joe"}}</div>
|
||||||
|
<div>{"children":{"value":6,"wat0":"joe"}}</div>
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
import {mutateAndReturn, Stringify, useIdentity} from 'shared-runtime';
|
||||||
|
|
||||||
|
function Component({value}) {
|
||||||
|
const arr = [{value: 'foo'}, {value: 'bar'}, {value}];
|
||||||
|
useIdentity();
|
||||||
|
const derived = Array.from(arr, mutateAndReturn);
|
||||||
|
return <Stringify>{derived.at(-1)}</Stringify>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const FIXTURE_ENTRYPOINT = {
|
||||||
|
fn: Component,
|
||||||
|
params: [{value: 5}],
|
||||||
|
sequentialRenders: [{value: 5}, {value: 6}, {value: 6}],
|
||||||
|
};
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
|
||||||
|
## Input
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
function useBar({arg}) {
|
||||||
|
/**
|
||||||
|
* Note that mutableIterator is mutated by the later object spread. Therefore,
|
||||||
|
* `s.values()` should be memoized within the same block as the object spread.
|
||||||
|
* In terms of compiler internals, they should have the same reactive scope.
|
||||||
|
*/
|
||||||
|
const obj = {};
|
||||||
|
const s = new Set([obj, 5, 4]);
|
||||||
|
const mutableIterator = s.values();
|
||||||
|
const arr = [...mutableIterator];
|
||||||
|
|
||||||
|
obj.x = arg;
|
||||||
|
return arr;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const FIXTURE_ENTRYPOINT = {
|
||||||
|
fn: useBar,
|
||||||
|
params: [{arg: 3}],
|
||||||
|
sequentialRenders: [{arg: 3}, {arg: 3}, {arg: 4}],
|
||||||
|
};
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
## Code
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
import { c as _c } from "react/compiler-runtime";
|
||||||
|
function useBar(t0) {
|
||||||
|
const $ = _c(2);
|
||||||
|
const { arg } = t0;
|
||||||
|
let arr;
|
||||||
|
if ($[0] !== arg) {
|
||||||
|
const obj = {};
|
||||||
|
const s = new Set([obj, 5, 4]);
|
||||||
|
const mutableIterator = s.values();
|
||||||
|
arr = [...mutableIterator];
|
||||||
|
|
||||||
|
obj.x = arg;
|
||||||
|
$[0] = arg;
|
||||||
|
$[1] = arr;
|
||||||
|
} else {
|
||||||
|
arr = $[1];
|
||||||
|
}
|
||||||
|
return arr;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const FIXTURE_ENTRYPOINT = {
|
||||||
|
fn: useBar,
|
||||||
|
params: [{ arg: 3 }],
|
||||||
|
sequentialRenders: [{ arg: 3 }, { arg: 3 }, { arg: 4 }],
|
||||||
|
};
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
### Eval output
|
||||||
|
(kind: ok) [{"x":3},5,4]
|
||||||
|
[{"x":3},5,4]
|
||||||
|
[{"x":4},5,4]
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
function useBar({arg}) {
|
||||||
|
/**
|
||||||
|
* Note that mutableIterator is mutated by the later object spread. Therefore,
|
||||||
|
* `s.values()` should be memoized within the same block as the object spread.
|
||||||
|
* In terms of compiler internals, they should have the same reactive scope.
|
||||||
|
*/
|
||||||
|
const obj = {};
|
||||||
|
const s = new Set([obj, 5, 4]);
|
||||||
|
const mutableIterator = s.values();
|
||||||
|
const arr = [...mutableIterator];
|
||||||
|
|
||||||
|
obj.x = arg;
|
||||||
|
return arr;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const FIXTURE_ENTRYPOINT = {
|
||||||
|
fn: useBar,
|
||||||
|
params: [{arg: 3}],
|
||||||
|
sequentialRenders: [{arg: 3}, {arg: 3}, {arg: 4}],
|
||||||
|
};
|
||||||
@@ -55,26 +55,20 @@ import { c as _c } from "react/compiler-runtime"; /**
|
|||||||
|
|
||||||
function useBar(t0) {
|
function useBar(t0) {
|
||||||
"use memo";
|
"use memo";
|
||||||
const $ = _c(3);
|
const $ = _c(2);
|
||||||
const { arg } = t0;
|
const { arg } = t0;
|
||||||
let t1;
|
let t1;
|
||||||
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
|
if ($[0] !== arg) {
|
||||||
const s = new Set([1, 5, 4]);
|
const s = new Set([1, 5, 4]);
|
||||||
t1 = s.values();
|
const mutableIterator = s.values();
|
||||||
$[0] = t1;
|
|
||||||
|
t1 = [arg, ...mutableIterator];
|
||||||
|
$[0] = arg;
|
||||||
|
$[1] = t1;
|
||||||
} else {
|
} else {
|
||||||
t1 = $[0];
|
t1 = $[1];
|
||||||
}
|
}
|
||||||
const mutableIterator = t1;
|
return t1;
|
||||||
let t2;
|
|
||||||
if ($[1] !== arg) {
|
|
||||||
t2 = [arg, ...mutableIterator];
|
|
||||||
$[1] = arg;
|
|
||||||
$[2] = t2;
|
|
||||||
} else {
|
|
||||||
t2 = $[2];
|
|
||||||
}
|
|
||||||
return t2;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const FIXTURE_ENTRYPOINT = {
|
export const FIXTURE_ENTRYPOINT = {
|
||||||
@@ -84,4 +78,8 @@ export const FIXTURE_ENTRYPOINT = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Eval output
|
||||||
|
(kind: ok) [3,1,5,4]
|
||||||
|
[3,1,5,4]
|
||||||
|
[4,1,5,4]
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
|
||||||
|
## Input
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
import {useIdentity} from 'shared-runtime';
|
||||||
|
|
||||||
|
function useFoo() {
|
||||||
|
const it = new Set([1, 2]).values();
|
||||||
|
useIdentity();
|
||||||
|
return Math.max(...it);
|
||||||
|
}
|
||||||
|
|
||||||
|
export const FIXTURE_ENTRYPOINT = {
|
||||||
|
fn: useFoo,
|
||||||
|
params: [{}],
|
||||||
|
sequentialRenders: [{}, {}],
|
||||||
|
};
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
## Code
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
import { useIdentity } from "shared-runtime";
|
||||||
|
|
||||||
|
function useFoo() {
|
||||||
|
const it = new Set([1, 2]).values();
|
||||||
|
useIdentity();
|
||||||
|
return Math.max(...it);
|
||||||
|
}
|
||||||
|
|
||||||
|
export const FIXTURE_ENTRYPOINT = {
|
||||||
|
fn: useFoo,
|
||||||
|
params: [{}],
|
||||||
|
sequentialRenders: [{}, {}],
|
||||||
|
};
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
### Eval output
|
||||||
|
(kind: ok) 2
|
||||||
|
2
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
import {useIdentity} from 'shared-runtime';
|
||||||
|
|
||||||
|
function useFoo() {
|
||||||
|
const it = new Set([1, 2]).values();
|
||||||
|
useIdentity();
|
||||||
|
return Math.max(...it);
|
||||||
|
}
|
||||||
|
|
||||||
|
export const FIXTURE_ENTRYPOINT = {
|
||||||
|
fn: useFoo,
|
||||||
|
params: [{}],
|
||||||
|
sequentialRenders: [{}, {}],
|
||||||
|
};
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
|
||||||
|
## Input
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
import {useIdentity} from 'shared-runtime';
|
||||||
|
|
||||||
|
function Component() {
|
||||||
|
const items = makeArray(0, 1, 2, null, 4, false, 6);
|
||||||
|
return useIdentity(...items.values());
|
||||||
|
}
|
||||||
|
|
||||||
|
export const FIXTURE_ENTRYPOINT = {
|
||||||
|
fn: Component,
|
||||||
|
params: [],
|
||||||
|
sequentialRenders: [{}, {}],
|
||||||
|
};
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## Error
|
||||||
|
|
||||||
|
```
|
||||||
|
3 | function Component() {
|
||||||
|
4 | const items = makeArray(0, 1, 2, null, 4, false, 6);
|
||||||
|
> 5 | return useIdentity(...items.values());
|
||||||
|
| ^^^^^^^^^^^^^^ Todo: Support spread syntax for hook arguments (5:5)
|
||||||
|
6 | }
|
||||||
|
7 |
|
||||||
|
8 | export const FIXTURE_ENTRYPOINT = {
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
import {useIdentity} from 'shared-runtime';
|
||||||
|
|
||||||
|
function Component() {
|
||||||
|
const items = makeArray(0, 1, 2, null, 4, false, 6);
|
||||||
|
return useIdentity(...items.values());
|
||||||
|
}
|
||||||
|
|
||||||
|
export const FIXTURE_ENTRYPOINT = {
|
||||||
|
fn: Component,
|
||||||
|
params: [],
|
||||||
|
sequentialRenders: [{}, {}],
|
||||||
|
};
|
||||||
@@ -4,9 +4,10 @@
|
|||||||
```javascript
|
```javascript
|
||||||
import {makeArray} from 'shared-runtime';
|
import {makeArray} from 'shared-runtime';
|
||||||
|
|
||||||
function Component(props) {
|
const other = [0, 1];
|
||||||
|
function Component({}) {
|
||||||
const items = makeArray(0, 1, 2, null, 4, false, 6);
|
const items = makeArray(0, 1, 2, null, 4, false, 6);
|
||||||
const max = Math.max(...items.filter(Boolean));
|
const max = Math.max(2, items.push(5), ...other);
|
||||||
return max;
|
return max;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -21,13 +22,13 @@ export const FIXTURE_ENTRYPOINT = {
|
|||||||
## Error
|
## Error
|
||||||
|
|
||||||
```
|
```
|
||||||
3 | function Component(props) {
|
4 | function Component({}) {
|
||||||
4 | const items = makeArray(0, 1, 2, null, 4, false, 6);
|
5 | const items = makeArray(0, 1, 2, null, 4, false, 6);
|
||||||
> 5 | const max = Math.max(...items.filter(Boolean));
|
> 6 | const max = Math.max(2, items.push(5), ...other);
|
||||||
| ^^^^^^^^ Invariant: [Codegen] Internal error: MethodCall::property must be an unpromoted + unmemoized MemberExpression. Got a `Identifier` (5:5)
|
| ^^^^^^^^ Invariant: [Codegen] Internal error: MethodCall::property must be an unpromoted + unmemoized MemberExpression. Got a `Identifier` (6:6)
|
||||||
6 | return max;
|
7 | return max;
|
||||||
7 | }
|
8 | }
|
||||||
8 |
|
9 |
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
@@ -1,8 +1,9 @@
|
|||||||
import {makeArray} from 'shared-runtime';
|
import {makeArray} from 'shared-runtime';
|
||||||
|
|
||||||
function Component(props) {
|
const other = [0, 1];
|
||||||
|
function Component({}) {
|
||||||
const items = makeArray(0, 1, 2, null, 4, false, 6);
|
const items = makeArray(0, 1, 2, null, 4, false, 6);
|
||||||
const max = Math.max(...items.filter(Boolean));
|
const max = Math.max(2, items.push(5), ...other);
|
||||||
return max;
|
return max;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,61 @@
|
|||||||
|
|
||||||
|
## Input
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
import {useFragment} from 'shared-runtime';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* React compiler should infer that the returned value is a primitive and avoid
|
||||||
|
* memoizing it.
|
||||||
|
*/
|
||||||
|
function useRelayData({query, idx}) {
|
||||||
|
'use memo';
|
||||||
|
const data = useFragment('', query);
|
||||||
|
return data.a[idx].toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
export const FIXTURE_ENTRYPOINT = {
|
||||||
|
fn: useRelayData,
|
||||||
|
params: [{query: '', idx: 0}],
|
||||||
|
sequentialRenders: [
|
||||||
|
{query: '', idx: 0},
|
||||||
|
{query: '', idx: 0},
|
||||||
|
{query: '', idx: 1},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
## Code
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
import { useFragment } from "shared-runtime";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* React compiler should infer that the returned value is a primitive and avoid
|
||||||
|
* memoizing it.
|
||||||
|
*/
|
||||||
|
function useRelayData(t0) {
|
||||||
|
"use memo";
|
||||||
|
const { query, idx } = t0;
|
||||||
|
|
||||||
|
const data = useFragment("", query);
|
||||||
|
return data.a[idx].toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
export const FIXTURE_ENTRYPOINT = {
|
||||||
|
fn: useRelayData,
|
||||||
|
params: [{ query: "", idx: 0 }],
|
||||||
|
sequentialRenders: [
|
||||||
|
{ query: "", idx: 0 },
|
||||||
|
{ query: "", idx: 0 },
|
||||||
|
{ query: "", idx: 1 },
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
### Eval output
|
||||||
|
(kind: ok) "1"
|
||||||
|
"1"
|
||||||
|
"2"
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
import {useFragment} from 'shared-runtime';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* React compiler should infer that the returned value is a primitive and avoid
|
||||||
|
* memoizing it.
|
||||||
|
*/
|
||||||
|
function useRelayData({query, idx}) {
|
||||||
|
'use memo';
|
||||||
|
const data = useFragment('', query);
|
||||||
|
return data.a[idx].toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
export const FIXTURE_ENTRYPOINT = {
|
||||||
|
fn: useRelayData,
|
||||||
|
params: [{query: '', idx: 0}],
|
||||||
|
sequentialRenders: [
|
||||||
|
{query: '', idx: 0},
|
||||||
|
{query: '', idx: 0},
|
||||||
|
{query: '', idx: 1},
|
||||||
|
],
|
||||||
|
};
|
||||||
@@ -68,21 +68,29 @@ function Validate({ x, input }) {
|
|||||||
}
|
}
|
||||||
function useFoo(input) {
|
function useFoo(input) {
|
||||||
"use memo";
|
"use memo";
|
||||||
const $ = _c(3);
|
const $ = _c(5);
|
||||||
|
|
||||||
const x = Array.from([{}]);
|
const x = Array.from([{}]);
|
||||||
useIdentity();
|
useIdentity();
|
||||||
x.push([input]);
|
|
||||||
let t0;
|
let t0;
|
||||||
if ($[0] !== input || $[1] !== x) {
|
if ($[0] !== input) {
|
||||||
t0 = <Validate x={x} input={input} />;
|
t0 = [input];
|
||||||
$[0] = input;
|
$[0] = input;
|
||||||
$[1] = x;
|
$[1] = t0;
|
||||||
$[2] = t0;
|
|
||||||
} else {
|
} else {
|
||||||
t0 = $[2];
|
t0 = $[1];
|
||||||
}
|
}
|
||||||
return t0;
|
x.push(t0);
|
||||||
|
let t1;
|
||||||
|
if ($[2] !== input || $[3] !== x) {
|
||||||
|
t1 = <Validate x={x} input={input} />;
|
||||||
|
$[2] = input;
|
||||||
|
$[3] = x;
|
||||||
|
$[4] = t1;
|
||||||
|
} else {
|
||||||
|
t1 = $[4];
|
||||||
|
}
|
||||||
|
return t1;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const FIXTURE_ENTRYPOINT = {
|
export const FIXTURE_ENTRYPOINT = {
|
||||||
|
|||||||
@@ -37,6 +37,12 @@ function useFoo({val1, val2}) {
|
|||||||
export const FIXTURE_ENTRYPOINT = {
|
export const FIXTURE_ENTRYPOINT = {
|
||||||
fn: useFoo,
|
fn: useFoo,
|
||||||
params: [{val1: 1, val2: 2}],
|
params: [{val1: 1, val2: 2}],
|
||||||
|
params: [
|
||||||
|
{val1: 1, val2: 2},
|
||||||
|
{val1: 1, val2: 2},
|
||||||
|
{val1: 1, val2: 3},
|
||||||
|
{val1: 4, val2: 2},
|
||||||
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
```
|
```
|
||||||
@@ -71,29 +77,51 @@ function Validate({ x, val1, val2 }) {
|
|||||||
}
|
}
|
||||||
function useFoo(t0) {
|
function useFoo(t0) {
|
||||||
"use memo";
|
"use memo";
|
||||||
const $ = _c(4);
|
const $ = _c(8);
|
||||||
const { val1, val2 } = t0;
|
const { val1, val2 } = t0;
|
||||||
|
|
||||||
const x = Array.from([]);
|
const x = Array.from([]);
|
||||||
useIdentity();
|
useIdentity();
|
||||||
x.push([val1]);
|
|
||||||
x.push([val2]);
|
|
||||||
let t1;
|
let t1;
|
||||||
if ($[0] !== val1 || $[1] !== val2 || $[2] !== x) {
|
if ($[0] !== val1) {
|
||||||
t1 = <Validate x={x} val1={val1} val2={val2} />;
|
t1 = [val1];
|
||||||
$[0] = val1;
|
$[0] = val1;
|
||||||
$[1] = val2;
|
$[1] = t1;
|
||||||
$[2] = x;
|
|
||||||
$[3] = t1;
|
|
||||||
} else {
|
} else {
|
||||||
t1 = $[3];
|
t1 = $[1];
|
||||||
}
|
}
|
||||||
return t1;
|
x.push(t1);
|
||||||
|
let t2;
|
||||||
|
if ($[2] !== val2) {
|
||||||
|
t2 = [val2];
|
||||||
|
$[2] = val2;
|
||||||
|
$[3] = t2;
|
||||||
|
} else {
|
||||||
|
t2 = $[3];
|
||||||
|
}
|
||||||
|
x.push(t2);
|
||||||
|
let t3;
|
||||||
|
if ($[4] !== val1 || $[5] !== val2 || $[6] !== x) {
|
||||||
|
t3 = <Validate x={x} val1={val1} val2={val2} />;
|
||||||
|
$[4] = val1;
|
||||||
|
$[5] = val2;
|
||||||
|
$[6] = x;
|
||||||
|
$[7] = t3;
|
||||||
|
} else {
|
||||||
|
t3 = $[7];
|
||||||
|
}
|
||||||
|
return t3;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const FIXTURE_ENTRYPOINT = {
|
export const FIXTURE_ENTRYPOINT = {
|
||||||
fn: useFoo,
|
fn: useFoo,
|
||||||
params: [{ val1: 1, val2: 2 }],
|
params: [{ val1: 1, val2: 2 }],
|
||||||
|
params: [
|
||||||
|
{ val1: 1, val2: 2 },
|
||||||
|
{ val1: 1, val2: 2 },
|
||||||
|
{ val1: 1, val2: 3 },
|
||||||
|
{ val1: 4, val2: 2 },
|
||||||
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
```
|
```
|
||||||
@@ -33,4 +33,10 @@ function useFoo({val1, val2}) {
|
|||||||
export const FIXTURE_ENTRYPOINT = {
|
export const FIXTURE_ENTRYPOINT = {
|
||||||
fn: useFoo,
|
fn: useFoo,
|
||||||
params: [{val1: 1, val2: 2}],
|
params: [{val1: 1, val2: 2}],
|
||||||
|
params: [
|
||||||
|
{val1: 1, val2: 2},
|
||||||
|
{val1: 1, val2: 2},
|
||||||
|
{val1: 1, val2: 3},
|
||||||
|
{val1: 4, val2: 2},
|
||||||
|
],
|
||||||
};
|
};
|
||||||
@@ -462,7 +462,6 @@ const skipFilter = new Set([
|
|||||||
|
|
||||||
// bugs
|
// bugs
|
||||||
'bug-object-expression-computed-key-modified-during-after-construction-hoisted-sequence-expr',
|
'bug-object-expression-computed-key-modified-during-after-construction-hoisted-sequence-expr',
|
||||||
'bug-array-spread-mutable-iterator',
|
|
||||||
`bug-capturing-func-maybealias-captured-mutate`,
|
`bug-capturing-func-maybealias-captured-mutate`,
|
||||||
'bug-aliased-capture-aliased-mutate',
|
'bug-aliased-capture-aliased-mutate',
|
||||||
'bug-aliased-capture-mutate',
|
'bug-aliased-capture-mutate',
|
||||||
|
|||||||
Reference in New Issue
Block a user