match_lang/error.rs
1//! Why a check could not be completed.
2
3use core::fmt;
4
5/// Why a check could not produce a [`Report`](crate::Report).
6///
7/// # Examples
8///
9/// ```
10/// use match_lang::{check, Arm, Error, Model, Pat, Reason, Signature};
11///
12/// struct Bytes;
13///
14/// impl Model for Bytes {
15/// type Ty = ();
16/// fn signature(&self, _: &()) -> Signature<()> {
17/// Signature::Int { min: 0, max: 255 }
18/// }
19/// fn fields(&self, _: &(), _: u32, _: &mut Vec<()>) {}
20/// }
21///
22/// let err = check(&Bytes, &(), &[Arm::new(Pat::int(300))]).unwrap_err();
23/// assert_eq!(err, Error::InvalidPattern { arm: 0, node: 0, reason: Reason::OutOfDomain });
24/// ```
25#[derive(Clone, Debug, PartialEq, Eq)]
26#[non_exhaustive]
27pub enum Error {
28 /// The analysis took more steps than the
29 /// [step limit](crate::Checker::with_step_limit) allows. Deciding
30 /// exhaustiveness is NP-hard in general, so some matrices (typically
31 /// many columns of overlapping or-patterns or ranges) need exponential
32 /// work; the limit turns them into this error instead of a hang. Raise
33 /// the limit if the match is legitimate, or report it to the user as too
34 /// complex to check.
35 StepLimit {
36 /// The limit that was exceeded.
37 limit: u64,
38 },
39 /// A pattern does not fit the type it is matched against. This is a type
40 /// error the language's checker should have caught; nothing was analysed.
41 InvalidPattern {
42 /// The index of the arm.
43 arm: usize,
44 /// The preorder index of the offending node within the arm's pattern
45 /// (see [`Pat`](crate::Pat#node-numbering)).
46 node: usize,
47 /// What is wrong with it.
48 reason: Reason,
49 },
50 /// The model answered inconsistently: the same type got different
51 /// signatures or field counts during one check, or a signature was
52 /// itself invalid (an `Int` with `min > max`). Fix the model.
53 Model {
54 /// What was inconsistent.
55 reason: &'static str,
56 },
57 /// The patterns, or the matrix built from them, need more than
58 /// `u32::MAX - 1` nodes, rows, or cells.
59 TooLarge,
60}
61
62/// What is wrong with a pattern, in [`Error::InvalidPattern`].
63///
64/// # Examples
65///
66/// ```
67/// use match_lang::Reason;
68///
69/// let reason = Reason::Fields { expected: 1, found: 2 };
70/// assert_eq!(reason.to_string(), "expected 1 fields, found 2");
71/// ```
72#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
73#[non_exhaustive]
74pub enum Reason {
75 /// The kind of pattern does not fit the type's signature, such as an
76 /// integer pattern on a sum type.
77 Kind,
78 /// A variant index at or past the sum type's variant count.
79 Variant {
80 /// The variant index used.
81 index: u32,
82 /// The number of variants the type has.
83 count: u32,
84 },
85 /// A variant or product pattern with the wrong number of fields.
86 Fields {
87 /// The number of fields the model reports.
88 expected: usize,
89 /// The number of field patterns given.
90 found: usize,
91 },
92 /// A range whose low end is above its high end.
93 EmptyRange,
94 /// An integer or range outside the bounds of an `Int` type.
95 OutOfDomain,
96 /// An array pattern that cannot have the array's length: a fixed slice
97 /// pattern of another length, or a rest pattern needing more elements.
98 Length {
99 /// The array's length.
100 expected: u32,
101 /// The number of elements the pattern requires.
102 found: usize,
103 },
104}
105
106impl fmt::Display for Error {
107 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
108 match self {
109 Error::StepLimit { limit } => {
110 write!(
111 f,
112 "match too complex to check: exceeded the step limit of {limit}"
113 )
114 }
115 Error::InvalidPattern { arm, node, reason } => {
116 write!(f, "arm {arm}, pattern node {node}: {reason}")
117 }
118 Error::Model { reason } => write!(f, "inconsistent constructor model: {reason}"),
119 Error::TooLarge => f.write_str("match too large: more than u32::MAX - 1 nodes"),
120 }
121 }
122}
123
124impl fmt::Display for Reason {
125 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
126 match self {
127 Reason::Kind => f.write_str("pattern does not fit the type"),
128 Reason::Variant { index, count } => {
129 write!(f, "variant {index} does not exist (the type has {count})")
130 }
131 Reason::Fields { expected, found } => {
132 write!(f, "expected {expected} fields, found {found}")
133 }
134 Reason::EmptyRange => f.write_str("range is empty (low end above high end)"),
135 Reason::OutOfDomain => f.write_str("value outside the type's bounds"),
136 Reason::Length { expected, found } => {
137 write!(
138 f,
139 "array of length {expected} cannot match a pattern of {found} elements"
140 )
141 }
142 }
143 }
144}
145
146impl core::error::Error for Error {}
147
148#[cfg(test)]
149mod tests {
150 use alloc::string::ToString;
151
152 use super::*;
153
154 #[test]
155 fn test_error_messages_name_the_problem() {
156 assert!(
157 Error::StepLimit { limit: 5 }
158 .to_string()
159 .contains("step limit of 5")
160 );
161 assert_eq!(
162 Error::InvalidPattern {
163 arm: 1,
164 node: 2,
165 reason: Reason::Kind
166 }
167 .to_string(),
168 "arm 1, pattern node 2: pattern does not fit the type"
169 );
170 assert!(Error::Model { reason: "x" }.to_string().ends_with(": x"));
171 assert!(Error::TooLarge.to_string().contains("too large"));
172 assert!(
173 Reason::Variant { index: 3, count: 2 }
174 .to_string()
175 .contains("variant 3")
176 );
177 assert!(Reason::EmptyRange.to_string().contains("empty"));
178 assert!(Reason::OutOfDomain.to_string().contains("bounds"));
179 assert!(
180 Reason::Length {
181 expected: 2,
182 found: 3
183 }
184 .to_string()
185 .contains("length 2")
186 );
187 }
188}