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