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