Skip to main content

charon_driver/translate/
translate_bodies.rs

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