Skip to main content

charon_driver/translate/
translate_bodies.rs

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