diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index a2ab7d3131..40fb1b6f7c 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -68,6 +68,7 @@ mut: cur_orm_ts table.TypeSymbol error_details []string for_in_mut_val_name string + fn_mut_arg_names []string vmod_file_content string // needed for @VMOD_FILE, contents of the file, *NOT its path** vweb_gen_types []table.Type // vweb route checks prevent_sum_type_unwrapping_once bool // needed for assign new values to sum type, stopping unwrapping then @@ -2579,7 +2580,7 @@ pub fn (mut c Checker) assign_stmt(mut assign_stmt ast.AssignStmt) { assign_stmt.pos) } left_is_ptr := left_type.is_ptr() || left_sym.is_pointer() - if left_is_ptr && c.for_in_mut_val_name != left.str() { + if left_is_ptr && c.for_in_mut_val_name != left.str() && left.str() !in c.fn_mut_arg_names { if !c.inside_unsafe && assign_stmt.op !in [.assign, .decl_assign] { // ptr op= c.warn('pointer arithmetic is only allowed in `unsafe` blocks', assign_stmt.pos) @@ -5512,8 +5513,17 @@ fn (mut c Checker) fn_decl(mut node ast.FnDecl) { } } } + for param in node.params { + if param.is_mut { + c.fn_mut_arg_names << param.name + } + } c.fn_scope = node.scope c.stmts(node.stmts) + + if c.fn_mut_arg_names.len > 0 { + c.fn_mut_arg_names.clear() + } returns := c.returns || has_top_return(node.stmts) if node.language == .v && !node.no_body && node.return_type != table.void_type && !returns && node.name !in ['panic', 'exit'] { diff --git a/vlib/v/gen/cgen.v b/vlib/v/gen/cgen.v index a891a2d014..08bb999dbb 100644 --- a/vlib/v/gen/cgen.v +++ b/vlib/v/gen/cgen.v @@ -118,6 +118,7 @@ mut: // strs_to_free []string // strings.Builder inside_call bool for_in_mut_val_name string + fn_mut_arg_names []string has_main bool inside_const bool comp_for_method string // $for method in T.methods {} @@ -1990,7 +1991,7 @@ fn (mut g Gen) gen_assign_stmt(assign_stmt ast.AssignStmt) { } else { g.out.go_back_to(pos) is_var_mut := !is_decl && left is ast.Ident - && g.for_in_mut_val_name == (left as ast.Ident).name + && (g.for_in_mut_val_name == (left as ast.Ident).name || (left as ast.Ident).name in g.fn_mut_arg_names) addr := if is_var_mut { '' } else { '&' } g.writeln('') g.write('memcpy($addr') @@ -2061,8 +2062,8 @@ fn (mut g Gen) gen_assign_stmt(assign_stmt ast.AssignStmt) { g.prevent_sum_type_unwrapping_once = true } if !is_fixed_array_copy || is_decl { - if !is_decl && left is ast.Ident - && g.for_in_mut_val_name == (left as ast.Ident).name { + if !is_decl && var_type != table.string_type_idx && left is ast.Ident + && (g.for_in_mut_val_name == (left as ast.Ident).name || (left as ast.Ident).name in g.fn_mut_arg_names) { g.write('*') } g.expr(left) diff --git a/vlib/v/gen/fn.v b/vlib/v/gen/fn.v index 3428bc7a9e..57508145c8 100644 --- a/vlib/v/gen/fn.v +++ b/vlib/v/gen/fn.v @@ -126,6 +126,11 @@ fn (mut g Gen) gen_fn_decl(it ast.FnDecl, skip bool) { g.definitions.write(fn_header) g.write(fn_header) } + for param in it.params { + if param.is_mut { + g.fn_mut_arg_names << param.name + } + } arg_start_pos := g.out.len fargs, fargtypes := g.fn_args(it.params, it.is_variadic) arg_str := g.out.after(arg_start_pos) @@ -170,7 +175,11 @@ fn (mut g Gen) gen_fn_decl(it ast.FnDecl, skip bool) { prev_defer_stmts := g.defer_stmts g.defer_stmts = [] g.stmts(it.stmts) - // + // clear g.fn_mut_arg_names + if g.fn_mut_arg_names.len > 0 { + g.fn_mut_arg_names.clear() + } + if it.return_type == table.void_type { g.write_defer_stmts_when_needed() } @@ -185,7 +194,7 @@ fn (mut g Gen) gen_fn_decl(it ast.FnDecl, skip bool) { if default_expr == '{0}' { if it.return_type.idx() == 1 && it.return_type.has_flag(.optional) { // The default return for anonymous functions that return `?, - // should have .ok = true set, otherwise calling them with + // should have .ok = true set, otherwise calling them with // optfn() or { panic(err) } will cause a panic: g.writeln('\treturn (Option_void){.ok = true};') } else { diff --git a/vlib/v/gen/str.v b/vlib/v/gen/str.v index 08b30b66db..d6b918b411 100644 --- a/vlib/v/gen/str.v +++ b/vlib/v/gen/str.v @@ -343,14 +343,16 @@ fn (mut g Gen) gen_expr_to_string(expr ast.Expr, etype table.Type) { } else if sym_has_str_method || sym.kind in [.array, .array_fixed, .map, .struct_, .multi_return, .sum_type, .interface_] { is_ptr := typ.is_ptr() + is_var_mut := expr is ast.Ident + && ((expr as ast.Ident).name == g.for_in_mut_val_name || (expr as ast.Ident).name in g.fn_mut_arg_names) str_fn_name := g.gen_str_for_type(typ) - if is_ptr { + if is_ptr && !is_var_mut { g.write('_STR("&%.*s\\000", 2, ') } g.write('${str_fn_name}(') if str_method_expects_ptr && !is_ptr { g.write('&') - } else if !str_method_expects_ptr && is_ptr { + } else if (!str_method_expects_ptr && is_ptr) || is_var_mut { g.write('*') } if expr is ast.ArrayInit { @@ -361,7 +363,7 @@ fn (mut g Gen) gen_expr_to_string(expr ast.Expr, etype table.Type) { } g.expr(expr) g.write(')') - if is_ptr { + if is_ptr && !is_var_mut { g.write(')') } } else { diff --git a/vlib/v/tests/fn_mut_args_test.v b/vlib/v/tests/fn_mut_args_test.v new file mode 100644 index 0000000000..d6dfa7ddf9 --- /dev/null +++ b/vlib/v/tests/fn_mut_args_test.v @@ -0,0 +1,23 @@ +fn func(mut a []int) { + a = [1, 2, 3, 4] + println('inside fn: $a') + assert '$a' == '[1, 2, 3, 4]' +} + +fn test_fn_mut_args_of_array() { + mut a := [1, 2, 3] + func(mut a) + println('inside main: $a') + assert '$a' == '[1, 2, 3, 4]' +} + +fn init_map(mut n map[string]int) { + n = {'one': 1} +} + +fn test_fn_mut_args_of_map() { + mut m := map[string]int{} + init_map(mut m) + println(m) + assert m == {'one': 1} +}