1use std::{borrow::Cow, hint::unreachable_unchecked, sync::LazyLock};
2
3use ftml_ontology::terms::{
4 ApplicationTerm, Argument, ArgumentMode, BindingTerm, BoundArgument, ComponentVar,
5 MaybeSequence, Term, Variable, eq::Alpha, patterns::Pattern,
6};
7use ftml_solver_trace::{CheckLogCow, Displayable, RefCheckLog, SizedSolverRule};
8use ftml_uris::{DocumentUri, Id, ModuleUri, SymbolUri};
9use smallvec::SmallVec;
10
11use crate::{
12 CheckRef,
13 impls::solving::{is_solvable_id, is_solvable_var},
14 rules::{RuleSet, implicits::ImplicitExtApp},
15 split::SplitStrategy,
16};
17
18macro_rules! uri {
19 ($( $name:ident $( : $t:ty := $l:literal)? $( = $lb:literal )? ),* $(,)?) => {
20 $(
21 uri!{@go
22 $name $( : $t := $l )? $( = $lb )?
23 }
24 )*
25 };
26 (@go $name:ident = $l:literal) => {
27 pub static $name: LazyLock<SymbolUri> = LazyLock::new(||
28 URI.clone() | $l.parse::<ftml_uris::UriName>().expect("Is a valid URI")
29 );
30 };
31 (@go $name:ident : $t:ty := $l:literal) => {
32 pub static $name: LazyLock<$t> = LazyLock::new(||
33 $l.parse().expect("Is a valid URI")
34 );
35 }
36}
37
38pub static NAMESPACE: &str = "http://mathhub.info?a=FTML/meta";
39uri! {
40 DOC_URI:DocumentUri := "http://mathhub.info?a=FTML/meta&d=Judgments&l=en",
41 URI:ModuleUri := "http://mathhub.info?a=FTML/meta&m=Judgments",
42
43 INH = "inhabitable",
44 UNIV = "universe",
45 HAS_PROOF = "has proof",
46 HAS_TYPE = "has type",
47 SUBTYPE = "subtype of",
48 SIMPLIFY = "simplifies to",
49 EQUAL = "equal to",
50 BINDS = "binds",
51 PROOF_BARRIER = "proof barrier"
52}
53
54pub trait GenericJudgment: SizedSolverRule {
55 fn parse<Split: SplitStrategy>(params: &[Term], rules: &mut RuleSet<Split>) {
56 parse(params, rules);
57 }
58 fn vars(&self) -> &[Id];
59 fn premises(&self) -> &[Premise];
60}
61pub fn parse<Split: SplitStrategy>(params: &[Term], rules: &mut RuleSet<Split>) {
62 let Some(Term::Application(concl)) = params.last() else {
69 return;
70 };
71 let mut vars = Vec::new();
72 let mut premises = Vec::with_capacity(params.len() - 1);
73 for p in ¶ms[..params.len() - 1] {
74 if let Some(p) = Premise::parse(p, &mut vars) {
75 premises.push(p);
76 } else {
77 tracing::debug!("Invalid premise: {:?}", p.debug_short());
78 return;
79 }
80 }
81
82 if let [Argument::Simple(t)] = &*concl.arguments {
83 if concl.head.is(&*INH) {
84 Pattern::from_with_vars(t, true, &mut vars);
85 tracing::debug!("New dynamic inhabitability rule: {:?}", t.debug_short());
86 rules.push_inhabitable(Box::new(GenericInhabitable {
87 vars: vars.into_boxed_slice(),
88 premises: premises.into_boxed_slice(),
89 concl: t.clone(),
90 }));
91 } else if concl.head.is(&*UNIV) {
92 Pattern::from_with_vars(t, true, &mut vars);
93 tracing::debug!("New dynamic universe rule: {:?}", t.debug_short());
94 rules.push_universe(Box::new(GenericUniverse {
95 vars: vars.into_boxed_slice(),
96 premises: premises.into_boxed_slice(),
97 concl: t.clone(),
98 }));
99 } else if concl.head.is(&*HAS_PROOF) {
100 Pattern::from_with_vars(t, true, &mut vars);
101 tracing::debug!("New dynamic provability rule: {:?}", t.debug_short());
102 rules.push_proof(Box::new(GenericProof {
103 vars: vars.into_boxed_slice(),
104 premises: premises.into_boxed_slice(),
105 concl: t.clone(),
106 }));
107 }
108 } else if let [Argument::Simple(a), Argument::Simple(b)] = &*concl.arguments {
109 if concl.head.is(&*HAS_TYPE) {
110 Pattern::from_with_vars(a, true, &mut vars);
111 Pattern::from_with_vars(b, true, &mut vars);
112 tracing::debug!(
113 "New dynamic typing rule: {:?} : {:?}",
114 a.debug_short(),
115 b.debug_short()
116 );
117 rules.push_checking(Box::new(GenericTyping {
118 vars: vars.into_boxed_slice(),
119 premises: premises.into_boxed_slice(),
120 concl: (a.clone(), b.clone()),
121 }));
122 } else if concl.head.is(&*SUBTYPE) {
123 Pattern::from_with_vars(a, true, &mut vars);
124 Pattern::from_with_vars(b, true, &mut vars);
125 tracing::debug!(
126 "New dynamic subtyping rule: {:?} <: {:?}",
127 a.debug_short(),
128 b.debug_short()
129 );
130 rules.push_subtyping(Box::new(GenericSubtyping {
131 vars: vars.into_boxed_slice(),
132 premises: premises.into_boxed_slice(),
133 concl: (a.clone(), b.clone()),
134 }));
135 } else if concl.head.is(&*SIMPLIFY) {
136 Pattern::from_with_vars(a, true, &mut vars);
137 Pattern::from_with_vars(b, true, &mut vars);
138 tracing::debug!(
139 "New dynamic simplification rule: {:?} --> {:?}",
140 a.debug_short(),
141 b.debug_short()
142 );
143 rules.push_simplification(Box::new(GenericSimplification {
144 vars: vars.into_boxed_slice(),
145 premises: premises.into_boxed_slice(),
146 concl: (a.clone(), b.clone()),
147 }));
148 } else if concl.head.is(&*EQUAL) {
149 Pattern::from_with_vars(a, true, &mut vars);
150 Pattern::from_with_vars(b, true, &mut vars);
151 tracing::debug!(
152 "New dynamic equality rule: {:?} == {:?}",
153 a.debug_short(),
154 b.debug_short()
155 );
156 rules.push_equality(Box::new(GenericEquality {
157 vars: vars.into_boxed_slice(),
158 premises: premises.into_boxed_slice(),
159 concl: (a.clone(), b.clone()),
160 }));
161 } else if concl.head.is(&*BINDS)
162 && let Term::Var { variable, .. } = a
163 {
164 let b = undo_implicits(b).unwrap_or_else(|| b.clone());
165 Pattern::from_with_vars(&b, true, &mut vars);
166 tracing::debug!("New dynamic preparation rule: {:?}", b.debug_short());
167 rules.push_preparation(Box::new(GenericBindPrep {
168 vars: vars.into_boxed_slice(),
169 premises: premises.into_boxed_slice(),
170 concl: (variable.clone(), b),
171 }));
172 }
173 }
174}
175
176fn undo_implicits(term: &Term) -> Option<Term> {
177 if let Some((head, _)) = term.unapply_implicits(false) {
178 Some(head.clone())
179 } else if let Term::Application(app) = term
180 && let Some((head, _)) = app.head.unapply_implicits(false)
181 {
182 Some(Term::Application(ApplicationTerm::new(
183 head.clone(),
184 app.arguments.clone(),
185 app.presentation.clone(),
186 )))
187 } else if let Term::Bound(app) = term
188 && let Some((head, _)) = app.head.unapply_implicits(false)
189 {
190 Some(Term::Bound(BindingTerm::new(
191 head.clone(),
192 app.arguments.clone(),
193 app.presentation.clone(),
194 )))
195 } else {
196 None
197 }
198}
199
200macro_rules! judg {
201 ($($name:ident($concl:ty $(>$prec:literal)?):$rl:ident { $($impl:tt)* } => $slf:ident{$($disp:tt)*} ),*$(,)?) => {
202 $(
203 #[derive(Clone,Debug,PartialEq,Eq)]
204 struct $name {
205 vars: Box<[Id]>,
206 premises: Box<[Premise]>,
207 concl: $concl,
208 }
209 impl SizedSolverRule for $name {
210 #[allow(unused_variables)]
211 fn display(&self) -> Vec<ftml_solver_trace::Displayable> {
212 let $slf = self;
213 $($disp)*
214 }
215 $(
216 fn priority(&self) -> isize {
217 $prec
218 }
219 )?
220 }
221 impl GenericJudgment for $name {
222 fn vars(&self) -> &[Id] { &self.vars}
223 fn premises(&self) -> &[Premise] {&self.premises}
224 }
225 impl<Split:SplitStrategy> crate::rules::$rl<Split> for $name {
226 $($impl)*
227 }
228 )*
229 };
230}
231judg! {
232 GenericInhabitable(Term): InhabitableRule {
233 fn applicable(&self, term: &Term) -> bool {
234 Pattern::r#match(term, &self.concl, &self.vars, true).is_some()
235 }
236 fn apply<'t>(&self, mut checker: CheckRef<'t, '_, Split>, term: &'t Term) -> Option<bool> {
237 let conc = Pattern::r#match(term,&self.concl,&self.vars,true)?;
238 let mut conc = self.vars.iter().cloned().zip(conc.into_iter().map(|o| o.map(Cow::into_owned))).collect::<Vec<_>>();
239 for p in &self.premises {
240 if p.check(&mut conc, &mut checker) != Some(true) { return None;}
241 }
242 if conc.iter().any(|(_,o)| o.is_none()) {None} else {
243 Some(true)
244 }
245 }
246 } => slf { ftml_solver_trace::trace!("Dynamic Rule") },
247
248 GenericUniverse(Term): UniverseRule {
249 fn applicable(&self, term: &Term) -> bool {
250 Pattern::r#match(term, &self.concl, &self.vars, true).is_some()
251 }
252 fn apply<'t>(&self, mut checker: CheckRef<'t, '_, Split>, term: &'t Term) -> Option<bool> {
253 let conc = Pattern::r#match(term,&self.concl,&self.vars,true)?;
254 let mut conc = self.vars.iter().cloned().zip(conc.into_iter().map(|o| o.map(Cow::into_owned))).collect::<Vec<_>>();
255 for p in &self.premises {
256 if p.check(&mut conc, &mut checker) != Some(true) { return None;}
257 }
258 if conc.iter().any(|(_,o)| o.is_none()) {None} else {
259 Some(true)
260 }
261 }
262 } => slf { ftml_solver_trace::trace!("Dynamic Rule") },
263
264 GenericProof(Term): ProofRule {
265 fn applicable(&self, term: &Term) -> bool {
266 Pattern::r#match(term, &self.concl, &self.vars, true).is_some()
267 }
268 fn prove<'t>(&self, mut checker: CheckRef<'t, '_, Split>, goal: &'t Term) -> Option<Term> {
269 let conc = Pattern::r#match(goal,&self.concl,&self.vars,true)?;
270 let mut conc = self.vars.iter().cloned().zip(conc.into_iter().map(|o| o.map(Cow::into_owned))).collect::<Vec<_>>();
271 for p in &self.premises {
272 if p.check(&mut conc, &mut checker) != Some(true) { return None;}
273 }
274 if conc.iter().any(|(_,o)| o.is_none()) {None} else {
275 Some(ftml_uris::metatheory::AUTO_PROVE.clone().into())
276 }
277 }
278 } => slf { ftml_solver_trace::trace!("Dynamic Rule") },
279
280 GenericBindPrep((Variable,Term) > 100_000): PreparationRule {
281 fn applicable(&self, checker: &CheckRef<'_, '_, Split>, t: &Term) -> bool {
282 let Term::Bound(b) = t else {
283 return false;
285 };
286 let Some(head) = checker.get_head(t) else {
287 return false;
288 };
289 let spec = head.as_ref().either(|s| &s.data.arity, |v| &v.data.arity);
290 if spec.num() as usize != b.arguments.len() {
291 return false;
293 }
294 spec.iter()
295 .zip(b.arguments.iter())
296 .any(|(a, b)| match (a, b) {
297 (ArgumentMode::BoundVariable, BoundArgument::Simple(t)) | (
298 ArgumentMode::BoundVariableSequence,
299 BoundArgument::Sequence(MaybeSequence::One(t)),
300 ) => {
301 Pattern::r#match(t, &self.concl.1, &self.vars, true).is_some()
302 },
303 (
304 ArgumentMode::BoundVariableSequence,
305 BoundArgument::Sequence(MaybeSequence::Seq(ts)),
306 ) => ts.iter().any(|t| {
307 Pattern::r#match(t, &self.concl.1, &self.vars, true).is_some()
308 }),
309 _ => false,
310 })
311 }
312 fn apply(
313 &self,
314 checker: &mut CheckRef<'_, '_, Split>,
315 t: Term,
316 path: Option<(&mut smallvec::SmallVec<u8, 16>, usize)>,
317 ) -> std::ops::ControlFlow<Term, Term> {
318 let Some(head) = checker.get_head(&t) else {
319 return std::ops::ControlFlow::Continue(t);
320 };
321 let Term::Bound(b) = t else {
322 unreachable!("wut");
323 };
324 let spec = head.as_ref().either(|s| &s.data.arity, |v| &v.data.arity);
325
326 let nargs = spec
327 .iter()
328 .zip(b.arguments.clone())
329 .map(|(m, a)| match (m, a) {
330 (ArgumentMode::BoundVariable, BoundArgument::Simple(t)) => {
331 if Pattern::r#match(&t, &self.concl.1, &self.vars, true).is_some() {
332 let var= unsafe { self.get_var(&t) };
334 BoundArgument::Bound(ComponentVar {
335 var,
336 tp: None,df: None,
338 })
339 } else {
351 BoundArgument::Simple(t)
352 }
353 }
354 (
355 ArgumentMode::BoundVariableSequence,
356 BoundArgument::Sequence(MaybeSequence::Seq(s)),
357 ) => {
358 let mut works = true;
359 let mut ns = s
361 .into_iter()
362 .flat_map(|t| {
363 if Pattern::r#match(&t, &self.concl.1, &self.vars, true).is_some() {
364 let v = unsafe { self.get_var(&t) };
366 vec![v.into()]
370 } else {
377 works = false;
378 vec![t]
379 }
380 })
381 .collect::<Vec<_>>();
382 if works {
383 BoundArgument::BoundSeq(if ns.len() == 1 && ns[0].is_sequence() {
384 let Some(Term::Var { variable, .. }) = ns.pop() else {
385 unsafe { unreachable_unchecked() }
387 };
388 MaybeSequence::One(ComponentVar {
389 var: variable,
390 tp: None,df: None,
392 })
393 }else {
394 MaybeSequence::Seq(
395 ns.into_iter()
396 .map(|t|{let Term::Var { variable, .. } = t else {
399 unsafe { unreachable_unchecked() }
401 };
402 ComponentVar {
403 var: variable,
404 tp: None,df: None,
406 }
407 })
408 .collect(),
409 )
410 })
411 } else {
412 BoundArgument::Sequence(MaybeSequence::Seq(ns.into_boxed_slice()))
413 }
414 }
415 (m, a) => {
416 a
418 }
419 })
420 .collect::<Vec<_>>()
421 .into_boxed_slice();
422 std::ops::ControlFlow::Continue(Term::Bound(BindingTerm::new(
423 b.head.clone(),
424 nargs,
425 b.presentation.clone(),
426 )))
427 }
428 fn applicable_revert(&self, _: &CheckRef<'_, '_, Split>, _: &Term) -> bool {
429 false
430 }
431 fn revert(&self, _: &CheckRef<'_, '_, Split>, t: Term) -> std::ops::ControlFlow<Term, Term> {
432 std::ops::ControlFlow::Continue(t)
433 }
434
435 } => slf { ftml_solver_trace::trace!("Dynamic Rule") },
436
437 GenericTyping((Term,Term)): CheckingRule {
438 fn applicable(&self, _: &CheckRef<'_, '_, Split>, term: &Term, tp: &Term) -> bool {
439 let (a,b) = &self.concl;
440 Pattern::r#match(term, a, &self.vars, true).is_some() &&
441 Pattern::r#match(tp, b, &self.vars, true).is_some()
442 }
443 fn apply<'t>(
444 &self,
445 mut checker: CheckRef<'t, '_, Split>,
446 term: &'t Term,
447 tp: &'t Term,
448 ) -> Option<bool> {
449 let (a,b) = &self.concl;
450 let mut vars: Vec<Option<Cow<'t, _>>> = vec![None; self.vars.len()];
451 Pattern::match_i(term,a,&self.vars,true,&mut vars,&mut Alpha::new())?;
452 Pattern::match_i(tp,b,&self.vars,true,&mut vars,&mut Alpha::new())?;
453 let mut conc = self.vars.iter().cloned().zip(vars.into_iter().map(|o| o.map(Cow::into_owned))).collect::<Vec<_>>();
454 for p in &self.premises {
455 if p.check(&mut conc, &mut checker) != Some(true) { return None;}
456 }
457 if conc.iter().any(|(_,o)| o.is_none()) {None} else {
458 Some(true)
459 }
460 }
461 } => slf { ftml_solver_trace::trace!("Dynamic Rule") },
462
463 GenericSubtyping((Term,Term)): SubtypeRule {
464 fn applicable(&self, _: &CheckRef<'_, '_, Split>, sub: &Term, sup: &Term) -> bool {
465 let (a,b) = &self.concl;
466 Pattern::r#match(sub, a, &self.vars, true).is_some() &&
467 Pattern::r#match(sup, b, &self.vars, true).is_some()
468 }
469 fn apply<'t>(
470 &self,
471 mut checker: CheckRef<'t, '_, Split>,
472 sub: &'t Term,
473 sup: &'t Term,
474 ) -> Option<bool> {
475 let (a,b) = &self.concl;
476 let mut vars: Vec<Option<Cow<'t, _>>> = vec![None; self.vars.len()];
477 Pattern::match_i(sub,a,&self.vars,true,&mut vars,&mut Alpha::new())?;
478 Pattern::match_i(sup,b,&self.vars,true,&mut vars,&mut Alpha::new())?;
479 let mut conc = self.vars.iter().cloned().zip(vars.into_iter().map(|o| o.map(Cow::into_owned))).collect::<Vec<_>>();
480 for p in &self.premises {
481 if p.check(&mut conc, &mut checker) != Some(true) { return None;}
482 }
483 if conc.iter().any(|(_,o)| o.is_none()) {None} else {
484 Some(true)
485 }
486 }
487 } => slf { ftml_solver_trace::trace!("Dynamic Rule") },
488
489 GenericEquality((Term,Term)): EqualityRule {
490 fn applicable(&self, lhs: &Term, rhs: &Term) -> bool {
491 let (a,b) = &self.concl;
492 Pattern::r#match(lhs, a, &self.vars, true).is_some() &&
493 Pattern::r#match(rhs, b, &self.vars, true).is_some()
494 }
495 fn apply<'t>(
496 &self,
497 mut checker: CheckRef<'t, '_, Split>,
498 lhs: &'t Term,
499 rhs: &'t Term,
500 ) -> Option<bool> {
501 let (a,b) = &self.concl;
502 let mut vars: Vec<Option<Cow<'t, _>>> = vec![None; self.vars.len()];
503 Pattern::match_i(lhs,a,&self.vars,true,&mut vars,&mut Alpha::new())?;
504 Pattern::match_i(rhs,b,&self.vars,true,&mut vars,&mut Alpha::new())?;
505 let mut conc = self.vars.iter().cloned().zip(vars.into_iter().map(|o| o.map(Cow::into_owned))).collect::<Vec<_>>();
506 for p in &self.premises {
507 if p.check(&mut conc, &mut checker) != Some(true) { return None;}
508 }
509 if conc.iter().any(|(_,o)| o.is_none()) {None} else {
510 Some(true)
511 }
512 }
513 } => slf { ftml_solver_trace::trace!("Dynamic Rule") },
514
515 GenericSimplification((Term,Term)): SimplificationRule {
516 fn applicable(&self, term: &Term) -> bool {
517 Pattern::r#match(term, &self.concl.0, &self.vars, true).is_some()
518 }
519 fn apply<'t>(
520 &self,
521 mut checker: CheckRef<'t, '_, Split>,
522 term: &'t Term,
523 ) -> Result<Term, Option<ftml_ontology::terms::termpaths::TermPath>> {
524 let (a,b) = &self.concl;
525 let mut vars: Vec<Option<Cow<'t, _>>> = vec![None; self.vars.len()];
526 Pattern::match_i(term,a,&self.vars,true,&mut vars,&mut Alpha::new()).ok_or(None)?;
527 let mut conc = self.vars.iter().cloned().zip(vars.into_iter().map(|o| o.map(Cow::into_owned))).collect::<Vec<_>>();
529 for (i,p) in self.premises.iter().enumerate() {
530 checker.comment(format!("Premise {}",i+1));
531 if p.check(&mut conc, &mut checker) != Some(true) { return Err(None);}
532 }
533 if conc.iter().any(|(v,o)| !is_solvable_id(v) && o.is_none()) {return Err(None)}
534 let subst = conc.iter().filter_map(|(v,o)| if is_solvable_id(v) && o.is_none() {
535 Some((v.as_ref(),Cow::Owned(checker.new_solvable())))
536 } else {o.as_ref().map(|t| (v.as_ref(),Cow::Borrowed(t)))}).collect::<Vec<_>>();
537 let ret = b.clone() / subst.as_slice();
538 checker.add_msg(CheckLogCow::Borrowed(RefCheckLog::Msg(vec!["Result: ".into(),ret.clone().into()], ftml_solver_trace::MessageLevel::Comment)));
539 Ok(ret)
541 }
542 } => slf {
543 let mut ret = vec!["Dynamic rule: ".into()];
544 if !slf.premises.is_empty() {
545 ret.push("[ ".into());
546 let mut first = true;
547 for p in &slf.premises {
548 if !first {
549 ret.push(", ".into());
550 }
551 first = false;
552 p.disps(&mut ret);
553 }
554 ret.push(" ] ".into());
555 }
556 ret.extend(["⊢ ".into(),slf.concl.0.clone().into()," ==> ".into(),slf.concl.1.clone().into()]);
557 ret
558 },
559}
560
561impl GenericBindPrep {
562 unsafe fn get_var(&self, t: &Term) -> Variable {
564 let Some(mut matches) = Pattern::r#match(t, &self.concl.1, &self.vars, true) else {
565 unreachable!("bug!");
566 };
567 self.vars
568 .iter()
569 .position(|v| v.as_ref() == self.concl.0.name())
570 .map_or_else(
571 || self.concl.0.clone(),
572 |i| {
573 if let Some(Term::Var { variable, .. }) = matches.remove(i).map(Cow::into_owned)
574 {
575 variable
576 } else {
577 self.concl.0.clone()
578 }
579 },
580 )
581 }
582}
583
584#[derive(Clone, Debug, PartialEq, Eq)]
585pub enum Premise {
586 Inhabitable(Term),
587 Universe(Term),
588 HasProof(Term),
589 HasType(Term, Term),
590 Subtype(Term, Term),
591 Equal(Term, Term),
592}
593impl Premise {
594 fn disps(&self, target: &mut Vec<Displayable>) {
595 match self {
596 Self::Inhabitable(t) => target.extend(["⊢ INH ".into(), t.clone().into()]),
597 Self::Universe(t) => target.extend(["⊢ UNIV ".into(), t.clone().into()]),
598 Self::HasProof(t) => target.extend(["⊢ PROOF ".into(), t.clone().into()]),
599 Self::HasType(t1, t2) => target.extend([
600 "⊢".into(),
601 t1.clone().into(),
602 " : ".into(),
603 t2.clone().into(),
604 ]),
605 Self::Subtype(t1, t2) => target.extend([
606 "⊢".into(),
607 t1.clone().into(),
608 " <: ".into(),
609 t2.clone().into(),
610 ]),
611 Self::Equal(t1, t2) => target.extend([
612 "⊢".into(),
613 t1.clone().into(),
614 " == ".into(),
615 t2.clone().into(),
616 ]),
617 }
618 }
619 fn parse(t: &Term, vars: &mut Vec<Id>) -> Option<Self> {
620 let Term::Application(p) = t else { return None };
621 if let [Argument::Simple(t)] = &*p.arguments {
622 if p.head.is(&*INH) {
623 Pattern::from_with_vars(t, true, vars);
624 Some(Self::Inhabitable(t.clone()))
625 } else if p.head.is(&*UNIV) {
626 Pattern::from_with_vars(t, true, vars);
627 Some(Self::Universe(t.clone()))
628 } else if p.head.is(&*HAS_PROOF) {
629 Pattern::from_with_vars(t, true, vars);
630 Some(Self::HasProof(t.clone()))
631 } else {
632 None
633 }
634 } else if let [Argument::Simple(a), Argument::Simple(b)] = &*p.arguments {
635 if p.head.is(&*HAS_TYPE) {
636 Pattern::from_with_vars(a, true, vars);
637 Pattern::from_with_vars(b, true, vars);
638 Some(Self::HasType(a.clone(), b.clone()))
639 } else if p.head.is(&*SUBTYPE) {
640 Pattern::from_with_vars(a, true, vars);
641 Pattern::from_with_vars(b, true, vars);
642 Some(Self::Subtype(a.clone(), b.clone()))
643 } else if p.head.is(&*EQUAL) {
644 Pattern::from_with_vars(a, true, vars);
645 Pattern::from_with_vars(b, true, vars);
646 Some(Self::Equal(a.clone(), b.clone()))
647 } else {
648 None
649 }
650 } else {
651 None
652 }
653 }
654 pub fn check<Split: SplitStrategy>(
655 &self,
656 context: &mut [(Id, Option<Term>)],
657 checker: &mut CheckRef<'_, '_, Split>,
658 ) -> Option<bool> {
659 fn has_free(t: &Term, ctx: &[(Id, Option<Term>)]) -> bool {
660 t.free_variables().iter().any(|v| {
661 ctx.iter()
662 .all(|(a, b)| a.as_ref() != v.name() || b.is_none())
663 })
664 }
665 macro_rules! check {
666 ($c:ident.$f:ident($($t:ident),+) ) => {{
667 let subst = context.iter().filter_map(|(id,o)| o.as_ref().map(|t| (id.as_ref(),t))).collect::<Vec<_>>();
668 $(
669 let $t = $t / subst.as_slice();
670 )*
671 checker.scoped(|$c| $c.$f($(&$t),*))
672 }}
673 }
674 match self {
675 Self::Inhabitable(t) => check!(c.check_inhabitable(t)),
676 Self::Universe(t) => check!(c.check_universe(t)),
677 Self::HasProof(t) => check!(c.prove(t)).map(|_| true),
678 Self::Subtype(a, b) => check!(c.check_subtype(a, b)),
679 Self::Equal(a, b) => {
680 if has_free(b, context) && !has_free(a, context) {
681 let subst = context
682 .iter()
683 .filter_map(|(id, o)| o.as_ref().map(|t| (id.as_ref(), t)))
684 .collect::<Vec<_>>();
685 let na = a / subst.as_slice();
686 let nb = b / subst.as_slice();
687 let all_vars = nb
688 .free_variables()
689 .iter()
690 .map(|v| v.name_id().into_owned())
691 .collect::<SmallVec<_, 2>>();
692 checker.comment(format!(
693 "matching {:?} against {:?}",
694 na.debug_short(),
695 nb.debug_short()
696 ));
697 let r = checker.scoped(|ch| {
698 ch.simplify_until(&na, |_, t| {
699 #[allow(clippy::option_if_let_else)]
700 if let Some(r) = Pattern::r#match(t, &nb, &all_vars, true) {
701 for (name, nt) in all_vars.iter().zip(r) {
702 if let Some((_, t)) =
703 context.iter_mut().find(|(a, _)| *a == *name)
704 {
705 *t = nt.map(Cow::into_owned);
706 }
707 }
708 true
709 } else {
710 false
711 }
712 })
713 .is_some()
714 });
715 if r { Some(true) } else { None }
716 } else {
717 check!(c.check_equality(a, b))
718 }
719 }
720 Self::HasType(a, b) => {
721 if let Term::Var {
722 variable: Variable::Name { name, .. },
723 ..
724 } = b
725 && context.iter().any(|(p, o)| p == name && o.is_none())
726 {
727 let nt = check!(c.infer_type(a))?;
728 context.iter_mut().find(|(a, _)| *a == *name)?.1 = Some(nt);
729 Some(true)
730 } else if !has_free(a, context) && !has_free(b, context) {
731 check!(c.check_type(a, b))
732 } else {
733 None
734 }
735 }
736 }
737 }
738}