hax_rust_engine/ast/identifiers/
global_id.rs

1//! The global identifiers of hax.
2//!
3//! ## Public API
4//! The main type provided by this module is `GlobalId`.
5//!
6//! A global identifier is either:
7//!  - a concrete identifier, something that could be represented as a Rust path
8//!  - a tuple identifier
9//!
10//! To print a global identifier, you have to use the method [`GlobalId::view`],
11//! which will output a [`view::View`].
12//!
13//! You can also try to interpret a global identifier as a tuple identifier
14//! ([`TupleId`]) via the method [`GlobalId::expect_tuple`].
15//!
16//! ## Internal representations
17//! [`GlobalId`] is a wrapper for an interned [`GlobalIdInner`].
18//!
19//! A [`GlobalIdInner`] is either a [`ConcreteId`] or a [`TupleId`]. A
20//! [`GlobalId`] can always be turned into a [`ConcreteId`].
21//!
22//! A [`ConcreteId`] is an [`ExplicitDefId`] that can be moved to fresh
23//! namespaces or suffixed with reserved suffixes.
24//!
25//! An [`ExplicitDefId`] is a [`DefId`] that adds one piece of information: is
26//! the identifier refering to a constructor or not. This information is
27//! ambiguous in Rust's `DefId`s.
28//!
29//! A [`DefId`] is an interned [`DefIdInner`], which in turn is a datatype
30//! isomorphic to the raw representation of `DefId`s in the frontend.
31//!
32//! A [`DefIdInner`] is basically a definition kind, a krate name and a path.
33
34use hax_frontend_exporter::{DefKind, DefPathItem, DisambiguatedDefPathItem};
35use hax_rust_engine_macros::*;
36
37use crate::interning::{Internable, Interned, InterningTable};
38
39mod compact_serialization;
40pub(crate) mod generated_names;
41pub mod view;
42
43/// A Rust `DefId`: a lighter version of [`hax_frontend_exporter::DefId`].
44#[derive_group_for_ast]
45struct DefIdInner {
46    /// The crate of the definition
47    krate: String,
48    /// The full path for this definition, under the crate `krate`
49    path: Vec<DisambiguatedDefPathItem>,
50    /// The parent `DefId`, if any.
51    /// `parent` if node if and only if `path` is empty
52    parent: Option<DefId>,
53    /// What kind is this definition? (e.g. an `enum`, a `const`, an assoc. `fn`...)
54    kind: DefKind,
55}
56
57impl DefIdInner {
58    fn to_debug_string(&self) -> String {
59        fn disambiguator_suffix(disambiguator: u32) -> String {
60            if disambiguator == 0 {
61                "".into()
62            } else {
63                format!("__{disambiguator}")
64            }
65        }
66        use itertools::Itertools;
67        std::iter::once(self.krate.clone())
68            .chain(self.path.iter().map(|item| match &item.data {
69                DefPathItem::TypeNs(s)
70                | DefPathItem::ValueNs(s)
71                | DefPathItem::MacroNs(s)
72                | DefPathItem::LifetimeNs(s) => s.clone(),
73                DefPathItem::Impl => "impl".into(),
74                other => format!("{other:?}"),
75            } + &disambiguator_suffix(item.disambiguator)))
76            .join("::")
77    }
78}
79
80use std::{
81    cell::{LazyCell, RefCell},
82    collections::HashMap,
83    sync::{LazyLock, Mutex},
84};
85impl Internable for DefIdInner {
86    fn interning_table() -> &'static Mutex<InterningTable<Self>> {
87        static TABLE: LazyLock<Mutex<InterningTable<DefIdInner>>> =
88            LazyLock::new(|| Mutex::new(InterningTable::default()));
89        &TABLE
90    }
91}
92
93/// An interned Rust `DefId`: a lighter version of [`hax_frontend_exporter::DefId`].
94type DefId = Interned<DefIdInner>;
95
96/// An [`ExpliciDefId`] is a Rust [`DefId`] tagged withg some disambiguation metadata.
97///
98/// [`DefId`] can be ambiguous, consider the following Rust code:
99///
100/// ```rust
101/// struct S;
102/// fn f() -> S { S }
103/// ```
104///
105/// Here, the return type of `f` (that is, `S`) and the constructor `S` in the body of `f` refer to the exact same identifier `mycrate::S`.
106/// Yet, they denote two very different objects: a type versus a constructor.
107///
108/// [`ExplicitDefId`] clears up this ambiguity, making constructors and types two separate things.
109///
110/// Also, an [`ExplicitDefId`] always points to an item: an [`ExplicitDefId`] is never pointing to a crate alone.
111#[derive_group_for_ast]
112struct ExplicitDefId {
113    /// Is this `DefId` a constructor?
114    is_constructor: bool,
115    /// The `DefId` itself
116    def_id: DefId,
117}
118
119impl ExplicitDefId {
120    /// Get the parent of an `ExplicitDefId`.
121    fn parent(&self) -> Option<Self> {
122        let def_id = &self.def_id;
123        let is_constructor = matches!(&def_id.kind, DefKind::Field);
124        Some(Self {
125            is_constructor,
126            def_id: def_id.parent?,
127        })
128    }
129    /// Returns an iterator that yields `self`, then `self.parent()`, etc.
130    /// This iterator is non-empty.
131    fn parents(&self) -> impl Iterator<Item = Self> {
132        std::iter::successors(Some(self.clone()), |id| id.parent())
133    }
134
135    /// Helper to get a `GlobalIdInner` out of an `ExplicitDefId`.
136    fn into_global_id_inner(self) -> GlobalIdInner {
137        GlobalIdInner::Concrete(ConcreteId {
138            def_id: self,
139            moved: None,
140            suffix: None,
141        })
142    }
143}
144
145/// Represents a fresh module: a module generated by hax and guaranteed to be fresh.
146#[derive_group_for_ast]
147pub struct FreshModule {
148    /// Internal (unique) identifier
149    id: usize,
150    /// Non-empty list of identifiers that will be used to decide the name of the fresh module.
151    hints: Vec<ExplicitDefId>,
152    /// A decoration label that will be also used to decide the name of the fresh module.
153    label: String,
154}
155
156/// [`ReservedSuffix`] helps at deriving fresh identifiers out of existing (Rust) ones.
157#[derive_group_for_ast]
158pub enum ReservedSuffix {
159    /// Precondition of a function-like item.
160    Pre,
161    /// Postcondition of a function-like item.
162    Post,
163    /// Cast function for an `enum` discriminant.
164    Cast,
165}
166
167/// A identifier that we call concrete: it exists concretely somewhere in Rust.
168#[derive_group_for_ast]
169pub struct ConcreteId {
170    /// The explicit `def_id`.
171    def_id: ExplicitDefId,
172    /// A fresh module if this definition was moved to a fresh module.
173    moved: Option<FreshModule>,
174    /// An optional suffix.
175    suffix: Option<ReservedSuffix>,
176}
177
178/// A global identifier in hax.
179#[derive_group_for_ast]
180enum GlobalIdInner {
181    /// A concrete identifier that exists in Rust.
182    Concrete(ConcreteId),
183    /// A projector.
184    Tuple(TupleId),
185}
186
187#[derive_group_for_ast]
188#[derive(Copy)]
189/// Represents tuple-related identifier in Rust.
190///
191/// Since Rust tuples do not have user-defined names, this type is used to
192/// represent synthesized identifiers for tuple types, their constructors, and
193/// fields. This is necessary in cases where we need to refer to these
194/// components in a structured and identifiable way.
195///
196/// For ergnomic purposes, `TupleId` can be transformed into `ConcreteId`s.
197/// After such a conversion, we loose structure, but we end up with a standard
198/// concrete identifier, which can be printed in a generic way.
199/// See [`ConcreteId::from_global_id`].
200pub enum TupleId {
201    /// Represents a tuple type with the given number of elements.
202    ///
203    /// For example, a tuple like `(i32, bool, String)` would have `length = 3`.
204    Type {
205        /// Number of elements in the tuple.
206        length: usize,
207    },
208
209    /// Represents the constructor function for a tuple with the given arity.
210    ///
211    /// This refers to the tuple expression itself (e.g., `(x, y, z)`), which constructs
212    /// a value of the tuple type.
213    Constructor {
214        /// Number of elements in the tuple.
215        length: usize,
216    },
217
218    /// Represents a field within a tuple, addressed by position.
219    ///
220    /// For instance, accessing `.0` or `.1` on a tuple corresponds to a specific field.
221    Field {
222        /// Number of elements in the tuple.
223        length: usize,
224        /// Index of the field (zero-based).
225        field: usize,
226    },
227}
228
229impl From<TupleId> for GlobalId {
230    fn from(tuple_id: TupleId) -> Self {
231        Self(GlobalIdInner::Tuple(tuple_id).intern())
232    }
233}
234
235impl From<TupleId> for ConcreteId {
236    fn from(value: TupleId) -> Self {
237        fn patch_def_id(template: GlobalId, length: usize, field: usize) -> ConcreteId {
238            let GlobalIdInner::Concrete(mut concrete_id) = template.0.get().clone() else {
239                // `patch_def_id` is called with constant values (`hax::Tuple2`
240                // and friends are constants) Those are of the shape
241                // `GlobalIdInner::Concrete(_)`, *not*
242                // `GlobalIdInner::Tuple(_)`. The tuple identifiers we deal with
243                // in this functions are private identifiers used only in this
244                // module, to provide normal concrete identifiers even for
245                // tuples.
246                unreachable!()
247            };
248            fn inner(did: &mut DefIdInner, length: usize, field: usize) {
249                for DisambiguatedDefPathItem { data, .. } in &mut did.path {
250                    // Patch field
251                    if let DefPathItem::ValueNs(s) = data
252                        && s == "1"
253                    {
254                        *s = field.to_string()
255                    }
256                    // Patch constructor / type name
257                    if let DefPathItem::TypeNs(s) = data
258                        && s.starts_with("Tuple")
259                    {
260                        *s = format!("Tuple{length}")
261                    }
262                }
263                if let Some(parent) = did.parent {
264                    let mut parent = parent.get().clone();
265                    inner(&mut parent, length, field);
266                    did.parent = Some(parent.intern());
267                }
268            }
269            let mut did = concrete_id.def_id.def_id.get().clone();
270            inner(&mut did, length, field);
271            concrete_id.def_id.def_id = did.intern();
272            concrete_id
273        }
274
275        use crate::names::rust_primitives::hax;
276
277        match value {
278            TupleId::Type { length } => patch_def_id(hax::Tuple2, length, 0),
279            TupleId::Constructor { length } => patch_def_id(hax::Tuple2::Constructor, length, 0),
280            TupleId::Field { length, field } => patch_def_id(hax::Tuple2::_1, length, field),
281        }
282    }
283}
284
285/// A interned global identifier in hax.
286#[derive_group_for_ast]
287#[derive(Copy)]
288pub struct GlobalId(Interned<GlobalIdInner>);
289
290impl GlobalId {
291    /// Extracts the Crate info
292    pub fn krate(self) -> &'static str {
293        &ConcreteId::from_global_id(self).def_id.def_id.krate
294    }
295
296    /// Returns true if this global identifier refers to a anonymous constant item.
297    /// TODO: drop this function. No logic should be derived from this.
298    pub fn is_anonymous_const(self) -> bool {
299        let def_id = self.0.get().def_id();
300        let Some(DisambiguatedDefPathItem {
301            data: DefPathItem::ValueNs(s),
302            ..
303        }) = def_id.path.last()
304        else {
305            return false;
306        };
307        matches!(self.0.get().def_id().kind, DefKind::Const) && s == "_"
308    }
309
310    /// Debug printing of identifiers, for testing purposes only.
311    /// Prints path in a Rust-like way, as a `::` separated dismabiguated path.
312    pub fn to_debug_string(self) -> String {
313        ConcreteId::from_global_id(self).to_debug_string()
314    }
315
316    /// Returns true if the underlying identifier is a constructor
317    pub fn is_constructor(self) -> bool {
318        self.0.get().is_constructor()
319    }
320
321    /// Returns true if the underlying identifier is a projector
322    pub fn is_projector(self) -> bool {
323        self.0.get().is_projector()
324    }
325
326    /// Returns true if the underlying identifier is a precondition (trait/impl item)
327    /// Should be removed once https://github.com/cryspen/hax/issues/1646 has been fixed
328    pub fn is_precondition(self) -> bool {
329        self.0.get().is_precondition()
330    }
331
332    /// Returns true if the underlying identifier is a postcondition (trait/impl item)
333    /// Should be removed once https://github.com/cryspen/hax/issues/1646 has been fixed
334    pub fn is_postcondition(self) -> bool {
335        self.0.get().is_postcondition()
336    }
337
338    /// Renders a view of the concrete identifier.
339    pub fn view(self) -> view::View {
340        ConcreteId::from_global_id(self).view()
341    }
342
343    /// Returns a tuple identifier if `self` is indeed a tuple.
344    pub fn expect_tuple(self) -> Option<TupleId> {
345        match self.0.get() {
346            GlobalIdInner::Concrete(..) => None,
347            GlobalIdInner::Tuple(tuple_id) => Some(*tuple_id),
348        }
349    }
350
351    /// Gets the closest module only parent identifier, that is, the closest
352    /// parent whose path contains only path chunks of kind `DefKind::Mod`.
353    pub fn mod_only_closest_parent(self) -> Self {
354        let concrete_id = ConcreteId::from_global_id(self).mod_only_closest_parent();
355        Self(GlobalIdInner::Concrete(concrete_id).intern())
356    }
357}
358
359impl GlobalIdInner {
360    /// Extract the raw `DefId` from a `GlobalId`.
361    /// This should never be used for name printing.
362    fn def_id(&self) -> DefId {
363        ConcreteId::from_global_id(GlobalId(self.intern()))
364            .def_id
365            .def_id
366    }
367
368    /// Extract the `ExplicitDefId` from a `GlobalId`.
369    fn explicit_def_id(&self) -> Option<ExplicitDefId> {
370        match self {
371            GlobalIdInner::Concrete(concrete_id) => Some(concrete_id.def_id.clone()),
372            GlobalIdInner::Tuple(_) => None,
373        }
374    }
375
376    /// Returns true if the underlying identifier is a constructor
377    pub fn is_constructor(&self) -> bool {
378        match self {
379            GlobalIdInner::Concrete(concrete_id) => concrete_id.def_id.is_constructor,
380            GlobalIdInner::Tuple(TupleId::Constructor { .. }) => true,
381            _ => false,
382        }
383    }
384
385    /// Returns true if the underlying identifier is a projector
386    pub fn is_projector(&self) -> bool {
387        match self {
388            GlobalIdInner::Concrete(concrete_id) => {
389                matches!(concrete_id.def_id.def_id.get().kind, DefKind::Field)
390            }
391            GlobalIdInner::Tuple(TupleId::Field { .. }) => true,
392            _ => false,
393        }
394    }
395
396    /// Returns true if the underlying identifier has the precondition suffix
397    /// Should be removed once https://github.com/cryspen/hax/issues/1646 has been fixed
398    pub fn is_precondition(&self) -> bool {
399        matches!(self, GlobalIdInner::Concrete(concrete_id) if matches!(concrete_id.suffix, Some(ReservedSuffix::Pre)))
400    }
401
402    /// Returns true if the underlying identifier has the postcondition suffix
403    /// Should be removed once https://github.com/cryspen/hax/issues/1646 has been fixed
404    pub fn is_postcondition(&self) -> bool {
405        matches!(self, GlobalIdInner::Concrete(concrete_id) if matches!(concrete_id.suffix, Some(ReservedSuffix::Post)))
406    }
407}
408
409impl ConcreteId {
410    /// Renders a view of the concrete identifier.
411    fn view(&self) -> view::View {
412        self.def_id.clone().into()
413    }
414
415    /// Gets the closest module only parent identifier, that is, the closest
416    /// parent whose path contains only path chunks of kind `DefKind::Mod`.
417    fn mod_only_closest_parent(&self) -> Self {
418        let mut parents = self.def_id.parents().collect::<Vec<_>>();
419        parents.reverse();
420        let def_id = parents
421            .into_iter()
422            .take_while(|id| matches!(id.def_id.kind, DefKind::Mod))
423            .next()
424            .expect("Invariant broken: a DefId must always contain at least on `mod` segment (the crate)");
425        Self {
426            def_id,
427            moved: self.moved.clone(),
428            suffix: None,
429        }
430    }
431
432    /// Get a static reference to a `ConcreteId` out of a `GlobalId`.
433    /// When a tuple is encountered, the tuple is rendered into a proper Rust name.
434    /// This function is memoized, so that we don't recompute Rust names for tuples all the time.
435    fn from_global_id(value: GlobalId) -> &'static ConcreteId {
436        thread_local! {
437            static MEMO: LazyCell<RefCell<HashMap<GlobalId, &'static ConcreteId>>> =
438                LazyCell::new(|| RefCell::new(HashMap::new()));
439        }
440
441        MEMO.with(|memo| {
442            let mut memo = memo.borrow_mut();
443            let reference: &'static ConcreteId =
444                memo.entry(value).or_insert_with(|| match value.0.get() {
445                    GlobalIdInner::Concrete(concrete_id) => concrete_id,
446                    GlobalIdInner::Tuple(tuple_id) => {
447                        match GlobalIdInner::Concrete((*tuple_id).into()).intern().get() {
448                            GlobalIdInner::Concrete(concrete_id) => concrete_id,
449                            GlobalIdInner::Tuple(_) => unreachable!(),
450                        }
451                    }
452                });
453            reference
454        })
455    }
456
457    fn to_debug_string(&self) -> String {
458        self.def_id.def_id.get().to_debug_string()
459    }
460}
461
462impl PartialEq<DefId> for GlobalId {
463    fn eq(&self, other: &DefId) -> bool {
464        if let GlobalIdInner::Concrete(concrete) = self.0.get() {
465            &concrete.def_id.def_id == other
466        } else {
467            false
468        }
469    }
470}
471impl PartialEq<GlobalId> for DefId {
472    fn eq(&self, other: &GlobalId) -> bool {
473        other == self
474    }
475}
476
477impl PartialEq<ExplicitDefId> for GlobalId {
478    fn eq(&self, other: &ExplicitDefId) -> bool {
479        self == &other.def_id
480    }
481}
482
483impl PartialEq<GlobalId> for ExplicitDefId {
484    fn eq(&self, other: &GlobalId) -> bool {
485        other == &self.def_id
486    }
487}