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