Commit cc26148ba7

Andrew Kelley <superjoe30@gmail.com>
2018-02-15 18:14:20
fix compiler crash when struct contains...
ptr to another struct which contains original struct
1 parent 1c1c069
src/analyze.cpp
@@ -2278,17 +2278,16 @@ static void resolve_struct_zero_bits(CodeGen *g, TypeTableEntry *struct_type) {
         return;
 
     if (struct_type->data.structure.zero_bits_loop_flag) {
-        // If we get here it's due to recursion. From this we conclude that the struct is
-        // not zero bits, and if abi_alignment == 0 we further conclude that the first field
-        // is a pointer to this very struct, or a function pointer with parameters that
-        // reference such a type.
+        // If we get here it's due to recursion. This is a design flaw in the compiler,
+        // we should be able to still figure out alignment, but here we give up and say that
+        // the alignment is pointer width, then assert that the first field is within that
+        // alignment
         struct_type->data.structure.zero_bits_known = true;
         if (struct_type->data.structure.abi_alignment == 0) {
             if (struct_type->data.structure.layout == ContainerLayoutPacked) {
                 struct_type->data.structure.abi_alignment = 1;
             } else {
-                struct_type->data.structure.abi_alignment = LLVMABIAlignmentOfType(g->target_data_ref,
-                        LLVMPointerType(LLVMInt8Type(), 0));
+                struct_type->data.structure.abi_alignment = LLVMABIAlignmentOfType(g->target_data_ref, LLVMPointerType(LLVMInt8Type(), 0));
             }
         }
         return;
@@ -2352,11 +2351,17 @@ static void resolve_struct_zero_bits(CodeGen *g, TypeTableEntry *struct_type) {
         if (gen_field_index == 0) {
             if (struct_type->data.structure.layout == ContainerLayoutPacked) {
                 struct_type->data.structure.abi_alignment = 1;
-            } else {
+            } else if (struct_type->data.structure.abi_alignment == 0) {
                 // Alignment of structs is the alignment of the first field, for now.
                 // TODO change this when we re-order struct fields (issue #168)
                 struct_type->data.structure.abi_alignment = get_abi_alignment(g, field_type);
                 assert(struct_type->data.structure.abi_alignment != 0);
+            } else {
+                // due to a design flaw in the compiler we assumed that alignment was
+                // pointer width, so we assert that this wasn't violated.
+                if (get_abi_alignment(g, field_type) > struct_type->data.structure.abi_alignment) {
+                    zig_panic("compiler design flaw: incorrect alignment assumption");
+                }
             }
         }
 
test/cases/struct_contains_null_ptr_itself.zig
@@ -0,0 +1,22 @@
+const std = @import("std");
+const assert = std.debug.assert;
+
+test "struct contains null pointer which contains original struct" {
+    var x: ?&NodeLineComment = null;
+    assert(x == null);
+}
+
+pub const Node = struct {
+    id: Id,
+    comment: ?&NodeLineComment,
+
+    pub const Id = enum {
+        Root,
+        LineComment,
+    };
+};
+
+pub const NodeLineComment = struct {
+    base: Node,
+};
+
test/behavior.zig
@@ -35,6 +35,7 @@ comptime {
     _ = @import("cases/slice.zig");
     _ = @import("cases/struct.zig");
     _ = @import("cases/struct_contains_slice_of_itself.zig");
+    _ = @import("cases/struct_contains_null_ptr_itself.zig");
     _ = @import("cases/switch.zig");
     _ = @import("cases/switch_prong_err_enum.zig");
     _ = @import("cases/switch_prong_implicit_cast.zig");