Use type assertions to drive inference
This PR uses the information from type cast expressions (`as` or `(variable: type)`) to inform type inference. BuildHIR converts the type annotation to our internal type format where possible, falling back to the generic `makeType()`. This is then used in InferTypes to help set the value's type.
Joe Savona committed
Dec 11, 2023 at 11:34 UTC
8b3898c164837ecc4d5d69a4e84ccb99375c31b0
10 files changed
+124
-53
compiler/packages/babel-plugin-react-forget/src/HIR/BuildHIR.ts
+57
-4
@@ -41,10 +41,12 @@ import {
41
SourceLocation,
42
SpreadPattern,
43
ThrowTerminal,
44
+ Type,
45
makeInstructionId,
46
makeType,
47
} from "./HIR";
48
import HIRBuilder, { Bindings } from "./HIRBuilder";
49
+import { BuiltInArrayId } from "./ObjectShape";
50
51
/*
52
* *******************************************************************************************
@@ -2148,21 +2150,23 @@ function lowerExpression(
2150
}
2151
case "TypeCastExpression": {
2152
let expr = exprPath as NodePath<t.TypeCastExpression>;
2153
+ const typeAnnotation = expr.get("typeAnnotation").get("typeAnnotation");
2154
return {
2155
kind: "TypeCastExpression",
2156
value: lowerExpressionToTemporary(builder, expr.get("expression")),
2154
- typeAnnotation: expr.get("typeAnnotation").get("typeAnnotation").node,
2155
- type: makeType(),
2157
+ typeAnnotation: typeAnnotation.node,
2158
+ type: lowerType(builder, typeAnnotation),
2159
loc: exprLoc,
2160
};
2161
}
2162
case "TSAsExpression": {
2163
let expr = exprPath as NodePath<t.TSAsExpression>;
2164
+ const typeAnnotation = expr.get("typeAnnotation");
2165
return {
2166
kind: "TypeCastExpression",
2167
value: lowerExpressionToTemporary(builder, expr.get("expression")),
2164
- typeAnnotation: expr.get("typeAnnotation").node,
2165
- type: makeType(),
2168
+ typeAnnotation: typeAnnotation.node,
2169
+ type: lowerType(builder, typeAnnotation),
2170
loc: exprLoc,
2171
};
2172
}
@@ -3791,3 +3795,52 @@ function gatherCapturedDeps(
3795
function notNull<T>(value: T | null): value is T {
3796
return value !== null;
3797
}
3798
+
3799
+function lowerType(
3800
+ _builder: HIRBuilder,
3801
+ path: NodePath<t.FlowType | t.TSType>
3802
+): Type {
3803
+ const node = path.node;
3804
+ switch (node.type) {
3805
+ case "GenericTypeAnnotation": {
3806
+ const typeAnnotation = path as NodePath<t.GenericTypeAnnotation>;
3807
+ const id = typeAnnotation.get("id");
3808
+ if (id.node.type === "Identifier" && id.node.name === "Array") {
3809
+ return { kind: "Object", shapeId: BuiltInArrayId };
3810
+ }
3811
+ return makeType();
3812
+ }
3813
+ case "TSTypeReference": {
3814
+ const typeReference = path as NodePath<t.TSTypeReference>;
3815
+ const typeName = typeReference.get("typeName").node;
3816
+ if (typeName.type === "Identifier" && typeName.name === "Array") {
3817
+ return { kind: "Object", shapeId: BuiltInArrayId };
3818
+ }
3819
+ return makeType();
3820
+ }
3821
+ case "ArrayTypeAnnotation":
3822
+ case "TSArrayType": {
3823
+ return { kind: "Object", shapeId: BuiltInArrayId };
3824
+ }
3825
+ case "BooleanLiteralTypeAnnotation":
3826
+ case "BooleanTypeAnnotation":
3827
+ case "NullLiteralTypeAnnotation":
3828
+ case "NumberLiteralTypeAnnotation":
3829
+ case "NumberTypeAnnotation":
3830
+ case "StringLiteralTypeAnnotation":
3831
+ case "StringTypeAnnotation":
3832
+ case "TSBooleanKeyword":
3833
+ case "TSNullKeyword":
3834
+ case "TSNumberKeyword":
3835
+ case "TSStringKeyword":
3836
+ case "TSSymbolKeyword":
3837
+ case "TSUndefinedKeyword":
3838
+ case "TSVoidKeyword":
3839
+ case "VoidTypeAnnotation": {
3840
+ return { kind: "Primitive" };
3841
+ }
3842
+ default: {
3843
+ return makeType();
3844
+ }
3845
+ }
3846
+}
compiler/packages/babel-plugin-react-forget/src/HIR/PrintHIR.ts
+3
-1
@@ -373,7 +373,9 @@ export function printInstructionValue(instrValue: ReactiveValue): string {
373
break;
374
}
375
case "TypeCastExpression": {
376
- value = `TypeCast ${printPlace(instrValue.value)}`;
376
+ value = `TypeCast ${printPlace(instrValue.value)}: ${printType(
377
+ instrValue.type
378
+ )}`;
379
break;
380
}
381
case "JsxExpression": {
compiler/packages/babel-plugin-react-forget/src/TypeInference/InferTypes.ts
+2
-1
@@ -262,7 +262,8 @@ function* generateInstructionTypes(
262
}
263
264
case "TypeCastExpression": {
265
- yield equation(left, value.value.identifier.type);
265
+ yield equation(value.type, value.value.identifier.type);
266
+ yield equation(left, value.type);
267
break;
268
}
269
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/type-annotations/type-annotation-as-array.expect.md
+31
-10
@@ -2,14 +2,16 @@
2
## Input
3
4
```javascript
5
-import { identity } from "shared-runtime";
6
-
5
function Component(props: { id: number }) {
8
- const x = [props.id] as number[];
9
- const y = identity(x[0]);
6
+ const x = makeArray(props.id) as number[];
7
+ const y = x.at(0);
8
return y;
9
}
10
11
+function makeArray<T>(x: T): Array<T> {
12
+ return [x];
13
+}
14
+
15
export const FIXTURE_ENTRYPOINT = {
16
fn: Component,
17
params: [{ id: 42 }],
@@ -21,23 +23,42 @@ export const FIXTURE_ENTRYPOINT = {
23
24
```javascript
25
import { unstable_useMemoCache as useMemoCache } from "react";
24
-import { identity } from "shared-runtime";
25
-
26
function Component(props) {
27
- const $ = useMemoCache(2);
27
+ const $ = useMemoCache(4);
28
let t0;
29
if ($[0] !== props.id) {
30
- const x = [props.id] as number[];
31
- t0 = identity(x[0]);
30
+ t0 = makeArray(props.id);
31
$[0] = props.id;
32
$[1] = t0;
33
} else {
34
t0 = $[1];
35
}
37
- const y = t0;
36
+ const x = t0 as number[];
37
+ let t1;
38
+ if ($[2] !== x) {
39
+ t1 = x.at(0);
40
+ $[2] = x;
41
+ $[3] = t1;
42
+ } else {
43
+ t1 = $[3];
44
+ }
45
+ const y = t1;
46
return y;
47
}
48
49
+function makeArray(x) {
50
+ const $ = useMemoCache(2);
51
+ let t0;
52
+ if ($[0] !== x) {
53
+ t0 = [x];
54
+ $[0] = x;
55
+ $[1] = t0;
56
+ } else {
57
+ t0 = $[1];
58
+ }
59
+ return t0;
60
+}
61
+
62
export const FIXTURE_ENTRYPOINT = {
63
fn: Component,
64
params: [{ id: 42 }],
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/type-annotations/type-annotation-as-array.ts
+6
-4
@@ -1,11 +1,13 @@
1
-import { identity } from "shared-runtime";
2
-
1
function Component(props: { id: number }) {
4
- const x = [props.id] as number[];
5
- const y = identity(x[0]);
2
+ const x = makeArray(props.id) as number[];
3
+ const y = x.at(0);
4
return y;
5
}
6
7
+function makeArray<T>(x: T): Array<T> {
8
+ return [x];
9
+}
10
+
11
export const FIXTURE_ENTRYPOINT = {
12
fn: Component,
13
params: [{ id: 42 }],
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/type-annotations/type-annotation-as-array_.flow.expect.md
+16
-8
@@ -3,11 +3,11 @@
3
4
```javascript
5
// @flow
6
-import { identity } from "shared-runtime";
6
+import { identity, makeArray } from "shared-runtime";
7
8
function Component(props: { id: number }) {
9
- const x = ([props.id]: Array<number>);
10
- const y = identity(x[0]);
9
+ const x = (makeArray(props.id): Array<number>);
10
+ const y = x.at(0);
11
return y;
12
}
13
@@ -22,20 +22,28 @@ export const FIXTURE_ENTRYPOINT = {
22
23
```javascript
24
import { unstable_useMemoCache as useMemoCache } from "react";
25
-import { identity } from "shared-runtime";
25
+import { identity, makeArray } from "shared-runtime";
26
27
function Component(props) {
28
- const $ = useMemoCache(2);
28
+ const $ = useMemoCache(4);
29
let t0;
30
if ($[0] !== props.id) {
31
- const x = ([props.id]: Array<number>);
32
- t0 = identity(x[0]);
31
+ t0 = makeArray(props.id);
32
$[0] = props.id;
33
$[1] = t0;
34
} else {
35
t0 = $[1];
36
}
38
- const y = t0;
37
+ const x = (t0: Array<number>);
38
+ let t1;
39
+ if ($[2] !== x) {
40
+ t1 = x.at(0);
41
+ $[2] = x;
42
+ $[3] = t1;
43
+ } else {
44
+ t1 = $[3];
45
+ }
46
+ const y = t1;
47
return y;
48
}
49
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/type-annotations/type-annotation-as-array_.flow.js
+3
-3
@@ -1,9 +1,9 @@
1
// @flow
2
-import { identity } from "shared-runtime";
2
+import { identity, makeArray } from "shared-runtime";
3
4
function Component(props: { id: number }) {
5
- const x = ([props.id]: Array<number>);
6
- const y = identity(x[0]);
5
+ const x = (makeArray(props.id): Array<number>);
6
+ const y = x.at(0);
7
return y;
8
}
9
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/type-annotations/type-annotation-as-number.expect.md
+1
-11
@@ -20,20 +20,10 @@ export const FIXTURE_ENTRYPOINT = {
20
## Code
21
22
```javascript
23
-import { unstable_useMemoCache as useMemoCache } from "react";
23
import { identity } from "shared-runtime";
24
25
function Component(props) {
27
- const $ = useMemoCache(2);
28
- let t0;
29
- if ($[0] !== props.id) {
30
- t0 = identity(props.id);
31
- $[0] = props.id;
32
- $[1] = t0;
33
- } else {
34
- t0 = $[1];
35
- }
36
- const x = t0;
26
+ const x = identity(props.id);
27
const y = x as number;
28
return y;
29
}
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/type-annotations/type-annotation-as-number_.flow.expect.md
+1
-11
@@ -21,20 +21,10 @@ export const FIXTURE_ENTRYPOINT = {
21
## Code
22
23
```javascript
24
-import { unstable_useMemoCache as useMemoCache } from "react";
24
import { identity } from "shared-runtime";
25
26
function Component(props) {
28
- const $ = useMemoCache(2);
29
- let t0;
30
- if ($[0] !== props.id) {
31
- t0 = identity(props.id);
32
- $[0] = props.id;
33
- $[1] = t0;
34
- } else {
35
- t0 = $[1];
36
- }
37
- const x = t0;
27
+ const x = identity(props.id);
28
const y = (x: number);
29
return y;
30
}
compiler/packages/sprout/src/shared-runtime.ts
+4
@@ -127,6 +127,10 @@ export function makeObject_Primitives(): StringKeyedObject {
127
return { a: 0, b: "value1", c: true };
128
}
129
130
+export function makeArray<T>(value: T): Array<T> {
131
+ return [value];
132
+}
133
+
134
export function addOne(value: number): number {
135
return value + 1;
136
}