ci(object): make the ObjectHeader shape-descriptor census a real gate - #8110
ci(object): make the ObjectHeader shape-descriptor census a real gate#8110proggeramlug wants to merge 2 commits into
Conversation
#8086 built the exact-callsite census #8067 asked for — the instrument that keeps `object_type`, `field_count` and `keys_array` retired as ShapeId takes over their facts — and then wired it into nothing. `grep -rn shape_descriptor_census` over every workflow, script and doc returns only the script naming its own baseline, so it has never been able to fail a build. Add it to `lint` alongside the other fifteen audits. Wiring it up exposed that one of its two arms was vacuous. The emitted-guard check rejected `add(..., "0"|"12"|"16")`, but all four functions in its list — `emit_class_field_loop_preheader_check`, `emit_proven_shape_recheck`, `emit_class_field_inline_precheck` and `emit_element_shape_field_load` — build their header address with `blk.gep(I8, &p, &[(I64, "N")])`. The pattern therefore matched a syntax those functions never emit. Planting `gep(I8, &elem_ptr, &[(I64, "16")])` in `emit_element_shape_field_load` — a read of the `keys_array` offset #8047 removes — left the census green. Match the gep form too. Sabotage-verified, each reverted after: * new `(*obj).field_count` read in `object/spill.rs` -> exit 1 * keys-pointer token in `proxy/put_value.rs::dyn_ic_try_store` -> exit 1 * offset 16 in `emit_element_shape_field_load` -> exit 1 (was 0) * offset 12 in the three `class_field_inline_guard` emitters -> exit 1 each Clean tree green before and after every one. No behaviour change; the census reports the same summary it did on 12f758a. Refs #8047, #8067, #8086.
|
Important Review skippedDraft detected. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
Verified and endorsed — and this is my miss to own. I audited and merged #8086. My checks were: all four lint gates on a landing-equivalent tree, Confirmed independently on The script is referenced by exactly one file — itself. #8067's acceptance asked for a census; #8086 built it and wired it into nothing, so it has never gated anything and never could. And the arm that would have mattered was vacuous. Your finding that it matched The generalisable lesson I am taking from this, since it is a gap in how I audit: when a PR adds a gate, checker or census, verifying its tests pass is not enough — verify it is wired into a workflow, and verify it can go red. "Does this new gate run?" and "can it fail?" are two separate questions and I asked neither. Six sabotages with the clean tree re-verified green after each, and all 16 Two things from your report worth carrying into #8112/#8113 rather than losing here: |
What
Wires
scripts/shape_descriptor_census.pyinto thelintjob, and fixes avacuous arm inside it that the wiring exposed.
Why
#8086 shipped the exact-callsite census that #8067 required — the instrument
that keeps
ObjectHeader::object_type,field_countandkeys_arrayretiredwhile
ShapeIdtakes over their facts, and the thing #8047 says must existbefore anyone touches the layout. It is a good instrument: it strips comments
and string literals, classifies every site
declarationvsaccess, diffs thefull multiset against a reviewed baseline (so it can tell an
ObjectHeaderreadfrom another struct's identically-named field), and carries its own lexer
self-test plus sabotage self-tests.
It is invoked by nothing:
The only hit is the script naming its own baseline.
lintruns fifteen otherpython audits; this is not one of them.
The vacuous arm
Wiring it up and sabotage-testing it found that the emitted-guard check could
not fail for any function it names. It matched
but all four functions in that list build their header address with
blk.gep(I8, &p, &[(I64, "N")]), notadd(..):emit_class_field_loop_preheader_checkblk.gep(I8, &obj_ptr, &[(I64, "4")])emit_proven_shape_recheckblk.gep(I8, &obj_ptr, &[(I64, "8")])emit_class_field_inline_precheckblk.gep(I8, &obj_ptr, &[(I64, "4")])emit_element_shape_field_loadblk.gep(I8, &elem_ptr, &[(I64, "8")])So the pattern targeted a syntax none of them uses. Planting a read of the
keys_arrayoffset that #8047 removes —gep(I8, &elem_ptr, &[(I64, "16")])inemit_element_shape_field_load— leftthe census green. This patch matches the gep form as well.
The two write-PIC checks below it are unaffected:
lower_generic_property_getand the
proxy_reflectemitters genuinely do usectx.block().add(I64, .., "N"),and their own regexes already spell that.
Verification
Sabotage-verified, every change reverted afterwards, clean tree green before and
after each:
(*obj).field_countread inobject/spill.rsproxy/put_value.rs::dyn_ic_try_storeemit_element_shape_field_loademit_class_field_loop_preheader_checkemit_proven_shape_recheckemit_class_field_inline_precheckAll sixteen
lintsteps, enumerated from.github/workflows/test.ymlratherthan from memory, pass on this branch:
cargo fmt --all -- --check,check_file_size.sh,workspace_architecture.py(--self-testand--check),local_binding_type_audit.py,gc_store_site_inventory.py,addr_class_inventory.py,class_id_collisions.py,gc_pin_sites.py,gc_runtime_root_holders.py,check_gc_env_knobs.py,check_llvm_corpus_currency.py,check_gc_doc_claims.py,raw_handle_debt.py,check_test_registration.py, and the newly wired census.No behaviour change: the census reports the same summary on this branch as on
12f758a22.Note this is a new gate, so per CLAUDE.md it has never been green — run it
once on
mainbefore promoting it to a required context.Not in this PR
The #8047 layout change itself. A full re-audit on
12f758a22found theprerequisite still incomplete; findings are posted on #8047.
Refs #8047, #8067, #8086.