diff --git a/src/v1/05_emit_rust.dag b/src/v1/05_emit_rust.dag index 23fbc08e3b0..daa1565ecc7 100644 --- a/src/v1/05_emit_rust.dag +++ b/src/v1/05_emit_rust.dag @@ -64,6 +64,7 @@ import v1.compiler.trait_derive_emit { v1_emit_struct_from_capability_table, v1_emit_enum_supplemental_impls, v1_generic_params_needing_clone_bound, v1_emit_type_params_with_clone_bounds, + v1_emit_type_params_with_clone_and_ord_bounds, v1_clone_bounded_type_params, v1_item_clone_bounded_param_names, v1_item_clone_undecided_head, v1_map_key_required_type_names, v1_item_field_type_exprs, v1_with_map_key_requirement, v1_clone_impl_required_type_params, @@ -73,7 +74,8 @@ import v1.compiler.trait_derive_emit { } import v1.compiler.trait_bound_witness { v1_rc_match_scrutinee_clone_bound_param_names, v1_union_clone_param_names, - v1_call_forwarding_clone_bound_wrapper_param_names + v1_call_forwarding_clone_bound_wrapper_param_names, + v1_set_union_ord_bound_param_names } import std.types { is_container_type, is_kernel_type, container_template_algebra } @@ -5969,6 +5971,89 @@ fn v1_fn_body_derived_clone_param_names(params: List, inferred: Node, body ) } +data v1_collect_set_union_call_elem_type_names_note: String = "Node-consuming extraction walker for v1_set_union_ord_bound_param_names (src/v1/trait_bound_witness.dag), gunbc dashboard node://adhoc-a9f61ade-340. Walks a function body collecting the resolved element-type-argument name of every argument passed to a set_union(..) call anywhere in the body -- not scoped to a direct top-level shape (unlike BoundedToDirectTopLevelMatchBody/BoundedToDirectSingleCallLambdaBody above) because the two real specimens this was traced from (audience_subset/audience_join, dag/std/authorization_profile.dag) both reach their set_union call through a nested match, one of them two matches deep. The variant coverage below starts from ownership.dag's collect_callable_refs (same file's own precedent for a full-body Node walker over this same expr_data vocabulary) rather than a blind recursion over .children -- ExprCall/ExprMatch/ExprIf/ExprLet/ExprBlock/ExprRecordLit/ExprMethodCall/ExprReturn/ExprLambda/ExprForEach/ExprFieldAccess are each named explicitly, matching that precedent -- plus one addition that precedent lacks: ExprBinOp, walking both binop_left/binop_right, needed because audience_subset's set_union call sits inside a `==` comparison (set_union(members, right_members) == right_members) and would otherwise be silently unreached; any other variant (ExprLiteral, ExprVar, etc.) is a base case with no nested expr to walk and contributes nothing." + +fn v1_collect_set_union_call_elem_type_names(texpr: Node, si: Map) -> List { + match texpr.expr_data { + ExprCall => + let callee_name = expr_call_func_at(texpr: texpr, source_indices: si) + let arg_hits = texpr.children |> fold(init: [], f: (acc, a) => + concat(acc, v1_collect_set_union_call_elem_type_names(texpr: arg_value(n: a), si: si)) + ) + if callee_name == "set_union" { + let elem_hits = texpr.children |> fold(init: [], f: (acc, a) => + let arg = arg_value(n: a) + match arg.inferred { + Present { value: Resolved { node: rt } } => + concat(acc, rt.children |> map(c => authored_name_at(source_indices: si, node: c))) + _ => acc + } + ) + concat(elem_hits, arg_hits) + } else { + arg_hits + } + ExprMatch => + let scrut = v1_collect_set_union_call_elem_type_names(texpr: match_scrutinee(texpr: texpr), si: si) + match_arm_nodes(texpr: texpr) |> fold(init: scrut, f: (acc, arm) => + concat(acc, v1_collect_set_union_call_elem_type_names(texpr: arm_body(n: arm), si: si)) + ) + ExprIf => + let cond = v1_collect_set_union_call_elem_type_names(texpr: if_condition(texpr: texpr), si: si) + let then_br = v1_collect_set_union_call_elem_type_names(texpr: if_then_branch(texpr: texpr), si: si) + let else_br = match if_else_branch(texpr: texpr) { + Present { value: eb } => v1_collect_set_union_call_elem_type_names(texpr: eb, si: si) + Absent => [] + } + concat(concat(cond, then_br), else_br) + ExprLet => + let v = v1_collect_set_union_call_elem_type_names(texpr: let_value(texpr: texpr), si: si) + match let_body(texpr: texpr) { + Present { value: lb } => concat(v, v1_collect_set_union_call_elem_type_names(texpr: lb, si: si)) + Absent => v + } + ExprBlock => + texpr.children |> fold(init: [], f: (acc, child) => + concat(acc, v1_collect_set_union_call_elem_type_names(texpr: child, si: si)) + ) + ExprRecordLit => + texpr.children |> fold(init: [], f: (acc, field) => + concat(acc, v1_collect_set_union_call_elem_type_names(texpr: arg_value(n: field), si: si)) + ) + ExprMethodCall { method_semantics: _ } => + let recv = v1_collect_set_union_call_elem_type_names(texpr: method_receiver(texpr: texpr), si: si) + method_arg_nodes(texpr: texpr) |> fold(init: recv, f: (acc, a) => + concat(acc, v1_collect_set_union_call_elem_type_names(texpr: arg_value(n: a), si: si)) + ) + ExprReturn => + match texpr.children |> first { + Present { value: child } => v1_collect_set_union_call_elem_type_names(texpr: child, si: si) + Absent => [] + } + ExprLambda => + v1_collect_set_union_call_elem_type_names(texpr: lambda_body(texpr: texpr), si: si) + ExprForEach => + let col = v1_collect_set_union_call_elem_type_names(texpr: foreach_collection(texpr: texpr), si: si) + concat(col, v1_collect_set_union_call_elem_type_names(texpr: foreach_body(texpr: texpr), si: si)) + ExprFieldAccess => + v1_collect_set_union_call_elem_type_names(texpr: field_access_base(texpr: texpr), si: si) + ExprBinOp { op: _, algebra_field: _ } => + let l = v1_collect_set_union_call_elem_type_names(texpr: binop_left(texpr: texpr), si: si) + let r = v1_collect_set_union_call_elem_type_names(texpr: binop_right(texpr: texpr), si: si) + concat(l, r) + _ => [] + } +} + +fn v1_fn_body_set_union_ord_bound_param_names( + generic_param_names: List, + body: Node, + source_indices: Map +) -> List { + let elem_type_names = v1_collect_set_union_call_elem_type_names(texpr: body, si: source_indices) + v1_set_union_ord_bound_param_names(generic_param_names: generic_param_names, set_union_call_elem_type_names: elem_type_names) +} + fn v1_call_forwarding_clone_bound_param_names( generic_param_names: List, body: Node, @@ -6100,7 +6185,15 @@ fn emit_fn_def(name: String, params: List, inferred: Node, body: Node, reg derived_clone_param_names_with_rc_match } let needs_clone_bound = (clone_param_names |> count) > 0 - let type_params_str = if needs_clone_bound { + let ord_param_names = v1_fn_body_set_union_ord_bound_param_names( + generic_param_names: generic_param_names, + body: body, + source_indices: si + ) + let needs_ord_bound = (ord_param_names |> count) > 0 + let type_params_str = if needs_ord_bound { + v1_emit_type_params_with_clone_and_ord_bounds(params: type_params, clone_param_names: clone_param_names, ord_param_names: ord_param_names, source_indices: si) + } else if needs_clone_bound { v1_emit_type_params_with_clone_bounds(params: type_params, clone_param_names: clone_param_names, source_indices: si) } else { emit_type_params(params: type_params, source_indices: si) diff --git a/src/v1/stage0/src/v1_compiler_emit_rust.rs b/src/v1/stage0/src/v1_compiler_emit_rust.rs index bcd1f8bb492..1527060673f 100644 --- a/src/v1/stage0/src/v1_compiler_emit_rust.rs +++ b/src/v1/stage0/src/v1_compiler_emit_rust.rs @@ -119,16 +119,17 @@ pub use crate::v1_compiler_resolve::get_exported_names; pub use crate::v1_compiler_runtime_rust::rust_runtime_source; pub use crate::v1_compiler_trait_bound_witness::{ v1_call_forwarding_clone_bound_wrapper_param_names, - v1_rc_match_scrutinee_clone_bound_param_names, v1_union_clone_param_names, + v1_rc_match_scrutinee_clone_bound_param_names, v1_set_union_ord_bound_param_names, + v1_union_clone_param_names, }; pub use crate::v1_compiler_trait_derive_emit::{ rust_nominal_identity_carrier_shape_eligible, rust_symbol_wrapped_ord_carrier_shape_eligible, trait_derive_emit_fn_clone_bound_keyed_carrier_module, v1_clone_bounded_type_params, v1_clone_impl_required_type_params, v1_emit_enum_derives, v1_emit_enum_supplemental_impls, - v1_emit_struct_from_capability_table, v1_emit_type_params_with_clone_bounds, - v1_generic_params_needing_clone_bound, v1_item_clone_bounded_param_names, - v1_item_clone_undecided_head, v1_item_field_type_exprs, v1_map_key_required_type_names, - v1_trait_derive_refuse, v1_with_map_key_requirement, + v1_emit_struct_from_capability_table, v1_emit_type_params_with_clone_and_ord_bounds, + v1_emit_type_params_with_clone_bounds, v1_generic_params_needing_clone_bound, + v1_item_clone_bounded_param_names, v1_item_clone_undecided_head, v1_item_field_type_exprs, + v1_map_key_required_type_names, v1_trait_derive_refuse, v1_with_map_key_requirement, }; use crate::v1_rt; use crate::v1_rt::{VecCompat, VecJoin}; @@ -14888,6 +14889,204 @@ pub fn v1_fn_body_derived_clone_param_names( } } +pub fn v1_collect_set_union_call_elem_type_names_note() -> String { + thread_local! { + static CACHED: String = { + "Node-consuming extraction walker for v1_set_union_ord_bound_param_names (src/v1/trait_bound_witness.dag), gunbc dashboard node://adhoc-a9f61ade-340. Walks a function body collecting the resolved element-type-argument name of every argument passed to a set_union(..) call anywhere in the body -- not scoped to a direct top-level shape (unlike BoundedToDirectTopLevelMatchBody/BoundedToDirectSingleCallLambdaBody above) because the two real specimens this was traced from (audience_subset/audience_join, dag/std/authorization_profile.dag) both reach their set_union call through a nested match, one of them two matches deep. The variant coverage below starts from ownership.dag's collect_callable_refs (same file's own precedent for a full-body Node walker over this same expr_data vocabulary) rather than a blind recursion over .children -- ExprCall/ExprMatch/ExprIf/ExprLet/ExprBlock/ExprRecordLit/ExprMethodCall/ExprReturn/ExprLambda/ExprForEach/ExprFieldAccess are each named explicitly, matching that precedent -- plus one addition that precedent lacks: ExprBinOp, walking both binop_left/binop_right, needed because audience_subset's set_union call sits inside a `==` comparison (set_union(members, right_members) == right_members) and would otherwise be silently unreached; any other variant (ExprLiteral, ExprVar, etc.) is a base case with no nested expr to walk and contributes nothing.".to_string() + }; + } + CACHED.with(|c: &String| c.clone()) +} + +pub fn v1_collect_set_union_call_elem_type_names( + texpr: Rc, + si: Rc>>, +) -> Rc> { + stacker::maybe_grow(512 * 1024, 2 * 1024 * 1024, || { + match (*texpr.expr_data.clone()).clone() { + ExprData::ExprCall { .. } => { + let callee_name = expr_call_func_at(texpr.clone(), si.clone()); + let arg_hits = texpr.children.clone().iter().cloned().fold( + Rc::new(vec![]), + |acc: _, a: Rc| { + v1_rt::concat( + acc, + v1_collect_set_union_call_elem_type_names( + arg_value(a.clone()), + si.clone(), + ), + ) + }, + ); + if (callee_name.clone() == "set_union".to_string()) { + { + let elem_hits = texpr.children.clone().iter().cloned().fold( + Rc::new(vec![]), + |acc: _, a: Rc| { + let arg = arg_value(a.clone()); + match arg.inferred.clone().as_deref().cloned() { + Some(InferredNode::Resolved { node: rt, .. }) => v1_rt::concat( + acc.clone(), + Rc::new({ + let mut __result = Vec::new(); + for c in rt.children.clone().iter().cloned() { + __result + .push(authored_name_at(si.clone(), c.clone())); + } + __result + }), + ), + _ => acc.clone(), + } + }, + ); + v1_rt::concat(elem_hits.clone(), arg_hits.clone()) + } + } else { + arg_hits.clone() + } + } + ExprData::ExprMatch => { + let scrut = v1_collect_set_union_call_elem_type_names( + match_scrutinee(texpr.clone()), + si.clone(), + ); + match_arm_nodes(texpr.clone()).iter().cloned().fold( + scrut.clone(), + |acc: Rc>, arm: Rc| { + v1_rt::concat( + acc, + v1_collect_set_union_call_elem_type_names( + arm_body(arm.clone()), + si.clone(), + ), + ) + }, + ) + } + ExprData::ExprIf => { + let cond = v1_collect_set_union_call_elem_type_names( + if_condition(texpr.clone()), + si.clone(), + ); + let then_br = v1_collect_set_union_call_elem_type_names( + if_then_branch(texpr.clone()), + si.clone(), + ); + let else_br = match if_else_branch(texpr.clone()) { + Some(eb) => v1_collect_set_union_call_elem_type_names(eb.clone(), si.clone()), + None => Rc::new(vec![]), + }; + v1_rt::concat( + v1_rt::concat(cond.clone(), then_br.clone()), + else_br.clone(), + ) + } + ExprData::ExprLet => { + let v = + v1_collect_set_union_call_elem_type_names(let_value(texpr.clone()), si.clone()); + match let_body(texpr.clone()) { + Some(lb) => v1_rt::concat( + v.clone(), + v1_collect_set_union_call_elem_type_names(lb.clone(), si.clone()), + ), + None => v.clone(), + } + } + ExprData::ExprBlock => texpr.children.clone().iter().cloned().fold( + Rc::new(vec![]), + |acc: Rc>, child: Rc| { + v1_rt::concat( + acc, + v1_collect_set_union_call_elem_type_names(child.clone(), si.clone()), + ) + }, + ), + ExprData::ExprRecordLit { .. } => texpr.children.clone().iter().cloned().fold( + Rc::new(vec![]), + |acc: Rc>, field: Rc| { + v1_rt::concat( + acc, + v1_collect_set_union_call_elem_type_names( + arg_value(field.clone()), + si.clone(), + ), + ) + }, + ), + ExprData::ExprMethodCall { + method_semantics: _, + .. + } => { + let recv = v1_collect_set_union_call_elem_type_names( + method_receiver(texpr.clone()), + si.clone(), + ); + method_arg_nodes(texpr.clone()).iter().cloned().fold( + recv.clone(), + |acc: Rc>, a: Rc| { + v1_rt::concat( + acc, + v1_collect_set_union_call_elem_type_names( + arg_value(a.clone()), + si.clone(), + ), + ) + }, + ) + } + ExprData::ExprReturn => match texpr.children.clone().first().cloned() { + Some(child) => v1_collect_set_union_call_elem_type_names(child.clone(), si.clone()), + None => Rc::new(vec![]), + }, + ExprData::ExprLambda => { + v1_collect_set_union_call_elem_type_names(lambda_body(texpr.clone()), si.clone()) + } + ExprData::ExprForEach => { + let col = v1_collect_set_union_call_elem_type_names( + foreach_collection(texpr.clone()), + si.clone(), + ); + v1_rt::concat( + col.clone(), + v1_collect_set_union_call_elem_type_names( + foreach_body(texpr.clone()), + si.clone(), + ), + ) + } + ExprData::ExprFieldAccess { .. } => v1_collect_set_union_call_elem_type_names( + field_access_base(texpr.clone()), + si.clone(), + ), + ExprData::ExprBinOp { .. } => { + let l = v1_collect_set_union_call_elem_type_names( + binop_left(texpr.clone()), + si.clone(), + ); + let r = v1_collect_set_union_call_elem_type_names( + binop_right(texpr.clone()), + si.clone(), + ); + v1_rt::concat(l.clone(), r.clone()) + } + _ => Rc::new(vec![]), + } + }) +} + +pub fn v1_fn_body_set_union_ord_bound_param_names( + generic_param_names: Rc>, + body: Rc, + source_indices: Rc>>, +) -> Rc> { + { + let elem_type_names = + v1_collect_set_union_call_elem_type_names(body.clone(), source_indices.clone()); + v1_set_union_ord_bound_param_names(generic_param_names.clone(), elem_type_names.clone()) + } +} + pub fn v1_call_forwarding_clone_bound_param_names( generic_param_names: Rc>, body: Rc, @@ -15075,14 +15274,29 @@ pub fn emit_fn_def( derived_clone_param_names_with_rc_match.clone() }; let needs_clone_bound = ((clone_param_names.clone().len() as i64) > 0); - let type_params_str = if needs_clone_bound.clone() { - v1_emit_type_params_with_clone_bounds( + let ord_param_names = v1_fn_body_set_union_ord_bound_param_names( + generic_param_names.clone(), + body.clone(), + si.clone(), + ); + let needs_ord_bound = ((ord_param_names.clone().len() as i64) > 0); + let type_params_str = if needs_ord_bound.clone() { + v1_emit_type_params_with_clone_and_ord_bounds( type_params.clone(), clone_param_names.clone(), + ord_param_names.clone(), si.clone(), ) } else { - emit_type_params(type_params.clone(), si.clone()) + if needs_clone_bound.clone() { + v1_emit_type_params_with_clone_bounds( + type_params.clone(), + clone_param_names.clone(), + si.clone(), + ) + } else { + emit_type_params(type_params.clone(), si.clone()) + } }; let params_str = emit_params( value_params.clone(), diff --git a/src/v1/stage0/src/v1_compiler_trait_bound_witness.rs b/src/v1/stage0/src/v1_compiler_trait_bound_witness.rs index 6c0d8167d29..1ceb239aca2 100644 --- a/src/v1/stage0/src/v1_compiler_trait_bound_witness.rs +++ b/src/v1/stage0/src/v1_compiler_trait_bound_witness.rs @@ -107,6 +107,49 @@ pub fn v1_union_clone_param_names( ) } +pub fn v1_set_union_ord_bound_note() -> String { + thread_local! { + static CACHED: String = { + "gunbc dashboard node://adhoc-a9f61ade-340 (E0277 root A residual: fn-generic bound inference is Clone-only). std.authorization_profile's audience_subset

/audience_join

(dag/std/authorization_profile.dag) call the set_union builtin on Set

values (EnumeratedAudience { members: Set

}); set_union lowers to v1_rt::rc_set_union::(src/v1/runtime_rust.dag), so the emitted signature needs P: Ord, not merely P: Clone. This is a DIFFERENT site from docs/probes/e0277_root_partition_2026-08-21.md's Root-A Ord-5 population, which is a struct-DERIVE bound on EnumeratedAudience's own members: Set

field (AudienceSet's #[derive(..)] header) -- that population is out of scope here and stays blocked on v2.std.compilers.target_model.target_derive_supplemental_generic_bound_contract per that document's own dissolution clause. This row is the fn-SIGNATURE half: the whole v1 supplemental-bound apparatus (v1_generic_params_needing_clone_bound, v1_clone_bounded_type_params, this module's own two Clone triggers above) is Clone-only by construction -- there is no arm anywhere in trait_derive_emit.dag or trait_bound_witness.dag that can emit a bound other than Clone on a function's own generic parameter list (confirmed by grep: no ': Ord' render exists at any fn-generic call site). Rather than widen the Clone apparatus to a general trait-inference system (DESIGN.md S6 bare-minimum-cost: no generalized multi-trait bound inference is built for a population of one real specimen), this is a narrowly-scoped THIRD trigger, structurally parallel to the two above: v1_set_union_ord_bound_param_names is the Node-free pure decision core (given a fn's generic_param_names and the element-type names found at every set_union call site anywhere in its body, already extracted by the Node-consuming walker at the call site in 05_emit_rust.dag, answers which of those element names ARE the fn's own bare generic params and therefore need Ord). Scope is exactly and only the set_union builtin's known Rust lowering (rc_set_union) -- a second builtin gaining an analogous non-Clone bound is a declared next-rung trigger for a fourth sibling trigger, not silent widening of this one.".to_string() + }; + } + CACHED.with(|c: &String| c.clone()) +} + +pub fn v1_set_union_ord_bound_param_names( + generic_param_names: Rc>, + set_union_call_elem_type_names: Rc>, +) -> Rc> { + set_union_call_elem_type_names.clone().iter().cloned().fold( + Rc::new(vec![]), + |acc: Rc>, elem_name: String| { + if ({ + let mut __found = false; + for g in generic_param_names.clone().iter().cloned() { + if (g.clone() == elem_name.clone()) { + __found = true; + break; + } + } + __found + } && !{ + let mut __found = false; + for a in acc.clone().iter().cloned() { + if (a.clone() == elem_name.clone()) { + __found = true; + break; + } + } + __found + }) { + v1_rt::concat(acc.clone(), Rc::new(vec![elem_name.clone()])) + } else { + acc.clone() + } + }, + ) +} + pub fn v1_call_forwarding_compound_type_arg_note() -> String { thread_local! { static CACHED: String = { diff --git a/src/v1/stage0/src/v1_compiler_trait_derive_emit.rs b/src/v1/stage0/src/v1_compiler_trait_derive_emit.rs index 253f5972a1b..ba4d77da32a 100644 --- a/src/v1/stage0/src/v1_compiler_trait_derive_emit.rs +++ b/src/v1/stage0/src/v1_compiler_trait_derive_emit.rs @@ -2734,6 +2734,76 @@ pub fn v1_emit_type_params_with_clone_bounds( } } +pub fn v1_emit_type_params_with_clone_and_ord_bounds_note() -> String { + thread_local! { + static CACHED: String = { + "gunbc dashboard node://adhoc-a9f61ade-340. New sibling of v1_emit_type_params_with_clone_bounds above rather than an edit to it (DESIGN.md S3: that function has two call sites -- the item/struct-derive header and emit_fn_def -- and only the fn-generic call site in emit_fn_def needs an Ord arm; widening the shared renderer would also touch the struct-header call site, which is out of scope for the fn-signature trigger this renders for, see v1_set_union_ord_bound_note in src/v1/trait_bound_witness.dag). Renders identically to v1_emit_type_params_with_clone_bounds when ord_param_names is empty (': Clone' or bare), so the fn-generic call site in emit_fn_def can switch to this renderer unconditionally without changing output for the 25-site Clone-only population; it adds ': Ord' and ': Ord + Clone' arms for the params set_union's rc_set_union lowering requires Ord of.".to_string() + }; + } + CACHED.with(|c: &String| c.clone()) +} + +pub fn v1_emit_type_params_with_clone_and_ord_bounds( + params: Rc>>, + clone_param_names: Rc>, + ord_param_names: Rc>, + source_indices: Rc>>, +) -> String { + if ((params.clone().len() as i64) == 0) { + "".to_string() + } else { + { + let names = Rc::new({ + let mut __result = Vec::new(); + for p in params.clone().iter().cloned() { + __result.push({ + let pname = generic_param_name_at(p.clone(), source_indices.clone()); + let pascal = to_pascal(pname.clone()); + let needs_clone = { + let mut __found = false; + for c in clone_param_names.clone().iter().cloned() { + if (c.clone() == pname.clone()) { + __found = true; + break; + } + } + __found + }; + let needs_ord = { + let mut __found = false; + for c in ord_param_names.clone().iter().cloned() { + if (c.clone() == pname.clone()) { + __found = true; + break; + } + } + __found + }; + if (needs_ord.clone() && needs_clone.clone()) { + v1_rt::concat(pascal.clone(), ": Ord + Clone".to_string()) + } else { + if needs_ord.clone() { + v1_rt::concat(pascal.clone(), ": Ord".to_string()) + } else { + if needs_clone.clone() { + v1_rt::concat(pascal.clone(), ": Clone".to_string()) + } else { + pascal.clone() + } + } + } + }); + } + __result + }); + v1_rt::concat( + v1_rt::concat("<".to_string(), names.clone().join(&", ".to_string())), + ">".to_string(), + ) + } + } +} + pub fn v1_emit_struct_from_capability_table( module_path: String, name: String, diff --git a/src/v1/trait_bound_witness.dag b/src/v1/trait_bound_witness.dag index 4863e377706..118af0875e8 100644 --- a/src/v1/trait_bound_witness.dag +++ b/src/v1/trait_bound_witness.dag @@ -33,6 +33,22 @@ fn v1_union_clone_param_names(base: List, extra: List) -> List filter(x => !(base |> any(y => y == x)))) } +data v1_set_union_ord_bound_note: String = "gunbc dashboard node://adhoc-a9f61ade-340 (E0277 root A residual: fn-generic bound inference is Clone-only). std.authorization_profile's audience_subset

/audience_join

(dag/std/authorization_profile.dag) call the set_union builtin on Set

values (EnumeratedAudience { members: Set

}); set_union lowers to v1_rt::rc_set_union::(src/v1/runtime_rust.dag), so the emitted signature needs P: Ord, not merely P: Clone. This is a DIFFERENT site from docs/probes/e0277_root_partition_2026-08-21.md's Root-A Ord-5 population, which is a struct-DERIVE bound on EnumeratedAudience's own members: Set

field (AudienceSet's #[derive(..)] header) -- that population is out of scope here and stays blocked on v2.std.compilers.target_model.target_derive_supplemental_generic_bound_contract per that document's own dissolution clause. This row is the fn-SIGNATURE half: the whole v1 supplemental-bound apparatus (v1_generic_params_needing_clone_bound, v1_clone_bounded_type_params, this module's own two Clone triggers above) is Clone-only by construction -- there is no arm anywhere in trait_derive_emit.dag or trait_bound_witness.dag that can emit a bound other than Clone on a function's own generic parameter list (confirmed by grep: no ': Ord' render exists at any fn-generic call site). Rather than widen the Clone apparatus to a general trait-inference system (DESIGN.md S6 bare-minimum-cost: no generalized multi-trait bound inference is built for a population of one real specimen), this is a narrowly-scoped THIRD trigger, structurally parallel to the two above: v1_set_union_ord_bound_param_names is the Node-free pure decision core (given a fn's generic_param_names and the element-type names found at every set_union call site anywhere in its body, already extracted by the Node-consuming walker at the call site in 05_emit_rust.dag, answers which of those element names ARE the fn's own bare generic params and therefore need Ord). Scope is exactly and only the set_union builtin's known Rust lowering (rc_set_union) -- a second builtin gaining an analogous non-Clone bound is a declared next-rung trigger for a fourth sibling trigger, not silent widening of this one." + +fn v1_set_union_ord_bound_param_names( + generic_param_names: List, + set_union_call_elem_type_names: List +) -> List { + set_union_call_elem_type_names + |> fold(init: [], f: (acc, elem_name) => + if (generic_param_names |> any(g => g == elem_name)) && !(acc |> any(a => a == elem_name)) { + concat(acc, [elem_name]) + } else { + acc + } + ) +} + data v1_call_forwarding_compound_type_arg_note: String = "Widening of v1_call_forwarding_clone_bound_wrapper_param_names (gunbc dashboard node://adhoc-574e999b-39c, Row 1b), traced against the census's own site list (docs/probes/materialization_carriers_rebaseline_2026-08-19.md S3: v2_std_staging.rs:16,31,44 -- v2.std.staging's cached_stage/then_outcome) rather than assumed in scope. cached_stage's body is fn(x) { resolve_probe(probe: lookup(x), stage: stage, x: x) } -- a body shape ALREADY covered by BoundedToDirectSingleCallLambdaBody (a lambda whose body is one top-level call), so this is not a body-shape gap. The gap is in the bare-name comparison above: resolve_probe's own `probe: CacheProbe` parameter needs Clone on B (resolve_probe is itself in callee_clone_param_names via the census's bind_outcome/resolve_probe E0277 rows), but callee_param_type_name for that param resolves to the bare outer name \"CacheProbe\", which is never itself one of resolve_probe's own generic_param_names (\"A\"/\"B\") -- so the original bare-string check can never fire when the callee's requirement sits on a TYPE ARGUMENT of a compound parameter type rather than being the parameter's whole type. The call argument lookup(x) has the same shape one level up: its resolved type is the compound CacheProbe where B is cached_stage's OWN generic (the wrapper's, not the callee's) -- so the fix pairs the callee parameter's type-argument names against the call argument's resolved type-argument names POSITIONALLY, deliberately narrowed to arity exactly 1 on both sides (checked, not assumed: no zip/fold2/map2 primitive exists anywhere in this corpus -- see v1_call_forwarding_clone_bound_note above -- so an N-ary positional pairing would need one written from scratch, and the only real specimens driving this row are CacheProbe, itself arity 1; DESIGN.md S6 forbids building for a hypothetical arity-2+ case no specimen needs). A specimen with two or more compound type arguments is a declared next-rung trigger for this exact reason, not a silent gap: it falls through to no match (empty list) rather than mis-pairing arguments by position under an unverified arity, which would be worse than the boundary." fn v1_call_forwarding_clone_bound_wrapper_param_names( diff --git a/src/v1/trait_derive_emit.dag b/src/v1/trait_derive_emit.dag index 14390f44e7e..51562502b6d 100644 --- a/src/v1/trait_derive_emit.dag +++ b/src/v1/trait_derive_emit.dag @@ -1633,6 +1633,35 @@ fn v1_emit_type_params_with_clone_bounds( } } +data v1_emit_type_params_with_clone_and_ord_bounds_note: String = "gunbc dashboard node://adhoc-a9f61ade-340. New sibling of v1_emit_type_params_with_clone_bounds above rather than an edit to it (DESIGN.md S3: that function has two call sites -- the item/struct-derive header and emit_fn_def -- and only the fn-generic call site in emit_fn_def needs an Ord arm; widening the shared renderer would also touch the struct-header call site, which is out of scope for the fn-signature trigger this renders for, see v1_set_union_ord_bound_note in src/v1/trait_bound_witness.dag). Renders identically to v1_emit_type_params_with_clone_bounds when ord_param_names is empty (': Clone' or bare), so the fn-generic call site in emit_fn_def can switch to this renderer unconditionally without changing output for the 25-site Clone-only population; it adds ': Ord' and ': Ord + Clone' arms for the params set_union's rc_set_union lowering requires Ord of." + +fn v1_emit_type_params_with_clone_and_ord_bounds( + params: List, + clone_param_names: List, + ord_param_names: List, + source_indices: Map +) -> String { + if params |> count == 0 { "" } + else { + let names = params |> map(p => + let pname = generic_param_name_at(n: p, source_indices: source_indices) + let pascal = to_pascal(name: pname) + let needs_clone = clone_param_names |> any(c => c == pname) + let needs_ord = ord_param_names |> any(c => c == pname) + if needs_ord && needs_clone { + concat(pascal, ": Ord + Clone") + } else if needs_ord { + concat(pascal, ": Ord") + } else if needs_clone { + concat(pascal, ": Clone") + } else { + pascal + } + ) + concat("<", names |> join(separator: ", "), ">") + } +} + fn v1_emit_struct_from_capability_table( module_path: String, name: String,