Improve storage class handling:
[cparser] / parser.c
index 2a114b7..5e5b419 100644 (file)
--- a/parser.c
+++ b/parser.c
@@ -76,8 +76,9 @@ struct declaration_specifiers_t {
        source_position_t  source_position;
        storage_class_t    storage_class;
        unsigned char      alignment;         /**< Alignment, 0 if not set. */
-       bool               is_inline : 1;
-       bool               deprecated : 1;
+       bool               is_inline    : 1;
+       bool               thread_local : 1;  /**< GCC __thread */
+       bool               deprecated   : 1;
        decl_modifiers_t   modifiers;         /**< declaration modifiers */
        gnu_attribute_t   *gnu_attributes;    /**< list of GNU attributes */
        const char        *deprecated_string; /**< can be set if declaration was marked deprecated. */
@@ -188,9 +189,17 @@ static void          parse_externals(void);
 static void          parse_external(void);
 
 static void parse_compound_type_entries(compound_t *compound_declaration);
+
+typedef enum declarator_flags_t {
+       DECL_FLAGS_NONE             = 0,
+       DECL_MAY_BE_ABSTRACT        = 1U << 0,
+       DECL_CREATE_COMPOUND_MEMBER = 1U << 1,
+       DECL_IS_PARAMETER           = 1U << 2
+} declarator_flags_t;
+
 static entity_t *parse_declarator(const declaration_specifiers_t *specifiers,
-                                  bool may_be_abstract,
-                                  bool create_compound_member);
+                                  declarator_flags_t flags);
+
 static entity_t *record_entity(entity_t *entity, bool is_definition);
 
 static void semantic_comparison(binary_expression_t *expression);
@@ -3560,7 +3569,6 @@ static void parse_declaration_specifiers(declaration_specifiers_t *specifiers)
                        modifiers |= TYPE_MODIFIER_TRANSPARENT_UNION;
 
                switch (token.type) {
-
                /* storage class */
 #define MATCH_STORAGE_CLASS(token, class)                                  \
                case token:                                                        \
@@ -3568,6 +3576,8 @@ static void parse_declaration_specifiers(declaration_specifiers_t *specifiers)
                                errorf(HERE, "multiple storage classes in declaration specifiers"); \
                        }                                                              \
                        specifiers->storage_class = class;                             \
+                       if (specifiers->thread_local)                                  \
+                               goto check_thread_storage_class;                           \
                        next_token();                                                  \
                        break;
 
@@ -3587,22 +3597,25 @@ static void parse_declaration_specifiers(declaration_specifiers_t *specifiers)
                        break;
 
                case T___thread:
-                       switch (specifiers->storage_class) {
-                       case STORAGE_CLASS_NONE:
-                               specifiers->storage_class = STORAGE_CLASS_THREAD;
-                               break;
-
-                       case STORAGE_CLASS_EXTERN:
-                               specifiers->storage_class = STORAGE_CLASS_THREAD_EXTERN;
-                               break;
-
-                       case STORAGE_CLASS_STATIC:
-                               specifiers->storage_class = STORAGE_CLASS_THREAD_STATIC;
-                               break;
+                       if (specifiers->thread_local) {
+                               errorf(HERE, "duplicate '__thread'");
+                       } else {
+                               specifiers->thread_local = true;
+check_thread_storage_class:
+                               switch (specifiers->storage_class) {
+                                       case STORAGE_CLASS_EXTERN:
+                                       case STORAGE_CLASS_NONE:
+                                       case STORAGE_CLASS_STATIC:
+                                               break;
 
-                       default:
-                               errorf(HERE, "multiple storage classes in declaration specifiers");
-                               break;
+                                               char const* wrong;
+                                       case STORAGE_CLASS_AUTO:     wrong = "auto";     goto wrong_thread_stoarge_class;
+                                       case STORAGE_CLASS_REGISTER: wrong = "register"; goto wrong_thread_stoarge_class;
+                                       case STORAGE_CLASS_TYPEDEF:  wrong = "typedef";  goto wrong_thread_stoarge_class;
+wrong_thread_stoarge_class:
+                                               errorf(HERE, "'__thread' used with '%s'", wrong);
+                                               break;
+                               }
                        }
                        next_token();
                        break;
@@ -4015,8 +4028,6 @@ static void parse_identifier_list(scope_t *scope)
        } while (token.type == T_IDENTIFIER);
 }
 
