1use serde::{Deserialize, Serialize};
12
13#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
15pub struct StructLayoutId(pub u32);
16
17#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
19pub struct EnumLayoutId(pub u32);
20
21#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
40pub struct NamedTypeId<Id> {
41 pub layout: Id,
44 pub name: Option<std::sync::Arc<str>>,
46}
47
48impl<Id> NamedTypeId<Id> {
49 pub fn named(layout: Id, name: impl Into<std::sync::Arc<str>>) -> Self {
51 NamedTypeId { layout, name: Some(name.into()) }
52 }
53
54 pub fn placeholder(layout: Id) -> Self {
56 NamedTypeId { layout, name: None }
57 }
58
59 pub fn name_str(&self) -> Option<&str> {
61 self.name.as_deref()
62 }
63}
64
65#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
67pub struct ClosureTypeId(pub u32);
68
69#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
71pub struct FunctionTypeId(pub u32);
72
73#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
81pub enum ConcreteType {
82 F64,
84 F32,
89 Char,
94 I64,
96 I32,
98 I16,
100 I8,
102 U64,
104 U32,
106 U16,
108 U8,
110 Bool,
112 String,
114 Struct(NamedTypeId<StructLayoutId>),
117 Array(Box<ConcreteType>),
120 HashMap(Box<ConcreteType>, Box<ConcreteType>),
122 Option(Box<ConcreteType>),
124 Result(Box<ConcreteType>, Box<ConcreteType>),
126 Enum(NamedTypeId<EnumLayoutId>),
129 Closure(ClosureTypeId),
131 Function(FunctionTypeId),
133 Pointer(Box<ConcreteType>),
135 Tuple(Vec<ConcreteType>),
137 Void,
139 Decimal,
141 BigInt,
143 DateTime,
145 HashSet(Box<ConcreteType>),
172 Deque(Box<ConcreteType>),
179 PriorityQueue,
184 Channel(Box<ConcreteType>),
190 Mutex(Box<ConcreteType>),
197 Atomic,
203 Lazy(Box<ConcreteType>),
208}
209
210impl ConcreteType {
211 pub fn named_struct(name: impl Into<std::sync::Arc<str>>, layout: StructLayoutId) -> Self {
216 ConcreteType::Struct(NamedTypeId::named(layout, name))
217 }
218
219 pub fn named_enum(name: impl Into<std::sync::Arc<str>>, layout: EnumLayoutId) -> Self {
221 ConcreteType::Enum(NamedTypeId::named(layout, name))
222 }
223
224 pub fn placeholder_struct(layout: StructLayoutId) -> Self {
227 ConcreteType::Struct(NamedTypeId::placeholder(layout))
228 }
229
230 pub fn placeholder_enum(layout: EnumLayoutId) -> Self {
233 ConcreteType::Enum(NamedTypeId::placeholder(layout))
234 }
235
236 #[inline]
238 pub fn stack_size(&self) -> usize {
239 8 }
241
242 #[inline]
244 pub fn alignment(&self) -> usize {
245 match self {
246 ConcreteType::I8 | ConcreteType::U8 | ConcreteType::Bool => 1,
247 ConcreteType::I16 | ConcreteType::U16 => 2,
248 ConcreteType::I32
251 | ConcreteType::U32
252 | ConcreteType::F32
253 | ConcreteType::Char => 4,
254 _ => 8, }
256 }
257
258 #[inline]
260 pub fn field_size(&self) -> usize {
261 match self {
262 ConcreteType::I8 | ConcreteType::U8 | ConcreteType::Bool => 1,
263 ConcreteType::I16 | ConcreteType::U16 => 2,
264 ConcreteType::I32
267 | ConcreteType::U32
268 | ConcreteType::F32
269 | ConcreteType::Char => 4,
270 _ => 8,
271 }
272 }
273
274 #[inline]
276 pub fn is_numeric(&self) -> bool {
277 matches!(
278 self,
279 ConcreteType::F64
280 | ConcreteType::F32
281 | ConcreteType::I64
282 | ConcreteType::I32
283 | ConcreteType::I16
284 | ConcreteType::I8
285 | ConcreteType::U64
286 | ConcreteType::U32
287 | ConcreteType::U16
288 | ConcreteType::U8
289 | ConcreteType::Decimal
290 | ConcreteType::BigInt
291 )
292 }
293
294 #[inline]
296 pub fn is_integer(&self) -> bool {
297 matches!(
298 self,
299 ConcreteType::I64
300 | ConcreteType::I32
301 | ConcreteType::I16
302 | ConcreteType::I8
303 | ConcreteType::U64
304 | ConcreteType::U32
305 | ConcreteType::U16
306 | ConcreteType::U8
307 )
308 }
309
310 #[inline]
312 pub fn is_heap(&self) -> bool {
313 matches!(
314 self,
315 ConcreteType::String
316 | ConcreteType::Struct(_)
317 | ConcreteType::Array(_)
318 | ConcreteType::HashMap(_, _)
319 | ConcreteType::Enum(_)
320 | ConcreteType::Closure(_)
321 | ConcreteType::Pointer(_)
322 | ConcreteType::BigInt
323 | ConcreteType::Decimal
324 | ConcreteType::DateTime
325 | ConcreteType::HashSet(_)
331 | ConcreteType::Deque(_)
332 | ConcreteType::PriorityQueue
333 | ConcreteType::Channel(_)
334 | ConcreteType::Mutex(_)
335 | ConcreteType::Atomic
336 | ConcreteType::Lazy(_)
337 )
338 }
339
340 #[inline]
342 pub fn is_scalar(&self) -> bool {
343 matches!(
344 self,
345 ConcreteType::F64
346 | ConcreteType::F32
347 | ConcreteType::I64
348 | ConcreteType::I32
349 | ConcreteType::I16
350 | ConcreteType::I8
351 | ConcreteType::U64
352 | ConcreteType::U32
353 | ConcreteType::U16
354 | ConcreteType::U8
355 | ConcreteType::Bool
356 | ConcreteType::Char
357 )
358 }
359
360 pub fn to_field_kind(&self) -> super::struct_layout::FieldKind {
362 use super::struct_layout::FieldKind;
363 match self {
364 ConcreteType::F64 => FieldKind::F64,
365 ConcreteType::I64 => FieldKind::I64,
366 ConcreteType::I32 => FieldKind::I32,
367 ConcreteType::I16 => FieldKind::I16,
368 ConcreteType::I8 => FieldKind::I8,
369 ConcreteType::U64 => FieldKind::U64,
370 ConcreteType::U32 => FieldKind::U32,
371 ConcreteType::U16 => FieldKind::U16,
372 ConcreteType::U8 => FieldKind::U8,
373 ConcreteType::Bool => FieldKind::Bool,
374 ConcreteType::F32 | ConcreteType::Char => FieldKind::U32,
385 _ => FieldKind::Ptr,
387 }
388 }
389
390 pub fn mono_key(&self) -> String {
393 match self {
394 ConcreteType::F64 => "f64".into(),
395 ConcreteType::F32 => "f32".into(),
396 ConcreteType::Char => "char".into(),
397 ConcreteType::I64 => "i64".into(),
398 ConcreteType::I32 => "i32".into(),
399 ConcreteType::I16 => "i16".into(),
400 ConcreteType::I8 => "i8".into(),
401 ConcreteType::U64 => "u64".into(),
402 ConcreteType::U32 => "u32".into(),
403 ConcreteType::U16 => "u16".into(),
404 ConcreteType::U8 => "u8".into(),
405 ConcreteType::Bool => "bool".into(),
406 ConcreteType::String => "string".into(),
407 ConcreteType::Struct(id) => match id.name_str() {
413 Some(name) => format!("struct_{}", name),
414 None => format!("struct_{}", id.layout.0),
415 },
416 ConcreteType::Array(elem) => format!("array_{}", elem.mono_key()),
417 ConcreteType::HashMap(k, v) => {
418 format!("hashmap_{}_{}", k.mono_key(), v.mono_key())
419 }
420 ConcreteType::Option(inner) => format!("option_{}", inner.mono_key()),
421 ConcreteType::Result(ok, err) => {
422 format!("result_{}_{}", ok.mono_key(), err.mono_key())
423 }
424 ConcreteType::Enum(id) => match id.name_str() {
427 Some(name) => format!("enum_{}", name),
428 None => format!("enum_{}", id.layout.0),
429 },
430 ConcreteType::Closure(id) => format!("closure_{}", id.0),
431 ConcreteType::Function(id) => format!("fn_{}", id.0),
432 ConcreteType::Pointer(inner) => format!("ptr_{}", inner.mono_key()),
433 ConcreteType::Tuple(elems) => {
434 let parts: Vec<_> = elems.iter().map(|e| e.mono_key()).collect();
435 format!("tuple_{}", parts.join("_"))
436 }
437 ConcreteType::Void => "void".into(),
438 ConcreteType::Decimal => "decimal".into(),
439 ConcreteType::BigInt => "bigint".into(),
440 ConcreteType::DateTime => "datetime".into(),
441 ConcreteType::HashSet(elem) => format!("hashset_{}", elem.mono_key()),
443 ConcreteType::Deque(elem) => format!("deque_{}", elem.mono_key()),
444 ConcreteType::PriorityQueue => "priority_queue".into(),
445 ConcreteType::Channel(elem) => format!("channel_{}", elem.mono_key()),
446 ConcreteType::Mutex(inner) => format!("mutex_{}", inner.mono_key()),
447 ConcreteType::Atomic => "atomic".into(),
448 ConcreteType::Lazy(inner) => format!("lazy_{}", inner.mono_key()),
449 }
450 }
451
452 pub fn type_tag(&self) -> u8 {
454 match self {
455 ConcreteType::F64 => 0,
456 ConcreteType::I64 => 1,
457 ConcreteType::I32 => 2,
458 ConcreteType::I16 => 3,
459 ConcreteType::I8 => 4,
460 ConcreteType::U64 => 5,
461 ConcreteType::U32 => 6,
462 ConcreteType::U16 => 7,
463 ConcreteType::U8 => 8,
464 ConcreteType::Bool => 9,
465 ConcreteType::String => 10,
466 ConcreteType::Struct(_) => 11,
467 ConcreteType::Array(_) => 12,
468 ConcreteType::HashMap(_, _) => 13,
469 ConcreteType::Option(_) => 14,
470 ConcreteType::Result(_, _) => 15,
471 ConcreteType::Enum(_) => 16,
472 ConcreteType::Closure(_) => 17,
473 ConcreteType::Function(_) => 18,
474 ConcreteType::Pointer(_) => 19,
475 ConcreteType::Tuple(_) => 20,
476 ConcreteType::Void => 21,
477 ConcreteType::Decimal => 22,
478 ConcreteType::BigInt => 23,
479 ConcreteType::DateTime => 24,
480 ConcreteType::HashSet(_) => 25,
482 ConcreteType::Deque(_) => 26,
483 ConcreteType::PriorityQueue => 27,
484 ConcreteType::Channel(_) => 28,
485 ConcreteType::Mutex(_) => 29,
486 ConcreteType::Atomic => 30,
487 ConcreteType::Lazy(_) => 31,
488 ConcreteType::F32 => 32,
493 ConcreteType::Char => 33,
494 }
495 }
496}
497
498impl From<super::struct_layout::FieldKind> for ConcreteType {
500 fn from(fk: super::struct_layout::FieldKind) -> Self {
501 use super::struct_layout::FieldKind;
502 match fk {
503 FieldKind::F64 => ConcreteType::F64,
504 FieldKind::I64 => ConcreteType::I64,
505 FieldKind::I32 => ConcreteType::I32,
506 FieldKind::I16 => ConcreteType::I16,
507 FieldKind::I8 => ConcreteType::I8,
508 FieldKind::U64 => ConcreteType::U64,
509 FieldKind::U32 => ConcreteType::U32,
510 FieldKind::U16 => ConcreteType::U16,
511 FieldKind::U8 => ConcreteType::U8,
512 FieldKind::Bool => ConcreteType::Bool,
513 FieldKind::Ptr => ConcreteType::Pointer(Box::new(ConcreteType::Void)),
515 }
516 }
517}
518
519impl std::fmt::Display for ConcreteType {
520 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
521 match self {
522 ConcreteType::F64 => write!(f, "number"),
523 ConcreteType::F32 => write!(f, "f32"),
524 ConcreteType::Char => write!(f, "char"),
525 ConcreteType::I64 => write!(f, "int"),
526 ConcreteType::I32 => write!(f, "i32"),
527 ConcreteType::I16 => write!(f, "i16"),
528 ConcreteType::I8 => write!(f, "i8"),
529 ConcreteType::U64 => write!(f, "u64"),
530 ConcreteType::U32 => write!(f, "u32"),
531 ConcreteType::U16 => write!(f, "u16"),
532 ConcreteType::U8 => write!(f, "u8"),
533 ConcreteType::Bool => write!(f, "bool"),
534 ConcreteType::String => write!(f, "string"),
535 ConcreteType::Struct(id) => match id.name_str() {
536 Some(name) => write!(f, "{}", name),
537 None => write!(f, "Struct#{}", id.layout.0),
538 },
539 ConcreteType::Array(elem) => write!(f, "Array<{elem}>"),
540 ConcreteType::HashMap(k, v) => write!(f, "HashMap<{k}, {v}>"),
541 ConcreteType::Option(inner) => write!(f, "{inner}?"),
542 ConcreteType::Result(ok, err) => write!(f, "Result<{ok}, {err}>"),
543 ConcreteType::Enum(id) => match id.name_str() {
544 Some(name) => write!(f, "{}", name),
545 None => write!(f, "Enum#{}", id.layout.0),
546 },
547 ConcreteType::Closure(id) => write!(f, "Closure#{}", id.0),
548 ConcreteType::Function(id) => write!(f, "Function#{}", id.0),
549 ConcreteType::Pointer(inner) => write!(f, "ptr<{inner}>"),
550 ConcreteType::Tuple(elems) => {
551 write!(f, "(")?;
552 for (i, e) in elems.iter().enumerate() {
553 if i > 0 {
554 write!(f, ", ")?;
555 }
556 write!(f, "{e}")?;
557 }
558 write!(f, ")")
559 }
560 ConcreteType::Void => write!(f, "void"),
561 ConcreteType::Decimal => write!(f, "decimal"),
562 ConcreteType::BigInt => write!(f, "bigint"),
563 ConcreteType::DateTime => write!(f, "DateTime"),
564 ConcreteType::HashSet(elem) => write!(f, "HashSet<{elem}>"),
566 ConcreteType::Deque(elem) => write!(f, "Deque<{elem}>"),
567 ConcreteType::PriorityQueue => write!(f, "PriorityQueue"),
568 ConcreteType::Channel(elem) => write!(f, "Channel<{elem}>"),
569 ConcreteType::Mutex(inner) => write!(f, "Mutex<{inner}>"),
570 ConcreteType::Atomic => write!(f, "Atomic"),
571 ConcreteType::Lazy(inner) => write!(f, "Lazy<{inner}>"),
572 }
573 }
574}
575
576#[cfg(test)]
577mod tests {
578 use super::*;
579
580 #[test]
581 fn test_mono_key_primitives() {
582 assert_eq!(ConcreteType::F64.mono_key(), "f64");
583 assert_eq!(ConcreteType::I64.mono_key(), "i64");
584 assert_eq!(ConcreteType::Bool.mono_key(), "bool");
585 assert_eq!(ConcreteType::String.mono_key(), "string");
586 }
587
588 #[test]
589 fn test_mono_key_composites() {
590 let arr_f64 = ConcreteType::Array(Box::new(ConcreteType::F64));
591 assert_eq!(arr_f64.mono_key(), "array_f64");
592
593 let map = ConcreteType::HashMap(
594 Box::new(ConcreteType::String),
595 Box::new(ConcreteType::I64),
596 );
597 assert_eq!(map.mono_key(), "hashmap_string_i64");
598
599 let nested = ConcreteType::Array(Box::new(ConcreteType::Array(Box::new(
600 ConcreteType::I32,
601 ))));
602 assert_eq!(nested.mono_key(), "array_array_i32");
603 }
604
605 #[test]
606 fn ws6_named_struct_mono_key_uses_name() {
607 let p = ConcreteType::named_struct("P", StructLayoutId(0));
611 let q = ConcreteType::named_struct("Q", StructLayoutId(0));
612 assert_eq!(p.mono_key(), "struct_P");
613 assert_eq!(q.mono_key(), "struct_Q");
614 assert_ne!(
615 p.mono_key(),
616 q.mono_key(),
617 "distinct structs must produce distinct mono keys"
618 );
619 assert_ne!(p, q, "named structs with distinct names are not equal");
620 }
621
622 #[test]
623 fn ws6_named_enum_mono_key_uses_name() {
624 let c = ConcreteType::named_enum("Color", EnumLayoutId(0));
625 assert_eq!(c.mono_key(), "enum_Color");
626 }
627
628 #[test]
629 fn ws6_placeholder_struct_falls_back_to_layout_id() {
630 let s = ConcreteType::placeholder_struct(StructLayoutId(0));
633 assert_eq!(s.mono_key(), "struct_0");
634 assert!(matches!(s, ConcreteType::Struct(ref id) if id.name_str().is_none()));
635 }
636
637 #[test]
638 fn test_type_tags_unique() {
639 let types = vec![
640 ConcreteType::F64,
641 ConcreteType::I64,
642 ConcreteType::I32,
643 ConcreteType::I16,
644 ConcreteType::I8,
645 ConcreteType::U64,
646 ConcreteType::U32,
647 ConcreteType::U16,
648 ConcreteType::U8,
649 ConcreteType::Bool,
650 ConcreteType::String,
651 ConcreteType::placeholder_struct(StructLayoutId(0)),
652 ConcreteType::Array(Box::new(ConcreteType::F64)),
653 ConcreteType::HashMap(Box::new(ConcreteType::String), Box::new(ConcreteType::F64)),
654 ConcreteType::Option(Box::new(ConcreteType::I64)),
655 ConcreteType::Result(Box::new(ConcreteType::I64), Box::new(ConcreteType::String)),
656 ConcreteType::placeholder_enum(EnumLayoutId(0)),
657 ConcreteType::Closure(ClosureTypeId(0)),
658 ConcreteType::Function(FunctionTypeId(0)),
659 ConcreteType::Pointer(Box::new(ConcreteType::U8)),
660 ConcreteType::Tuple(vec![ConcreteType::I64, ConcreteType::F64]),
661 ConcreteType::Void,
662 ConcreteType::Decimal,
663 ConcreteType::BigInt,
664 ConcreteType::DateTime,
665 ConcreteType::HashSet(Box::new(ConcreteType::String)),
667 ConcreteType::Deque(Box::new(ConcreteType::I64)),
668 ConcreteType::PriorityQueue,
669 ConcreteType::Channel(Box::new(ConcreteType::I64)),
670 ConcreteType::Mutex(Box::new(ConcreteType::I64)),
671 ConcreteType::Atomic,
672 ConcreteType::Lazy(Box::new(ConcreteType::I64)),
673 ConcreteType::F32,
675 ConcreteType::Char,
676 ];
677 let tags: Vec<u8> = types.iter().map(|t| t.type_tag()).collect();
678 let unique: std::collections::HashSet<u8> = tags.iter().copied().collect();
679 assert_eq!(tags.len(), unique.len(), "type tags must be unique");
680 }
681
682 #[test]
685 fn test_round_19_f32_char_scalars() {
686 assert_eq!(ConcreteType::F32.mono_key(), "f32");
687 assert_eq!(ConcreteType::Char.mono_key(), "char");
688 assert_eq!(format!("{}", ConcreteType::F32), "f32");
689 assert_eq!(format!("{}", ConcreteType::Char), "char");
690 assert_eq!(ConcreteType::F32.alignment(), 4);
692 assert_eq!(ConcreteType::F32.field_size(), 4);
693 assert_eq!(ConcreteType::Char.alignment(), 4);
694 assert_eq!(ConcreteType::Char.field_size(), 4);
695 assert!(ConcreteType::F32.is_scalar());
698 assert!(ConcreteType::Char.is_scalar());
699 assert!(ConcreteType::F32.is_numeric());
700 assert!(!ConcreteType::Char.is_numeric());
701 assert!(!ConcreteType::F32.is_integer());
702 assert!(!ConcreteType::Char.is_integer());
703 assert!(!ConcreteType::F32.is_heap());
705 assert!(!ConcreteType::Char.is_heap());
706 }
707
708 #[test]
709 fn test_round_11_collection_concurrency_arms_mono_key_and_display() {
710 let hs = ConcreteType::HashSet(Box::new(ConcreteType::String));
713 assert_eq!(hs.mono_key(), "hashset_string");
714 assert_eq!(format!("{hs}"), "HashSet<string>");
715
716 let dq = ConcreteType::Deque(Box::new(ConcreteType::I64));
717 assert_eq!(dq.mono_key(), "deque_i64");
718 assert_eq!(format!("{dq}"), "Deque<int>");
719
720 let pq = ConcreteType::PriorityQueue;
721 assert_eq!(pq.mono_key(), "priority_queue");
722 assert_eq!(format!("{pq}"), "PriorityQueue");
723
724 let ch = ConcreteType::Channel(Box::new(ConcreteType::I64));
725 assert_eq!(ch.mono_key(), "channel_i64");
726 assert_eq!(format!("{ch}"), "Channel<int>");
727
728 let mx = ConcreteType::Mutex(Box::new(ConcreteType::I64));
729 assert_eq!(mx.mono_key(), "mutex_i64");
730 assert_eq!(format!("{mx}"), "Mutex<int>");
731
732 let at = ConcreteType::Atomic;
733 assert_eq!(at.mono_key(), "atomic");
734 assert_eq!(format!("{at}"), "Atomic");
735
736 let lz = ConcreteType::Lazy(Box::new(ConcreteType::Bool));
737 assert_eq!(lz.mono_key(), "lazy_bool");
738 assert_eq!(format!("{lz}"), "Lazy<bool>");
739 }
740
741 #[test]
742 fn test_round_11_arms_are_heap() {
743 assert!(ConcreteType::HashSet(Box::new(ConcreteType::String)).is_heap());
746 assert!(ConcreteType::Deque(Box::new(ConcreteType::I64)).is_heap());
747 assert!(ConcreteType::PriorityQueue.is_heap());
748 assert!(ConcreteType::Channel(Box::new(ConcreteType::I64)).is_heap());
749 assert!(ConcreteType::Mutex(Box::new(ConcreteType::I64)).is_heap());
750 assert!(ConcreteType::Atomic.is_heap());
751 assert!(ConcreteType::Lazy(Box::new(ConcreteType::I64)).is_heap());
752
753 assert!(!ConcreteType::HashSet(Box::new(ConcreteType::String)).is_scalar());
755 assert!(!ConcreteType::Mutex(Box::new(ConcreteType::I64)).is_numeric());
756 assert!(!ConcreteType::Atomic.is_integer());
757 }
758
759 #[test]
760 fn test_field_kind_roundtrip() {
761 use super::super::struct_layout::FieldKind;
762 let kinds = [
763 FieldKind::F64,
764 FieldKind::I64,
765 FieldKind::I32,
766 FieldKind::I16,
767 FieldKind::I8,
768 FieldKind::U64,
769 FieldKind::U32,
770 FieldKind::U16,
771 FieldKind::U8,
772 FieldKind::Bool,
773 ];
774 for kind in kinds {
775 let ct = ConcreteType::from(kind);
776 let back = ct.to_field_kind();
777 assert_eq!(kind, back);
778 }
779 }
780
781 #[test]
782 fn test_is_numeric() {
783 assert!(ConcreteType::F64.is_numeric());
784 assert!(ConcreteType::I64.is_numeric());
785 assert!(ConcreteType::U8.is_numeric());
786 assert!(ConcreteType::Decimal.is_numeric());
787 assert!(!ConcreteType::Bool.is_numeric());
788 assert!(!ConcreteType::String.is_numeric());
789 }
790
791 #[test]
792 fn test_is_heap() {
793 assert!(ConcreteType::String.is_heap());
794 assert!(ConcreteType::Array(Box::new(ConcreteType::F64)).is_heap());
795 assert!(!ConcreteType::F64.is_heap());
796 assert!(!ConcreteType::Bool.is_heap());
797 }
798
799 #[test]
800 fn test_display() {
801 assert_eq!(format!("{}", ConcreteType::F64), "number");
802 assert_eq!(format!("{}", ConcreteType::I64), "int");
803 assert_eq!(
804 format!("{}", ConcreteType::Array(Box::new(ConcreteType::F64))),
805 "Array<number>"
806 );
807 assert_eq!(
808 format!("{}", ConcreteType::Option(Box::new(ConcreteType::I64))),
809 "int?"
810 );
811 }
812}