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 const_deps = ConstDeps { interpreted };
844 (const_deps, cstr)
845 }
846
847 pub fn generate_lean_files(
848 self,
849 def_id: MaybeExternId,
850 task: fixpoint::Task,
851 kvar_solutions: KVarSolutions,
852 ) -> QueryResult {
853 let opaque_sorts = self.scx.opaque_sorts_to_fixpoint(self.genv);
855 let (const_deps, constraint) = self.compute_const_deps(task.constants, task.constraint);
856 let sort_deps =
857 SortDeps { opaque_sorts, data_decls: task.data_decls, adt_map: self.scx.adt_sorts };
858
859 LeanEncoder::encode(
860 self.genv,
861 def_id,
862 self.ecx.local_var_env.pretty_var_map,
863 sort_deps,
864 task.define_funs,
865 const_deps,
866 task.kvars,
867 constraint,
868 kvar_solutions,
869 )
870 .map_err(|err| query_bug!("could not encode constraint: {err:?}"))
871 }
872
873 fn run_task_with_cache(
874 genv: GlobalEnv,
875 task: &fixpoint::Task,
876 def_id: DefId,
877 kind: FixpointQueryKind,
878 cache: &mut FixQueryCache,
879 ) -> VerificationResult<TagIdx> {
880 let key = kind.task_key(genv.tcx(), def_id);
881
882 let hash = task.hash_with_default();
883
884 if config::is_cache_enabled()
885 && let Some(result) = cache.lookup(&key, hash)
886 {
887 metrics::incr_metric_if(kind.is_body(), Metric::FnCached);
888 return result.clone();
889 }
890 let result = metrics::time_it(TimingKind::FixpointQuery(def_id, kind), || {
891 task.run()
892 .unwrap_or_else(|err| tracked_span_bug!("failed to run fixpoint: {err}"))
893 });
894
895 if config::is_cache_enabled() {
896 cache.insert(key, hash, result.clone());
897 }
898
899 result
900 }
901
902 fn tag_idx(&mut self, tag: Tag) -> TagIdx
903 where
904 Tag: std::fmt::Debug,
905 {
906 *self.tags_inv.entry(tag).or_insert_with(|| {
907 let idx = self.tags.push(tag);
908 self.comments.push(format!("Tag {idx}: {tag:?}"));
909 idx
910 })
911 }
912
913 pub(crate) fn with_early_param(&mut self, param: &EarlyReftParam) {
914 self.ecx
915 .local_var_env
916 .pretty_var_map
917 .set(PrettyVar::Param(*param), Some(param.name));
918 }
919
920 pub(crate) fn with_name_map<R>(
921 &mut self,
922 name: rty::Name,
923 provenance: NameProvenance,
924 f: impl FnOnce(&mut Self, fixpoint::LocalVar) -> R,
925 ) -> R {
926 let fresh = self.ecx.local_var_env.insert_fvar_map(name, provenance);
927 let r = f(self, fresh);
928 self.ecx.local_var_env.remove_fvar_map(name);
929 r
930 }
931
932 pub(crate) fn sort_to_fixpoint(&mut self, sort: &rty::Sort) -> fixpoint::Sort {
933 self.scx.sort_to_fixpoint(sort)
934 }
935
936 pub(crate) fn var_to_fixpoint(&self, var: &rty::Var) -> fixpoint::Var {
937 self.ecx.var_to_fixpoint(var)
938 }
939
940 pub(crate) fn head_to_fixpoint(
945 &mut self,
946 expr: &rty::Expr,
947 mk_tag: impl Fn(Option<ESpan>) -> Tag + Copy,
948 ) -> QueryResult<fixpoint::Constraint>
949 where
950 Tag: std::fmt::Debug,
951 {
952 match expr.kind() {
953 rty::ExprKind::BinaryOp(rty::BinOp::And, ..) => {
954 let cstrs = expr
956 .flatten_conjs()
957 .into_iter()
958 .map(|e| self.head_to_fixpoint(e, mk_tag))
959 .try_collect()?;
960 Ok(fixpoint::Constraint::conj(cstrs))
961 }
962 rty::ExprKind::BinaryOp(rty::BinOp::Imp, e1, e2) => {
963 let (bindings, assumption) = self.assumption_to_fixpoint(e1)?;
964 let cstr = self.head_to_fixpoint(e2, mk_tag)?;
965 Ok(fixpoint::Constraint::foralls(bindings, mk_implies(assumption, cstr)))
966 }
967 rty::ExprKind::KVar(kvar) => {
968 let mut bindings = vec![];
969 let pred = self.kvar_to_fixpoint(kvar, &mut bindings)?;
970 Ok(fixpoint::Constraint::foralls(bindings, fixpoint::Constraint::Pred(pred, None)))
971 }
972 rty::ExprKind::ForAll(pred) => {
973 self.ecx
974 .local_var_env
975 .push_layer_with_fresh_names(pred.vars().len());
976 let cstr = self.head_to_fixpoint(pred.as_ref().skip_binder(), mk_tag)?;
977 let vars = self.ecx.local_var_env.pop_layer();
978
979 let bindings = iter::zip(vars, pred.vars())
980 .map(|(var, kind)| {
981 fixpoint::Bind {
982 name: var.into(),
983 sort: self.scx.sort_to_fixpoint(kind.expect_sort()),
984 pred: fixpoint::Pred::TRUE,
985 }
986 })
987 .collect_vec();
988
989 Ok(fixpoint::Constraint::foralls(bindings, cstr))
990 }
991 _ => {
992 let tag_idx = self.tag_idx(mk_tag(expr.span()));
993 let pred = fixpoint::Pred::Expr(self.ecx.expr_to_fixpoint(expr, &mut self.scx)?);
994 Ok(fixpoint::Constraint::Pred(pred, Some(tag_idx)))
995 }
996 }
997 }
998
999 pub(crate) fn assumption_to_fixpoint(
1004 &mut self,
1005 pred: &rty::Expr,
1006 ) -> QueryResult<(Vec<fixpoint::Bind>, fixpoint::Pred)> {
1007 let mut bindings = vec![];
1008 let mut preds = vec![];
1009 self.assumption_to_fixpoint_aux(pred, &mut bindings, &mut preds)?;
1010 Ok((bindings, fixpoint::Pred::and(preds)))
1011 }
1012
1013 fn assumption_to_fixpoint_aux(
1015 &mut self,
1016 expr: &rty::Expr,
1017 bindings: &mut Vec<fixpoint::Bind>,
1018 preds: &mut Vec<fixpoint::Pred>,
1019 ) -> QueryResult {
1020 match expr.kind() {
1021 rty::ExprKind::BinaryOp(rty::BinOp::And, e1, e2) => {
1022 self.assumption_to_fixpoint_aux(e1, bindings, preds)?;
1023 self.assumption_to_fixpoint_aux(e2, bindings, preds)?;
1024 }
1025 rty::ExprKind::KVar(kvar) => {
1026 preds.push(self.kvar_to_fixpoint(kvar, bindings)?);
1027 }
1028 rty::ExprKind::ForAll(_) => {
1029 preds.push(fixpoint::Pred::TRUE);
1035 }
1036 _ => {
1037 preds.push(fixpoint::Pred::Expr(self.ecx.expr_to_fixpoint(expr, &mut self.scx)?));
1038 }
1039 }
1040 Ok(())
1041 }
1042
1043 fn kvar_to_fixpoint(
1044 &mut self,
1045 kvar: &rty::KVar,
1046 bindings: &mut Vec<fixpoint::Bind>,
1047 ) -> QueryResult<fixpoint::Pred> {
1048 let decl = self.kvars.get(kvar.kvid);
1049 let kvids = self.kcx.declare(kvar.kvid, decl, &self.ecx.backend);
1050
1051 let all_args = self.ecx.exprs_to_fixpoint(&kvar.args, &mut self.scx)?;
1052
1053 if all_args.is_empty() {
1057 let fresh = self.ecx.local_var_env.fresh_name();
1058 let var = fixpoint::Var::Local(fresh);
1059 bindings.push(fixpoint::Bind {
1060 name: fresh.into(),
1061 sort: fixpoint::Sort::Int,
1062 pred: fixpoint::Pred::Expr(fixpoint::Expr::eq(
1063 fixpoint::Expr::Var(var),
1064 fixpoint::Expr::int(0),
1065 )),
1066 });
1067 return Ok(fixpoint::Pred::KVar(kvids.start, vec![fixpoint::Expr::Var(var)]));
1068 }
1069
1070 let kvars = kvids
1071 .enumerate()
1072 .map(|(i, kvid)| {
1073 let args = all_args[i..].to_vec();
1074 fixpoint::Pred::KVar(kvid, args)
1075 })
1076 .collect_vec();
1077
1078 Ok(fixpoint::Pred::And(kvars))
1079 }
1080}
1081
1082fn const_to_fixpoint(cst: rty::Constant) -> fixpoint::Expr {
1083 match cst {
1084 rty::Constant::Int(i) => {
1085 if i.is_negative() {
1086 fixpoint::Expr::Neg(Box::new(fixpoint::Constant::Numeral(i.abs()).into()))
1087 } else {
1088 fixpoint::Constant::Numeral(i.abs()).into()
1089 }
1090 }
1091 rty::Constant::Real(r) => fixpoint::Constant::Real(r.0).into(),
1092 rty::Constant::Bool(b) => fixpoint::Constant::Boolean(b).into(),
1093 rty::Constant::Char(c) => fixpoint::Constant::Numeral(u128::from(c)).into(),
1094 rty::Constant::Str(s) => fixpoint::Constant::String(fixpoint::SymStr(s)).into(),
1095 rty::Constant::BitVec(i, size) => fixpoint::Constant::BitVec(i, size).into(),
1096 }
1097}
1098
1099#[derive(Default)]
1104struct KVarEncodingCtxt {
1105 ranges: FxIndexMap<rty::KVid, Range<fixpoint::KVid>>,
1108}
1109
1110impl KVarEncodingCtxt {
1111 fn declare(
1114 &mut self,
1115 kvid: rty::KVid,
1116 decl: &KVarDecl,
1117 backend: &Backend,
1118 ) -> Range<fixpoint::KVid> {
1119 let start = self
1121 .ranges
1122 .last()
1123 .map_or(fixpoint::KVid::from_u32(0), |(_, r)| r.end);
1124
1125 self.ranges
1126 .entry(kvid)
1127 .or_insert_with(|| {
1128 let single_encoding = matches!(decl.encoding, KVarEncoding::Single)
1129 || matches!(backend, Backend::Lean);
1130 if single_encoding {
1131 start..start + 1
1132 } else {
1133 let n = usize::max(decl.self_args, 1);
1134 start..start + n
1135 }
1136 })
1137 .clone()
1138 }
1139
1140 fn encode_kvars(&self, kvars: &KVarGen, scx: &mut SortEncodingCtxt) -> Vec<fixpoint::KVarDecl> {
1141 self.ranges
1142 .iter()
1143 .flat_map(|(orig, range)| {
1144 let mut all_sorts = kvars
1145 .get(*orig)
1146 .sorts
1147 .iter()
1148 .map(|s| scx.sort_to_fixpoint(s))
1149 .collect_vec();
1150
1151 if all_sorts.is_empty() {
1153 all_sorts = vec![fixpoint::Sort::Int];
1154 }
1155
1156 range.clone().enumerate().map(move |(i, kvid)| {
1157 let sorts = all_sorts[i..].to_vec();
1158 fixpoint::KVarDecl::new(kvid, sorts, format!("orig: {:?}", orig))
1159 })
1160 })
1161 .collect()
1162 }
1163
1164 fn group_kvar_solution(
1171 &self,
1172 mut items: Vec<(fixpoint::KVid, rty::Binder<rty::Expr>)>,
1173 ) -> FxIndexMap<rty::KVid, rty::Binder<rty::Expr>> {
1174 let mut map = FxIndexMap::default();
1175
1176 items.sort_by_key(|(kvid, _)| *kvid);
1177 items.reverse();
1178
1179 for (orig, range) in &self.ranges {
1180 let mut preds = vec![];
1181 while let Some((_, t)) = items.pop_if(|(k, _)| range.contains(k)) {
1182 preds.push(t);
1183 }
1184 if preds.len() == range.end.as_usize() - range.start.as_usize() {
1186 let vars = preds[0].vars().clone();
1187 let conj = rty::Expr::and_from_iter(
1188 preds
1189 .into_iter()
1190 .enumerate()
1191 .map(|(i, e)| e.skip_binder().shift_horizontally(i)),
1192 );
1193 map.insert(*orig, rty::Binder::bind_with_vars(conj, vars));
1194 }
1195 }
1196 map
1197 }
1198}
1199
1200struct LocalVarEnv {
1202 local_var_gen: IndexGen<fixpoint::LocalVar>,
1203 fvars: UnordMap<rty::Name, fixpoint::LocalVar>,
1204 layers: Vec<Vec<fixpoint::LocalVar>>,
1206 reverse_map: UnordMap<fixpoint::LocalVar, rty::Expr>,
1211 pretty_var_map: PrettyMap<fixpoint::LocalVar>,
1212}
1213
1214impl LocalVarEnv {
1215 fn new() -> Self {
1216 Self {
1217 local_var_gen: IndexGen::new(),
1218 fvars: Default::default(),
1219 layers: Vec::new(),
1220 reverse_map: Default::default(),
1221 pretty_var_map: PrettyMap::new(),
1222 }
1223 }
1224
1225 fn fresh_name(&mut self) -> fixpoint::LocalVar {
1228 self.local_var_gen.fresh()
1229 }
1230
1231 fn insert_fvar_map(
1232 &mut self,
1233 name: rty::Name,
1234 provenance: NameProvenance,
1235 ) -> fixpoint::LocalVar {
1236 let fresh = self.fresh_name();
1237 self.fvars.insert(name, fresh);
1238 self.reverse_map.insert(fresh, rty::Expr::fvar(name));
1239 self.pretty_var_map
1240 .set(PrettyVar::Local(fresh), provenance.opt_symbol());
1241 fresh
1242 }
1243
1244 fn remove_fvar_map(&mut self, name: rty::Name) {
1245 self.fvars.remove(&name);
1246 }
1247
1248 fn push_layer_with_fresh_names(&mut self, count: usize) {
1250 let layer = (0..count).map(|_| self.fresh_name()).collect();
1251 self.layers.push(layer);
1252 }
1254
1255 fn push_layer(&mut self, layer: Vec<fixpoint::LocalVar>) {
1256 self.layers.push(layer);
1257 }
1258
1259 fn pop_layer(&mut self) -> Vec<fixpoint::LocalVar> {
1260 self.layers.pop().unwrap()
1261 }
1262
1263 fn get_fvar(&self, name: rty::Name) -> Option<fixpoint::LocalVar> {
1264 self.fvars.get(&name).copied()
1265 }
1266
1267 fn get_late_bvar(&self, debruijn: DebruijnIndex, var: BoundVar) -> Option<fixpoint::LocalVar> {
1268 let depth = self.layers.len().checked_sub(debruijn.as_usize() + 1)?;
1269 self.layers[depth].get(var.as_usize()).copied()
1270 }
1271}
1272
1273pub struct KVarGen {
1274 kvars: IndexVec<rty::KVid, KVarDecl>,
1275 dummy: bool,
1280}
1281
1282impl KVarGen {
1283 pub(crate) fn new(dummy: bool) -> Self {
1284 Self { kvars: IndexVec::new(), dummy }
1285 }
1286
1287 fn get(&self, kvid: rty::KVid) -> &KVarDecl {
1288 &self.kvars[kvid]
1289 }
1290
1291 pub fn fresh(
1310 &mut self,
1311 binders: &[rty::BoundVariableKinds],
1312 scope: impl IntoIterator<Item = (rty::Var, rty::Sort)>,
1313 encoding: KVarEncoding,
1314 ) -> rty::Expr {
1315 if self.dummy {
1316 return rty::Expr::hole(rty::HoleKind::Pred);
1317 }
1318
1319 let args = itertools::chain(
1320 binders.iter().rev().enumerate().flat_map(|(level, vars)| {
1321 let debruijn = DebruijnIndex::from_usize(level);
1322 vars.iter()
1323 .cloned()
1324 .enumerate()
1325 .flat_map(move |(idx, var)| {
1326 if let rty::BoundVariableKind::Refine(sort, _, kind) = var {
1327 let br = rty::BoundReft { var: BoundVar::from_usize(idx), kind };
1328 Some((rty::Var::Bound(debruijn, br), sort))
1329 } else {
1330 None
1331 }
1332 })
1333 }),
1334 scope,
1335 );
1336 let [.., last] = binders else {
1337 return self.fresh_inner(0, [], encoding);
1338 };
1339 let num_self_args = last
1340 .iter()
1341 .filter(|var| matches!(var, rty::BoundVariableKind::Refine(..)))
1342 .count();
1343 self.fresh_inner(num_self_args, args, encoding)
1344 }
1345
1346 fn fresh_inner<A>(&mut self, self_args: usize, args: A, encoding: KVarEncoding) -> rty::Expr
1347 where
1348 A: IntoIterator<Item = (rty::Var, rty::Sort)>,
1349 {
1350 let mut sorts = vec![];
1352 let mut exprs = vec![];
1353
1354 let mut flattened_self_args = 0;
1355 for (i, (var, sort)) in args.into_iter().enumerate() {
1356 let is_self_arg = i < self_args;
1357 let var = var.to_expr();
1358 sort.walk(|sort, proj| {
1359 if !matches!(sort, rty::Sort::Loc) {
1360 flattened_self_args += is_self_arg as usize;
1361 sorts.push(sort.clone());
1362 exprs.push(rty::Expr::field_projs(&var, proj));
1363 }
1364 });
1365 }
1366
1367 let kvid = self
1368 .kvars
1369 .push(KVarDecl { self_args: flattened_self_args, sorts, encoding });
1370
1371 let kvar = rty::KVar::new(kvid, flattened_self_args, exprs);
1372 rty::Expr::kvar(kvar)
1373 }
1374}
1375
1376#[derive(Clone)]
1377struct KVarDecl {
1378 self_args: usize,
1379 sorts: Vec<rty::Sort>,
1380 encoding: KVarEncoding,
1381}
1382
1383#[derive(Clone, Copy)]
1385pub enum KVarEncoding {
1386 Single,
1389 Conj,
1393}
1394
1395impl std::fmt::Display for TagIdx {
1396 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1397 write!(f, "{}", self.as_u32())
1398 }
1399}
1400
1401impl std::str::FromStr for TagIdx {
1402 type Err = std::num::ParseIntError;
1403
1404 fn from_str(s: &str) -> Result<Self, Self::Err> {
1405 Ok(Self::from_u32(s.parse()?))
1406 }
1407}
1408
1409#[derive(Default)]
1410struct ConstEnv<'tcx> {
1411 const_map: ConstMap<'tcx>,
1412 const_map_rev: HashMap<fixpoint::GlobalVar, ConstKey<'tcx>>,
1413 global_var_gen: IndexGen<fixpoint::GlobalVar>,
1414 fun_decl_map: FunDeclMap,
1415}
1416
1417impl<'tcx> ConstEnv<'tcx> {
1418 fn get_or_insert(
1419 &mut self,
1420 key: ConstKey<'tcx>,
1421 make_const_decl: impl FnOnce(fixpoint::GlobalVar) -> fixpoint::ConstDecl,
1423 ) -> &fixpoint::ConstDecl {
1424 self.const_map.entry(key.clone()).or_insert_with(|| {
1425 let global_name = self.global_var_gen.fresh();
1426 self.const_map_rev.insert(global_name, key);
1427 make_const_decl(global_name)
1428 })
1429 }
1430}
1431
1432pub struct ExprEncodingCtxt<'genv, 'tcx> {
1433 genv: GlobalEnv<'genv, 'tcx>,
1434 local_var_env: LocalVarEnv,
1435 const_env: ConstEnv<'tcx>,
1436 errors: Errors<'genv>,
1437 def_id: Option<MaybeExternId>,
1440 infcx: rustc_infer::infer::InferCtxt<'tcx>,
1441 backend: Backend,
1442}
1443
1444pub struct KVarSolutions {
1445 pub cut_solutions: FxIndexMap<fixpoint::KVid, ClosedSolution>,
1446 pub non_cut_solutions: FxIndexMap<fixpoint::KVid, ClosedSolution>,
1447}
1448
1449impl KVarSolutions {
1450 pub(crate) fn closed_solutions(
1451 variable_sorts: IndexMap<fixpoint::Var, fixpoint::Sort>,
1452 cut_solutions: FxIndexMap<fixpoint::KVid, FixpointSolution>,
1453 non_cut_solutions: FxIndexMap<fixpoint::KVid, FixpointSolution>,
1454 ) -> Self {
1455 let closed_cut_solutions = cut_solutions
1456 .into_iter()
1457 .map(|(k, v)| (k, (vec![], v)))
1458 .collect();
1459
1460 let mut closed_non_cut_solutions: FxIndexMap<fixpoint::KVid, ClosedSolution> =
1461 FxIndexMap::default();
1462 for (kvid, mut solution) in non_cut_solutions {
1463 let bound_vars: HashSet<&_> = solution.0.iter().map(|(var, _)| var).collect();
1464 let vars = solution.1.free_vars();
1465 let free_vars = vars.iter().filter(|var| {
1466 !bound_vars.contains(var)
1467 && !matches!(
1468 var,
1469 fixpoint::Var::DataCtor(..)
1470 | fixpoint::Var::Global(..)
1471 | fixpoint::Var::DataProj { .. }
1472 )
1473 });
1474 let free_var_subs = free_vars
1475 .map(|fvar| (*fvar, variable_sorts.get(fvar).unwrap().clone()))
1476 .collect();
1477 solution.1.var_sorts_to_int();
1478 closed_non_cut_solutions.insert(kvid, (free_var_subs, solution));
1479 }
1480 KVarSolutions {
1481 cut_solutions: closed_cut_solutions,
1482 non_cut_solutions: closed_non_cut_solutions,
1483 }
1484 }
1485
1486 pub(crate) fn is_empty(&self) -> bool {
1487 self.cut_solutions.is_empty() && self.non_cut_solutions.is_empty()
1488 }
1489}
1490
1491#[derive(Debug)]
1492pub struct SortDeps {
1493 pub opaque_sorts: Vec<(FluxDefId, fixpoint::SortDecl)>,
1494 pub data_decls: Vec<fixpoint::DataDecl>,
1495 pub adt_map: FxIndexSet<DefId>,
1496}
1497
1498pub struct ConstDeps {
1499 pub interpreted: Vec<InterpretedConst>,
1500}
1501
1502impl<'genv, 'tcx> ExprEncodingCtxt<'genv, 'tcx> {
1503 pub fn new(
1504 genv: GlobalEnv<'genv, 'tcx>,
1505 def_id: Option<MaybeExternId>,
1506 backend: Backend,
1507 ) -> Self {
1508 Self {
1509 genv,
1510 local_var_env: LocalVarEnv::new(),
1511 const_env: Default::default(),
1512 errors: Errors::new(genv.sess()),
1513 def_id,
1514 infcx: genv
1515 .tcx()
1516 .infer_ctxt()
1517 .with_next_trait_solver(true)
1518 .build(TypingMode::non_body_analysis()),
1519 backend,
1520 }
1521 }
1522
1523 fn def_span(&self) -> Span {
1524 self.def_id
1525 .map_or(DUMMY_SP, |def_id| self.genv.tcx().def_span(def_id))
1526 }
1527
1528 fn var_to_fixpoint(&self, var: &rty::Var) -> fixpoint::Var {
1529 match var {
1530 rty::Var::Free(name) => {
1531 self.local_var_env
1532 .get_fvar(*name)
1533 .unwrap_or_else(|| {
1534 span_bug!(self.def_span(), "no entry found for name: `{name:?}`")
1535 })
1536 .into()
1537 }
1538 rty::Var::Bound(debruijn, breft) => {
1539 self.local_var_env
1540 .get_late_bvar(*debruijn, breft.var)
1541 .unwrap_or_else(|| {
1542 span_bug!(self.def_span(), "no entry found for late bound var: `{breft:?}`")
1543 })
1544 .into()
1545 }
1546 rty::Var::ConstGeneric(param) => fixpoint::Var::ConstGeneric(*param),
1547 rty::Var::EarlyParam(param) => fixpoint::Var::Param(*param),
1548 rty::Var::EVar(_) => {
1549 span_bug!(self.def_span(), "unexpected evar: `{var:?}`")
1550 }
1551 }
1552 }
1553
1554 fn variant_to_fixpoint(
1555 &self,
1556 scx: &mut SortEncodingCtxt,
1557 enum_def_id: &DefId,
1558 idx: VariantIdx,
1559 ) -> fixpoint::Var {
1560 let adt_id = scx.declare_adt(*enum_def_id);
1561 fixpoint::Var::DataCtor(adt_id, idx)
1562 }
1563
1564 fn struct_fields_to_fixpoint(
1565 &mut self,
1566 did: &DefId,
1567 flds: &[rty::Expr],
1568 scx: &mut SortEncodingCtxt,
1569 ) -> QueryResult<fixpoint::Expr> {
1570 if let [fld] = flds {
1572 self.expr_to_fixpoint(fld, scx)
1573 } else {
1574 let adt_id = scx.declare_adt(*did);
1575 let ctor = fixpoint::Expr::Var(fixpoint::Var::DataCtor(adt_id, VariantIdx::ZERO));
1576 let args = flds
1577 .iter()
1578 .map(|fld| self.expr_to_fixpoint(fld, scx))
1579 .try_collect()?;
1580 Ok(fixpoint::Expr::App(Box::new(ctor), None, args, None))
1581 }
1582 }
1583
1584 fn fields_to_fixpoint(
1585 &mut self,
1586 flds: &[rty::Expr],
1587 scx: &mut SortEncodingCtxt,
1588 ) -> QueryResult<fixpoint::Expr> {
1589 if let [fld] = flds {
1591 self.expr_to_fixpoint(fld, scx)
1592 } else {
1593 scx.declare_tuple(flds.len());
1594 let ctor = fixpoint::Expr::Var(fixpoint::Var::TupleCtor { arity: flds.len() });
1595 let args = flds
1596 .iter()
1597 .map(|fld| self.expr_to_fixpoint(fld, scx))
1598 .try_collect()?;
1599 Ok(fixpoint::Expr::App(Box::new(ctor), None, args, None))
1600 }
1601 }
1602
1603 fn internal_func_to_fixpoint(
1604 &mut self,
1605 internal_func: &InternalFuncKind,
1606 sort_args: &[rty::SortArg],
1607 args: &[rty::Expr],
1608 scx: &mut SortEncodingCtxt,
1609 ) -> QueryResult<fixpoint::Expr> {
1610 match internal_func {
1611 InternalFuncKind::Val(op) => {
1612 let func = fixpoint::Expr::Var(self.define_const_for_prim_op(op, scx));
1613 let args = self.exprs_to_fixpoint(args, scx)?;
1614 Ok(fixpoint::Expr::App(Box::new(func), None, args, None))
1615 }
1616 InternalFuncKind::Rel(op) => {
1617 let expr = if let Some(prim_rel) = self.genv.prim_rel_for(op)? {
1618 prim_rel.body.replace_bound_refts(args)
1619 } else {
1620 rty::Expr::tt()
1621 };
1622 self.expr_to_fixpoint(&expr, scx)
1623 }
1624 InternalFuncKind::Cast => {
1625 let [rty::SortArg::Sort(from), rty::SortArg::Sort(to)] = &sort_args else {
1626 span_bug!(self.def_span(), "unexpected cast")
1627 };
1628 match from.cast_kind(to) {
1629 rty::CastKind::Identity => self.expr_to_fixpoint(&args[0], scx),
1630 rty::CastKind::BoolToInt => {
1631 Ok(fixpoint::Expr::IfThenElse(Box::new([
1632 self.expr_to_fixpoint(&args[0], scx)?,
1633 fixpoint::Expr::int(1),
1634 fixpoint::Expr::int(0),
1635 ])))
1636 }
1637 rty::CastKind::IntoUnit => self.expr_to_fixpoint(&rty::Expr::unit(), scx),
1638 rty::CastKind::Uninterpreted => {
1639 let func = fixpoint::Expr::Var(self.define_const_for_cast(from, to, scx));
1640 let args = self.exprs_to_fixpoint(args, scx)?;
1641 Ok(fixpoint::Expr::App(Box::new(func), None, args, None))
1642 }
1643 }
1644 }
1645 InternalFuncKind::PtrSize => {
1646 let func = fixpoint::Expr::Var(self.define_const_for_ptr_size(scx));
1647 let args = self.exprs_to_fixpoint(args, scx)?;
1648 Ok(fixpoint::Expr::App(Box::new(func), None, args, None))
1649 }
1650 }
1651 }
1652
1653 fn structurally_normalize_expr(&self, expr: &rty::Expr) -> QueryResult<rty::Expr> {
1654 if let Some(def_id) = self.def_id {
1655 structurally_normalize_expr(self.genv, def_id.resolved_id(), &self.infcx, expr)
1656 } else {
1657 Ok(expr.clone())
1658 }
1659 }
1660
1661 fn expr_to_fixpoint(
1662 &mut self,
1663 expr: &rty::Expr,
1664 scx: &mut SortEncodingCtxt,
1665 ) -> QueryResult<fixpoint::Expr> {
1666 let expr = self.structurally_normalize_expr(expr)?;
1667 let e = match expr.kind() {
1668 rty::ExprKind::Var(var) => fixpoint::Expr::Var(self.var_to_fixpoint(var)),
1669 rty::ExprKind::Constant(c) => const_to_fixpoint(*c),
1670 rty::ExprKind::BinaryOp(op, e1, e2) => self.bin_op_to_fixpoint(op, e1, e2, scx)?,
1671 rty::ExprKind::UnaryOp(op, e) => self.un_op_to_fixpoint(*op, e, scx)?,
1672 rty::ExprKind::FieldProj(e, proj) => self.proj_to_fixpoint(e, *proj, scx)?,
1673 rty::ExprKind::Tuple(flds) => self.fields_to_fixpoint(flds, scx)?,
1674 rty::ExprKind::Ctor(rty::Ctor::Struct(did), flds) => {
1675 self.struct_fields_to_fixpoint(did, flds, scx)?
1676 }
1677 rty::ExprKind::IsCtor(def_id, variant_idx, e) => {
1678 let ctor = self.variant_to_fixpoint(scx, def_id, *variant_idx);
1679 let e = self.expr_to_fixpoint(e, scx)?;
1680 fixpoint::Expr::IsCtor(ctor, Box::new(e))
1681 }
1682 rty::ExprKind::Ctor(rty::Ctor::Enum(did, idx), args) => {
1683 let ctor = self.variant_to_fixpoint(scx, did, *idx);
1684 let args = self.exprs_to_fixpoint(args, scx)?;
1685 fixpoint::Expr::App(Box::new(fixpoint::Expr::Var(ctor)), None, args, None)
1686 }
1687 rty::ExprKind::ConstDefId(did) => {
1688 let var = self.define_const_for_rust_const(*did, scx);
1689 fixpoint::Expr::Var(var)
1690 }
1691 rty::ExprKind::App(func, sort_args, args) => {
1692 if let rty::ExprKind::InternalFunc(func) = func.kind() {
1693 self.internal_func_to_fixpoint(func, sort_args, args, scx)?
1694 } else {
1695 let func = self.expr_to_fixpoint(func, scx)?;
1696 let sort_args = self.sort_args_to_fixpoint(sort_args, scx);
1697 let args = self.exprs_to_fixpoint(args, scx)?;
1698 fixpoint::Expr::App(Box::new(func), Some(sort_args), args, None)
1699 }
1700 }
1701 rty::ExprKind::IfThenElse(p, e1, e2) => {
1702 fixpoint::Expr::IfThenElse(Box::new([
1703 self.expr_to_fixpoint(p, scx)?,
1704 self.expr_to_fixpoint(e1, scx)?,
1705 self.expr_to_fixpoint(e2, scx)?,
1706 ]))
1707 }
1708 rty::ExprKind::Alias(alias_reft, args) => {
1709 let sort = self.genv.sort_of_assoc_reft(alias_reft.assoc_id)?;
1710 let sort = sort.instantiate_identity();
1711 let func =
1712 fixpoint::Expr::Var(self.define_const_for_alias_reft(alias_reft, sort, scx));
1713 let args = args
1714 .iter()
1715 .map(|expr| self.expr_to_fixpoint(expr, scx))
1716 .try_collect()?;
1717 fixpoint::Expr::App(Box::new(func), None, args, None)
1718 }
1719 rty::ExprKind::Abs(lam) => {
1720 let var = self.define_const_for_lambda(lam, scx);
1721 fixpoint::Expr::Var(var)
1722 }
1723 rty::ExprKind::Let(init, body) => {
1724 debug_assert_eq!(body.vars().len(), 1);
1725 let init = self.expr_to_fixpoint(init, scx)?;
1726
1727 self.local_var_env.push_layer_with_fresh_names(1);
1728 let body = self.expr_to_fixpoint(body.skip_binder_ref(), scx)?;
1729 let vars = self.local_var_env.pop_layer();
1730
1731 fixpoint::Expr::Let(vars[0].into(), Box::new([init, body]))
1732 }
1733 rty::ExprKind::GlobalFunc(SpecFuncKind::Thy(itf)) => fixpoint::Expr::ThyFunc(*itf),
1734 rty::ExprKind::GlobalFunc(SpecFuncKind::Def(def_id)) => {
1735 fixpoint::Expr::Var(self.declare_fun(*def_id))
1736 }
1737 rty::ExprKind::Exists(expr) => {
1738 let expr = self.body_to_fixpoint(expr, scx)?;
1739 fixpoint::Expr::Exists(expr.0, Box::new(expr.1))
1740 }
1741 rty::ExprKind::Hole(..)
1742 | rty::ExprKind::KVar(_)
1743 | rty::ExprKind::Local(_)
1744 | rty::ExprKind::PathProj(..)
1745 | rty::ExprKind::ForAll(_)
1746 | rty::ExprKind::InternalFunc(_) => {
1747 span_bug!(self.def_span(), "unexpected expr: `{expr:?}`")
1748 }
1749 rty::ExprKind::BoundedQuant(kind, rng, body) => {
1750 let exprs = (rng.start..rng.end).map(|i| {
1751 let arg = rty::Expr::constant(rty::Constant::from(i));
1752 body.replace_bound_reft(&arg)
1753 });
1754 let expr = match kind {
1755 flux_middle::fhir::QuantKind::Forall => rty::Expr::and_from_iter(exprs),
1756 flux_middle::fhir::QuantKind::Exists => rty::Expr::or_from_iter(exprs),
1757 };
1758 self.expr_to_fixpoint(&expr, scx)?
1759 }
1760 };
1761 Ok(e)
1762 }
1763
1764 fn sort_args_to_fixpoint(
1765 &mut self,
1766 sort_args: &[rty::SortArg],
1767 scx: &mut SortEncodingCtxt,
1768 ) -> Vec<fixpoint::Sort> {
1769 sort_args
1770 .iter()
1771 .map(|s_arg| self.sort_arg_to_fixpoint(s_arg, scx))
1772 .collect()
1773 }
1774
1775 fn sort_arg_to_fixpoint(
1776 &mut self,
1777 sort_arg: &rty::SortArg,
1778 scx: &mut SortEncodingCtxt,
1779 ) -> fixpoint::Sort {
1780 match sort_arg {
1781 rty::SortArg::Sort(sort) => scx.sort_to_fixpoint(sort),
1782 rty::SortArg::BvSize(sz) => bv_size_to_fixpoint(*sz),
1783 }
1784 }
1785
1786 fn exprs_to_fixpoint<'b>(
1787 &mut self,
1788 exprs: impl IntoIterator<Item = &'b rty::Expr>,
1789 scx: &mut SortEncodingCtxt,
1790 ) -> QueryResult<Vec<fixpoint::Expr>> {
1791 exprs
1792 .into_iter()
1793 .map(|e| self.expr_to_fixpoint(e, scx))
1794 .try_collect()
1795 }
1796
1797 fn proj_to_fixpoint(
1798 &mut self,
1799 e: &rty::Expr,
1800 proj: rty::FieldProj,
1801 scx: &mut SortEncodingCtxt,
1802 ) -> QueryResult<fixpoint::Expr> {
1803 let arity = proj.arity(self.genv)?;
1804 if arity == 1 {
1806 self.expr_to_fixpoint(e, scx)
1807 } else {
1808 let proj = match proj {
1809 rty::FieldProj::Tuple { arity, field } => {
1810 scx.declare_tuple(arity);
1811 fixpoint::Var::TupleProj { arity, field }
1812 }
1813 rty::FieldProj::Adt { def_id, field } => {
1814 let adt_id = scx.declare_adt(def_id);
1815 fixpoint::Var::DataProj { adt_id, field }
1816 }
1817 };
1818 let proj = fixpoint::Expr::Var(proj);
1819 Ok(fixpoint::Expr::App(
1820 Box::new(proj),
1821 None,
1822 vec![self.expr_to_fixpoint(e, scx)?],
1823 None,
1824 ))
1825 }
1826 }
1827
1828 fn un_op_to_fixpoint(
1829 &mut self,
1830 op: rty::UnOp,
1831 e: &rty::Expr,
1832 scx: &mut SortEncodingCtxt,
1833 ) -> QueryResult<fixpoint::Expr> {
1834 match op {
1835 rty::UnOp::Not => Ok(fixpoint::Expr::Not(Box::new(self.expr_to_fixpoint(e, scx)?))),
1836 rty::UnOp::Neg => Ok(fixpoint::Expr::Neg(Box::new(self.expr_to_fixpoint(e, scx)?))),
1837 }
1838 }
1839
1840 fn bv_rel_to_fixpoint(&self, rel: &fixpoint::BinRel) -> fixpoint::Expr {
1841 let itf = match rel {
1842 fixpoint::BinRel::Gt => fixpoint::ThyFunc::BvUgt,
1843 fixpoint::BinRel::Ge => fixpoint::ThyFunc::BvUge,
1844 fixpoint::BinRel::Lt => fixpoint::ThyFunc::BvUlt,
1845 fixpoint::BinRel::Le => fixpoint::ThyFunc::BvUle,
1846 _ => span_bug!(self.def_span(), "not a bitvector relation!"),
1847 };
1848 fixpoint::Expr::ThyFunc(itf)
1849 }
1850
1851 fn set_op_to_fixpoint(&self, op: &rty::BinOp) -> fixpoint::Expr {
1852 let itf = match op {
1853 rty::BinOp::Sub(_) => fixpoint::ThyFunc::SetDif,
1854 rty::BinOp::BitAnd(_) => fixpoint::ThyFunc::SetCap,
1855 rty::BinOp::BitOr(_) => fixpoint::ThyFunc::SetCup,
1856 _ => span_bug!(self.def_span(), "not a set operation!"),
1857 };
1858 fixpoint::Expr::ThyFunc(itf)
1859 }
1860
1861 fn bv_op_to_fixpoint(&self, op: &rty::BinOp) -> fixpoint::Expr {
1862 let itf = match op {
1863 rty::BinOp::Add(_) => fixpoint::ThyFunc::BvAdd,
1864 rty::BinOp::Sub(_) => fixpoint::ThyFunc::BvSub,
1865 rty::BinOp::Mul(_) => fixpoint::ThyFunc::BvMul,
1866 rty::BinOp::Div(_) => fixpoint::ThyFunc::BvUdiv,
1867 rty::BinOp::Mod(_) => fixpoint::ThyFunc::BvUrem,
1868 rty::BinOp::BitAnd(_) => fixpoint::ThyFunc::BvAnd,
1869 rty::BinOp::BitOr(_) => fixpoint::ThyFunc::BvOr,
1870 rty::BinOp::BitXor(_) => fixpoint::ThyFunc::BvXor,
1871 rty::BinOp::BitShl(_) => fixpoint::ThyFunc::BvShl,
1872 rty::BinOp::BitShr(_) => fixpoint::ThyFunc::BvLshr,
1873 _ => span_bug!(self.def_span(), "not a bitvector operation!"),
1874 };
1875 fixpoint::Expr::ThyFunc(itf)
1876 }
1877
1878 fn bin_op_to_fixpoint(
1879 &mut self,
1880 op: &rty::BinOp,
1881 e1: &rty::Expr,
1882 e2: &rty::Expr,
1883 scx: &mut SortEncodingCtxt,
1884 ) -> QueryResult<fixpoint::Expr> {
1885 let op = match op {
1886 rty::BinOp::Eq => {
1887 return Ok(fixpoint::Expr::Atom(
1888 fixpoint::BinRel::Eq,
1889 Box::new([self.expr_to_fixpoint(e1, scx)?, self.expr_to_fixpoint(e2, scx)?]),
1890 ));
1891 }
1892 rty::BinOp::Ne => {
1893 return Ok(fixpoint::Expr::Atom(
1894 fixpoint::BinRel::Ne,
1895 Box::new([self.expr_to_fixpoint(e1, scx)?, self.expr_to_fixpoint(e2, scx)?]),
1896 ));
1897 }
1898 rty::BinOp::Gt(sort) => {
1899 return self.bin_rel_to_fixpoint(sort, fixpoint::BinRel::Gt, e1, e2, scx);
1900 }
1901 rty::BinOp::Ge(sort) => {
1902 return self.bin_rel_to_fixpoint(sort, fixpoint::BinRel::Ge, e1, e2, scx);
1903 }
1904 rty::BinOp::Lt(sort) => {
1905 return self.bin_rel_to_fixpoint(sort, fixpoint::BinRel::Lt, e1, e2, scx);
1906 }
1907 rty::BinOp::Le(sort) => {
1908 return self.bin_rel_to_fixpoint(sort, fixpoint::BinRel::Le, e1, e2, scx);
1909 }
1910 rty::BinOp::And => {
1911 return Ok(fixpoint::Expr::And(vec![
1912 self.expr_to_fixpoint(e1, scx)?,
1913 self.expr_to_fixpoint(e2, scx)?,
1914 ]));
1915 }
1916 rty::BinOp::Or => {
1917 return Ok(fixpoint::Expr::Or(vec![
1918 self.expr_to_fixpoint(e1, scx)?,
1919 self.expr_to_fixpoint(e2, scx)?,
1920 ]));
1921 }
1922 rty::BinOp::Imp => {
1923 return Ok(fixpoint::Expr::Imp(Box::new([
1924 self.expr_to_fixpoint(e1, scx)?,
1925 self.expr_to_fixpoint(e2, scx)?,
1926 ])));
1927 }
1928 rty::BinOp::Iff => {
1929 return Ok(fixpoint::Expr::Iff(Box::new([
1930 self.expr_to_fixpoint(e1, scx)?,
1931 self.expr_to_fixpoint(e2, scx)?,
1932 ])));
1933 }
1934
1935 rty::BinOp::Add(rty::Sort::BitVec(_))
1937 | rty::BinOp::Sub(rty::Sort::BitVec(_))
1938 | rty::BinOp::Mul(rty::Sort::BitVec(_))
1939 | rty::BinOp::Div(rty::Sort::BitVec(_))
1940 | rty::BinOp::Mod(rty::Sort::BitVec(_))
1941 | rty::BinOp::BitAnd(rty::Sort::BitVec(_))
1942 | rty::BinOp::BitOr(rty::Sort::BitVec(_))
1943 | rty::BinOp::BitXor(rty::Sort::BitVec(_))
1944 | rty::BinOp::BitShl(rty::Sort::BitVec(_))
1945 | rty::BinOp::BitShr(rty::Sort::BitVec(_)) => {
1946 let bv_func = self.bv_op_to_fixpoint(op);
1947 return Ok(fixpoint::Expr::App(
1948 Box::new(bv_func),
1949 None,
1950 vec![self.expr_to_fixpoint(e1, scx)?, self.expr_to_fixpoint(e2, scx)?],
1951 None,
1952 ));
1953 }
1954
1955 rty::BinOp::Sub(rty::Sort::App(rty::SortCtor::Set, _))
1957 | rty::BinOp::BitAnd(rty::Sort::App(rty::SortCtor::Set, _))
1958 | rty::BinOp::BitOr(rty::Sort::App(rty::SortCtor::Set, _)) => {
1959 let set_func = self.set_op_to_fixpoint(op);
1960 return Ok(fixpoint::Expr::App(
1961 Box::new(set_func),
1962 None,
1963 vec![self.expr_to_fixpoint(e1, scx)?, self.expr_to_fixpoint(e2, scx)?],
1964 None,
1965 ));
1966 }
1967
1968 rty::BinOp::Add(_) => fixpoint::BinOp::Add,
1970 rty::BinOp::Sub(_) => fixpoint::BinOp::Sub,
1971 rty::BinOp::Mul(_) => fixpoint::BinOp::Mul,
1972 rty::BinOp::Div(_) => fixpoint::BinOp::Div,
1973 rty::BinOp::Mod(_) => fixpoint::BinOp::Mod,
1974
1975 rty::BinOp::BitAnd(sort)
1976 | rty::BinOp::BitOr(sort)
1977 | rty::BinOp::BitXor(sort)
1978 | rty::BinOp::BitShl(sort)
1979 | rty::BinOp::BitShr(sort) => {
1980 bug!("unsupported operation `{op:?}` for sort `{sort:?}`");
1981 }
1982 };
1983 Ok(fixpoint::Expr::BinaryOp(
1984 op,
1985 Box::new([self.expr_to_fixpoint(e1, scx)?, self.expr_to_fixpoint(e2, scx)?]),
1986 ))
1987 }
1988
1989 fn bin_rel_to_fixpoint(
2006 &mut self,
2007 sort: &rty::Sort,
2008 rel: fixpoint::BinRel,
2009 e1: &rty::Expr,
2010 e2: &rty::Expr,
2011 scx: &mut SortEncodingCtxt,
2012 ) -> QueryResult<fixpoint::Expr> {
2013 let e = match sort {
2014 rty::Sort::Int | rty::Sort::Real | rty::Sort::Char => {
2015 fixpoint::Expr::Atom(
2016 rel,
2017 Box::new([self.expr_to_fixpoint(e1, scx)?, self.expr_to_fixpoint(e2, scx)?]),
2018 )
2019 }
2020 rty::Sort::BitVec(_) => {
2021 let e1 = self.expr_to_fixpoint(e1, scx)?;
2022 let e2 = self.expr_to_fixpoint(e2, scx)?;
2023 let rel = self.bv_rel_to_fixpoint(&rel);
2024 fixpoint::Expr::App(Box::new(rel), None, vec![e1, e2], None)
2025 }
2026 rty::Sort::Tuple(sorts) => {
2027 let arity = sorts.len();
2028 self.apply_bin_rel_rec(sorts, rel, e1, e2, scx, |field| {
2029 rty::FieldProj::Tuple { arity, field }
2030 })?
2031 }
2032 rty::Sort::App(rty::SortCtor::Adt(sort_def), args)
2033 if let Some(variant) = sort_def.opt_struct_variant() =>
2034 {
2035 let def_id = sort_def.did();
2036 let sorts = variant.field_sorts(args);
2037 self.apply_bin_rel_rec(&sorts, rel, e1, e2, scx, |field| {
2038 rty::FieldProj::Adt { def_id, field }
2039 })?
2040 }
2041 _ => {
2042 let rel = fixpoint::Expr::Var(fixpoint::Var::UIFRel(rel));
2043 fixpoint::Expr::App(
2044 Box::new(rel),
2045 None,
2046 vec![self.expr_to_fixpoint(e1, scx)?, self.expr_to_fixpoint(e2, scx)?],
2047 None,
2048 )
2049 }
2050 };
2051 Ok(e)
2052 }
2053
2054 fn apply_bin_rel_rec(
2056 &mut self,
2057 sorts: &[rty::Sort],
2058 rel: fixpoint::BinRel,
2059 e1: &rty::Expr,
2060 e2: &rty::Expr,
2061 scx: &mut SortEncodingCtxt,
2062 mk_proj: impl Fn(u32) -> rty::FieldProj,
2063 ) -> QueryResult<fixpoint::Expr> {
2064 Ok(fixpoint::Expr::and(
2065 sorts
2066 .iter()
2067 .enumerate()
2068 .map(|(idx, s)| {
2069 let proj = mk_proj(idx as u32);
2070 let e1 = e1.proj_and_reduce(proj);
2071 let e2 = e2.proj_and_reduce(proj);
2072 self.bin_rel_to_fixpoint(s, rel, &e1, &e2, scx)
2073 })
2074 .try_collect()?,
2075 ))
2076 }
2077
2078 pub fn declare_fun(&mut self, def_id: FluxDefId) -> fixpoint::Var {
2082 *self
2083 .const_env
2084 .fun_decl_map
2085 .entry(def_id)
2086 .or_insert_with(|| {
2087 let id = self.const_env.global_var_gen.fresh();
2088 fixpoint::Var::Global(id, def_id)
2089 })
2090 }
2091
2092 fn prim_op_sort(op: &rty::BinOp, span: Span) -> rty::PolyFuncSort {
2101 match op {
2102 rty::BinOp::BitAnd(rty::Sort::Int)
2103 | rty::BinOp::BitOr(rty::Sort::Int)
2104 | rty::BinOp::BitXor(rty::Sort::Int)
2105 | rty::BinOp::BitShl(rty::Sort::Int)
2106 | rty::BinOp::BitShr(rty::Sort::Int) => {
2107 let fsort =
2108 rty::FuncSort::new(vec![rty::Sort::Int, rty::Sort::Int], rty::Sort::Int);
2109 rty::PolyFuncSort::new(List::empty(), fsort)
2110 }
2111 _ => span_bug!(span, "unexpected prim op: {op:?} in `prim_op_sort`"),
2112 }
2113 }
2114
2115 fn define_const_for_cast(
2116 &mut self,
2117 from: &rty::Sort,
2118 to: &rty::Sort,
2119 scx: &mut SortEncodingCtxt,
2120 ) -> fixpoint::Var {
2121 let key = ConstKey::Cast(from.clone(), to.clone());
2122 self.const_env
2123 .get_or_insert(key, |global_name| {
2124 let fsort = rty::FuncSort::new(vec![from.clone()], to.clone());
2125 let fsort = rty::PolyFuncSort::new(List::empty(), fsort);
2126 let sort = scx.func_sort_to_fixpoint(&fsort).into_sort();
2127 fixpoint::ConstDecl {
2128 name: fixpoint::Var::Const(global_name, None),
2129 sort,
2130 comment: Some(format!("cast uif: ({from:?}) -> {to:?}")),
2131 }
2132 })
2133 .name
2134 }
2135
2136 fn define_const_for_ptr_size(&mut self, scx: &mut SortEncodingCtxt) -> fixpoint::Var {
2137 let key = ConstKey::PtrSize;
2138 self.const_env
2139 .get_or_insert(key, |global_name| {
2140 let fsort = rty::FuncSort::new(vec![rty::Sort::RawPtr], rty::Sort::Int);
2141 let fsort = rty::PolyFuncSort::new(List::empty(), fsort);
2142 let sort = scx.func_sort_to_fixpoint(&fsort).into_sort();
2143 fixpoint::ConstDecl {
2144 name: fixpoint::Var::Const(global_name, None),
2145 sort,
2146 comment: Some("ptr_size uif: RawPtr -> Int".to_string()),
2147 }
2148 })
2149 .name
2150 }
2151
2152 fn define_const_for_prim_op(
2153 &mut self,
2154 op: &rty::BinOp,
2155 scx: &mut SortEncodingCtxt,
2156 ) -> fixpoint::Var {
2157 let key = ConstKey::PrimOp(op.clone());
2158 let span = self.def_span();
2159 self.const_env
2160 .get_or_insert(key, |global_name| {
2161 let sort = scx
2162 .func_sort_to_fixpoint(&Self::prim_op_sort(op, span))
2163 .into_sort();
2164 fixpoint::ConstDecl {
2165 name: fixpoint::Var::Const(global_name, None),
2166 sort,
2167 comment: Some(format!("prim op uif: {op:?}")),
2168 }
2169 })
2170 .name
2171 }
2172
2173 fn define_const_for_rust_const(
2174 &mut self,
2175 def_id: DefId,
2176 scx: &mut SortEncodingCtxt,
2177 ) -> fixpoint::Var {
2178 let key = ConstKey::RustConst(def_id);
2179 self.const_env
2180 .get_or_insert(key, |global_name| {
2181 let sort = self.genv.sort_of_def_id(def_id).unwrap().unwrap();
2182 fixpoint::ConstDecl {
2183 name: fixpoint::Var::Const(global_name, Some(def_id)),
2184 sort: scx.sort_to_fixpoint(&sort),
2185 comment: Some(format!("rust const: {}", def_id_to_string(def_id))),
2186 }
2187 })
2188 .name
2189 }
2190
2191 fn define_const_for_alias_reft(
2194 &mut self,
2195 alias_reft: &rty::AliasReft,
2196 fsort: rty::FuncSort,
2197 scx: &mut SortEncodingCtxt,
2198 ) -> fixpoint::Var {
2199 let tcx = self.genv.tcx();
2200 let args = alias_reft
2201 .args
2202 .to_rustc(tcx)
2203 .truncate_to(tcx, tcx.generics_of(alias_reft.assoc_id.parent()));
2204 let args = tcx.erase_and_anonymize_regions(args);
2207 let key = ConstKey::Alias(alias_reft.assoc_id, args);
2208 self.const_env
2209 .get_or_insert(key, |global_name| {
2210 let comment = Some(format!("alias reft: {alias_reft:?}"));
2211 let name = fixpoint::Var::Const(global_name, None);
2212 let fsort = rty::PolyFuncSort::new(List::empty(), fsort);
2213 let sort = scx.func_sort_to_fixpoint(&fsort).into_sort();
2214 fixpoint::ConstDecl { name, comment, sort }
2215 })
2216 .name
2217 }
2218
2219 fn define_const_for_lambda(
2222 &mut self,
2223 lam: &rty::Lambda,
2224 scx: &mut SortEncodingCtxt,
2225 ) -> fixpoint::Var {
2226 let key = ConstKey::Lambda(lam.clone());
2227 self.const_env
2228 .get_or_insert(key, |global_name| {
2229 let comment = Some(format!("lambda: {lam:?}"));
2230 let name = fixpoint::Var::Const(global_name, None);
2231 let sort = scx
2232 .func_sort_to_fixpoint(&lam.fsort().to_poly())
2233 .into_sort();
2234 fixpoint::ConstDecl { name, comment, sort }
2235 })
2236 .name
2237 }
2238
2239 fn assume_const_values(
2240 &mut self,
2241 mut constraint: fixpoint::Constraint,
2242 scx: &mut SortEncodingCtxt,
2243 ) -> QueryResult<fixpoint::Constraint> {
2244 let mut idx = 0;
2247 while let Some((key, const_)) = self.const_env.const_map.get_index(idx) {
2248 idx += 1;
2249
2250 match key {
2251 ConstKey::RustConst(def_id) => {
2252 let info = self.genv.constant_info(def_id)?;
2253 match info {
2254 rty::ConstantInfo::Uninterpreted => {}
2255 rty::ConstantInfo::Interpreted(val, _) => {
2256 let const_name = const_.name;
2257 let const_sort = const_.sort.clone();
2258
2259 let e1 = fixpoint::Expr::Var(const_.name);
2260 let e2 = self.expr_to_fixpoint(&val, scx)?;
2261 let pred = fixpoint::Pred::Expr(e1.eq(e2));
2262
2263 let bind = match self.backend {
2264 Backend::Fixpoint => {
2265 fixpoint::Bind {
2266 name: fixpoint::Var::Underscore,
2267 sort: fixpoint::Sort::Int,
2268 pred,
2269 }
2270 }
2271 Backend::Lean => {
2272 fixpoint::Bind { name: const_name, sort: const_sort, pred }
2273 }
2274 };
2275 constraint = fixpoint::Constraint::ForAll(bind, Box::new(constraint));
2276 }
2277 }
2278 }
2279 ConstKey::Alias(..) if matches!(self.backend, Backend::Lean) => {
2280 constraint = fixpoint::Constraint::ForAll(
2281 fixpoint::Bind {
2282 name: const_.name,
2283 sort: const_.sort.clone(),
2284 pred: fixpoint::Pred::TRUE,
2285 },
2286 Box::new(constraint),
2287 );
2288 }
2289 ConstKey::Alias(..)
2290 | ConstKey::Cast(..)
2291 | ConstKey::Lambda(..)
2292 | ConstKey::PrimOp(..)
2293 | ConstKey::PtrSize => {}
2294 }
2295 }
2296 Ok(constraint)
2297 }
2298
2299 fn qualifiers_for(
2300 &mut self,
2301 def_id: LocalDefId,
2302 scx: &mut SortEncodingCtxt,
2303 ) -> QueryResult<Vec<fixpoint::Qualifier>> {
2304 self.genv
2305 .qualifiers_for(def_id)?
2306 .map(|qual| self.qualifier_to_fixpoint(qual, scx))
2307 .try_collect()
2308 }
2309
2310 fn define_funs(
2311 &mut self,
2312 def_id: MaybeExternId,
2313 scx: &mut SortEncodingCtxt,
2314 ) -> QueryResult<Vec<fixpoint::FunDef>> {
2315 let reveals: UnordSet<FluxDefId> = self
2316 .genv
2317 .reveals_for(def_id.local_id())
2318 .iter()
2319 .copied()
2320 .collect();
2321 let proven_externally = self.genv.proven_externally(def_id.local_id());
2322 let mut defs = vec![];
2323
2324 let mut idx = 0;
2326 while let Some((&did, _)) = self.const_env.fun_decl_map.get_index(idx) {
2327 idx += 1;
2328
2329 let info = self.genv.normalized_info(did);
2330 let revealed = reveals.contains(&did);
2331 let def = if info.uif || (info.hide && !revealed && proven_externally.is_none()) {
2332 self.fun_decl_to_fixpoint(did, scx)
2333 } else {
2334 self.fun_def_to_fixpoint(did, scx)?
2335 };
2336 defs.push((info.rank, def));
2337 }
2338
2339 let defs = defs
2341 .into_iter()
2342 .sorted_by_key(|(rank, _)| *rank)
2343 .map(|(_, def)| def)
2344 .collect();
2345
2346 Ok(defs)
2347 }
2348
2349 fn fun_decl_to_fixpoint(
2350 &mut self,
2351 def_id: FluxDefId,
2352 scx: &mut SortEncodingCtxt,
2353 ) -> fixpoint::FunDef {
2354 let name = self.const_env.fun_decl_map[&def_id];
2355 let sort = scx.func_sort_to_fixpoint(&self.genv.func_sort(def_id));
2356 fixpoint::FunDef { name, sort, body: None, comment: Some(format!("flux def: {def_id:?}")) }
2357 }
2358
2359 pub fn fun_def_to_fixpoint(
2360 &mut self,
2361 def_id: FluxDefId,
2362 scx: &mut SortEncodingCtxt,
2363 ) -> QueryResult<fixpoint::FunDef> {
2364 let name = *self.const_env.fun_decl_map.get(&def_id).unwrap();
2365 let body = self.genv.inlined_body(def_id);
2366 let output = scx.sort_to_fixpoint(self.genv.func_sort(def_id).expect_mono().output());
2367 let (args, expr) = self.body_to_fixpoint(&body, scx)?;
2368 let (args, inputs) = args.into_iter().unzip();
2369 Ok(fixpoint::FunDef {
2370 name,
2371 sort: fixpoint::FunSort { params: 0, inputs, output },
2372 body: Some(fixpoint::FunBody { args, expr }),
2373 comment: Some(format!("flux def: {def_id:?}")),
2374 })
2375 }
2376
2377 fn body_to_fixpoint(
2378 &mut self,
2379 body: &rty::Binder<rty::Expr>,
2380 scx: &mut SortEncodingCtxt,
2381 ) -> QueryResult<(Vec<(fixpoint::Var, fixpoint::Sort)>, fixpoint::Expr)> {
2382 self.local_var_env
2383 .push_layer_with_fresh_names(body.vars().len());
2384
2385 let expr = self.expr_to_fixpoint(body.as_ref().skip_binder(), scx)?;
2386
2387 let args: Vec<(fixpoint::Var, fixpoint::Sort)> =
2388 iter::zip(self.local_var_env.pop_layer(), body.vars())
2389 .map(|(name, var)| (name.into(), scx.sort_to_fixpoint(var.expect_sort())))
2390 .collect();
2391
2392 Ok((args, expr))
2393 }
2394
2395 fn qualifier_to_fixpoint(
2396 &mut self,
2397 qualifier: &rty::Qualifier,
2398 scx: &mut SortEncodingCtxt,
2399 ) -> QueryResult<fixpoint::Qualifier> {
2400 let (args, body) = self.body_to_fixpoint(&qualifier.body, scx)?;
2401 let name = qualifier.def_id.name().to_string();
2402 Ok(fixpoint::Qualifier { name, args, body })
2403 }
2404}
2405
2406fn mk_implies(assumption: fixpoint::Pred, cstr: fixpoint::Constraint) -> fixpoint::Constraint {
2407 fixpoint::Constraint::ForAll(
2408 fixpoint::Bind {
2409 name: fixpoint::Var::Underscore,
2410 sort: fixpoint::Sort::Int,
2411 pred: assumption,
2412 },
2413 Box::new(cstr),
2414 )
2415}
2416
2417fn parse_kvid(kvid: &str) -> fixpoint::KVid {
2418 if kvid.starts_with("k")
2419 && let Some(kvid) = kvid[1..].parse::<u32>().ok()
2420 {
2421 fixpoint::KVid::from_u32(kvid)
2422 } else {
2423 tracked_span_bug!("unexpected kvar name {kvid}")
2424 }
2425}
2426
2427fn parse_local_var(name: &str) -> Option<fixpoint::Var> {
2428 if let Some(rest) = name.strip_prefix('a')
2429 && let Ok(idx) = rest.parse::<u32>()
2430 {
2431 return Some(fixpoint::Var::Local(fixpoint::LocalVar::from(idx)));
2432 }
2433 None
2434}
2435
2436fn parse_global_var(
2437 name: &str,
2438 fun_decl_map: &HashMap<usize, FluxDefId>,
2439 const_decl_map: &HashMap<usize, DefId>,
2440) -> Option<fixpoint::Var> {
2441 if let Some(rest) = name.strip_prefix('c')
2442 && let Ok(idx) = rest.parse::<u32>()
2443 {
2444 return Some(fixpoint::Var::Const(
2445 fixpoint::GlobalVar::from(idx),
2446 const_decl_map.get(&(idx as usize)).copied(),
2447 ));
2448 }
2449 if let Some(rest) = name.strip_prefix("f$")
2451 && let parts = rest.split('$').collect::<Vec<_>>()
2452 && parts.len() == 2
2453 && let Ok(global_idx) = parts[1].parse::<u32>()
2454 && let Some(def_id) = fun_decl_map.get(&(global_idx as usize)).copied()
2455 {
2456 return Some(fixpoint::Var::Global(fixpoint::GlobalVar::from(global_idx), def_id));
2457 }
2458 None
2459}
2460
2461fn parse_param(name: &str) -> Option<fixpoint::Var> {
2462 if let Some(rest) = name.strip_prefix("reftgen$")
2463 && let parts = rest.split('$').collect::<Vec<_>>()
2464 && parts.len() == 2
2465 && let Ok(index) = parts[1].parse::<u32>()
2466 {
2467 let name = Symbol::intern(parts[0]);
2468 let param = EarlyReftParam { index, name };
2469 return Some(fixpoint::Var::Param(param));
2470 }
2471 None
2472}
2473
2474fn parse_data_proj(name: &str) -> Option<fixpoint::Var> {
2475 if let Some(rest) = name.strip_prefix("fld")
2476 && let parts = rest.split('$').collect::<Vec<_>>()
2477 && parts.len() == 2
2478 && let Ok(adt_id) = parts[0].parse::<u32>()
2479 && let Ok(field) = parts[1].parse::<u32>()
2480 {
2481 let adt_id = fixpoint::AdtId::from(adt_id);
2482 return Some(fixpoint::Var::DataProj { adt_id, field });
2483 }
2484 None
2485}
2486
2487fn parse_data_ctor(name: &str) -> Option<fixpoint::Var> {
2488 if let Some(rest) = name.strip_prefix("mkadt")
2489 && let parts = rest.split('$').collect::<Vec<_>>()
2490 && parts.len() == 2
2491 && let Ok(adt_id) = parts[0].parse::<u32>()
2492 && let Ok(variant_idx) = parts[1].parse::<u32>()
2493 {
2494 let adt_id = fixpoint::AdtId::from(adt_id);
2495 let variant_idx = VariantIdx::from(variant_idx);
2496 return Some(fixpoint::Var::DataCtor(adt_id, variant_idx));
2497 }
2498 None
2499}
2500
2501struct SexpParseCtxt<'a> {
2502 local_var_env: &'a mut LocalVarEnv,
2503 fun_decl_map: &'a HashMap<usize, FluxDefId>,
2504 const_decl_map: &'a HashMap<usize, DefId>,
2505}
2506
2507impl<'a> SexpParseCtxt<'a> {
2508 fn new(
2509 local_var_env: &'a mut LocalVarEnv,
2510 fun_decl_map: &'a HashMap<usize, FluxDefId>,
2511 const_decl_map: &'a HashMap<usize, DefId>,
2512 ) -> Self {
2513 Self { local_var_env, fun_decl_map, const_decl_map }
2514 }
2515}
2516
2517impl FromSexp<FixpointTypes> for SexpParseCtxt<'_> {
2518 fn fresh_var(&mut self) -> fixpoint::Var {
2519 fixpoint::Var::Local(self.local_var_env.fresh_name())
2520 }
2521
2522 fn kvar(&self, name: &str) -> Result<fixpoint::KVid, ParseError> {
2523 bug!("TODO: SexpParse: kvar: {name}")
2524 }
2525
2526 fn string(&self, s: &str) -> Result<fixpoint::SymStr, ParseError> {
2527 Ok(fixpoint::SymStr(Symbol::intern(s)))
2528 }
2529
2530 fn var(&self, name: &str) -> Result<fixpoint::Var, ParseError> {
2531 if let Some(var) = parse_local_var(name) {
2532 return Ok(var);
2533 }
2534 if let Some(var) = parse_global_var(name, self.fun_decl_map, self.const_decl_map) {
2535 return Ok(var);
2536 }
2537 if let Some(var) = parse_param(name) {
2538 return Ok(var);
2539 }
2540 if let Some(var) = parse_data_proj(name) {
2541 return Ok(var);
2542 }
2543 if let Some(var) = parse_data_ctor(name) {
2544 return Ok(var);
2545 }
2546 Err(ParseError::err(format!("Unknown variable: {name}")))
2547 }
2548
2549 fn sort(&self, name: &str) -> Result<fixpoint::DataSort, ParseError> {
2550 if let Some(idx) = name.strip_prefix("Adt")
2551 && let Ok(adt_id) = idx.parse::<u32>()
2552 {
2553 return Ok(fixpoint::DataSort::Adt(fixpoint::AdtId::from(adt_id)));
2554 }
2555 if let Some(idx) = name.strip_prefix("OpaqueAdt")
2556 && let Ok(opaque_id) = idx.parse::<u32>()
2557 {
2558 return Ok(fixpoint::DataSort::User(fixpoint::OpaqueId::from(opaque_id)));
2559 }
2560
2561 Err(ParseError::err(format!("Unknown sort: {name}")))
2562 }
2563}