1use rustc_hash::{FxHashMap as HashMap, FxHashSet as HashSet};
2use std::borrow::Borrow;
3use std::cell::OnceCell;
4
5use ecow::EcoString;
6
7#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
14#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
15pub struct Symbol(EcoString);
16
17impl Symbol {
18 pub fn from_parts(module: &str, local: &str) -> Self {
22 let mut s = String::with_capacity(module.len() + 1 + local.len());
23 s.push_str(module);
24 s.push('.');
25 s.push_str(local);
26 Self(EcoString::from(s))
27 }
28
29 pub fn with_segment(&self, segment: &str) -> Self {
34 let mut s = String::with_capacity(self.0.len() + 1 + segment.len());
35 s.push_str(&self.0);
36 s.push('.');
37 s.push_str(segment);
38 Self(EcoString::from(s))
39 }
40
41 pub fn from_raw(qualified: impl Into<EcoString>) -> Self {
44 Self(qualified.into())
45 }
46
47 pub fn as_str(&self) -> &str {
48 &self.0
49 }
50
51 pub fn as_eco(&self) -> &EcoString {
52 &self.0
53 }
54
55 pub fn last_segment(&self) -> &str {
57 self.0.rsplit('.').next().unwrap_or(&self.0)
58 }
59
60 pub fn without_last_segment(&self) -> Option<&str> {
63 self.0.rsplit_once('.').map(|(rest, _)| rest)
64 }
65
66 pub fn simple_module_part(&self) -> Option<&str> {
70 self.0.split_once('.').map(|(m, _)| m)
71 }
72}
73
74impl Borrow<str> for Symbol {
75 fn borrow(&self) -> &str {
76 &self.0
77 }
78}
79
80impl AsRef<str> for Symbol {
81 fn as_ref(&self) -> &str {
82 &self.0
83 }
84}
85
86impl std::ops::Deref for Symbol {
87 type Target = str;
88
89 fn deref(&self) -> &str {
90 &self.0
91 }
92}
93
94impl From<&Symbol> for EcoString {
95 fn from(s: &Symbol) -> Self {
96 s.0.clone()
97 }
98}
99
100impl std::fmt::Display for Symbol {
101 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
102 self.0.fmt(f)
103 }
104}
105
106impl From<EcoString> for Symbol {
107 fn from(s: EcoString) -> Self {
108 Self(s)
109 }
110}
111
112impl From<Symbol> for EcoString {
113 fn from(s: Symbol) -> Self {
114 s.0
115 }
116}
117
118impl From<&str> for Symbol {
119 fn from(s: &str) -> Self {
120 Self(EcoString::from(s))
121 }
122}
123
124impl From<String> for Symbol {
125 fn from(s: String) -> Self {
126 Self(EcoString::from(s))
127 }
128}
129
130impl PartialEq<str> for Symbol {
131 fn eq(&self, other: &str) -> bool {
132 self.0.as_str() == other
133 }
134}
135
136impl PartialEq<&str> for Symbol {
137 fn eq(&self, other: &&str) -> bool {
138 self.0.as_str() == *other
139 }
140}
141
142pub fn unqualified_name(id: &str) -> &str {
146 id.rsplit('.').next().unwrap_or(id)
147}
148
149pub fn module_part(id: &str) -> &str {
157 id.split('.').next().unwrap_or(id)
158}
159
160pub type SubstitutionMap = HashMap<EcoString, Type>;
162
163pub fn substitute(ty: &Type, map: &HashMap<EcoString, Type>) -> Type {
164 if map.is_empty() {
165 return ty.clone();
166 }
167 match ty {
168 Type::Parameter(name) => map.get(name).cloned().unwrap_or_else(|| ty.clone()),
169 Type::Nominal {
170 id,
171 params,
172 underlying_ty: underlying,
173 } => Type::Nominal {
174 id: id.clone(),
175 params: params.iter().map(|p| substitute(p, map)).collect(),
176 underlying_ty: underlying.as_ref().map(|u| Box::new(substitute(u, map))),
177 },
178 Type::Function {
179 params,
180 param_mutability,
181 bounds,
182 return_type,
183 } => Type::Function {
184 params: params.iter().map(|p| substitute(p, map)).collect(),
185 param_mutability: param_mutability.clone(),
186 bounds: bounds
187 .iter()
188 .map(|b| Bound {
189 param_name: b.param_name.clone(),
190 generic: substitute(&b.generic, map),
191 ty: substitute(&b.ty, map),
192 })
193 .collect(),
194 return_type: Box::new(substitute(return_type, map)),
195 },
196 Type::Var { .. } | Type::Error => ty.clone(),
197 Type::Forall { vars, body } => {
198 let has_overlap = map.keys().any(|k| vars.contains(k));
199 let substituted_body = if has_overlap {
200 let filtered_map: HashMap<EcoString, Type> = map
201 .iter()
202 .filter(|(k, _)| !vars.contains(*k))
203 .map(|(k, v)| (k.clone(), v.clone()))
204 .collect();
205 substitute(body, &filtered_map)
206 } else {
207 substitute(body, map)
208 };
209 Type::Forall {
210 vars: vars.clone(),
211 body: Box::new(substituted_body),
212 }
213 }
214 Type::Tuple(elements) => Type::Tuple(elements.iter().map(|e| substitute(e, map)).collect()),
215 Type::Compound { kind, args } => Type::Compound {
216 kind: *kind,
217 args: args.iter().map(|a| substitute(a, map)).collect(),
218 },
219 Type::Simple(_) | Type::Never | Type::ImportNamespace(_) | Type::ReceiverPlaceholder => {
220 ty.clone()
221 }
222 }
223}
224
225#[derive(Debug, Clone, PartialEq)]
226#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
227pub struct Bound {
228 pub param_name: EcoString,
229 pub generic: Type,
230 pub ty: Type,
231}
232
233#[derive(Clone, Copy, PartialEq, Eq, Hash)]
237#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
238pub struct TypeVarId(pub u32);
239
240impl TypeVarId {
241 pub const IGNORED: TypeVarId = TypeVarId(u32::MAX);
242 pub const UNINFERRED: TypeVarId = TypeVarId(u32::MAX - 1);
243
244 pub fn is_reserved(self) -> bool {
245 self == Self::IGNORED || self == Self::UNINFERRED
246 }
247
248 pub fn as_u32(self) -> u32 {
249 self.0
250 }
251}
252
253impl std::fmt::Debug for TypeVarId {
254 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
255 match *self {
256 Self::IGNORED => write!(f, "ignored"),
257 Self::UNINFERRED => write!(f, "uninferred"),
258 TypeVarId(n) => write!(f, "#{}", n),
259 }
260 }
261}
262
263#[derive(Clone)]
264#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
265pub enum Type {
266 Simple(SimpleKind),
267
268 Compound {
269 kind: CompoundKind,
270 args: Vec<Type>,
271 },
272
273 Nominal {
274 id: Symbol,
275 params: Vec<Type>,
276 underlying_ty: Option<Box<Type>>,
277 },
278
279 ImportNamespace(EcoString),
283
284 Function {
285 params: Vec<Type>,
286 param_mutability: Vec<bool>,
287 bounds: Vec<Bound>,
288 return_type: Box<Type>,
289 },
290
291 Var {
295 id: TypeVarId,
296 hint: Option<EcoString>,
297 },
298
299 Forall {
300 vars: Vec<EcoString>,
301 body: Box<Type>,
302 },
303
304 Parameter(EcoString),
305
306 Never,
307
308 Tuple(Vec<Type>),
309
310 Error,
313
314 ReceiverPlaceholder,
319}
320
321impl std::fmt::Debug for Type {
322 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
323 match self {
324 Type::Nominal { id, params, .. } => f
325 .debug_struct("Nominal")
326 .field("id", id)
327 .field("params", params)
328 .finish(),
329 Type::Function {
330 params,
331 param_mutability,
332 bounds,
333 return_type,
334 } => {
335 let mut s = f.debug_struct("Function");
336 s.field("params", params);
337 if param_mutability.iter().any(|m| *m) {
338 s.field("param_mutability", param_mutability);
339 }
340 s.field("bounds", bounds)
341 .field("return_type", return_type)
342 .finish()
343 }
344 Type::Var { id, hint } => {
345 let mut s = f.debug_struct("Var");
346 s.field("id", id);
347 if let Some(h) = hint {
348 s.field("hint", h);
349 }
350 s.finish()
351 }
352 Type::Forall { vars, body } => f
353 .debug_struct("Forall")
354 .field("vars", vars)
355 .field("body", body)
356 .finish(),
357 Type::Parameter(name) => f.debug_tuple("Parameter").field(name).finish(),
358 Type::Never => write!(f, "Never"),
359 Type::Tuple(elements) => f.debug_tuple("Tuple").field(elements).finish(),
360 Type::Error => write!(f, "Error"),
361 Type::ImportNamespace(module_id) => {
362 f.debug_tuple("ImportNamespace").field(module_id).finish()
363 }
364 Type::ReceiverPlaceholder => write!(f, "ReceiverPlaceholder"),
365 Type::Simple(kind) => f.debug_tuple("Simple").field(kind).finish(),
366 Type::Compound { kind, args } => f
367 .debug_struct("Compound")
368 .field("kind", kind)
369 .field("args", args)
370 .finish(),
371 }
372 }
373}
374
375impl PartialEq for Type {
376 fn eq(&self, other: &Self) -> bool {
377 match (self, other) {
378 (
379 Type::Nominal {
380 id: id1,
381 params: params1,
382 ..
383 },
384 Type::Nominal {
385 id: id2,
386 params: params2,
387 ..
388 },
389 ) => id1 == id2 && params1 == params2,
390 (
391 Type::Function {
392 params: p1,
393 param_mutability: m1,
394 bounds: b1,
395 return_type: r1,
396 },
397 Type::Function {
398 params: p2,
399 param_mutability: m2,
400 bounds: b2,
401 return_type: r2,
402 },
403 ) => p1 == p2 && m1 == m2 && b1 == b2 && r1 == r2,
404 (Type::Var { id: id1, .. }, Type::Var { id: id2, .. }) => id1 == id2,
405 (
406 Type::Forall {
407 vars: vars1,
408 body: body1,
409 },
410 Type::Forall {
411 vars: vars2,
412 body: body2,
413 },
414 ) => vars1 == vars2 && body1 == body2,
415 (Type::Parameter(name1), Type::Parameter(name2)) => name1 == name2,
416 (Type::Never, Type::Never) => true,
417 (Type::Tuple(elems1), Type::Tuple(elems2)) => elems1 == elems2,
418 (Type::ImportNamespace(m1), Type::ImportNamespace(m2)) => m1 == m2,
419 (Type::ReceiverPlaceholder, Type::ReceiverPlaceholder) => true,
420 (Type::Simple(k1), Type::Simple(k2)) => k1 == k2,
421 (Type::Compound { kind: k1, args: a1 }, Type::Compound { kind: k2, args: a2 }) => {
422 k1 == k2 && a1 == a2
423 }
424 _ => false,
425 }
426 }
427}
428
429thread_local! {
430 static INTERNED_INT: OnceCell<Type> = const { OnceCell::new() };
431 static INTERNED_STRING: OnceCell<Type> = const { OnceCell::new() };
432 static INTERNED_BOOL: OnceCell<Type> = const { OnceCell::new() };
433 static INTERNED_UNIT: OnceCell<Type> = const { OnceCell::new() };
434 static INTERNED_FLOAT64: OnceCell<Type> = const { OnceCell::new() };
435 static INTERNED_RUNE: OnceCell<Type> = const { OnceCell::new() };
436 static INTERNED_BYTE: OnceCell<Type> = const { OnceCell::new() };
437}
438
439impl Type {
440 pub fn simple(kind: SimpleKind) -> Type {
441 Self::Simple(kind)
442 }
443
444 pub fn compound(kind: CompoundKind, args: Vec<Type>) -> Type {
445 Self::Compound { kind, args }
446 }
447
448 pub fn int() -> Type {
449 INTERNED_INT.with(|cell| cell.get_or_init(|| Self::simple(SimpleKind::Int)).clone())
450 }
451
452 pub fn string() -> Type {
453 INTERNED_STRING.with(|cell| {
454 cell.get_or_init(|| Self::simple(SimpleKind::String))
455 .clone()
456 })
457 }
458
459 pub fn bool() -> Type {
460 INTERNED_BOOL.with(|cell| cell.get_or_init(|| Self::simple(SimpleKind::Bool)).clone())
461 }
462
463 pub fn unit() -> Type {
464 INTERNED_UNIT.with(|cell| cell.get_or_init(|| Self::simple(SimpleKind::Unit)).clone())
465 }
466
467 pub fn float64() -> Type {
468 INTERNED_FLOAT64.with(|cell| {
469 cell.get_or_init(|| Self::simple(SimpleKind::Float64))
470 .clone()
471 })
472 }
473
474 pub fn rune() -> Type {
475 INTERNED_RUNE.with(|cell| cell.get_or_init(|| Self::simple(SimpleKind::Rune)).clone())
476 }
477
478 pub fn byte() -> Type {
479 INTERNED_BYTE.with(|cell| cell.get_or_init(|| Self::simple(SimpleKind::Byte)).clone())
480 }
481}
482
483impl Type {
484 pub fn uninferred() -> Self {
485 Self::Var {
486 id: TypeVarId::UNINFERRED,
487 hint: None,
488 }
489 }
490
491 pub fn ignored() -> Self {
492 Self::Var {
493 id: TypeVarId::IGNORED,
494 hint: None,
495 }
496 }
497
498 pub fn get_type_params(&self) -> Option<&[Type]> {
499 match self {
500 Type::Nominal { params, .. } => Some(params),
501 Type::Compound { args, .. } => Some(args),
502 _ => None,
503 }
504 }
505
506 pub fn children(&self) -> Vec<&Type> {
508 match self {
509 Type::Nominal {
510 params,
511 underlying_ty,
512 ..
513 } => {
514 let mut c: Vec<&Type> = params.iter().collect();
515 if let Some(u) = underlying_ty {
516 c.push(u);
517 }
518 c
519 }
520 Type::Compound { args, .. } => args.iter().collect(),
521 Type::Function {
522 params,
523 return_type,
524 ..
525 } => {
526 let mut c: Vec<&Type> = params.iter().collect();
527 c.push(return_type);
528 c
529 }
530 Type::Tuple(elements) => elements.iter().collect(),
531 Type::Forall { body, .. } => vec![body],
532 _ => vec![],
533 }
534 }
535}
536
537#[derive(Debug, Clone, Copy, PartialEq, Eq)]
538pub enum NumericFamily {
539 SignedInt,
540 UnsignedInt,
541 Float,
542}
543
544#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
545#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
546pub enum CompoundKind {
547 Ref,
548 Slice,
549 EnumeratedSlice,
550 Map,
551 Channel,
552 Sender,
553 Receiver,
554 VarArgs,
555}
556
557impl CompoundKind {
558 pub fn leaf_name(self) -> &'static str {
559 match self {
560 CompoundKind::Ref => "Ref",
561 CompoundKind::Slice => "Slice",
562 CompoundKind::EnumeratedSlice => "EnumeratedSlice",
563 CompoundKind::Map => "Map",
564 CompoundKind::Channel => "Channel",
565 CompoundKind::Sender => "Sender",
566 CompoundKind::Receiver => "Receiver",
567 CompoundKind::VarArgs => "VarArgs",
568 }
569 }
570
571 pub fn from_name(name: &str) -> Option<CompoundKind> {
572 Some(match name {
573 "Ref" => CompoundKind::Ref,
574 "Slice" => CompoundKind::Slice,
575 "EnumeratedSlice" => CompoundKind::EnumeratedSlice,
576 "Map" => CompoundKind::Map,
577 "Channel" => CompoundKind::Channel,
578 "Sender" => CompoundKind::Sender,
579 "Receiver" => CompoundKind::Receiver,
580 "VarArgs" => CompoundKind::VarArgs,
581 _ => return None,
582 })
583 }
584}
585
586#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
587#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
588pub enum SimpleKind {
589 Int,
590 Int8,
591 Int16,
592 Int32,
593 Int64,
594 Uint,
595 Uint8,
596 Uint16,
597 Uint32,
598 Uint64,
599 Uintptr,
600 Byte,
601 Float32,
602 Float64,
603 Complex64,
604 Complex128,
605 Rune,
606 Bool,
607 String,
608 Unit,
609}
610
611impl SimpleKind {
612 pub fn leaf_name(self) -> &'static str {
613 match self {
614 SimpleKind::Int => "int",
615 SimpleKind::Int8 => "int8",
616 SimpleKind::Int16 => "int16",
617 SimpleKind::Int32 => "int32",
618 SimpleKind::Int64 => "int64",
619 SimpleKind::Uint => "uint",
620 SimpleKind::Uint8 => "uint8",
621 SimpleKind::Uint16 => "uint16",
622 SimpleKind::Uint32 => "uint32",
623 SimpleKind::Uint64 => "uint64",
624 SimpleKind::Uintptr => "uintptr",
625 SimpleKind::Byte => "byte",
626 SimpleKind::Float32 => "float32",
627 SimpleKind::Float64 => "float64",
628 SimpleKind::Complex64 => "complex64",
629 SimpleKind::Complex128 => "complex128",
630 SimpleKind::Rune => "rune",
631 SimpleKind::Bool => "bool",
632 SimpleKind::String => "string",
633 SimpleKind::Unit => "Unit",
634 }
635 }
636
637 pub fn from_name(name: &str) -> Option<SimpleKind> {
638 Some(match name {
639 "int" => SimpleKind::Int,
640 "int8" => SimpleKind::Int8,
641 "int16" => SimpleKind::Int16,
642 "int32" => SimpleKind::Int32,
643 "int64" => SimpleKind::Int64,
644 "uint" => SimpleKind::Uint,
645 "uint8" => SimpleKind::Uint8,
646 "uint16" => SimpleKind::Uint16,
647 "uint32" => SimpleKind::Uint32,
648 "uint64" => SimpleKind::Uint64,
649 "uintptr" => SimpleKind::Uintptr,
650 "byte" => SimpleKind::Byte,
651 "float32" => SimpleKind::Float32,
652 "float64" => SimpleKind::Float64,
653 "complex64" => SimpleKind::Complex64,
654 "complex128" => SimpleKind::Complex128,
655 "rune" => SimpleKind::Rune,
656 "bool" => SimpleKind::Bool,
657 "string" => SimpleKind::String,
658 "Unit" => SimpleKind::Unit,
659 _ => return None,
660 })
661 }
662
663 pub fn is_arithmetic(self) -> bool {
664 !matches!(
665 self,
666 SimpleKind::Bool | SimpleKind::String | SimpleKind::Unit | SimpleKind::Uintptr
667 )
668 }
669
670 pub fn is_ordered(self) -> bool {
671 self.is_arithmetic() && !matches!(self, SimpleKind::Complex64 | SimpleKind::Complex128)
672 }
673
674 pub fn is_unsigned_int(self) -> bool {
675 matches!(
676 self,
677 SimpleKind::Byte
678 | SimpleKind::Uint
679 | SimpleKind::Uint8
680 | SimpleKind::Uint16
681 | SimpleKind::Uint32
682 | SimpleKind::Uint64
683 )
684 }
685
686 pub fn is_signed_int(self) -> bool {
687 matches!(
688 self,
689 SimpleKind::Int
690 | SimpleKind::Int8
691 | SimpleKind::Int16
692 | SimpleKind::Int32
693 | SimpleKind::Int64
694 | SimpleKind::Rune
695 )
696 }
697
698 pub fn is_float(self) -> bool {
699 matches!(self, SimpleKind::Float32 | SimpleKind::Float64)
700 }
701
702 pub fn is_complex(self) -> bool {
703 matches!(self, SimpleKind::Complex64 | SimpleKind::Complex128)
704 }
705
706 pub fn numeric_family(self) -> Option<NumericFamily> {
707 if self.is_signed_int() {
708 Some(NumericFamily::SignedInt)
709 } else if self.is_unsigned_int() {
710 Some(NumericFamily::UnsignedInt)
711 } else if self.is_float() {
712 Some(NumericFamily::Float)
713 } else {
714 None
715 }
716 }
717}
718
719impl Type {
720 pub fn get_function_ret(&self) -> Option<&Type> {
721 match self {
722 Type::Function { return_type, .. } => Some(return_type),
723 _ => None,
724 }
725 }
726
727 pub fn has_name(&self, name: &str) -> bool {
728 match self {
729 Type::Nominal { id, .. } => id.last_segment() == name,
730 Type::Simple(kind) => kind.leaf_name() == name,
731 Type::Compound { kind, .. } => kind.leaf_name() == name,
732 _ => false,
733 }
734 }
735
736 pub fn get_qualified_id(&self) -> Option<&str> {
737 match self {
738 Type::Nominal { id, .. } => Some(id.as_str()),
739 _ => None,
740 }
741 }
742
743 pub fn get_underlying(&self) -> Option<&Type> {
744 match self {
745 Type::Nominal {
746 underlying_ty: underlying,
747 ..
748 } => underlying.as_deref(),
749 _ => None,
750 }
751 }
752
753 pub fn is_result(&self) -> bool {
754 self.has_qualified_id("prelude.Result")
755 }
756
757 pub fn is_option(&self) -> bool {
758 self.has_qualified_id("prelude.Option")
759 }
760
761 pub fn is_partial(&self) -> bool {
762 self.has_qualified_id("prelude.Partial")
763 }
764
765 fn has_qualified_id(&self, qualified_id: &str) -> bool {
766 matches!(self, Type::Nominal { id, .. } if id.as_str() == qualified_id)
767 }
768
769 pub fn is_unit(&self) -> bool {
770 self.is_simple(SimpleKind::Unit)
771 }
772
773 pub fn tuple_arity(&self) -> Option<usize> {
774 match self {
775 Type::Tuple(elements) => Some(elements.len()),
776 _ => None,
777 }
778 }
779
780 pub fn is_tuple(&self) -> bool {
781 matches!(self, Type::Tuple(_))
782 }
783
784 pub fn as_import_namespace(&self) -> Option<&str> {
785 match self {
786 Type::ImportNamespace(module_id) => Some(module_id),
787 _ => None,
788 }
789 }
790
791 pub fn as_compound(&self) -> Option<(CompoundKind, &[Type])> {
792 match self {
793 Type::Compound { kind, args } => Some((*kind, args.as_slice())),
794 Type::Nominal { id, params, .. } => {
795 CompoundKind::from_name(id.last_segment()).map(|k| (k, params.as_slice()))
796 }
797 _ => None,
798 }
799 }
800
801 pub fn is_native(&self, kind: CompoundKind) -> bool {
802 self.as_compound().is_some_and(|(k, _)| k == kind)
803 }
804
805 pub fn is_ref(&self) -> bool {
806 self.is_native(CompoundKind::Ref)
807 }
808
809 pub fn is_slice(&self) -> bool {
810 self.is_native(CompoundKind::Slice)
811 }
812
813 pub fn is_map(&self) -> bool {
814 self.is_native(CompoundKind::Map)
815 }
816
817 pub fn is_channel(&self) -> bool {
818 self.is_native(CompoundKind::Channel)
819 }
820
821 pub fn is_receiver_placeholder(&self) -> bool {
822 matches!(self, Type::ReceiverPlaceholder)
823 }
824
825 pub fn is_unknown(&self) -> bool {
826 self.has_name("Unknown")
827 }
828
829 pub fn resolves_to_unknown(&self) -> bool {
830 peel_alias(self, |_| true).is_unknown()
831 }
832
833 pub fn contains_unknown(&self) -> bool {
834 let peeled = peel_alias(self, |_| true);
835 if peeled.is_unknown() {
836 return true;
837 }
838 match &peeled {
839 Type::Compound { args, .. } => args.iter().any(|a| a.contains_unknown()),
840 Type::Function {
841 params,
842 return_type,
843 ..
844 } => params.iter().any(|p| p.contains_unknown()) || return_type.contains_unknown(),
845 Type::Tuple(elements) => elements.iter().any(|e| e.contains_unknown()),
846 Type::Nominal { params, .. } => params.iter().any(|p| p.contains_unknown()),
847 Type::Forall { body, .. } => body.contains_unknown(),
848 _ => false,
849 }
850 }
851
852 pub fn is_receiver(&self) -> bool {
853 self.is_native(CompoundKind::Receiver)
854 }
855
856 pub fn is_ignored(&self) -> bool {
857 matches!(self, Type::Var { id, .. } if *id == TypeVarId::IGNORED)
858 }
859
860 pub fn is_variadic(&self) -> Option<Type> {
861 let last = self.get_function_params()?.last()?;
862 match last.as_compound()? {
863 (CompoundKind::VarArgs, _) => last.inner(),
864 _ => None,
865 }
866 }
867
868 pub fn is_string(&self) -> bool {
869 self.is_simple(SimpleKind::String)
870 }
871
872 pub fn is_slice_of_simple(&self, element: SimpleKind) -> bool {
873 match self.as_compound() {
874 Some((CompoundKind::Slice, [elem])) => elem.is_simple(element),
875 _ => false,
876 }
877 }
878
879 pub fn is_slice_of(&self, element_name: &str) -> bool {
880 match self.as_compound() {
881 Some((CompoundKind::Slice, [elem])) => elem.has_name(element_name),
882 _ => false,
883 }
884 }
885
886 pub fn is_byte_slice(&self) -> bool {
887 self.is_slice_of_simple(SimpleKind::Byte) || self.is_slice_of_simple(SimpleKind::Uint8)
888 }
889
890 pub fn is_rune_slice(&self) -> bool {
891 self.is_slice_of_simple(SimpleKind::Rune)
892 }
893
894 pub fn is_byte_or_rune_slice(&self) -> bool {
895 self.is_byte_slice() || self.is_rune_slice()
896 }
897
898 pub fn has_byte_or_rune_slice_underlying(&self) -> bool {
899 if self.is_byte_or_rune_slice() {
900 return true;
901 }
902 match self {
903 Type::Nominal { underlying_ty, .. } => underlying_ty
904 .as_deref()
905 .is_some_and(|u| u.has_byte_or_rune_slice_underlying()),
906 _ => false,
907 }
908 }
909
910 pub fn as_simple(&self) -> Option<SimpleKind> {
911 match self {
912 Type::Simple(kind) => Some(*kind),
913 Type::Nominal { id, .. } => SimpleKind::from_name(id.last_segment()),
914 _ => None,
915 }
916 }
917
918 pub fn is_simple(&self, kind: SimpleKind) -> bool {
919 self.as_simple() == Some(kind)
920 }
921
922 pub fn is_boolean(&self) -> bool {
923 self.is_simple(SimpleKind::Bool)
924 }
925
926 pub fn is_rune(&self) -> bool {
927 self.is_simple(SimpleKind::Rune)
928 }
929
930 pub fn is_float64(&self) -> bool {
931 self.is_simple(SimpleKind::Float64)
932 }
933
934 pub fn is_float32(&self) -> bool {
935 self.is_simple(SimpleKind::Float32)
936 }
937
938 pub fn is_float(&self) -> bool {
939 self.as_simple().is_some_and(SimpleKind::is_float)
940 }
941
942 pub fn is_variable(&self) -> bool {
943 matches!(self, Type::Var { .. })
944 }
945
946 pub fn is_type_var(&self) -> bool {
947 matches!(self, Type::Var { .. })
948 }
949
950 pub fn is_numeric(&self) -> bool {
951 self.as_simple().is_some_and(SimpleKind::is_arithmetic)
952 }
953
954 pub fn is_ordered(&self) -> bool {
955 self.as_simple().is_some_and(SimpleKind::is_ordered)
956 }
957
958 pub fn satisfies_ordered_constraint(&self) -> bool {
960 if let Some(kind) = self.as_simple() {
961 return matches!(
962 kind,
963 SimpleKind::Int
964 | SimpleKind::Int8
965 | SimpleKind::Int16
966 | SimpleKind::Int32
967 | SimpleKind::Int64
968 | SimpleKind::Uint
969 | SimpleKind::Uint8
970 | SimpleKind::Uint16
971 | SimpleKind::Uint32
972 | SimpleKind::Uint64
973 | SimpleKind::Uintptr
974 | SimpleKind::Byte
975 | SimpleKind::Rune
976 | SimpleKind::Float32
977 | SimpleKind::Float64
978 | SimpleKind::String
979 );
980 }
981 match self {
982 Type::Nominal { underlying_ty, .. } => underlying_ty
983 .as_deref()
984 .is_some_and(Type::satisfies_ordered_constraint),
985 Type::Parameter(_) => true,
986 _ => false,
987 }
988 }
989
990 pub fn is_complex(&self) -> bool {
991 self.as_simple().is_some_and(SimpleKind::is_complex)
992 }
993
994 pub fn is_unsigned_int(&self) -> bool {
995 self.as_simple().is_some_and(SimpleKind::is_unsigned_int)
996 }
997
998 pub fn is_never(&self) -> bool {
999 matches!(self, Type::Never)
1000 }
1001
1002 pub fn is_error(&self) -> bool {
1003 matches!(self, Type::Error)
1004 }
1005
1006 pub fn contains_error(&self) -> bool {
1007 match self {
1008 Type::Error => true,
1009 Type::Nominal {
1010 params,
1011 underlying_ty,
1012 ..
1013 } => {
1014 params.iter().any(Type::contains_error)
1015 || underlying_ty.as_deref().is_some_and(Type::contains_error)
1016 }
1017 Type::Compound { args, .. } => args.iter().any(Type::contains_error),
1018 Type::Function {
1019 params,
1020 return_type,
1021 ..
1022 } => params.iter().any(Type::contains_error) || return_type.contains_error(),
1023 Type::Tuple(elements) => elements.iter().any(Type::contains_error),
1024 Type::Forall { body, .. } => body.contains_error(),
1025 _ => false,
1026 }
1027 }
1028
1029 pub fn has_unbound_variables(&self) -> bool {
1030 match self {
1031 Type::Var { hint, .. } => hint.is_some(),
1032 Type::Nominal { params, .. } => params.iter().any(|p| p.has_unbound_variables()),
1033 Type::Function {
1034 params,
1035 return_type,
1036 ..
1037 } => {
1038 params.iter().any(|p| p.has_unbound_variables())
1039 || return_type.has_unbound_variables()
1040 }
1041 Type::Forall { body, .. } => body.has_unbound_variables(),
1042 Type::Tuple(elements) => elements.iter().any(|e| e.has_unbound_variables()),
1043 Type::Compound { args, .. } => args.iter().any(|a| a.has_unbound_variables()),
1044 Type::Simple(_)
1045 | Type::Parameter(_)
1046 | Type::Never
1047 | Type::Error
1048 | Type::ImportNamespace(_)
1049 | Type::ReceiverPlaceholder => false,
1050 }
1051 }
1052
1053 pub fn remove_found_type_names(&self, names: &mut HashSet<EcoString>) {
1054 if names.is_empty() {
1055 return;
1056 }
1057
1058 match self {
1059 Type::Nominal { id, params, .. } => {
1060 names.remove(id.last_segment());
1061 for param in params {
1062 param.remove_found_type_names(names);
1063 }
1064 }
1065 Type::Function {
1066 params,
1067 return_type,
1068 bounds,
1069 ..
1070 } => {
1071 for param in params {
1072 param.remove_found_type_names(names);
1073 }
1074 return_type.remove_found_type_names(names);
1075 for bound in bounds {
1076 bound.generic.remove_found_type_names(names);
1077 bound.ty.remove_found_type_names(names);
1078 }
1079 }
1080 Type::Forall { body, .. } => {
1081 body.remove_found_type_names(names);
1082 }
1083 Type::Var { .. } => {}
1084 Type::Parameter(name) => {
1085 names.remove(name);
1086 }
1087 Type::Tuple(elements) => {
1088 for element in elements {
1089 element.remove_found_type_names(names);
1090 }
1091 }
1092 Type::Compound { kind, args } => {
1093 names.remove(kind.leaf_name());
1094 for arg in args {
1095 arg.remove_found_type_names(names);
1096 }
1097 }
1098 Type::Simple(kind) => {
1099 names.remove(kind.leaf_name());
1100 }
1101 Type::Never | Type::Error | Type::ImportNamespace(_) | Type::ReceiverPlaceholder => {}
1102 }
1103 }
1104}
1105
1106impl Type {
1107 pub fn get_name(&self) -> Option<&str> {
1108 match self {
1109 Type::Simple(kind) => Some(kind.leaf_name()),
1110 Type::Compound { kind, args } => match kind {
1111 CompoundKind::Ref => args.first().and_then(|inner| inner.get_name()),
1112 _ => Some(kind.leaf_name()),
1113 },
1114 Type::Nominal { id, params, .. } => {
1115 let name = id.last_segment();
1116 if CompoundKind::from_name(name) == Some(CompoundKind::Ref) {
1117 return params.first().and_then(|inner| inner.get_name());
1118 }
1119 Some(name)
1120 }
1121 Type::ImportNamespace(module_id) => {
1122 let path = module_id.strip_prefix("go:").unwrap_or(module_id);
1123 path.rsplit('/').next()
1124 }
1125 _ => None,
1126 }
1127 }
1128
1129 pub fn wraps(&self, name: &str, inner: &Type) -> bool {
1130 self.get_name().is_some_and(|n| n == name)
1131 && self
1132 .get_type_params()
1133 .and_then(|p| p.first())
1134 .is_some_and(|first| *first == *inner)
1135 }
1136
1137 pub fn get_function_params(&self) -> Option<&[Type]> {
1138 match self {
1139 Type::Function { params, .. } => Some(params),
1140 Type::Nominal {
1141 underlying_ty: Some(inner),
1142 ..
1143 } => inner.get_function_params(),
1144 _ => None,
1145 }
1146 }
1147
1148 pub fn param_count(&self) -> usize {
1149 match self {
1150 Type::Function { params, .. } => params.len(),
1151 _ => 0,
1152 }
1153 }
1154
1155 pub fn get_param_mutability(&self) -> &[bool] {
1156 match self {
1157 Type::Function {
1158 param_mutability, ..
1159 } => param_mutability,
1160 _ => &[],
1161 }
1162 }
1163
1164 pub fn with_replaced_first_param(&self, new_first: &Type) -> Type {
1165 match self {
1166 Type::Function {
1167 params,
1168 param_mutability,
1169 bounds,
1170 return_type,
1171 } => {
1172 if params.is_empty() {
1173 return self.clone();
1174 }
1175 let mut new_params = params.clone();
1176 new_params[0] = new_first.clone();
1177 Type::Function {
1178 params: new_params,
1179 param_mutability: param_mutability.clone(),
1180 bounds: bounds.clone(),
1181 return_type: return_type.clone(),
1182 }
1183 }
1184 Type::Forall { vars, body } => Type::Forall {
1185 vars: vars.clone(),
1186 body: Box::new(body.with_replaced_first_param(new_first)),
1187 },
1188 _ => self.clone(),
1189 }
1190 }
1191
1192 pub fn get_bounds(&self) -> &[Bound] {
1193 match self {
1194 Type::Function { bounds, .. } => bounds,
1195 Type::Forall { body, .. } => body.get_bounds(),
1196 _ => &[],
1197 }
1198 }
1199
1200 pub fn get_qualified_name(&self) -> Symbol {
1201 match self.strip_refs() {
1202 Type::Nominal { id, .. } => id,
1203 Type::Simple(kind) => Symbol::from_parts("prelude", kind.leaf_name()),
1204 Type::Compound { kind, .. } => Symbol::from_parts("prelude", kind.leaf_name()),
1205 _ => panic!("called get_qualified_name on {:#?}", self),
1206 }
1207 }
1208
1209 pub fn inner(&self) -> Option<Type> {
1210 self.get_type_params()
1211 .and_then(|args| args.first().cloned())
1212 }
1213
1214 pub fn ok_type(&self) -> Type {
1215 debug_assert!(
1216 self.is_result() || self.is_option() || self.is_partial(),
1217 "ok_type called on non-Result/Option/Partial type"
1218 );
1219 self.inner()
1220 .expect("Result/Option/Partial should have inner type")
1221 }
1222
1223 pub fn err_type(&self) -> Type {
1224 debug_assert!(
1225 self.is_result() || self.is_partial(),
1226 "err_type called on non-Result/Partial type"
1227 );
1228 self.get_type_params()
1229 .and_then(|args| args.get(1).cloned())
1230 .expect("Result/Partial should have error type")
1231 }
1232}
1233
1234pub fn peel_alias<F>(ty: &Type, is_alias: F) -> Type
1237where
1238 F: Fn(&str) -> bool,
1239{
1240 let mut current = ty.unwrap_forall().clone();
1241 let mut seen: Vec<String> = Vec::new();
1242 while let Type::Nominal {
1243 id,
1244 underlying_ty: Some(u),
1245 ..
1246 } = ¤t
1247 {
1248 if !is_alias(id.as_str()) {
1249 break;
1250 }
1251 if seen.iter().any(|s| s == id.as_str()) {
1252 break;
1253 }
1254 seen.push(id.to_string());
1255 current = u.unwrap_forall().clone();
1256 }
1257 current
1258}
1259
1260pub fn peel_alias_id<F>(id: &str, next_alias: F) -> String
1263where
1264 F: Fn(&str) -> Option<String>,
1265{
1266 let mut current = id.to_string();
1267 let mut seen: Vec<String> = Vec::new();
1268 loop {
1269 if seen.iter().any(|s| s == ¤t) {
1270 return current;
1271 }
1272 let Some(next) = next_alias(¤t) else {
1273 return current;
1274 };
1275 seen.push(current);
1276 current = next;
1277 }
1278}
1279
1280impl Type {
1281 pub fn unwrap_forall(&self) -> &Type {
1282 match self {
1283 Type::Forall { body, .. } => body.as_ref(),
1284 other => other,
1285 }
1286 }
1287
1288 pub fn strip_refs(&self) -> Type {
1289 if self.is_ref() {
1290 return self.inner().expect("ref type must have inner").strip_refs();
1291 }
1292
1293 self.clone()
1294 }
1295
1296 pub fn with_receiver_placeholder(self) -> Type {
1297 match self {
1298 Type::Function {
1299 params,
1300 param_mutability,
1301 bounds,
1302 return_type,
1303 } => {
1304 let mut new_params = vec![Type::ReceiverPlaceholder];
1305 new_params.extend(params);
1306
1307 let mut new_mutability = vec![false];
1308 new_mutability.extend(param_mutability);
1309
1310 Type::Function {
1311 params: new_params,
1312 param_mutability: new_mutability,
1313 bounds,
1314 return_type,
1315 }
1316 }
1317 _ => unreachable!(
1318 "with_receiver_placeholder called on non-function type: {:?}",
1319 self
1320 ),
1321 }
1322 }
1323
1324 pub fn remove_vars(types: &[&Type]) -> (Vec<Type>, Vec<EcoString>) {
1325 let mut vars = HashMap::default();
1326 let types = types
1327 .iter()
1328 .map(|v| Self::remove_vars_impl(v, &mut vars))
1329 .collect();
1330
1331 (types, vars.into_values().collect())
1332 }
1333
1334 fn remove_vars_impl(ty: &Type, vars: &mut HashMap<u32, EcoString>) -> Type {
1335 match ty {
1336 Type::Nominal {
1337 id: name,
1338 params: args,
1339 underlying_ty: underlying,
1340 } => Type::Nominal {
1341 id: name.clone(),
1342 params: args
1343 .iter()
1344 .map(|a| Self::remove_vars_impl(a, vars))
1345 .collect(),
1346 underlying_ty: underlying
1347 .as_ref()
1348 .map(|u| Box::new(Self::remove_vars_impl(u, vars))),
1349 },
1350
1351 Type::Function {
1352 params: args,
1353 param_mutability,
1354 bounds,
1355 return_type,
1356 } => Type::Function {
1357 params: args
1358 .iter()
1359 .map(|a| Self::remove_vars_impl(a, vars))
1360 .collect(),
1361 param_mutability: param_mutability.clone(),
1362 bounds: bounds
1363 .iter()
1364 .map(|b| Bound {
1365 param_name: b.param_name.clone(),
1366 generic: Self::remove_vars_impl(&b.generic, vars),
1367 ty: Self::remove_vars_impl(&b.ty, vars),
1368 })
1369 .collect(),
1370 return_type: Self::remove_vars_impl(return_type, vars).into(),
1371 },
1372
1373 Type::Var { id, hint } => match vars.get(&id.0) {
1374 Some(g) => Type::Parameter(g.clone()),
1375 None => {
1376 let name: EcoString = hint
1377 .clone()
1378 .unwrap_or_else(|| alpha_index(vars.len()).into());
1379
1380 vars.insert(id.0, name.clone());
1381 Type::Parameter(name)
1382 }
1383 },
1384
1385 Type::Forall { body, .. } => Self::remove_vars_impl(body, vars),
1386 Type::Tuple(elements) => Type::Tuple(
1387 elements
1388 .iter()
1389 .map(|e| Self::remove_vars_impl(e, vars))
1390 .collect(),
1391 ),
1392 Type::Compound { kind, args } => Type::Compound {
1393 kind: *kind,
1394 args: args
1395 .iter()
1396 .map(|a| Self::remove_vars_impl(a, vars))
1397 .collect(),
1398 },
1399 Type::Simple(_) | Type::Parameter(_) => ty.clone(),
1400 Type::Never | Type::Error | Type::ImportNamespace(_) | Type::ReceiverPlaceholder => {
1401 ty.clone()
1402 }
1403 }
1404 }
1405
1406 pub fn contains_type(&self, target: &Type) -> bool {
1407 if *self == *target {
1408 return true;
1409 }
1410 match self {
1411 Type::Nominal { params, .. } => params.iter().any(|p| p.contains_type(target)),
1412 Type::Function {
1413 params,
1414 return_type,
1415 ..
1416 } => {
1417 params.iter().any(|p| p.contains_type(target)) || return_type.contains_type(target)
1418 }
1419 Type::Var { .. } => false,
1420 Type::Forall { body, .. } => body.contains_type(target),
1421 Type::Tuple(elements) => elements.iter().any(|e| e.contains_type(target)),
1422 Type::Compound { args, .. } => args.iter().any(|a| a.contains_type(target)),
1423 Type::Simple(_)
1424 | Type::Parameter(_)
1425 | Type::Never
1426 | Type::Error
1427 | Type::ImportNamespace(_)
1428 | Type::ReceiverPlaceholder => false,
1429 }
1430 }
1431}
1432
1433impl Type {
1434 pub fn underlying_numeric_type(&self) -> Option<Type> {
1435 self.underlying_numeric_type_recursive(&mut HashSet::default())
1436 }
1437
1438 pub fn has_underlying_numeric_type(&self) -> bool {
1439 self.underlying_numeric_type().is_some()
1440 }
1441
1442 fn underlying_numeric_type_recursive(&self, visited: &mut HashSet<Symbol>) -> Option<Type> {
1443 match self {
1444 Type::Simple(_) if self.is_numeric() => Some(self.clone()),
1445 Type::Nominal {
1446 id,
1447 underlying_ty: underlying,
1448 ..
1449 } => {
1450 if self.is_numeric() {
1451 return Some(self.clone());
1452 }
1453
1454 if !visited.insert(id.clone()) {
1455 return None;
1456 }
1457
1458 underlying
1459 .as_ref()?
1460 .underlying_numeric_type_recursive(visited)
1461 }
1462 _ => None,
1463 }
1464 }
1465
1466 pub fn numeric_family(&self) -> Option<NumericFamily> {
1467 self.as_simple()?.numeric_family()
1468 }
1469
1470 pub fn is_numeric_compatible_with(&self, other: &Type) -> bool {
1471 let self_underlying_ty = self.underlying_numeric_type();
1472 let other_underlying_ty = other.underlying_numeric_type();
1473
1474 match (self_underlying_ty, other_underlying_ty) {
1475 (Some(s), Some(o)) => s.numeric_family() == o.numeric_family(),
1476 _ => false,
1477 }
1478 }
1479
1480 pub fn is_aliased_numeric_type(&self) -> bool {
1481 match self {
1482 Type::Nominal { underlying_ty, .. } => {
1483 underlying_ty.is_some() && !self.is_numeric() && self.has_underlying_numeric_type()
1484 }
1485 _ => false,
1486 }
1487 }
1488}
1489
1490fn alpha_index(idx: usize) -> String {
1492 let mut s = String::new();
1493 let mut n = idx + 1;
1494 while n > 0 {
1495 n -= 1;
1496 s.insert(0, (b'A' + (n % 26) as u8) as char);
1497 n /= 26;
1498 }
1499 s
1500}
1501
1502#[cfg(test)]
1503mod tests {
1504 use super::*;
1505
1506 #[test]
1507 fn alpha_index_single() {
1508 assert_eq!(alpha_index(0), "A");
1509 assert_eq!(alpha_index(5), "F");
1510 assert_eq!(alpha_index(25), "Z");
1511 }
1512
1513 #[test]
1514 fn alpha_index_double() {
1515 assert_eq!(alpha_index(26), "AA");
1516 assert_eq!(alpha_index(27), "AB");
1517 assert_eq!(alpha_index(51), "AZ");
1518 assert_eq!(alpha_index(52), "BA");
1519 assert_eq!(alpha_index(701), "ZZ");
1520 }
1521
1522 #[test]
1523 fn alpha_index_triple() {
1524 assert_eq!(alpha_index(702), "AAA");
1525 }
1526
1527 fn unhinted_var(id: u32) -> Type {
1528 Type::Var {
1529 id: TypeVarId(id),
1530 hint: None,
1531 }
1532 }
1533
1534 #[test]
1535 fn remove_vars_handles_more_than_six_unhinted_vars() {
1536 let func = Type::Function {
1537 params: (0..6).map(unhinted_var).collect(),
1538 param_mutability: vec![false; 6],
1539 bounds: vec![],
1540 return_type: Box::new(unhinted_var(6)),
1541 };
1542
1543 let (resolved, generics) = Type::remove_vars(&[&func]);
1544
1545 assert_eq!(generics.len(), 7);
1546 let Type::Function {
1547 params,
1548 return_type,
1549 ..
1550 } = &resolved[0]
1551 else {
1552 panic!("expected function type");
1553 };
1554 let names: Vec<_> = params
1555 .iter()
1556 .chain(std::iter::once(return_type.as_ref()))
1557 .map(|p| match p {
1558 Type::Parameter(name) => name.to_string(),
1559 other => panic!("expected parameter, got {:?}", other),
1560 })
1561 .collect();
1562 assert_eq!(names, vec!["A", "B", "C", "D", "E", "F", "G"]);
1563 }
1564
1565 #[test]
1566 fn remove_vars_handles_dozens_of_unhinted_vars() {
1567 let params: Vec<Type> = (0..30).map(unhinted_var).collect();
1568 let func = Type::Function {
1569 params: params.clone(),
1570 param_mutability: vec![false; params.len()],
1571 bounds: vec![],
1572 return_type: Box::new(Type::Simple(SimpleKind::Unit)),
1573 };
1574 let (_, generics) = Type::remove_vars(&[&func]);
1575 assert_eq!(generics.len(), 30);
1576 }
1577}