Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
12 changes: 10 additions & 2 deletions vlib/v/gen/c/struct.v
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Comment on lines +788 to +790

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Keep cloning for owning struct initializations

This new is_fn_arg fast path skips cloning for all array function arguments in struct literals, which changes ownership semantics beyond arrays.reverse_iterator. In -autofree, caller locals are still freed at scope end while generated struct free methods also free array fields, so code like fn wrap(a []int) S { return S{a:a} } with arr := [1]; s := wrap(arr) can now free the same backing buffer twice when both arr and s are cleaned up. The bypass should be limited to explicitly non-owning structs/fields instead of globally disabling gen_clone_assignment for arg-backed arrays.

Useful? React with 👍 / 👎.

if g.gen_clone_assignment(sfield.expected_type, sfield.expr, sfield.typ, false) {
cloned = true
}
}
}
if !cloned {
Expand Down
16 changes: 16 additions & 0 deletions vlib/v/tests/autofree_arrays_reverse_iter_test.v
Original file line number Diff line number Diff line change
@@ -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]
}
Loading