Comply with §6.7.5.3(8).
[cparser] / parser.c
index f1adb8e..f42ad8e 100644 (file)
--- a/parser.c
+++ b/parser.c
@@ -526,14 +526,14 @@ static void eat_until_matching_token(int type) {
        unsigned parenthesis_count = 0;
        unsigned brace_count = 0;
        unsigned bracket_count = 0;
-       int end_token = type;
 
-       if (type == '(')
-               end_token = ')';
-       else if (type == '{')
-               end_token = '}';
-       else if (type == '[')
-               end_token = ']';
+       int end_token;
+       switch (type) {
+               case '(': end_token = ')';  break;
+               case '{': end_token = '}';  break;
+               case '[': end_token = ']';  break;
+               default:  end_token = type; break;
+       }
 
        while(token.type != end_token ||
              (parenthesis_count > 0 || brace_count > 0 || bracket_count > 0)) {
@@ -1293,13 +1293,16 @@ static void parse_gnu_attribute_mode_arg(gnu_attribute_t *attribute)
        /* This isn't really correct, the backend should provide a list of machine
         * specific modes (according to gcc philosophy that is...) */
        const char *symbol_str = token.v.symbol->string;
-       if(strcmp_underscore("QI", symbol_str) == 0) {
+       if (strcmp_underscore("QI",   symbol_str) == 0 ||
+           strcmp_underscore("byte", symbol_str) == 0) {
                attribute->u.akind = ATOMIC_TYPE_CHAR;
        } else if (strcmp_underscore("HI", symbol_str) == 0) {
                attribute->u.akind = ATOMIC_TYPE_SHORT;
-       } else if(strcmp_underscore("SI", symbol_str) == 0) {
+       } else if (strcmp_underscore("SI",      symbol_str) == 0
+               || strcmp_underscore("word",    symbol_str) == 0
+               || strcmp_underscore("pointer", symbol_str) == 0) {
                attribute->u.akind = ATOMIC_TYPE_INT;
-       } else if(strcmp_underscore("DI", symbol_str) == 0) {
+       } else if (strcmp_underscore("DI", symbol_str) == 0) {
                attribute->u.akind = ATOMIC_TYPE_LONGLONG;
        } else {
                warningf(HERE, "ignoring unknown mode '%s'", symbol_str);
@@ -1541,9 +1544,6 @@ static decl_modifiers_t parse_gnu_attribute(gnu_attribute_t **attributes)
                                switch(kind) {
                                case GNU_AK_CONST:
                                case GNU_AK_VOLATILE:
-                               case GNU_AK_CDECL:
-                               case GNU_AK_STDCALL:
-                               case GNU_AK_FASTCALL:
                                case GNU_AK_DEPRECATED:
                                case GNU_AK_NAKED:
                                case GNU_AK_MALLOC:
@@ -1552,7 +1552,6 @@ static decl_modifiers_t parse_gnu_attribute(gnu_attribute_t **attributes)
                                case GNU_AK_NOCOMMON:
                                case GNU_AK_SHARED:
                                case GNU_AK_NOTSHARED:
-                               case GNU_AK_USED:
                                case GNU_AK_UNUSED:
                                case GNU_AK_NO_INSTRUMENT_FUNCTION:
                                case GNU_AK_WARN_UNUSED_RESULT:
@@ -1577,63 +1576,23 @@ static decl_modifiers_t parse_gnu_attribute(gnu_attribute_t **attributes)
                                case GNU_AK_MAY_ALIAS:
                                case GNU_AK_MS_STRUCT:
                                case GNU_AK_GCC_STRUCT:
-                                       check_no_argument(attribute, name);
-                                       break;
-
-                               case GNU_AK_PURE:
-                                       check_no_argument(attribute, name);
-                                       modifiers |= DM_PURE;
-                                       break;
-
-                               case GNU_AK_ALWAYS_INLINE:
-                                       check_no_argument(attribute, name);
-                                       modifiers |= DM_FORCEINLINE;
-                                       break;
-
-                               case GNU_AK_DLLIMPORT:
-                                       check_no_argument(attribute, name);
-                                       modifiers |= DM_DLLIMPORT;
-                                       break;
-
-                               case GNU_AK_DLLEXPORT:
-                                       check_no_argument(attribute, name);
-                                       modifiers |= DM_DLLEXPORT;
-                                       break;
-
-                               case GNU_AK_PACKED:
-                                       check_no_argument(attribute, name);
-                                       modifiers |= DM_PACKED;
-                                       break;
-
-                               case GNU_AK_NOINLINE:
-                                       check_no_argument(attribute, name);
-                                       modifiers |= DM_NOINLINE;
-                                       break;
-
-                               case GNU_AK_NORETURN:
-                                       check_no_argument(attribute, name);
-                                       modifiers |= DM_NORETURN;
-                                       break;
-
-                               case GNU_AK_NOTHROW:
-                                       check_no_argument(attribute, name);
-                                       modifiers |= DM_NOTHROW;
-                                       break;
-
-                               case GNU_AK_TRANSPARENT_UNION:
-                                       check_no_argument(attribute, name);
-                                       modifiers |= DM_TRANSPARENT_UNION;
-                                       break;
-
-                               case GNU_AK_CONSTRUCTOR:
-                                       check_no_argument(attribute, name);
-                                       modifiers |= DM_CONSTRUCTOR;
-                                       break;
-
-                               case GNU_AK_DESTRUCTOR:
-                                       check_no_argument(attribute, name);
-                                       modifiers |= DM_DESTRUCTOR;
-                                       break;
+                                       goto no_arg;
+
+                               case GNU_AK_CDECL:             modifiers |= DM_CDECL;             goto no_arg;
+                               case GNU_AK_FASTCALL:          modifiers |= DM_FASTCALL;          goto no_arg;
+                               case GNU_AK_STDCALL:           modifiers |= DM_STDCALL;           goto no_arg;
+                               case GNU_AK_USED:              modifiers |= DM_USED;              goto no_arg;
+                               case GNU_AK_PURE:              modifiers |= DM_PURE;              goto no_arg;
+                               case GNU_AK_ALWAYS_INLINE:     modifiers |= DM_FORCEINLINE;       goto no_arg;
+                               case GNU_AK_DLLIMPORT:         modifiers |= DM_DLLIMPORT;         goto no_arg;
+                               case GNU_AK_DLLEXPORT:         modifiers |= DM_DLLEXPORT;         goto no_arg;
+                               case GNU_AK_PACKED:            modifiers |= DM_PACKED;            goto no_arg;
+                               case GNU_AK_NOINLINE:          modifiers |= DM_NOINLINE;          goto no_arg;
+                               case GNU_AK_NORETURN:          modifiers |= DM_NORETURN;          goto no_arg;
+                               case GNU_AK_NOTHROW:           modifiers |= DM_NOTHROW;           goto no_arg;
+                               case GNU_AK_TRANSPARENT_UNION: modifiers |= DM_TRANSPARENT_UNION; goto no_arg;
+                               case GNU_AK_CONSTRUCTOR:       modifiers |= DM_CONSTRUCTOR;       goto no_arg;
+                               case GNU_AK_DESTRUCTOR:        modifiers |= DM_DESTRUCTOR;        goto no_arg;
 
                                case GNU_AK_ALIGNED:
                                        /* __align__ may be used without an argument */
@@ -1722,6 +1681,9 @@ static decl_modifiers_t parse_gnu_attribute(gnu_attribute_t **attributes)
                                case GNU_AK_LAST:
                                        /* already handled */
                                        break;
+
+no_arg:
+                                       check_no_argument(attribute, name);
                                }
                        }
                        if (attribute != NULL) {
@@ -1757,7 +1719,7 @@ static decl_modifiers_t parse_attributes(gnu_attribute_t **attributes)
                switch(token.type) {
                case T___attribute__:
                        modifiers |= parse_gnu_attribute(attributes);
-                       break;
+                       continue;
 
                case T_asm:
                        next_token();
@@ -1771,25 +1733,23 @@ static decl_modifiers_t parse_attributes(gnu_attribute_t **attributes)
                                parse_string_literals();
                        }
                        expect(')');
-                       break;
+                       continue;
+
+               case T_cdecl:     modifiers |= DM_CDECL;    break;
+               case T__fastcall: modifiers |= DM_FASTCALL; break;
+               case T__stdcall:  modifiers |= DM_STDCALL;  break;
 
-               case T_cdecl:
-               case T__fastcall:
-               case T__stdcall:
                case T___thiscall:
                        /* TODO record modifier */
                        warningf(HERE, "Ignoring declaration modifier %K", &token);
-                       next_token();
                        break;
 
-               default:
-                       goto attributes_finished;
+end_error:
+               default: return modifiers;
                }
-       }
 
-attributes_finished:
-end_error:
-       return modifiers;
+               next_token();
+       }
 }
 
 static designator_t *parse_designation(void)
