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}