Skip to main content

charon_lib/ast/
expressions.rs

1//! Implements expressions: paths, operands, rvalues, lvalues
2
3use crate::ast::*;
4use derive_generic_visitor::{Drive, DriveMut};
5use macros::{EnumAsGetters, EnumIsA, EnumToGetters, VariantIndexArity, VariantName};
6use serde::{Deserialize, Serialize};
7use serde_state::{DeserializeState, SerializeState};
8use std::vec::Vec;
9
10#[derive(Debug, PartialEq, Eq, Clone, SerializeState, DeserializeState, Drive, DriveMut)]
11#[serde_state(state_implements = HashConsSerializerState)] // Avoid corecursive impls due to perfect derive
12pub struct Place {
13    pub kind: PlaceKind,
14    pub ty: Ty,
15}
16
17#[derive(
18    Debug,
19    PartialEq,
20    Eq,
21    Clone,
22    EnumIsA,
23    EnumAsGetters,
24    EnumToGetters,
25    SerializeState,
26    DeserializeState,
27    Drive,
28    DriveMut,
29)]
30#[charon::variants_prefix("Place")]
31pub enum PlaceKind {
32    /// A local variable in a function body.
33    Local(LocalId),
34    /// A subplace of a place.
35    Projection(Box<Place>, ProjectionElem),
36    /// A global (const or static).
37    /// Not present in MIR; introduced in [simplify_constants.rs].
38    Global(GlobalDeclRef),
39}
40
41/// Note that we don't have the equivalent of "downcasts".
42/// Downcasts are actually necessary, for instance when initializing enumeration
43/// values: the value is initially `Bottom`, and we need a way of knowing the
44/// variant.
45/// For example:
46/// `((_0 as Right).0: T2) = move _1;`
47/// In MIR, downcasts always happen before field projections: in our internal
48/// language, we thus merge downcasts and field projections.
49#[derive(
50    Debug,
51    PartialEq,
52    Eq,
53    Clone,
54    EnumIsA,
55    EnumAsGetters,
56    EnumToGetters,
57    VariantName,
58    SerializeState,
59    DeserializeState,
60    Drive,
61    DriveMut,
62)]
63pub enum ProjectionElem {
64    /// Dereference a shared/mutable reference, a box, or a raw pointer.
65    Deref,
66    /// Projection from ADTs (variants, structures).
67    /// We allow projections to be used as left-values and right-values.
68    /// We should never have projections to fields of symbolic variants (they
69    /// should have been expanded before through a match).
70    Field(FieldProjKind, FieldId),
71    /// A built-in pointer (a reference, raw pointer, or `Box`) in Rust is always a fat pointer: it
72    /// contains an address and metadata for the pointed-to place. This metadata is empty for sized
73    /// types, it's the length for slices, and the vtable for `dyn Trait`.
74    ///
75    /// We consider such pointers to be like a struct with two fields; this represent access to the
76    /// metadata "field".
77    PtrMetadata,
78    /// MIR imposes that the argument to an index projection be a local variable, meaning
79    /// that even constant indices into arrays are let-bound as separate variables.
80    /// We **eliminate** this variant in a micro-pass for LLBC.
81    #[charon::rename("ProjIndex")]
82    Index {
83        offset: Box<Operand>,
84        #[drive(skip)]
85        from_end: bool,
86    },
87    /// Take a subslice of a slice or array. If `from_end` is `true` this is
88    /// `slice[from..slice.len() - to]`, otherwise this is `slice[from..to]`.
89    /// We **eliminate** this variant in a micro-pass for LLBC.
90    Subslice {
91        from: Box<Operand>,
92        to: Box<Operand>,
93        #[drive(skip)]
94        from_end: bool,
95    },
96}
97
98#[derive(
99    Debug,
100    PartialEq,
101    Eq,
102    Copy,
103    Clone,
104    EnumIsA,
105    EnumAsGetters,
106    SerializeState,
107    DeserializeState,
108    Drive,
109    DriveMut,
110)]
111#[charon::variants_prefix("Proj")]
112pub enum FieldProjKind {
113    Adt(TypeDeclId, Option<VariantId>),
114    /// If we project from a tuple, the projection kind gives the arity of the tuple.
115    #[drive(skip)]
116    Tuple(usize),
117}
118
119#[derive(
120    Debug,
121    PartialEq,
122    Eq,
123    Copy,
124    Clone,
125    EnumIsA,
126    EnumAsGetters,
127    Serialize,
128    Deserialize,
129    Drive,
130    DriveMut,
131)]
132#[charon::variants_prefix("B")]
133pub enum BorrowKind {
134    Shared,
135    Mut,
136    /// See <https://doc.rust-lang.org/beta/nightly-rustc/rustc_middle/mir/enum.MutBorrowKind.html#variant.TwoPhaseBorrow>
137    /// and <https://rustc-dev-guide.rust-lang.org/borrow_check/two_phase_borrows.html>
138    TwoPhaseMut,
139    /// Those are typically introduced when using guards in matches, to make sure guards don't
140    /// change the variant of an enum value while me match over it.
141    ///
142    /// See <https://doc.rust-lang.org/beta/nightly-rustc/rustc_middle/mir/enum.FakeBorrowKind.html#variant.Shallow>.
143    Shallow,
144    /// Data must be immutable but not aliasable. In other words you can't mutate the data but you
145    /// can mutate *through it*, e.g. if it points to a `&mut T`. This is only used in closure
146    /// captures, e.g.
147    /// ```rust,ignore
148    /// let mut z = 3;
149    /// let x: &mut isize = &mut z;
150    /// let y = || *x += 5;
151    /// ```
152    /// Here the captured variable can't be `&mut &mut x` since the `x` binding is not mutable, yet
153    /// we must be able to mutate what it points to.
154    ///
155    /// See <https://doc.rust-lang.org/beta/nightly-rustc/rustc_middle/mir/enum.MutBorrowKind.html#variant.ClosureCapture>.
156    UniqueImmutable,
157}
158
159/// Unary operation
160#[derive(
161    Debug,
162    PartialEq,
163    Eq,
164    Clone,
165    EnumIsA,
166    VariantName,
167    SerializeState,
168    DeserializeState,
169    Drive,
170    DriveMut,
171)]
172#[charon::rename("Unop")]
173pub enum UnOp {
174    Not,
175    /// This can overflow, for `-i::MIN`.
176    #[drive(skip)]
177    #[serde_state(stateless)]
178    Neg(OverflowMode),
179    /// Casts are rvalues in MIR, but we treat them as unops.
180    Cast(CastKind),
181}
182
183/// Nullary operation
184#[derive(
185    Debug,
186    PartialEq,
187    Eq,
188    Clone,
189    EnumIsA,
190    VariantName,
191    SerializeState,
192    DeserializeState,
193    Drive,
194    DriveMut,
195)]
196#[charon::rename("Nullop")]
197pub enum NullOp {
198    SizeOf,
199    AlignOf,
200    OffsetOf(TypeDeclRef, Option<VariantId>, FieldId),
201    UbChecks,
202    OverflowChecks,
203    ContractChecks,
204}
205
206/// For all the variants: the first type gives the source type, the second one gives
207/// the destination type.
208#[derive(
209    Debug,
210    PartialEq,
211    Eq,
212    Clone,
213    EnumIsA,
214    VariantName,
215    SerializeState,
216    DeserializeState,
217    Drive,
218    DriveMut,
219)]
220#[charon::variants_prefix("Cast")]
221pub enum CastKind {
222    /// Conversion between types in `{Integer, Bool}`
223    /// Remark: for now we don't support conversions with Char.
224    Scalar(LiteralTy, LiteralTy),
225    RawPtr(Ty, Ty),
226    FnPtr(Ty, Ty),
227    /// [Unsize coercion](https://doc.rust-lang.org/std/ops/trait.CoerceUnsized.html). This is
228    /// either `[T; N]` -> `[T]` or `T: Trait` -> `dyn Trait` coercions, behind a pointer
229    /// (reference, `Box`, or other type that implements `CoerceUnsized`).
230    ///
231    /// The special case of `&[T; N]` -> `&[T]` coercion is caught by `UnOp::ArrayToSlice`.
232    Unsize(Ty, Ty, UnsizingMetadata),
233    /// Reinterprets the bits of a value of one type as another type, i.e. exactly what
234    /// [`std::mem::transmute`] does.
235    Transmute(Ty, Ty),
236    /// Converts a receiver type with `dyn Trait<...>` to a concrete type `T`, used in vtable method shims.
237    /// Valid conversions are references, raw pointers, and (optionally) boxes:
238    /// - `&[mut] dyn Trait<...>` -> `&[mut] T`
239    /// - `*[mut] dyn Trait<...>` -> `*[mut] T`
240    /// - `Box<dyn Trait<...>>` -> `Box<T>` when no `--raw-boxes`
241    ///
242    /// For possible receivers, see: <https://doc.rust-lang.org/reference/items/traits.html#dyn-compatibility>.
243    /// Other receivers, e.g., `Rc` should be unpacked before the cast and re-boxed after.
244    /// FIXME(ssyram): but this is not implemented yet, namely, there may still be
245    ///     something like `Rc<dyn Trait<...>> -> Rc<T>` in the types.
246    Concretize(Ty, Ty),
247}
248
249#[derive(
250    Debug,
251    PartialEq,
252    Eq,
253    Clone,
254    EnumIsA,
255    VariantName,
256    SerializeState,
257    DeserializeState,
258    Drive,
259    DriveMut,
260    Hash,
261)]
262#[charon::variants_prefix("Meta")]
263pub enum UnsizingMetadata {
264    /// Cast from `[T; N]` to `[T]`.
265    Length(Box<ConstantExpr>),
266    /// Cast from a sized value to a `dyn Trait` value. The `TraitRef` gives the trait for which
267    /// we're getting a vtable (this is the _principal_ trait of the `dyn Trait` target). The second
268    /// field is the vtable instance, if we could resolve it.
269    VTable(TraitRef, Option<GlobalDeclRef>),
270    /// Cast from `dyn Trait` to `dyn OtherTrait`. The fields indicate how to retreive the vtable:
271    /// it's always either the same we already had, or the vtable for a (possibly nested) supertrait.
272    ///
273    /// Note that we cheat in one case: when upcasting to a marker trait (e.g. `dyn Trait -> dyn
274    /// Sized`), we keep the current vtable.
275    VTableUpcast(Vec<FieldId>),
276    Unknown,
277}
278
279#[derive(Debug, PartialEq, Eq, Copy, Clone, Serialize, Deserialize)]
280#[charon::variants_prefix("O")]
281pub enum OverflowMode {
282    /// If this operation overflows, it panics. Only exists in debug mode, for instance in
283    /// `a + b`, and only if `--reconstruct-fallible-operations` is passed to Charon. Otherwise the
284    /// bound check will be explicit.
285    Panic,
286    /// If this operation overflows, it is UB; for instance in `core::num::unchecked_add`. This can
287    /// exists in safe code, but will always be preceded by a bounds check.
288    UB,
289    /// If this operation overflows, it wraps around for instance in `core::num::wrapping_add`,
290    /// or `a + b` in release mode.
291    Wrap,
292}
293
294/// Binary operations.
295#[derive(
296    Debug,
297    PartialEq,
298    Eq,
299    Copy,
300    Clone,
301    EnumIsA,
302    VariantName,
303    SerializeState,
304    DeserializeState,
305    Drive,
306    DriveMut,
307)]
308#[charon::rename("Binop")]
309#[serde_state(stateless)]
310pub enum BinOp {
311    BitXor,
312    BitAnd,
313    BitOr,
314    Eq,
315    Lt,
316    Le,
317    Ne,
318    Ge,
319    Gt,
320    #[drive(skip)]
321    Add(OverflowMode),
322    #[drive(skip)]
323    Sub(OverflowMode),
324    #[drive(skip)]
325    Mul(OverflowMode),
326    #[drive(skip)]
327    Div(OverflowMode),
328    #[drive(skip)]
329    Rem(OverflowMode),
330    /// Returns `(result, did_overflow)`, where `result` is the result of the operation with
331    /// wrapping semantics, and `did_overflow` is a boolean that indicates whether the operation
332    /// overflowed. This operation does not fail.
333    AddChecked,
334    /// Like `AddChecked`.
335    SubChecked,
336    /// Like `AddChecked`.
337    MulChecked,
338    /// Fails if the shift is bigger than the bit-size of the type.
339    #[drive(skip)]
340    Shl(OverflowMode),
341    /// Fails if the shift is bigger than the bit-size of the type.
342    #[drive(skip)]
343    Shr(OverflowMode),
344    /// `BinOp(Offset, ptr, n)` for `ptr` a pointer to type `T` offsets `ptr` by `n * size_of::<T>()`.
345    Offset,
346    /// `BinOp(Cmp, a, b)` returns `-1u8` if `a < b`, `0u8` if `a == b`, and `1u8` if `a > b`.
347    Cmp,
348}
349
350#[derive(
351    Debug,
352    PartialEq,
353    Eq,
354    Clone,
355    EnumIsA,
356    EnumToGetters,
357    EnumAsGetters,
358    VariantName,
359    SerializeState,
360    DeserializeState,
361    Drive,
362    DriveMut,
363)]
364#[serde_state(state_implements = HashConsSerializerState)] // Avoid corecursive impls due to perfect derive
365pub enum Operand {
366    Copy(Place),
367    Move(Place),
368    /// Constant value (including constant and static variables)
369    #[charon::rename("Constant")]
370    Const(Box<ConstantExpr>),
371}
372
373/// A function identifier. See [crate::ullbc_ast::Terminator]
374#[derive(
375    Debug,
376    Clone,
377    PartialEq,
378    Eq,
379    Hash,
380    EnumIsA,
381    EnumAsGetters,
382    VariantName,
383    SerializeState,
384    DeserializeState,
385    Drive,
386    DriveMut,
387)]
388#[charon::variants_prefix("F")]
389#[serde_state(stateless)]
390pub enum FunId {
391    /// A "regular" function (function local to the crate, external function
392    /// not treated as a primitive one).
393    Regular(FunDeclId),
394    /// A primitive function, coming from a standard library (for instance:
395    /// `alloc::boxed::Box::new`).
396    /// TODO: rename to "Primitive"
397    #[charon::rename("FBuiltin")]
398    Builtin(BuiltinFunId),
399}
400
401impl From<FunDeclId> for FunId {
402    fn from(id: FunDeclId) -> Self {
403        Self::Regular(id)
404    }
405}
406impl From<BuiltinFunId> for FunId {
407    fn from(id: BuiltinFunId) -> Self {
408        Self::Builtin(id)
409    }
410}
411
412/// An built-in function identifier, identifying a function coming from a
413/// standard library.
414#[derive(
415    Debug,
416    Clone,
417    Copy,
418    PartialEq,
419    Eq,
420    Hash,
421    EnumIsA,
422    EnumAsGetters,
423    VariantName,
424    Serialize,
425    Deserialize,
426    Drive,
427    DriveMut,
428)]
429pub enum BuiltinFunId {
430    /// Used instead of `alloc::boxed::Box::new` when `--treat-box-as-builtin` is set.
431    BoxNew,
432    /// Cast `&[T; N]` to `&[T]`.
433    ///
434    /// This is used instead of unsizing coercions when `--ops-to-function-calls` is set.
435    ArrayToSliceShared,
436    /// Cast `&mut [T; N]` to `&mut [T]`.
437    ///
438    /// This is used instead of unsizing coercions when `--ops-to-function-calls` is set.
439    ArrayToSliceMut,
440    /// `repeat(n, x)` returns an array where `x` has been replicated `n` times.
441    ///
442    /// This is used instead of `Rvalue::ArrayRepeat` when `--ops-to-function-calls` is set.
443    ArrayRepeat,
444    /// A built-in funciton introduced instead of array/slice place indexing when
445    /// `--index-to-function-calls` is set. The signature depends on the parameters. It could look
446    /// like:
447    /// - `fn ArrayIndexShared<T,N>(&[T;N], usize) -> &T`
448    /// - `fn SliceIndexShared<T>(&[T], usize) -> &T`
449    /// - `fn ArraySubSliceShared<T,N>(&[T;N], usize, usize) -> &[T]`
450    /// - `fn SliceSubSliceMut<T>(&mut [T], usize, usize) -> &mut [T]`
451    /// - etc
452    Index(BuiltinIndexOp),
453    /// Build a raw pointer, from a data pointer and metadata. The metadata can be unit, if
454    /// building a thin pointer.
455    ///
456    /// This is used instead of `AggregateKind::RawPtr` when `--ops-to-function-calls` is set.
457    PtrFromParts(RefKind),
458}
459
460/// One of 8 built-in indexing operations.
461#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Drive, DriveMut)]
462pub struct BuiltinIndexOp {
463    /// Whether this is a slice or array.
464    #[drive(skip)]
465    pub is_array: bool,
466    /// Whether we're indexing mutably or not. Determines the type ofreference of the input and
467    /// output.
468    pub mutability: RefKind,
469    /// Whether we're indexing a single element or a subrange. If `true`, the function takes
470    /// two indices and the output is a slice; otherwise, the function take one index and the
471    /// output is a reference to a single element.
472    #[drive(skip)]
473    pub is_range: bool,
474}
475
476/// Reference to a function declaration or builtin function.
477#[derive(Debug, Clone, SerializeState, DeserializeState, PartialEq, Eq, Hash, Drive, DriveMut)]
478pub struct MaybeBuiltinFunDeclRef {
479    pub id: FunId,
480    pub generics: BoxedArgs,
481    pub trait_ref: Option<TraitRef>,
482}
483
484/// Reference to a trait method.
485#[derive(Debug, Clone, SerializeState, DeserializeState, PartialEq, Eq, Hash, Drive, DriveMut)]
486pub struct TraitMethodRef {
487    pub trait_ref: TraitRef,
488    pub name: TraitItemName,
489    pub generics: BoxedArgs,
490    /// Reference to the method declaration; can be derived from the trait_ref, provided here for
491    /// convenience. The generic args are for the method, not for this function.
492    pub method_decl_id: FunDeclId,
493}
494
495#[derive(
496    Debug,
497    Clone,
498    PartialEq,
499    Eq,
500    EnumAsGetters,
501    SerializeState,
502    DeserializeState,
503    Drive,
504    DriveMut,
505    Hash,
506)]
507pub enum FnPtrKind {
508    #[charon::rename("FunId")]
509    Fun(FunId),
510    /// If a trait: the reference to the trait and the id of the trait method.
511    /// The fun decl id is not really necessary - we put it here for convenience
512    /// purposes.
513    #[charon::rename("TraitMethod")]
514    Trait(TraitRef, TraitItemName, FunDeclId),
515}
516
517impl From<FunId> for FnPtrKind {
518    fn from(id: FunId) -> Self {
519        Self::Fun(id)
520    }
521}
522impl From<FunDeclId> for FnPtrKind {
523    fn from(id: FunDeclId) -> Self {
524        Self::Fun(id.into())
525    }
526}
527
528#[derive(Debug, PartialEq, Eq, Clone, SerializeState, DeserializeState, Drive, DriveMut, Hash)]
529pub struct FnPtr {
530    pub kind: Box<FnPtrKind>,
531    pub generics: BoxedArgs,
532}
533
534impl From<FunDeclRef> for FnPtr {
535    fn from(fn_ref: FunDeclRef) -> Self {
536        FnPtr::new(fn_ref.id.into(), fn_ref.generics)
537    }
538}
539
540#[derive(Debug, PartialEq, Eq, Clone, SerializeState, DeserializeState, Drive, DriveMut, Hash)]
541#[charon::variants_prefix("Prov")]
542pub enum Provenance {
543    Global(GlobalDeclRef),
544    Function(FunDeclRef),
545    Unknown,
546}
547
548/// A byte, in the MiniRust sense: it can either be uninitialized, a concrete u8 value,
549/// or part of a pointer with provenance (e.g. to a global or a function)
550#[derive(Debug, PartialEq, Eq, Clone, SerializeState, DeserializeState, Drive, DriveMut, Hash)]
551pub enum Byte {
552    /// An uninitialized byte
553    Uninit,
554    /// A concrete byte value
555    Value(u8),
556    /// A byte that is part of a pointer with provenance. The u8 is the offset within the
557    /// pointer. Note that we do not have an actual value for this pointer byte, unlike
558    /// MiniRust, as that is non-deterministic.
559    Provenance(Provenance, u8),
560}
561
562/// A constant expression.
563///
564/// Only the [`ConstantExprKind::Literal`] and [`ConstantExprKind::Var`]
565/// cases are left in the final LLBC.
566///
567/// The other cases come from a straight translation from the MIR:
568///
569/// [`ConstantExprKind::Adt`] case:
570/// It is a bit annoying, but rustc treats some ADT and tuple instances as
571/// constants when generating MIR:
572/// - an enumeration with one variant and no fields is a constant.
573/// - a structure with no field is a constant.
574/// - sometimes, Rust stores the initialization of an ADT as a constant
575///   (if all the fields are constant) rather than as an aggregated value
576/// We later desugar those to regular ADTs, see [regularize_constant_adts.rs].
577///
578/// [`ConstantExprKind::Global`] case: access to a global variable. We later desugar it to
579/// a copy of a place global.
580///
581/// [`ConstantExprKind::Ref`] case: reference to a constant value. We later desugar it to a separate
582/// statement.
583///
584/// [`ConstantExprKind::FnPtr`] case: a function pointer (to a top-level function).
585///
586/// Remark:
587/// MIR seems to forbid more complex expressions like paths. For instance,
588/// reading the constant `a.b` is translated to `{ _1 = const a; _2 = (_1.0) }`.
589#[derive(
590    Debug,
591    Hash,
592    PartialEq,
593    Eq,
594    Clone,
595    VariantName,
596    EnumIsA,
597    EnumAsGetters,
598    SerializeState,
599    DeserializeState,
600    Drive,
601    DriveMut,
602)]
603#[charon::variants_prefix("C")]
604pub enum ConstantExprKind {
605    #[serde_state(stateless)]
606    Literal(Literal),
607    /// In most situations:
608    /// Enumeration with one variant with no fields, structure with
609    /// no fields, unit (encoded as a 0-tuple).
610    ///
611    /// Less frequently: arbitrary ADT values.
612    ///
613    /// We eliminate this case in a micro-pass.
614    Adt(Option<VariantId>, Vec<ConstantExpr>),
615    Array(Vec<ConstantExpr>),
616    /// The value is a top-level constant/static.
617    ///
618    /// We eliminate this case in a micro-pass.
619    ///
620    /// Remark: constants can actually have generic parameters.
621    /// ```text
622    /// struct V<const N: usize, T> {
623    ///   x: [T; N],
624    /// }
625    ///
626    /// impl<const N: usize, T> V<N, T> {
627    ///   const LEN: usize = N; // This has generics <N, T>
628    /// }
629    ///
630    /// fn use_v<const N: usize, T>(v: V<N, T>) {
631    ///   let l = V::<N, T>::LEN; // We need to provided a substitution here
632    /// }
633    /// ```
634    Global(GlobalDeclRef),
635    ///
636    /// A trait constant.
637    ///
638    /// Ex.:
639    /// ```text
640    /// impl Foo for Bar {
641    ///   const C : usize = 32; // <-
642    /// }
643    /// ```
644    ///
645    /// Remark: trait constants can not be used in types, they are necessarily
646    /// values.
647    TraitConst(TraitRef, TraitItemName),
648    /// A shared reference to a constant value.
649    ///
650    /// We eliminate this case in a micro-pass.
651    Ref(Box<ConstantExpr>, Option<UnsizingMetadata>),
652    /// A pointer to a mutable static.
653    ///
654    /// We eliminate this case in a micro-pass.
655    Ptr(RefKind, Box<ConstantExpr>, Option<UnsizingMetadata>),
656    /// A const generic var
657    Var(ConstGenericDbVar),
658    /// Function definition -- this is a ZST constant
659    FnDef(FnPtr),
660    /// A function pointer to a function item; this is an actual pointer to that function item.
661    ///
662    /// We eliminate this case in a micro-pass.
663    FnPtr(FnPtr),
664    /// A pointer with no provenance (e.g. 0 for the null pointer)
665    ///
666    /// We eliminate this case in a micro-pass.
667    #[drive(skip)]
668    PtrNoProvenance(u128),
669    /// Raw memory value obtained from constant evaluation. Used when a more structured
670    /// representation isn't possible (e.g. for unions) or just isn't implemented yet.
671    #[drive(skip)]
672    RawMemory(Vec<Byte>),
673    /// A constant expression that Charon still doesn't handle, along with the reason why.
674    #[drive(skip)]
675    Opaque(String),
676}
677
678#[derive(Debug, Hash, PartialEq, Eq, Clone, SerializeState, DeserializeState, Drive, DriveMut)]
679#[serde_state(state_implements = HashConsSerializerState)] // Avoid corecursive impls due to perfect derive
680pub struct ConstantExpr {
681    pub kind: ConstantExprKind,
682    pub ty: Ty,
683}
684
685/// TODO: we could factor out [Rvalue] and function calls (for LLBC, not ULLBC).
686/// We can also factor out the unops, binops with the function calls.
687/// TODO: move the aggregate kind to operands
688/// TODO: we should prefix the type variants with "R" or "Rv", this would avoid collisions
689#[derive(
690    Debug,
691    Clone,
692    EnumToGetters,
693    EnumAsGetters,
694    EnumIsA,
695    SerializeState,
696    DeserializeState,
697    Drive,
698    DriveMut,
699)]
700pub enum Rvalue {
701    /// Lifts an operand as an rvalue.
702    Use(Operand),
703    /// Takes a reference to the given place.
704    /// The `Operand` refers to the init value of the metadata, it is `()` if no metadata
705    #[charon::rename("RvRef")]
706    Ref {
707        place: Place,
708        #[serde_state(stateless)]
709        kind: BorrowKind,
710        ptr_metadata: Operand,
711    },
712    /// Takes a raw pointer with the given mutability to the given place. This is generated by
713    /// pointer casts like `&v as *const _` or raw borrow expressions like `&raw const v.`
714    /// Like `Ref`, the `Operand` refers to the init value of the metadata, it is `()` if no metadata.
715    RawPtr {
716        place: Place,
717        kind: RefKind,
718        ptr_metadata: Operand,
719    },
720    /// Binary operations (note that we merge "checked" and "unchecked" binops)
721    BinaryOp(BinOp, Operand, Operand),
722    /// Unary operation (e.g. not, neg)
723    UnaryOp(UnOp, Operand),
724    /// Nullary operation (e.g. `size_of`)
725    NullaryOp(NullOp, Ty),
726    /// Discriminant read. Reads the discriminant value of an enum. The place must have the type of
727    /// an enum. The discriminant in question is the one in the `discriminant` field of the
728    /// corresponding `Variant`. This can be different than the value stored in memory (called
729    /// `tag`). That one is described by [`DiscriminantLayout`] and [`TagEncoding`].
730    Discriminant(Place),
731    /// Creates an aggregate value, like a tuple, a struct or an enum:
732    /// ```text
733    /// l = List::Cons { value:x, tail:tl };
734    /// ```
735    /// Note that in some MIR passes (like optimized MIR), aggregate values are
736    /// decomposed, like below:
737    /// ```text
738    /// (l as List::Cons).value = x;
739    /// (l as List::Cons).tail = tl;
740    /// ```
741    /// Because we may want to plug our translation mechanism at various
742    /// places, we need to take both into accounts in the translation and in
743    /// our semantics. Aggregate value initialization is easy, you might want
744    /// to have a look at expansion of `Bottom` values for explanations about the
745    /// other case.
746    ///
747    /// Remark: in case of closures, the aggregated value groups the closure id
748    /// together with its state.
749    Aggregate(AggregateKind, Vec<Operand>),
750    /// Length of a place of type `[T]` or `[T; N]`. This applies to the place itself, not to a
751    /// pointer value. This is inserted by rustc in a single case: slice patterns.
752    /// ```text
753    /// fn slice_pattern_4(x: &[()]) {
754    ///     match x {
755    ///         [_named] => (),
756    ///         _ => (),
757    ///     }
758    /// }
759    /// ```
760    Len(Place, Ty, Option<Box<ConstantExpr>>),
761    /// `Repeat(x, n)` creates an array where `x` is copied `n` times.
762    ///
763    /// We translate this to a function call for LLBC.
764    Repeat(Operand, Ty, Box<ConstantExpr>),
765    /// Transmutes a `*mut u8` (obtained from `malloc`) into shallow-initialized `Box<T>`. This
766    /// only appears as part of lowering `Box::new()` in some cases. We reconstruct the original
767    /// `Box::new()` call, but sometimes may fail to do so, leaking the expression.
768    ShallowInitBox(Operand, Ty),
769}
770
771/// An aggregated ADT.
772///
773/// Note that ADTs are desaggregated at some point in MIR. For instance, if
774/// we have in Rust:
775/// ```ignore
776///   let ls = Cons(hd, tl);
777/// ```
778///
779/// In MIR we have (yes, the discriminant update happens *at the end* for some
780/// reason):
781/// ```text
782///   (ls as Cons).0 = move hd;
783///   (ls as Cons).1 = move tl;
784///   discriminant(ls) = 0; // assuming `Cons` is the variant of index 0
785/// ```
786///
787/// Rem.: in the Aeneas semantics, both cases are handled (in case of desaggregated
788/// initialization, `ls` is initialized to `⊥`, then this `⊥` is expanded to
789/// `Cons (⊥, ⊥)` upon the first assignment, at which point we can initialize
790/// the field 0, etc.).
791#[derive(Debug, Clone, VariantIndexArity, SerializeState, DeserializeState, Drive, DriveMut)]
792#[charon::variants_prefix("Aggregated")]
793pub enum AggregateKind {
794    /// A struct, enum or union aggregate. The `VariantId`, if present, indicates this is an enum
795    /// and the aggregate uses that variant. The `FieldId`, if present, indicates this is a union
796    /// and the aggregate writes into that field. Otherwise this is a struct.
797    Adt(TypeDeclRef, Option<VariantId>, Option<FieldId>),
798    /// We don't put this with the ADT cas because this is the only built-in type
799    /// with aggregates, and it is a primitive type. In particular, it makes
800    /// sense to treat it differently because it has a variable number of fields.
801    Array(Ty, Box<ConstantExpr>),
802    /// Construct a raw pointer from a pointer value, and its metadata (can be unit, if building
803    /// a thin pointer). The type is the type of the pointee.
804    /// We lower this to a builtin function call for LLBC in [crate::transform::simplify_output::ops_to_function_calls].
805    RawPtr(Ty, RefKind),
806}