1use crate::declaration::{ConstantInfo, ConstructorVal, InductiveVal, QuotVal, RecursorVal};
6use crate::env::EnvError;
7use crate::Node;
8use crate::{Declaration, Environment, KernelError, TypeChecker};
9#[cfg(test)]
10use crate::{Expr, Name};
11use std::rc::Rc;
12
13use super::types::{
14 BatchCheckResult, CheckConfig, CheckStats, ConfigNode, DecisionNode, DeclKind, DeclSummary,
15 Either2, EnvBuilder, FlatSubstitution, FocusStack, LabelSet, NonEmptyVec, PathBuf, RewriteRule,
16 RewriteRuleSet, SimpleDag, SlidingSum, SmallMap, SparseVec, StackCalc, StatSummary, Stopwatch,
17 StringPool, TempEnvScope, TokenBucket, TransformStat, TransitiveClosure, VersionedRecord,
18 WellFormedResult, WindowIterator, WriteOnce,
19};
20
21#[allow(clippy::result_large_err)]
27pub fn check_declaration(env: &mut Environment, decl: Declaration) -> Result<(), KernelError> {
28 let name = decl.name().clone();
29 check_univ_param_hygiene(&decl)?;
34 match &decl {
35 Declaration::Axiom { ty, .. } => {
36 let mut tc = TypeChecker::new(env);
37 tc.ensure_sort(ty)?;
38 }
39 Declaration::Definition { ty, val, .. } => {
40 let mut tc = TypeChecker::new(env);
41 tc.ensure_sort(ty)?;
42 let val_ty = tc.infer_type(val)?;
43 tc.check_type(val, &val_ty, ty)?;
44 }
45 Declaration::Theorem { ty, val, .. } => {
46 let mut tc = TypeChecker::new(env);
47 tc.ensure_sort(ty)?;
48 let val_ty = tc.infer_type(val)?;
49 tc.check_type(val, &val_ty, ty)?;
50 }
51 Declaration::Opaque { ty, val, .. } => {
52 let mut tc = TypeChecker::new(env);
53 tc.ensure_sort(ty)?;
54 let val_ty = tc.infer_type(val)?;
55 tc.check_type(val, &val_ty, ty)?;
56 }
57 }
58 env.add(decl).map_err(|e| match e {
59 EnvError::DuplicateDeclaration(_) => {
60 KernelError::Other(format!("duplicate declaration: {}", name))
61 }
62 EnvError::NotFound(_) => KernelError::Other(format!("declaration not found: {}", name)),
63 EnvError::InvalidQuotient(msg) => KernelError::Other(msg),
64 })
65}
66#[allow(clippy::result_large_err)]
78pub(super) fn check_univ_param_hygiene(decl: &Declaration) -> Result<(), KernelError> {
79 check_univ_param_hygiene_parts(
80 decl.name(),
81 decl.univ_params(),
82 std::iter::once(decl.ty()).chain(decl.value()),
83 )
84}
85#[allow(clippy::result_large_err)]
95pub(super) fn check_univ_param_hygiene_ci(ci: &ConstantInfo) -> Result<(), KernelError> {
96 check_univ_param_hygiene_parts(
97 ci.name(),
98 ci.level_params(),
99 std::iter::once(ci.ty()).chain(ci.value()),
100 )
101}
102#[allow(clippy::result_large_err)]
117fn check_univ_param_hygiene_parts<'a>(
118 name: &crate::Name,
119 params: &[crate::Name],
120 exprs: impl Iterator<Item = &'a crate::Expr>,
121) -> Result<(), KernelError> {
122 use std::collections::HashSet;
123
124 let mut declared: HashSet<&crate::Name> = HashSet::new();
126 for p in params {
127 if !declared.insert(p) {
128 return Err(KernelError::Other(format!(
129 "declaration {}: duplicate universe parameter `{}`",
130 name, p
131 )));
132 }
133 }
134
135 for expr in exprs {
137 check_term_depth(name, expr)?;
140 check_closed_term(name, expr)?;
141 if crate::expr_util::has_level_mvar(expr) {
143 return Err(KernelError::Other(format!(
144 "declaration {}: universe metavariable in kernel-checked declaration",
145 name
146 )));
147 }
148 let mut used = Vec::new();
150 collect_expr_level_params(expr, &mut used);
151 for u in &used {
152 if !declared.contains(u) {
153 return Err(KernelError::Other(format!(
154 "declaration {}: undeclared universe parameter `{}`",
155 name, u
156 )));
157 }
158 }
159 }
160 Ok(())
161}
162#[allow(clippy::result_large_err)]
182pub(crate) fn check_closed_term(name: &crate::Name, expr: &crate::Expr) -> Result<(), KernelError> {
183 if crate::expr_util::has_any_fvar(expr) {
184 return Err(KernelError::Other(format!(
185 "declaration {}: free variable in kernel-checked declaration",
186 name
187 )));
188 }
189 if crate::expr_util::has_loose_bvars(expr) {
190 return Err(KernelError::Other(format!(
191 "declaration {}: loose bound variable in kernel-checked declaration",
192 name
193 )));
194 }
195 Ok(())
196}
197#[allow(clippy::result_large_err)]
211pub(crate) fn check_term_depth(name: &crate::Name, expr: &crate::Expr) -> Result<(), KernelError> {
212 #[cfg(feature = "term-depth")]
213 {
214 let depth = expr.depth();
215 if crate::fuel::term_depth_limit().is_some_and(|limit| depth > limit) {
216 crate::fuel::note_term_depth(depth);
217 return Err(KernelError::Other(format!(
218 "declaration {name}: a term {depth} deep is over the term-depth limit"
219 )));
220 }
221 }
222 #[cfg(not(feature = "term-depth"))]
223 let _ = (name, expr);
224 Ok(())
225}
226pub(super) fn collect_expr_level_params(expr: &crate::Expr, out: &mut Vec<crate::Name>) {
237 use crate::{Expr, Node};
238 let mut seen: std::collections::HashSet<*const Expr, crate::node_memo::WordBuildHasher> =
239 std::collections::HashSet::default();
240 let mut pending: Vec<&Node> = Vec::new();
241 let mut current: Option<&Expr> = Some(expr);
242 loop {
243 let cur = match current.take() {
244 Some(cur) => cur,
245 None => match pending.pop() {
246 Some(node) => {
247 if !node.has_level_param() || !seen.insert(node.as_ptr()) {
248 continue;
249 }
250 node
251 }
252 None => break,
253 },
254 };
255 match cur {
256 Expr::Sort(l) => collect_level_params_in_level(l, out),
257 Expr::Const(_, levels) => {
258 for l in levels {
259 collect_level_params_in_level(l, out);
260 }
261 }
262 Expr::App(f, a) => {
263 pending.push(a);
264 pending.push(f);
265 }
266 Expr::Lam(_, _, ty, body) | Expr::Pi(_, _, ty, body) => {
267 pending.push(body);
268 pending.push(ty);
269 }
270 Expr::Let(_, ty, val, body) => {
271 pending.push(body);
272 pending.push(val);
273 pending.push(ty);
274 }
275 Expr::Proj(_, _, e) => pending.push(e),
276 Expr::BVar(_) | Expr::FVar(_) | Expr::Lit(_) => {}
277 }
278 }
279}
280fn collect_level_params_in_level(l: &crate::Level, out: &mut Vec<crate::Name>) {
281 use crate::LevelView;
282 match l.view() {
283 LevelView::Param(n) => {
284 if !out.contains(n) {
285 out.push(n.clone());
286 }
287 }
288 LevelView::Succ(inner) => collect_level_params_in_level(inner, out),
289 LevelView::Max(a, b) | LevelView::IMax(a, b) => {
290 collect_level_params_in_level(a, out);
291 collect_level_params_in_level(b, out);
292 }
293 LevelView::Zero | LevelView::MVar(_) => {}
294 }
295}
296#[allow(clippy::result_large_err)]
302pub fn check_declarations(
303 env: &mut Environment,
304 decls: Vec<Declaration>,
305) -> Result<(), KernelError> {
306 for decl in decls {
307 check_declaration(env, decl)?;
308 }
309 Ok(())
310}
311#[allow(clippy::result_large_err)]
318pub fn check_constant_info(env: &mut Environment, ci: ConstantInfo) -> Result<(), KernelError> {
319 let name = ci.name().clone();
320 check_univ_param_hygiene_ci(&ci)?;
324 match &ci {
325 ConstantInfo::Axiom(av) => {
326 let mut tc = TypeChecker::new(env);
327 tc.ensure_sort(&av.common.ty)?;
328 }
329 ConstantInfo::Definition(dv) => {
330 let mut tc = TypeChecker::new(env);
331 tc.ensure_sort(&dv.common.ty)?;
332 let val_ty = tc.infer_type(&dv.value)?;
333 tc.check_type(&dv.value, &val_ty, &dv.common.ty)?;
334 }
335 ConstantInfo::Theorem(tv) => {
336 let mut tc = TypeChecker::new(env);
337 tc.ensure_sort(&tv.common.ty)?;
338 let val_ty = tc.infer_type(&tv.value)?;
339 tc.check_type(&tv.value, &val_ty, &tv.common.ty)?;
340 }
341 ConstantInfo::Opaque(ov) => {
342 let mut tc = TypeChecker::new(env);
343 tc.ensure_sort(&ov.common.ty)?;
344 let val_ty = tc.infer_type(&ov.value)?;
345 tc.check_type(&ov.value, &val_ty, &ov.common.ty)?;
346 }
347 ConstantInfo::Inductive(iv) => {
348 check_inductive_val(env, iv)?;
349 }
350 ConstantInfo::Constructor(cv) => {
351 check_constructor_val(env, cv)?;
352 }
353 ConstantInfo::Recursor(rv) => {
354 for rule in &rv.rules {
357 check_term_depth(&name, &rule.rhs)?;
358 check_closed_term(&name, &rule.rhs)?;
359 }
360 check_recursor_val(env, rv)?;
361 }
362 ConstantInfo::Quotient(qv) => {
363 check_quotient_val(env, qv)?;
364 }
365 }
366 env.add_constant(ci).map_err(|e| match e {
367 crate::env::EnvError::DuplicateDeclaration(_) => {
368 KernelError::Other(format!("duplicate declaration: {}", name))
369 }
370 crate::env::EnvError::NotFound(_) => {
371 KernelError::Other(format!("declaration not found: {}", name))
372 }
373 crate::env::EnvError::InvalidQuotient(msg) => KernelError::Other(msg),
374 })
375}
376#[allow(clippy::result_large_err)]
378pub fn check_constant_infos(
379 env: &mut Environment,
380 cis: Vec<ConstantInfo>,
381) -> Result<(), KernelError> {
382 for ci in cis {
383 check_constant_info(env, ci)?;
384 }
385 Ok(())
386}
387#[allow(clippy::result_large_err)]
399pub(super) fn check_inductive_val(
400 env: &mut Environment,
401 iv: &InductiveVal,
402) -> Result<(), KernelError> {
403 crate::inductive::derive::validate_inductive_val(env, iv)
404}
405#[allow(clippy::result_large_err)]
413pub(super) fn check_constructor_val(
414 env: &mut Environment,
415 cv: &ConstructorVal,
416) -> Result<(), KernelError> {
417 crate::inductive::derive::verify_constructor_val(env, cv)
418}
419#[allow(clippy::result_large_err)]
425pub(super) fn check_recursor_val(
426 env: &mut Environment,
427 rv: &RecursorVal,
428) -> Result<(), KernelError> {
429 crate::inductive::derive::verify_recursor_val(env, rv)
430}
431#[allow(clippy::result_large_err)]
441pub(super) fn check_quotient_val(env: &mut Environment, qv: &QuotVal) -> Result<(), KernelError> {
442 let canonical = crate::env::canonical_quot_type(qv.kind);
443 let expected_params = crate::env::canonical_quot_level_params(qv.kind);
444 if qv.common.level_params != expected_params {
445 return Err(KernelError::Other(format!(
446 "quotient `{}` has unexpected universe parameters: expected {:?}, got {:?}",
447 qv.common.name, expected_params, qv.common.level_params
448 )));
449 }
450 let mut tc = TypeChecker::new(env);
451 tc.ensure_sort(&qv.common.ty)?;
452 if !tc.is_def_eq(&qv.common.ty, &canonical) {
453 return Err(KernelError::TypeMismatch {
454 expected: canonical,
455 got: qv.common.ty.clone(),
456 context: format!("canonical type of quotient `{}`", qv.common.name),
457 });
458 }
459 Ok(())
460}
461pub fn summarize_declaration(decl: &Declaration) -> DeclSummary {
463 match decl {
464 Declaration::Axiom {
465 name, univ_params, ..
466 } => DeclSummary {
467 name: format!("{}", name),
468 kind: DeclKind::Axiom,
469 has_body: false,
470 num_univ_params: univ_params.len(),
471 },
472 Declaration::Definition {
473 name, univ_params, ..
474 } => DeclSummary {
475 name: format!("{}", name),
476 kind: DeclKind::Definition,
477 has_body: true,
478 num_univ_params: univ_params.len(),
479 },
480 Declaration::Theorem {
481 name, univ_params, ..
482 } => DeclSummary {
483 name: format!("{}", name),
484 kind: DeclKind::Theorem,
485 has_body: true,
486 num_univ_params: univ_params.len(),
487 },
488 Declaration::Opaque {
489 name, univ_params, ..
490 } => DeclSummary {
491 name: format!("{}", name),
492 kind: DeclKind::Opaque,
493 has_body: true,
494 num_univ_params: univ_params.len(),
495 },
496 }
497}
498pub fn check_declarations_collect_errors(
503 env: &mut Environment,
504 decls: Vec<Declaration>,
505) -> BatchCheckResult {
506 let mut num_ok = 0;
507 let mut num_failed = 0;
508 let mut errors = Vec::new();
509 for decl in decls {
510 let name = format!("{}", decl.name());
511 match check_declaration(env, decl) {
512 Ok(()) => num_ok += 1,
513 Err(e) => {
514 num_failed += 1;
515 errors.push((name, format!("{:?}", e)));
516 }
517 }
518 }
519 BatchCheckResult {
520 num_ok,
521 num_failed,
522 errors,
523 }
524}
525#[allow(clippy::result_large_err)]
530pub fn check_declarations_with_stats(
531 env: &mut Environment,
532 decls: Vec<Declaration>,
533) -> (CheckStats, Option<KernelError>) {
534 let mut stats = CheckStats::new();
535 for decl in decls {
536 let kind = summarize_declaration(&decl).kind;
537 match check_declaration(env, decl) {
538 Ok(()) => match kind {
539 DeclKind::Axiom => stats.num_axioms += 1,
540 DeclKind::Definition => stats.num_definitions += 1,
541 DeclKind::Theorem => stats.num_theorems += 1,
542 DeclKind::Opaque => stats.num_opaques += 1,
543 },
544 Err(e) => {
545 stats.num_failures += 1;
546 return (stats, Some(e));
547 }
548 }
549 }
550 (stats, None)
551}
552pub fn is_valid_decl_name(name: &str) -> bool {
554 !name.is_empty()
555 && name
556 .chars()
557 .all(|c| c.is_alphanumeric() || c == '_' || c == '.' || c == '\'')
558 && !name.starts_with('.')
559 && !name.ends_with('.')
560}
561pub fn has_unique_univ_params(params: &[crate::Name]) -> bool {
563 let mut seen = std::collections::HashSet::new();
564 params.iter().all(|p| seen.insert(format!("{}", p)))
565}
566pub fn univ_params_compatible(p1: &[crate::Name], p2: &[crate::Name]) -> bool {
568 p1.len() == p2.len()
569 && p1
570 .iter()
571 .zip(p2.iter())
572 .all(|(a, b)| format!("{}", a) == format!("{}", b))
573}
574pub fn format_decl_summary(summary: &DeclSummary) -> String {
576 let body_str = if summary.has_body { " := ..." } else { "" };
577 let params_str = if summary.num_univ_params > 0 {
578 format!(".{{u{}}}", summary.num_univ_params)
579 } else {
580 String::new()
581 };
582 format!(
583 "{} {}{}{} : ...",
584 summary.kind, summary.name, params_str, body_str
585 )
586}
587pub fn format_batch_result(result: &BatchCheckResult) -> String {
589 if result.all_ok() {
590 format!("All {} declarations OK", result.total())
591 } else {
592 let mut s = format!(
593 "{}/{} declarations OK. Failures:\n",
594 result.num_ok,
595 result.total()
596 );
597 for msg in result.error_messages() {
598 s.push_str(" - ");
599 s.push_str(&msg);
600 s.push('\n');
601 }
602 s
603 }
604}
605pub fn verify_environment_integrity(env: &Environment) -> Vec<String> {
610 let mut issues = Vec::new();
611 for name in env
612 .constant_names()
613 .cloned()
614 .collect::<Vec<_>>()
615 .into_iter()
616 {
617 if let Some(info) = env.get(&name) {
618 let ty = info.ty();
619 if !is_structurally_valid_expr(ty) {
620 issues.push(format!("{}: type is not structurally valid", name));
621 }
622 }
623 }
624 issues
625}
626pub fn is_structurally_valid_expr(expr: &crate::Expr) -> bool {
631 check_expr_bvars(expr, 0)
632}
633pub(super) fn check_expr_bvars(expr: &crate::Expr, depth: u32) -> bool {
634 use crate::Expr;
635 match expr {
636 Expr::BVar(i) => *i < depth,
637 Expr::FVar(_) | Expr::Sort(_) | Expr::Const(_, _) | Expr::Lit(_) => true,
638 Expr::App(f, a) => check_expr_bvars(f, depth) && check_expr_bvars(a, depth),
639 Expr::Lam(_, _, ty, body) | Expr::Pi(_, _, ty, body) => {
640 check_expr_bvars(ty, depth) && check_expr_bvars(body, depth + 1)
641 }
642 Expr::Let(_, ty, val, body) => {
643 check_expr_bvars(ty, depth)
644 && check_expr_bvars(val, depth)
645 && check_expr_bvars(body, depth + 1)
646 }
647 Expr::Proj(_, _, e) => check_expr_bvars(e, depth),
648 }
649}
650pub fn declaration_dependencies(decl: &Declaration) -> Vec<crate::Name> {
654 use crate::collect_const_names;
655 let mut deps = collect_const_names(decl.ty());
656 if let Some(val) = decl.value() {
657 deps.extend(collect_const_names(val));
658 }
659 deps.sort_by(|a, b| format!("{}", a).cmp(&format!("{}", b)));
660 deps.dedup_by(|a, b| format!("{}", a) == format!("{}", b));
661 deps
662}
663pub fn check_dependencies_exist(env: &Environment, decl: &Declaration) -> Vec<crate::Name> {
667 let deps = declaration_dependencies(decl);
668 deps.into_iter()
669 .filter(|name| env.get(name).is_none())
670 .collect()
671}
672#[cfg(test)]
673mod tests {
674 use super::*;
675 use crate::{Level, Literal, ReducibilityHint};
676 fn mk_type0() -> Expr {
677 Expr::Sort(Level::succ(Level::zero()))
678 }
679 fn mk_nat_axiom() -> Declaration {
680 Declaration::Axiom {
681 name: Name::str("Nat"),
682 univ_params: vec![],
683 ty: mk_type0(),
684 }
685 }
686 fn mk_nat_const() -> Expr {
687 Expr::Const(Name::str("Nat"), vec![])
688 }
689 #[test]
690 fn test_check_axiom() {
691 let mut env = Environment::new();
692 let nat_ty = Expr::Sort(Level::succ(Level::zero()));
693 let axiom = Declaration::Axiom {
694 name: Name::str("Nat"),
695 univ_params: vec![],
696 ty: nat_ty,
697 };
698 check_declaration(&mut env, axiom).expect("value should be present");
699 assert!(env.contains(&Name::str("Nat")));
700 }
701 #[test]
702 fn test_check_definition() {
703 let mut env = Environment::new();
704 env.add(mk_nat_axiom()).expect("value should be present");
705 let nat_ty = mk_nat_const();
706 let val = Expr::Lit(Literal::nat(42));
707 let def = Declaration::Definition {
708 name: Name::str("answer"),
709 univ_params: vec![],
710 ty: nat_ty,
711 val,
712 hint: ReducibilityHint::Regular(1),
713 };
714 check_declaration(&mut env, def).expect("value should be present");
715 assert!(env.contains(&Name::str("answer")));
716 }
717 #[test]
718 fn test_type_mismatch() {
719 let mut env = Environment::new();
720 let type0 = mk_type0();
721 env.add(Declaration::Axiom {
722 name: Name::str("String"),
723 univ_params: vec![],
724 ty: type0.clone(),
725 })
726 .expect("value should be present");
727 env.add(Declaration::Axiom {
728 name: Name::str("Nat"),
729 univ_params: vec![],
730 ty: type0,
731 })
732 .expect("value should be present");
733 let string_ty = Expr::Const(Name::str("String"), vec![]);
734 let val = Expr::Lit(Literal::nat(42));
735 let def = Declaration::Definition {
736 name: Name::str("bad"),
737 univ_params: vec![],
738 ty: string_ty,
739 val,
740 hint: ReducibilityHint::Regular(1),
741 };
742 let result = check_declaration(&mut env, def);
743 assert!(result.is_err());
744 }
745 #[test]
746 fn test_check_declarations_sequence() {
747 let mut env = Environment::new();
748 let decls = vec![
749 Declaration::Axiom {
750 name: Name::str("A"),
751 univ_params: vec![],
752 ty: mk_type0(),
753 },
754 Declaration::Axiom {
755 name: Name::str("B"),
756 univ_params: vec![],
757 ty: mk_type0(),
758 },
759 ];
760 check_declarations(&mut env, decls).expect("value should be present");
761 assert!(env.contains(&Name::str("A")));
762 assert!(env.contains(&Name::str("B")));
763 }
764 #[test]
765 fn test_decl_kind_display() {
766 assert_eq!(DeclKind::Axiom.as_str(), "axiom");
767 assert_eq!(DeclKind::Definition.as_str(), "def");
768 assert_eq!(DeclKind::Theorem.as_str(), "theorem");
769 assert_eq!(DeclKind::Opaque.as_str(), "opaque");
770 assert!(DeclKind::Theorem.is_proven());
771 assert!(DeclKind::Axiom.is_assumed());
772 assert!(DeclKind::Definition.is_definition());
773 assert!(!DeclKind::Axiom.is_definition());
774 }
775 #[test]
776 fn test_summarize_declaration() {
777 let decl = mk_nat_axiom();
778 let summary = summarize_declaration(&decl);
779 assert_eq!(summary.name, "Nat");
780 assert_eq!(summary.kind, DeclKind::Axiom);
781 assert!(!summary.has_body);
782 assert_eq!(summary.num_univ_params, 0);
783 }
784 #[test]
785 fn test_is_valid_decl_name() {
786 assert!(is_valid_decl_name("Nat"));
787 assert!(is_valid_decl_name("Nat.succ"));
788 assert!(is_valid_decl_name("foo_bar"));
789 assert!(!is_valid_decl_name(""));
790 assert!(!is_valid_decl_name(".leading_dot"));
791 assert!(!is_valid_decl_name("trailing_dot."));
792 }
793 #[test]
794 fn test_has_unique_univ_params() {
795 let u = Name::str("u");
796 let v = Name::str("v");
797 let params = vec![u.clone(), v.clone()];
798 assert!(has_unique_univ_params(¶ms));
799 let dup_params = vec![u.clone(), u.clone()];
800 assert!(!has_unique_univ_params(&dup_params));
801 }
802 #[test]
803 fn test_check_config_default() {
804 let cfg = CheckConfig::default();
805 assert!(cfg.check_type_is_sort);
806 assert!(cfg.check_value_type);
807 assert!(cfg.check_no_free_vars);
808 }
809 #[test]
810 fn test_check_config_lenient() {
811 let cfg = CheckConfig::lenient();
812 assert!(cfg.check_type_is_sort);
813 assert!(!cfg.check_value_type);
814 }
815 #[test]
816 fn test_check_config_strict() {
817 let cfg = CheckConfig::strict();
818 assert!(!cfg.allow_axioms);
819 assert!(cfg.max_depth > 0);
820 }
821 #[test]
822 fn test_batch_check_all_ok() {
823 let mut env = Environment::new();
824 let decls = vec![
825 Declaration::Axiom {
826 name: Name::str("X"),
827 univ_params: vec![],
828 ty: mk_type0(),
829 },
830 Declaration::Axiom {
831 name: Name::str("Y"),
832 univ_params: vec![],
833 ty: mk_type0(),
834 },
835 ];
836 let result = check_declarations_collect_errors(&mut env, decls);
837 assert!(result.all_ok());
838 assert_eq!(result.num_ok, 2);
839 assert_eq!(result.num_failed, 0);
840 }
841 #[test]
842 fn test_batch_check_with_error() {
843 let mut env = Environment::new();
844 let decls = vec![
845 Declaration::Axiom {
846 name: Name::str("M"),
847 univ_params: vec![],
848 ty: mk_type0(),
849 },
850 Declaration::Definition {
851 name: Name::str("bad_def"),
852 univ_params: vec![],
853 ty: Expr::Const(Name::str("UndefinedType"), vec![]),
854 val: Expr::Lit(Literal::nat(0)),
855 hint: ReducibilityHint::Regular(1),
856 },
857 ];
858 let result = check_declarations_collect_errors(&mut env, decls);
859 assert!(!result.all_ok());
860 assert_eq!(result.num_ok, 1);
861 assert_eq!(result.num_failed, 1);
862 }
863 #[test]
864 fn test_env_builder_clean() {
865 let mut builder = EnvBuilder::new();
866 let added = builder.add(Declaration::Axiom {
867 name: Name::str("P"),
868 univ_params: vec![],
869 ty: mk_type0(),
870 });
871 assert!(added);
872 assert!(builder.is_clean());
873 assert_eq!(builder.stats().num_axioms, 1);
874 }
875 #[test]
876 fn test_env_builder_with_error() {
877 let mut builder = EnvBuilder::new();
878 let added = builder.add(Declaration::Definition {
879 name: Name::str("oops"),
880 univ_params: vec![],
881 ty: Expr::Const(Name::str("NoSuchType"), vec![]),
882 val: Expr::Lit(Literal::nat(0)),
883 hint: ReducibilityHint::Regular(1),
884 });
885 assert!(!added);
886 assert!(!builder.is_clean());
887 assert_eq!(builder.errors().len(), 1);
888 }
889 #[test]
890 fn test_well_formed_result_display() {
891 let ok = WellFormedResult::Ok;
892 assert!(ok.is_ok());
893 assert_eq!(ok.description(), "well-formed");
894 let mismatch = WellFormedResult::TypeMismatch {
895 description: "Nat vs String".to_string(),
896 };
897 assert!(!mismatch.is_ok());
898 assert!(mismatch.description().contains("Nat vs String"));
899 }
900 #[test]
901 fn test_is_structurally_valid_expr() {
902 let bad = Expr::BVar(0);
903 assert!(!is_structurally_valid_expr(&bad));
904 let sort = Expr::Sort(Level::zero());
905 assert!(is_structurally_valid_expr(&sort));
906 let lam = Expr::Lam(
907 crate::BinderInfo::Default,
908 Name::str("x"),
909 Node::new(Expr::Sort(Level::zero())),
910 Node::new(Expr::BVar(0)),
911 );
912 assert!(is_structurally_valid_expr(&lam));
913 }
914 #[test]
915 fn test_format_decl_summary() {
916 let summary = DeclSummary {
917 name: "Nat.add".to_string(),
918 kind: DeclKind::Definition,
919 has_body: true,
920 num_univ_params: 0,
921 };
922 let s = format_decl_summary(&summary);
923 assert!(s.contains("Nat.add"));
924 assert!(s.contains("def"));
925 }
926 #[test]
927 fn test_check_stats_totals() {
928 let stats = CheckStats {
929 num_axioms: 2,
930 num_definitions: 3,
931 num_theorems: 1,
932 num_opaques: 0,
933 num_failures: 1,
934 };
935 assert_eq!(stats.total(), 6);
936 assert_eq!(stats.num_ok(), 6);
937 }
938 #[test]
939 fn test_format_batch_result_all_ok() {
940 let result = BatchCheckResult {
941 num_ok: 3,
942 num_failed: 0,
943 errors: vec![],
944 };
945 let s = format_batch_result(&result);
946 assert!(s.contains("3"));
947 }
948 #[test]
949 fn test_temp_env_scope() {
950 let mut env = Environment::new();
951 {
952 let mut scope = TempEnvScope::new(&mut env);
953 let ok = scope.add(Declaration::Axiom {
954 name: Name::str("TempDecl"),
955 univ_params: vec![],
956 ty: mk_type0(),
957 });
958 assert!(ok.is_ok());
959 assert_eq!(scope.num_added(), 1);
960 }
961 assert!(env.contains(&Name::str("TempDecl")));
962 }
963 #[allow(dead_code)]
964 fn mk_type1() -> Expr {
965 Expr::Sort(Level::succ(Level::succ(Level::zero())))
966 }
967 #[test]
968 fn test_check_constant_info_axiom() {
969 use crate::declaration::{AxiomVal, ConstantVal};
970 let mut env = Environment::new();
971 let ci = ConstantInfo::Axiom(AxiomVal {
972 common: ConstantVal {
973 name: Name::str("MyAxiom"),
974 level_params: vec![],
975 ty: mk_type0(),
976 },
977 is_unsafe: false,
978 });
979 check_constant_info(&mut env, ci).expect("value should be present");
980 assert!(env.contains(&Name::str("MyAxiom")));
981 }
982 #[test]
983 fn test_check_constant_info_inductive_simple() {
984 use crate::declaration::{ConstantVal, InductiveVal};
985 let mut env = Environment::new();
986 let iv = InductiveVal {
987 common: ConstantVal {
988 name: Name::str("Bool"),
989 level_params: vec![],
990 ty: mk_type0(),
991 },
992 num_params: 0,
993 num_indices: 0,
994 all: vec![Name::str("Bool")],
995 ctors: vec![Name::str("Bool.true"), Name::str("Bool.false")],
996 num_nested: 0,
997 is_rec: false,
998 is_unsafe: false,
999 is_reflexive: false,
1000 is_prop: false,
1001 };
1002 let ci = ConstantInfo::Inductive(iv);
1003 check_constant_info(&mut env, ci).expect("value should be present");
1004 assert!(env.contains(&Name::str("Bool")));
1005 }
1006 #[test]
1007 fn test_check_constant_info_inductive_empty() {
1008 use crate::declaration::{ConstantVal, InductiveVal};
1009 let mut env = Environment::new();
1010 let iv = InductiveVal {
1011 common: ConstantVal {
1012 name: Name::str("Empty"),
1013 level_params: vec![],
1014 ty: mk_type0(),
1015 },
1016 num_params: 0,
1017 num_indices: 0,
1018 all: vec![Name::str("Empty")],
1019 ctors: vec![],
1020 num_nested: 0,
1021 is_rec: false,
1022 is_unsafe: false,
1023 is_reflexive: false,
1024 is_prop: false,
1025 };
1026 let ci = ConstantInfo::Inductive(iv);
1027 check_constant_info(&mut env, ci).expect("value should be present");
1028 assert!(env.contains(&Name::str("Empty")));
1029 }
1030 #[test]
1031 fn test_check_constant_info_constructor_valid() {
1032 use crate::declaration::{ConstantVal, ConstructorVal, InductiveVal};
1033 let mut env = Environment::new();
1034 let ind_ci = ConstantInfo::Inductive(InductiveVal {
1035 common: ConstantVal {
1036 name: Name::str("Bool"),
1037 level_params: vec![],
1038 ty: mk_type0(),
1039 },
1040 num_params: 0,
1041 num_indices: 0,
1042 all: vec![Name::str("Bool")],
1043 ctors: vec![Name::str("Bool.true")],
1044 num_nested: 0,
1045 is_rec: false,
1046 is_unsafe: false,
1047 is_reflexive: false,
1048 is_prop: false,
1049 });
1050 check_constant_info(&mut env, ind_ci).expect("value should be present");
1051 let ctor_ci = ConstantInfo::Constructor(ConstructorVal {
1052 common: ConstantVal {
1053 name: Name::str("Bool.true"),
1054 level_params: vec![],
1055 ty: Expr::Const(Name::str("Bool"), vec![]),
1056 },
1057 induct: Name::str("Bool"),
1058 cidx: 0,
1059 num_params: 0,
1060 num_fields: 0,
1061 is_unsafe: false,
1062 });
1063 check_constant_info(&mut env, ctor_ci).expect("value should be present");
1064 assert!(env.contains(&Name::str("Bool.true")));
1065 }
1066 #[test]
1067 fn test_check_constant_info_constructor_wrong_return_type() {
1068 use crate::declaration::{ConstantVal, ConstructorVal, InductiveVal};
1069 let mut env = Environment::new();
1070 env.add_constant(ConstantInfo::Inductive(InductiveVal {
1071 common: ConstantVal {
1072 name: Name::str("Bool"),
1073 level_params: vec![],
1074 ty: mk_type0(),
1075 },
1076 num_params: 0,
1077 num_indices: 0,
1078 all: vec![Name::str("Bool")],
1079 ctors: vec![Name::str("Bool.bad")],
1080 num_nested: 0,
1081 is_rec: false,
1082 is_unsafe: false,
1083 is_reflexive: false,
1084 is_prop: false,
1085 }))
1086 .expect("value should be present");
1087 env.add_constant(ConstantInfo::Inductive(InductiveVal {
1088 common: ConstantVal {
1089 name: Name::str("Nat"),
1090 level_params: vec![],
1091 ty: mk_type0(),
1092 },
1093 num_params: 0,
1094 num_indices: 0,
1095 all: vec![Name::str("Nat")],
1096 ctors: vec![],
1097 num_nested: 0,
1098 is_rec: false,
1099 is_unsafe: false,
1100 is_reflexive: false,
1101 is_prop: false,
1102 }))
1103 .expect("value should be present");
1104 let ctor_ci = ConstantInfo::Constructor(ConstructorVal {
1105 common: ConstantVal {
1106 name: Name::str("Bool.bad"),
1107 level_params: vec![],
1108 ty: Expr::Const(Name::str("Nat"), vec![]),
1109 },
1110 induct: Name::str("Bool"),
1111 cidx: 0,
1112 num_params: 0,
1113 num_fields: 0,
1114 is_unsafe: false,
1115 });
1116 let result = check_constant_info(&mut env, ctor_ci);
1117 assert!(result.is_err(), "expected error for wrong return type");
1118 }
1119 #[test]
1120 fn test_check_constant_info_recursor_rejects_bogus() {
1121 use crate::declaration::{ConstantVal, InductiveVal, RecursorVal};
1125 let mut env = Environment::new();
1126 env.add_constant(ConstantInfo::Inductive(InductiveVal {
1127 common: ConstantVal {
1128 name: Name::str("Bool"),
1129 level_params: vec![],
1130 ty: mk_type0(),
1131 },
1132 num_params: 0,
1133 num_indices: 0,
1134 all: vec![Name::str("Bool")],
1135 ctors: vec![],
1136 num_nested: 0,
1137 is_rec: false,
1138 is_unsafe: false,
1139 is_reflexive: false,
1140 is_prop: false,
1141 }))
1142 .expect("value should be present");
1143 let rec_ty = mk_type0();
1144 let rec_ci = ConstantInfo::Recursor(RecursorVal {
1145 common: ConstantVal {
1146 name: Name::str("Bool.rec"),
1147 level_params: vec![],
1148 ty: rec_ty,
1149 },
1150 all: vec![Name::str("Bool")],
1151 num_params: 0,
1152 num_indices: 0,
1153 num_motives: 1,
1154 num_minors: 2,
1155 rules: vec![],
1156 k: false,
1157 is_unsafe: false,
1158 });
1159 let result = check_constant_info(&mut env, rec_ci);
1160 assert!(
1161 result.is_err(),
1162 "hand-supplied bogus recursor must be rejected"
1163 );
1164 assert!(!env.contains(&Name::str("Bool.rec")));
1165 }
1166 #[test]
1167 fn test_check_constant_info_quotient_rejects_noncanonical() {
1168 use crate::declaration::{ConstantVal, QuotKind, QuotVal};
1173 let mut env = Environment::new();
1174 let quot_ci = ConstantInfo::Quotient(QuotVal {
1175 common: ConstantVal {
1176 name: Name::str("Quot"),
1177 level_params: vec![],
1178 ty: mk_type0(),
1179 },
1180 kind: QuotKind::Type,
1181 });
1182 assert!(
1183 check_constant_info(&mut env, quot_ci).is_err(),
1184 "non-canonical quotient type must be rejected"
1185 );
1186 assert!(!env.contains(&Name::str("Quot")));
1187 }
1188 #[test]
1189 fn test_check_constant_infos_sequence() {
1190 use crate::declaration::{AxiomVal, ConstantVal};
1191 let mut env = Environment::new();
1192 let cis = vec![
1193 ConstantInfo::Axiom(AxiomVal {
1194 common: ConstantVal {
1195 name: Name::str("P"),
1196 level_params: vec![],
1197 ty: mk_type0(),
1198 },
1199 is_unsafe: false,
1200 }),
1201 ConstantInfo::Axiom(AxiomVal {
1202 common: ConstantVal {
1203 name: Name::str("Q"),
1204 level_params: vec![],
1205 ty: mk_type0(),
1206 },
1207 is_unsafe: false,
1208 }),
1209 ];
1210 check_constant_infos(&mut env, cis).expect("value should be present");
1211 assert!(env.contains(&Name::str("P")));
1212 assert!(env.contains(&Name::str("Q")));
1213 }
1214 #[test]
1215 fn test_check_constant_info_duplicate() {
1216 use crate::declaration::{AxiomVal, ConstantVal};
1217 let mut env = Environment::new();
1218 let make_ci = || {
1219 ConstantInfo::Axiom(AxiomVal {
1220 common: ConstantVal {
1221 name: Name::str("Dup"),
1222 level_params: vec![],
1223 ty: mk_type0(),
1224 },
1225 is_unsafe: false,
1226 })
1227 };
1228 check_constant_info(&mut env, make_ci()).expect("value should be present");
1229 let result = check_constant_info(&mut env, make_ci());
1230 assert!(result.is_err(), "duplicate should fail");
1231 }
1232}
1233#[cfg(test)]
1234mod tests_padding_infra {
1235 use super::*;
1236 #[test]
1237 fn test_stat_summary() {
1238 let mut ss = StatSummary::new();
1239 ss.record(10.0);
1240 ss.record(20.0);
1241 ss.record(30.0);
1242 assert_eq!(ss.count(), 3);
1243 assert!((ss.mean().expect("mean should succeed") - 20.0).abs() < 1e-9);
1244 assert_eq!(ss.min().expect("min should succeed") as i64, 10);
1245 assert_eq!(ss.max().expect("max should succeed") as i64, 30);
1246 }
1247 #[test]
1248 fn test_transform_stat() {
1249 let mut ts = TransformStat::new();
1250 ts.record_before(100.0);
1251 ts.record_after(80.0);
1252 let ratio = ts.mean_ratio().expect("ratio should be present");
1253 assert!((ratio - 0.8).abs() < 1e-9);
1254 }
1255 #[test]
1256 fn test_small_map() {
1257 let mut m: SmallMap<u32, &str> = SmallMap::new();
1258 m.insert(3, "three");
1259 m.insert(1, "one");
1260 m.insert(2, "two");
1261 assert_eq!(m.get(&2), Some(&"two"));
1262 assert_eq!(m.len(), 3);
1263 let keys = m.keys();
1264 assert_eq!(*keys[0], 1);
1265 assert_eq!(*keys[2], 3);
1266 }
1267 #[test]
1268 fn test_label_set() {
1269 let mut ls = LabelSet::new();
1270 ls.add("foo");
1271 ls.add("bar");
1272 ls.add("foo");
1273 assert_eq!(ls.count(), 2);
1274 assert!(ls.has("bar"));
1275 assert!(!ls.has("baz"));
1276 }
1277 #[test]
1278 fn test_config_node() {
1279 let mut root = ConfigNode::section("root");
1280 let child = ConfigNode::leaf("key", "value");
1281 root.add_child(child);
1282 assert_eq!(root.num_children(), 1);
1283 }
1284 #[test]
1285 fn test_versioned_record() {
1286 let mut vr = VersionedRecord::new(0u32);
1287 vr.update(1);
1288 vr.update(2);
1289 assert_eq!(*vr.current(), 2);
1290 assert_eq!(vr.version(), 2);
1291 assert!(vr.has_history());
1292 assert_eq!(*vr.at_version(0).expect("value should be present"), 0);
1293 }
1294 #[test]
1295 fn test_simple_dag() {
1296 let mut dag = SimpleDag::new(4);
1297 dag.add_edge(0, 1);
1298 dag.add_edge(1, 2);
1299 dag.add_edge(2, 3);
1300 assert!(dag.can_reach(0, 3));
1301 assert!(!dag.can_reach(3, 0));
1302 let order = dag.topological_sort().expect("order should be present");
1303 assert_eq!(order, vec![0, 1, 2, 3]);
1304 }
1305 #[test]
1306 fn test_focus_stack() {
1307 let mut fs: FocusStack<&str> = FocusStack::new();
1308 fs.focus("a");
1309 fs.focus("b");
1310 assert_eq!(fs.current(), Some(&"b"));
1311 assert_eq!(fs.depth(), 2);
1312 fs.blur();
1313 assert_eq!(fs.current(), Some(&"a"));
1314 }
1315}
1316#[cfg(test)]
1317mod tests_extra_iterators {
1318 use super::*;
1319 #[test]
1320 fn test_window_iterator() {
1321 let data = vec![1u32, 2, 3, 4, 5];
1322 let windows: Vec<_> = WindowIterator::new(&data, 3).collect();
1323 assert_eq!(windows.len(), 3);
1324 assert_eq!(windows[0], &[1, 2, 3]);
1325 assert_eq!(windows[2], &[3, 4, 5]);
1326 }
1327 #[test]
1328 fn test_non_empty_vec() {
1329 let mut nev = NonEmptyVec::singleton(10u32);
1330 nev.push(20);
1331 nev.push(30);
1332 assert_eq!(nev.len(), 3);
1333 assert_eq!(*nev.first(), 10);
1334 assert_eq!(*nev.last(), 30);
1335 }
1336}
1337#[cfg(test)]
1338mod tests_padding2 {
1339 use super::*;
1340 #[test]
1341 fn test_sliding_sum() {
1342 let mut ss = SlidingSum::new(3);
1343 ss.push(1.0);
1344 ss.push(2.0);
1345 ss.push(3.0);
1346 assert!((ss.sum() - 6.0).abs() < 1e-9);
1347 ss.push(4.0);
1348 assert!((ss.sum() - 9.0).abs() < 1e-9);
1349 assert_eq!(ss.count(), 3);
1350 }
1351 #[test]
1352 fn test_path_buf() {
1353 let mut pb = PathBuf::new();
1354 pb.push("src");
1355 pb.push("main");
1356 assert_eq!(pb.as_str(), "src/main");
1357 assert_eq!(pb.depth(), 2);
1358 pb.pop();
1359 assert_eq!(pb.as_str(), "src");
1360 }
1361 #[test]
1362 fn test_string_pool() {
1363 let mut pool = StringPool::new();
1364 let s = pool.take();
1365 assert!(s.is_empty());
1366 pool.give("hello".to_string());
1367 let s2 = pool.take();
1368 assert!(s2.is_empty());
1369 assert_eq!(pool.free_count(), 0);
1370 }
1371 #[test]
1372 fn test_transitive_closure() {
1373 let mut tc = TransitiveClosure::new(4);
1374 tc.add_edge(0, 1);
1375 tc.add_edge(1, 2);
1376 tc.add_edge(2, 3);
1377 assert!(tc.can_reach(0, 3));
1378 assert!(!tc.can_reach(3, 0));
1379 let r = tc.reachable_from(0);
1380 assert_eq!(r.len(), 4);
1381 }
1382 #[test]
1383 fn test_token_bucket() {
1384 let mut tb = TokenBucket::new(100, 0);
1385 assert_eq!(tb.available(), 100);
1386 assert!(tb.try_consume(50));
1387 assert_eq!(tb.available(), 50);
1388 assert!(!tb.try_consume(60));
1389 assert_eq!(tb.capacity(), 100);
1390 }
1391 #[test]
1392 fn test_rewrite_rule_set() {
1393 let mut rrs = RewriteRuleSet::new();
1394 rrs.add(RewriteRule::unconditional(
1395 "beta",
1396 "App(Lam(x, b), v)",
1397 "b[x:=v]",
1398 ));
1399 rrs.add(RewriteRule::conditional("comm", "a + b", "b + a"));
1400 assert_eq!(rrs.len(), 2);
1401 assert_eq!(rrs.unconditional_rules().len(), 1);
1402 assert_eq!(rrs.conditional_rules().len(), 1);
1403 assert!(rrs.get("beta").is_some());
1404 let disp = rrs
1405 .get("beta")
1406 .expect("element at \'beta\' should exist")
1407 .display();
1408 assert!(disp.contains("→"));
1409 }
1410}
1411#[cfg(test)]
1412mod tests_padding3 {
1413 use super::*;
1414 #[test]
1415 fn test_decision_node() {
1416 let tree = DecisionNode::Branch {
1417 key: "x".into(),
1418 val: "1".into(),
1419 yes_branch: Box::new(DecisionNode::Leaf("yes".into())),
1420 no_branch: Box::new(DecisionNode::Leaf("no".into())),
1421 };
1422 let mut ctx = std::collections::HashMap::new();
1423 ctx.insert("x".into(), "1".into());
1424 assert_eq!(tree.evaluate(&ctx), "yes");
1425 ctx.insert("x".into(), "2".into());
1426 assert_eq!(tree.evaluate(&ctx), "no");
1427 assert_eq!(tree.depth(), 1);
1428 }
1429 #[test]
1430 fn test_flat_substitution() {
1431 let mut sub = FlatSubstitution::new();
1432 sub.add("foo", "bar");
1433 sub.add("baz", "qux");
1434 assert_eq!(sub.apply("foo and baz"), "bar and qux");
1435 assert_eq!(sub.len(), 2);
1436 }
1437 #[test]
1438 fn test_stopwatch() {
1439 let mut sw = Stopwatch::start();
1440 sw.split();
1441 sw.split();
1442 assert_eq!(sw.num_splits(), 2);
1443 assert!(sw.elapsed_ms() >= 0.0);
1444 for &s in sw.splits() {
1445 assert!(s >= 0.0);
1446 }
1447 }
1448 #[test]
1449 fn test_either2() {
1450 let e: Either2<i32, &str> = Either2::First(42);
1451 assert!(e.is_first());
1452 let mapped = e.map_first(|x| x * 2);
1453 assert_eq!(mapped.first(), Some(84));
1454 let e2: Either2<i32, &str> = Either2::Second("hello");
1455 assert!(e2.is_second());
1456 assert_eq!(e2.second(), Some("hello"));
1457 }
1458 #[test]
1459 fn test_write_once() {
1460 let wo: WriteOnce<u32> = WriteOnce::new();
1461 assert!(!wo.is_written());
1462 assert!(wo.write(42));
1463 assert!(!wo.write(99));
1464 assert_eq!(wo.read(), Some(42));
1465 }
1466 #[test]
1467 fn test_sparse_vec() {
1468 let mut sv: SparseVec<i32> = SparseVec::new(100);
1469 sv.set(5, 10);
1470 sv.set(50, 20);
1471 assert_eq!(*sv.get(5), 10);
1472 assert_eq!(*sv.get(50), 20);
1473 assert_eq!(*sv.get(0), 0);
1474 assert_eq!(sv.nnz(), 2);
1475 sv.set(5, 0);
1476 assert_eq!(sv.nnz(), 1);
1477 }
1478 #[test]
1479 fn test_stack_calc() {
1480 let mut calc = StackCalc::new();
1481 calc.push(3);
1482 calc.push(4);
1483 assert_eq!(calc.add(), Some(7));
1484 assert_eq!(calc.peek(), Some(7));
1485 calc.push(2);
1486 assert_eq!(calc.mul(), Some(14));
1487 assert_eq!(calc.peek(), Some(14));
1488 }
1489}