ziran check checks every law reachable through the input modules. A failed
law blocks native output and portable bundles. Pick the kind that states the
guarantee you need:
| Kind | Evidence | Scope |
|---|---|---|
type, bounds, size, effect, abi |
structural |
A compiler-derived property or supported ABI shape |
custom |
evaluation |
One closed expression |
forall |
exhaustive |
Every member of a declared finite domain |
theorem |
kernel |
Every value of the declared scalar parameter types |
proved means that the selected checker established its obligation. The
method and domain tell you how far that result extends. A finite test of
n: 0..4 says nothing about other values. An abi result checks a signature;
it does not prove what a foreign procedure does.
Write an obligation and certificate
Long quantified laws can continue after each domain's comma and after =>.
Put each predicate on its own line, continuing after && or ||. The
formatter keeps these continuations indented until the terminating ;:
#law AddCommutes forall left: 0..4,
right: 0..4 =>
Add(left, right) == Add(right, left) &&
Add(left, 0) == left;
In scalar.zi:
Add :: (a: s32, b: s32) -> s32 { return a + b }
Larger :: (a: s32, b: s32) -> s32 {
if a > b { return a }
return b
}
In LAWS.zi:
Code :: #import "scalar";
#law AddZero theorem x: s32 => Code.Add(x, 0) == x;
#law MaximumCommutes theorem a: s32, b: s32 => Code.Larger(a, b) == Code.Larger(b, a);
In PROOF.zi:
Laws :: #import "LAWS";
#proof Laws.AddZero { unfold Code.Add; ring; }
#proof Laws.MaximumCommutes { unfold Code.Larger; cases; order; }
Run ziran check --root src src/PROOF.zi. These filenames are a convention;
the compiler follows imports. A certificate can also follow its theorem in
the same file. A law's identity belongs to its declaring module; use import
aliases to refer to another module's law. The same short name in unrelated
modules is allowed. Each theorem can have one certificate.
Certificate steps
Every step ends with ;. Put unfold steps first and end with one closing
step. Comments follow ordinary source comment rules.
| Step | Meaning |
|---|---|
unfold Procedure; |
Project this checked pure procedure and its acyclic callees into scalar expressions |
unfold; |
Permit all supported pure calls in the proposition |
cases; |
Permit the closing checker to split undecided boolean conditions and check both branches |
rewrite Equality(arguments); |
Check the named theorem's certificate and replace its left side with its right side, including inside larger expressions |
use Theorem(arguments); |
Close the goal with the exact proposition of a checked theorem instance |
refl; |
Close by reflexivity, boolean evaluation, and enabled case splits |
ring; |
Also normalize +, -, and * as modular polynomials |
order; |
Also use exact scalar order, transitivity, antisymmetry, and contradictory branches |
Theorem application uses explicit positional arguments. Lemmas are checked with an empty hypothesis context. A waiver cannot supply a certificate, and circular dependencies are invalid.
The first fragment includes bool, s8/s16/s32/s64, u8/u16/u32/u64, and
enum equality. Enum variables cover the entire backing integer domain,
including unnamed values obtainable through casts. Integer addition,
subtraction, and multiplication wrap modulo 2^width. Signed order interprets
the same bits as a signed value. ring can establish associativity and
distribution through overflow; order cannot assume that addition increases
a value. For example, x + 1 > x is false at unsigned maximum.
Procedure projection supports scalar parameters and returns, local scalar
declarations and assignments (=, +=, -=, *=), lexical blocks,
conditionals, and acyclic calls with derived pure effects. Symbolic casts
between different types, loops, recursion, pointers, arrays, floats, runtime
globals, and foreign calls are outside this proof fragment. Constant casts
and casts to the identical type are supported. Arrays and bounded loops can
still be checked with existing finite forall laws. Integer evaluation uses
exact bits rather than floating-point comparison. Float laws assume unfused
IEEE arithmetic at the declared width: +, -, *, and / on float32
values round once to float32, and on float64 values to float64; a result
that is infinite or NaN is unknown. An integer converts to float64 within
2^53 (float32 within 2^24); a wider conversion would round and is unknown.
A float literal is decided only when its text is the exact %.17g spelling of
its double, such as 2.5; text like 0.1, which rounds, is unknown.
Results, budgets, and waivers
Each JSON result retains law, kind, span, status, and detail, adds the
declaring module for qualified identity, and includes:
"evidence": {
"method": "kernel",
"domain": "x: s32",
"cases_checked": 0,
"counterexample": null,
"waived": false
}
cases_checked counts concrete evaluations for custom and forall. It is
zero for symbolic theorems. A failed forall includes a JSON object mapping
bindings to their concrete numbers or sequence value text. unknown reports unsupported semantics,
a missing/incomplete certificate, or an exhausted budget. A supplied closing
step that fails to establish its goal is invalid; it is not a concrete
counterexample. disproved and invalid always fail the gate.
#law_waive Name "reason"; allows only unknown. An imported law can be
waived by qualified name. Its result stays unknown, with waived: true;
the waiver also has its own result and is saved with the artifact.
The scalar kernel uses deterministic limits: 100,000 operations, 4,096 terms
and polynomial monomials, depth 64, 128 branch facts, 64 order terms, 16
parameters, and 64 locals. Exceeding a checking budget cannot establish a
proof. Finite forall has its separate 1,000,000-case budget.
Saved artifacts and trust
Version 48 .zir stores theorem proposition graphs, certificate steps and
their typed expression graphs, and evidence. Version 25 .zx carries the
linked verification dependencies and an evidence table. Loaders retype and
recheck certificates; saved status, type annotations, and evidence cannot
authorize a proof. Canonical checking rejects divergent saved fields. The
bundle loader also compares its outer evidence and waiver tables against
the checked embedded IR. Earlier artifact versions are rejected.
The built-in C checker has no runtime Lean or solver dependency.
proofs/Scalar.lean models fixed-width semantics and
proves the algebra, order, case, lemma-use, and rewrite rules sound. Its
certificates are mathematical derivations, not the serialized Ziran format.
The C polynomial normalizer, rule implementation, graph projection, parser,
serialization, and executable backends remain trusted code. This is not a
machine-checked proof of the whole compiler. Independent C and Lean
interpreters exhaustively check generated accepted claims at a small width;
integration tests cover runtime boundaries and tampering.
With the toolchain pinned in proofs/lean-toolchain, run:
make proof-model LEAN=/path/to/lean-4.34.1/bin/lean
make check
The model imports Lean's bundled standard libraries only. There are no admitted obligations, custom axioms, or external proof packages. Induction over arbitrary sequences and sorting remains future work.