1use std::collections::HashMap;
17
18use crate::error::{KernelError, KernelResult};
19use crate::term::Term;
20use crate::type_checker::substitute;
21use crate::{infer_type, normalize, Context};
22
23pub const ANON_CTOR_MARKER: &str = "⟨anon⟩";
30
31pub const DOT_MARKER: &str = "⟨proj⟩";
36
37enum Sugar<'a> {
39 AnonCtor(Vec<&'a Term>),
41 Dot(&'a Term, &'a str),
43}
44
45fn as_surface_sugar(term: &Term) -> Option<Sugar<'_>> {
49 let mut head = term;
52 while let Term::App(f, _) = head {
53 head = f;
54 }
55 let is_anon = matches!(head, Term::Global(n) if n == ANON_CTOR_MARKER);
56 let is_dot = matches!(head, Term::Global(n) if n == DOT_MARKER);
57 if !is_anon && !is_dot {
58 return None;
59 }
60 let mut args: Vec<&Term> = Vec::new();
62 let mut cur = term;
63 while let Term::App(f, a) = cur {
64 args.push(a);
65 cur = f;
66 }
67 args.reverse();
68 if is_anon {
69 Some(Sugar::AnonCtor(args))
70 } else if let [_, Term::Global(field)] = args.as_slice() {
71 Some(Sugar::Dot(args[0], field))
72 } else {
73 None
74 }
75}
76
77fn elaborate_sugar(
81 ctx: &Context,
82 mctx: &mut MetaCtx,
83 sugar: Sugar<'_>,
84 expected: Option<&Term>,
85) -> KernelResult<(Term, Term)> {
86 let result = match sugar {
87 Sugar::AnonCtor(comps) => {
88 let expected = expected.ok_or_else(|| {
89 KernelError::CertificationError(
90 "anonymous constructor `⟨…⟩` needs a known expected type to choose its \
91 inductive — annotate it or use it where a type is expected"
92 .to_string(),
93 )
94 })?;
95 let comps: Vec<Term> = comps.into_iter().cloned().collect();
96 elaborate_anon_ctor(ctx, mctx, expected, &comps)?
97 }
98 Sugar::Dot(receiver, field) => elaborate_dot(ctx, mctx, receiver, field)?,
99 };
100 let ty = infer_type(ctx, &result)?;
101 Ok((result, ty))
102}
103
104#[derive(Debug, Default, Clone)]
106pub struct MetaCtx {
107 solutions: HashMap<String, Term>,
108 counter: usize,
109}
110
111impl MetaCtx {
112 pub fn new() -> Self {
113 MetaCtx::default()
114 }
115
116 pub fn fresh(&mut self) -> Term {
118 let m = Term::Var(format!("?{}", self.counter));
119 self.counter += 1;
120 m
121 }
122
123 pub fn solution(&self, name: &str) -> Option<&Term> {
125 self.solutions.get(name)
126 }
127
128 pub fn solve(&mut self, name: &str, term: Term) {
133 self.solutions.insert(name.to_string(), term);
134 }
135}
136
137pub fn is_meta(name: &str) -> bool {
139 name.starts_with('?')
140}
141
142pub fn instantiate(term: &Term, mctx: &MetaCtx) -> Term {
145 match term {
146 Term::Var(name) if is_meta(name) => match mctx.solutions.get(name) {
147 Some(sol) => instantiate(sol, mctx),
148 None => term.clone(),
149 },
150 Term::Var(_) | Term::Global(_) | Term::Sort(_) | Term::Lit(_) | Term::Hole => term.clone(),
151 Term::Const { name, levels } => {
152 Term::Const { name: name.clone(), levels: levels.clone() }
153 }
154 Term::Pi { param, param_type, body_type } => Term::Pi {
155 param: param.clone(),
156 param_type: Box::new(instantiate(param_type, mctx)),
157 body_type: Box::new(instantiate(body_type, mctx)),
158 },
159 Term::Lambda { param, param_type, body } => Term::Lambda {
160 param: param.clone(),
161 param_type: Box::new(instantiate(param_type, mctx)),
162 body: Box::new(instantiate(body, mctx)),
163 },
164 Term::App(f, a) => {
165 Term::App(Box::new(instantiate(f, mctx)), Box::new(instantiate(a, mctx)))
166 }
167 Term::Match { discriminant, motive, cases } => Term::Match {
168 discriminant: Box::new(instantiate(discriminant, mctx)),
169 motive: Box::new(instantiate(motive, mctx)),
170 cases: cases.iter().map(|c| instantiate(c, mctx)).collect(),
171 },
172 Term::Fix { name, body } => {
173 Term::Fix { name: name.clone(), body: Box::new(instantiate(body, mctx)) }
174 }
175 Term::MutualFix { defs, index } => Term::MutualFix {
176 defs: defs.iter().map(|(n, b)| (n.clone(), instantiate(b, mctx))).collect(),
177 index: *index,
178 },
179 Term::Let { name, ty, value, body } => Term::Let {
180 name: name.clone(),
181 ty: Box::new(instantiate(ty, mctx)),
182 value: Box::new(instantiate(value, mctx)),
183 body: Box::new(instantiate(body, mctx)),
184 },
185 }
186}
187
188fn occurs(m: &str, term: &Term) -> bool {
191 match term {
192 Term::Var(name) => name == m,
193 Term::Global(_) | Term::Sort(_) | Term::Lit(_) | Term::Hole | Term::Const { .. } => false,
194 Term::Pi { param_type, body_type, .. } => occurs(m, param_type) || occurs(m, body_type),
195 Term::Lambda { param_type, body, .. } => occurs(m, param_type) || occurs(m, body),
196 Term::App(f, a) => occurs(m, f) || occurs(m, a),
197 Term::Match { discriminant, motive, cases } => {
198 occurs(m, discriminant) || occurs(m, motive) || cases.iter().any(|c| occurs(m, c))
199 }
200 Term::Fix { body, .. } => occurs(m, body),
201 Term::MutualFix { defs, .. } => defs.iter().any(|(_, b)| occurs(m, b)),
202 Term::Let { ty, value, body, .. } => {
203 occurs(m, ty) || occurs(m, value) || occurs(m, body)
204 }
205 }
206}
207
208fn resolve(ctx: &Context, mctx: &MetaCtx, t: &Term) -> Term {
210 normalize(ctx, &instantiate(t, mctx))
211}
212
213pub fn unify(ctx: &Context, mctx: &mut MetaCtx, a: &Term, b: &Term) -> bool {
217 unify_in(ctx, mctx, &[], a, b)
218}
219
220pub fn unify_in(
226 ctx: &Context,
227 mctx: &mut MetaCtx,
228 lctx: &[(String, Term)],
229 a: &Term,
230 b: &Term,
231) -> bool {
232 let a = resolve(ctx, mctx, a);
233 let b = resolve(ctx, mctx, b);
234
235 if let Term::Var(n) = &a {
236 if is_meta(n) {
237 return assign(ctx, mctx, n, &b);
238 }
239 }
240 if let Term::Var(n) = &b {
241 if is_meta(n) {
242 return assign(ctx, mctx, n, &a);
243 }
244 }
245
246 if let Some(result) = try_pattern(mctx, lctx, &a, &b) {
248 return result;
249 }
250 if let Some(result) = try_pattern(mctx, lctx, &b, &a) {
251 return result;
252 }
253
254 match (&a, &b) {
257 (Term::Lambda { param, param_type, body }, other)
258 | (other, Term::Lambda { param, param_type, body })
259 if !matches!(other, Term::Lambda { .. }) =>
260 {
261 let applied = Term::App(Box::new(other.clone()), Box::new(Term::Var(param.clone())));
262 let mut lctx2 = lctx.to_vec();
263 lctx2.push((param.clone(), (**param_type).clone()));
264 return unify_in(ctx, mctx, &lctx2, body, &applied);
265 }
266 _ => {}
267 }
268
269 match (&a, &b) {
270 (Term::Sort(u), Term::Sort(v)) => u.equiv(v),
271 (Term::Global(x), Term::Global(y)) => x == y,
272 (Term::Var(x), Term::Var(y)) => x == y,
273 (Term::Lit(x), Term::Lit(y)) => x == y,
274 (Term::Hole, Term::Hole) => true,
275 (Term::App(f1, a1), Term::App(f2, a2)) => {
276 unify_in(ctx, mctx, lctx, f1, f2) && unify_in(ctx, mctx, lctx, a1, a2)
277 }
278 (
279 Term::Pi { param: p1, param_type: t1, body_type: b1 },
280 Term::Pi { param: p2, param_type: t2, body_type: b2 },
281 ) => unify_binder(ctx, mctx, lctx, t1, b1, p1, t2, b2, p2),
282 (
283 Term::Lambda { param: p1, param_type: t1, body: b1 },
284 Term::Lambda { param: p2, param_type: t2, body: b2 },
285 ) => unify_binder(ctx, mctx, lctx, t1, b1, p1, t2, b2, p2),
286 (
287 Term::Const { name: n1, levels: l1 },
288 Term::Const { name: n2, levels: l2 },
289 ) => n1 == n2 && l1.len() == l2.len() && l1.iter().zip(l2.iter()).all(|(x, y)| x.equiv(y)),
290 _ => false,
291 }
292}
293
294fn try_pattern(
300 mctx: &mut MetaCtx,
301 lctx: &[(String, Term)],
302 a: &Term,
303 b: &Term,
304) -> Option<bool> {
305 let (head, args) = spine(a);
306 if args.is_empty() {
307 return None;
308 }
309 let meta = match &head {
310 Term::Var(m) if is_meta(m) && mctx.solution(m).is_none() => m.clone(),
311 _ => return None,
312 };
313 let mut var_names: Vec<String> = Vec::new();
315 for arg in &args {
316 match arg {
317 Term::Var(v) if !is_meta(v) && lctx.iter().any(|(n, _)| n == v) => {
318 if var_names.iter().any(|u| u == v) {
319 return None; }
321 var_names.push(v.clone());
322 }
323 _ => return None, }
325 }
326 if occurs(&meta, b) {
328 return Some(false); }
330 if !pattern_rhs_in_scope(b, &var_names, &mut Vec::new()) {
331 return Some(false); }
333 let mut sol = b.clone();
335 for name in var_names.iter().rev() {
336 let ty = lctx
337 .iter()
338 .find(|(n, _)| n == name)
339 .map(|(_, t)| t.clone())
340 .unwrap_or(Term::Hole);
341 sol = Term::Lambda { param: name.clone(), param_type: Box::new(ty), body: Box::new(sol) };
342 }
343 mctx.solve(&meta, sol);
344 Some(true)
345}
346
347fn pattern_rhs_in_scope(t: &Term, allowed: &[String], bound: &mut Vec<String>) -> bool {
351 match t {
352 Term::Var(v) => is_meta(v) || bound.iter().any(|b| b == v) || allowed.iter().any(|a| a == v),
353 Term::Sort(_) | Term::Global(_) | Term::Lit(_) | Term::Hole | Term::Const { .. } => true,
354 Term::App(f, a) => {
355 pattern_rhs_in_scope(f, allowed, bound) && pattern_rhs_in_scope(a, allowed, bound)
356 }
357 Term::Pi { param, param_type, body_type } => {
358 if !pattern_rhs_in_scope(param_type, allowed, bound) {
359 return false;
360 }
361 bound.push(param.clone());
362 let ok = pattern_rhs_in_scope(body_type, allowed, bound);
363 bound.pop();
364 ok
365 }
366 Term::Lambda { param, param_type, body } => {
367 if !pattern_rhs_in_scope(param_type, allowed, bound) {
368 return false;
369 }
370 bound.push(param.clone());
371 let ok = pattern_rhs_in_scope(body, allowed, bound);
372 bound.pop();
373 ok
374 }
375 Term::Fix { name, body } => {
376 bound.push(name.clone());
377 let ok = pattern_rhs_in_scope(body, allowed, bound);
378 bound.pop();
379 ok
380 }
381 Term::MutualFix { defs, .. } => {
382 for (n, _) in defs {
383 bound.push(n.clone());
384 }
385 let ok = defs.iter().all(|(_, b)| pattern_rhs_in_scope(b, allowed, bound));
386 for _ in defs {
387 bound.pop();
388 }
389 ok
390 }
391 Term::Match { discriminant, motive, cases } => {
392 pattern_rhs_in_scope(discriminant, allowed, bound)
393 && pattern_rhs_in_scope(motive, allowed, bound)
394 && cases.iter().all(|c| pattern_rhs_in_scope(c, allowed, bound))
395 }
396 Term::Let { name, ty, value, body } => {
397 if !pattern_rhs_in_scope(ty, allowed, bound)
398 || !pattern_rhs_in_scope(value, allowed, bound)
399 {
400 return false;
401 }
402 bound.push(name.clone());
403 let ok = pattern_rhs_in_scope(body, allowed, bound);
404 bound.pop();
405 ok
406 }
407 }
408}
409
410fn spine(t: &Term) -> (Term, Vec<Term>) {
412 let mut args = Vec::new();
413 let mut cur = t;
414 while let Term::App(f, a) = cur {
415 args.push((**a).clone());
416 cur = f;
417 }
418 args.reverse();
419 (cur.clone(), args)
420}
421
422#[allow(clippy::too_many_arguments)]
426fn unify_binder(
427 ctx: &Context,
428 mctx: &mut MetaCtx,
429 lctx: &[(String, Term)],
430 dom1: &Term,
431 body1: &Term,
432 p1: &str,
433 dom2: &Term,
434 body2: &Term,
435 p2: &str,
436) -> bool {
437 if !unify_in(ctx, mctx, lctx, dom1, dom2) {
438 return false;
439 }
440 let body2 = if p1 == p2 {
441 body2.clone()
442 } else {
443 substitute(body2, p2, &Term::Var(p1.to_string()))
444 };
445 let mut ext = lctx.to_vec();
446 ext.push((p1.to_string(), dom1.clone()));
447 unify_in(ctx, mctx, &ext, body1, &body2)
448}
449
450fn assign(ctx: &Context, mctx: &mut MetaCtx, m: &str, t: &Term) -> bool {
453 if let Some(sol) = mctx.solutions.get(m).cloned() {
454 return unify(ctx, mctx, &sol, t);
455 }
456 let t = instantiate(t, mctx);
457 if matches!(&t, Term::Var(n) if n == m) {
459 return true;
460 }
461 if occurs(m, &t) {
462 return false;
463 }
464 mctx.solutions.insert(m.to_string(), t);
465 true
466}
467
468pub fn elaborate(
472 ctx: &Context,
473 mctx: &mut MetaCtx,
474 term: &Term,
475 expected: Option<&Term>,
476) -> KernelResult<(Term, Term)> {
477 elaborate_in(ctx, mctx, &[], term, expected)
478}
479
480pub fn elaborate_in(
486 ctx: &Context,
487 mctx: &mut MetaCtx,
488 lctx: &[(String, Term)],
489 term: &Term,
490 expected: Option<&Term>,
491) -> KernelResult<(Term, Term)> {
492 if let Some(sugar) = as_surface_sugar(term) {
496 return elaborate_sugar(ctx, mctx, sugar, expected);
497 }
498 match term {
499 Term::Hole => {
500 let m = mctx.fresh();
501 let ty = expected.cloned().unwrap_or_else(|| mctx.fresh());
502 Ok((m, ty))
503 }
504 Term::App(f, a) => {
505 let (f_elab, f_ty) = elaborate_in(ctx, mctx, lctx, f, None)?;
506 let f_ty = resolve(ctx, mctx, &f_ty);
507 match f_ty {
508 Term::Pi { param, param_type, body_type } => {
509 let (a_elab, a_ty) = elaborate_in(ctx, mctx, lctx, a, Some(¶m_type))?;
510 if !unify_in(ctx, mctx, lctx, &a_ty, ¶m_type) {
511 return Err(KernelError::TypeMismatch {
512 expected: format!("{}", instantiate(¶m_type, mctx)),
513 found: format!("{}", instantiate(&a_ty, mctx)),
514 });
515 }
516 let result_ty = substitute(&body_type, ¶m, &a_elab);
517 if let Some(exp) = expected {
520 unify_in(ctx, mctx, lctx, &result_ty, exp);
521 }
522 Ok((Term::App(Box::new(f_elab), Box::new(a_elab)), result_ty))
523 }
524 other => Err(KernelError::NotAFunction(format!("{}", other))),
525 }
526 }
527 Term::Lambda { param, param_type, body } => {
528 let body_expected = match expected.map(|e| resolve(ctx, mctx, e)) {
532 Some(Term::Pi { param: ep, body_type: ecod, .. }) => Some(if ep == *param {
533 (*ecod).clone()
534 } else {
535 substitute(&ecod, &ep, &Term::Var(param.clone()))
536 }),
537 _ => None,
538 };
539 let ext_ctx = ctx.extend(param, (**param_type).clone());
540 let mut ext_lctx = lctx.to_vec();
541 ext_lctx.push((param.clone(), (**param_type).clone()));
542 let (body_elab, body_ty) =
543 elaborate_in(&ext_ctx, mctx, &ext_lctx, body, body_expected.as_ref())?;
544 Ok((
545 Term::Lambda {
546 param: param.clone(),
547 param_type: param_type.clone(),
548 body: Box::new(body_elab),
549 },
550 Term::Pi {
551 param: param.clone(),
552 param_type: param_type.clone(),
553 body_type: Box::new(body_ty),
554 },
555 ))
556 }
557 _ => {
558 let ty = infer_type(ctx, term)?;
563 if let Some(exp) = expected {
564 unify_in(ctx, mctx, lctx, &ty, exp);
565 }
566 Ok((term.clone(), ty))
567 }
568 }
569}
570
571pub fn fill_match_motives(
579 ctx: &Context,
580 term: &Term,
581 expected: Option<&Term>,
582) -> KernelResult<Term> {
583 match term {
584 Term::Match { discriminant, motive, cases } => {
585 let disc = fill_match_motives(ctx, discriminant, None)?;
586 let cases = cases
587 .iter()
588 .map(|c| fill_match_motives(ctx, c, None))
589 .collect::<KernelResult<Vec<_>>>()?;
590 let motive = if matches!(motive.as_ref(), Term::Hole) {
591 infer_match_motive(ctx, &disc, &cases, expected)?
592 } else {
593 fill_match_motives(ctx, motive, None)?
594 };
595 Ok(Term::Match {
596 discriminant: Box::new(disc),
597 motive: Box::new(motive),
598 cases,
599 })
600 }
601 Term::Lambda { param, param_type, body } => {
602 let ext = ctx.extend(param, (**param_type).clone());
603 let body_expected = match expected.map(|e| normalize(ctx, e)) {
605 Some(Term::Pi { param: ep, body_type, .. }) => Some(if ep == *param {
606 *body_type
607 } else {
608 substitute(&body_type, &ep, &Term::Var(param.clone()))
609 }),
610 _ => None,
611 };
612 Ok(Term::Lambda {
613 param: param.clone(),
614 param_type: param_type.clone(),
615 body: Box::new(fill_match_motives(&ext, body, body_expected.as_ref())?),
616 })
617 }
618 Term::App(f, a) => Ok(Term::App(
619 Box::new(fill_match_motives(ctx, f, None)?),
620 Box::new(fill_match_motives(ctx, a, None)?),
621 )),
622 Term::Pi { param, param_type, body_type } => {
623 let ext = ctx.extend(param, (**param_type).clone());
624 Ok(Term::Pi {
625 param: param.clone(),
626 param_type: Box::new(fill_match_motives(ctx, param_type, None)?),
627 body_type: Box::new(fill_match_motives(&ext, body_type, None)?),
628 })
629 }
630 Term::Fix { name, body } => Ok(Term::Fix {
631 name: name.clone(),
632 body: Box::new(fill_match_motives(ctx, body, None)?),
633 }),
634 _ => Ok(term.clone()),
635 }
636}
637
638fn infer_match_motive(
646 ctx: &Context,
647 disc: &Term,
648 cases: &[Term],
649 expected: Option<&Term>,
650) -> KernelResult<Term> {
651 let disc_ty = normalize(ctx, &infer_type(ctx, disc)?);
652 let result_ty = match expected {
653 Some(t) => t.clone(),
654 None => {
655 match cases.first() {
658 Some(c) if !matches!(c, Term::Lambda { .. }) => infer_type(ctx, c)?,
659 _ => {
660 return Err(KernelError::CertificationError(
661 "cannot infer the motive of this `match`; add a `return` clause or a \
662 type annotation"
663 .to_string(),
664 ))
665 }
666 }
667 }
668 };
669 let (param, body) = match disc {
670 Term::Var(v) => (v.clone(), result_ty),
673 other => {
675 let p = "__motive".to_string();
676 (p.clone(), replace_subterm(&result_ty, other, &Term::Var(p)))
677 }
678 };
679 Ok(Term::Lambda { param, param_type: Box::new(disc_ty), body: Box::new(body) })
680}
681
682fn replace_subterm(t: &Term, target: &Term, repl: &Term) -> Term {
684 if t == target {
685 return repl.clone();
686 }
687 match t {
688 Term::Var(_) | Term::Global(_) | Term::Sort(_) | Term::Lit(_) | Term::Hole
689 | Term::Const { .. } => t.clone(),
690 Term::Pi { param, param_type, body_type } => Term::Pi {
691 param: param.clone(),
692 param_type: Box::new(replace_subterm(param_type, target, repl)),
693 body_type: Box::new(replace_subterm(body_type, target, repl)),
694 },
695 Term::Lambda { param, param_type, body } => Term::Lambda {
696 param: param.clone(),
697 param_type: Box::new(replace_subterm(param_type, target, repl)),
698 body: Box::new(replace_subterm(body, target, repl)),
699 },
700 Term::App(f, a) => Term::App(
701 Box::new(replace_subterm(f, target, repl)),
702 Box::new(replace_subterm(a, target, repl)),
703 ),
704 Term::Match { discriminant, motive, cases } => Term::Match {
705 discriminant: Box::new(replace_subterm(discriminant, target, repl)),
706 motive: Box::new(replace_subterm(motive, target, repl)),
707 cases: cases.iter().map(|c| replace_subterm(c, target, repl)).collect(),
708 },
709 Term::Fix { name, body } => {
710 Term::Fix { name: name.clone(), body: Box::new(replace_subterm(body, target, repl)) }
711 }
712 Term::MutualFix { defs, index } => Term::MutualFix {
713 defs: defs.iter().map(|(n, b)| (n.clone(), replace_subterm(b, target, repl))).collect(),
714 index: *index,
715 },
716 Term::Let { name, ty, value, body } => Term::Let {
717 name: name.clone(),
718 ty: Box::new(replace_subterm(ty, target, repl)),
719 value: Box::new(replace_subterm(value, target, repl)),
720 body: Box::new(replace_subterm(body, target, repl)),
721 },
722 }
723}
724
725pub fn auto_bind_implicits(
734 ctx: &Context,
735 ty: &Term,
736 body: &Term,
737 existing_implicit: usize,
738) -> (Term, Term, usize) {
739 let mut candidates: Vec<String> = Vec::new();
740 collect_autobind(ctx, ty, &mut candidates);
741 collect_autobind(ctx, body, &mut candidates);
742 if candidates.is_empty() {
743 return (ty.clone(), body.clone(), existing_implicit);
744 }
745
746 let mut new_ty = ty.clone();
747 let mut new_body = body.clone();
748 for name in &candidates {
749 new_ty = global_to_var(&new_ty, name);
750 new_body = global_to_var(&new_body, name);
751 }
752 for name in candidates.iter().rev() {
754 new_ty = Term::Pi {
755 param: name.clone(),
756 param_type: Box::new(Term::Sort(crate::term::Universe::Type(0))),
757 body_type: Box::new(new_ty),
758 };
759 new_body = Term::Lambda {
760 param: name.clone(),
761 param_type: Box::new(Term::Sort(crate::term::Universe::Type(0))),
762 body: Box::new(new_body),
763 };
764 }
765 (new_ty, new_body, existing_implicit + candidates.len())
766}
767
768fn is_autobind_name(n: &str) -> bool {
770 n.len() == 1 && n.chars().next().is_some_and(|c| c.is_ascii_uppercase())
771}
772
773fn collect_autobind(ctx: &Context, term: &Term, acc: &mut Vec<String>) {
775 match term {
776 Term::Global(n) => {
777 if is_autobind_name(n) && ctx.get_global(n).is_none() && !acc.contains(n) {
778 acc.push(n.clone());
779 }
780 }
781 Term::Pi { param_type, body_type, .. } => {
782 collect_autobind(ctx, param_type, acc);
783 collect_autobind(ctx, body_type, acc);
784 }
785 Term::Lambda { param_type, body, .. } => {
786 collect_autobind(ctx, param_type, acc);
787 collect_autobind(ctx, body, acc);
788 }
789 Term::App(f, a) => {
790 collect_autobind(ctx, f, acc);
791 collect_autobind(ctx, a, acc);
792 }
793 Term::Match { discriminant, motive, cases } => {
794 collect_autobind(ctx, discriminant, acc);
795 collect_autobind(ctx, motive, acc);
796 for c in cases {
797 collect_autobind(ctx, c, acc);
798 }
799 }
800 Term::Fix { body, .. } => collect_autobind(ctx, body, acc),
801 _ => {}
802 }
803}
804
805pub fn bind_self_recursion(name: &str, body: &Term) -> Term {
815 if references_global(body, name) {
816 Term::Fix { name: name.to_string(), body: Box::new(global_to_var(body, name)) }
817 } else {
818 body.clone()
819 }
820}
821
822fn references_global(term: &Term, name: &str) -> bool {
825 match term {
826 Term::Global(n) => n == name,
827 Term::Var(_) | Term::Sort(_) | Term::Lit(_) | Term::Hole | Term::Const { .. } => false,
828 Term::Pi { param_type, body_type, .. } => {
829 references_global(param_type, name) || references_global(body_type, name)
830 }
831 Term::Lambda { param_type, body, .. } => {
832 references_global(param_type, name) || references_global(body, name)
833 }
834 Term::App(f, a) => references_global(f, name) || references_global(a, name),
835 Term::Match { discriminant, motive, cases } => {
836 references_global(discriminant, name)
837 || references_global(motive, name)
838 || cases.iter().any(|c| references_global(c, name))
839 }
840 Term::Fix { body, .. } => references_global(body, name),
841 Term::MutualFix { defs, .. } => defs.iter().any(|(_, b)| references_global(b, name)),
842 Term::Let { ty, value, body, .. } => {
843 references_global(ty, name)
844 || references_global(value, name)
845 || references_global(body, name)
846 }
847 }
848}
849
850fn global_to_var(term: &Term, name: &str) -> Term {
851 match term {
852 Term::Global(n) if n == name => Term::Var(n.clone()),
853 Term::Global(_) | Term::Var(_) | Term::Sort(_) | Term::Lit(_) | Term::Hole
854 | Term::Const { .. } => term.clone(),
855 Term::Pi { param, param_type, body_type } => Term::Pi {
856 param: param.clone(),
857 param_type: Box::new(global_to_var(param_type, name)),
858 body_type: Box::new(global_to_var(body_type, name)),
859 },
860 Term::Lambda { param, param_type, body } => Term::Lambda {
861 param: param.clone(),
862 param_type: Box::new(global_to_var(param_type, name)),
863 body: Box::new(global_to_var(body, name)),
864 },
865 Term::App(f, a) => {
866 Term::App(Box::new(global_to_var(f, name)), Box::new(global_to_var(a, name)))
867 }
868 Term::Match { discriminant, motive, cases } => Term::Match {
869 discriminant: Box::new(global_to_var(discriminant, name)),
870 motive: Box::new(global_to_var(motive, name)),
871 cases: cases.iter().map(|c| global_to_var(c, name)).collect(),
872 },
873 Term::Fix { name: fname, body } => {
874 Term::Fix { name: fname.clone(), body: Box::new(global_to_var(body, name)) }
875 }
876 Term::MutualFix { defs, index } => Term::MutualFix {
877 defs: defs.iter().map(|(fname, b)| (fname.clone(), global_to_var(b, name))).collect(),
878 index: *index,
879 },
880 Term::Let { name: lname, ty, value, body } => Term::Let {
881 name: lname.clone(),
882 ty: Box::new(global_to_var(ty, name)),
883 value: Box::new(global_to_var(value, name)),
884 body: Box::new(global_to_var(body, name)),
885 },
886 }
887}
888
889pub fn surface_elaborate(ctx: &Context, term: &Term) -> KernelResult<Term> {
895 surface_elaborate_against(ctx, term, None)
896}
897
898pub fn surface_elaborate_against(
903 ctx: &Context,
904 term: &Term,
905 expected: Option<&Term>,
906) -> KernelResult<Term> {
907 let mut mctx = MetaCtx::new();
908 let elaborated = elab_surface(ctx, &mut mctx, term, expected)?;
909 Ok(instantiate(&elaborated, &mctx))
910}
911
912fn elab_surface(
913 ctx: &Context,
914 mctx: &mut MetaCtx,
915 term: &Term,
916 expected: Option<&Term>,
917) -> KernelResult<Term> {
918 if let Some(sugar) = as_surface_sugar(term) {
921 return elaborate_sugar(ctx, mctx, sugar, expected).map(|(t, _)| t);
922 }
923 match term {
924 Term::App(..) => {
925 let mut args: Vec<&Term> = Vec::new();
927 let mut cur = term;
928 while let Term::App(f, a) = cur {
929 args.push(a);
930 cur = f;
931 }
932 args.reverse();
933 let head = elab_surface(ctx, mctx, cur, None)?;
934 let args: Vec<Term> = args
935 .iter()
936 .map(|a| elab_surface(ctx, mctx, a, None))
937 .collect::<KernelResult<_>>()?;
938
939 if let Term::Global(name) = &head {
940 if let Some(head_ty) = ctx.get_global(name).cloned() {
941 let kinds = match ctx.binder_kinds(name) {
951 Some(bk)
952 if bk.iter().filter(|k| **k == ParamKind::Explicit).count()
953 == args.len() =>
954 {
955 bk.to_vec()
956 }
957 _ => {
958 let k = ctx.implicit_args(name);
959 let mut kinds = vec![ParamKind::Implicit; k];
960 kinds.extend(std::iter::repeat(ParamKind::Explicit).take(args.len()));
961 kinds
962 }
963 };
964 if let Ok((t, _)) =
965 elaborate_app_against(ctx, mctx, &head, &head_ty, &kinds, &args, expected)
966 {
967 return Ok(t);
968 }
969 }
973 }
974 Ok(args.into_iter().fold(head, |f, a| Term::App(Box::new(f), Box::new(a))))
975 }
976 Term::Global(name) if expected.is_some() && ctx.implicit_args(name) > 0 => {
980 let k = ctx.implicit_args(name);
981 if let Some(head_ty) = ctx.get_global(name).cloned() {
982 let kinds = vec![ParamKind::Implicit; k];
983 let (t, _) =
984 elaborate_app_against(ctx, mctx, term, &head_ty, &kinds, &[], expected)?;
985 Ok(t)
986 } else {
987 Ok(term.clone())
988 }
989 }
990 Term::Lambda { param, param_type, body } => Ok(Term::Lambda {
991 param: param.clone(),
992 param_type: Box::new(elab_surface(ctx, mctx, param_type, None)?),
993 body: Box::new(elab_surface(ctx, mctx, body, None)?),
994 }),
995 Term::Pi { param, param_type, body_type } => Ok(Term::Pi {
996 param: param.clone(),
997 param_type: Box::new(elab_surface(ctx, mctx, param_type, None)?),
998 body_type: Box::new(elab_surface(ctx, mctx, body_type, None)?),
999 }),
1000 Term::Fix { name, body } => Ok(Term::Fix {
1001 name: name.clone(),
1002 body: Box::new(elab_surface(ctx, mctx, body, None)?),
1003 }),
1004 Term::Match { discriminant, motive, cases } => Ok(Term::Match {
1005 discriminant: Box::new(elab_surface(ctx, mctx, discriminant, None)?),
1006 motive: Box::new(elab_surface(ctx, mctx, motive, None)?),
1007 cases: cases
1008 .iter()
1009 .map(|c| elab_surface(ctx, mctx, c, None))
1010 .collect::<KernelResult<_>>()?,
1011 }),
1012 _ => Ok(term.clone()),
1013 }
1014}
1015
1016#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1018pub enum ParamKind {
1019 Explicit,
1021 Implicit,
1023 Instance,
1027}
1028
1029pub fn elaborate_app(
1035 ctx: &Context,
1036 mctx: &mut MetaCtx,
1037 head: &Term,
1038 head_ty: &Term,
1039 kinds: &[ParamKind],
1040 explicit_args: &[Term],
1041) -> KernelResult<(Term, Term)> {
1042 elaborate_app_against(ctx, mctx, head, head_ty, kinds, explicit_args, None)
1043}
1044
1045#[allow(clippy::too_many_arguments)]
1051pub fn elaborate_app_against(
1052 ctx: &Context,
1053 mctx: &mut MetaCtx,
1054 head: &Term,
1055 head_ty: &Term,
1056 kinds: &[ParamKind],
1057 explicit_args: &[Term],
1058 expected: Option<&Term>,
1059) -> KernelResult<(Term, Term)> {
1060 let mut cur_term = head.clone();
1061 let mut cur_ty = head_ty.clone();
1062 let mut next_arg = 0usize;
1063 let mut obligations: Vec<(String, Term)> = Vec::new();
1065
1066 for kind in kinds {
1067 let (param, dom, body) = match resolve(ctx, mctx, &cur_ty) {
1068 Term::Pi { param, param_type, body_type } => (param, *param_type, *body_type),
1069 other => {
1070 return Err(KernelError::NotAFunction(format!(
1071 "expected a Π to apply an argument, got {}",
1072 other
1073 )))
1074 }
1075 };
1076
1077 let arg = match kind {
1078 ParamKind::Implicit => mctx.fresh(),
1079 ParamKind::Instance => {
1080 let m = mctx.fresh();
1081 if let Term::Var(name) = &m {
1082 obligations.push((name.clone(), dom.clone()));
1083 }
1084 m
1085 }
1086 ParamKind::Explicit => {
1087 let provided = explicit_args.get(next_arg).ok_or_else(|| {
1088 KernelError::CertificationError("too few explicit arguments".to_string())
1089 })?;
1090 next_arg += 1;
1091 let (a_elab, a_ty) = elaborate(ctx, mctx, provided, Some(&dom))?;
1092 if unify(ctx, mctx, &a_ty, &dom) {
1093 a_elab
1094 } else {
1095 let a_ty_i = instantiate(&a_ty, mctx);
1099 let dom_i = instantiate(&dom, mctx);
1100 match resolve_coercion(ctx, mctx, &a_ty_i, &dom_i) {
1101 Some(coe) => Term::App(Box::new(coe), Box::new(a_elab)),
1102 None => {
1103 return Err(KernelError::TypeMismatch {
1104 expected: format!("{}", dom_i),
1105 found: format!("{}", a_ty_i),
1106 })
1107 }
1108 }
1109 }
1110 }
1111 };
1112
1113 cur_term = Term::App(Box::new(cur_term), Box::new(arg.clone()));
1114 cur_ty = substitute(&body, ¶m, &arg);
1115 }
1116
1117 if next_arg != explicit_args.len() {
1118 return Err(KernelError::CertificationError(format!(
1119 "too many explicit arguments: {} provided, {} consumed",
1120 explicit_args.len(),
1121 next_arg
1122 )));
1123 }
1124
1125 if let Some(exp) = expected {
1128 if !unify(ctx, mctx, &cur_ty, exp) {
1129 return Err(KernelError::TypeMismatch {
1130 expected: format!("{}", instantiate(exp, mctx)),
1131 found: format!("{}", instantiate(&cur_ty, mctx)),
1132 });
1133 }
1134 }
1135
1136 for (meta_name, class_ty) in &obligations {
1139 let required = instantiate(class_ty, mctx);
1140 match resolve_instance(ctx, mctx, &required) {
1141 Some(inst) => {
1142 mctx.solve(meta_name, inst);
1145 }
1146 None => {
1147 return Err(KernelError::CertificationError(format!(
1148 "no typeclass instance found for {}",
1149 required
1150 )))
1151 }
1152 }
1153 }
1154
1155 Ok((instantiate(&cur_term, mctx), instantiate(&cur_ty, mctx)))
1156}
1157
1158const MAX_INSTANCE_DEPTH: usize = 64;
1161
1162fn head_global(t: &Term) -> Option<&str> {
1164 let mut cur = t;
1165 while let Term::App(f, _) = cur {
1166 cur = f;
1167 }
1168 match cur {
1169 Term::Global(n) => Some(n),
1170 _ => None,
1171 }
1172}
1173
1174fn class_heads(ctx: &Context) -> std::collections::HashSet<String> {
1179 ctx.instances()
1180 .iter()
1181 .filter_map(|(ty, _)| {
1182 let mut cur = ty;
1183 while let Term::Pi { body_type, .. } = cur {
1184 cur = body_type;
1185 }
1186 head_global(cur).map(|s| s.to_string())
1187 })
1188 .collect()
1189}
1190
1191pub fn resolve_instance(ctx: &Context, mctx: &mut MetaCtx, required: &Term) -> Option<Term> {
1199 resolve_instance_at(ctx, mctx, required, 0)
1200}
1201
1202pub fn elaborate_anon_ctor(
1208 ctx: &Context,
1209 mctx: &mut MetaCtx,
1210 expected: &Term,
1211 fields: &[Term],
1212) -> KernelResult<Term> {
1213 let exp = crate::normalize(ctx, &instantiate(expected, mctx));
1214 let (head, args) = spine(&exp);
1215 let hname = match &head {
1216 Term::Global(n) => n.clone(),
1217 _ => {
1218 return Err(KernelError::CertificationError(format!(
1219 "anonymous constructor: expected type {exp} is not an inductive"
1220 )))
1221 }
1222 };
1223 let ctors = ctx.get_constructors(&hname);
1224 let ctor = match ctors.as_slice() {
1225 [(c, _)] => c.to_string(),
1226 _ => {
1227 return Err(KernelError::CertificationError(format!(
1228 "anonymous constructor: `{hname}` does not have exactly one constructor"
1229 )))
1230 }
1231 };
1232 let mut applied = Term::Global(ctor);
1235 for a in &args {
1236 applied = Term::App(Box::new(applied), Box::new(a.clone()));
1237 }
1238 for fv in fields {
1239 let dom = match resolve(ctx, mctx, &crate::infer_type(ctx, &applied)?) {
1240 Term::Pi { param_type, .. } => Some(*param_type),
1241 _ => None,
1242 };
1243 let (fe, fty) = elaborate(ctx, mctx, fv, dom.as_ref())?;
1244 let arg = if let Some(d) = &dom {
1245 if unify(ctx, mctx, &fty, d) {
1246 fe
1247 } else {
1248 match resolve_coercion(ctx, mctx, &instantiate(&fty, mctx), &instantiate(d, mctx)) {
1249 Some(coe) => Term::App(Box::new(coe), Box::new(fe)),
1250 None => fe,
1251 }
1252 }
1253 } else {
1254 fe
1255 };
1256 applied = Term::App(Box::new(applied), Box::new(arg));
1257 }
1258 crate::infer_type(ctx, &applied)?;
1259 Ok(instantiate(&applied, mctx))
1260}
1261
1262pub fn elaborate_dot(
1268 ctx: &Context,
1269 mctx: &mut MetaCtx,
1270 receiver: &Term,
1271 field: &str,
1272) -> KernelResult<Term> {
1273 let (r_elab, r_ty) = elaborate(ctx, mctx, receiver, None)?;
1274 let r_ty = crate::normalize(ctx, &instantiate(&r_ty, mctx));
1275 let (head, args) = spine(&r_ty);
1276 let hname = match &head {
1277 Term::Global(n) => n.clone(),
1278 _ => {
1279 return Err(KernelError::CertificationError(format!(
1280 "dot notation `.{field}`: the receiver's type {r_ty} is not headed by an inductive"
1281 )))
1282 }
1283 };
1284 let proj = format!("{hname}_{field}");
1285 if ctx.get_global(&proj).is_none() {
1286 return Err(KernelError::CertificationError(format!(
1287 "dot notation: no projection `{proj}` for field `{field}` of `{hname}`"
1288 )));
1289 }
1290 let mut applied = Term::Global(proj);
1292 for a in &args {
1293 applied = Term::App(Box::new(applied), Box::new(a.clone()));
1294 }
1295 applied = Term::App(Box::new(applied), Box::new(r_elab));
1296 crate::infer_type(ctx, &applied)?;
1298 Ok(applied)
1299}
1300
1301pub fn resolve_coercion(
1306 ctx: &Context,
1307 mctx: &mut MetaCtx,
1308 from: &Term,
1309 to: &Term,
1310) -> Option<Term> {
1311 for (c_from, c_to, c_fn) in ctx.coercions() {
1312 let mut trial = mctx.clone();
1313 if unify(ctx, &mut trial, c_from, from) && unify(ctx, &mut trial, c_to, to) {
1314 *mctx = trial;
1315 return Some(instantiate(c_fn, mctx));
1316 }
1317 }
1318 None
1319}
1320
1321fn resolve_instance_at(
1322 ctx: &Context,
1323 mctx: &mut MetaCtx,
1324 required: &Term,
1325 depth: usize,
1326) -> Option<Term> {
1327 if depth > MAX_INSTANCE_DEPTH {
1328 return None;
1329 }
1330 let heads = class_heads(ctx);
1331 for (inst_ty, inst_val) in ctx.instances() {
1332 let mut trial = mctx.clone();
1333 if let Some(result) =
1334 try_instance(ctx, &mut trial, inst_ty, inst_val, required, &heads, depth)
1335 {
1336 *mctx = trial;
1337 return Some(result);
1338 }
1339 }
1340 None
1341}
1342
1343#[allow(clippy::too_many_arguments)]
1346fn try_instance(
1347 ctx: &Context,
1348 mctx: &mut MetaCtx,
1349 inst_ty: &Term,
1350 inst_val: &Term,
1351 required: &Term,
1352 heads: &std::collections::HashSet<String>,
1353 depth: usize,
1354) -> Option<Term> {
1355 let mut applied = inst_val.clone();
1359 let mut premises: Vec<(Term, Term)> = Vec::new(); let mut cur = inst_ty.clone();
1361 loop {
1362 match cur {
1363 Term::Pi { param, param_type, body_type } => {
1364 let mv = mctx.fresh();
1365 if head_global(¶m_type).is_some_and(|h| heads.contains(h)) {
1366 premises.push((mv.clone(), (*param_type).clone()));
1367 }
1368 applied = Term::App(Box::new(applied), Box::new(mv.clone()));
1369 cur = substitute(&body_type, ¶m, &mv);
1370 }
1371 conclusion => {
1372 if !unify(ctx, mctx, &conclusion, required) {
1374 return None;
1375 }
1376 for (pm, pty) in &premises {
1379 let sub_goal = instantiate(pty, mctx);
1380 let resolved = resolve_instance_at(ctx, mctx, &sub_goal, depth + 1)?;
1381 match pm {
1384 Term::Var(name) => mctx.solve(name, resolved),
1385 _ => return None,
1386 }
1387 }
1388 return Some(instantiate(&applied, mctx));
1389 }
1390 }
1391 }
1392}