From 9df7e726e4a2f65051f1956d28ed8615d828d1e1 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Tue, 6 Oct 2026 22:56:54 +0000 Subject: [PATCH 1/5] compiler_tests resolve_source_closure: close through the one compile-closure authority. An import-line BFS omitted providers reached only by reference. Production now calls resolve_seeded_compile_closure; RED / import-only mutant / refusal control live on a four-file fixture (0.06s). Co-authored-by: Cursor --- src/v1/compiler_tests_rust.dag | 94 +--------- src/v1/stage0/src/cli_run.rs | 3 +- src/v1/stage0/src/cli_run/entry_resolve.rs | 162 +++++++++++++++++- src/v1/stage0/src/compiler_tests.rs | 104 +---------- .../src/v1_compiler_compiler_tests_rust.rs | 2 +- 5 files changed, 182 insertions(+), 183 deletions(-) diff --git a/src/v1/compiler_tests_rust.dag b/src/v1/compiler_tests_rust.dag index f4fec02f769..2563776db26 100644 --- a/src/v1/compiler_tests_rust.dag +++ b/src/v1/compiler_tests_rust.dag @@ -111,95 +111,19 @@ fn ct_source_builders() -> String { " })\n", " .collect()\n", " }\n\n", - " fn parse_module_or_panic(path: &str, content: &str) -> std::rc::Rc {\n", - " let tokens = tokenize(content.to_string(), path.to_string(), crate::extdeps_languages_dag_syntax::dag_parse_environment());\n", - " let mut source_indices = HashMap::new();\n", - " source_indices.insert(\n", - " path.to_string(),\n", - " crate::v1_std_core::build_newline_index(path.to_string(), content.to_string()),\n", - " );\n", - " let parsed = crate::v1_compiler_parse::parse_with_table(\n", - " tokens.clone(),\n", - " std::rc::Rc::new(source_indices),\n", - " crate::v1_std_core::empty_intern_table(),\n", - " );\n", - " if let Some(err) = parsed.result.error.as_ref() {\n", - " panic!(\n", - " \"failed to parse {} while building source closure: {}\",\n", - " path,\n", - " crate::v1_std_core::diagnostic_to_message(err.diagnostic.clone())\n", - " );\n", - " }\n", - " parsed.result.module.clone().unwrap_or_else(|| {\n", - " panic!(\"{} produced no module while building source closure\", path)\n", - " })\n", - " }\n\n", - " fn module_path_from_source(path: &str, content: &str) -> String {\n", - " parse_module_or_panic(path, content).name.clone()\n", - " }\n\n", - " fn import_paths_from_source(path: &str, content: &str) -> Vec {\n", - " let module = parse_module_or_panic(path, content);\n", - " crate::v1_std_core::module_imports(module)\n", - " .iter()\n", - " .map(|imp| imp.name.clone())\n", - " .collect()\n", - " }\n\n", - " fn build_source_index(\n", - " roots: &[&str],\n", - " ) -> HashMap {\n", - " let mut index = HashMap::new();\n", - " for root in roots {\n", - " for (path, content) in discover_dag_files(root) {\n", - " let module_path = module_path_from_source(&path, &content);\n", - " if let Some((existing, _)) = index.get(&module_path) {\n", - " panic!(\n", - " \"duplicate module path '{}': declared in both {} and {}\",\n", - " module_path,\n", - " existing,\n", - " path\n", - " );\n", - " }\n", - " index.insert(module_path, (path, content));\n", - " }\n", - " }\n", - " index\n", - " }\n\n", " fn resolve_source_closure(\n", " entry_pairs: Vec<(String, String)>,\n", " roots: &[&str],\n", " ) -> Vec> {\n", - " let index = build_source_index(roots);\n", - " let mut seen = HashMap::>::new();\n", - " let mut queue = Vec::new();\n\n", - " for (path, content) in entry_pairs {\n", - " let module_path = module_path_from_source(&path, &content);\n", - " seen.insert(\n", - " module_path,\n", - " std::rc::Rc::new(crate::v1_compiler_compile::SourceFile {\n", - " path: path.clone(),\n", - " content: content.clone(),\n", - " }),\n", - " );\n", - " queue.push((path, content));\n", - " }\n\n", - " while let Some((_path, content)) = queue.pop() {\n", - " for module_path in import_paths_from_source(&_path, &content) {\n", - " if seen.contains_key(&module_path) {\n", - " continue;\n", - " }\n", - " if let Some((path, file_content)) = index.get(&module_path).cloned() {\n", - " seen.insert(\n", - " module_path,\n", - " std::rc::Rc::new(crate::v1_compiler_compile::SourceFile {\n", - " path: path.clone(),\n", - " content: file_content.clone(),\n", - " }),\n", - " );\n", - " queue.push((path, file_content));\n", - " }\n", - " }\n", - " }\n\n", - " let mut result: Vec<_> = seen.into_iter().map(|(_, v)| v).collect();\n", + " let seeds = entry_pairs\n", + " .into_iter()\n", + " .map(|(path, content)| {\n", + " std::rc::Rc::new(crate::v1_compiler_compile::SourceFile { path, content })\n", + " })\n", + " .collect();\n", + " let pool: Vec = roots.iter().map(|r| (*r).to_string()).collect();\n", + " let mut result = crate::cli_run::resolve_seeded_compile_closure(seeds, &pool)\n", + " .unwrap_or_else(|e| panic!(\"resolve_source_closure: {e}\"));\n", " result.sort_by(|a, b| a.path.cmp(&b.path));\n", " result\n", " }\n\n", diff --git a/src/v1/stage0/src/cli_run.rs b/src/v1/stage0/src/cli_run.rs index 728d0d0d83a..1552b20167c 100644 --- a/src/v1/stage0/src/cli_run.rs +++ b/src/v1/stage0/src/cli_run.rs @@ -143,7 +143,8 @@ pub fn source_root_ingest_module_identities_for_ci( } pub use entry_resolve::{ load_sources_for_entry, process_shared_index, resolve_entry_graph, resolve_entry_with_index, - resolve_stage_totals, source_root_ingest_content_hash_fnv1a64, whole_tree_resolved_ctx, + resolve_seeded_compile_closure, resolve_stage_totals, source_root_ingest_content_hash_fnv1a64, + whole_tree_resolved_ctx, }; mod live_read_decode; pub(crate) use live_read_decode::*; diff --git a/src/v1/stage0/src/cli_run/entry_resolve.rs b/src/v1/stage0/src/cli_run/entry_resolve.rs index 9aa4c5988d2..13bba8dad7c 100644 --- a/src/v1/stage0/src/cli_run/entry_resolve.rs +++ b/src/v1/stage0/src/cli_run/entry_resolve.rs @@ -9,7 +9,7 @@ #![allow( clippy::disallowed_macros, // 10 clippy::doc_lazy_continuation, // 2 - clippy::items_after_test_module, // 1 + clippy::items_after_test_module, // 2 clippy::redundant_closure, // 1 clippy::type_complexity, // 8 dead_code, // 9 @@ -251,6 +251,22 @@ pub(crate) fn import_closure_dag_files( .collect()) } +/// Compile-subject closure of caller-supplied seed sources over an explicit pool. +/// +/// `compiler_tests` `resolve_source_closure` used to BFS `import` lines from the seed +/// pairs. Same class as #13437 / #13464: a provider reached only by reference was omitted. +/// This is not a second walker — it calls `extend_sources_to_both_closure_fixpoint`. +/// +/// SEED DELTA: production `resolve_source_closure` lost its import-line BFS. Net production +/// seed is this wrapper. `seeded_compile_closure_controls` is `#[cfg(test)]` only. +pub fn resolve_seeded_compile_closure( + seeds: Vec>, + pool_roots: &[String], +) -> Result>, String> { + let mei = try_index_for_run_or_owned_pool(pool_roots)?; + extend_sources_to_both_closure_fixpoint(seeds, &mei) +} + #[cfg(test)] mod import_closure_dag_files_controls { use super::*; @@ -402,6 +418,150 @@ fn broken() -> Int { no_such_function_anywhere() }\n"; } } +#[cfg(test)] +mod seeded_compile_closure_controls { + use super::*; + + const ENTRY: &str = "entry.dag"; + const MID: &str = "mid.dag"; + const PROVIDER: &str = "provider.dag"; + const BROKEN: &str = "broken.dag"; + + const ENTRY_SRC: &str = "module compiler.tests.closure.entry\n\ +import compiler.tests.closure.mid { mid_ok }\n\ +fn use_mid() -> Int { mid_ok() }\n"; + + const MID_SRC: &str = "module compiler.tests.closure.mid\n\ +fn mid_ok() -> Int { 1 }\n\ +fn uses_provider() -> compiler.tests.closure.provider.ProviderToken {\n\ + compiler.tests.closure.provider.ProviderToken { n: 1 }\n\ +}\n"; + + const PROVIDER_SRC: &str = "module compiler.tests.closure.provider\n\ +type ProviderToken {\n\ + n: Int\n\ +}\n"; + + const BROKEN_SRC: &str = "module compiler.tests.closure.broken\n\ +import compiler.tests.closure.mid { mid_ok }\n\ +fn broken() -> Int { no_such_function_anywhere() }\n"; + + fn fixture_tree() -> PathBuf { + let dir = process_workspace_root().join("target").join(format!( + "seeded_compile_closure_{}_{}", + std::process::id(), + std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .map(|d| d.as_nanos()) + .unwrap_or(0) + )); + std::fs::create_dir_all(&dir).unwrap(); + std::fs::write(dir.join(ENTRY), ENTRY_SRC).unwrap(); + std::fs::write(dir.join(MID), MID_SRC).unwrap(); + std::fs::write(dir.join(PROVIDER), PROVIDER_SRC).unwrap(); + std::fs::write(dir.join(BROKEN), BROKEN_SRC).unwrap(); + dir + } + + fn provider_in(sources: &[Rc]) -> bool { + sources.iter().any(|s| { + s.content.contains("module compiler.tests.closure.provider") + || Path::new(&s.path) + .file_name() + .is_some_and(|n| n == PROVIDER) + }) + } + + fn seed(path: &str, content: &str) -> Rc { + Rc::new(v1_compiler_compile::SourceFile { + path: path.to_string(), + content: content.to_string(), + }) + } + + /// Import-line BFS from the seed pairs only. THE MUTANT. + fn import_only_seeded_closure( + source_roots: &[PathBuf], + entry_pairs: Vec<(String, String)>, + ) -> Result>, String> { + let index = dag_module_index(source_roots)?; + let mut seen: std::collections::HashMap> = + std::collections::HashMap::new(); + let mut queue: Vec = Vec::new(); + for (path, content) in entry_pairs { + seen.insert(path.clone(), seed(&path, &content)); + queue.push(content); + } + while let Some(content) = queue.pop() { + for module_path in extract_import_paths(&content) { + let Some(candidates) = index.get(&module_path) else { + continue; + }; + for path in candidates { + let rel = normalize_repo_path(&module_index_path_key(path)); + if seen.contains_key(&rel) { + continue; + } + let file_content = std::fs::read_to_string(path) + .map_err(|e| format!("read imported module {}: {e}", path.display()))?; + seen.insert(rel.clone(), seed(&rel, &file_content)); + queue.push(file_content); + } + } + } + Ok(seen.into_iter().map(|(_, v)| v).collect()) + } + + #[test] + fn a_provider_reached_only_by_reference_is_closed() { + let dir = fixture_tree(); + let roots = [dir.to_string_lossy().into_owned()]; + let closed = + resolve_seeded_compile_closure(vec![seed(ENTRY, ENTRY_SRC)], &roots).expect("closure"); + assert!( + provider_in(&closed), + "provider missing from closed set {:?}", + closed.iter().map(|s| s.path.clone()).collect::>() + ); + let _ = std::fs::remove_dir_all(&dir); + } + + #[test] + fn import_only_mutant_omits_the_reference_only_provider() { + let dir = fixture_tree(); + let mutant = + import_only_seeded_closure(&[dir.clone()], vec![(ENTRY.into(), ENTRY_SRC.into())]) + .expect("mutant"); + assert!( + !provider_in(&mutant), + "the import-only mutant must omit the provider; got {:?}", + mutant.iter().map(|s| s.path.clone()).collect::>() + ); + let _ = std::fs::remove_dir_all(&dir); + } + + #[test] + fn a_real_error_in_the_entry_closure_still_refuses() { + let dir = fixture_tree(); + let roots = [dir.to_string_lossy().into_owned()]; + let closed = resolve_seeded_compile_closure(vec![seed(BROKEN, BROKEN_SRC)], &roots) + .expect("closure"); + assert!( + provider_in(&closed), + "the broken entry must still close the reference-only provider" + ); + let refusal = resolved_graph_from_sources(closed) + .expect_err("a call to a function nothing declares must refuse"); + assert!( + refusal.contains("no_such_function_anywhere") + || refusal.contains("not found") + || refusal.contains("blocking_diagnostics"), + "refusal must name the real error, got: {refusal}" + ); + let _ = std::fs::remove_dir_all(&dir); + } +} + /// Normalize `source_roots` to the workspace-relative form `import_resolution_facts` / /// `module_declaration_facts` expect when invoked from `.dag` (`witness_layer_roots` style). pub(crate) fn pool_roots_for_module_graph_closure(source_roots: &[String]) -> Vec { diff --git a/src/v1/stage0/src/compiler_tests.rs b/src/v1/stage0/src/compiler_tests.rs index 234e3dac8a2..1df3cbb82c1 100644 --- a/src/v1/stage0/src/compiler_tests.rs +++ b/src/v1/stage0/src/compiler_tests.rs @@ -78,105 +78,19 @@ mod compiler_tests { .collect() } - fn parse_module_or_panic(path: &str, content: &str) -> std::rc::Rc { - let tokens = tokenize( - content.to_string(), - path.to_string(), - crate::extdeps_languages_dag_syntax::dag_parse_environment(), - ); - let mut source_indices = HashMap::new(); - source_indices.insert( - path.to_string(), - crate::v1_std_core::build_newline_index(path.to_string(), content.to_string()), - ); - let parsed = crate::v1_compiler_parse::parse_with_table( - tokens.clone(), - std::rc::Rc::new(source_indices), - crate::v1_std_core::empty_intern_table(), - ); - if let Some(err) = parsed.result.error.as_ref() { - panic!( - "failed to parse {} while building source closure: {}", - path, - crate::v1_std_core::diagnostic_to_message(err.diagnostic.clone()) - ); - } - parsed - .result - .module - .clone() - .unwrap_or_else(|| panic!("{} produced no module while building source closure", path)) - } - - fn module_path_from_source(path: &str, content: &str) -> String { - parse_module_or_panic(path, content).name.clone() - } - - fn import_paths_from_source(path: &str, content: &str) -> Vec { - let module = parse_module_or_panic(path, content); - crate::v1_std_core::module_imports(module) - .iter() - .map(|imp| imp.name.clone()) - .collect() - } - - fn build_source_index(roots: &[&str]) -> HashMap { - let mut index = HashMap::new(); - for root in roots { - for (path, content) in discover_dag_files(root) { - let module_path = module_path_from_source(&path, &content); - if let Some((existing, _)) = index.get(&module_path) { - panic!( - "duplicate module path '{}': declared in both {} and {}", - module_path, existing, path - ); - } - index.insert(module_path, (path, content)); - } - } - index - } - fn resolve_source_closure( entry_pairs: Vec<(String, String)>, roots: &[&str], ) -> Vec> { - let index = build_source_index(roots); - let mut seen = - HashMap::>::new(); - let mut queue = Vec::new(); - - for (path, content) in entry_pairs { - let module_path = module_path_from_source(&path, &content); - seen.insert( - module_path, - std::rc::Rc::new(crate::v1_compiler_compile::SourceFile { - path: path.clone(), - content: content.clone(), - }), - ); - queue.push((path, content)); - } - - while let Some((_path, content)) = queue.pop() { - for module_path in import_paths_from_source(&_path, &content) { - if seen.contains_key(&module_path) { - continue; - } - if let Some((path, file_content)) = index.get(&module_path).cloned() { - seen.insert( - module_path, - std::rc::Rc::new(crate::v1_compiler_compile::SourceFile { - path: path.clone(), - content: file_content.clone(), - }), - ); - queue.push((path, file_content)); - } - } - } - - let mut result: Vec<_> = seen.into_iter().map(|(_, v)| v).collect(); + let seeds = entry_pairs + .into_iter() + .map(|(path, content)| { + std::rc::Rc::new(crate::v1_compiler_compile::SourceFile { path, content }) + }) + .collect(); + let pool: Vec = roots.iter().map(|r| (*r).to_string()).collect(); + let mut result = crate::cli_run::resolve_seeded_compile_closure(seeds, &pool) + .unwrap_or_else(|e| panic!("resolve_source_closure: {e}")); result.sort_by(|a, b| a.path.cmp(&b.path)); result } diff --git a/src/v1/stage0/src/v1_compiler_compiler_tests_rust.rs b/src/v1/stage0/src/v1_compiler_compiler_tests_rust.rs index 4396c6b6dcf..f60ae6c84c6 100644 --- a/src/v1/stage0/src/v1_compiler_compiler_tests_rust.rs +++ b/src/v1/stage0/src/v1_compiler_compiler_tests_rust.rs @@ -37,7 +37,7 @@ pub fn ct_file_discovery_helpers() -> String { } pub fn ct_source_builders() -> String { - v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(" /// Build SourceFile vec from discovered .dag files.\n".to_string(), " fn source_files_from(\n".to_string()), " pairs: &[(String, String)],\n".to_string()), " ) -> Vec> {\n".to_string()), " pairs\n".to_string()), " .iter()\n".to_string()), " .map(|(path, content)| {\n".to_string()), " std::rc::Rc::new(crate::v1_compiler_compile::SourceFile {\n".to_string()), " path: path.clone(),\n".to_string()), " content: content.clone(),\n".to_string()), " })\n".to_string()), " })\n".to_string()), " .collect()\n".to_string()), " }\n\n".to_string()), " fn parse_module_or_panic(path: &str, content: &str) -> std::rc::Rc {\n".to_string()), " let tokens = tokenize(content.to_string(), path.to_string(), crate::extdeps_languages_dag_syntax::dag_parse_environment());\n".to_string()), " let mut source_indices = HashMap::new();\n".to_string()), " source_indices.insert(\n".to_string()), " path.to_string(),\n".to_string()), " crate::v1_std_core::build_newline_index(path.to_string(), content.to_string()),\n".to_string()), " );\n".to_string()), " let parsed = crate::v1_compiler_parse::parse_with_table(\n".to_string()), " tokens.clone(),\n".to_string()), " std::rc::Rc::new(source_indices),\n".to_string()), " crate::v1_std_core::empty_intern_table(),\n".to_string()), " );\n".to_string()), " if let Some(err) = parsed.result.error.as_ref() {\n".to_string()), " panic!(\n".to_string()), " \"failed to parse {} while building source closure: {}\",\n".to_string()), " path,\n".to_string()), " crate::v1_std_core::diagnostic_to_message(err.diagnostic.clone())\n".to_string()), " );\n".to_string()), " }\n".to_string()), " parsed.result.module.clone().unwrap_or_else(|| {\n".to_string()), " panic!(\"{} produced no module while building source closure\", path)\n".to_string()), " })\n".to_string()), " }\n\n".to_string()), " fn module_path_from_source(path: &str, content: &str) -> String {\n".to_string()), " parse_module_or_panic(path, content).name.clone()\n".to_string()), " }\n\n".to_string()), " fn import_paths_from_source(path: &str, content: &str) -> Vec {\n".to_string()), " let module = parse_module_or_panic(path, content);\n".to_string()), " crate::v1_std_core::module_imports(module)\n".to_string()), " .iter()\n".to_string()), " .map(|imp| imp.name.clone())\n".to_string()), " .collect()\n".to_string()), " }\n\n".to_string()), " fn build_source_index(\n".to_string()), " roots: &[&str],\n".to_string()), " ) -> HashMap {\n".to_string()), " let mut index = HashMap::new();\n".to_string()), " for root in roots {\n".to_string()), " for (path, content) in discover_dag_files(root) {\n".to_string()), " let module_path = module_path_from_source(&path, &content);\n".to_string()), " if let Some((existing, _)) = index.get(&module_path) {\n".to_string()), " panic!(\n".to_string()), " \"duplicate module path '{}': declared in both {} and {}\",\n".to_string()), " module_path,\n".to_string()), " existing,\n".to_string()), " path\n".to_string()), " );\n".to_string()), " }\n".to_string()), " index.insert(module_path, (path, content));\n".to_string()), " }\n".to_string()), " }\n".to_string()), " index\n".to_string()), " }\n\n".to_string()), " fn resolve_source_closure(\n".to_string()), " entry_pairs: Vec<(String, String)>,\n".to_string()), " roots: &[&str],\n".to_string()), " ) -> Vec> {\n".to_string()), " let index = build_source_index(roots);\n".to_string()), " let mut seen = HashMap::>::new();\n".to_string()), " let mut queue = Vec::new();\n\n".to_string()), " for (path, content) in entry_pairs {\n".to_string()), " let module_path = module_path_from_source(&path, &content);\n".to_string()), " seen.insert(\n".to_string()), " module_path,\n".to_string()), " std::rc::Rc::new(crate::v1_compiler_compile::SourceFile {\n".to_string()), " path: path.clone(),\n".to_string()), " content: content.clone(),\n".to_string()), " }),\n".to_string()), " );\n".to_string()), " queue.push((path, content));\n".to_string()), " }\n\n".to_string()), " while let Some((_path, content)) = queue.pop() {\n".to_string()), " for module_path in import_paths_from_source(&_path, &content) {\n".to_string()), " if seen.contains_key(&module_path) {\n".to_string()), " continue;\n".to_string()), " }\n".to_string()), " if let Some((path, file_content)) = index.get(&module_path).cloned() {\n".to_string()), " seen.insert(\n".to_string()), " module_path,\n".to_string()), " std::rc::Rc::new(crate::v1_compiler_compile::SourceFile {\n".to_string()), " path: path.clone(),\n".to_string()), " content: file_content.clone(),\n".to_string()), " }),\n".to_string()), " );\n".to_string()), " queue.push((path, file_content));\n".to_string()), " }\n".to_string()), " }\n".to_string()), " }\n\n".to_string()), " let mut result: Vec<_> = seen.into_iter().map(|(_, v)| v).collect();\n".to_string()), " result.sort_by(|a, b| a.path.cmp(&b.path));\n".to_string()), " result\n".to_string()), " }\n\n".to_string()), ct_self_compile_sources()) + v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(" /// Build SourceFile vec from discovered .dag files.\n".to_string(), " fn source_files_from(\n".to_string()), " pairs: &[(String, String)],\n".to_string()), " ) -> Vec> {\n".to_string()), " pairs\n".to_string()), " .iter()\n".to_string()), " .map(|(path, content)| {\n".to_string()), " std::rc::Rc::new(crate::v1_compiler_compile::SourceFile {\n".to_string()), " path: path.clone(),\n".to_string()), " content: content.clone(),\n".to_string()), " })\n".to_string()), " })\n".to_string()), " .collect()\n".to_string()), " }\n\n".to_string()), " fn resolve_source_closure(\n".to_string()), " entry_pairs: Vec<(String, String)>,\n".to_string()), " roots: &[&str],\n".to_string()), " ) -> Vec> {\n".to_string()), " let seeds = entry_pairs\n".to_string()), " .into_iter()\n".to_string()), " .map(|(path, content)| {\n".to_string()), " std::rc::Rc::new(crate::v1_compiler_compile::SourceFile { path, content })\n".to_string()), " })\n".to_string()), " .collect();\n".to_string()), " let pool: Vec = roots.iter().map(|r| (*r).to_string()).collect();\n".to_string()), " let mut result = crate::cli_run::resolve_seeded_compile_closure(seeds, &pool)\n".to_string()), " .unwrap_or_else(|e| panic!(\"resolve_source_closure: {e}\"));\n".to_string()), " result.sort_by(|a, b| a.path.cmp(&b.path));\n".to_string()), " result\n".to_string()), " }\n\n".to_string()), ct_self_compile_sources()) } pub fn ct_self_compile_sources() -> String { From ea1a22c3c198a099dff9b7d9ebb13cdbd2a964f8 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Tue, 6 Oct 2026 23:01:43 +0000 Subject: [PATCH 2/5] clippy: into_values and from_ref on seeded_compile_closure_controls. Co-authored-by: Cursor --- src/v1/stage0/src/cli_run/entry_resolve.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/v1/stage0/src/cli_run/entry_resolve.rs b/src/v1/stage0/src/cli_run/entry_resolve.rs index 13bba8dad7c..68ca70b0f80 100644 --- a/src/v1/stage0/src/cli_run/entry_resolve.rs +++ b/src/v1/stage0/src/cli_run/entry_resolve.rs @@ -509,7 +509,7 @@ fn broken() -> Int { no_such_function_anywhere() }\n"; } } } - Ok(seen.into_iter().map(|(_, v)| v).collect()) + Ok(seen.into_values().collect()) } #[test] @@ -529,9 +529,11 @@ fn broken() -> Int { no_such_function_anywhere() }\n"; #[test] fn import_only_mutant_omits_the_reference_only_provider() { let dir = fixture_tree(); - let mutant = - import_only_seeded_closure(&[dir.clone()], vec![(ENTRY.into(), ENTRY_SRC.into())]) - .expect("mutant"); + let mutant = import_only_seeded_closure( + std::slice::from_ref(&dir), + vec![(ENTRY.into(), ENTRY_SRC.into())], + ) + .expect("mutant"); assert!( !provider_in(&mutant), "the import-only mutant must omit the provider; got {:?}", From 5eb50a1010a3d9e8ad695df5b98d02b2fae4fc27 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Tue, 6 Oct 2026 23:35:11 +0000 Subject: [PATCH 3/5] emit_resolve_transitively_fn: stamp the compile-closure authority into generated main. The emitted import-line BFS omitted reference-only providers. Generated resolve_transitively_with_seen now calls resolve_seeded_compile_closure. RED: production text names that authority and not extract_import_paths(&content). Co-authored-by: Cursor --- src/v1/05_emit_rust.dag | 47 ++++++++-------------- src/v1/stage0/src/cli_run/entry_resolve.rs | 43 ++++++++++++++++++++ src/v1/stage0/src/v1_compiler_emit_rust.rs | 2 +- 3 files changed, 60 insertions(+), 32 deletions(-) diff --git a/src/v1/05_emit_rust.dag b/src/v1/05_emit_rust.dag index 6003e8804ac..6fb36b6c789 100644 --- a/src/v1/05_emit_rust.dag +++ b/src/v1/05_emit_rust.dag @@ -20334,41 +20334,26 @@ fn emit_dep_pool_type_and_fns() -> String { fn emit_resolve_transitively_fn(crate_name: String, pipeline_mod: String) -> String { concat( - "/// Resolve imports transitively from entry modules.\n", - "/// Fail-closed: panics on unreadable imported files.\n", + "/// Compile-subject closure of entry modules (import, qualified, and bare references).\n", + "/// Fail-closed: panics if the closure authority refuses.\n", "/// Returns sources sorted by path for deterministic fixed-point convergence.\n", "fn resolve_transitively_with_seen(\n", " entry_sources: Vec<(String, String)>,\n", - " index: &HashMap,\n", - " mut seen: HashMap>,\n", + " _index: &HashMap,\n", + " seen: HashMap>,\n", + " source_roots: &[String],\n", ") -> Vec> {\n", - " let mut queue: Vec<(String, String)> = entry_sources;\n", - "\n", - " while let Some((_path, content)) = queue.pop() {\n", - " let imports = extract_import_paths(&content);\n", - " for module_path in imports {\n", - " if seen.contains_key(&module_path) {\n", - " continue;\n", - " }\n", - " if let Some(file_path) = index.get(&module_path) {\n", - " let file_content = std::fs::read_to_string(file_path)\n", - " .unwrap_or_else(|e| panic!(\"failed to read imported module '{}' at {:?}: {}\",\n", - " module_path, file_path, e));\n", - " let rel_path = file_path.to_string_lossy().to_string();\n", - " let source = Rc::new(", crate_name, "::", pipeline_mod, "::SourceFile {\n", - " path: rel_path.clone(),\n", - " content: file_content.clone(),\n", - " });\n", - " seen.insert(module_path, source);\n", - " queue.push((rel_path, file_content));\n", - " }\n", - " // If not found in index, the compiler's resolve stage will report the error.\n", - " }\n", - " }\n", - "\n", - " let mut result: Vec<_> = seen.into_iter().map(|(_, v)| v).collect();\n", - " result.sort_by(|a, b| a.path.cmp(&b.path));\n", - " result\n", + " let mut seeds: Vec> = entry_sources\n", + " .into_iter()\n", + " .map(|(path, content)| {\n", + " Rc::new(", crate_name, "::", pipeline_mod, "::SourceFile { path, content })\n", + " })\n", + " .collect();\n", + " seeds.extend(seen.into_iter().map(|(_, v)| v));\n", + " let mut closed = ", crate_name, "::cli_run::resolve_seeded_compile_closure(seeds, source_roots)\n", + " .unwrap_or_else(|e| panic!(\"compile-subject closure: {e}\"));\n", + " closed.sort_by(|a, b| a.path.cmp(&b.path));\n", + " closed\n", "}\n\n") } diff --git a/src/v1/stage0/src/cli_run/entry_resolve.rs b/src/v1/stage0/src/cli_run/entry_resolve.rs index 68ca70b0f80..ceb69101505 100644 --- a/src/v1/stage0/src/cli_run/entry_resolve.rs +++ b/src/v1/stage0/src/cli_run/entry_resolve.rs @@ -4334,3 +4334,46 @@ mod strict_refusal_counts_blocking_diagnostics { result.expect("one_blocker_is_counted_and_named_and_the_advisory_is_not panicked"); } } + +#[cfg(test)] +mod emit_resolve_transitively_fn_controls { + /// Import-line BFS the emitter used to stamp into generated main. THE MUTANT. + fn import_only_emitted_resolve() -> &'static str { + " let imports = extract_import_paths(&content);\n" + } + + #[test] + fn emitted_text_calls_the_closure_authority() { + let text = crate::v1_compiler_emit_rust::emit_resolve_transitively_fn( + "v1_compiler".to_string(), + "v1_compiler_compile".to_string(), + ); + assert!( + text.contains("resolve_seeded_compile_closure"), + "emitter must stamp the compile-closure authority, got: {text}" + ); + assert!( + !text.contains("extract_import_paths(&content)"), + "emitter must not stamp the import-only walk, got: {text}" + ); + } + + #[test] + fn import_only_mutant_omits_the_authority() { + let mutant = import_only_emitted_resolve(); + assert!(mutant.contains("extract_import_paths(&content)")); + assert!(!mutant.contains("resolve_seeded_compile_closure")); + } + + #[test] + fn emitted_wrapper_still_names_the_legacy_entry() { + let text = crate::v1_compiler_emit_rust::emit_resolve_transitively_fn( + "v1_compiler".to_string(), + "v1_compiler_compile".to_string(), + ); + assert!( + text.contains("fn resolve_transitively_with_seen("), + "generated main still looks up this name" + ); + } +} diff --git a/src/v1/stage0/src/v1_compiler_emit_rust.rs b/src/v1/stage0/src/v1_compiler_emit_rust.rs index f2e9fe6fcbf..e992c4650c9 100644 --- a/src/v1/stage0/src/v1_compiler_emit_rust.rs +++ b/src/v1/stage0/src/v1_compiler_emit_rust.rs @@ -42221,7 +42221,7 @@ pub fn emit_dep_pool_type_and_fns() -> String { } pub fn emit_resolve_transitively_fn(crate_name: String, pipeline_mod: String) -> String { - v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat("/// Resolve imports transitively from entry modules.\n".to_string(), "/// Fail-closed: panics on unreadable imported files.\n".to_string()), "/// Returns sources sorted by path for deterministic fixed-point convergence.\n".to_string()), "fn resolve_transitively_with_seen(\n".to_string()), " entry_sources: Vec<(String, String)>,\n".to_string()), " index: &HashMap,\n".to_string()), " mut seen: HashMap>,\n".to_string()), ") -> Vec> {\n".to_string()), " let mut queue: Vec<(String, String)> = entry_sources;\n".to_string()), "\n".to_string()), " while let Some((_path, content)) = queue.pop() {\n".to_string()), " let imports = extract_import_paths(&content);\n".to_string()), " for module_path in imports {\n".to_string()), " if seen.contains_key(&module_path) {\n".to_string()), " continue;\n".to_string()), " }\n".to_string()), " if let Some(file_path) = index.get(&module_path) {\n".to_string()), " let file_content = std::fs::read_to_string(file_path)\n".to_string()), " .unwrap_or_else(|e| panic!(\"failed to read imported module '{}' at {:?}: {}\",\n".to_string()), " module_path, file_path, e));\n".to_string()), " let rel_path = file_path.to_string_lossy().to_string();\n".to_string()), " let source = Rc::new(".to_string()), crate_name.clone()), "::".to_string()), pipeline_mod.clone()), "::SourceFile {\n".to_string()), " path: rel_path.clone(),\n".to_string()), " content: file_content.clone(),\n".to_string()), " });\n".to_string()), " seen.insert(module_path, source);\n".to_string()), " queue.push((rel_path, file_content));\n".to_string()), " }\n".to_string()), " // If not found in index, the compiler's resolve stage will report the error.\n".to_string()), " }\n".to_string()), " }\n".to_string()), "\n".to_string()), " let mut result: Vec<_> = seen.into_iter().map(|(_, v)| v).collect();\n".to_string()), " result.sort_by(|a, b| a.path.cmp(&b.path));\n".to_string()), " result\n".to_string()), "}\n\n".to_string()) + v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat("/// Compile-subject closure of entry modules (import, qualified, and bare references).\n".to_string(), "/// Fail-closed: panics if the closure authority refuses.\n".to_string()), "/// Returns sources sorted by path for deterministic fixed-point convergence.\n".to_string()), "fn resolve_transitively_with_seen(\n".to_string()), " entry_sources: Vec<(String, String)>,\n".to_string()), " _index: &HashMap,\n".to_string()), " seen: HashMap>,\n".to_string()), " source_roots: &[String],\n".to_string()), ") -> Vec> {\n".to_string()), " let mut seeds: Vec> = entry_sources\n".to_string()), " .into_iter()\n".to_string()), " .map(|(path, content)| {\n".to_string()), " Rc::new(".to_string()), crate_name.clone()), "::".to_string()), pipeline_mod.clone()), "::SourceFile { path, content })\n".to_string()), " })\n".to_string()), " .collect();\n".to_string()), " seeds.extend(seen.into_iter().map(|(_, v)| v));\n".to_string()), " let mut closed = ".to_string()), crate_name.clone()), "::cli_run::resolve_seeded_compile_closure(seeds, source_roots)\n".to_string()), " .unwrap_or_else(|e| panic!(\"compile-subject closure: {e}\"));\n".to_string()), " closed.sort_by(|a, b| a.path.cmp(&b.path));\n".to_string()), " closed\n".to_string()), "}\n\n".to_string()) } pub fn emit_main_diagnostic_fns(crate_name: String) -> String { From 9b25ca28f73b90c37a304a989586094509e6bb45 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Wed, 7 Oct 2026 00:51:31 +0000 Subject: [PATCH 4/5] Delete dead emit_resolve_transitively_fn stamp rather than rewrite it. Generated main already compiles through cli_run::compile_emission; the stamped walker and extract_import_paths had no callers. The string-literal mutant over that stamp was permanently green. Co-authored-by: Cursor --- .../namespace_cut_subject_roster.dag | 4 +- docs/plans/namespace-cut-replacement-plan.md | 2 +- src/v1/05_emit_rust.dag | 46 ------------------- src/v1/stage0/src/cli_run/entry_resolve.rs | 43 ----------------- src/v1/stage0/src/v1_compiler_emit_rust.rs | 18 +------- 5 files changed, 4 insertions(+), 109 deletions(-) diff --git a/dag/gunbc/namespace/namespace_cut_subject_roster.dag b/dag/gunbc/namespace/namespace_cut_subject_roster.dag index 1afccb3ae33..cd7418bb7ee 100644 --- a/dag/gunbc/namespace/namespace_cut_subject_roster.dag +++ b/dag/gunbc/namespace/namespace_cut_subject_roster.dag @@ -148,9 +148,7 @@ fn subject_probes(s: NamespaceCutSubjectIdentity) -> NamespaceCutSubjectProbes { } ImportDerivedUseLineInputs => NamespaceCutSubjectProbes { first: DagDeclarationProbe { module_path: "v1.compiler.emit_rust", decl_name: "emit_imports" }, - rest: [ - DagDeclarationProbe { module_path: "v1.compiler.emit_rust", decl_name: "emit_extract_import_paths_fn" }, - ], + rest: [], } LegacyImportRepairApparatus => NamespaceCutSubjectProbes { first: HostRustItemProbe { crate_relative_file: "src/v1/stage0/src/cli_run.rs", item_name: "classify_unlisted_import_binding_source" }, diff --git a/docs/plans/namespace-cut-replacement-plan.md b/docs/plans/namespace-cut-replacement-plan.md index cbd3d95bd22..d28f4ffe986 100644 --- a/docs/plans/namespace-cut-replacement-plan.md +++ b/docs/plans/namespace-cut-replacement-plan.md @@ -112,7 +112,7 @@ Standing census on the branch (2026-08-15, dated record): **5,531 hard diagnosti ## Step 2 — host and emit machinery - `src/v1/stage0/src/cli_run.rs` (**frozen file — surgical trims, never deletion**): `extract_import_paths` (the starts-with-import text scanner every host import fact builds on), `resolve_virtual_source_with_imports`, `source_declares_import_lines`, `import_module_paths_for_typed_module`, `emit_import_admission_list`; the closure-walk family (`build_import_adjacency`, `import_closure_from_adjacency`/`_from_facts`/`_live_paths`/`_live_paths_with_facts`, `reference_only_direct_import_paths`, `entry_file_touched_via_import_closure`, `import_closure_module_reaches_carrier_home`, `collect_import_closure_module_names_from_facts`, `roster_import_closure_nodes_pre_resolve`, `import_closure_files_from_graph`, `touched_file_in_import_closure`, `import_closure_repo_paths_for_entry`, `augment_closure_modules_from_import_facts`, `import_closure_dag_files`); the `declared_import_closure_*` compile family incl. `compile_entry_on_declared_import_closure_only` and `observe_declared_import_closure_symbol_binding`; the unlisted-import census family (`classify_unlisted_import_binding_source`, `compile_clean_unlisted_import_census`, `compile_clean_unlisted_import_use_blocks_from_policy`/`_cached`); the class-b import-closure gates -- `src/v1/05_emit_rust.dag` (+ twin `v1_compiler_emit_rust.rs`): `emit_imports`, `emit_extract_import_paths_fn` — **this one prints the import scanner into generated stage0; cut it in the same pass or the seed regrows it on regen** — `augment_scoped_data_item_index_with_imports`, `module_imports_std_serialization_coproduct_wire_contract`, `record_lit_resolved_ctor_import_names`, `emit_specific_import_block`, `explicit_import_source_module_for_name`, `import_variant_parent_for_name`, `wildcard_import_pool_surface_names`, `import_module_enum_scope` +- `src/v1/05_emit_rust.dag` (+ twin `v1_compiler_emit_rust.rs`): `emit_imports` — `emit_extract_import_paths_fn` was deleted (generated main already routes compile through `cli_run::compile_emission`; the stamp had no caller) — `augment_scoped_data_item_index_with_imports`, `module_imports_std_serialization_coproduct_wire_contract`, `record_lit_resolved_ctor_import_names`, `emit_specific_import_block`, `explicit_import_source_module_for_name`, `import_variant_parent_for_name`, `wildcard_import_pool_surface_names`, `import_module_enum_scope` - Whole-file deletions: `dag/gunbc/instruments/namespace_import_closure_behavioral_transport.dag` (its own dissolution trigger names this cut) · `dag/gunbc/class_b_import_closure_overlay.dag` · `dag/gunbc/declared_import_closure_binding.dag` · `dag/gunbc/instruments/dag_compile_clean_cli_floor_agreement.dag` · `dag/std/import.dag` (empty stub) · `src/v2/test/claim/long/dag_import_block_lexeme_stamp_test.dag` · `dag/test/claim/module_graph_edge_source_witness_test.dag` - Partial deletions: `dag/gunbc/compile_clean_diagnostic_policy.dag` (only the `UnlistedImportUse` enforcement declarations + dissolve rows) · `dag/gunbc/instruments/diagnostics_witness_transport.dag` (`diagnostics_import_resolution_suite`, `run_diagnostics_import_resolution_witness`) diff --git a/src/v1/05_emit_rust.dag b/src/v1/05_emit_rust.dag index 6fb36b6c789..521b2c9abc7 100644 --- a/src/v1/05_emit_rust.dag +++ b/src/v1/05_emit_rust.dag @@ -20117,9 +20117,7 @@ fn emit_main_pipeline_fns(crate_name: String, pipeline_module: String) -> String concat( emit_collect_dag_files_fn(), emit_extract_module_path_fn(), - emit_extract_import_paths_fn(), emit_dep_pool_type_and_fns(), - emit_resolve_transitively_fn(crate_name: crate_name, pipeline_mod: pipeline_mod), emit_compile_target_helpers(crate_name: crate_name, pipeline_mod: pipeline_mod, artifact_mod: artifact_mod)) } @@ -20241,25 +20239,6 @@ fn emit_extract_module_path_fn() -> String { "}\n\n") } -fn emit_extract_import_paths_fn() -> String { - concat( - "/// Extract import module paths from a .dag file's import declarations.\n", - "fn extract_import_paths(content: &str) -> Vec {\n", - " let mut imports = Vec::new();\n", - " for line in content.lines() {\n", - " let trimmed = line.trim();\n", - " if trimmed.starts_with(\"import \") {\n", - " let rest = trimmed[\"import \".len()..].trim();\n", - " let module_path = rest.split('{').next().unwrap_or(rest).trim();\n", - " if !module_path.is_empty() {\n", - " imports.push(module_path.to_string());\n", - " }\n", - " }\n", - " }\n", - " imports\n", - "}\n\n") -} - fn emit_dep_pool_type_and_fns() -> String { concat( "#[derive(Clone, Copy, PartialEq, Eq)]\nenum DependencyPoolIndex {\n Strict,\n PrimaryPrecedence,\n}\n\n", @@ -20332,31 +20311,6 @@ fn emit_dep_pool_type_and_fns() -> String { "}\n\n") } -fn emit_resolve_transitively_fn(crate_name: String, pipeline_mod: String) -> String { - concat( - "/// Compile-subject closure of entry modules (import, qualified, and bare references).\n", - "/// Fail-closed: panics if the closure authority refuses.\n", - "/// Returns sources sorted by path for deterministic fixed-point convergence.\n", - "fn resolve_transitively_with_seen(\n", - " entry_sources: Vec<(String, String)>,\n", - " _index: &HashMap,\n", - " seen: HashMap>,\n", - " source_roots: &[String],\n", - ") -> Vec> {\n", - " let mut seeds: Vec> = entry_sources\n", - " .into_iter()\n", - " .map(|(path, content)| {\n", - " Rc::new(", crate_name, "::", pipeline_mod, "::SourceFile { path, content })\n", - " })\n", - " .collect();\n", - " seeds.extend(seen.into_iter().map(|(_, v)| v));\n", - " let mut closed = ", crate_name, "::cli_run::resolve_seeded_compile_closure(seeds, source_roots)\n", - " .unwrap_or_else(|e| panic!(\"compile-subject closure: {e}\"));\n", - " closed.sort_by(|a, b| a.path.cmp(&b.path));\n", - " closed\n", - "}\n\n") -} - fn emit_main_diagnostic_fns(crate_name: String) -> String { let core_mod = module_to_filename(name: "v1.std.core") concat( diff --git a/src/v1/stage0/src/cli_run/entry_resolve.rs b/src/v1/stage0/src/cli_run/entry_resolve.rs index ceb69101505..68ca70b0f80 100644 --- a/src/v1/stage0/src/cli_run/entry_resolve.rs +++ b/src/v1/stage0/src/cli_run/entry_resolve.rs @@ -4334,46 +4334,3 @@ mod strict_refusal_counts_blocking_diagnostics { result.expect("one_blocker_is_counted_and_named_and_the_advisory_is_not panicked"); } } - -#[cfg(test)] -mod emit_resolve_transitively_fn_controls { - /// Import-line BFS the emitter used to stamp into generated main. THE MUTANT. - fn import_only_emitted_resolve() -> &'static str { - " let imports = extract_import_paths(&content);\n" - } - - #[test] - fn emitted_text_calls_the_closure_authority() { - let text = crate::v1_compiler_emit_rust::emit_resolve_transitively_fn( - "v1_compiler".to_string(), - "v1_compiler_compile".to_string(), - ); - assert!( - text.contains("resolve_seeded_compile_closure"), - "emitter must stamp the compile-closure authority, got: {text}" - ); - assert!( - !text.contains("extract_import_paths(&content)"), - "emitter must not stamp the import-only walk, got: {text}" - ); - } - - #[test] - fn import_only_mutant_omits_the_authority() { - let mutant = import_only_emitted_resolve(); - assert!(mutant.contains("extract_import_paths(&content)")); - assert!(!mutant.contains("resolve_seeded_compile_closure")); - } - - #[test] - fn emitted_wrapper_still_names_the_legacy_entry() { - let text = crate::v1_compiler_emit_rust::emit_resolve_transitively_fn( - "v1_compiler".to_string(), - "v1_compiler_compile".to_string(), - ); - assert!( - text.contains("fn resolve_transitively_with_seen("), - "generated main still looks up this name" - ); - } -} diff --git a/src/v1/stage0/src/v1_compiler_emit_rust.rs b/src/v1/stage0/src/v1_compiler_emit_rust.rs index e992c4650c9..f0a7354149b 100644 --- a/src/v1/stage0/src/v1_compiler_emit_rust.rs +++ b/src/v1/stage0/src/v1_compiler_emit_rust.rs @@ -42178,14 +42178,8 @@ pub fn emit_main_pipeline_fns(crate_name: String, pipeline_module: String) -> St crate::gunbc_rust_emitted_edge::module_to_filename("v1.compiler.artifact".to_string()); v1_rt::concat( v1_rt::concat( - v1_rt::concat( - v1_rt::concat( - v1_rt::concat(emit_collect_dag_files_fn(), emit_extract_module_path_fn()), - emit_extract_import_paths_fn(), - ), - emit_dep_pool_type_and_fns(), - ), - emit_resolve_transitively_fn(crate_name.clone(), pipeline_mod.clone()), + v1_rt::concat(emit_collect_dag_files_fn(), emit_extract_module_path_fn()), + emit_dep_pool_type_and_fns(), ), emit_compile_target_helpers( crate_name.clone(), @@ -42212,18 +42206,10 @@ pub fn emit_extract_module_path_fn() -> String { v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat("/// Extract the `module x.y.z` declaration from a .dag file's first lines.\n".to_string(), "fn extract_module_path(content: &str) -> Option {\n".to_string()), " for line in content.lines() {\n".to_string()), " let trimmed = line.trim();\n".to_string()), " if trimmed.starts_with(\"module \") {\n".to_string()), " return Some(trimmed[\"module \".len()..].trim().to_string());\n".to_string()), " }\n".to_string()), " if !trimmed.is_empty() && !trimmed.starts_with(\"//\") {\n".to_string()), " break;\n".to_string()), " }\n".to_string()), " }\n".to_string()), " None\n".to_string()), "}\n\n".to_string()) } -pub fn emit_extract_import_paths_fn() -> String { - v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat("/// Extract import module paths from a .dag file's import declarations.\n".to_string(), "fn extract_import_paths(content: &str) -> Vec {\n".to_string()), " let mut imports = Vec::new();\n".to_string()), " for line in content.lines() {\n".to_string()), " let trimmed = line.trim();\n".to_string()), " if trimmed.starts_with(\"import \") {\n".to_string()), " let rest = trimmed[\"import \".len()..].trim();\n".to_string()), " let module_path = rest.split('{').next().unwrap_or(rest).trim();\n".to_string()), " if !module_path.is_empty() {\n".to_string()), " imports.push(module_path.to_string());\n".to_string()), " }\n".to_string()), " }\n".to_string()), " }\n".to_string()), " imports\n".to_string()), "}\n\n".to_string()) -} - pub fn emit_dep_pool_type_and_fns() -> String { v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat("#[derive(Clone, Copy, PartialEq, Eq)]\nenum DependencyPoolIndex {\n Strict,\n PrimaryPrecedence,\n}\n\n".to_string(), "fn parse_dependency_pool_index(value: &str) -> DependencyPoolIndex {\n".to_string()), " match value {\n".to_string()), " \"strict\" => DependencyPoolIndex::Strict,\n".to_string()), " \"primary-precedence\" => DependencyPoolIndex::PrimaryPrecedence,\n".to_string()), " other => {\n".to_string()), " eprintln!(\"unknown --dependency-pool-index: {} (expected strict | primary-precedence)\", other);\n".to_string()), " std::process::exit(1);\n".to_string()), " }\n".to_string()), " }\n".to_string()), "}\n\n".to_string()), "fn insert_module_path(\n".to_string()), " index: &mut HashMap,\n".to_string()), " module_path: String,\n".to_string()), " path: std::path::PathBuf,\n".to_string()), " within_root: &mut HashMap,\n".to_string()), ") {\n".to_string()), " if let Some(existing) = within_root.get(&module_path) {\n".to_string()), " panic!(\"duplicate module path '{}' within source root: declared in both {:?} and {:?}\", module_path, existing, path);\n".to_string()), " }\n".to_string()), " within_root.insert(module_path.clone(), path.clone());\n".to_string()), " index.insert(module_path, path);\n".to_string()), "}\n\n".to_string()), "fn index_source_root(root: &str, index: &mut HashMap, pool_fill_only: bool) {\n".to_string()), " let root_path = std::path::Path::new(root);\n".to_string()), " if !root_path.exists() {\n".to_string()), " panic!(\"source root does not exist: {}\", root);\n".to_string()), " }\n".to_string()), " let mut dag_files = Vec::new();\n".to_string()), " collect_dag_files(root_path, &mut dag_files);\n".to_string()), " let mut within_root = HashMap::new();\n".to_string()), " for path in dag_files {\n".to_string()), " let content = std::fs::read_to_string(&path)\n".to_string()), " .unwrap_or_else(|e| panic!(\"failed to read {:?}: {}\", path, e));\n".to_string()), " if let Some(module_path) = extract_module_path(&content) {\n".to_string()), " if pool_fill_only {\n".to_string()), " if index.contains_key(&module_path) {\n".to_string()), " continue;\n".to_string()), " }\n".to_string()), " insert_module_path(index, module_path, path, &mut within_root);\n".to_string()), " } else if let Some(existing) = index.get(&module_path) {\n".to_string()), " panic!(\"duplicate module path '{}': declared in both {:?} and {:?}\", module_path, existing, path);\n".to_string()), " } else {\n".to_string()), " insert_module_path(index, module_path, path, &mut within_root);\n".to_string()), " }\n".to_string()), " }\n".to_string()), " }\n".to_string()), "}\n\n".to_string()), "fn build_module_index(source_roots: &[String], pool_index: DependencyPoolIndex) -> HashMap {\n".to_string()), " let mut index = HashMap::new();\n".to_string()), " if source_roots.is_empty() {\n".to_string()), " return index;\n".to_string()), " }\n".to_string()), " match pool_index {\n".to_string()), " DependencyPoolIndex::Strict => {\n".to_string()), " for root in source_roots {\n".to_string()), " index_source_root(root, &mut index, false);\n".to_string()), " }\n".to_string()), " }\n".to_string()), " DependencyPoolIndex::PrimaryPrecedence => {\n".to_string()), " index_source_root(&source_roots[0], &mut index, false);\n".to_string()), " for root in &source_roots[1..] {\n".to_string()), " index_source_root(root, &mut index, true);\n".to_string()), " }\n".to_string()), " }\n".to_string()), " }\n".to_string()), " index\n".to_string()), "}\n\n".to_string()) } -pub fn emit_resolve_transitively_fn(crate_name: String, pipeline_mod: String) -> String { - v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat("/// Compile-subject closure of entry modules (import, qualified, and bare references).\n".to_string(), "/// Fail-closed: panics if the closure authority refuses.\n".to_string()), "/// Returns sources sorted by path for deterministic fixed-point convergence.\n".to_string()), "fn resolve_transitively_with_seen(\n".to_string()), " entry_sources: Vec<(String, String)>,\n".to_string()), " _index: &HashMap,\n".to_string()), " seen: HashMap>,\n".to_string()), " source_roots: &[String],\n".to_string()), ") -> Vec> {\n".to_string()), " let mut seeds: Vec> = entry_sources\n".to_string()), " .into_iter()\n".to_string()), " .map(|(path, content)| {\n".to_string()), " Rc::new(".to_string()), crate_name.clone()), "::".to_string()), pipeline_mod.clone()), "::SourceFile { path, content })\n".to_string()), " })\n".to_string()), " .collect();\n".to_string()), " seeds.extend(seen.into_iter().map(|(_, v)| v));\n".to_string()), " let mut closed = ".to_string()), crate_name.clone()), "::cli_run::resolve_seeded_compile_closure(seeds, source_roots)\n".to_string()), " .unwrap_or_else(|e| panic!(\"compile-subject closure: {e}\"));\n".to_string()), " closed.sort_by(|a, b| a.path.cmp(&b.path));\n".to_string()), " closed\n".to_string()), "}\n\n".to_string()) -} - pub fn emit_main_diagnostic_fns(crate_name: String) -> String { { let core_mod = From 278f56e4ec9b1658ab799ded20bd91a63ecfacfa Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Wed, 7 Oct 2026 01:13:42 +0000 Subject: [PATCH 5/5] compiler_tests_rust.dag: do not interpolate panic {e} as a dag variable. v2 self-compile of the rust-in-string body treated format `{e}` as a free dag name and refused stage0-mirrors. Use `{}` plus the rust binding, matching the other unwrap_or_else panics in this file. Co-authored-by: Cursor --- src/v1/compiler_tests_rust.dag | 2 +- src/v1/stage0/src/compiler_tests.rs | 2 +- src/v1/stage0/src/v1_compiler_compiler_tests_rust.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/v1/compiler_tests_rust.dag b/src/v1/compiler_tests_rust.dag index 2563776db26..340dda11f6b 100644 --- a/src/v1/compiler_tests_rust.dag +++ b/src/v1/compiler_tests_rust.dag @@ -123,7 +123,7 @@ fn ct_source_builders() -> String { " .collect();\n", " let pool: Vec = roots.iter().map(|r| (*r).to_string()).collect();\n", " let mut result = crate::cli_run::resolve_seeded_compile_closure(seeds, &pool)\n", - " .unwrap_or_else(|e| panic!(\"resolve_source_closure: {e}\"));\n", + " .unwrap_or_else(|e| panic!(\"resolve_source_closure: {}\", e));\n", " result.sort_by(|a, b| a.path.cmp(&b.path));\n", " result\n", " }\n\n", diff --git a/src/v1/stage0/src/compiler_tests.rs b/src/v1/stage0/src/compiler_tests.rs index 1df3cbb82c1..a774f21fc38 100644 --- a/src/v1/stage0/src/compiler_tests.rs +++ b/src/v1/stage0/src/compiler_tests.rs @@ -90,7 +90,7 @@ mod compiler_tests { .collect(); let pool: Vec = roots.iter().map(|r| (*r).to_string()).collect(); let mut result = crate::cli_run::resolve_seeded_compile_closure(seeds, &pool) - .unwrap_or_else(|e| panic!("resolve_source_closure: {e}")); + .unwrap_or_else(|e| panic!("resolve_source_closure: {}", e)); result.sort_by(|a, b| a.path.cmp(&b.path)); result } diff --git a/src/v1/stage0/src/v1_compiler_compiler_tests_rust.rs b/src/v1/stage0/src/v1_compiler_compiler_tests_rust.rs index f60ae6c84c6..c5c5b0b9a60 100644 --- a/src/v1/stage0/src/v1_compiler_compiler_tests_rust.rs +++ b/src/v1/stage0/src/v1_compiler_compiler_tests_rust.rs @@ -37,7 +37,7 @@ pub fn ct_file_discovery_helpers() -> String { } pub fn ct_source_builders() -> String { - v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(" /// Build SourceFile vec from discovered .dag files.\n".to_string(), " fn source_files_from(\n".to_string()), " pairs: &[(String, String)],\n".to_string()), " ) -> Vec> {\n".to_string()), " pairs\n".to_string()), " .iter()\n".to_string()), " .map(|(path, content)| {\n".to_string()), " std::rc::Rc::new(crate::v1_compiler_compile::SourceFile {\n".to_string()), " path: path.clone(),\n".to_string()), " content: content.clone(),\n".to_string()), " })\n".to_string()), " })\n".to_string()), " .collect()\n".to_string()), " }\n\n".to_string()), " fn resolve_source_closure(\n".to_string()), " entry_pairs: Vec<(String, String)>,\n".to_string()), " roots: &[&str],\n".to_string()), " ) -> Vec> {\n".to_string()), " let seeds = entry_pairs\n".to_string()), " .into_iter()\n".to_string()), " .map(|(path, content)| {\n".to_string()), " std::rc::Rc::new(crate::v1_compiler_compile::SourceFile { path, content })\n".to_string()), " })\n".to_string()), " .collect();\n".to_string()), " let pool: Vec = roots.iter().map(|r| (*r).to_string()).collect();\n".to_string()), " let mut result = crate::cli_run::resolve_seeded_compile_closure(seeds, &pool)\n".to_string()), " .unwrap_or_else(|e| panic!(\"resolve_source_closure: {e}\"));\n".to_string()), " result.sort_by(|a, b| a.path.cmp(&b.path));\n".to_string()), " result\n".to_string()), " }\n\n".to_string()), ct_self_compile_sources()) + v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(" /// Build SourceFile vec from discovered .dag files.\n".to_string(), " fn source_files_from(\n".to_string()), " pairs: &[(String, String)],\n".to_string()), " ) -> Vec> {\n".to_string()), " pairs\n".to_string()), " .iter()\n".to_string()), " .map(|(path, content)| {\n".to_string()), " std::rc::Rc::new(crate::v1_compiler_compile::SourceFile {\n".to_string()), " path: path.clone(),\n".to_string()), " content: content.clone(),\n".to_string()), " })\n".to_string()), " })\n".to_string()), " .collect()\n".to_string()), " }\n\n".to_string()), " fn resolve_source_closure(\n".to_string()), " entry_pairs: Vec<(String, String)>,\n".to_string()), " roots: &[&str],\n".to_string()), " ) -> Vec> {\n".to_string()), " let seeds = entry_pairs\n".to_string()), " .into_iter()\n".to_string()), " .map(|(path, content)| {\n".to_string()), " std::rc::Rc::new(crate::v1_compiler_compile::SourceFile { path, content })\n".to_string()), " })\n".to_string()), " .collect();\n".to_string()), " let pool: Vec = roots.iter().map(|r| (*r).to_string()).collect();\n".to_string()), " let mut result = crate::cli_run::resolve_seeded_compile_closure(seeds, &pool)\n".to_string()), " .unwrap_or_else(|e| panic!(\"resolve_source_closure: {}\", e));\n".to_string()), " result.sort_by(|a, b| a.path.cmp(&b.path));\n".to_string()), " result\n".to_string()), " }\n\n".to_string()), ct_self_compile_sources()) } pub fn ct_self_compile_sources() -> String {