Skip to main content

softgpu_functional/
error.rs

1//! Fail-closed functional-execution errors.
2
3use crate::sanitize::Finding;
4use std::fmt;
5
6pub type Result<T> = std::result::Result<T, FunctionalError>;
7
8#[derive(Debug, Clone, PartialEq, Eq)]
9pub enum FunctionalError {
10    Io(String),
11    Parse(String),
12    Unsupported {
13        detail: String,
14    },
15    Validation {
16        detail: String,
17    },
18    Bounds {
19        addr: u64,
20        size: usize,
21        arena_len: usize,
22    },
23    UndefinedReg {
24        name: String,
25    },
26    StepBudgetExceeded {
27        steps: u64,
28    },
29    /// SoftGPU Phase 8 sanitizer finding (fail-fast or hard fault).
30    Sanitize(Finding),
31    /// SoftGPU Phase 9 debugger requested a break (snapshot lives in DebugSession).
32    DebugBreak,
33    Internal(String),
34}
35
36impl fmt::Display for FunctionalError {
37    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
38        match self {
39            Self::Io(s) => write!(f, "io: {s}"),
40            Self::Parse(s) => write!(f, "parse: {s}"),
41            Self::Unsupported { detail } => write!(f, "unsupported: {detail}"),
42            Self::Validation { detail } => write!(f, "validation: {detail}"),
43            Self::Bounds {
44                addr,
45                size,
46                arena_len,
47            } => write!(
48                f,
49                "bounds: addr={addr:#x} size={size} arena_len={arena_len}"
50            ),
51            Self::UndefinedReg { name } => write!(f, "undefined register '{name}'"),
52            Self::StepBudgetExceeded { steps } => {
53                write!(f, "step budget exceeded after {steps} steps")
54            }
55            Self::Sanitize(finding) => write!(
56                f,
57                "sanitize: {:?} space={:?} addr={:#x} detail={}",
58                finding.kind, finding.space, finding.addr, finding.detail
59            ),
60            Self::DebugBreak => write!(f, "debug: breakpoint"),
61            Self::Internal(s) => write!(f, "internal: {s}"),
62        }
63    }
64}
65
66impl std::error::Error for FunctionalError {}
67
68#[cfg(test)]
69mod tests {
70    use super::*;
71
72    #[test]
73    fn display_covers_variants() {
74        let cases = [
75            FunctionalError::Io("x".into()),
76            FunctionalError::Parse("p".into()),
77            FunctionalError::Unsupported { detail: "u".into() },
78            FunctionalError::Validation { detail: "v".into() },
79            FunctionalError::Bounds {
80                addr: 0x10,
81                size: 4,
82                arena_len: 2,
83            },
84            FunctionalError::UndefinedReg { name: "r0".into() },
85            FunctionalError::StepBudgetExceeded { steps: 9 },
86            FunctionalError::Sanitize(crate::sanitize::Finding {
87                kind: crate::sanitize::FindingKind::Race,
88                space: crate::ir::AddrSpace::Global,
89                addr: 0,
90                size: 4,
91                step: 1,
92                barrier_gen: 0,
93                actor: crate::sanitize::WorkItemId {
94                    workgroup: [0, 0, 0],
95                    wave: 0,
96                    lane: 0,
97                    flat_local: 0,
98                },
99                other: None,
100                detail: "t".into(),
101            }),
102            FunctionalError::DebugBreak,
103            FunctionalError::Internal("i".into()),
104        ];
105        for err in cases {
106            let s = err.to_string();
107            assert!(!s.is_empty(), "{err:?}");
108        }
109    }
110}