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