Skip to main content

charon_lib/transform/simplify_output/
builtins_to_function_calls.rs

1//! Desugar built-in operations and array/slice indexing to standard library function calls.
2
3use std::collections::{HashMap, HashSet};
4
5use crate::llbc_ast::*;
6use crate::name_matcher::NamePattern;
7use crate::transform::ctx::{BodyTransformCtx, LlbcStatementTransformCtx};
8use crate::transform::{CowBox, TransformCtx};
9use derive_generic_visitor::*;
10use itertools::Itertools;
11
12use crate::transform::ctx::LlbcPass;
13
14fn mk_fn_ptr(ctx: &TransformCtx, id: ItemId, mut generics: GenericArgs) -> FnPtr {
15    if ctx.options.add_destruct_bounds
16        && let Some(item) = ctx.translated.get_item(id)
17    {
18        // Charon adds `Destruct` as the last trait clause of every generic item.
19        let trait_decl_ref = item
20            .generic_params()
21            .trait_clauses
22            .last()
23            .unwrap()
24            .trait_
25            .clone()
26            .substitute(&generics);
27        let kind = TraitRefKind::BuiltinOrAuto {
28            builtin_data: BuiltinImplData::UntrackedDestruct,
29            parent_trait_refs: Default::default(),
30            types: Default::default(),
31            vtable: None,
32        };
33        generics
34            .trait_refs
35            .push(PolyTraitRef::new(kind, trait_decl_ref));
36    }
37    let fun_id = *id.as_fun().unwrap();
38    FnPtr::new(FnPtrKind::Fun(fun_id), generics)
39}
40
41/// Instantiate the trait impl that provides the given method.
42fn method_impl_trait_ref(
43    ctx: &TransformCtx,
44    id: ItemId,
45    fun_generics: &GenericArgs,
46) -> Option<TraitRef> {
47    let fun_id = *id.as_fun()?;
48    let fun = ctx.translated.fun_decls.get(fun_id)?;
49    let FunSource::TraitImpl { impl_ref, .. } = &fun.src else {
50        return None;
51    };
52    let impl_ref = impl_ref.clone().substitute(fun_generics);
53    let trait_impl = ctx.translated.trait_impls.get(impl_ref.id)?;
54    let trait_decl_ref = trait_impl.impl_trait.clone().substitute(&impl_ref.generics);
55    Some(TraitRef::new(
56        TraitRefKind::TraitImpl(impl_ref),
57        trait_decl_ref,
58    ))
59}
60
61fn index_method_types(
62    params: &GenericParams,
63    elem_ty: &Ty,
64    index_ty: &Ty,
65    output_ty: &Ty,
66) -> Option<IndexVec<TypeVarId, Ty>> {
67    let mut types: IndexVec<TypeVarId, Ty> =
68        [elem_ty.clone(), index_ty.clone()].into_iter().collect();
69    match params.types.len() {
70        // With `--lift-associated-types`, `Index::Output` is an explicit type parameter.
71        3 => {
72            types.push(output_ty.clone());
73        }
74        2 => {}
75        _ => return None,
76    }
77    Some(types)
78}
79
80fn transform_operation(std_items: &Transform, ctx: &TransformCtx, statement: &mut Statement) {
81    match &statement.kind {
82        // Transform the ArrayToSlice unop.
83        StatementKind::Assign(
84            place,
85            Rvalue::UnaryOp(
86                UnOp::Cast(CastKind::Unsize(tgt_ty, UnsizingMetadata::Length(_), _)),
87                operand,
88            ),
89        ) => {
90            if let (TyKind::Ref(_, src_ty, src_kind), TyKind::Ref(_, tgt_ty, tgt_kind)) =
91                (operand.ty().kind(), tgt_ty.kind())
92                && let TyKind::Array(elem_ty, len, elem_ty_is_sized) = src_ty.kind()
93                && let TyKind::Slice(..) = tgt_ty.kind()
94            {
95                // In MIR terminology, we go from &[T; l] to &[T] which means we
96                // effectively "unsize" the type, as `l` no longer appears in the
97                // destination type. At runtime, the converse happens: the length
98                // materializes into the fat pointer.
99                assert!(src_kind == tgt_kind);
100                // We could avoid the clone operations below if we take the content of
101                // the statement. In practice, this shouldn't have much impact.
102                let item = match src_kind {
103                    RefKind::Shared => StdItem::ArrayAsSlice,
104                    RefKind::Mut => StdItem::ArrayAsMutSlice,
105                };
106                let Some(&fun_id) = std_items.item_map.get(&item) else {
107                    return;
108                };
109                let generics = GenericArgs::new(
110                    [Region::Erased].into(),
111                    [elem_ty.clone()].into(),
112                    [len.clone()].into(),
113                    elem_ty_is_sized
114                        .iter()
115                        .cloned()
116                        .map(PolyTraitRef::empty)
117                        .collect(),
118                );
119                statement.kind = StatementKind::Call {
120                    call: Call {
121                        func: FnOperand::Regular(mk_fn_ptr(ctx, fun_id, generics)),
122                        args: vec![operand.clone()],
123                        dest: place.clone(),
124                        safety: CallSafety::Inherit,
125                    },
126                    on_unwind: Block::new_unreachable(statement.span),
127                };
128            }
129        }
130        // Transform the array aggregates to function calls.
131        StatementKind::Assign(place, Rvalue::Repeat(operand, ty, len, ty_is_copy)) => {
132            let Some(ty_is_copy) = ty_is_copy else {
133                return;
134            };
135            // We could avoid the clone operations below if we take the content of
136            // the statement. In practice, this shouldn't have much impact.
137            let Some(&fun_id) = std_items.item_map.get(&StdItem::ArrayRepeat) else {
138                return;
139            };
140            let TyKind::Array(_, _, ty_is_sized) = place.ty().kind() else {
141                return;
142            };
143            // `Copy`'s explicit `Clone` supertrait follows its optional implicit marker clause.
144            // `array::repeat` has the corresponding `Clone` bound at the same position.
145            let clone_clause_id = TraitClauseId::new(usize::from(ty_is_sized.is_some()));
146            let Some(ty_is_clone) = ty_is_copy
147                .clone()
148                .project_parent_clause(&ctx.translated, clone_clause_id)
149            else {
150                return;
151            };
152            let generics = GenericArgs::new(
153                [].into(),
154                [ty.clone()].into(),
155                [len.clone()].into(),
156                ty_is_sized
157                    .iter()
158                    .cloned()
159                    .map(PolyTraitRef::empty)
160                    .chain([ty_is_clone])
161                    .collect(),
162            );
163            statement.kind = StatementKind::Call {
164                call: Call {
165                    func: FnOperand::Regular(mk_fn_ptr(ctx, fun_id, generics)),
166                    args: vec![operand.clone()],
167                    dest: place.clone(),
168                    safety: CallSafety::Inherit,
169                },
170                on_unwind: Block::new_unreachable(statement.span),
171            };
172        }
173        _ => {}
174    }
175}
176
177/// We replace some place constructors with function calls. To do that, we explore all the places
178/// in a body and deconstruct a given place access into intermediate assignments.
179///
180/// We accumulate the new assignments as statements in the visitor, and at the end we insert these
181/// statements before the one that was just explored.
182#[derive(Visitor)]
183struct IndexVisitor<'a, 'b> {
184    ctx: &'b mut LlbcStatementTransformCtx<'a>,
185    std_items: &'b Transform,
186    // When we visit a place, we need to know if it is being accessed mutably or not. Whenever we
187    // visit something that contains a place we push the relevant mutability on this stack.
188    // Unfortunately this requires us to be very careful to catch all the cases where we see
189    // places.
190    place_mutability_stack: Vec<bool>,
191}
192
193impl<'a, 'b> IndexVisitor<'a, 'b> {
194    /// transform `place: subplace[i]` into indexing function calls for `subplace` and `i`
195    fn transform_place(&mut self, mut_access: bool, place: &mut Place) {
196        use ProjectionElem::*;
197        // This function is naturally called recusively, so `subplace` cannot be another `Index` or `Subslice`.
198        // Hence, `subplace`, if still projecting, must be either a `Deref` or a `Field`.
199        let Some((subplace, pe @ (Index { .. } | Subslice { .. }))) = place.as_projection() else {
200            return;
201        };
202
203        let (ty, len, ty_is_sized) = match subplace.ty.kind() {
204            TyKind::Array(ty, len, ty_is_sized) => (ty.clone(), Some(len.clone()), ty_is_sized),
205            TyKind::Slice(ty, ty_is_sized) => (ty.clone(), None, ty_is_sized),
206            _ => unreachable!("Indexing can only be done on arrays or slices"),
207        };
208
209        let mutability = RefKind::mutable(mut_access);
210        let item = match (pe.is_subslice(), mutability) {
211            (false, RefKind::Shared) => StdItem::SliceIndex,
212            (false, RefKind::Mut) => StdItem::SliceIndexMut,
213            (true, RefKind::Shared) => StdItem::RangeIndex,
214            (true, RefKind::Mut) => StdItem::RangeIndexMut,
215        };
216        let Some(&index_fun_id) = self.std_items.item_map.get(&item) else {
217            return;
218        };
219        let Some(index_fun) = index_fun_id
220            .as_fun()
221            .and_then(|id| self.ctx.ctx.translated.fun_decls.get(*id))
222        else {
223            return;
224        };
225        let index_generics = GenericArgs::new(
226            [Region::Erased].into(),
227            [ty.clone()].into(),
228            [].into(),
229            ty_is_sized
230                .iter()
231                .cloned()
232                .map(PolyTraitRef::empty)
233                .collect(),
234        );
235        let index_fn_ptr = mk_fn_ptr(self.ctx.ctx, index_fun_id, index_generics);
236        let index_ty = index_fun.signature.inputs[0]
237            .clone()
238            .substitute(&index_fn_ptr.generics);
239
240        let output_inner_ty = if matches!(pe, Index { .. }) {
241            ty.clone()
242        } else {
243            TyKind::Slice(ty.clone(), ty_is_sized.clone()).into_ty()
244        };
245        let output_ty = {
246            TyKind::Ref(
247                Region::Erased,
248                output_inner_ty.clone(),
249                RefKind::mutable(mut_access),
250            )
251            .into_ty()
252        };
253
254        // Push the statements:
255        // `storage_live(tmp0)`
256        // `tmp0 = &{mut}p`
257        let input_var =
258            self.ctx
259                .borrow_to_new_var(subplace.clone(), BorrowKind::mutable(mut_access), None);
260
261        // Construct the arguments to pass to the indexing function.
262        let (last_arg, from_end) = match &pe {
263            Index {
264                offset: x,
265                from_end,
266                ..
267            }
268            | Subslice {
269                to: x, from_end, ..
270            } => (x.as_ref().clone(), *from_end),
271            _ => unreachable!(),
272        };
273        let to_idx = self
274            .ctx
275            .compute_subslice_end_idx(subplace, last_arg, from_end);
276        let index = match &pe {
277            Index { .. } => to_idx,
278            Subslice { from, .. } => {
279                let Some(range_ref) = index_ty.as_adt().cloned() else {
280                    return;
281                };
282                let range_ty = TyKind::Adt(range_ref.clone()).into_ty();
283                let range_var = self.ctx.fresh_var(None, range_ty);
284                self.ctx.insert_assn_stmt(
285                    range_var.clone(),
286                    Rvalue::Aggregate(
287                        AggregateKind::Adt(range_ref, None, None),
288                        vec![from.as_ref().clone(), to_idx],
289                    ),
290                );
291                Operand::Move(range_var)
292            }
293            _ => unreachable!(),
294        };
295
296        let (index_fn_ptr, args) = if let Some(len) = len {
297            let (array_item, slice_item) = match mutability {
298                RefKind::Shared => (StdItem::ArrayIndex, StdItem::SliceIndexImpl),
299                RefKind::Mut => (StdItem::ArrayIndexMut, StdItem::SliceIndexMutImpl),
300            };
301            let Some(&array_fun_id) = self.std_items.item_map.get(&array_item) else {
302                return;
303            };
304            let Some(array_fun) = array_fun_id
305                .as_fun()
306                .and_then(|id| self.ctx.ctx.translated.fun_decls.get(*id))
307            else {
308                return;
309            };
310            let Some(&slice_fun_id) = self.std_items.item_map.get(&slice_item) else {
311                return;
312            };
313            let Some(slice_fun) = slice_fun_id
314                .as_fun()
315                .and_then(|id| self.ctx.ctx.translated.fun_decls.get(*id))
316            else {
317                return;
318            };
319
320            let Some(slice_types) =
321                index_method_types(&slice_fun.generics, &ty, &index_ty, &output_inner_ty)
322            else {
323                return;
324            };
325            let mut slice_generics =
326                GenericArgs::new([Region::Erased].into(), slice_types, [].into(), [].into());
327            let Some(slice_index_trait_ref) =
328                method_impl_trait_ref(self.ctx.ctx, index_fun_id, &index_fn_ptr.generics)
329            else {
330                return;
331            };
332            let index_ty_is_sized = if ty_is_sized.is_some() {
333                let Some(clause) = slice_fun.generics.trait_clauses.get(TraitClauseId::new(1))
334                else {
335                    return;
336                };
337                let trait_decl_ref = clause.trait_.clone().substitute_explicits(&slice_generics);
338                let Some(meta_sized) = slice_index_trait_ref
339                    .clone()
340                    .project_parent_clause(&self.ctx.ctx.translated, TraitClauseId::ZERO)
341                else {
342                    return;
343                };
344                Some(TraitRef::new(
345                    TraitRefKind::BuiltinOrAuto {
346                        builtin_data: BuiltinImplData::Sized,
347                        parent_trait_refs: [meta_sized].into_iter().collect(),
348                        types: Default::default(),
349                        vtable: None,
350                    },
351                    trait_decl_ref.no_bound_vars(),
352                ))
353            } else {
354                None
355            };
356            slice_generics.trait_refs = ty_is_sized
357                .iter()
358                .cloned()
359                .chain(index_ty_is_sized.iter().cloned())
360                .chain([slice_index_trait_ref])
361                .map(PolyTraitRef::empty)
362                .collect();
363            let slice_fn_ptr = mk_fn_ptr(self.ctx.ctx, slice_fun_id, slice_generics);
364            let Some(slice_trait_ref) =
365                method_impl_trait_ref(self.ctx.ctx, slice_fun_id, &slice_fn_ptr.generics)
366            else {
367                return;
368            };
369
370            let Some(array_types) =
371                index_method_types(&array_fun.generics, &ty, &index_ty, &output_inner_ty)
372            else {
373                return;
374            };
375            let array_generics = GenericArgs::new(
376                [Region::Erased].into(),
377                array_types,
378                [len].into(),
379                ty_is_sized
380                    .iter()
381                    .cloned()
382                    .chain(index_ty_is_sized)
383                    .chain([slice_trait_ref])
384                    .map(PolyTraitRef::empty)
385                    .collect(),
386            );
387            (
388                mk_fn_ptr(self.ctx.ctx, array_fun_id, array_generics),
389                vec![Operand::Move(input_var), index],
390            )
391        } else {
392            (index_fn_ptr, vec![index, Operand::Move(input_var)])
393        };
394
395        // Call the indexing function:
396        // `storage_live(tmp1)`
397        // `tmp1 = {Array,Slice}{Mut,Shared}{Index,SubSlice}(move tmp0, <other args>)`
398        let output_var = {
399            let output_var = self.ctx.fresh_var(None, output_ty);
400            let index_call = Call {
401                func: FnOperand::Regular(index_fn_ptr),
402                args,
403                dest: output_var.clone(),
404                safety: CallSafety::Inherit,
405            };
406            let kind = StatementKind::Call {
407                call: index_call,
408                on_unwind: Block::new_unreachable(self.ctx.span),
409            };
410            self.ctx
411                .statements
412                .push(Statement::new(self.ctx.span, kind));
413            output_var
414        };
415
416        // Slice patterns make array subslices that are themselves arrays. We cast the slice
417        // returned by indexing into that array.
418        let output_var =
419            if matches!(pe, Subslice { .. }) && matches!(place.ty().kind(), TyKind::Array(..)) {
420                let array_ref_ty =
421                    TyKind::Ref(Region::Erased, place.ty().clone(), mutability).into_ty();
422                let array_ref_var = self.ctx.fresh_var(None, array_ref_ty.clone());
423                self.ctx.insert_assn_stmt(
424                    array_ref_var.clone(),
425                    Rvalue::UnaryOp(
426                        UnOp::Cast(CastKind::Concretize(array_ref_ty, vec![])),
427                        Operand::Move(output_var),
428                    ),
429                );
430                array_ref_var
431            } else {
432                output_var
433            };
434        *place = output_var.project(ProjectionElem::Deref, place.ty().clone());
435    }
436
437    /// Calls `self.visit_inner()` with `mutability` pushed on the stack.
438    fn visit_inner_with_mutability<T>(
439        &mut self,
440        x: &mut T,
441        mutability: bool,
442    ) -> ControlFlow<Infallible>
443    where
444        T: for<'s> DriveMut<'s, BodyVisitableWrapper<Self>> + BodyVisitable,
445    {
446        self.place_mutability_stack.push(mutability);
447        self.visit_inner(x)?;
448        self.place_mutability_stack.pop();
449        Continue(())
450    }
451}
452
453/// The visitor methods.
454impl VisitBodyMut for IndexVisitor<'_, '_> {
455    /// We explore places from the inside-out --- recursion naturally happens here.
456    fn exit_place(&mut self, place: &mut Place) {
457        // We have intercepted every traversal that would reach a place and pushed the correct
458        // mutability on the stack.
459        let mut_access = *self.place_mutability_stack.last().unwrap();
460        self.transform_place(mut_access, place);
461    }
462
463    fn visit_operand(&mut self, x: &mut Operand) -> ControlFlow<Infallible> {
464        match x {
465            Operand::Move(_) => self.visit_inner_with_mutability(x, true),
466            Operand::Copy(_) => self.visit_inner_with_mutability(x, false),
467            Operand::Const(..) => self.visit_inner(x),
468        }
469    }
470
471    fn visit_call(&mut self, x: &mut Call) -> ControlFlow<Infallible> {
472        self.visit_inner_with_mutability(x, true)
473    }
474
475    fn visit_asm_operand(&mut self, x: &mut AsmOperand) -> ControlFlow<Infallible> {
476        match x {
477            AsmOperand::Out { .. } | AsmOperand::InOut { .. } => {
478                self.visit_inner_with_mutability(x, true)
479            }
480            _ => self.visit_inner(x),
481        }
482    }
483
484    fn visit_fn_operand(&mut self, x: &mut FnOperand) -> ControlFlow<Infallible> {
485        match x {
486            FnOperand::Regular(_) => self.visit_inner(x),
487            FnOperand::Dynamic(..) => self.visit_inner_with_mutability(x, true),
488        }
489    }
490
491    fn visit_rvalue(&mut self, x: &mut Rvalue) -> ControlFlow<Infallible> {
492        use Rvalue::*;
493        match x {
494            // `UniqueImmutable` de facto gives mutable access and only shows up if there is nested
495            // mutable access.
496            RawPtr {
497                kind: RefKind::Mut, ..
498            }
499            | Ref {
500                kind: BorrowKind::Mut | BorrowKind::TwoPhaseMut | BorrowKind::UniqueImmutable,
501                ..
502            } => self.visit_inner_with_mutability(x, true),
503            RawPtr {
504                kind: RefKind::Shared,
505                ..
506            }
507            | Ref {
508                kind: BorrowKind::Shared | BorrowKind::Shallow,
509                ..
510            }
511            | Discriminant(..)
512            | Len(..) => self.visit_inner_with_mutability(x, false),
513
514            Use(..) | NullaryOp(..) | UnaryOp(..) | BinaryOp(..) | Aggregate(..) | Repeat(..) => {
515                self.visit_inner(x)
516            }
517        }
518    }
519
520    fn visit_llbc_block(&mut self, _: &mut llbc_ast::Block) -> ControlFlow<Infallible> {
521        ControlFlow::Continue(())
522    }
523}
524
525/// We do the following.
526///
527/// If `p` is a projection (for instance: `var`, `*var`, `var.f`, etc.), we
528/// detect:
529/// - whether it operates on a slice or an array (we keep track of the types)
530/// - whether the access might mutate the value or not (it is
531///   the case if it is in a `move`, `&mut` or at the lhs of an assignment),
532///   and do the following transformations
533///
534/// ```text
535///   // If array and mutable access:
536///   ... p[i] ...
537///      ~~>
538///   tmp0 = &mut p
539///   tmp1 = <[_; _] as IndexMut<_>>::index_mut(move tmp0, i)
540///   ... *tmp1 ...
541///
542///   // If array and non-mutable access:
543///   ... p[i] ...
544///      ~~>
545///   tmp0 := & p
546///   tmp1 := <[_; _] as Index<_>>::index(move tmp0, i)
547///   ... *tmp1 ...
548///
549///   // Omitting the slice cases, which are similar
550/// ```
551///
552/// For instance, it leads to the following transformations:
553/// ```text
554///   // x : [u32; N]
555///   y : u32 = copy x[i]
556///      ~~>
557///   tmp0 : & [u32; N] := &x
558///   tmp1 : &u32 = <[_; _] as Index<_>>::index(move tmp0, i)
559///   y : u32 = copy (*tmp1)
560///
561///   // x : &[T; N]
562///   y : &T = & (*x)[i]
563///      ~~>
564///   tmp0 : & [T; N] := & (*x)
565///   tmp1 : &T = <[_; _] as Index<_>>::index(move tmp0, i)
566///   y : &T = & (*tmp1)
567///
568///   // x : [u32; N]
569///   y = &mut x[i]
570///      ~~>
571///   tmp0 : &mut [u32; N] := &mut x
572///   tmp1 : &mut u32 := <[_; _] as IndexMut<_>>::index_mut(move tmp0, i)
573///   y = &mut (*tmp)
574///
575///   // When using an index on the lhs:
576///   // y : [T; N]
577///   y[i] = x
578///      ~~>
579///   tmp0 : &mut [T; N] := &mut y;
580///   tmp1 : &mut T = <[_; _] as IndexMut<_>>::index_mut(move tmp0, i)
581///   *tmp1 = x
582/// ```
583#[derive(Copy, Clone, PartialEq, Eq, Hash)]
584enum StdItem {
585    ArrayAsSlice,
586    ArrayAsMutSlice,
587    ArrayRepeat,
588    ArrayIndex,
589    ArrayIndexMut,
590    SliceIndexImpl,
591    SliceIndexMutImpl,
592    SliceIndex,
593    SliceIndexMut,
594    RangeIndex,
595    RangeIndexMut,
596}
597
598pub struct Transform {
599    item_map: HashMap<StdItem, ItemId>,
600    item_set: HashSet<ItemId>,
601}
602
603impl Transform {
604    pub fn new(ctx: &TransformCtx) -> CowBox<dyn LlbcPass> {
605        use StdItem::*;
606
607        let mut matches: [(StdItem, NamePattern, Vec<ItemId>); _] = [
608            (ArrayAsSlice, "core::array::_::as_slice"),
609            (ArrayAsMutSlice, "core::array::_::as_mut_slice"),
610            (ArrayRepeat, "core::array::repeat"),
611            (
612                SliceIndex,
613                "core::slice::index::{impl core::slice::index::SliceIndex<_> for usize}::index",
614            ),
615            (
616                SliceIndexMut,
617                "core::slice::index::{impl core::slice::index::SliceIndex<_> for usize}::index_mut",
618            ),
619            (
620                RangeIndex,
621                "core::slice::index::{impl core::slice::index::SliceIndex<_> for core::ops::range::Range<usize>}::index",
622            ),
623            (
624                RangeIndexMut,
625                "core::slice::index::{impl core::slice::index::SliceIndex<_> for core::ops::range::Range<usize>}::index_mut",
626            ),
627        ]
628        .map(|(item, path)| (item, NamePattern::parse(path).unwrap(), Vec::new()));
629
630        // Resolve the items
631        for (id, name) in &ctx.translated.item_names {
632            for (_, pattern, found) in &mut matches {
633                if pattern.matches(&ctx.translated, name) {
634                    found.push(*id);
635                }
636            }
637        }
638
639        let mut index_impl_methods: HashMap<StdItem, Vec<ItemId>> = HashMap::new();
640        for fun in &ctx.translated.fun_decls {
641            let FunSource::TraitImpl { trait_ref, .. } = &fun.src else {
642                continue;
643            };
644            let Some(trait_decl) = ctx.translated.trait_decls.get(trait_ref.id) else {
645                continue;
646            };
647            let Some(self_ty) = trait_ref.self_ty(&ctx.translated) else {
648                continue;
649            };
650            let item = match (&trait_decl.item_meta.lang_item, self_ty.kind()) {
651                (Some(crate::ast::from_rustc::LangItem::Index), TyKind::Array(..)) => ArrayIndex,
652                (Some(crate::ast::from_rustc::LangItem::IndexMut), TyKind::Array(..)) => {
653                    ArrayIndexMut
654                }
655                (Some(crate::ast::from_rustc::LangItem::Index), TyKind::Slice(..)) => {
656                    SliceIndexImpl
657                }
658                (Some(crate::ast::from_rustc::LangItem::IndexMut), TyKind::Slice(..)) => {
659                    SliceIndexMutImpl
660                }
661                _ => continue,
662            };
663            index_impl_methods
664                .entry(item)
665                .or_default()
666                .push(ItemId::Fun(fun.def_id));
667        }
668        let item_map = matches
669            .into_iter()
670            .map(|(item, _, found)| (item, found))
671            .chain(index_impl_methods)
672            .filter_map(|(item, found)| found.into_iter().exactly_one().ok().map(|id| (item, id)))
673            .collect::<HashMap<_, _>>();
674        let item_set = item_map.values().copied().collect();
675        CowBox::Owned(Box::new(Self { item_map, item_set }))
676    }
677}
678
679impl LlbcPass for Transform {
680    fn should_run(&self, options: &crate::options::TranslateOptions) -> bool {
681        options.ops_to_function_calls || options.index_to_function_calls
682    }
683
684    fn transform_function(&self, ctx: &mut TransformCtx, decl: &mut FunDecl) {
685        if self.item_set.contains(&ItemId::Fun(decl.def_id)) {
686            return;
687        }
688        let Some(body) = decl.body.as_structured_mut() else {
689            return;
690        };
691        if ctx.options.ops_to_function_calls {
692            body.body
693                .visit_statements(&mut |statement: &mut Statement| {
694                    transform_operation(self, ctx, statement)
695                });
696        }
697        if ctx.options.index_to_function_calls {
698            decl.transform_llbc_statements(ctx, |ctx, st: &mut Statement| {
699                let mut visitor = IndexVisitor {
700                    ctx,
701                    std_items: self,
702                    place_mutability_stack: Vec::new(),
703                };
704                use StatementKind::*;
705                match &mut st.kind {
706                    Assign(..) | SetDiscriminant(..) | Drop { .. } | Call { .. } => {
707                        let _ = visitor.visit_inner_with_mutability(st, true);
708                    }
709                    Switch { .. } | PlaceMention(..) | Borrowck(..) => {
710                        let _ = visitor.visit_inner_with_mutability(st, false);
711                    }
712                    Nop
713                    | UnwindResume
714                    | InlineAsm { .. }
715                    | Assert { .. }
716                    | Panic { .. }
717                    | UndefinedBehavior
718                    | UnwindTerminate
719                    | StorageDead(..)
720                    | StorageLive(..)
721                    | Return
722                    | Break(..)
723                    | Continue(..)
724                    | Loop(..) => {
725                        let _ = st.drive_body_mut(&mut visitor);
726                    }
727                }
728            })
729        }
730    }
731}