1use std::borrow::Cow;
2
3use ftml_ontology::terms::{ComponentVar, Term};
4use ftml_solver_trace::CheckingTask;
5
6use crate::{
7 context::ContextWrap,
8 impls::solving::{Solutions, is_solvable_var},
9};
10
11pub type Judgments<'c> = rustc_hash::FxHashSet<(Vec<ComponentVar>, CachedJudgment)>;
12pub type Simplifications = rustc_hash::FxHashMap<Term, Vec<(Vec<ComponentVar>, Term)>>;
13
14#[derive(Default, Debug)]
15pub struct JudgmentCacheBase<'c> {
16 current: Vec<CheckingTask<'c>>,
17 cached: std::sync::Arc<parking_lot::Mutex<Judgments<'c>>>,
18 simplifications: std::sync::Arc<parking_lot::Mutex<Simplifications>>,
19}
20impl<'c> JudgmentCacheBase<'c> {
21 pub fn make_ref(&mut self) -> JudgmentCache<'c, '_> {
22 JudgmentCache {
23 current: &mut self.current,
24 cached: self.cached.clone(),
25 simplifications: self.simplifications.clone(),
26 }
27 }
28}
29
30#[derive(Debug)]
31pub struct JudgmentCache<'c, 'i> {
32 current: &'i mut Vec<CheckingTask<'c>>,
33 pub(crate) cached: std::sync::Arc<parking_lot::Mutex<Judgments<'c>>>,
34 simplifications: std::sync::Arc<parking_lot::Mutex<Simplifications>>,
35}
36impl<'c> JudgmentCache<'c, '_> {
37 pub fn copied(&mut self) -> JudgmentCache<'c, '_> {
38 JudgmentCache {
39 current: &mut self.current,
40 cached: self.cached.clone(),
41 simplifications: self.simplifications.clone(),
42 }
43 }
44 pub fn new_base(&self) -> JudgmentCacheBase<'c> {
45 JudgmentCacheBase {
46 current: self.current.clone(),
47 cached: self.cached.clone(),
48 simplifications: self.simplifications.clone(),
49 }
50 }
51 pub fn add_simplification(&self, from: &Term, to: &Term, ctx: &[Cow<ComponentVar>]) {
52 return ();
53 let mut vars = from.free_variables();
54
55 let mut nctx = Vec::new();
56 while let Some(v) = vars.pop() {
57 if is_solvable_var(v).is_some() {
58 return;
59 }
60 if let Some(v) = ctx.iter().rev().find(|vd| vd.var.name() == v.name()) {
61 nctx.push(v.clone().into_owned());
62 if let Some(tp) = v.tp.as_ref() {
64 for v in tp.free_variables() {
65 if !vars.contains(&v) && !nctx.iter().any(|nv| nv.var.name() == v.name()) {
66 vars.push(v);
67 }
68 }
69 }
70 if let Some(df) = v.df.as_ref() {
71 for v in df.free_variables() {
72 if !vars.contains(&v) && !nctx.iter().any(|nv| nv.var.name() == v.name()) {
73 vars.push(v);
74 }
75 }
76 }
77 }
78 }
79 nctx.reverse();
80 let mut simps = self.simplifications.lock();
81 match simps.entry(from.clone()) {
82 std::collections::hash_map::Entry::Vacant(v) => {
83 v.insert_entry(vec![(nctx, to.clone())]);
84 }
85 std::collections::hash_map::Entry::Occupied(mut o) => {
86 let o = o.get_mut();
87 o.push((nctx, to.clone()));
88 }
89 }
90 }
91 pub fn get_simplification(&self, from: &Term, ctx: &[Cow<ComponentVar>]) -> Option<Term> {
92 return None;
93 let simps = self.simplifications.lock();
94 simps.get(from).and_then(|v| {
95 v.iter().rev().find_map(|(c, t)| {
96 if c.iter().all(|v| {
97 ctx.iter()
98 .rev()
99 .find(|w| w.var.name() == v.var.name())
100 .is_some_and(|w| w.tp == v.tp && w.df == v.df)
101 }) {
102 Some(t.clone())
103 } else {
104 None
105 }
106 })
107 })
108 }
109
110 pub fn push(&mut self, task: CheckingTask<'c>) {
111 self.current.push(task);
112 }
113 pub fn pop<R: Clone>(
114 &mut self,
115 ret: &Option<R>,
116 ctx: &[Cow<ComponentVar>],
117 solutions: &Solutions,
118 ) -> Option<CheckingTask<'c>> {
119 fn filter_context(
120 ctx: &[Cow<ComponentVar>],
121 task: &CheckingTask,
122 ) -> Option<Vec<ComponentVar>> {
123 let mut vars: Vec<_> = match task {
124 CheckingTask::Equality(a, b)
125 | CheckingTask::HasType(a, b)
126 | CheckingTask::Subtype(a, b) => a
127 .free_variables()
128 .into_iter()
129 .chain(b.free_variables())
130 .collect(),
131 CheckingTask::Inference(t)
132 | CheckingTask::Inhabitable(t)
133 | CheckingTask::Proving(t)
134 | CheckingTask::Universe(t) => t.free_variables().into_iter().collect(),
135 CheckingTask::Rule(_)
136 | CheckingTask::Simplify(_)
137 | CheckingTask::Strategy(_)
138 | CheckingTask::VariableInference(_) => {
139 return None;
140 }
141 };
142 let mut ret = Vec::new();
143 while let Some(v) = vars.pop() {
144 if is_solvable_var(v).is_some() {
145 return None;
146 }
147 if let Some(v) = ctx.iter().find(|vd| vd.var.name() == v.name()) {
148 ret.push(v.clone().into_owned());
149 if let Some(tp) = v.tp.as_ref() {
151 for v in tp.free_variables() {
152 if is_solvable_var(v).is_some() {
153 return None;
154 }
155 if !vars.contains(&v) && !ret.iter().any(|nv| nv.var.name() == v.name())
156 {
157 vars.push(v);
158 }
159 }
160 }
161 if let Some(df) = v.df.as_ref() {
162 for v in df.free_variables() {
163 if is_solvable_var(v).is_some() {
164 return None;
165 }
166 if !vars.contains(&v) && !ret.iter().any(|nv| nv.var.name() == v.name())
167 {
168 vars.push(v);
169 }
170 }
171 }
172 }
173 }
174 ret.reverse();
175 Some(ret)
176 }
177 fn has_unsolveds(ctx: &[Cow<ComponentVar>], task: &CheckingTask) -> bool {
178 let vars = match task {
179 CheckingTask::Equality(a, b)
180 | CheckingTask::HasType(a, b)
181 | CheckingTask::Subtype(a, b) => {
182 either::Left(a.free_variables().into_iter().chain(b.free_variables()))
183 }
184 CheckingTask::Inference(t)
185 | CheckingTask::Inhabitable(t)
186 | CheckingTask::Proving(t)
187 | CheckingTask::Universe(t) => either::Right(t.free_variables().into_iter()),
188 CheckingTask::Rule(_)
189 | CheckingTask::Simplify(_)
190 | CheckingTask::Strategy(_)
191 | CheckingTask::VariableInference(_) => {
192 return true;
193 }
194 };
195 for v in vars {
196 if is_solvable_var(v).is_some() {
197 return true;
198 }
199 if let Some(v) = ctx.iter().find(|vd| vd.var.name() == v.name())
200 && (v
201 .tp
202 .as_ref()
203 .is_some_and(|t| t.has_free_such_that(|v| is_solvable_var(v).is_some()))
204 || v.df.as_ref().is_some_and(|t| {
205 t.has_free_such_that(|v| is_solvable_var(v).is_some())
206 }))
207 {
208 return true;
209 }
210 }
211 false
212 }
213 let task = self.current.pop()?;
214 if !solutions.0.is_empty()
216 {
231 return Some(task);
232 }
233
234 let Some(ctx) = filter_context(ctx, &task) else {
235 return Some(task);
236 };
237
238 macro_rules! insert {
239 ($e:expr) => {{
240 let mut store = self.cached.lock();
241 store.insert((ctx, $e));
243 }};
244 }
245 match task {
246 CheckingTask::Rule(_)
247 | CheckingTask::Strategy(_)
248 | CheckingTask::Simplify(_)
249 | CheckingTask::VariableInference(_) => (),
250 CheckingTask::Inference(t) => {
251 insert!(CachedJudgment::Inference {
254 term: t.clone(),
255 result: mutate(ret),
256 });
257 }
258 CheckingTask::Proving(t) => {
259 insert!(CachedJudgment::Proof {
260 goal: t.clone(),
261 result: mutate(ret),
262 });
263 }
264 CheckingTask::Inhabitable(t) => {
265 insert!(CachedJudgment::Inhabitable {
266 term: t.clone(),
267 result: mutate(ret),
268 });
269 }
270 CheckingTask::Universe(t) => {
271 insert!(CachedJudgment::Universe {
272 term: t.clone(),
273 result: mutate(ret),
274 });
275 }
276 CheckingTask::Equality(lhs, rhs) => {
277 insert!(CachedJudgment::Equal {
278 lhs: lhs.clone(),
279 rhs: rhs.clone(),
280 result: mutate(ret),
281 });
282 }
283 CheckingTask::Subtype(sub, sup) => {
284 insert!(CachedJudgment::Subtype {
285 sub: sub.clone(),
286 sup: sup.clone(),
287 result: mutate(ret),
288 });
289 }
290 CheckingTask::HasType(tm, tp) => {
291 insert!(CachedJudgment::HasType {
292 tm: tm.clone(),
293 tp: tp.clone(),
294 result: mutate(ret),
295 });
296 }
297 }
298 Some(task)
301 }
302
303 pub fn running(&self, task: &CheckingTask<'c>) -> bool {
304 self.current.contains(task)
305 }
306 pub fn has<R: Clone>(&self, task: &CheckingTask, context: &ContextWrap) -> Option<Option<R>> {
307 let curr = &*self.cached.lock();
309 for c in curr {
310 if let Some(r) = c.1.is(task, context, &c.0) {
311 return Some(r);
316 }
317 }
318 None
319 }
320}
321
322#[derive(Debug, Clone, Hash, PartialEq, Eq)]
323pub enum CachedJudgment {
324 Proof {
325 goal: Term,
327 result: Option<Term>,
328 },
329 Inference {
330 term: Term,
331 result: Option<Term>,
332 },
333 Inhabitable {
334 term: Term,
335 result: Option<bool>,
336 },
337 Universe {
338 term: Term,
339 result: Option<bool>,
340 },
341 Subtype {
342 sub: Term,
343 sup: Term,
344 result: Option<bool>,
345 },
346 HasType {
347 tm: Term,
348 tp: Term,
349 result: Option<bool>,
350 },
351 Equal {
352 lhs: Term,
353 rhs: Term,
354 result: Option<bool>,
355 },
356}
357impl CachedJudgment {
358 pub fn is<R: Clone>(
359 &self,
360 task: &CheckingTask,
361 ctx: &ContextWrap,
362 cached_ctx: &[ComponentVar],
363 ) -> Option<Option<R>> {
364 fn contexts(ctx: &ContextWrap, cached_ctx: &[ComponentVar]) -> bool {
365 cached_ctx.iter().all(|v| {
366 ctx.as_ref()
367 .iter()
368 .rev()
369 .find(|w| w.var.name() == v.var.name())
370 .is_some_and(|w| w.tp == v.tp && w.df == v.df)
371 })
372 }
373 match (task, self) {
374 (CheckingTask::Proving(t), Self::Proof { goal, result })
375 if goal.alpha_equal(t) && contexts(ctx, cached_ctx) =>
376 {
377 Some(mutate(result))
378 }
379 (CheckingTask::Inference(t), Self::Inference { term, result })
380 if t.alpha_equal(term) && contexts(ctx, cached_ctx) =>
381 {
382 Some(mutate(result))
383 }
384 (CheckingTask::Inhabitable(t), Self::Inhabitable { term, result })
385 if t.alpha_equal(term) && contexts(ctx, cached_ctx) =>
386 {
387 Some(mutate(result))
388 }
389 (CheckingTask::Universe(t), Self::Universe { term, result })
390 if t.alpha_equal(term) && contexts(ctx, cached_ctx) =>
391 {
392 Some(mutate(result))
393 }
394 (CheckingTask::Subtype(asub, asup), Self::Subtype { sub, sup, result })
395 if asub.alpha_equal(sub) && asup.alpha_equal(sup) && contexts(ctx, cached_ctx) =>
396 {
397 Some(mutate(result))
398 }
399 (CheckingTask::HasType(a, b), Self::HasType { tm, tp, result })
400 if a.alpha_equal(tm) && b.alpha_equal(tp) && contexts(ctx, cached_ctx) =>
401 {
402 Some(mutate(result))
403 }
404 (CheckingTask::Equality(l, r), Self::Equal { lhs, rhs, result })
405 if l.alpha_equal(lhs) && r.alpha_equal(rhs) && contexts(ctx, cached_ctx) =>
406 {
407 Some(mutate(result))
408 }
409 _ => None,
410 }
411 }
412}
413
414#[allow(clippy::ref_option)]
415fn mutate<R: Clone, T>(res: &Option<T>) -> Option<R> {
416 assert_eq!(std::mem::size_of::<R>(), std::mem::size_of::<T>());
417 unsafe {
418 std::ptr::from_ref(res)
419 .cast::<Option<R>>()
420 .as_ref()
421 .expect("wut")
422 .clone()
423 }
424}