ziglang / zig

General-purpose programming language and toolchain for maintaining robust, optimal, and reusable software.
https://ziglang.org
MIT License
34.6k stars 2.53k forks source link

`ArrayList(u0).appendSlice(list, &.{0, 0, 0})` `panic: @memcpy arguments alias` #21655

Open Des-Nerger opened 2 weeks ago

Des-Nerger commented 2 weeks ago

Zig Version

0.14.0-dev.1765+73de620ad

Steps to Reproduce and Observed Behavior

--- lib/std/array_list.zig.orig 2024-10-06 16:53:25.000000000 +1000
+++ lib/std/array_list.zig  2024-10-10 18:40:01.106947099 +1000
@@ -2170,17 +2170,15 @@
 test "ArrayList(u0)" {
     // An ArrayList on zero-sized types should not need to allocate
     const a = testing.failing_allocator;

     var list = ArrayList(u0).init(a);
     defer list.deinit();

-    try list.append(0);
-    try list.append(0);
-    try list.append(0);
+    try list.appendSlice(&.{0, 0, 0});
     try testing.expectEqual(list.items.len, 3);

     var count: usize = 0;
     for (list.items) |x| {
         try testing.expectEqual(x, 0);
         count += 1;
     }
$ zig test --test-filter 'array_list.test.ArrayList(u0)' lib/std/std.zig 
thread 35184 panic: @memcpy arguments alias
lib/std/array_list.zig:317:42: 0x11816b2 in appendSliceAssumeCapacity (test)
            @memcpy(self.items[old_len..][0..items.len], items);
                                         ^
lib/std/array_list.zig:306:43: 0x118143f in appendSlice (test)
            self.appendSliceAssumeCapacity(items);
                                          ^
lib/std/array_list.zig:2177:25: 0x11809d4 in test.ArrayList(u0) (test)
    try list.appendSlice(&.{0, 0, 0});
                        ^

Expected Behavior

I think slices of zero-sized types shouldn't ever be considered aliased, even if they start from the exact same address.

JonathanHallstrom commented 2 weeks ago

Maybe if the bit size of element type of memcpy is zero then memcpy should be a noop?

Small repro: https://godbolt.org/z/K6f3sE9z6

Darkfllame commented 2 weeks ago

well, if you think about it, is technically correct since, in the allocator interface you can see that if you try to allocate 0 bytes: it just returns the maximum address possible with backwarf alignement. i.e: u0 has an alignement of 1 and thus returns the address of std.math.maxInt(usize), and when the arraylist tries to allocate a new slice for the list it'll try to allocate 0*n bytes, thus 0 bytes and it will return the same value specified above.

Now yes this shouldn't be a bug and @memcpy should just be a no-op on 0 bytes types. Or change the ArrayList type to just change the 'len' field of the internal slice on 0-sized types.