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