Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Zig segfaults when initializing a tuple with double anonymous struct .{.{}} #18972

Open
timfayz opened this issue Feb 17, 2024 · 4 comments
Open
Labels
bug Observed behavior contradicts documented or intended behavior frontend Tokenization, parsing, AstGen, Sema, and Liveness.
Milestone

Comments

@timfayz
Copy link

timfayz commented Feb 17, 2024

Zig Version

0.12.0-dev.2790+fc7dd3e28

Steps to Reproduce and Observed Behavior

Zig segfaults immediately when compiles the following code:

pub fn main() !void {
    const val: struct {
        union {},
    } = .{.{}};
    _ = val;
}

Running the example gives:

$ zig build-exe demo.zig
zsh: segmentation fault  zig run demo.zig

Possibly related to or a duplicate of: #9284 and #17349

Expected Behavior

There should be an error message or successful compilation.

Update

First, I thought it is about using double anonymous struct/enum literals. However, I think it is just about initializing unions inside a tuple. Here are some crashing examples:

pub fn main() !void {
    const val: struct {
        union {},
    } = .{null};
    _ = val;
}
pub fn main() !void {
    const val: struct {
        union {},
        union {},
    } = .{null};
    _ = val;
}

Non-empty unions also cause the compiler to crash:

pub fn main() !void {
    const val: struct {
        union { field: bool },
    } = .{null};
    _ = val;
}
@timfayz timfayz added the bug Observed behavior contradicts documented or intended behavior label Feb 17, 2024
@rohlem
Copy link
Contributor

rohlem commented Feb 17, 2024

The issue (as I see it) is that the code would be instantiating an empty union, which is an uninstantiable ("noreturn-like") type.
(To be pedantic, having a field of such a type transitively makes the containing struct/tuple into an uninstantiable type as well.)
Since it has no possible state, instantiating it is always a logic bug. See also #15909 .

The compiler crashing is a bug however, as you've stated this should trigger a compile error - thank you for reporting!

@timfayz
Copy link
Author

timfayz commented Feb 17, 2024

I don't think it is about empty unions. Here is what segfaults as well:

pub fn main() !void {
    const val: struct {
        union { field: bool },
    } = .{.{}};
    _ = val;
}

@rohlem
Copy link
Contributor

rohlem commented Feb 17, 2024

You're right, that's a different bug then.

@nektro
Copy link
Contributor

nektro commented Feb 17, 2024

0.12.0-dev.2701+d18f52197

thread 95225 panic: reached unreachable code
Analyzing test2.zig: test2.zig:main
      %6 = dbg_block_begin()
      %7 = dbg_stmt(2, 5)
      %8 = block_comptime({
        %9 = extended(struct_decl(hash(96b7b114157d375757fa8da7eb019302) tuple, anon, Auto, {}, {
          @"0": {%10, %11},
        }) node_offset:4:16 to :4:22
        %12 = break(%8, %9)
      }) node_offset:4:16 to :4:22
      %13 = alloc(%8) node_offset:4:5 to :4:10
      %14 = opt_eu_base_ptr_init(%13) node_offset:6:9 to :6:15
    > %15 = array_init_elem_ptr(%14, 0) node_offset:6:11 to :6:14
      %16 = typeof(%15) node_offset:6:11 to :6:14
      %17 = elem_type(%16) node_offset:6:11 to :6:14
      %18 = struct_init_empty_result(%17) node_offset:6:11 to :6:14
      %19 = store_node(%15, %18) node_offset:6:11 to :6:14
      %20 = validate_ptr_array_init({
        %15 = array_init_elem_ptr(%14, 0) node_offset:6:11 to :6:14
      }) node_offset:6:9 to :6:15
      %21 = make_ptr_const(%13) node_offset:4:5 to :4:10
      %22 = dbg_var_ptr(%21, "val")
      %23 = dbg_stmt(5, 5)
      %24 = load(%21) node_offset:7:9 to :7:12
      %25 = ensure_result_non_error(%24) node_offset:7:9 to :7:12
      %26 = dbg_block_end()
      %27 = restore_err_ret_index(%5.none)
      %28 = break(%5, @void_value)
    For full context, use the command
      zig ast-check -t test2.zig

  in test2.zig: test2.zig:main
    > %5 = block({%6..%28}) node_offset:3:21 to :3:21
  in /home/meghan/src/zig/lib/std/start.zig: start.zig:callMain
    > %2423 = is_non_err(%2422) 
  in /home/meghan/src/zig/lib/std/start.zig: start.zig:callMain
    > %2425 = block({%2419..%2424}) 
  in /home/meghan/src/zig/lib/std/start.zig: start.zig:callMain
    > %2416 = block({%2417..%2563}) 
  in /home/meghan/src/zig/lib/std/start.zig: start.zig:callMain
    > %2327 = switch_block(%2325,
        else => {%2566..%2572},
        %2328 => {%2329..%2341},
        %2342 => {%2343..%2361},
        by_val %2362 => {%2363..%2413},
        %2414 => {%2415..%2565}) 
  in /home/meghan/src/zig/lib/std/start.zig: start.zig:callMain
    > %2311 = block({%2312..%2576}) 
  in /home/meghan/src/zig/lib/std/start.zig: start.zig:callMainWithArgs
    > %2086 = call(.auto, %2084, []) 
  in /home/meghan/src/zig/lib/std/start.zig: start.zig:callMainWithArgs
    > %2049 = block({%2050..%2093}) 
  in /home/meghan/src/zig/lib/std/start.zig: start.zig:posixCallMainAndExit
    > %1872 = call(.auto, %1870, [
        {%1873},
        {%1874},
        {%1875},
      ]) 
  in /home/meghan/src/zig/lib/std/start.zig: start.zig:posixCallMainAndExit
    > %1869 = field_call(nodiscard .auto, %1866, "exit", [
        {%1870..%1876},
      ]) 
  in /home/meghan/src/zig/lib/std/start.zig: start.zig:posixCallMainAndExit
    > %1478 = block({%1479..%1879}) 

/home/meghan/src/zig/lib/std/debug.zig:403:14: 0x170452c in assert (zig)
    if (!ok) unreachable; // assertion failure
             ^
/home/meghan/src/zig/src/InternPool.zig:4028:11: 0x1772dbd in indexToKey (zig)
    assert(index != .none);
          ^
/home/meghan/src/zig/src/InternPool.zig:2266:36: 0x1a5854c in loadUnionType (zig)
    const enum_info = ip.indexToKey(enum_ty).enum_type;
                                   ^
/home/meghan/src/zig/src/type.zig:2476:55: 0x1eda15c in onePossibleValue (zig)
                    const union_obj = ip.loadUnionType(union_type);
                                                      ^
/home/meghan/src/zig/src/type.zig:3048:102: 0x1ef6ba5 in structFieldValueComptime (zig)
                    return Type.fromInterned(struct_type.field_types.get(ip)[index]).onePossibleValue(mod);
                                                                                                     ^
/home/meghan/src/zig/src/Sema.zig:28216:46: 0x2be5ce7 in tupleFieldPtr (zig)
    if (try tuple_ty.structFieldValueComptime(mod, field_index)) |default_val| {
                                             ^
/home/meghan/src/zig/src/Sema.zig:28009:46: 0x2711b6c in elemPtr (zig)
            break :blk try sema.tupleFieldPtr(block, src, indexable_ptr, elem_index_src, index, init);
                                             ^
/home/meghan/src/zig/src/Sema.zig:10620:24: 0x2288fc2 in zirArrayInitElemPtr (zig)
    return sema.elemPtr(block, src, array_ptr, elem_index, src, true, true);
                       ^
/home/meghan/src/zig/src/Sema.zig:1121:74: 0x1e3c86c in analyzeBodyInner (zig)
            .array_init_elem_ptr          => try sema.zirArrayInitElemPtr(block, inst),
                                                                         ^
/home/meghan/src/zig/src/Sema.zig:5978:34: 0x27e41c7 in resolveBlockBody (zig)
        if (sema.analyzeBodyInner(child_block, body)) |_| {
                                 ^
/home/meghan/src/zig/src/Sema.zig:5961:33: 0x230950f in zirBlock (zig)
    return sema.resolveBlockBody(parent_block, src, &child_block, body, inst, &label.merges);
                                ^
/home/meghan/src/zig/src/Sema.zig:1602:49: 0x1e4926b in analyzeBodyInner (zig)
                    break :blk try sema.zirBlock(block, inst, tags[@intFromEnum(inst)] == .block_comptime);
                                                ^
/home/meghan/src/zig/src/Sema.zig:922:30: 0x215e7b8 in analyzeBody (zig)
    _ = sema.analyzeBodyInner(block, body) catch |err| switch (err) {
                             ^
/home/meghan/src/zig/src/Module.zig:4878:21: 0x1e0be57 in analyzeFnBody (zig)
    sema.analyzeBody(&inner_block, fn_info.body) catch |err| switch (err) {
                    ^
/home/meghan/src/zig/src/Module.zig:3552:32: 0x1b299c1 in ensureFuncBodyAnalyzed (zig)
    var air = zcu.analyzeFnBody(func_index, sema_arena) catch |err| switch (err) {
                               ^
/home/meghan/src/zig/src/Sema.zig:32431:31: 0x288cacd in ensureFuncBodyAnalyzed (zig)
    mod.ensureFuncBodyAnalyzed(func) catch |err| {
                              ^
/home/meghan/src/zig/src/Sema.zig:36374:40: 0x23348cb in resolveInferredErrorSet (zig)
        try sema.ensureFuncBodyAnalyzed(func_index);
                                       ^
/home/meghan/src/zig/src/Sema.zig:32762:69: 0x2305d46 in analyzeIsNonErrComptimeOnly (zig)
                const resolved_ty = try sema.resolveInferredErrorSet(block, src, set_ty);
                                                                    ^
/home/meghan/src/zig/src/Sema.zig:32791:56: 0x27ba6c9 in analyzeIsNonErr (zig)
    const result = try sema.analyzeIsNonErrComptimeOnly(block, src, operand);
                                                       ^
/home/meghan/src/zig/src/Sema.zig:18950:32: 0x225abfe in zirIsNonErr (zig)
    return sema.analyzeIsNonErr(block, src, operand);
                               ^
/home/meghan/src/zig/src/Sema.zig:1076:66: 0x1e3a728 in analyzeBodyInner (zig)
            .is_non_err                   => try sema.zirIsNonErr(block, inst),
                                                                 ^
/home/meghan/src/zig/src/Sema.zig:5978:34: 0x27e41c7 in resolveBlockBody (zig)
        if (sema.analyzeBodyInner(child_block, body)) |_| {
                                 ^
/home/meghan/src/zig/src/Sema.zig:5961:33: 0x230950f in zirBlock (zig)
    return sema.resolveBlockBody(parent_block, src, &child_block, body, inst, &label.merges);
                                ^
/home/meghan/src/zig/src/Sema.zig:1602:49: 0x1e4926b in analyzeBodyInner (zig)
                    break :blk try sema.zirBlock(block, inst, tags[@intFromEnum(inst)] == .block_comptime);
                                                ^
/home/meghan/src/zig/src/Sema.zig:5978:34: 0x27e41c7 in resolveBlockBody (zig)
        if (sema.analyzeBodyInner(child_block, body)) |_| {
                                 ^
/home/meghan/src/zig/src/Sema.zig:5961:33: 0x230950f in zirBlock (zig)
    return sema.resolveBlockBody(parent_block, src, &child_block, body, inst, &label.merges);
                                ^
/home/meghan/src/zig/src/Sema.zig:1602:49: 0x1e4926b in analyzeBodyInner (zig)
                    break :blk try sema.zirBlock(block, inst, tags[@intFromEnum(inst)] == .block_comptime);
                                                ^
/home/meghan/src/zig/src/Sema.zig:5978:34: 0x27e41c7 in resolveBlockBody (zig)
        if (sema.analyzeBodyInner(child_block, body)) |_| {
                                 ^
/home/meghan/src/zig/src/Sema.zig:10750:45: 0x27dac60 in resolveProngComptime (zig)
                return sema.resolveBlockBody(spa.parent_block, src, child_block, prong_body, spa.switch_block_inst, merges);
                                            ^
/home/meghan/src/zig/src/Sema.zig:12904:48: 0x27d94ef in resolveSwitchComptime (zig)
                return spa.resolveProngComptime(
                                               ^
/home/meghan/src/zig/src/Sema.zig:12145:37: 0x226a21f in zirSwitchBlock (zig)
        return resolveSwitchComptime(
                                    ^
/home/meghan/src/zig/src/Sema.zig:1099:69: 0x1e3b81d in analyzeBodyInner (zig)
            .switch_block                 => try sema.zirSwitchBlock(block, inst, false),
                                                                    ^
/home/meghan/src/zig/src/Sema.zig:5978:34: 0x27e41c7 in resolveBlockBody (zig)
        if (sema.analyzeBodyInner(child_block, body)) |_| {
                                 ^
/home/meghan/src/zig/src/Sema.zig:5961:33: 0x230950f in zirBlock (zig)
    return sema.resolveBlockBody(parent_block, src, &child_block, body, inst, &label.merges);
                                ^
/home/meghan/src/zig/src/Sema.zig:1602:49: 0x1e4926b in analyzeBodyInner (zig)
                    break :blk try sema.zirBlock(block, inst, tags[@intFromEnum(inst)] == .block_comptime);
                                                ^
/home/meghan/src/zig/src/Sema.zig:922:30: 0x215e7b8 in analyzeBody (zig)
    _ = sema.analyzeBodyInner(block, body) catch |err| switch (err) {
                             ^
/home/meghan/src/zig/src/Sema.zig:7619:33: 0x27a6063 in analyzeCall (zig)
                sema.analyzeBody(&child_block, fn_info.body) catch |err| switch (err) {
                                ^
/home/meghan/src/zig/src/Sema.zig:6818:43: 0x224b6bc in zirCall__anon_98729 (zig)
    const call_inst = try sema.analyzeCall(block, func, func_ty, callee_src, call_src, modifier, ensure_result_used, args_info, call_dbg_node, .call);
                                          ^
/home/meghan/src/zig/src/Sema.zig:1033:62: 0x1e38716 in analyzeBodyInner (zig)
            .call                         => try sema.zirCall(block, inst, .direct),
                                                             ^
/home/meghan/src/zig/src/Sema.zig:5978:34: 0x27e41c7 in resolveBlockBody (zig)
        if (sema.analyzeBodyInner(child_block, body)) |_| {
                                 ^
/home/meghan/src/zig/src/Sema.zig:5961:33: 0x230950f in zirBlock (zig)
    return sema.resolveBlockBody(parent_block, src, &child_block, body, inst, &label.merges);
                                ^
/home/meghan/src/zig/src/Sema.zig:1602:49: 0x1e4926b in analyzeBodyInner (zig)
                    break :blk try sema.zirBlock(block, inst, tags[@intFromEnum(inst)] == .block_comptime);
                                                ^
/home/meghan/src/zig/src/Sema.zig:922:30: 0x215e7b8 in analyzeBody (zig)
    _ = sema.analyzeBodyInner(block, body) catch |err| switch (err) {
                             ^
/home/meghan/src/zig/src/Sema.zig:7619:33: 0x27a6063 in analyzeCall (zig)
                sema.analyzeBody(&child_block, fn_info.body) catch |err| switch (err) {
                                ^
/home/meghan/src/zig/src/Sema.zig:6818:43: 0x224b6bc in zirCall__anon_98729 (zig)
    const call_inst = try sema.analyzeCall(block, func, func_ty, callee_src, call_src, modifier, ensure_result_used, args_info, call_dbg_node, .call);
                                          ^
/home/meghan/src/zig/src/Sema.zig:1033:62: 0x1e38716 in analyzeBodyInner (zig)
            .call                         => try sema.zirCall(block, inst, .direct),
                                                             ^
/home/meghan/src/zig/src/Sema.zig:939:45: 0x1b4eb54 in analyzeBodyBreak (zig)
    const break_inst = sema.analyzeBodyInner(block, body) catch |err| switch (err) {
                                            ^
/home/meghan/src/zig/src/Sema.zig:888:50: 0x279c583 in resolveBody (zig)
    const break_data = (try sema.analyzeBodyBreak(block, body)) orelse
                                                 ^
/home/meghan/src/zig/src/Sema.zig:7107:59: 0x2d3b075 in analyzeArg (zig)
                const uncoerced_arg = try sema.resolveBody(block, arg_body, zir_call.call_inst);
                                                          ^
/home/meghan/src/zig/src/Sema.zig:7683:49: 0x27a710d in analyzeCall (zig)
            arg_out.* = try args_info.analyzeArg(sema, block, arg_idx, param_ty, func_ty_info, func);
                                                ^
/home/meghan/src/zig/src/Sema.zig:6818:43: 0x224c83e in zirCall__anon_98730 (zig)
    const call_inst = try sema.analyzeCall(block, func, func_ty, callee_src, call_src, modifier, ensure_result_used, args_info, call_dbg_node, .call);
                                          ^
/home/meghan/src/zig/src/Sema.zig:1034:62: 0x1e387d2 in analyzeBodyInner (zig)
            .field_call                   => try sema.zirCall(block, inst, .field),
                                                             ^
/home/meghan/src/zig/src/Sema.zig:5978:34: 0x27e41c7 in resolveBlockBody (zig)
        if (sema.analyzeBodyInner(child_block, body)) |_| {
                                 ^
/home/meghan/src/zig/src/Sema.zig:5961:33: 0x230950f in zirBlock (zig)
    return sema.resolveBlockBody(parent_block, src, &child_block, body, inst, &label.merges);
                                ^
/home/meghan/src/zig/src/Sema.zig:1602:49: 0x1e4926b in analyzeBodyInner (zig)
                    break :blk try sema.zirBlock(block, inst, tags[@intFromEnum(inst)] == .block_comptime);
                                                ^
/home/meghan/src/zig/src/Sema.zig:922:30: 0x215e7b8 in analyzeBody (zig)
    _ = sema.analyzeBodyInner(block, body) catch |err| switch (err) {
                             ^
/home/meghan/src/zig/src/Module.zig:4878:21: 0x1e0be57 in analyzeFnBody (zig)
    sema.analyzeBody(&inner_block, fn_info.body) catch |err| switch (err) {
                    ^
/home/meghan/src/zig/src/Module.zig:3552:32: 0x1b299c1 in ensureFuncBodyAnalyzed (zig)
    var air = zcu.analyzeFnBody(func_index, sema_arena) catch |err| switch (err) {
                               ^
/home/meghan/src/zig/src/Compilation.zig:3579:42: 0x1b27b53 in processOneJob (zig)
            module.ensureFuncBodyAnalyzed(func) catch |err| switch (err) {
                                         ^
/home/meghan/src/zig/src/Compilation.zig:3508:30: 0x18d1ffa in performAllTheWork (zig)
            try processOneJob(comp, work_item, main_progress_node);
                             ^
/home/meghan/src/zig/src/Compilation.zig:2191:31: 0x18cdc7b in update (zig)
    try comp.performAllTheWork(main_progress_node);
                              ^
/home/meghan/src/zig/src/main.zig:4500:24: 0x18fdcfc in updateModule (zig)
        try comp.update(main_progress_node);
                       ^
/home/meghan/src/zig/src/main.zig:3353:17: 0x191c107 in buildOutputType (zig)
    updateModule(comp, color) catch |err| switch (err) {
                ^
/home/meghan/src/zig/src/main.zig:285:31: 0x1706a67 in mainArgs (zig)
        return buildOutputType(gpa, arena, args, .run);
                              ^
/home/meghan/src/zig/src/main.zig:223:20: 0x17037f5 in main (zig)
    return mainArgs(gpa, arena, args);
                   ^
/home/meghan/src/zig/lib/std/start.zig:511:37: 0x170326e in main (zig)
            const result = root.main() catch |err| {
                                    ^
???:?:?: 0x7fefca03dacd in ??? (libc.so.6)
Unwind information for `libc.so.6:0x7fefca03dacd` was not available, trace may be incomplete

Aborted (core dumped)

@Vexu Vexu added the frontend Tokenization, parsing, AstGen, Sema, and Liveness. label Feb 18, 2024
@Vexu Vexu added this to the 0.13.0 milestone Feb 18, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Observed behavior contradicts documented or intended behavior frontend Tokenization, parsing, AstGen, Sema, and Liveness.
Projects
None yet
Development

No branches or pull requests

4 participants