Skip to main content

charon_driver/translate/
translate_bodies.rs

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