1use 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
20struct LoopContext {
22 header: BasicBlockId,
24 exit: BasicBlockId,
26 break_destination: Option<Place>,
28}
29
30pub struct MirBuilder {
32 func: FunctionMIR,
34 current_block: Option<BasicBlockId>,
36 hir_to_local: HashMap<HirId, Local>,
38 _local_counter: u32,
40 _incomplete_blocks: Vec<BasicBlockId>,
42 loop_stack: Vec<LoopContext>,
44 variant_index_map: HashMap<(String, String), u128>,
46 pending_closures: Vec<FunctionMIR>,
48}
49
50impl MirBuilder {
51 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, _incomplete_blocks: Vec::new(),
59 loop_stack: Vec::new(),
60 variant_index_map: HashMap::new(),
61 pending_closures: Vec::new(),
62 }
63 }
64
65 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 pub fn build_function(hir_func: &hir::Function) -> (FunctionMIR, Vec<FunctionMIR>) {
87 Self::build_function_with_enums(hir_func, &HashMap::new())
88 }
89
90 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 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 builder.func.locals[0].ty = return_ty;
111
112 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 if let hir::Pattern::Ident { id, .. } = ¶m.pattern {
119 builder.hir_to_local.insert(*id, local);
120 }
121 }
122
123 let entry = builder.new_block();
125 builder.current_block = Some(entry);
126
127 builder.build_block_expr(&hir_func.body, Place::return_place());
129
130 builder.ensure_return();
132
133 let closures = builder.pending_closures;
134 (builder.func, closures)
135 }
136
137 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 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 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 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 fn push_statement(&mut self, stmt: Statement) {
166 self.current_block_mut().statements.push(stmt);
167 }
168
169 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 fn ensure_return(&mut self) {
178 if let Some(block_id) = self.current_block {
179 let terminator = Terminator::Return {
181 span: self.func.span,
182 };
183 self.func.block_mut(block_id).terminator = terminator;
184 }
185 }
186
187 fn build_block_expr(&mut self, block: &hir::Block, destination: Place) {
189 for stmt in &block.statements {
191 self.build_statement(stmt);
192 if self.current_block.is_none() {
194 return;
195 }
196 }
197
198 if let Some(expr) = &block.expr {
200 self.build_expr_into(expr, destination);
201 } else {
202 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 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 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 if let Some(init_expr) = init {
235 self.build_expr_into(init_expr, Place::from_local(tuple_local));
236 }
237
238 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 }
266 _ => {
267 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 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 self.push_statement(Statement::StorageLive { local, span: *span });
302
303 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 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 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 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 }
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 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 let target_place = self.build_place(target);
498 self.build_expr_into(value, target_place);
499
500 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 let func_name = if let hir::ExprKind::Var { name, .. } = &func.kind {
591 Some(*name)
592 } else {
593 None
594 };
595
596 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 let mut arg_operands = Vec::new();
602 for arg in args {
603 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 let next_block = self.new_block();
616
617 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 self.current_block = Some(next_block);
630 }
631
632 hir::ExprKind::Spawn { expr: spawn_expr } => {
634 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 let next_block = self.new_block();
640
641 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 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 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 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 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 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 self.current_block = Some(join_block);
692 }
693
694 hir::ExprKind::Channel {
695 element_ty,
696 capacity,
697 } => {
698 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 let cap_value = 16u64; 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 hir::ExprKind::EnergyBudgetBlock { body, .. } => {
752 self.build_block_expr(body, destination);
753 }
754
755 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 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 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; self.set_terminator(Terminator::Abort { span: expr.span });
804
805 let unreachable_block = self.new_block();
809 self.current_block = Some(unreachable_block);
810 }
811
812 hir::ExprKind::Perform { effect_name, operation, args } => {
815 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 hir::ExprKind::Handle { body, .. } => {
850 self.build_block_expr(body, destination);
851 }
852
853 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 self.build_block_expr(body, destination);
862 }
863
864 hir::ExprKind::Build { body, .. } => {
868 self.build_block_expr(body, destination);
869 }
870
871 hir::ExprKind::MethodCall {
873 receiver,
874 method,
875 args,
876 } => {
877 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 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 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 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 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 hir::ExprKind::Match {
920 expr: scrutinee,
921 arms,
922 } => {
923 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 let post_scrutinee_block = self.current_block;
930
931 let join_block = self.new_block();
932
933 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 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 self.current_block = Some(*body_block);
950
951 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 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 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 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 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 let real_body_block = self.new_block();
1030
1031 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 let pat = self.hir_pattern_to_mir_pat(&arm.pattern);
1061 mir_arms.push((*body_block, pat));
1062 }
1063
1064 let switch_block = self.new_block();
1066
1067 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 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 let default_target = string_otherwise.unwrap_or(join_block);
1110 for (i, (arm_block, lit_str)) in string_arms.iter().enumerate() {
1111 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 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 let next_target = if i + 1 < string_arms.len() {
1146 self.new_block()
1147 } else {
1148 default_target
1149 };
1150
1151 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 if i + 1 < string_arms.len() {
1160 self.current_block = Some(next_target);
1161 }
1162 }
1163 } else {
1164 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 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 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 for (i, (arm_block, lo, hi, inclusive)) in
1216 range_arms.iter().enumerate()
1217 {
1218 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 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 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 self.set_terminator(Terminator::Goto {
1327 target: otherwise,
1328 span: expr.span,
1329 });
1330 } else if has_variant_arms {
1331 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 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 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_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 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 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 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 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 } self.current_block = Some(join_block);
1500 }
1501
1502 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 self.set_terminator(Terminator::Goto {
1510 target: cond_block,
1511 span: expr.span,
1512 });
1513
1514 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 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 if self.current_block.is_some() {
1540 self.set_terminator(Terminator::Goto {
1541 target: cond_block,
1542 span: expr.span,
1543 });
1544 }
1545
1546 self.current_block = Some(exit_block);
1548
1549 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 hir::ExprKind::Loop { body } => {
1562 let loop_block = self.new_block();
1563 let exit_block = self.new_block();
1564
1565 self.set_terminator(Terminator::Goto {
1567 target: loop_block,
1568 span: expr.span,
1569 });
1570
1571 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 if self.current_block.is_some() {
1586 self.set_terminator(Terminator::Goto {
1587 target: loop_block,
1588 span: expr.span,
1589 });
1590 }
1591
1592 self.current_block = Some(exit_block);
1594 }
1595
1596 hir::ExprKind::For {
1598 pattern,
1599 iter,
1600 body,
1601 } => {
1602 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 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) } else if name_str == "RangeInclusive" {
1617 Some(true) } else {
1619 None
1620 }
1621 }
1622 _ => None,
1623 };
1624
1625 if let Some(inclusive) = range_info {
1626 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 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 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 self.set_terminator(Terminator::Goto {
1677 target: cond_block,
1678 span: expr.span,
1679 });
1680
1681 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 self.current_block = Some(body_block);
1707 self.loop_stack.push(LoopContext {
1710 header: increment_block,
1711 exit: exit_block,
1712 break_destination: None,
1713 });
1714
1715 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 if self.current_block.is_some() {
1738 self.set_terminator(Terminator::Goto {
1739 target: increment_block,
1740 span: expr.span,
1741 });
1742 }
1743
1744 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 self.current_block = Some(exit_block);
1769
1770 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 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 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 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 self.set_terminator(Terminator::Goto {
1822 target: cond_block,
1823 span: expr.span,
1824 });
1825
1826 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 self.current_block = Some(body_block);
1847 self.loop_stack.push(LoopContext {
1849 header: increment_block,
1850 exit: exit_block,
1851 break_destination: None,
1852 });
1853
1854 if let hir::Pattern::Ident { id, ty, .. } = pattern {
1856 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 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 if self.current_block.is_some() {
1888 self.set_terminator(Terminator::Goto {
1889 target: increment_block,
1890 span: expr.span,
1891 });
1892 }
1893
1894 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 self.current_block = Some(exit_block);
1919
1920 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 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 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 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 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 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 let all_single = indices
2000 .iter()
2001 .all(|ic| matches!(ic, hir::HirIndexComponent::Single(_)));
2002
2003 let ndarray_prefix = ndarray_type_prefix(&inner_ty);
2005
2006 if all_single {
2007 let func_name = format!("{ndarray_prefix}_get");
2009 let func_sym = joule_common::Symbol::intern(&func_name);
2010
2011 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 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 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 hir::ExprKind::Await { expr: inner } => {
2094 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 let next_block = self.new_block();
2100
2101 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 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 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 };
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 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 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 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 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 closure_builder.func.locals[0].ty = closure_return_ty;
2244
2245 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 for param in params {
2263 let param_ty = Ty::from_hir(¶m.ty);
2264 let param_name = if let hir::Pattern::Ident { name, .. } = ¶m.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, .. } = ¶m.pattern {
2274 closure_builder.hir_to_local.insert(*id, local);
2275 }
2276 }
2277
2278 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 let entry = closure_builder.new_block();
2292 closure_builder.current_block = Some(entry);
2293
2294 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 closure_builder.build_expr_into(body, Place::return_place());
2315 closure_builder.ensure_return();
2316
2317 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 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 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 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 ChannelOp::Timeout {
2368 duration_ns: 1_000_000_000, }
2370 }
2371 };
2372
2373 mir_arms.push(SelectArm {
2374 operation,
2375 target: arm_block,
2376 });
2377 }
2378
2379 let selected_arm = self.new_temp(Ty::Uint(UintTy::U32), span);
2381
2382 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 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 self.current_block = Some(join_block);
2405 }
2406
2407 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 let cond_temp = self.new_temp(Ty::Bool, condition.span);
2418 self.build_expr_into(condition, Place::from_local(cond_temp));
2419
2420 let then_bb = self.new_block();
2422 let else_bb = self.new_block();
2423 let join_bb = self.new_block();
2424
2425 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 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 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 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 self.current_block = Some(join_bb);
2466 }
2467
2468 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 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 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 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 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 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 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 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 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 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, }
2639 }
2640}
2641
2642fn get_impl_type_prefix(ty: &hir::Ty) -> String {
2644 match ty {
2645 hir::Ty::Named { name, .. } => {
2646 let s = name.as_str();
2647 if s == "Self" {
2649 "Self".to_string() } 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(), 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 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(), 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
2745fn 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
2778fn 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
2801pub fn build_mir(hir: &hir::Hir) -> crate::MirContext {
2803 let mut ctx = crate::MirContext::new();
2804
2805 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 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 let type_prefix = get_impl_type_prefix(&imp.ty);
2845
2846 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 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 hir::Item::Effect(_) | hir::Item::Supervisor(_) => {}
2874 }
2875 }
2876
2877 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); 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}