Skip to main content

charon_driver/translate/
translate_bodies.rs

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