1use 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 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
41fn 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 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 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 assert!(src_kind == tgt_kind);
100 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 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 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 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#[derive(Visitor)]
183struct IndexVisitor<'a, 'b> {
184 ctx: &'b mut LlbcStatementTransformCtx<'a>,
185 std_items: &'b Transform,
186 place_mutability_stack: Vec<bool>,
191}
192
193impl<'a, 'b> IndexVisitor<'a, 'b> {
194 fn transform_place(&mut self, mut_access: bool, place: &mut Place) {
196 use ProjectionElem::*;
197 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 let input_var =
258 self.ctx
259 .borrow_to_new_var(subplace.clone(), BorrowKind::mutable(mut_access), None);
260
261 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 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 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 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
453impl VisitBodyMut for IndexVisitor<'_, '_> {
455 fn exit_place(&mut self, place: &mut Place) {
457 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 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#[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 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}