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::{Deserialize, Serialize};
14
15/// A raw statement: a statement without meta data.
16#[derive(
17 Debug, Clone, EnumIsA, EnumToGetters, EnumAsGetters, Serialize, Deserialize, Drive, DriveMut,
18)]
19pub enum RawStatement {
20 /// Assigns an `Rvalue` to a `Place`. e.g. `let y = x;` could become
21 /// `y := move x` which is represented as `Assign(y, Rvalue::Use(Operand::Move(x)))`.
22 Assign(Place, Rvalue),
23 /// Not used today because we take MIR built.
24 SetDiscriminant(Place, VariantId),
25 /// Indicates that this local should be allocated; if it is already allocated, this frees
26 /// the local and re-allocates it. The return value and arguments do not receive a
27 /// `StorageLive`. We ensure in the micro-pass `insert_storage_lives` that all other locals
28 /// have a `StorageLive` associated with them.
29 StorageLive(LocalId),
30 /// Indicates that this local should be deallocated; if it is already deallocated, this is
31 /// a no-op. A local may not have a `StorageDead` in the function's body, in which case it
32 /// is implicitly deallocated at the end of the function.
33 StorageDead(LocalId),
34 Deinit(Place),
35 Drop(Place),
36 Assert(Assert),
37 Call(Call),
38 /// Panic also handles "unreachable". We keep the name of the panicking function that was
39 /// called.
40 Abort(AbortKind),
41 Return,
42 /// Break to outer loops.
43 /// The `usize` gives the index of the outer loop to break to:
44 /// * 0: break to first outer loop (the current loop)
45 /// * 1: break to second outer loop
46 /// * ...
47 #[drive(skip)]
48 Break(usize),
49 /// Continue to outer loops.
50 /// The `usize` gives the index of the outer loop to continue to:
51 /// * 0: continue to first outer loop (the current loop)
52 /// * 1: continue to second outer loop
53 /// * ...
54 #[drive(skip)]
55 Continue(usize),
56 /// No-op.
57 Nop,
58 Switch(Switch),
59 Loop(Block),
60 #[drive(skip)]
61 Error(String),
62}
63
64#[derive(Debug, Clone, Serialize, Deserialize, Drive, DriveMut)]
65pub struct Statement {
66 pub span: Span,
67 pub content: RawStatement,
68 /// Comments that precede this statement.
69 // This is filled in a late pass after all the control-flow manipulation.
70 #[drive(skip)]
71 pub comments_before: Vec<String>,
72}
73
74#[derive(Debug, Clone, Serialize, Deserialize, Drive, DriveMut)]
75pub struct Block {
76 pub span: Span,
77 pub statements: Vec<Statement>,
78}
79
80#[derive(
81 Debug,
82 Clone,
83 EnumIsA,
84 EnumToGetters,
85 EnumAsGetters,
86 Serialize,
87 Deserialize,
88 Drive,
89 DriveMut,
90 VariantName,
91 VariantIndexArity,
92)]
93pub enum Switch {
94 /// Gives the `if` block and the `else` block. The `Operand` is the condition of the `if`, e.g. `if (y == 0)` could become
95 /// ```rust,ignore
96 /// v@3 := copy y; // Represented as `Assign(v@3, Use(Copy(y))`
97 /// v@2 := move v@3 == 0; // Represented as `Assign(v@2, BinOp(BinOp::Eq, Move(y), Const(0)))`
98 /// if (move v@2) { // Represented as `If(Move(v@2), <then branch>, <else branch>)`
99 /// ```
100 If(Operand, Block, Block),
101 /// Gives the integer type, a map linking values to switch branches, and the
102 /// otherwise block. Note that matches over enumerations are performed by
103 /// switching over the discriminant, which is an integer.
104 /// Also, we use a `Vec` to make sure the order of the switch
105 /// branches is preserved.
106 ///
107 /// Rk.: we use a vector of values, because some of the branches may
108 /// be grouped together, like for the following code:
109 /// ```text
110 /// match e {
111 /// E::V1 | E::V2 => ..., // Grouped
112 /// E::V3 => ...
113 /// }
114 /// ```
115 SwitchInt(Operand, IntegerTy, Vec<(Vec<ScalarValue>, Block)>, Block),
116 /// A match over an ADT.
117 ///
118 /// The match statement is introduced in [crate::remove_read_discriminant]
119 /// (whenever we find a discriminant read, we merge it with the subsequent
120 /// switch into a match).
121 Match(Place, Vec<(Vec<VariantId>, Block)>, Option<Block>),
122}
123
124pub type ExprBody = GExprBody<Block>;