1use gruel_air::{
7 Air, AirArgMode, AirInstData, AirPattern, AirPlaceBase, AirPlaceRef, AirProjection, AirRef,
8 AnalyzedFunction, Type, TypeInternPool,
9};
10use gruel_error::{CompileWarning, WarningKind};
11
12use crate::CfgOutput;
13use crate::inst::{
14 BlockId, Cfg, CfgArgMode, CfgCallArg, CfgInst, CfgInstData, CfgValue, Place, PlaceBase,
15 Projection, Terminator,
16};
17
18type TracedPlace = Option<(PlaceBase, Vec<(Projection, Option<CfgValue>)>)>;
20
21struct ExprResult {
23 value: Option<CfgValue>,
25 continuation: Continuation,
27}
28
29enum Continuation {
31 Continues,
33 Diverged,
35}
36
37struct LoopContext {
39 header: BlockId,
41 exit: BlockId,
43 scope_depth: usize,
48}
49
50#[derive(Debug, Clone)]
53struct LiveSlot {
54 slot: u32,
56 ty: Type,
58 span: gruel_span::Span,
60}
61
62#[derive(Debug, Clone)]
64struct LiveParam {
65 param_slot: u32,
67 ty: Type,
69}
70
71pub struct CfgBuilder<'a> {
73 air: &'a Air,
74 cfg: Cfg,
75 type_pool: &'a TypeInternPool,
77 current_block: BlockId,
79 loop_stack: Vec<LoopContext>,
81 value_cache: Vec<Option<CfgValue>>,
83 warnings: Vec<CompileWarning>,
85 scope_stack: Vec<Vec<LiveSlot>>,
89 live_params: Vec<LiveParam>,
94}
95
96impl<'a> CfgBuilder<'a> {
97 pub fn build(func: &'a AnalyzedFunction, type_pool: &'a TypeInternPool) -> CfgOutput {
102 let live_params: Vec<LiveParam> = if func.is_destructor {
107 Vec::new()
108 } else {
109 func.param_slot_types
110 .iter()
111 .enumerate()
112 .filter(|(i, ty)| {
113 !func.param_modes.get(*i).copied().unwrap_or(false)
114 && crate::drop_names::type_needs_drop(**ty, type_pool)
115 })
116 .map(|(i, ty)| LiveParam {
117 param_slot: i as u32,
118 ty: *ty,
119 })
120 .collect()
121 };
122
123 let mut builder = CfgBuilder {
124 air: &func.air,
125 cfg: Cfg::new(
126 func.air.return_type(),
127 func.num_locals,
128 func.num_param_slots,
129 func.name.to_string(),
130 func.param_modes.clone(),
131 func.param_slot_types.clone(),
132 ),
133 type_pool,
134 current_block: BlockId(0),
135 loop_stack: Vec::new(),
136 value_cache: vec![None; func.air.len()],
137 warnings: Vec::new(),
138 scope_stack: vec![Vec::new()], live_params,
140 };
141
142 builder.current_block = builder.cfg.new_block();
144 builder.cfg.entry = builder.current_block;
145
146 if !func.air.is_empty() {
148 let root = AirRef::from_raw((func.air.len() - 1) as u32);
149 builder.lower_inst(root);
150 }
151
152 builder.cfg.compute_predecessors();
154
155 CfgOutput {
156 cfg: builder.cfg,
157 warnings: builder.warnings,
158 }
159 }
160
161 fn lower_inst(&mut self, air_ref: AirRef) -> ExprResult {
163 if let Some(cached) = self.value_cache[air_ref.as_u32() as usize] {
165 return ExprResult {
166 value: Some(cached),
167 continuation: Continuation::Continues,
168 };
169 }
170
171 let inst = self.air.get(air_ref);
172 let span = inst.span;
173 let ty = inst.ty;
174
175 match &inst.data {
176 AirInstData::Const(v) => {
177 let value = self.emit(CfgInstData::Const(*v), ty, span);
178 self.cache(air_ref, value);
179 ExprResult {
180 value: Some(value),
181 continuation: Continuation::Continues,
182 }
183 }
184
185 AirInstData::FloatConst(bits) => {
186 let value = self.emit(CfgInstData::FloatConst(*bits), ty, span);
187 self.cache(air_ref, value);
188 ExprResult {
189 value: Some(value),
190 continuation: Continuation::Continues,
191 }
192 }
193
194 AirInstData::BoolConst(v) => {
195 let value = self.emit(CfgInstData::BoolConst(*v), ty, span);
196 self.cache(air_ref, value);
197 ExprResult {
198 value: Some(value),
199 continuation: Continuation::Continues,
200 }
201 }
202
203 AirInstData::StringConst(string_id) => {
204 let value = self.emit(CfgInstData::StringConst(*string_id), ty, span);
205 self.cache(air_ref, value);
206 ExprResult {
207 value: Some(value),
208 continuation: Continuation::Continues,
209 }
210 }
211
212 AirInstData::UnitConst => {
213 let value = self.emit(CfgInstData::Const(0), ty, span);
217 self.cache(air_ref, value);
218 ExprResult {
219 value: Some(value),
220 continuation: Continuation::Continues,
221 }
222 }
223
224 AirInstData::TypeConst(_) => {
225 ExprResult {
234 value: None,
235 continuation: Continuation::Continues,
236 }
237 }
238
239 AirInstData::CallGeneric { .. } => {
240 panic!(
247 "CallGeneric instruction reached CFG building - this is a compiler bug. \
248 CallGeneric must be specialized to regular Call before codegen."
249 );
250 }
251
252 AirInstData::Param { index } => {
253 let value = self.emit(CfgInstData::Param { index: *index }, ty, span);
254 self.cache(air_ref, value);
255 ExprResult {
256 value: Some(value),
257 continuation: Continuation::Continues,
258 }
259 }
260
261 AirInstData::Add(lhs, rhs) => {
262 let Some(lhs_val) = self.lower_value(*lhs) else {
263 return Self::diverged();
264 };
265 let Some(rhs_val) = self.lower_value(*rhs) else {
266 return Self::diverged();
267 };
268 let value = self.emit(CfgInstData::Add(lhs_val, rhs_val), ty, span);
269 self.cache(air_ref, value);
270 ExprResult {
271 value: Some(value),
272 continuation: Continuation::Continues,
273 }
274 }
275
276 AirInstData::Sub(lhs, rhs) => {
277 let Some(lhs_val) = self.lower_value(*lhs) else {
278 return Self::diverged();
279 };
280 let Some(rhs_val) = self.lower_value(*rhs) else {
281 return Self::diverged();
282 };
283 let value = self.emit(CfgInstData::Sub(lhs_val, rhs_val), ty, span);
284 self.cache(air_ref, value);
285 ExprResult {
286 value: Some(value),
287 continuation: Continuation::Continues,
288 }
289 }
290
291 AirInstData::Mul(lhs, rhs) => {
292 let Some(lhs_val) = self.lower_value(*lhs) else {
293 return Self::diverged();
294 };
295 let Some(rhs_val) = self.lower_value(*rhs) else {
296 return Self::diverged();
297 };
298 let value = self.emit(CfgInstData::Mul(lhs_val, rhs_val), ty, span);
299 self.cache(air_ref, value);
300 ExprResult {
301 value: Some(value),
302 continuation: Continuation::Continues,
303 }
304 }
305
306 AirInstData::Div(lhs, rhs) => {
307 let Some(lhs_val) = self.lower_value(*lhs) else {
308 return Self::diverged();
309 };
310 let Some(rhs_val) = self.lower_value(*rhs) else {
311 return Self::diverged();
312 };
313 let value = self.emit(CfgInstData::Div(lhs_val, rhs_val), ty, span);
314 self.cache(air_ref, value);
315 ExprResult {
316 value: Some(value),
317 continuation: Continuation::Continues,
318 }
319 }
320
321 AirInstData::Mod(lhs, rhs) => {
322 let Some(lhs_val) = self.lower_value(*lhs) else {
323 return Self::diverged();
324 };
325 let Some(rhs_val) = self.lower_value(*rhs) else {
326 return Self::diverged();
327 };
328 let value = self.emit(CfgInstData::Mod(lhs_val, rhs_val), ty, span);
329 self.cache(air_ref, value);
330 ExprResult {
331 value: Some(value),
332 continuation: Continuation::Continues,
333 }
334 }
335
336 AirInstData::Eq(lhs, rhs) => {
337 let Some(lhs_val) = self.lower_value(*lhs) else {
338 return Self::diverged();
339 };
340 let Some(rhs_val) = self.lower_value(*rhs) else {
341 return Self::diverged();
342 };
343 let value = self.emit(CfgInstData::Eq(lhs_val, rhs_val), ty, span);
344 self.cache(air_ref, value);
345 ExprResult {
346 value: Some(value),
347 continuation: Continuation::Continues,
348 }
349 }
350
351 AirInstData::Ne(lhs, rhs) => {
352 let Some(lhs_val) = self.lower_value(*lhs) else {
353 return Self::diverged();
354 };
355 let Some(rhs_val) = self.lower_value(*rhs) else {
356 return Self::diverged();
357 };
358 let value = self.emit(CfgInstData::Ne(lhs_val, rhs_val), ty, span);
359 self.cache(air_ref, value);
360 ExprResult {
361 value: Some(value),
362 continuation: Continuation::Continues,
363 }
364 }
365
366 AirInstData::Lt(lhs, rhs) => {
367 let Some(lhs_val) = self.lower_value(*lhs) else {
368 return Self::diverged();
369 };
370 let Some(rhs_val) = self.lower_value(*rhs) else {
371 return Self::diverged();
372 };
373 let value = self.emit(CfgInstData::Lt(lhs_val, rhs_val), ty, span);
374 self.cache(air_ref, value);
375 ExprResult {
376 value: Some(value),
377 continuation: Continuation::Continues,
378 }
379 }
380
381 AirInstData::Gt(lhs, rhs) => {
382 let Some(lhs_val) = self.lower_value(*lhs) else {
383 return Self::diverged();
384 };
385 let Some(rhs_val) = self.lower_value(*rhs) else {
386 return Self::diverged();
387 };
388 let value = self.emit(CfgInstData::Gt(lhs_val, rhs_val), ty, span);
389 self.cache(air_ref, value);
390 ExprResult {
391 value: Some(value),
392 continuation: Continuation::Continues,
393 }
394 }
395
396 AirInstData::Le(lhs, rhs) => {
397 let Some(lhs_val) = self.lower_value(*lhs) else {
398 return Self::diverged();
399 };
400 let Some(rhs_val) = self.lower_value(*rhs) else {
401 return Self::diverged();
402 };
403 let value = self.emit(CfgInstData::Le(lhs_val, rhs_val), ty, span);
404 self.cache(air_ref, value);
405 ExprResult {
406 value: Some(value),
407 continuation: Continuation::Continues,
408 }
409 }
410
411 AirInstData::Ge(lhs, rhs) => {
412 let Some(lhs_val) = self.lower_value(*lhs) else {
413 return Self::diverged();
414 };
415 let Some(rhs_val) = self.lower_value(*rhs) else {
416 return Self::diverged();
417 };
418 let value = self.emit(CfgInstData::Ge(lhs_val, rhs_val), ty, span);
419 self.cache(air_ref, value);
420 ExprResult {
421 value: Some(value),
422 continuation: Continuation::Continues,
423 }
424 }
425
426 AirInstData::And(lhs, rhs) => {
427 let Some(lhs_val) = self.lower_value(*lhs) else {
430 return Self::diverged();
431 };
432
433 let rhs_block = self.cfg.new_block();
434 let join_block = self.cfg.new_block();
435
436 let result_param = self.cfg.add_block_param(join_block, Type::BOOL);
438
439 let false_val = self.emit(CfgInstData::BoolConst(false), Type::BOOL, span);
441 let (then_args_start, then_args_len) = self.cfg.push_extra(std::iter::empty());
442 let (else_args_start, else_args_len) =
443 self.cfg.push_extra(std::iter::once(false_val));
444 self.cfg.set_terminator(
445 self.current_block,
446 Terminator::Branch {
447 cond: lhs_val,
448 then_block: rhs_block,
449 then_args_start,
450 then_args_len,
451 else_block: join_block,
452 else_args_start,
453 else_args_len,
454 },
455 );
456
457 self.current_block = rhs_block;
459 let Some(rhs_val) = self.lower_value(*rhs) else {
460 return Self::diverged();
461 };
462 let (args_start, args_len) = self.cfg.push_extra(std::iter::once(rhs_val));
463 self.cfg.set_terminator(
464 self.current_block,
465 Terminator::Goto {
466 target: join_block,
467 args_start,
468 args_len,
469 },
470 );
471
472 self.current_block = join_block;
474 self.cache(air_ref, result_param);
475 ExprResult {
476 value: Some(result_param),
477 continuation: Continuation::Continues,
478 }
479 }
480
481 AirInstData::Or(lhs, rhs) => {
482 let Some(lhs_val) = self.lower_value(*lhs) else {
484 return Self::diverged();
485 };
486
487 let rhs_block = self.cfg.new_block();
488 let join_block = self.cfg.new_block();
489
490 let result_param = self.cfg.add_block_param(join_block, Type::BOOL);
492
493 let true_val = self.emit(CfgInstData::BoolConst(true), Type::BOOL, span);
495 let (then_args_start, then_args_len) =
496 self.cfg.push_extra(std::iter::once(true_val));
497 let (else_args_start, else_args_len) = self.cfg.push_extra(std::iter::empty());
498 self.cfg.set_terminator(
499 self.current_block,
500 Terminator::Branch {
501 cond: lhs_val,
502 then_block: join_block,
503 then_args_start,
504 then_args_len,
505 else_block: rhs_block,
506 else_args_start,
507 else_args_len,
508 },
509 );
510
511 self.current_block = rhs_block;
513 let Some(rhs_val) = self.lower_value(*rhs) else {
514 return Self::diverged();
515 };
516 let (args_start, args_len) = self.cfg.push_extra(std::iter::once(rhs_val));
517 self.cfg.set_terminator(
518 self.current_block,
519 Terminator::Goto {
520 target: join_block,
521 args_start,
522 args_len,
523 },
524 );
525
526 self.current_block = join_block;
528 self.cache(air_ref, result_param);
529 ExprResult {
530 value: Some(result_param),
531 continuation: Continuation::Continues,
532 }
533 }
534
535 AirInstData::Neg(operand) => {
536 let Some(op_val) = self.lower_value(*operand) else {
537 return Self::diverged();
538 };
539 let value = self.emit(CfgInstData::Neg(op_val), ty, span);
540 self.cache(air_ref, value);
541 ExprResult {
542 value: Some(value),
543 continuation: Continuation::Continues,
544 }
545 }
546
547 AirInstData::Not(operand) => {
548 let Some(op_val) = self.lower_value(*operand) else {
549 return Self::diverged();
550 };
551 let value = self.emit(CfgInstData::Not(op_val), ty, span);
552 self.cache(air_ref, value);
553 ExprResult {
554 value: Some(value),
555 continuation: Continuation::Continues,
556 }
557 }
558
559 AirInstData::BitNot(operand) => {
560 let Some(op_val) = self.lower_value(*operand) else {
561 return Self::diverged();
562 };
563 let value = self.emit(CfgInstData::BitNot(op_val), ty, span);
564 self.cache(air_ref, value);
565 ExprResult {
566 value: Some(value),
567 continuation: Continuation::Continues,
568 }
569 }
570
571 AirInstData::BitAnd(lhs, rhs) => {
572 let Some(lhs_val) = self.lower_value(*lhs) else {
573 return Self::diverged();
574 };
575 let Some(rhs_val) = self.lower_value(*rhs) else {
576 return Self::diverged();
577 };
578 let value = self.emit(CfgInstData::BitAnd(lhs_val, rhs_val), ty, span);
579 self.cache(air_ref, value);
580 ExprResult {
581 value: Some(value),
582 continuation: Continuation::Continues,
583 }
584 }
585
586 AirInstData::BitOr(lhs, rhs) => {
587 let Some(lhs_val) = self.lower_value(*lhs) else {
588 return Self::diverged();
589 };
590 let Some(rhs_val) = self.lower_value(*rhs) else {
591 return Self::diverged();
592 };
593 let value = self.emit(CfgInstData::BitOr(lhs_val, rhs_val), ty, span);
594 self.cache(air_ref, value);
595 ExprResult {
596 value: Some(value),
597 continuation: Continuation::Continues,
598 }
599 }
600
601 AirInstData::BitXor(lhs, rhs) => {
602 let Some(lhs_val) = self.lower_value(*lhs) else {
603 return Self::diverged();
604 };
605 let Some(rhs_val) = self.lower_value(*rhs) else {
606 return Self::diverged();
607 };
608 let value = self.emit(CfgInstData::BitXor(lhs_val, rhs_val), ty, span);
609 self.cache(air_ref, value);
610 ExprResult {
611 value: Some(value),
612 continuation: Continuation::Continues,
613 }
614 }
615
616 AirInstData::Shl(lhs, rhs) => {
617 let Some(lhs_val) = self.lower_value(*lhs) else {
618 return Self::diverged();
619 };
620 let Some(rhs_val) = self.lower_value(*rhs) else {
621 return Self::diverged();
622 };
623 let value = self.emit(CfgInstData::Shl(lhs_val, rhs_val), ty, span);
624 self.cache(air_ref, value);
625 ExprResult {
626 value: Some(value),
627 continuation: Continuation::Continues,
628 }
629 }
630
631 AirInstData::Shr(lhs, rhs) => {
632 let Some(lhs_val) = self.lower_value(*lhs) else {
633 return Self::diverged();
634 };
635 let Some(rhs_val) = self.lower_value(*rhs) else {
636 return Self::diverged();
637 };
638 let value = self.emit(CfgInstData::Shr(lhs_val, rhs_val), ty, span);
639 self.cache(air_ref, value);
640 ExprResult {
641 value: Some(value),
642 continuation: Continuation::Continues,
643 }
644 }
645
646 AirInstData::Alloc { slot, init } => {
647 self.forget_consumed_value(*init);
650
651 let init_result = self.lower_inst(*init);
652 let init_val = init_result
654 .value
655 .unwrap_or_else(|| self.emit(CfgInstData::Const(0), Type::UNIT, span));
656 self.emit(
657 CfgInstData::Alloc {
658 slot: *slot,
659 init: init_val,
660 },
661 Type::UNIT,
662 span,
663 );
664 ExprResult {
665 value: None,
666 continuation: Continuation::Continues,
667 }
668 }
669
670 AirInstData::Load { slot } => {
671 let value = self.emit(CfgInstData::Load { slot: *slot }, ty, span);
672 self.cache(air_ref, value);
673 ExprResult {
674 value: Some(value),
675 continuation: Continuation::Continues,
676 }
677 }
678
679 AirInstData::Store {
680 slot,
681 value,
682 had_live_value,
683 } => {
684 let Some(val) = self.lower_value(*value) else {
685 return Self::diverged();
686 };
687 let value_ty = self.air.get(*value).ty;
688 if *had_live_value && self.type_needs_drop(value_ty) {
690 let old_val = self.emit(CfgInstData::Load { slot: *slot }, value_ty, span);
691 self.emit(CfgInstData::Drop { value: old_val }, Type::UNIT, span);
692 }
693 if !*had_live_value && self.type_needs_drop(value_ty) {
697 self.re_add_local_slot(*slot, value_ty, span);
698 }
699 self.emit(
700 CfgInstData::Store {
701 slot: *slot,
702 value: val,
703 },
704 Type::UNIT,
705 span,
706 );
707 ExprResult {
708 value: None,
709 continuation: Continuation::Continues,
710 }
711 }
712
713 AirInstData::ParamStore { param_slot, value } => {
714 let Some(val) = self.lower_value(*value) else {
715 return Self::diverged();
716 };
717 self.emit(
718 CfgInstData::ParamStore {
719 param_slot: *param_slot,
720 value: val,
721 },
722 Type::UNIT,
723 span,
724 );
725 ExprResult {
726 value: None,
727 continuation: Continuation::Continues,
728 }
729 }
730
731 AirInstData::Call {
732 name,
733 args_start,
734 args_len,
735 } => {
736 let air_call_args: Vec<_> =
738 self.air.get_call_args(*args_start, *args_len).collect();
739
740 let mut arg_vals = Vec::new();
741 for arg in &air_call_args {
742 let Some(value) = self.lower_value(arg.value) else {
743 return Self::diverged();
744 };
745 arg_vals.push(CfgCallArg {
746 value,
747 mode: CfgArgMode::from(arg.mode),
748 });
749 }
750
751 for arg in &air_call_args {
754 if arg.mode == AirArgMode::Normal {
755 self.forget_consumed_value(arg.value);
756 }
757 }
758
759 let (args_start, args_len) = self.cfg.push_call_args(arg_vals);
761 let value = self.emit(
762 CfgInstData::Call {
763 name: *name,
764 args_start,
765 args_len,
766 },
767 ty,
768 span,
769 );
770 self.cache(air_ref, value);
771 ExprResult {
772 value: Some(value),
773 continuation: Continuation::Continues,
774 }
775 }
776
777 AirInstData::Intrinsic {
778 name,
779 args_start,
780 args_len,
781 } => {
782 let mut arg_vals = Vec::new();
783 for arg in self.air.get_air_refs(*args_start, *args_len) {
784 let Some(val) = self.lower_value(arg) else {
785 return Self::diverged();
786 };
787 arg_vals.push(val);
788 }
789 let (args_start, args_len) = self.cfg.push_extra(arg_vals);
791 let value = self.emit(
792 CfgInstData::Intrinsic {
793 name: *name,
794 args_start,
795 args_len,
796 },
797 ty,
798 span,
799 );
800 self.cache(air_ref, value);
801 ExprResult {
802 value: Some(value),
803 continuation: Continuation::Continues,
804 }
805 }
806
807 AirInstData::StructInit {
808 struct_id,
809 fields_start,
810 fields_len,
811 source_order_start,
812 } => {
813 let (fields, source_order) =
816 self.air
817 .get_struct_init(*fields_start, *fields_len, *source_order_start);
818 let fields: Vec<AirRef> = fields.collect();
819 let source_order: Vec<usize> = source_order.collect();
820
821 let mut lowered_fields: Vec<Option<CfgValue>> = vec![None; fields.len()];
822 for decl_idx in source_order {
823 let Some(lowered) = self.lower_value(fields[decl_idx]) else {
824 return Self::diverged();
825 };
826 lowered_fields[decl_idx] = Some(lowered);
827 }
828
829 for &field_air_ref in &fields {
836 self.forget_consumed_value(field_air_ref);
837 }
838
839 let field_vals: Vec<CfgValue> = lowered_fields
841 .into_iter()
842 .map(|opt: Option<CfgValue>| opt.expect("all fields should be lowered"))
843 .collect();
844
845 let (fields_start, fields_len) = self.cfg.push_extra(field_vals);
847 let value = self.emit(
848 CfgInstData::StructInit {
849 struct_id: *struct_id,
850 fields_start,
851 fields_len,
852 },
853 ty,
854 span,
855 );
856 self.cache(air_ref, value);
857 ExprResult {
858 value: Some(value),
859 continuation: Continuation::Continues,
860 }
861 }
862
863 AirInstData::FieldGet {
864 base,
865 struct_id,
866 field_index,
867 } => {
868 if let Some(value) = self.lower_place_read(air_ref, ty, span) {
870 self.cache(air_ref, value);
871 return ExprResult {
872 value: Some(value),
873 continuation: Continuation::Continues,
874 };
875 }
876
877 let Some(base_val) = self.lower_value(*base) else {
881 return Self::diverged();
882 };
883
884 let temp_slot = self.cfg.alloc_temp_local();
886
887 self.emit(
889 CfgInstData::StorageLive { slot: temp_slot },
890 Type::UNIT,
891 span,
892 );
893 self.emit(
894 CfgInstData::Alloc {
895 slot: temp_slot,
896 init: base_val,
897 },
898 Type::UNIT,
899 span,
900 );
901
902 let place = self.cfg.make_place(
904 PlaceBase::Local(temp_slot),
905 std::iter::once(Projection::Field {
906 struct_id: *struct_id,
907 field_index: *field_index,
908 }),
909 );
910 let value = self.emit(CfgInstData::PlaceRead { place }, ty, span);
911
912 self.emit(
914 CfgInstData::StorageDead { slot: temp_slot },
915 Type::UNIT,
916 span,
917 );
918
919 self.cache(air_ref, value);
920 ExprResult {
921 value: Some(value),
922 continuation: Continuation::Continues,
923 }
924 }
925
926 AirInstData::FieldSet {
927 slot,
928 struct_id,
929 field_index,
930 value,
931 } => {
932 let Some(val) = self.lower_value(*value) else {
933 return Self::diverged();
934 };
935 self.emit(
936 CfgInstData::FieldSet {
937 slot: *slot,
938 struct_id: *struct_id,
939 field_index: *field_index,
940 value: val,
941 },
942 Type::UNIT,
943 span,
944 );
945 ExprResult {
946 value: None,
947 continuation: Continuation::Continues,
948 }
949 }
950
951 AirInstData::ParamFieldSet {
952 param_slot,
953 inner_offset,
954 struct_id,
955 field_index,
956 value,
957 } => {
958 let Some(val) = self.lower_value(*value) else {
959 return Self::diverged();
960 };
961 self.emit(
962 CfgInstData::ParamFieldSet {
963 param_slot: *param_slot,
964 inner_offset: *inner_offset,
965 struct_id: *struct_id,
966 field_index: *field_index,
967 value: val,
968 },
969 Type::UNIT,
970 span,
971 );
972 ExprResult {
973 value: None,
974 continuation: Continuation::Continues,
975 }
976 }
977
978 AirInstData::Block {
979 stmts_start,
980 stmts_len,
981 value,
982 } => {
983 let statements: Vec<AirRef> =
985 self.air.get_air_refs(*stmts_start, *stmts_len).collect();
986
987 let is_storage_live_wrapper = statements.iter().all(|stmt| {
991 matches!(self.air.get(*stmt).data, AirInstData::StorageLive { .. })
992 });
993
994 if !is_storage_live_wrapper {
996 self.scope_stack.push(Vec::new());
997 }
998
999 for (i, stmt) in statements.iter().enumerate() {
1009 let result = self.lower_inst(*stmt);
1010 if matches!(result.continuation, Continuation::Diverged) {
1011 let diverging_span = self.air.get(*stmt).span;
1013
1014 let remaining = &statements[i + 1..];
1017 if !remaining.is_empty() {
1018 let unreachable_stmt = remaining[0];
1020 let unreachable_span = self.air.get(unreachable_stmt).span;
1021 self.warnings.push(
1022 CompileWarning::new(WarningKind::UnreachableCode, unreachable_span)
1023 .with_label(
1024 "any code following this expression is unreachable",
1025 diverging_span,
1026 )
1027 .with_note(
1028 "this warning occurs because the preceding expression \
1029 diverges (e.g., returns, breaks, or continues)",
1030 ),
1031 );
1032 } else {
1033 let value_span = self.air.get(*value).span;
1038 let is_synthetic = value_span.start == value_span.end;
1039 if !is_synthetic {
1040 self.warnings.push(
1041 CompileWarning::new(WarningKind::UnreachableCode, value_span)
1042 .with_label(
1043 "any code following this expression is unreachable",
1044 diverging_span,
1045 )
1046 .with_note(
1047 "this warning occurs because the preceding expression \
1048 diverges (e.g., returns, breaks, or continues)",
1049 ),
1050 );
1051 }
1052 }
1053 return ExprResult {
1056 value: None,
1057 continuation: Continuation::Diverged,
1058 };
1059 }
1060 }
1061
1062 let result = self.lower_inst(*value);
1064
1065 if !is_storage_live_wrapper && let Some(scope_slots) = self.scope_stack.pop() {
1070 if !matches!(result.continuation, Continuation::Diverged) {
1072 for live_slot in scope_slots.into_iter().rev() {
1073 if self.type_needs_drop(live_slot.ty) {
1075 let slot_val = self.emit(
1076 CfgInstData::Load {
1077 slot: live_slot.slot,
1078 },
1079 live_slot.ty,
1080 live_slot.span,
1081 );
1082 self.emit(
1083 CfgInstData::Drop { value: slot_val },
1084 Type::UNIT,
1085 live_slot.span,
1086 );
1087 }
1088 self.emit(
1089 CfgInstData::StorageDead {
1090 slot: live_slot.slot,
1091 },
1092 Type::UNIT,
1093 live_slot.span,
1094 );
1095 }
1096 }
1097 }
1098
1099 result
1100 }
1101
1102 AirInstData::Branch {
1103 cond,
1104 then_value,
1105 else_value,
1106 } => {
1107 let Some(cond_val) = self.lower_value(*cond) else {
1108 return Self::diverged();
1109 };
1110
1111 let then_block = self.cfg.new_block();
1112 let else_block = self.cfg.new_block();
1113 let join_block = self.cfg.new_block();
1114
1115 let then_type = self.air.get(*then_value).ty;
1117 let else_type = else_value.map(|e| self.air.get(e).ty);
1118
1119 let (then_args_start, then_args_len) = self.cfg.push_extra(std::iter::empty());
1121 let (else_args_start, else_args_len) = self.cfg.push_extra(std::iter::empty());
1122 self.cfg.set_terminator(
1123 self.current_block,
1124 Terminator::Branch {
1125 cond: cond_val,
1126 then_block,
1127 then_args_start,
1128 then_args_len,
1129 else_block,
1130 else_args_start,
1131 else_args_len,
1132 },
1133 );
1134
1135 self.current_block = then_block;
1137 let then_result = self.lower_inst(*then_value);
1138 let then_exit_block = self.current_block;
1139 let then_diverged = matches!(then_result.continuation, Continuation::Diverged);
1140
1141 self.current_block = else_block;
1143 let else_result = if let Some(else_val) = else_value {
1144 self.lower_inst(*else_val)
1145 } else {
1146 let unit_val = self.emit(CfgInstData::Const(0), Type::UNIT, span);
1148 ExprResult {
1149 value: Some(unit_val),
1150 continuation: Continuation::Continues,
1151 }
1152 };
1153 let else_exit_block = self.current_block;
1154 let else_diverged = matches!(else_result.continuation, Continuation::Diverged);
1155
1156 if then_diverged && else_diverged {
1158 self.cfg.set_terminator(join_block, Terminator::Unreachable);
1159 return ExprResult {
1160 value: None,
1161 continuation: Continuation::Diverged,
1162 };
1163 }
1164
1165 let result_type = if then_type.is_never() {
1167 else_type.unwrap_or(Type::UNIT)
1168 } else {
1169 then_type
1170 };
1171
1172 let result_param = if result_type != Type::UNIT && result_type != Type::NEVER {
1174 Some(self.cfg.add_block_param(join_block, result_type))
1175 } else {
1176 None
1177 };
1178
1179 if !then_diverged {
1181 let args: Vec<CfgValue> = if let Some(val) = then_result.value {
1182 if result_param.is_some() {
1183 vec![val]
1184 } else {
1185 vec![]
1186 }
1187 } else {
1188 vec![]
1189 };
1190 let (args_start, args_len) = self.cfg.push_extra(args);
1191 self.cfg.set_terminator(
1192 then_exit_block,
1193 Terminator::Goto {
1194 target: join_block,
1195 args_start,
1196 args_len,
1197 },
1198 );
1199 }
1200
1201 if !else_diverged {
1202 let args: Vec<CfgValue> = if let Some(val) = else_result.value {
1203 if result_param.is_some() {
1204 vec![val]
1205 } else {
1206 vec![]
1207 }
1208 } else {
1209 vec![]
1210 };
1211 let (args_start, args_len) = self.cfg.push_extra(args);
1212 self.cfg.set_terminator(
1213 else_exit_block,
1214 Terminator::Goto {
1215 target: join_block,
1216 args_start,
1217 args_len,
1218 },
1219 );
1220 }
1221
1222 self.current_block = join_block;
1223
1224 if let Some(param) = result_param {
1225 self.cache(air_ref, param);
1226 }
1227
1228 ExprResult {
1229 value: result_param,
1230 continuation: Continuation::Continues,
1231 }
1232 }
1233
1234 AirInstData::Loop { cond, body } => {
1235 let header_block = self.cfg.new_block();
1236 let body_block = self.cfg.new_block();
1237 let exit_block = self.cfg.new_block();
1238
1239 let (args_start, args_len) = self.cfg.push_extra(std::iter::empty());
1241 self.cfg.set_terminator(
1242 self.current_block,
1243 Terminator::Goto {
1244 target: header_block,
1245 args_start,
1246 args_len,
1247 },
1248 );
1249
1250 self.loop_stack.push(LoopContext {
1254 header: header_block,
1255 exit: exit_block,
1256 scope_depth: self.scope_stack.len(),
1257 });
1258
1259 self.current_block = header_block;
1261 let Some(cond_val) = self.lower_value(*cond) else {
1262 return Self::diverged();
1263 };
1264
1265 let (then_args_start, then_args_len) = self.cfg.push_extra(std::iter::empty());
1267 let (else_args_start, else_args_len) = self.cfg.push_extra(std::iter::empty());
1268 self.cfg.set_terminator(
1269 self.current_block,
1270 Terminator::Branch {
1271 cond: cond_val,
1272 then_block: body_block,
1273 then_args_start,
1274 then_args_len,
1275 else_block: exit_block,
1276 else_args_start,
1277 else_args_len,
1278 },
1279 );
1280
1281 self.current_block = body_block;
1283 let body_result = self.lower_inst(*body);
1284
1285 if !matches!(body_result.continuation, Continuation::Diverged) {
1287 let (args_start, args_len) = self.cfg.push_extra(std::iter::empty());
1288 self.cfg.set_terminator(
1289 self.current_block,
1290 Terminator::Goto {
1291 target: header_block,
1292 args_start,
1293 args_len,
1294 },
1295 );
1296 }
1297
1298 self.loop_stack.pop();
1299
1300 self.current_block = exit_block;
1302
1303 let unit_val = self.emit(CfgInstData::Const(0), Type::UNIT, span);
1305 ExprResult {
1306 value: Some(unit_val),
1307 continuation: Continuation::Continues,
1308 }
1309 }
1310
1311 AirInstData::InfiniteLoop { body } => {
1312 let body_block = self.cfg.new_block();
1322 let exit_block = self.cfg.new_block();
1323
1324 let (args_start, args_len) = self.cfg.push_extra(std::iter::empty());
1326 self.cfg.set_terminator(
1327 self.current_block,
1328 Terminator::Goto {
1329 target: body_block,
1330 args_start,
1331 args_len,
1332 },
1333 );
1334
1335 self.loop_stack.push(LoopContext {
1339 header: body_block,
1340 exit: exit_block,
1341 scope_depth: self.scope_stack.len(),
1342 });
1343
1344 self.current_block = body_block;
1346 let body_result = self.lower_inst(*body);
1347
1348 if !matches!(body_result.continuation, Continuation::Diverged) {
1350 let (args_start, args_len) = self.cfg.push_extra(std::iter::empty());
1351 self.cfg.set_terminator(
1352 self.current_block,
1353 Terminator::Goto {
1354 target: body_block,
1355 args_start,
1356 args_len,
1357 },
1358 );
1359 }
1360
1361 self.loop_stack.pop();
1362
1363 self.current_block = exit_block;
1369 self.cfg
1370 .set_terminator(self.current_block, Terminator::Unreachable);
1371
1372 let unit_val = self.emit(CfgInstData::Const(0), Type::UNIT, span);
1375 ExprResult {
1376 value: Some(unit_val),
1377 continuation: Continuation::Continues,
1378 }
1379 }
1380
1381 AirInstData::Match {
1382 scrutinee,
1383 arms_start,
1384 arms_len,
1385 } => {
1386 let Some(scrutinee_val) = self.lower_value(*scrutinee) else {
1388 return Self::diverged();
1389 };
1390
1391 let arms: Vec<(AirPattern, AirRef)> =
1393 self.air.get_match_arms(*arms_start, *arms_len).collect();
1394
1395 let arm_blocks: Vec<_> = arms.iter().map(|_| self.cfg.new_block()).collect();
1397 let join_block = self.cfg.new_block();
1398
1399 let result_type = arms
1401 .iter()
1402 .map(|(_, body)| self.air.get(*body).ty)
1403 .find(|ty| !ty.is_never())
1404 .unwrap_or(Type::NEVER);
1405
1406 let mut switch_cases = Vec::new();
1409 let mut default_block = None;
1410
1411 for (i, (pattern, _)) in arms.iter().enumerate() {
1412 match pattern {
1413 AirPattern::Wildcard => {
1414 default_block = Some(arm_blocks[i]);
1415 break;
1417 }
1418 AirPattern::Int(n) => {
1419 switch_cases.push((*n, arm_blocks[i]));
1420 }
1421 AirPattern::Bool(b) => {
1422 let val = if *b { 1 } else { 0 };
1424 switch_cases.push((val, arm_blocks[i]));
1425 }
1426 AirPattern::EnumVariant { variant_index, .. } => {
1427 switch_cases.push((*variant_index as i64, arm_blocks[i]));
1429 }
1430 }
1431 }
1432
1433 let default = default_block.unwrap_or_else(|| {
1437 let (_, last_block) = switch_cases
1439 .pop()
1440 .expect("match must have at least one arm");
1441 last_block
1442 });
1443
1444 let (cases_start, cases_len) = self.cfg.push_switch_cases(switch_cases);
1446 self.cfg.set_terminator(
1447 self.current_block,
1448 Terminator::Switch {
1449 scrutinee: scrutinee_val,
1450 cases_start,
1451 cases_len,
1452 default,
1453 },
1454 );
1455
1456 let mut all_diverged = true;
1458 let mut arm_results = Vec::new();
1459
1460 for (i, (_, body)) in arms.iter().enumerate() {
1461 self.current_block = arm_blocks[i];
1462 let body_result = self.lower_inst(*body);
1463 let exit_block = self.current_block;
1464 let diverged = matches!(body_result.continuation, Continuation::Diverged);
1465
1466 if !diverged {
1467 all_diverged = false;
1468 }
1469
1470 arm_results.push((exit_block, body_result, diverged));
1471 }
1472
1473 if all_diverged {
1475 self.cfg.set_terminator(join_block, Terminator::Unreachable);
1476 return ExprResult {
1477 value: None,
1478 continuation: Continuation::Diverged,
1479 };
1480 }
1481
1482 let result_param = if result_type != Type::UNIT && result_type != Type::NEVER {
1484 Some(self.cfg.add_block_param(join_block, result_type))
1485 } else {
1486 None
1487 };
1488
1489 for (exit_block, body_result, diverged) in arm_results {
1491 if !diverged {
1492 let args: Vec<CfgValue> = if let Some(val) = body_result.value {
1493 if result_param.is_some() {
1494 vec![val]
1495 } else {
1496 vec![]
1497 }
1498 } else {
1499 vec![]
1500 };
1501 let (args_start, args_len) = self.cfg.push_extra(args);
1502 self.cfg.set_terminator(
1503 exit_block,
1504 Terminator::Goto {
1505 target: join_block,
1506 args_start,
1507 args_len,
1508 },
1509 );
1510 }
1511 }
1512
1513 self.current_block = join_block;
1514
1515 if let Some(param) = result_param {
1516 self.cache(air_ref, param);
1517 }
1518
1519 ExprResult {
1520 value: result_param,
1521 continuation: Continuation::Continues,
1522 }
1523 }
1524
1525 AirInstData::Break => {
1526 let loop_ctx = self.loop_stack.last().expect("break outside loop");
1528 let target_depth = loop_ctx.scope_depth;
1529 let exit_block = loop_ctx.exit;
1530 self.emit_drops_for_loop_exit(target_depth, span);
1531
1532 let (args_start, args_len) = self.cfg.push_extra(std::iter::empty());
1533 self.cfg.set_terminator(
1534 self.current_block,
1535 Terminator::Goto {
1536 target: exit_block,
1537 args_start,
1538 args_len,
1539 },
1540 );
1541
1542 ExprResult {
1543 value: None,
1544 continuation: Continuation::Diverged,
1545 }
1546 }
1547
1548 AirInstData::Continue => {
1549 let loop_ctx = self.loop_stack.last().expect("continue outside loop");
1551 let target_depth = loop_ctx.scope_depth;
1552 let header_block = loop_ctx.header;
1553 self.emit_drops_for_loop_exit(target_depth, span);
1554
1555 let (args_start, args_len) = self.cfg.push_extra(std::iter::empty());
1556 self.cfg.set_terminator(
1557 self.current_block,
1558 Terminator::Goto {
1559 target: header_block,
1560 args_start,
1561 args_len,
1562 },
1563 );
1564
1565 ExprResult {
1566 value: None,
1567 continuation: Continuation::Diverged,
1568 }
1569 }
1570
1571 AirInstData::Ret(value) => {
1572 let val = match value {
1573 Some(v) => {
1574 let result = self.lower_inst(*v);
1575 if matches!(result.continuation, Continuation::Diverged) {
1576 return Self::diverged();
1580 }
1581
1582 self.forget_consumed_value(*v);
1585
1586 result.value
1588 }
1589 None => None,
1590 };
1591
1592 self.emit_drops_for_all_scopes(span);
1594
1595 self.cfg
1596 .set_terminator(self.current_block, Terminator::Return { value: val });
1597
1598 ExprResult {
1599 value: None,
1600 continuation: Continuation::Diverged,
1601 }
1602 }
1603
1604 AirInstData::ArrayInit {
1605 elems_start,
1606 elems_len,
1607 } => {
1608 let elems: Vec<AirRef> = self.air.get_air_refs(*elems_start, *elems_len).collect();
1609 let mut element_vals = Vec::new();
1610 for &elem in &elems {
1611 let Some(val) = self.lower_value(elem) else {
1612 return Self::diverged();
1613 };
1614 element_vals.push(val);
1615 }
1616
1617 for &elem in &elems {
1619 self.forget_consumed_value(elem);
1620 }
1621
1622 let (elements_start, elements_len) = self.cfg.push_extra(element_vals);
1624 let value = self.emit(
1625 CfgInstData::ArrayInit {
1626 elements_start,
1627 elements_len,
1628 },
1629 ty,
1630 span,
1631 );
1632 self.cache(air_ref, value);
1633 ExprResult {
1634 value: Some(value),
1635 continuation: Continuation::Continues,
1636 }
1637 }
1638
1639 AirInstData::IndexGet {
1640 base,
1641 array_type,
1642 index,
1643 } => {
1644 if let Some(value) = self.lower_place_read(air_ref, ty, span) {
1646 self.cache(air_ref, value);
1647 return ExprResult {
1648 value: Some(value),
1649 continuation: Continuation::Continues,
1650 };
1651 }
1652
1653 let Some(base_val) = self.lower_value(*base) else {
1659 return Self::diverged();
1660 };
1661 let Some(index_val) = self.lower_value(*index) else {
1662 return Self::diverged();
1663 };
1664
1665 let temp_slot = self.cfg.alloc_temp_local();
1667
1668 self.emit(
1670 CfgInstData::StorageLive { slot: temp_slot },
1671 Type::UNIT,
1672 span,
1673 );
1674 self.emit(
1675 CfgInstData::Alloc {
1676 slot: temp_slot,
1677 init: base_val,
1678 },
1679 Type::UNIT,
1680 span,
1681 );
1682
1683 let place = self.cfg.make_place(
1685 PlaceBase::Local(temp_slot),
1686 std::iter::once(Projection::Index {
1687 array_type: *array_type,
1688 index: index_val,
1689 }),
1690 );
1691 let value = self.emit(CfgInstData::PlaceRead { place }, ty, span);
1692
1693 self.emit(
1695 CfgInstData::StorageDead { slot: temp_slot },
1696 Type::UNIT,
1697 span,
1698 );
1699
1700 self.cache(air_ref, value);
1701 ExprResult {
1702 value: Some(value),
1703 continuation: Continuation::Continues,
1704 }
1705 }
1706
1707 AirInstData::IndexSet {
1708 slot,
1709 array_type,
1710 index,
1711 value,
1712 } => {
1713 let Some(index_val) = self.lower_value(*index) else {
1714 return Self::diverged();
1715 };
1716 let Some(val) = self.lower_value(*value) else {
1717 return Self::diverged();
1718 };
1719 self.emit(
1720 CfgInstData::IndexSet {
1721 slot: *slot,
1722 array_type: *array_type,
1723 index: index_val,
1724 value: val,
1725 },
1726 Type::UNIT,
1727 span,
1728 );
1729 ExprResult {
1730 value: None,
1731 continuation: Continuation::Continues,
1732 }
1733 }
1734
1735 AirInstData::ParamIndexSet {
1736 param_slot,
1737 array_type,
1738 index,
1739 value,
1740 } => {
1741 let Some(index_val) = self.lower_value(*index) else {
1742 return Self::diverged();
1743 };
1744 let Some(val) = self.lower_value(*value) else {
1745 return Self::diverged();
1746 };
1747 self.emit(
1748 CfgInstData::ParamIndexSet {
1749 param_slot: *param_slot,
1750 array_type: *array_type,
1751 index: index_val,
1752 value: val,
1753 },
1754 Type::UNIT,
1755 span,
1756 );
1757 ExprResult {
1758 value: None,
1759 continuation: Continuation::Continues,
1760 }
1761 }
1762
1763 AirInstData::PlaceRead { place } => {
1765 let Some(cfg_place) = self.lower_air_place(*place) else {
1767 return Self::diverged();
1768 };
1769 let value = self.emit(CfgInstData::PlaceRead { place: cfg_place }, ty, span);
1770 self.cache(air_ref, value);
1771 ExprResult {
1772 value: Some(value),
1773 continuation: Continuation::Continues,
1774 }
1775 }
1776
1777 AirInstData::PlaceWrite { place, value } => {
1778 let Some(val) = self.lower_value(*value) else {
1780 return Self::diverged();
1781 };
1782 let value_ty = self.air.get(*value).ty;
1783 let Some(cfg_place) = self.lower_air_place(*place) else {
1785 return Self::diverged();
1786 };
1787 if self.type_needs_drop(value_ty) {
1791 let old_val =
1792 self.emit(CfgInstData::PlaceRead { place: cfg_place }, value_ty, span);
1793 self.emit(CfgInstData::Drop { value: old_val }, Type::UNIT, span);
1794 }
1795 self.emit(
1796 CfgInstData::PlaceWrite {
1797 place: cfg_place,
1798 value: val,
1799 },
1800 Type::UNIT,
1801 span,
1802 );
1803 ExprResult {
1804 value: None,
1805 continuation: Continuation::Continues,
1806 }
1807 }
1808
1809 AirInstData::EnumVariant {
1810 enum_id,
1811 variant_index,
1812 } => {
1813 let value = self.emit(
1815 CfgInstData::EnumVariant {
1816 enum_id: *enum_id,
1817 variant_index: *variant_index,
1818 },
1819 ty,
1820 span,
1821 );
1822 self.cache(air_ref, value);
1823 ExprResult {
1824 value: Some(value),
1825 continuation: Continuation::Continues,
1826 }
1827 }
1828
1829 AirInstData::EnumCreate {
1830 enum_id,
1831 variant_index,
1832 fields_start,
1833 fields_len,
1834 } => {
1835 let field_air_refs = self
1837 .air
1838 .get_air_refs(*fields_start, *fields_len)
1839 .collect::<Vec<_>>();
1840 let mut field_vals = Vec::with_capacity(field_air_refs.len());
1841 for field_ref in field_air_refs {
1842 let Some(val) = self.lower_value(field_ref) else {
1843 return Self::diverged();
1844 };
1845 field_vals.push(val);
1846 }
1847
1848 let (fields_start_cfg, fields_len_cfg) = self.cfg.push_extra(field_vals);
1850
1851 let value = self.emit(
1852 CfgInstData::EnumCreate {
1853 enum_id: *enum_id,
1854 variant_index: *variant_index,
1855 fields_start: fields_start_cfg,
1856 fields_len: fields_len_cfg,
1857 },
1858 ty,
1859 span,
1860 );
1861 self.cache(air_ref, value);
1862 ExprResult {
1863 value: Some(value),
1864 continuation: Continuation::Continues,
1865 }
1866 }
1867
1868 AirInstData::EnumPayloadGet {
1869 base,
1870 variant_index,
1871 field_index,
1872 } => {
1873 let Some(base_val) = self.lower_value(*base) else {
1874 return Self::diverged();
1875 };
1876 let value = self.emit(
1877 CfgInstData::EnumPayloadGet {
1878 base: base_val,
1879 variant_index: *variant_index,
1880 field_index: *field_index,
1881 },
1882 ty,
1883 span,
1884 );
1885 self.cache(air_ref, value);
1886 ExprResult {
1887 value: Some(value),
1888 continuation: Continuation::Continues,
1889 }
1890 }
1891
1892 AirInstData::IntCast { value, from_ty } => {
1893 let Some(val) = self.lower_value(*value) else {
1894 return Self::diverged();
1895 };
1896 let result = self.emit(
1897 CfgInstData::IntCast {
1898 value: val,
1899 from_ty: *from_ty,
1900 },
1901 ty,
1902 span,
1903 );
1904 self.cache(air_ref, result);
1905 ExprResult {
1906 value: Some(result),
1907 continuation: Continuation::Continues,
1908 }
1909 }
1910
1911 AirInstData::FloatCast { value, from_ty } => {
1912 let Some(val) = self.lower_value(*value) else {
1913 return Self::diverged();
1914 };
1915 let result = self.emit(
1916 CfgInstData::FloatCast {
1917 value: val,
1918 from_ty: *from_ty,
1919 },
1920 ty,
1921 span,
1922 );
1923 self.cache(air_ref, result);
1924 ExprResult {
1925 value: Some(result),
1926 continuation: Continuation::Continues,
1927 }
1928 }
1929
1930 AirInstData::IntToFloat { value, from_ty } => {
1931 let Some(val) = self.lower_value(*value) else {
1932 return Self::diverged();
1933 };
1934 let result = self.emit(
1935 CfgInstData::IntToFloat {
1936 value: val,
1937 from_ty: *from_ty,
1938 },
1939 ty,
1940 span,
1941 );
1942 self.cache(air_ref, result);
1943 ExprResult {
1944 value: Some(result),
1945 continuation: Continuation::Continues,
1946 }
1947 }
1948
1949 AirInstData::FloatToInt { value, from_ty } => {
1950 let Some(val) = self.lower_value(*value) else {
1951 return Self::diverged();
1952 };
1953 let result = self.emit(
1954 CfgInstData::FloatToInt {
1955 value: val,
1956 from_ty: *from_ty,
1957 },
1958 ty,
1959 span,
1960 );
1961 self.cache(air_ref, result);
1962 ExprResult {
1963 value: Some(result),
1964 continuation: Continuation::Continues,
1965 }
1966 }
1967
1968 AirInstData::Drop { value } => {
1969 let Some(val) = self.lower_value(*value) else {
1971 return Self::diverged();
1972 };
1973 let val_ty = self.air.get(*value).ty;
1974
1975 if self.type_needs_drop(val_ty) {
1980 self.emit(CfgInstData::Drop { value: val }, Type::UNIT, span);
1981 }
1982
1983 ExprResult {
1985 value: None,
1986 continuation: Continuation::Continues,
1987 }
1988 }
1989
1990 AirInstData::StorageLive { slot } => {
1991 self.emit(CfgInstData::StorageLive { slot: *slot }, Type::UNIT, span);
1993
1994 if let Some(scope) = self.scope_stack.last_mut() {
1996 scope.push(LiveSlot {
1997 slot: *slot,
1998 ty,
1999 span,
2000 });
2001 }
2002
2003 ExprResult {
2004 value: None,
2005 continuation: Continuation::Continues,
2006 }
2007 }
2008
2009 AirInstData::StorageDead { slot } => {
2010 self.emit(CfgInstData::StorageDead { slot: *slot }, Type::UNIT, span);
2013 ExprResult {
2014 value: None,
2015 continuation: Continuation::Continues,
2016 }
2017 }
2018 }
2019 }
2020
2021 fn emit(&mut self, data: CfgInstData, ty: Type, span: gruel_span::Span) -> CfgValue {
2023 self.cfg
2024 .add_inst_to_block(self.current_block, CfgInst { data, ty, span })
2025 }
2026
2027 fn cache(&mut self, air_ref: AirRef, value: CfgValue) {
2029 self.value_cache[air_ref.as_u32() as usize] = Some(value);
2030 }
2031
2032 fn lower_value(&mut self, air_ref: AirRef) -> Option<CfgValue> {
2036 let result = self.lower_inst(air_ref);
2037 if matches!(result.continuation, Continuation::Diverged) {
2038 None
2039 } else {
2040 result.value
2041 }
2042 }
2043
2044 fn diverged() -> ExprResult {
2046 ExprResult {
2047 value: None,
2048 continuation: Continuation::Diverged,
2049 }
2050 }
2051
2052 fn forget_local_slot(&mut self, slot: u32) {
2058 for scope in self.scope_stack.iter_mut() {
2059 scope.retain(|ls| ls.slot != slot);
2060 }
2061 }
2062
2063 fn re_add_local_slot(&mut self, slot: u32, ty: Type, span: gruel_span::Span) {
2067 let already_tracked = self
2069 .scope_stack
2070 .iter()
2071 .any(|scope| scope.iter().any(|ls| ls.slot == slot));
2072 if !already_tracked && let Some(scope) = self.scope_stack.last_mut() {
2073 scope.push(LiveSlot { slot, ty, span });
2074 }
2075 }
2076
2077 fn forget_param(&mut self, param_slot: u32) {
2083 self.live_params.retain(|lp| lp.param_slot != param_slot);
2084 }
2085
2086 fn type_needs_drop(&self, ty: Type) -> bool {
2088 crate::drop_names::type_needs_drop(ty, self.type_pool)
2089 }
2090
2091 fn forget_consumed_value(&mut self, air_ref: AirRef) {
2095 let inst = self.air.get(air_ref);
2096 match inst.data {
2097 AirInstData::Load { slot } => {
2098 if self.type_needs_drop(inst.ty) {
2099 self.forget_local_slot(slot);
2100 }
2101 }
2102 AirInstData::Param { index } => {
2103 if self.type_needs_drop(inst.ty) {
2104 self.forget_param(index);
2105 }
2106 }
2107 _ => {}
2108 }
2109 }
2110
2111 fn emit_drops_for_all_scopes(&mut self, span: gruel_span::Span) {
2114 let all_slots: Vec<LiveSlot> = self
2116 .scope_stack
2117 .iter()
2118 .rev()
2119 .flat_map(|scope| scope.iter().rev().cloned())
2120 .collect();
2121
2122 for live_slot in all_slots {
2123 self.emit_drop_for_slot(&live_slot, span);
2124 }
2125
2126 let params: Vec<LiveParam> = self.live_params.iter().rev().cloned().collect();
2128 for live_param in params {
2129 self.emit_drop_for_param(&live_param, span);
2130 }
2131 }
2132
2133 fn emit_drops_for_loop_exit(&mut self, target_depth: usize, span: gruel_span::Span) {
2137 let loop_slots: Vec<LiveSlot> = self
2140 .scope_stack
2141 .iter()
2142 .skip(target_depth)
2143 .rev()
2144 .flat_map(|scope| scope.iter().rev().cloned())
2145 .collect();
2146
2147 for live_slot in loop_slots {
2148 self.emit_drop_for_slot(&live_slot, span);
2149 }
2150 }
2151
2152 fn emit_drop_for_slot(&mut self, live_slot: &LiveSlot, span: gruel_span::Span) {
2154 if self.type_needs_drop(live_slot.ty) {
2156 let slot_val = self.emit(
2157 CfgInstData::Load {
2158 slot: live_slot.slot,
2159 },
2160 live_slot.ty,
2161 span,
2162 );
2163 self.emit(CfgInstData::Drop { value: slot_val }, Type::UNIT, span);
2164 }
2165 self.emit(
2166 CfgInstData::StorageDead {
2167 slot: live_slot.slot,
2168 },
2169 Type::UNIT,
2170 span,
2171 );
2172 }
2173
2174 fn emit_drop_for_param(&mut self, live_param: &LiveParam, span: gruel_span::Span) {
2178 let param_val = self.emit(
2179 CfgInstData::Param {
2180 index: live_param.param_slot,
2181 },
2182 live_param.ty,
2183 span,
2184 );
2185 self.emit(CfgInstData::Drop { value: param_val }, Type::UNIT, span);
2186 }
2187
2188 fn try_trace_place(&mut self, air_ref: AirRef) -> TracedPlace {
2205 let inst = self.air.get(air_ref);
2206
2207 match &inst.data {
2208 AirInstData::Load { slot } => Some((PlaceBase::Local(*slot), Vec::new())),
2210
2211 AirInstData::Param { index } => Some((PlaceBase::Param(*index), Vec::new())),
2213
2214 AirInstData::IndexGet {
2216 base,
2217 array_type,
2218 index,
2219 } => {
2220 let (base_place, mut projections) = self.try_trace_place(*base)?;
2222
2223 let index_val = self.lower_value(*index)?;
2225
2226 projections.push((
2228 Projection::Index {
2229 array_type: *array_type,
2230 index: index_val,
2231 },
2232 Some(index_val),
2233 ));
2234
2235 Some((base_place, projections))
2236 }
2237
2238 AirInstData::FieldGet {
2240 base,
2241 struct_id,
2242 field_index,
2243 } => {
2244 let (base_place, mut projections) = self.try_trace_place(*base)?;
2246
2247 projections.push((
2249 Projection::Field {
2250 struct_id: *struct_id,
2251 field_index: *field_index,
2252 },
2253 None,
2254 ));
2255
2256 Some((base_place, projections))
2257 }
2258
2259 _ => None,
2261 }
2262 }
2263
2264 fn lower_place_read(
2269 &mut self,
2270 air_ref: AirRef,
2271 ty: Type,
2272 span: gruel_span::Span,
2273 ) -> Option<CfgValue> {
2274 let (base, projections) = self.try_trace_place(air_ref)?;
2276
2277 let proj_iter = projections.into_iter().map(|(proj, _)| proj);
2279 let place = self.cfg.make_place(base, proj_iter);
2280
2281 let value = self.emit(CfgInstData::PlaceRead { place }, ty, span);
2283
2284 Some(value)
2285 }
2286
2287 fn lower_air_place(&mut self, place_ref: AirPlaceRef) -> Option<Place> {
2295 let air_place = self.air.get_place(place_ref);
2296
2297 let base = match air_place.base {
2299 AirPlaceBase::Local(slot) => PlaceBase::Local(slot),
2300 AirPlaceBase::Param(slot) => PlaceBase::Param(slot),
2301 };
2302
2303 let air_projections = self.air.get_place_projections(air_place);
2305 let mut cfg_projections = Vec::with_capacity(air_projections.len());
2306
2307 for proj in air_projections {
2308 let cfg_proj = match proj {
2309 AirProjection::Field {
2310 struct_id,
2311 field_index,
2312 } => Projection::Field {
2313 struct_id: *struct_id,
2314 field_index: *field_index,
2315 },
2316 AirProjection::Index { array_type, index } => {
2317 let index_val = self.lower_value(*index)?;
2319 Projection::Index {
2320 array_type: *array_type,
2321 index: index_val,
2322 }
2323 }
2324 };
2325 cfg_projections.push(cfg_proj);
2326 }
2327
2328 let place = self.cfg.make_place(base, cfg_projections);
2330
2331 Some(place)
2332 }
2333}
2334
2335#[cfg(test)]
2336mod tests {
2337 use super::*;
2338 use gruel_air::Sema;
2339 use gruel_error::PreviewFeatures;
2340 use gruel_lexer::Lexer;
2341 use gruel_parser::Parser;
2342 use gruel_rir::AstGen;
2343
2344 fn build_cfg(source: &str) -> Cfg {
2345 let lexer = Lexer::new(source);
2346 let (tokens, interner) = lexer.tokenize().unwrap();
2347 let parser = Parser::new(tokens, interner);
2348 let (ast, interner) = parser.parse().unwrap();
2349
2350 let astgen = AstGen::new(&ast, &interner);
2351 let rir = astgen.generate();
2352
2353 let sema = Sema::new(&rir, &interner, PreviewFeatures::new());
2354 let output = sema.analyze_all().unwrap();
2355
2356 let func = &output.functions[0];
2357 CfgBuilder::build(func, &output.type_pool).cfg
2358 }
2359
2360 #[test]
2361 fn test_simple_return() {
2362 let cfg = build_cfg("fn main() -> i32 { 42 }");
2363
2364 assert_eq!(cfg.block_count(), 1);
2365 assert_eq!(cfg.fn_name(), "main");
2366
2367 let entry = cfg.get_block(cfg.entry);
2368 assert!(matches!(entry.terminator, Terminator::Return { .. }));
2369 }
2370
2371 #[test]
2372 fn test_if_else() {
2373 let cfg = build_cfg("fn main() -> i32 { if true { 1 } else { 2 } }");
2374
2375 assert!(cfg.block_count() >= 3);
2377 }
2378
2379 #[test]
2380 fn test_while_loop() {
2381 let cfg = build_cfg("fn main() -> i32 { let mut x = 0; while x < 10 { x = x + 1; } x }");
2382
2383 assert!(cfg.block_count() >= 3);
2385 }
2386
2387 #[test]
2388 fn test_short_circuit_and() {
2389 let cfg = build_cfg("fn main() -> i32 { if true && false { 1 } else { 0 } }");
2390
2391 assert!(cfg.block_count() >= 3);
2393 }
2394
2395 #[test]
2396 fn test_diverging_in_if_condition() {
2397 let cfg = build_cfg("fn main() -> i32 { if { return 1; true } { 2 } else { 3 } }");
2400
2401 assert!(cfg.block_count() >= 1);
2403 let entry = cfg.get_block(cfg.entry);
2405 assert!(matches!(entry.terminator, Terminator::Return { .. }));
2406 }
2407
2408 #[test]
2409 fn test_diverging_in_loop_body() {
2410 let cfg = build_cfg("fn main() -> i32 { loop { return 42; } }");
2412
2413 assert!(cfg.block_count() >= 2);
2415 }
2416}