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