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
619#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
620#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
621pub enum SimpleKind {
622 Int,
623 Int8,
624 Int16,
625 Int32,
626 Int64,
627 Uint,
628 Uint8,
629 Uint16,
630 Uint32,
631 Uint64,
632 Uintptr,
633 Byte,
634 Float32,
635 Float64,
636 Complex64,
637 Complex128,
638 Rune,
639 Bool,
640 String,
641 Unit,
642}
643
644impl SimpleKind {
645 pub fn leaf_name(self) -> &'static str {
646 match self {
647 SimpleKind::Int => "int",
648 SimpleKind::Int8 => "int8",
649 SimpleKind::Int16 => "int16",
650 SimpleKind::Int32 => "int32",
651 SimpleKind::Int64 => "int64",
652 SimpleKind::Uint => "uint",
653 SimpleKind::Uint8 => "uint8",
654 SimpleKind::Uint16 => "uint16",
655 SimpleKind::Uint32 => "uint32",
656 SimpleKind::Uint64 => "uint64",
657 SimpleKind::Uintptr => "uintptr",
658 SimpleKind::Byte => "byte",
659 SimpleKind::Float32 => "float32",
660 SimpleKind::Float64 => "float64",
661 SimpleKind::Complex64 => "complex64",
662 SimpleKind::Complex128 => "complex128",
663 SimpleKind::Rune => "rune",
664 SimpleKind::Bool => "bool",
665 SimpleKind::String => "string",
666 SimpleKind::Unit => "Unit",
667 }
668 }
669
670 pub fn from_name(name: &str) -> Option<SimpleKind> {
671 Some(match name {
672 "int" => SimpleKind::Int,
673 "int8" => SimpleKind::Int8,
674 "int16" => SimpleKind::Int16,
675 "int32" => SimpleKind::Int32,
676 "int64" => SimpleKind::Int64,
677 "uint" => SimpleKind::Uint,
678 "uint8" => SimpleKind::Uint8,
679 "uint16" => SimpleKind::Uint16,
680 "uint32" => SimpleKind::Uint32,
681 "uint64" => SimpleKind::Uint64,
682 "uintptr" => SimpleKind::Uintptr,
683 "byte" => SimpleKind::Byte,
684 "float32" => SimpleKind::Float32,
685 "float64" => SimpleKind::Float64,
686 "complex64" => SimpleKind::Complex64,
687 "complex128" => SimpleKind::Complex128,
688 "rune" => SimpleKind::Rune,
689 "bool" => SimpleKind::Bool,
690 "string" => SimpleKind::String,
691 "Unit" => SimpleKind::Unit,
692 _ => return None,
693 })
694 }
695
696 pub fn is_arithmetic(self) -> bool {
697 !matches!(
698 self,
699 SimpleKind::Bool | SimpleKind::String | SimpleKind::Unit | SimpleKind::Uintptr
700 )
701 }
702
703 pub fn is_ordered(self) -> bool {
704 self.is_arithmetic() && !matches!(self, SimpleKind::Complex64 | SimpleKind::Complex128)
705 }
706
707 pub fn is_unsigned_int(self) -> bool {
708 matches!(
709 self,
710 SimpleKind::Byte
711 | SimpleKind::Uint
712 | SimpleKind::Uint8
713 | SimpleKind::Uint16
714 | SimpleKind::Uint32
715 | SimpleKind::Uint64
716 )
717 }
718
719 pub fn is_signed_int(self) -> bool {
720 matches!(
721 self,
722 SimpleKind::Int
723 | SimpleKind::Int8
724 | SimpleKind::Int16
725 | SimpleKind::Int32
726 | SimpleKind::Int64
727 | SimpleKind::Rune
728 )
729 }
730
731 pub fn is_float(self) -> bool {
732 matches!(self, SimpleKind::Float32 | SimpleKind::Float64)
733 }
734
735 pub fn is_complex(self) -> bool {
736 matches!(self, SimpleKind::Complex64 | SimpleKind::Complex128)
737 }
738
739 pub fn numeric_family(self) -> Option<NumericFamily> {
740 if self.is_signed_int() {
741 Some(NumericFamily::SignedInt)
742 } else if self.is_unsigned_int() {
743 Some(NumericFamily::UnsignedInt)
744 } else if self.is_float() {
745 Some(NumericFamily::Float)
746 } else {
747 None
748 }
749 }
750}
751
752impl Type {
753 pub fn get_function_ret(&self) -> Option<&Type> {
754 match self {
755 Type::Function(f) => Some(&f.return_type),
756 _ => None,
757 }
758 }
759
760 pub fn is_stringer_signature(&self) -> bool {
761 let func = match self {
762 Type::Forall { body, .. } => body.as_ref(),
763 other => other,
764 };
765 matches!(
766 func,
767 Type::Function(f)
768 if f.params.len() == 1
769 && matches!(f.return_type.as_ref(), Type::Simple(SimpleKind::String))
770 )
771 }
772
773 pub fn has_name(&self, name: &str) -> bool {
774 match self {
775 Type::Nominal { id, .. } => id.last_segment() == name,
776 Type::Simple(kind) => kind.leaf_name() == name,
777 Type::Compound { kind, .. } => kind.leaf_name() == name,
778 _ => false,
779 }
780 }
781
782 pub fn get_qualified_id(&self) -> Option<&str> {
783 match self {
784 Type::Nominal { id, .. } => Some(id.as_str()),
785 _ => None,
786 }
787 }
788
789 pub fn get_underlying(&self) -> Option<&Type> {
790 match self {
791 Type::Nominal {
792 underlying_ty: underlying,
793 ..
794 } => underlying.as_deref(),
795 _ => None,
796 }
797 }
798
799 pub fn is_result(&self) -> bool {
800 self.has_qualified_id("prelude.Result")
801 }
802
803 pub fn is_option(&self) -> bool {
804 self.has_qualified_id("prelude.Option")
805 }
806
807 pub fn is_partial(&self) -> bool {
808 self.has_qualified_id("prelude.Partial")
809 }
810
811 fn has_qualified_id(&self, qualified_id: &str) -> bool {
812 matches!(self, Type::Nominal { id, .. } if id.as_str() == qualified_id)
813 }
814
815 pub fn is_unit(&self) -> bool {
816 self.is_simple(SimpleKind::Unit)
817 }
818
819 pub fn tuple_arity(&self) -> Option<usize> {
820 match self {
821 Type::Tuple(elements) => Some(elements.len()),
822 _ => None,
823 }
824 }
825
826 pub fn is_tuple(&self) -> bool {
827 matches!(self, Type::Tuple(_))
828 }
829
830 pub fn as_import_namespace(&self) -> Option<&str> {
831 match self {
832 Type::ImportNamespace(module_id) => Some(module_id),
833 _ => None,
834 }
835 }
836
837 pub fn as_compound(&self) -> Option<(CompoundKind, &[Type])> {
838 match self {
839 Type::Compound { kind, args } => Some((*kind, args.as_slice())),
840 Type::Nominal { id, params, .. } => {
841 CompoundKind::from_name(id.last_segment()).map(|k| (k, params.as_slice()))
842 }
843 _ => None,
844 }
845 }
846
847 pub fn is_native(&self, kind: CompoundKind) -> bool {
848 self.as_compound().is_some_and(|(k, _)| k == kind)
849 }
850
851 pub fn is_ref(&self) -> bool {
852 self.is_native(CompoundKind::Ref)
853 }
854
855 pub fn is_slice(&self) -> bool {
856 self.is_native(CompoundKind::Slice)
857 }
858
859 pub fn is_map(&self) -> bool {
860 self.is_native(CompoundKind::Map)
861 }
862
863 pub fn is_channel(&self) -> bool {
864 self.is_native(CompoundKind::Channel)
865 }
866
867 pub fn is_receiver_placeholder(&self) -> bool {
868 matches!(self, Type::ReceiverPlaceholder)
869 }
870
871 pub fn is_unknown(&self) -> bool {
872 self.has_name("Unknown")
873 }
874
875 pub fn resolves_to_unknown(&self) -> bool {
876 peel_alias(self, |_| true).is_unknown()
877 }
878
879 pub fn contains_unknown(&self) -> bool {
880 let peeled = peel_alias(self, |_| true);
881 if peeled.is_unknown() {
882 return true;
883 }
884 match &peeled {
885 Type::Compound { args, .. } => args.iter().any(|a| a.contains_unknown()),
886 Type::Function(f) => {
887 f.params.iter().any(|p| p.contains_unknown()) || f.return_type.contains_unknown()
888 }
889 Type::Tuple(elements) => elements.iter().any(|e| e.contains_unknown()),
890 Type::Nominal { params, .. } => params.iter().any(|p| p.contains_unknown()),
891 Type::Forall { body, .. } => body.contains_unknown(),
892 _ => false,
893 }
894 }
895
896 pub fn is_receiver(&self) -> bool {
897 self.is_native(CompoundKind::Receiver)
898 }
899
900 pub fn is_ignored(&self) -> bool {
901 matches!(self, Type::Var { id, .. } if *id == TypeVarId::IGNORED)
902 }
903
904 pub fn is_variadic(&self) -> Option<Type> {
905 let last = self.get_function_params()?.last()?;
906 match last.as_compound()? {
907 (CompoundKind::VarArgs, _) => last.inner(),
908 _ => None,
909 }
910 }
911
912 pub fn is_string(&self) -> bool {
913 self.is_simple(SimpleKind::String)
914 }
915
916 pub fn is_slice_of_simple(&self, element: SimpleKind) -> bool {
917 match self.as_compound() {
918 Some((CompoundKind::Slice, [elem])) => elem.is_simple(element),
919 _ => false,
920 }
921 }
922
923 pub fn is_slice_of(&self, element_name: &str) -> bool {
924 match self.as_compound() {
925 Some((CompoundKind::Slice, [elem])) => elem.has_name(element_name),
926 _ => false,
927 }
928 }
929
930 pub fn is_byte_slice(&self) -> bool {
931 self.is_slice_of_simple(SimpleKind::Byte) || self.is_slice_of_simple(SimpleKind::Uint8)
932 }
933
934 pub fn is_rune_slice(&self) -> bool {
935 self.is_slice_of_simple(SimpleKind::Rune)
936 }
937
938 pub fn is_byte_or_rune_slice(&self) -> bool {
939 self.is_byte_slice() || self.is_rune_slice()
940 }
941
942 pub fn has_underlying_rune(&self) -> bool {
943 self.underlying_numeric_type().is_some_and(|t| t.is_rune())
944 }
945
946 pub fn has_underlying_byte(&self) -> bool {
947 self.underlying_numeric_type()
948 .is_some_and(|t| t.is_simple(SimpleKind::Byte) || t.is_simple(SimpleKind::Uint8))
949 }
950
951 pub fn has_byte_or_rune_slice_underlying(&self) -> bool {
952 if self.is_byte_or_rune_slice() {
953 return true;
954 }
955 match self {
956 Type::Nominal { underlying_ty, .. } => underlying_ty
957 .as_deref()
958 .is_some_and(|u| u.has_byte_or_rune_slice_underlying()),
959 _ => false,
960 }
961 }
962
963 pub fn as_simple(&self) -> Option<SimpleKind> {
964 match self {
965 Type::Simple(kind) => Some(*kind),
966 Type::Nominal { id, .. } => SimpleKind::from_name(id.last_segment()),
967 _ => None,
968 }
969 }
970
971 pub fn is_simple(&self, kind: SimpleKind) -> bool {
972 self.as_simple() == Some(kind)
973 }
974
975 pub fn is_boolean(&self) -> bool {
976 self.is_simple(SimpleKind::Bool)
977 }
978
979 pub fn is_rune(&self) -> bool {
980 self.is_simple(SimpleKind::Rune)
981 }
982
983 pub fn is_float64(&self) -> bool {
984 self.is_simple(SimpleKind::Float64)
985 }
986
987 pub fn is_float32(&self) -> bool {
988 self.is_simple(SimpleKind::Float32)
989 }
990
991 pub fn is_float(&self) -> bool {
992 self.as_simple().is_some_and(SimpleKind::is_float)
993 }
994
995 pub fn is_variable(&self) -> bool {
996 matches!(self, Type::Var { .. })
997 }
998
999 pub fn is_type_var(&self) -> bool {
1000 matches!(self, Type::Var { .. })
1001 }
1002
1003 pub fn is_numeric(&self) -> bool {
1004 self.as_simple().is_some_and(SimpleKind::is_arithmetic)
1005 }
1006
1007 pub fn is_ordered(&self) -> bool {
1008 self.as_simple().is_some_and(SimpleKind::is_ordered)
1009 }
1010
1011 pub fn satisfies_ordered_constraint(&self) -> bool {
1013 if let Some(kind) = self.as_simple() {
1014 return matches!(
1015 kind,
1016 SimpleKind::Int
1017 | SimpleKind::Int8
1018 | SimpleKind::Int16
1019 | SimpleKind::Int32
1020 | SimpleKind::Int64
1021 | SimpleKind::Uint
1022 | SimpleKind::Uint8
1023 | SimpleKind::Uint16
1024 | SimpleKind::Uint32
1025 | SimpleKind::Uint64
1026 | SimpleKind::Uintptr
1027 | SimpleKind::Byte
1028 | SimpleKind::Rune
1029 | SimpleKind::Float32
1030 | SimpleKind::Float64
1031 | SimpleKind::String
1032 );
1033 }
1034 match self {
1035 Type::Nominal { underlying_ty, .. } => underlying_ty
1036 .as_deref()
1037 .is_some_and(Type::satisfies_ordered_constraint),
1038 Type::Parameter(_) => true,
1039 _ => false,
1040 }
1041 }
1042
1043 pub fn is_complex(&self) -> bool {
1044 self.as_simple().is_some_and(SimpleKind::is_complex)
1045 }
1046
1047 pub fn is_unsigned_int(&self) -> bool {
1048 self.as_simple().is_some_and(SimpleKind::is_unsigned_int)
1049 }
1050
1051 pub fn is_never(&self) -> bool {
1052 matches!(self, Type::Never)
1053 }
1054
1055 pub fn is_error(&self) -> bool {
1056 matches!(self, Type::Error)
1057 }
1058
1059 pub fn contains_error(&self) -> bool {
1060 match self {
1061 Type::Error => true,
1062 Type::Nominal {
1063 params,
1064 underlying_ty,
1065 ..
1066 } => {
1067 params.iter().any(Type::contains_error)
1068 || underlying_ty.as_deref().is_some_and(Type::contains_error)
1069 }
1070 Type::Compound { args, .. } => args.iter().any(Type::contains_error),
1071 Type::Function(f) => {
1072 f.params.iter().any(Type::contains_error) || f.return_type.contains_error()
1073 }
1074 Type::Tuple(elements) => elements.iter().any(Type::contains_error),
1075 Type::Forall { body, .. } => body.contains_error(),
1076 _ => false,
1077 }
1078 }
1079
1080 pub fn has_unbound_variables(&self) -> bool {
1081 match self {
1082 Type::Var { hint, .. } => hint.is_some(),
1083 Type::Nominal { params, .. } => params.iter().any(|p| p.has_unbound_variables()),
1084 Type::Function(f) => {
1085 f.params.iter().any(|p| p.has_unbound_variables())
1086 || f.return_type.has_unbound_variables()
1087 }
1088 Type::Forall { body, .. } => body.has_unbound_variables(),
1089 Type::Tuple(elements) => elements.iter().any(|e| e.has_unbound_variables()),
1090 Type::Compound { args, .. } => args.iter().any(|a| a.has_unbound_variables()),
1091 Type::Simple(_)
1092 | Type::Parameter(_)
1093 | Type::Never
1094 | Type::Error
1095 | Type::ImportNamespace(_)
1096 | Type::ReceiverPlaceholder => false,
1097 }
1098 }
1099
1100 pub fn remove_found_type_names(&self, names: &mut HashSet<EcoString>) {
1101 if names.is_empty() {
1102 return;
1103 }
1104
1105 match self {
1106 Type::Nominal { id, params, .. } => {
1107 names.remove(id.last_segment());
1108 for param in params {
1109 param.remove_found_type_names(names);
1110 }
1111 }
1112 Type::Function(f) => {
1113 for param in &f.params {
1114 param.remove_found_type_names(names);
1115 }
1116 f.return_type.remove_found_type_names(names);
1117 for bound in &f.bounds {
1118 bound.generic.remove_found_type_names(names);
1119 bound.ty.remove_found_type_names(names);
1120 }
1121 }
1122 Type::Forall { body, .. } => {
1123 body.remove_found_type_names(names);
1124 }
1125 Type::Var { .. } => {}
1126 Type::Parameter(name) => {
1127 names.remove(name);
1128 }
1129 Type::Tuple(elements) => {
1130 for element in elements {
1131 element.remove_found_type_names(names);
1132 }
1133 }
1134 Type::Compound { kind, args } => {
1135 names.remove(kind.leaf_name());
1136 for arg in args {
1137 arg.remove_found_type_names(names);
1138 }
1139 }
1140 Type::Simple(kind) => {
1141 names.remove(kind.leaf_name());
1142 }
1143 Type::Never | Type::Error | Type::ImportNamespace(_) | Type::ReceiverPlaceholder => {}
1144 }
1145 }
1146}
1147
1148impl Type {
1149 pub fn get_name(&self) -> Option<&str> {
1150 match self {
1151 Type::Simple(kind) => Some(kind.leaf_name()),
1152 Type::Compound { kind, args } => match kind {
1153 CompoundKind::Ref => args.first().and_then(|inner| inner.get_name()),
1154 _ => Some(kind.leaf_name()),
1155 },
1156 Type::Nominal { id, params, .. } => {
1157 let name = id.last_segment();
1158 if CompoundKind::from_name(name) == Some(CompoundKind::Ref) {
1159 return params.first().and_then(|inner| inner.get_name());
1160 }
1161 Some(name)
1162 }
1163 Type::ImportNamespace(module_id) => {
1164 let path = module_id.strip_prefix("go:").unwrap_or(module_id);
1165 path.rsplit('/').next()
1166 }
1167 _ => None,
1168 }
1169 }
1170
1171 pub fn wraps(&self, name: &str, inner: &Type) -> bool {
1172 self.get_name().is_some_and(|n| n == name)
1173 && self
1174 .get_type_params()
1175 .and_then(|p| p.first())
1176 .is_some_and(|first| *first == *inner)
1177 }
1178
1179 pub fn get_function_params(&self) -> Option<&[Type]> {
1180 match self {
1181 Type::Function(f) => Some(&f.params),
1182 Type::Nominal {
1183 underlying_ty: Some(inner),
1184 ..
1185 } => inner.get_function_params(),
1186 _ => None,
1187 }
1188 }
1189
1190 pub fn param_count(&self) -> usize {
1191 match self {
1192 Type::Function(f) => f.params.len(),
1193 _ => 0,
1194 }
1195 }
1196
1197 pub fn get_param_mutability(&self) -> &[bool] {
1198 match self {
1199 Type::Function(f) => &f.param_mutability,
1200 _ => &[],
1201 }
1202 }
1203
1204 pub fn with_replaced_first_param(&self, new_first: &Type) -> Type {
1205 match self {
1206 Type::Function(f) => {
1207 if f.params.is_empty() {
1208 return self.clone();
1209 }
1210 let mut new_params = f.params.clone();
1211 new_params[0] = new_first.clone();
1212 Type::function(
1213 new_params,
1214 f.param_mutability.clone(),
1215 f.bounds.clone(),
1216 f.return_type.clone(),
1217 )
1218 }
1219 Type::Forall { vars, body } => Type::Forall {
1220 vars: vars.clone(),
1221 body: Box::new(body.with_replaced_first_param(new_first)),
1222 },
1223 _ => self.clone(),
1224 }
1225 }
1226
1227 pub fn get_bounds(&self) -> &[Bound] {
1228 match self {
1229 Type::Function(f) => &f.bounds,
1230 Type::Forall { body, .. } => body.get_bounds(),
1231 _ => &[],
1232 }
1233 }
1234
1235 pub fn get_qualified_name(&self) -> Symbol {
1236 match self.strip_refs() {
1237 Type::Nominal { id, .. } => id,
1238 Type::Simple(kind) => Symbol::from_parts("prelude", kind.leaf_name()),
1239 Type::Compound { kind, .. } => Symbol::from_parts("prelude", kind.leaf_name()),
1240 _ => panic!("called get_qualified_name on {:#?}", self),
1241 }
1242 }
1243
1244 pub fn inner(&self) -> Option<Type> {
1245 self.get_type_params()
1246 .and_then(|args| args.first().cloned())
1247 }
1248
1249 pub fn ok_type(&self) -> Type {
1250 debug_assert!(
1251 self.is_result() || self.is_option() || self.is_partial(),
1252 "ok_type called on non-Result/Option/Partial type"
1253 );
1254 self.inner()
1255 .expect("Result/Option/Partial should have inner type")
1256 }
1257
1258 pub fn err_type(&self) -> Type {
1259 debug_assert!(
1260 self.is_result() || self.is_partial(),
1261 "err_type called on non-Result/Partial type"
1262 );
1263 self.get_type_params()
1264 .and_then(|args| args.get(1).cloned())
1265 .expect("Result/Partial should have error type")
1266 }
1267}
1268
1269pub fn peel_alias<F>(ty: &Type, is_alias: F) -> Type
1272where
1273 F: Fn(&str) -> bool,
1274{
1275 let mut current = ty.unwrap_forall().clone();
1276 let mut seen: Vec<String> = Vec::new();
1277 while let Type::Nominal {
1278 id,
1279 underlying_ty: Some(u),
1280 ..
1281 } = ¤t
1282 {
1283 if !is_alias(id.as_str()) {
1284 break;
1285 }
1286 if seen.iter().any(|s| s == id.as_str()) {
1287 break;
1288 }
1289 seen.push(id.to_string());
1290 current = u.unwrap_forall().clone();
1291 }
1292 current
1293}
1294
1295pub fn peel_alias_id<F>(id: &str, next_alias: F) -> String
1298where
1299 F: Fn(&str) -> Option<String>,
1300{
1301 let mut current = id.to_string();
1302 let mut seen: Vec<String> = Vec::new();
1303 loop {
1304 if seen.iter().any(|s| s == ¤t) {
1305 return current;
1306 }
1307 let Some(next) = next_alias(¤t) else {
1308 return current;
1309 };
1310 seen.push(current);
1311 current = next;
1312 }
1313}
1314
1315impl Type {
1316 pub fn unwrap_forall(&self) -> &Type {
1317 match self {
1318 Type::Forall { body, .. } => body.as_ref(),
1319 other => other,
1320 }
1321 }
1322
1323 pub fn strip_refs(&self) -> Type {
1324 if self.is_ref() {
1325 return self.inner().expect("ref type must have inner").strip_refs();
1326 }
1327
1328 self.clone()
1329 }
1330
1331 pub fn with_receiver_placeholder(self) -> Type {
1332 match self {
1333 Type::Function(f) => {
1334 let f = Arc::try_unwrap(f).unwrap_or_else(|arc| (*arc).clone());
1335 let mut new_params = vec![Type::ReceiverPlaceholder];
1336 new_params.extend(f.params);
1337
1338 let mut new_mutability = vec![false];
1339 new_mutability.extend(f.param_mutability);
1340
1341 Type::function(new_params, new_mutability, f.bounds, f.return_type)
1342 }
1343 _ => unreachable!(
1344 "with_receiver_placeholder called on non-function type: {:?}",
1345 self
1346 ),
1347 }
1348 }
1349
1350 pub fn remove_vars(types: &[&Type]) -> (Vec<Type>, Vec<EcoString>) {
1351 let mut vars = HashMap::default();
1352 let types = types
1353 .iter()
1354 .map(|v| Self::remove_vars_impl(v, &mut vars))
1355 .collect();
1356
1357 (types, vars.into_values().collect())
1358 }
1359
1360 fn remove_vars_impl(ty: &Type, vars: &mut HashMap<u32, EcoString>) -> Type {
1361 match ty {
1362 Type::Nominal {
1363 id: name,
1364 params: args,
1365 underlying_ty: underlying,
1366 } => Type::Nominal {
1367 id: name.clone(),
1368 params: args
1369 .iter()
1370 .map(|a| Self::remove_vars_impl(a, vars))
1371 .collect(),
1372 underlying_ty: underlying
1373 .as_ref()
1374 .map(|u| Box::new(Self::remove_vars_impl(u, vars))),
1375 },
1376
1377 Type::Function(f) => Type::function(
1378 f.params
1379 .iter()
1380 .map(|a| Self::remove_vars_impl(a, vars))
1381 .collect(),
1382 f.param_mutability.clone(),
1383 f.bounds
1384 .iter()
1385 .map(|b| Bound {
1386 param_name: b.param_name.clone(),
1387 generic: Self::remove_vars_impl(&b.generic, vars),
1388 ty: Self::remove_vars_impl(&b.ty, vars),
1389 })
1390 .collect(),
1391 Self::remove_vars_impl(&f.return_type, vars).into(),
1392 ),
1393
1394 Type::Var { id, hint } => match vars.get(&id.0) {
1395 Some(g) => Type::Parameter(g.clone()),
1396 None => {
1397 let name: EcoString = hint
1398 .clone()
1399 .unwrap_or_else(|| alpha_index(vars.len()).into());
1400
1401 vars.insert(id.0, name.clone());
1402 Type::Parameter(name)
1403 }
1404 },
1405
1406 Type::Forall { body, .. } => Self::remove_vars_impl(body, vars),
1407 Type::Tuple(elements) => Type::Tuple(
1408 elements
1409 .iter()
1410 .map(|e| Self::remove_vars_impl(e, vars))
1411 .collect(),
1412 ),
1413 Type::Compound { kind, args } => Type::Compound {
1414 kind: *kind,
1415 args: args
1416 .iter()
1417 .map(|a| Self::remove_vars_impl(a, vars))
1418 .collect(),
1419 },
1420 Type::Simple(_) | Type::Parameter(_) => ty.clone(),
1421 Type::Never | Type::Error | Type::ImportNamespace(_) | Type::ReceiverPlaceholder => {
1422 ty.clone()
1423 }
1424 }
1425 }
1426
1427 pub fn contains_type(&self, target: &Type) -> bool {
1428 if *self == *target {
1429 return true;
1430 }
1431 match self {
1432 Type::Nominal { params, .. } => params.iter().any(|p| p.contains_type(target)),
1433 Type::Function(f) => {
1434 f.params.iter().any(|p| p.contains_type(target))
1435 || f.return_type.contains_type(target)
1436 }
1437 Type::Var { .. } => false,
1438 Type::Forall { body, .. } => body.contains_type(target),
1439 Type::Tuple(elements) => elements.iter().any(|e| e.contains_type(target)),
1440 Type::Compound { args, .. } => args.iter().any(|a| a.contains_type(target)),
1441 Type::Simple(_)
1442 | Type::Parameter(_)
1443 | Type::Never
1444 | Type::Error
1445 | Type::ImportNamespace(_)
1446 | Type::ReceiverPlaceholder => false,
1447 }
1448 }
1449}
1450
1451impl Type {
1452 pub fn underlying_numeric_type(&self) -> Option<Type> {
1453 self.underlying_numeric_type_recursive(&mut HashSet::default())
1454 }
1455
1456 pub fn has_underlying_numeric_type(&self) -> bool {
1457 self.underlying_numeric_type().is_some()
1458 }
1459
1460 pub fn literal_adaptation_target(&self) -> Option<Type> {
1461 if let Some(numeric) = self.underlying_numeric_type() {
1462 return Some(numeric);
1463 }
1464 match self {
1465 Type::Nominal { .. } if self.underlying_simple_kind() == Some(SimpleKind::Uintptr) => {
1466 Some(Type::Simple(SimpleKind::Uintptr))
1467 }
1468 _ => None,
1469 }
1470 }
1471
1472 pub fn underlying_simple_kind(&self) -> Option<SimpleKind> {
1473 self.underlying_simple_kind_recursive(&mut HashSet::default())
1474 }
1475
1476 fn underlying_simple_kind_recursive(
1477 &self,
1478 visited: &mut HashSet<Symbol>,
1479 ) -> Option<SimpleKind> {
1480 if let Some(kind) = self.as_simple() {
1481 return Some(kind);
1482 }
1483 match self {
1484 Type::Nominal {
1485 id,
1486 underlying_ty: Some(underlying),
1487 ..
1488 } => {
1489 if !visited.insert(id.clone()) {
1490 return None;
1491 }
1492 underlying.underlying_simple_kind_recursive(visited)
1493 }
1494 _ => None,
1495 }
1496 }
1497
1498 fn underlying_numeric_type_recursive(&self, visited: &mut HashSet<Symbol>) -> Option<Type> {
1499 match self {
1500 Type::Simple(_) if self.is_numeric() => Some(self.clone()),
1501 Type::Nominal {
1502 id,
1503 underlying_ty: underlying,
1504 ..
1505 } => {
1506 if self.is_numeric() {
1507 return Some(self.clone());
1508 }
1509
1510 if !visited.insert(id.clone()) {
1511 return None;
1512 }
1513
1514 underlying
1515 .as_ref()?
1516 .underlying_numeric_type_recursive(visited)
1517 }
1518 _ => None,
1519 }
1520 }
1521
1522 pub fn numeric_family(&self) -> Option<NumericFamily> {
1523 self.as_simple()?.numeric_family()
1524 }
1525
1526 pub fn is_numeric_compatible_with(&self, other: &Type) -> bool {
1527 let self_underlying_ty = self.underlying_numeric_type();
1528 let other_underlying_ty = other.underlying_numeric_type();
1529
1530 match (self_underlying_ty, other_underlying_ty) {
1531 (Some(s), Some(o)) => s.numeric_family() == o.numeric_family(),
1532 _ => false,
1533 }
1534 }
1535
1536 pub fn is_aliased_numeric_type(&self) -> bool {
1537 match self {
1538 Type::Nominal { underlying_ty, .. } => {
1539 underlying_ty.is_some() && !self.is_numeric() && self.has_underlying_numeric_type()
1540 }
1541 _ => false,
1542 }
1543 }
1544}
1545
1546fn alpha_index(idx: usize) -> String {
1548 let mut s = String::new();
1549 let mut n = idx + 1;
1550 while n > 0 {
1551 n -= 1;
1552 s.insert(0, (b'A' + (n % 26) as u8) as char);
1553 n /= 26;
1554 }
1555 s
1556}
1557
1558#[cfg(test)]
1559mod tests {
1560 use super::*;
1561
1562 #[test]
1563 fn alpha_index_single() {
1564 assert_eq!(alpha_index(0), "A");
1565 assert_eq!(alpha_index(5), "F");
1566 assert_eq!(alpha_index(25), "Z");
1567 }
1568
1569 #[test]
1570 fn alpha_index_double() {
1571 assert_eq!(alpha_index(26), "AA");
1572 assert_eq!(alpha_index(27), "AB");
1573 assert_eq!(alpha_index(51), "AZ");
1574 assert_eq!(alpha_index(52), "BA");
1575 assert_eq!(alpha_index(701), "ZZ");
1576 }
1577
1578 #[test]
1579 fn alpha_index_triple() {
1580 assert_eq!(alpha_index(702), "AAA");
1581 }
1582
1583 fn unhinted_var(id: u32) -> Type {
1584 Type::Var {
1585 id: TypeVarId(id),
1586 hint: None,
1587 }
1588 }
1589
1590 #[test]
1591 fn remove_vars_handles_more_than_six_unhinted_vars() {
1592 let func = Type::function(
1593 (0..6).map(unhinted_var).collect(),
1594 vec![false; 6],
1595 vec![],
1596 Box::new(unhinted_var(6)),
1597 );
1598
1599 let (resolved, generics) = Type::remove_vars(&[&func]);
1600
1601 assert_eq!(generics.len(), 7);
1602 let Type::Function(f) = &resolved[0] else {
1603 panic!("expected function type");
1604 };
1605 let names: Vec<_> = f
1606 .params
1607 .iter()
1608 .chain(std::iter::once(f.return_type.as_ref()))
1609 .map(|p| match p {
1610 Type::Parameter(name) => name.to_string(),
1611 other => panic!("expected parameter, got {:?}", other),
1612 })
1613 .collect();
1614 assert_eq!(names, vec!["A", "B", "C", "D", "E", "F", "G"]);
1615 }
1616
1617 #[test]
1618 fn remove_vars_handles_dozens_of_unhinted_vars() {
1619 let params: Vec<Type> = (0..30).map(unhinted_var).collect();
1620 let func = Type::function(
1621 params.clone(),
1622 vec![false; params.len()],
1623 vec![],
1624 Box::new(Type::Simple(SimpleKind::Unit)),
1625 );
1626 let (_, generics) = Type::remove_vars(&[&func]);
1627 assert_eq!(generics.len(), 30);
1628 }
1629}