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(TraitRef::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 =
55 RegionBinder::empty(trait_impl.impl_trait.clone().substitute(&impl_ref.generics));
56 Some(TraitRef::new(
57 TraitRefKind::TraitImpl(impl_ref),
58 trait_decl_ref,
59 ))
60}
61
62fn index_method_types(
63 params: &GenericParams,
64 elem_ty: &Ty,
65 index_ty: &Ty,
66 output_ty: &Ty,
67) -> Option<IndexVec<TypeVarId, Ty>> {
68 let mut types: IndexVec<TypeVarId, Ty> =
69 [elem_ty.clone(), index_ty.clone()].into_iter().collect();
70 match params.types.len() {
71 3 => {
73 types.push(output_ty.clone());
74 }
75 2 => {}
76 _ => return None,
77 }
78 Some(types)
79}
80
81fn transform_operation(std_items: &Transform, ctx: &TransformCtx, statement: &mut Statement) {
82 match &statement.kind {
83 StatementKind::Assign(
85 place,
86 Rvalue::UnaryOp(
87 UnOp::Cast(CastKind::Unsize(src_ty, tgt_ty, UnsizingMetadata::Length(_))),
88 operand,
89 ),
90 ) => {
91 if let (TyKind::Ref(_, src_ty, src_kind), TyKind::Ref(_, tgt_ty, tgt_kind)) =
92 (src_ty.kind(), tgt_ty.kind())
93 && let TyKind::Array(elem_ty, len, elem_ty_is_sized) = src_ty.kind()
94 && let TyKind::Slice(..) = tgt_ty.kind()
95 {
96 assert!(src_kind == tgt_kind);
101 let item = match src_kind {
104 RefKind::Shared => StdItem::ArrayAsSlice,
105 RefKind::Mut => StdItem::ArrayAsMutSlice,
106 };
107 let Some(&fun_id) = std_items.item_map.get(&item) else {
108 return;
109 };
110 let generics = GenericArgs::new(
111 [Region::Erased].into(),
112 [elem_ty.clone()].into(),
113 [len.clone()].into(),
114 elem_ty_is_sized.iter().cloned().collect(),
115 );
116 statement.kind = StatementKind::Call {
117 call: Call {
118 func: FnOperand::Regular(mk_fn_ptr(ctx, fun_id, generics)),
119 args: vec![operand.clone()],
120 dest: place.clone(),
121 safety: CallSafety::Inherit,
122 },
123 on_unwind: Block::new_unreachable(statement.span),
124 };
125 }
126 }
127 StatementKind::Assign(place, Rvalue::Repeat(operand, ty, len, ty_is_copy)) => {
129 let Some(ty_is_copy) = ty_is_copy else {
130 return;
131 };
132 let Some(&fun_id) = std_items.item_map.get(&StdItem::ArrayRepeat) else {
135 return;
136 };
137 let TyKind::Array(_, _, ty_is_sized) = place.ty().kind() else {
138 return;
139 };
140 let clone_clause_id = TraitClauseId::new(usize::from(ty_is_sized.is_some()));
143 let Some(ty_is_clone) = ty_is_copy
144 .clone()
145 .project_parent_clause(&ctx.translated, clone_clause_id)
146 else {
147 return;
148 };
149 let generics = GenericArgs::new(
150 [].into(),
151 [ty.clone()].into(),
152 [len.clone()].into(),
153 ty_is_sized.iter().cloned().chain([ty_is_clone]).collect(),
154 );
155 statement.kind = StatementKind::Call {
156 call: Call {
157 func: FnOperand::Regular(mk_fn_ptr(ctx, fun_id, generics)),
158 args: vec![operand.clone()],
159 dest: place.clone(),
160 safety: CallSafety::Inherit,
161 },
162 on_unwind: Block::new_unreachable(statement.span),
163 };
164 }
165 _ => {}
166 }
167}
168
169#[derive(Visitor)]
175struct IndexVisitor<'a, 'b> {
176 ctx: &'b mut LlbcStatementTransformCtx<'a>,
177 std_items: &'b Transform,
178 place_mutability_stack: Vec<bool>,
183}
184
185impl<'a, 'b> IndexVisitor<'a, 'b> {
186 fn transform_place(&mut self, mut_access: bool, place: &mut Place) {
188 use ProjectionElem::*;
189 let Some((subplace, pe @ (Index { .. } | Subslice { .. }))) = place.as_projection() else {
192 return;
193 };
194
195 let (ty, len, ty_is_sized) = match subplace.ty.kind() {
196 TyKind::Array(ty, len, ty_is_sized) => (ty.clone(), Some(len.clone()), ty_is_sized),
197 TyKind::Slice(ty, ty_is_sized) => (ty.clone(), None, ty_is_sized),
198 _ => unreachable!("Indexing can only be done on arrays or slices"),
199 };
200
201 let mutability = RefKind::mutable(mut_access);
202 let item = match (pe.is_subslice(), mutability) {
203 (false, RefKind::Shared) => StdItem::SliceIndex,
204 (false, RefKind::Mut) => StdItem::SliceIndexMut,
205 (true, RefKind::Shared) => StdItem::RangeIndex,
206 (true, RefKind::Mut) => StdItem::RangeIndexMut,
207 };
208 let Some(&index_fun_id) = self.std_items.item_map.get(&item) else {
209 return;
210 };
211 let Some(index_fun) = index_fun_id
212 .as_fun()
213 .and_then(|id| self.ctx.ctx.translated.fun_decls.get(*id))
214 else {
215 return;
216 };
217 let index_generics = GenericArgs::new(
218 [Region::Erased].into(),
219 [ty.clone()].into(),
220 [].into(),
221 ty_is_sized.iter().cloned().collect(),
222 );
223 let index_fn_ptr = mk_fn_ptr(self.ctx.ctx, index_fun_id, index_generics);
224 let index_ty = index_fun.signature.inputs[0]
225 .clone()
226 .substitute(&index_fn_ptr.generics);
227
228 let output_inner_ty = if matches!(pe, Index { .. }) {
229 ty.clone()
230 } else {
231 TyKind::Slice(ty.clone(), ty_is_sized.clone()).into_ty()
232 };
233 let output_ty = {
234 TyKind::Ref(
235 Region::Erased,
236 output_inner_ty.clone(),
237 RefKind::mutable(mut_access),
238 )
239 .into_ty()
240 };
241
242 let input_var =
246 self.ctx
247 .borrow_to_new_var(subplace.clone(), BorrowKind::mutable(mut_access), None);
248
249 let (last_arg, from_end) = match &pe {
251 Index {
252 offset: x,
253 from_end,
254 ..
255 }
256 | Subslice {
257 to: x, from_end, ..
258 } => (x.as_ref().clone(), *from_end),
259 _ => unreachable!(),
260 };
261 let to_idx = self
262 .ctx
263 .compute_subslice_end_idx(subplace, last_arg, from_end);
264 let index = match &pe {
265 Index { .. } => to_idx,
266 Subslice { from, .. } => {
267 let Some(range_ref) = index_ty.as_adt().cloned() else {
268 return;
269 };
270 let range_ty = TyKind::Adt(range_ref.clone()).into_ty();
271 let range_var = self.ctx.fresh_var(None, range_ty);
272 self.ctx.insert_assn_stmt(
273 range_var.clone(),
274 Rvalue::Aggregate(
275 AggregateKind::Adt(range_ref, None, None),
276 vec![from.as_ref().clone(), to_idx],
277 ),
278 );
279 Operand::Move(range_var)
280 }
281 _ => unreachable!(),
282 };
283
284 let (index_fn_ptr, args) = if let Some(len) = len {
285 let (array_item, slice_item) = match mutability {
286 RefKind::Shared => (StdItem::ArrayIndex, StdItem::SliceIndexImpl),
287 RefKind::Mut => (StdItem::ArrayIndexMut, StdItem::SliceIndexMutImpl),
288 };
289 let Some(&array_fun_id) = self.std_items.item_map.get(&array_item) else {
290 return;
291 };
292 let Some(array_fun) = array_fun_id
293 .as_fun()
294 .and_then(|id| self.ctx.ctx.translated.fun_decls.get(*id))
295 else {
296 return;
297 };
298 let Some(&slice_fun_id) = self.std_items.item_map.get(&slice_item) else {
299 return;
300 };
301 let Some(slice_fun) = slice_fun_id
302 .as_fun()
303 .and_then(|id| self.ctx.ctx.translated.fun_decls.get(*id))
304 else {
305 return;
306 };
307
308 let Some(slice_types) =
309 index_method_types(&slice_fun.generics, &ty, &index_ty, &output_inner_ty)
310 else {
311 return;
312 };
313 let mut slice_generics =
314 GenericArgs::new([Region::Erased].into(), slice_types, [].into(), [].into());
315 let Some(slice_index_trait_ref) =
316 method_impl_trait_ref(self.ctx.ctx, index_fun_id, &index_fn_ptr.generics)
317 else {
318 return;
319 };
320 let index_ty_is_sized = if ty_is_sized.is_some() {
321 let Some(clause) = slice_fun.generics.trait_clauses.get(TraitClauseId::new(1))
322 else {
323 return;
324 };
325 let trait_decl_ref = clause.trait_.clone().substitute_explicits(&slice_generics);
326 let Some(meta_sized) = slice_index_trait_ref
327 .clone()
328 .project_parent_clause(&self.ctx.ctx.translated, TraitClauseId::ZERO)
329 else {
330 return;
331 };
332 Some(TraitRef::new(
333 TraitRefKind::BuiltinOrAuto {
334 builtin_data: BuiltinImplData::Sized,
335 parent_trait_refs: [meta_sized].into_iter().collect(),
336 types: Default::default(),
337 vtable: None,
338 },
339 trait_decl_ref,
340 ))
341 } else {
342 None
343 };
344 slice_generics.trait_refs = ty_is_sized
345 .iter()
346 .cloned()
347 .chain(index_ty_is_sized.iter().cloned())
348 .chain([slice_index_trait_ref])
349 .collect();
350 let slice_fn_ptr = mk_fn_ptr(self.ctx.ctx, slice_fun_id, slice_generics);
351 let Some(slice_trait_ref) =
352 method_impl_trait_ref(self.ctx.ctx, slice_fun_id, &slice_fn_ptr.generics)
353 else {
354 return;
355 };
356
357 let Some(array_types) =
358 index_method_types(&array_fun.generics, &ty, &index_ty, &output_inner_ty)
359 else {
360 return;
361 };
362 let array_generics = GenericArgs::new(
363 [Region::Erased].into(),
364 array_types,
365 [len].into(),
366 ty_is_sized
367 .iter()
368 .cloned()
369 .chain(index_ty_is_sized)
370 .chain([slice_trait_ref])
371 .collect(),
372 );
373 (
374 mk_fn_ptr(self.ctx.ctx, array_fun_id, array_generics),
375 vec![Operand::Move(input_var), index],
376 )
377 } else {
378 (index_fn_ptr, vec![index, Operand::Move(input_var)])
379 };
380
381 let output_var = {
385 let output_var = self.ctx.fresh_var(None, output_ty);
386 let index_call = Call {
387 func: FnOperand::Regular(index_fn_ptr),
388 args,
389 dest: output_var.clone(),
390 safety: CallSafety::Inherit,
391 };
392 let kind = StatementKind::Call {
393 call: index_call,
394 on_unwind: Block::new_unreachable(self.ctx.span),
395 };
396 self.ctx
397 .statements
398 .push(Statement::new(self.ctx.span, kind));
399 output_var
400 };
401
402 *place = output_var.project(ProjectionElem::Deref, output_inner_ty);
404 }
405
406 fn visit_inner_with_mutability<T>(
408 &mut self,
409 x: &mut T,
410 mutability: bool,
411 ) -> ControlFlow<Infallible>
412 where
413 T: for<'s> DriveMut<'s, BodyVisitableWrapper<Self>> + BodyVisitable,
414 {
415 self.place_mutability_stack.push(mutability);
416 self.visit_inner(x)?;
417 self.place_mutability_stack.pop();
418 Continue(())
419 }
420}
421
422impl VisitBodyMut for IndexVisitor<'_, '_> {
424 fn exit_place(&mut self, place: &mut Place) {
426 let mut_access = *self.place_mutability_stack.last().unwrap();
429 self.transform_place(mut_access, place);
430 }
431
432 fn visit_operand(&mut self, x: &mut Operand) -> ControlFlow<Infallible> {
433 match x {
434 Operand::Move(_) => self.visit_inner_with_mutability(x, true),
435 Operand::Copy(_) => self.visit_inner_with_mutability(x, false),
436 Operand::Const(..) => self.visit_inner(x),
437 }
438 }
439
440 fn visit_call(&mut self, x: &mut Call) -> ControlFlow<Infallible> {
441 self.visit_inner_with_mutability(x, true)
442 }
443
444 fn visit_asm_operand(&mut self, x: &mut AsmOperand) -> ControlFlow<Infallible> {
445 match x {
446 AsmOperand::Out { .. } | AsmOperand::InOut { .. } => {
447 self.visit_inner_with_mutability(x, true)
448 }
449 _ => self.visit_inner(x),
450 }
451 }
452
453 fn visit_fn_operand(&mut self, x: &mut FnOperand) -> ControlFlow<Infallible> {
454 match x {
455 FnOperand::Regular(_) => self.visit_inner(x),
456 FnOperand::Dynamic(_) => self.visit_inner_with_mutability(x, true),
457 }
458 }
459
460 fn visit_rvalue(&mut self, x: &mut Rvalue) -> ControlFlow<Infallible> {
461 use Rvalue::*;
462 match x {
463 RawPtr {
466 kind: RefKind::Mut, ..
467 }
468 | Ref {
469 kind: BorrowKind::Mut | BorrowKind::TwoPhaseMut | BorrowKind::UniqueImmutable,
470 ..
471 } => self.visit_inner_with_mutability(x, true),
472 RawPtr {
473 kind: RefKind::Shared,
474 ..
475 }
476 | Ref {
477 kind: BorrowKind::Shared | BorrowKind::Shallow,
478 ..
479 }
480 | Discriminant(..)
481 | Len(..) => self.visit_inner_with_mutability(x, false),
482
483 Use(..) | NullaryOp(..) | UnaryOp(..) | BinaryOp(..) | Aggregate(..) | Repeat(..) => {
484 self.visit_inner(x)
485 }
486 }
487 }
488
489 fn visit_llbc_block(&mut self, _: &mut llbc_ast::Block) -> ControlFlow<Infallible> {
490 ControlFlow::Continue(())
491 }
492}
493
494#[derive(Copy, Clone, PartialEq, Eq, Hash)]
553enum StdItem {
554 ArrayAsSlice,
555 ArrayAsMutSlice,
556 ArrayRepeat,
557 ArrayIndex,
558 ArrayIndexMut,
559 SliceIndexImpl,
560 SliceIndexMutImpl,
561 SliceIndex,
562 SliceIndexMut,
563 RangeIndex,
564 RangeIndexMut,
565}
566
567pub struct Transform {
568 item_map: HashMap<StdItem, ItemId>,
569 item_set: HashSet<ItemId>,
570}
571
572impl Transform {
573 pub fn new(ctx: &TransformCtx) -> CowBox<dyn LlbcPass> {
574 use StdItem::*;
575
576 let mut matches: [(StdItem, NamePattern, Vec<ItemId>); _] = [
577 (ArrayAsSlice, "core::array::_::as_slice"),
578 (ArrayAsMutSlice, "core::array::_::as_mut_slice"),
579 (ArrayRepeat, "core::array::repeat"),
580 (
581 SliceIndex,
582 "core::slice::index::{impl core::slice::index::SliceIndex<_> for usize}::index",
583 ),
584 (
585 SliceIndexMut,
586 "core::slice::index::{impl core::slice::index::SliceIndex<_> for usize}::index_mut",
587 ),
588 (
589 RangeIndex,
590 "core::slice::index::{impl core::slice::index::SliceIndex<_> for core::ops::range::Range<usize>}::index",
591 ),
592 (
593 RangeIndexMut,
594 "core::slice::index::{impl core::slice::index::SliceIndex<_> for core::ops::range::Range<usize>}::index_mut",
595 ),
596 ]
597 .map(|(item, path)| (item, NamePattern::parse(path).unwrap(), Vec::new()));
598
599 for (id, name) in &ctx.translated.item_names {
601 for (_, pattern, found) in &mut matches {
602 if pattern.matches(&ctx.translated, name) {
603 found.push(*id);
604 }
605 }
606 }
607
608 let mut index_impl_methods: HashMap<StdItem, Vec<ItemId>> = HashMap::new();
609 for fun in &ctx.translated.fun_decls {
610 let FunSource::TraitImpl { trait_ref, .. } = &fun.src else {
611 continue;
612 };
613 let Some(trait_decl) = ctx.translated.trait_decls.get(trait_ref.id) else {
614 continue;
615 };
616 let Some(self_ty) = trait_ref.self_ty(&ctx.translated) else {
617 continue;
618 };
619 let item = match (&trait_decl.item_meta.lang_item, self_ty.kind()) {
620 (Some(crate::ast::from_rustc::LangItem::Index), TyKind::Array(..)) => ArrayIndex,
621 (Some(crate::ast::from_rustc::LangItem::IndexMut), TyKind::Array(..)) => {
622 ArrayIndexMut
623 }
624 (Some(crate::ast::from_rustc::LangItem::Index), TyKind::Slice(..)) => {
625 SliceIndexImpl
626 }
627 (Some(crate::ast::from_rustc::LangItem::IndexMut), TyKind::Slice(..)) => {
628 SliceIndexMutImpl
629 }
630 _ => continue,
631 };
632 index_impl_methods
633 .entry(item)
634 .or_default()
635 .push(ItemId::Fun(fun.def_id));
636 }
637 let item_map = matches
638 .into_iter()
639 .map(|(item, _, found)| (item, found))
640 .chain(index_impl_methods)
641 .filter_map(|(item, found)| found.into_iter().exactly_one().ok().map(|id| (item, id)))
642 .collect::<HashMap<_, _>>();
643 let item_set = item_map.values().copied().collect();
644 CowBox::Owned(Box::new(Self { item_map, item_set }))
645 }
646}
647
648impl LlbcPass for Transform {
649 fn should_run(&self, options: &crate::options::TranslateOptions) -> bool {
650 options.ops_to_function_calls || options.index_to_function_calls
651 }
652
653 fn transform_function(&self, ctx: &mut TransformCtx, decl: &mut FunDecl) {
654 if self.item_set.contains(&ItemId::Fun(decl.def_id)) {
655 return;
656 }
657 let Some(body) = decl.body.as_structured_mut() else {
658 return;
659 };
660 if ctx.options.ops_to_function_calls {
661 body.body
662 .visit_statements(&mut |statement: &mut Statement| {
663 transform_operation(self, ctx, statement)
664 });
665 }
666 if ctx.options.index_to_function_calls {
667 decl.transform_llbc_statements(ctx, |ctx, st: &mut Statement| {
668 let mut visitor = IndexVisitor {
669 ctx,
670 std_items: self,
671 place_mutability_stack: Vec::new(),
672 };
673 use StatementKind::*;
674 match &mut st.kind {
675 Assign(..) | SetDiscriminant(..) | Drop { .. } | Call { .. } => {
676 let _ = visitor.visit_inner_with_mutability(st, true);
677 }
678 Switch { .. } | PlaceMention(..) | Borrowck(..) => {
679 let _ = visitor.visit_inner_with_mutability(st, false);
680 }
681 Nop
682 | UnwindResume
683 | InlineAsm { .. }
684 | Assert { .. }
685 | Panic { .. }
686 | UndefinedBehavior
687 | UnwindTerminate
688 | StorageDead(..)
689 | StorageLive(..)
690 | Return
691 | Break(..)
692 | Continue(..)
693 | Loop(..) => {
694 let _ = st.drive_body_mut(&mut visitor);
695 }
696 }
697 })
698 }
699 }
700}