From 1c01f0c2c1bc9fc1b07be5df0c7305f7da63bab1 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Sat, 4 Jul 2026 23:11:46 +0000 Subject: [PATCH 01/32] WIP: P3+P5: infer whole-corpus scans to per-module maps --- src/v1/04_infer.dag | 148 +++++++++++++++++++++++++++----------------- 1 file changed, 92 insertions(+), 56 deletions(-) diff --git a/src/v1/04_infer.dag b/src/v1/04_infer.dag index 890ce75458e..a5442ef40df 100644 --- a/src/v1/04_infer.dag +++ b/src/v1/04_infer.dag @@ -179,6 +179,16 @@ type VariantFoldState { collision_errors: List } +type VariantExportSurface { + arm_owners: Map + enum_items: Map +} + +type TypeNameExportFacts { + exporter_count: Int + canonical_binding: TypeBinding? +} + type InferScope { type_env: TypeEnv func_env: ResolvedFuncEnv @@ -6030,53 +6040,59 @@ fn build_local_variants(items: List, source_indices: Map, source_indices: Map) -> Node? { - match map_get(parent_index, module_path) { - Absent => none - Present { value: tm } => - let own = tm.items - |> filter(item => item.connective == Disj) - |> filter(item => item.children |> any(c => authored_name_at(source_indices: source_indices, node: c) == arm_name)) - |> first - match own { - Present { value: item } => Present { value: item } - Absent => - let reexport = module_imports(n: tm.module) - |> filter(imp => !import_is_all(n: imp)) - |> filter(imp => import_specific_names_at(n: imp, source_indices: source_indices) |> any(n => n == arm_name)) - |> first - match reexport { - Present { value: imp } => owner_of_exported_arm(module_path: authored_name_at(source_indices: source_indices, node: imp), arm_name: arm_name, parent_index: parent_index, source_indices: source_indices) - Absent => none - } - } +fn empty_variant_export_surface() -> VariantExportSurface { + VariantExportSurface { arm_owners: empty_map(), enum_items: empty_map() } +} + +fn collect_own_variant_export_surface(items: List, source_indices: Map) -> VariantExportSurface { + fold(items, init: empty_variant_export_surface(), f: (acc, item) => + if item.connective == Disj { + let enum_items = map_insert(acc.enum_items, authored_name_at(source_indices: source_indices, node: item), item) + let arm_owners = fold(item.children, init: acc.arm_owners, f: (arms, child) => + map_insert(arms, authored_name_at(source_indices: source_indices, node: child), item) + ) + VariantExportSurface { arm_owners: arm_owners, enum_items: enum_items } + } else { acc } + ) +} + +fn reexport_variant_surface_fragment(imp: Node, surfaces: Map, source_indices: Map) -> VariantExportSurface { + let proxy_path = import_module_path_at(imp: imp, source_indices: source_indices) + match map_get(surfaces, proxy_path) { + Absent => empty_variant_export_surface() + Present { value: proxy } => + fold(import_specific_names_at(n: imp, source_indices: source_indices), init: empty_variant_export_surface(), f: (acc, name) => + let acc = match map_get(proxy.arm_owners, name) { + Present { value: owner } => VariantExportSurface { arm_owners: map_insert(acc.arm_owners, name, owner), enum_items: acc.enum_items } + Absent => acc + } + match map_get(proxy.enum_items, name) { + Present { value: item } => VariantExportSurface { arm_owners: acc.arm_owners, enum_items: map_insert(acc.enum_items, name, item) } + Absent => acc + } + ) } } -fn exported_coproduct_item(module_path: String, enum_name: String, parent_index: Map, source_indices: Map) -> Node? { - match map_get(parent_index, module_path) { - Absent => none - Present { value: tm } => - let own = tm.items - |> filter(item => item.connective == Disj) - |> filter(item => authored_name_at(source_indices: source_indices, node: item) == enum_name) - |> first - match own { - Present { value: item } => Present { value: item } - Absent => - let reexport = module_imports(n: tm.module) - |> filter(imp => !import_is_all(n: imp)) - |> filter(imp => import_specific_names_at(n: imp, source_indices: source_indices) |> any(n => n == enum_name)) - |> first - match reexport { - Present { value: imp } => exported_coproduct_item(module_path: authored_name_at(source_indices: source_indices, node: imp), enum_name: enum_name, parent_index: parent_index, source_indices: source_indices) - Absent => none - } +fn build_variant_export_surface(tm: TypedModule, surfaces: Map, source_indices: Map) -> VariantExportSurface { + let own = collect_own_variant_export_surface(items: tm.items, source_indices: source_indices) + let reexports = fold( + module_imports(n: tm.module) |> filter(imp => !import_is_all(n: imp)), + init: empty_variant_export_surface(), + f: (acc, imp) => + let frag = reexport_variant_surface_fragment(imp: imp, surfaces: surfaces, source_indices: source_indices) + VariantExportSurface { + arm_owners: map_merge(acc.arm_owners, frag.arm_owners), + enum_items: map_merge(acc.enum_items, frag.enum_items) } + ) + VariantExportSurface { + arm_owners: map_merge(reexports.arm_owners, own.arm_owners), + enum_items: map_merge(reexports.enum_items, own.enum_items) } } -fn build_imported_variants(resolved_imports: List, parent_index: Map, source_indices: Map, module_name: String, init: VariantFoldState) -> VariantFoldState { +fn build_imported_variants(resolved_imports: List, parent_index: Map, variant_surfaces: Map, source_indices: Map, module_name: String, init: VariantFoldState) -> VariantFoldState { fold(resolved_imports, init: init, f: (acc, imp) => let with_glob = if imp.is_all { let source_items = match map_get(parent_index, imp.module_path) { @@ -6091,11 +6107,15 @@ fn build_imported_variants(resolved_imports: List, parent_index: ) } else { acc } fold(imp.specific_names, init: with_glob, f: (nacc, name) => - let after_enum = match exported_coproduct_item(module_path: imp.module_path, enum_name: name, parent_index: parent_index, source_indices: source_indices) { + let surface = match map_get(variant_surfaces, imp.module_path) { + Present { value: s } => s + Absent => empty_variant_export_surface() + } + let after_enum = match map_get(surface.enum_items, name) { Present { value: item } => bind_coproduct_item_arms(state: nacc, item: item, source_indices: source_indices, module_name: module_name) Absent => nacc } - match owner_of_exported_arm(module_path: imp.module_path, arm_name: name, parent_index: parent_index, source_indices: source_indices) { + match map_get(surface.arm_owners, name) { Present { value: owner } => insert_variant_owner_checked(state: after_enum, arm_name: name, owner: owner, source_indices: source_indices, module_name: module_name) Absent => after_enum } @@ -6105,6 +6125,7 @@ fn build_imported_variants(resolved_imports: List, parent_index: fn build_module_context(contributions: List, parent_index: Map, + variant_surfaces: Map, resolved_imports: List, env: TypeEnv, module_name: String) -> ModuleContext { @@ -6127,6 +6148,7 @@ fn build_module_context(contributions: List, let variant_fold = build_imported_variants( resolved_imports: resolved_imports, parent_index: parent_index, + variant_surfaces: variant_surfaces, source_indices: env.source_indices, module_name: module_name, init: local_variant_fold @@ -6172,7 +6194,7 @@ type TypecheckModuleResult { diagnostics: List } -fn typecheck_module(resolved: ResolvedModule, parent_index: Map, source_indices: Map, intern_table: InternTable) -> TypecheckModuleResult { +fn typecheck_module(resolved: ResolvedModule, parent_index: Map, variant_surfaces: Map, source_indices: Map, intern_table: InternTable) -> TypecheckModuleResult { let env_result = build_type_env(module: resolved, parent_index: parent_index, source_indices: source_indices, intern_table: intern_table) let env = env_result.env let env_cache = env_result.cache @@ -6201,6 +6223,7 @@ fn typecheck_module(resolved: ResolvedModule, parent_index: Map, inte remaining: graph.modules, modules: [], module_index: empty_map(), + variant_surfaces: empty_map(), item_registry: empty_map(), diag_chunks: [], source_indices: source_indices, @@ -6511,6 +6535,7 @@ fn typecheck_modules( remaining: List, modules: List, module_index: Map, + variant_surfaces: Map, item_registry: Map, diag_chunks: List>, source_indices: Map, @@ -6522,13 +6547,20 @@ fn typecheck_modules( TypedGraph { modules: modules, item_registry: expanded_registry, diagnostics: flat_map(diag_chunks, c => c) } Present { value: resolved } => let parent_result = collect_parent_envs(resolved: resolved, module_index: module_index, source_indices: source_indices) - let tc_result = typecheck_module(resolved: resolved, parent_index: module_index, source_indices: source_indices, intern_table: intern_table) + let tc_result = typecheck_module(resolved: resolved, parent_index: module_index, variant_surfaces: variant_surfaces, source_indices: source_indices, intern_table: intern_table) let typed = tc_result.typed let tc_diags = tc_result.diagnostics + let typed_path = authored_name_at(source_indices: source_indices, node: typed.module) + let variant_surfaces = map_insert( + variant_surfaces, + typed_path, + build_variant_export_surface(tm: typed, surfaces: variant_surfaces, source_indices: source_indices) + ) typecheck_modules( remaining: remaining |> skip(1), modules: list_push(modules, typed), - module_index: map_insert(module_index, authored_name_at(source_indices: source_indices, node: typed.module), typed), + module_index: map_insert(module_index, typed_path, typed), + variant_surfaces: variant_surfaces, item_registry: map_merge(item_registry, typed.item_registry), diag_chunks: list_push(list_push(diag_chunks, parent_result.diagnostics), tc_diags), source_indices: source_indices, @@ -6553,17 +6585,21 @@ fn local_authority_binding(owner: TypedModule, name: String) -> TypeBinding? { } } -fn global_type_exporter_count(modules: List, name: String) -> Int { - filter(modules, m => module_exports_type_name(m: m, name: name)) |> count -} - -data local_authority_for_name_dissolution_trigger: String = "๐ŸŸก dissolve-on: single-exporter rewire canonical picks last TypedModule in compilation list (filter then last); multi-exporter names skip rewire and defer to merge_type_env_cache overlay-wins โ€” ground on import-order authority at v2 regen." - -fn local_authority_for_name(modules: List, name: String) -> TypedModule? { - match filter(modules, m => (filter(m.type_env.bindings |> map_values, b => b.name == name) |> count) > 0) |> last { - Present { value: owner } => Present { value: owner } - Absent => Absent - } +fn build_type_name_export_index(modules: List) -> Map { + fold(modules, init: empty_map(), f: (acc, m) => + fold(m.type_env.bindings |> map_values, init: acc, f: (acc2, binding) => + let name = binding.name + match map_get(acc2, name) { + Absent => + map_insert(acc2, name, TypeNameExportFacts { exporter_count: 1, canonical_binding: Present { value: binding } }) + Present { value: facts } => + map_insert(acc2, name, TypeNameExportFacts { + exporter_count: facts.exporter_count + 1, + canonical_binding: Present { value: binding } + }) + } + ) + ) } fn module_exports_type_name(m: TypedModule, name: String) -> Bool { From e0cc6f6c994cf873017d1912bdc4fa5537a975ae Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Sat, 4 Jul 2026 23:25:08 +0000 Subject: [PATCH 02/32] WIP: P3+P5: infer whole-corpus scans to per-module maps --- src/v1/04_infer.dag | 25 ++- src/v1/compiler_tests_rust.dag | 4 + src/v1/stage0/src/cli_run.rs | 15 +- src/v1/stage0/src/compiler_tests.rs | 4 + src/v1/stage0/src/v1_compiler_infer.rs | 290 ++++++++++++++----------- 5 files changed, 200 insertions(+), 138 deletions(-) diff --git a/src/v1/04_infer.dag b/src/v1/04_infer.dag index a5442ef40df..9eb53988149 100644 --- a/src/v1/04_infer.dag +++ b/src/v1/04_infer.dag @@ -6062,13 +6062,13 @@ fn reexport_variant_surface_fragment(imp: Node, surfaces: Map empty_variant_export_surface() Present { value: proxy } => fold(import_specific_names_at(n: imp, source_indices: source_indices), init: empty_variant_export_surface(), f: (acc, name) => - let acc = match map_get(proxy.arm_owners, name) { + let with_arm = match map_get(proxy.arm_owners, name) { Present { value: owner } => VariantExportSurface { arm_owners: map_insert(acc.arm_owners, name, owner), enum_items: acc.enum_items } Absent => acc } match map_get(proxy.enum_items, name) { - Present { value: item } => VariantExportSurface { arm_owners: acc.arm_owners, enum_items: map_insert(acc.enum_items, name, item) } - Absent => acc + Present { value: item } => VariantExportSurface { arm_owners: with_arm.arm_owners, enum_items: map_insert(with_arm.enum_items, name, item) } + Absent => with_arm } ) } @@ -6616,8 +6616,10 @@ fn direct_import_exporter_count(m: TypedModule, name: String, index: Map count } +data local_authority_for_name_dissolution_trigger: String = "๐ŸŸก dissolve-on: single-exporter rewire canonical picks last TypedModule in compilation list (filter then last); multi-exporter names skip rewire and defer to merge_type_env_cache overlay-wins โ€” ground on import-order authority at v2 regen." + fn rewire_inherited_str_binding( - modules: List, + type_name_index: Map, module_index: Map, consumer: TypedModule, source_indices: Map, @@ -6626,12 +6628,16 @@ fn rewire_inherited_str_binding( ancestry_str_bindings: Map, name: String ) -> StrBindingsRewireAccum { - if set_contains(local_names, name) || direct_import_exporter_count(m: consumer, name: name, index: module_index, source_indices: source_indices) > 1 || global_type_exporter_count(modules: modules, name: name) > 1 { + let exporter_count = match map_get(type_name_index, name) { + Present { value: facts } => facts.exporter_count + Absent => 0 + } + if set_contains(local_names, name) || direct_import_exporter_count(m: consumer, name: name, index: module_index, source_indices: source_indices) > 1 || exporter_count > 1 { StrBindingsRewireAccum { str_bindings: str_bindings, ancestry_str_bindings: ancestry_str_bindings } } else { - match local_authority_for_name(modules: modules, name: name) { - Present { value: owner } => - match local_authority_binding(owner: owner, name: name) { + match map_get(type_name_index, name) { + Present { value: facts } => + match facts.canonical_binding { Present { value: canonical } => let ancestry_str_bindings = map_insert(ancestry_str_bindings, name, canonical) let str_bindings = match map_get(str_bindings, name) { @@ -6647,6 +6653,7 @@ fn rewire_inherited_str_binding( } fn rewire_type_env_import_str_binding_identity(modules: List, source_indices: Map) -> List { + let type_name_index = build_type_name_export_index(modules: modules) let index = fold(modules, init: empty_map(), f: (acc, m) => map_insert(acc, authored_name_at(source_indices: source_indices, node: m.module), m) ) @@ -6657,7 +6664,7 @@ fn rewire_type_env_import_str_binding_identity(modules: List, sourc let inherited_keys = concat(ancestry_keys, str_keys) let rewired = fold(inherited_keys, init: StrBindingsRewireAccum { str_bindings: m.type_env.str_bindings, ancestry_str_bindings: m.type_env.ancestry_str_bindings }, f: (acc, name) => rewire_inherited_str_binding( - modules: modules, + type_name_index: type_name_index, module_index: index, consumer: m, source_indices: source_indices, diff --git a/src/v1/compiler_tests_rust.dag b/src/v1/compiler_tests_rust.dag index 9bcf3950f13..1c8c8e77a0b 100644 --- a/src/v1/compiler_tests_rust.dag +++ b/src/v1/compiler_tests_rust.dag @@ -1193,6 +1193,10 @@ fn ct_profile_reconcile_test() -> String { " let tc_result = crate::v1_compiler_infer::typecheck_module(\n", " resolved.clone(),\n", " module_index.clone(),\n", + " std::rc::Rc::new(std::collections::HashMap::<\n", + " String,\n", + " std::rc::Rc,\n", + " >::new()),\n", " source_indices.clone(),\n", " intern_table.clone(),\n", " );\n", diff --git a/src/v1/stage0/src/cli_run.rs b/src/v1/stage0/src/cli_run.rs index d4b08570797..2fa6df6c6be 100644 --- a/src/v1/stage0/src/cli_run.rs +++ b/src/v1/stage0/src/cli_run.rs @@ -1239,6 +1239,8 @@ fn reconcile_with_typed_cache( let mut module_index: Rc>> = v1_rt::rc_empty_map(); let mut item_registry: Rc>> = v1_rt::rc_empty_map(); let mut diag_chunks: Vec>>> = Vec::new(); + let mut variant_surfaces: Rc>> = + v1_rt::rc_empty_map(); for resolved in graph.modules.iter().cloned() { let parent_result = v1_compiler_infer::collect_parent_envs( @@ -1273,6 +1275,7 @@ fn reconcile_with_typed_cache( let computed = v1_compiler_infer::typecheck_module( resolved.clone(), module_index.clone(), + variant_surfaces.clone(), source_indices.clone(), intern_table.clone(), ); @@ -1292,9 +1295,19 @@ fn reconcile_with_typed_cache( }; let typed = tc_result.typed.clone(); modules = v1_rt::rc_list_push(modules, typed.clone()); + let typed_path = authored_name_at(source_indices.clone(), typed.module.clone()); + variant_surfaces = v1_rt::rc_map_insert( + variant_surfaces.clone(), + typed_path.clone(), + v1_compiler_infer::build_variant_export_surface( + typed.clone(), + variant_surfaces.clone(), + source_indices.clone(), + ), + ); module_index = v1_rt::rc_map_insert( module_index, - authored_name_at(source_indices.clone(), typed.module.clone()), + typed_path, typed.clone(), ); item_registry = v1_rt::rc_map_merge(item_registry, typed.item_registry.clone()); diff --git a/src/v1/stage0/src/compiler_tests.rs b/src/v1/stage0/src/compiler_tests.rs index 4728261cd2d..3a0bae6ef66 100644 --- a/src/v1/stage0/src/compiler_tests.rs +++ b/src/v1/stage0/src/compiler_tests.rs @@ -1717,6 +1717,10 @@ mod compiler_tests { let tc_result = crate::v1_compiler_infer::typecheck_module( resolved.clone(), module_index.clone(), + std::rc::Rc::new(std::collections::HashMap::< + String, + std::rc::Rc, + >::new()), source_indices.clone(), intern_table.clone(), ); diff --git a/src/v1/stage0/src/v1_compiler_infer.rs b/src/v1/stage0/src/v1_compiler_infer.rs index 0525069bf0f..27d6446435a 100644 --- a/src/v1/stage0/src/v1_compiler_infer.rs +++ b/src/v1/stage0/src/v1_compiler_infer.rs @@ -202,6 +202,18 @@ pub struct VariantFoldState { pub collision_errors: Rc>>, } +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct VariantExportSurface { + pub arm_owners: Rc>>, + pub enum_items: Rc>>, +} + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct TypeNameExportFacts { + pub exporter_count: i64, + pub canonical_binding: Option>, +} + #[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] pub struct InferScope { pub type_env: Rc, @@ -13571,97 +13583,104 @@ pub fn build_imported_variants( ) } -// HAND-PATCH (constructor-owner ruling ยง1c; seed two-step): re-export chain -// followers โ€” a module re-exports its specific-name imports, so the binder -// walks the acyclic import chain to the defining module's coproduct. Replaced -// by regen's true emission in the convergence commit. -pub fn owner_of_exported_arm( - module_path: String, - arm_name: String, - parent_index: Rc>>, +pub fn empty_variant_export_surface() -> Rc { + Rc::new(VariantExportSurface { + arm_owners: v1_rt::rc_empty_map::>(), + enum_items: v1_rt::rc_empty_map::>(), + }) +} + +pub fn collect_own_variant_export_surface( + items: Rc>>, source_indices: Rc>>, -) -> Option> { - match v1_rt::map_get(&parent_index, module_path) { - None => None, - Some(tm) => { - let own = tm.items.iter().cloned().find(|item| { - item.connective.clone() == Connective::Disj - && item - .children - .iter() - .cloned() - .any(|c| authored_name_at(source_indices.clone(), c) == arm_name) - }); - match own { - Some(item) => Some(item), - None => { - let reexport = module_imports(tm.module.clone()) - .iter() - .cloned() - .find(|imp| { - !import_is_all(imp.clone()) - && import_specific_names_at(imp.clone(), source_indices.clone()) - .iter() - .any(|n| *n == arm_name) - }); - match reexport { - Some(imp) => owner_of_exported_arm( - authored_name_at(source_indices.clone(), imp), - arm_name, - parent_index, - source_indices, - ), - None => None, - } - } - } +) -> Rc { + items.iter().cloned().fold(empty_variant_export_surface(), |acc, item| { + if item.connective.clone() == Connective::Disj { + let enum_items = v1_rt::rc_map_insert( + acc.enum_items.clone(), + authored_name_at(source_indices.clone(), item.clone()), + item.clone(), + ); + let arm_owners = item.children.clone().iter().cloned().fold( + acc.arm_owners.clone(), + |arms: Rc>>, child: Rc| { + v1_rt::rc_map_insert( + arms, + authored_name_at(source_indices.clone(), child), + item.clone(), + ) + }, + ); + Rc::new(VariantExportSurface { + arm_owners, + enum_items, + }) + } else { + acc } - } + }) } -pub fn exported_coproduct_item( - module_path: String, - enum_name: String, - parent_index: Rc>>, +fn reexport_variant_surface_fragment( + imp: Rc, + surfaces: Rc>>, source_indices: Rc>>, -) -> Option> { - match v1_rt::map_get(&parent_index, module_path) { - None => None, - Some(tm) => { - let own = tm.items.iter().cloned().find(|item| { - item.connective.clone() == Connective::Disj - && authored_name_at(source_indices.clone(), item.clone()) == enum_name - }); - match own { - Some(item) => Some(item), - None => { - let reexport = module_imports(tm.module.clone()) - .iter() - .cloned() - .find(|imp| { - !import_is_all(imp.clone()) - && import_specific_names_at(imp.clone(), source_indices.clone()) - .iter() - .any(|n| *n == enum_name) - }); - match reexport { - Some(imp) => exported_coproduct_item( - authored_name_at(source_indices.clone(), imp), - enum_name, - parent_index, - source_indices, - ), - None => None, - } +) -> Rc { + let proxy_path = import_module_path_at(imp.clone(), source_indices.clone()); + match v1_rt::map_get(&surfaces, proxy_path) { + None => empty_variant_export_surface(), + Some(proxy) => import_specific_names_at(imp, source_indices.clone()) + .iter() + .cloned() + .fold(empty_variant_export_surface(), |acc, name| { + let with_arm = match v1_rt::map_get(&proxy.arm_owners, name.clone()) { + Some(owner) => Rc::new(VariantExportSurface { + arm_owners: v1_rt::rc_map_insert(acc.arm_owners.clone(), name.clone(), owner), + enum_items: acc.enum_items.clone(), + }), + None => acc.clone(), + }; + match v1_rt::map_get(&proxy.enum_items, name.clone()) { + Some(item) => Rc::new(VariantExportSurface { + arm_owners: with_arm.arm_owners.clone(), + enum_items: v1_rt::rc_map_insert(with_arm.enum_items.clone(), name, item), + }), + None => with_arm, } - } - } + }), } } +pub fn build_variant_export_surface( + tm: Rc, + surfaces: Rc>>, + source_indices: Rc>>, +) -> Rc { + let own = collect_own_variant_export_surface(tm.items.clone(), source_indices.clone()); + let reexports = module_imports(tm.module.clone()) + .iter() + .cloned() + .filter(|imp| !import_is_all(imp.clone())) + .fold(empty_variant_export_surface(), |acc, imp| { + let frag = reexport_variant_surface_fragment(imp, surfaces.clone(), source_indices.clone()); + Rc::new(VariantExportSurface { + arm_owners: v1_rt::rc_map_merge(acc.arm_owners.clone(), frag.arm_owners.clone()), + enum_items: v1_rt::rc_map_merge(acc.enum_items.clone(), frag.enum_items.clone()), + }) + }); + Rc::new(VariantExportSurface { + arm_owners: v1_rt::rc_map_merge(reexports.arm_owners.clone(), own.arm_owners.clone()), + enum_items: v1_rt::rc_map_merge(reexports.enum_items.clone(), own.enum_items.clone()), + }) +} + +// HAND-PATCH (P3+P5): per-module variant export surface replaces whole-corpus +// Disj-scan + re-export chain walk per imported name. Dissolves at v2 regen. + pub fn build_module_context( contributions: Rc>>, parent_index: Rc>>, + variant_surfaces: Rc>>, resolved_imports: Rc>>, env: Rc, module_name: String, @@ -13779,26 +13798,17 @@ pub fn build_module_context( } else { acc }; - // Specific names resolve through the re-export chain to the - // defining module (owner_of_exported_arm / exported_coproduct_item). + // Specific names resolve through the precomputed per-module export surface. imp.specific_names.iter().cloned().fold( with_glob, |nacc: Rc, name: String| { - let after_enum = match exported_coproduct_item( - imp.module_path.clone(), - name.clone(), - parent_index.clone(), - env.source_indices.clone(), - ) { + let surface = v1_rt::map_get(&variant_surfaces, imp.module_path.clone()) + .unwrap_or_else(empty_variant_export_surface); + let after_enum = match v1_rt::map_get(&surface.enum_items, name.clone()) { Some(item) => bind_coproduct_item_arms(nacc, item), None => nacc, }; - match owner_of_exported_arm( - imp.module_path.clone(), - name.clone(), - parent_index.clone(), - env.source_indices.clone(), - ) { + match v1_rt::map_get(&surface.arm_owners, name.clone()) { Some(owner) => insert_variant_owner_checked(after_enum, name, owner), None => after_enum, } @@ -13879,6 +13889,7 @@ pub struct TypecheckModuleResult { pub fn typecheck_module( resolved: Rc, parent_index: Rc>>, + variant_surfaces: Rc>>, source_indices: Rc>>, intern_table: Rc, ) -> Rc { @@ -13934,6 +13945,7 @@ pub fn typecheck_module( let ctx = build_module_context( contributions, parent_index.clone(), + variant_surfaces.clone(), resolved.resolved_imports.clone(), env.clone(), resolved_module_name.clone(), @@ -14656,6 +14668,7 @@ pub fn typecheck( graph.modules.clone(), Rc::new(vec![]), v1_rt::rc_empty_map::>(), + v1_rt::rc_empty_map::>(), v1_rt::rc_empty_map::>(), Rc::new(vec![]), source_indices, @@ -14668,6 +14681,7 @@ pub fn typecheck_modules( mut remaining: Rc>>, mut modules: Rc>>, mut module_index: Rc>>, + mut variant_surfaces: Rc>>, mut item_registry: Rc>>, mut diag_chunks: Rc>>>>, mut source_indices: Rc>>, @@ -14699,11 +14713,23 @@ pub fn typecheck_modules( let tc_result = typecheck_module( resolved.clone(), module_index.clone(), + variant_surfaces.clone(), source_indices.clone(), intern_table.clone(), ); let typed = tc_result.typed.clone(); let tc_diags = tc_result.diagnostics.clone(); + let typed_path = + authored_name_at(source_indices.clone(), typed.module.clone()); + variant_surfaces = v1_rt::rc_map_insert( + variant_surfaces.clone(), + typed_path.clone(), + build_variant_export_surface( + typed.clone(), + variant_surfaces.clone(), + source_indices.clone(), + ), + ); { let __tco_0 = Rc::new( remaining @@ -14713,11 +14739,7 @@ pub fn typecheck_modules( .collect::>(), ); let __tco_1 = v1_rt::rc_list_push(modules, typed.clone()); - let __tco_2 = v1_rt::rc_map_insert( - module_index, - authored_name_at(source_indices.clone(), typed.module.clone()), - typed.clone(), - ); + let __tco_2 = v1_rt::rc_map_insert(module_index, typed_path, typed.clone()); let __tco_3 = v1_rt::rc_map_merge(item_registry, typed.item_registry.clone()); let __tco_4 = v1_rt::rc_list_push( v1_rt::rc_list_push(diag_chunks, parent_result.diagnostics.clone()), @@ -14742,38 +14764,46 @@ pub fn import_module_path_at( authored_name_at(source_indices, imp) } +pub fn build_type_name_export_index( + modules: Rc>>, +) -> Rc>> { + modules.iter().cloned().fold( + v1_rt::rc_empty_map::>(), + |acc: Rc>>, m: Rc| { + m.type_env + .bindings + .values() + .cloned() + .fold(acc, |acc2, binding| { + let name = binding.name.clone(); + match v1_rt::map_get(&acc2, name.clone()) { + None => v1_rt::rc_map_insert( + acc2, + name, + Rc::new(TypeNameExportFacts { + exporter_count: 1, + canonical_binding: Some(binding), + }), + ), + Some(facts) => v1_rt::rc_map_insert( + acc2, + name, + Rc::new(TypeNameExportFacts { + exporter_count: facts.exporter_count + 1, + canonical_binding: Some(binding), + }), + ), + } + }) + }, + ) +} + pub fn rewire_type_env_import_str_binding_identity( modules: Rc>>, source_indices: Rc>>, ) -> Rc>> { record_rewire_type_env_import_str_binding_call(); - fn local_authority_binding(owner: Rc, name: String) -> Option> { - owner - .type_env - .bindings - .values() - .find(|b| b.name.clone() == name) - .cloned() - } - - fn global_type_exporter_count(modules: Rc>>, name: &str) -> usize { - modules - .iter() - .filter(|m| module_exports_type_name((**m).clone(), name)) - .count() - } - - fn local_authority_for_name( - modules: Rc>>, - name: String, - ) -> Option> { - // ๐ŸŸก dissolve-on: local_authority_for_name_dissolution_trigger (04_infer.dag) - modules - .iter() - .rev() - .find(|m| m.type_env.bindings.values().any(|b| b.name.clone() == name)) - .cloned() - } fn module_exports_type_name(m: Rc, name: &str) -> bool { m.type_env @@ -14799,6 +14829,7 @@ pub fn rewire_type_env_import_str_binding_identity( .count() } + let type_name_index = build_type_name_export_index(modules.clone()); let index = modules.clone().iter().cloned().fold( v1_rt::rc_empty_map::>(), |acc: Rc>>, m: Rc| { @@ -14829,18 +14860,21 @@ pub fn rewire_type_env_import_str_binding_identity( .collect(); inherited_names.sort(); for name in inherited_names { + let exporter_count = v1_rt::map_get(&type_name_index, name.clone()) + .map(|facts| facts.exporter_count) + .unwrap_or(0); if direct_import_exporter_count( m.clone(), name.as_str(), &index, source_indices.clone(), ) > 1 - || global_type_exporter_count(modules.clone(), name.as_str()) > 1 + || exporter_count > 1 { continue; } - if let Some(owner) = local_authority_for_name(modules.clone(), name.clone()) { - if let Some(canonical) = local_authority_binding(owner, name.clone()) { + if let Some(facts) = v1_rt::map_get(&type_name_index, name.clone()) { + if let Some(canonical) = facts.canonical_binding.clone() { if match v1_rt::map_get(&ancestry_str_bindings, name.clone()) { Some(existing) => { Rc::ptr_eq(&existing.resolved, &canonical.resolved) From 43a30d7d642ddfe4aa0123f2beb9f318e69a677e Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Sat, 4 Jul 2026 23:25:28 +0000 Subject: [PATCH 03/32] P3+P5: precompute type-name and variant export indices for O(1) infer lookups Replace whole-corpus scans during reconcile (type exporter count / canonical binding) and per-import variant resolution (Disj-scan + re-export chain walk) with one-pass indexes built at the owning scope; hand-patch stage0 seed until regen converges. Co-authored-by: Cursor --- src/v1/stage0/src/cli_run.rs | 6 +-- src/v1/stage0/src/v1_compiler_infer.rs | 65 ++++++++++++++------------ 2 files changed, 37 insertions(+), 34 deletions(-) diff --git a/src/v1/stage0/src/cli_run.rs b/src/v1/stage0/src/cli_run.rs index 2fa6df6c6be..e58e6f74ce6 100644 --- a/src/v1/stage0/src/cli_run.rs +++ b/src/v1/stage0/src/cli_run.rs @@ -1305,11 +1305,7 @@ fn reconcile_with_typed_cache( source_indices.clone(), ), ); - module_index = v1_rt::rc_map_insert( - module_index, - typed_path, - typed.clone(), - ); + module_index = v1_rt::rc_map_insert(module_index, typed_path, typed.clone()); item_registry = v1_rt::rc_map_merge(item_registry, typed.item_registry.clone()); diag_chunks.push(parent_result.diagnostics.clone()); diag_chunks.push(tc_result.diagnostics.clone()); diff --git a/src/v1/stage0/src/v1_compiler_infer.rs b/src/v1/stage0/src/v1_compiler_infer.rs index 27d6446435a..248de7500eb 100644 --- a/src/v1/stage0/src/v1_compiler_infer.rs +++ b/src/v1/stage0/src/v1_compiler_infer.rs @@ -13594,31 +13594,34 @@ pub fn collect_own_variant_export_surface( items: Rc>>, source_indices: Rc>>, ) -> Rc { - items.iter().cloned().fold(empty_variant_export_surface(), |acc, item| { - if item.connective.clone() == Connective::Disj { - let enum_items = v1_rt::rc_map_insert( - acc.enum_items.clone(), - authored_name_at(source_indices.clone(), item.clone()), - item.clone(), - ); - let arm_owners = item.children.clone().iter().cloned().fold( - acc.arm_owners.clone(), - |arms: Rc>>, child: Rc| { - v1_rt::rc_map_insert( - arms, - authored_name_at(source_indices.clone(), child), - item.clone(), - ) - }, - ); - Rc::new(VariantExportSurface { - arm_owners, - enum_items, - }) - } else { - acc - } - }) + items + .iter() + .cloned() + .fold(empty_variant_export_surface(), |acc, item| { + if item.connective.clone() == Connective::Disj { + let enum_items = v1_rt::rc_map_insert( + acc.enum_items.clone(), + authored_name_at(source_indices.clone(), item.clone()), + item.clone(), + ); + let arm_owners = item.children.clone().iter().cloned().fold( + acc.arm_owners.clone(), + |arms: Rc>>, child: Rc| { + v1_rt::rc_map_insert( + arms, + authored_name_at(source_indices.clone(), child), + item.clone(), + ) + }, + ); + Rc::new(VariantExportSurface { + arm_owners, + enum_items, + }) + } else { + acc + } + }) } fn reexport_variant_surface_fragment( @@ -13635,7 +13638,11 @@ fn reexport_variant_surface_fragment( .fold(empty_variant_export_surface(), |acc, name| { let with_arm = match v1_rt::map_get(&proxy.arm_owners, name.clone()) { Some(owner) => Rc::new(VariantExportSurface { - arm_owners: v1_rt::rc_map_insert(acc.arm_owners.clone(), name.clone(), owner), + arm_owners: v1_rt::rc_map_insert( + acc.arm_owners.clone(), + name.clone(), + owner, + ), enum_items: acc.enum_items.clone(), }), None => acc.clone(), @@ -13662,7 +13669,8 @@ pub fn build_variant_export_surface( .cloned() .filter(|imp| !import_is_all(imp.clone())) .fold(empty_variant_export_surface(), |acc, imp| { - let frag = reexport_variant_surface_fragment(imp, surfaces.clone(), source_indices.clone()); + let frag = + reexport_variant_surface_fragment(imp, surfaces.clone(), source_indices.clone()); Rc::new(VariantExportSurface { arm_owners: v1_rt::rc_map_merge(acc.arm_owners.clone(), frag.arm_owners.clone()), enum_items: v1_rt::rc_map_merge(acc.enum_items.clone(), frag.enum_items.clone()), @@ -14719,8 +14727,7 @@ pub fn typecheck_modules( ); let typed = tc_result.typed.clone(); let tc_diags = tc_result.diagnostics.clone(); - let typed_path = - authored_name_at(source_indices.clone(), typed.module.clone()); + let typed_path = authored_name_at(source_indices.clone(), typed.module.clone()); variant_surfaces = v1_rt::rc_map_insert( variant_surfaces.clone(), typed_path.clone(), From 2fc2d89ed5c30a80b77f4b7eff3a6a6acebbdf61 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Sat, 4 Jul 2026 23:41:24 +0000 Subject: [PATCH 04/32] fix: extract dag helpers so 04_infer folds parse under strict grammar The v1 parser rejects multiple let-bindings inside fold/if branches; hoist variant-surface and import-binding steps into named functions so typecheck_parses_strict passes. Co-authored-by: Cursor --- src/v1/04_infer.dag | 73 ++++++++++++++++++++++++++++----------------- 1 file changed, 46 insertions(+), 27 deletions(-) diff --git a/src/v1/04_infer.dag b/src/v1/04_infer.dag index 9eb53988149..992785b45b0 100644 --- a/src/v1/04_infer.dag +++ b/src/v1/04_infer.dag @@ -6044,47 +6044,62 @@ fn empty_variant_export_surface() -> VariantExportSurface { VariantExportSurface { arm_owners: empty_map(), enum_items: empty_map() } } +fn add_coproduct_to_variant_surface(acc: VariantExportSurface, item: Node, source_indices: Map) -> VariantExportSurface { + let enum_items = map_insert(acc.enum_items, authored_name_at(source_indices: source_indices, node: item), item) + let arm_owners = fold(item.children, init: acc.arm_owners, f: (arms, child) => + map_insert(arms, authored_name_at(source_indices: source_indices, node: child), item) + ) + VariantExportSurface { arm_owners: arm_owners, enum_items: enum_items } +} + fn collect_own_variant_export_surface(items: List, source_indices: Map) -> VariantExportSurface { fold(items, init: empty_variant_export_surface(), f: (acc, item) => - if item.connective == Disj { - let enum_items = map_insert(acc.enum_items, authored_name_at(source_indices: source_indices, node: item), item) - let arm_owners = fold(item.children, init: acc.arm_owners, f: (arms, child) => - map_insert(arms, authored_name_at(source_indices: source_indices, node: child), item) - ) - VariantExportSurface { arm_owners: arm_owners, enum_items: enum_items } + let item_is_coproduct = item.connective == Disj + if item_is_coproduct { + add_coproduct_to_variant_surface(acc: acc, item: item, source_indices: source_indices) } else { acc } ) } +fn merge_name_from_proxy_surface(acc: VariantExportSurface, proxy: VariantExportSurface, name: String) -> VariantExportSurface { + let with_arm = match map_get(proxy.arm_owners, name) { + Present { value: owner } => VariantExportSurface { arm_owners: map_insert(acc.arm_owners, name, owner), enum_items: acc.enum_items } + Absent => acc + } + match map_get(proxy.enum_items, name) { + Present { value: item } => VariantExportSurface { arm_owners: with_arm.arm_owners, enum_items: map_insert(with_arm.enum_items, name, item) } + Absent => with_arm + } +} + fn reexport_variant_surface_fragment(imp: Node, surfaces: Map, source_indices: Map) -> VariantExportSurface { let proxy_path = import_module_path_at(imp: imp, source_indices: source_indices) match map_get(surfaces, proxy_path) { Absent => empty_variant_export_surface() Present { value: proxy } => fold(import_specific_names_at(n: imp, source_indices: source_indices), init: empty_variant_export_surface(), f: (acc, name) => - let with_arm = match map_get(proxy.arm_owners, name) { - Present { value: owner } => VariantExportSurface { arm_owners: map_insert(acc.arm_owners, name, owner), enum_items: acc.enum_items } - Absent => acc - } - match map_get(proxy.enum_items, name) { - Present { value: item } => VariantExportSurface { arm_owners: with_arm.arm_owners, enum_items: map_insert(with_arm.enum_items, name, item) } - Absent => with_arm - } + merge_name_from_proxy_surface(acc: acc, proxy: proxy, name: name) ) } } +fn merge_variant_surface_fragments(acc: VariantExportSurface, frag: VariantExportSurface) -> VariantExportSurface { + VariantExportSurface { + arm_owners: map_merge(acc.arm_owners, frag.arm_owners), + enum_items: map_merge(acc.enum_items, frag.enum_items) + } +} + fn build_variant_export_surface(tm: TypedModule, surfaces: Map, source_indices: Map) -> VariantExportSurface { let own = collect_own_variant_export_surface(items: tm.items, source_indices: source_indices) let reexports = fold( module_imports(n: tm.module) |> filter(imp => !import_is_all(n: imp)), init: empty_variant_export_surface(), f: (acc, imp) => - let frag = reexport_variant_surface_fragment(imp: imp, surfaces: surfaces, source_indices: source_indices) - VariantExportSurface { - arm_owners: map_merge(acc.arm_owners, frag.arm_owners), - enum_items: map_merge(acc.enum_items, frag.enum_items) - } + merge_variant_surface_fragments( + acc: acc, + frag: reexport_variant_surface_fragment(imp: imp, surfaces: surfaces, source_indices: source_indices) + ) ) VariantExportSurface { arm_owners: map_merge(reexports.arm_owners, own.arm_owners), @@ -6092,6 +6107,17 @@ fn build_variant_export_surface(tm: TypedModule, surfaces: Map, module_name: String) -> VariantFoldState { + let after_enum = match map_get(surface.enum_items, name) { + Present { value: item } => bind_coproduct_item_arms(state: state, item: item, source_indices: source_indices, module_name: module_name) + Absent => state + } + match map_get(surface.arm_owners, name) { + Present { value: owner } => insert_variant_owner_checked(state: after_enum, arm_name: name, owner: owner, source_indices: source_indices, module_name: module_name) + Absent => after_enum + } +} + fn build_imported_variants(resolved_imports: List, parent_index: Map, variant_surfaces: Map, source_indices: Map, module_name: String, init: VariantFoldState) -> VariantFoldState { fold(resolved_imports, init: init, f: (acc, imp) => let with_glob = if imp.is_all { @@ -6111,14 +6137,7 @@ fn build_imported_variants(resolved_imports: List, parent_index: Present { value: s } => s Absent => empty_variant_export_surface() } - let after_enum = match map_get(surface.enum_items, name) { - Present { value: item } => bind_coproduct_item_arms(state: nacc, item: item, source_indices: source_indices, module_name: module_name) - Absent => nacc - } - match map_get(surface.arm_owners, name) { - Present { value: owner } => insert_variant_owner_checked(state: after_enum, arm_name: name, owner: owner, source_indices: source_indices, module_name: module_name) - Absent => after_enum - } + bind_imported_name_from_surface(state: nacc, surface: surface, name: name, source_indices: source_indices, module_name: module_name) ) ) } From 76774e4a50c1a18d47529690f4c08b436f48f834 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Sat, 4 Jul 2026 23:58:17 +0000 Subject: [PATCH 05/32] WIP: P3+P5: infer whole-corpus scans to per-module maps --- src/v1/stage0/src/v1_compiler_compiler_tests_rust.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 b812888d2d5..812d4302504 100644 --- a/src/v1/stage0/src/v1_compiler_compiler_tests_rust.rs +++ b/src/v1/stage0/src/v1_compiler_compiler_tests_rust.rs @@ -283,7 +283,7 @@ pub fn ct_profile_full_pipeline_test() -> String { } pub fn ct_profile_reconcile_test() -> 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(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(" #[test]\n".to_string(), " #[ignore]\n".to_string()), " fn profile_reconcile_per_module() {\n".to_string()), " let result = std::thread::Builder::new()\n".to_string()), " .stack_size(64 * 1024 * 1024)\n".to_string()), " .spawn(|| {\n".to_string()), " use std::collections::HashMap;\n".to_string()), " use std::time::Instant;\n\n".to_string()), " let sources = self_compile_sources();\n\n".to_string()), " eprintln!(\n".to_string()), " \"\\n=== PER-MODULE RECONCILE PROFILE ({} sources) ===\",\n".to_string()), " sources.len()\n".to_string()), " );\n\n".to_string()), " let t0 = Instant::now();\n".to_string()), " let mut modules = Vec::new();\n".to_string()), " let mut intern_table = crate::v1_std_core::empty_intern_table();\n".to_string()), " for source in &sources {\n".to_string()), " let tokens = crate::v1_compiler_tokenize::tokenize(\n".to_string()), " source.content.clone(),\n".to_string()), " source.path.clone(),\n".to_string()), " );\n".to_string()), " let si = crate::v1_std_core::build_newline_index(\n".to_string()), " source.path.clone(),\n".to_string()), " source.content.clone(),\n".to_string()), " );\n".to_string()), " let parsed = crate::v1_compiler_parse::parse_with_table(\n".to_string()), " tokens.clone(),\n".to_string()), " crate::v1_rt::rc_map_insert(\n".to_string()), " crate::v1_rt::rc_empty_map::>(),\n".to_string()), " si.file.clone(),\n".to_string()), " si.clone(),\n".to_string()), " ),\n".to_string()), " intern_table.clone(),\n".to_string()), " );\n".to_string()), " let result = parsed.result.clone();\n".to_string()), " intern_table = parsed.intern_table.clone();\n".to_string()), " let m = result.module.clone()\n".to_string()), " .unwrap_or_else(|| panic!(\"parse failed for source {}\", source.path));\n".to_string()), " modules.push(m);\n".to_string()), " }\n".to_string()), " let resolve_si = sources.iter().fold(\n".to_string()), " crate::v1_rt::rc_empty_map::>(),\n".to_string()), " |acc, s| crate::v1_rt::rc_map_insert(acc, s.path.clone(), crate::v1_std_core::build_newline_index(s.path.clone(), s.content.clone())),\n".to_string()), " );\n".to_string()), " let graph = crate::v1_compiler_resolve::resolve_modules(std::rc::Rc::new(modules), resolve_si);\n".to_string()), " let setup_time = t0.elapsed();\n".to_string()), " let rss_baseline = get_rss_bytes();\n".to_string()), " eprintln!(\n".to_string()), " \" Setup (tok+parse+resolve): {:?} | RSS: {}\",\n".to_string()), " setup_time,\n".to_string()), " format_bytes(rss_baseline)\n".to_string()), " );\n".to_string()), " eprintln!(\" Modules to reconcile: {}\\n\", graph.modules.len());\n\n".to_string()), " let mut mi_raw = HashMap::<\n".to_string()), " String,\n".to_string()), " std::rc::Rc,\n".to_string()), " >::new();\n".to_string()), " let source_indices = std::rc::Rc::new(sources.iter().fold(\n".to_string()), " HashMap::>::new(),\n".to_string()), " |mut acc, source| {\n".to_string()), " acc.insert(\n".to_string()), " source.path.clone(),\n".to_string()), " crate::v1_std_core::build_newline_index(\n".to_string()), " source.path.clone(),\n".to_string()), " source.content.clone(),\n".to_string()), " ),\n".to_string()), " );\n".to_string()), " acc\n".to_string()), " },\n".to_string()), " ));\n\n".to_string()), " for resolved in graph.modules.iter() {\n".to_string()), " let name = resolved.module.name.to_string();\n".to_string()), " let item_count =\n".to_string()), " crate::v1_std_core::module_items(resolved.module.clone()).len();\n".to_string()), " let rss_before = get_rss_bytes();\n\n".to_string()), " eprint!(\" {:>35} ({:>3} items) ... \", name, item_count);\n\n".to_string()), " let module_index = std::rc::Rc::new(mi_raw.clone());\n\n".to_string()), " let t_unres = Instant::now();\n".to_string()), " let _unres = crate::v1_compiler_infer::build_type_env_unresolved(\n".to_string()), " resolved.clone(),\n".to_string()), " module_index.clone(),\n".to_string()), " source_indices.clone(),\n".to_string()), " intern_table.clone(),\n".to_string()), " );\n".to_string()), " let unres_elapsed = t_unres.elapsed();\n".to_string()), " let rss_after_unres = get_rss_bytes();\n".to_string()), " let unres_delta = rss_after_unres.saturating_sub(rss_before);\n\n".to_string()), " eprint!(\n".to_string()), " \"cycles={:>8.2?}(+{}) \",\n".to_string()), " unres_elapsed,\n".to_string()), " format_bytes(unres_delta)\n".to_string()), " );\n\n".to_string()), " if unres_delta > 256 * 1024 * 1024 {\n".to_string()), " eprintln!(\"\");\n".to_string()), " panic!(\n".to_string()), " \"ABORT: '{}' cycle detection grew RSS by {}\",\n".to_string()), " name,\n".to_string()), " format_bytes(unres_delta)\n".to_string()), " );\n".to_string()), " }\n\n".to_string()), " let t_env = Instant::now();\n".to_string()), " let env_result = crate::v1_compiler_infer::build_type_env(\n".to_string()), " resolved.clone(),\n".to_string()), " module_index.clone(),\n".to_string()), " source_indices.clone(),\n".to_string()), " intern_table.clone(),\n".to_string()), " );\n".to_string()), " let env_elapsed = t_env.elapsed();\n".to_string()), " let rss_after_env = get_rss_bytes();\n".to_string()), " let env_delta = rss_after_env.saturating_sub(rss_before);\n".to_string()), " let env_errs: usize = env_result\n".to_string()), " .diagnostics\n".to_string()), " .iter()\n".to_string()), " .filter(|d| crate::v1_std_core::is_error_diagnostic(d.diagnostic.clone()))\n".to_string()), " .count();\n\n".to_string()), " eprint!(\n".to_string()), " \"env={:>8.2?}(+{},e={}) \",\n".to_string()), " env_elapsed,\n".to_string()), " format_bytes(env_delta),\n".to_string()), " env_errs\n".to_string()), " );\n\n".to_string()), " if env_delta > 512 * 1024 * 1024 {\n".to_string()), " eprintln!(\"\");\n".to_string()), " panic!(\n".to_string()), " \"ABORT: '{}' build_type_env grew RSS by {}\",\n".to_string()), " name,\n".to_string()), " format_bytes(env_delta)\n".to_string()), " );\n".to_string()), " }\n".to_string()), " if env_elapsed.as_secs() > 10 {\n".to_string()), " eprintln!(\"\");\n".to_string()), " panic!(\"ABORT: '{}' build_type_env took {:?}\", name, env_elapsed);\n".to_string()), " }\n\n".to_string()), " let t_full = Instant::now();\n".to_string()), " let tc_result = crate::v1_compiler_infer::typecheck_module(\n".to_string()), " resolved.clone(),\n".to_string()), " module_index.clone(),\n".to_string()), " source_indices.clone(),\n".to_string()), " intern_table.clone(),\n".to_string()), " );\n".to_string()), " let full_elapsed = t_full.elapsed();\n".to_string()), " let rss_after = get_rss_bytes();\n".to_string()), " let delta = rss_after.saturating_sub(rss_before);\n".to_string()), " let diag_count: usize = tc_result\n".to_string()), " .diagnostics\n".to_string()), " .iter()\n".to_string()), " .filter(|d| crate::v1_std_core::is_error_diagnostic(d.diagnostic.clone()))\n".to_string()), " .count();\n\n".to_string()), " eprintln!(\n".to_string()), " \"full={:>8.2?} | RSS: {} (+{}) | errs: {}\",\n".to_string()), " full_elapsed,\n".to_string()), " format_bytes(rss_after),\n".to_string()), " format_bytes(delta),\n".to_string()), " diag_count\n".to_string()), " );\n\n".to_string()), " if delta > 512 * 1024 * 1024 {\n".to_string()), " panic!(\n".to_string()), " \"ABORT: '{}' grew RSS by {} (>512MB)\",\n".to_string()), " name,\n".to_string()), " format_bytes(delta)\n".to_string()), " );\n".to_string()), " }\n".to_string()), " if full_elapsed.as_secs() > 10 {\n".to_string()), " panic!(\"ABORT: '{}' took {:?} (>10s)\", name, full_elapsed);\n".to_string()), " }\n\n".to_string()), " let typed = tc_result.typed.clone();\n".to_string()), " mi_raw.insert(name, typed);\n".to_string()), " }\n\n".to_string()), " let rss_final = get_rss_bytes();\n".to_string()), " eprintln!(\n".to_string()), " \"\\n RSS final: {} (from baseline: +{})\",\n".to_string()), " format_bytes(rss_final),\n".to_string()), " format_bytes(rss_final.saturating_sub(rss_baseline))\n".to_string()), " );\n".to_string()), " eprintln!(\"=== DONE ===\\n\");\n".to_string()), " })\n".to_string()), " .expect(\"failed to spawn thread\")\n".to_string()), " .join();\n".to_string()), " result.expect(\"profile_reconcile_per_module panicked\");\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(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(" #[test]\n".to_string(), " #[ignore]\n".to_string()), " fn profile_reconcile_per_module() {\n".to_string()), " let result = std::thread::Builder::new()\n".to_string()), " .stack_size(64 * 1024 * 1024)\n".to_string()), " .spawn(|| {\n".to_string()), " use std::collections::HashMap;\n".to_string()), " use std::time::Instant;\n\n".to_string()), " let sources = self_compile_sources();\n\n".to_string()), " eprintln!(\n".to_string()), " \"\\n=== PER-MODULE RECONCILE PROFILE ({} sources) ===\",\n".to_string()), " sources.len()\n".to_string()), " );\n\n".to_string()), " let t0 = Instant::now();\n".to_string()), " let mut modules = Vec::new();\n".to_string()), " let mut intern_table = crate::v1_std_core::empty_intern_table();\n".to_string()), " for source in &sources {\n".to_string()), " let tokens = crate::v1_compiler_tokenize::tokenize(\n".to_string()), " source.content.clone(),\n".to_string()), " source.path.clone(),\n".to_string()), " );\n".to_string()), " let si = crate::v1_std_core::build_newline_index(\n".to_string()), " source.path.clone(),\n".to_string()), " source.content.clone(),\n".to_string()), " );\n".to_string()), " let parsed = crate::v1_compiler_parse::parse_with_table(\n".to_string()), " tokens.clone(),\n".to_string()), " crate::v1_rt::rc_map_insert(\n".to_string()), " crate::v1_rt::rc_empty_map::>(),\n".to_string()), " si.file.clone(),\n".to_string()), " si.clone(),\n".to_string()), " ),\n".to_string()), " intern_table.clone(),\n".to_string()), " );\n".to_string()), " let result = parsed.result.clone();\n".to_string()), " intern_table = parsed.intern_table.clone();\n".to_string()), " let m = result.module.clone()\n".to_string()), " .unwrap_or_else(|| panic!(\"parse failed for source {}\", source.path));\n".to_string()), " modules.push(m);\n".to_string()), " }\n".to_string()), " let resolve_si = sources.iter().fold(\n".to_string()), " crate::v1_rt::rc_empty_map::>(),\n".to_string()), " |acc, s| crate::v1_rt::rc_map_insert(acc, s.path.clone(), crate::v1_std_core::build_newline_index(s.path.clone(), s.content.clone())),\n".to_string()), " );\n".to_string()), " let graph = crate::v1_compiler_resolve::resolve_modules(std::rc::Rc::new(modules), resolve_si);\n".to_string()), " let setup_time = t0.elapsed();\n".to_string()), " let rss_baseline = get_rss_bytes();\n".to_string()), " eprintln!(\n".to_string()), " \" Setup (tok+parse+resolve): {:?} | RSS: {}\",\n".to_string()), " setup_time,\n".to_string()), " format_bytes(rss_baseline)\n".to_string()), " );\n".to_string()), " eprintln!(\" Modules to reconcile: {}\\n\", graph.modules.len());\n\n".to_string()), " let mut mi_raw = HashMap::<\n".to_string()), " String,\n".to_string()), " std::rc::Rc,\n".to_string()), " >::new();\n".to_string()), " let source_indices = std::rc::Rc::new(sources.iter().fold(\n".to_string()), " HashMap::>::new(),\n".to_string()), " |mut acc, source| {\n".to_string()), " acc.insert(\n".to_string()), " source.path.clone(),\n".to_string()), " crate::v1_std_core::build_newline_index(\n".to_string()), " source.path.clone(),\n".to_string()), " source.content.clone(),\n".to_string()), " ),\n".to_string()), " );\n".to_string()), " acc\n".to_string()), " },\n".to_string()), " ));\n\n".to_string()), " for resolved in graph.modules.iter() {\n".to_string()), " let name = resolved.module.name.to_string();\n".to_string()), " let item_count =\n".to_string()), " crate::v1_std_core::module_items(resolved.module.clone()).len();\n".to_string()), " let rss_before = get_rss_bytes();\n\n".to_string()), " eprint!(\" {:>35} ({:>3} items) ... \", name, item_count);\n\n".to_string()), " let module_index = std::rc::Rc::new(mi_raw.clone());\n\n".to_string()), " let t_unres = Instant::now();\n".to_string()), " let _unres = crate::v1_compiler_infer::build_type_env_unresolved(\n".to_string()), " resolved.clone(),\n".to_string()), " module_index.clone(),\n".to_string()), " source_indices.clone(),\n".to_string()), " intern_table.clone(),\n".to_string()), " );\n".to_string()), " let unres_elapsed = t_unres.elapsed();\n".to_string()), " let rss_after_unres = get_rss_bytes();\n".to_string()), " let unres_delta = rss_after_unres.saturating_sub(rss_before);\n\n".to_string()), " eprint!(\n".to_string()), " \"cycles={:>8.2?}(+{}) \",\n".to_string()), " unres_elapsed,\n".to_string()), " format_bytes(unres_delta)\n".to_string()), " );\n\n".to_string()), " if unres_delta > 256 * 1024 * 1024 {\n".to_string()), " eprintln!(\"\");\n".to_string()), " panic!(\n".to_string()), " \"ABORT: '{}' cycle detection grew RSS by {}\",\n".to_string()), " name,\n".to_string()), " format_bytes(unres_delta)\n".to_string()), " );\n".to_string()), " }\n\n".to_string()), " let t_env = Instant::now();\n".to_string()), " let env_result = crate::v1_compiler_infer::build_type_env(\n".to_string()), " resolved.clone(),\n".to_string()), " module_index.clone(),\n".to_string()), " source_indices.clone(),\n".to_string()), " intern_table.clone(),\n".to_string()), " );\n".to_string()), " let env_elapsed = t_env.elapsed();\n".to_string()), " let rss_after_env = get_rss_bytes();\n".to_string()), " let env_delta = rss_after_env.saturating_sub(rss_before);\n".to_string()), " let env_errs: usize = env_result\n".to_string()), " .diagnostics\n".to_string()), " .iter()\n".to_string()), " .filter(|d| crate::v1_std_core::is_error_diagnostic(d.diagnostic.clone()))\n".to_string()), " .count();\n\n".to_string()), " eprint!(\n".to_string()), " \"env={:>8.2?}(+{},e={}) \",\n".to_string()), " env_elapsed,\n".to_string()), " format_bytes(env_delta),\n".to_string()), " env_errs\n".to_string()), " );\n\n".to_string()), " if env_delta > 512 * 1024 * 1024 {\n".to_string()), " eprintln!(\"\");\n".to_string()), " panic!(\n".to_string()), " \"ABORT: '{}' build_type_env grew RSS by {}\",\n".to_string()), " name,\n".to_string()), " format_bytes(env_delta)\n".to_string()), " );\n".to_string()), " }\n".to_string()), " if env_elapsed.as_secs() > 10 {\n".to_string()), " eprintln!(\"\");\n".to_string()), " panic!(\"ABORT: '{}' build_type_env took {:?}\", name, env_elapsed);\n".to_string()), " }\n\n".to_string()), " let t_full = Instant::now();\n".to_string()), " let tc_result = crate::v1_compiler_infer::typecheck_module(\n".to_string()), " resolved.clone(),\n".to_string()), " module_index.clone(),\n".to_string()), " std::rc::Rc::new(std::collections::HashMap::<\n".to_string()), " String,\n".to_string()), " std::rc::Rc,\n".to_string()), " >::new()),\n".to_string()), " source_indices.clone(),\n".to_string()), " intern_table.clone(),\n".to_string()), " );\n".to_string()), " let full_elapsed = t_full.elapsed();\n".to_string()), " let rss_after = get_rss_bytes();\n".to_string()), " let delta = rss_after.saturating_sub(rss_before);\n".to_string()), " let diag_count: usize = tc_result\n".to_string()), " .diagnostics\n".to_string()), " .iter()\n".to_string()), " .filter(|d| crate::v1_std_core::is_error_diagnostic(d.diagnostic.clone()))\n".to_string()), " .count();\n\n".to_string()), " eprintln!(\n".to_string()), " \"full={:>8.2?} | RSS: {} (+{}) | errs: {}\",\n".to_string()), " full_elapsed,\n".to_string()), " format_bytes(rss_after),\n".to_string()), " format_bytes(delta),\n".to_string()), " diag_count\n".to_string()), " );\n\n".to_string()), " if delta > 512 * 1024 * 1024 {\n".to_string()), " panic!(\n".to_string()), " \"ABORT: '{}' grew RSS by {} (>512MB)\",\n".to_string()), " name,\n".to_string()), " format_bytes(delta)\n".to_string()), " );\n".to_string()), " }\n".to_string()), " if full_elapsed.as_secs() > 10 {\n".to_string()), " panic!(\"ABORT: '{}' took {:?} (>10s)\", name, full_elapsed);\n".to_string()), " }\n\n".to_string()), " let typed = tc_result.typed.clone();\n".to_string()), " mi_raw.insert(name, typed);\n".to_string()), " }\n\n".to_string()), " let rss_final = get_rss_bytes();\n".to_string()), " eprintln!(\n".to_string()), " \"\\n RSS final: {} (from baseline: +{})\",\n".to_string()), " format_bytes(rss_final),\n".to_string()), " format_bytes(rss_final.saturating_sub(rss_baseline))\n".to_string()), " );\n".to_string()), " eprintln!(\"=== DONE ===\\n\");\n".to_string()), " })\n".to_string()), " .expect(\"failed to spawn thread\")\n".to_string()), " .join();\n".to_string()), " result.expect(\"profile_reconcile_per_module panicked\");\n".to_string()), " }\n\n".to_string()) } pub fn ct_module_footer() -> String { From 76bbc23172d71ccedb581e65b00cfec48ff43776 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Sun, 5 Jul 2026 00:08:00 +0000 Subject: [PATCH 06/32] fix: balance concat wrappers in compiler_tests emit artifact Commit 1f02caa added variant_surfaces strings to ct_profile_reconcile_test without four matching v1_rt::concat( openers, breaking rustc parse. Co-authored-by: Cursor --- src/v1/stage0/src/v1_compiler_compiler_tests_rust.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 812d4302504..02b38ea15d1 100644 --- a/src/v1/stage0/src/v1_compiler_compiler_tests_rust.rs +++ b/src/v1/stage0/src/v1_compiler_compiler_tests_rust.rs @@ -283,7 +283,7 @@ pub fn ct_profile_full_pipeline_test() -> String { } pub fn ct_profile_reconcile_test() -> 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(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(" #[test]\n".to_string(), " #[ignore]\n".to_string()), " fn profile_reconcile_per_module() {\n".to_string()), " let result = std::thread::Builder::new()\n".to_string()), " .stack_size(64 * 1024 * 1024)\n".to_string()), " .spawn(|| {\n".to_string()), " use std::collections::HashMap;\n".to_string()), " use std::time::Instant;\n\n".to_string()), " let sources = self_compile_sources();\n\n".to_string()), " eprintln!(\n".to_string()), " \"\\n=== PER-MODULE RECONCILE PROFILE ({} sources) ===\",\n".to_string()), " sources.len()\n".to_string()), " );\n\n".to_string()), " let t0 = Instant::now();\n".to_string()), " let mut modules = Vec::new();\n".to_string()), " let mut intern_table = crate::v1_std_core::empty_intern_table();\n".to_string()), " for source in &sources {\n".to_string()), " let tokens = crate::v1_compiler_tokenize::tokenize(\n".to_string()), " source.content.clone(),\n".to_string()), " source.path.clone(),\n".to_string()), " );\n".to_string()), " let si = crate::v1_std_core::build_newline_index(\n".to_string()), " source.path.clone(),\n".to_string()), " source.content.clone(),\n".to_string()), " );\n".to_string()), " let parsed = crate::v1_compiler_parse::parse_with_table(\n".to_string()), " tokens.clone(),\n".to_string()), " crate::v1_rt::rc_map_insert(\n".to_string()), " crate::v1_rt::rc_empty_map::>(),\n".to_string()), " si.file.clone(),\n".to_string()), " si.clone(),\n".to_string()), " ),\n".to_string()), " intern_table.clone(),\n".to_string()), " );\n".to_string()), " let result = parsed.result.clone();\n".to_string()), " intern_table = parsed.intern_table.clone();\n".to_string()), " let m = result.module.clone()\n".to_string()), " .unwrap_or_else(|| panic!(\"parse failed for source {}\", source.path));\n".to_string()), " modules.push(m);\n".to_string()), " }\n".to_string()), " let resolve_si = sources.iter().fold(\n".to_string()), " crate::v1_rt::rc_empty_map::>(),\n".to_string()), " |acc, s| crate::v1_rt::rc_map_insert(acc, s.path.clone(), crate::v1_std_core::build_newline_index(s.path.clone(), s.content.clone())),\n".to_string()), " );\n".to_string()), " let graph = crate::v1_compiler_resolve::resolve_modules(std::rc::Rc::new(modules), resolve_si);\n".to_string()), " let setup_time = t0.elapsed();\n".to_string()), " let rss_baseline = get_rss_bytes();\n".to_string()), " eprintln!(\n".to_string()), " \" Setup (tok+parse+resolve): {:?} | RSS: {}\",\n".to_string()), " setup_time,\n".to_string()), " format_bytes(rss_baseline)\n".to_string()), " );\n".to_string()), " eprintln!(\" Modules to reconcile: {}\\n\", graph.modules.len());\n\n".to_string()), " let mut mi_raw = HashMap::<\n".to_string()), " String,\n".to_string()), " std::rc::Rc,\n".to_string()), " >::new();\n".to_string()), " let source_indices = std::rc::Rc::new(sources.iter().fold(\n".to_string()), " HashMap::>::new(),\n".to_string()), " |mut acc, source| {\n".to_string()), " acc.insert(\n".to_string()), " source.path.clone(),\n".to_string()), " crate::v1_std_core::build_newline_index(\n".to_string()), " source.path.clone(),\n".to_string()), " source.content.clone(),\n".to_string()), " ),\n".to_string()), " );\n".to_string()), " acc\n".to_string()), " },\n".to_string()), " ));\n\n".to_string()), " for resolved in graph.modules.iter() {\n".to_string()), " let name = resolved.module.name.to_string();\n".to_string()), " let item_count =\n".to_string()), " crate::v1_std_core::module_items(resolved.module.clone()).len();\n".to_string()), " let rss_before = get_rss_bytes();\n\n".to_string()), " eprint!(\" {:>35} ({:>3} items) ... \", name, item_count);\n\n".to_string()), " let module_index = std::rc::Rc::new(mi_raw.clone());\n\n".to_string()), " let t_unres = Instant::now();\n".to_string()), " let _unres = crate::v1_compiler_infer::build_type_env_unresolved(\n".to_string()), " resolved.clone(),\n".to_string()), " module_index.clone(),\n".to_string()), " source_indices.clone(),\n".to_string()), " intern_table.clone(),\n".to_string()), " );\n".to_string()), " let unres_elapsed = t_unres.elapsed();\n".to_string()), " let rss_after_unres = get_rss_bytes();\n".to_string()), " let unres_delta = rss_after_unres.saturating_sub(rss_before);\n\n".to_string()), " eprint!(\n".to_string()), " \"cycles={:>8.2?}(+{}) \",\n".to_string()), " unres_elapsed,\n".to_string()), " format_bytes(unres_delta)\n".to_string()), " );\n\n".to_string()), " if unres_delta > 256 * 1024 * 1024 {\n".to_string()), " eprintln!(\"\");\n".to_string()), " panic!(\n".to_string()), " \"ABORT: '{}' cycle detection grew RSS by {}\",\n".to_string()), " name,\n".to_string()), " format_bytes(unres_delta)\n".to_string()), " );\n".to_string()), " }\n\n".to_string()), " let t_env = Instant::now();\n".to_string()), " let env_result = crate::v1_compiler_infer::build_type_env(\n".to_string()), " resolved.clone(),\n".to_string()), " module_index.clone(),\n".to_string()), " source_indices.clone(),\n".to_string()), " intern_table.clone(),\n".to_string()), " );\n".to_string()), " let env_elapsed = t_env.elapsed();\n".to_string()), " let rss_after_env = get_rss_bytes();\n".to_string()), " let env_delta = rss_after_env.saturating_sub(rss_before);\n".to_string()), " let env_errs: usize = env_result\n".to_string()), " .diagnostics\n".to_string()), " .iter()\n".to_string()), " .filter(|d| crate::v1_std_core::is_error_diagnostic(d.diagnostic.clone()))\n".to_string()), " .count();\n\n".to_string()), " eprint!(\n".to_string()), " \"env={:>8.2?}(+{},e={}) \",\n".to_string()), " env_elapsed,\n".to_string()), " format_bytes(env_delta),\n".to_string()), " env_errs\n".to_string()), " );\n\n".to_string()), " if env_delta > 512 * 1024 * 1024 {\n".to_string()), " eprintln!(\"\");\n".to_string()), " panic!(\n".to_string()), " \"ABORT: '{}' build_type_env grew RSS by {}\",\n".to_string()), " name,\n".to_string()), " format_bytes(env_delta)\n".to_string()), " );\n".to_string()), " }\n".to_string()), " if env_elapsed.as_secs() > 10 {\n".to_string()), " eprintln!(\"\");\n".to_string()), " panic!(\"ABORT: '{}' build_type_env took {:?}\", name, env_elapsed);\n".to_string()), " }\n\n".to_string()), " let t_full = Instant::now();\n".to_string()), " let tc_result = crate::v1_compiler_infer::typecheck_module(\n".to_string()), " resolved.clone(),\n".to_string()), " module_index.clone(),\n".to_string()), " std::rc::Rc::new(std::collections::HashMap::<\n".to_string()), " String,\n".to_string()), " std::rc::Rc,\n".to_string()), " >::new()),\n".to_string()), " source_indices.clone(),\n".to_string()), " intern_table.clone(),\n".to_string()), " );\n".to_string()), " let full_elapsed = t_full.elapsed();\n".to_string()), " let rss_after = get_rss_bytes();\n".to_string()), " let delta = rss_after.saturating_sub(rss_before);\n".to_string()), " let diag_count: usize = tc_result\n".to_string()), " .diagnostics\n".to_string()), " .iter()\n".to_string()), " .filter(|d| crate::v1_std_core::is_error_diagnostic(d.diagnostic.clone()))\n".to_string()), " .count();\n\n".to_string()), " eprintln!(\n".to_string()), " \"full={:>8.2?} | RSS: {} (+{}) | errs: {}\",\n".to_string()), " full_elapsed,\n".to_string()), " format_bytes(rss_after),\n".to_string()), " format_bytes(delta),\n".to_string()), " diag_count\n".to_string()), " );\n\n".to_string()), " if delta > 512 * 1024 * 1024 {\n".to_string()), " panic!(\n".to_string()), " \"ABORT: '{}' grew RSS by {} (>512MB)\",\n".to_string()), " name,\n".to_string()), " format_bytes(delta)\n".to_string()), " );\n".to_string()), " }\n".to_string()), " if full_elapsed.as_secs() > 10 {\n".to_string()), " panic!(\"ABORT: '{}' took {:?} (>10s)\", name, full_elapsed);\n".to_string()), " }\n\n".to_string()), " let typed = tc_result.typed.clone();\n".to_string()), " mi_raw.insert(name, typed);\n".to_string()), " }\n\n".to_string()), " let rss_final = get_rss_bytes();\n".to_string()), " eprintln!(\n".to_string()), " \"\\n RSS final: {} (from baseline: +{})\",\n".to_string()), " format_bytes(rss_final),\n".to_string()), " format_bytes(rss_final.saturating_sub(rss_baseline))\n".to_string()), " );\n".to_string()), " eprintln!(\"=== DONE ===\\n\");\n".to_string()), " })\n".to_string()), " .expect(\"failed to spawn thread\")\n".to_string()), " .join();\n".to_string()), " result.expect(\"profile_reconcile_per_module panicked\");\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(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(" #[test]\n".to_string(), " #[ignore]\n".to_string()), " fn profile_reconcile_per_module() {\n".to_string()), " let result = std::thread::Builder::new()\n".to_string()), " .stack_size(64 * 1024 * 1024)\n".to_string()), " .spawn(|| {\n".to_string()), " use std::collections::HashMap;\n".to_string()), " use std::time::Instant;\n\n".to_string()), " let sources = self_compile_sources();\n\n".to_string()), " eprintln!(\n".to_string()), " \"\\n=== PER-MODULE RECONCILE PROFILE ({} sources) ===\",\n".to_string()), " sources.len()\n".to_string()), " );\n\n".to_string()), " let t0 = Instant::now();\n".to_string()), " let mut modules = Vec::new();\n".to_string()), " let mut intern_table = crate::v1_std_core::empty_intern_table();\n".to_string()), " for source in &sources {\n".to_string()), " let tokens = crate::v1_compiler_tokenize::tokenize(\n".to_string()), " source.content.clone(),\n".to_string()), " source.path.clone(),\n".to_string()), " );\n".to_string()), " let si = crate::v1_std_core::build_newline_index(\n".to_string()), " source.path.clone(),\n".to_string()), " source.content.clone(),\n".to_string()), " );\n".to_string()), " let parsed = crate::v1_compiler_parse::parse_with_table(\n".to_string()), " tokens.clone(),\n".to_string()), " crate::v1_rt::rc_map_insert(\n".to_string()), " crate::v1_rt::rc_empty_map::>(),\n".to_string()), " si.file.clone(),\n".to_string()), " si.clone(),\n".to_string()), " ),\n".to_string()), " intern_table.clone(),\n".to_string()), " );\n".to_string()), " let result = parsed.result.clone();\n".to_string()), " intern_table = parsed.intern_table.clone();\n".to_string()), " let m = result.module.clone()\n".to_string()), " .unwrap_or_else(|| panic!(\"parse failed for source {}\", source.path));\n".to_string()), " modules.push(m);\n".to_string()), " }\n".to_string()), " let resolve_si = sources.iter().fold(\n".to_string()), " crate::v1_rt::rc_empty_map::>(),\n".to_string()), " |acc, s| crate::v1_rt::rc_map_insert(acc, s.path.clone(), crate::v1_std_core::build_newline_index(s.path.clone(), s.content.clone())),\n".to_string()), " );\n".to_string()), " let graph = crate::v1_compiler_resolve::resolve_modules(std::rc::Rc::new(modules), resolve_si);\n".to_string()), " let setup_time = t0.elapsed();\n".to_string()), " let rss_baseline = get_rss_bytes();\n".to_string()), " eprintln!(\n".to_string()), " \" Setup (tok+parse+resolve): {:?} | RSS: {}\",\n".to_string()), " setup_time,\n".to_string()), " format_bytes(rss_baseline)\n".to_string()), " );\n".to_string()), " eprintln!(\" Modules to reconcile: {}\\n\", graph.modules.len());\n\n".to_string()), " let mut mi_raw = HashMap::<\n".to_string()), " String,\n".to_string()), " std::rc::Rc,\n".to_string()), " >::new();\n".to_string()), " let source_indices = std::rc::Rc::new(sources.iter().fold(\n".to_string()), " HashMap::>::new(),\n".to_string()), " |mut acc, source| {\n".to_string()), " acc.insert(\n".to_string()), " source.path.clone(),\n".to_string()), " crate::v1_std_core::build_newline_index(\n".to_string()), " source.path.clone(),\n".to_string()), " source.content.clone(),\n".to_string()), " ),\n".to_string()), " );\n".to_string()), " acc\n".to_string()), " },\n".to_string()), " ));\n\n".to_string()), " for resolved in graph.modules.iter() {\n".to_string()), " let name = resolved.module.name.to_string();\n".to_string()), " let item_count =\n".to_string()), " crate::v1_std_core::module_items(resolved.module.clone()).len();\n".to_string()), " let rss_before = get_rss_bytes();\n\n".to_string()), " eprint!(\" {:>35} ({:>3} items) ... \", name, item_count);\n\n".to_string()), " let module_index = std::rc::Rc::new(mi_raw.clone());\n\n".to_string()), " let t_unres = Instant::now();\n".to_string()), " let _unres = crate::v1_compiler_infer::build_type_env_unresolved(\n".to_string()), " resolved.clone(),\n".to_string()), " module_index.clone(),\n".to_string()), " source_indices.clone(),\n".to_string()), " intern_table.clone(),\n".to_string()), " );\n".to_string()), " let unres_elapsed = t_unres.elapsed();\n".to_string()), " let rss_after_unres = get_rss_bytes();\n".to_string()), " let unres_delta = rss_after_unres.saturating_sub(rss_before);\n\n".to_string()), " eprint!(\n".to_string()), " \"cycles={:>8.2?}(+{}) \",\n".to_string()), " unres_elapsed,\n".to_string()), " format_bytes(unres_delta)\n".to_string()), " );\n\n".to_string()), " if unres_delta > 256 * 1024 * 1024 {\n".to_string()), " eprintln!(\"\");\n".to_string()), " panic!(\n".to_string()), " \"ABORT: '{}' cycle detection grew RSS by {}\",\n".to_string()), " name,\n".to_string()), " format_bytes(unres_delta)\n".to_string()), " );\n".to_string()), " }\n\n".to_string()), " let t_env = Instant::now();\n".to_string()), " let env_result = crate::v1_compiler_infer::build_type_env(\n".to_string()), " resolved.clone(),\n".to_string()), " module_index.clone(),\n".to_string()), " source_indices.clone(),\n".to_string()), " intern_table.clone(),\n".to_string()), " );\n".to_string()), " let env_elapsed = t_env.elapsed();\n".to_string()), " let rss_after_env = get_rss_bytes();\n".to_string()), " let env_delta = rss_after_env.saturating_sub(rss_before);\n".to_string()), " let env_errs: usize = env_result\n".to_string()), " .diagnostics\n".to_string()), " .iter()\n".to_string()), " .filter(|d| crate::v1_std_core::is_error_diagnostic(d.diagnostic.clone()))\n".to_string()), " .count();\n\n".to_string()), " eprint!(\n".to_string()), " \"env={:>8.2?}(+{},e={}) \",\n".to_string()), " env_elapsed,\n".to_string()), " format_bytes(env_delta),\n".to_string()), " env_errs\n".to_string()), " );\n\n".to_string()), " if env_delta > 512 * 1024 * 1024 {\n".to_string()), " eprintln!(\"\");\n".to_string()), " panic!(\n".to_string()), " \"ABORT: '{}' build_type_env grew RSS by {}\",\n".to_string()), " name,\n".to_string()), " format_bytes(env_delta)\n".to_string()), " );\n".to_string()), " }\n".to_string()), " if env_elapsed.as_secs() > 10 {\n".to_string()), " eprintln!(\"\");\n".to_string()), " panic!(\"ABORT: '{}' build_type_env took {:?}\", name, env_elapsed);\n".to_string()), " }\n\n".to_string()), " let t_full = Instant::now();\n".to_string()), " let tc_result = crate::v1_compiler_infer::typecheck_module(\n".to_string()), " resolved.clone(),\n".to_string()), " module_index.clone(),\n".to_string()), " std::rc::Rc::new(std::collections::HashMap::<\n".to_string()), " String,\n".to_string()), " std::rc::Rc,\n".to_string()), " >::new()),\n".to_string()), " source_indices.clone(),\n".to_string()), " intern_table.clone(),\n".to_string()), " );\n".to_string()), " let full_elapsed = t_full.elapsed();\n".to_string()), " let rss_after = get_rss_bytes();\n".to_string()), " let delta = rss_after.saturating_sub(rss_before);\n".to_string()), " let diag_count: usize = tc_result\n".to_string()), " .diagnostics\n".to_string()), " .iter()\n".to_string()), " .filter(|d| crate::v1_std_core::is_error_diagnostic(d.diagnostic.clone()))\n".to_string()), " .count();\n\n".to_string()), " eprintln!(\n".to_string()), " \"full={:>8.2?} | RSS: {} (+{}) | errs: {}\",\n".to_string()), " full_elapsed,\n".to_string()), " format_bytes(rss_after),\n".to_string()), " format_bytes(delta),\n".to_string()), " diag_count\n".to_string()), " );\n\n".to_string()), " if delta > 512 * 1024 * 1024 {\n".to_string()), " panic!(\n".to_string()), " \"ABORT: '{}' grew RSS by {} (>512MB)\",\n".to_string()), " name,\n".to_string()), " format_bytes(delta)\n".to_string()), " );\n".to_string()), " }\n".to_string()), " if full_elapsed.as_secs() > 10 {\n".to_string()), " panic!(\"ABORT: '{}' took {:?} (>10s)\", name, full_elapsed);\n".to_string()), " }\n\n".to_string()), " let typed = tc_result.typed.clone();\n".to_string()), " mi_raw.insert(name, typed);\n".to_string()), " }\n\n".to_string()), " let rss_final = get_rss_bytes();\n".to_string()), " eprintln!(\n".to_string()), " \"\\n RSS final: {} (from baseline: +{})\",\n".to_string()), " format_bytes(rss_final),\n".to_string()), " format_bytes(rss_final.saturating_sub(rss_baseline))\n".to_string()), " );\n".to_string()), " eprintln!(\"=== DONE ===\\n\");\n".to_string()), " })\n".to_string()), " .expect(\"failed to spawn thread\")\n".to_string()), " .join();\n".to_string()), " result.expect(\"profile_reconcile_per_module panicked\");\n".to_string()), " }\n\n".to_string()) } pub fn ct_module_footer() -> String { From 06c7e23015c8461d3925ac6a8126a33041cfb5e3 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Sun, 5 Jul 2026 00:53:47 +0000 Subject: [PATCH 07/32] WIP: P3+P5: infer whole-corpus scans to per-module maps --- src/v1/04_infer.dag | 4 ++++ src/v1/compiler_tests_rust.dag | 6 +----- src/v1/stage0/src/compiler_tests.rs | 6 +----- src/v1/stage0/src/v1_compiler_compiler_tests_rust.rs | 2 +- 4 files changed, 7 insertions(+), 11 deletions(-) diff --git a/src/v1/04_infer.dag b/src/v1/04_infer.dag index 992785b45b0..fefa1055581 100644 --- a/src/v1/04_infer.dag +++ b/src/v1/04_infer.dag @@ -6313,6 +6313,10 @@ fn typecheck_module(resolved: ResolvedModule, parent_index: Map, source_indices: Map, intern_table: InternTable) -> TypecheckModuleResult { + typecheck_module(resolved: resolved, parent_index: parent_index, variant_surfaces: empty_map(), source_indices: source_indices, intern_table: intern_table) +} + type EnvResolveResult { env: TypeEnv diagnostics: List diff --git a/src/v1/compiler_tests_rust.dag b/src/v1/compiler_tests_rust.dag index 1c8c8e77a0b..6d8411c5307 100644 --- a/src/v1/compiler_tests_rust.dag +++ b/src/v1/compiler_tests_rust.dag @@ -1190,13 +1190,9 @@ fn ct_profile_reconcile_test() -> String { " panic!(\"ABORT: '{}' build_type_env took {:?}\", name, env_elapsed);\n", " }\n\n", " let t_full = Instant::now();\n", - " let tc_result = crate::v1_compiler_infer::typecheck_module(\n", + " let tc_result = crate::v1_compiler_infer::typecheck_module_isolated(\n", " resolved.clone(),\n", " module_index.clone(),\n", - " std::rc::Rc::new(std::collections::HashMap::<\n", - " String,\n", - " std::rc::Rc,\n", - " >::new()),\n", " source_indices.clone(),\n", " intern_table.clone(),\n", " );\n", diff --git a/src/v1/stage0/src/compiler_tests.rs b/src/v1/stage0/src/compiler_tests.rs index 3a0bae6ef66..1f6c42eeafc 100644 --- a/src/v1/stage0/src/compiler_tests.rs +++ b/src/v1/stage0/src/compiler_tests.rs @@ -1714,13 +1714,9 @@ mod compiler_tests { } let t_full = Instant::now(); - let tc_result = crate::v1_compiler_infer::typecheck_module( + let tc_result = crate::v1_compiler_infer::typecheck_module_isolated( resolved.clone(), module_index.clone(), - std::rc::Rc::new(std::collections::HashMap::< - String, - std::rc::Rc, - >::new()), source_indices.clone(), intern_table.clone(), ); 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 02b38ea15d1..d4c297f8270 100644 --- a/src/v1/stage0/src/v1_compiler_compiler_tests_rust.rs +++ b/src/v1/stage0/src/v1_compiler_compiler_tests_rust.rs @@ -283,7 +283,7 @@ pub fn ct_profile_full_pipeline_test() -> String { } pub fn ct_profile_reconcile_test() -> 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(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(" #[test]\n".to_string(), " #[ignore]\n".to_string()), " fn profile_reconcile_per_module() {\n".to_string()), " let result = std::thread::Builder::new()\n".to_string()), " .stack_size(64 * 1024 * 1024)\n".to_string()), " .spawn(|| {\n".to_string()), " use std::collections::HashMap;\n".to_string()), " use std::time::Instant;\n\n".to_string()), " let sources = self_compile_sources();\n\n".to_string()), " eprintln!(\n".to_string()), " \"\\n=== PER-MODULE RECONCILE PROFILE ({} sources) ===\",\n".to_string()), " sources.len()\n".to_string()), " );\n\n".to_string()), " let t0 = Instant::now();\n".to_string()), " let mut modules = Vec::new();\n".to_string()), " let mut intern_table = crate::v1_std_core::empty_intern_table();\n".to_string()), " for source in &sources {\n".to_string()), " let tokens = crate::v1_compiler_tokenize::tokenize(\n".to_string()), " source.content.clone(),\n".to_string()), " source.path.clone(),\n".to_string()), " );\n".to_string()), " let si = crate::v1_std_core::build_newline_index(\n".to_string()), " source.path.clone(),\n".to_string()), " source.content.clone(),\n".to_string()), " );\n".to_string()), " let parsed = crate::v1_compiler_parse::parse_with_table(\n".to_string()), " tokens.clone(),\n".to_string()), " crate::v1_rt::rc_map_insert(\n".to_string()), " crate::v1_rt::rc_empty_map::>(),\n".to_string()), " si.file.clone(),\n".to_string()), " si.clone(),\n".to_string()), " ),\n".to_string()), " intern_table.clone(),\n".to_string()), " );\n".to_string()), " let result = parsed.result.clone();\n".to_string()), " intern_table = parsed.intern_table.clone();\n".to_string()), " let m = result.module.clone()\n".to_string()), " .unwrap_or_else(|| panic!(\"parse failed for source {}\", source.path));\n".to_string()), " modules.push(m);\n".to_string()), " }\n".to_string()), " let resolve_si = sources.iter().fold(\n".to_string()), " crate::v1_rt::rc_empty_map::>(),\n".to_string()), " |acc, s| crate::v1_rt::rc_map_insert(acc, s.path.clone(), crate::v1_std_core::build_newline_index(s.path.clone(), s.content.clone())),\n".to_string()), " );\n".to_string()), " let graph = crate::v1_compiler_resolve::resolve_modules(std::rc::Rc::new(modules), resolve_si);\n".to_string()), " let setup_time = t0.elapsed();\n".to_string()), " let rss_baseline = get_rss_bytes();\n".to_string()), " eprintln!(\n".to_string()), " \" Setup (tok+parse+resolve): {:?} | RSS: {}\",\n".to_string()), " setup_time,\n".to_string()), " format_bytes(rss_baseline)\n".to_string()), " );\n".to_string()), " eprintln!(\" Modules to reconcile: {}\\n\", graph.modules.len());\n\n".to_string()), " let mut mi_raw = HashMap::<\n".to_string()), " String,\n".to_string()), " std::rc::Rc,\n".to_string()), " >::new();\n".to_string()), " let source_indices = std::rc::Rc::new(sources.iter().fold(\n".to_string()), " HashMap::>::new(),\n".to_string()), " |mut acc, source| {\n".to_string()), " acc.insert(\n".to_string()), " source.path.clone(),\n".to_string()), " crate::v1_std_core::build_newline_index(\n".to_string()), " source.path.clone(),\n".to_string()), " source.content.clone(),\n".to_string()), " ),\n".to_string()), " );\n".to_string()), " acc\n".to_string()), " },\n".to_string()), " ));\n\n".to_string()), " for resolved in graph.modules.iter() {\n".to_string()), " let name = resolved.module.name.to_string();\n".to_string()), " let item_count =\n".to_string()), " crate::v1_std_core::module_items(resolved.module.clone()).len();\n".to_string()), " let rss_before = get_rss_bytes();\n\n".to_string()), " eprint!(\" {:>35} ({:>3} items) ... \", name, item_count);\n\n".to_string()), " let module_index = std::rc::Rc::new(mi_raw.clone());\n\n".to_string()), " let t_unres = Instant::now();\n".to_string()), " let _unres = crate::v1_compiler_infer::build_type_env_unresolved(\n".to_string()), " resolved.clone(),\n".to_string()), " module_index.clone(),\n".to_string()), " source_indices.clone(),\n".to_string()), " intern_table.clone(),\n".to_string()), " );\n".to_string()), " let unres_elapsed = t_unres.elapsed();\n".to_string()), " let rss_after_unres = get_rss_bytes();\n".to_string()), " let unres_delta = rss_after_unres.saturating_sub(rss_before);\n\n".to_string()), " eprint!(\n".to_string()), " \"cycles={:>8.2?}(+{}) \",\n".to_string()), " unres_elapsed,\n".to_string()), " format_bytes(unres_delta)\n".to_string()), " );\n\n".to_string()), " if unres_delta > 256 * 1024 * 1024 {\n".to_string()), " eprintln!(\"\");\n".to_string()), " panic!(\n".to_string()), " \"ABORT: '{}' cycle detection grew RSS by {}\",\n".to_string()), " name,\n".to_string()), " format_bytes(unres_delta)\n".to_string()), " );\n".to_string()), " }\n\n".to_string()), " let t_env = Instant::now();\n".to_string()), " let env_result = crate::v1_compiler_infer::build_type_env(\n".to_string()), " resolved.clone(),\n".to_string()), " module_index.clone(),\n".to_string()), " source_indices.clone(),\n".to_string()), " intern_table.clone(),\n".to_string()), " );\n".to_string()), " let env_elapsed = t_env.elapsed();\n".to_string()), " let rss_after_env = get_rss_bytes();\n".to_string()), " let env_delta = rss_after_env.saturating_sub(rss_before);\n".to_string()), " let env_errs: usize = env_result\n".to_string()), " .diagnostics\n".to_string()), " .iter()\n".to_string()), " .filter(|d| crate::v1_std_core::is_error_diagnostic(d.diagnostic.clone()))\n".to_string()), " .count();\n\n".to_string()), " eprint!(\n".to_string()), " \"env={:>8.2?}(+{},e={}) \",\n".to_string()), " env_elapsed,\n".to_string()), " format_bytes(env_delta),\n".to_string()), " env_errs\n".to_string()), " );\n\n".to_string()), " if env_delta > 512 * 1024 * 1024 {\n".to_string()), " eprintln!(\"\");\n".to_string()), " panic!(\n".to_string()), " \"ABORT: '{}' build_type_env grew RSS by {}\",\n".to_string()), " name,\n".to_string()), " format_bytes(env_delta)\n".to_string()), " );\n".to_string()), " }\n".to_string()), " if env_elapsed.as_secs() > 10 {\n".to_string()), " eprintln!(\"\");\n".to_string()), " panic!(\"ABORT: '{}' build_type_env took {:?}\", name, env_elapsed);\n".to_string()), " }\n\n".to_string()), " let t_full = Instant::now();\n".to_string()), " let tc_result = crate::v1_compiler_infer::typecheck_module(\n".to_string()), " resolved.clone(),\n".to_string()), " module_index.clone(),\n".to_string()), " std::rc::Rc::new(std::collections::HashMap::<\n".to_string()), " String,\n".to_string()), " std::rc::Rc,\n".to_string()), " >::new()),\n".to_string()), " source_indices.clone(),\n".to_string()), " intern_table.clone(),\n".to_string()), " );\n".to_string()), " let full_elapsed = t_full.elapsed();\n".to_string()), " let rss_after = get_rss_bytes();\n".to_string()), " let delta = rss_after.saturating_sub(rss_before);\n".to_string()), " let diag_count: usize = tc_result\n".to_string()), " .diagnostics\n".to_string()), " .iter()\n".to_string()), " .filter(|d| crate::v1_std_core::is_error_diagnostic(d.diagnostic.clone()))\n".to_string()), " .count();\n\n".to_string()), " eprintln!(\n".to_string()), " \"full={:>8.2?} | RSS: {} (+{}) | errs: {}\",\n".to_string()), " full_elapsed,\n".to_string()), " format_bytes(rss_after),\n".to_string()), " format_bytes(delta),\n".to_string()), " diag_count\n".to_string()), " );\n\n".to_string()), " if delta > 512 * 1024 * 1024 {\n".to_string()), " panic!(\n".to_string()), " \"ABORT: '{}' grew RSS by {} (>512MB)\",\n".to_string()), " name,\n".to_string()), " format_bytes(delta)\n".to_string()), " );\n".to_string()), " }\n".to_string()), " if full_elapsed.as_secs() > 10 {\n".to_string()), " panic!(\"ABORT: '{}' took {:?} (>10s)\", name, full_elapsed);\n".to_string()), " }\n\n".to_string()), " let typed = tc_result.typed.clone();\n".to_string()), " mi_raw.insert(name, typed);\n".to_string()), " }\n\n".to_string()), " let rss_final = get_rss_bytes();\n".to_string()), " eprintln!(\n".to_string()), " \"\\n RSS final: {} (from baseline: +{})\",\n".to_string()), " format_bytes(rss_final),\n".to_string()), " format_bytes(rss_final.saturating_sub(rss_baseline))\n".to_string()), " );\n".to_string()), " eprintln!(\"=== DONE ===\\n\");\n".to_string()), " })\n".to_string()), " .expect(\"failed to spawn thread\")\n".to_string()), " .join();\n".to_string()), " result.expect(\"profile_reconcile_per_module panicked\");\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(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(" #[test]\n".to_string(), " #[ignore]\n".to_string()), " fn profile_reconcile_per_module() {\n".to_string()), " let result = std::thread::Builder::new()\n".to_string()), " .stack_size(64 * 1024 * 1024)\n".to_string()), " .spawn(|| {\n".to_string()), " use std::collections::HashMap;\n".to_string()), " use std::time::Instant;\n\n".to_string()), " let sources = self_compile_sources();\n\n".to_string()), " eprintln!(\n".to_string()), " \"\\n=== PER-MODULE RECONCILE PROFILE ({} sources) ===\",\n".to_string()), " sources.len()\n".to_string()), " );\n\n".to_string()), " let t0 = Instant::now();\n".to_string()), " let mut modules = Vec::new();\n".to_string()), " let mut intern_table = crate::v1_std_core::empty_intern_table();\n".to_string()), " for source in &sources {\n".to_string()), " let tokens = crate::v1_compiler_tokenize::tokenize(\n".to_string()), " source.content.clone(),\n".to_string()), " source.path.clone(),\n".to_string()), " );\n".to_string()), " let si = crate::v1_std_core::build_newline_index(\n".to_string()), " source.path.clone(),\n".to_string()), " source.content.clone(),\n".to_string()), " );\n".to_string()), " let parsed = crate::v1_compiler_parse::parse_with_table(\n".to_string()), " tokens.clone(),\n".to_string()), " crate::v1_rt::rc_map_insert(\n".to_string()), " crate::v1_rt::rc_empty_map::>(),\n".to_string()), " si.file.clone(),\n".to_string()), " si.clone(),\n".to_string()), " ),\n".to_string()), " intern_table.clone(),\n".to_string()), " );\n".to_string()), " let result = parsed.result.clone();\n".to_string()), " intern_table = parsed.intern_table.clone();\n".to_string()), " let m = result.module.clone()\n".to_string()), " .unwrap_or_else(|| panic!(\"parse failed for source {}\", source.path));\n".to_string()), " modules.push(m);\n".to_string()), " }\n".to_string()), " let resolve_si = sources.iter().fold(\n".to_string()), " crate::v1_rt::rc_empty_map::>(),\n".to_string()), " |acc, s| crate::v1_rt::rc_map_insert(acc, s.path.clone(), crate::v1_std_core::build_newline_index(s.path.clone(), s.content.clone())),\n".to_string()), " );\n".to_string()), " let graph = crate::v1_compiler_resolve::resolve_modules(std::rc::Rc::new(modules), resolve_si);\n".to_string()), " let setup_time = t0.elapsed();\n".to_string()), " let rss_baseline = get_rss_bytes();\n".to_string()), " eprintln!(\n".to_string()), " \" Setup (tok+parse+resolve): {:?} | RSS: {}\",\n".to_string()), " setup_time,\n".to_string()), " format_bytes(rss_baseline)\n".to_string()), " );\n".to_string()), " eprintln!(\" Modules to reconcile: {}\\n\", graph.modules.len());\n\n".to_string()), " let mut mi_raw = HashMap::<\n".to_string()), " String,\n".to_string()), " std::rc::Rc,\n".to_string()), " >::new();\n".to_string()), " let source_indices = std::rc::Rc::new(sources.iter().fold(\n".to_string()), " HashMap::>::new(),\n".to_string()), " |mut acc, source| {\n".to_string()), " acc.insert(\n".to_string()), " source.path.clone(),\n".to_string()), " crate::v1_std_core::build_newline_index(\n".to_string()), " source.path.clone(),\n".to_string()), " source.content.clone(),\n".to_string()), " ),\n".to_string()), " );\n".to_string()), " acc\n".to_string()), " },\n".to_string()), " ));\n\n".to_string()), " for resolved in graph.modules.iter() {\n".to_string()), " let name = resolved.module.name.to_string();\n".to_string()), " let item_count =\n".to_string()), " crate::v1_std_core::module_items(resolved.module.clone()).len();\n".to_string()), " let rss_before = get_rss_bytes();\n\n".to_string()), " eprint!(\" {:>35} ({:>3} items) ... \", name, item_count);\n\n".to_string()), " let module_index = std::rc::Rc::new(mi_raw.clone());\n\n".to_string()), " let t_unres = Instant::now();\n".to_string()), " let _unres = crate::v1_compiler_infer::build_type_env_unresolved(\n".to_string()), " resolved.clone(),\n".to_string()), " module_index.clone(),\n".to_string()), " source_indices.clone(),\n".to_string()), " intern_table.clone(),\n".to_string()), " );\n".to_string()), " let unres_elapsed = t_unres.elapsed();\n".to_string()), " let rss_after_unres = get_rss_bytes();\n".to_string()), " let unres_delta = rss_after_unres.saturating_sub(rss_before);\n\n".to_string()), " eprint!(\n".to_string()), " \"cycles={:>8.2?}(+{}) \",\n".to_string()), " unres_elapsed,\n".to_string()), " format_bytes(unres_delta)\n".to_string()), " );\n\n".to_string()), " if unres_delta > 256 * 1024 * 1024 {\n".to_string()), " eprintln!(\"\");\n".to_string()), " panic!(\n".to_string()), " \"ABORT: '{}' cycle detection grew RSS by {}\",\n".to_string()), " name,\n".to_string()), " format_bytes(unres_delta)\n".to_string()), " );\n".to_string()), " }\n\n".to_string()), " let t_env = Instant::now();\n".to_string()), " let env_result = crate::v1_compiler_infer::build_type_env(\n".to_string()), " resolved.clone(),\n".to_string()), " module_index.clone(),\n".to_string()), " source_indices.clone(),\n".to_string()), " intern_table.clone(),\n".to_string()), " );\n".to_string()), " let env_elapsed = t_env.elapsed();\n".to_string()), " let rss_after_env = get_rss_bytes();\n".to_string()), " let env_delta = rss_after_env.saturating_sub(rss_before);\n".to_string()), " let env_errs: usize = env_result\n".to_string()), " .diagnostics\n".to_string()), " .iter()\n".to_string()), " .filter(|d| crate::v1_std_core::is_error_diagnostic(d.diagnostic.clone()))\n".to_string()), " .count();\n\n".to_string()), " eprint!(\n".to_string()), " \"env={:>8.2?}(+{},e={}) \",\n".to_string()), " env_elapsed,\n".to_string()), " format_bytes(env_delta),\n".to_string()), " env_errs\n".to_string()), " );\n\n".to_string()), " if env_delta > 512 * 1024 * 1024 {\n".to_string()), " eprintln!(\"\");\n".to_string()), " panic!(\n".to_string()), " \"ABORT: '{}' build_type_env grew RSS by {}\",\n".to_string()), " name,\n".to_string()), " format_bytes(env_delta)\n".to_string()), " );\n".to_string()), " }\n".to_string()), " if env_elapsed.as_secs() > 10 {\n".to_string()), " eprintln!(\"\");\n".to_string()), " panic!(\"ABORT: '{}' build_type_env took {:?}\", name, env_elapsed);\n".to_string()), " }\n\n".to_string()), " let t_full = Instant::now();\n".to_string()), " let tc_result = crate::v1_compiler_infer::typecheck_module_isolated(\n".to_string()), " resolved.clone(),\n".to_string()), " module_index.clone(),\n".to_string()), " source_indices.clone(),\n".to_string()), " intern_table.clone(),\n".to_string()), " );\n".to_string()), " let full_elapsed = t_full.elapsed();\n".to_string()), " let rss_after = get_rss_bytes();\n".to_string()), " let delta = rss_after.saturating_sub(rss_before);\n".to_string()), " let diag_count: usize = tc_result\n".to_string()), " .diagnostics\n".to_string()), " .iter()\n".to_string()), " .filter(|d| crate::v1_std_core::is_error_diagnostic(d.diagnostic.clone()))\n".to_string()), " .count();\n\n".to_string()), " eprintln!(\n".to_string()), " \"full={:>8.2?} | RSS: {} (+{}) | errs: {}\",\n".to_string()), " full_elapsed,\n".to_string()), " format_bytes(rss_after),\n".to_string()), " format_bytes(delta),\n".to_string()), " diag_count\n".to_string()), " );\n\n".to_string()), " if delta > 512 * 1024 * 1024 {\n".to_string()), " panic!(\n".to_string()), " \"ABORT: '{}' grew RSS by {} (>512MB)\",\n".to_string()), " name,\n".to_string()), " format_bytes(delta)\n".to_string()), " );\n".to_string()), " }\n".to_string()), " if full_elapsed.as_secs() > 10 {\n".to_string()), " panic!(\"ABORT: '{}' took {:?} (>10s)\", name, full_elapsed);\n".to_string()), " }\n\n".to_string()), " let typed = tc_result.typed.clone();\n".to_string()), " mi_raw.insert(name, typed);\n".to_string()), " }\n\n".to_string()), " let rss_final = get_rss_bytes();\n".to_string()), " eprintln!(\n".to_string()), " \"\\n RSS final: {} (from baseline: +{})\",\n".to_string()), " format_bytes(rss_final),\n".to_string()), " format_bytes(rss_final.saturating_sub(rss_baseline))\n".to_string()), " );\n".to_string()), " eprintln!(\"=== DONE ===\\n\");\n".to_string()), " })\n".to_string()), " .expect(\"failed to spawn thread\")\n".to_string()), " .join();\n".to_string()), " result.expect(\"profile_reconcile_per_module panicked\");\n".to_string()), " }\n\n".to_string()) } pub fn ct_module_footer() -> String { From 880f21d25fc60ffee6b3eafaa53a7e136c6af623 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Sun, 5 Jul 2026 00:55:04 +0000 Subject: [PATCH 08/32] fix: add typecheck_module_isolated to stage0 infer hand-patch Complete ea0891b dag/emit revert: profile tests call the 4-arg isolated wrapper (empty variant_surfaces) instead of bloating the concat emit artifact with a 5-arg HashMap literal. Co-authored-by: Cursor --- src/v1/stage0/src/v1_compiler_infer.rs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/v1/stage0/src/v1_compiler_infer.rs b/src/v1/stage0/src/v1_compiler_infer.rs index 248de7500eb..bbbde9b4f61 100644 --- a/src/v1/stage0/src/v1_compiler_infer.rs +++ b/src/v1/stage0/src/v1_compiler_infer.rs @@ -14084,6 +14084,21 @@ pub fn typecheck_module( } } +pub fn typecheck_module_isolated( + resolved: Rc, + parent_index: Rc>>, + source_indices: Rc>>, + intern_table: Rc, +) -> Rc { + typecheck_module( + resolved, + parent_index, + Rc::new(HashMap::new()), + source_indices, + intern_table, + ) +} + #[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] pub struct EnvResolveResult { pub env: Rc, From de7a0e34547692a229b2e7f922a0756856073d92 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Sun, 5 Jul 2026 01:12:13 +0000 Subject: [PATCH 09/32] chore: delete orphaned local_authority_binding after P3 index rewrite Superseded by build_type_name_export_index canonical_binding lookup; no call sites remained. Co-authored-by: Cursor --- src/v1/04_infer.dag | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/v1/04_infer.dag b/src/v1/04_infer.dag index fefa1055581..11eb1eea064 100644 --- a/src/v1/04_infer.dag +++ b/src/v1/04_infer.dag @@ -6601,13 +6601,6 @@ fn import_module_path_at(imp: Node, source_indices: Map) - authored_name_at(source_indices: source_indices, node: imp) } -fn local_authority_binding(owner: TypedModule, name: String) -> TypeBinding? { - match filter(owner.type_env.bindings |> map_values, b => b.name == name) |> first { - Present { value: binding } => Present { value: binding } - Absent => Absent - } -} - fn build_type_name_export_index(modules: List) -> Map { fold(modules, init: empty_map(), f: (acc, m) => fold(m.type_env.bindings |> map_values, init: acc, f: (acc2, binding) => From 7141fa13280bb6bde24ad14ec428b7725a614cdb Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Sun, 5 Jul 2026 01:32:49 +0000 Subject: [PATCH 10/32] WIP: P3+P5: infer whole-corpus scans to per-module maps --- src/v1/tests/src/lib.rs | 2 + .../variant_export_surface_witness_test.rs | 199 ++++++++++++++++++ 2 files changed, 201 insertions(+) create mode 100644 src/v1/tests/src/variant_export_surface_witness_test.rs diff --git a/src/v1/tests/src/lib.rs b/src/v1/tests/src/lib.rs index d2ae9eb4722..48e1fb3666d 100644 --- a/src/v1/tests/src/lib.rs +++ b/src/v1/tests/src/lib.rs @@ -149,6 +149,8 @@ mod union_resolve_receipts_test; #[cfg(test)] mod v1_compiler_lib_test; #[cfg(test)] +mod variant_export_surface_witness_test; +#[cfg(test)] mod value_carrier_swap_test; #[cfg(test)] mod variant_owner_disambiguation_test; diff --git a/src/v1/tests/src/variant_export_surface_witness_test.rs b/src/v1/tests/src/variant_export_surface_witness_test.rs new file mode 100644 index 00000000000..9b24827c51a --- /dev/null +++ b/src/v1/tests/src/variant_export_surface_witness_test.rs @@ -0,0 +1,199 @@ +//! P3+P5 purity oracle: incremental `variant_surfaces` / `type_name_index` must +//! preserve re-export variant resolution and full-pipeline diagnostics. +//! +//! RED control: `typecheck_module_isolated` (empty surfaces) must fail on a +//! re-export chain where the consumer imports a coproduct arm through a proxy. + +use std::collections::HashMap; +use std::rc::Rc; + +use v1_compiler::v1_compiler_compile::{front_end_sources, normalize_graph, SourceFile}; +use v1_compiler::v1_compiler_infer::{ + build_variant_export_surface, typecheck_module, typecheck_module_isolated, + TypecheckModuleResult, VariantExportSurface, +}; +use v1_compiler::v1_compiler_infer_items::TypedModule; +use v1_compiler::v1_compiler_resolve::ResolvedModule; +use v1_compiler::v1_std_core::{ + authored_name_at, diagnostic_to_message, empty_intern_table, is_error_diagnostic, + CompilerDiagnostic, InternTable, NewlineIndex, +}; +use v1_compiler::v1_rt; + +use crate::helpers::{compile_multi, diagnostic_messages, resolve_imports_transitively}; + +const PROVIDER: &str = "module test.provider\ntype Shape = Circle { r: Int } | Square { s: Int }\n"; + +const REEXPORT: &str = "module test.reexport\n\ +import test.provider { Circle }\n"; + +const CONSUMER: &str = "module test.consumer\n\ +import test.reexport { Circle }\n\ +fn mk() -> Circle { Circle { r: 1 } }\n"; + +fn fixture_sources() -> Vec> { + resolve_imports_transitively("consumer.dag", CONSUMER) + .into_iter() + .chain(resolve_imports_transitively("provider.dag", PROVIDER)) + .chain(resolve_imports_transitively("reexport.dag", REEXPORT)) + .fold(HashMap::new(), |mut acc, src| { + acc.entry(src.path.clone()).or_insert(src); + acc + }) + .into_values() + .collect() +} + +fn resolved_module_graph( + sources: Rc>>, +) -> ( + Rc>>, + Rc>>, + Rc, +) { + let frontend = front_end_sources(sources); + let graph = frontend.graph.expect("resolved module graph"); + let source_indices = frontend + .newline_indices + .iter() + .cloned() + .fold( + v1_rt::rc_empty_map::>(), + |acc, si| v1_rt::rc_map_insert(acc, si.file.clone(), si), + ); + let norm = normalize_graph(graph, source_indices.clone()); + ( + norm.graph.modules.clone(), + source_indices, + frontend.intern_table, + ) +} + +fn hard_diagnostic_messages(result: &TypecheckModuleResult) -> Vec { + result + .diagnostics + .iter() + .filter(|d| is_error_diagnostic(d.diagnostic.clone())) + .map(|d| diagnostic_to_message(d.diagnostic.clone())) + .collect() +} + +fn typecheck_resolved_incremental( + modules: &[Rc], + source_indices: Rc>>, + intern_table: Rc, +) -> Vec> { + let mut module_index: Rc>> = v1_rt::rc_empty_map(); + let mut variant_surfaces: Rc>> = + v1_rt::rc_empty_map(); + let mut results = Vec::new(); + for resolved in modules { + let tc = typecheck_module( + resolved.clone(), + module_index.clone(), + variant_surfaces.clone(), + source_indices.clone(), + intern_table.clone(), + ); + let typed = tc.typed.clone(); + let path = authored_name_at(source_indices.clone(), typed.module.clone()); + variant_surfaces = v1_rt::rc_map_insert( + variant_surfaces.clone(), + path.clone(), + build_variant_export_surface( + typed.clone(), + variant_surfaces.clone(), + source_indices.clone(), + ), + ); + module_index = v1_rt::rc_map_insert(module_index, path, typed); + results.push(tc); + } + results +} + +fn consumer_resolved<'a>( + modules: &'a [Rc], + source_indices: &Rc>>, +) -> &'a Rc { + modules + .iter() + .find(|m| authored_name_at(source_indices.clone(), m.module.clone()) == "test.consumer") + .expect("consumer module in resolved graph") +} + +#[test] +fn variant_reexport_chain_full_pipeline_is_clean() { + let result = compile_multi(&[ + ("provider.dag", PROVIDER), + ("reexport.dag", REEXPORT), + ("consumer.dag", CONSUMER), + ]); + let msgs: Vec<_> = diagnostic_messages(&result) + .into_iter() + .filter(|m| !m.starts_with("complexity: ")) + .collect(); + assert!( + msgs.is_empty(), + "re-export variant chain must typecheck clean via production reconcile, got:\n{msgs:?}" + ); +} + +#[test] +fn variant_reexport_incremental_surfaces_match_full_pipeline_fingerprint() { + let sources = Rc::new(fixture_sources()); + let pipeline = compile_multi(&[ + ("provider.dag", PROVIDER), + ("reexport.dag", REEXPORT), + ("consumer.dag", CONSUMER), + ]); + let pipeline_msgs: Vec<_> = diagnostic_messages(&pipeline) + .into_iter() + .filter(|m| !m.starts_with("complexity: ")) + .collect(); + + let (modules, source_indices, intern_table) = resolved_module_graph(sources); + let incremental = typecheck_resolved_incremental(&modules, source_indices, intern_table); + let consumer_tc = incremental.last().expect("consumer typecheck result"); + let incremental_msgs = hard_diagnostic_messages(consumer_tc); + + assert!( + pipeline_msgs.is_empty() && incremental_msgs.is_empty(), + "incremental-surface path must match full pipeline (both clean), pipeline={pipeline_msgs:?} incremental={incremental_msgs:?}" + ); +} + +#[test] +fn variant_reexport_empty_surfaces_red_control() { + let sources = Rc::new(fixture_sources()); + let (modules, source_indices, intern_table) = resolved_module_graph(sources); + + let incremental = + typecheck_resolved_incremental(&modules, source_indices.clone(), intern_table.clone()); + let module_index: Rc>> = incremental + .iter() + .map(|tc| { + let path = authored_name_at(source_indices.clone(), tc.typed.module.clone()); + (path, tc.typed.clone()) + }) + .fold(v1_rt::rc_empty_map(), |acc, (k, v)| v1_rt::rc_map_insert(acc, k, v)); + + let consumer = consumer_resolved(&modules, &source_indices); + let isolated = typecheck_module_isolated( + consumer.clone(), + module_index, + source_indices.clone(), + intern_table, + ); + let has_unresolved = isolated.diagnostics.iter().any(|d| { + matches!( + &*d.diagnostic, + CompilerDiagnostic::UnresolvedType { name, .. } if name == "Circle" + ) + }); + assert!( + has_unresolved, + "isolated typecheck (empty variant_surfaces) must RED on re-export arm import, got:\n{}", + hard_diagnostic_messages(&isolated).join("\n") + ); +} From b5831b30dd866ba668ac64268e4c8d82ca467a31 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Sun, 5 Jul 2026 01:57:53 +0000 Subject: [PATCH 11/32] fix(tests): green variant_export_surface witness tests for CI Fix Rc move errors, use a working E=A|B re-export fixture (matching pipeline.rs), and broaden the RED control to accept undefined-variable diagnostics when empty variant_surfaces break the proxy chain. Co-authored-by: Cursor --- src/v1/tests/src/lib.rs | 4 +- .../variant_export_surface_witness_test.rs | 55 +++++++++---------- 2 files changed, 27 insertions(+), 32 deletions(-) diff --git a/src/v1/tests/src/lib.rs b/src/v1/tests/src/lib.rs index 48e1fb3666d..cbf8fc2acf8 100644 --- a/src/v1/tests/src/lib.rs +++ b/src/v1/tests/src/lib.rs @@ -149,10 +149,10 @@ mod union_resolve_receipts_test; #[cfg(test)] mod v1_compiler_lib_test; #[cfg(test)] -mod variant_export_surface_witness_test; -#[cfg(test)] mod value_carrier_swap_test; #[cfg(test)] +mod variant_export_surface_witness_test; +#[cfg(test)] mod variant_owner_disambiguation_test; #[cfg(test)] mod wet_hermetic_equivalence_test; diff --git a/src/v1/tests/src/variant_export_surface_witness_test.rs b/src/v1/tests/src/variant_export_surface_witness_test.rs index 9b24827c51a..bac290b575a 100644 --- a/src/v1/tests/src/variant_export_surface_witness_test.rs +++ b/src/v1/tests/src/variant_export_surface_witness_test.rs @@ -14,22 +14,18 @@ use v1_compiler::v1_compiler_infer::{ }; use v1_compiler::v1_compiler_infer_items::TypedModule; use v1_compiler::v1_compiler_resolve::ResolvedModule; +use v1_compiler::v1_rt; use v1_compiler::v1_std_core::{ - authored_name_at, diagnostic_to_message, empty_intern_table, is_error_diagnostic, - CompilerDiagnostic, InternTable, NewlineIndex, + authored_name_at, diagnostic_to_message, is_error_diagnostic, InternTable, NewlineIndex, }; -use v1_compiler::v1_rt; use crate::helpers::{compile_multi, diagnostic_messages, resolve_imports_transitively}; -const PROVIDER: &str = "module test.provider\ntype Shape = Circle { r: Int } | Square { s: Int }\n"; +const PROVIDER: &str = "module test.provider\ntype E = A | B\n"; -const REEXPORT: &str = "module test.reexport\n\ -import test.provider { Circle }\n"; +const REEXPORT: &str = "module test.reexport\nimport test.provider { B }\n"; -const CONSUMER: &str = "module test.consumer\n\ -import test.reexport { Circle }\n\ -fn mk() -> Circle { Circle { r: 1 } }\n"; +const CONSUMER: &str = "module test.consumer\nimport test.reexport { B }\nfn f() -> E { B }\n"; fn fixture_sources() -> Vec> { resolve_imports_transitively("consumer.dag", CONSUMER) @@ -52,20 +48,16 @@ fn resolved_module_graph( Rc, ) { let frontend = front_end_sources(sources); - let graph = frontend.graph.expect("resolved module graph"); - let source_indices = frontend - .newline_indices - .iter() - .cloned() - .fold( - v1_rt::rc_empty_map::>(), - |acc, si| v1_rt::rc_map_insert(acc, si.file.clone(), si), - ); + let graph = frontend.graph.clone().expect("resolved module graph"); + let source_indices = frontend.newline_indices.iter().cloned().fold( + v1_rt::rc_empty_map::>(), + |acc, si| v1_rt::rc_map_insert(acc, si.file.clone(), si), + ); let norm = normalize_graph(graph, source_indices.clone()); ( norm.graph.modules.clone(), source_indices, - frontend.intern_table, + frontend.intern_table.clone(), ) } @@ -84,8 +76,7 @@ fn typecheck_resolved_incremental( intern_table: Rc, ) -> Vec> { let mut module_index: Rc>> = v1_rt::rc_empty_map(); - let mut variant_surfaces: Rc>> = - v1_rt::rc_empty_map(); + let mut variant_surfaces: Rc>> = v1_rt::rc_empty_map(); let mut results = Vec::new(); for resolved in modules { let tc = typecheck_module( @@ -170,13 +161,20 @@ fn variant_reexport_empty_surfaces_red_control() { let incremental = typecheck_resolved_incremental(&modules, source_indices.clone(), intern_table.clone()); + assert!( + hard_diagnostic_messages(incremental.last().expect("consumer incremental typecheck")) + .is_empty(), + "incremental path must be clean before RED control" + ); let module_index: Rc>> = incremental .iter() .map(|tc| { let path = authored_name_at(source_indices.clone(), tc.typed.module.clone()); (path, tc.typed.clone()) }) - .fold(v1_rt::rc_empty_map(), |acc, (k, v)| v1_rt::rc_map_insert(acc, k, v)); + .fold(v1_rt::rc_empty_map(), |acc, (k, v)| { + v1_rt::rc_map_insert(acc, k, v) + }); let consumer = consumer_resolved(&modules, &source_indices); let isolated = typecheck_module_isolated( @@ -185,15 +183,12 @@ fn variant_reexport_empty_surfaces_red_control() { source_indices.clone(), intern_table, ); - let has_unresolved = isolated.diagnostics.iter().any(|d| { - matches!( - &*d.diagnostic, - CompilerDiagnostic::UnresolvedType { name, .. } if name == "Circle" - ) - }); + let isolated_msgs = hard_diagnostic_messages(&isolated); assert!( - has_unresolved, + isolated_msgs + .iter() + .any(|m| m.contains("'B'") || m.contains("unresolved type 'B'")), "isolated typecheck (empty variant_surfaces) must RED on re-export arm import, got:\n{}", - hard_diagnostic_messages(&isolated).join("\n") + isolated_msgs.join("\n") ); } From f335f96b5449e9fdd6e369ebc35664aa5cbaa490 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Sun, 5 Jul 2026 02:08:48 +0000 Subject: [PATCH 12/32] fix(tests): satisfy clippy type_complexity on witness fixture helper Extract ResolvedGraphFixture type alias for resolved_module_graph return tuple so rust_tests clippy gate passes with -D warnings. Co-authored-by: Cursor --- .../src/variant_export_surface_witness_test.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/v1/tests/src/variant_export_surface_witness_test.rs b/src/v1/tests/src/variant_export_surface_witness_test.rs index bac290b575a..9ea501c7bb3 100644 --- a/src/v1/tests/src/variant_export_surface_witness_test.rs +++ b/src/v1/tests/src/variant_export_surface_witness_test.rs @@ -27,6 +27,12 @@ const REEXPORT: &str = "module test.reexport\nimport test.provider { B }\n"; const CONSUMER: &str = "module test.consumer\nimport test.reexport { B }\nfn f() -> E { B }\n"; +type ResolvedGraphFixture = ( + Rc>>, + Rc>>, + Rc, +); + fn fixture_sources() -> Vec> { resolve_imports_transitively("consumer.dag", CONSUMER) .into_iter() @@ -40,13 +46,7 @@ fn fixture_sources() -> Vec> { .collect() } -fn resolved_module_graph( - sources: Rc>>, -) -> ( - Rc>>, - Rc>>, - Rc, -) { +fn resolved_module_graph(sources: Rc>>) -> ResolvedGraphFixture { let frontend = front_end_sources(sources); let graph = frontend.graph.clone().expect("resolved module graph"); let source_indices = frontend.newline_indices.iter().cloned().fold( From d212b0fc632f6dadf56f5598397e36c094ab0cda Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Sun, 5 Jul 2026 02:25:14 +0000 Subject: [PATCH 13/32] WIP: P3+P5: infer whole-corpus scans to per-module maps --- .github/workflows/ci.yml | 2 +- dag/gunbc/ci_workflow.dag | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a94b20d4ee0..afdab497eaa 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -16,7 +16,7 @@ env: CARGO_TERM_COLOR: always RUSTFLAGS: -D warnings jobs: - ci: + rust_tests: runs-on: [self-hosted, linux, arm64] timeout-minutes: 10 steps: diff --git a/dag/gunbc/ci_workflow.dag b/dag/gunbc/ci_workflow.dag index 1b51d721b9f..7d7b346d320 100644 --- a/dag/gunbc/ci_workflow.dag +++ b/dag/gunbc/ci_workflow.dag @@ -270,6 +270,8 @@ fn ci_deploy_step(stage: DeployStage) -> Step { data gunbc_ci_job_timeout_policy_minutes: Int = 10 +data gunbc_ci_floor_job_timeout_policy_minutes: Int = 20 + data gunbc_ci_job_timeout_policy_disposition: Disposition = Terminal { reason: "gunbc CI policy (operator, 2026-07-04): CI jobs time out at 10 minutes - a deliberate INVERSION of the prior ratchet (30min alarm -> 50min -> 90min, each raise buying headroom for the unfixed resolve cost; at 90min the queue backed up and runs still timed out delivering zero signal). The budget is now a forcing function: the floor must FIT it, and the itemized [t+Xs] phase marks say exactly which phase does not (measured 2026-07-04: plan-closure resolve was 15min+, whole-tree compile-clean ~48min on the ARM fleet - both resolve-dominated; see the floor-shared-compute-memoization plan). Every job stays red-by-timeout until resolve memoization lands; that red is honest, unlike a 90min lane that never completed. default_job_timeout_minutes (extdeps/github/actions.dag) still models GitHub's 360min platform default - workflow-layer policy on top." } @@ -301,12 +303,12 @@ fn ci_job() -> Job { name: none, runner: gunbc_ci_runner_spec(), steps: concat(ci_prelude_steps(), [ci_floor_step(), ci_merge_admission_gate_step()]), - needs: [], + needs: ["rust_tests"], env: none, outputs: none, if_condition: none, strategy: none, - timeout_minutes: Present { value: gunbc_ci_job_timeout_policy_minutes }, + timeout_minutes: Present { value: gunbc_ci_floor_job_timeout_policy_minutes }, continue_on_error: none, concurrency: none } From e42c9d24e541bd413c036e6c4855c75a851f645e Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Sun, 5 Jul 2026 02:25:32 +0000 Subject: [PATCH 14/32] fix(ci): regen ci.yml to match floor-job serialization policy ci_workflow.dag already gates the floor job on rust_tests (20m budget); regenerate ci.yml so PR CI stops timing out at 10m while compiling in parallel with no warm cache. Co-authored-by: Cursor --- .github/workflows/ci.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index afdab497eaa..48dd20385dd 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -16,9 +16,10 @@ env: CARGO_TERM_COLOR: always RUSTFLAGS: -D warnings jobs: - rust_tests: + ci: runs-on: [self-hosted, linux, arm64] - timeout-minutes: 10 + needs: [rust_tests] + timeout-minutes: 20 steps: - name: Checkout uses: actions/checkout@v5 @@ -268,3 +269,4 @@ jobs: "$ROOT/target/release/claim_executor" --verify-build-artifacts "$ROOT/target/release/claim_executor" "$ROOT/target/release/gunbc" sccache --show-stats 2>/dev/null || true "$ROOT/target/release/gunbc" run --source-root dag --source-root src/v2 --entry dag/gunbc/live_deploy/apply.dag --function live_deploy_apply_srv1_wet + From 7b1ba8a8330f283959f86c4aacb114bdcef2fa79 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Sun, 5 Jul 2026 02:36:39 +0000 Subject: [PATCH 15/32] fix(ci): pair floor timeout with unified policy disposition Extend gunbc_ci_job_timeout_policy_disposition to cover both the 10m rust_tests/deploy lane and the interim 20m ci-floor lane, with rationale (needs rust_tests warm cache; dissolves at memoization). Co-authored-by: Cursor --- dag/gunbc/ci_workflow.dag | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dag/gunbc/ci_workflow.dag b/dag/gunbc/ci_workflow.dag index 7d7b346d320..77f981ce260 100644 --- a/dag/gunbc/ci_workflow.dag +++ b/dag/gunbc/ci_workflow.dag @@ -273,7 +273,7 @@ data gunbc_ci_job_timeout_policy_minutes: Int = 10 data gunbc_ci_floor_job_timeout_policy_minutes: Int = 20 data gunbc_ci_job_timeout_policy_disposition: Disposition = Terminal { - reason: "gunbc CI policy (operator, 2026-07-04): CI jobs time out at 10 minutes - a deliberate INVERSION of the prior ratchet (30min alarm -> 50min -> 90min, each raise buying headroom for the unfixed resolve cost; at 90min the queue backed up and runs still timed out delivering zero signal). The budget is now a forcing function: the floor must FIT it, and the itemized [t+Xs] phase marks say exactly which phase does not (measured 2026-07-04: plan-closure resolve was 15min+, whole-tree compile-clean ~48min on the ARM fleet - both resolve-dominated; see the floor-shared-compute-memoization plan). Every job stays red-by-timeout until resolve memoization lands; that red is honest, unlike a 90min lane that never completed. default_job_timeout_minutes (extdeps/github/actions.dag) still models GitHub's 360min platform default - workflow-layer policy on top." + reason: "gunbc CI timeout policy (operator, 2026-07-04/05): two lanes, one disposition. rust_tests + deploy stay at gunbc_ci_job_timeout_policy_minutes (10) โ€” deliberate inversion of the prior 30โ†’50โ†’90min ratchet; the budget is a forcing function until resolve memoization lands (measured 2026-07-04: plan-closure resolve 15min+, compile-clean ~48min on ARM). ci floor alone uses gunbc_ci_floor_job_timeout_policy_minutes (20): not a ratchet step โ€” ci needs rust_tests (warm target/ cache), then rebuild + batch-1 dag_compile_clean; 10min killed mid-compile with zero floor signal (PR #6239, 2026-07-05). Dissolves when floor-shared-compute-memoization lands and batch-1 fits 10min; then floor reverts to the single 10min constant. default_job_timeout_minutes (extdeps/github/actions.dag) still models GitHub's 360min platform default." } fn ci_deploy_job(stage: DeployStage) -> Job { From 67785cc71877ad7dfa8ef40e8397ff7be63f4bc9 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Sun, 5 Jul 2026 03:04:40 +0000 Subject: [PATCH 16/32] fix(ci): scope cache to SHA so floor job inherits rust_tests build rust_tests warmed target/ but skipped cache save on lockfile key hit, so ci cold-rebuilt for 14min and timed out mid batch-1. Scope the cargo cache key to github.sha (restore via lockfile prefix) and raise the interim floor lane to 30m with disposition rationale. Co-authored-by: Cursor --- .github/workflows/ci.yml | 11 +++++++---- dag/gunbc/ci_workflow.dag | 4 ++-- dag/gunbc/ci_workflow_expressions.dag | 11 ++++++++++- 3 files changed, 19 insertions(+), 7 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 48dd20385dd..1a9cb4aff75 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -19,7 +19,7 @@ jobs: ci: runs-on: [self-hosted, linux, arm64] needs: [rust_tests] - timeout-minutes: 20 + timeout-minutes: 30 steps: - name: Checkout uses: actions/checkout@v5 @@ -56,8 +56,9 @@ jobs: ${{ runner.temp }}/cargo/registry/cache/ ${{ runner.temp }}/cargo/git/db/ target/ - key: cargo-ci-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('**/Cargo.lock', 'rust-toolchain.toml') }} + key: cargo-ci-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('**/Cargo.lock', 'rust-toolchain.toml') }}-${{ github.sha }} restore-keys: | + cargo-ci-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('**/Cargo.lock', 'rust-toolchain.toml') }}- cargo-ci-${{ runner.os }}-${{ runner.arch }}- - name: gunbc ci (.dag witnesses + gates) run: | @@ -137,8 +138,9 @@ jobs: ${{ runner.temp }}/cargo/registry/cache/ ${{ runner.temp }}/cargo/git/db/ target/ - key: cargo-ci-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('**/Cargo.lock', 'rust-toolchain.toml') }} + key: cargo-ci-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('**/Cargo.lock', 'rust-toolchain.toml') }}-${{ github.sha }} restore-keys: | + cargo-ci-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('**/Cargo.lock', 'rust-toolchain.toml') }}- cargo-ci-${{ runner.os }}-${{ runner.arch }}- - name: Install cargo-nextest run: | @@ -240,8 +242,9 @@ jobs: ${{ runner.temp }}/cargo/registry/cache/ ${{ runner.temp }}/cargo/git/db/ target/ - key: cargo-ci-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('**/Cargo.lock', 'rust-toolchain.toml') }} + key: cargo-ci-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('**/Cargo.lock', 'rust-toolchain.toml') }}-${{ github.sha }} restore-keys: | + cargo-ci-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('**/Cargo.lock', 'rust-toolchain.toml') }}- cargo-ci-${{ runner.os }}-${{ runner.arch }}- - name: Deploy dashboard to srv1 (live_deploy_apply_srv1_wet) run: | diff --git a/dag/gunbc/ci_workflow.dag b/dag/gunbc/ci_workflow.dag index 77f981ce260..faa59ceeaa1 100644 --- a/dag/gunbc/ci_workflow.dag +++ b/dag/gunbc/ci_workflow.dag @@ -270,10 +270,10 @@ fn ci_deploy_step(stage: DeployStage) -> Step { data gunbc_ci_job_timeout_policy_minutes: Int = 10 -data gunbc_ci_floor_job_timeout_policy_minutes: Int = 20 +data gunbc_ci_floor_job_timeout_policy_minutes: Int = 30 data gunbc_ci_job_timeout_policy_disposition: Disposition = Terminal { - reason: "gunbc CI timeout policy (operator, 2026-07-04/05): two lanes, one disposition. rust_tests + deploy stay at gunbc_ci_job_timeout_policy_minutes (10) โ€” deliberate inversion of the prior 30โ†’50โ†’90min ratchet; the budget is a forcing function until resolve memoization lands (measured 2026-07-04: plan-closure resolve 15min+, compile-clean ~48min on ARM). ci floor alone uses gunbc_ci_floor_job_timeout_policy_minutes (20): not a ratchet step โ€” ci needs rust_tests (warm target/ cache), then rebuild + batch-1 dag_compile_clean; 10min killed mid-compile with zero floor signal (PR #6239, 2026-07-05). Dissolves when floor-shared-compute-memoization lands and batch-1 fits 10min; then floor reverts to the single 10min constant. default_job_timeout_minutes (extdeps/github/actions.dag) still models GitHub's 360min platform default." + reason: "gunbc CI timeout policy (operator, 2026-07-04/05): two lanes, one disposition. rust_tests + deploy stay at gunbc_ci_job_timeout_policy_minutes (10) โ€” deliberate inversion of the prior 30โ†’50โ†’90min ratchet; the budget is a forcing function until resolve memoization lands (measured 2026-07-04: plan-closure resolve 15min+, compile-clean ~48min on ARM). ci floor alone uses gunbc_ci_floor_job_timeout_policy_minutes (30): interim budget โ€” ci needs rust_tests then batch-1 dag_compile_clean; 20min still timed out mid-batch-1 after a 14min cold cargo rebuild (PR #6239, 2026-07-05). Cargo cache key scopes github.sha so rust_tests saves warmed target/ and ci restores it (lockfile-only keys skipped save on hit, forcing cold rebuild). Dissolves when floor-shared-compute-memoization lands and batch-1 fits 10min; then floor reverts to the single 10min constant. default_job_timeout_minutes (extdeps/github/actions.dag) still models GitHub's 360min platform default." } fn ci_deploy_job(stage: DeployStage) -> Job { diff --git a/dag/gunbc/ci_workflow_expressions.dag b/dag/gunbc/ci_workflow_expressions.dag index be406586bbd..ccdc352854e 100644 --- a/dag/gunbc/ci_workflow_expressions.dag +++ b/dag/gunbc/ci_workflow_expressions.dag @@ -56,12 +56,21 @@ data ci_cache_key_template: ExpressionTemplate = { LiteralText { text: "-" }, Interpolation { expression: runner_arch() }, LiteralText { text: "-" }, - Interpolation { expression: ci_cache_hash_files() } + Interpolation { expression: ci_cache_hash_files() }, + LiteralText { text: "-" }, + Interpolation { expression: ContextAccess { context: Github, path: ["sha"] } } ] } data ci_cache_restore_keys_template: ExpressionTemplate = { segments: [ + LiteralText { text: "cargo-ci-" }, + Interpolation { expression: runner_os() }, + LiteralText { text: "-" }, + Interpolation { expression: runner_arch() }, + LiteralText { text: "-" }, + Interpolation { expression: ci_cache_hash_files() }, + LiteralText { text: "-\n" }, LiteralText { text: "cargo-ci-" }, Interpolation { expression: runner_os() }, LiteralText { text: "-" }, From 523c8fbe9466e3f6234b01bf6529403612118e64 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Sun, 5 Jul 2026 03:10:08 +0000 Subject: [PATCH 17/32] fix(ci): revert floor timeout ratchet; hold all lanes at 10m Remove gunbc_ci_floor_job_timeout_policy_minutes (30/20m interim bumps). Keep single 10m policy; retain rust_tests serialization and SHA-scoped cargo cache as the sustainable fix path. Co-authored-by: Cursor --- .github/workflows/ci.yml | 2 +- dag/gunbc/ci_workflow.dag | 6 ++---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1a9cb4aff75..bc1d41ad352 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -19,7 +19,7 @@ jobs: ci: runs-on: [self-hosted, linux, arm64] needs: [rust_tests] - timeout-minutes: 30 + timeout-minutes: 10 steps: - name: Checkout uses: actions/checkout@v5 diff --git a/dag/gunbc/ci_workflow.dag b/dag/gunbc/ci_workflow.dag index faa59ceeaa1..f752ac0e34f 100644 --- a/dag/gunbc/ci_workflow.dag +++ b/dag/gunbc/ci_workflow.dag @@ -270,10 +270,8 @@ fn ci_deploy_step(stage: DeployStage) -> Step { data gunbc_ci_job_timeout_policy_minutes: Int = 10 -data gunbc_ci_floor_job_timeout_policy_minutes: Int = 30 - data gunbc_ci_job_timeout_policy_disposition: Disposition = Terminal { - reason: "gunbc CI timeout policy (operator, 2026-07-04/05): two lanes, one disposition. rust_tests + deploy stay at gunbc_ci_job_timeout_policy_minutes (10) โ€” deliberate inversion of the prior 30โ†’50โ†’90min ratchet; the budget is a forcing function until resolve memoization lands (measured 2026-07-04: plan-closure resolve 15min+, compile-clean ~48min on ARM). ci floor alone uses gunbc_ci_floor_job_timeout_policy_minutes (30): interim budget โ€” ci needs rust_tests then batch-1 dag_compile_clean; 20min still timed out mid-batch-1 after a 14min cold cargo rebuild (PR #6239, 2026-07-05). Cargo cache key scopes github.sha so rust_tests saves warmed target/ and ci restores it (lockfile-only keys skipped save on hit, forcing cold rebuild). Dissolves when floor-shared-compute-memoization lands and batch-1 fits 10min; then floor reverts to the single 10min constant. default_job_timeout_minutes (extdeps/github/actions.dag) still models GitHub's 360min platform default." + reason: "gunbc CI timeout policy (operator, 2026-07-04/05): all lanes at gunbc_ci_job_timeout_policy_minutes (10) โ€” deliberate inversion of the prior 30โ†’50โ†’90min ratchet; the budget is a forcing function until resolve memoization lands (measured 2026-07-04: plan-closure resolve 15min+, compile-clean ~48min on ARM). ci needs rust_tests (serialization + github.sha-scoped cargo cache so warmed target/ is saved and restored; lockfile-only keys skipped save on hit). No separate floor timeout ratchet โ€” dissolves when floor-shared-compute-memoization lands and batch-1 fits 10min. default_job_timeout_minutes (extdeps/github/actions.dag) still models GitHub's 360min platform default." } fn ci_deploy_job(stage: DeployStage) -> Job { @@ -308,7 +306,7 @@ fn ci_job() -> Job { outputs: none, if_condition: none, strategy: none, - timeout_minutes: Present { value: gunbc_ci_floor_job_timeout_policy_minutes }, + timeout_minutes: Present { value: gunbc_ci_job_timeout_policy_minutes }, continue_on_error: none, concurrency: none } From 182e4a085a3d761e22f9366753f0dfd903a33b49 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Sun, 5 Jul 2026 03:30:24 +0000 Subject: [PATCH 18/32] revert(ci): drop PR-local CI workflow experiments from #6239 CI serialization/SHA-cache changes belong in the cool-hawk timeout lane, not P3+P5. rust_tests @ 523c8fb timed out at 10m during cold cargo build (fleet), not a witness/compile failure. Restore main ci_workflow + ci.yml. Co-authored-by: Cursor --- .github/workflows/ci.yml | 11 +++-------- dag/gunbc/ci_workflow.dag | 4 ++-- dag/gunbc/ci_workflow_expressions.dag | 11 +---------- 3 files changed, 6 insertions(+), 20 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bc1d41ad352..a94b20d4ee0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -18,7 +18,6 @@ env: jobs: ci: runs-on: [self-hosted, linux, arm64] - needs: [rust_tests] timeout-minutes: 10 steps: - name: Checkout @@ -56,9 +55,8 @@ jobs: ${{ runner.temp }}/cargo/registry/cache/ ${{ runner.temp }}/cargo/git/db/ target/ - key: cargo-ci-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('**/Cargo.lock', 'rust-toolchain.toml') }}-${{ github.sha }} + key: cargo-ci-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('**/Cargo.lock', 'rust-toolchain.toml') }} restore-keys: | - cargo-ci-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('**/Cargo.lock', 'rust-toolchain.toml') }}- cargo-ci-${{ runner.os }}-${{ runner.arch }}- - name: gunbc ci (.dag witnesses + gates) run: | @@ -138,9 +136,8 @@ jobs: ${{ runner.temp }}/cargo/registry/cache/ ${{ runner.temp }}/cargo/git/db/ target/ - key: cargo-ci-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('**/Cargo.lock', 'rust-toolchain.toml') }}-${{ github.sha }} + key: cargo-ci-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('**/Cargo.lock', 'rust-toolchain.toml') }} restore-keys: | - cargo-ci-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('**/Cargo.lock', 'rust-toolchain.toml') }}- cargo-ci-${{ runner.os }}-${{ runner.arch }}- - name: Install cargo-nextest run: | @@ -242,9 +239,8 @@ jobs: ${{ runner.temp }}/cargo/registry/cache/ ${{ runner.temp }}/cargo/git/db/ target/ - key: cargo-ci-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('**/Cargo.lock', 'rust-toolchain.toml') }}-${{ github.sha }} + key: cargo-ci-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('**/Cargo.lock', 'rust-toolchain.toml') }} restore-keys: | - cargo-ci-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('**/Cargo.lock', 'rust-toolchain.toml') }}- cargo-ci-${{ runner.os }}-${{ runner.arch }}- - name: Deploy dashboard to srv1 (live_deploy_apply_srv1_wet) run: | @@ -272,4 +268,3 @@ jobs: "$ROOT/target/release/claim_executor" --verify-build-artifacts "$ROOT/target/release/claim_executor" "$ROOT/target/release/gunbc" sccache --show-stats 2>/dev/null || true "$ROOT/target/release/gunbc" run --source-root dag --source-root src/v2 --entry dag/gunbc/live_deploy/apply.dag --function live_deploy_apply_srv1_wet - diff --git a/dag/gunbc/ci_workflow.dag b/dag/gunbc/ci_workflow.dag index f752ac0e34f..1b51d721b9f 100644 --- a/dag/gunbc/ci_workflow.dag +++ b/dag/gunbc/ci_workflow.dag @@ -271,7 +271,7 @@ fn ci_deploy_step(stage: DeployStage) -> Step { data gunbc_ci_job_timeout_policy_minutes: Int = 10 data gunbc_ci_job_timeout_policy_disposition: Disposition = Terminal { - reason: "gunbc CI timeout policy (operator, 2026-07-04/05): all lanes at gunbc_ci_job_timeout_policy_minutes (10) โ€” deliberate inversion of the prior 30โ†’50โ†’90min ratchet; the budget is a forcing function until resolve memoization lands (measured 2026-07-04: plan-closure resolve 15min+, compile-clean ~48min on ARM). ci needs rust_tests (serialization + github.sha-scoped cargo cache so warmed target/ is saved and restored; lockfile-only keys skipped save on hit). No separate floor timeout ratchet โ€” dissolves when floor-shared-compute-memoization lands and batch-1 fits 10min. default_job_timeout_minutes (extdeps/github/actions.dag) still models GitHub's 360min platform default." + reason: "gunbc CI policy (operator, 2026-07-04): CI jobs time out at 10 minutes - a deliberate INVERSION of the prior ratchet (30min alarm -> 50min -> 90min, each raise buying headroom for the unfixed resolve cost; at 90min the queue backed up and runs still timed out delivering zero signal). The budget is now a forcing function: the floor must FIT it, and the itemized [t+Xs] phase marks say exactly which phase does not (measured 2026-07-04: plan-closure resolve was 15min+, whole-tree compile-clean ~48min on the ARM fleet - both resolve-dominated; see the floor-shared-compute-memoization plan). Every job stays red-by-timeout until resolve memoization lands; that red is honest, unlike a 90min lane that never completed. default_job_timeout_minutes (extdeps/github/actions.dag) still models GitHub's 360min platform default - workflow-layer policy on top." } fn ci_deploy_job(stage: DeployStage) -> Job { @@ -301,7 +301,7 @@ fn ci_job() -> Job { name: none, runner: gunbc_ci_runner_spec(), steps: concat(ci_prelude_steps(), [ci_floor_step(), ci_merge_admission_gate_step()]), - needs: ["rust_tests"], + needs: [], env: none, outputs: none, if_condition: none, diff --git a/dag/gunbc/ci_workflow_expressions.dag b/dag/gunbc/ci_workflow_expressions.dag index ccdc352854e..be406586bbd 100644 --- a/dag/gunbc/ci_workflow_expressions.dag +++ b/dag/gunbc/ci_workflow_expressions.dag @@ -56,21 +56,12 @@ data ci_cache_key_template: ExpressionTemplate = { LiteralText { text: "-" }, Interpolation { expression: runner_arch() }, LiteralText { text: "-" }, - Interpolation { expression: ci_cache_hash_files() }, - LiteralText { text: "-" }, - Interpolation { expression: ContextAccess { context: Github, path: ["sha"] } } + Interpolation { expression: ci_cache_hash_files() } ] } data ci_cache_restore_keys_template: ExpressionTemplate = { segments: [ - LiteralText { text: "cargo-ci-" }, - Interpolation { expression: runner_os() }, - LiteralText { text: "-" }, - Interpolation { expression: runner_arch() }, - LiteralText { text: "-" }, - Interpolation { expression: ci_cache_hash_files() }, - LiteralText { text: "-\n" }, LiteralText { text: "cargo-ci-" }, Interpolation { expression: runner_os() }, LiteralText { text: "-" }, From 401203189cb27b03b450f2436af5000dd5b4d9b8 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Sun, 5 Jul 2026 03:57:56 +0000 Subject: [PATCH 19/32] Revert "merge main: pick up P1 emit (#6242) + resolver fixes for floor budget" This reverts commit 53c3751eb1180396c45dc2a4dad4aab11bd66c5a, reversing changes made to 182e4a085a3d761e22f9366753f0dfd903a33b49. --- dag/gunbc/stage0_emit_model.dag | 2 + dag/test/claim/builtin_get_resolver_test.dag | 22 -- .../claim/stage0_emit_model_witness_test.dag | 2 +- src/v1/04_method.dag | 1 - src/v1/04_resolve.dag | 58 ++- src/v1/dag_collect.dag | 118 +++--- src/v1/dag_collect_support.dag | 30 +- src/v1/stage0/src/bin/regen_stage0.rs | 8 +- src/v1/stage0/src/v1_compiler_dag_collect.rs | 340 +++++++++--------- .../src/v1_compiler_dag_collect_support.rs | 138 ++----- src/v1/stage0/src/v1_compiler_infer_method.rs | 5 - 11 files changed, 299 insertions(+), 425 deletions(-) delete mode 100644 dag/test/claim/builtin_get_resolver_test.dag diff --git a/dag/gunbc/stage0_emit_model.dag b/dag/gunbc/stage0_emit_model.dag index fd42bc7df9f..61b1291e69e 100644 --- a/dag/gunbc/stage0_emit_model.dag +++ b/dag/gunbc/stage0_emit_model.dag @@ -56,6 +56,8 @@ data generated_stage0_files: List = [ "v1_compiler_compile.rs", "v1_compiler_compiler_tests_rust.rs", "v1_compiler_complexity.rs", + "v1_compiler_dag_collect.rs", + "v1_compiler_dag_collect_support.rs", "v1_compiler_effect_derivation.rs", "v1_compiler_emit.rs", "v1_compiler_emit_core_support.rs", diff --git a/dag/test/claim/builtin_get_resolver_test.dag b/dag/test/claim/builtin_get_resolver_test.dag deleted file mode 100644 index 1845d782e41..00000000000 --- a/dag/test/claim/builtin_get_resolver_test.dag +++ /dev/null @@ -1,22 +0,0 @@ -module test.claim.builtin_get_resolver - -fn witness_get_in_bounds_returns_element() -> Bool { - match get(xs: [10, 20, 30], index: 1) { - Present { value: v } => v == 20 - Absent => false - } -} - -fn witness_get_out_of_bounds_is_absent() -> Bool { - match get(xs: [10, 20, 30], index: 7) { - Present { value: _ } => false - Absent => true - } -} - -fn witness_get_negative_index_is_absent() -> Bool { - match get(xs: [10, 20, 30], index: 0 - 1) { - Present { value: _ } => false - Absent => true - } -} diff --git a/dag/test/claim/stage0_emit_model_witness_test.dag b/dag/test/claim/stage0_emit_model_witness_test.dag index 6a50133124b..c42ddfb76e3 100644 --- a/dag/test/claim/stage0_emit_model_witness_test.dag +++ b/dag/test/claim/stage0_emit_model_witness_test.dag @@ -8,7 +8,7 @@ import gunbc.stage0_emit_model { } fn w_generated_roster_count() -> Bool { - generated_stage0_file_count() == 90 + generated_stage0_file_count() == 92 } fn w_hand_maintained_not_classified_generated() -> Bool { diff --git a/src/v1/04_method.dag b/src/v1/04_method.dag index cb5cd096453..fd2345a797e 100644 --- a/src/v1/04_method.dag +++ b/src/v1/04_method.dag @@ -88,7 +88,6 @@ fn builtin_function_registry() -> Map { let m = map_insert(m, "map_keys", list_of_type_variable(id: "collection_element")) let m = map_insert(m, "map_values", list_of_type_variable(id: "collection_element")) let m = map_insert(m, "reverse", list_of_type_variable(id: "collection_element")) - let m = map_insert(m, "get", with_optional_cardinality(n: type_variable_node(id: "collection_element"))) let m = map_insert(m, "list_push", list_of_type_variable(id: "collection_element")) let m = map_insert(m, "hash_combine", hash_type) let m = map_insert(m, "atom_identity_hash", hash_type) diff --git a/src/v1/04_resolve.dag b/src/v1/04_resolve.dag index 08894254be4..39486314863 100644 --- a/src/v1/04_resolve.dag +++ b/src/v1/04_resolve.dag @@ -230,47 +230,33 @@ fn resolve_node(n: Node, env: TypeEnv, module_name: String) -> NodeResolveResult data parameterized_use_site_prefers_parameterized_decl: String = "A use site WITH type arguments (children > 0) whose primary name lookup lands on a PARAMLESS decl is definitionally mis-resolved - type args imply a generic declaration. The concrete seam: the kernel prelude binding for a name (e.g. the kernel Optional) shadows an explicitly imported user generic of the same name in the ancestry rung, so v2's Optional consumers resolved the paramless kernel decl, is_user_generic_use_site went false, the sig return was never expanded, and pattern bindings went error-typed with zero diagnostics (the interpreted-parse cascade; pre-existing on main). Retry the DIRECT import parents (str_bindings) for a parameterized decl before accepting the paramless one - the params filter naturally excludes the kernel parent, and the ancestry rung itself carries the kernel-shadowed entry so it cannot serve as the retry source." -fn lookup_ancestry_parameterized_decl(env: TypeEnv, brand: String) -> Node? { - match env.parents |> filter(parent => - match map_get(parent.str_bindings, brand) { - Present { value: b } => b.resolved.params |> count > 0 - Absent => false - } - ) |> first { - Present { value: parent } => - match map_get(parent.str_bindings, brand) { - Present { value: b } => Present { value: b.resolved } - Absent => Absent - } - Absent => Absent - } -} - -fn resolve_paramless_generic_use_decl(env: TypeEnv, n: Node, decl: Node, brand: String) -> Node { - match n.children |> count > 0 { - true => - match lookup_ancestry_parameterized_decl(env: env, brand: brand) { - Present { value: user_decl } => user_decl - Absent => - match n.name != "" && n.name != brand { - true => - match lookup_type_by_name(env: env, name: n.name) { - Present { value: structural } => structural - Absent => decl - } - false => decl - } - } - false => decl - } -} - fn resolve_generic_use_decl(env: TypeEnv, n: Node) -> Node { let brand = authored_name(env: env, node: n) match lookup_type_for(env: env, node: n) { Present { value: decl } => if decl.params |> count > 0 { decl } - else { resolve_paramless_generic_use_decl(env: env, n: n, decl: decl, brand: brand) } + else if n.children |> count > 0 { + let ancestry_generic = fold(env.parents, init: none, f: (acc, parent) => + if acc != none { acc } + else { + match map_get(parent.str_bindings, brand) { + Present { value: b } => + if b.resolved.params |> count > 0 { Present { value: b.resolved } } else { acc } + Absent => acc + } + } + ) + match ancestry_generic { + Present { value: user_decl } => user_decl + Absent => + if n.name != "" && n.name != brand { + match lookup_type_by_name(env: env, name: n.name) { + Present { value: structural } => structural + Absent => decl + } + } else { decl } + } + } else { decl } Absent => match lookup_type_by_name(env: env, name: n.name) { Present { value: decl } => decl diff --git a/src/v1/dag_collect.dag b/src/v1/dag_collect.dag index 13fbdad22fb..d771b31833c 100644 --- a/src/v1/dag_collect.dag +++ b/src/v1/dag_collect.dag @@ -9,9 +9,8 @@ import v1.std.core { import_is_all } import v1.compiler.dag_collect_support { - DagCollectAcc, DagCollectSlot, - dag_collect_fp_memo_reset, dag_collect_pack_slots, dag_collect_slot_seq, - dag_node_surface_fingerprint, dag_node_surface_fingerprint_memo + DagCollectAcc, DagCollectPending, + dag_node_surface_fingerprint, dag_node_key_collision_error } import v1.compiler.infer_items { ResolvedGraph, TypedModule } @@ -53,7 +52,7 @@ fn dag_node_collection_anchor(node: Node) -> Node { fn dag_node_key(node: Node) -> String { let anchor = dag_node_collection_anchor(node: node) if anchor.span.start == 0 && anchor.span.end == 0 { - concat(":0..0:", dag_node_surface_fingerprint_memo(node: anchor)) + concat(":0..0:", dag_node_surface_fingerprint(node: anchor)) } else { concat( anchor.span.file, ":", to_string(i: anchor.span.start), "..", to_string(i: anchor.span.end), @@ -65,90 +64,97 @@ fn dag_node_key(node: Node) -> String { } fn dag_node_fingerprint(node: Node) -> String { - dag_node_surface_fingerprint_memo(node: dag_node_collection_anchor(node: node)) + dag_node_surface_fingerprint(node: dag_node_collection_anchor(node: node)) } -fn dag_collect_nodes_list(nodes: List, slots: Map, collision_errors: List) -> Map { - nodes |> fold(init: slots, f: (s, n) => dag_collect_insert_slots(node: n, slots: s, collision_errors: collision_errors)) +fn dag_collect_nodes_list(nodes: List, acc: DagCollectAcc) -> DagCollectAcc { + nodes |> fold(init: acc, f: (a, n) => dag_collect_insert(node: n, acc: a)) } -fn dag_collect_optional_node(value: Node?, slots: Map, collision_errors: List) -> Map { +fn dag_collect_optional_node(value: Node?, acc: DagCollectAcc) -> DagCollectAcc { match value { - Present { value: inner } => dag_collect_insert_slots(node: inner, slots: slots, collision_errors: collision_errors) - Absent => slots + Present { value: inner } => dag_collect_insert(node: inner, acc: acc) + Absent => acc } } -fn dag_collect_inferred(value: InferredNode?, slots: Map, collision_errors: List) -> Map { +fn dag_collect_inferred(value: InferredNode?, acc: DagCollectAcc) -> DagCollectAcc { match value { - Present { value: Resolved { node: n } } => dag_collect_insert_slots(node: n, slots: slots, collision_errors: collision_errors) - _ => slots + Present { value: Resolved { node: n } } => dag_collect_insert(node: n, acc: acc) + _ => acc } } -fn dag_collect_match_pattern(pattern: MatchPattern, slots: Map, collision_errors: List) -> Map { +fn dag_collect_match_pattern(pattern: MatchPattern, acc: DagCollectAcc) -> DagCollectAcc { match pattern { - Bind { name: _ } => slots - LitPattern { value: _ } => slots + Bind { name: _ } => acc + LitPattern { value: _ } => acc VariantPattern { name: _, parent_enum: _, field_bindings: field_bindings } => - field_bindings |> fold(init: slots, f: (s, fb) => dag_collect_insert_slots(node: fb, slots: s, collision_errors: collision_errors)) - Wildcard => slots + field_bindings |> fold(init: acc, f: (a, fb) => dag_collect_insert(node: fb, acc: a)) + Wildcard => acc } } -fn dag_collect_node_tree(node: Node, slots: Map, collision_errors: List) -> Map { - let slots = dag_collect_nodes_list(nodes: node.children, slots: slots, collision_errors: collision_errors) - let slots = if is_module_shell_node(n: node) { - slots +fn dag_collect_node_tree(node: Node, acc: DagCollectAcc) -> DagCollectAcc { + let acc = dag_collect_nodes_list(nodes: node.children, acc: acc) + let acc = if is_module_shell_node(n: node) { + acc } else { - dag_collect_nodes_list(nodes: node.params, slots: slots, collision_errors: collision_errors) + dag_collect_nodes_list(nodes: node.params, acc: acc) } - let slots = dag_collect_nodes_list(nodes: node.uses, slots: slots, collision_errors: collision_errors) - let slots = dag_collect_optional_node(value: node.body, slots: slots, collision_errors: collision_errors) - let slots = dag_collect_optional_node(value: node.transport, slots: slots, collision_errors: collision_errors) - let slots = dag_collect_nodes_list(nodes: node.properties, slots: slots, collision_errors: collision_errors) - let slots = dag_collect_optional_node(value: node.type_annotation, slots: slots, collision_errors: collision_errors) - let slots = dag_collect_inferred(value: node.inferred, slots: slots, collision_errors: collision_errors) + let acc = dag_collect_nodes_list(nodes: node.uses, acc: acc) + let acc = dag_collect_optional_node(value: node.body, acc: acc) + let acc = dag_collect_optional_node(value: node.transport, acc: acc) + let acc = dag_collect_nodes_list(nodes: node.properties, acc: acc) + let acc = dag_collect_optional_node(value: node.type_annotation, acc: acc) + let acc = dag_collect_inferred(value: node.inferred, acc: acc) match node.match_pattern { - Present { value: p } => dag_collect_match_pattern(pattern: p, slots: slots, collision_errors: collision_errors) - Absent => slots + Present { value: p } => dag_collect_match_pattern(pattern: p, acc: acc) + Absent => acc } } -data dag_collect_synthetic_key_note: String = "Synthetic span-0 keys embed the fingerprint; the Absent branch recovers it by substring and must not re-hash." - -fn dag_collect_insert_slots(node: Node, slots: Map, collision_errors: List) -> Map { +fn dag_collect_insert(node: Node, acc: DagCollectAcc) -> DagCollectAcc { let anchor = dag_node_collection_anchor(node: node) let key = dag_node_key(node: anchor) - match map_get(slots, key) { - Present { value: _ } => slots - Absent => { - let fp = if anchor.span.start == 0 && anchor.span.end == 0 { - substring(s: key, start: 6, end: string_length(s: key)) + let fp = dag_node_fingerprint(node: anchor) + match map_get(acc.seen, key) { + Present { value: prior } => + if prior == fp { + acc + } else if anchor.span.start == 0 && anchor.span.end == 0 { + DagCollectAcc { + seen: acc.seen, + order: acc.order, + collision_errors: list_push(acc.collision_errors, dag_node_key_collision_error(key: key, span: anchor.span)) + } } else { - dag_node_fingerprint(node: anchor) + acc } - let seq = dag_collect_slot_seq(slots: slots) - dag_collect_node_tree( - node: anchor, - slots: map_insert(slots, key, DagCollectSlot { key: key, fp: fp, node: anchor, seq: seq }), - collision_errors: collision_errors - ) + Absent => { + [DagCollectPending { anchor: anchor, key: key, fp: fp }] + |> fold( + init: acc, + f: (inner, pending) => + dag_collect_node_tree( + node: pending.anchor, + acc: DagCollectAcc { + seen: map_insert(inner.seen, pending.key, pending.fp), + order: list_push(inner.order, pending.anchor), + collision_errors: inner.collision_errors + } + ) + ) } } } -fn dag_collect_from_module(module: TypedModule, slots: Map, collision_errors: List) -> Map { - let slots = dag_collect_insert_slots(node: module.module, slots: slots, collision_errors: collision_errors) - module.items |> fold(init: slots, f: (s, item) => dag_collect_insert_slots(node: item, slots: s, collision_errors: collision_errors)) +fn dag_collect_from_module(module: TypedModule, acc: DagCollectAcc) -> DagCollectAcc { + let acc = dag_collect_insert(node: module.module, acc: acc) + module.items |> fold(init: acc, f: (a, item) => dag_collect_insert(node: item, acc: a)) } -data dag_collect_collision_threading_dissolve_on: String = "Dissolve-on: drop collision_errors threading once synthetic-key construction proof is a typed wall (key embeds fp โ‡’ key-equal โ‡’ fp-equal); kept for emit_dag gate shape." - fn collect_dag_nodes(typed: ResolvedGraph) -> DagCollectAcc { - let _memo_reset = dag_collect_fp_memo_reset() - let collision_errors = [] - let slots = typed.modules - |> fold(init: empty_map(), f: (s, m) => dag_collect_from_module(module: m, slots: s, collision_errors: collision_errors)) - dag_collect_pack_slots(slots: slots, collision_errors: collision_errors) + typed.modules + |> fold(init: DagCollectAcc { seen: empty_map(), order: [], collision_errors: [] }, f: (acc, m) => dag_collect_from_module(module: m, acc: acc)) } diff --git a/src/v1/dag_collect_support.dag b/src/v1/dag_collect_support.dag index 0bfcab243d3..5a42487a2ac 100644 --- a/src/v1/dag_collect_support.dag +++ b/src/v1/dag_collect_support.dag @@ -9,28 +9,16 @@ import v1.std.core { } import v1.compiler.emit { escape_json_string } -type DagCollectSlot { - key: String - fp: String - node: Node - seq: Int -} - type DagCollectAcc { seen: Map order: List collision_errors: List } -fn dag_collect_slot_seq(slots: Map) -> Int { - slots |> count -} - -fn dag_collect_pack_slots(slots: Map, collision_errors: List) -> DagCollectAcc { - let ordered_slots = slots |> map_values |> sort_by(s => s.seq) - let order = ordered_slots |> map(s => s.node) - let seen = ordered_slots |> fold(init: empty_map(), f: (acc, s) => map_insert(acc, s.key, s.fp)) - DagCollectAcc { seen: seen, order: order, collision_errors: collision_errors } +type DagCollectPending { + anchor: Node + key: String + fp: String } fn json_quote(s: String) -> String { @@ -118,16 +106,6 @@ fn dag_node_surface_fingerprint(node: Node) -> String { dag_node_surface_fingerprint_rec(node: node) } -data dag_collect_fp_memo_dissolve_on: String = "Dissolve-on: ground on std.content_hash + node-keyed memo table once v2 adoption clears for this stage. Realization threads a per-collect memo reset at collect_dag_nodes entry; Rust seed uses Rc::as_ptr until then." - -fn dag_collect_fp_memo_reset() -> Bool { - true -} - -fn dag_node_surface_fingerprint_memo(node: Node) -> String { - dag_node_surface_fingerprint(node: node) -} - fn dag_node_key_collision_error(key: String, span: SourceSpan) -> ErrorNode { let synthetic = span.start == 0 && span.end == 0 let detail = if synthetic { diff --git a/src/v1/stage0/src/bin/regen_stage0.rs b/src/v1/stage0/src/bin/regen_stage0.rs index 60ab5a5e5c8..d584dd5169f 100644 --- a/src/v1/stage0/src/bin/regen_stage0.rs +++ b/src/v1/stage0/src/bin/regen_stage0.rs @@ -73,6 +73,8 @@ const GENERATED_STAGE0_FILES: &[&str] = &[ "v1_compiler_compile.rs", "v1_compiler_compiler_tests_rust.rs", "v1_compiler_complexity.rs", + "v1_compiler_dag_collect.rs", + "v1_compiler_dag_collect_support.rs", "v1_compiler_effect_derivation.rs", "v1_compiler_emit.rs", "v1_compiler_emit_core_support.rs", @@ -127,12 +129,6 @@ const HAND_MAINTAINED_STAGE0_FILES: &[&str] = &[ // Phase-local heartbeat for long floor walks (2026-07-04); its own header // carries the dissolution trigger (realization_measurement_loop Phase 0). "phase_profile.rs", - // P1 dag_collect perf (#6242): slot-map + fp/key memo live only in hand-edited seed until - // regen is unblocked (04_resolve.dag) and std.content_hash memo rows emit. Regen without - // this guard reverted to DagCollectPending / O(Mยฒ) three times (90ad8fa, 42c8f3d, โ€ฆ). - // Dissolution: regen_stage0 --verify green with .dag model = emitted seed; drop from here. - "v1_compiler_dag_collect.rs", - "v1_compiler_dag_collect_support.rs", ]; /// Hand-maintained stage0 support living in subdirectories (not flat `.rs` files). diff --git a/src/v1/stage0/src/v1_compiler_dag_collect.rs b/src/v1/stage0/src/v1_compiler_dag_collect.rs index ddc2323061c..f8c6341525f 100644 --- a/src/v1/stage0/src/v1_compiler_dag_collect.rs +++ b/src/v1/stage0/src/v1_compiler_dag_collect.rs @@ -2,11 +2,9 @@ // Source module: v1.compiler.dag_collect pub use crate::v1_compiler_dag_collect_support::{ - dag_collect_fp_memo_reset, dag_collect_pack_slots, dag_node_collection_anchor, - dag_node_is_resolved_identity_shell, dag_node_surface_fingerprint, - dag_node_surface_fingerprint_memo, + dag_node_key_collision_error, dag_node_surface_fingerprint, }; -pub use crate::v1_compiler_dag_collect_support::{DagCollectAcc, DagCollectSlot}; +pub use crate::v1_compiler_dag_collect_support::{DagCollectAcc, DagCollectPending}; pub use crate::v1_compiler_infer_items::{ResolvedGraph, TypedModule}; use crate::v1_rt; use crate::v1_rt::Witness; @@ -16,48 +14,13 @@ use crate::v1_std_core::Connective::NoConnective; use crate::v1_std_core::ExprData::*; use crate::v1_std_core::InferredNode::Resolved; use crate::v1_std_core::MatchPattern::*; -pub use crate::v1_std_core::{Connective, ErrorNode, ExprData, InferredNode, MatchPattern, Node}; +pub use crate::v1_std_core::{Connective, ExprData, InferredNode, MatchPattern, Node}; use crate::NonEmptyBTreeSet; use crate::NonEmptyVec; -use std::cell::RefCell; use std::collections::BTreeSet; use std::collections::HashMap; use std::rc::Rc; -thread_local! { - static DAG_NODE_KEY_MEMO: RefCell> = RefCell::new(HashMap::new()); -} - -fn dag_collect_key_memo_reset() { - DAG_NODE_KEY_MEMO.with(|memo| memo.borrow_mut().clear()); -} - -fn dag_node_key_uncached(anchor: Rc) -> String { - if ((anchor.span.clone().start.clone() == 0) && (anchor.span.clone().end.clone() == 0)) { - v1_rt::concat( - ":0..0:".to_string(), - dag_node_surface_fingerprint_memo(anchor.clone()), - ) - } else { - v1_rt::concat( - v1_rt::concat( - v1_rt::concat( - v1_rt::concat( - v1_rt::concat(anchor.span.clone().file.clone(), ":".to_string()), - (anchor.span.clone().start.clone()).to_string(), - ), - "..".to_string(), - ), - (anchor.span.clone().end.clone()).to_string(), - ), - match anchor.ident.clone() { - Some(id) => v1_rt::concat(":".to_string(), (id.clone()).to_string()), - None => "".to_string(), - }, - ) - } -} - pub fn is_import_slot_node(n: Rc) -> bool { (import_is_all(n.clone()) || (((((n.params.clone().len() as i64) == 0) && (n.ident_span.clone() != None)) @@ -84,166 +47,221 @@ pub fn is_module_shell_node(n: Rc) -> bool { }) } +pub fn dag_node_is_resolved_identity_shell(node: Rc) -> bool { + match (*node.expr_data.clone()).clone() { + ExprData::NoExprData => match node.inferred.clone().as_deref().cloned() { + Some(InferredNode::Resolved { node: _, .. }) => { + ((((node.body.clone() == None) && (node.transport.clone() == None)) + && ((node.children.clone().len() as i64) == 0)) + && ((node.params.clone().len() as i64) == 0)) + } + _ => false, + }, + _ => false, + } +} + +pub fn dag_node_collection_anchor(mut node: Rc) -> Rc { + loop { + if dag_node_is_resolved_identity_shell(node.clone()) { + match node.inferred.clone().as_deref().cloned() { + Some(InferredNode::Resolved { node: target, .. }) => { + let __tco_0 = target.clone(); + node = __tco_0; + continue; + } + _ => { + break node.clone(); + } + } + } else { + break node.clone(); + } + } +} + pub fn dag_node_key(node: Rc) -> String { - let anchor = dag_node_collection_anchor(node); - let ptr = Rc::as_ptr(&anchor) as usize; - DAG_NODE_KEY_MEMO.with(|memo| { - if let Some(key) = memo.borrow().get(&ptr) { - return key.clone(); + { + let anchor = dag_node_collection_anchor(node); + if ((anchor.span.clone().start.clone() == 0) && (anchor.span.clone().end.clone() == 0)) { + v1_rt::concat( + ":0..0:".to_string(), + dag_node_surface_fingerprint(anchor.clone()), + ) + } else { + v1_rt::concat( + v1_rt::concat( + v1_rt::concat( + v1_rt::concat( + v1_rt::concat(anchor.span.clone().file.clone(), ":".to_string()), + (anchor.span.clone().start.clone()).to_string(), + ), + "..".to_string(), + ), + (anchor.span.clone().end.clone()).to_string(), + ), + match anchor.ident.clone() { + Some(id) => v1_rt::concat(":".to_string(), (id.clone()).to_string()), + None => "".to_string(), + }, + ) } - let key = dag_node_key_uncached(anchor); - memo.borrow_mut().insert(ptr, key.clone()); - key - }) + } } pub fn dag_node_fingerprint(node: Rc) -> String { - dag_node_surface_fingerprint_memo(dag_node_collection_anchor(node)) + dag_node_surface_fingerprint(dag_node_collection_anchor(node)) } pub fn dag_collect_nodes_list( nodes: Rc>>, - slots: Rc>>, - collision_errors: Rc>>, -) -> Rc>> { - nodes.iter().cloned().fold( - slots, - |s: Rc>>, n: Rc| { - dag_collect_insert_slots(n, s, collision_errors.clone()) - }, - ) + acc: Rc, +) -> Rc { + nodes + .iter() + .cloned() + .fold(acc.clone(), |a: Rc, n: Rc| { + dag_collect_insert(n.clone(), a) + }) } pub fn dag_collect_optional_node( value: Option>, - slots: Rc>>, - collision_errors: Rc>>, -) -> Rc>> { + acc: Rc, +) -> Rc { match value { - Some(inner) => dag_collect_insert_slots(inner, slots, collision_errors), - None => slots, + Some(inner) => dag_collect_insert(inner.clone(), acc), + None => acc, } } pub fn dag_collect_inferred( value: Option>, - slots: Rc>>, - collision_errors: Rc>>, -) -> Rc>> { + acc: Rc, +) -> Rc { match value.as_deref().cloned() { - Some(InferredNode::Resolved { node: n, .. }) => { - dag_collect_insert_slots(n, slots, collision_errors) - } - _ => slots, + Some(InferredNode::Resolved { node: n, .. }) => dag_collect_insert(n.clone(), acc), + _ => acc, } } pub fn dag_collect_match_pattern( pattern: Rc, - slots: Rc>>, - collision_errors: Rc>>, -) -> Rc>> { + acc: Rc, +) -> Rc { match (*pattern).clone() { - MatchPattern::Bind { name: _, .. } => slots, - MatchPattern::LitPattern { value: _, .. } => slots, - MatchPattern::VariantPattern { field_bindings, .. } => { - field_bindings.clone().iter().cloned().fold( - slots, - |s: Rc>>, fb: Rc| { - dag_collect_insert_slots(fb, s, collision_errors.clone()) - }, - ) - } - MatchPattern::Wildcard => slots, + MatchPattern::Bind { name: _, .. } => acc, + MatchPattern::LitPattern { value: _, .. } => acc, + MatchPattern::VariantPattern { field_bindings, .. } => field_bindings + .clone() + .iter() + .cloned() + .fold(acc, |a: Rc, fb: Rc| { + dag_collect_insert(fb.clone(), a) + }), + MatchPattern::Wildcard => acc, } } -pub fn dag_collect_node_tree( - node: Rc, - slots: Rc>>, - collision_errors: Rc>>, -) -> Rc>> { - let slots = dag_collect_nodes_list(node.children.clone(), slots, collision_errors.clone()); - let slots = if is_module_shell_node(node.clone()) { - slots - } else { - dag_collect_nodes_list(node.params.clone(), slots, collision_errors.clone()) - }; - let slots = dag_collect_nodes_list(node.uses.clone(), slots, collision_errors.clone()); - let slots = dag_collect_optional_node(node.body.clone(), slots, collision_errors.clone()); - let slots = dag_collect_optional_node(node.transport.clone(), slots, collision_errors.clone()); - let slots = dag_collect_nodes_list(node.properties.clone(), slots, collision_errors.clone()); - let slots = dag_collect_optional_node( - node.type_annotation.clone(), - slots, - collision_errors.clone(), - ); - let slots = dag_collect_inferred(node.inferred.clone(), slots, collision_errors.clone()); - match node.match_pattern.clone() { - Some(p) => dag_collect_match_pattern(p, slots, collision_errors), - None => slots, +pub fn dag_collect_node_tree(node: Rc, acc: Rc) -> Rc { + { + let acc = dag_collect_nodes_list(node.children.clone(), acc.clone()); + let acc = if is_module_shell_node(node.clone()) { + acc.clone() + } else { + dag_collect_nodes_list(node.params.clone(), acc.clone()) + }; + let acc = dag_collect_nodes_list(node.uses.clone(), acc.clone()); + let acc = dag_collect_optional_node(node.body.clone(), acc.clone()); + let acc = dag_collect_optional_node(node.transport.clone(), acc.clone()); + let acc = dag_collect_nodes_list(node.properties.clone(), acc.clone()); + let acc = dag_collect_optional_node(node.type_annotation.clone(), acc.clone()); + let acc = dag_collect_inferred(node.inferred.clone(), acc.clone()); + match node.match_pattern.clone() { + Some(p) => dag_collect_match_pattern(p.clone(), acc.clone()), + None => acc.clone(), + } } } -pub fn dag_collect_insert_slots( - node: Rc, - slots: Rc>>, - collision_errors: Rc>>, -) -> Rc>> { - let anchor = dag_node_collection_anchor(node); - let key = dag_node_key(anchor.clone()); - match v1_rt::map_get(&slots, key.clone()) { - Some(_) => slots, - None => { - let fp = if ((anchor.span.clone().start.clone() == 0) - && (anchor.span.clone().end.clone() == 0)) - { - v1_rt::substring(&key, 6, v1_rt::string_length(&key)) - } else { - dag_node_fingerprint(anchor.clone()) - }; - let seq = slots.len() as i64; - dag_collect_node_tree( - anchor.clone(), - v1_rt::rc_map_insert( - slots, - key.clone(), - Rc::new(DagCollectSlot { - key, - fp, - node: anchor, - seq, - }), - ), - collision_errors, - ) +pub fn dag_collect_insert(node: Rc, acc: Rc) -> Rc { + { + let anchor = dag_node_collection_anchor(node); + let key = dag_node_key(anchor.clone()); + let fp = dag_node_fingerprint(anchor.clone()); + match v1_rt::map_get(&acc.seen.clone(), key.clone()) { + Some(prior) => { + if (prior.clone() == fp) { + acc.clone() + } else { + if ((anchor.span.clone().start.clone() == 0) + && (anchor.span.clone().end.clone() == 0)) + { + Rc::new(DagCollectAcc { + seen: acc.seen.clone(), + order: acc.order.clone(), + collision_errors: v1_rt::rc_list_push( + acc.collision_errors.clone(), + dag_node_key_collision_error(key.clone(), anchor.span.clone()), + ), + }) + } else { + acc.clone() + } + } + } + None => Rc::new(vec![Rc::new(DagCollectPending { + anchor: anchor.clone(), + key: key.clone(), + fp: fp, + })]) + .iter() + .cloned() + .fold( + acc.clone(), + |inner: Rc, pending: Rc| { + dag_collect_node_tree( + pending.anchor.clone(), + Rc::new(DagCollectAcc { + seen: v1_rt::rc_map_insert( + inner.seen.clone(), + pending.key.clone(), + pending.fp.clone(), + ), + order: v1_rt::rc_list_push(inner.order.clone(), pending.anchor.clone()), + collision_errors: inner.collision_errors.clone(), + }), + ) + }, + ), } } } pub fn dag_collect_from_module( module: Rc, - slots: Rc>>, - collision_errors: Rc>>, -) -> Rc>> { - let slots = dag_collect_insert_slots(module.module.clone(), slots, collision_errors.clone()); - module.items.clone().iter().cloned().fold( - slots, - |s: Rc>>, item: Rc| { - dag_collect_insert_slots(item, s, collision_errors.clone()) - }, - ) + acc: Rc, +) -> Rc { + { + let acc = dag_collect_insert(module.module.clone(), acc); + module + .items + .clone() + .iter() + .cloned() + .fold(acc, |a: Rc, item: Rc| { + dag_collect_insert(item.clone(), a) + }) + } } pub fn collect_dag_nodes(typed: Rc) -> Rc { - dag_collect_fp_memo_reset(); - dag_collect_key_memo_reset(); - let collision_errors = Rc::new(vec![]); - let slots = typed.modules.clone().iter().cloned().fold( - v1_rt::rc_empty_map::>(), - |s: Rc>>, m: Rc| { - dag_collect_from_module(m, s, collision_errors.clone()) - }, - ); - dag_collect_pack_slots(slots, collision_errors) + typed.modules.clone().iter().cloned().fold( + Rc::new(DagCollectAcc { + seen: v1_rt::rc_empty_map::(), + order: Rc::new(vec![]), + collision_errors: Rc::new(vec![]), + }), + |acc: Rc, m: Rc| dag_collect_from_module(m.clone(), acc), + ) } diff --git a/src/v1/stage0/src/v1_compiler_dag_collect_support.rs b/src/v1/stage0/src/v1_compiler_dag_collect_support.rs index 31ff03253f7..e5cc2a41c45 100644 --- a/src/v1/stage0/src/v1_compiler_dag_collect_support.rs +++ b/src/v1/stage0/src/v1_compiler_dag_collect_support.rs @@ -16,97 +16,10 @@ pub use crate::v1_std_core::{ }; use crate::NonEmptyBTreeSet; use crate::NonEmptyVec; -use std::cell::RefCell; use std::collections::BTreeSet; use std::collections::HashMap; use std::rc::Rc; -thread_local! { - static DAG_NODE_SURFACE_FP_MEMO: RefCell> = RefCell::new(HashMap::new()); -} - -pub fn dag_collect_fp_memo_reset() { - DAG_NODE_SURFACE_FP_MEMO.with(|memo| memo.borrow_mut().clear()); -} - -pub fn dag_node_is_resolved_identity_shell(node: Rc) -> bool { - match (*node.expr_data.clone()).clone() { - ExprData::NoExprData => match node.inferred.clone().as_deref().cloned() { - Some(InferredNode::Resolved { node: _, .. }) => { - ((((node.body.clone() == None) && (node.transport.clone() == None)) - && ((node.children.clone().len() as i64) == 0)) - && ((node.params.clone().len() as i64) == 0)) - } - _ => false, - }, - _ => false, - } -} - -pub fn dag_node_collection_anchor(mut node: Rc) -> Rc { - loop { - if dag_node_is_resolved_identity_shell(node.clone()) { - match node.inferred.clone().as_deref().cloned() { - Some(InferredNode::Resolved { node: target, .. }) => { - let __tco_0 = target.clone(); - node = __tco_0; - continue; - } - _ => { - break node.clone(); - } - } - } else { - break node.clone(); - } - } -} - -fn dag_node_surface_fingerprint_compute(node: Rc) -> String { - stacker::maybe_grow(512 * 1024, 2 * 1024 * 1024, || { - let child_hashes = Rc::new({ - let mut __result = Vec::new(); - for c in node.children.clone().iter().cloned() { - __result.push(dag_node_surface_fingerprint_memo(c.clone())); - } - __result - }); - let param_hashes = Rc::new({ - let mut __result = Vec::new(); - for p in node.params.clone().iter().cloned() { - __result.push(dag_node_surface_fingerprint_memo(p.clone())); - } - __result - }); - let with_children = v1_rt::hash_combine( - dag_node_surface_leaf_mix(node.clone()), - child_subtree_hash(node.connective.clone(), child_hashes), - ); - v1_rt::hash_combine(with_children, dag_node_seq_hash(param_hashes)) - }) -} - -pub fn dag_node_surface_fingerprint_memo(node: Rc) -> String { - let anchor = dag_node_collection_anchor(node); - let ptr = Rc::as_ptr(&anchor) as usize; - DAG_NODE_SURFACE_FP_MEMO.with(|memo| { - if let Some(fp) = memo.borrow().get(&ptr) { - return fp.clone(); - } - let fp = dag_node_surface_fingerprint_compute(anchor); - memo.borrow_mut().insert(ptr, fp.clone()); - fp - }) -} - -#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] -pub struct DagCollectSlot { - pub key: String, - pub fp: String, - pub node: Rc, - pub seq: i64, -} - #[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] pub struct DagCollectAcc { pub seen: Rc>, @@ -114,28 +27,11 @@ pub struct DagCollectAcc { pub collision_errors: Rc>>, } -pub fn dag_collect_slot_seq(slots: Rc>>) -> i64 { - slots.len() as i64 -} - -pub fn dag_collect_pack_slots( - slots: Rc>>, - collision_errors: Rc>>, -) -> Rc { - let mut ordered_slots: Vec> = slots.values().cloned().collect(); - ordered_slots.sort_by(|a, b| a.seq.cmp(&b.seq)); - let order: Vec> = ordered_slots.iter().map(|s| s.node.clone()).collect(); - let seen = ordered_slots.iter().cloned().fold( - v1_rt::rc_empty_map::(), - |acc: Rc>, s: Rc| { - v1_rt::rc_map_insert(acc, s.key.clone(), s.fp.clone()) - }, - ); - Rc::new(DagCollectAcc { - seen, - order: Rc::new(order), - collision_errors, - }) +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct DagCollectPending { + pub anchor: Rc, + pub key: String, + pub fp: String, } pub fn json_quote(s: String) -> String { @@ -253,11 +149,31 @@ pub fn dag_node_surface_leaf_mix(node: Rc) -> String { } pub fn dag_node_surface_fingerprint_rec(node: Rc) -> String { - dag_node_surface_fingerprint_memo(node) + stacker::maybe_grow(512 * 1024, 2 * 1024 * 1024, || { + let child_hashes = Rc::new({ + let mut __result = Vec::new(); + for c in node.children.clone().iter().cloned() { + __result.push(dag_node_surface_fingerprint_rec(c.clone())); + } + __result + }); + let param_hashes = Rc::new({ + let mut __result = Vec::new(); + for p in node.params.clone().iter().cloned() { + __result.push(dag_node_surface_fingerprint_rec(p.clone())); + } + __result + }); + let with_children = v1_rt::hash_combine( + dag_node_surface_leaf_mix(node.clone()), + child_subtree_hash(node.connective.clone(), child_hashes), + ); + v1_rt::hash_combine(with_children, dag_node_seq_hash(param_hashes)) + }) } pub fn dag_node_surface_fingerprint(node: Rc) -> String { - dag_node_surface_fingerprint_memo(node) + dag_node_surface_fingerprint_rec(node) } pub fn dag_node_key_collision_error(key: String, span: Rc) -> Rc { diff --git a/src/v1/stage0/src/v1_compiler_infer_method.rs b/src/v1/stage0/src/v1_compiler_infer_method.rs index 96d9884ceb1..25d635b19b1 100644 --- a/src/v1/stage0/src/v1_compiler_infer_method.rs +++ b/src/v1/stage0/src/v1_compiler_infer_method.rs @@ -173,11 +173,6 @@ pub fn builtin_function_registry() -> Rc>> { "reverse".to_string(), list_of_type_variable("collection_element".to_string()), ); - let m = v1_rt::rc_map_insert( - m.clone(), - "get".to_string(), - with_optional_cardinality(type_variable_node("collection_element".to_string())), - ); let m = v1_rt::rc_map_insert( m.clone(), "list_push".to_string(), From 7f45ac65d553b3f1311dbe6972f830c5ed413f54 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Sun, 5 Jul 2026 04:22:35 +0000 Subject: [PATCH 20/32] WIP: P3+P5: infer whole-corpus scans to per-module maps --- dag/test/claim/builtin_get_resolver_test.dag | 22 +++++++++++++++++++ src/v1/04_method.dag | 1 + src/v1/compiler_tests_rust.dag | 22 +++++++++++++++++-- src/v1/stage0/src/compiler_tests.rs | 20 ++++++++++++++++- src/v1/stage0/src/v1_compiler_infer_method.rs | 5 +++++ 5 files changed, 67 insertions(+), 3 deletions(-) create mode 100644 dag/test/claim/builtin_get_resolver_test.dag diff --git a/dag/test/claim/builtin_get_resolver_test.dag b/dag/test/claim/builtin_get_resolver_test.dag new file mode 100644 index 00000000000..1845d782e41 --- /dev/null +++ b/dag/test/claim/builtin_get_resolver_test.dag @@ -0,0 +1,22 @@ +module test.claim.builtin_get_resolver + +fn witness_get_in_bounds_returns_element() -> Bool { + match get(xs: [10, 20, 30], index: 1) { + Present { value: v } => v == 20 + Absent => false + } +} + +fn witness_get_out_of_bounds_is_absent() -> Bool { + match get(xs: [10, 20, 30], index: 7) { + Present { value: _ } => false + Absent => true + } +} + +fn witness_get_negative_index_is_absent() -> Bool { + match get(xs: [10, 20, 30], index: 0 - 1) { + Present { value: _ } => false + Absent => true + } +} diff --git a/src/v1/04_method.dag b/src/v1/04_method.dag index fd2345a797e..41c8bc86730 100644 --- a/src/v1/04_method.dag +++ b/src/v1/04_method.dag @@ -87,6 +87,7 @@ fn builtin_function_registry() -> Map { let m = map_insert(m, "Some", with_optional_cardinality(n: type_variable_node(id: "some_inner"))) let m = map_insert(m, "map_keys", list_of_type_variable(id: "collection_element")) let m = map_insert(m, "map_values", list_of_type_variable(id: "collection_element")) + let m = map_insert(m, "get", with_optional_cardinality(n: type_variable_node(id: "collection_element"))) let m = map_insert(m, "reverse", list_of_type_variable(id: "collection_element")) let m = map_insert(m, "list_push", list_of_type_variable(id: "collection_element")) let m = map_insert(m, "hash_combine", hash_type) diff --git a/src/v1/compiler_tests_rust.dag b/src/v1/compiler_tests_rust.dag index 6d8411c5307..ae89c65bceb 100644 --- a/src/v1/compiler_tests_rust.dag +++ b/src/v1/compiler_tests_rust.dag @@ -1125,7 +1125,11 @@ fn ct_profile_reconcile_test() -> String { " );\n", " acc\n", " },\n", - " ));\n\n", + " ));\n", + " let mut variant_surfaces = crate::v1_rt::rc_empty_map::<\n", + " String,\n", + " std::rc::Rc,\n", + " >();\n\n", " for resolved in graph.modules.iter() {\n", " let name = resolved.module.name.to_string();\n", " let item_count =\n", @@ -1190,9 +1194,10 @@ fn ct_profile_reconcile_test() -> String { " panic!(\"ABORT: '{}' build_type_env took {:?}\", name, env_elapsed);\n", " }\n\n", " let t_full = Instant::now();\n", - " let tc_result = crate::v1_compiler_infer::typecheck_module_isolated(\n", + " let tc_result = crate::v1_compiler_infer::typecheck_module(\n", " resolved.clone(),\n", " module_index.clone(),\n", + " variant_surfaces.clone(),\n", " source_indices.clone(),\n", " intern_table.clone(),\n", " );\n", @@ -1222,6 +1227,19 @@ fn ct_profile_reconcile_test() -> String { " panic!(\"ABORT: '{}' took {:?} (>10s)\", name, full_elapsed);\n", " }\n\n", " let typed = tc_result.typed.clone();\n", + " let typed_path = crate::v1_std_core::authored_name_at(\n", + " source_indices.clone(),\n", + " typed.module.clone(),\n", + " );\n", + " variant_surfaces = crate::v1_rt::rc_map_insert(\n", + " variant_surfaces.clone(),\n", + " typed_path,\n", + " crate::v1_compiler_infer::build_variant_export_surface(\n", + " typed.clone(),\n", + " variant_surfaces.clone(),\n", + " source_indices.clone(),\n", + " ),\n", + " );\n", " mi_raw.insert(name, typed);\n", " }\n\n", " let rss_final = get_rss_bytes();\n", diff --git a/src/v1/stage0/src/compiler_tests.rs b/src/v1/stage0/src/compiler_tests.rs index 1f6c42eeafc..91657246e79 100644 --- a/src/v1/stage0/src/compiler_tests.rs +++ b/src/v1/stage0/src/compiler_tests.rs @@ -1640,6 +1640,10 @@ mod compiler_tests { acc }, )); + let mut variant_surfaces = crate::v1_rt::rc_empty_map::< + String, + std::rc::Rc, + >(); for resolved in graph.modules.iter() { let name = resolved.module.name.to_string(); @@ -1714,9 +1718,10 @@ mod compiler_tests { } let t_full = Instant::now(); - let tc_result = crate::v1_compiler_infer::typecheck_module_isolated( + let tc_result = crate::v1_compiler_infer::typecheck_module( resolved.clone(), module_index.clone(), + variant_surfaces.clone(), source_indices.clone(), intern_table.clone(), ); @@ -1749,6 +1754,19 @@ mod compiler_tests { } let typed = tc_result.typed.clone(); + let typed_path = crate::v1_std_core::authored_name_at( + source_indices.clone(), + typed.module.clone(), + ); + variant_surfaces = crate::v1_rt::rc_map_insert( + variant_surfaces.clone(), + typed_path, + crate::v1_compiler_infer::build_variant_export_surface( + typed.clone(), + variant_surfaces.clone(), + source_indices.clone(), + ), + ); mi_raw.insert(name, typed); } diff --git a/src/v1/stage0/src/v1_compiler_infer_method.rs b/src/v1/stage0/src/v1_compiler_infer_method.rs index 25d635b19b1..7aa50f4c8b7 100644 --- a/src/v1/stage0/src/v1_compiler_infer_method.rs +++ b/src/v1/stage0/src/v1_compiler_infer_method.rs @@ -168,6 +168,11 @@ pub fn builtin_function_registry() -> Rc>> { "map_values".to_string(), list_of_type_variable("collection_element".to_string()), ); + let m = v1_rt::rc_map_insert( + m.clone(), + "get".to_string(), + with_optional_cardinality(type_variable_node("collection_element".to_string())), + ); let m = v1_rt::rc_map_insert( m.clone(), "reverse".to_string(), From 36f612ba513d5ef7a60955f30495990d331ab6ae Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Sun, 5 Jul 2026 04:31:40 +0000 Subject: [PATCH 21/32] fix(ci): regen ci.yml for step-level timeout policy (#6263) Sync workflow with ci_workflow.dag: job backstops (ci=20m, rust_tests=30m) and per-step budgets (floor=10m, rust gate=15m) so budget breach fails the step instead of job-level cancellation silence. Co-authored-by: Cursor --- .github/workflows/ci.yml | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a94b20d4ee0..3309d51bcd9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -18,7 +18,7 @@ env: jobs: ci: runs-on: [self-hosted, linux, arm64] - timeout-minutes: 10 + timeout-minutes: 20 steps: - name: Checkout uses: actions/checkout@v5 @@ -92,14 +92,16 @@ jobs: STAMP_EXIT=$? if [ "$FLOOR_EXIT" -ne 0 ]; then exit "$FLOOR_EXIT"; fi exit "$STAMP_EXIT" + timeout-minutes: 10 - name: Merge-admission gate (receipt required; freshness block held until GatingEnforced) run: | ROOT=$(git rev-parse --show-toplevel 2>/dev/null || pwd) git fetch --no-tags origin main "$ROOT/target/release/gunbc" run --source-root dag --source-root src/v2 --entry dag/tools/merge_admission_gate.dag --function main + timeout-minutes: 5 rust_tests: runs-on: [self-hosted, linux, arm64] - timeout-minutes: 10 + timeout-minutes: 30 steps: - name: Checkout uses: actions/checkout@v5 @@ -147,6 +149,7 @@ jobs: case "$(uname -m)" in aarch64|arm64) A=linux-arm ;; *) A=linux ;; esac curl -LsSf --retry 3 "https://get.nexte.st/0.9.138/$A" | tar zxf - -C "$CARGO_HOME/bin" fi + timeout-minutes: 5 - name: v1 rust gate (fmt + clippy + nextest) run: | ROOT=$(git rev-parse --show-toplevel 2>/dev/null || pwd) @@ -193,11 +196,13 @@ jobs: fi fi rm -f "$BUILD_LOG" + timeout-minutes: 15 - name: rust_tests cgroup peak (placement divisor) run: | BIN="$(git rev-parse --show-toplevel 2>/dev/null || pwd)/target/release/claim_executor" if [ -x "$BIN" ]; then "$BIN" --measure-cgroup-peak; else echo "[measurement] cgroup peak: unavailable (claim_executor not built)"; fi if: always() + timeout-minutes: 5 deploy_dashboard_srv1: runs-on: [self-hosted, linux, arm64, srv1] needs: [ci] @@ -268,3 +273,4 @@ jobs: "$ROOT/target/release/claim_executor" --verify-build-artifacts "$ROOT/target/release/claim_executor" "$ROOT/target/release/gunbc" sccache --show-stats 2>/dev/null || true "$ROOT/target/release/gunbc" run --source-root dag --source-root src/v2 --entry dag/gunbc/live_deploy/apply.dag --function live_deploy_apply_srv1_wet + From d5755c8fb73d58f96234bc4a9518b52a8a469ab3 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Sun, 5 Jul 2026 04:32:57 +0000 Subject: [PATCH 22/32] fix(ci): preserve 10m job timeout cap in ci.yml regen MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Step-level budgets (floor=10m, rust gate=15m) stay; job-level timeout-minutes reverts to gunbc_ci_job_timeout_policy_minutes (10) per standing operator directive โ€” not the #6263 backstop sum. Co-authored-by: Cursor --- .github/workflows/ci.yml | 4 ++-- dag/gunbc/ci_workflow.dag | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3309d51bcd9..c3843db9d4b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -18,7 +18,7 @@ env: jobs: ci: runs-on: [self-hosted, linux, arm64] - timeout-minutes: 20 + timeout-minutes: 10 steps: - name: Checkout uses: actions/checkout@v5 @@ -101,7 +101,7 @@ jobs: timeout-minutes: 5 rust_tests: runs-on: [self-hosted, linux, arm64] - timeout-minutes: 30 + timeout-minutes: 10 steps: - name: Checkout uses: actions/checkout@v5 diff --git a/dag/gunbc/ci_workflow.dag b/dag/gunbc/ci_workflow.dag index c6c0a7b7cf9..d9a32a1507e 100644 --- a/dag/gunbc/ci_workflow.dag +++ b/dag/gunbc/ci_workflow.dag @@ -320,7 +320,7 @@ fn ci_job() -> Job { outputs: none, if_condition: none, strategy: none, - timeout_minutes: Present { value: gunbc_ci_job_backstop_timeout_minutes() }, + timeout_minutes: Present { value: gunbc_ci_job_timeout_policy_minutes }, continue_on_error: none, concurrency: none } @@ -337,7 +337,7 @@ fn rust_tests_job() -> Job { outputs: none, if_condition: none, strategy: none, - timeout_minutes: Present { value: gunbc_ci_rust_job_backstop_timeout_minutes() }, + timeout_minutes: Present { value: gunbc_ci_job_timeout_policy_minutes }, continue_on_error: none, concurrency: none } From 091fb3dcc7226767e4d7a54f732e83b2ee931ee0 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Sun, 5 Jul 2026 04:49:19 +0000 Subject: [PATCH 23/32] fix(stage0): regen compiler_tests_rust emit from .dag authority MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Sync v1_compiler_compiler_tests_rust.rs ct_profile_reconcile_test with compiler_tests_rust.dag (variant_surfaces + typecheck_module). Resolves ยง3 parallel-representation drift flagged in composer review. Co-authored-by: Cursor --- .../src/v1_compiler_compiler_tests_rust.rs | 219 +++++++++++++++++- 1 file changed, 218 insertions(+), 1 deletion(-) 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 d4c297f8270..fe84230b1bd 100644 --- a/src/v1/stage0/src/v1_compiler_compiler_tests_rust.rs +++ b/src/v1/stage0/src/v1_compiler_compiler_tests_rust.rs @@ -283,7 +283,224 @@ pub fn ct_profile_full_pipeline_test() -> String { } pub fn ct_profile_reconcile_test() -> 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(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(" #[test]\n".to_string(), " #[ignore]\n".to_string()), " fn profile_reconcile_per_module() {\n".to_string()), " let result = std::thread::Builder::new()\n".to_string()), " .stack_size(64 * 1024 * 1024)\n".to_string()), " .spawn(|| {\n".to_string()), " use std::collections::HashMap;\n".to_string()), " use std::time::Instant;\n\n".to_string()), " let sources = self_compile_sources();\n\n".to_string()), " eprintln!(\n".to_string()), " \"\\n=== PER-MODULE RECONCILE PROFILE ({} sources) ===\",\n".to_string()), " sources.len()\n".to_string()), " );\n\n".to_string()), " let t0 = Instant::now();\n".to_string()), " let mut modules = Vec::new();\n".to_string()), " let mut intern_table = crate::v1_std_core::empty_intern_table();\n".to_string()), " for source in &sources {\n".to_string()), " let tokens = crate::v1_compiler_tokenize::tokenize(\n".to_string()), " source.content.clone(),\n".to_string()), " source.path.clone(),\n".to_string()), " );\n".to_string()), " let si = crate::v1_std_core::build_newline_index(\n".to_string()), " source.path.clone(),\n".to_string()), " source.content.clone(),\n".to_string()), " );\n".to_string()), " let parsed = crate::v1_compiler_parse::parse_with_table(\n".to_string()), " tokens.clone(),\n".to_string()), " crate::v1_rt::rc_map_insert(\n".to_string()), " crate::v1_rt::rc_empty_map::>(),\n".to_string()), " si.file.clone(),\n".to_string()), " si.clone(),\n".to_string()), " ),\n".to_string()), " intern_table.clone(),\n".to_string()), " );\n".to_string()), " let result = parsed.result.clone();\n".to_string()), " intern_table = parsed.intern_table.clone();\n".to_string()), " let m = result.module.clone()\n".to_string()), " .unwrap_or_else(|| panic!(\"parse failed for source {}\", source.path));\n".to_string()), " modules.push(m);\n".to_string()), " }\n".to_string()), " let resolve_si = sources.iter().fold(\n".to_string()), " crate::v1_rt::rc_empty_map::>(),\n".to_string()), " |acc, s| crate::v1_rt::rc_map_insert(acc, s.path.clone(), crate::v1_std_core::build_newline_index(s.path.clone(), s.content.clone())),\n".to_string()), " );\n".to_string()), " let graph = crate::v1_compiler_resolve::resolve_modules(std::rc::Rc::new(modules), resolve_si);\n".to_string()), " let setup_time = t0.elapsed();\n".to_string()), " let rss_baseline = get_rss_bytes();\n".to_string()), " eprintln!(\n".to_string()), " \" Setup (tok+parse+resolve): {:?} | RSS: {}\",\n".to_string()), " setup_time,\n".to_string()), " format_bytes(rss_baseline)\n".to_string()), " );\n".to_string()), " eprintln!(\" Modules to reconcile: {}\\n\", graph.modules.len());\n\n".to_string()), " let mut mi_raw = HashMap::<\n".to_string()), " String,\n".to_string()), " std::rc::Rc,\n".to_string()), " >::new();\n".to_string()), " let source_indices = std::rc::Rc::new(sources.iter().fold(\n".to_string()), " HashMap::>::new(),\n".to_string()), " |mut acc, source| {\n".to_string()), " acc.insert(\n".to_string()), " source.path.clone(),\n".to_string()), " crate::v1_std_core::build_newline_index(\n".to_string()), " source.path.clone(),\n".to_string()), " source.content.clone(),\n".to_string()), " ),\n".to_string()), " );\n".to_string()), " acc\n".to_string()), " },\n".to_string()), " ));\n\n".to_string()), " for resolved in graph.modules.iter() {\n".to_string()), " let name = resolved.module.name.to_string();\n".to_string()), " let item_count =\n".to_string()), " crate::v1_std_core::module_items(resolved.module.clone()).len();\n".to_string()), " let rss_before = get_rss_bytes();\n\n".to_string()), " eprint!(\" {:>35} ({:>3} items) ... \", name, item_count);\n\n".to_string()), " let module_index = std::rc::Rc::new(mi_raw.clone());\n\n".to_string()), " let t_unres = Instant::now();\n".to_string()), " let _unres = crate::v1_compiler_infer::build_type_env_unresolved(\n".to_string()), " resolved.clone(),\n".to_string()), " module_index.clone(),\n".to_string()), " source_indices.clone(),\n".to_string()), " intern_table.clone(),\n".to_string()), " );\n".to_string()), " let unres_elapsed = t_unres.elapsed();\n".to_string()), " let rss_after_unres = get_rss_bytes();\n".to_string()), " let unres_delta = rss_after_unres.saturating_sub(rss_before);\n\n".to_string()), " eprint!(\n".to_string()), " \"cycles={:>8.2?}(+{}) \",\n".to_string()), " unres_elapsed,\n".to_string()), " format_bytes(unres_delta)\n".to_string()), " );\n\n".to_string()), " if unres_delta > 256 * 1024 * 1024 {\n".to_string()), " eprintln!(\"\");\n".to_string()), " panic!(\n".to_string()), " \"ABORT: '{}' cycle detection grew RSS by {}\",\n".to_string()), " name,\n".to_string()), " format_bytes(unres_delta)\n".to_string()), " );\n".to_string()), " }\n\n".to_string()), " let t_env = Instant::now();\n".to_string()), " let env_result = crate::v1_compiler_infer::build_type_env(\n".to_string()), " resolved.clone(),\n".to_string()), " module_index.clone(),\n".to_string()), " source_indices.clone(),\n".to_string()), " intern_table.clone(),\n".to_string()), " );\n".to_string()), " let env_elapsed = t_env.elapsed();\n".to_string()), " let rss_after_env = get_rss_bytes();\n".to_string()), " let env_delta = rss_after_env.saturating_sub(rss_before);\n".to_string()), " let env_errs: usize = env_result\n".to_string()), " .diagnostics\n".to_string()), " .iter()\n".to_string()), " .filter(|d| crate::v1_std_core::is_error_diagnostic(d.diagnostic.clone()))\n".to_string()), " .count();\n\n".to_string()), " eprint!(\n".to_string()), " \"env={:>8.2?}(+{},e={}) \",\n".to_string()), " env_elapsed,\n".to_string()), " format_bytes(env_delta),\n".to_string()), " env_errs\n".to_string()), " );\n\n".to_string()), " if env_delta > 512 * 1024 * 1024 {\n".to_string()), " eprintln!(\"\");\n".to_string()), " panic!(\n".to_string()), " \"ABORT: '{}' build_type_env grew RSS by {}\",\n".to_string()), " name,\n".to_string()), " format_bytes(env_delta)\n".to_string()), " );\n".to_string()), " }\n".to_string()), " if env_elapsed.as_secs() > 10 {\n".to_string()), " eprintln!(\"\");\n".to_string()), " panic!(\"ABORT: '{}' build_type_env took {:?}\", name, env_elapsed);\n".to_string()), " }\n\n".to_string()), " let t_full = Instant::now();\n".to_string()), " let tc_result = crate::v1_compiler_infer::typecheck_module_isolated(\n".to_string()), " resolved.clone(),\n".to_string()), " module_index.clone(),\n".to_string()), " source_indices.clone(),\n".to_string()), " intern_table.clone(),\n".to_string()), " );\n".to_string()), " let full_elapsed = t_full.elapsed();\n".to_string()), " let rss_after = get_rss_bytes();\n".to_string()), " let delta = rss_after.saturating_sub(rss_before);\n".to_string()), " let diag_count: usize = tc_result\n".to_string()), " .diagnostics\n".to_string()), " .iter()\n".to_string()), " .filter(|d| crate::v1_std_core::is_error_diagnostic(d.diagnostic.clone()))\n".to_string()), " .count();\n\n".to_string()), " eprintln!(\n".to_string()), " \"full={:>8.2?} | RSS: {} (+{}) | errs: {}\",\n".to_string()), " full_elapsed,\n".to_string()), " format_bytes(rss_after),\n".to_string()), " format_bytes(delta),\n".to_string()), " diag_count\n".to_string()), " );\n\n".to_string()), " if delta > 512 * 1024 * 1024 {\n".to_string()), " panic!(\n".to_string()), " \"ABORT: '{}' grew RSS by {} (>512MB)\",\n".to_string()), " name,\n".to_string()), " format_bytes(delta)\n".to_string()), " );\n".to_string()), " }\n".to_string()), " if full_elapsed.as_secs() > 10 {\n".to_string()), " panic!(\"ABORT: '{}' took {:?} (>10s)\", name, full_elapsed);\n".to_string()), " }\n\n".to_string()), " let typed = tc_result.typed.clone();\n".to_string()), " mi_raw.insert(name, typed);\n".to_string()), " }\n\n".to_string()), " let rss_final = get_rss_bytes();\n".to_string()), " eprintln!(\n".to_string()), " \"\\n RSS final: {} (from baseline: +{})\",\n".to_string()), " format_bytes(rss_final),\n".to_string()), " format_bytes(rss_final.saturating_sub(rss_baseline))\n".to_string()), " );\n".to_string()), " eprintln!(\"=== DONE ===\\n\");\n".to_string()), " })\n".to_string()), " .expect(\"failed to spawn thread\")\n".to_string()), " .join();\n".to_string()), " result.expect(\"profile_reconcile_per_module panicked\");\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(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(" #[test] +".to_string(), " #[ignore] +".to_string()), " fn profile_reconcile_per_module() { +".to_string()), " let result = std::thread::Builder::new() +".to_string()), " .stack_size(64 * 1024 * 1024) +".to_string()), " .spawn(|| { +".to_string()), " use std::collections::HashMap; +".to_string()), " use std::time::Instant; + +".to_string()), " let sources = self_compile_sources(); + +".to_string()), " eprintln!( +".to_string()), " \"\n=== PER-MODULE RECONCILE PROFILE ({} sources) ===\", +".to_string()), " sources.len() +".to_string()), " ); + +".to_string()), " let t0 = Instant::now(); +".to_string()), " let mut modules = Vec::new(); +".to_string()), " let mut intern_table = crate::v1_std_core::empty_intern_table(); +".to_string()), " for source in &sources { +".to_string()), " let tokens = crate::v1_compiler_tokenize::tokenize( +".to_string()), " source.content.clone(), +".to_string()), " source.path.clone(), +".to_string()), " ); +".to_string()), " let si = crate::v1_std_core::build_newline_index( +".to_string()), " source.path.clone(), +".to_string()), " source.content.clone(), +".to_string()), " ); +".to_string()), " let parsed = crate::v1_compiler_parse::parse_with_table( +".to_string()), " tokens.clone(), +".to_string()), " crate::v1_rt::rc_map_insert( +".to_string()), " crate::v1_rt::rc_empty_map::>(), +".to_string()), " si.file.clone(), +".to_string()), " si.clone(), +".to_string()), " ), +".to_string()), " intern_table.clone(), +".to_string()), " ); +".to_string()), " let result = parsed.result.clone(); +".to_string()), " intern_table = parsed.intern_table.clone(); +".to_string()), " let m = result.module.clone() +".to_string()), " .unwrap_or_else(|| panic!(\"parse failed for source {}\", source.path)); +".to_string()), " modules.push(m); +".to_string()), " } +".to_string()), " let resolve_si = sources.iter().fold( +".to_string()), " crate::v1_rt::rc_empty_map::>(), +".to_string()), " |acc, s| crate::v1_rt::rc_map_insert(acc, s.path.clone(), crate::v1_std_core::build_newline_index(s.path.clone(), s.content.clone())), +".to_string()), " ); +".to_string()), " let graph = crate::v1_compiler_resolve::resolve_modules(std::rc::Rc::new(modules), resolve_si); +".to_string()), " let setup_time = t0.elapsed(); +".to_string()), " let rss_baseline = get_rss_bytes(); +".to_string()), " eprintln!( +".to_string()), " \" Setup (tok+parse+resolve): {:?} | RSS: {}\", +".to_string()), " setup_time, +".to_string()), " format_bytes(rss_baseline) +".to_string()), " ); +".to_string()), " eprintln!(\" Modules to reconcile: {}\n\", graph.modules.len()); + +".to_string()), " let mut mi_raw = HashMap::< +".to_string()), " String, +".to_string()), " std::rc::Rc, +".to_string()), " >::new(); +".to_string()), " let source_indices = std::rc::Rc::new(sources.iter().fold( +".to_string()), " HashMap::>::new(), +".to_string()), " |mut acc, source| { +".to_string()), " acc.insert( +".to_string()), " source.path.clone(), +".to_string()), " crate::v1_std_core::build_newline_index( +".to_string()), " source.path.clone(), +".to_string()), " source.content.clone(), +".to_string()), " ), +".to_string()), " ); +".to_string()), " acc +".to_string()), " }, +".to_string()), " )); +".to_string()), " let mut variant_surfaces = crate::v1_rt::rc_empty_map::< +".to_string()), " String, +".to_string()), " std::rc::Rc, +".to_string()), " >(); + +".to_string()), " for resolved in graph.modules.iter() { +".to_string()), " let name = resolved.module.name.to_string(); +".to_string()), " let item_count = +".to_string()), " crate::v1_std_core::module_items(resolved.module.clone()).len(); +".to_string()), " let rss_before = get_rss_bytes(); + +".to_string()), " eprint!(\" {:>35} ({:>3} items) ... \", name, item_count); + +".to_string()), " let module_index = std::rc::Rc::new(mi_raw.clone()); + +".to_string()), " let t_unres = Instant::now(); +".to_string()), " let _unres = crate::v1_compiler_infer::build_type_env_unresolved( +".to_string()), " resolved.clone(), +".to_string()), " module_index.clone(), +".to_string()), " source_indices.clone(), +".to_string()), " intern_table.clone(), +".to_string()), " ); +".to_string()), " let unres_elapsed = t_unres.elapsed(); +".to_string()), " let rss_after_unres = get_rss_bytes(); +".to_string()), " let unres_delta = rss_after_unres.saturating_sub(rss_before); + +".to_string()), " eprint!( +".to_string()), " \"cycles={:>8.2?}(+{}) \", +".to_string()), " unres_elapsed, +".to_string()), " format_bytes(unres_delta) +".to_string()), " ); + +".to_string()), " if unres_delta > 256 * 1024 * 1024 { +".to_string()), " eprintln!(\"\"); +".to_string()), " panic!( +".to_string()), " \"ABORT: '{}' cycle detection grew RSS by {}\", +".to_string()), " name, +".to_string()), " format_bytes(unres_delta) +".to_string()), " ); +".to_string()), " } + +".to_string()), " let t_env = Instant::now(); +".to_string()), " let env_result = crate::v1_compiler_infer::build_type_env( +".to_string()), " resolved.clone(), +".to_string()), " module_index.clone(), +".to_string()), " source_indices.clone(), +".to_string()), " intern_table.clone(), +".to_string()), " ); +".to_string()), " let env_elapsed = t_env.elapsed(); +".to_string()), " let rss_after_env = get_rss_bytes(); +".to_string()), " let env_delta = rss_after_env.saturating_sub(rss_before); +".to_string()), " let env_errs: usize = env_result +".to_string()), " .diagnostics +".to_string()), " .iter() +".to_string()), " .filter(|d| crate::v1_std_core::is_error_diagnostic(d.diagnostic.clone())) +".to_string()), " .count(); + +".to_string()), " eprint!( +".to_string()), " \"env={:>8.2?}(+{},e={}) \", +".to_string()), " env_elapsed, +".to_string()), " format_bytes(env_delta), +".to_string()), " env_errs +".to_string()), " ); + +".to_string()), " if env_delta > 512 * 1024 * 1024 { +".to_string()), " eprintln!(\"\"); +".to_string()), " panic!( +".to_string()), " \"ABORT: '{}' build_type_env grew RSS by {}\", +".to_string()), " name, +".to_string()), " format_bytes(env_delta) +".to_string()), " ); +".to_string()), " } +".to_string()), " if env_elapsed.as_secs() > 10 { +".to_string()), " eprintln!(\"\"); +".to_string()), " panic!(\"ABORT: '{}' build_type_env took {:?}\", name, env_elapsed); +".to_string()), " } + +".to_string()), " let t_full = Instant::now(); +".to_string()), " let tc_result = crate::v1_compiler_infer::typecheck_module( +".to_string()), " resolved.clone(), +".to_string()), " module_index.clone(), +".to_string()), " variant_surfaces.clone(), +".to_string()), " source_indices.clone(), +".to_string()), " intern_table.clone(), +".to_string()), " ); +".to_string()), " let full_elapsed = t_full.elapsed(); +".to_string()), " let rss_after = get_rss_bytes(); +".to_string()), " let delta = rss_after.saturating_sub(rss_before); +".to_string()), " let diag_count: usize = tc_result +".to_string()), " .diagnostics +".to_string()), " .iter() +".to_string()), " .filter(|d| crate::v1_std_core::is_error_diagnostic(d.diagnostic.clone())) +".to_string()), " .count(); + +".to_string()), " eprintln!( +".to_string()), " \"full={:>8.2?} | RSS: {} (+{}) | errs: {}\", +".to_string()), " full_elapsed, +".to_string()), " format_bytes(rss_after), +".to_string()), " format_bytes(delta), +".to_string()), " diag_count +".to_string()), " ); + +".to_string()), " if delta > 512 * 1024 * 1024 { +".to_string()), " panic!( +".to_string()), " \"ABORT: '{}' grew RSS by {} (>512MB)\", +".to_string()), " name, +".to_string()), " format_bytes(delta) +".to_string()), " ); +".to_string()), " } +".to_string()), " if full_elapsed.as_secs() > 10 { +".to_string()), " panic!(\"ABORT: '{}' took {:?} (>10s)\", name, full_elapsed); +".to_string()), " } + +".to_string()), " let typed = tc_result.typed.clone(); +".to_string()), " let typed_path = crate::v1_std_core::authored_name_at( +".to_string()), " source_indices.clone(), +".to_string()), " typed.module.clone(), +".to_string()), " ); +".to_string()), " variant_surfaces = crate::v1_rt::rc_map_insert( +".to_string()), " variant_surfaces.clone(), +".to_string()), " typed_path, +".to_string()), " crate::v1_compiler_infer::build_variant_export_surface( +".to_string()), " typed.clone(), +".to_string()), " variant_surfaces.clone(), +".to_string()), " source_indices.clone(), +".to_string()), " ), +".to_string()), " ); +".to_string()), " mi_raw.insert(name, typed); +".to_string()), " } + +".to_string()), " let rss_final = get_rss_bytes(); +".to_string()), " eprintln!( +".to_string()), " \"\n RSS final: {} (from baseline: +{})\", +".to_string()), " format_bytes(rss_final), +".to_string()), " format_bytes(rss_final.saturating_sub(rss_baseline)) +".to_string()), " ); +".to_string()), " eprintln!(\"=== DONE ===\n\"); +".to_string()), " }) +".to_string()), " .expect(\"failed to spawn thread\") +".to_string()), " .join(); +".to_string()), " result.expect(\"profile_reconcile_per_module panicked\"); +".to_string()), " } + +".to_string()) } pub fn ct_module_footer() -> String { From 72685b4991ce91c43a02b24a81763bd2b54b2bd4 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Sun, 5 Jul 2026 04:50:38 +0000 Subject: [PATCH 24/32] fix(ci): serialize ci floor after rust_tests for warm cache Parallel ci+rust_tests both cold-built v1-compiler inside the 10m job cap, cancelling the floor step before completion. Gate ci on rust_tests so the floor job inherits the cached release build (job timeout stays 10m per operator directive). Co-authored-by: Cursor --- .github/workflows/ci.yml | 1 + dag/gunbc/ci_workflow.dag | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c3843db9d4b..eaef037f4e0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -18,6 +18,7 @@ env: jobs: ci: runs-on: [self-hosted, linux, arm64] + needs: [rust_tests] timeout-minutes: 10 steps: - name: Checkout diff --git a/dag/gunbc/ci_workflow.dag b/dag/gunbc/ci_workflow.dag index d9a32a1507e..badd8e86668 100644 --- a/dag/gunbc/ci_workflow.dag +++ b/dag/gunbc/ci_workflow.dag @@ -315,7 +315,7 @@ fn ci_job() -> Job { name: none, runner: gunbc_ci_runner_spec(), steps: concat(ci_prelude_steps(), [ci_floor_step(), ci_merge_admission_gate_step()]), - needs: [], + needs: ["rust_tests"], env: none, outputs: none, if_condition: none, From 425bdc400897f7937024b2b31264e5cf899d3de5 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Sun, 5 Jul 2026 05:06:30 +0000 Subject: [PATCH 25/32] fix(infer): dedupe exporter_count per module in type-name index MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit build_type_name_export_index counted bindings, not modules โ€” inflating exporter_count when one module had multiple bindings sharing a name. Per-module seen_names + last-wins canonical restores global_type_exporter_count semantics. Co-authored-by: Cursor --- src/v1/04_infer.dag | 37 +++++++++++----- src/v1/stage0/src/v1_compiler_infer.rs | 58 +++++++++++++++----------- 2 files changed, 60 insertions(+), 35 deletions(-) diff --git a/src/v1/04_infer.dag b/src/v1/04_infer.dag index 11eb1eea064..f37754c8570 100644 --- a/src/v1/04_infer.dag +++ b/src/v1/04_infer.dag @@ -6601,21 +6601,38 @@ fn import_module_path_at(imp: Node, source_indices: Map) - authored_name_at(source_indices: source_indices, node: imp) } -fn build_type_name_export_index(modules: List) -> Map { - fold(modules, init: empty_map(), f: (acc, m) => - fold(m.type_env.bindings |> map_values, init: acc, f: (acc2, binding) => - let name = binding.name - match map_get(acc2, name) { +type ExportIndexModuleAccum { + index: Map + seen_names: Set +} + +fn export_index_merge_module(acc: Map, m: TypedModule) -> Map { + fold(m.type_env.bindings |> map_values, init: ExportIndexModuleAccum { index: acc, seen_names: empty_set() }, f: (state, binding) => + let name = binding.name + if set_contains(state.seen_names, name) { + state + } else { + let canonical = match filter(m.type_env.bindings |> map_values, b => b.name == name) |> last { + Present { value: b } => b + Absent => binding + } + let seen_names = set_insert(state.seen_names, name) + let index = match map_get(state.index, name) { Absent => - map_insert(acc2, name, TypeNameExportFacts { exporter_count: 1, canonical_binding: Present { value: binding } }) + map_insert(state.index, name, TypeNameExportFacts { exporter_count: 1, canonical_binding: Present { value: canonical } }) Present { value: facts } => - map_insert(acc2, name, TypeNameExportFacts { + map_insert(state.index, name, TypeNameExportFacts { exporter_count: facts.exporter_count + 1, - canonical_binding: Present { value: binding } + canonical_binding: Present { value: canonical } }) } - ) - ) + ExportIndexModuleAccum { index: index, seen_names: seen_names } + } + ).index +} + +fn build_type_name_export_index(modules: List) -> Map { + fold(modules, init: empty_map(), f: (acc, m) => export_index_merge_module(acc: acc, m: m)) } fn module_exports_type_name(m: TypedModule, name: String) -> Bool { diff --git a/src/v1/stage0/src/v1_compiler_infer.rs b/src/v1/stage0/src/v1_compiler_infer.rs index bbbde9b4f61..5f0f208b5ed 100644 --- a/src/v1/stage0/src/v1_compiler_infer.rs +++ b/src/v1/stage0/src/v1_compiler_infer.rs @@ -14792,31 +14792,39 @@ pub fn build_type_name_export_index( modules.iter().cloned().fold( v1_rt::rc_empty_map::>(), |acc: Rc>>, m: Rc| { - m.type_env - .bindings - .values() - .cloned() - .fold(acc, |acc2, binding| { - let name = binding.name.clone(); - match v1_rt::map_get(&acc2, name.clone()) { - None => v1_rt::rc_map_insert( - acc2, - name, - Rc::new(TypeNameExportFacts { - exporter_count: 1, - canonical_binding: Some(binding), - }), - ), - Some(facts) => v1_rt::rc_map_insert( - acc2, - name, - Rc::new(TypeNameExportFacts { - exporter_count: facts.exporter_count + 1, - canonical_binding: Some(binding), - }), - ), - } - }) + let mut seen_names = std::collections::HashSet::::new(); + m.type_env.bindings.values().cloned().fold(acc, |acc2, binding| { + let name = binding.name.clone(); + if !seen_names.insert(name.clone()) { + return acc2; + } + let canonical = m + .type_env + .bindings + .values() + .filter(|b| b.name == name) + .last() + .cloned() + .unwrap_or(binding); + match v1_rt::map_get(&acc2, name.clone()) { + None => v1_rt::rc_map_insert( + acc2, + name, + Rc::new(TypeNameExportFacts { + exporter_count: 1, + canonical_binding: Some(canonical), + }), + ), + Some(facts) => v1_rt::rc_map_insert( + acc2, + name, + Rc::new(TypeNameExportFacts { + exporter_count: facts.exporter_count + 1, + canonical_binding: Some(canonical), + }), + ), + } + }) }, ) } From a6e24bf77d4b423fe2e20191c5383080d0070722 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Sun, 5 Jul 2026 05:19:14 +0000 Subject: [PATCH 26/32] fix(fmt): rustfmt v1_compiler_infer after main merge rust_tests failed cargo fmt --check on build_type_name_export_index hand-patch block introduced by 425bdc; no semantic change. Co-authored-by: Cursor --- src/v1/stage0/src/v1_compiler_infer.rs | 68 ++++++++++++++------------ 1 file changed, 36 insertions(+), 32 deletions(-) diff --git a/src/v1/stage0/src/v1_compiler_infer.rs b/src/v1/stage0/src/v1_compiler_infer.rs index 5f0f208b5ed..2d1ebeebcb6 100644 --- a/src/v1/stage0/src/v1_compiler_infer.rs +++ b/src/v1/stage0/src/v1_compiler_infer.rs @@ -14793,38 +14793,42 @@ pub fn build_type_name_export_index( v1_rt::rc_empty_map::>(), |acc: Rc>>, m: Rc| { let mut seen_names = std::collections::HashSet::::new(); - m.type_env.bindings.values().cloned().fold(acc, |acc2, binding| { - let name = binding.name.clone(); - if !seen_names.insert(name.clone()) { - return acc2; - } - let canonical = m - .type_env - .bindings - .values() - .filter(|b| b.name == name) - .last() - .cloned() - .unwrap_or(binding); - match v1_rt::map_get(&acc2, name.clone()) { - None => v1_rt::rc_map_insert( - acc2, - name, - Rc::new(TypeNameExportFacts { - exporter_count: 1, - canonical_binding: Some(canonical), - }), - ), - Some(facts) => v1_rt::rc_map_insert( - acc2, - name, - Rc::new(TypeNameExportFacts { - exporter_count: facts.exporter_count + 1, - canonical_binding: Some(canonical), - }), - ), - } - }) + m.type_env + .bindings + .values() + .cloned() + .fold(acc, |acc2, binding| { + let name = binding.name.clone(); + if !seen_names.insert(name.clone()) { + return acc2; + } + let canonical = m + .type_env + .bindings + .values() + .filter(|b| b.name == name) + .last() + .cloned() + .unwrap_or(binding); + match v1_rt::map_get(&acc2, name.clone()) { + None => v1_rt::rc_map_insert( + acc2, + name, + Rc::new(TypeNameExportFacts { + exporter_count: 1, + canonical_binding: Some(canonical), + }), + ), + Some(facts) => v1_rt::rc_map_insert( + acc2, + name, + Rc::new(TypeNameExportFacts { + exporter_count: facts.exporter_count + 1, + canonical_binding: Some(canonical), + }), + ), + } + }) }, ) } From 7bef8c0a8686919614644e0e58e6bee98820631b Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Sun, 5 Jul 2026 05:34:40 +0000 Subject: [PATCH 27/32] WIP: P3+P5: infer whole-corpus scans to per-module maps --- src/v1/04_infer.dag | 2 +- src/v1/stage0/src/v1_compiler_infer.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/v1/04_infer.dag b/src/v1/04_infer.dag index f37754c8570..fb0c3489434 100644 --- a/src/v1/04_infer.dag +++ b/src/v1/04_infer.dag @@ -6612,7 +6612,7 @@ fn export_index_merge_module(acc: Map, m: TypedModu if set_contains(state.seen_names, name) { state } else { - let canonical = match filter(m.type_env.bindings |> map_values, b => b.name == name) |> last { + let canonical = match filter(m.type_env.bindings |> map_values, b => b.name == name) |> first { Present { value: b } => b Absent => binding } diff --git a/src/v1/stage0/src/v1_compiler_infer.rs b/src/v1/stage0/src/v1_compiler_infer.rs index 2d1ebeebcb6..eb3e93db183 100644 --- a/src/v1/stage0/src/v1_compiler_infer.rs +++ b/src/v1/stage0/src/v1_compiler_infer.rs @@ -14807,7 +14807,7 @@ pub fn build_type_name_export_index( .bindings .values() .filter(|b| b.name == name) - .last() + .next() .cloned() .unwrap_or(binding); match v1_rt::map_get(&acc2, name.clone()) { From a4d01c78524093b491c8bc66c302f508427ff1d4 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Sun, 5 Jul 2026 05:51:12 +0000 Subject: [PATCH 28/32] WIP: P3+P5: infer whole-corpus scans to per-module maps --- src/v1/dag_collect_support.dag | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/v1/dag_collect_support.dag b/src/v1/dag_collect_support.dag index 5a42487a2ac..9b30ca3fe16 100644 --- a/src/v1/dag_collect_support.dag +++ b/src/v1/dag_collect_support.dag @@ -103,6 +103,16 @@ fn dag_node_surface_fingerprint_rec(node: Node) -> String { } fn dag_node_surface_fingerprint(node: Node) -> String { + dag_node_surface_fingerprint_memo(node: node) +} + +data dag_collect_fp_memo_dissolve_on: String = "Dissolve-on: ground on std.content_hash + node-keyed memo table once v2 adoption clears for this stage. Realization threads a per-collect memo reset at collect_dag_nodes entry; Rust seed uses Rc::as_ptr until then." + +fn dag_collect_fp_memo_reset() -> Bool { + true +} + +fn dag_node_surface_fingerprint_memo(node: Node) -> String { dag_node_surface_fingerprint_rec(node: node) } From e725f930ebe34dc171de00c99736b60de1ee49df Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Sun, 5 Jul 2026 05:56:24 +0000 Subject: [PATCH 29/32] fix(dag_collect): restore fp/key memo and seen-path fast path Regen had dropped the Rc::as_ptr fingerprint/key memo tables and unconditionally rehashed on every dag_collect_insert visit. Restore main's realization seed, reset memos at collect_dag_nodes entry, and only materialize fingerprints on the Absent branch (synthetic keys recover fp from the embedded key substring). Co-authored-by: Cursor --- src/v1/dag_collect.dag | 19 ++-- src/v1/stage0/src/v1_compiler_dag_collect.rs | 105 ++++++++++-------- .../src/v1_compiler_dag_collect_support.rs | 33 +++++- 3 files changed, 96 insertions(+), 61 deletions(-) diff --git a/src/v1/dag_collect.dag b/src/v1/dag_collect.dag index d771b31833c..6254c79e3ed 100644 --- a/src/v1/dag_collect.dag +++ b/src/v1/dag_collect.dag @@ -114,24 +114,19 @@ fn dag_collect_node_tree(node: Node, acc: DagCollectAcc) -> DagCollectAcc { } } +data dag_collect_synthetic_key_note: String = "Synthetic span-0 keys embed the fingerprint; the Absent branch recovers it by substring and must not re-hash." + fn dag_collect_insert(node: Node, acc: DagCollectAcc) -> DagCollectAcc { let anchor = dag_node_collection_anchor(node: node) let key = dag_node_key(node: anchor) - let fp = dag_node_fingerprint(node: anchor) match map_get(acc.seen, key) { - Present { value: prior } => - if prior == fp { - acc - } else if anchor.span.start == 0 && anchor.span.end == 0 { - DagCollectAcc { - seen: acc.seen, - order: acc.order, - collision_errors: list_push(acc.collision_errors, dag_node_key_collision_error(key: key, span: anchor.span)) - } + Present { value: _ } => acc + Absent => { + let fp = if anchor.span.start == 0 && anchor.span.end == 0 { + substring(s: key, start: 6, end: string_length(s: key)) } else { - acc + dag_node_fingerprint(node: anchor) } - Absent => { [DagCollectPending { anchor: anchor, key: key, fp: fp }] |> fold( init: acc, diff --git a/src/v1/stage0/src/v1_compiler_dag_collect.rs b/src/v1/stage0/src/v1_compiler_dag_collect.rs index f8c6341525f..3e85e63d0e8 100644 --- a/src/v1/stage0/src/v1_compiler_dag_collect.rs +++ b/src/v1/stage0/src/v1_compiler_dag_collect.rs @@ -2,7 +2,8 @@ // Source module: v1.compiler.dag_collect pub use crate::v1_compiler_dag_collect_support::{ - dag_node_key_collision_error, dag_node_surface_fingerprint, + dag_collect_fp_memo_reset, dag_node_key_collision_error, dag_node_surface_fingerprint, + dag_node_surface_fingerprint_memo, }; pub use crate::v1_compiler_dag_collect_support::{DagCollectAcc, DagCollectPending}; pub use crate::v1_compiler_infer_items::{ResolvedGraph, TypedModule}; @@ -17,10 +18,19 @@ use crate::v1_std_core::MatchPattern::*; pub use crate::v1_std_core::{Connective, ExprData, InferredNode, MatchPattern, Node}; use crate::NonEmptyBTreeSet; use crate::NonEmptyVec; +use std::cell::RefCell; use std::collections::BTreeSet; use std::collections::HashMap; use std::rc::Rc; +thread_local! { + static DAG_NODE_KEY_MEMO: RefCell> = RefCell::new(HashMap::new()); +} + +fn dag_collect_key_memo_reset() { + DAG_NODE_KEY_MEMO.with(|memo| memo.borrow_mut().clear()); +} + pub fn is_import_slot_node(n: Rc) -> bool { (import_is_all(n.clone()) || (((((n.params.clone().len() as i64) == 0) && (n.ident_span.clone() != None)) @@ -80,37 +90,47 @@ pub fn dag_node_collection_anchor(mut node: Rc) -> Rc { } } -pub fn dag_node_key(node: Rc) -> String { - { - let anchor = dag_node_collection_anchor(node); - if ((anchor.span.clone().start.clone() == 0) && (anchor.span.clone().end.clone() == 0)) { - v1_rt::concat( - ":0..0:".to_string(), - dag_node_surface_fingerprint(anchor.clone()), - ) - } else { +fn dag_node_key_uncached(anchor: Rc) -> String { + if ((anchor.span.clone().start.clone() == 0) && (anchor.span.clone().end.clone() == 0)) { + v1_rt::concat( + ":0..0:".to_string(), + dag_node_surface_fingerprint_memo(anchor.clone()), + ) + } else { + v1_rt::concat( v1_rt::concat( v1_rt::concat( v1_rt::concat( - v1_rt::concat( - v1_rt::concat(anchor.span.clone().file.clone(), ":".to_string()), - (anchor.span.clone().start.clone()).to_string(), - ), - "..".to_string(), + v1_rt::concat(anchor.span.clone().file.clone(), ":".to_string()), + (anchor.span.clone().start.clone()).to_string(), ), - (anchor.span.clone().end.clone()).to_string(), + "..".to_string(), ), - match anchor.ident.clone() { - Some(id) => v1_rt::concat(":".to_string(), (id.clone()).to_string()), - None => "".to_string(), - }, - ) - } + (anchor.span.clone().end.clone()).to_string(), + ), + match anchor.ident.clone() { + Some(id) => v1_rt::concat(":".to_string(), (id.clone()).to_string()), + None => "".to_string(), + }, + ) } } +pub fn dag_node_key(node: Rc) -> String { + let anchor = dag_node_collection_anchor(node); + let ptr = Rc::as_ptr(&anchor) as usize; + DAG_NODE_KEY_MEMO.with(|memo| { + if let Some(key) = memo.borrow().get(&ptr) { + return key.clone(); + } + let key = dag_node_key_uncached(anchor); + memo.borrow_mut().insert(ptr, key.clone()); + key + }) +} + pub fn dag_node_fingerprint(node: Rc) -> String { - dag_node_surface_fingerprint(dag_node_collection_anchor(node)) + dag_node_surface_fingerprint_memo(dag_node_collection_anchor(node)) } pub fn dag_collect_nodes_list( @@ -188,29 +208,21 @@ pub fn dag_collect_insert(node: Rc, acc: Rc) -> Rc { - if (prior.clone() == fp) { - acc.clone() + Some(_) => acc.clone(), + None => { + let fp = if ((anchor.span.clone().start.clone() == 0) + && (anchor.span.clone().end.clone() == 0)) + { + v1_rt::substring( + &key, + 6, + v1_rt::string_length(&key), + ) } else { - if ((anchor.span.clone().start.clone() == 0) - && (anchor.span.clone().end.clone() == 0)) - { - Rc::new(DagCollectAcc { - seen: acc.seen.clone(), - order: acc.order.clone(), - collision_errors: v1_rt::rc_list_push( - acc.collision_errors.clone(), - dag_node_key_collision_error(key.clone(), anchor.span.clone()), - ), - }) - } else { - acc.clone() - } - } - } - None => Rc::new(vec![Rc::new(DagCollectPending { + dag_node_fingerprint(anchor.clone()) + }; + Rc::new(vec![Rc::new(DagCollectPending { anchor: anchor.clone(), key: key.clone(), fp: fp, @@ -233,7 +245,8 @@ pub fn dag_collect_insert(node: Rc, acc: Rc) -> Rc) -> Rc { + dag_collect_fp_memo_reset(); + dag_collect_key_memo_reset(); typed.modules.clone().iter().cloned().fold( Rc::new(DagCollectAcc { seen: v1_rt::rc_empty_map::(), diff --git a/src/v1/stage0/src/v1_compiler_dag_collect_support.rs b/src/v1/stage0/src/v1_compiler_dag_collect_support.rs index e5cc2a41c45..166562243bf 100644 --- a/src/v1/stage0/src/v1_compiler_dag_collect_support.rs +++ b/src/v1/stage0/src/v1_compiler_dag_collect_support.rs @@ -16,10 +16,19 @@ pub use crate::v1_std_core::{ }; use crate::NonEmptyBTreeSet; use crate::NonEmptyVec; +use std::cell::RefCell; use std::collections::BTreeSet; use std::collections::HashMap; use std::rc::Rc; +thread_local! { + static DAG_NODE_SURFACE_FP_MEMO: RefCell> = RefCell::new(HashMap::new()); +} + +pub fn dag_collect_fp_memo_reset() { + DAG_NODE_SURFACE_FP_MEMO.with(|memo| memo.borrow_mut().clear()); +} + #[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] pub struct DagCollectAcc { pub seen: Rc>, @@ -148,19 +157,19 @@ pub fn dag_node_surface_leaf_mix(node: Rc) -> String { )) } -pub fn dag_node_surface_fingerprint_rec(node: Rc) -> String { +fn dag_node_surface_fingerprint_compute(node: Rc) -> String { stacker::maybe_grow(512 * 1024, 2 * 1024 * 1024, || { let child_hashes = Rc::new({ let mut __result = Vec::new(); for c in node.children.clone().iter().cloned() { - __result.push(dag_node_surface_fingerprint_rec(c.clone())); + __result.push(dag_node_surface_fingerprint_memo(c.clone())); } __result }); let param_hashes = Rc::new({ let mut __result = Vec::new(); for p in node.params.clone().iter().cloned() { - __result.push(dag_node_surface_fingerprint_rec(p.clone())); + __result.push(dag_node_surface_fingerprint_memo(p.clone())); } __result }); @@ -172,8 +181,24 @@ pub fn dag_node_surface_fingerprint_rec(node: Rc) -> String { }) } +pub fn dag_node_surface_fingerprint_memo(node: Rc) -> String { + let ptr = Rc::as_ptr(&node) as usize; + DAG_NODE_SURFACE_FP_MEMO.with(|memo| { + if let Some(fp) = memo.borrow().get(&ptr) { + return fp.clone(); + } + let fp = dag_node_surface_fingerprint_compute(node); + memo.borrow_mut().insert(ptr, fp.clone()); + fp + }) +} + +pub fn dag_node_surface_fingerprint_rec(node: Rc) -> String { + dag_node_surface_fingerprint_memo(node) +} + pub fn dag_node_surface_fingerprint(node: Rc) -> String { - dag_node_surface_fingerprint_rec(node) + dag_node_surface_fingerprint_memo(node) } pub fn dag_node_key_collision_error(key: String, span: Rc) -> Rc { From 39638fee34d84d3fcd440ce38086a7015f9b18ce Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Sun, 5 Jul 2026 06:18:48 +0000 Subject: [PATCH 30/32] fix(ci): split floor build step + parallel jobs (#6243 shape) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Floor step bundled cargo build + claim_executor under one 10m step budget, so batch-1 compile-clean timed out after a warm build. Split per #6263: build step 15m, floor-only 10m, job backstop 35m. Drop needs: [rust_tests] โ€” 135s cold build leaves full loud-fail window (operator/cool-hawk ruling). Co-authored-by: Cursor --- .github/workflows/ci.yml | 9 ++++++--- dag/gunbc/ci_spec.dag | 20 ++++++++++++-------- dag/gunbc/ci_workflow.dag | 21 ++++++++++++++++++--- src/v2/test/claim/ci_spec_witness_test.dag | 16 ++++++++++++---- 4 files changed, 48 insertions(+), 18 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 06ff8d34ad5..b8b1a9670a3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -18,8 +18,7 @@ env: jobs: ci: runs-on: [self-hosted, linux, arm64] - needs: [rust_tests] - timeout-minutes: 20 + timeout-minutes: 35 steps: - name: Checkout uses: actions/checkout@v5 @@ -59,7 +58,7 @@ jobs: key: cargo-ci-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('**/Cargo.lock', 'rust-toolchain.toml') }} restore-keys: | cargo-ci-${{ runner.os }}-${{ runner.arch }}- - - name: gunbc ci (.dag witnesses + gates) + - name: Build release bins (claim_executor + gunbc) run: | ROOT=$(git rev-parse --show-toplevel 2>/dev/null || pwd) set -o pipefail @@ -84,6 +83,10 @@ jobs: rm -f "$BUILD_LOG" "$ROOT/target/release/claim_executor" --verify-build-artifacts "$ROOT/target/release/claim_executor" "$ROOT/target/release/gunbc" sccache --show-stats 2>/dev/null || true + timeout-minutes: 15 + - name: gunbc ci (.dag witnesses + gates) + run: | + ROOT=$(git rev-parse --show-toplevel 2>/dev/null || pwd) git fetch --no-tags origin main 2>/dev/null || true set +e "$ROOT/target/release/claim_executor" --source-root dag --source-root src/v2 --plan-entry src/v2/workflow/ci_floor_plan.dag --plan-function gunbc_ci_floor_batches --notice-title "v2 claim corpus" diff --git a/dag/gunbc/ci_spec.dag b/dag/gunbc/ci_spec.dag index 6dc16761ba1..2d9c7ad78d3 100644 --- a/dag/gunbc/ci_spec.dag +++ b/dag/gunbc/ci_spec.dag @@ -227,21 +227,25 @@ fn scheduler_invoke(spec: CiSpec) -> String { ) } -fn gunbc_ci_run_script(spec: CiSpec) -> String { +fn gunbc_ci_floor_only_script(spec: CiSpec) -> String { concat( + concat("ROOT=", ci_repo_root_shell(), "\n"), concat( - concat(ci_release_build_script(), "\n"), - concat(git_fetch_script(policy: spec.diff_policy), "\n") - ), - concat( - concat("set +e\n", concat(scheduler_invoke(spec: spec), "\n")), - ci_floor_stamp_merge_admission_script() + concat(git_fetch_script(policy: spec.diff_policy), "\n"), + concat( + concat("set +e\n", concat(scheduler_invoke(spec: spec), "\n")), + ci_floor_stamp_merge_admission_script() + ) ) ) } +fn gunbc_ci_run_script(spec: CiSpec) -> String { + concat(concat(ci_release_build_script(), "\n"), gunbc_ci_floor_only_script(spec: spec)) +} + fn gunbc_ci_workflow_run(spec: CiSpec) -> String { - gunbc_ci_run_script(spec: spec) + gunbc_ci_floor_only_script(spec: spec) } fn gunbc_ci_rust_gate_invoke() -> String { diff --git a/dag/gunbc/ci_workflow.dag b/dag/gunbc/ci_workflow.dag index 4b1ba281031..7b2490b3c7e 100644 --- a/dag/gunbc/ci_workflow.dag +++ b/dag/gunbc/ci_workflow.dag @@ -7,6 +7,7 @@ import gunbc.ci_workflow_expressions { import gunbc.ci_spec { CiSpec, gunbc_ci_spec, gunbc_ci_workflow_run, gunbc_ci_rust_job_run, gunbc_ci_deploy_stages, DeployStage, gunbc_ci_deploy_run_script, + ci_release_build_script, } import gunbc.merge_admission_produce { ci_merge_admission_gate_script, @@ -152,6 +153,20 @@ fn ci_floor_spec_from_roster() -> CiSpec { } } +fn ci_release_build_step() -> Step { + RunStep { + name: Present { value: "Build release bins (claim_executor + gunbc)" }, + id: none, + run: ci_release_build_script(), + shell: none, + env: none, + working_directory: none, + if_condition: none, + continue_on_error: none, + timeout_minutes: Present { value: gunbc_ci_rust_gate_step_timeout_minutes } + } +} + fn ci_floor_step() -> Step { RunStep { name: Present { value: "gunbc ci (.dag witnesses + gates)" }, @@ -281,7 +296,7 @@ data gunbc_ci_job_timeout_policy_disposition: Disposition = Terminal { } fn gunbc_ci_job_backstop_timeout_minutes() -> Int { - gunbc_ci_job_timeout_policy_minutes + gunbc_ci_aux_step_timeout_minutes + gunbc_ci_prelude_allowance_minutes + gunbc_ci_rust_gate_step_timeout_minutes + gunbc_ci_job_timeout_policy_minutes + gunbc_ci_aux_step_timeout_minutes + gunbc_ci_prelude_allowance_minutes } fn gunbc_ci_rust_job_backstop_timeout_minutes() -> Int { @@ -314,8 +329,8 @@ fn ci_job() -> Job { id: "ci", name: none, runner: gunbc_ci_runner_spec(), - steps: concat(ci_prelude_steps(), [ci_floor_step(), ci_merge_admission_gate_step()]), - needs: ["rust_tests"], + steps: concat(ci_prelude_steps(), [ci_release_build_step(), ci_floor_step(), ci_merge_admission_gate_step()]), + needs: [], env: none, outputs: none, if_condition: none, diff --git a/src/v2/test/claim/ci_spec_witness_test.dag b/src/v2/test/claim/ci_spec_witness_test.dag index 7c41184436c..6bcadc170b1 100644 --- a/src/v2/test/claim/ci_spec_witness_test.dag +++ b/src/v2/test/claim/ci_spec_witness_test.dag @@ -11,7 +11,7 @@ import gunbc.ci_spec { CiSpec, DiffPolicy, DiffMergeBase, Gate, RustMonolithGate, gunbc_ci_spec, gunbc_ci_gates, gunbc_ci_floor_gates, gunbc_ci_rust_job_gates, ci_release_features, - scheduler_invoke, gunbc_ci_run_script, gunbc_ci_workflow_run, git_fetch_script, + scheduler_invoke, gunbc_ci_run_script, gunbc_ci_workflow_run, gunbc_ci_floor_only_script, git_fetch_script, ci_release_build_script, gunbc_ci_rust_job_run, ci_floor_build_verify_script } @@ -179,8 +179,15 @@ fn witness_scheduler_uses_release_binary() -> Bool { !string_contains(s: scheduler_invoke(spec: gunbc_ci_spec), pattern: "cargo run") } -fn witness_workflow_run_is_collapsed_script() -> Bool { - gunbc_ci_workflow_run(spec: gunbc_ci_spec) == gunbc_ci_run_script(spec: gunbc_ci_spec) +fn witness_workflow_run_is_floor_only_script() -> Bool { + gunbc_ci_workflow_run(spec: gunbc_ci_spec) == gunbc_ci_floor_only_script(spec: gunbc_ci_spec) +} + +fn witness_collapsed_run_script_is_build_plus_floor() -> Bool { + gunbc_ci_run_script(spec: gunbc_ci_spec) == concat( + concat(ci_release_build_script(), "\n"), + gunbc_ci_floor_only_script(spec: gunbc_ci_spec) + ) } test fn ci_spec_witnesses() -> Bool { @@ -196,7 +203,8 @@ test fn ci_spec_witnesses() -> Bool { witness_release_build_verifies_nonempty_artifacts() && witness_floor_build_verify_script_byte_identical() && witness_scheduler_uses_release_binary() && - witness_workflow_run_is_collapsed_script() && + witness_workflow_run_is_floor_only_script() && + witness_collapsed_run_script_is_build_plus_floor() && witness_gate_roster_matches_coproduct_arms() && witness_rust_gate_split_is_real() } From 776bb21a76f413cb830979999d7c2f1b854edf0d Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Sun, 5 Jul 2026 06:33:10 +0000 Subject: [PATCH 31/32] WIP: P3+P5: infer whole-corpus scans to per-module maps --- dag/gunbc/ci_spec.dag | 60 ++++++++++++++++++++++++++++++- dag/gunbc/ci_workflow.dag | 36 ++++++++++++++++--- src/v2/workflow/ci_floor_plan.dag | 23 ++++++++++-- 3 files changed, 111 insertions(+), 8 deletions(-) diff --git a/dag/gunbc/ci_spec.dag b/dag/gunbc/ci_spec.dag index 2d9c7ad78d3..78e7ecfd0a6 100644 --- a/dag/gunbc/ci_spec.dag +++ b/dag/gunbc/ci_spec.dag @@ -55,6 +55,15 @@ data gunbc_ci_floor_gates: List = project_ci_floor_gates(roster: commit_ga data gunbc_ci_floor_witness_entries: List = project_ci_floor_witness_entries(roster: commit_gate_roster) +fn project_ci_floor_gates_without_compile_clean(gates: List) -> List { + filter(gates, f: g => + match g { + DagCompileCleanGate => false + _ => true + } + ) +} + data gunbc_ci_rust_job_gates: List = project_ci_rust_job_gates(roster: commit_gate_roster) data gunbc_ci_gates: List = concat(gunbc_ci_floor_gates, gunbc_ci_rust_job_gates) @@ -86,6 +95,20 @@ data gunbc_ci_spec: CiSpec = { deploy_stages: gunbc_ci_deploy_stages, } +data gunbc_ci_floor_executor_gates: List = project_ci_floor_gates_without_compile_clean(gates: gunbc_ci_floor_gates) + +data gunbc_ci_executor_spec: CiSpec = { + gates: gunbc_ci_floor_executor_gates, + witness_entries: gunbc_ci_floor_witness_entries, + diff_policy: { + base: "origin/main", + head: "HEAD", + mode: DiffMergeBase + }, + notice_title: "v2 claim corpus", + deploy_stages: [], +} + fn ci_spec_with_witness_entries(spec: CiSpec, entries: List) -> CiSpec { CiSpec { gates: spec.gates, @@ -213,13 +236,31 @@ fn git_fetch_script(policy: DiffPolicy) -> String { data floor_plan_entry: String = "src/v2/workflow/ci_floor_plan.dag" data floor_plan_function: String = "gunbc_ci_floor_batches" +data floor_plan_executor_function: String = "gunbc_ci_floor_executor_batches" + +fn spec_includes_compile_clean_gate(spec: CiSpec) -> Bool { + fold(spec.gates, init: false, f: fn(acc, g) { + match g { + DagCompileCleanGate => true + _ => acc + } + }) +} + +fn floor_plan_function_for(spec: CiSpec) -> String { + if spec_includes_compile_clean_gate(spec: spec) { + floor_plan_function + } else { + floor_plan_executor_function + } +} fn scheduler_invoke(spec: CiSpec) -> String { concat( concat( concat( concat(concat(claim_executor_bin_shell(), witness_layer_source_flags(roots: witness_layer_roots)), concat(" --plan-entry ", floor_plan_entry)), - concat(" --plan-function ", floor_plan_function) + concat(" --plan-function ", floor_plan_function_for(spec: spec)) ), concat(concat(" --notice-title \"", spec.notice_title), "\"") ), @@ -227,6 +268,23 @@ fn scheduler_invoke(spec: CiSpec) -> String { ) } +fn gunbc_ci_compile_clean_gate_invoke() -> String { + concat( + concat("\"$ROOT/target/release/gunbc\" run", witness_layer_source_flags(roots: witness_layer_roots)), + " --entry dag/tools/floor_effect_gate_witness.dag --function dag_compile_clean_gate_passes --claim-run" + ) +} + +fn gunbc_ci_compile_clean_step_script() -> String { + concat( + concat("ROOT=", ci_repo_root_shell(), "\n"), + concat( + concat(git_fetch_script(policy: gunbc_ci_spec.diff_policy), "\n"), + gunbc_ci_compile_clean_gate_invoke() + ) + ) +} + fn gunbc_ci_floor_only_script(spec: CiSpec) -> String { concat( concat("ROOT=", ci_repo_root_shell(), "\n"), diff --git a/dag/gunbc/ci_workflow.dag b/dag/gunbc/ci_workflow.dag index 7b2490b3c7e..2a68dbdfca2 100644 --- a/dag/gunbc/ci_workflow.dag +++ b/dag/gunbc/ci_workflow.dag @@ -5,9 +5,9 @@ import gunbc.ci_workflow_expressions { ci_concurrency_group, ci_setup_rust_home, ci_cache_path, ci_cache_key, ci_cache_restore_keys } import gunbc.ci_spec { - CiSpec, gunbc_ci_spec, gunbc_ci_workflow_run, gunbc_ci_rust_job_run, + CiSpec, gunbc_ci_spec, gunbc_ci_executor_spec, gunbc_ci_workflow_run, gunbc_ci_rust_job_run, gunbc_ci_deploy_stages, DeployStage, gunbc_ci_deploy_run_script, - ci_release_build_script, + ci_release_build_script, gunbc_ci_compile_clean_step_script, } import gunbc.merge_admission_produce { ci_merge_admission_gate_script, @@ -153,6 +153,32 @@ fn ci_floor_spec_from_roster() -> CiSpec { } } +fn ci_floor_executor_spec_from_roster() -> CiSpec { + { + gates: project_ci_floor_gates_without_compile_clean( + gates: project_ci_floor_gates(roster: commit_gate_roster) + ), + witness_entries: project_ci_floor_witness_entries(roster: commit_gate_roster), + diff_policy: gunbc_ci_spec.diff_policy, + notice_title: gunbc_ci_spec.notice_title, + deploy_stages: [], + } +} + +fn ci_compile_clean_step() -> Step { + RunStep { + name: Present { value: "dag compile-clean gate" }, + id: none, + run: gunbc_ci_compile_clean_step_script(), + shell: none, + env: none, + working_directory: none, + if_condition: none, + continue_on_error: none, + timeout_minutes: Present { value: gunbc_ci_rust_gate_step_timeout_minutes } + } +} + fn ci_release_build_step() -> Step { RunStep { name: Present { value: "Build release bins (claim_executor + gunbc)" }, @@ -171,7 +197,7 @@ fn ci_floor_step() -> Step { RunStep { name: Present { value: "gunbc ci (.dag witnesses + gates)" }, id: none, - run: gunbc_ci_workflow_run(spec: ci_floor_spec_from_roster()), + run: gunbc_ci_workflow_run(spec: ci_floor_executor_spec_from_roster()), shell: none, env: none, working_directory: none, @@ -296,7 +322,7 @@ data gunbc_ci_job_timeout_policy_disposition: Disposition = Terminal { } fn gunbc_ci_job_backstop_timeout_minutes() -> Int { - gunbc_ci_rust_gate_step_timeout_minutes + gunbc_ci_job_timeout_policy_minutes + gunbc_ci_aux_step_timeout_minutes + gunbc_ci_prelude_allowance_minutes + gunbc_ci_rust_gate_step_timeout_minutes + gunbc_ci_rust_gate_step_timeout_minutes + gunbc_ci_job_timeout_policy_minutes + gunbc_ci_aux_step_timeout_minutes + gunbc_ci_prelude_allowance_minutes } fn gunbc_ci_rust_job_backstop_timeout_minutes() -> Int { @@ -329,7 +355,7 @@ fn ci_job() -> Job { id: "ci", name: none, runner: gunbc_ci_runner_spec(), - steps: concat(ci_prelude_steps(), [ci_release_build_step(), ci_floor_step(), ci_merge_admission_gate_step()]), + steps: concat(ci_prelude_steps(), [ci_release_build_step(), ci_compile_clean_step(), ci_floor_step(), ci_merge_admission_gate_step()]), needs: [], env: none, outputs: none, diff --git a/src/v2/workflow/ci_floor_plan.dag b/src/v2/workflow/ci_floor_plan.dag index c07da7e8d80..787cc710586 100644 --- a/src/v2/workflow/ci_floor_plan.dag +++ b/src/v2/workflow/ci_floor_plan.dag @@ -5,7 +5,8 @@ import gunbc.ci_spec { gunbc_ci_spec, CiSpec, Gate, RustMonolithGate, EmitHostGate, LayeringImportsGate, ExtdepsExternalAuthorityGate, DagCompileCleanGate, GeneratedArtifactDriftGate, - SourceRootIngestGate, RegenVerifyGate, SelfHostRealizedComparisonGate, EmitDeterminismGate + SourceRootIngestGate, RegenVerifyGate, SelfHostRealizedComparisonGate, EmitDeterminismGate, + gunbc_ci_executor_spec, } import v2.std.algebra { filter, length, list_snoc_item, skip, zip_map } import v2.std.collection { List } @@ -355,8 +356,22 @@ fn floor_resource_dependencies(spec: CiSpec) -> List { ) } +fn floor_data_dependencies_for_spec(spec: CiSpec) -> List { + let has_compile_gate = fold(spec.gates, init: false, f: fn(acc, g) { + match g { + DagCompileCleanGate => true + _ => acc + } + }) + if has_compile_gate { + floor_data_dependencies(spec: spec) + } else { + [] + } +} + fn floor_dependencies_with(spec: CiSpec, serialize_heavy: Bool) -> List { - let data_deps = floor_data_dependencies(spec: spec) + let data_deps = floor_data_dependencies_for_spec(spec: spec) if serialize_heavy { concat(data_deps, floor_resource_dependencies(spec: spec)) } else { @@ -430,6 +445,10 @@ fn gunbc_ci_floor_batches() -> List> { gunbc_ci_floor_realization_plan().schedule } +fn gunbc_ci_floor_executor_batches() -> List> { + gunbc_ci_floor_realization_plan_for(spec: gunbc_ci_executor_spec).schedule +} + fn gunbc_ci_floor_schedule_lens_holds() -> Bool { match schedule_lens_verdict_for_ci_floor( plan: gunbc_ci_floor_realization_plan(), From 79e0417a81179d4471bfd61a43fcf850a6bad1f5 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Sun, 5 Jul 2026 06:33:29 +0000 Subject: [PATCH 32/32] fix(ci): split compile-clean into its own 15m step (#6243 extension) Batch-1 dag_compile_clean_gate exceeded the 10m floor step budget even after the release-build split. Run compile-clean as a dedicated step (15m rust-gate budget), feed the floor executor gunbc_ci_executor_spec without DagCompileCleanGate, and clear compile-anchor data deps when the anchor is satisfied out-of-band. Job backstop 50m (15+15+10+5+5). Co-authored-by: Cursor --- .github/workflows/ci.yml | 8 +++++++- dag/gunbc/ci_workflow.dag | 10 +--------- src/v2/test/claim/ci_spec_witness_test.dag | 14 +++++++++++++- 3 files changed, 21 insertions(+), 11 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b8b1a9670a3..f6aca871f97 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -18,7 +18,7 @@ env: jobs: ci: runs-on: [self-hosted, linux, arm64] - timeout-minutes: 35 + timeout-minutes: 50 steps: - name: Checkout uses: actions/checkout@v5 @@ -84,6 +84,12 @@ jobs: "$ROOT/target/release/claim_executor" --verify-build-artifacts "$ROOT/target/release/claim_executor" "$ROOT/target/release/gunbc" sccache --show-stats 2>/dev/null || true timeout-minutes: 15 + - name: dag compile-clean gate + run: | + ROOT=$(git rev-parse --show-toplevel 2>/dev/null || pwd) + git fetch --no-tags origin main 2>/dev/null || true + "$ROOT/target/release/gunbc" run --source-root dag --source-root src/v2 --entry dag/tools/floor_effect_gate_witness.dag --function dag_compile_clean_gate_passes --claim-run + timeout-minutes: 15 - name: gunbc ci (.dag witnesses + gates) run: | ROOT=$(git rev-parse --show-toplevel 2>/dev/null || pwd) diff --git a/dag/gunbc/ci_workflow.dag b/dag/gunbc/ci_workflow.dag index 2a68dbdfca2..a2afeefc410 100644 --- a/dag/gunbc/ci_workflow.dag +++ b/dag/gunbc/ci_workflow.dag @@ -154,15 +154,7 @@ fn ci_floor_spec_from_roster() -> CiSpec { } fn ci_floor_executor_spec_from_roster() -> CiSpec { - { - gates: project_ci_floor_gates_without_compile_clean( - gates: project_ci_floor_gates(roster: commit_gate_roster) - ), - witness_entries: project_ci_floor_witness_entries(roster: commit_gate_roster), - diff_policy: gunbc_ci_spec.diff_policy, - notice_title: gunbc_ci_spec.notice_title, - deploy_stages: [], - } + gunbc_ci_executor_spec } fn ci_compile_clean_step() -> Step { diff --git a/src/v2/test/claim/ci_spec_witness_test.dag b/src/v2/test/claim/ci_spec_witness_test.dag index 6bcadc170b1..2b6d6545344 100644 --- a/src/v2/test/claim/ci_spec_witness_test.dag +++ b/src/v2/test/claim/ci_spec_witness_test.dag @@ -12,7 +12,8 @@ import gunbc.ci_spec { gunbc_ci_spec, gunbc_ci_gates, gunbc_ci_floor_gates, gunbc_ci_rust_job_gates, ci_release_features, scheduler_invoke, gunbc_ci_run_script, gunbc_ci_workflow_run, gunbc_ci_floor_only_script, git_fetch_script, - ci_release_build_script, gunbc_ci_rust_job_run, ci_floor_build_verify_script + ci_release_build_script, gunbc_ci_rust_job_run, ci_floor_build_verify_script, + gunbc_ci_executor_spec, } data golden_floor_build_verify_script: String = "\"$ROOT/target/release/claim_executor\" --verify-build-artifacts \"$ROOT/target/release/claim_executor\" \"$ROOT/target/release/gunbc\"\n" @@ -183,6 +184,16 @@ fn witness_workflow_run_is_floor_only_script() -> Bool { gunbc_ci_workflow_run(spec: gunbc_ci_spec) == gunbc_ci_floor_only_script(spec: gunbc_ci_spec) } +fn witness_executor_workflow_uses_executor_batches_plan() -> Bool { + string_contains( + s: gunbc_ci_workflow_run(spec: gunbc_ci_executor_spec), + pattern: "--plan-function gunbc_ci_floor_executor_batches" + ) && !string_contains( + s: gunbc_ci_workflow_run(spec: gunbc_ci_executor_spec), + pattern: "--plan-function gunbc_ci_floor_batches" + ) +} + fn witness_collapsed_run_script_is_build_plus_floor() -> Bool { gunbc_ci_run_script(spec: gunbc_ci_spec) == concat( concat(ci_release_build_script(), "\n"), @@ -204,6 +215,7 @@ test fn ci_spec_witnesses() -> Bool { witness_floor_build_verify_script_byte_identical() && witness_scheduler_uses_release_binary() && witness_workflow_run_is_floor_only_script() && + witness_executor_workflow_uses_executor_batches_plan() && witness_collapsed_run_script_is_build_plus_floor() && witness_gate_roster_matches_coproduct_arms() && witness_rust_gate_split_is_real()