flux_infer/fixpoint_encoding/
decoding.rs1use flux_middle::{
2 big_int::BigInt,
3 rty::{self, EarlyReftParam, InternalFuncKind, List, SpecFuncKind},
4};
5use flux_rustc_bridge::lowering::Lower;
6use itertools::Itertools;
7
8use super::{ConstKey, FixpointCtxt, fixpoint};
9
10impl<'genv, 'tcx, Tag> FixpointCtxt<'genv, 'tcx, Tag>
11where
12 Tag: std::hash::Hash + Eq + Copy,
13{
14 #[allow(dead_code)]
15 pub(crate) fn fixpoint_to_expr(
16 &self,
17 fexpr: &fixpoint::Expr,
18 ) -> Result<rty::Expr, FixpointParseError> {
19 match fexpr {
20 fixpoint::Expr::Constant(constant) => {
21 let c = match constant {
22 fixpoint::Constant::Numeral(num) => rty::Constant::Int(BigInt::from(*num)),
23 fixpoint::Constant::Real(dec) => rty::Constant::Real(rty::Real(*dec)),
24 fixpoint::Constant::Boolean(b) => rty::Constant::Bool(*b),
25 fixpoint::Constant::String(s) => rty::Constant::Str(s.0),
26 fixpoint::Constant::BitVec(bv, size) => rty::Constant::BitVec(*bv, *size),
27 };
28 Ok(rty::Expr::constant(c))
29 }
30 fixpoint::Expr::Var(fvar) => {
31 match fvar {
32 fixpoint::Var::Underscore => {
33 unreachable!("Underscore should not appear in exprs")
34 }
35 fixpoint::Var::Global(global_var, _) => {
36 if let Some(const_key) = self.ecx.const_env.const_map_rev.get(global_var) {
37 match const_key {
38 ConstKey::Uif(def_id) => {
39 Ok(rty::Expr::global_func(SpecFuncKind::Uif(*def_id)))
40 }
41 ConstKey::RustConst(def_id) => Ok(rty::Expr::const_def_id(*def_id)),
42 ConstKey::Alias(_flux_id, _args) => {
43 unreachable!("Should be special-cased as the head of an app")
44 }
45 ConstKey::Lambda(lambda) => Ok(rty::Expr::abs(lambda.clone())),
46 ConstKey::PrimOp(bin_op) => {
47 Ok(rty::Expr::internal_func(InternalFuncKind::Rel(
48 bin_op.clone(),
49 )))
50 }
51 ConstKey::Cast(_sort, _sort1) => {
52 unreachable!(
53 "Should be specially handled as the head of a function app."
54 )
55 }
56 }
57 } else {
58 Err(FixpointParseError::NoGlobalVar(*global_var))
59 }
60 }
61 fixpoint::Var::Local(fname) => {
62 if let Some(var) = self.ecx.local_var_env.reverse_map.get(fname) {
63 Ok(rty::Expr::var(*var))
64 } else {
65 Err(FixpointParseError::NoLocalVar(*fname))
66 }
67 }
68 fixpoint::Var::DataCtor(adt_id, variant_idx) => {
69 let def_id = self.scx.adt_sorts[adt_id.as_usize()];
70 Ok(rty::Expr::ctor_enum(def_id, *variant_idx))
71 }
72 fixpoint::Var::TupleCtor { .. }
73 | fixpoint::Var::TupleProj { .. }
74 | fixpoint::Var::DataProj { .. }
75 | fixpoint::Var::UIFRel(_) => {
76 unreachable!(
77 "Trying to convert an atomic var, but reached a var that should only occur as the head of an app (and be special-cased in conversion as a result)"
78 )
79 }
80 fixpoint::Var::Param(EarlyReftParam { index, name }) => {
81 Ok(rty::Expr::early_param(*index, *name))
82 }
83 fixpoint::Var::ConstGeneric(const_generic) => {
84 Ok(rty::Expr::const_generic(*const_generic))
85 }
86 }
87 }
88 fixpoint::Expr::App(fhead, fargs) => {
89 match &**fhead {
90 fixpoint::Expr::Var(fixpoint::Var::TupleProj { arity, field }) => {
91 if fargs.len() == 1 {
92 let earg = self.fixpoint_to_expr(&fargs[0])?;
93 Ok(rty::Expr::field_proj(
94 earg,
95 rty::FieldProj::Tuple { arity: *arity, field: *field },
96 ))
97 } else {
98 Err(FixpointParseError::ProjArityMismatch(fargs.len()))
99 }
100 }
101 fixpoint::Expr::Var(fixpoint::Var::DataProj { adt_id, field }) => {
102 if fargs.len() == 1 {
103 let earg = self.fixpoint_to_expr(&fargs[0])?;
104 Ok(rty::Expr::field_proj(
105 earg,
106 rty::FieldProj::Adt {
107 def_id: self.scx.adt_sorts[adt_id.as_usize()],
108 field: *field,
109 },
110 ))
111 } else {
112 Err(FixpointParseError::ProjArityMismatch(fargs.len()))
113 }
114 }
115 fixpoint::Expr::Var(fixpoint::Var::TupleCtor { arity }) => {
116 if fargs.len() == *arity {
117 let eargs = fargs
118 .iter()
119 .map(|farg| self.fixpoint_to_expr(farg))
120 .try_collect()?;
121 Ok(rty::Expr::tuple(eargs))
122 } else {
123 Err(FixpointParseError::TupleCtorArityMismatch(*arity, fargs.len()))
124 }
125 }
126 fixpoint::Expr::Var(fixpoint::Var::UIFRel(fbinrel)) => {
127 if fargs.len() == 2 {
128 let e1 = self.fixpoint_to_expr(&fargs[0])?;
129 let e2 = self.fixpoint_to_expr(&fargs[1])?;
130 let binrel = match fbinrel {
131 fixpoint::BinRel::Eq => rty::BinOp::Eq,
132 fixpoint::BinRel::Ne => rty::BinOp::Ne,
133 fixpoint::BinRel::Gt => rty::BinOp::Gt(rty::Sort::Str),
140 fixpoint::BinRel::Ge => rty::BinOp::Ge(rty::Sort::Str),
141 fixpoint::BinRel::Lt => rty::BinOp::Lt(rty::Sort::Str),
142 fixpoint::BinRel::Le => rty::BinOp::Le(rty::Sort::Str),
143 };
144 Ok(rty::Expr::binary_op(binrel, e1, e2))
145 } else {
146 Err(FixpointParseError::UIFRelArityMismatch(fargs.len()))
147 }
148 }
149 fixpoint::Expr::Var(fixpoint::Var::Global(global_var, _)) => {
150 if let Some(const_key) = self.ecx.const_env.const_map_rev.get(global_var) {
151 match const_key {
152 ConstKey::PrimOp(bin_op) => {
156 if fargs.len() != 2 {
157 Err(FixpointParseError::PrimOpArityMismatch(fargs.len()))
158 } else {
159 Ok(rty::Expr::prim_val(
160 bin_op.clone(),
161 self.fixpoint_to_expr(&fargs[0])?,
162 self.fixpoint_to_expr(&fargs[1])?,
163 ))
164 }
165 }
166 ConstKey::Cast(sort1, sort2) => {
167 if fargs.len() != 1 {
168 Err(FixpointParseError::CastArityMismatch(fargs.len()))
169 } else {
170 Ok(rty::Expr::cast(
171 sort1.clone(),
172 sort2.clone(),
173 self.fixpoint_to_expr(&fargs[0])?,
174 ))
175 }
176 }
177 ConstKey::Alias(assoc_id, generic_args) => {
178 let lowered_args: flux_rustc_bridge::ty::GenericArgs =
179 generic_args.lower(self.genv.tcx()).unwrap();
180 let generic_args = rty::refining::Refiner::default_for_item(
181 self.genv,
182 assoc_id.parent(),
183 )
184 .unwrap()
185 .refine_generic_args(assoc_id.parent(), &lowered_args)
186 .unwrap();
187 let alias_reft =
188 rty::AliasReft { assoc_id: *assoc_id, args: generic_args };
189 let args = fargs
190 .iter()
191 .map(|farg| self.fixpoint_to_expr(farg))
192 .try_collect()?;
193 Ok(rty::Expr::alias(alias_reft, args))
194 }
195 ConstKey::Uif(..)
196 | ConstKey::RustConst(..)
197 | ConstKey::Lambda(..) => {
198 self.fixpoint_app_to_expr(fhead, fargs)
200 }
201 }
202 } else {
203 Err(FixpointParseError::NoGlobalVar(*global_var))
204 }
205 }
206 fhead => self.fixpoint_app_to_expr(fhead, fargs),
207 }
208 }
209 fixpoint::Expr::Neg(fexpr) => {
210 let e = self.fixpoint_to_expr(fexpr)?;
211 Ok(rty::Expr::neg(&e))
212 }
213 fixpoint::Expr::BinaryOp(fbinop, boxed_args) => {
214 let binop = match fbinop {
215 fixpoint::BinOp::Add => rty::BinOp::Add(rty::Sort::Int),
219 fixpoint::BinOp::Sub => rty::BinOp::Sub(rty::Sort::Int),
220 fixpoint::BinOp::Mul => rty::BinOp::Mul(rty::Sort::Int),
221 fixpoint::BinOp::Div => rty::BinOp::Div(rty::Sort::Int),
222 fixpoint::BinOp::Mod => rty::BinOp::Mod(rty::Sort::Int),
223 };
224 let [fe1, fe2] = &**boxed_args;
225 let e1 = self.fixpoint_to_expr(fe1)?;
226 let e2 = self.fixpoint_to_expr(fe2)?;
227 Ok(rty::Expr::binary_op(binop, e1, e2))
228 }
229 fixpoint::Expr::IfThenElse(boxed_args) => {
230 let [fe1, fe2, fe3] = &**boxed_args;
231 let e1 = self.fixpoint_to_expr(fe1)?;
232 let e2 = self.fixpoint_to_expr(fe2)?;
233 let e3 = self.fixpoint_to_expr(fe3)?;
234 Ok(rty::Expr::ite(e1, e2, e3))
235 }
236 fixpoint::Expr::And(fexprs) => {
237 let exprs: Vec<rty::Expr> = fexprs
238 .iter()
239 .map(|fexpr| self.fixpoint_to_expr(fexpr))
240 .try_collect()?;
241 Ok(rty::Expr::and_from_iter(exprs))
242 }
243 fixpoint::Expr::Or(fexprs) => {
244 let exprs: Vec<rty::Expr> = fexprs
245 .iter()
246 .map(|fexpr| self.fixpoint_to_expr(fexpr))
247 .try_collect()?;
248 Ok(rty::Expr::or_from_iter(exprs))
249 }
250 fixpoint::Expr::Not(fexpr) => {
251 let e = self.fixpoint_to_expr(fexpr)?;
252 Ok(rty::Expr::not(&e))
253 }
254 fixpoint::Expr::Imp(boxed_args) => {
255 let [fe1, fe2] = &**boxed_args;
256 let e1 = self.fixpoint_to_expr(fe1)?;
257 let e2 = self.fixpoint_to_expr(fe2)?;
258 Ok(rty::Expr::binary_op(rty::BinOp::Imp, e1, e2))
259 }
260 fixpoint::Expr::Iff(boxed_args) => {
261 let [fe1, fe2] = &**boxed_args;
262 let e1 = self.fixpoint_to_expr(fe1)?;
263 let e2 = self.fixpoint_to_expr(fe2)?;
264 Ok(rty::Expr::binary_op(rty::BinOp::Iff, e1, e2))
265 }
266 fixpoint::Expr::Atom(fbinrel, boxed_args) => {
267 let binrel = match fbinrel {
268 fixpoint::BinRel::Eq => rty::BinOp::Eq,
269 fixpoint::BinRel::Ne => rty::BinOp::Ne,
270 fixpoint::BinRel::Gt => rty::BinOp::Gt(rty::Sort::Int),
278 fixpoint::BinRel::Ge => rty::BinOp::Ge(rty::Sort::Int),
279 fixpoint::BinRel::Lt => rty::BinOp::Lt(rty::Sort::Int),
280 fixpoint::BinRel::Le => rty::BinOp::Le(rty::Sort::Int),
281 };
282 let [fe1, fe2] = &**boxed_args;
283 let e1 = self.fixpoint_to_expr(fe1)?;
284 let e2 = self.fixpoint_to_expr(fe2)?;
285 Ok(rty::Expr::binary_op(binrel, e1, e2))
286 }
287 fixpoint::Expr::Let(_var, _boxed_args) => {
288 todo!("Convert `var` in e2 to locally nameless var, then fill in sort");
295 }
297 fixpoint::Expr::ThyFunc(itf) => Ok(rty::Expr::global_func(SpecFuncKind::Thy(*itf))),
298 fixpoint::Expr::IsCtor(var, fe) => {
299 let (def_id, variant_idx) = match var {
300 fixpoint::Var::DataCtor(adt_id, variant_idx) => {
301 let def_id = self.scx.adt_sorts[adt_id.as_usize()];
302 Ok((def_id, *variant_idx))
303 }
304 _ => Err(FixpointParseError::WrongVarInIsCtor(*var)),
305 }?;
306 let e = self.fixpoint_to_expr(fe)?;
307 Ok(rty::Expr::is_ctor(def_id, variant_idx, e))
308 }
309 }
310 }
311
312 fn fixpoint_app_to_expr(
313 &self,
314 fhead: &fixpoint::Expr,
315 fargs: &[fixpoint::Expr],
316 ) -> Result<rty::Expr, FixpointParseError> {
317 let head = self.fixpoint_to_expr(fhead)?;
318 let args = fargs
319 .iter()
320 .map(|farg| self.fixpoint_to_expr(farg))
321 .try_collect()?;
322 Ok(rty::Expr::app(head, List::empty(), args))
323 }
324}
325
326#[derive(Debug)]
327pub enum FixpointParseError {
328 UIFRelArityMismatch(usize),
331 TupleCtorArityMismatch(usize, usize),
333 ProjArityMismatch(usize),
335 NoGlobalVar(fixpoint::GlobalVar),
336 CastArityMismatch(usize),
338 PrimOpArityMismatch(usize),
339 NoLocalVar(fixpoint::LocalVar),
340 WrongVarInIsCtor(fixpoint::Var),
342}