charon_lib/transform/simplify_output/
index_to_function_calls.rs1use crate::llbc_ast::*;
4use crate::transform::TransformCtx;
5use crate::transform::ctx::BodyTransformCtx;
6use derive_generic_visitor::*;
7
8use crate::transform::ctx::LlbcPass;
9
10#[derive(Visitor)]
16struct IndexVisitor<'a, 'b> {
17 locals: &'a mut Locals,
18 statements: Vec<Statement>,
20 place_mutability_stack: Vec<bool>,
25 span: Span,
27 ctx: &'b TransformCtx,
28 params: &'a GenericParams,
29}
30
31impl BodyTransformCtx for IndexVisitor<'_, '_> {
32 fn get_ctx(&self) -> &TransformCtx {
33 self.ctx
34 }
35 fn get_params(&self) -> &GenericParams {
36 self.params
37 }
38 fn get_locals_mut(&mut self) -> &mut Locals {
39 self.locals
40 }
41
42 fn insert_storage_live_stmt(&mut self, local: LocalId) {
43 let statement = StatementKind::StorageLive(local);
44 self.statements.push(Statement::new(self.span, statement));
45 }
46
47 fn insert_assn_stmt(&mut self, place: Place, rvalue: Rvalue) {
48 let statement = StatementKind::Assign(place, rvalue);
49 self.statements.push(Statement::new(self.span, statement));
50 }
51
52 fn insert_storage_dead_stmt(&mut self, local: LocalId) {
53 let statement = StatementKind::StorageDead(local);
54 self.statements.push(Statement::new(self.span, statement));
55 }
56}
57
58impl<'a, 'b> IndexVisitor<'a, 'b> {
59 fn transform_place(&mut self, mut_access: bool, place: &mut Place) {
61 use ProjectionElem::*;
62 let Some((subplace, pe @ (Index { .. } | Subslice { .. }))) = place.as_projection() else {
65 return;
66 };
67 let tref = subplace.ty.as_adt().unwrap();
68 let builtin_ty = tref.id.as_builtin().unwrap();
69
70 let indexing_function = {
72 let builtin_fun = BuiltinFunId::Index(BuiltinIndexOp {
73 is_array: matches!(builtin_ty, BuiltinTy::Array),
74 mutability: RefKind::mutable(mut_access),
75 is_range: matches!(pe, Subslice { .. }),
76 });
77 let mut generics = tref.generics.clone();
79 generics.regions = [Region::Erased].into();
80 FnOperand::Regular(FnPtr {
81 kind: Box::new(FnPtrKind::mk_builtin(builtin_fun)),
82 generics,
83 })
84 };
85
86 let input_ty = TyKind::Ref(
87 Region::Erased,
88 subplace.ty().clone(),
89 RefKind::mutable(mut_access),
90 )
91 .into_ty();
92
93 let elem_ty = tref.generics.types[0].clone();
94 let output_inner_ty = if matches!(pe, Index { .. }) {
95 elem_ty
96 } else {
97 TyKind::Adt(TypeDeclRef {
98 id: TypeId::Builtin(BuiltinTy::Slice),
99 generics: Box::new(GenericArgs::new_for_builtin(vec![elem_ty].into())),
100 })
101 .into_ty()
102 };
103 let output_ty = {
104 TyKind::Ref(
105 Region::Erased,
106 output_inner_ty.clone(),
107 RefKind::mutable(mut_access),
108 )
109 .into_ty()
110 };
111
112 let ptr_metadata = self.compute_place_metadata(subplace);
114
115 let input_var = {
119 let input_var = self.fresh_var(None, input_ty);
120 let kind = StatementKind::Assign(
121 input_var.clone(),
122 Rvalue::Ref {
123 place: subplace.clone(),
124 kind: BorrowKind::mutable(mut_access),
125 ptr_metadata,
126 },
127 );
128 self.statements.push(Statement::new(self.span, kind));
129 input_var
130 };
131
132 let mut args = vec![Operand::Move(input_var)];
134 if let Subslice { from, .. } = &pe {
135 args.push(from.as_ref().clone());
136 }
137 let (last_arg, from_end) = match &pe {
138 Index {
139 offset: x,
140 from_end,
141 ..
142 }
143 | Subslice {
144 to: x, from_end, ..
145 } => (x.as_ref().clone(), *from_end),
146 _ => unreachable!(),
147 };
148 let to_idx = self.compute_subslice_end_idx(subplace, last_arg, from_end);
149 args.push(to_idx);
150
151 let output_var = {
155 let output_var = self.fresh_var(None, output_ty);
156 let index_call = Call {
157 func: indexing_function,
158 args,
159 dest: output_var.clone(),
160 };
161 let kind = StatementKind::Call(index_call);
162 self.statements.push(Statement::new(self.span, kind));
163 output_var
164 };
165
166 *place = output_var.project(ProjectionElem::Deref, output_inner_ty);
168 }
169
170 fn visit_inner_with_mutability<T>(
172 &mut self,
173 x: &mut T,
174 mutability: bool,
175 ) -> ControlFlow<Infallible>
176 where
177 T: for<'s> DriveMut<'s, BodyVisitableWrapper<Self>> + BodyVisitable,
178 {
179 self.place_mutability_stack.push(mutability);
180 self.visit_inner(x)?;
181 self.place_mutability_stack.pop();
182 Continue(())
183 }
184}
185
186impl VisitBodyMut for IndexVisitor<'_, '_> {
188 fn exit_place(&mut self, place: &mut Place) {
190 let mut_access = *self.place_mutability_stack.last().unwrap();
193 self.transform_place(mut_access, place);
194 }
195
196 fn visit_operand(&mut self, x: &mut Operand) -> ControlFlow<Infallible> {
197 match x {
198 Operand::Move(_) => self.visit_inner_with_mutability(x, true),
199 Operand::Copy(_) => self.visit_inner_with_mutability(x, false),
200 Operand::Const(..) => self.visit_inner(x),
201 }
202 }
203
204 fn visit_call(&mut self, x: &mut Call) -> ControlFlow<Infallible> {
205 self.visit_inner_with_mutability(x, true)
206 }
207
208 fn visit_fn_operand(&mut self, x: &mut FnOperand) -> ControlFlow<Infallible> {
209 match x {
210 FnOperand::Regular(_) => self.visit_inner(x),
211 FnOperand::Move(_) => self.visit_inner_with_mutability(x, true),
212 }
213 }
214
215 fn visit_rvalue(&mut self, x: &mut Rvalue) -> ControlFlow<Infallible> {
216 use Rvalue::*;
217 match x {
218 RawPtr {
221 kind: RefKind::Mut, ..
222 }
223 | Ref {
224 kind: BorrowKind::Mut | BorrowKind::TwoPhaseMut | BorrowKind::UniqueImmutable,
225 ..
226 } => self.visit_inner_with_mutability(x, true),
227 RawPtr {
228 kind: RefKind::Shared,
229 ..
230 }
231 | Ref {
232 kind: BorrowKind::Shared | BorrowKind::Shallow,
233 ..
234 }
235 | Discriminant(..)
236 | Len(..) => self.visit_inner_with_mutability(x, false),
237
238 Use(_) | NullaryOp(..) | UnaryOp(..) | BinaryOp(..) | Aggregate(..) | Repeat(..)
239 | ShallowInitBox(..) => self.visit_inner(x),
240 }
241 }
242
243 fn visit_llbc_block(&mut self, _: &mut llbc_ast::Block) -> ControlFlow<Infallible> {
244 ControlFlow::Continue(())
245 }
246}
247
248pub struct Transform;
249
250impl Transform {
251 fn transform_body_with_param(
252 &self,
253 ctx: &mut TransformCtx,
254 b: &mut ExprBody,
255 params: &GenericParams,
256 ) {
257 b.body.transform(|st: &mut Statement| {
258 let mut visitor = IndexVisitor {
259 locals: &mut b.locals,
260 statements: Vec::new(),
261 place_mutability_stack: Vec::new(),
262 span: st.span,
263 ctx: &ctx,
264 params,
265 };
266 use StatementKind::*;
267 match &mut st.kind {
268 Assign(..)
269 | SetDiscriminant(..)
270 | CopyNonOverlapping(_)
271 | Drop(..)
272 | Deinit(..)
273 | Call(..) => {
274 let _ = visitor.visit_inner_with_mutability(st, true);
275 }
276 Switch(..) => {
277 let _ = visitor.visit_inner_with_mutability(st, false);
278 }
279 Nop | Error(..) | Assert(..) | Abort(..) | StorageDead(..) | StorageLive(..)
280 | Return | Break(..) | Continue(..) | Loop(..) => {
281 let _ = st.drive_body_mut(&mut visitor);
282 }
283 }
284 visitor.statements
285 });
286 }
287}
288
289impl LlbcPass for Transform {
348 fn transform_function(&self, ctx: &mut TransformCtx, decl: &mut FunDecl) {
349 if let Ok(body) = &mut decl.body {
350 self.transform_body_with_param(
351 ctx,
352 body.as_structured_mut().unwrap(),
353 &decl.signature.generics,
354 )
355 }
356 }
357}