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#[derive(
25    Debug,
26    PartialEq,
27    Eq,
28    Clone,
29    SerializeState,
30    DeserializeState,
31    Drive,
32    DriveMut,
33    DriveTwo,
34    EnumIsA,
35    EnumAsGetters,
36    EnumToGetters,
37)]
38#[serde_state(state_implements = HashConsSerializerState)]
39#[cfg_attr(feature = "charon_on_charon", charon::variants_suffix("Body"))]
40pub enum Body {
41    /// Body represented as a CFG. This is what ullbc is made of, and what we get after translating MIR.
42    Unstructured(ullbc_ast::ExprBody),
43    /// Body represented with structured control flow. This is what llbc is made of. We restructure
44    /// the control flow in the `ullbc_to_llbc` pass.
45    Structured(llbc_ast::ExprBody),
46    /// A façade body that dispatches to one of several per-target function bodies. Created during
47    /// multi-target merging for functions with the same signature but different bodies across
48    /// targets.
49    TargetDispatch(
50        #[serde(with = "SeqHashMapToArray::<TargetTriple, FunDeclRef>")]
51        SeqHashMap<TargetTriple, FunDeclRef>,
52    ),
53    /// Function declared in an `extern { ... }` block. The string is the foreign symbol name.
54    Extern(#[drive(skip)] String),
55    /// Rust intrinsic function.
56    Intrinsic {
57        /// The intrinsic name.
58        #[drive(skip)]
59        name: String,
60        /// The argument names, None if not available.
61        #[drive(skip)]
62        arg_names: Vec<Option<String>>,
63    },
64    /// A body that the user chose not to translate, based on opacity settings like
65    /// `--include`/`--opaque`.
66    Opaque,
67    /// A body that was not available. Typically that's function bodies for non-generic and
68    /// non-inlineable std functions, as these are not present in the compiled standard library
69    /// `.rmeta` file shipped with a rust toolchain.
70    Missing,
71    /// We encountered an error while translating this body.
72    #[drive(skip)]
73    #[serde_state(stateless)]
74    Error(Error),
75}
76
77generate_index_type!(LocalId, "");
78
79/// The local variables of a body.
80#[derive(
81    Debug,
82    PartialEq,
83    Eq,
84    Default,
85    Clone,
86    SerializeState,
87    DeserializeState,
88    Drive,
89    DriveMut,
90    DriveTwo,
91)]
92pub struct Locals {
93    /// The number of local variables used for the input arguments.
94    #[drive(skip)]
95    pub arg_count: usize,
96    /// The local variables.
97    /// We always have, in the following order:
98    /// - the local used for the return value (index 0)
99    /// - the `arg_count` input arguments
100    /// - the remaining locals, used for the intermediate computations
101    pub locals: IndexVec<LocalId, Local>,
102}
103
104/// A variable
105#[derive(
106    Debug, PartialEq, Eq, Clone, SerializeState, DeserializeState, Drive, DriveMut, DriveTwo,
107)]
108pub struct Local {
109    /// Unique index identifying the variable
110    pub index: LocalId,
111    /// Variable name - may be `None` if the variable was introduced by Rust
112    /// through desugaring.
113    #[drive(skip)]
114    pub name: Option<String>,
115    /// Span of the variable declaration.
116    pub span: Span,
117    /// The variable type
118    #[cfg_attr(feature = "charon_on_charon", charon::rename("local_ty"))]
119    pub ty: Ty,
120}
121
122/// An expression body.
123/// TODO: arg_count should be stored in GFunDecl below. But then,
124///       the print is obfuscated and Aeneas may need some refactoring.
125#[derive(
126    Debug, PartialEq, Eq, Clone, SerializeState, DeserializeState, Drive, DriveMut, DriveTwo,
127)]
128#[cfg_attr(feature = "charon_on_charon", charon::rename("GexprBody"))]
129pub struct GExprBody<T> {
130    pub span: Span,
131    /// The number of regions existentially bound in this body. We introduce fresh such regions
132    /// during translation instead of the erased regions that rustc gives us.
133    #[drive(skip)]
134    pub bound_body_regions: usize,
135    /// The local variables.
136    pub locals: Locals,
137    /// The statements and blocks that compose this body.
138    pub body: T,
139    /// For each line inside the body, we record any whole-line `//` comments found before it. They
140    /// are added to statements in the late `recover_body_comments` pass.
141    #[cfg_attr(feature = "charon_on_charon", charon::opaque)]
142    #[drive(skip)]
143    pub comments: Vec<(usize, Vec<String>)>,
144}
145
146/// A function operand is used in function calls.
147/// It either designates a top-level function, or a place in case
148/// we are using function pointers stored in local variables.
149#[derive(
150    Debug, PartialEq, Eq, Clone, SerializeState, DeserializeState, Drive, DriveMut, DriveTwo,
151)]
152#[cfg_attr(feature = "charon_on_charon", charon::variants_prefix("FnOp"))]
153pub enum FnOperand {
154    /// Regular case: call to a top-level function, trait method, etc.
155    Regular(FnPtr),
156    /// Use of a function pointer.
157    Dynamic(Operand),
158}
159
160#[derive(
161    Debug, PartialEq, Eq, Clone, SerializeState, DeserializeState, Drive, DriveMut, DriveTwo,
162)]
163pub struct Call {
164    pub func: FnOperand,
165    pub args: Vec<Operand>,
166    pub dest: Place,
167}
168
169/// Statements that only affect borrow-checking. They are no-ops at runtime.
170#[derive(
171    Debug, PartialEq, Eq, Clone, SerializeState, DeserializeState, Drive, DriveMut, DriveTwo,
172)]
173pub enum BorrowckStatement {
174    /// Acts like a read of the place.
175    FakeRead(Place),
176    /// Relate the type of a place to the provided type. For example, `let x: Self = value`
177    /// produces `SetType` for `x` and `Self`.
178    SetType {
179        place: Place,
180        ty: Ty,
181        #[drive(skip)]
182        #[serde_state(stateless)]
183        variance: Variance,
184    },
185    /// Require a type to outlive a region. For example, the `'a` bound in
186    /// `let x: impl Copy + 'a = value` produces `SetOutlives(typeof(x), 'a)`.
187    SetOutlives(Ty, Region),
188    /// Require a trait predicate to hold. For example, the `Copy` bound in
189    /// `let x: impl Copy = value` produces `PredicateHolds(typeof(x): Copy)`.
190    PredicateHolds(TraitRef),
191}
192
193/// (U)LLBC is a language with side-effects: a statement may abort in a way that isn't tracked by
194/// control-flow. The three kinds of abort are:
195/// - Panic
196/// - Undefined behavior (caused by an "assume")
197/// - Unwind termination
198#[derive(
199    Debug, PartialEq, Eq, Clone, SerializeState, DeserializeState, Drive, DriveMut, DriveTwo,
200)]
201pub enum AbortKind {
202    /// A built-in panicking function, or a panic due to a failed built-in check (e.g. for out-of-bounds accesses).
203    Panic(Option<Name>),
204    /// Undefined behavior in the rust abstract machine.
205    UndefinedBehavior,
206    /// Unwind had to stop for ABI reasons or because cleanup code panicked again.
207    UnwindTerminate,
208}
209
210/// A `Drop` statement/terminator can mean two things, depending on what MIR phase we retrieved
211/// from rustc: it could be a real drop, or it could be a "conditional drop", which is where drop
212/// may happen depending on whether the borrow-checker determines a drop is needed.
213#[derive(
214    Debug, PartialEq, Eq, Clone, Copy, SerializeState, DeserializeState, Drive, DriveMut, DriveTwo,
215)]
216pub enum DropKind {
217    /// A real drop. This calls `<T as Destruct>::drop_glue(&mut place)` and marks the
218    /// place as moved-out-of. Use `--desugar-drops` to transform all such drops to an actual
219    /// function call.
220    ///
221    /// The `drop_glue` method is added by Charon to the `Destruct` trait to make it possible
222    /// to track drop code in polymorphic code. It contains the same code as the
223    /// `core::ptr::drop_glue<T>` builtin would.
224    ///
225    /// Drop are precise in MIR `elaborated` and `optimized`.
226    Precise,
227    /// A conditional drop, which may or may not end up running drop code depending on the code
228    /// path that led to it. A conditional drop may also become a partial drop (dropping only the
229    /// subplaces that haven't been moved out of), may be conditional on the code path that led to
230    /// it, or become an async drop. The exact semantics are left intentionally unspecified by
231    /// rustc developers. To elaborate such drops into precise drops, pass `--precise-drops` to
232    /// Charon.
233    ///
234    /// A conditional drop may also be passed an unaligned place when dropping fields of packed
235    /// structs. Such a thing is UB for a precise drop.
236    ///
237    /// Drop are conditional in MIR `built` and `promoted`.
238    Conditional,
239}
240
241/// Check the value of an operand and abort if the value is not expected. This is introduced to
242/// avoid a lot of small branches.
243///
244/// We translate MIR asserts (introduced for out-of-bounds accesses or divisions by zero for
245/// instance) to this. We then eliminate them in [crate::transform::resugar::reconstruct_fallible_operations],
246/// because they're implicit in the semantics of our array accesses etc. Finally we introduce new asserts in
247/// [crate::transform::resugar::reconstruct_asserts].
248#[derive(
249    Debug, PartialEq, Eq, Clone, SerializeState, DeserializeState, Drive, DriveMut, DriveTwo,
250)]
251#[cfg_attr(feature = "charon_on_charon", charon::rename("Assertion"))]
252pub struct Assert {
253    pub cond: Operand,
254    /// The value that the operand should evaluate to for the assert to succeed.
255    #[drive(skip)]
256    pub expected: bool,
257    /// The kind of check performed by this assert. This is only used for error reporting, as the check
258    /// is actually performed by the instructions preceding the assert.
259    pub check_kind: Option<BuiltinAssertKind>,
260}
261
262/// The kind of a built-in assertion, which may panic and unwind. These are removed
263/// by `reconstruct_fallible_operations` because they're implicit in the semantics of (U)LLBC.
264/// This kind should only be used for error-reporting purposes, as the check itself
265/// is performed in the instructions preceding the assert.
266#[derive(
267    Debug, PartialEq, Eq, Clone, SerializeState, DeserializeState, Drive, DriveMut, DriveTwo,
268)]
269pub enum BuiltinAssertKind {
270    BoundsCheck { len: Operand, index: Operand },
271    Overflow(BinOp, Operand, Operand),
272    OverflowNeg(Operand),
273    DivisionByZero(Operand),
274    RemainderByZero(Operand),
275    MisalignedPointerDereference { required: Operand, found: Operand },
276    NullPointerDereference,
277    NullReferenceCreated,
278    InvalidEnumConstruction(Operand),
279    ResumedAfterReturn,
280    ResumedAfterPanic,
281    ResumedAfterDrop,
282}
283
284impl Body {
285    /// Whether there is an actual body with statements etc, as opposed to the body being missing
286    /// for some reason.
287    pub fn has_contents(&self) -> bool {
288        match self {
289            Body::Unstructured(..) | Body::Structured(..) => true,
290            Body::Extern(..)
291            | Body::Intrinsic { .. }
292            | Body::Opaque
293            | Body::Missing
294            | Body::Error(..)
295            | Body::TargetDispatch(..) => false,
296        }
297    }
298
299    pub fn locals(&self) -> &Locals {
300        match self {
301            Body::Structured(body) => &body.locals,
302            Body::Unstructured(body) => &body.locals,
303            _ => panic!("called `locals` on a missing body"),
304        }
305    }
306}
307
308impl Locals {
309    pub fn new(arg_count: usize) -> Self {
310        Self {
311            arg_count,
312            locals: Default::default(),
313        }
314    }
315
316    /// Creates a new variable and returns a place pointing to it.
317    /// Warning: don't forget to `StorageLive` it before using it.
318    pub fn new_var(&mut self, name: Option<String>, ty: Ty) -> Place {
319        let local_id = self.locals.push_with(|index| Local {
320            index,
321            name,
322            span: Span::dummy(),
323            ty: ty.clone(),
324        });
325        Place::new(local_id, ty)
326    }
327
328    /// Gets a place pointing to the corresponding variable.
329    pub fn place_for_var(&self, local_id: LocalId) -> Place {
330        let ty = self.locals[local_id].ty.clone();
331        Place::new(local_id, ty)
332    }
333
334    /// Returns whether this local is the special return local or one of the input argument locals.
335    pub fn is_return_or_arg(&self, lid: LocalId) -> bool {
336        lid.index() <= self.arg_count
337    }
338
339    /// The place where we write the return value.
340    pub fn return_place(&self) -> Place {
341        self.place_for_var(LocalId::new(0))
342    }
343
344    /// Locals that aren't arguments or return values.
345    pub fn non_argument_locals(&self) -> impl Iterator<Item = (LocalId, &Local)> {
346        self.locals.iter_enumerated().skip(1 + self.arg_count)
347    }
348}
349
350impl std::ops::Index<LocalId> for Locals {
351    type Output = Local;
352    fn index(&self, local_id: LocalId) -> &Self::Output {
353        &self.locals[local_id]
354    }
355}
356impl std::ops::IndexMut<LocalId> for Locals {
357    fn index_mut(&mut self, local_id: LocalId) -> &mut Self::Output {
358        &mut self.locals[local_id]
359    }
360}