Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
1c01f0c
WIP: P3+P5: infer whole-corpus scans to per-module maps
briansrls Jul 4, 2026
e0cc6f6
WIP: P3+P5: infer whole-corpus scans to per-module maps
briansrls Jul 4, 2026
43a30d7
P3+P5: precompute type-name and variant export indices for O(1) infer…
briansrls Jul 4, 2026
2fc2d89
fix: extract dag helpers so 04_infer folds parse under strict grammar
briansrls Jul 4, 2026
76774e4
WIP: P3+P5: infer whole-corpus scans to per-module maps
briansrls Jul 4, 2026
76bbc23
fix: balance concat wrappers in compiler_tests emit artifact
briansrls Jul 5, 2026
06c7e23
WIP: P3+P5: infer whole-corpus scans to per-module maps
briansrls Jul 5, 2026
880f21d
fix: add typecheck_module_isolated to stage0 infer hand-patch
briansrls Jul 5, 2026
de7a0e3
chore: delete orphaned local_authority_binding after P3 index rewrite
briansrls Jul 5, 2026
7141fa1
WIP: P3+P5: infer whole-corpus scans to per-module maps
briansrls Jul 5, 2026
b5831b3
fix(tests): green variant_export_surface witness tests for CI
briansrls Jul 5, 2026
f335f96
fix(tests): satisfy clippy type_complexity on witness fixture helper
briansrls Jul 5, 2026
192d3c1
Merge remote-tracking branch 'origin/main' into session/loyal-heron-170
briansrls Jul 5, 2026
d212b0f
WIP: P3+P5: infer whole-corpus scans to per-module maps
briansrls Jul 5, 2026
e42c9d2
fix(ci): regen ci.yml to match floor-job serialization policy
briansrls Jul 5, 2026
7b1ba8a
fix(ci): pair floor timeout with unified policy disposition
briansrls Jul 5, 2026
67785cc
fix(ci): scope cache to SHA so floor job inherits rust_tests build
briansrls Jul 5, 2026
523c8fb
fix(ci): revert floor timeout ratchet; hold all lanes at 10m
briansrls Jul 5, 2026
182e4a0
revert(ci): drop PR-local CI workflow experiments from #6239
briansrls Jul 5, 2026
53c3751
merge main: pick up P1 emit (#6242) + resolver fixes for floor budget
briansrls Jul 5, 2026
4012031
Revert "merge main: pick up P1 emit (#6242) + resolver fixes for floo…
briansrls Jul 5, 2026
7f45ac6
WIP: P3+P5: infer whole-corpus scans to per-module maps
briansrls Jul 5, 2026
61338e8
merge main: CI budget breach model (#6263) + enforcement rosters (#6256)
briansrls Jul 5, 2026
36f612b
fix(ci): regen ci.yml for step-level timeout policy (#6263)
briansrls Jul 5, 2026
d5755c8
fix(ci): preserve 10m job timeout cap in ci.yml regen
briansrls Jul 5, 2026
091fb3d
fix(stage0): regen compiler_tests_rust emit from .dag authority
briansrls Jul 5, 2026
72685b4
fix(ci): serialize ci floor after rust_tests for warm cache
briansrls Jul 5, 2026
425bdc4
fix(infer): dedupe exporter_count per module in type-name index
briansrls Jul 5, 2026
8922756
Merge remote-tracking branch 'origin/main' into session/loyal-heron-170
briansrls Jul 5, 2026
a6e24bf
fix(fmt): rustfmt v1_compiler_infer after main merge
briansrls Jul 5, 2026
7bef8c0
WIP: P3+P5: infer whole-corpus scans to per-module maps
briansrls Jul 5, 2026
a4d01c7
WIP: P3+P5: infer whole-corpus scans to per-module maps
briansrls Jul 5, 2026
e725f93
fix(dag_collect): restore fp/key memo and seen-path fast path
briansrls Jul 5, 2026
c10722f
merge(main): backstop CI timeouts + keep rust_tests→ci ordering
briansrls Jul 5, 2026
39638fe
fix(ci): split floor build step + parallel jobs (#6243 shape)
briansrls Jul 5, 2026
776bb21
WIP: P3+P5: infer whole-corpus scans to per-module maps
briansrls Jul 5, 2026
79e0417
fix(ci): split compile-clean into its own 15m step (#6243 extension)
briansrls Jul 5, 2026
6d16d92
WIP: P3+P5: infer whole-corpus scans to per-module maps
briansrls Jul 5, 2026
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
2 changes: 2 additions & 0 deletions dag/gunbc/stage0_emit_model.dag
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ data generated_stage0_files: List<String> = [
"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",
Expand Down
2 changes: 1 addition & 1 deletion dag/test/claim/stage0_emit_model_witness_test.dag
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
218 changes: 147 additions & 71 deletions src/v1/04_infer.dag

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion src/v1/04_method.dag
Original file line number Diff line number Diff line change
Expand Up @@ -87,8 +87,8 @@ fn builtin_function_registry() -> Map<String, Node> {
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, "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, "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)
let m = map_insert(m, "atom_identity_hash", hash_type)
Expand Down
58 changes: 22 additions & 36 deletions src/v1/04_resolve.dag
Original file line number Diff line number Diff line change
Expand Up @@ -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<T> 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
Expand Down
20 changes: 19 additions & 1 deletion src/v1/compiler_tests_rust.dag
Original file line number Diff line number Diff line change
Expand Up @@ -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<crate::v1_compiler_infer::VariantExportSurface>,\n",
" >();\n\n",
" for resolved in graph.modules.iter() {\n",
" let name = resolved.module.name.to_string();\n",
" let item_count =\n",
Expand Down Expand Up @@ -1193,6 +1197,7 @@ fn ct_profile_reconcile_test() -> String {
" 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",
Expand Down Expand Up @@ -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",
Expand Down
101 changes: 51 additions & 50 deletions src/v1/dag_collect.dag
Original file line number Diff line number Diff line change
Expand Up @@ -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 }

Expand Down Expand Up @@ -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),
Expand All @@ -65,90 +64,92 @@ 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<Node>, slots: Map<String, DagCollectSlot>, collision_errors: List<ErrorNode>) -> Map<String, DagCollectSlot> {
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<Node>, 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<String, DagCollectSlot>, collision_errors: List<ErrorNode>) -> Map<String, DagCollectSlot> {
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<String, DagCollectSlot>, collision_errors: List<ErrorNode>) -> Map<String, DagCollectSlot> {
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<String, DagCollectSlot>, collision_errors: List<ErrorNode>) -> Map<String, DagCollectSlot> {
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<String, DagCollectSlot>, collision_errors: List<ErrorNode>) -> Map<String, DagCollectSlot> {
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<String, DagCollectSlot>, collision_errors: List<ErrorNode>) -> Map<String, DagCollectSlot> {
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
match map_get(acc.seen, key) {
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 {
dag_node_fingerprint(node: anchor)
}
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
)
[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<String, DagCollectSlot>, collision_errors: List<ErrorNode>) -> Map<String, DagCollectSlot> {
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))
}
24 changes: 6 additions & 18 deletions src/v1/dag_collect_support.dag
Original file line number Diff line number Diff line change
Expand Up @@ -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<String, String>
order: List<Node>
collision_errors: List<ErrorNode>
}

fn dag_collect_slot_seq(slots: Map<String, DagCollectSlot>) -> Int {
slots |> count
}

fn dag_collect_pack_slots(slots: Map<String, DagCollectSlot>, collision_errors: List<ErrorNode>) -> 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 {
Expand Down Expand Up @@ -115,7 +103,7 @@ fn dag_node_surface_fingerprint_rec(node: Node) -> String {
}

fn dag_node_surface_fingerprint(node: Node) -> String {
dag_node_surface_fingerprint_rec(node: node)
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."
Expand All @@ -125,7 +113,7 @@ fn dag_collect_fp_memo_reset() -> Bool {
}

fn dag_node_surface_fingerprint_memo(node: Node) -> String {
dag_node_surface_fingerprint(node: node)
dag_node_surface_fingerprint_rec(node: node)
}

fn dag_node_key_collision_error(key: String, span: SourceSpan) -> ErrorNode {
Expand Down
8 changes: 2 additions & 6 deletions src/v1/stage0/src/bin/regen_stage0.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -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).
Expand Down
Loading
Loading