ziglang / zig

General-purpose programming language and toolchain for maintaining robust, optimal, and reusable software.
https://ziglang.org
MIT License
32.4k stars 2.36k forks source link

Tuples with default fields allowed by mistake, crash in self hosted #20277

Open MasonRemaley opened 1 month ago

MasonRemaley commented 1 month ago

Zig Version

0.14.0-dev.453+eda846b86

Steps to Reproduce and Observed Behavior

The compiler currently allows code like this:

    const Tuple = struct {
        u8 = 1,
        u8 = 2,
        u8 = 3,
    };

This would not be a crazy feature, I am personally indifferent to whether or not it should be supported.

However, the actual behavior of these default field tuples is not what one would reasonably expect. This leads me to believe that we don't actually intend to support them right now, but are failing to reject them by mistake.

For example, this case fails to compile:

test "tuple issue" {
    const Tuple = struct {
        u8 = 1,
        u8 = 2,
        u8 = 3,
    };
    const temp = Tuple{ 1, 2 };
    std.debug.print("\n\ntuple: {}\n\n", .{temp});
}
src/main.zig:38:23: error: missing tuple field with index 2
    const temp = Tuple{ 1, 2 };

Furthermore, if instead of building Zig from C++ I self host it, the compiler crashes on the same code (0.14.0-dev.437+d9bd34fd0):

test
└─ run test
   └─ zig test Debug native failure
