charon_lib/ast/
ullbc_ast.rs

1//! "Unstructured LLBC" ast (ULLBC). This is LLBC before the control-flow
2//! reconstruction. In effect, this is a cleaned up version of MIR.
3pub use crate::ast::*;
4use derive_generic_visitor::{Drive, DriveMut};
5use macros::{EnumAsGetters, EnumIsA, VariantIndexArity, VariantName};
6use serde_state::{DeserializeState, SerializeState};
7
8// Block identifier. Similar to rust's `BasicBlock`.
9generate_index_type!(BlockId, "Block");
10
11// The entry block of a function is always the block with id 0
12pub static START_BLOCK_ID: BlockId = BlockId::ZERO;
13
14#[charon::rename("Blocks")]
15pub type BodyContents = Vector<BlockId, BlockData>;
16pub type ExprBody = GExprBody<BodyContents>;
17
18/// A raw statement: a statement without meta data.
19#[derive(
20    Debug,
21    Clone,
22    EnumIsA,
23    EnumAsGetters,
24    VariantName,
25    SerializeState,
26    DeserializeState,
27    Drive,
28    DriveMut,
29)]
30pub enum StatementKind {
31    Assign(Place, Rvalue),
32    /// A call. For now, we don't support dynamic calls (i.e. to a function pointer in memory).
33    SetDiscriminant(Place, VariantId),
34    /// Equivalent to std::intrinsics::copy_nonoverlapping; this is not modelled as a function
35    /// call as it cannot diverge
36    CopyNonOverlapping(Box<CopyNonOverlapping>),
37    /// Indicates that this local should be allocated; if it is already allocated, this frees
38    /// the local and re-allocates it. The return value and arguments do not receive a
39    /// `StorageLive`. We ensure in the micro-pass `insert_storage_lives` that all other locals
40    /// have a `StorageLive` associated with them.
41    StorageLive(LocalId),
42    /// Indicates that this local should be deallocated; if it is already deallocated, this is
43    /// a no-op. A local may not have a `StorageDead` in the function's body, in which case it
44    /// is implicitly deallocated at the end of the function.
45    StorageDead(LocalId),
46    Deinit(Place),
47    /// A built-in assert, which corresponds to runtime checks that we remove, namely: bounds
48    /// checks, over/underflow checks, div/rem by zero checks, pointer alignement check.
49    Assert(Assert),
50    /// Does nothing. Useful for passes.
51    Nop,
52    #[charon::opaque]
53    #[drive(skip)]
54    Error(String),
55}
56
57#[derive(Debug, Clone, SerializeState, DeserializeState, Drive, DriveMut)]
58pub struct Statement {
59    pub span: Span,
60    pub kind: StatementKind,
61    /// Comments that precede this statement.
62    // This is filled in a late pass after all the control-flow manipulation.
63    #[drive(skip)]
64    pub comments_before: Vec<String>,
65}
66
67#[derive(
68    Debug,
69    Clone,
70    EnumIsA,
71    EnumAsGetters,
72    VariantName,
73    VariantIndexArity,
74    SerializeState,
75    DeserializeState,
76    Drive,
77    DriveMut,
78)]
79#[charon::rename("Switch")]
80pub enum SwitchTargets {
81    /// Gives the `if` block and the `else` block
82    If(BlockId, BlockId),
83    /// Gives the integer type, a map linking values to switch branches, and the
84    /// otherwise block. Note that matches over enumerations are performed by
85    /// switching over the discriminant, which is an integer.
86    SwitchInt(LiteralTy, Vec<(Literal, BlockId)>, BlockId),
87}
88
89/// A raw terminator: a terminator without meta data.
90#[derive(
91    Debug, Clone, EnumIsA, EnumAsGetters, SerializeState, DeserializeState, Drive, DriveMut,
92)]
93pub enum TerminatorKind {
94    Goto {
95        target: BlockId,
96    },
97    Switch {
98        discr: Operand,
99        targets: SwitchTargets,
100    },
101    Call {
102        call: Call,
103        target: BlockId,
104        on_unwind: BlockId,
105    },
106    /// Drop the value at the given place.
107    ///
108    /// Depending on `DropKind`, this may be a real call to `drop_in_place`, or a conditional call
109    /// that should only happen if the place has not been moved out of. See the docs of `DropKind`
110    /// for more details; to get precise drops use `--precise-drops`.
111    Drop {
112        #[drive(skip)]
113        kind: DropKind,
114        place: Place,
115        tref: TraitRef,
116        target: BlockId,
117        on_unwind: BlockId,
118    },
119    /// Handles panics and impossible cases.
120    Abort(AbortKind),
121    Return,
122    UnwindResume,
123}
124
125#[derive(Debug, Clone, SerializeState, DeserializeState, Drive, DriveMut)]
126pub struct Terminator {
127    pub span: Span,
128    pub kind: TerminatorKind,
129    /// Comments that precede this terminator.
130    // This is filled in a late pass after all the control-flow manipulation.
131    #[drive(skip)]
132    pub comments_before: Vec<String>,
133}
134
135#[derive(Debug, Clone, SerializeState, DeserializeState, Drive, DriveMut)]
136#[charon::rename("Block")]
137pub struct BlockData {
138    pub statements: Vec<Statement>,
139    pub terminator: Terminator,
140}