Skip to main content

charon_lib/ast/
gast.rs

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