Repository navigation
Emitter param defaults: constant-fold a field projection into a data record, closing all six whole-tree emission diagnostics without duplicating repository identity - #8693
Merged
Conversation
…record Whole-tree emission (source roots dag + src/v2) refused with six hard diagnostics, all one class: workflow CLI default for parameter `X` must be a string, int, float, bool literal, or data reference Four in gunbc.tools.review_codex, two in gunbc.tools.review, and no files emitted at all. ONE ROOT, TWO APPROACH ANGLES. v1.compiler.emit_rust resolve_param_default handled a default in exactly two shapes: a literal, and a bare reference to a data declaration whose body is a literal. review projects the field directly (`gunbc_repository.owner`), so it hit the refusing wildcard at depth 0. review_codex names a data declaration whose BODY is that projection, so it traversed the data-reference arm correctly and died one hop down on the same missing fold. The compiler could not constant-fold a field projection into a data record. fold_constant_default_expr is that fold: resolve a variable to its DataItem body, resolve a field access by folding its base and selecting the named field from the record literal, bounded by a depth limit and refusing past it. resolve_param_default now folds first and extracts a literal from the result, so a non-constant expression still reaches extract_literal_string and still refuses. This makes the diagnostic's own promise true rather than adding a capability it never claimed: the message already advertises "data reference" as admitted while the data-reference arm could not survive a non-literal body. NOT closed by respelling the projected values as literals at the six sites. gunbc.repository exists because five spellings of this repository's identity were consolidated into one authority; a sixth would be the DESIGN §3 violation the authority note exists to prevent. No repository literal appears in this diff. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…was the import closure, not the claim
The floor ran the new witness green — planned=9840 executed=9840 passed=9531
failed=0 — and then failed the run on cost:
COMPLETED-OVER-COST-REQUIREMENT
test.claim.workflow_default_field_projection_fold_witness_test
.field_projection_to_a_literal_field_is_folded_into_the_cli_default
reached its verdict and then exceeded its budget: Cpu, cost exactly
62567ms against 5000ms
and it was also the run's worst single claim by memory, growing RSS 1.12GB.
The cost was the fixture's import closure, not the assertion. `import
std.resources { Network }` and `import std.types { String }` resolve
transitively against the real corpus, so each arm compiled std to check a
param default. `Network` was never load-bearing: is_workflow_item only asks
that a `uses` clause exist, not what type it names, so a locally declared
`WdpNet` satisfies it and the fixture imports nothing at all. Both arms
still assert exactly what they asserted before — same record, same two
fields, same one-token difference — against a two-declaration closure that
compiles in ~27ms standalone rather than the whole corpus.
Re-run on the post-change binary: both arms true.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
6 tasks
Merged
5 of 15 tasks
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.
The subject
Whole-tree emission (
gunbc compile --source-root dag --source-root src/v2) refused with six hard diagnostics, all one class, and emitted no files at all:Four in
gunbc.tools.review_codex(pr_owner,pr_repo,cycle_owner,cycle_repo), two ingunbc.tools.review(owner,repo). Measured on the pre-change binary, not assumed.One root, two approach angles
v1.compiler.emit_rustresolve_param_defaulthandled a default expression in exactly two shapes — anExprLiteral, and anExprVarnaming aDataItemwhose body is a literal — and refused everything else.review.dagwritesowner: String = gunbc_repository.owner. That is anExprFieldAccess, so it hit the refusing wildcard at depth 0 and never reached the data-reference arm at all.review_codex.dagwritespr_owner: String = owner, anExprVar. It traversed the data-reference arm correctly, fetcheddata owner's body — which isgunbc_repository.owner, anExprFieldAccessagain — andextract_literal_stringreturnednone.Same wall, one hop apart. The compiler could not constant-fold a field projection into a data record.
The repair
fold_constant_default_expris that fold: anExprVarresolves to itsDataItembody and recurses; anExprFieldAccessfolds its base, selects the named field out of the record literal (record_lit_named_field_value_optional, which already existed inv1.std.core) and recurses on the field's value; anything else is returned as-is. Depth-bounded byworkflow_default_fold_depth_limit, refusing past it.resolve_param_defaultfolds first and then extracts a literal, so a non-constant expression still reachesextract_literal_stringand still refuses.No evaluation, no runtime, no new grammar: every field of
gunbc_repositoryis a literal.This makes the diagnostic's own promise true rather than adding a capability it never claimed — the message already advertises "data reference" as admitted while the data-reference arm could not survive a non-literal body.
What was NOT done
The six were not closed by respelling the projected values as literals at the call sites.
gunbc.repositoryexists because five spellings of this repository's identity were consolidated into one authority (gunbc_repository_authority_note); a sixth is the DESIGN §3 violation that note exists to prevent, and has already been rejected once on this carrier (review 46301). No repository literal appears in this diff.Controls
New enrolled witness:
dag/test/claim/workflow_default_field_projection_fold_witness_test.dag. Two arms over the same fixture module — same record type, same data declaration, same workflowfunc— differing in one token: which field is projected.host's authored value is a string literal;joined's is aconcatcall. SameExprFieldAccessshape, same record, sameStringtype.default_value = "wdp.invalid"Measured by execution, both binaries, sha256 printed and distinct:
gunbc769fc55a0317dd67…gunbc42b0271a5e27b9e3…Arm 2 is the one that matters: a repair that admitted any field access passes arm 1 and fails arm 2. The fold widens the admitted set by exactly the foldable projections and not one thing more.
Generation discipline
gen-0 build →
--required-regen→ installtarget/stage0-regen-candidate/src/v1_compiler_emit_rust.rs→ rebuild → re-measure. The first regen drifted exactly one generated file (v1_compiler_emit_rust.rs) and nothing else. After merging currentmain,--required-regenre-run reportsfirst_generation_equal=true planned=128 executed=128— the committed mirror is a fixed point of the emitter that produced it.Scope bound, stated against what was actually observed
The six diagnostics are closed.
validate_workflow_param_defaultsruns at the top of emit and returnsEmitResult { files: [], diagnostics }immediately on any refusal: before this change whole-tree emit finished in 18 s and reported the six; after it, emit ran 88 minutes without that refusal and was still emitting when a 90-minute bound cut it off. Passing the gate is what is established.What is NOT established, and is not claimed: that whole-tree emission completes. It did not finish within 90 minutes on this container, so "the first complete emitted-tree subject" is a thing this change unblocks rather than a thing it demonstrates. Whole-tree emit wall time is a separate, unmeasured question and this PR does not answer it. A second run under a larger bound is in flight; if it lands before merge the number goes here, and if it does not, this paragraph stands as written.
Beyond that: this does not show the emitted Rust compiles, does not show behavioural equivalence, does not retire the seed, and does not move the rustc board.
Witness cost, because the first revision failed the floor on it
The first push ran the floor green on the assertion —
planned=9840 executed=9840 passed=9531 failed=0— and then failed the run on cost:COMPLETED-OVER-COST-REQUIREMENT … Cpu, cost exactly 62567ms against 5000ms, and it was the run's worst single claim by memory at +1.12GB RSS.That cost was the fixture's import closure, not the claim.
import std.resources { Network }resolves transitively against the real corpus, so each arm compiled std in order to check a param default.Networkwas never load-bearing —is_workflow_itemonly asks that ausesclause exist, not what type it names — so the fixture now declares its ownWdpNetand imports nothing at all. Same record, same two fields, same one-token difference between the arms; ~27 ms standalone instead of the whole corpus. Both arms re-run true on the post-change binary.