1use std::{
2 fs::{self, OpenOptions},
3 io::{self, Write},
4 path::{Path, PathBuf},
5 process::{Command, Stdio},
6};
7
8use flux_common::{
9 bug,
10 dbg::{self, SpanTrace},
11 result::ResultExt,
12};
13use flux_config as config;
14use flux_middle::{
15 def_id::{FluxDefId, MaybeExternId},
16 global_env::GlobalEnv,
17 queries::QueryErr,
18 rty::{PrettyMap, local_deps},
19};
20use itertools::Itertools;
21use rustc_data_structures::fx::FxIndexSet;
22use rustc_hash::FxHashMap;
23use rustc_hir::def_id::DefId;
24use rustc_span::ErrorGuaranteed;
25
26use crate::{
27 fixpoint_encoding::{ConstDeps, InterpretedConst, KVarSolutions, SortDeps, fixpoint},
28 lean_format::{
29 self, BoolMode, LeanCtxt, WithLeanCtxt, def_id_to_pascal_case, snake_case_to_pascal_case,
30 },
31};
32
33macro_rules! string_vec {
35 ($($s:expr),* $(,)?) => {
36 vec![$($s.to_string()),*]
37 };
38}
39
40fn vc_name(genv: GlobalEnv, def_id: DefId) -> String {
41 def_id_to_pascal_case(&def_id, &genv.tcx())
42}
43
44fn project() -> String {
45 config::lean_project().to_string()
46}
47
48fn namespaced<F>(f: &mut fs::File, w: F) -> io::Result<()>
49where
50 F: Fn(&mut fs::File) -> io::Result<()>,
51{
52 let namespace = "F";
53 writeln!(f, "\nnamespace {namespace}\n")?;
54 w(f)?;
55 writeln!(f, "\nend {namespace}")
56}
57
58fn rename_dir_contents(src: &Path, dst: &Path) -> io::Result<()> {
61 if !dst.exists() {
63 fs::create_dir_all(dst)?;
64 }
65
66 for entry in fs::read_dir(src)? {
67 let entry = entry?;
68 let file_type = entry.file_type()?;
69 let src_path = entry.path();
70 let dst_path = dst.join(entry.file_name());
71
72 if file_type.is_dir() {
73 rename_dir_contents(&src_path, &dst_path)?;
75 } else {
76 fs::rename(&src_path, &dst_path)?;
80 }
81 }
82 Ok(())
83}
84
85fn constant_deps(expr: &fixpoint::Expr, acc: &mut FxIndexSet<fixpoint::Var>) {
86 match expr {
87 fixpoint::Expr::Var(v) if matches!(v, fixpoint::Var::Const(..)) => {
88 acc.insert(*v);
89 }
90 fixpoint::Expr::App(func, _, args, _) => {
91 constant_deps(func, acc);
92 args.iter().for_each(|expr| constant_deps(expr, acc));
93 }
94 fixpoint::Expr::And(inner) | fixpoint::Expr::Or(inner) => {
95 inner.iter().for_each(|expr| constant_deps(expr, acc));
96 }
97 fixpoint::Expr::Atom(_, inner)
98 | fixpoint::Expr::BinaryOp(_, inner)
99 | fixpoint::Expr::Imp(inner)
100 | fixpoint::Expr::Iff(inner)
101 | fixpoint::Expr::Let(_, inner) => {
102 inner.iter().for_each(|expr| constant_deps(expr, acc));
103 }
104 fixpoint::Expr::IfThenElse(inner) => {
105 inner.iter().for_each(|expr| constant_deps(expr, acc));
106 }
107 fixpoint::Expr::Exists(_, inner)
108 | fixpoint::Expr::Neg(inner)
109 | fixpoint::Expr::Not(inner)
110 | fixpoint::Expr::IsCtor(_, inner) => {
111 constant_deps(inner, acc);
112 }
113 fixpoint::Expr::Var(..) | fixpoint::Expr::Constant(..) | fixpoint::Expr::ThyFunc(..) => {}
114 }
115}
116
117pub fn finalize(genv: GlobalEnv) -> io::Result<()> {
118 let project = project();
119 let src = genv.temp_dir().path().join(&project);
120 let dst = genv.lean_parent_dir().join(&project);
121 if src.exists() { rename_dir_contents(&src, &dst) } else { Ok(()) }
122}
123
124fn project_path(genv: GlobalEnv, kind: FileKind) -> PathBuf {
125 let project = project();
126 match kind {
127 FileKind::Flux => genv.temp_dir().path().join(project),
128 FileKind::User => genv.lean_parent_dir().join(project),
129 }
130}
131
132fn run_lean(genv: GlobalEnv, def_id: DefId) -> io::Result<()> {
133 let proof_path = LeanFile::Proof(def_id).path(genv);
134 let out = Command::new("lake")
135 .arg("--quiet")
136 .arg("--log-level=error")
137 .arg("lean")
138 .arg(proof_path)
139 .stdout(Stdio::piped())
140 .stderr(Stdio::piped())
141 .current_dir(project_path(genv, FileKind::User))
142 .spawn()?
143 .wait_with_output()?;
144 if out.stderr.is_empty() && out.stdout.is_empty() {
145 Ok(())
146 } else {
147 let stderr =
148 std::str::from_utf8(&out.stderr).unwrap_or("Lean exited with a non-zero return code");
149 Err(io::Error::other(stderr))
150 }
151}
152
153pub fn check_proof(genv: GlobalEnv, def_id: DefId) -> Result<(), ErrorGuaranteed> {
154 run_lean(genv, def_id)
155 .map_err(|_| {
156 let name = genv.tcx().def_path(def_id).to_string_no_crate_verbose();
157 let msg = format!("failed to check external proof for `crate{name}`");
158 let span = genv.tcx().def_span(def_id);
159 QueryErr::Emitted(genv.sess().dcx().handle().struct_span_err(span, msg).emit())
160 })
161 .emit(&genv)?;
162 Ok(())
163}
164
165fn create_file_with_dirs<P: AsRef<Path>>(path: P) -> io::Result<Option<fs::File>> {
167 let path = path.as_ref();
168 if let Some(parent) = path.parent() {
169 fs::create_dir_all(parent)?;
170 }
171 match OpenOptions::new().write(true).create_new(true).open(path) {
172 Ok(file) => Ok(Some(file)),
173 Err(e) if e.kind() == io::ErrorKind::AlreadyExists => Ok(None),
174 Err(e) => Err(e),
175 }
176}
177
178#[derive(Eq, PartialEq, Hash, Debug, Clone)]
179pub enum FileKind {
180 Flux,
182 User,
184}
185
186#[derive(Eq, PartialEq, Hash, Debug, Clone)]
188pub enum LeanFile {
189 Basic,
191 Fluxlib,
193 OpaqueSort(String),
195 Struct(String),
197 OpaqueFun(String),
199 Fun(String),
201 Vc(DefId),
203 Proof(DefId),
205}
206
207impl LeanFile {
208 fn kind(&self) -> FileKind {
209 match self {
210 LeanFile::Basic
211 | LeanFile::Fluxlib
212 | LeanFile::Vc(_)
213 | LeanFile::Fun(_)
214 | LeanFile::Struct(_) => FileKind::Flux,
215 LeanFile::OpaqueSort(_) | LeanFile::OpaqueFun(_) | LeanFile::Proof(_) => FileKind::User,
216 }
217 }
218
219 fn segments(&self, genv: GlobalEnv) -> Vec<String> {
220 let project_name = snake_case_to_pascal_case(&project());
221 match self {
222 LeanFile::Basic => {
223 string_vec![project_name, "Basic"]
224 }
225 LeanFile::Fluxlib => {
226 string_vec![project_name, "Flux", "Prelude"]
227 }
228 LeanFile::OpaqueSort(name) => {
229 string_vec![project_name, "User", "Struct", name]
231 }
232 LeanFile::Struct(name) => {
233 string_vec![project_name, "Flux", "Struct", name]
235 }
236 LeanFile::OpaqueFun(name) => {
237 string_vec![project_name, "User", "Fun", name]
239 }
240 LeanFile::Fun(name) => {
241 string_vec![project_name, "Flux", "Fun", name]
243 }
244 LeanFile::Vc(def_id) => {
245 let name = vc_name(genv, *def_id);
246 string_vec![project_name, "Flux", "VC", name]
247 }
248 LeanFile::Proof(def_id) => {
249 let name = format!("{}Proof", vc_name(genv, *def_id));
250 string_vec![project_name, "User", "Proof", name]
251 }
252 }
253 }
254
255 fn path(&self, genv: GlobalEnv) -> PathBuf {
257 let mut path = project_path(genv, self.kind());
258 for segment in self.segments(genv) {
259 path = path.join(segment);
260 }
261 path.set_extension("lean");
262 path
263 }
264}
265
266pub struct LeanEncoder<'genv, 'tcx> {
267 genv: GlobalEnv<'genv, 'tcx>,
268 def_id: MaybeExternId,
269 pretty_var_map: PrettyMap<fixpoint::LocalVar>,
270 sort_deps: SortDeps,
271 fun_deps: Vec<fixpoint::FunDef>,
272 constants: ConstDeps,
273 kvar_solutions: KVarSolutions,
274 kvar_decls: Vec<fixpoint::KVarDecl>,
275 constraint: fixpoint::Constraint,
276 sort_files: FxHashMap<fixpoint::DataSort, LeanFile>,
277 fun_files: FxHashMap<FluxDefId, LeanFile>,
278 const_files: FxHashMap<fixpoint::Var, LeanFile>,
279}
280
281impl<'genv, 'tcx> LeanEncoder<'genv, 'tcx> {
282 fn lean_cx(&self) -> LeanCtxt<'_, 'genv, 'tcx> {
283 LeanCtxt {
284 genv: self.genv,
285 pretty_var_map: &self.pretty_var_map,
286 adt_map: &self.sort_deps.adt_map,
287 kvar_solutions: &self.kvar_solutions,
288 bool_mode: BoolMode::Bool,
289 }
290 }
291
292 fn datasort_name(&self, sort: &fixpoint::DataSort) -> String {
293 let name = format!("{}", WithLeanCtxt { item: sort, cx: &self.lean_cx() });
294 snake_case_to_pascal_case(&name)
295 }
296
297 fn lean_file_for_fun(&self, fun: &fixpoint::FunDef) -> LeanFile {
298 let name = self.var_name(&fun.name);
299 if fun.body.is_some() { LeanFile::Fun(name) } else { LeanFile::OpaqueFun(name) }
300 }
301
302 fn lean_file_for_interpreted_const(&self, const_: &InterpretedConst) -> LeanFile {
303 let name = self.var_name(&const_.0.name);
304 LeanFile::Fun(name)
305 }
306
307 fn var_name(&self, var: &fixpoint::Var) -> String {
308 let name = format!("{}", WithLeanCtxt { item: var, cx: &self.lean_cx() });
309 snake_case_to_pascal_case(&name)
310 }
311
312 fn import(&self, file: &LeanFile) -> String {
313 format!("import {}", file.segments(self.genv).join("."))
314 }
315
316 fn new(
317 genv: GlobalEnv<'genv, 'tcx>,
318 def_id: MaybeExternId,
319 pretty_var_map: PrettyMap<fixpoint::LocalVar>,
320 sort_deps: SortDeps,
321 fun_deps: Vec<fixpoint::FunDef>,
322 constants: ConstDeps,
323 kvar_solutions: KVarSolutions,
324 kvar_decls: Vec<fixpoint::KVarDecl>,
325 constraint: fixpoint::Constraint,
326 ) -> io::Result<Self> {
327 let mut encoder = Self {
328 genv,
329 def_id,
330 pretty_var_map,
331 sort_deps,
332 fun_deps,
333 constants,
334 kvar_decls,
335 kvar_solutions,
336 constraint,
337 fun_files: FxHashMap::default(),
338 sort_files: FxHashMap::default(),
339 const_files: FxHashMap::default(),
340 };
341 encoder.fun_files = encoder.fun_files();
342 encoder.sort_files = encoder.sort_files();
343 encoder.const_files = encoder.const_files();
344 Ok(encoder)
345 }
346
347 fn run(&self) -> io::Result<()> {
348 self.generate_lake_project_if_not_present()?;
349 self.generate_lib_if_absent()?;
350 self.generate_vc_file()?;
351 self.generate_proof_if_absent()?;
352 self.record_proof()?;
353 Ok(())
354 }
355
356 fn fun_files(&self) -> FxHashMap<FluxDefId, LeanFile> {
357 let mut res = FxHashMap::default();
358 for fun_def in &self.fun_deps {
359 let fixpoint::Var::Global(_, did) = fun_def.name else {
360 bug!("expected global var with id")
361 };
362 let name = self.var_name(&fun_def.name);
363 let file = LeanFile::Fun(name);
364 res.insert(did, file);
365 }
366 res
367 }
368
369 fn sort_files(&self) -> FxHashMap<fixpoint::DataSort, LeanFile> {
370 let mut res = FxHashMap::default();
371 for sort in &self.sort_deps.opaque_sorts {
372 let data_sort = sort.name.clone();
373 let name = self.datasort_name(&sort.name);
374 let file = LeanFile::OpaqueSort(name);
375 res.insert(data_sort, file);
376 }
377 for data_decl in &self.sort_deps.data_decls {
378 let data_sort = data_decl.name.clone();
379 let name = self.datasort_name(&data_decl.name);
380 let file = LeanFile::Struct(name);
381 res.insert(data_sort, file);
382 }
383 res
384 }
385
386 fn const_files(&self) -> FxHashMap<fixpoint::Var, LeanFile> {
387 let mut res = FxHashMap::default();
388 for (decl, _) in &self.constants.interpreted {
389 res.insert(decl.name, LeanFile::Fun(self.var_name(&decl.name)));
390 }
391 res
392 }
393
394 fn generate_lake_project_if_not_present(&self) -> io::Result<()> {
395 let path = project_path(self.genv, FileKind::User);
396 if !path.exists() {
397 Command::new("lake")
398 .current_dir(self.genv.lean_parent_dir())
399 .arg("new")
400 .arg(project())
401 .arg("lib")
402 .spawn()
403 .and_then(|mut child| child.wait())
404 .map(|_| ())?;
405 }
406 Ok(())
407 }
408
409 fn generate_opaque_sort_file_if_not_present(
410 &self,
411 sort: &fixpoint::SortDecl,
412 ) -> io::Result<()> {
413 let name = self.datasort_name(&sort.name);
414 let file = &LeanFile::OpaqueSort(name);
415
416 let path = file.path(self.genv);
417 if let Some(mut file) = create_file_with_dirs(path)? {
418 writeln!(file, "{}", self.import(&LeanFile::Fluxlib))?;
419 namespaced(&mut file, |f| {
420 writeln!(f, "def {} := sorry", WithLeanCtxt { item: sort, cx: &self.lean_cx() })
421 })?;
422 file.sync_all()?;
423 }
424 Ok(())
425 }
426
427 fn data_decl_dependencies(&self, data_decl: &fixpoint::DataDecl) -> Vec<&LeanFile> {
428 let name = &data_decl.name;
429 let mut acc = vec![];
430 data_decl.deps(&mut acc);
431 acc.into_iter()
432 .map(|data_sort| {
433 self.sort_files.get(&data_sort).unwrap_or_else(|| {
434 panic!(
435 "Missing sort file for dependency {:?} of data decl {:?}",
436 data_sort, name
437 )
438 })
439 })
440 .unique()
441 .collect()
442 }
443
444 fn generate_struct_file_if_not_present(
445 &self,
446 data_decl: &fixpoint::DataDecl,
447 ) -> io::Result<()> {
448 let name = self.datasort_name(&data_decl.name);
449 let file = &LeanFile::Struct(name);
450 let path = file.path(self.genv);
451 if let Some(mut file) = create_file_with_dirs(path)? {
453 writeln!(file, "{}", self.import(&LeanFile::Fluxlib))?;
455 for dep in self.data_decl_dependencies(data_decl) {
457 writeln!(file, "{}", self.import(dep))?;
458 }
459
460 namespaced(&mut file, |f| {
462 writeln!(f, "{}", WithLeanCtxt { item: data_decl, cx: &self.lean_cx() })
463 })?;
464 file.sync_all()?;
465 }
466 Ok(())
467 }
468
469 fn sort_file(&self, sort: &fixpoint::DataSort) -> &LeanFile {
470 self.sort_files
471 .get(sort)
472 .unwrap_or_else(|| panic!("Missing sort file for sort {:?}", sort))
473 }
474
475 fn fun_file(&self, did: &FluxDefId) -> &LeanFile {
476 self.fun_files
477 .get(did)
478 .unwrap_or_else(|| panic!("Missing fun file for fun {:?}", did))
479 }
480
481 fn const_file(&self, name: &fixpoint::Var) -> &LeanFile {
482 self.const_files
483 .get(name)
484 .unwrap_or_else(|| panic!("Missing const file for const {name:?}"))
485 }
486
487 fn fun_def_dependencies(&self, did: FluxDefId, fun_def: &fixpoint::FunDef) -> Vec<&LeanFile> {
488 let mut res = vec![];
489
490 let mut sorts = vec![];
492 fun_def.sort.deps(&mut sorts);
493 for data_sort in sorts {
494 res.push(self.sort_file(&data_sort));
495 }
496
497 if !self.genv.normalized_info(did).uif {
499 let body = self.genv.inlined_body(did);
500 for dep_id in local_deps(&body) {
501 res.push(self.fun_file(&dep_id.to_def_id()));
502 }
503 }
504
505 let mut deps = FxIndexSet::default();
506 if let Some(body) = &fun_def.body {
507 constant_deps(&body.expr, &mut deps);
508 }
509 for (decl, _) in &self.constants.interpreted {
510 if deps.contains(&decl.name) {
511 res.push(self.const_file(&decl.name));
512 }
513 }
514 res
515 }
516
517 fn generate_fun_def_file_if_not_present(
518 &self,
519 did: FluxDefId,
520 fun_def: &fixpoint::FunDef,
521 ) -> io::Result<()> {
522 let path = self.lean_file_for_fun(fun_def).path(self.genv);
523 if let Some(mut file) = create_file_with_dirs(path)? {
524 writeln!(file, "{}", self.import(&LeanFile::Fluxlib))?;
526 for dep in self.fun_def_dependencies(did, fun_def) {
528 writeln!(file, "{}", self.import(dep))?;
529 }
530
531 namespaced(&mut file, |f| {
533 writeln!(f, "{}", WithLeanCtxt { item: fun_def, cx: &self.lean_cx() })
534 })?;
535 file.sync_all()?;
536 }
537 Ok(())
538 }
539
540 fn generate_interpreted_const_file_if_not_present(
541 &self,
542 interpreted_const: &InterpretedConst,
543 ) -> io::Result<()> {
544 let (const_decl, _) = interpreted_const;
545 let path = self
546 .lean_file_for_interpreted_const(interpreted_const)
547 .path(self.genv);
548 if let Some(mut file) = create_file_with_dirs(path)? {
549 writeln!(file, "{}", self.import(&LeanFile::Fluxlib))?;
551
552 let mut sort_deps = vec![];
553 const_decl.sort.deps(&mut sort_deps);
554 for dep in sort_deps {
555 writeln!(file, "{}", self.import(self.sort_file(&dep)))?;
556 }
557
558 namespaced(&mut file, |f| {
559 if let Some(comment) = &const_decl.comment {
560 writeln!(f, "--{comment}")?;
561 }
562 writeln!(f, "{}", WithLeanCtxt { item: interpreted_const, cx: &self.lean_cx() })
563 })?;
564 file.sync_all()?;
565 }
566 Ok(())
567 }
568
569 fn generate_lib_if_absent(&self) -> io::Result<()> {
570 let path = LeanFile::Fluxlib.path(self.genv);
571 if let Some(mut file) = create_file_with_dirs(path)? {
572 writeln!(file, "-- FLUX LIBRARY [DO NOT MODIFY] --")?;
573 writeln!(
575 file,
576 "def BitVec_shiftLeft {{ n : Nat }} (x s : BitVec n) : BitVec n := BitVec.shiftLeft x (s.toNat)"
577 )?;
578 writeln!(
579 file,
580 "def BitVec_ushiftRight {{ n : Nat }} (x s : BitVec n) : BitVec n := BitVec.ushiftRight x (s.toNat)"
581 )?;
582 writeln!(
583 file,
584 "def BitVec_sshiftRight {{ n : Nat }} (x s : BitVec n) : BitVec n := BitVec.sshiftRight x (s.toNat)"
585 )?;
586 writeln!(
587 file,
588 "def BitVec_uge {{ n : Nat }} (x y : BitVec n) := (BitVec.ult x y).not"
589 )?;
590 writeln!(
591 file,
592 "def BitVec_sge {{ n : Nat }} (x y : BitVec n) := (BitVec.slt x y).not"
593 )?;
594 writeln!(
595 file,
596 "def BitVec_ugt {{ n : Nat }} (x y : BitVec n) := (BitVec.ule x y).not"
597 )?;
598 writeln!(
599 file,
600 "def BitVec_sgt {{ n : Nat }} (x y : BitVec n) := (BitVec.sle x y).not"
601 )?;
602 writeln!(
603 file,
604 "def SmtMap (t0 t1 : Type) [Inhabited t0] [BEq t0] [Inhabited t1] : Type := t0 -> t1"
605 )?;
606 writeln!(
607 file,
608 "def SmtMap_default {{ t0 t1: Type }} (v : t1) [Inhabited t0] [BEq t0] [Inhabited t1] : SmtMap t0 t1 := fun _ => v"
609 )?;
610 writeln!(
611 file,
612 "def SmtMap_store {{ t0 t1 : Type }} [Inhabited t0] [BEq t0] [Inhabited t1] (m : SmtMap t0 t1) (k : t0) (v : t1) : SmtMap t0 t1 :=\n fun x => if x == k then v else m x"
613 )?;
614 writeln!(
615 file,
616 "def SmtMap_select {{ t0 t1 : Type }} [Inhabited t0] [BEq t0] [Inhabited t1] (m : SmtMap t0 t1) (k : t0) := m k"
617 )?;
618 }
619 Ok(())
620 }
621
622 fn generate_vc_prelude(&self) -> io::Result<()> {
623 self.generate_lib_if_absent()?;
625
626 for sort in &self.sort_deps.opaque_sorts {
628 self.generate_opaque_sort_file_if_not_present(sort)?;
629 }
630 for data_decl in &self.sort_deps.data_decls {
632 self.generate_struct_file_if_not_present(data_decl)?;
633 }
634 for fun_def in &self.fun_deps {
636 let fixpoint::Var::Global(_, did) = fun_def.name else {
637 bug!("expected global var with id")
638 };
639 self.generate_fun_def_file_if_not_present(did, fun_def)?;
640 }
641 for const_decl in &self.constants.interpreted {
643 self.generate_interpreted_const_file_if_not_present(const_decl)?;
644 }
645 Ok(())
646 }
647
648 fn generate_vc_imports(&self, file: &mut fs::File) -> io::Result<()> {
649 writeln!(file, "{}", self.import(&LeanFile::Fluxlib))?;
650
651 for sort in &self.sort_deps.opaque_sorts {
652 let name = self.datasort_name(&sort.name);
653 writeln!(file, "{}", self.import(&LeanFile::OpaqueSort(name)))?;
654 }
655
656 for data_decl in &self.sort_deps.data_decls {
657 let name = self.datasort_name(&data_decl.name);
658 writeln!(file, "{}", self.import(&LeanFile::Struct(name)))?;
659 }
660
661 for fun_def in &self.fun_deps {
662 writeln!(file, "{}", self.import(&self.lean_file_for_fun(fun_def)))?;
663 }
664
665 for const_decl in &self.constants.interpreted {
666 writeln!(file, "{}", self.import(&self.lean_file_for_interpreted_const(const_decl)))?;
667 }
668
669 Ok(())
670 }
671
672 fn generate_vc_file(&self) -> io::Result<()> {
673 self.generate_vc_prelude()?;
675
676 let def_id = self.def_id.resolved_id();
678 let path = LeanFile::Vc(def_id).path(self.genv);
679 if let Some(mut file) = create_file_with_dirs(path)? {
680 self.generate_vc_imports(&mut file)?;
681
682 let vc_name = vc_name(self.genv, def_id);
683 namespaced(&mut file, |f| {
685 write!(
686 f,
687 "{}",
688 WithLeanCtxt {
689 item: lean_format::LeanKConstraint {
690 theorem_name: &vc_name,
691 kvars: &self.kvar_decls,
692 constr: &self.constraint,
693 },
694 cx: &self.lean_cx()
695 }
696 )
697 })?;
698 file.sync_all()?;
699 }
700
701 Ok(())
702 }
703
704 fn generate_proof_if_absent(&self) -> io::Result<()> {
705 let def_id = self.def_id.resolved_id();
706 let vc_name = vc_name(self.genv, def_id);
707 let proof_name = format!("{vc_name}_proof");
708 let path = LeanFile::Proof(def_id).path(self.genv);
709
710 if let Some(span) = self.genv.proven_externally(self.def_id.local_id()) {
711 let dst_span = SpanTrace::from_path(&path, 3, 5, proof_name.len());
712 dbg::hyperlink_json!(self.genv.tcx(), span, dst_span);
713 }
714
715 if let Some(mut file) = create_file_with_dirs(path)? {
716 writeln!(file, "{}", self.import(&LeanFile::Fluxlib))?;
717 writeln!(file, "{}", self.import(&LeanFile::Vc(def_id)))?;
718 namespaced(&mut file, |f| {
719 writeln!(f, "def {proof_name} : {vc_name} := by")?;
720 writeln!(f, " unfold {vc_name}")?;
721 writeln!(f, " sorry")
722 })?;
723 file.sync_all()?;
724 }
725 Ok(())
726 }
727
728 fn record_proof(&self) -> io::Result<()> {
729 let path = LeanFile::Basic.path(self.genv);
730
731 let mut file = match create_file_with_dirs(&path)? {
732 Some(mut file) => {
733 writeln!(file, "-- Flux Basic Imports [DO NOT MODIFY] --")?;
735 file
736 }
737 None => fs::OpenOptions::new().append(true).open(path)?,
738 };
739 writeln!(file, "{}", self.import(&LeanFile::Proof(self.def_id.resolved_id())))
740 }
741
742 pub fn encode(
743 genv: GlobalEnv<'genv, 'tcx>,
744 def_id: MaybeExternId,
745 pretty_var_map: PrettyMap<fixpoint::LocalVar>,
746 sort_deps: SortDeps,
747 fun_deps: Vec<fixpoint::FunDef>,
748 constants: ConstDeps,
749 kvar_decls: Vec<fixpoint::KVarDecl>,
750 constraint: fixpoint::Constraint,
751 kvar_solutions: KVarSolutions,
752 ) -> io::Result<()> {
753 let encoder = Self::new(
754 genv,
755 def_id,
756 pretty_var_map,
757 sort_deps,
758 fun_deps,
759 constants,
760 kvar_solutions,
761 kvar_decls,
762 constraint,
763 )?;
764 encoder.run()?;
765 Ok(())
766 }
767}