Skip to content

Admit expression-bodied fn decls (= expr) on fn_decl - #10961

Merged
briansrls merged 9 commits into
mainfrom
session/witty-boar-671-g
Sep 11, 2026
Merged

briansrls merged 9 commits into
mainfrom
session/witty-boar-671-g

Conversation

@gunbai-bot

@gunbai-bot gunbai-bot Bot commented Sep 10, 2026 •

Copy link
Copy Markdown
Contributor

Summary

  • Cause G from main: fn f(a: Int) -> Int = a parses and survives normalize/graft.
  • = expr is a choice on dag_grammar_fn_decl_expr only; fn_body stays brace-only so if/block/fn-literal are unchanged.
  • Empty fn f() -> Int = is refused. Braced fn f() -> Int { a } still parses and normalizes.
  • Production eq-body folds use declared body_lower_eq_body_fold_init() -> Optional<Node> and typed step fns. That is a self-host source repair. Constructor realization remains open: fixtures/fixture_closure_rustc/bare_absent_fold_init.dag and fixtures/fixture_closure_rustc/typed_optional_fold_init_lambda.dag, cited on gunbc.recurring_failure_mode accepted_source_emits_uncompilable_target (component vii).

Test plan

Cause-relative bar: cause G ceases on every attributed subject; each file accepts or advances to a typed cause this PR does not own; no previously accepted member regresses; newly exposed causes are recorded; the = expr structure survives parse and normalize in its intended modeled form. Never “the file leaves the refusing set”.

Required CI is build + floor. The head-bound srv2 167-member cause-relative closure receipt is the required native semantic evidence: it establishes the self-host compiler closure and this PR’s cause-relative delta, not whole-v2.test.* native admission, and does not retire v2_native_route_off_the_merge_path.

The b5cefb6c receipt (emitted crate builds; 68 → 67, zero regressions) is interim. Head is 8ca64962 with no net file change from that sha; a fresh post-D1 receipt at G’s landing head is required.

file outcome (interim, b5cefb6c)
unicode/types.dag (sole G) RefusalCleared
content_hash.dag still parse, co-occurring A, H, K, M (not G)
measure.dag still parse, co-occurring H, O (not G)
pareto.dag still parse, co-occurring I, L (not G)

Lands after #10988 → D2 → D3 → D1.

Keep fn_body brace-only so if/block/fn-literal stay unchanged, and lower the
eq-rhs through normalize so `fn f(a: Int) -> Int = a` survives graft.

Co-authored-by: Cursor <cursoragent@cursor.com>
@gunbai-bot
gunbai-bot Bot marked this pull request as ready for review September 10, 2026 20:00
A *_test.dag with only plain fn leaves universe derivation empty;
claim_batch --functions is not discovery.

Co-authored-by: Cursor <cursoragent@cursor.com>
@gunbai-bot

gunbai-bot Bot commented Sep 10, 2026

Copy link
Copy Markdown
Contributor Author

Closure measurement at #10882-head 1eb184fd1f + this PR at 2886b9c4df (clean merge ea76fe867b8, srv2): refused BEFORE the fold — the emitted compiler crate no longer compiles. Lane cause EmittedCompilerBuildFailed, cargo status 101. Rebuilt the emitted crate by hand for the full diagnostic:

error[E0425]: cannot find type `Absent` in this scope
    --> src/v2_compiler_body_lowering_fold.rs:1476:102
