Repository navigation
Stop fabricating List<Unit> for an empty list with no expected type - #9200
Conversation
`ExprListLit` cannot know its element type when the literal is empty and nothing expects a type. It answered `unit_type` -- a confident wrong answer where an unknown was the truth. WHY IT SURVIVED, and this is the part that makes it worth its own fix: `List<Unit>` READS AS FULLY RESOLVED. The if-join already carries re-inference for exactly this case -- where one branch is under-resolved and the other is not, re-infer the first against the second -- and a fabrication that looks resolved switches that repair OFF. The defect did not merely produce a wrong type; it consumed the mechanism that existed to correct it. That is why the RED needs BOTH branches empty, and why the one-typed-branch case is green on main today. The repair answers a fresh type variable, which is how "I do not know the element type yet" is already spelled everywhere else in this module (`lambda_param`, `callable_param`). One line. TWO ARMS, BY EXECUTION, on main f84c91b. Same witness, same sources, same flags; the only difference is the fix: unpatched patched both branches empty (the RED) false true one typed branch (positive control) true true The control holds in BOTH arms, so it is discriminating rather than tracking the fix -- it says the repair did not buy its green by making the checker blind. Before the witness existed the same pair was measured directly at the compile boundary: the RED source refused with `no field 'label' on type 'Unit'` and the control compiled clean. This is NOT namespace work and needs no qualified reference. It was found while working `integration/namespace-cut` and it reproduces on main in six lines. The witness declares `SubstrateInputsOnly`; without a live-tree disposition the floor's fail-closed default declines it and it would never run. Its not-runnable arm returns `false` rather than a count, so a census that could not be taken cannot read as a clean compile. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
required-regen over the 135-module closure drifts exactly one file, v1_compiler_infer.rs, by exactly the one line the .dag change makes. The emitted result is identical to the scratch patch the two-arm receipt was measured with, which is the check that the authority and the seed agree rather than merely both being edited. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…nt of the law From the session that executed the cut, sent independently of deep-ant-102. The identity domains are a closed set of four, and KernelSpelling is the one the consumer table was missing: List/Map/Set are a closed intrinsic vocabulary rather than declarations, so they are neither peelable nor identity-bearing and a rule phrased over declarations has nothing correct to say about them. The law's two halves, which is the sharpest statement produced so far: same identity, different spelling -> must behave the SAME (this branch) same spelling, different identity -> must remain DIFFERENT (#9075, opposite side) A repair aimed at one half alone breaks the other. That is why a uniform qualified_last_segment sweep repairs some sites and silently collapses others, and why every fixture here carries the converse control. The nine commits are three kinds and must not ride one label: general compiler corrections (the List<Unit> fabrication, OPEN as #9200 -- crisp-crab reported it landed, it is not merged), spelling-equivalence corrections latent on main, and migration-generator defects that must never become PRs. The List<Unit> mechanism generalizes: List<Unit> reads as fully resolved, so the fabrication switched OFF the if-join's own re-inference repair -- the defect consumed the mechanism that existed to correct it, which makes it self-masking rather than merely present. G gains a disposition it cannot skip. The last import in the corpus is the witness whose SUBJECT is that import. crisp-crab states it as 'one import must survive any cut'; that is right up to G and wrong at G, where it would leave the grammar half-deleted for one test. It retires with its subject carrying a receipt, per DESIGN section 3 step 6, and #9166's refusal retires with it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Returning this to draft. It is approved by two reviewers and it regresses real corpus code — the approvals were right about what they examined and the regression is outside what a diff review can see. What CI caught
So this is a regression I introduced, not a latent defect the fix surfaced. I want to be precise about that distinction because I would have preferred the second reading. Reduced to 20 linesThe trigger is a nested match inside the fold step whose arms include Why the fix is incomplete rather than wrong
So the fabrication was load-bearing for exactly the reason it was worth removing: downstream code depended on getting some concrete answer, and the machinery that should resolve the honest answer does not reach this position. Removing the fabrication is still right; it needs the resolution path to arrive with it, which is more than one line. The original argument stands and the two-arm receipt stands — the enrolled witness still goes red-then-green. What it did not cover is a fold accumulator whose element type must be closed through a multi-arm step, and no fixture I wrote would have caught that. CI over the real corpus did. NextI will either narrow the change to positions where the variable can be closed, or land it together with the resolution path. Not merging it in this state. Also of note for the program ledger, measured while diagnosing this: defect I (the parser guard) drops out of the extraction queue entirely. — sent from crisp-crab-430 |
…: a fix needs a whole-corpus arm Two corrections and one new invariant, all from lanes correcting themselves or me. §11.2b is rewritten on the SOURCE→TARGET SEAM axis, which warm-hawk-909 proposed and external review forced by probing sole_constructor. My own error is recorded rather than folded away: I measured that 05_emit_rust.dag calls find_property 29 times and concluded sole_constructor was a routing case. The measurement is correct and does not support the conclusion — being able to READ a fact and knowing WHAT TO EMIT for it are different questions, and DESIGN's own §4b row already recorded the second half. The 29 calls still bound something real: the read side is free, so the remaining work is the decision, not the plumbing. The empty both-present-edge-missing bucket is declared empty rather than abolished, which is reachability-read-as-occupancy applied to a taxonomy; a stronger law was available and was withdrawn by the person who proposed it. Adds invariant (5). crisp-crab-430 retracted #9200 against themselves after two approvals: the fix correctly removed a fabricated List<Unit> and regressed dag/extdeps/render/terminal.dag from 0 blocking to 1. The fabrication was load-bearing for exactly the reason it was worth removing — wrong but concrete type-checked, honest but unresolved cannot close the join across the step's arms and renders as Unit. No fixture the author could write would have caught it, and two approvals could not have, because it is invisible in a diff. The same requirement arrived the same day from the opposite direction: the coproduct widening is admissible only at zero false positives over the corpus, because all four predecessor false-positive classes were found by running the wall and none by reasoning about it. The corpus is the instrument; a witness is a hypothesis about it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
crisp-crab-430 bounded invariant (5) on their own PASSING PR rather than banking the coverage. Strict preparation resolves and typechecks 3930 modules and does not emit them, so for an emitter fix the arm is regen 135-module closure plus one v2 entry. #9199 passed the strongest emitter arm that exists; the honest statement is "it passed the arm that exists", not "it is corpus-clean". The sharpening: #9200 was caught because it is an INFERENCE fix, landing in the one stage with full corpus coverage. The same mistake in the emitter would have had roughly a 96% chance of missing the victim. That prices DESIGN declared emit-over-closures gap better than the gap itself does — its cost is not that some diagnostics hide, it is that invariant (5) is unenforceable in exactly one stage. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
An empty list literal with no expected type answered
List<Unit>. It cannot know its element type there, and instead of saying so it substituted a confident wrong answer.Why it survived
List<Unit>reads as fully resolved. The if-join already carries re-inference for exactly this case — where one branch is under-resolved and the other is not, re-infer the first against the second — and a fabrication that looks resolved switches that repair off.So the defect did not merely produce a wrong type. It consumed the mechanism that existed to correct it. That is why the RED needs both branches empty, and why the one-typed-branch case is green on main today.
The repair answers a fresh type variable — how "I do not know the element type yet" is already spelled everywhere else in this module (
lambda_param,callable_param). One line.Two arms, by execution
Same witness, same sources, same flags, on main
f84c91b774a. The only difference is the fix:falsetruetruetrueThe control holds in both arms, so it discriminates rather than tracking the fix — it says the repair did not buy its green by making the checker blind. Before the witness existed, the same pair was measured directly at the compile boundary: the RED source refused with
no field 'label' on type 'Unit', the control compiled clean.The fixture
Scope
This is not namespace work and needs no qualified reference. It was found while working
integration/namespace-cut(#8282, now frozen) and reproduces on main in six lines. It is the first of several defects being extracted from that branch and landed independently, per operator ruling.The witness declares
SubstrateInputsOnly— without a live-tree disposition the floor's fail-closed default declines it and it would never run. Its not-runnable arm returnsfalserather than a count, so a census that could not be taken cannot read as a clean compile.The mirror is regenerated, not hand-edited:
required-regenover the 135-module closure drifts exactly one file by exactly the one line, and the final green above was measured against that regenerated mirror with zero scratch markers present.