joule_mir/
build.rs

1//! HIR to MIR lowering (builder)
2//!
3//! This module converts High-level IR (HIR) to Mid-level IR (MIR).
4//! It handles:
5//! - Breaking down expressions into basic blocks
6//! - Converting control flow to explicit terminators
7//! - Creating temporaries for intermediate values
8//! - Converting patterns to explicit code
9
10use crate::{
11    AggregateKind, BasicBlock, BasicBlockId, Constant, FieldIdx, FunctionId, FunctionMIR, IntTy,
12    Local, LocalDecl, Operand, Place, PlaceElem, Rvalue, Statement, SwitchTargets, Terminator, Ty,
13    UintTy,
14};
15use joule_common::{Span, Symbol};
16use joule_hir::{self as hir, HirId};
17use joule_hir::{BinOp, Literal};
18use std::collections::HashMap;
19
20/// Loop context for break/continue targeting
21struct LoopContext {
22    /// Block to jump to on `continue`
23    header: BasicBlockId,
24    /// Block to jump to on `break`
25    exit: BasicBlockId,
26    /// Where `break VALUE` should store its result
27    break_destination: Option<Place>,
28}
29
30/// Builder for constructing MIR from HIR
31pub struct MirBuilder {
32    /// The function being built
33    func: FunctionMIR,
34    /// Current basic block being built
35    current_block: Option<BasicBlockId>,
36    /// Map from HIR IDs to MIR locals
37    hir_to_local: HashMap<HirId, Local>,
38    /// Counter for generating unique local IDs
39    _local_counter: u32,
40    /// Blocks that need terminators filled in
41    _incomplete_blocks: Vec<BasicBlockId>,
42    /// Stack of loop contexts for break/continue
43    loop_stack: Vec<LoopContext>,
44    /// Map from (enum_name, variant_name) to discriminant index
45    variant_index_map: HashMap<(String, String), u128>,
46    /// Closure body functions built during parent function construction
47    pending_closures: Vec<FunctionMIR>,
48}
49
50impl MirBuilder {
51    /// Create a new MIR builder for a function
52    pub fn new(id: FunctionId, name: Symbol, return_ty: Ty, span: Span) -> Self {
53        Self {
54            func: FunctionMIR::new(id, name, return_ty, span),
55            current_block: None,
56            hir_to_local: HashMap::new(),
57            _local_counter: 1, // 0 is reserved for return place
58            _incomplete_blocks: Vec::new(),
59            loop_stack: Vec::new(),
60            variant_index_map: HashMap::new(),
61            pending_closures: Vec::new(),
62        }
63    }
64
65    /// Create a new MIR builder with function attributes
66    pub fn with_attributes(
67        id: FunctionId,
68        name: Symbol,
69        return_ty: Ty,
70        attributes: crate::FunctionAttributes,
71        span: Span,
72    ) -> Self {
73        Self {
74            func: FunctionMIR::with_attributes(id, name, return_ty, attributes, span),
75            current_block: None,
76            hir_to_local: HashMap::new(),
77            _local_counter: 1,
78            _incomplete_blocks: Vec::new(),
79            loop_stack: Vec::new(),
80            variant_index_map: HashMap::new(),
81            pending_closures: Vec::new(),
82        }
83    }
84
85    /// Build MIR for a HIR function
86    pub fn build_function(hir_func: &hir::Function) -> (FunctionMIR, Vec<FunctionMIR>) {
87        Self::build_function_with_enums(hir_func, &HashMap::new())
88    }
89
90    /// Build MIR for a HIR function, with enum variant index map for match lowering.
91    /// Returns (main_function, closure_functions).
92    pub fn build_function_with_enums(
93        hir_func: &hir::Function,
94        variant_index_map: &HashMap<(String, String), u128>,
95    ) -> (FunctionMIR, Vec<FunctionMIR>) {
96        let id = FunctionId::from_hir(hir_func.id);
97        let return_ty = Ty::from_hir(&hir_func.return_ty);
98
99        // Propagate function attributes from HIR to MIR
100        let mut builder = Self::with_attributes(
101            id,
102            hir_func.name,
103            return_ty.clone(),
104            hir_func.attributes.clone(),
105            hir_func.span,
106        );
107        builder.variant_index_map = variant_index_map.clone();
108
109        // Update return place type
110        builder.func.locals[0].ty = return_ty;
111
112        // Create parameters
113        for param in &hir_func.params {
114            let local = builder.new_local(Some(param.pattern.ty().clone()), param.span, false);
115            builder.func.params.push(local);
116
117            // Map parameter pattern to local
118            if let hir::Pattern::Ident { id, .. } = &param.pattern {
119                builder.hir_to_local.insert(*id, local);
120            }
121        }
122
123        // Create entry block
124        let entry = builder.new_block();
125        builder.current_block = Some(entry);
126
127        // Build function body
128        builder.build_block_expr(&hir_func.body, Place::return_place());
129
130        // Ensure all blocks have terminators
131        builder.ensure_return();
132
133        let closures = builder.pending_closures;
134        (builder.func, closures)
135    }
136
137    /// Create a new local variable
138    fn new_local(&mut self, hir_ty: Option<hir::Ty>, span: Span, mutable: bool) -> Local {
139        let ty = hir_ty.map(|t| Ty::from_hir(&t)).unwrap_or(Ty::Unit);
140        let decl = LocalDecl::new(None, ty, mutable, span);
141        self.func.add_local(decl)
142    }
143
144    /// Create a new temporary
145    fn new_temp(&mut self, ty: Ty, span: Span) -> Local {
146        let decl = LocalDecl::new(None, ty, true, span);
147        self.func.add_local(decl)
148    }
149
150    /// Create a new basic block
151    fn new_block(&mut self) -> BasicBlockId {
152        let block = BasicBlock::new(Terminator::Unreachable {
153            span: Span::dummy(),
154        });
155        self.func.add_block(block)
156    }
157
158    /// Get the current basic block
159    fn current_block_mut(&mut self) -> &mut BasicBlock {
160        let id = self.current_block.expect("No current block");
161        self.func.block_mut(id)
162    }
163
164    /// Add a statement to the current block
165    fn push_statement(&mut self, stmt: Statement) {
166        self.current_block_mut().statements.push(stmt);
167    }
168
169    /// Set the terminator for the current block
170    fn set_terminator(&mut self, terminator: Terminator) {
171        let id = self.current_block.expect("No current block");
172        self.func.block_mut(id).terminator = terminator;
173        self.current_block = None;
174    }
175
176    /// Ensure the function has a proper return
177    fn ensure_return(&mut self) {
178        if let Some(block_id) = self.current_block {
179            // If there's an active block, add a return
180            let terminator = Terminator::Return {
181                span: self.func.span,
182            };
183            self.func.block_mut(block_id).terminator = terminator;
184        }
185    }
186
187    /// Build a block expression, storing result in destination
188    fn build_block_expr(&mut self, block: &hir::Block, destination: Place) {
189        // Storage live for all let bindings
190        for stmt in &block.statements {
191            self.build_statement(stmt);
192            // If statement created a terminator (e.g., return), stop processing
193            if self.current_block.is_none() {
194                return;
195            }
196        }
197
198        // Build the final expression (if any)
199        if let Some(expr) = &block.expr {
200            self.build_expr_into(expr, destination);
201        } else {
202            // No expression, assign unit
203            self.push_statement(Statement::Assign {
204                place: destination,
205                rvalue: Rvalue::Aggregate {
206                    kind: AggregateKind::Tuple,
207                    operands: vec![],
208                },
209                span: block.span,
210            });
211        }
212    }
213
214    /// Build a statement
215    fn build_statement(&mut self, stmt: &hir::Statement) {
216        match stmt {
217            hir::Statement::Let {
218                pattern,
219                ty,
220                init,
221                span,
222                ..
223            } => {
224                match pattern {
225                    hir::Pattern::Tuple { patterns, .. } => {
226                        // Create temp for the whole tuple
227                        let tuple_local = self.new_local(Some(ty.clone()), *span, false);
228                        self.push_statement(Statement::StorageLive {
229                            local: tuple_local,
230                            span: *span,
231                        });
232
233                        // Build init into tuple temp
234                        if let Some(init_expr) = init {
235                            self.build_expr_into(init_expr, Place::from_local(tuple_local));
236                        }
237
238                        // Extract each field into its own local
239                        for (i, sub_pat) in patterns.iter().enumerate() {
240                            match sub_pat {
241                                hir::Pattern::Ident {
242                                    id: pat_id,
243                                    mutable,
244                                    ty: field_ty,
245                                    ..
246                                } => {
247                                    let field_local =
248                                        self.new_local(Some(field_ty.clone()), *span, *mutable);
249                                    self.hir_to_local.insert(*pat_id, field_local);
250                                    self.push_statement(Statement::StorageLive {
251                                        local: field_local,
252                                        span: *span,
253                                    });
254                                    self.push_statement(Statement::Assign {
255                                        place: Place::from_local(field_local),
256                                        rvalue: Rvalue::Use(Operand::Copy(
257                                            Place::from_local(tuple_local)
258                                                .field(FieldIdx::new(i as u32)),
259                                        )),
260                                        span: *span,
261                                    });
262                                }
263                                hir::Pattern::Wildcard { .. } => {
264                                    // No binding needed for wildcard
265                                }
266                                _ => {
267                                    // Nested patterns: create a local for the sub-pattern
268                                    let sub_local = self.new_local(Some(ty.clone()), *span, false);
269                                    self.push_statement(Statement::StorageLive {
270                                        local: sub_local,
271                                        span: *span,
272                                    });
273                                    self.push_statement(Statement::Assign {
274                                        place: Place::from_local(sub_local),
275                                        rvalue: Rvalue::Use(Operand::Copy(
276                                            Place::from_local(tuple_local)
277                                                .field(FieldIdx::new(i as u32)),
278                                        )),
279                                        span: *span,
280                                    });
281                                }
282                            }
283                        }
284                    }
285                    _ => {
286                        let mutable = matches!(pattern, hir::Pattern::Ident { mutable: true, .. });
287                        let local = self.new_local(Some(ty.clone()), *span, mutable);
288
289                        // Map HIR pattern to local, and set the name for codegen lookups
290                        if let hir::Pattern::Ident {
291                            id: pattern_id,
292                            name,
293                            ..
294                        } = pattern
295                        {
296                            self.hir_to_local.insert(*pattern_id, local);
297                            self.func.locals[local.0 as usize].name = Some(*name);
298                        }
299
300                        // Storage live
301                        self.push_statement(Statement::StorageLive { local, span: *span });
302
303                        // Initialize if there's an initializer
304                        if let Some(init_expr) = init {
305                            let place = Place::from_local(local);
306                            self.build_expr_into(init_expr, place);
307                        }
308                    }
309                }
310            }
311            hir::Statement::Expr(expr) => {
312                // Expression statement - evaluate but discard result
313                let temp = self.new_temp(Ty::from_hir(&expr.ty), expr.span);
314                self.build_expr_into(expr, Place::from_local(temp));
315            }
316        }
317    }
318
319    /// Build an expression, storing result in destination
320    fn build_expr_into(&mut self, expr: &hir::Expr, destination: Place) {
321        match &expr.kind {
322            hir::ExprKind::Literal(lit) => {
323                let constant = Constant::new(lit.clone(), Ty::from_hir(&expr.ty), expr.span);
324                self.push_statement(Statement::Assign {
325                    place: destination,
326                    rvalue: Rvalue::Use(Operand::Constant(constant)),
327                    span: expr.span,
328                });
329            }
330
331            hir::ExprKind::Var { def_id, name, .. } => {
332                if let Some(&local) = self.hir_to_local.get(def_id) {
333                    let place = Place::from_local(local);
334                    let ty = Ty::from_hir(&expr.ty);
335                    let operand = if ty.is_copy() {
336                        Operand::Copy(place)
337                    } else {
338                        Operand::Move(place)
339                    };
340                    self.push_statement(Statement::Assign {
341                        place: destination,
342                        rvalue: Rvalue::Use(operand),
343                        span: expr.span,
344                    });
345                } else {
346                    // Check if this is an enum variant (e.g., TokenKind::Eof)
347                    // Enum variants start with uppercase; regular variables don't
348                    let name_str = name.as_str();
349                    let is_enum_variant = name_str
350                        .chars()
351                        .next()
352                        .map(|c| c.is_ascii_uppercase())
353                        .unwrap_or(false);
354                    if is_enum_variant {
355                        self.push_statement(Statement::Assign {
356                            place: destination,
357                            rvalue: Rvalue::EnumVariant {
358                                variant: *name,
359                                fields: vec![],
360                            },
361                            span: expr.span,
362                        });
363                    }
364                    // else: variable not found in hir_to_local — assignment silently skipped
365                }
366            }
367
368            hir::ExprKind::Binary { op, left, right } => {
369                let left_temp = self.new_temp(Ty::from_hir(&left.ty), left.span);
370                let right_temp = self.new_temp(Ty::from_hir(&right.ty), right.span);
371
372                self.build_expr_into(left, Place::from_local(left_temp));
373                self.build_expr_into(right, Place::from_local(right_temp));
374
375                self.push_statement(Statement::Assign {
376                    place: destination,
377                    rvalue: Rvalue::BinaryOp {
378                        op: *op,
379                        left: Operand::Copy(Place::from_local(left_temp)),
380                        right: Operand::Copy(Place::from_local(right_temp)),
381                    },
382                    span: expr.span,
383                });
384            }
385
386            hir::ExprKind::Unary { op, expr: inner } => {
387                let temp = self.new_temp(Ty::from_hir(&inner.ty), inner.span);
388                self.build_expr_into(inner, Place::from_local(temp));
389
390                self.push_statement(Statement::Assign {
391                    place: destination,
392                    rvalue: Rvalue::UnaryOp {
393                        op: *op,
394                        operand: Operand::Copy(Place::from_local(temp)),
395                    },
396                    span: expr.span,
397                });
398            }
399
400            hir::ExprKind::Tuple { elements } => {
401                let mut operands = Vec::new();
402                for elem in elements {
403                    let temp = self.new_temp(Ty::from_hir(&elem.ty), elem.span);
404                    self.build_expr_into(elem, Place::from_local(temp));
405                    operands.push(Operand::Copy(Place::from_local(temp)));
406                }
407
408                self.push_statement(Statement::Assign {
409                    place: destination,
410                    rvalue: Rvalue::Aggregate {
411                        kind: AggregateKind::Tuple,
412                        operands,
413                    },
414                    span: expr.span,
415                });
416            }
417
418            hir::ExprKind::Array { elements } => {
419                let mut operands = Vec::new();
420                let elem_ty = if let hir::Ty::Array { element, .. } = &expr.ty {
421                    Ty::from_hir(element)
422                } else {
423                    Ty::Unit
424                };
425
426                for elem in elements {
427                    let temp = self.new_temp(Ty::from_hir(&elem.ty), elem.span);
428                    self.build_expr_into(elem, Place::from_local(temp));
429                    operands.push(Operand::Copy(Place::from_local(temp)));
430                }
431
432                self.push_statement(Statement::Assign {
433                    place: destination,
434                    rvalue: Rvalue::Aggregate {
435                        kind: AggregateKind::Array(elem_ty),
436                        operands,
437                    },
438                    span: expr.span,
439                });
440            }
441
442            hir::ExprKind::Struct {
443                def_id,
444                fields,
445                variant_name,
446            } => {
447                let mut operands = Vec::new();
448                // Sort fields by index
449                let mut sorted_fields = fields.clone();
450                sorted_fields.sort_by_key(|f| f.field_idx);
451
452                for field in sorted_fields {
453                    let temp = self.new_temp(Ty::from_hir(&field.value.ty), field.value.span);
454                    self.build_expr_into(&field.value, Place::from_local(temp));
455                    operands.push(Operand::Copy(Place::from_local(temp)));
456                }
457
458                self.push_statement(Statement::Assign {
459                    place: destination,
460                    rvalue: Rvalue::Aggregate {
461                        kind: AggregateKind::Struct(*def_id, *variant_name),
462                        operands,
463                    },
464                    span: expr.span,
465                });
466            }
467
468            hir::ExprKind::Block(block) => {
469                self.build_block_expr(block, destination);
470            }
471
472            hir::ExprKind::If {
473                condition,
474                then_block,
475                else_block,
476            } => {
477                self.build_if(
478                    condition,
479                    then_block,
480                    else_block.as_deref(),
481                    destination,
482                    expr.span,
483                );
484            }
485
486            hir::ExprKind::Return { value } => {
487                if let Some(val) = value {
488                    self.build_expr_into(val, Place::return_place());
489                }
490                if self.current_block.is_some() {
491                    self.set_terminator(Terminator::Return { span: expr.span });
492                }
493            }
494
495            hir::ExprKind::Assign { target, value } => {
496                // Build target place
497                let target_place = self.build_place(target);
498                self.build_expr_into(value, target_place);
499
500                // Assign unit to destination
501                self.push_statement(Statement::Assign {
502                    place: destination,
503                    rvalue: Rvalue::Aggregate {
504                        kind: AggregateKind::Tuple,
505                        operands: vec![],
506                    },
507                    span: expr.span,
508                });
509            }
510
511            hir::ExprKind::Ref {
512                mutable,
513                expr: inner,
514            } => {
515                let place = self.build_place(inner);
516                self.push_statement(Statement::Assign {
517                    place: destination,
518                    rvalue: Rvalue::Ref {
519                        mutable: *mutable,
520                        place,
521                    },
522                    span: expr.span,
523                });
524            }
525
526            hir::ExprKind::Deref { expr: inner } => {
527                let temp = self.new_temp(Ty::from_hir(&inner.ty), inner.span);
528                self.build_expr_into(inner, Place::from_local(temp));
529
530                let deref_place = Place::from_local(temp).deref();
531                let ty = Ty::from_hir(&expr.ty);
532                let operand = if ty.is_copy() {
533                    Operand::Copy(deref_place)
534                } else {
535                    Operand::Move(deref_place)
536                };
537
538                self.push_statement(Statement::Assign {
539                    place: destination,
540                    rvalue: Rvalue::Use(operand),
541                    span: expr.span,
542                });
543            }
544
545            hir::ExprKind::Field {
546                expr: inner,
547                field_idx,
548                ..
549            } => {
550                let temp = self.new_temp(Ty::from_hir(&inner.ty), inner.span);
551                self.build_expr_into(inner, Place::from_local(temp));
552
553                let field_place = Place::from_local(temp).field(FieldIdx::new(*field_idx as u32));
554                let ty = Ty::from_hir(&expr.ty);
555                let operand = if ty.is_copy() {
556                    Operand::Copy(field_place)
557                } else {
558                    Operand::Move(field_place)
559                };
560
561                self.push_statement(Statement::Assign {
562                    place: destination,
563                    rvalue: Rvalue::Use(operand),
564                    span: expr.span,
565                });
566            }
567
568            hir::ExprKind::Cast {
569                expr: inner,
570                target_ty,
571            } => {
572                let temp = self.new_temp(Ty::from_hir(&inner.ty), inner.span);
573                self.build_expr_into(inner, Place::from_local(temp));
574
575                let cast_kind = self.determine_cast_kind(&inner.ty, target_ty);
576
577                self.push_statement(Statement::Assign {
578                    place: destination,
579                    rvalue: Rvalue::Cast {
580                        operand: Operand::Copy(Place::from_local(temp)),
581                        target_ty: Ty::from_hir(target_ty),
582                        kind: cast_kind,
583                    },
584                    span: expr.span,
585                });
586            }
587
588            hir::ExprKind::Call { func, args } => {
589                // Extract function name if it's a simple variable reference
590                let func_name = if let hir::ExprKind::Var { name, .. } = &func.kind {
591                    Some(*name)
592                } else {
593                    None
594                };
595
596                // Build function operand
597                let func_temp = self.new_temp(Ty::from_hir(&func.ty), func.span);
598                self.build_expr_into(func, Place::from_local(func_temp));
599
600                // Build argument operands
601                let mut arg_operands = Vec::new();
602                for arg in args {
603                    // Special case: keep string literals as constants
604                    if let hir::ExprKind::Literal(lit @ hir::Literal::String(_)) = &arg.kind {
605                        let constant = Constant::new(lit.clone(), Ty::from_hir(&arg.ty), arg.span);
606                        arg_operands.push(Operand::Constant(constant));
607                    } else {
608                        let arg_temp = self.new_temp(Ty::from_hir(&arg.ty), arg.span);
609                        self.build_expr_into(arg, Place::from_local(arg_temp));
610                        arg_operands.push(Operand::Copy(Place::from_local(arg_temp)));
611                    }
612                }
613
614                // Create a block to continue execution after the call
615                let next_block = self.new_block();
616
617                // Emit call terminator
618                self.set_terminator(Terminator::Call {
619                    func: Operand::Copy(Place::from_local(func_temp)),
620                    func_name,
621                    args: arg_operands,
622                    destination: destination.clone(),
623                    target: next_block,
624                    span: expr.span,
625                    is_virtual: false,
626                });
627
628                // Continue in next block
629                self.current_block = Some(next_block);
630            }
631
632            // === Concurrency Expressions ===
633            hir::ExprKind::Spawn { expr: spawn_expr } => {
634                // Build the expression to spawn
635                let spawn_temp = self.new_temp(Ty::from_hir(&spawn_expr.ty), spawn_expr.span);
636                self.build_expr_into(spawn_expr, Place::from_local(spawn_temp));
637
638                // Create block to continue in after spawn
639                let next_block = self.new_block();
640
641                // Emit spawn terminator
642                self.set_terminator(Terminator::Spawn {
643                    func: Operand::Copy(Place::from_local(spawn_temp)),
644                    args: vec![],
645                    destination: destination.clone(),
646                    target: next_block,
647                    span: expr.span,
648                });
649
650                self.current_block = Some(next_block);
651            }
652
653            hir::ExprKind::TaskGroup { body } => {
654                // Create blocks for task group
655                let group_temp = self.new_temp(Ty::TaskGroup, expr.span);
656                let body_block = self.new_block();
657                let join_block = self.new_block();
658
659                // Enter task group
660                self.set_terminator(Terminator::TaskGroupEnter {
661                    destination: Place::from_local(group_temp),
662                    body: body_block,
663                    join_block,
664                    span: expr.span,
665                });
666
667                // Build task group body
668                self.current_block = Some(body_block);
669                let body_result = self.new_temp(Ty::from_hir(&body.ty), body.span);
670                self.build_block_expr(body, Place::from_local(body_result));
671
672                // Exit task group (if we have a current block - may have returned)
673                if self.current_block.is_some() {
674                    let next_block = self.new_block();
675                    self.set_terminator(Terminator::TaskGroupExit {
676                        group: Operand::Copy(Place::from_local(group_temp)),
677                        target: next_block,
678                        span: expr.span,
679                    });
680                    self.current_block = Some(next_block);
681
682                    // Copy result to destination
683                    self.push_statement(Statement::Assign {
684                        place: destination,
685                        rvalue: Rvalue::Use(Operand::Copy(Place::from_local(body_result))),
686                        span: expr.span,
687                    });
688                }
689
690                // Continue in join block for structured completion
691                self.current_block = Some(join_block);
692            }
693
694            hir::ExprKind::Channel {
695                element_ty,
696                capacity,
697            } => {
698                // Evaluate capacity expression
699                let cap_temp = self.new_temp(Ty::Uint(UintTy::U64), capacity.span);
700                self.build_expr_into(capacity, Place::from_local(cap_temp));
701
702                // For now, use a constant capacity (we'll need proper const evaluation later)
703                // Extract capacity value if it's a literal
704                let cap_value = 16u64; // Default capacity
705
706                self.push_statement(Statement::Assign {
707                    place: destination,
708                    rvalue: Rvalue::ChannelCreate {
709                        element_ty: Ty::from_hir(element_ty),
710                        capacity: cap_value,
711                    },
712                    span: expr.span,
713                });
714            }
715
716            hir::ExprKind::Select { arms } => {
717                self.build_select(arms, false, destination, expr.span);
718            }
719
720            hir::ExprKind::SelectBiased { arms } => {
721                self.build_select(arms, true, destination, expr.span);
722            }
723
724            hir::ExprKind::Cancelled => {
725                self.push_statement(Statement::Assign {
726                    place: destination,
727                    rvalue: Rvalue::IsCancelled,
728                    span: expr.span,
729                });
730            }
731
732            // Advanced concurrency features that require runtime support not yet
733            // available in the MIR backend. Each of these needs:
734            //
735            // - SpawnWithDeadline: deadline-aware task scheduler in the runtime,
736            //   cancellation token propagation, and timeout MIR instructions.
737            // - TaskGroupAll: structured concurrency with join semantics,
738            //   requiring TaskGroupEnter/TaskGroupExit with barrier support.
739            // - Gpu: heterogeneous compute dispatch, requiring MLIR or SPIR-V
740            //   codegen backend and device memory management.
741            // - Distributed: remote execution and serialization of closures,
742            //   requiring network transport and distributed runtime.
743            // - SpawnRemote: remote node task dispatch, requiring node
744            //   discovery and remote procedure call infrastructure.
745            //
746            // These abort at MIR level so the compiler produces a clear error
747            // rather than silently generating incorrect code. The destination
748            // is assigned unit to satisfy the type system before the abort.
749            // Energy budget block: the budget is enforced at compile time
750            // by the energy estimator. At runtime, just execute the body.
751            hir::ExprKind::EnergyBudgetBlock { body, .. } => {
752                self.build_block_expr(body, destination);
753            }
754
755            // Thermal adaptation: dispatch based on runtime thermal state.
756            // Currently compiles the first arm as the default path. When the
757            // runtime thermal API is available, this will generate a thermal
758            // state query and branch to the appropriate arm.
759            hir::ExprKind::ThermalAdapt { arms } => {
760                if let Some(first_arm) = arms.first() {
761                    self.build_expr_into(&first_arm.body, destination);
762                } else {
763                    self.push_statement(Statement::Assign {
764                        place: destination,
765                        rvalue: Rvalue::Aggregate {
766                            kind: AggregateKind::Tuple,
767                            operands: vec![],
768                        },
769                        span: expr.span,
770                    });
771                }
772            }
773
774            hir::ExprKind::SpawnWithDeadline { .. }
775            | hir::ExprKind::TaskGroupAll { .. }
776            | hir::ExprKind::Gpu { .. }
777            | hir::ExprKind::Distributed { .. }
778            | hir::ExprKind::SpawnRemote { .. } => {
779                // Assign unit to destination for type-correctness in the
780                // abort block. This value is never observed at runtime.
781                self.push_statement(Statement::Assign {
782                    place: destination,
783                    rvalue: Rvalue::Aggregate {
784                        kind: AggregateKind::Tuple,
785                        operands: vec![],
786                    },
787                    span: expr.span,
788                });
789
790                // Terminate the current block with Abort. This makes the
791                // unreachable nature of this code path explicit in the MIR,
792                // enabling downstream passes (dead code elimination, energy
793                // analysis) to correctly handle it.
794                let feature_name = match &expr.kind {
795                    hir::ExprKind::SpawnWithDeadline { .. } => "spawn_with_deadline",
796                    hir::ExprKind::TaskGroupAll { .. } => "task_group_all",
797                    hir::ExprKind::Gpu { .. } => "gpu",
798                    hir::ExprKind::Distributed { .. } => "distributed",
799                    hir::ExprKind::SpawnRemote { .. } => "spawn_remote",
800                    _ => unreachable!(),
801                };
802                let _ = feature_name; // Used in diagnostic context
803                self.set_terminator(Terminator::Abort { span: expr.span });
804
805                // Create a new unreachable block for any subsequent code.
806                // This prevents the builder from trying to add statements
807                // to the terminated block.
808                let unreachable_block = self.new_block();
809                self.current_block = Some(unreachable_block);
810            }
811
812            // === Algebraic Effects ===
813            // Perform: `perform IO::read()` — translate to function call
814            hir::ExprKind::Perform { effect_name, operation, args } => {
815                // Effects are compiled as calls to the effect handler function.
816                // At MIR level, `perform` becomes a regular function call to
817                // `__effect_{effect}_{op}` which the runtime resolves.
818                let mut arg_ops = Vec::new();
819                for arg in args {
820                    let temp = self.new_temp(Ty::from_hir(&arg.ty), arg.span);
821                    self.build_expr_into(arg, Place::from_local(temp));
822                    arg_ops.push(Operand::Move(Place::from_local(temp)));
823                }
824                let func_name = format!("__effect_{}_{}", effect_name.as_str(), operation.as_str());
825                let func_sym = Symbol::intern(&func_name);
826                let func_temp = self.new_temp(
827                    Ty::FnPtr {
828                        params: arg_ops.iter().map(|_| Ty::Unit).collect(),
829                        return_ty: Box::new(Ty::from_hir(&expr.ty)),
830                    },
831                    expr.span,
832                );
833                let next_block = self.new_block();
834                self.set_terminator(Terminator::Call {
835                    func: Operand::Copy(Place::from_local(func_temp)),
836                    func_name: Some(func_sym),
837                    args: arg_ops,
838                    destination: destination.clone(),
839                    target: next_block,
840                    span: expr.span,
841                    is_virtual: false,
842                });
843                self.current_block = Some(next_block);
844            }
845
846            // Handle: `handle { body } with { IO => handler }` — wraps body with handler
847            // The body is compiled normally. Effect handlers are registered as
848            // function pointers that `perform` calls resolve through.
849            hir::ExprKind::Handle { body, .. } => {
850                self.build_block_expr(body, destination);
851            }
852
853            // Parallel for: `parallel for x in iter { body }` — compiles like regular for
854            // but with a hint to the C codegen to emit parallelism.
855            // At MIR level, this is structurally identical to a regular for loop.
856            hir::ExprKind::Parallel { iter, body, .. } => {
857                let iter_temp = self.new_temp(Ty::from_hir(&iter.ty), iter.span);
858                self.build_expr_into(iter, Place::from_local(iter_temp));
859
860                // Build body as block expression
861                self.build_block_expr(body, destination);
862            }
863
864            // Result builder: `build TypeName { body }` — at MIR level, lower the body.
865            // The builder protocol (build_block, build_expression, build_optional)
866            // transforms will be applied during a dedicated desugaring pass.
867            hir::ExprKind::Build { body, .. } => {
868                self.build_block_expr(body, destination);
869            }
870
871            // === Method call ===
872            hir::ExprKind::MethodCall {
873                receiver,
874                method,
875                args,
876            } => {
877                // Build receiver
878                let recv_temp = self.new_temp(Ty::from_hir(&receiver.ty), receiver.span);
879                self.build_expr_into(receiver, Place::from_local(recv_temp));
880
881                // Build arguments (receiver is first arg)
882                let mut arg_operands = vec![Operand::Copy(Place::from_local(recv_temp))];
883                for arg in args {
884                    let arg_temp = self.new_temp(Ty::from_hir(&arg.ty), arg.span);
885                    self.build_expr_into(arg, Place::from_local(arg_temp));
886                    arg_operands.push(Operand::Copy(Place::from_local(arg_temp)));
887                }
888
889                let next_block = self.new_block();
890
891                // Check if receiver is a trait object (dyn Trait) for virtual dispatch
892                let base_recv_ty = match &receiver.ty {
893                    hir::Ty::Ref { inner, .. } => inner.as_ref(),
894                    other => other,
895                };
896                let is_virtual = matches!(base_recv_ty, hir::Ty::TraitObject { .. });
897
898                // Qualify the method name with the receiver type
899                let recv_type_prefix = get_impl_type_prefix(&receiver.ty);
900                let qualified_method =
901                    Symbol::intern(&format!("{}::{}", recv_type_prefix, method.as_str()));
902
903                // Use a function constant for the method
904                let func_temp = self.new_temp(Ty::Unit, expr.span);
905                self.set_terminator(Terminator::Call {
906                    func: Operand::Copy(Place::from_local(func_temp)),
907                    func_name: Some(qualified_method),
908                    args: arg_operands,
909                    destination: destination.clone(),
910                    target: next_block,
911                    span: expr.span,
912                    is_virtual,
913                });
914
915                self.current_block = Some(next_block);
916            }
917
918            // === Match expression ===
919            hir::ExprKind::Match {
920                expr: scrutinee,
921                arms,
922            } => {
923                // Build the scrutinee
924                let scrut_temp = self.new_temp(Ty::from_hir(&scrutinee.ty), scrutinee.span);
925                self.build_expr_into(scrutinee, Place::from_local(scrut_temp));
926
927                // Save the block after scrutinee evaluation — we need to connect it
928                // to the switch block after we create it
929                let post_scrutinee_block = self.current_block;
930
931                let join_block = self.new_block();
932
933                // Build each arm as: body block that writes to destination then jumps to join
934                let mut mir_arms = Vec::new();
935                let mut arm_body_blocks = Vec::new();
936
937                for arm in arms {
938                    let body_block = self.new_block();
939                    arm_body_blocks.push((body_block, arm));
940                }
941
942                // Check if any arm uses a Variant pattern (enum matching)
943                let has_variant_arms = arm_body_blocks
944                    .iter()
945                    .any(|(_, arm)| matches!(&arm.pattern, hir::Pattern::Variant { .. }));
946
947                for (idx, (body_block, arm)) in arm_body_blocks.iter().enumerate() {
948                    // Build the arm body
949                    self.current_block = Some(*body_block);
950
951                    // Handle pattern bindings
952                    if let hir::Pattern::Ident {
953                        id,
954                        name: _,
955                        mutable: _,
956                        ty,
957                    } = &arm.pattern
958                    {
959                        let local = self.new_local(Some(ty.clone()), arm.body.span, false);
960                        self.hir_to_local.insert(*id, local);
961                        self.push_statement(Statement::Assign {
962                            place: Place::from_local(local),
963                            rvalue: Rvalue::Use(Operand::Copy(Place::from_local(scrut_temp))),
964                            span: arm.body.span,
965                        });
966                    } else if let hir::Pattern::Variant {
967                        fields,
968                        variant_name,
969                        ..
970                    } = &arm.pattern
971                    {
972                        // Bind variant fields from the scrutinee
973                        let variant_str = variant_name.as_str();
974                        let is_result_option =
975                            matches!(variant_str.as_str(), "Ok" | "Err" | "Some" | "None");
976
977                        if is_result_option {
978                            // For Result::Ok(s): s = scrutinee.ok (field 1)
979                            // For Result::Err(e): e = scrutinee.err (field 2)
980                            // For Option::Some(v): v = scrutinee.value (field 1)
981                            let value_field_idx = match variant_str.as_str() {
982                                "Ok" | "Some" => 1u32,
983                                "Err" => 2u32,
984                                _ => 1u32,
985                            };
986                            for (i, field_pat) in fields.iter().enumerate() {
987                                if let hir::Pattern::Ident { id, ty, .. } = field_pat {
988                                    let local =
989                                        self.new_local(Some(ty.clone()), arm.body.span, false);
990                                    self.hir_to_local.insert(*id, local);
991                                    self.push_statement(Statement::Assign {
992                                        place: Place::from_local(local),
993                                        rvalue: Rvalue::Use(Operand::Copy(
994                                            Place::from_local(scrut_temp)
995                                                .field(FieldIdx::new(value_field_idx + i as u32)),
996                                        )),
997                                        span: arm.body.span,
998                                    });
999                                }
1000                            }
1001                        } else {
1002                            // Tagged union enum (e.g., TokenKind::Ident(name))
1003                            // Use Downcast projection: scrutinee.VariantName._i
1004                            for (i, field_pat) in fields.iter().enumerate() {
1005                                if let hir::Pattern::Ident { id, ty, .. } = field_pat {
1006                                    let local =
1007                                        self.new_local(Some(ty.clone()), arm.body.span, false);
1008                                    self.hir_to_local.insert(*id, local);
1009                                    self.push_statement(Statement::Assign {
1010                                        place: Place::from_local(local),
1011                                        rvalue: Rvalue::Use(Operand::Copy(
1012                                            Place::from_local(scrut_temp)
1013                                                .project(PlaceElem::Downcast(*variant_name))
1014                                                .field(FieldIdx::new(i as u32)),
1015                                        )),
1016                                        span: arm.body.span,
1017                                    });
1018                                }
1019                            }
1020                        }
1021                    }
1022
1023                    // Handle guard expression: evaluate guard, branch on result
1024                    if let Some(guard) = &arm.guard {
1025                        let guard_temp = self.new_temp(Ty::Bool, guard.span);
1026                        self.build_expr_into(guard, Place::from_local(guard_temp));
1027
1028                        // Create the real body block for when guard succeeds
1029                        let real_body_block = self.new_block();
1030
1031                        // Guard failure falls through to the next arm's body, or join_block
1032                        let fallthrough = if idx + 1 < arm_body_blocks.len() {
1033                            arm_body_blocks[idx + 1].0
1034                        } else {
1035                            join_block
1036                        };
1037
1038                        self.set_terminator(Terminator::SwitchInt {
1039                            discriminant: Operand::Copy(Place::from_local(guard_temp)),
1040                            targets: SwitchTargets::new(
1041                                vec![(1, real_body_block)],
1042                                fallthrough,
1043                            ),
1044                            span: guard.span,
1045                        });
1046
1047                        self.current_block = Some(real_body_block);
1048                    }
1049
1050                    self.build_expr_into(&arm.body, destination.clone());
1051
1052                    if self.current_block.is_some() {
1053                        self.set_terminator(Terminator::Goto {
1054                            target: join_block,
1055                            span: arm.body.span,
1056                        });
1057                    }
1058
1059                    // Convert HIR pattern to match lowering pattern
1060                    let pat = self.hir_pattern_to_mir_pat(&arm.pattern);
1061                    mir_arms.push((*body_block, pat));
1062                }
1063
1064                // Build the switch: for now, simple literal/wildcard matching
1065                let switch_block = self.new_block();
1066
1067                // Connect the post-scrutinee block to the switch block
1068                if let Some(post_scrut) = post_scrutinee_block {
1069                    self.current_block = Some(post_scrut);
1070                    self.set_terminator(Terminator::Goto {
1071                        target: switch_block,
1072                        span: expr.span,
1073                    });
1074                }
1075
1076                self.current_block = Some(switch_block);
1077
1078                // Detect string literal patterns in the original HIR arms
1079                let mut string_arms: Vec<(BasicBlockId, String)> = Vec::new();
1080                let mut string_otherwise: Option<BasicBlockId> = None;
1081                let has_string_patterns = arm_body_blocks.iter().any(|(_, arm)| {
1082                    matches!(
1083                        &arm.pattern,
1084                        hir::Pattern::Literal {
1085                            value: hir::LitValue::String(_),
1086                            ..
1087                        }
1088                    )
1089                });
1090
1091                if has_string_patterns {
1092                    for (body_block, arm) in &arm_body_blocks {
1093                        match &arm.pattern {
1094                            hir::Pattern::Literal {
1095                                value: hir::LitValue::String(s),
1096                                ..
1097                            } => {
1098                                string_arms.push((*body_block, s.clone()));
1099                            }
1100                            _ => {
1101                                string_otherwise = Some(*body_block);
1102                            }
1103                        }
1104                    }
1105                }
1106
1107                if has_string_patterns && !string_arms.is_empty() {
1108                    // Generate if-else chain for string pattern matching
1109                    let default_target = string_otherwise.unwrap_or(join_block);
1110                    for (i, (arm_block, lit_str)) in string_arms.iter().enumerate() {
1111                        // Create a temp for the string literal
1112                        let str_temp = self.new_temp(
1113                            Ty::Named {
1114                                def_id: joule_hir::HirId(u32::MAX),
1115                                name: Symbol::intern("str"),
1116                            },
1117                            expr.span,
1118                        );
1119                        self.push_statement(Statement::Assign {
1120                            place: Place::from_local(str_temp),
1121                            rvalue: Rvalue::Use(Operand::Constant(Constant::new(
1122                                Literal::String(lit_str.clone()),
1123                                Ty::Named {
1124                                    def_id: joule_hir::HirId(u32::MAX),
1125                                    name: Symbol::intern("str"),
1126                                },
1127                                expr.span,
1128                            ))),
1129                            span: expr.span,
1130                        });
1131
1132                        // Create a temp for the comparison result
1133                        let cmp_temp = self.new_temp(Ty::Bool, expr.span);
1134                        self.push_statement(Statement::Assign {
1135                            place: Place::from_local(cmp_temp),
1136                            rvalue: Rvalue::BinaryOp {
1137                                op: BinOp::Eq,
1138                                left: Operand::Copy(Place::from_local(scrut_temp)),
1139                                right: Operand::Copy(Place::from_local(str_temp)),
1140                            },
1141                            span: expr.span,
1142                        });
1143
1144                        // Determine the next comparison block or default
1145                        let next_target = if i + 1 < string_arms.len() {
1146                            self.new_block()
1147                        } else {
1148                            default_target
1149                        };
1150
1151                        // SwitchInt: if equal → arm body, else → next comparison
1152                        self.set_terminator(Terminator::SwitchInt {
1153                            discriminant: Operand::Copy(Place::from_local(cmp_temp)),
1154                            targets: SwitchTargets::new(vec![(1, *arm_block)], next_target),
1155                            span: expr.span,
1156                        });
1157
1158                        // Move to the next comparison block
1159                        if i + 1 < string_arms.len() {
1160                            self.current_block = Some(next_target);
1161                        }
1162                    }
1163                } else {
1164                    // Non-string pattern matching (original code)
1165
1166                    let mut branches: Vec<(u128, BasicBlockId)> = Vec::new();
1167                    let mut otherwise = join_block;
1168                    let mut seen_discriminants = std::collections::HashSet::new();
1169
1170                    // Collect range-pattern arms for if-else chain emission
1171                    let mut range_arms: Vec<(BasicBlockId, Option<i128>, Option<i128>, bool)> =
1172                        Vec::new();
1173
1174                    for (body_block, pat) in &mir_arms {
1175                        match pat {
1176                            crate::match_lowering::Pat::Literal(lit) => {
1177                                let disc = lit.to_discriminant();
1178                                if seen_discriminants.insert(disc) {
1179                                    branches.push((disc, *body_block));
1180                                } else {
1181                                    otherwise = *body_block;
1182                                }
1183                            }
1184                            crate::match_lowering::Pat::Or(alternatives) => {
1185                                // Expand or-pattern: each literal alternative
1186                                // maps to the same body block
1187                                for alt in alternatives {
1188                                    if let crate::match_lowering::Pat::Literal(lit) = alt {
1189                                        let disc = lit.to_discriminant();
1190                                        if seen_discriminants.insert(disc) {
1191                                            branches.push((disc, *body_block));
1192                                        }
1193                                    }
1194                                }
1195                            }
1196                            crate::match_lowering::Pat::Range {
1197                                lo,
1198                                hi,
1199                                inclusive,
1200                            } => {
1201                                range_arms.push((*body_block, *lo, *hi, *inclusive));
1202                            }
1203                            crate::match_lowering::Pat::Wild
1204                            | crate::match_lowering::Pat::Bind { .. } => {
1205                                otherwise = *body_block;
1206                            }
1207                            _ => {
1208                                otherwise = *body_block;
1209                            }
1210                        }
1211                    }
1212
1213                    if !range_arms.is_empty() && branches.is_empty() {
1214                        // Range-only matching: emit if-else chain
1215                        for (i, (arm_block, lo, hi, inclusive)) in
1216                            range_arms.iter().enumerate()
1217                        {
1218                            // lo_check: scrutinee >= lo
1219                            let mut checks = Vec::new();
1220                            if let Some(lo_val) = lo {
1221                                let lo_temp = self.new_temp(Ty::Bool, expr.span);
1222                                let lo_const = self.new_temp(
1223                                    Ty::from_hir(&scrutinee.ty),
1224                                    expr.span,
1225                                );
1226                                self.push_statement(Statement::Assign {
1227                                    place: Place::from_local(lo_const),
1228                                    rvalue: Rvalue::Use(Operand::Constant(Constant::new(
1229                                        Literal::Int(*lo_val as i64, IntTy::I64),
1230                                        Ty::from_hir(&scrutinee.ty),
1231                                        expr.span,
1232                                    ))),
1233                                    span: expr.span,
1234                                });
1235                                self.push_statement(Statement::Assign {
1236                                    place: Place::from_local(lo_temp),
1237                                    rvalue: Rvalue::BinaryOp {
1238                                        op: BinOp::Ge,
1239                                        left: Operand::Copy(Place::from_local(scrut_temp)),
1240                                        right: Operand::Copy(Place::from_local(lo_const)),
1241                                    },
1242                                    span: expr.span,
1243                                });
1244                                checks.push(lo_temp);
1245                            }
1246                            if let Some(hi_val) = hi {
1247                                let hi_temp = self.new_temp(Ty::Bool, expr.span);
1248                                let hi_const = self.new_temp(
1249                                    Ty::from_hir(&scrutinee.ty),
1250                                    expr.span,
1251                                );
1252                                self.push_statement(Statement::Assign {
1253                                    place: Place::from_local(hi_const),
1254                                    rvalue: Rvalue::Use(Operand::Constant(Constant::new(
1255                                        Literal::Int(*hi_val as i64, IntTy::I64),
1256                                        Ty::from_hir(&scrutinee.ty),
1257                                        expr.span,
1258                                    ))),
1259                                    span: expr.span,
1260                                });
1261                                let cmp_op = if *inclusive { BinOp::Le } else { BinOp::Lt };
1262                                self.push_statement(Statement::Assign {
1263                                    place: Place::from_local(hi_temp),
1264                                    rvalue: Rvalue::BinaryOp {
1265                                        op: cmp_op,
1266                                        left: Operand::Copy(Place::from_local(scrut_temp)),
1267                                        right: Operand::Copy(Place::from_local(hi_const)),
1268                                    },
1269                                    span: expr.span,
1270                                });
1271                                checks.push(hi_temp);
1272                            }
1273
1274                            // AND the checks together
1275                            let result_temp = if checks.len() == 2 {
1276                                let and_temp = self.new_temp(Ty::Bool, expr.span);
1277                                self.push_statement(Statement::Assign {
1278                                    place: Place::from_local(and_temp),
1279                                    rvalue: Rvalue::BinaryOp {
1280                                        op: BinOp::BitAnd,
1281                                        left: Operand::Copy(Place::from_local(checks[0])),
1282                                        right: Operand::Copy(Place::from_local(checks[1])),
1283                                    },
1284                                    span: expr.span,
1285                                });
1286                                and_temp
1287                            } else if checks.len() == 1 {
1288                                checks[0]
1289                            } else {
1290                                // No bounds — always matches (open range)
1291                                let t = self.new_temp(Ty::Bool, expr.span);
1292                                self.push_statement(Statement::Assign {
1293                                    place: Place::from_local(t),
1294                                    rvalue: Rvalue::Use(Operand::Constant(Constant::new(
1295                                        Literal::Bool(true),
1296                                        Ty::Bool,
1297                                        expr.span,
1298                                    ))),
1299                                    span: expr.span,
1300                                });
1301                                t
1302                            };
1303
1304                            let next_target =
1305                                if i + 1 < range_arms.len() {
1306                                    self.new_block()
1307                                } else {
1308                                    otherwise
1309                                };
1310
1311                            self.set_terminator(Terminator::SwitchInt {
1312                                discriminant: Operand::Copy(Place::from_local(result_temp)),
1313                                targets: SwitchTargets::new(
1314                                    vec![(1, *arm_block)],
1315                                    next_target,
1316                                ),
1317                                span: expr.span,
1318                            });
1319
1320                            if i + 1 < range_arms.len() {
1321                                self.current_block = Some(next_target);
1322                            }
1323                        }
1324                    } else if branches.is_empty() && range_arms.is_empty() {
1325                        // All wildcards — just goto the first arm
1326                        self.set_terminator(Terminator::Goto {
1327                            target: otherwise,
1328                            span: expr.span,
1329                        });
1330                    } else if has_variant_arms {
1331                        // Determine if this is Result/Option matching (bool discriminant)
1332                        // or regular enum matching (integer tag discriminant)
1333                        let is_result_option = arm_body_blocks.iter().any(|(_, arm)| {
1334                            if let hir::Pattern::Variant { variant_name, .. } = &arm.pattern {
1335                                let vs = variant_name.as_str();
1336                                matches!(vs.as_str(), "Ok" | "Err" | "Some" | "None")
1337                            } else {
1338                                false
1339                            }
1340                        });
1341
1342                        if is_result_option {
1343                            // Check if any variant arm has literal inner fields
1344                            // (e.g., Some(' '), Some('/'))
1345                            let has_inner_literals = arm_body_blocks.iter().any(|(_, arm)| {
1346                                if let hir::Pattern::Variant { fields, .. } = &arm.pattern {
1347                                    fields
1348                                        .iter()
1349                                        .any(|f| matches!(f, hir::Pattern::Literal { .. }))
1350                                } else {
1351                                    false
1352                                }
1353                            });
1354
1355                            if has_inner_literals {
1356                                // Two-level matching: first on is_some/is_ok, then on inner value
1357                                let mut some_ok_literal_arms: Vec<(BasicBlockId, u128)> =
1358                                    Vec::new();
1359                                let mut some_ok_otherwise: Option<BasicBlockId> = None;
1360                                let mut none_err_block: Option<BasicBlockId> = None;
1361                                let mut wildcard_block: Option<BasicBlockId> = None;
1362
1363                                for (body_block, arm) in &arm_body_blocks {
1364                                    match &arm.pattern {
1365                                        hir::Pattern::Variant {
1366                                            variant_name,
1367                                            fields,
1368                                            ..
1369                                        } => {
1370                                            let vs = variant_name.as_str();
1371                                            match vs.as_str() {
1372                                                "Some" | "Ok" => {
1373                                                    if let Some(hir::Pattern::Literal {
1374                                                        value,
1375                                                        ..
1376                                                    }) = fields.first()
1377                                                    {
1378                                                        let disc = match value {
1379                                                            hir::LitValue::Int(n) => *n as u128,
1380                                                            hir::LitValue::Char(c) => *c as u128,
1381                                                            hir::LitValue::Bool(b) => {
1382                                                                if *b {
1383                                                                    1u128
1384                                                                } else {
1385                                                                    0u128
1386                                                                }
1387                                                            }
1388                                                            _ => 0u128,
1389                                                        };
1390                                                        some_ok_literal_arms
1391                                                            .push((*body_block, disc));
1392                                                    } else {
1393                                                        // Some(x) binding — catch-all for inner switch
1394                                                        some_ok_otherwise = Some(*body_block);
1395                                                    }
1396                                                }
1397                                                "None" | "Err" => {
1398                                                    none_err_block = Some(*body_block);
1399                                                }
1400                                                _ => {}
1401                                            }
1402                                        }
1403                                        _ => {
1404                                            wildcard_block = Some(*body_block);
1405                                        }
1406                                    }
1407                                }
1408
1409                                let overall_otherwise = wildcard_block.unwrap_or(join_block);
1410                                let false_target = none_err_block.unwrap_or(overall_otherwise);
1411                                let inner_otherwise =
1412                                    some_ok_otherwise.unwrap_or(overall_otherwise);
1413
1414                                // Level 1: SwitchInt on is_some/is_ok (field 0)
1415                                let disc_temp = self.new_temp(Ty::Bool, expr.span);
1416                                self.push_statement(Statement::Assign {
1417                                    place: Place::from_local(disc_temp),
1418                                    rvalue: Rvalue::Use(Operand::Copy(
1419                                        Place::from_local(scrut_temp).field(FieldIdx::new(0)),
1420                                    )),
1421                                    span: expr.span,
1422                                });
1423
1424                                let inner_switch_block = self.new_block();
1425                                self.set_terminator(Terminator::SwitchInt {
1426                                    discriminant: Operand::Copy(Place::from_local(disc_temp)),
1427                                    targets: SwitchTargets::new(
1428                                        vec![(1, inner_switch_block)],
1429                                        false_target,
1430                                    ),
1431                                    span: expr.span,
1432                                });
1433
1434                                // Level 2: extract .value (field 1) and switch on it
1435                                self.current_block = Some(inner_switch_block);
1436                                let value_temp = self.new_temp(Ty::Int(IntTy::I32), expr.span);
1437                                self.push_statement(Statement::Assign {
1438                                    place: Place::from_local(value_temp),
1439                                    rvalue: Rvalue::Use(Operand::Copy(
1440                                        Place::from_local(scrut_temp).field(FieldIdx::new(1)),
1441                                    )),
1442                                    span: expr.span,
1443                                });
1444
1445                                let mut inner_branches: Vec<(u128, BasicBlockId)> = Vec::new();
1446                                let mut inner_seen = std::collections::HashSet::new();
1447                                for (block, disc) in &some_ok_literal_arms {
1448                                    if inner_seen.insert(*disc) {
1449                                        inner_branches.push((*disc, *block));
1450                                    }
1451                                }
1452
1453                                self.set_terminator(Terminator::SwitchInt {
1454                                    discriminant: Operand::Copy(Place::from_local(value_temp)),
1455                                    targets: SwitchTargets::new(inner_branches, inner_otherwise),
1456                                    span: expr.span,
1457                                });
1458                            } else {
1459                                // Single-level Result/Option matching (no inner literals)
1460                                let disc_temp = self.new_temp(Ty::Bool, expr.span);
1461                                self.push_statement(Statement::Assign {
1462                                    place: Place::from_local(disc_temp),
1463                                    rvalue: Rvalue::Use(Operand::Copy(
1464                                        Place::from_local(scrut_temp).field(FieldIdx::new(0)),
1465                                    )),
1466                                    span: expr.span,
1467                                });
1468                                self.set_terminator(Terminator::SwitchInt {
1469                                    discriminant: Operand::Copy(Place::from_local(disc_temp)),
1470                                    targets: SwitchTargets::new(branches, otherwise),
1471                                    span: expr.span,
1472                                });
1473                            }
1474                        } else {
1475                            // Regular enum: field 0 is tag (integer)
1476                            let disc_temp = self.new_temp(Ty::Int(IntTy::I32), expr.span);
1477                            self.push_statement(Statement::Assign {
1478                                place: Place::from_local(disc_temp),
1479                                rvalue: Rvalue::Use(Operand::Copy(
1480                                    Place::from_local(scrut_temp).field(FieldIdx::new(0)),
1481                                )),
1482                                span: expr.span,
1483                            });
1484                            self.set_terminator(Terminator::SwitchInt {
1485                                discriminant: Operand::Copy(Place::from_local(disc_temp)),
1486                                targets: SwitchTargets::new(branches, otherwise),
1487                                span: expr.span,
1488                            });
1489                        }
1490                    } else {
1491                        self.set_terminator(Terminator::SwitchInt {
1492                            discriminant: Operand::Copy(Place::from_local(scrut_temp)),
1493                            targets: SwitchTargets::new(branches, otherwise),
1494                            span: expr.span,
1495                        });
1496                    }
1497                } // end of else (non-string pattern matching)
1498
1499                self.current_block = Some(join_block);
1500            }
1501
1502            // === While loop ===
1503            hir::ExprKind::While { condition, body } => {
1504                let cond_block = self.new_block();
1505                let body_block = self.new_block();
1506                let exit_block = self.new_block();
1507
1508                // Jump to condition block
1509                self.set_terminator(Terminator::Goto {
1510                    target: cond_block,
1511                    span: expr.span,
1512                });
1513
1514                // Build condition
1515                self.current_block = Some(cond_block);
1516                let cond_temp = self.new_temp(Ty::Bool, condition.span);
1517                self.build_expr_into(condition, Place::from_local(cond_temp));
1518
1519                self.set_terminator(Terminator::SwitchInt {
1520                    discriminant: Operand::Copy(Place::from_local(cond_temp)),
1521                    targets: SwitchTargets::if_else(body_block, exit_block),
1522                    span: expr.span,
1523                });
1524
1525                // Build body
1526                self.current_block = Some(body_block);
1527                self.loop_stack.push(LoopContext {
1528                    header: cond_block,
1529                    exit: exit_block,
1530                    break_destination: None,
1531                });
1532
1533                let body_temp = self.new_temp(Ty::Unit, body.span);
1534                self.build_block_expr(body, Place::from_local(body_temp));
1535
1536                self.loop_stack.pop();
1537
1538                // Loop back to condition
1539                if self.current_block.is_some() {
1540                    self.set_terminator(Terminator::Goto {
1541                        target: cond_block,
1542                        span: expr.span,
1543                    });
1544                }
1545
1546                // Continue after loop
1547                self.current_block = Some(exit_block);
1548
1549                // While produces unit
1550                self.push_statement(Statement::Assign {
1551                    place: destination,
1552                    rvalue: Rvalue::Aggregate {
1553                        kind: AggregateKind::Tuple,
1554                        operands: vec![],
1555                    },
1556                    span: expr.span,
1557                });
1558            }
1559
1560            // === Infinite loop ===
1561            hir::ExprKind::Loop { body } => {
1562                let loop_block = self.new_block();
1563                let exit_block = self.new_block();
1564
1565                // Jump to loop body
1566                self.set_terminator(Terminator::Goto {
1567                    target: loop_block,
1568                    span: expr.span,
1569                });
1570
1571                // Build body
1572                self.current_block = Some(loop_block);
1573                self.loop_stack.push(LoopContext {
1574                    header: loop_block,
1575                    exit: exit_block,
1576                    break_destination: Some(destination.clone()),
1577                });
1578
1579                let body_temp = self.new_temp(Ty::from_hir(&body.ty), body.span);
1580                self.build_block_expr(body, Place::from_local(body_temp));
1581
1582                self.loop_stack.pop();
1583
1584                // Loop back
1585                if self.current_block.is_some() {
1586                    self.set_terminator(Terminator::Goto {
1587                        target: loop_block,
1588                        span: expr.span,
1589                    });
1590                }
1591
1592                // Continue after loop (only reachable via break)
1593                self.current_block = Some(exit_block);
1594            }
1595
1596            // === For loop (desugared to while with iterator) ===
1597            hir::ExprKind::For {
1598                pattern,
1599                iter,
1600                body,
1601            } => {
1602                // Build iterator expression
1603                // Unwrap Ref types since iter() in bootstrap passes through the receiver
1604                let raw_iter_ty = Ty::from_hir(&iter.ty);
1605                let iter_ty = match raw_iter_ty {
1606                    Ty::Ref { inner, .. } => *inner,
1607                    other => other,
1608                };
1609
1610                // Check if iterating over a Range/RangeInclusive
1611                let range_info = match &iter_ty {
1612                    Ty::Named { name, .. } => {
1613                        let name_str = name.as_str();
1614                        if name_str == "Range" {
1615                            Some(false) // exclusive: start..end
1616                        } else if name_str == "RangeInclusive" {
1617                            Some(true) // inclusive: start..=end
1618                        } else {
1619                            None
1620                        }
1621                    }
1622                    _ => None,
1623                };
1624
1625                if let Some(inclusive) = range_info {
1626                    // === Range-based for loop ===
1627                    // Decompose the Range struct literal into start/end values
1628                    // and generate a counter-based loop (no Range struct in MIR)
1629
1630                    // Determine element type from the struct literal's start field
1631                    let elem_ty = if let hir::ExprKind::Struct { fields, .. } = &iter.kind {
1632                        fields
1633                            .iter()
1634                            .find(|f| f.name.as_str() == "start")
1635                            .map(|f| {
1636                                let t = Ty::from_hir(&f.value.ty);
1637                                if matches!(t, Ty::Unit) {
1638                                    Ty::Int(IntTy::I32)
1639                                } else {
1640                                    t
1641                                }
1642                            })
1643                            .unwrap_or(Ty::Int(IntTy::I32))
1644                    } else {
1645                        Ty::Int(IntTy::I32)
1646                    };
1647
1648                    // Build start and end values from the struct literal fields
1649                    let start_temp = self.new_temp(elem_ty.clone(), expr.span);
1650                    let end_temp = self.new_temp(elem_ty.clone(), expr.span);
1651
1652                    if let hir::ExprKind::Struct { fields, .. } = &iter.kind {
1653                        for field in fields {
1654                            if field.name.as_str() == "start" {
1655                                self.build_expr_into(&field.value, Place::from_local(start_temp));
1656                            } else if field.name.as_str() == "end" {
1657                                self.build_expr_into(&field.value, Place::from_local(end_temp));
1658                            }
1659                        }
1660                    }
1661
1662                    // Initialize counter to start value
1663                    let counter_temp = self.new_temp(elem_ty.clone(), expr.span);
1664                    self.push_statement(Statement::Assign {
1665                        place: Place::from_local(counter_temp),
1666                        rvalue: Rvalue::Use(Operand::Copy(Place::from_local(start_temp))),
1667                        span: expr.span,
1668                    });
1669
1670                    let cond_block = self.new_block();
1671                    let body_block = self.new_block();
1672                    let increment_block = self.new_block();
1673                    let exit_block = self.new_block();
1674
1675                    // Jump to condition check
1676                    self.set_terminator(Terminator::Goto {
1677                        target: cond_block,
1678                        span: expr.span,
1679                    });
1680
1681                    // Condition: counter < end (exclusive) or counter <= end (inclusive)
1682                    self.current_block = Some(cond_block);
1683                    let cond_temp = self.new_temp(Ty::Bool, expr.span);
1684                    let cmp_op = if inclusive {
1685                        hir::BinOp::Le
1686                    } else {
1687                        hir::BinOp::Lt
1688                    };
1689                    self.push_statement(Statement::Assign {
1690                        place: Place::from_local(cond_temp),
1691                        rvalue: Rvalue::BinaryOp {
1692                            op: cmp_op,
1693                            left: Operand::Copy(Place::from_local(counter_temp)),
1694                            right: Operand::Copy(Place::from_local(end_temp)),
1695                        },
1696                        span: expr.span,
1697                    });
1698
1699                    self.set_terminator(Terminator::SwitchInt {
1700                        discriminant: Operand::Copy(Place::from_local(cond_temp)),
1701                        targets: SwitchTargets::if_else(body_block, exit_block),
1702                        span: expr.span,
1703                    });
1704
1705                    // Body: bind pattern to counter value, execute body
1706                    self.current_block = Some(body_block);
1707                    // `continue` jumps to increment_block (not cond_block) so the
1708                    // loop counter is always incremented before re-checking the condition.
1709                    self.loop_stack.push(LoopContext {
1710                        header: increment_block,
1711                        exit: exit_block,
1712                        break_destination: None,
1713                    });
1714
1715                    // Bind loop variable to current counter value
1716                    if let hir::Pattern::Ident { id, ty, .. } = pattern {
1717                        let pat_ty = match ty {
1718                            hir::Ty::Error | hir::Ty::Infer(_) => elem_ty.clone(),
1719                            _ => Ty::from_hir(ty),
1720                        };
1721                        let elem_local = self.new_temp(pat_ty, body.span);
1722                        self.hir_to_local.insert(*id, elem_local);
1723
1724                        self.push_statement(Statement::Assign {
1725                            place: Place::from_local(elem_local),
1726                            rvalue: Rvalue::Use(Operand::Copy(Place::from_local(counter_temp))),
1727                            span: body.span,
1728                        });
1729                    }
1730
1731                    let body_result = self.new_temp(Ty::Unit, body.span);
1732                    self.build_block_expr(body, Place::from_local(body_result));
1733
1734                    self.loop_stack.pop();
1735
1736                    // Fall through from body to increment block
1737                    if self.current_block.is_some() {
1738                        self.set_terminator(Terminator::Goto {
1739                            target: increment_block,
1740                            span: expr.span,
1741                        });
1742                    }
1743
1744                    // Increment block: counter++ then jump back to condition
1745                    // `continue` also lands here, ensuring the counter always advances.
1746                    self.current_block = Some(increment_block);
1747                    let one = Operand::Constant(Constant::new(
1748                        hir::Literal::Int(1, hir::IntTy::I32),
1749                        elem_ty,
1750                        expr.span,
1751                    ));
1752                    self.push_statement(Statement::Assign {
1753                        place: Place::from_local(counter_temp),
1754                        rvalue: Rvalue::BinaryOp {
1755                            op: hir::BinOp::Add,
1756                            left: Operand::Copy(Place::from_local(counter_temp)),
1757                            right: one,
1758                        },
1759                        span: expr.span,
1760                    });
1761
1762                    self.set_terminator(Terminator::Goto {
1763                        target: cond_block,
1764                        span: expr.span,
1765                    });
1766
1767                    // Exit block
1768                    self.current_block = Some(exit_block);
1769
1770                    // For produces unit
1771                    self.push_statement(Statement::Assign {
1772                        place: destination,
1773                        rvalue: Rvalue::Aggregate {
1774                            kind: AggregateKind::Tuple,
1775                            operands: vec![],
1776                        },
1777                        span: expr.span,
1778                    });
1779                } else {
1780                    // === Collection-based for loop (Vec, Array, etc.) ===
1781                    // Index-based desugaring: for i in 0..len { elem = coll[i]; body }
1782
1783                    // When iterating `for x in &v`, unwrap the Ref and iterate
1784                    // over the underlying collection directly (by value copy).
1785                    // This avoids storing a reference in a non-ref temp.
1786                    let actual_iter = match &iter.kind {
1787                        hir::ExprKind::Ref { expr: inner, .. } => inner.as_ref(),
1788                        _ => iter,
1789                    };
1790                    let iter_temp = self.new_temp(iter_ty, actual_iter.span);
1791                    self.build_expr_into(actual_iter, Place::from_local(iter_temp));
1792
1793                    // Create a counter/index for iteration
1794                    let idx_temp = self.new_temp(Ty::Uint(UintTy::U64), expr.span);
1795                    self.push_statement(Statement::Assign {
1796                        place: Place::from_local(idx_temp),
1797                        rvalue: Rvalue::Use(Operand::Constant(Constant::new(
1798                            hir::Literal::Uint(0, hir::UintTy::U64),
1799                            Ty::Uint(UintTy::U64),
1800                            expr.span,
1801                        ))),
1802                        span: expr.span,
1803                    });
1804
1805                    // Get length
1806                    let len_temp = self.new_temp(Ty::Uint(UintTy::U64), expr.span);
1807                    self.push_statement(Statement::Assign {
1808                        place: Place::from_local(len_temp),
1809                        rvalue: Rvalue::Len {
1810                            place: Place::from_local(iter_temp),
1811                        },
1812                        span: expr.span,
1813                    });
1814
1815                    let cond_block = self.new_block();
1816                    let body_block = self.new_block();
1817                    let increment_block = self.new_block();
1818                    let exit_block = self.new_block();
1819
1820                    // Jump to condition
1821                    self.set_terminator(Terminator::Goto {
1822                        target: cond_block,
1823                        span: expr.span,
1824                    });
1825
1826                    // Condition: idx < len
1827                    self.current_block = Some(cond_block);
1828                    let cond_temp = self.new_temp(Ty::Bool, expr.span);
1829                    self.push_statement(Statement::Assign {
1830                        place: Place::from_local(cond_temp),
1831                        rvalue: Rvalue::BinaryOp {
1832                            op: hir::BinOp::Lt,
1833                            left: Operand::Copy(Place::from_local(idx_temp)),
1834                            right: Operand::Copy(Place::from_local(len_temp)),
1835                        },
1836                        span: expr.span,
1837                    });
1838
1839                    self.set_terminator(Terminator::SwitchInt {
1840                        discriminant: Operand::Copy(Place::from_local(cond_temp)),
1841                        targets: SwitchTargets::if_else(body_block, exit_block),
1842                        span: expr.span,
1843                    });
1844
1845                    // Body: bind pattern to current element, execute body
1846                    self.current_block = Some(body_block);
1847                    // `continue` jumps to increment_block so the index always advances.
1848                    self.loop_stack.push(LoopContext {
1849                        header: increment_block,
1850                        exit: exit_block,
1851                        break_destination: None,
1852                    });
1853
1854                    // Bind loop variable
1855                    if let hir::Pattern::Ident { id, ty, .. } = pattern {
1856                        // Infer element type from iterator when pattern type is Error
1857                        let elem_ty = match ty {
1858                            hir::Ty::Error | hir::Ty::Infer(_) => {
1859                                let iter_mir_ty = &self.func.locals[iter_temp.0 as usize].ty;
1860                                match iter_mir_ty {
1861                                    Ty::Generic { args, .. } if !args.is_empty() => args[0].clone(),
1862                                    Ty::Array { element, .. } => (**element).clone(),
1863                                    Ty::Slice { element } => (**element).clone(),
1864                                    _ => Ty::from_hir(ty),
1865                                }
1866                            }
1867                            _ => Ty::from_hir(ty),
1868                        };
1869                        let elem_local = self.new_temp(elem_ty, body.span);
1870                        self.hir_to_local.insert(*id, elem_local);
1871
1872                        // elem = iter[idx]
1873                        let indexed_place = Place::from_local(iter_temp).index(idx_temp);
1874                        self.push_statement(Statement::Assign {
1875                            place: Place::from_local(elem_local),
1876                            rvalue: Rvalue::Use(Operand::Copy(indexed_place)),
1877                            span: body.span,
1878                        });
1879                    }
1880
1881                    let body_result = self.new_temp(Ty::Unit, body.span);
1882                    self.build_block_expr(body, Place::from_local(body_result));
1883
1884                    self.loop_stack.pop();
1885
1886                    // Fall through from body to increment block
1887                    if self.current_block.is_some() {
1888                        self.set_terminator(Terminator::Goto {
1889                            target: increment_block,
1890                            span: expr.span,
1891                        });
1892                    }
1893
1894                    // Increment block: idx++ then jump back to condition
1895                    // `continue` also lands here, ensuring the index always advances.
1896                    self.current_block = Some(increment_block);
1897                    let one = Operand::Constant(Constant::new(
1898                        hir::Literal::Uint(1, hir::UintTy::U64),
1899                        Ty::Uint(UintTy::U64),
1900                        expr.span,
1901                    ));
1902                    self.push_statement(Statement::Assign {
1903                        place: Place::from_local(idx_temp),
1904                        rvalue: Rvalue::BinaryOp {
1905                            op: hir::BinOp::Add,
1906                            left: Operand::Copy(Place::from_local(idx_temp)),
1907                            right: one,
1908                        },
1909                        span: expr.span,
1910                    });
1911
1912                    self.set_terminator(Terminator::Goto {
1913                        target: cond_block,
1914                        span: expr.span,
1915                    });
1916
1917                    // Exit block
1918                    self.current_block = Some(exit_block);
1919
1920                    // For produces unit
1921                    self.push_statement(Statement::Assign {
1922                        place: destination,
1923                        rvalue: Rvalue::Aggregate {
1924                            kind: AggregateKind::Tuple,
1925                            operands: vec![],
1926                        },
1927                        span: expr.span,
1928                    });
1929                }
1930            }
1931
1932            // === Break ===
1933            hir::ExprKind::Break { value } => {
1934                if let Some(loop_ctx) = self.loop_stack.last() {
1935                    let exit = loop_ctx.exit;
1936                    let break_dest = loop_ctx.break_destination.clone();
1937                    // Write break value to the loop's destination (not the
1938                    // throwaway temp from Statement::Expr)
1939                    if let Some(val) = value {
1940                        if let Some(dest) = break_dest {
1941                            self.build_expr_into(val, dest);
1942                        } else {
1943                            self.build_expr_into(val, destination.clone());
1944                        }
1945                    }
1946                    self.set_terminator(Terminator::Goto {
1947                        target: exit,
1948                        span: expr.span,
1949                    });
1950                }
1951            }
1952
1953            // === Continue ===
1954            hir::ExprKind::Continue => {
1955                if let Some(loop_ctx) = self.loop_stack.last() {
1956                    let header = loop_ctx.header;
1957                    self.set_terminator(Terminator::Goto {
1958                        target: header,
1959                        span: expr.span,
1960                    });
1961                }
1962            }
1963
1964            // === Index expression ===
1965            hir::ExprKind::Index { expr: inner, index } => {
1966                let inner_temp = self.new_temp(Ty::from_hir(&inner.ty), inner.span);
1967                self.build_expr_into(inner, Place::from_local(inner_temp));
1968
1969                let index_temp = self.new_temp(Ty::from_hir(&index.ty), index.span);
1970                self.build_expr_into(index, Place::from_local(index_temp));
1971
1972                let indexed_place = Place::from_local(inner_temp).index(index_temp);
1973                let ty = Ty::from_hir(&expr.ty);
1974                let operand = if ty.is_copy() {
1975                    Operand::Copy(indexed_place)
1976                } else {
1977                    Operand::Move(indexed_place)
1978                };
1979
1980                self.push_statement(Statement::Assign {
1981                    place: destination,
1982                    rvalue: Rvalue::Use(operand),
1983                    span: expr.span,
1984                });
1985            }
1986
1987            // === Multi-dimensional index: arr[i, j] or arr[1..5, ::2] ===
1988            hir::ExprKind::MultiDimIndex {
1989                expr: inner,
1990                indices,
1991            } => {
1992                let inner_ty = Ty::from_hir(&inner.ty);
1993                let inner_temp = self.new_temp(inner_ty.clone(), inner.span);
1994                self.build_expr_into(inner, Place::from_local(inner_temp));
1995
1996                let result_ty = Ty::from_hir(&expr.ty);
1997
1998                // Check if all indices are single (scalar access → _get call)
1999                let all_single = indices
2000                    .iter()
2001                    .all(|ic| matches!(ic, hir::HirIndexComponent::Single(_)));
2002
2003                // Build ndarray method name from the inner type
2004                let ndarray_prefix = ndarray_type_prefix(&inner_ty);
2005
2006                if all_single {
2007                    // Scalar access: ndarray_get(&arr, i0, i1, ...)
2008                    let func_name = format!("{ndarray_prefix}_get");
2009                    let func_sym = joule_common::Symbol::intern(&func_name);
2010
2011                    // Create a temporary for the function pointer (required by Terminator::Call)
2012                    let fn_temp = self.new_temp(
2013                        Ty::FnPtr {
2014                            params: vec![],
2015                            return_ty: Box::new(result_ty.clone()),
2016                        },
2017                        expr.span,
2018                    );
2019
2020                    let mut args = vec![Operand::Copy(Place::from_local(inner_temp))];
2021                    for ic in indices {
2022                        if let hir::HirIndexComponent::Single(idx_expr) = ic {
2023                            let idx_temp =
2024                                self.new_temp(Ty::from_hir(&idx_expr.ty), idx_expr.span);
2025                            self.build_expr_into(idx_expr, Place::from_local(idx_temp));
2026                            args.push(Operand::Copy(Place::from_local(idx_temp)));
2027                        }
2028                    }
2029
2030                    let next_block = self.new_block();
2031                    self.set_terminator(Terminator::Call {
2032                        func: Operand::Copy(Place::from_local(fn_temp)),
2033                        func_name: Some(func_sym),
2034                        args,
2035                        destination,
2036                        target: next_block,
2037                        span: expr.span,
2038                        is_virtual: false,
2039                    });
2040                    self.current_block = Some(next_block);
2041                } else {
2042                    // Slicing — produces an NDView via stride manipulation.
2043                    // Full implementation in Phase 3 (C codegen for slice ops).
2044                    let func_name = format!("{ndarray_prefix}_slice");
2045                    let func_sym = joule_common::Symbol::intern(&func_name);
2046
2047                    let fn_temp = self.new_temp(
2048                        Ty::FnPtr {
2049                            params: vec![],
2050                            return_ty: Box::new(result_ty.clone()),
2051                        },
2052                        expr.span,
2053                    );
2054
2055                    let args = vec![Operand::Copy(Place::from_local(inner_temp))];
2056
2057                    let next_block = self.new_block();
2058                    self.set_terminator(Terminator::Call {
2059                        func: Operand::Copy(Place::from_local(fn_temp)),
2060                        func_name: Some(func_sym),
2061                        args,
2062                        destination,
2063                        target: next_block,
2064                        span: expr.span,
2065                        is_virtual: false,
2066                    });
2067                    self.current_block = Some(next_block);
2068                }
2069            }
2070
2071            // === Array repeat [value; count] ===
2072            hir::ExprKind::ArrayRepeat { element, count } => {
2073                let elem_temp = self.new_temp(Ty::from_hir(&element.ty), element.span);
2074                self.build_expr_into(element, Place::from_local(elem_temp));
2075
2076                let mut operands = Vec::new();
2077                for _ in 0..*count {
2078                    operands.push(Operand::Copy(Place::from_local(elem_temp)));
2079                }
2080
2081                let elem_ty = Ty::from_hir(&element.ty);
2082                self.push_statement(Statement::Assign {
2083                    place: destination,
2084                    rvalue: Rvalue::Aggregate {
2085                        kind: AggregateKind::Array(elem_ty),
2086                        operands,
2087                    },
2088                    span: expr.span,
2089                });
2090            }
2091
2092            // === Try operator: expr? ===
2093            hir::ExprKind::Await { expr: inner } => {
2094                // Evaluate the future/task expression
2095                let task_temp = self.new_temp(Ty::from_hir(&inner.ty), inner.span);
2096                self.build_expr_into(inner, Place::from_local(task_temp));
2097
2098                // Create block to continue in after await
2099                let next_block = self.new_block();
2100
2101                // Emit TaskAwait terminator — blocks until the task produces a result
2102                self.set_terminator(Terminator::TaskAwait {
2103                    task: Operand::Move(Place::from_local(task_temp)),
2104                    destination: destination.clone(),
2105                    target: next_block,
2106                    span: expr.span,
2107                });
2108
2109                self.current_block = Some(next_block);
2110            }
2111
2112            hir::ExprKind::Try { expr: inner } => {
2113                let inner_temp = self.new_temp(Ty::from_hir(&inner.ty), inner.span);
2114                self.build_expr_into(inner, Place::from_local(inner_temp));
2115
2116                self.push_statement(Statement::Assign {
2117                    place: destination,
2118                    rvalue: Rvalue::Try(Operand::Move(Place::from_local(inner_temp))),
2119                    span: expr.span,
2120                });
2121            }
2122
2123            hir::ExprKind::Path { segments } => {
2124                // Path expression like EnumName::VariantName
2125                // Create an enum variant construction
2126                self.push_statement(Statement::Assign {
2127                    place: destination,
2128                    rvalue: Rvalue::EnumVariant {
2129                        variant: segments
2130                            .last()
2131                            .cloned()
2132                            .unwrap_or_else(|| joule_common::Symbol::intern("unknown")),
2133                        fields: vec![],
2134                    },
2135                    span: expr.span,
2136                });
2137            }
2138
2139            hir::ExprKind::Closure {
2140                params,
2141                return_ty,
2142                body,
2143                captures,
2144                is_move,
2145            } => {
2146                // Get the closure id from the destination local's type (assigned by typechecker).
2147                // This ensures the aggregate ID matches the local's Ty::Closure ID.
2148                let closure_id = if let Ty::Closure { id, .. } =
2149                    &self.func.locals[destination.local.0 as usize].ty
2150                {
2151                    *id
2152                } else {
2153                    expr.id.0 // fallback for non-typed destinations
2154                };
2155                let closure_ty = Ty::Closure {
2156                    id: closure_id,
2157                    params: params.iter().map(|p| Ty::from_hir(&p.ty)).collect(),
2158                    return_ty: Box::new(Ty::from_hir(return_ty)),
2159                    capture_tys: captures.iter().map(|c| Ty::from_hir(&c.ty)).collect(),
2160                };
2161
2162                // Build the capture operands
2163                let mut capture_operands = Vec::new();
2164                for cap in captures {
2165                    if let Some(&local) = self.hir_to_local.get(&cap.def_id) {
2166                        let place = Place::from_local(local);
2167                        let cap_ty = Ty::from_hir(&cap.ty);
2168                        let operand = if *is_move || !cap_ty.is_copy() {
2169                            if cap.by_ref {
2170                                // Capture by reference
2171                                let ref_local = self.func.add_local(LocalDecl::new(
2172                                    None,
2173                                    Ty::Ref {
2174                                        mutable: false,
2175                                        inner: Box::new(cap_ty),
2176                                    },
2177                                    false,
2178                                    expr.span,
2179                                ));
2180                                self.push_statement(Statement::Assign {
2181                                    place: Place::from_local(ref_local),
2182                                    rvalue: Rvalue::Ref {
2183                                        mutable: false,
2184                                        place,
2185                                    },
2186                                    span: expr.span,
2187                                });
2188                                Operand::Move(Place::from_local(ref_local))
2189                            } else {
2190                                Operand::Move(place)
2191                            }
2192                        } else if cap.by_ref {
2193                            let ref_local = self.func.add_local(LocalDecl::new(
2194                                None,
2195                                Ty::Ref {
2196                                    mutable: false,
2197                                    inner: Box::new(cap_ty),
2198                                },
2199                                false,
2200                                expr.span,
2201                            ));
2202                            self.push_statement(Statement::Assign {
2203                                place: Place::from_local(ref_local),
2204                                rvalue: Rvalue::Ref {
2205                                    mutable: false,
2206                                    place,
2207                                },
2208                                span: expr.span,
2209                            });
2210                            Operand::Copy(Place::from_local(ref_local))
2211                        } else {
2212                            Operand::Copy(place)
2213                        };
2214                        capture_operands.push(operand);
2215                    }
2216                }
2217
2218                // Construct the closure aggregate (captures)
2219                self.push_statement(Statement::Assign {
2220                    place: destination,
2221                    rvalue: Rvalue::Aggregate {
2222                        kind: AggregateKind::Closure(closure_id),
2223                        operands: capture_operands,
2224                    },
2225                    span: expr.span,
2226                });
2227
2228                // Build a separate FunctionMIR for the closure body.
2229                // The function is named "closure_{id}" and takes:
2230                //   - env pointer (for captures) as first param
2231                //   - then the closure's own params
2232                let closure_return_ty = Ty::from_hir(return_ty);
2233                let closure_func_name = Symbol::intern(&format!("JouleClosure_{}_fn", closure_id));
2234                let mut closure_builder = MirBuilder::new(
2235                    FunctionId::new(closure_id),
2236                    closure_func_name,
2237                    closure_return_ty.clone(),
2238                    expr.span,
2239                );
2240                closure_builder.variant_index_map = self.variant_index_map.clone();
2241
2242                // Return place type
2243                closure_builder.func.locals[0].ty = closure_return_ty;
2244
2245                // First param: env pointer — use Named type so codegen can resolve fields
2246                let env_type_name = Symbol::intern(&format!("JouleClosure_{}_env", closure_id));
2247                let env_local = closure_builder.func.add_local(LocalDecl::new(
2248                    Some(Symbol::intern("_env")),
2249                    Ty::RawPtr {
2250                        mutable: false,
2251                        inner: Box::new(Ty::Named {
2252                            def_id: HirId::new(closure_id),
2253                            name: env_type_name,
2254                        }),
2255                    },
2256                    false,
2257                    expr.span,
2258                ));
2259                closure_builder.func.params.push(env_local);
2260
2261                // Add closure parameters
2262                for param in params {
2263                    let param_ty = Ty::from_hir(&param.ty);
2264                    let param_name = if let hir::Pattern::Ident { name, .. } = &param.pattern {
2265                        Some(*name)
2266                    } else {
2267                        None
2268                    };
2269                    let local = closure_builder
2270                        .func
2271                        .add_local(LocalDecl::new(param_name, param_ty, false, param.span));
2272                    closure_builder.func.params.push(local);
2273                    if let hir::Pattern::Ident { id, .. } = &param.pattern {
2274                        closure_builder.hir_to_local.insert(*id, local);
2275                    }
2276                }
2277
2278                // Map captured variables to env struct field accesses
2279                for cap in captures.iter() {
2280                    let cap_ty = Ty::from_hir(&cap.ty);
2281                    let cap_local = closure_builder.func.add_local(LocalDecl::new(
2282                        Some(cap.name),
2283                        cap_ty,
2284                        true,
2285                        expr.span,
2286                    ));
2287                    closure_builder.hir_to_local.insert(cap.def_id, cap_local);
2288                }
2289
2290                // Create entry block and build body
2291                let entry = closure_builder.new_block();
2292                closure_builder.current_block = Some(entry);
2293
2294                // For captures, emit assignments from env fields at the start of the body.
2295                // The env param is a pointer, so we need Deref before Field access:
2296                //   cap_local = (*env_local).field_i
2297                for (i, cap) in captures.iter().enumerate() {
2298                    if let Some(&cap_local) = closure_builder.hir_to_local.get(&cap.def_id) {
2299                        closure_builder.push_statement(Statement::Assign {
2300                            place: Place::from_local(cap_local),
2301                            rvalue: Rvalue::Use(Operand::Copy(Place {
2302                                local: env_local,
2303                                projection: vec![
2304                                    PlaceElem::Deref,
2305                                    PlaceElem::Field(FieldIdx::new(i as u32)),
2306                                ],
2307                            })),
2308                            span: expr.span,
2309                        });
2310                    }
2311                }
2312
2313                // Build the closure body expression
2314                closure_builder.build_expr_into(body, Place::return_place());
2315                closure_builder.ensure_return();
2316
2317                // Collect any nested closures from the closure builder
2318                let nested = closure_builder.pending_closures;
2319                self.pending_closures.push(closure_builder.func);
2320                self.pending_closures.extend(nested);
2321
2322                let _ = closure_ty;
2323            }
2324        }
2325    }
2326
2327    /// Build a select expression
2328    fn build_select(
2329        &mut self,
2330        arms: &[hir::HirSelectArm],
2331        _biased: bool,
2332        destination: Place,
2333        span: Span,
2334    ) {
2335        use crate::{ChannelOp, SelectArm};
2336
2337        let mut mir_arms = Vec::new();
2338        let mut arm_blocks = Vec::new();
2339        let join_block = self.new_block();
2340
2341        // Create blocks and operations for each arm
2342        for arm in arms {
2343            let arm_block = self.new_block();
2344            arm_blocks.push((arm_block, arm));
2345
2346            let operation = match &arm.operation {
2347                hir::HirSelectOperation::Recv { channel } => {
2348                    // Build channel expression into a temp
2349                    let chan_temp = self.new_temp(Ty::from_hir(&channel.ty), channel.span);
2350                    self.build_expr_into(channel, Place::from_local(chan_temp));
2351                    ChannelOp::Recv {
2352                        channel: Operand::Copy(Place::from_local(chan_temp)),
2353                    }
2354                }
2355                hir::HirSelectOperation::Send { channel, value } => {
2356                    let chan_temp = self.new_temp(Ty::from_hir(&channel.ty), channel.span);
2357                    self.build_expr_into(channel, Place::from_local(chan_temp));
2358                    let val_temp = self.new_temp(Ty::from_hir(&value.ty), value.span);
2359                    self.build_expr_into(value, Place::from_local(val_temp));
2360                    ChannelOp::Send {
2361                        channel: Operand::Copy(Place::from_local(chan_temp)),
2362                        value: Operand::Copy(Place::from_local(val_temp)),
2363                    }
2364                }
2365                hir::HirSelectOperation::Timeout { duration: _ } => {
2366                    // For now, use a default timeout
2367                    ChannelOp::Timeout {
2368                        duration_ns: 1_000_000_000, // 1 second default
2369                    }
2370                }
2371            };
2372
2373            mir_arms.push(SelectArm {
2374                operation,
2375                target: arm_block,
2376            });
2377        }
2378
2379        // Create temp for selected arm index
2380        let selected_arm = self.new_temp(Ty::Uint(UintTy::U32), span);
2381
2382        // Emit select terminator
2383        self.set_terminator(Terminator::Select {
2384            arms: mir_arms,
2385            default: None,
2386            destination: destination.clone(),
2387            selected_arm: Place::from_local(selected_arm),
2388            span,
2389        });
2390
2391        // Build arm bodies
2392        for (arm_block, arm) in arm_blocks {
2393            self.current_block = Some(arm_block);
2394            self.build_expr_into(&arm.body, destination.clone());
2395            if self.current_block.is_some() {
2396                self.set_terminator(Terminator::Goto {
2397                    target: join_block,
2398                    span: arm.span,
2399                });
2400            }
2401        }
2402
2403        // Continue in join block
2404        self.current_block = Some(join_block);
2405    }
2406
2407    /// Build an if expression
2408    fn build_if(
2409        &mut self,
2410        condition: &hir::Expr,
2411        then_block: &hir::Block,
2412        else_block: Option<&hir::Expr>,
2413        destination: Place,
2414        span: Span,
2415    ) {
2416        // Evaluate condition
2417        let cond_temp = self.new_temp(Ty::Bool, condition.span);
2418        self.build_expr_into(condition, Place::from_local(cond_temp));
2419
2420        // Create blocks
2421        let then_bb = self.new_block();
2422        let else_bb = self.new_block();
2423        let join_bb = self.new_block();
2424
2425        // Branch on condition
2426        self.set_terminator(Terminator::SwitchInt {
2427            discriminant: Operand::Copy(Place::from_local(cond_temp)),
2428            targets: SwitchTargets::if_else(then_bb, else_bb),
2429            span,
2430        });
2431
2432        // Build then block
2433        self.current_block = Some(then_bb);
2434        self.build_block_expr(then_block, destination.clone());
2435        if self.current_block.is_some() {
2436            self.set_terminator(Terminator::Goto {
2437                target: join_bb,
2438                span,
2439            });
2440        }
2441
2442        // Build else block
2443        self.current_block = Some(else_bb);
2444        if let Some(else_expr) = else_block {
2445            self.build_expr_into(else_expr, destination);
2446        } else {
2447            // No else block, assign unit
2448            self.push_statement(Statement::Assign {
2449                place: destination,
2450                rvalue: Rvalue::Aggregate {
2451                    kind: AggregateKind::Tuple,
2452                    operands: vec![],
2453                },
2454                span,
2455            });
2456        }
2457        if self.current_block.is_some() {
2458            self.set_terminator(Terminator::Goto {
2459                target: join_bb,
2460                span,
2461            });
2462        }
2463
2464        // Continue in join block
2465        self.current_block = Some(join_bb);
2466    }
2467
2468    /// Convert an HIR pattern to a MIR match lowering pattern
2469    fn hir_pattern_to_mir_pat(&self, pattern: &hir::Pattern) -> crate::match_lowering::Pat {
2470        match pattern {
2471            hir::Pattern::Wildcard { .. } => crate::match_lowering::Pat::Wild,
2472            hir::Pattern::Ident { name, .. } => crate::match_lowering::Pat::Bind {
2473                name: name.as_str().to_string(),
2474                inner: None,
2475            },
2476            hir::Pattern::Tuple { patterns, .. } => {
2477                let fields: Vec<_> = patterns
2478                    .iter()
2479                    .map(|p| self.hir_pattern_to_mir_pat(p))
2480                    .collect();
2481                crate::match_lowering::Pat::Ctor {
2482                    ctor: crate::match_lowering::CtorKind::Tuple(fields.len()),
2483                    fields,
2484                }
2485            }
2486            hir::Pattern::Struct { def_id, fields, .. } => {
2487                let field_pats: Vec<_> = fields
2488                    .iter()
2489                    .map(|f| self.hir_pattern_to_mir_pat(&f.pattern))
2490                    .collect();
2491                crate::match_lowering::Pat::Ctor {
2492                    ctor: crate::match_lowering::CtorKind::Struct {
2493                        struct_id: def_id.0,
2494                    },
2495                    fields: field_pats,
2496                }
2497            }
2498            hir::Pattern::Variant {
2499                enum_name,
2500                variant_name,
2501                ..
2502            } => {
2503                // Map variant name to discriminant value
2504                // Result: Ok=1 (is_ok=true), Err=0 (is_ok=false)
2505                // Option: Some=1 (is_some=true), None=0 (is_some=false)
2506                // Other enums: use variant index from enum definitions
2507                let variant_str = variant_name.as_str();
2508                let enum_str = enum_name.as_str();
2509                let disc = match variant_str.as_str() {
2510                    "Ok" | "Some" => 1u128,
2511                    "Err" | "None" => 0u128,
2512                    _ => {
2513                        // Look up actual variant index from enum definitions
2514                        self.variant_index_map
2515                            .get(&(enum_str.to_string(), variant_str.to_string()))
2516                            .copied()
2517                            .unwrap_or(0u128)
2518                    }
2519                };
2520                crate::match_lowering::Pat::Literal(crate::match_lowering::LitPat::Int(
2521                    disc as i128,
2522                ))
2523            }
2524            hir::Pattern::Literal { value, .. } => {
2525                // Strings can't be SwitchInt discriminants — treat as wildcard
2526                if matches!(value, hir::LitValue::String(_)) {
2527                    return crate::match_lowering::Pat::Wild;
2528                }
2529                let disc = match value {
2530                    hir::LitValue::Int(n) => *n,
2531                    hir::LitValue::Char(c) => *c as i128,
2532                    hir::LitValue::Bool(b) => {
2533                        if *b {
2534                            1
2535                        } else {
2536                            0
2537                        }
2538                    }
2539                    hir::LitValue::String(_) => unreachable!(),
2540                };
2541                crate::match_lowering::Pat::Literal(crate::match_lowering::LitPat::Int(disc))
2542            }
2543            hir::Pattern::Or { patterns, .. } => {
2544                let pats = patterns
2545                    .iter()
2546                    .map(|p| self.hir_pattern_to_mir_pat(p))
2547                    .collect();
2548                crate::match_lowering::Pat::Or(pats)
2549            }
2550            hir::Pattern::Range {
2551                lo, hi, inclusive, ..
2552            } => {
2553                let lo_val = match lo {
2554                    hir::LitValue::Int(n) => Some(*n),
2555                    hir::LitValue::Char(c) => Some(*c as i128),
2556                    _ => None,
2557                };
2558                let hi_val = match hi {
2559                    hir::LitValue::Int(n) => Some(*n),
2560                    hir::LitValue::Char(c) => Some(*c as i128),
2561                    _ => None,
2562                };
2563                crate::match_lowering::Pat::Range {
2564                    lo: lo_val,
2565                    hi: hi_val,
2566                    inclusive: *inclusive,
2567                }
2568            }
2569        }
2570    }
2571
2572    /// Build a place from an expression
2573    fn build_place(&mut self, expr: &hir::Expr) -> Place {
2574        match &expr.kind {
2575            hir::ExprKind::Var { def_id, .. } => {
2576                if let Some(&local) = self.hir_to_local.get(def_id) {
2577                    Place::from_local(local)
2578                } else {
2579                    // Fallback: create a temporary
2580                    let temp = self.new_temp(Ty::from_hir(&expr.ty), expr.span);
2581                    Place::from_local(temp)
2582                }
2583            }
2584
2585            hir::ExprKind::Deref { expr: inner } => {
2586                let inner_place = self.build_place(inner);
2587                inner_place.deref()
2588            }
2589
2590            hir::ExprKind::Field {
2591                expr: inner,
2592                field_idx,
2593                ..
2594            } => {
2595                let inner_place = self.build_place(inner);
2596                inner_place.field(FieldIdx::new(*field_idx as u32))
2597            }
2598
2599            hir::ExprKind::Index { expr: inner, index } => {
2600                let inner_place = self.build_place(inner);
2601                // Build index into a local
2602                let index_local = self.new_temp(Ty::from_hir(&index.ty), index.span);
2603                self.build_expr_into(index, Place::from_local(index_local));
2604                inner_place.index(index_local)
2605            }
2606
2607            hir::ExprKind::MultiDimIndex { .. } => {
2608                // Multi-dim indexing uses function calls (_get/_set),
2609                // so it cannot produce a Place directly.
2610                // Evaluate into a temporary for read access.
2611                let temp = self.new_temp(Ty::from_hir(&expr.ty), expr.span);
2612                self.build_expr_into(expr, Place::from_local(temp));
2613                Place::from_local(temp)
2614            }
2615
2616            _ => {
2617                // For other expressions, evaluate into a temporary
2618                let temp = self.new_temp(Ty::from_hir(&expr.ty), expr.span);
2619                self.build_expr_into(expr, Place::from_local(temp));
2620                Place::from_local(temp)
2621            }
2622        }
2623    }
2624
2625    /// Determine the kind of cast
2626    fn determine_cast_kind(&self, from: &hir::Ty, to: &hir::Ty) -> crate::CastKind {
2627        use crate::CastKind;
2628
2629        match (from, to) {
2630            (hir::Ty::Int(_) | hir::Ty::Uint(_), hir::Ty::Int(_) | hir::Ty::Uint(_)) => {
2631                CastKind::IntToInt
2632            }
2633            (hir::Ty::Float(_), hir::Ty::Float(_)) => CastKind::FloatToFloat,
2634            (hir::Ty::Int(_) | hir::Ty::Uint(_), hir::Ty::Float(_)) => CastKind::IntToFloat,
2635            (hir::Ty::Float(_), hir::Ty::Int(_) | hir::Ty::Uint(_)) => CastKind::FloatToInt,
2636            (hir::Ty::Ref { .. }, hir::Ty::Ref { .. }) => CastKind::PtrToPtr,
2637            _ => CastKind::IntToInt, // Default fallback
2638        }
2639    }
2640}
2641
2642/// Get the type name prefix for an impl block's self type
2643fn get_impl_type_prefix(ty: &hir::Ty) -> String {
2644    match ty {
2645        hir::Ty::Named { name, .. } => {
2646            let s = name.as_str();
2647            // Handle Self type - should be resolved by context but fallback gracefully
2648            if s == "Self" {
2649                "Self".to_string() // Will be resolved later if possible
2650            } else {
2651                s.to_string()
2652            }
2653        }
2654        hir::Ty::Generic { name, args, .. } => {
2655            let base = name.as_str();
2656            if args.is_empty() {
2657                base.to_string()
2658            } else {
2659                let arg_strs: Vec<String> = args.iter().map(get_impl_type_prefix).collect();
2660                format!("{}<{}>", base, arg_strs.join(", "))
2661            }
2662        }
2663        hir::Ty::TypeParam { name, .. } => name.as_str().to_string(),
2664        hir::Ty::Int(int_ty) => format!("{:?}", int_ty).to_lowercase(),
2665        hir::Ty::Uint(uint_ty) => format!("{:?}", uint_ty).to_lowercase(),
2666        hir::Ty::Float(float_ty) => format!("{:?}", float_ty).to_lowercase(),
2667        hir::Ty::Bool => "bool".to_string(),
2668        hir::Ty::Char => "char".to_string(),
2669        hir::Ty::String => "JouleString".to_string(), // Match C typedef
2670        hir::Ty::Unit => "unit".to_string(),
2671        hir::Ty::Never => "never".to_string(),
2672        hir::Ty::Tuple(tys) => {
2673            let arg_strs: Vec<String> = tys.iter().map(get_impl_type_prefix).collect();
2674            format!("({})", arg_strs.join(", "))
2675        }
2676        // For method receivers like &self, unwrap to get the actual type
2677        hir::Ty::Ref { inner, .. } => get_impl_type_prefix(inner),
2678        hir::Ty::Array { element, size } => {
2679            format!("[{}; {}]", get_impl_type_prefix(element), size)
2680        }
2681        hir::Ty::Slice { element } => {
2682            format!("[{}]", get_impl_type_prefix(element))
2683        }
2684        hir::Ty::Function { params, return_ty } => {
2685            let param_strs: Vec<String> = params.iter().map(get_impl_type_prefix).collect();
2686            format!(
2687                "fn({}) -> {}",
2688                param_strs.join(", "),
2689                get_impl_type_prefix(return_ty)
2690            )
2691        }
2692        hir::Ty::Infer(_) => "infer".to_string(),
2693        hir::Ty::Error => "void".to_string(), // Fallback for unresolved types
2694        hir::Ty::Future { result_ty } => format!("Future<{}>", get_impl_type_prefix(result_ty)),
2695        hir::Ty::Task { result_ty } => format!("Task<{}>", get_impl_type_prefix(result_ty)),
2696        hir::Ty::TaskGroup => "TaskGroup".to_string(),
2697        hir::Ty::Channel { element_ty } => format!("Channel<{}>", get_impl_type_prefix(element_ty)),
2698        hir::Ty::Sender { element_ty } => format!("Sender<{}>", get_impl_type_prefix(element_ty)),
2699        hir::Ty::Receiver { element_ty } => {
2700            format!("Receiver<{}>", get_impl_type_prefix(element_ty))
2701        }
2702        hir::Ty::TraitObject { trait_name } => {
2703            format!("dyn_{}", trait_name.as_str())
2704        }
2705        hir::Ty::Closure {
2706            id,
2707            params,
2708            return_ty,
2709            ..
2710        } => {
2711            let param_strs: Vec<String> = params.iter().map(get_impl_type_prefix).collect();
2712            format!(
2713                "Closure_{}({}) -> {}",
2714                id.0,
2715                param_strs.join(", "),
2716                get_impl_type_prefix(return_ty)
2717            )
2718        }
2719        hir::Ty::Union { variants } => {
2720            let var_strs: Vec<String> = variants.iter().map(get_impl_type_prefix).collect();
2721            var_strs.join("_or_")
2722        }
2723        hir::Ty::Opaque { name, .. } => name.as_str().to_string(),
2724        hir::Ty::NDArray { element, rank } => {
2725            format!("NDArray[{}; {}]", get_impl_type_prefix(element), rank)
2726        }
2727        hir::Ty::NDView { element, rank } => {
2728            format!("NDView[{}; {}]", get_impl_type_prefix(element), rank)
2729        }
2730        hir::Ty::CowArray { element, rank } => {
2731            format!("CowArray[{}; {}]", get_impl_type_prefix(element), rank)
2732        }
2733        hir::Ty::DynArray { element } => {
2734            format!("DynArray[{}]", get_impl_type_prefix(element))
2735        }
2736        hir::Ty::SmallVec { element, capacity } => {
2737            format!("SmallVec[{}; {}]", get_impl_type_prefix(element), capacity)
2738        }
2739        hir::Ty::Simd { element, lanes } => {
2740            format!("Simd[{}; {}]", get_impl_type_prefix(element), lanes)
2741        }
2742    }
2743}
2744
2745/// Get the C-compatible type prefix for an NDArray MIR type.
2746///
2747/// Used to generate function names like `JouleNDArray_double_2_get`.
2748fn ndarray_type_prefix(ty: &crate::Ty) -> String {
2749    match ty {
2750        crate::Ty::NDArray { element, rank } => {
2751            let elem = mir_ty_to_c_ident(element);
2752            format!("JouleNDArray_{elem}_{rank}")
2753        }
2754        crate::Ty::NDView { element, rank } => {
2755            let elem = mir_ty_to_c_ident(element);
2756            format!("JouleNDView_{elem}_{rank}")
2757        }
2758        crate::Ty::CowArray { element, rank } => {
2759            let elem = mir_ty_to_c_ident(element);
2760            format!("JouleCowArray_{elem}_{rank}")
2761        }
2762        crate::Ty::DynArray { element } => {
2763            let elem = mir_ty_to_c_ident(element);
2764            format!("JouleDynArray_{elem}")
2765        }
2766        crate::Ty::SmallVec { element, capacity } => {
2767            let elem = mir_ty_to_c_ident(element);
2768            format!("JouleSmallVec_{elem}_{capacity}")
2769        }
2770        crate::Ty::Simd { element, lanes } => {
2771            let elem = mir_ty_to_c_ident(element);
2772            format!("JouleSimd_{elem}_{lanes}")
2773        }
2774        _ => "ndarray_unknown".to_string(),
2775    }
2776}
2777
2778/// Convert a MIR type to a C-compatible identifier fragment (for ndarray naming).
2779fn mir_ty_to_c_ident(ty: &crate::Ty) -> String {
2780    match ty {
2781        crate::Ty::Bool => "bool".to_string(),
2782        crate::Ty::Char => "u32".to_string(),
2783        crate::Ty::Int(crate::IntTy::I8) => "i8".to_string(),
2784        crate::Ty::Int(crate::IntTy::I16) => "i16".to_string(),
2785        crate::Ty::Int(crate::IntTy::I32) => "i32".to_string(),
2786        crate::Ty::Int(crate::IntTy::I64) => "i64".to_string(),
2787        crate::Ty::Int(crate::IntTy::Isize) => "isize".to_string(),
2788        crate::Ty::Uint(crate::UintTy::U8) => "u8".to_string(),
2789        crate::Ty::Uint(crate::UintTy::U16) => "u16".to_string(),
2790        crate::Ty::Uint(crate::UintTy::U32) => "u32".to_string(),
2791        crate::Ty::Uint(crate::UintTy::U64) => "u64".to_string(),
2792        crate::Ty::Uint(crate::UintTy::Usize) => "usize".to_string(),
2793        crate::Ty::Float(crate::FloatTy::F16) => "JouleF16".to_string(),
2794        crate::Ty::Float(crate::FloatTy::BF16) => "JouleBF16".to_string(),
2795        crate::Ty::Float(crate::FloatTy::F32) => "float".to_string(),
2796        crate::Ty::Float(crate::FloatTy::F64) => "double".to_string(),
2797        _ => "void".to_string(),
2798    }
2799}
2800
2801/// Build MIR for an entire HIR module
2802pub fn build_mir(hir: &hir::Hir) -> crate::MirContext {
2803    let mut ctx = crate::MirContext::new();
2804
2805    // Precompute variant index map from all enum definitions
2806    let mut variant_index_map: HashMap<(String, String), u128> = HashMap::new();
2807    for item in &hir.items {
2808        if let hir::Item::Enum(e) = item {
2809            let enum_name = e.name.as_str().to_string();
2810            for (idx, variant) in e.variants.iter().enumerate() {
2811                let variant_name = variant.name.as_str().to_string();
2812                variant_index_map.insert((enum_name.clone(), variant_name), idx as u128);
2813            }
2814        }
2815    }
2816
2817    for item in &hir.items {
2818        match item {
2819            hir::Item::Function(func) => {
2820                if func.is_extern {
2821                    // Extern functions have no body — record them so the C emitter
2822                    // emits only a forward declaration (no stub).
2823                    ctx.extern_fns.push(func.clone());
2824                } else {
2825                    let (mir_func, closures) =
2826                        MirBuilder::build_function_with_enums(func, &variant_index_map);
2827                    ctx.add_function(mir_func);
2828                    for closure_func in closures {
2829                        ctx.add_function(closure_func);
2830                    }
2831                }
2832            }
2833            hir::Item::Struct(s) => {
2834                ctx.structs.push(s.clone());
2835            }
2836            hir::Item::Enum(e) => {
2837                ctx.enums.push(e.clone());
2838            }
2839            hir::Item::Trait(t) => {
2840                ctx.traits.push(t.clone());
2841            }
2842            hir::Item::Impl(imp) => {
2843                // Get the type name for prefixing method names
2844                let type_prefix = get_impl_type_prefix(&imp.ty);
2845
2846                // Build MIR for each method in the impl block
2847                for impl_item in &imp.items {
2848                    match impl_item {
2849                        hir::ImplItem::Function(func) => {
2850                            let (mut mir_func, closures) =
2851                                MirBuilder::build_function_with_enums(func, &variant_index_map);
2852                            // Prefix the function name with the type name for C compatibility
2853                            let qualified_name = format!("{}::{}", type_prefix, func.name.as_str());
2854                            mir_func.name = Symbol::intern(&qualified_name);
2855                            ctx.add_function(mir_func);
2856                            for closure_func in closures {
2857                                ctx.add_function(closure_func);
2858                            }
2859                        }
2860                    }
2861                }
2862                ctx.impls.push(imp.clone());
2863            }
2864            hir::Item::Const(c) => {
2865                ctx.consts.push(c.clone());
2866            }
2867            hir::Item::Static(s) => {
2868                ctx.statics.push(s.clone());
2869            }
2870            // Effect and Supervisor declarations are metadata-only items.
2871            // Their runtime support is compiled by the C codegen from the
2872            // declaration data, not through MIR function bodies.
2873            hir::Item::Effect(_) | hir::Item::Supervisor(_) => {}
2874        }
2875    }
2876
2877    // Copy type aliases from HIR to MIR
2878    for (name, ty) in &hir.type_aliases {
2879        ctx.type_aliases
2880            .insert(name.as_str().to_string(), ty.clone());
2881    }
2882
2883    ctx
2884}
2885
2886#[cfg(test)]
2887mod tests {
2888    use super::*;
2889    use joule_hir::IntTy;
2890
2891    #[test]
2892    fn test_mir_builder_creation() {
2893        let builder = MirBuilder::new(
2894            FunctionId::new(0),
2895            Symbol::from_u32(0),
2896            Ty::Unit,
2897            Span::dummy(),
2898        );
2899        assert_eq!(builder.func.locals.len(), 1); // Return place
2900        assert_eq!(builder.func.basic_blocks.len(), 0);
2901    }
2902
2903    #[test]
2904    fn test_new_local() {
2905        let mut builder = MirBuilder::new(
2906            FunctionId::new(0),
2907            Symbol::from_u32(0),
2908            Ty::Unit,
2909            Span::dummy(),
2910        );
2911
2912        let local = builder.new_local(Some(hir::Ty::Int(IntTy::I32)), Span::dummy(), false);
2913        assert_eq!(local, Local::new(1));
2914        assert_eq!(builder.func.locals.len(), 2);
2915    }
2916
2917    #[test]
2918    fn test_new_block() {
2919        let mut builder = MirBuilder::new(
2920            FunctionId::new(0),
2921            Symbol::from_u32(0),
2922            Ty::Unit,
2923            Span::dummy(),
2924        );
2925
2926        let block_id = builder.new_block();
2927        assert_eq!(block_id, BasicBlockId::new(0));
2928        assert_eq!(builder.func.basic_blocks.len(), 1);
2929    }
2930}