Skip to main content

hax_rust_engine/ast/
fragment.rs

1//! Enumeration types of any possible fragment of AST (`Fragment` / `FragmentRef`).
2//!
3//! Many components (diagnostics, logging, printers) want to refer to “some AST
4//! node” without knowing its concrete type. This module provides:
5//! - [`Fragment`]: an **owned** enum covering core AST node types.
6//! - [`FragmentRef`]: a **borrowed** counterpart.
7//!
8//! These are handy when implementing generic facilities such as error reporters,
9//! debugging helpers, or pretty-printers that need to branch on “what kind of
10//! node is this?” at runtime.
11//!
12//! ## Notes
13//! - Both enums are mechanically generated to stay in sync with the canonical
14//!   AST types. If you add a new core AST node, update the macro invocation at
15//!   the bottom of this file so `Fragment`/`FragmentRef` learn about it.
16//! - The [`Unknown`] variant exists as a last-resort placeholder when a value
17//!   cannot be represented by a known variant. Prefer concrete variants when
18//!   possible.
19
20use crate::ast::*;
21
22/// The `mk!` macro takes a flat list of AST type identifiers and expands to
23/// two enums:
24/// - `Fragment` with owned variants (`Foo(Foo)`), and
25/// - `FragmentRef<'a>` with borrowed variants (`Foo(&'a Foo)`).
26///
27/// The generated enums also implement the obvious `From<T>` conversions, making
28/// it ergonomic to wrap concrete AST values as fragments.
29macro_rules! mk {
30    (@visit_inner_call, Span, $self:ident, $x:expr) => {::std::ops::ControlFlow::Continue(())};
31    (@visit_inner_call, GloablId, $self:ident, $x:expr) => {::std::ops::ControlFlow::Continue(())};
32    (@visit_inner_call, $ty:ty, $self:ident, $x:expr) => {
33        $self.visit_inner($x)
34    };
35    ($($ty:ident),*) => {
36        #[derive_group_for_ast]
37        #[derive(Copy)]
38        /// Type identifiers for fragments
39        pub enum FragmentTypeId {
40            $(
41                #[doc = concat!("An identifier for the type [`", stringify!($ty), "`].")]
42                $ty,
43            )*
44        }
45
46        mod private {
47            pub use super::*;
48            pub trait Sealed {}
49            $(impl Sealed for $ty {})*
50        }
51
52        /// Operations on any fragment of the AST of hax.
53        pub trait AnyFragment: private::Sealed {
54            /// Get a type identifier for this fragment.
55            fn type_id() -> FragmentTypeId;
56            /// Coerce as a fragment reference.
57            fn as_fragment<'a>(&'a self, type_id: FragmentTypeId) -> Option<FragmentRef<'a>>;
58            /// Coerce as an owned fragment.
59            fn as_owned_fragment(&self, type_id: FragmentTypeId) -> Option<Fragment>;
60        }
61
62        $(
63            impl AnyFragment for $ty {
64                fn type_id() -> FragmentTypeId {
65                    FragmentTypeId::$ty
66                }
67                fn as_fragment<'a>(&'a self, type_id: FragmentTypeId) -> Option<FragmentRef<'a>> {
68                    if type_id == Self::type_id() {
69                        Some(self.into())
70                    } else {
71                        None
72                    }
73                }
74                fn as_owned_fragment(&self, type_id: FragmentTypeId) -> Option<Fragment> {
75                    if type_id == Self::type_id() {
76                        #[allow(unreachable_code)]
77                        Some(self.clone().into())
78                    } else {
79                        None
80                    }
81                }
82            }
83        )*
84
85        /// A marker about a sub AST fragment in a bigger AST.
86        pub struct FragmentMarker {
87            addr: usize,
88            type_id: fragment::FragmentTypeId,
89        }
90
91        impl FragmentMarker {
92            /// Creates a marker out of an AST fragment.
93            pub fn new<T: AnyFragment>(value: &T) -> Self {
94                Self {
95                    addr: (value as *const T).addr(),
96                    type_id: T::type_id(),
97                }
98            }
99        }
100
101
102        impl<'a> derive_generic_visitor::Visitor for FragmentMarker {
103            type Break = Fragment;
104        }
105
106        impl visitors::AstEarlyExitVisitor for FragmentMarker {
107            $(
108                pastey::paste!{
109                    fn [<visit_ $ty:snake>](&mut self, x: &$ty) -> ::std::ops::ControlFlow<Self::Break> {
110                        if self.addr == (x as *const $ty).addr()
111                            && let Some(fragment) = x.as_owned_fragment(self.type_id)
112                        {
113                            return ::std::ops::ControlFlow::Break(fragment);
114                        }
115                        mk!(@visit_inner_call, $ty, self, x)
116                    }
117                }
118            )*
119        }
120
121        #[derive_group_for_ast]
122        #[allow(missing_docs)]
123        /// An owned fragment of AST in hax.
124        pub enum Fragment {
125            $(
126                #[doc = concat!("An owned [`", stringify!($ty), "`] node.")]
127                $ty($ty),
128            )*
129            /// Represent an unknown node in the AST with a message.
130            Unknown(String),
131        }
132        #[derive(Copy)]
133        #[derive_group_for_ast_base]
134        #[derive(::serde::Serialize)]
135        #[allow(missing_docs)]
136        /// A borrowed fragment of AST in hax.
137        pub enum FragmentRef<'lt> {
138            $(
139                #[doc = concat!("A borrowed [`", stringify!($ty), "`] node.")]
140                $ty(&'lt $ty),
141            )*
142        }
143
144        $(
145            impl From<$ty> for Fragment {
146                fn from(fragment: $ty) -> Self {
147                    Self::$ty(fragment)
148                }
149            }
150            impl<'lt> From<&'lt $ty> for FragmentRef<'lt> {
151                fn from(fragment: &'lt $ty) -> Self {
152                    Self::$ty(fragment)
153                }
154            }
155        )*
156    };
157}
158
159#[hax_rust_engine_macros::replace(AstNodes => include(VisitableAstNodes))]
160mk!(GlobalId, Span, AstNodes);