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