Skip to content

Commit

Permalink
feat: allow incomplete struct
Browse files Browse the repository at this point in the history
  • Loading branch information
diohabara committed Jul 26, 2023
1 parent e06a742 commit 64b5565
Show file tree
Hide file tree
Showing 4 changed files with 47 additions and 10 deletions.
1 change: 1 addition & 0 deletions ccc.h
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,7 @@ Type *short_type();
Type *int_type();
Type *long_type();
Type *enum_type();
Type *struct_type();
Type *func_type(Type *return_ty);
Type *pointer_to(Type *base);
Type *array_of(Type *base, int size);
Expand Down
46 changes: 36 additions & 10 deletions parse.c
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ typedef struct VarScope VarScope;
struct VarScope {
VarScope *next;
char *name;
int depth;
Var *var;
Type *type_def;
Type *enum_ty;
Expand All @@ -16,6 +17,7 @@ typedef struct TagScope TagScope;
struct TagScope {
TagScope *next;
char *name;
int depth;
Type *ty;
};

Expand All @@ -29,19 +31,22 @@ VarList *globals;

VarScope *var_scope;
TagScope *tag_scope;
int scope_depth;

Node *current_switch;

Scope *enter_scope() {
Scope *sc = calloc(1, sizeof(Scope));
sc->var_scope = var_scope;
sc->tag_scope = tag_scope;
++scope_depth;
return sc;
}

void leave_scope(Scope *sc) {
var_scope = sc->var_scope;
tag_scope = sc->tag_scope;
--scope_depth;
}

// Find a variable or a typedef by name.
Expand Down Expand Up @@ -100,6 +105,7 @@ VarScope *push_scope(char *name) {
VarScope *sc = calloc(1, sizeof(VarScope));
sc->name = name;
sc->next = var_scope;
sc->depth = scope_depth;
var_scope = sc;
return sc;
}
Expand Down Expand Up @@ -375,28 +381,53 @@ void push_tag_scope(Token *tok, Type *ty) {
TagScope *sc = calloc(1, sizeof(TagScope));
sc->next = tag_scope;
sc->name = strndup(tok->str, tok->len);
sc->depth = scope_depth;
sc->ty = ty;
tag_scope = sc;
}

// struct-decl = "struct" ident
// | "struct" ident? "{" struct-member "}"
// struct-decl = "struct" iden | "struct" ident? ()"{" struct-member "}")?
Type *struct_decl() {
// read struct tag
expect("struct");
Token *tag = consume_ident();
if (tag && !peek("{")) {
TagScope *sc = find_tag(tag);
if (!sc) {
error_tok(tag, "unknown struct type");
Type *ty = struct_type();
push_tag_scope(tag, ty);
return ty;
}
if (sc->ty->kind != TY_STRUCT) {
error_tok(tag, "not a struct tag");
}
return sc->ty;
}

expect("{");
// "struct *st" is legal C that defines st as a pointer to an unamed
// incomplete struct type
if (!consume("{")) {
return struct_type();
}

TagScope *sc = find_tag(tag);
Type *ty;

if (sc && sc->depth == scope_depth) {
// if no existing tag has the same tag in the same block scope, it's a
// refinition
if (sc->ty->kind != TY_STRUCT) {
error_tok(tag, "not a struct tag");
}
ty = sc->ty;
} else {
// register a struct type as an incomplete type early, so you can write
// recursive structs such as "struct T { struct T *next; }"
ty = struct_type();
if (tag) {
push_tag_scope(tag, ty);
}
}

// read struct members
Member head;
Expand All @@ -408,8 +439,6 @@ Type *struct_decl() {
cur = cur->next;
}

Type *ty = calloc(1, sizeof(Type));
ty->kind = TY_STRUCT;
ty->members = head.next;

// Assign offsets within the struct to members
Expand All @@ -423,10 +452,7 @@ Type *struct_decl() {
}
}

// register the struct type if a name was given
if (tag) {
push_tag_scope(tag, ty);
}
ty->is_incomplete = false;
return ty;
}

Expand Down
4 changes: 4 additions & 0 deletions tests
Original file line number Diff line number Diff line change
Expand Up @@ -804,6 +804,10 @@ int main() {
assert(5, ({ int i=0; switch(0) { case 0:i=5;break; default:i=7; } i; }), "int i=0; switch(0) { case 0:i=5;break; default:i=7; } i;");
assert(7, ({ int i=0; switch(1) { case 0:i=5;break; default:i=7; } i; }), "int i=0; switch(1) { case 0:i=5;break; default:i=7; } i;");

assert(8, ({ struct *foo; sizeof(foo); }), "struct *foo; sizeof(foo);");
assert(4, ({ struct T *foo; struct T {int x;}; sizeof(struct T); }), "struct T *foo; struct T {int x;}; sizeof(struct T);");
assert(1, ({ struct T { struct T *next; int x; } a; struct T b; b.x=1; a.next=&b; a.next->x; }), "struct T { struct T *next; int x; } a; struct T b; b.x=1; a.next=&b; a.next->x;");

printf("OK\n");
return 0;
}
6 changes: 6 additions & 0 deletions type.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,12 @@ Type* int_type() { return new_type(TY_INT, 4); }

Type* long_type() { return new_type(TY_LONG, 8); }

Type* struct_type() {
Type* ty = new_type(TY_STRUCT, 1);
ty->is_incomplete = true;
return ty;
}

Type* enum_type() { return new_type(TY_ENUM, 4); }

Type* func_type(Type* return_ty) {
Expand Down

0 comments on commit 64b5565

Please sign in to comment.