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