1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
//! Extension trait for downstream crates to modify WGSL IR after transpilation
//! but before rendering/instantiation.
//!
//! Extensions are specified via the `extensions` attribute on the `#[wgsl]`
//! macro:
//!
//! ```ignore
//! #[wgsl(crate_path = crate, extensions = [my_crate::MyExt])]
//! pub mod my_shader { ... }
//! ```
//!
//! Each extension type must implement [`WgslExtension`]. The
//! [`modify_ir`](WgslExtension::modify_ir) method is called on the
//! base/template IR module **before** type instantiation, so modifications made
//! by extensions are carried through every `instantiate()` call.
//!
//! # Statement macro lowering
//!
//! When the `#[wgsl]` parser encounters a statement macro that is not one of
//! its builtins (`slab_copy!`, `discard!`), it
//! passes it through as a [`crate::ir::Stmt::Macro`] variant instead of
//! rejecting it. An extension can then recognize the macro by name in
//! `modify_ir` and replace the `Stmt::Macro` with lowered IR.
//!
//! Extensions declare which macro names they handle via the
//! [`MACROS`](WgslExtension::MACROS) associated const. The `#[wgsl]` macro
//! emits a compile-time `const` check ensuring every `Stmt::Macro` in the
//! module is claimed by at least one listed extension — so a typo or
//! missing extension is a compile error, not a runtime error.
/// A trait for modifying WGSL IR modules after transpilation.
///
/// Extensions can inspect IR items (including their preserved Rust attributes)
/// and add, remove, or modify items. The IR module is passed mutably, so
/// extensions can make any changes they need.
///
/// # Statement macro lowering
///
/// Extensions that lower statement macros (see [`crate::ir::Stmt::Macro`])
/// should declare the macro names they handle in [`MACROS`](Self::MACROS). The
/// `#[wgsl]` macro reads this const at compile time to verify every
/// `Stmt::Macro` in the module is claimed by at least one listed extension.
///
/// # Example
///
/// ```ignore
/// use wgsl_rs::ir;
/// use wgsl_rs::WgslExtension;
///
/// pub struct SlabItemExt;
///
/// impl WgslExtension for SlabItemExt {
/// const MACROS: &'static [&'static str] = &["slab_read", "slab_write"];
///
/// fn modify_ir(module: &mut ir::Module) {
/// // Find structs with #[derive(SlabItem)] in their attrs
/// let slab_structs: Vec<(String, Vec<ir::Field>)> = module.items.iter()
/// .filter_map(|item| match item {
/// ir::Item::Struct(s) if has_derive(&s.attrs, "SlabItem") => {
/// Some((s.name.clone(), s.fields.clone()))
/// }
/// _ => None,
/// })
/// .collect();
///
/// // Add helper functions for each slab struct
/// for (name, fields) in slab_structs {
/// module.items.push(ir::Item::Fn(make_slab_read_fn(&name, &fields)));
/// module.items.push(ir::Item::Fn(make_slab_write_fn(&name, &fields)));
/// }
/// }
/// }
/// ```
// ---------------------------------------------------------------------------
// Compile-time macro-claim verification helpers
// ---------------------------------------------------------------------------
/// Check if a `&str` slice contains a given needle, byte-by-byte.
///
/// This is a `const fn` because `str::PartialEq` is not yet stable in
/// `const` context. The `#[wgsl]` macro emits `const _: () = { ... }`
/// blocks that call this to verify every `Stmt::Macro` name is claimed by
/// at least one listed extension's `MACROS` const.
pub const
/// Check if any extension's `MACROS` list contains the given macro name.
///
/// `exts` is a slice of `MACROS` slices — one per extension listed in
/// `extensions = [...]`. Returns `true` if at least one extension claims
/// the macro.
pub const