@samitouri / QOS-React-2 / commits / ffc1b5b700

[be] More comments for ObjectShapes; rename BuiltIn shapes

Mofei Zhang committed Mar 30, 2023 at 15:03 UTC ffc1b5b7006ccfd7e1739d939be144b30aff92bc
6 files changed +86 -28
compiler/forget/src/HIR/Globals.ts
+8
@@ -9,6 +9,14 @@ import { Effect, ValueKind } from "./HIR";
9 import { Hook } from "./Hooks";
10 import { BuiltInType, HookType, PolyType } from "./Types";
11
12 +/**
13 + * This file exports types and defaults for JavaScript global objects.
14 + * A Forget `Environment` stores the GlobalRegistry and ShapeRegistry
15 + * used for the current project. These ultimately help Forget refine
16 + * its inference of types (i.e. Object vs Primitive) and effects
17 + * (i.e. read vs mutate) in source programs.
18 + */
19 +
20 // Hack until we add ObjectShapes for all globals
21 const UNTYPED_GLOBALS: Set<string> = new Set([
22 "String",
compiler/forget/src/HIR/ObjectShape.ts
+67 -21
@@ -6,20 +6,37 @@
6 */
7 import invariant from "invariant";
8 import { Effect } from "./HIR";
9 -import { BuiltInType, FunctionType, PolyType, PrimitiveType } from "./Types";
9 +import {
10 + BuiltInType,
11 + FunctionType,
12 + ObjectType,
13 + PolyType,
14 + PrimitiveType,
15 +} from "./Types";
16 +
17 +/**
18 + * This file exports types and defaults for JavaScript object shapes. These are
19 + * stored and used by a Forget `Environment`. See comments in `Types.ts`,
20 + * `Globals.ts`, and `Environment.ts` for more details.
21 + */
22
23 const PRIMITIVE_TYPE: PrimitiveType = {
24 kind: "Primitive",
25 };
26
27 let nextAnonId = 0;
16 -// use strings since they are easily debuggable, even though `Symbol()`
17 -// might be more performant
28 +// We currently use strings for anonymous ShapeIds since they are easily
29 +// debuggable, even though `Symbol()` might be more performant
30 function createAnonId(): string {
31 return `<generated_${nextAnonId++}>`;
32 }
33
22 -function addFunction(
34 +/**
35 + * Add a function to an existing ShapeRegistry.
36 + *
37 + * @returns a {@link FunctionType} representing the added function.
38 + */
39 +export function addFunction(
40 registry: ShapeRegistry,
41 properties: Iterable<[string, BuiltInType | null]>,
42 fn: FunctionSignature
@@ -33,15 +50,33 @@ function addFunction(
50 };
51 }
52
53 +/**
54 + * Add an object to an existing ShapeRegistry.
55 + *
56 + * @returns an {@link ObjectType} representing the added object.
57 + */
58 +export function addObject(
59 + registry: ShapeRegistry,
60 + id: string | null,
61 + properties: Iterable<[string, BuiltInType | null]>
62 +): ObjectType {
63 + const shapeId = id ?? createAnonId();
64 + addShape(registry, shapeId, properties, null);
65 + return {
66 + kind: "Object",
67 + shapeId,
68 + };
69 +}
70 +
71 function addShape(
72 registry: ShapeRegistry,
73 id: string,
74 properties: Iterable<[string, BuiltInType | null]>,
40 - functionType?: FunctionSignature
75 + functionType: FunctionSignature | null
76 ): ObjectShape {
77 const shape: ObjectShape = {
78 properties: new Map(properties),
44 - functionType: functionType ?? null,
79 + functionType,
80 };
81
82 invariant(
@@ -52,10 +87,14 @@ function addShape(
87 return shape;
88 }
89
55 -// Param type not recorded since it currently does not affect inference.
56 -// Specifically, we currently do not:
57 -// - infer types based on their usage in argument position
58 -// - handle inference for overloaded / generic functions
90 +/**
91 + * Call signature of a function, used for type and effect inference.
92 + *
93 + * Note: Param type is not recorded since it currently does not affect inference.
94 + * Specifically, we currently do not:
95 + * - infer types based on their usage in argument position
96 + * - handle inference for overloaded / generic functions
97 + */
98 export type FunctionSignature = {
99 positionalParams: Array<Effect>;
100 restParam: Effect | null;
@@ -63,27 +102,34 @@ export type FunctionSignature = {
102 calleeEffect: Effect;
103 };
104
105 +/**
106 + * Shape of an {@link FunctionType} if {@link ObjectShape.functionType} is present,
107 + * or {@link ObjectType} otherwise.
108 + *
109 + * Constructors (e.g. the global `Array` object) and other functions (e.g. `Math.min`)
110 + * are both represented by {@link ObjectShape.functionType}.
111 + */
112 export type ObjectShape = {
67 - // TODO(gsn): When can the key be null here?
113 properties: Map<string, BuiltInType | null>;
69 - // TODO(gsn): Why do Objects have a `functionType`? Oh, this the constructor.
70 - // Let's rename to constructor?
114 functionType: FunctionSignature | null;
115 };
116
117 +/**
118 + * Every valid ShapeRegistry must contain ObjectShape definitions for
119 + * {@link BuiltInArrayId} and {@link BuiltInObjectId}, since these are the
120 + * the inferred types for [] and {}.
121 + */
122 export type ShapeRegistry = Map<string, ObjectShape>;
123 +export const BuiltInArrayId = "BuiltInArray";
124 +export const BuiltInObjectId = "BuiltInObject";
125
126 /**
77 - * Shapes of built-in types
127 + * ShapeRegistry with default definitions for built-ins.
128 */
79 -
80 -// The only "entrypoints" should be Globals and recursive lookups from properties / functions
129 export const BUILTIN_SHAPES: ShapeRegistry = new Map();
82 -export const ArrayShapeId = "Array";
83 -export const ObjectShapeId = "Object";
130
131 /* Built-in array shape */
86 -addShape(BUILTIN_SHAPES, ArrayShapeId, [
132 +addObject(BUILTIN_SHAPES, BuiltInArrayId, [
133 [
134 "at",
135 addFunction(BUILTIN_SHAPES, [], {
@@ -100,7 +146,7 @@ addShape(BUILTIN_SHAPES, ArrayShapeId, [
146 restParam: Effect.Capture,
147 returnType: {
148 kind: "Object",
103 - shapeId: ArrayShapeId,
149 + shapeId: BuiltInArrayId,
150 },
151 calleeEffect: Effect.Read,
152 }),
@@ -119,7 +165,7 @@ addShape(BUILTIN_SHAPES, ArrayShapeId, [
165 ]);
166
167 /* Built-in Object shape */
122 -addShape(BUILTIN_SHAPES, ObjectShapeId, [
168 +addObject(BUILTIN_SHAPES, BuiltInObjectId, [
169 [
170 "toString",
171 addFunction(BUILTIN_SHAPES, [], {
compiler/forget/src/HIR/Types.ts
+6 -2
@@ -28,9 +28,13 @@ export type HookType = {
28 * subtly different from JS language semantics - `shape` represents both
29 * OwnPropertyDescriptors and properties present in the prototype chain.
30 *
31 - * In addition, a {@link FunctionType} may be associated with an inferred signature,
31 + * {@link ObjectShape.functionType} is always present on the shape of a {@link FunctionType},
32 + * and it represents the call signature of the function. Note that Forget thinks of a
33 + * {@link FunctionType} as any "callable object" (not to be confused with objects that
34 + * extend the global `Function`.)
35 *
33 - * If `shapeId` is present, it is a key into the global ShapeRegistry.
36 + * If `shapeId` is present, it is a key into the ShapeRegistry used to infer this
37 + * FunctionType or ObjectType instance (i.e. from an Environment).
38 */
39
40 export type FunctionType = {
compiler/forget/src/TypeInference/InferTypes.ts
+3 -3
@@ -10,7 +10,7 @@ import {
10 TypeId,
11 TypeVar,
12 } from "../HIR/HIR";
13 -import { ArrayShapeId, ObjectShapeId } from "../HIR/ObjectShape";
13 +import { BuiltInArrayId, BuiltInObjectId } from "../HIR/ObjectShape";
14 import { eachInstructionLValue, eachInstructionOperand } from "../HIR/visitors";
15
16 function isPrimitiveBinaryOp(op: t.BinaryExpression["operator"]): boolean {
@@ -158,12 +158,12 @@ function* generateInstructionTypes(
158 }
159
160 case "ObjectExpression": {
161 - yield equation(left, { kind: "Object", shapeId: ObjectShapeId });
161 + yield equation(left, { kind: "Object", shapeId: BuiltInObjectId });
162 break;
163 }
164
165 case "ArrayExpression": {
166 - yield equation(left, { kind: "Object", shapeId: ArrayShapeId });
166 + yield equation(left, { kind: "Object", shapeId: BuiltInArrayId });
167 break;
168 }
169
compiler/forget/src/__tests__/fixtures/compiler/error.mutate-after-aliased-freeze.expect.md
+1 -1
@@ -25,7 +25,7 @@ function Component(props) {
25 ## Error
26
27 ```
28 -[ReactForget] InvalidInput: InferReferenceEffects: inferred mutation of known immutable value $42:TObject<Array> (frozen) (13:13)
28 +[ReactForget] InvalidInput: InferReferenceEffects: inferred mutation of known immutable value $42:TObject<BuiltInArray> (frozen) (13:13)
29 ```
30
31
\ No newline at end of file
compiler/forget/src/__tests__/fixtures/compiler/error.mutate-after-freeze.expect.md
+1 -1
@@ -19,7 +19,7 @@ function Component(props) {
19 ## Error
20
21 ```
22 -[ReactForget] InvalidInput: InferReferenceEffects: inferred mutation of known immutable value $25:TObject<Array> (frozen) (7:7)
22 +[ReactForget] InvalidInput: InferReferenceEffects: inferred mutation of known immutable value $25:TObject<BuiltInArray> (frozen) (7:7)
23 ```
24
25
\ No newline at end of file