error: thread 64473 panic: reached unreachable code
Analyzing src/main.zig: main.zig:test.tuple issue
      %11 = dbg_stmt(2, 5)
      %12 = extended(struct_decl(hash(fcc5cad6a58e325ccaea84049d669e0a) known_non_opv, tuple, dbg_var, {}, auto, {}, {
        @"0": @u8_type = {%13},
        @"1": @u8_type = {%14, %15},
        @"2": @u8_type = {%16, %17},
      }) node_offset:33:19 to :33:25
      %18 = dbg_var_val(%12, "Tuple")
      %19 = dbg_stmt(7, 5)
      %20 = block_comptime({
        %21 = break(%20, %12)
      }) node_offset:38:18 to :38:23
      %22 = validate_array_init_ty(%20, 2) node_offset:38:18 to :38:31
      %23 = array_init_elem_type(%20, 0)
      %24 = array_init_elem_type(%20, 1)
      %25 = int(2)
    > %26 = array_init(%20{@one, %25}) node_offset:38:18 to :38:31
      %27 = dbg_var_val(%26, "temp")
      %28 = dbg_stmt(8, 5)
      %29 = decl_ref("std") token_offset:39:5 to :39:8
      %30 = dbg_stmt(8, 8)
      %31 = field_ptr(%29, "debug") node_offset:39:5 to :39:14
      %32 = dbg_stmt(8, 20)
      %33 = field_call(nodiscard .auto, %31, "print", [
        {
          %34 = str("\n\ntuple: {}\n\n")
          %35 = break_inline(%33, %34)
        },
        {
          %36 = validate_array_init_result_ty(%33, 1) node_offset:39:42 to :39:49
          %37 = array_init_elem_type(%33, 0)
          %38 = array_init(%33{%26}) node_offset:39:42 to :39:49
          %39 = break_inline(%33, %38)
        },
      ]) node_offset:39:5 to :39:50
      %40 = restore_err_ret_index_unconditional(.none) node_offset:32:1 to :32:5
      %41 = ret_implicit(@void_value) token_offset:40:1 to :40:1
    For full context, use the command
      zig ast-check -t src/main.zig

/home/mason/Documents/zon/zig/lib/std/debug.zig:412:14: 0x5ae68bc in assert (zig)
    if (!ok) unreachable; // assertion failure
             ^
/home/mason/Documents/zon/zig/src/InternPool.zig:2104:15: 0x649279c in fieldInit (zig)
        assert(s.haveFieldInits(ip));
              ^
/home/mason/Documents/zon/zig/src/type.zig:3217:50: 0x6d4e350 in structFieldDefaultValue (zig)
                const val = struct_type.fieldInit(ip, index);
                                                 ^
/home/mason/Documents/zon/zig/src/Sema.zig:20681:65: 0x68c840f in zirArrayInit (zig)
    const is_tuple = array_ty.zigTypeTag(mod) == .Struct;
                                                                ^
/home/mason/Documents/zon/zig/src/Sema.zig:1098:67: 0x6381fdb in analyzeBodyInner (zig)
            .array_init                   => try sema.zirArrayInit(block, inst, false),
                                                                  ^
/home/mason/Documents/zon/zig/src/Sema.zig:892:26: 0x637d7e1 in analyzeFnBody (zig)
    sema.analyzeBodyInner(block, body) catch |err| switch (err) {
                         ^
/home/mason/Documents/zon/zig/src/Module.zig:4640:23: 0x606dc96 in analyzeFnBody (zig)

                      ^
/home/mason/Documents/zon/zig/src/Module.zig:3144:32: 0x5dd1788 in ensureFuncBodyAnalyzed (zig)
        .inline_only => unreachable, // don't queue work for this
                               ^
/home/mason/Documents/zon/zig/src/Compilation.zig:3405:42: 0x5dcf29b in processOneJob (zig)

                                         ^
/home/mason/Documents/zon/zig/src/Compilation.zig:3345:30: 0x5c0885c in performAllTheWork (zig)
    while (true) {
                             ^
/home/mason/Documents/zon/zig/src/Compilation.zig:2132:31: 0x5c0441c in update (zig)
    }
                              ^
/home/mason/Documents/zon/zig/src/main.zig:4074:32: 0x5c7e07b in serve (zig)
                try comp.update(main_progress_node);
                               ^
/home/mason/Documents/zon/zig/src/main.zig:3373:22: 0x5c9c8d4 in buildOutputType (zig)
            try serve(
                     ^
/home/mason/Documents/zon/zig/src/main.zig:269:31: 0x5ae8d2b in mainArgs (zig)
        return buildOutputType(gpa, arena, args, .zig_test);
                              ^
/home/mason/Documents/zon/zig/src/main.zig:209:20: 0x5ae5a85 in main (zig)
 temp ➜ ~/Documents/zon/zig/build/stage4/bin/zig build test
test
└─ run test
   └─ zig test Debug native failure
error: thread 64550 panic: reached unreachable code
Analyzing src/main.zig: main.zig:test.tuple issue
      %11 = dbg_stmt(2, 5)
      %12 = extended(struct_decl(hash(fcc5cad6a58e325ccaea84049d669e0a) known_non_opv, tuple, dbg_var, {}, auto, {}, {
        @"0": @u8_type = {%13},
        @"1": @u8_type = {%14, %15},
        @"2": @u8_type = {%16, %17},
      }) node_offset:33:19 to :33:25
      %18 = dbg_var_val(%12, "Tuple")
      %19 = dbg_stmt(7, 5)
      %20 = block_comptime({
        %21 = break(%20, %12)
      }) node_offset:38:18 to :38:23
      %22 = validate_array_init_ty(%20, 2) node_offset:38:18 to :38:31
      %23 = array_init_elem_type(%20, 0)
      %24 = array_init_elem_type(%20, 1)
      %25 = int(2)
    > %26 = array_init(%20{@one, %25}) node_offset:38:18 to :38:31
      %27 = dbg_var_val(%26, "temp")
      %28 = dbg_stmt(8, 5)
      %29 = decl_ref("std") token_offset:39:5 to :39:8
      %30 = dbg_stmt(8, 8)
      %31 = field_ptr(%29, "debug") node_offset:39:5 to :39:14
      %32 = dbg_stmt(8, 20)
      %33 = field_call(nodiscard .auto, %31, "print", [
        {
          %34 = str("\n\ntuple: {}\n\n")
          %35 = break_inline(%33, %34)
        },
        {
          %36 = validate_array_init_result_ty(%33, 1) node_offset:39:42 to :39:49
          %37 = array_init_elem_type(%33, 0)
          %38 = array_init(%33{%26}) node_offset:39:42 to :39:49
          %39 = break_inline(%33, %38)
        },
      ]) node_offset:39:5 to :39:50
      %40 = restore_err_ret_index_unconditional(.none) node_offset:32:1 to :32:5
      %41 = ret_implicit(@void_value) token_offset:40:1 to :40:1
    For full context, use the command
      zig ast-check -t src/main.zig

/home/mason/Documents/zon/zig/lib/std/debug.zig:412:14: 0x5ae68bc in assert (zig)
    if (!ok) unreachable; // assertion failure
             ^
/home/mason/Documents/zon/zig/src/InternPool.zig:2104:15: 0x649279c in fieldInit (zig)
        assert(s.haveFieldInits(ip));
              ^
/home/mason/Documents/zon/zig/src/type.zig:3217:50: 0x6d4e350 in structFieldDefaultValue (zig)
                const val = struct_type.fieldInit(ip, index);
                                                 ^
/home/mason/Documents/zon/zig/src/Sema.zig:20681:65: 0x68c840f in zirArrayInit (zig)
    const is_tuple = array_ty.zigTypeTag(mod) == .Struct;
                                                                ^
/home/mason/Documents/zon/zig/src/Sema.zig:1098:67: 0x6381fdb in analyzeBodyInner (zig)
            .array_init                   => try sema.zirArrayInit(block, inst, false),
                                                                  ^
/home/mason/Documents/zon/zig/src/Sema.zig:892:26: 0x637d7e1 in analyzeFnBody (zig)
    sema.analyzeBodyInner(block, body) catch |err| switch (err) {
                         ^
/home/mason/Documents/zon/zig/src/Module.zig:4640:23: 0x606dc96 in analyzeFnBody (zig)

                      ^
/home/mason/Documents/zon/zig/src/Module.zig:3144:32: 0x5dd1788 in ensureFuncBodyAnalyzed (zig)
        .inline_only => unreachable, // don't queue work for this
                               ^
/home/mason/Documents/zon/zig/src/Compilation.zig:3405:42: 0x5dcf29b in processOneJob (zig)

                                         ^
/home/mason/Documents/zon/zig/src/Compilation.zig:3345:30: 0x5c0885c in performAllTheWork (zig)
    while (true) {
                             ^
/home/mason/Documents/zon/zig/src/Compilation.zig:2132:31: 0x5c0441c in update (zig)
    }
                              ^
/home/mason/Documents/zon/zig/src/main.zig:4074:32: 0x5c7e07b in serve (zig)
                try comp.update(main_progress_node);
                               ^
/home/mason/Documents/zon/zig/src/main.zig:3373:22: 0x5c9c8d4 in buildOutputType (zig)
            try serve(
                     ^
/home/mason/Documents/zon/zig/src/main.zig:269:31: 0x5ae8d2b in mainArgs (zig)
        return buildOutputType(gpa, arena, args, .zig_test);
                              ^
/home/mason/Documents/zon/zig/src/main.zig:209:20: 0x5ae5a85 in main (zig)
    return mainArgs(gpa, arena, args);
                   ^
/home/mason/Documents/zon/zig/lib/std/start.zig:524:37: 0x5ae551e in main (zig)
            const result = root.main() catch |err| {
                                    ^
../sysdeps/nptl/libc_start_call_main.h:58:16: 0x72ebc8029d8f in __libc_start_call_main (../sysdeps/x86/libc-start.c)
../csu/libc-start.c:392:3: 0x72ebc8029e3f in __libc_start_main_impl (../sysdeps/x86/libc-start.c)
???:?:?: 0x5ae5164 in ??? (???)
???:?:?: 0x0 in ??? (???)

error: the following command terminated unexpectedly:
/home/mason/Documents/zon/zig/build/stage4/bin/zig test -ODebug -Mroot=/home/mason/Downloads/zig-linux-x86_64-0.14.0-dev.23+d9bd34fd0/temp/src/main.zig --cache-dir /home/mason/Downloads/zig-linux-x86_64-0.14.0-dev.23+d9bd34fd0/temp/.zig-cache --global-cache-dir /home/mason/.cache/zig --name test --listen=-
Build Summary: 2/5 steps succeeded; 1 failed (disable with --summary none)
test transitive failure
└─ run test transitive failure
   └─ zig test Debug native failure
error: the following build command failed with exit code 1:
/home/mason/Downloads/zig-linux-x86_64-0.14.0-dev.23+d9bd34fd0/temp/.zig-cache/o/98bf75858b188f353668d0358e0cbc68/build /home/mason/Documents/zon/zig/build/stage4/bin/zig /home/mason/Downloads/zig-linux-x86_64-0.14.0-dev.23+d9bd34fd0/temp /home/mason/Downloads/zig-linux-x86_64-0.14.0-dev.23+d9bd34fd0/temp/.zig-cache /home/mason/.cache/zig --seed 0x8d4d9aa9 -Z6370c73436a66bfc test

Expected Behavior

I expected default tuple fields to be rejected by the compiler.

mlugg commented 1 month ago

The currently intended behavior for tuples (discussed in some compiler meeting waaaaayyyy back when) is to not permit default values, except in comptime fields. This has been on my list as a part of a bigger refactor around anon structs for a while (I don't see myself getting to it soon, so others are free to take it up).