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. */
modifiers |= TYPE_MODIFIER_TRANSPARENT_UNION;
switch (token.type) {
-
/* storage class */
#define MATCH_STORAGE_CLASS(token, class) \
case token: \
errorf(HERE, "multiple storage classes in declaration specifiers"); \
} \
specifiers->storage_class = class; \
+ if (specifiers->thread_local) \
+ goto check_thread_storage_class; \
next_token(); \
break;
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;
} else {
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'',
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.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->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);
}
}
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;
}
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");
}
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);
+ }
}
}
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...
*/