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