parseh: support anonymous enums and enums with initializers

This commit is contained in:
Andrew Kelley 2016-04-22 21:43:48 -07:00
parent 8187396f64
commit 66163692ad
5 changed files with 240 additions and 193 deletions

View File

@ -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:

View File

@ -42,6 +42,7 @@ struct Context {
HashMap<Buf *, TypeTableEntry *, buf_hash, buf_eql_buf> struct_type_table;
HashMap<Buf *, TypeTableEntry *, buf_hash, buf_eql_buf> struct_decl_table;
HashMap<Buf *, TypeTableEntry *, buf_hash, buf_eql_buf> enum_type_table;
HashMap<const void *, TypeTableEntry *, ptr_hash, ptr_eq> decl_table;
HashMap<Buf *, bool, buf_hash, buf_eql_buf> fn_table;
HashMap<Buf *, AstNode *, buf_hash, buf_eql_buf> 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<const EnumType*>(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<TypeEnumField>(field_count);
LLVMZigDIEnumerator **di_enumerators = allocate<LLVMZigDIEnumerator*>(field_count);
ZigList<AstNode *> 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<TypeEnumField>(field_count);
LLVMZigDIEnumerator **di_enumerators = allocate<LLVMZigDIEnumerator*>(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<MacroDefinitionRecord *>(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<ErrorMsg *> *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;

View File

@ -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;
}

View File

@ -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

View File

@ -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