Zig: Compiler crash using a field in a packed struct

Created on 19 Apr 2020  路  5Comments  路  Source: ziglang/zig

const std = @import("std");

const InlineString = packed struct {
    bytes: [10]u8,
};

pub fn main() !void {
    var s = InlineString {
        .bytes = [_]u8{0} ** 10,
    };

    std.mem.copy(u8, &s.bytes, "hi");
}

This code crashes the compiler:

$ zig build-exe compiler_crash.zig
[1]    52084 segmentation fault  zig build-exe compiler_crash.zig

The compiler only crashes if you use a packed struct.

(Version: [email protected] from homebrew on macos)

bug stage1

Most helpful comment

there's some bugs with the current implementation of packed structures

That's true, but they aren't compiler crashes. We should at least make this error.
That said it's worth noting @josephg that even with the compiler crash fixed, the InlineString structure ends up being 16 bytes due to these bugs. They will be addressed by #3133, but there are still pieces that need to be figured out so it could take a while. In the meantime I recommend using struct or extern struct and putting align(1) on fields where necessary.

It looks like what's happening is the type of &s.bytes is *align(:0:16) [10]u8, which can't coerce to the type []u8 required for the parameter to copy because of its weird bit-alignment (which is a result of the aforementioned bugs). Instead of causing an error though, this coercion case ends in a compiler crash. I was able to find this minimal repro:

pub fn main() void {
    var s: *align(:0:16) [10]u8 = undefined;
    _ = @as([]u8, s);
}

All 5 comments

This is a duplicate, I think.

tl;dr: there's some bugs with the current implementation of packed structures. I don't remember the exact trigger, but it's something along the lines of "non-power-of-two field size."

there's some bugs with the current implementation of packed structures

That's true, but they aren't compiler crashes. We should at least make this error.
That said it's worth noting @josephg that even with the compiler crash fixed, the InlineString structure ends up being 16 bytes due to these bugs. They will be addressed by #3133, but there are still pieces that need to be figured out so it could take a while. In the meantime I recommend using struct or extern struct and putting align(1) on fields where necessary.

It looks like what's happening is the type of &s.bytes is *align(:0:16) [10]u8, which can't coerce to the type []u8 required for the parameter to copy because of its weird bit-alignment (which is a result of the aforementioned bugs). Instead of causing an error though, this coercion case ends in a compiler crash. I was able to find this minimal repro:

pub fn main() void {
    var s: *align(:0:16) [10]u8 = undefined;
    _ = @as([]u8, s);
}

Here is another case of this:

const std = @import("std");

const S = packed struct {
    arr: [5]u8,
};

pub fn main() !void {
    var a: S = undefined;
    std.debug.warn("{}\n", .{a.arr[0..2]});
}

gdb backtrace at crash:

(gdb) run build-exe test.zig
Starting program: /mnt/c/dev/zig/build-linux/zig build-exe test.zig
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
Code Generation [402/531] std.mem.bytesAsSlice...
Program received signal SIGSEGV, Segmentation fault.
0x000000000e1a9ca8 in llvm::PointerType::get(llvm::Type*, unsigned int) ()
(gdb) bt
#0  0x000000000e1a9ca8 in llvm::PointerType::get(llvm::Type*, unsigned int) ()
#1  0x000000000e0c6f9d in LLVMBuildInBoundsGEP ()
#2  0x000000000917be6f in ir_render_slice (g=0x10b026c0, executable=0x154005b0, instruction=0x154a6a60) at /mnt/c/dev/zig/src/codegen.cpp:5582
#3  0x000000000918041c in ir_render_instruction (g=0x10b026c0, executable=0x154005b0, instruction=0x154a6a60) at /mnt/c/dev/zig/src/codegen.cpp:6624
#4  0x0000000009180b21 in ir_render (g=0x10b026c0, fn_entry=0x15400530) at /mnt/c/dev/zig/src/codegen.cpp:6744
#5  0x00000000091871ea in do_code_gen (g=0x10b026c0) at /mnt/c/dev/zig/src/codegen.cpp:8144
#6  0x00000000091928f6 in codegen_build_and_link (g=0x10b026c0) at /mnt/c/dev/zig/src/codegen.cpp:10917
#7  0x0000000009161e1a in main0 (argc=3, argv=0x7ffffffedbf8) at /mnt/c/dev/zig/src/main.cpp:1671
#8  0x000000000916299e in main (argc=3, argv=0x7ffffffedbf8) at /mnt/c/dev/zig/src/main.cpp:1858
(gdb) q

Another case of non-power-of-two-length arrays in packed structs causing a compiler crash:

const S = packed struct {
    data: [5]u8,
};

fn returnZeros() [5]u8 {
    const result = [_]u8 { 0 } ** 5;
    return result;
}

export fn x() void {
    const s = S {
        .data = returnZeros()
    };
}

Attempting to compile this produces the following error:

broken LLVM module found: Call parameter type does not match function signature!
  %1 = bitcast [8 x i8]* %0 to i64*, !dbg !57
 [5 x i8]*  call fastcc void @returnZeros(i64* sret %1), !dbg !58

This is a bug in the Zig compiler.
Unable to dump stack trace: debug info stripped
Was this page helpful?
0 / 5 - 0 ratings

Related issues

bronze1man picture bronze1man  路  3Comments

zimmi picture zimmi  路  3Comments

andrewrk picture andrewrk  路  3Comments

fengb picture fengb  路  3Comments

andrewrk picture andrewrk  路  3Comments