rune/runtime/inst.rs
1use core::cmp::Ordering;
2use core::fmt;
3
4#[cfg(feature = "musli")]
5use musli::{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(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))]
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))]
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 /// Pop the given number of values from the stack, and concatenate a string
682 /// from them.
683 ///
684 /// This is a dedicated template-string optimization.
685 ///
686 /// # Operation
687 ///
688 /// ```text
689 /// <value...>
690 /// => <string>
691 /// ```
692 #[cfg_attr(feature = "musli", musli(packed))]
693 StringConcat {
694 /// Where the strings to concatenate are stored.
695 addr: Address,
696 /// The number of items to pop from the stack.
697 len: usize,
698 /// The minimum string size used.
699 size_hint: usize,
700 /// Where to store the produced string.
701 out: Output,
702 },
703 /// Push a combined format specification and value onto the stack. The value
704 /// used is the last value on the stack.
705 #[cfg_attr(feature = "musli", musli(packed))]
706 Format {
707 /// Address of the value being formatted.
708 addr: Address,
709 /// The format specification to use.
710 spec: FormatSpec,
711 /// Where to store the produced format.
712 out: Output,
713 },
714 /// Perform the try operation which takes the value at the given `address`
715 /// and tries to unwrap it or return from the current call frame.
716 ///
717 /// # Operation
718 ///
719 /// ```text
720 /// <value>
721 /// => <boolean>
722 /// ```
723 #[cfg_attr(feature = "musli", musli(packed))]
724 Try {
725 /// Address of value to try.
726 addr: Address,
727 /// Where to store the value in case there is a continuation.
728 out: Output,
729 },
730 /// Test if the top of the stack is a specific character.
731 ///
732 /// # Operation
733 ///
734 /// ```text
735 /// <value>
736 /// => <boolean>
737 /// ```
738 #[cfg_attr(feature = "musli", musli(packed))]
739 EqChar {
740 /// Address of the value to compare.
741 addr: Address,
742 /// The character to test against.
743 #[inst_display(display_with = DisplayDebug::new)]
744 value: char,
745 /// Where to store the result of the comparison.
746 out: Output,
747 },
748 /// Test if the specified value is a specific signed integer.
749 #[cfg_attr(feature = "musli", musli(packed))]
750 EqSigned {
751 /// Address of the value to compare.
752 addr: Address,
753 /// The value to test against.
754 value: i64,
755 /// Where to store the result of the comparison.
756 out: Output,
757 },
758 /// Test if the specified value is a specific unsigned integer.
759 #[cfg_attr(feature = "musli", musli(packed))]
760 EqUnsigned {
761 /// Address of the value to compare.
762 addr: Address,
763 /// The value to test against.
764 value: u64,
765 /// Where to store the result of the comparison.
766 out: Output,
767 },
768 /// Test if the top of the stack is a specific boolean.
769 ///
770 /// # Operation
771 ///
772 /// ```text
773 /// <value>
774 /// => <boolean>
775 /// ```
776 #[cfg_attr(feature = "musli", musli(packed))]
777 EqBool {
778 /// Address of the value to compare.
779 addr: Address,
780 /// The value to test against.
781 value: bool,
782 /// Where to store the result of the comparison.
783 out: Output,
784 },
785 /// Compare the top of the stack against a static string slot.
786 ///
787 /// # Operation
788 ///
789 /// ```text
790 /// <value>
791 /// => <boolean>
792 /// ```
793 #[cfg_attr(feature = "musli", musli(packed))]
794 EqString {
795 /// Address of the value to compare.
796 addr: Address,
797 /// The slot to test against.
798 slot: usize,
799 /// Where to store the result of the comparison.
800 out: Output,
801 },
802 /// Compare the top of the stack against a static bytes slot.
803 ///
804 /// # Operation
805 ///
806 /// ```text
807 /// <value>
808 /// => <boolean>
809 /// ```
810 #[cfg_attr(feature = "musli", musli(packed))]
811 EqBytes {
812 /// Address of the value to compare.
813 addr: Address,
814 /// The slot to test against.
815 slot: usize,
816 /// Where to store the result of the comparison.
817 out: Output,
818 },
819 /// Test if the specified type matches.
820 ///
821 /// # Operation
822 ///
823 /// ```text
824 /// <value>
825 /// => <boolean>
826 /// ```
827 #[cfg_attr(feature = "musli", musli(packed))]
828 MatchType {
829 /// The type hash to match against.
830 hash: Hash,
831 /// The variant hash to match against.
832 variant_hash: Hash,
833 /// The address of the value to test.
834 addr: Address,
835 /// Where to store the output.
836 out: Output,
837 },
838 /// Test that the top of the stack is a tuple with the given length
839 /// requirements.
840 ///
841 /// # Operation
842 ///
843 /// ```text
844 /// <value>
845 /// => <boolean>
846 /// ```
847 #[cfg_attr(feature = "musli", musli(packed))]
848 MatchSequence {
849 /// Type constraints that the sequence must match.
850 hash: Hash,
851 /// The minimum length to test for.
852 len: usize,
853 /// Whether the operation should check exact `true` or minimum length
854 /// `false`.
855 exact: bool,
856 /// The address of the value to test.
857 addr: Address,
858 /// Where to store the output.
859 out: Output,
860 },
861 /// Test that the top of the stack is an object matching the given slot of
862 /// object keys.
863 ///
864 /// # Operation
865 ///
866 /// ```text
867 /// <object>
868 /// => <boolean>
869 /// ```
870 #[cfg_attr(feature = "musli", musli(packed))]
871 MatchObject {
872 /// The slot of object keys to use.
873 slot: usize,
874 /// Whether the operation should check exact `true` or minimum length
875 /// `false`.
876 exact: bool,
877 /// The address of the value to test.
878 addr: Address,
879 /// Where to store the output.
880 out: Output,
881 },
882 /// Perform a generator yield where the value yielded is expected to be
883 /// found at the top of the stack.
884 ///
885 /// This causes the virtual machine to suspend itself.
886 ///
887 /// # Operation
888 ///
889 /// ```text
890 /// <value>
891 /// => <value>
892 /// ```
893 Yield {
894 /// Address of the value being yielded.
895 addr: Address,
896 /// Where to store the produced resume value.
897 out: Output,
898 },
899 /// Perform a generator yield with a unit.
900 ///
901 /// This causes the virtual machine to suspend itself.
902 ///
903 /// # Operation
904 ///
905 /// ```text
906 /// => <unit>
907 /// ```
908 YieldUnit {
909 /// Where to store the produced resume value.
910 out: Output,
911 },
912 /// An operation.
913 #[cfg_attr(feature = "musli", musli(packed))]
914 Op {
915 /// The kind of operation.
916 op: InstOp,
917 /// The address of the first argument.
918 a: Address,
919 /// The address of the second argument.
920 b: Address,
921 /// Whether the produced value from the operation should be kept or not.
922 out: Output,
923 },
924 /// An arithmetic operation.
925 #[cfg_attr(feature = "musli", musli(packed))]
926 Arithmetic {
927 /// The kind of operation.
928 op: InstArithmeticOp,
929 /// The address of the first argument.
930 a: Address,
931 /// The address of the second argument.
932 b: Address,
933 /// Whether the produced value from the operation should be kept or not.
934 out: Output,
935 },
936 /// A bitwise operation.
937 #[cfg_attr(feature = "musli", musli(packed))]
938 Bitwise {
939 /// The kind of operation.
940 op: InstBitwiseOp,
941 /// The address of the first argument.
942 a: Address,
943 /// The address of the second argument.
944 b: Address,
945 /// Whether the produced value from the operation should be kept or not.
946 out: Output,
947 },
948 /// A shift operation.
949 #[cfg_attr(feature = "musli", musli(packed))]
950 Shift {
951 /// The kind of operation.
952 op: InstShiftOp,
953 /// The address of the first argument.
954 a: Address,
955 /// The address of the second argument.
956 b: Address,
957 /// Whether the produced value from the operation should be kept or not.
958 out: Output,
959 },
960 /// Instruction for assigned arithmetic operations.
961 #[cfg_attr(feature = "musli", musli(packed))]
962 AssignArithmetic {
963 /// The kind of operation.
964 op: InstArithmeticOp,
965 /// The target of the operation.
966 target: InstTarget,
967 /// The value being assigned.
968 rhs: Address,
969 },
970 /// Instruction for assigned bitwise operations.
971 #[cfg_attr(feature = "musli", musli(packed))]
972 AssignBitwise {
973 /// The kind of operation.
974 op: InstBitwiseOp,
975 /// The target of the operation.
976 target: InstTarget,
977 /// The value being assigned.
978 rhs: Address,
979 },
980 /// Instruction for assigned shift operations.
981 #[cfg_attr(feature = "musli", musli(packed))]
982 AssignShift {
983 /// The kind of operation.
984 op: InstShiftOp,
985 /// The target of the operation.
986 target: InstTarget,
987 /// The value being assigned.
988 rhs: Address,
989 },
990 /// Advance an iterator at the given position.
991 #[cfg_attr(feature = "musli", musli(packed))]
992 IterNext {
993 /// The address of the iterator to advance.
994 addr: Address,
995 /// A relative jump to perform if the iterator could not be advanced.
996 jump: usize,
997 /// Where to store the produced value from the iterator.
998 out: Output,
999 },
1000 /// Cause the VM to panic and error out without a reason.
1001 ///
1002 /// This should only be used during testing or extreme scenarios that are
1003 /// completely unrecoverable.
1004 #[cfg_attr(feature = "musli", musli(packed))]
1005 Panic {
1006 /// The reason for the panic.
1007 #[inst_display(display_with = PanicReason::ident)]
1008 reason: PanicReason,
1009 },
1010}
1011
1012impl Kind {
1013 /// Construct an instruction to push a unit.
1014 pub(crate) fn unit(out: Output) -> Self {
1015 Self::Store {
1016 value: InstValue::Unit,
1017 out,
1018 }
1019 }
1020
1021 /// Construct an instruction to push a boolean.
1022 pub(crate) fn bool(b: bool, out: Output) -> Self {
1023 Self::Store {
1024 value: InstValue::Bool(b),
1025 out,
1026 }
1027 }
1028
1029 /// Construct an instruction to push a character.
1030 pub(crate) fn char(c: char, out: Output) -> Self {
1031 Self::Store {
1032 value: InstValue::Char(c),
1033 out,
1034 }
1035 }
1036
1037 /// Construct an instruction to push an integer.
1038 pub(crate) fn signed(v: i64, out: Output) -> Self {
1039 Self::Store {
1040 value: InstValue::Integer(v),
1041 out,
1042 }
1043 }
1044
1045 /// Construct an instruction to push an unsigned integer.
1046 pub(crate) fn unsigned(v: u64, out: Output) -> Self {
1047 Self::Store {
1048 value: InstValue::Unsigned(v),
1049 out,
1050 }
1051 }
1052
1053 /// Construct an instruction to push a float.
1054 pub(crate) fn float(v: f64, out: Output) -> Self {
1055 Self::Store {
1056 value: InstValue::Float(v),
1057 out,
1058 }
1059 }
1060
1061 /// Construct an instruction to push a type.
1062 pub(crate) fn ty(ty: Type, out: Output) -> Self {
1063 Self::Store {
1064 value: InstValue::Type(ty),
1065 out,
1066 }
1067 }
1068
1069 /// Construct an instruction to push an ordering.
1070 pub(crate) fn ordering(ordering: Ordering, out: Output) -> Self {
1071 Self::Store {
1072 value: InstValue::Ordering(ordering),
1073 out,
1074 }
1075 }
1076
1077 /// Construct an instruction to push a type hash.
1078 pub(crate) fn hash(hash: Hash, out: Output) -> Self {
1079 Self::Store {
1080 value: InstValue::Hash(hash),
1081 out,
1082 }
1083 }
1084}
1085
1086/// What to do with the output of an instruction.
1087#[derive(TryClone, Clone, Copy, PartialEq, Eq, Hash)]
1088#[try_clone(copy)]
1089#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(transparent))]
1090#[cfg_attr(feature = "musli", derive(Decode, Encode), musli(transparent))]
1091pub struct Output {
1092 offset: usize,
1093}
1094
1095impl Output {
1096 /// Construct a keep output kind.
1097 #[inline]
1098 pub(crate) fn keep(offset: usize) -> Self {
1099 assert_ne!(offset, usize::MAX, "Address is invalid");
1100 Self { offset }
1101 }
1102
1103 /// Construct a discard output kind.
1104 #[inline]
1105 pub(crate) fn discard() -> Self {
1106 Self { offset: usize::MAX }
1107 }
1108
1109 /// Check if the output is a keep.
1110 #[inline(always)]
1111 pub(crate) fn as_addr(&self) -> Option<Address> {
1112 if self.offset == usize::MAX {
1113 None
1114 } else {
1115 Some(Address::new(self.offset))
1116 }
1117 }
1118}
1119
1120impl fmt::Display for Output {
1121 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1122 if self.offset == usize::MAX {
1123 write!(f, "discard")
1124 } else {
1125 write!(f, "keep({})", self.offset)
1126 }
1127 }
1128}
1129
1130impl fmt::Debug for Output {
1131 #[inline]
1132 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1133 fmt::Display::fmt(self, f)
1134 }
1135}
1136
1137/// How an instruction addresses a value.
1138#[derive(Default, TryClone, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
1139#[repr(transparent)]
1140#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(transparent))]
1141#[cfg_attr(feature = "musli", derive(Decode, Encode), musli(transparent))]
1142#[try_clone(copy)]
1143pub struct Address {
1144 offset: usize,
1145}
1146
1147impl Address {
1148 /// The first possible address.
1149 pub const ZERO: Address = Address { offset: 0 };
1150
1151 /// An invalid address.
1152 pub const INVALID: Address = Address { offset: usize::MAX };
1153
1154 /// Construct a new address.
1155 #[inline]
1156 pub(crate) const fn new(offset: usize) -> Self {
1157 Self { offset }
1158 }
1159
1160 /// Get the offset of the address.
1161 #[inline]
1162 pub(crate) fn offset(self) -> usize {
1163 self.offset
1164 }
1165
1166 /// Get the address as an output.
1167 #[inline]
1168 pub(crate) fn output(self) -> Output {
1169 Output::keep(self.offset)
1170 }
1171}
1172
1173impl fmt::Display for Address {
1174 #[inline]
1175 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1176 if self.offset == usize::MAX {
1177 write!(f, "invalid")
1178 } else {
1179 self.offset.fmt(f)
1180 }
1181 }
1182}
1183
1184impl fmt::Debug for Address {
1185 #[inline]
1186 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1187 fmt::Display::fmt(self, f)
1188 }
1189}
1190
1191/// Range limits of a range expression.
1192#[derive(Debug, TryClone, Clone, Copy)]
1193#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1194#[cfg_attr(feature = "musli", derive(Decode, Encode))]
1195#[try_clone(copy)]
1196pub(crate) enum InstRange {
1197 /// `start..`.
1198 RangeFrom {
1199 /// The start address of the range.
1200 start: Address,
1201 },
1202 /// `..`.
1203 RangeFull,
1204 /// `start..=end`.
1205 RangeInclusive {
1206 /// The start address of the range.
1207 start: Address,
1208 /// The end address of the range.
1209 end: Address,
1210 },
1211 /// `..=end`.
1212 RangeToInclusive {
1213 /// The end address of the range.
1214 end: Address,
1215 },
1216 /// `..end`.
1217 RangeTo {
1218 /// The end address of the range.
1219 end: Address,
1220 },
1221 /// `start..end`.
1222 Range {
1223 /// The start address of the range.
1224 start: Address,
1225 /// The end address of the range.
1226 end: Address,
1227 },
1228}
1229
1230impl fmt::Display for InstRange {
1231 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1232 match self {
1233 InstRange::RangeFrom { start } => write!(f, "{start}.."),
1234 InstRange::RangeFull => write!(f, ".."),
1235 InstRange::RangeInclusive { start, end } => write!(f, "{start}..={end}"),
1236 InstRange::RangeToInclusive { end } => write!(f, "..={end}"),
1237 InstRange::RangeTo { end } => write!(f, "..{end}"),
1238 InstRange::Range { start, end } => write!(f, "{start}..{end}"),
1239 }
1240 }
1241}
1242
1243/// The target of an operation.
1244#[derive(Debug, TryClone, Clone, Copy)]
1245#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1246#[cfg_attr(feature = "musli", derive(Decode, Encode))]
1247#[try_clone(copy)]
1248pub(crate) enum InstTarget {
1249 /// Target is an offset to the current call frame.
1250 #[cfg_attr(feature = "musli", musli(packed))]
1251 Address(Address),
1252 /// Target the field of an object.
1253 #[cfg_attr(feature = "musli", musli(packed))]
1254 Field(Address, usize),
1255 /// Target a tuple field.
1256 #[cfg_attr(feature = "musli", musli(packed))]
1257 TupleField(Address, usize),
1258}
1259
1260impl fmt::Display for InstTarget {
1261 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1262 match self {
1263 Self::Address(addr) => write!(f, "address({addr})"),
1264 Self::Field(addr, slot) => write!(f, "field({addr}, {slot})"),
1265 Self::TupleField(addr, slot) => write!(f, "tuple-field({addr}, {slot})"),
1266 }
1267 }
1268}
1269
1270/// An operation between two values on the machine.
1271#[derive(Debug, TryClone, Clone, Copy)]
1272#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1273#[cfg_attr(feature = "musli", derive(Decode, Encode))]
1274#[try_clone(copy)]
1275pub(crate) enum InstArithmeticOp {
1276 /// The add operation. `a + b`.
1277 Add,
1278 /// The sub operation. `a - b`.
1279 Sub,
1280 /// The multiply operation. `a * b`.
1281 Mul,
1282 /// The division operation. `a / b`.
1283 Div,
1284 /// The remainder operation. `a % b`.
1285 Rem,
1286}
1287
1288impl fmt::Display for InstArithmeticOp {
1289 #[inline]
1290 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1291 match self {
1292 Self::Add => {
1293 write!(f, "+")?;
1294 }
1295 Self::Sub => {
1296 write!(f, "-")?;
1297 }
1298 Self::Mul => {
1299 write!(f, "*")?;
1300 }
1301 Self::Div => {
1302 write!(f, "/")?;
1303 }
1304 Self::Rem => {
1305 write!(f, "%")?;
1306 }
1307 }
1308
1309 Ok(())
1310 }
1311}
1312
1313/// An operation between two values on the machine.
1314#[derive(Debug, TryClone, Clone, Copy)]
1315#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1316#[cfg_attr(feature = "musli", derive(Decode, Encode))]
1317#[try_clone(copy)]
1318pub(crate) enum InstBitwiseOp {
1319 /// The bitwise and operation. `a & b`.
1320 BitAnd,
1321 /// The bitwise xor operation. `a ^ b`.
1322 BitXor,
1323 /// The bitwise or operation. `a | b`.
1324 BitOr,
1325}
1326
1327impl fmt::Display for InstBitwiseOp {
1328 #[inline]
1329 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1330 match self {
1331 Self::BitAnd => {
1332 write!(f, "&")?;
1333 }
1334 Self::BitXor => {
1335 write!(f, "^")?;
1336 }
1337 Self::BitOr => {
1338 write!(f, "|")?;
1339 }
1340 }
1341
1342 Ok(())
1343 }
1344}
1345
1346/// An operation between two values on the machine.
1347#[derive(Debug, TryClone, Clone, Copy)]
1348#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1349#[cfg_attr(feature = "musli", derive(Decode, Encode))]
1350#[try_clone(copy)]
1351pub(crate) enum InstShiftOp {
1352 /// The shift left operation. `a << b`.
1353 Shl,
1354 /// The shift right operation. `a << b`.
1355 Shr,
1356}
1357
1358impl fmt::Display for InstShiftOp {
1359 #[inline]
1360 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1361 match self {
1362 Self::Shl => {
1363 write!(f, "<<")?;
1364 }
1365 Self::Shr => {
1366 write!(f, ">>")?;
1367 }
1368 }
1369
1370 Ok(())
1371 }
1372}
1373
1374/// An operation between two values on the machine.
1375#[derive(Debug, TryClone, Clone, Copy)]
1376#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1377#[cfg_attr(feature = "musli", derive(Decode, Encode))]
1378#[try_clone(copy)]
1379pub(crate) enum InstOp {
1380 /// Compare two values on the stack for lt and push the result as a
1381 /// boolean on the stack.
1382 Lt,
1383 /// Compare two values on the stack for lte and push the result as a
1384 /// boolean on the stack.
1385 Le,
1386 /// Compare two values on the stack for gt and push the result as a
1387 /// boolean on the stack.
1388 Gt,
1389 /// Compare two values on the stack for gte and push the result as a
1390 /// boolean on the stack.
1391 Ge,
1392 /// Compare two values on the stack for equality and push the result as a
1393 /// boolean on the stack.
1394 ///
1395 /// # Operation
1396 ///
1397 /// ```text
1398 /// <b>
1399 /// <a>
1400 /// => <bool>
1401 /// ```
1402 Eq,
1403 /// Compare two values on the stack for inequality and push the result as a
1404 /// boolean on the stack.
1405 ///
1406 /// # Operation
1407 ///
1408 /// ```text
1409 /// <b>
1410 /// <a>
1411 /// => <bool>
1412 /// ```
1413 Neq,
1414 /// Coerce a value into the given type.
1415 ///
1416 /// # Operation
1417 ///
1418 /// ```text
1419 /// <type>
1420 /// <value>
1421 /// => <boolean>
1422 /// ```
1423 As,
1424 /// Test if the top of the stack is an instance of the second item on the
1425 /// stack.
1426 ///
1427 /// # Operation
1428 ///
1429 /// ```text
1430 /// <type>
1431 /// <value>
1432 /// => <boolean>
1433 /// ```
1434 Is,
1435 /// Test if the top of the stack is not an instance of the second item on
1436 /// the stack.
1437 ///
1438 /// # Operation
1439 ///
1440 /// ```text
1441 /// <type>
1442 /// <value>
1443 /// => <boolean>
1444 /// ```
1445 IsNot,
1446 /// Pop two values from the stack and test if they are both boolean true.
1447 ///
1448 /// # Operation
1449 ///
1450 /// ```text
1451 /// <boolean>
1452 /// <boolean>
1453 /// => <boolean>
1454 /// ```
1455 And,
1456 /// Pop two values from the stack and test if either of them are boolean
1457 /// true.
1458 ///
1459 /// # Operation
1460 ///
1461 /// ```text
1462 /// <boolean>
1463 /// <boolean>
1464 /// => <boolean>
1465 /// ```
1466 Or,
1467}
1468
1469impl fmt::Display for InstOp {
1470 #[inline]
1471 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1472 match self {
1473 Self::Lt => {
1474 write!(f, "<")?;
1475 }
1476 Self::Gt => {
1477 write!(f, ">")?;
1478 }
1479 Self::Le => {
1480 write!(f, "<=")?;
1481 }
1482 Self::Ge => {
1483 write!(f, ">=")?;
1484 }
1485 Self::Eq => {
1486 write!(f, "==")?;
1487 }
1488 Self::Neq => {
1489 write!(f, "!=")?;
1490 }
1491 Self::As => {
1492 write!(f, "as")?;
1493 }
1494 Self::Is => {
1495 write!(f, "is")?;
1496 }
1497 Self::IsNot => {
1498 write!(f, "is not")?;
1499 }
1500 Self::And => {
1501 write!(f, "&&")?;
1502 }
1503 Self::Or => {
1504 write!(f, "||")?;
1505 }
1506 }
1507
1508 Ok(())
1509 }
1510}
1511
1512/// A literal value that can be pushed.
1513#[derive(Debug, TryClone, Clone, Copy)]
1514#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1515#[cfg_attr(feature = "musli", derive(Decode, Encode))]
1516#[try_clone(copy)]
1517pub(crate) enum InstValue {
1518 /// An empty tuple.
1519 Unit,
1520 /// A boolean.
1521 #[cfg_attr(feature = "musli", musli(packed))]
1522 Bool(bool),
1523 /// A character.
1524 #[cfg_attr(feature = "musli", musli(packed))]
1525 Char(char),
1526 /// An unsigned integer.
1527 #[cfg_attr(feature = "musli", musli(packed))]
1528 Unsigned(u64),
1529 /// An integer.
1530 #[cfg_attr(feature = "musli", musli(packed))]
1531 Integer(i64),
1532 /// A float.
1533 #[cfg_attr(feature = "musli", musli(packed))]
1534 Float(f64),
1535 /// A type hash.
1536 #[cfg_attr(feature = "musli", musli(packed))]
1537 Type(Type),
1538 /// An ordering.
1539 Ordering(
1540 #[cfg_attr(feature = "musli", musli(with = crate::musli::ordering))]
1541 #[cfg_attr(feature = "serde", serde(with = "crate::serde::ordering"))]
1542 Ordering,
1543 ),
1544 /// A hash.
1545 #[cfg_attr(feature = "musli", musli(packed))]
1546 Hash(Hash),
1547}
1548
1549impl InstValue {
1550 /// Convert into a value that can be pushed onto the stack.
1551 pub(crate) fn into_value(self) -> Value {
1552 match self {
1553 Self::Unit => Value::unit(),
1554 Self::Bool(v) => Value::from(v),
1555 Self::Char(v) => Value::from(v),
1556 Self::Unsigned(v) => Value::from(v),
1557 Self::Integer(v) => Value::from(v),
1558 Self::Float(v) => Value::from(v),
1559 Self::Type(v) => Value::from(v),
1560 Self::Ordering(v) => Value::from(v),
1561 Self::Hash(v) => Value::from(v),
1562 }
1563 }
1564}
1565
1566impl fmt::Display for InstValue {
1567 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1568 match self {
1569 Self::Unit => write!(f, "()")?,
1570 Self::Bool(v) => write!(f, "{v}")?,
1571 Self::Char(v) => write!(f, "{v:?}")?,
1572 Self::Unsigned(v) => write!(f, "{v}u64")?,
1573 Self::Integer(v) => write!(f, "{v}i64")?,
1574 Self::Float(v) => write!(f, "{v}")?,
1575 Self::Type(v) => write!(f, "{}", v.into_hash())?,
1576 Self::Ordering(v) => write!(f, "{v:?}")?,
1577 Self::Hash(v) => write!(f, "{v:?}")?,
1578 }
1579
1580 Ok(())
1581 }
1582}
1583
1584#[repr(transparent)]
1585struct DisplayArray<T>(T)
1586where
1587 T: ?Sized;
1588
1589impl<T> DisplayArray<[T]> {
1590 #[inline]
1591 fn new(value: &[T]) -> &Self {
1592 // SAFETY: The `DisplayArray` struct is a transparent wrapper around the
1593 // value.
1594 unsafe { &*(value as *const [T] as *const Self) }
1595 }
1596}
1597
1598impl<T> fmt::Display for DisplayArray<[T]>
1599where
1600 T: fmt::Display,
1601{
1602 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1603 let mut it = self.0.iter();
1604
1605 write!(f, "[")?;
1606 let last = it.next_back();
1607
1608 for value in it {
1609 write!(f, "{value}, ")?;
1610 }
1611
1612 if let Some(last) = last {
1613 last.fmt(f)?;
1614 }
1615
1616 write!(f, "]")?;
1617 Ok(())
1618 }
1619}
1620
1621#[repr(transparent)]
1622struct DisplayDebug<T>(T)
1623where
1624 T: ?Sized;
1625
1626impl<T> DisplayDebug<T>
1627where
1628 T: ?Sized,
1629{
1630 #[inline]
1631 fn new(value: &T) -> &Self {
1632 // SAFETY: The `DisplayDebug` struct is a transparent wrapper around the
1633 // value.
1634 unsafe { &*(value as *const T as *const Self) }
1635 }
1636}
1637
1638impl<T> fmt::Display for DisplayDebug<T>
1639where
1640 T: ?Sized + fmt::Debug,
1641{
1642 #[inline]
1643 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1644 fmt::Debug::fmt(&self.0, f)
1645 }
1646}