Skip to main content

ftml_solver/
lib.rs

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;
14//pub mod patterns;
15pub 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    // SAFETY: "DUMMY" is a valid ID
60    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::</*RayonStrategiesDepth<4>*/ 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
106/*
107static MINIMAL_STACK: std::sync::atomic::AtomicUsize =
108    std::sync::atomic::AtomicUsize::new(usize::MAX - 10);
109pub fn minimal_stack() -> usize {
110    MINIMAL_STACK.load(std::sync::atomic::Ordering::Acquire)
111}
112pub(crate) fn update_stack() {
113    let curr = MINIMAL_STACK.load(std::sync::atomic::Ordering::Acquire);
114    let Some(rem) = stacker::remaining_stack() else {
115        return;
116    };
117    let min = curr.min(rem);
118    if min < curr {
119        MINIMAL_STACK.store(min, std::sync::atomic::Ordering::Release);
120    }
121}
122 */
123
124pub 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            //println!("update: {:?}", t); //t.debug_short());
159            $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        /*tracing::error!(
213            "Current: {}",
214            bytesize::ByteSize::b(stacker::remaining_stack().expect("wut") as _)
215                .display()
216                .iec_short()
217        );*/
218
219        //let mut all = Vec::new();
220        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::UseModule { uri: module, .. } => all.push(module.clone()),
226                DocumentElementRef::Module { module, .. } => {
227                    //all.push(module.clone());
228                    modules.push(module.clone());
229                }
230                _ => (),
231            }
232        }
233        //self.set_context_i(all)?;
234        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                    //let _ = self.get_module(module).map_err(|_| module.clone())?;
245                    /*if self.get_module(module).is_err() {
246                        results.push(CheckResult::Missing(module.clone()));
247                    }*/
248                }
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                    /*m.initialize(&mut |uri| self.get_module_like(uri).map_err(|()| "not found"));
260                    if let Some(r) = self.check_morphism(&m) {
261                        results.extend(r.into_iter());
262                    }*/ // let's not touch morphisms for now
263                }
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.set_hoas();
288                    self.reset();
289                    tracing::debug!("Checking term {:?}", top.get_parsed().debug_short());
290                    //println!("All rules: {:#?}", self.rules);
291                    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.set_hoas();
314                    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.set_hoas();
325                    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.set_hoas();
336                    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    // TODO return checked Module
352    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        //self.set_hoas();
356        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.set_hoas();
395        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        //ctx.reverse();
403        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        /*
476        let mut frees = nt.free_variables();
477        for v in r.as_ref().map(|t| t.free_variables()).unwrap_or_default() {
478            if !frees.contains(&v) {
479                frees.push(v);
480            }
481        }
482         */
483        /*let context = ctx
484        .into_iter()
485        //.filter(|v| frees.iter().any(|f| f.name() == v.var.name()))
486        .cloned()
487        .collect();*/
488        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        //drop(frees);
500        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        //self.set_hoas();
523        match (tp, df) {
524            (Some(tp), None) => {
525                tracing::debug!("Checking Type: {:?}", tp.debug_short());
526                //tracing::debug!("Facts: {:#?}", self.facts);
527                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                    //tracing::debug!("Facts: {:#?}", self.facts);
558                    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        //tracing::debug!("Facts: {:#?}", self.facts);
575        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()], /*format!(
755                                                                                           "Unsolved unkowns remain in {:?}\n\n{unks:#?}",
756                                                                                           df.debug_short()
757                                                                                       )
758                                                                                       .into()*/
759                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    // TODO return checked term
794    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    // TODO return checked term
805    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                    // TODO:
882                    // - check that a *refined type* is a subtype of the original type's translation
883                    // - check that an *assigned definies* is ???? the original definiens
884                    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    /// #### Errors
898    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    /// #### Errors
910    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        //, todos: &mut Vec<ModuleUri>) {
922        tracing::trace!("Loading: {}", m.uri);
923        for d in m.dfs() {
924            match d {
925                /*
926                AnyDeclarationRef::Import { uri: m, .. } if !self.modules.contains(m) => {
927                    if m.is_top() {
928                        todos.push(m.clone());
929                    } else {
930                        let uri = !m.clone();
931                        if !self.modules.contains(&uri) {
932                            todos.push(uri);
933                        }
934                    }
935                } */
936                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(&parameters, &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            // SAFETY: uris.last() == Some(uri)
990            sorted.push(unsafe { new.pop().unwrap_unchecked() });
991            added += 1;
992            continue;
993        };
994        let curr = new.len();
995        //println!("Sorting {uri}");
996
997        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            // SAFETY: uris.last() == Some(uri)
1016            sorted.push(unsafe { new.pop().unwrap_unchecked() });
1017            added += 1;
1018        }
1019    }
1020    added
1021}