1use super::{declined, rejected, DiagnosticCode, ImportedContext, Result, Verdict};
26use crate::interface::surface::{Name, Namespaces, Resolution};
27use crate::interface::{print_command, Command, Repl};
28use crate::{Context, Term};
29use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
30
31#[path = "emit_literate.rs"]
32mod literate;
33#[path = "emit_names.rs"]
34mod names;
35#[path = "emit_publish.rs"]
36mod publish;
37#[path = "emit_terms.rs"]
38mod terms;
39
40pub use names::LeanPart;
41pub use publish::{publish, publish_through, Published, DOCUMENT_EXTENSION, FAILURE_EXTENSION};
42
43#[derive(Clone, Debug, PartialEq, Eq)]
45pub enum Origin {
46 Authored,
48 Helper { of: String },
50 Synthesized,
52 Prelude,
55}
56
57#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
59pub enum DeclarationKind {
60 Inductive,
61 Constructor,
62 Definition,
64 Theorem,
67 Opaque,
69 Axiom,
71 Quotient,
73}
74
75impl DeclarationKind {
76 pub fn as_str(self) -> &'static str {
77 match self {
78 DeclarationKind::Inductive => "inductive",
79 DeclarationKind::Constructor => "constructor",
80 DeclarationKind::Definition => "definition",
81 DeclarationKind::Theorem => "theorem",
82 DeclarationKind::Opaque => "opaque",
83 DeclarationKind::Axiom => "axiom",
84 DeclarationKind::Quotient => "quotient",
85 }
86 }
87}
88
89#[derive(Clone, Copy, Debug, PartialEq, Eq)]
91pub enum Form {
92 Literate,
94 Vernacular,
96 Prelude,
98}
99
100#[derive(Clone, Debug, PartialEq, Eq)]
102pub struct Correspondence {
103 pub lean: String,
107 pub importer_key: String,
109 pub logos: String,
111 pub kind: DeclarationKind,
112 pub origin: Origin,
113 pub form: Form,
114 pub levels: Vec<(String, String)>,
116 pub lines: Option<(usize, usize)>,
120}
121
122#[derive(Clone, Debug, PartialEq)]
124pub struct EmittedDocument {
125 pub source: String,
127 pub target: String,
129 pub correspondence: Vec<Correspondence>,
130 pub omissions: Vec<String>,
133 pub assumptions: Vec<String>,
135 pub losses: Vec<(String, String)>,
139 expected: Vec<Expected>,
141}
142
143#[derive(Clone, Debug, PartialEq, Eq)]
146pub enum EmissionFault {
147 Reparse { message: String },
149 CommandCount { expected: usize, found: usize },
151 Missing { logos: String },
153 Kind {
156 logos: String,
157 expected: DeclarationKind,
158 },
159 Levels {
161 logos: String,
162 expected: Vec<String>,
163 found: Vec<String>,
164 },
165 Type { logos: String },
167 Body { logos: String },
169 Constructors { logos: String },
171 Undeclared { logos: String },
173 Statement { logos: String },
175 Unmapped { name: String },
177 Recheck { logos: String },
179 Structure { logos: String },
181}
182
183impl std::fmt::Display for EmissionFault {
184 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
185 match self {
186 EmissionFault::Reparse { message } => {
187 write!(f, "the fresh context refused the document: {message}")
188 }
189 EmissionFault::CommandCount { expected, found } => {
190 write!(
191 f,
192 "the document holds {found} declaration commands where {expected} were written"
193 )
194 }
195 EmissionFault::Missing { logos } => {
196 write!(f, "`{logos}` is not declared after reparsing")
197 }
198 EmissionFault::Kind { logos, expected } => {
199 write!(f, "`{logos}` did not reparse as a {}", expected.as_str())
200 }
201 EmissionFault::Levels {
202 logos,
203 expected,
204 found,
205 } => {
206 write!(
207 f,
208 "`{logos}` reparsed with universe parameters {found:?}, not {expected:?}"
209 )
210 }
211 EmissionFault::Type { logos } => write!(f, "`{logos}` reparsed with a different type"),
212 EmissionFault::Body { logos } => write!(f, "`{logos}` reparsed with a different body"),
213 EmissionFault::Constructors { logos } => {
214 write!(
215 f,
216 "`{logos}` reparsed with different parameters or constructors"
217 )
218 }
219 EmissionFault::Undeclared { logos } => {
220 write!(
221 f,
222 "the document declares `{logos}`, which its correspondence does not list"
223 )
224 }
225 EmissionFault::Statement { logos } => {
226 write!(
227 f,
228 "`{logos}` does not state the reference translation of its Lean type"
229 )
230 }
231 EmissionFault::Unmapped { name } => write!(f, "`{name}` has no LOGOS correspondent"),
232 EmissionFault::Structure { logos } => {
233 write!(
234 f,
235 "`{logos}` did not reparse with the imported structure η and projections"
236 )
237 }
238 EmissionFault::Recheck { logos } => {
239 write!(
240 f,
241 "the independent re-checker does not confirm `{logos}` at its declared type"
242 )
243 }
244 }
245 }
246}
247
248pub const MAX_SOURCE_BYTES: usize = 64 * 1024 * 1024;
250
251pub fn emit_document(imported: &ImportedContext, target: &str) -> Result<EmittedDocument> {
257 let proofs = |key: &str| imported.retained(key).map(|(_, body)| body.clone());
258 emit_context(imported, &proofs, target)
259}
260
261pub fn emit_context(
264 ctx: &Context,
265 proofs: &(dyn Fn(&str) -> Option<Term> + Sync),
266 target: &str,
267) -> Result<EmittedDocument> {
268 let closure = Closure::of(ctx, proofs, target)?;
269 let depth = closure.depth();
270 let rungs = depth
271 .saturating_mul(3)
272 .saturating_add(crate::term_store::INLINE_DEPTH);
273 let stack = crate::term_store::stack_bytes_for_depth(rungs);
274 if stack > 8 * 1024 * 1024 * 1024 {
275 return Err(declined(
276 DiagnosticCode::ResourceLimit,
277 format!(
278 "a term of depth {depth} is deeper than LOGOS source emission reserves stack for"
279 ),
280 ));
281 }
282 std::thread::scope(|scope| {
283 std::thread::Builder::new()
284 .name("lean-emit".into())
285 .stack_size(stack)
286 .spawn_scoped(scope, || Emitter::new(ctx, &closure)?.run())
287 .map_err(|e| {
288 declined(
289 DiagnosticCode::Infrastructure,
290 format!("cannot reserve emitter stack: {e}"),
291 )
292 })?
293 .join()
294 .unwrap_or_else(|p| std::panic::resume_unwind(p))
295 })
296}
297
298pub fn verify(document: &EmittedDocument) -> std::result::Result<Repl, EmissionFault> {
301 let depth = document
302 .expected
303 .iter()
304 .flat_map(Expected::terms)
305 .map(crate::term_store::term_depth)
306 .max()
307 .unwrap_or(0);
308 let rungs = depth
309 .saturating_mul(3)
310 .saturating_add(crate::term_store::INLINE_DEPTH);
311 let stack = crate::term_store::stack_bytes_for_depth(rungs);
312 std::thread::scope(|scope| {
313 std::thread::Builder::new()
314 .name("lean-emit-verify".into())
315 .stack_size(stack)
316 .spawn_scoped(scope, || {
317 verify_on_this_stack(document, true).map(|(repl, _)| repl)
318 })
319 .map_err(|e| EmissionFault::Reparse {
320 message: format!("cannot reserve verifier stack: {e}"),
321 })?
322 .join()
323 .unwrap_or_else(|p| std::panic::resume_unwind(p))
324 })
325}
326
327pub fn check_statement(
333 document: &EmittedDocument,
334 reference: &super::reference::ReferenceDeclaration,
335) -> std::result::Result<(), EmissionFault> {
336 let repl = verify(document)?;
337 let logos = document.target.clone();
338 let target = document
339 .correspondence
340 .iter()
341 .find(|c| c.logos == logos)
342 .ok_or_else(|| EmissionFault::Missing {
343 logos: logos.clone(),
344 })?;
345 if reference.name != target.importer_key {
346 return Err(EmissionFault::Statement { logos });
347 }
348 let levels: HashMap<String, String> = target.levels.iter().cloned().collect();
349 let reference_levels = reference
350 .level_params
351 .iter()
352 .map(|l| {
353 levels
354 .get(l)
355 .cloned()
356 .ok_or_else(|| EmissionFault::Unmapped { name: l.clone() })
357 })
358 .collect::<std::result::Result<Vec<_>, _>>()?;
359 let globals: HashMap<&str, &str> = document
360 .correspondence
361 .iter()
362 .map(|c| (c.importer_key.as_str(), c.logos.as_str()))
363 .collect();
364 let lookup = |k: &str| globals.get(k).map(|s| s.to_string());
365 let renamed = terms::rename(&reference.ty, &lookup, &levels).map_err(|fault| match fault {
366 terms::TermFault::UnmappedGlobal(name) | terms::TermFault::UnboundLevel(name) => {
367 EmissionFault::Unmapped { name }
368 }
369 terms::TermFault::FreeVariable(_) | terms::TermFault::Hole => EmissionFault::Statement {
370 logos: logos.clone(),
371 },
372 })?;
373 let (found_levels, found) =
374 header(repl.context(), &logos).ok_or_else(|| EmissionFault::Missing {
375 logos: logos.clone(),
376 })?;
377 if found_levels != reference_levels {
378 return Err(EmissionFault::Levels {
379 logos,
380 expected: reference_levels,
381 found: found_levels,
382 });
383 }
384 if terms::alpha_eq(&renamed, &found) {
385 Ok(())
386 } else {
387 Err(EmissionFault::Statement { logos })
388 }
389}
390
391#[derive(Clone, Debug)]
397enum Decl {
398 Prelude { members: Vec<String> },
400 Inductive { members: Vec<String> },
402 Definition {
403 key: String,
404 levels: Vec<String>,
405 ty: Term,
406 body: Term,
407 },
408 Theorem {
411 key: String,
412 levels: Vec<String>,
413 ty: Term,
414 proof: Term,
415 transparent: bool,
416 },
417 Axiom {
418 key: String,
419 levels: Vec<String>,
420 ty: Term,
421 },
422 Quotient {
424 key: String,
425 kind: crate::quotient::Kind,
426 levels: Vec<String>,
427 ty: Term,
428 equality: Option<String>,
429 },
430}
431
432impl Decl {
433 fn key(&self) -> &str {
435 match self {
436 Decl::Prelude { members } | Decl::Inductive { members } => &members[0],
437 Decl::Definition { key, .. }
438 | Decl::Theorem { key, .. }
439 | Decl::Axiom { key, .. }
440 | Decl::Quotient { key, .. } => key,
441 }
442 }
443}
444
445const PRELUDE_IDENTITIES: [&str; 3] = ["Nat", "Zero", "Succ"];
447
448const HELPER_PREFIX: &str = "arena.internal.dag.";
449
450struct Closure {
451 decls: Vec<Decl>,
452 unit_of: BTreeMap<String, usize>,
454 deps: Vec<BTreeSet<usize>>,
456 target: usize,
458 target_key: String,
460}
461
462impl Closure {
463 fn of(
464 ctx: &Context,
465 proofs: &(dyn Fn(&str) -> Option<Term> + Sync),
466 target: &str,
467 ) -> Result<Closure> {
468 let mut decls: Vec<Decl> = Vec::new();
469 let mut unit_of: BTreeMap<String, usize> = BTreeMap::new();
470 let mut refs: Vec<BTreeSet<String>> = Vec::new();
471 let mut work: Vec<(String, Option<String>)> = vec![(target.to_string(), None)];
472 while let Some((key, referrer)) = work.pop() {
473 if unit_of.contains_key(&key) {
474 continue;
475 }
476 let decl = classify(ctx, proofs, &key).map_err(|verdict| match referrer {
477 Some(from) => verdict_with_prefix(verdict, format!("`{from}` refers to `{key}`")),
478 None => verdict,
479 })?;
480 let index = decls.len();
481 let mut mentioned = BTreeSet::new();
482 let covered = unit_keys(ctx, &decl);
483 match &decl {
484 Decl::Prelude { .. } => {}
485 Decl::Inductive { members } => {
486 for member in members {
487 if let Some(arity) = ctx.get_global(member) {
488 terms::references(arity, &mut mentioned);
489 }
490 for (_, ty) in ctx.get_constructors(member) {
491 terms::references(ty, &mut mentioned);
492 }
493 for projection in projections_of(ctx, member) {
494 if let Some((_, ty, body)) = definition_payload(ctx, &projection) {
495 terms::references(&ty, &mut mentioned);
496 terms::references(&body, &mut mentioned);
497 }
498 }
499 }
500 }
501 Decl::Definition { ty, body, .. } => {
502 terms::references(ty, &mut mentioned);
503 terms::references(body, &mut mentioned);
504 }
505 Decl::Theorem { ty, proof, .. } => {
506 terms::references(ty, &mut mentioned);
507 terms::references(proof, &mut mentioned);
508 }
509 Decl::Axiom { ty, .. } | Decl::Quotient { ty, .. } => {
510 terms::references(ty, &mut mentioned)
511 }
512 }
513 for k in &covered {
514 unit_of.insert(k.clone(), index);
515 }
516 for k in &mentioned {
517 if !covered.contains(k) {
518 work.push((k.clone(), Some(display_key(&key))));
519 }
520 }
521 mentioned.retain(|k| !covered.contains(k));
522 refs.push(mentioned);
523 decls.push(decl);
524 }
525 let deps = refs
526 .iter()
527 .map(|mentioned| mentioned.iter().map(|k| unit_of[k]).collect())
528 .collect();
529 Ok(Closure {
530 target: unit_of[target],
531 target_key: target.to_string(),
532 decls,
533 unit_of,
534 deps,
535 })
536 }
537
538 fn depth(&self) -> usize {
540 self.decls
541 .iter()
542 .flat_map(|d| match d {
543 Decl::Definition { ty, body, .. } => vec![ty, body],
544 Decl::Theorem { ty, proof, .. } => vec![ty, proof],
545 Decl::Axiom { ty, .. } | Decl::Quotient { ty, .. } => vec![ty],
546 Decl::Prelude { .. } | Decl::Inductive { .. } => vec![],
547 })
548 .map(crate::term_store::term_depth)
549 .max()
550 .unwrap_or(0)
551 }
552}
553
554fn verdict_with_prefix(verdict: Verdict, prefix: String) -> Verdict {
555 verdict.located(prefix)
556}
557
558fn display_key(key: &str) -> String {
560 names::lean_name(key).map_or_else(|| key.to_string(), |p| names::lean_display(&p))
561}
562
563fn unit_keys(ctx: &Context, decl: &Decl) -> BTreeSet<String> {
565 match decl {
566 Decl::Prelude { members } | Decl::Inductive { members } => {
567 let mut keys: BTreeSet<String> = members.iter().cloned().collect();
568 for member in members {
569 keys.extend(
570 ctx.get_constructors(member)
571 .into_iter()
572 .map(|(c, _)| c.to_string()),
573 );
574 keys.extend(projections_of(ctx, member));
575 }
576 keys
577 }
578 other => BTreeSet::from([other.key().to_string()]),
579 }
580}
581
582fn projections_of(ctx: &Context, inductive: &str) -> Vec<String> {
586 ctx.struct_info(inductive)
587 .map(|info| info.projections.clone())
588 .unwrap_or_default()
589}
590
591fn classify(
593 ctx: &Context,
594 proofs: &(dyn Fn(&str) -> Option<Term> + Sync),
595 key: &str,
596) -> Result<Decl> {
597 if let Some(kind) = quotient_kind(key) {
598 let (levels, ty) = header(ctx, key).ok_or_else(|| {
599 rejected(
600 DiagnosticCode::MissingDeclaration,
601 format!(
602 "the quotient primitive `{}` is not installed",
603 display_key(key)
604 ),
605 )
606 })?;
607 let equality = if kind == crate::quotient::Kind::Lift {
610 let mut names = BTreeSet::new();
611 terms::references(&ty, &mut names);
612 match names.into_iter().find(|n| quotient_kind(n).is_none()) {
613 Some(eq) => Some(eq),
614 None => {
615 return Err(rejected(
616 DiagnosticCode::MalformedExport,
617 "the quotient lift names no equality",
618 ))
619 }
620 }
621 } else {
622 None
623 };
624 return Ok(Decl::Quotient {
625 key: key.to_string(),
626 kind,
627 levels,
628 ty,
629 equality,
630 });
631 }
632 if PRELUDE_IDENTITIES.contains(&key) {
633 return Ok(Decl::Prelude {
634 members: vec!["Nat".to_string()],
635 });
636 }
637 if let Some(inductive) = ctx.constructor_inductive(key) {
638 return classify(ctx, proofs, &inductive.to_string());
639 }
640 if ctx.is_inductive(key) {
641 let members = ctx
642 .mutual_block_of(key)
643 .map(<[String]>::to_vec)
644 .unwrap_or_else(|| vec![key.to_string()]);
645 return Ok(Decl::Inductive { members });
646 }
647 if ctx.checked_opaque_dependencies(key).is_some() {
648 if let Some((levels, ty, proof)) = definition_payload(ctx, key) {
649 return Ok(Decl::Theorem {
650 key: key.to_string(),
651 levels,
652 ty,
653 proof,
654 transparent: true,
655 });
656 }
657 }
658 if let Some((levels, ty, body)) = ctx.get_universe_poly(key) {
659 return Ok(Decl::Definition {
660 key: key.to_string(),
661 levels: levels.clone(),
662 ty: ty.clone(),
663 body: body.clone(),
664 });
665 }
666 if let (Some(ty), Some(body)) = (ctx.get_definition_type(key), ctx.get_definition_body(key)) {
667 return Ok(Decl::Definition {
668 key: key.to_string(),
669 levels: Vec::new(),
670 ty: ty.clone(),
671 body: body.clone(),
672 });
673 }
674 if let Some((levels, ty)) = header(ctx, key) {
675 if ctx.checked_opaque_dependencies(key).is_some() {
676 let proof = proofs(key).ok_or_else(|| {
677 declined(
678 DiagnosticCode::UnsupportedFeature,
679 format!(
680 "the checked value of the opaque declaration `{}` was not retained by the \
681 import, and a checked declaration is never written as an assumption",
682 display_key(key)
683 ),
684 )
685 })?;
686 return Ok(Decl::Theorem {
687 key: key.to_string(),
688 levels,
689 ty,
690 proof,
691 transparent: false,
692 });
693 }
694 return Ok(Decl::Axiom {
695 key: key.to_string(),
696 levels,
697 ty,
698 });
699 }
700 if let Some((_, blocker)) = ctx
701 .pending_obligations()
702 .into_iter()
703 .find(|(pending, _)| *pending == key)
704 {
705 return Err(rejected(
706 DiagnosticCode::MissingDeclaration,
707 format!(
708 "`{}` waits for `{}`, which is not declared: an unresolved forward obligation, not a checked declaration",
709 display_key(key),
710 display_key(blocker)
711 ),
712 ));
713 }
714 Err(rejected(
715 DiagnosticCode::MissingDeclaration,
716 format!(
717 "`{}` is not declared in the imported environment",
718 display_key(key)
719 ),
720 ))
721}
722
723fn quotient_kind(key: &str) -> Option<crate::quotient::Kind> {
725 use crate::quotient::Kind;
726 [Kind::Type, Kind::Constructor, Kind::Lift, Kind::Induction]
727 .into_iter()
728 .find(|k| k.name() == key)
729}
730
731fn header(ctx: &Context, key: &str) -> Option<(Vec<String>, Term)> {
733 if let Some((levels, ty)) = ctx.poly_declaration(key) {
734 return Some((levels.to_vec(), ty.clone()));
735 }
736 ctx.get_global(key).map(|ty| (Vec::new(), ty.clone()))
737}
738
739fn definition_payload(ctx: &Context, key: &str) -> Option<(Vec<String>, Term, Term)> {
741 if let Some((levels, ty, body)) = ctx.get_universe_poly(key) {
742 return Some((levels.clone(), ty.clone(), body.clone()));
743 }
744 Some((
745 Vec::new(),
746 ctx.get_definition_type(key)?.clone(),
747 ctx.get_definition_body(key)?.clone(),
748 ))
749}
750
751#[derive(Clone, Debug, PartialEq)]
756struct ExpectedInductive {
757 logos: String,
758 levels: Vec<String>,
759 arity: Term,
760 params: usize,
761 constructors: Vec<(String, Term)>,
762 structure: Option<Vec<(String, Term, Term)>>,
764}
765
766#[derive(Clone, Debug, PartialEq)]
767enum Expected {
768 Inductive {
769 members: Vec<ExpectedInductive>,
770 written: bool,
771 },
772 Definition {
773 logos: String,
774 levels: Vec<String>,
775 ty: Term,
776 body: Term,
777 },
778 Theorem {
780 logos: String,
781 levels: Vec<String>,
782 ty: Term,
783 body: Option<Term>,
784 },
785 Axiom {
786 logos: String,
787 levels: Vec<String>,
788 ty: Term,
789 },
790 Quotient {
791 logos: String,
792 levels: Vec<String>,
793 ty: Term,
794 },
795}
796
797impl Expected {
798 fn terms(&self) -> Vec<&Term> {
799 match self {
800 Expected::Inductive { members, .. } => members
801 .iter()
802 .flat_map(|m| {
803 std::iter::once(&m.arity)
804 .chain(m.constructors.iter().map(|(_, t)| t))
805 .chain(
806 m.structure
807 .iter()
808 .flatten()
809 .flat_map(|(_, ty, body)| [ty, body]),
810 )
811 })
812 .collect(),
813 Expected::Definition { ty, body, .. } => vec![ty, body],
814 Expected::Theorem {
815 ty,
816 body: Some(body),
817 ..
818 } => vec![ty, body],
819 Expected::Theorem { ty, .. }
820 | Expected::Axiom { ty, .. }
821 | Expected::Quotient { ty, .. } => vec![ty],
822 }
823 }
824
825 fn commands(&self) -> usize {
827 match self {
828 Expected::Inductive {
830 written: true,
831 members,
832 } => {
833 1 + members
834 .iter()
835 .filter_map(|m| m.structure.as_ref())
836 .map(|p| p.len() + 1)
837 .sum::<usize>()
838 }
839 Expected::Inductive { written: false, .. } => 0,
840 _ => 1,
841 }
842 }
843
844 fn logos_keys(&self) -> Vec<&str> {
846 match self {
847 Expected::Inductive { members, .. } => members
848 .iter()
849 .flat_map(|m| {
850 std::iter::once(m.logos.as_str())
851 .chain(m.constructors.iter().map(|(c, _)| c.as_str()))
852 .chain(m.structure.iter().flatten().map(|(p, _, _)| p.as_str()))
853 })
854 .collect(),
855 Expected::Definition { logos, .. }
856 | Expected::Theorem { logos, .. }
857 | Expected::Axiom { logos, .. }
858 | Expected::Quotient { logos, .. } => {
859 vec![logos]
860 }
861 }
862 }
863}
864
865fn compare(ctx: &Context, expected: &Expected) -> std::result::Result<(), EmissionFault> {
867 let levels_match = |logos: &str, want: &[String], got: &[String]| {
868 if want == got {
869 Ok(())
870 } else {
871 Err(EmissionFault::Levels {
872 logos: logos.to_string(),
873 expected: want.to_vec(),
874 found: got.to_vec(),
875 })
876 }
877 };
878 match expected {
879 Expected::Definition {
880 logos,
881 levels,
882 ty,
883 body,
884 } => {
885 let Some((got_levels, got_ty, got_body)) = definition_payload(ctx, logos) else {
886 return Err(if header(ctx, logos).is_some() {
887 EmissionFault::Kind {
888 logos: logos.clone(),
889 expected: DeclarationKind::Definition,
890 }
891 } else {
892 EmissionFault::Missing {
893 logos: logos.clone(),
894 }
895 });
896 };
897 if ctx.checked_opaque_dependencies(logos).is_some() {
898 return Err(EmissionFault::Kind {
899 logos: logos.clone(),
900 expected: DeclarationKind::Definition,
901 });
902 }
903 levels_match(logos, levels, &got_levels)?;
904 if !terms::alpha_eq(ty, &got_ty) {
905 return Err(EmissionFault::Type {
906 logos: logos.clone(),
907 });
908 }
909 if !terms::alpha_eq(body, &got_body) {
910 return Err(EmissionFault::Body {
911 logos: logos.clone(),
912 });
913 }
914 Ok(())
915 }
916 Expected::Quotient { logos, levels, ty } => {
917 let Some((got_levels, got_ty)) = header(ctx, logos) else {
918 return Err(EmissionFault::Missing {
919 logos: logos.clone(),
920 });
921 };
922 let primitive =
923 ctx.quotient_primitives.get(logos).map(|c| c.kind) == quotient_kind(logos);
924 if !primitive || definition_payload(ctx, logos).is_some() {
925 return Err(EmissionFault::Kind {
926 logos: logos.clone(),
927 expected: DeclarationKind::Quotient,
928 });
929 }
930 levels_match(logos, levels, &got_levels)?;
931 if !terms::alpha_eq(ty, &got_ty) {
932 return Err(EmissionFault::Type {
933 logos: logos.clone(),
934 });
935 }
936 Ok(())
937 }
938 Expected::Theorem {
939 logos, levels, ty, ..
940 }
941 | Expected::Axiom { logos, levels, ty } => {
942 let (checked_expected, body, kind) = match expected {
944 Expected::Theorem {
945 body: Some(body), ..
946 } => (true, Some(body), DeclarationKind::Theorem),
947 Expected::Theorem { body: None, .. } => (true, None, DeclarationKind::Opaque),
948 _ => (false, None, DeclarationKind::Axiom),
949 };
950 let Some((got_levels, got_ty)) = header(ctx, logos) else {
951 return Err(EmissionFault::Missing {
952 logos: logos.clone(),
953 });
954 };
955 let payload = definition_payload(ctx, logos);
956 let checked = ctx.checked_opaque_dependencies(logos).is_some();
957 if payload.is_some() != body.is_some()
958 || ctx.is_inductive(logos)
959 || ctx.is_constructor(logos)
960 || checked != checked_expected
961 {
962 return Err(EmissionFault::Kind {
963 logos: logos.clone(),
964 expected: kind,
965 });
966 }
967 levels_match(logos, levels, &got_levels)?;
968 if !terms::alpha_eq(ty, &got_ty) {
969 return Err(EmissionFault::Type {
970 logos: logos.clone(),
971 });
972 }
973 if let (Some(want), Some((_, _, got))) = (body, payload) {
974 if !terms::alpha_eq(want, &got) {
975 return Err(EmissionFault::Body {
976 logos: logos.clone(),
977 });
978 }
979 }
980 Ok(())
981 }
982 Expected::Inductive { members, .. } => {
983 for member in members {
984 let logos = &member.logos;
985 if !ctx.is_inductive(logos) {
986 return Err(if header(ctx, logos).is_some() {
987 EmissionFault::Kind {
988 logos: logos.clone(),
989 expected: DeclarationKind::Inductive,
990 }
991 } else {
992 EmissionFault::Missing {
993 logos: logos.clone(),
994 }
995 });
996 }
997 let (got_levels, arity) =
998 header(ctx, logos).ok_or_else(|| EmissionFault::Missing {
999 logos: logos.clone(),
1000 })?;
1001 let got_levels = if ctx.is_universe_poly_inductive(logos) {
1002 got_levels
1003 } else {
1004 Vec::new()
1005 };
1006 levels_match(logos, &member.levels, &got_levels)?;
1007 if !terms::alpha_eq(&member.arity, &arity) {
1008 return Err(EmissionFault::Type {
1009 logos: logos.clone(),
1010 });
1011 }
1012 let ctors = ctx.get_constructors(logos);
1013 let same = ctx.inductive_num_params(logos) == member.params
1014 && ctors.len() == member.constructors.len()
1015 && ctors.iter().zip(&member.constructors).all(
1016 |((name, ty), (want, want_ty))| {
1017 name == want && terms::alpha_eq(want_ty, ty)
1018 },
1019 );
1020 if !same {
1021 return Err(EmissionFault::Constructors {
1022 logos: logos.clone(),
1023 });
1024 }
1025 match (&member.structure, ctx.struct_info(logos)) {
1027 (None, None) => {}
1028 (Some(want), Some(info)) => {
1029 let names: Vec<&str> = want.iter().map(|(p, _, _)| p.as_str()).collect();
1030 if info
1031 .projections
1032 .iter()
1033 .map(String::as_str)
1034 .ne(names.iter().copied())
1035 {
1036 return Err(EmissionFault::Structure {
1037 logos: logos.clone(),
1038 });
1039 }
1040 for (projection, ty, body) in want {
1041 let Some((got_levels, got_ty, got_body)) =
1042 definition_payload(ctx, projection)
1043 else {
1044 return Err(EmissionFault::Missing {
1045 logos: projection.clone(),
1046 });
1047 };
1048 levels_match(projection, &member.levels, &got_levels)?;
1049 if !terms::alpha_eq(ty, &got_ty) {
1050 return Err(EmissionFault::Type {
1051 logos: projection.clone(),
1052 });
1053 }
1054 if !terms::alpha_eq(body, &got_body) {
1055 return Err(EmissionFault::Body {
1056 logos: projection.clone(),
1057 });
1058 }
1059 }
1060 }
1061 _ => {
1062 return Err(EmissionFault::Structure {
1063 logos: logos.clone(),
1064 })
1065 }
1066 }
1067 }
1068 Ok(())
1069 }
1070 }
1071}
1072
1073fn mismatch_detail(ctx: &Context, expected: &Expected) -> String {
1075 let Expected::Inductive { members, .. } = expected else {
1076 return String::new();
1077 };
1078 let mut out = String::new();
1079 for m in members {
1080 let found_params = ctx.inductive_num_params(&m.logos);
1081 if found_params != m.params {
1082 out.push_str(&format!(
1083 " (`{}` has {found_params} parameters, not {})",
1084 m.logos, m.params
1085 ));
1086 }
1087 if let Some((_, arity)) = header(ctx, &m.logos) {
1088 if !terms::alpha_eq(&m.arity, &arity) {
1089 out.push_str(&format!(" (`{}`: arity {arity}, not {})", m.logos, m.arity));
1090 }
1091 }
1092 let found = ctx.get_constructors(&m.logos);
1093 if found.len() != m.constructors.len() {
1094 out.push_str(&format!(
1095 " (`{}` has {} constructors, not {})",
1096 m.logos,
1097 found.len(),
1098 m.constructors.len()
1099 ));
1100 }
1101 for ((name, ty), (want, want_ty)) in found.iter().zip(&m.constructors) {
1102 if name != want {
1103 out.push_str(&format!(
1104 " (constructor `{name}` where `{want}` was written)"
1105 ));
1106 } else if !terms::alpha_eq(want_ty, ty) {
1107 out.push_str(&format!(" (`{name}`: {ty}, not {want_ty})"));
1108 }
1109 }
1110 }
1111 out
1112}
1113
1114fn verify_on_this_stack(
1119 document: &EmittedDocument,
1120 listed_derivations: bool,
1121) -> std::result::Result<(Repl, BTreeMap<String, String>), EmissionFault> {
1122 let mut repl = Repl::new();
1123 let prelude = repl.context().clone();
1124 repl.execute_document(&document.source)
1125 .map_err(|e| EmissionFault::Reparse {
1126 message: e.to_string(),
1127 })?;
1128
1129 let expected_commands: usize = document.expected.iter().map(Expected::commands).sum();
1130 let found_commands = count_commands(&document.source)?;
1131 if found_commands != expected_commands {
1132 return Err(EmissionFault::CommandCount {
1133 expected: expected_commands,
1134 found: found_commands,
1135 });
1136 }
1137 for expected in &document.expected {
1138 compare(repl.context(), expected)?;
1139 }
1140 let ctx = repl.context();
1141
1142 for expected in &document.expected {
1145 if let Expected::Definition {
1146 logos, ty, body, ..
1147 } = expected
1148 {
1149 let rechecked = crate::recheck(ctx, body).map_err(|_| EmissionFault::Recheck {
1150 logos: logos.clone(),
1151 })?;
1152 if !crate::defeq_for_test(ctx, &rechecked, ty) {
1153 return Err(EmissionFault::Recheck {
1154 logos: logos.clone(),
1155 });
1156 }
1157 }
1158 }
1159
1160 let listed: HashSet<&str> = document
1161 .expected
1162 .iter()
1163 .flat_map(Expected::logos_keys)
1164 .collect();
1165 let mut declared: BTreeSet<String> = BTreeSet::new();
1166 declared.extend(ctx.iter_declarations().map(|(k, _)| k.to_string()));
1167 declared.extend(ctx.iter_definitions().map(|(k, _, _)| k.to_string()));
1168 for (k, _) in ctx.iter_inductives() {
1169 declared.insert(k.to_string());
1170 declared.extend(
1171 ctx.get_constructors(k)
1172 .into_iter()
1173 .map(|(c, _)| c.to_string()),
1174 );
1175 }
1176 for key in listed.iter() {
1177 declared.remove(*key);
1178 }
1179 let family = derived_family(&document.expected);
1186 let mut derived: BTreeMap<String, String> = BTreeMap::new();
1187 for key in declared {
1188 let in_prelude = prelude.get_global(&key).is_some()
1189 || prelude.poly_declaration(&key).is_some()
1190 || prelude.is_inductive(&key);
1191 if in_prelude {
1192 continue;
1193 }
1194 let owner = family
1195 .get(&key)
1196 .cloned()
1197 .or_else(|| equation_head(&key, &listed, &family));
1198 match owner {
1199 Some(owner) if definition_payload(ctx, &key).is_some() => {
1200 derived.insert(key, owner);
1201 }
1202 _ => return Err(EmissionFault::Undeclared { logos: key }),
1203 }
1204 }
1205 if listed_derivations {
1206 let recorded: BTreeSet<&str> = document
1207 .correspondence
1208 .iter()
1209 .filter(|c| c.importer_key.is_empty())
1210 .map(|c| c.logos.as_str())
1211 .collect();
1212 if let Some(key) = derived.keys().find(|k| !recorded.contains(k.as_str())) {
1213 return Err(EmissionFault::Undeclared { logos: key.clone() });
1214 }
1215 if let Some(key) = recorded.iter().find(|k| !derived.contains_key(**k)) {
1216 return Err(EmissionFault::Missing {
1217 logos: key.to_string(),
1218 });
1219 }
1220 }
1221 Ok((repl, derived))
1222}
1223
1224fn derived_family(expected: &[Expected]) -> HashMap<String, String> {
1227 let mut family = HashMap::new();
1228 for unit in expected {
1229 if let Expected::Inductive {
1230 members,
1231 written: true,
1232 } = unit
1233 {
1234 for member in members {
1235 for name in aux_family(
1236 &member.logos,
1237 member.constructors.iter().map(|(c, _)| c.as_str()),
1238 ) {
1239 family.insert(name, member.logos.clone());
1240 }
1241 }
1242 }
1243 }
1244 family
1245}
1246
1247fn equation_head(
1250 key: &str,
1251 listed: &HashSet<&str>,
1252 family: &HashMap<String, String>,
1253) -> Option<String> {
1254 if !crate::equations::is_equation_name(key) {
1255 return None;
1256 }
1257 let head = match key.strip_suffix(crate::equations::UNFOLD_SUFFIX) {
1258 Some(head) => head,
1259 None => &key[..key.rfind(crate::equations::EQUATION_INFIX)?],
1260 };
1261 (listed.contains(head) || family.contains_key(head)).then(|| head.to_string())
1262}
1263
1264fn aux_family<'n>(inductive: &str, ctors: impl Iterator<Item = &'n str>) -> Vec<String> {
1268 let mut names: Vec<String> = [
1269 "_rec",
1270 "_recOn",
1271 "_casesOn",
1272 "_noConfusionType",
1273 "_noConfusion",
1274 "_below",
1275 "_brecOn",
1276 ]
1277 .iter()
1278 .map(|suffix| format!("{inductive}{suffix}"))
1279 .collect();
1280 names.push(format!("decEq{inductive}"));
1281 names.extend(ctors.map(|c| format!("{c}_inj")));
1282 names
1283}
1284
1285fn is_equation_of_listed(key: &str, listed: &HashSet<&str>) -> bool {
1287 if !crate::equations::is_equation_name(key) {
1288 return false;
1289 }
1290 let head = match key.strip_suffix(crate::equations::UNFOLD_SUFFIX) {
1291 Some(head) => head,
1292 None => &key[..key.rfind(crate::equations::EQUATION_INFIX).unwrap_or(0)],
1293 };
1294 listed.contains(head)
1295}
1296
1297fn count_commands(source: &str) -> std::result::Result<usize, EmissionFault> {
1300 let surface = crate::interface::Surface::new();
1301 let mut offset = 0;
1302 let mut count = 0;
1303 while !source[offset..].trim().is_empty() {
1304 let (command, consumed) =
1305 crate::interface::parse_command_prefix_in(&surface, &source[offset..]).map_err(
1306 |e| EmissionFault::Reparse {
1307 message: e.to_string(),
1308 },
1309 )?;
1310 if consumed == 0 {
1311 return Err(EmissionFault::Reparse {
1312 message: "the document parser made no progress".into(),
1313 });
1314 }
1315 if !matches!(command, Command::Comment) {
1316 count += 1;
1317 }
1318 offset += consumed;
1319 }
1320 Ok(count)
1321}
1322
1323fn unsupported(message: impl Into<String>) -> Verdict {
1328 declined(DiagnosticCode::UnsupportedFeature, message)
1329}
1330
1331fn rank(decl: &Decl) -> u8 {
1333 match decl {
1334 Decl::Axiom { .. } => 0,
1335 Decl::Quotient { .. } => 1,
1336 Decl::Prelude { .. } | Decl::Inductive { .. } => 1,
1337 Decl::Definition { .. } => 2,
1338 Decl::Theorem { .. } => 3,
1339 }
1340}
1341
1342const SECTIONS: [&str; 5] = [
1343 "Assumptions",
1344 "Types",
1345 "Definitions",
1346 "Theorems",
1347 "The target",
1348];
1349
1350struct Emitter<'a> {
1351 ctx: &'a Context,
1352 closure: &'a Closure,
1353 logos: BTreeMap<String, String>,
1355 levels: Vec<Vec<(String, String)>>,
1357 owners: BTreeMap<usize, String>,
1359 relocated_under: Option<String>,
1360 avoid: HashSet<String>,
1362 prelude_names: HashSet<String>,
1365 prelude: Context,
1366 renames: std::cell::RefCell<Vec<(String, terms::Rename)>>,
1369 hygienic: std::cell::Cell<bool>,
1371}
1372
1373impl<'a> Emitter<'a> {
1374 fn new(ctx: &'a Context, closure: &'a Closure) -> Result<Emitter<'a>> {
1375 let prelude = crate::interface::standard_context();
1376 let mut taken = names::Taken {
1377 roots: HashSet::new(),
1378 keys: HashSet::new(),
1379 };
1380 {
1381 let mut add = |k: &str| {
1382 taken
1383 .roots
1384 .insert(k.split('.').next().unwrap_or(k).to_string());
1385 taken.keys.insert(k.to_string());
1386 };
1387 prelude.iter_declarations().for_each(|(k, _)| add(k));
1388 prelude.iter_definitions().for_each(|(k, _, _)| add(k));
1389 for (k, _) in prelude.iter_inductives() {
1390 add(k);
1391 for (c, _) in prelude.get_constructors(k) {
1392 add(c);
1393 }
1394 }
1395 }
1396
1397 let mut projection_owner: BTreeMap<String, (String, usize)> = BTreeMap::new();
1400 for decl in &closure.decls {
1401 if let Decl::Inductive { members } = decl {
1402 for member in members {
1403 for (i, projection) in projections_of(ctx, member).into_iter().enumerate() {
1404 projection_owner.insert(projection, (member.clone(), i + 1));
1405 }
1406 }
1407 }
1408 }
1409 let mut lean_named: BTreeSet<String> = BTreeSet::new();
1410 let mut helpers: Vec<String> = Vec::new();
1411 let mut fixed: BTreeMap<String, String> = BTreeMap::new();
1412 for key in closure.unit_of.keys() {
1413 if PRELUDE_IDENTITIES.contains(&key.as_str()) || quotient_kind(key).is_some() {
1414 fixed.insert(key.clone(), key.clone());
1415 } else if key.starts_with(HELPER_PREFIX) {
1416 helpers.push(key.clone());
1417 } else if projection_owner.contains_key(key) {
1418 continue;
1419 } else if names::lean_name(key).is_some() {
1420 lean_named.insert(key.clone());
1421 } else {
1422 return Err(unsupported(format!(
1423 "the kernel constant `{key}` has neither a Lean name nor a known reader origin"
1424 )));
1425 }
1426 }
1427
1428 let definitions: BTreeSet<&str> = closure
1429 .decls
1430 .iter()
1431 .filter_map(|d| match d {
1432 Decl::Definition { key, .. } => Some(key.as_str()),
1433 _ => None,
1434 })
1435 .collect();
1436 let inductive_members: Vec<(&str, Vec<String>)> = closure
1437 .decls
1438 .iter()
1439 .filter_map(|d| match d {
1440 Decl::Inductive { members } => Some(members),
1441 _ => None,
1442 })
1443 .flatten()
1444 .map(|m| {
1445 (
1446 m.as_str(),
1447 ctx.get_constructors(m)
1448 .into_iter()
1449 .map(|(c, _)| c.to_string())
1450 .collect(),
1451 )
1452 })
1453 .collect();
1454 let reserved = |allocation: &BTreeMap<String, String>| -> BTreeSet<String> {
1455 let mut derived = BTreeSet::new();
1456 for (member, ctors) in &inductive_members {
1457 let Some(l) = allocation.get(*member) else {
1458 continue;
1459 };
1460 derived.extend(aux_family(
1461 l,
1462 ctors
1463 .iter()
1464 .filter_map(|c| allocation.get(c))
1465 .map(String::as_str),
1466 ));
1467 }
1468 let written_definitions: HashSet<&str> = definitions
1469 .iter()
1470 .filter_map(|k| allocation.get(*k))
1471 .map(String::as_str)
1472 .collect();
1473 for logos in allocation.values() {
1474 if is_equation_of_listed(logos, &written_definitions) {
1475 derived.insert(logos.clone());
1476 }
1477 }
1478 derived
1479 };
1480 let allocation = names::allocate(&lean_named, &fixed, &taken, &reserved)
1481 .map_err(|e| unsupported(format!("LOGOS name allocation: {e}")))?;
1482 let mut logos = allocation.logos;
1483
1484 for (projection, (owner, i)) in &projection_owner {
1485 let mut name = format!("{}.proj_{i}", logos[owner]);
1486 while logos.values().any(|v| *v == name) || taken.keys.contains(&name) {
1487 name.push('\'');
1488 }
1489 logos.insert(projection.clone(), name);
1490 }
1491
1492 let mut owners: BTreeMap<usize, String> = BTreeMap::new();
1494 let mut referrers: Vec<BTreeSet<usize>> = vec![BTreeSet::new(); closure.decls.len()];
1495 for (from, deps) in closure.deps.iter().enumerate() {
1496 for &to in deps {
1497 referrers[to].insert(from);
1498 }
1499 }
1500 helpers.sort_by_key(|h| h[HELPER_PREFIX.len()..].parse::<u64>().unwrap_or(u64::MAX));
1501 let mut per_owner: BTreeMap<String, usize> = BTreeMap::new();
1502 for helper in &helpers {
1503 let unit = closure.unit_of[helper];
1504 let owner_unit = owner_of(unit, &referrers, closure).ok_or_else(|| {
1505 unsupported(format!(
1506 "the reader's helper `{helper}` is not used by any declaration of the closure"
1507 ))
1508 })?;
1509 let owner_key = closure.decls[owner_unit].key().to_string();
1510 let owner_logos = logos[&owner_key].clone();
1511 let count = per_owner.entry(owner_logos.clone()).or_insert(0);
1512 *count += 1;
1513 let mut name = format!("{owner_logos}.shared_{count}");
1514 while logos.values().any(|v| *v == name) || taken.keys.contains(&name) {
1515 name.push('\'');
1516 }
1517 logos.insert(helper.clone(), name);
1518 owners.insert(unit, display_key(&owner_key));
1519 }
1520
1521 for (key, name) in &logos {
1522 if fixed.contains_key(key) {
1523 continue;
1524 }
1525 let clashes = prelude.get_global(name).is_some()
1526 || prelude.poly_declaration(name).is_some()
1527 || prelude.get_universe_poly(name).is_some();
1528 let spellable =
1529 crate::interface::declaration_name(name).is_ok_and(|n| n.key() == *name);
1530 if clashes || !spellable {
1531 return Err(unsupported(format!(
1532 "the LOGOS name `{name}` allocated for `{}` is not a free, spellable name",
1533 display_key(key)
1534 )));
1535 }
1536 }
1537
1538 let mut avoid: HashSet<String> = HashSet::new();
1539 for name in logos.values() {
1540 avoid.extend(name.split('.').map(str::to_string));
1541 }
1542 avoid.extend(allocation.relocated_under.iter().cloned());
1543
1544 let levels = closure
1545 .decls
1546 .iter()
1547 .map(|d| match d {
1548 Decl::Definition { levels, .. }
1549 | Decl::Theorem { levels, .. }
1550 | Decl::Axiom { levels, .. }
1551 | Decl::Quotient { levels, .. } => level_names(levels),
1552 Decl::Inductive { members } | Decl::Prelude { members } => level_names(
1553 ctx.poly_declaration(&members[0])
1554 .filter(|_| ctx.is_universe_poly_inductive(&members[0]))
1555 .map(|(l, _)| l)
1556 .unwrap_or(&[]),
1557 ),
1558 })
1559 .collect();
1560
1561 let prelude_names: HashSet<String> =
1562 taken.roots.iter().chain(&taken.keys).cloned().collect();
1563
1564 Ok(Emitter {
1565 ctx,
1566 closure,
1567 logos,
1568 levels,
1569 owners,
1570 relocated_under: allocation.relocated_under,
1571 avoid,
1572 prelude_names,
1573 prelude,
1574 renames: std::cell::RefCell::new(Vec::new()),
1575 hygienic: std::cell::Cell::new(false),
1576 })
1577 }
1578
1579 fn rename(&self, unit: usize, term: &Term) -> Result<Term> {
1580 let levels: HashMap<String, String> = self.levels[unit].iter().cloned().collect();
1581 let globals = |k: &str| self.logos.get(k).cloned();
1582 terms::rename(term, &globals, &levels).map_err(|fault| {
1583 unsupported(format!(
1584 "`{}`: {fault}",
1585 display_key(self.closure.decls[unit].key())
1586 ))
1587 })
1588 }
1589
1590 fn level_list(&self, unit: usize) -> Vec<String> {
1591 self.levels[unit].iter().map(|(_, l)| l.clone()).collect()
1592 }
1593
1594 fn expected(&self, unit: usize) -> Result<Expected> {
1596 Ok(match &self.closure.decls[unit] {
1597 Decl::Definition { key, ty, body, .. } => Expected::Definition {
1598 logos: self.logos[key].clone(),
1599 levels: self.level_list(unit),
1600 ty: self.rename(unit, ty)?,
1601 body: self.rename(unit, body)?,
1602 },
1603 Decl::Theorem {
1604 key,
1605 ty,
1606 proof,
1607 transparent,
1608 ..
1609 } => Expected::Theorem {
1610 logos: self.logos[key].clone(),
1611 levels: self.level_list(unit),
1612 ty: self.rename(unit, ty)?,
1613 body: if *transparent {
1614 Some(self.rename(unit, proof)?)
1615 } else {
1616 None
1617 },
1618 },
1619 Decl::Quotient { key, ty, .. } => Expected::Quotient {
1620 logos: self.logos[key].clone(),
1621 levels: self.level_list(unit),
1622 ty: self.rename(unit, ty)?,
1623 },
1624 Decl::Axiom { key, ty, .. } => Expected::Axiom {
1625 logos: self.logos[key].clone(),
1626 levels: self.level_list(unit),
1627 ty: self.rename(unit, ty)?,
1628 },
1629 Decl::Inductive { members } | Decl::Prelude { members } => {
1630 let mut out = Vec::with_capacity(members.len());
1631 for member in members {
1632 let arity = self.ctx.get_global(member).ok_or_else(|| {
1633 unsupported(format!(
1634 "the inductive `{}` has no arity",
1635 display_key(member)
1636 ))
1637 })?;
1638 out.push(ExpectedInductive {
1639 logos: self.logos[member].clone(),
1640 levels: self.level_list(unit),
1641 arity: self.rename(unit, arity)?,
1642 params: self.ctx.inductive_num_params(member),
1643 constructors: self
1644 .ctx
1645 .get_constructors(member)
1646 .into_iter()
1647 .map(|(c, ty)| Ok((self.logos[c].clone(), self.rename(unit, ty)?)))
1648 .collect::<Result<_>>()?,
1649 structure: match self.ctx.struct_info(member) {
1650 None => None,
1651 Some(info) => Some(
1652 info.projections
1653 .iter()
1654 .map(|p| {
1655 let (_, ty, body) = definition_payload(self.ctx, p).ok_or_else(|| {
1656 unsupported(format!("the projection `{p}` has no checked definition"))
1657 })?;
1658 Ok((self.logos[p].clone(), self.rename(unit, &ty)?, self.rename(unit, &body)?))
1659 })
1660 .collect::<Result<_>>()?,
1661 ),
1662 },
1663 });
1664 }
1665 Expected::Inductive {
1666 members: out,
1667 written: matches!(self.closure.decls[unit], Decl::Inductive { .. }),
1668 }
1669 }
1670 })
1671 }
1672
1673 fn order(&self) -> Vec<usize> {
1676 let n = self.closure.decls.len();
1677 let mut waiting: Vec<usize> = self.closure.deps.iter().map(BTreeSet::len).collect();
1678 let mut referrers: Vec<Vec<usize>> = vec![Vec::new(); n];
1679 for (from, deps) in self.closure.deps.iter().enumerate() {
1680 for &to in deps {
1681 referrers[to].push(from);
1682 }
1683 }
1684 let priority = |u: usize| {
1685 let r = if u == self.closure.target {
1686 4
1687 } else {
1688 rank(&self.closure.decls[u])
1689 };
1690 (r, self.logos[self.closure.decls[u].key()].clone(), u)
1691 };
1692 let mut ready: BTreeSet<(u8, String, usize)> =
1693 (0..n).filter(|&u| waiting[u] == 0).map(priority).collect();
1694 let mut order = Vec::with_capacity(n);
1695 while let Some(next) = ready.pop_first() {
1696 let u = next.2;
1697 order.push(u);
1698 for &r in &referrers[u] {
1699 waiting[r] -= 1;
1700 if waiting[r] == 0 {
1701 ready.insert(priority(r));
1702 }
1703 }
1704 }
1705 order
1706 }
1707
1708 fn run(self) -> Result<EmittedDocument> {
1709 let order = self.order();
1710 if order.len() != self.closure.decls.len() {
1711 return Err(unsupported(
1712 "the imported declarations reference each other in a cycle",
1713 ));
1714 }
1715
1716 let mut expected = Vec::with_capacity(order.len());
1717 let mut blocks: Vec<(usize, String, Form)> = Vec::new();
1718 let mut omissions = Vec::new();
1719 let mut probe = Repl::new();
1720
1721 for &unit in &order {
1722 let want = self.expected(unit)?;
1723 if let Decl::Prelude { .. } = self.closure.decls[unit] {
1724 compare(&self.prelude, &want).map_err(|fault| {
1725 unsupported(format!(
1726 "the imported `Nat` is not the LOGOS prelude's `Nat`: {fault}"
1727 ))
1728 })?;
1729 expected.push(want);
1730 continue;
1731 }
1732 let written = self.written(unit, &want, probe.context())?;
1733 let (text, form) = match self.literate(&written) {
1734 Ok(candidate) => {
1735 let mut scratch = Repl::new();
1736 *scratch.context_mut() = probe.context().clone();
1737 let exact = scratch.execute_document(&candidate).is_ok()
1738 && compare(scratch.context(), &want).is_ok();
1739 if exact {
1740 (candidate, Form::Literate)
1741 } else {
1742 omissions.push(format!(
1743 "{}: the literate form did not reparse to the identical declaration",
1744 written.logos_name()
1745 ));
1746 (self.vernacular(&written)?, Form::Vernacular)
1747 }
1748 }
1749 Err(why) => {
1750 omissions.push(format!("{}: {why}", written.logos_name()));
1751 (self.vernacular(&written)?, Form::Vernacular)
1752 }
1753 };
1754 probe.execute_document(&text).map_err(|e| {
1755 unsupported(format!(
1756 "LOGOS refused the emitted declaration of `{}`: {e}",
1757 display_key(self.closure.decls[unit].key())
1758 ))
1759 })?;
1760 compare(probe.context(), &want).map_err(|fault| {
1761 unsupported(format!(
1762 "the emitted declaration of `{}` did not reparse to the imported one: {fault}{}\n{text}",
1763 display_key(self.closure.decls[unit].key()),
1764 mismatch_detail(probe.context(), &want),
1765 ))
1766 })?;
1767 expected.push(want);
1768 blocks.push((unit, text, form));
1769 }
1770
1771 let (source, lines) = self.assemble(&blocks)?;
1772 if source.len() > MAX_SOURCE_BYTES {
1773 return Err(declined(
1774 DiagnosticCode::ResourceLimit,
1775 format!(
1776 "the emitted document is {} bytes, over the {MAX_SOURCE_BYTES}-byte limit",
1777 source.len()
1778 ),
1779 ));
1780 }
1781 let forms: BTreeMap<usize, Form> = blocks.iter().map(|(u, _, f)| (*u, *f)).collect();
1782 let correspondence = self.correspondence(&order, &forms, &lines);
1783 let assumptions = order
1784 .iter()
1785 .filter_map(|&u| match &self.closure.decls[u] {
1786 Decl::Axiom { key, .. } => Some(self.logos[key].clone()),
1787 _ => None,
1788 })
1789 .collect();
1790 let document = EmittedDocument {
1791 source,
1792 target: self.logos[&self.closure.target_key].clone(),
1793 correspondence,
1794 omissions,
1795 assumptions,
1796 losses: self.losses(),
1797 expected,
1798 };
1799 let (_, derived) = verify_on_this_stack(&document, false).map_err(|fault| {
1800 unsupported(format!(
1801 "the emitted document did not survive its fresh reparse: {fault}"
1802 ))
1803 })?;
1804 let mut document = document;
1807 for (logos, owner) in derived {
1808 let producer = document
1809 .correspondence
1810 .iter()
1811 .find(|c| c.logos == owner)
1812 .cloned();
1813 document.correspondence.push(Correspondence {
1814 lean: String::new(),
1815 importer_key: String::new(),
1816 logos,
1817 kind: DeclarationKind::Definition,
1818 origin: Origin::Synthesized,
1819 form: producer.as_ref().map_or(Form::Vernacular, |p| p.form),
1820 levels: Vec::new(),
1821 lines: producer.and_then(|p| p.lines),
1822 });
1823 }
1824 Ok(document)
1825 }
1826
1827 fn written(&self, unit: usize, want: &Expected, probe: &Context) -> Result<Written> {
1830 let pending: Vec<String> = want.logos_keys().iter().map(|s| s.to_string()).collect();
1834 let known = |k: &str| {
1835 pending.iter().any(|p| p == k)
1836 || probe.get_global(k).is_some()
1837 || probe.poly_declaration(k).is_some()
1838 };
1839 let declared = self.logos[self.closure.decls[unit].key()].clone();
1840 let record = |renames: Vec<terms::Rename>| {
1841 self.renames
1842 .borrow_mut()
1843 .extend(renames.into_iter().map(|r| (declared.clone(), r)));
1844 };
1845 let unavailable = terms::Unavailable {
1846 constants: &self.avoid,
1847 prelude: &self.prelude_names,
1848 };
1849 let fresh_naming = |t: &Term, named: usize| {
1851 let mut renames = Vec::new();
1852 let mut hygienic = false;
1853 let out = terms::freshen_naming(t, named, &unavailable, &mut renames, &mut hygienic)
1854 .map_err(|fault| {
1855 unsupported(format!(
1856 "`{}`: {fault}",
1857 display_key(self.closure.decls[unit].key())
1858 ))
1859 })?;
1860 record(renames);
1861 self.hygienic.set(self.hygienic.get() || hygienic);
1862 Ok::<Term, Verdict>(out)
1863 };
1864 let fresh = |t: &Term| fresh_naming(t, 0);
1865 let spell_in = |declared: &str, t: &Term| -> Result<Term> {
1866 let scope = declaration_scope(declared);
1867 let mut failure = None;
1868 let out = terms::respell(t, &mut |key| match written_reference(&scope, &known, key) {
1869 Some(w) => w,
1870 None => {
1871 failure = Some(key.to_string());
1872 key.to_string()
1873 }
1874 });
1875 match failure {
1876 Some(key) => Err(unsupported(format!(
1877 "no written name reaches `{key}` from `{declared}`"
1878 ))),
1879 None => Ok(out),
1880 }
1881 };
1882 Ok(match want {
1883 Expected::Definition {
1884 logos,
1885 levels,
1886 ty,
1887 body,
1888 } => Written::Definition {
1889 name: logos.clone(),
1890 levels: levels.clone(),
1891 ty: spell_in(logos, &fresh(ty)?)?,
1892 body: spell_in(logos, &fresh(body)?)?,
1893 },
1894 Expected::Quotient { logos, levels, .. } => {
1895 let Decl::Quotient { kind, equality, .. } = &self.closure.decls[unit] else {
1896 unreachable!("a quotient's expectation comes from a quotient")
1897 };
1898 let equality = match equality {
1899 Some(eq) => match spell_in(logos, &Term::Global(self.logos[eq].clone()))? {
1900 Term::Global(written) => Some(written),
1901 _ => unreachable!("a global respells to a global"),
1902 },
1903 None => None,
1904 };
1905 Written::Quotient {
1906 name: logos.clone(),
1907 kind: *kind,
1908 levels: levels.clone(),
1909 equality,
1910 }
1911 }
1912 Expected::Axiom { logos, levels, ty } => Written::Axiom {
1913 name: logos.clone(),
1914 levels: levels.clone(),
1915 ty: spell_in(logos, &fresh(ty)?)?,
1916 },
1917 Expected::Theorem {
1918 logos,
1919 levels,
1920 ty,
1921 body,
1922 } => {
1923 let Decl::Theorem { proof, .. } = &self.closure.decls[unit] else {
1924 unreachable!("a theorem's expectation comes from a theorem")
1925 };
1926 Written::Theorem {
1927 name: logos.clone(),
1928 transparent: body.is_some(),
1929 levels: levels.clone(),
1930 ty: spell_in(logos, &fresh(ty)?)?,
1931 proof: spell_in(logos, &fresh(&self.rename(unit, proof)?)?)?,
1932 }
1933 }
1934 Expected::Inductive { members, .. } => {
1935 let mut out = Vec::with_capacity(members.len());
1936 for m in members {
1937 let arity = fresh_naming(&m.arity, m.params)?;
1939 let (params, sort) = terms::split_pis(&arity, m.params).ok_or_else(|| {
1940 unsupported(format!(
1941 "the arity of `{}` has fewer binders than parameters",
1942 m.logos
1943 ))
1944 })?;
1945 let param_names: Vec<String> = params.iter().map(|(n, _)| n.clone()).collect();
1946 let enclosing: Vec<&str> = {
1947 let parts: Vec<&str> = m.logos.split('.').collect();
1948 parts[..parts.len() - 1].to_vec()
1949 };
1950 let mut split = Vec::with_capacity(m.constructors.len());
1954 for (ctor, ty) in &m.constructors {
1955 let mut renames = Vec::new();
1956 let mut hygienic = false;
1957 let ty = terms::freshen_with_prefix(
1958 ty,
1959 ¶m_names,
1960 &unavailable,
1961 &mut renames,
1962 &mut hygienic,
1963 )
1964 .map_err(|fault| unsupported(format!("`{ctor}`: {fault}")))?;
1965 record(renames);
1966 self.hygienic.set(self.hygienic.get() || hygienic);
1967 let (restated, rest) =
1968 terms::split_pis(&ty, m.params).ok_or_else(|| {
1969 unsupported(format!(
1970 "the constructor `{ctor}` binds fewer parameters than its type"
1971 ))
1972 })?;
1973 split.push((ctor, ty, restated, rest));
1974 }
1975 let full = split.iter().any(|(_, _, restated, _)| {
1976 restated
1977 .iter()
1978 .zip(¶ms)
1979 .any(|((_, t), (_, p))| !terms::alpha_eq(t, p))
1980 });
1981 let mut constructors = Vec::with_capacity(split.len());
1982 for (ctor, ty, _, rest) in split {
1983 let written = if full { ty } else { rest };
1984 constructors
1985 .push((relative_to(ctor, &enclosing), spell_in(&m.logos, &written)?));
1986 }
1987 let params = params
1988 .into_iter()
1989 .map(|(n, t)| Ok((n, spell_in(&m.logos, &t)?)))
1990 .collect::<Result<Vec<_>>>()?;
1991 let own = m
1992 .logos
1993 .split('.')
1994 .next_back()
1995 .unwrap_or(&m.logos)
1996 .to_string();
1997 let written_self =
1998 written_reference(&declaration_scope(&m.logos), &known, &m.logos)
1999 .unwrap_or_else(|| m.logos.clone());
2000 let structure = match &m.structure {
2001 None => None,
2002 Some(projections) => Some(
2003 projections
2004 .iter()
2005 .map(|(p, ty, body)| {
2006 Ok((
2007 p.clone(),
2008 spell_in(p, &fresh(ty)?)?,
2009 spell_in(p, &fresh(body)?)?,
2010 ))
2011 })
2012 .collect::<Result<Vec<_>>>()?,
2013 ),
2014 };
2015 out.push(WrittenInductive {
2016 name: m.logos.clone(),
2017 levels: m.levels.clone(),
2018 params,
2019 sort: spell_in(&m.logos, &sort)?,
2020 constructors,
2021 full,
2022 structure,
2023 result_heads: vec![m.logos.clone(), written_self, own],
2024 });
2025 }
2026 Written::Inductive(out)
2027 }
2028 })
2029 }
2030
2031 fn vernacular(&self, written: &Written) -> Result<String> {
2033 let (levels, command) = match written {
2034 Written::Definition {
2035 name,
2036 levels,
2037 ty,
2038 body,
2039 } => (
2040 levels,
2041 Command::Definition {
2042 name: name.clone(),
2043 ty: Some(ty.clone()),
2044 body: body.clone(),
2045 is_hint: false,
2046 implicit_count: 0,
2047 proof_goal: None,
2048 },
2049 ),
2050 Written::Theorem {
2051 name,
2052 transparent: true,
2053 levels,
2054 ty,
2055 proof,
2056 } => (
2057 levels,
2058 Command::Theorem {
2059 name: name.clone(),
2060 ty: ty.clone(),
2061 proof: proof.clone(),
2062 },
2063 ),
2064 Written::Theorem {
2065 name,
2066 transparent: false,
2067 levels,
2068 ty,
2069 proof,
2070 } => (
2071 levels,
2072 Command::Opaque {
2073 name: name.clone(),
2074 ty: ty.clone(),
2075 value: proof.clone(),
2076 },
2077 ),
2078 Written::Axiom { name, levels, ty } => (
2079 levels,
2080 Command::Axiom {
2081 name: name.clone(),
2082 ty: ty.clone(),
2083 },
2084 ),
2085 Written::Quotient {
2086 kind,
2087 levels,
2088 equality,
2089 ..
2090 } => (
2091 levels,
2092 Command::Quotient {
2093 kind: *kind,
2094 equality: equality.clone(),
2095 },
2096 ),
2097 Written::Inductive(members) => (
2100 &members[0].levels,
2101 Command::InductiveBlock {
2102 members: members
2103 .iter()
2104 .map(|m| {
2105 let names: Vec<String> =
2106 m.params.iter().map(|(n, _)| n.clone()).collect();
2107 crate::interface::command::InductiveMember {
2108 name: m.name.clone(),
2109 params: m
2110 .params
2111 .iter()
2112 .enumerate()
2113 .map(|(i, (n, t))| {
2114 (n.clone(), terms::name_parameters(t, &names[..i]))
2115 })
2116 .collect(),
2117 sort: terms::name_parameters(&m.sort, &names),
2118 constructors: m
2119 .constructors
2120 .iter()
2121 .map(|(c, t)| {
2122 let t = if m.full {
2123 t.clone()
2124 } else {
2125 terms::name_parameters(t, &names)
2126 };
2127 (c.clone(), t)
2128 })
2129 .collect(),
2130 full_constructors: m.full,
2131 }
2132 })
2133 .collect(),
2134 },
2135 ),
2136 };
2137 let wrap = |command: Command| {
2138 if levels.is_empty() {
2139 command
2140 } else {
2141 Command::Universes {
2142 params: levels.clone(),
2143 inner: Box::new(command),
2144 }
2145 }
2146 };
2147 let mut commands = vec![wrap(command)];
2148 if let Written::Inductive(members) = written {
2151 for m in members {
2152 let Some(projections) = &m.structure else {
2153 continue;
2154 };
2155 for (p, ty, body) in projections {
2156 commands.push(wrap(Command::Definition {
2157 name: p.clone(),
2158 ty: Some(ty.clone()),
2159 body: body.clone(),
2160 is_hint: false,
2161 implicit_count: 0,
2162 proof_goal: None,
2163 }));
2164 }
2165 commands.push(Command::Structure {
2166 inductive: m.name.clone(),
2167 projections: projections.iter().map(|(p, _, _)| p.clone()).collect(),
2168 });
2169 }
2170 }
2171 let printed: Vec<String> = commands
2172 .iter()
2173 .map(print_command)
2174 .collect::<std::result::Result<_, _>>()
2175 .map_err(|e| unsupported(format!("`{}`: {e}", written.logos_name())))?;
2176 Ok(printed.join("\n\n"))
2177 }
2178
2179 fn literate(&self, written: &Written) -> std::result::Result<String, String> {
2181 match written {
2182 Written::Inductive(members) if members.iter().any(|m| m.structure.is_some()) => Err(
2183 "a structure's projections and η are declared by the vernacular `Structure` command, which \
2184 the literate surface has no form for"
2185 .into(),
2186 ),
2187 Written::Inductive(members) if members.iter().any(|m| m.full) => Err(
2188 "a constructor restates its parameters with types the inductive's arity does not use, \
2189 which only the vernacular's full constructor telescope can write"
2190 .into(),
2191 ),
2192 Written::Inductive(members) if members.len() == 1 && members[0].levels.is_empty() => {
2193 let m = &members[0];
2194 literate::inductive(&literate::WrittenInductive {
2195 name: &m.name,
2196 params: &m.params,
2197 sort: &m.sort,
2198 constructors: &m.constructors,
2199 result_heads: &m.result_heads,
2200 })
2201 }
2202 Written::Inductive(members) if members.len() > 1 => {
2203 Err("a mutual block has no literate form".into())
2204 }
2205 Written::Inductive(_) => Err("the literate surface has no universe parameters".into()),
2206 Written::Definition { name, levels, ty, body } if levels.is_empty() => {
2207 literate::definition(name, ty, body)
2208 }
2209 Written::Definition { .. } => Err("the literate surface has no universe parameters".into()),
2210 Written::Theorem { .. } => Err(
2211 "the literate `## Theorem:` surface proves by tactic, and this proof is a checked term".into(),
2212 ),
2213 Written::Axiom { .. } => Err("the literate surface has no form for an assumption".into()),
2214 Written::Quotient { .. } => Err("the literate surface has no form for a quotient primitive".into()),
2215 }
2216 }
2217
2218 fn describe(&self, unit: usize) -> String {
2219 let decl = &self.closure.decls[unit];
2220 let lean = display_key(decl.key());
2221 match decl {
2222 Decl::Axiom { .. } => format!("Axiom `{lean}`: assumed, not proved."),
2223 Decl::Quotient { .. } => format!(
2224 "Quotient primitive `{lean}`: the kernel's certified quotient, installed and checked against \
2225 Lean's canonical signature."
2226 ),
2227 Decl::Inductive { members } if members.len() > 1 => format!(
2228 "Mutual inductive block {}.",
2229 members.iter().map(|m| format!("`{}`", display_key(m))).collect::<Vec<_>>().join(", ")
2230 ),
2231 Decl::Inductive { members } if members.iter().any(|m| self.ctx.struct_info(m).is_some()) => format!(
2232 "Inductive `{lean}`, a structure: its projections and its η follow it, before anything that \
2233 relies on η."
2234 ),
2235 Decl::Inductive { .. } => format!("Inductive `{lean}`."),
2236 Decl::Theorem { transparent: true, .. } => format!(
2237 "Theorem `{lean}`: its checked proof is written; like Lean's kernel, LOGOS unfolds it."
2238 ),
2239 Decl::Theorem { transparent: false, .. } => {
2240 format!("Opaque constant `{lean}`: its checked value is written and never unfolds.")
2241 }
2242 Decl::Definition { key, .. } if key.starts_with(HELPER_PREFIX) => format!(
2243 "Shared subterm `{key}` of `{}`, introduced by the Lean reader to keep its sharing; a checked definition.",
2244 self.owners.get(&unit).cloned().unwrap_or_default()
2245 ),
2246 Decl::Definition { key, .. } if is_derived_recursor(self.ctx, key) => {
2247 format!("Recursor `{lean}`, derived by LOGOS from its inductive and checked.")
2248 }
2249 Decl::Definition { .. } => format!("Definition `{lean}`."),
2250 Decl::Prelude { .. } => format!("`{lean}` is the LOGOS prelude's own."),
2251 }
2252 }
2253
2254 fn assemble(
2257 &self,
2258 blocks: &[(usize, String, Form)],
2259 ) -> Result<(String, BTreeMap<usize, (usize, usize)>)> {
2260 let mut out = String::new();
2261 out.push_str(&self.header());
2262 let mut section = None;
2263 let mut lines = BTreeMap::new();
2264 for (unit, text, _) in blocks {
2265 let s = if *unit == self.closure.target {
2266 4
2267 } else {
2268 rank(&self.closure.decls[*unit]) as usize
2269 };
2270 if section != Some(s) {
2271 out.push_str(&format!("\n/-! ## {} -/\n", SECTIONS[s]));
2272 section = Some(s);
2273 }
2274 out.push_str(&format!("\n/-- {} -/\n", prose(&self.describe(*unit))));
2275 let first = out.lines().count() + 1;
2276 out.push_str(text);
2277 out.push('\n');
2278 let last = out.lines().count();
2279 lines.insert(*unit, (first, last));
2280 }
2281 Ok((out, lines))
2282 }
2283
2284 fn annotated(&self) -> Vec<String> {
2287 let mut out: Vec<String> = self
2288 .closure
2289 .unit_of
2290 .keys()
2291 .filter(|key| {
2292 self.ctx.binder_kinds(key).is_some_and(|kinds| {
2293 kinds
2294 .iter()
2295 .any(|k| *k != crate::elaborate::ParamKind::Explicit)
2296 })
2297 })
2298 .filter_map(|key| self.logos.get(key).cloned())
2299 .collect();
2300 out.sort();
2301 out
2302 }
2303
2304 fn losses(&self) -> Vec<(String, String)> {
2305 let mut out: Vec<(String, String)> = self
2306 .annotated()
2307 .into_iter()
2308 .map(|name| {
2309 (
2310 "binder_annotations".to_string(),
2311 format!("`{name}`: implicit, strict-implicit and instance binders are written as explicit ones"),
2312 )
2313 })
2314 .collect();
2315 out.extend(
2316 self.rename_lines()
2317 .into_iter()
2318 .map(|line| ("binder_renamed".to_string(), line)),
2319 );
2320 out
2321 }
2322
2323 fn rename_lines(&self) -> Vec<String> {
2325 let mut seen = BTreeSet::new();
2326 self.renames
2327 .borrow()
2328 .iter()
2329 .filter(|entry| seen.insert((*entry).clone()))
2330 .map(|(declared, (lean, written, reason))| {
2331 format!(
2332 "`{declared}`: the Lean binder `{lean}` is written `{written}` because {}",
2333 reason.as_str()
2334 )
2335 })
2336 .collect()
2337 }
2338
2339 fn header(&self) -> String {
2340 let target = self.closure.target_key.as_str();
2341 let lean = display_key(target);
2342 let written = self
2343 .closure
2344 .decls
2345 .iter()
2346 .filter(|d| !matches!(d, Decl::Prelude { .. }))
2347 .count();
2348 let axioms: Vec<String> = self
2349 .closure
2350 .decls
2351 .iter()
2352 .filter_map(|d| match d {
2353 Decl::Axiom { key, .. } => Some(format!(
2354 "`{}` (Lean `{}`)",
2355 self.logos[key],
2356 display_key(key)
2357 )),
2358 _ => None,
2359 })
2360 .collect();
2361 let mut text = String::new();
2362 text.push_str(&format!(
2363 "# LOGOS source for the Lean declaration `{lean}`\n\n"
2364 ));
2365 text.push_str(&format!(
2366 "The declaration `{}` and the {} declarations it depends on, written in LOGOS from a checked \
2367 import. Where a literate English form states a declaration exactly it is written that way; \
2368 every other declaration is written in the kernel vernacular. Before this document was \
2369 published it was read back in a fresh LOGOS context and every declaration was compared with \
2370 the imported one.\n\n",
2371 self.logos[target],
2372 written.saturating_sub(1)
2373 ));
2374 text.push_str("## Assumptions\n\n");
2375 if axioms.is_empty() {
2376 text.push_str("Nothing below is assumed: every declaration is a definition, a type or a checked proof.\n\n");
2377 } else {
2378 text.push_str("These axioms are assumed, each written below as an `Axiom`:\n\n");
2379 for a in &axioms {
2380 text.push_str(&format!("- {a}\n"));
2381 }
2382 text.push('\n');
2383 }
2384 text.push_str("## Names\n\n");
2385 match &self.relocated_under {
2386 Some(root) => text.push_str(&format!(
2387 "Lean names are kept. A Lean root namespace the LOGOS prelude already uses is placed, with \
2388 everything inside it, under `{root}`; Lean `Nat` is the prelude's own `Nat`, `Zero` and \
2389 `Succ`. The correspondence index lists every pair.\n\n"
2390 )),
2391 None => text.push_str(
2392 "Lean names are kept; Lean `Nat` is the prelude's own `Nat`, `Zero` and `Succ`. The \
2393 correspondence index lists every pair.\n\n",
2394 ),
2395 }
2396 let renamed = self.rename_lines();
2397 if self.hygienic.get() {
2398 text.push_str(
2399 "A hygienic Lean binder, which Lean shows as inaccessible (`a✝`), is written under its own \
2400 name (`a`), as Lean's `eraseMacroScopes` reads it.\n\n",
2401 );
2402 }
2403 if renamed.is_empty() {
2404 text.push_str("Every binder the text names keeps its Lean name.\n\n");
2405 } else {
2406 text.push_str(
2407 "Every binder the text names keeps its Lean name, except where that name would read as \
2408 something else — a constant this document writes or the LOGOS prelude declares, an enclosing \
2409 binder, or syntax; then the first free `name1`, `name2`, … is written:\n\n",
2410 );
2411 for line in &renamed {
2412 text.push_str(&format!("- {line}\n"));
2413 }
2414 text.push('\n');
2415 }
2416 text.push_str("## Not carried\n\n");
2420 text.push_str(
2421 "Binder annotations (implicit, strict-implicit and instance binders) are not written: every \
2422 application below is fully explicit, exactly as the kernel checked it.\n\n",
2423 );
2424 text.push_str(
2425 "A binder nothing refers to is written as an arrow (`A -> B`) and carries no name, as Lean \
2426 itself displays it.\n",
2427 );
2428 format!("/-!\n{}-/\n", prose(&text))
2429 }
2430
2431 fn correspondence(
2432 &self,
2433 order: &[usize],
2434 forms: &BTreeMap<usize, Form>,
2435 lines: &BTreeMap<usize, (usize, usize)>,
2436 ) -> Vec<Correspondence> {
2437 let mut out = Vec::new();
2438 for &unit in order {
2439 let decl = &self.closure.decls[unit];
2440 let form = forms.get(&unit).copied().unwrap_or(Form::Prelude);
2441 let span = lines.get(&unit).copied();
2442 let levels = self.levels[unit].clone();
2443 let entry = |key: &str, kind: DeclarationKind, origin: Origin| Correspondence {
2444 lean: display_key(key),
2445 importer_key: key.to_string(),
2446 logos: self.logos[key].clone(),
2447 kind,
2448 origin,
2449 form,
2450 levels: levels.clone(),
2451 lines: span,
2452 };
2453 match decl {
2454 Decl::Prelude { members } | Decl::Inductive { members } => {
2455 let origin = if matches!(decl, Decl::Prelude { .. }) {
2456 Origin::Prelude
2457 } else {
2458 Origin::Authored
2459 };
2460 for member in members {
2461 out.push(entry(member, DeclarationKind::Inductive, origin.clone()));
2462 for (ctor, _) in self.ctx.get_constructors(member) {
2463 out.push(entry(ctor, DeclarationKind::Constructor, origin.clone()));
2464 }
2465 for projection in projections_of(self.ctx, member) {
2466 out.push(entry(
2467 &projection,
2468 DeclarationKind::Definition,
2469 Origin::Synthesized,
2470 ));
2471 }
2472 }
2473 }
2474 Decl::Definition { key, .. } => {
2475 let origin = if key.starts_with(HELPER_PREFIX) {
2476 Origin::Helper {
2477 of: self.owners.get(&unit).cloned().unwrap_or_default(),
2478 }
2479 } else if is_derived_recursor(self.ctx, key) {
2480 Origin::Synthesized
2481 } else {
2482 Origin::Authored
2483 };
2484 let mut e = entry(key, DeclarationKind::Definition, origin);
2485 if key.starts_with(HELPER_PREFIX) {
2486 e.lean = key.clone();
2487 }
2488 out.push(e);
2489 }
2490 Decl::Theorem {
2491 key, transparent, ..
2492 } => {
2493 let kind = if *transparent {
2494 DeclarationKind::Theorem
2495 } else {
2496 DeclarationKind::Opaque
2497 };
2498 out.push(entry(key, kind, Origin::Authored))
2499 }
2500 Decl::Axiom { key, .. } => {
2501 out.push(entry(key, DeclarationKind::Axiom, Origin::Authored))
2502 }
2503 Decl::Quotient { key, .. } => {
2504 out.push(entry(key, DeclarationKind::Quotient, Origin::Authored))
2505 }
2506 }
2507 }
2508 out
2509 }
2510}
2511
2512fn is_derived_recursor(ctx: &Context, key: &str) -> bool {
2515 match key.strip_suffix("/s:\"rec\"") {
2516 Some(owner) => ctx.is_inductive(owner),
2517 None => false,
2518 }
2519}
2520
2521fn owner_of(unit: usize, referrers: &[BTreeSet<usize>], closure: &Closure) -> Option<usize> {
2523 let mut seen = BTreeSet::new();
2524 let mut frontier: Vec<usize> = vec![unit];
2525 while let Some(u) = frontier.pop() {
2526 for &r in &referrers[u] {
2527 if !seen.insert(r) {
2528 continue;
2529 }
2530 if closure.decls[r].key().starts_with(HELPER_PREFIX) {
2531 frontier.push(r);
2532 } else {
2533 return Some(r);
2534 }
2535 }
2536 }
2537 None
2538}
2539
2540fn level_names(levels: &[String]) -> Vec<(String, String)> {
2543 let mut used: HashSet<String> = HashSet::new();
2544 let mut out = Vec::with_capacity(levels.len());
2545 for level in levels {
2546 let base = match names::lean_name(level).and_then(|p| p.last().cloned()) {
2547 Some(LeanPart::Str(s)) if terms::is_plain_identifier(&s) => s,
2548 Some(LeanPart::Num(n)) => format!("u{n}"),
2549 _ => "u".to_string(),
2550 };
2551 let name = std::iter::once(base.clone())
2552 .chain((1usize..).map(|i| format!("{base}{i}")))
2553 .find(|n| !used.contains(n))
2554 .expect("an unbounded sequence of candidates has a free one");
2555 used.insert(name.clone());
2556 out.push((level.clone(), name));
2557 }
2558 out
2559}
2560
2561fn declaration_scope(declared: &str) -> Namespaces {
2563 let mut ns = Namespaces::new();
2564 if let Some(name) = Name::parse(declared) {
2565 ns.enter_declaration(&name, 0);
2566 }
2567 ns
2568}
2569
2570fn written_reference(
2572 scope: &Namespaces,
2573 known: &dyn Fn(&str) -> bool,
2574 key: &str,
2575) -> Option<String> {
2576 let parts: Vec<String> = key.split('.').map(str::to_string).collect();
2577 for take in 1..=parts.len() {
2578 let candidate = Name::from_parts(parts[parts.len() - take..].to_vec())?;
2579 if let Resolution::Found(found) = scope.resolve(&candidate, known) {
2580 if found == key {
2581 return Some(candidate.key());
2582 }
2583 }
2584 }
2585 let rooted = Name::from_parts(
2586 std::iter::once(crate::interface::surface::ROOT_ESCAPE.to_string())
2587 .chain(parts)
2588 .collect(),
2589 )?;
2590 match scope.resolve(&rooted, known) {
2591 Resolution::Found(found) if found == key => Some(rooted.key()),
2592 _ => None,
2593 }
2594}
2595
2596fn relative_to(key: &str, enclosing: &[&str]) -> String {
2599 let parts: Vec<&str> = key.split('.').collect();
2600 if parts.len() > enclosing.len() && parts[..enclosing.len()] == *enclosing {
2601 parts[enclosing.len()..].join(".")
2602 } else {
2603 format!("{}.{key}", crate::interface::surface::ROOT_ESCAPE)
2604 }
2605}
2606
2607fn prose(text: &str) -> String {
2609 text.replace("/-", "/\u{2010}").replace("-/", "\u{2010}/")
2610}
2611
2612enum Written {
2614 Definition {
2615 name: String,
2616 levels: Vec<String>,
2617 ty: Term,
2618 body: Term,
2619 },
2620 Theorem {
2621 name: String,
2622 transparent: bool,
2623 levels: Vec<String>,
2624 ty: Term,
2625 proof: Term,
2626 },
2627 Axiom {
2628 name: String,
2629 levels: Vec<String>,
2630 ty: Term,
2631 },
2632 Quotient {
2633 name: String,
2634 kind: crate::quotient::Kind,
2635 levels: Vec<String>,
2636 equality: Option<String>,
2637 },
2638 Inductive(Vec<WrittenInductive>),
2639}
2640
2641struct WrittenInductive {
2642 name: String,
2643 levels: Vec<String>,
2644 params: Vec<(String, Term)>,
2645 sort: Term,
2646 constructors: Vec<(String, Term)>,
2647 full: bool,
2649 structure: Option<Vec<(String, Term, Term)>>,
2651 result_heads: Vec<String>,
2652}
2653
2654impl Written {
2655 fn logos_name(&self) -> &str {
2656 match self {
2657 Written::Definition { name, .. }
2658 | Written::Theorem { name, .. }
2659 | Written::Axiom { name, .. }
2660 | Written::Quotient { name, .. } => name,
2661 Written::Inductive(members) => &members[0].name,
2662 }
2663 }
2664}