Skip to main content

charon_lib/ast/bodies/
places.rs

1//! Implements expressions: paths, operands, rvalues, lvalues
2use crate::ast::*;
3use derive_generic_visitor::{Drive, DriveMut, DriveTwo};
4use macros::{EnumAsGetters, EnumIsA, EnumToGetters, VariantName};
5use serde_state::{DeserializeState, SerializeState};
6
7#[derive(
8    Debug, PartialEq, Eq, Clone, SerializeState, DeserializeState, Drive, DriveMut, DriveTwo,
9)]
10#[serde_state(state_implements = HashConsSerializerState)] // Avoid corecursive impls due to perfect derive
11pub struct Place {
12    pub kind: PlaceKind,
13    pub ty: Ty,
14}
15
16#[derive(
17    Debug,
18    PartialEq,
19    Eq,
20    Clone,
21    EnumIsA,
22    EnumAsGetters,
23    EnumToGetters,
24    SerializeState,
25    DeserializeState,
26    Drive,
27    DriveMut,
28    DriveTwo,
29)]
30#[cfg_attr(feature = "charon_on_charon", 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/// Projects a place to a subplace.
42#[derive(
43    Debug,
44    PartialEq,
45    Eq,
46    Clone,
47    EnumIsA,
48    EnumAsGetters,
49    EnumToGetters,
50    VariantName,
51    SerializeState,
52    DeserializeState,
53    Drive,
54    DriveMut,
55    DriveTwo,
56)]
57pub enum ProjectionElem {
58    /// Dereference a shared/mutable reference, a box, or a raw pointer.
59    Deref,
60    /// Project to the field of an ADT (struct, union, or enum).
61    Field(Option<VariantId>, FieldId),
62    /// A built-in pointer (a reference, raw pointer, or `Box`) in Rust is always a fat pointer: it
63    /// contains an address and metadata for the pointed-to place. This metadata is empty for sized
64    /// types, it's the length for slices, and the vtable for `dyn Trait`.
65    ///
66    /// We consider such pointers to be like a struct with two fields; this represent access to the
67    /// metadata "field".
68    PtrMetadata,
69    /// MIR imposes that the argument to an index projection be a local variable, meaning
70    /// that even constant indices into arrays are let-bound as separate variables.
71    /// We **eliminate** this variant in a micro-pass for LLBC.
72    #[cfg_attr(feature = "charon_on_charon", charon::rename("ProjIndex"))]
73    Index {
74        offset: Box<Operand>,
75        #[drive(skip)]
76        from_end: bool,
77    },
78    /// Take a subslice of a slice or array. If `from_end` is `true` this is
79    /// `slice[from..slice.len() - to]`, otherwise this is `slice[from..to]`.
80    /// We **eliminate** this variant in a micro-pass for LLBC.
81    Subslice {
82        from: Box<Operand>,
83        to: Box<Operand>,
84        #[drive(skip)]
85        from_end: bool,
86    },
87}
88
89impl Place {
90    pub fn new(local_id: LocalId, ty: Ty) -> Place {
91        Place {
92            kind: PlaceKind::Local(local_id),
93            ty,
94        }
95    }
96
97    pub fn new_global(global: GlobalDeclRef, ty: Ty) -> Place {
98        Place {
99            kind: PlaceKind::Global(global),
100            ty,
101        }
102    }
103
104    pub fn ty(&self) -> &Ty {
105        &self.ty
106    }
107
108    /// Whether this place corresponds to a local variable without any projections.
109    pub fn is_local(&self) -> bool {
110        self.as_local().is_some()
111    }
112
113    /// If this place corresponds to an unprojected local, return the variable id.
114    pub fn as_local(&self) -> Option<LocalId> {
115        self.kind.as_local().copied()
116    }
117
118    pub fn as_projection(&self) -> Option<(&Self, &ProjectionElem)> {
119        self.kind.as_projection().map(|(pl, pj)| (pl.as_ref(), pj))
120    }
121
122    #[deprecated(note = "use `local_id` instead")]
123    pub fn var_id(&self) -> Option<LocalId> {
124        self.local_id()
125    }
126    pub fn local_id(&self) -> Option<LocalId> {
127        match &self.kind {
128            PlaceKind::Local(var_id) => Some(*var_id),
129            PlaceKind::Projection(subplace, _) => subplace.local_id(),
130            PlaceKind::Global(_) => None,
131        }
132    }
133
134    pub fn project(self, elem: ProjectionElem, ty: Ty) -> Self {
135        Self {
136            kind: PlaceKind::Projection(Box::new(self), elem),
137            ty,
138        }
139    }
140
141    pub fn project_auto_ty(self, krate: &TranslatedCrate, proj: ProjectionElem) -> Option<Self> {
142        Some(Place {
143            ty: proj.project_type(krate, &self.ty)?,
144            kind: PlaceKind::Projection(Box::new(self), proj),
145        })
146    }
147
148    /// Dereferences the place. Panics if the type cannot be dereferenced.
149    pub fn deref(self) -> Place {
150        use TyKind::*;
151        let proj_ty = match self.ty.kind() {
152            Ref(_, ty, _) | RawPtr(ty, _) => ty.clone(),
153            Adt(tref) if tref.is_box() => tref.generics.types[0].clone(),
154            Adt(..) | TypeVar(_) | Literal(_) | Never | TraitType(..) | DynTrait(..)
155            | FnPtr(..) | FnDef(..) | PtrMetadata(..) | Array(..) | Slice(_) | Pattern(..)
156            | Error(..) => {
157                panic!("internal type error")
158            }
159        };
160        Place {
161            ty: proj_ty,
162            kind: PlaceKind::Projection(Box::new(self), ProjectionElem::Deref),
163        }
164    }
165
166    pub fn projections(&self) -> impl Iterator<Item = &ProjectionElem> {
167        let mut place = self;
168        std::iter::from_fn(move || {
169            let (new_place, proj) = place.as_projection()?;
170            place = new_place;
171            Some(proj)
172        })
173    }
174}
175
176impl ProjectionElem {
177    /// Compute the type obtained when applying the current projection to a place of type `ty`.
178    pub fn project_type(&self, krate: &TranslatedCrate, ty: &Ty) -> Option<Ty> {
179        use ProjectionElem::*;
180        Some(match self {
181            Deref => {
182                use TyKind::*;
183                match ty.kind() {
184                    Ref(_, ty, _) | RawPtr(ty, _) => ty.clone(),
185                    Adt(tref) if tref.is_box() => tref.generics.types[0].clone(),
186                    Adt(..) | TypeVar(_) | Literal(_) | Never | TraitType(..) | DynTrait(..)
187                    | Array(..) | Slice(..) | FnPtr(..) | FnDef(..) | PtrMetadata(..)
188                    | Pattern(..) | Error(..) => {
189                        // Type error
190                        return None;
191                    }
192                }
193            }
194            Field(variant_id, field_id) => {
195                let tref = ty.as_adt()?;
196                match tref.as_builtin() {
197                    None => {
198                        // Can fail if the type declaration was not translated.
199                        let type_decl = krate.type_decls.get(tref.adt_id())?;
200                        use TypeDeclKind::*;
201                        match &type_decl.kind {
202                            Struct(fields) | Union(fields) => {
203                                if variant_id.is_some() {
204                                    return None;
205                                };
206                                fields.get(*field_id)?.ty.clone().substitute(&tref.generics)
207                            }
208                            Enum(variants) => {
209                                let variant_id = (*variant_id)?;
210                                let variant = variants.get(variant_id)?;
211                                variant
212                                    .fields
213                                    .get(*field_id)?
214                                    .ty
215                                    .clone()
216                                    .substitute(&tref.generics)
217                            }
218                            Opaque | Alias(_) | Error(_) => return None,
219                        }
220                    }
221                    Some(BuiltinTy::Tuple) => tref
222                        .generics
223                        .types
224                        .get(TypeVarId::from(usize::from(*field_id)))?
225                        .clone(),
226                    Some(_) => return None,
227                }
228            }
229            PtrMetadata => ty.get_ptr_metadata(krate).into_type(),
230            Index { .. } | Subslice { .. } => ty.as_array_or_slice()?.clone(),
231        })
232    }
233}