charon_lib/transform/resugar/
reconstruct_boxes.rs1use crate::register_error;
4use crate::transform::TransformCtx;
5use crate::ullbc_ast::*;
6
7use crate::transform::ctx::FusedUllbcPass;
8
9pub struct Transform;
10
11impl FusedUllbcPass for Transform {
23 fn should_run(&self, options: &crate::options::TranslateOptions) -> bool {
24 options.treat_box_as_builtin
25 }
26 fn transform_body(&self, ctx: &mut TransformCtx, b: &mut ExprBody) {
27 for candidate_block_idx in b.body.indices() {
45 let second_block;
46 let box_place;
47 let box_generics;
48 let value_to_write;
49 let old_assign_idx;
50 let assign_span;
51 let unwind_target;
52
53 if let Some(candidate_block) = b.body.get(candidate_block_idx)
54 && let TerminatorKind::Call {
56 target: target_block_idx,
57 call:
58 Call {
59 args: malloc_args,
60 func: _, dest: malloc_dest,
62 },
63 on_unwind,
64 } = &candidate_block.terminator.kind
65 && let [Operand::Const(..), Operand::Const(..)] = malloc_args.as_slice()
67 && let Some(target_block) = b.body.get(*target_block_idx)
68 && let [Statement {
69 kind: StatementKind::StorageLive(target_var),
70 ..
71 }, Statement {
72 kind:
73 StatementKind::Assign(box_make, Rvalue::ShallowInitBox(Operand::Move(alloc_use), _)),
74 ..
75 }, rest @ ..] = target_block.statements.as_slice()
76 && alloc_use == malloc_dest
77 && let Some(local_id) = box_make.as_local()
78 && local_id == *target_var
79 && let TyKind::Adt(ty_ref) = box_make.ty().kind()
80 && let TypeId::Builtin(BuiltinTy::Box) = ty_ref.id
81 && let Some((assign_idx_in_rest, val, span)) = rest.iter().enumerate().find_map(|(idx, st)| {
82 if let Statement {
83 kind: StatementKind::Assign(box_deref, val),
84 span,
85 ..
86 } = st
87 && let Some((sub, ProjectionElem::Deref)) = box_deref.as_projection()
88 && sub == box_make
89 {
90 Some((idx, val, span))
91 } else {
92 None
93 }
94 })
95 {
96 box_place = box_make.clone();
97 old_assign_idx = assign_idx_in_rest + 2; value_to_write = val.clone();
99 box_generics = ty_ref.generics.clone();
100 second_block = *target_block_idx;
101 assign_span = *span;
102 unwind_target = *on_unwind;
103 } else {
104 continue;
105 }
106
107 let first_block = b.body.get_mut(candidate_block_idx).unwrap();
108 let box_place_local = box_place.as_local().unwrap();
109 let value_to_write = match value_to_write {
110 Rvalue::Use(op) => op,
111 _ => {
112 let name = b.locals[box_place_local].name.clone();
114 let ty = box_generics.types[0].clone();
115 let var = b.locals.new_var(name, ty);
116 first_block.statements.push(Statement::new(
117 assign_span,
118 StatementKind::StorageLive(var.as_local().unwrap()),
119 ));
120 first_block.statements.push(Statement::new(
121 assign_span,
122 StatementKind::Assign(var.clone(), value_to_write),
123 ));
124 Operand::Move(var)
125 }
126 };
127 first_block.statements.push(Statement::new(
128 assign_span,
129 StatementKind::StorageLive(box_place_local),
130 ));
131 first_block.terminator.kind = TerminatorKind::Call {
132 call: Call {
133 func: FnOperand::Regular(FnPtr::new(
134 FnPtrKind::Fun(FunId::Builtin(BuiltinFunId::BoxNew)),
135 box_generics,
136 )),
137 args: vec![value_to_write],
138 dest: box_place,
139 },
140 target: second_block,
141 on_unwind: unwind_target,
142 };
143
144 let second_block = b.body.get_mut(second_block).unwrap();
146 second_block.statements.get_mut(0).unwrap().kind = StatementKind::Nop;
147 second_block.statements.get_mut(1).unwrap().kind = StatementKind::Nop;
148 second_block
149 .statements
150 .get_mut(old_assign_idx)
151 .unwrap()
152 .kind = StatementKind::Nop;
153 }
154
155 b.body.dyn_visit_in_body(|rvalue: &Rvalue| {
157 if rvalue.is_shallow_init_box() {
158 register_error!(
159 ctx,
160 b.span,
161 "Could not reconstruct `Box` initialization; \
162 branching during `Box` initialization is not supported."
163 );
164 }
165 });
166 }
167}