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