Proof automation: decision procedures and tactics
Status: design direction for review, 2026-10-29. The user’s direction: “the
bitvector example is just one, but we’d want other lean proof strategies as
well for this stuff, i’m sure there are proofs for other types of thing”.
This note catalogues the proof strategies Cinch needs, by domain, says which
Cinch obligations each one discharges, and fixes the architectural rule they
share. Companions: design/verified-primitives.md (the bit-vector chain,
one instance of this note), design/layouts-and-specialization.md (the
obligations specialization creates), design/dependent-core-kernel.md (the
kernel that checks everything), design/refinement-types.md (decidable
refinement predicates).
1 The architectural rule: untrusted search, small trusted check
Every strategy below has the same shape, which is what keeps the trusted base small no matter how much automation grows:
- Search is untrusted. A tactic, a decision procedure’s search, or an external solver may be arbitrarily large and clever, and may be wrong.
- The kernel checks the result. Only a kernel-checked artifact becomes a
proof. Three ways to produce one:
- Proof terms. The tactic emits an ordinary proof term (rewriting
chains from
simp, congruence-closure explanations fromgrind). The kernel type-checks it like any proof. - Proof by reflection. A decision procedure is written in Cinch,
proven correct once (soundness theorem: “if the procedure says yes, the
proposition holds”), and run inside the kernel by evaluation on a
reified goal.
omega,ring,decideandbv_decide’s bit-blaster work this way in Lean. - Certificate checking. An external solver emits a certificate (an LRAT refutation from a SAT solver, an Alethe or LFSC proof from an SMT solver, Farkas coefficients for linear arithmetic, a Positivstellensatz witness for polynomial inequalities) and a verified checker validates it in the kernel.
- Proof terms. The tactic emits an ordinary proof term (rewriting
chains from
- The check’s tier is recorded. Kernel evaluation is the minimal trusted
base. Running the check as compiled code is faster and adds the Cinch
compiler to the trusted base (Lean’s
bv_decideandnative_decidetake this route); it is allowed only as an explicit, recorded tier (design/layouts-and-specialization.mdsection 7), never silently. - Results are cached in the store. A discharged goal is keyed by its content hash in the package store, so each fact is proven once per machine and shipped with its package.
2 Where tactics live in Cinch
Lean’s tactics are Lean programs run at elaboration time that build proof
terms. Cinch already has the matching machinery: typed staging and macros
(P9: stage/macro definitions evaluated at compile time by the D92
evaluator, typed quotes, quoteDecls, hygienic name splices). A tactic is a
stage function from a goal to a proof term (or to a certificate plus the
reflective checker invocation), and the kernel checks what it returns. This
keeps tactics user-extensible, and it means the tactic language is Cinch
itself, not a second language. The open decision “Typed-code contexts and
stage dependencies” (design/open-decisions-and-implementation-plan.md)
must settle how a tactic sees its goal’s context.
3 Catalogue by domain
Lean names are given because Lean is the reference point; the Cinch uses are the obligations this project already creates.
| Domain | Lean (and other) strategies | Mechanism | Cinch obligations it discharges |
|---|---|---|---|
| Decidable finite propositions | decide (kernel evaluation of a Decidable instance) |
reflection | small exhaustive facts (an 8-bit operation over all 2^16 inputs), refinement predicates on literals, constructor tags |
| Linear integer and natural arithmetic (Presburger) | omega (Lean core); Coq lia |
reflection | array and Bytes index bounds, Vec n lengths, packed-layout offsets and sizes (design/layouts-and-specialization.md section 3), termination measures, the D113 bounded-recursion depths |
| Rewriting with a lemma database | simp, simp_arith, conv |
proof terms | representation-instance isomorphism laws (view (cons x xs) = Cons x xs), fast-path equalities, unfolding definitions for D55 rewrites |
| Equality and congruence reasoning, instantiation of lemmas | grind (congruence closure, E-matching, constraint propagation, case splits, linear arithmetic and commutative-ring solvers; reference) |
proof terms | the “everything else” of specialization proofs: chaining constructor injectivity, view/build round trips, instance-method unfolding |
| Commutative rings and fields | ring, field_simp (Mathlib); Coq ring |
reflection | algebraic class laws of numeric instances (Monoid (Sum Nat), Semiring), SIMD reduction re-association under wrapping arithmetic (platform specification section 4) |
| Associativity and commutativity normalization | ac_rfl, abel (Mathlib) |
reflection | Semigroup/Monoid laws, the balanced mconcat equalling the right fold (associativity, design/layouts-and-specialization.md), sconcat/stimes |
| Linear and nonlinear real/ordered arithmetic | linarith, nlinarith, polyrith, positivity (Mathlib) |
certificates (Farkas, Positivstellensatz) | cost bounds and resource bounds stated as inequalities; Rational/BigDecimal laws |
| Numerals | norm_num (Mathlib) |
proof terms | literal folding facts, Nat/Int constant arithmetic in specifications |
| Bit-vectors | bv_decide (bit-blast, SAT, LRAT) |
certificates | machine-integer prim laws and prim bodies against LLVM/ISA semantics (design/verified-primitives.md) |
| First-order goals, general automation | aesop (proof search), Duper (superposition), LeanHammer (premise selection plus external provers plus reconstruction) |
proof terms after reconstruction | glue goals that no single domain covers |
| SMT combinations | lean-smt (cvc5 with proof reconstruction); Isabelle’s Sledgehammer | certificates | mixed arithmetic, datatypes and uninterpreted functions, as one call |
| Induction and case analysis | induction, cases, rcases, functional induction, decreasing_by |
proof terms | structural proofs over List, trees and Map; totality of representation functions (a representation’s view must be total to appear in a proof) |
| Ownership and mutable state | separation logic (Iris); Verus (linear ghost types plus SMT); RustBelt | proof terms over a heap logic | representations over mutable buffers, Perceus in-place reuse (FP2-style fully-in-place functions), Mut r a and region reasoning |
| Floating point | IEEE formalizations (Flocq in Coq), SMT floating-point theory | certificates (bit-blasting) | F32/F64 prim laws; exact claims only, the platform specification’s separate floating-point contract |
| Counterexamples (falsification, not proof) | plausible (Lean), QuickCheck |
testing | already the law layer’s sample checking: the “sample-checked” tier, and the first thing to run before attempting a proof |
4 What Cinch needs first
Ordered by the obligations the current roadmap creates:
- Induction and case analysis, rewriting (
simp-like),decide. These discharge representation-instance isomorphisms over algebraic data types, which section 6 ofdesign/layouts-and-specialization.mdmakes the common case. - Linear arithmetic (
omega-like). Every layout, index and length obligation is Presburger arithmetic; packed layouts and value types (section 3 there) generate them by construction. The difference-logic fragment has landed (section 5), and so has a linear pass over facts with more than two terms (Fourier-Motzkin with a checked Farkas certificate, section 5); integer-only reasoning (Presburger proper, cutting planes) is open. - Congruence closure with E-matching (
grind-like). Glue for everything above; it is what makes specialization proofs short enough to write routinely. - AC normalization and commutative rings. Class laws of the base library’s instances, and SIMD re-association.
- Bit-vectors. The verified-primitives chain.
- A certificate bridge to SAT/SMT solvers. One verified LRAT checker, then an SMT proof format.
- Separation logic over Cinch’s ownership types. Representations over
mutable buffers and guaranteed in-place reuse (the user’s choice of “both
runtime checks and uniqueness discipline where able to be statically
determined”,
design/reclamation.mdsection 12). - Floating point.
5 Relation to what exists
- The kernel (
design/dependent-core-kernel.md, design draft) hasEq,refl,congr, structural-inductioncaseanddecidewith a supplied decision procedure;examples/source/03-map-fusion.cinchcarries a hand-writtenmapFusionproof (D230/D270). There is no tactic layer yet: proofs are written as terms. rewrite ... justify(D55) accepts kernel evidence or a citable classlaw, and records undischarged justifications inassumptions.declared; tactics produce exactly that evidence.- The P18 law layer checks class laws sample-wise: the falsification step and the “sample-checked” tier.
- Refinement predicates are a closed, decidable vocabulary
(
design/refinement-types.md);decide-style reflection already fits it. - Difference logic decides refinement obligations from every fact in
scope (as built, 2026-10-30;
design/refinement-types.md, “linear facts over variables”):x - y <= cconstraints overNat,Int, machine integers andChar, decided by Bellman-Ford negative-cycle detection with bounded case splits on/=facts (linear_entails,compiler/src/types.rs). It follows section 1’s rule in miniature. The search is untrusted: it emits a certificate (DiffCert), a negative cycle whose edges each cite a hypothesis conjunct, the negated goal, a sort’s range axiom or a case split’s alternative, and a split node refuting both strict alternatives of a disequality. A separate checker (check_linear_proof) re-reads every cited constraint from its source and sums each cycle to0 <= cwithc < 0; the discharge accepts only a checked proof. The checker is today Rust inside the checker, so it is the compiled-check tier of section 1. The kernel check belongs where the discharge produces its evidence: when the kernel checks evidence terms, the certificate becomes the erased proof term of the obligation, and the kernel runs the same check by reflection (the checker written in Cinch, proven sound once: a cycle of valid constraints summing to a negative bound is unsatisfiable). Its soundness argument is two lemmas: reading an atom as a constraint (linearizing, and roundinga * (x - y) <= cdown tox - y <= floor(c / a)over integers) is valid, and a cycle’s sum telescopes. - Three-term facts (as built, 2026-10-30, NF5). A fact or goal over
more than two terms (
di + n <= bytes-len dst,bytes-len out == bytes-len x + bytes-len y,i + k <= n) is not a difference constraint, and everybytes-copy-rangeobligation is one. When Bellman-Ford finds no cycle, a second pass reads every comparison assum(a * key) <= cover the same opaque keys (each row’s coefficients divided by their common factor and the bound rounded down, sound over the integers), keeps the rows connected to the goal’s keys, and eliminates keys by Fourier-Motzkin. A refutation is a Farkas certificate (LinCert::Farkas: positive integer multipliers whose weighted sum of cited constraints cancels every key and leaves0 <= cwithc < 0), or a split of a disequality into its two strict alternatives, each refuted (at most 2 deep, trying at most 6 disequalities per depth).check_lin_certre-reads each cited constraint from its source and re-sums. The cost is bounded and predictable: elimination stops once it holds 256 rows, and passing that cap refuses the obligation (it is then reported as unproved), never a longer search. The pass refutes the rational relaxation, so it is incomplete over the integers: a fact set with a rational solution and no integer one (x == 2 * yandx == 2 * z + 1) is not refuted beyond what the per-row rounding catches (tests/safe_bytes.rs,an_integer_only_contradiction_is_not_used). The soundness argument adds one lemma: a nonnegative combination of valid linear constraints is valid.
6 Open decisions
- The tactic surface:
by <tactic>in proof positions (Lean’s form) orjustify (tactic ...)reusing D55’s slot; and whether tactics arestagefunctions directly or a small tactic combinator library over them. - How a tactic sees its goal and local context (the open “typed-code contexts” decision).
- Which certificate formats are first-class (LRAT proposed first, then an SMT proof format) and which external solvers are supported.
- Whether the compiled-check tier exists at all, or kernel evaluation only.
- How lemma databases (
simpsets,grindE-matching patterns) are declared: attributes on theorems (Lean’s@[simp],@[grind]) fit the attribute system.
