arithmetic_typing/error/
kind.rs

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