diff --git a/src/v1/05_emit_rust.dag b/src/v1/05_emit_rust.dag index d842048873b..730da103628 100644 --- a/src/v1/05_emit_rust.dag +++ b/src/v1/05_emit_rust.dag @@ -8792,7 +8792,7 @@ fn emit_typed_collection_lambda(lambda_expr: Node, elem_type_str: String, regist } } -fn emit_typed_fold_lambda(lambda_expr: Node, acc_type_str: String, elem_type_str: String, registry: Map, scope: InferScope, depth: Int, shared_types: Set, emit_info: EmitGraphInfo) -> String { +fn emit_typed_fold_lambda(lambda_expr: Node, acc_type_str: String, elem_type_str: String, elem_borrowed: Bool, registry: Map, scope: InferScope, depth: Int, shared_types: Set, emit_info: EmitGraphInfo) -> String { match lambda_expr.expr_data { ExprLambda => let ps = lambda_param_names_at(texpr: lambda_expr, source_indices: scope.type_env.source_indices) @@ -8803,7 +8803,10 @@ fn emit_typed_fold_lambda(lambda_expr: Node, acc_type_str: String, elem_type_str let fallback_types = ps |> enumerate |> map(pair => if pair.first == 0 { safe_acc_type } else { elem_type_str } ) - let param_strs = lambda_param_type_strs(params: ps, param_nodes: pn, fallback_types: fallback_types, shared_types: shared_types, source_indices: scope.type_env.source_indices, emit_info: emit_info, fold_acc_uses_fallback: true, type_env: scope.type_env) + let param_strs_raw = lambda_param_type_strs(params: ps, param_nodes: pn, fallback_types: fallback_types, shared_types: shared_types, source_indices: scope.type_env.source_indices, emit_info: emit_info, fold_acc_uses_fallback: true, type_env: scope.type_env) + let param_strs = if elem_borrowed { + param_strs_raw |> enumerate |> map(pair => if pair.first == 1 { "_" } else { pair.second }) + } else { param_strs_raw } let params_str = param_strs |> join(separator: ", ") let lambda_scope = lambda_scope_from_children(scope: scope, params: ps, param_nodes: pn) let body_str = emit_typed_expr(texpr: bd, registry: registry, scope: lambda_scope, depth: depth, shared_types: shared_types, emit_info: emit_info, fuel: 1024) @@ -8820,6 +8823,26 @@ fn emit_typed_fold_lambda(lambda_expr: Node, acc_type_str: String, elem_type_str } } +// FOLD'S UNUSED-ELEMENT OPTIMIZATION CHANGES THE ITEM TYPE, AND THE CLOSURE SIGNATURE WAS NOT TOLD. +// Dropping `.cloned()` when the fold lambda's element parameter is `_` is a real saving -- the clone is +// paid per element for a value the body never names. But it also changes the ITEM TYPE the iterator +// yields, from `T` to `&T`, while the lambda's element parameter kept its by-value annotation: rustc +// E0631, `expected fn(Rc<_>, &Rc<_>)`, `found fn(Rc<_>, Rc<_>)`, with rustc's own help line reading +// "consider adjusting the signature so it borrows its argument". One fact, derived in two places, and +// the second place did not know about the first. +// +// emit_typed_fold_lambda's elem_borrowed forces `_` at the element position. THE FIRST ATTEMPT AT THIS +// CHANGED THE FALLBACK TYPE STRING AND MEASURED NO EFFECT: lambda_param_type_strs prefers the parameter +// node's own RESOLVED type and only falls back when there is none, so the site still emitted +// `_: Rc`. The override has to sit where the parameter string is chosen, not where the +// fallback is supplied. The element is `_` BY CONSTRUCTION -- +// fold_lambda_element_unused is exactly the test that the parameter is named `_` -- so the annotation +// carries no information for a reader and only has to be ACCEPTED by rustc. Emitting `_` lets inference +// supply whichever of `T` / `&T` the iterator actually yields, which keeps the two consequences in +// agreement without the emitter restating the item type at the second site. Emitting `&{elem_type}` +// instead would restate it, and would then have to be kept in step with any future change to the strip +// -- reintroducing the shape of this defect while fixing this instance of it. `_` is also the device +// already used for elem_render_has_spurious_generic a few bindings earlier. fn emit_rust_fold_method_call(method_call_node: Node, fold_accumulator_type: Node?, result_type: InferredNode?, receiver: Node, args: List, registry: Map, scope: InferScope, depth: Int, shared_types: Set, emit_info: EmitGraphInfo) -> String { let recv_str = emit_typed_iter_owned_receiver(receiver: receiver, downstream: args, registry: registry, scope: scope, depth: depth, shared_types: shared_types, emit_info: emit_info) let elem_type_str = collection_element_type(receiver_type: receiver.inferred, shared_types: shared_types, source_indices: scope.type_env.source_indices, emit_info: emit_info, type_env: scope.type_env) @@ -8968,16 +8991,16 @@ fn emit_rust_fold_method_call(method_call_node: Node, fold_accumulator_type: Nod } Absent => "compile_error!(\"missing fold init argument\")" } - let fold_fn = match args |> skip(1) |> first { - Present { value: a } => - emit_typed_fold_lambda(lambda_expr: arg_value(n: a), acc_type_str: lambda_acc_type_str, elem_type_str: fold_elem_type_str, registry: registry, scope: scope, depth: depth, shared_types: shared_types, emit_info: fold_emit_info) - Absent => "compile_error!(\"missing fold function argument\")" - } let sharing = language_spec(target: Rust).sharing let elem_unused = match args |> skip(1) |> first { Present { value: a } => fold_lambda_element_unused(lambda_expr: arg_value(n: a), source_indices: scope.type_env.source_indices) Absent => false } + let fold_fn = match args |> skip(1) |> first { + Present { value: a } => + emit_typed_fold_lambda(lambda_expr: arg_value(n: a), acc_type_str: lambda_acc_type_str, elem_type_str: fold_elem_type_str, elem_borrowed: elem_unused, registry: registry, scope: scope, depth: depth, shared_types: shared_types, emit_info: fold_emit_info) + Absent => "compile_error!(\"missing fold function argument\")" + } let iter_template = if elem_unused { replace(sharing.iter_owned, ".cloned()", "") } else { sharing.iter_owned } concat(apply_type_template1(template: iter_template, arg0: recv_str), ".fold(", init_str, ", ", fold_fn, ")") } diff --git a/src/v1/stage0/src/std_types.rs b/src/v1/stage0/src/std_types.rs index 1d767804ca4..44e4fedbfe9 100644 --- a/src/v1/stage0/src/std_types.rs +++ b/src/v1/stage0/src/std_types.rs @@ -224,7 +224,7 @@ pub type Set = Rc>; pub type Map = Rc>; pub fn list_length(items: Rc>) -> i64 { - items.iter().fold(0, |acc: i64, _: _| (acc + 1)) + items.iter().fold(0, |acc: i64, _| (acc + 1)) } pub type CommitSha = String; diff --git a/src/v1/stage0/src/v1_compiler_emit_rust.rs b/src/v1/stage0/src/v1_compiler_emit_rust.rs index b71ce441aed..cadcca22b36 100644 --- a/src/v1/stage0/src/v1_compiler_emit_rust.rs +++ b/src/v1/stage0/src/v1_compiler_emit_rust.rs @@ -22060,6 +22060,7 @@ pub fn emit_typed_fold_lambda( lambda_expr: Rc, acc_type_str: String, elem_type_str: String, + elem_borrowed: bool, registry: Rc>>, scope: Rc, depth: i64, @@ -22111,7 +22112,7 @@ pub fn emit_typed_fold_lambda( } __result }); - let param_strs = lambda_param_type_strs( + let param_strs_raw = lambda_param_type_strs( ps.clone(), pn.clone(), fallback_types.clone(), @@ -22121,6 +22122,32 @@ pub fn emit_typed_fold_lambda( true, scope.type_env.clone(), ); + let param_strs = if elem_borrowed.clone() { + Rc::new({ + let mut __result = Vec::new(); + for pair in Rc::new( + param_strs_raw + .clone() + .iter() + .cloned() + .enumerate() + .map(|(i, v)| (i as i64, v)) + .collect::>(), + ) + .iter() + .cloned() + { + __result.push(if (pair.0.clone() == 1) { + "_".to_string() + } else { + pair.1.clone() + }); + } + __result + }) + } else { + param_strs_raw.clone() + }; let params_str = param_strs.clone().join(&", ".to_string()); let lambda_scope = lambda_scope_from_children(scope.clone(), ps.clone(), pn.clone()); let body_str = emit_typed_expr( @@ -22594,11 +22621,20 @@ pub fn emit_rust_fold_method_call( }, None => "compile_error!(\"missing fold init argument\")".to_string(), }; + let sharing = language_spec(RenderTarget::Rust).sharing.clone(); + let elem_unused = match args.clone().iter().cloned().skip(1 as usize).next() { + Some(a) => fold_lambda_element_unused( + arg_value(a.clone()), + scope.type_env.clone().source_indices.clone(), + ), + None => false, + }; let fold_fn = match args.clone().iter().cloned().skip(1 as usize).next() { Some(a) => emit_typed_fold_lambda( arg_value(a.clone()), lambda_acc_type_str.clone(), fold_elem_type_str.clone(), + elem_unused.clone(), registry.clone(), scope.clone(), depth.clone(), @@ -22607,14 +22643,6 @@ pub fn emit_rust_fold_method_call( ), None => "compile_error!(\"missing fold function argument\")".to_string(), }; - let sharing = language_spec(RenderTarget::Rust).sharing.clone(); - let elem_unused = match args.clone().iter().cloned().skip(1 as usize).next() { - Some(a) => fold_lambda_element_unused( - arg_value(a.clone()), - scope.type_env.clone().source_indices.clone(), - ), - None => false, - }; let iter_template = if elem_unused.clone() { v1_rt::replace( sharing.iter_owned.clone(),