-static type_t *automatic_type_conversion(type_t *orig_type);
-
 static void semantic_parameter(declaration_t *declaration)
 {
        /* TODO: improve error messages */
@@ -4036,22 +4047,13 @@ static void semantic_parameter(declaration_t *declaration)
                        break;
        }
 
-       type_t *const orig_type = declaration->type;
-       /* §6.7.5.3:7  A declaration of a parameter as ``array of type'' shall be
-        *             adjusted to ``qualified pointer to type'', [...]
-        * §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;
-
        /* §6.7.5.3:4  After adjustment, the parameters in a parameter type list in
         *             a function declarator that is part of a definition of that
         *             function shall not have incomplete type. */
+       type_t *type = declaration->type;
        if (is_type_incomplete(skip_typeref(type))) {
                errorf(pos, "parameter '%#T' has incomplete type",
-                      orig_type, declaration->base.symbol);
+                      type, declaration->base.symbol);
        }
 }
 
@@ -4062,7 +4064,8 @@ static entity_t *parse_parameter(void)
 
        parse_declaration_specifiers(&specifiers);
 
-       entity_t *entity = parse_declarator(&specifiers, true, false);
+       entity_t *entity = parse_declarator(&specifiers,
+                       DECL_MAY_BE_ABSTRACT | DECL_IS_PARAMETER);
        anonymous_entity = NULL;
        return entity;
 }
@@ -4632,17 +4635,20 @@ static type_t *construct_declarator_type(construct_type_t *construct_list, type_
        return type;
 }
 
