Skip to main content

charon_driver/translate/
translate_bodies.rs

1//! Translate functions from the rust compiler MIR to our internal representation.
2//! Our internal representation is very close to MIR, but is more convenient for
3//! us to handle, and easier to maintain - rustc's representation can evolve
4//! independently.
5
6use itertools::Itertools;
7use std::collections::HashMap;
8use std::collections::VecDeque;
9use std::mem;
10use std::ops::Deref;
11use std::ops::DerefMut;
12use std::panic;
13use std::rc::Rc;
14
15use crate::hax;
16use rustc_middle::mir;
17use rustc_middle::ty;
18use rustc_span::{Symbol, sym};
19
20use super::translate_crate::*;
21use super::translate_ctx::*;
22use charon_lib::formatter::{FmtCtx, IntoFormatter, compute_local_names};
23use charon_lib::name_matcher::NamePattern;
24use charon_lib::options::TranslateOptions;
25use charon_lib::pretty::FmtWithCtx;
26use charon_lib::transform::ctx::BodyTransformCtx;
27use charon_lib::ullbc_ast::*;
28
29/// A translation context for function bodies.
30pub(crate) struct BodyTransCtx<'tcx, 'tctx, 'ictx> {
31    /// The translation context for the item.
32    pub i_ctx: &'ictx mut ItemTransCtx<'tcx, 'tctx>,
33    /// List of body locals.
34    pub local_decls: &'ictx rustc_index::IndexVec<mir::Local, mir::LocalDecl<'tcx>>,
35    /// Types supplied explicitly by the user.
36    pub user_type_annotations: ty::CanonicalUserTypeAnnotations<'tcx>,
37
38    /// What kind of drops we get in this body.
39    pub drop_kind: DropKind,
40    /// The (regular) variables in the current function body.
41    pub locals: Locals,
42    /// The map from rust variable indices to translated variables indices.
43    pub locals_map: HashMap<usize, LocalId>,
44    /// The translated blocks.
45    pub blocks: IndexMap<BlockId, BlockData>,
46    /// The map from rust blocks to translated blocks.
47    /// Note that when translating terminators like DropAndReplace, we might have
48    /// to introduce new blocks which don't appear in the original MIR.
49    pub blocks_map: HashMap<mir::BasicBlock, BlockId>,
50    /// We register the blocks to translate in a stack, so as to avoid
51    /// writing the translation functions as recursive functions. We do
52    /// so because we had stack overflows in the past.
53    pub blocks_stack: VecDeque<mir::BasicBlock>,
54}
55
56impl<'tcx, 'tctx, 'ictx> BodyTransCtx<'tcx, 'tctx, 'ictx> {
57    pub(crate) fn new(
58        i_ctx: &'ictx mut ItemTransCtx<'tcx, 'tctx>,
59        body: &'ictx Rc<mir::Body<'tcx>>,
60        drop_kind: DropKind,
61    ) -> Self {
62        i_ctx.lifetime_freshener = Some(IndexMap::new());
63        let mut user_type_annotations = body.user_type_annotations.clone();
64        if let RustcItem::Mono(item) = &i_ctx.item_src.item {
65            // `CanonicalUserTypeAnnotation::user_ty` is deliberately not folded when rustc
66            // instantiates a MIR body, so do the item substitution explicitly.
67            let item = item.clone();
68            let args = item.rustc_args(i_ctx.hax_state_with_id());
69            for annotation in &mut user_type_annotations {
70                annotation.user_ty.value = hax::substitute(
71                    i_ctx.tcx,
72                    hax::UnderOwnerState::typing_env(&i_ctx.hax_state),
73                    Some(args),
74                    annotation.user_ty.value,
75                );
76            }
77        }
78        BodyTransCtx {
79            i_ctx,
80            local_decls: &body.local_decls,
81            user_type_annotations,
82            drop_kind,
83            locals: Default::default(),
84            locals_map: Default::default(),
85            blocks: Default::default(),
86            blocks_map: Default::default(),
87            blocks_stack: Default::default(),
88        }
89    }
90}
91
92impl<'tcx, 'tctx, 'ictx> Deref for BodyTransCtx<'tcx, 'tctx, 'ictx> {
93    type Target = ItemTransCtx<'tcx, 'tctx>;
94    fn deref(&self) -> &Self::Target {
95        self.i_ctx
96    }
97}
98impl<'tcx, 'tctx, 'ictx> DerefMut for BodyTransCtx<'tcx, 'tctx, 'ictx> {
99    fn deref_mut(&mut self) -> &mut Self::Target {
100        self.i_ctx
101    }
102}
103
104/// A translation context for function blocks.
105pub(crate) struct BlockTransCtx<'tcx, 'tctx, 'ictx, 'bctx> {
106    /// The translation context for the item.
107    pub b_ctx: &'bctx mut BodyTransCtx<'tcx, 'tctx, 'ictx>,
108    /// Block onto which we're adding statements.
109    pub current_block: BlockId,
110    /// Span of the statement or terminator currently being translated.
111    pub span: Span,
112    /// List of currently translated statements
113    pub statements: Vec<Statement>,
114}
115
116impl<'tcx, 'tctx, 'ictx, 'bctx> BlockTransCtx<'tcx, 'tctx, 'ictx, 'bctx> {
117    pub(crate) fn new(
118        b_ctx: &'bctx mut BodyTransCtx<'tcx, 'tctx, 'ictx>,
119        current_block: BlockId,
120    ) -> Self {
121        BlockTransCtx {
122            b_ctx,
123            current_block,
124            span: Span::dummy(),
125            statements: Vec::new(),
126        }
127    }
128
129    fn finish_current_block(self, terminator: Terminator) {
130        let block = BlockData {
131            statements: self.statements,
132            terminator,
133        };
134        self.b_ctx.blocks.set_slot(self.current_block, block);
135    }
136
137    /// Used for non-diverging intrinsics.
138    fn push_nounwind_call(&mut self, span: Span, call: Call) {
139        let target = self.blocks.reserve_slot();
140        let on_unwind = self.blocks.push(
141            Terminator::new(span, TerminatorKind::Abort(AbortKind::UndefinedBehavior)).into_block(),
142        );
143        let block = BlockData {
144            statements: mem::take(&mut self.statements),
145            terminator: Terminator::new(
146                span,
147                TerminatorKind::Call {
148                    call,
149                    target,
150                    on_unwind,
151                },
152            ),
153        };
154        let current_block = mem::replace(&mut self.current_block, target);
155        self.blocks.set_slot(current_block, block);
156    }
157}
158
159impl<'tcx, 'tctx, 'ictx, 'bctx> Deref for BlockTransCtx<'tcx, 'tctx, 'ictx, 'bctx> {
160    type Target = BodyTransCtx<'tcx, 'tctx, 'ictx>;
161    fn deref(&self) -> &Self::Target {
162        self.b_ctx
163    }
164}
165impl<'tcx, 'tctx, 'ictx, 'bctx> DerefMut for BlockTransCtx<'tcx, 'tctx, 'ictx, 'bctx> {
166    fn deref_mut(&mut self) -> &mut Self::Target {
167        self.b_ctx
168    }
169}
170
171impl<'tcx> TranslateCtx<'tcx> {
172    pub fn translate_variant_id(&self, id: hax::VariantIdx) -> VariantId {
173        VariantId::new(id.as_usize())
174    }
175
176    pub fn translate_field_id(&self, id: hax::FieldIdx) -> FieldId {
177        FieldId::new(id.index())
178    }
179
180    fn translate_borrow_kind(&self, borrow_kind: mir::BorrowKind) -> BorrowKind {
181        match borrow_kind {
182            mir::BorrowKind::Shared => BorrowKind::Shared,
183            mir::BorrowKind::Mut { kind } => match kind {
184                mir::MutBorrowKind::Default => BorrowKind::Mut,
185                mir::MutBorrowKind::TwoPhaseBorrow => BorrowKind::TwoPhaseMut,
186                mir::MutBorrowKind::ClosureCapture => BorrowKind::UniqueImmutable,
187            },
188            mir::BorrowKind::Fake(mir::FakeBorrowKind::Shallow) => BorrowKind::Shallow,
189            // This one is used only in deref patterns.
190            mir::BorrowKind::Fake(mir::FakeBorrowKind::Deep) => unimplemented!(),
191        }
192    }
193}
194
195impl<'tcx> ItemTransCtx<'tcx, '_> {
196    /// Translate the MIR body of this definition if it has one. Catches any error and returns
197    /// `Body::Error` instead
198    pub fn translate_def_body(&mut self, span: Span, def: &hax::FullDef<'tcx>) -> Body {
199        match self.translate_def_body_inner(span, def) {
200            Ok(body) => body,
201            Err(e) => Body::Error(e),
202        }
203    }
204
205    fn translate_def_body_inner(
206        &mut self,
207        span: Span,
208        def: &hax::FullDef<'tcx>,
209    ) -> Result<Body, Error> {
210        // Retrieve the body
211        if let Some(body) = self.get_mir(def.this(), span)? {
212            Ok(self.translate_body(span, body, &def.source_text))
213        } else if let Some(value) = self.evaluate_const_def(def) {
214            // For globals without MIR, generate a body by evaluating the global. This is how we
215            // get the value of statics (which have no cross-crate MIR at all) and of "trivial"
216            // consts (whose value rustc stores directly instead of encoding MIR for it).
217            let c = self.translate_constant_expr(span, &value)?;
218            let mut bb = BodyBuilder::new(span, 0);
219            let ret = bb.new_var(None, c.ty().clone());
220            bb.push_statement(StatementKind::Assign(
221                ret,
222                Rvalue::Use(Operand::Const(c), WithRetag::No),
223            ));
224            Ok(Body::Unstructured(bb.build()))
225        } else {
226            Ok(Body::Missing)
227        }
228    }
229
230    /// Translate a function body. Catches errors and returns `Body::Error` instead.
231    /// That's the entrypoint of this module.
232    pub fn translate_body(
233        &mut self,
234        span: Span,
235        body: mir::Body<'tcx>,
236        source_text: &Option<String>,
237    ) -> Body {
238        let _guard = charon_lib::timing::scope("translate-body");
239        let drop_kind = match body.phase {
240            mir::MirPhase::Built | mir::MirPhase::Analysis(..) => DropKind::Conditional,
241            mir::MirPhase::Runtime(..) => DropKind::Precise,
242        };
243        let mut ctx = panic::AssertUnwindSafe(&mut *self);
244        let body = panic::AssertUnwindSafe(body);
245        // Stopgap measure because there are still many panics in charon and hax.
246        let res = panic::catch_unwind(move || {
247            let body = Rc::new({ body }.0);
248            let ctx = BodyTransCtx::new(*ctx, &body, drop_kind);
249            ctx.translate_body(&body, source_text)
250        });
251        match res {
252            Ok(Ok(body)) => body,
253            // Translation error
254            Ok(Err(e)) => Body::Error(e),
255            // Panic
256            Err(_) => {
257                let e = register_error!(self, span, "Thread panicked when extracting body.");
258                Body::Error(e)
259            }
260        }
261    }
262
263    fn translate_unsizing_metadata(
264        &mut self,
265        span: Span,
266        meta: hax::UnsizingMetadata,
267    ) -> Result<UnsizingMetadata, Error> {
268        Ok(match &meta {
269            hax::UnsizingMetadata::Length(len) => {
270                let len = self.translate_constant_expr(span, len)?;
271                UnsizingMetadata::Length(len)
272            }
273            hax::UnsizingMetadata::DirectVTable(trait_proof) => {
274                let tref = self.translate_trait_proof(span, trait_proof)?;
275                let vtable = self.translate_vtable_instance_const(span, trait_proof)?;
276                UnsizingMetadata::VTable(tref, vtable)
277            }
278            hax::UnsizingMetadata::NestedVTable(dyn_trait_proof) => {
279                // This binds a fake `T: SrcTrait` variable.
280                let binder =
281                    self.translate_dyn_binder(span, dyn_trait_proof, |ctx, _, trait_proof| {
282                        ctx.translate_trait_proof(span, trait_proof)
283                    })?;
284
285                // Compute the supertrait path from the source tref to the target
286                // tref.
287                let mut target_tref = &binder.skip_binder;
288                let mut clause_path: Vec<(TraitDeclId, TraitClauseId)> = vec![];
289                while let TraitRefKind::ParentClause(tref, id) = &target_tref.kind {
290                    clause_path.push((tref.trait_decl_ref.skip_binder.id, *id));
291                    target_tref = tref;
292                }
293
294                let mut field_path = vec![];
295                for &(trait_id, clause_id) in &clause_path {
296                    if let Ok(ItemRef::TraitDecl(tdecl)) = self.get_or_translate(trait_id.into())
297                        && let vtable_decl_id = tdecl.vtable.as_ref().unwrap().id
298                        && let Ok(ItemRef::Type(vtable_decl)) =
299                            self.get_or_translate(vtable_decl_id.into())
300                    {
301                        let TypeSource::VTable { supertrait_map, .. } = &vtable_decl.src else {
302                            unreachable!()
303                        };
304                        field_path.push(supertrait_map[clause_id].unwrap());
305                    } else {
306                        break;
307                    }
308                }
309
310                if field_path.len() == clause_path.len() {
311                    UnsizingMetadata::VTableUpcast(field_path)
312                } else {
313                    UnsizingMetadata::Unknown
314                }
315            }
316            hax::UnsizingMetadata::Unknown => UnsizingMetadata::Unknown,
317        })
318    }
319
320    /// Generate a fake function body for ADT constructors.
321    pub(crate) fn build_ctor_body(
322        &mut self,
323        span: Span,
324        def: &hax::FullDef<'tcx>,
325    ) -> Result<Body, Error> {
326        let hax::FullDefKind::Ctor {
327            adt_def_id,
328            ctor_of,
329            variant_id,
330            fields,
331            output_ty,
332            ..
333        } = def.kind()
334        else {
335            unreachable!()
336        };
337        let tref = self
338            .translate_type_decl_ref(span, &def.this().with_def_id(self.hax_state(), adt_def_id))?;
339        let output_ty = self.translate_ty(span, output_ty)?;
340
341        let mut builder = BodyBuilder::new(span, fields.len());
342        let return_place = builder.new_var(None, output_ty);
343        let args: Vec<_> = fields
344            .iter()
345            .map(|field| -> Result<Operand, Error> {
346                let ty = self.translate_ty(span, &field.ty)?;
347                let place = builder.new_var(None, ty);
348                Ok(Operand::Move(place))
349            })
350            .try_collect()?;
351        let variant = match ctor_of {
352            hax::CtorOf::Struct => None,
353            hax::CtorOf::Variant => Some(self.translate_variant_id(*variant_id)),
354        };
355        builder.push_statement(StatementKind::Assign(
356            return_place,
357            Rvalue::Aggregate(AggregateKind::Adt(tref, variant, None), args),
358        ));
359        Ok(Body::Unstructured(builder.build()))
360    }
361
362    /// FIXME(#865): Generate a function body for the `box_assume_init_into_vec_unsafe` function,
363    /// because the MIR we get for it is too optimized to be usable.
364    pub(crate) fn build_box_assume_init_into_vec_unsafe(
365        &mut self,
366        span: Span,
367        def: &hax::FullDef<'tcx>,
368    ) -> Result<Body, Error> {
369        // pub fn box_assume_init_into_vec_unsafe<T, const N: usize>(
370        //     b: Box<MaybeUninit<[T; N]>>,
371        // ) -> Vec<T> {
372        //     let x: Box<[T; N]> = unsafe { Box::assume_init(b) };
373        //     let y = x as Box<[T]>;
374        //     core::slice::into_vec(y)
375        // }
376        let tcx = self.tcx;
377        let hax::FullDefKind::Fn { sig: hax_sig, .. } = def.kind() else {
378            unreachable!()
379        };
380        let hax_sig = hax_sig.hax_skip_binder_ref();
381        let sig = self.translate_fun_sig(span, hax_sig)?;
382
383        // Get the `[T; N]` and `A` parameters.
384        let (array_rust_ty, alloc_rust_ty) = {
385            let input_box_rust_args = {
386                let hax::TyKind::Adt(input_box_item) = hax_sig.inputs[0].kind() else {
387                    raise_error!(self, span, "expected a boxed input in the hax signature");
388                };
389                input_box_item.rustc_args(self.hax_state_with_id())
390            };
391            let maybe_uninit_array_rust_ty = input_box_rust_args[0].as_type().unwrap();
392            let alloc_rust_ty = input_box_rust_args[1].as_type().unwrap();
393            let ty::Adt(_, maybe_uninit_rust_args) = maybe_uninit_array_rust_ty.kind() else {
394                raise_error!(
395                    self,
396                    span,
397                    "expected `MaybeUninit<[T; N]>` in the hax signature"
398                );
399            };
400            let Some(array_rust_ty) = maybe_uninit_rust_args[0].as_type() else {
401                raise_error!(
402                    self,
403                    span,
404                    "expected the first `MaybeUninit` parameter to be a type"
405                );
406            };
407            (array_rust_ty, alloc_rust_ty)
408        };
409        // `T`
410        let elem_rust_ty = array_rust_ty.builtin_index().unwrap();
411        // `[T]`
412        let slice_rust_ty = ty::Ty::new_slice(tcx, elem_rust_ty);
413        // `Box<[T; N]>`
414        let box_array_rust_ty = ty::Ty::new_box(tcx, array_rust_ty);
415        let box_array_ty = self.translate_rustc_ty(span, &box_array_rust_ty)?;
416        // `Box<[T]>`
417        let box_slice_rust_ty = ty::Ty::new_box(tcx, slice_rust_ty);
418        let box_slice_ty = self.translate_rustc_ty(span, &box_slice_rust_ty)?;
419
420        if !self.monomorphize() {
421            // Make `Box::new` and `Box::write` available to a later construction pass.
422            let path = NamePattern::parse(names::BOX_NEW).unwrap();
423            let box_new_def_id = self.resolve_single_path(span, &path)?;
424            let box_new_args = tcx.mk_args(&[array_rust_ty.into()]);
425            let box_new_item =
426                hax::ItemRef::translate(self.hax_state_with_id(), box_new_def_id, box_new_args);
427            let _ = self.translate_fn_ptr(span, &box_new_item, TransItemSourceKind::Fun)?;
428
429            let path = NamePattern::parse(names::BOX_WRITE).unwrap();
430            let box_write_def_id = self.resolve_single_path(span, &path)?;
431            let box_write_args = tcx.mk_args(&[array_rust_ty.into(), alloc_rust_ty.into()]);
432            let box_write_item =
433                hax::ItemRef::translate(self.hax_state_with_id(), box_write_def_id, box_write_args);
434            let _ = self.translate_fn_ptr(span, &box_write_item, TransItemSourceKind::Fun)?;
435        }
436
437        let body = {
438            let mut builder = BodyBuilder::new(span, sig.inputs.len());
439            let return_place = builder.new_var(Some("ret".to_string()), sig.output.clone());
440            let input = builder.new_var(Some("b".to_string()), sig.inputs[0].clone());
441            let initialized_box = builder.new_var(Some("x".to_string()), box_array_ty.clone());
442            let box_slice = builder.new_var(Some("y".to_string()), box_slice_ty.clone());
443
444            builder.call({
445                let assume_init_fn = {
446                    let path = NamePattern::parse("alloc::boxed::Box::assume_init").unwrap();
447                    let assume_init_def_id = self
448                        .resolve_path(span, &path, true)?
449                        .into_iter()
450                        // There's `assume_init` on `Box<MU<T>>` and `Box<[MU<T>]>`, we want the former.
451                        .filter(|&def_id| {
452                            let sig = self.tcx.fn_sig(def_id);
453                            !sig.skip_binder().inputs().skip_binder()[0]
454                                .expect_boxed_ty()
455                                .is_slice()
456                        })
457                        .exactly_one()
458                        .unwrap();
459                    let assume_init_args =
460                        tcx.mk_args(&[array_rust_ty.into(), alloc_rust_ty.into()]);
461                    let assume_init_item = hax::ItemRef::translate(
462                        self.hax_state_with_id(),
463                        assume_init_def_id,
464                        assume_init_args,
465                    );
466                    self.translate_fn_ptr(span, &assume_init_item, TransItemSourceKind::Fun)?
467                };
468                Call {
469                    func: FnOperand::Regular(assume_init_fn),
470                    args: vec![Operand::Move(input)],
471                    dest: initialized_box.clone(),
472                }
473            });
474
475            builder.push_statement({
476                let meta = hax::compute_unsizing_metadata(
477                    &self.hax_state,
478                    box_array_rust_ty,
479                    box_slice_rust_ty,
480                );
481                let meta = self.translate_unsizing_metadata(span, meta)?;
482                StatementKind::Assign(
483                    box_slice.clone(),
484                    Rvalue::UnaryOp(
485                        UnOp::Cast(CastKind::Unsize(box_array_ty, box_slice_ty, meta)),
486                        Operand::Move(initialized_box),
487                    ),
488                )
489            });
490
491            builder.call({
492                let into_vec_fn = {
493                    let path = NamePattern::parse("slice::into_vec").unwrap();
494                    let into_vec_def_id = self.resolve_single_path(span, &path)?;
495                    let into_vec_args = tcx.mk_args(&[elem_rust_ty.into(), alloc_rust_ty.into()]);
496                    let into_vec_item = hax::ItemRef::translate(
497                        self.hax_state_with_id(),
498                        into_vec_def_id,
499                        into_vec_args,
500                    );
501                    self.translate_fn_ptr(span, &into_vec_item, TransItemSourceKind::Fun)?
502                };
503                Call {
504                    func: FnOperand::Regular(into_vec_fn),
505                    args: vec![Operand::Move(box_slice)],
506                    dest: return_place,
507                }
508            });
509            builder.build()
510        };
511
512        Ok(Body::Unstructured(body))
513    }
514
515    /// Generate a function body for `core::intrinsics::type_id`.
516    pub(crate) fn build_type_id_body(
517        &mut self,
518        span: Span,
519        def: &hax::FullDef<'tcx>,
520        signature: &FunSig,
521    ) -> Result<Body, Error> {
522        let generics = self.translate_generic_args(span, &def.this().generic_args, &[])?;
523        let type_id_ty = generics.types[0].clone();
524
525        let mut builder = BodyBuilder::new(span, signature.inputs.len());
526        let return_place = builder.new_var(Some("ret".to_string()), signature.output.clone());
527        let type_id = ConstantExpr::new(
528            ConstantExprKind::TypeId(type_id_ty),
529            signature.output.clone(),
530        );
531        builder.push_statement(StatementKind::Assign(
532            return_place,
533            Rvalue::Use(Operand::Const(type_id), WithRetag::No),
534        ));
535        Ok(Body::Unstructured(builder.build()))
536    }
537
538    /// Generate a function body for `core::ptr::drop_glue`.
539    pub(crate) fn build_drop_glue_body(
540        &mut self,
541        span: Span,
542        def: &hax::FullDef<'tcx>,
543        signature: &FunSig,
544    ) -> Result<Body, Error> {
545        let hax::FullDefKind::Fn { .. } = def.kind() else {
546            unreachable!()
547        };
548        let def_id = def.def_id().as_real_def_id().unwrap();
549        let rustc_args = def.this().rustc_args(self.hax_state_with_id());
550        let rustc_sig = self.tcx.fn_sig(def_id).instantiate(self.tcx, rustc_args);
551        // `skip_binder` is ok because we have that lifetime in scope.
552        let input_ty = rustc_sig.skip_binder().inputs()[0];
553        let pointee_ty = input_ty
554            .builtin_deref(true)
555            .expect("`drop_glue` argument is not a pointer");
556        let fn_ptr = self.translate_drop_glue_method_call(span, pointee_ty)?;
557
558        let mut builder = BodyBuilder::new(span, signature.inputs.len());
559        let _return_place = builder.new_var(Some("ret".to_string()), signature.output.clone());
560        let input = builder.new_var(None, signature.inputs[0].clone());
561        builder.insert_drop(input.deref(), fn_ptr);
562        Ok(Body::Unstructured(builder.build()))
563    }
564}
565
566impl<'tcx> BodyTransCtx<'tcx, '_, '_> {
567    pub(crate) fn translate_local(&self, local: &mir::Local) -> Option<LocalId> {
568        self.locals_map.get(&local.index()).copied()
569    }
570
571    pub(crate) fn push_var(&mut self, rid: mir::Local, ty: Ty, name: Option<String>, span: Span) {
572        let local_id = self.locals.locals.push_with(|index| Local {
573            index,
574            name,
575            span,
576            ty,
577        });
578        self.locals_map.insert(rid.as_usize(), local_id);
579    }
580
581    /// Translate a function's local variables by adding them in the environment.
582    fn translate_body_locals(&mut self, body: &mir::Body<'tcx>) -> Result<(), Error> {
583        // Translate the parameters
584        for (index, var) in body.local_decls.iter_enumerated() {
585            // Find the name of the variable
586            let name: Option<String> = hax::name_of_local(index, &body.var_debug_info);
587
588            // Translate the type
589            let span = self.translate_span(&var.source_info.span);
590            let ty = self.translate_rustc_ty(span, &var.ty)?;
591
592            // Add the variable to the environment
593            self.push_var(index, ty, name, span);
594        }
595
596        Ok(())
597    }
598
599    /// Translate a basic block id and register it, if it hasn't been done.
600    fn translate_basic_block_id(&mut self, block_id: mir::BasicBlock) -> BlockId {
601        match self.blocks_map.get(&block_id) {
602            Some(id) => *id,
603            // Generate a fresh id - this also registers the block
604            None => {
605                // Push to the stack of blocks awaiting translation
606                self.blocks_stack.push_back(block_id);
607                let id = self.blocks.reserve_slot();
608                // Insert in the map
609                self.blocks_map.insert(block_id, id);
610                id
611            }
612        }
613    }
614
615    fn translate_basic_block(
616        &mut self,
617        block_id: BlockId,
618        source_scopes: &rustc_index::IndexVec<mir::SourceScope, mir::SourceScopeData>,
619        block: &mir::BasicBlockData<'tcx>,
620    ) -> Result<(), Error> {
621        // Translate the statements
622        let mut block_ctx = BlockTransCtx::new(self, block_id);
623        for statement in &block.statements {
624            trace!("statement: {:?}", statement);
625            block_ctx.translate_statement(source_scopes, statement)?;
626        }
627
628        // Translate the terminator
629        let terminator = block.terminator.as_ref().unwrap();
630        block_ctx.translate_terminator(source_scopes, terminator)?;
631
632        Ok(())
633    }
634
635    /// Gather all the lines that start with `//` inside the given span.
636    fn translate_body_comments(
637        &mut self,
638        source_text: &Option<String>,
639        charon_span: Span,
640    ) -> Vec<(u32, Vec<String>)> {
641        if let Some(body_text) = source_text {
642            let mut comments = body_text
643                .lines()
644                // Iter through the lines of this body in reverse order.
645                .rev()
646                .enumerate()
647                // Compute the absolute line number
648                .filter_map(|(i, line)| {
649                    Some(((charon_span.data().end.line).checked_sub(i as u32)?, line))
650                })
651                // Extract the comment if this line starts with `//`
652                .map(|(line_nbr, line)| (line_nbr, line.trim_start().strip_prefix("//")))
653                .peekable()
654                .batching(|iter| {
655                    // Get the next line. This is not a comment: it's either the last line of the
656                    // body or a line that wasn't consumed by `peeking_take_while`.
657                    let (line_nbr, _first) = iter.next()?;
658                    // Collect all the comments before this line.
659                    let mut comments = iter
660                        // `peeking_take_while` ensures we don't consume a line that returns
661                        // `false`. It will be consumed by the next round of `batching`.
662                        .peeking_take_while(|(_, opt_comment)| opt_comment.is_some())
663                        .map(|(_, opt_comment)| opt_comment.unwrap())
664                        .map(|s| s.strip_prefix(" ").unwrap_or(s))
665                        .map(str::to_owned)
666                        .collect_vec();
667                    comments.reverse();
668                    Some((line_nbr, comments))
669                })
670                .filter(|(_, comments)| !comments.is_empty())
671                .collect_vec();
672            comments.reverse();
673            comments
674        } else {
675            Vec::new()
676        }
677    }
678
679    fn translate_body(
680        mut self,
681        mir_body: &mir::Body<'tcx>,
682        source_text: &Option<String>,
683    ) -> Result<Body, Error> {
684        // Compute the span information
685        let span = self.translate_span(&mir_body.span);
686
687        // Initialize the local variables
688        trace!("Translating the body locals");
689        self.locals.arg_count = mir_body.arg_count;
690        self.translate_body_locals(mir_body)?;
691
692        // Translate the expression body
693        trace!("Translating the expression body");
694
695        // Register the start block
696        let id = self.translate_basic_block_id(rustc_index::Idx::new(mir::START_BLOCK.as_usize()));
697        assert!(id == START_BLOCK_ID);
698
699        // For as long as there are blocks in the stack, translate them
700        while let Some(mir_block_id) = self.blocks_stack.pop_front() {
701            let mir_block = mir_body.basic_blocks.get(mir_block_id).unwrap();
702            let block_id = self.translate_basic_block_id(mir_block_id);
703            self.translate_basic_block(block_id, &mir_body.source_scopes, mir_block)?;
704        }
705
706        // Create the body
707        let comments = self.translate_body_comments(source_text, span);
708        Ok(Body::Unstructured(ExprBody {
709            span,
710            locals: self.locals,
711            bound_body_regions: self.i_ctx.lifetime_freshener.take().unwrap().slot_count(),
712            body: self.blocks.make_contiguous(),
713            comments,
714        }))
715    }
716}
717
718impl BodyTransformCtx for BlockTransCtx<'_, '_, '_, '_> {
719    fn get_crate(&self) -> &TranslatedCrate {
720        &self.translated
721    }
722
723    fn get_options(&self) -> &TranslateOptions {
724        &self.options
725    }
726
727    fn get_params(&self) -> &GenericParams {
728        self.outermost_generics()
729    }
730
731    fn get_locals_mut(&mut self) -> &mut Locals {
732        &mut self.locals
733    }
734
735    fn insert_storage_live_stmt(&mut self, local: LocalId) {
736        self.statements
737            .push(Statement::new(self.span, StatementKind::StorageLive(local)));
738    }
739
740    fn insert_storage_dead_stmt(&mut self, local: LocalId) {
741        self.statements
742            .push(Statement::new(self.span, StatementKind::StorageDead(local)));
743    }
744
745    fn insert_assn_stmt(&mut self, place: Place, rvalue: Rvalue) {
746        self.statements.push(Statement::new(
747            self.span,
748            StatementKind::Assign(place, rvalue),
749        ));
750    }
751}
752
753impl<'tcx> BlockTransCtx<'tcx, '_, '_, '_> {
754    fn missing_ptr_metadata() -> Operand {
755        Operand::Const(ConstantExpr::new(
756            ConstantExprKind::Opaque("Missing metadata".to_string()),
757            Ty::mk_unit(),
758        ))
759    }
760
761    /// If all the input constants are identical and copyable, return an `Rvalue::Repeat`. This
762    /// simplifies some giant array constants.
763    fn try_reconstruct_array_repeat(
764        &mut self,
765        span: Span,
766        array_ty: &hax::Ty,
767        fields: impl ExactSizeIterator<Item = ConstantExpr>,
768    ) -> Result<Option<Rvalue>, Error> {
769        if fields.len() >= 2
770            && let Ok(field) = fields.dedup().exactly_one()
771        {
772            let hax::TyKind::Array(item_ref) = array_ty.kind() else {
773                panic!("expected an array type")
774            };
775            let translated_array_ty = self.translate_ty(span, array_ty)?;
776            let TyKind::Array(elem_ty, len, _) = translated_array_ty.kind() else {
777                unreachable!()
778            };
779            let rust_elem_ty = item_ref.rustc_args(&self.hax_state).type_at(0);
780            let copy_proof = hax::solve_copy(&self.hax_state, rust_elem_ty);
781            if matches!(&copy_proof.kind, hax::TraitProofKind::Error(_)) {
782                return Ok(None);
783            }
784            let ty_is_copy = self.translate_trait_proof(span, &copy_proof)?;
785            Ok(Some(Rvalue::Repeat(
786                Operand::Const(field),
787                elem_ty.clone(),
788                len.clone(),
789                ty_is_copy,
790            )))
791        } else {
792            Ok(None)
793        }
794    }
795
796    fn apply_user_type_projection(
797        &mut self,
798        span: Span,
799        mut ty: Ty,
800        projections: &[mir::ProjectionElem<(), ()>],
801    ) -> Result<Ty, Error> {
802        let mut downcast = None;
803        for projection in projections {
804            let projection = match projection {
805                mir::ProjectionElem::Deref => ProjectionElem::Deref,
806                mir::ProjectionElem::Field(field, ()) => {
807                    let field = self.translate_field_id(*field);
808                    let TyKind::Adt(type_ref) = ty.kind() else {
809                        raise_error!(self, span, "field projection on unexpected type");
810                    };
811                    match type_ref.as_builtin() {
812                        None => ProjectionElem::Field(downcast.take(), field),
813                        Some(BuiltinAdt::Tuple) => ProjectionElem::Field(None, field),
814                        Some(BuiltinAdt::Box) if field == FieldId::ZERO => ProjectionElem::Deref,
815                        _ => raise_error!(self, span, "field projection on unexpected type"),
816                    }
817                }
818                mir::ProjectionElem::Index(()) => ProjectionElem::Index {
819                    offset: Box::new(Operand::mk_const_unit()),
820                    from_end: false,
821                },
822                mir::ProjectionElem::ConstantIndex { from_end, .. } => ProjectionElem::Index {
823                    offset: Box::new(Operand::mk_const_unit()),
824                    from_end: *from_end,
825                },
826                mir::ProjectionElem::Subslice { from_end, .. } => ProjectionElem::Subslice {
827                    from: Box::new(Operand::mk_const_unit()),
828                    to: Box::new(Operand::mk_const_unit()),
829                    from_end: *from_end,
830                },
831                mir::ProjectionElem::Downcast(_, variant) => {
832                    downcast = Some(self.translate_variant_id(*variant));
833                    continue;
834                }
835                mir::ProjectionElem::OpaqueCast(()) => {
836                    raise_error!(self, span, "unexpected opaque cast in user type projection");
837                }
838                mir::ProjectionElem::UnwrapUnsafeBinder(()) => {
839                    raise_error!(
840                        self,
841                        span,
842                        "unsupported unsafe binder in user type projection"
843                    );
844                }
845            };
846            let Some(next_ty) = projection.project_type(&self.translated, &ty) else {
847                raise_error!(self, span, "invalid user type projection");
848            };
849            ty = next_ty;
850        }
851        Ok(ty)
852    }
853
854    fn translate_user_type_projection(
855        &mut self,
856        span: Span,
857        user_ty: &mir::UserTypeProjection,
858    ) -> Result<(Ty, Vec<BorrowckStatement>), Error> {
859        use rustc_infer::infer::canonical::CanonicalExt;
860
861        let annotation = self.user_type_annotations[user_ty.base].clone();
862        let canonical = *annotation.user_ty;
863
864        let mut facts = Vec::new();
865        if !canonical.value.bounds.is_empty() {
866            let user_ty_before_inference = match canonical.value.kind {
867                ty::UserTypeKind::Ty(ty) => ty,
868                ty::UserTypeKind::TypeOf(def_id, user_args)
869                    if user_args.args.len() == self.tcx.generics_of(def_id).count() =>
870                {
871                    self.tcx
872                        .type_of(def_id)
873                        .instantiate(self.tcx, user_args.args)
874                        .skip_normalization()
875                }
876                // Inherent associated type consts use a special argument format; rustc reconstructs
877                // their impl arguments with inference. Their resulting type is already recorded here.
878                ty::UserTypeKind::TypeOf(..) => annotation.inferred_ty,
879            };
880
881            // Rustc discards the original canonicalization values when it stores this annotation.
882            // Recover just enough of that mapping to instantiate the explicit user bounds. The
883            // place relation below deliberately uses `inferred_ty` directly.
884            let Some(var_values) = hax::rustc::match_canonical_var_values(
885                self.tcx,
886                canonical.var_kinds,
887                user_ty_before_inference,
888                annotation.inferred_ty,
889            ) else {
890                raise_error!(
891                    self,
892                    span,
893                    "could not match a user type annotation with its inferred type"
894                )
895            };
896            let instantiated_user_ty = canonical.instantiate(self.tcx, &var_values);
897
898            for clause in instantiated_user_ty.bounds {
899                if let Some(trait_predicate) = clause.as_trait_clause() {
900                    if trait_predicate.skip_binder().polarity != ty::PredicatePolarity::Positive {
901                        raise_error!(self, span, "negative trait bound in a user type annotation")
902                    }
903                    let proof = hax::solve_trait(
904                        &self.hax_state,
905                        trait_predicate.map_bound(|predicate| predicate.trait_ref),
906                    );
907                    facts.push(BorrowckStatement::PredicateHolds(
908                        self.translate_trait_proof(span, &proof)?,
909                    ));
910                } else if let Some(outlives) = clause.as_type_outlives_clause() {
911                    let Some(ty::OutlivesClause(outlived_ty, region)) = outlives.no_bound_vars()
912                    else {
913                        raise_error!(self, span, "higher-ranked outlives user type bound")
914                    };
915                    let outlived_ty = self.translate_rustc_ty(span, &outlived_ty)?;
916                    let region = self.catch_sinto(span, &region)?;
917                    let region = self.translate_region(span, &region)?;
918                    facts.push(BorrowckStatement::SetOutlives(outlived_ty, region));
919                }
920            }
921        }
922
923        // This is the type rustc itself relates the MIR place against. In particular, it has
924        // already revealed local `impl Trait` types and performed type normalization.
925        let ty = self.translate_rustc_ty(span, &annotation.inferred_ty)?;
926        let ty = self.apply_user_type_projection(span, ty, &user_ty.projs)?;
927
928        Ok((ty, facts))
929    }
930
931    fn translate_thread_local_ref(
932        &mut self,
933        span: Span,
934        def_id: rustc_hir::def_id::DefId,
935    ) -> Result<Rvalue, Error> {
936        let args = ty::GenericArgs::empty();
937        let item = hax::translate_item_ref(&self.hax_state, def_id, args);
938        let global_ref = self.translate_global_decl_ref(span, &item)?;
939
940        let ptr_ty = self.tcx.thread_local_ptr_ty(def_id);
941        let ty = ptr_ty.builtin_deref(true).unwrap();
942        let ty = self.translate_rustc_ty(span, &ty)?;
943        let place = Place::new_global(global_ref, ty);
944        match ptr_ty.kind() {
945            ty::TyKind::Ref(_, _, mutability) => {
946                let kind = if mutability.is_mut() {
947                    BorrowKind::Mut
948                } else {
949                    BorrowKind::Shared
950                };
951                Ok(Rvalue::Ref {
952                    place,
953                    kind,
954                    // Will be fixed by the cleanup pass `insert_ptr_metadata`.
955                    ptr_metadata: Self::missing_ptr_metadata(),
956                })
957            }
958            ty::TyKind::RawPtr(_, mutability) => {
959                let kind = if mutability.is_mut() {
960                    RefKind::Mut
961                } else {
962                    RefKind::Shared
963                };
964                Ok(Rvalue::RawPtr {
965                    place,
966                    kind,
967                    // Will be fixed by the cleanup pass `insert_ptr_metadata`.
968                    ptr_metadata: Self::missing_ptr_metadata(),
969                })
970            }
971            _ => raise_error!(
972                self,
973                span,
974                "unexpected type for thread-local reference: {ptr_ty:?}"
975            ),
976        }
977    }
978
979    fn translate_binaryop_kind(&mut self, _span: Span, binop: mir::BinOp) -> Result<BinOp, Error> {
980        Ok(match binop {
981            mir::BinOp::BitXor => BinOp::BitXor,
982            mir::BinOp::BitAnd => BinOp::BitAnd,
983            mir::BinOp::BitOr => BinOp::BitOr,
984            mir::BinOp::Eq => BinOp::Eq,
985            mir::BinOp::Lt => BinOp::Lt,
986            mir::BinOp::Le => BinOp::Le,
987            mir::BinOp::Ne => BinOp::Ne,
988            mir::BinOp::Ge => BinOp::Ge,
989            mir::BinOp::Gt => BinOp::Gt,
990            mir::BinOp::Add => BinOp::Add(OverflowMode::Wrap),
991            mir::BinOp::AddUnchecked => BinOp::Add(OverflowMode::UB),
992            mir::BinOp::Sub => BinOp::Sub(OverflowMode::Wrap),
993            mir::BinOp::SubUnchecked => BinOp::Sub(OverflowMode::UB),
994            mir::BinOp::Mul => BinOp::Mul(OverflowMode::Wrap),
995            mir::BinOp::MulUnchecked => BinOp::Mul(OverflowMode::UB),
996            mir::BinOp::Div => BinOp::Div(OverflowMode::UB),
997            mir::BinOp::Rem => BinOp::Rem(OverflowMode::UB),
998            mir::BinOp::AddWithOverflow => BinOp::AddChecked,
999            mir::BinOp::SubWithOverflow => BinOp::SubChecked,
1000            mir::BinOp::MulWithOverflow => BinOp::MulChecked,
1001            mir::BinOp::Shl => BinOp::Shl(OverflowMode::Wrap),
1002            mir::BinOp::ShlUnchecked => BinOp::Shl(OverflowMode::UB),
1003            mir::BinOp::Shr => BinOp::Shr(OverflowMode::Wrap),
1004            mir::BinOp::ShrUnchecked => BinOp::Shr(OverflowMode::UB),
1005            mir::BinOp::Cmp => BinOp::Cmp,
1006            mir::BinOp::Offset => BinOp::Offset,
1007        })
1008    }
1009
1010    fn translate_place(
1011        &mut self,
1012        span: Span,
1013        mir_place: &mir::Place<'tcx>,
1014    ) -> Result<Place, Error> {
1015        use crate::hax::{HasBase, SInto};
1016        use rustc_middle::ty;
1017
1018        let tcx = self.hax_state.base().tcx;
1019        let local_decls = self.local_decls;
1020        let mut place_ty: mir::PlaceTy = mir::Place::from(mir_place.local).ty(local_decls, tcx);
1021        let var_id = self.translate_local(&mir_place.local).unwrap();
1022        let mut place = self.locals.place_for_var(var_id);
1023        for elem in mir_place.projection.as_slice() {
1024            use mir::ProjectionElem::*;
1025            if let TyKind::Error(msg) = place.ty().kind() {
1026                return Err(Error {
1027                    span,
1028                    msg: msg.clone(),
1029                });
1030            }
1031            let projected_place_ty = place_ty.projection_ty(tcx, *elem);
1032            let next_place_ty = projected_place_ty.ty.sinto(&self.hax_state);
1033            let next_place_ty = self.translate_ty(span, &next_place_ty)?;
1034            let proj_elem = match elem {
1035                Deref => ProjectionElem::Deref,
1036                Field(index, _) => {
1037                    let TyKind::Adt(tref) = place.ty().kind() else {
1038                        raise_error!(
1039                            self,
1040                            span,
1041                            "found unexpected type in field projection: {}",
1042                            next_place_ty.with_ctx(&self.into_fmt())
1043                        )
1044                    };
1045                    let field_id = self.translate_field_id(*index);
1046                    match place_ty.ty.kind() {
1047                        ty::Adt(adt_def, _) => {
1048                            let variant = place_ty.variant_index;
1049                            let variant_id = variant.map(|id| self.translate_variant_id(id));
1050                            let generics = &tref.generics;
1051                            match tref.as_builtin() {
1052                                None => {
1053                                    assert!(
1054                                        ((adt_def.is_struct() || adt_def.is_union())
1055                                            && variant.is_none())
1056                                            || (adt_def.is_enum() && variant.is_some())
1057                                    );
1058                                    ProjectionElem::Field(variant_id, field_id)
1059                                }
1060                                Some(BuiltinAdt::Tuple) => {
1061                                    assert!(generics.regions.is_empty());
1062                                    assert!(variant.is_none());
1063                                    assert!(generics.const_generics.is_empty());
1064                                    ProjectionElem::Field(None, field_id)
1065                                }
1066                                Some(BuiltinAdt::Box)
1067                                    if self.t_ctx.options.treat_box_as_builtin =>
1068                                {
1069                                    // Some sanity checks
1070                                    assert!(generics.regions.is_empty());
1071                                    assert!(generics.types.len() == 2);
1072                                    assert!(generics.const_generics.is_empty());
1073                                    if field_id == FieldId::ZERO {
1074                                        // We pretend the pointee field is a deref.
1075                                        ProjectionElem::Deref
1076                                    } else {
1077                                        raise_error!(
1078                                            self,
1079                                            span,
1080                                            "trying to access the allocator field from Box, \
1081                                            but it is being treated as a builtin (without allocator)"
1082                                        )
1083                                    }
1084                                }
1085                                Some(BuiltinAdt::Box) => ProjectionElem::Field(None, field_id),
1086                                Some(_) => {
1087                                    raise_error!(self, span, "Unexpected field projection")
1088                                }
1089                            }
1090                        }
1091                        ty::Tuple(_types) => ProjectionElem::Field(None, field_id),
1092                        // We get there when we access one of the fields of the state captured by a
1093                        // closure.
1094                        ty::Closure(..) => ProjectionElem::Field(None, field_id),
1095                        _ => panic!(),
1096                    }
1097                }
1098                Index(local) => {
1099                    let var_id = self.translate_local(local).unwrap();
1100                    let local = self.locals.place_for_var(var_id);
1101                    let offset = Operand::Copy(local);
1102                    ProjectionElem::Index {
1103                        offset: Box::new(offset),
1104                        from_end: false,
1105                    }
1106                }
1107                &ConstantIndex {
1108                    offset, from_end, ..
1109                } => {
1110                    let offset =
1111                        Operand::Const(IntegerValue::mk_usize(offset as u128).to_constant());
1112                    ProjectionElem::Index {
1113                        offset: Box::new(offset),
1114                        from_end,
1115                    }
1116                }
1117                &Subslice { from, to, from_end } => {
1118                    let from = Operand::Const(IntegerValue::mk_usize(from as u128).to_constant());
1119                    let to = Operand::Const(IntegerValue::mk_usize(to as u128).to_constant());
1120                    ProjectionElem::Subslice {
1121                        from: Box::new(from),
1122                        to: Box::new(to),
1123                        from_end,
1124                    }
1125                }
1126                OpaqueCast(..) => {
1127                    raise_error!(self, span, "Unexpected ProjectionElem::OpaqueCast");
1128                }
1129                Downcast { .. } => {
1130                    // We keep the same `Place`, the variant is tracked in the `PlaceTy` and we can
1131                    // access it next loop iteration.
1132                    place_ty = projected_place_ty;
1133                    continue;
1134                }
1135                UnwrapUnsafeBinder { .. } => {
1136                    raise_error!(self, span, "unsupported feature: unsafe binders");
1137                }
1138            };
1139            place = place.project(proj_elem, next_place_ty);
1140            place_ty = projected_place_ty;
1141        }
1142        Ok(place)
1143    }
1144
1145    /// Translate an operand
1146    fn translate_operand(
1147        &mut self,
1148        span: Span,
1149        operand: &mir::Operand<'tcx>,
1150    ) -> Result<Operand, Error> {
1151        Ok(match operand {
1152            mir::Operand::Copy(place) => {
1153                let p = self.translate_place(span, place)?;
1154                Operand::Copy(p)
1155            }
1156            mir::Operand::Move(place) => {
1157                let p = self.translate_place(span, place)?;
1158                Operand::Move(p)
1159            }
1160            mir::Operand::Constant(const_op) => {
1161                let const_op = self.catch_sinto(span, &const_op)?;
1162                match &const_op.kind {
1163                    hax::ConstOperandKind::Value(constant) => {
1164                        let constant = self.translate_constant_expr(span, constant)?;
1165                        // Avoid large array constant.
1166                        if let ConstantExprKind::Array(fields) = constant.kind()
1167                            && matches!(const_op.ty.kind(), hax::TyKind::Array(_))
1168                            && let Some(repeat) = self.try_reconstruct_array_repeat(
1169                                span,
1170                                &const_op.ty,
1171                                fields.iter().cloned(),
1172                            )?
1173                        {
1174                            let local = self.fresh_var(None, constant.ty().clone());
1175                            self.insert_assn_stmt(local.clone(), repeat);
1176                            return Ok(Operand::Move(local));
1177                        }
1178                        Operand::Const(constant)
1179                    }
1180                    hax::ConstOperandKind::Promoted(item) => {
1181                        // A promoted constant that could not be evaluated.
1182                        let global_ref = self.translate_global_decl_ref(span, item)?;
1183                        let constant = ConstantExpr::new(
1184                            ConstantExprKind::Global(global_ref),
1185                            self.translate_ty(span, &const_op.ty)?,
1186                        );
1187                        Operand::Const(constant)
1188                    }
1189                }
1190            }
1191            mir::Operand::RuntimeChecks(check) => {
1192                let op = match check {
1193                    mir::RuntimeChecks::UbChecks => NullOp::UbChecks,
1194                    mir::RuntimeChecks::OverflowChecks => NullOp::OverflowChecks,
1195                    mir::RuntimeChecks::ContractChecks => NullOp::ContractChecks,
1196                };
1197                let local = self.fresh_var(None, Ty::mk_bool());
1198                self.insert_assn_stmt(local.clone(), Rvalue::NullaryOp(op));
1199                Operand::Move(local)
1200            }
1201        })
1202    }
1203
1204    /// Translate an rvalue
1205    fn translate_mir_rvalue(
1206        &mut self,
1207        span: Span,
1208        rvalue: &mir::Rvalue<'tcx>,
1209        tgt_ty: &Ty,
1210    ) -> Result<Rvalue, Error> {
1211        match rvalue {
1212            mir::Rvalue::Use(operand, retag) => {
1213                let retag = match retag {
1214                    mir::WithRetag::Yes => WithRetag::Yes,
1215                    mir::WithRetag::No => WithRetag::No,
1216                };
1217                Ok(Rvalue::Use(self.translate_operand(span, operand)?, retag))
1218            }
1219            mir::Rvalue::CopyForDeref(place) => {
1220                // According to the documentation, it seems to be an optimisation
1221                // for drop elaboration. We treat it as a regular copy.
1222                let place = self.translate_place(span, place)?;
1223                Ok(Rvalue::Use(Operand::Copy(place), WithRetag::No))
1224            }
1225            mir::Rvalue::Repeat(operand, cnst) => {
1226                let ty_is_copy = {
1227                    let rust_ty = operand.ty(self.local_decls, self.tcx);
1228                    let proof = hax::solve_copy(&self.hax_state, rust_ty);
1229                    self.translate_trait_proof(span, &proof)?
1230                };
1231                let c = self.translate_ty_constant_expr(span, cnst)?;
1232                let op = self.translate_operand(span, operand)?;
1233                let ty = op.ty().clone();
1234                // Remark: we could desugar this into a function call later.
1235                Ok(Rvalue::Repeat(op, ty, c, ty_is_copy))
1236            }
1237            mir::Rvalue::Ref(_region, borrow_kind, place) => {
1238                let place = self.translate_place(span, place)?;
1239                let borrow_kind = self.translate_borrow_kind(*borrow_kind);
1240                Ok(Rvalue::Ref {
1241                    place,
1242                    kind: borrow_kind,
1243                    // Will be fixed by the cleanup pass `insert_ptr_metadata`.
1244                    ptr_metadata: Self::missing_ptr_metadata(),
1245                })
1246            }
1247            mir::Rvalue::RawPtr(mtbl, place) => {
1248                let mtbl = match mtbl {
1249                    mir::RawPtrKind::Mut => RefKind::Mut,
1250                    mir::RawPtrKind::Const => RefKind::Shared,
1251                    mir::RawPtrKind::FakeForPtrMetadata => RefKind::Shared,
1252                };
1253                let place = self.translate_place(span, place)?;
1254                Ok(Rvalue::RawPtr {
1255                    place,
1256                    kind: mtbl,
1257                    // Will be fixed by the cleanup pass `insert_ptr_metadata`.
1258                    ptr_metadata: Self::missing_ptr_metadata(),
1259                })
1260            }
1261            mir::Rvalue::Cast(cast_kind, mir_operand, rust_tgt_ty) => {
1262                let op_ty = mir_operand.ty(self.local_decls, self.tcx);
1263                let tgt_ty = self.translate_rustc_ty(span, rust_tgt_ty)?;
1264
1265                // Translate the operand
1266                let mut operand = self.translate_operand(span, mir_operand)?;
1267                let src_ty = operand.ty().clone();
1268
1269                let cast_kind = match cast_kind {
1270                    mir::CastKind::IntToInt
1271                    | mir::CastKind::IntToFloat
1272                    | mir::CastKind::FloatToInt
1273                    | mir::CastKind::FloatToFloat => {
1274                        let tgt_ty = *tgt_ty.kind().as_scalar().unwrap();
1275                        let src_ty = *src_ty.kind().as_scalar().unwrap();
1276                        CastKind::Scalar(src_ty, tgt_ty)
1277                    }
1278                    mir::CastKind::PtrToPtr
1279                    | mir::CastKind::PointerCoercion(
1280                        ty::adjustment::PointerCoercion::MutToConstPointer,
1281                        ..,
1282                    )
1283                    | mir::CastKind::PointerCoercion(
1284                        ty::adjustment::PointerCoercion::ArrayToPointer,
1285                        ..,
1286                    )
1287                    | mir::CastKind::FnPtrToPtr
1288                    | mir::CastKind::PointerExposeProvenance
1289                    | mir::CastKind::PointerWithExposedProvenance => {
1290                        CastKind::RawPtr(src_ty, tgt_ty)
1291                    }
1292                    mir::CastKind::PointerCoercion(
1293                        ty::adjustment::PointerCoercion::ClosureFnPointer(_),
1294                        ..,
1295                    ) => {
1296                        let hax_op_ty: hax::Ty = self.catch_sinto(span, &op_ty)?;
1297                        // We model casts of closures to function pointers by generating a new
1298                        // function item without the closure's state, that calls the actual closure.
1299                        let hax::TyKind::Closure(closure, ..) = hax_op_ty.kind() else {
1300                            unreachable!("Non-closure type in PointerCoercion::ClosureFnPointer");
1301                        };
1302                        let fn_ref: RegionBinder<FunDeclRef> =
1303                            self.translate_stateless_closure_as_fn_ref(span, closure)?;
1304                        let fn_ptr_bound: RegionBinder<FnPtr> = fn_ref.map(FunDeclRef::into);
1305                        let fn_ptr: FnPtr = self.erase_region_binder(fn_ptr_bound.clone());
1306                        let src_ty = TyKind::FnDef(fn_ptr_bound).into_ty();
1307                        operand = Operand::Const(ConstantExpr::new(
1308                            ConstantExprKind::FnDef(fn_ptr),
1309                            src_ty.clone(),
1310                        ));
1311                        CastKind::FnPtr(src_ty, tgt_ty)
1312                    }
1313                    mir::CastKind::PointerCoercion(
1314                        ty::adjustment::PointerCoercion::UnsafeFnPointer
1315                        | ty::adjustment::PointerCoercion::ReifyFnPointer(_),
1316                        ..,
1317                    ) => CastKind::FnPtr(src_ty, tgt_ty),
1318                    mir::CastKind::Transmute | mir::CastKind::BoxDerefTransmute => {
1319                        CastKind::Transmute(src_ty, tgt_ty)
1320                    }
1321                    // TODO
1322                    mir::CastKind::Subtype => CastKind::Transmute(src_ty, tgt_ty),
1323                    mir::CastKind::PointerCoercion(ty::adjustment::PointerCoercion::Unsize, ..) => {
1324                        let meta =
1325                            hax::compute_unsizing_metadata(&self.hax_state, op_ty, *rust_tgt_ty);
1326                        let meta = self.translate_unsizing_metadata(span, meta)?;
1327                        CastKind::Unsize(src_ty, tgt_ty.clone(), meta)
1328                    }
1329                };
1330                let unop = UnOp::Cast(cast_kind);
1331                Ok(Rvalue::UnaryOp(unop, operand))
1332            }
1333            mir::Rvalue::BinaryOp(binop, (left, right)) => Ok(Rvalue::BinaryOp(
1334                self.translate_binaryop_kind(span, *binop)?,
1335                self.translate_operand(span, left)?,
1336                self.translate_operand(span, right)?,
1337            )),
1338            mir::Rvalue::UnaryOp(unop, operand) => {
1339                let operand = self.translate_operand(span, operand)?;
1340                let unop = match unop {
1341                    mir::UnOp::Not => UnOp::Not,
1342                    mir::UnOp::Neg => UnOp::Neg(OverflowMode::Wrap),
1343                    mir::UnOp::PtrMetadata => match operand {
1344                        Operand::Copy(p) | Operand::Move(p) => {
1345                            return Ok(Rvalue::Use(
1346                                Operand::Copy(
1347                                    p.project(ProjectionElem::PtrMetadata, tgt_ty.clone()),
1348                                ),
1349                                WithRetag::No,
1350                            ));
1351                        }
1352                        Operand::Const(_) => {
1353                            panic!("unexpected metadata operand")
1354                        }
1355                    },
1356                };
1357                Ok(Rvalue::UnaryOp(unop, operand))
1358            }
1359            mir::Rvalue::Discriminant(place) => {
1360                let place = self.translate_place(span, place)?;
1361                Ok(Rvalue::Discriminant(place))
1362            }
1363            mir::Rvalue::Aggregate(aggregate_kind, operands) => {
1364                // It seems this instruction is not present in certain passes:
1365                // for example, it seems it is not used in optimized MIR, where
1366                // ADT initialization is split into several instructions, for
1367                // instance:
1368                // ```
1369                // p = Pair { x:xv, y:yv };
1370                // ```
1371                // Might become:
1372                // ```
1373                // p.x = x;
1374                // p.y = yv;
1375                // ```
1376
1377                // First translate the operands
1378                let operands_t: Vec<Operand> = operands
1379                    .iter()
1380                    .map(|op| self.translate_operand(span, op))
1381                    .try_collect()?;
1382                match aggregate_kind {
1383                    mir::AggregateKind::Array(ty) => {
1384                        let t_ty = self.translate_rustc_ty(span, ty)?;
1385                        if operands_t.iter().all(Operand::is_const) {
1386                            let rust_array_ty =
1387                                ty::Ty::new_array(self.tcx, *ty, operands_t.len() as u64);
1388                            let hax_array_ty = self.catch_sinto(span, &rust_array_ty)?;
1389                            let fields = operands_t
1390                                .iter()
1391                                .map(|operand| operand.as_const().unwrap())
1392                                .cloned();
1393                            if let Some(repeat) =
1394                                self.try_reconstruct_array_repeat(span, &hax_array_ty, fields)?
1395                            {
1396                                return Ok(repeat);
1397                            }
1398                        }
1399                        let c = ConstantExpr::mk_usize(operands_t.len() as u128);
1400                        let TyKind::Array(_, _, ty_is_sized) = tgt_ty.kind() else {
1401                            raise_error!(self, span, "array aggregate has non-array type")
1402                        };
1403                        Ok(Rvalue::Aggregate(
1404                            AggregateKind::Array(t_ty, c, ty_is_sized.clone()),
1405                            operands_t,
1406                        ))
1407                    }
1408                    mir::AggregateKind::Tuple => {
1409                        let tys = operands.iter().map(|op| op.ty(self.local_decls, self.tcx));
1410                        let ty = ty::Ty::new_tup_from_iter(self.tcx, tys);
1411                        let ty = self.translate_rustc_ty(span, &ty)?;
1412                        let tref = ty.as_adt().unwrap().clone();
1413                        Ok(Rvalue::Aggregate(
1414                            AggregateKind::Adt(tref, None, None),
1415                            operands_t,
1416                        ))
1417                    }
1418                    mir::AggregateKind::Adt(def_id, variant_idx, generics, _, field_index) => {
1419                        use ty::AdtKind;
1420                        trace!("{:?}", rvalue);
1421
1422                        let adt_kind = self.tcx.adt_def(*def_id).adt_kind();
1423                        let item = hax::translate_item_ref(&self.hax_state, *def_id, generics);
1424                        let tref = self.translate_type_decl_ref(span, &item)?;
1425                        let variant_id = match adt_kind {
1426                            AdtKind::Struct | AdtKind::Union => None,
1427                            AdtKind::Enum => Some(self.translate_variant_id(*variant_idx)),
1428                        };
1429                        let field_id = match adt_kind {
1430                            AdtKind::Struct | AdtKind::Enum => None,
1431                            AdtKind::Union => Some(self.translate_field_id(field_index.unwrap())),
1432                        };
1433
1434                        let akind = AggregateKind::Adt(tref, variant_id, field_id);
1435                        Ok(Rvalue::Aggregate(akind, operands_t))
1436                    }
1437                    mir::AggregateKind::Closure(def_id, generics) => {
1438                        let args = hax::ClosureArgs::sfrom(&self.hax_state, *def_id, generics);
1439                        let tref = self.translate_closure_type_ref(span, &args)?;
1440                        let akind = AggregateKind::Adt(tref, None, None);
1441                        Ok(Rvalue::Aggregate(akind, operands_t))
1442                    }
1443                    mir::AggregateKind::RawPtr(ty, mutability) => {
1444                        let t_ty = self.translate_rustc_ty(span, ty)?;
1445                        let mutability = if mutability.is_mut() {
1446                            RefKind::Mut
1447                        } else {
1448                            RefKind::Shared
1449                        };
1450
1451                        let akind = AggregateKind::RawPtr(t_ty, mutability);
1452
1453                        Ok(Rvalue::Aggregate(akind, operands_t))
1454                    }
1455                    mir::AggregateKind::Coroutine(..)
1456                    | mir::AggregateKind::CoroutineClosure(..) => {
1457                        raise_error!(self, span, "Coroutines are not supported");
1458                    }
1459                }
1460            }
1461            mir::Rvalue::ThreadLocalRef(def_id) => self.translate_thread_local_ref(span, *def_id),
1462            mir::Rvalue::WrapUnsafeBinder { .. } => {
1463                raise_error!(
1464                    self,
1465                    span,
1466                    "charon does not support unsafe lifetime binders"
1467                );
1468            }
1469            mir::Rvalue::Reborrow(..) => {
1470                raise_error!(
1471                    self,
1472                    span,
1473                    "charon does not support reborrow rvalues (for Reborrow traits)"
1474                );
1475            }
1476        }
1477    }
1478
1479    /// Translate a statement.
1480    fn translate_statement(
1481        &mut self,
1482        source_scopes: &rustc_index::IndexVec<mir::SourceScope, mir::SourceScopeData>,
1483        statement: &mir::Statement<'tcx>,
1484    ) -> Result<(), Error> {
1485        trace!("About to translate statement (MIR) {:?}", statement);
1486        let span = self.translate_span_from_source_info(source_scopes, &statement.source_info);
1487
1488        self.span = span;
1489        let kind: Option<StatementKind> = match &statement.kind {
1490            mir::StatementKind::Assign((place, rvalue)) => {
1491                let t_place = self.translate_place(span, place)?;
1492                let t_rvalue = self.translate_mir_rvalue(span, rvalue, t_place.ty())?;
1493                Some(StatementKind::Assign(t_place, t_rvalue))
1494            }
1495            mir::StatementKind::SetDiscriminant {
1496                place,
1497                variant_index,
1498            } => {
1499                let t_place = self.translate_place(span, place)?;
1500                let variant_id = self.translate_variant_id(*variant_index);
1501                Some(StatementKind::SetDiscriminant(t_place, variant_id))
1502            }
1503            mir::StatementKind::StorageLive(local) => {
1504                let var_id = self.translate_local(local).unwrap();
1505                Some(StatementKind::StorageLive(var_id))
1506            }
1507            mir::StatementKind::StorageDead(local) => {
1508                let var_id = self.translate_local(local).unwrap();
1509                Some(StatementKind::StorageDead(var_id))
1510            }
1511            mir::StatementKind::Intrinsic(mir::NonDivergingIntrinsic::Assume(op)) => {
1512                let op = self.translate_operand(span, op)?;
1513                self.translate_intrinsic_call(
1514                    span,
1515                    sym::assume,
1516                    ty::GenericArgs::empty(),
1517                    vec![op],
1518                )?;
1519                None
1520            }
1521            mir::StatementKind::Intrinsic(mir::NonDivergingIntrinsic::CopyNonOverlapping(
1522                mir::CopyNonOverlapping { src, dst, count },
1523            )) => {
1524                let pointee_ty = src
1525                    .ty(self.local_decls, self.tcx)
1526                    .builtin_deref(true)
1527                    .unwrap();
1528                let generic_args = self.tcx.mk_args(&[pointee_ty.into()]);
1529                let src = self.translate_operand(span, src)?;
1530                let dst = self.translate_operand(span, dst)?;
1531                let count = self.translate_operand(span, count)?;
1532                self.translate_intrinsic_call(
1533                    span,
1534                    sym::copy_nonoverlapping,
1535                    generic_args,
1536                    vec![src, dst, count],
1537                )?;
1538                None
1539            }
1540            mir::StatementKind::PlaceMention(place) => {
1541                let place = self.translate_place(span, place)?;
1542                // We only translate this for places with projections, as
1543                // no UB can arise from simply mentioning a local variable.
1544                if place.is_local() {
1545                    None
1546                } else {
1547                    Some(StatementKind::PlaceMention(place))
1548                }
1549            }
1550            mir::StatementKind::FakeRead((_, place)) => {
1551                let place = self.translate_place(span, place)?;
1552                Some(StatementKind::Borrowck(BorrowckStatement::FakeRead(place)))
1553            }
1554            mir::StatementKind::AscribeUserType((place, user_ty), variance) => {
1555                let variance = match variance {
1556                    ty::Variance::Covariant => Variance::Covariant,
1557                    ty::Variance::Invariant => Variance::Invariant,
1558                    ty::Variance::Contravariant => Variance::Contravariant,
1559                    // Does nothing so we discard it.
1560                    ty::Variance::Bivariant => return Ok(()),
1561                };
1562                let place = self.translate_place(span, place)?;
1563                let (ty, facts) = self.translate_user_type_projection(span, user_ty)?;
1564                self.statements.push(Statement::new(
1565                    span,
1566                    StatementKind::Borrowck(BorrowckStatement::SetType {
1567                        place,
1568                        ty,
1569                        variance,
1570                    }),
1571                ));
1572                self.statements.extend(
1573                    facts
1574                        .into_iter()
1575                        .map(|fact| Statement::new(span, StatementKind::Borrowck(fact))),
1576                );
1577                None
1578            }
1579            // Used for coverage instrumentation.
1580            mir::StatementKind::Coverage(_) => None,
1581            // Used in the interpreter to check that const code doesn't run for too long or even
1582            // indefinitely.
1583            mir::StatementKind::ConstEvalCounter => None,
1584            // Semantically equivalent to `Nop`, used only for rustc lints.
1585            mir::StatementKind::BackwardIncompatibleDropHint { .. } => None,
1586            mir::StatementKind::Nop => None,
1587        };
1588
1589        let Some(kind) = kind else {
1590            return Ok(());
1591        };
1592        self.statements.push(Statement::new(span, kind));
1593        Ok(())
1594    }
1595
1596    /// Translate a call to a non-diverging intrinsic.
1597    fn translate_intrinsic_call(
1598        &mut self,
1599        span: Span,
1600        name: Symbol,
1601        generic_args: ty::GenericArgsRef<'tcx>,
1602        args: Vec<Operand>,
1603    ) -> Result<(), Error> {
1604        // Sadly rustc doesn't expose a Symbol -> DefId map for intrinsics.
1605        let path = NamePattern::parse(&format!("core::intrinsics::{name}")).unwrap();
1606        let def_id = self.resolve_single_path(span, &path)?;
1607        assert!(self.tcx.is_intrinsic(def_id, name));
1608        let item = hax::ItemRef::translate(self.hax_state_with_id(), def_id, generic_args);
1609        let func =
1610            FnOperand::Regular(self.translate_fn_ptr(span, &item, TransItemSourceKind::Fun)?);
1611        let dest = self.locals.new_var(None, Ty::mk_unit());
1612        self.push_nounwind_call(span, Call { func, args, dest });
1613        Ok(())
1614    }
1615
1616    /// Translate a terminator
1617    fn translate_terminator(
1618        mut self,
1619        source_scopes: &rustc_index::IndexVec<mir::SourceScope, mir::SourceScopeData>,
1620        terminator: &mir::Terminator<'tcx>,
1621    ) -> Result<(), Error> {
1622        trace!("About to translate terminator (MIR) {:?}", terminator);
1623        let span = self.translate_span_from_source_info(source_scopes, &terminator.source_info);
1624
1625        // Translate the terminator
1626        self.span = span;
1627        use mir::TerminatorKind;
1628        let kind: ullbc_ast::TerminatorKind = match &terminator.kind {
1629            TerminatorKind::Goto { target } => {
1630                let target = self.translate_basic_block_id(*target);
1631                ullbc_ast::TerminatorKind::Goto { target }
1632            }
1633            TerminatorKind::SwitchInt { discr, targets, .. } => {
1634                let discr = self.translate_operand(span, discr)?;
1635                let (data, branches) = self.translate_switch_targets(span, discr, targets)?;
1636                ullbc_ast::TerminatorKind::Switch { data, branches }
1637            }
1638            TerminatorKind::UnwindResume => ullbc_ast::TerminatorKind::UnwindResume,
1639            TerminatorKind::UnwindTerminate { .. } => {
1640                ullbc_ast::TerminatorKind::Abort(AbortKind::UnwindTerminate)
1641            }
1642            TerminatorKind::Return => ullbc_ast::TerminatorKind::Return,
1643            // A MIR `Unreachable` terminator indicates undefined behavior of the rust abstract
1644            // machine.
1645            TerminatorKind::Unreachable => {
1646                ullbc_ast::TerminatorKind::Abort(AbortKind::UndefinedBehavior)
1647            }
1648            TerminatorKind::Drop {
1649                place,
1650                target,
1651                unwind,
1652                ..
1653            } => self.translate_drop(span, place, target, unwind)?,
1654            TerminatorKind::Call {
1655                func,
1656                args,
1657                destination,
1658                target,
1659                unwind,
1660                ..
1661            } => self.translate_function_call(span, func, args, destination, target, unwind)?,
1662            TerminatorKind::Assert {
1663                cond,
1664                expected,
1665                msg,
1666                target,
1667                unwind,
1668            } => {
1669                let on_unwind = self.translate_unwind_action(span, unwind);
1670                let kind = self.translate_assert_kind(span, msg)?;
1671                let assert = Assert {
1672                    cond: self.translate_operand(span, cond)?,
1673                    expected: *expected,
1674                    check_kind: Some(kind),
1675                };
1676                let target = self.translate_basic_block_id(*target);
1677                ullbc_ast::TerminatorKind::Assert {
1678                    assert,
1679                    target,
1680                    on_unwind,
1681                }
1682            }
1683            TerminatorKind::FalseEdge {
1684                real_target,
1685                imaginary_target: _,
1686            } => {
1687                // False edges are used to make the borrow checker a bit conservative.
1688                // We translate them as Gotos.
1689                // Also note that they are used in some passes, and not in some others
1690                // (they are present in mir_promoted, but not mir_optimized).
1691                let target = self.translate_basic_block_id(*real_target);
1692                ullbc_ast::TerminatorKind::Goto { target }
1693            }
1694            TerminatorKind::FalseUnwind {
1695                real_target,
1696                unwind: _,
1697            } => {
1698                // We consider this to be a goto
1699                let target = self.translate_basic_block_id(*real_target);
1700                ullbc_ast::TerminatorKind::Goto { target }
1701            }
1702            TerminatorKind::InlineAsm {
1703                template,
1704                targets,
1705                unwind,
1706                ..
1707            } => {
1708                let asm = rustc_ast::ast::InlineAsmTemplatePiece::to_string(template);
1709                let targets = targets
1710                    .iter()
1711                    .map(|target| self.translate_basic_block_id(*target))
1712                    .collect();
1713                let on_unwind = self.translate_unwind_action(span, unwind);
1714                ullbc_ast::TerminatorKind::InlineAsm {
1715                    asm,
1716                    targets,
1717                    on_unwind,
1718                }
1719            }
1720            TerminatorKind::CoroutineDrop
1721            | TerminatorKind::TailCall { .. }
1722            | TerminatorKind::Yield { .. } => {
1723                raise_error!(self, span, "Unsupported terminator: {:?}", terminator.kind);
1724            }
1725        };
1726
1727        self.finish_current_block(Terminator::new(span, kind));
1728        Ok(())
1729    }
1730
1731    /// Translate switch targets
1732    fn translate_switch_targets(
1733        &mut self,
1734        span: Span,
1735        discr: Operand,
1736        targets: &mir::SwitchTargets,
1737    ) -> Result<(SwitchData, IndexVec<BranchId, BlockId>), Error> {
1738        // Convert all the test values to the proper values.
1739        let otherwise = targets.otherwise();
1740        let switch_ty = discr.ty();
1741        let switch_scalar_ty = *switch_ty.as_scalar().unwrap();
1742        let mut branch_targets: IndexVec<BranchId, BlockId> = IndexVec::new();
1743        let mut target_to_branch: SeqHashMap<BlockId, BranchId> = SeqHashMap::new();
1744        let mut switch_branches = Vec::with_capacity(targets.iter().count());
1745
1746        // Keep the historical true-then-false traversal order for boolean switches.
1747        let bool_fallback = (switch_scalar_ty == ScalarTy::Bool).then(|| {
1748            let target = self.translate_basic_block_id(otherwise);
1749            *target_to_branch
1750                .entry(target)
1751                .or_insert_with(|| branch_targets.push(target))
1752        });
1753
1754        for (bits, target) in targets.iter() {
1755            let Some(kind) = ConstantExprKind::from_bits(&switch_scalar_ty, bits) else {
1756                raise_error!(self, span, "Can't match on type {switch_scalar_ty}")
1757            };
1758            let target = self.translate_basic_block_id(target);
1759            let branch_id = *target_to_branch
1760                .entry(target)
1761                .or_insert_with(|| branch_targets.push(target));
1762            let value = ConstantExpr::new(kind, switch_ty.clone());
1763            switch_branches.push((value, branch_id));
1764        }
1765
1766        let fallback = bool_fallback.unwrap_or_else(|| {
1767            let target = self.translate_basic_block_id(otherwise);
1768            *target_to_branch
1769                .entry(target)
1770                .or_insert_with(|| branch_targets.push(target))
1771        });
1772        let data = SwitchData {
1773            scrutinee: SwitchScrutinee::Value(discr),
1774            branches: switch_branches,
1775            fallback: Some(fallback),
1776        };
1777        Ok((data, branch_targets))
1778    }
1779
1780    /// Translate a function call statement.
1781    /// Note that `body` is the body of the function being translated, not of the
1782    /// function referenced in the function call: we need it in order to translate
1783    /// the blocks we go to after the function call returns.
1784    #[allow(clippy::too_many_arguments)]
1785    fn translate_function_call(
1786        &mut self,
1787        span: Span,
1788        func: &mir::Operand<'tcx>,
1789        args: &[hax::Spanned<mir::Operand<'tcx>>],
1790        destination: &mir::Place<'tcx>,
1791        target: &Option<mir::BasicBlock>,
1792        unwind: &mir::UnwindAction,
1793    ) -> Result<TerminatorKind, Error> {
1794        let tcx = self.tcx;
1795        let op_ty = func.ty(self.local_decls, tcx);
1796        // There are two cases, depending on whether this is a "regular"
1797        // call to a top-level function identified by its id, or if we
1798        // are using a local function pointer (i.e., the operand is a "move").
1799        let lval = self.translate_place(span, destination)?;
1800        let on_unwind = self.translate_unwind_action(span, unwind);
1801        // Translate the function operand.
1802        let fn_operand = match op_ty.kind() {
1803            ty::TyKind::FnDef(def_id, generics) => {
1804                // The type of the value is one of the singleton types that corresponds to each function,
1805                // which is enough information.
1806                let generics = generics.no_bound_vars().expect("bound variables in FnDef");
1807                let item = &hax::translate_item_ref(&self.hax_state, *def_id, generics);
1808                trace!("func: {:?}", item.def_id);
1809                let fun_def = self.hax_def(item)?;
1810                let item_src =
1811                    TransItemSource::from_item(item, TransItemSourceKind::Fun, self.monomorphize());
1812                let name = self.t_ctx.translate_name(&item_src)?;
1813                let panic_lang_items = &["panic", "panic_fmt", "begin_panic"];
1814                let panic_names = &[&["core", "panicking", "assert_failed"], EXPLICIT_PANIC_NAME];
1815
1816                if fun_def
1817                    .lang_item
1818                    .as_ref()
1819                    .is_some_and(|lang_it| panic_lang_items.iter().contains(&lang_it.as_str()))
1820                    || panic_names.iter().any(|panic| name.equals_ref_name(panic))
1821                {
1822                    // If the call is `panic!`, then the target is `None`.
1823                    // I don't know in which other cases it can be `None`.
1824                    assert!(target.is_none());
1825                    // We ignore the arguments
1826                    // TODO: shouldn't we do something with the unwind edge?
1827                    return Ok(TerminatorKind::Abort(AbortKind::Panic(Some(name))));
1828                } else {
1829                    let fn_ptr = self.translate_fn_ptr(span, item, TransItemSourceKind::Fun)?;
1830                    FnOperand::Regular(fn_ptr)
1831                }
1832            }
1833            _ => {
1834                // Call to a function pointer.
1835                let op = self.translate_operand(span, func)?;
1836                FnOperand::Dynamic(op)
1837            }
1838        };
1839        let args = self.translate_arguments(span, args)?;
1840        let call = Call {
1841            func: fn_operand,
1842            args,
1843            dest: lval,
1844        };
1845
1846        let target = match target {
1847            Some(target) => self.translate_basic_block_id(*target),
1848            None => {
1849                let abort =
1850                    Terminator::new(span, TerminatorKind::Abort(AbortKind::UndefinedBehavior));
1851                self.blocks.push(abort.into_block())
1852            }
1853        };
1854
1855        Ok(TerminatorKind::Call {
1856            call,
1857            target,
1858            on_unwind,
1859        })
1860    }
1861
1862    /// Translate a drop terminator
1863    #[allow(clippy::too_many_arguments)]
1864    fn translate_drop(
1865        &mut self,
1866        span: Span,
1867        place: &mir::Place<'tcx>,
1868        target: &mir::BasicBlock,
1869        unwind: &mir::UnwindAction,
1870    ) -> Result<TerminatorKind, Error> {
1871        let place_ty = place.ty(self.local_decls, self.tcx).ty;
1872        let fn_ptr = self.translate_drop_glue_method_call(span, place_ty)?;
1873        let place = self.translate_place(span, place)?;
1874        let target = self.translate_basic_block_id(*target);
1875        let on_unwind = self.translate_unwind_action(span, unwind);
1876
1877        Ok(TerminatorKind::Drop {
1878            kind: self.drop_kind,
1879            place,
1880            fn_ptr,
1881            target,
1882            on_unwind,
1883        })
1884    }
1885
1886    // construct unwind block for the terminators
1887    fn translate_unwind_action(&mut self, span: Span, unwind: &mir::UnwindAction) -> BlockId {
1888        match unwind {
1889            mir::UnwindAction::Continue => {
1890                let unwind_continue = Terminator::new(span, TerminatorKind::UnwindResume);
1891                self.blocks.push(unwind_continue.into_block())
1892            }
1893            mir::UnwindAction::Unreachable => {
1894                let abort =
1895                    Terminator::new(span, TerminatorKind::Abort(AbortKind::UndefinedBehavior));
1896                self.blocks.push(abort.into_block())
1897            }
1898            mir::UnwindAction::Terminate(..) => {
1899                let abort =
1900                    Terminator::new(span, TerminatorKind::Abort(AbortKind::UnwindTerminate));
1901                self.blocks.push(abort.into_block())
1902            }
1903            mir::UnwindAction::Cleanup(bb) => self.translate_basic_block_id(*bb),
1904        }
1905    }
1906
1907    fn translate_assert_kind(
1908        &mut self,
1909        span: Span,
1910        kind: &mir::AssertKind<mir::Operand<'tcx>>,
1911    ) -> Result<BuiltinAssertKind, Error> {
1912        match kind {
1913            mir::AssertKind::BoundsCheck { len, index } => {
1914                let len = self.translate_operand(span, len)?;
1915                let index = self.translate_operand(span, index)?;
1916                Ok(BuiltinAssertKind::BoundsCheck { len, index })
1917            }
1918            mir::AssertKind::Overflow(binop, left, right) => {
1919                let binop = self.translate_binaryop_kind(span, *binop)?;
1920                let left = self.translate_operand(span, left)?;
1921                let right = self.translate_operand(span, right)?;
1922                Ok(BuiltinAssertKind::Overflow(binop, left, right))
1923            }
1924            mir::AssertKind::OverflowNeg(operand) => {
1925                let operand = self.translate_operand(span, operand)?;
1926                Ok(BuiltinAssertKind::OverflowNeg(operand))
1927            }
1928            mir::AssertKind::DivisionByZero(operand) => {
1929                let operand = self.translate_operand(span, operand)?;
1930                Ok(BuiltinAssertKind::DivisionByZero(operand))
1931            }
1932            mir::AssertKind::RemainderByZero(operand) => {
1933                let operand = self.translate_operand(span, operand)?;
1934                Ok(BuiltinAssertKind::RemainderByZero(operand))
1935            }
1936            mir::AssertKind::MisalignedPointerDereference { required, found } => {
1937                let required = self.translate_operand(span, required)?;
1938                let found = self.translate_operand(span, found)?;
1939                Ok(BuiltinAssertKind::MisalignedPointerDereference { required, found })
1940            }
1941            mir::AssertKind::NullPointerDereference => {
1942                Ok(BuiltinAssertKind::NullPointerDereference)
1943            }
1944            mir::AssertKind::NullReferenceConstructed => {
1945                Ok(BuiltinAssertKind::NullReferenceCreated)
1946            }
1947            mir::AssertKind::InvalidEnumConstruction(operand) => {
1948                let operand = self.translate_operand(span, operand)?;
1949                Ok(BuiltinAssertKind::InvalidEnumConstruction(operand))
1950            }
1951            mir::AssertKind::ResumedAfterDrop(..)
1952            | mir::AssertKind::ResumedAfterPanic(..)
1953            | mir::AssertKind::ResumedAfterReturn(..) => {
1954                raise_error!(self, span, "Coroutines are not supported");
1955            }
1956        }
1957    }
1958
1959    /// Evaluate function arguments in a context, and return the list of computed
1960    /// values.
1961    fn translate_arguments(
1962        &mut self,
1963        span: Span,
1964        args: &[hax::Spanned<mir::Operand<'tcx>>],
1965    ) -> Result<Vec<Operand>, Error> {
1966        let mut t_args: Vec<Operand> = Vec::new();
1967        for arg in args.iter().map(|x| &x.node) {
1968            // Translate
1969            let op = self.translate_operand(span, arg)?;
1970            t_args.push(op);
1971        }
1972        Ok(t_args)
1973    }
1974}
1975
1976impl<'a> IntoFormatter for &'a BodyTransCtx<'_, '_, '_> {
1977    type C = FmtCtx<'a>;
1978    fn into_fmt(self) -> Self::C {
1979        FmtCtx {
1980            local_names: Some(compute_local_names(&self.locals)),
1981            ..self.i_ctx.into_fmt()
1982        }
1983    }
1984}