arithmetic_typing/error/
kind.rs

1//! `ErrorKind` and tightly related types.
2
3use core::fmt;
4
5use arithmetic_parser::UnsupportedType;
6
7use crate::{
8    alloc::{Box, HashSet, String},
9    arith::Constraint,
10    ast::AstConversionError,
11    error::ErrorPathFragment,
12    PrimitiveType, TupleIndex, TupleLen, Type,
13};
14
15/// Context in which a tuple is used.
16#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
17#[non_exhaustive]
18pub enum TupleContext {
19    /// Generic tuple use: assignment, destructuring, or creating a tuple from elements.
20    Generic,
21    /// The tuple represents function arguments.
22    FnArgs,
23}
24
25impl TupleContext {
26    pub(crate) fn element(self, index: usize) -> ErrorPathFragment {
27        let index = TupleIndex::Start(index);
28        match self {
29            Self::Generic => ErrorPathFragment::TupleElement(Some(index)),
30            Self::FnArgs => ErrorPathFragment::FnArg(Some(index)),
31        }
32    }
33
34    pub(crate) fn end_element(self, index: usize) -> ErrorPathFragment {
35        let index = TupleIndex::End(index);
36        match self {
37            Self::Generic => ErrorPathFragment::TupleElement(Some(index)),
38            Self::FnArgs => ErrorPathFragment::FnArg(Some(index)),
39        }
40    }
41}
42
43/// Kinds of errors that can occur during type inference.
44#[derive(Debug, Clone)]
45#[non_exhaustive]
46pub enum ErrorKind<Prim: PrimitiveType> {
47    /// Trying to unify incompatible types. The first type is LHS, the second one is RHS.
48    TypeMismatch(Box<Type<Prim>>, Box<Type<Prim>>),
49    /// Incompatible tuple lengths.
50    TupleLenMismatch {
51        /// Length of the LHS. This is the length determined by type annotations
52        /// for assignments and the number of actually supplied args in function calls.
53        lhs: TupleLen,
54        /// Length of the RHS. This is usually the actual tuple length in assignments
55        /// and the number of expected args in function calls.
56        rhs: TupleLen,
57        /// Context in which the error has occurred.
58        context: TupleContext,
59    },
60    /// Undefined variable occurrence.
61    UndefinedVar(String),
62    /// Trying to unify a type with a type containing it.
63    RecursiveType(Box<Type<Prim>>),
64
65    /// Repeated assignment to the same variable in function args or tuple destructuring.
66    RepeatedAssignment(String),
67
68    /// Field name is invalid.
69    InvalidFieldName(String),
70    /// Value cannot be indexed (i.e., not a tuple).
71    CannotIndex,
72    /// Unsupported indexing operation. For example, the receiver type is not known,
73    /// or it is a tuple with an unknown length, and the type of the element cannot be decided.
74    UnsupportedIndex,
75    /// Index is out of bounds for the indexed tuple.
76    IndexOutOfBounds {
77        /// Index.
78        index: usize,
79        /// Actual tuple length.
80        len: TupleLen,
81    },
82
83    /// Repeated field in object initialization / destructuring.
84    RepeatedField(String),
85    /// Cannot access fields in a value (i.e., it's not an object).
86    CannotAccessFields,
87    /// Field set differs between LHS and RHS, which are both concrete objects.
88    FieldsMismatch {
89        /// Fields in LHS.
90        lhs_fields: HashSet<String>,
91        /// Fields in RHS.
92        rhs_fields: HashSet<String>,
93    },
94    /// Concrete object does not have required fields.
95    MissingFields {
96        /// Missing fields.
97        fields: HashSet<String>,
98        /// Available object fields.
99        available_fields: HashSet<String>,
100    },
101
102    /// Mention of a bounded type or length variable in a type supplied
103    /// to [`Substitutions::unify()`].
104    ///
105    /// Bounded variables are instantiated into free vars automatically during
106    /// type inference, so this error
107    /// can only occur with types manually supplied to `Substitutions::unify()`.
108    ///
109    /// [`Substitutions::unify()`]: crate::arith::Substitutions::unify()
110    UnresolvedParam,
111
112    /// Failure when applying constraint to a type.
113    FailedConstraint {
114        /// Type that fails constraint requirement.
115        ty: Box<Type<Prim>>,
116        /// Failing constraint.
117        constraint: Box<dyn Constraint<Prim>>,
118    },
119    /// Length with the static constraint is actually dynamic (contains [`UnknownLen::Dynamic`]).
120    ///
121    /// [`UnknownLen::Dynamic`]: crate::UnknownLen::Dynamic
122    DynamicLen(TupleLen),
123
124    /// Language feature not supported by type inference logic.
125    UnsupportedFeature(UnsupportedType),
126
127    /// Type not supported by type inference logic. For example,
128    /// a [`TypeArithmetic`] or [`Constraint`] implementations may return this error
129    /// if they encounter an unknown [`Type`] variant.
130    ///
131    /// [`TypeArithmetic`]: crate::arith::TypeArithmetic
132    UnsupportedType(Box<Type<Prim>>),
133
134    /// Unsupported use of type or length params in a function declaration.
135    ///
136    /// Type or length params are currently not supported in type annotations. Here's an example
137    /// of code that triggers this error:
138    ///
139    /// ```text
140    /// identity: (('T,)) -> ('T,) = |x| x;
141    /// ```
142    UnsupportedParam,
143
144    /// Error while instantiating a type from AST.
145    AstConversion(AstConversionError),
146}
147
148impl<Prim: PrimitiveType> fmt::Display for ErrorKind<Prim> {
149    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
150        match self {
151            Self::TypeMismatch(lhs, rhs) => write!(
152                formatter,
153                "Type `{rhs}` is not assignable to type `{lhs}`"
154            ),
155            Self::TupleLenMismatch {
156                lhs,
157                rhs,
158                context: TupleContext::FnArgs,
159            } => write!(
160                formatter,
161                "Function expects {lhs} args, but is called with {rhs} args"
162            ),
163            Self::TupleLenMismatch { lhs, rhs, .. } => write!(
164                formatter,
165                "Expected a tuple with {lhs} elements, got one with {rhs} elements"
166            ),
167
168            Self::UndefinedVar(name) => write!(formatter, "Variable `{name}` is not defined"),
169
170            Self::RecursiveType(ty) => write!(
171                formatter,
172                "Cannot unify type 'T with a type containing it: {ty}"
173            ),
174
175            Self::RepeatedAssignment(name) => {
176                write!(
177                    formatter,
178                    "Repeated assignment to the same variable `{name}`"
179                )
180            }
181
182            Self::InvalidFieldName(name) => {
183                write!(formatter, "`{name}` is not a valid field name")
184            }
185            Self::CannotIndex => formatter.write_str("Value cannot be indexed"),
186            Self::UnsupportedIndex => formatter.write_str("Unsupported indexing operation"),
187            Self::IndexOutOfBounds { index, len } => write!(
188                formatter,
189                "Attempting to get element {index} from tuple with length {len}"
190            ),
191
192            Self::RepeatedField(name) => write!(formatter, "Repeated object field `{name}`"),
193            Self::CannotAccessFields => formatter.write_str("Value is not an object"),
194            Self::FieldsMismatch {
195                lhs_fields,
196                rhs_fields,
197            } => write!(
198                formatter,
199                "Cannot assign object with fields {rhs_fields:?} to object with fields {lhs_fields:?}"
200            ),
201            Self::MissingFields {
202                fields,
203                available_fields,
204            } => write!(
205                formatter,
206                "Missing field(s) {fields:?} from object (available fields: {available_fields:?})"
207            ),
208
209            Self::UnresolvedParam => {
210                formatter.write_str("Params not instantiated into variables cannot be unified")
211            }
212
213            Self::FailedConstraint { ty, constraint } => {
214                write!(formatter, "Type `{ty}` fails constraint `{constraint}`")
215            }
216            Self::DynamicLen(len) => {
217                write!(formatter, "Length `{len}` is required to be static")
218            }
219
220            Self::UnsupportedFeature(ty) => write!(formatter, "Unsupported {ty}"),
221            Self::UnsupportedType(ty) => write!(formatter, "Unsupported type: {ty}"),
222            Self::UnsupportedParam => {
223                formatter.write_str("Params in declared function types are not supported yet")
224            }
225
226            Self::AstConversion(err) => write!(
227                formatter,
228                "Error instantiating type from annotation: {err}"
229            ),
230        }
231    }
232}
233
234#[cfg(feature = "std")]
235impl<Prim: PrimitiveType> std::error::Error for ErrorKind<Prim> {
236    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
237        match self {
238            Self::AstConversion(err) => Some(err),
239            _ => None,
240        }
241    }
242}
243
244impl<Prim: PrimitiveType> ErrorKind<Prim> {
245    /// Creates an error for an lvalue type not supported by the interpreter.
246    pub fn unsupported<T: Into<UnsupportedType>>(ty: T) -> Self {
247        Self::UnsupportedFeature(ty.into())
248    }
249
250    /// Creates a "failed constraint" error.
251    pub fn failed_constraint(ty: Type<Prim>, constraint: impl Constraint<Prim> + Clone) -> Self {
252        Self::FailedConstraint {
253            ty: Box::new(ty),
254            constraint: Box::new(constraint),
255        }
256    }
257}