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