1use 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_middle::{
17    FixpointQueryKind,
18    def_id::{FluxDefId, MaybeExternId},
19    def_id_to_string,
20    global_env::GlobalEnv,
21    metrics::{self, Metric, TimingKind},
22    queries::QueryResult,
23    query_bug,
24    rty::{
25        self, ESpan, EarlyReftParam, GenericArgsExt, InternalFuncKind, Lambda, List, SpecFuncKind,
26        VariantIdx, fold::TypeFoldable as _,
27    },
28};
29use itertools::Itertools;
30use liquid_fixpoint::{
31    FixpointResult, FixpointStatus, SmtSolver,
32    parser::{FromSexp, ParseError},
33    sexp::Parser,
34};
35use rustc_data_structures::{
36    fx::{FxIndexMap, FxIndexSet},
37    unord::{UnordMap, UnordSet},
38};
39use rustc_hir::def_id::{DefId, LocalDefId};
40use rustc_index::newtype_index;
41use rustc_infer::infer::TyCtxtInferExt as _;
42use rustc_middle::ty::TypingMode;
43use rustc_span::{DUMMY_SP, Span, Symbol};
44use rustc_type_ir::{BoundVar, DebruijnIndex};
45use serde::{Deserialize, Deserializer, Serialize};
46
47use crate::{
48    fixpoint_encoding::fixpoint::FixpointTypes, fixpoint_qualifiers::FIXPOINT_QUALIFIERS,
49    lean_encoding::LeanEncoder, projections::structurally_normalize_expr,
50};
51
52pub mod decoding;
53
54pub mod fixpoint {
55    use std::fmt;
56
57    use flux_middle::{def_id::FluxDefId, rty::EarlyReftParam};
58    use liquid_fixpoint::{FixpointFmt, Identifier};
59    use rustc_abi::VariantIdx;
60    use rustc_index::newtype_index;
61    use rustc_middle::ty::ParamConst;
62    use rustc_span::Symbol;
63
64    newtype_index! {
65        #[orderable]
66        pub struct KVid {}
67    }
68
69    impl Identifier for KVid {
70        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
71            write!(f, "k{}", self.as_u32())
72        }
73    }
74
75    newtype_index! {
76        pub struct LocalVar {}
77    }
78
79    newtype_index! {
80        pub struct GlobalVar {}
81    }
82
83    newtype_index! {
84        pub struct AdtId {}
87    }
88
89    #[derive(Hash, Copy, Clone, Debug, PartialEq, Eq)]
90    pub enum Var {
91        Underscore,
92        Global(GlobalVar, Option<FluxDefId>),
93        Local(LocalVar),
94        DataCtor(AdtId, VariantIdx),
95        TupleCtor { arity: usize },
96        TupleProj { arity: usize, field: u32 },
97        DataProj { adt_id: AdtId, field: u32 },
98        UIFRel(BinRel),
99        Param(EarlyReftParam),
100        ConstGeneric(ParamConst),
101    }
102
103    impl From<GlobalVar> for Var {
104        fn from(v: GlobalVar) -> Self {
105            Self::Global(v, None)
106        }
107    }
108
109    impl From<LocalVar> for Var {
110        fn from(v: LocalVar) -> Self {
111            Self::Local(v)
112        }
113    }
114
115    impl Identifier for Var {
116        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
117            match self {
118                Var::Global(v, None) => write!(f, "c{}", v.as_u32()),
119                Var::Global(v, Some(did)) => write!(f, "f${}${}", did.name(), v.as_u32()),
120                Var::Local(v) => write!(f, "a{}", v.as_u32()),
121                Var::DataCtor(adt_id, variant_idx) => {
122                    write!(f, "mkadt{}${}", adt_id.as_u32(), variant_idx.as_u32())
123                }
124                Var::TupleCtor { arity } => write!(f, "mktuple{arity}"),
125                Var::TupleProj { arity, field } => write!(f, "tuple{arity}${field}"),
126                Var::DataProj { adt_id, field } => write!(f, "fld{}${field}", adt_id.as_u32()),
127                Var::UIFRel(BinRel::Gt) => write!(f, "gt"),
128                Var::UIFRel(BinRel::Ge) => write!(f, "ge"),
129                Var::UIFRel(BinRel::Lt) => write!(f, "lt"),
130                Var::UIFRel(BinRel::Le) => write!(f, "le"),
131                Var::UIFRel(BinRel::Eq) => write!(f, "eq"),
133                Var::UIFRel(BinRel::Ne) => write!(f, "ne"),
134                Var::Underscore => write!(f, "_$"), Var::ConstGeneric(param) => {
136                    write!(f, "constgen${}${}", param.name, param.index)
137                }
138                Var::Param(param) => {
139                    write!(f, "reftgen${}${}", param.name, param.index)
140                }
141            }
142        }
143    }
144
145    #[derive(Clone, Hash, Debug, PartialEq, Eq)]
146    pub enum DataSort {
147        Tuple(usize),
148        Adt(AdtId),
149        User(FluxDefId),
150    }
151
152    impl Identifier for DataSort {
153        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
154            match self {
155                DataSort::Tuple(arity) => {
156                    write!(f, "Tuple{arity}")
157                }
158                DataSort::Adt(adt_id) => {
159                    write!(f, "Adt{}", adt_id.as_u32())
160                }
161                DataSort::User(..) => {
165                    write!(f, "int")
166                }
167            }
168        }
169    }
170
171    #[derive(Hash, Clone, Debug)]
172    pub struct SymStr(pub Symbol);
173
174    #[cfg(feature = "rust-fixpoint")]
175    impl FixpointFmt for SymStr {
176        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
177            write!(f, "{}", self.0)
178        }
179    }
180
181    #[cfg(not(feature = "rust-fixpoint"))]
182    impl FixpointFmt for SymStr {
183        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
184            write!(f, "\"{}\"", self.0)
185        }
186    }
187
188    liquid_fixpoint::declare_types! {
189        type Sort = DataSort;
190        type KVar = KVid;
191        type Var = Var;
192        type String = SymStr;
193        type Tag = super::TagIdx;
194    }
195    pub use fixpoint_generated::*;
196}
197
198pub type Solution = HashMap<rty::KVid, rty::Binder<rty::Expr>>;
200
201#[derive(Debug, Clone, Default)]
202pub struct Answer<Tag> {
203    pub errors: Vec<Tag>,
204    pub solution: Solution,
205}
206
207impl<Tag> Answer<Tag> {
208    pub fn trivial() -> Self {
209        Self { errors: Vec::new(), solution: HashMap::new() }
210    }
211}
212
213newtype_index! {
214    #[debug_format = "TagIdx({})"]
215    pub struct TagIdx {}
216}
217
218impl Serialize for TagIdx {
219    fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
220        self.as_u32().serialize(serializer)
221    }
222}
223
224impl<'de> Deserialize<'de> for TagIdx {
225    fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
226        let idx = usize::deserialize(deserializer)?;
227        Ok(TagIdx::from_u32(idx as u32))
228    }
229}
230
231#[derive(Default)]
233pub struct SortEncodingCtxt {
234    tuples: UnordSet<usize>,
236    adt_sorts: FxIndexSet<DefId>,
239    user_sorts: FxIndexSet<FluxDefId>,
241}
242
243impl SortEncodingCtxt {
244    pub fn sort_to_fixpoint(&mut self, sort: &rty::Sort) -> fixpoint::Sort {
245        match sort {
246            rty::Sort::Int => fixpoint::Sort::Int,
247            rty::Sort::Real => fixpoint::Sort::Real,
248            rty::Sort::Bool => fixpoint::Sort::Bool,
249            rty::Sort::Str => fixpoint::Sort::Str,
250            rty::Sort::Char => fixpoint::Sort::Int,
251            rty::Sort::BitVec(size) => fixpoint::Sort::BitVec(Box::new(bv_size_to_fixpoint(*size))),
252            rty::Sort::App(rty::SortCtor::User(def_id), args) => {
253                self.declare_opaque_sort(*def_id);
254                let args = args.iter().map(|s| self.sort_to_fixpoint(s)).collect_vec();
255                fixpoint::Sort::App(
256                    fixpoint::SortCtor::Data(fixpoint::DataSort::User(*def_id)),
257                    args,
258                )
259            }
260            rty::Sort::Param(_)
265            | rty::Sort::Alias(rty::AliasKind::Opaque | rty::AliasKind::Projection, ..) => {
266                fixpoint::Sort::Int
267            }
268            rty::Sort::App(rty::SortCtor::Set, args) => {
269                let args = args.iter().map(|s| self.sort_to_fixpoint(s)).collect_vec();
270                fixpoint::Sort::App(fixpoint::SortCtor::Set, args)
271            }
272            rty::Sort::App(rty::SortCtor::Map, args) => {
273                let args = args.iter().map(|s| self.sort_to_fixpoint(s)).collect_vec();
274                fixpoint::Sort::App(fixpoint::SortCtor::Map, args)
275            }
276            rty::Sort::App(rty::SortCtor::Adt(sort_def), args) => {
277                if let Some(variant) = sort_def.opt_struct_variant() {
278                    let sorts = variant.field_sorts(args);
279                    if let [sort] = &sorts[..] {
281                        self.sort_to_fixpoint(sort)
282                    } else {
283                        let adt_id = self.declare_adt(sort_def.did());
284                        let ctor = fixpoint::SortCtor::Data(fixpoint::DataSort::Adt(adt_id));
285                        let args = args.iter().map(|s| self.sort_to_fixpoint(s)).collect_vec();
286                        fixpoint::Sort::App(ctor, args)
287                    }
288                } else {
289                    debug_assert!(args.is_empty());
290                    let adt_id = self.declare_adt(sort_def.did());
291                    fixpoint::Sort::App(
292                        fixpoint::SortCtor::Data(fixpoint::DataSort::Adt(adt_id)),
293                        vec![],
294                    )
295                }
296            }
297            rty::Sort::Tuple(sorts) => {
298                if let [sort] = &sorts[..] {
300                    self.sort_to_fixpoint(sort)
301                } else {
302                    self.declare_tuple(sorts.len());
303                    let ctor = fixpoint::SortCtor::Data(fixpoint::DataSort::Tuple(sorts.len()));
304                    let args = sorts.iter().map(|s| self.sort_to_fixpoint(s)).collect();
305                    fixpoint::Sort::App(ctor, args)
306                }
307            }
308            rty::Sort::Func(sort) => self.func_sort_to_fixpoint(sort),
309            rty::Sort::Var(k) => fixpoint::Sort::Var(k.index()),
310            rty::Sort::Err
311            | rty::Sort::Infer(_)
312            | rty::Sort::Loc
313            | rty::Sort::Alias(rty::AliasKind::Free, _) => {
314                tracked_span_bug!("unexpected sort `{sort:?}`")
315            }
316        }
317    }
318
319    fn func_sort_to_fixpoint(&mut self, fsort: &rty::PolyFuncSort) -> fixpoint::Sort {
320        let params = fsort.params().len();
321        let fsort = fsort.skip_binders();
322        let output = self.sort_to_fixpoint(fsort.output());
323        fixpoint::Sort::mk_func(
324            params,
325            fsort.inputs().iter().map(|s| self.sort_to_fixpoint(s)),
326            output,
327        )
328    }
329
330    fn declare_tuple(&mut self, arity: usize) {
331        self.tuples.insert(arity);
332    }
333
334    pub fn declare_opaque_sort(&mut self, def_id: FluxDefId) {
335        self.user_sorts.insert(def_id);
336    }
337
338    pub fn declare_adt(&mut self, did: DefId) -> AdtId {
339        if let Some(idx) = self.adt_sorts.get_index_of(&did) {
340            AdtId::from_usize(idx)
341        } else {
342            let adt_id = AdtId::from_usize(self.adt_sorts.len());
343            self.adt_sorts.insert(did);
344            adt_id
345        }
346    }
347
348    pub fn user_sorts_to_fixpoint(&mut self, genv: GlobalEnv) -> Vec<fixpoint::SortDecl> {
349        self.user_sorts
350            .iter()
351            .map(|sort| {
352                let param_count = genv.sort_decl_param_count(sort);
353                fixpoint::SortDecl { name: fixpoint::DataSort::User(*sort), vars: param_count }
354            })
355            .collect()
356    }
357
358    fn append_adt_decls(
359        &mut self,
360        genv: GlobalEnv,
361        decls: &mut Vec<fixpoint::DataDecl>,
362    ) -> QueryResult {
363        let mut idx = 0;
366        while let Some(adt_def_id) = self.adt_sorts.get_index(idx) {
367            let adt_id = AdtId::from_usize(idx);
368            let adt_sort_def = genv.adt_sort_def_of(adt_def_id)?;
369            decls.push(fixpoint::DataDecl {
370                name: fixpoint::DataSort::Adt(adt_id),
371                vars: adt_sort_def.param_count(),
372                ctors: adt_sort_def
373                    .variants()
374                    .iter_enumerated()
375                    .map(|(idx, variant)| {
376                        let name = fixpoint::Var::DataCtor(adt_id, idx);
377                        let fields = variant
378                            .field_sorts_instantiate_identity()
379                            .iter()
380                            .enumerate()
381                            .map(|(i, sort)| {
382                                fixpoint::DataField {
383                                    name: fixpoint::Var::DataProj { adt_id, field: i as u32 },
384                                    sort: self.sort_to_fixpoint(sort),
385                                }
386                            })
387                            .collect_vec();
388                        fixpoint::DataCtor { name, fields }
389                    })
390                    .collect(),
391            });
392            idx += 1;
393        }
394        Ok(())
395    }
396
397    fn append_tuple_decls(tuples: &UnordSet<usize>, decls: &mut Vec<fixpoint::DataDecl>) {
398        decls.extend(
399            tuples
400                .items()
401                .into_sorted_stable_ord()
402                .into_iter()
403                .map(|arity| {
404                    fixpoint::DataDecl {
405                        name: fixpoint::DataSort::Tuple(*arity),
406                        vars: *arity,
407                        ctors: vec![fixpoint::DataCtor {
408                            name: fixpoint::Var::TupleCtor { arity: *arity },
409                            fields: (0..(*arity as u32))
410                                .map(|field| {
411                                    fixpoint::DataField {
412                                        name: fixpoint::Var::TupleProj { arity: *arity, field },
413                                        sort: fixpoint::Sort::Var(field as usize),
414                                    }
415                                })
416                                .collect(),
417                        }],
418                    }
419                }),
420        );
421    }
422
423    pub fn encode_data_decls(&mut self, genv: GlobalEnv) -> QueryResult<Vec<fixpoint::DataDecl>> {
424        let mut decls = vec![];
425        self.append_adt_decls(genv, &mut decls)?;
426        Self::append_tuple_decls(&self.tuples, &mut decls);
427        Ok(decls)
428    }
429}
430
431fn bv_size_to_fixpoint(size: rty::BvSize) -> fixpoint::Sort {
432    match size {
433        rty::BvSize::Fixed(size) => fixpoint::Sort::BvSize(size),
434        rty::BvSize::Param(_var) => {
435            bug!("unexpected parametric bit-vector size")
440        }
441        rty::BvSize::Infer(_) => bug!("unexpected infer variable for bit-vector size"),
442    }
443}
444
445pub type FunDefMap = FxIndexMap<FluxDefId, fixpoint::Var>;
446type ConstMap<'tcx> = FxIndexMap<ConstKey<'tcx>, fixpoint::ConstDecl>;
447
448#[derive(Eq, Hash, PartialEq, Clone)]
449enum ConstKey<'tcx> {
450    Uif(FluxDefId),
451    RustConst(DefId),
452    Alias(FluxDefId, rustc_middle::ty::GenericArgsRef<'tcx>),
453    Lambda(Lambda),
454    PrimOp(rty::BinOp),
455    Cast(rty::Sort, rty::Sort),
456}
457
458pub struct FixpointCtxt<'genv, 'tcx, T: Eq + Hash> {
459    comments: Vec<String>,
460    genv: GlobalEnv<'genv, 'tcx>,
461    kvars: KVarGen,
462    scx: SortEncodingCtxt,
463    kcx: KVarEncodingCtxt,
464    ecx: ExprEncodingCtxt<'genv, 'tcx>,
465    tags: IndexVec<TagIdx, T>,
466    tags_inv: UnordMap<T, TagIdx>,
467}
468
469pub type FixQueryCache = QueryCache<FixpointResult<TagIdx>>;
470
471impl<'genv, 'tcx, Tag> FixpointCtxt<'genv, 'tcx, Tag>
472where
473    Tag: std::hash::Hash + Eq + Copy,
474{
475    pub fn new(genv: GlobalEnv<'genv, 'tcx>, def_id: MaybeExternId, kvars: KVarGen) -> Self {
476        Self {
477            comments: vec![],
478            kvars,
479            scx: SortEncodingCtxt::default(),
480            genv,
481            ecx: ExprEncodingCtxt::new(genv, Some(def_id)),
482            kcx: Default::default(),
483            tags: IndexVec::new(),
484            tags_inv: Default::default(),
485        }
486    }
487
488    pub fn check(
489        mut self,
490        cache: &mut FixQueryCache,
491        def_id: MaybeExternId,
492        constraint: fixpoint::Constraint,
493        kind: FixpointQueryKind,
494        scrape_quals: bool,
495        solver: SmtSolver,
496    ) -> QueryResult<Answer<Tag>> {
497        let count = constraint.concrete_head_count();
499        metrics::incr_metric(Metric::CsTotal, count as u32);
500        if count == 0 {
501            metrics::incr_metric(Metric::FnTrivial, 1);
502            self.ecx.errors.to_result()?;
503            return Ok(Answer::trivial());
504        }
505        let def_span = self.ecx.def_span();
506        let kvars = self.kcx.encode_kvars(&self.kvars, &mut self.scx);
507        let (define_funs, define_constants) = self.ecx.define_funs(def_id, &mut self.scx)?;
508        let qualifiers = self
509            .ecx
510            .qualifiers_for(def_id.local_id(), &mut self.scx)?
511            .into_iter()
512            .chain(FIXPOINT_QUALIFIERS.iter().cloned())
513            .collect();
514
515        let constraint = self.ecx.assume_const_values(constraint, &mut self.scx)?;
518
519        let mut constants = self.ecx.const_env.const_map.values().cloned().collect_vec();
520        constants.extend(define_constants);
521
522        #[cfg(not(feature = "rust-fixpoint"))]
526        for rel in fixpoint::BinRel::INEQUALITIES {
527            let sort = fixpoint::Sort::mk_func(
529                1,
530                [fixpoint::Sort::Var(0), fixpoint::Sort::Var(0)],
531                fixpoint::Sort::Bool,
532            );
533            constants.push(fixpoint::ConstDecl {
534                name: fixpoint::Var::UIFRel(rel),
535                sort,
536                comment: None,
537            });
538        }
539
540        self.ecx.errors.to_result()?;
542
543        let task = fixpoint::Task {
544            comments: self.comments.clone(),
545            constants,
546            kvars,
547            define_funs,
548            constraint,
549            qualifiers,
550            scrape_quals,
551            solver,
552            data_decls: self.scx.encode_data_decls(self.genv)?,
553        };
554        if config::dump_constraint() {
555            dbg::dump_item_info(self.genv.tcx(), def_id.resolved_id(), "smt2", &task).unwrap();
556        }
557
558        let result = Self::run_task_with_cache(self.genv, task, def_id.resolved_id(), kind, cache);
559        let solution = if config::dump_checker_trace_info() {
560            self.parse_kvar_solutions(&result)?
561        } else {
562            HashMap::default()
563        };
564
565        let errors = match result.status {
566            FixpointStatus::Safe(_) => vec![],
567            FixpointStatus::Unsafe(_, errors) => {
568                metrics::incr_metric(Metric::CsError, errors.len() as u32);
569                errors
570                    .into_iter()
571                    .map(|err| self.tags[err.tag])
572                    .unique()
573                    .collect_vec()
574            }
575            FixpointStatus::Crash(err) => span_bug!(def_span, "fixpoint crash: {err:?}"),
576        };
577        Ok(Answer { errors, solution })
578    }
579
580    fn parse_kvar_solutions(&self, result: &FixpointResult<TagIdx>) -> QueryResult<Solution> {
581        Ok(self.kcx.group_kvar_solution(
582            result
583                .solution
584                .iter()
585                .chain(&result.non_cuts_solution)
586                .map(|b| self.convert_kvar_bind(b))
587                .try_collect_vec()?,
588        ))
589    }
590
591    fn convert_solution(&self, expr: &str) -> QueryResult<rty::Binder<rty::Expr>> {
592        let mut sexp_parser = Parser::new(expr);
594        let sexp = match sexp_parser.parse() {
595            Ok(sexp) => sexp,
596            Err(err) => {
597                tracked_span_bug!("cannot parse sexp: {expr:?}: {err:?}");
598            }
599        };
600
601        let mut sexp_ctx = SexpParseCtxt.into_wrapper();
603        let (sorts, expr) = sexp_ctx.parse_solution(&sexp).unwrap_or_else(|err| {
604            tracked_span_bug!("failed to parse solution sexp {sexp:?}: {err:?}");
605        });
606
607        let sorts = sorts
609            .iter()
610            .map(|s| self.fixpoint_to_sort(s))
611            .try_collect_vec()
612            .unwrap_or_else(|err| {
613                tracked_span_bug!("failed to convert sorts: {err:?}");
614            });
615        let expr = self
616            .fixpoint_to_expr(&expr)
617            .unwrap_or_else(|err| tracked_span_bug!("failed to convert expr: {err:?}"));
618        Ok(rty::Binder::bind_with_sorts(expr, &sorts))
619    }
620
621    fn convert_kvar_bind(
622        &self,
623        b: &liquid_fixpoint::KVarBind,
624    ) -> QueryResult<(fixpoint::KVid, rty::Binder<rty::Expr>)> {
625        let kvid = parse_kvid(&b.kvar);
626        let expr = self.convert_solution(&b.val)?;
627        Ok((kvid, expr))
628    }
629
630    pub fn generate_and_check_lean_lemmas(
631        mut self,
632        constraint: fixpoint::Constraint,
633    ) -> QueryResult<()> {
634        if let Some(def_id) = self.ecx.def_id {
635            let kvar_decls = self.kcx.encode_kvars(&self.kvars, &mut self.scx);
636            self.ecx.errors.to_result()?;
637
638            let lean_encoder = LeanEncoder::new(
639                self.genv,
640                std::path::Path::new("./"),
641                "lean_proofs".to_string(),
642                "Defs".to_string(),
643            );
644            lean_encoder
645                .encode_constraint(def_id, &kvar_decls, &constraint)
646                .map_err(|_| query_bug!("could not encode constraint"))?;
647            lean_encoder.check_proof(def_id)
648        } else {
649            Ok(())
650        }
651    }
652
653    fn run_task_with_cache(
654        genv: GlobalEnv,
655        task: fixpoint::Task,
656        def_id: DefId,
657        kind: FixpointQueryKind,
658        cache: &mut FixQueryCache,
659    ) -> FixpointResult<TagIdx> {
660        let key = kind.task_key(genv.tcx(), def_id);
661
662        let hash = task.hash_with_default();
663
664        if config::is_cache_enabled()
665            && let Some(result) = cache.lookup(&key, hash)
666        {
667            metrics::incr_metric(Metric::FnCached, 1);
668            return result.clone();
669        }
670        let result = metrics::time_it(TimingKind::FixpointQuery(def_id, kind), || {
671            task.run()
672                .unwrap_or_else(|err| tracked_span_bug!("failed to run fixpoint: {err}"))
673        });
674
675        if config::is_cache_enabled() {
676            cache.insert(key, hash, result.clone());
677        }
678
679        result
680    }
681
682    fn tag_idx(&mut self, tag: Tag) -> TagIdx
683    where
684        Tag: std::fmt::Debug,
685    {
686        *self.tags_inv.entry(tag).or_insert_with(|| {
687            let idx = self.tags.push(tag);
688            self.comments.push(format!("Tag {idx}: {tag:?}"));
689            idx
690        })
691    }
692
693    pub(crate) fn with_name_map<R>(
694        &mut self,
695        name: rty::Name,
696        f: impl FnOnce(&mut Self, fixpoint::LocalVar) -> R,
697    ) -> R {
698        let fresh = self.ecx.local_var_env.insert_fvar_map(name);
699        let r = f(self, fresh);
700        self.ecx.local_var_env.remove_fvar_map(name);
701        r
702    }
703
704    pub(crate) fn sort_to_fixpoint(&mut self, sort: &rty::Sort) -> fixpoint::Sort {
705        self.scx.sort_to_fixpoint(sort)
706    }
707
708    pub(crate) fn var_to_fixpoint(&self, var: &rty::Var) -> fixpoint::Var {
709        self.ecx.var_to_fixpoint(var)
710    }
711
712    pub(crate) fn head_to_fixpoint(
717        &mut self,
718        expr: &rty::Expr,
719        mk_tag: impl Fn(Option<ESpan>) -> Tag + Copy,
720    ) -> QueryResult<fixpoint::Constraint>
721    where
722        Tag: std::fmt::Debug,
723    {
724        match expr.kind() {
725            rty::ExprKind::BinaryOp(rty::BinOp::And, ..) => {
726                let cstrs = expr
728                    .flatten_conjs()
729                    .into_iter()
730                    .map(|e| self.head_to_fixpoint(e, mk_tag))
731                    .try_collect()?;
732                Ok(fixpoint::Constraint::conj(cstrs))
733            }
734            rty::ExprKind::BinaryOp(rty::BinOp::Imp, e1, e2) => {
735                let (bindings, assumption) = self.assumption_to_fixpoint(e1)?;
736                let cstr = self.head_to_fixpoint(e2, mk_tag)?;
737                Ok(fixpoint::Constraint::foralls(bindings, mk_implies(assumption, cstr)))
738            }
739            rty::ExprKind::KVar(kvar) => {
740                let mut bindings = vec![];
741                let pred = self.kvar_to_fixpoint(kvar, &mut bindings)?;
742                Ok(fixpoint::Constraint::foralls(bindings, fixpoint::Constraint::Pred(pred, None)))
743            }
744            rty::ExprKind::ForAll(pred) => {
745                self.ecx
746                    .local_var_env
747                    .push_layer_with_fresh_names(pred.vars().len());
748                let cstr = self.head_to_fixpoint(pred.as_ref().skip_binder(), mk_tag)?;
749                let vars = self.ecx.local_var_env.pop_layer();
750
751                let bindings = iter::zip(vars, pred.vars())
752                    .map(|(var, kind)| {
753                        fixpoint::Bind {
754                            name: var.into(),
755                            sort: self.scx.sort_to_fixpoint(kind.expect_sort()),
756                            pred: fixpoint::Pred::TRUE,
757                        }
758                    })
759                    .collect_vec();
760
761                Ok(fixpoint::Constraint::foralls(bindings, cstr))
762            }
763            _ => {
764                let tag_idx = self.tag_idx(mk_tag(expr.span()));
765                let pred = fixpoint::Pred::Expr(self.ecx.expr_to_fixpoint(expr, &mut self.scx)?);
766                Ok(fixpoint::Constraint::Pred(pred, Some(tag_idx)))
767            }
768        }
769    }
770
771    pub(crate) fn assumption_to_fixpoint(
776        &mut self,
777        pred: &rty::Expr,
778    ) -> QueryResult<(Vec<fixpoint::Bind>, fixpoint::Pred)> {
779        let mut bindings = vec![];
780        let mut preds = vec![];
781        self.assumption_to_fixpoint_aux(pred, &mut bindings, &mut preds)?;
782        Ok((bindings, fixpoint::Pred::and(preds)))
783    }
784
785    fn assumption_to_fixpoint_aux(
787        &mut self,
788        expr: &rty::Expr,
789        bindings: &mut Vec<fixpoint::Bind>,
790        preds: &mut Vec<fixpoint::Pred>,
791    ) -> QueryResult {
792        match expr.kind() {
793            rty::ExprKind::BinaryOp(rty::BinOp::And, e1, e2) => {
794                self.assumption_to_fixpoint_aux(e1, bindings, preds)?;
795                self.assumption_to_fixpoint_aux(e2, bindings, preds)?;
796            }
797            rty::ExprKind::KVar(kvar) => {
798                preds.push(self.kvar_to_fixpoint(kvar, bindings)?);
799            }
800            rty::ExprKind::ForAll(_) => {
801                preds.push(fixpoint::Pred::TRUE);
807            }
808            _ => {
809                preds.push(fixpoint::Pred::Expr(self.ecx.expr_to_fixpoint(expr, &mut self.scx)?));
810            }
811        }
812        Ok(())
813    }
814
815    fn kvar_to_fixpoint(
816        &mut self,
817        kvar: &rty::KVar,
818        bindings: &mut Vec<fixpoint::Bind>,
819    ) -> QueryResult<fixpoint::Pred> {
820        let decl = self.kvars.get(kvar.kvid);
821        let kvids = self.kcx.declare(kvar.kvid, decl);
822
823        let all_args = iter::zip(&kvar.args, &decl.sorts)
824            .map(|(arg, sort)| self.ecx.imm(arg, sort, &mut self.scx, bindings))
825            .try_collect_vec()?;
826
827        if all_args.is_empty() {
831            let fresh = self.ecx.local_var_env.fresh_name();
832            let var = fixpoint::Var::Local(fresh);
833            bindings.push(fixpoint::Bind {
834                name: fresh.into(),
835                sort: fixpoint::Sort::Int,
836                pred: fixpoint::Pred::Expr(fixpoint::Expr::eq(
837                    fixpoint::Expr::Var(var),
838                    fixpoint::Expr::int(0),
839                )),
840            });
841            return Ok(fixpoint::Pred::KVar(kvids.start, vec![var]));
842        }
843
844        let kvars = kvids
845            .enumerate()
846            .map(|(i, kvid)| {
847                let args = all_args[i..].to_vec();
848                fixpoint::Pred::KVar(kvid, args)
849            })
850            .collect_vec();
851
852        Ok(fixpoint::Pred::And(kvars))
853    }
854}
855
856fn const_to_fixpoint(cst: rty::Constant) -> fixpoint::Expr {
857    match cst {
858        rty::Constant::Int(i) => {
859            if i.is_negative() {
860                fixpoint::Expr::Neg(Box::new(fixpoint::Constant::Numeral(i.abs()).into()))
861            } else {
862                fixpoint::Constant::Numeral(i.abs()).into()
863            }
864        }
865        rty::Constant::Real(r) => fixpoint::Constant::Real(r.0).into(),
866        rty::Constant::Bool(b) => fixpoint::Constant::Boolean(b).into(),
867        rty::Constant::Char(c) => fixpoint::Constant::Numeral(u128::from(c)).into(),
868        rty::Constant::Str(s) => fixpoint::Constant::String(fixpoint::SymStr(s)).into(),
869        rty::Constant::BitVec(i, size) => fixpoint::Constant::BitVec(i, size).into(),
870    }
871}
872
873#[derive(Default)]
878struct KVarEncodingCtxt {
879    ranges: FxIndexMap<rty::KVid, Range<fixpoint::KVid>>,
882}
883
884impl KVarEncodingCtxt {
885    fn declare(&mut self, kvid: rty::KVid, decl: &KVarDecl) -> Range<fixpoint::KVid> {
888        let start = self
890            .ranges
891            .last()
892            .map_or(fixpoint::KVid::from_u32(0), |(_, r)| r.end);
893
894        self.ranges
895            .entry(kvid)
896            .or_insert_with(|| {
897                match decl.encoding {
898                    KVarEncoding::Single => start..start + 1,
899                    KVarEncoding::Conj => {
900                        let n = usize::max(decl.self_args, 1);
901                        start..start + n
902                    }
903                }
904            })
905            .clone()
906    }
907
908    fn encode_kvars(&self, kvars: &KVarGen, scx: &mut SortEncodingCtxt) -> Vec<fixpoint::KVarDecl> {
909        self.ranges
910            .iter()
911            .flat_map(|(orig, range)| {
912                let mut all_sorts = kvars
913                    .get(*orig)
914                    .sorts
915                    .iter()
916                    .map(|s| scx.sort_to_fixpoint(s))
917                    .collect_vec();
918
919                if all_sorts.is_empty() {
921                    all_sorts = vec![fixpoint::Sort::Int];
922                }
923
924                range.clone().enumerate().map(move |(i, kvid)| {
925                    let sorts = all_sorts[i..].to_vec();
926                    fixpoint::KVarDecl::new(kvid, sorts, format!("orig: {:?}", orig))
927                })
928            })
929            .collect()
930    }
931
932    fn group_kvar_solution(
939        &self,
940        mut items: Vec<(fixpoint::KVid, rty::Binder<rty::Expr>)>,
941    ) -> HashMap<rty::KVid, rty::Binder<rty::Expr>> {
942        let mut map = HashMap::default();
943
944        items.sort_by_key(|(kvid, _)| *kvid);
945        items.reverse();
946
947        for (orig, range) in &self.ranges {
948            let mut preds = vec![];
949            while let Some((_, t)) = items.pop_if(|(k, _)| range.contains(k)) {
950                preds.push(t);
951            }
952            if preds.len() == range.end.as_usize() - range.start.as_usize() {
954                let vars = preds[0].vars().clone();
955                let conj = rty::Expr::and_from_iter(
956                    preds
957                        .into_iter()
958                        .enumerate()
959                        .map(|(i, e)| e.skip_binder().shift_horizontally(i)),
960                );
961                map.insert(*orig, rty::Binder::bind_with_vars(conj, vars));
962            }
963        }
964        map
965    }
966}
967
968struct LocalVarEnv {
970    local_var_gen: IndexGen<fixpoint::LocalVar>,
971    fvars: UnordMap<rty::Name, fixpoint::LocalVar>,
972    layers: Vec<Vec<fixpoint::LocalVar>>,
974    reverse_map: UnordMap<fixpoint::LocalVar, rty::Expr>,
979}
980
981impl LocalVarEnv {
982    fn new() -> Self {
983        Self {
984            local_var_gen: IndexGen::new(),
985            fvars: Default::default(),
986            layers: Vec::new(),
987            reverse_map: Default::default(),
988        }
989    }
990
991    fn fresh_name(&mut self) -> fixpoint::LocalVar {
994        self.local_var_gen.fresh()
995    }
996
997    fn insert_fvar_map(&mut self, name: rty::Name) -> fixpoint::LocalVar {
998        let fresh = self.fresh_name();
999        self.fvars.insert(name, fresh);
1000        self.reverse_map.insert(fresh, rty::Expr::fvar(name));
1001        fresh
1002    }
1003
1004    fn remove_fvar_map(&mut self, name: rty::Name) {
1005        self.fvars.remove(&name);
1006    }
1007
1008    fn push_layer_with_fresh_names(&mut self, count: usize) {
1010        let layer = (0..count).map(|_| self.fresh_name()).collect();
1011        self.layers.push(layer);
1012        }
1014
1015    fn pop_layer(&mut self) -> Vec<fixpoint::LocalVar> {
1016        self.layers.pop().unwrap()
1017    }
1018
1019    fn get_fvar(&self, name: rty::Name) -> Option<fixpoint::LocalVar> {
1020        self.fvars.get(&name).copied()
1021    }
1022
1023    fn get_late_bvar(&self, debruijn: DebruijnIndex, var: BoundVar) -> Option<fixpoint::LocalVar> {
1024        let depth = self.layers.len().checked_sub(debruijn.as_usize() + 1)?;
1025        self.layers[depth].get(var.as_usize()).copied()
1026    }
1027}
1028
1029pub struct KVarGen {
1030    kvars: IndexVec<rty::KVid, KVarDecl>,
1031    dummy: bool,
1036}
1037
1038impl KVarGen {
1039    pub(crate) fn new(dummy: bool) -> Self {
1040        Self { kvars: IndexVec::new(), dummy }
1041    }
1042
1043    fn get(&self, kvid: rty::KVid) -> &KVarDecl {
1044        &self.kvars[kvid]
1045    }
1046
1047    pub fn fresh(
1066        &mut self,
1067        binders: &[rty::BoundVariableKinds],
1068        scope: impl IntoIterator<Item = (rty::Var, rty::Sort)>,
1069        encoding: KVarEncoding,
1070    ) -> rty::Expr {
1071        if self.dummy {
1072            return rty::Expr::hole(rty::HoleKind::Pred);
1073        }
1074
1075        let args = itertools::chain(
1076            binders.iter().rev().enumerate().flat_map(|(level, vars)| {
1077                let debruijn = DebruijnIndex::from_usize(level);
1078                vars.iter()
1079                    .cloned()
1080                    .enumerate()
1081                    .flat_map(move |(idx, var)| {
1082                        if let rty::BoundVariableKind::Refine(sort, _, kind) = var {
1083                            let br = rty::BoundReft { var: BoundVar::from_usize(idx), kind };
1084                            Some((rty::Var::Bound(debruijn, br), sort))
1085                        } else {
1086                            None
1087                        }
1088                    })
1089            }),
1090            scope,
1091        );
1092        let [.., last] = binders else {
1093            return self.fresh_inner(0, [], encoding);
1094        };
1095        let num_self_args = last
1096            .iter()
1097            .filter(|var| matches!(var, rty::BoundVariableKind::Refine(..)))
1098            .count();
1099        self.fresh_inner(num_self_args, args, encoding)
1100    }
1101
1102    fn fresh_inner<A>(&mut self, self_args: usize, args: A, encoding: KVarEncoding) -> rty::Expr
1103    where
1104        A: IntoIterator<Item = (rty::Var, rty::Sort)>,
1105    {
1106        let mut sorts = vec![];
1108        let mut exprs = vec![];
1109
1110        let mut flattened_self_args = 0;
1111        for (i, (var, sort)) in args.into_iter().enumerate() {
1112            let is_self_arg = i < self_args;
1113            let var = var.to_expr();
1114            sort.walk(|sort, proj| {
1115                if !matches!(sort, rty::Sort::Loc) {
1116                    flattened_self_args += is_self_arg as usize;
1117                    sorts.push(sort.clone());
1118                    exprs.push(rty::Expr::field_projs(&var, proj));
1119                }
1120            });
1121        }
1122
1123        let kvid = self
1124            .kvars
1125            .push(KVarDecl { self_args: flattened_self_args, sorts, encoding });
1126
1127        let kvar = rty::KVar::new(kvid, flattened_self_args, exprs);
1128        rty::Expr::kvar(kvar)
1129    }
1130}
1131
1132#[derive(Clone)]
1133struct KVarDecl {
1134    self_args: usize,
1135    sorts: Vec<rty::Sort>,
1136    encoding: KVarEncoding,
1137}
1138
1139#[derive(Clone, Copy)]
1141pub enum KVarEncoding {
1142    Single,
1145    Conj,
1149}
1150
1151impl std::fmt::Display for TagIdx {
1152    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1153        write!(f, "{}", self.as_u32())
1154    }
1155}
1156
1157impl std::str::FromStr for TagIdx {
1158    type Err = std::num::ParseIntError;
1159
1160    fn from_str(s: &str) -> Result<Self, Self::Err> {
1161        Ok(Self::from_u32(s.parse()?))
1162    }
1163}
1164
1165#[derive(Default)]
1166struct ConstEnv<'tcx> {
1167    const_map: ConstMap<'tcx>,
1168    const_map_rev: HashMap<fixpoint::GlobalVar, ConstKey<'tcx>>,
1169    global_var_gen: IndexGen<fixpoint::GlobalVar>,
1170    pub fun_def_map: FunDefMap,
1171}
1172
1173impl<'tcx> ConstEnv<'tcx> {
1174    fn get_or_insert(
1175        &mut self,
1176        key: ConstKey<'tcx>,
1177        make_const_decl: impl FnOnce(fixpoint::GlobalVar) -> fixpoint::ConstDecl,
1179    ) -> &fixpoint::ConstDecl {
1180        self.const_map.entry(key.clone()).or_insert_with(|| {
1181            let global_name = self.global_var_gen.fresh();
1182            self.const_map_rev.insert(global_name, key);
1183            make_const_decl(global_name)
1184        })
1185    }
1186}
1187
1188pub struct ExprEncodingCtxt<'genv, 'tcx> {
1189    genv: GlobalEnv<'genv, 'tcx>,
1190    local_var_env: LocalVarEnv,
1191    const_env: ConstEnv<'tcx>,
1192    errors: Errors<'genv>,
1193    def_id: Option<MaybeExternId>,
1196    infcx: rustc_infer::infer::InferCtxt<'tcx>,
1197}
1198
1199impl<'genv, 'tcx> ExprEncodingCtxt<'genv, 'tcx> {
1200    pub fn new(genv: GlobalEnv<'genv, 'tcx>, def_id: Option<MaybeExternId>) -> Self {
1201        Self {
1202            genv,
1203            local_var_env: LocalVarEnv::new(),
1204            const_env: Default::default(),
1205            errors: Errors::new(genv.sess()),
1206            def_id,
1207            infcx: genv
1208                .tcx()
1209                .infer_ctxt()
1210                .with_next_trait_solver(true)
1211                .build(TypingMode::non_body_analysis()),
1212        }
1213    }
1214
1215    fn def_span(&self) -> Span {
1216        self.def_id
1217            .map_or(DUMMY_SP, |def_id| self.genv.tcx().def_span(def_id))
1218    }
1219
1220    fn var_to_fixpoint(&self, var: &rty::Var) -> fixpoint::Var {
1221        match var {
1222            rty::Var::Free(name) => {
1223                self.local_var_env
1224                    .get_fvar(*name)
1225                    .unwrap_or_else(|| {
1226                        span_bug!(self.def_span(), "no entry found for name: `{name:?}`")
1227                    })
1228                    .into()
1229            }
1230            rty::Var::Bound(debruijn, breft) => {
1231                self.local_var_env
1232                    .get_late_bvar(*debruijn, breft.var)
1233                    .unwrap_or_else(|| {
1234                        span_bug!(self.def_span(), "no entry found for late bound var: `{breft:?}`")
1235                    })
1236                    .into()
1237            }
1238            rty::Var::ConstGeneric(param) => fixpoint::Var::ConstGeneric(*param),
1239            rty::Var::EarlyParam(param) => fixpoint::Var::Param(*param),
1240            rty::Var::EVar(_) => {
1241                span_bug!(self.def_span(), "unexpected evar: `{var:?}`")
1242            }
1243        }
1244    }
1245
1246    fn variant_to_fixpoint(
1247        &self,
1248        scx: &mut SortEncodingCtxt,
1249        enum_def_id: &DefId,
1250        idx: VariantIdx,
1251    ) -> fixpoint::Var {
1252        let adt_id = scx.declare_adt(*enum_def_id);
1253        fixpoint::Var::DataCtor(adt_id, idx)
1254    }
1255
1256    fn struct_fields_to_fixpoint(
1257        &mut self,
1258        did: &DefId,
1259        flds: &[rty::Expr],
1260        scx: &mut SortEncodingCtxt,
1261    ) -> QueryResult<fixpoint::Expr> {
1262        if let [fld] = flds {
1264            self.expr_to_fixpoint(fld, scx)
1265        } else {
1266            let adt_id = scx.declare_adt(*did);
1267            let ctor = fixpoint::Expr::Var(fixpoint::Var::DataCtor(adt_id, VariantIdx::ZERO));
1268            let args = flds
1269                .iter()
1270                .map(|fld| self.expr_to_fixpoint(fld, scx))
1271                .try_collect()?;
1272            Ok(fixpoint::Expr::App(Box::new(ctor), args))
1273        }
1274    }
1275
1276    fn fields_to_fixpoint(
1277        &mut self,
1278        flds: &[rty::Expr],
1279        scx: &mut SortEncodingCtxt,
1280    ) -> QueryResult<fixpoint::Expr> {
1281        if let [fld] = flds {
1283            self.expr_to_fixpoint(fld, scx)
1284        } else {
1285            scx.declare_tuple(flds.len());
1286            let ctor = fixpoint::Expr::Var(fixpoint::Var::TupleCtor { arity: flds.len() });
1287            let args = flds
1288                .iter()
1289                .map(|fld| self.expr_to_fixpoint(fld, scx))
1290                .try_collect()?;
1291            Ok(fixpoint::Expr::App(Box::new(ctor), args))
1292        }
1293    }
1294
1295    fn internal_func_to_fixpoint(
1296        &mut self,
1297        internal_func: &InternalFuncKind,
1298        sort_args: &[rty::SortArg],
1299        args: &[rty::Expr],
1300        scx: &mut SortEncodingCtxt,
1301    ) -> QueryResult<fixpoint::Expr> {
1302        match internal_func {
1303            InternalFuncKind::Val(op) => {
1304                let func = fixpoint::Expr::Var(self.define_const_for_prim_op(op, scx));
1305                let args = self.exprs_to_fixpoint(args, scx)?;
1306                Ok(fixpoint::Expr::App(Box::new(func), args))
1307            }
1308            InternalFuncKind::Rel(op) => {
1309                let expr = if let Some(prim_rel) = self.genv.prim_rel_for(op)? {
1310                    prim_rel.body.replace_bound_refts(args)
1311                } else {
1312                    rty::Expr::tt()
1313                };
1314                self.expr_to_fixpoint(&expr, scx)
1315            }
1316            InternalFuncKind::Cast => {
1317                let [rty::SortArg::Sort(from), rty::SortArg::Sort(to)] = &sort_args else {
1318                    span_bug!(self.def_span(), "unexpected cast")
1319                };
1320                match from.cast_kind(to) {
1321                    rty::CastKind::Identity => self.expr_to_fixpoint(&args[0], scx),
1322                    rty::CastKind::BoolToInt => {
1323                        Ok(fixpoint::Expr::IfThenElse(Box::new([
1324                            self.expr_to_fixpoint(&args[0], scx)?,
1325                            fixpoint::Expr::int(1),
1326                            fixpoint::Expr::int(0),
1327                        ])))
1328                    }
1329                    rty::CastKind::IntoUnit => self.expr_to_fixpoint(&rty::Expr::unit(), scx),
1330                    rty::CastKind::Uninterpreted => {
1331                        let func = fixpoint::Expr::Var(self.define_const_for_cast(from, to, scx));
1332                        let args = self.exprs_to_fixpoint(args, scx)?;
1333                        Ok(fixpoint::Expr::App(Box::new(func), args))
1334                    }
1335                }
1336            }
1337        }
1338    }
1339
1340    fn structurally_normalize_expr(&self, expr: &rty::Expr) -> QueryResult<rty::Expr> {
1341        if let Some(def_id) = self.def_id {
1342            structurally_normalize_expr(self.genv, def_id.resolved_id(), &self.infcx, expr)
1343        } else {
1344            Ok(expr.clone())
1345        }
1346    }
1347
1348    fn expr_to_fixpoint(
1349        &mut self,
1350        expr: &rty::Expr,
1351        scx: &mut SortEncodingCtxt,
1352    ) -> QueryResult<fixpoint::Expr> {
1353        let expr = self.structurally_normalize_expr(expr)?;
1354        let e = match expr.kind() {
1355            rty::ExprKind::Var(var) => fixpoint::Expr::Var(self.var_to_fixpoint(var)),
1356            rty::ExprKind::Constant(c) => const_to_fixpoint(*c),
1357            rty::ExprKind::BinaryOp(op, e1, e2) => self.bin_op_to_fixpoint(op, e1, e2, scx)?,
1358            rty::ExprKind::UnaryOp(op, e) => self.un_op_to_fixpoint(*op, e, scx)?,
1359            rty::ExprKind::FieldProj(e, proj) => self.proj_to_fixpoint(e, *proj, scx)?,
1360            rty::ExprKind::Tuple(flds) => self.fields_to_fixpoint(flds, scx)?,
1361            rty::ExprKind::Ctor(rty::Ctor::Struct(did), flds) => {
1362                self.struct_fields_to_fixpoint(did, flds, scx)?
1363            }
1364            rty::ExprKind::IsCtor(def_id, variant_idx, e) => {
1365                let ctor = self.variant_to_fixpoint(scx, def_id, *variant_idx);
1366                let e = self.expr_to_fixpoint(e, scx)?;
1367                fixpoint::Expr::IsCtor(ctor, Box::new(e))
1368            }
1369            rty::ExprKind::Ctor(rty::Ctor::Enum(did, idx), args) => {
1370                let ctor = self.variant_to_fixpoint(scx, did, *idx);
1371                let args = self.exprs_to_fixpoint(args, scx)?;
1372                fixpoint::Expr::App(Box::new(fixpoint::Expr::Var(ctor)), args)
1373            }
1374            rty::ExprKind::ConstDefId(did) => {
1375                let var = self.define_const_for_rust_const(*did, scx);
1376                fixpoint::Expr::Var(var)
1377            }
1378            rty::ExprKind::App(func, sort_args, args) => {
1379                if let rty::ExprKind::InternalFunc(func) = func.kind() {
1380                    self.internal_func_to_fixpoint(func, sort_args, args, scx)?
1381                } else {
1382                    let func = self.expr_to_fixpoint(func, scx)?;
1383                    let args = self.exprs_to_fixpoint(args, scx)?;
1384                    fixpoint::Expr::App(Box::new(func), args)
1385                }
1386            }
1387            rty::ExprKind::IfThenElse(p, e1, e2) => {
1388                fixpoint::Expr::IfThenElse(Box::new([
1389                    self.expr_to_fixpoint(p, scx)?,
1390                    self.expr_to_fixpoint(e1, scx)?,
1391                    self.expr_to_fixpoint(e2, scx)?,
1392                ]))
1393            }
1394            rty::ExprKind::Alias(alias_reft, args) => {
1395                let sort = self.genv.sort_of_assoc_reft(alias_reft.assoc_id)?;
1396                let sort = sort.instantiate_identity();
1397                let func =
1398                    fixpoint::Expr::Var(self.define_const_for_alias_reft(alias_reft, sort, scx));
1399                let args = args
1400                    .iter()
1401                    .map(|expr| self.expr_to_fixpoint(expr, scx))
1402                    .try_collect()?;
1403                fixpoint::Expr::App(Box::new(func), args)
1404            }
1405            rty::ExprKind::Abs(lam) => {
1406                let var = self.define_const_for_lambda(lam, scx);
1407                fixpoint::Expr::Var(var)
1408            }
1409            rty::ExprKind::Let(init, body) => {
1410                debug_assert_eq!(body.vars().len(), 1);
1411                let init = self.expr_to_fixpoint(init, scx)?;
1412
1413                self.local_var_env.push_layer_with_fresh_names(1);
1414                let body = self.expr_to_fixpoint(body.skip_binder_ref(), scx)?;
1415                let vars = self.local_var_env.pop_layer();
1416
1417                fixpoint::Expr::Let(vars[0].into(), Box::new([init, body]))
1418            }
1419            rty::ExprKind::GlobalFunc(SpecFuncKind::Thy(itf)) => fixpoint::Expr::ThyFunc(*itf),
1420            rty::ExprKind::GlobalFunc(SpecFuncKind::Uif(def_id)) => {
1421                fixpoint::Expr::Var(self.define_const_for_uif(*def_id, scx))
1422            }
1423            rty::ExprKind::GlobalFunc(SpecFuncKind::Def(def_id)) => {
1424                fixpoint::Expr::Var(self.declare_fun(*def_id))
1425            }
1426            rty::ExprKind::Hole(..)
1427            | rty::ExprKind::KVar(_)
1428            | rty::ExprKind::Local(_)
1429            | rty::ExprKind::PathProj(..)
1430            | rty::ExprKind::ForAll(_)
1431            | rty::ExprKind::Exists(_)
1432            | rty::ExprKind::InternalFunc(_) => {
1433                span_bug!(self.def_span(), "unexpected expr: `{expr:?}`")
1434            }
1435            rty::ExprKind::BoundedQuant(kind, rng, body) => {
1436                let exprs = (rng.start..rng.end).map(|i| {
1437                    let arg = rty::Expr::constant(rty::Constant::from(i));
1438                    body.replace_bound_reft(&arg)
1439                });
1440                let expr = match kind {
1441                    flux_middle::fhir::QuantKind::Forall => rty::Expr::and_from_iter(exprs),
1442                    flux_middle::fhir::QuantKind::Exists => rty::Expr::or_from_iter(exprs),
1443                };
1444                self.expr_to_fixpoint(&expr, scx)?
1445            }
1446        };
1447        Ok(e)
1448    }
1449
1450    fn exprs_to_fixpoint<'b>(
1451        &mut self,
1452        exprs: impl IntoIterator<Item = &'b rty::Expr>,
1453        scx: &mut SortEncodingCtxt,
1454    ) -> QueryResult<Vec<fixpoint::Expr>> {
1455        exprs
1456            .into_iter()
1457            .map(|e| self.expr_to_fixpoint(e, scx))
1458            .try_collect()
1459    }
1460
1461    fn proj_to_fixpoint(
1462        &mut self,
1463        e: &rty::Expr,
1464        proj: rty::FieldProj,
1465        scx: &mut SortEncodingCtxt,
1466    ) -> QueryResult<fixpoint::Expr> {
1467        let arity = proj.arity(self.genv)?;
1468        if arity == 1 {
1470            self.expr_to_fixpoint(e, scx)
1471        } else {
1472            let proj = match proj {
1473                rty::FieldProj::Tuple { arity, field } => {
1474                    scx.declare_tuple(arity);
1475                    fixpoint::Var::TupleProj { arity, field }
1476                }
1477                rty::FieldProj::Adt { def_id, field } => {
1478                    let adt_id = scx.declare_adt(def_id);
1479                    fixpoint::Var::DataProj { adt_id, field }
1480                }
1481            };
1482            let proj = fixpoint::Expr::Var(proj);
1483            Ok(fixpoint::Expr::App(Box::new(proj), vec![self.expr_to_fixpoint(e, scx)?]))
1484        }
1485    }
1486
1487    fn un_op_to_fixpoint(
1488        &mut self,
1489        op: rty::UnOp,
1490        e: &rty::Expr,
1491        scx: &mut SortEncodingCtxt,
1492    ) -> QueryResult<fixpoint::Expr> {
1493        match op {
1494            rty::UnOp::Not => Ok(fixpoint::Expr::Not(Box::new(self.expr_to_fixpoint(e, scx)?))),
1495            rty::UnOp::Neg => Ok(fixpoint::Expr::Neg(Box::new(self.expr_to_fixpoint(e, scx)?))),
1496        }
1497    }
1498
1499    fn bv_rel_to_fixpoint(&self, rel: &fixpoint::BinRel) -> fixpoint::Expr {
1500        let itf = match rel {
1501            fixpoint::BinRel::Gt => fixpoint::ThyFunc::BvUgt,
1502            fixpoint::BinRel::Ge => fixpoint::ThyFunc::BvUge,
1503            fixpoint::BinRel::Lt => fixpoint::ThyFunc::BvUlt,
1504            fixpoint::BinRel::Le => fixpoint::ThyFunc::BvUle,
1505            _ => span_bug!(self.def_span(), "not a bitvector relation!"),
1506        };
1507        fixpoint::Expr::ThyFunc(itf)
1508    }
1509
1510    fn bv_op_to_fixpoint(&self, op: &rty::BinOp) -> fixpoint::Expr {
1511        let itf = match op {
1512            rty::BinOp::Add(_) => fixpoint::ThyFunc::BvAdd,
1513            rty::BinOp::Sub(_) => fixpoint::ThyFunc::BvSub,
1514            rty::BinOp::Mul(_) => fixpoint::ThyFunc::BvMul,
1515            rty::BinOp::Div(_) => fixpoint::ThyFunc::BvUdiv,
1516            rty::BinOp::Mod(_) => fixpoint::ThyFunc::BvUrem,
1517            rty::BinOp::BitAnd => fixpoint::ThyFunc::BvAnd,
1518            rty::BinOp::BitOr => fixpoint::ThyFunc::BvOr,
1519            rty::BinOp::BitXor => fixpoint::ThyFunc::BvXor,
1520            rty::BinOp::BitShl => fixpoint::ThyFunc::BvShl,
1521            rty::BinOp::BitShr => fixpoint::ThyFunc::BvLshr,
1522            _ => span_bug!(self.def_span(), "not a bitvector operation!"),
1523        };
1524        fixpoint::Expr::ThyFunc(itf)
1525    }
1526
1527    fn bin_op_to_fixpoint(
1528        &mut self,
1529        op: &rty::BinOp,
1530        e1: &rty::Expr,
1531        e2: &rty::Expr,
1532        scx: &mut SortEncodingCtxt,
1533    ) -> QueryResult<fixpoint::Expr> {
1534        let op = match op {
1535            rty::BinOp::Eq => {
1536                return Ok(fixpoint::Expr::Atom(
1537                    fixpoint::BinRel::Eq,
1538                    Box::new([self.expr_to_fixpoint(e1, scx)?, self.expr_to_fixpoint(e2, scx)?]),
1539                ));
1540            }
1541            rty::BinOp::Ne => {
1542                return Ok(fixpoint::Expr::Atom(
1543                    fixpoint::BinRel::Ne,
1544                    Box::new([self.expr_to_fixpoint(e1, scx)?, self.expr_to_fixpoint(e2, scx)?]),
1545                ));
1546            }
1547            rty::BinOp::Gt(sort) => {
1548                return self.bin_rel_to_fixpoint(sort, fixpoint::BinRel::Gt, e1, e2, scx);
1549            }
1550            rty::BinOp::Ge(sort) => {
1551                return self.bin_rel_to_fixpoint(sort, fixpoint::BinRel::Ge, e1, e2, scx);
1552            }
1553            rty::BinOp::Lt(sort) => {
1554                return self.bin_rel_to_fixpoint(sort, fixpoint::BinRel::Lt, e1, e2, scx);
1555            }
1556            rty::BinOp::Le(sort) => {
1557                return self.bin_rel_to_fixpoint(sort, fixpoint::BinRel::Le, e1, e2, scx);
1558            }
1559            rty::BinOp::And => {
1560                return Ok(fixpoint::Expr::And(vec![
1561                    self.expr_to_fixpoint(e1, scx)?,
1562                    self.expr_to_fixpoint(e2, scx)?,
1563                ]));
1564            }
1565            rty::BinOp::Or => {
1566                return Ok(fixpoint::Expr::Or(vec![
1567                    self.expr_to_fixpoint(e1, scx)?,
1568                    self.expr_to_fixpoint(e2, scx)?,
1569                ]));
1570            }
1571            rty::BinOp::Imp => {
1572                return Ok(fixpoint::Expr::Imp(Box::new([
1573                    self.expr_to_fixpoint(e1, scx)?,
1574                    self.expr_to_fixpoint(e2, scx)?,
1575                ])));
1576            }
1577            rty::BinOp::Iff => {
1578                return Ok(fixpoint::Expr::Iff(Box::new([
1579                    self.expr_to_fixpoint(e1, scx)?,
1580                    self.expr_to_fixpoint(e2, scx)?,
1581                ])));
1582            }
1583            rty::BinOp::Add(rty::Sort::BitVec(_))
1584            | rty::BinOp::Sub(rty::Sort::BitVec(_))
1585            | rty::BinOp::Mul(rty::Sort::BitVec(_))
1586            | rty::BinOp::Div(rty::Sort::BitVec(_))
1587            | rty::BinOp::Mod(rty::Sort::BitVec(_))
1588            | rty::BinOp::BitAnd
1589            | rty::BinOp::BitOr
1590            | rty::BinOp::BitXor
1591            | rty::BinOp::BitShl
1592            | rty::BinOp::BitShr => {
1593                let bv_func = self.bv_op_to_fixpoint(op);
1594                return Ok(fixpoint::Expr::App(
1595                    Box::new(bv_func),
1596                    vec![self.expr_to_fixpoint(e1, scx)?, self.expr_to_fixpoint(e2, scx)?],
1597                ));
1598            }
1599            rty::BinOp::Add(_) => fixpoint::BinOp::Add,
1600            rty::BinOp::Sub(_) => fixpoint::BinOp::Sub,
1601            rty::BinOp::Mul(_) => fixpoint::BinOp::Mul,
1602            rty::BinOp::Div(_) => fixpoint::BinOp::Div,
1603            rty::BinOp::Mod(_) => fixpoint::BinOp::Mod,
1604        };
1605        Ok(fixpoint::Expr::BinaryOp(
1606            op,
1607            Box::new([self.expr_to_fixpoint(e1, scx)?, self.expr_to_fixpoint(e2, scx)?]),
1608        ))
1609    }
1610
1611    fn bin_rel_to_fixpoint(
1628        &mut self,
1629        sort: &rty::Sort,
1630        rel: fixpoint::BinRel,
1631        e1: &rty::Expr,
1632        e2: &rty::Expr,
1633        scx: &mut SortEncodingCtxt,
1634    ) -> QueryResult<fixpoint::Expr> {
1635        let e = match sort {
1636            rty::Sort::Int | rty::Sort::Real | rty::Sort::Char => {
1637                fixpoint::Expr::Atom(
1638                    rel,
1639                    Box::new([self.expr_to_fixpoint(e1, scx)?, self.expr_to_fixpoint(e2, scx)?]),
1640                )
1641            }
1642            rty::Sort::BitVec(_) => {
1643                let e1 = self.expr_to_fixpoint(e1, scx)?;
1644                let e2 = self.expr_to_fixpoint(e2, scx)?;
1645                let rel = self.bv_rel_to_fixpoint(&rel);
1646                fixpoint::Expr::App(Box::new(rel), vec![e1, e2])
1647            }
1648            rty::Sort::Tuple(sorts) => {
1649                let arity = sorts.len();
1650                self.apply_bin_rel_rec(sorts, rel, e1, e2, scx, |field| {
1651                    rty::FieldProj::Tuple { arity, field }
1652                })?
1653            }
1654            rty::Sort::App(rty::SortCtor::Adt(sort_def), args)
1655                if let Some(variant) = sort_def.opt_struct_variant() =>
1656            {
1657                let def_id = sort_def.did();
1658                let sorts = variant.field_sorts(args);
1659                self.apply_bin_rel_rec(&sorts, rel, e1, e2, scx, |field| {
1660                    rty::FieldProj::Adt { def_id, field }
1661                })?
1662            }
1663            _ => {
1664                let rel = fixpoint::Expr::Var(fixpoint::Var::UIFRel(rel));
1665                fixpoint::Expr::App(
1666                    Box::new(rel),
1667                    vec![self.expr_to_fixpoint(e1, scx)?, self.expr_to_fixpoint(e2, scx)?],
1668                )
1669            }
1670        };
1671        Ok(e)
1672    }
1673
1674    fn apply_bin_rel_rec(
1676        &mut self,
1677        sorts: &[rty::Sort],
1678        rel: fixpoint::BinRel,
1679        e1: &rty::Expr,
1680        e2: &rty::Expr,
1681        scx: &mut SortEncodingCtxt,
1682        mk_proj: impl Fn(u32) -> rty::FieldProj,
1683    ) -> QueryResult<fixpoint::Expr> {
1684        Ok(fixpoint::Expr::and(
1685            sorts
1686                .iter()
1687                .enumerate()
1688                .map(|(idx, s)| {
1689                    let proj = mk_proj(idx as u32);
1690                    let e1 = e1.proj_and_reduce(proj);
1691                    let e2 = e2.proj_and_reduce(proj);
1692                    self.bin_rel_to_fixpoint(s, rel, &e1, &e2, scx)
1693                })
1694                .try_collect()?,
1695        ))
1696    }
1697
1698    fn imm(
1699        &mut self,
1700        arg: &rty::Expr,
1701        sort: &rty::Sort,
1702        scx: &mut SortEncodingCtxt,
1703        bindings: &mut Vec<fixpoint::Bind>,
1704    ) -> QueryResult<fixpoint::Var> {
1705        let farg = self.expr_to_fixpoint(arg, scx)?;
1706        if let fixpoint::Expr::Var(var) = farg {
1709            Ok(var)
1710        } else {
1711            let fresh = self.local_var_env.fresh_name();
1712            self.local_var_env.reverse_map.insert(fresh, arg.clone());
1713            let pred = fixpoint::Expr::eq(fixpoint::Expr::Var(fresh.into()), farg);
1714            bindings.push(fixpoint::Bind {
1715                name: fresh.into(),
1716                sort: scx.sort_to_fixpoint(sort),
1717                pred: fixpoint::Pred::Expr(pred),
1718            });
1719            Ok(fresh.into())
1720        }
1721    }
1722
1723    pub fn declare_fun(&mut self, def_id: FluxDefId) -> fixpoint::Var {
1727        *self.const_env.fun_def_map.entry(def_id).or_insert_with(|| {
1728            let id = self.const_env.global_var_gen.fresh();
1729            fixpoint::Var::Global(id, Some(def_id))
1730        })
1731    }
1732
1733    fn prim_op_sort(op: &rty::BinOp, span: Span) -> rty::PolyFuncSort {
1742        match op {
1743            rty::BinOp::BitAnd
1744            | rty::BinOp::BitOr
1745            | rty::BinOp::BitXor
1746            | rty::BinOp::BitShl
1747            | rty::BinOp::BitShr => {
1748                let fsort =
1749                    rty::FuncSort::new(vec![rty::Sort::Int, rty::Sort::Int], rty::Sort::Int);
1750                rty::PolyFuncSort::new(List::empty(), fsort)
1751            }
1752            _ => span_bug!(span, "unexpected prim op: {op:?} in `prim_op_sort`"),
1753        }
1754    }
1755
1756    fn define_const_for_cast(
1757        &mut self,
1758        from: &rty::Sort,
1759        to: &rty::Sort,
1760        scx: &mut SortEncodingCtxt,
1761    ) -> fixpoint::Var {
1762        let key = ConstKey::Cast(from.clone(), to.clone());
1763        self.const_env
1764            .get_or_insert(key, |global_name| {
1765                let fsort = rty::FuncSort::new(vec![from.clone()], to.clone());
1766                let fsort = rty::PolyFuncSort::new(List::empty(), fsort);
1767                let sort = scx.func_sort_to_fixpoint(&fsort);
1768                fixpoint::ConstDecl {
1769                    name: fixpoint::Var::Global(global_name, None),
1770                    sort,
1771                    comment: Some(format!("cast uif: ({from:?}) -> {to:?}")),
1772                }
1773            })
1774            .name
1775    }
1776
1777    fn define_const_for_prim_op(
1778        &mut self,
1779        op: &rty::BinOp,
1780        scx: &mut SortEncodingCtxt,
1781    ) -> fixpoint::Var {
1782        let key = ConstKey::PrimOp(op.clone());
1783        let span = self.def_span();
1784        self.const_env
1785            .get_or_insert(key, |global_name| {
1786                let sort = scx.func_sort_to_fixpoint(&Self::prim_op_sort(op, span));
1787                fixpoint::ConstDecl {
1788                    name: fixpoint::Var::Global(global_name, None),
1789                    sort,
1790                    comment: Some(format!("prim op uif: {op:?}")),
1791                }
1792            })
1793            .name
1794    }
1795
1796    fn define_const_for_uif(
1797        &mut self,
1798        def_id: FluxDefId,
1799        scx: &mut SortEncodingCtxt,
1800    ) -> fixpoint::Var {
1801        let key = ConstKey::Uif(def_id);
1802        self.const_env
1803            .get_or_insert(key, |global_name| {
1804                let sort = scx.func_sort_to_fixpoint(&self.genv.func_sort(def_id));
1805                fixpoint::ConstDecl {
1806                    name: fixpoint::Var::Global(global_name, Some(def_id)),
1807                    sort,
1808                    comment: Some(format!("uif: {def_id:?}")),
1809                }
1810            })
1811            .name
1812    }
1813
1814    fn define_const_for_rust_const(
1815        &mut self,
1816        def_id: DefId,
1817        scx: &mut SortEncodingCtxt,
1818    ) -> fixpoint::Var {
1819        let key = ConstKey::RustConst(def_id);
1820        self.const_env
1821            .get_or_insert(key, |global_name| {
1822                let sort = self.genv.sort_of_def_id(def_id).unwrap().unwrap();
1823                fixpoint::ConstDecl {
1824                    name: fixpoint::Var::Global(global_name, None),
1825                    sort: scx.sort_to_fixpoint(&sort),
1826                    comment: Some(format!("rust const: {}", def_id_to_string(def_id))),
1827                }
1828            })
1829            .name
1830    }
1831
1832    fn define_const_for_alias_reft(
1835        &mut self,
1836        alias_reft: &rty::AliasReft,
1837        fsort: rty::FuncSort,
1838        scx: &mut SortEncodingCtxt,
1839    ) -> fixpoint::Var {
1840        let tcx = self.genv.tcx();
1841        let args = alias_reft
1842            .args
1843            .to_rustc(tcx)
1844            .truncate_to(tcx, tcx.generics_of(alias_reft.assoc_id.parent()));
1845        let key = ConstKey::Alias(alias_reft.assoc_id, args);
1846        self.const_env
1847            .get_or_insert(key, |global_name| {
1848                let comment = Some(format!("alias reft: {alias_reft:?}"));
1849                let name = fixpoint::Var::Global(global_name, None);
1850                let fsort = rty::PolyFuncSort::new(List::empty(), fsort);
1851                let sort = scx.func_sort_to_fixpoint(&fsort);
1852                fixpoint::ConstDecl { name, comment, sort }
1853            })
1854            .name
1855    }
1856
1857    fn define_const_for_lambda(
1860        &mut self,
1861        lam: &rty::Lambda,
1862        scx: &mut SortEncodingCtxt,
1863    ) -> fixpoint::Var {
1864        let key = ConstKey::Lambda(lam.clone());
1865        self.const_env
1866            .get_or_insert(key, |global_name| {
1867                let comment = Some(format!("lambda: {lam:?}"));
1868                let name = fixpoint::Var::Global(global_name, None);
1869                let sort = scx.func_sort_to_fixpoint(&lam.fsort().to_poly());
1870                fixpoint::ConstDecl { name, comment, sort }
1871            })
1872            .name
1873    }
1874
1875    fn assume_const_values(
1876        &mut self,
1877        mut constraint: fixpoint::Constraint,
1878        scx: &mut SortEncodingCtxt,
1879    ) -> QueryResult<fixpoint::Constraint> {
1880        let mut idx = 0;
1883        while let Some((key, const_)) = self.const_env.const_map.get_index(idx) {
1884            idx += 1;
1885
1886            let ConstKey::RustConst(def_id) = key else { continue };
1887            let info = self.genv.constant_info(def_id)?;
1888            match info {
1889                rty::ConstantInfo::Uninterpreted => {}
1890                rty::ConstantInfo::Interpreted(val, _) => {
1891                    let e1 = fixpoint::Expr::Var(const_.name);
1892                    let e2 = self.expr_to_fixpoint(&val, scx)?;
1893                    let pred = fixpoint::Pred::Expr(e1.eq(e2));
1894                    constraint = fixpoint::Constraint::ForAll(
1895                        fixpoint::Bind {
1896                            name: fixpoint::Var::Underscore,
1897                            sort: fixpoint::Sort::Int,
1898                            pred,
1899                        },
1900                        Box::new(constraint),
1901                    );
1902                }
1903            }
1904        }
1905        Ok(constraint)
1906    }
1907
1908    fn qualifiers_for(
1909        &mut self,
1910        def_id: LocalDefId,
1911        scx: &mut SortEncodingCtxt,
1912    ) -> QueryResult<Vec<fixpoint::Qualifier>> {
1913        self.genv
1914            .qualifiers_for(def_id)?
1915            .map(|qual| self.qualifier_to_fixpoint(qual, scx))
1916            .try_collect()
1917    }
1918
1919    fn define_funs(
1920        &mut self,
1921        def_id: MaybeExternId,
1922        scx: &mut SortEncodingCtxt,
1923    ) -> QueryResult<(Vec<fixpoint::FunDef>, Vec<fixpoint::ConstDecl>)> {
1924        let reveals: UnordSet<FluxDefId> = self
1925            .genv
1926            .reveals_for(def_id.local_id())
1927            .iter()
1928            .copied()
1929            .collect();
1930        let proven_externally = self.genv.proven_externally(def_id.local_id());
1931        let mut consts = vec![];
1932        let mut defs = vec![];
1933
1934        let mut idx = 0;
1937        while let Some((&did, _)) = self.const_env.fun_def_map.get_index(idx) {
1938            idx += 1;
1939
1940            let info = self.genv.normalized_info(did);
1941            let revealed = reveals.contains(&did);
1942            if info.hide && !revealed && !proven_externally {
1943                consts.push(self.fun_decl_to_fixpoint(did, scx));
1944            } else {
1945                defs.push((info.rank, self.fun_def_to_fixpoint(did, scx)?));
1946            };
1947        }
1948
1949        let defs = defs
1951            .into_iter()
1952            .sorted_by_key(|(rank, _)| *rank)
1953            .map(|(_, def)| def)
1954            .collect();
1955
1956        Ok((defs, consts))
1957    }
1958
1959    pub fn fun_decl_to_fixpoint(
1960        &mut self,
1961        def_id: FluxDefId,
1962        scx: &mut SortEncodingCtxt,
1963    ) -> fixpoint::ConstDecl {
1964        let name = self.const_env.fun_def_map[&def_id];
1965        let sort = scx.func_sort_to_fixpoint(&self.genv.func_sort(def_id));
1966        fixpoint::ConstDecl { name, sort, comment: Some(format!("flux def: {def_id:?}")) }
1967    }
1968
1969    pub fn fun_def_to_fixpoint(
1970        &mut self,
1971        def_id: FluxDefId,
1972        scx: &mut SortEncodingCtxt,
1973    ) -> QueryResult<fixpoint::FunDef> {
1974        let name = *self.const_env.fun_def_map.get(&def_id).unwrap();
1975        let info = self.genv.normalized_info(def_id);
1976        let out = scx.sort_to_fixpoint(self.genv.func_sort(def_id).expect_mono().output());
1977        let (args, body) = self.body_to_fixpoint(&info.body, scx)?;
1978        Ok(fixpoint::FunDef {
1979            name,
1980            args,
1981            body,
1982            out,
1983            comment: Some(format!("flux def: {def_id:?}")),
1984        })
1985    }
1986
1987    fn body_to_fixpoint(
1988        &mut self,
1989        body: &rty::Binder<rty::Expr>,
1990        scx: &mut SortEncodingCtxt,
1991    ) -> QueryResult<(Vec<(fixpoint::Var, fixpoint::Sort)>, fixpoint::Expr)> {
1992        self.local_var_env
1993            .push_layer_with_fresh_names(body.vars().len());
1994
1995        let expr = self.expr_to_fixpoint(body.as_ref().skip_binder(), scx)?;
1996
1997        let args: Vec<(fixpoint::Var, fixpoint::Sort)> =
1998            iter::zip(self.local_var_env.pop_layer(), body.vars())
1999                .map(|(name, var)| (name.into(), scx.sort_to_fixpoint(var.expect_sort())))
2000                .collect();
2001
2002        Ok((args, expr))
2003    }
2004
2005    fn qualifier_to_fixpoint(
2006        &mut self,
2007        qualifier: &rty::Qualifier,
2008        scx: &mut SortEncodingCtxt,
2009    ) -> QueryResult<fixpoint::Qualifier> {
2010        let (args, body) = self.body_to_fixpoint(&qualifier.body, scx)?;
2011        let name = qualifier.def_id.name().to_string();
2012        Ok(fixpoint::Qualifier { name, args, body })
2013    }
2014}
2015
2016fn mk_implies(assumption: fixpoint::Pred, cstr: fixpoint::Constraint) -> fixpoint::Constraint {
2017    fixpoint::Constraint::ForAll(
2018        fixpoint::Bind {
2019            name: fixpoint::Var::Underscore,
2020            sort: fixpoint::Sort::Int,
2021            pred: assumption,
2022        },
2023        Box::new(cstr),
2024    )
2025}
2026
2027fn parse_kvid(kvid: &str) -> fixpoint::KVid {
2028    if kvid.starts_with("k")
2029        && let Some(kvid) = kvid[1..].parse::<u32>().ok()
2030    {
2031        fixpoint::KVid::from_u32(kvid)
2032    } else {
2033        tracked_span_bug!("unexpected kvar name {kvid}")
2034    }
2035}
2036
2037fn parse_local_var(name: &str) -> Option<fixpoint::Var> {
2038    if let Some(rest) = name.strip_prefix('a')
2039        && let Ok(idx) = rest.parse::<u32>()
2040    {
2041        return Some(fixpoint::Var::Local(fixpoint::LocalVar::from(idx)));
2042    }
2043    None
2044}
2045
2046fn parse_global_var(name: &str) -> Option<fixpoint::Var> {
2047    if let Some(rest) = name.strip_prefix('c')
2048        && let Ok(idx) = rest.parse::<u32>()
2049    {
2050        return Some(fixpoint::Var::Global(fixpoint::GlobalVar::from(idx), None));
2051    }
2052    if let Some(rest) = name.strip_prefix("f$")
2054        && let parts = rest.split('$').collect::<Vec<_>>()
2055        && parts.len() == 2
2056        && let Ok(global_idx) = parts[1].parse::<u32>()
2057    {
2058        return Some(fixpoint::Var::Global(fixpoint::GlobalVar::from(global_idx), None));
2059    }
2060    None
2061}
2062
2063fn parse_param(name: &str) -> Option<fixpoint::Var> {
2064    if let Some(rest) = name.strip_prefix("reftgen$")
2065        && let parts = rest.split('$').collect::<Vec<_>>()
2066        && parts.len() == 2
2067        && let Ok(index) = parts[1].parse::<u32>()
2068    {
2069        let name = Symbol::intern(parts[0]);
2070        let param = EarlyReftParam { index, name };
2071        return Some(fixpoint::Var::Param(param));
2072    }
2073    None
2074}
2075
2076fn parse_data_proj(name: &str) -> Option<fixpoint::Var> {
2077    if let Some(rest) = name.strip_prefix("fld")
2078        && let parts = rest.split('$').collect::<Vec<_>>()
2079        && parts.len() == 2
2080        && let Ok(adt_id) = parts[0].parse::<u32>()
2081        && let Ok(field) = parts[1].parse::<u32>()
2082    {
2083        let adt_id = fixpoint::AdtId::from(adt_id);
2084        return Some(fixpoint::Var::DataProj { adt_id, field });
2085    }
2086    None
2087}
2088
2089fn parse_data_ctor(name: &str) -> Option<fixpoint::Var> {
2090    if let Some(rest) = name.strip_prefix("mkadt")
2091        && let parts = rest.split('$').collect::<Vec<_>>()
2092        && parts.len() == 2
2093        && let Ok(adt_id) = parts[0].parse::<u32>()
2094        && let Ok(variant_idx) = parts[1].parse::<u32>()
2095    {
2096        let adt_id = fixpoint::AdtId::from(adt_id);
2097        let variant_idx = VariantIdx::from(variant_idx);
2098        return Some(fixpoint::Var::DataCtor(adt_id, variant_idx));
2099    }
2100    None
2101}
2102
2103struct SexpParseCtxt;
2104
2105impl FromSexp<FixpointTypes> for SexpParseCtxt {
2106    fn kvar(&self, name: &str) -> Result<fixpoint::KVid, ParseError> {
2107        bug!("TODO: SexpParse: kvar: {name}")
2108    }
2109
2110    fn string(&self, s: &str) -> Result<fixpoint::SymStr, ParseError> {
2111        Ok(fixpoint::SymStr(Symbol::intern(s)))
2112    }
2113
2114    fn var(&self, name: &str) -> Result<fixpoint::Var, ParseError> {
2115        if let Some(var) = parse_local_var(name) {
2116            return Ok(var);
2117        }
2118        if let Some(var) = parse_global_var(name) {
2119            return Ok(var);
2120        }
2121        if let Some(var) = parse_param(name) {
2122            return Ok(var);
2123        }
2124        if let Some(var) = parse_data_proj(name) {
2125            return Ok(var);
2126        }
2127        if let Some(var) = parse_data_ctor(name) {
2128            return Ok(var);
2129        }
2130        Err(ParseError::err(format!("Unknown variable: {name}")))
2131    }
2132
2133    fn sort(&self, name: &str) -> Result<fixpoint::DataSort, ParseError> {
2134        if let Some(idx) = name.strip_prefix("Adt")
2135            && let Ok(adt_id) = idx.parse::<u32>()
2136        {
2137            return Ok(fixpoint::DataSort::Adt(fixpoint::AdtId::from(adt_id)));
2138        }
2139        Err(ParseError::err(format!("Unknown sort: {name}")))
2140    }
2141}