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