Skip to main content

vyre_primitives/graph/
alias_registry.rs

1//! Compiler Extension Bridge: Binds lock-free aliasing to vyre_foundation.
2//!
3//! Provides the generic `OpId` interception mechanism mapping the generic query dialect AST
4//! directly onto the `union_find` registry payload.
5
6use vyre_foundation::ir::DataType;
7
8/// Stable Operation UUID identifying the Lock-Free Alias Union subkernel.
9pub const ALIAS_UNION_OP_ID: &str = "vyre-primitives::graph::alias_union";
10
11/// Descriptor for an alias-analysis extension op.
12#[derive(Debug, Clone, PartialEq, Eq)]
13pub struct AliasOpDescriptor {
14    /// Operand types accepted by the op.
15    pub inputs: Vec<DataType>,
16    /// Result type produced by the op.
17    pub output: DataType,
18    /// Human-readable operation contract.
19    pub description: &'static str,
20    /// True when argument order does not affect the result.
21    pub commutative: bool,
22    /// True when the op updates the alias data structure.
23    pub side_effects: bool,
24}
25
26impl AliasOpDescriptor {
27    /// Build the lock-free alias-union descriptor.
28    #[must_use]
29    pub fn alias_union() -> Self {
30        Self {
31            inputs: vec![DataType::U32, DataType::U32],
32            output: DataType::U32,
33            description: "Lock-free warp-accelerated union-find alias join",
34            commutative: true,
35            side_effects: true,
36        }
37    }
38}
39
40/// Registry of alias-analysis extension operations keyed by stable op id.
41#[derive(Debug, Default, Clone)]
42pub struct AliasRegistry {
43    alias_union: Option<AliasOpDescriptor>,
44    extension_ops: Vec<(&'static str, AliasOpDescriptor)>,
45}
46
47impl AliasRegistry {
48    /// Register a descriptor under a stable op id.
49    pub fn register(&mut self, op_id: &'static str, descriptor: AliasOpDescriptor) {
50        if op_id == ALIAS_UNION_OP_ID {
51            self.alias_union = Some(descriptor);
52            return;
53        }
54        match self
55            .extension_ops
56            .binary_search_by(|(registered, _)| registered.cmp(&op_id))
57        {
58            Ok(index) => self.extension_ops[index].1 = descriptor,
59            Err(index) => self.extension_ops.insert(index, (op_id, descriptor)),
60        }
61    }
62
63    /// Look up a descriptor by stable op id.
64    #[must_use]
65    pub fn get(&self, op_id: &str) -> Option<&AliasOpDescriptor> {
66        if op_id == ALIAS_UNION_OP_ID {
67            return self.alias_union.as_ref();
68        }
69        self.extension_ops
70            .binary_search_by(|(registered, _)| registered.cmp(&op_id))
71            .ok()
72            .map(|index| &self.extension_ops[index].1)
73    }
74
75    /// True when a descriptor is registered for `op_id`.
76    #[must_use]
77    pub fn contains(&self, op_id: &str) -> bool {
78        self.get(op_id).is_some()
79    }
80
81    /// Number of registered alias operations.
82    #[must_use]
83    pub fn len(&self) -> usize {
84        usize::from(self.alias_union.is_some()) + self.extension_ops.len()
85    }
86
87    /// True when no alias operations are registered.
88    #[must_use]
89    pub fn is_empty(&self) -> bool {
90        self.alias_union.is_none() && self.extension_ops.is_empty()
91    }
92
93    /// Return registered op ids in deterministic lookup order.
94    ///
95    /// The well-known alias-union op is reported first when present; extension
96    /// ops follow in their binary-search order.
97    #[must_use]
98    pub fn registered_op_ids(&self) -> Vec<&'static str> {
99        let mut ids = Vec::with_capacity(self.len());
100        if self.alias_union.is_some() {
101            ids.push(ALIAS_UNION_OP_ID);
102        }
103        ids.extend(self.extension_ops.iter().map(|(op_id, _)| *op_id));
104        ids
105    }
106}
107
108/// Registers the lock-free alias solver dynamically onto the compiler engine.
109/// When an analysis compiler encounters `x == y` under aliased semantic boundaries,
110/// the lowering phase maps the AST into this Extern execution route.
111pub fn register_alias_ops(registry: &mut AliasRegistry) {
112    registry.register(ALIAS_UNION_OP_ID, AliasOpDescriptor::alias_union());
113}
114
115/// Build the primitive-default alias operation registry.
116#[must_use]
117pub fn default_alias_registry() -> AliasRegistry {
118    let mut registry = AliasRegistry::default();
119    register_alias_ops(&mut registry);
120    registry
121}
122
123/// True when the well-known alias-union operation is registered.
124#[must_use]
125pub fn alias_union_registered(registry: &AliasRegistry) -> bool {
126    registry.contains(ALIAS_UNION_OP_ID)
127}
128
129#[cfg(test)]
130mod tests {
131    use super::*;
132
133    #[test]
134    fn default_registry_contains_alias_union_only() {
135        let registry = default_alias_registry();
136        assert_eq!(registry.len(), 1);
137        assert!(alias_union_registered(&registry));
138    }
139
140    #[test]
141    fn empty_registry_has_no_implicit_alias_union() {
142        let registry = AliasRegistry::default();
143        assert!(registry.is_empty());
144        assert!(!alias_union_registered(&registry));
145        assert!(!registry.contains(ALIAS_UNION_OP_ID));
146    }
147
148    #[test]
149    fn alias_union_descriptor_contract_is_pinned() {
150        let registry = default_alias_registry();
151        let desc = registry
152            .get(ALIAS_UNION_OP_ID)
153            .expect("Fix: default registry must contain alias-union descriptor");
154        assert_eq!(desc.inputs, vec![DataType::U32, DataType::U32]);
155        assert_eq!(desc.output, DataType::U32);
156        assert!(desc.commutative, "alias-union must be commutative");
157        assert!(desc.side_effects, "alias-union mutates union-find state");
158    }
159
160    #[test]
161    fn extension_registration_updates_without_duplicating_entries() {
162        let mut registry = default_alias_registry();
163        let mut descriptor = AliasOpDescriptor::alias_union();
164        descriptor.description = "test extension alias op";
165        descriptor.commutative = false;
166        descriptor.side_effects = false;
167
168        registry.register("vyre-primitives::graph::alias_test_ext", descriptor.clone());
169        registry.register("vyre-primitives::graph::alias_test_ext", descriptor);
170
171        assert_eq!(registry.len(), 2);
172        let ext = registry
173            .get("vyre-primitives::graph::alias_test_ext")
174            .expect("Fix: extension alias op should be registered");
175        assert_eq!(ext.description, "test extension alias op");
176        assert!(!ext.commutative);
177        assert!(!ext.side_effects);
178        assert!(alias_union_registered(&registry));
179    }
180
181    #[test]
182    fn extension_ops_are_kept_sorted_for_binary_lookup() {
183        let mut registry = default_alias_registry();
184        for op_id in [
185            "vyre-primitives::graph::alias_z",
186            "vyre-primitives::graph::alias_a",
187            "vyre-primitives::graph::alias_m",
188            "vyre-primitives::graph::alias_b",
189        ] {
190            registry.register(op_id, AliasOpDescriptor::alias_union());
191        }
192
193        let ids = registry
194            .extension_ops
195            .iter()
196            .map(|(op_id, _)| *op_id)
197            .collect::<Vec<_>>();
198        assert_eq!(
199            ids,
200            vec![
201                "vyre-primitives::graph::alias_a",
202                "vyre-primitives::graph::alias_b",
203                "vyre-primitives::graph::alias_m",
204                "vyre-primitives::graph::alias_z",
205            ],
206            "Fix: alias extension registry must stay sorted so lookup is binary-searchable."
207        );
208        assert!(registry.get("vyre-primitives::graph::alias_m").is_some());
209        assert!(registry
210            .get("vyre-primitives::graph::alias_missing")
211            .is_none());
212    }
213
214    #[test]
215    fn duplicate_extension_update_preserves_sorted_registry_position() {
216        let mut registry = default_alias_registry();
217        registry.register(
218            "vyre-primitives::graph::alias_c",
219            AliasOpDescriptor::alias_union(),
220        );
221        registry.register(
222            "vyre-primitives::graph::alias_a",
223            AliasOpDescriptor::alias_union(),
224        );
225        let mut updated = AliasOpDescriptor::alias_union();
226        updated.description = "updated alias op";
227        updated.commutative = false;
228        registry.register("vyre-primitives::graph::alias_c", updated);
229
230        assert_eq!(registry.len(), 3);
231        assert_eq!(
232            registry.extension_ops[0].0,
233            "vyre-primitives::graph::alias_a"
234        );
235        assert_eq!(
236            registry.extension_ops[1].0,
237            "vyre-primitives::graph::alias_c"
238        );
239        let desc = registry
240            .get("vyre-primitives::graph::alias_c")
241            .expect("Fix: updated alias_c descriptor must remain registered");
242        assert_eq!(desc.description, "updated alias op");
243        assert!(!desc.commutative);
244    }
245
246    #[test]
247    fn registered_op_ids_are_deterministic_and_do_not_expose_descriptors() {
248        let mut registry = default_alias_registry();
249        registry.register(
250            "vyre-primitives::graph::alias_z",
251            AliasOpDescriptor::alias_union(),
252        );
253        registry.register(
254            "vyre-primitives::graph::alias_a",
255            AliasOpDescriptor::alias_union(),
256        );
257
258        assert_eq!(
259            registry.registered_op_ids(),
260            vec![
261                ALIAS_UNION_OP_ID,
262                "vyre-primitives::graph::alias_a",
263                "vyre-primitives::graph::alias_z",
264            ]
265        );
266    }
267}