1use crate::{
2 CheckRef,
3 impls::solving::TermExtSolvable,
4 rules::{
5 CheckingRule, InferenceRule, InhabitableRule, MarkerRule, PreparationRule,
6 SimplificationRule, SizedSolverRule, SubtypeRule, UniverseRule,
7 operators::numbers::{NumberRule, NumberType},
8 },
9 split::SplitStrategy,
10};
11use ftml_ontology::terms::{
12 ApplicationTerm, Argument, BindingTerm, BoundArgument, ComponentVar, MaybeSequence, Term,
13 Variable, sequences::SequenceType,
14};
15use ftml_solver_trace::traceref;
16use ftml_uris::SymbolUri;
17use smallvec::SmallVec;
18use std::{borrow::Cow, hint::unreachable_unchecked};
19
20#[allow(unpredictable_function_pointer_comparisons)]
21#[derive(Debug, Clone, PartialEq, Eq)]
22pub struct PiExtensionRule<Split: SplitStrategy> {
23 pub extension: SymbolUri,
24 pub pi: SymbolUri,
25 pub applicable: fn(&Self, &Term, &Argument) -> bool,
26 pub infer: for<'t> fn(
27 &Self,
28 &super::pi::PiInferenceRule,
29 &mut CheckRef<'t, '_, Split>,
30 &Term,
31 &'t [Argument],
32 &mut usize,
33 ) -> Option<Term>,
34}
35impl<Split: SplitStrategy> SizedSolverRule for PiExtensionRule<Split> {
36 fn display(&self) -> Vec<crate::trace::Displayable> {
37 ftml_solver_trace::trace!(&self.extension, "is extension of", &self.pi)
38 }
39}
40impl<Split: SplitStrategy> MarkerRule<Split> for PiExtensionRule<Split> {}
41
42macro_rules! ret_i {
43 ($(;)?) => {};
44 (& $e:expr; $($tk:tt)*) => {
45 if !$e {
46 return None;
47 }
48 ret_i!($($tk)*)
49 };
50 ($p:pat = $e:expr; $($tk:tt)*) => {
51 let $p = $e else {
52 return None;
53 };
54 ret_i!($($tk)*)
55 };
56
57}
58macro_rules! ret {
59 ( $($tk:tt)* ) => {
60 ret_i!($($tk)*;)
61 };
62}
63
64pub(crate) fn destruct_binder<'t>(
65 t: &'t Term,
66 head: &SymbolUri,
67) -> Option<(either::Either<&'t ComponentVar, &'t ComponentVar>, &'t Term)> {
68 ret!(
69 Term::Bound(b) = t;
70 & b.arguments.len() == 2;
71 & matches!(&b.head,Term::Symbol { uri, .. } if *uri == *head);
72 Some(BoundArgument::Simple(body)) = b.arguments.get(1);
74 );
75 let v = if let Some(BoundArgument::Bound(v)) = b.arguments.first() {
76 either::Left(v)
77 } else if let Some(BoundArgument::BoundSeq(MaybeSequence::One(s))) = b.arguments.first() {
78 either::Right(s)
79 } else {
80 return None;
81 };
82 Some((v, body))
83}
84fn construct_binder(var: ComponentVar, body: Term, head: &SymbolUri) -> Term {
85 Term::Bound(BindingTerm::new(
86 Term::Symbol {
87 uri: head.clone(),
88 presentation: None,
89 },
90 Box::new([BoundArgument::Bound(var), BoundArgument::Simple(body)]),
91 None,
92 ))
93}
94
95#[derive(Debug, Clone, PartialEq, Eq)]
96pub struct ArrowRule {
97 pub arrow: SymbolUri,
98 pub pi: SymbolUri,
99}
100impl SizedSolverRule for ArrowRule {
101 fn display(&self) -> Vec<crate::trace::Displayable> {
102 ftml_solver_trace::trace!(&self.arrow, " is simple version of ", &self.pi)
103 }
104 fn priority(&self) -> isize {
105 100_000_000 }
107}
108impl ArrowRule {
109 fn go<Split: SplitStrategy>(
110 &self,
111 t: &Term,
112 mut path: Option<(&mut smallvec::SmallVec<u8, 16>, usize)>,
113 ) -> Option<Term> {
114 let Term::Application(app) = t else {
115 return None;
116 };
117 let Some(Argument::Simple(ret)) = app.arguments.last() else {
118 return None;
119 };
120 let args = &app.arguments[..app.arguments.len() - 1];
121 if args.is_empty() {
122 return Some(ret.clone());
123 }
124 let mut count = 0u16;
125 let args = args.iter().map(|a| match a {
126 Argument::Simple(t) => {
127 if let Some((v, i)) = path.as_mut()
128 && v.get(*i).copied() == Some(count as u8)
129 {
130 v.insert(*i + 1, 0);
131 }
132 let (v, idx) = ret.fresh_variable(&crate::DUMMY, Some(count));
133 if let Some(idx) = idx {
134 count = idx + 1;
135 } else {
136 count += 1;
137 }
138 BoundArgument::Bound(ComponentVar {
139 var: v,
140 tp: Some(t.clone()),
141 df: None,
142 })
143 }
144 Argument::Sequence(MaybeSequence::One(t)) => {
145 if let Some((v, i)) = path.as_mut()
146 && v.get(*i).copied() == Some(count as u8)
147 {
148 v.insert(*i + 1, 0);
149 }
150 let (v, idx) = ret.fresh_variable(&crate::DUMMY, Some(count));
151 if let Some(idx) = idx {
152 count = idx + 1;
153 } else {
154 count += 1;
155 }
156 BoundArgument::BoundSeq(MaybeSequence::One(ComponentVar {
157 var: v,
158 tp: Some(t.clone()),
159 df: None,
160 }))
161 }
162 Argument::Sequence(MaybeSequence::Seq(ts)) => {
163 if let Some((v, i)) = path.as_mut()
164 && v.get(*i).copied() == Some(count as u8)
165 {
166 v.insert(*i + 2, 0);
167 }
168 BoundArgument::BoundSeq(MaybeSequence::Seq(
169 ts.iter()
170 .map(|t| {
171 let (v, idx) = ret.fresh_variable(&crate::DUMMY, Some(count));
172 if let Some(idx) = idx {
173 count = idx + 1;
174 } else {
175 count += 1;
176 }
177 ComponentVar {
178 var: v,
179 tp: Some(t.clone()),
180 df: None,
181 }
182 })
183 .collect(),
184 ))
185 }
186 });
187 let ret = Term::Bound(BindingTerm::new(
188 Term::Symbol {
189 uri: self.pi.clone(),
190 presentation: None,
191 },
192 args.chain([BoundArgument::Simple(ret.clone())]).collect(),
193 None,
194 ));
195 Some(ret)
196 }
197}
198impl<Split: SplitStrategy> SimplificationRule<Split> for ArrowRule {
199 fn applicable(&self, term: &Term) -> bool {
200 if let Term::Application(app) = term {
201 matches!(&app.head,Term::Symbol{uri,..} if *uri == self.arrow)
202 } else {
203 false
204 }
205 }
206 fn apply<'t>(
207 &self,
208 _: CheckRef<'t, '_, Split>,
209 t: &'t Term,
210 ) -> Result<Term, Option<ftml_ontology::terms::termpaths::TermPath>> {
211 self.go::<Split>(t, None).ok_or(None)
212 }
213}
214impl<Split: SplitStrategy> PreparationRule<Split> for ArrowRule {
215 fn applicable(&self, _: &CheckRef<'_, '_, Split>, t: &Term) -> bool {
216 <Self as SimplificationRule<Split>>::applicable(self, t)
217 }
218 fn applicable_revert(&self, _: &CheckRef<'_, '_, Split>, t: &Term) -> bool {
219 if let Term::Bound(bind) = t {
220 matches!(&bind.head,Term::Symbol { uri, .. } if *uri == self.pi)
221 } else {
222 false
223 }
224 }
225
226 #[allow(clippy::cast_possible_truncation)]
227 fn apply(
228 &self,
229 _: &mut CheckRef<'_, '_, Split>,
230 t: Term,
231 path: Option<(&mut smallvec::SmallVec<u8, 16>, usize)>,
232 ) -> std::ops::ControlFlow<Term, Term> {
233 std::ops::ControlFlow::Continue(self.go::<Split>(&t, path).unwrap_or(t))
234 }
235
236 fn revert(&self, _: &CheckRef<'_, '_, Split>, t: Term) -> std::ops::ControlFlow<Term, Term> {
237 let Term::Bound(app) = t else {
238 return std::ops::ControlFlow::Continue(t);
239 };
240 let Some(BoundArgument::Simple(ret)) = app.arguments.last() else {
241 return std::ops::ControlFlow::Continue(Term::Bound(app));
242 };
243 let args = &app.arguments[..app.arguments.len() - 1];
244 let mut free_vars = args.iter().map(|a| a.free_variables()).collect::<Vec<_>>();
245 free_vars.push(ret.free_variables());
246
247 let args: Result<Vec<Argument>, ()> = args
248 .iter()
249 .enumerate()
250 .map(|(i, a)| match a {
251 BoundArgument::Bound(ComponentVar {
252 var,
253 tp: Some(tp),
254 df: None,
255 }) if !free_vars[i + 1..]
256 .iter()
257 .flatten()
258 .any(|v| v.name() == var.name()) =>
259 {
260 Ok(Argument::Simple(tp.clone()))
261 }
262 BoundArgument::BoundSeq(MaybeSequence::One(ComponentVar {
263 var,
264 tp: Some(tp),
265 df: None,
266 })) if !free_vars[i + 1..]
267 .iter()
268 .flatten()
269 .any(|v| v.name() == var.name()) =>
270 {
271 Ok(Argument::Sequence(MaybeSequence::One(tp.clone())))
272 }
273 BoundArgument::BoundSeq(MaybeSequence::Seq(vs)) => {
274 let seq: Result<Box<[Term]>, ()> = vs
275 .iter()
276 .map(|v| {
277 if let ComponentVar {
278 var,
279 tp: Some(tp),
280 df: None,
281 } = v
282 && !free_vars[i + 1..]
283 .iter()
284 .flatten()
285 .any(|v| v.name() == var.name())
286 {
287 Ok(tp.clone())
288 } else {
289 Err(())
290 }
291 })
292 .collect();
293 Ok(Argument::Sequence(MaybeSequence::Seq(seq?)))
294 }
295 _ => Err(()),
296 })
297 .collect();
298 drop(free_vars);
299 std::ops::ControlFlow::Continue(match args {
300 Err(()) => Term::Bound(app),
301 Ok(mut args) => {
302 args.push(Argument::Simple(ret.clone()));
303 Term::Application(ApplicationTerm::new(
304 Term::Symbol {
305 uri: self.arrow.clone(),
306 presentation: None,
307 },
308 args.into_boxed_slice(),
309 None,
310 ))
311 }
312 })
313 }
314}
315
316#[derive(Debug, Clone, PartialEq, Eq)]
317pub struct NeedsTypeRule(pub SymbolUri);
318impl SizedSolverRule for NeedsTypeRule {
319 fn display(&self) -> Vec<ftml_solver_trace::Displayable> {
320 ftml_solver_trace::trace!(&self.0, "needs typed variables")
321 }
322}
323impl<Split: SplitStrategy> PreparationRule<Split> for NeedsTypeRule {
324 fn applicable(&self, _: &CheckRef<'_, '_, Split>, t: &Term) -> bool {
325 if let Term::Bound(b) = t
326 && let Term::Symbol { uri, .. } = &b.head
327 && *uri == self.0
328 {
329 b.arguments.iter().any(|a| matches!(a,BoundArgument::Bound(ComponentVar { tp:None, .. })|BoundArgument::BoundSeq(MaybeSequence::One(ComponentVar { tp:None, .. })))
330 || matches!(a,BoundArgument::BoundSeq(MaybeSequence::Seq(seq)) if seq.iter().any(|a| matches!(a,ComponentVar { tp:None, .. }))))
331 } else {
332 false
333 }
334 }
335 fn applicable_revert(&self, _: &CheckRef<'_, '_, Split>, _: &Term) -> bool {
336 false
337 }
338 fn apply(
339 &self,
340 checker: &mut CheckRef<'_, '_, Split>,
341 t: Term,
342 path: Option<(&mut smallvec::SmallVec<u8, 16>, usize)>,
343 ) -> std::ops::ControlFlow<Term, Term> {
344 let Term::Bound(b) = t else {
345 return std::ops::ControlFlow::Continue(t);
346 };
347 let arguments = &b.arguments;
348 let ret = checker.scoped(|checker| {
349 let mut changed = false;
350 let nargs = arguments
351 .iter()
352 .map(|a| match a {
353 BoundArgument::Bound(ComponentVar { var, tp: None, df }) => {
354 if checker.infer_var_type(var).is_none() {
355 changed = true;
356 let v = checker.new_solvable();
357 Cow::Owned(BoundArgument::Bound(ComponentVar {
358 var: var.clone(),
359 tp: Some(v),
360 df: df.clone(),
361 }))
362 } else {
363 Cow::Borrowed(a)
364 }
365 }
366 BoundArgument::BoundSeq(MaybeSequence::One(ComponentVar {
367 var,
368 tp: None,
369 df,
370 })) => {
371 if checker.infer_var_type(var).is_none() {
372 changed = true;
373 let v = checker.new_solvable();
374 Cow::Owned(BoundArgument::BoundSeq(MaybeSequence::One(ComponentVar {
375 var: var.clone(),
376 tp: Some(v),
377 df: df.clone(),
378 })))
379 } else {
380 Cow::Borrowed(a)
381 }
382 }
383 BoundArgument::BoundSeq(MaybeSequence::Seq(vs)) => {
384 let nvs = vs
385 .iter()
386 .map(|cv @ ComponentVar { var, tp, df }| {
387 if tp.is_none() && checker.infer_var_type(var).is_none() {
388 changed = true;
389 let v = checker.new_solvable();
390 Cow::Owned(ComponentVar {
391 var: var.clone(),
392 tp: Some(v),
393 df: df.clone(),
394 })
395 } else {
396 Cow::Borrowed(cv)
397 }
398 })
399 .collect::<Vec<_>>();
400 if changed {
401 Cow::Owned(BoundArgument::BoundSeq(MaybeSequence::Seq(
402 nvs.into_iter().map(Cow::into_owned).collect(),
403 )))
404 } else {
405 Cow::Borrowed(a)
406 }
407 }
408 _ => Cow::Borrowed(a),
409 })
410 .collect::<Vec<_>>();
411 if changed {
412 Some(Term::Bound(BindingTerm::new(
413 b.head.clone(),
414 nargs.into_iter().map(Cow::into_owned).collect(),
415 b.presentation.clone(),
416 )))
417 } else {
418 None
419 }
420 });
421 std::ops::ControlFlow::Continue(ret.unwrap_or(Term::Bound(b)))
422 }
423
424 fn revert(&self, _: &CheckRef<'_, '_, Split>, t: Term) -> std::ops::ControlFlow<Term, Term> {
425 std::ops::ControlFlow::Continue(t)
426 }
427}
428
429#[derive(Debug, Clone, PartialEq, Eq)]
430pub struct PiVarianceRule(pub SymbolUri);
431
432impl SizedSolverRule for PiVarianceRule {
433 fn display(&self) -> Vec<crate::trace::Displayable> {
434 ftml_solver_trace::trace!(
435 &self.0,
436 " is contravariant in arguments and covariant in its return type"
437 )
438 }
439}
440impl<Split: SplitStrategy> SubtypeRule<Split> for PiVarianceRule {
441 fn applicable(&self, _: &CheckRef<'_, '_, Split>, sub: &Term, sup: &Term) -> bool {
442 if let Term::Bound(b) = sub
443 && let Term::Bound(b2) = sup
444 && let Term::Symbol { uri, .. } = &b.head
445 && let Term::Symbol { uri: uri2, .. } = &b2.head
446 {
447 *uri == self.0 && *uri2 == self.0 && b.arguments.len() == b2.arguments.len()
448 } else {
449 false
450 }
451 }
452 fn apply<'t>(
453 &self,
454 mut checker: CheckRef<'t, '_, Split>,
455 sub: &'t Term,
456 sup: &'t Term,
457 ) -> Option<bool> {
458 let Term::Bound(sub) = sub else { return None };
459 let Term::Bound(sup) = sup else { return None };
460 let Some(BoundArgument::Simple(subret)) = sub.arguments.last() else {
461 return None;
462 };
463 let Some(BoundArgument::Simple(supret)) = sup.arguments.last() else {
464 return None;
465 };
466 let mut currsub = Vec::<(&str, Term)>::new();
467 for (sub, sup) in sub.arguments[..sub.arguments.len() - 1]
468 .iter()
469 .zip(sup.arguments[..sup.arguments.len() - 1].iter())
470 {
471 match (sub, sup) {
472 (
473 BoundArgument::Bound(ComponentVar {
474 var: varsub,
475 tp: Some(sub),
476 ..
477 }),
478 BoundArgument::Bound(ComponentVar {
479 var: varsup,
480 tp: Some(sup),
481 ..
482 }),
483 ) => {
484 let sup = sup / &*currsub;
485 if !checker.scoped(|checker| checker.check_subtype(&sup, sub))? {
486 return None;
487 }
488 currsub.push((varsup.name(), varsub.clone().into()));
489 checker.extend_context(ComponentVar {
490 var: varsub.clone(),
491 tp: Some(sub.clone()),
492 df: None,
493 });
494 }
495 (
496 BoundArgument::BoundSeq(MaybeSequence::One(ComponentVar {
497 var: varsub,
498 tp: Some(sub),
499 ..
500 })),
501 BoundArgument::BoundSeq(MaybeSequence::One(ComponentVar {
502 var: varsup,
503 tp: Some(sup),
504 ..
505 }))
506 | BoundArgument::Bound(ComponentVar {
507 var: varsup,
508 tp: Some(sup),
509 ..
510 }),
511 ) => {
512 let sup = sup / &*currsub;
513 if !checker.scoped(|checker| checker.check_subtype(&sup, sub))? {
514 return None;
515 }
516 currsub.push((varsup.name(), varsub.clone().into()));
517 checker.extend_context(ComponentVar {
518 var: varsub.clone(),
519 tp: Some(sub.clone()),
520 df: None,
521 });
522 }
523 _ => {
524 checker.failure("argument not bound single variable or variable sequence");
525 return None;
527 }
528 }
529 }
530 let supret = supret / &*currsub;
531 checker.scoped(|checker| checker.check_subtype(subret, &supret))
532 }
533}
534
535#[derive(Debug, Clone, PartialEq, Eq)]
536pub struct LambdaPiInferenceRule {
537 pub lambda: SymbolUri,
538 pub pi: SymbolUri,
539}
540impl SizedSolverRule for LambdaPiInferenceRule {
541 fn display(&self) -> Vec<crate::trace::Displayable> {
542 ftml_solver_trace::trace!(
543 "{ x:A, B(x):T } ⊢ (",
544 &self.lambda,
545 " x:A. B) :=> ",
546 &self.pi,
547 " x:A. T"
548 )
549 }
550}
551impl LambdaPiInferenceRule {
552 fn infer_simple<'t, Split: SplitStrategy>(
553 &self,
554 mut checker: CheckRef<'t, '_, Split>,
555 var: &'t ComponentVar,
556 body: &'t Term,
557 ) -> Option<Term> {
558 let btp = match &var.tp {
559 None => {
560 if checker.infer_var_type(&var.var).is_some() {
561 checker.scoped(|checker| {
562 checker.extend_context(var);
563 checker.infer_type(body)
564 })?
565 } else {
566 let nvar = ComponentVar {
567 var: var.var.clone(),
568 tp: Some(checker.new_solvable()),
569 df: var.df.clone(),
570 };
571 checker.scoped(|checker| {
572 checker.extend_context(nvar);
573 checker.infer_type(body)
574 })?
575 }
576 }
577 Some(tp) if tp.is_solvable().is_some() => {
578 let inf = checker.scoped(|checker| {
579 checker.extend_context(var);
580 checker.infer_type(body)
581 })?;
582 ret!(&checker.check_inhabitable(tp) == Some(true));
583 inf
584 }
585 Some(tp) => {
586 ret!(&checker.check_inhabitable(tp) == Some(true));
587 checker.scoped(|checker| {
588 checker.extend_context(var);
589 checker.infer_type(body)
590 })?
591 }
592 };
593 Some(construct_binder(var.clone(), btp, &self.pi))
594 }
595
596 fn infer_sequence<'t, Split: SplitStrategy>(
597 &self,
598 mut checker: CheckRef<'t, '_, Split>,
599 var: &'t ComponentVar,
600 body: &'t Term,
601 ) -> Option<Term> {
602 let btp = match &var.tp {
603 None => {
604 if let Some(tp) = checker.infer_var_type(&var.var) {
605 checker.scoped(|checker| {
606 checker.extend_context(var);
607 checker.infer_type(body)
608 })?
609 } else {
610 let nvar = ComponentVar {
611 var: var.var.clone(),
612 tp: Some(checker.new_solvable()),
613 df: var.df.clone(),
614 };
615 checker.scoped(|checker| {
616 checker.extend_context(nvar);
617 checker.infer_type(body)
618 })?
619 }
620 }
621 Some(tp) if tp.is_solvable().is_some() => {
622 let inf = checker.scoped(|checker| {
623 checker.extend_context(var);
624 checker.infer_type(body)
625 })?;
626 ret!(&checker.check_inhabitable(tp) == Some(true));
627 inf
628 }
629 Some(tp) => {
630 ret!(&checker.check_inhabitable(tp) == Some(true));
631 checker.scoped(|checker| {
632 checker.extend_context(var);
633 checker.infer_type(body)
634 })?
635 }
636 };
637 let r = Term::Bound(BindingTerm::new(
638 Term::Symbol {
639 uri: self.pi.clone(),
640 presentation: None,
641 },
642 Box::new([
643 BoundArgument::BoundSeq(MaybeSequence::One(var.clone())),
644 BoundArgument::Simple(btp),
645 ]),
646 None,
647 ));
648 Some(r)
649 }
650}
651impl<Split: SplitStrategy> InferenceRule<Split> for LambdaPiInferenceRule {
652 fn applicable(&self, term: &Term) -> bool {
653 destruct_binder(term, &self.lambda).is_some()
654 }
655 fn infer<'t>(&self, checker: CheckRef<'t, '_, Split>, term: &'t Term) -> Option<Term> {
656 let (var, body) = destruct_binder(term, &self.lambda)?;
657 match var {
658 either::Left(var) => self.infer_simple(checker, var, body),
659 either::Right(var) => self.infer_sequence(checker, var, body),
660 }
661 }
662}
663
664#[derive(Debug, Clone, PartialEq, Eq)]
665pub struct LambdaPiCheckingRule {
666 pub lambda: SymbolUri,
667 pub pi: SymbolUri,
668}
669impl SizedSolverRule for LambdaPiCheckingRule {
670 fn display(&self) -> Vec<crate::trace::Displayable> {
671 ftml_solver_trace::trace!(
672 "{ x:A, B(x):T } ⊢ (",
673 &self.lambda,
674 " x:A. B) : ",
675 &self.pi,
676 " y:A. T"
677 )
678 }
679}
680impl<Split: SplitStrategy> CheckingRule<Split> for LambdaPiCheckingRule {
681 fn applicable(&self, _: &CheckRef<'_, '_, Split>, term: &Term, tp: &Term) -> bool {
682 destruct_binder(term, &self.lambda).is_some_and(|(v, _)| v.is_left())
683 && destruct_binder(tp, &self.pi).is_some_and(|(v, _)| v.is_left())
684 }
685 fn apply<'t>(
686 &self,
687 mut checker: CheckRef<'t, '_, Split>,
688 term: &'t Term,
689 tp: &'t Term,
690 ) -> Option<bool> {
691 let (var, lambda_body) = destruct_binder(term, &self.lambda)?;
692 let var = var.expect_left("applicable");
693 let (pivar, pi_body) = destruct_binder(tp, &self.pi)?;
694 let pivar = pivar.expect_left("applicable");
695 let pi_tp = match &pivar.tp {
696 None => Cow::Owned(checker.infer_var_type(&var.var)?),
697 Some(tp) => {
698 Cow::Borrowed(tp)
700 }
701 };
702 let lam_tp = match &var.tp {
703 None => Cow::Owned(checker.infer_var_type(&var.var)?),
704 Some(tp) => {
705 Cow::Borrowed(tp)
707 }
708 };
709 ret!(&checker.scoped(|checker| { checker.check_subtype(&pi_tp, &lam_tp) }) == Some(true));
710 let ntp = pi_body
711 / (
712 pivar.var.name(),
713 &Term::Var {
714 variable: var.var.clone(),
715 presentation: None,
716 },
717 );
718 checker.scoped(|checker| {
719 checker.extend_context(var);
720 checker.check_type(lambda_body, &ntp)
721 })
722 }
723}
724
725#[derive(Debug, Clone, PartialEq, Eq)]
726pub struct PiInhabitableRule(pub SymbolUri);
727impl SizedSolverRule for PiInhabitableRule {
728 fn display(&self) -> Vec<crate::trace::Displayable> {
729 ftml_solver_trace::trace!("{ INH A, x:A, INH B(x) } ⊢ INH ", &self.0, " x:A. B")
730 }
731}
732
733impl<Split: SplitStrategy> InhabitableRule<Split> for PiInhabitableRule {
734 fn applicable(&self, term: &Term) -> bool {
735 matches!(term,Term::Bound(b) if matches!(&b.head,Term::Symbol { uri, .. } if *uri == self.0))
736 }
737 fn apply<'t>(&self, mut checker: CheckRef<'t, '_, Split>, term: &'t Term) -> Option<bool> {
738 let Term::Bound(b) = term else { return None };
739 let Some(BoundArgument::Simple(body)) = b.arguments.last() else {
740 return None;
741 };
742 let previous = &b.arguments[..&b.arguments.len() - 1];
743 let mut deferred = Vec::new();
744 for arg in previous {
746 match arg {
747 BoundArgument::Simple(t) | BoundArgument::Sequence(MaybeSequence::One(t)) => {
748 let _ = checker.infer_type(t)?;
749 }
750 BoundArgument::Sequence(MaybeSequence::Seq(ts)) => {
751 for t in ts {
752 let _ = checker.infer_type(t)?;
753 }
754 }
755 BoundArgument::Bound(cv @ ComponentVar { var, tp, .. })
756 | BoundArgument::BoundSeq(MaybeSequence::One(cv @ ComponentVar { var, tp, .. })) => {
757 if let Some(tp) = tp {
758 if tp.has_solvable() {
759 deferred.push(tp);
760 } else if !checker.check_inhabitable(tp)? {
761 return Some(false);
762 }
763 } else {
764 let _ = checker.infer_var_type(var)?;
765 }
766 checker.extend_context(cv);
767 }
768 BoundArgument::BoundSeq(MaybeSequence::Seq(vars)) => {
769 for cv @ ComponentVar { var, tp, .. } in vars {
770 if let Some(tp) = tp {
771 if tp.has_solvable() {
772 deferred.push(tp);
773 } else if !checker.check_inhabitable(tp)? {
774 return Some(false);
775 }
776 } else {
777 let _ = checker.infer_var_type(var)?;
778 }
779 checker.extend_context(cv);
780 }
781 }
782 }
783 }
784 if !checker.check_inhabitable(body)? {
785 return None;
786 }
787 for d in deferred {
788 if !checker.check_inhabitable(d)? {
789 return None;
790 }
791 }
792 Some(true)
793 }
794}
795
796#[derive(Debug, Clone, PartialEq, Eq)]
797pub struct PiUniverseRule(pub SymbolUri);
798impl SizedSolverRule for PiUniverseRule {
799 fn display(&self) -> Vec<crate::trace::Displayable> {
800 ftml_solver_trace::trace!("{ INH A, x:A, UNIV B(x) } ⊢ UNIV ", &self.0, " x:A. B")
801 }
802}
803
804impl<Split: SplitStrategy> UniverseRule<Split> for PiUniverseRule {
805 fn applicable(&self, term: &Term) -> bool {
806 matches!(term,Term::Bound(b) if matches!(&b.head,Term::Symbol { uri, .. } if *uri == self.0))
807 }
808 fn apply<'t>(&self, mut checker: CheckRef<'t, '_, Split>, term: &'t Term) -> Option<bool> {
809 let Term::Bound(b) = term else { return None };
810 let Some(BoundArgument::Simple(body)) = b.arguments.last() else {
811 return None;
812 };
813 let previous = &b.arguments[..&b.arguments.len() - 1];
814 let mut deferred = Vec::new();
815 for arg in previous {
817 match arg {
818 BoundArgument::Simple(t) | BoundArgument::Sequence(MaybeSequence::One(t)) => {
819 let _ = checker.infer_type(t)?;
820 }
821 BoundArgument::Sequence(MaybeSequence::Seq(ts)) => {
822 for t in ts {
823 let _ = checker.infer_type(t)?;
824 }
825 }
826 BoundArgument::Bound(cv @ ComponentVar { var, tp, .. })
827 | BoundArgument::BoundSeq(MaybeSequence::One(cv @ ComponentVar { var, tp, .. })) => {
828 if let Some(tp) = tp {
829 if tp.has_solvable() {
830 deferred.push(tp);
831 } else if !checker.check_inhabitable(tp)? {
832 return Some(false);
833 }
834 } else {
835 let _ = checker.infer_var_type(var)?;
836 }
837 checker.extend_context(cv);
838 }
839 BoundArgument::BoundSeq(MaybeSequence::Seq(vars)) => {
840 for cv @ ComponentVar { var, tp, .. } in vars {
841 if let Some(tp) = tp {
842 if tp.has_solvable() {
843 deferred.push(tp);
844 } else if !checker.check_inhabitable(tp)? {
845 return Some(false);
846 }
847 } else {
848 let _ = checker.infer_var_type(var)?;
849 }
850 checker.extend_context(cv);
851 }
852 }
853 }
854 }
855 if !checker.check_universe(body)? {
856 return None;
857 }
858 for d in deferred {
859 if !checker.check_inhabitable(d)? {
860 return None;
861 }
862 }
863 Some(true)
864 }
865}
866
867#[derive(Debug, Clone, PartialEq, Eq)]
868pub struct PiInferenceRule(pub SymbolUri);
869impl SizedSolverRule for PiInferenceRule {
870 fn display(&self) -> Vec<crate::trace::Displayable> {
871 ftml_solver_trace::trace!("{ f: ", &self.0, " x:A.B(x), t:A } ⊢ f(t) :=> B(t)")
872 }
873}
874
875impl<Split: SplitStrategy> InferenceRule<Split> for PiInferenceRule {
876 fn applicable(&self, term: &Term) -> bool {
877 matches!(term, Term::Application(_)) }
879 fn infer<'t>(&self, mut checker: CheckRef<'t, '_, Split>, term: &'t Term) -> Option<Term> {
880 if let Term::Application(app) = term {
881 if app.arguments.is_empty() {
882 return None;
883 }
884 let tp = checker.infer_type(&app.head)?;
885 if let Some(SequenceType::SeqType(tp, _)) = tp.as_sequence_type()
886 && let [Argument::Simple(idx)] = &*app.arguments
887 {
888 checker.comment("is sequence type");
889 let nat = checker.rules().marker().iter().rev().find_map(|rl| {
890 rl.as_any().downcast_ref::<NumberRule>().and_then(|rl| {
891 if rl.typ == NumberType::Naturals {
892 Some(rl.sym.clone())
893 } else {
894 None
895 }
896 })
897 })?;
898 let ntp: Term = nat.into();
899 if checker.scoped(|checker| checker.check_type(idx, &ntp)) != Some(true) {
900 return None;
901 }
902 Some(tp.clone())
903 } else {
904 self.type_apply(&mut checker, tp, &app.arguments)
905 }
906 } else {
907 checker.failure("Not an application");
908 None
909 }
910 }
911}
912
913impl PiInferenceRule {
914 pub fn deconstruct_tp<Split: SplitStrategy>(
916 bind_uri: &SymbolUri,
917 checker: &mut CheckRef<'_, '_, Split>,
918 tp: Term,
919 ) -> Result<BindingTerm, Term> {
920 let Some(nret) = checker.scoped(|checker| {
921 match checker.simplify_until(&tp, |_, t| {
922 if let Term::Bound(b) = t
923 && let Term::Symbol { uri, .. } = &b.head
924 {
925 *uri == *bind_uri
926 && b.arguments.len() == 2
927 && matches!(b.arguments.get(1), Some(BoundArgument::Simple(_)))
928 } else {
929 false
930 }
931 })? {
932 Cow::Borrowed(_) => Some(None),
933 Cow::Owned(tp) => Some(Some(tp)),
934 }
935 }) else {
936 checker.add_msg(traceref!(FAIL "type is not a binder: ",tp.clone()).into());
937 return Err(tp);
938 };
939 let Term::Bound(b) = nret.unwrap_or(tp) else {
940 unsafe { unreachable_unchecked() }
942 };
943 if !matches!(&b.head,Term::Symbol { uri, .. } if *uri == *bind_uri)
944 || b.arguments.len() != 2
945 || !matches!(b.arguments.get(1), Some(BoundArgument::Simple(_)))
946 {
947 checker.failure("Type is not a Î anymore");
948 return Err(Term::Bound(b));
949 }
950 Ok(b)
951 }
952
953 pub(super) fn flatten_sequence<Split: SplitStrategy>(
955 checker: &mut CheckRef<'_, '_, Split>,
956 tp: &Term,
957 b: &BindingTerm,
958 body: Term,
959 ) -> Term {
960 let Some(new_args) = tp.as_sequence().and_then(|s| s.to_concrete()) else {
961 unsafe { unreachable_unchecked() }
963 };
964 checker.comment("Flattening concrete sequence argument");
965 new_args.into_iter().rfold(body.clone(), |body, arg| {
966 Term::Bound(BindingTerm::new(
967 b.head.clone(),
968 Box::new([
969 BoundArgument::Bound(ComponentVar {
970 var: Variable::Name {
971 name: crate::DUMMY.clone(),
972 notated: None,
973 },
974 tp: Some(arg),
975 df: None,
976 }),
977 BoundArgument::Simple(body),
978 ]),
979 b.presentation.clone(),
980 ))
981 })
982 }
983
984 pub(super) fn recurse_seq_args<'t, Split: SplitStrategy>(
986 uri: &SymbolUri,
987 checker: &mut CheckRef<'t, '_, Split>,
988 b: &BindingTerm,
989 seq: &'t [Term],
990 body: &Term,
991 ) -> Option<Term> {
992 let first = unsafe { seq.first().unwrap_unchecked() };
994 let rest = &seq[1..];
995 let mut ret = Self::simple_apply(checker, b, first, body)?;
996 for arg in rest {
997 let b = Self::deconstruct_tp(uri, checker, ret).ok()?;
998 let [_, BoundArgument::Simple(body)] = &*b.arguments else {
999 unsafe { unreachable_unchecked() }
1001 };
1002 ret = Self::simple_apply(checker, &b, arg, body)?;
1003 }
1004 Some(ret)
1005 }
1006
1007 fn try_extension<'t, Split: SplitStrategy>(
1008 &self,
1009 checker: &mut CheckRef<'t, '_, Split>,
1010 tp: &'t Term,
1011 args: &'t [Argument],
1012 index: &mut usize,
1013 ) -> Option<Term> {
1014 let exts = checker
1015 .rules()
1016 .marker()
1017 .iter()
1018 .rev()
1019 .filter_map(|rl| rl.as_any().downcast_ref::<PiExtensionRule<Split>>())
1020 .cloned()
1021 .collect::<SmallVec<_, 1>>();
1022 let arg = &args[*index];
1023 let Some(ntp) =
1024 checker.simplify_until(tp, |_, t| exts.iter().any(|rl| (rl.applicable)(rl, t, arg)))
1025 else {
1026 checker.failure("type is not a pi");
1027 return None;
1028 };
1029 for rl in exts {
1030 if (rl.applicable)(&rl, &ntp, arg)
1031 && let Some(t) = (rl.infer)(&rl, self, checker, &ntp, args, index)
1032 {
1033 return Some(t);
1034 }
1035 }
1036 None
1037 }
1038
1039 pub fn type_apply<'t, Split: SplitStrategy>(
1040 &self,
1041 checker: &mut CheckRef<'t, '_, Split>,
1042 tp: Term,
1043 args: &'t [Argument],
1044 ) -> Option<Term> {
1045 let mut ret = tp;
1046 let mut i = 0;
1047 loop {
1048 let Some(arg) = args.get(i) else {
1049 return Some(ret);
1050 };
1051 let dec = Self::deconstruct_tp(&self.0, checker, ret);
1052 let b = match dec {
1053 Ok(b) => b,
1054 Err(t) => {
1055 ret =
1056 checker.scoped(|checker| self.try_extension(checker, &t, args, &mut i))?;
1057 continue;
1058 }
1059 };
1060 let [first, BoundArgument::Simple(body)] = &*b.arguments else {
1061 unsafe { unreachable_unchecked() }
1063 };
1064 match (first, arg) {
1065 (
1066 BoundArgument::Bound(ComponentVar {
1067 var,
1068 tp: Some(tp),
1069 df: None,
1070 }),
1071 Argument::Sequence(seq),
1072 ) if !body.has_free_such_that(|v| v.name() == var.name())
1073 && tp.as_sequence().is_some_and(|s| s.is_concrete()) =>
1074 {
1075 ret = Self::flatten_sequence(checker, tp, &b, body.clone());
1076 }
1077 (
1078 BoundArgument::Bound(ComponentVar {
1079 var,
1080 tp: Some(tp),
1081 df: None,
1082 }),
1083 Argument::Sequence(MaybeSequence::Seq(seq)),
1084 ) if !seq.is_empty() => {
1085 i += 1;
1086 checker.counter("(a) Checking Argument ", i);
1087 ret = Self::recurse_seq_args(&self.0, checker, &b, seq, body)?;
1088 }
1089 (_, Argument::Simple(arg)) => {
1090 i += 1;
1091 checker.counter("(b) Checking Argument ", i);
1092 ret = Self::simple_apply(checker, &b, arg, body)?;
1093 }
1094 (_, Argument::Sequence(arg)) => {
1095 i += 1;
1096 checker.counter("(c) Checking Argument ", i);
1097 ret = checker.scoped(|checker| Self::seq_apply(checker, &b, arg, body))?;
1098 }
1099 }
1100 }
1101 }
1102
1103 pub(super) fn simple_apply<'t, Split: SplitStrategy>(
1104 checker: &mut CheckRef<'t, '_, Split>,
1105 b: &BindingTerm,
1106 arg: &'t Term,
1107 body: &Term,
1108 ) -> Option<Term> {
1109 let headvar = match b.arguments.first() {
1110 Some(BoundArgument::Bound(headvar)) => headvar,
1111 Some(BoundArgument::BoundSeq(MaybeSequence::Seq(vs))) => {
1112 if let [headvar] = &**vs {
1113 headvar
1114 } else {
1115 checker.failure("First argument is not a bound variable");
1116 return None;
1117 }
1118 }
1119 Some(BoundArgument::BoundSeq(MaybeSequence::One(v))) if arg.is_sequence() => v,
1120 Some(BoundArgument::BoundSeq(MaybeSequence::One(_))) => {
1121 let seq = Term::into_seq(std::iter::once(arg.clone()));
1122 return checker.scoped(|slf| Self::simple_apply(slf, b, &seq, body));
1123 }
1124 _ => {
1125 checker.failure("First argument is not a bound variable");
1126 return None;
1133 }
1134 };
1135
1136 let (varname, vartp) = match headvar {
1137 ComponentVar {
1138 var, tp: Some(tp), ..
1139 } => (var.name(), tp.clone()),
1140 ComponentVar { var, .. } => (
1141 var.name(),
1142 checker.scoped(|checker| {
1143 checker
1144 .infer_var_type(var)
1145 .unwrap_or_else(|| checker.new_solvable())
1146 }),
1147 ),
1148 };
1149
1150 if checker
1151 .scoped(|checker| checker.check_type(arg, &vartp))
1152 .is_none_or(|b| !b)
1153 {
1154 return None;
1155 }
1156 Some((body / (varname, arg)).into_owned())
1157 }
1158
1159 pub(super) fn seq_apply<'t, Split: SplitStrategy>(
1160 checker: &mut CheckRef<'t, '_, Split>,
1161 b: &'t BindingTerm,
1162 arg: &'t MaybeSequence<Term>,
1163 body: &Term,
1164 ) -> Option<Term> {
1165 if let Some(BoundArgument::Bound(ComponentVar {
1166 var,
1167 tp: Some(tp),
1168 df: None,
1169 })) = b.arguments.first()
1170 && let Some(tpargs) = tp.as_sequence().and_then(|s| s.to_concrete())
1171 && !body
1172 .free_variables()
1173 .into_iter()
1174 .any(|v| v.name() == var.name())
1175 && let MaybeSequence::Seq(args) = arg
1176 && args.len() == tpargs.len()
1177 {
1178 for (a, t) in args.iter().zip(tpargs.iter()) {
1179 if !checker.scoped(|checker| checker.check_type(a, t))? {
1180 return None;
1181 }
1182 }
1183 return Some(body.clone());
1184 }
1185
1186 let Some(BoundArgument::BoundSeq(MaybeSequence::One(headvar))) = b.arguments.first() else {
1187 checker.failure("First argument is not a bound variable sequence");
1188 return None;
1194 };
1195
1196 let (varname, vartp) = match headvar {
1197 ComponentVar {
1198 var, tp: Some(tp), ..
1199 } => (var.name(), tp.clone()),
1200 ComponentVar { var, .. } => (
1201 var.name(),
1202 checker.scoped(|checker| {
1203 checker
1204 .infer_var_type(var)
1205 .unwrap_or_else(|| checker.new_solvable())
1206 }),
1207 ),
1208 };
1209
1210 match arg {
1211 MaybeSequence::One(arg) => {
1212 if checker
1213 .scoped(|checker| checker.check_type(arg, &vartp))
1214 .is_none_or(|b| !b)
1215 {
1216 return None;
1217 }
1218 Some((body / (varname, arg)).into_owned())
1219 }
1220 MaybeSequence::Seq(arg) => {
1221 let narg = Term::into_seq(arg.iter().cloned());
1222 if let Some(SequenceType::SeqType(vartp, _)) = vartp.as_sequence_type() {
1223 if !checker.scoped(|checker| {
1224 for a in arg {
1225 if !checker.check_type(a, vartp)? {
1226 return None;
1227 }
1228 }
1229 Some(true)
1230 })? {
1231 return None;
1232 }
1233 } else if checker
1234 .scoped(|checker| checker.check_type(&narg, &vartp))
1235 .is_none_or(|b| !b)
1236 {
1237 return None;
1238 }
1239
1240 Some((body / (varname, &narg)).into_owned())
1241 }
1242 }
1243 }
1244}
1245
1246#[derive(Debug, Clone, PartialEq, Eq)]
1247pub struct EtaRule(pub SymbolUri);
1248impl SizedSolverRule for EtaRule {
1249 fn display(&self) -> Vec<crate::trace::Displayable> {
1250 ftml_solver_trace::trace!(&self.0, " x1:A1,...,xn:An. s(x1,...,xn) ==> s")
1251 }
1252}
1253impl<Split: SplitStrategy> SimplificationRule<Split> for EtaRule {
1254 fn applicable(&self, term: &Term) -> bool {
1255 fn applicable_inner(idx: usize, term: &Term, slf: &SymbolUri) -> bool {
1256 if let Term::Bound(op) = &term
1257 && let Term::Symbol { uri, .. } = &op.head
1258 && *uri == *slf
1259 && op.arguments.len() == 2
1260 && let Some(BoundArgument::Bound(v)) = op.arguments.first()
1261 && let Some(BoundArgument::Simple(a)) = op.arguments.get(1)
1262 {
1263 matches!(a,Term::Application(app) if matches!(app.arguments.get(idx),Some(Argument::Simple(Term::Var { variable, .. })) if variable.name() == v.var.name()))
1264 || applicable_inner(idx + 1, a, slf)
1265 } else {
1266 false
1267 }
1268 }
1269 applicable_inner(0, term, &self.0)
1270 }
1271 fn apply<'t>(
1272 &self,
1273 checker: CheckRef<'t, '_, Split>,
1274 mut term: &'t Term,
1275 ) -> Result<Term, Option<ftml_ontology::terms::termpaths::TermPath>> {
1276 let mut vars = Vec::new();
1277 loop {
1278 let Term::Bound(op) = term else {
1279 return Err(None);
1280 };
1281 let Term::Symbol { uri, .. } = &op.head else {
1282 return Err(None);
1283 };
1284 if *uri != self.0 {
1285 return Err(None);
1286 };
1287 let [BoundArgument::Bound(v), BoundArgument::Simple(body)] = &*op.arguments else {
1288 return Err(None);
1289 };
1290 vars.push(&v.var);
1291 match body {
1292 Term::Application(app) if app.arguments.len() >= vars.len() => {
1293 let idx = app.arguments.len() - vars.len();
1294 let margs = &app.arguments[idx..];
1295 for (a, v) in margs.iter().zip(vars.iter()) {
1296 if let Argument::Simple(Term::Var { variable, .. }) = a
1297 && variable.name() == v.name()
1298 {
1299 } else {
1301 return Err(None);
1302 }
1303 }
1304 if idx == 0 {
1305 return Ok(app.head.clone());
1306 }
1307 return Ok(Term::Application(ApplicationTerm::new(
1308 app.head.clone(),
1309 app.arguments.iter().take(idx).cloned().collect(),
1310 None,
1311 )));
1312 }
1313 _ => term = body,
1314 }
1315 }
1316 }
1317}
1318
1319#[derive(Debug, Clone, PartialEq, Eq)]
1320pub struct BetaRule(pub SymbolUri);
1321impl SizedSolverRule for BetaRule {
1322 fn priority(&self) -> isize {
1323 100_000_000
1324 }
1325 fn display(&self) -> Vec<crate::trace::Displayable> {
1326 ftml_solver_trace::trace!("{ a:A } (", &self.0, " x:A. t)(a) ==> t[x/a]")
1327 }
1328}
1329impl<Split: SplitStrategy> SimplificationRule<Split> for BetaRule {
1330 fn applicable(&self, term: &Term) -> bool {
1331 if let Term::Application(app) = term
1332 && let Term::Bound(op) = &app.head
1333 && let Term::Symbol { uri, .. } = &op.head
1334 {
1335 *uri == self.0 && app.arguments.len() >= op.arguments.len() - 1
1336 } else {
1337 false
1338 }
1339 }
1340 fn apply<'t>(
1341 &self,
1342 mut checker: CheckRef<'t, '_, Split>,
1343 term: &'t Term,
1344 ) -> Result<Term, Option<ftml_ontology::terms::termpaths::TermPath>> {
1345 let Term::Application(app) = term else {
1346 return Err(None);
1347 };
1348 let Term::Bound(fun) = &app.head else {
1349 return Err(None);
1350 };
1351 let Some(BoundArgument::Simple(ret)) = fun.arguments.last() else {
1352 return Err(None);
1353 };
1354 let funargs = &fun.arguments[..fun.arguments.len() - 1];
1355 let actual_args = &app.arguments[..funargs.len()];
1356 let rest_args = &app.arguments[funargs.len()..];
1357 let mut ret = Cow::Borrowed(ret);
1358 for (i, (v, a)) in funargs.iter().zip(actual_args).enumerate() {
1359 checker.counter("Checking argument ", i + 1);
1360 match (v, a) {
1361 (
1362 BoundArgument::Bound(ComponentVar {
1363 var,
1364 tp: Some(tp),
1365 df: None,
1366 }),
1367 Argument::Simple(a),
1368 ) => {
1369 if !checker.check_type(a, tp).ok_or(None)? {
1370 return Err(None);
1371 }
1372 if let Cow::Owned(nr) = &*ret / (var.name(), a) {
1373 ret = Cow::Owned(nr);
1374 }
1375 }
1376 (
1377 BoundArgument::Bound(ComponentVar {
1378 var,
1379 tp: None,
1380 df: None,
1381 }),
1382 Argument::Simple(a),
1383 ) => {
1384 let Some(tp) = checker.infer_var_type(var) else {
1385 checker.add_msg(traceref!(FAIL "untyped variable ",var).into());
1386 return Err(None);
1387 };
1388 if checker.scoped(|checker| checker.check_type(a, &tp)) != Some(true) {
1389 return Err(None);
1390 }
1391 if let Cow::Owned(nr) = &*ret / (var.name(), a) {
1392 ret = Cow::Owned(nr);
1393 }
1394 }
1395 (
1396 BoundArgument::BoundSeq(MaybeSequence::One(ComponentVar {
1397 var,
1398 tp: Some(tp),
1399 df: None,
1400 })),
1401 Argument::Sequence(MaybeSequence::Seq(ts)),
1402 ) => {
1403 let Some(SequenceType::SeqType(tp, _)) = tp.as_sequence_type() else {
1404 checker.failure("Type of sequence variable is not a sequence type");
1405 return Err(None);
1407 };
1408 for t in ts {
1409 if !checker.check_type(t, tp).ok_or(None)? {
1410 return Err(None);
1411 }
1412 }
1413 if let Cow::Owned(nr) =
1414 &*ret / (var.name(), &Term::into_seq(ts.iter().cloned()))
1415 {
1416 ret = Cow::Owned(nr);
1417 }
1418 }
1419 (
1420 BoundArgument::BoundSeq(MaybeSequence::One(ComponentVar {
1421 var,
1422 tp: Some(tp),
1423 df: None,
1424 })),
1425 Argument::Simple(t),
1426 ) => {
1427 if tp.as_sequence_type().is_none() {
1428 checker.failure("Type of sequence variable is not a sequence type");
1429 return Err(None);
1431 }
1432 if !checker.check_type(t, tp).ok_or(None)? {
1433 return Err(None);
1434 }
1435 if let Cow::Owned(nr) = &*ret / (var.name(), t) {
1436 ret = Cow::Owned(nr);
1437 }
1438 }
1439
1440 (
1441 BoundArgument::Bound(ComponentVar {
1442 var,
1443 tp: None,
1444 df: None,
1445 }),
1446 Argument::Sequence(MaybeSequence::Seq(args)),
1447 ) => {
1448 let Some(tp) = checker.infer_var_type(var) else {
1449 checker.add_msg(traceref!(FAIL "untyped variable ",var).into());
1450 return Err(None);
1451 };
1452 let Some(tp) = checker.scoped(|checker| {
1453 checker
1454 .simplify_until(&tp, |_, t| {
1455 t.as_sequence().is_some_and(|s| s.is_concrete())
1456 })
1457 .map(Cow::into_owned)
1458 }) else {
1459 checker.failure("Not a concrete sequence");
1460 return Err(None);
1461 };
1462 let vartps = tp.as_sequence().and_then(|s| s.to_concrete()).ok_or(None)?;
1463 if vartps.len() != args.len() {
1464 checker.failure("sequence lengths don't match");
1465 return Err(None);
1466 }
1467 for (a, tp) in args.iter().zip(vartps) {
1468 if !checker
1469 .scoped(|checker| checker.check_type(a, &tp))
1470 .ok_or(None)?
1471 {
1472 return Err(None);
1473 }
1474 }
1475 }
1476 (
1477 BoundArgument::Bound(ComponentVar {
1478 var,
1479 tp: Some(tp),
1480 df: None,
1481 }),
1482 Argument::Sequence(MaybeSequence::Seq(args)),
1483 ) => {
1484 let ntp = checker
1485 .scoped(|checker| {
1486 checker
1487 .simplify_until(tp, |_, t| {
1488 t.as_sequence().is_some_and(|s| s.is_concrete())
1489 })
1490 .map(Cow::into_owned)
1491 })
1492 .ok_or(None)?;
1493 let Some(vartps) = ntp.as_sequence().and_then(|s| s.to_concrete()) else {
1494 checker.failure("type of bound variable not a concrete sequence");
1495 return Err(None);
1496 };
1497 if vartps.len() != args.len() {
1498 checker.failure("sequence lengths don't match");
1499 return Err(None);
1500 }
1501 for (a, tp) in args.iter().zip(vartps) {
1502 if !checker
1503 .scoped(|checker| checker.check_type(a, &tp))
1504 .ok_or(None)?
1505 {
1506 return Err(None);
1507 }
1508 }
1509 }
1510
1511 (a, b) => {
1512 checker.failure(format!("TODO: {a:?} <-> {b:?}"));
1513 return Err(None);
1514 }
1515 }
1516 }
1517 if rest_args.is_empty() {
1518 Ok(ret.into_owned())
1519 } else {
1520 Ok(Term::Application(ApplicationTerm::new(
1521 ret.into_owned(),
1522 rest_args.iter().cloned().collect(),
1523 None,
1524 )))
1525 }
1526 }
1527}
1528
1529#[derive(Debug, Clone, PartialEq, Eq)]
1530pub struct ApplyRule(pub SymbolUri);
1531impl SizedSolverRule for ApplyRule {
1532 fn display(&self) -> Vec<crate::trace::Displayable> {
1533 ftml_solver_trace::trace!("{ a:A } ", &self.0, "(f,a*) ==> f(a*)")
1534 }
1535 fn priority(&self) -> isize {
1536 isize::MAX
1537 }
1538}
1539impl<Split: SplitStrategy> PreparationRule<Split> for ApplyRule {
1540 fn applicable(&self, _: &CheckRef<'_, '_, Split>, t: &Term) -> bool {
1541 if let Term::Application(app) = t
1542 && let [Argument::Simple(_), Argument::Sequence(_)] = &*app.arguments
1543 && let Term::Symbol { uri, .. } = &app.head
1544 {
1545 *uri == self.0
1546 } else {
1547 false
1548 }
1549 }
1550 fn apply(
1551 &self,
1552 _: &mut CheckRef<'_, '_, Split>,
1553 t: Term,
1554 path: Option<(&mut smallvec::SmallVec<u8, 16>, usize)>,
1555 ) -> std::ops::ControlFlow<Term, Term> {
1556 let Term::Application(app) = t else {
1557 return std::ops::ControlFlow::Continue(t);
1558 };
1559 let [Argument::Simple(f), Argument::Sequence(a)] = &*app.arguments else {
1560 return std::ops::ControlFlow::Continue(Term::Application(app));
1561 };
1562 let ret = Term::Application(ApplicationTerm::new(
1563 f.clone(),
1564 match a {
1565 MaybeSequence::One(o) => {
1566 Box::new([Argument::Sequence(MaybeSequence::One(o.clone()))])
1567 }
1568 MaybeSequence::Seq(ts) => ts.iter().map(|t| Argument::Simple(t.clone())).collect(),
1569 },
1570 app.presentation.clone(),
1575 ));
1576 if let Some((p, i)) = path
1577 && let Some(j) = p.get_mut(i)
1578 {
1579 *j = j.saturating_sub(1);
1580 }
1581 std::ops::ControlFlow::Continue(ret)
1582 }
1583 fn applicable_revert(&self, _: &CheckRef<'_, '_, Split>, _: &Term) -> bool {
1584 false
1585 }
1586 fn revert(&self, _: &CheckRef<'_, '_, Split>, t: Term) -> std::ops::ControlFlow<Term, Term> {
1587 std::ops::ControlFlow::Continue(t)
1588 }
1589}