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