flux_infer/
fixpoint_encoding.rs

1//! Encoding of the refinement tree into a fixpoint constraint.
2
3use std::{collections::HashMap, hash::Hash, iter, ops::Range};
4
5use fixpoint::AdtId;
6use flux_common::{
7    bug,
8    cache::QueryCache,
9    dbg,
10    index::{IndexGen, IndexVec},
11    iter::IterExt,
12    span_bug, tracked_span_bug,
13};
14use flux_config::{self as config};
15use flux_errors::Errors;
16use flux_macros::DebugAsJson;
17use flux_middle::{
18    FixpointQueryKind,
19    def_id::{FluxDefId, MaybeExternId},
20    def_id_to_string,
21    global_env::GlobalEnv,
22    metrics::{self, Metric, TimingKind},
23    pretty::{NestedString, PrettyCx, PrettyNested},
24    queries::QueryResult,
25    query_bug,
26    rty::{
27        self, ESpan, EarlyReftParam, GenericArgsExt, InternalFuncKind, Lambda, List,
28        NameProvenance, PrettyMap, PrettyVar, SpecFuncKind, VariantIdx, fold::TypeFoldable as _,
29    },
30};
31use itertools::Itertools;
32use liquid_fixpoint::{
33    FixpointResult, FixpointStatus, SmtSolver,
34    parser::{FromSexp, ParseError},
35    sexp::Parser,
36};
37use rustc_data_structures::{
38    fx::{FxIndexMap, FxIndexSet},
39    unord::{UnordMap, UnordSet},
40};
41use rustc_hir::def_id::{DefId, LocalDefId};
42use rustc_index::newtype_index;
43use rustc_infer::infer::TyCtxtInferExt as _;
44use rustc_middle::ty::TypingMode;
45use rustc_span::{DUMMY_SP, Span, Symbol};
46use rustc_type_ir::{BoundVar, DebruijnIndex};
47use serde::{Deserialize, Deserializer, Serialize};
48
49use crate::{
50    fixpoint_encoding::fixpoint::FixpointTypes, fixpoint_qualifiers::FIXPOINT_QUALIFIERS,
51    lean_encoding::LeanEncoder, projections::structurally_normalize_expr,
52};
53
54pub mod decoding;
55
56pub mod fixpoint {
57    use std::fmt;
58
59    use flux_middle::{def_id::FluxDefId, rty::EarlyReftParam};
60    use liquid_fixpoint::{FixpointFmt, Identifier};
61    use rustc_abi::VariantIdx;
62    use rustc_index::newtype_index;
63    use rustc_middle::ty::ParamConst;
64    use rustc_span::Symbol;
65
66    newtype_index! {
67        #[orderable]
68        pub struct KVid {}
69    }
70
71    impl Identifier for KVid {
72        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
73            write!(f, "k{}", self.as_u32())
74        }
75    }
76
77    newtype_index! {
78        pub struct LocalVar {}
79    }
80
81    newtype_index! {
82        pub struct GlobalVar {}
83    }
84
85    newtype_index! {
86        /// Unique id assigned to each [`rty::AdtSortDef`] that needs to be encoded
87        /// into fixpoint
88        pub struct AdtId {}
89    }
90
91    #[derive(Hash, Copy, Clone, Debug, PartialEq, Eq)]
92    pub enum Var {
93        Underscore,
94        Global(GlobalVar, Option<FluxDefId>),
95        Local(LocalVar),
96        DataCtor(AdtId, VariantIdx),
97        TupleCtor { arity: usize },
98        TupleProj { arity: usize, field: u32 },
99        DataProj { adt_id: AdtId, /* variant_idx: VariantIdx, */ field: u32 },
100        UIFRel(BinRel),
101        Param(EarlyReftParam),
102        ConstGeneric(ParamConst),
103    }
104
105    impl From<GlobalVar> for Var {
106        fn from(v: GlobalVar) -> Self {
107            Self::Global(v, None)
108        }
109    }
110
111    impl From<LocalVar> for Var {
112        fn from(v: LocalVar) -> Self {
113            Self::Local(v)
114        }
115    }
116
117    impl Identifier for Var {
118        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
119            match self {
120                Var::Global(v, None) => write!(f, "c{}", v.as_u32()),
121                Var::Global(v, Some(did)) => write!(f, "f${}${}", did.name(), v.as_u32()),
122                Var::Local(v) => write!(f, "a{}", v.as_u32()),
123                Var::DataCtor(adt_id, variant_idx) => {
124                    write!(f, "mkadt{}${}", adt_id.as_u32(), variant_idx.as_u32())
125                }
126                Var::TupleCtor { arity } => write!(f, "mktuple{arity}"),
127                Var::TupleProj { arity, field } => write!(f, "tuple{arity}${field}"),
128                Var::DataProj { adt_id, field } => write!(f, "fld{}${field}", adt_id.as_u32()),
129                Var::UIFRel(BinRel::Gt) => write!(f, "gt"),
130                Var::UIFRel(BinRel::Ge) => write!(f, "ge"),
131                Var::UIFRel(BinRel::Lt) => write!(f, "lt"),
132                Var::UIFRel(BinRel::Le) => write!(f, "le"),
133                // these are actually not necessary because equality is interpreted for all sorts
134                Var::UIFRel(BinRel::Eq) => write!(f, "eq"),
135                Var::UIFRel(BinRel::Ne) => write!(f, "ne"),
136                Var::Underscore => write!(f, "_$"), // To avoid clashing with `_` used for `app (_ bv_op n)` for parametric SMT ops
137                Var::ConstGeneric(param) => {
138                    write!(f, "constgen${}${}", param.name, param.index)
139                }
140                Var::Param(param) => {
141                    write!(f, "reftgen${}${}", param.name, param.index)
142                }
143            }
144        }
145    }
146
147    #[derive(Clone, Hash, Debug, PartialEq, Eq)]
148    pub enum DataSort {
149        Tuple(usize),
150        Adt(AdtId),
151        User(FluxDefId),
152    }
153
154    impl Identifier for DataSort {
155        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
156            match self {
157                DataSort::Tuple(arity) => {
158                    write!(f, "Tuple{arity}")
159                }
160                DataSort::Adt(adt_id) => {
161                    write!(f, "Adt{}", adt_id.as_u32())
162                }
163                // There's no way to declare opaque sorts in the fixpoint horn syntax so we encode user
164                // declared opaque sorts as integers. Well-formedness should ensure values of these
165                // sorts are used "opaquely", i.e., the only values of these sorts are variables.
166                DataSort::User(..) => {
167                    write!(f, "int")
168                }
169            }
170        }
171    }
172
173    #[derive(Hash, Clone, Debug)]
174    pub struct SymStr(pub Symbol);
175
176    #[cfg(feature = "rust-fixpoint")]
177    impl FixpointFmt for SymStr {
178        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
179            write!(f, "{}", self.0)
180        }
181    }
182
183    #[cfg(not(feature = "rust-fixpoint"))]
184    impl FixpointFmt for SymStr {
185        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
186            write!(f, "\"{}\"", self.0)
187        }
188    }
189
190    liquid_fixpoint::declare_types! {
191        type Sort = DataSort;
192        type KVar = KVid;
193        type Var = Var;
194        type String = SymStr;
195        type Tag = super::TagIdx;
196    }
197    pub use fixpoint_generated::*;
198}
199
200/// A type to represent Solutions for KVars
201pub type Solution = HashMap<rty::KVid, rty::Binder<rty::Expr>>;
202
203/// A very explicit representation of [`Solution`] for debugging/tracing/serialization ONLY.
204#[derive(Serialize, DebugAsJson)]
205pub struct SolutionTrace(Vec<KvarSolutionTrace>);
206
207#[derive(Serialize, DebugAsJson)]
208pub struct KvarSolutionTrace {
209    pub name: String,
210    pub args: Vec<String>,
211    pub body: NestedString,
212}
213
214impl KvarSolutionTrace {
215    pub fn new(cx: &PrettyCx, kvid: rty::KVid, bind_expr: &rty::Binder<rty::Expr>) -> Self {
216        let mut args = Vec::new();
217        let body = cx
218            .nested_with_bound_vars("", bind_expr.vars(), Some("".to_string()), |prefix| {
219                for arg in prefix.split(',').map(|s| s.trim().to_string()) {
220                    args.push(arg);
221                }
222                bind_expr.skip_binder_ref().fmt_nested(cx)
223            })
224            .unwrap();
225
226        KvarSolutionTrace { name: format!("{kvid:?}"), args, body }
227    }
228}
229
230impl SolutionTrace {
231    pub fn new(genv: GlobalEnv, solution: &Solution) -> Self {
232        let cx = &PrettyCx::default(genv);
233        let res = solution
234            .iter()
235            .map(|(kvid, bind_expr)| KvarSolutionTrace::new(cx, *kvid, bind_expr))
236            .collect();
237        SolutionTrace(res)
238    }
239}
240
241#[derive(Debug, Clone, Default)]
242pub struct Answer<Tag> {
243    pub errors: Vec<Tag>,
244    pub solution: Solution,
245}
246
247impl<Tag> Answer<Tag> {
248    pub fn trivial() -> Self {
249        Self { errors: Vec::new(), solution: HashMap::new() }
250    }
251}
252
253newtype_index! {
254    #[debug_format = "TagIdx({})"]
255    pub struct TagIdx {}
256}
257
258impl Serialize for TagIdx {
259    fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
260        self.as_u32().serialize(serializer)
261    }
262}
263
264impl<'de> Deserialize<'de> for TagIdx {
265    fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
266        let idx = usize::deserialize(deserializer)?;
267        Ok(TagIdx::from_u32(idx as u32))
268    }
269}
270
271/// Keep track of all the data sorts that we need to define in fixpoint to encode the constraint.
272#[derive(Default)]
273pub struct SortEncodingCtxt {
274    /// Set of all the tuple arities that need to be defined
275    tuples: UnordSet<usize>,
276    /// Set of all the [`AdtDefSortDef`](rty::AdtSortDef) that need to be declared as
277    /// Fixpoint data-decls
278    adt_sorts: FxIndexSet<DefId>,
279    /// Set of all opaque types that need to be defined
280    user_sorts: FxIndexSet<FluxDefId>,
281}
282
283impl SortEncodingCtxt {
284    pub fn sort_to_fixpoint(&mut self, sort: &rty::Sort) -> fixpoint::Sort {
285        match sort {
286            rty::Sort::Int => fixpoint::Sort::Int,
287            rty::Sort::Real => fixpoint::Sort::Real,
288            rty::Sort::Bool => fixpoint::Sort::Bool,
289            rty::Sort::Str => fixpoint::Sort::Str,
290            rty::Sort::Char => fixpoint::Sort::Int,
291            rty::Sort::BitVec(size) => fixpoint::Sort::BitVec(Box::new(bv_size_to_fixpoint(*size))),
292            rty::Sort::App(rty::SortCtor::User(def_id), args) => {
293                self.declare_opaque_sort(*def_id);
294                let args = args.iter().map(|s| self.sort_to_fixpoint(s)).collect_vec();
295                fixpoint::Sort::App(
296                    fixpoint::SortCtor::Data(fixpoint::DataSort::User(*def_id)),
297                    args,
298                )
299            }
300            // There's no way to declare opaque sorts in the fixpoint horn syntax so we encode type
301            // parameter sorts and (unormalizable) type alias sorts as integers. Well-formedness
302            // should ensure values of these sorts are used "opaquely", i.e., the only values of
303            // these sorts are variables.
304            rty::Sort::Param(_)
305            | rty::Sort::Alias(rty::AliasKind::Opaque | rty::AliasKind::Projection, ..) => {
306                fixpoint::Sort::Int
307            }
308            rty::Sort::App(rty::SortCtor::Set, args) => {
309                let args = args.iter().map(|s| self.sort_to_fixpoint(s)).collect_vec();
310                fixpoint::Sort::App(fixpoint::SortCtor::Set, args)
311            }
312            rty::Sort::App(rty::SortCtor::Map, args) => {
313                let args = args.iter().map(|s| self.sort_to_fixpoint(s)).collect_vec();
314                fixpoint::Sort::App(fixpoint::SortCtor::Map, args)
315            }
316            rty::Sort::App(rty::SortCtor::Adt(sort_def), args) => {
317                if let Some(variant) = sort_def.opt_struct_variant() {
318                    let sorts = variant.field_sorts(args);
319                    // do not generate 1-tuples
320                    if let [sort] = &sorts[..] {
321                        self.sort_to_fixpoint(sort)
322                    } else {
323                        let adt_id = self.declare_adt(sort_def.did());
324                        let ctor = fixpoint::SortCtor::Data(fixpoint::DataSort::Adt(adt_id));
325                        let args = args.iter().map(|s| self.sort_to_fixpoint(s)).collect_vec();
326                        fixpoint::Sort::App(ctor, args)
327                    }
328                } else {
329                    debug_assert!(args.is_empty());
330                    let adt_id = self.declare_adt(sort_def.did());
331                    fixpoint::Sort::App(
332                        fixpoint::SortCtor::Data(fixpoint::DataSort::Adt(adt_id)),
333                        vec![],
334                    )
335                }
336            }
337            rty::Sort::Tuple(sorts) => {
338                // do not generate 1-tuples
339                if let [sort] = &sorts[..] {
340                    self.sort_to_fixpoint(sort)
341                } else {
342                    self.declare_tuple(sorts.len());
343                    let ctor = fixpoint::SortCtor::Data(fixpoint::DataSort::Tuple(sorts.len()));
344                    let args = sorts.iter().map(|s| self.sort_to_fixpoint(s)).collect();
345                    fixpoint::Sort::App(ctor, args)
346                }
347            }
348            rty::Sort::Func(sort) => self.func_sort_to_fixpoint(sort),
349            rty::Sort::Var(k) => fixpoint::Sort::Var(k.index()),
350            rty::Sort::Err
351            | rty::Sort::Infer(_)
352            | rty::Sort::Loc
353            | rty::Sort::Alias(rty::AliasKind::Free, _) => {
354                tracked_span_bug!("unexpected sort `{sort:?}`")
355            }
356        }
357    }
358
359    fn func_sort_to_fixpoint(&mut self, fsort: &rty::PolyFuncSort) -> fixpoint::Sort {
360        let params = fsort.params().len();
361        let fsort = fsort.skip_binders();
362        let output = self.sort_to_fixpoint(fsort.output());
363        fixpoint::Sort::mk_func(
364            params,
365            fsort.inputs().iter().map(|s| self.sort_to_fixpoint(s)),
366            output,
367        )
368    }
369
370    fn declare_tuple(&mut self, arity: usize) {
371        self.tuples.insert(arity);
372    }
373
374    pub fn declare_opaque_sort(&mut self, def_id: FluxDefId) {
375        self.user_sorts.insert(def_id);
376    }
377
378    pub fn declare_adt(&mut self, did: DefId) -> AdtId {
379        if let Some(idx) = self.adt_sorts.get_index_of(&did) {
380            AdtId::from_usize(idx)
381        } else {
382            let adt_id = AdtId::from_usize(self.adt_sorts.len());
383            self.adt_sorts.insert(did);
384            adt_id
385        }
386    }
387
388    pub fn user_sorts_to_fixpoint(&mut self, genv: GlobalEnv) -> Vec<fixpoint::SortDecl> {
389        self.user_sorts
390            .iter()
391            .map(|sort| {
392                let param_count = genv.sort_decl_param_count(sort);
393                fixpoint::SortDecl { name: fixpoint::DataSort::User(*sort), vars: param_count }
394            })
395            .collect()
396    }
397
398    fn append_adt_decls(
399        &mut self,
400        genv: GlobalEnv,
401        decls: &mut Vec<fixpoint::DataDecl>,
402    ) -> QueryResult {
403        // We iterate until we have processed all adt sorts because processing one adt sort may
404        // discover new adt sorts to process (e.g., if an adt field has an adt sort).
405        let mut idx = 0;
406        while let Some(adt_def_id) = self.adt_sorts.get_index(idx) {
407            let adt_id = AdtId::from_usize(idx);
408            let adt_sort_def = genv.adt_sort_def_of(adt_def_id)?;
409            decls.push(fixpoint::DataDecl {
410                name: fixpoint::DataSort::Adt(adt_id),
411                vars: adt_sort_def.param_count(),
412                ctors: adt_sort_def
413                    .variants()
414                    .iter_enumerated()
415                    .map(|(idx, variant)| {
416                        let name = fixpoint::Var::DataCtor(adt_id, idx);
417                        let fields = variant
418                            .field_sorts_instantiate_identity()
419                            .iter()
420                            .enumerate()
421                            .map(|(i, sort)| {
422                                fixpoint::DataField {
423                                    name: fixpoint::Var::DataProj { adt_id, field: i as u32 },
424                                    sort: self.sort_to_fixpoint(sort),
425                                }
426                            })
427                            .collect_vec();
428                        fixpoint::DataCtor { name, fields }
429                    })
430                    .collect(),
431            });
432            idx += 1;
433        }
434        Ok(())
435    }
436
437    fn append_tuple_decls(tuples: &UnordSet<usize>, decls: &mut Vec<fixpoint::DataDecl>) {
438        decls.extend(
439            tuples
440                .items()
441                .into_sorted_stable_ord()
442                .into_iter()
443                .map(|arity| {
444                    fixpoint::DataDecl {
445                        name: fixpoint::DataSort::Tuple(*arity),
446                        vars: *arity,
447                        ctors: vec![fixpoint::DataCtor {
448                            name: fixpoint::Var::TupleCtor { arity: *arity },
449                            fields: (0..(*arity as u32))
450                                .map(|field| {
451                                    fixpoint::DataField {
452                                        name: fixpoint::Var::TupleProj { arity: *arity, field },
453                                        sort: fixpoint::Sort::Var(field as usize),
454                                    }
455                                })
456                                .collect(),
457                        }],
458                    }
459                }),
460        );
461    }
462
463    pub fn encode_data_decls(&mut self, genv: GlobalEnv) -> QueryResult<Vec<fixpoint::DataDecl>> {
464        let mut decls = vec![];
465        self.append_adt_decls(genv, &mut decls)?;
466        Self::append_tuple_decls(&self.tuples, &mut decls);
467        Ok(decls)
468    }
469}
470
471fn bv_size_to_fixpoint(size: rty::BvSize) -> fixpoint::Sort {
472    match size {
473        rty::BvSize::Fixed(size) => fixpoint::Sort::BvSize(size),
474        rty::BvSize::Param(_var) => {
475            // I think we could encode the size as a sort variable, but this would require some care
476            // because smtlib doesn't really support parametric sizes. Fixpoint is probably already
477            // too liberal about this and it'd be easy to make it crash.
478            // fixpoint::Sort::Var(var.index)
479            bug!("unexpected parametric bit-vector size")
480        }
481        rty::BvSize::Infer(_) => bug!("unexpected infer variable for bit-vector size"),
482    }
483}
484
485pub type FunDefMap = FxIndexMap<FluxDefId, fixpoint::Var>;
486type ConstMap<'tcx> = FxIndexMap<ConstKey<'tcx>, fixpoint::ConstDecl>;
487
488#[derive(Eq, Hash, PartialEq, Clone)]
489enum ConstKey<'tcx> {
490    Uif(FluxDefId),
491    RustConst(DefId),
492    Alias(FluxDefId, rustc_middle::ty::GenericArgsRef<'tcx>),
493    Lambda(Lambda),
494    PrimOp(rty::BinOp),
495    Cast(rty::Sort, rty::Sort),
496}
497
498#[derive(Clone)]
499pub enum Backend {
500    Fixpoint,
501    Lean,
502}
503
504pub struct FixpointCtxt<'genv, 'tcx, T: Eq + Hash> {
505    comments: Vec<String>,
506    genv: GlobalEnv<'genv, 'tcx>,
507    kvars: KVarGen,
508    scx: SortEncodingCtxt,
509    kcx: KVarEncodingCtxt,
510    ecx: ExprEncodingCtxt<'genv, 'tcx>,
511    tags: IndexVec<TagIdx, T>,
512    tags_inv: UnordMap<T, TagIdx>,
513}
514
515pub type FixQueryCache = QueryCache<FixpointResult<TagIdx>>;
516
517impl<'genv, 'tcx, Tag> FixpointCtxt<'genv, 'tcx, Tag>
518where
519    Tag: std::hash::Hash + Eq + Copy,
520{
521    pub fn new(
522        genv: GlobalEnv<'genv, 'tcx>,
523        def_id: MaybeExternId,
524        kvars: KVarGen,
525        backend: Backend,
526    ) -> Self {
527        Self {
528            comments: vec![],
529            kvars,
530            scx: SortEncodingCtxt::default(),
531            genv,
532            ecx: ExprEncodingCtxt::new(genv, Some(def_id), backend),
533            kcx: Default::default(),
534            tags: IndexVec::new(),
535            tags_inv: Default::default(),
536        }
537    }
538
539    pub fn check(
540        mut self,
541        cache: &mut FixQueryCache,
542        def_id: MaybeExternId,
543        constraint: fixpoint::Constraint,
544        kind: FixpointQueryKind,
545        scrape_quals: bool,
546        solver: SmtSolver,
547    ) -> QueryResult<Answer<Tag>> {
548        // skip checking trivial constraints
549        let count = constraint.concrete_head_count();
550        metrics::incr_metric(Metric::CsTotal, count as u32);
551        if count == 0 {
552            metrics::incr_metric_if(kind.is_body(), Metric::FnTrivial);
553            self.ecx.errors.to_result()?;
554            return Ok(Answer::trivial());
555        }
556        let def_span = self.ecx.def_span();
557        let kvars = self.kcx.encode_kvars(&self.kvars, &mut self.scx);
558        let (define_funs, define_constants) = self.ecx.define_funs(def_id, &mut self.scx)?;
559        let qualifiers = self
560            .ecx
561            .qualifiers_for(def_id.local_id(), &mut self.scx)?
562            .into_iter()
563            .chain(FIXPOINT_QUALIFIERS.iter().cloned())
564            .collect();
565
566        // Assuming values should happen after all encoding is done so we are sure we've collected
567        // all constants.
568        let constraint = self.ecx.assume_const_values(constraint, &mut self.scx)?;
569
570        let mut constants = self.ecx.const_env.const_map.values().cloned().collect_vec();
571        constants.extend(define_constants);
572
573        // The rust fixpoint implementation does not yet support polymorphic functions.
574        // For now we avoid including these by default so that cases where they are not needed can work.
575        // Should be removed when support is added.
576        #[cfg(not(feature = "rust-fixpoint"))]
577        for rel in fixpoint::BinRel::INEQUALITIES {
578            // ∀a. a -> a -> bool
579            let sort = fixpoint::Sort::mk_func(
580                1,
581                [fixpoint::Sort::Var(0), fixpoint::Sort::Var(0)],
582                fixpoint::Sort::Bool,
583            );
584            constants.push(fixpoint::ConstDecl {
585                name: fixpoint::Var::UIFRel(rel),
586                sort,
587                comment: None,
588            });
589        }
590
591        // We are done encoding expressions. Check if there are any errors.
592        self.ecx.errors.to_result()?;
593
594        let task = fixpoint::Task {
595            comments: self.comments.clone(),
596            constants,
597            kvars,
598            define_funs,
599            constraint,
600            qualifiers,
601            scrape_quals,
602            solver,
603            data_decls: self.scx.encode_data_decls(self.genv)?,
604        };
605        if config::dump_constraint() {
606            dbg::dump_item_info(self.genv.tcx(), def_id.resolved_id(), "smt2", &task).unwrap();
607        }
608
609        let result = Self::run_task_with_cache(self.genv, task, def_id.resolved_id(), kind, cache);
610        let solution = if config::dump_checker_trace_info() {
611            self.parse_kvar_solutions(&result)?
612        } else {
613            HashMap::default()
614        };
615
616        let errors = match result.status {
617            FixpointStatus::Safe(_) => vec![],
618            FixpointStatus::Unsafe(_, errors) => {
619                metrics::incr_metric(Metric::CsError, errors.len() as u32);
620                errors
621                    .into_iter()
622                    .map(|err| self.tags[err.tag])
623                    .unique()
624                    .collect_vec()
625            }
626            FixpointStatus::Crash(err) => span_bug!(def_span, "fixpoint crash: {err:?}"),
627        };
628        Ok(Answer { errors, solution })
629    }
630
631    fn parse_kvar_solutions(&self, result: &FixpointResult<TagIdx>) -> QueryResult<Solution> {
632        Ok(self.kcx.group_kvar_solution(
633            result
634                .solution
635                .iter()
636                .chain(&result.non_cuts_solution)
637                .map(|b| self.convert_kvar_bind(b))
638                .try_collect_vec()?,
639        ))
640    }
641
642    fn convert_solution(&self, expr: &str) -> QueryResult<rty::Binder<rty::Expr>> {
643        // 1. convert str -> sexp
644        let mut sexp_parser = Parser::new(expr);
645        let sexp = match sexp_parser.parse() {
646            Ok(sexp) => sexp,
647            Err(err) => {
648                tracked_span_bug!("cannot parse sexp: {expr:?}: {err:?}");
649            }
650        };
651
652        // 2. convert sexp -> (binds, Expr<fixpoint_encoding::Types>)
653        let mut sexp_ctx = SexpParseCtxt.into_wrapper();
654        let (sorts, expr) = sexp_ctx.parse_solution(&sexp).unwrap_or_else(|err| {
655            tracked_span_bug!("failed to parse solution sexp {sexp:?}: {err:?}");
656        });
657
658        // 3. convert Expr<fixpoint_encoding::Types> -> Expr<rty::Expr>
659        let sorts = sorts
660            .iter()
661            .map(|s| self.fixpoint_to_sort(s))
662            .try_collect_vec()
663            .unwrap_or_else(|err| {
664                tracked_span_bug!("failed to convert sorts: {err:?}");
665            });
666        let expr = self
667            .fixpoint_to_expr(&expr)
668            .unwrap_or_else(|err| tracked_span_bug!("failed to convert expr: {err:?}"));
669        Ok(rty::Binder::bind_with_sorts(expr, &sorts))
670    }
671
672    fn convert_kvar_bind(
673        &self,
674        b: &liquid_fixpoint::KVarBind,
675    ) -> QueryResult<(fixpoint::KVid, rty::Binder<rty::Expr>)> {
676        let kvid = parse_kvid(&b.kvar);
677        let expr = self.convert_solution(&b.val)?;
678        Ok((kvid, expr))
679    }
680
681    pub fn generate_and_check_lean_lemmas(
682        mut self,
683        constraint: fixpoint::Constraint,
684    ) -> QueryResult<()> {
685        if let Some(def_id) = self.ecx.def_id {
686            let kvar_decls = self.kcx.encode_kvars(&self.kvars, &mut self.scx);
687            self.ecx.errors.to_result()?;
688
689            let lean_encoder = LeanEncoder::new(self.genv, self.ecx.local_var_env.pretty_var_map);
690            lean_encoder
691                .encode_constraint(def_id, &kvar_decls, &constraint)
692                .map_err(|_| query_bug!("could not encode constraint"))?;
693
694            if flux_config::lean().is_check() { lean_encoder.check_proof(def_id) } else { Ok(()) }
695        } else {
696            Ok(())
697        }
698    }
699
700    fn run_task_with_cache(
701        genv: GlobalEnv,
702        task: fixpoint::Task,
703        def_id: DefId,
704        kind: FixpointQueryKind,
705        cache: &mut FixQueryCache,
706    ) -> FixpointResult<TagIdx> {
707        let key = kind.task_key(genv.tcx(), def_id);
708
709        let hash = task.hash_with_default();
710
711        if config::is_cache_enabled()
712            && let Some(result) = cache.lookup(&key, hash)
713        {
714            metrics::incr_metric_if(kind.is_body(), Metric::FnCached);
715            return result.clone();
716        }
717        let result = metrics::time_it(TimingKind::FixpointQuery(def_id, kind), || {
718            task.run()
719                .unwrap_or_else(|err| tracked_span_bug!("failed to run fixpoint: {err}"))
720        });
721
722        if config::is_cache_enabled() {
723            cache.insert(key, hash, result.clone());
724        }
725
726        result
727    }
728
729    fn tag_idx(&mut self, tag: Tag) -> TagIdx
730    where
731        Tag: std::fmt::Debug,
732    {
733        *self.tags_inv.entry(tag).or_insert_with(|| {
734            let idx = self.tags.push(tag);
735            self.comments.push(format!("Tag {idx}: {tag:?}"));
736            idx
737        })
738    }
739
740    pub(crate) fn with_early_param(&mut self, param: &EarlyReftParam) {
741        self.ecx
742            .local_var_env
743            .pretty_var_map
744            .set(PrettyVar::Param(*param), Some(param.name));
745    }
746
747    pub(crate) fn with_name_map<R>(
748        &mut self,
749        name: rty::Name,
750        provenance: NameProvenance,
751        f: impl FnOnce(&mut Self, fixpoint::LocalVar) -> R,
752    ) -> R {
753        let fresh = self.ecx.local_var_env.insert_fvar_map(name, provenance);
754        let r = f(self, fresh);
755        self.ecx.local_var_env.remove_fvar_map(name);
756        r
757    }
758
759    pub(crate) fn sort_to_fixpoint(&mut self, sort: &rty::Sort) -> fixpoint::Sort {
760        self.scx.sort_to_fixpoint(sort)
761    }
762
763    pub(crate) fn var_to_fixpoint(&self, var: &rty::Var) -> fixpoint::Var {
764        self.ecx.var_to_fixpoint(var)
765    }
766
767    /// Encodes an expression in head position as a [`fixpoint::Constraint`] "peeling out"
768    /// implications and foralls.
769    ///
770    /// [`fixpoint::Constraint`]: liquid_fixpoint::Constraint
771    pub(crate) fn head_to_fixpoint(
772        &mut self,
773        expr: &rty::Expr,
774        mk_tag: impl Fn(Option<ESpan>) -> Tag + Copy,
775    ) -> QueryResult<fixpoint::Constraint>
776    where
777        Tag: std::fmt::Debug,
778    {
779        match expr.kind() {
780            rty::ExprKind::BinaryOp(rty::BinOp::And, ..) => {
781                // avoid creating nested conjunctions
782                let cstrs = expr
783                    .flatten_conjs()
784                    .into_iter()
785                    .map(|e| self.head_to_fixpoint(e, mk_tag))
786                    .try_collect()?;
787                Ok(fixpoint::Constraint::conj(cstrs))
788            }
789            rty::ExprKind::BinaryOp(rty::BinOp::Imp, e1, e2) => {
790                let (bindings, assumption) = self.assumption_to_fixpoint(e1)?;
791                let cstr = self.head_to_fixpoint(e2, mk_tag)?;
792                Ok(fixpoint::Constraint::foralls(bindings, mk_implies(assumption, cstr)))
793            }
794            rty::ExprKind::KVar(kvar) => {
795                let mut bindings = vec![];
796                let pred = self.kvar_to_fixpoint(kvar, &mut bindings)?;
797                Ok(fixpoint::Constraint::foralls(bindings, fixpoint::Constraint::Pred(pred, None)))
798            }
799            rty::ExprKind::ForAll(pred) => {
800                self.ecx
801                    .local_var_env
802                    .push_layer_with_fresh_names(pred.vars().len());
803                let cstr = self.head_to_fixpoint(pred.as_ref().skip_binder(), mk_tag)?;
804                let vars = self.ecx.local_var_env.pop_layer();
805
806                let bindings = iter::zip(vars, pred.vars())
807                    .map(|(var, kind)| {
808                        fixpoint::Bind {
809                            name: var.into(),
810                            sort: self.scx.sort_to_fixpoint(kind.expect_sort()),
811                            pred: fixpoint::Pred::TRUE,
812                        }
813                    })
814                    .collect_vec();
815
816                Ok(fixpoint::Constraint::foralls(bindings, cstr))
817            }
818            _ => {
819                let tag_idx = self.tag_idx(mk_tag(expr.span()));
820                let pred = fixpoint::Pred::Expr(self.ecx.expr_to_fixpoint(expr, &mut self.scx)?);
821                Ok(fixpoint::Constraint::Pred(pred, Some(tag_idx)))
822            }
823        }
824    }
825
826    /// Encodes an expression in assumptive position as a [`fixpoint::Pred`]. Returns the encoded
827    /// predicate and a list of bindings produced by ANF-ing kvars.
828    ///
829    /// [`fixpoint::Pred`]: liquid_fixpoint::Pred
830    pub(crate) fn assumption_to_fixpoint(
831        &mut self,
832        pred: &rty::Expr,
833    ) -> QueryResult<(Vec<fixpoint::Bind>, fixpoint::Pred)> {
834        let mut bindings = vec![];
835        let mut preds = vec![];
836        self.assumption_to_fixpoint_aux(pred, &mut bindings, &mut preds)?;
837        Ok((bindings, fixpoint::Pred::and(preds)))
838    }
839
840    /// Auxiliary function to merge nested conjunctions in a single predicate
841    fn assumption_to_fixpoint_aux(
842        &mut self,
843        expr: &rty::Expr,
844        bindings: &mut Vec<fixpoint::Bind>,
845        preds: &mut Vec<fixpoint::Pred>,
846    ) -> QueryResult {
847        match expr.kind() {
848            rty::ExprKind::BinaryOp(rty::BinOp::And, e1, e2) => {
849                self.assumption_to_fixpoint_aux(e1, bindings, preds)?;
850                self.assumption_to_fixpoint_aux(e2, bindings, preds)?;
851            }
852            rty::ExprKind::KVar(kvar) => {
853                preds.push(self.kvar_to_fixpoint(kvar, bindings)?);
854            }
855            rty::ExprKind::ForAll(_) => {
856                // If a forall appears in assumptive position replace it with true. This is sound
857                // because we are weakening the context, i.e., anything true without the assumption
858                // should remain true after adding it. Note that this relies on the predicate
859                // appearing negatively. This is guaranteed by the surface syntax because foralls
860                // can only appear at the top-level in a requires clause.
861                preds.push(fixpoint::Pred::TRUE);
862            }
863            _ => {
864                preds.push(fixpoint::Pred::Expr(self.ecx.expr_to_fixpoint(expr, &mut self.scx)?));
865            }
866        }
867        Ok(())
868    }
869
870    fn kvar_to_fixpoint(
871        &mut self,
872        kvar: &rty::KVar,
873        bindings: &mut Vec<fixpoint::Bind>,
874    ) -> QueryResult<fixpoint::Pred> {
875        let decl = self.kvars.get(kvar.kvid);
876        let kvids = self.kcx.declare(kvar.kvid, decl, &self.ecx.backend);
877
878        let all_args = iter::zip(&kvar.args, &decl.sorts)
879            .map(|(arg, sort)| self.ecx.imm(arg, sort, &mut self.scx, bindings))
880            .try_collect_vec()?;
881
882        // Fixpoint doesn't support kvars without arguments, which we do generate sometimes. To get
883        // around it, we encode `$k()` as ($k 0), or more precisely `(forall ((x int) (= x 0)) ... ($k x)`
884        // after ANF-ing.
885        if all_args.is_empty() {
886            let fresh = self.ecx.local_var_env.fresh_name();
887            let var = fixpoint::Var::Local(fresh);
888            bindings.push(fixpoint::Bind {
889                name: fresh.into(),
890                sort: fixpoint::Sort::Int,
891                pred: fixpoint::Pred::Expr(fixpoint::Expr::eq(
892                    fixpoint::Expr::Var(var),
893                    fixpoint::Expr::int(0),
894                )),
895            });
896            return Ok(fixpoint::Pred::KVar(kvids.start, vec![fixpoint::Expr::Var(var)]));
897        }
898
899        let kvars = kvids
900            .enumerate()
901            .map(|(i, kvid)| {
902                let args = all_args[i..].to_vec();
903                fixpoint::Pred::KVar(kvid, args)
904            })
905            .collect_vec();
906
907        Ok(fixpoint::Pred::And(kvars))
908    }
909}
910
911fn const_to_fixpoint(cst: rty::Constant) -> fixpoint::Expr {
912    match cst {
913        rty::Constant::Int(i) => {
914            if i.is_negative() {
915                fixpoint::Expr::Neg(Box::new(fixpoint::Constant::Numeral(i.abs()).into()))
916            } else {
917                fixpoint::Constant::Numeral(i.abs()).into()
918            }
919        }
920        rty::Constant::Real(r) => fixpoint::Constant::Real(r.0).into(),
921        rty::Constant::Bool(b) => fixpoint::Constant::Boolean(b).into(),
922        rty::Constant::Char(c) => fixpoint::Constant::Numeral(u128::from(c)).into(),
923        rty::Constant::Str(s) => fixpoint::Constant::String(fixpoint::SymStr(s)).into(),
924        rty::Constant::BitVec(i, size) => fixpoint::Constant::BitVec(i, size).into(),
925    }
926}
927
928/// During encoding into fixpoint we generate multiple fixpoint kvars per kvar in flux. A
929/// [`KVarEncodingCtxt`] is used to keep track of the state needed for this.
930///
931/// See [`KVarEncoding`]
932#[derive(Default)]
933struct KVarEncodingCtxt {
934    /// A map from a [`rty::KVid`] to the range of [`fixpoint::KVid`]s that will be used to
935    /// encode it.
936    ranges: FxIndexMap<rty::KVid, Range<fixpoint::KVid>>,
937}
938
939impl KVarEncodingCtxt {
940    /// Declares that a kvar has to be encoded into fixpoint and assigns a range of
941    /// [`fixpoint::KVid`]'s to it.
942    fn declare(
943        &mut self,
944        kvid: rty::KVid,
945        decl: &KVarDecl,
946        backend: &Backend,
947    ) -> Range<fixpoint::KVid> {
948        // The start of the next range
949        let start = self
950            .ranges
951            .last()
952            .map_or(fixpoint::KVid::from_u32(0), |(_, r)| r.end);
953
954        self.ranges
955            .entry(kvid)
956            .or_insert_with(|| {
957                let single_encoding = matches!(decl.encoding, KVarEncoding::Single)
958                    || matches!(backend, Backend::Lean);
959                if single_encoding {
960                    start..start + 1
961                } else {
962                    let n = usize::max(decl.self_args, 1);
963                    start..start + n
964                }
965            })
966            .clone()
967    }
968
969    fn encode_kvars(&self, kvars: &KVarGen, scx: &mut SortEncodingCtxt) -> Vec<fixpoint::KVarDecl> {
970        self.ranges
971            .iter()
972            .flat_map(|(orig, range)| {
973                let mut all_sorts = kvars
974                    .get(*orig)
975                    .sorts
976                    .iter()
977                    .map(|s| scx.sort_to_fixpoint(s))
978                    .collect_vec();
979
980                // See comment in `kvar_to_fixpoint`
981                if all_sorts.is_empty() {
982                    all_sorts = vec![fixpoint::Sort::Int];
983                }
984
985                range.clone().enumerate().map(move |(i, kvid)| {
986                    let sorts = all_sorts[i..].to_vec();
987                    fixpoint::KVarDecl::new(kvid, sorts, format!("orig: {:?}", orig))
988                })
989            })
990            .collect()
991    }
992
993    /// For each [`rty::KVid`] `$k`, this function collects all predicates associated
994    /// with the [`fixpoint::KVid`]s that encode `$k` and combines them into a single
995    /// predicate by conjoining them.
996    ///
997    /// A group (i.e., a combined predicate) is included in the result only if *all*
998    /// [`fixpoint::KVid`]s in the encoding range of `$k` are present in the input.
999    fn group_kvar_solution(
1000        &self,
1001        mut items: Vec<(fixpoint::KVid, rty::Binder<rty::Expr>)>,
1002    ) -> HashMap<rty::KVid, rty::Binder<rty::Expr>> {
1003        let mut map = HashMap::default();
1004
1005        items.sort_by_key(|(kvid, _)| *kvid);
1006        items.reverse();
1007
1008        for (orig, range) in &self.ranges {
1009            let mut preds = vec![];
1010            while let Some((_, t)) = items.pop_if(|(k, _)| range.contains(k)) {
1011                preds.push(t);
1012            }
1013            // We only put it in the map if the entire range is present.
1014            if preds.len() == range.end.as_usize() - range.start.as_usize() {
1015                let vars = preds[0].vars().clone();
1016                let conj = rty::Expr::and_from_iter(
1017                    preds
1018                        .into_iter()
1019                        .enumerate()
1020                        .map(|(i, e)| e.skip_binder().shift_horizontally(i)),
1021                );
1022                map.insert(*orig, rty::Binder::bind_with_vars(conj, vars));
1023            }
1024        }
1025        map
1026    }
1027}
1028
1029/// Environment used to map from [`rty::Var`] to a [`fixpoint::LocalVar`].
1030struct LocalVarEnv {
1031    local_var_gen: IndexGen<fixpoint::LocalVar>,
1032    fvars: UnordMap<rty::Name, fixpoint::LocalVar>,
1033    /// Layers of late bound variables
1034    layers: Vec<Vec<fixpoint::LocalVar>>,
1035    /// While it might seem like the signature should be
1036    /// [`UnordMap<fixpoint::LocalVar, rty::Var>`], we encode the arguments to
1037    /// kvars (which can be arbitrary expressions) as local variables; thus we
1038    /// need to keep the output as an [`rty::Expr`] to reflect this.
1039    reverse_map: UnordMap<fixpoint::LocalVar, rty::Expr>,
1040    pretty_var_map: PrettyMap<fixpoint::LocalVar>,
1041}
1042
1043impl LocalVarEnv {
1044    fn new() -> Self {
1045        Self {
1046            local_var_gen: IndexGen::new(),
1047            fvars: Default::default(),
1048            layers: Vec::new(),
1049            reverse_map: Default::default(),
1050            pretty_var_map: PrettyMap::new(),
1051        }
1052    }
1053
1054    // This doesn't require to be mutable because `IndexGen` uses atomics, but we make it mutable
1055    // to better declare the intent.
1056    fn fresh_name(&mut self) -> fixpoint::LocalVar {
1057        self.local_var_gen.fresh()
1058    }
1059
1060    fn insert_fvar_map(
1061        &mut self,
1062        name: rty::Name,
1063        provenance: NameProvenance,
1064    ) -> fixpoint::LocalVar {
1065        let fresh = self.fresh_name();
1066        self.fvars.insert(name, fresh);
1067        self.reverse_map.insert(fresh, rty::Expr::fvar(name));
1068        self.pretty_var_map
1069            .set(PrettyVar::Local(fresh), provenance.opt_symbol());
1070        fresh
1071    }
1072
1073    fn remove_fvar_map(&mut self, name: rty::Name) {
1074        self.fvars.remove(&name);
1075    }
1076
1077    /// Push a layer of bound variables assigning a fresh [`fixpoint::LocalVar`] to each one
1078    fn push_layer_with_fresh_names(&mut self, count: usize) {
1079        let layer = (0..count).map(|_| self.fresh_name()).collect();
1080        self.layers.push(layer);
1081        // FIXME: (ck) what to put in reverse_map here?
1082    }
1083
1084    fn pop_layer(&mut self) -> Vec<fixpoint::LocalVar> {
1085        self.layers.pop().unwrap()
1086    }
1087
1088    fn get_fvar(&self, name: rty::Name) -> Option<fixpoint::LocalVar> {
1089        self.fvars.get(&name).copied()
1090    }
1091
1092    fn get_late_bvar(&self, debruijn: DebruijnIndex, var: BoundVar) -> Option<fixpoint::LocalVar> {
1093        let depth = self.layers.len().checked_sub(debruijn.as_usize() + 1)?;
1094        self.layers[depth].get(var.as_usize()).copied()
1095    }
1096}
1097
1098pub struct KVarGen {
1099    kvars: IndexVec<rty::KVid, KVarDecl>,
1100    /// If true, generate dummy [holes] instead of kvars. Used during shape mode to avoid generating
1101    /// unnecessary kvars.
1102    ///
1103    /// [holes]: rty::ExprKind::Hole
1104    dummy: bool,
1105}
1106
1107impl KVarGen {
1108    pub(crate) fn new(dummy: bool) -> Self {
1109        Self { kvars: IndexVec::new(), dummy }
1110    }
1111
1112    fn get(&self, kvid: rty::KVid) -> &KVarDecl {
1113        &self.kvars[kvid]
1114    }
1115
1116    /// Generate a fresh [kvar] under several layers of [binders]. Each layer may contain any kind
1117    /// of bound variable, but variables that are not of kind [`BoundVariableKind::Refine`] will
1118    /// be filtered out.
1119    ///
1120    /// The variables bound in the last layer (last element of the `binders` slice) is expected to
1121    /// have only [`BoundVariableKind::Refine`] and all its elements are used as the [self arguments].
1122    /// The rest of the binders are appended to the `scope`.
1123    ///
1124    /// Note that the returned expression will have escaping variables and it is up to the caller to
1125    /// put it under an appropriate number of binders.
1126    ///
1127    /// Prefer using [`InferCtxt::fresh_kvar`] when possible.
1128    ///
1129    /// [binders]: rty::Binder
1130    /// [kvar]: rty::KVar
1131    /// [`InferCtxt::fresh_kvar`]: crate::infer::InferCtxt::fresh_kvar
1132    /// [self arguments]: rty::KVar::self_args
1133    /// [`BoundVariableKind::Refine`]: rty::BoundVariableKind::Refine
1134    pub fn fresh(
1135        &mut self,
1136        binders: &[rty::BoundVariableKinds],
1137        scope: impl IntoIterator<Item = (rty::Var, rty::Sort)>,
1138        encoding: KVarEncoding,
1139    ) -> rty::Expr {
1140        if self.dummy {
1141            return rty::Expr::hole(rty::HoleKind::Pred);
1142        }
1143
1144        let args = itertools::chain(
1145            binders.iter().rev().enumerate().flat_map(|(level, vars)| {
1146                let debruijn = DebruijnIndex::from_usize(level);
1147                vars.iter()
1148                    .cloned()
1149                    .enumerate()
1150                    .flat_map(move |(idx, var)| {
1151                        if let rty::BoundVariableKind::Refine(sort, _, kind) = var {
1152                            let br = rty::BoundReft { var: BoundVar::from_usize(idx), kind };
1153                            Some((rty::Var::Bound(debruijn, br), sort))
1154                        } else {
1155                            None
1156                        }
1157                    })
1158            }),
1159            scope,
1160        );
1161        let [.., last] = binders else {
1162            return self.fresh_inner(0, [], encoding);
1163        };
1164        let num_self_args = last
1165            .iter()
1166            .filter(|var| matches!(var, rty::BoundVariableKind::Refine(..)))
1167            .count();
1168        self.fresh_inner(num_self_args, args, encoding)
1169    }
1170
1171    fn fresh_inner<A>(&mut self, self_args: usize, args: A, encoding: KVarEncoding) -> rty::Expr
1172    where
1173        A: IntoIterator<Item = (rty::Var, rty::Sort)>,
1174    {
1175        // asset last one has things
1176        let mut sorts = vec![];
1177        let mut exprs = vec![];
1178
1179        let mut flattened_self_args = 0;
1180        for (i, (var, sort)) in args.into_iter().enumerate() {
1181            let is_self_arg = i < self_args;
1182            let var = var.to_expr();
1183            sort.walk(|sort, proj| {
1184                if !matches!(sort, rty::Sort::Loc) {
1185                    flattened_self_args += is_self_arg as usize;
1186                    sorts.push(sort.clone());
1187                    exprs.push(rty::Expr::field_projs(&var, proj));
1188                }
1189            });
1190        }
1191
1192        let kvid = self
1193            .kvars
1194            .push(KVarDecl { self_args: flattened_self_args, sorts, encoding });
1195
1196        let kvar = rty::KVar::new(kvid, flattened_self_args, exprs);
1197        rty::Expr::kvar(kvar)
1198    }
1199}
1200
1201#[derive(Clone)]
1202struct KVarDecl {
1203    self_args: usize,
1204    sorts: Vec<rty::Sort>,
1205    encoding: KVarEncoding,
1206}
1207
1208/// How an [`rty::KVar`] is encoded in the fixpoint constraint
1209#[derive(Clone, Copy)]
1210pub enum KVarEncoding {
1211    /// Generate a single kvar appending the self arguments and the scope, i.e.,
1212    /// a kvar `$k(a0, ...)[b0, ...]` becomes `$k(a0, ..., b0, ...)` in the fixpoint constraint.
1213    Single,
1214    /// Generate a conjunction of kvars, one per argument in [`rty::KVar::args`].
1215    /// Concretely, a kvar `$k(a0, a1, ..., an)[b0, ...]` becomes
1216    /// `$k0(a0, a1, ..., an, b0, ...) ∧ $k1(a1, ..., an, b0, ...) ∧ ... ∧ $kn(an, b0, ...)`
1217    Conj,
1218}
1219
1220impl std::fmt::Display for TagIdx {
1221    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1222        write!(f, "{}", self.as_u32())
1223    }
1224}
1225
1226impl std::str::FromStr for TagIdx {
1227    type Err = std::num::ParseIntError;
1228
1229    fn from_str(s: &str) -> Result<Self, Self::Err> {
1230        Ok(Self::from_u32(s.parse()?))
1231    }
1232}
1233
1234#[derive(Default)]
1235struct ConstEnv<'tcx> {
1236    const_map: ConstMap<'tcx>,
1237    const_map_rev: HashMap<fixpoint::GlobalVar, ConstKey<'tcx>>,
1238    global_var_gen: IndexGen<fixpoint::GlobalVar>,
1239    pub fun_def_map: FunDefMap,
1240}
1241
1242impl<'tcx> ConstEnv<'tcx> {
1243    fn get_or_insert(
1244        &mut self,
1245        key: ConstKey<'tcx>,
1246        // make_name: impl FnOnce() -> fixpoint::GlobalVar,
1247        make_const_decl: impl FnOnce(fixpoint::GlobalVar) -> fixpoint::ConstDecl,
1248    ) -> &fixpoint::ConstDecl {
1249        self.const_map.entry(key.clone()).or_insert_with(|| {
1250            let global_name = self.global_var_gen.fresh();
1251            self.const_map_rev.insert(global_name, key);
1252            make_const_decl(global_name)
1253        })
1254    }
1255}
1256
1257pub struct ExprEncodingCtxt<'genv, 'tcx> {
1258    genv: GlobalEnv<'genv, 'tcx>,
1259    local_var_env: LocalVarEnv,
1260    const_env: ConstEnv<'tcx>,
1261    errors: Errors<'genv>,
1262    /// Id of the item being checked. This is a [`MaybeExternId`] because we may be encoding
1263    /// invariants for an extern spec on an enum.
1264    def_id: Option<MaybeExternId>,
1265    infcx: rustc_infer::infer::InferCtxt<'tcx>,
1266    backend: Backend,
1267}
1268
1269impl<'genv, 'tcx> ExprEncodingCtxt<'genv, 'tcx> {
1270    pub fn new(
1271        genv: GlobalEnv<'genv, 'tcx>,
1272        def_id: Option<MaybeExternId>,
1273        backend: Backend,
1274    ) -> Self {
1275        Self {
1276            genv,
1277            local_var_env: LocalVarEnv::new(),
1278            const_env: Default::default(),
1279            errors: Errors::new(genv.sess()),
1280            def_id,
1281            infcx: genv
1282                .tcx()
1283                .infer_ctxt()
1284                .with_next_trait_solver(true)
1285                .build(TypingMode::non_body_analysis()),
1286            backend,
1287        }
1288    }
1289
1290    fn def_span(&self) -> Span {
1291        self.def_id
1292            .map_or(DUMMY_SP, |def_id| self.genv.tcx().def_span(def_id))
1293    }
1294
1295    fn var_to_fixpoint(&self, var: &rty::Var) -> fixpoint::Var {
1296        match var {
1297            rty::Var::Free(name) => {
1298                self.local_var_env
1299                    .get_fvar(*name)
1300                    .unwrap_or_else(|| {
1301                        span_bug!(self.def_span(), "no entry found for name: `{name:?}`")
1302                    })
1303                    .into()
1304            }
1305            rty::Var::Bound(debruijn, breft) => {
1306                self.local_var_env
1307                    .get_late_bvar(*debruijn, breft.var)
1308                    .unwrap_or_else(|| {
1309                        span_bug!(self.def_span(), "no entry found for late bound var: `{breft:?}`")
1310                    })
1311                    .into()
1312            }
1313            rty::Var::ConstGeneric(param) => fixpoint::Var::ConstGeneric(*param),
1314            rty::Var::EarlyParam(param) => fixpoint::Var::Param(*param),
1315            rty::Var::EVar(_) => {
1316                span_bug!(self.def_span(), "unexpected evar: `{var:?}`")
1317            }
1318        }
1319    }
1320
1321    fn variant_to_fixpoint(
1322        &self,
1323        scx: &mut SortEncodingCtxt,
1324        enum_def_id: &DefId,
1325        idx: VariantIdx,
1326    ) -> fixpoint::Var {
1327        let adt_id = scx.declare_adt(*enum_def_id);
1328        fixpoint::Var::DataCtor(adt_id, idx)
1329    }
1330
1331    fn struct_fields_to_fixpoint(
1332        &mut self,
1333        did: &DefId,
1334        flds: &[rty::Expr],
1335        scx: &mut SortEncodingCtxt,
1336    ) -> QueryResult<fixpoint::Expr> {
1337        // do not generate 1-tuples
1338        if let [fld] = flds {
1339            self.expr_to_fixpoint(fld, scx)
1340        } else {
1341            let adt_id = scx.declare_adt(*did);
1342            let ctor = fixpoint::Expr::Var(fixpoint::Var::DataCtor(adt_id, VariantIdx::ZERO));
1343            let args = flds
1344                .iter()
1345                .map(|fld| self.expr_to_fixpoint(fld, scx))
1346                .try_collect()?;
1347            Ok(fixpoint::Expr::App(Box::new(ctor), None, args))
1348        }
1349    }
1350
1351    fn fields_to_fixpoint(
1352        &mut self,
1353        flds: &[rty::Expr],
1354        scx: &mut SortEncodingCtxt,
1355    ) -> QueryResult<fixpoint::Expr> {
1356        // do not generate 1-tuples
1357        if let [fld] = flds {
1358            self.expr_to_fixpoint(fld, scx)
1359        } else {
1360            scx.declare_tuple(flds.len());
1361            let ctor = fixpoint::Expr::Var(fixpoint::Var::TupleCtor { arity: flds.len() });
1362            let args = flds
1363                .iter()
1364                .map(|fld| self.expr_to_fixpoint(fld, scx))
1365                .try_collect()?;
1366            Ok(fixpoint::Expr::App(Box::new(ctor), None, args))
1367        }
1368    }
1369
1370    fn internal_func_to_fixpoint(
1371        &mut self,
1372        internal_func: &InternalFuncKind,
1373        sort_args: &[rty::SortArg],
1374        args: &[rty::Expr],
1375        scx: &mut SortEncodingCtxt,
1376    ) -> QueryResult<fixpoint::Expr> {
1377        match internal_func {
1378            InternalFuncKind::Val(op) => {
1379                let func = fixpoint::Expr::Var(self.define_const_for_prim_op(op, scx));
1380                let args = self.exprs_to_fixpoint(args, scx)?;
1381                Ok(fixpoint::Expr::App(Box::new(func), None, args))
1382            }
1383            InternalFuncKind::Rel(op) => {
1384                let expr = if let Some(prim_rel) = self.genv.prim_rel_for(op)? {
1385                    prim_rel.body.replace_bound_refts(args)
1386                } else {
1387                    rty::Expr::tt()
1388                };
1389                self.expr_to_fixpoint(&expr, scx)
1390            }
1391            InternalFuncKind::Cast => {
1392                let [rty::SortArg::Sort(from), rty::SortArg::Sort(to)] = &sort_args else {
1393                    span_bug!(self.def_span(), "unexpected cast")
1394                };
1395                match from.cast_kind(to) {
1396                    rty::CastKind::Identity => self.expr_to_fixpoint(&args[0], scx),
1397                    rty::CastKind::BoolToInt => {
1398                        Ok(fixpoint::Expr::IfThenElse(Box::new([
1399                            self.expr_to_fixpoint(&args[0], scx)?,
1400                            fixpoint::Expr::int(1),
1401                            fixpoint::Expr::int(0),
1402                        ])))
1403                    }
1404                    rty::CastKind::IntoUnit => self.expr_to_fixpoint(&rty::Expr::unit(), scx),
1405                    rty::CastKind::Uninterpreted => {
1406                        let func = fixpoint::Expr::Var(self.define_const_for_cast(from, to, scx));
1407                        let args = self.exprs_to_fixpoint(args, scx)?;
1408                        Ok(fixpoint::Expr::App(Box::new(func), None, args))
1409                    }
1410                }
1411            }
1412        }
1413    }
1414
1415    fn structurally_normalize_expr(&self, expr: &rty::Expr) -> QueryResult<rty::Expr> {
1416        if let Some(def_id) = self.def_id {
1417            structurally_normalize_expr(self.genv, def_id.resolved_id(), &self.infcx, expr)
1418        } else {
1419            Ok(expr.clone())
1420        }
1421    }
1422
1423    fn expr_to_fixpoint(
1424        &mut self,
1425        expr: &rty::Expr,
1426        scx: &mut SortEncodingCtxt,
1427    ) -> QueryResult<fixpoint::Expr> {
1428        let expr = self.structurally_normalize_expr(expr)?;
1429        let e = match expr.kind() {
1430            rty::ExprKind::Var(var) => fixpoint::Expr::Var(self.var_to_fixpoint(var)),
1431            rty::ExprKind::Constant(c) => const_to_fixpoint(*c),
1432            rty::ExprKind::BinaryOp(op, e1, e2) => self.bin_op_to_fixpoint(op, e1, e2, scx)?,
1433            rty::ExprKind::UnaryOp(op, e) => self.un_op_to_fixpoint(*op, e, scx)?,
1434            rty::ExprKind::FieldProj(e, proj) => self.proj_to_fixpoint(e, *proj, scx)?,
1435            rty::ExprKind::Tuple(flds) => self.fields_to_fixpoint(flds, scx)?,
1436            rty::ExprKind::Ctor(rty::Ctor::Struct(did), flds) => {
1437                self.struct_fields_to_fixpoint(did, flds, scx)?
1438            }
1439            rty::ExprKind::IsCtor(def_id, variant_idx, e) => {
1440                let ctor = self.variant_to_fixpoint(scx, def_id, *variant_idx);
1441                let e = self.expr_to_fixpoint(e, scx)?;
1442                fixpoint::Expr::IsCtor(ctor, Box::new(e))
1443            }
1444            rty::ExprKind::Ctor(rty::Ctor::Enum(did, idx), args) => {
1445                let ctor = self.variant_to_fixpoint(scx, did, *idx);
1446                let args = self.exprs_to_fixpoint(args, scx)?;
1447                fixpoint::Expr::App(Box::new(fixpoint::Expr::Var(ctor)), None, args)
1448            }
1449            rty::ExprKind::ConstDefId(did) => {
1450                let var = self.define_const_for_rust_const(*did, scx);
1451                fixpoint::Expr::Var(var)
1452            }
1453            rty::ExprKind::App(func, sort_args, args) => {
1454                if let rty::ExprKind::InternalFunc(func) = func.kind() {
1455                    self.internal_func_to_fixpoint(func, sort_args, args, scx)?
1456                } else {
1457                    let func = self.expr_to_fixpoint(func, scx)?;
1458                    let sort_args = self.sort_args_to_fixpoint(sort_args, scx);
1459                    let args = self.exprs_to_fixpoint(args, scx)?;
1460                    fixpoint::Expr::App(Box::new(func), Some(sort_args), args)
1461                }
1462            }
1463            rty::ExprKind::IfThenElse(p, e1, e2) => {
1464                fixpoint::Expr::IfThenElse(Box::new([
1465                    self.expr_to_fixpoint(p, scx)?,
1466                    self.expr_to_fixpoint(e1, scx)?,
1467                    self.expr_to_fixpoint(e2, scx)?,
1468                ]))
1469            }
1470            rty::ExprKind::Alias(alias_reft, args) => {
1471                let sort = self.genv.sort_of_assoc_reft(alias_reft.assoc_id)?;
1472                let sort = sort.instantiate_identity();
1473                let func =
1474                    fixpoint::Expr::Var(self.define_const_for_alias_reft(alias_reft, sort, scx));
1475                let args = args
1476                    .iter()
1477                    .map(|expr| self.expr_to_fixpoint(expr, scx))
1478                    .try_collect()?;
1479                fixpoint::Expr::App(Box::new(func), None, args)
1480            }
1481            rty::ExprKind::Abs(lam) => {
1482                let var = self.define_const_for_lambda(lam, scx);
1483                fixpoint::Expr::Var(var)
1484            }
1485            rty::ExprKind::Let(init, body) => {
1486                debug_assert_eq!(body.vars().len(), 1);
1487                let init = self.expr_to_fixpoint(init, scx)?;
1488
1489                self.local_var_env.push_layer_with_fresh_names(1);
1490                let body = self.expr_to_fixpoint(body.skip_binder_ref(), scx)?;
1491                let vars = self.local_var_env.pop_layer();
1492
1493                fixpoint::Expr::Let(vars[0].into(), Box::new([init, body]))
1494            }
1495            rty::ExprKind::GlobalFunc(SpecFuncKind::Thy(itf)) => fixpoint::Expr::ThyFunc(*itf),
1496            rty::ExprKind::GlobalFunc(SpecFuncKind::Uif(def_id)) => {
1497                fixpoint::Expr::Var(self.define_const_for_uif(*def_id, scx))
1498            }
1499            rty::ExprKind::GlobalFunc(SpecFuncKind::Def(def_id)) => {
1500                fixpoint::Expr::Var(self.declare_fun(*def_id))
1501            }
1502            rty::ExprKind::Hole(..)
1503            | rty::ExprKind::KVar(_)
1504            | rty::ExprKind::Local(_)
1505            | rty::ExprKind::PathProj(..)
1506            | rty::ExprKind::ForAll(_)
1507            | rty::ExprKind::Exists(_)
1508            | rty::ExprKind::InternalFunc(_) => {
1509                span_bug!(self.def_span(), "unexpected expr: `{expr:?}`")
1510            }
1511            rty::ExprKind::BoundedQuant(kind, rng, body) => {
1512                let exprs = (rng.start..rng.end).map(|i| {
1513                    let arg = rty::Expr::constant(rty::Constant::from(i));
1514                    body.replace_bound_reft(&arg)
1515                });
1516                let expr = match kind {
1517                    flux_middle::fhir::QuantKind::Forall => rty::Expr::and_from_iter(exprs),
1518                    flux_middle::fhir::QuantKind::Exists => rty::Expr::or_from_iter(exprs),
1519                };
1520                self.expr_to_fixpoint(&expr, scx)?
1521            }
1522        };
1523        Ok(e)
1524    }
1525
1526    fn sort_args_to_fixpoint(
1527        &mut self,
1528        sort_args: &[rty::SortArg],
1529        scx: &mut SortEncodingCtxt,
1530    ) -> Vec<fixpoint::Sort> {
1531        sort_args
1532            .iter()
1533            .map(|s_arg| self.sort_arg_to_fixpoint(s_arg, scx))
1534            .collect()
1535    }
1536
1537    fn sort_arg_to_fixpoint(
1538        &mut self,
1539        sort_arg: &rty::SortArg,
1540        scx: &mut SortEncodingCtxt,
1541    ) -> fixpoint::Sort {
1542        match sort_arg {
1543            rty::SortArg::Sort(sort) => scx.sort_to_fixpoint(sort),
1544            rty::SortArg::BvSize(sz) => bv_size_to_fixpoint(*sz),
1545        }
1546    }
1547
1548    fn exprs_to_fixpoint<'b>(
1549        &mut self,
1550        exprs: impl IntoIterator<Item = &'b rty::Expr>,
1551        scx: &mut SortEncodingCtxt,
1552    ) -> QueryResult<Vec<fixpoint::Expr>> {
1553        exprs
1554            .into_iter()
1555            .map(|e| self.expr_to_fixpoint(e, scx))
1556            .try_collect()
1557    }
1558
1559    fn proj_to_fixpoint(
1560        &mut self,
1561        e: &rty::Expr,
1562        proj: rty::FieldProj,
1563        scx: &mut SortEncodingCtxt,
1564    ) -> QueryResult<fixpoint::Expr> {
1565        let arity = proj.arity(self.genv)?;
1566        // we encode 1-tuples as the single element inside so no projection necessary here
1567        if arity == 1 {
1568            self.expr_to_fixpoint(e, scx)
1569        } else {
1570            let proj = match proj {
1571                rty::FieldProj::Tuple { arity, field } => {
1572                    scx.declare_tuple(arity);
1573                    fixpoint::Var::TupleProj { arity, field }
1574                }
1575                rty::FieldProj::Adt { def_id, field } => {
1576                    let adt_id = scx.declare_adt(def_id);
1577                    fixpoint::Var::DataProj { adt_id, field }
1578                }
1579            };
1580            let proj = fixpoint::Expr::Var(proj);
1581            Ok(fixpoint::Expr::App(Box::new(proj), None, vec![self.expr_to_fixpoint(e, scx)?]))
1582        }
1583    }
1584
1585    fn un_op_to_fixpoint(
1586        &mut self,
1587        op: rty::UnOp,
1588        e: &rty::Expr,
1589        scx: &mut SortEncodingCtxt,
1590    ) -> QueryResult<fixpoint::Expr> {
1591        match op {
1592            rty::UnOp::Not => Ok(fixpoint::Expr::Not(Box::new(self.expr_to_fixpoint(e, scx)?))),
1593            rty::UnOp::Neg => Ok(fixpoint::Expr::Neg(Box::new(self.expr_to_fixpoint(e, scx)?))),
1594        }
1595    }
1596
1597    fn bv_rel_to_fixpoint(&self, rel: &fixpoint::BinRel) -> fixpoint::Expr {
1598        let itf = match rel {
1599            fixpoint::BinRel::Gt => fixpoint::ThyFunc::BvUgt,
1600            fixpoint::BinRel::Ge => fixpoint::ThyFunc::BvUge,
1601            fixpoint::BinRel::Lt => fixpoint::ThyFunc::BvUlt,
1602            fixpoint::BinRel::Le => fixpoint::ThyFunc::BvUle,
1603            _ => span_bug!(self.def_span(), "not a bitvector relation!"),
1604        };
1605        fixpoint::Expr::ThyFunc(itf)
1606    }
1607
1608    fn set_op_to_fixpoint(&self, op: &rty::BinOp) -> fixpoint::Expr {
1609        let itf = match op {
1610            rty::BinOp::Sub(_) => fixpoint::ThyFunc::SetDif,
1611            rty::BinOp::BitAnd(_) => fixpoint::ThyFunc::SetCap,
1612            rty::BinOp::BitOr(_) => fixpoint::ThyFunc::SetCup,
1613            _ => span_bug!(self.def_span(), "not a set operation!"),
1614        };
1615        fixpoint::Expr::ThyFunc(itf)
1616    }
1617
1618    fn bv_op_to_fixpoint(&self, op: &rty::BinOp) -> fixpoint::Expr {
1619        let itf = match op {
1620            rty::BinOp::Add(_) => fixpoint::ThyFunc::BvAdd,
1621            rty::BinOp::Sub(_) => fixpoint::ThyFunc::BvSub,
1622            rty::BinOp::Mul(_) => fixpoint::ThyFunc::BvMul,
1623            rty::BinOp::Div(_) => fixpoint::ThyFunc::BvUdiv,
1624            rty::BinOp::Mod(_) => fixpoint::ThyFunc::BvUrem,
1625            rty::BinOp::BitAnd(_) => fixpoint::ThyFunc::BvAnd,
1626            rty::BinOp::BitOr(_) => fixpoint::ThyFunc::BvOr,
1627            rty::BinOp::BitXor(_) => fixpoint::ThyFunc::BvXor,
1628            rty::BinOp::BitShl(_) => fixpoint::ThyFunc::BvShl,
1629            rty::BinOp::BitShr(_) => fixpoint::ThyFunc::BvLshr,
1630            _ => span_bug!(self.def_span(), "not a bitvector operation!"),
1631        };
1632        fixpoint::Expr::ThyFunc(itf)
1633    }
1634
1635    fn bin_op_to_fixpoint(
1636        &mut self,
1637        op: &rty::BinOp,
1638        e1: &rty::Expr,
1639        e2: &rty::Expr,
1640        scx: &mut SortEncodingCtxt,
1641    ) -> QueryResult<fixpoint::Expr> {
1642        let op = match op {
1643            rty::BinOp::Eq => {
1644                return Ok(fixpoint::Expr::Atom(
1645                    fixpoint::BinRel::Eq,
1646                    Box::new([self.expr_to_fixpoint(e1, scx)?, self.expr_to_fixpoint(e2, scx)?]),
1647                ));
1648            }
1649            rty::BinOp::Ne => {
1650                return Ok(fixpoint::Expr::Atom(
1651                    fixpoint::BinRel::Ne,
1652                    Box::new([self.expr_to_fixpoint(e1, scx)?, self.expr_to_fixpoint(e2, scx)?]),
1653                ));
1654            }
1655            rty::BinOp::Gt(sort) => {
1656                return self.bin_rel_to_fixpoint(sort, fixpoint::BinRel::Gt, e1, e2, scx);
1657            }
1658            rty::BinOp::Ge(sort) => {
1659                return self.bin_rel_to_fixpoint(sort, fixpoint::BinRel::Ge, e1, e2, scx);
1660            }
1661            rty::BinOp::Lt(sort) => {
1662                return self.bin_rel_to_fixpoint(sort, fixpoint::BinRel::Lt, e1, e2, scx);
1663            }
1664            rty::BinOp::Le(sort) => {
1665                return self.bin_rel_to_fixpoint(sort, fixpoint::BinRel::Le, e1, e2, scx);
1666            }
1667            rty::BinOp::And => {
1668                return Ok(fixpoint::Expr::And(vec![
1669                    self.expr_to_fixpoint(e1, scx)?,
1670                    self.expr_to_fixpoint(e2, scx)?,
1671                ]));
1672            }
1673            rty::BinOp::Or => {
1674                return Ok(fixpoint::Expr::Or(vec![
1675                    self.expr_to_fixpoint(e1, scx)?,
1676                    self.expr_to_fixpoint(e2, scx)?,
1677                ]));
1678            }
1679            rty::BinOp::Imp => {
1680                return Ok(fixpoint::Expr::Imp(Box::new([
1681                    self.expr_to_fixpoint(e1, scx)?,
1682                    self.expr_to_fixpoint(e2, scx)?,
1683                ])));
1684            }
1685            rty::BinOp::Iff => {
1686                return Ok(fixpoint::Expr::Iff(Box::new([
1687                    self.expr_to_fixpoint(e1, scx)?,
1688                    self.expr_to_fixpoint(e2, scx)?,
1689                ])));
1690            }
1691
1692            // Bit vector operations
1693            rty::BinOp::Add(rty::Sort::BitVec(_))
1694            | rty::BinOp::Sub(rty::Sort::BitVec(_))
1695            | rty::BinOp::Mul(rty::Sort::BitVec(_))
1696            | rty::BinOp::Div(rty::Sort::BitVec(_))
1697            | rty::BinOp::Mod(rty::Sort::BitVec(_))
1698            | rty::BinOp::BitAnd(rty::Sort::BitVec(_))
1699            | rty::BinOp::BitOr(rty::Sort::BitVec(_))
1700            | rty::BinOp::BitXor(rty::Sort::BitVec(_))
1701            | rty::BinOp::BitShl(rty::Sort::BitVec(_))
1702            | rty::BinOp::BitShr(rty::Sort::BitVec(_)) => {
1703                let bv_func = self.bv_op_to_fixpoint(op);
1704                return Ok(fixpoint::Expr::App(
1705                    Box::new(bv_func),
1706                    None,
1707                    vec![self.expr_to_fixpoint(e1, scx)?, self.expr_to_fixpoint(e2, scx)?],
1708                ));
1709            }
1710
1711            // Set operations
1712            rty::BinOp::Sub(rty::Sort::App(rty::SortCtor::Set, _))
1713            | rty::BinOp::BitAnd(rty::Sort::App(rty::SortCtor::Set, _))
1714            | rty::BinOp::BitOr(rty::Sort::App(rty::SortCtor::Set, _)) => {
1715                let set_func = self.set_op_to_fixpoint(op);
1716                return Ok(fixpoint::Expr::App(
1717                    Box::new(set_func),
1718                    None,
1719                    vec![self.expr_to_fixpoint(e1, scx)?, self.expr_to_fixpoint(e2, scx)?],
1720                ));
1721            }
1722
1723            // Interpreted arithmetic operations
1724            rty::BinOp::Add(_) => fixpoint::BinOp::Add,
1725            rty::BinOp::Sub(_) => fixpoint::BinOp::Sub,
1726            rty::BinOp::Mul(_) => fixpoint::BinOp::Mul,
1727            rty::BinOp::Div(_) => fixpoint::BinOp::Div,
1728            rty::BinOp::Mod(_) => fixpoint::BinOp::Mod,
1729
1730            rty::BinOp::BitAnd(sort)
1731            | rty::BinOp::BitOr(sort)
1732            | rty::BinOp::BitXor(sort)
1733            | rty::BinOp::BitShl(sort)
1734            | rty::BinOp::BitShr(sort) => {
1735                bug!("unsupported operation `{op:?}` for sort `{sort:?}`");
1736            }
1737        };
1738        Ok(fixpoint::Expr::BinaryOp(
1739            op,
1740            Box::new([self.expr_to_fixpoint(e1, scx)?, self.expr_to_fixpoint(e2, scx)?]),
1741        ))
1742    }
1743
1744    /// A binary relation is encoded as a structurally recursive relation between aggregate sorts.
1745    /// For "leaf" expressions, we encode them as an interpreted relation if the sort supports it,
1746    /// otherwise we use an uninterpreted function. For example, consider the following relation
1747    /// between two tuples of sort `(int, int -> int)`
1748    /// ```text
1749    /// (0, λv. v + 1) <= (1, λv. v + 1)
1750    /// ```
1751    /// The encoding in fixpoint will be
1752    ///
1753    /// ```text
1754    /// 0 <= 1 && (le (λv. v + 1) (λv. v + 1))
1755    /// ```
1756    /// Where `<=` is the (interpreted) less than or equal relation between integers and `le` is
1757    /// an uninterpreted relation between ([the encoding] of) lambdas.
1758    ///
1759    /// [the encoding]: Self::define_const_for_lambda
1760    fn bin_rel_to_fixpoint(
1761        &mut self,
1762        sort: &rty::Sort,
1763        rel: fixpoint::BinRel,
1764        e1: &rty::Expr,
1765        e2: &rty::Expr,
1766        scx: &mut SortEncodingCtxt,
1767    ) -> QueryResult<fixpoint::Expr> {
1768        let e = match sort {
1769            rty::Sort::Int | rty::Sort::Real | rty::Sort::Char => {
1770                fixpoint::Expr::Atom(
1771                    rel,
1772                    Box::new([self.expr_to_fixpoint(e1, scx)?, self.expr_to_fixpoint(e2, scx)?]),
1773                )
1774            }
1775            rty::Sort::BitVec(_) => {
1776                let e1 = self.expr_to_fixpoint(e1, scx)?;
1777                let e2 = self.expr_to_fixpoint(e2, scx)?;
1778                let rel = self.bv_rel_to_fixpoint(&rel);
1779                fixpoint::Expr::App(Box::new(rel), None, vec![e1, e2])
1780            }
1781            rty::Sort::Tuple(sorts) => {
1782                let arity = sorts.len();
1783                self.apply_bin_rel_rec(sorts, rel, e1, e2, scx, |field| {
1784                    rty::FieldProj::Tuple { arity, field }
1785                })?
1786            }
1787            rty::Sort::App(rty::SortCtor::Adt(sort_def), args)
1788                if let Some(variant) = sort_def.opt_struct_variant() =>
1789            {
1790                let def_id = sort_def.did();
1791                let sorts = variant.field_sorts(args);
1792                self.apply_bin_rel_rec(&sorts, rel, e1, e2, scx, |field| {
1793                    rty::FieldProj::Adt { def_id, field }
1794                })?
1795            }
1796            _ => {
1797                let rel = fixpoint::Expr::Var(fixpoint::Var::UIFRel(rel));
1798                fixpoint::Expr::App(
1799                    Box::new(rel),
1800                    None,
1801                    vec![self.expr_to_fixpoint(e1, scx)?, self.expr_to_fixpoint(e2, scx)?],
1802                )
1803            }
1804        };
1805        Ok(e)
1806    }
1807
1808    /// Apply binary relation recursively over aggregate expressions
1809    fn apply_bin_rel_rec(
1810        &mut self,
1811        sorts: &[rty::Sort],
1812        rel: fixpoint::BinRel,
1813        e1: &rty::Expr,
1814        e2: &rty::Expr,
1815        scx: &mut SortEncodingCtxt,
1816        mk_proj: impl Fn(u32) -> rty::FieldProj,
1817    ) -> QueryResult<fixpoint::Expr> {
1818        Ok(fixpoint::Expr::and(
1819            sorts
1820                .iter()
1821                .enumerate()
1822                .map(|(idx, s)| {
1823                    let proj = mk_proj(idx as u32);
1824                    let e1 = e1.proj_and_reduce(proj);
1825                    let e2 = e2.proj_and_reduce(proj);
1826                    self.bin_rel_to_fixpoint(s, rel, &e1, &e2, scx)
1827                })
1828                .try_collect()?,
1829        ))
1830    }
1831
1832    fn imm(
1833        &mut self,
1834        arg: &rty::Expr,
1835        sort: &rty::Sort,
1836        scx: &mut SortEncodingCtxt,
1837        bindings: &mut Vec<fixpoint::Bind>,
1838    ) -> QueryResult<fixpoint::Expr> {
1839        let farg = self.expr_to_fixpoint(arg, scx)?;
1840        // If we're translating to Lean, no need to do any ANF-ing.
1841        if matches!(self.backend, Backend::Lean) {
1842            return Ok(farg);
1843        }
1844        // Check if it's a variable after encoding, in case the encoding produced a variable from a
1845        // non-variable.
1846        if let fixpoint::Expr::Var(var) = farg {
1847            Ok(fixpoint::Expr::Var(var))
1848        } else {
1849            let fresh = self.local_var_env.fresh_name();
1850            self.local_var_env.reverse_map.insert(fresh, arg.clone());
1851            let pred = fixpoint::Expr::eq(fixpoint::Expr::Var(fresh.into()), farg);
1852            bindings.push(fixpoint::Bind {
1853                name: fresh.into(),
1854                sort: scx.sort_to_fixpoint(sort),
1855                pred: fixpoint::Pred::Expr(pred),
1856            });
1857            Ok(fixpoint::Expr::Var(fresh.into()))
1858        }
1859    }
1860
1861    /// Declare that the `def_id` of a Flux function definition needs to be encoded and assigns
1862    /// a name to it if it hasn't yet been declared. The encoding of the function body happens
1863    /// in [`Self::define_funs`].
1864    pub fn declare_fun(&mut self, def_id: FluxDefId) -> fixpoint::Var {
1865        *self.const_env.fun_def_map.entry(def_id).or_insert_with(|| {
1866            let id = self.const_env.global_var_gen.fresh();
1867            fixpoint::Var::Global(id, Some(def_id))
1868        })
1869    }
1870
1871    /// The logic below is a bit "duplicated" with the `prim_op_sort` in `sortck.rs`;
1872    /// They are not exactly the same because this is on rty and the other one on fhir.
1873    /// We should make sure these two remain in sync.
1874    ///
1875    /// (NOTE:PrimOpSort) We are somewhat "overloading" the `BinOps`: as we are using them
1876    /// for (a) interpreted operations on bit vectors AND (b) uninterpreted functions on integers.
1877    /// So when Binop::BitShr (a) appears in a ExprKind::BinOp, it means bit vectors, but
1878    /// (b) inside ExprKind::InternalFunc it means int.
1879    fn prim_op_sort(op: &rty::BinOp, span: Span) -> rty::PolyFuncSort {
1880        match op {
1881            rty::BinOp::BitAnd(rty::Sort::Int)
1882            | rty::BinOp::BitOr(rty::Sort::Int)
1883            | rty::BinOp::BitXor(rty::Sort::Int)
1884            | rty::BinOp::BitShl(rty::Sort::Int)
1885            | rty::BinOp::BitShr(rty::Sort::Int) => {
1886                let fsort =
1887                    rty::FuncSort::new(vec![rty::Sort::Int, rty::Sort::Int], rty::Sort::Int);
1888                rty::PolyFuncSort::new(List::empty(), fsort)
1889            }
1890            _ => span_bug!(span, "unexpected prim op: {op:?} in `prim_op_sort`"),
1891        }
1892    }
1893
1894    fn define_const_for_cast(
1895        &mut self,
1896        from: &rty::Sort,
1897        to: &rty::Sort,
1898        scx: &mut SortEncodingCtxt,
1899    ) -> fixpoint::Var {
1900        let key = ConstKey::Cast(from.clone(), to.clone());
1901        self.const_env
1902            .get_or_insert(key, |global_name| {
1903                let fsort = rty::FuncSort::new(vec![from.clone()], to.clone());
1904                let fsort = rty::PolyFuncSort::new(List::empty(), fsort);
1905                let sort = scx.func_sort_to_fixpoint(&fsort);
1906                fixpoint::ConstDecl {
1907                    name: fixpoint::Var::Global(global_name, None),
1908                    sort,
1909                    comment: Some(format!("cast uif: ({from:?}) -> {to:?}")),
1910                }
1911            })
1912            .name
1913    }
1914
1915    fn define_const_for_prim_op(
1916        &mut self,
1917        op: &rty::BinOp,
1918        scx: &mut SortEncodingCtxt,
1919    ) -> fixpoint::Var {
1920        let key = ConstKey::PrimOp(op.clone());
1921        let span = self.def_span();
1922        self.const_env
1923            .get_or_insert(key, |global_name| {
1924                let sort = scx.func_sort_to_fixpoint(&Self::prim_op_sort(op, span));
1925                fixpoint::ConstDecl {
1926                    name: fixpoint::Var::Global(global_name, None),
1927                    sort,
1928                    comment: Some(format!("prim op uif: {op:?}")),
1929                }
1930            })
1931            .name
1932    }
1933
1934    fn define_const_for_uif(
1935        &mut self,
1936        def_id: FluxDefId,
1937        scx: &mut SortEncodingCtxt,
1938    ) -> fixpoint::Var {
1939        let key = ConstKey::Uif(def_id);
1940        self.const_env
1941            .get_or_insert(key, |global_name| {
1942                let sort = scx.func_sort_to_fixpoint(&self.genv.func_sort(def_id));
1943                fixpoint::ConstDecl {
1944                    name: fixpoint::Var::Global(global_name, Some(def_id)),
1945                    sort,
1946                    comment: Some(format!("uif: {def_id:?}")),
1947                }
1948            })
1949            .name
1950    }
1951
1952    fn define_const_for_rust_const(
1953        &mut self,
1954        def_id: DefId,
1955        scx: &mut SortEncodingCtxt,
1956    ) -> fixpoint::Var {
1957        let key = ConstKey::RustConst(def_id);
1958        self.const_env
1959            .get_or_insert(key, |global_name| {
1960                let sort = self.genv.sort_of_def_id(def_id).unwrap().unwrap();
1961                fixpoint::ConstDecl {
1962                    name: fixpoint::Var::Global(global_name, None),
1963                    sort: scx.sort_to_fixpoint(&sort),
1964                    comment: Some(format!("rust const: {}", def_id_to_string(def_id))),
1965                }
1966            })
1967            .name
1968    }
1969
1970    /// returns the 'constant' UIF for Var used to represent the alias_pred, creating and adding it
1971    /// to the const_map if necessary
1972    fn define_const_for_alias_reft(
1973        &mut self,
1974        alias_reft: &rty::AliasReft,
1975        fsort: rty::FuncSort,
1976        scx: &mut SortEncodingCtxt,
1977    ) -> fixpoint::Var {
1978        let tcx = self.genv.tcx();
1979        let args = alias_reft
1980            .args
1981            .to_rustc(tcx)
1982            .truncate_to(tcx, tcx.generics_of(alias_reft.assoc_id.parent()));
1983        let key = ConstKey::Alias(alias_reft.assoc_id, args);
1984        self.const_env
1985            .get_or_insert(key, |global_name| {
1986                let comment = Some(format!("alias reft: {alias_reft:?}"));
1987                let name = fixpoint::Var::Global(global_name, None);
1988                let fsort = rty::PolyFuncSort::new(List::empty(), fsort);
1989                let sort = scx.func_sort_to_fixpoint(&fsort);
1990                fixpoint::ConstDecl { name, comment, sort }
1991            })
1992            .name
1993    }
1994
1995    /// We encode lambdas with uninterpreted constant. Two syntactically equal lambdas will be encoded
1996    /// with the same constant.
1997    fn define_const_for_lambda(
1998        &mut self,
1999        lam: &rty::Lambda,
2000        scx: &mut SortEncodingCtxt,
2001    ) -> fixpoint::Var {
2002        let key = ConstKey::Lambda(lam.clone());
2003        self.const_env
2004            .get_or_insert(key, |global_name| {
2005                let comment = Some(format!("lambda: {lam:?}"));
2006                let name = fixpoint::Var::Global(global_name, None);
2007                let sort = scx.func_sort_to_fixpoint(&lam.fsort().to_poly());
2008                fixpoint::ConstDecl { name, comment, sort }
2009            })
2010            .name
2011    }
2012
2013    fn assume_const_values(
2014        &mut self,
2015        mut constraint: fixpoint::Constraint,
2016        scx: &mut SortEncodingCtxt,
2017    ) -> QueryResult<fixpoint::Constraint> {
2018        // Encoding the value for a constant could in theory define more constants for which
2019        // we need to assume values, so we iterate until there are no more constants.
2020        let mut idx = 0;
2021        while let Some((key, const_)) = self.const_env.const_map.get_index(idx) {
2022            idx += 1;
2023
2024            let ConstKey::RustConst(def_id) = key else { continue };
2025            let info = self.genv.constant_info(def_id)?;
2026            match info {
2027                rty::ConstantInfo::Uninterpreted => {}
2028                rty::ConstantInfo::Interpreted(val, _) => {
2029                    let e1 = fixpoint::Expr::Var(const_.name);
2030                    let e2 = self.expr_to_fixpoint(&val, scx)?;
2031                    let pred = fixpoint::Pred::Expr(e1.eq(e2));
2032                    constraint = fixpoint::Constraint::ForAll(
2033                        fixpoint::Bind {
2034                            name: fixpoint::Var::Underscore,
2035                            sort: fixpoint::Sort::Int,
2036                            pred,
2037                        },
2038                        Box::new(constraint),
2039                    );
2040                }
2041            }
2042        }
2043        Ok(constraint)
2044    }
2045
2046    fn qualifiers_for(
2047        &mut self,
2048        def_id: LocalDefId,
2049        scx: &mut SortEncodingCtxt,
2050    ) -> QueryResult<Vec<fixpoint::Qualifier>> {
2051        self.genv
2052            .qualifiers_for(def_id)?
2053            .map(|qual| self.qualifier_to_fixpoint(qual, scx))
2054            .try_collect()
2055    }
2056
2057    fn define_funs(
2058        &mut self,
2059        def_id: MaybeExternId,
2060        scx: &mut SortEncodingCtxt,
2061    ) -> QueryResult<(Vec<fixpoint::FunDef>, Vec<fixpoint::ConstDecl>)> {
2062        let reveals: UnordSet<FluxDefId> = self
2063            .genv
2064            .reveals_for(def_id.local_id())
2065            .iter()
2066            .copied()
2067            .collect();
2068        let proven_externally = self.genv.proven_externally(def_id.local_id());
2069        let mut consts = vec![];
2070        let mut defs = vec![];
2071
2072        // We iterate until encoding the body of functions doesn't require any more functions
2073        // to be encoded.
2074        let mut idx = 0;
2075        while let Some((&did, _)) = self.const_env.fun_def_map.get_index(idx) {
2076            idx += 1;
2077
2078            let info = self.genv.normalized_info(did);
2079            let revealed = reveals.contains(&did);
2080            if info.hide && !revealed && proven_externally.is_none() {
2081                consts.push(self.fun_decl_to_fixpoint(did, scx));
2082            } else {
2083                defs.push((info.rank, self.fun_def_to_fixpoint(did, scx)?));
2084            };
2085        }
2086
2087        // we sort by rank so the definitions go out without any forward dependencies.
2088        let defs = defs
2089            .into_iter()
2090            .sorted_by_key(|(rank, _)| *rank)
2091            .map(|(_, def)| def)
2092            .collect();
2093
2094        Ok((defs, consts))
2095    }
2096
2097    pub fn fun_decl_to_fixpoint(
2098        &mut self,
2099        def_id: FluxDefId,
2100        scx: &mut SortEncodingCtxt,
2101    ) -> fixpoint::ConstDecl {
2102        let name = self.const_env.fun_def_map[&def_id];
2103        let sort = scx.func_sort_to_fixpoint(&self.genv.func_sort(def_id));
2104        fixpoint::ConstDecl { name, sort, comment: Some(format!("flux def: {def_id:?}")) }
2105    }
2106
2107    pub fn fun_def_to_fixpoint(
2108        &mut self,
2109        def_id: FluxDefId,
2110        scx: &mut SortEncodingCtxt,
2111    ) -> QueryResult<fixpoint::FunDef> {
2112        let name = *self.const_env.fun_def_map.get(&def_id).unwrap();
2113        let info = self.genv.normalized_info(def_id);
2114        let out = scx.sort_to_fixpoint(self.genv.func_sort(def_id).expect_mono().output());
2115        let (args, body) = self.body_to_fixpoint(&info.body, scx)?;
2116        Ok(fixpoint::FunDef {
2117            name,
2118            args,
2119            body,
2120            out,
2121            comment: Some(format!("flux def: {def_id:?}")),
2122        })
2123    }
2124
2125    fn body_to_fixpoint(
2126        &mut self,
2127        body: &rty::Binder<rty::Expr>,
2128        scx: &mut SortEncodingCtxt,
2129    ) -> QueryResult<(Vec<(fixpoint::Var, fixpoint::Sort)>, fixpoint::Expr)> {
2130        self.local_var_env
2131            .push_layer_with_fresh_names(body.vars().len());
2132
2133        let expr = self.expr_to_fixpoint(body.as_ref().skip_binder(), scx)?;
2134
2135        let args: Vec<(fixpoint::Var, fixpoint::Sort)> =
2136            iter::zip(self.local_var_env.pop_layer(), body.vars())
2137                .map(|(name, var)| (name.into(), scx.sort_to_fixpoint(var.expect_sort())))
2138                .collect();
2139
2140        Ok((args, expr))
2141    }
2142
2143    fn qualifier_to_fixpoint(
2144        &mut self,
2145        qualifier: &rty::Qualifier,
2146        scx: &mut SortEncodingCtxt,
2147    ) -> QueryResult<fixpoint::Qualifier> {
2148        let (args, body) = self.body_to_fixpoint(&qualifier.body, scx)?;
2149        let name = qualifier.def_id.name().to_string();
2150        Ok(fixpoint::Qualifier { name, args, body })
2151    }
2152}
2153
2154fn mk_implies(assumption: fixpoint::Pred, cstr: fixpoint::Constraint) -> fixpoint::Constraint {
2155    fixpoint::Constraint::ForAll(
2156        fixpoint::Bind {
2157            name: fixpoint::Var::Underscore,
2158            sort: fixpoint::Sort::Int,
2159            pred: assumption,
2160        },
2161        Box::new(cstr),
2162    )
2163}
2164
2165fn parse_kvid(kvid: &str) -> fixpoint::KVid {
2166    if kvid.starts_with("k")
2167        && let Some(kvid) = kvid[1..].parse::<u32>().ok()
2168    {
2169        fixpoint::KVid::from_u32(kvid)
2170    } else {
2171        tracked_span_bug!("unexpected kvar name {kvid}")
2172    }
2173}
2174
2175fn parse_local_var(name: &str) -> Option<fixpoint::Var> {
2176    if let Some(rest) = name.strip_prefix('a')
2177        && let Ok(idx) = rest.parse::<u32>()
2178    {
2179        return Some(fixpoint::Var::Local(fixpoint::LocalVar::from(idx)));
2180    }
2181    None
2182}
2183
2184fn parse_global_var(name: &str) -> Option<fixpoint::Var> {
2185    if let Some(rest) = name.strip_prefix('c')
2186        && let Ok(idx) = rest.parse::<u32>()
2187    {
2188        return Some(fixpoint::Var::Global(fixpoint::GlobalVar::from(idx), None));
2189    }
2190    // try parsing as a named global variable
2191    if let Some(rest) = name.strip_prefix("f$")
2192        && let parts = rest.split('$').collect::<Vec<_>>()
2193        && parts.len() == 2
2194        && let Ok(global_idx) = parts[1].parse::<u32>()
2195    {
2196        return Some(fixpoint::Var::Global(fixpoint::GlobalVar::from(global_idx), None));
2197    }
2198    None
2199}
2200
2201fn parse_param(name: &str) -> Option<fixpoint::Var> {
2202    if let Some(rest) = name.strip_prefix("reftgen$")
2203        && let parts = rest.split('$').collect::<Vec<_>>()
2204        && parts.len() == 2
2205        && let Ok(index) = parts[1].parse::<u32>()
2206    {
2207        let name = Symbol::intern(parts[0]);
2208        let param = EarlyReftParam { index, name };
2209        return Some(fixpoint::Var::Param(param));
2210    }
2211    None
2212}
2213
2214fn parse_data_proj(name: &str) -> Option<fixpoint::Var> {
2215    if let Some(rest) = name.strip_prefix("fld")
2216        && let parts = rest.split('$').collect::<Vec<_>>()
2217        && parts.len() == 2
2218        && let Ok(adt_id) = parts[0].parse::<u32>()
2219        && let Ok(field) = parts[1].parse::<u32>()
2220    {
2221        let adt_id = fixpoint::AdtId::from(adt_id);
2222        return Some(fixpoint::Var::DataProj { adt_id, field });
2223    }
2224    None
2225}
2226
2227fn parse_data_ctor(name: &str) -> Option<fixpoint::Var> {
2228    if let Some(rest) = name.strip_prefix("mkadt")
2229        && let parts = rest.split('$').collect::<Vec<_>>()
2230        && parts.len() == 2
2231        && let Ok(adt_id) = parts[0].parse::<u32>()
2232        && let Ok(variant_idx) = parts[1].parse::<u32>()
2233    {
2234        let adt_id = fixpoint::AdtId::from(adt_id);
2235        let variant_idx = VariantIdx::from(variant_idx);
2236        return Some(fixpoint::Var::DataCtor(adt_id, variant_idx));
2237    }
2238    None
2239}
2240
2241struct SexpParseCtxt;
2242
2243impl FromSexp<FixpointTypes> for SexpParseCtxt {
2244    fn kvar(&self, name: &str) -> Result<fixpoint::KVid, ParseError> {
2245        bug!("TODO: SexpParse: kvar: {name}")
2246    }
2247
2248    fn string(&self, s: &str) -> Result<fixpoint::SymStr, ParseError> {
2249        Ok(fixpoint::SymStr(Symbol::intern(s)))
2250    }
2251
2252    fn var(&self, name: &str) -> Result<fixpoint::Var, ParseError> {
2253        if let Some(var) = parse_local_var(name) {
2254            return Ok(var);
2255        }
2256        if let Some(var) = parse_global_var(name) {
2257            return Ok(var);
2258        }
2259        if let Some(var) = parse_param(name) {
2260            return Ok(var);
2261        }
2262        if let Some(var) = parse_data_proj(name) {
2263            return Ok(var);
2264        }
2265        if let Some(var) = parse_data_ctor(name) {
2266            return Ok(var);
2267        }
2268        Err(ParseError::err(format!("Unknown variable: {name}")))
2269    }
2270
2271    fn sort(&self, name: &str) -> Result<fixpoint::DataSort, ParseError> {
2272        if let Some(idx) = name.strip_prefix("Adt")
2273            && let Ok(adt_id) = idx.parse::<u32>()
2274        {
2275            return Ok(fixpoint::DataSort::Adt(fixpoint::AdtId::from(adt_id)));
2276        }
2277        Err(ParseError::err(format!("Unknown sort: {name}")))
2278    }
2279}