Skip to main content

crisp_typeck/
types.rs

1use crisp_ast::Span;
2use std::collections::{BTreeMap, HashMap};
3
4pub type TypeVar = u32;
5
6#[derive(Debug, Clone, PartialEq)]
7pub enum Ty {
8    Var(TypeVar),
9    Never,
10    Unit,
11    Bool,
12    Int,
13    UInt,
14    Float,
15    Char,
16    Str,
17    StrSlice,
18    Tuple(Vec<Ty>),
19    Array { elem: Box<Ty>, len: u64 },
20    Slice(Box<Ty>),
21    Fn { params: Vec<Ty>, ret: Box<Ty> },
22    Option(Box<Ty>),
23    Ref { mutable: bool, inner: Box<Ty> },
24    Named { name: String, args: Vec<Ty> },
25    Error,
26}
27
28impl Ty {
29    pub fn is_stringish(&self) -> bool {
30        matches!(self, Ty::Str | Ty::StrSlice)
31    }
32}
33
34#[derive(Debug, Clone)]
35pub struct Scheme {
36    pub vars: Vec<TypeVar>,
37    pub ty: Ty,
38}
39
40#[derive(Debug, Clone)]
41pub struct InferContext {
42    pub next_var: TypeVar,
43    pub subst: HashMap<TypeVar, Ty>,
44}
45
46impl InferContext {
47    pub fn new() -> Self {
48        Self {
49            next_var: 0,
50            subst: HashMap::new(),
51        }
52    }
53
54    pub fn fresh(&mut self) -> Ty {
55        let v = self.next_var;
56        self.next_var += 1;
57        Ty::Var(v)
58    }
59
60    pub fn apply(&mut self, ty: &Ty) -> Ty {
61        match ty {
62            Ty::Var(v) => {
63                if let Some(t) = self.subst.get(v).cloned() {
64                    let t = self.apply(&t);
65                    self.subst.insert(*v, t.clone());
66                    t
67                } else {
68                    ty.clone()
69                }
70            }
71            Ty::Tuple(ts) => Ty::Tuple(ts.iter().map(|t| self.apply(t)).collect()),
72            Ty::Array { elem, len } => Ty::Array {
73                elem: Box::new(self.apply(elem)),
74                len: *len,
75            },
76            Ty::Slice(inner) => Ty::Slice(Box::new(self.apply(inner))),
77            Ty::Fn { params, ret } => Ty::Fn {
78                params: params.iter().map(|p| self.apply(p)).collect(),
79                ret: Box::new(self.apply(ret)),
80            },
81            Ty::Option(inner) => Ty::Option(Box::new(self.apply(inner))),
82            Ty::Ref { mutable, inner } => Ty::Ref {
83                mutable: *mutable,
84                inner: Box::new(self.apply(inner)),
85            },
86            Ty::Named { name, args } => Ty::Named {
87                name: name.clone(),
88                args: args.iter().map(|a| self.apply(a)).collect(),
89            },
90            other => other.clone(),
91        }
92    }
93}
94
95#[derive(Debug, Clone)]
96pub struct InferredSig {
97    pub module: String,
98    pub name: String,
99    /// When set, this signature is an inherent `impl Type` method (§5.4).
100    pub impl_ty: Option<String>,
101    pub params: Vec<(String, Ty)>,
102    pub ret: Ty,
103    pub span: Span,
104    /// Explicit or inferred type parameters (`id(x) = x` → `["T"]`).
105    pub generics: Vec<String>,
106    pub is_pub: bool,
107    /// True when generics were named from leftover free vars (`id(x) = x`), not a pin.
108    pub inferred_from_use: bool,
109    /// Distinct concrete call-site instantiations (`int`, `str`) for reveal.
110    pub instantiations: Vec<String>,
111    /// Single concrete instantiation for crate-internal emit (#76). Scheme stays generic.
112    pub mono_args: Option<Vec<Ty>>,
113    /// Inferred bounds on generics (`T` → `Add` / `Show` / …) from operators and unique trait methods (#84).
114    pub op_bounds: BTreeMap<String, Vec<String>>,
115}
116
117impl InferredSig {
118    /// Param types, return type, and generics as they should be emitted (#76).
119    pub fn emit_view(&self) -> (Vec<(String, Ty)>, Ty, Vec<String>) {
120        let Some(args) = &self.mono_args else {
121            return (self.params.clone(), self.ret.clone(), self.generics.clone());
122        };
123        let mut subst = std::collections::BTreeMap::new();
124        for ((_, sty), ity) in self.params.iter().zip(args.iter()) {
125            collect_generic_subst(sty, ity, &self.generics, &mut subst);
126        }
127        let params = self
128            .params
129            .iter()
130            .zip(args.iter())
131            .map(|((n, _), t)| (n.clone(), t.clone()))
132            .collect();
133        let ret = subst_named(&self.ret, &subst);
134        (params, ret, Vec::new())
135    }
136
137    /// Emit-style binder list, including the hidden `T: Clone` bound (#78).
138    pub fn scheme_prefix(&self) -> String {
139        self.scheme_prefix_for(&self.generics)
140    }
141
142    pub fn scheme_prefix_for(&self, gens: &[String]) -> String {
143        if gens.is_empty() {
144            String::new()
145        } else {
146            format!(
147                "<{}>",
148                gens.iter()
149                    .map(|g| self.crisp_generic_bound(g))
150                    .collect::<Vec<_>>()
151                    .join(", ")
152            )
153        }
154    }
155
156    fn crisp_generic_bound(&self, g: &str) -> String {
157        let mut parts = vec![format!("{g}: Clone")];
158        if let Some(ops) = self.op_bounds.get(g) {
159            if ops.iter().any(|o| is_arith_bound(o)) {
160                parts.push("Copy".into());
161            }
162            for op in ops {
163                parts.push(op.clone());
164            }
165        }
166        parts.join(" + ")
167    }
168
169    /// Rust binder list (`T: Clone + std::ops::Add<Output = T>`).
170    pub fn rust_scheme_prefix_for(&self, gens: &[String]) -> String {
171        if gens.is_empty() {
172            String::new()
173        } else {
174            format!(
175                "<{}>",
176                gens.iter()
177                    .map(|g| self.rust_generic_bound(g))
178                    .collect::<Vec<_>>()
179                    .join(", ")
180            )
181        }
182    }
183
184    pub fn rust_generic_bound(&self, g: &str) -> String {
185        let mut parts = vec![format!("{g}: Clone")];
186        if let Some(ops) = self.op_bounds.get(g) {
187            if ops.iter().any(|o| is_arith_bound(o)) {
188                parts.push("Copy".into());
189            }
190            for op in ops {
191                parts.push(rust_op_bound(g, op));
192            }
193        }
194        parts.join(" + ")
195    }
196}
197
198/// Prelude arithmetic traits inferred from `+` `-` `*` `/` (spec §15.4).
199pub fn is_arith_bound(name: &str) -> bool {
200    matches!(name, "Add" | "Sub" | "Mul" | "Div")
201}
202
203/// Prelude operator trait → `std::ops` bound (spec §15.4).
204pub fn rust_op_bound(generic: &str, op: &str) -> String {
205    match op {
206        "Add" => format!("std::ops::Add<Output = {generic}>"),
207        "Sub" => format!("std::ops::Sub<Output = {generic}>"),
208        "Mul" => format!("std::ops::Mul<Output = {generic}>"),
209        "Div" => format!("std::ops::Div<Output = {generic}>"),
210        other => other.to_string(),
211    }
212}
213
214fn collect_generic_subst(
215    scheme: &Ty,
216    inst: &Ty,
217    generics: &[String],
218    subst: &mut std::collections::BTreeMap<String, Ty>,
219) {
220    match (scheme, inst) {
221        (Ty::Named { name, args }, inst)
222            if args.is_empty() && generics.iter().any(|g| g == name) =>
223        {
224            subst.entry(name.clone()).or_insert_with(|| inst.clone());
225        }
226        (Ty::Fn { params: a, ret: ra }, Ty::Fn { params: b, ret: rb }) if a.len() == b.len() => {
227            for (x, y) in a.iter().zip(b.iter()) {
228                collect_generic_subst(x, y, generics, subst);
229            }
230            collect_generic_subst(ra, rb, generics, subst);
231        }
232        (Ty::Named { args: a, .. }, Ty::Named { args: b, .. }) if a.len() == b.len() => {
233            for (x, y) in a.iter().zip(b.iter()) {
234                collect_generic_subst(x, y, generics, subst);
235            }
236        }
237        (Ty::Option(a), Ty::Option(b))
238        | (Ty::Slice(a), Ty::Slice(b))
239        | (Ty::Ref { inner: a, .. }, Ty::Ref { inner: b, .. }) => {
240            collect_generic_subst(a, b, generics, subst);
241        }
242        (Ty::Tuple(a), Ty::Tuple(b)) if a.len() == b.len() => {
243            for (x, y) in a.iter().zip(b.iter()) {
244                collect_generic_subst(x, y, generics, subst);
245            }
246        }
247        _ => {}
248    }
249}
250
251fn subst_named(ty: &Ty, subst: &std::collections::BTreeMap<String, Ty>) -> Ty {
252    match ty {
253        Ty::Named { name, args } if args.is_empty() => {
254            subst.get(name).cloned().unwrap_or_else(|| ty.clone())
255        }
256        Ty::Named { name, args } => Ty::Named {
257            name: name.clone(),
258            args: args.iter().map(|a| subst_named(a, subst)).collect(),
259        },
260        Ty::Fn { params, ret } => Ty::Fn {
261            params: params.iter().map(|p| subst_named(p, subst)).collect(),
262            ret: Box::new(subst_named(ret, subst)),
263        },
264        Ty::Option(inner) => Ty::Option(Box::new(subst_named(inner, subst))),
265        Ty::Slice(inner) => Ty::Slice(Box::new(subst_named(inner, subst))),
266        Ty::Array { elem, len } => Ty::Array {
267            elem: Box::new(subst_named(elem, subst)),
268            len: *len,
269        },
270        Ty::Ref { mutable, inner } => Ty::Ref {
271            mutable: *mutable,
272            inner: Box::new(subst_named(inner, subst)),
273        },
274        Ty::Tuple(ts) => Ty::Tuple(ts.iter().map(|t| subst_named(t, subst)).collect()),
275        other => other.clone(),
276    }
277}