std.ArrayList[T] failure in 0.13.0

i’m receiving the following error in 0.13.0, which didn’t occur in 0.12.0:

Segmentation fault at address 0xa5cab8
C:\tools\zig-windows-x86_64-0.13.0\lib\std\array_list.zig:434:51: 0xa24032 in ensureTotalCapacity (host-main.exe.obj)
            return self.ensureTotalCapacityPrecise(better_capacity);
                                                  ^
C:\tools\zig-windows-x86_64-0.13.0\lib\std\array_list.zig:483:41: 0xa151f3 in addOne (host-main.exe.obj)
            try self.ensureTotalCapacity(newlen);
                                        ^
C:\tools\zig-windows-x86_64-0.13.0\lib\std\array_list.zig:262:49: 0xa07db4 in append (host-main.exe.obj)
            const new_item_ptr = try self.addOne();
                                                ^
C:\Users\biosb\zig\zig-em-dev\work\em.core\em.lang\em.zig:372:30: 0x9f9912 in add (host-main.exe.obj)
            self._list.append(item) catch fail();

self._list is of type std.ArrayList([]const u8) and this is the very first call to the append method…

the ArrayList uses an ArenaAllocator created in the simplest possible way…

What code causes the error?

I use std.ArrayList(const u8) a lot, for several versions and even with the master and I have no bug
there was no modification…
upload your code please

