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