1use core::fmt;
2use std::{fmt::Write, iter};
3
4use flux_common::{
5 bug,
6 dbg::{self, as_subscript},
7};
8use flux_middle::{
9 def_id::FluxDefId,
10 global_env::GlobalEnv,
11 rty::{PrettyMap, PrettyVar},
12};
13use itertools::Itertools;
14use liquid_fixpoint::{FixpointFmt, Identifier, ThyFunc};
15use rustc_data_structures::fx::FxIndexSet;
16use rustc_hir::def_id::DefId;
17
18use crate::fixpoint_encoding::{
19 ClosedSolution, InterpretedConst, KVarSolutions,
20 fixpoint::{
21 self, AdtId, BinOp, BinRel, Constant, Constraint, DataDecl, DataField, DataSort, Expr,
22 FunDef, FunSort, KVarDecl, KVid, LocalVar, Pred, Sort, SortCtor, SortDecl, Var,
23 },
24};
25
26pub struct LeanCtxt<'a, 'genv, 'tcx> {
27 pub genv: GlobalEnv<'genv, 'tcx>,
28 pub pretty_var_map: &'a PrettyMap<LocalVar>,
29 pub adt_map: &'a FxIndexSet<DefId>,
30 pub opaque_adt_map: &'a [(FluxDefId, SortDecl)],
31 pub kvar_solutions: &'a KVarSolutions,
32}
33
34pub struct WithLeanCtxt<'a, 'b, 'genv, 'tcx, T> {
35 pub item: T,
36 pub cx: &'a LeanCtxt<'b, 'genv, 'tcx>,
37}
38
39impl<'a, 'b, 'genv, 'tcx, T: LeanFmt> fmt::Display for WithLeanCtxt<'a, 'b, 'genv, 'tcx, T> {
40 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
41 self.item.lean_fmt(f, self.cx)
42 }
43}
44
45pub trait LeanFmt {
46 fn lean_fmt(&self, f: &mut fmt::Formatter, cx: &LeanCtxt) -> fmt::Result;
47}
48
49pub struct LeanKConstraint<'a> {
50 pub theorem_name: &'a str,
51 pub kvars: &'a [KVarDecl],
52 pub constr: &'a Constraint,
53}
54
55struct LeanThyFunc<'a>(&'a ThyFunc);
56
57impl LeanFmt for SortDecl {
58 fn lean_fmt(&self, f: &mut fmt::Formatter, cx: &LeanCtxt) -> fmt::Result {
59 self.name.lean_fmt(f, cx)?;
60 write!(
61 f,
62 " {} : Type",
63 (0..(self.vars))
64 .map(|i| format!("(t{i} : Type) [Inhabited t{i}]"))
65 .format(" ")
66 )
67 }
68}
69
70impl LeanFmt for InterpretedConst {
71 fn lean_fmt(&self, f: &mut fmt::Formatter, cx: &LeanCtxt) -> fmt::Result {
72 write!(
73 f,
74 "def {} : {} := {}",
75 WithLeanCtxt { item: &self.0.name, cx },
76 WithLeanCtxt { item: &self.0.sort, cx },
77 WithLeanCtxt { item: &self.1, cx }
78 )
79 }
80}
81
82impl LeanFmt for DataField {
84 fn lean_fmt(&self, f: &mut fmt::Formatter, cx: &LeanCtxt) -> fmt::Result {
85 write!(
86 f,
87 "({} : {})",
88 self.name.display().to_string().replace("$", "_"),
89 WithLeanCtxt { item: &self.sort, cx }
90 )
91 }
92}
93
94impl LeanFmt for DataSort {
95 fn lean_fmt(&self, f: &mut fmt::Formatter, cx: &LeanCtxt) -> std::fmt::Result {
96 match self {
97 DataSort::User(opaque_id) => {
98 let (def_id, _) = cx.opaque_adt_map.get(opaque_id.as_usize()).unwrap();
99 write!(f, "{}", snake_case_to_pascal_case(def_id.name().as_str()))
100 }
101 DataSort::Tuple(n) => write!(f, "Tupleₓ{}", dbg::as_subscript(n)),
102 DataSort::Adt(adt_id) => {
103 let def_id = cx.adt_map.get_index(adt_id.as_usize()).unwrap();
104 write!(f, "{}", def_id_to_pascal_case(def_id, &cx.genv.tcx()))
105 }
106 }
107 }
108}
109
110impl LeanFmt for DataDecl {
111 fn lean_fmt(&self, f: &mut fmt::Formatter, cx: &LeanCtxt) -> fmt::Result {
112 if self.ctors.len() == 1 {
113 writeln!(f, "@[ext]")?;
114 write!(f, "structure ")?;
115 self.name.lean_fmt(f, cx)?;
116 writeln!(
117 f,
118 " {} where",
119 (0..self.vars)
120 .map(|i| format!("(t{i} : Type) [Inhabited t{i}]"))
121 .format(" ")
122 )?;
123 let ctor = &self.ctors[0];
124 if let fixpoint::Var::DataCtor(adt_id, _) = &ctor.name {
125 writeln!(
126 f,
127 " mk{}{} ::",
128 WithLeanCtxt { item: &DataSort::Adt(*adt_id), cx },
129 as_subscript(0)
130 )?;
131 for (idx, field) in ctor.fields.iter().enumerate() {
132 writeln!(
133 f,
134 " {} : {} ",
135 WithLeanCtxt { item: LeanField(*adt_id, idx.try_into().unwrap()), cx },
136 WithLeanCtxt { item: &field.sort, cx }
137 )?;
138 }
139 } else {
140 bug!("unexpected ctor {ctor:?} in datadecl");
141 };
142 } else {
143 write!(f, "inductive ")?;
144 self.name.lean_fmt(f, cx)?;
145 writeln!(
146 f,
147 " {} where",
148 (0..self.vars)
149 .map(|i| format!("(t{i} : Type) [Inhabited t{i}]"))
150 .format(" ")
151 )?;
152 for data_ctor in &self.ctors {
153 let fixpoint::Var::DataCtor(adt_id, variant_id) = &data_ctor.name else {
154 bug!("unexpected ctor {data_ctor:?} in datadecl")
155 };
156 write!(f, "| ")?;
157 write!(
158 f,
159 " mk{}{} ",
160 WithLeanCtxt { item: &DataSort::Adt(*adt_id), cx },
161 as_subscript(variant_id.as_usize()),
162 )?;
163 for field in &data_ctor.fields {
165 write!(f, " ")?;
166 field.lean_fmt(f, cx)?;
167 }
168 writeln!(f)?;
169 }
170 }
171 Ok(())
172 }
173}
174
175impl<'a> fmt::Display for LeanThyFunc<'a> {
176 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
177 match self.0 {
178 ThyFunc::IntToBv8 => write!(f, "BitVec.ofInt 8"),
179 ThyFunc::IntToBv32 => write!(f, "BitVec.ofInt 32"),
180 ThyFunc::IntToBv64 => write!(f, "BitVec.ofInt 64"),
181 ThyFunc::Bv8ToInt | ThyFunc::Bv32ToInt | ThyFunc::Bv64ToInt => {
182 write!(f, "BitVec.toNat")
183 }
184 ThyFunc::BvAdd => write!(f, "BitVec.add"),
185 ThyFunc::BvSub => write!(f, "BitVec.sub"),
186 ThyFunc::BvMul => write!(f, "BitVec.mul"),
187 ThyFunc::BvNeg => write!(f, "BitVec.neg"),
188 ThyFunc::BvSdiv => write!(f, "BitVec.sdiv"),
189 ThyFunc::BvSrem => write!(f, "BitVec.srem"),
190 ThyFunc::BvUdiv => write!(f, "BitVec.udiv"),
191 ThyFunc::BvAnd => write!(f, "BitVec.and"),
192 ThyFunc::BvOr => write!(f, "BitVec.or"),
193 ThyFunc::BvXor => write!(f, "BitVec.xor"),
194 ThyFunc::BvNot => write!(f, "BitVec.not"),
195 ThyFunc::BvSle => write!(f, "BitVec.sle"),
196 ThyFunc::BvSlt => write!(f, "BitVec.slt"),
197 ThyFunc::BvUle => write!(f, "BitVec.ule"),
198 ThyFunc::BvUlt => write!(f, "BitVec.ult"),
199 ThyFunc::BvAshr => write!(f, "BitVec_sshiftRight"),
200 ThyFunc::BvLshr => write!(f, "BitVec_ushiftRight"),
201 ThyFunc::BvShl => write!(f, "BitVec_shiftLeft"),
202 ThyFunc::BvSignExtend(size) => write!(f, "BitVec.signExtend {}", size),
203 ThyFunc::BvZeroExtend(size) => write!(f, "BitVec.zeroExtend {}", size),
204 ThyFunc::BvUrem => write!(f, "BitVec.umod"),
205 ThyFunc::BvSge => write!(f, "BitVec_sge"),
206 ThyFunc::BvSgt => write!(f, "BitVec_sgt"),
207 ThyFunc::BvUge => write!(f, "BitVec_uge"),
208 ThyFunc::BvUgt => write!(f, "BitVec_ugt"),
209 ThyFunc::MapDefault => write!(f, "SmtMap_default"),
210 ThyFunc::MapSelect => write!(f, "SmtMap_select"),
211 ThyFunc::MapStore => write!(f, "SmtMap_store"),
212 func => panic!("Unsupported theory function {}", func),
213 }
214 }
215}
216
217impl<T: LeanFmt> LeanFmt for &T {
218 fn lean_fmt(&self, f: &mut fmt::Formatter, cx: &LeanCtxt) -> fmt::Result {
219 (*self).lean_fmt(f, cx)
220 }
221}
222
223struct LeanAdt(AdtId);
224struct LeanDataProj(AdtId, u32);
225struct LeanField(AdtId, u32);
226
227impl LeanFmt for LeanField {
228 fn lean_fmt(&self, f: &mut fmt::Formatter, cx: &LeanCtxt) -> fmt::Result {
229 let adt_id = self.0;
230 if let Some(def_id) = cx.adt_map.get_index(adt_id.as_usize())
231 && let Ok(adt_sort_def) = cx.genv.adt_sort_def_of(def_id)
232 {
233 write!(f, "{}", adt_sort_def.struct_variant().field_names()[self.1 as usize])
234 } else {
235 write!(f, "fld{}", as_subscript(self.1 as usize))
236 }
237 }
238}
239
240impl LeanFmt for LeanAdt {
241 fn lean_fmt(&self, f: &mut fmt::Formatter, cx: &LeanCtxt) -> fmt::Result {
242 let def_id = cx.adt_map.get_index(self.0.as_usize()).unwrap();
243 write!(f, "{}", def_id_to_pascal_case(def_id, &cx.genv.tcx()))
244 }
245}
246
247impl LeanFmt for LeanDataProj {
248 fn lean_fmt(&self, f: &mut fmt::Formatter, cx: &LeanCtxt) -> fmt::Result {
249 write!(
250 f,
251 "{}.{}",
252 WithLeanCtxt { item: LeanAdt(self.0), cx },
253 WithLeanCtxt { item: LeanField(self.0, self.1), cx }
254 )
255 }
256}
257
258impl LeanFmt for Var {
259 fn lean_fmt(&self, f: &mut fmt::Formatter, cx: &LeanCtxt) -> fmt::Result {
260 match self {
261 Var::Global(_gvar, def_id) => {
262 let path = cx
263 .genv
264 .tcx()
265 .def_path(def_id.parent())
266 .to_filename_friendly_no_crate()
267 .replace("-", "_");
268 if path.is_empty() {
269 write!(f, "{}", def_id.name())
270 } else {
271 write!(f, "{path}_{}", def_id.name())
272 }
273 }
274 Var::Const(_, Some(did)) => {
275 let path = cx
276 .genv
277 .tcx()
278 .def_path(*did)
279 .to_filename_friendly_no_crate()
280 .replace("-", "_");
281 write!(f, "{path}")
282 }
283 Var::DataCtor(adt_id, idx) => {
284 write!(
285 f,
286 "{}.mk{}{}",
287 WithLeanCtxt { item: LeanAdt(*adt_id), cx },
288 WithLeanCtxt { item: &DataSort::Adt(*adt_id), cx },
289 as_subscript(idx.as_usize())
290 )
291 }
292 Var::DataProj { adt_id, field } => LeanDataProj(*adt_id, *field).lean_fmt(f, cx),
293 Var::Local(local_var) => {
294 write!(f, "{}", cx.pretty_var_map.get(&PrettyVar::Local(*local_var)))
295 }
296 Var::Param(param) => {
297 write!(f, "{}", cx.pretty_var_map.get(&PrettyVar::Param(*param)))
298 }
299 _ => {
300 write!(f, "{}", self.display().to_string().replace("$", "_"))
301 }
302 }
303 }
304}
305
306impl LeanFmt for Sort {
307 fn lean_fmt(&self, f: &mut std::fmt::Formatter, cx: &LeanCtxt) -> std::fmt::Result {
308 match self {
309 Sort::Int => write!(f, "Int"),
310 Sort::Bool => write!(f, "Prop"),
311 Sort::Real => write!(f, "Real"),
312 Sort::Str => write!(f, "String"),
313 Sort::Func(f_sort) => {
314 write!(
315 f,
316 "({} -> {})",
317 WithLeanCtxt { item: &f_sort[0], cx },
318 WithLeanCtxt { item: &f_sort[1], cx }
319 )
320 }
321 Sort::App(sort_ctor, args) => {
322 match sort_ctor {
323 SortCtor::Data(sort) => {
324 if args.is_empty() {
325 sort.lean_fmt(f, cx)
326 } else {
327 write!(
328 f,
329 "({} {})",
330 WithLeanCtxt { item: sort, cx },
331 args.iter()
332 .map(|arg| { WithLeanCtxt { item: arg, cx } })
333 .format(" ")
334 )
335 }
336 }
337 SortCtor::Map => {
338 write!(
339 f,
340 "(SmtMap {} {})",
341 WithLeanCtxt { item: &args[0], cx },
342 WithLeanCtxt { item: &args[1], cx }
343 )
344 }
345 _ => todo!(),
346 }
347 }
348 Sort::BitVec(bv_size) => {
349 match bv_size.as_ref() {
350 Sort::BvSize(size) => write!(f, "BitVec {}", size),
351 s => {
352 panic!(
353 "encountered sort {} where bitvec size was expected",
354 WithLeanCtxt { item: s, cx }
355 )
356 }
357 }
358 }
359 Sort::Abs(v, sort) => {
360 write!(
361 f,
362 "{{t{v} : Type}} -> [Inhabited t{v}] -> {}",
363 WithLeanCtxt { item: sort.as_ref(), cx }
364 )
365 }
366 Sort::Var(v) => write!(f, "t{v}"),
367 s => todo!("{:?}", s),
368 }
369 }
370}
371
372impl LeanFmt for Expr {
373 fn lean_fmt(&self, f: &mut fmt::Formatter, cx: &LeanCtxt) -> fmt::Result {
374 match self {
375 Expr::Var(v) => v.lean_fmt(f, cx),
376 Expr::Constant(c) => {
377 match c {
378 Constant::Numeral(n) => write!(f, "{n}",),
379 Constant::Boolean(b) => write!(f, "{}", if *b { "True" } else { "False" }),
380 Constant::String(s) => write!(f, "{}", s.display()),
381 Constant::Real(n) => write!(f, "{n}.0"),
382 Constant::BitVec(bv, size) => write!(f, "{}#{}", bv, size),
383 }
384 }
385 Expr::BinaryOp(bin_op, args) => {
386 let bin_op_str = match bin_op {
387 BinOp::Add => "+",
388 BinOp::Sub => "-",
389 BinOp::Mul => "*",
390 BinOp::Div => "/",
391 BinOp::Mod => "%",
392 };
393 write!(f, "(")?;
394 args[0].lean_fmt(f, cx)?;
395 write!(f, " {} ", bin_op_str)?;
396 args[1].lean_fmt(f, cx)?;
397 write!(f, ")")
398 }
399 Expr::Atom(bin_rel, args) => {
400 let bin_rel_str = match bin_rel {
401 BinRel::Eq => "=",
402 BinRel::Ne => "≠",
403 BinRel::Le => "≤",
404 BinRel::Lt => "<",
405 BinRel::Ge => "≥",
406 BinRel::Gt => ">",
407 };
408 write!(f, "(")?;
409 args[0].lean_fmt(f, cx)?;
410 write!(f, " {} ", bin_rel_str)?;
411 args[1].lean_fmt(f, cx)?;
412 write!(f, ")")
413 }
414 Expr::App(function, sort_args, args, out_sort) => {
415 if out_sort.is_some() {
416 write!(f, "(")?;
417 }
418 write!(f, "(")?;
419 function.as_ref().lean_fmt(f, cx)?;
420 if let Some(sort_args) = sort_args {
421 for (i, s_arg) in sort_args.iter().enumerate() {
422 write!(f, " (t{i} := {})", WithLeanCtxt { item: s_arg, cx })?;
423 }
424 }
425 for arg in args {
426 write!(f, " ")?;
427 arg.lean_fmt(f, cx)?;
428 }
429 write!(f, ")")?;
430 if let Some(out_sort) = out_sort {
431 write!(f, " : (")?;
432 out_sort.lean_fmt(f, &cx)?;
433 write!(f, "))")?;
434 }
435 Ok(())
436 }
437 Expr::And(exprs) => {
438 write!(f, "(")?;
439 for (i, expr) in exprs.iter().enumerate() {
440 if i > 0 {
441 write!(f, " ∧ ")?;
442 }
443 expr.lean_fmt(f, cx)?;
444 }
445 write!(f, ")")
446 }
447 Expr::Or(exprs) => {
448 write!(f, "(")?;
449 for (i, expr) in exprs.iter().enumerate() {
450 if i > 0 {
451 write!(f, " ∨ ")?;
452 }
453 expr.lean_fmt(f, cx)?;
454 }
455 write!(f, ")")
456 }
457 Expr::Neg(inner) => {
458 write!(f, "(-")?;
459 inner.as_ref().lean_fmt(f, cx)?;
460 write!(f, ")")
461 }
462 Expr::IfThenElse(ite) => {
463 let [condition, if_true, if_false] = ite.as_ref();
464 write!(f, "(if ")?;
465 condition.lean_fmt(f, cx)?;
466 write!(f, " then ")?;
467 if_true.lean_fmt(f, cx)?;
468 write!(f, " else ")?;
469 if_false.lean_fmt(f, cx)?;
470 write!(f, ")")
471 }
472 Expr::Not(inner) => {
473 write!(f, "(")?;
474 write!(f, "¬")?;
475 inner.as_ref().lean_fmt(f, cx)?;
476 write!(f, ")")
477 }
478 Expr::Imp(implication) => {
479 let [lhs, rhs] = implication.as_ref();
480 write!(f, "(")?;
481 lhs.lean_fmt(f, cx)?;
482 write!(f, " -> ")?;
483 rhs.lean_fmt(f, cx)?;
484 write!(f, ")")
485 }
486 Expr::Iff(equiv) => {
487 let [lhs, rhs] = equiv.as_ref();
488 write!(f, "(")?;
489 lhs.lean_fmt(f, cx)?;
490 write!(f, " <-> ")?;
491 rhs.lean_fmt(f, cx)?;
492 write!(f, ")")
493 }
494 Expr::Let(binder, exprs) => {
495 let [def, body] = exprs.as_ref();
496 write!(f, "(let ")?;
497 binder.lean_fmt(f, cx)?;
498 write!(f, " := ")?;
499 def.lean_fmt(f, cx)?;
500 write!(f, "; ")?;
501 body.lean_fmt(f, cx)?;
502 write!(f, ")")
503 }
504 Expr::ThyFunc(thy_func) => {
505 write!(f, "{}", LeanThyFunc(thy_func))
506 }
507 Expr::IsCtor(..) => {
508 todo!("not yet implemented: datatypes in lean")
509 }
510 Expr::Exists(bind, expr) => {
511 write!(f, "(∃ ")?;
512 for (var, sort) in bind {
513 write!(f, "(")?;
514 var.lean_fmt(f, cx)?;
515 write!(f, " : {})", WithLeanCtxt { item: sort, cx })?;
516 }
517 write!(f, ", ")?;
518 expr.lean_fmt(f, cx)?;
519 write!(f, ")")?;
520 Ok(())
521 }
522 }
523 }
524}
525
526impl LeanFmt for FunDef {
527 fn lean_fmt(&self, f: &mut fmt::Formatter, cx: &LeanCtxt) -> fmt::Result {
528 let FunDef { name, sort, comment: _, body } = self;
529 write!(f, "noncomputable def ")?;
530 name.lean_fmt(f, cx)?;
531 if let Some(body) = body {
532 for (arg, arg_sort) in iter::zip(&body.args, &sort.inputs) {
533 write!(f, " (")?;
534 arg.lean_fmt(f, cx)?;
535 write!(f, " : {})", WithLeanCtxt { item: arg_sort, cx })?;
536 }
537 writeln!(f, " : {} :=", WithLeanCtxt { item: &sort.output, cx })?;
538 write!(f, " ")?;
539 body.expr.lean_fmt(f, cx)?;
540 } else {
541 write!(f, " : {} := sorry", WithLeanCtxt { item: sort, cx })?;
542 }
543 writeln!(f)
544 }
545}
546
547impl LeanFmt for FunSort {
548 fn lean_fmt(&self, f: &mut fmt::Formatter, cx: &LeanCtxt) -> fmt::Result {
549 for i in 0..self.params {
550 write!(f, "{{t{i} : Type}} -> [Inhabited t{i}] -> ")?;
551 }
552 if self.inputs.is_empty() {
553 write!(f, "{}", WithLeanCtxt { item: &self.output, cx })
554 } else {
555 write!(
556 f,
557 "{} -> {}",
558 self.inputs.iter().format_with(" -> ", |sort, f| {
559 f(&format_args!("{}", WithLeanCtxt { item: sort, cx }))
560 }),
561 WithLeanCtxt { item: &self.output, cx }
562 )
563 }
564 }
565}
566
567impl LeanFmt for Pred {
568 fn lean_fmt(&self, f: &mut fmt::Formatter, cx: &LeanCtxt) -> fmt::Result {
569 match self {
570 Pred::Expr(expr) => expr.lean_fmt(f, cx),
571 Pred::And(preds) => {
572 write!(f, "(")?;
573 for (i, pred) in preds.iter().enumerate() {
574 if i > 0 {
575 write!(f, " ∧ ")?;
576 }
577 pred.lean_fmt(f, cx)?;
578 }
579 write!(f, ")")
580 }
581 Pred::KVar(kvid, args) => {
582 write!(f, "({}", kvid.display().to_string().replace("$", "_"))?;
583 for imp in cx
584 .kvar_solutions
585 .non_cut_solutions
586 .get(kvid)
587 .map(|sol| sol.0.clone())
588 .unwrap_or(vec![])
589 {
590 write!(f, " ")?;
591 imp.0.lean_fmt(f, cx)?;
592 }
593 for arg in args {
594 write!(f, " ")?;
595 arg.lean_fmt(f, cx)?;
596 }
597 write!(f, ")")
598 }
599 }
600 }
601}
602
603impl LeanFmt for KVarDecl {
604 fn lean_fmt(&self, f: &mut std::fmt::Formatter, cx: &LeanCtxt) -> std::fmt::Result {
605 let implicits: Vec<_> = cx
606 .kvar_solutions
607 .non_cut_solutions
608 .get(&self.kvid)
609 .map(|solution| solution.0.clone())
610 .unwrap_or(vec![]);
611 let sorts = implicits
612 .iter()
613 .map(|(_, sort)| sort)
614 .chain(&self.sorts)
615 .enumerate()
616 .map(|(i, sort)| format!("(a{i} : {})", WithLeanCtxt { item: sort, cx }))
617 .format(" -> ");
618 write!(f, "∃ {} : {} -> Prop", self.kvid.display().to_string().replace("$", "_"), sorts)
619 }
620}
621
622impl LeanFmt for (&KVid, &ClosedSolution) {
623 fn lean_fmt(&self, f: &mut fmt::Formatter, cx: &LeanCtxt) -> fmt::Result {
624 let (kvid, (implicit, (explicit, inner))) = self;
625 write!(f, "def k{} ", kvid.as_usize())?;
626 for (arg, sort) in implicit.iter().chain(explicit) {
627 write!(f, "(")?;
628 arg.lean_fmt(f, cx)?;
629 write!(f, " : {}) ", WithLeanCtxt { item: sort, cx })?;
630 }
631 writeln!(f, ": Prop :=")?;
632 write!(f, " ")?;
633 inner.lean_fmt(f, cx)?;
634 Ok(())
635 }
636}
637
638impl<'a> LeanFmt for LeanKConstraint<'a> {
639 fn lean_fmt(&self, f: &mut fmt::Formatter, cx: &LeanCtxt) -> fmt::Result {
640 let theorem_name = self.theorem_name.replace(".", "_");
641 let namespace = format!("{}KVarSolutions", snake_case_to_pascal_case(&theorem_name));
642 if !cx.kvar_solutions.is_empty() {
643 writeln!(f, "namespace {namespace}\n")?;
644
645 if !cx.kvar_solutions.cut_solutions.is_empty() {
646 writeln!(f, "-- cyclic (cut) kvars")?;
647 for kvar_solution in &cx.kvar_solutions.cut_solutions {
648 kvar_solution.lean_fmt(f, &cx)?;
649 writeln!(f)?;
650 }
651 }
652
653 if !cx.kvar_solutions.non_cut_solutions.is_empty() {
654 writeln!(f, "-- acyclic (non-cut) kvars")?;
655 for kvar_solution in &cx.kvar_solutions.non_cut_solutions {
656 kvar_solution.lean_fmt(f, &cx)?;
657 writeln!(f)?;
658 }
659 }
660 writeln!(f, "\nend {namespace}\n\n")?;
661 writeln!(f, "open {namespace}\n\n")?;
662 }
663
664 write!(f, "\n\ndef {theorem_name} := ")?;
665
666 if self.kvars.is_empty() {
667 self.constr.lean_fmt(f, cx)
668 } else {
669 write!(
670 f,
671 "{}, ",
672 self.kvars
673 .iter()
674 .map(|kvar| { WithLeanCtxt { item: kvar, cx } })
675 .format(", ")
676 )?;
677 self.constr.lean_fmt(f, cx)
678 }
679 }
680}
681
682impl LeanFmt for Constraint {
683 fn lean_fmt(&self, f: &mut fmt::Formatter, cx: &LeanCtxt) -> fmt::Result {
684 let mut fmt_cx = ConstraintFormatter::default();
685 fmt_cx.incr();
686 fmt_cx.newline(f)?;
687 self.fmt_nested(f, cx, &mut fmt_cx)?;
688 fmt_cx.decr();
689 Ok(())
690 }
691}
692
693impl FormatNested for Constraint {
694 fn fmt_nested(
695 &self,
696 f: &mut fmt::Formatter,
697 lean_cx: &LeanCtxt,
698 fmt_cx: &mut ConstraintFormatter,
699 ) -> fmt::Result {
700 match self {
701 Constraint::ForAll(bind, inner) => {
702 let trivial_pred = bind.pred.is_trivially_true();
703 let trivial_bind = bind.name.display().to_string().starts_with("_");
704 if !trivial_bind {
705 write!(f, "∀ (")?;
706 bind.name.lean_fmt(f, lean_cx)?;
707 write!(f, " : {}),", WithLeanCtxt { item: &bind.sort, cx: lean_cx })?;
708 fmt_cx.incr();
709 fmt_cx.newline(f)?;
710 }
711 if !trivial_pred {
712 bind.pred.lean_fmt(f, lean_cx)?;
713 write!(f, " ->")?;
714 fmt_cx.incr();
715 fmt_cx.newline(f)?;
716 }
717 inner.fmt_nested(f, lean_cx, fmt_cx)?;
718 if !trivial_pred {
719 fmt_cx.decr();
720 }
721 if !trivial_bind {
722 fmt_cx.decr();
723 }
724 Ok(())
725 }
726 Constraint::Conj(constraints) => {
727 let n = constraints.len();
728 for (i, constraint) in constraints.iter().enumerate() {
729 write!(f, "(")?;
730 constraint.fmt_nested(f, lean_cx, fmt_cx)?;
731 write!(f, ")")?;
732 if i < n - 1 {
733 write!(f, " ∧")?;
734 }
735 fmt_cx.newline(f)?;
736 }
737 Ok(())
738 }
739 Constraint::Pred(pred, _) => pred.lean_fmt(f, lean_cx),
740 }
741 }
742}
743
744pub trait FormatNested {
745 fn fmt_nested(
746 &self,
747 f: &mut fmt::Formatter,
748 lean_cx: &LeanCtxt,
749 fmt_cx: &mut ConstraintFormatter,
750 ) -> fmt::Result;
751}
752
753#[derive(Default)]
754pub struct ConstraintFormatter {
755 level: u32,
756}
757
758impl ConstraintFormatter {
759 pub fn incr(&mut self) {
760 self.level += 1;
761 }
762
763 pub fn decr(&mut self) {
764 self.level -= 1;
765 }
766
767 pub fn newline(&self, f: &mut fmt::Formatter) -> fmt::Result {
768 f.write_char('\n')?;
769 self.padding(f)
770 }
771
772 pub fn padding(&self, f: &mut fmt::Formatter) -> fmt::Result {
773 for _ in 0..self.level {
774 f.write_str(" ")?;
775 }
776 Ok(())
777 }
778}
779
780pub fn def_id_to_pascal_case(def_id: &DefId, tcx: &rustc_middle::ty::TyCtxt) -> String {
781 let snake = tcx
782 .def_path(*def_id)
783 .to_filename_friendly_no_crate()
784 .replace("-", "_");
785 let pascal_case = snake_case_to_pascal_case(&snake);
786 let re = regex::Regex::new(r"\{impl#(\d+)\}").unwrap();
787 re.replace_all(&pascal_case, "Impl__$1__").to_string()
788}
789
790pub fn snake_case_to_pascal_case(snake: &str) -> String {
791 snake
792 .split('_')
793 .filter(|s| !s.is_empty()) .map(|word| {
795 let mut chars = word.chars();
796 match chars.next() {
797 Some(first) => first.to_ascii_uppercase().to_string() + chars.as_str(),
798 None => String::new(),
799 }
800 })
801 .collect::<String>()
802}