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 pub impl_ty: Option<String>,
101 pub params: Vec<(String, Ty)>,
102 pub ret: Ty,
103 pub span: Span,
104 pub generics: Vec<String>,
106 pub is_pub: bool,
107 pub inferred_from_use: bool,
109 pub instantiations: Vec<String>,
111 pub mono_args: Option<Vec<Ty>>,
113 pub op_bounds: BTreeMap<String, Vec<String>>,
115}
116
117impl InferredSig {
118 pub fn emit_subst(&self) -> BTreeMap<String, Ty> {
120 let mut subst = BTreeMap::new();
121 let Some(args) = &self.mono_args else {
122 return subst;
123 };
124 for ((_, sty), ity) in self.params.iter().zip(args.iter()) {
125 collect_generic_subst(sty, ity, &self.generics, &mut subst);
126 }
127 subst
128 }
129
130 pub fn emit_view(&self) -> (Vec<(String, Ty)>, Ty, Vec<String>) {
132 let Some(args) = &self.mono_args else {
133 return (self.params.clone(), self.ret.clone(), self.generics.clone());
134 };
135 let subst = self.emit_subst();
136 let params = self
137 .params
138 .iter()
139 .zip(args.iter())
140 .map(|((n, _), t)| (n.clone(), t.clone()))
141 .collect();
142 let ret = subst_named(&self.ret, &subst);
143 (params, ret, Vec::new())
144 }
145
146 pub fn scheme_prefix(&self) -> String {
148 self.scheme_prefix_for(&self.generics)
149 }
150
151 pub fn scheme_prefix_for(&self, gens: &[String]) -> String {
152 if gens.is_empty() {
153 String::new()
154 } else {
155 format!(
156 "<{}>",
157 gens.iter()
158 .map(|g| self.crisp_generic_bound(g))
159 .collect::<Vec<_>>()
160 .join(", ")
161 )
162 }
163 }
164
165 fn crisp_generic_bound(&self, g: &str) -> String {
166 let mut parts = vec![format!("{g}: Clone")];
167 if let Some(ops) = self.op_bounds.get(g) {
168 if ops.iter().any(|o| is_arith_bound(o)) {
169 parts.push("Copy".into());
170 }
171 for op in ops {
172 parts.push(op.clone());
173 }
174 }
175 parts.join(" + ")
176 }
177
178 pub fn rust_scheme_prefix_for(&self, gens: &[String]) -> String {
180 if gens.is_empty() {
181 String::new()
182 } else {
183 format!(
184 "<{}>",
185 gens.iter()
186 .map(|g| self.rust_generic_bound(g))
187 .collect::<Vec<_>>()
188 .join(", ")
189 )
190 }
191 }
192
193 pub fn rust_generic_bound(&self, g: &str) -> String {
194 let mut parts = vec![format!("{g}: Clone")];
195 if let Some(ops) = self.op_bounds.get(g) {
196 if ops.iter().any(|o| is_arith_bound(o)) {
197 parts.push("Copy".into());
198 }
199 for op in ops {
200 parts.push(rust_op_bound(g, op));
201 }
202 }
203 parts.join(" + ")
204 }
205}
206
207pub fn is_arith_bound(name: &str) -> bool {
209 matches!(name, "Add" | "Sub" | "Mul" | "Div")
210}
211
212pub fn rust_op_bound(generic: &str, op: &str) -> String {
214 match op {
215 "Add" => format!("std::ops::Add<Output = {generic}>"),
216 "Sub" => format!("std::ops::Sub<Output = {generic}>"),
217 "Mul" => format!("std::ops::Mul<Output = {generic}>"),
218 "Div" => format!("std::ops::Div<Output = {generic}>"),
219 other => other.to_string(),
220 }
221}
222
223fn collect_generic_subst(
224 scheme: &Ty,
225 inst: &Ty,
226 generics: &[String],
227 subst: &mut std::collections::BTreeMap<String, Ty>,
228) {
229 match (scheme, inst) {
230 (Ty::Named { name, args }, inst)
231 if args.is_empty() && generics.iter().any(|g| g == name) =>
232 {
233 subst.entry(name.clone()).or_insert_with(|| inst.clone());
234 }
235 (Ty::Fn { params: a, ret: ra }, Ty::Fn { params: b, ret: rb }) if a.len() == b.len() => {
236 for (x, y) in a.iter().zip(b.iter()) {
237 collect_generic_subst(x, y, generics, subst);
238 }
239 collect_generic_subst(ra, rb, generics, subst);
240 }
241 (Ty::Named { args: a, .. }, Ty::Named { args: b, .. }) if a.len() == b.len() => {
242 for (x, y) in a.iter().zip(b.iter()) {
243 collect_generic_subst(x, y, generics, subst);
244 }
245 }
246 (Ty::Option(a), Ty::Option(b))
247 | (Ty::Slice(a), Ty::Slice(b))
248 | (Ty::Ref { inner: a, .. }, Ty::Ref { inner: b, .. }) => {
249 collect_generic_subst(a, b, generics, subst);
250 }
251 (Ty::Tuple(a), Ty::Tuple(b)) if a.len() == b.len() => {
252 for (x, y) in a.iter().zip(b.iter()) {
253 collect_generic_subst(x, y, generics, subst);
254 }
255 }
256 _ => {}
257 }
258}
259
260pub fn subst_named(ty: &Ty, subst: &std::collections::BTreeMap<String, Ty>) -> Ty {
261 match ty {
262 Ty::Named { name, args } if args.is_empty() => {
263 subst.get(name).cloned().unwrap_or_else(|| ty.clone())
264 }
265 Ty::Named { name, args } => Ty::Named {
266 name: name.clone(),
267 args: args.iter().map(|a| subst_named(a, subst)).collect(),
268 },
269 Ty::Fn { params, ret } => Ty::Fn {
270 params: params.iter().map(|p| subst_named(p, subst)).collect(),
271 ret: Box::new(subst_named(ret, subst)),
272 },
273 Ty::Option(inner) => Ty::Option(Box::new(subst_named(inner, subst))),
274 Ty::Slice(inner) => Ty::Slice(Box::new(subst_named(inner, subst))),
275 Ty::Array { elem, len } => Ty::Array {
276 elem: Box::new(subst_named(elem, subst)),
277 len: *len,
278 },
279 Ty::Ref { mutable, inner } => Ty::Ref {
280 mutable: *mutable,
281 inner: Box::new(subst_named(inner, subst)),
282 },
283 Ty::Tuple(ts) => Ty::Tuple(ts.iter().map(|t| subst_named(t, subst)).collect()),
284 other => other.clone(),
285 }
286}