Repository navigation
Locate FastPower's Power instance and result Array instead of pinning their ids (fixes #53) - #136
jiro-by-nori[bot] wants to merge 1 commit into
Conversation
… their ids (issue #53) `Books/FastPower/Proof.lean` named the `Power` instance and the address of the result Array by the literals `114`/`115`. A bootstrap class or a boot fixture object moves every id the program allocates, and the stale id then names a different object without failing where it is written: both sides of each `kernel_rfl` stub reduce to the same wrong machine, so the break shows up as a type mismatch at `setup`/`fast_power_correct` and costs a scavenger hunt per additive boot change. The ids are now read off the machine by the invariant that makes them that object: `powerIdOf` finds the only object carrying both `@base` and `@steps`, and `resultIdOf` is the last object of the heap at `final`, which is the Array the program answers with. `#guard`s pin both invariants at build time, so a second matching object or an extra allocation between the loop head and `final` fails at the definition rather than somewhere below. Proof-only: the model, the prelude and the kernel are untouched.
There was a problem hiding this comment.
Reviewed head 93c4375.
Checks observed: all green at this head — CI, proofs (build, every book, axioms, soundness, comparator), conformance (the model against CRuby), front-end (the desugarer against CRuby), lint, docs, checks not weakened.
Files reviewed: books/Books/FastPower/Proof.lean (only file changed, +46/−7).
Validation: no Lean toolchain in this session, so make books / make metatheory were not re-run locally; those jobs passed in CI on this head. I read the complete diff and verified by grep that no sorry, axiom, or native_decide was added and that no other pinned id (114, 115) is left in the file. The change:
- replaces the pinned Power-instance id (
114) withpowerIdOf, a heap search for the only object carrying both@baseand@steps; - replaces the pinned answer-Array id (
115) withresultIdOf, the last object in the heap; - adds three
#guards that fail elaboration unless the heap holds exactly one Power instance atentryand atfinal, and exactly one object is allocated between them — i.e. unless both reads still name what the proof says they name.
Risk: low. The theorem statements are not weakened: outcome_value and outcome_array keep their claims with the id computed instead of written, and fast_power_correct now witnesses resultIdOf where it witnessed 115. The one caveat is scope of the guards: they are evaluated at the representative program (3, 2), so they are sound for all (b, n) only because this program's allocation sequence does not depend on its arguments — a fact the proof states in prose but does not itself establish. That is a reasonable trade for removing a boot-order pin, and it fails loudly rather than silently if the boot or the program changes.
Verdict: approve.
…thod_missing (issue #147) A Pathname instance answers every unmodeled selector with a named `Unsupported`, including selectors no Ruby defines, because the prelude's `Pathname` carried a blanket `method_missing` and a `respond_to_missing?` that answered `true` for anything. An invented name therefore looked like an honest gap instead of the `NoMethodError` CRuby raises, and a real unmodeled one (`realpath`, `stat`, ...) lost its own name behind one generic reason. `Pathname` is a prelude class but it ships with every CRuby, so its surface is now generated like a builtin's: `scripts/gen_cruby_names.rb` emits Pathname's instance selectors, singleton methods and constants into `crubyMethodNames` / `crubySingletonNames` / `crubyNamespaceConstants`, and the prelude's `method_missing`/`respond_to_missing?` are deleted. A miss is then an ordinary lookup miss: a real unmodeled method declines as `unmodeled method Pathname#realpath`, an invented one raises CRuby's byte-exact `NoMethodError`, and `respond_to?` answers from the same tables. `Pathname#path` is protected in CRuby, so it is modeled as a protected method: `p.path` raises the protected-method `NoMethodError` and `p.send(:path)` answers. The prelude's `include Comparable` is removed for the same reason: CRuby's Pathname is not Comparable, so `<`, `>` and `between?` on one are `NoMethodError` rather than answers. The checker's copied `nativeInstanceNames` projection grows to keep `instance_covered` true (the conservative direction: more selectors it refuses) and `nativeQueryRows` records Pathname's own `===`/`to_s`. `instance_covered` now needs `maxRecDepth 4000` to walk the larger list. Stacked on #136: the prelude change shifts the boot heap, which the FastPower proof pinned object ids against (issue #53). Co-authored-by: Nori <contact@tilework.tech>
Fixes #53.
Books/FastPower/Proof.leannamed two heap ids by literals: thePowerinstance (114) and the address of the result Array (115). A bootstrap class or a boot fixture object moves every id the program allocates, and the stale id then names a different object without failing where it is written — both sides of eachkernel_rflstub reduce to the same wrong machine, so the break surfaces as a type mismatch atsetupandfast_power_correct. Correcting the literals was a per-boot-change scavenger hunt; the file now derives both ids.What changed (only
books/Books/FastPower/Proof.lean)powerIdOf h— the id of thePowerinstance, read off the heap as the only objectinitializehas given both@baseand@steps.resultIdOf h— the id of the result Array, read off the heap atfinalas the last object, which is the last thing the program allocates.loopHead,outcome_value,outcome_arrayandfast_power_correctuse those instead of114/115. The statements keep their content: the value is still required to be a reference (outcome_value) to an object whose payload is the[result, steps]Array (outcome_array).#guards pin the invariants the reads rely on: exactly onePowerinstance at the loop head and atfinal, and exactly one object allocated between the two (the Array). Both sides of each comparison move with the boot, so an additive boot change keeps passing; a second object carrying@base/@steps, or an extra allocation, fails at the definition instead of somewhere below.Test plan
The acceptance bar in #53 is "an additive boot change requires no edit to this file", so that is what I measured, in a scratch tree (the boot edits are not in this PR):
lake build FastPoweron the unmodified boot — passes, 59 jobs.Books.FastPower.Prooftakes 47s, the same as before the change..fileallocation, so every program-allocated id shifts by one): with the old pinned ids,lake build FastPowerfails with five kernel type mismatches —setup,odd_iter,even_iter,outcome_value,outcome_array. With this change and no edit toProof.lean, it passes, including the#guards.Boot.classTable((47, "ShiftProbe", some objectId),mainId := 48): with this change and no edit toProof.lean,lake build FastPowerpasses.#guards reportevaluates to falseat their line andsetupfails, rather than a stale id silently naming another object.lake buildinbooks/— all 1399 jobs,Build completed successfully, so every other book still builds; the axiom audit inBooks/FastPower/Check.lean(#print axioms fast_power_correct,fast_power_run) still reports[propext, Classical.choice, Quot.sound].python3 scripts/check-not-weakened.py --base origin/main—CHECKS NOT WEAKENED.Not run, and why: this machine has no Ruby toolchain (only the Lean one is installable here — no
make, no C compiler), so the CRuby-dependent targets ofmake check(gen-check,conformance,desugar-*,difftest-*,book-checks) did not run locally. Nothing in this change touches the model, the prelude or the desugarer, so those comparisons cannot move; I ran the Lean side (lake buildfor the model,genbootedheap, and the full books build) in full.Authored by Jiro, an autonomous agent (Nori sessions), on behalf of @sam-xif.