1use std::{collections::HashMap, hash::Hash, iter, ops::Range};
4
5use fixpoint::AdtId;
6use flux_common::{
7 bug,
8 cache::QueryCache,
9 dbg,
10 index::{IndexGen, IndexVec},
11 iter::IterExt,
12 span_bug, tracked_span_bug,
13};
14use flux_config::{self as config};
15use flux_errors::Errors;
16use flux_macros::DebugAsJson;
17use flux_middle::{
18 FixpointQueryKind,
19 def_id::{FluxDefId, MaybeExternId},
20 def_id_to_string,
21 global_env::GlobalEnv,
22 metrics::{self, Metric, TimingKind},
23 pretty::{NestedString, PrettyCx, PrettyNested},
24 queries::QueryResult,
25 query_bug,
26 rty::{
27 self, ESpan, EarlyReftParam, GenericArgsExt, InternalFuncKind, Lambda, List,
28 NameProvenance, PrettyMap, PrettyVar, SpecFuncKind, VariantIdx, fold::TypeFoldable as _,
29 },
30};
31use itertools::Itertools;
32use liquid_fixpoint::{
33 FixpointResult, FixpointStatus, SmtSolver,
34 parser::{FromSexp, ParseError},
35 sexp::Parser,
36};
37use rustc_data_structures::{
38 fx::{FxIndexMap, FxIndexSet},
39 unord::{UnordMap, UnordSet},
40};
41use rustc_hir::def_id::{DefId, LocalDefId};
42use rustc_index::newtype_index;
43use rustc_infer::infer::TyCtxtInferExt as _;
44use rustc_middle::ty::TypingMode;
45use rustc_span::{DUMMY_SP, Span, Symbol};
46use rustc_type_ir::{BoundVar, DebruijnIndex};
47use serde::{Deserialize, Deserializer, Serialize};
48
49use crate::{
50 fixpoint_encoding::fixpoint::FixpointTypes, fixpoint_qualifiers::FIXPOINT_QUALIFIERS,
51 lean_encoding::LeanEncoder, projections::structurally_normalize_expr,
52};
53
54pub mod decoding;
55
56pub mod fixpoint {
57 use std::fmt;
58
59 use flux_middle::{def_id::FluxDefId, rty::EarlyReftParam};
60 use liquid_fixpoint::{FixpointFmt, Identifier};
61 use rustc_abi::VariantIdx;
62 use rustc_index::newtype_index;
63 use rustc_middle::ty::ParamConst;
64 use rustc_span::Symbol;
65
66 newtype_index! {
67 #[orderable]
68 pub struct KVid {}
69 }
70
71 impl Identifier for KVid {
72 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
73 write!(f, "k{}", self.as_u32())
74 }
75 }
76
77 newtype_index! {
78 pub struct LocalVar {}
79 }
80
81 newtype_index! {
82 pub struct GlobalVar {}
83 }
84
85 newtype_index! {
86 pub struct AdtId {}
89 }
90
91 #[derive(Hash, Copy, Clone, Debug, PartialEq, Eq)]
92 pub enum Var {
93 Underscore,
94 Global(GlobalVar, Option<FluxDefId>),
95 Local(LocalVar),
96 DataCtor(AdtId, VariantIdx),
97 TupleCtor { arity: usize },
98 TupleProj { arity: usize, field: u32 },
99 DataProj { adt_id: AdtId, field: u32 },
100 UIFRel(BinRel),
101 Param(EarlyReftParam),
102 ConstGeneric(ParamConst),
103 }
104
105 impl From<GlobalVar> for Var {
106 fn from(v: GlobalVar) -> Self {
107 Self::Global(v, None)
108 }
109 }
110
111 impl From<LocalVar> for Var {
112 fn from(v: LocalVar) -> Self {
113 Self::Local(v)
114 }
115 }
116
117 impl Identifier for Var {
118 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
119 match self {
120 Var::Global(v, None) => write!(f, "c{}", v.as_u32()),
121 Var::Global(v, Some(did)) => write!(f, "f${}${}", did.name(), v.as_u32()),
122 Var::Local(v) => write!(f, "a{}", v.as_u32()),
123 Var::DataCtor(adt_id, variant_idx) => {
124 write!(f, "mkadt{}${}", adt_id.as_u32(), variant_idx.as_u32())
125 }
126 Var::TupleCtor { arity } => write!(f, "mktuple{arity}"),
127 Var::TupleProj { arity, field } => write!(f, "tuple{arity}${field}"),
128 Var::DataProj { adt_id, field } => write!(f, "fld{}${field}", adt_id.as_u32()),
129 Var::UIFRel(BinRel::Gt) => write!(f, "gt"),
130 Var::UIFRel(BinRel::Ge) => write!(f, "ge"),
131 Var::UIFRel(BinRel::Lt) => write!(f, "lt"),
132 Var::UIFRel(BinRel::Le) => write!(f, "le"),
133 Var::UIFRel(BinRel::Eq) => write!(f, "eq"),
135 Var::UIFRel(BinRel::Ne) => write!(f, "ne"),
136 Var::Underscore => write!(f, "_$"), Var::ConstGeneric(param) => {
138 write!(f, "constgen${}${}", param.name, param.index)
139 }
140 Var::Param(param) => {
141 write!(f, "reftgen${}${}", param.name, param.index)
142 }
143 }
144 }
145 }
146
147 #[derive(Clone, Hash, Debug, PartialEq, Eq)]
148 pub enum DataSort {
149 Tuple(usize),
150 Adt(AdtId),
151 User(FluxDefId),
152 }
153
154 impl Identifier for DataSort {
155 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
156 match self {
157 DataSort::Tuple(arity) => {
158 write!(f, "Tuple{arity}")
159 }
160 DataSort::Adt(adt_id) => {
161 write!(f, "Adt{}", adt_id.as_u32())
162 }
163 DataSort::User(..) => {
167 write!(f, "int")
168 }
169 }
170 }
171 }
172
173 #[derive(Hash, Clone, Debug)]
174 pub struct SymStr(pub Symbol);
175
176 #[cfg(feature = "rust-fixpoint")]
177 impl FixpointFmt for SymStr {
178 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
179 write!(f, "{}", self.0)
180 }
181 }
182
183 #[cfg(not(feature = "rust-fixpoint"))]
184 impl FixpointFmt for SymStr {
185 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
186 write!(f, "\"{}\"", self.0)
187 }
188 }
189
190 liquid_fixpoint::declare_types! {
191 type Sort = DataSort;
192 type KVar = KVid;
193 type Var = Var;
194 type String = SymStr;
195 type Tag = super::TagIdx;
196 }
197 pub use fixpoint_generated::*;
198}
199
200pub type Solution = HashMap<rty::KVid, rty::Binder<rty::Expr>>;
202
203#[derive(Serialize, DebugAsJson)]
205pub struct SolutionTrace(Vec<KvarSolutionTrace>);
206
207#[derive(Serialize, DebugAsJson)]
208pub struct KvarSolutionTrace {
209 pub name: String,
210 pub args: Vec<String>,
211 pub body: NestedString,
212}
213
214impl KvarSolutionTrace {
215 pub fn new(cx: &PrettyCx, kvid: rty::KVid, bind_expr: &rty::Binder<rty::Expr>) -> Self {
216 let mut args = Vec::new();
217 let body = cx
218 .nested_with_bound_vars("", bind_expr.vars(), Some("".to_string()), |prefix| {
219 for arg in prefix.split(',').map(|s| s.trim().to_string()) {
220 args.push(arg);
221 }
222 bind_expr.skip_binder_ref().fmt_nested(cx)
223 })
224 .unwrap();
225
226 KvarSolutionTrace { name: format!("{kvid:?}"), args, body }
227 }
228}
229
230impl SolutionTrace {
231 pub fn new(genv: GlobalEnv, solution: &Solution) -> Self {
232 let cx = &PrettyCx::default(genv);
233 let res = solution
234 .iter()
235 .map(|(kvid, bind_expr)| KvarSolutionTrace::new(cx, *kvid, bind_expr))
236 .collect();
237 SolutionTrace(res)
238 }
239}
240
241#[derive(Debug, Clone, Default)]
242pub struct Answer<Tag> {
243 pub errors: Vec<Tag>,
244 pub solution: Solution,
245}
246
247impl<Tag> Answer<Tag> {
248 pub fn trivial() -> Self {
249 Self { errors: Vec::new(), solution: HashMap::new() }
250 }
251}
252
253newtype_index! {
254 #[debug_format = "TagIdx({})"]
255 pub struct TagIdx {}
256}
257
258impl Serialize for TagIdx {
259 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
260 self.as_u32().serialize(serializer)
261 }
262}
263
264impl<'de> Deserialize<'de> for TagIdx {
265 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
266 let idx = usize::deserialize(deserializer)?;
267 Ok(TagIdx::from_u32(idx as u32))
268 }
269}
270
271#[derive(Default)]
273pub struct SortEncodingCtxt {
274 tuples: UnordSet<usize>,
276 adt_sorts: FxIndexSet<DefId>,
279 user_sorts: FxIndexSet<FluxDefId>,
281}
282
283impl SortEncodingCtxt {
284 pub fn sort_to_fixpoint(&mut self, sort: &rty::Sort) -> fixpoint::Sort {
285 match sort {
286 rty::Sort::Int => fixpoint::Sort::Int,
287 rty::Sort::Real => fixpoint::Sort::Real,
288 rty::Sort::Bool => fixpoint::Sort::Bool,
289 rty::Sort::Str => fixpoint::Sort::Str,
290 rty::Sort::Char => fixpoint::Sort::Int,
291 rty::Sort::BitVec(size) => fixpoint::Sort::BitVec(Box::new(bv_size_to_fixpoint(*size))),
292 rty::Sort::App(rty::SortCtor::User(def_id), args) => {
293 self.declare_opaque_sort(*def_id);
294 let args = args.iter().map(|s| self.sort_to_fixpoint(s)).collect_vec();
295 fixpoint::Sort::App(
296 fixpoint::SortCtor::Data(fixpoint::DataSort::User(*def_id)),
297 args,
298 )
299 }
300 rty::Sort::Param(_)
305 | rty::Sort::Alias(rty::AliasKind::Opaque | rty::AliasKind::Projection, ..) => {
306 fixpoint::Sort::Int
307 }
308 rty::Sort::App(rty::SortCtor::Set, args) => {
309 let args = args.iter().map(|s| self.sort_to_fixpoint(s)).collect_vec();
310 fixpoint::Sort::App(fixpoint::SortCtor::Set, args)
311 }
312 rty::Sort::App(rty::SortCtor::Map, args) => {
313 let args = args.iter().map(|s| self.sort_to_fixpoint(s)).collect_vec();
314 fixpoint::Sort::App(fixpoint::SortCtor::Map, args)
315 }
316 rty::Sort::App(rty::SortCtor::Adt(sort_def), args) => {
317 if let Some(variant) = sort_def.opt_struct_variant() {
318 let sorts = variant.field_sorts(args);
319 if let [sort] = &sorts[..] {
321 self.sort_to_fixpoint(sort)
322 } else {
323 let adt_id = self.declare_adt(sort_def.did());
324 let ctor = fixpoint::SortCtor::Data(fixpoint::DataSort::Adt(adt_id));
325 let args = args.iter().map(|s| self.sort_to_fixpoint(s)).collect_vec();
326 fixpoint::Sort::App(ctor, args)
327 }
328 } else {
329 debug_assert!(args.is_empty());
330 let adt_id = self.declare_adt(sort_def.did());
331 fixpoint::Sort::App(
332 fixpoint::SortCtor::Data(fixpoint::DataSort::Adt(adt_id)),
333 vec![],
334 )
335 }
336 }
337 rty::Sort::Tuple(sorts) => {
338 if let [sort] = &sorts[..] {
340 self.sort_to_fixpoint(sort)
341 } else {
342 self.declare_tuple(sorts.len());
343 let ctor = fixpoint::SortCtor::Data(fixpoint::DataSort::Tuple(sorts.len()));
344 let args = sorts.iter().map(|s| self.sort_to_fixpoint(s)).collect();
345 fixpoint::Sort::App(ctor, args)
346 }
347 }
348 rty::Sort::Func(sort) => self.func_sort_to_fixpoint(sort),
349 rty::Sort::Var(k) => fixpoint::Sort::Var(k.index()),
350 rty::Sort::Err
351 | rty::Sort::Infer(_)
352 | rty::Sort::Loc
353 | rty::Sort::Alias(rty::AliasKind::Free, _) => {
354 tracked_span_bug!("unexpected sort `{sort:?}`")
355 }
356 }
357 }
358
359 fn func_sort_to_fixpoint(&mut self, fsort: &rty::PolyFuncSort) -> fixpoint::Sort {
360 let params = fsort.params().len();
361 let fsort = fsort.skip_binders();
362 let output = self.sort_to_fixpoint(fsort.output());
363 fixpoint::Sort::mk_func(
364 params,
365 fsort.inputs().iter().map(|s| self.sort_to_fixpoint(s)),
366 output,
367 )
368 }
369
370 fn declare_tuple(&mut self, arity: usize) {
371 self.tuples.insert(arity);
372 }
373
374 pub fn declare_opaque_sort(&mut self, def_id: FluxDefId) {
375 self.user_sorts.insert(def_id);
376 }
377
378 pub fn declare_adt(&mut self, did: DefId) -> AdtId {
379 if let Some(idx) = self.adt_sorts.get_index_of(&did) {
380 AdtId::from_usize(idx)
381 } else {
382 let adt_id = AdtId::from_usize(self.adt_sorts.len());
383 self.adt_sorts.insert(did);
384 adt_id
385 }
386 }
387
388 pub fn user_sorts_to_fixpoint(&mut self, genv: GlobalEnv) -> Vec<fixpoint::SortDecl> {
389 self.user_sorts
390 .iter()
391 .map(|sort| {
392 let param_count = genv.sort_decl_param_count(sort);
393 fixpoint::SortDecl { name: fixpoint::DataSort::User(*sort), vars: param_count }
394 })
395 .collect()
396 }
397
398 fn append_adt_decls(
399 &mut self,
400 genv: GlobalEnv,
401 decls: &mut Vec<fixpoint::DataDecl>,
402 ) -> QueryResult {
403 let mut idx = 0;
406 while let Some(adt_def_id) = self.adt_sorts.get_index(idx) {
407 let adt_id = AdtId::from_usize(idx);
408 let adt_sort_def = genv.adt_sort_def_of(adt_def_id)?;
409 decls.push(fixpoint::DataDecl {
410 name: fixpoint::DataSort::Adt(adt_id),
411 vars: adt_sort_def.param_count(),
412 ctors: adt_sort_def
413 .variants()
414 .iter_enumerated()
415 .map(|(idx, variant)| {
416 let name = fixpoint::Var::DataCtor(adt_id, idx);
417 let fields = variant
418 .field_sorts_instantiate_identity()
419 .iter()
420 .enumerate()
421 .map(|(i, sort)| {
422 fixpoint::DataField {
423 name: fixpoint::Var::DataProj { adt_id, field: i as u32 },
424 sort: self.sort_to_fixpoint(sort),
425 }
426 })
427 .collect_vec();
428 fixpoint::DataCtor { name, fields }
429 })
430 .collect(),
431 });
432 idx += 1;
433 }
434 Ok(())
435 }
436
437 fn append_tuple_decls(tuples: &UnordSet<usize>, decls: &mut Vec<fixpoint::DataDecl>) {
438 decls.extend(
439 tuples
440 .items()
441 .into_sorted_stable_ord()
442 .into_iter()
443 .map(|arity| {
444 fixpoint::DataDecl {
445 name: fixpoint::DataSort::Tuple(*arity),
446 vars: *arity,
447 ctors: vec![fixpoint::DataCtor {
448 name: fixpoint::Var::TupleCtor { arity: *arity },
449 fields: (0..(*arity as u32))
450 .map(|field| {
451 fixpoint::DataField {
452 name: fixpoint::Var::TupleProj { arity: *arity, field },
453 sort: fixpoint::Sort::Var(field as usize),
454 }
455 })
456 .collect(),
457 }],
458 }
459 }),
460 );
461 }
462
463 pub fn encode_data_decls(&mut self, genv: GlobalEnv) -> QueryResult<Vec<fixpoint::DataDecl>> {
464 let mut decls = vec![];
465 self.append_adt_decls(genv, &mut decls)?;
466 Self::append_tuple_decls(&self.tuples, &mut decls);
467 Ok(decls)
468 }
469}
470
471fn bv_size_to_fixpoint(size: rty::BvSize) -> fixpoint::Sort {
472 match size {
473 rty::BvSize::Fixed(size) => fixpoint::Sort::BvSize(size),
474 rty::BvSize::Param(_var) => {
475 bug!("unexpected parametric bit-vector size")
480 }
481 rty::BvSize::Infer(_) => bug!("unexpected infer variable for bit-vector size"),
482 }
483}
484
485pub type FunDefMap = FxIndexMap<FluxDefId, fixpoint::Var>;
486type ConstMap<'tcx> = FxIndexMap<ConstKey<'tcx>, fixpoint::ConstDecl>;
487
488#[derive(Eq, Hash, PartialEq, Clone)]
489enum ConstKey<'tcx> {
490 Uif(FluxDefId),
491 RustConst(DefId),
492 Alias(FluxDefId, rustc_middle::ty::GenericArgsRef<'tcx>),
493 Lambda(Lambda),
494 PrimOp(rty::BinOp),
495 Cast(rty::Sort, rty::Sort),
496}
497
498#[derive(Clone)]
499pub enum Backend {
500 Fixpoint,
501 Lean,
502}
503
504pub struct FixpointCtxt<'genv, 'tcx, T: Eq + Hash> {
505 comments: Vec<String>,
506 genv: GlobalEnv<'genv, 'tcx>,
507 kvars: KVarGen,
508 scx: SortEncodingCtxt,
509 kcx: KVarEncodingCtxt,
510 ecx: ExprEncodingCtxt<'genv, 'tcx>,
511 tags: IndexVec<TagIdx, T>,
512 tags_inv: UnordMap<T, TagIdx>,
513}
514
515pub type FixQueryCache = QueryCache<FixpointResult<TagIdx>>;
516
517impl<'genv, 'tcx, Tag> FixpointCtxt<'genv, 'tcx, Tag>
518where
519 Tag: std::hash::Hash + Eq + Copy,
520{
521 pub fn new(
522 genv: GlobalEnv<'genv, 'tcx>,
523 def_id: MaybeExternId,
524 kvars: KVarGen,
525 backend: Backend,
526 ) -> Self {
527 Self {
528 comments: vec![],
529 kvars,
530 scx: SortEncodingCtxt::default(),
531 genv,
532 ecx: ExprEncodingCtxt::new(genv, Some(def_id), backend),
533 kcx: Default::default(),
534 tags: IndexVec::new(),
535 tags_inv: Default::default(),
536 }
537 }
538
539 pub fn check(
540 mut self,
541 cache: &mut FixQueryCache,
542 def_id: MaybeExternId,
543 constraint: fixpoint::Constraint,
544 kind: FixpointQueryKind,
545 scrape_quals: bool,
546 solver: SmtSolver,
547 ) -> QueryResult<Answer<Tag>> {
548 let count = constraint.concrete_head_count();
550 metrics::incr_metric(Metric::CsTotal, count as u32);
551 if count == 0 {
552 metrics::incr_metric_if(kind.is_body(), Metric::FnTrivial);
553 self.ecx.errors.to_result()?;
554 return Ok(Answer::trivial());
555 }
556 let def_span = self.ecx.def_span();
557 let kvars = self.kcx.encode_kvars(&self.kvars, &mut self.scx);
558 let (define_funs, define_constants) = self.ecx.define_funs(def_id, &mut self.scx)?;
559 let qualifiers = self
560 .ecx
561 .qualifiers_for(def_id.local_id(), &mut self.scx)?
562 .into_iter()
563 .chain(FIXPOINT_QUALIFIERS.iter().cloned())
564 .collect();
565
566 let constraint = self.ecx.assume_const_values(constraint, &mut self.scx)?;
569
570 let mut constants = self.ecx.const_env.const_map.values().cloned().collect_vec();
571 constants.extend(define_constants);
572
573 #[cfg(not(feature = "rust-fixpoint"))]
577 for rel in fixpoint::BinRel::INEQUALITIES {
578 let sort = fixpoint::Sort::mk_func(
580 1,
581 [fixpoint::Sort::Var(0), fixpoint::Sort::Var(0)],
582 fixpoint::Sort::Bool,
583 );
584 constants.push(fixpoint::ConstDecl {
585 name: fixpoint::Var::UIFRel(rel),
586 sort,
587 comment: None,
588 });
589 }
590
591 self.ecx.errors.to_result()?;
593
594 let task = fixpoint::Task {
595 comments: self.comments.clone(),
596 constants,
597 kvars,
598 define_funs,
599 constraint,
600 qualifiers,
601 scrape_quals,
602 solver,
603 data_decls: self.scx.encode_data_decls(self.genv)?,
604 };
605 if config::dump_constraint() {
606 dbg::dump_item_info(self.genv.tcx(), def_id.resolved_id(), "smt2", &task).unwrap();
607 }
608
609 let result = Self::run_task_with_cache(self.genv, task, def_id.resolved_id(), kind, cache);
610 let solution = if config::dump_checker_trace_info() {
611 self.parse_kvar_solutions(&result)?
612 } else {
613 HashMap::default()
614 };
615
616 let errors = match result.status {
617 FixpointStatus::Safe(_) => vec![],
618 FixpointStatus::Unsafe(_, errors) => {
619 metrics::incr_metric(Metric::CsError, errors.len() as u32);
620 errors
621 .into_iter()
622 .map(|err| self.tags[err.tag])
623 .unique()
624 .collect_vec()
625 }
626 FixpointStatus::Crash(err) => span_bug!(def_span, "fixpoint crash: {err:?}"),
627 };
628 Ok(Answer { errors, solution })
629 }
630
631 fn parse_kvar_solutions(&self, result: &FixpointResult<TagIdx>) -> QueryResult<Solution> {
632 Ok(self.kcx.group_kvar_solution(
633 result
634 .solution
635 .iter()
636 .chain(&result.non_cuts_solution)
637 .map(|b| self.convert_kvar_bind(b))
638 .try_collect_vec()?,
639 ))
640 }
641
642 fn convert_solution(&self, expr: &str) -> QueryResult<rty::Binder<rty::Expr>> {
643 let mut sexp_parser = Parser::new(expr);
645 let sexp = match sexp_parser.parse() {
646 Ok(sexp) => sexp,
647 Err(err) => {
648 tracked_span_bug!("cannot parse sexp: {expr:?}: {err:?}");
649 }
650 };
651
652 let mut sexp_ctx = SexpParseCtxt.into_wrapper();
654 let (sorts, expr) = sexp_ctx.parse_solution(&sexp).unwrap_or_else(|err| {
655 tracked_span_bug!("failed to parse solution sexp {sexp:?}: {err:?}");
656 });
657
658 let sorts = sorts
660 .iter()
661 .map(|s| self.fixpoint_to_sort(s))
662 .try_collect_vec()
663 .unwrap_or_else(|err| {
664 tracked_span_bug!("failed to convert sorts: {err:?}");
665 });
666 let expr = self
667 .fixpoint_to_expr(&expr)
668 .unwrap_or_else(|err| tracked_span_bug!("failed to convert expr: {err:?}"));
669 Ok(rty::Binder::bind_with_sorts(expr, &sorts))
670 }
671
672 fn convert_kvar_bind(
673 &self,
674 b: &liquid_fixpoint::KVarBind,
675 ) -> QueryResult<(fixpoint::KVid, rty::Binder<rty::Expr>)> {
676 let kvid = parse_kvid(&b.kvar);
677 let expr = self.convert_solution(&b.val)?;
678 Ok((kvid, expr))
679 }
680
681 pub fn generate_and_check_lean_lemmas(
682 mut self,
683 constraint: fixpoint::Constraint,
684 ) -> QueryResult<()> {
685 if let Some(def_id) = self.ecx.def_id {
686 let kvar_decls = self.kcx.encode_kvars(&self.kvars, &mut self.scx);
687 self.ecx.errors.to_result()?;
688
689 let lean_encoder = LeanEncoder::new(self.genv, self.ecx.local_var_env.pretty_var_map);
690 lean_encoder
691 .encode_constraint(def_id, &kvar_decls, &constraint)
692 .map_err(|_| query_bug!("could not encode constraint"))?;
693
694 if flux_config::lean().is_check() { lean_encoder.check_proof(def_id) } else { Ok(()) }
695 } else {
696 Ok(())
697 }
698 }
699
700 fn run_task_with_cache(
701 genv: GlobalEnv,
702 task: fixpoint::Task,
703 def_id: DefId,
704 kind: FixpointQueryKind,
705 cache: &mut FixQueryCache,
706 ) -> FixpointResult<TagIdx> {
707 let key = kind.task_key(genv.tcx(), def_id);
708
709 let hash = task.hash_with_default();
710
711 if config::is_cache_enabled()
712 && let Some(result) = cache.lookup(&key, hash)
713 {
714 metrics::incr_metric_if(kind.is_body(), Metric::FnCached);
715 return result.clone();
716 }
717 let result = metrics::time_it(TimingKind::FixpointQuery(def_id, kind), || {
718 task.run()
719 .unwrap_or_else(|err| tracked_span_bug!("failed to run fixpoint: {err}"))
720 });
721
722 if config::is_cache_enabled() {
723 cache.insert(key, hash, result.clone());
724 }
725
726 result
727 }
728
729 fn tag_idx(&mut self, tag: Tag) -> TagIdx
730 where
731 Tag: std::fmt::Debug,
732 {
733 *self.tags_inv.entry(tag).or_insert_with(|| {
734 let idx = self.tags.push(tag);
735 self.comments.push(format!("Tag {idx}: {tag:?}"));
736 idx
737 })
738 }
739
740 pub(crate) fn with_early_param(&mut self, param: &EarlyReftParam) {
741 self.ecx
742 .local_var_env
743 .pretty_var_map
744 .set(PrettyVar::Param(*param), Some(param.name));
745 }
746
747 pub(crate) fn with_name_map<R>(
748 &mut self,
749 name: rty::Name,
750 provenance: NameProvenance,
751 f: impl FnOnce(&mut Self, fixpoint::LocalVar) -> R,
752 ) -> R {
753 let fresh = self.ecx.local_var_env.insert_fvar_map(name, provenance);
754 let r = f(self, fresh);
755 self.ecx.local_var_env.remove_fvar_map(name);
756 r
757 }
758
759 pub(crate) fn sort_to_fixpoint(&mut self, sort: &rty::Sort) -> fixpoint::Sort {
760 self.scx.sort_to_fixpoint(sort)
761 }
762
763 pub(crate) fn var_to_fixpoint(&self, var: &rty::Var) -> fixpoint::Var {
764 self.ecx.var_to_fixpoint(var)
765 }
766
767 pub(crate) fn head_to_fixpoint(
772 &mut self,
773 expr: &rty::Expr,
774 mk_tag: impl Fn(Option<ESpan>) -> Tag + Copy,
775 ) -> QueryResult<fixpoint::Constraint>
776 where
777 Tag: std::fmt::Debug,
778 {
779 match expr.kind() {
780 rty::ExprKind::BinaryOp(rty::BinOp::And, ..) => {
781 let cstrs = expr
783 .flatten_conjs()
784 .into_iter()
785 .map(|e| self.head_to_fixpoint(e, mk_tag))
786 .try_collect()?;
787 Ok(fixpoint::Constraint::conj(cstrs))
788 }
789 rty::ExprKind::BinaryOp(rty::BinOp::Imp, e1, e2) => {
790 let (bindings, assumption) = self.assumption_to_fixpoint(e1)?;
791 let cstr = self.head_to_fixpoint(e2, mk_tag)?;
792 Ok(fixpoint::Constraint::foralls(bindings, mk_implies(assumption, cstr)))
793 }
794 rty::ExprKind::KVar(kvar) => {
795 let mut bindings = vec![];
796 let pred = self.kvar_to_fixpoint(kvar, &mut bindings)?;
797 Ok(fixpoint::Constraint::foralls(bindings, fixpoint::Constraint::Pred(pred, None)))
798 }
799 rty::ExprKind::ForAll(pred) => {
800 self.ecx
801 .local_var_env
802 .push_layer_with_fresh_names(pred.vars().len());
803 let cstr = self.head_to_fixpoint(pred.as_ref().skip_binder(), mk_tag)?;
804 let vars = self.ecx.local_var_env.pop_layer();
805
806 let bindings = iter::zip(vars, pred.vars())
807 .map(|(var, kind)| {
808 fixpoint::Bind {
809 name: var.into(),
810 sort: self.scx.sort_to_fixpoint(kind.expect_sort()),
811 pred: fixpoint::Pred::TRUE,
812 }
813 })
814 .collect_vec();
815
816 Ok(fixpoint::Constraint::foralls(bindings, cstr))
817 }
818 _ => {
819 let tag_idx = self.tag_idx(mk_tag(expr.span()));
820 let pred = fixpoint::Pred::Expr(self.ecx.expr_to_fixpoint(expr, &mut self.scx)?);
821 Ok(fixpoint::Constraint::Pred(pred, Some(tag_idx)))
822 }
823 }
824 }
825
826 pub(crate) fn assumption_to_fixpoint(
831 &mut self,
832 pred: &rty::Expr,
833 ) -> QueryResult<(Vec<fixpoint::Bind>, fixpoint::Pred)> {
834 let mut bindings = vec![];
835 let mut preds = vec![];
836 self.assumption_to_fixpoint_aux(pred, &mut bindings, &mut preds)?;
837 Ok((bindings, fixpoint::Pred::and(preds)))
838 }
839
840 fn assumption_to_fixpoint_aux(
842 &mut self,
843 expr: &rty::Expr,
844 bindings: &mut Vec<fixpoint::Bind>,
845 preds: &mut Vec<fixpoint::Pred>,
846 ) -> QueryResult {
847 match expr.kind() {
848 rty::ExprKind::BinaryOp(rty::BinOp::And, e1, e2) => {
849 self.assumption_to_fixpoint_aux(e1, bindings, preds)?;
850 self.assumption_to_fixpoint_aux(e2, bindings, preds)?;
851 }
852 rty::ExprKind::KVar(kvar) => {
853 preds.push(self.kvar_to_fixpoint(kvar, bindings)?);
854 }
855 rty::ExprKind::ForAll(_) => {
856 preds.push(fixpoint::Pred::TRUE);
862 }
863 _ => {
864 preds.push(fixpoint::Pred::Expr(self.ecx.expr_to_fixpoint(expr, &mut self.scx)?));
865 }
866 }
867 Ok(())
868 }
869
870 fn kvar_to_fixpoint(
871 &mut self,
872 kvar: &rty::KVar,
873 bindings: &mut Vec<fixpoint::Bind>,
874 ) -> QueryResult<fixpoint::Pred> {
875 let decl = self.kvars.get(kvar.kvid);
876 let kvids = self.kcx.declare(kvar.kvid, decl, &self.ecx.backend);
877
878 let all_args = iter::zip(&kvar.args, &decl.sorts)
879 .map(|(arg, sort)| self.ecx.imm(arg, sort, &mut self.scx, bindings))
880 .try_collect_vec()?;
881
882 if all_args.is_empty() {
886 let fresh = self.ecx.local_var_env.fresh_name();
887 let var = fixpoint::Var::Local(fresh);
888 bindings.push(fixpoint::Bind {
889 name: fresh.into(),
890 sort: fixpoint::Sort::Int,
891 pred: fixpoint::Pred::Expr(fixpoint::Expr::eq(
892 fixpoint::Expr::Var(var),
893 fixpoint::Expr::int(0),
894 )),
895 });
896 return Ok(fixpoint::Pred::KVar(kvids.start, vec![fixpoint::Expr::Var(var)]));
897 }
898
899 let kvars = kvids
900 .enumerate()
901 .map(|(i, kvid)| {
902 let args = all_args[i..].to_vec();
903 fixpoint::Pred::KVar(kvid, args)
904 })
905 .collect_vec();
906
907 Ok(fixpoint::Pred::And(kvars))
908 }
909}
910
911fn const_to_fixpoint(cst: rty::Constant) -> fixpoint::Expr {
912 match cst {
913 rty::Constant::Int(i) => {
914 if i.is_negative() {
915 fixpoint::Expr::Neg(Box::new(fixpoint::Constant::Numeral(i.abs()).into()))
916 } else {
917 fixpoint::Constant::Numeral(i.abs()).into()
918 }
919 }
920 rty::Constant::Real(r) => fixpoint::Constant::Real(r.0).into(),
921 rty::Constant::Bool(b) => fixpoint::Constant::Boolean(b).into(),
922 rty::Constant::Char(c) => fixpoint::Constant::Numeral(u128::from(c)).into(),
923 rty::Constant::Str(s) => fixpoint::Constant::String(fixpoint::SymStr(s)).into(),
924 rty::Constant::BitVec(i, size) => fixpoint::Constant::BitVec(i, size).into(),
925 }
926}
927
928#[derive(Default)]
933struct KVarEncodingCtxt {
934 ranges: FxIndexMap<rty::KVid, Range<fixpoint::KVid>>,
937}
938
939impl KVarEncodingCtxt {
940 fn declare(
943 &mut self,
944 kvid: rty::KVid,
945 decl: &KVarDecl,
946 backend: &Backend,
947 ) -> Range<fixpoint::KVid> {
948 let start = self
950 .ranges
951 .last()
952 .map_or(fixpoint::KVid::from_u32(0), |(_, r)| r.end);
953
954 self.ranges
955 .entry(kvid)
956 .or_insert_with(|| {
957 let single_encoding = matches!(decl.encoding, KVarEncoding::Single)
958 || matches!(backend, Backend::Lean);
959 if single_encoding {
960 start..start + 1
961 } else {
962 let n = usize::max(decl.self_args, 1);
963 start..start + n
964 }
965 })
966 .clone()
967 }
968
969 fn encode_kvars(&self, kvars: &KVarGen, scx: &mut SortEncodingCtxt) -> Vec<fixpoint::KVarDecl> {
970 self.ranges
971 .iter()
972 .flat_map(|(orig, range)| {
973 let mut all_sorts = kvars
974 .get(*orig)
975 .sorts
976 .iter()
977 .map(|s| scx.sort_to_fixpoint(s))
978 .collect_vec();
979
980 if all_sorts.is_empty() {
982 all_sorts = vec![fixpoint::Sort::Int];
983 }
984
985 range.clone().enumerate().map(move |(i, kvid)| {
986 let sorts = all_sorts[i..].to_vec();
987 fixpoint::KVarDecl::new(kvid, sorts, format!("orig: {:?}", orig))
988 })
989 })
990 .collect()
991 }
992
993 fn group_kvar_solution(
1000 &self,
1001 mut items: Vec<(fixpoint::KVid, rty::Binder<rty::Expr>)>,
1002 ) -> HashMap<rty::KVid, rty::Binder<rty::Expr>> {
1003 let mut map = HashMap::default();
1004
1005 items.sort_by_key(|(kvid, _)| *kvid);
1006 items.reverse();
1007
1008 for (orig, range) in &self.ranges {
1009 let mut preds = vec![];
1010 while let Some((_, t)) = items.pop_if(|(k, _)| range.contains(k)) {
1011 preds.push(t);
1012 }
1013 if preds.len() == range.end.as_usize() - range.start.as_usize() {
1015 let vars = preds[0].vars().clone();
1016 let conj = rty::Expr::and_from_iter(
1017 preds
1018 .into_iter()
1019 .enumerate()
1020 .map(|(i, e)| e.skip_binder().shift_horizontally(i)),
1021 );
1022 map.insert(*orig, rty::Binder::bind_with_vars(conj, vars));
1023 }
1024 }
1025 map
1026 }
1027}
1028
1029struct LocalVarEnv {
1031 local_var_gen: IndexGen<fixpoint::LocalVar>,
1032 fvars: UnordMap<rty::Name, fixpoint::LocalVar>,
1033 layers: Vec<Vec<fixpoint::LocalVar>>,
1035 reverse_map: UnordMap<fixpoint::LocalVar, rty::Expr>,
1040 pretty_var_map: PrettyMap<fixpoint::LocalVar>,
1041}
1042
1043impl LocalVarEnv {
1044 fn new() -> Self {
1045 Self {
1046 local_var_gen: IndexGen::new(),
1047 fvars: Default::default(),
1048 layers: Vec::new(),
1049 reverse_map: Default::default(),
1050 pretty_var_map: PrettyMap::new(),
1051 }
1052 }
1053
1054 fn fresh_name(&mut self) -> fixpoint::LocalVar {
1057 self.local_var_gen.fresh()
1058 }
1059
1060 fn insert_fvar_map(
1061 &mut self,
1062 name: rty::Name,
1063 provenance: NameProvenance,
1064 ) -> fixpoint::LocalVar {
1065 let fresh = self.fresh_name();
1066 self.fvars.insert(name, fresh);
1067 self.reverse_map.insert(fresh, rty::Expr::fvar(name));
1068 self.pretty_var_map
1069 .set(PrettyVar::Local(fresh), provenance.opt_symbol());
1070 fresh
1071 }
1072
1073 fn remove_fvar_map(&mut self, name: rty::Name) {
1074 self.fvars.remove(&name);
1075 }
1076
1077 fn push_layer_with_fresh_names(&mut self, count: usize) {
1079 let layer = (0..count).map(|_| self.fresh_name()).collect();
1080 self.layers.push(layer);
1081 }
1083
1084 fn pop_layer(&mut self) -> Vec<fixpoint::LocalVar> {
1085 self.layers.pop().unwrap()
1086 }
1087
1088 fn get_fvar(&self, name: rty::Name) -> Option<fixpoint::LocalVar> {
1089 self.fvars.get(&name).copied()
1090 }
1091
1092 fn get_late_bvar(&self, debruijn: DebruijnIndex, var: BoundVar) -> Option<fixpoint::LocalVar> {
1093 let depth = self.layers.len().checked_sub(debruijn.as_usize() + 1)?;
1094 self.layers[depth].get(var.as_usize()).copied()
1095 }
1096}
1097
1098pub struct KVarGen {
1099 kvars: IndexVec<rty::KVid, KVarDecl>,
1100 dummy: bool,
1105}
1106
1107impl KVarGen {
1108 pub(crate) fn new(dummy: bool) -> Self {
1109 Self { kvars: IndexVec::new(), dummy }
1110 }
1111
1112 fn get(&self, kvid: rty::KVid) -> &KVarDecl {
1113 &self.kvars[kvid]
1114 }
1115
1116 pub fn fresh(
1135 &mut self,
1136 binders: &[rty::BoundVariableKinds],
1137 scope: impl IntoIterator<Item = (rty::Var, rty::Sort)>,
1138 encoding: KVarEncoding,
1139 ) -> rty::Expr {
1140 if self.dummy {
1141 return rty::Expr::hole(rty::HoleKind::Pred);
1142 }
1143
1144 let args = itertools::chain(
1145 binders.iter().rev().enumerate().flat_map(|(level, vars)| {
1146 let debruijn = DebruijnIndex::from_usize(level);
1147 vars.iter()
1148 .cloned()
1149 .enumerate()
1150 .flat_map(move |(idx, var)| {
1151 if let rty::BoundVariableKind::Refine(sort, _, kind) = var {
1152 let br = rty::BoundReft { var: BoundVar::from_usize(idx), kind };
1153 Some((rty::Var::Bound(debruijn, br), sort))
1154 } else {
1155 None
1156 }
1157 })
1158 }),
1159 scope,
1160 );
1161 let [.., last] = binders else {
1162 return self.fresh_inner(0, [], encoding);
1163 };
1164 let num_self_args = last
1165 .iter()
1166 .filter(|var| matches!(var, rty::BoundVariableKind::Refine(..)))
1167 .count();
1168 self.fresh_inner(num_self_args, args, encoding)
1169 }
1170
1171 fn fresh_inner<A>(&mut self, self_args: usize, args: A, encoding: KVarEncoding) -> rty::Expr
1172 where
1173 A: IntoIterator<Item = (rty::Var, rty::Sort)>,
1174 {
1175 let mut sorts = vec![];
1177 let mut exprs = vec![];
1178
1179 let mut flattened_self_args = 0;
1180 for (i, (var, sort)) in args.into_iter().enumerate() {
1181 let is_self_arg = i < self_args;
1182 let var = var.to_expr();
1183 sort.walk(|sort, proj| {
1184 if !matches!(sort, rty::Sort::Loc) {
1185 flattened_self_args += is_self_arg as usize;
1186 sorts.push(sort.clone());
1187 exprs.push(rty::Expr::field_projs(&var, proj));
1188 }
1189 });
1190 }
1191
1192 let kvid = self
1193 .kvars
1194 .push(KVarDecl { self_args: flattened_self_args, sorts, encoding });
1195
1196 let kvar = rty::KVar::new(kvid, flattened_self_args, exprs);
1197 rty::Expr::kvar(kvar)
1198 }
1199}
1200
1201#[derive(Clone)]
1202struct KVarDecl {
1203 self_args: usize,
1204 sorts: Vec<rty::Sort>,
1205 encoding: KVarEncoding,
1206}
1207
1208#[derive(Clone, Copy)]
1210pub enum KVarEncoding {
1211 Single,
1214 Conj,
1218}
1219
1220impl std::fmt::Display for TagIdx {
1221 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1222 write!(f, "{}", self.as_u32())
1223 }
1224}
1225
1226impl std::str::FromStr for TagIdx {
1227 type Err = std::num::ParseIntError;
1228
1229 fn from_str(s: &str) -> Result<Self, Self::Err> {
1230 Ok(Self::from_u32(s.parse()?))
1231 }
1232}
1233
1234#[derive(Default)]
1235struct ConstEnv<'tcx> {
1236 const_map: ConstMap<'tcx>,
1237 const_map_rev: HashMap<fixpoint::GlobalVar, ConstKey<'tcx>>,
1238 global_var_gen: IndexGen<fixpoint::GlobalVar>,
1239 pub fun_def_map: FunDefMap,
1240}
1241
1242impl<'tcx> ConstEnv<'tcx> {
1243 fn get_or_insert(
1244 &mut self,
1245 key: ConstKey<'tcx>,
1246 make_const_decl: impl FnOnce(fixpoint::GlobalVar) -> fixpoint::ConstDecl,
1248 ) -> &fixpoint::ConstDecl {
1249 self.const_map.entry(key.clone()).or_insert_with(|| {
1250 let global_name = self.global_var_gen.fresh();
1251 self.const_map_rev.insert(global_name, key);
1252 make_const_decl(global_name)
1253 })
1254 }
1255}
1256
1257pub struct ExprEncodingCtxt<'genv, 'tcx> {
1258 genv: GlobalEnv<'genv, 'tcx>,
1259 local_var_env: LocalVarEnv,
1260 const_env: ConstEnv<'tcx>,
1261 errors: Errors<'genv>,
1262 def_id: Option<MaybeExternId>,
1265 infcx: rustc_infer::infer::InferCtxt<'tcx>,
1266 backend: Backend,
1267}
1268
1269impl<'genv, 'tcx> ExprEncodingCtxt<'genv, 'tcx> {
1270 pub fn new(
1271 genv: GlobalEnv<'genv, 'tcx>,
1272 def_id: Option<MaybeExternId>,
1273 backend: Backend,
1274 ) -> Self {
1275 Self {
1276 genv,
1277 local_var_env: LocalVarEnv::new(),
1278 const_env: Default::default(),
1279 errors: Errors::new(genv.sess()),
1280 def_id,
1281 infcx: genv
1282 .tcx()
1283 .infer_ctxt()
1284 .with_next_trait_solver(true)
1285 .build(TypingMode::non_body_analysis()),
1286 backend,
1287 }
1288 }
1289
1290 fn def_span(&self) -> Span {
1291 self.def_id
1292 .map_or(DUMMY_SP, |def_id| self.genv.tcx().def_span(def_id))
1293 }
1294
1295 fn var_to_fixpoint(&self, var: &rty::Var) -> fixpoint::Var {
1296 match var {
1297 rty::Var::Free(name) => {
1298 self.local_var_env
1299 .get_fvar(*name)
1300 .unwrap_or_else(|| {
1301 span_bug!(self.def_span(), "no entry found for name: `{name:?}`")
1302 })
1303 .into()
1304 }
1305 rty::Var::Bound(debruijn, breft) => {
1306 self.local_var_env
1307 .get_late_bvar(*debruijn, breft.var)
1308 .unwrap_or_else(|| {
1309 span_bug!(self.def_span(), "no entry found for late bound var: `{breft:?}`")
1310 })
1311 .into()
1312 }
1313 rty::Var::ConstGeneric(param) => fixpoint::Var::ConstGeneric(*param),
1314 rty::Var::EarlyParam(param) => fixpoint::Var::Param(*param),
1315 rty::Var::EVar(_) => {
1316 span_bug!(self.def_span(), "unexpected evar: `{var:?}`")
1317 }
1318 }
1319 }
1320
1321 fn variant_to_fixpoint(
1322 &self,
1323 scx: &mut SortEncodingCtxt,
1324 enum_def_id: &DefId,
1325 idx: VariantIdx,
1326 ) -> fixpoint::Var {
1327 let adt_id = scx.declare_adt(*enum_def_id);
1328 fixpoint::Var::DataCtor(adt_id, idx)
1329 }
1330
1331 fn struct_fields_to_fixpoint(
1332 &mut self,
1333 did: &DefId,
1334 flds: &[rty::Expr],
1335 scx: &mut SortEncodingCtxt,
1336 ) -> QueryResult<fixpoint::Expr> {
1337 if let [fld] = flds {
1339 self.expr_to_fixpoint(fld, scx)
1340 } else {
1341 let adt_id = scx.declare_adt(*did);
1342 let ctor = fixpoint::Expr::Var(fixpoint::Var::DataCtor(adt_id, VariantIdx::ZERO));
1343 let args = flds
1344 .iter()
1345 .map(|fld| self.expr_to_fixpoint(fld, scx))
1346 .try_collect()?;
1347 Ok(fixpoint::Expr::App(Box::new(ctor), None, args))
1348 }
1349 }
1350
1351 fn fields_to_fixpoint(
1352 &mut self,
1353 flds: &[rty::Expr],
1354 scx: &mut SortEncodingCtxt,
1355 ) -> QueryResult<fixpoint::Expr> {
1356 if let [fld] = flds {
1358 self.expr_to_fixpoint(fld, scx)
1359 } else {
1360 scx.declare_tuple(flds.len());
1361 let ctor = fixpoint::Expr::Var(fixpoint::Var::TupleCtor { arity: flds.len() });
1362 let args = flds
1363 .iter()
1364 .map(|fld| self.expr_to_fixpoint(fld, scx))
1365 .try_collect()?;
1366 Ok(fixpoint::Expr::App(Box::new(ctor), None, args))
1367 }
1368 }
1369
1370 fn internal_func_to_fixpoint(
1371 &mut self,
1372 internal_func: &InternalFuncKind,
1373 sort_args: &[rty::SortArg],
1374 args: &[rty::Expr],
1375 scx: &mut SortEncodingCtxt,
1376 ) -> QueryResult<fixpoint::Expr> {
1377 match internal_func {
1378 InternalFuncKind::Val(op) => {
1379 let func = fixpoint::Expr::Var(self.define_const_for_prim_op(op, scx));
1380 let args = self.exprs_to_fixpoint(args, scx)?;
1381 Ok(fixpoint::Expr::App(Box::new(func), None, args))
1382 }
1383 InternalFuncKind::Rel(op) => {
1384 let expr = if let Some(prim_rel) = self.genv.prim_rel_for(op)? {
1385 prim_rel.body.replace_bound_refts(args)
1386 } else {
1387 rty::Expr::tt()
1388 };
1389 self.expr_to_fixpoint(&expr, scx)
1390 }
1391 InternalFuncKind::Cast => {
1392 let [rty::SortArg::Sort(from), rty::SortArg::Sort(to)] = &sort_args else {
1393 span_bug!(self.def_span(), "unexpected cast")
1394 };
1395 match from.cast_kind(to) {
1396 rty::CastKind::Identity => self.expr_to_fixpoint(&args[0], scx),
1397 rty::CastKind::BoolToInt => {
1398 Ok(fixpoint::Expr::IfThenElse(Box::new([
1399 self.expr_to_fixpoint(&args[0], scx)?,
1400 fixpoint::Expr::int(1),
1401 fixpoint::Expr::int(0),
1402 ])))
1403 }
1404 rty::CastKind::IntoUnit => self.expr_to_fixpoint(&rty::Expr::unit(), scx),
1405 rty::CastKind::Uninterpreted => {
1406 let func = fixpoint::Expr::Var(self.define_const_for_cast(from, to, scx));
1407 let args = self.exprs_to_fixpoint(args, scx)?;
1408 Ok(fixpoint::Expr::App(Box::new(func), None, args))
1409 }
1410 }
1411 }
1412 }
1413 }
1414
1415 fn structurally_normalize_expr(&self, expr: &rty::Expr) -> QueryResult<rty::Expr> {
1416 if let Some(def_id) = self.def_id {
1417 structurally_normalize_expr(self.genv, def_id.resolved_id(), &self.infcx, expr)
1418 } else {
1419 Ok(expr.clone())
1420 }
1421 }
1422
1423 fn expr_to_fixpoint(
1424 &mut self,
1425 expr: &rty::Expr,
1426 scx: &mut SortEncodingCtxt,
1427 ) -> QueryResult<fixpoint::Expr> {
1428 let expr = self.structurally_normalize_expr(expr)?;
1429 let e = match expr.kind() {
1430 rty::ExprKind::Var(var) => fixpoint::Expr::Var(self.var_to_fixpoint(var)),
1431 rty::ExprKind::Constant(c) => const_to_fixpoint(*c),
1432 rty::ExprKind::BinaryOp(op, e1, e2) => self.bin_op_to_fixpoint(op, e1, e2, scx)?,
1433 rty::ExprKind::UnaryOp(op, e) => self.un_op_to_fixpoint(*op, e, scx)?,
1434 rty::ExprKind::FieldProj(e, proj) => self.proj_to_fixpoint(e, *proj, scx)?,
1435 rty::ExprKind::Tuple(flds) => self.fields_to_fixpoint(flds, scx)?,
1436 rty::ExprKind::Ctor(rty::Ctor::Struct(did), flds) => {
1437 self.struct_fields_to_fixpoint(did, flds, scx)?
1438 }
1439 rty::ExprKind::IsCtor(def_id, variant_idx, e) => {
1440 let ctor = self.variant_to_fixpoint(scx, def_id, *variant_idx);
1441 let e = self.expr_to_fixpoint(e, scx)?;
1442 fixpoint::Expr::IsCtor(ctor, Box::new(e))
1443 }
1444 rty::ExprKind::Ctor(rty::Ctor::Enum(did, idx), args) => {
1445 let ctor = self.variant_to_fixpoint(scx, did, *idx);
1446 let args = self.exprs_to_fixpoint(args, scx)?;
1447 fixpoint::Expr::App(Box::new(fixpoint::Expr::Var(ctor)), None, args)
1448 }
1449 rty::ExprKind::ConstDefId(did) => {
1450 let var = self.define_const_for_rust_const(*did, scx);
1451 fixpoint::Expr::Var(var)
1452 }
1453 rty::ExprKind::App(func, sort_args, args) => {
1454 if let rty::ExprKind::InternalFunc(func) = func.kind() {
1455 self.internal_func_to_fixpoint(func, sort_args, args, scx)?
1456 } else {
1457 let func = self.expr_to_fixpoint(func, scx)?;
1458 let sort_args = self.sort_args_to_fixpoint(sort_args, scx);
1459 let args = self.exprs_to_fixpoint(args, scx)?;
1460 fixpoint::Expr::App(Box::new(func), Some(sort_args), args)
1461 }
1462 }
1463 rty::ExprKind::IfThenElse(p, e1, e2) => {
1464 fixpoint::Expr::IfThenElse(Box::new([
1465 self.expr_to_fixpoint(p, scx)?,
1466 self.expr_to_fixpoint(e1, scx)?,
1467 self.expr_to_fixpoint(e2, scx)?,
1468 ]))
1469 }
1470 rty::ExprKind::Alias(alias_reft, args) => {
1471 let sort = self.genv.sort_of_assoc_reft(alias_reft.assoc_id)?;
1472 let sort = sort.instantiate_identity();
1473 let func =
1474 fixpoint::Expr::Var(self.define_const_for_alias_reft(alias_reft, sort, scx));
1475 let args = args
1476 .iter()
1477 .map(|expr| self.expr_to_fixpoint(expr, scx))
1478 .try_collect()?;
1479 fixpoint::Expr::App(Box::new(func), None, args)
1480 }
1481 rty::ExprKind::Abs(lam) => {
1482 let var = self.define_const_for_lambda(lam, scx);
1483 fixpoint::Expr::Var(var)
1484 }
1485 rty::ExprKind::Let(init, body) => {
1486 debug_assert_eq!(body.vars().len(), 1);
1487 let init = self.expr_to_fixpoint(init, scx)?;
1488
1489 self.local_var_env.push_layer_with_fresh_names(1);
1490 let body = self.expr_to_fixpoint(body.skip_binder_ref(), scx)?;
1491 let vars = self.local_var_env.pop_layer();
1492
1493 fixpoint::Expr::Let(vars[0].into(), Box::new([init, body]))
1494 }
1495 rty::ExprKind::GlobalFunc(SpecFuncKind::Thy(itf)) => fixpoint::Expr::ThyFunc(*itf),
1496 rty::ExprKind::GlobalFunc(SpecFuncKind::Uif(def_id)) => {
1497 fixpoint::Expr::Var(self.define_const_for_uif(*def_id, scx))
1498 }
1499 rty::ExprKind::GlobalFunc(SpecFuncKind::Def(def_id)) => {
1500 fixpoint::Expr::Var(self.declare_fun(*def_id))
1501 }
1502 rty::ExprKind::Hole(..)
1503 | rty::ExprKind::KVar(_)
1504 | rty::ExprKind::Local(_)
1505 | rty::ExprKind::PathProj(..)
1506 | rty::ExprKind::ForAll(_)
1507 | rty::ExprKind::Exists(_)
1508 | rty::ExprKind::InternalFunc(_) => {
1509 span_bug!(self.def_span(), "unexpected expr: `{expr:?}`")
1510 }
1511 rty::ExprKind::BoundedQuant(kind, rng, body) => {
1512 let exprs = (rng.start..rng.end).map(|i| {
1513 let arg = rty::Expr::constant(rty::Constant::from(i));
1514 body.replace_bound_reft(&arg)
1515 });
1516 let expr = match kind {
1517 flux_middle::fhir::QuantKind::Forall => rty::Expr::and_from_iter(exprs),
1518 flux_middle::fhir::QuantKind::Exists => rty::Expr::or_from_iter(exprs),
1519 };
1520 self.expr_to_fixpoint(&expr, scx)?
1521 }
1522 };
1523 Ok(e)
1524 }
1525
1526 fn sort_args_to_fixpoint(
1527 &mut self,
1528 sort_args: &[rty::SortArg],
1529 scx: &mut SortEncodingCtxt,
1530 ) -> Vec<fixpoint::Sort> {
1531 sort_args
1532 .iter()
1533 .map(|s_arg| self.sort_arg_to_fixpoint(s_arg, scx))
1534 .collect()
1535 }
1536
1537 fn sort_arg_to_fixpoint(
1538 &mut self,
1539 sort_arg: &rty::SortArg,
1540 scx: &mut SortEncodingCtxt,
1541 ) -> fixpoint::Sort {
1542 match sort_arg {
1543 rty::SortArg::Sort(sort) => scx.sort_to_fixpoint(sort),
1544 rty::SortArg::BvSize(sz) => bv_size_to_fixpoint(*sz),
1545 }
1546 }
1547
1548 fn exprs_to_fixpoint<'b>(
1549 &mut self,
1550 exprs: impl IntoIterator<Item = &'b rty::Expr>,
1551 scx: &mut SortEncodingCtxt,
1552 ) -> QueryResult<Vec<fixpoint::Expr>> {
1553 exprs
1554 .into_iter()
1555 .map(|e| self.expr_to_fixpoint(e, scx))
1556 .try_collect()
1557 }
1558
1559 fn proj_to_fixpoint(
1560 &mut self,
1561 e: &rty::Expr,
1562 proj: rty::FieldProj,
1563 scx: &mut SortEncodingCtxt,
1564 ) -> QueryResult<fixpoint::Expr> {
1565 let arity = proj.arity(self.genv)?;
1566 if arity == 1 {
1568 self.expr_to_fixpoint(e, scx)
1569 } else {
1570 let proj = match proj {
1571 rty::FieldProj::Tuple { arity, field } => {
1572 scx.declare_tuple(arity);
1573 fixpoint::Var::TupleProj { arity, field }
1574 }
1575 rty::FieldProj::Adt { def_id, field } => {
1576 let adt_id = scx.declare_adt(def_id);
1577 fixpoint::Var::DataProj { adt_id, field }
1578 }
1579 };
1580 let proj = fixpoint::Expr::Var(proj);
1581 Ok(fixpoint::Expr::App(Box::new(proj), None, vec![self.expr_to_fixpoint(e, scx)?]))
1582 }
1583 }
1584
1585 fn un_op_to_fixpoint(
1586 &mut self,
1587 op: rty::UnOp,
1588 e: &rty::Expr,
1589 scx: &mut SortEncodingCtxt,
1590 ) -> QueryResult<fixpoint::Expr> {
1591 match op {
1592 rty::UnOp::Not => Ok(fixpoint::Expr::Not(Box::new(self.expr_to_fixpoint(e, scx)?))),
1593 rty::UnOp::Neg => Ok(fixpoint::Expr::Neg(Box::new(self.expr_to_fixpoint(e, scx)?))),
1594 }
1595 }
1596
1597 fn bv_rel_to_fixpoint(&self, rel: &fixpoint::BinRel) -> fixpoint::Expr {
1598 let itf = match rel {
1599 fixpoint::BinRel::Gt => fixpoint::ThyFunc::BvUgt,
1600 fixpoint::BinRel::Ge => fixpoint::ThyFunc::BvUge,
1601 fixpoint::BinRel::Lt => fixpoint::ThyFunc::BvUlt,
1602 fixpoint::BinRel::Le => fixpoint::ThyFunc::BvUle,
1603 _ => span_bug!(self.def_span(), "not a bitvector relation!"),
1604 };
1605 fixpoint::Expr::ThyFunc(itf)
1606 }
1607
1608 fn set_op_to_fixpoint(&self, op: &rty::BinOp) -> fixpoint::Expr {
1609 let itf = match op {
1610 rty::BinOp::Sub(_) => fixpoint::ThyFunc::SetDif,
1611 rty::BinOp::BitAnd(_) => fixpoint::ThyFunc::SetCap,
1612 rty::BinOp::BitOr(_) => fixpoint::ThyFunc::SetCup,
1613 _ => span_bug!(self.def_span(), "not a set operation!"),
1614 };
1615 fixpoint::Expr::ThyFunc(itf)
1616 }
1617
1618 fn bv_op_to_fixpoint(&self, op: &rty::BinOp) -> fixpoint::Expr {
1619 let itf = match op {
1620 rty::BinOp::Add(_) => fixpoint::ThyFunc::BvAdd,
1621 rty::BinOp::Sub(_) => fixpoint::ThyFunc::BvSub,
1622 rty::BinOp::Mul(_) => fixpoint::ThyFunc::BvMul,
1623 rty::BinOp::Div(_) => fixpoint::ThyFunc::BvUdiv,
1624 rty::BinOp::Mod(_) => fixpoint::ThyFunc::BvUrem,
1625 rty::BinOp::BitAnd(_) => fixpoint::ThyFunc::BvAnd,
1626 rty::BinOp::BitOr(_) => fixpoint::ThyFunc::BvOr,
1627 rty::BinOp::BitXor(_) => fixpoint::ThyFunc::BvXor,
1628 rty::BinOp::BitShl(_) => fixpoint::ThyFunc::BvShl,
1629 rty::BinOp::BitShr(_) => fixpoint::ThyFunc::BvLshr,
1630 _ => span_bug!(self.def_span(), "not a bitvector operation!"),
1631 };
1632 fixpoint::Expr::ThyFunc(itf)
1633 }
1634
1635 fn bin_op_to_fixpoint(
1636 &mut self,
1637 op: &rty::BinOp,
1638 e1: &rty::Expr,
1639 e2: &rty::Expr,
1640 scx: &mut SortEncodingCtxt,
1641 ) -> QueryResult<fixpoint::Expr> {
1642 let op = match op {
1643 rty::BinOp::Eq => {
1644 return Ok(fixpoint::Expr::Atom(
1645 fixpoint::BinRel::Eq,
1646 Box::new([self.expr_to_fixpoint(e1, scx)?, self.expr_to_fixpoint(e2, scx)?]),
1647 ));
1648 }
1649 rty::BinOp::Ne => {
1650 return Ok(fixpoint::Expr::Atom(
1651 fixpoint::BinRel::Ne,
1652 Box::new([self.expr_to_fixpoint(e1, scx)?, self.expr_to_fixpoint(e2, scx)?]),
1653 ));
1654 }
1655 rty::BinOp::Gt(sort) => {
1656 return self.bin_rel_to_fixpoint(sort, fixpoint::BinRel::Gt, e1, e2, scx);
1657 }
1658 rty::BinOp::Ge(sort) => {
1659 return self.bin_rel_to_fixpoint(sort, fixpoint::BinRel::Ge, e1, e2, scx);
1660 }
1661 rty::BinOp::Lt(sort) => {
1662 return self.bin_rel_to_fixpoint(sort, fixpoint::BinRel::Lt, e1, e2, scx);
1663 }
1664 rty::BinOp::Le(sort) => {
1665 return self.bin_rel_to_fixpoint(sort, fixpoint::BinRel::Le, e1, e2, scx);
1666 }
1667 rty::BinOp::And => {
1668 return Ok(fixpoint::Expr::And(vec![
1669 self.expr_to_fixpoint(e1, scx)?,
1670 self.expr_to_fixpoint(e2, scx)?,
1671 ]));
1672 }
1673 rty::BinOp::Or => {
1674 return Ok(fixpoint::Expr::Or(vec![
1675 self.expr_to_fixpoint(e1, scx)?,
1676 self.expr_to_fixpoint(e2, scx)?,
1677 ]));
1678 }
1679 rty::BinOp::Imp => {
1680 return Ok(fixpoint::Expr::Imp(Box::new([
1681 self.expr_to_fixpoint(e1, scx)?,
1682 self.expr_to_fixpoint(e2, scx)?,
1683 ])));
1684 }
1685 rty::BinOp::Iff => {
1686 return Ok(fixpoint::Expr::Iff(Box::new([
1687 self.expr_to_fixpoint(e1, scx)?,
1688 self.expr_to_fixpoint(e2, scx)?,
1689 ])));
1690 }
1691
1692 rty::BinOp::Add(rty::Sort::BitVec(_))
1694 | rty::BinOp::Sub(rty::Sort::BitVec(_))
1695 | rty::BinOp::Mul(rty::Sort::BitVec(_))
1696 | rty::BinOp::Div(rty::Sort::BitVec(_))
1697 | rty::BinOp::Mod(rty::Sort::BitVec(_))
1698 | rty::BinOp::BitAnd(rty::Sort::BitVec(_))
1699 | rty::BinOp::BitOr(rty::Sort::BitVec(_))
1700 | rty::BinOp::BitXor(rty::Sort::BitVec(_))
1701 | rty::BinOp::BitShl(rty::Sort::BitVec(_))
1702 | rty::BinOp::BitShr(rty::Sort::BitVec(_)) => {
1703 let bv_func = self.bv_op_to_fixpoint(op);
1704 return Ok(fixpoint::Expr::App(
1705 Box::new(bv_func),
1706 None,
1707 vec![self.expr_to_fixpoint(e1, scx)?, self.expr_to_fixpoint(e2, scx)?],
1708 ));
1709 }
1710
1711 rty::BinOp::Sub(rty::Sort::App(rty::SortCtor::Set, _))
1713 | rty::BinOp::BitAnd(rty::Sort::App(rty::SortCtor::Set, _))
1714 | rty::BinOp::BitOr(rty::Sort::App(rty::SortCtor::Set, _)) => {
1715 let set_func = self.set_op_to_fixpoint(op);
1716 return Ok(fixpoint::Expr::App(
1717 Box::new(set_func),
1718 None,
1719 vec![self.expr_to_fixpoint(e1, scx)?, self.expr_to_fixpoint(e2, scx)?],
1720 ));
1721 }
1722
1723 rty::BinOp::Add(_) => fixpoint::BinOp::Add,
1725 rty::BinOp::Sub(_) => fixpoint::BinOp::Sub,
1726 rty::BinOp::Mul(_) => fixpoint::BinOp::Mul,
1727 rty::BinOp::Div(_) => fixpoint::BinOp::Div,
1728 rty::BinOp::Mod(_) => fixpoint::BinOp::Mod,
1729
1730 rty::BinOp::BitAnd(sort)
1731 | rty::BinOp::BitOr(sort)
1732 | rty::BinOp::BitXor(sort)
1733 | rty::BinOp::BitShl(sort)
1734 | rty::BinOp::BitShr(sort) => {
1735 bug!("unsupported operation `{op:?}` for sort `{sort:?}`");
1736 }
1737 };
1738 Ok(fixpoint::Expr::BinaryOp(
1739 op,
1740 Box::new([self.expr_to_fixpoint(e1, scx)?, self.expr_to_fixpoint(e2, scx)?]),
1741 ))
1742 }
1743
1744 fn bin_rel_to_fixpoint(
1761 &mut self,
1762 sort: &rty::Sort,
1763 rel: fixpoint::BinRel,
1764 e1: &rty::Expr,
1765 e2: &rty::Expr,
1766 scx: &mut SortEncodingCtxt,
1767 ) -> QueryResult<fixpoint::Expr> {
1768 let e = match sort {
1769 rty::Sort::Int | rty::Sort::Real | rty::Sort::Char => {
1770 fixpoint::Expr::Atom(
1771 rel,
1772 Box::new([self.expr_to_fixpoint(e1, scx)?, self.expr_to_fixpoint(e2, scx)?]),
1773 )
1774 }
1775 rty::Sort::BitVec(_) => {
1776 let e1 = self.expr_to_fixpoint(e1, scx)?;
1777 let e2 = self.expr_to_fixpoint(e2, scx)?;
1778 let rel = self.bv_rel_to_fixpoint(&rel);
1779 fixpoint::Expr::App(Box::new(rel), None, vec![e1, e2])
1780 }
1781 rty::Sort::Tuple(sorts) => {
1782 let arity = sorts.len();
1783 self.apply_bin_rel_rec(sorts, rel, e1, e2, scx, |field| {
1784 rty::FieldProj::Tuple { arity, field }
1785 })?
1786 }
1787 rty::Sort::App(rty::SortCtor::Adt(sort_def), args)
1788 if let Some(variant) = sort_def.opt_struct_variant() =>
1789 {
1790 let def_id = sort_def.did();
1791 let sorts = variant.field_sorts(args);
1792 self.apply_bin_rel_rec(&sorts, rel, e1, e2, scx, |field| {
1793 rty::FieldProj::Adt { def_id, field }
1794 })?
1795 }
1796 _ => {
1797 let rel = fixpoint::Expr::Var(fixpoint::Var::UIFRel(rel));
1798 fixpoint::Expr::App(
1799 Box::new(rel),
1800 None,
1801 vec![self.expr_to_fixpoint(e1, scx)?, self.expr_to_fixpoint(e2, scx)?],
1802 )
1803 }
1804 };
1805 Ok(e)
1806 }
1807
1808 fn apply_bin_rel_rec(
1810 &mut self,
1811 sorts: &[rty::Sort],
1812 rel: fixpoint::BinRel,
1813 e1: &rty::Expr,
1814 e2: &rty::Expr,
1815 scx: &mut SortEncodingCtxt,
1816 mk_proj: impl Fn(u32) -> rty::FieldProj,
1817 ) -> QueryResult<fixpoint::Expr> {
1818 Ok(fixpoint::Expr::and(
1819 sorts
1820 .iter()
1821 .enumerate()
1822 .map(|(idx, s)| {
1823 let proj = mk_proj(idx as u32);
1824 let e1 = e1.proj_and_reduce(proj);
1825 let e2 = e2.proj_and_reduce(proj);
1826 self.bin_rel_to_fixpoint(s, rel, &e1, &e2, scx)
1827 })
1828 .try_collect()?,
1829 ))
1830 }
1831
1832 fn imm(
1833 &mut self,
1834 arg: &rty::Expr,
1835 sort: &rty::Sort,
1836 scx: &mut SortEncodingCtxt,
1837 bindings: &mut Vec<fixpoint::Bind>,
1838 ) -> QueryResult<fixpoint::Expr> {
1839 let farg = self.expr_to_fixpoint(arg, scx)?;
1840 if matches!(self.backend, Backend::Lean) {
1842 return Ok(farg);
1843 }
1844 if let fixpoint::Expr::Var(var) = farg {
1847 Ok(fixpoint::Expr::Var(var))
1848 } else {
1849 let fresh = self.local_var_env.fresh_name();
1850 self.local_var_env.reverse_map.insert(fresh, arg.clone());
1851 let pred = fixpoint::Expr::eq(fixpoint::Expr::Var(fresh.into()), farg);
1852 bindings.push(fixpoint::Bind {
1853 name: fresh.into(),
1854 sort: scx.sort_to_fixpoint(sort),
1855 pred: fixpoint::Pred::Expr(pred),
1856 });
1857 Ok(fixpoint::Expr::Var(fresh.into()))
1858 }
1859 }
1860
1861 pub fn declare_fun(&mut self, def_id: FluxDefId) -> fixpoint::Var {
1865 *self.const_env.fun_def_map.entry(def_id).or_insert_with(|| {
1866 let id = self.const_env.global_var_gen.fresh();
1867 fixpoint::Var::Global(id, Some(def_id))
1868 })
1869 }
1870
1871 fn prim_op_sort(op: &rty::BinOp, span: Span) -> rty::PolyFuncSort {
1880 match op {
1881 rty::BinOp::BitAnd(rty::Sort::Int)
1882 | rty::BinOp::BitOr(rty::Sort::Int)
1883 | rty::BinOp::BitXor(rty::Sort::Int)
1884 | rty::BinOp::BitShl(rty::Sort::Int)
1885 | rty::BinOp::BitShr(rty::Sort::Int) => {
1886 let fsort =
1887 rty::FuncSort::new(vec![rty::Sort::Int, rty::Sort::Int], rty::Sort::Int);
1888 rty::PolyFuncSort::new(List::empty(), fsort)
1889 }
1890 _ => span_bug!(span, "unexpected prim op: {op:?} in `prim_op_sort`"),
1891 }
1892 }
1893
1894 fn define_const_for_cast(
1895 &mut self,
1896 from: &rty::Sort,
1897 to: &rty::Sort,
1898 scx: &mut SortEncodingCtxt,
1899 ) -> fixpoint::Var {
1900 let key = ConstKey::Cast(from.clone(), to.clone());
1901 self.const_env
1902 .get_or_insert(key, |global_name| {
1903 let fsort = rty::FuncSort::new(vec![from.clone()], to.clone());
1904 let fsort = rty::PolyFuncSort::new(List::empty(), fsort);
1905 let sort = scx.func_sort_to_fixpoint(&fsort);
1906 fixpoint::ConstDecl {
1907 name: fixpoint::Var::Global(global_name, None),
1908 sort,
1909 comment: Some(format!("cast uif: ({from:?}) -> {to:?}")),
1910 }
1911 })
1912 .name
1913 }
1914
1915 fn define_const_for_prim_op(
1916 &mut self,
1917 op: &rty::BinOp,
1918 scx: &mut SortEncodingCtxt,
1919 ) -> fixpoint::Var {
1920 let key = ConstKey::PrimOp(op.clone());
1921 let span = self.def_span();
1922 self.const_env
1923 .get_or_insert(key, |global_name| {
1924 let sort = scx.func_sort_to_fixpoint(&Self::prim_op_sort(op, span));
1925 fixpoint::ConstDecl {
1926 name: fixpoint::Var::Global(global_name, None),
1927 sort,
1928 comment: Some(format!("prim op uif: {op:?}")),
1929 }
1930 })
1931 .name
1932 }
1933
1934 fn define_const_for_uif(
1935 &mut self,
1936 def_id: FluxDefId,
1937 scx: &mut SortEncodingCtxt,
1938 ) -> fixpoint::Var {
1939 let key = ConstKey::Uif(def_id);
1940 self.const_env
1941 .get_or_insert(key, |global_name| {
1942 let sort = scx.func_sort_to_fixpoint(&self.genv.func_sort(def_id));
1943 fixpoint::ConstDecl {
1944 name: fixpoint::Var::Global(global_name, Some(def_id)),
1945 sort,
1946 comment: Some(format!("uif: {def_id:?}")),
1947 }
1948 })
1949 .name
1950 }
1951
1952 fn define_const_for_rust_const(
1953 &mut self,
1954 def_id: DefId,
1955 scx: &mut SortEncodingCtxt,
1956 ) -> fixpoint::Var {
1957 let key = ConstKey::RustConst(def_id);
1958 self.const_env
1959 .get_or_insert(key, |global_name| {
1960 let sort = self.genv.sort_of_def_id(def_id).unwrap().unwrap();
1961 fixpoint::ConstDecl {
1962 name: fixpoint::Var::Global(global_name, None),
1963 sort: scx.sort_to_fixpoint(&sort),
1964 comment: Some(format!("rust const: {}", def_id_to_string(def_id))),
1965 }
1966 })
1967 .name
1968 }
1969
1970 fn define_const_for_alias_reft(
1973 &mut self,
1974 alias_reft: &rty::AliasReft,
1975 fsort: rty::FuncSort,
1976 scx: &mut SortEncodingCtxt,
1977 ) -> fixpoint::Var {
1978 let tcx = self.genv.tcx();
1979 let args = alias_reft
1980 .args
1981 .to_rustc(tcx)
1982 .truncate_to(tcx, tcx.generics_of(alias_reft.assoc_id.parent()));
1983 let key = ConstKey::Alias(alias_reft.assoc_id, args);
1984 self.const_env
1985 .get_or_insert(key, |global_name| {
1986 let comment = Some(format!("alias reft: {alias_reft:?}"));
1987 let name = fixpoint::Var::Global(global_name, None);
1988 let fsort = rty::PolyFuncSort::new(List::empty(), fsort);
1989 let sort = scx.func_sort_to_fixpoint(&fsort);
1990 fixpoint::ConstDecl { name, comment, sort }
1991 })
1992 .name
1993 }
1994
1995 fn define_const_for_lambda(
1998 &mut self,
1999 lam: &rty::Lambda,
2000 scx: &mut SortEncodingCtxt,
2001 ) -> fixpoint::Var {
2002 let key = ConstKey::Lambda(lam.clone());
2003 self.const_env
2004 .get_or_insert(key, |global_name| {
2005 let comment = Some(format!("lambda: {lam:?}"));
2006 let name = fixpoint::Var::Global(global_name, None);
2007 let sort = scx.func_sort_to_fixpoint(&lam.fsort().to_poly());
2008 fixpoint::ConstDecl { name, comment, sort }
2009 })
2010 .name
2011 }
2012
2013 fn assume_const_values(
2014 &mut self,
2015 mut constraint: fixpoint::Constraint,
2016 scx: &mut SortEncodingCtxt,
2017 ) -> QueryResult<fixpoint::Constraint> {
2018 let mut idx = 0;
2021 while let Some((key, const_)) = self.const_env.const_map.get_index(idx) {
2022 idx += 1;
2023
2024 let ConstKey::RustConst(def_id) = key else { continue };
2025 let info = self.genv.constant_info(def_id)?;
2026 match info {
2027 rty::ConstantInfo::Uninterpreted => {}
2028 rty::ConstantInfo::Interpreted(val, _) => {
2029 let e1 = fixpoint::Expr::Var(const_.name);
2030 let e2 = self.expr_to_fixpoint(&val, scx)?;
2031 let pred = fixpoint::Pred::Expr(e1.eq(e2));
2032 constraint = fixpoint::Constraint::ForAll(
2033 fixpoint::Bind {
2034 name: fixpoint::Var::Underscore,
2035 sort: fixpoint::Sort::Int,
2036 pred,
2037 },
2038 Box::new(constraint),
2039 );
2040 }
2041 }
2042 }
2043 Ok(constraint)
2044 }
2045
2046 fn qualifiers_for(
2047 &mut self,
2048 def_id: LocalDefId,
2049 scx: &mut SortEncodingCtxt,
2050 ) -> QueryResult<Vec<fixpoint::Qualifier>> {
2051 self.genv
2052 .qualifiers_for(def_id)?
2053 .map(|qual| self.qualifier_to_fixpoint(qual, scx))
2054 .try_collect()
2055 }
2056
2057 fn define_funs(
2058 &mut self,
2059 def_id: MaybeExternId,
2060 scx: &mut SortEncodingCtxt,
2061 ) -> QueryResult<(Vec<fixpoint::FunDef>, Vec<fixpoint::ConstDecl>)> {
2062 let reveals: UnordSet<FluxDefId> = self
2063 .genv
2064 .reveals_for(def_id.local_id())
2065 .iter()
2066 .copied()
2067 .collect();
2068 let proven_externally = self.genv.proven_externally(def_id.local_id());
2069 let mut consts = vec![];
2070 let mut defs = vec![];
2071
2072 let mut idx = 0;
2075 while let Some((&did, _)) = self.const_env.fun_def_map.get_index(idx) {
2076 idx += 1;
2077
2078 let info = self.genv.normalized_info(did);
2079 let revealed = reveals.contains(&did);
2080 if info.hide && !revealed && proven_externally.is_none() {
2081 consts.push(self.fun_decl_to_fixpoint(did, scx));
2082 } else {
2083 defs.push((info.rank, self.fun_def_to_fixpoint(did, scx)?));
2084 };
2085 }
2086
2087 let defs = defs
2089 .into_iter()
2090 .sorted_by_key(|(rank, _)| *rank)
2091 .map(|(_, def)| def)
2092 .collect();
2093
2094 Ok((defs, consts))
2095 }
2096
2097 pub fn fun_decl_to_fixpoint(
2098 &mut self,
2099 def_id: FluxDefId,
2100 scx: &mut SortEncodingCtxt,
2101 ) -> fixpoint::ConstDecl {
2102 let name = self.const_env.fun_def_map[&def_id];
2103 let sort = scx.func_sort_to_fixpoint(&self.genv.func_sort(def_id));
2104 fixpoint::ConstDecl { name, sort, comment: Some(format!("flux def: {def_id:?}")) }
2105 }
2106
2107 pub fn fun_def_to_fixpoint(
2108 &mut self,
2109 def_id: FluxDefId,
2110 scx: &mut SortEncodingCtxt,
2111 ) -> QueryResult<fixpoint::FunDef> {
2112 let name = *self.const_env.fun_def_map.get(&def_id).unwrap();
2113 let info = self.genv.normalized_info(def_id);
2114 let out = scx.sort_to_fixpoint(self.genv.func_sort(def_id).expect_mono().output());
2115 let (args, body) = self.body_to_fixpoint(&info.body, scx)?;
2116 Ok(fixpoint::FunDef {
2117 name,
2118 args,
2119 body,
2120 out,
2121 comment: Some(format!("flux def: {def_id:?}")),
2122 })
2123 }
2124
2125 fn body_to_fixpoint(
2126 &mut self,
2127 body: &rty::Binder<rty::Expr>,
2128 scx: &mut SortEncodingCtxt,
2129 ) -> QueryResult<(Vec<(fixpoint::Var, fixpoint::Sort)>, fixpoint::Expr)> {
2130 self.local_var_env
2131 .push_layer_with_fresh_names(body.vars().len());
2132
2133 let expr = self.expr_to_fixpoint(body.as_ref().skip_binder(), scx)?;
2134
2135 let args: Vec<(fixpoint::Var, fixpoint::Sort)> =
2136 iter::zip(self.local_var_env.pop_layer(), body.vars())
2137 .map(|(name, var)| (name.into(), scx.sort_to_fixpoint(var.expect_sort())))
2138 .collect();
2139
2140 Ok((args, expr))
2141 }
2142
2143 fn qualifier_to_fixpoint(
2144 &mut self,
2145 qualifier: &rty::Qualifier,
2146 scx: &mut SortEncodingCtxt,
2147 ) -> QueryResult<fixpoint::Qualifier> {
2148 let (args, body) = self.body_to_fixpoint(&qualifier.body, scx)?;
2149 let name = qualifier.def_id.name().to_string();
2150 Ok(fixpoint::Qualifier { name, args, body })
2151 }
2152}
2153
2154fn mk_implies(assumption: fixpoint::Pred, cstr: fixpoint::Constraint) -> fixpoint::Constraint {
2155 fixpoint::Constraint::ForAll(
2156 fixpoint::Bind {
2157 name: fixpoint::Var::Underscore,
2158 sort: fixpoint::Sort::Int,
2159 pred: assumption,
2160 },
2161 Box::new(cstr),
2162 )
2163}
2164
2165fn parse_kvid(kvid: &str) -> fixpoint::KVid {
2166 if kvid.starts_with("k")
2167 && let Some(kvid) = kvid[1..].parse::<u32>().ok()
2168 {
2169 fixpoint::KVid::from_u32(kvid)
2170 } else {
2171 tracked_span_bug!("unexpected kvar name {kvid}")
2172 }
2173}
2174
2175fn parse_local_var(name: &str) -> Option<fixpoint::Var> {
2176 if let Some(rest) = name.strip_prefix('a')
2177 && let Ok(idx) = rest.parse::<u32>()
2178 {
2179 return Some(fixpoint::Var::Local(fixpoint::LocalVar::from(idx)));
2180 }
2181 None
2182}
2183
2184fn parse_global_var(name: &str) -> Option<fixpoint::Var> {
2185 if let Some(rest) = name.strip_prefix('c')
2186 && let Ok(idx) = rest.parse::<u32>()
2187 {
2188 return Some(fixpoint::Var::Global(fixpoint::GlobalVar::from(idx), None));
2189 }
2190 if let Some(rest) = name.strip_prefix("f$")
2192 && let parts = rest.split('$').collect::<Vec<_>>()
2193 && parts.len() == 2
2194 && let Ok(global_idx) = parts[1].parse::<u32>()
2195 {
2196 return Some(fixpoint::Var::Global(fixpoint::GlobalVar::from(global_idx), None));
2197 }
2198 None
2199}
2200
2201fn parse_param(name: &str) -> Option<fixpoint::Var> {
2202 if let Some(rest) = name.strip_prefix("reftgen$")
2203 && let parts = rest.split('$').collect::<Vec<_>>()
2204 && parts.len() == 2
2205 && let Ok(index) = parts[1].parse::<u32>()
2206 {
2207 let name = Symbol::intern(parts[0]);
2208 let param = EarlyReftParam { index, name };
2209 return Some(fixpoint::Var::Param(param));
2210 }
2211 None
2212}
2213
2214fn parse_data_proj(name: &str) -> Option<fixpoint::Var> {
2215 if let Some(rest) = name.strip_prefix("fld")
2216 && let parts = rest.split('$').collect::<Vec<_>>()
2217 && parts.len() == 2
2218 && let Ok(adt_id) = parts[0].parse::<u32>()
2219 && let Ok(field) = parts[1].parse::<u32>()
2220 {
2221 let adt_id = fixpoint::AdtId::from(adt_id);
2222 return Some(fixpoint::Var::DataProj { adt_id, field });
2223 }
2224 None
2225}
2226
2227fn parse_data_ctor(name: &str) -> Option<fixpoint::Var> {
2228 if let Some(rest) = name.strip_prefix("mkadt")
2229 && let parts = rest.split('$').collect::<Vec<_>>()
2230 && parts.len() == 2
2231 && let Ok(adt_id) = parts[0].parse::<u32>()
2232 && let Ok(variant_idx) = parts[1].parse::<u32>()
2233 {
2234 let adt_id = fixpoint::AdtId::from(adt_id);
2235 let variant_idx = VariantIdx::from(variant_idx);
2236 return Some(fixpoint::Var::DataCtor(adt_id, variant_idx));
2237 }
2238 None
2239}
2240
2241struct SexpParseCtxt;
2242
2243impl FromSexp<FixpointTypes> for SexpParseCtxt {
2244 fn kvar(&self, name: &str) -> Result<fixpoint::KVid, ParseError> {
2245 bug!("TODO: SexpParse: kvar: {name}")
2246 }
2247
2248 fn string(&self, s: &str) -> Result<fixpoint::SymStr, ParseError> {
2249 Ok(fixpoint::SymStr(Symbol::intern(s)))
2250 }
2251
2252 fn var(&self, name: &str) -> Result<fixpoint::Var, ParseError> {
2253 if let Some(var) = parse_local_var(name) {
2254 return Ok(var);
2255 }
2256 if let Some(var) = parse_global_var(name) {
2257 return Ok(var);
2258 }
2259 if let Some(var) = parse_param(name) {
2260 return Ok(var);
2261 }
2262 if let Some(var) = parse_data_proj(name) {
2263 return Ok(var);
2264 }
2265 if let Some(var) = parse_data_ctor(name) {
2266 return Ok(var);
2267 }
2268 Err(ParseError::err(format!("Unknown variable: {name}")))
2269 }
2270
2271 fn sort(&self, name: &str) -> Result<fixpoint::DataSort, ParseError> {
2272 if let Some(idx) = name.strip_prefix("Adt")
2273 && let Ok(adt_id) = idx.parse::<u32>()
2274 {
2275 return Ok(fixpoint::DataSort::Adt(fixpoint::AdtId::from(adt_id)));
2276 }
2277 Err(ParseError::err(format!("Unknown sort: {name}")))
2278 }
2279}