+static type_t *automatic_type_conversion(type_t *orig_type);
+
 static entity_t *parse_declarator(const declaration_specifiers_t *specifiers,
-                                  bool may_be_abstract,
-                                  bool create_compound_member)
+                                  declarator_flags_t flags)
 {
        parse_declarator_env_t env;
        memset(&env, 0, sizeof(env));
        env.modifiers = specifiers->modifiers;
 
-       construct_type_t *construct_type
-               = parse_inner_declarator(&env, may_be_abstract);
-       type_t *type = construct_declarator_type(construct_type, specifiers->type);
+       construct_type_t *construct_type =
+               parse_inner_declarator(&env, (flags & DECL_MAY_BE_ABSTRACT) != 0);
+       type_t           *orig_type      =
+               construct_declarator_type(construct_type, specifiers->type);
+       type_t           *type           = skip_typeref(orig_type);
 
        if (construct_type != NULL) {
                obstack_free(&temp_obst, construct_type);
@@ -4653,7 +4659,7 @@ static entity_t *parse_declarator(const declaration_specifiers_t *specifiers,
                entity                       = allocate_entity_zero(ENTITY_TYPEDEF);
                entity->base.symbol          = env.symbol;
                entity->base.source_position = env.source_position;
-               entity->typedefe.type        = type;
+               entity->typedefe.type        = orig_type;
 
                if (anonymous_entity != NULL) {
                        if (is_type_compound(type)) {
@@ -4670,14 +4676,45 @@ static entity_t *parse_declarator(const declaration_specifiers_t *specifiers,
                        }
                }
        } else {
-               if (create_compound_member) {
-                       entity = allocate_entity_zero(ENTITY_COMPOUND_MEMBER);
-               } else if (is_type_function(skip_typeref(type))) {
+               if (flags & DECL_CREATE_COMPOUND_MEMBER) {
+                 entity = allocate_entity_zero(ENTITY_COMPOUND_MEMBER);
+
+                       if (specifiers->is_inline && is_type_valid(type)) {
+                               errorf(&env.source_position,
+                                               "compound member '%Y' declared 'inline'", env.symbol);
+                       }
+
+                 if (specifiers->thread_local ||
+                               specifiers->storage_class != STORAGE_CLASS_NONE) {
+                       errorf(&env.source_position,
+                                       "compound member '%Y' must have no storage class",
+                                       env.symbol);
+                 }
+               } else if (flags & DECL_IS_PARAMETER) {
+                       /* §6.7.5.3:7  A declaration of a parameter as ``array of type''
+                        *             shall be adjusted to ``qualified pointer to type'',
+                        *             [...]
+                        * §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. */
+                       orig_type = automatic_type_conversion(type);
+                       goto create_variable;
+               } else if (is_type_function(type)) {
                        entity = allocate_entity_zero(ENTITY_FUNCTION);
 
                        entity->function.is_inline  = specifiers->is_inline;
                        entity->function.parameters = env.parameters;
+
+                       if (specifiers->thread_local || (
+                                               specifiers->storage_class != STORAGE_CLASS_EXTERN &&
+                                               specifiers->storage_class != STORAGE_CLASS_NONE   &&
+                                               specifiers->storage_class != STORAGE_CLASS_STATIC)
+                                       ) {
+                               errorf(&env.source_position,
+                                               "invalid storage class for function '%Y'", env.symbol);
+                       }
                } else {
+create_variable:
                        entity = allocate_entity_zero(ENTITY_VARIABLE);
 
                        entity->variable.get_property_sym = specifiers->get_property_sym;
@@ -4687,16 +4724,36 @@ static entity_t *parse_declarator(const declaration_specifiers_t *specifiers,
                                entity->variable.alignment = specifiers->alignment;
                        }
 
-                       if (warning.other && specifiers->is_inline && is_type_valid(type)) {
-                               warningf(&env.source_position,
-                                                "variable '%Y' declared 'inline'\n", env.symbol);
+                       if (specifiers->is_inline && is_type_valid(type)) {
+                               errorf(&env.source_position,
+                                               "variable '%Y' declared 'inline'", env.symbol);
+                       }
+
+                       entity->variable.thread_local = specifiers->thread_local;
+
+                       bool invalid_storage_class = false;
+                       if (current_scope == file_scope) {
+                               if (specifiers->storage_class != STORAGE_CLASS_EXTERN &&
+                                               specifiers->storage_class != STORAGE_CLASS_NONE   &&
+                                               specifiers->storage_class != STORAGE_CLASS_STATIC) {
+                                       invalid_storage_class = true;
+                               }
+                       } else {
+                               if (specifiers->thread_local &&
+                                               specifiers->storage_class == STORAGE_CLASS_NONE) {
+                                       invalid_storage_class = true;
+                               }
+                       }
+                       if (invalid_storage_class) {
+                               errorf(&env.source_position,
+                                               "invalid storage class for variable '%Y'", env.symbol);
                        }
                }
 
                entity->base.source_position          = env.source_position;
                entity->base.symbol                   = env.symbol;
                entity->base.namespc                  = NAMESPACE_NORMAL;
-               entity->declaration.type              = type;
+               entity->declaration.type              = orig_type;
                entity->declaration.modifiers         = env.modifiers;
                entity->declaration.deprecated_string = specifiers->deprecated_string;
 
@@ -5043,8 +5100,7 @@ error_redeclaration:
                        && entity->kind == ENTITY_VARIABLE
                        && current_scope == file_scope) {
                declaration_t *declaration = &entity->declaration;
-               if (declaration->storage_class == STORAGE_CLASS_NONE ||
-                               declaration->storage_class == STORAGE_CLASS_THREAD) {
+               if (declaration->storage_class == STORAGE_CLASS_NONE) {
                        warningf(pos, "no previous declaration for '%#T'",
                                 declaration->type, symbol);
                }
@@ -5104,8 +5160,7 @@ static void parse_init_declarator_rest(entity_t *entity)
        }
 
        bool must_be_constant = false;
-       if (declaration->storage_class == STORAGE_CLASS_STATIC        ||
-           declaration->storage_class == STORAGE_CLASS_THREAD_STATIC ||
+       if (declaration->storage_class == STORAGE_CLASS_STATIC ||
            entity->base.parent_scope  == file_scope) {
                must_be_constant = true;
        }
@@ -5142,7 +5197,8 @@ static void parse_anonymous_declaration_rest(
        anonymous_entity = NULL;
 
        if (warning.other) {
-               if (specifiers->storage_class != STORAGE_CLASS_NONE) {
+               if (specifiers->storage_class != STORAGE_CLASS_NONE ||
+                               specifiers->thread_local) {
                        warningf(&specifiers->source_position,
                                 "useless storage class in empty declaration");
                }
@@ -5200,11 +5256,16 @@ static void parse_declaration_rest(entity_t *ndeclaration,
                if (token.type == '=') {
                        parse_init_declarator_rest(entity);
                } else if (entity->kind == ENTITY_VARIABLE) {
-                       type_t *type = entity->declaration.type;
-                       if (is_type_reference(skip_typeref(type))) {
-                               errorf(&entity->base.source_position,
-                                               "reference '%#T' must be initialized",
-                                               type, entity->base.symbol);
+                       /* ISO/IEC 14882:1998(E) §8.5.3:3  The initializer can be omitted
+                        * [...] where the extern specifier is explicitly used. */
+                       declaration_t *decl = &entity->declaration;
+                       if (decl->storage_class != STORAGE_CLASS_EXTERN) {
+                               type_t *type = decl->type;
+                               if (is_type_reference(skip_typeref(type))) {
+                                       errorf(&entity->base.source_position,
+                                                       "reference '%#T' must be initialized",
+                                                       type, entity->base.symbol);
+                               }
                        }
                }
 
@@ -5215,7 +5276,7 @@ static void parse_declaration_rest(entity_t *ndeclaration,
                eat(',');
 
                add_anchor_token('=');
-               ndeclaration = parse_declarator(specifiers, /*may_be_abstract=*/false, false);
+               ndeclaration = parse_declarator(specifiers, DECL_FLAGS_NONE);
                rem_anchor_token('=');
        }
        expect(';');
@@ -5262,7 +5323,7 @@ static void parse_declaration(parsed_declaration_func finished_declaration)
        if (token.type == ';') {
                parse_anonymous_declaration_rest(&specifiers);
        } else {
-               entity_t *entity = parse_declarator(&specifiers, /*may_be_abstract=*/false, false);
+               entity_t *entity = parse_declarator(&specifiers, DECL_FLAGS_NONE);
                parse_declaration_rest(entity, &specifiers, finished_declaration);
        }
 }
@@ -6001,7 +6062,7 @@ static void parse_external_declaration(void)
        add_anchor_token('{');
 
        /* declarator is common to both function-definitions and declarations */
-       entity_t *ndeclaration = parse_declarator(&specifiers, /*may_be_abstract=*/false, false);
+       entity_t *ndeclaration = parse_declarator(&specifiers, DECL_FLAGS_NONE);
 
        rem_anchor_token('{');
        rem_anchor_token(';');
@@ -6229,7 +6290,8 @@ static void parse_compound_declarators(compound_t *compound,
                        entity->declaration.modifiers              = specifiers->modifiers;
                        entity->declaration.type                   = type;
                } else {
-                       entity = parse_declarator(specifiers,/*may_be_abstract=*/true, true);
+                       entity = parse_declarator(specifiers,
+                                       DECL_MAY_BE_ABSTRACT | DECL_CREATE_COMPOUND_MEMBER);
                        assert(entity->kind == ENTITY_COMPOUND_MEMBER);
 
                        if (token.type == ':') {
@@ -6315,7 +6377,8 @@ static type_t *parse_typename(void)
        declaration_specifiers_t specifiers;
        memset(&specifiers, 0, sizeof(specifiers));
        parse_declaration_specifiers(&specifiers);
-       if (specifiers.storage_class != STORAGE_CLASS_NONE) {
+       if (specifiers.storage_class != STORAGE_CLASS_NONE ||
+                       specifiers.thread_local) {
                /* TODO: improve error message, user does probably not know what a
                 * storage class is...
                 */
@@ -8222,11 +8285,16 @@ static bool is_lvalue(const expression_t *expression)
        case EXPR_UNARY_DEREFERENCE:
                return true;
 
-       default:
+       default: {
+         type_t *type = skip_typeref(expression->base.type);
+         return
+               /* ISO/IEC 14882:1998(E) §3.10:3 */
+               is_type_reference(type) ||
                /* Claim it is an lvalue, if the type is invalid.  There was a parse
                 * error before, which maybe prevented properly recognizing it as
                 * lvalue. */
-               return !is_type_valid(skip_typeref(expression->base.type));
+               !is_type_valid(type);
+       }
        }
 }