Skip to main content

charon_lib/ast/meta/
names.rs

1//! User-visible names of items.
2use crate::ast::*;
3use derive_generic_visitor::{Drive, DriveMut, DriveTwo};
4use macros::{EnumAsGetters, EnumIsA};
5use serde_state::{DeserializeState, SerializeState};
6
7generate_index_type!(Disambiguator);
8
9// Some known names we may refer to.
10/// We treat this one specially in the `inline_local_panic_functions` pass. See there for details.
11pub static EXPLICIT_PANIC_NAME: &[&str] = &["core", "panicking", "panic_explicit"];
12pub static BOX_ASSUME_INIT_INTO_VEC_UNSAFE: &str = "box_assume_init_into_vec_unsafe";
13pub static BOX_WRITE: &str = "alloc::boxed::Box::write";
14pub static BOX_WRITE_PATTERN: &str = "alloc::boxed::_::write"; // `_` matches an impl block
15
16/// See the comments for [Name]
17#[derive(
18    Debug,
19    Clone,
20    PartialEq,
21    Eq,
22    Hash,
23    SerializeState,
24    DeserializeState,
25    Drive,
26    DriveMut,
27    DriveTwo,
28    EnumIsA,
29    EnumAsGetters,
30)]
31#[cfg_attr(feature = "charon_on_charon", charon::variants_prefix("Pe"))]
32pub enum PathElem {
33    #[serde_state(stateless)]
34    Ident(#[drive(skip)] String, Disambiguator),
35    Impl(ImplElem),
36    /// This item was obtained by instantiating its parent with the given args. The binder binds
37    /// the parameters of the new items. If the binder binds nothing then this is a
38    /// monomorphization.
39    Instantiated(Box<Binder<GenericArgs>>),
40    /// This item is only available on the given target. Only appears in multi-target mode.
41    #[serde_state(stateless)]
42    Target(#[drive(skip)] TargetTriple),
43}
44
45/// There are two kinds of `impl` blocks:
46/// - impl blocks linked to a type ("inherent" impl blocks following Rust terminology):
47///   ```text
48///   impl<T> List<T> { ...}
49///   ```
50/// - trait impl blocks:
51///   ```text
52///   impl<T> PartialEq for List<T> { ...}
53///   ```
54/// We distinguish the two.
55#[derive(
56    Debug,
57    Clone,
58    PartialEq,
59    Eq,
60    Hash,
61    SerializeState,
62    DeserializeState,
63    Drive,
64    DriveMut,
65    DriveTwo,
66    EnumIsA,
67    EnumAsGetters,
68)]
69#[cfg_attr(feature = "charon_on_charon", charon::variants_prefix("ImplElem"))]
70pub enum ImplElem {
71    Ty(Box<Binder<Ty>>),
72    Trait(TraitImplId),
73}
74
75/// An item name/path
76///
77/// A name really is a list of strings. However, we sometimes need to
78/// introduce unique indices to disambiguate. This mostly happens because
79/// of "impl" blocks:
80///   ```text
81///   impl<T> List<T> {
82///     ...
83///   }
84///   ```
85///
86/// A type in Rust can have several "impl" blocks, and  those blocks can
87/// contain items with similar names. For this reason, we need to disambiguate
88/// them with unique indices. Rustc calls those "disambiguators". In rustc, this
89/// gives names like this:
90/// - `betree_main::betree::NodeIdCounter{impl#0}::new`
91/// - note that impl blocks can be nested, and macros sometimes generate
92///   weird names (which require disambiguation):
93///   `betree_main::betree_utils::_#1::{impl#0}::deserialize::{impl#0}`
94///
95/// Finally, the paths used by rustc are a lot more precise and explicit than
96/// those we expose in LLBC: for instance, every identifier belongs to a specific
97/// namespace (value namespace, type namespace, etc.), and is coupled with a
98/// disambiguator.
99///
100/// On our side, we want to stay high-level and simple: we use string identifiers
101/// as much as possible, insert disambiguators only when necessary (for instance
102/// when we find an "impl" block or when two loaded crates have the same name)
103/// and check that the disambiguator is useless in the other situations (i.e.,
104/// the disambiguator is always equal to 0).
105///
106/// Moreover, the items are uniquely disambiguated by their (integer) ids
107/// (`TypeDeclId`, etc.), and when extracting the code we have to deal with
108/// name clashes anyway. Still, we might want to be more precise in the future.
109///
110/// Also note that the first path element in the name is always the crate name.
111#[derive(
112    Debug,
113    Default,
114    Clone,
115    PartialEq,
116    Eq,
117    Hash,
118    SerializeState,
119    DeserializeState,
120    Drive,
121    DriveMut,
122    DriveTwo,
123)]
124#[serde(transparent)]
125#[cfg_attr(feature = "charon_on_charon", charon::transparent)]
126pub struct Name {
127    pub name: Vec<PathElem>,
128}
129
130impl PathElem {
131    fn equals_ident(&self, id: &str) -> bool {
132        match self {
133            PathElem::Ident(s, d) => s == id && d.is_zero(),
134            _ => false,
135        }
136    }
137
138    pub fn as_monomorphized(&self) -> Option<&GenericArgs> {
139        let binder = self.as_instantiated()?;
140        binder.params.is_empty().then_some(&binder.skip_binder)
141    }
142    pub fn as_monomorphized_mut(&mut self) -> Option<&mut GenericArgs> {
143        let binder = self.as_instantiated_mut()?;
144        binder.params.is_empty().then_some(&mut binder.skip_binder)
145    }
146    pub fn is_monomorphized(&self) -> bool {
147        self.as_monomorphized().is_some()
148    }
149}
150
151impl Name {
152    /// Convert a path like `["std", "alloc", "Box"]` to a name. Needed on occasion when crafting
153    /// names that were not present in the original code.
154    pub fn from_path(path: &[&str]) -> Name {
155        Name {
156            name: path
157                .iter()
158                .map(|elem| PathElem::Ident(elem.to_string(), Disambiguator::ZERO))
159                .collect(),
160        }
161    }
162
163    #[allow(clippy::len_without_is_empty)]
164    pub fn len(&self) -> usize {
165        self.name.len()
166    }
167
168    /// If this item comes from monomorphization, return the arguments used.
169    pub fn mono_args(&self) -> Option<&GenericArgs> {
170        self.name.last()?.as_monomorphized()
171    }
172    /// If this item comes from monomorphization, return the arguments used.
173    pub fn mono_args_mut(&mut self) -> Option<&mut GenericArgs> {
174        self.name.last_mut()?.as_monomorphized_mut()
175    }
176
177    /// Strip the trailing `PathElem::Target` from a name, if any.
178    pub fn strip_target_suffix(&self) -> Option<(Name, TargetTriple)> {
179        match self.name.last() {
180            Some(PathElem::Target(target)) => {
181                let target = target.clone();
182                let mut base = self.clone();
183                base.name.pop();
184                Some((base, target))
185            }
186            _ => None,
187        }
188    }
189
190    /// Compare the name to a constant array.
191    /// This ignores disambiguators.
192    ///
193    /// `equal`: if `true`, check that the name is equal to the ref. If `false`:
194    /// only check if the ref is a prefix of the name.
195    pub fn compare_with_ref_name(&self, equal: bool, ref_name: &[&str]) -> bool {
196        let name: Vec<&PathElem> = self.name.iter().filter(|e| e.is_ident()).collect();
197
198        if name.len() < ref_name.len() || (equal && name.len() != ref_name.len()) {
199            return false;
200        }
201
202        for i in 0..ref_name.len() {
203            if !name[i].equals_ident(ref_name[i]) {
204                return false;
205            }
206        }
207        true
208    }
209
210    /// Compare the name to a constant array.
211    /// This ignores disambiguators.
212    pub fn equals_ref_name(&self, ref_name: &[&str]) -> bool {
213        self.compare_with_ref_name(true, ref_name)
214    }
215
216    /// Created an instantiated version of this name by putting a `PathElem::Instantiated` last. If
217    /// the item was already instantiated, this merges the two instantiations.
218    pub fn instantiate(mut self, binder: Binder<GenericArgs>) -> Self {
219        if let [.., PathElem::Instantiated(x)] = self.name.as_mut_slice() {
220            // Put the new args in place; the params are what we want but the args are wrong.
221            let old_args = std::mem::replace(x.as_mut(), binder);
222            // Apply the new args to the old binder to get correct args.
223            x.skip_binder = old_args.apply(&x.skip_binder);
224        } else {
225            self.name.push(PathElem::Instantiated(Box::new(binder)));
226        }
227        self
228    }
229
230    /// Get the last identifier of the name, if any. This is useful for error messages and such.
231    /// Panics if the name is empty or if the last element is not an identifier.
232    pub fn short_str(&self) -> Option<&str> {
233        Some(self.name.last()?.as_ident()?.0)
234    }
235}