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