ad_astra/runtime/
error.rs

1////////////////////////////////////////////////////////////////////////////////
2// This file is part of "Ad Astra", an embeddable scripting programming       //
3// language platform.                                                         //
4//                                                                            //
5// This work is proprietary software with source-available code.              //
6//                                                                            //
7// To copy, use, distribute, or contribute to this work, you must agree to    //
8// the terms of the General License Agreement:                                //
9//                                                                            //
10// https://github.com/Eliah-Lakhin/ad-astra/blob/master/EULA.md               //
11//                                                                            //
12// The agreement grants a Basic Commercial License, allowing you to use       //
13// this work in non-commercial and limited commercial products with a total   //
14// gross revenue cap. To remove this commercial limit for one of your         //
15// products, you must acquire a Full Commercial License.                      //
16//                                                                            //
17// If you contribute to the source code, documentation, or related materials, //
18// you must grant me an exclusive license to these contributions.             //
19// Contributions are governed by the "Contributions" section of the General   //
20// License Agreement.                                                         //
21//                                                                            //
22// Copying the work in parts is strictly forbidden, except as permitted       //
23// under the General License Agreement.                                       //
24//                                                                            //
25// If you do not or cannot agree to the terms of this Agreement,              //
26// do not use this work.                                                      //
27//                                                                            //
28// This work is provided "as is", without any warranties, express or implied, //
29// except where such disclaimers are legally invalid.                         //
30//                                                                            //
31// Copyright (c) 2024 Ilya Lakhin (Илья Александрович Лахин).                 //
32// All rights reserved.                                                       //
33////////////////////////////////////////////////////////////////////////////////
34
35use std::{
36    collections::hash_map::Entry,
37    error::Error as StdError,
38    fmt::{Debug, Display, Formatter},
39    ops::Range,
40    result::Result as StdResult,
41    str::Utf8Error,
42    sync::Arc,
43};
44
45use ahash::{AHashMap, AHashSet};
46use lady_deirdre::{arena::Identifiable, format::AnnotationPriority, lexis::ToSpan};
47
48use crate::{
49    analysis::ModuleTextResolver,
50    format::{format_script_path, ScriptSnippet},
51    runtime::{ops::OperatorKind, Origin, TypeMeta},
52};
53
54/// A result of a runtime API call, which can either be a normal value or a
55/// [RuntimeError].
56pub type RuntimeResult<T> = StdResult<T, RuntimeError>;
57
58/// A helper trait for the [RuntimeResult] object.
59///
60/// This trait is automatically implemented for RuntimeResult and provides the
61/// [expect_blame](Self::expect_blame) function, which either unwraps the
62/// underlying value or panics if the result is [Err], indicating where the
63/// RuntimeError [originated](RuntimeError::primary_origin).
64pub trait RuntimeResultExt {
65    /// The [Ok] type of the underlying [Result].
66    type OkType;
67
68    /// If the result is [Ok], returns the underlying data; otherwise, panics
69    /// at the location where the RuntimeError
70    /// [originated](RuntimeError::primary_origin).
71    fn expect_blame(self, message: &str) -> Self::OkType;
72}
73
74impl<T> RuntimeResultExt for RuntimeResult<T> {
75    type OkType = T;
76
77    #[inline(always)]
78    fn expect_blame(self, message: &str) -> Self::OkType {
79        match self {
80            Ok(ok) => ok,
81
82            Err(error) => {
83                let origin = *error.primary_origin();
84
85                match origin {
86                    Origin::Rust(origin) => origin.blame(&format!("{message}\n{error}")),
87
88                    Origin::Script(origin) => {
89                        panic!(
90                            "{}: {message}\n{error}",
91                            format_script_path(origin.id(), None),
92                        );
93                    }
94                }
95            }
96        }
97    }
98}
99
100/// Represents any error that may occur during the evaluation of Script code.
101///
102/// This object implements the [Debug] and [Display] traits. The Display
103/// implementation provides a brief description of the underlying error.
104///
105/// However, it is recommended to use the [RuntimeError::display] function
106/// instead, as it renders the script's source code and annotates it with
107/// detailed error messages.
108#[derive(Clone, Debug)]
109#[non_exhaustive]
110pub enum RuntimeError {
111    /// The script code attempts to access [Nil](crate::runtime::Cell::nil)
112    /// data.
113    Nil {
114        /// The range in Rust or Script source code where the data was accessed.
115        access_origin: Origin,
116    },
117
118    /// The script code attempts to access an object representing an array
119    /// with zero or more than one element.
120    NonSingleton {
121        /// The range in Rust or Script source code where the data was accessed.
122        access_origin: Origin,
123
124        /// The actual length of the array.
125        actual: usize,
126    },
127
128    /// The script array is too short and cannot be interpreted as an array
129    /// with the requested number of elements.
130    ShortSlice {
131        /// The range in Rust or Script source code where the data was accessed.
132        access_origin: Origin,
133
134        /// The required length of the array.
135        minimum: usize,
136
137        /// The actual length of the array.
138        actual: usize,
139    },
140
141    /// The script code attempts to index into an array or string, but the index
142    /// is out of bounds.
143    OutOfBounds {
144        /// The range in Rust or Script source code where the data was accessed.
145        access_origin: Origin,
146
147        /// The requested index for the array or string.
148        index: usize,
149
150        /// The actual length of the array or string.
151        length: usize,
152    },
153
154    /// The script code attempts to mutate an object that only provides
155    /// read-only access.
156    ReadOnly {
157        /// The range in Rust or Script source code where the data was accessed.
158        access_origin: Origin,
159
160        /// The range in Rust or Script source code where the data was created.
161        data_origin: Origin,
162    },
163
164    /// The script code attempts to read an object that only provides mutation
165    /// access.
166    WriteOnly {
167        /// The range in Rust or Script source code where the data was accessed.
168        access_origin: Origin,
169
170        /// The range in Rust or Script source code where the data was created.
171        data_origin: Origin,
172    },
173
174    /// The script code attempts to mutate an object that is currently borrowed
175    /// for reading.
176    ReadToWrite {
177        /// The range in Rust or Script source code where the data was accessed.
178        access_origin: Origin,
179
180        /// The range in Rust or Script source code where the data was
181        /// previously borrowed.
182        borrow_origin: Origin,
183    },
184
185    /// The script code attempts to read an object that is currently borrowed
186    /// for writing.
187    WriteToRead {
188        /// The range in Rust or Script source code where the data was accessed.
189        access_origin: Origin,
190
191        /// The range in Rust or Script source code where the data was
192        /// previously borrowed.
193        borrow_origin: Origin,
194    },
195
196    /// The script code attempts to borrow data for mutation more than once
197    /// at a time.
198    WriteToWrite {
199        /// The range in Rust or Script source code where the data was accessed.
200        access_origin: Origin,
201
202        /// The range in Rust or Script source code where the data was
203        /// previously borrowed.
204        borrow_origin: Origin,
205    },
206
207    /// The script attempts to decode a byte array that is not a valid UTF-8
208    /// encoding.
209    Utf8Decoding {
210        /// The range in Rust or Script source code where the data was accessed.
211        access_origin: Origin,
212
213        /// An error that occurred during UTF-8 decoding.
214        cause: Box<Utf8Error>,
215    },
216
217    /// The script attempts to borrow data too many times simultaneously.
218    BorrowLimit {
219        /// The range in Rust or Script source code where the data was accessed.
220        access_origin: Origin,
221
222        /// The maximum number of allowed simultaneous active borrows.
223        limit: usize,
224    },
225
226    /// The script attempts to use a data object as an argument for a function
227    /// or an operator, but the data type does not meet the requirements.
228    TypeMismatch {
229        /// The range in Rust or Script source code where the data was accessed.
230        access_origin: Origin,
231
232        /// The type of the data object being provided as an argument.
233        data_type: &'static TypeMeta,
234
235        /// A list of expected types acceptable for this operation.
236        expected_types: Vec<&'static TypeMeta>,
237    },
238
239    /// The script attempts to dereference a data object, but the data object
240    /// does not live long enough.
241    DowncastStatic {
242        /// The range in Rust or Script source code where the data was accessed.
243        access_origin: Origin,
244    },
245
246    /// The script calls a Rust function that results in [Result::Err].
247    UpcastResult {
248        /// The range in Rust or Script source code where the function was
249        /// invoked.
250        access_origin: Origin,
251
252        /// The inner value of the [Err] variant.
253        cause: Arc<dyn StdError + Send + Sync + 'static>,
254    },
255
256    /// The script attempts to cast one numeric type into another, but the
257    /// conversion is not possible for the specified source number value and the
258    /// requested destination type.
259    NumberCast {
260        /// The range in Rust or Script source code where the numeric object was
261        /// accessed for conversion.
262        access_origin: Origin,
263
264        /// The source type of the value from which the conversion was
265        /// requested.
266        from: &'static TypeMeta,
267
268        /// The destination type into which the source number should be
269        /// converted.
270        to: &'static TypeMeta,
271
272        /// The cause of the failure.
273        cause: NumberCastCause,
274
275        /// The source numeric value. This object implements [Display].
276        value: Arc<dyn NumValue>,
277    },
278
279    /// The script attempts to perform an operation between two primitive
280    /// numeric values (or on a single numeric value), but the operation results
281    /// in an error due to specific reasons (e.g., numeric overflow).
282    NumericOperation {
283        /// The range in Rust or Script source code where the operation was
284        /// requested.
285        invoke_origin: Origin,
286
287        /// The type of numeric operation.
288        kind: NumericOperationKind,
289
290        /// The left-hand side of the operation.
291        lhs: (&'static TypeMeta, Arc<dyn NumValue>),
292
293        /// The right-hand side of the operation. If omitted, the operation has
294        /// only one argument.
295        rhs: Option<(&'static TypeMeta, Arc<dyn NumValue>)>,
296
297        /// The numeric type expected to represent the result of the operation.
298        target: &'static TypeMeta,
299    },
300
301    /// The script attempts to cast a [Range] object into another range type
302    /// (e.g., `100..200` into `100..=199`), but such casting is not possible
303    /// due to unsatisfied bounds.
304    RangeCast {
305        /// The range in Rust or Script source code where the casting was
306        /// requested.
307        access_origin: Origin,
308
309        /// The original value of the range from which the casting was supposed
310        /// to happen.
311        from: Range<usize>,
312
313        /// The name of the target range type.
314        to: &'static str,
315    },
316
317    /// The script attempts to use a malformed range (e.g., `200..100`).
318    MalformedRange {
319        /// The range in Rust or Script source code where the range was
320        /// accessed.
321        access_origin: Origin,
322
323        /// The lower bound of the [Range].
324        start_bound: usize,
325
326        /// The upper bound of the [Range].
327        end_bound: usize,
328    },
329
330    /// The script attempts to parse a string into a primitive type, but the
331    /// string is malformed and cannot be interpreted as the requested
332    /// primitive type.
333    PrimitiveParse {
334        /// The range in Rust or Script source code where the string was
335        /// accessed for parsing.
336        access_origin: Origin,
337
338        /// The content of the string being parsed.
339        from: String,
340
341        /// The primitive type into which the string was supposed to be parsed.
342        to: &'static TypeMeta,
343
344        /// A description of the parse error.
345        cause: Arc<dyn StdError + Send + Sync + 'static>,
346    },
347
348    /// The script attempts to call a function with an incorrect number of
349    /// arguments, either too few or too many.
350    ArityMismatch {
351        /// The range in Rust or Script source code where the function was
352        /// invoked.
353        invocation_origin: Origin,
354
355        /// The range in Rust or Script source code where the function was
356        /// declared.
357        function_origin: Origin,
358
359        /// The expected number of parameters for the function.
360        parameters: usize,
361
362        /// The actual number of arguments that were passed during the
363        /// invocation.
364        arguments: usize,
365    },
366
367    /// The script attempts to apply an operator to an object, but the object's
368    /// type does not support this operator.
369    UndefinedOperator {
370        /// The range in Rust or Script source code where the operator was
371        /// applied.
372        access_origin: Origin,
373
374        /// The range in Rust or Script source code where the object was
375        /// created. If omitted, the object instance has not been created yet
376        /// (e.g., the operator is an instantiation operator).
377        receiver_origin: Option<Origin>,
378
379        /// The type of the object to which the operator was applied.
380        receiver_type: &'static TypeMeta,
381
382        /// The type of operator.
383        operator: OperatorKind,
384    },
385
386    /// The script attempts to access a field of an object, but the object does
387    /// not have the specified field.
388    UnknownField {
389        /// A Rust or Script source code range, where the field was accessed.
390        access_origin: Origin,
391
392        /// The range in Rust or Script source code where the field was
393        /// accessed.
394        receiver_origin: Origin,
395
396        /// The type of the receiver object.
397        receiver_type: &'static TypeMeta,
398
399        /// The name of the field.
400        field: String,
401    },
402
403    /// The script attempts to format a data object using the [Debug] or
404    /// [Display] implementations, but the formatter returns an error.
405    FormatError {
406        /// The range in Rust or Script source code where the object was
407        /// accessed for formatting.
408        access_origin: Origin,
409
410        /// The origin of the receiver object.
411        receiver_origin: Origin,
412    },
413
414    /// The script attempts to access a package that is not fully registered
415    /// (e.g., the export system has been switched to a shallow mode).
416    UnknownPackage {
417        /// The range in Rust or Script source code where the package metadata
418        /// was accessed.
419        access_origin: Origin,
420
421        /// The name of the package.
422        name: &'static str,
423
424        /// The version of the package.
425        version: &'static str,
426    },
427
428    /// The script evaluation has been interrupted by the thread's
429    /// [runtime hook](crate::interpret::set_runtime_hook).
430    Interrupted {
431        /// The range in Rust or Script source code where the interruption
432        /// occurred.
433        origin: Origin,
434    },
435
436    /// The script has been interrupted because the interpreter's memory stack
437    /// for the thread overflowed.
438    StackOverflow {
439        /// The range in Rust or Script source code where the interruption
440        /// occurred.
441        origin: Origin,
442    },
443}
444
445impl Display for RuntimeError {
446    fn fmt(&self, formatter: &mut Formatter<'_>) -> std::fmt::Result {
447        match self {
448            Self::Nil { .. } => formatter.write_str("inaccessible data"),
449
450            Self::NonSingleton { actual, .. } => formatter.write_fmt(format_args!(
451                "expected a single data instance, but the array with {actual} \
452                elements provided",
453            )),
454
455            Self::ShortSlice {
456                minimum, actual, ..
457            } => formatter.write_fmt(format_args!(
458                "expected an array with at least {minimum} elements, but the \
459                array with {actual} elements provided",
460            )),
461
462            Self::OutOfBounds { index, length, .. } => {
463                formatter.write_fmt(format_args!("index {index} out of 0..{length} bounds",))
464            }
465
466            Self::ReadOnly { .. } => formatter.write_str("read-only data"),
467
468            Self::WriteOnly { .. } => formatter.write_str("write-only data"),
469
470            Self::ReadToWrite { .. } => {
471                formatter.write_str("cannot access data for write while it is being read")
472            }
473
474            Self::WriteToRead { .. } => {
475                formatter.write_str("cannot access data for read while it is being written")
476            }
477
478            Self::WriteToWrite { .. } => {
479                formatter.write_str("cannot access data for write more than once")
480            }
481
482            Self::Utf8Decoding { .. } => formatter.write_str("invalid utf-8 encoding"),
483
484            Self::BorrowLimit { .. } => formatter.write_str("too many simultaneous data accesses"),
485
486            Self::TypeMismatch {
487                data_type,
488                expected_types,
489                ..
490            } => {
491                let partition = Self::partition_types(expected_types);
492
493                match partition.is_empty() {
494                    true => formatter.write_fmt(format_args!("unexpected '{data_type}' data type")),
495
496                    false => {
497                        let partition = partition.join(", or ");
498
499                        formatter.write_fmt(format_args!(
500                            "expected {partition}, but '{data_type}' data type provided"
501                        ))
502                    }
503                }
504            }
505
506            Self::DowncastStatic { .. } => formatter
507                .write_str("cannot get static reference to the data owned by the script engine"),
508
509            Self::UpcastResult { .. } => {
510                formatter.write_str("the function returned explicit error")
511            }
512
513            Self::NumberCast {
514                from,
515                to,
516                cause,
517                value,
518                ..
519            } => {
520                use NumberCastCause::*;
521
522                match cause {
523                    Infinite => formatter.write_fmt(format_args!(
524                        "cannot cast infinity value of {from} type to {to}"
525                    )),
526
527                    NAN => formatter
528                        .write_fmt(format_args!("cannot cast NAN value of {from} type to {to}")),
529
530                    Overflow => {
531                        formatter.write_fmt(format_args!("cannot cast {value}{from} to {to} type"))
532                    }
533
534                    Underflow => {
535                        formatter.write_fmt(format_args!("cannot cast {value}{from} to {to} type"))
536                    }
537                }
538            }
539
540            Self::NumericOperation { kind, lhs, rhs, .. } => {
541                use NumericOperationKind::*;
542
543                match (lhs, kind, rhs) {
544                    ((lhs_ty, lhs_value), Add, Some((rhs_ty, rhs_value))) => formatter.write_fmt(
545                        format_args!("cannot add {rhs_value}{rhs_ty} to {lhs_value}{lhs_ty}"),
546                    ),
547
548                    ((lhs_ty, lhs_value), Sub, Some((rhs_ty, rhs_value))) => {
549                        formatter.write_fmt(format_args!(
550                            "cannot subtract {rhs_value}{rhs_ty} from {lhs_value}{lhs_ty}"
551                        ))
552                    }
553
554                    ((lhs_ty, lhs_value), Mul, Some((rhs_ty, rhs_value))) => formatter.write_fmt(
555                        format_args!("cannot multiply {rhs_value}{rhs_ty} by {lhs_value}{lhs_ty}"),
556                    ),
557
558                    ((lhs_ty, lhs_value), Div, Some((rhs_ty, rhs_value))) => formatter.write_fmt(
559                        format_args!("cannot divide {rhs_value}{rhs_ty} by {lhs_value}{lhs_ty}"),
560                    ),
561
562                    ((lhs_ty, lhs_value), Neg, None) => formatter.write_fmt(format_args!(
563                        "cannot get negative number of {lhs_value}{lhs_ty}"
564                    )),
565
566                    ((lhs_ty, lhs_value), Shl, Some((rhs_ty, rhs_value))) => {
567                        formatter.write_fmt(format_args!(
568                            "cannot shift {lhs_value}{lhs_ty} left by {rhs_value}{rhs_ty} bits"
569                        ))
570                    }
571
572                    ((lhs_ty, lhs_value), Shr, Some((rhs_ty, rhs_value))) => {
573                        formatter.write_fmt(format_args!(
574                            "cannot shift {lhs_value}{lhs_ty} right by {rhs_value}{rhs_ty} bits"
575                        ))
576                    }
577
578                    ((lhs_ty, lhs_value), Rem, Some((_rhs_ty, _rhs_value))) => {
579                        formatter.write_fmt(format_args!(
580                            "cannot get {lhs_value}{lhs_ty} reminder of \
581                            division by {lhs_value}{lhs_ty}"
582                        ))
583                    }
584
585                    _ => formatter.write_str("invalid numeric operation"),
586                }
587            }
588
589            Self::RangeCast { from, to, .. } => formatter.write_fmt(format_args!(
590                "cannot cast {from:?} range to {to} type. target type bounds mismatch"
591            )),
592
593            Self::MalformedRange {
594                start_bound,
595                end_bound,
596                ..
597            } => formatter.write_fmt(format_args!(
598                "range {start_bound} start bound is greater than the range end bound {end_bound}"
599            )),
600
601            Self::PrimitiveParse { from, to, .. } => {
602                formatter.write_fmt(format_args!("failed to parse {from:?} as {to}"))
603            }
604
605            Self::ArityMismatch {
606                parameters,
607                arguments,
608                ..
609            } => match *parameters == 1 {
610                true => formatter.write_fmt(format_args!(
611                    "the function requires 1 argument, but {arguments} provided"
612                )),
613                false => formatter.write_fmt(format_args!(
614                    "the function requires {parameters} arguments, but {arguments} provided"
615                )),
616            },
617
618            Self::UndefinedOperator {
619                receiver_type,
620                operator,
621                ..
622            } => formatter.write_fmt(format_args!(
623                "type '{receiver_type}' does not implement {operator}"
624            )),
625
626            Self::UnknownField {
627                receiver_type,
628                field,
629                ..
630            } => formatter.write_fmt(format_args!(
631                "type '{receiver_type}' does not have field '{field}'"
632            )),
633
634            Self::FormatError { .. } => formatter
635                .write_str("an error occurred during Debug or Display format function call"),
636
637            Self::UnknownPackage { name, version, .. } => {
638                formatter.write_fmt(format_args!("unknown {name}@{version} script package"))
639            }
640
641            Self::Interrupted { .. } => formatter.write_str("script evaluation interrupted"),
642
643            Self::StackOverflow { .. } => formatter.write_str("script engine stack overflow"),
644        }
645    }
646}
647
648impl StdError for RuntimeError {
649    #[inline]
650    fn source(&self) -> Option<&(dyn StdError + 'static)> {
651        match self {
652            Self::Utf8Decoding { cause, .. } => Some(cause),
653            Self::UpcastResult { cause, .. } => Some(cause),
654            Self::PrimitiveParse { cause, .. } => Some(cause),
655            _ => None,
656        }
657    }
658}
659
660impl RuntimeError {
661    /// Returns a printable object that renders the script's source code and
662    /// annotates it with error messages describing the underlying error object
663    /// and pointing to the source code location(s) where the error occurred.
664    ///
665    /// This function provides a canonical way to print end-user-facing script
666    /// evaluation error messages.
667    ///
668    /// The `resolver` parameter specifies an object through which the returned
669    /// printer accesses the [ModuleText](crate::analysis::ModuleText).
670    ///
671    /// In a multi-module environment, a RuntimeError may occur in any of these
672    /// modules and could potentially relate to several scripts. The resolver
673    /// allows the printer to access their source code texts when formatting the
674    /// message.
675    ///
676    /// If your runtime configuration consists of just one script module, or if
677    /// you are confident that the modules are semantically isolated from each
678    /// other (by default, modules are isolated), you can directly use the
679    /// ModuleText of the [script module](crate::analysis::ScriptModule) as the
680    /// `resolver` argument.
681    pub fn display<'a>(&self, resolver: &'a impl ModuleTextResolver) -> impl Display + 'a {
682        enum DisplayError<'a> {
683            Snippet(ScriptSnippet<'a>),
684            String(String),
685        }
686
687        impl<'a> Display for DisplayError<'a> {
688            #[inline(always)]
689            fn fmt(&self, formatter: &mut Formatter<'_>) -> std::fmt::Result {
690                match self {
691                    Self::Snippet(display) => Display::fmt(display, formatter),
692                    Self::String(display) => Display::fmt(display, formatter),
693                }
694            }
695        }
696
697        let primary_description = self.primary_description();
698
699        let Origin::Script(primary_origin) = self.primary_origin() else {
700            return DisplayError::String(primary_description);
701        };
702
703        let Some(primary_text) = resolver.resolve(primary_origin.id()) else {
704            return DisplayError::String(primary_description);
705        };
706
707        if !primary_origin.is_valid_span(primary_text) {
708            return DisplayError::String(primary_description);
709        }
710
711        let secondary_description = self.secondary_description();
712
713        let mut snippet = primary_text.snippet();
714
715        snippet.set_caption("runtime error");
716        snippet.annotate(
717            primary_origin,
718            AnnotationPriority::Primary,
719            primary_description,
720        );
721
722        let mut summary = self.summary();
723
724        match self.secondary_origin() {
725            Some(Origin::Script(secondary_origin))
726                if secondary_origin.id() == primary_origin.id() =>
727            {
728                snippet.annotate(
729                    secondary_origin,
730                    AnnotationPriority::Secondary,
731                    secondary_description,
732                );
733            }
734
735            Some(Origin::Script(secondary_origin)) => {
736                if let Some(secondary_text) = resolver.resolve(primary_origin.id()) {
737                    if secondary_origin.is_valid_span(secondary_text) {
738                        summary.push_str("\n\n");
739
740                        let mut inner_snippet = secondary_text.snippet();
741
742                        inner_snippet.annotate(
743                            secondary_origin,
744                            AnnotationPriority::Secondary,
745                            secondary_description,
746                        );
747
748                        summary.push_str(&inner_snippet.to_string());
749                    }
750                }
751            }
752
753            Some(Origin::Rust(secondary_origin)) => {
754                if let Some(code) = secondary_origin.code {
755                    summary.push_str(&format!("\n\n{code}: {secondary_description}"));
756                }
757            }
758
759            None => (),
760        }
761
762        snippet.set_summary(summary);
763
764        DisplayError::Snippet(snippet)
765    }
766
767    /// Returns the Rust or Script source code range where the error occurred.
768    pub fn primary_origin(&self) -> &Origin {
769        match self {
770            Self::Nil { access_origin, .. } => access_origin,
771
772            Self::NonSingleton { access_origin, .. } => access_origin,
773
774            Self::ShortSlice { access_origin, .. } => access_origin,
775
776            Self::OutOfBounds { access_origin, .. } => access_origin,
777
778            Self::ReadOnly { access_origin, .. } => access_origin,
779
780            Self::WriteOnly { access_origin, .. } => access_origin,
781
782            Self::ReadToWrite { access_origin, .. } => access_origin,
783
784            Self::WriteToRead { access_origin, .. } => access_origin,
785
786            Self::WriteToWrite { access_origin, .. } => access_origin,
787
788            Self::Utf8Decoding { access_origin, .. } => access_origin,
789
790            Self::BorrowLimit { access_origin, .. } => access_origin,
791
792            Self::TypeMismatch { access_origin, .. } => access_origin,
793
794            Self::DowncastStatic { access_origin, .. } => access_origin,
795
796            Self::UpcastResult { access_origin, .. } => access_origin,
797
798            Self::NumberCast { access_origin, .. } => access_origin,
799
800            Self::RangeCast { access_origin, .. } => access_origin,
801
802            Self::MalformedRange { access_origin, .. } => access_origin,
803
804            Self::NumericOperation { invoke_origin, .. } => invoke_origin,
805
806            Self::PrimitiveParse { access_origin, .. } => access_origin,
807
808            Self::ArityMismatch {
809                invocation_origin, ..
810            } => invocation_origin,
811
812            Self::UndefinedOperator { access_origin, .. } => access_origin,
813
814            Self::UnknownField { access_origin, .. } => access_origin,
815
816            Self::FormatError { access_origin, .. } => access_origin,
817
818            Self::UnknownPackage { access_origin, .. } => access_origin,
819
820            Self::Interrupted { origin } => origin,
821
822            Self::StackOverflow { origin, .. } => origin,
823        }
824    }
825
826    /// Returns an additional Rust or Script source code range that hints at an
827    /// extra location related to the cause of the error.
828    ///
829    /// For example, the [ReadToWrite](Self::ReadToWrite) error variant includes
830    /// an additional location that indicates the previous borrowing site.
831    ///
832    /// Most error variants do not have an extra location, and in such cases,
833    /// this function will return None.
834    pub fn secondary_origin(&self) -> Option<&Origin> {
835        match self {
836            Self::Nil { .. } => None,
837
838            Self::NonSingleton { .. } => None,
839
840            Self::ShortSlice { .. } => None,
841
842            Self::OutOfBounds { .. } => None,
843
844            Self::ReadOnly { data_origin, .. } => Some(data_origin),
845
846            Self::WriteOnly { data_origin, .. } => Some(data_origin),
847
848            Self::ReadToWrite { borrow_origin, .. } => Some(borrow_origin),
849
850            Self::WriteToRead { borrow_origin, .. } => Some(borrow_origin),
851
852            Self::WriteToWrite { borrow_origin, .. } => Some(borrow_origin),
853
854            Self::Utf8Decoding { .. } => None,
855
856            Self::BorrowLimit { .. } => None,
857
858            Self::TypeMismatch { .. } => None,
859
860            Self::DowncastStatic { .. } => None,
861
862            Self::UpcastResult { .. } => None,
863
864            Self::NumberCast { .. } => None,
865
866            Self::NumericOperation { .. } => None,
867
868            Self::RangeCast { .. } => None,
869
870            Self::MalformedRange { .. } => None,
871
872            Self::PrimitiveParse { .. } => None,
873
874            Self::ArityMismatch {
875                function_origin, ..
876            } => Some(function_origin),
877
878            Self::UndefinedOperator {
879                receiver_origin, ..
880            } => receiver_origin.as_ref(),
881
882            Self::UnknownField {
883                receiver_origin, ..
884            } => Some(receiver_origin),
885
886            Self::FormatError {
887                receiver_origin, ..
888            } => Some(receiver_origin),
889
890            Self::UnknownPackage { .. } => None,
891
892            Self::Interrupted { .. } => None,
893
894            Self::StackOverflow { .. } => None,
895        }
896    }
897
898    /// Returns an error message string related to the
899    /// [primary_origin](Self::primary_origin).
900    ///
901    /// This function returns the same message string that you would get by
902    /// formatting RuntimeError using the Display implementation.
903    #[inline(always)]
904    pub fn primary_description(&self) -> String {
905        self.to_string()
906    }
907
908    /// Returns an error message string related to the
909    /// [secondary_origin](Self::secondary_origin).
910    ///
911    /// If the error variant does not have an extra location, this function
912    /// returns an empty string.
913    pub fn secondary_description(&self) -> String {
914        match self {
915            Self::Nil { .. } => String::new(),
916
917            Self::NonSingleton { .. } => String::new(),
918
919            Self::ShortSlice { .. } => String::new(),
920
921            Self::OutOfBounds { .. } => String::new(),
922
923            Self::ReadOnly { .. } => String::from("data object origin"),
924
925            Self::WriteOnly { .. } => String::from("data object origin"),
926
927            Self::ReadToWrite { .. } => String::from("active read access"),
928
929            Self::WriteToRead { .. } => String::from("active write access"),
930
931            Self::WriteToWrite { .. } => String::from("active write access"),
932
933            Self::Utf8Decoding { .. } => String::new(),
934
935            Self::BorrowLimit { .. } => String::new(),
936
937            Self::TypeMismatch { .. } => String::new(),
938
939            Self::DowncastStatic { .. } => String::new(),
940
941            Self::UpcastResult { .. } => String::new(),
942
943            Self::NumberCast { .. } => String::new(),
944
945            Self::NumericOperation { .. } => String::new(),
946
947            Self::RangeCast { .. } => String::new(),
948
949            Self::MalformedRange { .. } => String::new(),
950
951            Self::PrimitiveParse { .. } => String::new(),
952
953            Self::ArityMismatch { .. } => String::from("function origin"),
954
955            Self::UndefinedOperator {
956                receiver_origin, ..
957            } if receiver_origin.is_some() => String::from("receiver origin"),
958
959            Self::UndefinedOperator { .. } => String::new(),
960
961            Self::UnknownField { .. } => String::from("receiver origin"),
962
963            Self::FormatError { .. } => String::from("receiver object"),
964
965            Self::UnknownPackage { .. } => String::new(),
966
967            Self::Interrupted { .. } => String::new(),
968
969            Self::StackOverflow { .. } => String::new(),
970        }
971    }
972
973    /// Returns a detailed summary of this error.
974    ///
975    /// This message is the same one that would be printed in the footer of the
976    /// [RuntimeError::display] object.
977    pub fn summary(&self) -> String {
978        let result = match self {
979            Self::Nil { .. } => {
980                r#"The requested operation has been applied on the void data.
981
982The source of the void data could be:
983    - an empty array "[]",
984    - a struct field that does not exists in the struct,
985    - a function or operator that does not return any value,
986    - a function that returns "Option::None",
987    - or any other source.
988
989Use the ? operator to check if the value is void: "if a? {...}"."#
990            }
991
992            Self::NonSingleton { .. } => {
993                r#"Most script operations require singleton objects (just normal objects)
994and cannot be applied to arrays with zero or more than one element.
995
996Consider using the index operator to retrieve a single element from the array:
997    "my_array[3] + 10" instead of "my_array + 10"."#
998            }
999
1000            Self::ShortSlice { .. } => {
1001                r#"The underlying operation requires an array of longer length."#
1002            }
1003
1004            Self::OutOfBounds { .. } => {
1005                r#"The specified range or an index is out of the array bounds."#
1006            }
1007
1008            Self::ReadOnly { .. } => {
1009                r#"The underlying operation requires write access to one of its arguments,
1010but the argument reference provides read-only access."#
1011            }
1012
1013            Self::WriteOnly { .. } => {
1014                r#"The underlying operation requires read access to one of its arguments,
1015but the argument reference provides write-only access."#
1016            }
1017
1018            Self::ReadToWrite { .. } => {
1019                r#"The underlying operation requires write access to one of its arguments,
1020but the argument object is currently being read.
1021
1022The script engine does not allow simultaneous read and write access
1023to the same data.
1024
1025For instance, if the script calls a function (or an operator) with this object
1026as an argument and the function returns a reference that indirectly points
1027to the argument's data, the object is blocked for writing until the reference's
1028lifetime ends."#
1029            }
1030
1031            Self::WriteToRead { .. } => {
1032                r#"The underlying operation requires read access to one of its arguments,
1033but the argument object is currently being written.
1034
1035The script engine does not allow simultaneous read and write access
1036to the same data.
1037
1038For instance, if the script calls a function (or an operator) with this object
1039as an argument and the function returns a reference that indirectly modifies
1040argument's data, the object is blocked for reading until the reference's
1041lifetime ends."#
1042            }
1043
1044            Self::WriteToWrite { .. } => {
1045                r#"The underlying operation requires write access to one of its arguments,
1046but the argument object is currently being written.
1047
1048The script engine mandates that the ongoing write access to the data object
1049is exclusive.
1050
1051For instance, if the script calls a function (or an operator) with this object
1052as an argument and the function returns a reference that indirectly modifies
1053argument's data, the object is blocked from another write access until
1054the reference's lifetime ends."#
1055            }
1056
1057            Self::Utf8Decoding { cause, .. } => {
1058                let mut result = String::from(
1059                    r#"The underlying operation is attempting to reinterpret an array of bytes
1060as a UTF-8 encoding of the Unicode text, but the script engine has detected
1061that this encoding is invalid.
1062
1063Error description:"#,
1064                );
1065
1066                for line in cause.to_string().split("\n") {
1067                    result.push_str("\n    ");
1068                    result.push_str(line);
1069                }
1070
1071                return result;
1072            }
1073
1074            Self::BorrowLimit { .. } => {
1075                r#"The script engine has a predefined limit for active references
1076to the same data object.
1077
1078This limit may be exceeded, for example, if a recursive function attempts
1079to access the same object too many times."#
1080            }
1081
1082            Self::TypeMismatch { .. } => {
1083                r#"The underlying function (or operator) requires an argument of a different type
1084than the one being provided."#
1085            }
1086
1087            Self::DowncastStatic { .. } => {
1088                r#"The underlying function (or operator) requested a data object
1089with a lifetime that may be different from the actual object's lifetime."#
1090            }
1091
1092            Self::UpcastResult { cause, .. } => {
1093                let mut result = String::from(
1094                    r#"The invoked function (or operator) returned explicit error.
1095
1096Error description:"#,
1097                );
1098
1099                for line in cause.to_string().split("\n") {
1100                    result.push_str("\n    ");
1101                    result.push_str(line);
1102                }
1103
1104                return result;
1105            }
1106
1107            Self::NumberCast { cause, .. } => match cause {
1108                NumberCastCause::Infinite | NumberCastCause::NAN => {
1109                    r#"Failed to cast primitive numeric type to another numeric type."#
1110                }
1111
1112                NumberCastCause::Overflow => {
1113                    r#"Failed to cast primitive numeric type to another numeric type.
1114
1115The source value is bigger than the target type upper bound.
1116"#
1117                }
1118
1119                NumberCastCause::Underflow => {
1120                    r#"Failed to cast primitive numeric type to another numeric type.
1121
1122The source value is lesser than the target type lower bound.
1123"#
1124                }
1125            },
1126
1127            Self::NumericOperation { target, .. } => {
1128                let mut result = String::from(
1129                    r#"Failed to perform numeric operation between two primitive types
1130
1131The result overflows "#,
1132                );
1133
1134                result.push_str(&format!("{target}"));
1135
1136                result.push_str(" bounds.");
1137
1138                return result;
1139            }
1140
1141            Self::RangeCast { .. } => r#"Failed to cast a range to another range type."#,
1142
1143            Self::MalformedRange { .. } => r#"Malformed range bounds."#,
1144
1145            Self::PrimitiveParse { cause, .. } => {
1146                let mut result = String::from(r#"String parse error:"#);
1147
1148                for line in cause.to_string().split("\n") {
1149                    result.push_str("\n    ");
1150                    result.push_str(line);
1151                }
1152
1153                return result;
1154            }
1155
1156            Self::ArityMismatch {
1157                parameters,
1158                arguments,
1159                ..
1160            } => match *parameters > *arguments {
1161                true => r#"Not enough arguments."#,
1162                false => r#"Too many arguments."#,
1163            },
1164
1165            Self::UndefinedOperator { .. } => {
1166                r#"The object's type that is responsible to perform specified operation does not
1167implement this operator."#
1168            }
1169
1170            Self::UnknownField { .. } => r#"The object does not have specified field."#,
1171
1172            Self::FormatError { .. } => r#"Failed to turn the object into string representation."#,
1173
1174            Self::UnknownPackage { .. } => r#"Package lookup failure."#,
1175
1176            Self::Interrupted { .. } => r#"The script explicitly terminated by the host request."#,
1177
1178            Self::StackOverflow { .. } => {
1179                r#"The script engine failed to invoke the function,
1180because the engine's thread stack exceeded its limit.
1181                
1182This situation may occur in functions with unlimited recursion."#
1183            }
1184        };
1185
1186        String::from(result)
1187    }
1188
1189    fn partition_types(types: &[&'static TypeMeta]) -> Vec<String> {
1190        let mut result = Vec::new();
1191        let type_metas = types.iter().copied().collect::<AHashSet<_>>();
1192        let mut type_families = AHashMap::new();
1193
1194        for ty in type_metas {
1195            let family = ty.family();
1196
1197            if family.is_fn() || family.len() <= 1 {
1198                result.push(format!("'{}'", ty.name()));
1199                continue;
1200            }
1201
1202            match type_families.entry(family) {
1203                Entry::Vacant(entry) => {
1204                    let _ = entry.insert(AHashSet::from([ty]));
1205                }
1206
1207                Entry::Occupied(mut entry) => {
1208                    let _ = entry.get_mut().insert(ty);
1209                }
1210            }
1211        }
1212
1213        for (family, types) in type_families {
1214            if family.len() == types.len() {
1215                result.push(format!("'{}'", family.name()));
1216                continue;
1217            }
1218
1219            for ty in types {
1220                result.push(format!("'{}'", ty.name()));
1221            }
1222        }
1223
1224        result.sort();
1225
1226        result
1227    }
1228}
1229
1230/// A type of the [RuntimeError::NumberCast] error.
1231///
1232/// This object describes the reason why the source numeric value cannot be
1233/// converted into the destination numeric value.
1234#[derive(Clone, Copy, PartialEq, Eq, Debug)]
1235pub enum NumberCastCause {
1236    /// The target type does not support representation of infinite numbers.
1237    Infinite,
1238
1239    /// The target type does not support representation of NaN numbers.
1240    NAN,
1241
1242    /// The source numeric value is too large for the range of the target type.
1243    Overflow,
1244
1245    /// The source numeric value is too small for the range of the target type.
1246    Underflow,
1247}
1248
1249/// A type of the [RuntimeError::NumericOperation] error.
1250///
1251/// This object describes the type of operation that caused the error.
1252#[derive(Clone, Copy, PartialEq, Eq, Debug)]
1253pub enum NumericOperationKind {
1254    /// The sum of two numbers: `10 + 20`.
1255    Add,
1256
1257    /// The subtraction of two numbers: `10 - 20`.
1258    Sub,
1259
1260    /// The multiplication of two numbers: `10 * 20`.
1261    Mul,
1262
1263    /// The division of two numbers: `10 / 20`.
1264    Div,
1265
1266    /// The negative value of a number: `-10`.
1267    Neg,
1268
1269    /// A bitwise shift to the left: `10 << 20`.
1270    Shl,
1271
1272    /// A bitwise shift to the right: `10 >> 20`.
1273    Shr,
1274
1275    /// The remainder of division: `10 % 20`.
1276    Rem,
1277}
1278
1279pub trait NumValue: Debug + Display + Send + Sync + 'static {}
1280
1281impl<T: Debug + Display + Send + Sync + 'static> NumValue for T {}