zig icon indicating copy to clipboard operation
zig copied to clipboard

compiler crash when the source code contains pointer array concatenation

Open hronro opened this issue 2 years ago • 7 comments

Zig Version

0.11.0-dev.3386+99fe2a23c

Steps to Reproduce and Observed Behavior

const MyStruct = struct {
    foobar: u8,
};

var my_struct1 = MyStruct{ .foobar = 1 };
var my_struct2 = MyStruct{ .foobar = 2 };
var my_struct3 = MyStruct{ .foobar = 3 };
var my_struct4 = MyStruct{ .foobar = 4 };
const foo: []const *MyStruct = &[_]*MyStruct{ &my_struct1, &my_struct2, &my_struct3, &my_struct4 };
const bar: []const *MyStruct = foo[0..2] ++ foo[2..];    // this line will make compiler crash

pub fn main() void {
  @import("std").debug.print("{any}\n", .{bar});
}

Expected Behavior

Should able to compile the code above.

hronro avatar Jun 10 '23 12:06 hronro

Interestingly, if I use an integer array instead of a pointer array, it actually works as expected.

const foo: []const u8 = &[_]u8{ 1, 2, 3, 4 };
const bar: []const u8 = foo[0..2] ++ foo[2..];    // this line works

hronro avatar Jun 10 '23 12:06 hronro

The compiler doesn't crash, but instead outputs an expected error message (below).

This (admittedly slightly unintuitive behavior) is intentional, and happens because foo isn't comptime-known. my_structX are all runtime vars, so pointers to them are runtime-known, so in turn foo is runtime-known, and (since it's a slice) that makes its length unknown, which means it can't participate in concatenation (++). Your working example is okay because foo is comptime-known, so its length also is, so it can safely participate in concatenation.

The note on the error message does explain what's going on here:

thing.zig:11:52: error: unable to resolve comptime value
    const bar: []const *MyStruct = foo[0..2] ++ foo[2..]; // this line will make compiler crash
                                                ~~~^~~~~
thing.zig:11:52: note: slice value being concatenated must be comptime-known

mlugg avatar Jun 10 '23 13:06 mlugg

@mlugg The compiler crashes for me as well with attempt to use null value. Here is the debug error output from the latest master version(0.11.0-dev.3394+c842deea7):

Expand
$ ~/zig/stage3/bin/zig run test.zig
thread 12963 panic: attempt to use null value
Analyzing test.zig: test.zig:bar
    %59 = decl_val("MyStruct") token_offset:12:21 to :12:29
    %60 = ptr_type(%59, One) node_offset:12:20 to :12:29
    %61 = ptr_type(%60, const, Slice) node_offset:12:12 to :12:29
    %62 = as_node(@Zir.Inst.Ref.type_type, %61) node_offset:12:12 to :12:29
    %63 = decl_ref("foo") token_offset:12:32 to :12:35
    %64 = int(2)
    %65 = slice_end(%63, @Zir.Inst.Ref.zero, %64) node_offset:12:32 to :12:41
    %66 = decl_ref("foo") token_offset:12:45 to :12:48
    %67 = int(2)
    %68 = slice_start(%66, %67) node_offset:12:45 to :12:53
  > %69 = array_cat(%65, %68) node_offset:12:32 to :12:53
    %70 = as_node(%62, %69) node_offset:12:32 to :12:53
    %71 = break_inline(%58, %70)
  For full context, use the command
    zig ast-check -t test.zig

in test.zig: test.zig:main
  > %76 = decl_val("bar") token_offset:15:6 to :15:9
in test.zig: test.zig:main
  > %73 = block({%74..%80}) node_offset:14:20 to :14:21

/home/mint/zig/src/Sema.zig:12748:73: 0x8c0191d in zirArrayCat (zig)
              (try sema.pointerDeref(block, lhs_src, lhs_val, lhs_ty)).?
                                                                      ^
/home/mint/zig/src/Sema.zig:906:66: 0x892bd06 in analyzeBodyInner (zig)
          .array_cat                    => try sema.zirArrayCat(block, inst),
                                                               ^
