cedar_policy_core/evaluator/
err.rs

1/*
2 * Copyright Cedar Contributors
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      https://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17use crate::ast::*;
18use crate::extensions::ExtensionFunctionLookupError;
19use crate::parser::Loc;
20use miette::Diagnostic;
21use nonempty::{nonempty, NonEmpty};
22use smol_str::SmolStr;
23use std::sync::Arc;
24use thiserror::Error;
25
26// How many attrs will we store in an error before cutting off for performance reason
27const TOO_MANY_ATTRS: usize = 5;
28
29/// Enumeration of the possible errors that can occur during evaluation
30//
31// CAUTION: this type is publicly exported in `cedar-policy`.
32// Don't make fields `pub`, don't make breaking changes, and use caution when
33// adding public methods.
34#[derive(Debug, PartialEq, Eq, Clone, Diagnostic, Error)]
35pub enum EvaluationError {
36    /// Tried to lookup an entity UID, but it didn't exist in the provided
37    /// entities
38    #[error(transparent)]
39    #[diagnostic(transparent)]
40    EntityDoesNotExist(#[from] evaluation_errors::EntityDoesNotExistError),
41
42    /// Tried to get an attribute, but the specified entity didn't
43    /// have that attribute
44    #[error(transparent)]
45    #[diagnostic(transparent)]
46    EntityAttrDoesNotExist(#[from] evaluation_errors::EntityAttrDoesNotExistError),
47
48    /// Tried to get an attribute of a (non-entity) record, but that record
49    /// didn't have that attribute
50    #[error(transparent)]
51    #[diagnostic(transparent)]
52    RecordAttrDoesNotExist(#[from] evaluation_errors::RecordAttrDoesNotExistError),
53
54    /// An error occurred when looking up an extension function
55    #[error(transparent)]
56    #[diagnostic(transparent)]
57    FailedExtensionFunctionLookup(#[from] ExtensionFunctionLookupError),
58
59    /// Tried to evaluate an operation on values with incorrect types for that
60    /// operation
61    #[error(transparent)]
62    #[diagnostic(transparent)]
63    TypeError(#[from] evaluation_errors::TypeError),
64
65    /// Wrong number of arguments provided to an extension function
66    #[error(transparent)]
67    #[diagnostic(transparent)]
68    WrongNumArguments(#[from] evaluation_errors::WrongNumArgumentsError),
69
70    /// Overflow during an integer operation
71    #[error(transparent)]
72    #[diagnostic(transparent)]
73    IntegerOverflow(#[from] evaluation_errors::IntegerOverflowError),
74
75    /// Not all template slots were linked
76    #[error(transparent)]
77    #[diagnostic(transparent)]
78    UnlinkedSlot(#[from] evaluation_errors::UnlinkedSlotError),
79
80    /// Evaluation error thrown by an extension function
81    #[error(transparent)]
82    #[diagnostic(transparent)]
83    FailedExtensionFunctionExecution(#[from] evaluation_errors::ExtensionFunctionExecutionError),
84
85    /// This error is raised if an expression contains unknowns and cannot be
86    /// reduced to a [`Value`]. In order to return partial results, use the
87    /// partial evaluation APIs instead.
88    #[error(transparent)]
89    #[diagnostic(transparent)]
90    NonValue(#[from] evaluation_errors::NonValueError),
91
92    /// Maximum recursion limit reached for expression evaluation
93    #[error(transparent)]
94    #[diagnostic(transparent)]
95    RecursionLimit(#[from] evaluation_errors::RecursionLimitError),
96}
97
98impl EvaluationError {
99    /// Extract the source location of the error, if one is attached
100    pub(crate) fn source_loc(&self) -> Option<&Loc> {
101        match self {
102            Self::EntityDoesNotExist(e) => e.source_loc.as_ref(),
103            Self::EntityAttrDoesNotExist(e) => e.source_loc.as_ref(),
104            Self::RecordAttrDoesNotExist(e) => e.source_loc.as_ref(),
105            Self::FailedExtensionFunctionLookup(e) => e.source_loc(),
106            Self::TypeError(e) => e.source_loc.as_ref(),
107            Self::WrongNumArguments(e) => e.source_loc.as_ref(),
108            Self::IntegerOverflow(e) => e.source_loc(),
109            Self::UnlinkedSlot(e) => e.source_loc.as_ref(),
110            Self::FailedExtensionFunctionExecution(e) => e.source_loc.as_ref(),
111            Self::NonValue(e) => e.source_loc.as_ref(),
112            Self::RecursionLimit(e) => e.source_loc.as_ref(),
113        }
114    }
115
116    /// Return the `EvaluationError`, but with the new `source_loc` (or `None`).
117    pub(crate) fn with_maybe_source_loc(self, source_loc: Option<Loc>) -> Self {
118        match self {
119            Self::EntityDoesNotExist(e) => {
120                Self::EntityDoesNotExist(evaluation_errors::EntityDoesNotExistError {
121                    source_loc,
122                    ..e
123                })
124            }
125            Self::EntityAttrDoesNotExist(e) => {
126                Self::EntityAttrDoesNotExist(evaluation_errors::EntityAttrDoesNotExistError {
127                    source_loc,
128                    ..e
129                })
130            }
131            Self::RecordAttrDoesNotExist(e) => {
132                Self::RecordAttrDoesNotExist(evaluation_errors::RecordAttrDoesNotExistError {
133                    source_loc,
134                    ..e
135                })
136            }
137            Self::FailedExtensionFunctionLookup(e) => {
138                Self::FailedExtensionFunctionLookup(e.with_maybe_source_loc(source_loc))
139            }
140            Self::TypeError(e) => Self::TypeError(evaluation_errors::TypeError { source_loc, ..e }),
141            Self::WrongNumArguments(e) => {
142                Self::WrongNumArguments(evaluation_errors::WrongNumArgumentsError {
143                    source_loc,
144                    ..e
145                })
146            }
147            Self::IntegerOverflow(e) => Self::IntegerOverflow(e.with_maybe_source_loc(source_loc)),
148            Self::UnlinkedSlot(e) => {
149                Self::UnlinkedSlot(evaluation_errors::UnlinkedSlotError { source_loc, ..e })
150            }
151            Self::FailedExtensionFunctionExecution(e) => Self::FailedExtensionFunctionExecution(
152                evaluation_errors::ExtensionFunctionExecutionError { source_loc, ..e },
153            ),
154            Self::NonValue(e) => {
155                Self::NonValue(evaluation_errors::NonValueError { source_loc, ..e })
156            }
157            Self::RecursionLimit(_) => {
158                Self::RecursionLimit(evaluation_errors::RecursionLimitError { source_loc })
159            }
160        }
161    }
162
163    /// Construct a [`EntityDoesNotExist`] error
164    pub(crate) fn entity_does_not_exist(uid: Arc<EntityUID>, source_loc: Option<Loc>) -> Self {
165        evaluation_errors::EntityDoesNotExistError { uid, source_loc }.into()
166    }
167
168    /// Construct a [`EntityAttrDoesNotExist`] error
169    pub(crate) fn entity_attr_does_not_exist<'a>(
170        entity: Arc<EntityUID>,
171        attr: SmolStr,
172        available_attrs: impl IntoIterator<Item = &'a SmolStr>,
173        total_attrs: usize,
174        source_loc: Option<Loc>,
175    ) -> Self {
176        evaluation_errors::EntityAttrDoesNotExistError {
177            entity,
178            attr,
179            available_attrs: available_attrs
180                .into_iter()
181                .take(TOO_MANY_ATTRS)
182                .cloned()
183                .collect::<Vec<_>>(),
184            total_attrs,
185            source_loc,
186        }
187        .into()
188    }
189
190    /// Construct a [`RecordAttrDoesNotExist`] error
191    pub(crate) fn record_attr_does_not_exist<'a>(
192        attr: SmolStr,
193        available_attrs: impl IntoIterator<Item = &'a SmolStr>,
194        total_attrs: usize,
195        source_loc: Option<Loc>,
196    ) -> Self {
197        evaluation_errors::RecordAttrDoesNotExistError {
198            attr,
199            available_attrs: available_attrs
200                .into_iter()
201                .take(TOO_MANY_ATTRS)
202                .cloned()
203                .collect(),
204            total_attrs,
205            source_loc,
206        }
207        .into()
208    }
209
210    /// Construct a [`TypeError`] error
211    pub(crate) fn type_error(expected: NonEmpty<Type>, actual: &Value) -> Self {
212        evaluation_errors::TypeError {
213            expected,
214            actual: actual.type_of(),
215            advice: None,
216            source_loc: actual.source_loc().cloned(),
217        }
218        .into()
219    }
220
221    pub(crate) fn type_error_single(expected: Type, actual: &Value) -> Self {
222        Self::type_error(nonempty![expected], actual)
223    }
224
225    /// Construct a [`TypeError`] error with the advice field set
226    pub(crate) fn type_error_with_advice(
227        expected: NonEmpty<Type>,
228        actual: &Value,
229        advice: String,
230    ) -> Self {
231        evaluation_errors::TypeError {
232            expected,
233            actual: actual.type_of(),
234            advice: Some(advice),
235            source_loc: actual.source_loc().cloned(),
236        }
237        .into()
238    }
239
240    pub(crate) fn type_error_with_advice_single(
241        expected: Type,
242        actual: &Value,
243        advice: String,
244    ) -> Self {
245        Self::type_error_with_advice(nonempty![expected], actual, advice)
246    }
247
248    /// Construct a [`WrongNumArguments`] error
249    pub(crate) fn wrong_num_arguments(
250        function_name: Name,
251        expected: usize,
252        actual: usize,
253        source_loc: Option<Loc>,
254    ) -> Self {
255        evaluation_errors::WrongNumArgumentsError {
256            function_name,
257            expected,
258            actual,
259            source_loc,
260        }
261        .into()
262    }
263
264    /// Construct a [`UnlinkedSlot`] error
265    pub(crate) fn unlinked_slot(slot: SlotId, source_loc: Option<Loc>) -> Self {
266        evaluation_errors::UnlinkedSlotError { slot, source_loc }.into()
267    }
268
269    /// Construct a [`FailedExtensionFunctionApplication`] error
270    pub(crate) fn failed_extension_function_application(
271        extension_name: Name,
272        msg: String,
273        source_loc: Option<Loc>,
274    ) -> Self {
275        evaluation_errors::ExtensionFunctionExecutionError {
276            extension_name,
277            msg,
278            source_loc,
279        }
280        .into()
281    }
282
283    /// Construct a [`NonValue`] error
284    pub(crate) fn non_value(expr: Expr) -> Self {
285        let source_loc = expr.source_loc().cloned();
286        evaluation_errors::NonValueError { expr, source_loc }.into()
287    }
288
289    /// Construct a [`RecursionLimit`] error
290    #[cfg(not(target_arch = "wasm32"))]
291    pub(crate) fn recursion_limit(source_loc: Option<Loc>) -> Self {
292        evaluation_errors::RecursionLimitError { source_loc }.into()
293    }
294}
295
296/// Error subtypes for [`EvaluationError`]
297pub mod evaluation_errors {
298    use crate::ast::{BinaryOp, EntityUID, Expr, SlotId, Type, UnaryOp, Value};
299    use crate::parser::Loc;
300    use itertools::Itertools;
301    use miette::Diagnostic;
302    use nonempty::NonEmpty;
303    use smol_str::SmolStr;
304    use std::sync::Arc;
305    use thiserror::Error;
306
307    use super::Name;
308
309    /// Tried to lookup an entity UID, but it didn't exist in the provided entities
310    //
311    // CAUTION: this type is publicly exported in `cedar-policy`.
312    // Don't make fields `pub`, don't make breaking changes, and use caution
313    // when adding public methods.
314    #[derive(Debug, PartialEq, Eq, Clone, Error)]
315    #[error("entity `{uid}` does not exist")]
316    pub struct EntityDoesNotExistError {
317        /// Entity UID which didn't exist in the provided entities
318        pub(crate) uid: Arc<EntityUID>,
319        /// Source location
320        pub(crate) source_loc: Option<Loc>,
321    }
322
323    // This and similar `Diagnostic` impls could just be derived with
324    //
325    // #[source_code]
326    // #[label]
327    // source_loc: Option<Loc>,
328    //
329    // if [miette#377](https://github.com/zkat/miette/issues/377) gets fixed.
330    // Or, we could have separate fields for source code and label instead of
331    // combining them into `Loc`, which would work around the issue.
332    impl Diagnostic for EntityDoesNotExistError {
333        impl_diagnostic_from_source_loc_opt_field!(source_loc);
334    }
335
336    /// Tried to get an attribute, but the specified entity didn't have that
337    /// attribute
338    //
339    // CAUTION: this type is publicly exported in `cedar-policy`.
340    // Don't make fields `pub`, don't make breaking changes, and use caution
341    // when adding public methods.
342    #[derive(Debug, PartialEq, Eq, Clone, Error)]
343    #[error("`{entity}` does not have the attribute `{attr}`")]
344    pub struct EntityAttrDoesNotExistError {
345        /// Entity that didn't have the attribute
346        pub(crate) entity: Arc<EntityUID>,
347        /// Name of the attribute it didn't have
348        pub(crate) attr: SmolStr,
349        /// (First five) Available attributes on the entity
350        pub(crate) available_attrs: Vec<SmolStr>,
351        /// Total number of attributes on the entity
352        pub(crate) total_attrs: usize,
353        /// Source location
354        pub(crate) source_loc: Option<Loc>,
355    }
356
357    impl Diagnostic for EntityAttrDoesNotExistError {
358        impl_diagnostic_from_source_loc_opt_field!(source_loc);
359
360        fn help<'a>(&'a self) -> Option<Box<dyn std::fmt::Display + 'a>> {
361            if self.available_attrs.is_empty() {
362                Some(Box::new("entity does not have any attributes"))
363            } else if self.available_attrs.len() == self.total_attrs {
364                Some(Box::new(format!(
365                    "Available attributes: {:?}",
366                    self.available_attrs
367                )))
368            } else {
369                Some(Box::new(format!(
370                    "available attributes: [{}, ... ({} more attributes) ]",
371                    self.available_attrs.iter().join(","),
372                    self.total_attrs - self.available_attrs.len()
373                )))
374            }
375        }
376    }
377
378    /// Tried to get an attribute of a (non-entity) record, but that record didn't
379    /// have that attribute
380    //
381    // CAUTION: this type is publicly exported in `cedar-policy`.
382    // Don't make fields `pub`, don't make breaking changes, and use caution
383    // when adding public methods.
384    #[derive(Debug, PartialEq, Eq, Clone, Error)]
385    #[error("record does not have the attribute `{attr}`")]
386    pub struct RecordAttrDoesNotExistError {
387        /// Name of the attribute we tried to access
388        pub(crate) attr: SmolStr,
389        /// (First five) Available attributes on the record
390        pub(crate) available_attrs: Vec<SmolStr>,
391        /// The total number of attrs this record has
392        pub(crate) total_attrs: usize,
393        /// Source location
394        pub(crate) source_loc: Option<Loc>,
395    }
396
397    impl Diagnostic for RecordAttrDoesNotExistError {
398        impl_diagnostic_from_source_loc_opt_field!(source_loc);
399
400        fn help<'a>(&'a self) -> Option<Box<dyn std::fmt::Display + 'a>> {
401            if self.available_attrs.is_empty() {
402                Some(Box::new("record does not have any attributes"))
403            } else if self.available_attrs.len() == self.total_attrs {
404                Some(Box::new(format!(
405                    "available attributes: {:?}",
406                    self.available_attrs
407                )))
408            } else {
409                Some(Box::new(format!(
410                    "available attributes: [{}, ... ({} more attributes) ]",
411                    self.available_attrs.iter().join(","),
412                    self.total_attrs - self.available_attrs.len()
413                )))
414            }
415        }
416    }
417
418    /// Tried to evaluate an operation on values with incorrect types for that
419    /// operation
420    //
421    // CAUTION: this type is publicly exported in `cedar-policy`.
422    // Don't make fields `pub`, don't make breaking changes, and use caution
423    // when adding public methods.
424    #[derive(Debug, PartialEq, Eq, Clone, Error)]
425    pub struct TypeError {
426        /// Expected one of these types
427        pub(crate) expected: NonEmpty<Type>,
428        /// Encountered this type instead
429        pub(crate) actual: Type,
430        /// Optional advice for how to fix this error
431        pub(crate) advice: Option<String>,
432        /// Source location
433        pub(crate) source_loc: Option<Loc>,
434    }
435
436    impl Diagnostic for TypeError {
437        impl_diagnostic_from_source_loc_opt_field!(source_loc);
438
439        fn help<'a>(&'a self) -> Option<Box<dyn std::fmt::Display + 'a>> {
440            self.advice.as_ref().map(|advice| Box::new(advice) as _)
441        }
442    }
443
444    impl std::fmt::Display for TypeError {
445        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
446            if self.expected.len() == 1 {
447                write!(
448                    f,
449                    "type error: expected {}, got {}",
450                    self.expected.first(),
451                    self.actual
452                )
453            } else {
454                write!(
455                    f,
456                    "type error: expected one of [{}], got {}",
457                    self.expected.iter().join(", "),
458                    self.actual
459                )
460            }
461        }
462    }
463
464    /// Wrong number of arguments provided to an extension function
465    //
466    // CAUTION: this type is publicly exported in `cedar-policy`.
467    // Don't make fields `pub`, don't make breaking changes, and use caution
468    // when adding public methods.
469    #[derive(Debug, PartialEq, Eq, Clone, Error)]
470    #[error("wrong number of arguments provided to extension function `{function_name}`: expected {expected}, got {actual}")]
471    pub struct WrongNumArgumentsError {
472        /// arguments to this function
473        pub(crate) function_name: Name,
474        /// expected number of arguments
475        pub(crate) expected: usize,
476        /// actual number of arguments
477        pub(crate) actual: usize,
478        /// Source location
479        pub(crate) source_loc: Option<Loc>,
480    }
481
482    impl Diagnostic for WrongNumArgumentsError {
483        impl_diagnostic_from_source_loc_opt_field!(source_loc);
484    }
485
486    /// Overflow during an integer operation
487    //
488    // CAUTION: this type is publicly exported in `cedar-policy`.
489    // Don't make fields `pub`, don't make breaking changes, and use caution
490    // when adding public methods.
491    #[derive(Debug, PartialEq, Eq, Clone, Diagnostic, Error)]
492    pub enum IntegerOverflowError {
493        /// Overflow during a binary operation
494        #[error(transparent)]
495        #[diagnostic(transparent)]
496        BinaryOp(#[from] BinaryOpOverflowError),
497
498        /// Overflow during a unary operation
499        #[error(transparent)]
500        #[diagnostic(transparent)]
501        UnaryOp(#[from] UnaryOpOverflowError),
502    }
503
504    impl IntegerOverflowError {
505        pub(crate) fn source_loc(&self) -> Option<&Loc> {
506            match self {
507                Self::BinaryOp(e) => e.source_loc.as_ref(),
508                Self::UnaryOp(e) => e.source_loc.as_ref(),
509            }
510        }
511
512        pub(crate) fn with_maybe_source_loc(self, source_loc: Option<Loc>) -> Self {
513            match self {
514                Self::BinaryOp(e) => Self::BinaryOp(BinaryOpOverflowError { source_loc, ..e }),
515                Self::UnaryOp(e) => Self::UnaryOp(UnaryOpOverflowError { source_loc, ..e }),
516            }
517        }
518    }
519
520    /// Overflow during a binary operation
521    //
522    // CAUTION: this type is publicly exported in `cedar-policy`.
523    // Don't make fields `pub`, don't make breaking changes, and use caution
524    // when adding public methods.
525    #[derive(Debug, PartialEq, Eq, Clone, Error)]
526    #[error("integer overflow while attempting to {} the values `{arg1}` and `{arg2}`", match .op { BinaryOp::Add => "add", BinaryOp::Sub => "subtract", BinaryOp::Mul => "multiply", _ => "perform an operation on" })]
527    pub struct BinaryOpOverflowError {
528        /// overflow while evaluating this operator
529        pub(crate) op: BinaryOp,
530        /// first argument to that operator
531        pub(crate) arg1: Value,
532        /// second argument to that operator
533        pub(crate) arg2: Value,
534        /// Source location
535        pub(crate) source_loc: Option<Loc>,
536    }
537
538    impl Diagnostic for BinaryOpOverflowError {
539        impl_diagnostic_from_source_loc_opt_field!(source_loc);
540    }
541
542    /// Overflow during a unary operation
543    //
544    // CAUTION: this type is publicly exported in `cedar-policy`.
545    // Don't make fields `pub`, don't make breaking changes, and use caution
546    // when adding public methods.
547    #[derive(Debug, PartialEq, Eq, Clone, Error)]
548    #[error("integer overflow while attempting to {} the value `{arg}`", match .op { UnaryOp::Neg => "negate", _ => "perform an operation on" })]
549    pub struct UnaryOpOverflowError {
550        /// overflow while evaluating this operator
551        pub(crate) op: UnaryOp,
552        /// argument to that operator
553        pub(crate) arg: Value,
554        /// Source location
555        pub(crate) source_loc: Option<Loc>,
556    }
557
558    impl Diagnostic for UnaryOpOverflowError {
559        impl_diagnostic_from_source_loc_opt_field!(source_loc);
560    }
561
562    /// Not all template slots were linked
563    //
564    // CAUTION: this type is publicly exported in `cedar-policy`.
565    // Don't make fields `pub`, don't make breaking changes, and use caution
566    // when adding public methods.
567    #[derive(Debug, PartialEq, Eq, Clone, Error)]
568    #[error("template slot `{slot}` was not linked")]
569    pub struct UnlinkedSlotError {
570        /// Slot which was not linked
571        pub(crate) slot: SlotId,
572        /// Source location
573        pub(crate) source_loc: Option<Loc>,
574    }
575
576    impl Diagnostic for UnlinkedSlotError {
577        impl_diagnostic_from_source_loc_opt_field!(source_loc);
578    }
579
580    /// Evaluation error thrown by an extension function
581    //
582    // CAUTION: this type is publicly exported in `cedar-policy`.
583    // Don't make fields `pub`, don't make breaking changes, and use caution
584    // when adding public methods.
585    #[derive(Debug, PartialEq, Eq, Clone, Error)]
586    #[error("error while evaluating `{extension_name}` extension function: {msg}")]
587    pub struct ExtensionFunctionExecutionError {
588        /// Name of the extension throwing the error
589        pub(crate) extension_name: Name,
590        /// Error message from the extension
591        pub(crate) msg: String,
592        /// Source location
593        pub(crate) source_loc: Option<Loc>,
594    }
595
596    impl Diagnostic for ExtensionFunctionExecutionError {
597        impl_diagnostic_from_source_loc_opt_field!(source_loc);
598    }
599
600    impl ExtensionFunctionExecutionError {
601        /// Get the name of the extension that threw this error
602        pub fn extension_name(&self) -> String {
603            self.extension_name.to_string()
604        }
605    }
606
607    /// This error is raised if an expression contains unknowns and cannot be
608    /// reduced to a [`Value`]. In order to return partial results, use the
609    /// partial evaluation APIs instead.
610    //
611    // CAUTION: this type is publicly exported in `cedar-policy`.
612    // Don't make fields `pub`, don't make breaking changes, and use caution
613    // when adding public methods.
614    #[derive(Debug, PartialEq, Eq, Clone, Error)]
615    #[error("the expression contains unknown(s): `{expr}`")]
616    pub struct NonValueError {
617        /// Expression that contained unknown(s)
618        pub(crate) expr: Expr,
619        /// Source location
620        pub(crate) source_loc: Option<Loc>,
621    }
622
623    impl Diagnostic for NonValueError {
624        impl_diagnostic_from_source_loc_opt_field!(source_loc);
625
626        fn help<'a>(&'a self) -> Option<Box<dyn std::fmt::Display + 'a>> {
627            Some(Box::new("consider using the partial evaluation APIs"))
628        }
629    }
630
631    /// Maximum recursion limit reached for expression evaluation
632    //
633    // CAUTION: this type is publicly exported in `cedar-policy`.
634    // Don't make fields `pub`, don't make breaking changes, and use caution
635    // when adding public methods.
636    #[derive(Debug, PartialEq, Eq, Clone, Error)]
637    #[error("recursion limit reached")]
638    pub struct RecursionLimitError {
639        /// Source location
640        pub(crate) source_loc: Option<Loc>,
641    }
642
643    impl Diagnostic for RecursionLimitError {
644        impl_diagnostic_from_source_loc_opt_field!(source_loc);
645    }
646}
647
648/// Type alias for convenience
649pub type Result<T> = std::result::Result<T, EvaluationError>;