Delete the false axiom liftLooseBVars_eq: 29 → 28 - #21
Merged
Conversation
`Lean.Expr.liftLooseBVars_eq` is **false**, machine-checked, and has
**zero dependents** -- so it is deleted rather than weakened.
**The defect.** The C wrapper begins
`if (!lean_is_scalar(s) || !lean_is_scalar(d)) { lean_inc(e); return e; }`
-- a bignum guard at `2^63`, *before* the algorithm. Witness `e := .bvar
0`, `s := 0`, `d := 2^63`, verified by two different instruments because
the two sides cannot be evaluated in one expression:
- C side, differential test on compiled code:
`#eval idx ((Expr.bvar 0).liftLooseBVars 0 (2^63))` prints `"bvar 0"`.
- model side, kernel reduction:
`liftLooseBVars' (.bvar 0) 0 (2^63) = .bvar (2^63)`.
So the axiom asserts `.bvar 0 = .bvar (2^63)`. Unlike the panic cases,
**the call completes** -- both arguments are ordinary; only the model's
output is not runtime-constructible.
**Why delete rather than bound.** An axiom-cone scan
(`Lean.collectAxioms`) over every non-internal `Lean4Lean.*` declaration
gives this axiom **0** dependents against 35-162 for each of the other
13 in scope -- because the checker never calls `Expr.liftLooseBVars` at
all. A grep could not have shown this: the axiom is `@[simp]` and has no
explicit call sites either way, while a `@[simp]` lemma that fires does
appear in the proof term. Deleting it cannot break a proof.
**The model `liftLooseBVars'` stays**, decided by the same instrument: a
constant-dependency scan gives it **29** direct users including
`instantiate1'`. It is the axiom that was false, not the model.
A tombstone records the reasoning in place.
**Guard**: name out of `axiomWhitelist`, all **8** count sites 29 -> 28.
Count re-derived independently with guard 1's own method -- enumerating
`axiomInfo` constants whose defining module is `Lean4Lean.Verify.Axioms`
-- giving 28, with the enumerated list matching the whitelist
name-for-name. `lake build` exit 0; `Lean4Lean.Tests` exit 0. Guard 1
prints 28; guards 2 and 3 unchanged.
**Method correction in §2**, where the next audit starts: check 1 read
the C *workers* faithfully and skipped the `extern "C"` **wrapper**,
which is where argument validation lives.
> For any `@[extern]` axiom, read the wrapper first and enumerate every
> early return before reading the algorithm.
A `Nat` crossing into C arrives boxed and is *always*
`lean_is_scalar`-guarded at `2^63`; the guard either panics or
substitutes a fallback, and **the fallback is what the axiom must
match**. Seven of fourteen axioms in scope sit on one, resolving four
different ways. Companion rule recorded: an identity holding only
through an `@[extern]`/`@[export]` linker pairing is weaker than a
definitional one and can never be discharged by `rfl` -- record which
kind you have.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Owner
Author
|
I approve, merge |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Part of #19. Frozen files change — needs your review.
Lean.Expr.liftLooseBVars_eqis false, and it has zero dependents, so this deletes it rather than weakening it. Whitelist 29 → 28.The defect
The C wrapper starts with a bignum guard, before the algorithm:
Witness
e := .bvar 0,s := 0,d := 2^63. Checked by two different instruments, because the two sides cannot be evaluated in one expression —#evalof the model's answer tripslean_expr_mk_data's own panic:#eval idx ((Expr.bvar 0).liftLooseBVars 0 (2^63))→"bvar 0"liftLooseBVars' (.bvar 0) 0 (2^63) = .bvar (2^63)So the axiom asserts
.bvar 0 = .bvar (2^63).Unlike the panic cases fixed earlier, the call completes — no panic, both arguments ordinary. Only the model's output is not runtime-constructible.
Why delete rather than bound
An axiom-cone scan (
Lean.collectAxioms) over every non-internalLean4Lean.*declaration:liftLooseBVars_eqZero for a structural reason: the checker never calls
Expr.liftLooseBVars. A grep could not have established this — the axiom is@[simp]and has no explicit call sites either way, while a@[simp]lemma that fires does appear in the proof term. So deletion cannot break a proof.The model
liftLooseBVars'stays, decided by the same instrument rather than by eye: a constant-dependency scan gives it 29 direct users includinginstantiate1'. It is the axiom that was false, not the model. A tombstone inAxioms.leanrecords all of this in place.If you would rather keep a bounded statement than delete: the guard needs both arguments,
s < 2^63 ∧ d < 2^63, notdalone — and aUSizerestatement does not fit without changing the signature.Verification
axiomInfoconstants whose defining module isLean4Lean.Verify.Axioms): 28, list matching the whitelist name-for-name.Guard.leanupdated. The two historical re-pin sentences left as history; a new paragraph records this one.lake buildexit 0;Lean4Lean.Testsexit 0.guard 1: … exactly the 28 frozen axioms ✓; guards 2 and 3 unchanged.Levelsection is untouched — another stream is working there and may also re-pin; whoever lands second re-pins again.Method correction, in §2 where the next audit starts
Check 1 read the C workers faithfully and skipped the
extern "C"wrapper, which is where argument validation lives.A
Natcrossing into C arrives boxed and is alwayslean_is_scalar-guarded at2^63; the guard either panics or silently substitutes a fallback — and the fallback is what the axiom must match. Seven of fourteen axioms in scope sit on one, resolving four different ways: one false (this one), two surviving with machine-checked agreement lemmas, one surviving for a reason the audit had not stated, and two whose side condition turns out necessary but not sufficient.Companion rule also recorded: an identity holding only through an
@[extern]/@[export]linker pairing is weaker than a definitional one and can never be discharged byrfl— so record which kind you have. One axiom's stated justification was wrong in exactly that way, while its verdict was right.🤖 Generated with Claude Code