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_span::Span;
34use rustc_type_ir::{BoundVar, DebruijnIndex};
35use serde::{Deserialize, Deserializer, Serialize};
36
37pub mod fixpoint {
38 use std::fmt;
39
40 use flux_middle::rty::{EarlyReftParam, Real};
41 use liquid_fixpoint::{FixpointFmt, Identifier};
42 use rustc_abi::VariantIdx;
43 use rustc_index::newtype_index;
44 use rustc_middle::ty::ParamConst;
45 use rustc_span::Symbol;
46
47 newtype_index! {
48 pub struct KVid {}
49 }
50
51 impl Identifier for KVid {
52 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
53 write!(f, "k{}", self.as_u32())
54 }
55 }
56
57 newtype_index! {
58 pub struct LocalVar {}
59 }
60
61 newtype_index! {
62 pub struct GlobalVar {}
63 }
64
65 newtype_index! {
66 pub struct AdtId {}
69 }
70
71 #[derive(Hash, Copy, Clone)]
72 pub enum Var {
73 Underscore,
74 Global(GlobalVar, Option<Symbol>),
75 Local(LocalVar),
76 DataCtor(AdtId, VariantIdx),
77 TupleCtor {
78 arity: usize,
79 },
80 TupleProj {
81 arity: usize,
82 field: u32,
83 },
84 UIFRel(BinRel),
85 Itf(liquid_fixpoint::ThyFunc),
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::Itf(name) => write!(f, "{name}"),
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)]
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)]
154 pub struct SymStr(pub Symbol);
155
156 impl FixpointFmt for SymStr {
157 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
158 write!(f, "\"{}\"", self.0)
159 }
160 }
161
162 liquid_fixpoint::declare_types! {
163 type Sort = DataSort;
164 type KVar = KVid;
165 type Var = Var;
166 type Decimal = Real;
167 type String = SymStr;
168 type Tag = super::TagIdx;
169 }
170 pub use fixpoint_generated::*;
171}
172
173newtype_index! {
174 #[debug_format = "TagIdx({})"]
175 pub struct TagIdx {}
176}
177
178impl Serialize for TagIdx {
179 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
180 self.as_u32().serialize(serializer)
181 }
182}
183
184impl<'de> Deserialize<'de> for TagIdx {
185 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
186 let idx = usize::deserialize(deserializer)?;
187 Ok(TagIdx::from_u32(idx as u32))
188 }
189}
190
191#[derive(Default)]
193struct SortEncodingCtxt {
194 tuples: UnordSet<usize>,
196 adt_sorts: FxIndexSet<DefId>,
199}
200
201impl SortEncodingCtxt {
202 fn sort_to_fixpoint(&mut self, sort: &rty::Sort) -> fixpoint::Sort {
203 match sort {
204 rty::Sort::Int => fixpoint::Sort::Int,
205 rty::Sort::Real => fixpoint::Sort::Real,
206 rty::Sort::Bool => fixpoint::Sort::Bool,
207 rty::Sort::Str => fixpoint::Sort::Str,
208 rty::Sort::Char => fixpoint::Sort::Int,
209 rty::Sort::BitVec(size) => fixpoint::Sort::BitVec(Box::new(bv_size_to_fixpoint(*size))),
210 rty::Sort::App(rty::SortCtor::User { .. }, _)
215 | rty::Sort::Param(_)
216 | rty::Sort::Alias(rty::AliasKind::Opaque | rty::AliasKind::Projection, ..) => {
217 fixpoint::Sort::Int
218 }
219 rty::Sort::App(rty::SortCtor::Set, args) => {
220 let args = args.iter().map(|s| self.sort_to_fixpoint(s)).collect_vec();
221 fixpoint::Sort::App(fixpoint::SortCtor::Set, args)
222 }
223 rty::Sort::App(rty::SortCtor::Map, args) => {
224 let args = args.iter().map(|s| self.sort_to_fixpoint(s)).collect_vec();
225 fixpoint::Sort::App(fixpoint::SortCtor::Map, args)
226 }
227 rty::Sort::App(rty::SortCtor::Adt(sort_def), args) => {
228 if let Some(variant) = sort_def.opt_struct_variant() {
229 let sorts = variant.field_sorts(args);
230 if let [sort] = &sorts[..] {
232 self.sort_to_fixpoint(sort)
233 } else {
234 self.declare_tuple(sorts.len());
235 let ctor = fixpoint::SortCtor::Data(fixpoint::DataSort::Tuple(sorts.len()));
236 let args = sorts.iter().map(|s| self.sort_to_fixpoint(s)).collect_vec();
237 fixpoint::Sort::App(ctor, args)
238 }
239 } else {
240 debug_assert!(args.is_empty());
241 let adt_id = self.declare_adt(sort_def.did());
242 fixpoint::Sort::App(
243 fixpoint::SortCtor::Data(fixpoint::DataSort::Adt(adt_id)),
244 vec![],
245 )
246 }
247 }
248 rty::Sort::Tuple(sorts) => {
249 if let [sort] = &sorts[..] {
251 self.sort_to_fixpoint(sort)
252 } else {
253 self.declare_tuple(sorts.len());
254 let ctor = fixpoint::SortCtor::Data(fixpoint::DataSort::Tuple(sorts.len()));
255 let args = sorts.iter().map(|s| self.sort_to_fixpoint(s)).collect();
256 fixpoint::Sort::App(ctor, args)
257 }
258 }
259 rty::Sort::Func(sort) => self.func_sort_to_fixpoint(sort),
260 rty::Sort::Var(k) => fixpoint::Sort::Var(k.index()),
261 rty::Sort::Err
262 | rty::Sort::Infer(_)
263 | rty::Sort::Loc
264 | rty::Sort::Alias(rty::AliasKind::Free, _) => {
265 tracked_span_bug!("unexpected sort `{sort:?}`")
266 }
267 }
268 }
269
270 fn func_sort_to_fixpoint(&mut self, fsort: &rty::PolyFuncSort) -> fixpoint::Sort {
271 let params = fsort.params().len();
272 let fsort = fsort.skip_binders();
273 let output = self.sort_to_fixpoint(fsort.output());
274 fixpoint::Sort::mk_func(
275 params,
276 fsort.inputs().iter().map(|s| self.sort_to_fixpoint(s)),
277 output,
278 )
279 }
280
281 fn declare_tuple(&mut self, arity: usize) {
282 self.tuples.insert(arity);
283 }
284
285 pub fn declare_adt(&mut self, did: DefId) -> AdtId {
286 if let Some(idx) = self.adt_sorts.get_index_of(&did) {
287 AdtId::from_usize(idx)
288 } else {
289 let adt_id = AdtId::from_usize(self.adt_sorts.len());
290 self.adt_sorts.insert(did);
291 adt_id
292 }
293 }
294
295 fn append_adt_decls(
296 genv: GlobalEnv,
297 adt_sorts: FxIndexSet<DefId>,
298 decls: &mut Vec<fixpoint::DataDecl>,
299 ) -> QueryResult {
300 for (idx, adt_def_id) in adt_sorts.iter().enumerate() {
301 let adt_id = AdtId::from_usize(idx);
302 let adt_sort_def = genv.adt_sort_def_of(adt_def_id)?;
303 decls.push(fixpoint::DataDecl {
304 name: fixpoint::DataSort::Adt(adt_id),
305 vars: adt_sort_def.param_count(),
306 ctors: adt_sort_def
307 .variants()
308 .iter_enumerated()
309 .map(|(idx, variant)| {
310 debug_assert_eq!(variant.fields(), 0);
311 fixpoint::DataCtor {
312 name: fixpoint::Var::DataCtor(adt_id, idx),
313 fields: vec![],
314 }
315 })
316 .collect(),
317 });
318 }
319 Ok(())
320 }
321
322 fn append_tuple_decls(tuples: UnordSet<usize>, decls: &mut Vec<fixpoint::DataDecl>) {
323 decls.extend(
324 tuples
325 .into_items()
326 .into_sorted_stable_ord()
327 .into_iter()
328 .map(|arity| {
329 fixpoint::DataDecl {
330 name: fixpoint::DataSort::Tuple(arity),
331 vars: arity,
332 ctors: vec![fixpoint::DataCtor {
333 name: fixpoint::Var::TupleCtor { arity },
334 fields: (0..(arity as u32))
335 .map(|field| {
336 fixpoint::DataField {
337 name: fixpoint::Var::TupleProj { arity, field },
338 sort: fixpoint::Sort::Var(field as usize),
339 }
340 })
341 .collect(),
342 }],
343 }
344 }),
345 );
346 }
347
348 fn into_data_decls(self, genv: GlobalEnv) -> QueryResult<Vec<fixpoint::DataDecl>> {
349 let mut decls = vec![];
350 Self::append_tuple_decls(self.tuples, &mut decls);
351 Self::append_adt_decls(genv, self.adt_sorts, &mut decls)?;
352 Ok(decls)
353 }
354}
355
356fn bv_size_to_fixpoint(size: rty::BvSize) -> fixpoint::Sort {
357 match size {
358 rty::BvSize::Fixed(size) => fixpoint::Sort::BvSize(size),
359 rty::BvSize::Param(_var) => {
360 bug!("unexpected parametric bit-vector size")
365 }
366 rty::BvSize::Infer(_) => bug!("unexpected infer variable for bit-vector size"),
367 }
368}
369
370type FunDefMap = FxIndexMap<FluxDefId, fixpoint::Var>;
371type ConstMap<'tcx> = FxIndexMap<ConstKey<'tcx>, fixpoint::ConstDecl>;
372
373#[derive(Eq, Hash, PartialEq)]
374enum ConstKey<'tcx> {
375 Uif(FluxDefId),
376 RustConst(DefId),
377 Alias(FluxDefId, rustc_middle::ty::GenericArgsRef<'tcx>),
378 Lambda(Lambda),
379 PrimOp(rty::BinOp),
380}
381
382pub struct FixpointCtxt<'genv, 'tcx, T: Eq + Hash> {
383 comments: Vec<String>,
384 genv: GlobalEnv<'genv, 'tcx>,
385 kvars: KVarGen,
386 scx: SortEncodingCtxt,
387 kcx: KVarEncodingCtxt,
388 ecx: ExprEncodingCtxt<'genv, 'tcx>,
389 tags: IndexVec<TagIdx, T>,
390 tags_inv: UnordMap<T, TagIdx>,
391 def_id: MaybeExternId,
394}
395
396pub type FixQueryCache = QueryCache<FixpointResult<TagIdx>>;
397
398impl<'genv, 'tcx, Tag> FixpointCtxt<'genv, 'tcx, Tag>
399where
400 Tag: std::hash::Hash + Eq + Copy,
401{
402 pub fn new(genv: GlobalEnv<'genv, 'tcx>, def_id: MaybeExternId, kvars: KVarGen) -> Self {
403 let def_span = genv.tcx().def_span(def_id);
404 Self {
405 comments: vec![],
406 kvars,
407 scx: SortEncodingCtxt::default(),
408 genv,
409 ecx: ExprEncodingCtxt::new(genv, def_span),
410 kcx: Default::default(),
411 tags: IndexVec::new(),
412 tags_inv: Default::default(),
413 def_id,
414 }
415 }
416
417 pub fn check(
418 mut self,
419 cache: &mut FixQueryCache,
420 constraint: fixpoint::Constraint,
421 kind: FixpointQueryKind,
422 scrape_quals: bool,
423 solver: SmtSolver,
424 ) -> QueryResult<Vec<Tag>> {
425 if !constraint.is_concrete() {
427 self.ecx.errors.into_result()?;
428 return Ok(vec![]);
429 }
430 let def_span = self.def_span();
431 let def_id = self.def_id;
432
433 let kvars = self.kcx.into_fixpoint();
434
435 let (define_funs, define_constants) = self.ecx.define_funs(def_id, &mut self.scx)?;
436 let qualifiers = self.ecx.qualifiers_for(def_id.local_id(), &mut self.scx)?;
437
438 let constraint = self.ecx.assume_const_values(constraint, &mut self.scx)?;
441
442 let mut constants = self.ecx.const_map.into_values().collect_vec();
443 constants.extend(define_constants);
444
445 for rel in fixpoint::BinRel::INEQUALITIES {
446 let sort = fixpoint::Sort::mk_func(
448 1,
449 [fixpoint::Sort::Var(0), fixpoint::Sort::Var(0)],
450 fixpoint::Sort::Bool,
451 );
452 constants.push(fixpoint::ConstDecl {
453 name: fixpoint::Var::UIFRel(rel),
454 sort,
455 comment: None,
456 });
457 }
458
459 self.ecx.errors.into_result()?;
461
462 let task = fixpoint::Task {
463 comments: self.comments,
464 constants,
465 kvars,
466 define_funs,
467 constraint,
468 qualifiers,
469 scrape_quals,
470 solver,
471 data_decls: self.scx.into_data_decls(self.genv)?,
472 };
473 if config::dump_constraint() {
474 dbg::dump_item_info(self.genv.tcx(), self.def_id.resolved_id(), "smt2", &task).unwrap();
475 }
476
477 match Self::run_task_with_cache(self.genv, task, self.def_id.resolved_id(), kind, cache) {
478 FixpointResult::Safe(_) => Ok(vec![]),
479 FixpointResult::Unsafe(_, errors) => {
480 Ok(errors
481 .into_iter()
482 .map(|err| self.tags[err.tag])
483 .unique()
484 .collect_vec())
485 }
486 FixpointResult::Crash(err) => span_bug!(def_span, "fixpoint crash: {err:?}"),
487 }
488 }
489
490 fn run_task_with_cache(
491 genv: GlobalEnv,
492 task: fixpoint::Task,
493 def_id: DefId,
494 kind: FixpointQueryKind,
495 cache: &mut FixQueryCache,
496 ) -> FixpointResult<TagIdx> {
497 let key = kind.task_key(genv.tcx(), def_id);
498
499 let hash = task.hash_with_default();
500
501 if config::is_cache_enabled()
502 && let Some(result) = cache.lookup(&key, hash)
503 {
504 return result.clone();
505 }
506 let result = timings::time_it(TimingKind::FixpointQuery(def_id, kind), || {
507 task.run()
508 .unwrap_or_else(|err| tracked_span_bug!("failed to run fixpoint: {err}"))
509 });
510
511 if config::is_cache_enabled() {
512 cache.insert(key, hash, result.clone());
513 }
514 result
515 }
516
517 fn tag_idx(&mut self, tag: Tag) -> TagIdx
518 where
519 Tag: std::fmt::Debug,
520 {
521 *self.tags_inv.entry(tag).or_insert_with(|| {
522 let idx = self.tags.push(tag);
523 self.comments.push(format!("Tag {idx}: {tag:?}"));
524 idx
525 })
526 }
527
528 pub(crate) fn with_name_map<R>(
529 &mut self,
530 name: rty::Name,
531 f: impl FnOnce(&mut Self, fixpoint::LocalVar) -> R,
532 ) -> R {
533 let fresh = self.ecx.local_var_env.insert_fvar_map(name);
534 let r = f(self, fresh);
535 self.ecx.local_var_env.remove_fvar_map(name);
536 r
537 }
538
539 pub(crate) fn sort_to_fixpoint(&mut self, sort: &rty::Sort) -> fixpoint::Sort {
540 self.scx.sort_to_fixpoint(sort)
541 }
542
543 pub(crate) fn var_to_fixpoint(&self, var: &rty::Var) -> fixpoint::Var {
544 self.ecx.var_to_fixpoint(var)
545 }
546
547 pub(crate) fn head_to_fixpoint(
552 &mut self,
553 expr: &rty::Expr,
554 mk_tag: impl Fn(Option<ESpan>) -> Tag + Copy,
555 ) -> QueryResult<fixpoint::Constraint>
556 where
557 Tag: std::fmt::Debug,
558 {
559 match expr.kind() {
560 rty::ExprKind::BinaryOp(rty::BinOp::And, ..) => {
561 let cstrs = expr
563 .flatten_conjs()
564 .into_iter()
565 .map(|e| self.head_to_fixpoint(e, mk_tag))
566 .try_collect()?;
567 Ok(fixpoint::Constraint::conj(cstrs))
568 }
569 rty::ExprKind::BinaryOp(rty::BinOp::Imp, e1, e2) => {
570 let (bindings, assumption) = self.assumption_to_fixpoint(e1)?;
571 let cstr = self.head_to_fixpoint(e2, mk_tag)?;
572 Ok(fixpoint::Constraint::foralls(bindings, mk_implies(assumption, cstr)))
573 }
574 rty::ExprKind::KVar(kvar) => {
575 let mut bindings = vec![];
576 let pred = self.kvar_to_fixpoint(kvar, &mut bindings)?;
577 Ok(fixpoint::Constraint::foralls(bindings, fixpoint::Constraint::Pred(pred, None)))
578 }
579 rty::ExprKind::ForAll(pred) => {
580 self.ecx
581 .local_var_env
582 .push_layer_with_fresh_names(pred.vars().len());
583 let cstr = self.head_to_fixpoint(pred.as_ref().skip_binder(), mk_tag)?;
584 let vars = self.ecx.local_var_env.pop_layer();
585
586 let bindings = iter::zip(vars, pred.vars())
587 .map(|(var, kind)| {
588 fixpoint::Bind {
589 name: var.into(),
590 sort: self.scx.sort_to_fixpoint(kind.expect_sort()),
591 pred: fixpoint::Pred::TRUE,
592 }
593 })
594 .collect_vec();
595
596 Ok(fixpoint::Constraint::foralls(bindings, cstr))
597 }
598 _ => {
599 let tag_idx = self.tag_idx(mk_tag(expr.span()));
600 let pred = fixpoint::Pred::Expr(self.ecx.expr_to_fixpoint(expr, &mut self.scx)?);
601 Ok(fixpoint::Constraint::Pred(pred, Some(tag_idx)))
602 }
603 }
604 }
605
606 pub(crate) fn assumption_to_fixpoint(
611 &mut self,
612 pred: &rty::Expr,
613 ) -> QueryResult<(Vec<fixpoint::Bind>, fixpoint::Pred)> {
614 let mut bindings = vec![];
615 let mut preds = vec![];
616 self.assumption_to_fixpoint_aux(pred, &mut bindings, &mut preds)?;
617 Ok((bindings, fixpoint::Pred::and(preds)))
618 }
619
620 fn assumption_to_fixpoint_aux(
622 &mut self,
623 expr: &rty::Expr,
624 bindings: &mut Vec<fixpoint::Bind>,
625 preds: &mut Vec<fixpoint::Pred>,
626 ) -> QueryResult {
627 match expr.kind() {
628 rty::ExprKind::BinaryOp(rty::BinOp::And, e1, e2) => {
629 self.assumption_to_fixpoint_aux(e1, bindings, preds)?;
630 self.assumption_to_fixpoint_aux(e2, bindings, preds)?;
631 }
632 rty::ExprKind::KVar(kvar) => {
633 preds.push(self.kvar_to_fixpoint(kvar, bindings)?);
634 }
635 rty::ExprKind::ForAll(_) => {
636 preds.push(fixpoint::Pred::TRUE);
642 }
643 _ => {
644 preds.push(fixpoint::Pred::Expr(self.ecx.expr_to_fixpoint(expr, &mut self.scx)?));
645 }
646 }
647 Ok(())
648 }
649
650 fn kvar_to_fixpoint(
651 &mut self,
652 kvar: &rty::KVar,
653 bindings: &mut Vec<fixpoint::Bind>,
654 ) -> QueryResult<fixpoint::Pred> {
655 let decl = self.kvars.get(kvar.kvid);
656 let kvids = self.kcx.encode(kvar.kvid, decl, &mut self.scx);
657
658 let all_args = iter::zip(&kvar.args, &decl.sorts)
659 .map(|(arg, sort)| self.ecx.imm(arg, sort, &mut self.scx, bindings))
660 .try_collect_vec()?;
661
662 if all_args.is_empty() {
666 let fresh = self.ecx.local_var_env.fresh_name();
667 let var = fixpoint::Var::Local(fresh);
668 bindings.push(fixpoint::Bind {
669 name: fresh.into(),
670 sort: fixpoint::Sort::Int,
671 pred: fixpoint::Pred::Expr(fixpoint::Expr::eq(
672 fixpoint::Expr::Var(var),
673 fixpoint::Expr::int(0),
674 )),
675 });
676 return Ok(fixpoint::Pred::KVar(kvids[0], vec![var]));
677 }
678
679 let kvars = kvids
680 .iter()
681 .enumerate()
682 .map(|(i, kvid)| {
683 let args = all_args[i..].to_vec();
684 fixpoint::Pred::KVar(*kvid, args)
685 })
686 .collect_vec();
687
688 Ok(fixpoint::Pred::And(kvars))
689 }
690
691 fn def_span(&self) -> Span {
692 self.genv.tcx().def_span(self.def_id)
693 }
694}
695
696fn const_to_fixpoint(cst: rty::Constant) -> fixpoint::Expr {
697 match cst {
698 rty::Constant::Int(i) => {
699 if i.is_negative() {
700 fixpoint::Expr::Neg(Box::new(fixpoint::Constant::Numeral(i.abs()).into()))
701 } else {
702 fixpoint::Constant::Numeral(i.abs()).into()
703 }
704 }
705 rty::Constant::Real(r) => fixpoint::Constant::Decimal(r).into(),
706 rty::Constant::Bool(b) => fixpoint::Constant::Boolean(b).into(),
707 rty::Constant::Char(c) => fixpoint::Constant::Numeral(u128::from(c)).into(),
708 rty::Constant::Str(s) => fixpoint::Constant::String(fixpoint::SymStr(s)).into(),
709 rty::Constant::BitVec(i, size) => fixpoint::Constant::BitVec(i, size).into(),
710 }
711}
712
713struct FixpointKVar {
714 sorts: Vec<fixpoint::Sort>,
715 orig: rty::KVid,
716}
717
718#[derive(Default)]
721struct KVarEncodingCtxt {
722 kvars: IndexVec<fixpoint::KVid, FixpointKVar>,
724 map: UnordMap<rty::KVid, Vec<fixpoint::KVid>>,
726}
727
728impl KVarEncodingCtxt {
729 fn encode(
730 &mut self,
731 kvid: rty::KVid,
732 decl: &KVarDecl,
733 scx: &mut SortEncodingCtxt,
734 ) -> &[fixpoint::KVid] {
735 self.map.entry(kvid).or_insert_with(|| {
736 let all_args = decl
737 .sorts
738 .iter()
739 .map(|s| scx.sort_to_fixpoint(s))
740 .collect_vec();
741
742 if all_args.is_empty() {
744 let sorts = vec![fixpoint::Sort::Int];
745 let kvid = self.kvars.push(FixpointKVar::new(sorts, kvid));
746 return vec![kvid];
747 }
748
749 match decl.encoding {
750 KVarEncoding::Single => {
751 let kvid = self.kvars.push(FixpointKVar::new(all_args, kvid));
752 vec![kvid]
753 }
754 KVarEncoding::Conj => {
755 let n = usize::max(decl.self_args, 1);
756 (0..n)
757 .map(|i| {
758 let sorts = all_args[i..].to_vec();
759 self.kvars.push(FixpointKVar::new(sorts, kvid))
760 })
761 .collect_vec()
762 }
763 }
764 })
765 }
766
767 fn into_fixpoint(self) -> Vec<fixpoint::KVarDecl> {
768 self.kvars
769 .into_iter_enumerated()
770 .map(|(kvid, kvar)| {
771 fixpoint::KVarDecl::new(kvid, kvar.sorts, format!("orig: {:?}", kvar.orig))
772 })
773 .collect()
774 }
775}
776
777struct LocalVarEnv {
779 local_var_gen: IndexGen<fixpoint::LocalVar>,
780 fvars: UnordMap<rty::Name, fixpoint::LocalVar>,
781 layers: Vec<Vec<fixpoint::LocalVar>>,
783}
784
785impl LocalVarEnv {
786 fn new() -> Self {
787 Self { local_var_gen: IndexGen::new(), fvars: Default::default(), layers: Vec::new() }
788 }
789
790 fn fresh_name(&mut self) -> fixpoint::LocalVar {
793 self.local_var_gen.fresh()
794 }
795
796 fn insert_fvar_map(&mut self, name: rty::Name) -> fixpoint::LocalVar {
797 let fresh = self.fresh_name();
798 self.fvars.insert(name, fresh);
799 fresh
800 }
801
802 fn remove_fvar_map(&mut self, name: rty::Name) {
803 self.fvars.remove(&name);
804 }
805
806 fn push_layer_with_fresh_names(&mut self, count: usize) {
808 let layer = (0..count).map(|_| self.fresh_name()).collect();
809 self.layers.push(layer);
810 }
811
812 fn pop_layer(&mut self) -> Vec<fixpoint::LocalVar> {
813 self.layers.pop().unwrap()
814 }
815
816 fn get_fvar(&self, name: rty::Name) -> Option<fixpoint::LocalVar> {
817 self.fvars.get(&name).copied()
818 }
819
820 fn get_late_bvar(&self, debruijn: DebruijnIndex, var: BoundVar) -> Option<fixpoint::LocalVar> {
821 let depth = self.layers.len().checked_sub(debruijn.as_usize() + 1)?;
822 self.layers[depth].get(var.as_usize()).copied()
823 }
824}
825
826impl FixpointKVar {
827 fn new(sorts: Vec<fixpoint::Sort>, orig: rty::KVid) -> Self {
828 Self { sorts, orig }
829 }
830}
831
832pub struct KVarGen {
833 kvars: IndexVec<rty::KVid, KVarDecl>,
834 dummy: bool,
839}
840
841impl KVarGen {
842 pub(crate) fn new(dummy: bool) -> Self {
843 Self { kvars: IndexVec::new(), dummy }
844 }
845
846 fn get(&self, kvid: rty::KVid) -> &KVarDecl {
847 &self.kvars[kvid]
848 }
849
850 pub fn fresh(
869 &mut self,
870 binders: &[rty::BoundVariableKinds],
871 scope: impl IntoIterator<Item = (rty::Var, rty::Sort)>,
872 encoding: KVarEncoding,
873 ) -> rty::Expr {
874 if self.dummy {
875 return rty::Expr::hole(rty::HoleKind::Pred);
876 }
877
878 let args = itertools::chain(
879 binders.iter().rev().enumerate().flat_map(|(level, vars)| {
880 let debruijn = DebruijnIndex::from_usize(level);
881 vars.iter()
882 .cloned()
883 .enumerate()
884 .flat_map(move |(idx, var)| {
885 if let rty::BoundVariableKind::Refine(sort, _, kind) = var {
886 let br = rty::BoundReft { var: BoundVar::from_usize(idx), kind };
887 Some((rty::Var::Bound(debruijn, br), sort))
888 } else {
889 None
890 }
891 })
892 }),
893 scope,
894 );
895 let [.., last] = binders else {
896 return self.fresh_inner(0, [], encoding);
897 };
898 let num_self_args = last
899 .iter()
900 .filter(|var| matches!(var, rty::BoundVariableKind::Refine(..)))
901 .count();
902 self.fresh_inner(num_self_args, args, encoding)
903 }
904
905 fn fresh_inner<A>(&mut self, self_args: usize, args: A, encoding: KVarEncoding) -> rty::Expr
906 where
907 A: IntoIterator<Item = (rty::Var, rty::Sort)>,
908 {
909 let mut sorts = vec![];
911 let mut exprs = vec![];
912
913 let mut flattened_self_args = 0;
914 for (i, (var, sort)) in args.into_iter().enumerate() {
915 let is_self_arg = i < self_args;
916 let var = var.to_expr();
917 sort.walk(|sort, proj| {
918 if !matches!(sort, rty::Sort::Loc) {
919 flattened_self_args += is_self_arg as usize;
920 sorts.push(sort.clone());
921 exprs.push(rty::Expr::field_projs(&var, proj));
922 }
923 });
924 }
925
926 let kvid = self
927 .kvars
928 .push(KVarDecl { self_args: flattened_self_args, sorts, encoding });
929
930 let kvar = rty::KVar::new(kvid, flattened_self_args, exprs);
931 rty::Expr::kvar(kvar)
932 }
933}
934
935#[derive(Clone)]
936struct KVarDecl {
937 self_args: usize,
938 sorts: Vec<rty::Sort>,
939 encoding: KVarEncoding,
940}
941
942#[derive(Clone, Copy)]
944pub enum KVarEncoding {
945 Single,
948 Conj,
952}
953
954impl std::fmt::Display for TagIdx {
955 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
956 write!(f, "{}", self.as_u32())
957 }
958}
959
960impl std::str::FromStr for TagIdx {
961 type Err = std::num::ParseIntError;
962
963 fn from_str(s: &str) -> Result<Self, Self::Err> {
964 Ok(Self::from_u32(s.parse()?))
965 }
966}
967
968struct ExprEncodingCtxt<'genv, 'tcx> {
969 genv: GlobalEnv<'genv, 'tcx>,
970 local_var_env: LocalVarEnv,
971 global_var_gen: IndexGen<fixpoint::GlobalVar>,
972 const_map: ConstMap<'tcx>,
973 fun_def_map: FunDefMap,
974 errors: Errors<'genv>,
975 def_span: Span,
976}
977
978impl<'genv, 'tcx> ExprEncodingCtxt<'genv, 'tcx> {
979 fn new(genv: GlobalEnv<'genv, 'tcx>, def_span: Span) -> Self {
980 Self {
981 genv,
982 local_var_env: LocalVarEnv::new(),
983 global_var_gen: IndexGen::new(),
984 const_map: Default::default(),
985 fun_def_map: Default::default(),
986 errors: Errors::new(genv.sess()),
987 def_span,
988 }
989 }
990
991 fn var_to_fixpoint(&self, var: &rty::Var) -> fixpoint::Var {
992 match var {
993 rty::Var::Free(name) => {
994 self.local_var_env
995 .get_fvar(*name)
996 .unwrap_or_else(|| {
997 span_bug!(self.def_span, "no entry found for name: `{name:?}`")
998 })
999 .into()
1000 }
1001 rty::Var::Bound(debruijn, breft) => {
1002 self.local_var_env
1003 .get_late_bvar(*debruijn, breft.var)
1004 .unwrap_or_else(|| {
1005 span_bug!(self.def_span, "no entry found for late bound var: `{breft:?}`")
1006 })
1007 .into()
1008 }
1009 rty::Var::ConstGeneric(param) => fixpoint::Var::ConstGeneric(*param),
1010 rty::Var::EarlyParam(param) => fixpoint::Var::Param(*param),
1011 rty::Var::EVar(_) => {
1012 span_bug!(self.def_span, "unexpected evar: `{var:?}`")
1013 }
1014 }
1015 }
1016
1017 fn variant_to_fixpoint(
1018 &self,
1019 scx: &mut SortEncodingCtxt,
1020 enum_def_id: &DefId,
1021 idx: VariantIdx,
1022 ) -> fixpoint::Expr {
1023 let adt_id = scx.declare_adt(*enum_def_id);
1024 let var = fixpoint::Var::DataCtor(adt_id, idx);
1025 fixpoint::Expr::Var(var)
1026 }
1027
1028 fn fields_to_fixpoint(
1029 &mut self,
1030 flds: &[rty::Expr],
1031 scx: &mut SortEncodingCtxt,
1032 ) -> QueryResult<fixpoint::Expr> {
1033 if let [fld] = flds {
1035 self.expr_to_fixpoint(fld, scx)
1036 } else {
1037 scx.declare_tuple(flds.len());
1038 let ctor = fixpoint::Expr::Var(fixpoint::Var::TupleCtor { arity: flds.len() });
1039 let args = flds
1040 .iter()
1041 .map(|fld| self.expr_to_fixpoint(fld, scx))
1042 .try_collect()?;
1043 Ok(fixpoint::Expr::App(Box::new(ctor), args))
1044 }
1045 }
1046
1047 fn internal_func_to_fixpoint(
1048 &mut self,
1049 internal_func: &InternalFuncKind,
1050 args: &[rty::Expr],
1051 scx: &mut SortEncodingCtxt,
1052 ) -> QueryResult<fixpoint::Expr> {
1053 match internal_func {
1054 InternalFuncKind::Val(op) => {
1055 let func = fixpoint::Expr::Var(self.define_const_for_prim_op(op, scx));
1056 let args = self.exprs_to_fixpoint(args, scx)?;
1057 Ok(fixpoint::Expr::App(Box::new(func), args))
1058 }
1059 InternalFuncKind::Rel(op) => {
1060 let expr = if let Some(prim_rel) = self.genv.prim_rel_for(op)? {
1061 prim_rel.body.replace_bound_refts(args)
1062 } else {
1063 rty::Expr::tt()
1064 };
1065 self.expr_to_fixpoint(&expr, scx)
1066 }
1067 InternalFuncKind::CharToInt | InternalFuncKind::IntToChar => {
1068 self.expr_to_fixpoint(&args[0], scx)
1071 }
1072 }
1073 }
1074
1075 fn expr_to_fixpoint(
1076 &mut self,
1077 expr: &rty::Expr,
1078 scx: &mut SortEncodingCtxt,
1079 ) -> QueryResult<fixpoint::Expr> {
1080 let e = match expr.kind() {
1081 rty::ExprKind::Var(var) => fixpoint::Expr::Var(self.var_to_fixpoint(var)),
1082 rty::ExprKind::Constant(c) => const_to_fixpoint(*c),
1083 rty::ExprKind::BinaryOp(op, e1, e2) => self.bin_op_to_fixpoint(op, e1, e2, scx)?,
1084 rty::ExprKind::UnaryOp(op, e) => self.un_op_to_fixpoint(*op, e, scx)?,
1085 rty::ExprKind::FieldProj(e, proj) => self.proj_to_fixpoint(e, *proj, scx)?,
1086 rty::ExprKind::Tuple(flds) => self.fields_to_fixpoint(flds, scx)?,
1087 rty::ExprKind::Ctor(rty::Ctor::Struct(_), flds) => {
1088 self.fields_to_fixpoint(flds, scx)?
1089 }
1090 rty::ExprKind::Ctor(rty::Ctor::Enum(did, idx), _) => {
1091 self.variant_to_fixpoint(scx, did, *idx)
1092 }
1093 rty::ExprKind::ConstDefId(did) => {
1094 let var = self.define_const_for_rust_const(*did, scx);
1095 fixpoint::Expr::Var(var)
1096 }
1097 rty::ExprKind::App(func, args) => {
1098 if let rty::ExprKind::InternalFunc(func) = func.kind() {
1099 self.internal_func_to_fixpoint(func, args, scx)?
1100 } else {
1101 let func = self.expr_to_fixpoint(func, scx)?;
1102 let args = self.exprs_to_fixpoint(args, scx)?;
1103 fixpoint::Expr::App(Box::new(func), args)
1104 }
1105 }
1106 rty::ExprKind::IfThenElse(p, e1, e2) => {
1107 fixpoint::Expr::IfThenElse(Box::new([
1108 self.expr_to_fixpoint(p, scx)?,
1109 self.expr_to_fixpoint(e1, scx)?,
1110 self.expr_to_fixpoint(e2, scx)?,
1111 ]))
1112 }
1113 rty::ExprKind::Alias(alias_reft, args) => {
1114 let sort = self.genv.sort_of_assoc_reft(alias_reft.assoc_id)?;
1115 let sort = sort.instantiate_identity();
1116 let func =
1117 fixpoint::Expr::Var(self.define_const_for_alias_reft(alias_reft, sort, scx));
1118 let args = args
1119 .iter()
1120 .map(|expr| self.expr_to_fixpoint(expr, scx))
1121 .try_collect()?;
1122 fixpoint::Expr::App(Box::new(func), args)
1123 }
1124 rty::ExprKind::Abs(lam) => {
1125 let var = self.define_const_for_lambda(lam, scx);
1126 fixpoint::Expr::Var(var)
1127 }
1128 rty::ExprKind::Let(init, body) => {
1129 debug_assert_eq!(body.vars().len(), 1);
1130 let init = self.expr_to_fixpoint(init, scx)?;
1131
1132 self.local_var_env.push_layer_with_fresh_names(1);
1133 let body = self.expr_to_fixpoint(body.skip_binder_ref(), scx)?;
1134 let vars = self.local_var_env.pop_layer();
1135
1136 fixpoint::Expr::Let(vars[0].into(), Box::new([init, body]))
1137 }
1138 rty::ExprKind::GlobalFunc(SpecFuncKind::Thy(itf)) => {
1139 fixpoint::Expr::Var(fixpoint::Var::Itf(*itf))
1140 }
1141 rty::ExprKind::GlobalFunc(SpecFuncKind::Uif(def_id)) => {
1142 fixpoint::Expr::Var(self.define_const_for_uif(*def_id, scx))
1143 }
1144 rty::ExprKind::GlobalFunc(SpecFuncKind::Def(def_id)) => {
1145 fixpoint::Expr::Var(self.declare_fun(*def_id))
1146 }
1147 rty::ExprKind::Hole(..)
1148 | rty::ExprKind::KVar(_)
1149 | rty::ExprKind::Local(_)
1150 | rty::ExprKind::PathProj(..)
1151 | rty::ExprKind::ForAll(_)
1152 | rty::ExprKind::InternalFunc(_) => {
1153 span_bug!(self.def_span, "unexpected expr: `{expr:?}`")
1154 }
1155 rty::ExprKind::BoundedQuant(kind, rng, body) => {
1156 let exprs = (rng.start..rng.end).map(|i| {
1157 let arg = rty::Expr::constant(rty::Constant::from(i));
1158 body.replace_bound_reft(&arg)
1159 });
1160 let expr = match kind {
1161 flux_middle::fhir::QuantKind::Forall => rty::Expr::and_from_iter(exprs),
1162 flux_middle::fhir::QuantKind::Exists => rty::Expr::or_from_iter(exprs),
1163 };
1164 self.expr_to_fixpoint(&expr, scx)?
1165 }
1166 };
1167 Ok(e)
1168 }
1169
1170 fn exprs_to_fixpoint<'b>(
1171 &mut self,
1172 exprs: impl IntoIterator<Item = &'b rty::Expr>,
1173 scx: &mut SortEncodingCtxt,
1174 ) -> QueryResult<Vec<fixpoint::Expr>> {
1175 exprs
1176 .into_iter()
1177 .map(|e| self.expr_to_fixpoint(e, scx))
1178 .try_collect()
1179 }
1180
1181 fn proj_to_fixpoint(
1182 &mut self,
1183 e: &rty::Expr,
1184 proj: rty::FieldProj,
1185 scx: &mut SortEncodingCtxt,
1186 ) -> QueryResult<fixpoint::Expr> {
1187 let arity = proj.arity(self.genv)?;
1188 if arity == 1 {
1190 self.expr_to_fixpoint(e, scx)
1191 } else {
1192 let field = proj.field_idx();
1193 scx.declare_tuple(arity);
1194 let proj = fixpoint::Var::TupleProj { arity, field };
1195 let proj = fixpoint::Expr::Var(proj);
1196 Ok(fixpoint::Expr::App(Box::new(proj), vec![self.expr_to_fixpoint(e, scx)?]))
1197 }
1198 }
1199
1200 fn un_op_to_fixpoint(
1201 &mut self,
1202 op: rty::UnOp,
1203 e: &rty::Expr,
1204 scx: &mut SortEncodingCtxt,
1205 ) -> QueryResult<fixpoint::Expr> {
1206 match op {
1207 rty::UnOp::Not => Ok(fixpoint::Expr::Not(Box::new(self.expr_to_fixpoint(e, scx)?))),
1208 rty::UnOp::Neg => Ok(fixpoint::Expr::Neg(Box::new(self.expr_to_fixpoint(e, scx)?))),
1209 }
1210 }
1211
1212 fn bv_rel_to_fixpoint(&self, rel: &fixpoint::BinRel) -> fixpoint::Expr {
1213 let itf = match rel {
1214 fixpoint::BinRel::Gt => fixpoint::ThyFunc::BvUgt,
1215 fixpoint::BinRel::Ge => fixpoint::ThyFunc::BvUge,
1216 fixpoint::BinRel::Lt => fixpoint::ThyFunc::BvUlt,
1217 fixpoint::BinRel::Le => fixpoint::ThyFunc::BvUle,
1218 _ => span_bug!(self.def_span, "not a bitvector relation!"),
1219 };
1220 fixpoint::Expr::Var(fixpoint::Var::Itf(itf))
1221 }
1222
1223 fn bv_op_to_fixpoint(&self, op: &rty::BinOp) -> fixpoint::Expr {
1224 let itf = match op {
1225 rty::BinOp::Add(_) => fixpoint::ThyFunc::BvAdd,
1226 rty::BinOp::Sub(_) => fixpoint::ThyFunc::BvSub,
1227 rty::BinOp::Mul(_) => fixpoint::ThyFunc::BvMul,
1228 rty::BinOp::Div(_) => fixpoint::ThyFunc::BvUdiv,
1229 rty::BinOp::Mod(_) => fixpoint::ThyFunc::BvUrem,
1230 rty::BinOp::BitAnd => fixpoint::ThyFunc::BvAnd,
1231 rty::BinOp::BitOr => fixpoint::ThyFunc::BvOr,
1232 rty::BinOp::BitShl => fixpoint::ThyFunc::BvShl,
1233 rty::BinOp::BitShr => fixpoint::ThyFunc::BvLshr,
1234 _ => span_bug!(self.def_span, "not a bitvector operation!"),
1235 };
1236 fixpoint::Expr::Var(fixpoint::Var::Itf(itf))
1237 }
1238
1239 fn bin_op_to_fixpoint(
1240 &mut self,
1241 op: &rty::BinOp,
1242 e1: &rty::Expr,
1243 e2: &rty::Expr,
1244 scx: &mut SortEncodingCtxt,
1245 ) -> QueryResult<fixpoint::Expr> {
1246 let op = match op {
1247 rty::BinOp::Eq => {
1248 return Ok(fixpoint::Expr::Atom(
1249 fixpoint::BinRel::Eq,
1250 Box::new([self.expr_to_fixpoint(e1, scx)?, self.expr_to_fixpoint(e2, scx)?]),
1251 ));
1252 }
1253 rty::BinOp::Ne => {
1254 return Ok(fixpoint::Expr::Atom(
1255 fixpoint::BinRel::Ne,
1256 Box::new([self.expr_to_fixpoint(e1, scx)?, self.expr_to_fixpoint(e2, scx)?]),
1257 ));
1258 }
1259 rty::BinOp::Gt(sort) => {
1260 return self.bin_rel_to_fixpoint(sort, fixpoint::BinRel::Gt, e1, e2, scx);
1261 }
1262 rty::BinOp::Ge(sort) => {
1263 return self.bin_rel_to_fixpoint(sort, fixpoint::BinRel::Ge, e1, e2, scx);
1264 }
1265 rty::BinOp::Lt(sort) => {
1266 return self.bin_rel_to_fixpoint(sort, fixpoint::BinRel::Lt, e1, e2, scx);
1267 }
1268 rty::BinOp::Le(sort) => {
1269 return self.bin_rel_to_fixpoint(sort, fixpoint::BinRel::Le, e1, e2, scx);
1270 }
1271 rty::BinOp::And => {
1272 return Ok(fixpoint::Expr::And(vec![
1273 self.expr_to_fixpoint(e1, scx)?,
1274 self.expr_to_fixpoint(e2, scx)?,
1275 ]));
1276 }
1277 rty::BinOp::Or => {
1278 return Ok(fixpoint::Expr::Or(vec![
1279 self.expr_to_fixpoint(e1, scx)?,
1280 self.expr_to_fixpoint(e2, scx)?,
1281 ]));
1282 }
1283 rty::BinOp::Imp => {
1284 return Ok(fixpoint::Expr::Imp(Box::new([
1285 self.expr_to_fixpoint(e1, scx)?,
1286 self.expr_to_fixpoint(e2, scx)?,
1287 ])));
1288 }
1289 rty::BinOp::Iff => {
1290 return Ok(fixpoint::Expr::Iff(Box::new([
1291 self.expr_to_fixpoint(e1, scx)?,
1292 self.expr_to_fixpoint(e2, scx)?,
1293 ])));
1294 }
1295 rty::BinOp::Add(rty::Sort::BitVec(_))
1296 | rty::BinOp::Sub(rty::Sort::BitVec(_))
1297 | rty::BinOp::Mul(rty::Sort::BitVec(_))
1298 | rty::BinOp::Div(rty::Sort::BitVec(_))
1299 | rty::BinOp::Mod(rty::Sort::BitVec(_))
1300 | rty::BinOp::BitAnd
1301 | rty::BinOp::BitOr
1302 | rty::BinOp::BitShl
1303 | rty::BinOp::BitShr => {
1304 let bv_func = self.bv_op_to_fixpoint(op);
1305 return Ok(fixpoint::Expr::App(
1306 Box::new(bv_func),
1307 vec![self.expr_to_fixpoint(e1, scx)?, self.expr_to_fixpoint(e2, scx)?],
1308 ));
1309 }
1310 rty::BinOp::Add(_) => fixpoint::BinOp::Add,
1311 rty::BinOp::Sub(_) => fixpoint::BinOp::Sub,
1312 rty::BinOp::Mul(_) => fixpoint::BinOp::Mul,
1313 rty::BinOp::Div(_) => fixpoint::BinOp::Div,
1314 rty::BinOp::Mod(_) => fixpoint::BinOp::Mod,
1315 };
1316 Ok(fixpoint::Expr::BinaryOp(
1317 op,
1318 Box::new([self.expr_to_fixpoint(e1, scx)?, self.expr_to_fixpoint(e2, scx)?]),
1319 ))
1320 }
1321
1322 fn bin_rel_to_fixpoint(
1339 &mut self,
1340 sort: &rty::Sort,
1341 rel: fixpoint::BinRel,
1342 e1: &rty::Expr,
1343 e2: &rty::Expr,
1344 scx: &mut SortEncodingCtxt,
1345 ) -> QueryResult<fixpoint::Expr> {
1346 let e = match sort {
1347 rty::Sort::Int | rty::Sort::Real | rty::Sort::Char => {
1348 fixpoint::Expr::Atom(
1349 rel,
1350 Box::new([self.expr_to_fixpoint(e1, scx)?, self.expr_to_fixpoint(e2, scx)?]),
1351 )
1352 }
1353 rty::Sort::BitVec(_) => {
1354 let e1 = self.expr_to_fixpoint(e1, scx)?;
1355 let e2 = self.expr_to_fixpoint(e2, scx)?;
1356 let rel = self.bv_rel_to_fixpoint(&rel);
1357 fixpoint::Expr::App(Box::new(rel), vec![e1, e2])
1358 }
1359 rty::Sort::Tuple(sorts) => {
1360 let arity = sorts.len();
1361 self.apply_bin_rel_rec(sorts, rel, e1, e2, scx, |field| {
1362 rty::FieldProj::Tuple { arity, field }
1363 })?
1364 }
1365 rty::Sort::App(rty::SortCtor::Adt(sort_def), args)
1366 if let Some(variant) = sort_def.opt_struct_variant() =>
1367 {
1368 let def_id = sort_def.did();
1369 let sorts = variant.field_sorts(args);
1370 self.apply_bin_rel_rec(&sorts, rel, e1, e2, scx, |field| {
1371 rty::FieldProj::Adt { def_id, field }
1372 })?
1373 }
1374 _ => {
1375 let rel = fixpoint::Expr::Var(fixpoint::Var::UIFRel(rel));
1376 fixpoint::Expr::App(
1377 Box::new(rel),
1378 vec![self.expr_to_fixpoint(e1, scx)?, self.expr_to_fixpoint(e2, scx)?],
1379 )
1380 }
1381 };
1382 Ok(e)
1383 }
1384
1385 fn apply_bin_rel_rec(
1387 &mut self,
1388 sorts: &[rty::Sort],
1389 rel: fixpoint::BinRel,
1390 e1: &rty::Expr,
1391 e2: &rty::Expr,
1392 scx: &mut SortEncodingCtxt,
1393 mk_proj: impl Fn(u32) -> rty::FieldProj,
1394 ) -> QueryResult<fixpoint::Expr> {
1395 Ok(fixpoint::Expr::and(
1396 sorts
1397 .iter()
1398 .enumerate()
1399 .map(|(idx, s)| {
1400 let proj = mk_proj(idx as u32);
1401 let e1 = e1.proj_and_reduce(proj);
1402 let e2 = e2.proj_and_reduce(proj);
1403 self.bin_rel_to_fixpoint(s, rel, &e1, &e2, scx)
1404 })
1405 .try_collect()?,
1406 ))
1407 }
1408
1409 fn imm(
1410 &mut self,
1411 arg: &rty::Expr,
1412 sort: &rty::Sort,
1413 scx: &mut SortEncodingCtxt,
1414 bindings: &mut Vec<fixpoint::Bind>,
1415 ) -> QueryResult<fixpoint::Var> {
1416 let arg = self.expr_to_fixpoint(arg, scx)?;
1417 if let fixpoint::Expr::Var(var) = arg {
1420 Ok(var)
1421 } else {
1422 let fresh = self.local_var_env.fresh_name();
1423 let pred = fixpoint::Expr::eq(fixpoint::Expr::Var(fresh.into()), arg);
1424 bindings.push(fixpoint::Bind {
1425 name: fresh.into(),
1426 sort: scx.sort_to_fixpoint(sort),
1427 pred: fixpoint::Pred::Expr(pred),
1428 });
1429 Ok(fresh.into())
1430 }
1431 }
1432
1433 fn declare_fun(&mut self, def_id: FluxDefId) -> fixpoint::Var {
1437 *self.fun_def_map.entry(def_id).or_insert_with(|| {
1438 let id = self.global_var_gen.fresh();
1439 fixpoint::Var::Global(id, Some(def_id.name()))
1440 })
1441 }
1442
1443 fn prim_op_sort(op: &rty::BinOp, span: Span) -> rty::PolyFuncSort {
1452 match op {
1453 rty::BinOp::BitAnd | rty::BinOp::BitOr | rty::BinOp::BitShl | rty::BinOp::BitShr => {
1454 let fsort =
1455 rty::FuncSort::new(vec![rty::Sort::Int, rty::Sort::Int], rty::Sort::Int);
1456 rty::PolyFuncSort::new(List::empty(), fsort)
1457 }
1458 _ => span_bug!(span, "unexpected prim op: {op:?} in `prim_op_sort`"),
1459 }
1460 }
1461
1462 fn define_const_for_prim_op(
1463 &mut self,
1464 op: &rty::BinOp,
1465 scx: &mut SortEncodingCtxt,
1466 ) -> fixpoint::Var {
1467 let key = ConstKey::PrimOp(op.clone());
1468 let span = self.def_span;
1469 self.const_map
1470 .entry(key)
1471 .or_insert_with(|| {
1472 let sort = scx.func_sort_to_fixpoint(&Self::prim_op_sort(op, span));
1473 fixpoint::ConstDecl {
1474 name: fixpoint::Var::Global(self.global_var_gen.fresh(), None),
1475 sort,
1476 comment: Some(format!("prim op uif: {op:?}")),
1477 }
1478 })
1479 .name
1480 }
1481
1482 fn define_const_for_uif(
1483 &mut self,
1484 def_id: FluxDefId,
1485 scx: &mut SortEncodingCtxt,
1486 ) -> fixpoint::Var {
1487 let key = ConstKey::Uif(def_id);
1488 self.const_map
1489 .entry(key)
1490 .or_insert_with(|| {
1491 let sort = scx.func_sort_to_fixpoint(&self.genv.func_sort(def_id));
1492 fixpoint::ConstDecl {
1493 name: fixpoint::Var::Global(self.global_var_gen.fresh(), Some(def_id.name())),
1494 sort,
1495 comment: Some(format!("uif: {def_id:?}")),
1496 }
1497 })
1498 .name
1499 }
1500
1501 fn define_const_for_rust_const(
1502 &mut self,
1503 def_id: DefId,
1504 scx: &mut SortEncodingCtxt,
1505 ) -> fixpoint::Var {
1506 let key = ConstKey::RustConst(def_id);
1507 self.const_map
1508 .entry(key)
1509 .or_insert_with(|| {
1510 let sort = self.genv.sort_of_def_id(def_id).unwrap().unwrap();
1511 fixpoint::ConstDecl {
1512 name: fixpoint::Var::Global(self.global_var_gen.fresh(), None),
1513 sort: scx.sort_to_fixpoint(&sort),
1514 comment: Some(format!("rust const: {}", def_id_to_string(def_id))),
1515 }
1516 })
1517 .name
1518 }
1519
1520 fn define_const_for_alias_reft(
1523 &mut self,
1524 alias_reft: &rty::AliasReft,
1525 fsort: rty::FuncSort,
1526 scx: &mut SortEncodingCtxt,
1527 ) -> fixpoint::Var {
1528 let tcx = self.genv.tcx();
1529 let args = alias_reft
1530 .args
1531 .to_rustc(tcx)
1532 .truncate_to(tcx, tcx.generics_of(alias_reft.assoc_id.parent()));
1533 let key = ConstKey::Alias(alias_reft.assoc_id, args);
1534 self.const_map
1535 .entry(key)
1536 .or_insert_with(|| {
1537 let comment = Some(format!("alias reft: {alias_reft:?}"));
1538 let name = fixpoint::Var::Global(self.global_var_gen.fresh(), None);
1539 let fsort = rty::PolyFuncSort::new(List::empty(), fsort);
1540 let sort = scx.func_sort_to_fixpoint(&fsort);
1541 fixpoint::ConstDecl { name, comment, sort }
1542 })
1543 .name
1544 }
1545
1546 fn define_const_for_lambda(
1549 &mut self,
1550 lam: &rty::Lambda,
1551 scx: &mut SortEncodingCtxt,
1552 ) -> fixpoint::Var {
1553 let key = ConstKey::Lambda(lam.clone());
1554 self.const_map
1555 .entry(key)
1556 .or_insert_with(|| {
1557 let comment = Some(format!("lambda: {lam:?}"));
1558 let name = fixpoint::Var::Global(self.global_var_gen.fresh(), None);
1559 let sort = scx.func_sort_to_fixpoint(&lam.fsort().to_poly());
1560 fixpoint::ConstDecl { name, comment, sort }
1561 })
1562 .name
1563 }
1564
1565 fn assume_const_values(
1566 &mut self,
1567 mut constraint: fixpoint::Constraint,
1568 scx: &mut SortEncodingCtxt,
1569 ) -> QueryResult<fixpoint::Constraint> {
1570 let mut idx = 0;
1573 while let Some((key, const_)) = self.const_map.get_index(idx) {
1574 idx += 1;
1575
1576 let ConstKey::RustConst(def_id) = key else { continue };
1577 let info = self.genv.constant_info(def_id)?;
1578 match info {
1579 rty::ConstantInfo::Uninterpreted => {}
1580 rty::ConstantInfo::Interpreted(val, _) => {
1581 let e1 = fixpoint::Expr::Var(const_.name);
1582 let e2 = self.expr_to_fixpoint(&val, scx)?;
1583 let pred = fixpoint::Pred::Expr(e1.eq(e2));
1584 constraint = fixpoint::Constraint::ForAll(
1585 fixpoint::Bind {
1586 name: fixpoint::Var::Underscore,
1587 sort: fixpoint::Sort::Int,
1588 pred,
1589 },
1590 Box::new(constraint),
1591 );
1592 }
1593 }
1594 }
1595 Ok(constraint)
1596 }
1597
1598 fn qualifiers_for(
1599 &mut self,
1600 def_id: LocalDefId,
1601 scx: &mut SortEncodingCtxt,
1602 ) -> QueryResult<Vec<fixpoint::Qualifier>> {
1603 self.genv
1604 .qualifiers_for(def_id)?
1605 .map(|qual| self.qualifier_to_fixpoint(qual, scx))
1606 .try_collect()
1607 }
1608
1609 fn define_funs(
1610 &mut self,
1611 def_id: MaybeExternId,
1612 scx: &mut SortEncodingCtxt,
1613 ) -> QueryResult<(Vec<fixpoint::FunDef>, Vec<fixpoint::ConstDecl>)> {
1614 let reveals: UnordSet<FluxDefId> = self.genv.reveals_for(def_id.local_id())?.collect();
1615 let mut consts = vec![];
1616 let mut defs = vec![];
1617
1618 let mut idx = 0;
1621 while let Some((&did, &name)) = self.fun_def_map.get_index(idx) {
1622 idx += 1;
1623
1624 let comment = format!("flux def: {did:?}");
1625 let info = self.genv.normalized_info(did);
1626 let revealed = reveals.contains(&did);
1627 if info.hide && !revealed {
1628 let sort = scx.func_sort_to_fixpoint(&self.genv.func_sort(did));
1629 consts.push(fixpoint::ConstDecl { name, sort, comment: Some(comment) });
1630 } else {
1631 let out = scx.sort_to_fixpoint(self.genv.func_sort(did).expect_mono().output());
1632 let (args, body) = self.body_to_fixpoint(&info.body, scx)?;
1633 let fun_def = fixpoint::FunDef { name, args, body, out, comment: Some(comment) };
1634 defs.push((info.rank, fun_def));
1635 };
1636 }
1637
1638 let defs = defs
1640 .into_iter()
1641 .sorted_by_key(|(rank, _)| *rank)
1642 .map(|(_, def)| def)
1643 .collect();
1644
1645 Ok((defs, consts))
1646 }
1647
1648 fn body_to_fixpoint(
1649 &mut self,
1650 body: &rty::Binder<rty::Expr>,
1651 scx: &mut SortEncodingCtxt,
1652 ) -> QueryResult<(Vec<(fixpoint::Var, fixpoint::Sort)>, fixpoint::Expr)> {
1653 self.local_var_env
1654 .push_layer_with_fresh_names(body.vars().len());
1655
1656 let expr = self.expr_to_fixpoint(body.as_ref().skip_binder(), scx)?;
1657
1658 let args: Vec<(fixpoint::Var, fixpoint::Sort)> =
1659 iter::zip(self.local_var_env.pop_layer(), body.vars())
1660 .map(|(name, var)| (name.into(), scx.sort_to_fixpoint(var.expect_sort())))
1661 .collect();
1662
1663 Ok((args, expr))
1664 }
1665
1666 fn qualifier_to_fixpoint(
1667 &mut self,
1668 qualifier: &rty::Qualifier,
1669 scx: &mut SortEncodingCtxt,
1670 ) -> QueryResult<fixpoint::Qualifier> {
1671 let (args, body) = self.body_to_fixpoint(&qualifier.body, scx)?;
1672 let name = qualifier.def_id.name().to_string();
1673 Ok(fixpoint::Qualifier { name, args, body })
1674 }
1675}
1676
1677fn mk_implies(assumption: fixpoint::Pred, cstr: fixpoint::Constraint) -> fixpoint::Constraint {
1678 fixpoint::Constraint::ForAll(
1679 fixpoint::Bind {
1680 name: fixpoint::Var::Underscore,
1681 sort: fixpoint::Sort::Int,
1682 pred: assumption,
1683 },
1684 Box::new(cstr),
1685 )
1686}