vyre_spec/category.rs
1//! Frozen operation-category records used to separate composition from intrinsics.
2
3/// Backend support predicate for a Category C operation.
4pub trait BackendAvailability: Send + Sync {
5 /// Return true when the backend identified by `op` supports the operation.
6 fn available(&self, op: &str) -> bool;
7}
8
9impl<F> BackendAvailability for F
10where
11 F: Fn(&str) -> bool + Send + Sync,
12{
13 fn available(&self, op: &str) -> bool {
14 self(op)
15 }
16}
17
18/// Function-pointer wrapper for static backend-availability predicates.
19#[derive(Clone, Copy)]
20pub struct BackendAvailabilityPredicate {
21 predicate: fn(&str) -> bool,
22}
23
24impl BackendAvailabilityPredicate {
25 /// Create a backend-availability predicate from a total function.
26 #[must_use]
27 pub const fn new(predicate: fn(&str) -> bool) -> Self {
28 Self { predicate }
29 }
30
31 /// Return true when the named backend supports the operation.
32 #[must_use]
33 pub fn available(&self, op: &str) -> bool {
34 <Self as BackendAvailability>::available(self, op)
35 }
36}
37
38impl core::fmt::Debug for BackendAvailabilityPredicate {
39 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
40 f.write_str("BackendAvailabilityPredicate(..)")
41 }
42}
43
44impl BackendAvailability for BackendAvailabilityPredicate {
45 fn available(&self, op: &str) -> bool {
46 (self.predicate)(op)
47 }
48}
49
50/// vyre operation category in the frozen data contract.
51///
52/// Category B — runtime opcode dispatch, stack-machine VMs, and eval engines
53/// — is intentionally absent from this enum. vyre has no opcode interpreter
54/// and no execution path that is not a lowered IR program on a backend, so
55/// Category B is forbidden by the conformance model rather than specified
56/// as a valid operation category. Example: `Category::Intrinsic` records that an op is
57/// backed by a named hardware capability such as a subgroup intrinsic.
58#[derive(Debug, Clone)]
59#[non_exhaustive]
60pub enum Category {
61 /// Compositional operation that must disappear after lowering.
62 A {
63 /// Operation IDs that define the zero-overhead composition.
64 composition_of: Vec<&'static str>,
65 },
66 /// Hardware intrinsic with declared per-backend availability.
67 C {
68 /// Hardware unit or backend feature required by the intrinsic.
69 hardware: &'static str,
70 /// Predicate that returns true when the backend supports this op.
71 backend_availability: BackendAvailabilityPredicate,
72 },
73}
74
75impl Category {
76 /// Temporary marker used until every operation receives a real category.
77 #[must_use]
78 pub fn unclassified() -> Self {
79 Self::A {
80 composition_of: Vec::new(),
81 }
82 }
83
84 /// True when the category is the compile-only empty Category A marker.
85 #[must_use]
86 pub fn is_unclassified(&self) -> bool {
87 matches!(self, Self::A { composition_of } if composition_of.is_empty())
88 }
89}
90
91impl PartialEq for Category {
92 fn eq(&self, other: &Self) -> bool {
93 match (self, other) {
94 (
95 Self::A {
96 composition_of: left,
97 },
98 Self::A {
99 composition_of: right,
100 },
101 ) => left == right,
102 (
103 Self::C { hardware: left, .. },
104 Self::C {
105 hardware: right, ..
106 },
107 ) => left == right,
108 _ => false,
109 }
110 }
111}
112
113impl Eq for Category {}