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);