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