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 itertools::Itertools;
5use macros::{EnumAsGetters, EnumIsA};
6use serde::{Deserialize, Serialize};
7use serde_state::{DeserializeState, SerializeState};
8
9generate_index_type!(Disambiguator);
10
11// Some known names we may refer to.
12/// We treat this one specially in the `inline_local_panic_functions` pass. See there for details.
13pub static EXPLICIT_PANIC_NAME: &[&str] = &["core", "panicking", "panic_explicit"];
14pub static BOX_ASSUME_INIT_INTO_VEC_UNSAFE: &str = "box_assume_init_into_vec_unsafe";
15pub static BOX_NEW: &str = "alloc::boxed::Box::new";
16pub static BOX_WRITE: &str = "alloc::boxed::Box::write";
17pub static BOX_WRITE_PATTERN: &str = "alloc::boxed::_::write"; // `_` matches an impl block
18
19/// See the comments for [Name]
20#[derive(
21    Debug,
22    Clone,
23    PartialEq,
24    Eq,
25    Hash,
26    SerializeState,
27    DeserializeState,
28    Drive,
29    DriveMut,
30    DriveTwo,
31    EnumIsA,
32    EnumAsGetters,
33)]
34#[cfg_attr(feature = "charon_on_charon", charon::variants_prefix("Pe"))]
35pub enum PathElem {
36    #[serde_state(stateless)]
37    Ident(String, Disambiguator),
38    Impl(ImplElem),
39    /// This item was obtained by instantiating its parent with the given args. The binder binds
40    /// the parameters of the new items. If the binder binds nothing then this is a
41    /// monomorphization.
42    Instantiated(Box<Binder<GenericArgs>>),
43    /// This item is only available on the given target. Only appears in multi-target mode.
44    #[serde_state(stateless)]
45    Target(TargetTriple),
46    /// A path element that doesn't come from the source code: either a builtin type such as
47    /// tuples, or an item that has no name of its own such as a closure or a vtable.
48    #[serde_state(stateless)]
49    Builtin(BuiltinPathElem, Disambiguator),
50}
51
52/// Used for builtin items, rather than hardcoding these as strings.
53#[derive(
54    Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, EnumIsA, EnumAsGetters,
55)]
56#[cfg_attr(feature = "charon_on_charon", charon::variants_prefix("Pe"))]
57pub enum BuiltinPathElem {
58    /// The tuple of the given arity.
59    Tuple(usize),
60    /// `str`, which is a struct containing a `[u8]` the standard library expects
61    /// to be valid UTF-8.
62    Str,
63    /// A closure.
64    Closure,
65    /// A `use` declaration.
66    Use,
67    /// An anonymous constant.
68    AnonConst,
69    /// A constant that rustc promoted out of a body.
70    PromotedConst,
71    /// The function item we generate for a closure that is cast to a function pointer.
72    ClosureAsFn,
73    /// The method we add to the `Destruct` trait to hold the drop glue.
74    DropGlue,
75    /// The vtable struct of a trait, or the vtable global of a trait impl.
76    VTable,
77    /// The version of a method that is stored in a vtable.
78    VTableMethod,
79    /// The `drop_in_place` shim stored in a vtable.
80    VTableDropShim,
81}
82
83/// There are two kinds of `impl` blocks:
84/// - impl blocks linked to a type ("inherent" impl blocks following Rust terminology):
85///   ```text
86///   impl<T> List<T> { ...}
87///   ```
88/// - trait impl blocks:
89///   ```text
90///   impl<T> PartialEq for List<T> { ...}
91///   ```
92/// We distinguish the two.
93#[derive(
94    Debug,
95    Clone,
96    PartialEq,
97    Eq,
98    Hash,
99    SerializeState,
100    DeserializeState,
101    Drive,
102    DriveMut,
103    DriveTwo,
104    EnumIsA,
105    EnumAsGetters,
106)]
107#[cfg_attr(feature = "charon_on_charon", charon::variants_prefix("ImplElem"))]
108pub enum ImplElem {
109    Ty(Box<Binder<Ty>>),
110    Trait(TraitImplId),
111}
112
113/// An item name/path
114///
115/// A name really is a list of strings. However, we sometimes need to
116/// introduce unique indices to disambiguate. This mostly happens because
117/// of "impl" blocks:
118///   ```text
119///   impl<T> List<T> {
120///     ...
121///   }
122///   ```
123///
124/// A type in Rust can have several "impl" blocks, and  those blocks can
125/// contain items with similar names. For this reason, we need to disambiguate
126/// them with unique indices. Rustc calls those "disambiguators". In rustc, this
127/// gives names like this:
128/// - `betree_main::betree::NodeIdCounter{impl#0}::new`
129/// - note that impl blocks can be nested, and macros sometimes generate
130///   weird names (which require disambiguation):
131///   `betree_main::betree_utils::_#1::{impl#0}::deserialize::{impl#0}`
132///
133/// Finally, the paths used by rustc are a lot more precise and explicit than
134/// those we expose in LLBC: for instance, every identifier belongs to a specific
135/// namespace (value namespace, type namespace, etc.), and is coupled with a
136/// disambiguator.
137///
138/// On our side, we want to stay high-level and simple: we use string identifiers
139/// as much as possible, insert disambiguators only when necessary (for instance
140/// when we find an "impl" block or when two loaded crates have the same name)
141/// and check that the disambiguator is useless in the other situations (i.e.,
142/// the disambiguator is always equal to 0).
143///
144/// Moreover, the items are uniquely disambiguated by their (integer) ids
145/// (`TypeDeclId`, etc.), and when extracting the code we have to deal with
146/// name clashes anyway. Still, we might want to be more precise in the future.
147///
148/// Also note that the first path element in the name is always the crate name.
149#[derive(
150    Debug,
151    Default,
152    Clone,
153    PartialEq,
154    Eq,
155    Hash,
156    SerializeState,
157    DeserializeState,
158    Drive,
159    DriveMut,
160    DriveTwo,
161)]
162#[serde(transparent)]
163#[cfg_attr(feature = "charon_on_charon", charon::transparent)]
164pub struct Name {
165    pub name: Vec<PathElem>,
166}
167
168impl PathElem {
169    fn equals_ident(&self, id: &str) -> bool {
170        match self {
171            PathElem::Ident(s, d) => s == id && d.is_zero(),
172            _ => false,
173        }
174    }
175
176    pub fn as_monomorphized(&self) -> Option<&GenericArgs> {
177        let binder = self.as_instantiated()?;
178        binder.params.is_empty().then_some(&binder.skip_binder)
179    }
180    pub fn as_monomorphized_mut(&mut self) -> Option<&mut GenericArgs> {
181        let binder = self.as_instantiated_mut()?;
182        binder.params.is_empty().then_some(&mut binder.skip_binder)
183    }
184    pub fn is_monomorphized(&self) -> bool {
185        self.as_monomorphized().is_some()
186    }
187}
188
189impl Name {
190    /// Convert a path like `["std", "alloc", "Box"]` to a name. Needed on occasion when crafting
191    /// names that were not present in the original code.
192    pub fn from_path(path: &[&str]) -> Name {
193        Name {
194            name: path
195                .iter()
196                .map(|elem| PathElem::Ident(elem.to_string(), Disambiguator::ZERO))
197                .collect(),
198        }
199    }
200
201    #[allow(clippy::len_without_is_empty)]
202    pub fn len(&self) -> usize {
203        self.name.len()
204    }
205
206    /// If this item comes from monomorphization, return the arguments used.
207    pub fn mono_args(&self) -> Option<&GenericArgs> {
208        self.name.last()?.as_monomorphized()
209    }
210    /// If this item comes from monomorphization, return the arguments used.
211    pub fn mono_args_mut(&mut self) -> Option<&mut GenericArgs> {
212        self.name.last_mut()?.as_monomorphized_mut()
213    }
214
215    /// Strip the trailing `PathElem::Target` from a name, if any.
216    pub fn strip_target_suffix(&self) -> Option<(Name, TargetTriple)> {
217        match self.name.last() {
218            Some(PathElem::Target(target)) => {
219                let target = target.clone();
220                let mut base = self.clone();
221                base.name.pop();
222                Some((base, target))
223            }
224            _ => None,
225        }
226    }
227
228    /// Returns this name with the `PathElem::Instantiated` part removed, if it has one.
229    pub fn as_slice_uninstantiated(&self) -> &[PathElem] {
230        match self.name.as_slice() {
231            [name @ .., PathElem::Instantiated(_)] => name,
232            name => name,
233        }
234    }
235
236    /// Compare the name to a constant array.
237    /// This ignores disambiguators.
238    ///
239    /// `equal`: if `true`, check that the name is equal to the ref. If `false`:
240    /// only check if the ref is a prefix of the name.
241    pub fn compare_with_ref_name(&self, equal: bool, ref_name: &[&str]) -> bool {
242        let name: Vec<&PathElem> = self.name.iter().filter(|e| e.is_ident()).collect();
243
244        if name.len() < ref_name.len() || (equal && name.len() != ref_name.len()) {
245            return false;
246        }
247
248        for i in 0..ref_name.len() {
249            if !name[i].equals_ident(ref_name[i]) {
250                return false;
251            }
252        }
253        true
254    }
255
256    /// Compare the name to a constant array.
257    /// This ignores disambiguators.
258    pub fn equals_ref_name(&self, ref_name: &[&str]) -> bool {
259        self.compare_with_ref_name(true, ref_name)
260    }
261
262    /// Created an instantiated version of this name by putting a `PathElem::Instantiated` last. If
263    /// the item was already instantiated, this merges the two instantiations.
264    pub fn instantiate(mut self, binder: Binder<GenericArgs>) -> Self {
265        if let [.., PathElem::Instantiated(x)] = self.name.as_mut_slice() {
266            // Put the new args in place; the params are what we want but the args are wrong.
267            let old_args = std::mem::replace(x.as_mut(), binder);
268            // Apply the new args to the old binder to get correct args.
269            x.skip_binder = old_args.apply(&x.skip_binder);
270        } else {
271            self.name.push(PathElem::Instantiated(Box::new(binder)));
272        }
273        self
274    }
275
276    /// Whether this names one of the items Rust builds into the language (tuples, `str`, arrays,
277    /// slices) or an item we generate for one, such as its drop glue. They belong to no crate, so
278    /// their name starts either with the builtin itself or with the `impl` block we generated for them.
279    pub fn is_builtin(&self) -> bool {
280        matches!(
281            self.name.first(),
282            Some(PathElem::Builtin(..) | PathElem::Impl(_))
283        )
284    }
285
286    /// Get the last identifier of the name, if any. This is useful for error messages and such.
287    /// Returns `None` if the name is empty or if the last element has no identifier to give.
288    pub fn short_str(&self) -> Option<&str> {
289        match self.name.last()? {
290            PathElem::Builtin(builtin, _) => Some(builtin.ident()),
291            PathElem::Ident(str, _) => Some(str),
292            _ => None,
293        }
294    }
295
296    /// `Name` is a complex datastructure; to inspect it we serialize it a little bit.
297    /// This must only be used for debug printing; it is not reliable or exact.
298    pub fn debug_repr(&self, crate_data: &TranslatedCrate) -> String {
299        // Small helper
300        let trait_name = |impl_id: TraitImplId| {
301            crate_data
302                .trait_impls
303                .get(impl_id)
304                .and_then(|timpl| crate_data.trait_decls.get(timpl.impl_trait.id))
305                .and_then(|tr| tr.item_meta.name.name.last())
306                .and_then(|p| p.as_ident())
307                .map(|(name, _)| name)
308        };
309
310        self.name
311            .iter()
312            .map(|path_elem| match path_elem {
313                PathElem::Ident(i, _) => i.clone(),
314                PathElem::Impl(elem) => match elem {
315                    ImplElem::Trait(impl_id) => match trait_name(*impl_id) {
316                        None => format!("<trait impl#{impl_id}>"),
317                        Some(name) => format!("<impl {name} for ??>"),
318                    },
319                    ImplElem::Ty(..) => "<inherent impl>".to_string(),
320                },
321                PathElem::Instantiated(..) => "<mono>".to_string(),
322                PathElem::Target(target) => target.clone(),
323                PathElem::Builtin(builtin, _) => format!("<{}>", builtin.ident()),
324            })
325            .join("::")
326    }
327}
328
329impl BuiltinPathElem {
330    /// If this builtin name is also how Rust refers to the item, in which case we don't
331    /// need to put braces around the name, as it is part of the actual path of the item.
332    pub fn is_rust_name(self) -> bool {
333        matches!(
334            self,
335            BuiltinPathElem::Str | BuiltinPathElem::Tuple(_) | BuiltinPathElem::DropGlue
336        )
337    }
338
339    /// The identifier we use to refer to this element.
340    pub fn ident(self) -> &'static str {
341        match self {
342            BuiltinPathElem::Tuple(0) => "unit",
343            BuiltinPathElem::Tuple(_) => "tuple",
344            BuiltinPathElem::Str => "str",
345            BuiltinPathElem::Closure => "closure",
346            BuiltinPathElem::Use => "use",
347            BuiltinPathElem::AnonConst => "const",
348            BuiltinPathElem::PromotedConst => "promoted_const",
349            BuiltinPathElem::ClosureAsFn => "as_fn",
350            BuiltinPathElem::DropGlue => "drop_glue",
351            BuiltinPathElem::VTable => "vtable",
352            BuiltinPathElem::VTableMethod => "vtable_method",
353            BuiltinPathElem::VTableDropShim => "vtable_drop_shim",
354        }
355    }
356}