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