@@ -2590,10 +2550,21 @@ static declaration_t *parse_compound_type_specifier(bool is_struct)
                symbol = token.v.symbol;
                next_token();
 
-               if (is_struct) {
-                       declaration = get_declaration(symbol, NAMESPACE_STRUCT);
-               } else {
-                       declaration = get_declaration(symbol, NAMESPACE_UNION);
+               namespace_t const namespc =
+                       is_struct ? NAMESPACE_STRUCT : NAMESPACE_UNION;
+               declaration = get_declaration(symbol, namespc);
+               if (declaration != NULL) {
+                       if (declaration->parent_scope != scope &&
+                           (token.type == '{' || token.type == ';')) {
+                               declaration = NULL;
+                       } else if (declaration->init.complete &&
+                                  token.type == '{') {
+                               assert(symbol != NULL);
+                               errorf(HERE, "multiple definitions of '%s %Y' (previous definition at %P)",
+                                      is_struct ? "struct" : "union", symbol,
+                                      &declaration->source_position);
+                               declaration->scope.declarations = NULL;
+                       }
                }
        } else if (token.type != '{') {
                if (is_struct) {
@@ -2621,13 +2592,6 @@ static declaration_t *parse_compound_type_specifier(bool is_struct)
        }
 
        if (token.type == '{') {
-               if (declaration->init.complete) {
-                       assert(symbol != NULL);
-                       errorf(HERE, "multiple definitions of '%s %Y' (previous definition at %P)",
-                              is_struct ? "struct" : "union", symbol,
-                              &declaration->source_position);
-                       declaration->scope.declarations = NULL;
-               }
                declaration->init.complete = true;
 
                parse_compound_type_entries(declaration);
@@ -3019,6 +2983,11 @@ static void parse_declaration_specifiers(declaration_specifiers_t *specifiers)
        specifiers->source_position = token.source_position;
 
        while(true) {
+               specifiers->modifiers
+                       |= parse_attributes(&specifiers->gnu_attributes);
+               if (specifiers->modifiers & DM_TRANSPARENT_UNION)
+                       modifiers |= TYPE_MODIFIER_TRANSPARENT_UNION;
+
                switch(token.type) {
 
                /* storage class */
@@ -3160,13 +3129,6 @@ static void parse_declaration_specifiers(declaration_specifiers_t *specifiers)
                        next_token();
                        break;
 
-               case T___attribute__:
-                       specifiers->modifiers
-                               |= parse_attributes(&specifiers->gnu_attributes);
-                       if (specifiers->modifiers & DM_TRANSPARENT_UNION)
-                               modifiers |= TYPE_MODIFIER_TRANSPARENT_UNION;
-                       break;
-
                case T_IDENTIFIER: {
                        /* only parse identifier if we haven't found a type yet */
                        if (type != NULL || type_specifiers != 0)
@@ -3425,6 +3387,8 @@ static declaration_t *parse_identifier_list(void)
        return declarations;
 }
 
+static type_t *automatic_type_conversion(type_t *orig_type);
+
 static void semantic_parameter(declaration_t *declaration)
 {
        /* TODO: improve error messages */
@@ -3437,19 +3401,15 @@ static void semantic_parameter(declaration_t *declaration)
        }
 
        type_t *const orig_type = declaration->type;
-       type_t *      type      = skip_typeref(orig_type);
-
-       /* Array as last part of a parameter type is just syntactic sugar.  Turn it
-        * into a pointer. § 6.7.5.3 (7) */
-       if (is_type_array(type)) {
-               type_t *const element_type = type->array.element_type;
-
-               type = make_pointer_type(element_type, type->base.qualifiers);
-
-               declaration->type = type;
-       }
+       /* §6.7.5.3(7): Array as last part of a parameter type is just syntactic
+        * sugar.  Turn it into a pointer.
+        * §6.7.5.3(8): A declaration of a parameter as ``function returning type''
+        * shall be adjusted to ``pointer to function returning type'', as in 6.3.2.1.
+        */
+       type_t *const type = automatic_type_conversion(orig_type);
+       declaration->type = type;
 
-       if (is_type_incomplete(type)) {
+       if (is_type_incomplete(skip_typeref(type))) {
                errorf(HERE, "incomplete type '%T' not allowed for parameter '%Y'",
                       orig_type, declaration->symbol);
        }
@@ -8411,7 +8371,9 @@ static void check_unused_globals(void)
                return;
 
        for (const declaration_t *decl = global_scope->declarations; decl != NULL; decl = decl->next) {
-               if (decl->used || decl->storage_class != STORAGE_CLASS_STATIC)
+               if (decl->used                ||
+                   decl->modifiers & DM_USED ||
+                   decl->storage_class != STORAGE_CLASS_STATIC)
                        continue;
 
                type_t *const type = decl->type;
@@ -8433,18 +8395,43 @@ static void check_unused_globals(void)
        }
 }
 
+static void parse_global_asm(void)
+{
+       eat(T_asm);
+       expect('(');
+
+       statement_t *statement          = allocate_statement_zero(STATEMENT_ASM);
+       statement->base.source_position = token.source_position;
+       statement->asms.asm_text        = parse_string_literals();
+       statement->base.next            = unit->global_asm;
+       unit->global_asm                = statement;
+
+       expect(')');
+       expect(';');
+
+end_error:;
+}
+
 /**
  * Parse a translation unit.
  */
 static void parse_translation_unit(void)
 {
        while(token.type != T_EOF) {
-               if (token.type == ';') {
-                       /* TODO error in strict mode */
-                       warningf(HERE, "stray ';' outside of function");
-                       next_token();
-               } else {
-                       parse_external_declaration();
+               switch (token.type) {
+                       case ';':
+                               /* TODO error in strict mode */
+                               warningf(HERE, "stray ';' outside of function");
+                               next_token();
+                               break;
+
+                       case T_asm:
+                               parse_global_asm();
+                               break;
+
+                       default:
+                               parse_external_declaration();
+                               break;
                }
        }
 }