Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 6 additions & 4 deletions src/v1/03_normalize.dag
Original file line number Diff line number Diff line change
Expand Up @@ -55,11 +55,13 @@ fn check_bare_containers(n: Node, module_name: String, source_indices: Map<Strin
flat_map([self_diags, child_diags, param_diags, type_ann_diags, inferred_diags, body_diags, uses_diags, prop_diags], d => d)
}

fn normalize_module_diagnostics(m: ResolvedModule, source_indices: Map<String, NewlineIndex>) -> List<ErrorNode> {
let items = module_items(n: m.module)
flat_map(items, item => check_bare_containers(n: item, module_name: authored_name_at(source_indices: source_indices, node: m.module), source_indices: source_indices))
}

fn normalize_graph(graph: ModuleGraph, source_indices: Map<String, NewlineIndex>) -> NormalizeResult {
let diags = flat_map(graph.modules, m => {
let items = module_items(n: m.module)
flat_map(items, item => check_bare_containers(n: item, module_name: authored_name_at(source_indices: source_indices, node: m.module), source_indices: source_indices))
})
let diags = flat_map(graph.modules, m => normalize_module_diagnostics(m: m, source_indices: source_indices))
NormalizeResult {
graph: graph,
diagnostics: diags
Expand Down
12 changes: 7 additions & 5 deletions src/v1/compile.dag
Original file line number Diff line number Diff line change
Expand Up @@ -93,14 +93,16 @@ fn build_recursion_context(typed: ResolvedGraph) -> RecursionContext {
RecursionContext {}
}

fn module_ownership_proofs(m: TypedModule) -> List<OwnershipProof> {
m.items |> filter(item => item.body != Absent) |> map(item =>
analyze_ownership(func_name: authored_name_at(source_indices: m.type_env.source_indices, node: item), params: item.params, body: item.body.value, si: m.type_env.source_indices)
)
}

fn extract_ownership_proofs(typed: ResolvedGraph) -> List<OwnershipProof> {
typed.modules
|> filter(m => m.items |> any(item => item.body != Absent))
|> flat_map(m =>
m.items |> filter(item => item.body != Absent) |> map(item =>
analyze_ownership(func_name: authored_name_at(source_indices: m.type_env.source_indices, node: item), params: item.params, body: item.body.value, si: m.type_env.source_indices)
)
)
|> flat_map(m => module_ownership_proofs(m: m))
}

fn ownership_diagnostics(proofs: List<OwnershipProof>) -> List<ErrorNode> {
Expand Down
88 changes: 81 additions & 7 deletions src/v1/stage0/src/cli_run.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2695,6 +2695,18 @@ pub struct MultiEntryIndex {
),
>,
>,
// Per-module memo for the two per-entry recomputations the resolve-split receipt
// priced at ~15% of whole-corpus resolve (normalize 8% + ownership 7% — the
// "Track A denomination receipt", docs/plans/v1-run-stability-throughline.md §1).
// Normalize diagnostics are a pure function of the parsed module node
// (v1.compiler.normalize.normalize_module_diagnostics) and ownership diagnostics
// a pure function of the typed module (v1.compiler.compile.module_ownership_proofs)
// — both stable per declaring-file path within one process, the same in-process
// source-stability contract parse_cache and typed_module_cache already ride
// (module_source_identity walls name↔file divergence loud). Keyed by the module's
// declaring file (span.file, the parse_cache key's own identity).
normalize_diag_cache: RefCell<HashMap<String, Rc<im_rc::Vector<Rc<ErrorNode>>>>>,
ownership_diag_cache: RefCell<HashMap<String, Rc<im_rc::Vector<Rc<ErrorNode>>>>>,
}

// Once-per-node resolve receipt (union-resolve minimum-upper-bound contract, §6.2 of
Expand Down Expand Up @@ -2758,6 +2770,8 @@ pub fn build_multi_entry_index(source_roots: &[String]) -> MultiEntryIndex {
typed_module_cache: RefCell::new(HashMap::new()),
module_source_identity: RefCell::new(HashMap::new()),
resolved_graph_memo: RefCell::new(HashMap::new()),
normalize_diag_cache: RefCell::new(HashMap::new()),
ownership_diag_cache: RefCell::new(HashMap::new()),
}
}

Expand All @@ -2777,6 +2791,8 @@ fn build_v1_attribution_multi_entry_index() -> MultiEntryIndex {
typed_module_cache: RefCell::new(HashMap::new()),
module_source_identity: RefCell::new(HashMap::new()),
resolved_graph_memo: RefCell::new(HashMap::new()),
normalize_diag_cache: RefCell::new(HashMap::new()),
ownership_diag_cache: RefCell::new(HashMap::new()),
}
}

