Skip to content

Locate FastPower's Power instance and result Array instead of pinning their ids (fixes #53) - #136

Open
jiro-by-nori[bot] wants to merge 1 commit into
mainfrom
nori/fix-53-fastpower-ids
Open

jiro-by-nori[bot] wants to merge 1 commit into
mainfrom
nori/fix-53-fastpower-ids

Conversation

@jiro-by-nori

@jiro-by-nori jiro-by-nori Bot commented Nov 11, 2026

Copy link
Copy Markdown
Contributor

Fixes #53.

Books/FastPower/Proof.lean named two heap ids by literals: the Power instance (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 each kernel_rfl stub reduce to the same wrong machine, so the break surfaces as a type mismatch at setup and fast_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 the Power instance, read off the heap as the only object initialize has given both @base and @steps.
  • resultIdOf h — the id of the result Array, read off the heap at final as the last object, which is the last thing the program allocates.
  • loopHead, outcome_value, outcome_array and fast_power_correct use those instead of 114/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).
  • Three #guards pin the invariants the reads rely on: exactly one Power instance at the loop head and at final, 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):

  1. lake build FastPower on the unmodified boot — passes, 59 jobs. Books.FastPower.Proof takes 47s, the same as before the change.
  2. Fixture object added to the boot VFS (one extra .file allocation, so every program-allocated id shifts by one): with the old pinned ids, lake build FastPower fails with five kernel type mismatches — setup, odd_iter, even_iter, outcome_value, outcome_array. With this change and no edit to Proof.lean, it passes, including the #guards.
  3. Bootstrap class added to Boot.classTable ((47, "ShiftProbe", some objectId), mainId := 48): with this change and no edit to Proof.lean, lake build FastPower passes.
  4. Search miss (predicate changed to a name no object carries): the failure is loud and local — the #guards report evaluates to false at their line and setup fails, rather than a stale id silently naming another object.
  5. Full lake build in books/ — all 1399 jobs, Build completed successfully, so every other book still builds; the axiom audit in Books/FastPower/Check.lean (#print axioms fast_power_correct, fast_power_run) still reports [propext, Classical.choice, Quot.sound].
  6. 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 of make 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 build for the model, genbootedheap, and the full books build) in full.

Authored by Jiro, an autonomous agent (Nori sessions), on behalf of @sam-xif.

… 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.

@jiro-approver-samxif jiro-approver-samxif Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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) with powerIdOf, a heap search for the only object carrying both @base and @steps;
  • replaces the pinned answer-Array id (115) with resultIdOf, the last object in the heap;
  • adds three #guards that fail elaboration unless the heap holds exactly one Power instance at entry and at final, 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.

jiro-by-nori Bot added a commit that referenced this pull request Nov 13, 2026
…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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Books/FastPower/Proof.lean pins concrete heap object ids and breaks silently on any boot shift (extends #22)

1 participant