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