Skip to main content

litex/verify/
verify_exist_fact_with_known_forall.rs

1use crate::prelude::*;
2use std::collections::HashMap;
3use std::rc::Rc;
4use std::result::Result;
5
6// Same ∀-instantiation strategy as `verify_atomic_fact_with_known_forall`, plus exist-internal params.
7
8impl Runtime {
9    pub fn verify_exist_fact_with_known_forall(
10        &mut self,
11        exist_fact: &ExistFactEnum,
12        verify_state: &VerifyState,
13    ) -> Result<StmtResult, RuntimeError> {
14        if let Some(fact_verified) =
15            self.try_verify_exist_fact_with_known_forall_facts_in_envs(exist_fact, verify_state)?
16        {
17            return Ok((fact_verified).into());
18        }
19        Ok((StmtUnknown::new()).into())
20    }
21
22    fn get_matched_exist_fact_in_known_forall_fact_in_envs(
23        &mut self,
24        iterate_from_env_index: usize,
25        iterate_from_known_forall_fact_index: usize,
26        given_exist_fact: &ExistFactEnum,
27    ) -> Result<
28        (
29            (usize, usize),
30            Option<HashMap<String, Obj>>,
31            Option<(ExistFactEnum, Rc<KnownForallFactParamsAndDom>)>,
32        ),
33        RuntimeError,
34    > {
35        let lookup_keys = known_exist_lookup_keys_for_forall_bucket(given_exist_fact);
36
37        let envs_count = self.environment_stack.len();
38        for i in iterate_from_env_index..envs_count {
39            let env = &self.environment_stack[envs_count - 1 - i];
40            let mut merged_bucket: Vec<(ExistFactEnum, Rc<KnownForallFactParamsAndDom>)> =
41                Vec::new();
42            for lk in lookup_keys.iter() {
43                if let Some(known_forall_facts_in_env) =
44                    env.known_exist_facts_in_forall_facts.get(lk.as_str())
45                {
46                    merged_bucket.extend(known_forall_facts_in_env.iter().cloned());
47                }
48            }
49            merged_bucket.sort_by(|a, b| a.0.to_string().cmp(&b.0.to_string()));
50            merged_bucket.dedup_by(|a, b| a.0.to_string() == b.0.to_string());
51            if !merged_bucket.is_empty() {
52                let known_forall_facts_count = merged_bucket.len();
53                for j in iterate_from_known_forall_fact_index..known_forall_facts_count {
54                    let entry_idx = known_forall_facts_count - 1 - j;
55                    let (fact_args_in_known_forall, given_fact_args, current_known_forall) = {
56                        let current_known_forall = &merged_bucket[entry_idx];
57                        (
58                            current_known_forall.0.get_args_from_fact(),
59                            given_exist_fact.get_args_from_fact(),
60                            current_known_forall.clone(),
61                        )
62                    };
63                    let match_result = self
64                        .match_args_in_fact_in_known_forall_fact_with_given_args(
65                            &fact_args_in_known_forall,
66                            &given_fact_args,
67                        )?;
68                    if let Some(arg_map) = match_result {
69                        let exist_in_forall = &current_known_forall.0;
70                        if !exist_in_forall.can_be_used_to_verify_goal(given_exist_fact) {
71                            continue;
72                        }
73                        return Ok(((i, j), Some(arg_map), Some(current_known_forall)));
74                    }
75                }
76            }
77        }
78
79        Ok(((0, 0), None, None))
80    }
81
82    fn try_verify_exist_fact_with_known_forall_facts_in_envs(
83        &mut self,
84        exist_fact: &ExistFactEnum,
85        verify_state: &VerifyState,
86    ) -> Result<Option<FactualStmtSuccess>, RuntimeError> {
87        let mut iterate_from_env_index = 0;
88        let mut iterate_from_known_forall_fact_index = 0;
89
90        loop {
91            let result = self.get_matched_exist_fact_in_known_forall_fact_in_envs(
92                iterate_from_env_index,
93                iterate_from_known_forall_fact_index,
94                exist_fact,
95            )?;
96            let ((i, j), arg_map_opt, known_forall_opt) = result;
97            match (arg_map_opt, known_forall_opt) {
98                (Some(arg_map), Some((exist_fact_in_known_forall, forall_rc))) => {
99                    if let Some(fact_verified) = self
100                        .verify_exist_fact_args_satisfy_forall_requirements(
101                            &exist_fact_in_known_forall,
102                            &forall_rc,
103                            arg_map,
104                            exist_fact,
105                            verify_state,
106                        )?
107                    {
108                        return Ok(Some(fact_verified));
109                    }
110                    iterate_from_env_index = i;
111                    iterate_from_known_forall_fact_index = j + 1;
112                }
113                _ => return Ok(None),
114            }
115        }
116    }
117
118    fn verify_exist_fact_args_satisfy_forall_requirements(
119        &mut self,
120        exist_fact_in_known_forall: &ExistFactEnum,
121        known_forall: &Rc<KnownForallFactParamsAndDom>,
122        arg_map: HashMap<String, Obj>,
123        given_exist_fact: &ExistFactEnum,
124        verify_state: &VerifyState,
125    ) -> Result<Option<FactualStmtSuccess>, RuntimeError> {
126        if !exist_fact_in_known_forall.can_be_used_to_verify_goal(given_exist_fact) {
127            return Ok(None);
128        }
129        // exist param matches exist param
130        let given_exist_param_names = given_exist_fact
131            .params_def_with_type()
132            .collect_param_names();
133
134        let known_exist_param_names = exist_fact_in_known_forall
135            .params_def_with_type()
136            .collect_param_names();
137        if !known_exist_param_names
138            .iter()
139            .all(|param_name| arg_map.contains_key(param_name))
140        {
141            return Ok(None);
142        }
143
144        if given_exist_param_names.len() != known_exist_param_names.len() {
145            return Ok(None);
146        }
147
148        let mut known_exist_params_to_given_exist_params_map: Vec<Obj> = Vec::new();
149        for (known_param_name, given_param_name) in known_exist_param_names
150            .iter()
151            .zip(given_exist_param_names.iter())
152        {
153            let obj = match arg_map.get(known_param_name) {
154                Some(v) => {
155                    if !Self::obj_matches_exist_forall_binding_name(v, given_param_name) {
156                        return Ok(None);
157                    }
158                    v
159                }
160                None => return Ok(None),
161            };
162            known_exist_params_to_given_exist_params_map.push(obj.clone());
163        }
164
165        // given exist param can only match known exist param, it can not match other params
166        for (key, obj) in arg_map.iter() {
167            if let Some(spine) = Self::obj_binding_spine_name_for_arg_map(obj) {
168                if given_exist_param_names.iter().any(|n| n == spine) {
169                    if !known_exist_param_names.contains(key) {
170                        return Ok(None);
171                    }
172                }
173            }
174        }
175
176        // arg that matches forall params
177        let param_names = known_forall.params_def.collect_param_names();
178
179        if !param_names
180            .iter()
181            .all(|param_name| arg_map.contains_key(param_name))
182        {
183            return Ok(None);
184        }
185
186        let mut args_for_params: Vec<Obj> = Vec::new();
187
188        for param_name in param_names.iter() {
189            let obj = match arg_map.get(param_name) {
190                Some(v) => v,
191                None => return Ok(None),
192            };
193            if Self::obj_depends_on_given_exist_param(obj, &given_exist_param_names) {
194                return Ok(None);
195            }
196
197            args_for_params.push(obj.clone());
198        }
199
200        let args_param_types = self
201            .verify_args_satisfy_param_def_flat_types(
202                &known_forall.params_def,
203                &args_for_params,
204                verify_state,
205                ParamObjType::Forall,
206            )
207            .map_err(|e| {
208                {
209                    RuntimeError::from(VerifyRuntimeError(RuntimeErrorStruct::new(
210                        Some(Fact::from(given_exist_fact.clone()).into_stmt()),
211                        String::new(),
212                        given_exist_fact.line_file(),
213                        Some(e),
214                        vec![],
215                    )))
216                }
217            })?;
218        if args_param_types.is_unknown() {
219            return Ok(None);
220        }
221
222        let param_to_arg_map = match known_forall
223            .params_def
224            .param_def_params_to_arg_map(&arg_map)
225        {
226            Some(m) => m,
227            None => return Ok(None),
228        };
229
230        for dom_fact in known_forall.dom.iter() {
231            let instantiated_dom_fact = self
232                .inst_fact(dom_fact, &param_to_arg_map, ParamObjType::Forall, None)
233                .map_err(|e| {
234                    {
235                        RuntimeError::from(VerifyRuntimeError(RuntimeErrorStruct::new(
236                            Some(Fact::from(given_exist_fact.clone()).into_stmt()),
237                            String::new(),
238                            given_exist_fact.line_file(),
239                            Some(e),
240                            vec![],
241                        )))
242                    }
243                })?;
244            let result = self
245                .verify_fact(&instantiated_dom_fact, verify_state)
246                .map_err(|e| {
247                    {
248                        RuntimeError::from(VerifyRuntimeError(RuntimeErrorStruct::new(
249                            Some(Fact::from(given_exist_fact.clone()).into_stmt()),
250                            String::new(),
251                            given_exist_fact.line_file(),
252                            Some(e),
253                            vec![],
254                        )))
255                    }
256                })?;
257            if result.is_unknown() {
258                return Ok(None);
259            }
260        }
261
262        let verified_by_known_forall_fact = ForallFact::new(
263            known_forall.params_def.clone(),
264            known_forall.dom.clone(),
265            vec![exist_fact_in_known_forall.clone().into()],
266            known_forall.line_file.clone(),
267        )?;
268        let fact_verified = FactualStmtSuccess::new_with_verified_by_known_fact(
269            given_exist_fact.clone().into(),
270            VerifiedByResult::cited_fact(
271                given_exist_fact.clone().into(),
272                verified_by_known_forall_fact.clone().into(),
273                None,
274            ),
275            Vec::new(),
276        );
277        Ok(Some(fact_verified))
278    }
279
280    fn obj_binding_spine_name_for_arg_map(obj: &Obj) -> Option<&str> {
281        match obj {
282            Obj::Atom(AtomObj::Exist(p)) => Some(p.name.as_str()),
283            Obj::Atom(AtomObj::Forall(p)) => Some(p.name.as_str()),
284            Obj::Atom(AtomObj::FnSet(p)) => Some(p.name.as_str()),
285            Obj::Atom(AtomObj::Def(p)) => Some(p.name.as_str()),
286            Obj::Atom(AtomObj::SetBuilder(p)) => Some(p.name.as_str()),
287            Obj::Atom(AtomObj::Induc(p)) => Some(p.name.as_str()),
288            Obj::Atom(AtomObj::DefAlgo(p)) => Some(p.name.as_str()),
289            _ => None,
290        }
291    }
292
293    fn obj_matches_exist_forall_binding_name(obj: &Obj, name: &str) -> bool {
294        match obj {
295            Obj::Atom(AtomObj::Exist(p)) => p.name == name,
296            Obj::Atom(AtomObj::Forall(p)) => p.name == name,
297            Obj::Atom(AtomObj::FnSet(p)) => p.name == name,
298            Obj::Atom(AtomObj::Def(p)) => p.name == name,
299            Obj::Atom(AtomObj::SetBuilder(p)) => p.name == name,
300            Obj::Atom(AtomObj::Induc(p)) => p.name == name,
301            Obj::Atom(AtomObj::DefAlgo(p)) => p.name == name,
302            _ => obj.to_string() == name,
303        }
304    }
305
306    fn obj_depends_on_given_exist_param(obj: &Obj, names: &[String]) -> bool {
307        match obj {
308            Obj::Atom(AtomObj::Exist(p)) => names.iter().any(|name| name == &p.name),
309            Obj::Atom(_) | Obj::Number(_) | Obj::StandardSet(_) => false,
310            Obj::Add(x) => Self::obj_pair_depends_on_given_exist_param(
311                x.left.as_ref(),
312                x.right.as_ref(),
313                names,
314            ),
315            Obj::Sub(x) => Self::obj_pair_depends_on_given_exist_param(
316                x.left.as_ref(),
317                x.right.as_ref(),
318                names,
319            ),
320            Obj::Mul(x) => Self::obj_pair_depends_on_given_exist_param(
321                x.left.as_ref(),
322                x.right.as_ref(),
323                names,
324            ),
325            Obj::Div(x) => Self::obj_pair_depends_on_given_exist_param(
326                x.left.as_ref(),
327                x.right.as_ref(),
328                names,
329            ),
330            Obj::Mod(x) => Self::obj_pair_depends_on_given_exist_param(
331                x.left.as_ref(),
332                x.right.as_ref(),
333                names,
334            ),
335            Obj::Pow(x) => Self::obj_pair_depends_on_given_exist_param(
336                x.base.as_ref(),
337                x.exponent.as_ref(),
338                names,
339            ),
340            Obj::Max(x) => Self::obj_pair_depends_on_given_exist_param(
341                x.left.as_ref(),
342                x.right.as_ref(),
343                names,
344            ),
345            Obj::Min(x) => Self::obj_pair_depends_on_given_exist_param(
346                x.left.as_ref(),
347                x.right.as_ref(),
348                names,
349            ),
350            Obj::Union(x) => Self::obj_pair_depends_on_given_exist_param(
351                x.left.as_ref(),
352                x.right.as_ref(),
353                names,
354            ),
355            Obj::Intersect(x) => Self::obj_pair_depends_on_given_exist_param(
356                x.left.as_ref(),
357                x.right.as_ref(),
358                names,
359            ),
360            Obj::SetMinus(x) => Self::obj_pair_depends_on_given_exist_param(
361                x.left.as_ref(),
362                x.right.as_ref(),
363                names,
364            ),
365            Obj::SetDiff(x) => Self::obj_pair_depends_on_given_exist_param(
366                x.left.as_ref(),
367                x.right.as_ref(),
368                names,
369            ),
370            Obj::MatrixAdd(x) => Self::obj_pair_depends_on_given_exist_param(
371                x.left.as_ref(),
372                x.right.as_ref(),
373                names,
374            ),
375            Obj::MatrixSub(x) => Self::obj_pair_depends_on_given_exist_param(
376                x.left.as_ref(),
377                x.right.as_ref(),
378                names,
379            ),
380            Obj::MatrixMul(x) => Self::obj_pair_depends_on_given_exist_param(
381                x.left.as_ref(),
382                x.right.as_ref(),
383                names,
384            ),
385            Obj::MatrixScalarMul(x) => Self::obj_pair_depends_on_given_exist_param(
386                x.scalar.as_ref(),
387                x.matrix.as_ref(),
388                names,
389            ),
390            Obj::MatrixPow(x) => Self::obj_pair_depends_on_given_exist_param(
391                x.base.as_ref(),
392                x.exponent.as_ref(),
393                names,
394            ),
395            Obj::Log(x) => {
396                Self::obj_pair_depends_on_given_exist_param(x.base.as_ref(), x.arg.as_ref(), names)
397            }
398            Obj::Range(x) => {
399                Self::obj_pair_depends_on_given_exist_param(x.start.as_ref(), x.end.as_ref(), names)
400            }
401            Obj::ClosedRange(x) => {
402                Self::obj_pair_depends_on_given_exist_param(x.start.as_ref(), x.end.as_ref(), names)
403            }
404            Obj::FnRange(x) => Self::obj_depends_on_given_exist_param(x.fn_obj.as_ref(), names),
405            Obj::FnDom(x) => Self::obj_depends_on_given_exist_param(x.fn_obj.as_ref(), names),
406            Obj::Proj(x) => {
407                Self::obj_pair_depends_on_given_exist_param(x.set.as_ref(), x.dim.as_ref(), names)
408            }
409            Obj::FiniteSeqSet(x) => {
410                Self::obj_pair_depends_on_given_exist_param(x.set.as_ref(), x.n.as_ref(), names)
411            }
412            Obj::MatrixSet(x) => {
413                Self::obj_depends_on_given_exist_param(x.set.as_ref(), names)
414                    || Self::obj_depends_on_given_exist_param(x.row_len.as_ref(), names)
415                    || Self::obj_depends_on_given_exist_param(x.col_len.as_ref(), names)
416            }
417            Obj::ObjAtIndex(x) => {
418                Self::obj_pair_depends_on_given_exist_param(x.obj.as_ref(), x.index.as_ref(), names)
419            }
420            Obj::Abs(x) => Self::obj_depends_on_given_exist_param(x.arg.as_ref(), names),
421            Obj::Cup(x) => Self::obj_depends_on_given_exist_param(x.left.as_ref(), names),
422            Obj::Cap(x) => Self::obj_depends_on_given_exist_param(x.left.as_ref(), names),
423            Obj::PowerSet(x) => Self::obj_depends_on_given_exist_param(x.set.as_ref(), names),
424            Obj::CartDim(x) => Self::obj_depends_on_given_exist_param(x.set.as_ref(), names),
425            Obj::TupleDim(x) => Self::obj_depends_on_given_exist_param(x.arg.as_ref(), names),
426            Obj::Count(x) => Self::obj_depends_on_given_exist_param(x.set.as_ref(), names),
427            Obj::SeqSet(x) => Self::obj_depends_on_given_exist_param(x.set.as_ref(), names),
428            Obj::Choose(x) => Self::obj_depends_on_given_exist_param(x.set.as_ref(), names),
429            Obj::Sum(x) => {
430                Self::obj_depends_on_given_exist_param(x.start.as_ref(), names)
431                    || Self::obj_depends_on_given_exist_param(x.end.as_ref(), names)
432                    || Self::obj_depends_on_given_exist_param(x.func.as_ref(), names)
433            }
434            Obj::Product(x) => {
435                Self::obj_depends_on_given_exist_param(x.start.as_ref(), names)
436                    || Self::obj_depends_on_given_exist_param(x.end.as_ref(), names)
437                    || Self::obj_depends_on_given_exist_param(x.func.as_ref(), names)
438            }
439            Obj::ListSet(x) => Self::obj_list_depends_on_given_exist_param(&x.list, names),
440            Obj::Cart(x) => Self::obj_list_depends_on_given_exist_param(&x.args, names),
441            Obj::Tuple(x) => Self::obj_list_depends_on_given_exist_param(&x.args, names),
442            Obj::FiniteSeqListObj(x) => Self::obj_list_depends_on_given_exist_param(&x.objs, names),
443            Obj::MatrixListObj(x) => x.rows.iter().any(|row| {
444                row.iter()
445                    .any(|obj| Self::obj_depends_on_given_exist_param(obj.as_ref(), names))
446            }),
447            Obj::FamilyObj(x) => x
448                .params
449                .iter()
450                .any(|obj| Self::obj_depends_on_given_exist_param(obj, names)),
451            Obj::StructObj(x) => x
452                .params
453                .iter()
454                .any(|obj| Self::obj_depends_on_given_exist_param(obj, names)),
455            Obj::ObjAsStructInstanceWithFieldAccess(x) => {
456                x.struct_obj
457                    .params
458                    .iter()
459                    .any(|obj| Self::obj_depends_on_given_exist_param(obj, names))
460                    || Self::obj_depends_on_given_exist_param(x.obj.as_ref(), names)
461            }
462            Obj::SetBuilder(x) => {
463                Self::obj_depends_on_given_exist_param(x.param_set.as_ref(), names)
464                    || x.facts.iter().any(|fact| {
465                        Self::or_and_chain_fact_depends_on_given_exist_param(fact, names)
466                    })
467            }
468            Obj::FnSet(x) => Self::fn_set_body_depends_on_given_exist_param(&x.body, names),
469            Obj::AnonymousFn(x) => {
470                Self::fn_set_body_depends_on_given_exist_param(&x.body, names)
471                    || Self::obj_depends_on_given_exist_param(x.equal_to.as_ref(), names)
472            }
473            Obj::FnObj(x) => {
474                Self::fn_obj_head_depends_on_given_exist_param(x.head.as_ref(), names)
475                    || x.body.iter().any(|args| {
476                        args.iter()
477                            .any(|arg| Self::obj_depends_on_given_exist_param(arg.as_ref(), names))
478                    })
479            }
480        }
481    }
482
483    fn obj_pair_depends_on_given_exist_param(left: &Obj, right: &Obj, names: &[String]) -> bool {
484        Self::obj_depends_on_given_exist_param(left, names)
485            || Self::obj_depends_on_given_exist_param(right, names)
486    }
487
488    fn obj_list_depends_on_given_exist_param(objs: &[Box<Obj>], names: &[String]) -> bool {
489        objs.iter()
490            .any(|obj| Self::obj_depends_on_given_exist_param(obj.as_ref(), names))
491    }
492
493    fn fn_set_body_depends_on_given_exist_param(body: &FnSetBody, names: &[String]) -> bool {
494        body.params_def_with_set.iter().any(|param_group| {
495            Self::param_group_with_set_depends_on_given_exist_param(param_group, names)
496        }) || body
497            .dom_facts
498            .iter()
499            .any(|fact| Self::or_and_chain_fact_depends_on_given_exist_param(fact, names))
500            || Self::obj_depends_on_given_exist_param(body.ret_set.as_ref(), names)
501    }
502
503    fn fn_obj_head_depends_on_given_exist_param(head: &FnObjHead, names: &[String]) -> bool {
504        match head {
505            FnObjHead::Exist(p) => names.iter().any(|name| name == &p.name),
506            FnObjHead::AnonymousFnLiteral(x) => {
507                Self::fn_set_body_depends_on_given_exist_param(&x.body, names)
508                    || Self::obj_depends_on_given_exist_param(x.equal_to.as_ref(), names)
509            }
510            _ => false,
511        }
512    }
513
514    fn param_group_with_set_depends_on_given_exist_param(
515        param_group: &ParamGroupWithSet,
516        names: &[String],
517    ) -> bool {
518        Self::obj_depends_on_given_exist_param(param_group.set_obj(), names)
519    }
520
521    fn or_and_chain_fact_depends_on_given_exist_param(
522        fact: &OrAndChainAtomicFact,
523        names: &[String],
524    ) -> bool {
525        fact.get_args_from_fact()
526            .iter()
527            .any(|obj| Self::obj_depends_on_given_exist_param(obj, names))
528    }
529}
530
531fn known_exist_lookup_keys_for_forall_bucket(goal: &ExistFactEnum) -> Vec<String> {
532    let mut keys = vec![goal.alpha_normalized_key(), goal.key()];
533    if let ExistFactEnum::ExistFact(body) = goal {
534        let unique = ExistFactEnum::ExistUniqueFact(body.clone());
535        keys.push(unique.alpha_normalized_key());
536        keys.push(unique.key());
537    }
538    keys.sort();
539    keys.dedup();
540    keys
541}
542
543#[cfg(test)]
544mod tests {
545    use super::*;
546
547    #[test]
548    fn detects_nested_exist_witness_dependency() {
549        let names = vec!["x".to_string()];
550        let witness: Obj = ExistFreeParamObj::new("x".to_string()).into();
551        let external: Obj = Identifier::new("a".to_string()).into();
552        let nested: Obj = Union::new(witness, external.clone()).into();
553
554        assert!(Runtime::obj_depends_on_given_exist_param(&nested, &names));
555        assert!(!Runtime::obj_depends_on_given_exist_param(
556            &external, &names
557        ));
558    }
559
560    #[test]
561    fn detects_function_call_on_exist_witness() {
562        let names = vec!["x".to_string()];
563        let head: FnObjHead = ExistFreeParamObj::new("x".to_string()).into();
564        let arg: Obj = Number::new("1".to_string()).into();
565        let fn_obj: Obj = FnObj::new(head, vec![vec![Box::new(arg)]]).into();
566
567        assert!(Runtime::obj_depends_on_given_exist_param(&fn_obj, &names));
568    }
569}