Handle struct initializers for local variables

This commit is contained in:
Rui Ueyama 2019-08-18 17:56:36 +09:00
parent b15b929b53
commit 4fc741d109
2 changed files with 50 additions and 1 deletions

34
parse.c
View File

@ -682,7 +682,8 @@ static void global_var(void) {
typedef struct Designator Designator;
struct Designator {
Designator *next;
int idx;
int idx; // array
Member *mem; // struct
};
// Creates a node for an array access. For example, if var represents
@ -693,6 +694,13 @@ static Node *new_desg_node2(Var *var, Designator *desg, Token *tok) {
return new_var_node(var, tok);
Node *node = new_desg_node2(var, desg->next, tok);
if (desg->mem) {
node = new_unary(ND_MEMBER, node, desg->mem->tok);
node->member = desg->mem;
return node;
}
node = new_add(node, new_num(desg->idx, tok), tok);
return new_unary(ND_DEREF, node, tok);
}
@ -730,6 +738,9 @@ static Node *lvar_init_zero(Node *cur, Var *var, Type *ty, Designator *desg) {
// x[1][1]=5;
// x[1][2]=6;
//
// Struct members are initialized in declaration order. For example,
// `struct { int a; int b; } x = {1, 2}` sets x.a to 1 and x.b to 2.
//
// There are a few special rules for ambiguous initializers and
// shorthand notations:
//
@ -799,6 +810,27 @@ static Node *lvar_initializer2(Node *cur, Var *var, Type *ty, Designator *desg)
return cur;
}
if (ty->kind == TY_STRUCT) {
expect("{");
Member *mem = ty->members;
if (!peek("}")) {
do {
Designator desg2 = {desg, 0, mem};
cur = lvar_initializer2(cur, var, mem->ty, &desg2);
mem = mem->next;
} while (!peek_end() && consume(","));
}
expect_end();
// Set excess struct elements to zero.
for (; mem; mem = mem->next) {
Designator desg2 = {desg, 0, mem};
cur = lvar_init_zero(cur, var, mem->ty, &desg2);
}
return cur;
}
cur->next = new_desg_node(var, desg, assign());
return cur->next;
}

17
tests
View File

@ -506,6 +506,23 @@ int main() {
assert(16, ({ int x[]={1,2,3,4}; sizeof(x); }), "int x[]={1,2,3,4}; sizeof(x);");
assert(4, ({ char x[]="foo"; sizeof(x); }), "char x[]=\"foo\"; sizeof(x); }");
assert(1, ({ struct {int a; int b; int c;} x={1,2,3}; x.a; }), "struct {int a; int b; int c;} x={1,2,3}; x.a;");
assert(2, ({ struct {int a; int b; int c;} x={1,2,3}; x.b; }), "struct {int a; int b; int c;} x={1,2,3}; x.b;");
assert(3, ({ struct {int a; int b; int c;} x={1,2,3}; x.c; }), "struct {int a; int b; int c;} x={1,2,3}; x.c;");
assert(1, ({ struct {int a; int b; int c;} x={1}; x.a; }), "struct {int a; int b; int c;} x={1}; x.a;");
assert(0, ({ struct {int a; int b; int c;} x={1}; x.b; }), "struct {int a; int b; int c;} x={1}; x.b;");
assert(0, ({ struct {int a; int b; int c;} x={1}; x.c; }), "struct {int a; int b; int c;} x={1}; x.c;");
assert(1, ({ struct {int a; int b;} x[2]={{1,2},{3,4}}; x[0].a; }), "struct {int a; int b;} x[2]={{1,2},{3,4}}; x[0].a;");
assert(2, ({ struct {int a; int b;} x[2]={{1,2},{3,4}}; x[0].b; }), "struct {int a; int b;} x[2]={{1,2},{3,4}}; x[0].b;");
assert(3, ({ struct {int a; int b;} x[2]={{1,2},{3,4}}; x[1].a; }), "struct {int a; int b;} x[2]={{1,2},{3,4}}; x[1].a;");
assert(4, ({ struct {int a; int b;} x[2]={{1,2},{3,4}}; x[1].b; }), "struct {int a; int b;} x[2]={{1,2},{3,4}}; x[1].b;");
assert(0, ({ struct {int a; int b;} x[2]={{1,2}}; x[1].b; }), "struct {int a; int b;} x[2]={{1,2}}; x[1].b;");
assert(0, ({ struct {int a; int b;} x={}; x.a; }), "struct {int a; int b;} x={}; x.a;");
assert(0, ({ struct {int a; int b;} x={}; x.b; }), "struct {int a; int b;} x={}; x.b;");
printf("OK\n");
return 0;
}