1use crate::declaration::{ConstructorVal, InductiveVal};
6use crate::def_eq::DefEqChecker;
7use crate::error::KernelError;
8use crate::expr_util::{get_app_args, get_app_fn, has_any_fvar, mk_app};
9use crate::instantiate::instantiate_type_lparams;
10use crate::reduce::{Reducer, TransparencyMode};
11use crate::subst::{abstract_expr, instantiate};
12use crate::{BinderInfo, Environment, Expr, FVarId, Level, Literal, Name};
13
14use std::collections::HashMap;
15
16#[allow(dead_code)]
18pub struct SimpleDag {
19 edges: Vec<Vec<usize>>,
21}
22#[allow(dead_code)]
23impl SimpleDag {
24 pub fn new(n: usize) -> Self {
26 Self {
27 edges: vec![Vec::new(); n],
28 }
29 }
30 pub fn add_edge(&mut self, from: usize, to: usize) {
32 if from < self.edges.len() {
33 self.edges[from].push(to);
34 }
35 }
36 pub fn successors(&self, node: usize) -> &[usize] {
38 self.edges.get(node).map(|v| v.as_slice()).unwrap_or(&[])
39 }
40 pub fn can_reach(&self, from: usize, to: usize) -> bool {
42 let mut visited = vec![false; self.edges.len()];
43 self.dfs(from, to, &mut visited)
44 }
45 fn dfs(&self, cur: usize, target: usize, visited: &mut Vec<bool>) -> bool {
46 if cur == target {
47 return true;
48 }
49 if cur >= visited.len() || visited[cur] {
50 return false;
51 }
52 visited[cur] = true;
53 for &next in self.successors(cur) {
54 if self.dfs(next, target, visited) {
55 return true;
56 }
57 }
58 false
59 }
60 pub fn topological_sort(&self) -> Option<Vec<usize>> {
62 let n = self.edges.len();
63 let mut in_degree = vec![0usize; n];
64 for succs in &self.edges {
65 for &s in succs {
66 if s < n {
67 in_degree[s] += 1;
68 }
69 }
70 }
71 let mut queue: std::collections::VecDeque<usize> =
72 (0..n).filter(|&i| in_degree[i] == 0).collect();
73 let mut order = Vec::new();
74 while let Some(node) = queue.pop_front() {
75 order.push(node);
76 for &s in self.successors(node) {
77 if s < n {
78 in_degree[s] -= 1;
79 if in_degree[s] == 0 {
80 queue.push_back(s);
81 }
82 }
83 }
84 }
85 if order.len() == n {
86 Some(order)
87 } else {
88 None
89 }
90 }
91 pub fn num_nodes(&self) -> usize {
93 self.edges.len()
94 }
95}
96#[allow(dead_code)]
98pub struct TransitiveClosure {
99 adj: Vec<Vec<usize>>,
100 n: usize,
101}
102#[allow(dead_code)]
103impl TransitiveClosure {
104 pub fn new(n: usize) -> Self {
106 Self {
107 adj: vec![Vec::new(); n],
108 n,
109 }
110 }
111 pub fn add_edge(&mut self, from: usize, to: usize) {
113 if from < self.n {
114 self.adj[from].push(to);
115 }
116 }
117 pub fn reachable_from(&self, start: usize) -> Vec<usize> {
119 let mut visited = vec![false; self.n];
120 let mut queue = std::collections::VecDeque::new();
121 queue.push_back(start);
122 while let Some(node) = queue.pop_front() {
123 if node >= self.n || visited[node] {
124 continue;
125 }
126 visited[node] = true;
127 for &next in &self.adj[node] {
128 queue.push_back(next);
129 }
130 }
131 (0..self.n).filter(|&i| visited[i]).collect()
132 }
133 pub fn can_reach(&self, from: usize, to: usize) -> bool {
135 self.reachable_from(from).contains(&to)
136 }
137}
138#[allow(dead_code)]
140pub struct ConfigNode {
141 key: String,
142 value: Option<String>,
143 children: Vec<ConfigNode>,
144}
145#[allow(dead_code)]
146impl ConfigNode {
147 pub fn leaf(key: impl Into<String>, value: impl Into<String>) -> Self {
149 Self {
150 key: key.into(),
151 value: Some(value.into()),
152 children: Vec::new(),
153 }
154 }
155 pub fn section(key: impl Into<String>) -> Self {
157 Self {
158 key: key.into(),
159 value: None,
160 children: Vec::new(),
161 }
162 }
163 pub fn add_child(&mut self, child: ConfigNode) {
165 self.children.push(child);
166 }
167 pub fn key(&self) -> &str {
169 &self.key
170 }
171 pub fn value(&self) -> Option<&str> {
173 self.value.as_deref()
174 }
175 pub fn num_children(&self) -> usize {
177 self.children.len()
178 }
179 pub fn lookup(&self, path: &str) -> Option<&str> {
181 let mut parts = path.splitn(2, '.');
182 let head = parts.next()?;
183 let tail = parts.next();
184 if head != self.key {
185 return None;
186 }
187 match tail {
188 None => self.value.as_deref(),
189 Some(rest) => self.children.iter().find_map(|c| c.lookup_relative(rest)),
190 }
191 }
192 fn lookup_relative(&self, path: &str) -> Option<&str> {
193 let mut parts = path.splitn(2, '.');
194 let head = parts.next()?;
195 let tail = parts.next();
196 if head != self.key {
197 return None;
198 }
199 match tail {
200 None => self.value.as_deref(),
201 Some(rest) => self.children.iter().find_map(|c| c.lookup_relative(rest)),
202 }
203 }
204}
205#[allow(dead_code)]
207#[derive(Debug, Clone)]
208pub struct TypingJudgment {
209 pub expr: Expr,
211 pub ty: Expr,
213 pub success: bool,
215}
216impl TypingJudgment {
217 #[allow(dead_code)]
219 pub fn ok(expr: Expr, ty: Expr) -> Self {
220 Self {
221 expr,
222 ty,
223 success: true,
224 }
225 }
226 #[allow(dead_code)]
228 pub fn fail(expr: Expr) -> Self {
229 Self {
230 ty: Expr::Sort(Level::zero()),
231 expr,
232 success: false,
233 }
234 }
235 #[allow(dead_code)]
237 pub fn is_ok(&self) -> bool {
238 self.success
239 }
240}
241#[allow(dead_code)]
243pub struct SparseVec<T: Default + Clone + PartialEq> {
244 entries: std::collections::HashMap<usize, T>,
245 default_: T,
246 logical_len: usize,
247}
248#[allow(dead_code)]
249impl<T: Default + Clone + PartialEq> SparseVec<T> {
250 pub fn new(len: usize) -> Self {
252 Self {
253 entries: std::collections::HashMap::new(),
254 default_: T::default(),
255 logical_len: len,
256 }
257 }
258 pub fn set(&mut self, idx: usize, val: T) {
260 if val == self.default_ {
261 self.entries.remove(&idx);
262 } else {
263 self.entries.insert(idx, val);
264 }
265 }
266 pub fn get(&self, idx: usize) -> &T {
268 self.entries.get(&idx).unwrap_or(&self.default_)
269 }
270 pub fn len(&self) -> usize {
272 self.logical_len
273 }
274 pub fn is_empty(&self) -> bool {
276 self.len() == 0
277 }
278 pub fn nnz(&self) -> usize {
280 self.entries.len()
281 }
282}
283#[allow(dead_code)]
285pub struct StackCalc {
286 stack: Vec<i64>,
287}
288#[allow(dead_code)]
289impl StackCalc {
290 pub fn new() -> Self {
292 Self { stack: Vec::new() }
293 }
294 pub fn push(&mut self, n: i64) {
296 self.stack.push(n);
297 }
298 pub fn add(&mut self) {
300 let b = self
301 .stack
302 .pop()
303 .expect("stack must have at least two values for add");
304 let a = self
305 .stack
306 .pop()
307 .expect("stack must have at least two values for add");
308 self.stack.push(a + b);
309 }
310 pub fn sub(&mut self) {
312 let b = self
313 .stack
314 .pop()
315 .expect("stack must have at least two values for sub");
316 let a = self
317 .stack
318 .pop()
319 .expect("stack must have at least two values for sub");
320 self.stack.push(a - b);
321 }
322 pub fn mul(&mut self) {
324 let b = self
325 .stack
326 .pop()
327 .expect("stack must have at least two values for mul");
328 let a = self
329 .stack
330 .pop()
331 .expect("stack must have at least two values for mul");
332 self.stack.push(a * b);
333 }
334 pub fn peek(&self) -> Option<i64> {
336 self.stack.last().copied()
337 }
338 pub fn depth(&self) -> usize {
340 self.stack.len()
341 }
342}
343#[allow(dead_code)]
345pub struct WindowIterator<'a, T> {
346 pub(super) data: &'a [T],
347 pub(super) pos: usize,
348 pub(super) window: usize,
349}
350#[allow(dead_code)]
351impl<'a, T> WindowIterator<'a, T> {
352 pub fn new(data: &'a [T], window: usize) -> Self {
354 Self {
355 data,
356 pos: 0,
357 window,
358 }
359 }
360}
361#[allow(dead_code)]
363#[derive(Debug, Clone)]
364pub struct InferCacheEntry {
365 pub expr: Expr,
367 pub ty: Expr,
369}
370#[allow(dead_code)]
372#[allow(missing_docs)]
373pub struct RewriteRule {
374 pub name: String,
376 pub lhs: String,
378 pub rhs: String,
380 pub conditional: bool,
382}
383#[allow(dead_code)]
384impl RewriteRule {
385 pub fn unconditional(
387 name: impl Into<String>,
388 lhs: impl Into<String>,
389 rhs: impl Into<String>,
390 ) -> Self {
391 Self {
392 name: name.into(),
393 lhs: lhs.into(),
394 rhs: rhs.into(),
395 conditional: false,
396 }
397 }
398 pub fn conditional(
400 name: impl Into<String>,
401 lhs: impl Into<String>,
402 rhs: impl Into<String>,
403 ) -> Self {
404 Self {
405 name: name.into(),
406 lhs: lhs.into(),
407 rhs: rhs.into(),
408 conditional: true,
409 }
410 }
411 pub fn display(&self) -> String {
413 format!("{}: {} → {}", self.name, self.lhs, self.rhs)
414 }
415}
416#[allow(dead_code)]
418pub struct RewriteRuleSet {
419 rules: Vec<RewriteRule>,
420}
421#[allow(dead_code)]
422impl RewriteRuleSet {
423 pub fn new() -> Self {
425 Self { rules: Vec::new() }
426 }
427 pub fn add(&mut self, rule: RewriteRule) {
429 self.rules.push(rule);
430 }
431 pub fn len(&self) -> usize {
433 self.rules.len()
434 }
435 pub fn is_empty(&self) -> bool {
437 self.rules.is_empty()
438 }
439 pub fn conditional_rules(&self) -> Vec<&RewriteRule> {
441 self.rules.iter().filter(|r| r.conditional).collect()
442 }
443 pub fn unconditional_rules(&self) -> Vec<&RewriteRule> {
445 self.rules.iter().filter(|r| !r.conditional).collect()
446 }
447 pub fn get(&self, name: &str) -> Option<&RewriteRule> {
449 self.rules.iter().find(|r| r.name == name)
450 }
451}
452#[allow(dead_code)]
454pub struct Stopwatch {
455 start: std::time::Instant,
456 splits: Vec<f64>,
457}
458#[allow(dead_code)]
459impl Stopwatch {
460 pub fn start() -> Self {
462 Self {
463 start: std::time::Instant::now(),
464 splits: Vec::new(),
465 }
466 }
467 pub fn split(&mut self) {
469 self.splits.push(self.elapsed_ms());
470 }
471 pub fn elapsed_ms(&self) -> f64 {
473 self.start.elapsed().as_secs_f64() * 1000.0
474 }
475 pub fn splits(&self) -> &[f64] {
477 &self.splits
478 }
479 pub fn num_splits(&self) -> usize {
481 self.splits.len()
482 }
483}
484#[derive(Clone, Debug, Default)]
486pub struct InferStats {
487 pub infer_calls: usize,
489 pub whnf_calls: usize,
491 pub def_eq_calls: usize,
493 pub const_lookups: usize,
495 pub cache_hits: usize,
497}
498impl InferStats {
499 pub fn reset(&mut self) {
501 *self = InferStats::default();
502 }
503 pub fn total_ops(&self) -> usize {
505 self.infer_calls + self.whnf_calls + self.def_eq_calls
506 }
507}
508#[allow(dead_code)]
510pub struct FocusStack<T> {
511 items: Vec<T>,
512}
513#[allow(dead_code)]
514impl<T> FocusStack<T> {
515 pub fn new() -> Self {
517 Self { items: Vec::new() }
518 }
519 pub fn focus(&mut self, item: T) {
521 self.items.push(item);
522 }
523 pub fn blur(&mut self) -> Option<T> {
525 self.items.pop()
526 }
527 pub fn current(&self) -> Option<&T> {
529 self.items.last()
530 }
531 pub fn depth(&self) -> usize {
533 self.items.len()
534 }
535 pub fn is_empty(&self) -> bool {
537 self.items.is_empty()
538 }
539}
540#[allow(dead_code)]
542pub struct NonEmptyVec<T> {
543 head: T,
544 tail: Vec<T>,
545}
546#[allow(dead_code)]
547impl<T> NonEmptyVec<T> {
548 pub fn singleton(val: T) -> Self {
550 Self {
551 head: val,
552 tail: Vec::new(),
553 }
554 }
555 pub fn push(&mut self, val: T) {
557 self.tail.push(val);
558 }
559 pub fn first(&self) -> &T {
561 &self.head
562 }
563 pub fn last(&self) -> &T {
565 self.tail.last().unwrap_or(&self.head)
566 }
567 pub fn len(&self) -> usize {
569 1 + self.tail.len()
570 }
571 pub fn is_empty(&self) -> bool {
573 self.len() == 0
574 }
575 pub fn to_vec(&self) -> Vec<&T> {
577 let mut v = vec![&self.head];
578 v.extend(self.tail.iter());
579 v
580 }
581}
582#[allow(dead_code)]
584pub struct TransformStat {
585 before: StatSummary,
586 after: StatSummary,
587}
588#[allow(dead_code)]
589impl TransformStat {
590 pub fn new() -> Self {
592 Self {
593 before: StatSummary::new(),
594 after: StatSummary::new(),
595 }
596 }
597 pub fn record_before(&mut self, v: f64) {
599 self.before.record(v);
600 }
601 pub fn record_after(&mut self, v: f64) {
603 self.after.record(v);
604 }
605 pub fn mean_ratio(&self) -> Option<f64> {
607 let b = self.before.mean()?;
608 let a = self.after.mean()?;
609 if b.abs() < f64::EPSILON {
610 return None;
611 }
612 Some(a / b)
613 }
614}
615#[allow(dead_code)]
617pub struct TokenBucket {
618 capacity: u64,
619 tokens: u64,
620 refill_per_ms: u64,
621 last_refill: std::time::Instant,
622}
623#[allow(dead_code)]
624impl TokenBucket {
625 pub fn new(capacity: u64, refill_per_ms: u64) -> Self {
627 Self {
628 capacity,
629 tokens: capacity,
630 refill_per_ms,
631 last_refill: std::time::Instant::now(),
632 }
633 }
634 pub fn try_consume(&mut self, n: u64) -> bool {
636 self.refill();
637 if self.tokens >= n {
638 self.tokens -= n;
639 true
640 } else {
641 false
642 }
643 }
644 fn refill(&mut self) {
645 let now = std::time::Instant::now();
646 let elapsed_ms = now.duration_since(self.last_refill).as_millis() as u64;
647 if elapsed_ms > 0 {
648 let new_tokens = elapsed_ms * self.refill_per_ms;
649 self.tokens = (self.tokens + new_tokens).min(self.capacity);
650 self.last_refill = now;
651 }
652 }
653 pub fn available(&self) -> u64 {
655 self.tokens
656 }
657 pub fn capacity(&self) -> u64 {
659 self.capacity
660 }
661}
662#[allow(dead_code)]
664pub struct FlatSubstitution {
665 pairs: Vec<(String, String)>,
666}
667#[allow(dead_code)]
668impl FlatSubstitution {
669 pub fn new() -> Self {
671 Self { pairs: Vec::new() }
672 }
673 pub fn add(&mut self, from: impl Into<String>, to: impl Into<String>) {
675 self.pairs.push((from.into(), to.into()));
676 }
677 pub fn apply(&self, s: &str) -> String {
679 let mut result = s.to_string();
680 for (from, to) in &self.pairs {
681 result = result.replace(from.as_str(), to.as_str());
682 }
683 result
684 }
685 pub fn len(&self) -> usize {
687 self.pairs.len()
688 }
689 pub fn is_empty(&self) -> bool {
691 self.pairs.is_empty()
692 }
693}
694#[allow(dead_code)]
696#[derive(Debug, Clone, Copy, PartialEq, Eq)]
697pub enum TypeKind {
698 Prop,
700 Type0,
702 LargeType,
704 Universe,
706 Pi,
708 Lambda,
710 Application,
712 FreeVar,
714 Constant,
716 Literal,
718 Unknown,
720}
721#[allow(dead_code)]
723pub struct WriteOnce<T> {
724 value: std::cell::Cell<Option<T>>,
725}
726#[allow(dead_code)]
727impl<T: Copy> WriteOnce<T> {
728 pub fn new() -> Self {
730 Self {
731 value: std::cell::Cell::new(None),
732 }
733 }
734 pub fn write(&self, val: T) -> bool {
736 if self.value.get().is_some() {
737 return false;
738 }
739 self.value.set(Some(val));
740 true
741 }
742 pub fn read(&self) -> Option<T> {
744 self.value.get()
745 }
746 pub fn is_written(&self) -> bool {
748 self.value.get().is_some()
749 }
750}
751pub struct TypeChecker<'env> {
756 env: &'env Environment,
757 local_ctx: Vec<LocalDecl>,
758 reducer: Reducer,
759 def_eq_checker: DefEqChecker<'env>,
760 next_fvar: u64,
761 check_mode: bool,
763}
764impl<'env> TypeChecker<'env> {
765 pub fn new(env: &'env Environment) -> Self {
767 Self {
768 env,
769 local_ctx: Vec::new(),
770 reducer: Reducer::new(),
771 def_eq_checker: DefEqChecker::new(env),
772 next_fvar: 0,
773 check_mode: true,
774 }
775 }
776 pub fn new_infer_only(env: &'env Environment) -> Self {
778 Self {
779 env,
780 local_ctx: Vec::new(),
781 reducer: Reducer::new(),
782 def_eq_checker: DefEqChecker::new(env),
783 next_fvar: 0,
784 check_mode: false,
785 }
786 }
787 pub fn set_transparency(&mut self, mode: TransparencyMode) {
789 self.reducer.set_transparency(mode);
790 self.def_eq_checker.set_transparency(mode);
791 }
792 pub fn env(&self) -> &Environment {
794 self.env
795 }
796 pub fn fresh_fvar(&mut self, name: Name, ty: Expr) -> FVarId {
798 let fvar = FVarId(self.next_fvar);
799 self.next_fvar += 1;
800 self.local_ctx.push(LocalDecl {
801 fvar,
802 name,
803 ty,
804 val: None,
805 });
806 fvar
807 }
808 pub fn fresh_fvar_let(&mut self, name: Name, ty: Expr, val: Expr) -> FVarId {
810 let fvar = FVarId(self.next_fvar);
811 self.next_fvar += 1;
812 self.local_ctx.push(LocalDecl {
813 fvar,
814 name,
815 ty,
816 val: Some(val),
817 });
818 fvar
819 }
820 pub fn push_local(&mut self, decl: LocalDecl) {
822 self.local_ctx.push(decl);
823 }
824 pub fn pop_local(&mut self) -> Option<LocalDecl> {
826 self.local_ctx.pop()
827 }
828 #[allow(clippy::result_large_err)]
830 fn lookup_fvar(&self, fvar: FVarId) -> Result<&LocalDecl, KernelError> {
831 self.local_ctx
832 .iter()
833 .find(|decl| decl.fvar == fvar)
834 .ok_or_else(|| KernelError::Other(format!("free variable not found: {:?}", fvar)))
835 }
836 pub fn local_ctx(&self) -> &[LocalDecl] {
838 &self.local_ctx
839 }
840 pub fn whnf(&mut self, expr: &Expr) -> Expr {
842 self.reducer.whnf_env(expr, self.env)
843 }
844 #[allow(clippy::result_large_err)]
848 pub fn ensure_sort(&mut self, expr: &Expr) -> Result<Level, KernelError> {
849 let ty = self.infer_type(expr)?;
850 let ty_whnf = self.whnf(&ty);
851 match ty_whnf {
852 Expr::Sort(l) => Ok(l),
853 _ => Err(KernelError::NotASort(ty_whnf)),
854 }
855 }
856 #[allow(clippy::result_large_err)]
858 pub fn ensure_pi(&mut self, expr: &Expr) -> Result<Expr, KernelError> {
859 let ty = self.infer_type(expr)?;
860 let ty_whnf = self.whnf(&ty);
861 if ty_whnf.is_pi() {
862 Ok(ty_whnf)
863 } else {
864 Err(KernelError::NotAFunction(ty_whnf))
865 }
866 }
867 pub fn is_def_eq(&mut self, t: &Expr, s: &Expr) -> bool {
869 self.def_eq_checker.is_def_eq(t, s)
870 }
871 #[allow(clippy::result_large_err)]
873 pub fn check_type(
874 &mut self,
875 expr: &Expr,
876 inferred: &Expr,
877 expected: &Expr,
878 ) -> Result<(), KernelError> {
879 if self.is_def_eq(inferred, expected) {
880 Ok(())
881 } else {
882 Err(KernelError::TypeMismatch {
883 expected: expected.clone(),
884 got: inferred.clone(),
885 context: format!("checking {}", expr),
886 })
887 }
888 }
889 #[allow(clippy::result_large_err)]
891 pub fn infer_type(&mut self, expr: &Expr) -> Result<Expr, KernelError> {
892 match expr {
893 Expr::Sort(l) => Ok(Expr::Sort(Level::succ(l.clone()))),
894 Expr::BVar(idx) => Err(KernelError::UnboundVariable(*idx)),
895 Expr::FVar(fvar) => {
896 let decl = self.lookup_fvar(*fvar)?;
897 Ok(decl.ty.clone())
898 }
899 Expr::Const(name, levels) => self.infer_const(name, levels),
900 Expr::App(f, a) => self.infer_app(f, a),
901 Expr::Lam(bi, name, ty, body) => {
902 if self.check_mode {
903 self.ensure_sort(ty)?;
904 }
905 let fvar = self.fresh_fvar(name.clone(), (**ty).clone());
906 let body_open = instantiate(body, &Expr::FVar(fvar));
907 let body_ty = self.infer_type(&body_open)?;
908 self.pop_local();
909 let body_ty_closed = abstract_expr(&body_ty, fvar);
910 Ok(Expr::Pi(
911 *bi,
912 name.clone(),
913 ty.clone(),
914 Box::new(body_ty_closed),
915 ))
916 }
917 Expr::Pi(_, _, dom, cod) => {
918 let dom_sort = self.ensure_sort(dom)?;
919 let fvar = self.fresh_fvar(Name::str("_"), (**dom).clone());
920 let cod_open = instantiate(cod, &Expr::FVar(fvar));
921 let cod_sort = self.ensure_sort(&cod_open)?;
922 self.pop_local();
923 Ok(Expr::Sort(Level::imax(dom_sort, cod_sort)))
924 }
925 Expr::Let(_, ty, val, body) => {
926 if self.check_mode {
927 self.ensure_sort(ty)?;
928 let val_ty = self.infer_type(val)?;
929 self.check_type(val, &val_ty, ty)?;
930 }
931 let body_inst = instantiate(body, val);
932 self.infer_type(&body_inst)
933 }
934 Expr::Lit(Literal::Nat(_)) => Ok(Expr::Const(Name::str("Nat"), vec![])),
935 Expr::Lit(Literal::Str(_)) => Ok(Expr::Const(Name::str("String"), vec![])),
936 Expr::Proj(struct_name, idx, struct_expr) => {
937 self.infer_proj(struct_name, *idx, struct_expr)
938 }
939 }
940 }
941 #[allow(clippy::result_large_err)]
943 fn infer_const(&self, name: &Name, levels: &[Level]) -> Result<Expr, KernelError> {
944 if let Some(ci) = self.env.find(name) {
945 let params = ci.level_params();
946 if !params.is_empty() && !levels.is_empty() && params.len() != levels.len() {
947 return Err(KernelError::Other(format!(
948 "universe parameter count mismatch for {}: expected {}, got {}",
949 name,
950 params.len(),
951 levels.len()
952 )));
953 }
954 if params.is_empty() || levels.is_empty() {
955 return Ok(ci.ty().clone());
956 }
957 return Ok(instantiate_type_lparams(ci.ty(), params, levels));
958 }
959 let decl = self
960 .env
961 .get(name)
962 .ok_or_else(|| KernelError::UnknownConstant(name.clone()))?;
963 Ok(decl.ty().clone())
964 }
965 #[allow(clippy::result_large_err)]
967 fn infer_app(&mut self, f: &Expr, a: &Expr) -> Result<Expr, KernelError> {
968 let f_ty = self.infer_type(f)?;
969 let f_ty_whnf = self.whnf(&f_ty);
970 match &f_ty_whnf {
971 Expr::Pi(_, _, dom, cod) => {
972 if self.check_mode {
973 let a_ty = self.infer_type(a)?;
974 self.check_type(a, &a_ty, dom)?;
975 }
976 Ok(instantiate(cod, a))
977 }
978 _ => Err(KernelError::NotAFunction(f_ty_whnf)),
979 }
980 }
981 #[allow(clippy::result_large_err)]
983 fn infer_proj(
984 &mut self,
985 struct_name: &Name,
986 idx: u32,
987 struct_expr: &Expr,
988 ) -> Result<Expr, KernelError> {
989 let ind_val = self
990 .env
991 .get_inductive_val(struct_name)
992 .ok_or_else(|| KernelError::Other(format!("not a structure type: {}", struct_name)))?
993 .clone();
994 if ind_val.ctors.len() != 1 {
995 return Err(KernelError::Other(format!(
996 "{} is not a structure (has {} constructors)",
997 struct_name,
998 ind_val.ctors.len()
999 )));
1000 }
1001 let ctor_name = &ind_val.ctors[0];
1002 let ctor_val = self
1003 .env
1004 .get_constructor_val(ctor_name)
1005 .ok_or_else(|| KernelError::Other(format!("constructor not found: {}", ctor_name)))?
1006 .clone();
1007 if idx >= ctor_val.num_fields {
1008 return Err(KernelError::Other(format!(
1009 "field index {} out of range for {} (has {} fields)",
1010 idx, struct_name, ctor_val.num_fields
1011 )));
1012 }
1013 let struct_ty = self.infer_type(struct_expr)?;
1014 Ok(self.infer_proj_field_type(&ind_val, &ctor_val, idx, struct_expr, &struct_ty))
1015 }
1016 fn infer_proj_field_type(
1025 &mut self,
1026 ind_val: &InductiveVal,
1027 ctor_val: &ConstructorVal,
1028 idx: u32,
1029 struct_expr: &Expr,
1030 struct_ty: &Expr,
1031 ) -> Expr {
1032 let ctor_ty = ctor_val.common.ty.clone();
1033 let struct_ty_whnf = self.whnf(struct_ty);
1034 let levels: Vec<Level> = match get_app_fn(&struct_ty_whnf) {
1035 Expr::Const(_, lvls) => lvls.clone(),
1036 _ => vec![],
1037 };
1038 let level_params = &ind_val.common.level_params;
1039 let mut cur_ty = instantiate_type_lparams(&ctor_ty, level_params, &levels);
1040 let struct_args: Vec<Expr> = get_app_args(&struct_ty_whnf).into_iter().cloned().collect();
1041 for i in 0..ind_val.num_params as usize {
1042 match cur_ty {
1043 Expr::Pi(_, _, _, body) => {
1044 let param = struct_args.get(i).cloned().unwrap_or(Expr::BVar(0));
1045 cur_ty = instantiate(&body, ¶m);
1046 }
1047 _ => return struct_ty.clone(),
1048 }
1049 }
1050 for j in 0..idx {
1051 match cur_ty {
1052 Expr::Pi(_, _, _, body) => {
1053 let field_val = Expr::Proj(
1054 ind_val.common.name.clone(),
1055 j,
1056 Box::new(struct_expr.clone()),
1057 );
1058 cur_ty = instantiate(&body, &field_val);
1059 }
1060 _ => return struct_ty.clone(),
1061 }
1062 }
1063 match cur_ty {
1064 Expr::Pi(_, _, dom, _) => *dom,
1065 _ => struct_ty.clone(),
1066 }
1067 }
1068 pub fn is_prop(&mut self, expr: &Expr) -> bool {
1070 if let Ok(ty) = self.infer_type(expr) {
1071 let ty_whnf = self.whnf(&ty);
1072 matches!(ty_whnf, Expr::Sort(l) if l.is_zero())
1073 } else {
1074 false
1075 }
1076 }
1077 pub fn is_proof(&mut self, expr: &Expr) -> bool {
1079 if let Ok(ty) = self.infer_type(expr) {
1080 self.is_prop(&ty)
1081 } else {
1082 false
1083 }
1084 }
1085 pub fn is_type(&mut self, expr: &Expr) -> bool {
1087 if let Ok(ty) = self.infer_type(expr) {
1088 let ty_whnf = self.whnf(&ty);
1089 ty_whnf.is_sort()
1090 } else {
1091 false
1092 }
1093 }
1094 #[allow(clippy::result_large_err)]
1096 pub fn get_level(&mut self, expr: &Expr) -> Result<Level, KernelError> {
1097 let ty = self.infer_type(expr)?;
1098 let ty_whnf = self.whnf(&ty);
1099 match ty_whnf {
1100 Expr::Sort(l) => Ok(l),
1101 _ => Err(KernelError::NotASort(ty_whnf)),
1102 }
1103 }
1104 pub fn unfold_definition(&mut self, expr: &Expr) -> Option<Expr> {
1106 let head = get_app_fn(expr);
1107 if let Expr::Const(name, levels) = head {
1108 if let Some(ci) = self.env.find(name) {
1109 if let Some(val) = ci.value() {
1110 let hint = ci.reducibility_hint();
1111 if hint.should_unfold() {
1112 let val_inst = if ci.level_params().is_empty() || levels.is_empty() {
1113 val.clone()
1114 } else {
1115 instantiate_type_lparams(val, ci.level_params(), levels)
1116 };
1117 let args: Vec<Expr> = get_app_args(expr).into_iter().cloned().collect();
1118 return Some(mk_app(val_inst, &args));
1119 }
1120 }
1121 }
1122 }
1123 None
1124 }
1125}
1126impl<'env> TypeChecker<'env> {
1127 #[allow(clippy::result_large_err)]
1129 pub fn infer_app_chain(
1130 &mut self,
1131 f: &Expr,
1132 args: &[Expr],
1133 ) -> Result<Expr, crate::error::KernelError> {
1134 let mut ty = self.infer_type(f)?;
1135 for arg in args {
1136 let whnf = self.whnf(&ty);
1137 match whnf {
1138 Expr::Pi(_, _, dom, cod) => {
1139 if self.check_mode {
1140 let arg_ty = self.infer_type(arg)?;
1141 if !self.is_def_eq(&arg_ty, &dom) {
1142 return Err(crate::error::KernelError::TypeMismatch {
1143 expected: *dom,
1144 got: arg_ty,
1145 context: "application argument".to_string(),
1146 });
1147 }
1148 }
1149 ty = instantiate(&cod, arg);
1150 }
1151 other => return Err(crate::error::KernelError::NotAFunction(other)),
1152 }
1153 }
1154 Ok(ty)
1155 }
1156 pub fn telescope_type(&mut self, ty: &Expr, max_pis: usize) -> (Vec<LocalDecl>, Expr) {
1158 let mut fvars = Vec::new();
1159 let mut current = ty.clone();
1160 for _ in 0..max_pis {
1161 let whnf = self.whnf(¤t);
1162 match whnf {
1163 Expr::Pi(bi, name, dom, cod) => {
1164 let fvar_id = self.fresh_fvar(name.clone(), *dom.clone());
1165 let decl = LocalDecl {
1166 fvar: fvar_id,
1167 name,
1168 ty: *dom,
1169 val: None,
1170 };
1171 let body = instantiate(&cod, &Expr::FVar(fvar_id));
1172 fvars.push(decl);
1173 current = body;
1174 let _ = bi;
1175 }
1176 _ => break,
1177 }
1178 }
1179 (fvars, current)
1180 }
1181 pub fn close_type_over_fvars(&mut self, fvars: &[LocalDecl], ty: Expr) -> Expr {
1183 let mut result = ty;
1184 for decl in fvars.iter().rev() {
1185 result = abstract_expr(&result, decl.fvar);
1186 result = Expr::Pi(
1187 crate::BinderInfo::Default,
1188 decl.name.clone(),
1189 Box::new(decl.ty.clone()),
1190 Box::new(result),
1191 );
1192 }
1193 result
1194 }
1195 pub fn close_term_over_fvars(&mut self, fvars: &[LocalDecl], term: Expr) -> Expr {
1197 let mut result = term;
1198 for decl in fvars.iter().rev() {
1199 result = abstract_expr(&result, decl.fvar);
1200 result = Expr::Lam(
1201 crate::BinderInfo::Default,
1202 decl.name.clone(),
1203 Box::new(decl.ty.clone()),
1204 Box::new(result),
1205 );
1206 }
1207 result
1208 }
1209 #[allow(clippy::result_large_err)]
1211 pub fn check(&mut self, expr: &Expr, expected: &Expr) -> Result<(), crate::error::KernelError> {
1212 let inferred = self.infer_type(expr)?;
1213 if self.is_def_eq(&inferred, expected) {
1214 Ok(())
1215 } else {
1216 Err(crate::error::KernelError::TypeMismatch {
1217 expected: expected.clone(),
1218 got: inferred,
1219 context: format!("check({:?})", expr),
1220 })
1221 }
1222 }
1223 pub fn try_check(&mut self, expr: &Expr, expected: &Expr) -> bool {
1225 if let Ok(inferred) = self.infer_type(expr) {
1226 self.is_def_eq(&inferred, expected)
1227 } else {
1228 false
1229 }
1230 }
1231 pub fn count_pi_binders(&mut self, ty: &Expr) -> usize {
1233 let mut count = 0;
1234 let mut current = ty.clone();
1235 loop {
1236 let whnf = self.whnf(¤t);
1237 if let Expr::Pi(_, _, _, cod) = whnf {
1238 count += 1;
1239 current = *cod;
1240 } else {
1241 break;
1242 }
1243 }
1244 count
1245 }
1246 #[allow(clippy::result_large_err)]
1248 pub fn verify_declaration(
1249 &mut self,
1250 name: &Name,
1251 ty: &Expr,
1252 val: Option<&Expr>,
1253 ) -> Result<(), crate::error::KernelError> {
1254 self.ensure_sort(ty)?;
1255 if let Some(v) = val {
1256 let v_ty = self.infer_type(v)?;
1257 if !self.is_def_eq(&v_ty, ty) {
1258 return Err(crate::error::KernelError::TypeMismatch {
1259 expected: ty.clone(),
1260 got: v_ty,
1261 context: format!("verifying declaration {}", name),
1262 });
1263 }
1264 }
1265 Ok(())
1266 }
1267 pub fn normalize(&mut self, expr: &Expr) -> Expr {
1269 let whnf = self.whnf(expr);
1270 match &whnf {
1271 Expr::App(f, a) => {
1272 let f_norm = self.normalize(f);
1273 let a_norm = self.normalize(a);
1274 Expr::App(Box::new(f_norm), Box::new(a_norm))
1275 }
1276 Expr::Lam(bi, name, ty, body) => {
1277 let ty_norm = self.normalize(ty);
1278 let body_norm = self.normalize(body);
1279 Expr::Lam(*bi, name.clone(), Box::new(ty_norm), Box::new(body_norm))
1280 }
1281 Expr::Pi(bi, name, ty, body) => {
1282 let ty_norm = self.normalize(ty);
1283 let body_norm = self.normalize(body);
1284 Expr::Pi(*bi, name.clone(), Box::new(ty_norm), Box::new(body_norm))
1285 }
1286 Expr::Let(name, ty, val, body) => {
1287 let ty_norm = self.normalize(ty);
1288 let val_norm = self.normalize(val);
1289 let body_norm = self.normalize(body);
1290 Expr::Let(
1291 name.clone(),
1292 Box::new(ty_norm),
1293 Box::new(val_norm),
1294 Box::new(body_norm),
1295 )
1296 }
1297 Expr::Proj(sname, idx, inner) => {
1298 let inner_norm = self.normalize(inner);
1299 Expr::Proj(sname.clone(), *idx, Box::new(inner_norm))
1300 }
1301 _ => whnf,
1302 }
1303 }
1304 pub fn constant_arity(&mut self, name: &Name) -> Option<usize> {
1306 let ty = if let Some(ci) = self.env.find(name) {
1307 ci.ty().clone()
1308 } else {
1309 self.env.get(name)?.ty().clone()
1310 };
1311 Some(self.count_pi_binders(&ty))
1312 }
1313 pub fn is_level_eq(&self, l1: &Level, l2: &Level) -> bool {
1315 l1 == l2 || (l1.is_zero() && l2.is_zero())
1316 }
1317}
1318#[allow(dead_code)]
1320pub struct SmallMap<K: Ord + Clone, V: Clone> {
1321 entries: Vec<(K, V)>,
1322}
1323#[allow(dead_code)]
1324impl<K: Ord + Clone, V: Clone> SmallMap<K, V> {
1325 pub fn new() -> Self {
1327 Self {
1328 entries: Vec::new(),
1329 }
1330 }
1331 pub fn insert(&mut self, key: K, val: V) {
1333 match self.entries.binary_search_by_key(&&key, |(k, _)| k) {
1334 Ok(i) => self.entries[i].1 = val,
1335 Err(i) => self.entries.insert(i, (key, val)),
1336 }
1337 }
1338 pub fn get(&self, key: &K) -> Option<&V> {
1340 self.entries
1341 .binary_search_by_key(&key, |(k, _)| k)
1342 .ok()
1343 .map(|i| &self.entries[i].1)
1344 }
1345 pub fn len(&self) -> usize {
1347 self.entries.len()
1348 }
1349 pub fn is_empty(&self) -> bool {
1351 self.entries.is_empty()
1352 }
1353 pub fn keys(&self) -> Vec<&K> {
1355 self.entries.iter().map(|(k, _)| k).collect()
1356 }
1357 pub fn values(&self) -> Vec<&V> {
1359 self.entries.iter().map(|(_, v)| v).collect()
1360 }
1361}
1362#[allow(dead_code)]
1364pub struct VersionedRecord<T: Clone> {
1365 history: Vec<T>,
1366}
1367#[allow(dead_code)]
1368impl<T: Clone> VersionedRecord<T> {
1369 pub fn new(initial: T) -> Self {
1371 Self {
1372 history: vec![initial],
1373 }
1374 }
1375 pub fn update(&mut self, val: T) {
1377 self.history.push(val);
1378 }
1379 pub fn current(&self) -> &T {
1381 self.history
1382 .last()
1383 .expect("VersionedRecord history is always non-empty after construction")
1384 }
1385 pub fn at_version(&self, n: usize) -> Option<&T> {
1387 self.history.get(n)
1388 }
1389 pub fn version(&self) -> usize {
1391 self.history.len() - 1
1392 }
1393 pub fn has_history(&self) -> bool {
1395 self.history.len() > 1
1396 }
1397}
1398#[allow(dead_code)]
1402pub struct InferCache {
1403 entries: Vec<InferCacheEntry>,
1404 capacity: usize,
1405}
1406impl InferCache {
1407 #[allow(dead_code)]
1409 pub fn new(capacity: usize) -> Self {
1410 Self {
1411 entries: Vec::with_capacity(capacity),
1412 capacity,
1413 }
1414 }
1415 #[allow(dead_code)]
1417 pub fn get(&self, expr: &Expr) -> Option<&Expr> {
1418 self.entries
1419 .iter()
1420 .rev()
1421 .find(|e| &e.expr == expr)
1422 .map(|e| &e.ty)
1423 }
1424 #[allow(dead_code)]
1426 pub fn insert(&mut self, expr: Expr, ty: Expr) {
1427 if self.entries.len() >= self.capacity {
1428 self.entries.remove(0);
1429 }
1430 self.entries.push(InferCacheEntry { expr, ty });
1431 }
1432 #[allow(dead_code)]
1434 pub fn clear(&mut self) {
1435 self.entries.clear();
1436 }
1437 #[allow(dead_code)]
1439 pub fn len(&self) -> usize {
1440 self.entries.len()
1441 }
1442 #[allow(dead_code)]
1444 pub fn is_empty(&self) -> bool {
1445 self.entries.is_empty()
1446 }
1447}
1448#[allow(dead_code)]
1450pub struct SlidingSum {
1451 window: Vec<f64>,
1452 capacity: usize,
1453 pos: usize,
1454 sum: f64,
1455 count: usize,
1456}
1457#[allow(dead_code)]
1458impl SlidingSum {
1459 pub fn new(capacity: usize) -> Self {
1461 Self {
1462 window: vec![0.0; capacity],
1463 capacity,
1464 pos: 0,
1465 sum: 0.0,
1466 count: 0,
1467 }
1468 }
1469 pub fn push(&mut self, val: f64) {
1471 let oldest = self.window[self.pos];
1472 self.sum -= oldest;
1473 self.sum += val;
1474 self.window[self.pos] = val;
1475 self.pos = (self.pos + 1) % self.capacity;
1476 if self.count < self.capacity {
1477 self.count += 1;
1478 }
1479 }
1480 pub fn sum(&self) -> f64 {
1482 self.sum
1483 }
1484 pub fn mean(&self) -> Option<f64> {
1486 if self.count == 0 {
1487 None
1488 } else {
1489 Some(self.sum / self.count as f64)
1490 }
1491 }
1492 pub fn count(&self) -> usize {
1494 self.count
1495 }
1496}
1497#[derive(Clone, Debug)]
1499pub struct LocalDecl {
1500 pub fvar: FVarId,
1502 pub name: Name,
1504 pub ty: Expr,
1506 pub val: Option<Expr>,
1508}
1509#[allow(dead_code)]
1511pub struct StringPool {
1512 free: Vec<String>,
1513}
1514#[allow(dead_code)]
1515impl StringPool {
1516 pub fn new() -> Self {
1518 Self { free: Vec::new() }
1519 }
1520 pub fn take(&mut self) -> String {
1522 self.free.pop().unwrap_or_default()
1523 }
1524 pub fn give(&mut self, mut s: String) {
1526 s.clear();
1527 self.free.push(s);
1528 }
1529 pub fn free_count(&self) -> usize {
1531 self.free.len()
1532 }
1533}
1534#[allow(dead_code)]
1536#[allow(missing_docs)]
1537pub enum DecisionNode {
1538 Leaf(String),
1540 Branch {
1542 key: String,
1543 val: String,
1544 yes_branch: Box<DecisionNode>,
1545 no_branch: Box<DecisionNode>,
1546 },
1547}
1548#[allow(dead_code)]
1549impl DecisionNode {
1550 pub fn evaluate(&self, ctx: &std::collections::HashMap<String, String>) -> &str {
1552 match self {
1553 DecisionNode::Leaf(action) => action.as_str(),
1554 DecisionNode::Branch {
1555 key,
1556 val,
1557 yes_branch,
1558 no_branch,
1559 } => {
1560 let actual = ctx.get(key).map(|s| s.as_str()).unwrap_or("");
1561 if actual == val.as_str() {
1562 yes_branch.evaluate(ctx)
1563 } else {
1564 no_branch.evaluate(ctx)
1565 }
1566 }
1567 }
1568 }
1569 pub fn depth(&self) -> usize {
1571 match self {
1572 DecisionNode::Leaf(_) => 0,
1573 DecisionNode::Branch {
1574 yes_branch,
1575 no_branch,
1576 ..
1577 } => 1 + yes_branch.depth().max(no_branch.depth()),
1578 }
1579 }
1580}
1581#[allow(dead_code)]
1583pub struct StatSummary {
1584 count: u64,
1585 sum: f64,
1586 min: f64,
1587 max: f64,
1588}
1589#[allow(dead_code)]
1590impl StatSummary {
1591 pub fn new() -> Self {
1593 Self {
1594 count: 0,
1595 sum: 0.0,
1596 min: f64::INFINITY,
1597 max: f64::NEG_INFINITY,
1598 }
1599 }
1600 pub fn record(&mut self, val: f64) {
1602 self.count += 1;
1603 self.sum += val;
1604 if val < self.min {
1605 self.min = val;
1606 }
1607 if val > self.max {
1608 self.max = val;
1609 }
1610 }
1611 pub fn mean(&self) -> Option<f64> {
1613 if self.count == 0 {
1614 None
1615 } else {
1616 Some(self.sum / self.count as f64)
1617 }
1618 }
1619 pub fn min(&self) -> Option<f64> {
1621 if self.count == 0 {
1622 None
1623 } else {
1624 Some(self.min)
1625 }
1626 }
1627 pub fn max(&self) -> Option<f64> {
1629 if self.count == 0 {
1630 None
1631 } else {
1632 Some(self.max)
1633 }
1634 }
1635 pub fn count(&self) -> u64 {
1637 self.count
1638 }
1639}
1640#[allow(dead_code)]
1642pub struct LabelSet {
1643 labels: Vec<String>,
1644}
1645#[allow(dead_code)]
1646impl LabelSet {
1647 pub fn new() -> Self {
1649 Self { labels: Vec::new() }
1650 }
1651 pub fn add(&mut self, label: impl Into<String>) {
1653 let s = label.into();
1654 if !self.labels.contains(&s) {
1655 self.labels.push(s);
1656 }
1657 }
1658 pub fn has(&self, label: &str) -> bool {
1660 self.labels.iter().any(|l| l == label)
1661 }
1662 pub fn count(&self) -> usize {
1664 self.labels.len()
1665 }
1666 pub fn all(&self) -> &[String] {
1668 &self.labels
1669 }
1670}
1671#[allow(dead_code)]
1673pub struct PathBuf {
1674 components: Vec<String>,
1675}
1676#[allow(dead_code)]
1677impl PathBuf {
1678 pub fn new() -> Self {
1680 Self {
1681 components: Vec::new(),
1682 }
1683 }
1684 pub fn push(&mut self, comp: impl Into<String>) {
1686 self.components.push(comp.into());
1687 }
1688 pub fn pop(&mut self) {
1690 self.components.pop();
1691 }
1692 pub fn as_str(&self) -> String {
1694 self.components.join("/")
1695 }
1696 pub fn depth(&self) -> usize {
1698 self.components.len()
1699 }
1700 pub fn clear(&mut self) {
1702 self.components.clear();
1703 }
1704}
1705#[allow(dead_code)]
1707pub struct RawFnPtr {
1708 ptr: usize,
1710 arity: usize,
1711 name: String,
1712}
1713#[allow(dead_code)]
1714impl RawFnPtr {
1715 pub fn new(ptr: usize, arity: usize, name: impl Into<String>) -> Self {
1717 Self {
1718 ptr,
1719 arity,
1720 name: name.into(),
1721 }
1722 }
1723 pub fn arity(&self) -> usize {
1725 self.arity
1726 }
1727 pub fn name(&self) -> &str {
1729 &self.name
1730 }
1731 pub fn raw(&self) -> usize {
1733 self.ptr
1734 }
1735}
1736#[allow(dead_code)]
1738pub enum Either2<A, B> {
1739 First(A),
1741 Second(B),
1743}
1744#[allow(dead_code)]
1745impl<A, B> Either2<A, B> {
1746 pub fn is_first(&self) -> bool {
1748 matches!(self, Either2::First(_))
1749 }
1750 pub fn is_second(&self) -> bool {
1752 matches!(self, Either2::Second(_))
1753 }
1754 pub fn first(self) -> Option<A> {
1756 match self {
1757 Either2::First(a) => Some(a),
1758 _ => None,
1759 }
1760 }
1761 pub fn second(self) -> Option<B> {
1763 match self {
1764 Either2::Second(b) => Some(b),
1765 _ => None,
1766 }
1767 }
1768 pub fn map_first<C, F: FnOnce(A) -> C>(self, f: F) -> Either2<C, B> {
1770 match self {
1771 Either2::First(a) => Either2::First(f(a)),
1772 Either2::Second(b) => Either2::Second(b),
1773 }
1774 }
1775}