Skip to main content

rune/runtime/
inst.rs

1use core::cmp::Ordering;
2use core::fmt;
3
4#[cfg(feature = "musli")]
5use musli_core::{Decode, Encode};
6use rune_macros::InstDisplay;
7#[cfg(feature = "serde")]
8use serde::{Deserialize, Serialize};
9
10use crate as rune;
11use crate::alloc;
12use crate::alloc::prelude::*;
13use crate::Hash;
14
15use super::{Call, FormatSpec, Type, Value};
16
17/// An instruction in the virtual machine.
18#[derive(Clone, Copy)]
19#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(transparent))]
20#[cfg_attr(feature = "musli", derive(Decode, Encode), musli(crate = musli_core, transparent))]
21pub struct Inst {
22    pub(crate) kind: Kind,
23}
24
25impl Inst {
26    #[inline]
27    pub(crate) fn new(kind: Kind) -> Self {
28        Self { kind }
29    }
30}
31
32impl fmt::Display for Inst {
33    #[inline]
34    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
35        self.kind.fmt(fmt)
36    }
37}
38
39impl fmt::Debug for Inst {
40    #[inline]
41    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
42        self.kind.fmt(fmt)
43    }
44}
45
46impl TryClone for Inst {
47    #[inline]
48    fn try_clone(&self) -> alloc::Result<Self> {
49        Ok(Self {
50            kind: self.kind.try_clone()?,
51        })
52    }
53}
54
55/// Pre-canned panic reasons.
56///
57/// To formulate a custom reason, use
58/// [`VmError::panic`][crate::runtime::VmError::panic].
59#[derive(Debug, TryClone, Clone, Copy)]
60#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
61#[cfg_attr(feature = "musli", derive(Decode, Encode), musli(crate = musli_core))]
62#[try_clone(copy)]
63pub(crate) enum PanicReason {
64    /// A pattern didn't match where it unconditionally has to.
65    UnmatchedPattern,
66}
67
68impl PanicReason {
69    /// The identifier of the panic.
70    fn ident(&self) -> &'static str {
71        match *self {
72            Self::UnmatchedPattern => "unmatched pattern",
73        }
74    }
75}
76
77impl fmt::Display for PanicReason {
78    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
79        match *self {
80            Self::UnmatchedPattern => write!(fmt, "pattern did not match")?,
81        }
82
83        Ok(())
84    }
85}
86
87/// The kind of an instruction in the virtual machine.
88#[derive(Debug, TryClone, Clone, Copy, InstDisplay)]
89#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
90#[cfg_attr(feature = "musli", derive(Decode, Encode), musli(crate = musli_core))]
91#[try_clone(copy)]
92pub(crate) enum Kind {
93    /// Make sure that the memory region has `size` slots of memory available.
94    Allocate {
95        /// The size of the memory region to allocate.
96        size: usize,
97    },
98    /// Not operator. Takes a boolean from the top of the stack  and inverts its
99    /// logical value.
100    ///
101    /// # Operation
102    ///
103    /// ```text
104    /// <bool>
105    /// => <bool>
106    /// ```
107    Not {
108        /// The operand to negate.
109        addr: Address,
110        /// Whether the produced value from the not should be kept or not.
111        out: Output,
112    },
113    /// Negate the numerical value on the stack.
114    ///
115    /// # Operation
116    ///
117    /// ```text
118    /// <number>
119    /// => <number>
120    /// ```
121    Neg {
122        /// The operand to negate.
123        addr: Address,
124        /// Whether the produced value from the negation should be kept or not.
125        out: Output,
126    },
127    /// Construct a closure that takes the given number of arguments and
128    /// captures `count` elements from the top of the stack.
129    ///
130    /// # Operation
131    ///
132    /// ```text
133    /// <value..>
134    /// => <fn>
135    /// ```
136    #[cfg_attr(feature = "musli", musli(packed))]
137    Closure {
138        /// The hash of the internally stored closure function.
139        hash: Hash,
140        /// Where to load captured values from.
141        addr: Address,
142        /// The number of captured values to store in the environment.
143        count: usize,
144        /// Where to store the produced closure.
145        out: Output,
146    },
147    /// Perform a function call within the same unit.
148    ///
149    /// It will construct a new stack frame which includes the last `args`
150    /// number of entries.
151    #[cfg_attr(feature = "musli", musli(packed))]
152    CallOffset {
153        /// The offset of the function being called in the same unit.
154        offset: usize,
155        /// The calling convention to use.
156        call: Call,
157        /// The address where the arguments are stored.
158        addr: Address,
159        /// The number of arguments passed in at `addr`.
160        args: usize,
161        /// Whether the return value should be kept or not.
162        out: Output,
163    },
164    /// Call a function by hash.
165    ///
166    /// The function will be looked up in the unit and context. The arguments
167    /// passed to the function call are stored at `addr`, where `size`
168    /// determines the number of arguments. The arguments will be dropped.
169    ///
170    /// The return value of the function call will be written to `out`.
171    #[cfg_attr(feature = "musli", musli(packed))]
172    Call {
173        /// The hash of the function to call.
174        hash: Hash,
175        /// The address of the arguments being passed.
176        addr: Address,
177        /// The number of arguments passed in at `addr`.
178        args: usize,
179        /// Whether the return value should be kept or not.
180        out: Output,
181    },
182    /// Call an associated function.
183    ///
184    /// The instance being called should be the the object at address `addr`.
185    /// The number of arguments specified should include this object.
186    ///
187    /// The return value of the function call will be written to `out`.
188    #[cfg_attr(feature = "musli", musli(packed))]
189    CallAssociated {
190        /// The hash of the name of the function to call.
191        hash: Hash,
192        /// The address of arguments being passed.
193        addr: Address,
194        /// The number of arguments passed in at `addr`.
195        args: usize,
196        /// Whether the return value should be kept or not.
197        out: Output,
198    },
199    /// Look up an instance function.
200    ///
201    /// The instance being used is stored at `addr`, and the function hash to look up is `hash`.
202    #[cfg_attr(feature = "musli", musli(packed))]
203    LoadInstanceFn {
204        /// The address of the instance for which the function is being loaded.
205        addr: Address,
206        /// The name hash of the instance function.
207        hash: Hash,
208        /// Where to store the loaded instance function.
209        out: Output,
210    },
211    /// Perform a function call on a function pointer stored on the stack.
212    ///
213    /// # Operation
214    ///
215    /// ```text
216    /// <fn>
217    /// <args...>
218    /// => <ret>
219    /// ```
220    #[cfg_attr(feature = "musli", musli(packed))]
221    CallFn {
222        /// The address of the function being called.
223        function: Address,
224        /// The address of the arguments being passed.
225        addr: Address,
226        /// The number of arguments passed in at `addr`.
227        args: usize,
228        /// Whether the returned value from calling the function should be kept
229        /// or not.
230        out: Output,
231    },
232    /// Perform an index get operation. Pushing the result on the stack.
233    ///
234    /// # Operation
235    ///
236    /// ```text
237    /// <target>
238    /// <index>
239    /// => <value>
240    /// ```
241    #[cfg_attr(feature = "musli", musli(packed))]
242    IndexGet {
243        /// How the target is addressed.
244        target: Address,
245        /// How the index is addressed.
246        index: Address,
247        /// Whether the produced value should be kept or not.
248        out: Output,
249    },
250    /// Set the given index of the tuple on the stack, with the given value.
251    ///
252    /// # Operation
253    ///
254    /// ```text
255    /// <value>
256    /// <tuple>
257    /// => *nothing*
258    /// ```
259    #[cfg_attr(feature = "musli", musli(packed))]
260    TupleIndexSet {
261        /// The object being assigned to.
262        target: Address,
263        /// The index to set.
264        index: usize,
265        /// The value being assigned.
266        value: Address,
267    },
268    /// Get the given index out of a tuple from the given variable slot.
269    /// Errors if the item doesn't exist or the item is not a tuple.
270    ///
271    /// # Operation
272    ///
273    /// ```text
274    /// => <value>
275    /// ```
276    #[cfg_attr(feature = "musli", musli(packed))]
277    TupleIndexGetAt {
278        /// The address where the tuple we are getting from is stored.
279        addr: Address,
280        /// The index to fetch.
281        index: usize,
282        /// Whether the produced value should be kept or not.
283        out: Output,
284    },
285    /// Set the given index out of an object on the top of the stack.
286    /// Errors if the item doesn't exist or the item is not an object.
287    ///
288    /// The index is identifier by a static string slot, which is provided as an
289    /// argument.
290    ///
291    /// # Operation
292    ///
293    /// ```text
294    /// <object>
295    /// <value>
296    /// =>
297    /// ```
298    #[cfg_attr(feature = "musli", musli(packed))]
299    ObjectIndexSet {
300        /// The object being assigned to.
301        target: Address,
302        /// The static string slot corresponding to the index to set.
303        slot: usize,
304        /// The value being assigned.
305        value: Address,
306    },
307    /// Get the given index out of an object from the given variable slot.
308    /// Errors if the item doesn't exist or the item is not an object.
309    ///
310    /// The index is identifier by a static string slot, which is provided as an
311    /// argument.
312    ///
313    /// # Operation
314    ///
315    /// ```text
316    /// => <value>
317    /// ```
318    #[cfg_attr(feature = "musli", musli(packed))]
319    ObjectIndexGetAt {
320        /// The address where the object is stored.
321        addr: Address,
322        /// The static string slot corresponding to the index to fetch.
323        slot: usize,
324        /// Where to store the fetched value.
325        out: Output,
326    },
327    /// Perform an index set operation.
328    ///
329    /// # Operation
330    ///
331    /// ```text
332    /// <target>
333    /// <index>
334    /// <value>
335    /// => *noop*
336    /// ```
337    IndexSet {
338        /// The object being assigned to.
339        target: Address,
340        /// The index to set.
341        index: Address,
342        /// The value being assigned.
343        value: Address,
344    },
345    /// Await the future that is on the stack and push the value that it
346    /// produces.
347    ///
348    /// # Operation
349    ///
350    /// ```text
351    /// <future>
352    /// => <value>
353    /// ```
354    Await {
355        /// Address of the future being awaited.
356        addr: Address,
357        /// Whether the produced value from the await should be kept or not.
358        out: Output,
359    },
360    /// Select over `len` futures stored at address `addr`.
361    ///
362    /// Once a branch has been matched, will store the branch that matched in
363    /// the branch register and perform a jump by the index of the branch that
364    /// matched.
365    ///
366    /// Will also store the output if the future into `value`. If no branch
367    /// matched, the empty value will be stored.
368    #[cfg_attr(feature = "musli", musli(packed))]
369    Select {
370        /// The base address of futures being waited on.
371        addr: Address,
372        /// The number of futures to poll.
373        len: usize,
374        /// Where to store the value produced by the future that completed.
375        value: Output,
376    },
377    /// Load the given function by hash and push onto the stack.
378    ///
379    /// # Operation
380    ///
381    /// ```text
382    /// => <value>
383    /// ```
384    #[cfg_attr(feature = "musli", musli(packed))]
385    LoadFn {
386        /// The hash of the function to push.
387        hash: Hash,
388        /// Where to store the loaded function.
389        out: Output,
390    },
391    /// Push a value onto the stack.
392    ///
393    /// # Operation
394    ///
395    /// ```text
396    /// => <value>
397    /// ```
398    #[cfg_attr(feature = "musli", musli(packed))]
399    Store {
400        /// The value to push.
401        value: InstValue,
402        /// Where the value is being copied to.
403        out: Output,
404    },
405    /// Copy a variable from a location `offset` relative to the current call
406    /// frame.
407    ///
408    /// A copy is very cheap. It simply means pushing a reference to the stack.
409    #[cfg_attr(feature = "musli", musli(packed))]
410    Copy {
411        /// Address of the value being copied.
412        addr: Address,
413        /// Where the value is being copied to.
414        out: Output,
415    },
416    /// Move a variable from a location `offset` relative to the current call
417    /// frame.
418    #[cfg_attr(feature = "musli", musli(packed))]
419    Move {
420        /// Address of the value being moved.
421        addr: Address,
422        /// Where the value is being moved to.
423        out: Output,
424    },
425    /// Drop the given value set.
426    #[cfg_attr(feature = "musli", musli(packed))]
427    Drop {
428        /// An indicator of the set of addresses to drop.
429        set: usize,
430    },
431    /// Swap two values on the stack using their offsets relative to the current
432    /// stack frame.
433    #[cfg_attr(feature = "musli", musli(packed))]
434    Swap {
435        /// Offset to the first value.
436        a: Address,
437        /// Offset to the second value.
438        b: Address,
439    },
440    /// Pop the current stack frame and restore the instruction pointer from it.
441    ///
442    /// The stack frame will be cleared, and the value on the top of the stack
443    /// will be left on top of it.
444    #[cfg_attr(feature = "musli", musli(packed))]
445    Return {
446        /// The address of the value to return.
447        addr: Address,
448    },
449    /// Pop the current stack frame and restore the instruction pointer from it.
450    ///
451    /// The stack frame will be cleared, and a unit value will be pushed to the
452    /// top of the stack.
453    ReturnUnit,
454    /// Unconditionally jump to `offset` relative to the current instruction
455    /// pointer.
456    ///
457    /// # Operation
458    ///
459    /// ```text
460    /// *nothing*
461    /// => *nothing*
462    /// ```
463    #[cfg_attr(feature = "musli", musli(packed))]
464    Jump {
465        /// Offset to jump to.
466        jump: usize,
467    },
468    /// Jump to `offset` relative to the current instruction pointer if the
469    /// condition is `true`.
470    ///
471    /// # Operation
472    ///
473    /// ```text
474    /// <boolean>
475    /// => *nothing*
476    /// ```
477    #[cfg_attr(feature = "musli", musli(packed))]
478    JumpIf {
479        /// The address of the condition for the jump.
480        cond: Address,
481        /// Offset to jump to.
482        jump: usize,
483    },
484    /// Jump to the given offset If the top of the stack is false.
485    ///
486    /// # Operation
487    ///
488    /// ```text
489    /// <bool>
490    /// => *noop*
491    /// ```
492    #[cfg_attr(feature = "musli", musli(packed))]
493    JumpIfNot {
494        /// The address of the condition for the jump.
495        cond: Address,
496        /// The offset to jump if the condition is true.
497        jump: usize,
498    },
499    /// Construct a vector at `out`, populating it with `count` elements from
500    /// `addr`.
501    ///
502    /// The values at `addr` are dropped.
503    #[cfg_attr(feature = "musli", musli(packed))]
504    Vec {
505        /// Where the arguments to the vector are stored.
506        addr: Address,
507        /// The number of elements in the vector.
508        count: usize,
509        /// Where to store the produced vector.
510        out: Output,
511    },
512    /// Construct a one element tuple at `out`, populating it with `count`
513    /// elements from `addr`.
514    ///
515    /// The values at `addr` are not dropped.
516    #[cfg_attr(feature = "musli", musli(packed))]
517    Tuple1 {
518        /// Tuple arguments.
519        #[inst_display(display_with = DisplayArray::new)]
520        addr: [Address; 1],
521        /// Where to store the produced tuple.
522        out: Output,
523    },
524    /// Construct a two element tuple at `out`, populating it with `count`
525    /// elements from `addr`.
526    ///
527    /// The values at `addr` are not dropped.
528    #[cfg_attr(feature = "musli", musli(packed))]
529    Tuple2 {
530        /// Tuple arguments.
531        #[inst_display(display_with = DisplayArray::new)]
532        addr: [Address; 2],
533        /// Where to store the produced tuple.
534        out: Output,
535    },
536    /// Construct a three element tuple at `out`, populating it with `count`
537    /// elements from `addr`.
538    ///
539    /// The values at `addr` are not dropped.
540    #[cfg_attr(feature = "musli", musli(packed))]
541    Tuple3 {
542        /// Tuple arguments.
543        #[inst_display(display_with = DisplayArray::new)]
544        addr: [Address; 3],
545        /// Where to store the produced tuple.
546        out: Output,
547    },
548    /// Construct a four element tuple at `out`, populating it with `count`
549    /// elements from `addr`.
550    ///
551    /// The values at `addr` are not dropped.
552    #[cfg_attr(feature = "musli", musli(packed))]
553    Tuple4 {
554        /// Tuple arguments.
555        #[inst_display(display_with = DisplayArray::new)]
556        addr: [Address; 4],
557        /// Where to store the produced tuple.
558        out: Output,
559    },
560    /// Construct a tuple at `out`, populating it with `count` elements from
561    /// `addr`.
562    ///
563    /// Unlike `TupleN` variants, values at `addr` are dropped.
564    #[cfg_attr(feature = "musli", musli(packed))]
565    Tuple {
566        /// Where the arguments to the tuple are stored.
567        addr: Address,
568        /// The number of elements in the tuple.
569        count: usize,
570        /// Where to store the produced tuple.
571        out: Output,
572    },
573    /// Take the tuple that is on top of the stack and push its content onto the
574    /// stack.
575    ///
576    /// This is used to unpack an environment for closures - if the closure has
577    /// an environment.
578    ///
579    /// # Operation
580    ///
581    /// ```text
582    /// <tuple>
583    /// => <value...>
584    /// ```
585    Environment {
586        /// The tuple to push.
587        addr: Address,
588        /// The expected size of the tuple.
589        count: usize,
590        /// Where to unpack the environment.
591        out: Output,
592    },
593    /// Construct a push an object onto the stack. The number of elements
594    /// in the object are determined the slot of the object keys `slot` and are
595    /// popped from the stack.
596    ///
597    /// For each element, a value is popped corresponding to the object key.
598    ///
599    /// # Operation
600    ///
601    /// ```text
602    /// <value..>
603    /// => <object>
604    /// ```
605    #[cfg_attr(feature = "musli", musli(packed))]
606    Object {
607        /// Where the arguments to the tuple are stored.
608        addr: Address,
609        /// The static slot of the object keys.
610        slot: usize,
611        /// Where to store the produced tuple.
612        out: Output,
613    },
614    /// Construct a range.
615    ///
616    /// The arguments loaded are determined by the range being constructed.
617    #[cfg_attr(feature = "musli", musli(packed))]
618    Range {
619        /// The kind of the range, which determines the number arguments on the
620        /// stack.
621        range: InstRange,
622        /// Where to store the produced range.
623        out: Output,
624    },
625    /// Construct a struct of type `hash` at `out`, populating it with fields
626    /// from `addr`. The number of fields and their names is determined by the
627    /// `slot` being referenced.
628    ///
629    /// The values at `addr` are dropped.
630    #[cfg_attr(feature = "musli", musli(packed))]
631    Struct {
632        /// The address to load fields from.
633        addr: Address,
634        /// The type of the struct to construct.
635        hash: Hash,
636        /// Where to write the constructed struct.
637        out: Output,
638    },
639    /// Construct a struct from a constant.
640    ///
641    /// The values at `addr` are dropped.
642    #[cfg_attr(feature = "musli", musli(packed))]
643    ConstConstruct {
644        /// Where constructor arguments are stored.
645        addr: Address,
646        /// The type of the struct to construct.
647        hash: Hash,
648        /// The number of constructor arguments.
649        count: usize,
650        /// Where to write the constructed struct.
651        out: Output,
652    },
653    /// Load a literal string from a static string slot.
654    ///
655    /// # Operation
656    ///
657    /// ```text
658    /// => <string>
659    /// ```
660    #[cfg_attr(feature = "musli", musli(packed))]
661    String {
662        /// The static string slot to load the string from.
663        slot: usize,
664        /// Where to store the string.
665        out: Output,
666    },
667    /// Load a literal byte string from a static byte string slot.
668    ///
669    /// # Operation
670    ///
671    /// ```text
672    /// => <bytes>
673    /// ```
674    #[cfg_attr(feature = "musli", musli(packed))]
675    Bytes {
676        /// The static byte string slot to load the string from.
677        slot: usize,
678        /// Where to store the bytes.
679        out: Output,
680    },
681    /// Load the value of a static item from the configured global storage.
682    ///
683    /// If the slot has not been initialized yet, the initializer declared for
684    /// it in the unit is evaluated and stored first.
685    ///
686    /// # Operation
687    ///
688    /// ```text
689    /// => <value>
690    /// ```
691    #[cfg_attr(feature = "musli", musli(packed))]
692    GlobalGet {
693        /// The static slot to load the value from.
694        slot: usize,
695        /// Where to store the value.
696        out: Output,
697    },
698    /// Store a value into a static item in the configured global storage.
699    ///
700    /// # Operation
701    ///
702    /// ```text
703    /// <value>
704    /// =>
705    /// ```
706    #[cfg_attr(feature = "musli", musli(packed))]
707    GlobalSet {
708        /// The static slot to store the value in.
709        slot: usize,
710        /// The address of the value being stored.
711        value: Address,
712    },
713    /// Pop the given number of values from the stack, and concatenate a string
714    /// from them.
715    ///
716    /// This is a dedicated template-string optimization.
717    ///
718    /// # Operation
719    ///
720    /// ```text
721    /// <value...>
722    /// => <string>
723    /// ```
724    #[cfg_attr(feature = "musli", musli(packed))]
725    StringConcat {
726        /// Where the strings to concatenate are stored.
727        addr: Address,
728        /// The number of items to pop from the stack.
729        len: usize,
730        /// The minimum string size used.
731        size_hint: usize,
732        /// Where to store the produced string.
733        out: Output,
734    },
735    /// Push a combined format specification and value onto the stack. The value
736    /// used is the last value on the stack.
737    #[cfg_attr(feature = "musli", musli(packed))]
738    Format {
739        /// Address of the value being formatted.
740        addr: Address,
741        /// The format specification to use.
742        spec: FormatSpec,
743        /// Where to store the produced format.
744        out: Output,
745    },
746    /// Perform the try operation which takes the value at the given `address`
747    /// and tries to unwrap it or return from the current call frame.
748    ///
749    /// # Operation
750    ///
751    /// ```text
752    /// <value>
753    /// => <boolean>
754    /// ```
755    #[cfg_attr(feature = "musli", musli(packed))]
756    Try {
757        /// Address of value to try.
758        addr: Address,
759        /// Where to store the value in case there is a continuation.
760        out: Output,
761    },
762    /// Test if the top of the stack is a specific character.
763    ///
764    /// # Operation
765    ///
766    /// ```text
767    /// <value>
768    /// => <boolean>
769    /// ```
770    #[cfg_attr(feature = "musli", musli(packed))]
771    EqChar {
772        /// Address of the value to compare.
773        addr: Address,
774        /// The character to test against.
775        #[inst_display(display_with = DisplayDebug::new)]
776        value: char,
777        /// Where to store the result of the comparison.
778        out: Output,
779    },
780    /// Test if the specified value is a specific signed integer.
781    #[cfg_attr(feature = "musli", musli(packed))]
782    EqSigned {
783        /// Address of the value to compare.
784        addr: Address,
785        /// The value to test against.
786        value: i64,
787        /// Where to store the result of the comparison.
788        out: Output,
789    },
790    /// Test if the specified value is a specific unsigned integer.
791    #[cfg_attr(feature = "musli", musli(packed))]
792    EqUnsigned {
793        /// Address of the value to compare.
794        addr: Address,
795        /// The value to test against.
796        value: u64,
797        /// Where to store the result of the comparison.
798        out: Output,
799    },
800    /// Test if the top of the stack is a specific boolean.
801    ///
802    /// # Operation
803    ///
804    /// ```text
805    /// <value>
806    /// => <boolean>
807    /// ```
808    #[cfg_attr(feature = "musli", musli(packed))]
809    EqBool {
810        /// Address of the value to compare.
811        addr: Address,
812        /// The value to test against.
813        value: bool,
814        /// Where to store the result of the comparison.
815        out: Output,
816    },
817    /// Compare the top of the stack against a static string slot.
818    ///
819    /// # Operation
820    ///
821    /// ```text
822    /// <value>
823    /// => <boolean>
824    /// ```
825    #[cfg_attr(feature = "musli", musli(packed))]
826    EqString {
827        /// Address of the value to compare.
828        addr: Address,
829        /// The slot to test against.
830        slot: usize,
831        /// Where to store the result of the comparison.
832        out: Output,
833    },
834    /// Compare the top of the stack against a static bytes slot.
835    ///
836    /// # Operation
837    ///
838    /// ```text
839    /// <value>
840    /// => <boolean>
841    /// ```
842    #[cfg_attr(feature = "musli", musli(packed))]
843    EqBytes {
844        /// Address of the value to compare.
845        addr: Address,
846        /// The slot to test against.
847        slot: usize,
848        /// Where to store the result of the comparison.
849        out: Output,
850    },
851    /// Test if the specified type matches.
852    ///
853    /// # Operation
854    ///
855    /// ```text
856    /// <value>
857    /// => <boolean>
858    /// ```
859    #[cfg_attr(feature = "musli", musli(packed))]
860    MatchType {
861        /// The type hash to match against.
862        hash: Hash,
863        /// The variant hash to match against.
864        variant_hash: Hash,
865        /// The address of the value to test.
866        addr: Address,
867        /// Where to store the output.
868        out: Output,
869    },
870    /// Test that the top of the stack is a tuple with the given length
871    /// requirements.
872    ///
873    /// # Operation
874    ///
875    /// ```text
876    /// <value>
877    /// => <boolean>
878    /// ```
879    #[cfg_attr(feature = "musli", musli(packed))]
880    MatchSequence {
881        /// Type constraints that the sequence must match.
882        hash: Hash,
883        /// The minimum length to test for.
884        len: usize,
885        /// Whether the operation should check exact `true` or minimum length
886        /// `false`.
887        exact: bool,
888        /// The address of the value to test.
889        addr: Address,
890        /// Where to store the output.
891        out: Output,
892    },
893    /// Test that the top of the stack is an object matching the given slot of
894    /// object keys.
895    ///
896    /// # Operation
897    ///
898    /// ```text
899    /// <object>
900    /// => <boolean>
901    /// ```
902    #[cfg_attr(feature = "musli", musli(packed))]
903    MatchObject {
904        /// The slot of object keys to use.
905        slot: usize,
906        /// Whether the operation should check exact `true` or minimum length
907        /// `false`.
908        exact: bool,
909        /// The address of the value to test.
910        addr: Address,
911        /// Where to store the output.
912        out: Output,
913    },
914    /// Perform a generator yield where the value yielded is expected to be
915    /// found at the top of the stack.
916    ///
917    /// This causes the virtual machine to suspend itself.
918    ///
919    /// # Operation
920    ///
921    /// ```text
922    /// <value>
923    /// => <value>
924    /// ```
925    Yield {
926        /// Address of the value being yielded.
927        addr: Address,
928        /// Where to store the produced resume value.
929        out: Output,
930    },
931    /// Perform a generator yield with a unit.
932    ///
933    /// This causes the virtual machine to suspend itself.
934    ///
935    /// # Operation
936    ///
937    /// ```text
938    /// => <unit>
939    /// ```
940    YieldUnit {
941        /// Where to store the produced resume value.
942        out: Output,
943    },
944    /// An operation.
945    #[cfg_attr(feature = "musli", musli(packed))]
946    Op {
947        /// The kind of operation.
948        op: InstOp,
949        /// The address of the first argument.
950        a: Address,
951        /// The address of the second argument.
952        b: Address,
953        /// Whether the produced value from the operation should be kept or not.
954        out: Output,
955    },
956    /// An arithmetic operation.
957    #[cfg_attr(feature = "musli", musli(packed))]
958    Arithmetic {
959        /// The kind of operation.
960        op: InstArithmeticOp,
961        /// The address of the first argument.
962        a: Address,
963        /// The address of the second argument.
964        b: Address,
965        /// Whether the produced value from the operation should be kept or not.
966        out: Output,
967    },
968    /// A bitwise operation.
969    #[cfg_attr(feature = "musli", musli(packed))]
970    Bitwise {
971        /// The kind of operation.
972        op: InstBitwiseOp,
973        /// The address of the first argument.
974        a: Address,
975        /// The address of the second argument.
976        b: Address,
977        /// Whether the produced value from the operation should be kept or not.
978        out: Output,
979    },
980    /// A shift operation.
981    #[cfg_attr(feature = "musli", musli(packed))]
982    Shift {
983        /// The kind of operation.
984        op: InstShiftOp,
985        /// The address of the first argument.
986        a: Address,
987        /// The address of the second argument.
988        b: Address,
989        /// Whether the produced value from the operation should be kept or not.
990        out: Output,
991    },
992    /// Instruction for assigned arithmetic operations.
993    #[cfg_attr(feature = "musli", musli(packed))]
994    AssignArithmetic {
995        /// The kind of operation.
996        op: InstArithmeticOp,
997        /// The target of the operation.
998        target: InstTarget,
999        /// The value being assigned.
1000        rhs: Address,
1001    },
1002    /// Instruction for assigned bitwise operations.
1003    #[cfg_attr(feature = "musli", musli(packed))]
1004    AssignBitwise {
1005        /// The kind of operation.
1006        op: InstBitwiseOp,
1007        /// The target of the operation.
1008        target: InstTarget,
1009        /// The value being assigned.
1010        rhs: Address,
1011    },
1012    /// Instruction for assigned shift operations.
1013    #[cfg_attr(feature = "musli", musli(packed))]
1014    AssignShift {
1015        /// The kind of operation.
1016        op: InstShiftOp,
1017        /// The target of the operation.
1018        target: InstTarget,
1019        /// The value being assigned.
1020        rhs: Address,
1021    },
1022    /// Advance an iterator at the given position.
1023    #[cfg_attr(feature = "musli", musli(packed))]
1024    IterNext {
1025        /// The address of the iterator to advance.
1026        addr: Address,
1027        /// A relative jump to perform if the iterator could not be advanced.
1028        jump: usize,
1029        /// Where to store the produced value from the iterator.
1030        out: Output,
1031    },
1032    /// Cause the VM to panic and error out without a reason.
1033    ///
1034    /// This should only be used during testing or extreme scenarios that are
1035    /// completely unrecoverable.
1036    #[cfg_attr(feature = "musli", musli(packed))]
1037    Panic {
1038        /// The reason for the panic.
1039        #[inst_display(display_with = PanicReason::ident)]
1040        reason: PanicReason,
1041    },
1042}
1043
1044impl Kind {
1045    /// Construct an instruction to push a unit.
1046    pub(crate) fn unit(out: Output) -> Self {
1047        Self::Store {
1048            value: InstValue::Unit,
1049            out,
1050        }
1051    }
1052
1053    /// Construct an instruction to push a boolean.
1054    pub(crate) fn bool(b: bool, out: Output) -> Self {
1055        Self::Store {
1056            value: InstValue::Bool(b),
1057            out,
1058        }
1059    }
1060
1061    /// Construct an instruction to push a character.
1062    pub(crate) fn char(c: char, out: Output) -> Self {
1063        Self::Store {
1064            value: InstValue::Char(c),
1065            out,
1066        }
1067    }
1068
1069    /// Construct an instruction to push an integer.
1070    pub(crate) fn signed(v: i64, out: Output) -> Self {
1071        Self::Store {
1072            value: InstValue::Integer(v),
1073            out,
1074        }
1075    }
1076
1077    /// Construct an instruction to push an unsigned integer.
1078    pub(crate) fn unsigned(v: u64, out: Output) -> Self {
1079        Self::Store {
1080            value: InstValue::Unsigned(v),
1081            out,
1082        }
1083    }
1084
1085    /// Construct an instruction to push a float.
1086    pub(crate) fn float(v: f64, out: Output) -> Self {
1087        Self::Store {
1088            value: InstValue::Float(v),
1089            out,
1090        }
1091    }
1092
1093    /// Construct an instruction to push a type.
1094    pub(crate) fn ty(ty: Type, out: Output) -> Self {
1095        Self::Store {
1096            value: InstValue::Type(ty),
1097            out,
1098        }
1099    }
1100
1101    /// Construct an instruction to push an ordering.
1102    pub(crate) fn ordering(ordering: Ordering, out: Output) -> Self {
1103        Self::Store {
1104            value: InstValue::Ordering(ordering),
1105            out,
1106        }
1107    }
1108
1109    /// Construct an instruction to push a type hash.
1110    pub(crate) fn hash(hash: Hash, out: Output) -> Self {
1111        Self::Store {
1112            value: InstValue::Hash(hash),
1113            out,
1114        }
1115    }
1116}
1117
1118/// What to do with the output of an instruction.
1119#[derive(TryClone, Clone, Copy, PartialEq, Eq, Hash)]
1120#[try_clone(copy)]
1121#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(transparent))]
1122#[cfg_attr(feature = "musli", derive(Decode, Encode), musli(crate = musli_core, transparent))]
1123pub struct Output {
1124    offset: usize,
1125}
1126
1127impl Output {
1128    /// Construct a keep output kind.
1129    #[inline]
1130    pub(crate) fn keep(offset: usize) -> Self {
1131        assert_ne!(offset, usize::MAX, "Address is invalid");
1132        Self { offset }
1133    }
1134
1135    /// Construct a discard output kind.
1136    #[inline]
1137    pub(crate) fn discard() -> Self {
1138        Self { offset: usize::MAX }
1139    }
1140
1141    /// Check if the output is a keep.
1142    #[inline(always)]
1143    pub(crate) fn as_addr(&self) -> Option<Address> {
1144        if self.offset == usize::MAX {
1145            None
1146        } else {
1147            Some(Address::new(self.offset))
1148        }
1149    }
1150}
1151
1152impl fmt::Display for Output {
1153    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1154        if self.offset == usize::MAX {
1155            write!(f, "discard")
1156        } else {
1157            write!(f, "keep({})", self.offset)
1158        }
1159    }
1160}
1161
1162impl fmt::Debug for Output {
1163    #[inline]
1164    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1165        fmt::Display::fmt(self, f)
1166    }
1167}
1168
1169/// How an instruction addresses a value.
1170#[derive(Default, TryClone, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
1171#[repr(transparent)]
1172#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(transparent))]
1173#[cfg_attr(feature = "musli", derive(Decode, Encode), musli(crate = musli_core, transparent))]
1174#[try_clone(copy)]
1175pub struct Address {
1176    offset: usize,
1177}
1178
1179impl Address {
1180    /// The first possible address.
1181    pub const ZERO: Address = Address { offset: 0 };
1182
1183    /// An invalid address.
1184    pub const INVALID: Address = Address { offset: usize::MAX };
1185
1186    /// Construct a new address.
1187    #[inline]
1188    pub(crate) const fn new(offset: usize) -> Self {
1189        Self { offset }
1190    }
1191
1192    /// Get the offset of the address.
1193    #[inline]
1194    pub(crate) fn offset(self) -> usize {
1195        self.offset
1196    }
1197
1198    /// Get the address as an output.
1199    #[inline]
1200    pub(crate) fn output(self) -> Output {
1201        Output::keep(self.offset)
1202    }
1203}
1204
1205impl fmt::Display for Address {
1206    #[inline]
1207    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1208        if self.offset == usize::MAX {
1209            write!(f, "invalid")
1210        } else {
1211            self.offset.fmt(f)
1212        }
1213    }
1214}
1215
1216impl fmt::Debug for Address {
1217    #[inline]
1218    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1219        fmt::Display::fmt(self, f)
1220    }
1221}
1222
1223/// Range limits of a range expression.
1224#[derive(Debug, TryClone, Clone, Copy)]
1225#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1226#[cfg_attr(feature = "musli", derive(Decode, Encode), musli(crate = musli_core))]
1227#[try_clone(copy)]
1228pub(crate) enum InstRange {
1229    /// `start..`.
1230    RangeFrom {
1231        /// The start address of the range.
1232        start: Address,
1233    },
1234    /// `..`.
1235    RangeFull,
1236    /// `start..=end`.
1237    RangeInclusive {
1238        /// The start address of the range.
1239        start: Address,
1240        /// The end address of the range.
1241        end: Address,
1242    },
1243    /// `..=end`.
1244    RangeToInclusive {
1245        /// The end address of the range.
1246        end: Address,
1247    },
1248    /// `..end`.
1249    RangeTo {
1250        /// The end address of the range.
1251        end: Address,
1252    },
1253    /// `start..end`.
1254    Range {
1255        /// The start address of the range.
1256        start: Address,
1257        /// The end address of the range.
1258        end: Address,
1259    },
1260}
1261
1262impl fmt::Display for InstRange {
1263    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1264        match self {
1265            InstRange::RangeFrom { start } => write!(f, "{start}.."),
1266            InstRange::RangeFull => write!(f, ".."),
1267            InstRange::RangeInclusive { start, end } => write!(f, "{start}..={end}"),
1268            InstRange::RangeToInclusive { end } => write!(f, "..={end}"),
1269            InstRange::RangeTo { end } => write!(f, "..{end}"),
1270            InstRange::Range { start, end } => write!(f, "{start}..{end}"),
1271        }
1272    }
1273}
1274
1275/// The target of an operation.
1276#[derive(Debug, TryClone, Clone, Copy)]
1277#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1278#[cfg_attr(feature = "musli", derive(Decode, Encode), musli(crate = musli_core))]
1279#[try_clone(copy)]
1280pub(crate) enum InstTarget {
1281    /// Target is an offset to the current call frame.
1282    #[cfg_attr(feature = "musli", musli(packed))]
1283    Address(Address),
1284    /// Target the field of an object.
1285    #[cfg_attr(feature = "musli", musli(packed))]
1286    Field(Address, usize),
1287    /// Target a tuple field.
1288    #[cfg_attr(feature = "musli", musli(packed))]
1289    TupleField(Address, usize),
1290}
1291
1292impl fmt::Display for InstTarget {
1293    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1294        match self {
1295            Self::Address(addr) => write!(f, "address({addr})"),
1296            Self::Field(addr, slot) => write!(f, "field({addr}, {slot})"),
1297            Self::TupleField(addr, slot) => write!(f, "tuple-field({addr}, {slot})"),
1298        }
1299    }
1300}
1301
1302/// An operation between two values on the machine.
1303#[derive(Debug, TryClone, Clone, Copy)]
1304#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1305#[cfg_attr(feature = "musli", derive(Decode, Encode), musli(crate = musli_core))]
1306#[try_clone(copy)]
1307pub(crate) enum InstArithmeticOp {
1308    /// The add operation. `a + b`.
1309    Add,
1310    /// The sub operation. `a - b`.
1311    Sub,
1312    /// The multiply operation. `a * b`.
1313    Mul,
1314    /// The division operation. `a / b`.
1315    Div,
1316    /// The remainder operation. `a % b`.
1317    Rem,
1318}
1319
1320impl fmt::Display for InstArithmeticOp {
1321    #[inline]
1322    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1323        match self {
1324            Self::Add => {
1325                write!(f, "+")?;
1326            }
1327            Self::Sub => {
1328                write!(f, "-")?;
1329            }
1330            Self::Mul => {
1331                write!(f, "*")?;
1332            }
1333            Self::Div => {
1334                write!(f, "/")?;
1335            }
1336            Self::Rem => {
1337                write!(f, "%")?;
1338            }
1339        }
1340
1341        Ok(())
1342    }
1343}
1344
1345/// An operation between two values on the machine.
1346#[derive(Debug, TryClone, Clone, Copy)]
1347#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1348#[cfg_attr(feature = "musli", derive(Decode, Encode), musli(crate = musli_core))]
1349#[try_clone(copy)]
1350pub(crate) enum InstBitwiseOp {
1351    /// The bitwise and operation. `a & b`.
1352    BitAnd,
1353    /// The bitwise xor operation. `a ^ b`.
1354    BitXor,
1355    /// The bitwise or operation. `a | b`.
1356    BitOr,
1357}
1358
1359impl fmt::Display for InstBitwiseOp {
1360    #[inline]
1361    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1362        match self {
1363            Self::BitAnd => {
1364                write!(f, "&")?;
1365            }
1366            Self::BitXor => {
1367                write!(f, "^")?;
1368            }
1369            Self::BitOr => {
1370                write!(f, "|")?;
1371            }
1372        }
1373
1374        Ok(())
1375    }
1376}
1377
1378/// An operation between two values on the machine.
1379#[derive(Debug, TryClone, Clone, Copy)]
1380#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1381#[cfg_attr(feature = "musli", derive(Decode, Encode), musli(crate = musli_core))]
1382#[try_clone(copy)]
1383pub(crate) enum InstShiftOp {
1384    /// The shift left operation. `a << b`.
1385    Shl,
1386    /// The shift right operation. `a << b`.
1387    Shr,
1388}
1389
1390impl fmt::Display for InstShiftOp {
1391    #[inline]
1392    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1393        match self {
1394            Self::Shl => {
1395                write!(f, "<<")?;
1396            }
1397            Self::Shr => {
1398                write!(f, ">>")?;
1399            }
1400        }
1401
1402        Ok(())
1403    }
1404}
1405
1406/// An operation between two values on the machine.
1407#[derive(Debug, TryClone, Clone, Copy)]
1408#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1409#[cfg_attr(feature = "musli", derive(Decode, Encode), musli(crate = musli_core))]
1410#[try_clone(copy)]
1411pub(crate) enum InstOp {
1412    /// Compare two values on the stack for lt and push the result as a
1413    /// boolean on the stack.
1414    Lt,
1415    /// Compare two values on the stack for lte and push the result as a
1416    /// boolean on the stack.
1417    Le,
1418    /// Compare two values on the stack for gt and push the result as a
1419    /// boolean on the stack.
1420    Gt,
1421    /// Compare two values on the stack for gte and push the result as a
1422    /// boolean on the stack.
1423    Ge,
1424    /// Compare two values on the stack for equality and push the result as a
1425    /// boolean on the stack.
1426    ///
1427    /// # Operation
1428    ///
1429    /// ```text
1430    /// <b>
1431    /// <a>
1432    /// => <bool>
1433    /// ```
1434    Eq,
1435    /// Compare two values on the stack for inequality and push the result as a
1436    /// boolean on the stack.
1437    ///
1438    /// # Operation
1439    ///
1440    /// ```text
1441    /// <b>
1442    /// <a>
1443    /// => <bool>
1444    /// ```
1445    Neq,
1446    /// Coerce a value into the given type.
1447    ///
1448    /// # Operation
1449    ///
1450    /// ```text
1451    /// <type>
1452    /// <value>
1453    /// => <boolean>
1454    /// ```
1455    As,
1456    /// Test if the top of the stack is an instance of the second item on the
1457    /// stack.
1458    ///
1459    /// # Operation
1460    ///
1461    /// ```text
1462    /// <type>
1463    /// <value>
1464    /// => <boolean>
1465    /// ```
1466    Is,
1467    /// Test if the top of the stack is not an instance of the second item on
1468    /// the stack.
1469    ///
1470    /// # Operation
1471    ///
1472    /// ```text
1473    /// <type>
1474    /// <value>
1475    /// => <boolean>
1476    /// ```
1477    IsNot,
1478    /// Pop two values from the stack and test if they are both boolean true.
1479    ///
1480    /// # Operation
1481    ///
1482    /// ```text
1483    /// <boolean>
1484    /// <boolean>
1485    /// => <boolean>
1486    /// ```
1487    And,
1488    /// Pop two values from the stack and test if either of them are boolean
1489    /// true.
1490    ///
1491    /// # Operation
1492    ///
1493    /// ```text
1494    /// <boolean>
1495    /// <boolean>
1496    /// => <boolean>
1497    /// ```
1498    Or,
1499}
1500
1501impl fmt::Display for InstOp {
1502    #[inline]
1503    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1504        match self {
1505            Self::Lt => {
1506                write!(f, "<")?;
1507            }
1508            Self::Gt => {
1509                write!(f, ">")?;
1510            }
1511            Self::Le => {
1512                write!(f, "<=")?;
1513            }
1514            Self::Ge => {
1515                write!(f, ">=")?;
1516            }
1517            Self::Eq => {
1518                write!(f, "==")?;
1519            }
1520            Self::Neq => {
1521                write!(f, "!=")?;
1522            }
1523            Self::As => {
1524                write!(f, "as")?;
1525            }
1526            Self::Is => {
1527                write!(f, "is")?;
1528            }
1529            Self::IsNot => {
1530                write!(f, "is not")?;
1531            }
1532            Self::And => {
1533                write!(f, "&&")?;
1534            }
1535            Self::Or => {
1536                write!(f, "||")?;
1537            }
1538        }
1539
1540        Ok(())
1541    }
1542}
1543
1544/// A literal value that can be pushed.
1545#[derive(Debug, TryClone, Clone, Copy)]
1546#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1547#[cfg_attr(feature = "musli", derive(Decode, Encode), musli(crate = musli_core))]
1548#[try_clone(copy)]
1549pub(crate) enum InstValue {
1550    /// An empty tuple.
1551    Unit,
1552    /// A boolean.
1553    #[cfg_attr(feature = "musli", musli(packed))]
1554    Bool(bool),
1555    /// A character.
1556    #[cfg_attr(feature = "musli", musli(packed))]
1557    Char(char),
1558    /// An unsigned integer.
1559    #[cfg_attr(feature = "musli", musli(packed))]
1560    Unsigned(u64),
1561    /// An integer.
1562    #[cfg_attr(feature = "musli", musli(packed))]
1563    Integer(i64),
1564    /// A float.
1565    #[cfg_attr(feature = "musli", musli(packed))]
1566    Float(f64),
1567    /// A type hash.
1568    #[cfg_attr(feature = "musli", musli(packed))]
1569    Type(Type),
1570    /// An ordering.
1571    Ordering(
1572        #[cfg_attr(feature = "musli", musli(with = crate::musli::ordering))]
1573        #[cfg_attr(feature = "serde", serde(with = "crate::serde::ordering"))]
1574        Ordering,
1575    ),
1576    /// A hash.
1577    #[cfg_attr(feature = "musli", musli(packed))]
1578    Hash(Hash),
1579}
1580
1581impl InstValue {
1582    /// Convert into a value that can be pushed onto the stack.
1583    pub(crate) fn into_value(self) -> Value {
1584        match self {
1585            Self::Unit => Value::unit(),
1586            Self::Bool(v) => Value::from(v),
1587            Self::Char(v) => Value::from(v),
1588            Self::Unsigned(v) => Value::from(v),
1589            Self::Integer(v) => Value::from(v),
1590            Self::Float(v) => Value::from(v),
1591            Self::Type(v) => Value::from(v),
1592            Self::Ordering(v) => Value::from(v),
1593            Self::Hash(v) => Value::from(v),
1594        }
1595    }
1596}
1597
1598impl fmt::Display for InstValue {
1599    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1600        match self {
1601            Self::Unit => write!(f, "()")?,
1602            Self::Bool(v) => write!(f, "{v}")?,
1603            Self::Char(v) => write!(f, "{v:?}")?,
1604            Self::Unsigned(v) => write!(f, "{v}u64")?,
1605            Self::Integer(v) => write!(f, "{v}i64")?,
1606            Self::Float(v) => write!(f, "{v}")?,
1607            Self::Type(v) => write!(f, "{}", v.into_hash())?,
1608            Self::Ordering(v) => write!(f, "{v:?}")?,
1609            Self::Hash(v) => write!(f, "{v:?}")?,
1610        }
1611
1612        Ok(())
1613    }
1614}
1615
1616#[repr(transparent)]
1617struct DisplayArray<T>(T)
1618where
1619    T: ?Sized;
1620
1621impl<T> DisplayArray<[T]> {
1622    #[inline]
1623    fn new(value: &[T]) -> &Self {
1624        // SAFETY: The `DisplayArray` struct is a transparent wrapper around the
1625        // value.
1626        unsafe { &*(value as *const [T] as *const Self) }
1627    }
1628}
1629
1630impl<T> fmt::Display for DisplayArray<[T]>
1631where
1632    T: fmt::Display,
1633{
1634    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1635        let mut it = self.0.iter();
1636
1637        write!(f, "[")?;
1638        let last = it.next_back();
1639
1640        for value in it {
1641            write!(f, "{value}, ")?;
1642        }
1643
1644        if let Some(last) = last {
1645            last.fmt(f)?;
1646        }
1647
1648        write!(f, "]")?;
1649        Ok(())
1650    }
1651}
1652
1653#[repr(transparent)]
1654struct DisplayDebug<T>(T)
1655where
1656    T: ?Sized;
1657
1658impl<T> DisplayDebug<T>
1659where
1660    T: ?Sized,
1661{
1662    #[inline]
1663    fn new(value: &T) -> &Self {
1664        // SAFETY: The `DisplayDebug` struct is a transparent wrapper around the
1665        // value.
1666        unsafe { &*(value as *const T as *const Self) }
1667    }
1668}
1669
1670impl<T> fmt::Display for DisplayDebug<T>
1671where
1672    T: ?Sized + fmt::Debug,
1673{
1674    #[inline]
1675    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1676        fmt::Debug::fmt(&self.0, f)
1677    }
1678}