Skip to main content

charon_lib/ast/
llbc_ast.rs

1//! LLBC
2//!
3//! MIR code where we have rebuilt the control-flow (`if ... then ... else ...`,
4//! `while ...`, ...).
5//!
6//! Also note that we completely break the definitions Statement and Terminator
7//! from MIR to use Statement only.
8
9pub use super::llbc_ast_utils::*;
10pub use crate::ast::*;
11use derive_generic_visitor::{Drive, DriveMut};
12use macros::{EnumAsGetters, EnumIsA, EnumToGetters, VariantIndexArity, VariantName};
13use serde_state::{DeserializeState, SerializeState};
14
15generate_index_type!(StatementId);
16
17/// A raw statement: a statement without meta data.
18#[derive(
19    Debug,
20    PartialEq,
21    Eq,
22    Clone,
23    EnumIsA,
24    EnumToGetters,
25    EnumAsGetters,
26    SerializeState,
27    DeserializeState,
28    Drive,
29    DriveMut,
30)]
31pub enum StatementKind {
32    /// Assigns an `Rvalue` to a `Place`. e.g. `let y = x;` could become
33    /// `y := move x` which is represented as `Assign(y, Rvalue::Use(Operand::Move(x)))`.
34    Assign(Place, Rvalue),
35    /// Not used today because we take MIR built.
36    SetDiscriminant(Place, VariantId),
37    /// Equivalent to std::intrinsics::copy_nonoverlapping; this is not modelled as a function
38    /// call as it cannot diverge
39    CopyNonOverlapping(Box<CopyNonOverlapping>),
40    /// Indicates that this local should be allocated; if it is already allocated, this frees
41    /// the local and re-allocates it. The return value and arguments do not receive a
42    /// `StorageLive`. We ensure in the micro-pass `insert_storage_lives` that all other locals
43    /// have a `StorageLive` associated with them.
44    StorageLive(LocalId),
45    /// Indicates that this local should be deallocated; if it is already deallocated, this is
46    /// a no-op. A local may not have a `StorageDead` in the function's body, in which case it
47    /// is implicitly deallocated at the end of the function.
48    StorageDead(LocalId),
49    /// A place is mentioned, but not accessed. The place itself must still be valid though, so
50    /// this statement is not a no-op: it can trigger UB if the place's projections are not valid
51    /// (e.g. because they go out of bounds).
52    PlaceMention(Place),
53    /// Drop the value at the given place.
54    ///
55    /// Depending on `DropKind`, this may be a real call to `drop_glue`, or a conditional call
56    /// that should only happen if the place has not been moved out of. See the docs of `DropKind`
57    /// for more details; to get precise drops use `--precise-drops`.
58    Drop(Place, FnPtr, #[drive(skip)] DropKind),
59    Assert {
60        assert: Assert,
61        on_failure: AbortKind,
62    },
63    /// An inline assembly block. For now we only preserve the template string.
64    InlineAsm {
65        asm: String,
66        targets: Vec<Block>,
67    },
68    Call(Call),
69    /// Panic also handles "unreachable". We keep the name of the panicking function that was
70    /// called.
71    Abort(AbortKind),
72    Return,
73    /// Break to outer loops.
74    /// The `usize` gives the index of the outer loop to break to:
75    /// * 0: break to first outer loop (the current loop)
76    /// * 1: break to second outer loop
77    /// * ...
78    #[drive(skip)]
79    Break(usize),
80    /// Continue to outer loops.
81    /// The `usize` gives the index of the outer loop to continue to:
82    /// * 0: continue to first outer loop (the current loop)
83    /// * 1: continue to second outer loop
84    /// * ...
85    #[drive(skip)]
86    Continue(usize),
87    /// No-op.
88    Nop,
89    Switch(Switch),
90    Loop(Block),
91    #[drive(skip)]
92    Error(String),
93}
94
95#[derive(Debug, Eq, Clone, SerializeState, DeserializeState, Drive, DriveMut)]
96pub struct Statement {
97    pub span: Span,
98    /// Integer uniquely identifying this statement among the statmeents in the current body. To
99    /// simplify things we generate globally-fresh ids when creating a new `Statement`.
100    #[cfg_attr(feature = "charon_on_charon", charon::rename("statement_id"))]
101    pub id: StatementId,
102    pub kind: StatementKind,
103    /// Comments that precede this statement.
104    // This is filled in a late pass after all the control-flow manipulation.
105    #[drive(skip)]
106    pub comments_before: Vec<String>,
107}
108
109/// Ignores statement ids.
110impl PartialEq for Statement {
111    fn eq(&self, other: &Self) -> bool {
112        self.span == other.span
113            && self.kind == other.kind
114            && self.comments_before == other.comments_before
115    }
116}
117
118#[derive(Debug, PartialEq, Eq, Clone, SerializeState, DeserializeState, Drive, DriveMut)]
119#[serde_state(state_implements = HashConsSerializerState)] // Avoid corecursive impls due to perfect derive
120pub struct Block {
121    pub span: Span,
122    pub statements: Vec<Statement>,
123}
124
125#[derive(
126    Debug,
127    PartialEq,
128    Eq,
129    Clone,
130    EnumIsA,
131    EnumToGetters,
132    EnumAsGetters,
133    SerializeState,
134    DeserializeState,
135    Drive,
136    DriveMut,
137    VariantName,
138    VariantIndexArity,
139)]
140pub enum Switch {
141    /// Gives the `if` block and the `else` block. The `Operand` is the condition of the `if`, e.g. `if (y == 0)` could become
142    /// ```text
143    /// v@3 := copy y; // Represented as `Assign(v@3, Use(Copy(y))`
144    /// v@2 := move v@3 == 0; // Represented as `Assign(v@2, BinOp(BinOp::Eq, Move(y), Const(0)))`
145    /// if (move v@2) { // Represented as `If(Move(v@2), <then branch>, <else branch>)`
146    /// ```
147    If(Operand, Block, Block),
148    /// Gives the integer type, a map linking values to switch branches, and the
149    /// otherwise block. Note that matches over enumerations are performed by
150    /// switching over the discriminant, which is an integer.
151    /// Also, we use a `Vec` to make sure the order of the switch
152    /// branches is preserved.
153    ///
154    /// Rk.: we use a vector of values, because some of the branches may
155    /// be grouped together, like for the following code:
156    /// ```text
157    /// match e {
158    ///   E::V1 | E::V2 => ..., // Grouped
159    ///   E::V3 => ...
160    /// }
161    /// ```
162    SwitchInt(Operand, LiteralTy, Vec<(Vec<Literal>, Block)>, Block),
163    /// A match over an ADT.
164    ///
165    /// The match statement is introduced in [crate::transform::resugar::reconstruct_matches]
166    /// (whenever we find a discriminant read, we merge it with the subsequent
167    /// switch into a match).
168    Match(Place, Vec<(Vec<VariantId>, Block)>, Option<Block>),
169}
170
171pub type ExprBody = GExprBody<Block>;