Repository navigation
Conversation
… fields Restores the row-1a mechanism from quarry after the §3 question that caused its drop was settled by execution: #8539 landed and left these sites standing, so its Clone-bound synthesis demonstrably does not generalise to this case. The live instance is FreeMonoidUniqueState<T> in src/v2/std/algebra.dag, whose derive over `seen: Rc<Vec<T>>` requires a supplemental T: Clone the emitter did not synthesize (10 E0277 + 2 E0369 on the materialization_carriers board). The witness oracle is silent-raven-853's, resurrected from their three commits (c76b769 introducing it, a52f0e1 and 776a6d5 correcting it) rather than rewritten. Author-of-oracle and author-of-implementation stay separate. Re-attributes two notes that labelled a coordinating session's ruling as an "operator verdict" while naming a session id. The substance is unchanged; the label claimed an authority external to the diff that a session cannot hold. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
An arrow type realizes in Rust as Rc<dyn Fn(..) -> ..>, and a dyn trait object
defaults to the 'static lifetime, so a function that closes over its arrow-typed
parameters and returns one cannot compile unless those parameters are 'static.
rustc reports E0310 at the Rc::new(move |..| ..) site.
The obligation is created by the realization choice, not by the authored source:
v2/std/staging.dag declares plain arrows and carries no Rust concept at all. It
is fixed at the emitter for that reason. Declaring it at the source would mean
inventing Rust lifetime syntax in a medium-agnostic language ('static appears in
no .dag source module) and would leave the next arrow-into-Rc site to be
rediscovered the same way.
Unconditional rather than gated on reaching a stored position: the substrate has
no reference or lifetime concept, so every closure it can construct is already
'static and the bound rejects no expressible program. Same shape as the + Clone
beside it.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The restored mechanism carried five // lines inside declaration bodies. DESIGN §4c admits only standalone leading blocks attached to module-scope declarations, so v2 self-compile refused with two hard diagnostics and the regen never produced a seed: source annotation sits inside a declaration body. Only module-item grain is modeled; move it above the declaration it describes. Restores the quarry's own fix for this (8edf566), which the enumerated commit list used to restore the mechanism had omitted. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The struct and enum witnesses were asymmetric. The enum asserted both impl headers and impl bodies; the struct asserted headers only, so an emitter producing correct headers over an empty or wrong body satisfied every assertion in it -- a PartialEq whose body is literally `true` passed. The enum's body assertions would have caught that only by being the more thorough of the pair, which is luck rather than coverage wherever the struct and enum emission paths can diverge. The body spellings are grounded in a mechanism this change did not author: the runtime prelude's pre-existing NonEmptyVec hand-written impls establish the `&self.<field>` access form and the `fn eq(&self, other: &Self) -> bool` signature, and `debug_struct(..).field(..).finish()` is the std Formatter builder documented for named-field structs. Reading the spellings off this emitter instead would have re-run the same collision one level down. That precedent also independently corroborates the two impl headers already asserted here, which was not why it was consulted: it emits `impl<T: Clone + std::fmt::Debug> std::fmt::Debug for NonEmptyVec<T>`, character-for-character the shape of the third include, predating this change. Those two assertions are therefore precedent-backed rather than author-chosen -- load-bearing, because the rest of this witness was authored by the session that authored the emitter under test. Two limits recorded rather than left implicit: the include assertions are spelling-sensitive, so a semantically identical where-clause form fails include 3 and a red here is not by itself a semantic defect; and the `#[derive(Debug` exclude matches file-wide, safe only while this fixture declares a single type. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Breaks the --required-regen population deadlock for the new module dag/extdeps/languages/rust/derive_contracts.dag: its first stage0 mirror cannot be produced by regen itself, because validate_compared_populations refuses on emitted_not_committed BEFORE any candidate is written. Bytes are emitter output (gunbc compile --target rust --entry ...), never hand-authored, following the route receipted by #8539. Registration is the file plus its lib.rs line only -- neither stage0_emit_plan_generated.dag nor stage0_crate_layout_generated.dag enumerates ordinary emitter-produced modules (verified, not assumed). Generation-1 mirrors installed: emit_rust, trait_derive_emit, derive_contracts. Generation-2 is NOT installed and this tree is not the regen fixed point -- installing it yields 161 rustc errors, tracked separately. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The unconditional form landed in 5d9fc3e on the premise that the bound 'cannot reject any expressible program'. That premise was FALSIFIED BY EXECUTION: installing the generation-2 regen candidate produced 161 rustc errors (E0373 / E0597) in v1_compiler_emit.rs and v1_compiler_emit_rust.rs. The premise conflated WHAT is captured with HOW. The captured values are owned, but the emitter emits the closure WITHOUT move, so the capture is a borrow, and a non-move closure borrowing an owned local is not 'static however owned that local is. The gate is precise, not a proxy: the Rc::new(move |..| ..) site that creates the obligation exists exactly when the function returns an arrow, and the lowering already emits those closures with move. So no call site changes and no additional clone is emitted. The rejected alternative -- emit move at every closure site to satisfy a bound synthesized at every parameter -- is two unconditional widenings composing, and a move closure must clone anything still live afterward, raising clone count on the axis this lane is reducing. Measured: the seed closure has 99 impl Fn parameters and ZERO functions returning Rc<dyn Fn>, so the unconditional form bought nothing there. After the gate, emitted '+ 'static' bounds in the regenerated seed closure: ZERO (the two remaining matches are a hand-maintained file's own bound and this emitter's own string literal). Regen drift falls from 8 files to 2. Not yet at the fixed point: std_occurrence_binding.rs carries a SEPARATE regression -- AmbiguousBindingCandidates<N> loses a required N: Clone through an Rc-wrapped field -- which is Row 1a header-bareness, a different class, tracked separately and not repaired here. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
… fields
impl_bodies-nonempty was ONE FLAG ENCODING TWO FACTS: it stood in for "does
some field's type require this parameter to carry a bound", AND it was the
switch keeping the item header in agreement with its supplemental impl
headers. Removing it from the header site alone revealed the second fact
rather than breaking an invariant.
Both facts now derive from one authority. Header bareness reads
clone_bounded_type_params (the well-formedness fixpoint, "does NAMING G<A..>
require P: Clone"), which is exactly the field-derived property; the
GroupCompletion supplemental impls read the same record through one Bool.
Rust REQUIRES an impl's generics to satisfy the type's own bounds, so
struct S<M: Clone> with impl<M> Neg for S<M> cannot compile. The two headers
are ONE decision rendered twice, which makes single authority the construction
answer here, not a style preference. Note the two renderings are NOT the same
string: the bound belongs in the impl generics and must NOT appear in the type
reference (GroupCompletion<M: Clone> is not Rust).
Only a compiler fact crosses into extdeps.languages.rust.emit -- a Bool about
the carrier's declaration. The spelling (Clone, trait paths, where-clauses)
stays in extdeps, so the §3 duty is intact. Deliberately NOT routed through
spelling.where_bounds, which carries the OP's own requirement: merging them
would recreate the fused flag this change splits.
Precedent, same file: reference_use_lines_representation_invariant_note
resolved this class by deleting both halves of a fused flag and letting each
fact derive from its own authority.
ERROR TRAJECTORY -- the 21 were not a regression of the 1; they appeared ONLY
because the header fix was correct, and are resolved by feeding the impl
headers the same record:
161 unconditional 'static
1 after the 'static gate
21 after the field-derived header rule (header fixed, impls still bare)
0 after both headers read one authority
VERIFIED: first_generation_equal=true AND fixed_point_equal=true.
FILE OUTSIDE THIS LANE'S DECLARED PAIR: dag/extdeps/languages/rust/emit.dag
carries the impl header literal and has no sole-write owner; announced here so
anyone who considers it theirs sees it. Leaving the literal and matching it
compiler-side would have been a second representation of one fact.
KNOWN ASYMMETRY, named not fixed: the item header is rendered compiler-side
while the impl header is rendered in extdeps, so the tree answers "where does
a Rust bound get spelled" two ways.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Splitting the fused impl_bodies flag left has_fn_fields as the only thing
between the well-formedness record and the emitted header, which is exactly
where a second proxy would hide. Checked rather than asserted: it IS partly a
proxy.
It is a real discriminator for the DERIVE half -- a derive emits per-impl
bounds, so Monoid<T> { identity: T } with derive(Clone) gets
impl<T: Clone> Clone generated and needs no header bound. That half is correct
and is now stated as the reason, replacing a vague 'different carrier shape'.
It has NO authority over the WELL-FORMEDNESS half, which is independent of
derives entirely, yet it gates that computation too.
Population measured as a closed enumeration, not a spot check: 17 callable-
field generic structs (16 derive Clone); the complete non-callable, non-phantom
field set is &'a str, identity/zero/one/top/bottom: T, and
Rc<PartialFunction<K, V>>. Exactly one names a declared type, and
PartialFunction<K, V> is itself bare, so it carries no obligation. The exposing
population is EMPTY -- 'no instance today', NOT 'cannot occur'.
Deferred on displaced cost, NOT on unavailable evidence: a controlled fixture
plus a regen fixed point over the live 17 are both available today. With the
population empty the benefit is zero while the risk against 17 live carriers
is not, so it is worth less than what it displaces. Recording 'no evidence'
would have been false and would have retired on the wrong condition.
Mirrored defect class, worth stating together: 'static was a bound APPLIED to
a population that could not need it; this is a bound SUPPRESSED for a
population that does not yet exist. 'It cannot reject anything' and 'nothing
needs it' are the same unmeasured claim at opposite signs.
Annotation-only: regen fixed point unchanged (§4c -- annotations cannot alter
target-program bytes), verified rather than assumed.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…tion control CI caught a real miss: rust_supplemental_impls_group_completion gained a parameter and src/v2/test/claim/emit/trait_derive_seed_emit_binding_test.dag still called it with zero arguments -- four call sites, four call-shape refusals. I verified the emitter and the regen fixed point but not the WITNESS consumers of the signature I changed. Local regen cannot catch this; only the floor runs those tests. Also adds the control that was missing for the failure mode most likely to recur. Rust requires an impl's generics to satisfy the type's own bounds, so the carrier bound is ONE FACT rendered TWICE, and the two renderings are not the same string: impl<M: Clone> Neg for GroupCompletion<M> compiles impl<M: Clone> Neg for GroupCompletion<M: Clone> is not Rust An implementation that renders one bounded value and substitutes it at both positions passes a naive presence check and emits code rustc refuses, so the negative assertion is the load-bearing half. The false arm additionally proves the parameter DISCRIMINATES: without it, hardwiring the bound on would satisfy every positive assertion. VERIFIED BY EXECUTION, both directions, not by compiling: correct emitter -> returned true perturbed to bound the TYPE position too -> returned false restored -> returned true Also corrects a now-false sentence in trait_derive_emit_item_clone_bound_rule_ note, which still claimed arithmetic carriers with supplemental impl blocks keep bare item params. They no longer do. Corrected in place rather than left standing beside the change that falsified it. Note the two prose edits behave differently, which is §4c working as specified: the leading // annotation added earlier changed ZERO emitted bytes, while this data ...: String row is program data and drifted its mirror. Prose in a String is not free. Regen: first_generation_equal=true AND fixed_point_equal=true. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
`Read` was the else-branch of a tail test. `walk_expr` recorded `kind: Consumed`
when `in_tail` and `kind: Read` otherwise, and `in_tail` is true only at the
function-body root and `ExprReturn` children, propagating solely through
tail-preserving constructs. Call arguments never receive it. So `f(x)`,
`S { a: x }` and `vec![x]` all recorded `Read` while consuming by value,
indistinguishable from `xs.len()`, which genuinely borrows.
That made `Read` ignorance wearing the name of a finding. A consumer reading it
as "borrowed" -- a proposed third emitter arm did exactly this -- would emit a
borrow where an owned value is required, or a move where the name is still live.
Use-after-move, corpus-wide. The existing `.clone()` is not a missing arm; it is
the sound answer given a signal that cannot tell borrow from move.
The walk now records only what it observes, on two axes that are orthogonal:
position TailPosition | NonTailPosition propagates through tail-preserving
constructs, as `in_tail` did
context the immediate syntactic parent set at each recursion, never propagated
They cannot share a carrier. Tail-ness must propagate, so a merged value's
context half would describe whichever ancestor last wrote it -- in
`f(match x { A => y, B => z })`, `y` would record a call-argument context it does
not have. `UnclassifiedContextUse` is spelled as ignorance on purpose, and is the
arm to watch: a rising count locates expression forms the walk cannot classify.
`EdgeKind` is unchanged and still derived from position alone, so this names the
state space without narrowing it. Narrowing a context to a borrow requires
evidence that the context cannot consume, which a method receiver (`into_iter`)
and a call argument do not have. That evidence, and the per-use-site decision it
licenses, are the next increment; this commit deliberately adds no behaviour.
Verified by execution rather than by argument. Generation 1 proves nothing here,
since a change inside the compiler can only show its own mirror drifting; the
oracle is generation 2 -- install, rebuild so the compiler contains the new walk,
regen again:
required-regen: first_generation_equal=true planned=129 executed=129
All 129 modules re-emit byte-identically. The oracle can fail: any third drifting
file would falsify behaviour preservation. Binary freshness proved by content on
both sides -- the new `ForEachCollectionUse` present and the prior build's
`UnclassifiedPosition` absent -- which a stale artifact cannot satisfy.
`v1_compiler_emit_rust.rs` drops two dead `EdgeKind` re-export lines; nothing in
the corpus references them.
|
Holding this as a draft deliberately — please do not flip it to ready yet. Two independent reasons, either of which is sufficient. 1. It is a superset of #8574, which is already approved and awaiting merge. This branch was cut from #8574's head, so it carries 13 commits of which 12 are #8574's: Flipping this to ready would put twelve already-approved commits back in front of a reviewer and split review attention across two PRs holding the same content. The sequencing is: #8574 merges, then I rebase this onto main so it carries one commit. 2. The commit's new field has zero readers, and that is disqualifying on its own. The change splits That is DESIGN §6's "new artifact with no final consumer." The What the commit is for, since that context is not obvious from the diff alone: Verification already executed, for whoever picks this up: generation 1 proves nothing here, because a change inside the compiler can only show its own mirror drifting. The oracle is generation 2 — install, rebuild so the compiler contains the new walk, regen again: All 129 modules re-emit byte-identically, so the change is behaviour-preserving by execution rather than by argument. It can fail — any third drifting file would falsify it. Binary freshness was proved by content on both sides (new Note the byte oracle's blind spot, since it bears on reviewing the next increment: it cannot see a meaningless field. An earlier version of this commit merged both facts into one carrier, so propagating tail-ness dragged the grandparent's context along — — sent from swift-moth-294 |
The emitter derived Clone bounds on generic fn params ~30-48 references deep
and derived nothing for equality, so a generic fn comparing two values of its
own type param emitted bare and rustc refused it:
error[E0369]: binary operation `==` cannot be applied to type `K`
Two existing body-reading Clone triggers each declare a depth boundary --
BoundedToDirectTopLevelMatchBody and BoundedToDirectSingleCallLambdaBody, both
refusing to walk a body that reaches its target through Let/If/Branch -- and
both name the SAME next-rung trigger in the same words: a recursive body walker,
once a real specimen needs either boundary widened. Equality is that specimen,
so this builds the walker rather than adding a fourth shallow body-reading path
beside two already-declared-incomplete ones.
The traversal is generous and the predicate is exact, because the two directions
have asymmetric cost. binop_left/binop_right are children 0 and 1, so descending
every child reaches every operand, and a node the walk misses simply yields no
bound -- the status quo. But a bound is not free in the other direction: adding
`K: PartialEq` TIGHTENS the emitted signature, so a spurious bound breaks every
caller instantiating K with a non-PartialEq type. Precision therefore lives in
v1_equality_bound_param_name, which requires both operands to resolve to the
SAME type name and that name to be one of the fn's own generic params -- never
the presence of a `==`.
Layering follows the existing split, for the reason trait_bound_witness.dag's
own import note gives: the Node-free decision core lives there, the Node-walking
extraction lives at the call site in emit_fn_def. Reversing that would close an
emit_rust -> trait_bound_witness -> emit_rust cycle, which acyclicity forbids.
Rendering is generalized rather than forked. The trait was a hardcoded literal
(`concat(pascal, ": Clone")`), so a param earning two derivations could not be
expressed. v1_emit_type_params_with_bounds is keyed on a per-param trait list --
one axis, so a third trait later is a row rather than another parameter --
and v1_emit_type_params_with_clone_bounds is retained at its original signature
as a byte-identical adapter over it, leaving its own caller untouched.
MEASURED ON REAL EMISSION, both arms, same fixture shape differing only in what
the `==` compares:
positive fn cap_row_has<K>(row: CapRow<K>, shape: ShapeTag, key: K)
row.shape == shape && (row.keys |> any(k => k == key))
-> pub fn cap_row_has<K: Clone + PartialEq>(...)
negative fn cap_row_shape_is<K>(row: CapRow<K>, shape: ShapeTag)
row.shape == shape
-> pub fn cap_row_shape_is<K: Clone>(...)
The negative is the discriminating arm: a generic param IS present and a `==` IS
present, and it must still earn nothing. A predicate keyed on the operator emits
`K: Clone + PartialEq` there and breaks callers; zero PartialEq occurrences
appear anywhere in that emission. The walker reached the earning comparison
through lambda -> any() -> pipeline -> `&&`, the depth both boundaries refuse.
The expected spelling is externally grounded rather than copied from this tree:
quick-lynx-620 measured it against rustc on the real emission, hand-adding the
bound to the emitted mirror and rebuilding the seed to zero errors, and reported
`K: Clone + PartialEq` -- the equality bound COMPOSING with the separately
derived Clone bound rather than replacing it. Clone survives in both arms above.
Corpus safety, with the trigger live:
required-regen: first_generation_equal=true planned=129 executed=129
All 129 modules re-emit byte-identically, so the trigger fires nowhere in the
existing corpus and adds no spurious bound. That also means the corpus supplies
no positive evidence, which is why the enrolled witness is a controlled fixture
authoring both comparisons itself rather than a pin to a live population; the
real-world specimen lives on an unlanded branch and is confirmation, not gate.
|
Correcting my earlier comment on this PR: it now carries TWO new commits, not one. My first comment said "13 commits of which 12 are #8574's" and named The two have opposite readiness, which is exactly why they must not be reviewed as one PR:
These get split. The equality trigger will move to its own PR once the mirror queue ahead of it clears (#8693, then #8691 — that one's regen surface is corpus-wide at ~50 mirrors against my 3, so it goes first and I rebase onto it). This PR keeps the ownership carrier and stays draft. Why the equality commit is landable on its own, since that is the part a reader would otherwise have to reconstruct from the diff: the emitter derived Both arms measured on real emission, same fixture shape, differing only in what the The negative arm is the discriminating one: a generic param is present and a The expected spelling is externally grounded rather than copied from this tree: another session measured it against rustc on the real emission, hand-adding the bound to the emitted mirror and rebuilding the seed to zero errors, and reported Corpus safety, with the trigger live: — sent from swift-moth-294 |
|
CI failure diagnosed: it is not a defect in any commit here, and this PR cannot go green as constituted. Recording the cause and the remedy. The failing phase is The receipt phase refused on breadth: Six changed authorities because this branch carries three separate pieces of work at once — #8574's twelve commits, the ownership carrier, and the equality trigger. That is exactly the superset problem documented in the two comments above, now enforced by a gate rather than only by argument. The remedy is the split, and the gate names it. From
So: split. Raising the cap is an operator decision and not mine to take — and a diff that defeats a fail-closed refusal is a hard reject under §5 regardless of what else it delivers. This is a well-built gate: it declines to sample-and-report-pass, and its dissolution condition is stated (it exists because the differential rebuilds the whole crate per candidate; it is raised, not removed, when per-module compilation makes cost linear in the changed surface). Verified the split is viable rather than assumed. The equality commit's source diff applies cleanly to current main with no conflicts, confirming it does not depend on #8574. Split apart, each piece is under the cap:
So no fix is pushed to this branch. The equality work moves to its own PR; this one keeps the ownership carrier and stays draft until #8574 merges and it can be rebased to carry one commit. The ownership carrier remains blocked on its missing consumer independently of all this — 34 writes, 0 reads. One thing worth noting for anyone reading the run: the receipt phase's self-test passed immediately before the refusal — — sent from swift-moth-294 |
Closing this unmerged, deliberately, with the finding landed firstRuling: smart-ram-730, who prioritized this lane, reversed that prioritization. The deciding fact is that the consumer this was built for does not exist. Why it is being closed rather than finishedThe carrier is complete and verified. It splits a real state-space conflation in It also writes I built it expecting the So this is not a lane declining to use the carrier. It is a lane that found a proof which does not need one. Manufacturing a plausible consumer to keep verified code alive would be exactly the failure §6 warns about, and neither smart-ram nor I could name a real one after looking. The finding is landed, which was the conditionI claimed this finding was "already durable" and that was false — I asserted a property of the repository without measuring it. Grepping It now lives on the carrier itself, as a leading annotation above That last part is the whole point of closing this way. A deletion that hides prior art from the person who next needs it is worse than the draft staying open. What survives, precisely
Not deleted, not lost, and not pretending to be finished. |
…omes quarry (#8710) Read is the `else` of `in_tail`: walk_expr's ExprVar arm branches on one bit, so a tail position records Consumed and every other position -- argument, receiver, scrutinee -- falls to Read. Read therefore conflates "provably non-consuming" with "never classified", which is invisible from a declaration that reads like a four-way classification of use. The consequence worth recording is that an optimization keyed on Read emits a borrow where an owned value is required, because Read fires in genuine by-value positions. That arm was proposed and refuted on this reasoning before it was written, and nothing in the tree said why -- so the next reader would propose it again. The sound alternative is already in tree: the iter_owned receiver disposition proves borrow-safety from the receiver's syntactic position and consults no ownership classification at all. The annotation also carries #8689 as a quarry pointer. The two-axis split that fixes the conflation was built and is behaviour-preserving at gen-2 first_generation_equal 129/129, and is being closed unmerged for want of a consumer. Without the pointer the deletion hides prior art from exactly the person who next needs it. Annotations are erased before the semantic passes, so this costs no regen. Verified rather than assumed: the same binary compiled this module with and without the annotation and the emitted v1_compiler_ownership.rs is byte-identical (33683 bytes both arms), with 0 differing files across the entire emitted closure. Erasure control discriminates -- the annotation text appears in the source and not in the emission, while EdgeKind itself survives. Co-authored-by: Brian Searls <briansearls1@gmail.com> Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Auto-opened by session-dashboard for session
swift-moth-294.Pushing to
ownership-use-site-carrieradvances this PR.Worker attestation
Before flipping this PR to ready for review, confirm each item:
npm test,cargo test) and the result.Closes #Ndirective.Summary
TODO: replace this paragraph with one or two sentences naming the change and its motivation. Reviewers read this first.
Test plan