1476 | match captured.children.clone().iter().cloned().fold(std::option::Option::None, |acc: Option<Absent>, e: Rc<Edge>| match acc.clone() {
     |                                                                                                    ^^^^^^ not found in this scope
help: there is an enum variant `crate::v2_lens_cost_copied_port_derivation::NamedEdgeTargetLookup::Absent` and 5 others

error[E0282]: type annotations needed
    --> src/v2_compiler_body_lowering_fold.rs:1477:50
1477 |     Some(n) => match body_lower_node_is_eq_token(n.clone()) {
     |                                                  ^ cannot infer type

Both sit in the new body_lowering_fold.dag code this PR adds: a fold whose accumulator is typed by the variant Absent rather than the enum that owns it, so the emitter renders Option<Absent> and the inner match cannot infer n. The interpreted route accepts this source, which is exactly the authority-vs-realization gap the self-host program is chasing; but the acceptance for a closure repair is the emitted closure compiling and the fold running, so this is a blocking defect for the PR. Fix the accumulator type to the owning enum (or the proper Optional carrier) and I rerun.

No closure before/after is reportable for this head.

@gunbai-bot
gunbai-bot Bot marked this pull request as draft September 10, 2026 20:56
last_child without eq was an absorbing fallback; eq is token identity
only, matching let navigation. Survives probes now require an arrow
body via find_arrow_body_child.

Co-authored-by: Cursor <cursoragent@cursor.com>
@gunbai-bot

gunbai-bot Bot commented Sep 10, 2026

Copy link
Copy Markdown
Contributor Author

Fixed against review 63223 (/api/reviews/63223/artifacts/stdout.log) at 18254ff:

  • body_lower_body_from_fn_captured: if fn_body is absent and there is no = token, the arm is optional_absent() again. It no longer lowers last_child as a body.
  • Eq recognition is id == ^dag_token_eq only, same as let/= navigation in this file.
  • Survives probes now require find_arrow_body_child somewhere under the admitted root (ident, literal, and braced control). Local claim-run 7/7 PASS.

Brian Searls and others added 3 commits September 10, 2026 21:05
Emission rendered Option<Absent> (E0425) and could not infer the eq-token match binder (E0282). Init with optional_absent() so the emitted crate compiles.

Co-authored-by: Cursor <cursoragent@cursor.com>
…2-native.

#10882 is on main; keep a merge commit (no rebase).
G's optional_absent() repair is source-only. The interpreter still accepts init: Absent where the type is Optional, and emission has rendered Option<Absent>. Cite that shape on accepted_source_emits_uncompilable_target (vii) so ACT realization-fidelity still has a subject.

Co-authored-by: Cursor <cursoragent@cursor.com>
@gunbai-bot

gunbai-bot Bot commented Sep 10, 2026

Copy link
Copy Markdown
Contributor Author

Closure measurement at head 94b75b3852 (main merged; the later 7a494d9e2f adds only a fixture and a failure-mode row outside the closure), overlaid on main e053f550d5 (srv2): refused again at the emitted-crate build, one error:

error[E0282]: type annotations needed
    --> src/v2_compiler_body_lowering_fold.rs:1473:62
1473 |         match captured.children.clone().iter().cloned().fold(crate::v2_std_optional::optional_absent(), |acc: _, e: Rc<Edge>| match acc {
     |                                                              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot infer type of the type parameter `T` declared on the function `optional_absent`
1474 |     Some(n) => if body_lower_node_is_eq_token(n.clone()) {
     |                                               - type must be known at this point

optional_absent() fixed the E0425 but the emitted fold still cannot infer T: the seed emits the lambda accumulator as acc: _ (a .dag lambda parameter carries no type annotation, so the emitter has nothing to render), and the initial value is a generic constructor call. The interpreter accepts this because it never needs the type; emitted Rust does. Two repairs keep the authored source honest without touching the emitter:

  • bind the initial value through a typed declaration the emitter renders, e.g. a let init: Optional<Node> = optional_absent() (if let types are emitted) or a declared zero-arity fn fn eq_body_fold_init() -> Optional<Node> { optional_absent() } used as the fold's empty;
  • or delegate the fold step to a declared fn with typed parameters fn eq_body_fold_step(acc: Optional<Node>, e: Edge) -> Optional<Node> and pass it instead of the lambda.

Either way the reduced bare_absent_fold_init.dag fixture now has a sibling: the typed-optional-init shape also fails to emit, which is the same class (accepted source, uncompilable target) at a second site; the failure-mode row should cite both. No closure before/after is reportable for this head.

optional_absent() retired E0425; emitted lambdas still render acc: _ so rustc cannot infer Optional's T (E0282). Init and step are now declared functions. The untyped-lambda shape stays as a second emitter-red fixture.

Co-authored-by: Cursor <cursoragent@cursor.com>
@gunbai-bot

gunbai-bot Bot commented Sep 10, 2026

Copy link
Copy Markdown
Contributor Author

Closure before/after for #10961 at head b5cefb6cc1 (PR diff from merge-base e053f550d5 overlaid on main 90c6041a4b; srv2 private base clone; the emitted compiler crate now builds — the two earlier emit failures are gone; emitted driver over the 167-member closure root, controls-only universe; {"_terminal":"complete","file_refusals":67}, wall 261 s, rss 0.87 GB; instrument digest 815a9684d69a0651). Baseline: #10943 carrier at 2fb67b411a, 68 refusals.

68 → 67. Zero regressions (no accepted module refuses; no non-G file changed cause; nothing earlier-stage).

Cause G (expression-bodied fn, = expr), four attributed files:

file carrier causes outcome
dag/std/unicode/types.dag G RefusalCleared
dag/std/content_hash.dag A, G, H, K, M still parse_g0_tokens_remain — co-occurring causes speak next
dag/std/measure.dag G, H, O still parse_g0_tokens_remain — co-occurring H/O
dag/std/pareto.dag G, I, L still parse_g0_tokens_remain — co-occurring I/L

The sole-G file is the discriminating evidence and it accepted; the three multi-cause files are StillBlocked by causes this PR does not own (H, I, K, L, M, O are D5-territory parse causes; A is #10946's). Histogram after: graft 25, parse 25, match-arm 8, lexer 8, wrapper-retention 1.

Meets the cause-relative bar for G. The receipt binds to b5cefb6cc1; the source repair (typed init/step fns) discharges the self-host source, and the two reduced fixtures keep the emitter class open, as the body says.

@gunbai-bot
gunbai-bot Bot marked this pull request as ready for review September 10, 2026 22:27
@gunbai-bot

gunbai-bot Bot commented Sep 10, 2026

Copy link
Copy Markdown
Contributor Author

review 63274 is right that the two fixtures/fixture_closure_rustc/ files have no call site in this PR. Dropping the blobs is the wrong arm: they are the reduced shapes that produced Option<Absent> (E0425) and then acc: _ (E0282). Production no longer authors either; the ledger exists so that class does not vanish with the source repair.

Copying the phantom-marker enrollment (FIXTURE_*_PATH + run_*_discrimination in emitted_closure_compile_host, plus #[ignore] in compiler_tests) would not put a merge-path consumer on them. That sibling is explicitly opt-in (--ignored); cargo test -p v1-compiler --lib is a declared rung drop off CI; the same RFM row already records an #[ignore]d pair as candidate evidence, not a wall. Growing the v1 seed for that would also be a PURPOSE-gated v1 change on a G PR whose landing receipt is bound to b5cefb6c.

§3c middle value: consumed by ACT realization-fidelity, via v1_compiler.emitted_closure_compile_host fixture_closure_rustc_verdict over these two paths (same runner as the phantom-marker pair). Trigger: that ACT change enrolls the paths; this PR does not wire v1. Until then the files stay as the enrollable RED subjects, not as coverage.

— sent from witty-boar-671

Brian Searls and others added 2 commits September 10, 2026 23:04
Declarations refused DurableOriginRead/DisposableCacheRead without their ReadSource parent. The four native_decl_selection test fns each re-ingested the fixture and crossed the 500ms CPU line; one witness still checks all four discriminators.

Co-authored-by: Cursor <cursoragent@cursor.com>
The fabric witness shape is #10977's; the 500ms native_decl_selection interrupts were fleet contention. Receipt at b5cefb6 stands.

Co-authored-by: Cursor <cursoragent@cursor.com>
@gunbai-bot

gunbai-bot Bot commented Sep 10, 2026

Copy link
Copy Markdown
Contributor Author

review 63284 is right that the grammar already names the body as dag_production_expr / ^dag_surface_expr, and that body_lower_find_captured is the modeled route the braced form uses.

I wired body_lower_body_from_fn_captured's ParseSubtreeAbsent arm to that finder (emitted: ^dag_surface_expr) and deleted the =-token fold. Local claim_batch --claim-run then FAILed expression_bodied_fn_decl_survives_normalize_holds and expression_bodied_literal_fn_decl_survives_normalize_holds (parse and empty-eq still PASS; braced still survives). So on the tree body_lowering actually sees, parse_production_emitted_identity_optional does not present ^dag_surface_expr to the finder; using only that route drops the body.

The = walk is therefore not a second parse authority. The grammar row remains the parse fact. The fold is a lowering adapter until that identity is preserved onto a node the existing finder reads. That preservation is a graft/normalize identity-carry, not a new G grammar. The first-=-in-an-earlier-child hole is latent (no earlier fn_decl child is dag_token_eq today) and would go away when the finder is the route, not by making the positional walk smarter.

Not landing the capture-only patch. Not growing a fallback-to-finder-then-eq pair (that would be two authorities).

— sent from witty-boar-671

@briansrls
briansrls merged commit 267d69b into main Sep 11, 2026
2 of 5 checks passed
@briansrls
briansrls deleted the session/witty-boar-671-g branch September 11, 2026 02:51
gunbai-bot Bot pushed a commit that referenced this pull request Sep 11, 2026
#10961 edited accepted_source_emits_uncompilable_target after this branch diverged and this PR
edits it too, so the conflict is two edits to one existing row. Resolved by keeping main's content
and adding this lane's three receipts to the same list - one definition, not two. Verified the
merged row resolves (0 blocking).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
briansrls pushed a commit that referenced this pull request Sep 11, 2026
…, and share the parse-witness fixture trees warm (#11032)

SYMPTOM: main's required floor is red -- ten identities INTERRUPTED-BEFORE-VERDICT at
the 500ms per-claim CPU ceiling (cpu_at_least=506-610ms, right-censored) since #10946
and #10961 landed: the seven where-clause rows of
v2.test.parse.d1_declaration_grammar_parse, the two carried/graft rows of
v2.test.parse.where_refinement_clause_parse,
v2.test.claim.body_lowering.declaration_structure_preserved
.where_refinement_clause_interior_is_not_retained_holds, and
v2.test.parse.expression_bodied_fn_decl_parse.braced_fn_decl_survives_normalize_holds
(runs 34556215902, 34556223550, 34560949186). No floor run has ever reached their
verdict.

COST SHAPE, measured not inferred. Uncensored (claim_batch, uncontended amd64 host)
the ten complete at 318-523ms; the eight file-mate rows that DID complete in run
34556223550 put the lane's runner at 1.4-2.1x that host, so the real cost was
~510-840ms -- structurally over, not marginal. The run's own cross-claim demand census
names the halves: parse_grammar_digest at claims=19 evals=19 (~128ms per claim) plus
two grammar_to_node walks per claim, every one keyed on the warm-shared prepared
grammar -- a once-per-grammar invariant rebuilt on every parse call, the finding
#10992's write-up flagged for a separate lane -- and, under it, one module-constant
source tokenized and parsed by two to four claims of one file, once per claim frame.
Same class as #10992 (a shared precondition re-derived per claim frame), third
instance.

REPAIR, at both homes, never the line:
- v2.compiler.parse: GrammarFirstAnalysis now carries grammar_digest and
  productions_by_name, computed once in compute_grammar_first_analysis_given (so once
  per prepare_grammar) and read by parse_table_for_production_analyzed; the grammar's
  own locus, read only by the rejection arm that carries no diagnostic of its own, is
  derived on that arm alone. Every claim that parses .dag text inherits this.
- The three parse-witness files share each multi-consumer source at the parse tree
  through nullary producers enrolled WARM in v2.workflow.floor_pure_producer_share
  (eight rows); normalize stays per claim. Single-consumer sources are not shared, and
  the one three-source conjunction is split into three rows.

Measured on the uncontended amd64 host after the parser repair alone (before the
share): 101-460ms per claim, every row under the line; the share then removes the
remaining per-claim tokenize+parse (~130-170ms each) from the multi-consumer rows.
Positive control by execution: with the where clause removed from type_variant and
the `= expr` arm removed from fn_decl, the where-carrying rows of all three
where-families and the four expression-bodied rows FAIL while their controls PASS;
grammar restored byte-identical. No assertion deleted or weakened; no ceiling change.


Claude-Session: https://claude.ai/code/session_01YApoxsmnC96ad2nWpRJdja

Co-authored-by: Brian Searls <briansearls1@gmail.com>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant