chibicc icon indicating copy to clipboard operation
chibicc copied to clipboard

miscellaneous fixes (patches)

Open ghost opened this issue 3 years ago • 2 comments

Hello! After working on #37 and #82, I felt motivated to try to spend the day resolving other open issues too!

I wanted to say: I know you’re very busy working on other projects, and that chibicc isn’t your biggest focus at the moment, so it is fine if you don’t have the time to look into all these patches. Either way, I really hope that you can appreciate them in some way or another!

anonymous arguments

fixes: #63

note: I also took the liberty to allow omitting parameter names in function definitions, since it seems at least GCC allows it.

diff --git a/parse.c b/parse.c
index 6acaeb8..692eaca 100644
--- a/parse.c
+++ b/parse.c
@@ -681,7 +681,7 @@ static Type *pointers(Token **rest, Token *tok, Type *ty) {
 static Type *declarator(Token **rest, Token *tok, Type *ty) {
   ty = pointers(&tok, tok, ty);
 
-  if (equal(tok, "(")) {
+  if (equal(tok, "(") && !is_typename(tok->next) && !equal(tok->next, ")")) {
     Token *start = tok;
     Type dummy = {};
     declarator(&tok, start->next, &dummy);
@@ -3147,7 +3147,7 @@ static void create_param_lvars(Type *param) {
   if (param) {
     create_param_lvars(param->next);
     if (!param->name)
-      error_tok(param->name_pos, "parameter name omitted");
+      return;
     new_lvar(get_ident(param->name), param);
   }
 }
anonymous struct and union members

fixes: #31 #45

note: Without this patch, chibicc will handle anonymous members very poorly.

diff --git a/parse.c b/parse.c
index 6acaeb8..468deba 100644
--- a/parse.c
+++ b/parse.c
@@ -1003,10 +1003,12 @@ static Member *struct_designator(Token **rest, Token *tok, Type *ty) {
 
   for (Member *mem = ty->members; mem; mem = mem->next) {
     // Anonymous struct member
-    if (mem->ty->kind == TY_STRUCT && !mem->name) {
-      if (get_struct_member(mem->ty, tok)) {
-        *rest = start;
-        return mem;
+    if (!mem->name) {
+      if (mem->ty->kind == TY_STRUCT || mem->ty->kind == TY_UNION) {
+        if (get_struct_member(mem->ty, tok)) {
+          *rest = start;
+          return mem;
+        }
       }
       continue;
     }
@@ -2738,10 +2740,10 @@ static Type *union_decl(Token **rest, Token *tok) {
 static Member *get_struct_member(Type *ty, Token *tok) {
   for (Member *mem = ty->members; mem; mem = mem->next) {
     // Anonymous struct member
-    if ((mem->ty->kind == TY_STRUCT || mem->ty->kind == TY_UNION) &&
-        !mem->name) {
-      if (get_struct_member(mem->ty, tok))
-        return mem;
+    if (!mem->name) {
+      if (mem->ty->kind == TY_STRUCT || mem->ty->kind == TY_UNION)
+        if (get_struct_member(mem->ty, tok))
+          return mem;
       continue;
     }
bitfield validation

fixes: #28

diff --git a/parse.c b/parse.c
index 6acaeb8..4f01100 100644
--- a/parse.c
+++ b/parse.c
@@ -2575,6 +2575,9 @@ static void struct_members(Token **rest, Token *tok, Type *ty) {
       mem->align = attr.align ? attr.align : mem->ty->align;
 
       if (consume(&tok, tok, ":")) {
+        if (!is_integer(mem->ty)) {
+          error_tok(tok, "only integers can be bitfields");
+        }
         mem->is_bitfield = true;
         mem->bit_width = const_expr(&tok, tok);
       }
designator syntax fixes

fixes: #62

note: Without this patch, chibicc handles nested designators very poorly and weirdly.

diff --git a/parse.c b/parse.c
index 6acaeb8..354ce1a 100644
--- a/parse.c
+++ b/parse.c
@@ -1021,7 +1021,7 @@ static Member *struct_designator(Token **rest, Token *tok, Type *ty) {
   error_tok(tok, "struct has no such member");
 }
 
-// designation = ("[" const-expr "]" | "." ident)* "="? initializer
+// designation = ("[" const-expr "]" | "." ident)+ "=" initializer
 static void designation(Token **rest, Token *tok, Initializer *init) {
   if (equal(tok, "[")) {
     if (init->ty->kind != TY_ARRAY)
@@ -1030,18 +1030,16 @@ static void designation(Token **rest, Token *tok, Initializer *init) {
     int begin, end;
     array_designator(&tok, tok, init->ty, &begin, &end);
 
-    Token *tok2;
     for (int i = begin; i <= end; i++)
-      designation(&tok2, tok, init->children[i]);
-    array_initializer2(rest, tok2, init, begin + 1);
+      designation(&tok, tok, init->children[i]);
+    *rest = tok;
     return;
   }
 
   if (equal(tok, ".") && init->ty->kind == TY_STRUCT) {
     Member *mem = struct_designator(&tok, tok, init->ty);
-    designation(&tok, tok, init->children[mem->idx]);
+    designation(rest, tok, init->children[mem->idx]);
     init->expr = NULL;
-    struct_initializer2(rest, tok, init, mem->next);
     return;
   }
 
@@ -1055,8 +1053,7 @@ static void designation(Token **rest, Token *tok, Initializer *init) {
   if (equal(tok, "."))
     error_tok(tok, "field name not in struct or union initializer");
 
-  if (equal(tok, "="))
-    tok = tok->next;
+  tok = skip(tok, "=");
   initializer2(rest, tok, init);
 }
empty structs and unions

fixes: #36 #66

note: This patch adds an anonymous field to structs and unions declared as empty so that they implicitly gain the size of 1, just like in GCC and Clang.

diff --git a/parse.c b/parse.c
index 6acaeb8..21d773a 100644
--- a/parse.c
+++ b/parse.c
@@ -2583,6 +2583,14 @@ static void struct_members(Token **rest, Token *tok, Type *ty) {
     }
   }
 
+  if (idx == 0) {
+    Member *mem = calloc(1, sizeof(Member));
+    mem->ty = ty_char;
+    mem->idx = 0;
+    mem->align = mem->ty->align;
+    cur = cur->next = mem;
+  }
+
   // If the last element is an array of incomplete type, it's
   // called a "flexible array member". It should behave as if
   // if were a zero-sized array.
postfix tails on compound literals

fixes: #47

diff --git a/parse.c b/parse.c
index 6acaeb8..dd68f62 100644
--- a/parse.c
+++ b/parse.c
@@ -2794,7 +2794,7 @@ static Node *new_inc_dec(Node *node, Token *tok, int addend) {
                   node->ty);
 }
 
-// postfix = "(" type-name ")" "{" initializer-list "}"
+// postfix = "(" type-name ")" "{" initializer-list "}" postfix-tail*
 //         = ident "(" func-args ")" postfix-tail*
 //         | primary postfix-tail*
 //
@@ -2805,6 +2805,8 @@ static Node *new_inc_dec(Node *node, Token *tok, int addend) {
 //              | "++"
 //              | "--"
 static Node *postfix(Token **rest, Token *tok) {
+  Node *node;
+
   if (equal(tok, "(") && is_typename(tok->next)) {
     // Compound literal
     Token *start = tok;
@@ -2813,18 +2815,18 @@ static Node *postfix(Token **rest, Token *tok) {
 
     if (scope->next == NULL) {
       Obj *var = new_anon_gvar(ty);
-      gvar_initializer(rest, tok, var);
-      return new_var_node(var, start);
+      gvar_initializer(&tok, tok, var);
+      node = new_var_node(var, start);
+    } else {
+      Obj *var = new_lvar("", ty);
+      Node *lhs = lvar_initializer(&tok, tok, var);
+      Node *rhs = new_var_node(var, tok);
+      node = new_binary(ND_COMMA, lhs, rhs, start);
     }
-
-    Obj *var = new_lvar("", ty);
-    Node *lhs = lvar_initializer(rest, tok, var);
-    Node *rhs = new_var_node(var, tok);
-    return new_binary(ND_COMMA, lhs, rhs, start);
+  } else {
+    node = primary(&tok, tok);
   }
 
-  Node *node = primary(&tok, tok);
-
   for (;;) {
     if (equal(tok, "(")) {
       node = funcall(&tok, tok->next, node);
string initializer validation

fixes: #72

note: Without this patch, chibicc will wrongly allow string literals to be used as initializers for arrays of element type other than char.

diff --git a/parse.c b/parse.c
index 6acaeb8..7425922 100644
--- a/parse.c
+++ b/parse.c
@@ -1228,9 +1228,11 @@ static void union_initializer(Token **rest, Token *tok, Initializer *init) {
 //             | struct-initializer | union-initializer
 //             | assign
 static void initializer2(Token **rest, Token *tok, Initializer *init) {
-  if (init->ty->kind == TY_ARRAY && tok->kind == TK_STR) {
-    string_initializer(rest, tok, init);
-    return;
+  if (tok->kind == TK_STR) {
+    if (init->ty->kind == TY_ARRAY && init->ty->base->kind == TY_CHAR) {
+      string_initializer(rest, tok, init);
+      return;
+    }
   }
 
   if (init->ty->kind == TY_ARRAY) {
string scalar initializer

fixes: #80

diff --git a/parse.c b/parse.c
index 6acaeb8..1cbb653 100644
--- a/parse.c
+++ b/parse.c
@@ -1234,10 +1234,17 @@ static void initializer2(Token **rest, Token *tok, Initializer *init) {
   }
 
   if (init->ty->kind == TY_ARRAY) {
-    if (equal(tok, "{"))
+    if (equal(tok, "{")) {
+      if (init->ty->base->kind == TY_CHAR && tok->next->kind == TK_STR) {
+        // A string initializer for a char array can be surrounded by braces.
+        // E.g. `char str[] = {"foo"};`.
+        initializer2(&tok, tok->next, init);
+        *rest = skip(tok, "}");
+      }
       array_initializer1(rest, tok, init);
-    else
-      array_initializer2(rest, tok, init, 0);
+      return;
+    }
+    array_initializer2(rest, tok, init, 0);
     return;
   }

ghost avatar Jan 25 '22 21:01 ghost

Hi, @zamfofex I found a small issue on your fix for issue#80. probably a return is missing after the skip of token "}". Very good job on your patches. Best regards, Stormalf

stormalf avatar Sep 13 '22 19:09 stormalf

Hi, thank you very much for posting these patches!

I think the patch for issue#72 needs a small change, otherwise it disallows initialization of char16_t, char32_t and wchar_t strings. I'm not sure if we easily have the written type or only the typedef-mapped one at this point in the parse.

(I also wasn't able to get the fix for issue#62 to work , but I haven't investigated that one too much to know why, just that the test case still fails.)

sgraham avatar Apr 01 '23 05:04 sgraham