Bit-packed struct with undefined default has inconsistent access
Zig Version
0.13.0-dev.211+6a65561e3
Steps to Reproduce and Observed Behavior
Run the following code:
const std = @import("std");
pub const Foo = packed struct(u2) {
a: bool = undefined,
b: bool,
};
const foo = Foo{
.b = true,
};
pub fn main() void {
std.debug.print("{}\n", .{foo});
std.debug.print("{}\n", .{foo.b});
}
which prints
example.Foo{ .a = false, .b = false }
true
Notice how when we print foo, we get .b = false, but when we print foo.b we get true.
Expected Behavior
Both of them should print b as being true, but the first one doesn't.
A few things I've noticed:
- If you change the
undefineddefault value withfalse, then everything works as expected - Pretty recent regression, works in zig 0.12.0
On 0.12:
❯ zig run empty.zig
empty.Foo{ .a = false, .b = true }
true
On 0.13.0-dev.55+fc45e5b39:
❯ zig run empty.zig
empty.Foo{ .a = false, .b = false }
true
❯ zig run empty.zig -fno-llvm -fno-lld
empty.Foo{ .a = false, .b = true }
true
Note that fc45e5b39 is before the LLVM upgrade.
In #19630 it was made so that having undefined in a packed struct would propagate to the entire struct iirc.
So see:
const std = @import("std");
pub const Foo = packed struct(u2) {
a: bool,
b: bool,
};
pub fn main() void {
var foo: Foo = undefined;
_ = &foo;
foo.b = true;
std.debug.print("{}\n", .{foo});
std.debug.print("{}\n", .{foo.b});
}
which works fine.
Might be talking non-sense, but I believe this is what is happening.
having undefined in a packed struct would propagate to the entire struct
If that's the expected/desired behaviour with:
const std = @import("std");
pub const Foo = packed struct(u2) {
a: bool = undefined,
b: bool,
};
pub fn main() void {
var foo = Foo{
.b = true,
};
_ = &foo;
std.debug.print("{}\n", .{foo});
std.debug.print("{}\n", .{foo.b});
}
consistent with that expectation (master branch) does mark as undef:
store i2 undef, ptr %2, align 1, !dbg !2322
however, simply swapping field order:
pub const Foo = packed struct(u2) {
b: bool,
a: bool = undefined,
};
the IR becomes inconsistent with that expectation:
store i2 1, ptr %2, align 1, !dbg !2322
fwiw, this specific repro bisects to c231d94960ec2cecbea0de877f645ba5d439fd13
@Rexicon226 that idea was put on hold for now, and so has never been in a build of Zig.
Ah, my bad. No idea then.
Is there a way to print an undefined value without branching on it and invoking UB? It doesn't surprise me personally that LLVM seems to do weird things after branching on undefined...
Is there a way to print an undefined value without branching on it and invoking UB? It doesn't surprise me personally that LLVM seems to do weird things after branching on undefined...
The thing is that only one field is undefined. Also, when casting to an int (where there's no branching) this also happens.
Caused by #20095, using -fno-llvm -fno-lld results in a.Foo{ .a = false, .b = true }.