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;
4use std::sync::Arc;
5
6use ecow::EcoString;
7
8use crate::ast::Generic;
9
10/// Dot-qualified identifier for a named type, method, value, or variant.
11///
12/// Wraps the qualified name (`"main.Point.sum"`, `"prelude.Option"`,
13/// `"go:net/http.Handler"`) as a single `EcoString` and exposes structured
14/// accessors. Centralizes the join/split logic that used to live in ad-hoc
15/// `format!("{}.{}", ..)` and `split_once('.')` call sites.
16#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
17#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
18pub struct Symbol(EcoString);
19
20impl Symbol {
21    /// Joins a module id and a local (possibly multi-segment) name.
22    ///
23    /// `Symbol::from_parts("main", "Point.sum")` → `"main.Point.sum"`.
24    pub fn from_parts(module: &str, local: &str) -> Self {
25        // Build straight into the EcoString: results up to its 15-byte inline
26        // limit never touch the heap, and longer ones allocate once instead of
27        // twice (a temporary `String` plus the `EcoString` copy).
28        let mut s = EcoString::with_capacity(module.len() + 1 + local.len());
29        s.push_str(module);
30        s.push('.');
31        s.push_str(local);
32        Self(s)
33    }
34
35    /// Appends an additional dot-segment to an already-qualified symbol.
36    ///
37    /// `Symbol::from_raw("main.Shape").with_segment("Circle")` →
38    /// `"main.Shape.Circle"`.
39    pub fn with_segment(&self, segment: &str) -> Self {
40        let mut s = EcoString::with_capacity(self.0.len() + 1 + segment.len());
41        s.push_str(&self.0);
42        s.push('.');
43        s.push_str(segment);
44        Self(s)
45    }
46
47    /// Wraps an already-constructed qualified string. Prefer `from_parts`
48    /// when the module id and local name are available separately.
49    pub fn from_raw(qualified: impl Into<EcoString>) -> Self {
50        Self(qualified.into())
51    }
52
53    pub fn as_str(&self) -> &str {
54        &self.0
55    }
56
57    pub fn as_eco(&self) -> &EcoString {
58        &self.0
59    }
60
61    /// Last dot-separated segment. `"main.Point.sum"` → `"sum"`.
62    pub fn last_segment(&self) -> &str {
63        self.0.rsplit('.').next().unwrap_or(&self.0)
64    }
65
66    /// Strips the last dot-separated segment. `"main.Point.sum"` → `"main.Point"`.
67    /// Returns `None` if the symbol has no dot.
68    pub fn without_last_segment(&self) -> Option<&str> {
69        self.0.rsplit_once('.').map(|(rest, _)| rest)
70    }
71}
72
73impl Borrow<str> for Symbol {
74    fn borrow(&self) -> &str {
75        &self.0
76    }
77}
78
79impl AsRef<str> for Symbol {
80    fn as_ref(&self) -> &str {
81        &self.0
82    }
83}
84
85impl std::ops::Deref for Symbol {
86    type Target = str;
87
88    fn deref(&self) -> &str {
89        &self.0
90    }
91}
92
93impl From<&Symbol> for EcoString {
94    fn from(s: &Symbol) -> Self {
95        s.0.clone()
96    }
97}
98
99impl std::fmt::Display for Symbol {
100    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
101        self.0.fmt(f)
102    }
103}
104
105impl From<EcoString> for Symbol {
106    fn from(s: EcoString) -> Self {
107        Self(s)
108    }
109}
110
111impl From<Symbol> for EcoString {
112    fn from(s: Symbol) -> Self {
113        s.0
114    }
115}
116
117impl From<&str> for Symbol {
118    fn from(s: &str) -> Self {
119        Self(EcoString::from(s))
120    }
121}
122
123impl From<String> for Symbol {
124    fn from(s: String) -> Self {
125        Self(EcoString::from(s))
126    }
127}
128
129impl PartialEq<str> for Symbol {
130    fn eq(&self, other: &str) -> bool {
131        self.0.as_str() == other
132    }
133}
134
135impl PartialEq<&str> for Symbol {
136    fn eq(&self, other: &&str) -> bool {
137        self.0.as_str() == *other
138    }
139}
140
141/// Extract the unqualified name from a dot-qualified identifier.
142///
143/// `"prelude.Option"` → `"Option"`, `"**nominal.int"` → `"int"`, `"foo"` → `"foo"`
144pub fn unqualified_name(id: &str) -> &str {
145    id.rsplit('.').next().unwrap_or(id)
146}
147
148pub const GO_IMPORT_PREFIX: &str = "go:";
149
150/// Resolve the module of a qualified ID. For `go:` IDs containing `/`,
151/// does a longest-prefix match against `module_ids` to disambiguate paths
152/// whose module segment contains dots (e.g. `gopkg.in/yaml.v3`). Otherwise
153/// splits on the first dot. Returns `None` when the id has no dot and is
154/// not a registered `go:` module.
155pub fn module_for_qualified_name<'a, I>(id: &'a str, module_ids: I) -> Option<&'a str>
156where
157    I: IntoIterator<Item = &'a str>,
158{
159    if !id.starts_with(GO_IMPORT_PREFIX) || !id.contains('/') {
160        return id.split_once('.').map(|(m, _)| m);
161    }
162    let mut best: Option<&str> = None;
163    for module_id in module_ids {
164        if id.starts_with(module_id)
165            && id.as_bytes().get(module_id.len()) == Some(&b'.')
166            && best.is_none_or(|prev| module_id.len() > prev.len())
167        {
168            best = Some(module_id);
169        }
170    }
171    best
172}
173
174pub fn is_range_type_name(name: &str) -> bool {
175    matches!(
176        name,
177        "Range" | "RangeInclusive" | "RangeFrom" | "RangeTo" | "RangeToInclusive"
178    )
179}
180
181pub fn peel_to_range_type(ty: &Type) -> Option<&Type> {
182    std::iter::successors(Some(ty), |t| match t {
183        Type::Nominal {
184            underlying_ty: Some(u),
185            ..
186        } => Some(u.as_ref()),
187        _ => None,
188    })
189    .find(|t| t.get_name().is_some_and(is_range_type_name))
190}
191
192/// type param name -> type variable
193pub type SubstitutionMap = HashMap<EcoString, Type>;
194
195/// Build a substitution map from a list of generics and their type arguments,
196/// pairing each generic's name with the type at the same position.
197pub fn build_substitution_map(generics: &[Generic], type_args: &[Type]) -> SubstitutionMap {
198    generics
199        .iter()
200        .zip(type_args.iter())
201        .map(|(g, t)| (g.name.clone(), t.clone()))
202        .collect()
203}
204
205pub fn type_args_match_params<'a>(
206    args: &[Type],
207    params: impl ExactSizeIterator<Item = &'a EcoString>,
208) -> bool {
209    args.len() == params.len()
210        && args
211            .iter()
212            .zip(params)
213            .all(|(arg, param)| matches!(arg, Type::Parameter(name) if name == param))
214}
215
216pub fn substitute(ty: &Type, map: &HashMap<EcoString, Type>) -> Type {
217    if map.is_empty() {
218        return ty.clone();
219    }
220    match ty {
221        Type::Parameter(name) => map.get(name).cloned().unwrap_or_else(|| ty.clone()),
222        Type::Nominal {
223            id,
224            params,
225            underlying_ty: underlying,
226        } => Type::Nominal {
227            id: id.clone(),
228            params: params.iter().map(|p| substitute(p, map)).collect(),
229            underlying_ty: underlying.as_ref().map(|u| Box::new(substitute(u, map))),
230        },
231        Type::Function(f) => f.rebuild(
232            f.params.iter().map(|p| substitute(p, map)).collect(),
233            f.bounds
234                .iter()
235                .map(|b| Bound {
236                    param_name: b.param_name.clone(),
237                    generic: substitute(&b.generic, map),
238                    ty: substitute(&b.ty, map),
239                })
240                .collect(),
241            Box::new(substitute(&f.return_type, map)),
242        ),
243        Type::Var { .. } | Type::Error => ty.clone(),
244        Type::Forall { vars, body } => {
245            let has_overlap = map.keys().any(|k| vars.contains(k));
246            let substituted_body = if has_overlap {
247                let filtered_map: HashMap<EcoString, Type> = map
248                    .iter()
249                    .filter(|(k, _)| !vars.contains(*k))
250                    .map(|(k, v)| (k.clone(), v.clone()))
251                    .collect();
252                substitute(body, &filtered_map)
253            } else {
254                substitute(body, map)
255            };
256            Type::Forall {
257                vars: vars.clone(),
258                body: Box::new(substituted_body),
259            }
260        }
261        Type::Tuple(elements) => Type::Tuple(elements.iter().map(|e| substitute(e, map)).collect()),
262        Type::Compound { kind, args } => Type::Compound {
263            kind: *kind,
264            args: args.iter().map(|a| substitute(a, map)).collect(),
265        },
266        Type::Simple(_) | Type::Never | Type::ImportNamespace(_) | Type::ReceiverPlaceholder => {
267            ty.clone()
268        }
269    }
270}
271
272#[derive(Debug, Clone, PartialEq)]
273#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
274pub struct Bound {
275    pub param_name: EcoString,
276    pub generic: Type,
277    pub ty: Type,
278}
279
280#[derive(Clone)]
281#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
282pub struct FunctionType {
283    pub params: Vec<Type>,
284    #[cfg_attr(feature = "serde", serde(default))]
285    pub param_names: Vec<Option<EcoString>>,
286    pub param_mutability: Vec<bool>,
287    pub bounds: Vec<Bound>,
288    pub return_type: Box<Type>,
289}
290
291impl PartialEq for FunctionType {
292    fn eq(&self, other: &Self) -> bool {
293        self.params == other.params
294            && self.param_mutability == other.param_mutability
295            && self.bounds == other.bounds
296            && self.return_type == other.return_type
297    }
298}
299
300impl FunctionType {
301    pub fn remove_receiver(&mut self) -> Type {
302        let receiver = self.params.remove(0);
303        if !self.param_mutability.is_empty() {
304            self.param_mutability.remove(0);
305        }
306        if !self.param_names.is_empty() {
307            self.param_names.remove(0);
308        }
309        receiver
310    }
311
312    pub fn without_receiver(&self) -> Type {
313        let mut stripped = self.clone();
314        if !stripped.params.is_empty() {
315            stripped.remove_receiver();
316        }
317        Type::Function(Arc::new(stripped))
318    }
319
320    pub fn rebuild(&self, params: Vec<Type>, bounds: Vec<Bound>, return_type: Box<Type>) -> Type {
321        debug_assert!(
322            self.param_names.is_empty() || self.param_names.len() == params.len(),
323            "rebuild changed arity: param_names would misalign with params"
324        );
325        debug_assert!(
326            self.param_mutability.is_empty() || self.param_mutability.len() == params.len(),
327            "rebuild changed arity: param_mutability would misalign with params"
328        );
329        Type::function_with_names(
330            params,
331            self.param_names.clone(),
332            self.param_mutability.clone(),
333            bounds,
334            return_type,
335        )
336    }
337}
338
339/// A unique handle identifying a type variable. The binding state (Unbound /
340/// Bound-to-a-Type) lives in a `TypeEnv` owned by the checker; the handle is
341/// a plain id so `Type` stays a pure value (Clone, Eq, Hash, Serialize).
342#[derive(Clone, Copy, PartialEq, Eq, Hash)]
343#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
344pub struct TypeVarId(pub u32);
345
346impl TypeVarId {
347    pub const IGNORED: TypeVarId = TypeVarId(u32::MAX);
348    pub const UNINFERRED: TypeVarId = TypeVarId(u32::MAX - 1);
349
350    pub fn is_reserved(self) -> bool {
351        self == Self::IGNORED || self == Self::UNINFERRED
352    }
353
354    pub fn as_u32(self) -> u32 {
355        self.0
356    }
357}
358
359impl std::fmt::Debug for TypeVarId {
360    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
361        match *self {
362            Self::IGNORED => write!(f, "ignored"),
363            Self::UNINFERRED => write!(f, "uninferred"),
364            TypeVarId(n) => write!(f, "#{}", n),
365        }
366    }
367}
368
369#[derive(Clone)]
370#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
371pub enum Type {
372    Simple(SimpleKind),
373
374    Compound {
375        kind: CompoundKind,
376        args: Vec<Type>,
377    },
378
379    Nominal {
380        id: Symbol,
381        params: Vec<Type>,
382        underlying_ty: Option<Box<Type>>,
383    },
384
385    /// Module namespace handle. Produced by imports (e.g. `import http "net/http"`
386    /// produces an `ImportNamespace("go:net/http")` on the local identifier).
387    /// Dot-access on this type resolves to the module's exports.
388    ImportNamespace(EcoString),
389
390    Function(Arc<FunctionType>),
391
392    /// Type variable handle. Binding state lives in a `TypeEnv` owned by the
393    /// checker; the inline `hint` is display metadata set at allocation time
394    /// so `Display`/`Debug` work without env access.
395    Var {
396        id: TypeVarId,
397        hint: Option<EcoString>,
398    },
399
400    Forall {
401        vars: Vec<EcoString>,
402        body: Box<Type>,
403    },
404
405    Parameter(EcoString),
406
407    Never,
408
409    Tuple(Vec<Type>),
410
411    /// Poison type returned after an error has been reported.
412    /// Unifies with everything silently, preventing cascading diagnostics.
413    Error,
414
415    /// Sentinel occupying the `self` slot of an interface method type.
416    /// Unifies silently so an implementing type's receiver does not conflict
417    /// with the abstract method shape. Previously encoded as
418    /// `Constructor { id: "**nominal.__receiver__" }`.
419    ReceiverPlaceholder,
420}
421
422impl std::fmt::Debug for Type {
423    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
424        match self {
425            Type::Nominal { id, params, .. } => f
426                .debug_struct("Nominal")
427                .field("id", id)
428                .field("params", params)
429                .finish(),
430            Type::Function(f_ty) => {
431                let mut s = f.debug_struct("Function");
432                s.field("params", &f_ty.params);
433                if f_ty.param_mutability.iter().any(|m| *m) {
434                    s.field("param_mutability", &f_ty.param_mutability);
435                }
436                s.field("bounds", &f_ty.bounds)
437                    .field("return_type", &f_ty.return_type)
438                    .finish()
439            }
440            Type::Var { id, hint } => {
441                let mut s = f.debug_struct("Var");
442                s.field("id", id);
443                if let Some(h) = hint {
444                    s.field("hint", h);
445                }
446                s.finish()
447            }
448            Type::Forall { vars, body } => f
449                .debug_struct("Forall")
450                .field("vars", vars)
451                .field("body", body)
452                .finish(),
453            Type::Parameter(name) => f.debug_tuple("Parameter").field(name).finish(),
454            Type::Never => write!(f, "Never"),
455            Type::Tuple(elements) => f.debug_tuple("Tuple").field(elements).finish(),
456            Type::Error => write!(f, "Error"),
457            Type::ImportNamespace(module_id) => {
458                f.debug_tuple("ImportNamespace").field(module_id).finish()
459            }
460            Type::ReceiverPlaceholder => write!(f, "ReceiverPlaceholder"),
461            Type::Simple(kind) => f.debug_tuple("Simple").field(kind).finish(),
462            Type::Compound { kind, args } => f
463                .debug_struct("Compound")
464                .field("kind", kind)
465                .field("args", args)
466                .finish(),
467        }
468    }
469}
470
471impl PartialEq for Type {
472    fn eq(&self, other: &Self) -> bool {
473        match (self, other) {
474            (
475                Type::Nominal {
476                    id: id1,
477                    params: params1,
478                    ..
479                },
480                Type::Nominal {
481                    id: id2,
482                    params: params2,
483                    ..
484                },
485            ) => id1 == id2 && params1 == params2,
486            (Type::Function(f1), Type::Function(f2)) => f1 == f2,
487            (Type::Var { id: id1, .. }, Type::Var { id: id2, .. }) => id1 == id2,
488            (
489                Type::Forall {
490                    vars: vars1,
491                    body: body1,
492                },
493                Type::Forall {
494                    vars: vars2,
495                    body: body2,
496                },
497            ) => vars1 == vars2 && body1 == body2,
498            (Type::Parameter(name1), Type::Parameter(name2)) => name1 == name2,
499            (Type::Never, Type::Never) => true,
500            (Type::Tuple(elems1), Type::Tuple(elems2)) => elems1 == elems2,
501            (Type::ImportNamespace(m1), Type::ImportNamespace(m2)) => m1 == m2,
502            (Type::ReceiverPlaceholder, Type::ReceiverPlaceholder) => true,
503            (Type::Simple(k1), Type::Simple(k2)) => k1 == k2,
504            (Type::Compound { kind: k1, args: a1 }, Type::Compound { kind: k2, args: a2 }) => {
505                k1 == k2 && a1 == a2
506            }
507            _ => false,
508        }
509    }
510}
511
512thread_local! {
513    static INTERNED_INT: OnceCell<Type> = const { OnceCell::new() };
514    static INTERNED_STRING: OnceCell<Type> = const { OnceCell::new() };
515    static INTERNED_BOOL: OnceCell<Type> = const { OnceCell::new() };
516    static INTERNED_UNIT: OnceCell<Type> = const { OnceCell::new() };
517    static INTERNED_FLOAT64: OnceCell<Type> = const { OnceCell::new() };
518    static INTERNED_RUNE: OnceCell<Type> = const { OnceCell::new() };
519    static INTERNED_BYTE: OnceCell<Type> = const { OnceCell::new() };
520}
521
522impl Type {
523    pub fn simple(kind: SimpleKind) -> Type {
524        Self::Simple(kind)
525    }
526
527    pub fn compound(kind: CompoundKind, args: Vec<Type>) -> Type {
528        Self::Compound { kind, args }
529    }
530
531    pub fn function(
532        params: Vec<Type>,
533        param_mutability: Vec<bool>,
534        bounds: Vec<Bound>,
535        return_type: Box<Type>,
536    ) -> Type {
537        Self::function_with_names(params, Vec::new(), param_mutability, bounds, return_type)
538    }
539
540    pub fn function_with_names(
541        params: Vec<Type>,
542        param_names: Vec<Option<EcoString>>,
543        param_mutability: Vec<bool>,
544        bounds: Vec<Bound>,
545        return_type: Box<Type>,
546    ) -> Type {
547        Type::Function(Arc::new(FunctionType {
548            params,
549            param_names,
550            param_mutability,
551            bounds,
552            return_type,
553        }))
554    }
555
556    pub fn int() -> Type {
557        INTERNED_INT.with(|cell| cell.get_or_init(|| Self::simple(SimpleKind::Int)).clone())
558    }
559
560    pub fn string() -> Type {
561        INTERNED_STRING.with(|cell| {
562            cell.get_or_init(|| Self::simple(SimpleKind::String))
563                .clone()
564        })
565    }
566
567    pub fn bool() -> Type {
568        INTERNED_BOOL.with(|cell| cell.get_or_init(|| Self::simple(SimpleKind::Bool)).clone())
569    }
570
571    pub fn unit() -> Type {
572        INTERNED_UNIT.with(|cell| cell.get_or_init(|| Self::simple(SimpleKind::Unit)).clone())
573    }
574
575    pub fn float64() -> Type {
576        INTERNED_FLOAT64.with(|cell| {
577            cell.get_or_init(|| Self::simple(SimpleKind::Float64))
578                .clone()
579        })
580    }
581
582    pub fn rune() -> Type {
583        INTERNED_RUNE.with(|cell| cell.get_or_init(|| Self::simple(SimpleKind::Rune)).clone())
584    }
585
586    pub fn byte() -> Type {
587        INTERNED_BYTE.with(|cell| cell.get_or_init(|| Self::simple(SimpleKind::Byte)).clone())
588    }
589}
590
591impl Type {
592    pub fn uninferred() -> Self {
593        Self::Var {
594            id: TypeVarId::UNINFERRED,
595            hint: None,
596        }
597    }
598
599    pub fn ignored() -> Self {
600        Self::Var {
601            id: TypeVarId::IGNORED,
602            hint: None,
603        }
604    }
605
606    pub fn get_type_params(&self) -> Option<&[Type]> {
607        match self {
608            Type::Nominal { params, .. } => Some(params),
609            Type::Compound { args, .. } => Some(args),
610            _ => None,
611        }
612    }
613
614    /// Direct child types, for read-only walks. Excludes `Function.bounds`.
615    pub fn children(&self) -> Vec<&Type> {
616        match self {
617            Type::Nominal {
618                params,
619                underlying_ty,
620                ..
621            } => {
622                let mut c: Vec<&Type> = params.iter().collect();
623                if let Some(u) = underlying_ty {
624                    c.push(u);
625                }
626                c
627            }
628            Type::Compound { args, .. } => args.iter().collect(),
629            Type::Function(f) => {
630                let mut c: Vec<&Type> = f.params.iter().collect();
631                c.push(&f.return_type);
632                c
633            }
634            Type::Tuple(elements) => elements.iter().collect(),
635            Type::Forall { body, .. } => vec![body],
636            _ => vec![],
637        }
638    }
639}
640
641#[derive(Debug, Clone, Copy, PartialEq, Eq)]
642pub enum NumericFamily {
643    SignedInt,
644    UnsignedInt,
645    Float,
646}
647
648#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
649#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
650pub enum CompoundKind {
651    Ref,
652    Slice,
653    EnumeratedSlice,
654    Map,
655    Channel,
656    Sender,
657    Receiver,
658    VarArgs,
659}
660
661impl CompoundKind {
662    pub fn leaf_name(self) -> &'static str {
663        match self {
664            CompoundKind::Ref => "Ref",
665            CompoundKind::Slice => "Slice",
666            CompoundKind::EnumeratedSlice => "EnumeratedSlice",
667            CompoundKind::Map => "Map",
668            CompoundKind::Channel => "Channel",
669            CompoundKind::Sender => "Sender",
670            CompoundKind::Receiver => "Receiver",
671            CompoundKind::VarArgs => "VarArgs",
672        }
673    }
674
675    pub fn from_name(name: &str) -> Option<CompoundKind> {
676        Some(match name {
677            "Ref" => CompoundKind::Ref,
678            "Slice" => CompoundKind::Slice,
679            "EnumeratedSlice" => CompoundKind::EnumeratedSlice,
680            "Map" => CompoundKind::Map,
681            "Channel" => CompoundKind::Channel,
682            "Sender" => CompoundKind::Sender,
683            "Receiver" => CompoundKind::Receiver,
684            "VarArgs" => CompoundKind::VarArgs,
685            _ => return None,
686        })
687    }
688
689    pub fn from_qualified_id(id: &str) -> Option<CompoundKind> {
690        Self::from_name(id.strip_prefix("prelude.")?)
691    }
692}
693
694#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
695#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
696pub enum SimpleKind {
697    Int,
698    Int8,
699    Int16,
700    Int32,
701    Int64,
702    Uint,
703    Uint8,
704    Uint16,
705    Uint32,
706    Uint64,
707    Uintptr,
708    Byte,
709    Float32,
710    Float64,
711    Complex64,
712    Complex128,
713    Rune,
714    Bool,
715    String,
716    Unit,
717}
718
719impl SimpleKind {
720    pub fn leaf_name(self) -> &'static str {
721        match self {
722            SimpleKind::Int => "int",
723            SimpleKind::Int8 => "int8",
724            SimpleKind::Int16 => "int16",
725            SimpleKind::Int32 => "int32",
726            SimpleKind::Int64 => "int64",
727            SimpleKind::Uint => "uint",
728            SimpleKind::Uint8 => "uint8",
729            SimpleKind::Uint16 => "uint16",
730            SimpleKind::Uint32 => "uint32",
731            SimpleKind::Uint64 => "uint64",
732            SimpleKind::Uintptr => "uintptr",
733            SimpleKind::Byte => "byte",
734            SimpleKind::Float32 => "float32",
735            SimpleKind::Float64 => "float64",
736            SimpleKind::Complex64 => "complex64",
737            SimpleKind::Complex128 => "complex128",
738            SimpleKind::Rune => "rune",
739            SimpleKind::Bool => "bool",
740            SimpleKind::String => "string",
741            SimpleKind::Unit => "Unit",
742        }
743    }
744
745    pub fn from_name(name: &str) -> Option<SimpleKind> {
746        Some(match name {
747            "int" => SimpleKind::Int,
748            "int8" => SimpleKind::Int8,
749            "int16" => SimpleKind::Int16,
750            "int32" => SimpleKind::Int32,
751            "int64" => SimpleKind::Int64,
752            "uint" => SimpleKind::Uint,
753            "uint8" => SimpleKind::Uint8,
754            "uint16" => SimpleKind::Uint16,
755            "uint32" => SimpleKind::Uint32,
756            "uint64" => SimpleKind::Uint64,
757            "uintptr" => SimpleKind::Uintptr,
758            "byte" => SimpleKind::Byte,
759            "float32" => SimpleKind::Float32,
760            "float64" => SimpleKind::Float64,
761            "complex64" => SimpleKind::Complex64,
762            "complex128" => SimpleKind::Complex128,
763            "rune" => SimpleKind::Rune,
764            "bool" => SimpleKind::Bool,
765            "string" => SimpleKind::String,
766            "Unit" => SimpleKind::Unit,
767            _ => return None,
768        })
769    }
770
771    pub fn is_arithmetic(self) -> bool {
772        !matches!(
773            self,
774            SimpleKind::Bool | SimpleKind::String | SimpleKind::Unit | SimpleKind::Uintptr
775        )
776    }
777
778    pub fn is_ordered(self) -> bool {
779        self.is_arithmetic() && !matches!(self, SimpleKind::Complex64 | SimpleKind::Complex128)
780    }
781
782    pub fn integer_range(self) -> Option<(i128, i128)> {
783        use SimpleKind::*;
784        Some(match self {
785            Int8 => (i8::MIN as i128, i8::MAX as i128),
786            Int16 => (i16::MIN as i128, i16::MAX as i128),
787            Int32 | Rune => (i32::MIN as i128, i32::MAX as i128),
788            Int | Int64 => (i64::MIN as i128, i64::MAX as i128),
789            Uint8 | Byte => (0, u8::MAX as i128),
790            Uint16 => (0, u16::MAX as i128),
791            Uint32 => (0, u32::MAX as i128),
792            Uint | Uint64 | Uintptr => (0, u64::MAX as i128),
793            _ => return None,
794        })
795    }
796
797    pub fn is_unsigned_int(self) -> bool {
798        matches!(
799            self,
800            SimpleKind::Byte
801                | SimpleKind::Uint
802                | SimpleKind::Uint8
803                | SimpleKind::Uint16
804                | SimpleKind::Uint32
805                | SimpleKind::Uint64
806        )
807    }
808
809    pub fn is_signed_int(self) -> bool {
810        matches!(
811            self,
812            SimpleKind::Int
813                | SimpleKind::Int8
814                | SimpleKind::Int16
815                | SimpleKind::Int32
816                | SimpleKind::Int64
817                | SimpleKind::Rune
818        )
819    }
820
821    pub fn is_float(self) -> bool {
822        matches!(self, SimpleKind::Float32 | SimpleKind::Float64)
823    }
824
825    pub fn is_complex(self) -> bool {
826        matches!(self, SimpleKind::Complex64 | SimpleKind::Complex128)
827    }
828
829    pub fn numeric_family(self) -> Option<NumericFamily> {
830        if self.is_signed_int() {
831            Some(NumericFamily::SignedInt)
832        } else if self.is_unsigned_int() {
833            Some(NumericFamily::UnsignedInt)
834        } else if self.is_float() {
835            Some(NumericFamily::Float)
836        } else {
837            None
838        }
839    }
840}
841
842impl Type {
843    pub fn get_function_ret(&self) -> Option<&Type> {
844        match self {
845            Type::Function(f) => Some(&f.return_type),
846            _ => None,
847        }
848    }
849
850    pub fn is_stringer_signature(&self) -> bool {
851        let func = match self {
852            Type::Forall { body, .. } => body.as_ref(),
853            other => other,
854        };
855        matches!(
856            func,
857            Type::Function(f)
858                if f.params.len() == 1
859                    && matches!(f.return_type.as_ref(), Type::Simple(SimpleKind::String))
860        )
861    }
862
863    pub fn is_equals_signature(&self) -> bool {
864        let func = match self {
865            Type::Forall { body, .. } => body.as_ref(),
866            other => other,
867        };
868        matches!(
869            func,
870            Type::Function(f)
871                if f.params.len() == 2
872                    && matches!(f.return_type.as_ref(), Type::Simple(SimpleKind::Bool))
873                    && f.params[0] == f.params[1]
874                    && !f.params[0].is_ref()
875        )
876    }
877
878    pub fn equals_receiver_vars(&self, owner_id: &str, arity: usize) -> Option<Vec<EcoString>> {
879        if !self.is_equals_signature() {
880            return None;
881        }
882        let (quantified, func): (&[EcoString], &Type) = match self {
883            Type::Forall { vars, body } => (vars, body.as_ref()),
884            other => (&[], other),
885        };
886        if quantified.len() != arity {
887            return None;
888        }
889        let Type::Function(f) = func else {
890            return None;
891        };
892        let Type::Nominal { id, params, .. } = &f.params[0] else {
893            return None;
894        };
895        if id.as_str() != owner_id || params.len() != arity {
896            return None;
897        }
898        let mut vars = Vec::with_capacity(arity);
899        for param in params {
900            let Type::Parameter(name) = param else {
901                return None;
902            };
903            if vars.contains(name) {
904                return None;
905            }
906            vars.push(name.clone());
907        }
908        if !quantified.iter().all(|v| vars.contains(v)) {
909            return None;
910        }
911        Some(vars)
912    }
913
914    pub fn has_name(&self, name: &str) -> bool {
915        match self {
916            Type::Nominal { id, .. } => id.last_segment() == name,
917            Type::Simple(kind) => kind.leaf_name() == name,
918            Type::Compound { kind, .. } => kind.leaf_name() == name,
919            _ => false,
920        }
921    }
922
923    pub fn get_qualified_id(&self) -> Option<&str> {
924        match self {
925            Type::Nominal { id, .. } => Some(id.as_str()),
926            _ => None,
927        }
928    }
929
930    pub fn get_underlying(&self) -> Option<&Type> {
931        match self {
932            Type::Nominal {
933                underlying_ty: underlying,
934                ..
935            } => underlying.as_deref(),
936            _ => None,
937        }
938    }
939
940    pub fn is_result(&self) -> bool {
941        self.has_qualified_id("prelude.Result")
942    }
943
944    pub fn is_option(&self) -> bool {
945        self.has_qualified_id("prelude.Option")
946    }
947
948    pub fn is_partial(&self) -> bool {
949        self.has_qualified_id("prelude.Partial")
950    }
951
952    fn has_qualified_id(&self, qualified_id: &str) -> bool {
953        matches!(self, Type::Nominal { id, .. } if id.as_str() == qualified_id)
954    }
955
956    pub fn is_unit(&self) -> bool {
957        self.is_simple(SimpleKind::Unit)
958    }
959
960    pub fn tuple_arity(&self) -> Option<usize> {
961        match self {
962            Type::Tuple(elements) => Some(elements.len()),
963            _ => None,
964        }
965    }
966
967    pub fn is_tuple(&self) -> bool {
968        matches!(self, Type::Tuple(_))
969    }
970
971    pub fn as_import_namespace(&self) -> Option<&str> {
972        match self {
973            Type::ImportNamespace(module_id) => Some(module_id),
974            _ => None,
975        }
976    }
977
978    pub fn as_compound(&self) -> Option<(CompoundKind, &[Type])> {
979        match self {
980            Type::Compound { kind, args } => Some((*kind, args.as_slice())),
981            Type::Nominal { id, params, .. } => {
982                CompoundKind::from_qualified_id(id.as_str()).map(|k| (k, params.as_slice()))
983            }
984            _ => None,
985        }
986    }
987
988    pub fn is_native(&self, kind: CompoundKind) -> bool {
989        self.as_compound().is_some_and(|(k, _)| k == kind)
990    }
991
992    pub fn is_ref(&self) -> bool {
993        self.is_native(CompoundKind::Ref)
994    }
995
996    pub fn is_slice(&self) -> bool {
997        self.is_native(CompoundKind::Slice)
998    }
999
1000    pub fn is_map(&self) -> bool {
1001        self.is_native(CompoundKind::Map)
1002    }
1003
1004    pub fn is_channel(&self) -> bool {
1005        self.is_native(CompoundKind::Channel)
1006    }
1007
1008    pub fn is_receiver_placeholder(&self) -> bool {
1009        matches!(self, Type::ReceiverPlaceholder)
1010    }
1011
1012    pub fn is_unknown(&self) -> bool {
1013        self.has_name("Unknown")
1014    }
1015
1016    pub fn resolves_to_unknown(&self) -> bool {
1017        peel_alias(self, |_| true).is_unknown()
1018    }
1019
1020    pub fn contains_unknown(&self) -> bool {
1021        let peeled = peel_alias(self, |_| true);
1022        if peeled.is_unknown() {
1023            return true;
1024        }
1025        match &peeled {
1026            Type::Compound { args, .. } => args.iter().any(|a| a.contains_unknown()),
1027            Type::Function(f) => {
1028                f.params.iter().any(|p| p.contains_unknown()) || f.return_type.contains_unknown()
1029            }
1030            Type::Tuple(elements) => elements.iter().any(|e| e.contains_unknown()),
1031            Type::Nominal { params, .. } => params.iter().any(|p| p.contains_unknown()),
1032            Type::Forall { body, .. } => body.contains_unknown(),
1033            _ => false,
1034        }
1035    }
1036
1037    pub fn is_receiver(&self) -> bool {
1038        self.is_native(CompoundKind::Receiver)
1039    }
1040
1041    pub fn is_ignored(&self) -> bool {
1042        matches!(self, Type::Var { id, .. } if *id == TypeVarId::IGNORED)
1043    }
1044
1045    pub fn is_variadic(&self) -> Option<Type> {
1046        let last = self.get_function_params()?.last()?;
1047        match last.as_compound()? {
1048            (CompoundKind::VarArgs, _) => last.inner(),
1049            _ => None,
1050        }
1051    }
1052
1053    pub fn is_string(&self) -> bool {
1054        self.is_simple(SimpleKind::String)
1055    }
1056
1057    pub fn is_slice_of_simple(&self, element: SimpleKind) -> bool {
1058        match self.as_compound() {
1059            Some((CompoundKind::Slice, [elem])) => elem.is_simple(element),
1060            _ => false,
1061        }
1062    }
1063
1064    pub fn is_slice_of(&self, element_name: &str) -> bool {
1065        match self.as_compound() {
1066            Some((CompoundKind::Slice, [elem])) => elem.has_name(element_name),
1067            _ => false,
1068        }
1069    }
1070
1071    pub fn is_byte_slice(&self) -> bool {
1072        self.is_slice_of_simple(SimpleKind::Byte) || self.is_slice_of_simple(SimpleKind::Uint8)
1073    }
1074
1075    pub fn is_rune_slice(&self) -> bool {
1076        self.is_slice_of_simple(SimpleKind::Rune)
1077    }
1078
1079    pub fn is_byte_or_rune_slice(&self) -> bool {
1080        self.is_byte_slice() || self.is_rune_slice()
1081    }
1082
1083    pub fn has_underlying_rune(&self) -> bool {
1084        self.underlying_numeric_type().is_some_and(|t| t.is_rune())
1085    }
1086
1087    pub fn has_underlying_byte(&self) -> bool {
1088        self.underlying_numeric_type()
1089            .is_some_and(|t| t.is_simple(SimpleKind::Byte) || t.is_simple(SimpleKind::Uint8))
1090    }
1091
1092    pub fn has_byte_or_rune_slice_underlying(&self) -> bool {
1093        if self.is_byte_or_rune_slice() {
1094            return true;
1095        }
1096        match self {
1097            Type::Nominal { underlying_ty, .. } => underlying_ty
1098                .as_deref()
1099                .is_some_and(|u| u.has_byte_or_rune_slice_underlying()),
1100            _ => false,
1101        }
1102    }
1103
1104    pub fn as_simple(&self) -> Option<SimpleKind> {
1105        match self {
1106            Type::Simple(kind) => Some(*kind),
1107            Type::Nominal { id, .. } => SimpleKind::from_name(id.last_segment()),
1108            _ => None,
1109        }
1110    }
1111
1112    pub fn is_simple(&self, kind: SimpleKind) -> bool {
1113        self.as_simple() == Some(kind)
1114    }
1115
1116    pub fn is_boolean(&self) -> bool {
1117        self.is_simple(SimpleKind::Bool)
1118    }
1119
1120    pub fn is_rune(&self) -> bool {
1121        self.is_simple(SimpleKind::Rune)
1122    }
1123
1124    pub fn is_float64(&self) -> bool {
1125        self.is_simple(SimpleKind::Float64)
1126    }
1127
1128    pub fn is_float32(&self) -> bool {
1129        self.is_simple(SimpleKind::Float32)
1130    }
1131
1132    pub fn is_float(&self) -> bool {
1133        self.as_simple().is_some_and(SimpleKind::is_float)
1134    }
1135
1136    pub fn is_variable(&self) -> bool {
1137        matches!(self, Type::Var { .. })
1138    }
1139
1140    pub fn is_type_var(&self) -> bool {
1141        matches!(self, Type::Var { .. })
1142    }
1143
1144    pub fn is_numeric(&self) -> bool {
1145        self.as_simple().is_some_and(SimpleKind::is_arithmetic)
1146    }
1147
1148    pub fn is_ordered(&self) -> bool {
1149        self.as_simple().is_some_and(SimpleKind::is_ordered)
1150    }
1151
1152    /// Whether `<`/`<=`/`>`/`>=` accept this type: an ordered numeric, a
1153    /// string-backed type (resolved through named types), or a plain boolean.
1154    pub fn is_orderable(&self) -> bool {
1155        matches!(
1156            self.underlying_simple_kind(),
1157            Some(kind) if kind.is_ordered() || kind == SimpleKind::String
1158        ) || self.is_boolean()
1159    }
1160
1161    /// True for Go's `cmp.Ordered` set: ints, floats, strings, and named aliases over them.
1162    pub fn satisfies_ordered_constraint(&self) -> bool {
1163        if let Some(kind) = self.as_simple() {
1164            return matches!(
1165                kind,
1166                SimpleKind::Int
1167                    | SimpleKind::Int8
1168                    | SimpleKind::Int16
1169                    | SimpleKind::Int32
1170                    | SimpleKind::Int64
1171                    | SimpleKind::Uint
1172                    | SimpleKind::Uint8
1173                    | SimpleKind::Uint16
1174                    | SimpleKind::Uint32
1175                    | SimpleKind::Uint64
1176                    | SimpleKind::Uintptr
1177                    | SimpleKind::Byte
1178                    | SimpleKind::Rune
1179                    | SimpleKind::Float32
1180                    | SimpleKind::Float64
1181                    | SimpleKind::String
1182            );
1183        }
1184        match self {
1185            Type::Nominal { underlying_ty, .. } => underlying_ty
1186                .as_deref()
1187                .is_some_and(Type::satisfies_ordered_constraint),
1188            Type::Parameter(_) => true,
1189            _ => false,
1190        }
1191    }
1192
1193    pub fn is_complex(&self) -> bool {
1194        self.as_simple().is_some_and(SimpleKind::is_complex)
1195    }
1196
1197    pub fn is_unsigned_int(&self) -> bool {
1198        self.as_simple().is_some_and(SimpleKind::is_unsigned_int)
1199    }
1200
1201    pub fn underlying_is_unsigned_int(&self) -> bool {
1202        self.underlying_simple_kind()
1203            .is_some_and(SimpleKind::is_unsigned_int)
1204    }
1205
1206    pub fn is_never(&self) -> bool {
1207        matches!(self, Type::Never)
1208    }
1209
1210    pub fn is_error(&self) -> bool {
1211        matches!(self, Type::Error)
1212    }
1213
1214    pub fn contains_error(&self) -> bool {
1215        match self {
1216            Type::Error => true,
1217            Type::Nominal {
1218                params,
1219                underlying_ty,
1220                ..
1221            } => {
1222                params.iter().any(Type::contains_error)
1223                    || underlying_ty.as_deref().is_some_and(Type::contains_error)
1224            }
1225            Type::Compound { args, .. } => args.iter().any(Type::contains_error),
1226            Type::Function(f) => {
1227                f.params.iter().any(Type::contains_error) || f.return_type.contains_error()
1228            }
1229            Type::Tuple(elements) => elements.iter().any(Type::contains_error),
1230            Type::Forall { body, .. } => body.contains_error(),
1231            _ => false,
1232        }
1233    }
1234
1235    pub fn has_unbound_variables(&self) -> bool {
1236        match self {
1237            Type::Var { hint, .. } => hint.is_some(),
1238            Type::Nominal { params, .. } => params.iter().any(|p| p.has_unbound_variables()),
1239            Type::Function(f) => {
1240                f.params.iter().any(|p| p.has_unbound_variables())
1241                    || f.return_type.has_unbound_variables()
1242            }
1243            Type::Forall { body, .. } => body.has_unbound_variables(),
1244            Type::Tuple(elements) => elements.iter().any(|e| e.has_unbound_variables()),
1245            Type::Compound { args, .. } => args.iter().any(|a| a.has_unbound_variables()),
1246            Type::Simple(_)
1247            | Type::Parameter(_)
1248            | Type::Never
1249            | Type::Error
1250            | Type::ImportNamespace(_)
1251            | Type::ReceiverPlaceholder => false,
1252        }
1253    }
1254
1255    pub fn remove_found_type_names(&self, names: &mut HashSet<EcoString>) {
1256        if names.is_empty() {
1257            return;
1258        }
1259
1260        match self {
1261            Type::Nominal { id, params, .. } => {
1262                names.remove(id.last_segment());
1263                for param in params {
1264                    param.remove_found_type_names(names);
1265                }
1266            }
1267            Type::Function(f) => {
1268                for param in &f.params {
1269                    param.remove_found_type_names(names);
1270                }
1271                f.return_type.remove_found_type_names(names);
1272                for bound in &f.bounds {
1273                    bound.generic.remove_found_type_names(names);
1274                    bound.ty.remove_found_type_names(names);
1275                }
1276            }
1277            Type::Forall { body, .. } => {
1278                body.remove_found_type_names(names);
1279            }
1280            Type::Var { .. } => {}
1281            Type::Parameter(name) => {
1282                names.remove(name);
1283            }
1284            Type::Tuple(elements) => {
1285                for element in elements {
1286                    element.remove_found_type_names(names);
1287                }
1288            }
1289            Type::Compound { kind, args } => {
1290                names.remove(kind.leaf_name());
1291                for arg in args {
1292                    arg.remove_found_type_names(names);
1293                }
1294            }
1295            Type::Simple(kind) => {
1296                names.remove(kind.leaf_name());
1297            }
1298            Type::Never | Type::Error | Type::ImportNamespace(_) | Type::ReceiverPlaceholder => {}
1299        }
1300    }
1301}
1302
1303impl Type {
1304    pub fn get_name(&self) -> Option<&str> {
1305        match self {
1306            Type::Simple(kind) => Some(kind.leaf_name()),
1307            Type::Compound { kind, args } => match kind {
1308                CompoundKind::Ref => args.first().and_then(|inner| inner.get_name()),
1309                _ => Some(kind.leaf_name()),
1310            },
1311            Type::Nominal { id, params, .. } => {
1312                if CompoundKind::from_qualified_id(id.as_str()) == Some(CompoundKind::Ref) {
1313                    return params.first().and_then(|inner| inner.get_name());
1314                }
1315                Some(id.last_segment())
1316            }
1317            Type::ImportNamespace(module_id) => {
1318                let path = module_id.strip_prefix("go:").unwrap_or(module_id);
1319                path.rsplit('/').next()
1320            }
1321            _ => None,
1322        }
1323    }
1324
1325    pub fn wraps(&self, name: &str, inner: &Type) -> bool {
1326        self.get_name().is_some_and(|n| n == name)
1327            && self
1328                .get_type_params()
1329                .and_then(|p| p.first())
1330                .is_some_and(|first| *first == *inner)
1331    }
1332
1333    pub fn get_function_params(&self) -> Option<&[Type]> {
1334        match self {
1335            Type::Function(f) => Some(&f.params),
1336            Type::Nominal {
1337                underlying_ty: Some(inner),
1338                ..
1339            } => inner.get_function_params(),
1340            _ => None,
1341        }
1342    }
1343
1344    pub fn param_count(&self) -> usize {
1345        match self {
1346            Type::Function(f) => f.params.len(),
1347            _ => 0,
1348        }
1349    }
1350
1351    pub fn get_param_mutability(&self) -> &[bool] {
1352        match self {
1353            Type::Function(f) => &f.param_mutability,
1354            _ => &[],
1355        }
1356    }
1357
1358    pub fn with_replaced_first_param(&self, new_first: &Type) -> Type {
1359        match self {
1360            Type::Function(f) => {
1361                if f.params.is_empty() {
1362                    return self.clone();
1363                }
1364                let mut new_params = f.params.clone();
1365                new_params[0] = new_first.clone();
1366                f.rebuild(new_params, f.bounds.clone(), f.return_type.clone())
1367            }
1368            Type::Forall { vars, body } => Type::Forall {
1369                vars: vars.clone(),
1370                body: Box::new(body.with_replaced_first_param(new_first)),
1371            },
1372            _ => self.clone(),
1373        }
1374    }
1375
1376    pub fn get_bounds(&self) -> &[Bound] {
1377        match self {
1378            Type::Function(f) => &f.bounds,
1379            Type::Forall { body, .. } => body.get_bounds(),
1380            _ => &[],
1381        }
1382    }
1383
1384    pub fn get_qualified_name(&self) -> Symbol {
1385        match self.strip_refs() {
1386            Type::Nominal { id, .. } => id,
1387            Type::Simple(kind) => Symbol::from_parts("prelude", kind.leaf_name()),
1388            Type::Compound { kind, .. } => Symbol::from_parts("prelude", kind.leaf_name()),
1389            _ => panic!("called get_qualified_name on {:#?}", self),
1390        }
1391    }
1392
1393    pub fn inner(&self) -> Option<Type> {
1394        self.get_type_params()
1395            .and_then(|args| args.first().cloned())
1396    }
1397
1398    pub fn ok_type(&self) -> Type {
1399        debug_assert!(
1400            self.is_result() || self.is_option() || self.is_partial(),
1401            "ok_type called on non-Result/Option/Partial type"
1402        );
1403        self.inner()
1404            .expect("Result/Option/Partial should have inner type")
1405    }
1406
1407    pub fn err_type(&self) -> Type {
1408        debug_assert!(
1409            self.is_result() || self.is_partial(),
1410            "err_type called on non-Result/Partial type"
1411        );
1412        self.get_type_params()
1413            .and_then(|args| args.get(1).cloned())
1414            .expect("Result/Partial should have error type")
1415    }
1416}
1417
1418/// Walk an alias chain via `underlying_ty` (preserves substitution); cycle
1419/// guard defends against chains that slip past `circular_type_alias`.
1420pub fn peel_alias<F>(ty: &Type, is_alias: F) -> Type
1421where
1422    F: Fn(&str) -> bool,
1423{
1424    let mut current = ty.unwrap_forall().clone();
1425    let mut seen: Vec<String> = Vec::new();
1426    while let Type::Nominal {
1427        id,
1428        underlying_ty: Some(u),
1429        ..
1430    } = &current
1431    {
1432        if !is_alias(id.as_str()) {
1433            break;
1434        }
1435        if seen.iter().any(|s| s == id.as_str()) {
1436            break;
1437        }
1438        seen.push(id.to_string());
1439        current = u.unwrap_forall().clone();
1440    }
1441    current
1442}
1443
1444/// Walk an alias chain by id alone; used when no `Type` with
1445/// `underlying_ty` is available (e.g. Go-name resolution).
1446pub fn peel_alias_id<F>(id: &str, next_alias: F) -> String
1447where
1448    F: Fn(&str) -> Option<String>,
1449{
1450    let mut current = id.to_string();
1451    let mut seen: Vec<String> = Vec::new();
1452    loop {
1453        if seen.iter().any(|s| s == &current) {
1454            return current;
1455        }
1456        let Some(next) = next_alias(&current) else {
1457            return current;
1458        };
1459        seen.push(current);
1460        current = next;
1461    }
1462}
1463
1464impl Type {
1465    pub fn unwrap_forall(&self) -> &Type {
1466        match self {
1467            Type::Forall { body, .. } => body.as_ref(),
1468            other => other,
1469        }
1470    }
1471
1472    pub fn strip_refs(&self) -> Type {
1473        if self.is_ref() {
1474            return self.inner().expect("ref type must have inner").strip_refs();
1475        }
1476
1477        self.clone()
1478    }
1479
1480    pub fn with_receiver_placeholder(self) -> Type {
1481        match self {
1482            Type::Function(f) => {
1483                let f = Arc::try_unwrap(f).unwrap_or_else(|arc| (*arc).clone());
1484                let mut new_params = vec![Type::ReceiverPlaceholder];
1485                new_params.extend(f.params);
1486
1487                let mut new_mutability = vec![false];
1488                new_mutability.extend(f.param_mutability);
1489
1490                let new_param_names = if f.param_names.is_empty() {
1491                    Vec::new()
1492                } else {
1493                    let mut names = vec![None];
1494                    names.extend(f.param_names);
1495                    names
1496                };
1497
1498                Type::function_with_names(
1499                    new_params,
1500                    new_param_names,
1501                    new_mutability,
1502                    f.bounds,
1503                    f.return_type,
1504                )
1505            }
1506            _ => unreachable!(
1507                "with_receiver_placeholder called on non-function type: {:?}",
1508                self
1509            ),
1510        }
1511    }
1512
1513    pub fn remove_vars(types: &[&Type]) -> (Vec<Type>, Vec<EcoString>) {
1514        let mut vars = HashMap::default();
1515        let types = types
1516            .iter()
1517            .map(|v| Self::remove_vars_impl(v, &mut vars))
1518            .collect();
1519
1520        (types, vars.into_values().collect())
1521    }
1522
1523    fn remove_vars_impl(ty: &Type, vars: &mut HashMap<u32, EcoString>) -> Type {
1524        match ty {
1525            Type::Nominal {
1526                id: name,
1527                params: args,
1528                underlying_ty: underlying,
1529            } => Type::Nominal {
1530                id: name.clone(),
1531                params: args
1532                    .iter()
1533                    .map(|a| Self::remove_vars_impl(a, vars))
1534                    .collect(),
1535                underlying_ty: underlying
1536                    .as_ref()
1537                    .map(|u| Box::new(Self::remove_vars_impl(u, vars))),
1538            },
1539
1540            Type::Function(f) => Type::function(
1541                f.params
1542                    .iter()
1543                    .map(|a| Self::remove_vars_impl(a, vars))
1544                    .collect(),
1545                f.param_mutability.clone(),
1546                f.bounds
1547                    .iter()
1548                    .map(|b| Bound {
1549                        param_name: b.param_name.clone(),
1550                        generic: Self::remove_vars_impl(&b.generic, vars),
1551                        ty: Self::remove_vars_impl(&b.ty, vars),
1552                    })
1553                    .collect(),
1554                Self::remove_vars_impl(&f.return_type, vars).into(),
1555            ),
1556
1557            Type::Var { id, hint } => match vars.get(&id.0) {
1558                Some(g) => Type::Parameter(g.clone()),
1559                None => {
1560                    let name: EcoString = hint
1561                        .clone()
1562                        .unwrap_or_else(|| alpha_index(vars.len()).into());
1563
1564                    vars.insert(id.0, name.clone());
1565                    Type::Parameter(name)
1566                }
1567            },
1568
1569            Type::Forall { body, .. } => Self::remove_vars_impl(body, vars),
1570            Type::Tuple(elements) => Type::Tuple(
1571                elements
1572                    .iter()
1573                    .map(|e| Self::remove_vars_impl(e, vars))
1574                    .collect(),
1575            ),
1576            Type::Compound { kind, args } => Type::Compound {
1577                kind: *kind,
1578                args: args
1579                    .iter()
1580                    .map(|a| Self::remove_vars_impl(a, vars))
1581                    .collect(),
1582            },
1583            Type::Simple(_) | Type::Parameter(_) => ty.clone(),
1584            Type::Never | Type::Error | Type::ImportNamespace(_) | Type::ReceiverPlaceholder => {
1585                ty.clone()
1586            }
1587        }
1588    }
1589
1590    pub fn contains_type(&self, target: &Type) -> bool {
1591        if *self == *target {
1592            return true;
1593        }
1594        match self {
1595            Type::Nominal { params, .. } => params.iter().any(|p| p.contains_type(target)),
1596            Type::Function(f) => {
1597                f.params.iter().any(|p| p.contains_type(target))
1598                    || f.return_type.contains_type(target)
1599            }
1600            Type::Var { .. } => false,
1601            Type::Forall { body, .. } => body.contains_type(target),
1602            Type::Tuple(elements) => elements.iter().any(|e| e.contains_type(target)),
1603            Type::Compound { args, .. } => args.iter().any(|a| a.contains_type(target)),
1604            Type::Simple(_)
1605            | Type::Parameter(_)
1606            | Type::Never
1607            | Type::Error
1608            | Type::ImportNamespace(_)
1609            | Type::ReceiverPlaceholder => false,
1610        }
1611    }
1612}
1613
1614impl Type {
1615    pub fn underlying_numeric_type(&self) -> Option<Type> {
1616        self.underlying_numeric_type_recursive(&mut HashSet::default())
1617    }
1618
1619    pub fn has_underlying_numeric_type(&self) -> bool {
1620        self.underlying_numeric_type().is_some()
1621    }
1622
1623    pub fn literal_adaptation_target(&self) -> Option<Type> {
1624        if let Some(numeric) = self.underlying_numeric_type() {
1625            return Some(numeric);
1626        }
1627        match self {
1628            Type::Nominal { .. } if self.underlying_simple_kind() == Some(SimpleKind::Uintptr) => {
1629                Some(Type::Simple(SimpleKind::Uintptr))
1630            }
1631            _ => None,
1632        }
1633    }
1634
1635    pub fn underlying_simple_kind(&self) -> Option<SimpleKind> {
1636        self.underlying_simple_kind_recursive(&mut HashSet::default())
1637    }
1638
1639    fn underlying_simple_kind_recursive(
1640        &self,
1641        visited: &mut HashSet<Symbol>,
1642    ) -> Option<SimpleKind> {
1643        if let Some(kind) = self.as_simple() {
1644            return Some(kind);
1645        }
1646        match self {
1647            Type::Nominal {
1648                id,
1649                underlying_ty: Some(underlying),
1650                ..
1651            } => {
1652                if !visited.insert(id.clone()) {
1653                    return None;
1654                }
1655                underlying.underlying_simple_kind_recursive(visited)
1656            }
1657            _ => None,
1658        }
1659    }
1660
1661    fn underlying_numeric_type_recursive(&self, visited: &mut HashSet<Symbol>) -> Option<Type> {
1662        match self {
1663            Type::Simple(_) if self.is_numeric() => Some(self.clone()),
1664            Type::Nominal {
1665                id,
1666                underlying_ty: underlying,
1667                ..
1668            } => {
1669                if self.is_numeric() {
1670                    return Some(self.clone());
1671                }
1672
1673                if !visited.insert(id.clone()) {
1674                    return None;
1675                }
1676
1677                underlying
1678                    .as_ref()?
1679                    .underlying_numeric_type_recursive(visited)
1680            }
1681            _ => None,
1682        }
1683    }
1684
1685    pub fn numeric_family(&self) -> Option<NumericFamily> {
1686        self.as_simple()?.numeric_family()
1687    }
1688
1689    pub fn is_numeric_compatible_with(&self, other: &Type) -> bool {
1690        let self_underlying_ty = self.underlying_numeric_type();
1691        let other_underlying_ty = other.underlying_numeric_type();
1692
1693        match (self_underlying_ty, other_underlying_ty) {
1694            (Some(s), Some(o)) => s.numeric_family() == o.numeric_family(),
1695            _ => false,
1696        }
1697    }
1698
1699    pub fn is_aliased_numeric_type(&self) -> bool {
1700        match self {
1701            Type::Nominal { underlying_ty, .. } => {
1702                underlying_ty.is_some() && !self.is_numeric() && self.has_underlying_numeric_type()
1703            }
1704            _ => false,
1705        }
1706    }
1707}
1708
1709/// 0 → "A", 25 → "Z", 26 → "AA", 27 → "AB", ... (bijective base-26 over A-Z).
1710fn alpha_index(idx: usize) -> String {
1711    let mut s = String::new();
1712    let mut n = idx + 1;
1713    while n > 0 {
1714        n -= 1;
1715        s.insert(0, (b'A' + (n % 26) as u8) as char);
1716        n /= 26;
1717    }
1718    s
1719}
1720
1721#[cfg(test)]
1722mod tests {
1723    use super::*;
1724
1725    #[test]
1726    fn function_equality_ignores_param_names() {
1727        let named = Type::function_with_names(
1728            vec![Type::int()],
1729            vec![Some("width".into())],
1730            vec![false],
1731            vec![],
1732            Box::new(Type::bool()),
1733        );
1734        let differently_named = Type::function_with_names(
1735            vec![Type::int()],
1736            vec![Some("height".into())],
1737            vec![false],
1738            vec![],
1739            Box::new(Type::bool()),
1740        );
1741        let unnamed = Type::function(
1742            vec![Type::int()],
1743            vec![false],
1744            vec![],
1745            Box::new(Type::bool()),
1746        );
1747
1748        assert_eq!(named, differently_named);
1749        assert_eq!(named, unnamed);
1750    }
1751
1752    #[test]
1753    fn alpha_index_single() {
1754        assert_eq!(alpha_index(0), "A");
1755        assert_eq!(alpha_index(5), "F");
1756        assert_eq!(alpha_index(25), "Z");
1757    }
1758
1759    #[test]
1760    fn alpha_index_double() {
1761        assert_eq!(alpha_index(26), "AA");
1762        assert_eq!(alpha_index(27), "AB");
1763        assert_eq!(alpha_index(51), "AZ");
1764        assert_eq!(alpha_index(52), "BA");
1765        assert_eq!(alpha_index(701), "ZZ");
1766    }
1767
1768    #[test]
1769    fn alpha_index_triple() {
1770        assert_eq!(alpha_index(702), "AAA");
1771    }
1772
1773    fn unhinted_var(id: u32) -> Type {
1774        Type::Var {
1775            id: TypeVarId(id),
1776            hint: None,
1777        }
1778    }
1779
1780    #[test]
1781    fn remove_vars_handles_more_than_six_unhinted_vars() {
1782        let func = Type::function(
1783            (0..6).map(unhinted_var).collect(),
1784            vec![false; 6],
1785            vec![],
1786            Box::new(unhinted_var(6)),
1787        );
1788
1789        let (resolved, generics) = Type::remove_vars(&[&func]);
1790
1791        assert_eq!(generics.len(), 7);
1792        let Type::Function(f) = &resolved[0] else {
1793            panic!("expected function type");
1794        };
1795        let names: Vec<_> = f
1796            .params
1797            .iter()
1798            .chain(std::iter::once(f.return_type.as_ref()))
1799            .map(|p| match p {
1800                Type::Parameter(name) => name.to_string(),
1801                other => panic!("expected parameter, got {:?}", other),
1802            })
1803            .collect();
1804        assert_eq!(names, vec!["A", "B", "C", "D", "E", "F", "G"]);
1805    }
1806
1807    #[test]
1808    fn remove_vars_handles_dozens_of_unhinted_vars() {
1809        let params: Vec<Type> = (0..30).map(unhinted_var).collect();
1810        let func = Type::function(
1811            params.clone(),
1812            vec![false; params.len()],
1813            vec![],
1814            Box::new(Type::Simple(SimpleKind::Unit)),
1815        );
1816        let (_, generics) = Type::remove_vars(&[&func]);
1817        assert_eq!(generics.len(), 30);
1818    }
1819}