Skip to main content

cjc_mir/
lib.rs

1//! CJC MIR (Mid-level Intermediate Representation)
2//!
3//! MIR is a control-flow graph (CFG) of basic blocks. Every value is an
4//! explicit temporary. This is the level where:
5//! - Pattern matching is compiled to decision trees (Stage 2.2)
6//! - Closures are lambda-lifted (Stage 2.1)
7//! - `nogc` verification runs (Stage 2.4)
8//! - Optimization passes operate (Stage 2.4)
9//!
10//! For Milestone 2.0, MIR is a simplified representation that mirrors HIR
11//! closely — we lower HIR items into MIR functions with basic blocks for
12//! straight-line code, if/else, while, and function calls.
13
14pub mod cfg;
15pub mod dominators;
16pub mod escape;
17pub mod inspect;
18pub mod loop_analysis;
19pub mod monomorph;
20pub mod nogc_verify;
21pub mod optimize;
22pub mod reduction;
23pub mod ssa;
24pub mod ssa_loop_overlay;
25pub mod ssa_optimize;
26pub mod verify;
27
28use cjc_ast::{BinOp, UnaryOp, Visibility};
29pub use escape::AllocHint;
30
31// ---------------------------------------------------------------------------
32// IDs
33// ---------------------------------------------------------------------------
34
35/// Unique identifier for a MIR function within a [`MirProgram`].
36///
37/// Assigned sequentially during HIR-to-MIR lowering. The synthetic `__main`
38/// entry function and lambda-lifted closures each receive their own ID.
39#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
40pub struct MirFnId(pub u32);
41
42/// Unique identifier for a basic block within a [`cfg::MirCfg`].
43///
44/// Block IDs are dense indices into `MirCfg::basic_blocks`. `BlockId(0)` is
45/// always the entry block. IDs are assigned deterministically in creation order.
46#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
47pub struct BlockId(pub u32);
48
49/// Unique identifier for a temporary value in the MIR.
50///
51/// Reserved for future use when MIR transitions to explicit temporaries
52/// instead of named variables.
53#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
54pub struct TempId(pub u32);
55
56// ---------------------------------------------------------------------------
57// Program
58// ---------------------------------------------------------------------------
59
60/// A MIR program is a collection of functions + struct defs + an entry point.
61#[derive(Debug, Clone)]
62pub struct MirProgram {
63    pub functions: Vec<MirFunction>,
64    pub struct_defs: Vec<MirStructDef>,
65    pub enum_defs: Vec<MirEnumDef>,
66    /// Top-level statements (let bindings, expr stmts) are collected into
67    /// a synthetic `__main` function.
68    pub entry: MirFnId,
69}
70
71/// A struct (or record/class) type definition at the MIR level.
72///
73/// Struct definitions carry through from HIR without modification.
74/// The [`is_record`](MirStructDef::is_record) flag distinguishes immutable
75/// value-type records from mutable class-style structs.
76#[derive(Debug, Clone)]
77pub struct MirStructDef {
78    /// Name of the struct type.
79    pub name: String,
80    /// Fields as `(field_name, type_name)` pairs, in declaration order.
81    pub fields: Vec<(String, String)>,
82    /// True if this is a record (immutable value type).
83    pub is_record: bool,
84    /// Visibility of this struct definition.
85    pub vis: Visibility,
86}
87
88/// An enum type definition at the MIR level.
89///
90/// Contains the enum name and its ordered list of variant definitions.
91#[derive(Debug, Clone)]
92pub struct MirEnumDef {
93    /// Name of the enum type.
94    pub name: String,
95    /// Variant definitions in declaration order.
96    pub variants: Vec<MirVariantDef>,
97}
98
99/// A single variant of a [`MirEnumDef`].
100///
101/// Each variant can carry zero or more positional fields identified by
102/// their type names.
103#[derive(Debug, Clone)]
104pub struct MirVariantDef {
105    /// Name of this variant.
106    pub name: String,
107    /// Positional field type names.
108    pub fields: Vec<String>,
109}
110
111// ---------------------------------------------------------------------------
112// Functions
113// ---------------------------------------------------------------------------
114
115/// A MIR function definition.
116///
117/// Contains both the tree-form [`MirBody`] and an optional CFG representation.
118/// The tree-form body is canonical after lowering; the CFG is built on demand
119/// via [`build_cfg`](MirFunction::build_cfg) for analyses that require
120/// explicit control-flow edges (SSA, dominators, loop analysis).
121///
122/// Lambda-lifted closures and the synthetic `__main` entry function are
123/// represented as regular `MirFunction` instances.
124#[derive(Debug, Clone)]
125pub struct MirFunction {
126    /// Unique function ID within the program.
127    pub id: MirFnId,
128    /// Function name. Lambda-lifted closures use `__closure_N` names.
129    /// Impl methods use `Target.method` qualified names.
130    pub name: String,
131    /// Generic type parameters as `(param_name, trait_bounds)` pairs.
132    pub type_params: Vec<(String, Vec<String>)>,
133    /// Function parameters in declaration order.
134    pub params: Vec<MirParam>,
135    /// Return type name, if explicitly annotated.
136    pub return_type: Option<String>,
137    /// Tree-form function body (statements + optional tail expression).
138    pub body: MirBody,
139    /// Whether this function is annotated with `@nogc`.
140    /// When true, the [`nogc_verify`] module rejects any GC-triggering operations.
141    pub is_nogc: bool,
142    /// CFG representation of this function's body.
143    /// Built lazily from tree-form `body` via `build_cfg()`.
144    /// When present, this is the canonical representation for the CFG executor.
145    pub cfg_body: Option<cfg::MirCfg>,
146    /// Decorator names applied to this function (e.g., `@memoize`, `@trace`).
147    pub decorators: Vec<String>,
148    /// Visibility of this function definition.
149    pub vis: Visibility,
150}
151
152/// A function parameter at the MIR level.
153///
154/// Parameters carry their type annotation name and optional default value.
155/// For lambda-lifted closures, capture parameters appear first with type
156/// `"any"` (type-erased at MIR level).
157#[derive(Debug, Clone)]
158pub struct MirParam {
159    /// Parameter name.
160    pub name: String,
161    /// Type annotation name (e.g., `"i64"`, `"f64"`, `"any"`).
162    pub ty_name: String,
163    /// Optional default value expression for this parameter.
164    pub default: Option<MirExpr>,
165    /// Variadic parameter: collects remaining args into an array.
166    pub is_variadic: bool,
167}
168
169impl MirFunction {
170    /// Build the CFG representation from the tree-form body.
171    /// Stores the result in `cfg_body`.
172    pub fn build_cfg(&mut self) {
173        let cfg = cfg::CfgBuilder::build(&self.body);
174        self.cfg_body = Some(cfg);
175    }
176
177    /// Return a reference to the CFG body, building it on demand if needed.
178    ///
179    /// Subsequent calls reuse the cached CFG. Prefer [`build_cfg`](Self::build_cfg)
180    /// if you need to force a rebuild.
181    pub fn cfg(&mut self) -> &cfg::MirCfg {
182        if self.cfg_body.is_none() {
183            self.build_cfg();
184        }
185        self.cfg_body.as_ref().unwrap()
186    }
187}
188
189impl MirProgram {
190    /// Build CFG for all functions in this program.
191    pub fn build_all_cfgs(&mut self) {
192        for func in &mut self.functions {
193            func.build_cfg();
194        }
195    }
196}
197
198/// The body of a MIR function — a list of MIR statements.
199/// In Milestone 2.0 we use a simplified tree-form (not full CFG with basic
200/// blocks). This is extended to a proper CFG in Milestone 2.2+ for pattern
201/// matching compilation.
202#[derive(Debug, Clone)]
203pub struct MirBody {
204    pub stmts: Vec<MirStmt>,
205    pub result: Option<Box<MirExpr>>,
206}
207
208// ---------------------------------------------------------------------------
209// Statements
210// ---------------------------------------------------------------------------
211
212/// A MIR statement.
213///
214/// Statements represent side-effecting or control-flow operations in the
215/// tree-form MIR body. In the CFG representation, control-flow statements
216/// (`If`, `While`, `Break`, `Continue`) are compiled into basic block
217/// terminators and edges.
218#[derive(Debug, Clone)]
219pub enum MirStmt {
220    /// Variable binding: `let [mut] name = init;`
221    ///
222    /// The [`alloc_hint`](AllocHint) is populated by escape analysis after
223    /// lowering to guide allocation strategy.
224    Let {
225        /// Binding name.
226        name: String,
227        /// Whether the binding is mutable.
228        mutable: bool,
229        /// Initializer expression.
230        init: MirExpr,
231        /// Escape analysis annotation. `None` before analysis runs.
232        alloc_hint: Option<AllocHint>,
233    },
234    /// A standalone expression statement (e.g., function call, assignment).
235    Expr(MirExpr),
236    /// Conditional statement: `if cond { then } [else { else_ }]`.
237    If {
238        /// Condition expression (must evaluate to a boolean).
239        cond: MirExpr,
240        /// Body executed when the condition is true.
241        then_body: MirBody,
242        /// Optional body executed when the condition is false.
243        else_body: Option<MirBody>,
244    },
245    /// While loop: `while cond { body }`.
246    While {
247        /// Loop condition expression.
248        cond: MirExpr,
249        /// Loop body.
250        body: MirBody,
251    },
252    /// Return from the current function with an optional value.
253    Return(Option<MirExpr>),
254    /// Break out of the innermost enclosing loop.
255    Break,
256    /// Continue to the next iteration of the innermost enclosing loop.
257    Continue,
258    /// A `nogc { ... }` block where GC-triggering operations are forbidden.
259    ///
260    /// Verified by [`nogc_verify::verify_nogc`].
261    NoGcBlock(MirBody),
262}
263
264// ---------------------------------------------------------------------------
265// Expressions
266// ---------------------------------------------------------------------------
267
268/// A MIR expression node.
269///
270/// Wraps a [`MirExprKind`] discriminant. All MIR expressions are trees
271/// (no sharing / DAG structure).
272#[derive(Debug, Clone)]
273pub struct MirExpr {
274    /// The kind of this expression.
275    pub kind: MirExprKind,
276}
277
278/// The discriminant for a MIR expression.
279///
280/// Covers literals, variables, operators, control flow, pattern matching,
281/// closures, linalg opcodes, and container constructors. Each variant
282/// corresponds to a distinct runtime operation in both the tree-walk
283/// interpreter (`cjc-eval`) and the MIR executor (`cjc-mir-exec`).
284#[derive(Debug, Clone)]
285pub enum MirExprKind {
286    /// 64-bit signed integer literal.
287    IntLit(i64),
288    /// 64-bit IEEE 754 floating-point literal.
289    FloatLit(f64),
290    /// Boolean literal (`true` or `false`).
291    BoolLit(bool),
292    /// UTF-8 string literal.
293    StringLit(String),
294    /// Byte string literal (`b"..."`).
295    ByteStringLit(Vec<u8>),
296    /// Single byte character literal (`b'x'`).
297    ByteCharLit(u8),
298    /// Raw string literal (`r"..."`).
299    RawStringLit(String),
300    /// Raw byte string literal (`rb"..."`).
301    RawByteStringLit(Vec<u8>),
302    /// Regex literal with pattern and flags.
303    RegexLit {
304        /// The regex pattern string.
305        pattern: String,
306        /// Regex flags (e.g., `"gi"`).
307        flags: String,
308    },
309    /// Tensor literal: a 2D grid of expressions (rows x columns).
310    TensorLit {
311        /// Each inner `Vec` is one row of the tensor.
312        rows: Vec<Vec<MirExpr>>,
313    },
314    /// NA (missing value) literal.
315    NaLit,
316    /// Variable reference by name.
317    Var(String),
318    /// Binary operation.
319    /// Binary operation.
320    Binary {
321        /// The binary operator.
322        op: BinOp,
323        /// Left-hand operand.
324        left: Box<MirExpr>,
325        /// Right-hand operand.
326        right: Box<MirExpr>,
327    },
328    /// Unary operation (negation, logical not, bitwise not).
329    Unary {
330        /// The unary operator.
331        op: UnaryOp,
332        /// The operand.
333        operand: Box<MirExpr>,
334    },
335    /// Function or closure call.
336    Call {
337        /// The callee expression (usually a [`Var`](MirExprKind::Var) or
338        /// [`Field`](MirExprKind::Field) for method calls).
339        callee: Box<MirExpr>,
340        /// Positional arguments.
341        args: Vec<MirExpr>,
342    },
343    /// Field access: `object.name`.
344    Field {
345        /// The object being accessed.
346        object: Box<MirExpr>,
347        /// Field name.
348        name: String,
349    },
350    /// Single-index access: `object[index]`.
351    Index {
352        /// The collection being indexed.
353        object: Box<MirExpr>,
354        /// The index expression.
355        index: Box<MirExpr>,
356    },
357    /// Multi-dimensional index access: `object[i, j, ...]`.
358    MultiIndex {
359        /// The collection being indexed.
360        object: Box<MirExpr>,
361        /// Index expressions for each dimension.
362        indices: Vec<MirExpr>,
363    },
364    /// Assignment: `target = value`.
365    Assign {
366        /// Assignment target (variable, field, or index expression).
367        target: Box<MirExpr>,
368        /// Value being assigned.
369        value: Box<MirExpr>,
370    },
371    /// Block expression: evaluates a [`MirBody`] and returns its result.
372    Block(MirBody),
373    /// Struct literal: `Name { field1: expr1, field2: expr2, ... }`.
374    StructLit {
375        /// Struct type name.
376        name: String,
377        /// Field initializers as `(name, value)` pairs.
378        fields: Vec<(String, MirExpr)>,
379    },
380    /// Array literal: `[expr1, expr2, ...]`.
381    ArrayLit(Vec<MirExpr>),
382    /// Column reference in a data DSL context (e.g., `col("name")`).
383    Col(String),
384    /// Lambda expression (non-capturing).
385    Lambda {
386        /// Lambda parameters.
387        params: Vec<MirParam>,
388        /// Lambda body expression.
389        body: Box<MirExpr>,
390    },
391    /// Create a closure: captures + a reference to the lifted function.
392    /// At runtime, evaluates each capture expression and bundles them with
393    /// the function name into a Closure value.
394    MakeClosure {
395        /// Name of the lambda-lifted top-level function.
396        fn_name: String,
397        /// Expressions that produce the captured values (evaluated at closure
398        /// creation time). Order matches the extra leading params of the
399        /// lifted function.
400        captures: Vec<MirExpr>,
401    },
402    /// If expression: `if cond { then } [else { else_ }]`.
403    ///
404    /// Used as both a statement and an expression (the branch bodies can
405    /// produce values).
406    If {
407        /// Condition expression.
408        cond: Box<MirExpr>,
409        /// Body evaluated when the condition is true.
410        then_body: MirBody,
411        /// Optional body evaluated when the condition is false.
412        else_body: Option<MirBody>,
413    },
414    /// Match expression compiled as a decision tree.
415    /// Each arm is tried in order; first matching arm's body is evaluated.
416    Match {
417        /// The value being matched against.
418        scrutinee: Box<MirExpr>,
419        /// Match arms in order of priority.
420        arms: Vec<MirMatchArm>,
421    },
422    /// Enum variant literal constructor: `EnumName::Variant(fields...)`.
423    VariantLit {
424        /// Enum type name.
425        enum_name: String,
426        /// Variant name.
427        variant: String,
428        /// Positional field values.
429        fields: Vec<MirExpr>,
430    },
431    /// Tuple literal: `(expr1, expr2, ...)`.
432    TupleLit(Vec<MirExpr>),
433    /// LU decomposition opcode.
434    LinalgLU {
435        /// Matrix operand.
436        operand: Box<MirExpr>,
437    },
438    /// QR decomposition opcode.
439    LinalgQR {
440        /// Matrix operand.
441        operand: Box<MirExpr>,
442    },
443    /// Cholesky decomposition opcode.
444    LinalgCholesky {
445        /// Matrix operand (must be symmetric positive-definite).
446        operand: Box<MirExpr>,
447    },
448    /// Matrix inverse opcode.
449    LinalgInv {
450        /// Matrix operand.
451        operand: Box<MirExpr>,
452    },
453    /// Broadcast a tensor to a target shape (zero-copy view with stride=0).
454    Broadcast {
455        /// Tensor operand to broadcast.
456        operand: Box<MirExpr>,
457        /// Target shape dimensions.
458        target_shape: Vec<MirExpr>,
459    },
460    /// Unit/void value (no meaningful result).
461    Void,
462}
463
464// ---------------------------------------------------------------------------
465// Match / Pattern types (MIR level)
466// ---------------------------------------------------------------------------
467
468/// A match arm at MIR level: pattern + body.
469#[derive(Debug, Clone)]
470pub struct MirMatchArm {
471    pub pattern: MirPattern,
472    pub body: MirBody,
473}
474
475/// A pattern at MIR level.
476#[derive(Debug, Clone)]
477pub enum MirPattern {
478    /// Wildcard: matches anything, binds nothing.
479    Wildcard,
480    /// Binding: matches anything, binds the value to a name.
481    Binding(String),
482    /// Literal patterns
483    LitInt(i64),
484    LitFloat(f64),
485    LitBool(bool),
486    LitString(String),
487    /// Tuple destructuring
488    Tuple(Vec<MirPattern>),
489    /// Struct destructuring
490    Struct {
491        name: String,
492        fields: Vec<(String, MirPattern)>,
493    },
494    /// Enum variant pattern
495    Variant {
496        enum_name: String,
497        variant: String,
498        fields: Vec<MirPattern>,
499    },
500}
501
502// ===========================================================================
503// HIR -> MIR Lowering
504// ===========================================================================
505
506use cjc_hir::*;
507
508/// Lowers HIR into MIR.
509///
510/// Performs a single-pass traversal of the [`HirProgram`], converting each
511/// HIR item into its MIR equivalent. During lowering:
512///
513/// - Top-level statements are collected into a synthetic `__main` function.
514/// - Closures are lambda-lifted into top-level functions with extra leading
515///   parameters for captured values, and replaced with [`MirExprKind::MakeClosure`].
516/// - Impl methods are flattened to qualified `Target.method` names.
517/// - Traits produce no MIR output (metadata only).
518///
519/// # Usage
520///
521/// ```rust,ignore
522/// let mut lowering = HirToMir::new();
523/// let mir_program = lowering.lower_program(&hir_program);
524/// ```
525pub struct HirToMir {
526    next_fn_id: u32,
527    next_lambda_id: u32,
528    /// Lambda-lifted functions accumulated during lowering.
529    /// These are appended to the MirProgram's function list.
530    lifted_functions: Vec<MirFunction>,
531}
532
533impl HirToMir {
534    /// Create a new HIR-to-MIR lowering pass with fresh ID counters.
535    pub fn new() -> Self {
536        Self {
537            next_fn_id: 0,
538            next_lambda_id: 0,
539            lifted_functions: Vec::new(),
540        }
541    }
542
543    fn fresh_fn_id(&mut self) -> MirFnId {
544        let id = MirFnId(self.next_fn_id);
545        self.next_fn_id += 1;
546        id
547    }
548
549    fn fresh_lambda_name(&mut self) -> String {
550        let name = format!("__closure_{}", self.next_lambda_id);
551        self.next_lambda_id += 1;
552        name
553    }
554
555    /// Lower a HIR program to MIR.
556    pub fn lower_program(&mut self, hir: &HirProgram) -> MirProgram {
557        let mut functions = Vec::new();
558        let mut struct_defs = Vec::new();
559        let mut enum_defs = Vec::new();
560        let mut main_stmts: Vec<MirStmt> = Vec::new();
561
562        for item in &hir.items {
563            match item {
564                HirItem::Fn(f) => {
565                    functions.push(self.lower_fn(f));
566                }
567                HirItem::Struct(s) => {
568                    struct_defs.push(MirStructDef {
569                        name: s.name.clone(),
570                        fields: s.fields.clone(),
571                        is_record: false,
572                        vis: s.vis,
573                    });
574                }
575                HirItem::Class(c) => {
576                    struct_defs.push(MirStructDef {
577                        name: c.name.clone(),
578                        fields: c.fields.clone(),
579                        is_record: false,
580                        vis: c.vis,
581                    });
582                }
583                HirItem::Record(r) => {
584                    struct_defs.push(MirStructDef {
585                        name: r.name.clone(),
586                        fields: r.fields.clone(),
587                        is_record: true,
588                        vis: r.vis,
589                    });
590                }
591                HirItem::Enum(e) => {
592                    enum_defs.push(MirEnumDef {
593                        name: e.name.clone(),
594                        variants: e
595                            .variants
596                            .iter()
597                            .map(|v| MirVariantDef {
598                                name: v.name.clone(),
599                                fields: v.fields.clone(),
600                            })
601                            .collect(),
602                    });
603                }
604                HirItem::Let(l) => {
605                    main_stmts.push(MirStmt::Let {
606                        name: l.name.clone(),
607                        mutable: l.mutable,
608                        init: self.lower_expr(&l.init),
609                        alloc_hint: None,
610                    });
611                }
612                HirItem::Stmt(s) => {
613                    main_stmts.push(self.lower_stmt(s));
614                }
615                HirItem::Impl(i) => {
616                    for method in &i.methods {
617                        // Register as qualified name: Target.method
618                        let mut mir_fn = self.lower_fn(method);
619                        mir_fn.name = format!("{}.{}", i.target, method.name);
620                        functions.push(mir_fn);
621                    }
622                }
623                HirItem::Trait(_) => {
624                    // Traits are metadata only; no MIR output
625                }
626            }
627        }
628
629        // Create __main entry function from top-level statements
630        let main_id = self.fresh_fn_id();
631        functions.push(MirFunction {
632            id: main_id,
633            name: "__main".to_string(),
634            type_params: vec![],
635            params: vec![],
636            return_type: None,
637            body: MirBody {
638                stmts: main_stmts,
639                result: None,
640            },
641            is_nogc: false,
642            cfg_body: None,
643            decorators: vec![],
644            vis: Visibility::Private,
645        });
646
647        // Append all lambda-lifted functions
648        functions.append(&mut self.lifted_functions);
649
650        MirProgram {
651            functions,
652            struct_defs,
653            enum_defs,
654            entry: main_id,
655        }
656    }
657
658    /// Lower a single HIR function definition to a [`MirFunction`].
659    ///
660    /// Assigns a fresh [`MirFnId`] and recursively lowers parameters, body
661    /// statements, and the tail expression. Closures encountered within the
662    /// body are lambda-lifted and accumulated in `self.lifted_functions`.
663    pub fn lower_fn(&mut self, f: &HirFn) -> MirFunction {
664        let id = self.fresh_fn_id();
665        let params = f
666            .params
667            .iter()
668            .map(|p| MirParam {
669                name: p.name.clone(),
670                ty_name: p.ty_name.clone(),
671                default: p.default.as_ref().map(|d| self.lower_expr(d)),
672                is_variadic: p.is_variadic,
673            })
674            .collect();
675        let body = self.lower_block(&f.body);
676        MirFunction {
677            id,
678            name: f.name.clone(),
679            type_params: f.type_params.clone(),
680            params,
681            return_type: f.return_type.clone(),
682            body,
683            is_nogc: f.is_nogc,
684            cfg_body: None,
685            decorators: f.decorators.clone(),
686            vis: f.vis,
687        }
688    }
689
690    fn lower_block(&mut self, block: &HirBlock) -> MirBody {
691        let stmts = block.stmts.iter().map(|s| self.lower_stmt(s)).collect();
692        let result = block.expr.as_ref().map(|e| Box::new(self.lower_expr(e)));
693        MirBody { stmts, result }
694    }
695
696    fn lower_stmt(&mut self, stmt: &HirStmt) -> MirStmt {
697        match &stmt.kind {
698            HirStmtKind::Let {
699                name,
700                mutable,
701                init,
702                ..
703            } => MirStmt::Let {
704                name: name.clone(),
705                mutable: *mutable,
706                init: self.lower_expr(init),
707                alloc_hint: None,
708            },
709            HirStmtKind::Expr(e) => MirStmt::Expr(self.lower_expr(e)),
710            HirStmtKind::If(if_expr) => self.lower_if_stmt(if_expr),
711            HirStmtKind::While { cond, body } => MirStmt::While {
712                cond: self.lower_expr(cond),
713                body: self.lower_block(body),
714            },
715            HirStmtKind::Return(e) => {
716                MirStmt::Return(e.as_ref().map(|ex| self.lower_expr(ex)))
717            }
718            HirStmtKind::Break => MirStmt::Break,
719            HirStmtKind::Continue => MirStmt::Continue,
720            HirStmtKind::NoGcBlock(block) => MirStmt::NoGcBlock(self.lower_block(block)),
721        }
722    }
723
724    /// Lower a HIR `if` expression to a [`MirStmt::If`].
725    ///
726    /// Nested `else if` chains are recursively lowered into nested
727    /// [`MirStmt::If`] nodes wrapped in a [`MirBody`].
728    pub fn lower_if_stmt(&mut self, if_expr: &HirIfExpr) -> MirStmt {
729        let cond = self.lower_expr(&if_expr.cond);
730        let then_body = self.lower_block(&if_expr.then_block);
731        let else_body = if_expr.else_branch.as_ref().map(|eb| match eb {
732            HirElseBranch::ElseIf(elif) => {
733                // Nested if-else becomes a block containing the if stmt
734                let nested = self.lower_if_stmt(elif);
735                MirBody {
736                    stmts: vec![nested],
737                    result: None,
738                }
739            }
740            HirElseBranch::Else(block) => self.lower_block(block),
741        });
742        MirStmt::If {
743            cond,
744            then_body,
745            else_body,
746        }
747    }
748
749    /// Lower a HIR expression to a [`MirExpr`].
750    ///
751    /// Handles all HIR expression kinds including closures (lambda-lifted),
752    /// match expressions (compiled to [`MirExprKind::Match`] decision trees),
753    /// and if-expressions.
754    pub fn lower_expr(&mut self, expr: &HirExpr) -> MirExpr {
755        let kind = match &expr.kind {
756            HirExprKind::IntLit(v) => MirExprKind::IntLit(*v),
757            HirExprKind::FloatLit(v) => MirExprKind::FloatLit(*v),
758            HirExprKind::BoolLit(b) => MirExprKind::BoolLit(*b),
759            HirExprKind::NaLit => MirExprKind::NaLit,
760            HirExprKind::StringLit(s) => MirExprKind::StringLit(s.clone()),
761            HirExprKind::ByteStringLit(bytes) => MirExprKind::ByteStringLit(bytes.clone()),
762            HirExprKind::ByteCharLit(b) => MirExprKind::ByteCharLit(*b),
763            HirExprKind::RawStringLit(s) => MirExprKind::RawStringLit(s.clone()),
764            HirExprKind::RawByteStringLit(bytes) => MirExprKind::RawByteStringLit(bytes.clone()),
765            HirExprKind::RegexLit { pattern, flags } => MirExprKind::RegexLit { pattern: pattern.clone(), flags: flags.clone() },
766            HirExprKind::TensorLit { rows } => {
767                let mir_rows = rows.iter().map(|row| {
768                    row.iter().map(|e| self.lower_expr(e)).collect()
769                }).collect();
770                MirExprKind::TensorLit { rows: mir_rows }
771            }
772            HirExprKind::Var(name) => MirExprKind::Var(name.clone()),
773            HirExprKind::Binary { op, left, right } => MirExprKind::Binary {
774                op: *op,
775                left: Box::new(self.lower_expr(left)),
776                right: Box::new(self.lower_expr(right)),
777            },
778            HirExprKind::Unary { op, operand } => MirExprKind::Unary {
779                op: *op,
780                operand: Box::new(self.lower_expr(operand)),
781            },
782            HirExprKind::Call { callee, args } => MirExprKind::Call {
783                callee: Box::new(self.lower_expr(callee)),
784                args: args.iter().map(|a| self.lower_expr(a)).collect(),
785            },
786            HirExprKind::Field { object, name } => MirExprKind::Field {
787                object: Box::new(self.lower_expr(object)),
788                name: name.clone(),
789            },
790            HirExprKind::Index { object, index } => MirExprKind::Index {
791                object: Box::new(self.lower_expr(object)),
792                index: Box::new(self.lower_expr(index)),
793            },
794            HirExprKind::MultiIndex { object, indices } => MirExprKind::MultiIndex {
795                object: Box::new(self.lower_expr(object)),
796                indices: indices.iter().map(|i| self.lower_expr(i)).collect(),
797            },
798            HirExprKind::Assign { target, value } => MirExprKind::Assign {
799                target: Box::new(self.lower_expr(target)),
800                value: Box::new(self.lower_expr(value)),
801            },
802            HirExprKind::Block(block) => MirExprKind::Block(self.lower_block(block)),
803            HirExprKind::StructLit { name, fields } => MirExprKind::StructLit {
804                name: name.clone(),
805                fields: fields
806                    .iter()
807                    .map(|(n, e)| (n.clone(), self.lower_expr(e)))
808                    .collect(),
809            },
810            HirExprKind::ArrayLit(elems) => {
811                MirExprKind::ArrayLit(elems.iter().map(|e| self.lower_expr(e)).collect())
812            }
813            HirExprKind::Col(name) => MirExprKind::Col(name.clone()),
814            HirExprKind::Lambda { params, body } => MirExprKind::Lambda {
815                params: params
816                    .iter()
817                    .map(|p| MirParam {
818                        name: p.name.clone(),
819                        ty_name: p.ty_name.clone(),
820                        default: p.default.as_ref().map(|d| self.lower_expr(d)),
821                        is_variadic: p.is_variadic,
822                    })
823                    .collect(),
824                body: Box::new(self.lower_expr(body)),
825            },
826            HirExprKind::Closure {
827                params,
828                body,
829                captures,
830            } => {
831                // Lambda-lift: create a top-level function with extra
832                // leading parameters for the captured values.
833                let lifted_name = self.fresh_lambda_name();
834                let lifted_id = self.fresh_fn_id();
835
836                // Build params: captures first, then the original params
837                let mut lifted_params: Vec<MirParam> = captures
838                    .iter()
839                    .map(|c| MirParam {
840                        name: c.name.clone(),
841                        ty_name: "any".to_string(), // Type erasure at MIR level
842                        default: None,
843                        is_variadic: false,
844                    })
845                    .collect();
846                for p in params {
847                    lifted_params.push(MirParam {
848                        name: p.name.clone(),
849                        ty_name: p.ty_name.clone(),
850                        default: p.default.as_ref().map(|d| self.lower_expr(d)),
851                        is_variadic: p.is_variadic,
852                    });
853                }
854
855                let lifted_body = MirBody {
856                    stmts: vec![],
857                    result: Some(Box::new(self.lower_expr(body))),
858                };
859
860                self.lifted_functions.push(MirFunction {
861                    id: lifted_id,
862                    name: lifted_name.clone(),
863                    type_params: vec![],
864                    params: lifted_params,
865                    return_type: None,
866                    body: lifted_body,
867                    is_nogc: false,
868                    cfg_body: None,
869                    decorators: vec![],
870                    vis: Visibility::Private,
871                });
872
873                // At the call site, emit MakeClosure with the capture
874                // variable references as capture expressions
875                let capture_exprs: Vec<MirExpr> = captures
876                    .iter()
877                    .map(|c| MirExpr {
878                        kind: MirExprKind::Var(c.name.clone()),
879                    })
880                    .collect();
881
882                MirExprKind::MakeClosure {
883                    fn_name: lifted_name,
884                    captures: capture_exprs,
885                }
886            }
887            HirExprKind::Match { scrutinee, arms } => {
888                let mir_scrutinee = Box::new(self.lower_expr(scrutinee));
889                let mir_arms = arms
890                    .iter()
891                    .map(|arm| {
892                        let pattern = self.lower_pattern(&arm.pattern);
893                        let body = MirBody {
894                            stmts: vec![],
895                            result: Some(Box::new(self.lower_expr(&arm.body))),
896                        };
897                        MirMatchArm { pattern, body }
898                    })
899                    .collect();
900                MirExprKind::Match {
901                    scrutinee: mir_scrutinee,
902                    arms: mir_arms,
903                }
904            }
905            HirExprKind::TupleLit(elems) => {
906                MirExprKind::TupleLit(elems.iter().map(|e| self.lower_expr(e)).collect())
907            }
908            HirExprKind::VariantLit {
909                enum_name,
910                variant,
911                fields,
912            } => MirExprKind::VariantLit {
913                enum_name: enum_name.clone(),
914                variant: variant.clone(),
915                fields: fields.iter().map(|f| self.lower_expr(f)).collect(),
916            },
917            HirExprKind::If { cond, then_block, else_branch } => {
918                let mir_cond = Box::new(self.lower_expr(cond));
919                let mir_then = self.lower_block(then_block);
920                let mir_else = else_branch.as_ref().map(|eb| match eb {
921                    HirElseBranch::ElseIf(elif) => {
922                        // Nested else-if: lower as MirStmt::If inside a MirBody
923                        let nested = self.lower_if_stmt(elif);
924                        MirBody {
925                            stmts: vec![nested],
926                            result: None,
927                        }
928                    }
929                    HirElseBranch::Else(block) => self.lower_block(block),
930                });
931                MirExprKind::If {
932                    cond: mir_cond,
933                    then_body: mir_then,
934                    else_body: mir_else,
935                }
936            }
937            HirExprKind::Void => MirExprKind::Void,
938        };
939        MirExpr { kind }
940    }
941
942    fn lower_pattern(&self, pat: &HirPattern) -> MirPattern {
943        match &pat.kind {
944            HirPatternKind::Wildcard => MirPattern::Wildcard,
945            HirPatternKind::Binding(name) => MirPattern::Binding(name.clone()),
946            HirPatternKind::LitInt(v) => MirPattern::LitInt(*v),
947            HirPatternKind::LitFloat(v) => MirPattern::LitFloat(*v),
948            HirPatternKind::LitBool(b) => MirPattern::LitBool(*b),
949            HirPatternKind::LitString(s) => MirPattern::LitString(s.clone()),
950            HirPatternKind::Tuple(pats) => {
951                MirPattern::Tuple(pats.iter().map(|p| self.lower_pattern(p)).collect())
952            }
953            HirPatternKind::Struct { name, fields } => MirPattern::Struct {
954                name: name.clone(),
955                fields: fields
956                    .iter()
957                    .map(|f| (f.name.clone(), self.lower_pattern(&f.pattern)))
958                    .collect(),
959            },
960            HirPatternKind::Variant {
961                enum_name,
962                variant,
963                fields,
964            } => MirPattern::Variant {
965                enum_name: enum_name.clone(),
966                variant: variant.clone(),
967                fields: fields.iter().map(|f| self.lower_pattern(f)).collect(),
968            },
969        }
970    }
971}
972
973impl Default for HirToMir {
974    fn default() -> Self {
975        Self::new()
976    }
977}
978
979// ---------------------------------------------------------------------------
980// Tests
981// ---------------------------------------------------------------------------
982
983#[cfg(test)]
984mod tests {
985    use super::*;
986    use cjc_hir::*;
987
988    fn hir_id(n: u32) -> HirId {
989        HirId(n)
990    }
991
992    fn hir_int(v: i64) -> HirExpr {
993        HirExpr {
994            kind: HirExprKind::IntLit(v),
995            hir_id: hir_id(0),
996        }
997    }
998
999    fn hir_var(name: &str) -> HirExpr {
1000        HirExpr {
1001            kind: HirExprKind::Var(name.to_string()),
1002            hir_id: hir_id(0),
1003        }
1004    }
1005
1006    #[test]
1007    fn test_lower_hir_literal() {
1008        let mut lowering = HirToMir::new();
1009        let hir = hir_int(42);
1010        let mir = lowering.lower_expr(&hir);
1011        assert!(matches!(mir.kind, MirExprKind::IntLit(42)));
1012    }
1013
1014    #[test]
1015    fn test_lower_hir_binary() {
1016        let mut lowering = HirToMir::new();
1017        let hir = HirExpr {
1018            kind: HirExprKind::Binary {
1019                op: BinOp::Add,
1020                left: Box::new(hir_int(1)),
1021                right: Box::new(hir_int(2)),
1022            },
1023            hir_id: hir_id(0),
1024        };
1025        let mir = lowering.lower_expr(&hir);
1026        match &mir.kind {
1027            MirExprKind::Binary { op, .. } => assert_eq!(*op, BinOp::Add),
1028            _ => panic!("expected Binary"),
1029        }
1030    }
1031
1032    #[test]
1033    fn test_lower_hir_fn() {
1034        let mut lowering = HirToMir::new();
1035        let hir_fn = HirFn {
1036            name: "add".to_string(),
1037            type_params: vec![],
1038            params: vec![
1039                HirParam {
1040                    name: "a".to_string(),
1041                    ty_name: "i64".to_string(),
1042                    default: None,
1043                    is_variadic: false,
1044                    hir_id: hir_id(1),
1045                },
1046                HirParam {
1047                    name: "b".to_string(),
1048                    ty_name: "i64".to_string(),
1049                    default: None,
1050                    is_variadic: false,
1051                    hir_id: hir_id(2),
1052                },
1053            ],
1054            return_type: Some("i64".to_string()),
1055            body: HirBlock {
1056                stmts: vec![],
1057                expr: Some(Box::new(HirExpr {
1058                    kind: HirExprKind::Binary {
1059                        op: BinOp::Add,
1060                        left: Box::new(hir_var("a")),
1061                        right: Box::new(hir_var("b")),
1062                    },
1063                    hir_id: hir_id(3),
1064                })),
1065                hir_id: hir_id(4),
1066            },
1067            is_nogc: false,
1068            hir_id: hir_id(5),
1069            decorators: vec![],
1070            vis: cjc_ast::Visibility::Private,
1071        };
1072        let mir_fn = lowering.lower_fn(&hir_fn);
1073        assert_eq!(mir_fn.name, "add");
1074        assert_eq!(mir_fn.params.len(), 2);
1075        assert!(mir_fn.body.result.is_some());
1076    }
1077
1078    #[test]
1079    fn test_lower_hir_program_entry() {
1080        let mut lowering = HirToMir::new();
1081        let hir = HirProgram {
1082            items: vec![
1083                HirItem::Let(HirLetDecl {
1084                    name: "x".to_string(),
1085                    mutable: false,
1086                    ty_name: None,
1087                    init: hir_int(42),
1088                    hir_id: hir_id(0),
1089                }),
1090                HirItem::Fn(HirFn {
1091                    name: "f".to_string(),
1092                    type_params: vec![],
1093                    params: vec![],
1094                    return_type: None,
1095                    body: HirBlock {
1096                        stmts: vec![],
1097                        expr: Some(Box::new(hir_var("x"))),
1098                        hir_id: hir_id(1),
1099                    },
1100                    is_nogc: false,
1101                    hir_id: hir_id(2),
1102                    decorators: vec![],
1103                    vis: cjc_ast::Visibility::Private,
1104                }),
1105            ],
1106        };
1107        let mir = lowering.lower_program(&hir);
1108        // Should have: function 'f' + synthetic __main
1109        assert_eq!(mir.functions.len(), 2);
1110        let main = mir.functions.iter().find(|f| f.name == "__main").unwrap();
1111        assert_eq!(main.body.stmts.len(), 1); // the let x = 42
1112        assert_eq!(mir.entry, main.id);
1113    }
1114
1115    #[test]
1116    fn test_lower_hir_if_stmt() {
1117        let mut lowering = HirToMir::new();
1118        let hir_if = HirIfExpr {
1119            cond: Box::new(HirExpr {
1120                kind: HirExprKind::BoolLit(true),
1121                hir_id: hir_id(0),
1122            }),
1123            then_block: HirBlock {
1124                stmts: vec![],
1125                expr: Some(Box::new(hir_int(1))),
1126                hir_id: hir_id(1),
1127            },
1128            else_branch: Some(HirElseBranch::Else(HirBlock {
1129                stmts: vec![],
1130                expr: Some(Box::new(hir_int(2))),
1131                hir_id: hir_id(2),
1132            })),
1133            hir_id: hir_id(3),
1134        };
1135        let mir_stmt = lowering.lower_if_stmt(&hir_if);
1136        match &mir_stmt {
1137            MirStmt::If {
1138                then_body,
1139                else_body,
1140                ..
1141            } => {
1142                assert!(then_body.result.is_some());
1143                assert!(else_body.is_some());
1144            }
1145            _ => panic!("expected If"),
1146        }
1147    }
1148
1149    #[test]
1150    fn test_lower_struct_def() {
1151        let mut lowering = HirToMir::new();
1152        let hir = HirProgram {
1153            items: vec![HirItem::Struct(HirStructDef {
1154                name: "Point".to_string(),
1155                fields: vec![
1156                    ("x".to_string(), "f64".to_string()),
1157                    ("y".to_string(), "f64".to_string()),
1158                ],
1159                hir_id: hir_id(0),
1160                vis: cjc_ast::Visibility::Private,
1161            })],
1162        };
1163        let mir = lowering.lower_program(&hir);
1164        assert_eq!(mir.struct_defs.len(), 1);
1165        assert_eq!(mir.struct_defs[0].name, "Point");
1166        assert_eq!(mir.struct_defs[0].fields.len(), 2);
1167    }
1168}