WP1: HIR types & construction (@analyzer subagent)

/home/jm/data/ai/cloudlang-trees/react · 6 messages · 33 parts · · 107,550 tokens
Created Feb 10, 04:55:57 AM · Updated Feb 11, 04:54:05 AM · 1 syncs
userFeb 10, 04:55:57 AM

You are analyzing the React Compiler's HIR (High-level Intermediate Representation) type system and construction. This is RESEARCH ONLY -- do NOT edit or write any files. Return your complete analysis as your final message.

The React Compiler source is at: /home/jm/data/ai/cloudlang-trees/react/react/compiler/packages/babel-plugin-react-compiler/src/

Files to read and analyze:

  1. HIR/HIR.ts - Core type definitions (~1700+ lines, read in chunks)
  2. HIR/Types.ts - Type system types
  3. HIR/ObjectShape.ts - Function signature / shape system
  4. HIR/BuildHIR.ts - AST -> HIR lowering
  5. HIR/HIRBuilder.ts - HIR construction helpers
  6. HIR/visitors.ts - HIR traversal utilities
  7. HIR/PrintHIR.ts - Debug printing

Also read the existing CloudLang types for comparison:

  • /home/jm/data/ai/cloudlang-trees/react/lib/ast.ml - Current AST (19 node types)
  • /home/jm/data/ai/cloudlang-trees/react/lib/ir.ml - Current IR (Cloudflare config)

For each major component, produce documentation in this template:

# Component Name

## File
## Purpose
## Input Invariants
## Output Guarantees
## Algorithm (detailed, step-by-step)
## Key Data Structures (with type signatures)
## Edge Cases
## Mapping to CloudLang
  - What we'd adapt and how
  - What we'd skip and why  
  - OCaml type sketches for adapted versions
## Example (before/after)

Specific questions to answer:

  1. HIR Core Model: What are Place, Identifier, Instruction, InstructionValue, Terminal, BasicBlock, HIR, HIRFunction? How do they relate? Draw the ownership/containment hierarchy.

  2. Identity System: How are BlockId, InstructionId, IdentifierId, ScopeId generated and used? Why branded/opaque types?

  3. InstructionValue variants: List ALL ~40 variants. For each: (a) relevant to CloudLang's TypeScript subset? (b) what data does it carry? (c) semantics? Group by category.

  4. Terminal variants: List ALL ~20 variants with same treatment.

  5. The Place/Identifier distinction: Why separate Place from Identifier? What does effect, reactive, mutableRange, scope buy you?

  6. BuildHIR lowering: How does it convert Babel AST to HIR instructions? Approach for control flow (if/else, loops, try/catch)? Scope/variable handling?

  7. Type System (Types.ts): What types exist? How does the shape system work? How do shapes encode function signatures?

  8. Visitor patterns (visitors.ts): What traversal abstractions exist?

CloudLang context: CloudLang has a flat ESTree AST (19 node types, no position info) and a Cloudflare-config IR. No proper HIR between them. We want to design an HIR that:

  • Represents TypeScript constructs relevant to closure/mutation analysis
  • Is extensible (start small, grow incrementally)
  • Supports SSA, effect annotation, mutable ranges
  • Will be implemented in OCaml with algebraic data types

Return your COMPLETE analysis as a single structured markdown document in your final message. Do NOT write any files.

assistantFeb 10, 04:55:57 AM
glm-4.7 · zai-coding-plan
This is a research-only task to analyze the React Compiler's HIR type system and construction. I need to read multiple files and produce a comprehensive analysis. Let me start by reading all the specified files. The files I need to read: 1. `/home/jm/data/ai/cloudlang-trees/react/react/compiler/packages/babel-plugin-react-compiler/src/HIR/HIR.ts` - Core type definitions (~1700+ lines) 2. `/home/jm/data/ai/cloudlang-trees/react/react/compiler/packages/babel-plugin-react-compiler/src/HIR/Types.ts` - Type system types 3. `/home/jm/data/ai/cloudlang-trees/react/react/compiler/packages/babel-plugin-react-compiler/src/HIR/ObjectShape.ts` - Function signature / shape system 4. `/home/jm/data/ai/cloudlang-trees/react/react/compiler/packages/babel-plugin-react-compiler/src/HIR/BuildHIR.ts` - AST -> HIR lowering 5. `/home/jm/data/ai/cloudlang-trees/react/react/compiler/packages/babel-plugin-react-compiler/src/HIR/HIRBuilder.ts` - HIR construction helpers 6. `/home/jm/data/ai/cloudlang-trees/react/react/compiler/packages/babel-plugin-react-compiler/src/HIR/visitors.ts` - HIR traversal utilities 7. `/home/jm/data/ai/cloudlang-trees/react/react/compiler/packages/babel-plugin-react-compiler/src/HIR/PrintHIR.ts` - Debug printing And for CloudLang comparison: - `/home/jm/data/ai/cloudlang-trees/react/lib/ast.ml` - Current AST - `/home/jm/data/ai/cloudlang-trees/react/lib/ir.ml` - Current IR Let me start reading all these files. Since HIR.ts is ~1700+ lines, I'll need to read it in chunks.

I'll analyze the React Compiler's HIR type system by reading all the specified files. Let me start by reading the core files in parallel.

