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