1use crate::types::{
12 self, resolve, resolve_row, CmdArgType, Kind, MonoType, Row, RowVarRef, TyVarRef,
13};
14use std::collections::BTreeSet;
15
16#[derive(Debug, thiserror::Error)]
17pub enum UnifyError {
18 #[error("type mismatch: expected `{expected}`, found `{found}`")]
19 Mismatch { expected: MonoType, found: MonoType },
20
21 #[error("occurs check failed: the type would be infinite")]
22 OccursCheck,
23
24 #[error("record is missing label `{label}` (required for `{ty}`)")]
25 MissingLabel { label: String, ty: MonoType },
26
27 #[error("arity mismatch: expected {expected} element(s), found {found}")]
28 ArityMismatch { expected: usize, found: usize },
29
30 #[error("command argument optionality mismatch for `{ty}` (`?` on one side only)")]
31 OptionalMismatch { ty: MonoType },
32
33 #[error(
41 "command optional-argument label set mismatch: expected `{expected}`, found `{found}`"
42 )]
43 CmdLabelMismatch { expected: String, found: String },
44}
45
46pub fn unify(a: &MonoType, b: &MonoType) -> Result<(), UnifyError> {
50 let ra = resolve(a);
51 let rb = resolve(b);
52 match (&*ra, &*rb) {
53 (MonoType::Var(v1), MonoType::Var(v2)) if v1.same(v2) => Ok(()),
54 (MonoType::Var(v), _) => bind_var(v, (*rb).clone()),
55 (_, MonoType::Var(v)) => bind_var(v, (*ra).clone()),
56
57 (MonoType::Base(x), MonoType::Base(y)) => {
58 if x == y {
59 Ok(())
60 } else {
61 Err(UnifyError::Mismatch {
62 expected: (*ra).clone(),
63 found: (*rb).clone(),
64 })
65 }
66 }
67
68 (MonoType::Func(r1, d1, c1), MonoType::Func(r2, d2, c2)) => {
69 unify_row(r1, r2)?;
70 unify(d1, d2)?;
71 unify(c1, c2)
72 }
73
74 (MonoType::Product(ts1), MonoType::Product(ts2)) => {
75 if ts1.len() != ts2.len() {
76 return Err(UnifyError::ArityMismatch {
77 expected: ts1.len(),
78 found: ts2.len(),
79 });
80 }
81 for (x, y) in ts1.iter().zip(ts2) {
82 unify(x, y)?;
83 }
84 Ok(())
85 }
86
87 (MonoType::List(x), MonoType::List(y)) => unify(x, y),
88 (MonoType::Ref(x), MonoType::Ref(y)) => unify(x, y),
89 (MonoType::Code(x), MonoType::Code(y)) => unify(x, y),
90
91 (MonoType::Record(r1), MonoType::Record(r2)) => unify_row(r1, r2),
92
93 (MonoType::Variant(n1, a1), MonoType::Variant(n2, a2)) => {
94 if n1 != n2 {
95 return Err(UnifyError::Mismatch {
96 expected: (*ra).clone(),
97 found: (*rb).clone(),
98 });
99 }
100 if a1.len() != a2.len() {
101 return Err(UnifyError::ArityMismatch {
102 expected: a1.len(),
103 found: a2.len(),
104 });
105 }
106 for (x, y) in a1.iter().zip(a2) {
107 unify(x, y)?;
108 }
109 Ok(())
110 }
111
112 (MonoType::InlineCmd(c1), MonoType::InlineCmd(c2))
113 | (MonoType::BlockCmd(c1), MonoType::BlockCmd(c2))
114 | (MonoType::MathCmd(c1), MonoType::MathCmd(c2)) => unify_cmd_args(c1, c2),
115
116 _ => Err(UnifyError::Mismatch {
117 expected: (*ra).clone(),
118 found: (*rb).clone(),
119 }),
120 }
121}
122
123fn unify_cmd_args(a: &[CmdArgType], b: &[CmdArgType]) -> Result<(), UnifyError> {
124 if a.len() != b.len() {
125 return Err(UnifyError::ArityMismatch {
126 expected: a.len(),
127 found: b.len(),
128 });
129 }
130 for (x, y) in a.iter().zip(b) {
131 if x.optional != y.optional {
132 return Err(UnifyError::OptionalMismatch { ty: x.ty.clone() });
133 }
134 if x.opt_labels.len() != y.opt_labels.len()
142 || x.opt_labels
143 .iter()
144 .zip(&y.opt_labels)
145 .any(|((lx, _), (ly, _))| lx != ly)
146 {
147 return Err(UnifyError::CmdLabelMismatch {
148 expected: fmt_opt_label_set(&x.opt_labels),
149 found: fmt_opt_label_set(&y.opt_labels),
150 });
151 }
152 for ((_, tx), (_, ty2)) in x.opt_labels.iter().zip(&y.opt_labels) {
153 unify(tx, ty2)?;
154 }
155 unify(&x.ty, &y.ty)?;
156 }
157 Ok(())
158}
159
160fn fmt_opt_label_set(labels: &[(String, MonoType)]) -> String {
163 let mut s = String::from("?(");
164 for (i, (l, t)) in labels.iter().enumerate() {
165 if i > 0 {
166 s.push_str(", ");
167 }
168 s.push_str(&format!("{l} : {t}"));
169 }
170 s.push(')');
171 s
172}
173
174fn bind_var(v: &TyVarRef, ty: MonoType) -> Result<(), UnifyError> {
180 if let MonoType::Var(v2) = &ty {
181 if v.same(v2) {
182 return Ok(());
183 }
184 }
185 if occurs_var(v, &ty) {
186 return Err(UnifyError::OccursCheck);
187 }
188 match v.kind() {
189 Kind::Universal => {
190 v.bind(ty);
191 Ok(())
192 }
193 Kind::Record(required) => match &ty {
194 MonoType::Var(v2) => {
195 let merged = match v2.kind() {
196 Kind::Universal => Kind::Record(required.clone()),
197 Kind::Record(r2) => Kind::Record(required.union(&r2).cloned().collect()),
198 };
199 v2.set_kind(merged);
200 v.bind(ty);
201 Ok(())
202 }
203 MonoType::Record(row) => {
204 for label in &required {
205 row_require_label(row, label)?;
206 }
207 v.bind(ty);
208 Ok(())
209 }
210 other => Err(UnifyError::Mismatch {
211 expected: MonoType::Var(v.clone()),
212 found: other.clone(),
213 }),
214 },
215 }
216}
217
218fn row_require_label(row: &Row, label: &str) -> Result<(), UnifyError> {
223 match &*resolve_row(row) {
224 Row::Empty => Err(UnifyError::MissingLabel {
225 label: label.to_string(),
226 ty: MonoType::Record(Row::Empty),
227 }),
228 Row::Cons(l, _, rest) => {
229 if l == label {
230 Ok(())
231 } else {
232 row_require_label(&rest, label)
233 }
234 }
235 Row::Var(v) => {
236 let level = v.level().unwrap_or(0);
237 let field = types::new_ty_var(level);
238 let remainder = types::new_row_var(level);
239 let extended = Row::Cons(
240 label.to_string(),
241 Box::new(MonoType::Var(field)),
242 Box::new(Row::Var(remainder)),
243 );
244 bind_row_var(&v, extended)
245 }
246 }
247}
248
249fn unify_row(a: &Row, b: &Row) -> Result<(), UnifyError> {
254 let ra = resolve_row(a).into_owned();
258 let rb = resolve_row(b).into_owned();
259 match (ra, rb) {
260 (Row::Empty, Row::Empty) => Ok(()),
261 (Row::Var(v1), Row::Var(v2)) if v1.same(&v2) => Ok(()),
262
263 (Row::Cons(l, t, rest), other) => {
264 let (t2, rest2) = row_extract(&other, &l)?;
265 unify(&t, &t2)?;
266 unify_row(&rest, &rest2)
267 }
268 (other, Row::Cons(l, t, rest)) => {
269 let (t2, rest2) = row_extract(&other, &l)?;
270 unify(&t2, &t)?;
271 unify_row(&rest2, &rest)
272 }
273
274 (Row::Empty, Row::Var(v)) | (Row::Var(v), Row::Empty) => {
275 if let Some(label) = v.kind().iter().next().cloned() {
276 return Err(UnifyError::MissingLabel {
277 label,
278 ty: MonoType::Record(Row::Empty),
279 });
280 }
281 bind_row_var(&v, Row::Empty)
282 }
283
284 (Row::Var(v1), Row::Var(v2)) => {
285 let union: BTreeSet<String> = v1.kind().union(&v2.kind()).cloned().collect();
286 v2.set_kind(union);
287 bind_row_var(&v1, Row::Var(v2))
288 }
289 }
290}
291
292fn row_extract(row: &Row, label: &str) -> Result<(MonoType, Row), UnifyError> {
299 match resolve_row(row).into_owned() {
301 Row::Empty => Err(UnifyError::MissingLabel {
302 label: label.to_string(),
303 ty: MonoType::Record(Row::Empty),
304 }),
305 Row::Cons(l, t, rest) => {
306 if l == label {
307 Ok((*t, *rest))
308 } else {
309 let (t2, rest2) = row_extract(&rest, label)?;
310 Ok((t2, Row::Cons(l, t, Box::new(rest2))))
311 }
312 }
313 Row::Var(v) => {
314 let level = v.level().unwrap_or(0);
315 let field = types::new_ty_var(level);
316 let remainder = types::new_row_var(level);
317 let extended = Row::Cons(
318 label.to_string(),
319 Box::new(MonoType::Var(field.clone())),
320 Box::new(Row::Var(remainder.clone())),
321 );
322 bind_row_var(&v, extended)?;
323 Ok((MonoType::Var(field), Row::Var(remainder)))
324 }
325 }
326}
327
328fn bind_row_var(v: &RowVarRef, row: Row) -> Result<(), UnifyError> {
329 if let Row::Var(v2) = &row {
330 if v.same(v2) {
331 return Ok(());
332 }
333 }
334 if occurs_rowvar_in_row(v, &row) {
335 return Err(UnifyError::OccursCheck);
336 }
337 v.bind(row);
338 Ok(())
339}
340
341fn occurs_var(tv: &TyVarRef, ty: &MonoType) -> bool {
350 match &*resolve(ty) {
351 MonoType::Var(v) => {
352 if v.same(tv) {
353 return true;
354 }
355 if let (Some(tv_level), Some(v_level)) = (tv.level(), v.level()) {
356 if tv_level < v_level {
357 v.set_level(tv_level);
358 }
359 }
360 false
361 }
362 MonoType::Base(_) => false,
363 MonoType::Func(row, a, b) => {
364 occurs_var_in_row(tv, &row) || occurs_var(tv, &a) || occurs_var(tv, &b)
365 }
366 MonoType::Product(ts) => ts.iter().any(|t| occurs_var(tv, t)),
367 MonoType::List(t) | MonoType::Ref(t) | MonoType::Code(t) => occurs_var(tv, &t),
368 MonoType::Record(row) => occurs_var_in_row(tv, &row),
369 MonoType::Variant(_, args) => args.iter().any(|t| occurs_var(tv, t)),
370 MonoType::InlineCmd(cs) | MonoType::BlockCmd(cs) | MonoType::MathCmd(cs) => cs
377 .iter()
378 .any(|c| c.opt_labels.iter().any(|(_, t)| occurs_var(tv, t)) || occurs_var(tv, &c.ty)),
379 }
380}
381
382fn occurs_var_in_row(tv: &TyVarRef, row: &Row) -> bool {
383 match &*resolve_row(row) {
384 Row::Empty => false,
385 Row::Var(_) => false,
386 Row::Cons(_, t, rest) => occurs_var(tv, &t) || occurs_var_in_row(tv, &rest),
387 }
388}
389
390fn occurs_rowvar_in_type(rv: &RowVarRef, ty: &MonoType) -> bool {
391 match &*resolve(ty) {
392 MonoType::Var(_) => false,
393 MonoType::Base(_) => false,
394 MonoType::Func(row, a, b) => {
395 occurs_rowvar_in_row(rv, &row)
396 || occurs_rowvar_in_type(rv, &a)
397 || occurs_rowvar_in_type(rv, &b)
398 }
399 MonoType::Product(ts) => ts.iter().any(|t| occurs_rowvar_in_type(rv, t)),
400 MonoType::List(t) | MonoType::Ref(t) | MonoType::Code(t) => occurs_rowvar_in_type(rv, &t),
401 MonoType::Record(row) => occurs_rowvar_in_row(rv, &row),
402 MonoType::Variant(_, args) => args.iter().any(|t| occurs_rowvar_in_type(rv, t)),
403 MonoType::InlineCmd(cs) | MonoType::BlockCmd(cs) | MonoType::MathCmd(cs) => {
405 cs.iter().any(|c| {
406 c.opt_labels
407 .iter()
408 .any(|(_, t)| occurs_rowvar_in_type(rv, t))
409 || occurs_rowvar_in_type(rv, &c.ty)
410 })
411 }
412 }
413}
414
415fn occurs_rowvar_in_row(rv: &RowVarRef, row: &Row) -> bool {
416 match &*resolve_row(row) {
417 Row::Empty => false,
418 Row::Var(v) => {
419 if v.same(rv) {
420 return true;
421 }
422 if let (Some(rv_level), Some(v_level)) = (rv.level(), v.level()) {
423 if rv_level < v_level {
424 v.set_level(rv_level);
425 }
426 }
427 false
428 }
429 Row::Cons(_, t, rest) => occurs_rowvar_in_type(rv, &t) || occurs_rowvar_in_row(rv, &rest),
430 }
431}