here’s a little more context, showing just one field and method from a more complex struct:

        _list: std.ArrayList(T) = std.ArrayList(T).init(arena.allocator()),

        pub fn add(self: *Self, item: T) void {
            std.log.debug("capacity = {d}", .{self._list.items.len});
            self._list.ensureTotalCapacityPrecise(1) catch fail();
            std.log.debug("capacity = {d}", .{self._list.items.len});

and here’s the output:

debug: capacity = 0
Segmentation fault at address 0xfedab8
C:\Users\biosb\zig\zig-em-dev\work\em.core\em.lang\em.zig:373:50: 0xf8993c in add (host-main.exe.obj)
            self._list.ensureTotalCapacityPrecise(1) catch fail();
                                                 ^
C:\Users\biosb\zig\zig-em-dev\work\em.arch\em.arch.arm\IntrVec.em.zig:23:21: 0xf45626 in addIntrH (host-main.exe.obj)
        name_tab.add(name);
                    ^
C:\Users\biosb\zig\zig-em-dev\work\em.arch\em.arch.arm\IntrVec.em.zig:48:21: 0xf4349c in em__initH (host-main.exe.obj)
            addIntrH(n);
                    ^

this is the FIRST call to my add method (as i’ve verified by other means)… as you can see, the actually length of the underlying array is 0… when i EXPLICITLY try to increase capacity to 1, i receive the same fault as shown in my OP…

FWIW, i’ve tried this with different allocators – same problem… needless to say, i can create a “simple” standalone example that works…

it seems like there is interaction with something else in my program – which again worked just find under 0.12.0 ???

is there something i can do get more precise info about the fault???

How are you initialing this struct?
Having a default value with the ArrayList(u8).init() like that doesn’t always work. It looks like you might be getting an uninitialized ArrayList. If your standalone examples work, then I think it’s how you are initializing.
The other thing I’ve run into with 0.13.0 is that sometimes the error messages pointing to the wrong code spot. You might try changing briefly to 0.14.0-dev and see if it changes the message.

2 Likes

sounds like you’re on the right track… the struct containing _list is part of ANOTHER struct which is initialized with std.mem.zeroInit… (yes, i’ve heard this isn’t exactly a best practice!!!)

as a practical matter, i really do want my fields to be “zero”; and i have such a strange mixture of “runtime” structs inside of “comptime structs” (or is it the other way around??) that i can clearly understand why i’m skating on thin ice…

there’s no problem for me to add an additional sentinel field which i’ll check before using ._list and then lazily initialize on first use…

or should i follow a discipline where each of these interior structs has their own init method in lieu of my using std.mem.zeroInit

I would recommend having an init method. That’s the more idiomatic way of doing things, and then you are less likely to have this issue. Especially if you are utilizing std structs that require an allocator.

3 Likes

and to be clear, is this an init(self: *Self) method applied to a struct instance or is this an init() method returning a newly initialized instance???

There are two ways to do it:

Have an method that returns an instance.

pub fn init(allocator: std.mem.Allocator) Self {
    return .{
         ._list = std.ArrayList(u8).init(allocator),
    };
}

or to have it initilize an existing variable:

pub fn init(self: *Self, allocator: std.mem.Allocator) void {
    self._list = std.ArrayList(u8).init(allocator);
}

And they are use respectively:

var thing = Struct.init(allocator);

// vs
var thing: Struct = undefined;
thing.init(allocator);

In general I try to use the 1st pattern. As long as I don’t have any crazy pointers to handle or internal structures that need a place to live before initialization, the first pattern makes usage cleaner. That being said, I have run into a few cases where I need internal pointer references that have required the 2nd pattern. Based on the little bit of code you have shown, I think you should be able to use the 1st pattern just fine.

4 Likes

I usually call the 2nd variant setup.

my use-case is a little strange, in that i have user-supplied top-level struct whose fields are themselves struct types which i’ve implemented…

const C = struct {
    tabA: Table(u32),
    tabB: Table([]const u8),
        ...
};

in this case, the type returned by Table uses a std.ArrayList in its implementation…

while i could surely declare C with explicit initializers:

const C = struct {
    tabA: Table(u32) = Table(u32).init(),
    tabB: Table([]const u8) = Table([]const u8).init(),
        ...
};

i’d prefer not to “burden” my newbie user with the extra noise; and since the user simply provides the type C, my framework will actually allocate/initialize an instance…

assuming my init() method (1st variant) is comptime-callable (and relies only on similar methods like std.ArrayList.init), i’m not sure how to exactly pull this off through type reflection…

i can certainly discover tabA and tabB; and i can determine that the types of these field support an init method… what i can’t wrap my head around is the actual creation of the top-level C instance…

@AndrewCodeDev – is this a case where i need to “reify” an instance of C, while doing a @call(auto, fld, "init") as needed???

@Southporter – some quick experiments using the initialization techniques you suggested did NOT solve the problem…

here is a (runtime) print of my struct containing the std.ArrayList under 0.13.0, which looks no different from what i’ll see under 0.12.0:

debug: self = em.core.em.lang.em.Table_H([]const u8,.RO){ ._dname = {  }, ._is_virgin = true, ._list = array_list.ArrayListAligned([]const u8,null){ .items = {  }, .capacity = 0, .allocator = mem.Allocator{ .ptr = anyopaque@44d030, .vtable = mem.Allocator.VTable{ ... } } } }
debug: add { 78, 77, 73 }

what would an “incorrectly initialized” std.ArrayList look like???

That looks like its being initialized right to me. The fact that you can print it out is a good sign, unless the allocator ptr is off. Is it giving the same stacktrace error?
Can you also try with 0.14.0-dev? I’ve had some issues with stacktraces being wrong on 0.13.0.
Also, if you could give a bit more context on the code and how it is being setup, that would help a lot. Otherwise we are just reaching in the dark.

1 Like

Reification may cause the interface to be easier to follow… “give me the field names and the types and I’ll give you a struct” kind of deal. In contrast, being handed a user defined struct and then having to pick it apart for all the right pieces sounds ugly. Not impossible, but annoying.

I’m sure as you know the issue with the @call(x, y, z) is that you may have init functions that have different arity.

And I’d say that a mangled ArrayList is something you’d expect to see if you allocated the memory for one before setting the fields. It would contain garbage or have an invalid pointer address.

1 Like

same results on 0.14.0-dev… display of the initialized struct followed by a failure upon adding the first item…

debug: self = em.core.em.lang.em.Table_H([]const u8,.RO){ ._dname = {  }, ._is_virgin = true, ._list = array_list.ArrayListAligned([]const u8,null){ .items = {  }, .capacity = 0, .allocator = mem.Allocator{ .ptr = anyopaque@b7e028, .vtable = mem.Allocator.VTable{ ... } } } }
debug: add { 78, 77, 73 }
Segmentation fault at address 0xb33618
C:\tools\zig-windows-x86_64-0.14.0-dev.367+a57479afc\lib\std\array_list.zig:434:51: 0xaf5352 in ensureTotalCapacity (host-main.exe.obj)
            return self.ensureTotalCapacityPrecise(better_capacity);
                                                  ^
C:\tools\zig-windows-x86_64-0.14.0-dev.367+a57479afc\lib\std\array_list.zig:483:41: 0xae5bc3 in addOne (host-main.exe.obj)
            try self.ensureTotalCapacity(newlen);
                                        ^
C:\tools\zig-windows-x86_64-0.14.0-dev.367+a57479afc\lib\std\array_list.zig:262:49: 0xad8494 in append (host-main.exe.obj)
            const new_item_ptr = try self.addOne();
                                                ^
C:\Users\biosb\zig\zig-em-dev\work\em.core\em.lang\em.zig:391:30: 0xac9ba0 in add (host-main.exe.obj)
            self._list.append(item) catch fail();
                             ^
C:\Users\biosb\zig\zig-em-dev\work\em.arch\em.arch.arm\IntrVec.em.zig:23:21: 0xa854a6 in addIntrH (host-main.exe.obj)
        name_tab.add(name);
                    ^
C:\Users\biosb\zig\zig-em-dev\work\em.arch\em.arch.arm\IntrVec.em.zig:48:21: 0xa8348c in em__initH (host-main.exe.obj)
            addIntrH(n);
                    ^
C:\Users\biosb\zig\zig-em-dev\work\em.core\em.lang\host-main.zig:22:12: 0xa832af in exec__anon_3104 (host-main.exe.obj)
    callAll("em__initH", ulist_bot, false);
           ^
C:\Users\biosb\zig\zig-em-dev\work\.gen\host.zig:4:53: 0xa61177 in exec (host-main.exe.obj)
    @import("../em.core/em.lang/host-main.zig").exec(em.import.@"em.examples.basic/EmptyP".em__U) catch em.fail();
                                                    ^
C:\Users\biosb\zig\zig-em-dev\work\.main-host.zig:1:51: 0xa6106e in main (host-main.exe.obj)
pub fn main() void { @import(".gen/host.zig").exec(); }
                                                  ^
C:\tools\zig-windows-x86_64-0.14.0-
dev.367+a57479afc\lib\std\start.zig:373:53: 0xa61017 in WinStartup (host-main.exe.obj)
    std.os.windows.ntdll.RtlExitUserProcess(callMain());
                                                    ^
???:?:?: 0x7ff84da67373 in ??? (KERNEL32.DLL)
???:?:?: 0x7ff84f95cc90 in ??? (ntdll.dll)

i’ve included the full stack trace back to main, which reveals some layers of a “framework” i’m building…

when running, the framework calls the em__initH method in each application module… in this case, the IntrVec module is being invoked…

inside IntrVec.em__initH, this module is adding a list of (interrupt) names to a table… the table itself is declared as part of this module’s “configuration” struct defined as follows:

pub const EM__CONFIG = struct {
    name_tab: em.Table([]const u8, .RO),
    used_tab: em.Table([]const u8, .RO),
};

a single instance of this EM__CONFIG struct is created through some (comptime) boilerplate code at the top of each module…

the generic framework type em.Table(T) returns a struct containing a ._list field of type std.ArrayList(T); as mentioned in an earlier reply, the config struct instance is initialize using std.mem.zeroInit


hopefully that provides a little more context…

question: if i wanted to add some adhoc debug code to std.ArrayList, should i simply “hack” the copy in my downloaded install bundle??? what’s the easiest way to do such as one-off???

more information… i added some std.log.debug calls inside of ArrayList.ensureTotalCapacityPrecise which suggest the fault might be occurring on the return from this function – and not from within…

first, the baseline stacktrace without the debug calls:

debug: self = em.core.em.lang.em.Table_H([]const u8,.RO){ ._dname = {  }, ._is_virgin = true, ._list = array_list.ArrayListAligned([]const u8,null){ .items = {  }, .capacity = 0, .allocator = mem.Allocator{ .ptr = anyopaque@57e028, .vtable = mem.Allocator.VTable{ ... } } } }
debug: add { 78, 77, 73 }
Segmentation fault at address 0x533618
C:\tools\zig-windows-x86_64-0.14.0-dev.367+a57479afc\lib\std\array_list.zig:434:51: 0x4f5352 in ensureTotalCapacity (host-main.exe.obj)
            return self.ensureTotalCapacityPrecise(better_capacity);
                                                  ^
C:\tools\zig-windows-x86_64-0.14.0-dev.367+a57479afc\lib\std\array_list.zig:491:41: 0x4e5bc3 in addOne (host-main.exe.obj)
            try self.ensureTotalCapacity(newlen);
                                        ^
C:\tools\zig-windows-x86_64-0.14.0-dev.367+a57479afc\lib\std\array_list.zig:262:49: 0x4d8494 in append (host-main.exe.obj)
            const new_item_ptr = try self.addOne();
                                                ^
C:\Users\biosb\zig\zig-em-dev\work\em.core\em.lang\em.zig:391:30: 0x4c9ba0 in add (host-main.exe.obj)
            self._list.append(item) catch fail();
                             ^
C:\Users\biosb\zig\zig-em-dev\work\em.arch\em.arch.arm\IntrVec.em.zig:23:21: 0x4854a6 in addIntrH (host-main.exe.obj)
        name_tab.add(name);
                    ^
C:\Users\biosb\zig\zig-em-dev\work\em.arch\em.arch.arm\IntrVec.em.zig:48:21: 0x48348c in em__initH (host-main.exe.obj)
            addIntrH(n);
                    ^
C:\Users\biosb\zig\zig-em-dev\work\em.core\em.lang\host-main.zig:22:12: 0x4832af in exec__anon_3104 (host-main.exe.obj)
    callAll("em__initH", ulist_bot, false);
           ^
C:\Users\biosb\zig\zig-em-dev\work\.gen\host.zig:4:53: 0x461177 in exec (host-main.exe.obj)
    @import("../em.core/em.lang/host-main.zig").exec(em.import.@"em.examples.basic/EmptyP".em__U) catch em.fail();
                                                    ^
C:\Users\biosb\zig\zig-em-dev\work\.main-host.zig:1:51: 0x46106e in main (host-main.exe.obj)
pub fn main() void { @import(".gen/host.zig").exec(); }
                                                  ^
C:\tools\zig-windows-x86_64-0.14.0-dev.367+a57479afc\lib\std\start.zig:373:53: 0x461017 in WinStartup (host-main.exe.obj)
    std.os.windows.ntdll.RtlExitUserProcess(callMain());
                                                    ^
???:?:?: 0x7ff84da67373 in ??? (KERNEL32.DLL)
???:?:?: 0x7ff84f95cc90 in ??? (ntdll.dll)

and here’s some debug added to array_list.zig beginning at line 440:

        /// If the current capacity is less than `new_capacity`, this function will
        /// modify the array so that it can hold at least `new_capacity` items.
        /// Invalidates element pointers if additional memory is needed.
        pub fn ensureTotalCapacity(self: *Self, new_capacity: usize) Allocator.Error!void {
            if (@sizeOf(T) == 0) {
                self.capacity = math.maxInt(usize);
                return;
            }

            if (self.capacity >= new_capacity) return;

            const better_capacity = growCapacity(self.capacity, new_capacity);
            return self.ensureTotalCapacityPrecise(better_capacity);
        }

        /// If the current capacity is less than `new_capacity`, this function will
        /// modify the array so that it can hold exactly `new_capacity` items.
        /// Invalidates element pointers if additional memory is needed.
        pub fn ensureTotalCapacityPrecise(self: *Self, new_capacity: usize) Allocator.Error!void {
            if (@sizeOf(T) == 0) {
                self.capacity = math.maxInt(usize);
                return;
            }

            if (self.capacity >= new_capacity) return;

            std.log.debug("self.capacity = {d}, new_capacity = {}", .{self.capacity, new_capacity});

            // Here we avoid copying allocated but unused bytes by
            // attempting a resize in place, and falling back to allocating
            // a new buffer and doing our own copy. With a realloc() call,
            // the allocator implementation would pointlessly copy our
            // extra capacity.
            const old_memory = self.allocatedSlice();
            std.log.debug("old_memory = {d} {any}", .{old_memory.len, old_memory.ptr});
            std.log.debug("resize = {any}", .{self.allocator.resize(old_memory, new_capacity)});
            if (self.allocator.resize(old_memory, new_capacity)) {
                self.capacity = new_capacity;
            } else {
                const new_memory = try self.allocator.alignedAlloc(T, alignment, new_capacity);
                std.log.debug("new_memory = {d} {any}", .{new_memory.len, new_memory.ptr});
                @memcpy(new_memory[0..self.items.len], self.items);
                std.log.debug("after memcpy", .{});
                self.allocator.free(old_memory);
                std.log.debug("after free", .{});
                self.items.ptr = new_memory.ptr;
                self.capacity = new_memory.len;
                std.process.exit(0);
           }
        }

if i now run the same test, the debug output looks completely sane; and the program gracefully exits after the first call to this code:

debug: self = em.core.em.lang.em.Table_H([]const u8,.RO){ ._dname = {  }, ._is_virgin = true, ._list = array_list.ArrayListAligned([]const u8,null){ .items = {  }, .capacity = 0, .allocator = mem.Allocator{ .ptr = anyopaque@2fa028, .vtable = mem.Allocator.VTable{ ... } } } }
debug: add { 78, 77, 73 }
debug: self.capacity = 0, new_capacity = 8
debug: old_memory = 0 []const u8@aaaaaaaaaaaaaaaa
debug: resize = false
debug: new_memory = 8 []const u8@1e2c8760010
debug: after memcpy
debug: after free

if a remove the std.process.exit, however, i do see the original fault – with looks like MANY additional calls to the same code above:

debug: self = em.core.em.lang.em.Table_H([]const u8,.RO){ ._dname = {  }, ._is_virgin = true, ._list = array_list.ArrayListAligned([]const u8,null){ .items = {  }, .capacity = 0, .allocator = mem.Allocator{ .ptr = anyopaque@dda028, .vtable = mem.Allocator.VTable{ ... } } } }
debug: add { 78, 77, 73 }
debug: self.capacity = 0, new_capacity = 8
debug: old_memory = 0 []const u8@aaaaaaaaaaaaaaaa
debug: resize = false
debug: new_memory = 8 []const u8@18a38cf0010
debug: after memcpy
debug: after free
Segmentation fault at address 0xd8c618
debug: self.capacity = 0, new_capacity = 8
debug: old_memory = 0 u32@aaaaaaaaaaaaaaaa
debug: resize = false
debug: new_memory = 8 u32@18a38d10584
debug: after memcpy
debug: after free
debug: self.capacity = 0, new_capacity = 8
debug: old_memory = 0 u8@aaaaaaaaaaaaaaaa
debug: resize = false
debug: new_memory = 8 u8@18a38d10584
debug: after memcpy
debug: after free
debug: self.capacity = 8, new_capacity = 20
debug: old_memory = 8 u8@18a38d10584
debug: resize = true
debug: new_memory = 20 u8@18a38d10598
debug: after memcpy
debug: after free
debug: self.capacity = 20, new_capacity = 38
debug: old_memory = 20 u8@18a38d10598
debug: resize = true
debug: new_memory = 38 u8@18a38d105be
debug: after memcpy
debug: after free
debug: self.capacity = 38, new_capacity = 65
debug: old_memory = 38 u8@18a38d105be
debug: resize = true
debug: new_memory = 65 u8@18a38d105ff
debug: after memcpy
debug: after free
debug: self.capacity = 0, new_capacity = 8
debug: old_memory = 0 u8@aaaaaaaaaaaaaaaa
debug: resize = false
debug: new_memory = 8 u8@18a38d10638
debug: after memcpy
debug: after free
debug: self.capacity = 8, new_capacity = 20
debug: old_memory = 8 u8@18a38d10638
debug: resize = true
debug: new_memory = 20 u8@18a38d1064c
debug: after memcpy
debug: after free
debug: self.capacity = 20, new_capacity = 38
debug: old_memory = 20 u8@18a38d1064c
debug: resize = true
debug: new_memory = 38 u8@18a38d10672
debug: after memcpy
debug: after free
debug: self.capacity = 38, new_capacity = 65
debug: old_memory = 38 u8@18a38d10672
debug: resize = true
debug: new_memory = 65 u8@18a38d106b3
debug: after memcpy
debug: after free
debug: self.capacity = 0, new_capacity = 8
debug: old_memory = 0 pdb.Pdb.Module@aaaaaaaaaaaaaaaa
debug: resize = false
debug: new_memory = 8 pdb.Pdb.Module@18a38d106f0
debug: after memcpy
debug: after free
debug: self.capacity = 0, new_capacity = 8
debug: old_memory = 0 u8@aaaaaaaaaaaaaaaa
debug: resize = false
debug: new_memory = 8 u8@18a38d10bb0
debug: after memcpy
debug: after free
debug: self.capacity = 8, new_capacity = 20
debug: old_memory = 8 u8@18a38d10bb0
debug: resize = true
debug: new_memory = 20 u8@18a38d10bc4
debug: after memcpy
debug: after free
debug: self.capacity = 20, new_capacity = 38
debug: old_memory = 20 u8@18a38d10bc4
debug: resize = true
debug: new_memory = 38 u8@18a38d10bea
debug: after memcpy
debug: after free
debug: self.capacity = 38, new_capacity = 65
debug: old_memory = 38 u8@18a38d10bea
debug: resize = true
debug: new_memory = 65 u8@18a38d10c2b
debug: after memcpy
debug: after free
debug: self.capacity = 65, new_capacity = 105
debug: old_memory = 65 u8@18a38d10c2b
debug: resize = true
debug: new_memory = 105 u8@18a38d10c94
debug: after memcpy
debug: after free
debug: self.capacity = 0, new_capacity = 8
debug: old_memory = 0 u8@aaaaaaaaaaaaaaaa
debug: resize = false
debug: new_memory = 8 u8@18a38d10ceb
debug: after memcpy
debug: after free
debug: self.capacity = 8, new_capacity = 20
debug: old_memory = 8 u8@18a38d10ceb
debug: resize = true
debug: new_memory = 20 u8@18a38d10cff
debug: after memcpy
debug: after free
debug: self.capacity = 20, new_capacity = 38
debug: old_memory = 20 u8@18a38d10cff
debug: resize = true
debug: new_memory = 38 u8@18a38d10d25
debug: after memcpy
debug: after free
debug: self.capacity = 38, new_capacity = 65
debug: old_memory = 38 u8@18a38d10d25
debug: resize = true
debug: new_memory = 65 u8@18a38d10d66
debug: after memcpy
debug: after free
debug: self.capacity = 65, new_capacity = 105
debug: old_memory = 65 u8@18a38d10d66
debug: resize = true
debug: new_memory = 105 u8@18a38d10dcf
debug: after memcpy
debug: after free
debug: self.capacity = 0, new_capacity = 8
debug: old_memory = 0 u8@aaaaaaaaaaaaaaaa
debug: resize = false
debug: new_memory = 8 u8@18a38d10e22
debug: after memcpy
debug: after free
debug: self.capacity = 8, new_capacity = 20
debug: old_memory = 8 u8@18a38d10e22
debug: resize = true
debug: new_memory = 20 u8@18a38d10e36
debug: after memcpy
debug: after free
debug: self.capacity = 0, new_capacity = 8
debug: old_memory = 0 u8@aaaaaaaaaaaaaaaa
debug: resize = false
debug: new_memory = 8 u8@18a38d10e3f
debug: after memcpy
debug: after free
debug: self.capacity = 8, new_capacity = 20
debug: old_memory = 8 u8@18a38d10e3f
debug: resize = true
debug: new_memory = 20 u8@18a38d10e53
debug: after memcpy
debug: after free
debug: self.capacity = 20, new_capacity = 38
debug: old_memory = 20 u8@18a38d10e53
debug: resize = true
debug: new_memory = 38 u8@18a38d10e79
debug: after memcpy
debug: after free
debug: self.capacity = 38, new_capacity = 65
debug: old_memory = 38 u8@18a38d10e79
debug: resize = true
debug: new_memory = 65 u8@18a38d10eba
debug: after memcpy
debug: after free
debug: self.capacity = 65, new_capacity = 105
debug: old_memory = 65 u8@18a38d10eba
debug: resize = true
debug: new_memory = 105 u8@18a38d10f23
debug: after memcpy
debug: after free
debug: self.capacity = 0, new_capacity = 8
debug: old_memory = 0 u8@aaaaaaaaaaaaaaaa
debug: resize = false
debug: new_memory = 8 u8@18a38d10f70
debug: after memcpy
debug: after free
debug: self.capacity = 8, new_capacity = 20
debug: old_memory = 8 u8@18a38d10f70
debug: resize = true
debug: new_memory = 20 u8@18a38d10f84
debug: after memcpy
debug: after free
debug: self.capacity = 0, new_capacity = 8
debug: old_memory = 0 u8@aaaaaaaaaaaaaaaa
debug: resize = false
debug: new_memory = 8 u8@18a38d10f94
debug: after memcpy
debug: after free
debug: self.capacity = 8, new_capacity = 20
debug: old_memory = 8 u8@18a38d10f94
debug: resize = true
debug: new_memory = 20 u8@18a38d10fa8
debug: after memcpy
debug: after free
debug: self.capacity = 20, new_capacity = 38
debug: old_memory = 20 u8@18a38d10fa8
debug: resize = true
debug: new_memory = 38 u8@18a38d10fce
debug: after memcpy
debug: after free
debug: self.capacity = 38, new_capacity = 65
debug: old_memory = 38 u8@18a38d10fce
debug: resize = true
debug: new_memory = 65 u8@18a38d1100f
debug: after memcpy
debug: after free
debug: self.capacity = 65, new_capacity = 105
debug: old_memory = 65 u8@18a38d1100f
debug: resize = true
debug: new_memory = 105 u8@18a38d11078
debug: after memcpy
debug: after free
debug: self.capacity = 0, new_capacity = 8
debug: old_memory = 0 u8@aaaaaaaaaaaaaaaa
debug: resize = false
debug: new_memory = 8 u8@18a38d110c5
debug: after memcpy
debug: after free
debug: self.capacity = 8, new_capacity = 20
debug: old_memory = 8 u8@18a38d110c5
debug: resize = true
debug: new_memory = 20 u8@18a38d110d9
debug: after memcpy
debug: after free
debug: self.capacity = 0, new_capacity = 8
debug: old_memory = 0 u8@aaaaaaaaaaaaaaaa
debug: resize = false
debug: new_memory = 8 u8@18a38d110e5
debug: after memcpy
debug: after free
debug: self.capacity = 8, new_capacity = 20
debug: old_memory = 8 u8@18a38d110e5
debug: resize = true
debug: new_memory = 20 u8@18a38d110f9
debug: after memcpy
debug: after free
debug: self.capacity = 20, new_capacity = 38
debug: old_memory = 20 u8@18a38d110f9
debug: resize = true
debug: new_memory = 38 u8@18a38d1111f
debug: after memcpy
debug: after free
debug: self.capacity = 38, new_capacity = 65
debug: old_memory = 38 u8@18a38d1111f
debug: resize = true
debug: new_memory = 65 u8@18a38d11160
debug: after memcpy
debug: after free
debug: self.capacity = 65, new_capacity = 105
debug: old_memory = 65 u8@18a38d11160
debug: resize = true
debug: new_memory = 105 u8@18a38d111c9
debug: after memcpy
debug: after free
debug: self.capacity = 0, new_capacity = 8
debug: old_memory = 0 u8@aaaaaaaaaaaaaaaa
debug: resize = false
debug: new_memory = 8 u8@18a38d11219
debug: after memcpy
debug: after free
debug: self.capacity = 8, new_capacity = 20
debug: old_memory = 8 u8@18a38d11219
debug: resize = true
debug: new_memory = 20 u8@18a38d1122d
debug: after memcpy
debug: after free
debug: self.capacity = 0, new_capacity = 8
debug: old_memory = 0 u8@aaaaaaaaaaaaaaaa
debug: resize = false
debug: new_memory = 8 u8@18a38d11240
debug: after memcpy
debug: after free
debug: self.capacity = 8, new_capacity = 20
debug: old_memory = 8 u8@18a38d11240
debug: resize = true
debug: new_memory = 20 u8@18a38d11254
debug: after memcpy
debug: after free
debug: self.capacity = 20, new_capacity = 38
debug: old_memory = 20 u8@18a38d11254
debug: resize = true
debug: new_memory = 38 u8@18a38d1127a
debug: after memcpy
debug: after free
debug: self.capacity = 38, new_capacity = 65
debug: old_memory = 38 u8@18a38d1127a
debug: resize = true
debug: new_memory = 65 u8@18a38d112bb
debug: after memcpy
debug: after free
debug: self.capacity = 65, new_capacity = 105
debug: old_memory = 65 u8@18a38d112bb
debug: resize = true
debug: new_memory = 105 u8@18a38d11324
debug: after memcpy
debug: after free
debug: self.capacity = 0, new_capacity = 8
debug: old_memory = 0 u8@aaaaaaaaaaaaaaaa
debug: resize = false
debug: new_memory = 8 u8@18a38d11374
debug: after memcpy
debug: after free
debug: self.capacity = 8, new_capacity = 20
debug: old_memory = 8 u8@18a38d11374
debug: resize = true
debug: new_memory = 20 u8@18a38d11388
debug: after memcpy
debug: after free
debug: self.capacity = 0, new_capacity = 8
debug: old_memory = 0 pdb.SectionContribEntry@aaaaaaaaaaaaaaaa
debug: resize = false
debug: new_memory = 8 pdb.SectionContribEntry@18a38d11394
debug: after memcpy
debug: after free
debug: self.capacity = 8, new_capacity = 20
debug: old_memory = 8 pdb.SectionContribEntry@18a38d11394
debug: resize = true
debug: new_memory = 20 pdb.SectionContribEntry@18a38d115c4
debug: after memcpy
debug: after free
debug: self.capacity = 20, new_capacity = 38
debug: old_memory = 20 pdb.SectionContribEntry@18a38d115c4
debug: resize = false
debug: new_memory = 38 pdb.SectionContribEntry@18a38d117f4
debug: after memcpy
debug: after free
debug: self.capacity = 38, new_capacity = 65
debug: old_memory = 38 pdb.SectionContribEntry@18a38d117f4
debug: resize = false
debug: new_memory = 65 pdb.SectionContribEntry@18a38d20010
debug: after memcpy
debug: after free
debug: self.capacity = 65, new_capacity = 105
debug: old_memory = 65 pdb.SectionContribEntry@18a38d20010
debug: resize = true
debug: new_memory = 105 pdb.SectionContribEntry@18a38d20b8c
debug: after memcpy
debug: after free
debug: self.capacity = 105, new_capacity = 165
debug: old_memory = 105 pdb.SectionContribEntry@18a38d20b8c
debug: resize = true
debug: new_memory = 165 pdb.SectionContribEntry@18a38d21d98
debug: after memcpy
debug: after free
debug: self.capacity = 165, new_capacity = 255
debug: old_memory = 165 pdb.SectionContribEntry@18a38d21d98
debug: resize = false
debug: new_memory = 255 pdb.SectionContribEntry@18a38f00010
debug: after memcpy
debug: after free
debug: self.capacity = 255, new_capacity = 390
debug: old_memory = 255 pdb.SectionContribEntry@18a38f00010
debug: resize = true
debug: new_memory = 390 pdb.SectionContribEntry@18a38f02ab8
debug: after memcpy
debug: after free
debug: self.capacity = 390, new_capacity = 593
debug: old_memory = 390 pdb.SectionContribEntry@18a38f02ab8
debug: resize = true
debug: new_memory = 593 pdb.SectionContribEntry@18a38f10010
debug: after memcpy
debug: after free
debug: self.capacity = 593, new_capacity = 897
debug: old_memory = 593 pdb.SectionContribEntry@18a38f10010
debug: resize = true
debug: new_memory = 897 pdb.SectionContribEntry@18a38f1622c
debug: after memcpy
debug: after free
debug: self.capacity = 0, new_capacity = 8
debug: old_memory = 0 u8@aaaaaaaaaaaaaaaa
debug: resize = false
debug: new_memory = 8 u8@18a3907cc40
debug: after memcpy
debug: after free
debug: self.capacity = 8, new_capacity = 20
debug: old_memory = 8 u8@18a3907cc40
debug: resize = true
debug: new_memory = 20 u8@18a3907cc54
debug: after memcpy
debug: after free
debug: self.capacity = 20, new_capacity = 38
debug: old_memory = 20 u8@18a3907cc54
debug: resize = true
debug: new_memory = 38 u8@18a3907cc7a
debug: after memcpy
debug: after free
debug: self.capacity = 38, new_capacity = 65
debug: old_memory = 38 u8@18a3907cc7a
debug: resize = true
debug: new_memory = 65 u8@18a3907ccbb
debug: after memcpy
debug: after free
debug: self.capacity = 65, new_capacity = 105
debug: old_memory = 65 u8@18a3907ccbb
debug: resize = true
debug: new_memory = 105 u8@18a3907cd24
debug: after memcpy
debug: after free
C:\tools\zig-windows-x86_64-0.14.0-dev.367+a57479afc\lib\std\array_list.zig:434:51: 0xd45962 in ensureTotalCapacity (host-main.exe.obj)
            return self.ensureTotalCapacityPrecise(better_capacity);
                                                  ^
debug: self.capacity = 0, new_capacity = 8
debug: old_memory = 0 u8@aaaaaaaaaaaaaaaa
debug: resize = false
debug: new_memory = 8 u8@18a3907cd24
debug: after memcpy
debug: after free
debug: self.capacity = 8, new_capacity = 20
debug: old_memory = 8 u8@18a3907cd24
debug: resize = true
debug: new_memory = 20 u8@18a3907cd38
debug: after memcpy
debug: after free
debug: self.capacity = 20, new_capacity = 38
debug: old_memory = 20 u8@18a3907cd38
debug: resize = true
debug: new_memory = 38 u8@18a3907cd5e
debug: after memcpy
debug: after free
debug: self.capacity = 38, new_capacity = 65
debug: old_memory = 38 u8@18a3907cd5e
debug: resize = true
debug: new_memory = 65 u8@18a3907cd9f
debug: after memcpy
debug: after free
debug: self.capacity = 65, new_capacity = 105
debug: old_memory = 65 u8@18a3907cd9f
debug: resize = true
debug: new_memory = 105 u8@18a3907ce08
debug: after memcpy
debug: after free
C:\tools\zig-windows-x86_64-0.14.0-dev.367+a57479afc\lib\std\array_list.zig:491:41: 0xd35dd3 in addOne (host-main.exe.obj)
            try self.ensureTotalCapacity(newlen);
                                        ^
debug: self.capacity = 0, new_capacity = 8
debug: old_memory = 0 u8@aaaaaaaaaaaaaaaa
debug: resize = false
debug: new_memory = 8 u8@18a3907ce08
debug: after memcpy
debug: after free
debug: self.capacity = 8, new_capacity = 20
debug: old_memory = 8 u8@18a3907ce08
debug: resize = true
debug: new_memory = 20 u8@18a3907ce1c
debug: after memcpy
debug: after free
debug: self.capacity = 20, new_capacity = 38
debug: old_memory = 20 u8@18a3907ce1c
debug: resize = true
debug: new_memory = 38 u8@18a3907ce42
debug: after memcpy
debug: after free
debug: self.capacity = 38, new_capacity = 65
debug: old_memory = 38 u8@18a3907ce42
debug: resize = true
debug: new_memory = 65 u8@18a3907ce83
debug: after memcpy
debug: after free
debug: self.capacity = 65, new_capacity = 105
debug: old_memory = 65 u8@18a3907ce83
debug: resize = true
debug: new_memory = 105 u8@18a3907ceec
debug: after memcpy
debug: after free
C:\tools\zig-windows-x86_64-0.14.0-dev.367+a57479afc\lib\std\array_list.zig:262:49: 0xd28494 in append (host-main.exe.obj)
            const new_item_ptr = try self.addOne();
                                                ^
debug: self.capacity = 0, new_capacity = 8
debug: old_memory = 0 u8@aaaaaaaaaaaaaaaa
debug: resize = false
debug: new_memory = 8 u8@18a3907ceec
debug: after memcpy
debug: after free
debug: self.capacity = 8, new_capacity = 20
debug: old_memory = 8 u8@18a3907ceec
debug: resize = true
debug: new_memory = 20 u8@18a3907cf00
debug: after memcpy
debug: after free
debug: self.capacity = 20, new_capacity = 38
debug: old_memory = 20 u8@18a3907cf00
debug: resize = true
debug: new_memory = 38 u8@18a3907cf26
debug: after memcpy
debug: after free
debug: self.capacity = 38, new_capacity = 65
debug: old_memory = 38 u8@18a3907cf26
debug: resize = true
debug: new_memory = 65 u8@18a3907cf67
debug: after memcpy
debug: after free
C:\Users\biosb\zig\zig-em-dev\work\em.core\em.lang\em.zig:391:30: 0xd19ba0 in add (host-main.exe.obj)
            self._list.append(item) catch fail();
                             ^
debug: self.capacity = 0, new_capacity = 8
debug: old_memory = 0 u8@aaaaaaaaaaaaaaaa
debug: resize = false
debug: new_memory = 8 u8@18a3907cf67
debug: after memcpy
debug: after free
debug: self.capacity = 8, new_capacity = 20
debug: old_memory = 8 u8@18a3907cf67
debug: resize = true
debug: new_memory = 20 u8@18a3907cf7b
debug: after memcpy
debug: after free
debug: self.capacity = 20, new_capacity = 38
debug: old_memory = 20 u8@18a3907cf7b
debug: resize = true
debug: new_memory = 38 u8@18a3907cfa1
debug: after memcpy
debug: after free
debug: self.capacity = 38, new_capacity = 65
debug: old_memory = 38 u8@18a3907cfa1
debug: resize = true
debug: new_memory = 65 u8@18a3907cfe2
debug: after memcpy
debug: after free
debug: self.capacity = 65, new_capacity = 105
debug: old_memory = 65 u8@18a3907cfe2
debug: resize = true
debug: new_memory = 105 u8@18a3907d04b
debug: after memcpy
debug: after free
C:\Users\biosb\zig\zig-em-dev\work\em.arch\em.arch.arm\IntrVec.em.zig:23:21: 0xcd54a6 in addIntrH (host-main.exe.obj)
        name_tab.add(name);
                    ^
debug: self.capacity = 0, new_capacity = 8
debug: old_memory = 0 u8@aaaaaaaaaaaaaaaa
debug: resize = false
debug: new_memory = 8 u8@18a3907d04b
debug: after memcpy
debug: after free
debug: self.capacity = 8, new_capacity = 20
debug: old_memory = 8 u8@18a3907d04b
debug: resize = true
debug: new_memory = 20 u8@18a3907d05f
debug: after memcpy
debug: after free
debug: self.capacity = 20, new_capacity = 38
debug: old_memory = 20 u8@18a3907d05f
debug: resize = true
debug: new_memory = 38 u8@18a3907d085
debug: after memcpy
debug: after free
debug: self.capacity = 38, new_capacity = 65
debug: old_memory = 38 u8@18a3907d085
debug: resize = true
debug: new_memory = 65 u8@18a3907d0c6
debug: after memcpy
debug: after free
debug: self.capacity = 65, new_capacity = 105
debug: old_memory = 65 u8@18a3907d0c6
debug: resize = true
debug: new_memory = 105 u8@18a3907d12f
debug: after memcpy
debug: after free
C:\Users\biosb\zig\zig-em-dev\work\em.arch\em.arch.arm\IntrVec.em.zig:48:21: 0xcd348c in em__initH (host-main.exe.obj)
            addIntrH(n);
                    ^
debug: self.capacity = 0, new_capacity = 8
debug: old_memory = 0 u8@aaaaaaaaaaaaaaaa
debug: resize = false
debug: new_memory = 8 u8@18a3907d12f
debug: after memcpy
debug: after free
debug: self.capacity = 8, new_capacity = 20
debug: old_memory = 8 u8@18a3907d12f
debug: resize = true
debug: new_memory = 20 u8@18a3907d143
debug: after memcpy
debug: after free
debug: self.capacity = 20, new_capacity = 38
debug: old_memory = 20 u8@18a3907d143
debug: resize = true
debug: new_memory = 38 u8@18a3907d169
debug: after memcpy
debug: after free
debug: self.capacity = 38, new_capacity = 65
debug: old_memory = 38 u8@18a3907d169
debug: resize = true
debug: new_memory = 65 u8@18a3907d1aa
debug: after memcpy
debug: after free
C:\Users\biosb\zig\zig-em-dev\work\em.core\em.lang\host-main.zig:22:12: 0xcd32af in exec__anon_3104 (host-main.exe.obj)
    callAll("em__initH", ulist_bot, false);
           ^
debug: self.capacity = 0, new_capacity = 8
debug: old_memory = 0 u8@aaaaaaaaaaaaaaaa
debug: resize = false
debug: new_memory = 8 u8@18a3907d1aa
debug: after memcpy
debug: after free
debug: self.capacity = 8, new_capacity = 20
debug: old_memory = 8 u8@18a3907d1aa
debug: resize = true
debug: new_memory = 20 u8@18a3907d1be
debug: after memcpy
debug: after free
debug: self.capacity = 20, new_capacity = 38
debug: old_memory = 20 u8@18a3907d1be
debug: resize = true
debug: new_memory = 38 u8@18a3907d1e4
debug: after memcpy
debug: after free
debug: self.capacity = 38, new_capacity = 65
debug: old_memory = 38 u8@18a3907d1e4
debug: resize = true
debug: new_memory = 65 u8@18a3907d225
debug: after memcpy
debug: after free
C:\Users\biosb\zig\zig-em-dev\work\.gen\host.zig:4:53: 0xcb1177 in exec (host-main.exe.obj)
    @import("../em.core/em.lang/host-main.zig").exec(em.import.@"em.examples.basic/EmptyP".em__U) catch em.fail();
                                                    ^
debug: self.capacity = 0, new_capacity = 8
debug: old_memory = 0 u8@aaaaaaaaaaaaaaaa
debug: resize = false
debug: new_memory = 8 u8@18a3907d225
debug: after memcpy
debug: after free
debug: self.capacity = 8, new_capacity = 20
debug: old_memory = 8 u8@18a3907d225
debug: resize = true
debug: new_memory = 20 u8@18a3907d239
debug: after memcpy
debug: after free
debug: self.capacity = 20, new_capacity = 38
debug: old_memory = 20 u8@18a3907d239
debug: resize = true
debug: new_memory = 38 u8@18a3907d25f
debug: after memcpy
debug: after free
debug: self.capacity = 38, new_capacity = 65
debug: old_memory = 38 u8@18a3907d25f
debug: resize = true
debug: new_memory = 65 u8@18a3907d2a0
debug: after memcpy
debug: after free
C:\Users\biosb\zig\zig-em-dev\work\.main-host.zig:1:51: 0xcb106e in main (host-main.exe.obj)
pub fn main() void { @import(".gen/host.zig").exec(); }
                                                  ^
debug: self.capacity = 0, new_capacity = 8
debug: old_memory = 0 u8@aaaaaaaaaaaaaaaa
debug: resize = false
debug: new_memory = 8 u8@18a3907d2a0
debug: after memcpy
debug: after free
debug: self.capacity = 8, new_capacity = 20
debug: old_memory = 8 u8@18a3907d2a0
debug: resize = true
debug: new_memory = 20 u8@18a3907d2b4
debug: after memcpy
debug: after free
debug: self.capacity = 20, new_capacity = 38
debug: old_memory = 20 u8@18a3907d2b4
debug: resize = true
debug: new_memory = 38 u8@18a3907d2da
debug: after memcpy
debug: after free
debug: self.capacity = 38, new_capacity = 65
debug: old_memory = 38 u8@18a3907d2da
debug: resize = true
debug: new_memory = 65 u8@18a3907d31b
debug: after memcpy
debug: after free
debug: self.capacity = 65, new_capacity = 105
debug: old_memory = 65 u8@18a3907d31b
debug: resize = true
debug: new_memory = 105 u8@18a3907d384
debug: after memcpy
debug: after free
C:\tools\zig-windows-x86_64-0.14.0-dev.367+a57479afc\lib\std\start.zig:373:53: 0xcb1017 in WinStartup (host-main.exe.obj)
    std.os.windows.ntdll.RtlExitUserProcess(callMain());
                                                    ^
???:?:?: 0x7ff84da67373 in ??? (KERNEL32.DLL)
???:?:?: 0x7ff84f95cc90 in ??? (ntdll.dll)

haven’t checked, but is it possible that my ArenaAllocator is itself using an ArrayList internally??? but if so, why wouldn’t i have seen output in my FIRST-AND-ONLY case when capacity was expanded from 0 to 8???

for avoidance of doubt, i tried a FixedBufferAllocator – same results…

I’ve briefly scrolled the thread so I might be missing something, but it seems that a chunk of it was about initiating at comptime an arraylist.

It doesn’t seem it was mentioned, but std.ArrayListUnmanaged(T) = .{} can be statically initialized and it’s also a smaller struct in case space matters (because it doesn’t hold an allocator inside of it). The tradeoff of course is that every method that adds stuff to it requires to pass also an allocator.

it’s trivial for me to make that change… to be clear, however, items are appended to the list at runtime… it’s the hierarchy of structs which ultimately contains this list which are initialized (using std.mem.zeroInit) at comptime…

Yes you can absolutely do that, as you’ve already seen.

Arena uses std.SinglyLinkedList, but other parts of Zig do use ArrayList, so your debug prints will also show those cases.

I’ve read now the thread more in detail, given what was discussed my guesses would be that the allocator might have been copied inadvertently or that some other memory issue is messing with either your allocator or your arraylist data.

Have you tried running your program under valgrind to see if catches anything?