Skip to main content

xlog_core/
error.rs

1//! Error types for XLOG
2
3use thiserror::Error;
4
5/// Primary error type for XLOG operations
6#[derive(Debug, Error)]
7#[non_exhaustive]
8pub enum XlogError {
9    /// Parse error from the Datalog frontend.
10    #[error("Parse error: {0}")]
11    Parse(String),
12
13    /// Stratification failed due to a cycle through negation.
14    #[error("Stratification failed: cycle through negation involving {0:?}")]
15    StratificationCycle(Vec<String>),
16
17    /// A variable is not bound in any positive body literal (domain safety violation).
18    #[error("Domain safety: variable {0} not bound in positive literal")]
19    UnsafeVariable(String),
20
21    /// GPU memory budget exceeded.
22    #[error("Resource exhausted: {context}, estimated {estimated_bytes} bytes, budget {budget_bytes} bytes")]
23    ResourceExhausted {
24        /// Description of the operation that exceeded the budget.
25        context: String,
26        /// Estimated memory required in bytes.
27        estimated_bytes: u64,
28        /// Available memory budget in bytes.
29        budget_bytes: u64,
30    },
31
32    /// The D4 **compile** phase declined a CNF too large to compile safely
33    /// (the knowledge-compilation emit buffers are fixed-capacity; a larger
34    /// instance would overrun them and fail with a context-poisoning CUDA
35    /// launch error). A typed, catchable decline distinct from the verify-phase
36    /// signal — "too big to compile", not "verify gave up". The caller can skip
37    /// the query or fall back to an approximate engine.
38    #[error("D4 compile declined: {context}: {detail}")]
39    CompileCapacityExceeded {
40        /// Description of the compile operation that was declined.
41        context: String,
42        /// Which capacity tripped and its measured/configured values.
43        detail: String,
44    },
45
46    /// The GPU CDCL equivalence **verifier** declined rather than risk a
47    /// CUDA launch failure that poisons the primary context: a per-verify
48    /// conflict budget ran out before the search reached a definite answer
49    /// (INDETERMINATE — declined fail-closed, never trusted as a proof).
50    /// Distinct from [`CompileCapacityExceeded`] so the two phases stay
51    /// diagnosably separate. The caller can skip the query or fall back to an
52    /// approximate engine.
53    #[error("D4 equivalence verify declined: {context}: {detail}")]
54    VerifyBudgetExceeded {
55        /// Description of the verify operation that was declined.
56        context: String,
57        /// Which budget tripped and its measured/configured values.
58        detail: String,
59    },
60
61    /// GPU kernel launch or execution error.
62    #[error("Kernel error: {0}")]
63    Kernel(String),
64
65    /// Type checking or inference error.
66    #[error("Type error: {0}")]
67    Type(String),
68
69    /// Compilation pipeline error.
70    #[error("Compilation error: {0}")]
71    Compilation(String),
72
73    /// Epistemic construct is known to the frontend but unsupported in this context.
74    #[error("Unsupported epistemic construct: {construct} ({context})")]
75    UnsupportedEpistemicConstruct {
76        /// Construct that was rejected.
77        construct: String,
78        /// Context where the construct was rejected.
79        context: String,
80    },
81
82    /// A compiler-generated integrity-constraint relation contains witness rows.
83    #[error(
84        "Constraint {constraint_index} violated: {relation_name} produced {witness_rows} witness row(s)"
85    )]
86    ConstraintViolation {
87        /// Source-order constraint index encoded by the compiler-generated relation.
88        constraint_index: usize,
89        /// Compiler-generated relation that contains the violation witnesses.
90        relation_name: String,
91        /// Number of materialized violation witnesses.
92        witness_rows: usize,
93    },
94
95    /// Runtime execution error.
96    #[error("Execution error: {0}")]
97    Execution(String),
98}
99
100impl XlogError {
101    /// Create a Kernel error with structured context: "op: detail: source".
102    pub fn kernel_ctx(op: &str, detail: &str, source: &impl std::fmt::Display) -> Self {
103        XlogError::Kernel(format!("{op}: {detail}: {source}"))
104    }
105
106    /// Create an Execution error with structured context.
107    pub fn execution_ctx(op: &str, detail: &str, source: &impl std::fmt::Display) -> Self {
108        XlogError::Execution(format!("{op}: {detail}: {source}"))
109    }
110
111    /// Create a Compilation error with structured context.
112    pub fn compilation_ctx(op: &str, detail: &str, source: &impl std::fmt::Display) -> Self {
113        XlogError::Compilation(format!("{op}: {detail}: {source}"))
114    }
115}
116
117/// Result alias using XlogError
118pub type Result<T> = std::result::Result<T, XlogError>;
119
120#[cfg(test)]
121mod tests {
122    use super::*;
123
124    #[test]
125    fn test_parse_error_display() {
126        let err = XlogError::Parse("unexpected token".to_string());
127        assert_eq!(err.to_string(), "Parse error: unexpected token");
128    }
129
130    #[test]
131    fn test_stratification_cycle_display() {
132        let err = XlogError::StratificationCycle(vec!["foo".to_string(), "bar".to_string()]);
133        assert!(err.to_string().contains("foo"));
134        assert!(err.to_string().contains("bar"));
135    }
136
137    #[test]
138    fn test_resource_exhausted_display() {
139        let err = XlogError::ResourceExhausted {
140            context: "join operation".to_string(),
141            estimated_bytes: 1024,
142            budget_bytes: 512,
143        };
144        assert!(err.to_string().contains("1024"));
145        assert!(err.to_string().contains("512"));
146    }
147
148    #[test]
149    fn test_constraint_violation_display() {
150        let err = XlogError::ConstraintViolation {
151            constraint_index: 3,
152            relation_name: "__xlog_constraint_3".to_string(),
153            witness_rows: 2,
154        };
155        assert_eq!(
156            err.to_string(),
157            "Constraint 3 violated: __xlog_constraint_3 produced 2 witness row(s)"
158        );
159    }
160
161    #[test]
162    fn test_kernel_ctx() {
163        let err = XlogError::kernel_ctx("download_column", "dtoh copy failed", &"device error 42");
164        assert_eq!(
165            err.to_string(),
166            "Kernel error: download_column: dtoh copy failed: device error 42"
167        );
168    }
169
170    #[test]
171    fn test_execution_ctx() {
172        let err = XlogError::execution_ctx("execute_node", "filter failed", &"type mismatch");
173        assert_eq!(
174            err.to_string(),
175            "Execution error: execute_node: filter failed: type mismatch"
176        );
177    }
178
179    #[test]
180    fn test_compilation_ctx() {
181        let err = XlogError::compilation_ctx("compile_d4", "frontier overflow", &"limit 1024");
182        assert_eq!(
183            err.to_string(),
184            "Compilation error: compile_d4: frontier overflow: limit 1024"
185        );
186    }
187}