Surprising side effect using print statements

Hey!

While trying to learn more about zigs internpool i stumbled upon this issue, which i can’t wrap my head around.

Simplified Code example:

    const key: Key = .{ .one = .{ .one = 1234, .two = "hello world" } };

    const seed = @backingInt(key);
    std.debug.print("{d}\n", .{seed});
    switch (key) {
        .one => |v| {
            const x: u64 = std.hash.Wyhash.hash(1, std.mem.asBytes(&v));
            std.debug.print("{d}\n", .{x});
        },
        else => {},
    }

    const z: u64 = 1;
    std.debug.print("{d}\n", .{z});
    std.debug.print("hello world\n", .{});

My observations:

  1. When running this code as is, the value of x is 8864718373389434152.
  2. When removing the print statements after the switch case x suddenly becomes 13165288763890694823.
  3. Additionally removing the seed print statement (note, seed was never even used for the hash) x prints as 5643058626213398210.

Now, i’m pretty surprised by this behaviour! I’d guess it’s a bug, but i’m also unfamiliar with wyhash, so i could as well be doing something wrong.

The Code in question i’ve looked at is the hash64 function of the internpool.
It happens on 0.16 and master in Debug mode. ReleaseSafe seems to be printing consistent results (although different ones all together) so i’m thinking it’s a bug with the self hosted backend.

If someone has any idea what’s going on let me know!

Thanks!

Is it that you’re hashing a structure that has a slice (i.e.ptr + len) in it, and the pointer is changing because the string is moving in memory?

4 Likes

This seems to be it! Totally on the money :smiley:
Thanks!

1 Like

You’re hashing std.mem.asBytes(&v), and v is a struct that almost certainly has padding bytes. Those padding bytes are uninitialized, so their garbage contents shift around depending on stack layout, which the surrounding print statements happen to change. That’s why the hash is unstable rather than a wyhash bug. Try zeroing the struct or hashing fields explicitly and it should stabilize.

1 Like

both @viktoriyanavrotskaya and @WeeBull are right. It could be either, or both, the slice ptr changing, and the padding changine.

zeroing the memory is not a solution, writing to the padding of undefined layout types is illegal behaviour. It also does not address the issue of the slice ptr/len being hashed instead of the actual contents.

Some hash implementations support hashing values of arbitrary types through reflection, unfortunately Wyhash implementation does not, so OP has to hash the fields individually.

1 Like