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