Skip to main content

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 From<hax_frontend_exporter::DefId> for DefIdInner {
58    fn from(value: hax_frontend_exporter::DefId) -> Self {
59        Self {
60            krate: value.krate.clone(),
61            path: value.path.clone(),
62            parent: value
63                .parent
64                .clone()
65                .map(|def_id| DefIdInner::from(def_id).intern()),
66            kind: value.kind.clone(),
67        }
68    }
69}
70
71impl DefIdInner {
72    /// Change the krate field of `self` and propagate the change into all parents.
73    fn rename_krate(&self, name: &str) -> Self {
74        let mut def_id = self.clone();
75        def_id.krate = name.into();
76        def_id.parent = def_id.parent.map(|parent: DefId| parent.rename_krate(name));
77        def_id
78    }
79
80    fn to_debug_string(&self) -> String {
81        fn disambiguator_suffix(disambiguator: u32) -> String {
82            if disambiguator == 0 {
83                "".into()
84            } else {
85                format!("__{disambiguator}")
86            }
87        }
88        use itertools::Itertools;
89        std::iter::once(self.krate.clone())
90            .chain(self.path.iter().map(|item| match &item.data {
91                DefPathItem::TypeNs(s)
92                | DefPathItem::ValueNs(s)
93                | DefPathItem::MacroNs(s)
94                | DefPathItem::LifetimeNs(s) => s.clone(),
95                DefPathItem::Impl => "impl".into(),
96                other => format!("{other:?}"),
97            } + &disambiguator_suffix(item.disambiguator)))
98            .join("::")
99    }
100}
101
102use std::{
103    cell::{LazyCell, RefCell},
104    collections::HashMap,
105    sync::{LazyLock, Mutex},
106};
107impl Internable for DefIdInner {
108    fn interning_table() -> &'static Mutex<InterningTable<Self>> {
109        static TABLE: LazyLock<Mutex<InterningTable<DefIdInner>>> =
110            LazyLock::new(|| Mutex::new(InterningTable::default()));
111        &TABLE
112    }
113}
114
115/// An interned Rust `DefId`: a lighter version of [`hax_frontend_exporter::DefId`].
116type DefId = Interned<DefIdInner>;
117
118impl DefId {
119    /// Change the krate name to `name`.
120    fn rename_krate(&self, name: &str) -> Self {
121        (*self).get().rename_krate(name).intern()
122    }
123}
124
125/// An [`ExpliciDefId`] is a Rust [`DefId`] tagged withg some disambiguation metadata.
126///
127/// [`DefId`] can be ambiguous, consider the following Rust code:
128///
129/// ```rust
130/// struct S;
131/// fn f() -> S { S }
132/// ```
133///
134/// 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`.
135/// Yet, they denote two very different objects: a type versus a constructor.
136///
137/// [`ExplicitDefId`] clears up this ambiguity, making constructors and types two separate things.
138///
139/// Also, an [`ExplicitDefId`] always points to an item: an [`ExplicitDefId`] is never pointing to a crate alone.
140#[derive_group_for_ast]
141struct ExplicitDefId {
142    /// Is this `DefId` a constructor?
143    is_constructor: bool,
144    /// The `DefId` itself
145    def_id: DefId,
146}
147
148impl ExplicitDefId {
149    /// Get the parent of an `ExplicitDefId`.
150    fn parent(&self) -> Option<Self> {
151        let def_id = &self.def_id;
152        let is_constructor = matches!(&def_id.kind, DefKind::Field);
153        Some(Self {
154            is_constructor,
155            def_id: def_id.parent?,
156        })
157    }
158    /// Returns an iterator that yields `self`, then `self.parent()`, etc.
159    /// This iterator is non-empty.
160    fn parents(&self) -> impl Iterator<Item = Self> {
161        std::iter::successors(Some(self.clone()), |id| id.parent())
162    }
163
164    /// Change the krate name to `name`.
165    fn rename_krate(&mut self, name: &str) {
166        self.def_id = self.def_id.rename_krate(name);
167    }
168
169    /// Helper to get a `GlobalIdInner` out of an `ExplicitDefId`.
170    fn into_global_id_inner(self) -> GlobalIdInner {
171        GlobalIdInner::Concrete(ConcreteId {
172            def_id: self,
173            moved: None,
174            suffix: None,
175        })
176    }
177}
178
179/// Represents a fresh module: a module generated by hax and guaranteed to be fresh.
180#[derive_group_for_ast]
181pub struct FreshModule {
182    /// Internal (unique) identifier
183    id: usize,
184    /// Non-empty list of identifiers that will be used to decide the name of the fresh module.
185    hints: Vec<ExplicitDefId>,
186    /// A decoration label that will be also used to decide the name of the fresh module.
187    label: String,
188}
189
190impl FreshModule {
191    /// Renders a view of the fresh module identifier.
192    fn view(&self) -> view::View {
193        self.clone().into()
194    }
195
196    /// Change the krate name in all hints.
197    fn rename_krate(&self, name: &str) -> Self {
198        let hints = self
199            .hints
200            .iter()
201            .map(|hint| {
202                let mut hint = hint.clone();
203                hint.rename_krate(name);
204                hint
205            })
206            .collect();
207        Self {
208            hints,
209            id: self.id,
210            label: self.label.clone(),
211        }
212    }
213
214    fn to_debug_string(&self) -> String {
215        format!("fresh_module_{}_{}", self.id, self.label)
216    }
217}
218
219/// [`ReservedSuffix`] helps at deriving fresh identifiers out of existing (Rust) ones.
220#[derive_group_for_ast]
221pub enum ReservedSuffix {
222    /// Precondition of a function-like item.
223    Pre,
224    /// Postcondition of a function-like item.
225    Post,
226    /// Cast function for an `enum` discriminant.
227    Cast,
228}
229
230/// A identifier that we call concrete: it exists concretely somewhere in Rust.
231#[derive_group_for_ast]
232pub struct ConcreteId {
233    /// The explicit `def_id`.
234    def_id: ExplicitDefId,
235    /// A fresh module if this definition was moved to a fresh module.
236    moved: Option<FreshModule>,
237    /// An optional suffix.
238    suffix: Option<ReservedSuffix>,
239}
240
241/// A global identifier in hax.
242#[derive_group_for_ast]
243enum GlobalIdInner {
244    /// A concrete identifier that exists in Rust.
245    Concrete(ConcreteId),
246    /// A fresh module introduced by Hax (typically, a bundle)
247    FreshModule(FreshModule),
248    /// A projector.
249    Tuple(TupleId),
250}
251
252#[derive_group_for_ast]
253#[derive(Copy)]
254/// Represents tuple-related identifier in Rust.
255///
256/// Since Rust tuples do not have user-defined names, this type is used to
257/// represent synthesized identifiers for tuple types, their constructors, and
258/// fields. This is necessary in cases where we need to refer to these
259/// components in a structured and identifiable way.
260///
261/// For ergnomic purposes, `TupleId` can be transformed into `ConcreteId`s.
262/// After such a conversion, we loose structure, but we end up with a standard
263/// concrete identifier, which can be printed in a generic way.
264/// See [`ConcreteId::from_global_id`].
265pub enum TupleId {
266    /// Represents a tuple type with the given number of elements.
267    ///
268    /// For example, a tuple like `(i32, bool, String)` would have `length = 3`.
269    Type {
270        /// Number of elements in the tuple.
271        length: usize,
272    },
273
274    /// Represents the constructor function for a tuple with the given arity.
275    ///
276    /// This refers to the tuple expression itself (e.g., `(x, y, z)`), which constructs
277    /// a value of the tuple type.
278    Constructor {
279        /// Number of elements in the tuple.
280        length: usize,
281    },
282
283    /// Represents a field within a tuple, addressed by position.
284    ///
285    /// For instance, accessing `.0` or `.1` on a tuple corresponds to a specific field.
286    Field {
287        /// Number of elements in the tuple.
288        length: usize,
289        /// Index of the field (zero-based).
290        field: usize,
291    },
292}
293
294impl From<TupleId> for GlobalId {
295    fn from(tuple_id: TupleId) -> Self {
296        Self(GlobalIdInner::Tuple(tuple_id).intern())
297    }
298}
299
300impl TupleId {
301    /// Creates a ConcreteId from a TupleId: `Tuple(1)` returns `Tuple1`
302    fn into_owned_concrete_id(self) -> ConcreteId {
303        fn patch_def_id(template: GlobalId, length: usize, field: usize) -> ConcreteId {
304            let GlobalIdInner::Concrete(mut concrete_id) = template.0.get().clone() else {
305                // `patch_def_id` is called with constant values (`hax::Tuple2`
306                // and friends are constants) Those are of the shape
307                // `GlobalIdInner::Concrete(_)`, *not*
308                // `GlobalIdInner::Tuple(_)`. The tuple identifiers we deal with
309                // in this functions are private identifiers used only in this
310                // module, to provide normal concrete identifiers even for
311                // tuples.
312                unreachable!()
313            };
314            fn inner(did: &mut DefIdInner, length: usize, field: usize) {
315                for DisambiguatedDefPathItem { data, .. } in &mut did.path {
316                    // Patch field
317                    if let DefPathItem::ValueNs(s) = data
318                        && s == "1"
319                    {
320                        *s = field.to_string()
321                    }
322                    // Patch constructor / type name
323                    if let DefPathItem::TypeNs(s) = data
324                        && s.starts_with("Tuple")
325                    {
326                        *s = format!("Tuple{length}")
327                    }
328                }
329                if let Some(parent) = did.parent {
330                    let mut parent = parent.get().clone();
331                    inner(&mut parent, length, field);
332                    did.parent = Some(parent.intern());
333                }
334            }
335            let mut did = concrete_id.def_id.def_id.get().clone();
336            inner(&mut did, length, field);
337            concrete_id.def_id.def_id = did.intern();
338            concrete_id
339        }
340
341        use crate::names::rust_primitives::hax;
342
343        match self {
344            TupleId::Type { length } => patch_def_id(hax::Tuple2, length, 0),
345            TupleId::Constructor { length } => patch_def_id(hax::Tuple2::Constructor, length, 0),
346            TupleId::Field { length, field } => patch_def_id(hax::Tuple2::_1, length, field),
347        }
348    }
349
350    /// Creates a static [`ConcreteId`] from a [`TupleId`]: `Tuple(1)` returns `Tuple1`. The function is
351    /// memoized (as the same tuple ids may appear a lot in a program), and inserts identifiers in
352    /// the GlobalId table to return a static lifetime.
353    pub fn as_concreteid(self) -> &'static ConcreteId {
354        thread_local! {
355            static MEMO: LazyCell<RefCell<HashMap<TupleId, &'static ConcreteId>>> =
356                LazyCell::new(|| RefCell::new(HashMap::new()));
357        }
358
359        MEMO.with(|memo| {
360            let mut memo = memo.borrow_mut();
361            let reference: &'static ConcreteId = memo.entry(self).or_insert_with(|| {
362                match GlobalIdInner::Concrete(self.into_owned_concrete_id())
363                    .intern()
364                    .get()
365                {
366                    GlobalIdInner::Concrete(concrete_id) => concrete_id,
367                    GlobalIdInner::FreshModule(_) | GlobalIdInner::Tuple(_) => {
368                        // This is a match on the Id that was just inserted in the table as a
369                        // ConcreteId
370                        unreachable!()
371                    }
372                }
373            });
374            reference
375        })
376    }
377}
378
379/// A interned global identifier in hax.
380#[derive_group_for_ast]
381#[derive(Copy)]
382pub struct GlobalId(Interned<GlobalIdInner>);
383
384impl GlobalId {
385    /// Import a def_id from the frontend
386    pub fn from_frontend(id: hax_frontend_exporter::DefId, is_value: bool) -> Self {
387        let mut def_id: DefIdInner = id.into();
388        use hax_frontend_exporter::DefKind as DK;
389
390        let mut popped_ctor = false;
391        if let Some(last) = def_id.path.last()
392            && matches!(&last.data, DefPathItem::Ctor)
393        {
394            def_id.path.pop();
395            popped_ctor = true;
396            if let Some(parent) = def_id.parent.as_ref() {
397                def_id.parent = parent.parent;
398            }
399        }
400
401        let is_constructor = is_value
402            && (matches!(&def_id.kind, DK::Variant | DK::Union | DK::Struct) || popped_ctor);
403        let inner = GlobalIdInner::Concrete(ConcreteId {
404            def_id: ExplicitDefId {
405                is_constructor,
406                def_id: def_id.intern(),
407            },
408            moved: None,
409            suffix: None,
410        });
411        Self(inner.intern())
412    }
413
414    /// Extracts the Crate info
415    pub fn krate(self) -> &'static str {
416        match self.0.get() {
417            GlobalIdInner::FreshModule(fresh_module) => {
418                &fresh_module
419                    .hints
420                    .first()
421                    .expect("The hint list should always be non-empty")
422                    .def_id
423                    .krate
424            }
425            GlobalIdInner::Concrete(concrete_id) => &concrete_id.def_id.def_id.krate,
426            GlobalIdInner::Tuple(tuple_id) => &tuple_id.as_concreteid().def_id.def_id.krate,
427        }
428    }
429
430    /// Debug printing of identifiers, for testing purposes only.
431    /// Prints path in a Rust-like way, as a `::` separated dismabiguated path.
432    pub fn to_debug_string(self) -> String {
433        match self.0.get() {
434            GlobalIdInner::Concrete(id) => id.to_debug_string(),
435            GlobalIdInner::FreshModule(id) => id.to_debug_string(),
436            GlobalIdInner::Tuple(id) => id.as_concreteid().to_debug_string(),
437        }
438    }
439
440    /// Returns true if the underlying identifier is a constructor
441    pub fn is_constructor(self) -> bool {
442        self.0.get().is_constructor()
443    }
444
445    /// Returns true if the underlying identifier is a projector
446    pub fn is_projector(self) -> bool {
447        self.0.get().is_projector()
448    }
449
450    /// Returns true if the underlying identifier is a precondition (trait/impl item)
451    /// Should be removed once https://github.com/cryspen/hax/issues/1646 has been fixed
452    pub fn is_precondition(self) -> bool {
453        self.0.get().is_precondition()
454    }
455
456    /// Returns true if the underlying identifier is a postcondition (trait/impl item)
457    /// Should be removed once https://github.com/cryspen/hax/issues/1646 has been fixed
458    pub fn is_postcondition(self) -> bool {
459        self.0.get().is_postcondition()
460    }
461
462    /// Renders a view of the global identifier.
463    pub fn view(self) -> view::View {
464        match self.0.get() {
465            GlobalIdInner::FreshModule(id) => id.view(),
466            GlobalIdInner::Concrete(id) => id.view(),
467            GlobalIdInner::Tuple(id) => id.as_concreteid().view(),
468        }
469    }
470
471    /// Returns a tuple identifier if `self` is indeed a tuple.
472    pub fn expect_tuple(self) -> Option<TupleId> {
473        match self.0.get() {
474            GlobalIdInner::Tuple(tuple_id) => Some(*tuple_id),
475            _ => None,
476        }
477    }
478
479    /// Gets the closest module only parent identifier, that is, the closest parent whose path
480    /// contains only path chunks of kind `DefKind::Mod`. Can be itself (for fresh modules).
481    pub fn mod_only_closest_parent(self) -> Self {
482        match self.0.get() {
483            GlobalIdInner::FreshModule(_) => self,
484            GlobalIdInner::Concrete(concrete_id) => concrete_id.mod_only_closest_parent().into(),
485            GlobalIdInner::Tuple(tuple_id) => {
486                tuple_id.as_concreteid().mod_only_closest_parent().into()
487            }
488        }
489    }
490
491    /// Change the krate name (the first element of the `GlobalId`) to `name`.
492    pub fn rename_krate(self, name: &str) -> Self {
493        match self.0.get() {
494            GlobalIdInner::FreshModule(fresh_module) => {
495                Self(GlobalIdInner::FreshModule(fresh_module.rename_krate(name)).intern())
496            }
497            GlobalIdInner::Concrete(concrete_id) => {
498                let mut concrete_id = concrete_id.clone();
499                concrete_id.rename_krate(name);
500                Self(GlobalIdInner::Concrete(concrete_id).intern())
501            }
502            GlobalIdInner::Tuple(tuple_id) => {
503                let mut concrete_id = tuple_id.as_concreteid().clone();
504                concrete_id.rename_krate(name);
505                Self(GlobalIdInner::Concrete(concrete_id).intern())
506            }
507        }
508    }
509
510    /// Add a suffix to a GlobalId
511    pub fn with_suffix(self, suffix: ReservedSuffix) -> Self {
512        match self.0.get() {
513            GlobalIdInner::Concrete(concrete_id) => Self(
514                GlobalIdInner::Concrete(ConcreteId {
515                    suffix: Some(suffix),
516                    ..concrete_id.clone()
517                })
518                .intern(),
519            ),
520            GlobalIdInner::Tuple(_) | GlobalIdInner::FreshModule(_) => self,
521        }
522    }
523}
524
525impl GlobalIdInner {
526    /// Extract the `ExplicitDefId` from a `GlobalId`.
527    fn explicit_def_id(&self) -> Option<ExplicitDefId> {
528        match self {
529            GlobalIdInner::Concrete(concrete_id) => Some(concrete_id.def_id.clone()),
530            _ => None,
531        }
532    }
533
534    /// Returns true if the underlying identifier is a constructor
535    pub fn is_constructor(&self) -> bool {
536        match self {
537            GlobalIdInner::Concrete(concrete_id) => concrete_id.def_id.is_constructor,
538            GlobalIdInner::Tuple(TupleId::Constructor { .. }) => true,
539            _ => false,
540        }
541    }
542
543    /// Returns true if the underlying identifier is a projector
544    pub fn is_projector(&self) -> bool {
545        match self {
546            GlobalIdInner::Concrete(concrete_id) => {
547                matches!(concrete_id.def_id.def_id.get().kind, DefKind::Field)
548            }
549            GlobalIdInner::Tuple(TupleId::Field { .. }) => true,
550            _ => false,
551        }
552    }
553
554    /// Returns true if the underlying identifier has the precondition suffix
555    /// Should be removed once https://github.com/cryspen/hax/issues/1646 has been fixed
556    pub fn is_precondition(&self) -> bool {
557        matches!(self, GlobalIdInner::Concrete(concrete_id) if matches!(concrete_id.suffix, Some(ReservedSuffix::Pre)))
558    }
559
560    /// Returns true if the underlying identifier has the postcondition suffix
561    /// Should be removed once https://github.com/cryspen/hax/issues/1646 has been fixed
562    pub fn is_postcondition(&self) -> bool {
563        matches!(self, GlobalIdInner::Concrete(concrete_id) if matches!(concrete_id.suffix, Some(ReservedSuffix::Post)))
564    }
565}
566
567impl From<ConcreteId> for GlobalId {
568    fn from(concrete_id: ConcreteId) -> Self {
569        Self(GlobalIdInner::Concrete(concrete_id).intern())
570    }
571}
572
573impl ConcreteId {
574    /// Renders a view of the concrete identifier.
575    fn view(&self) -> view::View {
576        view::View::from(self.def_id.clone()).with_suffix(self.suffix.clone())
577    }
578
579    /// Gets the closest module only parent identifier, that is, the closest
580    /// parent whose path contains only path chunks of kind `DefKind::Mod`.
581    fn mod_only_closest_parent(&self) -> Self {
582        let mut parents = self.def_id.parents().collect::<Vec<_>>();
583        parents.reverse();
584        let def_id = parents
585            .into_iter()
586            .take_while(|id| matches!(id.def_id.kind, DefKind::Mod))
587            .last()
588            .expect("Invariant broken: a DefId must always contain at least on `mod` segment (the crate)");
589        Self {
590            def_id,
591            moved: self.moved.clone(),
592            suffix: None,
593        }
594    }
595
596    fn rename_krate(&mut self, name: &str) {
597        self.def_id.rename_krate(name);
598    }
599
600    fn to_debug_string(&self) -> String {
601        self.def_id.def_id.get().to_debug_string()
602    }
603}
604
605impl PartialEq<DefId> for GlobalId {
606    fn eq(&self, other: &DefId) -> bool {
607        if let GlobalIdInner::Concrete(concrete) = self.0.get() {
608            &concrete.def_id.def_id == other
609        } else {
610            false
611        }
612    }
613}
614impl PartialEq<GlobalId> for DefId {
615    fn eq(&self, other: &GlobalId) -> bool {
616        other == self
617    }
618}
619
620impl PartialEq<ExplicitDefId> for GlobalId {
621    fn eq(&self, other: &ExplicitDefId) -> bool {
622        self == &other.def_id
623    }
624}
625
626impl PartialEq<GlobalId> for ExplicitDefId {
627    fn eq(&self, other: &GlobalId) -> bool {
628        other == &self.def_id
629    }
630}