llvm backend panic when using tagged union backed by enum with negative values
Zig Version
0.12.0
Steps to Reproduce and Observed Behavior
Running the following code with zig 0.12.0 leads to "panic: switch on corrupt value" while the code runs fine with 0.11.0 (tested on linux x86_64):
const std = @import("std");
const RoomTitle = struct {
delay: f32 = 0,
fn init(self: *RoomTitle) void {
_ = self;
}
};
const EntityType = enum(i8) {
room_title = -1,
};
const Entity = union(EntityType) {
room_title: RoomTitle,
};
pub fn main() void {
var rt: RoomTitle = RoomTitle{};
rt.init();
const e = Entity{ .room_title = rt };
// zig v0.12.0 -> panic: switch on corrupt value
switch (e) {
else => {
std.log.debug("ok", .{});
},
}
}
[jo@norfair celeste_clazzig]$ zig run src/celeste.zig
thread 44163 panic: switch on corrupt value
/home/jo/sandbox/celeste/celeste_clazzig/src/celeste.zig:25:13: 0x1033e6d in main (celeste)
switch (e) {
^
/nix/store/mq5vacngyamayigxiz5rfamlnyjpr49b-zig-0.12.0/lib/std/start.zig:501:22: 0x10336c9 in posixCallMainAndExit (celeste)
root.main();
^
/nix/store/mq5vacngyamayigxiz5rfamlnyjpr49b-zig-0.12.0/lib/std/start.zig:253:5: 0x1033231 in _start (celeste)
asm volatile (switch (native_arch) {
^
???:?:?: 0x0 in ??? (???)
Aborted (core dumped)
Expected Behavior
Same behaviour as zig 0.11.0 - no crash.
This can also be replicated with the latest nightly: 0.13.0-dev.46+3648d7df1 The first nightly release to have this issue seems to be: 0.12.0-dev.587+eb072fa52 0.12.0-dev.564+759b0fe00 is fine.
After further bisecting, commit 09a57583a4 may be the first to introduce this issue, but reading this change and the associated commit message, I don't see an obvious link. I think I've reached the limit of what I can do to help here.
Here is the associated stack trace which for some reason, is rather different from what I got with the nightly builds:
[jo@norfair build]$ git rev-parse --short HEAD
09a57583a4
[jo@norfair build]$ ./stage3/bin/zig run ./celeste.zig
Semantic Analysis [116] thread 92227 panic: attempt to use null value
/home/jo/sandbox/zig/src/value.zig:560:40: 0xafa541 in toUnsignedInt (zig)
return getUnsignedInt(val, mod).?;
^
/home/jo/sandbox/zig/src/codegen/llvm.zig:9792:49: 0x159bf64 in airUnionInit (zig)
break :blk tag_int_val.toUnsignedInt(mod);
^
/home/jo/sandbox/zig/src/codegen/llvm.zig:4854:57: 0x10190a2 in genBody (zig)
.union_init => try self.airUnionInit(inst),
^
/home/jo/sandbox/zig/src/codegen/llvm.zig:1625:19: 0x10116ec in updateFunc (zig)
fg.genBody(air.getMainBody()) catch |err| switch (err) {
^
/home/jo/sandbox/zig/src/link/Elf.zig:2738:70: 0x101c3d9 in updateFunc (zig)
if (self.llvm_object) |llvm_object| return llvm_object.updateFunc(mod, func_index, air, liveness);
^
/home/jo/sandbox/zig/src/link.zig:594:77: 0xd14e18 in updateFunc (zig)
.elf => return @fieldParentPtr(Elf, "base", base).updateFunc(module, func_index, air, liveness),
^
/home/jo/sandbox/zig/src/Module.zig:3508:37: 0xae00aa in ensureFuncBodyAnalyzed (zig)
comp.bin_file.updateFunc(mod, func_index, air, liveness) catch |err| switch (err) {
^
/home/jo/sandbox/zig/src/Compilation.zig:3370:42: 0xadd74d in processOneJob (zig)
module.ensureFuncBodyAnalyzed(func) catch |err| switch (err) {
^
/home/jo/sandbox/zig/src/Compilation.zig:3307:30: 0x94a2cb in performAllTheWork (zig)
try processOneJob(comp, work_item, main_progress_node);
^
/home/jo/sandbox/zig/src/Compilation.zig:2184:31: 0x94585c in update (zig)
try comp.performAllTheWork(main_progress_node);
^
/home/jo/sandbox/zig/src/main.zig:4159:24: 0x975d70 in updateModule (zig)
try comp.update(main_progress_node);
^
/home/jo/sandbox/zig/src/main.zig:3580:17: 0x999388 in buildOutputType (zig)
updateModule(comp) catch |err| switch (err) {
^
/home/jo/sandbox/zig/src/main.zig:278:31: 0x7ef32f in mainArgs (zig)
return buildOutputType(gpa, arena, args, .run);
^
/home/jo/sandbox/zig/src/main.zig:214:20: 0x7ec2b5 in main (zig)
return mainArgs(gpa, arena, args);
^
/home/jo/sandbox/zig/lib/std/start.zig:486:37: 0x7ebcce in main (zig)
std.os.argv = @as([*][*:0]u8, @ptrCast(c_argv))[0..@as(usize, @intCast(c_argc))];
^
???:?:?: 0x7744fbe43ccf in ??? (libc.so.6)
Unwind information for `libc.so.6:0x7744fbe43ccf` was not available, trace may be incomplete
Aborted (core dumped)
It seems like the issue occurs when the enum type is signed and the sign-bit of the value is on. I'm guessing that the discrepancy in errors is due to safety checks present in debug builds of the compiler, causing a panic at compile-time instead of runtime.
The minimal reproducible code is as follows in c231d94960ec2cecbea0de877f645ba5d439fd13:
const std = @import("std");
const EntityType = enum(i8) {
room_title = -1, // negative
};
const Entity = union(EntityType) {
room_title: i32,
};
pub fn main() void {
var rt: i32 = 0;
rt = rt; // for 'var' declare is work, if use `const` the example build success.
const e = Entity{ .room_title = rt };
_ = e;
}
If using 0.13.0-dev.46+3648d7df1, you must add switch to trigger a crash at runtime.