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}