1use std::collections::{BTreeSet, HashMap};
15use std::fmt;
16
17use crate::context::{Context, SimpRule};
18use crate::elaborate::{self, MetaCtx};
19use crate::error::KernelError;
20use crate::term::Term;
21use crate::type_checker::{
22 alpha_equivalent, def_eq, demanded_type_head, infer_sort, infer_type, is_subtype, substitute,
23 TypeHead,
24};
25
26#[derive(Debug, Clone, PartialEq)]
29pub struct Goal {
30 pub hyps: Vec<(String, Term)>,
31 pub target: Term,
32}
33
34impl fmt::Display for Goal {
35 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
36 for (name, ty) in &self.hyps {
37 writeln!(f, "{name} : {}", Shown(ty))?;
38 }
39 write!(f, "⊢ {}", Shown(&self.target))
40 }
41}
42
43#[derive(Debug)]
45pub enum TacticError {
46 NoGoals,
48 DoesNotApply {
50 tactic: &'static str,
51 goal: Goal,
52 why: String,
53 },
54 GoalsRemain(Vec<Goal>),
56 Kernel(KernelError),
59}
60
61impl fmt::Display for TacticError {
62 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
63 match self {
64 TacticError::NoGoals => write!(f, "no goals: the proof is complete"),
65 TacticError::DoesNotApply { tactic, goal, why } => {
66 write!(f, "`{tactic}` does not apply: {why}\nin the goal\n{goal}")
67 }
68 TacticError::GoalsRemain(goals) => {
69 write!(f, "{} goal(s) remain:", goals.len())?;
70 for goal in goals {
71 write!(f, "\n{goal}")?;
72 }
73 Ok(())
74 }
75 TacticError::Kernel(e) => write!(f, "{e}"),
76 }
77 }
78}
79
80impl std::error::Error for TacticError {}
81
82#[derive(Debug, Clone)]
83struct Decl {
84 hyps: Vec<(String, Term)>,
85 target: Term,
86}
87
88#[derive(Debug, Clone, PartialEq)]
90pub enum TacticStep {
91 Intro(Vec<String>),
92 Exact(Term),
93 Apply(Term),
94 Assumption,
95 Exfalso,
96 Constructor,
97 Left,
98 Right,
99 Exists(Term),
100 Have(String, Term),
101 Rfl,
102 Change(Term),
103 Rewrite(Term, bool),
104 Induction(String, Vec<Vec<String>>),
105 Cases(String, Vec<Vec<String>>),
106 Refine(Term),
107 Subst(String),
108 Injection(String, Vec<String>),
109 Calc(Vec<(Term, Term)>),
111 Cc,
112 Decide,
113 Simp(Vec<Term>),
115}
116
117impl fmt::Display for TacticStep {
118 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
119 let pattern = |cases: &[Vec<String>]| {
120 let cases: Vec<String> = cases.iter().map(|c| c.join(" ")).collect();
121 format!(" as [{}]", cases.join(" | "))
122 };
123 match self {
124 TacticStep::Intro(names) => write!(f, "intro {}", names.join(" ")),
125 TacticStep::Exact(t) => write!(f, "exact {}", Shown(t)),
126 TacticStep::Apply(t) => write!(f, "apply {}", Shown(t)),
127 TacticStep::Assumption => write!(f, "assumption"),
128 TacticStep::Exfalso => write!(f, "exfalso"),
129 TacticStep::Constructor => write!(f, "constructor"),
130 TacticStep::Left => write!(f, "left"),
131 TacticStep::Right => write!(f, "right"),
132 TacticStep::Exists(t) => write!(f, "exists {}", Shown(t)),
133 TacticStep::Have(n, t) => write!(f, "have {n} : {}", Shown(t)),
134 TacticStep::Rfl => write!(f, "rfl"),
135 TacticStep::Change(t) => write!(f, "change {}", Shown(t)),
136 TacticStep::Rewrite(t, reverse) => {
137 write!(f, "rw [{}{}]", if *reverse { "<- " } else { "" }, Shown(t))
138 }
139 TacticStep::Induction(x, cases) if cases.is_empty() => write!(f, "induction {x}"),
140 TacticStep::Induction(x, cases) => write!(f, "induction {x}{}", pattern(cases)),
141 TacticStep::Cases(x, cases) if cases.is_empty() => write!(f, "cases {x}"),
142 TacticStep::Cases(x, cases) => write!(f, "cases {x}{}", pattern(cases)),
143 TacticStep::Refine(t) => write!(f, "refine {}", Shown(t)),
144 TacticStep::Subst(h) => write!(f, "subst {h}"),
145 TacticStep::Injection(h, names) if names.is_empty() => write!(f, "injection {h}"),
146 TacticStep::Injection(h, names) => write!(f, "injection {h} as [{}]", names.join(" ")),
147 TacticStep::Calc(steps) => {
148 let steps: Vec<String> = steps
149 .iter()
150 .map(|(r, p)| format!("{} := {}", Shown(r), Shown(p)))
151 .collect();
152 write!(f, "calc [{}]", steps.join(", "))
153 }
154 TacticStep::Cc => write!(f, "cc"),
155 TacticStep::Decide => write!(f, "decide"),
156 TacticStep::Simp(lemmas) if lemmas.is_empty() => write!(f, "simp"),
157 TacticStep::Simp(lemmas) => {
158 let lemmas: Vec<String> = lemmas.iter().map(|l| Shown(l).to_string()).collect();
159 write!(f, "simp [{}]", lemmas.join(", "))
160 }
161 }
162 }
163}
164
165#[derive(Debug)]
168pub struct ScriptError {
169 pub step: usize,
170 pub tactic: Option<TacticStep>,
171 pub error: TacticError,
172}
173
174impl fmt::Display for ScriptError {
175 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
176 match &self.tactic {
177 Some(tactic) => write!(f, "step {} (`{tactic}`): {}", self.step + 1, self.error),
178 None => write!(f, "after the last step: {}", self.error),
179 }
180 }
181}
182
183impl std::error::Error for ScriptError {}
184
185#[derive(Debug, Clone)]
188pub struct GoalState {
189 ctx: Context,
190 statement: Term,
191 mctx: MetaCtx,
192 decls: HashMap<String, Decl>,
193 open: Vec<String>,
194 root: String,
195}
196
197impl GoalState {
198 pub fn new(ctx: &Context, statement: &Term) -> Result<GoalState, TacticError> {
200 infer_sort(ctx, statement).map_err(TacticError::Kernel)?;
201 let mut st = GoalState {
202 ctx: ctx.clone(),
203 statement: statement.clone(),
204 mctx: MetaCtx::new(),
205 decls: HashMap::new(),
206 open: Vec::new(),
207 root: String::new(),
208 };
209 let root = st.declare(Vec::new(), statement.clone());
210 st.root = root.clone();
211 st.open.push(root);
212 Ok(st)
213 }
214
215 pub fn goals(&self) -> Vec<Goal> {
217 self.open.iter().map(|m| self.view(m)).collect()
218 }
219
220 pub fn read(&self, term: &Term) -> Term {
223 match self.open.first() {
224 Some(m) => localize(term, &self.decls[m].hyps),
225 None => term.clone(),
226 }
227 }
228
229 pub fn qed(&self) -> Result<Term, TacticError> {
232 if !self.open.is_empty() {
233 return Err(TacticError::GoalsRemain(self.goals()));
234 }
235 let proof = elaborate::instantiate(&Term::Var(self.root.clone()), &self.mctx);
236 if elaborate::mentions_meta(&proof) {
237 return Err(TacticError::Kernel(KernelError::CertificationError(
238 format!(
239 "the proof still holds an unsolved metavariable: {}",
240 Shown(&proof)
241 ),
242 )));
243 }
244 let ty = infer_type(&self.ctx, &proof).map_err(TacticError::Kernel)?;
245 if !is_subtype(&self.ctx, &ty, &self.statement) {
246 return Err(TacticError::Kernel(KernelError::TypeMismatch {
247 expected: Shown(&self.statement).to_string(),
248 found: Shown(&ty).to_string(),
249 }));
250 }
251 Ok(proof)
252 }
253
254 pub fn step(&mut self, step: &TacticStep) -> Result<(), TacticError> {
257 let saved = self.clone();
258 let result = self.step_unguarded(step);
259 if result.is_err() {
260 *self = saved;
261 }
262 result
263 }
264
265 fn step_unguarded(&mut self, step: &TacticStep) -> Result<(), TacticError> {
266 match step {
267 TacticStep::Intro(xs) => {
268 for x in xs {
269 self.intro(x)?;
270 }
271 Ok(())
272 }
273 TacticStep::Exact(t) => self.exact(t),
274 TacticStep::Apply(t) => self.apply(t),
275 TacticStep::Assumption => self.assumption(),
276 TacticStep::Exfalso => self.exfalso(),
277 TacticStep::Constructor => self.constructor(),
278 TacticStep::Left => self.left(),
279 TacticStep::Right => self.right(),
280 TacticStep::Exists(t) => self.exists(t),
281 TacticStep::Have(n, t) => self.have(n, t),
282 TacticStep::Rfl => self.rfl(),
283 TacticStep::Change(t) => self.change(t),
284 TacticStep::Rewrite(t, reverse) => self.rewrite(t, *reverse),
285 TacticStep::Induction(x, cases) => {
286 let refs: Vec<Vec<&str>> = cases
287 .iter()
288 .map(|c| c.iter().map(String::as_str).collect())
289 .collect();
290 let slices: Vec<&[&str]> = refs.iter().map(Vec::as_slice).collect();
291 self.induction(x, &slices)
292 }
293 TacticStep::Cases(x, cases) => {
294 let refs: Vec<Vec<&str>> = cases
295 .iter()
296 .map(|c| c.iter().map(String::as_str).collect())
297 .collect();
298 let slices: Vec<&[&str]> = refs.iter().map(Vec::as_slice).collect();
299 self.cases(x, &slices)
300 }
301 TacticStep::Refine(t) => self.refine(t),
302 TacticStep::Subst(h) => self.subst(h),
303 TacticStep::Injection(h, names) => {
304 let names: Vec<&str> = names.iter().map(String::as_str).collect();
305 self.injection(h, &names)
306 }
307 TacticStep::Calc(steps) => self.calc(steps),
308 TacticStep::Cc => self.cc(),
309 TacticStep::Decide => self.decide(),
310 TacticStep::Simp(lemmas) => self.simp(lemmas),
311 }
312 }
313
314 pub fn run(&mut self, script: &[TacticStep]) -> Result<(), ScriptError> {
317 for (i, step) in script.iter().enumerate() {
318 self.step(step).map_err(|error| ScriptError {
319 step: i,
320 tactic: Some(step.clone()),
321 error,
322 })?;
323 }
324 Ok(())
325 }
326
327 pub fn intro(&mut self, name: &str) -> Result<(), TacticError> {
329 let (m, goal) = self.focus()?;
330 if goal.hyps.iter().any(|(n, _)| n == name) {
331 return Err(refuse(
332 "intro",
333 &goal,
334 format!("`{name}` already names a hypothesis"),
335 ));
336 }
337 let ctx = self.scope(&goal.hyps);
338 let Term::Pi {
339 param,
340 param_type,
341 body_type,
342 } = demanded_type_head(&ctx, goal.target.clone(), TypeHead::Function)
343 else {
344 return Err(refuse(
345 "intro",
346 &goal,
347 "the goal is not a function type".to_string(),
348 ));
349 };
350 let body = if param == name {
351 *body_type
352 } else {
353 substitute(&body_type, ¶m, &Term::Var(name.to_string()))
354 };
355 let mut hyps = goal.hyps.clone();
356 hyps.push((name.to_string(), (*param_type).clone()));
357 let inner = self.declare(hyps, body);
358 let proof = Term::Lambda {
359 param: name.to_string(),
360 param_type,
361 body: Box::new(Term::Var(inner.clone())),
362 };
363 self.close(&m, proof, vec![inner]);
364 Ok(())
365 }
366
367 pub fn exact(&mut self, term: &Term) -> Result<(), TacticError> {
369 let (m, goal) = self.focus()?;
370 let ctx = self.scope(&goal.hyps);
371 let term = localize(term, &goal.hyps);
372 let mut mctx = self.mctx.clone();
373 let (value, ty) =
374 typed(&ctx, &mut mctx, &goal.hyps, &term, Some(&goal.target)).map_err(|e| {
375 refuse(
376 "exact",
377 &goal,
378 format!("{} does not type: {e}", Shown(&term)),
379 )
380 })?;
381 elaborate::unify_in(&ctx, &mut mctx, &goal.hyps, &ty, &goal.target);
382 let (value, ty, target) = (
383 elaborate::instantiate(&value, &mctx),
384 elaborate::instantiate(&ty, &mctx),
385 elaborate::instantiate(&goal.target, &mctx),
386 );
387 if elaborate::mentions_meta(&value) {
388 return Err(refuse(
389 "exact",
390 &goal,
391 format!("{} leaves a hole unsolved", Shown(&value)),
392 ));
393 }
394 if !is_subtype(&ctx, &ty, &target) {
395 return Err(refuse(
396 "exact",
397 &goal,
398 format!(
399 "the goal is {}, and {} has type {}",
400 Shown(&target),
401 Shown(&value),
402 Shown(&ty)
403 ),
404 ));
405 }
406 self.mctx = mctx;
407 self.close(&m, value, Vec::new());
408 Ok(())
409 }
410
411 pub fn apply(&mut self, term: &Term) -> Result<(), TacticError> {
414 let term = self.read(term);
415 self.apply_term("apply", term)
416 }
417
418 pub fn assumption(&mut self) -> Result<(), TacticError> {
420 let (m, goal) = self.focus()?;
421 let ctx = self.scope(&goal.hyps);
422 let Some((name, _)) = goal
423 .hyps
424 .iter()
425 .rev()
426 .find(|(_, ty)| is_subtype(&ctx, ty, &goal.target))
427 else {
428 return Err(refuse(
429 "assumption",
430 &goal,
431 "no hypothesis states the goal".to_string(),
432 ));
433 };
434 let proof = Term::Var(name.clone());
435 self.close(&m, proof, Vec::new());
436 Ok(())
437 }
438
439 pub fn exfalso(&mut self) -> Result<(), TacticError> {
441 let (m, goal) = self.focus()?;
442 let falsity = Term::Global("False".into());
443 let inner = self.declare(goal.hyps.clone(), falsity.clone());
444 let proof = Term::Match {
445 discriminant: Box::new(Term::Var(inner.clone())),
446 motive: Box::new(Term::Lambda {
447 param: "_".into(),
448 param_type: Box::new(falsity),
449 body: Box::new(goal.target.clone()),
450 }),
451 cases: Vec::new(),
452 };
453 self.close(&m, proof, vec![inner]);
454 Ok(())
455 }
456
457 pub fn constructor(&mut self) -> Result<(), TacticError> {
459 self.constructor_among("constructor", None)
460 }
461
462 pub fn left(&mut self) -> Result<(), TacticError> {
464 self.constructor_among("left", Some(0))
465 }
466
467 pub fn right(&mut self) -> Result<(), TacticError> {
469 self.constructor_among("right", Some(1))
470 }
471
472 pub fn exists(&mut self, witness: &Term) -> Result<(), TacticError> {
475 let (_, goal) = self.focus()?;
476 let ctx = self.scope(&goal.hyps);
477 let w = localize(witness, &goal.hyps);
478 let (ind, args) = self.goal_family("exists", &ctx, &goal)?;
479 let constructors = ctx.get_constructors(&ind);
480 let [(constructor, _)] = constructors.as_slice() else {
481 return Err(refuse(
482 "exists",
483 &goal,
484 format!("`{ind}` has {} constructors, not one", constructors.len()),
485 ));
486 };
487 let np = ctx.inductive_num_params(&ind).min(args.len());
488 let mut parts = args[..np].to_vec();
489 parts.push(w);
490 let term = apps(Term::Global(constructor.to_string()), &parts);
491 self.apply_term("exists", term)
492 }
493
494 pub fn have(&mut self, name: &str, fact: &Term) -> Result<(), TacticError> {
496 let (m, goal) = self.focus()?;
497 if goal.hyps.iter().any(|(n, _)| n == name) {
498 return Err(refuse(
499 "have",
500 &goal,
501 format!("`{name}` already names a hypothesis"),
502 ));
503 }
504 let ctx = self.scope(&goal.hyps);
505 let fact = localize(fact, &goal.hyps);
506 infer_sort(&ctx, &fact).map_err(|e| {
507 refuse(
508 "have",
509 &goal,
510 format!("{} is not a type: {e}", Shown(&fact)),
511 )
512 })?;
513 let first = self.declare(goal.hyps.clone(), fact.clone());
514 let mut hyps = goal.hyps.clone();
515 hyps.push((name.to_string(), fact.clone()));
516 let then = self.declare(hyps, goal.target.clone());
517 let proof = Term::App(
518 Box::new(Term::Lambda {
519 param: name.to_string(),
520 param_type: Box::new(fact),
521 body: Box::new(Term::Var(then.clone())),
522 }),
523 Box::new(Term::Var(first.clone())),
524 );
525 self.close(&m, proof, vec![first, then]);
526 Ok(())
527 }
528
529 fn constructor_among(
530 &mut self,
531 tactic: &'static str,
532 only: Option<usize>,
533 ) -> Result<(), TacticError> {
534 let (_, goal) = self.focus()?;
535 let ctx = self.scope(&goal.hyps);
536 let (ind, _) = self.goal_family(tactic, &ctx, &goal)?;
537 let constructors: Vec<String> = ctx
538 .get_constructors(&ind)
539 .iter()
540 .map(|(n, _)| n.to_string())
541 .collect();
542 let candidates = match only {
543 Some(i) if constructors.len() == 2 => vec![constructors[i].clone()],
544 Some(_) => {
545 return Err(refuse(
546 tactic,
547 &goal,
548 format!("`{ind}` has {} constructors, not two", constructors.len()),
549 ))
550 }
551 None => constructors,
552 };
553 let mut last = refuse(tactic, &goal, format!("`{ind}` has no constructor"));
554 for c in candidates {
555 match self.apply_term(tactic, Term::Global(c)) {
556 Ok(()) => return Ok(()),
557 Err(e) => last = e,
558 }
559 }
560 Err(last)
561 }
562
563 fn goal_family(
565 &self,
566 tactic: &'static str,
567 ctx: &Context,
568 goal: &Goal,
569 ) -> Result<(String, Vec<Term>), TacticError> {
570 let family = demanded_type_head(ctx, goal.target.clone(), TypeHead::Inductive);
571 let (head, args) = spine(&family);
572 match head {
573 Term::Global(n) | Term::Const { name: n, .. } if ctx.is_inductive(n) => {
574 Ok((n.clone(), args))
575 }
576 _ => Err(refuse(
577 tactic,
578 goal,
579 "the goal is not an instance of an inductive family".to_string(),
580 )),
581 }
582 }
583
584 fn apply_term(&mut self, tactic: &'static str, term: Term) -> Result<(), TacticError> {
585 let (m, goal) = self.focus()?;
586 let ctx = self.scope(&goal.hyps);
587 let mut mctx = self.mctx.clone();
588 let (f, f_ty) = typed(&ctx, &mut mctx, &goal.hyps, &term, None).map_err(|e| {
589 refuse(
590 tactic,
591 &goal,
592 format!("{} does not type: {e}", Shown(&term)),
593 )
594 })?;
595 let f_ty = elaborate::instantiate(&f_ty, &mctx);
596 let (f_arity, target_arity) = (pi_arity(&ctx, &f_ty), pi_arity(&ctx, &goal.target));
597 let Some(n) = f_arity.checked_sub(target_arity) else {
598 return Err(refuse(
599 tactic,
600 &goal,
601 format!(
602 "{} : {} has fewer premises than the goal",
603 Shown(&f),
604 Shown(&f_ty)
605 ),
606 ));
607 };
608 let mut ty = f_ty;
609 let mut args = Vec::with_capacity(n);
610 let mut premises = Vec::with_capacity(n);
611 for _ in 0..n {
612 let Term::Pi {
613 param,
614 param_type,
615 body_type,
616 } = demanded_type_head(&ctx, ty, TypeHead::Function)
617 else {
618 unreachable!("`pi_arity` counted these premises");
619 };
620 let a = mctx.fresh();
621 let Term::Var(name) = &a else {
622 unreachable!("a metavariable is a `Var`")
623 };
624 premises.push((name.clone(), *param_type));
625 ty = substitute(&body_type, ¶m, &a);
626 args.push(a);
627 }
628 if !elaborate::unify_in(&ctx, &mut mctx, &goal.hyps, &ty, &goal.target) {
629 let ty = elaborate::instantiate(&ty, &mctx);
630 return Err(refuse(
631 tactic,
632 &goal,
633 format!(
634 "{}'s conclusion {} does not unify with the goal",
635 Shown(&f),
636 Shown(&ty)
637 ),
638 ));
639 }
640 self.mctx = mctx;
641 let mut subgoals = Vec::with_capacity(premises.len());
642 for (name, target) in premises {
643 self.decls.insert(
644 name.clone(),
645 Decl {
646 hyps: goal.hyps.clone(),
647 target,
648 },
649 );
650 subgoals.push(name);
651 }
652 let proof = args
653 .into_iter()
654 .fold(f, |acc, a| Term::App(Box::new(acc), Box::new(a)));
655 self.close(&m, proof, subgoals);
656 Ok(())
657 }
658
659 pub fn rfl(&mut self) -> Result<(), TacticError> {
661 let (m, goal) = self.focus()?;
662 let ctx = self.scope(&goal.hyps);
663 let Some(eq) = equation(&ctx, &goal.target) else {
664 return Err(refuse(
665 "rfl",
666 &goal,
667 "the goal is not an equation".to_string(),
668 ));
669 };
670 let proof = apps(eq.refl(), &[eq.carrier.clone(), eq.lhs.clone()]);
671 match infer_type(&ctx, &proof) {
672 Ok(ty) if is_subtype(&ctx, &ty, &goal.target) => {}
673 _ => {
674 return Err(refuse(
675 "rfl",
676 &goal,
677 format!("{} and {} do not convert", Shown(&eq.lhs), Shown(&eq.rhs)),
678 ))
679 }
680 }
681 self.close(&m, proof, Vec::new());
682 Ok(())
683 }
684
685 pub fn change(&mut self, target: &Term) -> Result<(), TacticError> {
687 let (m, goal) = self.focus()?;
688 let ctx = self.scope(&goal.hyps);
689 let new = localize(target, &goal.hyps);
690 infer_sort(&ctx, &new).map_err(|e| {
691 refuse(
692 "change",
693 &goal,
694 format!("{} is not a type: {e}", Shown(&new)),
695 )
696 })?;
697 if !def_eq(&ctx, &new, &goal.target) {
698 return Err(refuse(
699 "change",
700 &goal,
701 format!("{} does not convert with the goal", Shown(&new)),
702 ));
703 }
704 let inner = self.declare(goal.hyps.clone(), new);
705 self.close(&m, Term::Var(inner.clone()), vec![inner]);
706 Ok(())
707 }
708
709 pub fn rewrite(&mut self, eq_proof: &Term, reverse: bool) -> Result<(), TacticError> {
713 let (m, goal) = self.focus()?;
714 let ctx = self.scope(&goal.hyps);
715 let h = localize(eq_proof, &goal.hyps);
716 let h_ty = infer_type(&ctx, &h).map_err(|e| {
717 refuse(
718 "rewrite",
719 &goal,
720 format!("{} does not type: {e}", Shown(&h)),
721 )
722 })?;
723 let Some(eq) = equation(&ctx, &h_ty) else {
724 return Err(refuse(
725 "rewrite",
726 &goal,
727 format!("{} : {} is not an equation", Shown(&h), Shown(&h_ty)),
728 ));
729 };
730 let (from, to) = if reverse {
731 (&eq.rhs, &eq.lhs)
732 } else {
733 (&eq.lhs, &eq.rhs)
734 };
735 let x = fresh_name("x", &ctx, &goal.hyps, &[&goal.target, from, to]);
736 let (body, found) = abstract_occurrences(&goal.target, from, &x);
737 if !found {
738 return Err(refuse(
739 "rewrite",
740 &goal,
741 format!("{} does not occur in the goal", Shown(from)),
742 ));
743 }
744 let new_target = substitute(&body, &x, to);
745 let motive = Term::Lambda {
746 param: x,
747 param_type: Box::new(eq.carrier.clone()),
748 body: Box::new(body),
749 };
750 let back = if reverse {
752 h
753 } else {
754 apps(
755 Term::Global("Eq_sym".into()),
756 &[eq.carrier.clone(), eq.lhs.clone(), eq.rhs.clone(), h],
757 )
758 };
759 let inner = self.declare(goal.hyps.clone(), new_target.clone());
760 let proof = apps(
761 Term::Global("Eq_rec".into()),
762 &[
763 eq.carrier.clone(),
764 to.clone(),
765 motive,
766 Term::Var(inner.clone()),
767 from.clone(),
768 back,
769 ],
770 );
771 let probe = ctx.extend(&inner, new_target);
773 match infer_type(&probe, &proof) {
774 Ok(ty) if is_subtype(&probe, &ty, &goal.target) => {}
775 Ok(ty) => {
776 self.decls.remove(&inner);
777 return Err(refuse(
778 "rewrite",
779 &goal,
780 format!("the transport proves {}, not the goal", Shown(&ty)),
781 ));
782 }
783 Err(e) => {
784 self.decls.remove(&inner);
785 return Err(refuse(
786 "rewrite",
787 &goal,
788 format!("the transport does not type: {e}"),
789 ));
790 }
791 }
792 self.close(&m, proof, vec![inner]);
793 Ok(())
794 }
795
796 pub fn induction(&mut self, var: &str, names: &[&[&str]]) -> Result<(), TacticError> {
802 let (m, goal) = self.focus()?;
803 let ctx = self.scope(&goal.hyps);
804 let Some((_, var_ty)) = goal.hyps.iter().find(|(n, _)| n == var).cloned() else {
805 return Err(refuse(
806 "induction",
807 &goal,
808 format!("`{var}` is not a hypothesis"),
809 ));
810 };
811 let family = demanded_type_head(&ctx, var_ty, TypeHead::Inductive);
812 let (head, args) = spine(&family);
813 let ind = match head {
814 Term::Global(n) | Term::Const { name: n, .. } if ctx.is_inductive(n) => n.clone(),
815 _ => {
816 return Err(refuse(
817 "induction",
818 &goal,
819 format!("`{var}` : {} is not of an inductive family", Shown(&family)),
820 ))
821 }
822 };
823 let np = ctx.inductive_num_params(&ind);
824 let (params, indices) = args.split_at(np.min(args.len()));
825 let mut index_vars: Vec<String> = Vec::new();
826 for ix in indices {
827 match ix {
828 Term::Var(n)
829 if goal.hyps.iter().any(|(h, _)| h == n)
830 && !index_vars.contains(n)
831 && !params
832 .iter()
833 .any(|p| crate::type_checker::occurs_free(p, n)) =>
834 {
835 index_vars.push(n.clone())
836 }
837 other => {
838 return Err(refuse(
839 "induction",
840 &goal,
841 format!(
842 "the index {} of `{var}`'s type is not a hypothesis of its own",
843 Shown(other)
844 ),
845 ))
846 }
847 }
848 }
849 let rec_name = format!("{ind}_rec");
850 if ctx.get_global(&rec_name).is_none() && ctx.get_universe_poly(&rec_name).is_none() {
851 return Err(refuse(
852 "induction",
853 &goal,
854 format!("`{ind}` has no recursor `{rec_name}`"),
855 ));
856 }
857 let mut abstracted: BTreeSet<String> = index_vars.iter().cloned().collect();
859 abstracted.insert(var.to_string());
860 let mut dependents: Vec<(String, Term)> = Vec::new();
861 for (n, ty) in &goal.hyps {
862 if abstracted.contains(n) {
863 continue;
864 }
865 if abstracted
866 .iter()
867 .any(|a| crate::type_checker::occurs_free(ty, a))
868 {
869 abstracted.insert(n.clone());
870 dependents.push((n.clone(), ty.clone()));
871 }
872 }
873 let kept: Vec<(String, Term)> = goal
874 .hyps
875 .iter()
876 .filter(|(n, _)| !abstracted.contains(n))
877 .cloned()
878 .collect();
879 let generalized = dependents
880 .iter()
881 .rev()
882 .fold(goal.target.clone(), |t, (n, ty)| Term::Pi {
883 param: n.clone(),
884 param_type: Box::new(ty.clone()),
885 body_type: Box::new(t),
886 });
887 let rec = apps(Term::Global(rec_name), params);
889 let rec_ty = infer_type(&ctx, &rec).map_err(|e| {
890 refuse(
891 "induction",
892 &goal,
893 format!("the recursor does not type at these parameters: {e}"),
894 )
895 })?;
896 let Term::Pi {
897 param: motive_param,
898 param_type: motive_ty,
899 body_type: after_motive,
900 } = demanded_type_head(&ctx, rec_ty, TypeHead::Function)
901 else {
902 return Err(refuse(
903 "induction",
904 &goal,
905 "the recursor takes no motive".to_string(),
906 ));
907 };
908 let telescope = pi_telescope(&ctx, &motive_ty, index_vars.len() + 1);
909 if telescope.len() != index_vars.len() + 1 {
910 return Err(refuse(
911 "induction",
912 &goal,
913 format!(
914 "the motive {} does not range over the family",
915 Shown(&motive_ty)
916 ),
917 ));
918 }
919 let mut avoid_terms: Vec<&Term> = vec![&goal.target];
920 avoid_terms.extend(goal.hyps.iter().map(|(_, t)| t));
921 let mut motive_names: Vec<String> = Vec::new();
922 let mut motive_types: Vec<Term> = Vec::new();
923 for (i, (bname, bty)) in telescope.iter().enumerate() {
924 let mut bty = bty.clone();
925 for (j, prev) in motive_names.iter().enumerate() {
926 bty = substitute(&bty, &telescope[j].0, &Term::Var(prev.clone()));
927 }
928 let base = if i == index_vars.len() {
929 "y"
930 } else {
931 bname.as_str()
932 };
933 let fresh = fresh_name_avoiding(base, &ctx, &goal.hyps, &avoid_terms, &motive_names);
934 motive_names.push(fresh);
935 motive_types.push(bty);
936 }
937 let mut body = generalized;
938 for (ix, fresh) in index_vars.iter().zip(&motive_names) {
939 body = substitute(&body, ix, &Term::Var(fresh.clone()));
940 }
941 body = substitute(
942 &body,
943 var,
944 &Term::Var(motive_names[index_vars.len()].clone()),
945 );
946 let motive = motive_names
947 .iter()
948 .zip(&motive_types)
949 .rev()
950 .fold(body, |b, (n, t)| Term::Lambda {
951 param: n.clone(),
952 param_type: Box::new(t.clone()),
953 body: Box::new(b),
954 });
955 let mut ty = substitute(&after_motive, &motive_param, &motive);
956 let constructors = ctx.get_constructors(&ind).len();
959 let mut minors = Vec::with_capacity(constructors);
960 let mut cases = Vec::with_capacity(constructors);
961 let mut new_decls: Vec<(String, Decl)> = Vec::new();
962 let mut taken: Vec<String> = kept.iter().map(|(n, _)| n.clone()).collect();
963 for i in 0..constructors {
964 let Term::Pi {
965 param,
966 param_type,
967 body_type,
968 } = demanded_type_head(&ctx, ty, TypeHead::Function)
969 else {
970 return Err(refuse(
971 "induction",
972 &goal,
973 "the recursor has fewer minor premises than constructors".to_string(),
974 ));
975 };
976 let minor_ty = beta_normal(¶m_type);
977 let given: &[&str] = names.get(i).copied().unwrap_or(&[]);
978 let mut case_hyps = kept.clone();
979 let mut binders: Vec<(String, Term)> = Vec::new();
980 let mut t = minor_ty;
981 let mut j = 0;
982 let case_ctx_base = self.scope(&kept);
983 loop {
984 let case_ctx = binders
985 .iter()
986 .fold(case_ctx_base.clone(), |c, (n, ty)| c.extend(n, ty.clone()));
987 let Term::Pi {
988 param: b,
989 param_type: bt,
990 body_type: rest,
991 } = &t
992 else {
993 break;
994 };
995 let _ = &case_ctx;
996 let name = match given.get(j) {
997 Some(n) => {
998 if taken.iter().any(|t| t == n) {
999 return Err(refuse(
1000 "induction",
1001 &goal,
1002 format!("`{n}` already names a hypothesis"),
1003 ));
1004 }
1005 n.to_string()
1006 }
1007 None => fresh_name_avoiding(b, &ctx, &case_hyps, &[&goal.target], &taken),
1008 };
1009 taken.push(name.clone());
1010 let rest = if *b == name {
1011 (**rest).clone()
1012 } else {
1013 substitute(rest, b, &Term::Var(name.clone()))
1014 };
1015 binders.push((name.clone(), (**bt).clone()));
1016 case_hyps.push((name, (**bt).clone()));
1017 t = rest;
1018 j += 1;
1019 }
1020 if given.len() > binders.len() {
1021 return Err(refuse(
1022 "induction",
1023 &goal,
1024 format!(
1025 "case {} names {} hypotheses and has {}",
1026 i + 1,
1027 given.len(),
1028 binders.len()
1029 ),
1030 ));
1031 }
1032 for (dn, _) in &dependents {
1034 let Term::Pi {
1035 param: b,
1036 param_type: bt,
1037 body_type: rest,
1038 } = &t
1039 else {
1040 unreachable!("the motive's body is a Π over the dependents");
1041 };
1042 let rest = if b == dn {
1043 (**rest).clone()
1044 } else {
1045 substitute(rest, b, &Term::Var(dn.clone()))
1046 };
1047 binders.push((dn.clone(), (**bt).clone()));
1048 case_hyps.push((dn.clone(), (**bt).clone()));
1049 t = rest;
1050 }
1051 let case = self.fresh_meta_name();
1052 new_decls.push((
1053 case.clone(),
1054 Decl {
1055 hyps: case_hyps,
1056 target: t,
1057 },
1058 ));
1059 let minor = binders
1060 .iter()
1061 .rev()
1062 .fold(Term::Var(case.clone()), |b, (n, ty)| Term::Lambda {
1063 param: n.clone(),
1064 param_type: Box::new(ty.clone()),
1065 body: Box::new(b),
1066 });
1067 ty = substitute(&body_type, ¶m, &minor);
1068 minors.push(minor);
1069 cases.push(case);
1070 }
1071 let mut proof = apps(apps(rec, &[motive]), &minors);
1072 for ix in &index_vars {
1073 proof = Term::App(Box::new(proof), Box::new(Term::Var(ix.clone())));
1074 }
1075 proof = Term::App(Box::new(proof), Box::new(Term::Var(var.to_string())));
1076 for (dn, _) in &dependents {
1077 proof = Term::App(Box::new(proof), Box::new(Term::Var(dn.clone())));
1078 }
1079 for (name, decl) in new_decls {
1080 self.decls.insert(name, decl);
1081 }
1082 self.close(&m, proof, cases);
1083 Ok(())
1084 }
1085
1086 pub fn cases(&mut self, var: &str, names: &[&[&str]]) -> Result<(), TacticError> {
1094 let (m, goal) = self.focus()?;
1095 let ctx = self.scope(&goal.hyps);
1096 let Some((_, var_ty)) = goal.hyps.iter().find(|(n, _)| n == var).cloned() else {
1097 return Err(refuse(
1098 "cases",
1099 &goal,
1100 format!("`{var}` is not a hypothesis"),
1101 ));
1102 };
1103 let family = demanded_type_head(&ctx, var_ty, TypeHead::Inductive);
1104 let (head, args) = spine(&family);
1105 let ind = match head {
1106 Term::Global(n) | Term::Const { name: n, .. } if ctx.is_inductive(n) => n.clone(),
1107 _ => {
1108 return Err(refuse(
1109 "cases",
1110 &goal,
1111 format!("`{var}` : {} is not of an inductive family", Shown(&family)),
1112 ))
1113 }
1114 };
1115 let np = ctx.inductive_num_params(&ind).min(args.len());
1116 let (params, indices) = args.split_at(np);
1117 let allowed = crate::type_checker::constructors_the_indices_allow(&ctx, &ind, np, &args);
1118 let index_vars: Option<Vec<String>> = {
1119 let mut vars: Vec<String> = Vec::new();
1120 let mut ok = true;
1121 for ix in indices {
1122 match ix {
1123 Term::Var(n)
1124 if goal.hyps.iter().any(|(h, _)| h == n)
1125 && !vars.contains(n)
1126 && !params
1127 .iter()
1128 .any(|p| crate::type_checker::occurs_free(p, n)) =>
1129 {
1130 vars.push(n.clone())
1131 }
1132 _ => ok = false,
1133 }
1134 }
1135 ok.then_some(vars)
1136 };
1137 if index_vars.is_none() && !allowed.is_empty() {
1138 let head = head.clone();
1139 return self.cases_by_equations(
1140 var, names, &m, &goal, &ctx, &head, params, indices, &allowed,
1141 );
1142 }
1143 let index_vars = index_vars.unwrap_or_default();
1144 let mut abstracted: BTreeSet<String> = index_vars.iter().cloned().collect();
1145 abstracted.insert(var.to_string());
1146 let mut dependents: Vec<(String, Term)> = Vec::new();
1147 for (n, ty) in &goal.hyps {
1148 if abstracted.contains(n) {
1149 continue;
1150 }
1151 if abstracted
1152 .iter()
1153 .any(|a| crate::type_checker::occurs_free(ty, a))
1154 {
1155 abstracted.insert(n.clone());
1156 dependents.push((n.clone(), ty.clone()));
1157 }
1158 }
1159 let kept: Vec<(String, Term)> = goal
1160 .hyps
1161 .iter()
1162 .filter(|(n, _)| !abstracted.contains(n))
1163 .cloned()
1164 .collect();
1165 let generalized = dependents
1166 .iter()
1167 .rev()
1168 .fold(goal.target.clone(), |t, (n, ty)| Term::Pi {
1169 param: n.clone(),
1170 param_type: Box::new(ty.clone()),
1171 body_type: Box::new(t),
1172 });
1173 let arity = infer_type(&ctx, &apps(head.clone(), params)).map_err(|e| {
1175 refuse(
1176 "cases",
1177 &goal,
1178 format!("the family does not type at these parameters: {e}"),
1179 )
1180 })?;
1181 let index_telescope = pi_telescope(&ctx, &arity, usize::MAX);
1182 let mut avoid: Vec<&Term> = vec![&goal.target];
1183 avoid.extend(goal.hyps.iter().map(|(_, t)| t));
1184 let mut motive_names: Vec<String> = Vec::new();
1185 let mut motive_types: Vec<Term> = Vec::new();
1186 for (bname, bty) in &index_telescope {
1187 let mut bty = bty.clone();
1188 for (j, prev) in motive_names.iter().enumerate() {
1189 bty = substitute(&bty, &index_telescope[j].0, &Term::Var(prev.clone()));
1190 }
1191 let fresh = fresh_name_avoiding(bname, &ctx, &goal.hyps, &avoid, &motive_names);
1192 motive_names.push(fresh);
1193 motive_types.push(bty);
1194 }
1195 let scrutinee_name = fresh_name_avoiding("y", &ctx, &goal.hyps, &avoid, &motive_names);
1196 let scrutinee_ty = apps(
1197 apps(head.clone(), params),
1198 &motive_names
1199 .iter()
1200 .map(|n| Term::Var(n.clone()))
1201 .collect::<Vec<_>>(),
1202 );
1203 let mut body = generalized;
1204 for (ix, fresh) in index_vars.iter().zip(&motive_names) {
1205 body = substitute(&body, ix, &Term::Var(fresh.clone()));
1206 }
1207 body = substitute(&body, var, &Term::Var(scrutinee_name.clone()));
1208 let motive = motive_names.iter().zip(&motive_types).rev().fold(
1209 Term::Lambda {
1210 param: scrutinee_name,
1211 param_type: Box::new(scrutinee_ty),
1212 body: Box::new(body),
1213 },
1214 |b, (n, t)| Term::Lambda {
1215 param: n.clone(),
1216 param_type: Box::new(t.clone()),
1217 body: Box::new(b),
1218 },
1219 );
1220 let constructors = ctx.get_constructors(&ind);
1222 let mut case_terms = Vec::with_capacity(allowed.len());
1223 let mut case_goals = Vec::with_capacity(allowed.len());
1224 let mut new_decls: Vec<(String, Decl)> = Vec::new();
1225 let mut taken: Vec<String> = kept.iter().map(|(n, _)| n.clone()).collect();
1226 for (slot, &ci) in allowed.iter().enumerate() {
1227 let cname = constructors[ci].0.to_string();
1228 let ctor = apps(
1229 match head {
1230 Term::Const { levels, .. } if ctx.poly_declaration(&cname).is_some() => {
1231 Term::Const {
1232 name: cname.clone(),
1233 levels: levels.clone(),
1234 }
1235 }
1236 _ => Term::Global(cname.clone()),
1237 },
1238 params,
1239 );
1240 let ctor_ty = infer_type(&ctx, &ctor).map_err(|e| {
1241 refuse(
1242 "cases",
1243 &goal,
1244 format!("`{cname}` does not type at these parameters: {e}"),
1245 )
1246 })?;
1247 let given: &[&str] = names.get(slot).copied().unwrap_or(&[]);
1248 let mut case_hyps = kept.clone();
1249 let mut fields: Vec<(String, Term)> = Vec::new();
1250 let mut t = ctor_ty;
1251 let mut j = 0;
1252 loop {
1253 let scope = fields
1254 .iter()
1255 .fold(self.scope(&kept), |c, (n, ty)| c.extend(n, ty.clone()));
1256 let Term::Pi {
1257 param: b,
1258 param_type: bt,
1259 body_type: rest,
1260 } = demanded_type_head(&scope, t.clone(), TypeHead::Function)
1261 else {
1262 break;
1263 };
1264 let name = match given.get(j) {
1265 Some(n) => {
1266 if taken.iter().any(|t| t == n) {
1267 return Err(refuse(
1268 "cases",
1269 &goal,
1270 format!("`{n}` already names a hypothesis"),
1271 ));
1272 }
1273 n.to_string()
1274 }
1275 None => fresh_name_avoiding(&b, &ctx, &case_hyps, &[&goal.target], &taken),
1276 };
1277 taken.push(name.clone());
1278 t = if b == name {
1279 *rest
1280 } else {
1281 substitute(&rest, &b, &Term::Var(name.clone()))
1282 };
1283 fields.push((name.clone(), (*bt).clone()));
1284 case_hyps.push((name, *bt));
1285 j += 1;
1286 }
1287 if given.len() > fields.len() {
1288 return Err(refuse(
1289 "cases",
1290 &goal,
1291 format!(
1292 "case `{cname}` names {} hypotheses and has {}",
1293 given.len(),
1294 fields.len()
1295 ),
1296 ));
1297 }
1298 let (_, result) = spine(&t);
1300 let result_indices = &result[np.min(result.len())..];
1301 let value = apps(
1302 ctor.clone(),
1303 &fields
1304 .iter()
1305 .map(|(n, _)| Term::Var(n.clone()))
1306 .collect::<Vec<_>>(),
1307 );
1308 let mut target = beta_normal(&apps(apps(motive.clone(), result_indices), &[value]));
1309 let mut binders = fields.clone();
1310 for (dn, _) in &dependents {
1311 let Term::Pi {
1312 param: b,
1313 param_type: bt,
1314 body_type: rest,
1315 } = &target
1316 else {
1317 unreachable!("the motive's body is a Π over the dependents");
1318 };
1319 let rest = if b == dn {
1320 (**rest).clone()
1321 } else {
1322 substitute(rest, b, &Term::Var(dn.clone()))
1323 };
1324 binders.push((dn.clone(), (**bt).clone()));
1325 case_hyps.push((dn.clone(), (**bt).clone()));
1326 target = rest;
1327 }
1328 let case = self.fresh_meta_name();
1329 new_decls.push((
1330 case.clone(),
1331 Decl {
1332 hyps: case_hyps,
1333 target,
1334 },
1335 ));
1336 case_terms.push(
1337 binders
1338 .iter()
1339 .rev()
1340 .fold(Term::Var(case.clone()), |b, (n, ty)| Term::Lambda {
1341 param: n.clone(),
1342 param_type: Box::new(ty.clone()),
1343 body: Box::new(b),
1344 }),
1345 );
1346 case_goals.push(case);
1347 }
1348 let mut proof = Term::Match {
1349 discriminant: Box::new(Term::Var(var.to_string())),
1350 motive: Box::new(motive),
1351 cases: case_terms,
1352 };
1353 for (dn, _) in &dependents {
1354 proof = Term::App(Box::new(proof), Box::new(Term::Var(dn.clone())));
1355 }
1356 for (name, decl) in new_decls {
1357 self.decls.insert(name, decl);
1358 }
1359 self.close(&m, proof, case_goals);
1360 Ok(())
1361 }
1362
1363 #[allow(clippy::too_many_arguments)]
1372 fn cases_by_equations(
1373 &mut self,
1374 var: &str,
1375 names: &[&[&str]],
1376 m: &str,
1377 goal: &Goal,
1378 ctx: &Context,
1379 head: &Term,
1380 params: &[Term],
1381 indices: &[Term],
1382 allowed: &[usize],
1383 ) -> Result<(), TacticError> {
1384 let family_at = |ix: &[Term]| apps(apps(head.clone(), params), ix);
1385 let arity = infer_type(ctx, &apps(head.clone(), params)).map_err(|e| {
1386 refuse(
1387 "cases",
1388 goal,
1389 format!("the family does not type at these parameters: {e}"),
1390 )
1391 })?;
1392 let telescope = pi_telescope(ctx, &arity, indices.len());
1393 for (k, (_, ty)) in telescope.iter().enumerate() {
1394 if let Some((earlier, _)) = telescope[..k]
1395 .iter()
1396 .find(|(n, _)| crate::type_checker::occurs_free(ty, n))
1397 {
1398 return Err(refuse(
1399 "cases",
1400 goal,
1401 format!(
1402 "the type {} of index {} depends on the index `{earlier}`: its equation would be heterogeneous",
1403 Shown(ty),
1404 k + 1
1405 ),
1406 ));
1407 }
1408 }
1409 let index_types: Vec<Term> = telescope.into_iter().map(|(_, t)| t).collect();
1410 let n = indices.len();
1411 let mut avoid: Vec<&Term> = vec![&goal.target];
1412 avoid.extend(goal.hyps.iter().map(|(_, t)| t));
1413 avoid.extend(indices.iter());
1414 let mut taken: Vec<String> = goal.hyps.iter().map(|(h, _)| h.clone()).collect();
1415 let fresh = |base: &str, taken: &mut Vec<String>| {
1416 let name = fresh_name_avoiding(base, ctx, &goal.hyps, &avoid, taken);
1417 taken.push(name.clone());
1418 name
1419 };
1420 let js: Vec<String> = (0..n).map(|_| fresh("j", &mut taken)).collect();
1421 let es: Vec<String> = (0..n).map(|_| fresh("e", &mut taken)).collect();
1422 let zs: Vec<String> = (0..n).map(|_| fresh("z", &mut taken)).collect();
1423 let ws: Vec<String> = (0..n).map(|_| fresh("w", &mut taken)).collect();
1424 let y = fresh("y", &mut taken);
1425 let mut abstracted: BTreeSet<String> = BTreeSet::from([var.to_string()]);
1426 let mut dependents: Vec<(String, Term)> = Vec::new();
1427 for (h, ty) in &goal.hyps {
1428 if !abstracted.contains(h)
1429 && abstracted
1430 .iter()
1431 .any(|a| crate::type_checker::occurs_free(ty, a))
1432 {
1433 abstracted.insert(h.clone());
1434 dependents.push((h.clone(), ty.clone()));
1435 }
1436 }
1437 let kept: Vec<(String, Term)> = goal
1438 .hyps
1439 .iter()
1440 .filter(|(h, _)| !abstracted.contains(h))
1441 .cloned()
1442 .collect();
1443 let cast = |value: Term| {
1445 (0..n).fold(value, |value, k| {
1446 let at = |z: Term| {
1447 let mut ix: Vec<Term> = indices[..k].to_vec();
1448 ix.push(z);
1449 ix.extend(js[k + 1..].iter().map(|j| Term::Var(j.clone())));
1450 family_at(&ix)
1451 };
1452 let landed = at(indices[k].clone());
1453 let motive = lam(
1454 &zs[k],
1455 index_types[k].clone(),
1456 lam(
1457 "_",
1458 eq_term(&index_types[k], &indices[k], &Term::Var(zs[k].clone())),
1459 pi("_", at(Term::Var(zs[k].clone())), landed.clone()),
1460 ),
1461 );
1462 let transport = Term::Match {
1463 discriminant: Box::new(Term::Var(es[k].clone())),
1464 motive: Box::new(motive),
1465 cases: vec![lam(&ws[k], landed, Term::Var(ws[k].clone()))],
1466 };
1467 Term::App(Box::new(transport), Box::new(value))
1468 })
1469 };
1470 let mentioned =
1471 crate::type_checker::occurs_free(&goal.target, var) || !dependents.is_empty();
1472 let moved = if mentioned {
1473 cast(Term::Var(y.clone()))
1474 } else {
1475 Term::Var(y.clone())
1476 };
1477 let mut body = dependents
1478 .iter()
1479 .rev()
1480 .fold(goal.target.clone(), |t, (h, ty)| pi(h, ty.clone(), t));
1481 body = substitute(&body, var, &moved);
1482 for k in (0..n).rev() {
1483 body = pi(
1484 &es[k],
1485 eq_term(&index_types[k], &indices[k], &Term::Var(js[k].clone())),
1486 body,
1487 );
1488 }
1489 let js_vars: Vec<Term> = js.iter().map(|j| Term::Var(j.clone())).collect();
1490 let mut motive = lam(&y, family_at(&js_vars), body);
1491 for k in (0..n).rev() {
1492 motive = lam(&js[k], index_types[k].clone(), motive);
1493 }
1494 let constructors = ctx.get_constructors(&self.family_name(head));
1498 let mut case_terms = Vec::with_capacity(allowed.len());
1499 let mut case_goals = Vec::with_capacity(allowed.len());
1500 taken.extend(dependents.iter().map(|(h, _)| h.clone()));
1501 let hypotheses: Vec<String> = kept
1502 .iter()
1503 .map(|(h, _)| h.clone())
1504 .chain(es.iter().cloned())
1505 .chain(dependents.iter().map(|(h, _)| h.clone()))
1506 .collect();
1507 for (slot, &ci) in allowed.iter().enumerate() {
1508 let cname = constructors[ci].0.to_string();
1509 let ctor = apps(constructor_at(ctx, head, &cname), params);
1510 let ctor_ty = infer_type(ctx, &ctor).map_err(|e| {
1511 refuse(
1512 "cases",
1513 goal,
1514 format!("`{cname}` does not type at these parameters: {e}"),
1515 )
1516 })?;
1517 let given: &[&str] = names.get(slot).copied().unwrap_or(&[]);
1518 let mut fields: Vec<(String, Term)> = Vec::new();
1519 let mut t = ctor_ty;
1520 loop {
1521 let scope = fields
1522 .iter()
1523 .fold(self.scope(&kept), |c, (h, ty)| c.extend(h, ty.clone()));
1524 let Term::Pi {
1525 param: b,
1526 param_type: bt,
1527 body_type: rest,
1528 } = demanded_type_head(&scope, t.clone(), TypeHead::Function)
1529 else {
1530 break;
1531 };
1532 let name = match given.get(fields.len()) {
1533 Some(h)
1534 if hypotheses
1535 .iter()
1536 .chain(fields.iter().map(|(f, _)| f))
1537 .any(|t| t == h) =>
1538 {
1539 return Err(refuse(
1540 "cases",
1541 goal,
1542 format!("`{h}` already names a hypothesis"),
1543 ))
1544 }
1545 Some(h) => h.to_string(),
1546 None => fresh_name_avoiding(&b, ctx, &kept, &[&goal.target], &taken),
1547 };
1548 taken.push(name.clone());
1549 t = if b == name {
1550 *rest
1551 } else {
1552 substitute(&rest, &b, &Term::Var(name.clone()))
1553 };
1554 fields.push((name, *bt));
1555 }
1556 if given.len() > fields.len() {
1557 return Err(refuse(
1558 "cases",
1559 goal,
1560 format!(
1561 "case `{cname}` names {} hypotheses and has {}",
1562 given.len(),
1563 fields.len()
1564 ),
1565 ));
1566 }
1567 let (_, result) = spine(&t);
1568 let result_indices = &result[params.len().min(result.len())..];
1569 let value = apps(
1570 ctor.clone(),
1571 &fields
1572 .iter()
1573 .map(|(h, _)| Term::Var(h.clone()))
1574 .collect::<Vec<_>>(),
1575 );
1576 let target = beta_normal(&apps(apps(motive.clone(), result_indices), &[value]));
1577 let mut case_hyps = kept.clone();
1578 case_hyps.extend(fields.iter().cloned());
1579 let case = self.declare(case_hyps, target);
1580 case_terms.push(
1581 fields
1582 .iter()
1583 .rev()
1584 .fold(Term::Var(case.clone()), |b, (h, ty)| lam(h, ty.clone(), b)),
1585 );
1586 case_goals.push(case);
1587 }
1588 let mut proof = Term::Match {
1589 discriminant: Box::new(Term::Var(var.to_string())),
1590 motive: Box::new(motive),
1591 cases: case_terms,
1592 };
1593 for k in 0..n {
1594 proof = Term::App(
1595 Box::new(proof),
1596 Box::new(apps(
1597 Term::Global("refl".into()),
1598 &[index_types[k].clone(), indices[k].clone()],
1599 )),
1600 );
1601 }
1602 for (h, _) in &dependents {
1603 proof = Term::App(Box::new(proof), Box::new(Term::Var(h.clone())));
1604 }
1605 self.close(m, proof, case_goals.clone());
1606 for case in case_goals {
1607 let foreign: BTreeSet<String> =
1608 self.open.iter().filter(|g| **g != case).cloned().collect();
1609 self.on_goal(&case, |st| {
1610 for e in &es {
1611 st.intro(e)?;
1612 }
1613 for (h, _) in &dependents {
1614 st.intro(h)?;
1615 }
1616 st.solve_index_equations(es.clone(), &foreign)
1617 })?;
1618 }
1619 Ok(())
1620 }
1621
1622 fn family_name(&self, head: &Term) -> String {
1624 match head {
1625 Term::Global(n) | Term::Const { name: n, .. } => n.clone(),
1626 other => unreachable!("a family head is a constant, not {other:?}"),
1627 }
1628 }
1629
1630 fn solve_index_equations(
1633 &mut self,
1634 equations: Vec<String>,
1635 foreign: &BTreeSet<String>,
1636 ) -> Result<(), TacticError> {
1637 let mut queue: std::collections::VecDeque<String> = equations.into();
1638 let mut injected: BTreeSet<String> = BTreeSet::new();
1639 while let Some(e) = queue.pop_front() {
1640 match self.open.first() {
1641 Some(g) if !foreign.contains(g) => {}
1642 _ => return Ok(()),
1643 }
1644 let (_, goal) = self.focus()?;
1645 let Some((_, ty)) = goal.hyps.iter().find(|(h, _)| *h == e).cloned() else {
1646 continue;
1647 };
1648 let ctx = self.scope(&goal.hyps);
1649 let Some(eq) = equation(&ctx, &ty) else {
1650 continue;
1651 };
1652 if def_eq(&ctx, &eq.lhs, &eq.rhs) {
1653 let _ = self.clear(&e);
1654 continue;
1655 }
1656 if substitutable(&goal, &eq).is_some() {
1657 self.subst(&e)?;
1658 continue;
1659 }
1660 if injected.contains(&e) {
1661 continue;
1662 }
1663 let heads = (
1664 crate::type_checker::demanded_index_constructor_head(&ctx, &eq.lhs),
1665 crate::type_checker::demanded_index_constructor_head(&ctx, &eq.rhs),
1666 );
1667 if let (Some((cl, _)), Some((cr, _))) = heads {
1668 if ctx.constructor_inductive(&cl).is_some()
1669 && ctx.constructor_inductive(&cl) == ctx.constructor_inductive(&cr)
1670 {
1671 let produced = self.inject(&e, &[])?;
1672 injected.insert(e.clone());
1673 for h in produced.into_iter().rev() {
1674 queue.push_front(h);
1675 }
1676 queue.push_back(e);
1677 }
1678 }
1679 }
1680 Ok(())
1681 }
1682
1683 fn on_goal(
1685 &mut self,
1686 m: &str,
1687 f: impl FnOnce(&mut Self) -> Result<(), TacticError>,
1688 ) -> Result<(), TacticError> {
1689 let Some(pos) = self.open.iter().position(|g| g == m) else {
1690 return Ok(());
1691 };
1692 let before: Vec<String> = self.open.clone();
1693 let goal = self.open.remove(pos);
1694 self.open.insert(0, goal);
1695 let result = f(self);
1696 let produced: Vec<String> = self
1697 .open
1698 .iter()
1699 .filter(|g| !before.contains(g) || *g == m)
1700 .cloned()
1701 .collect();
1702 let mut order: Vec<String> = before
1703 .iter()
1704 .filter(|g| *g != m && self.open.contains(g))
1705 .cloned()
1706 .collect();
1707 let at = pos.min(order.len());
1708 order.splice(at..at, produced);
1709 self.open = order;
1710 result
1711 }
1712
1713 pub fn subst(&mut self, h: &str) -> Result<(), TacticError> {
1716 let (_, goal) = self.focus()?;
1717 let ctx = self.scope(&goal.hyps);
1718 let Some((_, h_ty)) = goal.hyps.iter().find(|(n, _)| n == h).cloned() else {
1719 return Err(refuse("subst", &goal, format!("`{h}` is not a hypothesis")));
1720 };
1721 let Some(eq) = equation(&ctx, &h_ty) else {
1722 return Err(refuse(
1723 "subst",
1724 &goal,
1725 format!("`{h}` : {} is not an equation", Shown(&h_ty)),
1726 ));
1727 };
1728 match substitutable(&goal, &eq) {
1729 Some(Side::Right) => self.cases(h, &[&[]]),
1730 Some(Side::Left) => {
1731 let flipped = fresh_name(h, &ctx, &goal.hyps, &[&goal.target]);
1732 let z = fresh_name("z", &ctx, &goal.hyps, &[&goal.target, &h_ty]);
1733 let symmetric = Term::Match {
1734 discriminant: Box::new(Term::Var(h.to_string())),
1735 motive: Box::new(lam(
1736 &z,
1737 eq.carrier.clone(),
1738 lam(
1739 "_",
1740 eq_term(&eq.carrier, &eq.lhs, &Term::Var(z.clone())),
1741 eq_term(&eq.carrier, &Term::Var(z.clone()), &eq.lhs),
1742 ),
1743 )),
1744 cases: vec![apps(eq.refl(), &[eq.carrier.clone(), eq.lhs.clone()])],
1745 };
1746 self.assert_fact(&flipped, eq_term(&eq.carrier, &eq.rhs, &eq.lhs), symmetric)?;
1747 self.cases(&flipped, &[&[]])?;
1748 let _ = self.clear(h);
1749 Ok(())
1750 }
1751 None => Err(refuse(
1752 "subst",
1753 &goal,
1754 format!("neither side of `{h}` is a hypothesis the other side does not mention"),
1755 )),
1756 }
1757 }
1758
1759 pub fn injection(&mut self, h: &str, names: &[&str]) -> Result<(), TacticError> {
1764 self.inject(h, names).map(|_| ())
1765 }
1766
1767 fn inject(&mut self, h: &str, names: &[&str]) -> Result<Vec<String>, TacticError> {
1770 let (m, goal) = self.focus()?;
1771 let ctx = self.scope(&goal.hyps);
1772 let Some((_, h_ty)) = goal.hyps.iter().find(|(n, _)| n == h).cloned() else {
1773 return Err(refuse(
1774 "injection",
1775 &goal,
1776 format!("`{h}` is not a hypothesis"),
1777 ));
1778 };
1779 let Some(eq) = equation(&ctx, &h_ty) else {
1780 return Err(refuse(
1781 "injection",
1782 &goal,
1783 format!("`{h}` : {} is not an equation", Shown(&h_ty)),
1784 ));
1785 };
1786 let family = demanded_type_head(&ctx, eq.carrier.clone(), TypeHead::Inductive);
1787 let (fhead, fargs) = spine(&family);
1788 let ind = match fhead {
1789 Term::Global(n) | Term::Const { name: n, .. } if ctx.is_inductive(n) => n.clone(),
1790 _ => {
1791 return Err(refuse(
1792 "injection",
1793 &goal,
1794 format!("{} is not an inductive family", Shown(&eq.carrier)),
1795 ))
1796 }
1797 };
1798 let np = ctx.inductive_num_params(&ind).min(fargs.len());
1799 let read = |t: &Term| crate::type_checker::demanded_index_constructor_head(&ctx, t);
1800 let (Some((cl, al)), Some((cr, ar))) = (read(&eq.lhs), read(&eq.rhs)) else {
1801 return Err(refuse(
1802 "injection",
1803 &goal,
1804 format!(
1805 "{} and {} are not both constructor applications",
1806 Shown(&eq.lhs),
1807 Shown(&eq.rhs)
1808 ),
1809 ));
1810 };
1811 let allowed = crate::type_checker::constructors_the_indices_allow(&ctx, &ind, np, &fargs);
1812 let constructors: Vec<String> = ctx
1813 .get_constructors(&ind)
1814 .iter()
1815 .map(|(c, _)| c.to_string())
1816 .collect();
1817 let mut avoid: Vec<&Term> = vec![&goal.target, &h_ty];
1818 avoid.extend(goal.hyps.iter().map(|(_, t)| t));
1819 let index_types = {
1821 let arity = infer_type(&ctx, &apps(fhead.clone(), &fargs[..np])).map_err(|e| {
1822 refuse(
1823 "injection",
1824 &goal,
1825 format!("the family does not type at these parameters: {e}"),
1826 )
1827 })?;
1828 pi_telescope(&ctx, &arity, fargs.len() - np)
1829 };
1830 let mut taken: Vec<String> = Vec::new();
1831 let fresh = |base: &str, taken: &mut Vec<String>| {
1832 let name = fresh_name_avoiding(base, &ctx, &goal.hyps, &avoid, taken);
1833 taken.push(name.clone());
1834 name
1835 };
1836 let jvars: Vec<String> = index_types.iter().map(|_| fresh("j", &mut taken)).collect();
1837 let zp = fresh("z", &mut taken);
1838 let z = fresh("z", &mut taken);
1839 let mut fields_of: Vec<Vec<(String, Term)>> = Vec::new();
1840 for &ci in &allowed {
1841 let ctor_ty = infer_type(
1842 &ctx,
1843 &apps(constructor_at(&ctx, fhead, &constructors[ci]), &fargs[..np]),
1844 )
1845 .map_err(|e| {
1846 refuse(
1847 "injection",
1848 &goal,
1849 format!("`{}` does not type: {e}", constructors[ci]),
1850 )
1851 })?;
1852 let telescope = pi_telescope(&ctx, &ctor_ty, usize::MAX);
1855 taken.extend(telescope.iter().map(|(b, _)| b.clone()));
1856 let mut fields: Vec<(String, Term)> = Vec::new();
1857 let mut renames: Vec<(String, String)> = Vec::new();
1858 for (b, bt) in telescope {
1859 let bt = renames.iter().fold(bt, |t, (from, to)| {
1860 substitute(&t, from, &Term::Var(to.clone()))
1861 });
1862 let renamed = fresh(&b, &mut taken);
1863 renames.push((b, renamed.clone()));
1864 fields.push((renamed, bt));
1865 }
1866 fields_of.push(fields);
1867 }
1868 let predicate = |on: &dyn Fn(&str, &[(String, Term)]) -> Term, of: Term| -> Term {
1869 let mut motive = lam(
1870 &zp,
1871 apps(
1872 apps(fhead.clone(), &fargs[..np]),
1873 &jvars
1874 .iter()
1875 .map(|j| Term::Var(j.clone()))
1876 .collect::<Vec<_>>(),
1877 ),
1878 Term::Sort(crate::term::Universe::Prop),
1879 );
1880 for (k, (_, t)) in index_types.iter().enumerate().rev() {
1881 motive = lam(&jvars[k], t.clone(), motive);
1882 }
1883 let cases = allowed
1884 .iter()
1885 .zip(&fields_of)
1886 .map(|(&ci, fields)| {
1887 fields
1888 .iter()
1889 .rev()
1890 .fold(on(&constructors[ci], fields), |b, (x, t)| {
1891 lam(x, t.clone(), b)
1892 })
1893 })
1894 .collect();
1895 Term::Match {
1896 discriminant: Box::new(of),
1897 motive: Box::new(motive),
1898 cases,
1899 }
1900 };
1901 let transport = |on: &dyn Fn(&str, &[(String, Term)]) -> Term, start: Term| -> Term {
1902 Term::Match {
1903 discriminant: Box::new(Term::Var(h.to_string())),
1904 motive: Box::new(lam(
1905 &z,
1906 eq.carrier.clone(),
1907 lam(
1908 "_",
1909 eq_term(&eq.carrier, &eq.lhs, &Term::Var(z.clone())),
1910 predicate(on, Term::Var(z.clone())),
1911 ),
1912 )),
1913 cases: vec![start],
1914 }
1915 };
1916 if cl != cr {
1917 let falsity = transport(
1918 &|c: &str, _: &[(String, Term)]| {
1919 Term::Global(if c == cl { "True" } else { "False" }.into())
1920 },
1921 Term::Global("I".into()),
1922 );
1923 let proof = Term::Match {
1924 discriminant: Box::new(falsity),
1925 motive: Box::new(lam("_", Term::Global("False".into()), goal.target.clone())),
1926 cases: Vec::new(),
1927 };
1928 match infer_type(&ctx, &proof) {
1929 Ok(ty) if is_subtype(&ctx, &ty, &goal.target) => {}
1930 Ok(ty) => {
1931 return Err(refuse(
1932 "injection",
1933 &goal,
1934 format!("the refutation proves {}", Shown(&ty)),
1935 ))
1936 }
1937 Err(e) => {
1938 return Err(refuse(
1939 "injection",
1940 &goal,
1941 format!("the refutation does not type: {e}"),
1942 ))
1943 }
1944 }
1945 self.close(&m, proof, Vec::new());
1946 return Ok(Vec::new());
1947 }
1948 let position = allowed
1949 .iter()
1950 .position(|&ci| constructors[ci] == cl)
1951 .ok_or_else(|| {
1952 refuse(
1953 "injection",
1954 &goal,
1955 format!("`{cl}` cannot produce a value of {}", Shown(&eq.carrier)),
1956 )
1957 })?;
1958 let fields = &fields_of[position];
1959 let (lhs_fields, rhs_fields) = (&al[np.min(al.len())..], &ar[np.min(ar.len())..]);
1960 let mut facts: Vec<(String, Term, Term)> = Vec::new();
1961 for (k, (_, field_ty)) in fields.iter().enumerate() {
1962 if fields[..k]
1963 .iter()
1964 .any(|(x, _)| crate::type_checker::occurs_free(field_ty, x))
1965 {
1966 continue;
1967 }
1968 let (Some(a), Some(b)) = (lhs_fields.get(k), rhs_fields.get(k)) else {
1969 continue;
1970 };
1971 let on = |c: &str, xs: &[(String, Term)]| {
1972 if c == cl {
1973 eq_term(field_ty, a, &Term::Var(xs[k].0.clone()))
1974 } else {
1975 Term::Global("True".into())
1976 }
1977 };
1978 let proof = transport(
1979 &on,
1980 apps(Term::Global("refl".into()), &[field_ty.clone(), a.clone()]),
1981 );
1982 let fact = eq_term(field_ty, a, b);
1983 match infer_type(&ctx, &proof) {
1984 Ok(ty) if is_subtype(&ctx, &ty, &fact) => {}
1985 Ok(ty) => {
1986 return Err(refuse(
1987 "injection",
1988 &goal,
1989 format!("the field's transport proves {}", Shown(&ty)),
1990 ))
1991 }
1992 Err(e) => {
1993 return Err(refuse(
1994 "injection",
1995 &goal,
1996 format!("the field's transport does not type: {e}"),
1997 ))
1998 }
1999 }
2000 let name = match names.get(facts.len()) {
2001 Some(n) if goal.hyps.iter().any(|(h, _)| h == n) => {
2002 return Err(refuse(
2003 "injection",
2004 &goal,
2005 format!("`{n}` already names a hypothesis"),
2006 ))
2007 }
2008 Some(n) => n.to_string(),
2009 None => fresh_name_avoiding(
2010 "e",
2011 &ctx,
2012 &goal.hyps,
2013 &avoid,
2014 &facts.iter().map(|(n, _, _)| n.clone()).collect::<Vec<_>>(),
2015 ),
2016 };
2017 facts.push((name, fact, proof));
2018 }
2019 if facts.is_empty() {
2020 return Err(refuse(
2021 "injection",
2022 &goal,
2023 format!("`{cl}` has no field whose equation can be stated"),
2024 ));
2025 }
2026 if names.len() > facts.len() {
2027 return Err(refuse(
2028 "injection",
2029 &goal,
2030 format!("{} names for {} equations", names.len(), facts.len()),
2031 ));
2032 }
2033 let mut hyps = goal.hyps.clone();
2034 hyps.extend(facts.iter().map(|(n, t, _)| (n.clone(), t.clone())));
2035 let inner = self.declare(hyps, goal.target.clone());
2036 let lambda = facts
2037 .iter()
2038 .rev()
2039 .fold(Term::Var(inner.clone()), |b, (n, t, _)| {
2040 lam(n, t.clone(), b)
2041 });
2042 let proof = apps(
2043 lambda,
2044 &facts.iter().map(|(_, _, p)| p.clone()).collect::<Vec<_>>(),
2045 );
2046 self.close(&m, proof, vec![inner]);
2047 Ok(facts.into_iter().map(|(n, _, _)| n).collect())
2048 }
2049
2050 pub fn calc(&mut self, steps: &[(Term, Term)]) -> Result<(), TacticError> {
2054 let (m, goal) = self.focus()?;
2055 let ctx = self.scope(&goal.hyps);
2056 if steps.is_empty() {
2057 return Err(refuse(
2058 "calc",
2059 &goal,
2060 "a calculation has at least one step".to_string(),
2061 ));
2062 }
2063 let mut mctx = self.mctx.clone();
2064 let mut carrier: Option<Term> = None;
2065 let mut first: Option<Term> = None;
2066 let mut last: Option<Term> = None;
2067 let mut proofs: Vec<(Term, Term)> = Vec::with_capacity(steps.len());
2068 let mut holes: Vec<(String, Term)> = Vec::new();
2069 for (i, (relation, proof)) in steps.iter().enumerate() {
2070 let step = i + 1;
2071 let relation = localize(relation, &goal.hyps);
2072 let (head, args) = spine(&relation);
2073 let (Term::Global(eq) | Term::Const { name: eq, .. }) = head else {
2074 return Err(refuse(
2075 "calc",
2076 &goal,
2077 format!("step {step}, {}, is not an equation", Shown(&relation)),
2078 ));
2079 };
2080 let [a, l, r] = args.as_slice() else {
2081 return Err(refuse(
2082 "calc",
2083 &goal,
2084 format!("step {step}, {}, is not an equation", Shown(&relation)),
2085 ));
2086 };
2087 if eq != "Eq" {
2088 return Err(refuse(
2089 "calc",
2090 &goal,
2091 format!("step {step}, {}, is not an equation", Shown(&relation)),
2092 ));
2093 }
2094 let l = match (l, &last) {
2095 (Term::Hole, Some(previous)) => previous.clone(),
2096 (Term::Hole, None) => {
2097 return Err(refuse(
2098 "calc",
2099 &goal,
2100 "the first step's left side has no previous step to read".to_string(),
2101 ))
2102 }
2103 (l, Some(previous)) if !def_eq(&ctx, previous, l) => {
2104 return Err(refuse(
2105 "calc",
2106 &goal,
2107 format!(
2108 "step {step} starts at {}, and step {i} ends at {}",
2109 Shown(l),
2110 Shown(previous)
2111 ),
2112 ))
2113 }
2114 (l, _) => l.clone(),
2115 };
2116 if let Some(c) = &carrier {
2117 if !def_eq(&ctx, c, a) {
2118 return Err(refuse(
2119 "calc",
2120 &goal,
2121 format!(
2122 "step {step} relates values of {}, not of {}",
2123 Shown(a),
2124 Shown(c)
2125 ),
2126 ));
2127 }
2128 }
2129 let stated = eq_term(a, &l, r);
2130 infer_sort(&ctx, &stated).map_err(|e| {
2131 refuse(
2132 "calc",
2133 &goal,
2134 format!("step {step}, {}, is not a type: {e}", Shown(&stated)),
2135 )
2136 })?;
2137 let proof = localize(proof, &goal.hyps);
2138 if matches!(proof, Term::Hole) {
2139 let name = fresh_meta(&mut mctx);
2140 holes.push((name.clone(), stated.clone()));
2141 proofs.push((Term::Var(name), stated));
2142 } else {
2143 let (value, ty) = typed(&ctx, &mut mctx, &goal.hyps, &proof, Some(&stated))
2144 .map_err(|e| {
2145 refuse(
2146 "calc",
2147 &goal,
2148 format!("step {step}'s proof {} does not type: {e}", Shown(&proof)),
2149 )
2150 })?;
2151 elaborate::unify_in(&ctx, &mut mctx, &goal.hyps, &ty, &stated);
2152 let (value, ty) = (
2153 elaborate::instantiate(&value, &mctx),
2154 elaborate::instantiate(&ty, &mctx),
2155 );
2156 if elaborate::mentions_meta(&value) {
2157 return Err(refuse(
2158 "calc",
2159 &goal,
2160 format!("step {step}'s proof leaves a hole; write `_` for the whole proof to leave it as a goal"),
2161 ));
2162 }
2163 if !is_subtype(&ctx, &ty, &stated) {
2164 return Err(refuse(
2165 "calc",
2166 &goal,
2167 format!(
2168 "step {step} states {}, and its proof proves {}",
2169 Shown(&stated),
2170 Shown(&ty)
2171 ),
2172 ));
2173 }
2174 proofs.push((value, stated));
2175 }
2176 carrier.get_or_insert_with(|| a.clone());
2177 first.get_or_insert_with(|| l.clone());
2178 last = Some(r.clone());
2179 }
2180 let (carrier, first, last) = (
2181 carrier.expect("a step"),
2182 first.expect("a step"),
2183 last.expect("a step"),
2184 );
2185 let conclusion = eq_term(&carrier, &first, &last);
2186 if !is_subtype(&ctx, &conclusion, &goal.target) {
2187 return Err(refuse(
2188 "calc",
2189 &goal,
2190 format!(
2191 "the calculation proves {}, and the goal is {}",
2192 Shown(&conclusion),
2193 Shown(&goal.target)
2194 ),
2195 ));
2196 }
2197 let mut avoid: Vec<&Term> = vec![&goal.target];
2199 avoid.extend(proofs.iter().map(|(p, _)| p));
2200 let w = fresh_name("w", &ctx, &goal.hyps, &avoid);
2201 let mut chained = proofs[0].0.clone();
2202 for (q, stated) in &proofs[1..] {
2203 let (_, args) = spine(stated);
2204 let y = args[1].clone();
2205 chained = Term::Match {
2206 discriminant: Box::new(q.clone()),
2207 motive: Box::new(lam(
2208 &w,
2209 carrier.clone(),
2210 lam(
2211 "_",
2212 eq_term(&carrier, &y, &Term::Var(w.clone())),
2213 eq_term(&carrier, &first, &Term::Var(w.clone())),
2214 ),
2215 )),
2216 cases: vec![chained],
2217 };
2218 }
2219 let mut probe_ctx = ctx.clone();
2221 let mut probe = MetaCtx::new();
2222 let mut locals: Vec<String> = Vec::new();
2223 for (name, stated) in &holes {
2224 let local =
2225 fresh_name_avoiding("step", &ctx, &goal.hyps, &[&chained, &goal.target], &locals);
2226 locals.push(local.clone());
2227 probe_ctx = probe_ctx.extend(&local, stated.clone());
2228 probe.solve(name, Term::Var(local));
2229 }
2230 let checked = elaborate::instantiate(&chained, &probe);
2231 match infer_type(&probe_ctx, &checked) {
2232 Ok(t) if is_subtype(&probe_ctx, &t, &goal.target) => {}
2233 Ok(t) => {
2234 return Err(refuse(
2235 "calc",
2236 &goal,
2237 format!("the chain proves {}, not the goal", Shown(&t)),
2238 ))
2239 }
2240 Err(e) => {
2241 return Err(refuse(
2242 "calc",
2243 &goal,
2244 format!("the chain does not type: {e}"),
2245 ))
2246 }
2247 }
2248 self.mctx = mctx;
2249 let mut subgoals = Vec::with_capacity(holes.len());
2250 for (name, stated) in holes {
2251 self.decls.insert(
2252 name.clone(),
2253 Decl {
2254 hyps: goal.hyps.clone(),
2255 target: stated,
2256 },
2257 );
2258 subgoals.push(name);
2259 }
2260 self.close(&m, chained, subgoals);
2261 Ok(())
2262 }
2263
2264 pub fn cc(&mut self) -> Result<(), TacticError> {
2269 let (m, goal) = self.focus()?;
2270 let ctx = self.scope(&goal.hyps);
2271 let Some(target) = equation(&ctx, &goal.target) else {
2272 return Err(refuse(
2273 "cc",
2274 &goal,
2275 "the goal is not an equation".to_string(),
2276 ));
2277 };
2278 let mut closure = congruence::Closure::new(&ctx, &goal);
2279 for (h, ty) in &goal.hyps {
2280 if let Some(eq) = equation(&ctx, ty) {
2281 closure.assert_hypothesis(h, &eq.lhs, &eq.rhs);
2282 }
2283 }
2284 let (a, b) = (closure.term(&target.lhs), closure.term(&target.rhs));
2285 closure.saturate();
2286 let Some(proof) = closure.prove(a, b) else {
2287 return Err(refuse(
2288 "cc",
2289 &goal,
2290 format!(
2291 "the hypotheses do not entail {} = {}",
2292 Shown(&target.lhs),
2293 Shown(&target.rhs)
2294 ),
2295 ));
2296 };
2297 match infer_type(&ctx, &proof) {
2298 Ok(ty) if is_subtype(&ctx, &ty, &goal.target) => {}
2299 Ok(ty) => {
2300 return Err(refuse(
2301 "cc",
2302 &goal,
2303 format!("the chain proves {}, not the goal", Shown(&ty)),
2304 ))
2305 }
2306 Err(e) => return Err(refuse("cc", &goal, format!("the chain does not type: {e}"))),
2307 }
2308 self.close(&m, proof, Vec::new());
2309 Ok(())
2310 }
2311
2312 pub fn decide(&mut self) -> Result<(), TacticError> {
2316 let (m, goal) = self.focus()?;
2317 let ctx = self.scope(&goal.hyps);
2318 let required = apps(Term::Global("Decidable".into()), &[goal.target.clone()]);
2319 let mut mctx = self.mctx.clone();
2320 let Some(instance) = elaborate::resolve_instance(&ctx, &mut mctx, &required) else {
2321 return Err(refuse(
2322 "decide",
2323 &goal,
2324 format!("no `Decidable` instance decides {}", Shown(&goal.target)),
2325 ));
2326 };
2327 let instance = elaborate::instantiate(&instance, &mctx);
2328 let verdict = crate::reduction::normalize(
2329 &ctx,
2330 &apps(
2331 Term::Global("decide".into()),
2332 &[goal.target.clone(), instance.clone()],
2333 ),
2334 );
2335 match &verdict {
2336 Term::Global(b) if b == "true" => {}
2337 Term::Global(b) if b == "false" => {
2338 return Err(refuse(
2339 "decide",
2340 &goal,
2341 format!(
2342 "{} is false: its instance decides it so",
2343 Shown(&goal.target)
2344 ),
2345 ))
2346 }
2347 other => {
2348 return Err(refuse(
2349 "decide",
2350 &goal,
2351 format!(
2352 "the instance does not compute to a verdict: `decide` reduces to {}",
2353 Shown(other)
2354 ),
2355 ))
2356 }
2357 }
2358 let proof = apps(
2359 Term::Global("of_decide_eq_true".into()),
2360 &[
2361 goal.target.clone(),
2362 instance,
2363 apps(
2364 Term::Global("refl".into()),
2365 &[Term::Global("Bool".into()), Term::Global("true".into())],
2366 ),
2367 ],
2368 );
2369 match infer_type(&ctx, &proof) {
2370 Ok(ty) if is_subtype(&ctx, &ty, &goal.target) => {}
2371 Ok(ty) => {
2372 return Err(refuse(
2373 "decide",
2374 &goal,
2375 format!("the bridge proves {}, not the goal", Shown(&ty)),
2376 ))
2377 }
2378 Err(e) => {
2379 return Err(refuse(
2380 "decide",
2381 &goal,
2382 format!("the bridge does not type: {e}"),
2383 ))
2384 }
2385 }
2386 self.close(&m, proof, Vec::new());
2387 Ok(())
2388 }
2389
2390 pub fn simp(&mut self, lemmas: &[Term]) -> Result<(), TacticError> {
2398 let saved = self.clone();
2399 let result = self.simp_rewrites(lemmas);
2400 if result.is_err() {
2401 *self = saved;
2402 }
2403 result
2404 }
2405
2406 fn simp_rewrites(&mut self, lemmas: &[Term]) -> Result<(), TacticError> {
2407 let (_, goal) = self.focus()?;
2408 let ctx = self.scope(&goal.hyps);
2409 let rules: Vec<(Term, SimpRule)> = if lemmas.is_empty() {
2410 ctx.simp_rules()
2411 .iter()
2412 .map(|r| (Term::Global(r.name.clone()), r.clone()))
2413 .collect()
2414 } else {
2415 lemmas
2416 .iter()
2417 .map(|lemma| {
2418 let proof = localize(lemma, &goal.hyps);
2419 let ty = infer_type(&ctx, &proof).map_err(|e| {
2420 refuse(
2421 "simp",
2422 &goal,
2423 format!("{} does not type: {e}", Shown(&proof)),
2424 )
2425 })?;
2426 let rule = SimpRule::compile(&Shown(&proof).to_string(), &ty).map_err(|e| {
2427 refuse(
2428 "simp",
2429 &goal,
2430 format!("{} : {} is not a rewrite: {e}", Shown(&proof), Shown(&ty)),
2431 )
2432 })?;
2433 Ok((proof, rule))
2434 })
2435 .collect::<Result<_, TacticError>>()?
2436 };
2437 if rules.is_empty() {
2438 return Err(refuse(
2439 "simp",
2440 &goal,
2441 "there is no lemma to rewrite by".to_string(),
2442 ));
2443 }
2444 let mut fired: Vec<String> = Vec::new();
2445 loop {
2446 let (_, goal) = self.focus()?;
2447 let Some((proof, name)) = simp_instance(&rules, &goal.target, &mut Vec::new()) else {
2448 break;
2449 };
2450 if fired.len() == SIMP_STEPS {
2451 let last: Vec<&str> = fired.iter().rev().take(4).map(String::as_str).collect();
2452 return Err(refuse(
2453 "simp",
2454 &goal,
2455 format!(
2456 "still rewriting after {SIMP_STEPS} rewrites (the last by {})",
2457 last.join(", ")
2458 ),
2459 ));
2460 }
2461 self.rewrite(&proof, false)?;
2462 fired.push(name);
2463 }
2464 if fired.is_empty() {
2465 let (_, goal) = self.focus()?;
2466 return Err(refuse(
2467 "simp",
2468 &goal,
2469 "no lemma rewrites the goal".to_string(),
2470 ));
2471 }
2472 if self
2473 .focus()
2474 .is_ok_and(|(_, goal)| equation(&self.scope(&goal.hyps), &goal.target).is_some())
2475 {
2476 let _ = self.rfl();
2477 }
2478 Ok(())
2479 }
2480
2481 fn assert_fact(&mut self, name: &str, fact: Term, proof: Term) -> Result<(), TacticError> {
2483 let (m, goal) = self.focus()?;
2484 let ctx = self.scope(&goal.hyps);
2485 match infer_type(&ctx, &proof) {
2486 Ok(ty) if is_subtype(&ctx, &ty, &fact) => {}
2487 Ok(ty) => {
2488 return Err(refuse(
2489 "have",
2490 &goal,
2491 format!("the proof proves {}, not {}", Shown(&ty), Shown(&fact)),
2492 ))
2493 }
2494 Err(e) => {
2495 return Err(refuse(
2496 "have",
2497 &goal,
2498 format!("the proof of {} does not type: {e}", Shown(&fact)),
2499 ))
2500 }
2501 }
2502 let mut hyps = goal.hyps.clone();
2503 hyps.push((name.to_string(), fact.clone()));
2504 let inner = self.declare(hyps, goal.target.clone());
2505 self.close(
2506 &m,
2507 Term::App(
2508 Box::new(lam(name, fact, Term::Var(inner.clone()))),
2509 Box::new(proof),
2510 ),
2511 vec![inner],
2512 );
2513 Ok(())
2514 }
2515
2516 fn clear(&mut self, h: &str) -> Result<(), TacticError> {
2518 let (m, goal) = self.focus()?;
2519 if !goal.hyps.iter().any(|(n, _)| n == h) {
2520 return Err(refuse("clear", &goal, format!("`{h}` is not a hypothesis")));
2521 }
2522 if crate::type_checker::occurs_free(&goal.target, h)
2523 || goal
2524 .hyps
2525 .iter()
2526 .any(|(n, t)| n != h && crate::type_checker::occurs_free(t, h))
2527 {
2528 return Err(refuse(
2529 "clear",
2530 &goal,
2531 format!("the goal still mentions `{h}`"),
2532 ));
2533 }
2534 let hyps: Vec<(String, Term)> = goal.hyps.iter().filter(|(n, _)| n != h).cloned().collect();
2535 let inner = self.declare(hyps, goal.target.clone());
2536 self.close(&m, Term::Var(inner.clone()), vec![inner]);
2537 Ok(())
2538 }
2539
2540 pub fn refine(&mut self, term: &Term) -> Result<(), TacticError> {
2543 let (m, goal) = self.focus()?;
2544 let ctx = self.scope(&goal.hyps);
2545 let term = localize(term, &goal.hyps);
2546 let mut mctx = self.mctx.clone();
2547 let first = mctx.holes().len();
2548 let (value, ty) =
2549 elaborate::elaborate_in(&ctx, &mut mctx, &goal.hyps, &term, Some(&goal.target))
2550 .map_err(|e| {
2551 refuse(
2552 "refine",
2553 &goal,
2554 format!(
2555 "{} does not elaborate against the goal {}: {e}",
2556 Shown(&term),
2557 Shown(&goal.target)
2558 ),
2559 )
2560 })?;
2561 if !elaborate::unify_in(&ctx, &mut mctx, &goal.hyps, &ty, &goal.target) {
2562 let ty = elaborate::instantiate(&ty, &mctx);
2563 return Err(refuse(
2564 "refine",
2565 &goal,
2566 format!(
2567 "the goal is {}, and {} has type {}",
2568 Shown(&goal.target),
2569 Shown(&term),
2570 Shown(&ty)
2571 ),
2572 ));
2573 }
2574 let value = elaborate::instantiate(&value, &mctx);
2575 let holes: Vec<(String, Vec<(String, Term)>, Term)> = mctx.holes()[first..]
2576 .iter()
2577 .filter(|h| mctx.solution(&h.name).is_none())
2578 .map(|h| {
2579 let scope = h
2580 .scope
2581 .iter()
2582 .map(|(n, t)| (n.clone(), elaborate::instantiate(t, &mctx)))
2583 .collect();
2584 (h.name.clone(), scope, elaborate::instantiate(&h.ty, &mctx))
2585 })
2586 .collect();
2587 for (_, scope, ty) in &holes {
2588 if elaborate::mentions_meta(ty) {
2589 return Err(refuse(
2590 "refine",
2591 &goal,
2592 format!("the type {} of a hole is not determined", Shown(ty)),
2593 ));
2594 }
2595 if let Some((n, _)) = scope
2596 .iter()
2597 .enumerate()
2598 .find(|(i, (n, _))| scope[..*i].iter().any(|(o, _)| o == n))
2599 .map(|(_, b)| b)
2600 {
2601 return Err(refuse(
2602 "refine",
2603 &goal,
2604 format!("a binder around a hole shadows the hypothesis `{n}`; name it apart"),
2605 ));
2606 }
2607 }
2608 let open: BTreeSet<&str> = holes.iter().map(|(n, _, _)| n.as_str()).collect();
2609 let mut stray = BTreeSet::new();
2610 metas(&value, &mut stray);
2611 if let Some(meta) = stray.iter().find(|n| !open.contains(n.as_str())) {
2612 return Err(refuse(
2613 "refine",
2614 &goal,
2615 format!(
2616 "{} leaves {meta}, an argument unification cannot determine",
2617 Shown(&term)
2618 ),
2619 ));
2620 }
2621 let mut probe_ctx = ctx.clone();
2623 let mut probe = MetaCtx::new();
2624 let mut locals: Vec<String> = Vec::new();
2625 for (name, scope, ty) in &holes {
2626 let extra = &scope[goal.hyps.len().min(scope.len())..];
2627 let local =
2628 fresh_name_avoiding("hole", &ctx, &goal.hyps, &[&value, &goal.target], &locals);
2629 locals.push(local.clone());
2630 let local_ty = extra
2631 .iter()
2632 .rev()
2633 .fold(ty.clone(), |b, (n, t)| pi(n, t.clone(), b));
2634 probe_ctx = probe_ctx.extend(&local, local_ty);
2635 probe.solve(
2636 name,
2637 apps(
2638 Term::Var(local),
2639 &extra
2640 .iter()
2641 .map(|(n, _)| Term::Var(n.clone()))
2642 .collect::<Vec<_>>(),
2643 ),
2644 );
2645 }
2646 let checked = elaborate::instantiate(&value, &probe);
2647 match infer_type(&probe_ctx, &checked) {
2648 Ok(t) if is_subtype(&probe_ctx, &t, &goal.target) => {}
2649 Ok(t) => {
2650 return Err(refuse(
2651 "refine",
2652 &goal,
2653 format!("{} proves {}, not the goal", Shown(&term), Shown(&t)),
2654 ))
2655 }
2656 Err(e) => {
2657 return Err(refuse(
2658 "refine",
2659 &goal,
2660 format!("{} does not type: {e}", Shown(&term)),
2661 ))
2662 }
2663 }
2664 self.mctx = mctx;
2665 let mut subgoals = Vec::with_capacity(holes.len());
2666 for (name, scope, ty) in holes {
2667 self.decls.insert(
2668 name.clone(),
2669 Decl {
2670 hyps: scope,
2671 target: ty,
2672 },
2673 );
2674 subgoals.push(name);
2675 }
2676 self.close(&m, value, subgoals);
2677 Ok(())
2678 }
2679
2680 fn declare(&mut self, hyps: Vec<(String, Term)>, target: Term) -> String {
2681 let name = self.fresh_meta_name();
2682 self.decls.insert(name.clone(), Decl { hyps, target });
2683 name
2684 }
2685
2686 fn fresh_meta_name(&mut self) -> String {
2687 match self.mctx.fresh() {
2688 Term::Var(name) => name,
2689 other => unreachable!("a metavariable is a `Var`, not {other:?}"),
2690 }
2691 }
2692
2693 fn view(&self, m: &str) -> Goal {
2694 let decl = &self.decls[m];
2695 let mut hyps: Vec<(String, Term)> = Vec::with_capacity(decl.hyps.len());
2696 for (n, t) in &decl.hyps {
2697 let t = simplify(
2698 &self.scope(&hyps),
2699 &hyps,
2700 &elaborate::instantiate(t, &self.mctx),
2701 );
2702 hyps.push((n.clone(), t));
2703 }
2704 let target = simplify(
2705 &self.scope(&hyps),
2706 &hyps,
2707 &elaborate::instantiate(&decl.target, &self.mctx),
2708 );
2709 Goal { hyps, target }
2710 }
2711
2712 fn focus(&self) -> Result<(String, Goal), TacticError> {
2713 let m = self.open.first().ok_or(TacticError::NoGoals)?;
2714 Ok((m.clone(), self.view(m)))
2715 }
2716
2717 fn scope(&self, hyps: &[(String, Term)]) -> Context {
2718 hyps.iter()
2719 .fold(self.ctx.clone(), |c, (n, t)| c.extend(n, t.clone()))
2720 }
2721
2722 fn close(&mut self, m: &str, proof: Term, subgoals: Vec<String>) {
2725 self.mctx.solve(m, proof);
2726 self.open.retain(|g| g != m);
2727 let fresh: Vec<String> = subgoals
2728 .into_iter()
2729 .filter(|g| self.mctx.solution(g).is_none())
2730 .collect();
2731 for (i, g) in fresh.into_iter().enumerate() {
2732 self.open.insert(i, g);
2733 }
2734 let solved: Vec<String> = self
2735 .open
2736 .iter()
2737 .filter(|g| self.mctx.solution(g).is_some())
2738 .cloned()
2739 .collect();
2740 self.open.retain(|g| !solved.contains(g));
2741 }
2742}
2743
2744pub const SIMP_STEPS: usize = 256;
2746
2747fn simp_instance(
2751 rules: &[(Term, SimpRule)],
2752 t: &Term,
2753 bound: &mut Vec<String>,
2754) -> Option<(Term, String)> {
2755 if !bound.iter().any(|b| crate::type_checker::occurs_free(t, b)) {
2756 for (proof, rule) in rules {
2757 if let Some(instance) = rule.matching(t) {
2758 return Some((apps(proof.clone(), &instance), rule.name.clone()));
2759 }
2760 }
2761 }
2762 let mut under = |name: &str, body: &Term, bound: &mut Vec<String>| {
2763 bound.push(name.to_string());
2764 let found = simp_instance(rules, body, bound);
2765 bound.pop();
2766 found
2767 };
2768 match t {
2769 Term::App(f, a) => {
2770 simp_instance(rules, f, bound).or_else(|| simp_instance(rules, a, bound))
2771 }
2772 Term::Pi {
2773 param,
2774 param_type,
2775 body_type,
2776 } => simp_instance(rules, param_type, bound).or_else(|| under(param, body_type, bound)),
2777 Term::Lambda {
2778 param,
2779 param_type,
2780 body,
2781 } => simp_instance(rules, param_type, bound).or_else(|| under(param, body, bound)),
2782 _ => None,
2783 }
2784}
2785
2786fn fresh_meta(mctx: &mut MetaCtx) -> String {
2787 match mctx.fresh() {
2788 Term::Var(name) => name,
2789 other => unreachable!("a metavariable is a `Var`, not {other:?}"),
2790 }
2791}
2792
2793fn lam(param: &str, ty: Term, body: Term) -> Term {
2794 Term::Lambda {
2795 param: param.to_string(),
2796 param_type: Box::new(ty),
2797 body: Box::new(body),
2798 }
2799}
2800
2801fn pi(param: &str, ty: Term, body: Term) -> Term {
2802 Term::Pi {
2803 param: param.to_string(),
2804 param_type: Box::new(ty),
2805 body_type: Box::new(body),
2806 }
2807}
2808
2809fn eq_term(carrier: &Term, lhs: &Term, rhs: &Term) -> Term {
2810 apps(
2811 Term::Global("Eq".into()),
2812 &[carrier.clone(), lhs.clone(), rhs.clone()],
2813 )
2814}
2815
2816fn constructor_at(ctx: &Context, family_head: &Term, name: &str) -> Term {
2818 match family_head {
2819 Term::Const { levels, .. } if ctx.poly_declaration(name).is_some() => Term::Const {
2820 name: name.to_string(),
2821 levels: levels.clone(),
2822 },
2823 _ => Term::Global(name.to_string()),
2824 }
2825}
2826
2827#[derive(Clone, Copy)]
2830enum Side {
2831 Left,
2832 Right,
2833}
2834
2835fn substitutable(goal: &Goal, eq: &Equation) -> Option<Side> {
2836 let free = |t: &Term, other: &Term| match t {
2837 Term::Var(v) => {
2838 goal.hyps.iter().any(|(n, _)| n == v)
2839 && !crate::type_checker::occurs_free(other, v)
2840 && !crate::type_checker::occurs_free(&eq.carrier, v)
2841 }
2842 _ => false,
2843 };
2844 if free(&eq.rhs, &eq.lhs) {
2845 Some(Side::Right)
2846 } else if free(&eq.lhs, &eq.rhs) {
2847 Some(Side::Left)
2848 } else {
2849 None
2850 }
2851}
2852
2853fn metas(t: &Term, out: &mut BTreeSet<String>) {
2855 match t {
2856 Term::Var(v) if elaborate::is_meta(v) => {
2857 out.insert(v.clone());
2858 }
2859 Term::Var(_)
2860 | Term::Global(_)
2861 | Term::Const { .. }
2862 | Term::Sort(_)
2863 | Term::Lit(_)
2864 | Term::Hole => {}
2865 Term::App(a, b) => {
2866 metas(a, out);
2867 metas(b, out);
2868 }
2869 Term::Pi {
2870 param_type: a,
2871 body_type: b,
2872 ..
2873 }
2874 | Term::Lambda {
2875 param_type: a,
2876 body: b,
2877 ..
2878 } => {
2879 metas(a, out);
2880 metas(b, out);
2881 }
2882 Term::Let {
2883 ty, value, body, ..
2884 } => {
2885 metas(ty, out);
2886 metas(value, out);
2887 metas(body, out);
2888 }
2889 Term::Match {
2890 discriminant,
2891 motive,
2892 cases,
2893 } => {
2894 metas(discriminant, out);
2895 metas(motive, out);
2896 cases.iter().for_each(|c| metas(c, out));
2897 }
2898 Term::Fix { body, .. } => metas(body, out),
2899 Term::MutualFix { defs, .. } => defs.iter().for_each(|(_, b)| metas(b, out)),
2900 Term::Annotated { term, .. } => metas(term, out),
2901 }
2902}
2903
2904fn simplify(ctx: &Context, hyps: &[(String, Term)], t: &Term) -> Term {
2908 let go = |t: &Term| simplify(ctx, hyps, t);
2909 match t {
2910 Term::App(f, a) => match (go(f), go(a)) {
2911 (Term::Lambda { param, body, .. }, a) => go(&substitute(&body, ¶m, &a)),
2912 (f, a) => Term::App(Box::new(f), Box::new(a)),
2913 },
2914 Term::Pi {
2915 param,
2916 param_type,
2917 body_type,
2918 } => pi(param, go(param_type), go(body_type)),
2919 Term::Lambda {
2920 param,
2921 param_type,
2922 body,
2923 } => lam(param, go(param_type), go(body)),
2924 Term::Match {
2925 discriminant,
2926 motive,
2927 cases,
2928 } => {
2929 let discriminant = go(discriminant);
2930 let cases: Vec<Term> = cases.iter().map(go).collect();
2931 if let Some(reduced) = iota(ctx, hyps, &discriminant, &cases) {
2932 return go(&reduced);
2933 }
2934 Term::Match {
2935 discriminant: Box::new(discriminant),
2936 motive: Box::new(go(motive)),
2937 cases,
2938 }
2939 }
2940 Term::Annotated { term, .. } => go(term),
2941 _ => t.clone(),
2942 }
2943}
2944
2945fn iota(
2946 ctx: &Context,
2947 hyps: &[(String, Term)],
2948 discriminant: &Term,
2949 cases: &[Term],
2950) -> Option<Term> {
2951 let (head, args) = spine(discriminant);
2952 match head {
2953 Term::Global(c) | Term::Const { name: c, .. } if ctx.is_constructor(c) => {
2954 let ind = ctx.constructor_inductive(c)?.to_string();
2955 let constructors = ctx.get_constructors(&ind);
2956 if cases.len() != constructors.len() {
2957 return None;
2958 }
2959 let position = constructors.iter().position(|(n, _)| n == c)?;
2960 let np = ctx.inductive_num_params(&ind);
2961 Some(apps(cases[position].clone(), args.get(np..)?))
2962 }
2963 Term::Var(h) if cases.len() == 1 => {
2964 let (_, ty) = hyps.iter().rev().find(|(n, _)| n == h)?;
2965 let eq = equation(ctx, ty)?;
2966 def_eq(ctx, &eq.lhs, &eq.rhs).then(|| cases[0].clone())
2967 }
2968 _ => None,
2969 }
2970}
2971
2972fn typed(
2977 ctx: &Context,
2978 mctx: &mut MetaCtx,
2979 hyps: &[(String, Term)],
2980 term: &Term,
2981 expected: Option<&Term>,
2982) -> Result<(Term, Term), KernelError> {
2983 if !crate::type_checker::mentions_hole(term) {
2984 if let Ok(ty) = infer_type(ctx, term) {
2985 return Ok((term.clone(), ty));
2986 }
2987 }
2988 elaborate::elaborate_in(ctx, mctx, hyps, term, expected)
2989}
2990
2991struct Shown<'a>(&'a Term);
2995
2996impl fmt::Display for Shown<'_> {
2997 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2998 let locals = crate::type_checker::free_vars(self.0);
2999 let locals: Vec<&str> = locals.iter().map(String::as_str).collect();
3000 match crate::interface::print_term_in(self.0, &locals) {
3001 Ok(text) => f.write_str(&text),
3002 Err(_) => write!(f, "{}", self.0),
3003 }
3004 }
3005}
3006
3007fn refuse(tactic: &'static str, goal: &Goal, why: String) -> TacticError {
3008 TacticError::DoesNotApply {
3009 tactic,
3010 goal: goal.clone(),
3011 why,
3012 }
3013}
3014
3015fn apps(f: Term, args: &[Term]) -> Term {
3016 args.iter()
3017 .fold(f, |acc, a| Term::App(Box::new(acc), Box::new(a.clone())))
3018}
3019
3020fn spine(t: &Term) -> (&Term, Vec<Term>) {
3021 let mut args = Vec::new();
3022 let mut head = t;
3023 while let Term::App(f, a) = head {
3024 args.push((**a).clone());
3025 head = f;
3026 }
3027 args.reverse();
3028 (head, args)
3029}
3030
3031fn localize(term: &Term, hyps: &[(String, Term)]) -> Term {
3033 if hyps.is_empty() {
3034 return term.clone();
3035 }
3036 let names: BTreeSet<&str> = hyps.iter().map(|(n, _)| n.as_str()).collect();
3037 map_globals(term, &|n| {
3038 names.contains(n).then(|| Term::Var(n.to_string()))
3039 })
3040}
3041
3042fn map_globals(t: &Term, f: &dyn Fn(&str) -> Option<Term>) -> Term {
3043 let go = |t: &Term| Box::new(map_globals(t, f));
3044 match t {
3045 Term::Global(n) => f(n).unwrap_or_else(|| t.clone()),
3046 Term::Var(_) | Term::Const { .. } | Term::Sort(_) | Term::Lit(_) | Term::Hole => t.clone(),
3047 Term::App(a, b) => Term::App(go(a), go(b)),
3048 Term::Pi {
3049 param,
3050 param_type,
3051 body_type,
3052 } => Term::Pi {
3053 param: param.clone(),
3054 param_type: go(param_type),
3055 body_type: go(body_type),
3056 },
3057 Term::Lambda {
3058 param,
3059 param_type,
3060 body,
3061 } => Term::Lambda {
3062 param: param.clone(),
3063 param_type: go(param_type),
3064 body: go(body),
3065 },
3066 Term::Match {
3067 discriminant,
3068 motive,
3069 cases,
3070 } => Term::Match {
3071 discriminant: go(discriminant),
3072 motive: go(motive),
3073 cases: cases.iter().map(|c| map_globals(c, f)).collect(),
3074 },
3075 Term::Fix { name, body } => Term::Fix {
3076 name: name.clone(),
3077 body: go(body),
3078 },
3079 Term::MutualFix { defs, index } => Term::MutualFix {
3080 defs: defs
3081 .iter()
3082 .map(|(n, b)| (n.clone(), map_globals(b, f)))
3083 .collect(),
3084 index: *index,
3085 },
3086 Term::Let {
3087 name,
3088 ty,
3089 value,
3090 body,
3091 } => Term::Let {
3092 name: name.clone(),
3093 ty: go(ty),
3094 value: go(value),
3095 body: go(body),
3096 },
3097 Term::Annotated { annotation, term } => Term::Annotated {
3098 annotation: annotation.clone(),
3099 term: go(term),
3100 },
3101 }
3102}
3103
3104fn pi_arity(ctx: &Context, ty: &Term) -> usize {
3106 pi_telescope(ctx, ty, usize::MAX).len()
3107}
3108
3109fn pi_telescope(ctx: &Context, ty: &Term, limit: usize) -> Vec<(String, Term)> {
3111 let mut out = Vec::new();
3112 let mut scope = ctx.clone();
3113 let mut t = ty.clone();
3114 while out.len() < limit {
3115 match demanded_type_head(&scope, t, TypeHead::Function) {
3116 Term::Pi {
3117 param,
3118 param_type,
3119 body_type,
3120 } => {
3121 scope = scope.extend(¶m, (*param_type).clone());
3122 out.push((param, *param_type));
3123 t = *body_type;
3124 }
3125 _ => break,
3126 }
3127 }
3128 out
3129}
3130
3131struct Equation {
3133 head: Term,
3134 carrier: Term,
3135 lhs: Term,
3136 rhs: Term,
3137}
3138
3139impl Equation {
3140 fn refl(&self) -> Term {
3142 match &self.head {
3143 Term::Const { levels, .. } => Term::Const {
3144 name: "refl".into(),
3145 levels: levels.clone(),
3146 },
3147 _ => Term::Global("refl".into()),
3148 }
3149 }
3150}
3151
3152fn equation(ctx: &Context, ty: &Term) -> Option<Equation> {
3153 let read = |t: &Term| {
3154 let (head, args) = spine(t);
3155 match head {
3156 Term::Global(n) | Term::Const { name: n, .. } if n == "Eq" && args.len() == 3 => {
3157 Some(Equation {
3158 head: head.clone(),
3159 carrier: args[0].clone(),
3160 lhs: args[1].clone(),
3161 rhs: args[2].clone(),
3162 })
3163 }
3164 _ => None,
3165 }
3166 };
3167 read(ty).or_else(|| read(&demanded_type_head(ctx, ty.clone(), TypeHead::Inductive)))
3168}
3169
3170fn abstract_occurrences(t: &Term, from: &Term, x: &str) -> (Term, bool) {
3174 if alpha_equivalent(t, from) {
3175 return (Term::Var(x.to_string()), true);
3176 }
3177 let captured = |name: &str| crate::type_checker::occurs_free(from, name);
3178 let mut found = false;
3179 let mut go = |t: &Term| {
3180 let (r, f) = abstract_occurrences(t, from, x);
3181 found |= f;
3182 Box::new(r)
3183 };
3184 let r = match t {
3185 Term::App(a, b) => Term::App(go(a), go(b)),
3186 Term::Pi {
3187 param,
3188 param_type,
3189 body_type,
3190 } => {
3191 let pt = go(param_type);
3192 let bt = if captured(param) {
3193 body_type.clone()
3194 } else {
3195 go(body_type)
3196 };
3197 Term::Pi {
3198 param: param.clone(),
3199 param_type: pt,
3200 body_type: bt,
3201 }
3202 }
3203 Term::Lambda {
3204 param,
3205 param_type,
3206 body,
3207 } => {
3208 let pt = go(param_type);
3209 let b = if captured(param) {
3210 body.clone()
3211 } else {
3212 go(body)
3213 };
3214 Term::Lambda {
3215 param: param.clone(),
3216 param_type: pt,
3217 body: b,
3218 }
3219 }
3220 Term::Let {
3221 name,
3222 ty,
3223 value,
3224 body,
3225 } => {
3226 let (ty2, v2) = (go(ty), go(value));
3227 let b = if captured(name) {
3228 body.clone()
3229 } else {
3230 go(body)
3231 };
3232 Term::Let {
3233 name: name.clone(),
3234 ty: ty2,
3235 value: v2,
3236 body: b,
3237 }
3238 }
3239 Term::Match {
3240 discriminant,
3241 motive,
3242 cases,
3243 } => {
3244 let d = go(discriminant);
3245 let mo = go(motive);
3246 let cs = cases.iter().map(|c| *go(c)).collect();
3247 Term::Match {
3248 discriminant: d,
3249 motive: mo,
3250 cases: cs,
3251 }
3252 }
3253 Term::Fix { name, body } => {
3254 let b = if captured(name) {
3255 body.clone()
3256 } else {
3257 go(body)
3258 };
3259 Term::Fix {
3260 name: name.clone(),
3261 body: b,
3262 }
3263 }
3264 Term::Annotated { term, .. } => *go(term),
3265 _ => t.clone(),
3266 };
3267 (r, found)
3268}
3269
3270fn beta_normal(t: &Term) -> Term {
3272 match t {
3273 Term::App(f, a) => {
3274 let (f, a) = (beta_normal(f), beta_normal(a));
3275 match f {
3276 Term::Lambda { param, body, .. } => beta_normal(&substitute(&body, ¶m, &a)),
3277 f => Term::App(Box::new(f), Box::new(a)),
3278 }
3279 }
3280 Term::Pi {
3281 param,
3282 param_type,
3283 body_type,
3284 } => Term::Pi {
3285 param: param.clone(),
3286 param_type: Box::new(beta_normal(param_type)),
3287 body_type: Box::new(beta_normal(body_type)),
3288 },
3289 Term::Lambda {
3290 param,
3291 param_type,
3292 body,
3293 } => Term::Lambda {
3294 param: param.clone(),
3295 param_type: Box::new(beta_normal(param_type)),
3296 body: Box::new(beta_normal(body)),
3297 },
3298 Term::Annotated { term, .. } => beta_normal(term),
3299 _ => t.clone(),
3300 }
3301}
3302
3303fn fresh_name(base: &str, ctx: &Context, hyps: &[(String, Term)], terms: &[&Term]) -> String {
3306 fresh_name_avoiding(base, ctx, hyps, terms, &[])
3307}
3308
3309fn fresh_name_avoiding(
3310 base: &str,
3311 ctx: &Context,
3312 hyps: &[(String, Term)],
3313 terms: &[&Term],
3314 also: &[String],
3315) -> String {
3316 let base = if base.is_empty() || base == "_" {
3317 "x"
3318 } else {
3319 base
3320 };
3321 let clashes = |n: &str| {
3322 hyps.iter().any(|(h, _)| h == n)
3323 || also.iter().any(|a| a == n)
3324 || ctx.get_global(n).is_some()
3325 || terms.iter().any(|t| mentions_name(t, n))
3326 };
3327 if !clashes(base) {
3328 return base.to_string();
3329 }
3330 (1..)
3331 .map(|i| format!("{base}{i}"))
3332 .find(|n| !clashes(n))
3333 .expect("a fresh name exists")
3334}
3335
3336fn mentions_name(t: &Term, n: &str) -> bool {
3337 match t {
3338 Term::Var(v) | Term::Global(v) => v == n,
3339 Term::Const { name, .. } => name == n,
3340 Term::Sort(_) | Term::Lit(_) | Term::Hole => false,
3341 Term::App(a, b) => mentions_name(a, n) || mentions_name(b, n),
3342 Term::Pi {
3343 param,
3344 param_type,
3345 body_type,
3346 } => param == n || mentions_name(param_type, n) || mentions_name(body_type, n),
3347 Term::Lambda {
3348 param,
3349 param_type,
3350 body,
3351 } => param == n || mentions_name(param_type, n) || mentions_name(body, n),
3352 Term::Let {
3353 name,
3354 ty,
3355 value,
3356 body,
3357 } => name == n || mentions_name(ty, n) || mentions_name(value, n) || mentions_name(body, n),
3358 Term::Match {
3359 discriminant,
3360 motive,
3361 cases,
3362 } => {
3363 mentions_name(discriminant, n)
3364 || mentions_name(motive, n)
3365 || cases.iter().any(|c| mentions_name(c, n))
3366 }
3367 Term::Fix { name, body } => name == n || mentions_name(body, n),
3368 Term::MutualFix { defs, .. } => defs.iter().any(|(m, b)| m == n || mentions_name(b, n)),
3369 Term::Annotated { term, .. } => mentions_name(term, n),
3370 }
3371}
3372
3373mod congruence {
3377 use super::{apps, eq_term, lam, Goal};
3378 use crate::context::Context;
3379 use crate::term::Term;
3380 use crate::type_checker::{demanded_type_head, infer_type, occurs_free, TypeHead};
3381 use std::collections::HashMap;
3382
3383 #[derive(Clone)]
3385 enum Reason {
3386 Hypothesis { name: String, lhs: usize },
3388 Congruence { left: usize, right: usize },
3390 }
3391
3392 pub(super) struct Closure<'a> {
3393 ctx: &'a Context,
3394 avoid: Vec<String>,
3395 terms: Vec<Term>,
3396 types: Vec<Option<Term>>,
3397 index: HashMap<String, usize>,
3398 app: Vec<Option<(usize, usize)>>,
3400 parent: Vec<usize>,
3401 edge: Vec<Option<(usize, Reason)>>,
3403 pending: Vec<(usize, usize, Reason)>,
3404 }
3405
3406 impl<'a> Closure<'a> {
3407 pub(super) fn new(ctx: &'a Context, goal: &Goal) -> Closure<'a> {
3408 let mut avoid: Vec<String> = goal.hyps.iter().map(|(h, _)| h.clone()).collect();
3409 avoid.push("w".to_string());
3410 Closure {
3411 ctx,
3412 avoid,
3413 terms: Vec::new(),
3414 types: Vec::new(),
3415 index: HashMap::new(),
3416 app: Vec::new(),
3417 parent: Vec::new(),
3418 edge: Vec::new(),
3419 pending: Vec::new(),
3420 }
3421 }
3422
3423 pub(super) fn term(&mut self, t: &Term) -> usize {
3425 let key = format!("{t:?}");
3426 if let Some(&id) = self.index.get(&key) {
3427 return id;
3428 }
3429 let app = match t {
3430 Term::App(f, a) if !self.dependent(f) => {
3431 let (f, a) = (self.term(f), self.term(a));
3432 Some((f, a))
3433 }
3434 _ => None,
3435 };
3436 let id = self.terms.len();
3437 self.terms.push(t.clone());
3438 self.types.push(infer_type(self.ctx, t).ok());
3439 self.index.insert(key, id);
3440 self.app.push(app);
3441 self.parent.push(id);
3442 self.edge.push(None);
3443 id
3444 }
3445
3446 fn dependent(&self, f: &Term) -> bool {
3449 let Ok(ty) = infer_type(self.ctx, f) else {
3450 return true;
3451 };
3452 match demanded_type_head(self.ctx, ty, TypeHead::Function) {
3453 Term::Pi {
3454 param, body_type, ..
3455 } => occurs_free(&body_type, ¶m),
3456 _ => true,
3457 }
3458 }
3459
3460 pub(super) fn assert_hypothesis(&mut self, name: &str, lhs: &Term, rhs: &Term) {
3461 let (l, r) = (self.term(lhs), self.term(rhs));
3462 self.pending.push((
3463 l,
3464 r,
3465 Reason::Hypothesis {
3466 name: name.to_string(),
3467 lhs: l,
3468 },
3469 ));
3470 }
3471
3472 fn find(&mut self, mut x: usize) -> usize {
3473 while self.parent[x] != x {
3474 self.parent[x] = self.parent[self.parent[x]];
3475 x = self.parent[x];
3476 }
3477 x
3478 }
3479
3480 pub(super) fn saturate(&mut self) {
3483 loop {
3484 while let Some((a, b, why)) = self.pending.pop() {
3485 let (ra, rb) = (self.find(a), self.find(b));
3486 if ra == rb {
3487 continue;
3488 }
3489 self.parent[ra] = rb;
3490 self.reroot(a);
3491 self.edge[a] = Some((b, why));
3492 }
3493 let mut signatures: HashMap<(usize, usize), usize> = HashMap::new();
3496 for x in 0..self.terms.len() {
3497 let Some((f, a)) = self.app[x] else { continue };
3498 let key = (self.find(f), self.find(a));
3499 match signatures.get(&key) {
3500 Some(&y) if self.find(x) != self.find(y) => {
3501 self.pending
3502 .push((y, x, Reason::Congruence { left: y, right: x }));
3503 }
3504 Some(_) => {}
3505 None => {
3506 signatures.insert(key, x);
3507 }
3508 }
3509 }
3510 if self.pending.is_empty() {
3511 return;
3512 }
3513 }
3514 }
3515
3516 fn reroot(&mut self, x: usize) {
3518 let mut path = Vec::new();
3519 let mut at = x;
3520 while let Some((next, why)) = self.edge[at].clone() {
3521 path.push((at, next, why));
3522 at = next;
3523 }
3524 self.edge[x] = None;
3525 for (from, to, why) in path {
3526 self.edge[to] = Some((from, why));
3527 }
3528 }
3529
3530 pub(super) fn prove(&mut self, a: usize, b: usize) -> Option<Term> {
3532 if self.find(a) != self.find(b) {
3533 return None;
3534 }
3535 self.explain(a, b)
3536 }
3537
3538 fn explain(&self, a: usize, b: usize) -> Option<Term> {
3540 let up = |mut x: usize| {
3541 let mut path = vec![x];
3542 while let Some((next, _)) = &self.edge[x] {
3543 x = *next;
3544 path.push(x);
3545 }
3546 path
3547 };
3548 let (pa, pb) = (up(a), up(b));
3549 let meet = *pa.iter().find(|x| pb.contains(x))?;
3550 let mut proof = self.refl(a)?;
3551 let mut at = a;
3552 while at != meet {
3553 let (next, why) = self.edge[at].clone()?;
3554 let step = self.step(at, next, &why)?;
3555 proof = self.trans(at, a, proof, step)?;
3556 at = next;
3557 }
3558 let back: Vec<usize> = pb.iter().take_while(|x| **x != meet).copied().collect();
3559 for &x in back.iter().rev() {
3560 let (next, why) = self.edge[x].clone()?;
3561 let step = self.step(next, x, &why)?;
3562 proof = self.trans(next, a, proof, step)?;
3563 }
3564 Some(proof)
3565 }
3566
3567 fn step(&self, from: usize, to: usize, why: &Reason) -> Option<Term> {
3569 match why {
3570 Reason::Hypothesis { name, lhs } => {
3571 let h = Term::Var(name.clone());
3572 if *lhs == from {
3573 Some(h)
3574 } else {
3575 self.symm(to, h)
3576 }
3577 }
3578 Reason::Congruence { left, right } => {
3579 let (l, r) = if *left == from {
3580 (*left, *right)
3581 } else {
3582 (*right, *left)
3583 };
3584 let ((fl, al), (fr, ar)) = (self.app[l]?, self.app[r]?);
3585 let args = self.explain(al, ar)?;
3587 let by_arg = self.congr_arg(fl, al, args)?;
3588 if fl == fr {
3589 return Some(by_arg);
3590 }
3591 let funs = self.explain(fl, fr)?;
3592 let by_fun = self.congr_fun(fl, ar, funs)?;
3593 let mid = Term::App(
3594 Box::new(self.terms[fl].clone()),
3595 Box::new(self.terms[ar].clone()),
3596 );
3597 Some(self.trans_terms(
3598 &self.types[l].clone()?,
3599 &self.terms[l],
3600 &mid,
3601 by_arg,
3602 by_fun,
3603 ))
3604 }
3605 }
3606 }
3607
3608 fn refl(&self, x: usize) -> Option<Term> {
3609 Some(apps(
3610 Term::Global("refl".into()),
3611 &[self.types[x].clone()?, self.terms[x].clone()],
3612 ))
3613 }
3614
3615 fn fresh(&self, base: &str) -> String {
3616 let taken = |n: &str| {
3617 self.avoid.iter().any(|a| a == n)
3618 || self.terms.iter().any(|t| super::mentions_name(t, n))
3619 };
3620 if !taken(base) {
3621 return base.to_string();
3622 }
3623 (1..)
3624 .map(|i| format!("{base}{i}"))
3625 .find(|n| !taken(n))
3626 .expect("a fresh name")
3627 }
3628
3629 fn symm(&self, x: usize, p: Term) -> Option<Term> {
3631 let (t, xs) = (self.types[x].clone()?, self.terms[x].clone());
3632 let z = self.fresh("z");
3633 Some(Term::Match {
3634 discriminant: Box::new(p),
3635 motive: Box::new(lam(
3636 &z,
3637 t.clone(),
3638 lam(
3639 "_",
3640 eq_term(&t, &xs, &Term::Var(z.clone())),
3641 eq_term(&t, &Term::Var(z.clone()), &xs),
3642 ),
3643 )),
3644 cases: vec![apps(Term::Global("refl".into()), &[t, xs])],
3645 })
3646 }
3647
3648 fn trans(&self, y: usize, a: usize, p: Term, q: Term) -> Option<Term> {
3650 let t = self.types[a].clone()?;
3651 Some(self.trans_terms(&t, &self.terms[a], &self.terms[y], p, q))
3652 }
3653
3654 fn trans_terms(&self, t: &Term, a: &Term, y: &Term, p: Term, q: Term) -> Term {
3656 let w = self.fresh("w");
3657 Term::Match {
3658 discriminant: Box::new(q),
3659 motive: Box::new(lam(
3660 &w,
3661 t.clone(),
3662 lam(
3663 "_",
3664 eq_term(t, y, &Term::Var(w.clone())),
3665 eq_term(t, a, &Term::Var(w.clone())),
3666 ),
3667 )),
3668 cases: vec![p],
3669 }
3670 }
3671
3672 fn congr_arg(&self, f: usize, a: usize, p: Term) -> Option<Term> {
3674 let fa = Term::App(
3675 Box::new(self.terms[f].clone()),
3676 Box::new(self.terms[a].clone()),
3677 );
3678 let ta = self.types[a].clone()?;
3679 let tb = infer_type(self.ctx, &fa).ok()?;
3680 let w = self.fresh("w");
3681 let at_w = Term::App(
3682 Box::new(self.terms[f].clone()),
3683 Box::new(Term::Var(w.clone())),
3684 );
3685 Some(Term::Match {
3686 discriminant: Box::new(p),
3687 motive: Box::new(lam(
3688 &w,
3689 ta.clone(),
3690 lam(
3691 "_",
3692 eq_term(&ta, &self.terms[a], &Term::Var(w.clone())),
3693 eq_term(&tb, &fa, &at_w),
3694 ),
3695 )),
3696 cases: vec![apps(Term::Global("refl".into()), &[tb, fa.clone()])],
3697 })
3698 }
3699
3700 fn congr_fun(&self, f: usize, b: usize, p: Term) -> Option<Term> {
3702 let tf = self.types[f].clone()?;
3703 let fb = Term::App(
3704 Box::new(self.terms[f].clone()),
3705 Box::new(self.terms[b].clone()),
3706 );
3707 let tb = infer_type(self.ctx, &fb).ok()?;
3708 let w = self.fresh("w");
3709 let w_b = Term::App(
3710 Box::new(Term::Var(w.clone())),
3711 Box::new(self.terms[b].clone()),
3712 );
3713 Some(Term::Match {
3714 discriminant: Box::new(p),
3715 motive: Box::new(lam(
3716 &w,
3717 tf.clone(),
3718 lam(
3719 "_",
3720 eq_term(&tf, &self.terms[f], &Term::Var(w.clone())),
3721 eq_term(&tb, &fb, &w_b),
3722 ),
3723 )),
3724 cases: vec![apps(Term::Global("refl".into()), &[tb, fb.clone()])],
3725 })
3726 }
3727 }
3728}