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