From 39567e8b50e9026288bb1323d84f481740bd0de7 Mon Sep 17 00:00:00 2001 From: kristopher tate Date: Thu, 20 Dec 2018 22:47:44 +0900 Subject: [PATCH 1/3] src/analyze.cpp: support alignOf(struct T) aligned member inside struct T; ref: ziglang/zig#1832 --- src/analyze.cpp | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/src/analyze.cpp b/src/analyze.cpp index 46686ce772..e209822132 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -2686,13 +2686,22 @@ static Error resolve_struct_alignment(CodeGen *g, ZigType *struct_type) { } size_t field_count = struct_type->data.structure.src_field_count; + bool self_resolving = false; for (size_t i = 0; i < field_count; i += 1) { TypeStructField *field = &struct_type->data.structure.fields[i]; - // If this assertion trips, look up the call stack. Probably something is - // calling type_resolve with ResolveStatusAlignmentKnown when it should only - // be resolving ResolveStatusZeroBitsKnown - assert(field->type_entry != nullptr); + // If we have no type_entry for the field, assume that we are in the + // midst of resolving this struct. We further assume that since the + // resolved alignment of the other fields of this struct is ultimately + // equal to the resolved alignment of this struct, we can safely ignore. + // + // If this struct is used down-stream in aligning a sub-struct, ignoring + // this struct in the context of a sub struct has the same effect since + // the other fields will be calculated and bubble-up. + if (nullptr == field->type_entry) { + self_resolving = true; + continue; + } if (type_is_invalid(field->type_entry)) { struct_type->data.structure.resolve_status = ResolveStatusInvalid; @@ -2723,6 +2732,14 @@ static Error resolve_struct_alignment(CodeGen *g, ZigType *struct_type) { return ErrorSemanticAnalyzeFail; } + if ( self_resolving + && field_count > 0 + ) { + // If we get here it's due to self-referencing this struct before it has been fully resolved. + // In this case, set alignment to target pointer default. + struct_type->data.structure.abi_alignment = LLVMABIAlignmentOfType(g->target_data_ref, + LLVMPointerType(LLVMInt8Type(), 0)); + } struct_type->data.structure.resolve_status = ResolveStatusAlignmentKnown; return ErrorNone; } From fb81b1978b87be5a72328a4d9efc21561ae53cfc Mon Sep 17 00:00:00 2001 From: kristopher tate Date: Thu, 20 Dec 2018 22:49:09 +0900 Subject: [PATCH 2/3] tests: re: 79db394; ref: ziglang/zig#1832 --- std/array_list.zig | 11 +++++ .../cases/struct_contains_slice_of_itself.zig | 42 +++++++++++++++++++ 2 files changed, 53 insertions(+) diff --git a/std/array_list.zig b/std/array_list.zig index 3ee425fe14..ddad9c989c 100644 --- a/std/array_list.zig +++ b/std/array_list.zig @@ -398,3 +398,14 @@ test "std.ArrayList.insertSlice" { assert(list.len == 6); assert(list.items[0] == 1); } + +const Item = struct { + integer: i32, + sub_items: ArrayList(Item), +}; + +test "std.ArrayList: ArrayList(T) of struct T" { + var root = Item{ .integer = 1, .sub_items = ArrayList(Item).init(debug.global_allocator) }; + try root.sub_items.append( Item{ .integer = 42, .sub_items = ArrayList(Item).init(debug.global_allocator) } ); + assert(root.sub_items.items[0].integer == 42); +} diff --git a/test/cases/struct_contains_slice_of_itself.zig b/test/cases/struct_contains_slice_of_itself.zig index 07987ae32b..aa3075312c 100644 --- a/test/cases/struct_contains_slice_of_itself.zig +++ b/test/cases/struct_contains_slice_of_itself.zig @@ -5,6 +5,11 @@ const Node = struct { children: []Node, }; +const NodeAligned = struct { + payload: i32, + children: []align(@alignOf(NodeAligned)) NodeAligned, +}; + test "struct contains slice of itself" { var other_nodes = []Node{ Node{ @@ -41,3 +46,40 @@ test "struct contains slice of itself" { assert(root.children[2].children[0].payload == 31); assert(root.children[2].children[1].payload == 32); } + +test "struct contains aligned slice of itself" { + var other_nodes = []NodeAligned{ + NodeAligned{ + .payload = 31, + .children = []NodeAligned{}, + }, + NodeAligned{ + .payload = 32, + .children = []NodeAligned{}, + }, + }; + var nodes = []NodeAligned{ + NodeAligned{ + .payload = 1, + .children = []NodeAligned{}, + }, + NodeAligned{ + .payload = 2, + .children = []NodeAligned{}, + }, + NodeAligned{ + .payload = 3, + .children = other_nodes[0..], + }, + }; + const root = NodeAligned{ + .payload = 1234, + .children = nodes[0..], + }; + assert(root.payload == 1234); + assert(root.children[0].payload == 1); + assert(root.children[1].payload == 2); + assert(root.children[2].payload == 3); + assert(root.children[2].children[0].payload == 31); + assert(root.children[2].children[1].payload == 32); +} From 0f54194e6ab2b0cbdc798b84cc4213a07ed1d0c1 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 20 Dec 2018 12:36:15 -0500 Subject: [PATCH 3/3] fixups --- src/analyze.cpp | 70 ++++++++++++++++++++++--------------------------- 1 file changed, 32 insertions(+), 38 deletions(-) diff --git a/src/analyze.cpp b/src/analyze.cpp index e209822132..9c24f3cc8d 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -2681,48 +2681,50 @@ static Error resolve_struct_alignment(CodeGen *g, ZigType *struct_type) { assert(decl_node->type == NodeTypeContainerDecl); assert(struct_type->di_type); + size_t field_count = struct_type->data.structure.src_field_count; if (struct_type->data.structure.layout == ContainerLayoutPacked) { struct_type->data.structure.abi_alignment = 1; - } - - size_t field_count = struct_type->data.structure.src_field_count; - bool self_resolving = false; - for (size_t i = 0; i < field_count; i += 1) { + for (size_t i = 0; i < field_count; i += 1) { + TypeStructField *field = &struct_type->data.structure.fields[i]; + if (field->type_entry != nullptr && type_is_invalid(field->type_entry)) { + struct_type->data.structure.resolve_status = ResolveStatusInvalid; + break; + } + } + } else for (size_t i = 0; i < field_count; i += 1) { TypeStructField *field = &struct_type->data.structure.fields[i]; + uint32_t this_field_align; - // If we have no type_entry for the field, assume that we are in the - // midst of resolving this struct. We further assume that since the - // resolved alignment of the other fields of this struct is ultimately - // equal to the resolved alignment of this struct, we can safely ignore. - // - // If this struct is used down-stream in aligning a sub-struct, ignoring - // this struct in the context of a sub struct has the same effect since - // the other fields will be calculated and bubble-up. - if (nullptr == field->type_entry) { - self_resolving = true; - continue; - } + // TODO If we have no type_entry for the field, we've already failed to + // compile the program correctly. This stage1 compiler needs a deeper + // reworking to make this correct, or we can ignore the problem + // and make sure it is fixed in stage2. This workaround is for when + // there is a false positive of a dependency loop, of alignment depending + // on itself. When this false positive happens we assume a pointer-aligned + // field, which is usually fine but could be incorrectly over-aligned or + // even under-aligned. See https://github.com/ziglang/zig/issues/1512 + if (field->type_entry == nullptr) { + this_field_align = LLVMABIAlignmentOfType(g->target_data_ref, LLVMPointerType(LLVMInt8Type(), 0)); + } else { + if (type_is_invalid(field->type_entry)) { + struct_type->data.structure.resolve_status = ResolveStatusInvalid; + break; + } - if (type_is_invalid(field->type_entry)) { - struct_type->data.structure.resolve_status = ResolveStatusInvalid; - break; - } + if (!type_has_bits(field->type_entry)) + continue; - if (!type_has_bits(field->type_entry)) - continue; - - // alignment of structs is the alignment of the most-aligned field - if (struct_type->data.structure.layout != ContainerLayoutPacked) { if ((err = type_resolve(g, field->type_entry, ResolveStatusAlignmentKnown))) { struct_type->data.structure.resolve_status = ResolveStatusInvalid; break; } - uint32_t this_field_align = get_abi_alignment(g, field->type_entry); + this_field_align = get_abi_alignment(g, field->type_entry); assert(this_field_align != 0); - if (this_field_align > struct_type->data.structure.abi_alignment) { - struct_type->data.structure.abi_alignment = this_field_align; - } + } + // alignment of structs is the alignment of the most-aligned field + if (this_field_align > struct_type->data.structure.abi_alignment) { + struct_type->data.structure.abi_alignment = this_field_align; } } @@ -2732,14 +2734,6 @@ static Error resolve_struct_alignment(CodeGen *g, ZigType *struct_type) { return ErrorSemanticAnalyzeFail; } - if ( self_resolving - && field_count > 0 - ) { - // If we get here it's due to self-referencing this struct before it has been fully resolved. - // In this case, set alignment to target pointer default. - struct_type->data.structure.abi_alignment = LLVMABIAlignmentOfType(g->target_data_ref, - LLVMPointerType(LLVMInt8Type(), 0)); - } struct_type->data.structure.resolve_status = ResolveStatusAlignmentKnown; return ErrorNone; }