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