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