Skip to main content

charon_lib/ast/
bodies.rs

1//! The bodies of functions.
2use crate::ast::*;
3use crate::ids::IndexVec;
4use crate::llbc_ast;
5use crate::ullbc_ast;
6use crate::utils::serialize_map_to_array::SeqHashMapToArray;
7use derive_generic_visitor::{Drive, DriveMut, DriveTwo};
8use macros::EnumAsGetters;
9use macros::{EnumIsA, EnumToGetters};
10use serde_state::DeserializeState;
11use serde_state::SerializeState;
12
13pub mod expressions;
14pub mod places;
15pub mod structured;
16pub mod unstructured;
17pub mod values;
18
19pub use expressions::*;
20pub use places::*;
21pub use values::*;
22
23/// The body of a function.
24///
25/// A normal function has a body that's either structured or unstructured. These are two equivalent
26/// representations of the same function body, which only differ in how control-flow is
27/// represented. By default bodies are structured; passing `--ullbc` to Charon makes bodies
28/// unstructured.
29///
30/// Besides these, some functions have virtual bodies or no body, see the doc for each variant.
31#[derive(
32    Debug,
33    Clone,
34    SerializeState,
35    DeserializeState,
36    Drive,
37    DriveMut,
38    DriveTwo,
39    EnumIsA,
40    EnumAsGetters,
41    EnumToGetters,
42)]
43#[serde_state(state_implements = DedupSerializerState)]
44#[cfg_attr(feature = "charon_on_charon", charon::variants_suffix("Body"))]
45pub enum Body {
46    /// Body represented as a control-flow graph (CFG), i.e. with numbered blocks and jumps/GOTOs
47    /// between them. This is available when passing `--ullbc` to Charon.
48    ///
49    /// This is also the same structure as rustc's MIR, and is in fact a direct translation of it.
50    Unstructured(ullbc_ast::ExprBody),
51    /// Body represented with structured control flow, i.e. with nested `if`/`match`/`loop` blocks.
52    /// This is available when `--ullbc` is not passed to Charon.
53    ///
54    /// This structure is recovered from the unstructured body in the `ullbc_to_llbc` pass.
55    Structured(llbc_ast::ExprBody),
56    /// A façade body that dispatches to one of several per-target function bodies. This is created
57    /// during multi-target merging (using `--target`) to represent functions that have a different
58    /// implementation across targets.
59    TargetDispatch(
60        #[serde(with = "SeqHashMapToArray::<TargetTriple, FunDeclRef>")]
61        SeqHashMap<TargetTriple, FunDeclRef>,
62    ),
63    /// Function declared in an `extern { ... }` block. The string is the foreign symbol name.
64    Extern(String),
65    /// Rust intrinsic function. This has no body and describes a "built-in" operation that must be
66    /// handled by the codegen backend.
67    Intrinsic {
68        /// The intrinsic name.
69        name: String,
70        /// The argument names, None if not available.
71        arg_names: Vec<Option<String>>,
72    },
73    /// A body that the user chose not to translate, based on opacity settings like
74    /// `--include`/`--opaque`.
75    Opaque,
76    /// A body that was not available.
77    ///
78    /// These can occur when using a sysroot that doesn't have MIR for all the standard library
79    /// functions. This is the case of the sysroot that ships with every Rust toolchain, but Charon
80    /// uses a custom-built sysroot that does. So this shouldn't happen unless you're passing
81    /// `--sysroot` to charon.
82    Missing,
83    /// We encountered an error while translating this body.
84    #[serde_state(stateless)]
85    Error(Error),
86}
87
88generate_index_type!(LocalId, "");
89
90/// The local variables of a body.
91#[derive(Debug, Default, Clone, SerializeState, DeserializeState, Drive, DriveMut, DriveTwo)]
92pub struct Locals {
93    /// The number of local variables used for the input arguments.
94    pub arg_count: usize,
95    /// The local variables.
96    /// We always have, in the following order:
97    /// - the local used for the return value (index 0)
98    /// - the `arg_count` input arguments
99    /// - the remaining locals, used for the intermediate computations
100    pub locals: IndexVec<LocalId, Local>,
101}
102
103/// A variable
104#[derive(Debug, Clone, SerializeState, DeserializeState, Drive, DriveMut, DriveTwo)]
105pub struct Local {
106    /// Unique index identifying the variable
107    pub index: LocalId,
108    /// Variable name - may be `None` if the variable was introduced by Rust
109    /// through desugaring.
110    pub name: Option<String>,
111    /// Span of the variable declaration.
112    pub span: Span,
113    /// The variable type
114    #[cfg_attr(feature = "charon_on_charon", charon::rename("local_ty"))]
115    pub ty: Ty,
116}
117
118/// An expression body.
119/// TODO: arg_count should be stored in GFunDecl below. But then,
120///       the print is obfuscated and Aeneas may need some refactoring.
121#[derive(Debug, Clone, SerializeState, DeserializeState, Drive, DriveMut, DriveTwo)]
122#[cfg_attr(feature = "charon_on_charon", charon::rename("GexprBody"))]
123pub struct GExprBody<T> {
124    pub span: Span,
125    /// The number of regions existentially bound in this body. We introduce fresh such regions
126    /// during translation instead of the erased regions that rustc gives us.
127    pub bound_body_regions: usize,
128    /// The local variables.
129    pub locals: Locals,
130    /// The statements and blocks that compose this body.
131    pub body: T,
132    /// For each line inside the body, we record any whole-line `//` comments found before it. They
133    /// are added to statements in the late `recover_body_comments` pass.
134    #[cfg_attr(feature = "charon_on_charon", charon::opaque)]
135    pub comments: Vec<(u32, Vec<String>)>,
136}
137
138generate_index_type!(BranchId, "Branch");
139
140/// The value inspected by a switch. Must be of integer, bool or char type.
141#[derive(
142    Debug, PartialEq, Eq, Clone, SerializeState, DeserializeState, Drive, DriveMut, DriveTwo,
143)]
144#[cfg_attr(feature = "charon_on_charon", charon::variants_prefix("Switch"))]
145pub enum SwitchScrutinee {
146    /// Inspect the value produced by an operand.
147    Value(Operand),
148    /// Inspect the discriminant of an enum place.
149    Discriminant(Place),
150}
151
152/// A branching operation.
153#[derive(
154    Debug, PartialEq, Eq, Clone, SerializeState, DeserializeState, Drive, DriveMut, DriveTwo,
155)]
156pub struct SwitchData {
157    /// The value to branch over.
158    pub scrutinee: SwitchScrutinee,
159    /// Which branch to take for each value of the scrutinee. Several values may point to the same
160    /// branch, and not all values may be accounted for.
161    ///
162    /// If the scrutinee is an operand, the constant expressions are literal values. If the
163    /// scrutinee is a discriminant read, the expressions are of the form
164    /// `ConstantExprKind::Discriminant`.
165    pub branches: Vec<(ConstantExpr, BranchId)>,
166    /// Branch to use if the scrutinee didn't match any of the values above. `None` if the set of
167    /// branch values is known to be exhaustive.
168    pub fallback: Option<BranchId>,
169}
170
171/// A function operand is used in function calls.
172/// It either designates a top-level function, or a place in case
173/// we are using function pointers stored in local variables.
174#[derive(
175    Debug, PartialEq, Eq, Clone, SerializeState, DeserializeState, Drive, DriveMut, DriveTwo,
176)]
177#[cfg_attr(feature = "charon_on_charon", charon::variants_prefix("FnOp"))]
178pub enum FnOperand {
179    /// Regular case: call to a top-level function, trait method, etc.
180    Regular(FnPtr),
181    /// Use of a function pointer.
182    Dynamic(Operand),
183}
184
185#[derive(
186    Debug, PartialEq, Eq, Clone, SerializeState, DeserializeState, Drive, DriveMut, DriveTwo,
187)]
188pub struct Call {
189    pub func: FnOperand,
190    pub args: Vec<Operand>,
191    pub dest: Place,
192}
193
194/// Statements that only affect borrow-checking. They are no-ops at runtime.
195#[derive(
196    Debug, PartialEq, Eq, Clone, SerializeState, DeserializeState, Drive, DriveMut, DriveTwo,
197)]
198pub enum BorrowckStatement {
199    /// Acts like a read of the place.
200    FakeRead(Place),
201    /// Relate the type of a place to the provided type. For example, `let x: Self = value`
202    /// produces `SetType` for `x` and `Self`.
203    SetType {
204        place: Place,
205        ty: Ty,
206        #[serde_state(stateless)]
207        variance: Variance,
208    },
209    /// Require a type to outlive a region. For example, the `'a` bound in
210    /// `let x: impl Copy + 'a = value` produces `SetOutlives(typeof(x), 'a)`.
211    SetOutlives(Ty, Region),
212    /// Require a trait predicate to hold. For example, the `Copy` bound in
213    /// `let x: impl Copy = value` produces `PredicateHolds(typeof(x): Copy)`.
214    PredicateHolds(TraitRef),
215}
216
217/// (U)LLBC is a language with side-effects: a statement may abort in a way that isn't tracked by
218/// control-flow. The three kinds of abort are:
219/// - Panic
220/// - Undefined behavior (caused by an "assume")
221/// - Unwind termination
222#[derive(
223    Debug, PartialEq, Eq, Clone, SerializeState, DeserializeState, Drive, DriveMut, DriveTwo,
224)]
225pub enum AbortKind {
226    /// A built-in panicking function, or a panic due to a failed built-in check (e.g. for out-of-bounds accesses).
227    Panic(Option<Name>),
228    /// Undefined behavior in the rust abstract machine.
229    UndefinedBehavior,
230    /// Unwind had to stop for ABI reasons or because cleanup code panicked again.
231    UnwindTerminate,
232}
233
234/// A `Drop` statement/terminator can mean two things, depending on what MIR phase we retrieved
235/// from rustc: it could be a real drop, or it could be a "conditional drop", which is where drop
236/// may happen depending on whether the borrow-checker determines a drop is needed.
237#[derive(
238    Debug, PartialEq, Eq, Clone, Copy, SerializeState, DeserializeState, Drive, DriveMut, DriveTwo,
239)]
240pub enum DropKind {
241    /// A real drop. This calls `<T as Destruct>::drop_glue(&mut place)` and marks the
242    /// place as moved-out-of. Use `--desugar-drops` to transform all such drops to an actual
243    /// function call.
244    ///
245    /// The `drop_glue` method is added by Charon to the `Destruct` trait to make it possible
246    /// to track drop code in polymorphic code. It contains the same code as the
247    /// `core::ptr::drop_glue<T>` builtin would.
248    ///
249    /// Drop are precise in MIR `elaborated` and `optimized`.
250    Precise,
251    /// A conditional drop, which may or may not end up running drop code depending on the code
252    /// path that led to it. A conditional drop may also become a partial drop (dropping only the
253    /// subplaces that haven't been moved out of), may be conditional on the code path that led to
254    /// it, or become an async drop. The exact semantics are left intentionally unspecified by
255    /// rustc developers. To elaborate such drops into precise drops, pass `--precise-drops` to
256    /// Charon.
257    ///
258    /// A conditional drop may also be passed an unaligned place when dropping fields of packed
259    /// structs. Such a thing is UB for a precise drop.
260    ///
261    /// Drop are conditional in MIR `built` and `promoted`.
262    Conditional,
263}
264
265/// Check the value of an operand and abort if the value is not expected. This is introduced to
266/// avoid a lot of small branches.
267///
268/// We translate MIR asserts (introduced for out-of-bounds accesses or divisions by zero for
269/// instance) to this. We then eliminate them in [crate::transform::resugar::reconstruct_fallible_operations],
270/// because they're implicit in the semantics of our array accesses etc. Finally we introduce new asserts in
271/// [crate::transform::resugar::reconstruct_asserts].
272#[derive(
273    Debug, PartialEq, Eq, Clone, SerializeState, DeserializeState, Drive, DriveMut, DriveTwo,
274)]
275#[cfg_attr(feature = "charon_on_charon", charon::rename("Assertion"))]
276pub struct Assert {
277    pub cond: Operand,
278    /// The value that the operand should evaluate to for the assert to succeed.
279    pub expected: bool,
280    /// The kind of check performed by this assert. This is only used for error reporting, as the check
281    /// is actually performed by the instructions preceding the assert.
282    pub check_kind: Option<BuiltinAssertKind>,
283}
284
285/// The kind of a built-in assertion, which may panic and unwind. These are removed
286/// by `reconstruct_fallible_operations` because they're implicit in the semantics of (U)LLBC.
287/// This kind should only be used for error-reporting purposes, as the check itself
288/// is performed in the instructions preceding the assert.
289#[derive(
290    Debug, PartialEq, Eq, Clone, SerializeState, DeserializeState, Drive, DriveMut, DriveTwo,
291)]
292pub enum BuiltinAssertKind {
293    BoundsCheck { len: Operand, index: Operand },
294    Overflow(BinOp, Operand, Operand),
295    OverflowNeg(Operand),
296    DivisionByZero(Operand),
297    RemainderByZero(Operand),
298    MisalignedPointerDereference { required: Operand, found: Operand },
299    NullPointerDereference,
300    NullReferenceCreated,
301    InvalidEnumConstruction(Operand),
302    ResumedAfterReturn,
303    ResumedAfterPanic,
304    ResumedAfterDrop,
305}
306
307impl Body {
308    /// Whether there is an actual body with statements etc, as opposed to the body being missing
309    /// for some reason.
310    pub fn has_contents(&self) -> bool {
311        match self {
312            Body::Unstructured(..) | Body::Structured(..) => true,
313            Body::Extern(..)
314            | Body::Intrinsic { .. }
315            | Body::Opaque
316            | Body::Missing
317            | Body::Error(..)
318            | Body::TargetDispatch(..) => false,
319        }
320    }
321
322    pub fn locals(&self) -> &Locals {
323        match self {
324            Body::Structured(body) => &body.locals,
325            Body::Unstructured(body) => &body.locals,
326            _ => panic!("called `locals` on a missing body"),
327        }
328    }
329}
330
331impl Locals {
332    pub fn new(arg_count: usize) -> Self {
333        Self {
334            arg_count,
335            locals: Default::default(),
336        }
337    }
338
339    /// Creates a new variable and returns a place pointing to it.
340    /// Warning: don't forget to `StorageLive` it before using it.
341    pub fn new_var(&mut self, name: Option<String>, ty: Ty) -> Place {
342        let local_id = self.locals.push_with(|index| Local {
343            index,
344            name,
345            span: Span::dummy(),
346            ty: ty.clone(),
347        });
348        Place::new(local_id, ty)
349    }
350
351    /// Gets a place pointing to the corresponding variable.
352    pub fn place_for_var(&self, local_id: LocalId) -> Place {
353        let ty = self.locals[local_id].ty.clone();
354        Place::new(local_id, ty)
355    }
356
357    /// Returns whether this local is the special return local or one of the input argument locals.
358    pub fn is_return_or_arg(&self, lid: LocalId) -> bool {
359        lid.index() <= self.arg_count
360    }
361
362    /// The place where we write the return value.
363    pub fn return_place(&self) -> Place {
364        self.place_for_var(LocalId::new(0))
365    }
366
367    /// Locals that aren't arguments or return values.
368    pub fn non_argument_locals(&self) -> impl Iterator<Item = (LocalId, &Local)> {
369        self.locals.iter_enumerated().skip(1 + self.arg_count)
370    }
371}
372
373impl SwitchData {
374    /// If this is a switch over a boolean, return its `then` and `else` branches.
375    pub fn as_if(&self) -> Option<(BranchId, BranchId)> {
376        let SwitchScrutinee::Value(scrutinee) = &self.scrutinee else {
377            return None;
378        };
379        if !matches!(scrutinee.ty().kind(), TyKind::Scalar(ScalarTy::Bool)) {
380            return None;
381        }
382
383        let branch_for = |value| {
384            self.branches
385                .iter()
386                .find_map(|(case, branch_id)| match case.kind() {
387                    ConstantExprKind::Bool(case_value) if *case_value == value => Some(*branch_id),
388                    _ => None,
389                })
390                .or(self.fallback)
391        };
392        Some((branch_for(true)?, branch_for(false)?))
393    }
394
395    /// Group the explicit switch values by the branch they select.
396    pub fn group_by_branch(&self) -> IndexVec<BranchId, Vec<ConstantExpr>> {
397        let mut grouped = IndexVec::new();
398        if let Some(branch_id) = self.fallback {
399            grouped.get_or_extend_and_insert(branch_id, Vec::new);
400        }
401        for (value, branch_id) in &self.branches {
402            grouped
403                .get_or_extend_and_insert(*branch_id, Vec::new)
404                .push(value.clone());
405        }
406        grouped
407    }
408}
409
410impl std::ops::Index<LocalId> for Locals {
411    type Output = Local;
412    fn index(&self, local_id: LocalId) -> &Self::Output {
413        &self.locals[local_id]
414    }
415}
416impl std::ops::IndexMut<LocalId> for Locals {
417    fn index_mut(&mut self, local_id: LocalId) -> &mut Self::Output {
418        &mut self.locals[local_id]
419    }
420}