I now have a good understanding of the React Compiler's HIR system and CloudLang's approach. Let me compile this comprehensive analysis document. I have all the key information needed to answer the user's specific questions.
Let me structure my response to answer all the questions:
1. **HIR Core Model**: What are Place, Identifier, Instruction, InstructionValue, Terminal, BasicBlock, HIR, HIRFunction?
2. **Identity System**: How are BlockId, InstructionId, IdentifierId, ScopeId generated and used?
3. **InstructionValue variants**: List ALL variants with their data
4. **Terminal variants**: List ALL variants
5. **Place/Identifier distinction**: Why separate?
6. **BuildHIR lowering**: How does it convert AST to HIR?
7. **Type System (Types.ts)**: What types exist?
8. **Visitor patterns (visitors.ts)**: What traversal abstractions exist?
9. **CloudLang mapping**: Sketch CloudLang HIR in OCaml types
Let me prepare the comprehensive documentation now.
Based on my analysis of the React Compiler's HIR system and CloudLang's architecture, here is my complete research documentation:
React Compiler HIR Type System and Construction Analysis
1. HIR Core Model (HIR.ts - Core Types)
File
/home/jm/data/ai/cloudlang-trees/react/react/compiler/packages/babel-plugin-react-compiler/src/HIR/HIR.ts
Purpose
Defines the core data model for React Compiler's High-level Intermediate Representation (HIR). This is an SSA-based CFG intermediate form between Babel AST and reactive scope analysis.
Ownership/Containment Hierarchy
HIRFunction (top-level unit)
└── body: HIR
├── entry: BlockId
└── blocks: Map<BlockId, BasicBlock>
└── BasicBlock
├── kind: BlockKind
├── id: BlockId
├── instructions: Array<Instruction>
├── terminal: Terminal
├── preds: Set<BlockId>
└── phis: Set<Phi>
HIRFunction
├── params: Array<Place | SpreadPattern>
├── context: Array<Place> (captured vars)
├── returns: Place
└── env: Environment
Key Data Structures:
// HIR - The control flow graph
export type HIR = {
entry: BlockId;
blocks: Map<BlockId, BasicBlock>;
};
// BasicBlock - Single entry, single exit sequence of instructions
export type BasicBlock = {
kind: BlockKind;
id: BlockId;
instructions: Array<Instruction>;
terminal: Terminal;
preds: Set<BlockId>;
phis: Set<Phi>;
};
// HIRFunction - A compiled function
export 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;
};
// Instruction - A single operation
export type Instruction = {
id: InstructionId;
lvalue: Place | null;
value: InstructionValue;
effects: Array<AliasingEffect> | null;
loc: SourceLocation;
};
Input Invariants
- Block entry has no predecessors (initial entry point)
- Basic blocks have linear control flow (no branching within block)
- Instructions in a block execute consecutively
- Terminal is always the last element in a block
Output Guarantees
- Blocks are in reverse postorder (predecessors before successors, except back edges)
- Each block has correct predecessor tracking
- Instructions have sequential IDs starting from 1
- All Place references resolve to valid Identifiers
2. 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
Uses branded opaque types to prevent accidentally using raw integers as IDs.
// Branded opaque types
const opaqueBlockId = Symbol();
export type BlockId = number & {[opaqueBlockId]: 'BlockId'};
const opaqueScopeId = Symbol();
export type ScopeId = number & {[opaqueScopeId]: 'ScopeId'};
const opaqueIdentifierId = Symbol();
export type IdentifierId = number & {[opaqueIdentifierId]: 'IdentifierId'};
const opageDeclarationId = Symbol();
export type DeclarationId = number & {[opageDeclarationId]: 'DeclarationId'};
const opaqueInstructionId = Symbol();
export type InstructionId = number & {[opaqueInstructionId]: 'IdentifierId'};
// Constructor functions with validation
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;
}
Why Branded/Opaque Types?
- Type Safety: Prevents mixing BlockId, InstructionId, IdentifierId, etc.
- API Clarity: Makes it impossible to accidentally pass the wrong ID type
- Refactoring Safety: If ID semantics change, compiler catches mismatches
Generation and Usage
- BlockId: Generated by
env.nextBlockId during CFG construction (HIRBuilder)
- InstructionId: Assigned after CFG completion in
markInstructionIds() (sequential 1..N)
- IdentifierId: Generated by
env.nextIdentifierId during variable resolution
- DeclarationId: Assigned once per original variable declaration (used for SSA phi nodes)
- ScopeId: Generated during reactive scope analysis
3. InstructionValue Variants
File
/home/jm/data/ai/cloudlang-trees/react/react/compiler/packages/babel-plugin-react-compiler/src/HIR/HIR.ts (lines 899-1125)
Purpose
All possible operation types in HIR. Each represents a flattened expression where operands are always Places (no nesting).
ALL Variants (by category):
Value Loads
// LoadLocal - Load from local variable
LoadLocal = {
kind: 'LoadLocal';
place: Place;
loc: SourceLocation;
};
// LoadContext - Load from captured outer scope variable
LoadContext = {
kind: 'LoadContext';
place: Place;
loc: SourceLocation;
};
// LoadGlobal - Load from global or module-level binding
LoadGlobal = {
kind: 'LoadGlobal';
binding: NonLocalBinding;
loc: SourceLocation;
};
CloudLang Relevance: All relevant. LoadLocal and LoadContext map to local/outer-scope reads. LoadGlobal would map to module imports or global Cloudflare APIs.
Value Stores
// DeclareLocal - const/let declaration
{
kind: 'DeclareLocal';
lvalue: LValue;
type: t.FlowType | t.TSType | null;
loc: SourceLocation;
};
// StoreLocal - Assignment to let/reassignment
StoreLocal = {
kind: 'StoreLocal';
lvalue: LValue;
value: Place;
type: t.FlowType | t.TSType | null;
loc: SourceLocation;
};
// DeclareContext - hoisted context variable declaration
{
kind: 'DeclareContext';
lvalue: {
kind: InstructionKind.Let | HoistedConst | HoistedLet | HoistedFunction;
place: Place;
};
loc: SourceLocation;
};
// StoreContext - Context variable assignment
{
kind: 'StoreContext';
lvalue: {
kind: InstructionKind.Reassign | Const | Let | Function;
place: Place;
};
value: Place;
loc: SourceLocation;
};
// StoreGlobal - Global assignment
StoreGlobal = {
kind: 'StoreGlobal';
name: string;
value: Place;
loc: SourceLocation;
};
CloudLang Relevance: All relevant. Need Declare/Store for locals, maybe skip global stores (CloudLang doesn't support global assignment).
Primitive Values
// Primitive - Literal values
{
kind: 'Primitive';
value: number | boolean | string | null | undefined;
loc: SourceLocation;
};
// RegExpLiteral
{
kind: 'RegExpLiteral';
pattern: string;
flags: string;
loc: SourceLocation;
};
// JSXText - Text content inside JSX
JSXText = {kind: 'JSXText'; value: string; loc: SourceLocation};
CloudLang Relevance: Primitive - relevant (strings, numbers, bools). RegExpLiteral and JSXText - skip (no JSX in CloudLang).
Object Operations
// ObjectExpression - Object literal creation
{
kind: 'ObjectExpression';
properties: Array<ObjectProperty | SpreadPattern>;
loc: SourceLocation;
};
// PropertyLoad - object.prop
PropertyLoad = {
kind: 'PropertyLoad';
object: Place;
property: PropertyLiteral;
loc: SourceLocation;
};
// PropertyStore - object.prop = value
{
kind: 'PropertyStore';
object: Place;
property: PropertyLiteral;
value: Place;
loc: SourceLocation;
};
// PropertyDelete - delete object.prop
{
kind: 'PropertyDelete';
object: Place;
property: PropertyLiteral;
loc: SourceLocation;
};
// ComputedLoad - object[key]
{
kind: 'ComputedLoad';
object: Place;
property: Place;
loc: SourceLocation;
};
// ComputedStore - object[key] = value
{
kind: 'ComputedStore';
object: Place;
property: Place;
value: Place;
loc: SourceLocation;
};
// ComputedDelete - delete object[key]
{
kind: 'ComputedDelete';
object: Place;
property: Place;
loc: SourceLocation;
};
CloudLang Relevance: ObjectExpression and Load/Store operations are all relevant for DTOs and config objects.
Array Operations
// ArrayExpression - Array literal
ArrayExpression = {
kind: 'ArrayExpression';
elements: Array<Place | SpreadPattern | Hole>;
loc: SourceLocation;
};
CloudLang Relevance: Relevant for config arrays (e.g., routes, handlers).
Expressions
// BinaryExpression - a + b, x == y, etc.
{
kind: 'BinaryExpression';
operator: Exclude<t.BinaryExpression['operator'], '|>'>;
left: Place;
right: Place;
loc: SourceLocation;
};
// UnaryExpression - !x, -y, typeof x, etc.
{
kind: 'UnaryExpression';
operator: Exclude<t.UnaryExpression['operator'], 'throw' | 'delete'>;
value: Place;
loc: SourceLocation;
};
// LogicalExpression and TernaryExpression are handled via terminals
CloudLang Relevance: BinaryExpression - highly relevant. UnaryExpression - relevant for typeof, !, etc.
Function Calls
// CallExpression - foo(arg1, arg2)
CallExpression = {
kind: 'CallExpression';
callee: Place;
args: Array<Place | SpreadPattern>;
loc: SourceLocation;
typeArguments?: Array<t.FlowType>;
};
// NewExpression - new Foo()
NewExpression = {
kind: 'NewExpression';
callee: Place;
args: Array<Place | SpreadPattern>;
loc: SourceLocation;
};
// MethodCall - obj.method(arg1, arg2)
MethodCall = {
kind: 'MethodCall';
receiver: Place;
property: Place;
args: Array<Place | SpreadPattern>;
loc: SourceLocation;
};
CloudLang Relevance: CallExpression - very relevant (Cloudflare API calls). NewExpression - maybe for constructing objects. MethodCall - relevant.
Functions and Classes
// FunctionExpression - Nested function definition
FunctionExpression = {
kind: 'FunctionExpression';
name: ValidIdentifierName | null;
nameHint: string | null;
loweredFunc: LoweredFunction;
type: 'ArrowFunctionExpression' | 'FunctionExpression' | 'FunctionDeclaration';
loc: SourceLocation;
};
// ObjectMethod - Method inside object literal
ObjectMethod = {
kind: 'ObjectMethod';
loc: SourceLocation;
loweredFunc: LoweredFunction;
};
CloudLang Relevance: FunctionExpression - relevant for handler functions. ObjectMethod - maybe skip.
Destructuring
// Destructure - const {a, b} = obj; const [x, y] = arr;
Destructure = {
kind: 'Destructure';
lvalue: LValuePattern;
value: Place;
loc: SourceLocation;
};
CloudLang Relevance: Maybe skip initially (simplify), add later if needed.
Pattern Matching
// ArrayPattern - [a, b, ...rest]
ArrayPattern = {
kind: 'ArrayPattern';
items: Array<Place | SpreadPattern | Hole>;
loc: SourceLocation;
};
// ObjectPattern - {a, b: x, ...rest}
ObjectPattern = {
kind: 'ObjectPattern';
properties: Array<ObjectProperty | SpreadPattern>;
loc: SourceLocation;
};
// ObjectProperty - {key: value}
ObjectProperty = {
kind: 'ObjectProperty';
key: ObjectPropertyKey;
type: 'property' | 'method';
place: Place;
};
// ObjectPropertyKey
ObjectPropertyKey =
| {kind: 'string'; name: string}
| {kind: 'identifier'; name: string}
| {kind: 'computed'; name: Place}
| {kind: 'number'; name: number};
// SpreadPattern - ...rest
SpreadPattern = {kind: 'Spread'; place: Place};
// Hole - _ (pattern wildcard)
Hole = {kind: 'Hole'};
CloudLang Relevance: Initially skip, can add for complex destructuring.
JSX
// JsxExpression - <Foo prop={value}>children</Foo>
JsxExpression = {
kind: 'JsxExpression';
tag: Place | BuiltinTag;
props: Array<JsxAttribute>;
children: Array<Place> | null;
loc: SourceLocation;
openingLoc: SourceLocation;
closingLoc: SourceLocation;
};
// JsxAttribute
JsxAttribute =
| {kind: 'JsxSpreadAttribute'; argument: Place}
| {kind: 'JsxAttribute'; name: string; place: Place};
// JsxFragment - <>children</>
{kind: 'JsxFragment'; children: Array<Place>; loc: SourceLocation};
// BuiltinTag - <div>, <span>, etc.
BuiltinTag = {kind: 'BuiltinTag'; name: string; loc: SourceLocation};
CloudLang Relevance: SKIP all JSX variants - CloudLang doesn't compile React components.
Templates and Literals
// TemplateLiteral - `hello ${name}`
{
kind: 'TemplateLiteral';
subexprs: Array<Place>;
quasis: Array<{raw: string; cooked?: string}>;
loc: SourceLocation;
};
// TaggedTemplateExpression - html`<div>...</div>`
{
kind: 'TaggedTemplateExpression';
tag: Place;
value: {raw: string; cooked?: string};
loc: SourceLocation;
};
// TypeCastExpression - x as Type
({kind: 'TypeCastExpression'; value: Place; type: Type; loc: SourceLocation} & (
{typeAnnotation: t.FlowType; typeAnnotationKind: 'cast'} |
{typeAnnotation: t.TSType; typeAnnotationKind: 'as' | 'satisfies'}
))
// MetaProperty - import.meta, new.target
{
kind: 'MetaProperty';
meta: string;
property: string;
loc: SourceLocation;
};
CloudLang Relevance: TemplateLiteral - relevant for string interpolation. TaggedTemplateExpression - skip. TypeCastExpression - maybe skip initially. MetaProperty - skip.
Iteration
// GetIterator - for (const x of y)
{
kind: 'GetIterator';
collection: Place;
loc: SourceLocation;
};
// IteratorNext - iteration step
{
kind: 'IteratorNext';
iterator: Place;
collection: Place;
loc: SourceLocation;
};
// NextPropertyOf - for (const key in obj)
{
kind: 'NextPropertyOf';
value: Place;
loc: SourceLocation;
};
CloudLang Relevance: Iteration operations are relevant for processing arrays/maps.
Update Operations
// PrefixUpdate - ++x, --y
{
kind: 'PrefixUpdate';
lvalue: Place;
operation: t.UpdateExpression['operator'];
value: Place;
loc: SourceLocation;
};
// PostfixUpdate - x++, y--
{
kind: 'PostfixUpdate';
lvalue: Place;
operation: t.UpdateExpression['operator'];
value: Place;
loc: SourceLocation;
};
CloudLang Relevance: Relevant for counter variables.
Async
// Await - await value
{
kind: 'Await';
value: Place;
loc: SourceLocation;
};
CloudLang Relevance: SKIP - CloudLang handles async implicitly through Cloudflare API calls, no explicit await needed.
Memoization (React-specific)
// StartMemoize - useMemo/useCallback boundary
StartMemoize = {
kind: 'StartMemoize';
manualMemoId: number;
deps: Array<ManualMemoDependency> | null;
depsLoc: SourceLocation | null;
loc: SourceLocation;
};
// FinishMemoize - End of memoized computation
FinishMemoize = {
kind: 'FinishMemoize';
manualMemoId: number;
decl: Place;
pruned?: true;
loc: SourceLocation;
};
CloudLang Relevance: SKIP - React-specific memoization, not needed for CloudLang.
Other
// Debugger statement
{kind: 'Debugger'; loc: SourceLocation}
// Unsupported nodes that pass through
{
kind: 'UnsupportedNode';
node: t.Node;
loc: SourceLocation;
};
CloudLang Relevance: Skip both - not needed for target compilation.
4. Terminal Variants
File
/home/jm/data/ai/cloudlang-trees/react/react/compiler/packages/babel-plugin-react-compiler/src/HIR/HIR.ts (lines 361-640)
Purpose
Block terminators that define control flow structure.
ALL Variants (by category):
Control Flow - Unconditional
// Goto - Unconditional jump
GotoTerminal = {
kind: 'goto';
block: BlockId;
variant: GotoVariant; // Break | Continue | Try
id: InstructionId;
loc: SourceLocation;
};
// Unreachable - Dead code block
UnreachableTerminal = {
kind: 'unreachable';
id: InstructionId;
loc: SourceLocation;
};
// Unsupported - Could not lower correctly
UnsupportedTerminal = {
kind: 'unsupported';
id: InstructionId;
loc: SourceLocation;
};
CloudLang Relevance: Goto - relevant for break/continue. Unreachable - useful for optimization. Unsupported - maybe not needed.
Control Flow - Conditional
// If - if/else statement
IfTerminal = {
kind: 'if';
test: Place;
consequent: BlockId;
alternate: BlockId;
fallthrough: BlockId;
id: InstructionId;
loc: SourceLocation;
};
// Branch - Expression-level conditional
BranchTerminal = {
kind: 'branch';
test: Place;
consequent: BlockId;
alternate: BlockId;
id: InstructionId;
loc: SourceLocation;
fallthrough: BlockId;
};
// Switch - switch statement
SwitchTerminal = {
kind: 'switch';
test: Place;
cases: Array<{test: Place | null; block: BlockId}>;
fallthrough: BlockId;
id: InstructionId;
loc: SourceLocation;
};
CloudLang Relevance: All three are relevant - If, Branch, Switch map directly to AST control flow.
Control Flow - Loops
// While - while loop
WhileTerminal = {
kind: 'while';
loc: SourceLocation;
test: BlockId;
loop: BlockId;
fallthrough: BlockId;
id: InstructionId;
};
// DoWhile - do...while
DoWhileTerminal = {
kind: 'do-while';
loop: BlockId;
test: BlockId;
fallthrough: BlockId;
id: InstructionId;
loc: SourceLocation;
};
// For - for (init; test; update)
ForTerminal = {
kind: 'for';
loc: SourceLocation;
init: BlockId;
test: BlockId;
update: BlockId | null;
loop: BlockId;
fallthrough: BlockId;
id: InstructionId;
};
// ForOf - for (const x of arr)
ForOfTerminal = {
kind: 'for-of';
loc: SourceLocation;
init: BlockId;
test: BlockId;
loop: BlockId;
fallthrough: BlockId;
id: InstructionId;
};
// ForIn - for (const key in obj)
ForInTerminal = {
kind: 'for-in';
loc: SourceLocation;
init: BlockId;
loop: BlockId;
fallthrough: BlockId;
id: InstructionId;
};
CloudLang Relevance: All loops are relevant. While, ForOf, ForIn are primary patterns. For is less common but useful.
Control Flow - Special
// Logical - &&, ||, ?? terminals
LogicalTerminal = {
kind: 'logical';
operator: t.LogicalExpression['operator'];
test: BlockId;
fallthrough: BlockId;
id: InstructionId;
loc: SourceLocation;
};
// Ternary - conditional expression
TernaryTerminal = {
kind: 'ternary';
test: BlockId;
fallthrough: BlockId;
id: InstructionId;
loc: SourceLocation;
};
// Optional - optional chaining (?.)
OptionalTerminal = {
kind: 'optional';
optional: boolean;
test: BlockId;
fallthrough: BlockId;
id: InstructionId;
loc: SourceLocation;
};
// Sequence - comma operator
SequenceTerminal = {
kind: 'sequence';
block: BlockId;
fallthrough: BlockId;
id: InstructionId;
loc: SourceLocation;
};
// Label - labeled block
LabelTerminal = {
kind: 'label';
block: BlockId;
fallthrough: BlockId;
id: InstructionId;
loc: SourceLocation;
};
CloudLang Relevance: Logical and Ternary - highly relevant. Optional - maybe skip (no optional chaining needed). Sequence and Label - skip initially.
Control Flow - Exceptions
// Try - try/catch
TryTerminal = {
kind: 'try';
block: BlockId;
handlerBinding: Place | null;
handler: BlockId;
fallthrough: BlockId;
id: InstructionId;
loc: SourceLocation;
};
// MaybeThrow - Instruction that might throw
MaybeThrowTerminal = {
kind: 'maybe-throw';
continuation: BlockId;
handler: BlockId | null;
id: InstructionId;
loc: SourceLocation;
effects: Array<AliasingEffect> | null;
};
CloudLang Relevance: Try - relevant for error handling. MaybeThrow - maybe skip (no exception modeling needed).
Control Flow - Exit
// Return - function return
ReturnTerminal = {
kind: 'return';
returnVariant: ReturnVariant; // Void | Implicit | Explicit
loc: SourceLocation;
value: Place;
id: InstructionId;
fallthrough?: never;
effects: Array<AliasingEffect> | null;
};
// Throw - throw statement
ThrowTerminal = {
kind: 'throw';
value: Place;
id: InstructionId;
loc: SourceLocation;
};
CloudLang Relevance: Return - highly relevant. Throw - relevant for error handling.
Reactive Scopes (React-specific)
// ReactiveScope - Start of reactive scope
ReactiveScopeTerminal = {
kind: 'scope';
fallthrough: BlockId;
block: BlockId;
scope: ReactiveScope;
id: InstructionId;
loc: SourceLocation;
};
// PrunedScope - Pruned reactive scope
PrunedScopeTerminal = {
kind: 'pruned-scope';
fallthrough: BlockId;
block: BlockId;
scope: ReactiveScope;
id: InstructionId;
loc: SourceLocation;
};
CloudLang Relevance: SKIP - React-specific reactive scope tracking, not needed for CloudLang.
5. Place and Identifier Distinction
File
/home/jm/data/ai/cloudlang-trees/react/react/compiler/packages/babel-plugin-react-compiler/src/HIR/HIR.ts (lines 1164-1278)
Purpose
Place is a value reference with metadata about how it's being used (effect, reactive flag). Identifier is the canonical variable definition.
Place Structure
export type Place = {
kind: 'Identifier'; // Only variant currently
identifier: Identifier;
effect: Effect; // How this reference modifies the value
reactive: boolean; // Is this a reactive dependency?
loc: SourceLocation;
};
Identifier Structure
export type Identifier = {
id: IdentifierId; // SSA instance ID (after EnterSSA)
declarationId: DeclarationId; // Original declaration ID
name: IdentifierName | null; // Debug name (null for temps)
mutableRange: MutableRange; // Where this variable can be mutated
scope: ReactiveScope | null; // Which scope computes this
type: Type;
loc: SourceLocation;
};
Why Separate Them?
- Effect Tracking:
Place.effect captures how each use of a variable affects it (Read, Mutate, Freeze, Capture, etc.)
- Reactive Tracking:
Place.reactive marks if this usage is part of a reactive dependency
- SSA Form:
Identifier.id changes after SSA transformation, but declarationId stays constant
- Mutable Range Tracking:
mutableRange on Identifier tracks liveness and mutability
- Context Distinguishing: A variable can be used with different effects in different contexts
Effect Values
export enum Effect {
Unknown = '<unknown>',
Freeze = 'freeze', // Makes value immutable
Read = 'read', // Read-only access
Capture = 'capture', // Read + store into new location
ConditionallyMutate = 'mutate?', // May mutate
Mutate = 'mutate', // Definitely mutates
Store = 'store', // Writes value (may alias)
}
Mutable Range
export type MutableRange = {
start: InstructionId; // Inclusive
end: InstructionId; // Exclusive
};
This tracks where a variable can be reassigned. For const, range is empty. For let, it extends until reassignment or end of scope.
CloudLang Relevance
- Effect: Critical for tracking which values can be modified (Cloudflare APIs are immutable)
- Reactive: Not needed - CloudLang doesn't have React-style reactivity
- MutableRange: Useful for SSA form and optimization
- Identifier.id vs declarationId: Needed for SSA transformation
6. 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 control flow graph. Handles all control flow constructs.
Algorithm Overview
export function lower(
func: NodePath<t.Function>,
env: Environment,
bindings: Bindings | null,
capturedRefs: Map<t.Identifier, SourceLocation>
): Result<HIRFunction, CompilerError> {
// 1. Create HIRBuilder with environment
const builder = new HIRBuilder(env, {bindings, context: capturedRefs});
// 2. Process parameters (handling patterns like {a, b})
func.get('params').forEach(param => {...});
// 3. Lower function body (expression or block)
if (body.isExpression()) {
// Arrow function with implicit return
const terminal: ReturnTerminal = {kind: 'return', returnVariant: 'Implicit', ...};
builder.terminateWithContinuation(terminal, fallthrough);
} else if (body.isBlockStatement()) {
lowerStatement(builder, body);
}
// 4. Build final HIR (post-processing)
const hir = builder.build();
// - Reverse postorder blocks
// - Mark instruction IDs
// - Mark predecessors
return Ok({...HIRFunction...});
}
Control Flow Construction
IfStatement
case 'IfStatement': {
const stmt = stmtPath as NodePath<t.IfStatement>;
const continuationBlock = builder.reserve('block'); // After if/else
const consequentBlock = builder.enter('block', _blockId => {
lowerStatement(builder, stmt.get('consequent'));
return {kind: 'goto', block: continuationBlock.id, ...};
});
let alternateBlock: BlockId;
const alternate = stmt.get('alternate');
if (hasNode(alternate)) {
alternateBlock = builder.enter('block', _blockId => {
lowerStatement(builder, alternate);
return {kind: 'goto', block: continuationBlock.id, ...};
});
} else {
alternateBlock = continuationBlock.id; // Direct to continuation
}
const test = lowerExpressionToTemporary(builder, stmt.get('test'));
const terminal: IfTerminal = {
kind: 'if',
test, consequent: consequentBlock,
alternate: alternateBlock,
fallthrough: continuationBlock.id, ...
};
builder.terminateWithContinuation(terminal, continuationBlock);
}
WhileStatement
// Structure:
// entry -> test block -> (condition check)
// -> if true: loop body -> update -> back to test
// -> if false: fallthrough
builder.loop(null, /* continueBlock = */ testBlockId, /* breakBlock = */ fallthroughId, () => {
const loopBody = builder.enter('block', _blockId => {
lowerStatement(builder, stmt.get('body'));
return {kind: 'goto', block: testBlockId, ...};
});
const test = builder.enter('value', testBlockId => {
const testValue = lowerExpressionToTemporary(builder, stmt.get('test'));
return {
kind: 'branch',
test: testValue,
consequent: loopBody.id,
alternate: fallthroughId,
fallthrough: fallthroughId, ...
};
});
builder.terminate({kind: 'goto', block: testBlockId, ...}, null);
});
SwitchStatement
// Creates:
// - test block (evaluates switch expression)
// - one block per case
// - default block (if exists)
// - fallthrough block
builder.switch(label, breakBlockId, () => {
const switchBlock = builder.enter('value', testBlockId => {
const testValue = lowerExpressionToTemporary(builder, stmt.get('discriminant'));
const cases: Array<{test: Place | null; block: BlockId}> = [];
stmt.get('cases').forEach(casePath => {
const caseBlock = builder.enter('block', _caseId => {
casePath.get('consequent').forEach(s => lowerStatement(builder, s));
return {kind: 'goto', block: breakBlockId, ...};
});
const test = casePath.get('test');
cases.push({
test: hasNode(test) ? lowerExpressionToTemporary(builder, test) : null,
block: caseBlock.id,
});
});
return {
kind: 'switch',
test: testValue,
cases,
fallthrough: breakBlockId,
...
};
});
builder.terminateWithContinuation({kind: 'goto', ...}, switchBlock);
});
TryStatement
case 'TryStatement': {
// Creates:
// - try block
// - handler (catch) block
// - continuation block
const handlerBlock = builder.reserve('catch');
const continuation = builder.reserve('block');
const tryBlock = builder.enter('block', tryBlockId => {
builder.enterTryCatch(handlerBlock.id, () => {
lowerStatement(builder, stmt.get('block'));
});
return {kind: 'goto', block: continuation.id, ...};
});
const handlerPath = stmt.get('handler');
if (hasNode(handlerPath)) {
const binding = lowerParameter(builder, handlerPath.get('param'));
builder.enterReserved(handlerBlock, () => {
lowerStatement(builder, handlerPath.get('body'));
return {kind: 'goto', block: continuation.id, ...};
});
}
builder.terminateWithContinuation({
kind: 'try',
block: tryBlock.id,
handlerBinding: binding ?? null,
handler: handlerBlock.id,
fallthrough: continuation.id,
...
}, continuation);
}
Scope and Variable Management
Variable Resolution
resolveIdentifier(path: NodePath<t.Identifier | t.JSXIdentifier>): VariableBinding {
const babelBinding = path.scope.getBinding(path.node.name);
// Global or import
if (babelBinding === null) {
return {kind: 'Global', name: path.node.name};
}
// Module-level (imports, etc.)
if (babelBinding === outerBinding) {
// Check for import variants
if (path.isImportDefaultSpecifier()) return {...ImportDefault...};
if (path.isImportSpecifier()) return {...ImportSpecifier...};
if (path.isImportNamespaceSpecifier()) return {...ImportNamespace...};
return {kind: 'ModuleLocal', name: path.node.name};
}
// Local variable - resolve or create Identifier
const resolvedBinding = this.resolveBinding(babelBinding.identifier);
return {
kind: 'Identifier',
identifier: resolvedBinding,
bindingKind: babelBinding.kind,
};
}
Hoisting
// Variables referenced before declaration (e.g., in closures) are hoisted
// Traverses block looking for early references
for (const stmt of statements) {
const willHoist = new Set<NodePath<t.Identifier>>();
stmt.traverse({
Identifier(id: NodePath<t.Identifier>) {
const binding = id.scope.getBinding(id.node.name);
if (hoistableIdentifiers.has(binding.identifier) && fnDepth > 0) {
willHoist.add(id);
}
}
});
// Emit DeclareLocal instruction at earliest reference point
for (const id of willHoist) {
const identifier = builder.resolveIdentifier(id);
const place: Place = {...};
builder.push({
id: makeInstructionId(0),
lvalue: place,
value: {kind: 'DeclareLocal', lvalue: {...}, ...},
effects: null,
loc: id.node.loc,
});
}
}
Expression Lowering
function lowerExpressionToTemporary(builder, exprPath): Place {
const value = lowerExpressionValue(builder, exprPath);
const temp = builder.makeTemporary(exprPath.node.loc);
builder.push({
id: makeInstructionId(0),
lvalue: temp,
value,
effects: null,
loc: exprPath.node.loc,
});
return temp;
}
// Recursively lowers nested expressions, creating temporaries for each sub-expression
// e.g., a(b(c)) becomes:
// tmp0 = lowerExpression(c)
// tmp1 = lowerExpression(a(tmp0))
Key Invariants
- All instructions have sequential IDs after build()
- Blocks are in reverse postorder
- All Place references resolve to valid Identifiers
- Hoisted variables have DeclareLocal before first use
- Loops have correct continue/break block tracking via scopes stack
Output Guarantees
- Valid control flow graph (no invalid jumps)
- All control flow structures (if, loops, switch) represented correctly
- Variables resolved with correct scope
- Hoisting handled correctly
7. Type System (Types.ts)
File
/home/jm/data/ai/cloudlang-trees/react/react/compiler/packages/babel-plugin-react-compiler/src/HIR/Types.ts
Purpose
Defines the type system for HIR. Simple but extensible.
Type Hierarchy
export type Type =
| BuiltInType // Primitive | Function | Object
| PhiType // Join of multiple types
| TypeVar // Type variable (for inference)
| PolyType // Polymorphic type
| PropType // Property access
| ObjectMethod; // Method type
export type BuiltInType = PrimitiveType | FunctionType | ObjectType;
export type PrimitiveType = {kind: 'Primitive'};
export type FunctionType = {
kind: 'Function';
shapeId: string | null; // Key into ShapeRegistry
return: Type;
isConstructor: boolean;
};
export type ObjectType = {
kind: 'Object';
shapeId: string | null; // Key into ShapeRegistry
};
export type TypeVar = {
kind: 'Type';
id: TypeId; // Opaque branded type
};
export type PolyType = {kind: 'Poly'};
export type PhiType = {
kind: 'Phi';
operands: Array<Type>; // Types being joined
};
export type PropType = {
kind: 'Property';
objectType: Type;
objectName: string;
propertyName:
| {kind: 'literal'; value: PropertyLiteral}
| {kind: 'computed'; value: Type};
};
export type ObjectMethod = {kind: 'ObjectMethod'};
Type Variable Creation
let typeCounter = 0;
export function makeType(): TypeVar {
return {
kind: 'Type',
id: makeTypeId(typeCounter++),
};
}
// Branded type for type safety
const opaqueTypeId = Symbol();
export type TypeId = number & {[opaqueTypeId]: 'IdentifierId'};
export function makeTypeId(id: number): TypeId {
CompilerError.invariant(id >= 0 && Number.isInteger(id), {
reason: 'Expected instruction id to be a non-negative integer',
loc: GeneratedSource,
});
return id as TypeId;
}
Type Equality
export function typeEquals(tA: Type, tB: Type): boolean {
if (tA.kind !== tB.kind) return false;
return (
typeVarEquals(tA, tB) ||
funcTypeEquals(tA, tB) ||
objectTypeEquals(tA, tB) ||
primitiveTypeEquals(tA, tB) ||
polyTypeEquals(tA, tB) ||
phiTypeEquals(tA, tB) ||
propTypeEquals(tA, tB) ||
objectMethodTypeEquals(tA, tB)
);
}
CloudLang Relevance
- TypeVar: Needed for type inference
- FunctionType: Relevant for function signatures
- ObjectType: Relevant for config objects
- PrimitiveType: Basic types
- PhiType: Needed for SSA phi nodes
- PropType: Maybe skip (simpler property tracking)
- PolyType: Maybe skip (no generics initially)
- ObjectMethod: Maybe skip
8. Object Shape System (ObjectShape.ts)
File
/home/jm/data/ai/cloudlang-trees/react/react/compiler/packages/babel-plugin-react-compiler/src/HIR/ObjectShape.ts
Purpose
Encodes function signatures and object shapes for type inference and effect tracking.
Shape Registry
export type ShapeRegistry = Map<string, ObjectShape>;
export type ObjectShape = {
properties: Map<string, BuiltInType | PolyType>;
functionType: FunctionSignature | null;
};
Function Signature
export 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;
knownIncompatible?: string | null;
canonicalName?: string;
aliasing?: AliasingSignature | null; // Detailed effect specification
};
Hook Kinds
export type HookKind =
| 'useContext' | 'useState' | 'useActionState' | 'useReducer'
| 'useRef' | 'useEffect' | 'useLayoutEffect' | 'useInsertionEffect'
| 'useMemo' | 'useCallback' | 'useTransition' | 'useImperativeHandle'
| 'useEffectEvent' | 'useOptimistic' | 'Custom';
Aliasing Signature
export type AliasingSignature = {
receiver: IdentifierId;
params: Array<IdentifierId>;
rest: IdentifierId | null;
returns: IdentifierId;
temporaries: Array<Place>;
effects: Array<AliasingEffect>;
};
Built-in Shapes Examples
// Array.push signature with detailed aliasing
addFunction(BUILTIN_SHAPES, [], {
positionalParams: [],
restParam: Effect.Capture,
returnType: PRIMITIVE_TYPE,
calleeEffect: Effect.Store,
returnValueKind: ValueKind.Primitive,
aliasing: {
receiver: '@receiver',
params: [],
rest: '@rest',
returns: '@returns',
temporaries: [],
effects: [
{kind: 'Mutate', value: '@receiver'}, // Mutates array
{kind: 'Capture', from: '@rest', into: '@receiver'}, // Captures args
{kind: 'Create', into: '@returns', value: ValueKind.Primitive, reason: ...},
],
},
}, 'Array.push');
Effect Values
export enum Effect {
Read = 'read',
Mutate = 'mutate',
ConditionallyMutate = 'mutate?',
ConditionallyMutateIterator = 'mutate-iterator?',
Capture = 'capture',
Store = 'store',
Freeze = 'freeze',
}
Value Kinds
export enum ValueKind {
MaybeFrozen = 'maybefrozen',
Frozen = 'frozen',
Primitive = 'primitive',
Global = 'global',
Mutable = 'mutable',
Context = 'context',
}
CloudLang Relevance
- ShapeRegistry: Useful for encoding Cloudflare API signatures
- FunctionSignature: Adapt for Cloudflare Worker / DO / R2 / D1 APIs
- Effect: Use for tracking mutation (Cloudflare APIs are mostly immutable)
- ValueKind: Simplified - focus on
Primitive vs Mutable
- HookKind: SKIP - React-specific
CloudLang Adaptation Example
type effect_kind =
| Read (* Read-only access *)
| Mutate (* Definitely mutates *)
| Capture (* Aliases/captures value *)
| Store (* Writes value *)
type value_kind =
| Primitive
| Mutable
| Immutable
type function_signature = {
positional_params : effect_kind list;
rest_param : effect_kind option;
return_type : typ;
return_value_kind : value_kind;
mutable_only_if_operands_are_mutable : bool;
aliasing : aliasing_effect list;
}
type aliasing_effect =
| Capture of { from : int; into : int } (* @from -> @into *)
| Alias of { from : int; into : int } (* Alias relationship *)
| Mutate of { value : int } (* Mutates @value *)
| Freeze of { value : int } (* Freezes @value *)
| Create of { into : int; value : value_kind } (* Creates new @into *)
9. 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 generic traversal and transformation utilities for HIR.
Key Abstractions
Iteration Over Operands
// Yield all Place values referenced by an instruction
export function* eachInstructionOperand(instr: Instruction): Iterable<Place> {
yield* eachInstructionValueOperand(instr.value);
}
// Handle each instruction variant
export function* eachInstructionValueOperand(
instrValue: InstructionValue,
): Iterable<Place> {
switch (instrValue.kind) {
case 'BinaryExpression':
yield instrValue.left;
yield instrValue.right;
case 'CallExpression':
yield instrValue.callee;
yield* eachCallArgument(instrValue.args);
case 'PropertyLoad':
yield instrValue.object;
// ... all variants
}
}
Iteration Over L-values
// Yield all lvalue places (targets of assignment)
export function* eachInstructionLValueWithKind(
instr: Instruction,
): Iterable<[Place, InstructionKind]> {
switch (instr.value.kind) {
case 'DeclareLocal':
case 'StoreLocal':
yield [instr.value.lvalue.place, instr.value.lvalue.kind];
case 'Destructure':
for (const place of eachPatternOperand(instr.value.lvalue.pattern)) {
yield [place, instr.value.lvalue.kind];
}
}
if (instr.lvalue !== null) {
yield [instr.lvalue, InstructionKind.Reassign]; // Infer reassign
}
}
Iteration Over Terminal Successors
// Yield all successor block IDs for control flow
export function* eachTerminalSuccessor(terminal: Terminal): Iterable<BlockId> {
switch (terminal.kind) {
case 'goto':
yield terminal.block;
case 'if':
yield terminal.consequent;
yield terminal.alternate;
case 'switch':
for (const case_ of terminal.cases) {
yield case_.block;
}
case 'while':
yield terminal.test;
// ... all variants
}
}
Transformation (Mapping)
// Transform all Place references in an instruction
export function mapInstructionOperands(
instr: Instruction,
fn: (place: Place) => Place,
): void {
mapInstructionValueOperands(instr.value, fn);
}
// Map terminal successors
export function mapTerminalSuccessors(
terminal: Terminal,
fn: (block: BlockId) => BlockId,
): Terminal {
switch (terminal.kind) {
case 'if': {
const consequent = fn(terminal.consequent);
const alternate = fn(terminal.alternate);
const fallthrough = fn(terminal.fallthrough);
return {kind: 'if', test: terminal.test, consequent, alternate, fallthrough, ...};
}
// ... all variants
}
}
Pattern Traversal
// Yield all places in a pattern
export function* eachPatternOperand(pattern: Pattern): Iterable<Place> {
switch (pattern.kind) {
case 'ArrayPattern':
for (const item of pattern.items) {
if (item.kind === 'Identifier') yield item;
else if (item.kind === 'Spread') yield item.place;
}
case 'ObjectPattern':
for (const prop of pattern.properties) {
if (prop.kind === 'ObjectProperty') yield prop.place;
else if (prop.kind === 'Spread') yield prop.place;
}
}
}
Scope Block Traversal
// Helper for traversing reactive scope boundaries
export class ScopeBlockTraversal {
#activeScopes: Array<ScopeId> = [];
blockInfos: Map<BlockId, BlockInfo> = new Map();
recordScopes(block: BasicBlock): void {
if (blockInfo?.kind === 'begin') {
this.#activeScopes.push(blockInfo.scope.id);
} else if (blockInfo?.kind === 'end') {
this.#activeScopes.pop();
}
if (block.terminal.kind === 'scope' || block.terminal.kind === 'pruned-scope') {
this.blockInfos.set(block.terminal.block, {kind: 'begin', ...});
this.blockInfos.set(block.terminal.fallthrough, {kind: 'end', ...});
}
}
isScopeActive(scopeId: ScopeId): boolean {
return this.#activeScopes.indexOf(scopeId) !== -1;
}
get currentScope(): ScopeId | null {
return this.#activeScopes.at(-1) ?? null;
}
}
CloudLang Relevance
- eachInstructionOperand: Very useful for data flow analysis
- eachTerminalSuccessor: Critical for CFG traversal
- mapInstructionOperands: Useful for SSA transformation
- ScopeBlockTraversal: Adapt for CloudLang's "effect scopes" instead of reactive scopes
10. PrintHIR (Debug Printing)
File
/home/jm/data/ai/cloudlang-trees/react/react/compiler/packages/babel-plugin-react-compiler/src/HIR/PrintHIR.ts
Purpose
Provides human-readable debug output for HIR structures.
Example Output Format
function Foo(a: $0, b: $1): $2 {
'use strict'
bb0 (block):
[1] $0 = Primitive {value: 42}
[2] $1 = Primitive {value: "hello"}
[3] $2 = Binary $0 + $1
[4] Return Explicit $2
bb1 (value):
[5] $3 = Call $4($0)
[6] Return Implicit $5
}
CloudLang Relevance
Adapt for debugging OCaml HIR. Similar format with OCaml syntax.
11. CloudLang Mapping
Current CloudLang Architecture
lib/ast.ml (44 lines - Minimal ESTree subset)
type var_kind = Const | Let | Var
type literal_value =
| LString of string
| LNumber of float
| LBool of bool
| LNull
| LUndefined
type node =
| Program of { body : node list }
| VariableDeclaration of { kind : var_kind; declarations : node list }
| VariableDeclarator of { id : node; init : node option }
| Identifier of { name : string }
| Literal of { value : literal_value }
| ArrowFunctionExpression of { params : node list; body : node; async_ : bool }
| BlockStatement of { body : node list }
| ReturnStatement of { argument : node option }
| IfStatement of { test : node; consequent : node; alternate : node option }
| ExpressionStatement of { expression : node }
| CallExpression of { callee : node; arguments : node list }
| MemberExpression of { object_ : node; property : node }
| NewExpression of { callee : node; arguments : node list }
| BinaryExpression of { operator : string; left : node; right : node }
| LogicalExpression of { operator : string; left : node; right : node }
| AwaitExpression of { argument : node }
| RawCode of { code : string }
| ExportNamedDeclaration of { specifiers : node list }
| ExportSpecifier of { local : node }
lib/ir.ml (68 lines - Direct Cloudflare config output)
type binding =
| D1Binding of { name : string; database_id : string }
| KVBinding of { name : string; namespace_id : string }
| R2Binding of { name : string; bucket_name : string }
| ServiceBinding of { name : string; service : string }
type worker = {
name : string;
script : string;
bindings : binding list;
routes : string list;
}
type config = resource list
Missing Layer: HIR
CloudLang has no HIR - it goes directly from 19-node AST to Cloudflare config. This limits optimization opportunities and complicates closure decomposition.
Proposed CloudLang HIR Types (OCaml)
(* Core identity types - using phantom types for safety *)
module Block_id : sig
type t
val make : int -> t
val to_int : t -> int
end
module Instruction_id : sig
type t
val make : int -> t
val to_int : t -> int
end
module Identifier_id : sig
type t
val make : int -> t
val to_int : t -> int
end
(* Effect system - simplified from React Compiler *)
type effect_kind =
| Unknown (* Default, before analysis *)
| Read (* Read-only access *)
| Mutate (* Definitely mutates *)
| Capture (* Aliases into another location *)
| Freeze (* Marks as immutable *)
(* Value kinds - simplified *)
type value_kind =
| Primitive (* number, string, bool *)
| Mutable (* Can be modified *)
| Immutable (* Cannot be modified *)
(* Block kinds *)
type block_kind =
| Block (* Statement block *)
| Value (* Expression block *)
| Loop (* Loop body *)
| Catch (* Catch block *)
(* Source location *)
type source_loc = {
file : string option;
line : int option;
column : int option;
}
type generated = Generated
(* Mutable range for tracking liveness *)
type mutable_range = {
start : Instruction_id.t; (* Inclusive *)
end : Instruction_id.t; (* Exclusive *)
}
(* Identifier - canonical variable definition *)
type identifier = {
id : Identifier_id.t; (* SSA instance ID *)
declaration_id : Identifier_id.t; (* Original declaration ID *)
name : string option; (* null for temporaries *)
mutable_range : mutable_range;
kind : value_kind; (* Inferred type *)
loc : source_loc;
}
(* Place - value reference with metadata *)
type place = {
identifier : identifier;
effect : effect_kind;
loc : source_loc;
}
(* Pattern for destructuring - minimal initially *)
type pattern =
| Pattern_identifier of identifier
| Pattern_array of pattern list
| Pattern_object of (string * pattern) list
| Pattern_spread of identifier
(* Instruction value variants - minimal subset for CloudLang *)
type instruction_value =
(* Loads *)
| Load_local of { place : place }
| Load_global of { name : string }
(* Stores *)
| Declare_local of { lvalue : identifier; kind : var_kind }
| Store_local of { lvalue : identifier; value : place }
(* Primitives *)
| Primitive of {
value : literal_value;
}
(* Expressions *)
| Binary_expression of {
operator : string; (* +, -, *, /, ==, etc. *)
left : place;
right : place;
}
| Unary_expression of {
operator : string; (* !, -, typeof, etc. *)
value : place;
}
(* Objects/Arrays *)
| Object_expression of {
properties : (string * place) list;
}
| Array_expression of {
elements : (place option) list; (* None for holes *)
}
(* Property access *)
| Property_load of {
object : place;
property : string; (* Literal property *)
}
| Property_store of {
object : place;
property : string;
value : place;
}
| Computed_load of {
object : place;
property : place;
}
| Computed_store of {
object : place;
property : place;
value : place;
}
(* Function calls *)
| Call_expression of {
callee : place;
args : place list;
}
| Method_call of {
receiver : place;
method : string;
args : place list;
}
(* Functions *)
| Function_expression of {
name : string option;
params : identifier list;
body : hir_function;
captures : identifier list;
}
(* Iteration - for CloudLang-specific patterns *)
| Get_iterator of { collection : place }
| Iterator_next of { iterator : place }
(* Raw code insertion for Cloudflare-specific constructs *)
| Raw_code of { code : string }
(* Instruction *)
type instruction = {
id : Instruction_id.t;
lvalue : place option; (* None for statements without result *)
value : instruction_value;
loc : source_loc;
}
(* Phi node for SSA *)
type phi = {
place : place;
operands : (Block_id.t * place) list; (* Map from predecessor to value *)
}
(* Basic block *)
type basic_block = {
kind : block_kind;
id : Block_id.t;
instructions : instruction list;
terminal : terminal;
preds : Block_id.t list;
phis : phi list;
}
(* Terminal variants *)
type terminal =
(* Control flow - unconditional *)
| Goto of {
block : Block_id.t;
variant : goto_variant;
}
| Unreachable
(* Control flow - conditional *)
| If of {
test : place;
consequent : Block_id.t;
alternate : Block_id.t;
fallthrough : Block_id.t;
}
| Switch of {
test : place;
cases : (place option * Block_id.t) list; (* None for default *)
fallthrough : Block_id.t;
}
(* Loops *)
| While of {
test_block : Block_id.t;
loop_block : Block_id.t;
fallthrough : Block_id.t;
}
| For_each of {
init_block : Block_id.t; (* Setup: GetIterator *)
loop_block : Block_id.t; (* Body with IteratorNext *)
fallthrough : Block_id.t;
}
(* Control flow - special *)
| Logical of {
operator : string; (* &&, ||, ?? *)
test_block : Block_id.t;
fallthrough : Block_id.t;
}
| Ternary of {
test_block : Block_id.t;
fallthrough : Block_id.t;
}
(* Exceptions *)
| Try of {
try_block : Block_id.t;
handler_block : Block_id.t;
fallthrough : Block_id.t;
}
(* Exit *)
| Return of { value : place }
| Throw of { value : place }
type goto_variant =
| Break
| Continue
(* HIR - Control flow graph *)
type hir = {
entry : Block_id.t;
blocks : basic_block Block_id.t Map.t; (* Ordered map *)
}
(* HIR Function *)
type hir_function = {
name : string option;
params : identifier list;
returns : place;
body : hir;
is_async : bool;
captures : identifier list;
loc : source_loc;
}
(* Program - top-level compilation unit *)
type hir_program = {
functions : hir_function list;
exports : string list;
}
Adaptation Strategy
What to Adapt:
- Core CFG Structure: HIR, BasicBlock, Terminal, Instruction
- Identity System: Branded ID types for type safety
- SSA Form: Phi nodes, Identifier.id vs declarationId
- Effect Tracking: Read/Mutate/Capture for Cloudflare API analysis
- Mutable Range: For liveness analysis and optimization
- Control Flow: If, While, ForEach, Switch, Try
- Expressions: Binary, Unary, Call, Property access
- Visitor Patterns: eachInstructionOperand, eachTerminalSuccessor
What to Skip/Defer:
- JSX: All JSX nodes - CloudLang doesn't target React
- Reactive Scopes: scope/pruned-scope terminals, ReactiveScope type
- Memoization: StartMemoize, FinishMemoize
- Optional Chaining: optional terminal
- Complex Patterns: ArrayPattern, ObjectPattern (initially)
- Destructuring: Destructure instruction (initially)
- Template Literals: TaggedTemplateExpression (simple templates only)
- Class Support: NewExpression with constructors (initially)
- Async/Await: Async operations modeled implicitly
- Global Assignment: StoreGlobal (Cloudflare APIs don't support)
CloudLang-Specific Additions:
- ForEach Terminal: Custom for CloudLang's iteration patterns (arrays, maps)
- GetIterator/IteratorNext: For CloudLang-specific iteration
- RawCode: Insertion points for Cloudflare-specific code
- Durable Object Markers: DO/D1/R2 binding annotations
- Coordination Intent: For distributed coordination patterns
Example Transformation
Before (AST):
// Input CloudLang code
const user = await KV.get("user:123");
const greeting = `Hello, ${user?.name || "Stranger"}!`;
return greeting;
After (HIR):
function <<anonymous>>($0: #1): $2 {
bb0 (block):
[1] $1 = Primitive {value: "user:123"}
[2] $3 = Call KV.get($1) (* RawCode inserted for KV API *)
[3] $4 = PropertyLoad object=$3 property="name"
[4] $5 = Primitive {value: "Stranger"}
[5] $6 = Ternary test=$5 consequent=$4 alternate=$5
[6] $7 = Primitive {value: "Hello, "}
[8] $8 = Primitive {value: "!"}
[9] $9 = RawCode {code: "`${$7}${$6}${$8}`"}
[10] Return $9
}
Incremental Expansion Path
Phase 1 (MVP):
- Control flow: If, While
- Expressions: Binary, Call, Property access
- Functions: Simple closures
- SSA: Basic phi nodes
- Effects: Read/Mutate tracking
Phase 2:
- Loops: ForEach
- Arrays: Array expressions, iteration
- Objects: Object expressions, computed access
- Try/Catch: Error handling
- Pattern matching: Simple destructuring
Phase 3:
- Advanced control flow: Switch, Ternary
- Advanced effects: Capture, Freeze
- Optimization: Dead code elimination, constant folding
- SSA optimization: Copy propagation, dead store elimination
Phase 4:
- Cloudflare-specific: DO/D1/R2 annotations
- Coordination: Per-entity serial, global singleton
- Advanced patterns: Realtime fanout, high-fan-in coordination
Summary
The React Compiler's HIR system is a sophisticated SSA-based CFG intermediate representation designed for React-specific optimizations (reactive scope analysis, memoization). Key innovations include:
- Branded opaque types for compile-time ID safety
- Effect tracking on every Place reference
- Reactive scope boundaries as first-class CFG nodes
- Phi nodes for SSA join points
- Mutable ranges for liveness tracking
- Comprehensive visitor utilities for traversal and transformation
For CloudLang, we should:
- Adapt the core CFG structure but strip React-specific features
- Keep the ID branding pattern for OCaml type safety
- Simplify effect system to focus on mutability (Cloudflare APIs are immutable)
- Use SSA form for closure decomposition optimization
- Build incrementally starting with 19 AST nodes mapped to minimal HIR
- Add CloudLang-specific constructs (ForEach iteration, DO/D1/R2 bindings)
The key insight is that HIR enables analyses and optimizations that are impossible directly on AST or final IR. Adding this layer to CloudLang will significantly improve the quality of generated Cloudflare configurations.