arithmetic_typing/error/
kind.rs

1//! `ErrorKind` and tightly related types.
2
3use core::fmt;
4
5use arithmetic_parser::UnsupportedType;
6
7use crate::{
8    PrimitiveType, TupleIndex, TupleLen, Type,
9    alloc::{Box, HashSet, String},
10    arith::Constraint,
11    ast::AstConversionError,
12    error::ErrorPathFragment,
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) => {
152                write!(formatter, "Type `{rhs}` is not assignable to type `{lhs}`")
153            }
154            Self::TupleLenMismatch {
155                lhs,
156                rhs,
157                context: TupleContext::FnArgs,
158            } => write!(
159                formatter,
160                "Function expects {lhs} args, but is called with {rhs} args"
161            ),
162            Self::TupleLenMismatch { lhs, rhs, .. } => write!(
163                formatter,
164                "Expected a tuple with {lhs} elements, got one with {rhs} elements"
165            ),
166
167            Self::UndefinedVar(name) => write!(formatter, "Variable `{name}` is not defined"),
168
169            Self::RecursiveType(ty) => write!(
170                formatter,
171                "Cannot unify type 'T with a type containing it: {ty}"
172            ),
173
174            Self::RepeatedAssignment(name) => {
175                write!(
176                    formatter,
177                    "Repeated assignment to the same variable `{name}`"
178                )
179            }
180
181            Self::InvalidFieldName(name) => {
182                write!(formatter, "`{name}` is not a valid field name")
183            }
184            Self::CannotIndex => formatter.write_str("Value cannot be indexed"),
185            Self::UnsupportedIndex => formatter.write_str("Unsupported indexing operation"),
186            Self::IndexOutOfBounds { index, len } => write!(
187                formatter,
188                "Attempting to get element {index} from tuple with length {len}"
189            ),
190
191            Self::RepeatedField(name) => write!(formatter, "Repeated object field `{name}`"),
192            Self::CannotAccessFields => formatter.write_str("Value is not an object"),
193            Self::FieldsMismatch {
194                lhs_fields,
195                rhs_fields,
196            } => write!(
197                formatter,
198                "Cannot assign object with fields {rhs_fields:?} to object with fields {lhs_fields:?}"
199            ),
200            Self::MissingFields {
201                fields,
202                available_fields,
203            } => write!(
204                formatter,
205                "Missing field(s) {fields:?} from object (available fields: {available_fields:?})"
206            ),
207
208            Self::UnresolvedParam => {
209                formatter.write_str("Params not instantiated into variables cannot be unified")
210            }
211
212            Self::FailedConstraint { ty, constraint } => {
213                write!(formatter, "Type `{ty}` fails constraint `{constraint}`")
214            }
215            Self::DynamicLen(len) => {
216                write!(formatter, "Length `{len}` is required to be static")
217            }
218
219            Self::UnsupportedFeature(ty) => write!(formatter, "Unsupported {ty}"),
220            Self::UnsupportedType(ty) => write!(formatter, "Unsupported type: {ty}"),
221            Self::UnsupportedParam => {
222                formatter.write_str("Params in declared function types are not supported yet")
223            }
224
225            Self::AstConversion(err) => {
226                write!(formatter, "Error instantiating type from annotation: {err}")
227            }
228        }
229    }
230}
231
232#[cfg(feature = "std")]
233impl<Prim: PrimitiveType> std::error::Error for ErrorKind<Prim> {
234    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
235        match self {
236            Self::AstConversion(err) => Some(err),
237            _ => None,
238        }
239    }
240}
241
242impl<Prim: PrimitiveType> ErrorKind<Prim> {
243    /// Creates an error for an lvalue type not supported by the interpreter.
244    pub fn unsupported<T: Into<UnsupportedType>>(ty: T) -> Self {
245        Self::UnsupportedFeature(ty.into())
246    }
247
248    /// Creates a "failed constraint" error.
249    pub fn failed_constraint(ty: Type<Prim>, constraint: impl Constraint<Prim> + Clone) -> Self {
250        Self::FailedConstraint {
251            ty: Box::new(ty),
252            constraint: Box::new(constraint),
253        }
254    }
255}