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_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 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 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
198pub fn is_arith_bound(name: &str) -> bool {
200 matches!(name, "Add" | "Sub" | "Mul" | "Div")
201}
202
203pub 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}