From 66163692ad35048e0bab5861e371067b63ee0f88 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 22 Apr 2016 21:43:48 -0700 Subject: [PATCH] parseh: support anonymous enums and enums with initializers --- src/ast_render.cpp | 3 +- src/parseh.cpp | 396 +++++++++++++++++++++++++-------------------- src/util.cpp | 8 + src/util.hpp | 2 + test/run_tests.cpp | 24 +-- 5 files changed, 240 insertions(+), 193 deletions(-) diff --git a/src/ast_render.cpp b/src/ast_render.cpp index d39f858ed1..5e7a5c3fce 100644 --- a/src/ast_render.cpp +++ b/src/ast_render.cpp @@ -523,7 +523,8 @@ static void render_node(AstRender *ar, AstNode *node) { AstNode *lhs = node->data.field_access_expr.struct_expr; Buf *rhs = &node->data.field_access_expr.field_name; render_node(ar, lhs); - fprintf(ar->f, ".%s", buf_ptr(rhs)); + fprintf(ar->f, "."); + print_symbol(ar, rhs); break; } case NodeTypeUse: diff --git a/src/parseh.cpp b/src/parseh.cpp index 4ae426f1c7..fc141743a8 100644 --- a/src/parseh.cpp +++ b/src/parseh.cpp @@ -42,6 +42,7 @@ struct Context { HashMap struct_type_table; HashMap struct_decl_table; HashMap enum_type_table; + HashMap decl_table; HashMap fn_table; HashMap macro_table; SourceManager *source_manager; @@ -58,6 +59,7 @@ static TypeTableEntry *resolve_qual_type_with_table(Context *c, QualType qt, con static TypeTableEntry *resolve_qual_type(Context *c, QualType qt, const Decl *decl); static TypeTableEntry *resolve_record_decl(Context *c, const RecordDecl *record_decl); +static TypeTableEntry *resolve_enum_decl(Context *c, const EnumDecl *enum_decl); __attribute__ ((format (printf, 3, 4))) @@ -292,6 +294,7 @@ static const char *decl_name(const Decl *decl) { static AstNode *add_typedef_node(Context *c, TypeTableEntry *type_decl) { assert(type_decl); + assert(type_decl->id == TypeTableEntryIdTypeDecl); AstNode *node = create_type_decl_node(c, buf_ptr(&type_decl->name), make_type_node(c, type_decl->data.type_decl.child_type)); @@ -310,6 +313,26 @@ static AstNode *add_const_var_node(Context *c, Buf *name, TypeTableEntry *type_e return node; } +static AstNode *create_ap_num_lit_node(Context *c, const Decl *source_decl, + const llvm::APSInt &aps_int) +{ + if (aps_int.isSigned()) { + if (aps_int > INT64_MAX || aps_int < INT64_MIN) { + emit_warning(c, source_decl, "integer overflow\n"); + return nullptr; + } else { + return create_num_lit_signed(c, aps_int.getExtValue()); + } + } else { + if (aps_int > INT64_MAX) { + emit_warning(c, source_decl, "integer overflow\n"); + return nullptr; + } else { + return create_num_lit_unsigned(c, aps_int.getExtValue()); + } + } +} + static bool is_c_void_type(Context *c, TypeTableEntry *type_entry) { while (type_entry->id == TypeTableEntryIdTypeDecl) { if (type_entry == c->codegen->builtin_types.entry_c_void) { @@ -586,18 +609,7 @@ static TypeTableEntry *resolve_type_with_table(Context *c, const Type *ty, const case Type::Enum: { const EnumType *enum_ty = static_cast(ty); - Buf *record_name = buf_create_from_str(decl_name(enum_ty->getDecl())); - if (buf_len(record_name) == 0) { - emit_warning(c, decl, "unhandled anonymous enum"); - return c->codegen->builtin_types.entry_invalid; - } - - auto entry = type_table->maybe_get(record_name); - if (!entry) { - return c->codegen->builtin_types.entry_invalid; - } - - return entry->value; + return resolve_enum_decl(c, enum_ty->getDecl()); } case Type::ConstantArray: { @@ -759,146 +771,6 @@ static void add_alias(Context *c, const char *new_name, const char *target_name) c->aliases.append(alias_node); } -static void visit_enum_decl(Context *c, const EnumDecl *enum_decl) { - const char *raw_name = decl_name(enum_decl); - // we have no interest in top level anonymous enums since they're - // not exposing anything. - if (raw_name[0] == 0) { - return; - } - - Buf *bare_name = buf_create_from_str(raw_name); - Buf *full_type_name = buf_sprintf("enum_%s", buf_ptr(bare_name)); - - if (c->enum_type_table.maybe_get(bare_name)) { - // we've already seen it - return; - } - - const EnumDecl *enum_def = enum_decl->getDefinition(); - - if (!enum_def) { - TypeTableEntry *typedecl_type = get_typedecl_type(c->codegen, buf_ptr(full_type_name), - c->codegen->builtin_types.entry_u8); - c->enum_type_table.put(bare_name, typedecl_type); - - // this is a type that we can point to but that's it, same as `struct Foo;`. - add_typedef_node(c, typedecl_type); - add_alias(c, buf_ptr(bare_name), buf_ptr(full_type_name)); - return; - } - - // count and validate - uint32_t field_count = 0; - for (auto it = enum_def->enumerator_begin(), - it_end = enum_def->enumerator_end(); - it != it_end; ++it, field_count += 1) - { - const EnumConstantDecl *enum_const = *it; - if (enum_const->getInitExpr()) { - emit_warning(c, enum_const, "skipping enum %s - has init expression\n", buf_ptr(bare_name)); - return; - } - } - - TypeTableEntry *enum_type = get_partial_container_type(c->codegen, c->import, - ContainerKindEnum, c->source_node, buf_ptr(full_type_name)); - - enum_type->data.enumeration.gen_field_count = 0; - enum_type->data.enumeration.complete = true; - - TypeTableEntry *tag_type_entry = get_smallest_unsigned_int_type(c->codegen, field_count); - enum_type->data.enumeration.tag_type = tag_type_entry; - - c->enum_type_table.put(bare_name, enum_type); - // make an alias without the "enum_" prefix. this will get emitted at the - // end if it doesn't conflict with anything else - add_alias(c, buf_ptr(bare_name), buf_ptr(full_type_name)); - - enum_type->data.enumeration.field_count = field_count; - enum_type->data.enumeration.fields = allocate(field_count); - LLVMZigDIEnumerator **di_enumerators = allocate(field_count); - - ZigList var_decls = {0}; - uint32_t i = 0; - for (auto it = enum_def->enumerator_begin(), - it_end = enum_def->enumerator_end(); - it != it_end; ++it, i += 1) - { - const EnumConstantDecl *enum_const = *it; - - Buf *enum_val_name = buf_create_from_str(decl_name(enum_const)); - Buf *field_name; - if (buf_starts_with_buf(enum_val_name, bare_name)) { - Buf *slice = buf_slice(enum_val_name, buf_len(bare_name), buf_len(enum_val_name)); - if (valid_symbol_starter(buf_ptr(slice)[0])) { - field_name = slice; - } else { - field_name = buf_sprintf("_%s", buf_ptr(slice)); - } - } else { - field_name = enum_val_name; - } - - TypeEnumField *type_enum_field = &enum_type->data.enumeration.fields[i]; - type_enum_field->name = field_name; - type_enum_field->type_entry = c->codegen->builtin_types.entry_void; - type_enum_field->value = i; - - di_enumerators[i] = LLVMZigCreateDebugEnumerator(c->codegen->dbuilder, buf_ptr(type_enum_field->name), i); - - - // in C each enum value is in the global namespace. so we put them there too. - // at this point we can rely on the enum emitting successfully - AstNode *field_access_node = create_field_access_node(c, buf_ptr(full_type_name), buf_ptr(field_name)); - AstNode *var_node = create_var_decl_node(c, buf_ptr(enum_val_name), field_access_node); - var_decls.append(var_node); - c->global_value_table.put(enum_val_name, {enum_type, true}); - } - - // create llvm type for root struct - enum_type->type_ref = tag_type_entry->type_ref; - - // create debug type for tag - unsigned line = c->source_node ? (c->source_node->line + 1) : 0; - uint64_t debug_size_in_bits = 8*LLVMStoreSizeOfType(c->codegen->target_data_ref, enum_type->type_ref); - uint64_t debug_align_in_bits = 8*LLVMABISizeOfType(c->codegen->target_data_ref, enum_type->type_ref); - LLVMZigDIType *tag_di_type = LLVMZigCreateDebugEnumerationType(c->codegen->dbuilder, - LLVMZigFileToScope(c->import->di_file), buf_ptr(bare_name), - c->import->di_file, line, - debug_size_in_bits, - debug_align_in_bits, - di_enumerators, field_count, tag_type_entry->di_type, ""); - - LLVMZigReplaceTemporary(c->codegen->dbuilder, enum_type->di_type, tag_di_type); - enum_type->di_type = tag_di_type; - - ////////// - - // now create top level decl for the type - AstNode *enum_node = create_node(c, NodeTypeStructDecl); - buf_init_from_buf(&enum_node->data.struct_decl.name, full_type_name); - enum_node->data.struct_decl.kind = ContainerKindEnum; - enum_node->data.struct_decl.top_level_decl.visib_mod = VisibModExport; - enum_node->data.struct_decl.type_entry = enum_type; - - for (uint32_t i = 0; i < field_count; i += 1) { - TypeEnumField *type_enum_field = &enum_type->data.enumeration.fields[i]; - AstNode *type_node = make_type_node(c, type_enum_field->type_entry); - AstNode *field_node = create_struct_field_node(c, buf_ptr(type_enum_field->name), type_node); - enum_node->data.struct_decl.fields.append(field_node); - } - - normalize_parent_ptrs(enum_node); - c->root->data.root.top_level_decls.append(enum_node); - - for (int i = 0; i < var_decls.length; i += 1) { - AstNode *var_node = var_decls.at(i); - c->root->data.root.top_level_decls.append(var_node); - } - -} - static void replace_with_fwd_decl(Context *c, TypeTableEntry *struct_type, Buf *full_type_name) { unsigned line = c->source_node ? c->source_node->line : 0; LLVMZigDIType *replacement_di_type = LLVMZigCreateDebugForwardDeclType(c->codegen->dbuilder, @@ -909,7 +781,192 @@ static void replace_with_fwd_decl(Context *c, TypeTableEntry *struct_type, Buf * struct_type->di_type = replacement_di_type; } +static TypeTableEntry *resolve_enum_decl(Context *c, const EnumDecl *enum_decl) { + auto existing_entry = c->decl_table.maybe_get((void*)enum_decl); + if (existing_entry) { + return existing_entry->value; + } + + const char *raw_name = decl_name(enum_decl); + + Buf *bare_name; + if (raw_name[0] == 0) { + bare_name = buf_sprintf("anon_$%" PRIu32, get_next_node_index(c)); + } else { + bare_name = buf_create_from_str(raw_name); + } + + Buf *full_type_name = buf_sprintf("enum_%s", buf_ptr(bare_name)); + + const EnumDecl *enum_def = enum_decl->getDefinition(); + if (!enum_def) { + TypeTableEntry *enum_type = get_partial_container_type(c->codegen, c->import, + ContainerKindEnum, c->source_node, buf_ptr(full_type_name)); + c->enum_type_table.put(bare_name, enum_type); + c->decl_table.put(enum_decl, enum_type); + replace_with_fwd_decl(c, enum_type, full_type_name); + + return enum_type; + } + + bool pure_enum = true; + uint32_t field_count = 0; + for (auto it = enum_def->enumerator_begin(), + it_end = enum_def->enumerator_end(); + it != it_end; ++it, field_count += 1) + { + const EnumConstantDecl *enum_const = *it; + if (enum_const->getInitExpr()) { + pure_enum = false; + } + } + + TypeTableEntry *tag_type_entry = resolve_qual_type(c, enum_decl->getIntegerType(), enum_decl); + + if (pure_enum) { + TypeTableEntry *enum_type = get_partial_container_type(c->codegen, c->import, + ContainerKindEnum, c->source_node, buf_ptr(full_type_name)); + c->enum_type_table.put(bare_name, enum_type); + c->decl_table.put(enum_decl, enum_type); + + enum_type->data.enumeration.gen_field_count = 0; + enum_type->data.enumeration.complete = true; + enum_type->data.enumeration.tag_type = tag_type_entry; + + enum_type->data.enumeration.field_count = field_count; + enum_type->data.enumeration.fields = allocate(field_count); + LLVMZigDIEnumerator **di_enumerators = allocate(field_count); + + uint32_t i = 0; + for (auto it = enum_def->enumerator_begin(), + it_end = enum_def->enumerator_end(); + it != it_end; ++it, i += 1) + { + const EnumConstantDecl *enum_const = *it; + + Buf *enum_val_name = buf_create_from_str(decl_name(enum_const)); + Buf *field_name; + if (buf_starts_with_buf(enum_val_name, bare_name)) { + field_name = buf_slice(enum_val_name, buf_len(bare_name), buf_len(enum_val_name)); + } else { + field_name = enum_val_name; + } + + TypeEnumField *type_enum_field = &enum_type->data.enumeration.fields[i]; + type_enum_field->name = field_name; + type_enum_field->type_entry = c->codegen->builtin_types.entry_void; + type_enum_field->value = i; + + di_enumerators[i] = LLVMZigCreateDebugEnumerator(c->codegen->dbuilder, buf_ptr(type_enum_field->name), i); + + + // in C each enum value is in the global namespace. so we put them there too. + // at this point we can rely on the enum emitting successfully + AstNode *field_access_node = create_field_access_node(c, buf_ptr(full_type_name), buf_ptr(field_name)); + AstNode *var_node = create_var_decl_node(c, buf_ptr(enum_val_name), field_access_node); + c->root->data.root.top_level_decls.append(var_node); + + c->global_value_table.put(enum_val_name, {enum_type, true}); + } + + // create llvm type for root struct + enum_type->type_ref = tag_type_entry->type_ref; + + // create debug type for tag + unsigned line = c->source_node ? (c->source_node->line + 1) : 0; + uint64_t debug_size_in_bits = 8*LLVMStoreSizeOfType(c->codegen->target_data_ref, enum_type->type_ref); + uint64_t debug_align_in_bits = 8*LLVMABISizeOfType(c->codegen->target_data_ref, enum_type->type_ref); + LLVMZigDIType *tag_di_type = LLVMZigCreateDebugEnumerationType(c->codegen->dbuilder, + LLVMZigFileToScope(c->import->di_file), buf_ptr(bare_name), + c->import->di_file, line, + debug_size_in_bits, + debug_align_in_bits, + di_enumerators, field_count, tag_type_entry->di_type, ""); + + LLVMZigReplaceTemporary(c->codegen->dbuilder, enum_type->di_type, tag_di_type); + enum_type->di_type = tag_di_type; + + return enum_type; + } else { + TypeTableEntry *enum_type = get_typedecl_type(c->codegen, buf_ptr(full_type_name), tag_type_entry); + c->enum_type_table.put(bare_name, enum_type); + c->decl_table.put(enum_decl, enum_type); + + // add variables for all the values with enum_type + for (auto it = enum_def->enumerator_begin(), + it_end = enum_def->enumerator_end(); + it != it_end; ++it) + { + const EnumConstantDecl *enum_const = *it; + AstNode *num_lit_node = create_ap_num_lit_node(c, enum_decl, enum_const->getInitVal()); + if (!num_lit_node) { + return c->codegen->builtin_types.entry_invalid; + } + + Buf *enum_val_name = buf_create_from_str(decl_name(enum_const)); + + AstNode *type_node = make_type_node(c, enum_type); + AstNode *var_decl_node = create_typed_var_decl_node(c, true, buf_ptr(enum_val_name), + type_node, num_lit_node); + + c->root->data.root.top_level_decls.append(var_decl_node); + c->global_value_table.put(enum_val_name, {enum_type, true}); + + } + + return enum_type; + } +} + +static void visit_enum_decl(Context *c, const EnumDecl *enum_decl) { + TypeTableEntry *enum_type = resolve_enum_decl(c, enum_decl); + + if (enum_type->id == TypeTableEntryIdInvalid) { + return; + } + + // make an alias without the "enum_" prefix. this will get emitted at the + // end if it doesn't conflict with anything else + if (decl_name(enum_decl)[0] != 0) { + add_alias(c, decl_name(enum_decl), buf_ptr(&enum_type->name)); + } + + if (enum_type->id == TypeTableEntryIdEnum) { + if (enum_type->data.enumeration.complete) { + // now create top level decl for the type + AstNode *enum_node = create_node(c, NodeTypeStructDecl); + buf_init_from_buf(&enum_node->data.struct_decl.name, &enum_type->name); + enum_node->data.struct_decl.kind = ContainerKindEnum; + enum_node->data.struct_decl.top_level_decl.visib_mod = VisibModExport; + enum_node->data.struct_decl.type_entry = enum_type; + + for (uint32_t i = 0; i < enum_type->data.enumeration.field_count; i += 1) { + TypeEnumField *type_enum_field = &enum_type->data.enumeration.fields[i]; + AstNode *type_node = make_type_node(c, type_enum_field->type_entry); + AstNode *field_node = create_struct_field_node(c, buf_ptr(type_enum_field->name), type_node); + enum_node->data.struct_decl.fields.append(field_node); + } + + normalize_parent_ptrs(enum_node); + c->root->data.root.top_level_decls.append(enum_node); + } else { + TypeTableEntry *typedecl_type = get_typedecl_type(c->codegen, buf_ptr(&enum_type->name), + c->codegen->builtin_types.entry_u8); + add_typedef_node(c, typedecl_type); + } + } else if (enum_type->id == TypeTableEntryIdTypeDecl) { + add_typedef_node(c, enum_type); + } else { + zig_unreachable(); + } +} + static TypeTableEntry *resolve_record_decl(Context *c, const RecordDecl *record_decl) { + auto existing_entry = c->decl_table.maybe_get((void*)record_decl); + if (existing_entry) { + return existing_entry->value; + } + const char *raw_name = decl_name(record_decl); if (!record_decl->isStruct()) { @@ -922,11 +979,6 @@ static TypeTableEntry *resolve_record_decl(Context *c, const RecordDecl *record_ bare_name = buf_sprintf("anon_$%" PRIu32, get_next_node_index(c)); } else { bare_name = buf_create_from_str(raw_name); - - auto existing_entry = c->struct_type_table.maybe_get(bare_name); - if (existing_entry) { - return existing_entry->value; - } } Buf *full_type_name = buf_sprintf("struct_%s", buf_ptr(bare_name)); @@ -936,6 +988,7 @@ static TypeTableEntry *resolve_record_decl(Context *c, const RecordDecl *record_ ContainerKindStruct, c->source_node, buf_ptr(full_type_name)); c->struct_type_table.put(bare_name, struct_type); + c->decl_table.put(record_decl, struct_type); RecordDecl *record_def = record_decl->getDefinition(); unsigned line = c->source_node ? c->source_node->line : 0; @@ -1125,23 +1178,9 @@ static void visit_var_decl(Context *c, const VarDecl *var_decl) { "ignoring variable '%s' - int initializer for non int type\n", buf_ptr(name)); return; } - llvm::APSInt aps_int = ap_value->getInt(); - if (aps_int.isSigned()) { - if (aps_int > INT64_MAX || aps_int < INT64_MIN) { - emit_warning(c, var_decl, - "ignoring variable '%s' - initializer overflow\n", buf_ptr(name)); - return; - } else { - init_node = create_num_lit_signed(c, aps_int.getExtValue()); - } - } else { - if (aps_int > UINT64_MAX) { - emit_warning(c, var_decl, - "ignoring variable '%s' - initializer overflow\n", buf_ptr(name)); - return; - } else { - init_node = create_num_lit_unsigned(c, aps_int.getExtValue()); - } + init_node = create_ap_num_lit_node(c, var_decl, ap_value->getInt()); + if (!init_node) { + return; } break; } @@ -1360,7 +1399,7 @@ static void process_preprocessor_entities(Context *c, ASTUnit &unit) { case PreprocessedEntity::MacroDefinitionKind: { MacroDefinitionRecord *macro = static_cast(entity); - const char *name = macro->getName()->getNameStart(); + const char *raw_name = macro->getName()->getNameStart(); SourceRange range = macro->getSourceRange(); SourceLocation begin_loc = range.getBegin(); SourceLocation end_loc = range.getEnd(); @@ -1370,9 +1409,13 @@ static void process_preprocessor_entities(Context *c, ASTUnit &unit) { // we don't care about such things continue; } + Buf *name = buf_create_from_str(raw_name); + if (name_exists(c, name)) { + continue; + } const char *end_c = c->source_manager->getCharacterData(end_loc); - process_macro(c, &ctok, buf_create_from_str(name), end_c); + process_macro(c, &ctok, name, end_c); } } } @@ -1408,6 +1451,7 @@ int parse_h_file(ImportTableEntry *import, ZigList *errors, const ch c->enum_type_table.init(8); c->struct_type_table.init(8); c->struct_decl_table.init(8); + c->decl_table.init(8); c->fn_table.init(8); c->macro_table.init(8); c->codegen = codegen; diff --git a/src/util.cpp b/src/util.cpp index ceba4cde85..215c396fba 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -34,3 +34,11 @@ uint32_t uint64_hash(uint64_t i) { bool uint64_eq(uint64_t a, uint64_t b) { return a == b; } + +uint32_t ptr_hash(const void *ptr) { + return ((uintptr_t)ptr) % UINT32_MAX; +} + +bool ptr_eq(const void *a, const void *b) { + return a == b; +} diff --git a/src/util.hpp b/src/util.hpp index 8cf9f7f438..e6eeae2ae0 100644 --- a/src/util.hpp +++ b/src/util.hpp @@ -83,5 +83,7 @@ uint32_t int_hash(int i); bool int_eq(int a, int b); uint32_t uint64_hash(uint64_t i); bool uint64_eq(uint64_t a, uint64_t b); +uint32_t ptr_hash(const void *ptr); +bool ptr_eq(const void *a, const void *b); #endif diff --git a/test/run_tests.cpp b/test/run_tests.cpp index 618e75d717..d8f3c9a41d 100644 --- a/test/run_tests.cpp +++ b/test/run_tests.cpp @@ -1244,15 +1244,14 @@ enum Foo { FooB, Foo1, }; - )SOURCE", 1, R"OUTPUT(export enum enum_Foo { + )SOURCE", 3, R"(export enum enum_Foo { A, B, - _1, -} -pub const FooA = enum_Foo.A; + @"1", +})", R"(pub const FooA = enum_Foo.A; pub const FooB = enum_Foo.B; -pub const Foo1 = enum_Foo._1;)OUTPUT", - R"OUTPUT(pub const Foo = enum_Foo;)OUTPUT"); +pub const Foo1 = enum_Foo.@"1";)", + R"(pub const Foo = enum_Foo;)"); add_parseh_case("restrict -> noalias", R"SOURCE( void foo(void *restrict bar, void *restrict); @@ -1279,15 +1278,14 @@ enum Bar { BarB, }; void func(struct Foo *a, enum Bar **b); - )SOURCE", 3, R"OUTPUT(export struct struct_Foo { + )SOURCE", 5, R"OUTPUT(export struct struct_Foo { x: c_int, y: c_int, -} +})OUTPUT", R"OUTPUT( export enum enum_Bar { A, B, -} -pub const BarA = enum_Bar.A; +})OUTPUT", R"OUTPUT(pub const BarA = enum_Bar.A; pub const BarB = enum_Bar.B;)OUTPUT", "pub extern fn func(a: ?&struct_Foo, b: ?&?&enum_Bar);", R"OUTPUT(pub const Foo = struct_Foo; @@ -1325,12 +1323,6 @@ pub extern fn some_func(foo: ?&struct_Foo, x: c_int) -> ?&struct_Foo;)OUTPUT", )SOURCE", 1, R"OUTPUT(pub const CHANNEL_COUNT = 24;)OUTPUT"); - add_parseh_case("overide previous #define", R"SOURCE( -#define A_CHAR 'a' -#define A_CHAR 'b' - )SOURCE", 1, "pub const A_CHAR = 'b';"); - - add_parseh_case("#define referencing another #define", R"SOURCE( #define THING2 THING1 #define THING1 1234