Search code examples
zig

Non determinism in the output when using ArrayList and Slices


I'm working on Advent of Code with Zig and I'm at day 3. I've uploaded the code I wrote. The puzzle description and the code is here: https://github.com/secondspass/adventofcodeday3 . The code is in day3_part2.zig, the input file is day3.in. I run with zig run day3_part2.zig.

I get one of two outputs when I run the code. Either the below integer overflow in the co2ScrubberRating function.

thread 174927 panic: integer overflow
/home/subil/Projects/zig_learn/adventofcode/test/day3_part2.zig:80:25: 0x23401c in co2ScrubberRating (day3_part2)
    while (i >= 0) : (i -= 1) {
                        ^
/home/subil/Projects/zig_learn/adventofcode/test/day3_part2.zig:112:42: 0x22c80e in main (day3_part2)
    var co2rating = try co2ScrubberRating(numbers.items[0..], allocator);
                                         ^
/home/subil/tools/zig-0.9.0/lib/std/start.zig:535:37: 0x224eaa in std.start.callMain (day3_part2)
            const result = root.main() catch |err| {
                                    ^
/home/subil/tools/zig-0.9.0/lib/std/start.zig:477:12: 0x208ace in std.start.callMainWithArgs (day3_part2)
    return @call(.{ .modifier = .always_inline }, callMain, .{});
           ^
/home/subil/tools/zig-0.9.0/lib/std/start.zig:391:17: 0x207b56 in std.start.posixCallMainAndExit (day3_part2)
    std.os.exit(@call(.{ .modifier = .always_inline }, callMainWithArgs, .{ argc, argv, envp }));
                ^
/home/subil/tools/zig-0.9.0/lib/std/start.zig:304:5: 0x207962 in std.start._start (day3_part2)
    @call(.{ .modifier = .never_inline }, posixCallMainAndExit, .{});
    ^
Aborted (core dumped)

or I would get the following output, where the binary number on the second line is the output of the co2ScrubberRating function is a seemingly random number.

100111011110
1000000111101101010111 

And which output I get is random every time I run the zig run day3_part2.zig.

I'm passing the same ArrayList.items slice and allocator to both the oxygenGeneratorRating and the co2ScrubberRating. And if I only run one of those functions in the main (i.e. I comment out one of them), the correct output is produced for that function. But if I have both functions in the main, I get the random bad outputs from the co2ScrubberRating function. And I have no idea why it behaves like that. I guess it has something to do with the ArrayList being passed to a function a second time.

Any insight would be appreciated.


Solution

  • This is because readToNumberList is returning a pointer to stack memory that goes out of scope, which is undefined behaviour and not yet caught in debug builds.

    fn readNumberList(…) !*std.ArrayList(u32) {
        …
        return &numbers;
    

    Changing the code to

    fn readNumberList(…) !std.ArrayList(u32) {
        …
        return numbers;
    

    fixes the issue.

    You could also fix it by putting the ArrayList struct in heap memory:

    fn readNumberList(…) !*std.ArrayList(u32) {
        const numbers = try allocator.create(std.ArrayList(u32));
        numbers.* = std.ArrayList(u32).init(allocator);
        …
        return numbers;