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::{Deserialize, Serialize};
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, Clone, EnumIsA, EnumAsGetters, VariantName, Serialize, Deserialize, Drive, DriveMut,
21)]
22pub enum RawStatement {
23    Assign(Place, Rvalue),
24    /// A call. For now, we don't support dynamic calls (i.e. to a function pointer in memory).
25    SetDiscriminant(Place, VariantId),
26    /// Indicates that this local should be allocated; if it is already allocated, this frees
27    /// the local and re-allocates it. The return value and arguments do not receive a
28    /// `StorageLive`. We ensure in the micro-pass `insert_storage_lives` that all other locals
29    /// have a `StorageLive` associated with them.
30    StorageLive(LocalId),
31    /// Indicates that this local should be deallocated; if it is already deallocated, this is
32    /// a no-op. A local may not have a `StorageDead` in the function's body, in which case it
33    /// is implicitly deallocated at the end of the function.
34    StorageDead(LocalId),
35    Deinit(Place),
36    Drop(Place),
37    /// A built-in assert, which corresponds to runtime checks that we remove, namely: bounds
38    /// checks, over/underflow checks, div/rem by zero checks, pointer alignement check.
39    Assert(Assert),
40    /// Does nothing. Useful for passes.
41    Nop,
42    #[charon::opaque]
43    #[drive(skip)]
44    Error(String),
45}
46
47#[derive(Debug, Clone, Serialize, Deserialize, Drive, DriveMut)]
48pub struct Statement {
49    pub span: Span,
50    pub content: RawStatement,
51    /// Comments that precede this statement.
52    // This is filled in a late pass after all the control-flow manipulation.
53    #[drive(skip)]
54    pub comments_before: Vec<String>,
55}
56
57#[derive(
58    Debug,
59    Clone,
60    EnumIsA,
61    EnumAsGetters,
62    VariantName,
63    VariantIndexArity,
64    Serialize,
65    Deserialize,
66    Drive,
67    DriveMut,
68)]
69#[charon::rename("Switch")]
70pub enum SwitchTargets {
71    /// Gives the `if` block and the `else` block
72    If(BlockId, BlockId),
73    /// Gives the integer type, a map linking values to switch branches, and the
74    /// otherwise block. Note that matches over enumerations are performed by
75    /// switching over the discriminant, which is an integer.
76    SwitchInt(IntegerTy, Vec<(ScalarValue, BlockId)>, BlockId),
77}
78
79/// A raw terminator: a terminator without meta data.
80#[derive(Debug, Clone, EnumIsA, EnumAsGetters, Serialize, Deserialize, Drive, DriveMut)]
81pub enum RawTerminator {
82    Goto {
83        target: BlockId,
84    },
85    Switch {
86        discr: Operand,
87        targets: SwitchTargets,
88    },
89    Call {
90        call: Call,
91        target: BlockId,
92    },
93    /// Handles panics and impossible cases.
94    Abort(AbortKind),
95    Return,
96}
97
98#[derive(Debug, Clone, Serialize, Deserialize, Drive, DriveMut)]
99pub struct Terminator {
100    pub span: Span,
101    pub content: RawTerminator,
102    /// Comments that precede this terminator.
103    // This is filled in a late pass after all the control-flow manipulation.
104    #[drive(skip)]
105    pub comments_before: Vec<String>,
106}
107
108#[derive(Debug, Clone, Serialize, Deserialize, Drive, DriveMut)]
109#[charon::rename("Block")]
110pub struct BlockData {
111    pub statements: Vec<Statement>,
112    pub terminator: Terminator,
113}