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