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. */
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);
-#define STORAGE_CLASSES \
- case T_typedef: \
- case T_extern: \
- case T_static: \
- case T_auto: \
- case T_register: \
- case T___thread:
+#define STORAGE_CLASSES \
+ STORAGE_CLASSES_NO_EXTERN \
+ case T_extern:
#define STORAGE_CLASSES_NO_EXTERN \
case T_typedef: \
va_end(ap);
}
-/**
- * Report a type error.
- */
-static void type_error(const char *msg, const source_position_t *source_position,
- type_t *type)
-{
- errorf(source_position, "%s, but found type '%T'", msg, type);
-}
-
/**
* Report an incompatible type.
*/
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;
} 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 */
source_position_t const* const pos = &declaration->base.source_position;
- /* §6.9.1:6 */
+ /* §6.9.1:6 The declarations in the declaration list shall contain no
+ * storage-class specifier other than register and no
+ * initializations. */
switch (declaration->declared_storage_class) {
/* Allowed storage classes */
case STORAGE_CLASS_NONE:
break;
}
- type_t *const orig_type = declaration->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;
-
+ /* §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' is of incomplete type",
- orig_type, declaration->base.symbol);
+ errorf(pos, "parameter '%#T' has incomplete type",
+ type, declaration->base.symbol);
}
}
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;
}
case '&':
if (!(c_mode & _CXX))
errorf(HERE, "references are only available for C++");
- if (base_spec.base_variable != NULL)
+ if (base_spec.base_variable != NULL && warning.other) {
warningf(&base_spec.source_position,
"__based does not precede a pointer operator, ignored");
+ }
type = parse_reference_declarator();
/* consumed */
base_spec.base_variable = NULL;
modifiers |= parse_attributes(&attributes);
}
ptr_operator_end:
- if (base_spec.base_variable != NULL)
+ if (base_spec.base_variable != NULL && warning.other) {
warningf(&base_spec.source_position,
"__based does not precede a pointer operator, ignored");
+ }
if (env != NULL) {
modifiers |= env->modifiers;
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);
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)) {
}
}
} 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;
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;
&& 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 (ent->kind != ENTITY_VARIABLE)
return;
+ /* §6.7:7 If an identifier for an object is declared with no linkage, the
+ * type for the object shall be complete [...] */
declaration_t *decl = &ent->declaration;
- if (decl->storage_class == STORAGE_CLASS_EXTERN)
+ if (decl->storage_class != STORAGE_CLASS_NONE)
return;
type_t *type = decl->type;
if (!is_type_incomplete(skip_typeref(type)))
return;
- errorf(&ent->base.source_position,
- "variable '%#T' is of incomplete type", type, ent->base.symbol);
+ errorf(&ent->base.source_position, "variable '%#T' has incomplete type",
+ type, ent->base.symbol);
}
if (token.type == '=') {
parse_init_declarator_rest(entity);
+ } else if (entity->kind == ENTITY_VARIABLE) {
+ /* 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);
+ }
+ }
}
check_variable_type_complete(entity);
eat(',');
add_anchor_token('=');
- ndeclaration = parse_declarator(specifiers, /*may_be_abstract=*/false, false);
+ ndeclaration = parse_declarator(specifiers, DECL_FLAGS_NONE);
rem_anchor_token('=');
}
expect(';');
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);
}
}
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(';');
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 == ':') {
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...
*/
}
}
+static void semantic_condition(expression_t const *const expr,
+ char const *const context)
+{
+ type_t *const type = skip_typeref(expr->base.type);
+ if (is_type_scalar(type)) {
+ warn_reference_address_as_bool(expr);
+ } else if (is_type_valid(type)) {
+ errorf(&expr->base.source_position,
+ "%s must have scalar type", context);
+ }
+}
+
/**
* Parse a conditional expression, ie. 'expression ? ... : ...'.
*
conditional_expression_t *conditional = &result->conditional;
conditional->condition = expression;
- warn_reference_address_as_bool(expression);
-
eat('?');
add_anchor_token(':');
- /* 6.5.15.2 */
- type_t *const condition_type_orig = expression->base.type;
- type_t *const condition_type = skip_typeref(condition_type_orig);
- if (!is_type_scalar(condition_type) && is_type_valid(condition_type)) {
- type_error("expected a scalar type in conditional condition",
- &expression->base.source_position, condition_type_orig);
- }
+ /* §6.5.15:2 The first operand shall have scalar type. */
+ semantic_condition(expression, "condition of conditional operator");
expression_t *true_expression = expression;
bool gnu_cond = false;
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);
+ }
}
}
static void semantic_not(unary_expression_t *expression)
{
- type_t *const orig_type = expression->value->base.type;
- type_t *const type = skip_typeref(orig_type);
- if (!is_type_scalar(type) && is_type_valid(type)) {
- errorf(&expression->base.source_position,
- "operand of ! must be of scalar type");
- }
-
- warn_reference_address_as_bool(expression->value);
-
+ /* §6.5.3.3:1 The operand [...] of the ! operator, scalar type. */
+ semantic_condition(expression->value, "operand of !");
expression->base.type = c_mode & _CXX ? type_bool : type_int;
}
*/
static void semantic_logical_op(binary_expression_t *expression)
{
- expression_t *const left = expression->left;
- expression_t *const right = expression->right;
- type_t *const orig_type_left = left->base.type;
- type_t *const orig_type_right = right->base.type;
- type_t *const type_left = skip_typeref(orig_type_left);
- type_t *const type_right = skip_typeref(orig_type_right);
-
- warn_reference_address_as_bool(left);
- warn_reference_address_as_bool(right);
-
- if (!is_type_scalar(type_left) || !is_type_scalar(type_right)) {
- /* TODO: improve error message */
- if (is_type_valid(type_left) && is_type_valid(type_right)) {
- errorf(&expression->base.source_position,
- "operation needs scalar types");
- }
- return;
- }
-
+ /* §6.5.13:2 Each of the operands shall have scalar type.
+ * §6.5.14:2 Each of the operands shall have scalar type. */
+ semantic_condition(expression->left, "left operand of logical operator");
+ semantic_condition(expression->right, "right operand of logical operator");
expression->base.type = c_mode & _CXX ? type_bool : type_int;
}
add_anchor_token(')');
expression_t *const expr = parse_expression();
statement->ifs.condition = expr;
- warn_reference_address_as_bool(expr);
+ /* §6.8.4.1:1 The controlling expression of an if statement shall have
+ * scalar type. */
+ semantic_condition(expr, "condition of 'if'-statment");
mark_vars_read(expr, NULL);
rem_anchor_token(')');
expect(')');
return body;
}
-static void check_conditon_type(expression_t const *const expr,
- char const *const stmt_name)
-{
- type_t *const type = skip_typeref(expr->base.type);
- /* §6.8.5:2 */
- if (!is_type_scalar(type) && is_type_valid(type)) {
- errorf(&expr->base.source_position,
- "condition of %s statement must have scalar type", stmt_name);
- }
-}
-
/**
* Parse a while statement.
*/
add_anchor_token(')');
expression_t *const cond = parse_expression();
statement->whiles.condition = cond;
- check_conditon_type(cond, "while");
- warn_reference_address_as_bool(cond);
+ /* §6.8.5:2 The controlling expression of an iteration statement shall
+ * have scalar type. */
+ semantic_condition(cond, "condition of 'while'-statement");
mark_vars_read(cond, NULL);
rem_anchor_token(')');
expect(')');
add_anchor_token(')');
expression_t *const cond = parse_expression();
statement->do_while.condition = cond;
- check_conditon_type(cond, "do-while");
- warn_reference_address_as_bool(cond);
+ /* §6.8.5:2 The controlling expression of an iteration statement shall
+ * have scalar type. */
+ semantic_condition(cond, "condition of 'do-while'-statement");
mark_vars_read(cond, NULL);
rem_anchor_token(')');
expect(')');
add_anchor_token(';');
expression_t *const cond = parse_expression();
statement->fors.condition = cond;
- check_conditon_type(cond, "for");
- warn_reference_address_as_bool(cond);
+ /* §6.8.5:2 The controlling expression of an iteration statement shall
+ * have scalar type. */
+ semantic_condition(cond, "condition of 'for'-statement");
mark_vars_read(cond, NULL);
rem_anchor_token(';');
}
} while (token.type == T___extension__);
bool old_gcc_extension = in_gcc_extension;
in_gcc_extension = true;
- statement = parse_statement();
+ statement = intern_parse_statement();
in_gcc_extension = old_gcc_extension;
break;