1use super::env::Kind;
38use super::error::{KResult, KernelFailure};
39use super::expr::{ExprId, Lit, Node};
40use super::level::LevelsId;
41use super::logos::{lift, lower_open};
42use super::tc::{Kernel, Theory};
43use crate::context::Context;
44use crate::reduction::{CertifiedProgram, ProgramShape};
45use crate::term::Term;
46use rustc_hash::FxHashMap as FxMap;
47use std::rc::Rc;
48
49#[derive(Clone)]
51pub struct LogosState {
52 pub(crate) ctx: Context,
54 columns: FxMap<ExprId, Option<usize>>,
57 builtins: FxMap<ExprId, Option<ExprId>>,
59 numerals: crate::context::CanonicalNumerals,
62 irrelevant: FxMap<super::name::NameId, u64>,
65 pub(super) certified_obs: Option<super::logos_obs::Certified>,
67 programs: FxMap<(super::name::NameId, usize), Option<Rc<(ProgramShape, CertifiedProgram)>>>,
71}
72
73impl LogosState {
74 pub(crate) fn end_decl(&mut self) {
76 self.columns.retain(|k, _| !k.is_scratch());
77 self.builtins
78 .retain(|k, v| !k.is_scratch() && !v.is_some_and(ExprId::is_scratch));
79 }
80}
81
82macro_rules! bail {
83 ($($arg:tt)*) => {
84 return Err(KernelFailure::reject(format!($($arg)*)))
85 };
86}
87
88impl Kernel {
89 pub fn logos(ctx: &Context) -> Kernel {
91 let mut k = Kernel::new();
92 k.theory = Theory::Logos;
93 k.wk = super::wellknown::WellKnown::logos(&mut k.s.names);
94 let certified_obs = super::logos_obs::Certified::of(ctx, &mut k.s.names);
95 k.logos = Some(Box::new(LogosState {
96 ctx: ctx.clone(),
97 columns: FxMap::default(),
98 builtins: FxMap::default(),
99 numerals: ctx.canonical_numerals(),
100 irrelevant: FxMap::default(),
101 certified_obs,
102 programs: FxMap::default(),
103 }));
104 k
105 }
106
107 pub(crate) fn follow(&mut self, ctx: &Context) {
110 let certified_obs = super::logos_obs::Certified::of(ctx, &mut self.s.names);
111 let state = self
112 .logos
113 .as_mut()
114 .expect("a LOGOS kernel carries its context");
115 state.ctx = ctx.clone();
116 state.certified_obs = certified_obs;
117 state.programs.clear();
118 }
119
120 pub(crate) fn numerals(&self) -> crate::context::CanonicalNumerals {
122 self.logos
123 .as_ref()
124 .expect("a LOGOS kernel carries its context")
125 .numerals
126 }
127
128 pub(crate) fn reads_nat_literal(&self, n: &super::nat::Nat) -> bool {
133 match &self.logos {
134 None => true,
135 Some(state) if n.is_zero() => state.numerals.zero,
136 Some(state) => state.numerals.succ,
137 }
138 }
139
140 pub(crate) fn nat_literal_rules(&self) -> bool {
143 self.logos
144 .as_ref()
145 .map_or(true, |state| state.numerals.succ)
146 }
147
148 fn logos_ctx(&self) -> &Context {
149 &self
150 .logos
151 .as_ref()
152 .expect("a LOGOS kernel carries its context")
153 .ctx
154 }
155
156 fn global(&mut self, name: &str) -> ExprId {
157 let n = self.s.names.str(super::name::NameId::ANON, name);
158 self.s.cnst(n, LevelsId::EMPTY)
159 }
160
161 pub(crate) fn infer_logos(&mut self, e: ExprId, infer_only: bool) -> KResult<ExprId> {
162 if self.theory != Theory::Logos {
163 bail!("a LOGOS construct outside LOGOS's theory");
164 }
165 match self.s.node(e) {
166 Node::Lit(l) => {
167 let carrier = match self.s.lit_value(l) {
168 Lit::Int(_) | Lit::BigInt(_) => "Int",
169 Lit::Float(_) => "Float",
170 Lit::Duration(_) => "Duration",
171 Lit::Date(_) => "Date",
172 Lit::Moment(_) => "Moment",
173 };
174 Ok(self.global(carrier))
175 }
176 Node::NatLit(_) => Ok(self.global("Nat")),
177 Node::StrLit(_) => Ok(self.global("Text")),
178 Node::Match(d, m, cs) => self.infer_match(d, m, cs, infer_only),
179 Node::Fix(body) => self.infer_fix(e, body, infer_only),
180 Node::MutualFix(ds, i) => self.infer_mutual_fix(ds, i, infer_only),
181 other => unreachable!("not a LOGOS construct: {other:?}"),
182 }
183 }
184
185 fn inductive_app(
187 &mut self,
188 ty: ExprId,
189 ) -> KResult<(super::name::NameId, LevelsId, Vec<ExprId>)> {
190 let w = self.whnf(ty)?;
191 let (head, args) = self.s.app_args(w);
192 if let Node::Const(n, ls) = self.s.node(head) {
193 if let Some(c) = self.env.get(n) {
194 if matches!(c.kind, Kind::Induct(_)) {
195 return Ok((n, ls, args.to_vec()));
196 }
197 }
198 }
199 bail!(
200 "match on a value whose type {} is not an inductive family",
201 self.show(ty)
202 )
203 }
204
205 fn infer_match(
206 &mut self,
207 d: ExprId,
208 m: ExprId,
209 cs: ExprId,
210 infer_only: bool,
211 ) -> KResult<ExprId> {
212 let dt = self.infer(d, infer_only)?;
213 let (ind, ls, args) = self.inductive_app(dt)?;
214 let info = match &self.env.get(ind).expect("checked above").kind {
215 Kind::Induct(i) => i.clone(),
216 _ => unreachable!(),
217 };
218 let dtw = self.whnf(dt)?;
219 if info.num_indices > 0 {
220 return self.infer_indexed_match(d, m, cs, ind, ls, &args, &info, dtw, infer_only);
221 }
222 let mt = self.infer(m, infer_only)?;
223 let mtw = self.whnf(mt)?;
224 let motive = match self.s.node(mtw) {
225 Node::Pi(..) if infer_only => m,
228 Node::Pi(dom, cod) => {
229 if !self.is_def_eq(dom, dtw)? {
230 bail!(
231 "motive parameter {} doesn't match discriminant type {}",
232 self.show(dom),
233 self.show(dtw)
234 );
235 }
236 let x = self.push_local(dom, None);
237 let codw = self.s.instantiate1(cod, x);
238 let codw = self.whnf(codw)?;
239 if !matches!(self.s.node(codw), Node::Sort(_)) {
240 bail!("motive type codomain is not a sort");
241 }
242 m
243 }
244 Node::Sort(_) => {
245 let body = self.s.lift_loose(m, 0, 1);
246 self.s.lam(dtw, body)
247 }
248 _ => bail!("motive {} is not a function or type", self.show(m)),
249 };
250 let cases = self.s.seq_items(cs);
251 if cases.len() != info.ctors.len() {
252 bail!(
253 "wrong number of cases: expected {}, found {}",
254 info.ctors.len(),
255 cases.len()
256 );
257 }
258 let params = &args[..info.num_params as usize];
259 for (case, ctor) in cases.iter().zip(info.ctors.iter()) {
260 if infer_only {
261 continue;
262 }
263 let expected = self.case_type(*ctor, ls, params, motive)?;
264 if !self.check_fix_against(*case, expected)? {
265 let ct = self.infer(*case, false)?;
266 if !self.conforms(ct, expected)? {
267 bail!(
268 "case for '{}' has type {} where {} is expected",
269 self.s.names.display(*ctor),
270 self.show(ct),
271 self.show(expected)
272 );
273 }
274 }
275 }
276 let applied = self.s.app(motive, d);
277 let ret = self.whnf_core(applied, false)?;
278 if !infer_only {
279 self.check_elimination(ind, dtw, ret)?;
280 }
281 Ok(ret)
282 }
283
284 fn case_type(
286 &mut self,
287 ctor: super::name::NameId,
288 ls: LevelsId,
289 params: &[ExprId],
290 motive: ExprId,
291 ) -> KResult<ExprId> {
292 let c = self
293 .env
294 .get(ctor)
295 .expect("an inductive's constructor is declared")
296 .clone();
297 let us: Vec<_> = self.s.levels.get_list(ls).to_vec();
298 let mut ty = self.s.instantiate_lparams(c.ty, &c.lparams, &us);
299 for p in params {
300 let w = self.whnf(ty)?;
301 let Node::Pi(_, b) = self.s.node(w) else {
302 bail!("constructor type has fewer parameters than its family")
303 };
304 ty = self.s.instantiate1(b, *p);
305 }
306 let head = self.s.cnst(ctor, ls);
307 let head = self.s.mk_app_n(head, params);
308 let mut fields = Vec::new();
309 loop {
310 let w = self.whnf(ty)?;
311 match self.s.node(w) {
312 Node::Pi(a, b) => {
313 let x = self.push_local(a, None);
314 fields.push(x);
315 ty = self.s.instantiate1(b, x);
316 }
317 _ => break,
318 }
319 }
320 let value = self.s.mk_app_n(head, &fields);
321 let w = self.whnf(ty)?;
322 let (_, result_args) = self.s.app_args(w);
323 let indices: Vec<ExprId> = result_args.iter().skip(params.len()).copied().collect();
324 let body = self.s.mk_app_n(motive, &indices);
325 let body = self.s.app(body, value);
326 let body = self.whnf_core(body, false)?;
327 Ok(self.mk_pi(&fields, body))
328 }
329
330 #[allow(clippy::too_many_arguments)]
336 fn infer_indexed_match(
337 &mut self,
338 d: ExprId,
339 m: ExprId,
340 cs: ExprId,
341 ind: super::name::NameId,
342 ls: LevelsId,
343 args: &[ExprId],
344 info: &super::env::InductiveInfo,
345 dtw: ExprId,
346 infer_only: bool,
347 ) -> KResult<ExprId> {
348 let np = info.num_params as usize;
349 let (params, indices) = args.split_at(np);
350 if !infer_only {
351 self.validate_indexed_motive(ind, ls, params, indices.len(), m)?;
352 }
353 let cases = self.s.seq_items(cs);
354 let owed: Vec<super::name::NameId> = if cases.len() == info.ctors.len() {
355 info.ctors.to_vec()
356 } else {
357 let allowed = self.allowed_constructors(ind, np, args);
358 if cases.len() != allowed.len() {
359 bail!(
360 "wrong number of cases: expected {}, found {}",
361 allowed.len(),
362 cases.len()
363 );
364 }
365 allowed.iter().map(|i| info.ctors[*i]).collect()
366 };
367 for (case, ctor) in cases.iter().zip(owed.iter()) {
368 if infer_only {
369 continue;
370 }
371 let expected = self.case_type(*ctor, ls, params, m)?;
372 if !self.check_fix_against(*case, expected)? {
373 let ct = self.infer(*case, false)?;
374 if !self.conforms(ct, expected)? {
375 bail!(
376 "case for '{}' has type {} where {} is expected",
377 self.s.names.display(*ctor),
378 self.show(ct),
379 self.show(expected)
380 );
381 }
382 }
383 }
384 let ret = self.s.mk_app_n(m, indices);
385 let ret = self.s.app(ret, d);
386 let ret = self.whnf_core(ret, false)?;
387 if !infer_only {
388 let rs = self.infer(ret, true)?;
389 let rs = self.whnf(rs)?;
390 if !matches!(self.s.node(rs), Node::Sort(_)) {
391 bail!("indexed match on '{}' has a non-type result: the motive is not a family into a sort", self.s.names.display(ind));
392 }
393 self.check_elimination(ind, dtw, ret)?;
394 }
395 Ok(ret)
396 }
397
398 fn validate_indexed_motive(
401 &mut self,
402 ind: super::name::NameId,
403 ls: LevelsId,
404 params: &[ExprId],
405 num_indices: usize,
406 m: ExprId,
407 ) -> KResult<()> {
408 let c = self.env.get(ind).expect("an inductive").clone();
409 let us: Vec<_> = self.s.levels.get_list(ls).to_vec();
410 let mut arity = self.s.instantiate_lparams(c.ty, &c.lparams, &us);
411 for p in params {
412 let w = self.whnf(arity)?;
413 let Node::Pi(_, b) = self.s.node(w) else {
414 bail!("inductive has fewer parameters than its discriminant")
415 };
416 arity = self.s.instantiate1(b, *p);
417 }
418 let head = self.s.cnst(ind, ls);
419 let mut family = self.s.mk_app_n(head, params);
420 let mut mt = self.infer(m, false)?;
421 for _ in 0..num_indices {
422 let aw = self.whnf(arity)?;
423 let Node::Pi(ad, ab) = self.s.node(aw) else {
424 bail!("inductive index telescope is shorter than its discriminant")
425 };
426 let mw = self.whnf(mt)?;
427 let Node::Pi(md, mb) = self.s.node(mw) else {
428 bail!("indexed motive lacks an index binder")
429 };
430 if !self.is_def_eq(md, ad)? {
431 bail!("indexed motive index domain differs from the inductive telescope");
432 }
433 let x = self.push_local(ad, None);
434 arity = self.s.instantiate1(ab, x);
435 mt = self.s.instantiate1(mb, x);
436 family = self.s.app(family, x);
437 }
438 let aw = self.whnf(arity)?;
439 if !matches!(self.s.node(aw), Node::Sort(_)) {
440 bail!("discriminant does not instantiate the full inductive index telescope");
441 }
442 let mw = self.whnf(mt)?;
443 let Node::Pi(md, mb) = self.s.node(mw) else {
444 bail!("indexed motive lacks its major-premise binder")
445 };
446 if !self.is_def_eq(md, family)? {
447 bail!("indexed motive major domain differs from the generic indexed family");
448 }
449 let x = self.push_local(family, None);
450 let cod = self.s.instantiate1(mb, x);
451 let cod = self.whnf(cod)?;
452 if !matches!(self.s.node(cod), Node::Sort(_)) {
453 bail!("indexed motive must return a sort after its major-premise binder");
454 }
455 Ok(())
456 }
457
458 fn allowed_constructors(
461 &mut self,
462 ind: super::name::NameId,
463 np: usize,
464 args: &[ExprId],
465 ) -> Vec<usize> {
466 let name = self.s.names.display(ind);
467 let lifted: Vec<Term> = args.iter().map(|i| lift(&self.s, *i)).collect();
468 crate::type_checker::constructors_the_indices_allow(self.logos_ctx(), &name, np, &lifted)
469 }
470
471 fn check_elimination(
476 &mut self,
477 ind: super::name::NameId,
478 disc_ty: ExprId,
479 ret: ExprId,
480 ) -> KResult<()> {
481 let ds = self.infer(disc_ty, true)?;
482 let ds = self.whnf(ds)?;
483 let rs = self.infer(ret, true)?;
484 let rs = self.whnf(rs)?;
485 let (Node::Sort(dl), Node::Sort(rl)) = (self.s.node(ds), self.s.node(rs)) else {
486 bail!("a match whose scrutinee or result is not a type");
487 };
488 let levels = &mut self.s.levels;
489 if levels.is_sprop(dl) && !levels.is_sprop(rl) {
490 bail!(
491 "elimination of the definitionally-irrelevant proposition '{}' into a relevant sort is not allowed",
492 self.s.names.display(ind)
493 );
494 }
495 let zero = super::level::LevelId::ZERO;
496 let one = levels.succ(zero);
497 let large = if levels.is_sprop(dl) {
498 false
499 } else if levels.equiv(dl, zero) {
500 !levels.equiv(rl, zero) && !levels.is_sprop(rl)
501 } else if !levels.leq(one, dl) {
502 !levels.leq(rl, dl)
503 } else {
504 false
505 };
506 if large {
507 let name = self.s.names.display(ind);
508 if !crate::type_checker::is_subsingleton_prop(self.logos_ctx(), &name).unwrap_or(false)
509 {
510 bail!(
511 "large elimination of proposition '{name}' into a larger sort is not allowed"
512 );
513 }
514 }
515 Ok(())
516 }
517
518 pub(crate) fn check_fix_against(&mut self, value: ExprId, expected: ExprId) -> KResult<bool> {
524 let Node::Fix(body) = self.s.node(value) else {
525 return Ok(false);
526 };
527 let Term::Fix { name, body: b } = lift(&self.s, value) else {
528 unreachable!()
529 };
530 if crate::termination::check_termination(self.logos_ctx(), &name, &b).is_err() {
531 return Ok(false);
532 }
533 let x = self.push_local(expected, None);
534 let opened = self.s.instantiate1(body, x);
535 match self.infer(opened, false) {
536 Ok(got) => self.conforms(got, expected),
537 Err(_) => Ok(false),
538 }
539 }
540
541 fn infer_fix(&mut self, fix: ExprId, body: ExprId, infer_only: bool) -> KResult<ExprId> {
544 let ty = self.fix_type(body)?;
545 if !infer_only {
546 let Term::Fix { name, body: b } = lift(&self.s, fix) else {
547 unreachable!()
548 };
549 crate::termination::check_termination(self.logos_ctx(), &name, &b)
550 .map_err(|e| KernelFailure::reject(e.to_string()))?;
551 let x = self.push_local(ty, None);
552 let opened = self.s.instantiate1(body, x);
553 let got = self.infer(opened, false)?;
554 if !self.is_def_eq(got, ty)? {
555 bail!(
556 "a fixpoint's body has type {} where its binders and motive read {}",
557 self.show(got),
558 self.show(ty)
559 );
560 }
561 }
562 Ok(ty)
563 }
564
565 fn infer_mutual_fix(&mut self, ds: ExprId, index: u32, infer_only: bool) -> KResult<ExprId> {
566 let bodies = self.s.seq_items(ds);
567 if bodies.is_empty() || index as usize >= bodies.len() {
568 bail!("mutual fixpoint with an empty block or out-of-range index");
569 }
570 let mut types = Vec::with_capacity(bodies.len());
573 let n = bodies.len() as u32;
574 for b in bodies.iter() {
575 if self.s.loose_bvar_range(*b) > n {
576 bail!("a mutual fixpoint member mentions a variable outside its block");
577 }
578 types.push(self.fix_type(*b)?);
579 }
580 if !infer_only {
581 let whole = self.s.intern(Node::MutualFix(ds, index));
582 let Term::MutualFix { defs, .. } = lift(&self.s, whole) else {
583 unreachable!()
584 };
585 crate::termination::check_termination_mutual(self.logos_ctx(), &defs)
586 .map_err(|e| KernelFailure::reject(e.to_string()))?;
587 let xs: Vec<ExprId> = types.iter().map(|t| self.push_local(*t, None)).collect();
588 for b in bodies.iter() {
589 let opened = self.s.instantiate_rev(*b, &xs);
590 self.infer(opened, false)?;
591 }
592 }
593 Ok(types[index as usize])
594 }
595
596 fn fix_type(&mut self, body: ExprId) -> KResult<ExprId> {
601 let mut locals = Vec::new();
602 let mut cur = body;
603 while let Node::Lam(a, b) = self.s.node(cur) {
604 let a = self.s.instantiate_rev(a, &locals);
605 self.infer(a, true)?;
606 let x = self.push_local(a, None);
607 locals.push(x);
608 cur = b;
609 }
610 let inner = self.s.instantiate_rev(cur, &locals);
611 let (head, args) = self.s.app_args(inner);
612 let result = match self.s.node(head) {
613 Node::Match(d, m, _) => {
614 let mut ty = self.match_result_type(d, m)?;
615 for a in args.iter() {
616 let w = self.whnf(ty)?;
617 let Node::Pi(_, b) = self.s.node(w) else {
618 bail!(
619 "a convoy applies a match whose type {} is not a function",
620 self.show(w)
621 )
622 };
623 ty = self.s.instantiate1(b, *a);
624 }
625 ty
626 }
627 _ => self.infer(inner, true)?,
628 };
629 Ok(self.mk_pi(&locals, result))
630 }
631
632 fn match_result_type(&mut self, d: ExprId, m: ExprId) -> KResult<ExprId> {
635 let mt = self.infer(m, true)?;
636 let mtw = self.whnf(mt)?;
637 if matches!(self.s.node(mtw), Node::Sort(_)) {
638 return Ok(m);
639 }
640 let dt = self.infer(d, true)?;
641 let indices = match self.inductive_app(dt) {
642 Ok((ind, _, args)) => match &self.env.get(ind).expect("an inductive").kind {
643 Kind::Induct(i) => args[i.num_params as usize..].to_vec(),
644 _ => Vec::new(),
645 },
646 Err(_) => Vec::new(),
647 };
648 let applied = self.s.mk_app_n(m, &indices);
649 let applied = self.s.app(applied, d);
650 self.whnf_core(applied, false)
651 }
652
653 pub(crate) fn reduce_match(&mut self, e: ExprId) -> KResult<Option<ExprId>> {
655 if self.theory != Theory::Logos {
656 return Ok(None);
657 }
658 let Node::Match(d, m, cs) = self.s.node(e) else {
659 unreachable!()
660 };
661 let dw = self.whnf(d)?;
662 let cases = self.s.seq_items(cs);
663 let Some((ctor, fields)) = self.constructor_view(dw)? else {
664 if cases.len() == 1 {
665 if let Some(k) = self.k_constructor_form(dw)? {
666 return Ok(Some(self.s.intern(Node::Match(k, m, cs))));
667 }
668 }
669 return Ok(None);
670 };
671 let Some(pos) = self.case_position(dw, ctor, cases.len())? else {
672 return Ok(None);
673 };
674 let Some(case) = cases.get(pos).copied() else {
675 return Ok(None);
676 };
677 Ok(Some(self.s.mk_app_n(case, &fields)))
678 }
679
680 fn case_position(
683 &mut self,
684 v: ExprId,
685 ctor: super::name::NameId,
686 num_cases: usize,
687 ) -> KResult<Option<usize>> {
688 let Some(c) = self.env.get(ctor).cloned() else {
689 return Ok(None);
690 };
691 let Kind::Ctor(ci) = &c.kind else {
692 return Ok(None);
693 };
694 let Some(ic) = self.env.get(ci.induct).cloned() else {
695 return Ok(None);
696 };
697 let Kind::Induct(info) = &ic.kind else {
698 return Ok(None);
699 };
700 if num_cases == info.ctors.len() {
701 return Ok(Some(ci.cidx as usize));
702 }
703 let (head, actual) = self.s.app_args(v);
705 let Node::Const(_, ls) = self.s.node(head) else {
706 return Ok(None);
707 };
708 let us: Vec<_> = self.s.levels.get_list(ls).to_vec();
709 let mut ty = self.s.instantiate_lparams(c.ty, &c.lparams, &us);
710 for a in actual.iter() {
711 let w = self.whnf(ty)?;
712 let Node::Pi(_, b) = self.s.node(w) else {
713 return Ok(None);
714 };
715 ty = self.s.instantiate1(b, *a);
716 }
717 let w = self.whnf(ty)?;
718 let (_, result) = self.s.app_args(w);
719 let np = info.num_params as usize;
720 let allowed = self.allowed_constructors(ci.induct, np, &result);
721 if allowed.len() != num_cases {
722 return Ok(None);
723 }
724 Ok(allowed.iter().position(|i| *i == ci.cidx as usize))
725 }
726
727 fn k_constructor_form(&mut self, proof: ExprId) -> KResult<Option<ExprId>> {
732 let pt = match self.infer(proof, true) {
733 Ok(t) => self.whnf(t)?,
734 Err(_) => return Ok(None),
735 };
736 let (head, args) = self.s.app_args(pt);
737 let Node::Const(ind, ls) = self.s.node(head) else {
738 return Ok(None);
739 };
740 let name = self.s.names.display(ind);
741 let ctx = self.logos_ctx();
742 let unit = ctx.unit_constructor(&name).map(str::to_string);
743 let Some(ctor) = ctx
744 .k_constructor(&name)
745 .map(str::to_string)
746 .or(unit.clone())
747 else {
748 return Ok(None);
749 };
750 let np = ctx.inductive_num_params(&name);
751 if unit.is_none() {
752 let sort = self.infer(pt, true)?;
753 let sort = self.whnf(sort)?;
754 let Node::Sort(l) = self.s.node(sort) else {
755 return Ok(None);
756 };
757 if self.s.levels.is_sprop(l) || l != super::level::LevelId::ZERO {
758 return Ok(None);
759 }
760 }
761 let c = self.s.names.str(super::name::NameId::ANON, &ctor);
762 let value = self.s.cnst(c, ls);
763 let value = self.s.mk_app_n(value, &args[..np.min(args.len())]);
764 let vt = match self.infer(value, true) {
765 Ok(t) => t,
766 Err(_) => return Ok(None),
767 };
768 Ok(self.is_def_eq(pt, vt)?.then_some(value))
769 }
770
771 fn constructor_view(
774 &mut self,
775 v: ExprId,
776 ) -> KResult<Option<(super::name::NameId, Vec<ExprId>)>> {
777 if let Some(c) = self.int_constructor_form(v) {
778 return self.constructor_view(c);
779 }
780 if let Node::NatLit(n) = self.s.node(v) {
781 let n = self.s.nat(n).clone();
782 if !self.reads_nat_literal(&n) {
783 return Ok(None);
784 }
785 if n.is_zero() {
786 let z = self.s.names.str(super::name::NameId::ANON, "Zero");
787 return Ok(Some((z, Vec::new())));
788 }
789 let pred = self.s.nat_lit(n.sub(&super::nat::Nat::from_u64(1)));
790 let succ = self.s.names.str(super::name::NameId::ANON, "Succ");
791 return Ok(Some((succ, vec![pred])));
792 }
793 let (head, args) = self.s.app_args(v);
794 let Node::Const(n, _) = self.s.node(head) else {
795 return Ok(None);
796 };
797 let Some(c) = self.env.get(n) else {
798 return Ok(None);
799 };
800 let Kind::Ctor(ci) = &c.kind else {
801 return Ok(None);
802 };
803 let np = ci.num_params as usize;
804 if args.len() < np {
805 return Ok(None);
806 }
807 Ok(Some((n, args[np..].to_vec())))
808 }
809
810 #[inline(never)]
814 pub(crate) fn whnf_core_logos_app(&mut self, e: ExprId, cheap_proj: bool) -> KResult<ExprId> {
815 match self.reduce_logos_app(e)? {
816 Some(r) => self.whnf_core(r, cheap_proj),
817 None => Ok(e),
818 }
819 }
820
821 pub(crate) fn reduce_logos_app(&mut self, e: ExprId) -> KResult<Option<ExprId>> {
826 if self.env.quot_init {
827 if let Some(r) = self.quot_reduce_rec(e)? {
828 return Ok(Some(r));
829 }
830 }
831 if let Some(r) = self.reduce_observational(e)? {
832 return Ok(Some(r));
833 }
834 let (head, args) = self.s.app_args(e);
835 match self.s.node(head) {
836 Node::Fix(body) => {
837 let Some(col) = self.structural_column(head)? else {
838 return Ok(None);
839 };
840 let Some(arg) = args.get(col).copied() else {
841 return Ok(None);
842 };
843 let aw = self.whnf(arg)?;
844 if self.constructor_view(aw)?.is_none() {
845 return Ok(None);
846 }
847 let unrolled = self.s.instantiate1(body, head);
848 Ok(Some(self.s.mk_app_n(unrolled, &args)))
849 }
850 Node::MutualFix(ds, index) => {
851 let Some(arg) = args.first().copied() else {
853 return Ok(None);
854 };
855 let aw = self.whnf(arg)?;
856 if self.constructor_view(aw)?.is_none() {
857 return Ok(None);
858 }
859 let bodies = self.s.seq_items(ds);
860 let members: Vec<ExprId> = (0..bodies.len() as u32)
861 .map(|i| self.s.intern(Node::MutualFix(ds, i)))
862 .collect();
863 let unrolled = self.s.instantiate_rev(bodies[index as usize], &members);
864 Ok(Some(self.s.mk_app_n(unrolled, &args)))
865 }
866 _ => Ok(None),
867 }
868 }
869
870 fn structural_column(&mut self, fix: ExprId) -> KResult<Option<usize>> {
872 if let Some(c) = self.logos.as_ref().and_then(|l| l.columns.get(&fix)) {
873 return Ok(*c);
874 }
875 let Term::Fix { body, .. } = lift(&self.s, fix) else {
876 unreachable!("a single fixpoint")
877 };
878 let col = crate::termination::structural_columns(self.logos_ctx(), &body)
879 .first()
880 .copied();
881 self.logos
882 .as_mut()
883 .expect("LOGOS state")
884 .columns
885 .insert(fix, col);
886 Ok(col)
887 }
888
889 pub(crate) fn reduce_builtin(&mut self, e: ExprId) -> KResult<Option<ExprId>> {
893 let Some((n, _)) = self.s.const_head(e) else {
894 return Ok(None);
895 };
896 if self.s.app_num_args(e) == 0 {
897 return Ok(None);
898 }
899 let name = self.s.names.display(n);
900 let ctx = self.logos_ctx();
901 if !(crate::reduction::computes_by_name(&name)
902 || crate::reduction::accelerates_nat_order(ctx, &name))
903 {
904 return Ok(None);
905 }
906 if let Some(r) = self.logos.as_ref().and_then(|l| l.builtins.get(&e)) {
907 return Ok(*r);
908 }
909 let term = lift(&self.s, e);
910 let reduced = crate::reduction::normalize(self.logos_ctx(), &term);
911 let r = if reduced == term {
912 None
913 } else {
914 Some(lower_open(&mut self.s, &reduced).map_err(|e| {
915 KernelFailure::reject(format!("a builtin's answer is not a kernel term: {e:?}"))
916 })?)
917 };
918 self.logos
919 .as_mut()
920 .expect("LOGOS state")
921 .builtins
922 .insert(e, r);
923 Ok(r)
924 }
925
926 pub(crate) fn reduce_certified_program(&mut self, e: ExprId) -> KResult<Option<ExprId>> {
934 let Some((name, levels)) = self.s.const_head(e) else {
935 return Ok(None);
936 };
937 let arity = self.s.app_num_args(e);
938 if levels != LevelsId::EMPTY || !(1..=2).contains(&arity) {
939 return Ok(None);
940 }
941 let Some(certified) = self.certified_program_of(name, arity) else {
942 return Ok(None);
943 };
944 let (shape, program) = &*certified;
945 let (_, args) = self.s.app_args(e);
946 let (x, y) = match shape {
947 ProgramShape::Binary => (args[0], args[1]),
948 ProgramShape::Pair { pair, parameters } => {
949 let parameters = *parameters;
950 let observed = self.whnf(args[0])?;
951 let (constructor, fields) = self.s.app_args(observed);
952 let Node::Const(c, _) = self.s.node(constructor) else {
953 return Ok(None);
954 };
955 if fields.len() != parameters + 2 || self.s.names.display(c) != *pair {
956 return Ok(None);
957 }
958 (fields[parameters], fields[parameters + 1])
959 }
960 };
961 let Some((x, y)) = self.nat_bin_args(x, y)? else {
962 return Ok(None);
963 };
964 let Some(value) =
965 crate::reduction::certified_application(program, &x.to_bigint(), &y.to_bigint())
966 else {
967 return Ok(None);
968 };
969 lower_open(&mut self.s, &value).map(Some).map_err(|e| {
970 KernelFailure::reject(format!(
971 "a certified program's value is not a kernel term: {e:?}"
972 ))
973 })
974 }
975
976 fn certified_program_of(
979 &mut self,
980 name: super::name::NameId,
981 arity: usize,
982 ) -> Option<Rc<(ProgramShape, CertifiedProgram)>> {
983 if let Some(known) = self
984 .logos
985 .as_ref()
986 .and_then(|l| l.programs.get(&(name, arity)))
987 {
988 return known.clone();
989 }
990 let spelled = self.s.names.display(name);
991 let ctx = self.logos_ctx();
992 let known = crate::reduction::program_shape(ctx, &spelled, arity).and_then(|shape| {
993 let program = crate::reduction::certified_program(ctx, &spelled)?;
994 let division = matches!(program, CertifiedProgram::Division { .. });
995 (division == matches!(shape, ProgramShape::Pair { .. }))
996 .then(|| Rc::new((shape, program)))
997 });
998 self.logos
999 .as_mut()
1000 .expect("LOGOS state")
1001 .programs
1002 .insert((name, arity), known.clone());
1003 known
1004 }
1005
1006 pub(crate) fn record_irrelevant_levels(&mut self, name: super::name::NameId, mask: u64) {
1007 if mask != 0 {
1008 self.logos
1009 .as_mut()
1010 .expect("LOGOS state")
1011 .irrelevant
1012 .insert(name, mask);
1013 }
1014 }
1015
1016 fn cumulative_instances(&mut self, t: ExprId, s: ExprId) -> KResult<bool> {
1020 let (Some((n, tl)), Some((m, sl))) = (self.s.const_head(t), self.s.const_head(s)) else {
1021 return Ok(false);
1022 };
1023 if n != m || tl == sl {
1024 return Ok(false);
1025 }
1026 let Some(mask) = self
1027 .logos
1028 .as_ref()
1029 .and_then(|l| l.irrelevant.get(&n))
1030 .copied()
1031 else {
1032 return Ok(false);
1033 };
1034 let num_params = match self.env.get(n).map(|c| &c.kind) {
1035 Some(Kind::Induct(info)) => info.num_params,
1036 Some(Kind::Ctor(ci)) => ci.num_params,
1037 _ => return Ok(false),
1038 };
1039 let (a, b) = (
1040 self.s.levels.get_list(tl).to_vec(),
1041 self.s.levels.get_list(sl).to_vec(),
1042 );
1043 if a.len() != b.len() {
1044 return Ok(false);
1045 }
1046 for (i, (u, v)) in a.iter().zip(&b).enumerate() {
1047 let irrelevant = i < 64 && (mask >> i) & 1 == 1;
1048 if !irrelevant && !self.s.levels.equiv(*u, *v) {
1049 return Ok(false);
1050 }
1051 }
1052 let (_, targs) = self.s.app_args(t);
1053 let (_, sargs) = self.s.app_args(s);
1054 if targs.len() != sargs.len() || targs.len() < num_params as usize {
1055 return Ok(false);
1056 }
1057 for (x, y) in targs.iter().zip(&sargs) {
1058 if !self.is_def_eq(*x, *y)? {
1059 return Ok(false);
1060 }
1061 }
1062 Ok(true)
1063 }
1064
1065 pub(super) fn int_constructor_form(&mut self, e: ExprId) -> Option<ExprId> {
1069 use logicaffeine_base::BigInt;
1070 let Node::Lit(l) = self.s.node(e) else {
1071 return None;
1072 };
1073 let value = match self.s.lit_value(l) {
1074 Lit::Int(i) => BigInt::from_i64(*i),
1075 Lit::BigInt(b) => b.clone(),
1076 _ => return None,
1077 };
1078 if !self.logos.as_ref()?.numerals.int {
1079 return None;
1080 }
1081 let (ctor, field) = if value.is_negative() {
1082 ("Int_negSucc", value.abs().sub(&BigInt::from_i64(1)))
1083 } else {
1084 ("Int_ofNat", value)
1085 };
1086 let c = self.global(ctor);
1087 let n = self.s.nat_lit(super::nat::Nat::from_bigint(&field));
1088 Some(self.s.app(c, n))
1089 }
1090
1091 pub(crate) fn logos_eta_struct(&mut self, t: ExprId, s: ExprId) -> KResult<bool> {
1100 let Some((f, ls)) = self.s.const_head(s) else {
1101 return Ok(false);
1102 };
1103 if self.s.const_head(t).is_some_and(|(g, _)| g == f) {
1104 return Ok(false);
1105 }
1106 let constructor = super::logos::component(&self.s, f);
1107 let Some(state) = self.logos.as_ref() else {
1108 return Ok(false);
1109 };
1110 let Some((_, info)) = state.ctx.struct_of_constructor(&constructor) else {
1111 return Ok(false);
1112 };
1113 let (num_params, projections) = (info.num_params, info.projections.clone());
1114 let (_, args) = self.s.app_args(s);
1115 if args.len() != num_params + projections.len() {
1116 return Ok(false);
1117 }
1118 for (i, projection) in projections.iter().enumerate() {
1119 let p = self.s.names.str(super::name::NameId::ANON, projection);
1120 if self.env.get(p).is_none() {
1121 return Ok(false);
1122 }
1123 let head = self.s.cnst(p, ls);
1124 let applied = self.s.mk_app_n(head, &args[..num_params]);
1125 let applied = self.s.app(applied, t);
1126 if !self.is_def_eq(args[num_params + i], applied)? {
1127 return Ok(false);
1128 }
1129 }
1130 Ok(true)
1131 }
1132
1133 pub(crate) fn logos_def_eq(&mut self, t: ExprId, s: ExprId) -> KResult<bool> {
1134 if self.cumulative_instances(t, s)? {
1135 return Ok(true);
1136 }
1137 if let Some(tc) = self.int_constructor_form(t) {
1138 let sc = self.int_constructor_form(s).unwrap_or(s);
1139 return self.is_def_eq(tc, sc);
1140 }
1141 if let Some(sc) = self.int_constructor_form(s) {
1142 return self.is_def_eq(t, sc);
1143 }
1144 match (self.s.node(t), self.s.node(s)) {
1145 (Node::Match(d1, m1, c1), Node::Match(d2, m2, c2)) => {
1146 let (cs1, cs2) = (self.s.seq_items(c1), self.s.seq_items(c2));
1147 if cs1.len() != cs2.len() || !self.is_def_eq(d1, d2)? || !self.is_def_eq(m1, m2)? {
1148 return Ok(false);
1149 }
1150 for (a, b) in cs1.iter().zip(&cs2) {
1151 if !self.is_def_eq(*a, *b)? {
1152 return Ok(false);
1153 }
1154 }
1155 Ok(true)
1156 }
1157 (Node::Fix(b1), Node::Fix(b2)) => {
1158 let ty = self.infer(t, true)?;
1159 let x = self.push_local(ty, None);
1160 let (o1, o2) = (self.s.instantiate1(b1, x), self.s.instantiate1(b2, x));
1161 self.is_def_eq(o1, o2)
1162 }
1163 (Node::MutualFix(ds1, i1), Node::MutualFix(ds2, i2)) if i1 == i2 => {
1164 let (bs1, bs2) = (self.s.seq_items(ds1), self.s.seq_items(ds2));
1165 if bs1.len() != bs2.len() {
1166 return Ok(false);
1167 }
1168 let mut xs = Vec::with_capacity(bs1.len());
1169 for b in bs1.iter() {
1170 let ty = self.fix_type(*b)?;
1171 xs.push(self.push_local(ty, None));
1172 }
1173 for (a, b) in bs1.iter().zip(&bs2) {
1174 let (oa, ob) = (
1175 self.s.instantiate_rev(*a, &xs),
1176 self.s.instantiate_rev(*b, &xs),
1177 );
1178 if !self.is_def_eq(oa, ob)? {
1179 return Ok(false);
1180 }
1181 }
1182 Ok(true)
1183 }
1184 _ => Ok(false),
1185 }
1186 }
1187
1188 pub(crate) fn is_subtype(&mut self, a: ExprId, b: ExprId) -> KResult<bool> {
1191 if a == b {
1192 return Ok(true);
1193 }
1194 let (aw, bw) = (self.whnf(a)?, self.whnf(b)?);
1195 match (self.s.node(aw), self.s.node(bw)) {
1196 (Node::Sort(u), Node::Sort(v)) => Ok(self.s.levels.leq(u, v)),
1197 (Node::Pi(d1, c1), Node::Pi(d2, c2)) => {
1198 if !self.is_def_eq(d1, d2)? {
1199 return Ok(false);
1200 }
1201 let x = self.push_local(d2, None);
1202 let (c1, c2) = (self.s.instantiate1(c1, x), self.s.instantiate1(c2, x));
1203 self.is_subtype(c1, c2)
1204 }
1205 _ => self.is_def_eq(aw, bw),
1206 }
1207 }
1208}