[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