From b723cf240c2e66f4e08f0aecf615fbd485fd027b Mon Sep 17 00:00:00 2001 From: gunbc-ci-auto-heal Date: Sun, 23 Aug 2026 15:00:25 +0000 Subject: [PATCH 1/6] Order underscore-idiom named calls by declared parameters --- ...e_named_call_order_treatment_2026-08-23.md | 47 +++++++++++++++++++ src/v1/05_emit.dag | 24 +++++----- src/v1/compiler_tests_rust.dag | 15 +++++- 3 files changed, 74 insertions(+), 12 deletions(-) create mode 100644 docs/probes/underscore_named_call_order_treatment_2026-08-23.md diff --git a/docs/probes/underscore_named_call_order_treatment_2026-08-23.md b/docs/probes/underscore_named_call_order_treatment_2026-08-23.md new file mode 100644 index 00000000000..bf3c72c8e6f --- /dev/null +++ b/docs/probes/underscore_named_call_order_treatment_2026-08-23.md @@ -0,0 +1,47 @@ +# Underscore-idiom named-call order — preregistered treatment + +This is one bounded producer decision from the retained `03_ingest` emission board at +`907f19c2cc7`. It is not a partition of that board. + +## Current population + +The retained log has seven `error[E0308]: arguments to this function are incorrect` blocks in +the emitted `v2.compiler.eval` module whose callees are `eval_value_node`, +`eval_transform_node`, `eval_branch_node`, `eval_loop_node`, `eval_bind_node`, and +`eval_match_node` (two calls reach `eval_loop_node`). Each declaration uses at least one +underscore-prefixed unused parameter and each authored call supplies its caller-visible label. + +The other three diagnostics with the same rustc message are controls, not members: two are +positional calls inside fold lambdas, and one supplies `NormalizedTree` values to a function that +requires `Node`. This treatment predicts no change to those three. + +## Producer and intended fact + +`v1.compiler.emit` `order_typed_call_args` translates source named arguments into target +positional arguments. It previously compared a caller label to the exact declaration spelling. +The accepted label relation already has one authority: `v1.compiler.infer` +`call_arg_label_matches_param`, which defines declaration `_args` as caller label `args` (and also +serves inference's call-shape wall). Exact spelling therefore made emission answer the same fact +differently from inference. + +The intended fact is unique: an accepted named call is ordered by declaration parameters using +`call_arg_label_matches_param`. A bare anonymous `_` has no unique caller-visible identity and is +not reordered by name. + +## Discriminating treatment and preregistered receipt + +The witness compiles two otherwise-identical declarations and calls. The control declares +`direct_order(a, b)`; the treatment declares `underscore_order(_a, b)`. Both calls author `b` +before `a`. Their emitted Rust must contain, respectively, `direct_order(11, 23)` and +`underscore_order(11, 23)`. + +Before observing a candidate board, the registered result is: + +- all seven in-scope `v2.compiler.eval` E0308 blocks disappear rather than convert: their arity + and types already agree with the declarations, and only their positional order differs, so no + E0061 or type-mismatch successor is predicted; +- the three out-of-scope same-message blocks remain byte-identifiable by callee and enclosing + declaration; +- the control and treatment emit the same positional order; +- any successor diagnostic refutes that prediction and is reported as a conversion, not silently + counted as removal. diff --git a/src/v1/05_emit.dag b/src/v1/05_emit.dag index adbbca8408c..b1aec2163cd 100644 --- a/src/v1/05_emit.dag +++ b/src/v1/05_emit.dag @@ -49,7 +49,7 @@ import v1.compiler.infer_lookup { lookup_func_sig, func_sig_if_resolved } import v1.compiler.infer { InferScope, - build_params_scope, extend_scope + build_params_scope, extend_scope, call_arg_label_matches_param } import v1.compiler.infer_emit_info { TypeSummary, EmitGraphInfo } @@ -303,12 +303,14 @@ fn lookup_func_sig_in_scope(scope: InferScope, name: String) -> ResolvedFuncSig? func_sig_if_resolved(lookup: lookup_func_sig(func_env: scope.func_env, type_env: scope.type_env, name: name)) } +data typed_named_arg_matches_note: String = "Emission translates source named arguments into target positional arguments. The accepted caller-label relation is v1.compiler.infer call_arg_label_matches_param, so this projection consumes that authority rather than comparing exact declaration spelling independently: a declaration-side unused parameter `_args` carries caller label `args`. A bare anonymous `_` stays positional because it has no unique caller-visible identity with which to reorder an argument. This closes the seven retained v2.compiler.eval E0308 call-order blocks on board 907f19c2cc7; the preregistered treatment and controls are docs/probes/underscore_named_call_order_treatment_2026-08-23.md." + fn typed_named_arg_matches(arg: Node, name: String, source_indices: Map) -> Bool { let n = arg_name_at(n: arg, source_indices: source_indices) if n == none { false } else { - n.value == name + name != "_" && call_arg_label_matches_param(param_name: name, arg_label: n.value) } } @@ -320,15 +322,9 @@ fn order_typed_call_args(args: List, func: String, scope: InferScope) -> L match lookup_func_sig_in_scope(scope: scope, name: func) { Absent => args Present { value: sig } => { - let arg_map = args |> fold(init: empty_map(), f: (acc, arg) => - let n = arg_name_at(n: arg, source_indices: scope.type_env.source_indices) - if n != none { map_insert(acc, n.value, arg) } else { acc } - ) - let param_name_set = sig.params |> fold(init: empty_map(), f: (acc, param) => - map_insert(acc, param_node_name_at(n: param, source_indices: scope.type_env.source_indices), true) - ) let ordered = sig.params |> flat_map(param => - match map_get(arg_map, param_node_name_at(n: param, source_indices: scope.type_env.source_indices)) { + let param_name = param_node_name_at(n: param, source_indices: scope.type_env.source_indices) + match args |> filter(arg => typed_named_arg_matches(arg: arg, name: param_name, source_indices: scope.type_env.source_indices)) |> first { Present { value: arg } => [arg] Absent => [] } @@ -338,7 +334,13 @@ fn order_typed_call_args(args: List, func: String, scope: InferScope) -> L if n == none { true } else { - emit_map_has(m: param_name_set, key: n.value) == false + sig.params |> any(param => + typed_named_arg_matches( + arg: arg, + name: param_node_name_at(n: param, source_indices: scope.type_env.source_indices), + source_indices: scope.type_env.source_indices + ) + ) == false } ) concat(ordered, leftovers) diff --git a/src/v1/compiler_tests_rust.dag b/src/v1/compiler_tests_rust.dag index 46d22c2196f..ddad6c4604f 100644 --- a/src/v1/compiler_tests_rust.dag +++ b/src/v1/compiler_tests_rust.dag @@ -2080,13 +2080,26 @@ fn ct_call_shape_wall_witness_test() -> String { " // it, so the compile seam must too) all stay silent.\n", " let green = compile_one(\n", " \"green.dag\",\n", - " \"module green\\nfn sub(a: Int, b: Int) -> Int { a - b }\\nfn ignore_ctx(_ctx: Int, b: Int) -> Int { b }\\nfn named() -> Int { sub(a: 10, b: 3) }\\nfn positional() -> Int { sub(10, 3) }\\nfn underscore_idiom() -> Int { ignore_ctx(ctx: 1, b: 2) }\\n\",\n", + " \"module green\\nfn sub(a: Int, b: Int) -> Int { a - b }\\nfn ignore_ctx(_ctx: Int, b: Int) -> Int { b }\\nfn direct_order(a: Int, b: Int) -> Int { b }\\nfn underscore_order(_a: Int, b: Int) -> Int { b }\\nfn named() -> Int { sub(a: 10, b: 3) }\\nfn positional() -> Int { sub(10, 3) }\\nfn underscore_idiom() -> Int { ignore_ctx(ctx: 1, b: 2) }\\nfn direct_order_control() -> Int { direct_order(b: 23, a: 11) }\\nfn underscore_order_witness() -> Int { underscore_order(b: 23, a: 11) }\\n\",\n", " );\n", " assert!(\n", " green.diagnostics.is_empty(),\n", " \"correct call shapes must compile with NO diagnostic of any severity, got: {:?}\",\n", " green.diagnostics\n", " );\n", + " let emitted = green.files.iter()\n", + " .find(|f| f.path.ends_with(\"green.rs\"))\n", + " .expect(\"green fixture must emit its Rust module\");\n", + " assert!(\n", + " emitted.content.contains(\"direct_order(11, 23)\"),\n", + " \"control: ordinary named arguments must follow declaration order, got: {}\",\n", + " emitted.content\n", + " );\n", + " assert!(\n", + " emitted.content.contains(\"underscore_order(11, 23)\"),\n", + " \"named arguments accepted through the declaration-side underscore idiom must follow that same declaration order, got: {}\",\n", + " emitted.content\n", + " );\n", " })\n", " .expect(\"failed to spawn thread\")\n", " .join();\n", From e2317ed083851933f68bc16f778a6d359a0ca50d Mon Sep 17 00:00:00 2001 From: gunbc-ci-auto-heal Date: Sun, 23 Aug 2026 15:33:47 +0000 Subject: [PATCH 2/6] Regenerate stage0 named-call ordering mirrors --- .../src/v1_compiler_compiler_tests_rust.rs | 2 +- src/v1/stage0/src/v1_compiler_emit.rs | 91 +++++++++++-------- 2 files changed, 54 insertions(+), 39 deletions(-) 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 28a4861a7a0..151d06d25e2 100644 --- a/src/v1/stage0/src/v1_compiler_compiler_tests_rust.rs +++ b/src/v1/stage0/src/v1_compiler_compiler_tests_rust.rs @@ -337,7 +337,7 @@ pub fn ct_contracts_sidecar_witness_test() -> String { } pub fn ct_call_shape_wall_witness_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(" #[test]\n".to_string(), " fn call_shape_wall_witness() {\n".to_string()), " // DISCRIMINATING RED for direct_call_shape_wall_note (04_infer). Before the\n".to_string()), " // wall, `sub(a: 10, bb: 3)` against `fn sub(a: Int, b: Int)` compiled with\n".to_string()), " // ZERO diagnostics: the per-param type walk absorbed the mislabeled arg into\n".to_string()), " // its position, the interpreter refused the same call at runtime\n".to_string()), " // (CallContractMismatch in call_function_inner), and the Rust emitter\n".to_string()), " // silently REORDERED it positionally — two realizations of one program\n".to_string()), " // disagreeing silently. The wall makes the compile seam agree with the\n".to_string()), " // runtime authority, on both of the classes the runtime refuses.\n".to_string()), " let result = std::thread::Builder::new()\n".to_string()), " .stack_size(8 * 1024 * 1024)\n".to_string()), " .spawn(|| {\n".to_string()), " let compile_one = |path: &str, content: &str| {\n".to_string()), " crate::v1_compiler_compile::compile_sources(\n".to_string()), " std::rc::Rc::new(im::vector![std::rc::Rc::new(\n".to_string()), " crate::v1_compiler_compile::SourceFile {\n".to_string()), " path: path.to_string(),\n".to_string()), " content: content.to_string(),\n".to_string()), " }\n".to_string()), " )]),\n".to_string()), " crate::v1_compiler_artifact::RenderTarget::Rust,\n".to_string()), " )\n".to_string()), " };\n".to_string()), " let mislabeled = compile_one(\n".to_string()), " \"mislabel.dag\",\n".to_string()), " \"module mislabel\\nfn sub(a: Int, b: Int) -> Int { a - b }\\nfn f() -> Int { sub(a: 10, bb: 3) }\\n\",\n".to_string()), " );\n".to_string()), " let unknown: Vec<_> = mislabeled.diagnostics.iter()\n".to_string()), " .filter(|d| matches!(*d.diagnostic, crate::v1_std_core::CompilerDiagnostic::CallArgumentNameUnknown { .. }))\n".to_string()), " .collect();\n".to_string()), " assert!(\n".to_string()), " !unknown.is_empty(),\n".to_string()), " \"an argument labeling a parameter that does not exist must refuse at the compile seam — the interpreter already refuses this call at runtime, and the emitter silently reorders it, got: {:?}\",\n".to_string()), " mislabeled.diagnostics\n".to_string()), " );\n".to_string()), " assert!(\n".to_string()), " unknown.iter().all(|d| crate::v1_std_core::is_error_diagnostic(d.diagnostic.clone())\n".to_string()), " && crate::v1_std_core::is_interpreter_blocking_diagnostic(d.diagnostic.clone())),\n".to_string()), " \"CallArgumentNameUnknown must BLOCK — a counted advisory would still emit the silently-reordered realization\"\n".to_string()), " );\n".to_string()), " let surplus = compile_one(\n".to_string()), " \"surplus.dag\",\n".to_string()), " \"module surplus\\nfn two(a: Int, b: Int) -> Int { a + b }\\nfn f() -> Int { two(1, 2, 3) }\\n\",\n".to_string()), " );\n".to_string()), " assert!(\n".to_string()), " surplus.diagnostics.iter().any(|d| matches!(*d.diagnostic, crate::v1_std_core::CompilerDiagnostic::CallPositionalSurplus { .. })),\n".to_string()), " \"a positional argument beyond the declared positional parameters must refuse — the interpreter refuses the same call (too many positional arguments), got: {:?}\",\n".to_string()), " surplus.diagnostics\n".to_string()), " );\n".to_string()), " // POSITIVE CONTROLS at ZERO diagnostics of any severity (the filtering\n".to_string()), " // lesson of codex review 45357: asserting only the absence of the\n".to_string()), " // blocking variant lets an advisory pass unnoticed). Correct labels,\n".to_string()), " // correct positional binding, and the deliberately-unused-parameter\n".to_string()), " // idiom (label `ctx` against declared `_ctx` — the interpreter accepts\n".to_string()), " // it, so the compile seam must too) all stay silent.\n".to_string()), " let green = compile_one(\n".to_string()), " \"green.dag\",\n".to_string()), " \"module green\\nfn sub(a: Int, b: Int) -> Int { a - b }\\nfn ignore_ctx(_ctx: Int, b: Int) -> Int { b }\\nfn named() -> Int { sub(a: 10, b: 3) }\\nfn positional() -> Int { sub(10, 3) }\\nfn underscore_idiom() -> Int { ignore_ctx(ctx: 1, b: 2) }\\n\",\n".to_string()), " );\n".to_string()), " assert!(\n".to_string()), " green.diagnostics.is_empty(),\n".to_string()), " \"correct call shapes must compile with NO diagnostic of any severity, got: {:?}\",\n".to_string()), " green.diagnostics\n".to_string()), " );\n".to_string()), " })\n".to_string()), " .expect(\"failed to spawn thread\")\n".to_string()), " .join();\n".to_string()), " result.expect(\"call_shape_wall_witness 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(" #[test]\n".to_string(), " fn call_shape_wall_witness() {\n".to_string()), " // DISCRIMINATING RED for direct_call_shape_wall_note (04_infer). Before the\n".to_string()), " // wall, `sub(a: 10, bb: 3)` against `fn sub(a: Int, b: Int)` compiled with\n".to_string()), " // ZERO diagnostics: the per-param type walk absorbed the mislabeled arg into\n".to_string()), " // its position, the interpreter refused the same call at runtime\n".to_string()), " // (CallContractMismatch in call_function_inner), and the Rust emitter\n".to_string()), " // silently REORDERED it positionally — two realizations of one program\n".to_string()), " // disagreeing silently. The wall makes the compile seam agree with the\n".to_string()), " // runtime authority, on both of the classes the runtime refuses.\n".to_string()), " let result = std::thread::Builder::new()\n".to_string()), " .stack_size(8 * 1024 * 1024)\n".to_string()), " .spawn(|| {\n".to_string()), " let compile_one = |path: &str, content: &str| {\n".to_string()), " crate::v1_compiler_compile::compile_sources(\n".to_string()), " std::rc::Rc::new(im::vector![std::rc::Rc::new(\n".to_string()), " crate::v1_compiler_compile::SourceFile {\n".to_string()), " path: path.to_string(),\n".to_string()), " content: content.to_string(),\n".to_string()), " }\n".to_string()), " )]),\n".to_string()), " crate::v1_compiler_artifact::RenderTarget::Rust,\n".to_string()), " )\n".to_string()), " };\n".to_string()), " let mislabeled = compile_one(\n".to_string()), " \"mislabel.dag\",\n".to_string()), " \"module mislabel\\nfn sub(a: Int, b: Int) -> Int { a - b }\\nfn f() -> Int { sub(a: 10, bb: 3) }\\n\",\n".to_string()), " );\n".to_string()), " let unknown: Vec<_> = mislabeled.diagnostics.iter()\n".to_string()), " .filter(|d| matches!(*d.diagnostic, crate::v1_std_core::CompilerDiagnostic::CallArgumentNameUnknown { .. }))\n".to_string()), " .collect();\n".to_string()), " assert!(\n".to_string()), " !unknown.is_empty(),\n".to_string()), " \"an argument labeling a parameter that does not exist must refuse at the compile seam — the interpreter already refuses this call at runtime, and the emitter silently reorders it, got: {:?}\",\n".to_string()), " mislabeled.diagnostics\n".to_string()), " );\n".to_string()), " assert!(\n".to_string()), " unknown.iter().all(|d| crate::v1_std_core::is_error_diagnostic(d.diagnostic.clone())\n".to_string()), " && crate::v1_std_core::is_interpreter_blocking_diagnostic(d.diagnostic.clone())),\n".to_string()), " \"CallArgumentNameUnknown must BLOCK — a counted advisory would still emit the silently-reordered realization\"\n".to_string()), " );\n".to_string()), " let surplus = compile_one(\n".to_string()), " \"surplus.dag\",\n".to_string()), " \"module surplus\\nfn two(a: Int, b: Int) -> Int { a + b }\\nfn f() -> Int { two(1, 2, 3) }\\n\",\n".to_string()), " );\n".to_string()), " assert!(\n".to_string()), " surplus.diagnostics.iter().any(|d| matches!(*d.diagnostic, crate::v1_std_core::CompilerDiagnostic::CallPositionalSurplus { .. })),\n".to_string()), " \"a positional argument beyond the declared positional parameters must refuse — the interpreter refuses the same call (too many positional arguments), got: {:?}\",\n".to_string()), " surplus.diagnostics\n".to_string()), " );\n".to_string()), " // POSITIVE CONTROLS at ZERO diagnostics of any severity (the filtering\n".to_string()), " // lesson of codex review 45357: asserting only the absence of the\n".to_string()), " // blocking variant lets an advisory pass unnoticed). Correct labels,\n".to_string()), " // correct positional binding, and the deliberately-unused-parameter\n".to_string()), " // idiom (label `ctx` against declared `_ctx` — the interpreter accepts\n".to_string()), " // it, so the compile seam must too) all stay silent.\n".to_string()), " let green = compile_one(\n".to_string()), " \"green.dag\",\n".to_string()), " \"module green\\nfn sub(a: Int, b: Int) -> Int { a - b }\\nfn ignore_ctx(_ctx: Int, b: Int) -> Int { b }\\nfn direct_order(a: Int, b: Int) -> Int { b }\\nfn underscore_order(_a: Int, b: Int) -> Int { b }\\nfn named() -> Int { sub(a: 10, b: 3) }\\nfn positional() -> Int { sub(10, 3) }\\nfn underscore_idiom() -> Int { ignore_ctx(ctx: 1, b: 2) }\\nfn direct_order_control() -> Int { direct_order(b: 23, a: 11) }\\nfn underscore_order_witness() -> Int { underscore_order(b: 23, a: 11) }\\n\",\n".to_string()), " );\n".to_string()), " assert!(\n".to_string()), " green.diagnostics.is_empty(),\n".to_string()), " \"correct call shapes must compile with NO diagnostic of any severity, got: {:?}\",\n".to_string()), " green.diagnostics\n".to_string()), " );\n".to_string()), " let emitted = green.files.iter()\n".to_string()), " .find(|f| f.path.ends_with(\"green.rs\"))\n".to_string()), " .expect(\"green fixture must emit its Rust module\");\n".to_string()), " assert!(\n".to_string()), " emitted.content.contains(\"direct_order(11, 23)\"),\n".to_string()), " \"control: ordinary named arguments must follow declaration order, got: {}\",\n".to_string()), " emitted.content\n".to_string()), " );\n".to_string()), " assert!(\n".to_string()), " emitted.content.contains(\"underscore_order(11, 23)\"),\n".to_string()), " \"named arguments accepted through the declaration-side underscore idiom must follow that same declaration order, got: {}\",\n".to_string()), " emitted.content\n".to_string()), " );\n".to_string()), " })\n".to_string()), " .expect(\"failed to spawn thread\")\n".to_string()), " .join();\n".to_string()), " result.expect(\"call_shape_wall_witness panicked\");\n".to_string()), " }\n\n".to_string()) } pub fn ct_call_shape_duplicate_wall_witness_test() -> String { diff --git a/src/v1/stage0/src/v1_compiler_emit.rs b/src/v1/stage0/src/v1_compiler_emit.rs index 1d06c415c6f..745c6562789 100644 --- a/src/v1/stage0/src/v1_compiler_emit.rs +++ b/src/v1/stage0/src/v1_compiler_emit.rs @@ -41,7 +41,9 @@ pub use crate::v1_compiler_emit_core_support::{ }; pub use crate::v1_compiler_emit_core_support::{EmitResult, TestProjection}; pub use crate::v1_compiler_infer::InferScope; -pub use crate::v1_compiler_infer::{build_params_scope, extend_scope}; +pub use crate::v1_compiler_infer::{ + build_params_scope, call_arg_label_matches_param, extend_scope, +}; pub use crate::v1_compiler_infer_emit_info::{EmitGraphInfo, TypeSummary}; use crate::v1_compiler_infer_env::GlobalBareLookupState::*; pub use crate::v1_compiler_infer_env::{authored_name, empty_symbol_index}; @@ -658,6 +660,15 @@ pub fn lookup_func_sig_in_scope( )) } +pub fn typed_named_arg_matches_note() -> String { + thread_local! { + static CACHED: String = { + "Emission translates source named arguments into target positional arguments. The accepted caller-label relation is v1.compiler.infer call_arg_label_matches_param, so this projection consumes that authority rather than comparing exact declaration spelling independently: a declaration-side unused parameter `_args` carries caller label `args`. A bare anonymous `_` stays positional because it has no unique caller-visible identity with which to reorder an argument. This closes the seven retained v2.compiler.eval E0308 call-order blocks on board 907f19c2cc7; the preregistered treatment and controls are docs/probes/underscore_named_call_order_treatment_2026-08-23.md.".to_string() + }; + } + CACHED.with(|c: &String| c.clone()) +} + pub fn typed_named_arg_matches( arg: Rc, name: String, @@ -668,7 +679,8 @@ pub fn typed_named_arg_matches( if (n.clone() == None) { false } else { - (n.clone().unwrap() == name.clone()) + ((name.clone() != "_".to_string()) + && call_arg_label_matches_param(name.clone(), n.clone().unwrap())) } } } @@ -696,46 +708,34 @@ pub fn order_typed_call_args( match lookup_func_sig_in_scope(scope.clone(), func.clone()) { None => args.clone(), Some(sig) => { - let arg_map = args.iter().cloned().fold( - v1_rt::rc_empty_map::>(), - |acc: Rc>>, arg: Rc| { - let n = arg_name_at( - arg.clone(), - scope.type_env.clone().source_indices.clone(), - ); - if (n.clone() != None) { - v1_rt::rc_map_insert(acc.clone(), n.clone().unwrap(), arg.clone()) - } else { - acc.clone() - } - }, - ); - let param_name_set = sig.params.clone().iter().cloned().fold( - v1_rt::rc_empty_map::(), - |acc: Rc>, param: Rc| { - v1_rt::rc_map_insert( - acc, - param_node_name_at( - param.clone(), - scope.type_env.clone().source_indices.clone(), - ), - true, - ) - }, - ); let ordered = Rc::new({ let mut __result = Vec::new(); for param in sig.params.clone().iter().cloned() { __result.extend( - (*match v1_rt::map_get( - &arg_map, - param_node_name_at( + (*{ + let param_name = param_node_name_at( param.clone(), scope.type_env.clone().source_indices.clone(), - ), - ) { - Some(arg) => Rc::new(vec![arg.clone()]), - None => Rc::new(vec![]), + ); + match Rc::new({ + let mut __result = Vec::new(); + for arg in args.iter().cloned() { + if typed_named_arg_matches( + arg.clone(), + param_name.clone(), + scope.type_env.clone().source_indices.clone(), + ) { + __result.push(arg); + } + } + __result + }) + .first() + .cloned() + { + Some(arg) => Rc::new(vec![arg.clone()]), + None => Rc::new(vec![]), + } }) .iter() .cloned(), @@ -754,8 +754,23 @@ pub fn order_typed_call_args( if (n.clone() == None) { true } else { - (emit_map_has(param_name_set.clone(), n.clone().unwrap()) - == false) + ({ + let mut __found = false; + for param in sig.params.clone().iter().cloned() { + if typed_named_arg_matches( + arg.clone(), + param_node_name_at( + param.clone(), + scope.type_env.clone().source_indices.clone(), + ), + scope.type_env.clone().source_indices.clone(), + ) { + __found = true; + break; + } + } + __found + } == false) } } { __result.push(arg); From d305527e1946a7638bd0c276540093bc086c0fc7 Mon Sep 17 00:00:00 2001 From: gunbc-ci-auto-heal Date: Sun, 23 Aug 2026 16:03:22 +0000 Subject: [PATCH 3/6] Record paired named-call board conversion --- ...e_named_call_order_treatment_2026-08-23.md | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/docs/probes/underscore_named_call_order_treatment_2026-08-23.md b/docs/probes/underscore_named_call_order_treatment_2026-08-23.md index bf3c72c8e6f..ebea024a5ab 100644 --- a/docs/probes/underscore_named_call_order_treatment_2026-08-23.md +++ b/docs/probes/underscore_named_call_order_treatment_2026-08-23.md @@ -45,3 +45,24 @@ Before observing a candidate board, the registered result is: - the control and treatment emit the same positional order; - any successor diagnostic refutes that prediction and is reported as a conversion, not silently counted as removal. + +## Observed paired receipt + +The prediction was tested in one remote dispatch with the annotation-only #9027 correction in +both arms. Because that correction conflicted with the older board tree, both arms took the same +resolved file from #9027; it is therefore common setup rather than a treatment difference. The +before arm was `183e5972999` plus that correction. The after arm was PR #9026 at `e2317ed0838` +(including its regenerated stage0 mirrors) plus the identical correction. + +| observation | before | after | +|---|---:|---:| +| coded rustc errors | 324 | 317 | +| E0308 | 128 | 121 | +| scoped `v2.compiler.eval` call-order identities | 7 | 0 | +| three preregistered control identities (location-reference count) | 5 | 5 | +| E0061 | 17 | 17 | + +The prediction held exactly: all seven scoped E0308 blocks vanished, none converted, and no +control moved. The paired board total is 324 rather than the authorized retained board's 316 +because the common #9027 file resolution changes that composite subject; no count is compared +across those subjects. Only the within-pair delta is claimed here. From fc6640a0509a366c3969658cbff111452d5e5802 Mon Sep 17 00:00:00 2001 From: gunbc-ci-auto-heal Date: Sun, 23 Aug 2026 16:38:46 +0000 Subject: [PATCH 4/6] Keep named call ordering linear --- src/v1/04_infer.dag | 24 +++-- src/v1/05_emit.dag | 40 ++++----- src/v1/05_emit_go.dag | 2 +- src/v1/05_emit_python.dag | 2 +- src/v1/05_emit_rust.dag | 2 +- src/v1/stage0/src/compiler_tests.rs | 15 +++- src/v1/stage0/src/v1_compiler_emit.rs | 92 +++++++++----------- src/v1/stage0/src/v1_compiler_emit_go.rs | 2 +- src/v1/stage0/src/v1_compiler_emit_python.rs | 1 - src/v1/stage0/src/v1_compiler_emit_rust.rs | 3 +- src/v1/stage0/src/v1_compiler_infer.rs | 31 +++++-- 11 files changed, 123 insertions(+), 91 deletions(-) diff --git a/src/v1/04_infer.dag b/src/v1/04_infer.dag index 94fb43ab685..87f0fc2e3d9 100644 --- a/src/v1/04_infer.dag +++ b/src/v1/04_infer.dag @@ -2476,12 +2476,26 @@ fn borrowed_callable_call_type(gnode: Node, typed_args: List, scope: Infer fn is_anonymous_bound_name(name: String) -> Bool { name == "_" } +fn call_param_caller_label(param_name: String) -> String? { + if param_name == "_" { + none + } else if string_length(s: param_name) >= 1 + && substring(s: param_name, start: 0, end: 1) == "_" { + Present { value: substring(s: param_name, start: 1, end: string_length(s: param_name)) } + } else { + Present { value: param_name } + } +} + fn call_arg_label_matches_param(param_name: String, arg_label: String) -> Bool { - param_name == arg_label - || param_name == "_" - || (string_length(s: param_name) >= 1 - && substring(s: param_name, start: 0, end: 1) == "_" - && substring(s: param_name, start: 1, end: string_length(s: param_name)) == arg_label) + if param_name == "_" { + true + } else { + match call_param_caller_label(param_name: param_name) { + Present { value: caller_label } => caller_label == arg_label + Absent => false + } + } } fn param_binds_positionally(p: Node, source_indices: Map) -> Bool { diff --git a/src/v1/05_emit.dag b/src/v1/05_emit.dag index b1aec2163cd..ddf60c45e8a 100644 --- a/src/v1/05_emit.dag +++ b/src/v1/05_emit.dag @@ -49,7 +49,7 @@ import v1.compiler.infer_lookup { lookup_func_sig, func_sig_if_resolved } import v1.compiler.infer { InferScope, - build_params_scope, extend_scope, call_arg_label_matches_param + build_params_scope, extend_scope, call_param_caller_label } import v1.compiler.infer_emit_info { TypeSummary, EmitGraphInfo } @@ -303,16 +303,7 @@ fn lookup_func_sig_in_scope(scope: InferScope, name: String) -> ResolvedFuncSig? func_sig_if_resolved(lookup: lookup_func_sig(func_env: scope.func_env, type_env: scope.type_env, name: name)) } -data typed_named_arg_matches_note: String = "Emission translates source named arguments into target positional arguments. The accepted caller-label relation is v1.compiler.infer call_arg_label_matches_param, so this projection consumes that authority rather than comparing exact declaration spelling independently: a declaration-side unused parameter `_args` carries caller label `args`. A bare anonymous `_` stays positional because it has no unique caller-visible identity with which to reorder an argument. This closes the seven retained v2.compiler.eval E0308 call-order blocks on board 907f19c2cc7; the preregistered treatment and controls are docs/probes/underscore_named_call_order_treatment_2026-08-23.md." - -fn typed_named_arg_matches(arg: Node, name: String, source_indices: Map) -> Bool { - let n = arg_name_at(n: arg, source_indices: source_indices) - if n == none { - false - } else { - name != "_" && call_arg_label_matches_param(param_name: name, arg_label: n.value) - } -} +data order_typed_call_args_label_authority_note: String = "Emission translates source named arguments into target positional arguments. The accepted caller-label relation is v1.compiler.infer call_param_caller_label, the canonical projection consumed by call_arg_label_matches_param, so emission does not compare exact declaration spelling independently: a declaration-side unused parameter `_args` carries caller label `args`. A bare anonymous `_` has no caller-visible identity and stays positional. Both argument and parameter indexes use that canonical label, preserving linear construction and lookup rather than scanning either population per member. This closes the seven retained v2.compiler.eval E0308 call-order blocks on board 907f19c2cc7; the preregistered treatment and controls are docs/probes/underscore_named_call_order_treatment_2026-08-23.md." fn order_typed_call_args(args: List, func: String, scope: InferScope) -> List { let has_unnamed = args |> any(arg => arg_name_at(n: arg, source_indices: scope.type_env.source_indices) == none) @@ -322,10 +313,25 @@ fn order_typed_call_args(args: List, func: String, scope: InferScope) -> L match lookup_func_sig_in_scope(scope: scope, name: func) { Absent => args Present { value: sig } => { + let arg_map = args |> fold(init: empty_map(), f: (acc, arg) => + let n = arg_name_at(n: arg, source_indices: scope.type_env.source_indices) + if n != none { map_insert(acc, n.value, arg) } else { acc } + ) + let param_label_set = sig.params |> fold(init: empty_map(), f: (acc, param) => + let caller_label = call_param_caller_label(param_name: param_node_name_at(n: param, source_indices: scope.type_env.source_indices)) + match caller_label { + Present { value: label } => map_insert(acc, label, true) + Absent => acc + } + ) let ordered = sig.params |> flat_map(param => let param_name = param_node_name_at(n: param, source_indices: scope.type_env.source_indices) - match args |> filter(arg => typed_named_arg_matches(arg: arg, name: param_name, source_indices: scope.type_env.source_indices)) |> first { - Present { value: arg } => [arg] + match call_param_caller_label(param_name: param_name) { + Present { value: label } => + match map_get(arg_map, label) { + Present { value: arg } => [arg] + Absent => [] + } Absent => [] } ) @@ -334,13 +340,7 @@ fn order_typed_call_args(args: List, func: String, scope: InferScope) -> L if n == none { true } else { - sig.params |> any(param => - typed_named_arg_matches( - arg: arg, - name: param_node_name_at(n: param, source_indices: scope.type_env.source_indices), - source_indices: scope.type_env.source_indices - ) - ) == false + emit_map_has(m: param_label_set, key: n.value) == false } ) concat(ordered, leftovers) diff --git a/src/v1/05_emit_go.dag b/src/v1/05_emit_go.dag index bb56993f3be..a6492f66237 100644 --- a/src/v1/05_emit_go.dag +++ b/src/v1/05_emit_go.dag @@ -68,7 +68,7 @@ import v1.compiler.emit { lookup_item, is_null_coalesce, emit_null_coalesce, has_nested_records_node, - typed_named_arg_matches, order_typed_call_args, + order_typed_call_args, is_tco_eligible, service_fallback_transport, effective_operation_transport, ServiceFieldSet, compute_service_fields, service_field_decls, diff --git a/src/v1/05_emit_python.dag b/src/v1/05_emit_python.dag index f5a6a8983f7..c67b2421e7a 100644 --- a/src/v1/05_emit_python.dag +++ b/src/v1/05_emit_python.dag @@ -63,7 +63,7 @@ import v1.compiler.emit { empty_emit_scope, module_emit_scope, scope_after_expr, lookup_item, - typed_named_arg_matches, order_typed_call_args, + order_typed_call_args, has_nested_records_node, is_null_coalesce, emit_null_coalesce, has_service_items, diff --git a/src/v1/05_emit_rust.dag b/src/v1/05_emit_rust.dag index 0e2397c2ab7..f30f7d6a332 100644 --- a/src/v1/05_emit_rust.dag +++ b/src/v1/05_emit_rust.dag @@ -138,7 +138,7 @@ import v1.compiler.emit { emit_lambda_params, emit_null_coalesce, emit_list_lit_expr, emit_shared_expr, emit_typed_cast_shared, emit_string_literal, emit_simple_expr, escape_rust_interp_text, module_emit_scope, scope_after_expr, lookup_item, has_nested_records_node, - emit_data_value_json, EmitterOutcome, Emitted, Refused, is_null_coalesce, has_service_items, typed_named_arg_matches, + emit_data_value_json, EmitterOutcome, Emitted, Refused, is_null_coalesce, has_service_items, order_typed_call_args, is_tco_eligible, is_self_recursive, emit_shared_tco_expr, tco_reassign_core, is_tco_identity_passthrough, service_fallback_transport, effective_operation_transport, ServiceFieldSet, compute_service_fields, service_field_decls, diff --git a/src/v1/stage0/src/compiler_tests.rs b/src/v1/stage0/src/compiler_tests.rs index a70bfdc921b..e1450876fc1 100644 --- a/src/v1/stage0/src/compiler_tests.rs +++ b/src/v1/stage0/src/compiler_tests.rs @@ -522,13 +522,26 @@ mod compiler_tests { // it, so the compile seam must too) all stay silent. let green = compile_one( "green.dag", - "module green\nfn sub(a: Int, b: Int) -> Int { a - b }\nfn ignore_ctx(_ctx: Int, b: Int) -> Int { b }\nfn named() -> Int { sub(a: 10, b: 3) }\nfn positional() -> Int { sub(10, 3) }\nfn underscore_idiom() -> Int { ignore_ctx(ctx: 1, b: 2) }\n", + "module green\nfn sub(a: Int, b: Int) -> Int { a - b }\nfn ignore_ctx(_ctx: Int, b: Int) -> Int { b }\nfn direct_order(a: Int, b: Int) -> Int { b }\nfn underscore_order(_a: Int, b: Int) -> Int { b }\nfn named() -> Int { sub(a: 10, b: 3) }\nfn positional() -> Int { sub(10, 3) }\nfn underscore_idiom() -> Int { ignore_ctx(ctx: 1, b: 2) }\nfn direct_order_control() -> Int { direct_order(b: 23, a: 11) }\nfn underscore_order_witness() -> Int { underscore_order(b: 23, a: 11) }\n", ); assert!( green.diagnostics.is_empty(), "correct call shapes must compile with NO diagnostic of any severity, got: {:?}", green.diagnostics ); + let emitted = green.files.iter() + .find(|f| f.path.ends_with("green.rs")) + .expect("green fixture must emit its Rust module"); + assert!( + emitted.content.contains("direct_order(11, 23)"), + "control: ordinary named arguments must follow declaration order, got: {}", + emitted.content + ); + assert!( + emitted.content.contains("underscore_order(11, 23)"), + "named arguments accepted through the declaration-side underscore idiom must follow that same declaration order, got: {}", + emitted.content + ); }) .expect("failed to spawn thread") .join(); diff --git a/src/v1/stage0/src/v1_compiler_emit.rs b/src/v1/stage0/src/v1_compiler_emit.rs index 745c6562789..d13efc5993f 100644 --- a/src/v1/stage0/src/v1_compiler_emit.rs +++ b/src/v1/stage0/src/v1_compiler_emit.rs @@ -41,9 +41,7 @@ pub use crate::v1_compiler_emit_core_support::{ }; pub use crate::v1_compiler_emit_core_support::{EmitResult, TestProjection}; pub use crate::v1_compiler_infer::InferScope; -pub use crate::v1_compiler_infer::{ - build_params_scope, call_arg_label_matches_param, extend_scope, -}; +pub use crate::v1_compiler_infer::{build_params_scope, call_param_caller_label, extend_scope}; pub use crate::v1_compiler_infer_emit_info::{EmitGraphInfo, TypeSummary}; use crate::v1_compiler_infer_env::GlobalBareLookupState::*; pub use crate::v1_compiler_infer_env::{authored_name, empty_symbol_index}; @@ -660,31 +658,15 @@ pub fn lookup_func_sig_in_scope( )) } -pub fn typed_named_arg_matches_note() -> String { +pub fn order_typed_call_args_label_authority_note() -> String { thread_local! { static CACHED: String = { - "Emission translates source named arguments into target positional arguments. The accepted caller-label relation is v1.compiler.infer call_arg_label_matches_param, so this projection consumes that authority rather than comparing exact declaration spelling independently: a declaration-side unused parameter `_args` carries caller label `args`. A bare anonymous `_` stays positional because it has no unique caller-visible identity with which to reorder an argument. This closes the seven retained v2.compiler.eval E0308 call-order blocks on board 907f19c2cc7; the preregistered treatment and controls are docs/probes/underscore_named_call_order_treatment_2026-08-23.md.".to_string() + "Emission translates source named arguments into target positional arguments. The accepted caller-label relation is v1.compiler.infer call_param_caller_label, the canonical projection consumed by call_arg_label_matches_param, so emission does not compare exact declaration spelling independently: a declaration-side unused parameter `_args` carries caller label `args`. A bare anonymous `_` has no caller-visible identity and stays positional. Both argument and parameter indexes use that canonical label, preserving linear construction and lookup rather than scanning either population per member. This closes the seven retained v2.compiler.eval E0308 call-order blocks on board 907f19c2cc7; the preregistered treatment and controls are docs/probes/underscore_named_call_order_treatment_2026-08-23.md.".to_string() }; } CACHED.with(|c: &String| c.clone()) } -pub fn typed_named_arg_matches( - arg: Rc, - name: String, - source_indices: Rc>>, -) -> bool { - { - let n = arg_name_at(arg.clone(), source_indices.clone()); - if (n.clone() == None) { - false - } else { - ((name.clone() != "_".to_string()) - && call_arg_label_matches_param(name.clone(), n.clone().unwrap())) - } - } -} - pub fn order_typed_call_args( args: Rc>>, func: String, @@ -708,6 +690,35 @@ pub fn order_typed_call_args( match lookup_func_sig_in_scope(scope.clone(), func.clone()) { None => args.clone(), Some(sig) => { + let arg_map = args.iter().cloned().fold( + v1_rt::rc_empty_map::>(), + |acc: Rc>>, arg: Rc| { + let n = arg_name_at( + arg.clone(), + scope.type_env.clone().source_indices.clone(), + ); + if (n.clone() != None) { + v1_rt::rc_map_insert(acc.clone(), n.clone().unwrap(), arg.clone()) + } else { + acc.clone() + } + }, + ); + let param_label_set = sig.params.clone().iter().cloned().fold( + v1_rt::rc_empty_map::(), + |acc: Rc>, param: Rc| { + let caller_label = call_param_caller_label(param_node_name_at( + param.clone(), + scope.type_env.clone().source_indices.clone(), + )); + match caller_label.clone() { + Some(label) => { + v1_rt::rc_map_insert(acc.clone(), label.clone(), true) + } + None => acc.clone(), + } + }, + ); let ordered = Rc::new({ let mut __result = Vec::new(); for param in sig.params.clone().iter().cloned() { @@ -717,23 +728,13 @@ pub fn order_typed_call_args( param.clone(), scope.type_env.clone().source_indices.clone(), ); - match Rc::new({ - let mut __result = Vec::new(); - for arg in args.iter().cloned() { - if typed_named_arg_matches( - arg.clone(), - param_name.clone(), - scope.type_env.clone().source_indices.clone(), - ) { - __result.push(arg); + match call_param_caller_label(param_name.clone()) { + Some(label) => { + match v1_rt::map_get(&arg_map, label.clone()) { + Some(arg) => Rc::new(vec![arg.clone()]), + None => Rc::new(vec![]), } } - __result - }) - .first() - .cloned() - { - Some(arg) => Rc::new(vec![arg.clone()]), None => Rc::new(vec![]), } }) @@ -754,23 +755,8 @@ pub fn order_typed_call_args( if (n.clone() == None) { true } else { - ({ - let mut __found = false; - for param in sig.params.clone().iter().cloned() { - if typed_named_arg_matches( - arg.clone(), - param_node_name_at( - param.clone(), - scope.type_env.clone().source_indices.clone(), - ), - scope.type_env.clone().source_indices.clone(), - ) { - __found = true; - break; - } - } - __found - } == false) + (emit_map_has(param_label_set.clone(), n.clone().unwrap()) + == false) } } { __result.push(arg); diff --git a/src/v1/stage0/src/v1_compiler_emit_go.rs b/src/v1/stage0/src/v1_compiler_emit_go.rs index 82888e5e842..fd073552680 100644 --- a/src/v1/stage0/src/v1_compiler_emit_go.rs +++ b/src/v1/stage0/src/v1_compiler_emit_go.rs @@ -29,7 +29,7 @@ pub use crate::v1_compiler_emit::{ empty_emit_scope, escape_go_interp_text, extract_string_interp_parts, has_nested_records_node, is_null_coalesce, is_tco_eligible, lookup_item, module_emit_scope, order_typed_call_args, scope_after_expr, seed_bindings, service_fallback_transport, service_field_decls, - test_file_path, typed_named_arg_matches, + test_file_path, }; pub use crate::v1_compiler_emit::{BlockEmitState, BoundOperation, InterpPart, ServiceFieldSet}; pub use crate::v1_compiler_emit_core_support::{ diff --git a/src/v1/stage0/src/v1_compiler_emit_python.rs b/src/v1/stage0/src/v1_compiler_emit_python.rs index f244c7fe8f5..2907754657d 100644 --- a/src/v1/stage0/src/v1_compiler_emit_python.rs +++ b/src/v1/stage0/src/v1_compiler_emit_python.rs @@ -29,7 +29,6 @@ pub use crate::v1_compiler_emit::{ has_nested_records_node, has_service_items, is_null_coalesce, is_tco_eligible, lookup_item, module_emit_scope, order_typed_call_args, scope_after_expr, seed_bindings, service_fallback_transport, service_field_ctors, service_field_decls, test_file_path, - typed_named_arg_matches, }; pub use crate::v1_compiler_emit::{BlockEmitState, BoundOperation, InterpPart, ServiceFieldSet}; pub use crate::v1_compiler_emit_core_support::{ diff --git a/src/v1/stage0/src/v1_compiler_emit_rust.rs b/src/v1/stage0/src/v1_compiler_emit_rust.rs index 470375776e0..d731c3767b3 100644 --- a/src/v1/stage0/src/v1_compiler_emit_rust.rs +++ b/src/v1/stage0/src/v1_compiler_emit_rust.rs @@ -66,8 +66,7 @@ pub use crate::v1_compiler_emit::{ is_tco_identity_passthrough, lookup_item, module_emit_scope, order_typed_call_args, render_node_type, render_tuple_parts, rust_literal_for_pattern, scope_after_expr, seed_bindings, service_fallback_transport, service_field_ctors, service_field_decls, - tco_reassign_core, transport_binding_refusal_fact, typed_named_arg_matches, - unwrap_single_field_product, + tco_reassign_core, transport_binding_refusal_fact, unwrap_single_field_product, }; pub use crate::v1_compiler_emit::{ BlockEmitState, BoundOperation, EmitterOutcome, FileResultChannel, FileResultField, FileVerb, diff --git a/src/v1/stage0/src/v1_compiler_infer.rs b/src/v1/stage0/src/v1_compiler_infer.rs index b178163f57c..bd1aa17b3e0 100644 --- a/src/v1/stage0/src/v1_compiler_infer.rs +++ b/src/v1/stage0/src/v1_compiler_infer.rs @@ -3710,12 +3710,33 @@ pub fn is_anonymous_bound_name(name: String) -> bool { (name.clone() == "_".to_string()) } -pub fn call_arg_label_matches_param(param_name: String, arg_label: String) -> bool { - (((param_name.clone() == arg_label.clone()) || (param_name.clone() == "_".to_string())) - || (((v1_rt::string_length(¶m_name) >= 1) +pub fn call_param_caller_label(param_name: String) -> Option { + if (param_name.clone() == "_".to_string()) { + None + } else { + if ((v1_rt::string_length(¶m_name) >= 1) && (v1_rt::substring(¶m_name, 0, 1) == "_".to_string())) - && (v1_rt::substring(¶m_name, 1, v1_rt::string_length(¶m_name)) - == arg_label.clone()))) + { + Some(v1_rt::substring( + ¶m_name, + 1, + v1_rt::string_length(¶m_name), + )) + } else { + Some(param_name.clone()) + } + } +} + +pub fn call_arg_label_matches_param(param_name: String, arg_label: String) -> bool { + if (param_name.clone() == "_".to_string()) { + true + } else { + match call_param_caller_label(param_name.clone()) { + Some(caller_label) => (caller_label.clone() == arg_label.clone()), + None => false, + } + } } pub fn param_binds_positionally( From c28e8ed99ec745d78a8fb6dab4c25b26f737d8b4 Mon Sep 17 00:00:00 2001 From: gunbc-ci-auto-heal Date: Sun, 23 Aug 2026 17:43:29 +0000 Subject: [PATCH 5/6] Preserve exact underscore call labels --- src/v1/04_infer.dag | 13 ++---- src/v1/05_emit.dag | 24 +++++----- src/v1/compiler_tests_rust.dag | 7 ++- src/v1/stage0/src/compiler_tests.rs | 7 ++- .../src/v1_compiler_compiler_tests_rust.rs | 2 +- src/v1/stage0/src/v1_compiler_emit.rs | 46 ++++++++++++------- src/v1/stage0/src/v1_compiler_infer.rs | 27 ++++++----- 7 files changed, 75 insertions(+), 51 deletions(-) diff --git a/src/v1/04_infer.dag b/src/v1/04_infer.dag index 87f0fc2e3d9..38cf5048933 100644 --- a/src/v1/04_infer.dag +++ b/src/v1/04_infer.dag @@ -2476,14 +2476,14 @@ fn borrowed_callable_call_type(gnode: Node, typed_args: List, scope: Infer fn is_anonymous_bound_name(name: String) -> Bool { name == "_" } -fn call_param_caller_label(param_name: String) -> String? { +fn call_param_caller_labels(param_name: String) -> List { if param_name == "_" { - none + [] } else if string_length(s: param_name) >= 1 && substring(s: param_name, start: 0, end: 1) == "_" { - Present { value: substring(s: param_name, start: 1, end: string_length(s: param_name)) } + [param_name, substring(s: param_name, start: 1, end: string_length(s: param_name))] } else { - Present { value: param_name } + [param_name] } } @@ -2491,10 +2491,7 @@ fn call_arg_label_matches_param(param_name: String, arg_label: String) -> Bool { if param_name == "_" { true } else { - match call_param_caller_label(param_name: param_name) { - Present { value: caller_label } => caller_label == arg_label - Absent => false - } + call_param_caller_labels(param_name: param_name) |> any(caller_label => caller_label == arg_label) } } diff --git a/src/v1/05_emit.dag b/src/v1/05_emit.dag index ddf60c45e8a..053e27b53a8 100644 --- a/src/v1/05_emit.dag +++ b/src/v1/05_emit.dag @@ -49,7 +49,7 @@ import v1.compiler.infer_lookup { lookup_func_sig, func_sig_if_resolved } import v1.compiler.infer { InferScope, - build_params_scope, extend_scope, call_param_caller_label + build_params_scope, extend_scope, call_param_caller_labels } import v1.compiler.infer_emit_info { TypeSummary, EmitGraphInfo } @@ -303,7 +303,7 @@ fn lookup_func_sig_in_scope(scope: InferScope, name: String) -> ResolvedFuncSig? func_sig_if_resolved(lookup: lookup_func_sig(func_env: scope.func_env, type_env: scope.type_env, name: name)) } -data order_typed_call_args_label_authority_note: String = "Emission translates source named arguments into target positional arguments. The accepted caller-label relation is v1.compiler.infer call_param_caller_label, the canonical projection consumed by call_arg_label_matches_param, so emission does not compare exact declaration spelling independently: a declaration-side unused parameter `_args` carries caller label `args`. A bare anonymous `_` has no caller-visible identity and stays positional. Both argument and parameter indexes use that canonical label, preserving linear construction and lookup rather than scanning either population per member. This closes the seven retained v2.compiler.eval E0308 call-order blocks on board 907f19c2cc7; the preregistered treatment and controls are docs/probes/underscore_named_call_order_treatment_2026-08-23.md." +data order_typed_call_args_label_authority_note: String = "Emission translates source named arguments into target positional arguments. The accepted caller-label relation is v1.compiler.infer call_param_caller_labels, the bounded label set consumed by call_arg_label_matches_param, so emission does not compare declaration spelling independently: a declaration-side unused parameter `_args` accepts both exact label `_args` and caller-facing label `args`. A bare anonymous `_` has no caller-visible identity and stays positional. Both argument and parameter indexes use that accepted set, preserving linear construction and lookup rather than scanning either population per member. This closes the seven retained v2.compiler.eval E0308 call-order blocks on board 907f19c2cc7; the preregistered treatment and controls are docs/probes/underscore_named_call_order_treatment_2026-08-23.md." fn order_typed_call_args(args: List, func: String, scope: InferScope) -> List { let has_unnamed = args |> any(arg => arg_name_at(n: arg, source_indices: scope.type_env.source_indices) == none) @@ -318,20 +318,18 @@ fn order_typed_call_args(args: List, func: String, scope: InferScope) -> L if n != none { map_insert(acc, n.value, arg) } else { acc } ) let param_label_set = sig.params |> fold(init: empty_map(), f: (acc, param) => - let caller_label = call_param_caller_label(param_name: param_node_name_at(n: param, source_indices: scope.type_env.source_indices)) - match caller_label { - Present { value: label } => map_insert(acc, label, true) - Absent => acc - } + call_param_caller_labels(param_name: param_node_name_at(n: param, source_indices: scope.type_env.source_indices)) + |> fold(init: acc, f: (labels, label) => map_insert(labels, label, true)) ) let ordered = sig.params |> flat_map(param => let param_name = param_node_name_at(n: param, source_indices: scope.type_env.source_indices) - match call_param_caller_label(param_name: param_name) { - Present { value: label } => - match map_get(arg_map, label) { - Present { value: arg } => [arg] - Absent => [] - } + let matching = call_param_caller_labels(param_name: param_name) + |> flat_map(label => match map_get(arg_map, label) { + Present { value: arg } => [arg] + Absent => [] + }) + match matching |> first { + Present { value: arg } => [arg] Absent => [] } ) diff --git a/src/v1/compiler_tests_rust.dag b/src/v1/compiler_tests_rust.dag index ddad6c4604f..c783051dc49 100644 --- a/src/v1/compiler_tests_rust.dag +++ b/src/v1/compiler_tests_rust.dag @@ -2080,7 +2080,7 @@ fn ct_call_shape_wall_witness_test() -> String { " // it, so the compile seam must too) all stay silent.\n", " let green = compile_one(\n", " \"green.dag\",\n", - " \"module green\\nfn sub(a: Int, b: Int) -> Int { a - b }\\nfn ignore_ctx(_ctx: Int, b: Int) -> Int { b }\\nfn direct_order(a: Int, b: Int) -> Int { b }\\nfn underscore_order(_a: Int, b: Int) -> Int { b }\\nfn named() -> Int { sub(a: 10, b: 3) }\\nfn positional() -> Int { sub(10, 3) }\\nfn underscore_idiom() -> Int { ignore_ctx(ctx: 1, b: 2) }\\nfn direct_order_control() -> Int { direct_order(b: 23, a: 11) }\\nfn underscore_order_witness() -> Int { underscore_order(b: 23, a: 11) }\\n\",\n", + " \"module green\\nfn sub(a: Int, b: Int) -> Int { a - b }\\nfn ignore_ctx(_ctx: Int, b: Int) -> Int { b }\\nfn direct_order(a: Int, b: Int) -> Int { b }\\nfn underscore_order(_a: Int, b: Int) -> Int { b }\\nfn underscore_exact_order(_a: Int, b: Int) -> Int { b }\\nfn named() -> Int { sub(a: 10, b: 3) }\\nfn positional() -> Int { sub(10, 3) }\\nfn underscore_idiom() -> Int { ignore_ctx(ctx: 1, b: 2) }\\nfn direct_order_control() -> Int { direct_order(b: 23, a: 11) }\\nfn underscore_order_witness() -> Int { underscore_order(b: 23, a: 11) }\\nfn underscore_exact_order_control() -> Int { underscore_exact_order(b: 23, _a: 11) }\\n\",\n", " );\n", " assert!(\n", " green.diagnostics.is_empty(),\n", @@ -2096,6 +2096,11 @@ fn ct_call_shape_wall_witness_test() -> String { " emitted.content\n", " );\n", " assert!(\n", + " emitted.content.contains(\"underscore_exact_order(11, 23)\"),\n", + " \"control: exact underscore-prefixed caller labels must remain accepted and follow declaration order, got: {}\",\n", + " emitted.content\n", + " );\n", + " assert!(\n", " emitted.content.contains(\"underscore_order(11, 23)\"),\n", " \"named arguments accepted through the declaration-side underscore idiom must follow that same declaration order, got: {}\",\n", " emitted.content\n", diff --git a/src/v1/stage0/src/compiler_tests.rs b/src/v1/stage0/src/compiler_tests.rs index e1450876fc1..dcb2bceb5ef 100644 --- a/src/v1/stage0/src/compiler_tests.rs +++ b/src/v1/stage0/src/compiler_tests.rs @@ -522,7 +522,7 @@ mod compiler_tests { // it, so the compile seam must too) all stay silent. let green = compile_one( "green.dag", - "module green\nfn sub(a: Int, b: Int) -> Int { a - b }\nfn ignore_ctx(_ctx: Int, b: Int) -> Int { b }\nfn direct_order(a: Int, b: Int) -> Int { b }\nfn underscore_order(_a: Int, b: Int) -> Int { b }\nfn named() -> Int { sub(a: 10, b: 3) }\nfn positional() -> Int { sub(10, 3) }\nfn underscore_idiom() -> Int { ignore_ctx(ctx: 1, b: 2) }\nfn direct_order_control() -> Int { direct_order(b: 23, a: 11) }\nfn underscore_order_witness() -> Int { underscore_order(b: 23, a: 11) }\n", + "module green\nfn sub(a: Int, b: Int) -> Int { a - b }\nfn ignore_ctx(_ctx: Int, b: Int) -> Int { b }\nfn direct_order(a: Int, b: Int) -> Int { b }\nfn underscore_order(_a: Int, b: Int) -> Int { b }\nfn underscore_exact_order(_a: Int, b: Int) -> Int { b }\nfn named() -> Int { sub(a: 10, b: 3) }\nfn positional() -> Int { sub(10, 3) }\nfn underscore_idiom() -> Int { ignore_ctx(ctx: 1, b: 2) }\nfn direct_order_control() -> Int { direct_order(b: 23, a: 11) }\nfn underscore_order_witness() -> Int { underscore_order(b: 23, a: 11) }\nfn underscore_exact_order_control() -> Int { underscore_exact_order(b: 23, _a: 11) }\n", ); assert!( green.diagnostics.is_empty(), @@ -537,6 +537,11 @@ mod compiler_tests { "control: ordinary named arguments must follow declaration order, got: {}", emitted.content ); + assert!( + emitted.content.contains("underscore_exact_order(11, 23)"), + "control: exact underscore-prefixed caller labels must remain accepted and follow declaration order, got: {}", + emitted.content + ); assert!( emitted.content.contains("underscore_order(11, 23)"), "named arguments accepted through the declaration-side underscore idiom must follow that same declaration order, got: {}", 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 151d06d25e2..9532f0ecda6 100644 --- a/src/v1/stage0/src/v1_compiler_compiler_tests_rust.rs +++ b/src/v1/stage0/src/v1_compiler_compiler_tests_rust.rs @@ -337,7 +337,7 @@ pub fn ct_contracts_sidecar_witness_test() -> String { } pub fn ct_call_shape_wall_witness_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(" #[test]\n".to_string(), " fn call_shape_wall_witness() {\n".to_string()), " // DISCRIMINATING RED for direct_call_shape_wall_note (04_infer). Before the\n".to_string()), " // wall, `sub(a: 10, bb: 3)` against `fn sub(a: Int, b: Int)` compiled with\n".to_string()), " // ZERO diagnostics: the per-param type walk absorbed the mislabeled arg into\n".to_string()), " // its position, the interpreter refused the same call at runtime\n".to_string()), " // (CallContractMismatch in call_function_inner), and the Rust emitter\n".to_string()), " // silently REORDERED it positionally — two realizations of one program\n".to_string()), " // disagreeing silently. The wall makes the compile seam agree with the\n".to_string()), " // runtime authority, on both of the classes the runtime refuses.\n".to_string()), " let result = std::thread::Builder::new()\n".to_string()), " .stack_size(8 * 1024 * 1024)\n".to_string()), " .spawn(|| {\n".to_string()), " let compile_one = |path: &str, content: &str| {\n".to_string()), " crate::v1_compiler_compile::compile_sources(\n".to_string()), " std::rc::Rc::new(im::vector![std::rc::Rc::new(\n".to_string()), " crate::v1_compiler_compile::SourceFile {\n".to_string()), " path: path.to_string(),\n".to_string()), " content: content.to_string(),\n".to_string()), " }\n".to_string()), " )]),\n".to_string()), " crate::v1_compiler_artifact::RenderTarget::Rust,\n".to_string()), " )\n".to_string()), " };\n".to_string()), " let mislabeled = compile_one(\n".to_string()), " \"mislabel.dag\",\n".to_string()), " \"module mislabel\\nfn sub(a: Int, b: Int) -> Int { a - b }\\nfn f() -> Int { sub(a: 10, bb: 3) }\\n\",\n".to_string()), " );\n".to_string()), " let unknown: Vec<_> = mislabeled.diagnostics.iter()\n".to_string()), " .filter(|d| matches!(*d.diagnostic, crate::v1_std_core::CompilerDiagnostic::CallArgumentNameUnknown { .. }))\n".to_string()), " .collect();\n".to_string()), " assert!(\n".to_string()), " !unknown.is_empty(),\n".to_string()), " \"an argument labeling a parameter that does not exist must refuse at the compile seam — the interpreter already refuses this call at runtime, and the emitter silently reorders it, got: {:?}\",\n".to_string()), " mislabeled.diagnostics\n".to_string()), " );\n".to_string()), " assert!(\n".to_string()), " unknown.iter().all(|d| crate::v1_std_core::is_error_diagnostic(d.diagnostic.clone())\n".to_string()), " && crate::v1_std_core::is_interpreter_blocking_diagnostic(d.diagnostic.clone())),\n".to_string()), " \"CallArgumentNameUnknown must BLOCK — a counted advisory would still emit the silently-reordered realization\"\n".to_string()), " );\n".to_string()), " let surplus = compile_one(\n".to_string()), " \"surplus.dag\",\n".to_string()), " \"module surplus\\nfn two(a: Int, b: Int) -> Int { a + b }\\nfn f() -> Int { two(1, 2, 3) }\\n\",\n".to_string()), " );\n".to_string()), " assert!(\n".to_string()), " surplus.diagnostics.iter().any(|d| matches!(*d.diagnostic, crate::v1_std_core::CompilerDiagnostic::CallPositionalSurplus { .. })),\n".to_string()), " \"a positional argument beyond the declared positional parameters must refuse — the interpreter refuses the same call (too many positional arguments), got: {:?}\",\n".to_string()), " surplus.diagnostics\n".to_string()), " );\n".to_string()), " // POSITIVE CONTROLS at ZERO diagnostics of any severity (the filtering\n".to_string()), " // lesson of codex review 45357: asserting only the absence of the\n".to_string()), " // blocking variant lets an advisory pass unnoticed). Correct labels,\n".to_string()), " // correct positional binding, and the deliberately-unused-parameter\n".to_string()), " // idiom (label `ctx` against declared `_ctx` — the interpreter accepts\n".to_string()), " // it, so the compile seam must too) all stay silent.\n".to_string()), " let green = compile_one(\n".to_string()), " \"green.dag\",\n".to_string()), " \"module green\\nfn sub(a: Int, b: Int) -> Int { a - b }\\nfn ignore_ctx(_ctx: Int, b: Int) -> Int { b }\\nfn direct_order(a: Int, b: Int) -> Int { b }\\nfn underscore_order(_a: Int, b: Int) -> Int { b }\\nfn named() -> Int { sub(a: 10, b: 3) }\\nfn positional() -> Int { sub(10, 3) }\\nfn underscore_idiom() -> Int { ignore_ctx(ctx: 1, b: 2) }\\nfn direct_order_control() -> Int { direct_order(b: 23, a: 11) }\\nfn underscore_order_witness() -> Int { underscore_order(b: 23, a: 11) }\\n\",\n".to_string()), " );\n".to_string()), " assert!(\n".to_string()), " green.diagnostics.is_empty(),\n".to_string()), " \"correct call shapes must compile with NO diagnostic of any severity, got: {:?}\",\n".to_string()), " green.diagnostics\n".to_string()), " );\n".to_string()), " let emitted = green.files.iter()\n".to_string()), " .find(|f| f.path.ends_with(\"green.rs\"))\n".to_string()), " .expect(\"green fixture must emit its Rust module\");\n".to_string()), " assert!(\n".to_string()), " emitted.content.contains(\"direct_order(11, 23)\"),\n".to_string()), " \"control: ordinary named arguments must follow declaration order, got: {}\",\n".to_string()), " emitted.content\n".to_string()), " );\n".to_string()), " assert!(\n".to_string()), " emitted.content.contains(\"underscore_order(11, 23)\"),\n".to_string()), " \"named arguments accepted through the declaration-side underscore idiom must follow that same declaration order, got: {}\",\n".to_string()), " emitted.content\n".to_string()), " );\n".to_string()), " })\n".to_string()), " .expect(\"failed to spawn thread\")\n".to_string()), " .join();\n".to_string()), " result.expect(\"call_shape_wall_witness 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(" #[test]\n".to_string(), " fn call_shape_wall_witness() {\n".to_string()), " // DISCRIMINATING RED for direct_call_shape_wall_note (04_infer). Before the\n".to_string()), " // wall, `sub(a: 10, bb: 3)` against `fn sub(a: Int, b: Int)` compiled with\n".to_string()), " // ZERO diagnostics: the per-param type walk absorbed the mislabeled arg into\n".to_string()), " // its position, the interpreter refused the same call at runtime\n".to_string()), " // (CallContractMismatch in call_function_inner), and the Rust emitter\n".to_string()), " // silently REORDERED it positionally — two realizations of one program\n".to_string()), " // disagreeing silently. The wall makes the compile seam agree with the\n".to_string()), " // runtime authority, on both of the classes the runtime refuses.\n".to_string()), " let result = std::thread::Builder::new()\n".to_string()), " .stack_size(8 * 1024 * 1024)\n".to_string()), " .spawn(|| {\n".to_string()), " let compile_one = |path: &str, content: &str| {\n".to_string()), " crate::v1_compiler_compile::compile_sources(\n".to_string()), " std::rc::Rc::new(im::vector![std::rc::Rc::new(\n".to_string()), " crate::v1_compiler_compile::SourceFile {\n".to_string()), " path: path.to_string(),\n".to_string()), " content: content.to_string(),\n".to_string()), " }\n".to_string()), " )]),\n".to_string()), " crate::v1_compiler_artifact::RenderTarget::Rust,\n".to_string()), " )\n".to_string()), " };\n".to_string()), " let mislabeled = compile_one(\n".to_string()), " \"mislabel.dag\",\n".to_string()), " \"module mislabel\\nfn sub(a: Int, b: Int) -> Int { a - b }\\nfn f() -> Int { sub(a: 10, bb: 3) }\\n\",\n".to_string()), " );\n".to_string()), " let unknown: Vec<_> = mislabeled.diagnostics.iter()\n".to_string()), " .filter(|d| matches!(*d.diagnostic, crate::v1_std_core::CompilerDiagnostic::CallArgumentNameUnknown { .. }))\n".to_string()), " .collect();\n".to_string()), " assert!(\n".to_string()), " !unknown.is_empty(),\n".to_string()), " \"an argument labeling a parameter that does not exist must refuse at the compile seam — the interpreter already refuses this call at runtime, and the emitter silently reorders it, got: {:?}\",\n".to_string()), " mislabeled.diagnostics\n".to_string()), " );\n".to_string()), " assert!(\n".to_string()), " unknown.iter().all(|d| crate::v1_std_core::is_error_diagnostic(d.diagnostic.clone())\n".to_string()), " && crate::v1_std_core::is_interpreter_blocking_diagnostic(d.diagnostic.clone())),\n".to_string()), " \"CallArgumentNameUnknown must BLOCK — a counted advisory would still emit the silently-reordered realization\"\n".to_string()), " );\n".to_string()), " let surplus = compile_one(\n".to_string()), " \"surplus.dag\",\n".to_string()), " \"module surplus\\nfn two(a: Int, b: Int) -> Int { a + b }\\nfn f() -> Int { two(1, 2, 3) }\\n\",\n".to_string()), " );\n".to_string()), " assert!(\n".to_string()), " surplus.diagnostics.iter().any(|d| matches!(*d.diagnostic, crate::v1_std_core::CompilerDiagnostic::CallPositionalSurplus { .. })),\n".to_string()), " \"a positional argument beyond the declared positional parameters must refuse — the interpreter refuses the same call (too many positional arguments), got: {:?}\",\n".to_string()), " surplus.diagnostics\n".to_string()), " );\n".to_string()), " // POSITIVE CONTROLS at ZERO diagnostics of any severity (the filtering\n".to_string()), " // lesson of codex review 45357: asserting only the absence of the\n".to_string()), " // blocking variant lets an advisory pass unnoticed). Correct labels,\n".to_string()), " // correct positional binding, and the deliberately-unused-parameter\n".to_string()), " // idiom (label `ctx` against declared `_ctx` — the interpreter accepts\n".to_string()), " // it, so the compile seam must too) all stay silent.\n".to_string()), " let green = compile_one(\n".to_string()), " \"green.dag\",\n".to_string()), " \"module green\\nfn sub(a: Int, b: Int) -> Int { a - b }\\nfn ignore_ctx(_ctx: Int, b: Int) -> Int { b }\\nfn direct_order(a: Int, b: Int) -> Int { b }\\nfn underscore_order(_a: Int, b: Int) -> Int { b }\\nfn underscore_exact_order(_a: Int, b: Int) -> Int { b }\\nfn named() -> Int { sub(a: 10, b: 3) }\\nfn positional() -> Int { sub(10, 3) }\\nfn underscore_idiom() -> Int { ignore_ctx(ctx: 1, b: 2) }\\nfn direct_order_control() -> Int { direct_order(b: 23, a: 11) }\\nfn underscore_order_witness() -> Int { underscore_order(b: 23, a: 11) }\\nfn underscore_exact_order_control() -> Int { underscore_exact_order(b: 23, _a: 11) }\\n\",\n".to_string()), " );\n".to_string()), " assert!(\n".to_string()), " green.diagnostics.is_empty(),\n".to_string()), " \"correct call shapes must compile with NO diagnostic of any severity, got: {:?}\",\n".to_string()), " green.diagnostics\n".to_string()), " );\n".to_string()), " let emitted = green.files.iter()\n".to_string()), " .find(|f| f.path.ends_with(\"green.rs\"))\n".to_string()), " .expect(\"green fixture must emit its Rust module\");\n".to_string()), " assert!(\n".to_string()), " emitted.content.contains(\"direct_order(11, 23)\"),\n".to_string()), " \"control: ordinary named arguments must follow declaration order, got: {}\",\n".to_string()), " emitted.content\n".to_string()), " );\n".to_string()), " assert!(\n".to_string()), " emitted.content.contains(\"underscore_exact_order(11, 23)\"),\n".to_string()), " \"control: exact underscore-prefixed caller labels must remain accepted and follow declaration order, got: {}\",\n".to_string()), " emitted.content\n".to_string()), " );\n".to_string()), " assert!(\n".to_string()), " emitted.content.contains(\"underscore_order(11, 23)\"),\n".to_string()), " \"named arguments accepted through the declaration-side underscore idiom must follow that same declaration order, got: {}\",\n".to_string()), " emitted.content\n".to_string()), " );\n".to_string()), " })\n".to_string()), " .expect(\"failed to spawn thread\")\n".to_string()), " .join();\n".to_string()), " result.expect(\"call_shape_wall_witness panicked\");\n".to_string()), " }\n\n".to_string()) } pub fn ct_call_shape_duplicate_wall_witness_test() -> String { diff --git a/src/v1/stage0/src/v1_compiler_emit.rs b/src/v1/stage0/src/v1_compiler_emit.rs index d13efc5993f..abddf432bc1 100644 --- a/src/v1/stage0/src/v1_compiler_emit.rs +++ b/src/v1/stage0/src/v1_compiler_emit.rs @@ -41,7 +41,7 @@ pub use crate::v1_compiler_emit_core_support::{ }; pub use crate::v1_compiler_emit_core_support::{EmitResult, TestProjection}; pub use crate::v1_compiler_infer::InferScope; -pub use crate::v1_compiler_infer::{build_params_scope, call_param_caller_label, extend_scope}; +pub use crate::v1_compiler_infer::{build_params_scope, call_param_caller_labels, extend_scope}; pub use crate::v1_compiler_infer_emit_info::{EmitGraphInfo, TypeSummary}; use crate::v1_compiler_infer_env::GlobalBareLookupState::*; pub use crate::v1_compiler_infer_env::{authored_name, empty_symbol_index}; @@ -661,7 +661,7 @@ pub fn lookup_func_sig_in_scope( pub fn order_typed_call_args_label_authority_note() -> String { thread_local! { static CACHED: String = { - "Emission translates source named arguments into target positional arguments. The accepted caller-label relation is v1.compiler.infer call_param_caller_label, the canonical projection consumed by call_arg_label_matches_param, so emission does not compare exact declaration spelling independently: a declaration-side unused parameter `_args` carries caller label `args`. A bare anonymous `_` has no caller-visible identity and stays positional. Both argument and parameter indexes use that canonical label, preserving linear construction and lookup rather than scanning either population per member. This closes the seven retained v2.compiler.eval E0308 call-order blocks on board 907f19c2cc7; the preregistered treatment and controls are docs/probes/underscore_named_call_order_treatment_2026-08-23.md.".to_string() + "Emission translates source named arguments into target positional arguments. The accepted caller-label relation is v1.compiler.infer call_param_caller_labels, the bounded label set consumed by call_arg_label_matches_param, so emission does not compare declaration spelling independently: a declaration-side unused parameter `_args` accepts both exact label `_args` and caller-facing label `args`. A bare anonymous `_` has no caller-visible identity and stays positional. Both argument and parameter indexes use that accepted set, preserving linear construction and lookup rather than scanning either population per member. This closes the seven retained v2.compiler.eval E0308 call-order blocks on board 907f19c2cc7; the preregistered treatment and controls are docs/probes/underscore_named_call_order_treatment_2026-08-23.md.".to_string() }; } CACHED.with(|c: &String| c.clone()) @@ -707,16 +707,18 @@ pub fn order_typed_call_args( let param_label_set = sig.params.clone().iter().cloned().fold( v1_rt::rc_empty_map::(), |acc: Rc>, param: Rc| { - let caller_label = call_param_caller_label(param_node_name_at( + call_param_caller_labels(param_node_name_at( param.clone(), scope.type_env.clone().source_indices.clone(), - )); - match caller_label.clone() { - Some(label) => { - v1_rt::rc_map_insert(acc.clone(), label.clone(), true) - } - None => acc.clone(), - } + )) + .iter() + .cloned() + .fold( + acc, + |labels: Rc>, label: String| { + v1_rt::rc_map_insert(labels, label.clone(), true) + }, + ) }, ); let ordered = Rc::new({ @@ -728,13 +730,25 @@ pub fn order_typed_call_args( param.clone(), scope.type_env.clone().source_indices.clone(), ); - match call_param_caller_label(param_name.clone()) { - Some(label) => { - match v1_rt::map_get(&arg_map, label.clone()) { - Some(arg) => Rc::new(vec![arg.clone()]), - None => Rc::new(vec![]), - } + let matching = Rc::new({ + let mut __result = Vec::new(); + for label in call_param_caller_labels(param_name.clone()) + .iter() + .cloned() + { + __result.extend( + (*match v1_rt::map_get(&arg_map, label.clone()) { + Some(arg) => Rc::new(vec![arg.clone()]), + None => Rc::new(vec![]), + }) + .iter() + .cloned(), + ); } + __result + }); + match matching.clone().first().cloned() { + Some(arg) => Rc::new(vec![arg.clone()]), None => Rc::new(vec![]), } }) diff --git a/src/v1/stage0/src/v1_compiler_infer.rs b/src/v1/stage0/src/v1_compiler_infer.rs index bd1aa17b3e0..2dec1153bad 100644 --- a/src/v1/stage0/src/v1_compiler_infer.rs +++ b/src/v1/stage0/src/v1_compiler_infer.rs @@ -3710,20 +3710,19 @@ pub fn is_anonymous_bound_name(name: String) -> bool { (name.clone() == "_".to_string()) } -pub fn call_param_caller_label(param_name: String) -> Option { +pub fn call_param_caller_labels(param_name: String) -> Rc> { if (param_name.clone() == "_".to_string()) { - None + Rc::new(vec![]) } else { if ((v1_rt::string_length(¶m_name) >= 1) && (v1_rt::substring(¶m_name, 0, 1) == "_".to_string())) { - Some(v1_rt::substring( - ¶m_name, - 1, - v1_rt::string_length(¶m_name), - )) + Rc::new(vec![ + param_name.clone(), + v1_rt::substring(¶m_name, 1, v1_rt::string_length(¶m_name)), + ]) } else { - Some(param_name.clone()) + Rc::new(vec![param_name.clone()]) } } } @@ -3732,9 +3731,15 @@ pub fn call_arg_label_matches_param(param_name: String, arg_label: String) -> bo if (param_name.clone() == "_".to_string()) { true } else { - match call_param_caller_label(param_name.clone()) { - Some(caller_label) => (caller_label.clone() == arg_label.clone()), - None => false, + { + let mut __found = false; + for caller_label in call_param_caller_labels(param_name.clone()).iter().cloned() { + if (caller_label.clone() == arg_label.clone()) { + __found = true; + break; + } + } + __found } } } From b2bf7d709f72433ed5ca93ba76536ec90561e356 Mon Sep 17 00:00:00 2001 From: gunbc-ci-auto-heal Date: Sun, 23 Aug 2026 18:11:28 +0000 Subject: [PATCH 6/6] Demote call ordering rationale to annotation --- src/v1/05_emit.dag | 6 ++++-- src/v1/stage0/src/v1_compiler_emit.rs | 9 --------- 2 files changed, 4 insertions(+), 11 deletions(-) diff --git a/src/v1/05_emit.dag b/src/v1/05_emit.dag index 053e27b53a8..c34cdd0cd30 100644 --- a/src/v1/05_emit.dag +++ b/src/v1/05_emit.dag @@ -303,8 +303,10 @@ fn lookup_func_sig_in_scope(scope: InferScope, name: String) -> ResolvedFuncSig? func_sig_if_resolved(lookup: lookup_func_sig(func_env: scope.func_env, type_env: scope.type_env, name: name)) } -data order_typed_call_args_label_authority_note: String = "Emission translates source named arguments into target positional arguments. The accepted caller-label relation is v1.compiler.infer call_param_caller_labels, the bounded label set consumed by call_arg_label_matches_param, so emission does not compare declaration spelling independently: a declaration-side unused parameter `_args` accepts both exact label `_args` and caller-facing label `args`. A bare anonymous `_` has no caller-visible identity and stays positional. Both argument and parameter indexes use that accepted set, preserving linear construction and lookup rather than scanning either population per member. This closes the seven retained v2.compiler.eval E0308 call-order blocks on board 907f19c2cc7; the preregistered treatment and controls are docs/probes/underscore_named_call_order_treatment_2026-08-23.md." - +// Translate named source arguments to target positional order through infer's +// accepted-label authority. `_args` accepts exact `_args` and caller-facing +// `args`; anonymous `_` has no reorderable identity. Both indexes consume that +// bounded label set, keeping construction and lookup linear. fn order_typed_call_args(args: List, func: String, scope: InferScope) -> List { let has_unnamed = args |> any(arg => arg_name_at(n: arg, source_indices: scope.type_env.source_indices) == none) if has_unnamed { diff --git a/src/v1/stage0/src/v1_compiler_emit.rs b/src/v1/stage0/src/v1_compiler_emit.rs index abddf432bc1..d4ab2396501 100644 --- a/src/v1/stage0/src/v1_compiler_emit.rs +++ b/src/v1/stage0/src/v1_compiler_emit.rs @@ -658,15 +658,6 @@ pub fn lookup_func_sig_in_scope( )) } -pub fn order_typed_call_args_label_authority_note() -> String { - thread_local! { - static CACHED: String = { - "Emission translates source named arguments into target positional arguments. The accepted caller-label relation is v1.compiler.infer call_param_caller_labels, the bounded label set consumed by call_arg_label_matches_param, so emission does not compare declaration spelling independently: a declaration-side unused parameter `_args` accepts both exact label `_args` and caller-facing label `args`. A bare anonymous `_` has no caller-visible identity and stays positional. Both argument and parameter indexes use that accepted set, preserving linear construction and lookup rather than scanning either population per member. This closes the seven retained v2.compiler.eval E0308 call-order blocks on board 907f19c2cc7; the preregistered treatment and controls are docs/probes/underscore_named_call_order_treatment_2026-08-23.md.".to_string() - }; - } - CACHED.with(|c: &String| c.clone()) -} - pub fn order_typed_call_args( args: Rc>>, func: String,