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    /// True for Go's `cmp.Ordered` set: ints, floats, strings, and named aliases over them.
895    pub fn satisfies_ordered_constraint(&self) -> bool {
896        if let Some(kind) = self.as_simple() {
897            return matches!(
898                kind,
899                SimpleKind::Int
900                    | SimpleKind::Int8
901                    | SimpleKind::Int16
902                    | SimpleKind::Int32
903                    | SimpleKind::Int64
904                    | SimpleKind::Uint
905                    | SimpleKind::Uint8
906                    | SimpleKind::Uint16
907                    | SimpleKind::Uint32
908                    | SimpleKind::Uint64
909                    | SimpleKind::Uintptr
910                    | SimpleKind::Byte
911                    | SimpleKind::Rune
912                    | SimpleKind::Float32
913                    | SimpleKind::Float64
914                    | SimpleKind::String
915            );
916        }
917        match self {
918            Type::Nominal { underlying_ty, .. } => underlying_ty
919                .as_deref()
920                .is_some_and(Type::satisfies_ordered_constraint),
921            Type::Parameter(_) => true,
922            _ => false,
923        }
924    }
925
926    pub fn is_complex(&self) -> bool {
927        self.as_simple().is_some_and(SimpleKind::is_complex)
928    }
929
930    pub fn is_unsigned_int(&self) -> bool {
931        self.as_simple().is_some_and(SimpleKind::is_unsigned_int)
932    }
933
934    pub fn is_never(&self) -> bool {
935        matches!(self, Type::Never)
936    }
937
938    pub fn is_error(&self) -> bool {
939        matches!(self, Type::Error)
940    }
941
942    pub fn has_unbound_variables(&self) -> bool {
943        match self {
944            Type::Var { hint, .. } => hint.is_some(),
945            Type::Nominal { params, .. } => params.iter().any(|p| p.has_unbound_variables()),
946            Type::Function {
947                params,
948                return_type,
949                ..
950            } => {
951                params.iter().any(|p| p.has_unbound_variables())
952                    || return_type.has_unbound_variables()
953            }
954            Type::Forall { body, .. } => body.has_unbound_variables(),
955            Type::Tuple(elements) => elements.iter().any(|e| e.has_unbound_variables()),
956            Type::Compound { args, .. } => args.iter().any(|a| a.has_unbound_variables()),
957            Type::Simple(_)
958            | Type::Parameter(_)
959            | Type::Never
960            | Type::Error
961            | Type::ImportNamespace(_)
962            | Type::ReceiverPlaceholder => false,
963        }
964    }
965
966    pub fn remove_found_type_names(&self, names: &mut HashSet<EcoString>) {
967        if names.is_empty() {
968            return;
969        }
970
971        match self {
972            Type::Nominal { id, params, .. } => {
973                names.remove(id.last_segment());
974                for param in params {
975                    param.remove_found_type_names(names);
976                }
977            }
978            Type::Function {
979                params,
980                return_type,
981                bounds,
982                ..
983            } => {
984                for param in params {
985                    param.remove_found_type_names(names);
986                }
987                return_type.remove_found_type_names(names);
988                for bound in bounds {
989                    bound.generic.remove_found_type_names(names);
990                    bound.ty.remove_found_type_names(names);
991                }
992            }
993            Type::Forall { body, .. } => {
994                body.remove_found_type_names(names);
995            }
996            Type::Var { .. } => {}
997            Type::Parameter(name) => {
998                names.remove(name);
999            }
1000            Type::Tuple(elements) => {
1001                for element in elements {
1002                    element.remove_found_type_names(names);
1003                }
1004            }
1005            Type::Compound { kind, args } => {
1006                names.remove(kind.leaf_name());
1007                for arg in args {
1008                    arg.remove_found_type_names(names);
1009                }
1010            }
1011            Type::Simple(kind) => {
1012                names.remove(kind.leaf_name());
1013            }
1014            Type::Never | Type::Error | Type::ImportNamespace(_) | Type::ReceiverPlaceholder => {}
1015        }
1016    }
1017}
1018
1019impl Type {
1020    pub fn get_name(&self) -> Option<&str> {
1021        match self {
1022            Type::Simple(kind) => Some(kind.leaf_name()),
1023            Type::Compound { kind, args } => match kind {
1024                CompoundKind::Ref => args.first().and_then(|inner| inner.get_name()),
1025                _ => Some(kind.leaf_name()),
1026            },
1027            Type::Nominal { id, params, .. } => {
1028                let name = id.last_segment();
1029                if CompoundKind::from_name(name) == Some(CompoundKind::Ref) {
1030                    return params.first().and_then(|inner| inner.get_name());
1031                }
1032                Some(name)
1033            }
1034            Type::ImportNamespace(module_id) => {
1035                let path = module_id.strip_prefix("go:").unwrap_or(module_id);
1036                path.rsplit('/').next()
1037            }
1038            _ => None,
1039        }
1040    }
1041
1042    pub fn wraps(&self, name: &str, inner: &Type) -> bool {
1043        self.get_name().is_some_and(|n| n == name)
1044            && self
1045                .get_type_params()
1046                .and_then(|p| p.first())
1047                .is_some_and(|first| *first == *inner)
1048    }
1049
1050    pub fn get_function_params(&self) -> Option<&[Type]> {
1051        match self {
1052            Type::Function { params, .. } => Some(params),
1053            Type::Nominal {
1054                underlying_ty: Some(inner),
1055                ..
1056            } => inner.get_function_params(),
1057            _ => None,
1058        }
1059    }
1060
1061    pub fn param_count(&self) -> usize {
1062        match self {
1063            Type::Function { params, .. } => params.len(),
1064            _ => 0,
1065        }
1066    }
1067
1068    pub fn get_param_mutability(&self) -> &[bool] {
1069        match self {
1070            Type::Function {
1071                param_mutability, ..
1072            } => param_mutability,
1073            _ => &[],
1074        }
1075    }
1076
1077    pub fn with_replaced_first_param(&self, new_first: &Type) -> Type {
1078        match self {
1079            Type::Function {
1080                params,
1081                param_mutability,
1082                bounds,
1083                return_type,
1084            } => {
1085                if params.is_empty() {
1086                    return self.clone();
1087                }
1088                let mut new_params = params.clone();
1089                new_params[0] = new_first.clone();
1090                Type::Function {
1091                    params: new_params,
1092                    param_mutability: param_mutability.clone(),
1093                    bounds: bounds.clone(),
1094                    return_type: return_type.clone(),
1095                }
1096            }
1097            Type::Forall { vars, body } => Type::Forall {
1098                vars: vars.clone(),
1099                body: Box::new(body.with_replaced_first_param(new_first)),
1100            },
1101            _ => self.clone(),
1102        }
1103    }
1104
1105    pub fn get_bounds(&self) -> &[Bound] {
1106        match self {
1107            Type::Function { bounds, .. } => bounds,
1108            Type::Forall { body, .. } => body.get_bounds(),
1109            _ => &[],
1110        }
1111    }
1112
1113    pub fn get_qualified_name(&self) -> Symbol {
1114        match self.strip_refs() {
1115            Type::Nominal { id, .. } => id,
1116            Type::Simple(kind) => Symbol::from_parts("prelude", kind.leaf_name()),
1117            Type::Compound { kind, .. } => Symbol::from_parts("prelude", kind.leaf_name()),
1118            _ => panic!("called get_qualified_name on {:#?}", self),
1119        }
1120    }
1121
1122    pub fn inner(&self) -> Option<Type> {
1123        self.get_type_params()
1124            .and_then(|args| args.first().cloned())
1125    }
1126
1127    pub fn ok_type(&self) -> Type {
1128        debug_assert!(
1129            self.is_result() || self.is_option() || self.is_partial(),
1130            "ok_type called on non-Result/Option/Partial type"
1131        );
1132        self.inner()
1133            .expect("Result/Option/Partial should have inner type")
1134    }
1135
1136    pub fn err_type(&self) -> Type {
1137        debug_assert!(
1138            self.is_result() || self.is_partial(),
1139            "err_type called on non-Result/Partial type"
1140        );
1141        self.get_type_params()
1142            .and_then(|args| args.get(1).cloned())
1143            .expect("Result/Partial should have error type")
1144    }
1145}
1146
1147impl Type {
1148    pub fn unwrap_forall(&self) -> &Type {
1149        match self {
1150            Type::Forall { body, .. } => body.as_ref(),
1151            other => other,
1152        }
1153    }
1154
1155    pub fn strip_refs(&self) -> Type {
1156        if self.is_ref() {
1157            return self.inner().expect("ref type must have inner").strip_refs();
1158        }
1159
1160        self.clone()
1161    }
1162
1163    pub fn with_receiver_placeholder(self) -> Type {
1164        match self {
1165            Type::Function {
1166                params,
1167                param_mutability,
1168                bounds,
1169                return_type,
1170            } => {
1171                let mut new_params = vec![Type::ReceiverPlaceholder];
1172                new_params.extend(params);
1173
1174                let mut new_mutability = vec![false];
1175                new_mutability.extend(param_mutability);
1176
1177                Type::Function {
1178                    params: new_params,
1179                    param_mutability: new_mutability,
1180                    bounds,
1181                    return_type,
1182                }
1183            }
1184            _ => unreachable!(
1185                "with_receiver_placeholder called on non-function type: {:?}",
1186                self
1187            ),
1188        }
1189    }
1190
1191    pub fn remove_vars(types: &[&Type]) -> (Vec<Type>, Vec<EcoString>) {
1192        let mut vars = HashMap::default();
1193        let types = types
1194            .iter()
1195            .map(|v| Self::remove_vars_impl(v, &mut vars))
1196            .collect();
1197
1198        (types, vars.into_values().collect())
1199    }
1200
1201    fn remove_vars_impl(ty: &Type, vars: &mut HashMap<u32, EcoString>) -> Type {
1202        match ty {
1203            Type::Nominal {
1204                id: name,
1205                params: args,
1206                underlying_ty: underlying,
1207            } => Type::Nominal {
1208                id: name.clone(),
1209                params: args
1210                    .iter()
1211                    .map(|a| Self::remove_vars_impl(a, vars))
1212                    .collect(),
1213                underlying_ty: underlying
1214                    .as_ref()
1215                    .map(|u| Box::new(Self::remove_vars_impl(u, vars))),
1216            },
1217
1218            Type::Function {
1219                params: args,
1220                param_mutability,
1221                bounds,
1222                return_type,
1223            } => Type::Function {
1224                params: args
1225                    .iter()
1226                    .map(|a| Self::remove_vars_impl(a, vars))
1227                    .collect(),
1228                param_mutability: param_mutability.clone(),
1229                bounds: bounds
1230                    .iter()
1231                    .map(|b| Bound {
1232                        param_name: b.param_name.clone(),
1233                        generic: Self::remove_vars_impl(&b.generic, vars),
1234                        ty: Self::remove_vars_impl(&b.ty, vars),
1235                    })
1236                    .collect(),
1237                return_type: Self::remove_vars_impl(return_type, vars).into(),
1238            },
1239
1240            Type::Var { id, hint } => match vars.get(&id.0) {
1241                Some(g) => Type::Parameter(g.clone()),
1242                None => {
1243                    let name: EcoString = hint.clone().unwrap_or_else(|| {
1244                        char::from_digit(
1245                            (vars.len() + 10)
1246                                .try_into()
1247                                .expect("type var count fits in u32"),
1248                            16,
1249                        )
1250                        .expect("type var index is valid hex digit")
1251                        .to_uppercase()
1252                        .to_string()
1253                        .into()
1254                    });
1255
1256                    vars.insert(id.0, name.clone());
1257                    Type::Parameter(name)
1258                }
1259            },
1260
1261            Type::Forall { body, .. } => Self::remove_vars_impl(body, vars),
1262            Type::Tuple(elements) => Type::Tuple(
1263                elements
1264                    .iter()
1265                    .map(|e| Self::remove_vars_impl(e, vars))
1266                    .collect(),
1267            ),
1268            Type::Compound { kind, args } => Type::Compound {
1269                kind: *kind,
1270                args: args
1271                    .iter()
1272                    .map(|a| Self::remove_vars_impl(a, vars))
1273                    .collect(),
1274            },
1275            Type::Simple(_) | Type::Parameter(_) => ty.clone(),
1276            Type::Never | Type::Error | Type::ImportNamespace(_) | Type::ReceiverPlaceholder => {
1277                ty.clone()
1278            }
1279        }
1280    }
1281
1282    pub fn contains_type(&self, target: &Type) -> bool {
1283        if *self == *target {
1284            return true;
1285        }
1286        match self {
1287            Type::Nominal { params, .. } => params.iter().any(|p| p.contains_type(target)),
1288            Type::Function {
1289                params,
1290                return_type,
1291                ..
1292            } => {
1293                params.iter().any(|p| p.contains_type(target)) || return_type.contains_type(target)
1294            }
1295            Type::Var { .. } => false,
1296            Type::Forall { body, .. } => body.contains_type(target),
1297            Type::Tuple(elements) => elements.iter().any(|e| e.contains_type(target)),
1298            Type::Compound { args, .. } => args.iter().any(|a| a.contains_type(target)),
1299            Type::Simple(_)
1300            | Type::Parameter(_)
1301            | Type::Never
1302            | Type::Error
1303            | Type::ImportNamespace(_)
1304            | Type::ReceiverPlaceholder => false,
1305        }
1306    }
1307}
1308
1309impl Type {
1310    pub fn underlying_numeric_type(&self) -> Option<Type> {
1311        self.underlying_numeric_type_recursive(&mut HashSet::default())
1312    }
1313
1314    pub fn has_underlying_numeric_type(&self) -> bool {
1315        self.underlying_numeric_type().is_some()
1316    }
1317
1318    fn underlying_numeric_type_recursive(&self, visited: &mut HashSet<Symbol>) -> Option<Type> {
1319        match self {
1320            Type::Simple(_) if self.is_numeric() => Some(self.clone()),
1321            Type::Nominal {
1322                id,
1323                underlying_ty: underlying,
1324                ..
1325            } => {
1326                if self.is_numeric() {
1327                    return Some(self.clone());
1328                }
1329
1330                if !visited.insert(id.clone()) {
1331                    return None;
1332                }
1333
1334                underlying
1335                    .as_ref()?
1336                    .underlying_numeric_type_recursive(visited)
1337            }
1338            _ => None,
1339        }
1340    }
1341
1342    pub fn numeric_family(&self) -> Option<NumericFamily> {
1343        self.as_simple()?.numeric_family()
1344    }
1345
1346    pub fn is_numeric_compatible_with(&self, other: &Type) -> bool {
1347        let self_underlying_ty = self.underlying_numeric_type();
1348        let other_underlying_ty = other.underlying_numeric_type();
1349
1350        match (self_underlying_ty, other_underlying_ty) {
1351            (Some(s), Some(o)) => s.numeric_family() == o.numeric_family(),
1352            _ => false,
1353        }
1354    }
1355
1356    pub fn is_aliased_numeric_type(&self) -> bool {
1357        match self {
1358            Type::Nominal { underlying_ty, .. } => {
1359                underlying_ty.is_some() && !self.is_numeric() && self.has_underlying_numeric_type()
1360            }
1361            _ => false,
1362        }
1363    }
1364}