readcompleted
react/compiler/packages/babel-plugin-react-compiler/src/HIR/HIR.ts
Output
<file>
00001| /**
00002|  * Copyright (c) Meta Platforms, Inc. and affiliates.
00003|  *
00004|  * This source code is licensed under the MIT license found in the
00005|  * LICENSE file in the root directory of this source tree.
00006|  */
00007| 
00008| import {BindingKind} from '@babel/traverse';
00009| import * as t from '@babel/types';
00010| import {
00011|   CompilerDiagnostic,
00012|   CompilerError,
00013|   ErrorCategory,
00014| } from '../CompilerError';
00015| import {assertExhaustive} from '../Utils/utils';
00016| import {Environment, ReactFunctionType} from './Environment';
00017| import type {HookKind} from './ObjectShape';
00018| import {Type, makeType} from './Types';
00019| import {z} from 'zod/v4';
00020| import type {AliasingEffect} from '../Inference/AliasingEffects';
00021| import {isReservedWord} from '../Utils/Keyword';
00022| import {Err, Ok, Result} from '../Utils/Result';
00023| 
00024| /*
00025|  * *******************************************************************************************
00026|  * *******************************************************************************************
00027|  * ************************************* Core Data Model *************************************
00028|  * *******************************************************************************************
00029|  * *******************************************************************************************
00030|  */
00031| 
00032| // AST -> (lowering) -> HIR -> (analysis) -> Reactive Scopes -> (codegen) -> AST
00033| 
00034| /*
00035|  * A location in a source file, intended to be used for providing diagnostic information and
00036|  * transforming code while preserving source information (ie to emit source maps).
00037|  *
00038|  * `GeneratedSource` indicates that there is no single source location from which the code derives.
00039|  */
00040| export const GeneratedSource = Symbol();
00041| export type SourceLocation = t.SourceLocation | typeof Generate...
readcompleted
react/compiler/packages/babel-plugin-react-compiler/src/HIR/Types.ts
Output
<file>
00001| /**
00002|  * Copyright (c) Meta Platforms, Inc. and affiliates.
00003|  *
00004|  * This source code is licensed under the MIT license found in the
00005|  * LICENSE file in the root directory of this source tree.
00006|  */
00007| 
00008| import {CompilerError} from '../CompilerError';
00009| import {GeneratedSource, PropertyLiteral} from './HIR';
00010| 
00011| export type BuiltInType = PrimitiveType | FunctionType | ObjectType;
00012| 
00013| export type Type =
00014|   | BuiltInType
00015|   | PhiType
00016|   | TypeVar
00017|   | PolyType
00018|   | PropType
00019|   | ObjectMethod;
00020| export type PrimitiveType = {kind: 'Primitive'};
00021| 
00022| /*
00023|  * An {@link FunctionType} or {@link ObjectType} (also a JS object) may be associated with an
00024|  * inferred "object shape", i.e. a known property (key -> Type) map. This is
00025|  * subtly different from JS language semantics - `shape` represents both
00026|  * OwnPropertyDescriptors and properties present in the prototype chain.
00027|  *
00028|  * {@link ObjectShape.functionType} is always present on the shape of a {@link FunctionType},
00029|  * and it represents the call signature of the function. Note that Forget thinks of a
00030|  * {@link FunctionType} as any "callable object" (not to be confused with objects that
00031|  *   extend the global `Function`.)
00032|  *
00033|  * If `shapeId` is present, it is a key into the ShapeRegistry used to infer this
00034|  * FunctionType or ObjectType instance (i.e. from an Environment).
00035|  */
00036| 
00037| export type FunctionType = {
00038|   kind: 'Function';
00039|   shapeId: string | null;
00040|   return: Type;
00041|   isConstructor: boolean;
00042| };
00043| 
00044| export type ObjectType = {
00045|   kind: 'Object';
00046|   shapeId: string | null;
00047| };
00048| 
00049| export type TypeVar = {
00050|   kind: 'Type';
00051|   id: TypeId;
00052| };
00053| export type PolyType = {
00054|   kind: 'Poly';
00055| };
00056| e...
readcompleted
react/compiler/packages/babel-plugin-react-compiler/src/HIR/ObjectShape.ts
Output
<file>
00001| /**
00002|  * Copyright (c) Meta Platforms, Inc. and affiliates.
00003|  *
00004|  * This source code is licensed under the MIT license found in the
00005|  * LICENSE file in the root directory of this source tree.
00006|  */
00007| 
00008| import {CompilerError} from '../CompilerError';
00009| import {AliasingEffect, AliasingSignature} from '../Inference/AliasingEffects';
00010| import {assertExhaustive} from '../Utils/utils';
00011| import {
00012|   Effect,
00013|   GeneratedSource,
00014|   Hole,
00015|   makeDeclarationId,
00016|   makeIdentifierId,
00017|   makeInstructionId,
00018|   Place,
00019|   SourceLocation,
00020|   SpreadPattern,
00021|   ValueKind,
00022|   ValueReason,
00023| } from './HIR';
00024| import {
00025|   BuiltInType,
00026|   FunctionType,
00027|   makeType,
00028|   ObjectType,
00029|   PolyType,
00030|   PrimitiveType,
00031| } from './Types';
00032| import {AliasingEffectConfig, AliasingSignatureConfig} from './TypeSchema';
00033| 
00034| /*
00035|  * This file exports types and defaults for JavaScript object shapes. These are
00036|  * stored and used by a Forget `Environment`. See comments in `Types.ts`,
00037|  * `Globals.ts`, and `Environment.ts` for more details.
00038|  */
00039| 
00040| const PRIMITIVE_TYPE: PrimitiveType = {
00041|   kind: 'Primitive',
00042| };
00043| 
00044| let nextAnonId = 0;
00045| /*
00046|  * We currently use strings for anonymous ShapeIds since they are easily
00047|  * debuggable, even though `Symbol()` might be more performant
00048|  */
00049| function createAnonId(): string {
00050|   return `<generated_${nextAnonId++}>`;
00051| }
00052| 
00053| /*
00054|  * Add a non-hook function to an existing ShapeRegistry.
00055|  *
00056|  * @returns a {@link FunctionType} representing the added function.
00057|  */
00058| export function addFunction(
00059|   registry: ShapeRegistry,
00060|   properties: Iterable<[string, BuiltInType | PolyType]>,
00061|   fn: Omit<FunctionSignature, 'hookKind...
readcompleted
lib/ast.ml
Output
<file>
00001| (** ESTree-compatible AST subset for TypeScript.
00002| 
00003|     This represents the minimal set of TypeScript constructs needed for
00004|     closure analysis and Worker decomposition. Node types follow the ESTree
00005|     specification used by @typescript-eslint/typescript-estree. *)
00006| 
00007| type var_kind = Const | Let | Var
00008| 
00009| type literal_value =
00010|   | LString of string
00011|   | LNumber of float
00012|   | LBool of bool
00013|   | LNull
00014|   | LUndefined
00015| 
00016| (** A single AST node. The variant name matches the ESTree "type" field. *)
00017| type node =
00018|   | Program of { body : node list }
00019|   | VariableDeclaration of { kind : var_kind; declarations : node list }
00020|   | VariableDeclarator of { id : node; init : node option }
00021|   | Identifier of { name : string }
00022|   | Literal of { value : literal_value }
00023|   | ArrowFunctionExpression of {
00024|       params : node list;
00025|       body : node;
00026|       async_ : bool;
00027|     }
00028|   | BlockStatement of { body : node list }
00029|   | ReturnStatement of { argument : node option }
00030|   | IfStatement of { test : node; consequent : node; alternate : node option }
00031|   | ExpressionStatement of { expression : node }
00032|   | CallExpression of { callee : node; arguments : node list }
00033|   | MemberExpression of { object_ : node; property : node }
00034|   | NewExpression of { callee : node; arguments : node list }
00035|   | BinaryExpression of { operator : string; left : node; right : node }
00036|   | LogicalExpression of { operator : string; left : node; right : node }
00037|   | AwaitExpression of { argument : node }
00038|   | RawCode of { code : string }
00039|       (** Verbatim TypeScript fragment. Used by the lowering pass for
00040|           constructs that don't have AST representation (e.g. object
00041|           literals for fetch options). Not produced by the parser. *)
00042|   | ExportNam...
readcompleted
lib/ir.ml
Output
<file>
00001| type binding =
00002|   | D1Binding of { name : string; database_id : string }
00003|   | KVBinding of { name : string; namespace_id : string }
00004|   | R2Binding of { name : string; bucket_name : string }
00005|   | ServiceBinding of { name : string; service : string }
00006| 
00007| type do_marker =
00008|   | SerialConflictHotspot
00009|   | GlobalSingletonNeeded
00010|   | EntityAffinityKeyed of { key_hint : string option }
00011|   | StrictOrderingRequired
00012|   | ConsistencyBoundaryCrossed
00013|   | RealtimeFanout
00014|   | DurableTimerNeeded
00015|   | HighFanInCoordinator
00016|   | StatelessFastPathPreferWorker
00017| 
00018| type coordination_intent =
00019|   | PerEntitySerial of { key_expr : string; reason : string }
00020|   | GlobalSingleton of { name : string; reason : string }
00021|   | RealtimeRoom of { room_key_expr : string }
00022|   | ScheduledQueue of { queue_key_expr : string }
00023| 
00024| type routing_policy =
00025|   | DeterministicName of { key_expr : string }
00026|   | ShardedName of { key_expr : string; shards : int }
00027|   | UniqueIdPool of { pool : string }
00028| 
00029| type storage_backend = DoSqlite | DoSyncKv
00030| type alarm_mode = NoAlarm | SingleAlarm | QueuedAlarm
00031| 
00032| type durability_policy = {
00033|   storage_backend : storage_backend;
00034|   alarm_mode : alarm_mode;
00035|   pitr_required : bool;
00036| }
00037| 
00038| type do_annotation = {
00039|   class_name : string;
00040|   binding_name : string;
00041|   markers : do_marker list;
00042|   intent : coordination_intent;
00043|   routing : routing_policy;
00044|   durability : durability_policy;
00045|   do_fit_score : int;
00046|   reasons : string list;
00047| }
00048| 
00049| type worker = {
00050|   name : string;
00051|   script : string;
00052|   bindings : binding list;
00053|   routes : string list;
00054|   do_annotation : do_annotation option;
00055| }
00056| 
00057| type durable_object = { class_name : string; scrip...
11,069in / 570out / 385reasoning
assistantFeb 10, 04:56:08 AM
glm-4.7 · zai-coding-plan
Good progress! Let me continue reading more files. I need to read more of HIR.ts (which is 1700+ lines), and also read BuildHIR.ts, HIRBuilder.ts, visitors.ts, and PrintHIR.ts.
readcompleted
react/compiler/packages/babel-plugin-react-compiler/src/HIR/HIR.ts
Output
<file>
00501|   id: InstructionId;
00502|   loc: SourceLocation;
00503| };
00504| 
00505| export type DoWhileTerminal = {
00506|   kind: 'do-while';
00507|   loop: BlockId;
00508|   test: BlockId;
00509|   fallthrough: BlockId;
00510|   id: InstructionId;
00511|   loc: SourceLocation;
00512| };
00513| 
00514| export type WhileTerminal = {
00515|   kind: 'while';
00516|   loc: SourceLocation;
00517|   test: BlockId;
00518|   loop: BlockId;
00519|   fallthrough: BlockId;
00520|   id: InstructionId;
00521| };
00522| 
00523| export type ForTerminal = {
00524|   kind: 'for';
00525|   loc: SourceLocation;
00526|   init: BlockId;
00527|   test: BlockId;
00528|   update: BlockId | null;
00529|   loop: BlockId;
00530|   fallthrough: BlockId;
00531|   id: InstructionId;
00532| };
00533| 
00534| export type ForOfTerminal = {
00535|   kind: 'for-of';
00536|   loc: SourceLocation;
00537|   init: BlockId;
00538|   test: BlockId;
00539|   loop: BlockId;
00540|   fallthrough: BlockId;
00541|   id: InstructionId;
00542| };
00543| 
00544| export type ForInTerminal = {
00545|   kind: 'for-in';
00546|   loc: SourceLocation;
00547|   init: BlockId;
00548|   loop: BlockId;
00549|   fallthrough: BlockId;
00550|   id: InstructionId;
00551| };
00552| 
00553| export type LogicalTerminal = {
00554|   kind: 'logical';
00555|   operator: t.LogicalExpression['operator'];
00556|   test: BlockId;
00557|   fallthrough: BlockId;
00558|   id: InstructionId;
00559|   loc: SourceLocation;
00560| };
00561| 
00562| export type TernaryTerminal = {
00563|   kind: 'ternary';
00564|   test: BlockId;
00565|   fallthrough: BlockId;
00566|   id: InstructionId;
00567|   loc: SourceLocation;
00568| };
00569| 
00570| export type LabelTerminal = {
00571|   kind: 'label';
00572|   block: BlockId;
00573|   fallthrough: BlockId;
00574|   id: InstructionId;
00575|   loc: SourceLocation;
00576| };
00577| 
00578| export type OptionalTerminal = {
00579|   kind: 'optional';
00580|   /*
00581|    * Specifies whether this no...
readcompleted
react/compiler/packages/babel-plugin-react-compiler/src/HIR/BuildHIR.ts
Output
<file>
00001| /**
00002|  * Copyright (c) Meta Platforms, Inc. and affiliates.
00003|  *
00004|  * This source code is licensed under the MIT license found in the
00005|  * LICENSE file in the root directory of this source tree.
00006|  */
00007| 
00008| import {NodePath, Scope} from '@babel/traverse';
00009| import * as t from '@babel/types';
00010| import invariant from 'invariant';
00011| import {
00012|   CompilerDiagnostic,
00013|   CompilerError,
00014|   CompilerSuggestionOperation,
00015|   ErrorCategory,
00016| } from '../CompilerError';
00017| import {Err, Ok, Result} from '../Utils/Result';
00018| import {assertExhaustive, hasNode} from '../Utils/utils';
00019| import {Environment} from './Environment';
00020| import {
00021|   ArrayExpression,
00022|   ArrayPattern,
00023|   BlockId,
00024|   BranchTerminal,
00025|   BuiltinTag,
00026|   Case,
00027|   Effect,
00028|   GeneratedSource,
00029|   GotoVariant,
00030|   HIRFunction,
00031|   IfTerminal,
00032|   InstructionKind,
00033|   InstructionValue,
00034|   JsxAttribute,
00035|   LoweredFunction,
00036|   ObjectPattern,
00037|   ObjectProperty,
00038|   ObjectPropertyKey,
00039|   Place,
00040|   PropertyLiteral,
00041|   ReturnTerminal,
00042|   SourceLocation,
00043|   SpreadPattern,
00044|   ThrowTerminal,
00045|   Type,
00046|   makeInstructionId,
00047|   makePropertyLiteral,
00048|   makeType,
00049|   promoteTemporary,
00050|   validateIdentifierName,
00051| } from './HIR';
00052| import HIRBuilder, {Bindings, createTemporaryPlace} from './HIRBuilder';
00053| import {BuiltInArrayId} from './ObjectShape';
00054| 
00055| /*
00056|  * *******************************************************************************************
00057|  * *******************************************************************************************
00058|  * ************************************* Lowering to HIR *************************************
00059|  * ***************************************************************...
readcompleted
react/compiler/packages/babel-plugin-react-compiler/src/HIR/HIRBuilder.ts
Output
<file>
00001| /**
00002|  * Copyright (c) Meta Platforms, Inc. and affiliates.
00003|  *
00004|  * This source code is licensed under the MIT license found in the
00005|  * LICENSE file in the root directory of this source tree.
00006|  */
00007| 
00008| import {Binding, NodePath} from '@babel/traverse';
00009| import * as t from '@babel/types';
00010| import {CompilerError, ErrorCategory} from '../CompilerError';
00011| import {Environment} from './Environment';
00012| import {
00013|   BasicBlock,
00014|   BlockId,
00015|   BlockKind,
00016|   Effect,
00017|   GeneratedSource,
00018|   GotoVariant,
00019|   HIR,
00020|   Identifier,
00021|   IdentifierId,
00022|   Instruction,
00023|   Place,
00024|   SourceLocation,
00025|   Terminal,
00026|   VariableBinding,
00027|   makeBlockId,
00028|   makeDeclarationId,
00029|   makeIdentifierName,
00030|   makeInstructionId,
00031|   makeTemporaryIdentifier,
00032|   makeType,
00033| } from './HIR';
00034| import {printInstruction} from './PrintHIR';
00035| import {
00036|   eachTerminalSuccessor,
00037|   mapTerminalSuccessors,
00038|   terminalFallthrough,
00039| } from './visitors';
00040| 
00041| /*
00042|  * *******************************************************************************************
00043|  * *******************************************************************************************
00044|  * ************************************* Lowering to HIR *************************************
00045|  * *******************************************************************************************
00046|  * *******************************************************************************************
00047|  */
00048| 
00049| // A work-in-progress block that does not yet have a terminator
00050| export type WipBlock = {
00051|   id: BlockId;
00052|   instructions: Array<Instruction>;
00053|   kind: BlockKind;
00054| };
00055| 
00056| type Scope = LoopScope | LabelScope | SwitchScope;
00057| 
00058| type LoopScope = {
00...
readcompleted
react/compiler/packages/babel-plugin-react-compiler/src/HIR/visitors.ts
Output
<file>
00001| /**
00002|  * Copyright (c) Meta Platforms, Inc. and affiliates.
00003|  *
00004|  * This source code is licensed under the MIT license found in the
00005|  * LICENSE file in the root directory of this source tree.
00006|  */
00007| 
00008| import {assertExhaustive} from '../Utils/utils';
00009| import {CompilerError} from '..';
00010| import {
00011|   BasicBlock,
00012|   BlockId,
00013|   Instruction,
00014|   InstructionKind,
00015|   InstructionValue,
00016|   makeInstructionId,
00017|   Pattern,
00018|   Place,
00019|   ReactiveInstruction,
00020|   ReactiveScope,
00021|   ReactiveValue,
00022|   ScopeId,
00023|   SpreadPattern,
00024|   Terminal,
00025| } from './HIR';
00026| 
00027| export function* eachInstructionLValue(
00028|   instr: ReactiveInstruction,
00029| ): Iterable<Place> {
00030|   if (instr.lvalue !== null) {
00031|     yield instr.lvalue;
00032|   }
00033|   yield* eachInstructionValueLValue(instr.value);
00034| }
00035| 
00036| export function* eachInstructionLValueWithKind(
00037|   instr: ReactiveInstruction,
00038| ): Iterable<[Place, InstructionKind]> {
00039|   switch (instr.value.kind) {
00040|     case 'DeclareContext':
00041|     case 'StoreContext':
00042|     case 'DeclareLocal':
00043|     case 'StoreLocal': {
00044|       yield [instr.value.lvalue.place, instr.value.lvalue.kind];
00045|       break;
00046|     }
00047|     case 'Destructure': {
00048|       const kind = instr.value.lvalue.kind;
00049|       for (const place of eachPatternOperand(instr.value.lvalue.pattern)) {
00050|         yield [place, kind];
00051|       }
00052|       break;
00053|     }
00054|     case 'PostfixUpdate':
00055|     case 'PrefixUpdate': {
00056|       yield [instr.value.lvalue, InstructionKind.Reassign];
00057|       break;
00058|     }
00059|   }
00060| }
00061| 
00062| export function* eachInstructionValueLValue(
00063|   value: ReactiveValue,
00064| ): Iterable<Place> {
00065|   switch (value.kind) {
00066|     case 'DeclareCont...
readcompleted
react/compiler/packages/babel-plugin-react-compiler/src/HIR/PrintHIR.ts
Output
<file>
00001| /**
00002|  * Copyright (c) Meta Platforms, Inc. and affiliates.
00003|  *
00004|  * This source code is licensed under the MIT license found in the
00005|  * LICENSE file in the root directory of this source tree.
00006|  */
00007| 
00008| import {CompilerError} from '../CompilerError';
00009| import {printReactiveScopeSummary} from '../ReactiveScopes/PrintReactiveFunction';
00010| import DisjointSet from '../Utils/DisjointSet';
00011| import {assertExhaustive} from '../Utils/utils';
00012| import type {
00013|   FunctionExpression,
00014|   HIR,
00015|   HIRFunction,
00016|   Identifier,
00017|   IdentifierName,
00018|   Instruction,
00019|   InstructionValue,
00020|   LValue,
00021|   ManualMemoDependency,
00022|   MutableRange,
00023|   ObjectMethod,
00024|   ObjectPropertyKey,
00025|   Pattern,
00026|   Phi,
00027|   Place,
00028|   ReactiveInstruction,
00029|   ReactiveScope,
00030|   ReactiveValue,
00031|   SourceLocation,
00032|   SpreadPattern,
00033|   Terminal,
00034|   Type,
00035| } from './HIR';
00036| import {GotoVariant, InstructionKind} from './HIR';
00037| import {AliasingEffect, AliasingSignature} from '../Inference/AliasingEffects';
00038| 
00039| export type Options = {
00040|   indent: number;
00041| };
00042| 
00043| export function printFunctionWithOutlined(fn: HIRFunction): string {
00044|   const output = [printFunction(fn)];
00045|   for (const outlined of fn.env.getOutlinedFunctions()) {
00046|     output.push(`\nfunction ${outlined.fn.id}:\n${printHIR(outlined.fn.body)}`);
00047|   }
00048|   return output.join('\n');
00049| }
00050| 
00051| export function printFunction(fn: HIRFunction): string {
00052|   const output = [];
00053|   let definition = '';
00054|   if (fn.id !== null) {
00055|     definition += fn.id;
00056|   } else {
00057|     definition += '<<anonymous>>';
00058|   }
00059|   if (fn.nameHint != null) {
00060|     definition += ` ${fn.nameHint}`;
00061|   }
00062|   if (fn.params.length !== 0) {
00063|    ...
26,613in / 254out / 48reasoning
assistantFeb 10, 04:56:17 AM
glm-4.7 · zai-coding-plan
Let me continue reading more of the HIR.ts file to get all the InstructionValue and Terminal variants. I also need to read more of BuildHIR.ts and HIRBuilder.ts for the lowering details.
readcompleted
react/compiler/packages/babel-plugin-react-compiler/src/HIR/HIR.ts
Output
<file>
01001| 
01002|   // store `object.property = value`
01003|   | {
01004|       kind: 'PropertyStore';
01005|       object: Place;
01006|       property: PropertyLiteral;
01007|       value: Place;
01008|       loc: SourceLocation;
01009|     }
01010|   // load `object.property`
01011|   | PropertyLoad
01012|   // `delete object.property`
01013|   | {
01014|       kind: 'PropertyDelete';
01015|       object: Place;
01016|       property: PropertyLiteral;
01017|       loc: SourceLocation;
01018|     }
01019| 
01020|   // store `object[index] = value` - like PropertyStore but with a dynamic property
01021|   | {
01022|       kind: 'ComputedStore';
01023|       object: Place;
01024|       property: Place;
01025|       value: Place;
01026|       loc: SourceLocation;
01027|     }
01028|   // load `object[index]` - like PropertyLoad but with a dynamic property
01029|   | {
01030|       kind: 'ComputedLoad';
01031|       object: Place;
01032|       property: Place;
01033|       loc: SourceLocation;
01034|     }
01035|   // `delete object[property]`
01036|   | {
01037|       kind: 'ComputedDelete';
01038|       object: Place;
01039|       property: Place;
01040|       loc: SourceLocation;
01041|     }
01042|   | LoadGlobal
01043|   | StoreGlobal
01044|   | FunctionExpression
01045|   | {
01046|       kind: 'TaggedTemplateExpression';
01047|       tag: Place;
01048|       value: {raw: string; cooked?: string};
01049|       loc: SourceLocation;
01050|     }
01051|   | {
01052|       kind: 'TemplateLiteral';
01053|       subexprs: Array<Place>;
01054|       quasis: Array<{raw: string; cooked?: string}>;
01055|       loc: SourceLocation;
01056|     }
01057|   | {
01058|       kind: 'Await';
01059|       value: Place;
01060|       loc: SourceLocation;
01061|     }
01062|   | {
01063|       kind: 'GetIterator';
01064|       collection: Place; // the collection
01065|       loc: SourceLocation;
01066|     }
01067|   | {
01068|       kind: 'IteratorNext';
01069|       iterator...
readcompleted
react/compiler/packages/babel-plugin-react-compiler/src/HIR/HIR.ts
Output
<file>
01501|   ValueReason.HookReturn,
01502|   ValueReason.JsxCaptured,
01503|   ValueReason.KnownReturnSignature,
01504|   ValueReason.Other,
01505|   ValueReason.ReactiveFunctionArgument,
01506|   ValueReason.ReducerState,
01507|   ValueReason.State,
01508| ]);
01509| 
01510| // The effect with which a value is modified.
01511| export enum Effect {
01512|   // Default value: not allowed after lifetime inference
01513|   Unknown = '<unknown>',
01514|   // This reference freezes the value (corresponds to a place where codegen should emit a freeze instruction)
01515|   Freeze = 'freeze',
01516|   // This reference reads the value
01517|   Read = 'read',
01518|   // This reference reads and stores the value
01519|   Capture = 'capture',
01520|   ConditionallyMutateIterator = 'mutate-iterator?',
01521|   /*
01522|    * This reference *may* write to (mutate) the value. This covers two similar cases:
01523|    * - The compiler is being conservative and assuming that a value *may* be mutated
01524|    * - The effect is polymorphic: mutable values may be mutated, non-mutable values
01525|    *   will not be mutated.
01526|    * In both cases, we conservatively assume that mutable values will be mutated.
01527|    * But we do not error if the value is known to be immutable.
01528|    */
01529|   ConditionallyMutate = 'mutate?',
01530| 
01531|   /*
01532|    * This reference *does* write to (mutate) the value. It is an error (invalid input)
01533|    * if an immutable value flows into a location with this effect.
01534|    */
01535|   Mutate = 'mutate',
01536|   // This reference may alias to (mutate) the value
01537|   Store = 'store',
01538| }
01539| export const EffectSchema = z.enum([
01540|   Effect.Read,
01541|   Effect.Mutate,
01542|   Effect.ConditionallyMutate,
01543|   Effect.ConditionallyMutateIterator,
01544|   Effect.Capture,
01545|   Effect.Store,
01546|   Effect.Freeze,
01547| ]);
01548| 
01549| export function isMutableEffect(
01550|   effect: Effect,
01551...
readcompleted
react/compiler/packages/babel-plugin-react-compiler/src/HIR/BuildHIR.ts
Output
<file>
00601|           continuationBlock.id,
00602|           () => {
00603|             const body = stmt.get('body');
00604|             lowerStatement(builder, body);
00605|             return {
00606|               kind: 'goto',
00607|               block: updateBlock ?? testBlock.id,
00608|               variant: GotoVariant.Continue,
00609|               id: makeInstructionId(0),
00610|               loc: body.node.loc ?? GeneratedSource,
00611|             };
00612|           },
00613|         );
00614|       });
00615| 
00616|       builder.terminateWithContinuation(
00617|         {
00618|           kind: 'for',
00619|           loc: stmtNode.loc ?? GeneratedSource,
00620|           init: initBlock,
00621|           test: testBlock.id,
00622|           update: updateBlock,
00623|           loop: bodyBlock,
00624|           fallthrough: continuationBlock.id,
00625|           id: makeInstructionId(0),
00626|         },
00627|         testBlock,
00628|       );
00629| 
00630|       const test = stmt.get('test');
00631|       if (test.node == null) {
00632|         builder.errors.push({
00633|           reason: `(BuildHIR::lowerStatement) Handle empty test in ForStatement`,
00634|           category: ErrorCategory.Todo,
00635|           loc: stmt.node.loc ?? null,
00636|           suggestions: null,
00637|         });
00638|       } else {
00639|         builder.terminateWithContinuation(
00640|           {
00641|             kind: 'branch',
00642|             test: lowerExpressionToTemporary(
00643|               builder,
00644|               test as NodePath<t.Expression>,
00645|             ),
00646|             consequent: bodyBlock,
00647|             alternate: continuationBlock.id,
00648|             fallthrough: continuationBlock.id,
00649|             id: makeInstructionId(0),
00650|             loc: stmt.node.loc ?? GeneratedSource,
00651|           },
00652|           continuationBlock,
00653|         );
00654|       }
00655|       return;
00656|   ...
33,659in / 184out / 41reasoning
assistantFeb 10, 04:56:26 AM
glm-4.7 · zai-coding-plan
Now I have enough information to start building the comprehensive analysis. Let me continue reading a bit more to capture any remaining important details about the lowering process and other parts of HIR.ts.
readcompleted
react/compiler/packages/babel-plugin-react-compiler/src/HIR/BuildHIR.ts
Output
<file>
01101|       builder.terminateWithContinuation(
01102|         {
01103|           id: makeInstructionId(0),
01104|           kind: 'goto',
01105|           block: testBlock.id,
01106|           variant: GotoVariant.Break,
01107|           loc: stmt.node.loc ?? GeneratedSource,
01108|         },
01109|         testBlock,
01110|       );
01111| 
01112|       const left = stmt.get('left');
01113|       const leftLoc = left.node.loc ?? GeneratedSource;
01114|       let test: Place;
01115|       const advanceIterator = lowerValueToTemporary(builder, {
01116|         kind: 'IteratorNext',
01117|         loc: leftLoc,
01118|         iterator: {...iterator},
01119|         collection: {...value},
01120|       });
01121|       if (left.isVariableDeclaration()) {
01122|         const declarations = left.get('declarations');
01123|         CompilerError.invariant(declarations.length === 1, {
01124|           reason: `Expected only one declaration in the init of a ForOfStatement, got ${declarations.length}`,
01125|           loc: left.node.loc ?? GeneratedSource,
01126|         });
01127|         const id = declarations[0].get('id');
01128|         const assign = lowerAssignment(
01129|           builder,
01130|           leftLoc,
01131|           InstructionKind.Let,
01132|           id,
01133|           advanceIterator,
01134|           'Assignment',
01135|         );
01136|         test = lowerValueToTemporary(builder, assign);
01137|       } else {
01138|         CompilerError.invariant(left.isLVal(), {
01139|           reason: 'Expected ForOf init to be a variable declaration or lval',
01140|           loc: leftLoc,
01141|         });
01142|         const assign = lowerAssignment(
01143|           builder,
01144|           leftLoc,
01145|           InstructionKind.Reassign,
01146|           left,
01147|           advanceIterator,
01148|           'Assignment',
01149|         );
01150|         test = lowerValueToTemporary(builder, assign);
01151|       }
01152|      ...
readcompleted
react/compiler/packages/babel-plugin-react-compiler/src/HIR/HIR.ts
Output
<file>
00601| export type TryTerminal = {
00602|   kind: 'try';
00603|   block: BlockId;
00604|   handlerBinding: Place | null;
00605|   handler: BlockId;
00606|   // TODO: support `finally`
00607|   fallthrough: BlockId;
00608|   id: InstructionId;
00609|   loc: SourceLocation;
00610| };
00611| 
00612| export type MaybeThrowTerminal = {
00613|   kind: 'maybe-throw';
00614|   continuation: BlockId;
00615|   handler: BlockId | null;
00616|   id: InstructionId;
00617|   loc: SourceLocation;
00618|   fallthrough?: never;
00619|   effects: Array<AliasingEffect> | null;
00620| };
00621| 
00622| export type ReactiveScopeTerminal = {
00623|   kind: 'scope';
00624|   fallthrough: BlockId;
00625|   block: BlockId;
00626|   scope: ReactiveScope;
00627|   id: InstructionId;
00628|   loc: SourceLocation;
00629| };
00630| 
00631| export type PrunedScopeTerminal = {
00632|   kind: 'pruned-scope';
00633|   fallthrough: BlockId;
00634|   block: BlockId;
00635|   scope: ReactiveScope;
00636|   id: InstructionId;
00637|   loc: SourceLocation;
00638| };
00639| 
00640| /*
00641|  * Instructions generally represent expressions but with all nesting flattened away,
00642|  * such that all operands to each instruction are either primitive values OR are
00643|  * references to a place, which may be a temporary that holds the results of a
00644|  * previous instruction. So `foo(bar(a))` would decompose into two instructions,
00645|  * one to store `tmp0 = bar(a)`, one for `foo(tmp0)`.
00646|  *
00647|  * Instructions generally store their value into a Place, though some instructions
00648|  * may not produce a value that is necessary to track (for example, class definitions)
00649|  * or may occur only for side-effects (many expression statements).
00650|  */
00651| export type Instruction = {
00652|   id: InstructionId;
00653|   lvalue: Place;
00654|   value: InstructionValue;
00655|   loc: SourceLocation;
00656|   effects: Array<AliasingEffect> | null;
00657| };
00658| 
00659| export type ...
15,931in / 133out / 37reasoning
assistantFeb 10, 04:56:32 AM
glm-4.7 · zai-coding-plan
Now I have a comprehensive view of the React Compiler's HIR system. Let me compile all this information into the final analysis document. I'll structure it according to the requested template. The key components I need to analyze are: 1. HIR Core Model - Place, Identifier, Instruction, InstructionValue, Terminal, BasicBlock, HIR, HIRFunction 2. Identity System - BlockId, InstructionId, IdentifierId, ScopeId 3. InstructionValue variants - all ~40 variants 4. Terminal variants - all ~20 variants 5. Place/Identifier distinction 6. BuildHIR lowering - AST to HIR conversion 7. Type System (Types.ts) - types, shapes 8. Visitor patterns (visitors.ts) Let me also compare with the CloudLang types: - ast.ml (19 node types) - ir.ml (Cloudflare config IR) I need to provide specific OCaml type sketches for the adapted CloudLang version.

Based on my analysis of the React Compiler's HIR system and the CloudLang types, here is the complete analysis:


React Compiler HIR Type System and Construction - Complete Analysis

HIR Core Model

File

/home/jm/data/ai/cloudlang-trees/react/react/compiler/packages/babel-plugin-react-compiler/src/HIR/HIR.ts

Purpose

The HIR (High-level Intermediate Representation) is the core data model for the React Compiler. It represents TypeScript/JavaScript code as a Control Flow Graph (CFG) with:

  • SSA-style instructions for expressions
  • Basic blocks with terminal nodes for control flow
  • Effect and mutation tracking via AliasingEffects
  • Reactive scope tracking for memoization analysis

Input Invariants

  • Babel AST is well-formed (valid TypeScript/JavaScript)
  • Variables are properly scoped according to JavaScript scoping rules
  • No duplicate identifiers in same lexical scope (after renaming)

Output Guarantees

  • All instructions have unique IDs
  • Blocks are in reverse postorder for forward dataflow analysis
  • Every variable reference maps to a unique Identifier with consistent id and declarationId
  • Phi nodes at join points for SSA form
  • ReactiveScope boundaries are properly nested

Algorithm (detailed, step-by-step)

The HIR construction follows a multi-stage process:

  1. Environment Setup: Create HIRBuilder with Environment tracking bindings, scopes, and exception handlers
  2. Function Entry: Process parameters, create entry block, handle hoistable bindings
  3. Statement Lowering: For each statement in AST body:
    • If IfStatement: Create consequent/alternate blocks + continuation, branch terminal
    • If WhileStatement: Create conditional block, loop body, test branch terminal
    • If ForStatement: Create init/test/update blocks, loop body, branch chain
    • If ForOfStatement: Create GetIterator + IteratorNext + loop structure
    • If ForInStatement: Create NextPropertyOf + loop structure
    • If TryStatement: Create try block, catch handler block, exception terminal
    • If VariableDeclaration: Lower with DeclareLocal/StoreLocal or Destructure
    • If ReturnStatement: Create return terminal
    • If Break/Continue: Lookup block from scope stack, goto terminal
    • If SwitchStatement: Create case blocks in reverse order for fallthrough
  4. Expression Lowering: Each expression becomes instruction(s) storing to temporaries
  5. Block Completion:
    • Compute reverse postorder
    • Remove dead code and unnecessary try/catch
    • Mark instruction IDs and predecessors

Key Data Structures (with type signatures)

// Core function representation
type HIRFunction = {
  loc: SourceLocation;
  id: ValidIdentifierName | null;
  nameHint: string | null;
  fnType: ReactFunctionType;
  env: Environment;
  params: Array<Place | SpreadPattern>;
  returnTypeAnnotation: t.FlowType | t.TSType | null;
  returns: Place;
  context: Array<Place>;
  body: HIR;
  generator: boolean;
  async: boolean;
  directives: Array<string>;
  aliasingEffects: Array<AliasingEffect> | null;
}

// CFG representation
type HIR = {
  entry: BlockId;
  blocks: Map<BlockId, BasicBlock>; // reverse postorder
}

type BasicBlock = {
  kind: BlockKind; // 'block' | 'value' | 'loop' | 'sequence' | 'catch'
  id: BlockId;
  instructions: Array<Instruction>;
  terminal: Terminal;
  preds: Set<BlockId>;
  phis: Set<Phi>;
}

// Single operation
type Instruction = {
  id: InstructionId;
  lvalue: Place | null;
  value: InstructionValue;
  effects: Array<AliasingEffect> | null;
  loc: SourceLocation;
}

// Value reference point
type Place = {
  kind: 'Identifier';
  identifier: Identifier;
  effect: Effect; // Unknown, Read, Capture, Mutate, etc.
  reactive: boolean;
  loc: SourceLocation;
}

// SSA variable
type Identifier = {
  id: IdentifierId;          // Unique SSA version
  declarationId: DeclarationId; // Original declaration
  name: IdentifierName | null;   // null for temps
  mutableRange: MutableRange;
  scope: ReactiveScope | null;
  type: Type;
  loc: SourceLocation;
}

type MutableRange = {
  start: InstructionId;
  end: InstructionId;
}

Edge Cases

  • Dead code: Unreachable blocks retained for analysis
  • Empty branches: If without else uses continuation directly
  • Hoisting: Variables used before declaration require HoistedConst/Let
  • Exception handling: try/catch creates MaybeThrowTerminal between all instructions
  • Optional chaining: OptionalTerminal tests for null/undefined at each step

Mapping to CloudLang

What we'd adapt:

  • Basic block CFG structure (essential for closure analysis)
  • Place/Identifier with effect tracking (core to mutation analysis)
  • Instruction flattening (critical for SSA and dataflow)
  • BlockKind distinction (statement vs expression blocks)
  • MutableRange tracking (needed for closure lifetime analysis)

What we'd skip:

  • JSX support (CloudLang targets Workers, not React)
  • ReactiveScope (specific to React memoization)
  • Hooks-specific instructions (StartMemoize, FinishMemoize)
  • Complex exception modeling (simpler bailout for CloudLang)
  • ForInStatement (rare in CloudLang patterns)
  • Class/ObjectMethod (CloudLang focuses on functional patterns)

OCaml type sketches:

(* Core function representation *)
type function_body = {
  entry : block_id;
  blocks : (block_id, basic_block) Map.t;
}

type basic_block = {
  kind : block_kind;
  id : block_id;
  instructions : instruction list;
  terminal : terminal;
  preds : block_id Set.t;
  phis : phi Set.t;
}

and block_kind =
  | Block
  | Value
  | Loop
  | Sequence
  | Catch

(* Single operation *)
and instruction = {
  id : instruction_id;
  lvalue : place option;
  value : instruction_value;
  loc : location;
}

(* Value reference point *)
and place = {
  identifier : identifier;
  effect : effect;
  loc : location;
}

(* SSA variable *)
and identifier = {
  id : identifier_id;           (* Unique SSA version *)
  declaration_id : declaration_id; (* Original declaration *)
  name : string option;       (* None for temps *)
  mutable_range : (instruction_id * instruction_id);
  scope : scope_id option;
  type : ir_type;
  loc : location;
}

and effect =
  | Unknown
  | Read
  | Capture
  | Mutate
  | ConditionallyMutate
  | Store
  | Freeze

and instruction_id = int
and block_id = int
and declaration_id = int
and scope_id = int

Example (before/after)

Before (TypeScript AST):

function foo(x: number) {
  const y = x + 1;
  if (y > 10) {
    return y;
  } else {
    const z = y * 2;
    return z;
  }
}

After (HIR):

foo(x: number): #t3
bb0 (block):
  [#0] #t0 = Primitive { value: 1 }
  [#1] #t1 = Binary x + #t0
  [#2] Const y = #t1
  [#3] If (#2) then:bb1 else:bb2 fallthrough=bb3

bb1 (value):
  [#4] Return Explicit #t2

bb2 (block):
  [#5] #t3 = Primitive { value: 2 }
  [#6] #t4 = Binary #t2 * #t3
  [#7] Const z = #t6
  [#8] Return Explicit #t7

bb3 (value):
  [#9] Return Void <undefined>

Identity System

File

/home/jm/data/ai/cloudlang-trees/react/react/compiler/packages/babel-plugin-react-compiler/src/HIR/HIR.ts (lines 1709-1781)

Purpose

Branded/opaque types prevent accidental misuse of IDs and provide type safety at the compiler level.

Input Invariants

  • IDs are non-negative integers
  • IDs are monotonically increasing during construction
  • Each ID space is independent (BlockId != InstructionId, etc.)

Output Guarantees

  • IDs from different spaces cannot be confused at type level
  • Invalid IDs fail runtime assertions via make*Id constructors
  • Type system enforces proper use (e.g., can't pass BlockId where InstructionId expected)

Algorithm

Each ID type has a constructor that validates the input:

const opaqueBlockId = Symbol();
export type BlockId = number & {[opaqueBlockId]: 'BlockId'};

export function makeBlockId(id: number): BlockId {
  CompilerError.invariant(id >= 0 && Number.isInteger(id), {
    reason: 'Expected block id to be a non-negative integer',
    loc: GeneratedSource,
  });
  return id as BlockId;
}

Key Data Structures

type BlockId = number & { [opaqueBlockId]: 'BlockId' };
type InstructionId = number & { [opaqueInstructionId]: 'InstructionId' };
type IdentifierId = number & { [opaqueIdentifierId]: 'IdentifierId' };
type ScopeId = number & { [opaqueScopeId]: 'ScopeId' };
type DeclarationId = number & { [opaqueDeclarationId]: 'DeclarationId' };
type TypeId = number & { [opaqueTypeId]: 'IdentifierId' };
type PropertyLiteral = (string | number) & { [opaquePropertyLiteral]: 'PropertyLiteral' };

Edge Cases

  • IDs from external sources (like AST node positions) are not automatically opaque
  • Generated IDs are sequential but not necessarily contiguous after optimizations

Mapping to CloudLang

What we'd adapt:

  • Same opaque type pattern for ID safety
  • All same ID types (BlockId, InstructionId, etc.)

What we'd skip:

  • TypeId (CloudLang may not need type inference initially)

OCaml type sketches:

type block_id = int (* Branded via constructor *)
type instruction_id = int
type identifier_id = int
type declaration_id = int
type scope_id = int

(* Opaque constructors with validation *)
let make_block_id : int -> block_id
let make_instruction_id : int -> instruction_id
let make_identifier_id : int -> identifier_id

InstructionValue Variants

File

/home/jm/data/ai/cloudlang-trees/react/react/compiler/packages/babel-plugin-react-compiler/src/HIR/HIR.ts (lines 899-1124)

Purpose

InstructionValue represents all possible operations that produce values. Each instruction stores its result in a Place.

Input Invariants

  • All operands are Places (no nested expressions)
  • All values have source locations

Output Guarantees

  • Instructions are SSA form (each Place assigned once per block)
  • Complex expressions are flattened to sequence of simple instructions

Algorithm

Each InstructionValue variant carries its specific operands and location.

Key Data Structures

type InstructionValue =
  | LoadLocal | LoadContext
  | { kind: 'DeclareLocal'; lvalue: LValue; type: ... }
  | { kind: 'DeclareContext'; lvalue: LValue; }
  | StoreLocal
  | { kind: 'StoreContext'; lvalue: LValue; value: Place }
  | Destructure
  | { kind: 'Primitive'; value: number | boolean | string | null | undefined }
  | JSXText
  | { kind: 'BinaryExpression'; operator: ...; left: Place; right: Place }
  | NewExpression
  | CallExpression
  | MethodCall
  | { kind: 'UnaryExpression'; operator: ...; value: Place }
  | { kind: 'TypeCastExpression'; value: Place; type: Type }
  | JsxExpression
  | { kind: 'ObjectExpression'; properties: Array<...> }
  | ObjectMethod
  | ArrayExpression
  | { kind: 'JsxFragment'; children: Array<Place> }
  | { kind: 'RegExpLiteral'; pattern: string; flags: string }
  | { kind: 'MetaProperty'; meta: string; property: string }
  | { kind: 'PropertyStore'; object: Place; property: PropertyLiteral; value: Place }
  | PropertyLoad
  | { kind: 'PropertyDelete'; object: Place; property: PropertyLiteral }
  | { kind: 'ComputedStore'; object: Place; property: Place; value: Place }
  | { kind: 'ComputedLoad'; object: Place; property: Place }
  | { kind: 'ComputedDelete'; object: Place; property: Place }
  | LoadGlobal | StoreGlobal
  | FunctionExpression
  | { kind: 'TaggedTemplateExpression'; tag: Place; value: ... }
  | { kind: 'TemplateLiteral'; subexprs: Array<Place>; quasis: Array<...> }
  | { kind: 'Await'; value: Place }
  | { kind: 'GetIterator'; collection: Place }
  | { kind: 'IteratorNext'; iterator: Place; collection: Place }
  | { kind: 'NextPropertyOf'; value: Place }
  | { kind: 'PrefixUpdate'; lvalue: Place; operation: ...; value: Place }
  | { kind: 'PostfixUpdate'; lvalue: Place; operation: ...; value: Place }
  | { kind: 'Debugger' }
  | StartMemoize | FinishMemoize
  | { kind: 'UnsupportedNode'; node: t.Node }

Edge Cases

  • UnsupportedNode used for constructs not represented but passed through
  • Debugger has no lvalue (side-effect only)

Mapping to CloudLang

Adaptable to CloudLang's TypeScript subset:

Category InstructionValue CloudLang Relevant? Data Carried Semantics
Primitives Primitive Yes number/bool/string/null/undefined Literal value
JSXText No string JSX content (skip)
RegExpLiteral No pattern, flags Regular expression (skip initially)
Literals ArrayExpression Yes Array<Place | SpreadPattern | Hole> Array literal
ObjectExpression Yes Array<ObjectProperty | SpreadPattern> Object literal
TemplateLiteral Maybe quasis, subexprs Template string
TaggedTemplateExpression No tag, value Tagged template (rare)
JsxFragment No children JSX fragment (skip)
MetaProperty No meta, property import.meta/new.target (skip)
Operators BinaryExpression Yes operator, left, right +, -, *, /, %, ===, !==, <, >, etc.
UnaryExpression Yes operator, value -, !, typeof, void, etc.
PrefixUpdate Yes lvalue, operation, value ++x, --y
PostfixUpdate Yes lvalue, operation, value x++, y--
Control Flow NewExpression Maybe callee, args new Constructor()
CallExpression Yes callee, args func(...args)
MethodCall Yes receiver, property, args obj.method(...args)
Await Maybe value await expr (for async Workers)
Debugger Maybe - debugger statement
Variables LoadLocal Yes place Read local variable
LoadContext No place Read captured variable (skip - simplify)
StoreLocal Yes lvalue, value, type x = value
StoreContext No lvalue, value Store to captured (skip)
DeclareLocal Yes lvalue, type const/let declaration
DeclareContext No lvalue Hoisted capture declaration (skip)
Destructure Yes lvalue, value const {a, b} = obj
LoadGlobal Maybe binding Read global/imported
StoreGlobal Maybe name, value Write to global (rare)
Properties PropertyLoad Yes object, property obj.prop
PropertyStore Yes object, property, value obj.prop = value
PropertyDelete Maybe object, property delete obj.prop
ComputedLoad Yes object, property obj[prop]
ComputedStore Yes object, property, value obj[prop] = value
ComputedDelete Maybe object, property delete obj[prop]
Functions FunctionExpression Yes loweredFunc () => {} or function() {}
ObjectMethod Maybe loweredFunc { method() {} }
Iterators GetIterator Maybe collection for-of iterator
IteratorNext Maybe iterator, collection Iterator iteration
NextPropertyOf Maybe value for-in enumeration
React-specific JsxExpression No tag, props, children JSX element (skip)
TypeCastExpression Maybe value, type x as Type, satisfies Type
StartMemoize No manualMemoId, deps useMemo marker (skip)
FinishMemoize No manualMemoId, decl useMemo marker (skip)
UnsupportedNode No node Fallback (skip)

What we'd skip for CloudLang:

  • JSX-related (JsxExpression, JSXText, JsxFragment)
  • React-specific (StartMemoize, FinishMemoize)
  • Complex iterators (GetIterator, IteratorNext, NextPropertyOf)
  • ObjectMethod (use function expressions instead)
  • TemplateLiteral (simplify to string concatenation initially)
  • TaggedTemplateExpression, MetaProperty

OCaml type sketches:

type instruction_value =
  | Primitive of primitive_value
  | ArrayExpression of (place_or_spread_or_hole list)
  | ObjectExpression of (obj_prop_or_spread list)
  | BinaryExpression of { op: binary_op; left: place; right: place }
  | UnaryExpression of { op: unary_op; value: place }
  | PrefixUpdate of { lvalue: place; op: update_op; value: place }
  | PostfixUpdate of { lvalue: place; op: update_op; value: place }
  | CallExpression of { callee: place; args: (place_or_spread list) }
  | MethodCall of { receiver: place; property: place; args: (place_or_spread list) }
  | NewExpression of { callee: place; args: (place_or_spread list) }
  | LoadLocal of place
  | StoreLocal of { lvalue: place; value: place; type: ir_type option }
  | DeclareLocal of { lvalue: place; kind: var_kind }
  | Destructure of { lvalue: pattern; value: place }
  | PropertyLoad of { object: place; property: property_literal }
  | PropertyStore of { object: place; property: property_literal; value: place }
  | ComputedLoad of { object: place; property: place }
  | ComputedStore of { object: place; property: place; value: place }
  | FunctionExpression of { params: (place_or_spread list); body: function_body }
  | LoadGlobal of { binding: non_local_binding }
  | Debugger
  | TypeCast of { value: place; type: ir_type }
  | Await of place

and primitive_value =
  | LString of string
  | LNumber of float
  | LBool of bool
  | LNull
  | LUndefined

and binary_op = 
  | Add | Sub | Mul | Div | Mod
  | EqEq | NotEq | StrictEq | StrictNotEq
  | Lt | LtEq | Gt | GtEq
  | And | Or | BitwiseAnd | BitwiseOr | BitwiseXor | LeftShift | RightShift

and unary_op =
  | Negate | Not | BitwiseNot | Typeof | Void | Delete

and update_op = Increment | Decrement

and place_or_spread_or_hole =
  | Place of place
  | Spread of place
  | Hole

and obj_prop_or_spread =
  | Property of { key: obj_property_key; value: place }
  | Spread of place

and obj_property_key =
  | StringKey of string
  | IdentifierKey of string
  | ComputedKey of place

Terminal Variants

File

/home/jm/data/ai/cloudlang-trees/react/react/compiler/packages/babel-plugin-react-compiler/src/HIR/HIR.ts (lines 361-638)

Purpose

Terminal nodes end basic blocks and describe control flow between blocks.

Input Invariants

  • Each terminal has a unique InstructionId
  • Fallthrough blocks exist where applicable
  • Terminal successors are valid BlockIds in the CFG

Output Guarantees

  • CFG is well-formed (all referenced blocks exist)
  • Loops have proper entry/exit points
  • Exception handlers are reachable via MaybeThrowTerminal

Algorithm

Terminals are created based on control flow structure and connected via BlockIds.

Key Data Structures

type Terminal =
  | UnsupportedTerminal
  | UnreachableTerminal
  | ThrowTerminal
  | ReturnTerminal
  | GotoTerminal
  | IfTerminal
  | BranchTerminal
  | SwitchTerminal
  | ForTerminal
  | ForOfTerminal
  | ForInTerminal
  | DoWhileTerminal
  | WhileTerminal
  | LogicalTerminal
  | TernaryTerminal
  | OptionalTerminal
  | LabelTerminal
  | SequenceTerminal
  | MaybeThrowTerminal
  | TryTerminal
  | ReactiveScopeTerminal
  | PrunedScopeTerminal

type TerminalWithFallthrough = Terminal & { fallthrough: BlockId }

Edge Cases

  • UnreachableTerminal marks paths that can never execute
  • MaybeThrowTerminal is inserted before every instruction in try block
  • SequenceTerminal groups expression statements within control flow

Mapping to CloudLang

Category Terminal CloudLang Relevant? Data Carried Semantics
Unconditional Goto Yes block, variant (Break/Continue/Try) Jump to block
Return Yes returnVariant (Void/Implicit/Explicit), value Return from function
Throw Yes value Throw exception
Unreachable Maybe - Code never reached
Unsupported No - Fallback (skip)
Conditional If Yes test, consequent, alternate, fallthrough if/else statement
Branch Yes test, consequent, alternate, fallthrough Conditional jump (for loops)
Switch Maybe test, cases, fallthrough switch statement
Loops While Yes test, loop, fallthrough while loop
DoWhile Maybe loop, test, fallthrough do-while loop
For Yes init, test, update, loop, fallthrough for loop
ForOf Maybe init, test, loop, fallthrough for-of loop
ForIn No init, loop, fallthrough for-in loop (skip)
Expression-level Logical No operator, test, fallthrough &&,
Ternary No test, fallthrough ? : (skip - simplify)
Optional No optional, test, fallthrough ?. chaining (skip)
Sequence Maybe block, fallthrough Group expressions
Label Maybe block, fallthrough Labeled statement
Exception Try Maybe block, handlerBinding, handler, fallthrough try/catch
MaybeThrow No continuation, handler Exception check (skip)
React-specific ReactiveScope No scope, block, fallthrough Scope boundary (skip)
PrunedScope No scope, block, fallthrough Pruned scope (skip)

What we'd skip:

  • Logical, Ternary, Optional (simplify to If/Branch terminals)
  • ReactiveScope, PrunedScope (React-specific)
  • ForIn (rare in CloudLang)

OCaml type sketches:

type terminal =
  | Goto of { block: block_id; variant: goto_variant }
  | Return of { return_variant: return_variant; value: place }
  | Throw of place
  | Unreachable
  | If of { test: place; consequent: block_id; alternate: block_id; fallthrough: block_id }
  | Branch of { test: place; consequent: block_id; alternate: block_id; fallthrough: block_id }
  | Switch of { test: place; cases: switch_case list; fallthrough: block_id }
  | While of { test: block_id; loop: block_id; fallthrough: block_id }
  | DoWhile of { loop: block_id; test: block_id; fallthrough: block_id }
  | For of { init: block_id; test: block_id; update: block_id option; loop: block_id; fallthrough: block_id }
  | ForOf of { init: block_id; test: block_id; loop: block_id; fallthrough: block_id }
  | Try of { block: block_id; handler: place option; handler: block_id; fallthrough: block_id }
  | Sequence of { block: block_id; fallthrough: block_id }

and goto_variant =
  | Break
  | Continue
  | Try

and return_variant =
  | Void
  | Implicit
  | Explicit

and switch_case = {
  test: place option;  (* None for default *)
  block: block_id;
}

Place/Identifier Distinction

File

/home/jm/data/ai/cloudlang-trees/react/react/compiler/packages/babel-plugin-react-compiler/src/HIR/HIR.ts (lines 1160-1177, 1248-1277)

Purpose

Separate Place (where a value is stored) from Identifier (what the value is) enables:

  • Effect tracking at each use site
  • Separate analysis of variable definitions vs uses
  • Efficient aliasing/closure analysis

Input Invariants

  • Every Place contains an Identifier
  • Identifiers have unique IDs but same declarationId for same original variable

Output Guarantees

  • Can track how each variable is used at each point
  • MutableRange describes when reassignments are legal
  • Scope tracking enables reactive dependency inference

Key Data Structures

// A reference point with metadata
type Place = {
  kind: 'Identifier';  // Currently only Identifier, designed for extensibility
  identifier: Identifier;
  effect: Effect;
  reactive: boolean;
  loc: SourceLocation;
}

// The actual variable definition
type Identifier = {
  id: IdentifierId;          // SSA version
  declarationId: DeclarationId; // Links all SSA versions
  name: IdentifierName | null;
  mutableRange: MutableRange;
  scope: ReactiveScope | null;
  type: Type;
  loc: SourceLocation;
}

// What we track about this variable's lifetime
type MutableRange = {
  start: InstructionId;  // First instruction where mutable
  end: InstructionId;    // First instruction where immutable
}

Why These Separate?

Aspect Place Identifier
Represents Use site Definition site
Has per-site metadata Effect, reactive N/A
SSA version References Identifier.id Own id
Multiple instances Yes (one per use) No (one per SSA version)

What Effect, Reactive, MutableRange, Scope Buy You

Effect:

  • Describes how this specific use affects the value
  • Read: pure read, no mutation
  • Mutate: writes to the value
  • Capture: stores into mutable container (potential alias)
  • Store: assignment or store operation
  • Freeze: marks value as immutable

Reactive:

  • True if this Place is a dependency for a reactive scope
  • Used for React memoization (skip for CloudLang)

MutableRange:

  • Defines the instruction range where the Identifier can be mutated
  • After end, the value becomes immutable (enables freeze analysis)
  • Critical for proving closures don't modify captured values

Scope:

  • Links to the ReactiveScope that produces this value
  • Enables dependency tracking for memoization (skip for CloudLang)

Edge Cases

  • Hoisted variables have id different from declarationId temporaries
  • Temporary identifiers have name: null
  • SSA form ensures single definition per block

Mapping to CloudLang

What we'd adapt:

  • Place/Identifier separation (core concept)
  • Effect tracking (essential for mutation analysis)
  • MutableRange (needed for closure safety)
  • Simplified reactive: false (always false for CloudLang)
  • No scope field initially

What we'd simplify:

  • Remove reactive field (always false)
  • Remove scope field initially (no reactive scopes)
  • Keep kind: 'Identifier' as only variant initially

OCaml type sketches:

type place = {
  identifier : identifier;
  effect : effect;
  loc : location;
}

type identifier = {
  id : identifier_id;
  declaration_id : declaration_id;
  name : string option;
  mutable_range : (instruction_id * instruction_id);
  type : ir_type;
  loc : location;
}

and effect =
  | Unknown
  | Read
  | Mutate
  | Capture
  | Store
  | Freeze

BuildHIR Lowering

File

/home/jm/data/ai/cloudlang-trees/react/react/compiler/packages/babel-plugin-react-compiler/src/HIR/BuildHIR.ts

Purpose

Converts Babel AST to HIR CFG, flattening expressions to SSA form while preserving control flow structure.

Input Invariants

  • Babel AST is valid TypeScript/JavaScript
  • Babel scope information is correct
  • Function has proper parameter declarations

Output Guarantees

  • HIR CFG is well-formed and connected
  • All expressions are flattened to temporaries
  • Hoistable bindings are properly declared before first use
  • Exception handlers are modeled where present

Algorithm (detailed, step-by-step)

Phase 1: Builder Initialization

  1. Create HIRBuilder with Environment
  2. Register function parameters as Places
  3. Process complex parameter patterns (destructuring, rest)

Phase 2: Statement Lowering (main switch on stmtNode.type)

For each statement:

  • ThrowStatement: Lower argument to temporary, create ThrowTerminal

  • ReturnStatement: Lower argument (or undefined), create ReturnTerminal

  • IfStatement:

    1. Reserve continuation block
    2. Create consequent block with goto to continuation
    3. Create alternate block (or use continuation) with goto to continuation
    4. Lower test to temporary
    5. Create IfTerminal with test, consequent, alternate, fallthrough
  • BlockStatement:

    1. Collect hoistable bindings from scope
    2. For each statement: traverse to find forward references
    3. Insert DeclareContext for hoisted bindings at earliest use point
    4. Lower each statement in order
    5. Remove bindings as declarations are encountered
  • ForStatement:

    1. Reserve: test, continuation, init, update (optional), loop blocks
    2. In init block: lower VariableDeclaration (init)
    3. In loop block: push loop scope, lower body, continue to update or test
    4. In update block (optional): lower update expression
    5. In test block: lower test, branch to loop or continuation
    6. Create ForTerminal with init, test, update, loop, fallthrough
  • WhileStatement:

    1. Reserve: conditional, continuation, loop blocks
    2. In loop block: push loop scope, lower body, continue to conditional
    3. Terminate with WhileTerminal pointing to conditional
    4. Complete conditional: lower test, branch to loop or continuation
  • DoWhileStatement:

    1. Reserve: conditional, continuation, loop blocks
    2. In loop block: push loop scope, lower body, continue to conditional
    3. Terminate with DoWhileTerminal pointing to conditional
    4. Complete conditional: lower test, branch to loop or continuation
  • ForOfStatement:

    1. Reserve: init, test, continuation, loop blocks
    2. In loop block: push loop scope, lower body, continue to init
    3. Create GetIterator for collection
    4. Create IteratorNext in loop
    5. Assign iterator result to loop variable (Destructure)
    6. Branch to loop or continuation based on done flag
    7. Create ForOfTerminal
  • ForInStatement: Similar to ForOf but uses NextPropertyOf

  • SwitchStatement:

    1. Reserve continuation block
    2. Iterate cases in REVERSE order (for fallthrough)
    3. For each case:
      • Create block, lower consequent
      • Add goto to next case's block (or continuation for last)
      • Build test (null for default)
    4. If no default, add implicit default to continuation
    5. Create SwitchTerminal
  • VariableDeclaration:

    1. Determine kind (Const/Let)
    2. For each declarator:
      • If init: lower init to temporary
      • Create DeclareLocal/StoreLocal/StoreContext/Destructure based on binding kind
      • Context variables: DeclareContext, StoreContext
      • Local variables: DeclareLocal, StoreLocal
      • Patterns: Destructure
  • FunctionDeclaration:

    1. Skip in Babel (don't re-traverse)
    2. Lower function to value
    3. Store to binding with InstructionKind.Function
  • BreakStatement:

    1. Lookup break block from scope stack (loop/switch/label)
    2. Create GotoTerminal with GotoVariant.Break
  • ContinueStatement:

    1. Lookup continue block from scope stack (loop only)
    2. Create GotoTerminal with GotoVariant.Continue
  • TryStatement:

    1. Reserve: try, handler, continuation blocks
    2. Create catch binding (if present)
    3. In handler: lower binding, lower body
    4. In try block: push exception handler, lower body
    5. Create TryTerminal
  • LabeledStatement:

    • Labeled loops: push label down to loop()
    • Other statements: create LabelTerminal with label scope
  • DebuggerStatement: Create Debugger instruction

  • ExpressionStatement: Lower expression to temporary (side effect)

Phase 3: Expression Lowering

Each expression is lowered to a sequence of instructions:

  1. For literals: Create Primitive instruction
  2. For identifiers: Resolve to Place (LoadLocal/LoadContext/LoadGlobal)
  3. For binary/unary: Lower operands, create BinaryExpression/UnaryExpression
  4. For calls: Lower callee and args, create CallExpression/MethodCall/NewExpression
  5. For member access: Lower object, create PropertyLoad/ComputedLoad
  6. For objects/arrays: Lower elements, create ObjectExpression/ArrayExpression
  7. For functions: Recursively lower to FunctionExpression

Phase 4: Builder Finalization

  1. Call builder.build()
  2. Compute reverse postorder of blocks
  3. Mark instruction IDs and predecessors
  4. Remove dead code and unnecessary try/catch
  5. Return HIRFunction

Key Data Structures

class HIRBuilder {
  #completed: Map<BlockId, BasicBlock>
  #current: WipBlock
  #entry: BlockId
  #scopes: Array<Scope>           // Loop/Label/Switch
  #context: Map<t.Identifier, SourceLocation>  // Captured refs
  #bindings: Bindings               // Name -> Identifier mapping
  #env: Environment
  #exceptionHandlerStack: Array<BlockId>
}

type WipBlock = {
  id: BlockId;
  instructions: Array<Instruction>;
  kind: BlockKind;
}

type Scope = LoopScope | LabelScope | SwitchScope
type LoopScope = { kind: 'loop'; label: string | null; continueBlock: BlockId; breakBlock: BlockId }

Edge Cases

  • Empty test in for: generates Todo error
  • Multiple defaults in switch: error
  • Var declarations: Todo error (only const/let supported)
  • For-await: Todo error
  • Class declarations: Unsupported (lift out required)
  • With statement: Unsupported

Mapping to CloudLang

What we'd adapt:

  • Statement lowering for: If, While, For, VariableDeclaration, ExpressionStatement, Return, Break, Continue
  • Expression flattening algorithm
  • Scope management for loops
  • Block ordering and terminal creation
  • Basic error handling (bailout vs error)

What we'd skip/simplify:

  • JSX-related statements
  • ForIn/ForOf loops (use simple while/for pattern)
  • LabeledStatement (labels rare in CloudLang)
  • WithStatement (unsupported anyway)
  • ClassDeclaration (unsupported anyway)
  • Complex hoisting (simplify: declare at top of block)
  • TryStatement (bailout for now)

OCaml algorithm sketch:

let lower_function (ast_node : node) (env : environment) : hir_function_result =
  let builder = new_builder env in
  (* Process parameters *)
  List.iter (fun p -> 
    match p with
    | Identifier name -> add_param builder name
    | _ -> error "Complex params not supported"
  ) ast_node.params;
  
  (* Lower body *)
  match ast_node.body with
  | BlockStatement stmts -> List.iter (lower_statement builder) stmts
  | Expression expr -> 
    let tmp = lower_expression_to_temporary builder expr in
    terminate builder (Return Explicit tmp);
  
  (* Finalize and return *)
  Ok { builder.build () with 
    id = ast_node.name;
    params = builder.params;
    (* ... *)
  }

and lower_statement (builder : builder) (stmt : node) : unit =
  match stmt with
  | IfStatement { test; consequent; alternate; _ } ->
    let continuation = reserve builder Block in
    let consequent = enter builder Block (fun consequent_block ->
      lower_statement builder consequent;
      Goto { block = continuation; variant = Break }
    ) in
    let alternate = match alternate with
      | Some alt -> enter builder Block (fun alternate_block ->
        lower_statement builder alt;
        Goto { block = continuation; variant = Break }
      )
      | None -> continuation
    in
    let test_place = lower_expression_to_temporary builder test in
    terminate builder (If {
      test = test_place;
      consequent;
      alternate;
      fallthrough = continuation
    }) continuation
  
  | WhileStatement { test; body; _ } ->
    let cond_block = reserve builder Loop in
    let continuation = reserve builder Block in
    let loop_block = enter builder Block (fun loop_block_id ->
      loop builder None cond_block continuation (fun () ->
        lower_statement builder body;
        Goto { block = cond_block; variant = Continue }
      )
    ) in
    let test_place = lower_expression_to_temporary builder test in
    terminate builder (While {
      test = cond_block;
      loop = loop_block;
      fallthrough = continuation
    }) cond_block;
    (* Complete cond block with branch *)
    terminate builder (Branch {
      test = test_place;
      consequent = loop_block;
      alternate = continuation;
      fallthrough = cond_block
    }) continuation
  
  | VariableDeclaration { kind; declarations } ->
    let var_kind = match kind with
      | "const" -> Const
      | "let" -> Let
      | "var" -> error "var not supported"
    in
    List.iter (fun decl ->
      match decl with
      | VariableDeclarator { id; init } ->
        let id_place = resolve_identifier builder id in
        (match init with
        | Some init_expr ->
          let value_place = lower_expression_to_temporary builder init_expr in
          (* Check if pattern or simple identifier *)
          match id with
          | Identifier _ -> 
            store_value builder var_kind id_place value_place
        | _ ->
            destructure builder var_kind id value_place
        | None -> 
            (* Uninitialized - skip for now *)
            ()
        )
    ) declarations
  
  | ReturnStatement { argument } ->
    let value_place = match argument with
      | Some arg -> lower_expression_to_temporary builder arg
      | None -> make_primitive_place Undefined
    in
    terminate builder (Return { return_variant = Explicit; value = value_place })
  
  | ExpressionStatement expr ->
    let _ = lower_expression_to_temporary builder expr in ()
  
  | _ -> error "Unsupported statement: %s" (node_type stmt)

and lower_expression_to_temporary (builder : builder) (expr : node) : place =
  let value = lower_expression builder expr in
  emit_instruction builder { lvalue = None; value; loc = expr.loc };
  value

and lower_expression (builder : builder) (expr : node) : place =
  match expr with
  | Literal lit -> 
      let value = lower_literal lit in
      make_primitive_place value
  | Identifier name ->
      resolve_identifier builder name
  | BinaryExpression { op; left; right } ->
      let left_place = lower_expression_to_temporary builder left in
      let right_place = lower_expression_to_temporary builder right in
      let tmp = make_temporary builder in
      emit_instruction builder {
        lvalue = Some tmp;
        value = BinaryExpression { op; left = left_place; right = right_place };
      };
      tmp
  | CallExpression { callee; args } ->
      let callee_place = lower_expression_to_temporary builder callee in
      let arg_places = List.map (lower_expression_to_temporary builder) args in
      let tmp = make_temporary builder in
      emit_instruction builder {
        lvalue = Some tmp;
        value = CallExpression { callee = callee_place; args = arg_places };
      };
      tmp
  | _ -> error "Unsupported expression: %s" (node_type expr)

Type System (Types.ts)

File

/home/jm/data/ai/cloudlang-trees/react/react/compiler/packages/babel-plugin-react-compiler/src/HIR/Types.ts

Purpose

Provides a type system for tracking value categories, shapes, and function signatures used in effect inference.

Input Invariants

  • Types are created via makeType() for fresh TypeVars
  • Built-in types use predefined shapeIds

Output Guarantees

  • Type equality is structural where applicable
  • TypeVars are globally unique (via counter)
  • Types can be duplicated with fresh TypeVars

Key Data Structures

type Type =
  | BuiltInType    // Primitive, Function, Object
  | PhiType        // Join of types at CFG merge
  | TypeVar        // Fresh type variable (for inference)
  | PolyType       // Polymorphic type
  | PropType       // Property access
  | ObjectMethod   // Method type

type BuiltInType = PrimitiveType | FunctionType | ObjectType

type PrimitiveType = { kind: 'Primitive' }

type FunctionType = {
  kind: 'Function';
  shapeId: string | null;
  return: Type;
  isConstructor: boolean;
}

type ObjectType = {
  kind: 'Object';
  shapeId: string | null;
}

type TypeVar = {
  kind: 'Type';
  id: TypeId;
}

type PhiType = {
  kind: 'Phi';
  operands: Array<Type>;
}

type PropType = {
  kind: 'Property';
  objectType: Type;
  objectName: string;
  propertyName:
    | { kind: 'literal'; value: PropertyLiteral }
    | { kind: 'computed'; value: Type };
}

type ObjectMethod = {
  kind: 'ObjectMethod';
}

Algorithm

  • makeType() increments global counter and returns fresh TypeVar
  • duplicateType() creates copy with fresh TypeVars
  • typeEquals() structural comparison of types
  • TypeVars compared by id; others structurally

Edge Cases

  • PhiType operands can have different types (unification needed)
  • PropertyType with computed propertyName depends on runtime value

Mapping to CloudLang

What we'd adapt initially:

  • Simple distinction: Primitive, Function, Object
  • TypeVar for future type inference
  • PhiType for SSA join points
  • Skip complex shape system initially

What we'd skip:

  • ObjectMethod (use FunctionExpression)
  • PropType (computed properties rare)
  • PolyType (not needed for basic CloudLang)
  • Complex shapeId system

OCaml type sketches:

type ir_type =
  | Primitive
  | Function of function_type
  | Object of { shape_id: string option }
  | TypeVar of type_id
  | PhiType of ir_type list

and function_type = {
  return_type: ir_type;
  is_constructor: bool;
}

ObjectShape (Shape System)

File

/home/jm/data/ai/cloudlang-trees/react/react/compiler/packages/babel-plugin-react-compiler/src/HIR/ObjectShape.ts

Purpose

Encodes function signatures and property shapes for effect inference (what hooks/builtins do with their arguments).

Input Invariants

  • ShapeIds are unique in registry
  • Aliasing signatures reference parameter names correctly

Output Guarantees

  • Hook effects are correctly encoded
  • Built-in behavior is captured

Key Data Structures

type ShapeRegistry = Map<string, ObjectShape>

type ObjectShape = {
  properties: Map<string, BuiltInType | PolyType>;
  functionType: FunctionSignature | null;
}

type FunctionSignature = {
  positionalParams: Array<Effect>;
  restParam: Effect | null;
  returnType: BuiltInType | PolyType;
  returnValueKind: ValueKind;
  returnValueReason?: ValueReason;
  calleeEffect: Effect;
  hookKind: HookKind | null;
  noAlias?: boolean;
  mutableOnlyIfOperandsAreMutable?: boolean;
  impure?: boolean;
  aliasing?: AliasingSignature | null;
}

type HookKind =
  | 'useContext' | 'useState' | 'useReducer' | 'useRef'
  | 'useEffect' | 'useLayoutEffect' | 'useInsertionEffect'
  | 'useMemo' | 'useCallback' | 'useTransition'
  | 'useImperativeHandle' | 'useEffectEvent' | 'useOptimistic'
  | 'useActionState' | 'Custom'

type AliasingSignature = {
  receiver: IdentifierId;
  params: Array<IdentifierId>;
  rest: IdentifierId | null;
  returns: IdentifierId;
  temporaries: Array<Place>;
  effects: Array<AliasingEffect>;
}

Algorithm

  • addFunction() adds function with signature to registry
  • parseAliasingSignatureConfig() converts declarative config to runtime form
  • Effects reference placeholders like @receiver, @param0 resolved to Place IDs

Edge Cases

  • Hooks without aliasing signature fall back to DefaultNonmutatingHook
  • Method calls with mutableOnlyIfOperandsAreMutable have polymorphic effects

Mapping to CloudLang

What we'd skip:

  • Entire shape system initially (React-specific)
  • Hook signatures
  • AliasingEffect signatures

Future consideration:

  • Could adapt for Cloudflare Worker API shapes (fetch, KV, D1, etc.)

Visitor Patterns (visitors.ts)

File

/home/jm/data/ai/cloudlang-trees/react/react/compiler/packages/babel-plugin-react-compiler/src/HIR/visitors.ts

Purpose

Provides traversal utilities for HIR analysis passes (operands, successors, mapping, etc.)

Input Invariants

  • HIR is well-formed CFG

Output Guarantees

  • All operands are visited exactly once
  • All successors are enumerable

Key Traversal Abstractions

// Operand iteration
function* eachInstructionOperand(instr: Instruction): Iterable<Place>
function* eachInstructionValueOperand(instrValue: InstructionValue): Iterable<Place>

// LValue iteration
function* eachInstructionLValue(instr: ReactiveInstruction): Iterable<Place>

// Mapping functions
function mapInstructionOperands(instr: Instruction, fn: (Place) => Place): void
function mapInstructionValueOperands(instrValue: InstructionValue, fn: (Place) => Place): void
function mapTerminalSuccessors(terminal: Terminal, fn: (BlockId) => BlockId): Terminal

// Terminal queries
function* eachTerminalSuccessor(terminal: Terminal): Iterable<BlockId>
function terminalFallthrough(terminal: Terminal): BlockId | null
function terminalHasFallthrough<T extends Terminal>(terminal: T): boolean

// Pattern iteration
function* eachPatternOperand(pattern: Pattern): Iterable<Place>
function mapPatternOperands(pattern: Pattern, fn: (Place) => Place): void

// Scope traversal
class ScopeBlockTraversal {
  recordScopes(block: BasicBlock): void
  isScopeActive(scopeId: ScopeId): boolean
  get currentScope(): ScopeId | null
}

Edge Cases

  • SwitchTerminal yields all case blocks as successors
  • Some terminals (return, throw) have no successors
  • MaybeThrowTerminal yields continuation and optional handler

Mapping to CloudLang

What we'd adapt:

  • All operand iteration utilities (essential for dataflow)
  • Terminal successor enumeration (essential for CFG analysis)
  • Mapping functions (essential for transformation passes)

What we'd skip:

  • ReactiveScope-specific traversal
  • JSX-specific patterns

OCaml type sketches:

(* Operand iteration *)
let iter_instruction_operands : instruction -> place iterator
let iter_instruction_value_operands : instruction_value -> place iterator
let iter_terminal_successors : terminal -> block_id iterator

(* Terminal queries *)
let terminal_fallthrough : terminal -> block_id option
let has_fallthrough : terminal -> bool

(* Mapping functions *)
let map_instruction_operands : (place -> place) -> instruction -> instruction
let map_terminal_successors : (block_id -> block_id) -> terminal -> terminal

(* Pattern iteration *)
let iter_pattern_operands : pattern -> place iterator
let map_pattern_operands : (place -> place) -> pattern -> unit

PrintHIR (Debug Printing)

File

/home/jm/data/ai/cloudlang-trees/react/react/compiler/packages/babel-plugin-react-compiler/src/HIR/PrintHIR.ts

Purpose

Human-readable HIR debugging output.

Output Format

function_name(param1, param2): return_place
bb0 (block):
  [#0] Local x = Primitive { value: 42 }
  [#1] y = Binary x + #0
  [#2] If (#1) then:bb1 else:bb2 fallthrough=bb3

bb1 (value):
  [#3] Return Explicit #1

bb2 (value):
  [#4] Return Void <undefined>

bb3 (value):
  [#5] Return Void <undefined>

Mapping to CloudLang

What we'd adapt:

  • Same output format structure
  • Block/ID/terminal representation

OCaml sketch:

let print_function (fn : hir_function) : string =
  let header = Printf.sprintf "%s(%s): %s" 
    (Option.value_or "<anonymous>" fn.id)
    (String.concat ", " (List.map print_place fn.params))
    (print_place fn.returns)
  in
  header ^ "\n" ^ (print_hir fn.body)

and print_hir (hir : hir) : string =
  String.concat "\n" (Map.to_list (fun (bid, block) ->
    Printf.sprintf "bb%d (%s):\n%s" 
      bid
      (string_of_block_kind block.kind)
      (String.concat "\n" (List.map print_instruction block.instructions))
      ^ (print_terminal block.terminal)
  ) hir.blocks)

and print_instruction (instr : instruction) : string =
  let value_str = print_instruction_value instr.value in
  match instr.lvalue with
  | Some place -> Printf.sprintf "[#%d] %s = %s" instr.id (print_place place) value_str
  | None -> Printf.sprintf "[#%d] %s" instr.id value_str

and print_place (place : place) : string =
  Printf.sprintf "%s(%s%s%s)" 
    (print_identifier place.identifier)
    (string_of_effect place.effect)
    (if place.reactive then " reactive" else "")

Ownership/Containment Hierarchy

HIRFunction
├── params: Array<Place>
├── context: Array<Place>         (captured variables)
├── body: HIR
│   ├── entry: BlockId
│   └── blocks: Map<BlockId, BasicBlock>
│       └── BasicBlock
│           ├── id: BlockId
│           ├── kind: BlockKind
│           ├── instructions: Array<Instruction>
│           │   └── Instruction
│           │       ├── id: InstructionId
│           │       ├── lvalue: Place | null
│           │       │   └── Place
│           │       │       └── identifier: Identifier
│           │       │           ├── id: IdentifierId
│           │       │           ├── declarationId: DeclarationId
│           │       │           ├── name: IdentifierName | null
│           │       │           ├── mutableRange: MutableRange
│           │       │           ├── scope: ReactiveScope | null
│           │       │           └── type: Type
│           │       └── value: InstructionValue
│           │               └── (one of ~40 variants)
│           │                   └── operands: Array<Place>
│           └── terminal: Terminal
│               └── (one of ~20 variants)
│                   └── successors: Array<BlockId>
├── env: Environment
│   ├── bindings: Map<name, {node, identifier}>
│   └── shapeRegistry: ShapeRegistry
│       └── Map<string, ObjectShape>
│           └── ObjectShape
│               ├── properties: Map<string, Type>
│               └── functionType: FunctionSignature | null
└── returns: Place

Summary: CloudLang HIR Design Recommendations

Minimal V1 for Closure Analysis

type hir_function = {
  id : string option;
  params : place list;
  body : hir;
  returns : place option;
  loc : location option;
}

and hir = {
  entry : block_id;
  blocks : (block_id, basic_block) Map.t;
}

and basic_block = {
  kind : block_kind;
  id : block_id;
  instructions : instruction list;
  terminal : terminal;
  preds : block_id Set.t;
  phis : phi Set.t;
}

and place = {
  identifier : identifier;
  effect : effect;
  loc : location option;
}

and identifier = {
  id : identifier_id;
  declaration_id : declaration_id;
  name : string option;
  mutable_range : (instruction_id * instruction_id);
  type : ir_type option;  (* Start simple *)
  loc : location option;
}

and instruction = {
  id : instruction_id;
  lvalue : place option;
  value : instruction_value;
  effects : effect list option;  (* For future aliasing analysis *)
  loc : location option;
}

and instruction_value =
  | Primitive of primitive_value
  | BinaryExpression of { op: binary_op; left: place; right: place }
  | UnaryExpression of { op: unary_op; value: place }
  | CallExpression of { callee: place; args: (place_or_spread list) }
  | FunctionExpression of { params: (place_or_spread list); body: hir_function }
  | LoadLocal of place
  | StoreLocal of { lvalue: place; value: place; kind: var_kind option }
  | DeclareLocal of { lvalue: place; kind: var_kind }
  | Destructure of { lvalue: pattern; value: place }
  | ArrayExpression of (place_or_spread_or_hole list)
  | ObjectExpression of (obj_prop_or_spread list)
  | PropertyLoad of { object: place; property: property_literal }
  | PropertyStore of { object: place; property: property_literal; value: place }
  | ComputedLoad of { object: place; property: place }
  | ComputedStore of { object: place; property: place; value: place }
  | Return
  | Debugger

and terminal =
  | Goto of { block: block_id; variant: goto_variant }
  | If of { test: place; consequent: block_id; alternate: block_id; fallthrough: block_id }
  | Branch of { test: place; consequent: block_id; alternate: block_id; fallthrough: block_id }
  | While of { test: block_id; loop: block_id; fallthrough: block_id }
  | For of { init: block_id; test: block_id; update: block_id option; loop: block_id; fallthrough: block_id }
  | Try of { block: block_id; handler: place option; handler: block_id; fallthrough: block_id }
  | Return
  | Unreachable

and effect =
  | Unknown
  | Read
  | Mutate
  | Capture
  | Store
  | Freeze

and primitive_value =
  | LString of string
  | LNumber of float
  | LBool of bool
  | LNull
  | LUndefined

Key Adaptation Decisions

  1. Simplify: Start with ~15 instruction variants (vs 40+ in React)
  2. Skip JSX: CloudLang targets Workers, not UI
  3. Skip React-specific: No reactive scopes, hooks, memoization
  4. Retain Core: SSA, CFG, effect tracking, mutable ranges
  5. Extensible Design: Use ADTs for easy addition of new variants
  6. OCaml idioms: Use GADTs for pattern matching exhaustiveness
6,130in / 13,007out / 196reasoning