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