1use rustc_hash::{FxHashMap as HashMap, FxHashSet as HashSet};
2use std::cell::{OnceCell, RefCell};
3use std::rc::Rc;
4
5use ecow::EcoString;
6
7pub fn unqualified_name(id: &str) -> &str {
11 id.rsplit('.').next().unwrap_or(id)
12}
13
14pub type SubstitutionMap = HashMap<EcoString, Type>;
16
17pub fn substitute(ty: &Type, map: &HashMap<EcoString, Type>) -> Type {
18 if map.is_empty() {
19 return ty.clone();
20 }
21 match ty {
22 Type::Parameter(name) => map.get(name).cloned().unwrap_or_else(|| ty.clone()),
23 Type::Constructor {
24 id,
25 params,
26 underlying_ty: underlying,
27 } => Type::Constructor {
28 id: id.clone(),
29 params: params.iter().map(|p| substitute(p, map)).collect(),
30 underlying_ty: underlying.as_ref().map(|u| Box::new(substitute(u, map))),
31 },
32 Type::Function {
33 params,
34 param_mutability,
35 bounds,
36 return_type,
37 } => Type::Function {
38 params: params.iter().map(|p| substitute(p, map)).collect(),
39 param_mutability: param_mutability.clone(),
40 bounds: bounds
41 .iter()
42 .map(|b| Bound {
43 param_name: b.param_name.clone(),
44 generic: substitute(&b.generic, map),
45 ty: substitute(&b.ty, map),
46 })
47 .collect(),
48 return_type: Box::new(substitute(return_type, map)),
49 },
50 Type::Variable(_) | Type::Error => ty.clone(),
51 Type::Forall { vars, body } => {
52 let has_overlap = map.keys().any(|k| vars.contains(k));
53 let substituted_body = if has_overlap {
54 let filtered_map: HashMap<EcoString, Type> = map
55 .iter()
56 .filter(|(k, _)| !vars.contains(*k))
57 .map(|(k, v)| (k.clone(), v.clone()))
58 .collect();
59 substitute(body, &filtered_map)
60 } else {
61 substitute(body, map)
62 };
63 Type::Forall {
64 vars: vars.clone(),
65 body: Box::new(substituted_body),
66 }
67 }
68 Type::Tuple(elements) => Type::Tuple(elements.iter().map(|e| substitute(e, map)).collect()),
69 Type::Never => ty.clone(),
70 }
71}
72
73#[derive(Debug, Clone, PartialEq)]
74pub struct Bound {
75 pub param_name: EcoString,
76 pub generic: Type,
77 pub ty: Type,
78}
79
80#[derive(Clone)]
81pub enum TypeVariableState {
82 Unbound { id: i32, hint: Option<EcoString> },
83 Link(Type),
84}
85
86impl TypeVariableState {
87 pub fn is_unbound(&self) -> bool {
88 matches!(self, TypeVariableState::Unbound { .. })
89 }
90}
91
92impl std::fmt::Debug for TypeVariableState {
93 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
94 match self {
95 TypeVariableState::Unbound { id, hint } => match hint {
96 Some(name) => write!(f, "{}", name),
97 None => write!(f, "{}", id),
98 },
99 TypeVariableState::Link(ty) => write!(f, "{:?}", ty),
100 }
101 }
102}
103
104impl PartialEq for TypeVariableState {
105 fn eq(&self, other: &Self) -> bool {
106 match (self, other) {
107 (
108 TypeVariableState::Unbound { id: id1, .. },
109 TypeVariableState::Unbound { id: id2, .. },
110 ) => id1 == id2,
111 (TypeVariableState::Link(ty1), TypeVariableState::Link(ty2)) => ty1 == ty2,
112 _ => false,
113 }
114 }
115}
116
117#[derive(Clone)]
118pub enum Type {
119 Constructor {
120 id: EcoString,
121 params: Vec<Type>,
122 underlying_ty: Option<Box<Type>>,
123 },
124
125 Function {
126 params: Vec<Type>,
127 param_mutability: Vec<bool>,
128 bounds: Vec<Bound>,
129 return_type: Box<Type>,
130 },
131
132 Variable(Rc<RefCell<TypeVariableState>>),
133
134 Forall {
135 vars: Vec<EcoString>,
136 body: Box<Type>,
137 },
138
139 Parameter(EcoString),
140
141 Never,
142
143 Tuple(Vec<Type>),
144
145 Error,
148}
149
150impl std::fmt::Debug for Type {
151 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
152 match self {
153 Type::Constructor { id, params, .. } => f
154 .debug_struct("Constructor")
155 .field("id", id)
156 .field("params", params)
157 .finish(),
158 Type::Function {
159 params,
160 param_mutability,
161 bounds,
162 return_type,
163 } => {
164 let mut s = f.debug_struct("Function");
165 s.field("params", params);
166 if param_mutability.iter().any(|m| *m) {
167 s.field("param_mutability", param_mutability);
168 }
169 s.field("bounds", bounds)
170 .field("return_type", return_type)
171 .finish()
172 }
173 Type::Variable(type_var) => f
174 .debug_tuple("Variable")
175 .field(&*type_var.borrow())
176 .finish(),
177 Type::Forall { vars, body } => f
178 .debug_struct("Forall")
179 .field("vars", vars)
180 .field("body", body)
181 .finish(),
182 Type::Parameter(name) => f.debug_tuple("Parameter").field(name).finish(),
183 Type::Never => write!(f, "Never"),
184 Type::Tuple(elements) => f.debug_tuple("Tuple").field(elements).finish(),
185 Type::Error => write!(f, "Error"),
186 }
187 }
188}
189
190impl PartialEq for Type {
191 fn eq(&self, other: &Self) -> bool {
192 match (self, other) {
193 (
194 Type::Constructor {
195 id: id1,
196 params: params1,
197 ..
198 },
199 Type::Constructor {
200 id: id2,
201 params: params2,
202 ..
203 },
204 ) => id1 == id2 && params1 == params2,
205 (
206 Type::Function {
207 params: p1,
208 param_mutability: m1,
209 bounds: b1,
210 return_type: r1,
211 },
212 Type::Function {
213 params: p2,
214 param_mutability: m2,
215 bounds: b2,
216 return_type: r2,
217 },
218 ) => p1 == p2 && m1 == m2 && b1 == b2 && r1 == r2,
219 (Type::Variable(v1), Type::Variable(v2)) => {
220 Rc::ptr_eq(v1, v2) || *v1.borrow() == *v2.borrow()
221 }
222 (
223 Type::Forall {
224 vars: vars1,
225 body: body1,
226 },
227 Type::Forall {
228 vars: vars2,
229 body: body2,
230 },
231 ) => vars1 == vars2 && body1 == body2,
232 (Type::Parameter(name1), Type::Parameter(name2)) => name1 == name2,
233 (Type::Never, Type::Never) => true,
234 (Type::Tuple(elems1), Type::Tuple(elems2)) => elems1 == elems2,
235 _ => false,
236 }
237 }
238}
239
240thread_local! {
241 static INTERNED_INT: OnceCell<Type> = const { OnceCell::new() };
242 static INTERNED_STRING: OnceCell<Type> = const { OnceCell::new() };
243 static INTERNED_BOOL: OnceCell<Type> = const { OnceCell::new() };
244 static INTERNED_UNIT: OnceCell<Type> = const { OnceCell::new() };
245 static INTERNED_FLOAT64: OnceCell<Type> = const { OnceCell::new() };
246 static INTERNED_RUNE: OnceCell<Type> = const { OnceCell::new() };
247}
248
249impl Type {
250 pub fn int() -> Type {
251 INTERNED_INT.with(|cell| cell.get_or_init(|| Self::nominal("int")).clone())
252 }
253
254 pub fn string() -> Type {
255 INTERNED_STRING.with(|cell| cell.get_or_init(|| Self::nominal("string")).clone())
256 }
257
258 pub fn bool() -> Type {
259 INTERNED_BOOL.with(|cell| cell.get_or_init(|| Self::nominal("bool")).clone())
260 }
261
262 pub fn unit() -> Type {
263 INTERNED_UNIT.with(|cell| cell.get_or_init(|| Self::nominal("Unit")).clone())
264 }
265
266 pub fn float64() -> Type {
267 INTERNED_FLOAT64.with(|cell| cell.get_or_init(|| Self::nominal("float64")).clone())
268 }
269
270 pub fn rune() -> Type {
271 INTERNED_RUNE.with(|cell| cell.get_or_init(|| Self::nominal("rune")).clone())
272 }
273}
274
275impl Type {
276 const UNINFERRED_ID: i32 = -1;
277 const IGNORED_ID: i32 = -333;
278
279 pub fn nominal(name: &str) -> Self {
280 Self::Constructor {
281 id: format!("**nominal.{}", name).into(),
282 params: vec![],
283 underlying_ty: None,
284 }
285 }
286
287 pub fn uninferred() -> Self {
288 Self::Variable(Rc::new(RefCell::new(TypeVariableState::Unbound {
289 id: Self::UNINFERRED_ID,
290 hint: None,
291 })))
292 }
293
294 pub fn ignored() -> Self {
295 Self::Variable(Rc::new(RefCell::new(TypeVariableState::Unbound {
296 id: Self::IGNORED_ID,
297 hint: None,
298 })))
299 }
300
301 pub fn get_type_params(&self) -> Option<&[Type]> {
302 match self {
303 Type::Constructor { params, .. } => Some(params),
304 _ => None,
305 }
306 }
307}
308
309const ARITHMETIC_TYPES: &[&str] = &[
310 "byte",
311 "complex128",
312 "complex64",
313 "float32",
314 "float64",
315 "int",
316 "int16",
317 "int32",
318 "int64",
319 "int8",
320 "rune",
321 "uint",
322 "uint16",
323 "uint32",
324 "uint64",
325 "uint8",
326];
327
328const ORDERED_TYPES: &[&str] = &[
329 "byte", "float32", "float64", "int", "int16", "int32", "int64", "int8", "rune", "uint",
330 "uint16", "uint32", "uint64", "uint8",
331];
332
333const UNSIGNED_INT_TYPES: &[&str] = &["byte", "uint", "uint8", "uint16", "uint32", "uint64"];
334
335impl Type {
336 pub fn get_function_ret(&self) -> Option<&Type> {
337 match self {
338 Type::Function { return_type, .. } => Some(return_type),
339 _ => None,
340 }
341 }
342
343 pub fn has_name(&self, name: &str) -> bool {
344 match self {
345 Type::Constructor { id, .. } => unqualified_name(id) == name,
346 _ => false,
347 }
348 }
349
350 pub fn get_qualified_id(&self) -> Option<&str> {
351 match self {
352 Type::Constructor { id, .. } => Some(id.as_str()),
353 _ => None,
354 }
355 }
356
357 pub fn get_underlying(&self) -> Option<&Type> {
358 match self {
359 Type::Constructor {
360 underlying_ty: underlying,
361 ..
362 } => underlying.as_deref(),
363 _ => None,
364 }
365 }
366
367 pub fn is_result(&self) -> bool {
368 self.has_name("Result")
369 }
370
371 pub fn is_option(&self) -> bool {
372 self.has_name("Option")
373 }
374
375 pub fn is_unit(&self) -> bool {
376 matches!(self.resolve(), Type::Constructor { ref id, .. } if id.as_ref() == "**nominal.Unit")
377 }
378
379 pub fn tuple_arity(&self) -> Option<usize> {
380 match self {
381 Type::Tuple(elements) => Some(elements.len()),
382 _ => None,
383 }
384 }
385
386 pub fn is_tuple(&self) -> bool {
387 matches!(self, Type::Tuple(_))
388 }
389
390 pub fn is_ref(&self) -> bool {
391 self.has_name("Ref")
392 }
393
394 pub fn is_receiver_placeholder(&self) -> bool {
395 self.has_name("__receiver__")
396 }
397
398 pub fn is_unknown(&self) -> bool {
399 self.has_name("Unknown")
400 }
401
402 pub fn is_receiver(&self) -> bool {
403 self.has_name("Receiver")
404 }
405
406 pub fn is_ignored(&self) -> bool {
407 match self {
408 Type::Variable(var) => {
409 matches!(&*var.borrow(), TypeVariableState::Unbound { id, .. } if *id == Self::IGNORED_ID)
410 }
411 _ => false,
412 }
413 }
414
415 pub fn is_variadic(&self) -> Option<Type> {
416 let args = self.get_function_params()?;
417 let last = args.last()?;
418
419 if last.get_name()? == "VarArgs" {
420 return last.inner();
421 }
422
423 None
424 }
425
426 pub fn is_string(&self) -> bool {
427 self.has_name("string")
428 }
429
430 pub fn is_slice_of(&self, element_name: &str) -> bool {
431 match self {
432 Type::Constructor { id, params, .. } => {
433 if unqualified_name(id) != "Slice" || params.len() != 1 {
434 return false;
435 }
436 params[0].resolve().has_name(element_name)
437 }
438 _ => false,
439 }
440 }
441
442 pub fn is_byte_slice(&self) -> bool {
443 self.is_slice_of("byte") || self.is_slice_of("uint8")
444 }
445
446 pub fn is_rune_slice(&self) -> bool {
447 self.is_slice_of("rune")
448 }
449
450 pub fn is_byte_or_rune_slice(&self) -> bool {
451 self.is_byte_slice() || self.is_rune_slice()
452 }
453
454 pub fn has_byte_or_rune_slice_underlying(&self) -> bool {
455 if self.is_byte_or_rune_slice() {
456 return true;
457 }
458 match self {
459 Type::Constructor { underlying_ty, .. } => underlying_ty
460 .as_deref()
461 .is_some_and(|u| u.has_byte_or_rune_slice_underlying()),
462 _ => false,
463 }
464 }
465
466 pub fn is_boolean(&self) -> bool {
467 self.has_name("bool")
468 }
469
470 pub fn is_rune(&self) -> bool {
471 self.has_name("rune")
472 }
473
474 pub fn is_float64(&self) -> bool {
475 self.has_name("float64")
476 }
477
478 pub fn is_float32(&self) -> bool {
479 self.has_name("float32")
480 }
481
482 pub fn is_float(&self) -> bool {
483 self.is_float64() || self.is_float32()
484 }
485
486 pub fn is_variable(&self) -> bool {
487 matches!(self, Type::Variable(_))
488 }
489
490 pub fn is_unbound_variable(&self) -> bool {
491 matches!(self, Type::Variable(cell) if cell.borrow().is_unbound())
492 }
493
494 pub fn is_numeric(&self) -> bool {
495 match self {
496 Type::Constructor { id, .. } => ARITHMETIC_TYPES.contains(&unqualified_name(id)),
497 _ => false,
498 }
499 }
500
501 pub fn is_ordered(&self) -> bool {
502 match self {
503 Type::Constructor { id, .. } => ORDERED_TYPES.contains(&unqualified_name(id)),
504 _ => false,
505 }
506 }
507
508 pub fn is_complex(&self) -> bool {
509 match self {
510 Type::Constructor { id, .. } => {
511 matches!(unqualified_name(id), "complex128" | "complex64")
512 }
513 _ => false,
514 }
515 }
516
517 pub fn is_unsigned_int(&self) -> bool {
518 match self {
519 Type::Constructor { id, .. } => UNSIGNED_INT_TYPES.contains(&unqualified_name(id)),
520 _ => false,
521 }
522 }
523
524 pub fn is_never(&self) -> bool {
525 matches!(self.shallow_resolve(), Type::Never)
526 }
527
528 pub fn is_error(&self) -> bool {
529 matches!(self.shallow_resolve(), Type::Error)
530 }
531
532 pub fn has_unbound_variables(&self) -> bool {
533 match self {
534 Type::Variable(type_var) => match &*type_var.borrow() {
535 TypeVariableState::Unbound { hint, .. } => hint.is_some(),
536 TypeVariableState::Link(ty) => ty.has_unbound_variables(),
537 },
538 Type::Constructor { params, .. } => params.iter().any(|p| p.has_unbound_variables()),
539 Type::Function {
540 params,
541 return_type,
542 ..
543 } => {
544 params.iter().any(|p| p.has_unbound_variables())
545 || return_type.has_unbound_variables()
546 }
547 Type::Forall { body, .. } => body.has_unbound_variables(),
548 Type::Tuple(elements) => elements.iter().any(|e| e.has_unbound_variables()),
549 Type::Parameter(_) | Type::Never | Type::Error => false,
550 }
551 }
552
553 pub fn remove_found_type_names(&self, names: &mut HashSet<EcoString>) {
554 if names.is_empty() {
555 return;
556 }
557
558 match self {
559 Type::Constructor { id, params, .. } => {
560 names.remove(unqualified_name(id));
561 for param in params {
562 param.remove_found_type_names(names);
563 }
564 }
565 Type::Function {
566 params,
567 return_type,
568 bounds,
569 ..
570 } => {
571 for param in params {
572 param.remove_found_type_names(names);
573 }
574 return_type.remove_found_type_names(names);
575 for bound in bounds {
576 bound.generic.remove_found_type_names(names);
577 bound.ty.remove_found_type_names(names);
578 }
579 }
580 Type::Forall { body, .. } => {
581 body.remove_found_type_names(names);
582 }
583 Type::Variable(type_var) => {
584 if let TypeVariableState::Link(ty) = &*type_var.borrow() {
585 ty.remove_found_type_names(names);
586 }
587 }
588 Type::Parameter(name) => {
589 names.remove(name);
590 }
591 Type::Tuple(elements) => {
592 for element in elements {
593 element.remove_found_type_names(names);
594 }
595 }
596 Type::Never | Type::Error => {}
597 }
598 }
599}
600
601impl Type {
602 pub fn get_name(&self) -> Option<&str> {
603 match self {
604 Type::Constructor { id, params, .. } => {
605 let name = unqualified_name(id);
606 if name == "Ref" {
607 return params.first().and_then(|inner| inner.get_name());
608 }
609 if let Some(module_path) = id.strip_prefix("@import/") {
610 let path = module_path.strip_prefix("go:").unwrap_or(module_path);
611 return path.rsplit('/').next();
612 }
613 Some(name)
614 }
615 _ => None,
616 }
617 }
618
619 pub fn wraps(&self, name: &str, inner: &Type) -> bool {
620 self.get_name().is_some_and(|n| n == name)
621 && self
622 .get_type_params()
623 .and_then(|p| p.first())
624 .is_some_and(|first| *first == *inner)
625 }
626
627 pub fn get_function_params(&self) -> Option<&[Type]> {
628 match self {
629 Type::Function { params, .. } => Some(params),
630 Type::Constructor {
631 underlying_ty: Some(inner),
632 ..
633 } => inner.get_function_params(),
634 _ => None,
635 }
636 }
637
638 pub fn param_count(&self) -> usize {
639 match self {
640 Type::Function { params, .. } => params.len(),
641 _ => 0,
642 }
643 }
644
645 pub fn get_param_mutability(&self) -> &[bool] {
646 match self {
647 Type::Function {
648 param_mutability, ..
649 } => param_mutability,
650 _ => &[],
651 }
652 }
653
654 pub fn with_replaced_first_param(&self, new_first: &Type) -> Type {
655 match self {
656 Type::Function {
657 params,
658 param_mutability,
659 bounds,
660 return_type,
661 } => {
662 if params.is_empty() {
663 return self.clone();
664 }
665 let mut new_params = params.clone();
666 new_params[0] = new_first.clone();
667 Type::Function {
668 params: new_params,
669 param_mutability: param_mutability.clone(),
670 bounds: bounds.clone(),
671 return_type: return_type.clone(),
672 }
673 }
674 Type::Forall { vars, body } => Type::Forall {
675 vars: vars.clone(),
676 body: Box::new(body.with_replaced_first_param(new_first)),
677 },
678 _ => self.clone(),
679 }
680 }
681
682 pub fn get_bounds(&self) -> &[Bound] {
683 match self {
684 Type::Function { bounds, .. } => bounds,
685 Type::Forall { body, .. } => body.get_bounds(),
686 _ => &[],
687 }
688 }
689
690 pub fn get_qualified_name(&self) -> EcoString {
691 match self.strip_refs() {
692 Type::Constructor { id, .. } => id,
693 _ => panic!("called get_qualified_name on {:#?}", self),
694 }
695 }
696
697 pub fn inner(&self) -> Option<Type> {
698 self.get_type_params()
699 .and_then(|args| args.first().cloned())
700 }
701
702 pub fn ok_type(&self) -> Type {
703 debug_assert!(
704 self.is_result() || self.is_option(),
705 "ok_type called on non-Result/Option type"
706 );
707 self.inner().expect("Result/Option should have inner type")
708 }
709
710 pub fn err_type(&self) -> Type {
711 debug_assert!(self.is_result(), "err_type called on non-Result type");
712 self.get_type_params()
713 .and_then(|args| args.get(1).cloned())
714 .expect("Result should have error type")
715 }
716}
717
718impl Type {
719 pub fn unwrap_forall(&self) -> &Type {
720 match self {
721 Type::Forall { body, .. } => body.as_ref(),
722 other => other,
723 }
724 }
725
726 pub fn strip_refs(&self) -> Type {
727 if self.is_ref() {
728 return self.inner().expect("ref type must have inner").strip_refs();
729 }
730
731 self.clone()
732 }
733
734 pub fn with_receiver_placeholder(self) -> Type {
735 match self {
736 Type::Function {
737 params,
738 param_mutability,
739 bounds,
740 return_type,
741 } => {
742 let mut new_params = vec![Type::nominal("__receiver__")];
743 new_params.extend(params);
744
745 let mut new_mutability = vec![false];
746 new_mutability.extend(param_mutability);
747
748 Type::Function {
749 params: new_params,
750 param_mutability: new_mutability,
751 bounds,
752 return_type,
753 }
754 }
755 _ => unreachable!(
756 "with_receiver_placeholder called on non-function type: {:?}",
757 self
758 ),
759 }
760 }
761
762 pub fn remove_vars(types: &[&Type]) -> (Vec<Type>, Vec<EcoString>) {
763 let mut vars = HashMap::default();
764 let types = types
765 .iter()
766 .map(|v| Self::remove_vars_impl(v, &mut vars))
767 .collect();
768
769 (types, vars.into_values().collect())
770 }
771
772 fn remove_vars_impl(ty: &Type, vars: &mut HashMap<i32, EcoString>) -> Type {
773 match ty {
774 Type::Constructor {
775 id: name,
776 params: args,
777 underlying_ty: underlying,
778 } => Type::Constructor {
779 id: name.clone(),
780 params: args
781 .iter()
782 .map(|a| Self::remove_vars_impl(a, vars))
783 .collect(),
784 underlying_ty: underlying
785 .as_ref()
786 .map(|u| Box::new(Self::remove_vars_impl(u, vars))),
787 },
788
789 Type::Function {
790 params: args,
791 param_mutability,
792 bounds,
793 return_type,
794 } => Type::Function {
795 params: args
796 .iter()
797 .map(|a| Self::remove_vars_impl(a, vars))
798 .collect(),
799 param_mutability: param_mutability.clone(),
800 bounds: bounds
801 .iter()
802 .map(|b| Bound {
803 param_name: b.param_name.clone(),
804 generic: Self::remove_vars_impl(&b.generic, vars),
805 ty: Self::remove_vars_impl(&b.ty, vars),
806 })
807 .collect(),
808 return_type: Self::remove_vars_impl(return_type, vars).into(),
809 },
810
811 Type::Variable(type_var) => match &*type_var.borrow() {
812 TypeVariableState::Unbound { id, hint } => match vars.get(id) {
813 Some(g) => Self::nominal(g),
814 None => {
815 let name: EcoString = hint.clone().unwrap_or_else(|| {
816 char::from_digit(
817 (vars.len() + 10)
818 .try_into()
819 .expect("type var count fits in u32"),
820 16,
821 )
822 .expect("type var index is valid hex digit")
823 .to_uppercase()
824 .to_string()
825 .into()
826 });
827
828 vars.insert(*id, name.clone());
829 Self::nominal(&name)
830 }
831 },
832 TypeVariableState::Link(ty) => Self::remove_vars_impl(ty, vars),
833 },
834
835 Type::Forall { body, .. } => Self::remove_vars_impl(body, vars),
836 Type::Tuple(elements) => Type::Tuple(
837 elements
838 .iter()
839 .map(|e| Self::remove_vars_impl(e, vars))
840 .collect(),
841 ),
842 Type::Parameter(name) => Type::Parameter(name.clone()),
843 Type::Never | Type::Error => ty.clone(),
844 }
845 }
846
847 pub fn contains_type(&self, target: &Type) -> bool {
848 if *self == *target {
849 return true;
850 }
851 match self {
852 Type::Constructor { params, .. } => params.iter().any(|p| p.contains_type(target)),
853 Type::Function {
854 params,
855 return_type,
856 ..
857 } => {
858 params.iter().any(|p| p.contains_type(target)) || return_type.contains_type(target)
859 }
860 Type::Variable(var) => {
861 if let TypeVariableState::Link(linked) = &*var.borrow() {
862 linked.contains_type(target)
863 } else {
864 false
865 }
866 }
867 Type::Forall { body, .. } => body.contains_type(target),
868 Type::Tuple(elements) => elements.iter().any(|e| e.contains_type(target)),
869 Type::Parameter(_) | Type::Never | Type::Error => false,
870 }
871 }
872
873 pub fn shallow_resolve(&self) -> Type {
877 match self {
878 Type::Variable(type_var) => {
879 let state = type_var.borrow();
880 match &*state {
881 TypeVariableState::Unbound { .. } => self.clone(),
882 TypeVariableState::Link(linked) => linked.shallow_resolve(),
883 }
884 }
885 _ => self.clone(),
886 }
887 }
888
889 pub fn resolve(&self) -> Type {
890 match self {
891 Type::Variable(type_var) => {
892 let state = type_var.borrow();
893 match &*state {
894 TypeVariableState::Unbound { .. } => self.clone(),
895 TypeVariableState::Link(linked) => {
896 let resolved = linked.resolve();
897 drop(state);
898 *type_var.borrow_mut() = TypeVariableState::Link(resolved.clone());
899 resolved
900 }
901 }
902 }
903 Type::Constructor {
904 id,
905 params,
906 underlying_ty: underlying,
907 } => Type::Constructor {
908 id: id.clone(),
909 params: params.iter().map(|p| p.resolve()).collect(),
910 underlying_ty: underlying.as_ref().map(|u| Box::new(u.resolve())),
911 },
912 Type::Function {
913 params,
914 param_mutability,
915 bounds,
916 return_type,
917 } => Type::Function {
918 params: params.iter().map(|p| p.resolve()).collect(),
919 param_mutability: param_mutability.clone(),
920 bounds: bounds
921 .iter()
922 .map(|b| Bound {
923 param_name: b.param_name.clone(),
924 generic: b.generic.resolve(),
925 ty: b.ty.resolve(),
926 })
927 .collect(),
928 return_type: Box::new(return_type.resolve()),
929 },
930 Type::Forall { .. } => {
931 unreachable!("Forall types are always instantiated before resolve")
932 }
933 Type::Tuple(elements) => Type::Tuple(elements.iter().map(|e| e.resolve()).collect()),
934 Type::Parameter(_) | Type::Error => self.clone(),
935 Type::Never => Type::Never,
936 }
937 }
938}
939
940#[derive(Debug, Clone, Copy, PartialEq, Eq)]
941pub enum NumericFamily {
942 SignedInt,
943 UnsignedInt,
944 Float,
945}
946
947const SIGNED_INT_TYPES: &[&str] = &["int", "int8", "int16", "int32", "int64", "rune"];
948const FLOAT_TYPES: &[&str] = &["float32", "float64"];
949
950impl Type {
951 pub fn underlying_numeric_type(&self) -> Option<Type> {
952 self.underlying_numeric_type_recursive(&mut HashSet::default())
953 }
954
955 pub fn has_underlying_numeric_type(&self) -> bool {
956 self.underlying_numeric_type().is_some()
957 }
958
959 fn underlying_numeric_type_recursive(&self, visited: &mut HashSet<EcoString>) -> Option<Type> {
960 match self {
961 Type::Constructor {
962 id,
963 underlying_ty: underlying,
964 ..
965 } => {
966 if self.is_numeric() {
967 return Some(self.clone());
968 }
969
970 if !visited.insert(id.clone()) {
971 return None;
972 }
973
974 underlying
975 .as_ref()?
976 .underlying_numeric_type_recursive(visited)
977 }
978 _ => None,
979 }
980 }
981
982 pub fn numeric_family(&self) -> Option<NumericFamily> {
983 let name = match self {
984 Type::Constructor { id, .. } => unqualified_name(id),
985 _ => return None,
986 };
987
988 if SIGNED_INT_TYPES.contains(&name) {
989 Some(NumericFamily::SignedInt)
990 } else if UNSIGNED_INT_TYPES.contains(&name) {
991 Some(NumericFamily::UnsignedInt)
992 } else if FLOAT_TYPES.contains(&name) {
993 Some(NumericFamily::Float)
994 } else {
995 None
996 }
997 }
998
999 pub fn is_numeric_compatible_with(&self, other: &Type) -> bool {
1000 let self_underlying_ty = self.underlying_numeric_type();
1001 let other_underlying_ty = other.underlying_numeric_type();
1002
1003 match (self_underlying_ty, other_underlying_ty) {
1004 (Some(s), Some(o)) => s.numeric_family() == o.numeric_family(),
1005 _ => false,
1006 }
1007 }
1008
1009 pub fn is_aliased_numeric_type(&self) -> bool {
1010 match self {
1011 Type::Constructor { underlying_ty, .. } => {
1012 underlying_ty.is_some() && !self.is_numeric()
1013 }
1014 _ => false,
1015 }
1016 }
1017}