1use std::{hash::Hash, iter};
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_middle::{
17 FixpointQueryKind,
18 def_id::{FluxDefId, MaybeExternId},
19 def_id_to_string,
20 global_env::GlobalEnv,
21 queries::QueryResult,
22 rty::{self, ESpan, GenericArgsExt, InternalFuncKind, Lambda, List, SpecFuncKind, VariantIdx},
23 timings::{self, TimingKind},
24};
25use itertools::Itertools;
26use liquid_fixpoint::{FixpointResult, SmtSolver};
27use rustc_data_structures::{
28 fx::{FxIndexMap, FxIndexSet},
29 unord::{UnordMap, UnordSet},
30};
31use rustc_hir::def_id::{DefId, LocalDefId};
32use rustc_index::newtype_index;
33use rustc_infer::infer::TyCtxtInferExt as _;
34use rustc_middle::ty::TypingMode;
35use rustc_span::Span;
36use rustc_type_ir::{BoundVar, DebruijnIndex};
37use serde::{Deserialize, Deserializer, Serialize};
38
39use crate::{
40 fixpoint_qualifiers::FIXPOINT_QUALIFIERS, lean_encoding::LeanEncoder,
41 projections::structurally_normalize_expr,
42};
43
44pub mod fixpoint {
45 use std::fmt;
46
47 use flux_middle::rty::{EarlyReftParam, Real};
48 use liquid_fixpoint::{FixpointFmt, Identifier};
49 use rustc_abi::VariantIdx;
50 use rustc_index::newtype_index;
51 use rustc_middle::ty::ParamConst;
52 use rustc_span::Symbol;
53
54 newtype_index! {
55 pub struct KVid {}
56 }
57
58 impl Identifier for KVid {
59 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
60 write!(f, "k{}", self.as_u32())
61 }
62 }
63
64 newtype_index! {
65 pub struct LocalVar {}
66 }
67
68 newtype_index! {
69 pub struct GlobalVar {}
70 }
71
72 newtype_index! {
73 pub struct AdtId {}
76 }
77
78 #[derive(Hash, Copy, Clone, Debug, PartialEq, Eq)]
79 pub enum Var {
80 Underscore,
81 Global(GlobalVar, Option<Symbol>),
82 Local(LocalVar),
83 DataCtor(AdtId, VariantIdx),
84 TupleCtor { arity: usize },
85 TupleProj { arity: usize, field: u32 },
86 DataProj { adt_id: AdtId, field: u32 },
87 UIFRel(BinRel),
88 Param(EarlyReftParam),
89 ConstGeneric(ParamConst),
90 }
91
92 impl From<GlobalVar> for Var {
93 fn from(v: GlobalVar) -> Self {
94 Self::Global(v, None)
95 }
96 }
97
98 impl From<LocalVar> for Var {
99 fn from(v: LocalVar) -> Self {
100 Self::Local(v)
101 }
102 }
103
104 impl Identifier for Var {
105 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
106 match self {
107 Var::Global(v, None) => write!(f, "c{}", v.as_u32()),
108 Var::Global(v, Some(sym)) => write!(f, "f${}${}", sym, v.as_u32()),
109 Var::Local(v) => write!(f, "a{}", v.as_u32()),
110 Var::DataCtor(adt_id, variant_idx) => {
111 write!(f, "mkadt{}${}", adt_id.as_u32(), variant_idx.as_u32())
112 }
113 Var::TupleCtor { arity } => write!(f, "mktuple{arity}"),
114 Var::TupleProj { arity, field } => write!(f, "tuple{arity}${field}"),
115 Var::DataProj { adt_id, field } => write!(f, "fld{}${field}", adt_id.as_u32()),
116 Var::UIFRel(BinRel::Gt) => write!(f, "gt"),
117 Var::UIFRel(BinRel::Ge) => write!(f, "ge"),
118 Var::UIFRel(BinRel::Lt) => write!(f, "lt"),
119 Var::UIFRel(BinRel::Le) => write!(f, "le"),
120 Var::UIFRel(BinRel::Eq) => write!(f, "eq"),
122 Var::UIFRel(BinRel::Ne) => write!(f, "ne"),
123 Var::Underscore => write!(f, "_$"), Var::ConstGeneric(param) => {
125 write!(f, "constgen${}${}", param.name, param.index)
126 }
127 Var::Param(param) => {
128 write!(f, "reftgen${}${}", param.name, param.index)
129 }
130 }
131 }
132 }
133
134 #[derive(Clone, Hash, Debug, PartialEq, Eq)]
135 pub enum DataSort {
136 Tuple(usize),
137 Adt(AdtId),
138 }
139
140 impl Identifier for DataSort {
141 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
142 match self {
143 DataSort::Tuple(arity) => {
144 write!(f, "Tuple{arity}")
145 }
146 DataSort::Adt(adt_id) => {
147 write!(f, "Adt{}", adt_id.as_u32())
148 }
149 }
150 }
151 }
152
153 #[derive(Hash, Clone, Debug)]
154 pub struct SymStr(pub Symbol);
155
156 #[cfg(feature = "rust-fixpoint")]
157 impl FixpointFmt for SymStr {
158 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
159 write!(f, "{}", self.0)
160 }
161 }
162
163 #[cfg(not(feature = "rust-fixpoint"))]
164 impl FixpointFmt for SymStr {
165 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
166 write!(f, "\"{}\"", self.0)
167 }
168 }
169
170 liquid_fixpoint::declare_types! {
171 type Sort = DataSort;
172 type KVar = KVid;
173 type Var = Var;
174 type Decimal = Real;
175 type String = SymStr;
176 type Tag = super::TagIdx;
177 }
178 pub use fixpoint_generated::*;
179}
180
181newtype_index! {
182 #[debug_format = "TagIdx({})"]
183 pub struct TagIdx {}
184}
185
186impl Serialize for TagIdx {
187 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
188 self.as_u32().serialize(serializer)
189 }
190}
191
192impl<'de> Deserialize<'de> for TagIdx {
193 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
194 let idx = usize::deserialize(deserializer)?;
195 Ok(TagIdx::from_u32(idx as u32))
196 }
197}
198
199#[derive(Default)]
201struct SortEncodingCtxt {
202 tuples: UnordSet<usize>,
204 adt_sorts: FxIndexSet<DefId>,
207}
208
209impl SortEncodingCtxt {
210 fn sort_to_fixpoint(&mut self, sort: &rty::Sort) -> fixpoint::Sort {
211 match sort {
212 rty::Sort::Int => fixpoint::Sort::Int,
213 rty::Sort::Real => fixpoint::Sort::Real,
214 rty::Sort::Bool => fixpoint::Sort::Bool,
215 rty::Sort::Str => fixpoint::Sort::Str,
216 rty::Sort::Char => fixpoint::Sort::Int,
217 rty::Sort::BitVec(size) => fixpoint::Sort::BitVec(Box::new(bv_size_to_fixpoint(*size))),
218 rty::Sort::App(rty::SortCtor::User { .. }, _)
223 | rty::Sort::Param(_)
224 | rty::Sort::Alias(rty::AliasKind::Opaque | rty::AliasKind::Projection, ..) => {
225 fixpoint::Sort::Int
226 }
227 rty::Sort::App(rty::SortCtor::Set, args) => {
228 let args = args.iter().map(|s| self.sort_to_fixpoint(s)).collect_vec();
229 fixpoint::Sort::App(fixpoint::SortCtor::Set, args)
230 }
231 rty::Sort::App(rty::SortCtor::Map, args) => {
232 let args = args.iter().map(|s| self.sort_to_fixpoint(s)).collect_vec();
233 fixpoint::Sort::App(fixpoint::SortCtor::Map, args)
234 }
235 rty::Sort::App(rty::SortCtor::Adt(sort_def), args) => {
236 if let Some(variant) = sort_def.opt_struct_variant() {
237 let sorts = variant.field_sorts(args);
238 if let [sort] = &sorts[..] {
240 self.sort_to_fixpoint(sort)
241 } else {
242 let adt_id = self.declare_adt(sort_def.did());
243 let ctor = fixpoint::SortCtor::Data(fixpoint::DataSort::Adt(adt_id));
244 let args = args.iter().map(|s| self.sort_to_fixpoint(s)).collect_vec();
245 fixpoint::Sort::App(ctor, args)
246 }
247 } else {
248 debug_assert!(args.is_empty());
249 let adt_id = self.declare_adt(sort_def.did());
250 fixpoint::Sort::App(
251 fixpoint::SortCtor::Data(fixpoint::DataSort::Adt(adt_id)),
252 vec![],
253 )
254 }
255 }
256 rty::Sort::Tuple(sorts) => {
257 if let [sort] = &sorts[..] {
259 self.sort_to_fixpoint(sort)
260 } else {
261 self.declare_tuple(sorts.len());
262 let ctor = fixpoint::SortCtor::Data(fixpoint::DataSort::Tuple(sorts.len()));
263 let args = sorts.iter().map(|s| self.sort_to_fixpoint(s)).collect();
264 fixpoint::Sort::App(ctor, args)
265 }
266 }
267 rty::Sort::Func(sort) => self.func_sort_to_fixpoint(sort),
268 rty::Sort::Var(k) => fixpoint::Sort::Var(k.index()),
269 rty::Sort::Err
270 | rty::Sort::Infer(_)
271 | rty::Sort::Loc
272 | rty::Sort::Alias(rty::AliasKind::Free, _) => {
273 tracked_span_bug!("unexpected sort `{sort:?}`")
274 }
275 }
276 }
277
278 fn func_sort_to_fixpoint(&mut self, fsort: &rty::PolyFuncSort) -> fixpoint::Sort {
279 let params = fsort.params().len();
280 let fsort = fsort.skip_binders();
281 let output = self.sort_to_fixpoint(fsort.output());
282 fixpoint::Sort::mk_func(
283 params,
284 fsort.inputs().iter().map(|s| self.sort_to_fixpoint(s)),
285 output,
286 )
287 }
288
289 fn declare_tuple(&mut self, arity: usize) {
290 self.tuples.insert(arity);
291 }
292
293 pub fn declare_adt(&mut self, did: DefId) -> AdtId {
294 if let Some(idx) = self.adt_sorts.get_index_of(&did) {
295 AdtId::from_usize(idx)
296 } else {
297 let adt_id = AdtId::from_usize(self.adt_sorts.len());
298 self.adt_sorts.insert(did);
299 adt_id
300 }
301 }
302
303 fn append_adt_decls(
304 &mut self,
305 genv: GlobalEnv,
306 decls: &mut Vec<fixpoint::DataDecl>,
307 ) -> QueryResult {
308 let mut idx = 0;
311 while let Some(adt_def_id) = self.adt_sorts.get_index(idx) {
312 let adt_id = AdtId::from_usize(idx);
313 let adt_sort_def = genv.adt_sort_def_of(adt_def_id)?;
314 decls.push(fixpoint::DataDecl {
315 name: fixpoint::DataSort::Adt(adt_id),
316 vars: adt_sort_def.param_count(),
317 ctors: adt_sort_def
318 .variants()
319 .iter_enumerated()
320 .map(|(idx, variant)| {
321 let name = fixpoint::Var::DataCtor(adt_id, idx);
322 let fields = variant
323 .field_sorts_instantiate_identity()
324 .iter()
325 .enumerate()
326 .map(|(i, sort)| {
327 fixpoint::DataField {
328 name: fixpoint::Var::DataProj { adt_id, field: i as u32 },
329 sort: self.sort_to_fixpoint(sort),
330 }
331 })
332 .collect_vec();
333 fixpoint::DataCtor { name, fields }
334 })
335 .collect(),
336 });
337 idx += 1;
338 }
339 Ok(())
340 }
341
342 fn append_tuple_decls(tuples: UnordSet<usize>, decls: &mut Vec<fixpoint::DataDecl>) {
343 decls.extend(
344 tuples
345 .into_items()
346 .into_sorted_stable_ord()
347 .into_iter()
348 .map(|arity| {
349 fixpoint::DataDecl {
350 name: fixpoint::DataSort::Tuple(arity),
351 vars: arity,
352 ctors: vec![fixpoint::DataCtor {
353 name: fixpoint::Var::TupleCtor { arity },
354 fields: (0..(arity as u32))
355 .map(|field| {
356 fixpoint::DataField {
357 name: fixpoint::Var::TupleProj { arity, field },
358 sort: fixpoint::Sort::Var(field as usize),
359 }
360 })
361 .collect(),
362 }],
363 }
364 }),
365 );
366 }
367
368 fn into_data_decls(mut self, genv: GlobalEnv) -> QueryResult<Vec<fixpoint::DataDecl>> {
369 let mut decls = vec![];
370 self.append_adt_decls(genv, &mut decls)?;
371 Self::append_tuple_decls(self.tuples, &mut decls);
372 Ok(decls)
373 }
374}
375
376fn bv_size_to_fixpoint(size: rty::BvSize) -> fixpoint::Sort {
377 match size {
378 rty::BvSize::Fixed(size) => fixpoint::Sort::BvSize(size),
379 rty::BvSize::Param(_var) => {
380 bug!("unexpected parametric bit-vector size")
385 }
386 rty::BvSize::Infer(_) => bug!("unexpected infer variable for bit-vector size"),
387 }
388}
389
390type FunDefMap = FxIndexMap<FluxDefId, fixpoint::Var>;
391type ConstMap<'tcx> = FxIndexMap<ConstKey<'tcx>, fixpoint::ConstDecl>;
392
393#[derive(Eq, Hash, PartialEq)]
394enum ConstKey<'tcx> {
395 Uif(FluxDefId),
396 RustConst(DefId),
397 Alias(FluxDefId, rustc_middle::ty::GenericArgsRef<'tcx>),
398 Lambda(Lambda),
399 PrimOp(rty::BinOp),
400 Cast(rty::Sort, rty::Sort),
401}
402
403pub struct FixpointCtxt<'genv, 'tcx, T: Eq + Hash> {
404 comments: Vec<String>,
405 genv: GlobalEnv<'genv, 'tcx>,
406 kvars: KVarGen,
407 scx: SortEncodingCtxt,
408 kcx: KVarEncodingCtxt,
409 ecx: ExprEncodingCtxt<'genv, 'tcx>,
410 tags: IndexVec<TagIdx, T>,
411 tags_inv: UnordMap<T, TagIdx>,
412}
413
414pub type FixQueryCache = QueryCache<FixpointResult<TagIdx>>;
415
416impl<'genv, 'tcx, Tag> FixpointCtxt<'genv, 'tcx, Tag>
417where
418 Tag: std::hash::Hash + Eq + Copy,
419{
420 pub fn new(genv: GlobalEnv<'genv, 'tcx>, def_id: MaybeExternId, kvars: KVarGen) -> Self {
421 Self {
422 comments: vec![],
423 kvars,
424 scx: SortEncodingCtxt::default(),
425 genv,
426 ecx: ExprEncodingCtxt::new(genv, def_id),
427 kcx: Default::default(),
428 tags: IndexVec::new(),
429 tags_inv: Default::default(),
430 }
431 }
432
433 pub fn check(
434 mut self,
435 cache: &mut FixQueryCache,
436 constraint: fixpoint::Constraint,
437 kind: FixpointQueryKind,
438 scrape_quals: bool,
439 solver: SmtSolver,
440 ) -> QueryResult<Vec<Tag>> {
441 if !constraint.is_concrete() {
443 self.ecx.errors.into_result()?;
444 return Ok(vec![]);
445 }
446 let def_span = self.ecx.def_span();
447 let def_id = self.ecx.def_id;
448
449 let kvars = self.kcx.into_fixpoint();
450
451 let (define_funs, define_constants) = self.ecx.define_funs(def_id, &mut self.scx)?;
452 let qualifiers = self
453 .ecx
454 .qualifiers_for(def_id.local_id(), &mut self.scx)?
455 .into_iter()
456 .chain(FIXPOINT_QUALIFIERS.iter().cloned())
457 .collect();
458
459 let constraint = self.ecx.assume_const_values(constraint, &mut self.scx)?;
462
463 let mut constants = self.ecx.const_map.into_values().collect_vec();
464 constants.extend(define_constants);
465
466 #[cfg(not(feature = "rust-fixpoint"))]
470 for rel in fixpoint::BinRel::INEQUALITIES {
471 let sort = fixpoint::Sort::mk_func(
473 1,
474 [fixpoint::Sort::Var(0), fixpoint::Sort::Var(0)],
475 fixpoint::Sort::Bool,
476 );
477 constants.push(fixpoint::ConstDecl {
478 name: fixpoint::Var::UIFRel(rel),
479 sort,
480 comment: None,
481 });
482 }
483
484 self.ecx.errors.into_result()?;
486
487 let task = fixpoint::Task {
488 comments: self.comments,
489 constants,
490 kvars,
491 define_funs,
492 constraint,
493 qualifiers,
494 scrape_quals,
495 solver,
496 data_decls: self.scx.into_data_decls(self.genv)?,
497 };
498 if config::dump_constraint() {
499 dbg::dump_item_info(self.genv.tcx(), def_id.resolved_id(), "smt2", &task).unwrap();
500 }
501
502 match Self::run_task_with_cache(self.genv, task, def_id.resolved_id(), kind, cache) {
503 FixpointResult::Safe(_) => Ok(vec![]),
504 FixpointResult::Unsafe(_, errors) => {
505 Ok(errors
506 .into_iter()
507 .map(|err| self.tags[err.tag])
508 .unique()
509 .collect_vec())
510 }
511 FixpointResult::Crash(err) => span_bug!(def_span, "fixpoint crash: {err:?}"),
512 }
513 }
514
515 pub fn generate_and_check_lean_lemmas(
516 mut self,
517 constraint: fixpoint::Constraint,
518 ) -> QueryResult<()> {
519 let def_id = self.ecx.def_id;
520
521 if !self.kcx.into_fixpoint().is_empty() {
522 tracked_span_bug!("cannot generate lean lemmas for constraints with kvars");
523 }
524
525 let (define_funs, define_constants) = self.ecx.define_funs(def_id, &mut self.scx)?;
526
527 let constraint = self.ecx.assume_const_values(constraint, &mut self.scx)?;
528
529 let mut constants = self.ecx.const_map.into_values().collect_vec();
530 constants.extend(define_constants);
531
532 self.ecx.errors.into_result()?;
533
534 let lean_encoder = LeanEncoder::new(def_id, self.genv, define_funs, constants, constraint);
535 let lean_path = std::path::Path::new("./");
536 let project_name = "lean_proofs";
537 lean_encoder.check_proof(lean_path, project_name)
538 }
539
540 fn run_task_with_cache(
541 genv: GlobalEnv,
542 task: fixpoint::Task,
543 def_id: DefId,
544 kind: FixpointQueryKind,
545 cache: &mut FixQueryCache,
546 ) -> FixpointResult<TagIdx> {
547 let key = kind.task_key(genv.tcx(), def_id);
548
549 let hash = task.hash_with_default();
550
551 if config::is_cache_enabled()
552 && let Some(result) = cache.lookup(&key, hash)
553 {
554 return result.clone();
555 }
556 let result = timings::time_it(TimingKind::FixpointQuery(def_id, kind), || {
557 task.run()
558 .unwrap_or_else(|err| tracked_span_bug!("failed to run fixpoint: {err}"))
559 });
560
561 if config::is_cache_enabled() {
562 cache.insert(key, hash, result.clone());
563 }
564 result
565 }
566
567 fn tag_idx(&mut self, tag: Tag) -> TagIdx
568 where
569 Tag: std::fmt::Debug,
570 {
571 *self.tags_inv.entry(tag).or_insert_with(|| {
572 let idx = self.tags.push(tag);
573 self.comments.push(format!("Tag {idx}: {tag:?}"));
574 idx
575 })
576 }
577
578 pub(crate) fn with_name_map<R>(
579 &mut self,
580 name: rty::Name,
581 f: impl FnOnce(&mut Self, fixpoint::LocalVar) -> R,
582 ) -> R {
583 let fresh = self.ecx.local_var_env.insert_fvar_map(name);
584 let r = f(self, fresh);
585 self.ecx.local_var_env.remove_fvar_map(name);
586 r
587 }
588
589 pub(crate) fn sort_to_fixpoint(&mut self, sort: &rty::Sort) -> fixpoint::Sort {
590 self.scx.sort_to_fixpoint(sort)
591 }
592
593 pub(crate) fn var_to_fixpoint(&self, var: &rty::Var) -> fixpoint::Var {
594 self.ecx.var_to_fixpoint(var)
595 }
596
597 pub(crate) fn head_to_fixpoint(
602 &mut self,
603 expr: &rty::Expr,
604 mk_tag: impl Fn(Option<ESpan>) -> Tag + Copy,
605 ) -> QueryResult<fixpoint::Constraint>
606 where
607 Tag: std::fmt::Debug,
608 {
609 match expr.kind() {
610 rty::ExprKind::BinaryOp(rty::BinOp::And, ..) => {
611 let cstrs = expr
613 .flatten_conjs()
614 .into_iter()
615 .map(|e| self.head_to_fixpoint(e, mk_tag))
616 .try_collect()?;
617 Ok(fixpoint::Constraint::conj(cstrs))
618 }
619 rty::ExprKind::BinaryOp(rty::BinOp::Imp, e1, e2) => {
620 let (bindings, assumption) = self.assumption_to_fixpoint(e1)?;
621 let cstr = self.head_to_fixpoint(e2, mk_tag)?;
622 Ok(fixpoint::Constraint::foralls(bindings, mk_implies(assumption, cstr)))
623 }
624 rty::ExprKind::KVar(kvar) => {
625 let mut bindings = vec![];
626 let pred = self.kvar_to_fixpoint(kvar, &mut bindings)?;
627 Ok(fixpoint::Constraint::foralls(bindings, fixpoint::Constraint::Pred(pred, None)))
628 }
629 rty::ExprKind::ForAll(pred) => {
630 self.ecx
631 .local_var_env
632 .push_layer_with_fresh_names(pred.vars().len());
633 let cstr = self.head_to_fixpoint(pred.as_ref().skip_binder(), mk_tag)?;
634 let vars = self.ecx.local_var_env.pop_layer();
635
636 let bindings = iter::zip(vars, pred.vars())
637 .map(|(var, kind)| {
638 fixpoint::Bind {
639 name: var.into(),
640 sort: self.scx.sort_to_fixpoint(kind.expect_sort()),
641 pred: fixpoint::Pred::TRUE,
642 }
643 })
644 .collect_vec();
645
646 Ok(fixpoint::Constraint::foralls(bindings, cstr))
647 }
648 _ => {
649 let tag_idx = self.tag_idx(mk_tag(expr.span()));
650 let pred = fixpoint::Pred::Expr(self.ecx.expr_to_fixpoint(expr, &mut self.scx)?);
651 Ok(fixpoint::Constraint::Pred(pred, Some(tag_idx)))
652 }
653 }
654 }
655
656 pub(crate) fn assumption_to_fixpoint(
661 &mut self,
662 pred: &rty::Expr,
663 ) -> QueryResult<(Vec<fixpoint::Bind>, fixpoint::Pred)> {
664 let mut bindings = vec![];
665 let mut preds = vec![];
666 self.assumption_to_fixpoint_aux(pred, &mut bindings, &mut preds)?;
667 Ok((bindings, fixpoint::Pred::and(preds)))
668 }
669
670 fn assumption_to_fixpoint_aux(
672 &mut self,
673 expr: &rty::Expr,
674 bindings: &mut Vec<fixpoint::Bind>,
675 preds: &mut Vec<fixpoint::Pred>,
676 ) -> QueryResult {
677 match expr.kind() {
678 rty::ExprKind::BinaryOp(rty::BinOp::And, e1, e2) => {
679 self.assumption_to_fixpoint_aux(e1, bindings, preds)?;
680 self.assumption_to_fixpoint_aux(e2, bindings, preds)?;
681 }
682 rty::ExprKind::KVar(kvar) => {
683 preds.push(self.kvar_to_fixpoint(kvar, bindings)?);
684 }
685 rty::ExprKind::ForAll(_) => {
686 preds.push(fixpoint::Pred::TRUE);
692 }
693 _ => {
694 preds.push(fixpoint::Pred::Expr(self.ecx.expr_to_fixpoint(expr, &mut self.scx)?));
695 }
696 }
697 Ok(())
698 }
699
700 fn kvar_to_fixpoint(
701 &mut self,
702 kvar: &rty::KVar,
703 bindings: &mut Vec<fixpoint::Bind>,
704 ) -> QueryResult<fixpoint::Pred> {
705 let decl = self.kvars.get(kvar.kvid);
706 let kvids = self.kcx.encode(kvar.kvid, decl, &mut self.scx);
707
708 let all_args = iter::zip(&kvar.args, &decl.sorts)
709 .map(|(arg, sort)| self.ecx.imm(arg, sort, &mut self.scx, bindings))
710 .try_collect_vec()?;
711
712 if all_args.is_empty() {
716 let fresh = self.ecx.local_var_env.fresh_name();
717 let var = fixpoint::Var::Local(fresh);
718 bindings.push(fixpoint::Bind {
719 name: fresh.into(),
720 sort: fixpoint::Sort::Int,
721 pred: fixpoint::Pred::Expr(fixpoint::Expr::eq(
722 fixpoint::Expr::Var(var),
723 fixpoint::Expr::int(0),
724 )),
725 });
726 return Ok(fixpoint::Pred::KVar(kvids[0], vec![var]));
727 }
728
729 let kvars = kvids
730 .iter()
731 .enumerate()
732 .map(|(i, kvid)| {
733 let args = all_args[i..].to_vec();
734 fixpoint::Pred::KVar(*kvid, args)
735 })
736 .collect_vec();
737
738 Ok(fixpoint::Pred::And(kvars))
739 }
740}
741
742fn const_to_fixpoint(cst: rty::Constant) -> fixpoint::Expr {
743 match cst {
744 rty::Constant::Int(i) => {
745 if i.is_negative() {
746 fixpoint::Expr::Neg(Box::new(fixpoint::Constant::Numeral(i.abs()).into()))
747 } else {
748 fixpoint::Constant::Numeral(i.abs()).into()
749 }
750 }
751 rty::Constant::Real(r) => fixpoint::Constant::Decimal(r).into(),
752 rty::Constant::Bool(b) => fixpoint::Constant::Boolean(b).into(),
753 rty::Constant::Char(c) => fixpoint::Constant::Numeral(u128::from(c)).into(),
754 rty::Constant::Str(s) => fixpoint::Constant::String(fixpoint::SymStr(s)).into(),
755 rty::Constant::BitVec(i, size) => fixpoint::Constant::BitVec(i, size).into(),
756 }
757}
758
759struct FixpointKVar {
760 sorts: Vec<fixpoint::Sort>,
761 orig: rty::KVid,
762}
763
764#[derive(Default)]
767struct KVarEncodingCtxt {
768 kvars: IndexVec<fixpoint::KVid, FixpointKVar>,
770 map: UnordMap<rty::KVid, Vec<fixpoint::KVid>>,
772}
773
774impl KVarEncodingCtxt {
775 fn encode(
776 &mut self,
777 kvid: rty::KVid,
778 decl: &KVarDecl,
779 scx: &mut SortEncodingCtxt,
780 ) -> &[fixpoint::KVid] {
781 self.map.entry(kvid).or_insert_with(|| {
782 let all_args = decl
783 .sorts
784 .iter()
785 .map(|s| scx.sort_to_fixpoint(s))
786 .collect_vec();
787
788 if all_args.is_empty() {
790 let sorts = vec![fixpoint::Sort::Int];
791 let kvid = self.kvars.push(FixpointKVar::new(sorts, kvid));
792 return vec![kvid];
793 }
794
795 match decl.encoding {
796 KVarEncoding::Single => {
797 let kvid = self.kvars.push(FixpointKVar::new(all_args, kvid));
798 vec![kvid]
799 }
800 KVarEncoding::Conj => {
801 let n = usize::max(decl.self_args, 1);
802 (0..n)
803 .map(|i| {
804 let sorts = all_args[i..].to_vec();
805 self.kvars.push(FixpointKVar::new(sorts, kvid))
806 })
807 .collect_vec()
808 }
809 }
810 })
811 }
812
813 fn into_fixpoint(self) -> Vec<fixpoint::KVarDecl> {
814 self.kvars
815 .into_iter_enumerated()
816 .map(|(kvid, kvar)| {
817 fixpoint::KVarDecl::new(kvid, kvar.sorts, format!("orig: {:?}", kvar.orig))
818 })
819 .collect()
820 }
821}
822
823struct LocalVarEnv {
825 local_var_gen: IndexGen<fixpoint::LocalVar>,
826 fvars: UnordMap<rty::Name, fixpoint::LocalVar>,
827 layers: Vec<Vec<fixpoint::LocalVar>>,
829}
830
831impl LocalVarEnv {
832 fn new() -> Self {
833 Self { local_var_gen: IndexGen::new(), fvars: Default::default(), layers: Vec::new() }
834 }
835
836 fn fresh_name(&mut self) -> fixpoint::LocalVar {
839 self.local_var_gen.fresh()
840 }
841
842 fn insert_fvar_map(&mut self, name: rty::Name) -> fixpoint::LocalVar {
843 let fresh = self.fresh_name();
844 self.fvars.insert(name, fresh);
845 fresh
846 }
847
848 fn remove_fvar_map(&mut self, name: rty::Name) {
849 self.fvars.remove(&name);
850 }
851
852 fn push_layer_with_fresh_names(&mut self, count: usize) {
854 let layer = (0..count).map(|_| self.fresh_name()).collect();
855 self.layers.push(layer);
856 }
857
858 fn pop_layer(&mut self) -> Vec<fixpoint::LocalVar> {
859 self.layers.pop().unwrap()
860 }
861
862 fn get_fvar(&self, name: rty::Name) -> Option<fixpoint::LocalVar> {
863 self.fvars.get(&name).copied()
864 }
865
866 fn get_late_bvar(&self, debruijn: DebruijnIndex, var: BoundVar) -> Option<fixpoint::LocalVar> {
867 let depth = self.layers.len().checked_sub(debruijn.as_usize() + 1)?;
868 self.layers[depth].get(var.as_usize()).copied()
869 }
870}
871
872impl FixpointKVar {
873 fn new(sorts: Vec<fixpoint::Sort>, orig: rty::KVid) -> Self {
874 Self { sorts, orig }
875 }
876}
877
878pub struct KVarGen {
879 kvars: IndexVec<rty::KVid, KVarDecl>,
880 dummy: bool,
885}
886
887impl KVarGen {
888 pub(crate) fn new(dummy: bool) -> Self {
889 Self { kvars: IndexVec::new(), dummy }
890 }
891
892 fn get(&self, kvid: rty::KVid) -> &KVarDecl {
893 &self.kvars[kvid]
894 }
895
896 pub fn fresh(
915 &mut self,
916 binders: &[rty::BoundVariableKinds],
917 scope: impl IntoIterator<Item = (rty::Var, rty::Sort)>,
918 encoding: KVarEncoding,
919 ) -> rty::Expr {
920 if self.dummy {
921 return rty::Expr::hole(rty::HoleKind::Pred);
922 }
923
924 let args = itertools::chain(
925 binders.iter().rev().enumerate().flat_map(|(level, vars)| {
926 let debruijn = DebruijnIndex::from_usize(level);
927 vars.iter()
928 .cloned()
929 .enumerate()
930 .flat_map(move |(idx, var)| {
931 if let rty::BoundVariableKind::Refine(sort, _, kind) = var {
932 let br = rty::BoundReft { var: BoundVar::from_usize(idx), kind };
933 Some((rty::Var::Bound(debruijn, br), sort))
934 } else {
935 None
936 }
937 })
938 }),
939 scope,
940 );
941 let [.., last] = binders else {
942 return self.fresh_inner(0, [], encoding);
943 };
944 let num_self_args = last
945 .iter()
946 .filter(|var| matches!(var, rty::BoundVariableKind::Refine(..)))
947 .count();
948 self.fresh_inner(num_self_args, args, encoding)
949 }
950
951 fn fresh_inner<A>(&mut self, self_args: usize, args: A, encoding: KVarEncoding) -> rty::Expr
952 where
953 A: IntoIterator<Item = (rty::Var, rty::Sort)>,
954 {
955 let mut sorts = vec![];
957 let mut exprs = vec![];
958
959 let mut flattened_self_args = 0;
960 for (i, (var, sort)) in args.into_iter().enumerate() {
961 let is_self_arg = i < self_args;
962 let var = var.to_expr();
963 sort.walk(|sort, proj| {
964 if !matches!(sort, rty::Sort::Loc) {
965 flattened_self_args += is_self_arg as usize;
966 sorts.push(sort.clone());
967 exprs.push(rty::Expr::field_projs(&var, proj));
968 }
969 });
970 }
971
972 let kvid = self
973 .kvars
974 .push(KVarDecl { self_args: flattened_self_args, sorts, encoding });
975
976 let kvar = rty::KVar::new(kvid, flattened_self_args, exprs);
977 rty::Expr::kvar(kvar)
978 }
979}
980
981#[derive(Clone)]
982struct KVarDecl {
983 self_args: usize,
984 sorts: Vec<rty::Sort>,
985 encoding: KVarEncoding,
986}
987
988#[derive(Clone, Copy)]
990pub enum KVarEncoding {
991 Single,
994 Conj,
998}
999
1000impl std::fmt::Display for TagIdx {
1001 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1002 write!(f, "{}", self.as_u32())
1003 }
1004}
1005
1006impl std::str::FromStr for TagIdx {
1007 type Err = std::num::ParseIntError;
1008
1009 fn from_str(s: &str) -> Result<Self, Self::Err> {
1010 Ok(Self::from_u32(s.parse()?))
1011 }
1012}
1013
1014struct ExprEncodingCtxt<'genv, 'tcx> {
1015 genv: GlobalEnv<'genv, 'tcx>,
1016 local_var_env: LocalVarEnv,
1017 global_var_gen: IndexGen<fixpoint::GlobalVar>,
1018 const_map: ConstMap<'tcx>,
1019 fun_def_map: FunDefMap,
1020 errors: Errors<'genv>,
1021 def_id: MaybeExternId,
1024 infcx: rustc_infer::infer::InferCtxt<'tcx>,
1025}
1026
1027impl<'genv, 'tcx> ExprEncodingCtxt<'genv, 'tcx> {
1028 fn new(genv: GlobalEnv<'genv, 'tcx>, def_id: MaybeExternId) -> Self {
1029 Self {
1030 genv,
1031 local_var_env: LocalVarEnv::new(),
1032 global_var_gen: IndexGen::new(),
1033 const_map: Default::default(),
1034 fun_def_map: Default::default(),
1035 errors: Errors::new(genv.sess()),
1036 def_id,
1037 infcx: genv
1038 .tcx()
1039 .infer_ctxt()
1040 .with_next_trait_solver(true)
1041 .build(TypingMode::non_body_analysis()),
1042 }
1043 }
1044
1045 fn def_span(&self) -> Span {
1046 self.genv.tcx().def_span(self.def_id)
1047 }
1048
1049 fn var_to_fixpoint(&self, var: &rty::Var) -> fixpoint::Var {
1050 match var {
1051 rty::Var::Free(name) => {
1052 self.local_var_env
1053 .get_fvar(*name)
1054 .unwrap_or_else(|| {
1055 span_bug!(self.def_span(), "no entry found for name: `{name:?}`")
1056 })
1057 .into()
1058 }
1059 rty::Var::Bound(debruijn, breft) => {
1060 self.local_var_env
1061 .get_late_bvar(*debruijn, breft.var)
1062 .unwrap_or_else(|| {
1063 span_bug!(self.def_span(), "no entry found for late bound var: `{breft:?}`")
1064 })
1065 .into()
1066 }
1067 rty::Var::ConstGeneric(param) => fixpoint::Var::ConstGeneric(*param),
1068 rty::Var::EarlyParam(param) => fixpoint::Var::Param(*param),
1069 rty::Var::EVar(_) => {
1070 span_bug!(self.def_span(), "unexpected evar: `{var:?}`")
1071 }
1072 }
1073 }
1074
1075 fn variant_to_fixpoint(
1076 &self,
1077 scx: &mut SortEncodingCtxt,
1078 enum_def_id: &DefId,
1079 idx: VariantIdx,
1080 ) -> fixpoint::Var {
1081 let adt_id = scx.declare_adt(*enum_def_id);
1082 fixpoint::Var::DataCtor(adt_id, idx)
1083 }
1084
1085 fn struct_fields_to_fixpoint(
1086 &mut self,
1087 did: &DefId,
1088 flds: &[rty::Expr],
1089 scx: &mut SortEncodingCtxt,
1090 ) -> QueryResult<fixpoint::Expr> {
1091 if let [fld] = flds {
1093 self.expr_to_fixpoint(fld, scx)
1094 } else {
1095 let adt_id = scx.declare_adt(*did);
1096 let ctor = fixpoint::Expr::Var(fixpoint::Var::DataCtor(adt_id, VariantIdx::ZERO));
1097 let args = flds
1098 .iter()
1099 .map(|fld| self.expr_to_fixpoint(fld, scx))
1100 .try_collect()?;
1101 Ok(fixpoint::Expr::App(Box::new(ctor), args))
1102 }
1103 }
1104
1105 fn fields_to_fixpoint(
1106 &mut self,
1107 flds: &[rty::Expr],
1108 scx: &mut SortEncodingCtxt,
1109 ) -> QueryResult<fixpoint::Expr> {
1110 if let [fld] = flds {
1112 self.expr_to_fixpoint(fld, scx)
1113 } else {
1114 scx.declare_tuple(flds.len());
1115 let ctor = fixpoint::Expr::Var(fixpoint::Var::TupleCtor { arity: flds.len() });
1116 let args = flds
1117 .iter()
1118 .map(|fld| self.expr_to_fixpoint(fld, scx))
1119 .try_collect()?;
1120 Ok(fixpoint::Expr::App(Box::new(ctor), args))
1121 }
1122 }
1123
1124 fn internal_func_to_fixpoint(
1125 &mut self,
1126 internal_func: &InternalFuncKind,
1127 sort_args: &[rty::SortArg],
1128 args: &[rty::Expr],
1129 scx: &mut SortEncodingCtxt,
1130 ) -> QueryResult<fixpoint::Expr> {
1131 match internal_func {
1132 InternalFuncKind::Val(op) => {
1133 let func = fixpoint::Expr::Var(self.define_const_for_prim_op(op, scx));
1134 let args = self.exprs_to_fixpoint(args, scx)?;
1135 Ok(fixpoint::Expr::App(Box::new(func), args))
1136 }
1137 InternalFuncKind::Rel(op) => {
1138 let expr = if let Some(prim_rel) = self.genv.prim_rel_for(op)? {
1139 prim_rel.body.replace_bound_refts(args)
1140 } else {
1141 rty::Expr::tt()
1142 };
1143 self.expr_to_fixpoint(&expr, scx)
1144 }
1145 InternalFuncKind::Cast => {
1146 let [rty::SortArg::Sort(from), rty::SortArg::Sort(to)] = &sort_args else {
1147 span_bug!(self.def_span(), "unexpected cast")
1148 };
1149 match from.cast_kind(to) {
1150 rty::CastKind::Identity => self.expr_to_fixpoint(&args[0], scx),
1151 rty::CastKind::BoolToInt => {
1152 Ok(fixpoint::Expr::IfThenElse(Box::new([
1153 self.expr_to_fixpoint(&args[0], scx)?,
1154 fixpoint::Expr::int(1),
1155 fixpoint::Expr::int(0),
1156 ])))
1157 }
1158 rty::CastKind::IntoUnit => self.expr_to_fixpoint(&rty::Expr::unit(), scx),
1159 rty::CastKind::Uninterpreted => {
1160 let func = fixpoint::Expr::Var(self.define_const_for_cast(from, to, scx));
1161 let args = self.exprs_to_fixpoint(args, scx)?;
1162 Ok(fixpoint::Expr::App(Box::new(func), args))
1163 }
1164 }
1165 }
1166 }
1167 }
1168
1169 fn structurally_normalize_expr(&self, expr: &rty::Expr) -> QueryResult<rty::Expr> {
1170 structurally_normalize_expr(self.genv, self.def_id.resolved_id(), &self.infcx, expr)
1171 }
1172
1173 fn expr_to_fixpoint(
1174 &mut self,
1175 expr: &rty::Expr,
1176 scx: &mut SortEncodingCtxt,
1177 ) -> QueryResult<fixpoint::Expr> {
1178 let expr = self.structurally_normalize_expr(expr)?;
1179 let e = match expr.kind() {
1180 rty::ExprKind::Var(var) => fixpoint::Expr::Var(self.var_to_fixpoint(var)),
1181 rty::ExprKind::Constant(c) => const_to_fixpoint(*c),
1182 rty::ExprKind::BinaryOp(op, e1, e2) => self.bin_op_to_fixpoint(op, e1, e2, scx)?,
1183 rty::ExprKind::UnaryOp(op, e) => self.un_op_to_fixpoint(*op, e, scx)?,
1184 rty::ExprKind::FieldProj(e, proj) => self.proj_to_fixpoint(e, *proj, scx)?,
1185 rty::ExprKind::Tuple(flds) => self.fields_to_fixpoint(flds, scx)?,
1186 rty::ExprKind::Ctor(rty::Ctor::Struct(did), flds) => {
1187 self.struct_fields_to_fixpoint(did, flds, scx)?
1188 }
1189 rty::ExprKind::IsCtor(def_id, variant_idx, e) => {
1190 let ctor = self.variant_to_fixpoint(scx, def_id, *variant_idx);
1191 let e = self.expr_to_fixpoint(e, scx)?;
1192 fixpoint::Expr::IsCtor(ctor, Box::new(e))
1193 }
1194 rty::ExprKind::Ctor(rty::Ctor::Enum(did, idx), _) => {
1195 fixpoint::Expr::Var(self.variant_to_fixpoint(scx, did, *idx))
1196 }
1197 rty::ExprKind::ConstDefId(did) => {
1198 let var = self.define_const_for_rust_const(*did, scx);
1199 fixpoint::Expr::Var(var)
1200 }
1201 rty::ExprKind::App(func, sort_args, args) => {
1202 if let rty::ExprKind::InternalFunc(func) = func.kind() {
1203 self.internal_func_to_fixpoint(func, sort_args, args, scx)?
1204 } else {
1205 let func = self.expr_to_fixpoint(func, scx)?;
1206 let args = self.exprs_to_fixpoint(args, scx)?;
1207 fixpoint::Expr::App(Box::new(func), args)
1208 }
1209 }
1210 rty::ExprKind::IfThenElse(p, e1, e2) => {
1211 fixpoint::Expr::IfThenElse(Box::new([
1212 self.expr_to_fixpoint(p, scx)?,
1213 self.expr_to_fixpoint(e1, scx)?,
1214 self.expr_to_fixpoint(e2, scx)?,
1215 ]))
1216 }
1217 rty::ExprKind::Alias(alias_reft, args) => {
1218 let sort = self.genv.sort_of_assoc_reft(alias_reft.assoc_id)?;
1219 let sort = sort.instantiate_identity();
1220 let func =
1221 fixpoint::Expr::Var(self.define_const_for_alias_reft(alias_reft, sort, scx));
1222 let args = args
1223 .iter()
1224 .map(|expr| self.expr_to_fixpoint(expr, scx))
1225 .try_collect()?;
1226 fixpoint::Expr::App(Box::new(func), args)
1227 }
1228 rty::ExprKind::Abs(lam) => {
1229 let var = self.define_const_for_lambda(lam, scx);
1230 fixpoint::Expr::Var(var)
1231 }
1232 rty::ExprKind::Let(init, body) => {
1233 debug_assert_eq!(body.vars().len(), 1);
1234 let init = self.expr_to_fixpoint(init, scx)?;
1235
1236 self.local_var_env.push_layer_with_fresh_names(1);
1237 let body = self.expr_to_fixpoint(body.skip_binder_ref(), scx)?;
1238 let vars = self.local_var_env.pop_layer();
1239
1240 fixpoint::Expr::Let(vars[0].into(), Box::new([init, body]))
1241 }
1242 rty::ExprKind::GlobalFunc(SpecFuncKind::Thy(itf)) => fixpoint::Expr::ThyFunc(*itf),
1243 rty::ExprKind::GlobalFunc(SpecFuncKind::Uif(def_id)) => {
1244 fixpoint::Expr::Var(self.define_const_for_uif(*def_id, scx))
1245 }
1246 rty::ExprKind::GlobalFunc(SpecFuncKind::Def(def_id)) => {
1247 fixpoint::Expr::Var(self.declare_fun(*def_id))
1248 }
1249 rty::ExprKind::Hole(..)
1250 | rty::ExprKind::KVar(_)
1251 | rty::ExprKind::Local(_)
1252 | rty::ExprKind::PathProj(..)
1253 | rty::ExprKind::ForAll(_)
1254 | rty::ExprKind::InternalFunc(_) => {
1255 span_bug!(self.def_span(), "unexpected expr: `{expr:?}`")
1256 }
1257 rty::ExprKind::BoundedQuant(kind, rng, body) => {
1258 let exprs = (rng.start..rng.end).map(|i| {
1259 let arg = rty::Expr::constant(rty::Constant::from(i));
1260 body.replace_bound_reft(&arg)
1261 });
1262 let expr = match kind {
1263 flux_middle::fhir::QuantKind::Forall => rty::Expr::and_from_iter(exprs),
1264 flux_middle::fhir::QuantKind::Exists => rty::Expr::or_from_iter(exprs),
1265 };
1266 self.expr_to_fixpoint(&expr, scx)?
1267 }
1268 };
1269 Ok(e)
1270 }
1271
1272 fn exprs_to_fixpoint<'b>(
1273 &mut self,
1274 exprs: impl IntoIterator<Item = &'b rty::Expr>,
1275 scx: &mut SortEncodingCtxt,
1276 ) -> QueryResult<Vec<fixpoint::Expr>> {
1277 exprs
1278 .into_iter()
1279 .map(|e| self.expr_to_fixpoint(e, scx))
1280 .try_collect()
1281 }
1282
1283 fn proj_to_fixpoint(
1284 &mut self,
1285 e: &rty::Expr,
1286 proj: rty::FieldProj,
1287 scx: &mut SortEncodingCtxt,
1288 ) -> QueryResult<fixpoint::Expr> {
1289 let arity = proj.arity(self.genv)?;
1290 if arity == 1 {
1292 self.expr_to_fixpoint(e, scx)
1293 } else {
1294 let proj = match proj {
1295 rty::FieldProj::Tuple { arity, field } => {
1296 scx.declare_tuple(arity);
1297 fixpoint::Var::TupleProj { arity, field }
1298 }
1299 rty::FieldProj::Adt { def_id, field } => {
1300 let adt_id = scx.declare_adt(def_id);
1301 fixpoint::Var::DataProj { adt_id, field }
1302 }
1303 };
1304 let proj = fixpoint::Expr::Var(proj);
1305 Ok(fixpoint::Expr::App(Box::new(proj), vec![self.expr_to_fixpoint(e, scx)?]))
1306 }
1307 }
1308
1309 fn un_op_to_fixpoint(
1310 &mut self,
1311 op: rty::UnOp,
1312 e: &rty::Expr,
1313 scx: &mut SortEncodingCtxt,
1314 ) -> QueryResult<fixpoint::Expr> {
1315 match op {
1316 rty::UnOp::Not => Ok(fixpoint::Expr::Not(Box::new(self.expr_to_fixpoint(e, scx)?))),
1317 rty::UnOp::Neg => Ok(fixpoint::Expr::Neg(Box::new(self.expr_to_fixpoint(e, scx)?))),
1318 }
1319 }
1320
1321 fn bv_rel_to_fixpoint(&self, rel: &fixpoint::BinRel) -> fixpoint::Expr {
1322 let itf = match rel {
1323 fixpoint::BinRel::Gt => fixpoint::ThyFunc::BvUgt,
1324 fixpoint::BinRel::Ge => fixpoint::ThyFunc::BvUge,
1325 fixpoint::BinRel::Lt => fixpoint::ThyFunc::BvUlt,
1326 fixpoint::BinRel::Le => fixpoint::ThyFunc::BvUle,
1327 _ => span_bug!(self.def_span(), "not a bitvector relation!"),
1328 };
1329 fixpoint::Expr::ThyFunc(itf)
1330 }
1331
1332 fn bv_op_to_fixpoint(&self, op: &rty::BinOp) -> fixpoint::Expr {
1333 let itf = match op {
1334 rty::BinOp::Add(_) => fixpoint::ThyFunc::BvAdd,
1335 rty::BinOp::Sub(_) => fixpoint::ThyFunc::BvSub,
1336 rty::BinOp::Mul(_) => fixpoint::ThyFunc::BvMul,
1337 rty::BinOp::Div(_) => fixpoint::ThyFunc::BvUdiv,
1338 rty::BinOp::Mod(_) => fixpoint::ThyFunc::BvUrem,
1339 rty::BinOp::BitAnd => fixpoint::ThyFunc::BvAnd,
1340 rty::BinOp::BitOr => fixpoint::ThyFunc::BvOr,
1341 rty::BinOp::BitXor => fixpoint::ThyFunc::BvXor,
1342 rty::BinOp::BitShl => fixpoint::ThyFunc::BvShl,
1343 rty::BinOp::BitShr => fixpoint::ThyFunc::BvLshr,
1344 _ => span_bug!(self.def_span(), "not a bitvector operation!"),
1345 };
1346 fixpoint::Expr::ThyFunc(itf)
1347 }
1348
1349 fn bin_op_to_fixpoint(
1350 &mut self,
1351 op: &rty::BinOp,
1352 e1: &rty::Expr,
1353 e2: &rty::Expr,
1354 scx: &mut SortEncodingCtxt,
1355 ) -> QueryResult<fixpoint::Expr> {
1356 let op = match op {
1357 rty::BinOp::Eq => {
1358 return Ok(fixpoint::Expr::Atom(
1359 fixpoint::BinRel::Eq,
1360 Box::new([self.expr_to_fixpoint(e1, scx)?, self.expr_to_fixpoint(e2, scx)?]),
1361 ));
1362 }
1363 rty::BinOp::Ne => {
1364 return Ok(fixpoint::Expr::Atom(
1365 fixpoint::BinRel::Ne,
1366 Box::new([self.expr_to_fixpoint(e1, scx)?, self.expr_to_fixpoint(e2, scx)?]),
1367 ));
1368 }
1369 rty::BinOp::Gt(sort) => {
1370 return self.bin_rel_to_fixpoint(sort, fixpoint::BinRel::Gt, e1, e2, scx);
1371 }
1372 rty::BinOp::Ge(sort) => {
1373 return self.bin_rel_to_fixpoint(sort, fixpoint::BinRel::Ge, e1, e2, scx);
1374 }
1375 rty::BinOp::Lt(sort) => {
1376 return self.bin_rel_to_fixpoint(sort, fixpoint::BinRel::Lt, e1, e2, scx);
1377 }
1378 rty::BinOp::Le(sort) => {
1379 return self.bin_rel_to_fixpoint(sort, fixpoint::BinRel::Le, e1, e2, scx);
1380 }
1381 rty::BinOp::And => {
1382 return Ok(fixpoint::Expr::And(vec![
1383 self.expr_to_fixpoint(e1, scx)?,
1384 self.expr_to_fixpoint(e2, scx)?,
1385 ]));
1386 }
1387 rty::BinOp::Or => {
1388 return Ok(fixpoint::Expr::Or(vec![
1389 self.expr_to_fixpoint(e1, scx)?,
1390 self.expr_to_fixpoint(e2, scx)?,
1391 ]));
1392 }
1393 rty::BinOp::Imp => {
1394 return Ok(fixpoint::Expr::Imp(Box::new([
1395 self.expr_to_fixpoint(e1, scx)?,
1396 self.expr_to_fixpoint(e2, scx)?,
1397 ])));
1398 }
1399 rty::BinOp::Iff => {
1400 return Ok(fixpoint::Expr::Iff(Box::new([
1401 self.expr_to_fixpoint(e1, scx)?,
1402 self.expr_to_fixpoint(e2, scx)?,
1403 ])));
1404 }
1405 rty::BinOp::Add(rty::Sort::BitVec(_))
1406 | rty::BinOp::Sub(rty::Sort::BitVec(_))
1407 | rty::BinOp::Mul(rty::Sort::BitVec(_))
1408 | rty::BinOp::Div(rty::Sort::BitVec(_))
1409 | rty::BinOp::Mod(rty::Sort::BitVec(_))
1410 | rty::BinOp::BitAnd
1411 | rty::BinOp::BitOr
1412 | rty::BinOp::BitXor
1413 | rty::BinOp::BitShl
1414 | rty::BinOp::BitShr => {
1415 let bv_func = self.bv_op_to_fixpoint(op);
1416 return Ok(fixpoint::Expr::App(
1417 Box::new(bv_func),
1418 vec![self.expr_to_fixpoint(e1, scx)?, self.expr_to_fixpoint(e2, scx)?],
1419 ));
1420 }
1421 rty::BinOp::Add(_) => fixpoint::BinOp::Add,
1422 rty::BinOp::Sub(_) => fixpoint::BinOp::Sub,
1423 rty::BinOp::Mul(_) => fixpoint::BinOp::Mul,
1424 rty::BinOp::Div(_) => fixpoint::BinOp::Div,
1425 rty::BinOp::Mod(_) => fixpoint::BinOp::Mod,
1426 };
1427 Ok(fixpoint::Expr::BinaryOp(
1428 op,
1429 Box::new([self.expr_to_fixpoint(e1, scx)?, self.expr_to_fixpoint(e2, scx)?]),
1430 ))
1431 }
1432
1433 fn bin_rel_to_fixpoint(
1450 &mut self,
1451 sort: &rty::Sort,
1452 rel: fixpoint::BinRel,
1453 e1: &rty::Expr,
1454 e2: &rty::Expr,
1455 scx: &mut SortEncodingCtxt,
1456 ) -> QueryResult<fixpoint::Expr> {
1457 let e = match sort {
1458 rty::Sort::Int | rty::Sort::Real | rty::Sort::Char => {
1459 fixpoint::Expr::Atom(
1460 rel,
1461 Box::new([self.expr_to_fixpoint(e1, scx)?, self.expr_to_fixpoint(e2, scx)?]),
1462 )
1463 }
1464 rty::Sort::BitVec(_) => {
1465 let e1 = self.expr_to_fixpoint(e1, scx)?;
1466 let e2 = self.expr_to_fixpoint(e2, scx)?;
1467 let rel = self.bv_rel_to_fixpoint(&rel);
1468 fixpoint::Expr::App(Box::new(rel), vec![e1, e2])
1469 }
1470 rty::Sort::Tuple(sorts) => {
1471 let arity = sorts.len();
1472 self.apply_bin_rel_rec(sorts, rel, e1, e2, scx, |field| {
1473 rty::FieldProj::Tuple { arity, field }
1474 })?
1475 }
1476 rty::Sort::App(rty::SortCtor::Adt(sort_def), args)
1477 if let Some(variant) = sort_def.opt_struct_variant() =>
1478 {
1479 let def_id = sort_def.did();
1480 let sorts = variant.field_sorts(args);
1481 self.apply_bin_rel_rec(&sorts, rel, e1, e2, scx, |field| {
1482 rty::FieldProj::Adt { def_id, field }
1483 })?
1484 }
1485 _ => {
1486 let rel = fixpoint::Expr::Var(fixpoint::Var::UIFRel(rel));
1487 fixpoint::Expr::App(
1488 Box::new(rel),
1489 vec![self.expr_to_fixpoint(e1, scx)?, self.expr_to_fixpoint(e2, scx)?],
1490 )
1491 }
1492 };
1493 Ok(e)
1494 }
1495
1496 fn apply_bin_rel_rec(
1498 &mut self,
1499 sorts: &[rty::Sort],
1500 rel: fixpoint::BinRel,
1501 e1: &rty::Expr,
1502 e2: &rty::Expr,
1503 scx: &mut SortEncodingCtxt,
1504 mk_proj: impl Fn(u32) -> rty::FieldProj,
1505 ) -> QueryResult<fixpoint::Expr> {
1506 Ok(fixpoint::Expr::and(
1507 sorts
1508 .iter()
1509 .enumerate()
1510 .map(|(idx, s)| {
1511 let proj = mk_proj(idx as u32);
1512 let e1 = e1.proj_and_reduce(proj);
1513 let e2 = e2.proj_and_reduce(proj);
1514 self.bin_rel_to_fixpoint(s, rel, &e1, &e2, scx)
1515 })
1516 .try_collect()?,
1517 ))
1518 }
1519
1520 fn imm(
1521 &mut self,
1522 arg: &rty::Expr,
1523 sort: &rty::Sort,
1524 scx: &mut SortEncodingCtxt,
1525 bindings: &mut Vec<fixpoint::Bind>,
1526 ) -> QueryResult<fixpoint::Var> {
1527 let arg = self.expr_to_fixpoint(arg, scx)?;
1528 if let fixpoint::Expr::Var(var) = arg {
1531 Ok(var)
1532 } else {
1533 let fresh = self.local_var_env.fresh_name();
1534 let pred = fixpoint::Expr::eq(fixpoint::Expr::Var(fresh.into()), arg);
1535 bindings.push(fixpoint::Bind {
1536 name: fresh.into(),
1537 sort: scx.sort_to_fixpoint(sort),
1538 pred: fixpoint::Pred::Expr(pred),
1539 });
1540 Ok(fresh.into())
1541 }
1542 }
1543
1544 fn declare_fun(&mut self, def_id: FluxDefId) -> fixpoint::Var {
1548 *self.fun_def_map.entry(def_id).or_insert_with(|| {
1549 let id = self.global_var_gen.fresh();
1550 fixpoint::Var::Global(id, Some(def_id.name()))
1551 })
1552 }
1553
1554 fn prim_op_sort(op: &rty::BinOp, span: Span) -> rty::PolyFuncSort {
1563 match op {
1564 rty::BinOp::BitAnd
1565 | rty::BinOp::BitOr
1566 | rty::BinOp::BitXor
1567 | rty::BinOp::BitShl
1568 | rty::BinOp::BitShr => {
1569 let fsort =
1570 rty::FuncSort::new(vec![rty::Sort::Int, rty::Sort::Int], rty::Sort::Int);
1571 rty::PolyFuncSort::new(List::empty(), fsort)
1572 }
1573 _ => span_bug!(span, "unexpected prim op: {op:?} in `prim_op_sort`"),
1574 }
1575 }
1576
1577 fn define_const_for_cast(
1578 &mut self,
1579 from: &rty::Sort,
1580 to: &rty::Sort,
1581 scx: &mut SortEncodingCtxt,
1582 ) -> fixpoint::Var {
1583 let key = ConstKey::Cast(from.clone(), to.clone());
1584 self.const_map
1585 .entry(key)
1586 .or_insert_with(|| {
1587 let fsort = rty::FuncSort::new(vec![from.clone()], to.clone());
1588 let fsort = rty::PolyFuncSort::new(List::empty(), fsort);
1589 let sort = scx.func_sort_to_fixpoint(&fsort);
1590 fixpoint::ConstDecl {
1591 name: fixpoint::Var::Global(self.global_var_gen.fresh(), None),
1592 sort,
1593 comment: Some(format!("cast uif: ({from:?}) -> {to:?}")),
1594 }
1595 })
1596 .name
1597 }
1598
1599 fn define_const_for_prim_op(
1600 &mut self,
1601 op: &rty::BinOp,
1602 scx: &mut SortEncodingCtxt,
1603 ) -> fixpoint::Var {
1604 let key = ConstKey::PrimOp(op.clone());
1605 let span = self.def_span();
1606 self.const_map
1607 .entry(key)
1608 .or_insert_with(|| {
1609 let sort = scx.func_sort_to_fixpoint(&Self::prim_op_sort(op, span));
1610 fixpoint::ConstDecl {
1611 name: fixpoint::Var::Global(self.global_var_gen.fresh(), None),
1612 sort,
1613 comment: Some(format!("prim op uif: {op:?}")),
1614 }
1615 })
1616 .name
1617 }
1618
1619 fn define_const_for_uif(
1620 &mut self,
1621 def_id: FluxDefId,
1622 scx: &mut SortEncodingCtxt,
1623 ) -> fixpoint::Var {
1624 let key = ConstKey::Uif(def_id);
1625 self.const_map
1626 .entry(key)
1627 .or_insert_with(|| {
1628 let sort = scx.func_sort_to_fixpoint(&self.genv.func_sort(def_id));
1629 fixpoint::ConstDecl {
1630 name: fixpoint::Var::Global(self.global_var_gen.fresh(), Some(def_id.name())),
1631 sort,
1632 comment: Some(format!("uif: {def_id:?}")),
1633 }
1634 })
1635 .name
1636 }
1637
1638 fn define_const_for_rust_const(
1639 &mut self,
1640 def_id: DefId,
1641 scx: &mut SortEncodingCtxt,
1642 ) -> fixpoint::Var {
1643 let key = ConstKey::RustConst(def_id);
1644 self.const_map
1645 .entry(key)
1646 .or_insert_with(|| {
1647 let sort = self.genv.sort_of_def_id(def_id).unwrap().unwrap();
1648 fixpoint::ConstDecl {
1649 name: fixpoint::Var::Global(self.global_var_gen.fresh(), None),
1650 sort: scx.sort_to_fixpoint(&sort),
1651 comment: Some(format!("rust const: {}", def_id_to_string(def_id))),
1652 }
1653 })
1654 .name
1655 }
1656
1657 fn define_const_for_alias_reft(
1660 &mut self,
1661 alias_reft: &rty::AliasReft,
1662 fsort: rty::FuncSort,
1663 scx: &mut SortEncodingCtxt,
1664 ) -> fixpoint::Var {
1665 let tcx = self.genv.tcx();
1666 let args = alias_reft
1667 .args
1668 .to_rustc(tcx)
1669 .truncate_to(tcx, tcx.generics_of(alias_reft.assoc_id.parent()));
1670 let key = ConstKey::Alias(alias_reft.assoc_id, args);
1671 self.const_map
1672 .entry(key)
1673 .or_insert_with(|| {
1674 let comment = Some(format!("alias reft: {alias_reft:?}"));
1675 let name = fixpoint::Var::Global(self.global_var_gen.fresh(), None);
1676 let fsort = rty::PolyFuncSort::new(List::empty(), fsort);
1677 let sort = scx.func_sort_to_fixpoint(&fsort);
1678 fixpoint::ConstDecl { name, comment, sort }
1679 })
1680 .name
1681 }
1682
1683 fn define_const_for_lambda(
1686 &mut self,
1687 lam: &rty::Lambda,
1688 scx: &mut SortEncodingCtxt,
1689 ) -> fixpoint::Var {
1690 let key = ConstKey::Lambda(lam.clone());
1691 self.const_map
1692 .entry(key)
1693 .or_insert_with(|| {
1694 let comment = Some(format!("lambda: {lam:?}"));
1695 let name = fixpoint::Var::Global(self.global_var_gen.fresh(), None);
1696 let sort = scx.func_sort_to_fixpoint(&lam.fsort().to_poly());
1697 fixpoint::ConstDecl { name, comment, sort }
1698 })
1699 .name
1700 }
1701
1702 fn assume_const_values(
1703 &mut self,
1704 mut constraint: fixpoint::Constraint,
1705 scx: &mut SortEncodingCtxt,
1706 ) -> QueryResult<fixpoint::Constraint> {
1707 let mut idx = 0;
1710 while let Some((key, const_)) = self.const_map.get_index(idx) {
1711 idx += 1;
1712
1713 let ConstKey::RustConst(def_id) = key else { continue };
1714 let info = self.genv.constant_info(def_id)?;
1715 match info {
1716 rty::ConstantInfo::Uninterpreted => {}
1717 rty::ConstantInfo::Interpreted(val, _) => {
1718 let e1 = fixpoint::Expr::Var(const_.name);
1719 let e2 = self.expr_to_fixpoint(&val, scx)?;
1720 let pred = fixpoint::Pred::Expr(e1.eq(e2));
1721 constraint = fixpoint::Constraint::ForAll(
1722 fixpoint::Bind {
1723 name: fixpoint::Var::Underscore,
1724 sort: fixpoint::Sort::Int,
1725 pred,
1726 },
1727 Box::new(constraint),
1728 );
1729 }
1730 }
1731 }
1732 Ok(constraint)
1733 }
1734
1735 fn qualifiers_for(
1736 &mut self,
1737 def_id: LocalDefId,
1738 scx: &mut SortEncodingCtxt,
1739 ) -> QueryResult<Vec<fixpoint::Qualifier>> {
1740 self.genv
1741 .qualifiers_for(def_id)?
1742 .map(|qual| self.qualifier_to_fixpoint(qual, scx))
1743 .try_collect()
1744 }
1745
1746 fn define_funs(
1747 &mut self,
1748 def_id: MaybeExternId,
1749 scx: &mut SortEncodingCtxt,
1750 ) -> QueryResult<(Vec<fixpoint::FunDef>, Vec<fixpoint::ConstDecl>)> {
1751 let reveals: UnordSet<FluxDefId> = self
1752 .genv
1753 .reveals_for(def_id.local_id())
1754 .iter()
1755 .copied()
1756 .collect();
1757 let proven_externally = self.genv.proven_externally(def_id.local_id());
1758 let mut consts = vec![];
1759 let mut defs = vec![];
1760
1761 let mut idx = 0;
1764 while let Some((&did, &name)) = self.fun_def_map.get_index(idx) {
1765 idx += 1;
1766
1767 let comment = format!("flux def: {did:?}");
1768 let info = self.genv.normalized_info(did);
1769 let revealed = reveals.contains(&did);
1770 if info.hide && !revealed && !proven_externally {
1771 let sort = scx.func_sort_to_fixpoint(&self.genv.func_sort(did));
1772 consts.push(fixpoint::ConstDecl { name, sort, comment: Some(comment) });
1773 } else {
1774 let out = scx.sort_to_fixpoint(self.genv.func_sort(did).expect_mono().output());
1775 let (args, body) = self.body_to_fixpoint(&info.body, scx)?;
1776 let fun_def = fixpoint::FunDef { name, args, body, out, comment: Some(comment) };
1777 defs.push((info.rank, fun_def));
1778 };
1779 }
1780
1781 let defs = defs
1783 .into_iter()
1784 .sorted_by_key(|(rank, _)| *rank)
1785 .map(|(_, def)| def)
1786 .collect();
1787
1788 Ok((defs, consts))
1789 }
1790
1791 fn body_to_fixpoint(
1792 &mut self,
1793 body: &rty::Binder<rty::Expr>,
1794 scx: &mut SortEncodingCtxt,
1795 ) -> QueryResult<(Vec<(fixpoint::Var, fixpoint::Sort)>, fixpoint::Expr)> {
1796 self.local_var_env
1797 .push_layer_with_fresh_names(body.vars().len());
1798
1799 let expr = self.expr_to_fixpoint(body.as_ref().skip_binder(), scx)?;
1800
1801 let args: Vec<(fixpoint::Var, fixpoint::Sort)> =
1802 iter::zip(self.local_var_env.pop_layer(), body.vars())
1803 .map(|(name, var)| (name.into(), scx.sort_to_fixpoint(var.expect_sort())))
1804 .collect();
1805
1806 Ok((args, expr))
1807 }
1808
1809 fn qualifier_to_fixpoint(
1810 &mut self,
1811 qualifier: &rty::Qualifier,
1812 scx: &mut SortEncodingCtxt,
1813 ) -> QueryResult<fixpoint::Qualifier> {
1814 let (args, body) = self.body_to_fixpoint(&qualifier.body, scx)?;
1815 let name = qualifier.def_id.name().to_string();
1816 Ok(fixpoint::Qualifier { name, args, body })
1817 }
1818}
1819
1820fn mk_implies(assumption: fixpoint::Pred, cstr: fixpoint::Constraint) -> fixpoint::Constraint {
1821 fixpoint::Constraint::ForAll(
1822 fixpoint::Bind {
1823 name: fixpoint::Var::Underscore,
1824 sort: fixpoint::Sort::Int,
1825 pred: assumption,
1826 },
1827 Box::new(cstr),
1828 )
1829}