Skip to main content

oxilean_kernel/env/
quot.rs

1//! In-kernel construction of the four `#QUOT` primitives.
2//!
3//! This module owns the canonical, universe-polymorphic types of `Quot`,
4//! `Quot.mk`, `Quot.lift` and `Quot.ind`, mirroring Lean's
5//! `src/kernel/quot.cpp`. The kernel *never* trusts a caller-supplied type for
6//! these primitives: [`Environment::add_quot`] builds them here and installs
7//! all four atomically, exactly once, and only after `Eq` is present with its
8//! expected inductive shape (needed because `Quot.lift`'s soundness hypothesis
9//! mentions `Eq`).
10//!
11//! The canonical types (with binder info per Lean):
12//!
13//! ```text
14//! Quot      : {α : Sort u} → (α → α → Prop) → Sort u
15//! Quot.mk   : {α : Sort u} → (r : α → α → Prop) → α → Quot r
16//! Quot.lift : {α : Sort u} → {r : α → α → Prop} → {β : Sort v} →
17//!               (f : α → β) → (∀ a b, r a b → f a = f b) → Quot r → β
18//! Quot.ind  : {α : Sort u} → {r : α → α → Prop} → {β : Quot r → Prop} →
19//!               (∀ a, β (Quot.mk r a)) → ∀ q : Quot r, β q
20//! ```
21//!
22//! `Quot.sound` is **not** a `#QUOT` primitive: in Lean it is a plain `axiom`
23//! declared in `Init.Prelude` and it arrives in exports as a regular axiom
24//! record. Its canonical type is nevertheless owned by the kernel (see
25//! [`canonical_quot_sound_type`]) so that a replayer can verify the exported
26//! axiom instead of trusting it:
27//!
28//! ```text
29//! Quot.sound : ∀ {α : Sort u} {r : α → α → Prop} {a b : α},
30//!                r a b → Quot.mk r a = Quot.mk r b
31//! ```
32
33use crate::declaration::{ConstantInfo, ConstantVal, QuotKind, QuotVal};
34use crate::env::EnvError;
35use crate::Node;
36use crate::{BinderInfo, Environment, Expr, Level, Name};
37use std::rc::Rc;
38
39/// Hierarchical name `Quot`.
40fn name_quot() -> Name {
41    Name::str("Quot")
42}
43/// Hierarchical name `Quot.mk`.
44fn name_quot_mk() -> Name {
45    Name::str("Quot").append_str("mk")
46}
47/// Hierarchical name `Quot.lift`.
48fn name_quot_lift() -> Name {
49    Name::str("Quot").append_str("lift")
50}
51/// Hierarchical name `Quot.ind`.
52fn name_quot_ind() -> Name {
53    Name::str("Quot").append_str("ind")
54}
55/// Hierarchical name `Eq`.
56fn name_eq() -> Name {
57    Name::str("Eq")
58}
59
60/// `Prop` = `Sort 0`.
61fn prop() -> Expr {
62    Expr::Sort(Level::zero())
63}
64/// `Sort u` for a level parameter named `u`.
65fn sort_param(u: &str) -> Expr {
66    Expr::Sort(Level::param(Name::str(u)))
67}
68/// A non-dependent arrow `dom → cod` (a `Pi` whose body ignores the binder).
69///
70/// The caller must supply `cod` already shifted for the extra binder if it
71/// mentions outer variables (here it never does, so no shift is needed).
72fn arrow(dom: Expr, cod: Expr) -> Expr {
73    Expr::Pi(
74        BinderInfo::Default,
75        Name::str("_"),
76        Node::new(dom),
77        Node::new(cod),
78    )
79}
80
81/// The relation type `α → α → Prop`, where `α` is `Expr::BVar(alpha_idx)` in the
82/// *current* context (i.e. the context in which this whole expression sits).
83///
84/// The two nested binders shift `α` outward by one each, so the caller passes
85/// the index of `α` as seen from the position where the relation type appears.
86fn rel_type(alpha_idx: u32) -> Expr {
87    // Innermost `Prop` ignores both binders.
88    let body = prop();
89    // `b : α` — under one extra binder, α is `alpha_idx + 1`.
90    let inner = Expr::Pi(
91        BinderInfo::Default,
92        Name::str("b"),
93        Node::new(Expr::BVar(alpha_idx + 1)),
94        Node::new(body),
95    );
96    // `a : α` — α is `alpha_idx` here.
97    Expr::Pi(
98        BinderInfo::Default,
99        Name::str("a"),
100        Node::new(Expr::BVar(alpha_idx)),
101        Node::new(inner),
102    )
103}
104
105/// The canonical type of `Quot`:
106/// `{α : Sort u} → (α → α → Prop) → Sort u`.
107fn quot_type() -> Expr {
108    // Body of the `r` binder: `Sort u`. Level params are name-based, so no
109    // de Bruijn shifting is needed for the sort.
110    let body = sort_param("u");
111    // `(r : α → α → Prop)` — α is BVar(0) at this depth.
112    let r_pi = Expr::Pi(
113        BinderInfo::Default,
114        Name::str("r"),
115        Node::new(rel_type(0)),
116        Node::new(body),
117    );
118    // `{α : Sort u}`.
119    Expr::Pi(
120        BinderInfo::Implicit,
121        Name::str("α"),
122        Node::new(sort_param("u")),
123        Node::new(r_pi),
124    )
125}
126
127/// `@Quot α r` where `α = BVar(alpha_idx)` and `r = BVar(r_idx)`.
128fn quot_app(alpha_idx: u32, r_idx: u32) -> Expr {
129    Expr::App(
130        Node::new(Expr::App(
131            Node::new(Expr::Const(name_quot(), vec![Level::param(Name::str("u"))])),
132            Node::new(Expr::BVar(alpha_idx)),
133        )),
134        Node::new(Expr::BVar(r_idx)),
135    )
136}
137
138/// The canonical type of `Quot.mk`:
139/// `{α : Sort u} → (r : α → α → Prop) → α → Quot r`.
140fn quot_mk_type() -> Expr {
141    // Context after binding α (idx grows as 0 on the outside):
142    //   α (BVar increases inward), r, (element).
143    // Innermost result `@Quot α r`. In the context of the element binder:
144    //   element = BVar(0), r = BVar(1), α = BVar(2).
145    let result = quot_app(2, 1);
146    // `(a : α)` — the quotiented element. α is BVar(1) here.
147    let elem_pi = Expr::Pi(
148        BinderInfo::Default,
149        Name::str("a"),
150        Node::new(Expr::BVar(1)),
151        Node::new(result),
152    );
153    // `(r : α → α → Prop)` — α is BVar(0) here.
154    let r_pi = Expr::Pi(
155        BinderInfo::Default,
156        Name::str("r"),
157        Node::new(rel_type(0)),
158        Node::new(elem_pi),
159    );
160    // `{α : Sort u}`.
161    Expr::Pi(
162        BinderInfo::Implicit,
163        Name::str("α"),
164        Node::new(sort_param("u")),
165        Node::new(r_pi),
166    )
167}
168
169/// `@Eq.{lvl} ty lhs rhs`.
170fn eq_app(lvl: Level, ty: Expr, lhs: Expr, rhs: Expr) -> Expr {
171    Expr::App(
172        Node::new(Expr::App(
173            Node::new(Expr::App(
174                Node::new(Expr::Const(name_eq(), vec![lvl])),
175                Node::new(ty),
176            )),
177            Node::new(lhs),
178        )),
179        Node::new(rhs),
180    )
181}
182
183/// The canonical type of `Quot.lift`:
184/// `{α : Sort u} → {r : α → α → Prop} → {β : Sort v} → (f : α → β) →
185///    (∀ a b, r a b → f a = f b) → Quot r → β`.
186fn quot_lift_type() -> Expr {
187    let lvl_v = Level::param(Name::str("v"));
188    // Binders, outermost → innermost: α, r, β, f, h, q.
189    //
190    // Build from the innermost result outward.
191    //
192    // Result type `β`. In the context of `q` binder:
193    //   q = 0, h = 1, f = 2, β = 3, r = 4, α = 5.
194    let result = Expr::BVar(3);
195    // `Quot r` — the `q` binder domain. In the context *before* `q` (i.e. inside
196    // the `h` binder body position): h = 0, f = 1, β = 2, r = 3, α = 4.
197    let q_dom = quot_app(4, 3);
198    let q_pi = Expr::Pi(
199        BinderInfo::Default,
200        Name::str("q"),
201        Node::new(q_dom),
202        Node::new(result),
203    );
204    // The soundness hypothesis `h : ∀ (a b : α), r a b → f a = f b`.
205    //
206    // In the context before `h`: f = 0, β = 1, r = 2, α = 3.
207    // We add binders a, b, then the arrow `r a b → f a = f b`.
208    //
209    // In the context after binding a, b: b = 0, a = 1, f = 2, β = 3, r = 4,
210    // α = 5.
211    //
212    // Domain `r a b`:
213    let r_a_b = Expr::App(
214        Node::new(Expr::App(
215            Node::new(Expr::BVar(4)), // r
216            Node::new(Expr::BVar(1)), // a
217        )),
218        Node::new(Expr::BVar(0)), // b
219    );
220    // Codomain `f a = f b`. It sits *under* the non-dependent arrow binder
221    // (`_ : r a b`), so every variable shifts by 1: _ = 0, b = 1, a = 2, f = 3,
222    // β = 4, r = 5, α = 6.
223    let eq_fa_fb = {
224        let f_a = Expr::App(Node::new(Expr::BVar(3)), Node::new(Expr::BVar(2)));
225        let f_b = Expr::App(Node::new(Expr::BVar(3)), Node::new(Expr::BVar(1)));
226        eq_app(lvl_v.clone(), Expr::BVar(4), f_a, f_b)
227    };
228    let impl_arrow = arrow(r_a_b, eq_fa_fb);
229    // `∀ (b : α), r a b → f a = f b`. b domain is α. In the context after
230    // binding a: α = BVar(4) (α=3 before `a`, +1 for `a`).
231    let b_pi = Expr::Pi(
232        BinderInfo::Default,
233        Name::str("b"),
234        Node::new(Expr::BVar(4)),
235        Node::new(impl_arrow),
236    );
237    // `∀ (a : α), ...`. a domain is α. Before `a`, α = BVar(3).
238    let a_pi = Expr::Pi(
239        BinderInfo::Default,
240        Name::str("a"),
241        Node::new(Expr::BVar(3)),
242        Node::new(b_pi),
243    );
244    let h_pi = Expr::Pi(
245        BinderInfo::Default,
246        Name::str("h"),
247        Node::new(a_pi),
248        Node::new(q_pi),
249    );
250    // `(f : α → β)`. In the context before `f`: β = 0, r = 1, α = 2.
251    // Domain `α → β`: α = BVar(2); β under the `α` binder of the arrow = BVar(1)
252    // shifted by the arrow binder → β = BVar(1). Rebuild explicitly.
253    let f_dom = Expr::Pi(
254        BinderInfo::Default,
255        Name::str("_"),
256        Node::new(Expr::BVar(2)), // α
257        Node::new(Expr::BVar(1)), // β (shifted by the arrow binder)
258    );
259    let f_pi = Expr::Pi(
260        BinderInfo::Default,
261        Name::str("f"),
262        Node::new(f_dom),
263        Node::new(h_pi),
264    );
265    // `{β : Sort v}`. Before `β`: r = 0, α = 1.
266    let beta_pi = Expr::Pi(
267        BinderInfo::Implicit,
268        Name::str("β"),
269        Node::new(Expr::Sort(lvl_v)),
270        Node::new(f_pi),
271    );
272    // `{r : α → α → Prop}`. Before `r`: α = 0.
273    let r_pi = Expr::Pi(
274        BinderInfo::Implicit,
275        Name::str("r"),
276        Node::new(rel_type(0)),
277        Node::new(beta_pi),
278    );
279    // `{α : Sort u}`.
280    Expr::Pi(
281        BinderInfo::Implicit,
282        Name::str("α"),
283        Node::new(sort_param("u")),
284        Node::new(r_pi),
285    )
286}
287
288/// The canonical type of `Quot.ind`:
289/// `{α : Sort u} → {r : α → α → Prop} → {β : Quot r → Prop} →
290///    (∀ a, β (Quot.mk r a)) → ∀ q : Quot r, β q`.
291fn quot_ind_type() -> Expr {
292    // Binders, outermost → innermost: α, r, β, m (minor premise), q.
293    //
294    // Result `β q`. In the context of `q`: q = 0, m = 1, β = 2, r = 3, α = 4.
295    let result = Expr::App(Node::new(Expr::BVar(2)), Node::new(Expr::BVar(0)));
296    // `q : Quot r`. Before `q`: m = 0, β = 1, r = 2, α = 3.
297    let q_dom = quot_app(3, 2);
298    let q_pi = Expr::Pi(
299        BinderInfo::Default,
300        Name::str("q"),
301        Node::new(q_dom),
302        Node::new(result),
303    );
304    // Minor premise `m : ∀ (a : α), β (Quot.mk r a)`.
305    // Before `m`: β = 0, r = 1, α = 2.
306    // Under the `a` binder: β = 1, r = 2, α = 3, a = 0.
307    //
308    // `Quot.mk α r a` = `@Quot.mk α r a`.
309    let mk_app = Expr::App(
310        Node::new(Expr::App(
311            Node::new(Expr::App(
312                Node::new(Expr::Const(
313                    name_quot_mk(),
314                    vec![Level::param(Name::str("u"))],
315                )),
316                Node::new(Expr::BVar(3)), // α
317            )),
318            Node::new(Expr::BVar(2)), // r
319        )),
320        Node::new(Expr::BVar(0)), // a
321    );
322    // `β (Quot.mk α r a)` — β = BVar(1).
323    let beta_mk = Expr::App(Node::new(Expr::BVar(1)), Node::new(mk_app));
324    // `∀ (a : α), β (Quot.mk r a)`. a domain α = BVar(2) before `a`.
325    let a_pi = Expr::Pi(
326        BinderInfo::Default,
327        Name::str("a"),
328        Node::new(Expr::BVar(2)),
329        Node::new(beta_mk),
330    );
331    let m_pi = Expr::Pi(
332        BinderInfo::Default,
333        Name::str("m"),
334        Node::new(a_pi),
335        Node::new(q_pi),
336    );
337    // `{β : Quot r → Prop}`. Before `β`: r = 0, α = 1.
338    // The domain `Quot r → Prop` = `Pi(_ : Quot r, Prop)`. The `Quot r` is the
339    // *domain* of the arrow, evaluated in the same context (NOT under the `_`
340    // binder), so α = BVar(1), r = BVar(0). Only the codomain `Prop` sits under
341    // the `_` binder (and it ignores it).
342    let beta_dom = Expr::Pi(
343        BinderInfo::Default,
344        Name::str("_"),
345        Node::new(quot_app(1, 0)),
346        Node::new(prop()),
347    );
348    let beta_pi = Expr::Pi(
349        BinderInfo::Implicit,
350        Name::str("β"),
351        Node::new(beta_dom),
352        Node::new(m_pi),
353    );
354    // `{r : α → α → Prop}`. Before `r`: α = 0.
355    let r_pi = Expr::Pi(
356        BinderInfo::Implicit,
357        Name::str("r"),
358        Node::new(rel_type(0)),
359        Node::new(beta_pi),
360    );
361    // `{α : Sort u}`.
362    Expr::Pi(
363        BinderInfo::Implicit,
364        Name::str("α"),
365        Node::new(sort_param("u")),
366        Node::new(r_pi),
367    )
368}
369
370/// The canonical universe-polymorphic type for a given quotient kind.
371///
372/// - [`QuotKind::Type`], [`QuotKind::Mk`] use only the level parameter `u`.
373/// - [`QuotKind::Lift`] additionally uses `v`.
374/// - [`QuotKind::Ind`] uses only `u`.
375///
376/// The returned type is closed (no loose bound variables) and is the exact
377/// type the kernel installs / requires for the primitive.
378pub fn canonical_quot_type(kind: QuotKind) -> Expr {
379    match kind {
380        QuotKind::Type => quot_type(),
381        QuotKind::Mk => quot_mk_type(),
382        QuotKind::Lift => quot_lift_type(),
383        QuotKind::Ind => quot_ind_type(),
384    }
385}
386
387/// The universe level parameters for a given quotient kind (in order).
388pub fn canonical_quot_level_params(kind: QuotKind) -> Vec<Name> {
389    match kind {
390        QuotKind::Type | QuotKind::Mk | QuotKind::Ind => vec![Name::str("u")],
391        QuotKind::Lift => vec![Name::str("u"), Name::str("v")],
392    }
393}
394
395/// The hierarchical name `Quot.sound` of the quotient soundness axiom.
396pub fn quot_sound_name() -> Name {
397    Name::str("Quot").append_str("sound")
398}
399
400/// The canonical type of the quotient soundness **axiom** `Quot.sound`
401/// (mirroring `Init.Prelude`):
402///
403/// ```text
404/// ∀ {α : Sort u} {r : α → α → Prop} {a b : α},
405///     r a b → Quot.mk r a = Quot.mk r b
406/// ```
407///
408/// `Quot.sound` is *not* installed by [`Environment::add_quot`] — in Lean it is
409/// an ordinary axiom, added after the `#QUOT` primitives. This constructor
410/// exists so a replayer can validate the exported axiom's type (up to
411/// definitional equality, after aligning the single level parameter) instead of
412/// accepting an arbitrary assertion under the trusted name `Quot.sound`.
413///
414/// The returned type is closed and universe-polymorphic in
415/// [`canonical_quot_sound_level_params`] (`[u]`).
416pub fn canonical_quot_sound_type() -> Expr {
417    let lvl_u = Level::param(Name::str("u"));
418    // Binders, outermost → innermost: α, r, a, b, then the anonymous
419    // hypothesis `_ : r a b`, with body `Quot.mk r a = Quot.mk r b`.
420    //
421    // Body context: _ = 0, b = 1, a = 2, r = 3, α = 4.
422    // `@Quot α r : Sort u` — the `Eq` instance's type argument.
423    let quot_alpha_r = quot_app(4, 3);
424    // `@Quot.mk α r x` for x ∈ {a, b}.
425    let mk = |x: u32| {
426        Expr::App(
427            Node::new(Expr::App(
428                Node::new(Expr::App(
429                    Node::new(Expr::Const(name_quot_mk(), vec![lvl_u.clone()])),
430                    Node::new(Expr::BVar(4)), // α
431                )),
432                Node::new(Expr::BVar(3)), // r
433            )),
434            Node::new(Expr::BVar(x)),
435        )
436    };
437    let (mk_a, mk_b) = (mk(2), mk(1));
438    let body = eq_app(lvl_u, quot_alpha_r, mk_a, mk_b);
439    // Hypothesis domain `r a b`. Context before the hypothesis binder:
440    // b = 0, a = 1, r = 2, α = 3.
441    let r_a_b = Expr::App(
442        Node::new(Expr::App(
443            Node::new(Expr::BVar(2)), // r
444            Node::new(Expr::BVar(1)), // a
445        )),
446        Node::new(Expr::BVar(0)), // b
447    );
448    let hyp_arrow = Expr::Pi(
449        BinderInfo::Default,
450        Name::str("_"),
451        Node::new(r_a_b),
452        Node::new(body),
453    );
454    // `{b : α}` — before `b`: a = 0, r = 1, α = 2.
455    let b_pi = Expr::Pi(
456        BinderInfo::Implicit,
457        Name::str("b"),
458        Node::new(Expr::BVar(2)),
459        Node::new(hyp_arrow),
460    );
461    // `{a : α}` — before `a`: r = 0, α = 1.
462    let a_pi = Expr::Pi(
463        BinderInfo::Implicit,
464        Name::str("a"),
465        Node::new(Expr::BVar(1)),
466        Node::new(b_pi),
467    );
468    // `{r : α → α → Prop}` — before `r`: α = 0.
469    let r_pi = Expr::Pi(
470        BinderInfo::Implicit,
471        Name::str("r"),
472        Node::new(rel_type(0)),
473        Node::new(a_pi),
474    );
475    // `{α : Sort u}`.
476    Expr::Pi(
477        BinderInfo::Implicit,
478        Name::str("α"),
479        Node::new(sort_param("u")),
480        Node::new(r_pi),
481    )
482}
483
484/// The universe level parameters of the canonical `Quot.sound` axiom (`[u]`).
485pub fn canonical_quot_sound_level_params() -> Vec<Name> {
486    vec![Name::str("u")]
487}
488
489/// Build the `QuotVal` for a canonical quotient primitive.
490fn canonical_quot_val(name: Name, kind: QuotKind) -> QuotVal {
491    QuotVal {
492        common: ConstantVal {
493            name,
494            level_params: canonical_quot_level_params(kind),
495            ty: canonical_quot_type(kind),
496        },
497        kind,
498    }
499}
500
501/// The type the quotient primitives assume for `Eq`, over `Eq`'s own
502/// universe parameter `u`: `{α : Sort u} → α → α → Prop`.
503fn expected_eq_type(u: &Level) -> Expr {
504    Expr::Pi(
505        BinderInfo::Implicit,
506        Name::str("α"),
507        Node::new(Expr::Sort(u.clone())),
508        Node::new(rel_type(0)),
509    )
510}
511
512/// The type the quotient primitives assume for `Eq`'s constructor, over the
513/// constructor's own universe parameter `u`:
514/// `{α : Sort u} → (a : α) → @Eq.{u} α a a`.
515fn expected_eq_refl_type(u: &Level) -> Expr {
516    Expr::Pi(
517        BinderInfo::Implicit,
518        Name::str("α"),
519        Node::new(Expr::Sort(u.clone())),
520        Node::new(Expr::Pi(
521            BinderInfo::Default,
522            Name::str("a"),
523            Node::new(Expr::BVar(0)),
524            Node::new(eq_app(
525                u.clone(),
526                Expr::BVar(1),
527                Expr::BVar(0),
528                Expr::BVar(0),
529            )),
530        )),
531    )
532}
533
534/// Whether two terms are the same up to the names and the binder
535/// information of their binders — the comparison Lean's kernel makes
536/// between `Eq`'s declared types and the ones it expects. (Not definitional
537/// equality: a type that only reduces to the expected one is refused, as
538/// Lean refuses it.)
539///
540/// This is kept apart from the public
541/// [`crate::inductive::supplied::same_up_to_binders`] on purpose: it covers
542/// only the terms the two expected types are built of (no `let`, literal,
543/// projection or free variable compares equal here), so merging the two
544/// would change what an `Eq` declaration may be compared equal to.
545fn same_up_to_binders(a: &Expr, b: &Expr) -> bool {
546    match (a, b) {
547        (Expr::Pi(_, _, t1, b1), Expr::Pi(_, _, t2, b2))
548        | (Expr::Lam(_, _, t1, b1), Expr::Lam(_, _, t2, b2)) => {
549            same_up_to_binders(t1, t2) && same_up_to_binders(b1, b2)
550        }
551        (Expr::App(f1, a1), Expr::App(f2, a2)) => {
552            same_up_to_binders(f1, f2) && same_up_to_binders(a1, a2)
553        }
554        (Expr::Sort(l1), Expr::Sort(l2)) => l1 == l2,
555        (Expr::Const(n1, ls1), Expr::Const(n2, ls2)) => n1 == n2 && ls1 == ls2,
556        (Expr::BVar(i), Expr::BVar(j)) => i == j,
557        // The expected types are built of nothing else.
558        _ => false,
559    }
560}
561
562/// Verify that `Eq` is the inductive type the quotient primitives assume:
563///
564/// * an inductive type with exactly one universe parameter `u` and the type
565///   `{α : Sort u} → α → α → Prop`;
566/// * with exactly one constructor, which has one universe parameter and the
567///   type `{α : Sort u} → (a : α) → @Eq α a a`.
568///
569/// This is Lean's `check_eq_type`, the precondition of its `add_quot`
570/// (measured on Lean 4 v4.32.0-rc1, 2026-11-12: "failed to initialize quot
571/// module, unexpected number of universe params at 'Eq' type" / "'Eq' has
572/// an expected type" / "unexpected number of constructors for 'Eq' type" /
573/// "unexpected type for 'Eq' type constructor"). `Quot.lift`'s hypothesis
574/// `∀ a b, r a b → f a = f b` and `Quot.sound`'s conclusion are stated with
575/// `Eq`: under an `Eq` of another shape — one whose constructor relates any
576/// two elements, say — they would say something else, and the canonical
577/// types of the primitives might not even be well typed.
578///
579/// The two types are compared up to the names and the binder information of
580/// their binders, as Lean compares them, and the constructor is looked at
581/// for its type, not for its name (Lean accepts `Eq` with a constructor
582/// called otherwise, and with either split of its arguments into
583/// parameters and indices: measured). `Eq.refl` may therefore be spelled as
584/// a flat single-atom `Name` or as the hierarchical `Eq.refl`.
585fn check_eq_present(env: &Environment) -> Result<(), EnvError> {
586    let invalid = |msg: &str| Err(EnvError::InvalidQuotient(msg.into()));
587    let eq_name = name_eq();
588    let Some(iv) = env.get_inductive_val(&eq_name) else {
589        return invalid("`Eq` must be declared (as an inductive type) before quotients");
590    };
591    let [u] = iv.common.level_params.as_slice() else {
592        return invalid("`Eq` must have exactly one universe parameter");
593    };
594    if !same_up_to_binders(&iv.common.ty, &expected_eq_type(&Level::param(u.clone()))) {
595        return invalid("`Eq` must have the type `{α : Sort u} → α → α → Prop`");
596    }
597    let [ctor_name] = iv.ctors.as_slice() else {
598        return invalid("`Eq` must be an inductive with exactly one constructor");
599    };
600    let Some(cv) = env.get_constructor_val(ctor_name) else {
601        return invalid("`Eq`'s constructor must be registered");
602    };
603    let [u] = cv.common.level_params.as_slice() else {
604        return invalid("`Eq`'s constructor must have exactly one universe parameter");
605    };
606    if !same_up_to_binders(
607        &cv.common.ty,
608        &expected_eq_refl_type(&Level::param(u.clone())),
609    ) {
610        return invalid(
611            "`Eq`'s constructor must have the type `{α : Sort u} → (a : α) → @Eq α a a`",
612        );
613    }
614    // Implied by the two types for a constructor the kernel checked; kept
615    // for a constant that reached the environment unchecked.
616    if !iv.is_prop {
617        return invalid("`Eq` must be a `Prop`-valued inductive");
618    }
619    if cv.induct != eq_name {
620        return invalid("`Eq`'s constructor does not point back to `Eq`");
621    }
622    Ok(())
623}
624
625impl Environment {
626    /// Install the four `#QUOT` primitives (`Quot`, `Quot.mk`, `Quot.lift`,
627    /// `Quot.ind`) with their canonical, kernel-constructed types.
628    ///
629    /// This is the trusted `#QUOT` entry point, mirroring Lean's
630    /// `add_quot` in `src/kernel/quot.cpp`:
631    ///
632    /// - **Precondition:** `Eq` must already exist as the inductive type the
633    ///   primitives assume — one universe parameter, the type
634    ///   `{α : Sort u} → α → α → Prop`, and one constructor of type
635    ///   `{α : Sort u} → (a : α) → @Eq α a a` (`check_eq_present`) — because
636    ///   `Quot.lift`'s soundness hypothesis mentions `Eq`. A typed error is
637    ///   returned otherwise.
638    /// - All four types are constructed **in-kernel** (universe-polymorphic in
639    ///   `u`, plus `v` for `Quot.lift`); the caller never supplies them.
640    /// - The four constants are added atomically. If any name already exists,
641    ///   nothing is installed.
642    /// - A once-only [`Environment::quot_initialized`] flag is set; a second
643    ///   `add_quot` is a typed error, and any other `ConstantInfo::Quotient`
644    ///   addition is rejected by [`Environment::add_constant`].
645    pub fn add_quot(&mut self) -> Result<(), EnvError> {
646        if self.quot_initialized() {
647            return Err(EnvError::InvalidQuotient(
648                "quotients have already been initialized".into(),
649            ));
650        }
651        check_eq_present(self)?;
652        let decls = [
653            (name_quot(), QuotKind::Type),
654            (name_quot_mk(), QuotKind::Mk),
655            (name_quot_lift(), QuotKind::Lift),
656            (name_quot_ind(), QuotKind::Ind),
657        ];
658        // Atomicity: reject up-front if any target name is taken.
659        for (name, _) in &decls {
660            if self.contains(name) {
661                return Err(EnvError::DuplicateDeclaration(name.clone()));
662            }
663        }
664        // The guard of an install (`crate::inductive::installed`): every
665        // constant a canonical type names resolves once the four are in —
666        // one of the four primitives, or the `Eq` that `check_eq_present`
667        // has just held to the shape they assume — and is that constant.
668        let eq_name = name_eq();
669        for (name, kind) in &decls {
670            let ty = canonical_quot_type(*kind);
671            let stray = crate::expr_util::find_mentioned_name(&ty, |c| {
672                let primitive = decls.iter().any(|(p, _)| p == c);
673                let eq = c == &eq_name && self.get_inductive_val(c).is_some();
674                !(primitive || eq)
675            });
676            if let Some(c) = stray {
677                return Err(EnvError::InvalidQuotient(format!(
678                    "the canonical type of '{name}' names '{c}', which is neither a quotient \
679                     primitive nor the inductive `Eq` they assume"
680                )));
681            }
682        }
683        for (name, kind) in decls {
684            let qv = canonical_quot_val(name, kind);
685            self.insert_quotient(ConstantInfo::Quotient(qv));
686        }
687        self.set_quot_initialized();
688        Ok(())
689    }
690}