Skip to main content

litex/infer/
infer_in_fact.rs

1use crate::prelude::*;
2use std::collections::hash_map::Entry;
3use std::collections::HashMap;
4
5/// Objects whose `to_string()` is used as the key in `Environment::known_objs_in_fn_sets`.
6pub(crate) fn obj_eligible_for_known_objs_in_fn_sets(obj: &Obj) -> bool {
7    matches!(
8        obj,
9        Obj::Atom(AtomObj::Identifier(_))
10            | Obj::Atom(AtomObj::IdentifierWithMod(_))
11            | Obj::Atom(AtomObj::Forall(_))
12            | Obj::Atom(AtomObj::Exist(_))
13            | Obj::Atom(AtomObj::Def(_))
14            | Obj::Atom(AtomObj::SetBuilder(_))
15            | Obj::Atom(AtomObj::FnSet(_))
16            | Obj::Atom(AtomObj::Induc(_))
17            | Obj::Atom(AtomObj::DefAlgo(_))
18    )
19}
20
21/// Extra map keys so `FnObj` well-defined lookup (`Identifier` head) finds entries
22/// registered under tagged free-param display (e.g. `~1a` vs `a`).
23fn extra_known_fn_set_keys_for_bare_name_lookup(element: &Obj) -> Vec<String> {
24    match element {
25        Obj::Atom(AtomObj::Forall(p)) => vec![p.name.clone()],
26        Obj::Atom(AtomObj::Exist(p)) => vec![p.name.clone()],
27        Obj::Atom(AtomObj::Def(p)) => vec![p.name.clone()],
28        Obj::Atom(AtomObj::SetBuilder(p)) => vec![p.name.clone()],
29        Obj::Atom(AtomObj::FnSet(p)) => vec![p.name.clone()],
30        Obj::Atom(AtomObj::Induc(p)) => vec![p.name.clone()],
31        Obj::Atom(AtomObj::DefAlgo(p)) => vec![p.name.clone()],
32        _ => vec![],
33    }
34}
35
36impl Runtime {
37    fn upsert_known_fn_info_for_key(
38        map: &mut HashMap<ObjString, KnownFnInfo>,
39        key: ObjString,
40        body: Option<(FnSetBody, LineFile)>,
41        equal_to: Option<(Obj, LineFile)>,
42    ) {
43        match map.entry(key) {
44            Entry::Occupied(mut o) => {
45                let info = o.get_mut();
46                if let Some((b, lf)) = body {
47                    info.fn_set = Some((b, lf));
48                }
49                if let Some((eq, lf)) = equal_to {
50                    info.equal_to = Some((eq, lf));
51                }
52            }
53            Entry::Vacant(v) => {
54                if body.is_none() && equal_to.is_none() {
55                    return;
56                }
57                v.insert(KnownFnInfo::merge_fn_set_equal_to(body, equal_to));
58            }
59        }
60    }
61
62    fn upsert_known_fn_restrict_to_for_key(
63        map: &mut HashMap<ObjString, KnownFnInfo>,
64        key: ObjString,
65        restrict_body: FnSetBody,
66        line_file: LineFile,
67    ) {
68        match map.entry(key) {
69            Entry::Occupied(mut o) => {
70                o.get_mut()
71                    .update_restrict_to(restrict_body, line_file.clone());
72            }
73            Entry::Vacant(v) => {
74                let mut info = KnownFnInfo::default();
75                info.update_restrict_to(restrict_body, line_file);
76                v.insert(info);
77            }
78        }
79    }
80
81    /// Record `$restrict_fn_in(f, fn …)` RHS body for `f` (overwrites prior `restrict_to` for that key).
82    pub(crate) fn register_known_fn_restrict_to_for_element(
83        &mut self,
84        element: &Obj,
85        restrict_body: FnSetBody,
86        line_file: LineFile,
87    ) {
88        if !obj_eligible_for_known_objs_in_fn_sets(element) {
89            return;
90        }
91        let key = element.to_string();
92        let env = self.top_level_env();
93        Self::upsert_known_fn_restrict_to_for_key(
94            &mut env.known_objs_in_fn_sets,
95            key.clone(),
96            restrict_body.clone(),
97            line_file.clone(),
98        );
99        for alias in extra_known_fn_set_keys_for_bare_name_lookup(element) {
100            if alias != key {
101                Self::upsert_known_fn_restrict_to_for_key(
102                    &mut env.known_objs_in_fn_sets,
103                    alias,
104                    restrict_body.clone(),
105                    line_file.clone(),
106                );
107            }
108        }
109    }
110
111    /// `$restrict_fn_in(f, narrower_fn_set)` after the fact is stored: remember the narrowed `FnSetBody`.
112    pub fn infer_restrict_fact(&mut self, rf: &RestrictFact) -> Result<InferResult, RuntimeError> {
113        let restrict_body = match &rf.obj_can_restrict_to_fn_set {
114            Obj::FnSet(fs) => fs.body.clone(),
115            _ => return Ok(InferResult::new()),
116        };
117        self.register_known_fn_restrict_to_for_element(
118            &rf.obj,
119            restrict_body,
120            rf.line_file.clone(),
121        );
122        Ok(InferResult::new())
123    }
124
125    /// Record `element` as having function signature `body` (same keys/aliases as `element $in fn ...` infer).
126    /// When `equal_to` is `Some`, stores the defining expression (e.g. from `a = '…{…}` or `have fn`).
127    pub(crate) fn register_known_objs_in_fn_sets_for_element_body(
128        &mut self,
129        element: &Obj,
130        body: FnSetBody,
131        equal_to: Option<Obj>,
132        fn_signature_line_file: LineFile,
133        defining_expr_line_file: LineFile,
134    ) {
135        if !obj_eligible_for_known_objs_in_fn_sets(element) {
136            return;
137        }
138        let key = element.to_string();
139        let env = self.top_level_env();
140        let body_opt = Some((body.clone(), fn_signature_line_file.clone()));
141        let equal_opt = equal_to
142            .clone()
143            .map(|eq| (eq, defining_expr_line_file.clone()));
144        Self::upsert_known_fn_info_for_key(
145            &mut env.known_objs_in_fn_sets,
146            key.clone(),
147            body_opt,
148            equal_opt.clone(),
149        );
150        for alias in extra_known_fn_set_keys_for_bare_name_lookup(element) {
151            if alias != key {
152                Self::upsert_known_fn_info_for_key(
153                    &mut env.known_objs_in_fn_sets,
154                    alias,
155                    Some((body.clone(), fn_signature_line_file.clone())),
156                    equal_opt.clone(),
157                );
158            }
159        }
160    }
161
162    // Expand a type-level `FamilyObj` to its `equal_to` set object under the given type arguments.
163    pub fn instantiate_family_member_set(
164        &mut self,
165        family_ty: &FamilyObj,
166    ) -> Result<Obj, RuntimeError> {
167        let family_name = family_ty.name.to_string();
168        let def =
169            match self.get_cloned_family_definition_by_name(&family_name) {
170                Some(d) => d,
171                None => {
172                    return Err(UnknownRuntimeError(RuntimeErrorStruct::new_with_just_msg(
173                        format!("family `{}` is not defined", family_name),
174                    ))
175                    .into());
176                }
177            };
178        let expected_count = def.params_def_with_type.number_of_params();
179        if family_ty.params.len() != expected_count {
180            return Err(
181                UnknownRuntimeError(RuntimeErrorStruct::new_with_just_msg(format!(
182                    "family `{}` expects {} type argument(s), got {}",
183                    family_name,
184                    expected_count,
185                    family_ty.params.len()
186                )))
187                .into(),
188            );
189        }
190        let param_to_arg_map = def
191            .params_def_with_type
192            .param_defs_and_args_to_param_to_arg_map(family_ty.params.as_slice());
193        self.inst_obj(&def.equal_to, &param_to_arg_map, ParamObjType::DefHeader)
194    }
195
196    // Param typed as `FamilyObj`: store `name $in` the instantiated member set and run membership infer.
197    pub fn infer_membership_in_family_for_param_binding(
198        &mut self,
199        name: &str,
200        family_ty: &FamilyObj,
201        binding_kind: ParamObjType,
202    ) -> Result<InferResult, RuntimeError> {
203        let member_set = self.instantiate_family_member_set(family_ty)?;
204        let type_fact = InFact::new(
205            param_binding_element_obj_for_store(name.to_string(), binding_kind),
206            member_set,
207            default_line_file(),
208        )
209        .into();
210        self.verify_well_defined_and_store_and_infer_with_default_verify_state(type_fact)
211    }
212
213    // RHS is `FamilyObj`: instantiate to a concrete set, then infer `element $in` that set.
214    pub fn infer_membership_in_family_from_in_fact(
215        &mut self,
216        in_fact: &InFact,
217        family_obj: &FamilyObj,
218    ) -> Result<InferResult, RuntimeError> {
219        let member_set = self.instantiate_family_member_set(family_obj)?;
220        let expanded = InFact::new(
221            in_fact.element.clone(),
222            member_set,
223            in_fact.line_file.clone(),
224        );
225        self.infer_in_fact(&expanded)
226    }
227
228    // RHS is a function space `FnSet`: record the element in `known_objs_in_fn_sets`.
229    pub fn infer_membership_in_fn_set_from_in_fact(
230        &mut self,
231        in_fact: &InFact,
232        fn_set_with_dom: &FnSet,
233    ) -> Result<InferResult, RuntimeError> {
234        if !obj_eligible_for_known_objs_in_fn_sets(&in_fact.element) {
235            return Ok(InferResult::new());
236        }
237
238        let lf = in_fact.line_file.clone();
239        self.register_known_objs_in_fn_sets_for_element_body(
240            &in_fact.element,
241            fn_set_with_dom.body.clone(),
242            None,
243            lf.clone(),
244            lf,
245        );
246
247        let mut infer_result = InferResult::new();
248        infer_result.new_fact(&in_fact.clone().into());
249        Ok(infer_result)
250    }
251
252    // RHS is set-builder `{ x $in S | ... }`: emit `element $in S` and each defining fact with `x := element`.
253    pub fn infer_membership_in_set_builder_from_in_fact(
254        &mut self,
255        in_fact: &InFact,
256        set_builder: &SetBuilder,
257    ) -> Result<InferResult, RuntimeError> {
258        let mut param_to_arg_map: HashMap<String, Obj> = HashMap::new();
259        param_to_arg_map.insert(set_builder.param.clone(), in_fact.element.clone());
260
261        let element_in_param_set_fact = InFact::new(
262            in_fact.element.clone(),
263            *set_builder.param_set.clone(),
264            in_fact.line_file.clone(),
265        )
266        .into();
267
268        let mut infer_result = InferResult::new();
269        infer_result.new_fact(&element_in_param_set_fact);
270        self.verify_well_defined_and_store_and_infer_with_default_verify_state(
271            element_in_param_set_fact,
272        )?;
273
274        for fact_in_set_builder in set_builder.facts.iter() {
275            let instantiated_fact_in_set_builder: OrAndChainAtomicFact = self
276                .inst_or_and_chain_atomic_fact(
277                    fact_in_set_builder,
278                    &param_to_arg_map,
279                    ParamObjType::SetBuilder,
280                    Some(&in_fact.line_file),
281                )
282                .map_err(|e| {
283                    RuntimeError::from(InferRuntimeError(RuntimeErrorStruct::new(
284                        None,
285                        format!(
286                            "failed to instantiate set builder fact while inferring `{}`",
287                            in_fact
288                        ),
289                        in_fact.line_file.clone(),
290                        Some(e),
291                        vec![],
292                    )))
293                })?;
294            let instantiated_fact_as_fact = instantiated_fact_in_set_builder.to_fact();
295            let fact_to_store = instantiated_fact_as_fact;
296
297            infer_result.new_fact(&fact_to_store);
298            self.verify_well_defined_and_store_and_infer_with_default_verify_state(fact_to_store)?;
299        }
300
301        Ok(infer_result)
302    }
303
304    // Membership `x $in S`: unfold `S` into stored facts (disjunction, bounds, predicate instances, …).
305    pub(in crate::infer) fn infer_in_fact(
306        &mut self,
307        in_fact: &InFact,
308    ) -> Result<InferResult, RuntimeError> {
309        match &in_fact.set {
310            // Function space: side table `known_objs_in_fn_sets` for typing/satisfaction later.
311            Obj::FnSet(fn_set_with_dom) => {
312                self.infer_membership_in_fn_set_from_in_fact(in_fact, fn_set_with_dom)
313            }
314            // Finite enum set: `a $in {1,2}` => fact `(a = 1) or (a = 2)`.
315            Obj::ListSet(list_set) => {
316                if list_set.list.is_empty() {
317                    return Ok(InferResult::new());
318                }
319
320                let mut or_case_facts: Vec<AndChainAtomicFact> =
321                    Vec::with_capacity(list_set.list.len());
322                for obj_in_list_set in list_set.list.iter() {
323                    let equal_fact = EqualFact::new(
324                        in_fact.element.clone(),
325                        *obj_in_list_set.clone(),
326                        in_fact.line_file.clone(),
327                    )
328                    .into();
329                    or_case_facts.push(AndChainAtomicFact::AtomicFact(equal_fact));
330                }
331
332                let or_fact = OrFact::new(or_case_facts, in_fact.line_file.clone()).into();
333                let mut infer_result = InferResult::new();
334                infer_result.new_fact(&or_fact);
335                self.verify_well_defined_and_store_and_infer_with_default_verify_state(or_fact)?;
336                Ok(infer_result)
337            }
338            // Set comprehension: membership in parameter domain plus instantiated filter facts.
339            Obj::SetBuilder(set_builder) => {
340                self.infer_membership_in_set_builder_from_in_fact(in_fact, set_builder)
341            }
342            // Cartesian product: element is an n-tuple with matching dimension; bind tuple/cart metadata.
343            Obj::Cart(cart) => {
344                if cart.args.len() < 2 {
345                    return Ok(InferResult::new());
346                }
347                let mut infer_result = InferResult::new();
348
349                let is_cart_fact =
350                    IsTupleFact::new(in_fact.element.clone(), in_fact.line_file.clone()).into();
351
352                infer_result.new_fact(&is_cart_fact);
353                self.verify_well_defined_and_store_and_infer_with_default_verify_state(
354                    is_cart_fact,
355                )?;
356
357                let cart_args_count = cart.args.len();
358                let tuple_dim_obj = TupleDim::new(in_fact.element.clone()).into();
359                let cart_args_count_obj = Number::new(cart_args_count.to_string()).into();
360                let tuple_dim_fact = EqualFact::new(
361                    tuple_dim_obj,
362                    cart_args_count_obj,
363                    in_fact.line_file.clone(),
364                )
365                .into();
366
367                infer_result.new_fact(&tuple_dim_fact);
368                self.verify_well_defined_and_store_and_infer_with_default_verify_state(
369                    tuple_dim_fact,
370                )?;
371
372                self.store_tuple_obj_and_cart(
373                    &in_fact.element.to_string(),
374                    None,
375                    Some(cart.clone()),
376                    in_fact.line_file.clone(),
377                );
378
379                Ok(infer_result)
380            }
381            // Half-open integer interval: `i $in range(a,b)` => `i $in Z`, `a <= i`, `i < b`.
382            Obj::Range(r) => {
383                let start = (*r.start).clone();
384                let end = (*r.end).clone();
385                self.infer_in_fact_element_in_integer_interval(in_fact, start, end, false)
386            }
387            // Closed integer interval: `i $in closed_range(a,b)` => `i $in Z`, `a <= i`, `i <= b`.
388            Obj::ClosedRange(c) => {
389                let start = (*c.start).clone();
390                let end = (*c.end).clone();
391                self.infer_in_fact_element_in_integer_interval(in_fact, start, end, true)
392            }
393            // Strictly positive number sets: `x $in R_pos` (etc.) => `0 < x`.
394            Obj::StandardSet(StandardSet::QPos)
395            | Obj::StandardSet(StandardSet::RPos)
396            | Obj::StandardSet(StandardSet::NPos) => {
397                let zero_obj: Obj = Number::new("0".to_string()).into();
398                let inferred_atomic_fact =
399                    LessFact::new(zero_obj, in_fact.element.clone(), in_fact.line_file.clone())
400                        .into();
401                let mut infer_result = InferResult::new();
402                infer_result.push_atomic_fact(&inferred_atomic_fact);
403                self.store_atomic_fact_without_well_defined_verified_and_infer(
404                    inferred_atomic_fact.clone(),
405                )?;
406                Ok(infer_result)
407            }
408            // Strictly negative rays: `x $in R_neg` (etc.) => `x < 0`.
409            Obj::StandardSet(StandardSet::QNeg)
410            | Obj::StandardSet(StandardSet::ZNeg)
411            | Obj::StandardSet(StandardSet::RNeg) => {
412                let zero_obj: Obj = Number::new("0".to_string()).into();
413                let inferred_atomic_fact =
414                    LessFact::new(in_fact.element.clone(), zero_obj, in_fact.line_file.clone())
415                        .into();
416                let mut infer_result = InferResult::new();
417                infer_result.push_atomic_fact(&inferred_atomic_fact);
418                self.store_atomic_fact_without_well_defined_verified_and_infer(
419                    inferred_atomic_fact.clone(),
420                )?;
421                Ok(infer_result)
422            }
423            // Nonzero: `x $in R_nz` (etc.) => `x != 0`.
424            Obj::StandardSet(StandardSet::QNz)
425            | Obj::StandardSet(StandardSet::ZNz)
426            | Obj::StandardSet(StandardSet::RNz) => {
427                let zero_obj: Obj = Number::new("0".to_string()).into();
428                let inferred_atomic_fact =
429                    NotEqualFact::new(in_fact.element.clone(), zero_obj, in_fact.line_file.clone())
430                        .into();
431                let mut infer_result = InferResult::new();
432                infer_result.push_atomic_fact(&inferred_atomic_fact);
433                self.store_atomic_fact_without_well_defined_verified_and_infer(
434                    inferred_atomic_fact.clone(),
435                )?;
436                Ok(infer_result)
437            }
438            // `N` = {0,1,2,…}: store `n >= 0` so numeric resolution and order checks match `forall n N:`.
439            // Example: after `k $in N`, infer stores `k >= 0` (same as an explicit second line).
440            Obj::StandardSet(StandardSet::N) => {
441                let zero_obj: Obj = Number::new("0".to_string()).into();
442                let inferred_atomic_fact = GreaterEqualFact::new(
443                    in_fact.element.clone(),
444                    zero_obj,
445                    in_fact.line_file.clone(),
446                )
447                .into();
448                let mut infer_result = InferResult::new();
449                infer_result.push_atomic_fact(&inferred_atomic_fact);
450                self.store_atomic_fact_without_well_defined_verified_and_infer(
451                    inferred_atomic_fact.clone(),
452                )?;
453                Ok(infer_result)
454            }
455            // Full `Z`, `Q`, `R`: no extra atomic facts inferred here.
456            Obj::StandardSet(StandardSet::Q)
457            | Obj::StandardSet(StandardSet::Z)
458            | Obj::StandardSet(StandardSet::R) => Ok(InferResult::new()),
459            Obj::FamilyObj(family_obj) => {
460                self.infer_membership_in_family_from_in_fact(in_fact, family_obj)
461            }
462            // Finite sequence space: desugar to `FnSet`, then same as function-space membership.
463            Obj::FiniteSeqSet(fs) => {
464                let fn_set = self.finite_seq_set_to_fn_set(fs, in_fact.line_file.clone());
465                let mut infer_result =
466                    self.infer_membership_in_fn_set_from_in_fact(in_fact, &fn_set)?;
467                let expanded_atomic: AtomicFact = InFact::new(
468                    in_fact.element.clone(),
469                    fn_set.into(),
470                    in_fact.line_file.clone(),
471                )
472                .into();
473                infer_result.new_infer_result_inside(
474                    self.store_atomic_fact_without_well_defined_verified_and_infer(
475                        expanded_atomic,
476                    )?,
477                );
478                Ok(infer_result)
479            }
480            // General sequence set: desugar to `FnSet` + store expanded `InFact`.
481            Obj::SeqSet(ss) => {
482                let fn_set = self.seq_set_to_fn_set(ss, in_fact.line_file.clone());
483                let mut infer_result =
484                    self.infer_membership_in_fn_set_from_in_fact(in_fact, &fn_set)?;
485                let expanded_atomic: AtomicFact = InFact::new(
486                    in_fact.element.clone(),
487                    fn_set.into(),
488                    in_fact.line_file.clone(),
489                )
490                .into();
491                infer_result.new_infer_result_inside(
492                    self.store_atomic_fact_without_well_defined_verified_and_infer(
493                        expanded_atomic,
494                    )?,
495                );
496                Ok(infer_result)
497            }
498            // Matrix set: desugar to `FnSet` + store expanded `InFact`.
499            Obj::MatrixSet(ms) => {
500                let fn_set = self.matrix_set_to_fn_set(ms, in_fact.line_file.clone());
501                let mut infer_result =
502                    self.infer_membership_in_fn_set_from_in_fact(in_fact, &fn_set)?;
503                let expanded_atomic: AtomicFact = InFact::new(
504                    in_fact.element.clone(),
505                    fn_set.into(),
506                    in_fact.line_file.clone(),
507                )
508                .into();
509                infer_result.new_infer_result_inside(
510                    self.store_atomic_fact_without_well_defined_verified_and_infer(
511                        expanded_atomic,
512                    )?,
513                );
514                Ok(infer_result)
515            }
516            // Other set forms: no membership unfold on this path.
517            _ => Ok(InferResult::new()),
518        }
519    }
520
521    // Shared integer interval body: always `element $in Z`, lower `start <= element`, upper strict or closed.
522    fn infer_in_fact_element_in_integer_interval(
523        &mut self,
524        in_fact: &InFact,
525        start: Obj,
526        end: Obj,
527        end_inclusive: bool,
528    ) -> Result<InferResult, RuntimeError> {
529        let element = in_fact.element.clone();
530        let lf = in_fact.line_file.clone();
531
532        let inferred_in_z_fact =
533            InFact::new(element.clone(), StandardSet::Z.into(), lf.clone()).into();
534        let mut infer_result = InferResult::new();
535        infer_result.push_atomic_fact(&inferred_in_z_fact);
536        self.store_atomic_fact_without_well_defined_verified_and_infer(inferred_in_z_fact.clone())?;
537
538        let lower_bound = LessEqualFact::new(start, element.clone(), lf.clone()).into();
539        infer_result.push_atomic_fact(&lower_bound);
540        self.store_atomic_fact_without_well_defined_verified_and_infer(lower_bound.clone())?;
541
542        let upper_bound = if end_inclusive {
543            LessEqualFact::new(element, end, lf.clone()).into()
544        } else {
545            LessFact::new(element, end, lf.clone()).into()
546        };
547        infer_result.push_atomic_fact(&upper_bound);
548        self.store_atomic_fact_without_well_defined_verified_and_infer(upper_bound.clone())?;
549
550        Ok(infer_result)
551    }
552}