From 2069269903e316e548984d8ca49304da1fca3fd1 Mon Sep 17 00:00:00 2001 From: Joe Savona Date: Wed, 1 Feb 2023 08:42:33 -0800 Subject: [PATCH] Support TypeCastExpression MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. --- compiler/forget/.prettierignore | 1 + .../src/Babel/RunReactForgetBabelPlugin.ts | 2 +- compiler/forget/src/HIR/BuildHIR.ts | 9 +++++ compiler/forget/src/HIR/HIR.ts | 1 + compiler/forget/src/HIR/PrintHIR.ts | 4 ++ compiler/forget/src/HIR/visitors.ts | 8 ++++ compiler/forget/src/Inference/InferAlias.ts | 6 ++- .../src/Inference/InferReferenceEffects.ts | 14 +++++++ .../ReactiveScopes/CodegenReactiveFunction.ts | 7 ++++ .../InferReactiveScopeVariables.ts | 1 + .../hir/type-cast-expression.flow.expect.md | 40 +++++++++++++++++++ .../fixtures/hir/type-cast-expression.flow.js | 9 +++++ 12 files changed, 100 insertions(+), 2 deletions(-) create mode 100644 compiler/forget/src/__tests__/fixtures/hir/type-cast-expression.flow.expect.md create mode 100644 compiler/forget/src/__tests__/fixtures/hir/type-cast-expression.flow.js diff --git a/compiler/forget/.prettierignore b/compiler/forget/.prettierignore index 9eb3b48441..f647affa83 100644 --- a/compiler/forget/.prettierignore +++ b/compiler/forget/.prettierignore @@ -1,6 +1,7 @@ /packages/demo-* **/dist **/__tests__/fixtures/**/*.expect.md +**/__tests__/fixtures/**/*.flow.js **/.next test262/ *.md diff --git a/compiler/forget/src/Babel/RunReactForgetBabelPlugin.ts b/compiler/forget/src/Babel/RunReactForgetBabelPlugin.ts index 6dd0b482dd..68cb4efa68 100644 --- a/compiler/forget/src/Babel/RunReactForgetBabelPlugin.ts +++ b/compiler/forget/src/Babel/RunReactForgetBabelPlugin.ts @@ -36,7 +36,7 @@ export default function runReactForgetBabelPlugin( ast: result.ast, code: prettier.format(result.code, { semi: true, - parser: "babel-ts", + parser: language === "typescript" ? "babel-ts" : "flow", }), map: result.map, }; diff --git a/compiler/forget/src/HIR/BuildHIR.ts b/compiler/forget/src/HIR/BuildHIR.ts index 4a7e167fa8..599ddccfdf 100644 --- a/compiler/forget/src/HIR/BuildHIR.ts +++ b/compiler/forget/src/HIR/BuildHIR.ts @@ -1386,6 +1386,15 @@ function lowerExpression( loc: exprLoc, }; } + case "TypeCastExpression": { + let expr = exprPath as NodePath; + return { + kind: "TypeCastExpression", + value: lowerExpressionToPlace(builder, expr.get("expression")), + type: expr.get("typeAnnotation").node, + loc: exprLoc, + }; + } default: { builder.errors.push({ reason: `(BuildHIR::lowerExpression) Handle ${exprPath.type} expressions`, diff --git a/compiler/forget/src/HIR/HIR.ts b/compiler/forget/src/HIR/HIR.ts index cb15ba7126..779cd611ff 100644 --- a/compiler/forget/src/HIR/HIR.ts +++ b/compiler/forget/src/HIR/HIR.ts @@ -416,6 +416,7 @@ export type InstructionData = args: Array; } | { kind: "UnaryExpression"; operator: string; value: Place } + | { kind: "TypeCastExpression"; value: Place; type: t.TypeAnnotation } | { kind: "JsxExpression"; tag: Place; diff --git a/compiler/forget/src/HIR/PrintHIR.ts b/compiler/forget/src/HIR/PrintHIR.ts index 32179e8ec6..8ca6738666 100644 --- a/compiler/forget/src/HIR/PrintHIR.ts +++ b/compiler/forget/src/HIR/PrintHIR.ts @@ -259,6 +259,10 @@ export function printInstructionValue(instrValue: ReactiveValue): string { value = JSON.stringify(instrValue.value); break; } + case "TypeCastExpression": { + value = `TypeCast ${printPlace(instrValue.value)}`; + break; + } case "JsxExpression": { const propItems = []; for (const attribute of instrValue.props) { diff --git a/compiler/forget/src/HIR/visitors.ts b/compiler/forget/src/HIR/visitors.ts index 3c6763765d..4ed13fc8e3 100644 --- a/compiler/forget/src/HIR/visitors.ts +++ b/compiler/forget/src/HIR/visitors.ts @@ -123,6 +123,10 @@ export function* eachInstructionValueOperand( yield instrValue.tag; break; } + case "TypeCastExpression": { + yield instrValue.value; + break; + } case "UnsupportedNode": case "Primitive": case "JSXText": { @@ -243,6 +247,10 @@ export function mapInstructionOperands( instrValue.tag = fn(instrValue.tag); break; } + case "TypeCastExpression": { + instrValue.value = fn(instrValue.value); + break; + } case "UnsupportedNode": case "Primitive": case "JSXText": { diff --git a/compiler/forget/src/Inference/InferAlias.ts b/compiler/forget/src/Inference/InferAlias.ts index 6a1127369d..474e62b56c 100644 --- a/compiler/forget/src/Inference/InferAlias.ts +++ b/compiler/forget/src/Inference/InferAlias.ts @@ -1,4 +1,3 @@ -import DisjointSet from "../Utils/DisjointSet"; import { HIRFunction, Identifier, @@ -7,6 +6,7 @@ import { LValue, Place, } from "../HIR/HIR"; +import DisjointSet from "../Utils/DisjointSet"; export type AliasSet = Set; @@ -44,6 +44,10 @@ function inferInstr(instr: Instruction, state: AliasAnalyser) { alias = instrValue.object; break; } + case "TypeCastExpression": { + alias = instrValue.value; + break; + } default: return; } diff --git a/compiler/forget/src/Inference/InferReferenceEffects.ts b/compiler/forget/src/Inference/InferReferenceEffects.ts index 1ee90e9335..cde4340860 100644 --- a/compiler/forget/src/Inference/InferReferenceEffects.ts +++ b/compiler/forget/src/Inference/InferReferenceEffects.ts @@ -693,6 +693,20 @@ function inferBlock(env: Environment, block: BasicBlock) { env.define(lvalue.place, instrValue); continue; } + case "TypeCastExpression": { + // A type cast expression has no effect at runtime, so it's equivalent to a raw + // identifier: + // ``` + // x = (y: type) // is equivalent to... + // x = y + // ``` + env.initialize(instrValue, env.kind(instrValue.value)); + env.reference(instrValue.value, Effect.Read); + const lvalue = instr.lvalue; + lvalue.place.effect = Effect.Mutate; + env.alias(lvalue.place, instrValue.value); + continue; + } case "Identifier": { env.reference(instrValue, Effect.Read); const lvalue = instr.lvalue; diff --git a/compiler/forget/src/ReactiveScopes/CodegenReactiveFunction.ts b/compiler/forget/src/ReactiveScopes/CodegenReactiveFunction.ts index 2c6565e6da..b119b6fd37 100644 --- a/compiler/forget/src/ReactiveScopes/CodegenReactiveFunction.ts +++ b/compiler/forget/src/ReactiveScopes/CodegenReactiveFunction.ts @@ -672,6 +672,13 @@ function codegenInstructionValue( ); break; } + case "TypeCastExpression": { + value = t.typeCastExpression( + codegenInstructionValue(cx, instrValue.value), + instrValue.type + ); + break; + } case "LogicalExpression": { value = createLogicalExpression( instrValue.loc, diff --git a/compiler/forget/src/ReactiveScopes/InferReactiveScopeVariables.ts b/compiler/forget/src/ReactiveScopes/InferReactiveScopeVariables.ts index 742dd23544..01924935ad 100644 --- a/compiler/forget/src/ReactiveScopes/InferReactiveScopeVariables.ts +++ b/compiler/forget/src/ReactiveScopes/InferReactiveScopeVariables.ts @@ -167,6 +167,7 @@ function isMutable({ id }: Instruction, place: Place): boolean { function mayAllocate(value: InstructionValue): boolean { switch (value.kind) { + case "TypeCastExpression": case "BinaryExpression": case "Identifier": case "PropertyLoad": diff --git a/compiler/forget/src/__tests__/fixtures/hir/type-cast-expression.flow.expect.md b/compiler/forget/src/__tests__/fixtures/hir/type-cast-expression.flow.expect.md new file mode 100644 index 0000000000..3e69909675 --- /dev/null +++ b/compiler/forget/src/__tests__/fixtures/hir/type-cast-expression.flow.expect.md @@ -0,0 +1,40 @@ + +## Input + +```javascript +// @flow +type Foo = {bar: string}; +function Component(props) { + const x = {bar: props.bar}; + const y = (x: Foo); + y.bar = 'hello'; + const z = (y: Foo); + return z; +} +``` + +## Code + +```javascript +// @flow +type Foo = { bar: string }; +function Component(props) { + const $ = React.useMemoCache(); + const c_0 = $[0] !== props.bar; + let y; + if (c_0) { + const x = { bar: props.bar }; + y = (x: Foo); + y.bar = "hello"; + $[0] = props.bar; + $[1] = y; + } else { + y = $[1]; + } + + const z = (y: Foo); + return z; +} + +``` + \ No newline at end of file diff --git a/compiler/forget/src/__tests__/fixtures/hir/type-cast-expression.flow.js b/compiler/forget/src/__tests__/fixtures/hir/type-cast-expression.flow.js new file mode 100644 index 0000000000..967318f7d9 --- /dev/null +++ b/compiler/forget/src/__tests__/fixtures/hir/type-cast-expression.flow.js @@ -0,0 +1,9 @@ +// @flow +type Foo = {bar: string}; +function Component(props) { + const x = {bar: props.bar}; + const y = (x: Foo); + y.bar = 'hello'; + const z = (y: Foo); + return z; +} \ No newline at end of file