Repository navigation
Drop receiver narrowings for []= writes in content write-back positions - #391
Merged
Merged
Conversation
…ions
A `local[k]` store replayed through a literal block/lambda body or a
`while`/`until` body rebinds the receiver on the reference —
`content_writeback_block_captures` for the non-escaping-block call,
`loop_content_writeback` for the iterated body — and the rebind drops
every indexed narrowing rooted at it. The port's `[]=` mutation applied
only `invalidate_after_call` semantics (a literal-key slot drop, nothing
for the compound `h[k] op= v`), so `h[:a] ||= "t"` inside `each {}` left
the pre-block `h[:a]` -> `"s" | 1` record live and a post-block
`h[:a].frobnicate` fired a diagnostic the oracle never emits
(rigor-rs#388).
`path_content_writeback` walks the `flow_children` descent to the write
and reports whether it crosses a Barrier edge (a deferred body — the
capture-writeback rebind) or a `while`/`until` body edge. `for` joins
instead of writing back, so its body stays excluded; joined positions —
`if`/`case`/`rescue`/`ensure` arms, `&&`/`?:` operands, rescue-modifier
operands — keep the record their arm's own eval re-recorded.
Closes #388
Generated with [Devin](https://devin.ai)
Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
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.
Closes #388
Summary
h[:a] ||= "t"inside[1].each { … }(or&&=/op=, a plainh[k] = v, a masgn/for/rescueindex target) left the pre-blockh[:a]narrowing live:apply_mutation_effectsran onlyinvalidate_after_callsemantics — a keyed slot drop for a stable[]=key, nothing for the compound forms — so the post-blockh[:a].frobnicatefiredfor "s" | 1where the oracle emits nothing.The oracle drops the record because it REBINDS the captured receiver:
content_writeback_block_captures(invoke_call) for a literalblock/lambda,
loop_content_writebackfor awhile/untilbody. Thisports that positional fact:
path_content_writebackwalks theflow_childrendescent to the write and reports a crossedFlowEdge::Barrier(deferred body) or awhile/untilbody edge —and
apply_mutation_effectsthen drops every narrowing rooted at thereceiver, whatever
drop_keycarried.Boundary (all probe-verified against
e59b7b89)Drops (silent on ref): literal block (
each,loop do, unknownm {…}), lambda/proc/->() {}bodies,while/untilbodies —including nested (
whileunderif, block underfor), and any[]=form inside them (compound, plain, different slot, index target).
Keeps + joins (fires
"s" | "t" | 1on ref):if/case/whenarms,
&&/||/?:operands,begin/rescue/else/ensure,rescue-modifier operands,
forbodies,while/untilpredicates.foris the trap: it lowers toNode::Loopwithpredicate = the collection, so the discriminator isindex.is_empty() && index_writes.is_empty()(aforalways has an index target — excepttargets binding no local like
for @a in xs, which misread aswhile;that direction declines to silence, never a new fire).
Measured
pre-existing FP silent now; joined positions still fire.
harness/gate.sh: docs_check,cargo test -p rigor-cli,cargo test -p rigor-infer,run_snapshot.rb— all pass.fp_audit --gaps --sweep: 0 FP / 815 gaps on thestanding corpora — identical to master's count, no gap delta.
writeback_index_write_drops_indexed_narrowing(14 silent rows) +
joined_index_write_keeps_indexed_narrowing(9 firing rows + the straight-line
"s" | "t" | 1union control).Known non-parity left behind (all safe-side)
for "s" | 1where the oracle saysfor "s" | "t" | 1— the pre-existing missing stored-slot union onconditional arms (both fire; message drift only).
h[k]read where the body also contains anh[k] op= vnow reads the widened-
Dynamicreceiver and declines — e.g.each { h[:a].m; h[:a] ||= "t" }loses an oraclefor "s" | 1in-bodydiagnostic (coverage loss; the descend env is the end-of-file flat env
and cannot hold both the write-back drop and the pre-write record).
forwith a target that binds nothing (for @a in xs,for A in xs,for ::A in xs,for $g in xs,for a.b in xs,for *w in xs,for * in xs) misreads aswhile/until— bothindexandindex_writesare empty — so ah[k] op= vin its bodydrops the record where the oracle joins it: silent vs
for "s" | "t" | 1(coverage loss on a rare shape; tightening thediscriminator needs a
Node::Loopkind flag, deferred).h&.[]=:aarity (wrong-arityonHash#[]=) is a pre-existing portgap on master — unrelated rule, untouched here.
Generated with Devin
Review (fd7e07d)
Opus 5.5 + Grok 4.6, independent probes on both engines: Approved ×2 —
headline and every boundary row verified, no new false positives.
Filed follow-ups: #392 (
fordiscriminator hole — the fullbinding-less-target list above), #393 (
retryoperand leaking anindexed write — pre-existing on master).