1pub mod cfg;
15pub mod dominators;
16pub mod escape;
17pub mod loop_analysis;
18pub mod monomorph;
19pub mod nogc_verify;
20pub mod optimize;
21pub mod reduction;
22pub mod ssa;
23pub mod ssa_optimize;
24pub mod verify;
25
26use cjc_ast::{BinOp, UnaryOp, Visibility};
27pub use escape::AllocHint;
28
29#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
34pub struct MirFnId(pub u32);
35
36#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
37pub struct BlockId(pub u32);
38
39#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
40pub struct TempId(pub u32);
41
42#[derive(Debug, Clone)]
48pub struct MirProgram {
49 pub functions: Vec<MirFunction>,
50 pub struct_defs: Vec<MirStructDef>,
51 pub enum_defs: Vec<MirEnumDef>,
52 pub entry: MirFnId,
55}
56
57#[derive(Debug, Clone)]
58pub struct MirStructDef {
59 pub name: String,
60 pub fields: Vec<(String, String)>, pub is_record: bool,
63 pub vis: Visibility,
64}
65
66#[derive(Debug, Clone)]
67pub struct MirEnumDef {
68 pub name: String,
69 pub variants: Vec<MirVariantDef>,
70}
71
72#[derive(Debug, Clone)]
73pub struct MirVariantDef {
74 pub name: String,
75 pub fields: Vec<String>, }
77
78#[derive(Debug, Clone)]
83pub struct MirFunction {
84 pub id: MirFnId,
85 pub name: String,
86 pub type_params: Vec<(String, Vec<String>)>, pub params: Vec<MirParam>,
88 pub return_type: Option<String>,
89 pub body: MirBody,
90 pub is_nogc: bool,
91 pub cfg_body: Option<cfg::MirCfg>,
95 pub decorators: Vec<String>,
97 pub vis: Visibility,
98}
99
100#[derive(Debug, Clone)]
101pub struct MirParam {
102 pub name: String,
103 pub ty_name: String,
104 pub default: Option<MirExpr>,
106 pub is_variadic: bool,
108}
109
110impl MirFunction {
111 pub fn build_cfg(&mut self) {
114 let cfg = cfg::CfgBuilder::build(&self.body);
115 self.cfg_body = Some(cfg);
116 }
117
118 pub fn cfg(&mut self) -> &cfg::MirCfg {
120 if self.cfg_body.is_none() {
121 self.build_cfg();
122 }
123 self.cfg_body.as_ref().unwrap()
124 }
125}
126
127impl MirProgram {
128 pub fn build_all_cfgs(&mut self) {
130 for func in &mut self.functions {
131 func.build_cfg();
132 }
133 }
134}
135
136#[derive(Debug, Clone)]
141pub struct MirBody {
142 pub stmts: Vec<MirStmt>,
143 pub result: Option<Box<MirExpr>>,
144}
145
146#[derive(Debug, Clone)]
151pub enum MirStmt {
152 Let {
153 name: String,
154 mutable: bool,
155 init: MirExpr,
156 alloc_hint: Option<AllocHint>,
158 },
159 Expr(MirExpr),
160 If {
161 cond: MirExpr,
162 then_body: MirBody,
163 else_body: Option<MirBody>,
164 },
165 While {
166 cond: MirExpr,
167 body: MirBody,
168 },
169 Return(Option<MirExpr>),
170 Break,
171 Continue,
172 NoGcBlock(MirBody),
173}
174
175#[derive(Debug, Clone)]
180pub struct MirExpr {
181 pub kind: MirExprKind,
182}
183
184#[derive(Debug, Clone)]
185pub enum MirExprKind {
186 IntLit(i64),
187 FloatLit(f64),
188 BoolLit(bool),
189 StringLit(String),
190 ByteStringLit(Vec<u8>),
191 ByteCharLit(u8),
192 RawStringLit(String),
193 RawByteStringLit(Vec<u8>),
194 RegexLit { pattern: String, flags: String },
195 TensorLit { rows: Vec<Vec<MirExpr>> },
196 Var(String),
197 Binary {
198 op: BinOp,
199 left: Box<MirExpr>,
200 right: Box<MirExpr>,
201 },
202 Unary {
203 op: UnaryOp,
204 operand: Box<MirExpr>,
205 },
206 Call {
207 callee: Box<MirExpr>,
208 args: Vec<MirExpr>,
209 },
210 Field {
211 object: Box<MirExpr>,
212 name: String,
213 },
214 Index {
215 object: Box<MirExpr>,
216 index: Box<MirExpr>,
217 },
218 MultiIndex {
219 object: Box<MirExpr>,
220 indices: Vec<MirExpr>,
221 },
222 Assign {
223 target: Box<MirExpr>,
224 value: Box<MirExpr>,
225 },
226 Block(MirBody),
227 StructLit {
228 name: String,
229 fields: Vec<(String, MirExpr)>,
230 },
231 ArrayLit(Vec<MirExpr>),
232 Col(String),
233 Lambda {
234 params: Vec<MirParam>,
235 body: Box<MirExpr>,
236 },
237 MakeClosure {
241 fn_name: String,
243 captures: Vec<MirExpr>,
247 },
248 If {
249 cond: Box<MirExpr>,
250 then_body: MirBody,
251 else_body: Option<MirBody>,
252 },
253 Match {
256 scrutinee: Box<MirExpr>,
257 arms: Vec<MirMatchArm>,
258 },
259 VariantLit {
261 enum_name: String,
262 variant: String,
263 fields: Vec<MirExpr>,
264 },
265 TupleLit(Vec<MirExpr>),
267 LinalgLU { operand: Box<MirExpr> },
269 LinalgQR { operand: Box<MirExpr> },
270 LinalgCholesky { operand: Box<MirExpr> },
271 LinalgInv { operand: Box<MirExpr> },
272 Broadcast {
274 operand: Box<MirExpr>,
275 target_shape: Vec<MirExpr>,
276 },
277 Void,
278}
279
280#[derive(Debug, Clone)]
286pub struct MirMatchArm {
287 pub pattern: MirPattern,
288 pub body: MirBody,
289}
290
291#[derive(Debug, Clone)]
293pub enum MirPattern {
294 Wildcard,
296 Binding(String),
298 LitInt(i64),
300 LitFloat(f64),
301 LitBool(bool),
302 LitString(String),
303 Tuple(Vec<MirPattern>),
305 Struct {
307 name: String,
308 fields: Vec<(String, MirPattern)>,
309 },
310 Variant {
312 enum_name: String,
313 variant: String,
314 fields: Vec<MirPattern>,
315 },
316}
317
318use cjc_hir::*;
323
324pub struct HirToMir {
326 next_fn_id: u32,
327 next_lambda_id: u32,
328 lifted_functions: Vec<MirFunction>,
331}
332
333impl HirToMir {
334 pub fn new() -> Self {
335 Self {
336 next_fn_id: 0,
337 next_lambda_id: 0,
338 lifted_functions: Vec::new(),
339 }
340 }
341
342 fn fresh_fn_id(&mut self) -> MirFnId {
343 let id = MirFnId(self.next_fn_id);
344 self.next_fn_id += 1;
345 id
346 }
347
348 fn fresh_lambda_name(&mut self) -> String {
349 let name = format!("__closure_{}", self.next_lambda_id);
350 self.next_lambda_id += 1;
351 name
352 }
353
354 pub fn lower_program(&mut self, hir: &HirProgram) -> MirProgram {
356 let mut functions = Vec::new();
357 let mut struct_defs = Vec::new();
358 let mut enum_defs = Vec::new();
359 let mut main_stmts: Vec<MirStmt> = Vec::new();
360
361 for item in &hir.items {
362 match item {
363 HirItem::Fn(f) => {
364 functions.push(self.lower_fn(f));
365 }
366 HirItem::Struct(s) => {
367 struct_defs.push(MirStructDef {
368 name: s.name.clone(),
369 fields: s.fields.clone(),
370 is_record: false,
371 vis: s.vis,
372 });
373 }
374 HirItem::Class(c) => {
375 struct_defs.push(MirStructDef {
376 name: c.name.clone(),
377 fields: c.fields.clone(),
378 is_record: false,
379 vis: c.vis,
380 });
381 }
382 HirItem::Record(r) => {
383 struct_defs.push(MirStructDef {
384 name: r.name.clone(),
385 fields: r.fields.clone(),
386 is_record: true,
387 vis: r.vis,
388 });
389 }
390 HirItem::Enum(e) => {
391 enum_defs.push(MirEnumDef {
392 name: e.name.clone(),
393 variants: e
394 .variants
395 .iter()
396 .map(|v| MirVariantDef {
397 name: v.name.clone(),
398 fields: v.fields.clone(),
399 })
400 .collect(),
401 });
402 }
403 HirItem::Let(l) => {
404 main_stmts.push(MirStmt::Let {
405 name: l.name.clone(),
406 mutable: l.mutable,
407 init: self.lower_expr(&l.init),
408 alloc_hint: None,
409 });
410 }
411 HirItem::Stmt(s) => {
412 main_stmts.push(self.lower_stmt(s));
413 }
414 HirItem::Impl(i) => {
415 for method in &i.methods {
416 let mut mir_fn = self.lower_fn(method);
418 mir_fn.name = format!("{}.{}", i.target, method.name);
419 functions.push(mir_fn);
420 }
421 }
422 HirItem::Trait(_) => {
423 }
425 }
426 }
427
428 let main_id = self.fresh_fn_id();
430 functions.push(MirFunction {
431 id: main_id,
432 name: "__main".to_string(),
433 type_params: vec![],
434 params: vec![],
435 return_type: None,
436 body: MirBody {
437 stmts: main_stmts,
438 result: None,
439 },
440 is_nogc: false,
441 cfg_body: None,
442 decorators: vec![],
443 vis: Visibility::Private,
444 });
445
446 functions.append(&mut self.lifted_functions);
448
449 MirProgram {
450 functions,
451 struct_defs,
452 enum_defs,
453 entry: main_id,
454 }
455 }
456
457 pub fn lower_fn(&mut self, f: &HirFn) -> MirFunction {
458 let id = self.fresh_fn_id();
459 let params = f
460 .params
461 .iter()
462 .map(|p| MirParam {
463 name: p.name.clone(),
464 ty_name: p.ty_name.clone(),
465 default: p.default.as_ref().map(|d| self.lower_expr(d)),
466 is_variadic: p.is_variadic,
467 })
468 .collect();
469 let body = self.lower_block(&f.body);
470 MirFunction {
471 id,
472 name: f.name.clone(),
473 type_params: f.type_params.clone(),
474 params,
475 return_type: f.return_type.clone(),
476 body,
477 is_nogc: f.is_nogc,
478 cfg_body: None,
479 decorators: f.decorators.clone(),
480 vis: f.vis,
481 }
482 }
483
484 fn lower_block(&mut self, block: &HirBlock) -> MirBody {
485 let stmts = block.stmts.iter().map(|s| self.lower_stmt(s)).collect();
486 let result = block.expr.as_ref().map(|e| Box::new(self.lower_expr(e)));
487 MirBody { stmts, result }
488 }
489
490 fn lower_stmt(&mut self, stmt: &HirStmt) -> MirStmt {
491 match &stmt.kind {
492 HirStmtKind::Let {
493 name,
494 mutable,
495 init,
496 ..
497 } => MirStmt::Let {
498 name: name.clone(),
499 mutable: *mutable,
500 init: self.lower_expr(init),
501 alloc_hint: None,
502 },
503 HirStmtKind::Expr(e) => MirStmt::Expr(self.lower_expr(e)),
504 HirStmtKind::If(if_expr) => self.lower_if_stmt(if_expr),
505 HirStmtKind::While { cond, body } => MirStmt::While {
506 cond: self.lower_expr(cond),
507 body: self.lower_block(body),
508 },
509 HirStmtKind::Return(e) => {
510 MirStmt::Return(e.as_ref().map(|ex| self.lower_expr(ex)))
511 }
512 HirStmtKind::Break => MirStmt::Break,
513 HirStmtKind::Continue => MirStmt::Continue,
514 HirStmtKind::NoGcBlock(block) => MirStmt::NoGcBlock(self.lower_block(block)),
515 }
516 }
517
518 pub fn lower_if_stmt(&mut self, if_expr: &HirIfExpr) -> MirStmt {
519 let cond = self.lower_expr(&if_expr.cond);
520 let then_body = self.lower_block(&if_expr.then_block);
521 let else_body = if_expr.else_branch.as_ref().map(|eb| match eb {
522 HirElseBranch::ElseIf(elif) => {
523 let nested = self.lower_if_stmt(elif);
525 MirBody {
526 stmts: vec![nested],
527 result: None,
528 }
529 }
530 HirElseBranch::Else(block) => self.lower_block(block),
531 });
532 MirStmt::If {
533 cond,
534 then_body,
535 else_body,
536 }
537 }
538
539 pub fn lower_expr(&mut self, expr: &HirExpr) -> MirExpr {
540 let kind = match &expr.kind {
541 HirExprKind::IntLit(v) => MirExprKind::IntLit(*v),
542 HirExprKind::FloatLit(v) => MirExprKind::FloatLit(*v),
543 HirExprKind::BoolLit(b) => MirExprKind::BoolLit(*b),
544 HirExprKind::StringLit(s) => MirExprKind::StringLit(s.clone()),
545 HirExprKind::ByteStringLit(bytes) => MirExprKind::ByteStringLit(bytes.clone()),
546 HirExprKind::ByteCharLit(b) => MirExprKind::ByteCharLit(*b),
547 HirExprKind::RawStringLit(s) => MirExprKind::RawStringLit(s.clone()),
548 HirExprKind::RawByteStringLit(bytes) => MirExprKind::RawByteStringLit(bytes.clone()),
549 HirExprKind::RegexLit { pattern, flags } => MirExprKind::RegexLit { pattern: pattern.clone(), flags: flags.clone() },
550 HirExprKind::TensorLit { rows } => {
551 let mir_rows = rows.iter().map(|row| {
552 row.iter().map(|e| self.lower_expr(e)).collect()
553 }).collect();
554 MirExprKind::TensorLit { rows: mir_rows }
555 }
556 HirExprKind::Var(name) => MirExprKind::Var(name.clone()),
557 HirExprKind::Binary { op, left, right } => MirExprKind::Binary {
558 op: *op,
559 left: Box::new(self.lower_expr(left)),
560 right: Box::new(self.lower_expr(right)),
561 },
562 HirExprKind::Unary { op, operand } => MirExprKind::Unary {
563 op: *op,
564 operand: Box::new(self.lower_expr(operand)),
565 },
566 HirExprKind::Call { callee, args } => MirExprKind::Call {
567 callee: Box::new(self.lower_expr(callee)),
568 args: args.iter().map(|a| self.lower_expr(a)).collect(),
569 },
570 HirExprKind::Field { object, name } => MirExprKind::Field {
571 object: Box::new(self.lower_expr(object)),
572 name: name.clone(),
573 },
574 HirExprKind::Index { object, index } => MirExprKind::Index {
575 object: Box::new(self.lower_expr(object)),
576 index: Box::new(self.lower_expr(index)),
577 },
578 HirExprKind::MultiIndex { object, indices } => MirExprKind::MultiIndex {
579 object: Box::new(self.lower_expr(object)),
580 indices: indices.iter().map(|i| self.lower_expr(i)).collect(),
581 },
582 HirExprKind::Assign { target, value } => MirExprKind::Assign {
583 target: Box::new(self.lower_expr(target)),
584 value: Box::new(self.lower_expr(value)),
585 },
586 HirExprKind::Block(block) => MirExprKind::Block(self.lower_block(block)),
587 HirExprKind::StructLit { name, fields } => MirExprKind::StructLit {
588 name: name.clone(),
589 fields: fields
590 .iter()
591 .map(|(n, e)| (n.clone(), self.lower_expr(e)))
592 .collect(),
593 },
594 HirExprKind::ArrayLit(elems) => {
595 MirExprKind::ArrayLit(elems.iter().map(|e| self.lower_expr(e)).collect())
596 }
597 HirExprKind::Col(name) => MirExprKind::Col(name.clone()),
598 HirExprKind::Lambda { params, body } => MirExprKind::Lambda {
599 params: params
600 .iter()
601 .map(|p| MirParam {
602 name: p.name.clone(),
603 ty_name: p.ty_name.clone(),
604 default: p.default.as_ref().map(|d| self.lower_expr(d)),
605 is_variadic: p.is_variadic,
606 })
607 .collect(),
608 body: Box::new(self.lower_expr(body)),
609 },
610 HirExprKind::Closure {
611 params,
612 body,
613 captures,
614 } => {
615 let lifted_name = self.fresh_lambda_name();
618 let lifted_id = self.fresh_fn_id();
619
620 let mut lifted_params: Vec<MirParam> = captures
622 .iter()
623 .map(|c| MirParam {
624 name: c.name.clone(),
625 ty_name: "any".to_string(), default: None,
627 is_variadic: false,
628 })
629 .collect();
630 for p in params {
631 lifted_params.push(MirParam {
632 name: p.name.clone(),
633 ty_name: p.ty_name.clone(),
634 default: p.default.as_ref().map(|d| self.lower_expr(d)),
635 is_variadic: p.is_variadic,
636 });
637 }
638
639 let lifted_body = MirBody {
640 stmts: vec![],
641 result: Some(Box::new(self.lower_expr(body))),
642 };
643
644 self.lifted_functions.push(MirFunction {
645 id: lifted_id,
646 name: lifted_name.clone(),
647 type_params: vec![],
648 params: lifted_params,
649 return_type: None,
650 body: lifted_body,
651 is_nogc: false,
652 cfg_body: None,
653 decorators: vec![],
654 vis: Visibility::Private,
655 });
656
657 let capture_exprs: Vec<MirExpr> = captures
660 .iter()
661 .map(|c| MirExpr {
662 kind: MirExprKind::Var(c.name.clone()),
663 })
664 .collect();
665
666 MirExprKind::MakeClosure {
667 fn_name: lifted_name,
668 captures: capture_exprs,
669 }
670 }
671 HirExprKind::Match { scrutinee, arms } => {
672 let mir_scrutinee = Box::new(self.lower_expr(scrutinee));
673 let mir_arms = arms
674 .iter()
675 .map(|arm| {
676 let pattern = self.lower_pattern(&arm.pattern);
677 let body = MirBody {
678 stmts: vec![],
679 result: Some(Box::new(self.lower_expr(&arm.body))),
680 };
681 MirMatchArm { pattern, body }
682 })
683 .collect();
684 MirExprKind::Match {
685 scrutinee: mir_scrutinee,
686 arms: mir_arms,
687 }
688 }
689 HirExprKind::TupleLit(elems) => {
690 MirExprKind::TupleLit(elems.iter().map(|e| self.lower_expr(e)).collect())
691 }
692 HirExprKind::VariantLit {
693 enum_name,
694 variant,
695 fields,
696 } => MirExprKind::VariantLit {
697 enum_name: enum_name.clone(),
698 variant: variant.clone(),
699 fields: fields.iter().map(|f| self.lower_expr(f)).collect(),
700 },
701 HirExprKind::If { cond, then_block, else_branch } => {
702 let mir_cond = Box::new(self.lower_expr(cond));
703 let mir_then = self.lower_block(then_block);
704 let mir_else = else_branch.as_ref().map(|eb| match eb {
705 HirElseBranch::ElseIf(elif) => {
706 let nested = self.lower_if_stmt(elif);
708 MirBody {
709 stmts: vec![nested],
710 result: None,
711 }
712 }
713 HirElseBranch::Else(block) => self.lower_block(block),
714 });
715 MirExprKind::If {
716 cond: mir_cond,
717 then_body: mir_then,
718 else_body: mir_else,
719 }
720 }
721 HirExprKind::Void => MirExprKind::Void,
722 };
723 MirExpr { kind }
724 }
725
726 fn lower_pattern(&self, pat: &HirPattern) -> MirPattern {
727 match &pat.kind {
728 HirPatternKind::Wildcard => MirPattern::Wildcard,
729 HirPatternKind::Binding(name) => MirPattern::Binding(name.clone()),
730 HirPatternKind::LitInt(v) => MirPattern::LitInt(*v),
731 HirPatternKind::LitFloat(v) => MirPattern::LitFloat(*v),
732 HirPatternKind::LitBool(b) => MirPattern::LitBool(*b),
733 HirPatternKind::LitString(s) => MirPattern::LitString(s.clone()),
734 HirPatternKind::Tuple(pats) => {
735 MirPattern::Tuple(pats.iter().map(|p| self.lower_pattern(p)).collect())
736 }
737 HirPatternKind::Struct { name, fields } => MirPattern::Struct {
738 name: name.clone(),
739 fields: fields
740 .iter()
741 .map(|f| (f.name.clone(), self.lower_pattern(&f.pattern)))
742 .collect(),
743 },
744 HirPatternKind::Variant {
745 enum_name,
746 variant,
747 fields,
748 } => MirPattern::Variant {
749 enum_name: enum_name.clone(),
750 variant: variant.clone(),
751 fields: fields.iter().map(|f| self.lower_pattern(f)).collect(),
752 },
753 }
754 }
755}
756
757impl Default for HirToMir {
758 fn default() -> Self {
759 Self::new()
760 }
761}
762
763#[cfg(test)]
768mod tests {
769 use super::*;
770 use cjc_hir::*;
771
772 fn hir_id(n: u32) -> HirId {
773 HirId(n)
774 }
775
776 fn hir_int(v: i64) -> HirExpr {
777 HirExpr {
778 kind: HirExprKind::IntLit(v),
779 hir_id: hir_id(0),
780 }
781 }
782
783 fn hir_var(name: &str) -> HirExpr {
784 HirExpr {
785 kind: HirExprKind::Var(name.to_string()),
786 hir_id: hir_id(0),
787 }
788 }
789
790 #[test]
791 fn test_lower_hir_literal() {
792 let mut lowering = HirToMir::new();
793 let hir = hir_int(42);
794 let mir = lowering.lower_expr(&hir);
795 assert!(matches!(mir.kind, MirExprKind::IntLit(42)));
796 }
797
798 #[test]
799 fn test_lower_hir_binary() {
800 let mut lowering = HirToMir::new();
801 let hir = HirExpr {
802 kind: HirExprKind::Binary {
803 op: BinOp::Add,
804 left: Box::new(hir_int(1)),
805 right: Box::new(hir_int(2)),
806 },
807 hir_id: hir_id(0),
808 };
809 let mir = lowering.lower_expr(&hir);
810 match &mir.kind {
811 MirExprKind::Binary { op, .. } => assert_eq!(*op, BinOp::Add),
812 _ => panic!("expected Binary"),
813 }
814 }
815
816 #[test]
817 fn test_lower_hir_fn() {
818 let mut lowering = HirToMir::new();
819 let hir_fn = HirFn {
820 name: "add".to_string(),
821 type_params: vec![],
822 params: vec![
823 HirParam {
824 name: "a".to_string(),
825 ty_name: "i64".to_string(),
826 default: None,
827 is_variadic: false,
828 hir_id: hir_id(1),
829 },
830 HirParam {
831 name: "b".to_string(),
832 ty_name: "i64".to_string(),
833 default: None,
834 is_variadic: false,
835 hir_id: hir_id(2),
836 },
837 ],
838 return_type: Some("i64".to_string()),
839 body: HirBlock {
840 stmts: vec![],
841 expr: Some(Box::new(HirExpr {
842 kind: HirExprKind::Binary {
843 op: BinOp::Add,
844 left: Box::new(hir_var("a")),
845 right: Box::new(hir_var("b")),
846 },
847 hir_id: hir_id(3),
848 })),
849 hir_id: hir_id(4),
850 },
851 is_nogc: false,
852 hir_id: hir_id(5),
853 decorators: vec![],
854 vis: cjc_ast::Visibility::Private,
855 };
856 let mir_fn = lowering.lower_fn(&hir_fn);
857 assert_eq!(mir_fn.name, "add");
858 assert_eq!(mir_fn.params.len(), 2);
859 assert!(mir_fn.body.result.is_some());
860 }
861
862 #[test]
863 fn test_lower_hir_program_entry() {
864 let mut lowering = HirToMir::new();
865 let hir = HirProgram {
866 items: vec![
867 HirItem::Let(HirLetDecl {
868 name: "x".to_string(),
869 mutable: false,
870 ty_name: None,
871 init: hir_int(42),
872 hir_id: hir_id(0),
873 }),
874 HirItem::Fn(HirFn {
875 name: "f".to_string(),
876 type_params: vec![],
877 params: vec![],
878 return_type: None,
879 body: HirBlock {
880 stmts: vec![],
881 expr: Some(Box::new(hir_var("x"))),
882 hir_id: hir_id(1),
883 },
884 is_nogc: false,
885 hir_id: hir_id(2),
886 decorators: vec![],
887 vis: cjc_ast::Visibility::Private,
888 }),
889 ],
890 };
891 let mir = lowering.lower_program(&hir);
892 assert_eq!(mir.functions.len(), 2);
894 let main = mir.functions.iter().find(|f| f.name == "__main").unwrap();
895 assert_eq!(main.body.stmts.len(), 1); assert_eq!(mir.entry, main.id);
897 }
898
899 #[test]
900 fn test_lower_hir_if_stmt() {
901 let mut lowering = HirToMir::new();
902 let hir_if = HirIfExpr {
903 cond: Box::new(HirExpr {
904 kind: HirExprKind::BoolLit(true),
905 hir_id: hir_id(0),
906 }),
907 then_block: HirBlock {
908 stmts: vec![],
909 expr: Some(Box::new(hir_int(1))),
910 hir_id: hir_id(1),
911 },
912 else_branch: Some(HirElseBranch::Else(HirBlock {
913 stmts: vec![],
914 expr: Some(Box::new(hir_int(2))),
915 hir_id: hir_id(2),
916 })),
917 hir_id: hir_id(3),
918 };
919 let mir_stmt = lowering.lower_if_stmt(&hir_if);
920 match &mir_stmt {
921 MirStmt::If {
922 then_body,
923 else_body,
924 ..
925 } => {
926 assert!(then_body.result.is_some());
927 assert!(else_body.is_some());
928 }
929 _ => panic!("expected If"),
930 }
931 }
932
933 #[test]
934 fn test_lower_struct_def() {
935 let mut lowering = HirToMir::new();
936 let hir = HirProgram {
937 items: vec![HirItem::Struct(HirStructDef {
938 name: "Point".to_string(),
939 fields: vec![
940 ("x".to_string(), "f64".to_string()),
941 ("y".to_string(), "f64".to_string()),
942 ],
943 hir_id: hir_id(0),
944 vis: cjc_ast::Visibility::Private,
945 })],
946 };
947 let mir = lowering.lower_program(&hir);
948 assert_eq!(mir.struct_defs.len(), 1);
949 assert_eq!(mir.struct_defs[0].name, "Point");
950 assert_eq!(mir.struct_defs[0].fields.len(), 2);
951 }
952}