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