calyx_utils/
errors.rs

1//! Errors generated by the compiler.
2use crate::{GPosIdx, Id, WithPos};
3
4/// Convience wrapper to represent success or meaningul compiler error.
5pub type CalyxResult<T> = std::result::Result<T, Error>;
6
7/// Errors generated by the compiler
8pub struct Error {
9    kind: Box<ErrorKind>,
10    pos: GPosIdx,
11    post_msg: Option<String>,
12}
13
14impl std::fmt::Debug for Error {
15    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
16        if self.pos == GPosIdx::UNKNOWN {
17            write!(f, "{}", self.kind)?
18        } else {
19            write!(f, "{}", self.pos.format(self.kind.to_string()))?
20        }
21        if let Some(post) = &self.post_msg {
22            write!(f, "\n{}", post)?;
23        }
24        Ok(())
25    }
26}
27
28impl Error {
29    pub fn with_pos<T: WithPos>(mut self, pos: &T) -> Self {
30        self.pos = pos.copy_span();
31        self
32    }
33
34    pub fn with_post_msg(mut self, msg: Option<String>) -> Self {
35        self.post_msg = msg;
36        self
37    }
38
39    pub fn reserved_name(name: Id) -> Self {
40        Self {
41            kind: Box::new(ErrorKind::ReservedName(name)),
42            pos: GPosIdx::UNKNOWN,
43            post_msg: None,
44        }
45    }
46    pub fn malformed_control<S: ToString>(msg: S) -> Self {
47        Self {
48            kind: Box::new(ErrorKind::MalformedControl(msg.to_string())),
49            pos: GPosIdx::UNKNOWN,
50            post_msg: None,
51        }
52    }
53    pub fn malformed_structure<S: ToString>(msg: S) -> Self {
54        Self {
55            kind: Box::new(ErrorKind::MalformedStructure(msg.to_string())),
56            pos: GPosIdx::UNKNOWN,
57            post_msg: None,
58        }
59    }
60    pub fn pass_assumption<S: ToString, M: ToString>(pass: S, msg: M) -> Self {
61        Self {
62            kind: Box::new(ErrorKind::PassAssumption(
63                pass.to_string(),
64                msg.to_string(),
65            )),
66            pos: GPosIdx::UNKNOWN,
67            post_msg: None,
68        }
69    }
70    pub fn undefined<S: ToString>(name: Id, typ: S) -> Self {
71        Self {
72            kind: Box::new(ErrorKind::Undefined(name, typ.to_string())),
73            pos: GPosIdx::UNKNOWN,
74            post_msg: None,
75        }
76    }
77    pub fn already_bound<S: ToString>(name: Id, typ: S) -> Self {
78        Self {
79            kind: Box::new(ErrorKind::AlreadyBound(name, typ.to_string())),
80            pos: GPosIdx::UNKNOWN,
81            post_msg: None,
82        }
83    }
84    pub fn unused<S: ToString>(group: Id, typ: S) -> Self {
85        Self {
86            kind: Box::new(ErrorKind::Unused(group, typ.to_string())),
87            pos: GPosIdx::UNKNOWN,
88            post_msg: None,
89        }
90    }
91    pub fn papercut<S: ToString>(msg: S) -> Self {
92        Self {
93            kind: Box::new(ErrorKind::Papercut(msg.to_string())),
94            pos: GPosIdx::UNKNOWN,
95            post_msg: None,
96        }
97    }
98    pub fn misc<S: ToString>(msg: S) -> Self {
99        Self {
100            kind: Box::new(ErrorKind::Misc(msg.to_string())),
101            pos: GPosIdx::UNKNOWN,
102            post_msg: None,
103        }
104    }
105    pub fn invalid_file<S: ToString>(msg: S) -> Self {
106        Self {
107            kind: Box::new(ErrorKind::InvalidFile(msg.to_string())),
108            pos: GPosIdx::UNKNOWN,
109            post_msg: None,
110        }
111    }
112    pub fn write_error<S: ToString>(msg: S) -> Self {
113        Self {
114            kind: Box::new(ErrorKind::WriteError(msg.to_string())),
115            pos: GPosIdx::UNKNOWN,
116            post_msg: None,
117        }
118    }
119    pub fn location(&self) -> (&str, usize, usize) {
120        self.pos.get_location()
121    }
122    pub fn message(&self) -> String {
123        self.kind.to_string()
124    }
125}
126
127/// Standard error type for Calyx errors.
128enum ErrorKind {
129    /// Using a reserved keyword as a program identifier.
130    ReservedName(Id),
131
132    /// The control program is malformed.
133    MalformedControl(String),
134    /// The connections are malformed.
135    MalformedStructure(String),
136
137    /// Requirement of a pass was not satisfied
138    PassAssumption(String, String),
139
140    /// The name has not been bound
141    Undefined(Id, String),
142    /// The name has already been bound.
143    AlreadyBound(Id, String),
144
145    /// The group was not used in the program.
146    Unused(Id, String),
147
148    /// Papercut error: signals a commonly made mistake in Calyx program.
149    Papercut(String),
150
151    // =========== Frontend Errors ===============
152    /// Miscellaneous error message
153    Misc(String),
154    /// The input file is invalid (does not exist).
155    InvalidFile(String),
156    /// Failed to write the output
157    WriteError(String),
158}
159
160impl std::fmt::Display for ErrorKind {
161    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
162        use ErrorKind::*;
163        match self {
164            Papercut(msg) => {
165                write!(f, "[Papercut] {}", msg)
166            }
167            Unused(name, typ) => {
168                write!(f, "Unused {typ} `{name}'")
169            }
170            AlreadyBound(name, bound_by) => {
171                write!(f, "Name `{name}' already bound by {bound_by}")
172            }
173            ReservedName(name) => {
174                write!(f, "Use of reserved keyword: {name}")
175            }
176            Undefined(name, typ) => {
177                write!(f, "Undefined {typ} name: {name}")
178            }
179            MalformedControl(msg) => write!(f, "Malformed Control: {msg}"),
180            PassAssumption(pass, msg) => {
181                write!(f, "Pass `{pass}` assumption violated: {msg}")
182            }
183            MalformedStructure(msg) => {
184                write!(f, "Malformed Structure: {msg}")
185            }
186            InvalidFile(msg) | WriteError(msg) | Misc(msg) => {
187                write!(f, "{msg}")
188            }
189        }
190    }
191}
192
193// Conversions from other error types to our error type so that
194// we can use `?` in all the places.
195impl From<std::str::Utf8Error> for Error {
196    fn from(err: std::str::Utf8Error) -> Self {
197        Error::invalid_file(err.to_string())
198    }
199}
200
201impl From<std::io::Error> for Error {
202    fn from(e: std::io::Error) -> Self {
203        Error::write_error(format!("IO Error: {}", e))
204    }
205}
206
207impl From<serde_json::Error> for Error {
208    fn from(e: serde_json::Error) -> Self {
209        Error::write_error(format!("serde_json Error: {}", e))
210    }
211}