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