@samitouri / QOS-React / commits / 2069269903

Support TypeCastExpression

Support TypeCastExpressions — `(x: TypeAnnotation)`. This is pretty straightforward, it's semantically identical to a raw identifier. One catch is that our prettier config is hard-coded to use the babel-ts parser, i wasn't sure how to make that dynamic based on the file extension so for now i just ignored .flow.js files in our pretter config.

Joe Savona committed Feb 1, 2023 at 08:42 UTC 2069269903e316e548984d8ca49304da1fca3fd1
12 files changed +100 -2
compiler/forget/.prettierignore
+1
@@ -1,6 +1,7 @@
1 /packages/demo-*
2 **/dist
3 **/__tests__/fixtures/**/*.expect.md
4 +**/__tests__/fixtures/**/*.flow.js
5 **/.next
6 test262/
7 *.md
compiler/forget/src/Babel/RunReactForgetBabelPlugin.ts
+1 -1
@@ -36,7 +36,7 @@ export default function runReactForgetBabelPlugin(
36 ast: result.ast,
37 code: prettier.format(result.code, {
38 semi: true,
39 - parser: "babel-ts",
39 + parser: language === "typescript" ? "babel-ts" : "flow",
40 }),
41 map: result.map,
42 };
compiler/forget/src/HIR/BuildHIR.ts
+9
@@ -1386,6 +1386,15 @@ function lowerExpression(
1386 loc: exprLoc,
1387 };
1388 }
1389 + case "TypeCastExpression": {
1390 + let expr = exprPath as NodePath<t.TypeCastExpression>;
1391 + return {
1392 + kind: "TypeCastExpression",
1393 + value: lowerExpressionToPlace(builder, expr.get("expression")),
1394 + type: expr.get("typeAnnotation").node,
1395 + loc: exprLoc,
1396 + };
1397 + }
1398 default: {
1399 builder.errors.push({
1400 reason: `(BuildHIR::lowerExpression) Handle ${exprPath.type} expressions`,
compiler/forget/src/HIR/HIR.ts
+1
@@ -416,6 +416,7 @@ export type InstructionData =
416 args: Array<Place>;
417 }
418 | { kind: "UnaryExpression"; operator: string; value: Place }
419 + | { kind: "TypeCastExpression"; value: Place; type: t.TypeAnnotation }
420 | {
421 kind: "JsxExpression";
422 tag: Place;
compiler/forget/src/HIR/PrintHIR.ts
+4
@@ -259,6 +259,10 @@ export function printInstructionValue(instrValue: ReactiveValue): string {
259 value = JSON.stringify(instrValue.value);
260 break;
261 }
262 + case "TypeCastExpression": {
263 + value = `TypeCast ${printPlace(instrValue.value)}`;
264 + break;
265 + }
266 case "JsxExpression": {
267 const propItems = [];
268 for (const attribute of instrValue.props) {
compiler/forget/src/HIR/visitors.ts
+8
@@ -123,6 +123,10 @@ export function* eachInstructionValueOperand(
123 yield instrValue.tag;
124 break;
125 }
126 + case "TypeCastExpression": {
127 + yield instrValue.value;
128 + break;
129 + }
130 case "UnsupportedNode":
131 case "Primitive":
132 case "JSXText": {
@@ -243,6 +247,10 @@ export function mapInstructionOperands(
247 instrValue.tag = fn(instrValue.tag);
248 break;
249 }
250 + case "TypeCastExpression": {
251 + instrValue.value = fn(instrValue.value);
252 + break;
253 + }
254 case "UnsupportedNode":
255 case "Primitive":
256 case "JSXText": {
compiler/forget/src/Inference/InferAlias.ts
+5 -1
@@ -1,4 +1,3 @@
1 -import DisjointSet from "../Utils/DisjointSet";
1 import {
2 HIRFunction,
3 Identifier,
@@ -7,6 +6,7 @@ import {
6 LValue,
7 Place,
8 } from "../HIR/HIR";
9 +import DisjointSet from "../Utils/DisjointSet";
10
11 export type AliasSet = Set<Identifier>;
12
@@ -44,6 +44,10 @@ function inferInstr(instr: Instruction, state: AliasAnalyser) {
44 alias = instrValue.object;
45 break;
46 }
47 + case "TypeCastExpression": {
48 + alias = instrValue.value;
49 + break;
50 + }
51 default:
52 return;
53 }
compiler/forget/src/Inference/InferReferenceEffects.ts
+14
@@ -693,6 +693,20 @@ function inferBlock(env: Environment, block: BasicBlock) {
693 env.define(lvalue.place, instrValue);
694 continue;
695 }
696 + case "TypeCastExpression": {
697 + // A type cast expression has no effect at runtime, so it's equivalent to a raw
698 + // identifier:
699 + // ```
700 + // x = (y: type) // is equivalent to...
701 + // x = y
702 + // ```
703 + env.initialize(instrValue, env.kind(instrValue.value));
704 + env.reference(instrValue.value, Effect.Read);
705 + const lvalue = instr.lvalue;
706 + lvalue.place.effect = Effect.Mutate;
707 + env.alias(lvalue.place, instrValue.value);
708 + continue;
709 + }
710 case "Identifier": {
711 env.reference(instrValue, Effect.Read);
712 const lvalue = instr.lvalue;
compiler/forget/src/ReactiveScopes/CodegenReactiveFunction.ts
+7
@@ -672,6 +672,13 @@ function codegenInstructionValue(
672 );
673 break;
674 }
675 + case "TypeCastExpression": {
676 + value = t.typeCastExpression(
677 + codegenInstructionValue(cx, instrValue.value),
678 + instrValue.type
679 + );
680 + break;
681 + }
682 case "LogicalExpression": {
683 value = createLogicalExpression(
684 instrValue.loc,
compiler/forget/src/ReactiveScopes/InferReactiveScopeVariables.ts
+1
@@ -167,6 +167,7 @@ function isMutable({ id }: Instruction, place: Place): boolean {
167
168 function mayAllocate(value: InstructionValue): boolean {
169 switch (value.kind) {
170 + case "TypeCastExpression":
171 case "BinaryExpression":
172 case "Identifier":
173 case "PropertyLoad":
compiler/forget/src/__tests__/fixtures/hir/type-cast-expression.flow.expect.md new
+40
@@ -0,0 +1,40 @@
1 +
2 +## Input
3 +
4 +```javascript
5 +// @flow
6 +type Foo = {bar: string};
7 +function Component(props) {
8 + const x = {bar: props.bar};
9 + const y = (x: Foo);
10 + y.bar = 'hello';
11 + const z = (y: Foo);
12 + return z;
13 +}
14 +```
15 +
16 +## Code
17 +
18 +```javascript
19 +// @flow
20 +type Foo = { bar: string };
21 +function Component(props) {
22 + const $ = React.useMemoCache();
23 + const c_0 = $[0] !== props.bar;
24 + let y;
25 + if (c_0) {
26 + const x = { bar: props.bar };
27 + y = (x: Foo);
28 + y.bar = "hello";
29 + $[0] = props.bar;
30 + $[1] = y;
31 + } else {
32 + y = $[1];
33 + }
34 +
35 + const z = (y: Foo);
36 + return z;
37 +}
38 +
39 +```
40 +
\ No newline at end of file
compiler/forget/src/__tests__/fixtures/hir/type-cast-expression.flow.js new
+9
@@ -0,0 +1,9 @@
1 +// @flow
2 +type Foo = {bar: string};
3 +function Component(props) {
4 + const x = {bar: props.bar};
5 + const y = (x: Foo);
6 + y.bar = 'hello';
7 + const z = (y: Foo);
8 + return z;
9 +}
\ No newline at end of file