charon_lib/ast/
gast.rs

1//! Definitions common to [crate::ullbc_ast] and [crate::llbc_ast]
2use crate::ast::*;
3use crate::ids::Vector;
4use crate::llbc_ast;
5use crate::ullbc_ast;
6use derive_generic_visitor::{Drive, DriveMut};
7use indexmap::IndexMap;
8use macros::{EnumIsA, EnumToGetters};
9use serde::{Deserialize, Serialize};
10
11/// A variable
12#[derive(Debug, Clone, Serialize, Deserialize, Drive, DriveMut)]
13pub struct Local {
14    /// Unique index identifying the variable
15    pub index: LocalId,
16    /// Variable name - may be `None` if the variable was introduced by Rust
17    /// through desugaring.
18    #[drive(skip)]
19    pub name: Option<String>,
20    /// The variable type
21    #[charon::rename("var_ty")]
22    pub ty: Ty,
23}
24#[deprecated(note = "use `Local` intead")]
25pub type Var = Local;
26#[deprecated(note = "use `LocalId` intead")]
27pub type VarId = LocalId;
28
29/// Marker to indicate that a declaration is opaque (i.e. we don't inspect its body).
30#[derive(Debug, Copy, Clone, Serialize, Deserialize, Drive, DriveMut)]
31pub struct Opaque;
32
33/// The local variables of a body.
34#[derive(Debug, Default, Clone, Serialize, Deserialize, Drive, DriveMut)]
35pub struct Locals {
36    /// The number of local variables used for the input arguments.
37    #[drive(skip)]
38    pub arg_count: usize,
39    /// The local variables.
40    /// We always have, in the following order:
41    /// - the local used for the return value (index 0)
42    /// - the `arg_count` input arguments
43    /// - the remaining locals, used for the intermediate computations
44    pub locals: Vector<LocalId, Local>,
45}
46
47/// An expression body.
48/// TODO: arg_count should be stored in GFunDecl below. But then,
49///       the print is obfuscated and Aeneas may need some refactoring.
50#[derive(Debug, Clone, Serialize, Deserialize, Drive, DriveMut)]
51#[charon::rename("GexprBody")]
52pub struct GExprBody<T> {
53    pub span: Span,
54    /// The local variables.
55    pub locals: Locals,
56    /// For each line inside the body, we record any whole-line `//` comments found before it. They
57    /// are added to statements in the late `recover_body_comments` pass.
58    #[charon::opaque]
59    #[drive(skip)]
60    pub comments: Vec<(usize, Vec<String>)>,
61    pub body: T,
62}
63
64/// The body of a function or a constant.
65#[derive(Debug, Clone, Serialize, Deserialize, Drive, DriveMut, EnumIsA, EnumToGetters)]
66pub enum Body {
67    /// Body represented as a CFG. This is what ullbc is made of, and what we get after translating MIR.
68    Unstructured(ullbc_ast::ExprBody),
69    /// Body represented with structured control flow. This is what llbc is made of. We restructure
70    /// the control flow in `ullbc_to_llbc`.
71    Structured(llbc_ast::ExprBody),
72}
73
74/// Item kind: whether this function/const is part of a trait declaration, trait implementation, or
75/// neither.
76///
77/// Example:
78/// ```text
79/// trait Foo {
80///     fn bar(x : u32) -> u32; // trait item decl without default
81///
82///     fn baz(x : bool) -> bool { x } // trait item decl with default
83/// }
84///
85/// impl Foo for ... {
86///     fn bar(x : u32) -> u32 { x } // trait item implementation
87/// }
88///
89/// fn test(...) { ... } // regular
90///
91/// impl Type {
92///     fn test(...) { ... } // regular
93/// }
94/// ```
95#[derive(Debug, Clone, Serialize, Deserialize, Drive, DriveMut, PartialEq, Eq)]
96#[charon::variants_suffix("Item")]
97pub enum ItemKind {
98    /// This item stands on its own.
99    TopLevel,
100    /// This is a closure in a function body.
101    Closure { info: ClosureInfo },
102    /// This is an associated item in a trait declaration. It has a body if and only if the trait
103    /// provided a default implementation.
104    TraitDecl {
105        /// The trait declaration this item belongs to.
106        trait_ref: TraitDeclRef,
107        /// The name of the item.
108        // TODO: also include method generics so we can recover a full `FnPtr::TraitMethod`
109        #[drive(skip)]
110        item_name: TraitItemName,
111        /// Whether the trait declaration provides a default implementation.
112        #[drive(skip)]
113        has_default: bool,
114    },
115    /// This is an associated item in a trait implementation.
116    TraitImpl {
117        /// The trait implementation the method belongs to.
118        impl_ref: TraitImplRef,
119        /// The trait declaration that the impl block implements.
120        trait_ref: TraitDeclRef,
121        /// The name of the item
122        // TODO: also include method generics so we can recover a full `FnPtr::TraitMethod`
123        #[drive(skip)]
124        item_name: TraitItemName,
125        /// True if the trait decl had a default implementation for this function/const and this
126        /// item is a copy of the default item.
127        #[drive(skip)]
128        reuses_default: bool,
129    },
130    /// This is a vtable struct for a trait.
131    VTableTy {
132        /// The `dyn Trait` predicate implemented by this vtable.
133        dyn_predicate: DynPredicate,
134    },
135    /// This is a vtable value for an impl.
136    VTableInstance { impl_ref: TraitImplRef },
137}
138
139/// A function definition
140#[derive(Debug, Clone, Serialize, Deserialize, Drive, DriveMut)]
141pub struct FunDecl {
142    #[drive(skip)]
143    pub def_id: FunDeclId,
144    /// The meta data associated with the declaration.
145    pub item_meta: ItemMeta,
146    /// The signature contains the inputs/output types *with* non-erased regions.
147    /// It also contains the list of region and type parameters.
148    pub signature: FunSig,
149    /// The function kind: "regular" function, trait method declaration, etc.
150    pub kind: ItemKind,
151    /// Whether this function is in fact the body of a constant/static that we turned into an
152    /// initializer function.
153    pub is_global_initializer: Option<GlobalDeclId>,
154    /// The function body, unless the function is opaque.
155    /// Opaque functions are: external functions, or local functions tagged
156    /// as opaque.
157    pub body: Result<Body, Opaque>,
158}
159
160/// Reference to a function declaration.
161#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash, Drive, DriveMut)]
162pub struct FunDeclRef {
163    pub id: FunDeclId,
164    /// Generic arguments passed to the function.
165    pub generics: BoxedArgs,
166}
167
168#[derive(Debug, Clone, Serialize, Deserialize, Drive, DriveMut)]
169pub enum GlobalKind {
170    /// A static.
171    Static,
172    /// A const with a name (either top-level or an associated const in a trait).
173    NamedConst,
174    /// A const without a name:
175    /// - An inline const expression (`const { 1 + 1 }`);
176    /// - A const expression in a type (`[u8; sizeof::<T>()]`);
177    /// - A promoted constant, automatically lifted from a body (`&0`).
178    AnonConst,
179}
180
181/// A global variable definition (constant or static).
182#[derive(Debug, Clone, Serialize, Deserialize, Drive, DriveMut)]
183pub struct GlobalDecl {
184    #[drive(skip)]
185    pub def_id: GlobalDeclId,
186    /// The meta data associated with the declaration.
187    pub item_meta: ItemMeta,
188    pub generics: GenericParams,
189    pub ty: Ty,
190    /// The context of the global: distinguishes top-level items from trait-associated items.
191    pub kind: ItemKind,
192    /// The kind of global (static or const).
193    #[drive(skip)]
194    pub global_kind: GlobalKind,
195    /// The initializer function used to compute the initial value for this constant/static. It
196    /// uses the same generic parameters as the global.
197    #[charon::rename("body")]
198    pub init: FunDeclId,
199}
200
201/// Reference to a global declaration.
202#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash, Drive, DriveMut)]
203pub struct GlobalDeclRef {
204    pub id: GlobalDeclId,
205    pub generics: BoxedArgs,
206}
207
208#[derive(
209    Debug, Clone, Serialize, Deserialize, Drive, DriveMut, PartialEq, Eq, Hash, PartialOrd, Ord,
210)]
211#[drive(skip)]
212pub struct TraitItemName(pub String);
213
214/// A trait **declaration**.
215///
216/// For instance:
217/// ```text
218/// trait Foo {
219///   type Bar;
220///
221///   fn baz(...); // required method (see below)
222///
223///   fn test() -> bool { true } // provided method (see below)
224/// }
225/// ```
226///
227/// In case of a trait declaration, we don't include the provided methods (the methods
228/// with a default implementation): they will be translated on a per-need basis. This is
229/// important for two reasons:
230/// - this makes the trait definitions a lot smaller (the Iterator trait
231///   has *one* declared function and more than 70 provided functions)
232/// - this is important for the external traits, whose provided methods
233///   often use features we don't support yet
234///
235/// Remark:
236/// In Aeneas, we still translate the provided methods on an individual basis,
237/// and in such a way thay they take as input a trait instance. This means that
238/// we can use default methods *but*:
239/// - implementations of required methods shoudln't call default methods
240/// - trait implementations shouldn't redefine required methods
241/// The use case we have in mind is [std::iter::Iterator]: it declares one required
242/// method (`next`) that should be implemented for every iterator, and defines many
243/// helpers like `all`, `map`, etc. that shouldn't be re-implemented.
244/// Of course, this forbids other useful use cases such as visitors implemented
245/// by means of traits.
246#[allow(clippy::type_complexity)]
247#[derive(Debug, Clone, Serialize, Deserialize, Drive, DriveMut)]
248pub struct TraitDecl {
249    #[drive(skip)]
250    pub def_id: TraitDeclId,
251    pub item_meta: ItemMeta,
252    pub generics: GenericParams,
253    /// The "parent" clauses: the supertraits.
254    ///
255    /// Supertraits are actually regular where clauses, but we decided to have
256    /// a custom treatment.
257    /// ```text
258    /// trait Foo : Bar {
259    ///             ^^^
260    ///         supertrait, that we treat as a parent predicate
261    /// }
262    /// ```
263    /// TODO: actually, as of today, we consider that all trait clauses of
264    /// trait declarations are parent clauses.
265    pub parent_clauses: Vector<TraitClauseId, TraitClause>,
266    /// The associated constants declared in the trait, along with their type.
267    pub consts: Vec<(TraitItemName, Ty)>,
268    /// Records associated constants that have a default value.
269    #[charon::opaque]
270    pub const_defaults: IndexMap<TraitItemName, GlobalDeclRef>,
271    /// The associated types declared in the trait.
272    pub types: Vec<TraitItemName>,
273    /// Records associated types that have a default value.
274    #[charon::opaque]
275    pub type_defaults: IndexMap<TraitItemName, Ty>,
276    /// List of trait clauses that apply to each associated type. This is used during translation,
277    /// but the `lift_associated_item_clauses` pass moves them to be parent clauses later. Hence
278    /// this is empty after that pass.
279    /// TODO: Do this as we translate to avoid the need to store this vector.
280    #[charon::opaque]
281    pub type_clauses: Vec<(TraitItemName, Vector<TraitClauseId, TraitClause>)>,
282    /// The methods declared by the trait. The signature of the methods can be found in each
283    /// corresponding `FunDecl`. These `FunDecl` may have a body if the trait provided a default
284    /// implementation for that method; otherwise it has an `Opaque` body.
285    ///
286    /// The binder contains the type parameters specific to the method. The `FunDeclRef` then
287    /// provides a full list of arguments to the pointed-to function.
288    pub methods: Vec<(TraitItemName, Binder<FunDeclRef>)>,
289    /// The virtual table struct for this trait, if it has one.
290    /// It is guaranteed that the trait has a vtable iff it is dyn-compatible.
291    pub vtable: Option<TypeDeclRef>,
292}
293
294/// A trait **implementation**.
295///
296/// For instance:
297/// ```text
298/// impl Foo for List {
299///   type Bar = ...
300///
301///   fn baz(...) { ... }
302/// }
303/// ```
304#[derive(Debug, Clone, Serialize, Deserialize, Drive, DriveMut)]
305pub struct TraitImpl {
306    #[drive(skip)]
307    pub def_id: TraitImplId,
308    pub item_meta: ItemMeta,
309    /// The information about the implemented trait.
310    /// Note that this contains the instantiation of the "parent"
311    /// clauses.
312    pub impl_trait: TraitDeclRef,
313    pub generics: GenericParams,
314    /// The trait references for the parent clauses (see [TraitDecl]).
315    pub parent_trait_refs: Vector<TraitClauseId, TraitRef>,
316    /// The associated constants declared in the trait.
317    pub consts: Vec<(TraitItemName, GlobalDeclRef)>,
318    /// The associated types declared in the trait.
319    pub types: Vec<(TraitItemName, Ty)>,
320    /// The `Vec` corresponds to the same `Vector` in `TraitDecl`. In the same way, this is
321    /// empty after the `lift_associated_item_clauses` pass.
322    #[charon::opaque]
323    pub type_clauses: Vec<(TraitItemName, Vector<TraitClauseId, TraitRef>)>,
324    /// The implemented methods
325    pub methods: Vec<(TraitItemName, Binder<FunDeclRef>)>,
326    /// The virtual table instance for this trait implementation. This is `Some` iff the trait is
327    /// dyn-compatible.
328    pub vtable: Option<GlobalDeclRef>,
329}
330
331/// A function operand is used in function calls.
332/// It either designates a top-level function, or a place in case
333/// we are using function pointers stored in local variables.
334#[derive(Debug, Clone, Serialize, Deserialize, Drive, DriveMut)]
335#[charon::variants_prefix("FnOp")]
336pub enum FnOperand {
337    /// Regular case: call to a top-level function, trait method, etc.
338    Regular(FnPtr),
339    /// Use of a function pointer stored in a local variable
340    Move(Place),
341}
342
343#[derive(Debug, Clone, Serialize, Deserialize, Drive, DriveMut)]
344pub struct Call {
345    pub func: FnOperand,
346    pub args: Vec<Operand>,
347    pub dest: Place,
348}
349
350#[derive(Debug, Clone, Serialize, Deserialize, Drive, DriveMut)]
351pub struct CopyNonOverlapping {
352    pub src: Operand,
353    pub dst: Operand,
354    pub count: Operand,
355}
356
357/// (U)LLBC is a language with side-effects: a statement may abort in a way that isn't tracked by
358/// control-flow. The two kinds of abort are:
359/// - Panic (may unwind or not depending on compilation setting);
360/// - Undefined behavior:
361#[derive(Debug, Clone, Serialize, Deserialize, Drive, DriveMut)]
362pub enum AbortKind {
363    /// A built-in panicking function.
364    Panic(Option<Name>),
365    /// Undefined behavior in the rust abstract machine.
366    UndefinedBehavior,
367    /// Unwind had to stop for Abi reasons or because cleanup code panicked again.
368    UnwindTerminate,
369}
370
371/// Check the value of an operand and abort if the value is not expected. This is introduced to
372/// avoid a lot of small branches.
373///
374/// We translate MIR asserts (introduced for out-of-bounds accesses or divisions by zero for
375/// instance) to this. We then eliminate them in [crate::transform::remove_dynamic_checks],
376/// because they're implicit in the semantics of our array accesses etc. Finally we introduce new asserts in
377/// [crate::transform::reconstruct_asserts].
378#[derive(Debug, Clone, Serialize, Deserialize, Drive, DriveMut)]
379#[charon::rename("Assertion")]
380pub struct Assert {
381    pub cond: Operand,
382    /// The value that the operand should evaluate to for the assert to succeed.
383    #[drive(skip)]
384    pub expected: bool,
385    /// What kind of abort happens on assert failure.
386    pub on_failure: AbortKind,
387}