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 macros::EnumAsGetters;
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("local_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/// The local variables of a body.
30#[derive(Debug, Default, Clone, Serialize, Deserialize, Drive, DriveMut)]
31pub struct Locals {
32    /// The number of local variables used for the input arguments.
33    #[drive(skip)]
34    pub arg_count: usize,
35    /// The local variables.
36    /// We always have, in the following order:
37    /// - the local used for the return value (index 0)
38    /// - the `arg_count` input arguments
39    /// - the remaining locals, used for the intermediate computations
40    pub locals: Vector<LocalId, Local>,
41}
42
43/// An expression body.
44/// TODO: arg_count should be stored in GFunDecl below. But then,
45///       the print is obfuscated and Aeneas may need some refactoring.
46#[derive(Debug, Clone, Serialize, Deserialize, Drive, DriveMut)]
47#[charon::rename("GexprBody")]
48pub struct GExprBody<T> {
49    pub span: Span,
50    /// The local variables.
51    pub locals: Locals,
52    /// For each line inside the body, we record any whole-line `//` comments found before it. They
53    /// are added to statements in the late `recover_body_comments` pass.
54    #[charon::opaque]
55    #[drive(skip)]
56    pub comments: Vec<(usize, Vec<String>)>,
57    pub body: T,
58}
59
60/// The body of a function.
61#[derive(
62    Debug, Clone, Serialize, Deserialize, Drive, DriveMut, EnumIsA, EnumAsGetters, EnumToGetters,
63)]
64pub enum Body {
65    /// Body represented as a CFG. This is what ullbc is made of, and what we get after translating MIR.
66    Unstructured(ullbc_ast::ExprBody),
67    /// Body represented with structured control flow. This is what llbc is made of. We restructure
68    /// the control flow in the `ullbc_to_llbc` pass.
69    Structured(llbc_ast::ExprBody),
70    /// The body of the function item we add for each trait method declaration, if the trait
71    /// doesn't provide a default for that method.
72    TraitMethodWithoutDefault,
73    /// A body that the user chose not to translate, based on opacity settings like
74    /// `--include`/`--opaque`.
75    Opaque,
76    /// A body that was not available. Typically that's function bodies for non-generic and
77    /// non-inlineable std functions, as these are not present in the compiled standard library
78    /// `.rmeta` file shipped with a rust toolchain.
79    Missing,
80    /// We encountered an error while translating this body.
81    #[drive(skip)]
82    Error(Error),
83}
84
85/// Item kind: whether this function/const is part of a trait declaration, trait implementation, or
86/// neither.
87///
88/// Example:
89/// ```text
90/// trait Foo {
91///     fn bar(x : u32) -> u32; // trait item decl without default
92///
93///     fn baz(x : bool) -> bool { x } // trait item decl with default
94/// }
95///
96/// impl Foo for ... {
97///     fn bar(x : u32) -> u32 { x } // trait item implementation
98/// }
99///
100/// fn test(...) { ... } // regular
101///
102/// impl Type {
103///     fn test(...) { ... } // regular
104/// }
105/// ```
106#[derive(Debug, Clone, Serialize, Deserialize, Drive, DriveMut, PartialEq, Eq)]
107#[charon::variants_suffix("Item")]
108pub enum ItemSource {
109    /// This item stands on its own.
110    TopLevel,
111    /// This is a closure in a function body.
112    Closure { info: ClosureInfo },
113    /// This is an associated item in a trait declaration. It has a body if and only if the trait
114    /// provided a default implementation.
115    TraitDecl {
116        /// The trait declaration this item belongs to.
117        trait_ref: TraitDeclRef,
118        /// The name of the item.
119        // TODO: also include method generics so we can recover a full `FnPtr::TraitMethod`
120        #[drive(skip)]
121        item_name: TraitItemName,
122        /// Whether the trait declaration provides a default implementation.
123        #[drive(skip)]
124        has_default: bool,
125    },
126    /// This is an associated item in a trait implementation.
127    TraitImpl {
128        /// The trait implementation the method belongs to.
129        impl_ref: TraitImplRef,
130        /// The trait declaration that the impl block implements.
131        trait_ref: TraitDeclRef,
132        /// The name of the item
133        // TODO: also include method generics so we can recover a full `FnPtr::TraitMethod`
134        #[drive(skip)]
135        item_name: TraitItemName,
136        /// True if the trait decl had a default implementation for this function/const and this
137        /// item is a copy of the default item.
138        #[drive(skip)]
139        reuses_default: bool,
140    },
141    /// This is a vtable struct for a trait.
142    VTableTy {
143        /// The `dyn Trait` predicate implemented by this vtable.
144        dyn_predicate: DynPredicate,
145    },
146    /// This is a vtable value for an impl.
147    VTableInstance { impl_ref: TraitImplRef },
148    /// The method shim wraps a concrete implementation of a method into a function that takes `dyn
149    /// Trait` as its `Self` type. This shim casts the receiver to the known concrete type and
150    /// calls the real method.
151    VTableMethodShim,
152}
153
154/// A function definition
155#[derive(Debug, Clone, Serialize, Deserialize, Drive, DriveMut)]
156pub struct FunDecl {
157    #[drive(skip)]
158    pub def_id: FunDeclId,
159    /// The meta data associated with the declaration.
160    pub item_meta: ItemMeta,
161    /// The signature contains the inputs/output types *with* non-erased regions.
162    /// It also contains the list of region and type parameters.
163    pub signature: FunSig,
164    /// The function kind: "regular" function, trait method declaration, etc.
165    pub src: ItemSource,
166    /// Whether this function is in fact the body of a constant/static that we turned into an
167    /// initializer function.
168    pub is_global_initializer: Option<GlobalDeclId>,
169    /// The function body, unless the function is opaque.
170    /// Opaque functions are: external functions, or local functions tagged
171    /// as opaque.
172    pub body: Body,
173}
174
175/// Reference to a function declaration.
176#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash, Drive, DriveMut)]
177pub struct FunDeclRef {
178    pub id: FunDeclId,
179    /// Generic arguments passed to the function.
180    pub generics: BoxedArgs,
181}
182
183#[derive(Debug, Clone, Serialize, Deserialize, Drive, DriveMut)]
184pub enum GlobalKind {
185    /// A static.
186    Static,
187    /// A const with a name (either top-level or an associated const in a trait).
188    NamedConst,
189    /// A const without a name:
190    /// - An inline const expression (`const { 1 + 1 }`);
191    /// - A const expression in a type (`[u8; sizeof::<T>()]`);
192    /// - A promoted constant, automatically lifted from a body (`&0`).
193    AnonConst,
194}
195
196/// A global variable definition (constant or static).
197#[derive(Debug, Clone, Serialize, Deserialize, Drive, DriveMut)]
198pub struct GlobalDecl {
199    #[drive(skip)]
200    pub def_id: GlobalDeclId,
201    /// The meta data associated with the declaration.
202    pub item_meta: ItemMeta,
203    pub generics: GenericParams,
204    pub ty: Ty,
205    /// The context of the global: distinguishes top-level items from trait-associated items.
206    pub src: ItemSource,
207    /// The kind of global (static or const).
208    #[drive(skip)]
209    pub global_kind: GlobalKind,
210    /// The initializer function used to compute the initial value for this constant/static. It
211    /// uses the same generic parameters as the global.
212    pub init: FunDeclId,
213}
214
215/// Reference to a global declaration.
216#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash, Drive, DriveMut)]
217pub struct GlobalDeclRef {
218    pub id: GlobalDeclId,
219    pub generics: BoxedArgs,
220}
221
222#[derive(
223    Debug,
224    Clone,
225    Copy,
226    Serialize,
227    Deserialize,
228    Drive,
229    DriveMut,
230    PartialEq,
231    Eq,
232    Hash,
233    PartialOrd,
234    Ord,
235)]
236#[drive(skip)]
237pub struct TraitItemName(pub ustr::Ustr);
238
239/// A trait **declaration**.
240///
241/// For instance:
242/// ```text
243/// trait Foo {
244///   type Bar;
245///
246///   fn baz(...); // required method (see below)
247///
248///   fn test() -> bool { true } // provided method (see below)
249/// }
250/// ```
251///
252/// In case of a trait declaration, we don't include the provided methods (the methods
253/// with a default implementation): they will be translated on a per-need basis. This is
254/// important for two reasons:
255/// - this makes the trait definitions a lot smaller (the Iterator trait
256///   has *one* declared function and more than 70 provided functions)
257/// - this is important for the external traits, whose provided methods
258///   often use features we don't support yet
259///
260/// Remark:
261/// In Aeneas, we still translate the provided methods on an individual basis,
262/// and in such a way thay they take as input a trait instance. This means that
263/// we can use default methods *but*:
264/// - implementations of required methods shoudln't call default methods
265/// - trait implementations shouldn't redefine required methods
266/// The use case we have in mind is [std::iter::Iterator]: it declares one required
267/// method (`next`) that should be implemented for every iterator, and defines many
268/// helpers like `all`, `map`, etc. that shouldn't be re-implemented.
269/// Of course, this forbids other useful use cases such as visitors implemented
270/// by means of traits.
271#[allow(clippy::type_complexity)]
272#[derive(Debug, Clone, Serialize, Deserialize, Drive, DriveMut)]
273pub struct TraitDecl {
274    #[drive(skip)]
275    pub def_id: TraitDeclId,
276    pub item_meta: ItemMeta,
277    pub generics: GenericParams,
278    /// The "parent" clauses: the supertraits.
279    ///
280    /// Supertraits are actually regular where clauses, but we decided to have
281    /// a custom treatment.
282    /// ```text
283    /// trait Foo : Bar {
284    ///             ^^^
285    ///         supertrait, that we treat as a parent predicate
286    /// }
287    /// ```
288    /// TODO: actually, as of today, we consider that all trait clauses of
289    /// trait declarations are parent clauses.
290    pub implied_clauses: Vector<TraitClauseId, TraitParam>,
291    /// The associated constants declared in the trait.
292    pub consts: Vec<TraitAssocConst>,
293    /// The associated types declared in the trait. The binder binds the generic parameters of the
294    /// type if it is a GAT (Generic Associated Type). For a plain associated type the binder binds
295    /// nothing.
296    pub types: Vec<Binder<TraitAssocTy>>,
297    /// The methods declared by the trait. The binder binds the generic parameters of the method.
298    ///
299    /// ```rust
300    /// trait Trait<T> {
301    ///   // The `Binder` for this method binds `'a` and `U`.
302    ///   fn method<'a, U>(x: &'a U);
303    /// }
304    /// ```
305    pub methods: Vec<Binder<TraitMethod>>,
306    /// The virtual table struct for this trait, if it has one.
307    /// It is guaranteed that the trait has a vtable iff it is dyn-compatible.
308    pub vtable: Option<TypeDeclRef>,
309}
310
311/// An associated constant in a trait.
312#[derive(Debug, Clone, Serialize, Deserialize, Drive, DriveMut)]
313pub struct TraitAssocConst {
314    pub name: TraitItemName,
315    pub ty: Ty,
316    pub default: Option<GlobalDeclRef>,
317}
318
319/// An associated type in a trait.
320#[derive(Debug, Clone, Serialize, Deserialize, Drive, DriveMut)]
321pub struct TraitAssocTy {
322    pub name: TraitItemName,
323    pub default: Option<Ty>,
324    /// List of trait clauses that apply to this type.
325    pub implied_clauses: Vector<TraitClauseId, TraitParam>,
326}
327
328/// A trait method.
329#[derive(Debug, Clone, Serialize, Deserialize, Drive, DriveMut)]
330pub struct TraitMethod {
331    pub name: TraitItemName,
332    /// Each method declaration is represented by a function item. That function contains the
333    /// signature of the method as well as information like attributes. It has a body iff the
334    /// method declaration has a default implementation; otherwise it has an `Opaque` body.
335    pub item: FunDeclRef,
336}
337
338/// A trait **implementation**.
339///
340/// For instance:
341/// ```text
342/// impl Foo for List {
343///   type Bar = ...
344///
345///   fn baz(...) { ... }
346/// }
347/// ```
348#[derive(Debug, Clone, Serialize, Deserialize, Drive, DriveMut)]
349pub struct TraitImpl {
350    #[drive(skip)]
351    pub def_id: TraitImplId,
352    pub item_meta: ItemMeta,
353    /// The information about the implemented trait.
354    /// Note that this contains the instantiation of the "parent"
355    /// clauses.
356    pub impl_trait: TraitDeclRef,
357    pub generics: GenericParams,
358    /// The trait references for the parent clauses (see [TraitDecl]).
359    pub implied_trait_refs: Vector<TraitClauseId, TraitRef>,
360    /// The implemented associated constants.
361    pub consts: Vec<(TraitItemName, GlobalDeclRef)>,
362    /// The implemented associated types.
363    pub types: Vec<(TraitItemName, Binder<TraitAssocTyImpl>)>,
364    /// The implemented methods
365    pub methods: Vec<(TraitItemName, Binder<FunDeclRef>)>,
366    /// The virtual table instance for this trait implementation. This is `Some` iff the trait is
367    /// dyn-compatible.
368    pub vtable: Option<GlobalDeclRef>,
369}
370
371/// The value of a trait associated type.
372#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, Drive, DriveMut)]
373pub struct TraitAssocTyImpl {
374    pub value: Ty,
375    /// The `Vec` corresponds to the same `Vector` in `TraitAssocTy`. In the same way, this is
376    /// empty after the `lift_associated_item_clauses` pass.
377    #[charon::opaque]
378    pub implied_trait_refs: Vector<TraitClauseId, TraitRef>,
379}
380
381/// A function operand is used in function calls.
382/// It either designates a top-level function, or a place in case
383/// we are using function pointers stored in local variables.
384#[derive(Debug, Clone, Serialize, Deserialize, Drive, DriveMut)]
385#[charon::variants_prefix("FnOp")]
386pub enum FnOperand {
387    /// Regular case: call to a top-level function, trait method, etc.
388    Regular(FnPtr),
389    /// Use of a function pointer stored in a local variable
390    Move(Place),
391}
392
393#[derive(Debug, Clone, Serialize, Deserialize, Drive, DriveMut)]
394pub struct Call {
395    pub func: FnOperand,
396    pub args: Vec<Operand>,
397    pub dest: Place,
398}
399
400#[derive(Debug, Clone, Serialize, Deserialize, Drive, DriveMut)]
401pub struct CopyNonOverlapping {
402    pub src: Operand,
403    pub dst: Operand,
404    pub count: Operand,
405}
406
407/// (U)LLBC is a language with side-effects: a statement may abort in a way that isn't tracked by
408/// control-flow. The two kinds of abort are:
409/// - Panic (may unwind or not depending on compilation setting);
410/// - Undefined behavior:
411#[derive(Debug, Clone, Serialize, Deserialize, Drive, DriveMut)]
412pub enum AbortKind {
413    /// A built-in panicking function.
414    Panic(Option<Name>),
415    /// Undefined behavior in the rust abstract machine.
416    UndefinedBehavior,
417    /// Unwind had to stop for Abi reasons or because cleanup code panicked again.
418    UnwindTerminate,
419}
420
421/// A `Drop` statement/terminator can mean two things, depending on what MIR phase we retrieved
422/// from rustc: it could be a real drop, or it could be a "conditional drop", which is where drop
423/// may happen depending on whether the borrow-checker determines a drop is needed.
424#[derive(Debug, Clone, Copy, Serialize, Deserialize, Drive, DriveMut)]
425pub enum DropKind {
426    /// A real drop. This calls `<T as Destruct>::drop_in_place(&raw mut place)` and marks the
427    /// place as moved-out-of. Use `--desugar-drops` to transform all such drops to an actual
428    /// function call.
429    ///
430    /// The `drop_in_place` method is added by Charon to the `Destruct` trait to make it possible
431    /// to track drop code in polymorphic code. It contains the same code as the
432    /// `core::ptr::drop_in_place<T>` builtin would.
433    ///
434    /// Drop are precise in MIR `elaborated` and `optimized`.
435    Precise,
436    /// A conditional drop, which may or may not end up running drop code depending on the code
437    /// path that led to it. A conditional drop may also become a partial drop (dropping only the
438    /// subplaces that haven't been moved out of), may be conditional on the code path that led to
439    /// it, or become an async drop. The exact semantics are left intentionally unspecified by
440    /// rustc developers. To elaborate such drops into precise drops, pass `--precise-drops` to
441    /// Charon.
442    ///
443    /// A conditional drop may also be passed an unaligned place when dropping fields of packed
444    /// structs. Such a thing is UB for a precise drop.
445    ///
446    /// Drop are conditional in MIR `built` and `promoted`.
447    Conditional,
448}
449
450/// Check the value of an operand and abort if the value is not expected. This is introduced to
451/// avoid a lot of small branches.
452///
453/// We translate MIR asserts (introduced for out-of-bounds accesses or divisions by zero for
454/// instance) to this. We then eliminate them in [crate::transform::resugar::reconstruct_fallible_operations],
455/// because they're implicit in the semantics of our array accesses etc. Finally we introduce new asserts in
456/// [crate::transform::resugar::reconstruct_asserts].
457#[derive(Debug, Clone, Serialize, Deserialize, Drive, DriveMut)]
458#[charon::rename("Assertion")]
459pub struct Assert {
460    pub cond: Operand,
461    /// The value that the operand should evaluate to for the assert to succeed.
462    #[drive(skip)]
463    pub expected: bool,
464    /// What kind of abort happens on assert failure.
465    pub on_failure: AbortKind,
466}
467
468/// A generic `*DeclRef`-shaped struct, used when we're generic over the type of item.
469pub struct DeclRef<Id> {
470    pub id: Id,
471    pub generics: BoxedArgs,
472    /// If the item is a trait associated item, `generics` are only those of the item, and this
473    /// contains a reference to the trait.
474    pub trait_ref: Option<TraitRef>,
475}
476
477impl DeclRef<ItemId> {
478    pub fn try_convert_id<Id>(self) -> Result<DeclRef<Id>, <ItemId as TryInto<Id>>::Error>
479    where
480        ItemId: TryInto<Id>,
481    {
482        Ok(DeclRef {
483            id: self.id.try_into()?,
484            generics: self.generics,
485            trait_ref: self.trait_ref,
486        })
487    }
488}
489
490// Implement `DeclRef<_>` -> `FooDeclRef` conversions.
491macro_rules! convert_item_ref {
492    ($item_ref_ty:ident($id:ident)) => {
493        impl TryFrom<DeclRef<ItemId>> for $item_ref_ty {
494            type Error = ();
495            fn try_from(item: DeclRef<ItemId>) -> Result<Self, ()> {
496                assert!(item.trait_ref.is_none());
497                Ok($item_ref_ty {
498                    id: item.id.try_into()?,
499                    generics: item.generics,
500                })
501            }
502        }
503        impl From<DeclRef<$id>> for $item_ref_ty {
504            fn from(item: DeclRef<$id>) -> Self {
505                assert!(item.trait_ref.is_none());
506                $item_ref_ty {
507                    id: item.id,
508                    generics: item.generics,
509                }
510            }
511        }
512    };
513}
514convert_item_ref!(TypeDeclRef(TypeId));
515convert_item_ref!(FunDeclRef(FunDeclId));
516convert_item_ref!(GlobalDeclRef(GlobalDeclId));
517convert_item_ref!(TraitDeclRef(TraitDeclId));
518convert_item_ref!(TraitImplRef(TraitImplId));
519impl TryFrom<DeclRef<ItemId>> for FnPtr {
520    type Error = ();
521    fn try_from(item: DeclRef<ItemId>) -> Result<Self, ()> {
522        let id: FunId = item.id.try_into()?;
523        Ok(FnPtr::new(id.into(), item.generics))
524    }
525}
526
527impl TryFrom<DeclRef<ItemId>> for MaybeBuiltinFunDeclRef {
528    type Error = ();
529    fn try_from(item: DeclRef<ItemId>) -> Result<Self, ()> {
530        Ok(item.try_convert_id::<FunId>()?.into())
531    }
532}
533impl From<DeclRef<FunId>> for MaybeBuiltinFunDeclRef {
534    fn from(item: DeclRef<FunId>) -> Self {
535        MaybeBuiltinFunDeclRef {
536            id: item.id,
537            generics: item.generics,
538            trait_ref: item.trait_ref,
539        }
540    }
541}