Commit 3ef6a00bb8

Andrew Kelley <superjoe30@gmail.com>
2018-01-30 17:52:03
add compile error for duplicate struct, enum, union fields
closes #730
1 parent 0995a81
src/all_types.hpp
@@ -331,12 +331,14 @@ struct TypeEnumField {
     Buf *name;
     BigInt value;
     uint32_t decl_index;
+    AstNode *decl_node;
 };
 
 struct TypeUnionField {
     Buf *name;
     TypeEnumField *enum_field;
     TypeTableEntry *type_entry;
+    AstNode *decl_node;
     uint32_t gen_index;
 };
 
@@ -961,6 +963,7 @@ struct TypeStructField {
     size_t packed_bits_offset;
     size_t packed_bits_size;
     size_t unaligned_bit_count;
+    AstNode *decl_node;
 };
 struct TypeTableEntryStruct {
     AstNode *decl_node;
@@ -982,6 +985,8 @@ struct TypeTableEntryStruct {
     bool zero_bits_loop_flag;
     bool zero_bits_known;
     uint32_t abi_alignment; // also figured out with zero_bits pass
+
+    HashMap<Buf *, TypeStructField *, buf_hash, buf_eql_buf> fields_by_name;
 };
 
 struct TypeTableEntryMaybe {
@@ -1013,6 +1018,8 @@ struct TypeTableEntryEnum {
 
     bool generate_name_table;
     LLVMValueRef name_table;
+
+    HashMap<Buf *, TypeEnumField *, buf_hash, buf_eql_buf> fields_by_name;
 };
 
 uint32_t type_ptr_hash(const TypeTableEntry *ptr);
@@ -1045,6 +1052,8 @@ struct TypeTableEntryUnion {
 
     uint32_t union_size_bytes;
     TypeTableEntry *most_aligned_union_member;
+
+    HashMap<Buf *, TypeUnionField *, buf_hash, buf_eql_buf> fields_by_name;
 };
 
 struct FnGenParamInfo {
src/analyze.cpp
@@ -633,20 +633,27 @@ TypeTableEntry *get_array_type(CodeGen *g, TypeTableEntry *child_type, uint64_t
 
 static void slice_type_common_init(CodeGen *g, TypeTableEntry *pointer_type, TypeTableEntry *entry) {
     unsigned element_count = 2;
+    Buf *ptr_field_name = buf_create_from_str("ptr");
+    Buf *len_field_name = buf_create_from_str("len");
+
     entry->data.structure.layout = ContainerLayoutAuto;
     entry->data.structure.is_slice = true;
     entry->data.structure.src_field_count = element_count;
     entry->data.structure.gen_field_count = element_count;
     entry->data.structure.fields = allocate<TypeStructField>(element_count);
-    entry->data.structure.fields[slice_ptr_index].name = buf_create_from_str("ptr");
+    entry->data.structure.fields_by_name.init(element_count);
+    entry->data.structure.fields[slice_ptr_index].name = ptr_field_name;
     entry->data.structure.fields[slice_ptr_index].type_entry = pointer_type;
     entry->data.structure.fields[slice_ptr_index].src_index = slice_ptr_index;
     entry->data.structure.fields[slice_ptr_index].gen_index = 0;
-    entry->data.structure.fields[slice_len_index].name = buf_create_from_str("len");
+    entry->data.structure.fields[slice_len_index].name = len_field_name;
     entry->data.structure.fields[slice_len_index].type_entry = g->builtin_types.entry_usize;
     entry->data.structure.fields[slice_len_index].src_index = slice_len_index;
     entry->data.structure.fields[slice_len_index].gen_index = 1;
 
+    entry->data.structure.fields_by_name.put(ptr_field_name, &entry->data.structure.fields[slice_ptr_index]);
+    entry->data.structure.fields_by_name.put(len_field_name, &entry->data.structure.fields[slice_len_index]);
+
     assert(type_has_zero_bits_known(pointer_type->data.pointer.child_type));
     if (pointer_type->data.pointer.child_type->zero_bits) {
         entry->data.structure.gen_field_count = 1;
@@ -1555,6 +1562,7 @@ TypeTableEntry *get_struct_type(CodeGen *g, const char *type_name, const char *f
     struct_type->data.structure.zero_bits_known = true;
     struct_type->data.structure.complete = true;
     struct_type->data.structure.fields = allocate<TypeStructField>(field_count);
+    struct_type->data.structure.fields_by_name.init(field_count);
 
     ZigLLVMDIType **di_element_types = allocate<ZigLLVMDIType*>(field_count);
     LLVMTypeRef *element_types = allocate<LLVMTypeRef>(field_count);
@@ -1566,6 +1574,9 @@ TypeTableEntry *get_struct_type(CodeGen *g, const char *type_name, const char *f
         field->type_entry = field_types[i];
         field->src_index = i;
         field->gen_index = i;
+
+        auto prev_entry = struct_type->data.structure.fields_by_name.put_unique(field->name, field);
+        assert(prev_entry == nullptr);
     }
 
     struct_type->type_ref = LLVMStructCreateNamed(LLVMGetGlobalContext(), type_name);
@@ -2102,6 +2113,7 @@ static void resolve_enum_zero_bits(CodeGen *g, TypeTableEntry *enum_type) {
 
     enum_type->data.enumeration.src_field_count = field_count;
     enum_type->data.enumeration.fields = allocate<TypeEnumField>(field_count);
+    enum_type->data.enumeration.fields_by_name.init(field_count);
 
     Scope *scope = &enum_type->data.enumeration.decls_scope->base;
 
@@ -2139,6 +2151,7 @@ static void resolve_enum_zero_bits(CodeGen *g, TypeTableEntry *enum_type) {
         TypeEnumField *type_enum_field = &enum_type->data.enumeration.fields[field_i];
         type_enum_field->name = field_node->data.struct_field.name;
         type_enum_field->decl_index = field_i;
+        type_enum_field->decl_node = field_node;
 
         if (field_node->data.struct_field.type != nullptr) {
             ErrorMsg *msg = add_node_error(g, field_node->data.struct_field.type,
@@ -2147,6 +2160,15 @@ static void resolve_enum_zero_bits(CodeGen *g, TypeTableEntry *enum_type) {
                     buf_sprintf("consider 'union(enum)' here"));
         }
 
+        auto field_entry = enum_type->data.enumeration.fields_by_name.put_unique(type_enum_field->name, type_enum_field);
+        if (field_entry != nullptr) {
+            ErrorMsg *msg = add_node_error(g, field_node,
+                buf_sprintf("duplicate enum field: '%s'", buf_ptr(type_enum_field->name)));
+            add_error_note(g, msg, field_entry->value->decl_node, buf_sprintf("other field here"));
+            enum_type->data.enumeration.is_invalid = true;
+            continue;
+        }
+
         AstNode *tag_value = field_node->data.struct_field.value;
 
         // In this first pass we resolve explicit tag values.
@@ -2242,6 +2264,7 @@ static void resolve_struct_zero_bits(CodeGen *g, TypeTableEntry *struct_type) {
     size_t field_count = decl_node->data.container_decl.fields.length;
     struct_type->data.structure.src_field_count = (uint32_t)field_count;
     struct_type->data.structure.fields = allocate<TypeStructField>(field_count);
+    struct_type->data.structure.fields_by_name.init(field_count);
 
     Scope *scope = &struct_type->data.structure.decls_scope->base;
 
@@ -2250,6 +2273,7 @@ static void resolve_struct_zero_bits(CodeGen *g, TypeTableEntry *struct_type) {
         AstNode *field_node = decl_node->data.container_decl.fields.at(i);
         TypeStructField *type_struct_field = &struct_type->data.structure.fields[i];
         type_struct_field->name = field_node->data.struct_field.name;
+        type_struct_field->decl_node = field_node;
 
         if (field_node->data.struct_field.type == nullptr) {
             add_node_error(g, field_node, buf_sprintf("struct field missing type"));
@@ -2257,6 +2281,15 @@ static void resolve_struct_zero_bits(CodeGen *g, TypeTableEntry *struct_type) {
             continue;
         }
 
+        auto field_entry = struct_type->data.structure.fields_by_name.put_unique(type_struct_field->name, type_struct_field);
+        if (field_entry != nullptr) {
+            ErrorMsg *msg = add_node_error(g, field_node,
+                buf_sprintf("duplicate struct field: '%s'", buf_ptr(type_struct_field->name)));
+            add_error_note(g, msg, field_entry->value->decl_node, buf_sprintf("other field here"));
+            struct_type->data.structure.is_invalid = true;
+            continue;
+        }
+
         TypeTableEntry *field_type = analyze_type_expr(g, scope, field_node->data.struct_field.type);
         type_struct_field->type_entry = field_type;
         type_struct_field->src_index = i;
@@ -2344,6 +2377,7 @@ static void resolve_union_zero_bits(CodeGen *g, TypeTableEntry *union_type) {
     }
     union_type->data.unionation.src_field_count = field_count;
     union_type->data.unionation.fields = allocate<TypeUnionField>(field_count);
+    union_type->data.unionation.fields_by_name.init(field_count);
 
     uint32_t biggest_align_bytes = 0;
 
@@ -2395,6 +2429,7 @@ static void resolve_union_zero_bits(CodeGen *g, TypeTableEntry *union_type) {
         tag_type->data.enumeration.layout = ContainerLayoutAuto;
         tag_type->data.enumeration.src_field_count = field_count;
         tag_type->data.enumeration.fields = allocate<TypeEnumField>(field_count);
+        tag_type->data.enumeration.fields_by_name.init(field_count);
         tag_type->data.enumeration.decls_scope = union_type->data.unionation.decls_scope;
         tag_type->data.enumeration.complete = true;
     } else if (enum_type_node != nullptr) {
@@ -2424,6 +2459,16 @@ static void resolve_union_zero_bits(CodeGen *g, TypeTableEntry *union_type) {
         Buf *field_name = field_node->data.struct_field.name;
         TypeUnionField *union_field = &union_type->data.unionation.fields[i];
         union_field->name = field_node->data.struct_field.name;
+        union_field->decl_node = field_node;
+
+        auto field_entry = union_type->data.unionation.fields_by_name.put_unique(union_field->name, union_field);
+        if (field_entry != nullptr) {
+            ErrorMsg *msg = add_node_error(g, field_node,
+                buf_sprintf("duplicate union field: '%s'", buf_ptr(union_field->name)));
+            add_error_note(g, msg, field_entry->value->decl_node, buf_sprintf("other field here"));
+            union_type->data.unionation.is_invalid = true;
+            continue;
+        }
 
         TypeTableEntry *field_type;
         if (field_node->data.struct_field.type == nullptr) {
@@ -2456,6 +2501,10 @@ static void resolve_union_zero_bits(CodeGen *g, TypeTableEntry *union_type) {
             union_field->enum_field = &tag_type->data.enumeration.fields[i];
             union_field->enum_field->name = field_name;
             union_field->enum_field->decl_index = i;
+            union_field->enum_field->decl_node = field_node;
+
+            auto prev_entry = tag_type->data.enumeration.fields_by_name.put_unique(union_field->enum_field->name, union_field->enum_field);
+            assert(prev_entry == nullptr); // caught by union de-duplicator above
 
             AstNode *tag_value = field_node->data.struct_field.value;
             // In this first pass we resolve explicit tag values.
@@ -3499,37 +3548,35 @@ FnTableEntry *scope_get_fn_if_root(Scope *scope) {
 }
 
 TypeEnumField *find_enum_type_field(TypeTableEntry *enum_type, Buf *name) {
-    for (uint32_t i = 0; i < enum_type->data.enumeration.src_field_count; i += 1) {
-        TypeEnumField *type_enum_field = &enum_type->data.enumeration.fields[i];
-        if (buf_eql_buf(type_enum_field->name, name)) {
-            return type_enum_field;
-        }
-    }
-    return nullptr;
+    assert(enum_type->id == TypeTableEntryIdEnum);
+    if (enum_type->data.enumeration.src_field_count == 0)
+        return nullptr;
+    auto entry = enum_type->data.enumeration.fields_by_name.maybe_get(name);
+    if (entry == nullptr)
+        return nullptr;
+    return entry->value;
 }
 
 TypeStructField *find_struct_type_field(TypeTableEntry *type_entry, Buf *name) {
     assert(type_entry->id == TypeTableEntryIdStruct);
     assert(type_entry->data.structure.complete);
-    for (uint32_t i = 0; i < type_entry->data.structure.src_field_count; i += 1) {
-        TypeStructField *field = &type_entry->data.structure.fields[i];
-        if (buf_eql_buf(field->name, name)) {
-            return field;
-        }
-    }
-    return nullptr;
+    if (type_entry->data.structure.src_field_count == 0)
+        return nullptr;
+    auto entry = type_entry->data.structure.fields_by_name.maybe_get(name);
+    if (entry == nullptr)
+        return nullptr;
+    return entry->value;
 }
 
 TypeUnionField *find_union_type_field(TypeTableEntry *type_entry, Buf *name) {
     assert(type_entry->id == TypeTableEntryIdUnion);
     assert(type_entry->data.unionation.zero_bits_known);
-    for (uint32_t i = 0; i < type_entry->data.unionation.src_field_count; i += 1) {
-        TypeUnionField *field = &type_entry->data.unionation.fields[i];
-        if (buf_eql_buf(field->enum_field->name, name)) {
-            return field;
-        }
-    }
-    return nullptr;
+    if (type_entry->data.unionation.src_field_count == 0)
+        return nullptr;
+    auto entry = type_entry->data.unionation.fields_by_name.maybe_get(name);
+    if (entry == nullptr)
+        return nullptr;
+    return entry->value;
 }
 
 TypeUnionField *find_union_field_by_tag(TypeTableEntry *type_entry, const BigInt *tag) {
src/analyze.hpp
@@ -60,8 +60,8 @@ bool type_is_complete(TypeTableEntry *type_entry);
 bool type_is_invalid(TypeTableEntry *type_entry);
 bool type_has_zero_bits_known(TypeTableEntry *type_entry);
 void resolve_container_type(CodeGen *g, TypeTableEntry *type_entry);
-TypeStructField *find_struct_type_field(TypeTableEntry *type_entry, Buf *name);
 ScopeDecls *get_container_scope(TypeTableEntry *type_entry);
+TypeStructField *find_struct_type_field(TypeTableEntry *type_entry, Buf *name);
 TypeEnumField *find_enum_type_field(TypeTableEntry *enum_type, Buf *name);
 TypeUnionField *find_union_type_field(TypeTableEntry *type_entry, Buf *name);
 TypeEnumField *find_enum_field_by_tag(TypeTableEntry *enum_type, const BigInt *tag);
test/compile_errors.zig
@@ -1,6 +1,43 @@
 const tests = @import("tests.zig");
 
 pub fn addCases(cases: &tests.CompileErrorContext) void {
+    cases.add("duplicate struct field",
+        \\const Foo = struct {
+        \\    Bar: i32,
+        \\    Bar: usize,
+        \\};
+        \\export fn entry() void {
+        \\    const a: Foo = undefined;
+        \\}
+    ,
+        ".tmp_source.zig:3:5: error: duplicate struct field: 'Bar'",
+        ".tmp_source.zig:2:5: note: other field here");
+
+    cases.add("duplicate union field",
+        \\const Foo = union {
+        \\    Bar: i32,
+        \\    Bar: usize,
+        \\};
+        \\export fn entry() void {
+        \\    const a: Foo = undefined;
+        \\}
+    ,
+        ".tmp_source.zig:3:5: error: duplicate union field: 'Bar'",
+        ".tmp_source.zig:2:5: note: other field here");
+
+    cases.add("duplicate enum field",
+        \\const Foo = enum {
+        \\    Bar,
+        \\    Bar,
+        \\};
+        \\
+        \\export fn entry() void {
+        \\    const a: Foo = undefined;
+        \\}
+    ,
+        ".tmp_source.zig:3:5: error: duplicate enum field: 'Bar'",
+        ".tmp_source.zig:2:5: note: other field here");
+
     cases.add("calling function with naked calling convention",
         \\export fn entry() void {
         \\    foo();
@@ -10,7 +47,6 @@ pub fn addCases(cases: &tests.CompileErrorContext) void {
         ".tmp_source.zig:2:5: error: unable to call function with naked calling convention",
         ".tmp_source.zig:4:9: note: declared here");
 
-
     cases.add("function with invalid return type",
         \\export fn foo() boid {}
     , ".tmp_source.zig:1:17: error: use of undeclared identifier 'boid'");