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