Skip to main content

litex/fact/
exist_fact.rs

1// `exist` / `exist!` / `not exist`: same [`ExistFactBody`]; the outer variant selects the keyword.
2// For `exist!`, verification may also discharge a companion uniqueness `forall`.
3
4use crate::prelude::*;
5use std::fmt;
6
7#[derive(Clone)]
8pub enum ExistFactEnum {
9    ExistFact(ExistFactBody),
10    ExistUniqueFact(ExistFactBody),
11    NotExistFact(ExistFactBody),
12}
13
14#[derive(Clone)]
15pub struct ExistFactBody {
16    pub params_def_with_type: ParamDefWithType,
17    pub facts: Vec<ExistBodyFact>,
18    pub line_file: LineFile,
19}
20
21#[derive(Clone)]
22pub enum ExistBodyFact {
23    AtomicFact(AtomicFact),
24    AndFact(AndFact),
25    ChainFact(ChainFact),
26    OrFact(OrFact),
27    InlineForall(ForallFact),
28}
29
30impl ExistFactBody {
31    pub fn new(
32        params_def_with_type: ParamDefWithType,
33        facts: Vec<ExistBodyFact>,
34        line_file: LineFile,
35    ) -> Result<Self, RuntimeError> {
36        let body = ExistFactBody {
37            params_def_with_type,
38            facts,
39            line_file,
40        };
41        check_exist_fact_has_no_duplicate_exist_free_parameter(&ExistFactEnum::ExistFact(
42            body.clone(),
43        ))?;
44        Ok(body)
45    }
46
47    pub fn exist_fact_string_without_exist_as_prefix(&self) -> String {
48        exist_fact_string_without_exist_as_prefix(&self.params_def_with_type, &self.facts)
49    }
50
51    pub fn get_args_from_fact(&self) -> Vec<Obj> {
52        let mut args: Vec<Obj> = Vec::new();
53        for param_def_with_type in self.params_def_with_type.groups.iter() {
54            if let ParamType::Obj(obj) = &param_def_with_type.param_type {
55                args.push(obj.clone());
56            }
57        }
58
59        for fact in self.facts.iter() {
60            for arg in fact.get_args_from_fact() {
61                args.push(arg);
62            }
63        }
64
65        args
66    }
67}
68
69impl ExistBodyFact {
70    pub fn key(&self) -> String {
71        match self {
72            ExistBodyFact::AtomicFact(a) => a.key(),
73            ExistBodyFact::AndFact(a) => a.key(),
74            ExistBodyFact::ChainFact(c) => c.key(),
75            ExistBodyFact::OrFact(o) => o.key(),
76            ExistBodyFact::InlineForall(f) => inline_forall_fact_string(f),
77        }
78    }
79
80    pub fn line_file(&self) -> LineFile {
81        match self {
82            ExistBodyFact::AtomicFact(a) => a.line_file(),
83            ExistBodyFact::AndFact(a) => a.line_file(),
84            ExistBodyFact::ChainFact(c) => c.line_file(),
85            ExistBodyFact::OrFact(o) => o.line_file.clone(),
86            ExistBodyFact::InlineForall(f) => f.line_file.clone(),
87        }
88    }
89
90    pub fn get_args_from_fact(&self) -> Vec<Obj> {
91        match self {
92            ExistBodyFact::AtomicFact(a) => a.get_args_from_fact(),
93            ExistBodyFact::AndFact(a) => a.get_args_from_fact(),
94            ExistBodyFact::ChainFact(c) => c.get_args_from_fact(),
95            ExistBodyFact::OrFact(o) => o.get_args_from_fact(),
96            ExistBodyFact::InlineForall(f) => forall_fact_args(f),
97        }
98    }
99
100    pub fn to_fact(self) -> Fact {
101        match self {
102            ExistBodyFact::AtomicFact(a) => a.into(),
103            ExistBodyFact::AndFact(a) => a.into(),
104            ExistBodyFact::ChainFact(c) => c.into(),
105            ExistBodyFact::OrFact(o) => o.into(),
106            ExistBodyFact::InlineForall(f) => f.into(),
107        }
108    }
109
110    pub fn from_ref_to_cloned_fact(&self) -> Fact {
111        self.clone().to_fact()
112    }
113
114    pub fn replace_bound_identifier(self, from: &str, to: &str) -> Self {
115        match self {
116            ExistBodyFact::AtomicFact(a) => {
117                ExistBodyFact::AtomicFact(a.replace_bound_identifier(from, to))
118            }
119            ExistBodyFact::AndFact(a) => ExistBodyFact::AndFact(AndFact::new(
120                a.facts
121                    .into_iter()
122                    .map(|x| x.replace_bound_identifier(from, to))
123                    .collect(),
124                a.line_file,
125            )),
126            ExistBodyFact::ChainFact(c) => ExistBodyFact::ChainFact(ChainFact::new(
127                c.objs
128                    .into_iter()
129                    .map(|o| Obj::replace_bound_identifier(o, from, to))
130                    .collect(),
131                c.prop_names,
132                c.line_file,
133            )),
134            ExistBodyFact::OrFact(o) => ExistBodyFact::OrFact(OrFact::new(
135                o.facts
136                    .into_iter()
137                    .map(|x| x.replace_bound_identifier(from, to))
138                    .collect(),
139                o.line_file,
140            )),
141            ExistBodyFact::InlineForall(f) => {
142                ExistBodyFact::InlineForall(replace_in_inline_forall_for_exist_alpha(f, from, to))
143            }
144        }
145    }
146}
147
148impl fmt::Display for ExistBodyFact {
149    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
150        match self {
151            ExistBodyFact::AtomicFact(a) => write!(f, "{}", a),
152            ExistBodyFact::AndFact(a) => write!(f, "{}", a),
153            ExistBodyFact::ChainFact(c) => write!(f, "{}", c),
154            ExistBodyFact::OrFact(o) => write!(f, "{}", o),
155            ExistBodyFact::InlineForall(forall_fact) => {
156                write!(f, "{}", inline_forall_fact_string(forall_fact))
157            }
158        }
159    }
160}
161
162impl From<OrAndChainAtomicFact> for ExistBodyFact {
163    fn from(fact: OrAndChainAtomicFact) -> Self {
164        match fact {
165            OrAndChainAtomicFact::AtomicFact(a) => ExistBodyFact::AtomicFact(a),
166            OrAndChainAtomicFact::AndFact(a) => ExistBodyFact::AndFact(a),
167            OrAndChainAtomicFact::ChainFact(c) => ExistBodyFact::ChainFact(c),
168            OrAndChainAtomicFact::OrFact(o) => ExistBodyFact::OrFact(o),
169        }
170    }
171}
172
173impl From<AtomicFact> for ExistBodyFact {
174    fn from(atomic_fact: AtomicFact) -> Self {
175        ExistBodyFact::AtomicFact(atomic_fact)
176    }
177}
178
179impl From<EqualFact> for ExistBodyFact {
180    fn from(equal_fact: EqualFact) -> Self {
181        ExistBodyFact::AtomicFact(equal_fact.into())
182    }
183}
184
185fn forall_fact_args(forall_fact: &ForallFact) -> Vec<Obj> {
186    let mut args: Vec<Obj> = Vec::new();
187    for param_def_with_type in forall_fact.params_def_with_type.groups.iter() {
188        if let ParamType::Obj(obj) = &param_def_with_type.param_type {
189            args.push(obj.clone());
190        }
191    }
192    for fact in forall_fact.dom_facts.iter() {
193        match fact {
194            Fact::AtomicFact(a) => args.extend(a.get_args_from_fact()),
195            Fact::ExistFact(e) => args.extend(e.get_args_from_fact()),
196            Fact::OrFact(o) => args.extend(o.get_args_from_fact()),
197            Fact::AndFact(a) => args.extend(a.get_args_from_fact()),
198            Fact::ChainFact(c) => args.extend(c.get_args_from_fact()),
199            Fact::ForallFact(f) => args.extend(forall_fact_args(f)),
200            Fact::ForallFactWithIff(_) | Fact::NotForall(_) => {}
201        }
202    }
203    for fact in forall_fact.then_facts.iter() {
204        match fact {
205            ExistOrAndChainAtomicFact::AtomicFact(a) => args.extend(a.get_args_from_fact()),
206            ExistOrAndChainAtomicFact::AndFact(a) => args.extend(a.get_args_from_fact()),
207            ExistOrAndChainAtomicFact::ChainFact(c) => args.extend(c.get_args_from_fact()),
208            ExistOrAndChainAtomicFact::OrFact(o) => args.extend(o.get_args_from_fact()),
209            ExistOrAndChainAtomicFact::ExistFact(e) => args.extend(e.get_args_from_fact()),
210        }
211    }
212    args
213}
214
215fn inline_forall_fact_string(forall_fact: &ForallFact) -> String {
216    let then_facts = curly_braced_vec_to_string_with_sep(
217        &forall_fact
218            .then_facts
219            .iter()
220            .map(|fact| fact.to_string())
221            .collect::<Vec<String>>(),
222        format!("{} ", COMMA),
223    );
224    if forall_fact.dom_facts.is_empty() {
225        return format!(
226            "{} {}{} {}",
227            FORALL_BANG, forall_fact.params_def_with_type, COLON, then_facts
228        );
229    }
230    format!(
231        "{} {}{} {} {} {}",
232        FORALL_BANG,
233        forall_fact.params_def_with_type,
234        COLON,
235        vec_to_string_join_by_comma(
236            &forall_fact
237                .dom_facts
238                .iter()
239                .map(inline_fact_string)
240                .collect::<Vec<String>>()
241        ),
242        RIGHT_ARROW,
243        then_facts
244    )
245}
246
247fn inline_fact_string(fact: &Fact) -> String {
248    match fact {
249        Fact::ForallFact(forall_fact) => inline_forall_fact_string(forall_fact),
250        Fact::NotForall(not_forall) => {
251            format!(
252                "{} {}",
253                NOT,
254                inline_forall_fact_string(&not_forall.forall_fact)
255            )
256        }
257        _ => fact.to_string(),
258    }
259}
260
261fn replace_in_inline_forall_for_exist_alpha(
262    forall_fact: ForallFact,
263    from: &str,
264    to: &str,
265) -> ForallFact {
266    let binds_same_name = forall_fact
267        .params_def_with_type
268        .collect_param_names()
269        .iter()
270        .any(|name| name == from);
271    if binds_same_name {
272        return forall_fact;
273    }
274
275    let dom_facts = forall_fact
276        .dom_facts
277        .into_iter()
278        .map(|fact| replace_in_fact_for_exist_alpha(fact, from, to))
279        .collect();
280    let then_facts = forall_fact
281        .then_facts
282        .into_iter()
283        .map(|fact| replace_in_exist_or_and_chain_for_exist_alpha(fact, from, to))
284        .collect();
285
286    ForallFact::new(
287        forall_fact.params_def_with_type,
288        dom_facts,
289        then_facts,
290        forall_fact.line_file,
291    )
292    .expect("alpha replacement preserves inline forall validity")
293}
294
295fn replace_in_fact_for_exist_alpha(fact: Fact, from: &str, to: &str) -> Fact {
296    match fact {
297        Fact::AtomicFact(a) => a.replace_bound_identifier(from, to).into(),
298        Fact::ExistFact(e) => e.into(),
299        Fact::OrFact(o) => OrFact::new(
300            o.facts
301                .into_iter()
302                .map(|x| x.replace_bound_identifier(from, to))
303                .collect(),
304            o.line_file,
305        )
306        .into(),
307        Fact::AndFact(a) => AndFact::new(
308            a.facts
309                .into_iter()
310                .map(|x| x.replace_bound_identifier(from, to))
311                .collect(),
312            a.line_file,
313        )
314        .into(),
315        Fact::ChainFact(c) => ChainFact::new(
316            c.objs
317                .into_iter()
318                .map(|o| Obj::replace_bound_identifier(o, from, to))
319                .collect(),
320            c.prop_names,
321            c.line_file,
322        )
323        .into(),
324        Fact::ForallFact(f) => replace_in_inline_forall_for_exist_alpha(f, from, to).into(),
325        Fact::ForallFactWithIff(f) => f.into(),
326        Fact::NotForall(f) => f.into(),
327    }
328}
329
330fn replace_in_exist_or_and_chain_for_exist_alpha(
331    fact: ExistOrAndChainAtomicFact,
332    from: &str,
333    to: &str,
334) -> ExistOrAndChainAtomicFact {
335    match fact {
336        ExistOrAndChainAtomicFact::AtomicFact(a) => a.replace_bound_identifier(from, to).into(),
337        ExistOrAndChainAtomicFact::AndFact(a) => AndFact::new(
338            a.facts
339                .into_iter()
340                .map(|x| x.replace_bound_identifier(from, to))
341                .collect(),
342            a.line_file,
343        )
344        .into(),
345        ExistOrAndChainAtomicFact::ChainFact(c) => ChainFact::new(
346            c.objs
347                .into_iter()
348                .map(|o| Obj::replace_bound_identifier(o, from, to))
349                .collect(),
350            c.prop_names,
351            c.line_file,
352        )
353        .into(),
354        ExistOrAndChainAtomicFact::OrFact(o) => OrFact::new(
355            o.facts
356                .into_iter()
357                .map(|x| x.replace_bound_identifier(from, to))
358                .collect(),
359            o.line_file,
360        )
361        .into(),
362        ExistOrAndChainAtomicFact::ExistFact(e) => e.into(),
363    }
364}
365
366impl ExistFactEnum {
367    pub fn body(&self) -> &ExistFactBody {
368        match self {
369            ExistFactEnum::ExistFact(b)
370            | ExistFactEnum::ExistUniqueFact(b)
371            | ExistFactEnum::NotExistFact(b) => b,
372        }
373    }
374
375    pub fn is_exist_unique(&self) -> bool {
376        matches!(self, ExistFactEnum::ExistUniqueFact(_))
377    }
378
379    pub fn is_not_exist(&self) -> bool {
380        matches!(self, ExistFactEnum::NotExistFact(_))
381    }
382
383    pub fn is_plain_exist(&self) -> bool {
384        matches!(self, ExistFactEnum::ExistFact(_))
385    }
386
387    pub fn keyword_prefix(&self) -> String {
388        if self.is_not_exist() {
389            format!("{} {}", NOT, EXIST)
390        } else if self.is_exist_unique() {
391            EXIST_BANG.to_string()
392        } else {
393            EXIST.to_string()
394        }
395    }
396
397    /// Whether a stored exist fact can directly verify the `goal`.
398    /// `exist!` can verify `exist`, but other cross-variant matches are rejected.
399    pub fn can_be_used_to_verify_goal(&self, goal: &ExistFactEnum) -> bool {
400        match self {
401            ExistFactEnum::ExistFact(_) => goal.is_plain_exist(),
402            ExistFactEnum::ExistUniqueFact(_) => goal.is_plain_exist() || goal.is_exist_unique(),
403            ExistFactEnum::NotExistFact(_) => goal.is_not_exist(),
404        }
405    }
406
407    pub fn exist_fact_string_without_exist_as_prefix(&self) -> String {
408        self.body().exist_fact_string_without_exist_as_prefix()
409    }
410
411    pub fn key(&self) -> String {
412        let head = self.keyword_prefix();
413        let b = self.body();
414        format!(
415            "{} {}{}{}",
416            head,
417            LEFT_CURLY_BRACE,
418            vec_to_string_join_by_comma(
419                &b.facts
420                    .iter()
421                    .map(|fact| fact.key())
422                    .collect::<Vec<String>>()
423            ),
424            RIGHT_CURLY_BRACE
425        )
426    }
427
428    /// Key for indexing `known_exist_facts_in_forall_facts`: exist witnesses renamed to `#0`, `#1`, …
429    /// so different witness names match the same stored shape.
430    pub fn alpha_normalized_key(&self) -> String {
431        let b = self.body();
432        let names = b.params_def_with_type.collect_param_names();
433        let mut normalized_facts: Vec<ExistBodyFact> = b.facts.clone();
434        for (i, name) in names.iter().enumerate() {
435            let ph = format!("#{}", i);
436            normalized_facts = normalized_facts
437                .into_iter()
438                .map(|f| f.replace_bound_identifier(name, &ph))
439                .collect();
440        }
441        let head = self.keyword_prefix();
442        format!(
443            "{} {}{}{}",
444            head,
445            LEFT_CURLY_BRACE,
446            vec_to_string_join_by_comma(
447                &normalized_facts
448                    .iter()
449                    .map(|fact| fact.key())
450                    .collect::<Vec<String>>()
451            ),
452            RIGHT_CURLY_BRACE
453        )
454    }
455
456    pub fn line_file(&self) -> LineFile {
457        self.body().line_file.clone()
458    }
459
460    pub fn params_def_with_type(&self) -> &ParamDefWithType {
461        &self.body().params_def_with_type
462    }
463
464    pub fn facts(&self) -> &Vec<ExistBodyFact> {
465        &self.body().facts
466    }
467
468    pub fn get_args_from_fact(&self) -> Vec<Obj> {
469        self.body().get_args_from_fact()
470    }
471}
472
473fn exist_fact_string_without_exist_as_prefix(
474    param_defs: &ParamDefWithType,
475    facts: &Vec<ExistBodyFact>,
476) -> String {
477    format!(
478        "{} {} {}",
479        param_defs.to_string(),
480        ST,
481        curly_braced_vec_to_string_with_sep(
482            &facts
483                .iter()
484                .map(|fact| fact.to_string())
485                .collect::<Vec<String>>(),
486            format!("{} ", COMMA)
487        )
488    )
489}
490
491impl fmt::Display for ExistFactEnum {
492    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
493        let head = self.keyword_prefix();
494        write!(
495            f,
496            "{} {}",
497            head,
498            self.exist_fact_string_without_exist_as_prefix()
499        )
500    }
501}