From cf78da95f48e5c949f67358c5f251b6f9396fb6b Mon Sep 17 00:00:00 2001 From: gunbc-ci-auto-heal Date: Sat, 29 Aug 2026 07:07:19 +0000 Subject: [PATCH] Building the module-path index was full-parsing the whole corpus as a side effect: one fact, two authorities, 21.8s to 13.4s MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `build_module_path_index_uncached` walks every `.dag` file under the source roots and FULL-PARSES each one, to project two facts that are both declaration HEADS: the module's name, and that name's span. On the seed closure (`--entry src/v1/compile.dag`, 3031 `.dag` files in tree) that pass costs 21.8s of a 451s run, and it runs before `compile.frontend` emits its first phase mark, so it does not appear in the phase table at all. The bodies were never the subject. What the full parse bought over the heads reading is a whole-tree "every authored `.dag` body is grammatical" refusal -- and that question already has an authority: required-CI phase 1, `cli_run::run_dag_parse_sweep` over `DAG_PARSE_SWEEP_ROOTS` (`src/v1`, `dag`, `src/v2`), which full-parses exactly these roots and exists to answer it. Two producers answering one question is the §3 fork, and this is the same cut already taken once for the pool census -- the scope statement at `v1.compiler.parse.parse_heads_with_table` records it, in the same words: answering it here "coupled every pool-derived resolve in the process to the grammaticality of every body in the corpus". So the index walk drops to the heads reading. This is a consolidation, not a cache: no memo, no shared state, no second path -- one producer stops answering a question that was never its own. WHAT STILL REFUSES, unchanged: an unterminated body, and every malformed item HEAD. What narrows is exactly one arm -- a well-braced but UNGRAMMATICAL `fn` BODY in a pool file no longer refuses HERE. It is not an unrefused state: the sweep above refuses it, and any file this invocation actually COMPILES is full-parsed again by the front end, which refuses there. A pool file outside the closure is never compiled by this invocation, so its body grammar was never a fact this walk needed. Measured, same instrument vintage both arms (nested `trace_mark` plus an accumulating timer compiled into the mirrors, localized by sampled backtrace; throwaway, not in this diff): build_module_path_index_uncached 21.84s -> 13.38s (-39%) reference_resolution_facts 24.56s -> 24.88s unchanged (control) extend_sources_to_both_closure 29.70s -> 29.26s unchanged (control) The two untouched whole-tree passes moving by less than a second is the control that says the delta is this walk and not the machine. The residual 13.4s is corpus acquisition -- read, tokenize, newline index -- not parsing. BEHAVIOUR, by execution on the real 3031-file tree: the emitted 108-file crate is BYTE-IDENTICAL (`diff -r`), and the run's 8723 diagnostic lines including the `resolved 102 sources ... 2928 indexed modules` receipt are identical. Discriminating REDs, both arms executed rather than argued: - a pool file with a malformed item head still refuses at the index, typed and located ("module index refused: 1 unparseable .dag source(s)", with path and span); - a pool file with a well-braced ungrammatical body does NOT refuse at the index (the declared narrowing), and `v1_src_dag_parse` refuses it -- exit 1 with the file and span, against a control run of exit 0 with the probe removed. The fact kept its authority; it lost its second one. `cargo fmt --all --check` clean; `cargo clippy --lib -- -D warnings` clean. The `--all-targets` clippy reds are pre-existing on this merge base -- verified by compiling each named target on the unmodified tree and getting the identical errors. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01XJLHPutzoQ4FCJ9fofP135 --- src/v1/stage0/src/module_path_index/index.rs | 26 +++++++++++++++++--- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/src/v1/stage0/src/module_path_index/index.rs b/src/v1/stage0/src/module_path_index/index.rs index b72849a43d1..4c1e78dec17 100644 --- a/src/v1/stage0/src/module_path_index/index.rs +++ b/src/v1/stage0/src/module_path_index/index.rs @@ -2,11 +2,11 @@ use im::HashMap; use std::path::Path; use std::rc::Rc; -use crate::v1_compiler_parse::parse; +use crate::v1_compiler_parse::parse_heads_with_table; use crate::v1_compiler_tokenize::tokenize; use crate::v1_std_core::{ - build_newline_index, diagnostic_to_message, diagnostic_to_span, node_name_span, - CompilerDiagnostic, SourceSpan, + build_newline_index, diagnostic_to_message, diagnostic_to_span, empty_intern_table, + node_name_span, CompilerDiagnostic, SourceSpan, }; /// One parse-derived module⇄path row for manifest emission (host binding authority). @@ -131,7 +131,25 @@ pub fn parse_module_binding( let mut indices = HashMap::new(); indices.insert(key.clone(), source_index); let source_indices = Rc::new(indices); - let result = parse(tokens, source_indices); + // THE DECLARATION HEADS ARE THE WHOLE SUBJECT, so this is the heads reading of the + // grammar rather than the full one. Every item HEAD is parsed by the same productions, + // and both facts this walk projects -- `module.name` and its span -- are heads. + // + // WHAT THE NARROWING GIVES UP, stated exactly: a well-braced but UNGRAMMATICAL `fn` + // BODY in a pool file no longer refuses here. That is not a lost refusal, it is one + // fact losing its second authority. Required-CI phase 1 (`cli_run::run_dag_parse_sweep` + // over `DAG_PARSE_SWEEP_ROOTS` -- `src/v1`, `dag`, `src/v2`) full-parses exactly these + // roots and owns that question; and any file this invocation actually COMPILES is + // full-parsed again by the front end, which refuses there. What still refuses below is + // unchanged: an unterminated body, and every malformed item head. + // + // This is the same cut already taken for the pool census -- see the scope statement at + // `v1.compiler.parse.parse_heads_with_table`. Building an index was full-parsing the + // corpus as a side effect, which coupled every pool-derived resolve in the process to + // the grammaticality of every body in the tree (DESIGN §3: one fact, one authority). + let result = parse_heads_with_table(tokens, source_indices, empty_intern_table()) + .result + .clone(); if let Some(err) = result.error.as_ref() { if module_declaration_line_present(content) { return Err(ModuleBindingRefusal {