1use crate::transform::TransformCtx;
3use crate::ullbc_ast::*;
4use derive_generic_visitor::*;
5
6use super::ctx::UllbcPass;
7
8#[derive(Visitor)]
14struct IndexVisitor<'a> {
15 locals: &'a mut Locals,
16 statements: Vec<Statement>,
18 place_mutability_stack: Vec<bool>,
23 span: Span,
25}
26
27impl<'a> IndexVisitor<'a> {
28 fn fresh_var(&mut self, name: Option<String>, ty: Ty) -> Place {
29 let var = self.locals.new_var(name, ty);
30 let live_kind = RawStatement::StorageLive(var.local_id());
31 self.statements.push(Statement::new(self.span, live_kind));
32 var
33 }
34
35 fn transform_place(&mut self, mut_access: bool, place: &mut Place) {
36 use ProjectionElem::*;
37 let Some((subplace, pe @ (Index { .. } | Subslice { .. }))) = place.as_projection() else {
38 return;
39 };
40 let TyKind::Adt(TypeId::Builtin(builtin_ty), generics) = subplace.ty().kind() else {
41 unreachable!()
42 };
43
44 let indexing_function = {
46 let builtin_fun = BuiltinFunId::Index(BuiltinIndexOp {
47 is_array: matches!(builtin_ty, BuiltinTy::Array),
48 mutability: RefKind::mutable(mut_access),
49 is_range: matches!(pe, Subslice { .. }),
50 });
51 let generics = GenericArgs {
53 regions: vec![Region::Erased].into(),
54 ..generics.clone()
55 };
56 FnOperand::Regular(FnPtr {
57 func: FunIdOrTraitMethodRef::mk_builtin(builtin_fun),
58 generics,
59 })
60 };
61
62 let input_ty = TyKind::Ref(
63 Region::Erased,
64 subplace.ty().clone(),
65 RefKind::mutable(mut_access),
66 )
67 .into_ty();
68
69 let elem_ty = generics.types[0].clone();
70 let output_inner_ty = if matches!(pe, Index { .. }) {
71 elem_ty
72 } else {
73 TyKind::Adt(
74 TypeId::Builtin(BuiltinTy::Slice),
75 GenericArgs::new_for_builtin(vec![elem_ty].into()),
76 )
77 .into_ty()
78 };
79 let output_ty = {
80 TyKind::Ref(
81 Region::Erased,
82 output_inner_ty.clone(),
83 RefKind::mutable(mut_access),
84 )
85 .into_ty()
86 };
87
88 let input_var = {
92 let input_var = self.fresh_var(None, input_ty);
93 let kind = RawStatement::Assign(
94 input_var.clone(),
95 Rvalue::Ref(subplace.clone(), BorrowKind::mutable(mut_access)),
96 );
97 self.statements.push(Statement::new(self.span, kind));
98 input_var
99 };
100
101 let mut args = vec![Operand::Move(input_var)];
103 if let Subslice { from, .. } = &pe {
104 args.push(from.as_ref().clone());
105 }
106 let (last_arg, from_end) = match &pe {
107 Index {
108 offset: x,
109 from_end,
110 ..
111 }
112 | Subslice {
113 to: x, from_end, ..
114 } => (x.as_ref().clone(), *from_end),
115 _ => unreachable!(),
116 };
117 if from_end {
118 let usize_ty = TyKind::Literal(LiteralTy::Integer(IntegerTy::Usize)).into_ty();
121 let len_var = self.fresh_var(None, usize_ty.clone());
122 let kind = RawStatement::Assign(
123 len_var.clone(),
124 Rvalue::Len(
125 subplace.clone(),
126 subplace.ty().clone(),
127 generics.const_generics.get(0.into()).cloned(),
128 ),
129 );
130 self.statements.push(Statement::new(self.span, kind));
131
132 let index_var = self.fresh_var(None, usize_ty);
136 let kind = RawStatement::Assign(
137 index_var.clone(),
138 Rvalue::BinaryOp(BinOp::Sub, Operand::Copy(len_var.clone()), last_arg),
139 );
140 self.statements.push(Statement::new(self.span, kind));
141 let dead_kind = RawStatement::StorageDead(len_var.local_id());
142 self.statements.push(Statement::new(self.span, dead_kind));
143 args.push(Operand::Copy(index_var));
144 } else {
145 args.push(last_arg);
146 }
147
148 let output_var = {
152 let output_var = self.fresh_var(None, output_ty);
153 let index_call = Call {
154 func: indexing_function,
155 args,
156 dest: output_var.clone(),
157 };
158 let kind = RawStatement::Call(index_call);
159 self.statements.push(Statement::new(self.span, kind));
160 output_var
161 };
162
163 *place = output_var.project(ProjectionElem::Deref, output_inner_ty);
165 }
166
167 fn visit_inner_with_mutability<T>(
169 &mut self,
170 x: &mut T,
171 mutability: bool,
172 ) -> ControlFlow<Infallible>
173 where
174 T: for<'s> DriveMut<'s, BodyVisitableWrapper<Self>>,
175 {
176 self.place_mutability_stack.push(mutability);
177 self.visit_inner(x)?;
178 self.place_mutability_stack.pop();
179 Continue(())
180 }
181}
182
183impl VisitBodyMut for IndexVisitor<'_> {
185 fn exit_place(&mut self, place: &mut Place) {
187 let mut_access = *self.place_mutability_stack.last().unwrap();
190 self.transform_place(mut_access, place);
191 }
192
193 fn visit_operand(&mut self, x: &mut Operand) -> ControlFlow<Infallible> {
194 match x {
195 Operand::Move(_) => self.visit_inner_with_mutability(x, true),
196 Operand::Copy(_) => self.visit_inner_with_mutability(x, false),
197 Operand::Const(..) => self.visit_inner(x),
198 }
199 }
200
201 fn visit_call(&mut self, x: &mut Call) -> ControlFlow<Infallible> {
202 self.visit_inner_with_mutability(x, true)
203 }
204
205 fn visit_fn_operand(&mut self, x: &mut FnOperand) -> ControlFlow<Infallible> {
206 match x {
207 FnOperand::Regular(_) => self.visit_inner(x),
208 FnOperand::Move(_) => self.visit_inner_with_mutability(x, true),
209 }
210 }
211
212 fn visit_rvalue(&mut self, x: &mut Rvalue) -> ControlFlow<Infallible> {
213 use Rvalue::*;
214 match x {
215 RawPtr(_, RefKind::Mut)
218 | Ref(_, BorrowKind::Mut | BorrowKind::TwoPhaseMut | BorrowKind::UniqueImmutable) => {
219 self.visit_inner_with_mutability(x, true)
220 }
221 RawPtr(_, RefKind::Shared)
222 | Ref(_, BorrowKind::Shared | BorrowKind::Shallow)
223 | Discriminant(..)
224 | Len(..) => self.visit_inner_with_mutability(x, false),
225
226 Use(_) | NullaryOp(..) | UnaryOp(..) | BinaryOp(..) | Aggregate(..) | Global(..)
227 | GlobalRef(..) | Repeat(..) | ShallowInitBox(..) => self.visit_inner(x),
228 }
229 }
230}
231
232pub struct Transform;
233
234impl UllbcPass for Transform {
293 fn transform_body(&self, _ctx: &mut TransformCtx, b: &mut ExprBody) {
294 for block in &mut b.body {
295 block.transform(|st: &mut Statement| {
297 let mut visitor = IndexVisitor {
298 locals: &mut b.locals,
299 statements: Vec::new(),
300 place_mutability_stack: Vec::new(),
301 span: st.span,
302 };
303
304 use RawStatement::*;
305 match &mut st.content {
306 Assign(..) | SetDiscriminant(..) | Drop(..) | Deinit(..) => {
307 let _ = visitor.visit_inner_with_mutability(st, true);
308 }
309 Nop | Error(..) | Assert(..) | Call(..) | StorageDead(..) | StorageLive(..) => {
310 let _ = st.drive_body_mut(&mut visitor);
311 }
312 }
313 visitor.statements
314 });
315
316 let terminator = &mut block.terminator;
318 let mut visitor = IndexVisitor {
319 locals: &mut b.locals,
320 statements: Vec::new(),
321 place_mutability_stack: Vec::new(),
322 span: terminator.span,
323 };
324 use RawTerminator::*;
325 match &mut terminator.content {
326 Switch { .. } => {
327 let _ = visitor.visit_inner_with_mutability(terminator, false);
328 }
329 Abort { .. } | Return | Goto { .. } => {}
330 }
331 block.statements.append(&mut visitor.statements);
332 }
333 }
334}