diff --git a/chibicc.h b/chibicc.h index 463dfc981e2f0bd960bdab454ee906b5d0310142..e93dd4badd95723730502207f914788e6848f5ee 100644 --- a/chibicc.h +++ b/chibicc.h @@ -145,6 +145,7 @@ typedef enum { TY_FUNC, TY_ARRAY, TY_STRUCT, + TY_UNION, } TypeKind; struct Type { diff --git a/parse.c b/parse.c index ef95a972fc9596712b3ada2bd58c565dbf7cba19..dd46f8a8a582f69565b6800daa2abf0c99d069f6 100644 --- a/parse.c +++ b/parse.c @@ -27,7 +27,7 @@ struct VarScope { Var *var; }; -// Scope for struct tags +// Scope for struct or union tags typedef struct TagScope TagScope; struct TagScope { TagScope *next; @@ -72,6 +72,7 @@ static Node *relational(Token **rest, Token *tok); static Node *add(Token **rest, Token *tok); static Node *mul(Token **rest, Token *tok); static Type *struct_decl(Token **rest, Token *tok); +static Type *union_decl(Token **rest, Token *tok); static Node *postfix(Token **rest, Token *tok); static Node *unary(Token **rest, Token *tok); static Node *primary(Token **rest, Token *tok); @@ -225,6 +226,9 @@ static Type *typespec(Token **rest, Token *tok) { if (equal(tok, "struct")) return struct_decl(rest, tok->next); + if (equal(tok, "union")) + return union_decl(rest, tok->next); + error_tok(tok, "typename expected"); } @@ -310,7 +314,8 @@ static Node *declaration(Token **rest, Token *tok) { // Returns true if a given token represents a type. static bool is_typename(Token *tok) { - return equal(tok, "char") || equal(tok, "int") || equal(tok, "struct"); + return equal(tok, "char") || equal(tok, "int") || equal(tok, "struct") || + equal(tok, "union"); } // stmt = "return" expr ";" @@ -626,9 +631,9 @@ static void struct_members(Token **rest, Token *tok, Type *ty) { ty->members = head.next; } -// struct-decl = ident? "{" struct-members -static Type *struct_decl(Token **rest, Token *tok) { - // Read a struct tag. +// struct-union-decl = ident? ("{" struct-members)? +static Type *struct_union_decl(Token **rest, Token *tok) { + // Read a tag. Token *tag = NULL; if (tok->kind == TK_IDENT) { tag = tok; @@ -649,6 +654,17 @@ static Type *struct_decl(Token **rest, Token *tok) { struct_members(rest, tok->next, ty); ty->align = 1; + // Register the struct type if a name was given. + if (tag) + push_tag_scope(tag, ty); + return ty; +} + +// struct-decl = struct-union-decl +static Type *struct_decl(Token **rest, Token *tok) { + Type *ty = struct_union_decl(rest, tok); + ty->kind = TY_STRUCT; + // Assign offsets within the struct to members. int offset = 0; for (Member *mem = ty->members; mem; mem = mem->next) { @@ -660,10 +676,24 @@ static Type *struct_decl(Token **rest, Token *tok) { ty->align = mem->ty->align; } ty->size = align_to(offset, ty->align); + return ty; +} - // Register the struct type if a name was given. - if (tag) - push_tag_scope(tag, ty); +// union-decl = struct-union-decl +static Type *union_decl(Token **rest, Token *tok) { + Type *ty = struct_union_decl(rest, tok); + ty->kind = TY_UNION; + + // If union, we don't have to assign offsets because they + // are already initialized to zero. We need to compute the + // alignment and the size though. + for (Member *mem = ty->members; mem; mem = mem->next) { + if (ty->align < mem->ty->align) + ty->align = mem->ty->align; + if (ty->size < mem->ty->size) + ty->size = mem->ty->size; + } + ty->size = align_to(ty->size, ty->align); return ty; } @@ -677,8 +707,8 @@ static Member *get_struct_member(Type *ty, Token *tok) { static Node *struct_ref(Node *lhs, Token *tok) { add_type(lhs); - if (lhs->ty->kind != TY_STRUCT) - error_tok(lhs->tok, "not a struct"); + if (lhs->ty->kind != TY_STRUCT && lhs->ty->kind != TY_UNION) + error_tok(lhs->tok, "not a struct nor a union"); Node *node = new_unary(ND_MEMBER, lhs, tok); node->member = get_struct_member(lhs->ty, tok); diff --git a/test/union.c b/test/union.c new file mode 100644 index 0000000000000000000000000000000000000000..f891e8c46cb8c65fc393945afd84ba46385c763c --- /dev/null +++ b/test/union.c @@ -0,0 +1,12 @@ +#include "test.h" + +int main() { + ASSERT(8, ({ union { int a; char b[6]; } x; sizeof(x); })); + ASSERT(3, ({ union { int a; char b[4]; } x; x.a = 515; x.b[0]; })); + ASSERT(2, ({ union { int a; char b[4]; } x; x.a = 515; x.b[1]; })); + ASSERT(0, ({ union { int a; char b[4]; } x; x.a = 515; x.b[2]; })); + ASSERT(0, ({ union { int a; char b[4]; } x; x.a = 515; x.b[3]; })); + + printf("OK\n"); + return 0; +} diff --git a/tokenize.c b/tokenize.c index 4fea2364cec3c8dcddd587e903d14cbaabd1880c..5da1b699f90f9ba18b344283062f9532769b31b7 100644 --- a/tokenize.c +++ b/tokenize.c @@ -117,7 +117,7 @@ static int from_hex(char c) { static bool is_keyword(Token *tok) { static char *kw[] = { "return", "if", "else", "for", "while", "int", "sizeof", "char", - "struct", + "struct", "union", }; for (int i = 0; i < sizeof(kw) / sizeof(*kw); i++)