Expand Down Expand Up @@ -3019,21 +3035,47 @@ fn resolve_entry_with_parse_cache(
resolve_stage_slot_add(|s| s.resolve += resolve_started.elapsed().as_nanos());

let normalize_started = std::time::Instant::now();
let norm = v1_compiler_normalize::normalize_graph(graph.clone(), source_indices.clone());
// Per-module memo (normalize_diag_cache): normalize is diagnostics-only — the
// authority passes the graph through unchanged (v1.compiler.normalize
// `NormalizeResult { graph: graph, .. }`) — and its per-module row
// `normalize_module_diagnostics` is a pure function of the parsed module node,
// so an entry pays only for modules this process has not normalized before
// (resolve-split receipt: normalize was 8% of whole-corpus resolve, recomputed
// per entry at zero marginal information).
let mut norm_diag_vec: im_rc::Vector<Rc<ErrorNode>> = im_rc::Vector::new();
for m in graph.modules.iter() {
let key = m.module.span.file.clone();
let cached = index.normalize_diag_cache.borrow().get(&key).cloned();
let module_diags = match cached {
Some(hit) => hit,
None => {
let computed = v1_compiler_normalize::normalize_module_diagnostics(
m.clone(),
source_indices.clone(),
);
index
.normalize_diag_cache
.borrow_mut()
.insert(key, computed.clone());
computed
}
};
norm_diag_vec.extend(module_diags.iter().cloned());
}
let norm_diags = Rc::new(norm_diag_vec);

if norm
.diagnostics
if norm_diags
.iter()
.any(|d| is_error_diagnostic(d.diagnostic.clone()))
{
return Err(format_error_nodes(&norm.diagnostics, &source_indices));
return Err(format_error_nodes(&norm_diags, &source_indices));
}
resolve_stage_slot_add(|s| s.normalize += normalize_started.elapsed().as_nanos());

set_phase(FloorPhase::Typecheck, entry_file);
let reconcile_started = std::time::Instant::now();
let typed = reconcile_with_typed_cache(
norm.graph.clone(),
graph.clone(),
source_indices.clone(),
global_table,
&index.typed_module_cache,
Expand Down Expand Up @@ -3064,8 +3106,33 @@ fn resolve_entry_with_parse_cache(
}

let ownership_started = std::time::Instant::now();
let ownership = v1_compiler_compile::extract_ownership_proofs(typed.clone());
let ownership_diags = v1_compiler_compile::ownership_diagnostics(ownership);
// Per-module memo (ownership_diag_cache): ownership proofs are a pure per-module
// map (v1.compiler.compile `module_ownership_proofs`; the authority's graph fold
// is exactly this row flat_mapped in module order) and `ownership_diagnostics`
// distributes over per-module concatenation, so the diagnostic list assembled in
// `typed.modules` order is identical to the graph-grain computation — a module
// with no bodied items contributes the same empty row the authority's filter
// skips. First-touch per module; the per-entry graph-grain rerun (7% of
// whole-corpus resolve in the resolve-split receipt) collapses to cache reads.
let mut ownership_diag_vec: im_rc::Vector<Rc<ErrorNode>> = im_rc::Vector::new();
for m in typed.modules.iter() {
let key = m.module.span.file.clone();
let cached = index.ownership_diag_cache.borrow().get(&key).cloned();
let module_diags = match cached {
Some(hit) => hit,
None => {
let proofs = v1_compiler_compile::module_ownership_proofs(m.clone());
let computed = v1_compiler_compile::ownership_diagnostics(proofs);
index
.ownership_diag_cache
.borrow_mut()
.insert(key, computed.clone());
computed
}
};
ownership_diag_vec.extend(module_diags.iter().cloned());
}
let ownership_diags = Rc::new(ownership_diag_vec);
if ownership_diags
.iter()
.any(|d| is_error_diagnostic(d.diagnostic.clone()))
Expand Down Expand Up @@ -11900,6 +11967,13 @@ pub fn module_declaration_facts(pool_roots: &[String]) -> Vec<ModuleDeclarationF
const NON_FOLD_RESIDUE_ROSTER: &[&str] = &[
"dag/extdeps/bmc/webui/nbd_proxy_serve.dag::shell_command_leading_lit_text",
"dag/extdeps/bmc/webui/nbd_proxy_serve.dag::shell_rawline_starts_with_tool",
// 2026-07-13 backfill: #6533 (Wave 2 frontier probe) landed this site unrostered — the nfr
// witnesses are corpus-read host-fed rows the affected-set selection did not run for that
// diff, so the red surfaced on the next whole-corpus cold sweep, not on the landing PR
// (third instance of the masking class receipted on #6530). Declared here so the ratchet
// re-arms; burns down with the frontier probe's fold migration. (#6533's other wildcard
// fn, compiler_frontier_probe_entry_test.dag, is outside the scan universe — no row.)
"src/v2/compiler/self_host/frontier_probe_types.dag::frontier_blocker_class_matches",
// 2026-07-12 backfill: sites that landed unrostered while the gate was red during the
// land-red-with-local-proof era (revoked 2026-07-12). Declared here so the ratchet
// re-arms; each burns down with its owning file's fold migration.
Expand Down
57 changes: 27 additions & 30 deletions src/v1/stage0/src/v1_compiler_compile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,32 @@ pub fn build_recursion_context(typed: Rc<ResolvedGraph>) -> RecursionContext {
RecursionContext {}
}

pub fn module_ownership_proofs(m: Rc<TypedModule>) -> Rc<Vec<Rc<OwnershipProof>>> {
Rc::new({
let mut __result = Vec::new();
for item in Rc::new({
let mut __result = Vec::new();
for item in m.items.clone().iter().cloned() {
if (item.body.clone() != None) {
__result.push(item);
}
}
__result
})
.iter()
.cloned()
{
__result.push(analyze_ownership(
authored_name_at(m.type_env.clone().source_indices.clone(), item.clone()),
item.params.clone(),
item.body.clone().clone().unwrap(),
m.type_env.clone().source_indices.clone(),
));
}
__result
})
}

pub fn extract_ownership_proofs(typed: Rc<ResolvedGraph>) -> Rc<Vec<Rc<OwnershipProof>>> {
Rc::new({
let mut __result = Vec::new();
Expand All @@ -215,36 +241,7 @@ pub fn extract_ownership_proofs(typed: Rc<ResolvedGraph>) -> Rc<Vec<Rc<Ownership
.iter()
.cloned()
{
__result.extend(
(*Rc::new({
let mut __result = Vec::new();
for item in Rc::new({
let mut __result = Vec::new();
for item in m.items.clone().iter().cloned() {
if (item.body.clone() != None) {
__result.push(item);
}
}
__result
})
.iter()
.cloned()
{
__result.push(analyze_ownership(
authored_name_at(
m.type_env.clone().source_indices.clone(),
item.clone(),
),
item.params.clone(),
item.body.clone().clone().unwrap(),
m.type_env.clone().source_indices.clone(),
));
}
__result
}))
.iter()
.cloned(),
);
__result.extend((*module_ownership_proofs(m.clone())).iter().cloned());
}
__result
})
Expand Down
47 changes: 27 additions & 20 deletions src/v1/stage0/src/v1_compiler_normalize.rs
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,30 @@ pub fn check_bare_containers(
})
}

pub fn normalize_module_diagnostics(
m: Rc<ResolvedModule>,
source_indices: Rc<HashMap<String, Rc<NewlineIndex>>>,
) -> Rc<Vec<Rc<ErrorNode>>> {
{
let items = module_items(m.module.clone());
Rc::new({
let mut __result = Vec::new();
for item in items.clone().iter().cloned() {
__result.extend(
(*check_bare_containers(
item.clone(),
authored_name_at(source_indices.clone(), m.module.clone()),
source_indices.clone(),
))
.iter()
.cloned(),
);
}
__result
})
}
}

pub fn normalize_graph(
graph: Rc<ModuleGraph>,
source_indices: Rc<HashMap<String, Rc<NewlineIndex>>>,
Expand All @@ -186,26 +210,9 @@ pub fn normalize_graph(
let mut __result = Vec::new();
for m in graph.modules.clone().iter().cloned() {
__result.extend(
(*{
let items = module_items(m.module.clone());
Rc::new({
let mut __result = Vec::new();
for item in items.clone().iter().cloned() {
__result.extend(
(*check_bare_containers(
item.clone(),
authored_name_at(source_indices.clone(), m.module.clone()),
source_indices.clone(),
))
.iter()
.cloned(),
);
}
__result
})
})
.iter()
.cloned(),
(*normalize_module_diagnostics(m.clone(), source_indices.clone()))
.iter()
.cloned(),
);
}
__result
Expand Down
Loading