diff --git a/vlib/v/gen/c/struct.v b/vlib/v/gen/c/struct.v index 68a367fa1fd075..95f5825308f581 100644 --- a/vlib/v/gen/c/struct.v +++ b/vlib/v/gen/c/struct.v @@ -781,8 +781,16 @@ fn (mut g Gen) struct_init_field(sfield ast.StructInitField, language ast.Langua field_type_sym := g.table.sym(sfield.typ) mut cloned := false if g.is_autofree && !sfield.typ.is_ptr() && field_type_sym.kind in [.array, .string] { - if g.gen_clone_assignment(sfield.expected_type, sfield.expr, sfield.typ, false) { - cloned = true + // array fn args should not be cloned into struct fields: the caller owns and + // frees the underlying data, so cloning would break mutation aliasing + // (e.g. `for mut x in iter`) and leak the clone. strings are still cloned + // since they may be freed before the struct field is used. + is_fn_arg := field_type_sym.kind == .array && sfield.expr is ast.Ident + && sfield.expr.obj is ast.Var && (sfield.expr.obj as ast.Var).is_arg + if !is_fn_arg { + if g.gen_clone_assignment(sfield.expected_type, sfield.expr, sfield.typ, false) { + cloned = true + } } } if !cloned { diff --git a/vlib/v/tests/autofree_arrays_reverse_iter_test.v b/vlib/v/tests/autofree_arrays_reverse_iter_test.v new file mode 100644 index 00000000000000..4b32f37df3e455 --- /dev/null +++ b/vlib/v/tests/autofree_arrays_reverse_iter_test.v @@ -0,0 +1,16 @@ +// vtest vflags: -autofree +// vtest build: !sanitize-address-gcc && !sanitize-address-clang +import arrays + +fn test_arrays_fns_arg() { + mut original := [10, 20] + for mut x in arrays.reverse_iterator(original) { + (*x)++ + } + assert original == [11, 21] + + for mut x in original { + (*x)++ + } + assert original == [12, 22] +}