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