@samitouri / QOS-React-2 / commits / 8b3898c164

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 }