From 775b8f94fd8c1d1aec431624f4a1bd2ac0dc9845 Mon Sep 17 00:00:00 2001 From: Alexander Medvednikov Date: Fri, 12 Jun 2026 10:12:12 +0300 Subject: [PATCH] cgen: fix autofree temp args in map expressions (#27321) --- vlib/v/gen/c/array.v | 75 ++++++++++++++-- vlib/v/gen/c/cgen.v | 14 ++- vlib/v/gen/c/fn.v | 21 ++++- vlib/v/gen/c/infix.v | 68 +++++++++++++- .../testdata/autofree_map_if_expr.c.must_have | 12 +++ .../v/gen/c/testdata/autofree_map_if_expr.out | 13 +++ vlib/v/gen/c/testdata/autofree_map_if_expr.vv | 90 +++++++++++++++++++ 7 files changed, 283 insertions(+), 10 deletions(-) create mode 100644 vlib/v/gen/c/testdata/autofree_map_if_expr.c.must_have create mode 100644 vlib/v/gen/c/testdata/autofree_map_if_expr.out create mode 100644 vlib/v/gen/c/testdata/autofree_map_if_expr.vv diff --git a/vlib/v/gen/c/array.v b/vlib/v/gen/c/array.v index c51a5a1e8..18b97adde 100644 --- a/vlib/v/gen/c/array.v +++ b/vlib/v/gen/c/array.v @@ -951,7 +951,10 @@ fn (mut g Gen) gen_array_map(node ast.CallExpr) { if mut expr is ast.AnonFn { if expr.inherited_vars.len > 0 { closure_var = g.new_tmp_var() + prev_track_closure_tmp_arg_vars := g.track_lambda_autofree_tmp_arg_vars + g.track_lambda_autofree_tmp_arg_vars = false g.declare_closure_fn(mut expr, closure_var) + g.track_lambda_autofree_tmp_arg_vars = prev_track_closure_tmp_arg_vars } } @@ -967,6 +970,11 @@ fn (mut g Gen) gen_array_map(node ast.CallExpr) { is_auto_heap) g.set_current_pos_as_last_stmt_pos() mut is_embed_map_filter := false + lambda_autofree_tmp_arg_vars_start := g.lambda_autofree_tmp_arg_vars.len + prev_inside_lambda_autofree_tmp := g.inside_lambda_autofree_tmp + prev_track_lambda_autofree_tmp_arg_vars := g.track_lambda_autofree_tmp_arg_vars + g.inside_lambda_autofree_tmp = true + g.track_lambda_autofree_tmp_arg_vars = true match mut expr { ast.AnonFn { g.write('${ret_elem_styp} ${tmp_map_expr_result_name} = ') @@ -978,7 +986,10 @@ fn (mut g Gen) gen_array_map(node ast.CallExpr) { if expr.inherited_vars.len > 0 { g.write_closure_fn(mut expr, var_name, closure_var) } else { + prev_track_anon_fn_tmp_arg_vars := g.track_lambda_autofree_tmp_arg_vars + g.track_lambda_autofree_tmp_arg_vars = false g.gen_anon_fn_decl(mut expr) + g.track_lambda_autofree_tmp_arg_vars = prev_track_anon_fn_tmp_arg_vars g.write('${expr.decl.name}(${var_name})') } if ret_elem_sym.kind == .array_fixed { @@ -1069,11 +1080,17 @@ fn (mut g Gen) gen_array_map(node ast.CallExpr) { } } + g.inside_lambda_autofree_tmp = prev_inside_lambda_autofree_tmp + g.track_lambda_autofree_tmp_arg_vars = prev_track_lambda_autofree_tmp_arg_vars + if left_is_array { - g.writeln2(';', - 'builtin__array_push${noscan}((array*)&${past.tmp_var}, &${tmp_map_expr_result_name});') + g.writeln(';') + g.write_lambda_autofree_tmp_arg_vars(lambda_autofree_tmp_arg_vars_start) + g.writeln('builtin__array_push${noscan}((array*)&${past.tmp_var}, &${tmp_map_expr_result_name});') } else { - g.writeln2(';', '${past.tmp_var}[${i}] = ${tmp_map_expr_result_name};') + g.writeln(';') + g.write_lambda_autofree_tmp_arg_vars(lambda_autofree_tmp_arg_vars_start) + g.writeln('${past.tmp_var}[${i}] = ${tmp_map_expr_result_name};') } g.indent-- g.writeln('}') @@ -1086,6 +1103,16 @@ fn (mut g Gen) gen_array_map(node ast.CallExpr) { } } +fn (mut g Gen) write_lambda_autofree_tmp_arg_vars(start int) { + if g.lambda_autofree_tmp_arg_vars.len <= start { + return + } + for var_name in g.lambda_autofree_tmp_arg_vars[start..] { + g.writeln('builtin__string_free(&${var_name});') + } + g.lambda_autofree_tmp_arg_vars = g.lambda_autofree_tmp_arg_vars[..start].clone() +} + @[inline] fn (mut g Gen) array_receiver_is_auto_heap(left ast.Expr) bool { return left is ast.Ident && g.resolved_ident_is_auto_heap(left) @@ -1632,7 +1659,10 @@ fn (mut g Gen) gen_array_filter(node ast.CallExpr) { if mut expr is ast.AnonFn { if expr.inherited_vars.len > 0 { closure_var = g.new_tmp_var() + prev_track_closure_tmp_arg_vars := g.track_lambda_autofree_tmp_arg_vars + g.track_lambda_autofree_tmp_arg_vars = false g.declare_closure_fn(mut expr, closure_var) + g.track_lambda_autofree_tmp_arg_vars = prev_track_closure_tmp_arg_vars } } @@ -1643,13 +1673,17 @@ fn (mut g Gen) gen_array_filter(node ast.CallExpr) { left_is_array, false) g.set_current_pos_as_last_stmt_pos() mut is_embed_map_filter := false + lambda_autofree_tmp_arg_vars_start := g.lambda_autofree_tmp_arg_vars.len match mut expr { ast.AnonFn { g.write('if (') if expr.inherited_vars.len > 0 { g.write_closure_fn(mut expr, var_name, closure_var) } else { + prev_track_anon_fn_tmp_arg_vars := g.track_lambda_autofree_tmp_arg_vars + g.track_lambda_autofree_tmp_arg_vars = false g.gen_anon_fn_decl(mut expr) + g.track_lambda_autofree_tmp_arg_vars = prev_track_anon_fn_tmp_arg_vars g.write('${expr.decl.name}(${var_name})') } } @@ -1689,6 +1723,7 @@ fn (mut g Gen) gen_array_filter(node ast.CallExpr) { g.writeln2(') {', '\tbuiltin__array_push${noscan}((array*)&${past.tmp_var}, &${var_name});') g.writeln('}') + g.write_lambda_autofree_tmp_arg_vars(lambda_autofree_tmp_arg_vars_start) g.indent-- g.writeln('}') if !is_embed_map_filter { @@ -2229,7 +2264,10 @@ fn (mut g Gen) gen_array_any(node ast.CallExpr) { if mut expr is ast.AnonFn { if expr.inherited_vars.len > 0 { closure_var = g.new_tmp_var() + prev_track_closure_tmp_arg_vars := g.track_lambda_autofree_tmp_arg_vars + g.track_lambda_autofree_tmp_arg_vars = false g.declare_closure_fn(mut expr, closure_var) + g.track_lambda_autofree_tmp_arg_vars = prev_track_closure_tmp_arg_vars } } i := g.new_tmp_var() @@ -2239,13 +2277,17 @@ fn (mut g Gen) gen_array_any(node ast.CallExpr) { g.write_prepared_var(var_name, elem_type, elem_type_str, past.tmp_var, i, left_is_array, false) g.set_current_pos_as_last_stmt_pos() mut is_embed_map_filter := false + lambda_autofree_tmp_arg_vars_start := g.lambda_autofree_tmp_arg_vars.len match mut expr { ast.AnonFn { g.write('if (') if expr.inherited_vars.len > 0 { g.write_closure_fn(mut expr, var_name, closure_var) } else { + prev_track_anon_fn_tmp_arg_vars := g.track_lambda_autofree_tmp_arg_vars + g.track_lambda_autofree_tmp_arg_vars = false g.gen_anon_fn_decl(mut expr) + g.track_lambda_autofree_tmp_arg_vars = prev_track_anon_fn_tmp_arg_vars g.write('${expr.decl.name}(${var_name})') } } @@ -2284,7 +2326,11 @@ fn (mut g Gen) gen_array_any(node ast.CallExpr) { } g.writeln2(') {', '\t${past.tmp_var} = true;') - g.writeln2('\tbreak;', '}') + g.writeln('}') + g.write_lambda_autofree_tmp_arg_vars(lambda_autofree_tmp_arg_vars_start) + g.writeln('if (${past.tmp_var}) {') + g.writeln('\tbreak;') + g.writeln('}') g.indent-- g.writeln('}') if !is_embed_map_filter { @@ -2323,7 +2369,10 @@ fn (mut g Gen) gen_array_count(node ast.CallExpr) { if mut expr is ast.AnonFn { if expr.inherited_vars.len > 0 { closure_var = g.new_tmp_var() + prev_track_closure_tmp_arg_vars := g.track_lambda_autofree_tmp_arg_vars + g.track_lambda_autofree_tmp_arg_vars = false g.declare_closure_fn(mut expr, closure_var) + g.track_lambda_autofree_tmp_arg_vars = prev_track_closure_tmp_arg_vars } } i := g.new_tmp_var() @@ -2333,13 +2382,17 @@ fn (mut g Gen) gen_array_count(node ast.CallExpr) { g.write_prepared_var(var_name, elem_type, elem_type_str, past.tmp_var, i, left_is_array, false) g.set_current_pos_as_last_stmt_pos() mut is_embed_map_filter := false + lambda_autofree_tmp_arg_vars_start := g.lambda_autofree_tmp_arg_vars.len match mut expr { ast.AnonFn { g.write('if (') if expr.inherited_vars.len > 0 { g.write_closure_fn(mut expr, var_name, closure_var) } else { + prev_track_anon_fn_tmp_arg_vars := g.track_lambda_autofree_tmp_arg_vars + g.track_lambda_autofree_tmp_arg_vars = false g.gen_anon_fn_decl(mut expr) + g.track_lambda_autofree_tmp_arg_vars = prev_track_anon_fn_tmp_arg_vars g.write('${expr.decl.name}(${var_name})') } } @@ -2379,6 +2432,7 @@ fn (mut g Gen) gen_array_count(node ast.CallExpr) { g.writeln2(') {', '\t++${past.tmp_var};') g.writeln('}') + g.write_lambda_autofree_tmp_arg_vars(lambda_autofree_tmp_arg_vars_start) g.indent-- g.writeln('}') if !is_embed_map_filter { @@ -2418,7 +2472,10 @@ fn (mut g Gen) gen_array_all(node ast.CallExpr) { if mut expr is ast.AnonFn { if expr.inherited_vars.len > 0 { closure_var = g.new_tmp_var() + prev_track_closure_tmp_arg_vars := g.track_lambda_autofree_tmp_arg_vars + g.track_lambda_autofree_tmp_arg_vars = false g.declare_closure_fn(mut expr, closure_var) + g.track_lambda_autofree_tmp_arg_vars = prev_track_closure_tmp_arg_vars } } @@ -2428,13 +2485,17 @@ fn (mut g Gen) gen_array_all(node ast.CallExpr) { g.empty_line = true g.set_current_pos_as_last_stmt_pos() mut is_embed_map_filter := false + lambda_autofree_tmp_arg_vars_start := g.lambda_autofree_tmp_arg_vars.len match mut expr { ast.AnonFn { g.write('if (!(') if expr.inherited_vars.len > 0 { g.write_closure_fn(mut expr, var_name, closure_var) } else { + prev_track_anon_fn_tmp_arg_vars := g.track_lambda_autofree_tmp_arg_vars + g.track_lambda_autofree_tmp_arg_vars = false g.gen_anon_fn_decl(mut expr) + g.track_lambda_autofree_tmp_arg_vars = prev_track_anon_fn_tmp_arg_vars g.write('${expr.decl.name}(${var_name})') } } @@ -2473,7 +2534,11 @@ fn (mut g Gen) gen_array_all(node ast.CallExpr) { } g.writeln2(')) {', '\t${past.tmp_var} = false;') - g.writeln2('\tbreak;', '}') + g.writeln('}') + g.write_lambda_autofree_tmp_arg_vars(lambda_autofree_tmp_arg_vars_start) + g.writeln('if (!${past.tmp_var}) {') + g.writeln('\tbreak;') + g.writeln('}') g.indent-- g.writeln('}') if !is_embed_map_filter { diff --git a/vlib/v/gen/c/cgen.v b/vlib/v/gen/c/cgen.v index 084bacfb5..f34aeb2a2 100644 --- a/vlib/v/gen/c/cgen.v +++ b/vlib/v/gen/c/cgen.v @@ -188,6 +188,8 @@ mut: inside_global_decl bool inside_interface_deref bool inside_assign_fn_var bool + inside_lambda_autofree_tmp bool + track_lambda_autofree_tmp_arg_vars bool outer_tmp_var string // tmp var from outer context (e.g. from stmts_with_tmp_var) to be used by nested if/match expressions last_tmp_call_var []string last_if_option_type ast.Type // stores the expected if type on nested if expr @@ -254,6 +256,7 @@ mut: sql_side SqlExprSide // left or right, to distinguish idents in `name == name` sql_last_stmt_out_len int strs_to_free0 []string // strings.Builder + lambda_autofree_tmp_arg_vars []string // strs_to_free []string // strings.Builder // tmp_arg_vars_to_free []string // autofree_pregen map[string]string @@ -3320,6 +3323,7 @@ fn is_noreturn_callexpr(expr ast.Expr) bool { // It returns true, if the last statement was a `return` or `branch` fn (mut g Gen) stmts_with_tmp_var(stmts []ast.Stmt, tmp_var string) bool { g.indent++ + lambda_autofree_tmp_arg_vars_start := g.lambda_autofree_tmp_arg_vars.len if g.inside_ternary > 0 { g.write('(') } @@ -3539,6 +3543,13 @@ fn (mut g Gen) stmts_with_tmp_var(stmts []ast.Stmt, tmp_var string) bool { g.autofree_scope_vars(stmt_pos.pos - 1, stmt_pos.line_nr, false) } } + // Branch-local lambda temp args are freed by the scope cleanup above. + // Keep the outer map cleanup list limited to temps declared in the map body scope. + if g.track_lambda_autofree_tmp_arg_vars && g.inside_ternary == 0 + && g.lambda_autofree_tmp_arg_vars.len > lambda_autofree_tmp_arg_vars_start { + g.lambda_autofree_tmp_arg_vars = + g.lambda_autofree_tmp_arg_vars[..lambda_autofree_tmp_arg_vars_start].clone() + } return last_stmt_was_return } @@ -6085,7 +6096,8 @@ fn (mut g Gen) expr(node_ ast.Expr) { } g.call_expr(node) if g.is_autofree && !g.is_builtin_mod && !g.is_js_call && g.strs_to_free0.len == 0 - && !g.is_autofree_tmp && !g.inside_lambda && g.inside_ternary == 0 { + && !g.is_autofree_tmp && (!g.inside_lambda || g.inside_lambda_autofree_tmp) + && g.inside_ternary == 0 { // if len != 0, that means we are handling call expr inside call expr (arg) // and it'll get messed up here, since it's handled recursively in autofree_call_pregen() // so just skip it diff --git a/vlib/v/gen/c/fn.v b/vlib/v/gen/c/fn.v index 3a3f2a726..2e15026d8 100644 --- a/vlib/v/gen/c/fn.v +++ b/vlib/v/gen/c/fn.v @@ -1963,9 +1963,18 @@ fn (mut g Gen) gen_anon_fn_decl(mut node ast.AnonFn) { g.anon_fn = node old_inside_return := g.inside_return old_inside_return_expr := g.inside_return_expr + old_inside_lambda := g.inside_lambda + old_inside_lambda_autofree_tmp := g.inside_lambda_autofree_tmp + old_track_lambda_autofree_tmp_arg_vars := g.track_lambda_autofree_tmp_arg_vars g.inside_return = false g.inside_return_expr = false + g.inside_lambda = false + g.inside_lambda_autofree_tmp = false + g.track_lambda_autofree_tmp_arg_vars = false g.fn_decl(decl) + g.track_lambda_autofree_tmp_arg_vars = old_track_lambda_autofree_tmp_arg_vars + g.inside_lambda_autofree_tmp = old_inside_lambda_autofree_tmp + g.inside_lambda = old_inside_lambda g.inside_return_expr = old_inside_return_expr g.inside_return = old_inside_return g.anon_fn = was_anon_fn @@ -5364,7 +5373,8 @@ fn (mut g Gen) method_call(node ast.CallExpr) { } } - if g.is_autofree && node.free_receiver && !g.inside_lambda && !g.is_builtin_mod { + if g.is_autofree && node.free_receiver && (!g.inside_lambda || g.inside_lambda_autofree_tmp) + && !g.is_builtin_mod { // The receiver expression needs to be freed, use the temp var. fn_name := node.name.replace('.', '_') arg_name := '_arg_expr_${fn_name}_0_${node.pos.pos}' @@ -6133,7 +6143,11 @@ fn (mut g Gen) autofree_call_pregen(node ast.CallExpr) { g.is_autofree = old_is_autofree g.is_autofree_tmp = false g.strs_to_free0 << tmp_arg_init - // This tmp arg var will be freed with the rest of the vars at the end of the scope. + if g.track_lambda_autofree_tmp_arg_vars { + g.lambda_autofree_tmp_arg_vars << t + } + // This tmp arg var will be freed with the rest of the vars at the end of the scope, + // or explicitly after an inline lambda expression that does not run scope cleanup. } } @@ -6558,7 +6572,8 @@ fn (mut g Gen) call_args(node ast.CallExpr) { // name := '_tt${g.tmp_count_af}_arg_expr_${fn_name}_${i}' name := '_arg_expr_${fn_name}_${i + 1}_${node.pos.pos}' scope := g.file.scope.innermost(node.pos.pos) - if !g.is_autofree_tmp || scope.known_var(name) { + if (!g.is_autofree_tmp && (!g.inside_lambda || g.inside_lambda_autofree_tmp)) + || scope.known_var(name) { tmp_arg := ast.CallArg{ typ: arg.typ is_mut: effective_arg.is_mut diff --git a/vlib/v/gen/c/infix.v b/vlib/v/gen/c/infix.v index c1fb2a4ac..26ddf9e4d 100644 --- a/vlib/v/gen/c/infix.v +++ b/vlib/v/gen/c/infix.v @@ -1709,8 +1709,69 @@ fn (mut g Gen) need_tmp_var_in_array_call(node ast.Expr) bool { return false } +fn (mut g Gen) expr_has_lambda_autofree_tmp_arg(expr ast.Expr) bool { + match expr { + ast.CallExpr { + if expr.free_receiver { + return true + } + if expr.args.any(it.typ == ast.string_type && it.is_tmp_autofree) { + return true + } + if g.expr_has_lambda_autofree_tmp_arg(expr.left) { + return true + } + for arg in expr.args { + if g.expr_has_lambda_autofree_tmp_arg(arg.expr) { + return true + } + } + } + ast.CastExpr { + return g.expr_has_lambda_autofree_tmp_arg(expr.expr) + } + ast.IfExpr { + for branch in expr.branches { + if g.expr_has_lambda_autofree_tmp_arg(branch.cond) { + return true + } + for stmt in branch.stmts { + if stmt is ast.ExprStmt && g.expr_has_lambda_autofree_tmp_arg(stmt.expr) { + return true + } + } + } + } + ast.IndexExpr { + return g.expr_has_lambda_autofree_tmp_arg(expr.left) + || g.expr_has_lambda_autofree_tmp_arg(expr.index) + } + ast.InfixExpr { + return g.expr_has_lambda_autofree_tmp_arg(expr.left) + || g.expr_has_lambda_autofree_tmp_arg(expr.right) + } + ast.ParExpr { + return g.expr_has_lambda_autofree_tmp_arg(expr.expr) + } + ast.PostfixExpr { + return g.expr_has_lambda_autofree_tmp_arg(expr.expr) + } + ast.PrefixExpr { + return g.expr_has_lambda_autofree_tmp_arg(expr.right) + } + ast.SelectorExpr { + return g.expr_has_lambda_autofree_tmp_arg(expr.expr) + } + else {} + } + + return false +} + // infix_expr_and_or_op generates code for `&&` and `||` fn (mut g Gen) infix_expr_and_or_op(node ast.InfixExpr) { + rhs_has_lambda_autofree_tmp_arg := g.inside_lambda_autofree_tmp + && g.expr_has_lambda_autofree_tmp_arg(node.right) if g.need_tmp_var_in_array_call(node.right) && g.inside_ternary == 0 { // `if a == 0 || arr.any(it.is_letter()) {...}` tmp := g.new_tmp_var() @@ -1728,7 +1789,8 @@ fn (mut g Gen) infix_expr_and_or_op(node ast.InfixExpr) { g.infix_left_var_name = if node.op == .and { tmp } else { '!${tmp}' } g.expr(node.right) g.infix_left_var_name = '' - } else if g.need_tmp_var_in_expr(node.right) && g.inside_ternary == 0 { + } else if (rhs_has_lambda_autofree_tmp_arg || g.need_tmp_var_in_expr(node.right)) + && g.inside_ternary == 0 { prev_inside_ternary := g.inside_ternary g.inside_ternary = 0 tmp := g.new_tmp_var() @@ -1750,9 +1812,13 @@ fn (mut g Gen) infix_expr_and_or_op(node ast.InfixExpr) { g.indent++ g.set_current_pos_as_last_stmt_pos() g.infix_left_var_name = '' + lambda_autofree_tmp_arg_vars_start := g.lambda_autofree_tmp_arg_vars.len g.write('${tmp} = ') g.expr(node.right) g.writeln(';') + if rhs_has_lambda_autofree_tmp_arg { + g.write_lambda_autofree_tmp_arg_vars(lambda_autofree_tmp_arg_vars_start) + } g.indent-- g.writeln('}') g.set_current_pos_as_last_stmt_pos() diff --git a/vlib/v/gen/c/testdata/autofree_map_if_expr.c.must_have b/vlib/v/gen/c/testdata/autofree_map_if_expr.c.must_have new file mode 100644 index 000000000..5d0fa751f --- /dev/null +++ b/vlib/v/gen/c/testdata/autofree_map_if_expr.c.must_have @@ -0,0 +1,12 @@ +string _arg_expr_ends_with_1_ += builtin__string__plus(suffix, _S("\"")); +if (builtin__string_ends_with(it, /*autofree arg*/_arg_expr_ends_with_1_ +builtin__string_free(&_arg_expr_ends_with_1_ +string _arg_expr_main_accepts_1_ +builtin__string_free(&_arg_expr_main_accepts_1_ +string _arg_expr_starts_with_0_ +builtin__string_free(&_arg_expr_starts_with_0_ +string _arg_expr_main_mark_1_ += builtin__string__plus(it, _S("x")); +main__mark(/*autofree arg*/_arg_expr_main_mark_1_ +builtin__string_free(&_arg_expr_main_mark_1_ diff --git a/vlib/v/gen/c/testdata/autofree_map_if_expr.out b/vlib/v/gen/c/testdata/autofree_map_if_expr.out new file mode 100644 index 000000000..a971ea165 --- /dev/null +++ b/vlib/v/gen/c/testdata/autofree_map_if_expr.out @@ -0,0 +1,13 @@ +['"/tmp/libgc.dylib"', 'other'] +['ax!', 'b'] +['ax!', 'b'] +['a!'] +['a!!', 'b!!'] +['hit', 'miss'] +[1, 0] +[1, 0] +[true, false] +['a'] +['a!'] +['a'] +['a!'] diff --git a/vlib/v/gen/c/testdata/autofree_map_if_expr.vv b/vlib/v/gen/c/testdata/autofree_map_if_expr.vv new file mode 100644 index 000000000..59d0cf4ac --- /dev/null +++ b/vlib/v/gen/c/testdata/autofree_map_if_expr.vv @@ -0,0 +1,90 @@ +// vtest vflags: -autofree + +fn mark(s string) string { + return s + '!' +} + +fn accepts(s string) bool { + return s.len > 0 +} + +fn side_effect() string { + println('side effect called') + return 'x' +} + +fn main() { + suffix := 'tcc/lib/libgc.dylib' + flags := ['"thirdparty/tcc/lib/libgc.dylib"', 'other'].map(if it.ends_with(suffix + '"') + && it.starts_with('"') { + '"/tmp/libgc.dylib"' + } else { + it + }) + println(flags) + branch_flags := ['a', 'b'].map(if it == 'a' { + mark(it + 'x') + } else { + it + }) + println(branch_flags) + anon_flags := ['ax', 'b'].map(fn (it string) string { + suffix := 'x' + if it.ends_with(suffix + '') { + return mark(it) + } + return it + }) + println(anon_flags) + anon_short_flags := ['a'].map(fn (it string) string { + if true && accepts(it + '') { + return mark(it) + } + return it + }) + println(anon_short_flags) + nested_suffix := '!' + nested_flags := [['a'], ['b']].map(it.map(fn [nested_suffix] (s string) string { + return mark(s + nested_suffix) + })[0]) + println(nested_flags) + needle := 'x' + nested_any_flags := [['ax'], ['b']].map(if it.any(it.ends_with(needle + '')) { + 'hit' + } else { + 'miss' + }) + println(nested_any_flags) + nested_filter_counts := [['ax'], ['b']].map(it.filter(it.ends_with(needle + '')).len) + println(nested_filter_counts) + nested_counts := [['ax'], ['b']].map(it.count(it.ends_with(needle + ''))) + println(nested_counts) + nested_all_flags := [['ax'], ['b']].map(it.all(it.ends_with(needle + ''))) + println(nested_all_flags) + short_flags := ['a'].map(if false && accepts(side_effect() + '') { + 'bad' + } else { + it + }) + println(short_flags) + evaluated_short_flags := ['a'].map(if true && accepts(it + '') { + mark(it) + } else { + it + }) + println(evaluated_short_flags) + receiver_short_flags := ['a'].map(if false && (side_effect() + '').starts_with('x') { + 'bad' + } else { + it + }) + println(receiver_short_flags) + evaluated_receiver_flags := ['a'].map(if true && (it + '').starts_with('a') { + mark(it) + } else { + it + }) + println(evaluated_receiver_flags) + // This regression only needs the generated C to compile. + exit(0) +} -- 2.39.5