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