Skip to main content

charon_lib/
minirust.rs

1//! Translate a monomorphized ULLBC crate into MiniRust program.
2//!
3//! See the [manual] for known unsupported features.
4//!
5//! [manual]: https://aeneasverif.github.io/charon/manual/minirust.html#known-unsupported-features
6// Reminder to keep the manual page updated as we add more support for features.
7use itertools::Itertools;
8use minirust_rs::{mem::TreeBorrowsMemory, prelude::TerminationInfo};
9use miniutil::build as mb;
10use smallvec::{SmallVec, smallvec};
11use std::hash::{DefaultHasher, Hash, Hasher};
12use std::{cell::RefCell, io::Write, marker::PhantomData};
13
14use crate::{
15    ast::{from_rustc::LangItem, *},
16    errors::{Error, ErrorContext},
17    formatter::{FmtCtx, IntoFormatter},
18    ids::Generator,
19    pretty::FmtWithCtx,
20    ullbc_ast::{BlockId, START_BLOCK_ID},
21};
22use intrinsics::FunctionBuilder;
23
24mod mini {
25    pub use minirust_rs::lang::*;
26    pub use minirust_rs::libspecr::hidden::GcCow;
27    pub use minirust_rs::libspecr::prelude::*;
28    pub use minirust_rs::libspecr::{Align, Int, Map, Name, Size};
29    pub use minirust_rs::mem::*;
30    pub use minirust_rs::prelude::{Target, x86_64};
31}
32
33type Result<T> = std::result::Result<T, Error>;
34
35macro_rules! raise {
36    ($span:expr, $($fmt:tt)*) => {
37        return Err(Error::new($span, format!($($fmt)*)))
38    };
39}
40
41macro_rules! check {
42    ($span:expr, $condition:expr, $($fmt:tt)*) => {
43        if !$condition {
44            raise!($span, $($fmt)*);
45        }
46    };
47}
48
49mod intrinsics;
50mod types;
51
52fn name(index: u32) -> mini::Name {
53    mini::Name::from_internal(index)
54}
55
56fn mini_size(bytes: u64) -> mini::Size {
57    mini::Size::from_bytes_const(bytes)
58}
59
60fn mini_align(span: Span, bytes: u64) -> Result<mini::Align> {
61    mini::Align::from_bytes(bytes)
62        .ok_or_else(|| format!("invalid MiniRust alignment {bytes}"))
63        .context(span)
64}
65
66fn mini_int(value: IntegerValue) -> mini::Int {
67    match value {
68        IntegerValue::Unsigned(_, value) => mini::Int::from(value),
69        IntegerValue::Signed(_, value) => mini::Int::from(value),
70    }
71}
72
73fn size_bytes(span: Span, size: mini::Size) -> Result<u64> {
74    let bytes = size
75        .bytes()
76        .try_to_usize()
77        .ok_or("MiniRust size does not fit usize")
78        .context(span)?;
79    u64::try_from(bytes)
80        .map_err(|_| "MiniRust size does not fit u64")
81        .context(span)
82}
83
84fn align_bytes(span: Span, align: mini::Align) -> Result<u64> {
85    let bytes = align
86        .bytes()
87        .try_to_usize()
88        .ok_or("MiniRust alignment does not fit usize")
89        .context(span)?;
90    u64::try_from(bytes)
91        .map_err(|_| "MiniRust alignment does not fit u64")
92        .context(span)
93}
94
95pub fn serialize(krate: &TranslatedCrate, writer: impl Write) -> Result<()> {
96    let program = TranslateCtx::<mini::x86_64>::new(krate)?.translate()?;
97    serde_json::to_writer_pretty(writer, &program)
98        .map_err(|error| format!("serializing MiniRust program: {error}"))
99        .context(Span::dummy())
100}
101
102struct TranslateCtx<'a, T: mini::Target> {
103    krate: &'a TranslatedCrate,
104    fmt: FmtCtx<'a>,
105    target_name: &'a TargetTriple,
106    target: &'a TargetInfo,
107    global_ids: RefCell<Generator<GlobalDeclId>>,
108    string_literals: RefCell<SeqHashMap<String, mini::GlobalName>>,
109    mini_target: PhantomData<T>,
110}
111
112impl<'a, T: mini::Target> TranslateCtx<'a, T> {
113    fn new(krate: &'a TranslatedCrate) -> Result<Self> {
114        let (target_name, target) = krate
115            .target_information
116            .iter()
117            .exactly_one()
118            .map_err(|_| "MiniRust requires exactly one compilation target")
119            .context(Span::dummy())?;
120        check!(
121            Span::dummy(),
122            target.target_pointer_size == size_bytes(Span::dummy(), T::PTR_SIZE)?
123                && target.is_little_endian == (T::ENDIANNESS == mini::LittleEndian),
124            "the Charon and MiniRust targets do not match"
125        );
126        Ok(Self {
127            krate,
128            fmt: krate.into_fmt(),
129            target_name,
130            target,
131            global_ids: RefCell::new(Generator::new_with_init_value(krate.global_decls.next_id())),
132            string_literals: RefCell::new(Default::default()),
133            mini_target: PhantomData,
134        })
135    }
136
137    fn fn_name(&self, id: FunDeclId) -> mini::FnName {
138        mini::FnName(name(id.index() as u32))
139    }
140
141    fn global_name(&self, id: GlobalDeclId) -> mini::GlobalName {
142        mini::GlobalName(name(id.index() as u32))
143    }
144
145    fn trait_name(&self, id: TypeDeclId) -> mini::TraitName {
146        mini::TraitName(name(id.index() as u32))
147    }
148
149    fn vtable_name(&self, id: GlobalDeclId) -> mini::VTableName {
150        mini::VTableName(name(id.index() as u32))
151    }
152
153    fn method_name(&self, id: FieldId) -> mini::TraitMethodName {
154        mini::TraitMethodName(name(id.index() as u32))
155    }
156
157    fn local_name(&self, local: LocalId) -> mini::LocalName {
158        mini::LocalName(name(local.index() as u32))
159    }
160
161    fn field_name(&self, field: FieldId) -> mini::Int {
162        mini::Int::from(field.index() as u128)
163    }
164
165    fn block_name(&self, id: BlockId) -> mini::BbName {
166        mini::BbName(name(id.index() as u32))
167    }
168
169    fn translate(&self) -> Result<mini::Program> {
170        let mut functions: mini::Map<mini::FnName, mini::Function> = Default::default();
171        for (id, fdecl) in self.krate.fun_decls.iter_enumerated() {
172            if let FunSource::GlobalInitializer(init) = &fdecl.src
173                && let Some(gdecl) = self.krate.global_decls.get(init.id)
174                && gdecl.global_kind.is_vtable()
175            {
176                continue;
177            }
178            let span = fdecl.item_meta.span;
179            let mini_function = if let Some(function) = self.lower_intrinsic(fdecl)? {
180                function
181            } else if let Body::Unstructured(body) = &fdecl.body {
182                self.function(span, fdecl, body)?
183            } else {
184                raise!(
185                    span,
186                    "unable to translate {} to MiniRust",
187                    fdecl.def_id.with_ctx(&self.fmt)
188                )
189            };
190            functions.insert(self.fn_name(id), mini_function);
191        }
192
193        let mut globals: mini::Map<mini::GlobalName, mini::Global> = Default::default();
194        for (id, gdecl) in self.krate.global_decls.iter_enumerated() {
195            if matches!(
196                gdecl.global_kind,
197                GlobalKind::Static {
198                    is_thread_local: false,
199                    ..
200                } | GlobalKind::NamedConst
201                    | GlobalKind::AnonConst
202            ) {
203                let mini_global = self.global(gdecl.item_meta.span, gdecl)?;
204                globals.insert(self.global_name(id), mini_global);
205            }
206        }
207        for (value, name) in self.string_literals.borrow().iter() {
208            globals.insert(
209                *name,
210                mini::Global {
211                    bytes: value.as_bytes().iter().copied().map(Some).collect(),
212                    relocations: Default::default(),
213                    align: mini::Align::ONE,
214                },
215            );
216        }
217
218        // For MiniRust, traits are basically just their vtables.
219        let mut traits: mini::Map<mini::TraitName, mini::Set<mini::TraitMethodName>> =
220            mini::Map::new();
221        for (id, tdecl) in self.krate.type_decls.iter_enumerated() {
222            if let TypeSource::VTable { field_map, .. } = &tdecl.src {
223                let methods = field_map
224                    .iter_enumerated()
225                    .filter(|&(_field, kind)| {
226                        matches!(kind, VTableField::Drop | VTableField::Method(..))
227                    })
228                    .map(|(field, _kind)| self.method_name(field))
229                    .collect();
230                traits.insert(self.trait_name(id), methods);
231            }
232        }
233        let mut vtables: mini::Map<mini::VTableName, mini::VTable> = mini::Map::new();
234        for (id, gdecl) in self.krate.global_decls.iter_enumerated() {
235            if matches!(gdecl.global_kind, GlobalKind::VTable) {
236                vtables.insert(self.vtable_name(id), self.vtable(gdecl)?);
237            }
238        }
239
240        let main = self
241            .krate
242            .fun_decls
243            .iter_enumerated()
244            .find(|(_, fdecl)| {
245                fdecl
246                    .item_meta
247                    .name
248                    .equals_ref_name(&[&self.krate.crate_name, "main"])
249            })
250            .map(|(id, _)| id)
251            .ok_or("MiniRust needs a `main()` function")
252            .context(Span::dummy())?;
253        let main_span = self.krate.fun_decls[main].item_meta.span;
254        let start = self.fn_name(self.krate.fun_decls.next_id());
255        functions.insert(start, self.make_start_function(main_span, main)?);
256
257        Ok(mini::Program {
258            functions,
259            start,
260            globals,
261            traits,
262            vtables,
263        })
264    }
265}
266
267/// Function bodies.
268impl<T: mini::Target> TranslateCtx<'_, T> {
269    /// Make a start function with the right calling convention that just calls into `main()`.
270    fn make_start_function(&self, span: Span, main: FunDeclId) -> Result<mini::Function> {
271        let mut signature = self.krate.fun_decls[main].signature.clone();
272        signature.abi = Abi::C;
273        check!(
274            span,
275            signature.inputs.is_empty() && signature.output.is_unit(),
276            "MiniRust output only supports an entry point with signature `fn main()`"
277        );
278        let mut builder = FunctionBuilder::new(self, span, &signature)?;
279        let ret = builder.return_local();
280        let start = builder.declare_block();
281        let exit = builder.declare_block();
282        let abort = builder.declare_block();
283        builder.set_block(
284            start,
285            mb::block(
286                &[],
287                mini::Terminator::Call {
288                    callee: self.fn_pointer(main),
289                    calling_convention: mini::CallingConvention::Rust,
290                    arguments: Default::default(),
291                    ret: mini::PlaceExpr::Local(ret),
292                    next_block: Some(exit),
293                    unwind_block: Some(abort),
294                },
295                mini::BbKind::Regular,
296            ),
297        );
298        builder.set_block(
299            exit,
300            mb::block(
301                &[],
302                mini::Terminator::Intrinsic {
303                    intrinsic: mini::IntrinsicOp::Exit,
304                    arguments: Default::default(),
305                    ret: mini::PlaceExpr::Local(ret),
306                    next_block: None,
307                },
308                mini::BbKind::Regular,
309            ),
310        );
311        builder.set_block(
312            abort,
313            mb::block(
314                &[],
315                mini::Terminator::Intrinsic {
316                    intrinsic: mini::IntrinsicOp::Abort,
317                    arguments: Default::default(),
318                    ret: mini::PlaceExpr::Local(ret),
319                    next_block: None,
320                },
321                mini::BbKind::Catch,
322            ),
323        );
324        Ok(builder.finish())
325    }
326
327    fn function(
328        &self,
329        span: Span,
330        fdecl: &FunDecl,
331        body: &ullbc_ast::ExprBody,
332    ) -> Result<mini::Function> {
333        let locals: mini::Map<mini::LocalName, mini::Type> = body
334            .locals
335            .iter()
336            .map(|local| {
337                Ok((
338                    self.local_name(local.index),
339                    self.ty(local.span, &local.ty)?,
340                ))
341            })
342            .collect::<Result<_>>()?;
343        let args: mini::List<mini::LocalName> = body
344            .locals
345            .arguments()
346            .map(|local| self.local_name(local.index))
347            .collect();
348
349        let mut blocks = mini::Map::new();
350        let mut block_id_gen = Generator::new_with_init_value(body.body.next_idx());
351        for (id, block) in body.body.iter_enumerated() {
352            let mut current_block = self.block_name(id);
353            let mut statements = Vec::new();
354            if id == START_BLOCK_ID {
355                // Start the function by retagging the function arguments.
356                statements.extend(args.iter().map(|arg| mini::Statement::Validate {
357                    place: mini::PlaceExpr::Local(arg),
358                    fn_entry: true,
359                }));
360            }
361            let block_kind = match block.kind {
362                ullbc_ast::UnwindKind::Regular => mini::BbKind::Regular,
363                ullbc_ast::UnwindKind::Cleanup => mini::BbKind::Cleanup,
364                ullbc_ast::UnwindKind::Terminate => mini::BbKind::Terminate,
365            };
366            for statement in &block.statements {
367                match &statement.kind {
368                    // MiniRust keeps the return place and arguments live for the entire call.
369                    ullbc_ast::StatementKind::StorageLive(local)
370                    | ullbc_ast::StatementKind::StorageDead(local)
371                        if body.locals.is_return_or_arg(*local) => {}
372
373                    // These casts are intrinsics for MiniRust.
374                    ullbc_ast::StatementKind::Assign(
375                        destination,
376                        Rvalue::UnaryOp(
377                            UnOp::Cast(
378                                cast @ (CastKind::PtrExposeProvenance
379                                | CastKind::PtrWithExposedProvenance(..)),
380                            ),
381                            operand,
382                        ),
383                    ) => {
384                        let intrinsic = match cast {
385                            CastKind::PtrExposeProvenance => {
386                                mini::IntrinsicOp::PointerExposeProvenance
387                            }
388                            CastKind::PtrWithExposedProvenance(_) => {
389                                mini::IntrinsicOp::PointerWithExposedProvenance
390                            }
391                            _ => unreachable!(),
392                        };
393                        let next_block = self.block_name(block_id_gen.fresh_id());
394                        blocks.insert(
395                            current_block,
396                            mb::block(
397                                &std::mem::take(&mut statements),
398                                mini::Terminator::Intrinsic {
399                                    intrinsic,
400                                    arguments: [self.operand(statement.span, operand)?]
401                                        .into_iter()
402                                        .collect(),
403                                    ret: self.place(statement.span, destination)?,
404                                    next_block: Some(next_block),
405                                },
406                                block_kind,
407                            ),
408                        );
409                        current_block = next_block;
410                    }
411
412                    _ => statements.extend(self.statement(statement)?),
413                }
414            }
415            let terminator = self.terminator(
416                &block.terminator,
417                block.kind,
418                &mut blocks,
419                &mut block_id_gen,
420            )?;
421            blocks.insert(
422                current_block,
423                mb::block(&statements, terminator, block_kind),
424            );
425        }
426
427        Ok(mini::Function {
428            locals,
429            args,
430            ret: self.local_name(body.locals.return_local().index),
431            calling_convention: self.calling_convention(span, &fdecl.signature.abi)?,
432            blocks,
433            start: self.block_name(START_BLOCK_ID),
434            implicit_writes: true,
435        })
436    }
437
438    fn statement(
439        &self,
440        statement: &ullbc_ast::Statement,
441    ) -> Result<SmallVec<[mini::Statement; 1]>> {
442        use ullbc_ast::StatementKind as S;
443        let span = statement.span;
444        Ok(match &statement.kind {
445            S::Assign(place, value) => {
446                let destination = self.place(span, place)?;
447                let mut statements = smallvec![mini::Statement::Assign {
448                    destination,
449                    source: self.rvalue(span, value)?,
450                }];
451                if let Rvalue::Use(_, WithRetag::Yes) = value {
452                    statements.push(mini::Statement::Validate {
453                        place: destination,
454                        fn_entry: false,
455                    });
456                }
457                statements
458            }
459            S::SetDiscriminant(place, variant) => smallvec![mini::Statement::SetDiscriminant {
460                destination: self.place(span, place)?,
461                value: self.variant_discriminant(span, place.ty(), *variant)?,
462            }],
463            S::StorageLive(local) => {
464                smallvec![mini::Statement::StorageLive(self.local_name(*local))]
465            }
466            S::StorageDead(local) => {
467                smallvec![mini::Statement::StorageDead(self.local_name(*local))]
468            }
469            S::PlaceMention(place) => {
470                smallvec![mini::Statement::PlaceMention(self.place(span, place)?)]
471            }
472            S::Borrowck(_) | S::Nop => SmallVec::new(),
473            S::Assert { .. } => {
474                raise!(
475                    span,
476                    "MiniRust is incompatible with `--reconstruct-asserts`"
477                )
478            }
479        })
480    }
481
482    fn terminator(
483        &self,
484        terminator: &ullbc_ast::Terminator,
485        unwind_kind: ullbc_ast::UnwindKind,
486        extra_blocks: &mut mini::Map<mini::BbName, mini::BasicBlock>,
487        block_id_gen: &mut Generator<BlockId>,
488    ) -> Result<mini::Terminator> {
489        use ullbc_ast::TerminatorKind as T;
490        let span = terminator.span;
491        Ok(match &terminator.kind {
492            T::Goto { target } => mini::Terminator::Goto(self.block_name(*target)),
493            T::Switch { data, branches } => {
494                let value = self.switch_value(span, &data.scrutinee)?;
495                let cases = data
496                    .branches
497                    .iter()
498                    .map(|(value, branch)| -> Result<_> {
499                        Ok((
500                            self.constant_int(span, value)?,
501                            self.block_name(branches[*branch]),
502                        ))
503                    })
504                    .try_collect()?;
505                let fallback = if let Some(branch) = data.fallback {
506                    self.block_name(branches[branch])
507                } else {
508                    let name = self.block_name(block_id_gen.fresh_id());
509                    extra_blocks.insert(
510                        name,
511                        mb::block(&[], mini::Terminator::Unreachable, mini::BbKind::Regular),
512                    );
513                    name
514                };
515                mini::Terminator::Switch {
516                    value,
517                    cases,
518                    fallback,
519                }
520            }
521            T::Call {
522                call,
523                target,
524                on_unwind,
525            } => mini::Terminator::Call {
526                callee: match &call.func {
527                    FnOperand::Regular(fn_ptr) => match *fn_ptr.kind {
528                        FnPtrKind::Fun(id) => self.fn_pointer(id),
529                        FnPtrKind::Trait(..) => {
530                            raise!(span, "trait-method call remained after monomorphization")
531                        }
532                    },
533                    FnOperand::Dynamic(operand, _) => self.operand(span, operand)?,
534                },
535                calling_convention: self.callee_convention(span, &call.func)?,
536                arguments: call
537                    .args
538                    .iter()
539                    .map(|arg| {
540                        Ok(match arg {
541                            Operand::Move(place) => {
542                                mini::ArgumentExpr::InPlace(self.place(span, place)?)
543                            }
544                            _ => mini::ArgumentExpr::ByValue(self.operand(span, arg)?),
545                        })
546                    })
547                    .collect::<Result<_>>()?,
548                ret: self.place(span, &call.dest)?,
549                next_block: Some(self.block_name(*target)),
550                unwind_block: Some(self.block_name(*on_unwind)),
551            },
552            T::Assert {
553                assert,
554                target,
555                on_unwind,
556            } => {
557                let condition = mb::bool_to_int::<u8>(self.operand(span, &assert.cond)?);
558                let success = self.block_name(*target);
559                let failure = if unwind_kind != ullbc_ast::UnwindKind::Regular {
560                    // A second panic while unwinding follows the pre-existing terminate path.
561                    self.block_name(*on_unwind)
562                } else {
563                    let name = self.block_name(block_id_gen.fresh_id());
564                    extra_blocks.insert(
565                        name,
566                        mb::block(&[], self.start_unwind(*on_unwind), mini::BbKind::Regular),
567                    );
568                    name
569                };
570                let (cases, fallback) = if assert.expected {
571                    ([(mini::Int::from(1), success)], failure)
572                } else {
573                    ([(mini::Int::from(0), success)], failure)
574                };
575                mini::Terminator::Switch {
576                    value: condition,
577                    cases: cases.into_iter().collect(),
578                    fallback,
579                }
580            }
581            T::UndefinedBehavior => mini::Terminator::Unreachable,
582            T::UnwindTerminate => mini::Terminator::Intrinsic {
583                intrinsic: mini::IntrinsicOp::Abort,
584                arguments: Default::default(),
585                ret: mini::PlaceExpr::Local(self.local_name(LocalId::ZERO)),
586                next_block: None,
587            },
588            T::Return => mini::Terminator::Return,
589            T::UnwindResume => mini::Terminator::ResumeUnwind,
590            T::Drop { .. } => raise!(span, "MiniRust requires drops to be desugared"),
591            T::InlineAsm { .. } => raise!(span, "MiniRust does not support inline assembly"),
592            T::Panic { .. } => raise!(
593                span,
594                "MiniRust output does not support --reconstruct-panic-calls"
595            ),
596        })
597    }
598
599    fn start_unwind(&self, on_unwind: BlockId) -> mini::Terminator {
600        mini::Terminator::StartUnwind {
601            unwind_payload: self.opaque_panic_payload(),
602            unwind_block: self.block_name(on_unwind),
603        }
604    }
605
606    fn opaque_panic_payload(&self) -> mini::ValueExpr {
607        // FIXME(minirust): preserve Rust's actual panic payload. For now we use a dangling
608        // pointer, as MiniRust only models an opaque raw-pointer payload.
609        mini::ValueExpr::Constant(
610            mini::Constant::PointerWithoutProvenance(mini::Int::from(1)),
611            mini::Type::Ptr(mini::PtrType::Raw {
612                meta_kind: mini::PointerMetaKind::None,
613            }),
614        )
615    }
616
617    fn place(&self, span: Span, place: &Place) -> Result<mini::PlaceExpr> {
618        Ok(match &place.kind {
619            PlaceKind::Local(local) => mini::PlaceExpr::Local(self.local_name(*local)),
620            PlaceKind::Global(gref) => {
621                let pointer = mini::ValueExpr::Constant(
622                    mini::Constant::GlobalPointer(mini::Relocation {
623                        name: self.global_name(gref.id),
624                        offset: mini_size(0),
625                    }),
626                    mini::Type::Ptr(mini::PtrType::Raw {
627                        meta_kind: mini::PointerMetaKind::None,
628                    }),
629                );
630                let meta_kind = self.metadata_kind(span, &place.ty)?;
631                let pointer = if meta_kind == mini::PointerMetaKind::None {
632                    pointer
633                } else {
634                    let metadata = &self.krate.global_decls[gref.id].ptr_metadata;
635                    mb::construct_wide_pointer(
636                        pointer,
637                        self.constant(span, metadata)?,
638                        mb::raw_ptr_ty(meta_kind),
639                    )
640                };
641                mb::deref(pointer, self.ty(span, &place.ty)?)
642            }
643            PlaceKind::Projection(subplace, projection) => {
644                let subplace_expr = self.place(span, subplace)?;
645                match projection {
646                    ProjectionElem::Deref => {
647                        mb::deref(mb::load(subplace_expr), self.ty(span, &place.ty)?)
648                    }
649                    ProjectionElem::Field(variant, field) => {
650                        let subplace_expr = if let Some(variant) = variant {
651                            mb::downcast(
652                                subplace_expr,
653                                self.variant_discriminant(span, subplace.ty(), *variant)?,
654                            )
655                        } else {
656                            subplace_expr
657                        };
658                        mb::field(subplace_expr, self.field_name(*field))
659                    }
660                    ProjectionElem::Index {
661                        offset,
662                        from_end: false,
663                    } => mb::index(subplace_expr, self.operand(span, offset)?),
664                    ProjectionElem::Offset {
665                        bytes, in_bounds, ..
666                    } => {
667                        let meta_kind = self.metadata_kind(span, subplace.ty())?;
668                        let pointer = mb::addr_of(subplace_expr, mb::raw_ptr_ty(meta_kind));
669                        let in_bounds = match in_bounds {
670                            true => mb::InBounds::Yes,
671                            false => mb::InBounds::No,
672                        };
673                        mb::deref(
674                            mb::ptr_offset(pointer, self.operand(span, bytes)?, in_bounds),
675                            self.ty(span, &place.ty)?,
676                        )
677                    }
678                    ProjectionElem::Index { from_end: true, .. }
679                    | ProjectionElem::Subslice { .. }
680                    | ProjectionElem::PtrMetadata => {
681                        raise!(
682                            span,
683                            "Failed to translate place to MiniRust: {}",
684                            place.with_ctx(&self.fmt)
685                        )
686                    }
687                }
688            }
689        })
690    }
691
692    fn operand(&self, span: Span, operand: &Operand) -> Result<mini::ValueExpr> {
693        Ok(match operand {
694            Operand::Copy(place) | Operand::Move(place) => {
695                // Charon translates dyn dispatch to a vtable-field read. MiniRust vtables are
696                // opaque, so read the method through its lookup operation.
697                if let PlaceKind::Projection(vtable, ProjectionElem::Field(None, field)) =
698                    &place.kind
699                    && let PlaceKind::Projection(metadata, ProjectionElem::Deref) = &vtable.kind
700                    && let PlaceKind::Projection(pointer, ProjectionElem::PtrMetadata) =
701                        &metadata.kind
702                    && let Some(tref) = vtable.ty.as_adt()
703                    && let TypeSource::VTable { field_map, .. } =
704                        &self.krate.type_decls[tref.id].src
705                    && matches!(
706                        field_map[*field],
707                        VTableField::Drop | VTableField::Method(..)
708                    )
709                {
710                    mb::vtable_method_lookup(
711                        mb::get_metadata(mb::load(self.place(span, pointer)?)),
712                        self.method_name(*field),
713                    )
714                } else if let PlaceKind::Projection(pointer, ProjectionElem::PtrMetadata) =
715                    &place.kind
716                {
717                    mb::get_metadata(mb::load(self.place(span, pointer)?))
718                } else {
719                    mb::load(self.place(span, place)?)
720                }
721            }
722            Operand::Const(value) => self.constant(span, value)?,
723        })
724    }
725
726    fn switch_value(&self, span: Span, scrutinee: &SwitchScrutinee) -> Result<mini::ValueExpr> {
727        let (value, ty) = match scrutinee {
728            SwitchScrutinee::Value(operand) => (self.operand(span, operand)?, operand.ty()),
729            SwitchScrutinee::Discriminant(place) => {
730                (mb::get_discriminant(self.place(span, place)?), place.ty())
731            }
732        };
733        if matches!(ty.kind(), TyKind::Scalar(ScalarTy::Bool)) {
734            Ok(mb::bool_to_int::<u8>(value))
735        } else {
736            Ok(value)
737        }
738    }
739
740    fn rvalue(&self, span: Span, value: &Rvalue) -> Result<mini::ValueExpr> {
741        Ok(match value {
742            // Retagging is handled at the statement level.
743            Rvalue::Use(operand, _) => self.operand(span, operand)?,
744            Rvalue::Ref { place, kind, .. } => mini::ValueExpr::AddrOf {
745                target: mini::GcCow::new(self.place(span, place)?),
746                ptr_ty: mini::PtrType::Ref {
747                    mutbl: if kind.is_mut() {
748                        mini::Mutability::Mutable
749                    } else {
750                        mini::Mutability::Immutable
751                    },
752                    pointee: self.pointee_info(span, place.ty())?,
753                },
754            },
755            Rvalue::RawPtr { place, .. } => mini::ValueExpr::AddrOf {
756                target: mini::GcCow::new(self.place(span, place)?),
757                ptr_ty: mini::PtrType::Raw {
758                    meta_kind: self.metadata_kind(span, place.ty())?,
759                },
760            },
761            Rvalue::BinaryOp(op, left, right) => self.binop(span, *op, left, right)?,
762            Rvalue::UnaryOp(op, operand) => self.unop(span, op, operand)?,
763            Rvalue::NullaryOp(op) => {
764                let checks = self.krate.runtime_checks;
765                let value = match op {
766                    NullOp::UbChecks => checks.ub_checks,
767                    NullOp::OverflowChecks => checks.overflow_checks,
768                    NullOp::ContractChecks => checks.contract_checks,
769                };
770                mb::const_bool(value)
771            }
772            Rvalue::Discriminant(place) => mb::get_discriminant(self.place(span, place)?),
773            Rvalue::Aggregate(kind, operands) => self.aggregate(span, kind, operands)?,
774            Rvalue::Len(place, _, known_len) => {
775                if let Some(len) = known_len {
776                    self.constant(span, len)?
777                } else {
778                    let ptr = mini::ValueExpr::AddrOf {
779                        target: mini::GcCow::new(self.place(span, place)?),
780                        ptr_ty: mini::PtrType::Raw {
781                            meta_kind: mini::PointerMetaKind::ElementCount,
782                        },
783                    };
784                    mb::get_metadata(ptr)
785                }
786            }
787            Rvalue::Repeat(operand, ty, count, _) => {
788                let count = count
789                    .as_usize_literal()
790                    .ok_or("non-concrete array length")
791                    .context(span)?;
792                let count = usize::try_from(count)
793                    .map_err(|_| "array length does not fit usize")
794                    .context(span)?;
795                let value = self.operand(span, operand)?;
796                mb::array(&vec![value; count], self.ty(span, ty)?)
797            }
798        })
799    }
800
801    fn binop(
802        &self,
803        span: Span,
804        op: BinOp,
805        left: &Operand,
806        right: &Operand,
807    ) -> Result<mini::ValueExpr> {
808        let left_value = self.operand(span, left)?;
809        let right_value = self.operand(span, right)?;
810
811        let overflowing_op = |mode, regular, unchecked| {
812            Ok(match mode {
813                OverflowMode::Wrap => regular,
814                OverflowMode::UB => unchecked,
815                OverflowMode::Panic => raise!(
816                    span,
817                    "MiniRust translation is incompatible with `--reconstruct-fallible-operations`"
818                ),
819            })
820        };
821        let operator = match op {
822            BinOp::BitXor => mini::BinOp::Int(mini::IntBinOp::BitXor),
823            BinOp::BitAnd => mini::BinOp::Int(mini::IntBinOp::BitAnd),
824            BinOp::BitOr => mini::BinOp::Int(mini::IntBinOp::BitOr),
825            BinOp::Eq => mini::BinOp::Rel(mini::RelOp::Eq),
826            BinOp::Lt => mini::BinOp::Rel(mini::RelOp::Lt),
827            BinOp::Le => mini::BinOp::Rel(mini::RelOp::Le),
828            BinOp::Ne => mini::BinOp::Rel(mini::RelOp::Ne),
829            BinOp::Ge => mini::BinOp::Rel(mini::RelOp::Ge),
830            BinOp::Gt => mini::BinOp::Rel(mini::RelOp::Gt),
831            BinOp::AddChecked => mini::BinOp::IntWithOverflow(mini::IntBinOpWithOverflow::Add),
832            BinOp::SubChecked => mini::BinOp::IntWithOverflow(mini::IntBinOpWithOverflow::Sub),
833            BinOp::MulChecked => mini::BinOp::IntWithOverflow(mini::IntBinOpWithOverflow::Mul),
834            BinOp::Add(mode) => mini::BinOp::Int(overflowing_op(
835                mode,
836                mini::IntBinOp::Add,
837                mini::IntBinOp::AddUnchecked,
838            )?),
839            BinOp::Sub(mode) => mini::BinOp::Int(overflowing_op(
840                mode,
841                mini::IntBinOp::Sub,
842                mini::IntBinOp::SubUnchecked,
843            )?),
844            BinOp::Mul(mode) => mini::BinOp::Int(overflowing_op(
845                mode,
846                mini::IntBinOp::Mul,
847                mini::IntBinOp::MulUnchecked,
848            )?),
849            BinOp::Div(_) => mini::BinOp::Int(mini::IntBinOp::Div),
850            BinOp::Rem(_) => mini::BinOp::Int(mini::IntBinOp::Rem),
851            BinOp::Shl(mode) => mini::BinOp::Int(overflowing_op(
852                mode,
853                mini::IntBinOp::Shl,
854                mini::IntBinOp::ShlUnchecked,
855            )?),
856            BinOp::Shr(mode) => mini::BinOp::Int(overflowing_op(
857                mode,
858                mini::IntBinOp::Shr,
859                mini::IntBinOp::ShrUnchecked,
860            )?),
861            BinOp::Cmp => mini::BinOp::Rel(mini::RelOp::Cmp),
862        };
863
864        if left.ty().is_bool() && matches!(operator, mini::BinOp::Int(_)) {
865            let value = mini::ValueExpr::BinOp {
866                operator,
867                left: mini::GcCow::new(mb::bool_to_int::<u8>(left_value)),
868                right: mini::GcCow::new(mb::bool_to_int::<u8>(right_value)),
869            };
870            Ok(mb::transmute(value, mini::Type::Bool))
871        } else {
872            Ok(mini::ValueExpr::BinOp {
873                operator,
874                left: mini::GcCow::new(left_value),
875                right: mini::GcCow::new(right_value),
876            })
877        }
878    }
879
880    fn unop(&self, span: Span, op: &UnOp, operand: &Operand) -> Result<mini::ValueExpr> {
881        let mut operand_value = self.operand(span, operand)?;
882        let operator = match op {
883            UnOp::Not if operand.ty().is_bool() => {
884                return Ok(mb::not(operand_value));
885            }
886            UnOp::Not => mini::UnOp::Int(mini::IntUnOp::BitNot),
887            UnOp::Neg(_) => mini::UnOp::Int(mini::IntUnOp::Neg),
888            UnOp::Cast(CastKind::Scalar(target_ty)) => {
889                if operand.ty().is_bool() {
890                    operand_value = mb::bool_to_int::<u8>(operand_value);
891                }
892                mini::UnOp::Cast(mini::CastOp::IntToInt(match *target_ty {
893                    ScalarTy::Integer(ty) => self.int_type(ty),
894                    ScalarTy::Char => self.int_type(IntegerTy::Unsigned(UIntTy::U32)),
895                    ScalarTy::Bool => self.int_type(IntegerTy::Unsigned(UIntTy::U8)),
896                    ScalarTy::Float(_) => raise!(span, "float unexpected in a scalar cast"),
897                }))
898            }
899            UnOp::Cast(CastKind::Transmute(target_ty) | CastKind::Subtype(target_ty)) => {
900                mini::UnOp::Cast(mini::CastOp::Transmute(self.ty(span, target_ty)?))
901            }
902            UnOp::Cast(CastKind::RawPtr(target_ty)) => {
903                let source_ty = operand.ty();
904                if source_ty.kind().is_fn_ptr() || target_ty.kind().is_fn_ptr() {
905                    return Ok(mb::transmute(operand_value, self.ty(span, target_ty)?));
906                }
907                // MiniRust does not track the type of pointers, so the only effect of this cast is
908                // on metadata.
909                let old = self.metadata_kind(
910                    span,
911                    source_ty
912                        .builtin_deref(self.krate)
913                        .ok_or("raw-pointer cast from a non-pointer")
914                        .context(span)?,
915                )?;
916                let new = self.metadata_kind(
917                    span,
918                    target_ty
919                        .builtin_deref(self.krate)
920                        .ok_or("raw-pointer cast to a non-pointer")
921                        .context(span)?,
922                )?;
923                if old == new {
924                    return Ok(operand_value);
925                }
926                if matches!(new, mini::PointerMetaKind::None) {
927                    mini::UnOp::GetThinPointer
928                } else {
929                    raise!(span, "raw-pointer cast adds metadata")
930                }
931            }
932            // Handled at the statement level.
933            UnOp::Cast(CastKind::PtrExposeProvenance | CastKind::PtrWithExposedProvenance(..)) => {
934                unreachable!()
935            }
936            UnOp::Cast(CastKind::FnDefToFnPtr) => {
937                match operand.ty().kind() {
938                    // Turn the function-item ZST into a function pointer.
939                    TyKind::FnDef(fn_ptr) => {
940                        let FnPtrKind::Fun(id) = *fn_ptr.skip_binder.kind else {
941                            raise!(span, "method reference remained after monomorphization")
942                        };
943                        return Ok(self.fn_pointer(id));
944                    }
945                    _ => raise!(
946                        span,
947                        "unexpected type for function pointer cast: {}",
948                        operand.ty().with_ctx(&self.fmt)
949                    ),
950                }
951            }
952            // MiniRust function pointer values are untyped.
953            UnOp::Cast(CastKind::FnPtrToUnsafe) => {
954                return Ok(operand_value);
955            }
956            UnOp::Cast(CastKind::Unsize(_, _, path) | CastKind::Concretize(_, path))
957                if !path.is_empty() =>
958            {
959                raise!(
960                    span,
961                    "MiniRust does not support casts through smart pointers"
962                )
963            }
964            UnOp::Cast(CastKind::Unsize(target_ty, UnsizingMetadata::Length(len), _)) => {
965                let target_ty = self.ty(span, target_ty)?;
966                let mini::Type::Ptr(_) = target_ty else {
967                    raise!(span, "unsizing target is not a pointer")
968                };
969                return Ok(mb::construct_wide_pointer(
970                    operand_value,
971                    self.constant(span, len)?,
972                    target_ty,
973                ));
974            }
975            UnOp::Cast(CastKind::Unsize(target_ty, UnsizingMetadata::VTable(_, vtable), _)) => {
976                let ConstantExprKind::Ref(global, None) = vtable.kind() else {
977                    raise!(span, "unsupported vtable reference")
978                };
979                let GlobalPlaceKind::Global(GlobalProvenance::Global(gref)) = &global.kind else {
980                    raise!(span, "vtable reference does not name a global")
981                };
982                let pointee = target_ty
983                    .builtin_deref(self.krate)
984                    .ok_or("unsizing target is not a pointer")
985                    .context(span)?;
986                let mini::PointerMetaKind::VTablePointer(trait_name) =
987                    self.metadata_kind(span, pointee)?
988                else {
989                    raise!(span, "unsizing target has no vtable metadata")
990                };
991                return Ok(mb::construct_wide_pointer(
992                    operand_value,
993                    mb::const_vtable(self.vtable_name(gref.id), trait_name),
994                    self.ty(span, target_ty)?,
995                ));
996            }
997            UnOp::Cast(CastKind::Unsize(target_ty, UnsizingMetadata::VTableUpcast(path), _)) => {
998                if !path.is_empty() {
999                    raise!(span, "MiniRust does not support supertrait vtable upcasts")
1000                }
1001                return Ok(mb::transmute(operand_value, self.ty(span, target_ty)?));
1002            }
1003            UnOp::Cast(CastKind::Concretize(target_ty, _)) => {
1004                return Ok(mb::transmute(
1005                    mb::get_thin_pointer(operand_value),
1006                    self.ty(span, target_ty)?,
1007                ));
1008            }
1009            UnOp::Cast(CastKind::Unsize(..)) => {
1010                raise!(span, "unsupported unsizing coercion")
1011            }
1012        };
1013        Ok(mini::ValueExpr::UnOp {
1014            operator,
1015            operand: mini::GcCow::new(operand_value),
1016        })
1017    }
1018
1019    fn aggregate(
1020        &self,
1021        span: Span,
1022        kind: &AggregateKind,
1023        operands: &[Operand],
1024    ) -> Result<mini::ValueExpr> {
1025        let values: Vec<_> = operands
1026            .iter()
1027            .map(|operand| self.operand(span, operand))
1028            .try_collect()?;
1029        Ok(match kind {
1030            AggregateKind::Adt(tref, variant, union_field) => {
1031                let ty = self.ty(span, &TyKind::Adt(tref.clone()).into_ty())?;
1032                if let Some(field) = union_field {
1033                    mini::ValueExpr::Union {
1034                        field: self.field_name(*field),
1035                        expr: mini::GcCow::new(values.into_iter().next().unwrap()),
1036                        union_ty: ty,
1037                    }
1038                } else if let Some(variant) = variant {
1039                    let discriminant = self.variant_discriminant_for_tref(span, tref, *variant)?;
1040                    let variant_ty = match &ty {
1041                        mini::Type::Enum { variants, .. } => variants
1042                            .iter()
1043                            .find(|(candidate, _)| *candidate == discriminant)
1044                            .map(|(_, variant)| variant.ty)
1045                            .ok_or("missing MiniRust enum variant")
1046                            .context(span)?,
1047                        _ => raise!(span, "enum aggregate has non-enum type"),
1048                    };
1049                    mb::variant(
1050                        discriminant,
1051                        mini::ValueExpr::Tuple(values.into_iter().collect(), variant_ty),
1052                        ty,
1053                    )
1054                } else {
1055                    mini::ValueExpr::Tuple(values.into_iter().collect(), ty)
1056                }
1057            }
1058            AggregateKind::Array(element, count, _) => {
1059                let count = count
1060                    .as_usize_literal()
1061                    .ok_or("non-concrete array length")
1062                    .context(span)?;
1063                mini::ValueExpr::Tuple(
1064                    values.into_iter().collect(),
1065                    mini::Type::Array {
1066                        elem: mini::GcCow::new(self.ty(span, element)?),
1067                        count: mini::Int::from(count),
1068                    },
1069                )
1070            }
1071            AggregateKind::RawPtr(pointee, _) => {
1072                check!(
1073                    span,
1074                    values.len() == 2,
1075                    "raw pointer aggregate does not have two fields"
1076                );
1077                let ptr_ty = mini::PtrType::Raw {
1078                    meta_kind: self.metadata_kind(span, pointee)?,
1079                };
1080                mb::construct_wide_pointer(values[0], values[1], mini::Type::Ptr(ptr_ty))
1081            }
1082        })
1083    }
1084
1085    fn fn_pointer(&self, id: FunDeclId) -> mini::ValueExpr {
1086        mb::fn_ptr(self.fn_name(id))
1087    }
1088
1089    fn calling_convention(&self, span: Span, abi: &Abi) -> Result<mini::CallingConvention> {
1090        Ok(match abi {
1091            Abi::Rust => mini::CallingConvention::Rust,
1092            Abi::C => mini::CallingConvention::C,
1093            Abi::Other(name) if name == "rust-call" => mini::CallingConvention::Rust,
1094            Abi::Other(name) => {
1095                raise!(
1096                    span,
1097                    "calling convention `{name}` is not supported by MiniRust"
1098                )
1099            }
1100        })
1101    }
1102
1103    fn callee_convention(
1104        &self,
1105        span: Span,
1106        operand: &FnOperand,
1107    ) -> Result<mini::CallingConvention> {
1108        let signature = match operand {
1109            FnOperand::Regular(fn_ptr) => match *fn_ptr.kind {
1110                FnPtrKind::Fun(id) => {
1111                    &self
1112                        .krate
1113                        .fun_decls
1114                        .get(id)
1115                        .ok_or_else(|| format!("missing function {}", id.with_ctx(&self.fmt)))
1116                        .context(span)?
1117                        .signature
1118                }
1119                FnPtrKind::Trait(..) => {
1120                    raise!(span, "trait-method call remained after monomorphization")
1121                }
1122            },
1123            FnOperand::Dynamic(operand, _) => match operand.ty().kind() {
1124                TyKind::FnPtr(signature) => &signature.skip_binder,
1125                _ => raise!(span, "dynamic callee is not a function pointer"),
1126            },
1127        };
1128        self.calling_convention(span, &signature.abi)
1129    }
1130}
1131
1132/// Constants
1133impl<T: mini::Target> TranslateCtx<'_, T> {
1134    pub(super) fn global(&self, span: Span, gdecl: &GlobalDecl) -> Result<mini::Global> {
1135        let ConstantExprKind::RawMemory(memory) = gdecl.value.kind() else {
1136            raise!(
1137                span,
1138                "MiniRust output needs all globals to be evaluated to raw bytes: {}",
1139                gdecl.value.with_ctx(&self.fmt)
1140            )
1141        };
1142        if gdecl.ty.get_ptr_metadata(self.krate).is_none() {
1143            check!(
1144                span,
1145                u64::try_from(memory.len()).context(span)? == self.size(span, &gdecl.size)?,
1146                "global value and layout have different sizes"
1147            );
1148        }
1149        let mut bytes = mini::List::new();
1150        let mut relocations = mini::List::new();
1151        for (offset, byte) in memory.iter().enumerate() {
1152            match byte {
1153                Byte::Uninit => bytes.push(None),
1154                Byte::Value(value) => bytes.push(Some(*value)),
1155                // MiniRust doesn't have TypeId provenance so we lower it to a hash of the type.
1156                Byte::Provenance(GlobalProvenance::TypeId(ty), ptr_offset, i) => {
1157                    let val = self.type_id_val(span, ty, ptr_offset.to_bits() as u64)?;
1158                    let val_bytes = if T::ENDIANNESS == mini::LittleEndian {
1159                        val.to_le_bytes()
1160                    } else {
1161                        val.to_be_bytes()
1162                    };
1163                    bytes.push(Some(val_bytes[*i as usize]));
1164                }
1165                Byte::Provenance(provenance, ptr_offset, pointer_byte) => {
1166                    let name = match provenance {
1167                        GlobalProvenance::Global(gref)
1168                            if self
1169                                .krate
1170                                .global_decls
1171                                .get(gref.id)
1172                                .is_some_and(|global| global.global_kind.is_vtable()) =>
1173                        {
1174                            raise!(span, "MiniRust globals cannot contain pointers to vtables")
1175                        }
1176                        GlobalProvenance::Global(gref) => self.global_name(gref.id),
1177                        GlobalProvenance::Function(..) => {
1178                            raise!(span, "MiniRust globals cannot contain function pointers")
1179                        }
1180                        GlobalProvenance::TypeId(_) => unreachable!(),
1181                    };
1182                    // FIXME(minirust): MiniRust does not seem to support pointer fragments
1183                    if *pointer_byte == 0 {
1184                        relocations.push((
1185                            mini_size(u64::try_from(offset).context(span)?),
1186                            mini::Relocation {
1187                                name,
1188                                offset: mini_size(
1189                                    u64::try_from(ptr_offset.to_bits()).context(span)?,
1190                                ),
1191                            },
1192                        ));
1193                    }
1194                    // MiniRust overwrites these bytes when applying the relocation.
1195                    bytes.push(None);
1196                }
1197            }
1198        }
1199        Ok(mini::Global {
1200            bytes,
1201            relocations,
1202            align: mini_align(span, self.size(span, &gdecl.align)?)?,
1203        })
1204    }
1205
1206    pub(super) fn constant(&self, span: Span, constant: &ConstantExpr) -> Result<mini::ValueExpr> {
1207        let ty = self.ty(span, constant.ty())?;
1208        Ok(match constant.kind() {
1209            ConstantExprKind::Bool(value) => mb::const_bool(*value),
1210            ConstantExprKind::Integer(value) => {
1211                mini::ValueExpr::Constant(mini::Constant::Int(mini_int(*value)), ty)
1212            }
1213            ConstantExprKind::Char(value) => mini::ValueExpr::Constant(
1214                mini::Constant::Int(mini::Int::from(u32::from(*value))),
1215                ty,
1216            ),
1217            ConstantExprKind::Adt(variant, fields) => {
1218                let values = fields
1219                    .iter()
1220                    .map(|field| self.constant(span, field))
1221                    .try_collect()?;
1222                if let Some(variant) = variant {
1223                    let tref = constant
1224                        .ty()
1225                        .as_adt()
1226                        .ok_or("enum constant without ADT type")
1227                        .context(span)?;
1228                    let discriminant = self.variant_discriminant_for_tref(span, tref, *variant)?;
1229                    let variant_ty = match &ty {
1230                        mini::Type::Enum { variants, .. } => variants
1231                            .iter()
1232                            .find(|(candidate, _)| *candidate == discriminant)
1233                            .map(|(_, variant)| variant.ty)
1234                            .ok_or("missing enum variant")
1235                            .context(span)?,
1236                        _ => raise!(span, "enum constant translated to non-enum type"),
1237                    };
1238                    mb::variant(discriminant, mini::ValueExpr::Tuple(values, variant_ty), ty)
1239                } else {
1240                    mini::ValueExpr::Tuple(values, ty)
1241                }
1242            }
1243            ConstantExprKind::Array(values) => mini::ValueExpr::Tuple(
1244                values
1245                    .iter()
1246                    .map(|value| self.constant(span, value))
1247                    .try_collect()?,
1248                ty,
1249            ),
1250            ConstantExprKind::Str(value) => {
1251                let mut literals = self.string_literals.borrow_mut();
1252                let name = if let Some(name) = literals.get(value) {
1253                    *name
1254                } else {
1255                    let name = self.global_name(self.global_ids.borrow_mut().fresh_id());
1256                    literals.insert(value.clone(), name);
1257                    name
1258                };
1259                mb::construct_wide_pointer(
1260                    mini::ValueExpr::Constant(
1261                        mini::Constant::GlobalPointer(mini::Relocation {
1262                            name,
1263                            offset: mini::Size::ZERO,
1264                        }),
1265                        mini::Type::Ptr(mini::PtrType::Raw {
1266                            meta_kind: mini::PointerMetaKind::None,
1267                        }),
1268                    ),
1269                    mini::ValueExpr::Constant(
1270                        mini::Constant::Int(mini::Int::from(value.len())),
1271                        mb::int_ty(mini::Signedness::Unsigned, T::PTR_SIZE),
1272                    ),
1273                    ty,
1274                )
1275            }
1276            ConstantExprKind::FnDef(_) => mini::ValueExpr::Tuple(Default::default(), ty),
1277            ConstantExprKind::FnPtr(fn_ptr) => match *fn_ptr.kind {
1278                FnPtrKind::Fun(id) => self.fn_pointer(id),
1279                FnPtrKind::Trait(..) => {
1280                    raise!(
1281                        span,
1282                        "trait function pointer remained after monomorphization"
1283                    )
1284                }
1285            },
1286            ConstantExprKind::Cast(value, CastKind::FnDefToFnPtr) => {
1287                let ConstantExprKind::FnDef(fn_ptr) = value.kind() else {
1288                    raise!(span, "expected a function item in fn_def_to_fn_ptr")
1289                };
1290                let FnPtrKind::Fun(id) = *fn_ptr.kind else {
1291                    raise!(span, "method reference remained after monomorphization")
1292                };
1293                self.fn_pointer(id)
1294            }
1295            ConstantExprKind::Cast(value, CastKind::FnPtrToUnsafe) => self.constant(span, value)?,
1296            ConstantExprKind::Cast(value, CastKind::Subtype(..)) => {
1297                mb::transmute(self.constant(span, value)?, ty)
1298            }
1299            ConstantExprKind::Cast(value, CastKind::RawPtr(..)) => {
1300                mb::transmute(self.constant(span, value)?, ty)
1301            }
1302            ConstantExprKind::PtrNoProvenance(value) => mini::ValueExpr::Constant(
1303                mini::Constant::PointerWithoutProvenance(mini::Int::from(*value)),
1304                ty,
1305            ),
1306            ConstantExprKind::Discriminant(tref, variant) => mini::ValueExpr::Constant(
1307                mini::Constant::Int(self.variant_discriminant_for_tref(span, tref, *variant)?),
1308                ty,
1309            ),
1310            // Like in globals, we lower a `TypeId` to a hash of the type.
1311            ConstantExprKind::TypeId(tid_ty) => {
1312                let value = self.type_id_val(span, tid_ty, 0)?;
1313                let int_ty = mb::int_ty(mini::Signedness::Unsigned, mini_size(16));
1314                let int = mini::ValueExpr::Constant(mini::Constant::Int(value.into()), int_ty);
1315                mb::transmute(int, ty)
1316            }
1317            ConstantExprKind::SizeOf(value) => {
1318                let (size, _) = self.size_and_align(span, value)?;
1319                mini::ValueExpr::Constant(mini::Constant::Int(mini::Int::from(size)), ty)
1320            }
1321            ConstantExprKind::AlignOf(value) => {
1322                let (_, align) = self.size_and_align(span, value)?;
1323                mini::ValueExpr::Constant(mini::Constant::Int(mini::Int::from(align)), ty)
1324            }
1325            _ => raise!(
1326                span,
1327                "unsupported MiniRust constant: {}",
1328                constant.with_ctx(&self.fmt)
1329            ),
1330        })
1331    }
1332
1333    /// The value of the `TypeId` of `ty`, as a 16-byte integer, with its pointers offset by
1334    /// `ptr_offset`. MiniRust doesn't have `TypeId` provenance so we make up the values of these
1335    /// pointers by hashing the type.
1336    fn type_id_val(&self, span: Span, ty: &Ty, ptr_offset: u64) -> Result<u128> {
1337        // A TypeId is `[*const (); 16 / size_of::<*const ()>()]`; its number of chunks
1338        // depends on the pointer size of the target platform
1339        let ptr_size = size_bytes(span, T::PTR_SIZE)?;
1340        let ptr_count = 16 / ptr_size;
1341        let mut val = 0;
1342        for i in 0..ptr_count {
1343            let mut hasher = DefaultHasher::new();
1344            (ty, i).hash(&mut hasher);
1345            let ptr = hasher.finish();
1346            // the ptr may have an offset, which we must apply
1347            let ptr = ptr.wrapping_add(ptr_offset);
1348            // we then make the ptr the right size
1349            let ptr = ptr as u128 & ((1 << (8 * ptr_size)) - 1);
1350            // the first pointer comes first in memory.
1351            let pos = if T::ENDIANNESS == mini::LittleEndian {
1352                i
1353            } else {
1354                ptr_count - 1 - i
1355            };
1356            val |= ptr << (8 * ptr_size * pos);
1357        }
1358        Ok(val)
1359    }
1360
1361    pub(super) fn constant_int(&self, span: Span, constant: &ConstantExpr) -> Result<mini::Int> {
1362        Ok(match self.constant(span, constant)? {
1363            mini::ValueExpr::Constant(c, _) => match c {
1364                mini::Constant::Int(int) => int,
1365                mini::Constant::Bool(b) => mini::Int::from(u128::from(b)),
1366                _ => raise!(
1367                    span,
1368                    "switch case is not an integer constant: {}",
1369                    constant.with_ctx(&self.fmt)
1370                ),
1371            },
1372            _ => raise!(
1373                span,
1374                "switch case is not an integer constant: {}",
1375                constant.with_ctx(&self.fmt)
1376            ),
1377        })
1378    }
1379}
1380
1381/// Traits and vtables
1382impl<T: mini::Target> TranslateCtx<'_, T> {
1383    fn vtable(&self, gdecl: &GlobalDecl) -> Result<mini::VTable> {
1384        let span = gdecl.item_meta.span;
1385        let tref = gdecl
1386            .ty
1387            .as_adt()
1388            .ok_or("vtable is not an ADT")
1389            .context(span)?;
1390        let tdecl = &self.krate.type_decls[tref.id];
1391        let TypeSource::VTable { field_map, .. } = &tdecl.src else {
1392            raise!(span, "vtable global has no vtable type")
1393        };
1394        let ConstantExprKind::Adt(None, fields) = gdecl.value.kind() else {
1395            raise!(span, "MiniRust needs an evaluated vtable value")
1396        };
1397        let GlobalSource::VTableInstance { self_ty, .. } = &gdecl.src else {
1398            raise!(span, "vtable global is not a vtable")
1399        };
1400        let (size, align) = self.size_and_align(span, self_ty)?;
1401        let mini::UnsafeCellStrategy::Sized { cells } = self.unsafe_cell_strategy(span, self_ty)?
1402        else {
1403            raise!(span, "vtable self type is not sized")
1404        };
1405        let mut methods = mini::Map::new();
1406        for (field, kind) in field_map.iter_enumerated() {
1407            if matches!(kind, VTableField::Drop | VTableField::Method(..)) {
1408                let field_val = &fields[field];
1409                let method = match field_val.kind() {
1410                    ConstantExprKind::Cast(value, _) => value,
1411                    _ => field_val,
1412                };
1413                let ConstantExprKind::FnPtr(fn_ptr) = method.kind() else {
1414                    raise!(span, "vtable method has no function shim")
1415                };
1416                let FnPtrKind::Fun(id) = fn_ptr.kind.as_ref() else {
1417                    raise!(span, "vtable method did not resolve to a function")
1418                };
1419                methods.insert(self.method_name(field), self.fn_name(*id));
1420            }
1421        }
1422        Ok(mini::VTable {
1423            trait_name: self.trait_name(tref.id),
1424            size: mini_size(size),
1425            align: mini_align(span, align)?,
1426            cells,
1427            methods,
1428        })
1429    }
1430}
1431
1432pub enum RunError {
1433    Translation(Error),
1434    Panic,
1435    Ub(String),
1436    Other(TerminationInfo),
1437}
1438
1439pub fn run<T>(krate: &TranslatedCrate) -> std::result::Result<(), RunError>
1440where
1441    T: mini::Target + serde::Serialize + serde::de::DeserializeOwned,
1442{
1443    let translator = TranslateCtx::<T>::new(krate).map_err(RunError::Translation)?;
1444    let program = translator.translate().map_err(RunError::Translation)?;
1445    match miniutil::run::run_program::<TreeBorrowsMemory<T>>(program) {
1446        TerminationInfo::MachineStop => Ok(()),
1447        TerminationInfo::IllFormed(message) => Err(RunError::Translation(Error::new(
1448            Span::dummy(),
1449            format!("MiniRust rejected the program: {message}"),
1450        ))),
1451        TerminationInfo::Abort => Err(RunError::Panic),
1452        TerminationInfo::Ub(message) => Err(RunError::Ub(message.to_string())),
1453        error @ (TerminationInfo::Deadlock | TerminationInfo::MemoryLeak) => {
1454            Err(RunError::Other(error))
1455        }
1456    }
1457}