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