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