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