Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
ed235bd
v1 infer: use each method arg's own declared parameter type, not the …
Aug 19, 2026
b59309e
scratch: isolated-branch verify dispatch script (not part of the fix,…
Aug 19, 2026
e89dd91
scratch: fix full-SHA reference in verify dispatch script
Aug 19, 2026
5ba214e
scratch: verify sort_by algebra fix dispatch script (not part of the …
Aug 19, 2026
2087d8b
std/algebra: sort_by template row was missing its closure param_types…
Aug 19, 2026
0ebac3c
scratch: SHA-pinned, sccache-cleared verify dispatch for sort_by fix …
Aug 19, 2026
e900b3e
scratch: add verbatim-invocation logging to sccache-cleared verify di…
Aug 19, 2026
767de29
scratch: fix shallow-clone SHA resolution in verify dispatch (hardcod…
Aug 19, 2026
4b40f0b
Merge origin/main into infer-arg-contract-fix
Aug 19, 2026
93928c2
scratch: regen+measure dispatch for post-merge infer/algebra fix (not…
Aug 19, 2026
1fba7e6
fix: restore closing brace for match arg_contract dropped during merg…
Aug 19, 2026
093c2e3
scratch: pin regen+measure dispatch to the brace-fixed merge head
Aug 19, 2026
71ca5fb
scratch: v3 dispatch -- dual baseline (pre-8579 true delta + main-tip…
Aug 20, 2026
10542cb
fix: relocate body-scoped comment to module-item grain in 04_infer.dag
Aug 20, 2026
9b70e9d
scratch: pin v3 dispatch to post-comment-relocation head (10542cb3bb4)
Aug 20, 2026
52c9564
chore: remove scratch verification scripts flagged by review 53879 / …
Aug 20, 2026
4dbd4d5
scratch (temporary, will be removed before merge): scoped content-syn…
Aug 20, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion dag/std/algebra.dag
Original file line number Diff line number Diff line change
Expand Up @@ -331,7 +331,7 @@ fn free_monoid_collection_templates() -> List<AlgebraFieldTemplate> {
{ name: "get", param_types: [ReceiverSelf, NamedTemplate { name: "Int" }], return_type: OptionalOf { inner: ReceiverElement }, size_effect: Present { value: ProjectionEffect }, cost_shape: Present { value: ShapeLinearScan }, callback_element_position: none },
{ name: "skip", param_types: [ReceiverSelf, NamedTemplate { name: "Int" }], return_type: ReceiverSelf, size_effect: Present { value: ShrinkEffect }, cost_shape: Present { value: ShapeIterateBody }, callback_element_position: none },
{ name: "take", param_types: [ReceiverSelf, NamedTemplate { name: "Int" }], return_type: ReceiverSelf, size_effect: none, cost_shape: Present { value: ShapeIterateBody }, callback_element_position: none },
{ name: "sort_by", param_types: [ReceiverSelf], return_type: ReceiverSelf, size_effect: Present { value: IdentityEffect }, cost_shape: Present { value: ShapeSortBody }, callback_element_position: none },
{ name: "sort_by", param_types: [ReceiverSelf, CallableOf { params: [ReceiverElement], return_type: AlgebraTypeVariable { id: "SortKey" } }], return_type: ReceiverSelf, size_effect: Present { value: IdentityEffect }, cost_shape: Present { value: ShapeSortBody }, callback_element_position: Present { value: 0 } },
{ name: "append", param_types: [ReceiverSelf, ReceiverElement], return_type: ReceiverSelf, size_effect: none, cost_shape: Present { value: ShapeConstant }, callback_element_position: none },
{ name: "contains", param_types: [ReceiverSelf, ReceiverElement], return_type: NamedTemplate { name: "Bool" }, size_effect: none, cost_shape: none, callback_element_position: none },
{ name: "enumerate", param_types: [ReceiverSelf], return_type: ContainerOf { source: SameAsReceiver, element: TupleOf { first: NamedTemplate { name: "Int" }, second: ReceiverElement } }, size_effect: Present { value: IdentityEffect }, cost_shape: Present { value: ShapeIterateBody }, callback_element_position: none },
Expand Down
15 changes: 15 additions & 0 deletions scratch_content_sync_verify.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
set -e
unset RUSTC_WRAPPER || true
export RUSTC_WRAPPER=
FULL=52c9564429d0c5166f94f703c02e6d4d90892b80
git fetch origin "$FULL"
git checkout --force -B mirror-verify-scratch "$FULL"
GOT=$(git rev-parse HEAD)
if [ "$GOT" != "$FULL" ]; then
echo "SHA MISMATCH got=$GOT want=$FULL"
exit 99
fi
echo "PINNED_OK $FULL"
cd src/v1/stage0
rm -rf target
cargo test --release --package v1-compiler required_regen_host::tests::scratch_content_sync_two_files -- --nocapture --exact
91 changes: 30 additions & 61 deletions src/v1/04_infer.dag
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,7 @@ import v1.compiler.infer_lookup {
KnownMethodResolution,
resolve_known_method_node, resolve_scrutinee_type_node, resolve_method_receiver_type,
lookup_structural_method,
DeclaredArgContract, ContractKnown, MethodUnresolved, ContractUnavailable, declared_arg_types_for_method,
field_summary_for_type,
lookup_field_type_node, lookup_coproduct_common_field_node, product_field_result_type,
map_value_type_in_env, map_key_type_in_env, set_element_type_in_env,
Expand Down Expand Up @@ -3089,7 +3090,7 @@ fn derive_element_provenance(coll_expr: Node, elem_type: Node, scope: InferScope
}
}

fn infer_method_args_with_fold(method_name: String?, method_args: List<Node>, fold_info: ArgInferResult?, fold_acc_type: Node, element_type: Node, elem_provenance: SubValueRelation, scope: InferScope) -> List<ArgInferResult> {
fn infer_method_args_with_fold(method_name: String?, method_args: List<Node>, fold_info: ArgInferResult?, fold_acc_type: Node, element_type: Node, elem_provenance: SubValueRelation, arg_contract: DeclaredArgContract, scope: InferScope) -> List<ArgInferResult> {
let is_fold = method_name_is(opt: method_name, expected: "fold")
method_args |> enumerate |> map(idx_pair =>
let a = idx_pair.second
Expand Down Expand Up @@ -3151,58 +3152,25 @@ fn infer_method_args_with_fold(method_name: String?, method_args: List<Node>, fo
lambda_param_provenance: nf_prov_map
}
} else { scope }
if is_lambda_expr(e: nf_lam_value) {
infer_arg_with_element_type(arg: a, element_type: element_type, scope: nf_scope)
} else {
match scalar_shaped_builtin_method_arg_type(method_name: method_name) {
Present { value: declared_type } =>
infer_arg_with_element_type(arg: a, element_type: declared_type, scope: nf_scope)
Absent =>
infer_arg_with_element_type(arg: a, element_type: element_type, scope: nf_scope)
}
match arg_contract {
MethodUnresolved => infer_arg_with_element_type(arg: a, element_type: element_type, scope: nf_scope)
ContractUnavailable =>
let ar = infer_expr(texpr: nf_lam_value, scope: nf_scope, expected: none)
let contract_diag = categorized_error(message: "no declared argument contract is available for this method call; refusing rather than substituting the receiver element type", span: a.span, module_name: nf_scope.module_name, category: "method-arg-contract-unavailable")
ArgInferResult { typed_arg: make_arg_node(name: arg_name_at(n: a, source_indices: nf_scope.type_env.source_indices), value: ar.typed, span: a.span, name_span: a.span), diagnostics: concat(ar.diagnostics, [contract_diag]) }
ContractKnown { types: declared_types } =>
match declared_types |> skip(idx) |> first {
Present { value: declared_type } => infer_arg_with_element_type(arg: a, element_type: declared_type, scope: nf_scope)
Absent =>
let ar = infer_expr(texpr: nf_lam_value, scope: nf_scope, expected: none)
let contract_diag = categorized_error(message: "no declared argument contract is available at this parameter position; refusing rather than substituting the receiver element type", span: a.span, module_name: nf_scope.module_name, category: "method-arg-contract-unavailable")
ArgInferResult { typed_arg: make_arg_node(name: arg_name_at(n: a, source_indices: nf_scope.type_env.source_indices), value: ar.typed, span: a.span, name_span: a.span), diagnostics: concat(ar.diagnostics, [contract_diag]) }
}
}
}
)
}

// Interim classification standing in for a per-method parameter contract
// that does not yet exist anywhere in the tree (the corpus run this fix
// shipped with found no such authority to cite). The terminal shape is one
// authority naming each builtin method's parameter shape once -- arity,
// per-position type -- which this function and every other consumer reads,
// replacing this inline name list. Dissolves when that authority lands and
// this match is rewritten to look each method up in it instead of naming
// five methods by string.
//
// The Absent arm intentionally still defaults to element_type rather than
// refusing: this same fold's "method" position also receives qualified
// variant-constructor calls whose symbol_index_lookup misses at the
// ExprMethodCall seam (direct_call_shape_wall_note names this fallthrough
// as deliberate -- "the method-call path... is the method wall's
// territory," left uncovered). That population's real parameter types come
// from the constructor's declared field shapes, not from a collection's
// element_type or an Int refinement, and only the missing per-method
// parameter-contract authority above can classify it correctly. Refusing
// it here -- as an earlier version of this function did -- surfaced a
// pre-existing silent mistype as a hard compile break across every
// unrelated call site that happens to route through this bridge (measured:
// dozens of sites in host_effect_realize.dag, fleet_workflow_steps.dag,
// generated_artifact_gate.dag and others, none of them skip/take/at/nth/
// index calls). That is refusing an undecided, ungrounded population
// rather than the decided one this fix actually grounds -- the DESIGN §5
// "wall after grounding" distinction, not a wall this fix can build now.
fn scalar_shaped_builtin_method_arg_type(method_name: String?) -> Node? {
match method_name {
Present { value: mn } =>
if mn == "skip" || mn == "take" || mn == "at" || mn == "nth" || mn == "index" {
Present { value: int_type }
} else {
none
}
Absent => none
}
}

fn is_lambda_expr(e: Node) -> Bool {
match e.expr_data {
ExprLambda => true
Expand Down Expand Up @@ -3541,7 +3509,7 @@ fn infer_expr_body(texpr: Node, scope: InferScope, expected: Node?) -> InferResu
let first_type = resolved_type(n: first_result.typed)
let elem_type = for_each_element_type_node(n: first_type, source_indices: scope.type_env.source_indices)
let call_elem_provenance = derive_element_provenance(coll_expr: first_result.typed, elem_type: elem_type, scope: scope)
let remaining_results = infer_method_args_with_fold(method_name: call_method_name, method_args: call_method_args, fold_info: call_fold_info, fold_acc_type: call_fold_acc_type, element_type: elem_type, elem_provenance: call_elem_provenance, scope: scope)
let remaining_results = infer_method_args_with_fold(method_name: call_method_name, method_args: call_method_args, fold_info: call_fold_info, fold_acc_type: call_fold_acc_type, element_type: elem_type, elem_provenance: call_elem_provenance, arg_contract: MethodUnresolved, scope: scope)
concat(
[ArgInferResult { typed_arg: make_arg_node(name: arg_name_at(n: first_arg, source_indices: scope.type_env.source_indices), value: first_result.typed, span: first_arg.span, name_span: first_arg.span), diagnostics: first_result.diagnostics }],
remaining_results
Expand Down Expand Up @@ -4005,7 +3973,8 @@ fn infer_expr_body(texpr: Node, scope: InferScope, expected: Node?) -> InferResu
Absent => error_type
}
let mc_elem_provenance = derive_element_provenance(coll_expr: recv_typed, elem_type: recv_elem_type, scope: scope)
let mc_arg_infer_results = infer_method_args_with_fold(method_name: mc_method_name, method_args: mc_args, fold_info: fold_info, fold_acc_type: fold_acc_type, element_type: recv_elem_type, elem_provenance: mc_elem_provenance, scope: scope)
let mc_arg_contract = declared_arg_types_for_method(receiver_type: recv_rt, method_name: method_name, source_indices: scope.type_env.source_indices)
let mc_arg_infer_results = infer_method_args_with_fold(method_name: mc_method_name, method_args: mc_args, fold_info: fold_info, fold_acc_type: fold_acc_type, element_type: recv_elem_type, elem_provenance: mc_elem_provenance, arg_contract: mc_arg_contract, scope: scope)
let typed_mc_args = mc_arg_infer_results |> map(air => air.typed_arg)
let mc_arg_diags = flat_map(mc_arg_infer_results, air => air.diagnostics)
let mc_acc_is_under_resolved = !is_fully_resolved(n: fold_acc_type, source_indices: scope.type_env.source_indices)
Expand Down Expand Up @@ -8334,6 +8303,17 @@ fn corpus_item_name_counts(modules: List<ResolvedModule>, source_indices: Map<St
corpus_item_name_counts_nodes(module_nodes: census_module_nodes(modules: modules), source_indices: source_indices)
}

// Unconditional fully-qualified insertion, ahead of the uniqueness-gated 2-segment
// aliases below: `module_path.TypeName.variant_name` disambiguates by construction
// (a variant name is unique within one Disj, and a TypeName is unique within one
// module), so it needs no corpus-wide uniqueness check the way the bare `module_path.
// variant_name` shortcut does. This closes the population-A gap named in
// direct_call_shape_wall_note: a qualified constructor call site like
// `gunbc.WitnessBin.Run(...)` builds exactly this 3-segment dotted name
// (ExprMethodCall's qualified_direct_call gate, and func_sig_from_global_bare's
// borrowed_census_decl, both call symbol_index_lookup on it) and previously found
// nothing, falling through to the generic method-call bridge instead of the
// direct_call_shape_diags wall.
fn symbol_index_insert_unique_disj_variant_aliases(
index: SymbolIndex,
module_path: String,
Expand All @@ -8349,17 +8329,6 @@ fn symbol_index_insert_unique_disj_variant_aliases(
let tname = authored_name_at(source_indices: source_indices, node: item)
fold(item.children, init: acc, f: (a2, child) =>
let vname = authored_name_at(source_indices: source_indices, node: child)
// Unconditional fully-qualified insertion, ahead of the uniqueness-gated 2-segment
// aliases below: `module_path.TypeName.variant_name` disambiguates by construction
// (a variant name is unique within one Disj, and a TypeName is unique within one
// module), so it needs no corpus-wide uniqueness check the way the bare `module_path.
// variant_name` shortcut does. This closes the population-A gap named in
// direct_call_shape_wall_note: a qualified constructor call site like
// `gunbc.WitnessBin.Run(...)` builds exactly this 3-segment dotted name
// (ExprMethodCall's qualified_direct_call gate, and func_sig_from_global_bare's
// borrowed_census_decl, both call symbol_index_lookup on it) and previously found
// nothing, falling through to the generic method-call bridge instead of the
// direct_call_shape_diags wall.
let a2_qualified = symbol_index_insert(
index: a2,
qualified_name: concat(concat(module_path, ".", tname), ".", vname),
Expand Down
35 changes: 34 additions & 1 deletion src/v1/04_lookup.dag
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@ import v1.compiler.infer_types {
method_receiver_element_node,
emit_map_has,
enrich_kernel_type, is_declared_container_alias_spelling, reground_alias_carrier_identity,
kernel_profile_lookup
kernel_profile_lookup,
instantiate_algebra_type
}

import v1.compiler.infer_env {
Expand Down Expand Up @@ -588,6 +589,38 @@ fn lookup_structural_method(receiver_type: Node, method_name: String, source_ind
}
}

type DeclaredArgContract
= ContractKnown { types: List<Node> }
| MethodUnresolved
| ContractUnavailable

fn declared_arg_types_for_method(receiver_type: Node, method_name: String, source_indices: Map<String, NewlineIndex>) -> DeclaredArgContract {
let lookup = lookup_structural_method(receiver_type: receiver_type, method_name: method_name, source_indices: source_indices)
match lookup.resolution {
Absent => MethodUnresolved
Present { value: mfr } =>
match mfr.algebra_template {
Present { value: t } =>
let first_is_self = match t.param_types |> first {
Present { value: tp } => match tp { ReceiverSelf => true _ => false }
Absent => false
}
let non_receiver_templates = if first_is_self { t.param_types |> skip(1) } else { t.param_types }
ContractKnown { types: non_receiver_templates |> map(tp => instantiate_algebra_type(template: tp, base: receiver_type, source_indices: source_indices).ty) }
Absent =>
match mfr.field_node.inferred {
Present { value: Resolved { node: callable } } =>
if callable.params |> count > 0 {
ContractKnown { types: callable.params |> map(p => param_node_type_expr(n: p)) }
} else {
ContractUnavailable
}
_ => ContractUnavailable
}
}
}
}

fn resolve_known_method_node(receiver: Node, receiver_type: Node, method_name: String, fold_accumulator_type: Node?, service_registry: Map<String, List<OpEntry>>, source_indices: Map<String, NewlineIndex>) -> KnownMethodResolution {

let tier0 = lookup_structural_method(receiver_type: receiver_type, method_name: method_name, source_indices: source_indices)
Expand Down
38 changes: 38 additions & 0 deletions src/v1/stage0/src/required_regen_host.rs
Original file line number Diff line number Diff line change
Expand Up @@ -622,6 +622,44 @@ mod tests {
path
}

#[test]
fn scratch_content_sync_two_files() {
let workspace = super::super::workspace_root();
let emitted = compile_stage0(&workspace).expect("compile_stage0 must succeed");
let stage0_src = workspace.join("src/v1/stage0/src");
for basename in ["v1_compiler_infer.rs", "std_algebra.rs"] {
let emitted_content = emitted
.iter()
.find(|(path, _)| {
Path::new(path.as_str())
.file_name()
.and_then(|n| n.to_str())
== Some(basename)
})
.map(|(_, content)| content.clone());
let committed_content =
fs::read_to_string(stage0_src.join(basename)).expect("read committed file");
match emitted_content {
Some(emitted) if emitted == committed_content => {
eprintln!("SCRATCH_SYNC_RESULT {basename} MATCH");
}
Some(emitted) => {
eprintln!(
"SCRATCH_SYNC_RESULT {basename} MISMATCH emitted_len={} committed_len={}",
emitted.len(),
committed_content.len()
);
let out = std::env::temp_dir().join(format!("scratch_emitted_{basename}"));
fs::write(&out, &emitted).expect("write emitted scratch file");
eprintln!("SCRATCH_SYNC_EMITTED_WRITTEN {}", out.display());
}
None => {
eprintln!("SCRATCH_SYNC_RESULT {basename} NOT_EMITTED");
}
}
}
}

#[test]
fn empty_population_digest_refuses() {
let err = tree_digest_for_basenames(Path::new("/tmp"), &[], "committed").unwrap_err();
Expand Down