/home/mint/zig/src/Sema.zig:821:45: 0x8737491 in analyzeBodyBreak (zig)
  const break_inst = sema.analyzeBodyInner(block, body) catch |err| switch (err) {
                                          ^
/home/mint/zig/src/Module.zig:4731:50: 0x8733426 in semaDecl (zig)
  const result_ref = (try sema.analyzeBodyBreak(&block_scope, body)).?.operand;
                                               ^
/home/mint/zig/src/Module.zig:4250:32: 0x853ee71 in ensureDeclAnalyzed (zig)
      break :blk mod.semaDecl(decl_index) catch |err| switch (err) {
                             ^
/home/mint/zig/src/Sema.zig:28989:32: 0x91dc275 in ensureDeclAnalyzed (zig)
  sema.mod.ensureDeclAnalyzed(decl_index) catch |err| {
                             ^
/home/mint/zig/src/Sema.zig:29040:32: 0x9264552 in analyzeDeclRefInner (zig)
  try sema.ensureDeclAnalyzed(decl_index);
                             ^
/home/mint/zig/src/Sema.zig:28959:50: 0x91adf17 in analyzeDeclVal (zig)
  const decl_ref = try sema.analyzeDeclRefInner(decl_index, false);
                                               ^
/home/mint/zig/src/Sema.zig:6019:31: 0x8c16842 in zirDeclVal (zig)
  return sema.analyzeDeclVal(block, src, decl);
                            ^
/home/mint/zig/src/Sema.zig:934:65: 0x892d5e0 in analyzeBodyInner (zig)
          .decl_val                     => try sema.zirDeclVal(block, inst),
                                                              ^
/home/mint/zig/src/Sema.zig:5503:34: 0x91d60d9 in resolveBlockBody (zig)
      if (sema.analyzeBodyInner(child_block, body)) |_| {
                               ^
/home/mint/zig/src/Sema.zig:5486:33: 0x8ce5665 in zirBlock (zig)
  return sema.resolveBlockBody(parent_block, src, &child_block, body, inst, &label.merges);
                              ^
/home/mint/zig/src/Sema.zig:1461:49: 0x8941184 in analyzeBodyInner (zig)
                  break :blk try sema.zirBlock(block, inst, tags[inst] == .block_comptime);
                                              ^
/home/mint/zig/src/Sema.zig:804:30: 0x8bb3858 in analyzeBody (zig)
  _ = sema.analyzeBodyInner(block, body) catch |err| switch (err) {
                           ^
/home/mint/zig/src/Module.zig:5753:21: 0x891255f in analyzeFnBody (zig)
  sema.analyzeBody(&inner_block, fn_info.body) catch |err| switch (err) {
                  ^
/home/mint/zig/src/Module.zig:4342:40: 0x87143d8 in ensureFuncBodyAnalyzed (zig)
          var air = mod.analyzeFnBody(func, sema_arena) catch |err| switch (err) {
                                     ^
/home/mint/zig/src/Compilation.zig:3134:42: 0x871219d in processOneJob (zig)
          module.ensureFuncBodyAnalyzed(func) catch |err| switch (err) {
                                       ^
/home/mint/zig/src/Compilation.zig:3071:30: 0x85a415e in performAllTheWork (zig)
          try processOneJob(comp, work_item, main_progress_node);
                           ^
/home/mint/zig/src/Compilation.zig:2026:31: 0x85a0658 in update (zig)
  try comp.performAllTheWork(main_progress_node);
                            ^
/home/mint/zig/src/main.zig:3835:24: 0x85d1cd4 in updateModule (zig)
      try comp.update(main_progress_node);
                     ^
/home/mint/zig/src/main.zig:3270:17: 0x8453280 in buildOutputType (zig)
  updateModule(comp) catch |err| switch (err) {
              ^
/home/mint/zig/src/main.zig:277:31: 0x8402987 in mainArgs (zig)
      return buildOutputType(gpa, arena, args, .run);
                            ^
/home/mint/zig/src/main.zig:213:20: 0x83ff495 in main (zig)
  return mainArgs(gpa, arena, args);
                 ^
/home/mint/zig/lib/std/start.zig:609:37: 0x83fee84 in main (zig)
          const result = root.main() catch |err| {
                                  ^
???:?:?: 0x7f833b95a082 in ??? (???)
Aborted (core dumped)

IntegratedQuantum avatar Jun 10 '23 14:06 IntegratedQuantum

My apologies, I thought this code was meant to be at function scope. At global scope with bar referenced I can indeed repro the crash.

mlugg avatar Jun 10 '23 14:06 mlugg

I'm working on this now

ghost avatar Jun 16 '23 01:06 ghost

Very odd... I expected the fix would lead to a compile error as mlugg said, but with the following patch the code actually works for me and has the intended behavior:

diff --git a/src/Sema.zig b/src/Sema.zig
index f703c6154..a45c8a311 100644
--- a/src/Sema.zig
+++ b/src/Sema.zig
@@ -13326,12 +13326,12 @@ fn zirArrayCat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
             else => unreachable,
         }) |rhs_val| {
             const lhs_sub_val = if (lhs_ty.isSinglePointer(mod))
-                (try sema.pointerDeref(block, lhs_src, lhs_val, lhs_ty)).?
+                (try sema.pointerDeref(block, lhs_src, lhs_val, lhs_ty)) orelse lhs_val
             else
                 lhs_val;

             const rhs_sub_val = if (rhs_ty.isSinglePointer(mod))
-                (try sema.pointerDeref(block, rhs_src, rhs_val, rhs_ty)).?
+                (try sema.pointerDeref(block, rhs_src, rhs_val, rhs_ty)) orelse rhs_val
             else
                 rhs_val;

Output:

{ t.MyStruct{ .foobar = 1 }, t.MyStruct{ .foobar = 2 }, t.MyStruct{ .foobar = 3 }, t.MyStruct{ .foobar = 4 } }

ghost avatar Jun 16 '23 02:06 ghost

Am I missing something? All zig build test-cases pass. Should I put this in a PR?

ghost avatar Jun 16 '23 02:06 ghost