Skip to main content

lisette_syntax/
types.rs

1use rustc_hash::{FxHashMap as HashMap, FxHashSet as HashSet};
2use std::borrow::Borrow;
3use std::cell::OnceCell;
4
5use ecow::EcoString;
6
7/// Dot-qualified identifier for a named type, method, value, or variant.
8///
9/// Wraps the qualified name (`"main.Point.sum"`, `"prelude.Option"`,
10/// `"go:net/http.Handler"`) as a single `EcoString` and exposes structured
11/// accessors. Centralizes the join/split logic that used to live in ad-hoc
12/// `format!("{}.{}", ..)` and `split_once('.')` call sites.
13#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
14#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
15pub struct Symbol(EcoString);
16
17impl Symbol {
18    /// Joins a module id and a local (possibly multi-segment) name.
19    ///
20    /// `Symbol::from_parts("main", "Point.sum")` → `"main.Point.sum"`.
21    pub fn from_parts(module: &str, local: &str) -> Self {
22        let mut s = String::with_capacity(module.len() + 1 + local.len());
23        s.push_str(module);
24        s.push('.');
25        s.push_str(local);
26        Self(EcoString::from(s))
27    }
28
29    /// Appends an additional dot-segment to an already-qualified symbol.
30    ///
31    /// `Symbol::from_raw("main.Shape").with_segment("Circle")` →
32    /// `"main.Shape.Circle"`.
33    pub fn with_segment(&self, segment: &str) -> Self {
34        let mut s = String::with_capacity(self.0.len() + 1 + segment.len());
35        s.push_str(&self.0);
36        s.push('.');
37        s.push_str(segment);
38        Self(EcoString::from(s))
39    }
40
41    /// Wraps an already-constructed qualified string. Prefer `from_parts`
42    /// when the module id and local name are available separately.
43    pub fn from_raw(qualified: impl Into<EcoString>) -> Self {
44        Self(qualified.into())
45    }
46
47    pub fn as_str(&self) -> &str {
48        &self.0
49    }
50
51    pub fn as_eco(&self) -> &EcoString {
52        &self.0
53    }
54
55    /// Last dot-separated segment. `"main.Point.sum"` → `"sum"`.
56    pub fn last_segment(&self) -> &str {
57        self.0.rsplit('.').next().unwrap_or(&self.0)
58    }
59
60    /// Strips the last dot-separated segment. `"main.Point.sum"` → `"main.Point"`.
61    /// Returns `None` if the symbol has no dot.
62    pub fn without_last_segment(&self) -> Option<&str> {
63        self.0.rsplit_once('.').map(|(rest, _)| rest)
64    }
65
66    /// Naive first segment (first dot split). Correct for user modules; for
67    /// `go:net/http.Handler`-style symbols, resolve via
68    /// `Store::module_for_qualified_name` instead.
69    pub fn simple_module_part(&self) -> Option<&str> {
70        self.0.split_once('.').map(|(m, _)| m)
71    }
72}
73
74impl Borrow<str> for Symbol {
75    fn borrow(&self) -> &str {
76        &self.0
77    }
78}
79
80impl AsRef<str> for Symbol {
81    fn as_ref(&self) -> &str {
82        &self.0
83    }
84}
85
86impl std::ops::Deref for Symbol {
87    type Target = str;
88
89    fn deref(&self) -> &str {
90        &self.0
91    }
92}
93
94impl From<&Symbol> for EcoString {
95    fn from(s: &Symbol) -> Self {
96        s.0.clone()
97    }
98}
99
100impl std::fmt::Display for Symbol {
101    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
102        self.0.fmt(f)
103    }
104}
105
106impl From<EcoString> for Symbol {
107    fn from(s: EcoString) -> Self {
108        Self(s)
109    }
110}
111
112impl From<Symbol> for EcoString {
113    fn from(s: Symbol) -> Self {
114        s.0
115    }
116}
117
118impl From<&str> for Symbol {
119    fn from(s: &str) -> Self {
120        Self(EcoString::from(s))
121    }
122}
123
124impl From<String> for Symbol {
125    fn from(s: String) -> Self {
126        Self(EcoString::from(s))
127    }
128}
129
130impl PartialEq<str> for Symbol {
131    fn eq(&self, other: &str) -> bool {
132        self.0.as_str() == other
133    }
134}
135
136impl PartialEq<&str> for Symbol {
137    fn eq(&self, other: &&str) -> bool {
138        self.0.as_str() == *other
139    }
140}
141
142/// Extract the unqualified name from a dot-qualified identifier.
143///
144/// `"prelude.Option"` → `"Option"`, `"**nominal.int"` → `"int"`, `"foo"` → `"foo"`
145pub fn unqualified_name(id: &str) -> &str {
146    id.rsplit('.').next().unwrap_or(id)
147}
148
149/// type param name -> type variable
150pub type SubstitutionMap = HashMap<EcoString, Type>;
151
152pub fn substitute(ty: &Type, map: &HashMap<EcoString, Type>) -> Type {
153    if map.is_empty() {
154        return ty.clone();
155    }
156    match ty {
157        Type::Parameter(name) => map.get(name).cloned().unwrap_or_else(|| ty.clone()),
158        Type::Nominal {
159            id,
160            params,
161            underlying_ty: underlying,
162        } => Type::Nominal {
163            id: id.clone(),
164            params: params.iter().map(|p| substitute(p, map)).collect(),
165            underlying_ty: underlying.as_ref().map(|u| Box::new(substitute(u, map))),
166        },
167        Type::Function {
168            params,
169            param_mutability,
170            bounds,
171            return_type,
172        } => Type::Function {
173            params: params.iter().map(|p| substitute(p, map)).collect(),
174            param_mutability: param_mutability.clone(),
175            bounds: bounds
176                .iter()
177                .map(|b| Bound {
178                    param_name: b.param_name.clone(),
179                    generic: substitute(&b.generic, map),
180                    ty: substitute(&b.ty, map),
181                })
182                .collect(),
183            return_type: Box::new(substitute(return_type, map)),
184        },
185        Type::Var { .. } | Type::Error => ty.clone(),
186        Type::Forall { vars, body } => {
187            let has_overlap = map.keys().any(|k| vars.contains(k));
188            let substituted_body = if has_overlap {
189                let filtered_map: HashMap<EcoString, Type> = map
190                    .iter()
191                    .filter(|(k, _)| !vars.contains(*k))
192                    .map(|(k, v)| (k.clone(), v.clone()))
193                    .collect();
194                substitute(body, &filtered_map)
195            } else {
196                substitute(body, map)
197            };
198            Type::Forall {
199                vars: vars.clone(),
200                body: Box::new(substituted_body),
201            }
202        }
203        Type::Tuple(elements) => Type::Tuple(elements.iter().map(|e| substitute(e, map)).collect()),
204        Type::Compound { kind, args } => Type::Compound {
205            kind: *kind,
206            args: args.iter().map(|a| substitute(a, map)).collect(),
207        },
208        Type::Simple(_) | Type::Never | Type::ImportNamespace(_) | Type::ReceiverPlaceholder => {
209            ty.clone()
210        }
211    }
212}
213
214#[derive(Debug, Clone, PartialEq)]
215#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
216pub struct Bound {
217    pub param_name: EcoString,
218    pub generic: Type,
219    pub ty: Type,
220}
221
222/// A unique handle identifying a type variable. The binding state (Unbound /
223/// Bound-to-a-Type) lives in a `TypeEnv` owned by the checker; the handle is
224/// a plain id so `Type` stays a pure value (Clone, Eq, Hash, Serialize).
225#[derive(Clone, Copy, PartialEq, Eq, Hash)]
226#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
227pub struct TypeVarId(pub u32);
228
229impl TypeVarId {
230    pub const IGNORED: TypeVarId = TypeVarId(u32::MAX);
231    pub const UNINFERRED: TypeVarId = TypeVarId(u32::MAX - 1);
232
233    pub fn is_reserved(self) -> bool {
234        self == Self::IGNORED || self == Self::UNINFERRED
235    }
236
237    pub fn as_u32(self) -> u32 {
238        self.0
239    }
240}
241
242impl std::fmt::Debug for TypeVarId {
243    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
244        match *self {
245            Self::IGNORED => write!(f, "ignored"),
246            Self::UNINFERRED => write!(f, "uninferred"),
247            TypeVarId(n) => write!(f, "#{}", n),
248        }
249    }
250}
251
252#[derive(Clone)]
253#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
254pub enum Type {
255    Simple(SimpleKind),
256
257    Compound {
258        kind: CompoundKind,
259        args: Vec<Type>,
260    },
261
262    Nominal {
263        id: Symbol,
264        params: Vec<Type>,
265        underlying_ty: Option<Box<Type>>,
266    },
267
268    /// Module namespace handle. Produced by imports (e.g. `import http "net/http"`
269    /// produces an `ImportNamespace("go:net/http")` on the local identifier).
270    /// Dot-access on this type resolves to the module's exports.
271    ImportNamespace(EcoString),
272
273    Function {
274        params: Vec<Type>,
275        param_mutability: Vec<bool>,
276        bounds: Vec<Bound>,
277        return_type: Box<Type>,
278    },
279
280    /// Type variable handle. Binding state lives in a `TypeEnv` owned by the
281    /// checker; the inline `hint` is display metadata set at allocation time
282    /// so `Display`/`Debug` work without env access.
283    Var {
284        id: TypeVarId,
285        hint: Option<EcoString>,
286    },
287
288    Forall {
289        vars: Vec<EcoString>,
290        body: Box<Type>,
291    },
292
293    Parameter(EcoString),
294
295    Never,
296
297    Tuple(Vec<Type>),
298
299    /// Poison type returned after an error has been reported.
300    /// Unifies with everything silently, preventing cascading diagnostics.
301    Error,
302
303    /// Sentinel occupying the `self` slot of an interface method type.
304    /// Unifies silently so an implementing type's receiver does not conflict
305    /// with the abstract method shape. Previously encoded as
306    /// `Constructor { id: "**nominal.__receiver__" }`.
307    ReceiverPlaceholder,
308}
309
310impl std::fmt::Debug for Type {
311    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
312        match self {
313            Type::Nominal { id, params, .. } => f
314                .debug_struct("Nominal")
315                .field("id", id)
316                .field("params", params)
317                .finish(),
318            Type::Function {
319                params,
320                param_mutability,
321                bounds,
322                return_type,
323            } => {
324                let mut s = f.debug_struct("Function");
325                s.field("params", params);
326                if param_mutability.iter().any(|m| *m) {
327                    s.field("param_mutability", param_mutability);
328                }
329                s.field("bounds", bounds)
330                    .field("return_type", return_type)
331                    .finish()
332            }
333            Type::Var { id, hint } => {
334                let mut s = f.debug_struct("Var");
335                s.field("id", id);
336                if let Some(h) = hint {
337                    s.field("hint", h);
338                }
339                s.finish()
340            }
341            Type::Forall { vars, body } => f
342                .debug_struct("Forall")
343                .field("vars", vars)
344                .field("body", body)
345                .finish(),
346            Type::Parameter(name) => f.debug_tuple("Parameter").field(name).finish(),
347            Type::Never => write!(f, "Never"),
348            Type::Tuple(elements) => f.debug_tuple("Tuple").field(elements).finish(),
349            Type::Error => write!(f, "Error"),
350            Type::ImportNamespace(module_id) => {
351                f.debug_tuple("ImportNamespace").field(module_id).finish()
352            }
353            Type::ReceiverPlaceholder => write!(f, "ReceiverPlaceholder"),
354            Type::Simple(kind) => f.debug_tuple("Simple").field(kind).finish(),
355            Type::Compound { kind, args } => f
356                .debug_struct("Compound")
357                .field("kind", kind)
358                .field("args", args)
359                .finish(),
360        }
361    }
362}
363
364impl PartialEq for Type {
365    fn eq(&self, other: &Self) -> bool {
366        match (self, other) {
367            (
368                Type::Nominal {
369                    id: id1,
370                    params: params1,
371                    ..
372                },
373                Type::Nominal {
374                    id: id2,
375                    params: params2,
376                    ..
377                },
378            ) => id1 == id2 && params1 == params2,
379            (
380                Type::Function {
381                    params: p1,
382                    param_mutability: m1,
383                    bounds: b1,
384                    return_type: r1,
385                },
386                Type::Function {
387                    params: p2,
388                    param_mutability: m2,
389                    bounds: b2,
390                    return_type: r2,
391                },
392            ) => p1 == p2 && m1 == m2 && b1 == b2 && r1 == r2,
393            (Type::Var { id: id1, .. }, Type::Var { id: id2, .. }) => id1 == id2,
394            (
395                Type::Forall {
396                    vars: vars1,
397                    body: body1,
398                },
399                Type::Forall {
400                    vars: vars2,
401                    body: body2,
402                },
403            ) => vars1 == vars2 && body1 == body2,
404            (Type::Parameter(name1), Type::Parameter(name2)) => name1 == name2,
405            (Type::Never, Type::Never) => true,
406            (Type::Tuple(elems1), Type::Tuple(elems2)) => elems1 == elems2,
407            (Type::ImportNamespace(m1), Type::ImportNamespace(m2)) => m1 == m2,
408            (Type::ReceiverPlaceholder, Type::ReceiverPlaceholder) => true,
409            (Type::Simple(k1), Type::Simple(k2)) => k1 == k2,
410            (Type::Compound { kind: k1, args: a1 }, Type::Compound { kind: k2, args: a2 }) => {
411                k1 == k2 && a1 == a2
412            }
413            _ => false,
414        }
415    }
416}
417
418thread_local! {
419    static INTERNED_INT: OnceCell<Type> = const { OnceCell::new() };
420    static INTERNED_STRING: OnceCell<Type> = const { OnceCell::new() };
421    static INTERNED_BOOL: OnceCell<Type> = const { OnceCell::new() };
422    static INTERNED_UNIT: OnceCell<Type> = const { OnceCell::new() };
423    static INTERNED_FLOAT64: OnceCell<Type> = const { OnceCell::new() };
424    static INTERNED_RUNE: OnceCell<Type> = const { OnceCell::new() };
425    static INTERNED_BYTE: OnceCell<Type> = const { OnceCell::new() };
426}
427
428impl Type {
429    pub fn simple(kind: SimpleKind) -> Type {
430        Self::Simple(kind)
431    }
432
433    pub fn compound(kind: CompoundKind, args: Vec<Type>) -> Type {
434        Self::Compound { kind, args }
435    }
436
437    pub fn int() -> Type {
438        INTERNED_INT.with(|cell| cell.get_or_init(|| Self::simple(SimpleKind::Int)).clone())
439    }
440
441    pub fn string() -> Type {
442        INTERNED_STRING.with(|cell| {
443            cell.get_or_init(|| Self::simple(SimpleKind::String))
444                .clone()
445        })
446    }
447
448    pub fn bool() -> Type {
449        INTERNED_BOOL.with(|cell| cell.get_or_init(|| Self::simple(SimpleKind::Bool)).clone())
450    }
451
452    pub fn unit() -> Type {
453        INTERNED_UNIT.with(|cell| cell.get_or_init(|| Self::simple(SimpleKind::Unit)).clone())
454    }
455
456    pub fn float64() -> Type {
457        INTERNED_FLOAT64.with(|cell| {
458            cell.get_or_init(|| Self::simple(SimpleKind::Float64))
459                .clone()
460        })
461    }
462
463    pub fn rune() -> Type {
464        INTERNED_RUNE.with(|cell| cell.get_or_init(|| Self::simple(SimpleKind::Rune)).clone())
465    }
466
467    pub fn byte() -> Type {
468        INTERNED_BYTE.with(|cell| cell.get_or_init(|| Self::simple(SimpleKind::Byte)).clone())
469    }
470}
471
472impl Type {
473    pub fn uninferred() -> Self {
474        Self::Var {
475            id: TypeVarId::UNINFERRED,
476            hint: None,
477        }
478    }
479
480    pub fn ignored() -> Self {
481        Self::Var {
482            id: TypeVarId::IGNORED,
483            hint: None,
484        }
485    }
486
487    pub fn get_type_params(&self) -> Option<&[Type]> {
488        match self {
489            Type::Nominal { params, .. } => Some(params),
490            Type::Compound { args, .. } => Some(args),
491            _ => None,
492        }
493    }
494}
495
496#[derive(Debug, Clone, Copy, PartialEq, Eq)]
497pub enum NumericFamily {
498    SignedInt,
499    UnsignedInt,
500    Float,
501}
502
503#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
504#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
505pub enum CompoundKind {
506    Ref,
507    Slice,
508    EnumeratedSlice,
509    Map,
510    Channel,
511    Sender,
512    Receiver,
513    VarArgs,
514}
515
516impl CompoundKind {
517    pub fn leaf_name(self) -> &'static str {
518        match self {
519            CompoundKind::Ref => "Ref",
520            CompoundKind::Slice => "Slice",
521            CompoundKind::EnumeratedSlice => "EnumeratedSlice",
522            CompoundKind::Map => "Map",
523            CompoundKind::Channel => "Channel",
524            CompoundKind::Sender => "Sender",
525            CompoundKind::Receiver => "Receiver",
526            CompoundKind::VarArgs => "VarArgs",
527        }
528    }
529
530    pub fn from_name(name: &str) -> Option<CompoundKind> {
531        Some(match name {
532            "Ref" => CompoundKind::Ref,
533            "Slice" => CompoundKind::Slice,
534            "EnumeratedSlice" => CompoundKind::EnumeratedSlice,
535            "Map" => CompoundKind::Map,
536            "Channel" => CompoundKind::Channel,
537            "Sender" => CompoundKind::Sender,
538            "Receiver" => CompoundKind::Receiver,
539            "VarArgs" => CompoundKind::VarArgs,
540            _ => return None,
541        })
542    }
543}
544
545#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
546#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
547pub enum SimpleKind {
548    Int,
549    Int8,
550    Int16,
551    Int32,
552    Int64,
553    Uint,
554    Uint8,
555    Uint16,
556    Uint32,
557    Uint64,
558    Uintptr,
559    Byte,
560    Float32,
561    Float64,
562    Complex64,
563    Complex128,
564    Rune,
565    Bool,
566    String,
567    Unit,
568}
569
570impl SimpleKind {
571    pub fn leaf_name(self) -> &'static str {
572        match self {
573            SimpleKind::Int => "int",
574            SimpleKind::Int8 => "int8",
575            SimpleKind::Int16 => "int16",
576            SimpleKind::Int32 => "int32",
577            SimpleKind::Int64 => "int64",
578            SimpleKind::Uint => "uint",
579            SimpleKind::Uint8 => "uint8",
580            SimpleKind::Uint16 => "uint16",
581            SimpleKind::Uint32 => "uint32",
582            SimpleKind::Uint64 => "uint64",
583            SimpleKind::Uintptr => "uintptr",
584            SimpleKind::Byte => "byte",
585            SimpleKind::Float32 => "float32",
586            SimpleKind::Float64 => "float64",
587            SimpleKind::Complex64 => "complex64",
588            SimpleKind::Complex128 => "complex128",
589            SimpleKind::Rune => "rune",
590            SimpleKind::Bool => "bool",
591            SimpleKind::String => "string",
592            SimpleKind::Unit => "Unit",
593        }
594    }
595
596    pub fn from_name(name: &str) -> Option<SimpleKind> {
597        Some(match name {
598            "int" => SimpleKind::Int,
599            "int8" => SimpleKind::Int8,
600            "int16" => SimpleKind::Int16,
601            "int32" => SimpleKind::Int32,
602            "int64" => SimpleKind::Int64,
603            "uint" => SimpleKind::Uint,
604            "uint8" => SimpleKind::Uint8,
605            "uint16" => SimpleKind::Uint16,
606            "uint32" => SimpleKind::Uint32,
607            "uint64" => SimpleKind::Uint64,
608            "uintptr" => SimpleKind::Uintptr,
609            "byte" => SimpleKind::Byte,
610            "float32" => SimpleKind::Float32,
611            "float64" => SimpleKind::Float64,
612            "complex64" => SimpleKind::Complex64,
613            "complex128" => SimpleKind::Complex128,
614            "rune" => SimpleKind::Rune,
615            "bool" => SimpleKind::Bool,
616            "string" => SimpleKind::String,
617            "Unit" => SimpleKind::Unit,
618            _ => return None,
619        })
620    }
621
622    pub fn is_arithmetic(self) -> bool {
623        !matches!(
624            self,
625            SimpleKind::Bool | SimpleKind::String | SimpleKind::Unit | SimpleKind::Uintptr
626        )
627    }
628
629    pub fn is_ordered(self) -> bool {
630        self.is_arithmetic() && !matches!(self, SimpleKind::Complex64 | SimpleKind::Complex128)
631    }
632
633    pub fn is_unsigned_int(self) -> bool {
634        matches!(
635            self,
636            SimpleKind::Byte
637                | SimpleKind::Uint
638                | SimpleKind::Uint8
639                | SimpleKind::Uint16
640                | SimpleKind::Uint32
641                | SimpleKind::Uint64
642        )
643    }
644
645    pub fn is_signed_int(self) -> bool {
646        matches!(
647            self,
648            SimpleKind::Int
649                | SimpleKind::Int8
650                | SimpleKind::Int16
651                | SimpleKind::Int32
652                | SimpleKind::Int64
653                | SimpleKind::Rune
654        )
655    }
656
657    pub fn is_float(self) -> bool {
658        matches!(self, SimpleKind::Float32 | SimpleKind::Float64)
659    }
660
661    pub fn is_complex(self) -> bool {
662        matches!(self, SimpleKind::Complex64 | SimpleKind::Complex128)
663    }
664
665    pub fn numeric_family(self) -> Option<NumericFamily> {
666        if self.is_signed_int() {
667            Some(NumericFamily::SignedInt)
668        } else if self.is_unsigned_int() {
669            Some(NumericFamily::UnsignedInt)
670        } else if self.is_float() {
671            Some(NumericFamily::Float)
672        } else {
673            None
674        }
675    }
676}
677
678impl Type {
679    pub fn get_function_ret(&self) -> Option<&Type> {
680        match self {
681            Type::Function { return_type, .. } => Some(return_type),
682            _ => None,
683        }
684    }
685
686    pub fn has_name(&self, name: &str) -> bool {
687        match self {
688            Type::Nominal { id, .. } => id.last_segment() == name,
689            Type::Simple(kind) => kind.leaf_name() == name,
690            Type::Compound { kind, .. } => kind.leaf_name() == name,
691            _ => false,
692        }
693    }
694
695    pub fn get_qualified_id(&self) -> Option<&str> {
696        match self {
697            Type::Nominal { id, .. } => Some(id.as_str()),
698            _ => None,
699        }
700    }
701
702    pub fn get_underlying(&self) -> Option<&Type> {
703        match self {
704            Type::Nominal {
705                underlying_ty: underlying,
706                ..
707            } => underlying.as_deref(),
708            _ => None,
709        }
710    }
711
712    pub fn is_result(&self) -> bool {
713        self.has_qualified_id("prelude.Result")
714    }
715
716    pub fn is_option(&self) -> bool {
717        self.has_qualified_id("prelude.Option")
718    }
719
720    pub fn is_partial(&self) -> bool {
721        self.has_qualified_id("prelude.Partial")
722    }
723
724    fn has_qualified_id(&self, qualified_id: &str) -> bool {
725        matches!(self, Type::Nominal { id, .. } if id.as_str() == qualified_id)
726    }
727
728    pub fn is_unit(&self) -> bool {
729        self.is_simple(SimpleKind::Unit)
730    }
731
732    pub fn tuple_arity(&self) -> Option<usize> {
733        match self {
734            Type::Tuple(elements) => Some(elements.len()),
735            _ => None,
736        }
737    }
738
739    pub fn is_tuple(&self) -> bool {
740        matches!(self, Type::Tuple(_))
741    }
742
743    pub fn as_import_namespace(&self) -> Option<&str> {
744        match self {
745            Type::ImportNamespace(module_id) => Some(module_id),
746            _ => None,
747        }
748    }
749
750    pub fn as_compound(&self) -> Option<(CompoundKind, &[Type])> {
751        match self {
752            Type::Compound { kind, args } => Some((*kind, args.as_slice())),
753            Type::Nominal { id, params, .. } => {
754                CompoundKind::from_name(id.last_segment()).map(|k| (k, params.as_slice()))
755            }
756            _ => None,
757        }
758    }
759
760    pub fn is_native(&self, kind: CompoundKind) -> bool {
761        self.as_compound().is_some_and(|(k, _)| k == kind)
762    }
763
764    pub fn is_ref(&self) -> bool {
765        self.is_native(CompoundKind::Ref)
766    }
767
768    pub fn is_slice(&self) -> bool {
769        self.is_native(CompoundKind::Slice)
770    }
771
772    pub fn is_map(&self) -> bool {
773        self.is_native(CompoundKind::Map)
774    }
775
776    pub fn is_channel(&self) -> bool {
777        self.is_native(CompoundKind::Channel)
778    }
779
780    pub fn is_receiver_placeholder(&self) -> bool {
781        matches!(self, Type::ReceiverPlaceholder)
782    }
783
784    pub fn is_unknown(&self) -> bool {
785        self.has_name("Unknown")
786    }
787
788    pub fn is_receiver(&self) -> bool {
789        self.is_native(CompoundKind::Receiver)
790    }
791
792    pub fn is_ignored(&self) -> bool {
793        matches!(self, Type::Var { id, .. } if *id == TypeVarId::IGNORED)
794    }
795
796    pub fn is_variadic(&self) -> Option<Type> {
797        let last = self.get_function_params()?.last()?;
798        match last.as_compound()? {
799            (CompoundKind::VarArgs, _) => last.inner(),
800            _ => None,
801        }
802    }
803
804    pub fn is_string(&self) -> bool {
805        self.is_simple(SimpleKind::String)
806    }
807
808    pub fn is_slice_of_simple(&self, element: SimpleKind) -> bool {
809        match self.as_compound() {
810            Some((CompoundKind::Slice, [elem])) => elem.is_simple(element),
811            _ => false,
812        }
813    }
814
815    pub fn is_slice_of(&self, element_name: &str) -> bool {
816        match self.as_compound() {
817            Some((CompoundKind::Slice, [elem])) => elem.has_name(element_name),
818            _ => false,
819        }
820    }
821
822    pub fn is_byte_slice(&self) -> bool {
823        self.is_slice_of_simple(SimpleKind::Byte) || self.is_slice_of_simple(SimpleKind::Uint8)
824    }
825
826    pub fn is_rune_slice(&self) -> bool {
827        self.is_slice_of_simple(SimpleKind::Rune)
828    }
829
830    pub fn is_byte_or_rune_slice(&self) -> bool {
831        self.is_byte_slice() || self.is_rune_slice()
832    }
833
834    pub fn has_byte_or_rune_slice_underlying(&self) -> bool {
835        if self.is_byte_or_rune_slice() {
836            return true;
837        }
838        match self {
839            Type::Nominal { underlying_ty, .. } => underlying_ty
840                .as_deref()
841                .is_some_and(|u| u.has_byte_or_rune_slice_underlying()),
842            _ => false,
843        }
844    }
845
846    pub fn as_simple(&self) -> Option<SimpleKind> {
847        match self {
848            Type::Simple(kind) => Some(*kind),
849            Type::Nominal { id, .. } => SimpleKind::from_name(id.last_segment()),
850            _ => None,
851        }
852    }
853
854    pub fn is_simple(&self, kind: SimpleKind) -> bool {
855        self.as_simple() == Some(kind)
856    }
857
858    pub fn is_boolean(&self) -> bool {
859        self.is_simple(SimpleKind::Bool)
860    }
861
862    pub fn is_rune(&self) -> bool {
863        self.is_simple(SimpleKind::Rune)
864    }
865
866    pub fn is_float64(&self) -> bool {
867        self.is_simple(SimpleKind::Float64)
868    }
869
870    pub fn is_float32(&self) -> bool {
871        self.is_simple(SimpleKind::Float32)
872    }
873
874    pub fn is_float(&self) -> bool {
875        self.as_simple().is_some_and(SimpleKind::is_float)
876    }
877
878    pub fn is_variable(&self) -> bool {
879        matches!(self, Type::Var { .. })
880    }
881
882    pub fn is_type_var(&self) -> bool {
883        matches!(self, Type::Var { .. })
884    }
885
886    pub fn is_numeric(&self) -> bool {
887        self.as_simple().is_some_and(SimpleKind::is_arithmetic)
888    }
889
890    pub fn is_ordered(&self) -> bool {
891        self.as_simple().is_some_and(SimpleKind::is_ordered)
892    }
893
894    pub fn is_complex(&self) -> bool {
895        self.as_simple().is_some_and(SimpleKind::is_complex)
896    }
897
898    pub fn is_unsigned_int(&self) -> bool {
899        self.as_simple().is_some_and(SimpleKind::is_unsigned_int)
900    }
901
902    pub fn is_never(&self) -> bool {
903        matches!(self, Type::Never)
904    }
905
906    pub fn is_error(&self) -> bool {
907        matches!(self, Type::Error)
908    }
909
910    pub fn has_unbound_variables(&self) -> bool {
911        match self {
912            Type::Var { hint, .. } => hint.is_some(),
913            Type::Nominal { params, .. } => params.iter().any(|p| p.has_unbound_variables()),
914            Type::Function {
915                params,
916                return_type,
917                ..
918            } => {
919                params.iter().any(|p| p.has_unbound_variables())
920                    || return_type.has_unbound_variables()
921            }
922            Type::Forall { body, .. } => body.has_unbound_variables(),
923            Type::Tuple(elements) => elements.iter().any(|e| e.has_unbound_variables()),
924            Type::Compound { args, .. } => args.iter().any(|a| a.has_unbound_variables()),
925            Type::Simple(_)
926            | Type::Parameter(_)
927            | Type::Never
928            | Type::Error
929            | Type::ImportNamespace(_)
930            | Type::ReceiverPlaceholder => false,
931        }
932    }
933
934    pub fn remove_found_type_names(&self, names: &mut HashSet<EcoString>) {
935        if names.is_empty() {
936            return;
937        }
938
939        match self {
940            Type::Nominal { id, params, .. } => {
941                names.remove(id.last_segment());
942                for param in params {
943                    param.remove_found_type_names(names);
944                }
945            }
946            Type::Function {
947                params,
948                return_type,
949                bounds,
950                ..
951            } => {
952                for param in params {
953                    param.remove_found_type_names(names);
954                }
955                return_type.remove_found_type_names(names);
956                for bound in bounds {
957                    bound.generic.remove_found_type_names(names);
958                    bound.ty.remove_found_type_names(names);
959                }
960            }
961            Type::Forall { body, .. } => {
962                body.remove_found_type_names(names);
963            }
964            Type::Var { .. } => {}
965            Type::Parameter(name) => {
966                names.remove(name);
967            }
968            Type::Tuple(elements) => {
969                for element in elements {
970                    element.remove_found_type_names(names);
971                }
972            }
973            Type::Compound { kind, args } => {
974                names.remove(kind.leaf_name());
975                for arg in args {
976                    arg.remove_found_type_names(names);
977                }
978            }
979            Type::Simple(kind) => {
980                names.remove(kind.leaf_name());
981            }
982            Type::Never | Type::Error | Type::ImportNamespace(_) | Type::ReceiverPlaceholder => {}
983        }
984    }
985}
986
987impl Type {
988    pub fn get_name(&self) -> Option<&str> {
989        match self {
990            Type::Simple(kind) => Some(kind.leaf_name()),
991            Type::Compound { kind, args } => match kind {
992                CompoundKind::Ref => args.first().and_then(|inner| inner.get_name()),
993                _ => Some(kind.leaf_name()),
994            },
995            Type::Nominal { id, params, .. } => {
996                let name = id.last_segment();
997                if CompoundKind::from_name(name) == Some(CompoundKind::Ref) {
998                    return params.first().and_then(|inner| inner.get_name());
999                }
1000                Some(name)
1001            }
1002            Type::ImportNamespace(module_id) => {
1003                let path = module_id.strip_prefix("go:").unwrap_or(module_id);
1004                path.rsplit('/').next()
1005            }
1006            _ => None,
1007        }
1008    }
1009
1010    pub fn wraps(&self, name: &str, inner: &Type) -> bool {
1011        self.get_name().is_some_and(|n| n == name)
1012            && self
1013                .get_type_params()
1014                .and_then(|p| p.first())
1015                .is_some_and(|first| *first == *inner)
1016    }
1017
1018    pub fn get_function_params(&self) -> Option<&[Type]> {
1019        match self {
1020            Type::Function { params, .. } => Some(params),
1021            Type::Nominal {
1022                underlying_ty: Some(inner),
1023                ..
1024            } => inner.get_function_params(),
1025            _ => None,
1026        }
1027    }
1028
1029    pub fn param_count(&self) -> usize {
1030        match self {
1031            Type::Function { params, .. } => params.len(),
1032            _ => 0,
1033        }
1034    }
1035
1036    pub fn get_param_mutability(&self) -> &[bool] {
1037        match self {
1038            Type::Function {
1039                param_mutability, ..
1040            } => param_mutability,
1041            _ => &[],
1042        }
1043    }
1044
1045    pub fn with_replaced_first_param(&self, new_first: &Type) -> Type {
1046        match self {
1047            Type::Function {
1048                params,
1049                param_mutability,
1050                bounds,
1051                return_type,
1052            } => {
1053                if params.is_empty() {
1054                    return self.clone();
1055                }
1056                let mut new_params = params.clone();
1057                new_params[0] = new_first.clone();
1058                Type::Function {
1059                    params: new_params,
1060                    param_mutability: param_mutability.clone(),
1061                    bounds: bounds.clone(),
1062                    return_type: return_type.clone(),
1063                }
1064            }
1065            Type::Forall { vars, body } => Type::Forall {
1066                vars: vars.clone(),
1067                body: Box::new(body.with_replaced_first_param(new_first)),
1068            },
1069            _ => self.clone(),
1070        }
1071    }
1072
1073    pub fn get_bounds(&self) -> &[Bound] {
1074        match self {
1075            Type::Function { bounds, .. } => bounds,
1076            Type::Forall { body, .. } => body.get_bounds(),
1077            _ => &[],
1078        }
1079    }
1080
1081    pub fn get_qualified_name(&self) -> Symbol {
1082        match self.strip_refs() {
1083            Type::Nominal { id, .. } => id,
1084            Type::Simple(kind) => Symbol::from_parts("prelude", kind.leaf_name()),
1085            Type::Compound { kind, .. } => Symbol::from_parts("prelude", kind.leaf_name()),
1086            _ => panic!("called get_qualified_name on {:#?}", self),
1087        }
1088    }
1089
1090    pub fn inner(&self) -> Option<Type> {
1091        self.get_type_params()
1092            .and_then(|args| args.first().cloned())
1093    }
1094
1095    pub fn ok_type(&self) -> Type {
1096        debug_assert!(
1097            self.is_result() || self.is_option() || self.is_partial(),
1098            "ok_type called on non-Result/Option/Partial type"
1099        );
1100        self.inner()
1101            .expect("Result/Option/Partial should have inner type")
1102    }
1103
1104    pub fn err_type(&self) -> Type {
1105        debug_assert!(
1106            self.is_result() || self.is_partial(),
1107            "err_type called on non-Result/Partial type"
1108        );
1109        self.get_type_params()
1110            .and_then(|args| args.get(1).cloned())
1111            .expect("Result/Partial should have error type")
1112    }
1113}
1114
1115impl Type {
1116    pub fn unwrap_forall(&self) -> &Type {
1117        match self {
1118            Type::Forall { body, .. } => body.as_ref(),
1119            other => other,
1120        }
1121    }
1122
1123    pub fn strip_refs(&self) -> Type {
1124        if self.is_ref() {
1125            return self.inner().expect("ref type must have inner").strip_refs();
1126        }
1127
1128        self.clone()
1129    }
1130
1131    pub fn with_receiver_placeholder(self) -> Type {
1132        match self {
1133            Type::Function {
1134                params,
1135                param_mutability,
1136                bounds,
1137                return_type,
1138            } => {
1139                let mut new_params = vec![Type::ReceiverPlaceholder];
1140                new_params.extend(params);
1141
1142                let mut new_mutability = vec![false];
1143                new_mutability.extend(param_mutability);
1144
1145                Type::Function {
1146                    params: new_params,
1147                    param_mutability: new_mutability,
1148                    bounds,
1149                    return_type,
1150                }
1151            }
1152            _ => unreachable!(
1153                "with_receiver_placeholder called on non-function type: {:?}",
1154                self
1155            ),
1156        }
1157    }
1158
1159    pub fn remove_vars(types: &[&Type]) -> (Vec<Type>, Vec<EcoString>) {
1160        let mut vars = HashMap::default();
1161        let types = types
1162            .iter()
1163            .map(|v| Self::remove_vars_impl(v, &mut vars))
1164            .collect();
1165
1166        (types, vars.into_values().collect())
1167    }
1168
1169    fn remove_vars_impl(ty: &Type, vars: &mut HashMap<u32, EcoString>) -> Type {
1170        match ty {
1171            Type::Nominal {
1172                id: name,
1173                params: args,
1174                underlying_ty: underlying,
1175            } => Type::Nominal {
1176                id: name.clone(),
1177                params: args
1178                    .iter()
1179                    .map(|a| Self::remove_vars_impl(a, vars))
1180                    .collect(),
1181                underlying_ty: underlying
1182                    .as_ref()
1183                    .map(|u| Box::new(Self::remove_vars_impl(u, vars))),
1184            },
1185
1186            Type::Function {
1187                params: args,
1188                param_mutability,
1189                bounds,
1190                return_type,
1191            } => Type::Function {
1192                params: args
1193                    .iter()
1194                    .map(|a| Self::remove_vars_impl(a, vars))
1195                    .collect(),
1196                param_mutability: param_mutability.clone(),
1197                bounds: bounds
1198                    .iter()
1199                    .map(|b| Bound {
1200                        param_name: b.param_name.clone(),
1201                        generic: Self::remove_vars_impl(&b.generic, vars),
1202                        ty: Self::remove_vars_impl(&b.ty, vars),
1203                    })
1204                    .collect(),
1205                return_type: Self::remove_vars_impl(return_type, vars).into(),
1206            },
1207
1208            Type::Var { id, hint } => match vars.get(&id.0) {
1209                Some(g) => Type::Parameter(g.clone()),
1210                None => {
1211                    let name: EcoString = hint.clone().unwrap_or_else(|| {
1212                        char::from_digit(
1213                            (vars.len() + 10)
1214                                .try_into()
1215                                .expect("type var count fits in u32"),
1216                            16,
1217                        )
1218                        .expect("type var index is valid hex digit")
1219                        .to_uppercase()
1220                        .to_string()
1221                        .into()
1222                    });
1223
1224                    vars.insert(id.0, name.clone());
1225                    Type::Parameter(name)
1226                }
1227            },
1228
1229            Type::Forall { body, .. } => Self::remove_vars_impl(body, vars),
1230            Type::Tuple(elements) => Type::Tuple(
1231                elements
1232                    .iter()
1233                    .map(|e| Self::remove_vars_impl(e, vars))
1234                    .collect(),
1235            ),
1236            Type::Compound { kind, args } => Type::Compound {
1237                kind: *kind,
1238                args: args
1239                    .iter()
1240                    .map(|a| Self::remove_vars_impl(a, vars))
1241                    .collect(),
1242            },
1243            Type::Simple(_) | Type::Parameter(_) => ty.clone(),
1244            Type::Never | Type::Error | Type::ImportNamespace(_) | Type::ReceiverPlaceholder => {
1245                ty.clone()
1246            }
1247        }
1248    }
1249
1250    pub fn contains_type(&self, target: &Type) -> bool {
1251        if *self == *target {
1252            return true;
1253        }
1254        match self {
1255            Type::Nominal { params, .. } => params.iter().any(|p| p.contains_type(target)),
1256            Type::Function {
1257                params,
1258                return_type,
1259                ..
1260            } => {
1261                params.iter().any(|p| p.contains_type(target)) || return_type.contains_type(target)
1262            }
1263            Type::Var { .. } => false,
1264            Type::Forall { body, .. } => body.contains_type(target),
1265            Type::Tuple(elements) => elements.iter().any(|e| e.contains_type(target)),
1266            Type::Compound { args, .. } => args.iter().any(|a| a.contains_type(target)),
1267            Type::Simple(_)
1268            | Type::Parameter(_)
1269            | Type::Never
1270            | Type::Error
1271            | Type::ImportNamespace(_)
1272            | Type::ReceiverPlaceholder => false,
1273        }
1274    }
1275}
1276
1277impl Type {
1278    pub fn underlying_numeric_type(&self) -> Option<Type> {
1279        self.underlying_numeric_type_recursive(&mut HashSet::default())
1280    }
1281
1282    pub fn has_underlying_numeric_type(&self) -> bool {
1283        self.underlying_numeric_type().is_some()
1284    }
1285
1286    fn underlying_numeric_type_recursive(&self, visited: &mut HashSet<Symbol>) -> Option<Type> {
1287        match self {
1288            Type::Simple(_) if self.is_numeric() => Some(self.clone()),
1289            Type::Nominal {
1290                id,
1291                underlying_ty: underlying,
1292                ..
1293            } => {
1294                if self.is_numeric() {
1295                    return Some(self.clone());
1296                }
1297
1298                if !visited.insert(id.clone()) {
1299                    return None;
1300                }
1301
1302                underlying
1303                    .as_ref()?
1304                    .underlying_numeric_type_recursive(visited)
1305            }
1306            _ => None,
1307        }
1308    }
1309
1310    pub fn numeric_family(&self) -> Option<NumericFamily> {
1311        self.as_simple()?.numeric_family()
1312    }
1313
1314    pub fn is_numeric_compatible_with(&self, other: &Type) -> bool {
1315        let self_underlying_ty = self.underlying_numeric_type();
1316        let other_underlying_ty = other.underlying_numeric_type();
1317
1318        match (self_underlying_ty, other_underlying_ty) {
1319            (Some(s), Some(o)) => s.numeric_family() == o.numeric_family(),
1320            _ => false,
1321        }
1322    }
1323
1324    pub fn is_aliased_numeric_type(&self) -> bool {
1325        match self {
1326            Type::Nominal { underlying_ty, .. } => {
1327                underlying_ty.is_some() && !self.is_numeric() && self.has_underlying_numeric_type()
1328            }
1329            _ => false,
1330        }
1331    }
1332}