1pub mod context;
2mod impls;
3pub mod results {
4 pub use ftml_solver_trace::results::*;
5}
6pub mod paragraphs;
7pub mod rules;
8pub mod split;
9pub mod trace {
10 pub use ftml_solver_trace::*;
11}
12pub mod facts;
13pub mod hoas;
14pub mod utils;
16
17use crate::{
18 context::ContextWrap,
19 facts::GlobalFacts,
20 hoas::HOASSymbols,
21 impls::{
22 proving::ProverState,
23 solving::{Solutions, TermExtSolvable, is_solvable_var},
24 },
25 results::{
26 CheckResult, ContentCheckResult, DocumentCheckResult, SymbolCheckResult, TypeCheckResult,
27 },
28 rules::RuleSet,
29 split::{CancelToken, RayonStrategiesDepth, SingleThreadedSplit, SplitStrategy},
30 trace::{CheckLogCow, CheckingTask, PreCheckLog},
31 utils::MutableRefList,
32};
33use flams_math_archives::{
34 artifacts::{ContentUpdate, FileOrString},
35 backend::{AnyBackend, LocalBackend},
36 formats::{BuildResult, TaskDependency},
37};
38use ftml_ontology::{
39 domain::{
40 HasDeclarations,
41 declarations::{AnyDeclarationRef, morphisms::Morphism, symbols::Symbol},
42 modules::Module,
43 },
44 narrative::{
45 documents::Document,
46 elements::{
47 DocumentElementRef, LogicalParagraph, VariableDeclaration, paragraphs::ParagraphKind,
48 },
49 },
50 terms::{ComponentVar, Term, TermContainer, helpers::IntoTerm, termpaths::TermPath},
51 utils::RefTree,
52};
53use ftml_solver_trace::CheckLog;
54use ftml_uris::{Id, IsDomainUri, LeafUri, ModuleUri};
55use smallvec::SmallVec;
56use std::marker::PhantomData;
57
58pub static DUMMY: std::sync::LazyLock<Id> =
59 std::sync::LazyLock::new(|| unsafe { "DUMMY".parse().unwrap_unchecked() });
61
62flams_math_archives::build_target!(CHECK {
63 name: "typecheck",
64 description: "check the validity of formal/complex expressions",
65 run: check
66});
67
68#[allow(clippy::needless_pass_by_value)]
69fn check(task: flams_math_archives::formats::BuildSpec) -> BuildResult {
70 let d = match task.backend.get_document(task.uri) {
71 Ok(d) => d,
72 Err(e) => {
73 return BuildResult {
74 log: FileOrString::Str(
75 format!("Document not found {}: {e}", task.uri).into_boxed_str(),
76 ),
77 result: Err(Vec::new()),
78 };
79 }
80 };
81 let mut checker =
82 Checker::<SingleThreadedSplit>::new(task.backend.clone());
83 match checker.check_document(&d) {
84 Ok((logs, modules)) => {
85 let log = logs.to_json();
86 BuildResult {
87 log: FileOrString::Str(log.into_boxed_str()),
88 result: Ok(Some(Box::new(ContentUpdate {
89 document: Some(d),
90 modules,
91 }) as _)),
92 }
93 }
94 Err(e) => BuildResult {
95 log: FileOrString::Str(format!("Module missing: {e}").into_boxed_str()),
96 result: Err(vec![TaskDependency::Logical {
97 uri: e,
98 strict: true,
99 }]),
100 },
101 }
102}
103
104type BigSet<T> = dashmap::DashSet<T, rustc_hash::FxBuildHasher>;
105
106pub struct SubtermCheckResult {
125 pub simplified: Term,
126 pub inferred_type: Option<Term>,
127 pub context: Vec<ComponentVar>,
128 pub log: CheckLog,
129}
130
131pub struct Checker<Split: SplitStrategy> {
132 backend: AnyBackend,
133 rules: RuleSet<Split>,
134 modules: BigSet<Module>,
135 documents: BigSet<Document>,
136 facts: GlobalFacts,
137 hoas: Option<HOASSymbols>,
138 implicits: std::sync::atomic::AtomicUsize,
139 current: Vec<LeafUri>,
140 context: Vec<ModuleUri>,
141 __phantom: PhantomData<Split>,
142}
143
144pub struct CheckRef<'c, 'i, Split: SplitStrategy> {
145 pub(crate) top: &'c Checker<Split>,
146 pub(crate) context: ContextWrap<'c, 'i>,
147 pub(crate) proof_state: &'i ProverState,
148 pub(crate) solutions: MutableRefList<'i, Solutions>,
149 messages: &'i mut SmallVec<CheckLogCow<'c>, 2>,
150 pub(crate) cancel: &'i CancelToken<'i, Split::CancelToken>,
151 added: u8,
152 traced: bool,
153}
154
155macro_rules! update {
156 ($slf:ident $t:ident if $bind:expr) => {
157 if $bind && let Some(t) = $slf.bind_implicits(&$t) {
158 $t = t;
160 }
161 };
162}
163
164#[derive(Default)]
165pub struct CheckerCache {
166 modules: BigSet<Module>,
167 documents: BigSet<Document>,
168}
169
170impl<Split: SplitStrategy> Checker<Split> {
171 fn reset(&self) {
172 self.implicits
173 .store(0, std::sync::atomic::Ordering::Release);
174 }
175 pub fn into_cache(self) -> CheckerCache {
176 CheckerCache {
177 modules: self.modules,
178 documents: self.documents,
179 }
180 }
181 pub fn set_cache(&mut self, cache: CheckerCache) {
182 self.modules = cache.modules;
183 self.documents = cache.documents;
184 }
185 #[must_use]
186 pub fn new(backend: AnyBackend) -> Self {
187 Self {
188 backend,
189 modules: BigSet::default(),
190 documents: BigSet::default(),
191 rules: RuleSet::default(),
192 current: Vec::default(),
193 facts: GlobalFacts::default(),
194 context: Vec::default(),
195 hoas: None,
196 implicits: std::sync::atomic::AtomicUsize::new(1),
197 __phantom: PhantomData,
198 }
199 }
200
201 fn set_hoas(&mut self) {
202 self.hoas = HOASSymbols::get(self);
203 }
204 pub fn hoas(&self) -> Option<&HOASSymbols> {
205 self.hoas.as_ref()
206 }
207
208 pub fn check_document(
209 &mut self,
210 d: &Document,
211 ) -> Result<(DocumentCheckResult, Vec<Module>), ModuleUri> {
212 let mut modules = Vec::new();
221 let mut results = Vec::new();
222 self.documents.insert(d.clone());
223 for e in d.dfs() {
224 match e {
225 DocumentElementRef::Module { module, .. } => {
227 modules.push(module.clone());
229 }
230 _ => (),
231 }
232 }
233 let modules: Vec<_> = modules
235 .into_iter()
236 .filter_map(|uri| self.get_module(&uri).ok())
237 .collect();
238 tracing::trace!("Rules: {:?}", self.rules);
239 for e in d.dfs() {
240 match e {
241 DocumentElementRef::Module { module, .. }
242 | DocumentElementRef::UseModule { uri: module, .. } => {
243 self.set_context(vec![module.clone()])?;
244 }
249 DocumentElementRef::Morphism { morphism, .. } => {
250 let top = morphism.clone().simple_module();
251 let Some(m) = self
252 .get_module(top.module_uri())
253 .ok()
254 .and_then(|m| m.get_as::<Morphism>(top.name()))
255 else {
256 results.push(CheckResult::Missing(morphism.module.clone()));
257 continue;
258 };
259 }
264 DocumentElementRef::VariableDeclaration(v) => {
265 if let Some(r) = self.check_variable(v) {
266 results.push(CheckResult::Variable(v.uri.clone(), r));
267 }
268 }
269 DocumentElementRef::SymbolDeclaration(uri) => {
270 let top = uri.clone().simple_module();
271 if let Some(s) = modules
272 .iter()
273 .find(|m| m.uri == top.module)
274 .and_then(|m| m.find::<Symbol>(top.name.steps()))
275 {
276 if let Some(r) = self.check_symbol(s) {
277 results.push(CheckResult::Content(ContentCheckResult::Symbol(
278 uri.clone(),
279 r,
280 )));
281 }
282 } else {
283 results.push(CheckResult::Missing(uri.module.clone()));
284 }
285 }
286 DocumentElementRef::Term(top) => {
287 self.reset();
289 tracing::debug!("Checking term {:?}", top.get_parsed().debug_short());
290 let (unks, tm) = self.prepare(None, top.get_parsed().clone());
292 let (t, unks, log) = self.infer_type(Some(unks), &tm);
293 let t = t.map(|t| {
294 self.wrap_none(Some(unks), |slf| slf.revert_prepare(slf.subst(t)))
295 .1
296 });
297 if let Some(t) = &t {
298 top.set_type(t.clone());
299 }
300 results.push(CheckResult::Term {
301 uri: top.uri.clone(),
302 inferred: t,
303 log: CheckLog::from_pre(log, &mut |t| self.revert_prepare(t)),
304 });
305 }
306 DocumentElementRef::Paragraph(
307 p @ LogicalParagraph {
308 kind: ParagraphKind::Proof,
309 fors,
310 ..
311 },
312 ) if fors.len() == 1 => {
313 self.reset();
315 results.extend(self.check_proof(p));
316 }
317 DocumentElementRef::Paragraph(
318 p @ LogicalParagraph {
319 kind: ParagraphKind::Assertion,
320 fors,
321 ..
322 },
323 ) if p.fors.iter().any(|(_, t)| t.is_some()) => {
324 self.reset();
326 if let Some(r) = self.check_assertion(p) {
327 results.extend(r);
328 }
329 }
330 DocumentElementRef::Paragraph(
331 p @ LogicalParagraph {
332 kind, fors, styles, ..
333 },
334 ) if kind.is_definition_like(styles) && p.fors.iter().any(|(_, t)| t.is_some()) => {
335 self.reset();
337 results.extend(self.check_definition(p));
338 }
339 _ => (),
340 }
341 }
342 Ok((
343 DocumentCheckResult {
344 uri: d.uri.clone(),
345 checks: results.into_boxed_slice(),
346 },
347 modules,
348 ))
349 }
350
351 pub fn check_module(&mut self, m: &Module) -> Result<Vec<ContentCheckResult>, ModuleUri> {
353 self.modules.insert(m.clone());
354 self.set_context(vec![m.uri.clone()])?;
355 let mut ret = Vec::new();
357 for d in m.dfs().filter_map(|e| {
358 if let AnyDeclarationRef::Symbol(s) = e {
359 Some(s)
360 } else {
361 None
362 }
363 }) {
364 if let Some(r) = self.check_symbol(d) {
365 ret.push(ContentCheckResult::Symbol(d.uri.clone(), r));
366 }
367 }
368 Ok(ret)
369 }
370
371 pub fn check_subterm_term(&mut self, term: Term, sub: Term) -> Option<SubtermCheckResult> {
372 self.reset();
373 let (unks, nterm) = self.wrap_none(None, |mut slf| {
374 let (s, r) = slf.prepare(term, None);
375 slf.merge_solutions(s);
376 r
377 });
378 let (unks, nsub) = self.wrap_none(Some(unks), |mut slf| {
379 let (s, r) = slf.prepare(sub, None);
380 slf.merge_solutions(s);
381 r
382 });
383 let ctx = nterm
384 .path_of_subterm_with_ctx(&nsub)
385 .map_or(Vec::new(), |p| p.0);
386 Some(self.check_subterm_i(unks, &nsub, ctx))
387 }
388
389 pub fn check_subterm_path(
390 &mut self,
391 term: Term,
392 mut path: TermPath,
393 ) -> Option<SubtermCheckResult> {
394 self.reset();
396 let (unks, nterm) = self.wrap_none(None, |mut slf| {
397 let (s, r) = slf.prepare(term, Some(&mut path));
398 slf.merge_solutions(s);
399 r
400 });
401 let (ctx, t) = nterm.subterm_at_path(&path)?;
402 Some(self.check_subterm_i(unks, t, ctx))
404 }
405
406 fn check_subterm_i(
407 &self,
408 unks: Solutions,
409 sub: &Term,
410 ctx: Vec<&ComponentVar>,
411 ) -> SubtermCheckResult {
412 let mut ctx = ctx.into_iter().cloned().rev().collect::<Vec<_>>();
413 let mut nt = sub.clone();
414 let (r, s, log) = self.wrap_task(CheckingTask::Inference(sub), Some(unks), |mut slf| {
415 let allvars = sub.free_variables();
416 for v in allvars {
417 if is_solvable_var(v).is_none() && !ctx.iter().any(|cv| cv.var == *v) {
418 let tp = slf.infer_var_type_i(v);
419 let df = slf.get_var_definiens(v);
420 ctx.push(ComponentVar {
421 var: v.clone(),
422 tp,
423 df,
424 });
425 }
426 }
427 let mut i = 0;
428 while let Some(vd) = ctx.get(i) {
429 i += 1;
430 let tp = vd.tp.clone();
431 let df = vd.df.clone();
432 if let Some(t) = tp {
433 let allvars = t
434 .free_variables()
435 .into_iter()
436 .filter(|v| !ctx.iter().any(|cv| cv.var == **v))
437 .cloned()
438 .collect::<smallvec::SmallVec<_, 2>>();
439 for v in allvars {
440 if is_solvable_var(&v).is_none() {
441 let tp = slf.infer_var_type_i(&v);
442 let df = slf.get_var_definiens(&v);
443 ctx.push(ComponentVar { var: v, tp, df });
444 }
445 }
446 }
447 if let Some(t) = df {
448 let allvars = t
449 .free_variables()
450 .into_iter()
451 .filter(|v| !ctx.iter().any(|cv| cv.var == **v))
452 .cloned()
453 .collect::<smallvec::SmallVec<_, 2>>();
454 for v in allvars {
455 if is_solvable_var(&v).is_none() {
456 let tp = slf.infer_var_type_i(&v);
457 let df = slf.get_var_definiens(&v);
458 ctx.push(ComponentVar { var: v, tp, df });
459 }
460 }
461 }
462 }
463 ctx.reverse();
464 for c in &ctx {
465 slf.extend_context(c);
466 }
467
468 let tp = slf
469 .infer_type(sub)
470 .map(|t| slf.subst(slf.revert_prepare(t)));
471 let simp = slf.simplify_full(true, sub).unwrap_or_else(|| sub.clone());
472 nt = slf.subst(slf.revert_prepare(simp));
473 tp
474 });
475 let (_, log) = self.wrap_none(None, |slf| {
489 for c in &mut ctx {
490 if let Some(tp) = c.tp.take() {
491 c.tp = Some(slf.revert_prepare(tp));
492 }
493 if let Some(df) = c.df.take() {
494 c.df = Some(slf.revert_prepare(df));
495 }
496 }
497 CheckLog::from_pre(log, &mut |t| slf.revert_prepare(t))
498 });
499 SubtermCheckResult {
501 simplified: nt,
502 inferred_type: r,
503 context: ctx,
504 log,
505 }
506 }
507
508 fn check_components(
509 &self,
510 tpc: &TermContainer,
511 dfc: &TermContainer,
512 bind: bool,
513 ) -> Option<SymbolCheckResult> {
514 self.reset();
515 let tp = tpc.get_parsed();
516 let df = dfc.get_parsed();
517 let df = if df.is_some_and(Term::is_marker) {
518 None
519 } else {
520 df
521 };
522 match (tp, df) {
524 (Some(tp), None) => {
525 tracing::debug!("Checking Type: {:?}", tp.debug_short());
526 let (unks, tp) = self.prepare(None, tp.clone());
528 let (b, unks, mut l) = self.check_inhabitable(Some(unks), &tp);
529 let mut tp = self.wrap_none(Some(unks), |slf| slf.subst(tp)).1;
530 if tp.has_solvable() {
531 l.push(PreCheckLog::Msg(
532 vec![format!("Unsolved unkowns remain: {:?}", tp.solvables()).into()],
533 ftml_solver_trace::MessageLevel::Failure,
534 ));
535 return Some(SymbolCheckResult::TypeOnly {
536 result: TypeCheckResult {
537 success: false,
538 log: CheckLog::from_pre(l, &mut |t| self.revert_prepare(t)),
539 },
540 });
541 }
542 update!(self tp if bind);
543 tpc.set_checked(tp);
544 Some(SymbolCheckResult::TypeOnly {
545 result: TypeCheckResult {
546 success: b.unwrap_or(false),
547 log: CheckLog::from_pre(l, &mut |t| self.revert_prepare(t)),
548 },
549 })
550 }
551 (None, Some(df)) => Some(self.df_only(df, dfc, tpc, bind, false)),
552 (Some(tp), Some(df)) => {
553 tracing::debug!("Checking Type and Definiens");
554 if ftml_uris::metatheory::AUTO_PROVE.term_is(df) {
555 Some(self.infer_df(dfc, tp, tpc, bind))
556 } else {
557 Some(self.df_and_tp(df, dfc, tp, tpc, bind, false))
559 }
560 }
561 (None, None) => None,
562 }
563 }
564
565 fn df_only(
566 &self,
567 df: &Term,
568 dfc: &TermContainer,
569 tpc: &TermContainer,
570 bind: bool,
571 set_presentation: bool,
572 ) -> SymbolCheckResult {
573 tracing::debug!("Checking Definiens");
574 let (unks, df) = self.prepare(None, df.clone());
576
577 let (tp, unks, mut l) = self.infer_type(Some(unks), &df);
578 let mut df = self.wrap_none(Some(unks), |slf| slf.subst(df)).1;
579
580 if df.has_solvable() {
581 l.push(PreCheckLog::Msg(
582 vec![format!("Unsolved unkowns remain: {:?}", df.solvables()).into()],
583 ftml_solver_trace::MessageLevel::Failure,
584 ));
585 return SymbolCheckResult::DefiniensOnly {
586 inferred: None,
587 log: CheckLog::from_pre(l, &mut |t| self.revert_prepare(t)),
588 };
589 }
590
591 update!(self df if bind);
592 if set_presentation {
593 dfc.set_presentation(self.revert_prepare(df.clone()));
594 }
595 dfc.set_checked(df);
596
597 if let Some(mut tp) = tp {
598 if tp.has_solvable() {
599 l.push(PreCheckLog::Msg(
600 vec![format!("Unsolved unkowns remain: {:?}", tp.solvables()).into()],
601 ftml_solver_trace::MessageLevel::Failure,
602 ));
603 return SymbolCheckResult::DefiniensOnly {
604 inferred: None,
605 log: CheckLog::from_pre(l, &mut |t| self.revert_prepare(t)),
606 };
607 }
608 update!(self tp if bind);
609 tpc.set_checked(tp.clone());
610 let tp = self.revert_prepare(tp);
611 tpc.set_presentation(tp.clone());
612 SymbolCheckResult::DefiniensOnly {
613 inferred: Some(tp),
614 log: CheckLog::from_pre(l, &mut |t| self.revert_prepare(t)),
615 }
616 } else {
617 SymbolCheckResult::DefiniensOnly {
618 inferred: None,
619 log: CheckLog::from_pre(l, &mut |t| self.revert_prepare(t)),
620 }
621 }
622 }
623
624 fn infer_df(
625 &self,
626 dfc: &TermContainer,
627 tp: &Term,
628 tpc: &TermContainer,
629 bind: bool,
630 ) -> SymbolCheckResult {
631 let (tunks, tp) = self.prepare(None, tp.clone());
632 let (b, tunks, mut l) = self.check_inhabitable(Some(tunks), &tp);
633 if b.is_some_and(|b| !b) {
634 let mut tp = self.wrap_none(Some(tunks), |slf| slf.subst(tp)).1;
635 update!(self tp if bind);
636 tpc.set_checked(tp);
637 return SymbolCheckResult::Both {
638 inhabitable: TypeCheckResult {
639 success: false,
640 log: CheckLog::from_pre(l, &mut |t| self.revert_prepare(t)),
641 },
642 matches: None,
643 };
644 }
645 let (tunks, mut tp) = self.wrap_none(Some(tunks), |slf| slf.subst(tp));
646
647 if tp.has_solvable() {
648 l.push(PreCheckLog::Msg(
649 vec![format!("Unsolved unkowns remain: {:?}", tp.solvables()).into()],
650 ftml_solver_trace::MessageLevel::Failure,
651 ));
652 return SymbolCheckResult::TypeOnly {
653 result: TypeCheckResult {
654 success: false,
655 log: CheckLog::from_pre(l, &mut |t| self.revert_prepare(t)),
656 },
657 };
658 }
659 let (ndf, _, mut l2) = self.prove(Some(tunks), &tp);
660
661 update!(self tp if bind);
662 tpc.set_checked(tp);
663 if let Some(mut df) = ndf {
664 if df.has_solvable() {
665 l2.push(PreCheckLog::Msg(
666 vec![format!("Unsolved unkowns remain: {:?}", df.solvables()).into()],
667 ftml_solver_trace::MessageLevel::Failure,
668 ));
669 return SymbolCheckResult::Both {
670 inhabitable: TypeCheckResult {
671 success: true,
672 log: CheckLog::from_pre(l, &mut |t| self.revert_prepare(t)),
673 },
674 matches: Some(TypeCheckResult {
675 success: false,
676 log: CheckLog::from_pre(l2, &mut |t| self.revert_prepare(t)),
677 }),
678 };
679 }
680
681 update!(self df if bind);
682 dfc.set_checked(df.clone());
683 let df = self.revert_prepare(df);
684 dfc.set_presentation(df);
685 SymbolCheckResult::Both {
686 inhabitable: TypeCheckResult {
687 success: true,
688 log: CheckLog::from_pre(l, &mut |t| self.revert_prepare(t)),
689 },
690 matches: Some(TypeCheckResult {
691 success: true,
692 log: CheckLog::from_pre(l2, &mut |t| self.revert_prepare(t)),
693 }),
694 }
695 } else {
696 SymbolCheckResult::Both {
697 inhabitable: TypeCheckResult {
698 success: true,
699 log: CheckLog::from_pre(l, &mut |t| self.revert_prepare(t)),
700 },
701 matches: Some(TypeCheckResult {
702 success: false,
703 log: CheckLog::from_pre(l2, &mut |t| self.revert_prepare(t)),
704 }),
705 }
706 }
707 }
708
709 fn df_and_tp(
710 &self,
711 df: &Term,
712 dfc: &TermContainer,
713 tp: &Term,
714 tpc: &TermContainer,
715 bind: bool,
716 set_df_presentation: bool,
717 ) -> SymbolCheckResult {
718 let (tunks, tp) = self.prepare(None, tp.clone());
719 let (b, tunks, mut l) = self.check_inhabitable(Some(tunks), &tp);
720 if b.is_some_and(|b| !b) {
721 let mut tp = self.wrap_none(Some(tunks), |slf| slf.subst(tp)).1;
722 if tp.has_solvable() {
723 l.push(PreCheckLog::Msg(
724 vec![format!("Unsolved unkowns remain: {:?}", tp.solvables()).into()],
725 ftml_solver_trace::MessageLevel::Failure,
726 ));
727 return SymbolCheckResult::TypeOnly {
728 result: TypeCheckResult {
729 success: false,
730 log: CheckLog::from_pre(l, &mut |t| self.revert_prepare(t)),
731 },
732 };
733 }
734
735 update!(self tp if bind);
736 tpc.set_checked(tp);
737 return SymbolCheckResult::Both {
738 inhabitable: TypeCheckResult {
739 success: false,
740 log: CheckLog::from_pre(l, &mut |t| self.revert_prepare(t)),
741 },
742 matches: None,
743 };
744 }
745 let (tunks, mut tp) = self.wrap_none(Some(tunks), |slf| slf.subst(tp));
746 tracing::debug!("Checking definiens");
747 let (dunks, df) = self.prepare(Some(tunks), df.clone());
748
749 let (b, unks, mut l2) = self.check_type(Some(dunks), &df, &tp);
750 let (unks, mut df) = self.wrap_none(Some(unks), |slf| slf.subst(df));
751
752 if df.has_solvable() {
753 l2.push(PreCheckLog::Msg(
754 vec![format!("Unsolved unkowns remain: {:?}", df.solvables()).into()], ftml_solver_trace::MessageLevel::Failure,
760 ));
761 return SymbolCheckResult::Both {
762 inhabitable: TypeCheckResult {
763 success: true,
764 log: CheckLog::from_pre(l, &mut |t| self.revert_prepare(t)),
765 },
766 matches: Some(TypeCheckResult {
767 success: false,
768 log: CheckLog::from_pre(l2, &mut |t| self.revert_prepare(t)),
769 }),
770 };
771 }
772
773 update!(self tp if bind);
774 update!(self df if bind);
775 tracing::debug!("Result:\n{:?}\n : {:?}", df.debug_short(), tp.debug_short());
776 tpc.set_checked(tp);
777 if set_df_presentation {
778 dfc.set_presentation(self.revert_prepare(df.clone()));
779 }
780 dfc.set_checked(df);
781 SymbolCheckResult::Both {
782 inhabitable: TypeCheckResult {
783 success: true,
784 log: CheckLog::from_pre(l, &mut |t| self.revert_prepare(t)),
785 },
786 matches: Some(TypeCheckResult {
787 success: b.unwrap_or(false),
788 log: CheckLog::from_pre(l2, &mut |t| self.revert_prepare(t)),
789 }),
790 }
791 }
792
793 pub fn check_symbol(&mut self, s: &Symbol) -> Option<SymbolCheckResult> {
795 tracing::debug!("Checking Symbol {}", s.uri);
796 tracing::trace!("{s:?}");
797 self.current.push(s.uri.clone().into());
798 let r = self.check_components(&s.data.tp, &s.data.df, true);
799 self.current.pop();
800 self.add_fact(s);
801 r
802 }
803
804 pub fn check_variable(&mut self, s: &VariableDeclaration) -> Option<SymbolCheckResult> {
806 tracing::debug!("Checking Variable {}", s.uri);
807 self.current.push(s.uri.clone().into());
808 let r = self.check_components(&s.data.tp, &s.data.df, false);
809 self.current.pop();
810 r
811 }
812
813 fn check_type(
814 &self,
815 unknowns: Option<Solutions>,
816 tm: &Term,
817 tp: &Term,
818 ) -> (Option<bool>, Solutions, PreCheckLog) {
819 self.wrap_task(CheckingTask::HasType(tm, tp), unknowns, |mut slf| {
820 slf.check_type_i(tm, tp)
821 })
822 }
823
824 fn prove(
825 &self,
826 unknowns: Option<Solutions>,
827 goal: &Term,
828 ) -> (Option<Term>, Solutions, PreCheckLog) {
829 self.wrap_task(CheckingTask::Proving(goal), unknowns, |mut slf| {
830 slf.prove(goal)
831 })
832 }
833
834 fn check_subtype(
835 &self,
836 unknowns: Option<Solutions>,
837 sub: &Term,
838 sup: &Term,
839 ) -> (Option<bool>, Solutions, PreCheckLog) {
840 self.wrap_task(CheckingTask::Subtype(sub, sup), unknowns, |mut slf| {
841 slf.check_subtype_i(sub, sup)
842 })
843 }
844
845 fn infer_type(
846 &self,
847 unknowns: Option<Solutions>,
848 t: &Term,
849 ) -> (Option<Term>, Solutions, PreCheckLog) {
850 self.wrap_task(CheckingTask::Inference(t), unknowns, |mut slf| {
851 slf.infer_type_i(t).map(|t| slf.subst(t))
852 })
853 }
854
855 fn check_inhabitable(
856 &self,
857 unknowns: Option<Solutions>,
858 t: &Term,
859 ) -> (Option<bool>, Solutions, PreCheckLog) {
860 self.wrap_task(CheckingTask::Inhabitable(t), unknowns, |mut slf| {
861 slf.check_inhabitable_i(t)
862 })
863 }
864
865 fn prepare(&self, unks: Option<Solutions>, t: Term) -> (Solutions, Term) {
866 self.wrap_none(unks, |mut slf| {
867 let (sols, r) = slf.prepare(t, None);
868 slf.merge_solutions(sols);
869 r
870 })
871 }
872 fn revert_prepare(&self, t: Term) -> Term {
873 self.wrap_none(None, |slf| slf.revert_prepare(t)).1
874 }
875
876 fn check_morphism(&mut self, m: &Morphism) -> Option<Vec<CheckResult>> {
877 let mut ret = Vec::new();
878 for d in m.declarations() {
879 match d {
880 AnyDeclarationRef::Symbol(s) => {
881 if let Some(r) = self.check_components(&s.data.tp, &s.data.df, true) {
885 ret.push(CheckResult::Content(ContentCheckResult::Symbol(
886 s.uri.clone(),
887 r,
888 )));
889 }
890 }
891 _ => (),
892 }
893 }
894 Some(ret)
895 }
896
897 pub fn add_modules(&mut self, modules: Vec<Module>) -> Result<(), ModuleUri> {
899 let mut todos = Vec::new();
900 for m in modules {
901 let _ = self.modules.remove(&m);
902 todos.push(m.uri.clone());
903 self.modules.insert(m);
904 }
905 self.set_context(todos)?;
906 Ok(())
907 }
908
909 pub fn set_context(&mut self, m: Vec<ModuleUri>) -> Result<(), ModuleUri> {
911 let new = self.sort(m);
912 for i in self.context.len() - new..self.context.len() {
913 let uri = &self.context[i];
914 let m = self.get_module(uri).map_err(|()| uri.clone())?;
915 self.load_context(&m);
916 }
917 Ok(())
918 }
919
920 fn load_context(&mut self, m: &Module) {
921 tracing::trace!("Loading: {}", m.uri);
923 for d in m.dfs() {
924 match d {
925 AnyDeclarationRef::Symbol(s) => {
937 let markers = self.rules.marker().len();
938 for e in Split::SYMBOL_EXTRACTORS {
939 e(s, &mut self.rules);
940 }
941 if self.rules.marker().len() > markers {
942 self.set_hoas();
943 }
944 self.add_fact(s);
945 }
946 AnyDeclarationRef::Rule { id, parameters, .. } => {
947 tracing::debug!("Rule: {id}");
948 let markers = self.rules.marker().len();
949 if let Some(rule) = Split::RULE_EXTRACTORS
950 .iter()
951 .find_map(|(s, f)| if id.as_ref() == *s { Some(f) } else { None })
952 {
953 let parameters = parameters
954 .iter()
955 .map(|t| self.prepare(None, t.clone()).1)
956 .collect::<Vec<_>>();
957 rule(¶meters, &mut self.rules);
958 }
959 if self.rules.marker().len() > markers {
960 self.set_hoas();
961 }
962 }
963 _ => (),
964 }
965 }
966 }
967
968 fn sort(&mut self, modules: Vec<ModuleUri>) -> usize {
969 let mut ctx = std::mem::take(&mut self.context);
970 let new = topo_sort(modules, &mut ctx, |uri| self.get_module(uri).ok());
971 self.context = ctx;
972 new
973 }
974}
975
976#[allow(clippy::useless_let_if_seq)]
977fn topo_sort(
978 mut new: Vec<ModuleUri>,
979 sorted: &mut Vec<ModuleUri>,
980 get: impl Fn(&ModuleUri) -> Option<Module>,
981) -> usize {
982 let mut added = 0;
983 while let Some(uri) = new.last() {
984 if !uri.is_top() || sorted.contains(uri) {
985 let _ = new.pop();
986 continue;
987 }
988 let Some(m) = get(uri) else {
989 sorted.push(unsafe { new.pop().unwrap_unchecked() });
991 added += 1;
992 continue;
993 };
994 let curr = new.len();
995 let mut changed = false;
998 if let Some(e) = m.meta_module.as_ref()
999 && !sorted.contains(e)
1000 {
1001 new.insert(curr, e.clone());
1002 changed = true;
1003 }
1004 for e in m.dfs() {
1005 let AnyDeclarationRef::Import { uri, .. } = e else {
1006 continue;
1007 };
1008 if !uri.is_top() || sorted.contains(uri) {
1009 continue;
1010 }
1011 new.insert(curr, uri.clone());
1012 changed = true;
1013 }
1014 if !changed {
1015 sorted.push(unsafe { new.pop().unwrap_unchecked() });
1017 added += 1;
1018 }
1019 }
1020 added
1021}