arithmetic_typing/error/
mod.rs

1//! Errors related to type inference.
2
3use std::fmt;
4
5use crate::{
6    arith::{BinaryOpContext, UnaryOpContext},
7    ast::AstConversionError,
8    visit::VisitMut,
9    PrimitiveType, Tuple, Type,
10};
11use arithmetic_parser::{Spanned, UnsupportedType};
12
13mod kind;
14mod location;
15mod op_errors;
16
17pub use self::{
18    kind::{ErrorKind, TupleContext},
19    location::ErrorLocation,
20    op_errors::OpErrors,
21};
22
23/// Type error together with the corresponding code span.
24// TODO: implement `StripCode`?
25#[derive(Debug, Clone)]
26pub struct Error<'a, Prim: PrimitiveType> {
27    inner: Spanned<'a, ErrorKind<Prim>>,
28    root_span: Spanned<'a>,
29    context: ErrorContext<Prim>,
30    location: Vec<ErrorLocation>,
31}
32
33impl<Prim: PrimitiveType> fmt::Display for Error<'_, Prim> {
34    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
35        write!(
36            formatter,
37            "{}:{}: {}",
38            self.main_span().location_line(),
39            self.main_span().get_column(),
40            self.kind()
41        )
42    }
43}
44
45impl<Prim: PrimitiveType> std::error::Error for Error<'_, Prim> {
46    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
47        Some(self.kind())
48    }
49}
50
51impl<'a, Prim: PrimitiveType> Error<'a, Prim> {
52    pub(crate) fn unsupported<T>(
53        unsupported: impl Into<UnsupportedType>,
54        span: &Spanned<'a, T>,
55    ) -> Self {
56        let kind = ErrorKind::unsupported(unsupported);
57        Self {
58            inner: span.copy_with_extra(kind),
59            root_span: span.with_no_extra(),
60            context: ErrorContext::None,
61            location: vec![],
62        }
63    }
64
65    pub(crate) fn undefined_var<T>(span: &Spanned<'a, T>) -> Self {
66        let ident = (*span.fragment()).to_owned();
67        Self {
68            inner: span.copy_with_extra(ErrorKind::UndefinedVar(ident)),
69            root_span: span.with_no_extra(),
70            context: ErrorContext::None,
71            location: vec![],
72        }
73    }
74
75    pub(crate) fn repeated_assignment(span: Spanned<'a>) -> Self {
76        let ident = (*span.fragment()).to_owned();
77        Self {
78            inner: span.copy_with_extra(ErrorKind::RepeatedAssignment(ident)),
79            root_span: span.with_no_extra(),
80            context: ErrorContext::None,
81            location: vec![],
82        }
83    }
84
85    pub(crate) fn repeated_field(span: Spanned<'a>) -> Self {
86        let ident = (*span.fragment()).to_owned();
87        Self {
88            inner: span.copy_with_extra(ErrorKind::RepeatedField(ident)),
89            root_span: span.with_no_extra(),
90            context: ErrorContext::None,
91            location: vec![],
92        }
93    }
94
95    pub(crate) fn conversion<T>(kind: AstConversionError, span: &Spanned<'a, T>) -> Self {
96        let kind = ErrorKind::AstConversion(kind);
97        Self {
98            inner: span.copy_with_extra(kind),
99            root_span: span.with_no_extra(),
100            context: ErrorContext::None,
101            location: vec![],
102        }
103    }
104
105    pub(crate) fn invalid_field_name(span: Spanned<'a>) -> Self {
106        let ident = (*span.fragment()).to_owned();
107        Self {
108            inner: span.copy_with_extra(ErrorKind::InvalidFieldName(ident)),
109            root_span: span,
110            context: ErrorContext::None,
111            location: vec![],
112        }
113    }
114
115    pub(crate) fn index_out_of_bounds<T>(
116        receiver: Tuple<Prim>,
117        span: &Spanned<'a, T>,
118        index: usize,
119    ) -> Self {
120        Self {
121            inner: span.copy_with_extra(ErrorKind::IndexOutOfBounds {
122                index,
123                len: receiver.len(),
124            }),
125            root_span: span.with_no_extra(),
126            context: ErrorContext::TupleIndex {
127                ty: Type::Tuple(receiver),
128            },
129            location: vec![],
130        }
131    }
132
133    pub(crate) fn cannot_index<T>(receiver: Type<Prim>, span: &Spanned<'a, T>) -> Self {
134        Self {
135            inner: span.copy_with_extra(ErrorKind::CannotIndex),
136            root_span: span.with_no_extra(),
137            context: ErrorContext::TupleIndex { ty: receiver },
138            location: vec![],
139        }
140    }
141
142    pub(crate) fn unsupported_index<T>(receiver: Type<Prim>, span: &Spanned<'a, T>) -> Self {
143        Self {
144            inner: span.copy_with_extra(ErrorKind::UnsupportedIndex),
145            root_span: span.with_no_extra(),
146            context: ErrorContext::TupleIndex { ty: receiver },
147            location: vec![],
148        }
149    }
150
151    /// Gets the kind of this error.
152    pub fn kind(&self) -> &ErrorKind<Prim> {
153        &self.inner.extra
154    }
155
156    /// Gets the most specific code span of this error.
157    pub fn main_span(&self) -> Spanned<'a> {
158        self.inner.with_no_extra()
159    }
160
161    /// Gets the root code span of the failed operation. May coincide with [`Self::main_span()`].
162    pub fn root_span(&self) -> Spanned<'a> {
163        self.root_span
164    }
165
166    /// Gets the context for an operation that has failed.
167    pub fn context(&self) -> &ErrorContext<Prim> {
168        &self.context
169    }
170
171    /// Gets the location of this error relative to the failed top-level operation.
172    /// This can be used for highlighting relevant parts of types in [`Self::context()`].
173    pub fn location(&self) -> &[ErrorLocation] {
174        &self.location
175    }
176}
177
178/// List of [`Error`]s.
179///
180/// # Examples
181///
182/// ```
183/// # use arithmetic_parser::grammars::{F32Grammar, Parse};
184/// # use arithmetic_typing::{defs::Prelude, error::Errors, Annotated, TypeEnvironment};
185/// # use std::collections::HashSet;
186/// # fn main() -> anyhow::Result<()> {
187/// let buggy_code = Annotated::<F32Grammar>::parse_statements(r#"
188///     numbers: ['T; _] = (1, 2, 3);
189///     numbers.filter(|x| x, 1)
190/// "#)?;
191/// let mut env: TypeEnvironment = Prelude::iter().collect();
192/// let errors: Errors<_> = env.process_statements(&buggy_code).unwrap_err();
193/// assert_eq!(errors.len(), 3);
194///
195/// let messages: HashSet<_> = errors.iter().map(ToString::to_string).collect();
196/// assert!(messages
197///     .iter()
198///     .any(|msg| msg.contains("Type param `T` is not scoped by function definition")));
199/// assert!(messages
200///     .contains("3:20: Type `Num` is not assignable to type `Bool`"));
201/// assert!(messages
202///     .contains("3:5: Function expects 2 args, but is called with 3 args"));
203/// # Ok(())
204/// # }
205/// ```
206#[derive(Debug, Clone)]
207pub struct Errors<'a, Prim: PrimitiveType> {
208    inner: Vec<Error<'a, Prim>>,
209    first_failing_statement: usize,
210}
211
212impl<'a, Prim: PrimitiveType> Errors<'a, Prim> {
213    pub(crate) fn new() -> Self {
214        Self {
215            inner: vec![],
216            first_failing_statement: 0,
217        }
218    }
219
220    pub(crate) fn push(&mut self, err: Error<'a, Prim>) {
221        self.inner.push(err);
222    }
223
224    pub(crate) fn extend(&mut self, errors: Vec<Error<'a, Prim>>) {
225        self.inner.extend(errors.into_iter());
226    }
227
228    /// Returns the number of errors in this list.
229    pub fn len(&self) -> usize {
230        self.inner.len()
231    }
232
233    /// Checks if this list is empty (there are no errors).
234    pub fn is_empty(&self) -> bool {
235        self.inner.is_empty()
236    }
237
238    /// Iterates over errors contained in this list.
239    pub fn iter(&self) -> impl Iterator<Item = &Error<'a, Prim>> + '_ {
240        self.inner.iter()
241    }
242
243    /// Returns the index of the first failing statement within a `Block` that has errored.
244    /// If the error is in the return value, this index will be equal to the number of statements
245    /// in the block.
246    pub fn first_failing_statement(&self) -> usize {
247        self.first_failing_statement
248    }
249
250    pub(crate) fn set_first_failing_statement(&mut self, index: usize) {
251        self.first_failing_statement = index;
252    }
253
254    /// Post-processes these errors, resolving the contained `Type`s using
255    /// the provided `type_resolver`.
256    pub(crate) fn post_process(&mut self, type_resolver: &mut impl VisitMut<Prim>) {
257        for error in &mut self.inner {
258            error.context.map_types(type_resolver);
259        }
260    }
261}
262
263impl<Prim: PrimitiveType> fmt::Display for Errors<'_, Prim> {
264    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
265        for (i, error) in self.inner.iter().enumerate() {
266            write!(formatter, "{}", error)?;
267            if i + 1 < self.inner.len() {
268                formatter.write_str("\n")?;
269            }
270        }
271        Ok(())
272    }
273}
274
275impl<Prim: PrimitiveType> std::error::Error for Errors<'_, Prim> {}
276
277impl<'a, Prim: PrimitiveType> IntoIterator for Errors<'a, Prim> {
278    type Item = Error<'a, Prim>;
279    type IntoIter = std::vec::IntoIter<Self::Item>;
280
281    fn into_iter(self) -> Self::IntoIter {
282        self.inner.into_iter()
283    }
284}
285
286/// Context of a [`Error`] corresponding to a top-level operation that has errored.
287/// Generally, contains resolved types concerning the operation, such as operands of
288/// a binary arithmetic op.
289#[derive(Debug, Clone)]
290#[non_exhaustive]
291pub enum ErrorContext<Prim: PrimitiveType> {
292    /// No context.
293    None,
294    /// Processing lvalue (before assignment).
295    Lvalue(Type<Prim>),
296    /// Function definition.
297    FnDefinition {
298        /// Types of function arguments.
299        args: Tuple<Prim>,
300    },
301    /// Function call.
302    FnCall {
303        /// Function definition. Note that this is not necessarily a [`Function`](crate::Function).
304        definition: Type<Prim>,
305        /// Signature of the call.
306        call_signature: Type<Prim>,
307    },
308    /// Assignment.
309    Assignment {
310        /// Left-hand side of the assignment.
311        lhs: Type<Prim>,
312        /// Right-hand side of the assignment.
313        rhs: Type<Prim>,
314    },
315    /// Type cast.
316    TypeCast {
317        /// Source type of the casted value.
318        source: Type<Prim>,
319        /// Target type of the cast.
320        target: Type<Prim>,
321    },
322    /// Unary operation.
323    UnaryOp(UnaryOpContext<Prim>),
324    /// Binary operation.
325    BinaryOp(BinaryOpContext<Prim>),
326    /// Tuple indexing operation.
327    TupleIndex {
328        /// Type being indexed.
329        ty: Type<Prim>,
330    },
331    /// Field access for an object.
332    ObjectFieldAccess {
333        /// Type being accessed.
334        ty: Type<Prim>,
335    },
336}
337
338impl<Prim: PrimitiveType> From<UnaryOpContext<Prim>> for ErrorContext<Prim> {
339    fn from(value: UnaryOpContext<Prim>) -> Self {
340        Self::UnaryOp(value)
341    }
342}
343
344impl<Prim: PrimitiveType> From<BinaryOpContext<Prim>> for ErrorContext<Prim> {
345    fn from(value: BinaryOpContext<Prim>) -> Self {
346        Self::BinaryOp(value)
347    }
348}
349
350impl<Prim: PrimitiveType> ErrorContext<Prim> {
351    fn map_types(&mut self, mapper: &mut impl VisitMut<Prim>) {
352        match self {
353            Self::None => { /* Do nothing. */ }
354            Self::Lvalue(lvalue) => mapper.visit_type_mut(lvalue),
355            Self::FnDefinition { args } => mapper.visit_tuple_mut(args),
356            Self::FnCall {
357                definition,
358                call_signature,
359            } => {
360                mapper.visit_type_mut(definition);
361                mapper.visit_type_mut(call_signature);
362            }
363            Self::Assignment { lhs, rhs } | Self::BinaryOp(BinaryOpContext { lhs, rhs, .. }) => {
364                mapper.visit_type_mut(lhs);
365                mapper.visit_type_mut(rhs);
366            }
367            Self::TypeCast { source, target } => {
368                mapper.visit_type_mut(source);
369                mapper.visit_type_mut(target);
370            }
371            Self::UnaryOp(UnaryOpContext { arg, .. }) => {
372                mapper.visit_type_mut(arg);
373            }
374            Self::TupleIndex { ty } | Self::ObjectFieldAccess { ty } => {
375                mapper.visit_type_mut(ty);
376            }
377        }
378    }
379}