#include "adt/array.h"
//#define PRINT_TOKENS
-#define MAX_LOOKAHEAD 2
+#define MAX_LOOKAHEAD 1
typedef struct {
entity_t *old_entity;
typedef struct gnu_attribute_t gnu_attribute_t;
struct gnu_attribute_t {
- gnu_attribute_kind_t kind; /**< The kind of the GNU attribute. */
+ gnu_attribute_kind_t kind; /**< The kind of the GNU attribute. */
gnu_attribute_t *next;
- bool invalid; /**< Set if this attribute had argument errors, */
- bool have_arguments; /**< True, if this attribute has arguments. */
+ bool invalid; /**< Set if this attribute had argument errors, */
+ bool has_arguments; /**< True, if this attribute has arguments. */
union {
size_t value;
string_t string;
- atomic_type_kind_t akind;
+ symbol_t *symbol;
long argument; /**< Single argument. */
argument_list_t *arguments; /**< List of argument expressions. */
} u;
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. */
bool must_be_constant;
} parse_initializer_env_t;
+/**
+ * Capture a MS __base extension.
+ */
+typedef struct based_spec_t {
+ source_position_t source_position;
+ variable_t *base_variable;
+} based_spec_t;
+
typedef entity_t* (*parsed_declaration_func) (entity_t *declaration, bool is_definition);
/** The current token. */
-static token_t token;
+static token_t token;
/** The lookahead ring-buffer. */
-static token_t lookahead_buffer[MAX_LOOKAHEAD];
+static token_t lookahead_buffer[MAX_LOOKAHEAD];
/** Position of the next token in the lookahead buffer. */
-static int lookahead_bufpos;
-static stack_entry_t *environment_stack = NULL;
-static stack_entry_t *label_stack = NULL;
-static scope_t *file_scope = NULL;
-static scope_t *current_scope = NULL;
+static size_t lookahead_bufpos;
+static stack_entry_t *environment_stack = NULL;
+static stack_entry_t *label_stack = NULL;
+static scope_t *file_scope = NULL;
+static scope_t *current_scope = NULL;
/** Point to the current function declaration if inside a function. */
-static function_t *current_function = NULL;
-static entity_t *current_init_decl = NULL;
-static switch_statement_t *current_switch = NULL;
-static statement_t *current_loop = NULL;
-static statement_t *current_parent = NULL;
-static ms_try_statement_t *current_try = NULL;
-static linkage_kind_t current_linkage = LINKAGE_INVALID;
-static goto_statement_t *goto_first = NULL;
-static goto_statement_t *goto_last = NULL;
-static label_statement_t *label_first = NULL;
-static label_statement_t *label_last = NULL;
+static function_t *current_function = NULL;
+static entity_t *current_init_decl = NULL;
+static switch_statement_t *current_switch = NULL;
+static statement_t *current_loop = NULL;
+static statement_t *current_parent = NULL;
+static ms_try_statement_t *current_try = NULL;
+static linkage_kind_t current_linkage = LINKAGE_INVALID;
+static goto_statement_t *goto_first = NULL;
+static goto_statement_t **goto_anchor = NULL;
+static label_statement_t *label_first = NULL;
+static label_statement_t **label_anchor = NULL;
/** current translation unit. */
-static translation_unit_t *unit = NULL;
+static translation_unit_t *unit = NULL;
/** true if we are in a type property context (evaluation only for type. */
-static bool in_type_prop = false;
+static bool in_type_prop = false;
/** true in we are in a __extension__ context. */
-static bool in_gcc_extension = false;
-static struct obstack temp_obst;
+static bool in_gcc_extension = false;
+static struct obstack temp_obst;
+static entity_t *anonymous_entity;
+static declaration_t **incomplete_arrays;
#define PUSH_PARENT(stmt) \
static const symbol_t *sym_anonymous = NULL;
/* symbols for Microsoft extended-decl-modifier */
-static const symbol_t *sym_align = NULL;
-static const symbol_t *sym_allocate = NULL;
-static const symbol_t *sym_dllimport = NULL;
-static const symbol_t *sym_dllexport = NULL;
-static const symbol_t *sym_naked = NULL;
-static const symbol_t *sym_noinline = NULL;
-static const symbol_t *sym_noreturn = NULL;
-static const symbol_t *sym_nothrow = NULL;
-static const symbol_t *sym_novtable = NULL;
-static const symbol_t *sym_property = NULL;
-static const symbol_t *sym_get = NULL;
-static const symbol_t *sym_put = NULL;
-static const symbol_t *sym_selectany = NULL;
-static const symbol_t *sym_thread = NULL;
-static const symbol_t *sym_uuid = NULL;
-static const symbol_t *sym_deprecated = NULL;
-static const symbol_t *sym_restrict = NULL;
-static const symbol_t *sym_noalias = NULL;
+static const symbol_t *sym_align = NULL;
+static const symbol_t *sym_allocate = NULL;
+static const symbol_t *sym_dllimport = NULL;
+static const symbol_t *sym_dllexport = NULL;
+static const symbol_t *sym_naked = NULL;
+static const symbol_t *sym_noinline = NULL;
+static const symbol_t *sym_returns_twice = NULL;
+static const symbol_t *sym_noreturn = NULL;
+static const symbol_t *sym_nothrow = NULL;
+static const symbol_t *sym_novtable = NULL;
+static const symbol_t *sym_property = NULL;
+static const symbol_t *sym_get = NULL;
+static const symbol_t *sym_put = NULL;
+static const symbol_t *sym_selectany = NULL;
+static const symbol_t *sym_thread = NULL;
+static const symbol_t *sym_uuid = NULL;
+static const symbol_t *sym_deprecated = NULL;
+static const symbol_t *sym_restrict = NULL;
+static const symbol_t *sym_noalias = NULL;
/** The token anchor set */
static unsigned char token_anchor_set[T_LAST_TOKEN];
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: \
case T_union: \
case T_unsigned: \
case T_void: \
+ case T_wchar_t: \
COMPLEX_SPECIFIERS \
IMAGINARY_SPECIFIERS
return res;
}
+/**
+ * Returns the size of an entity node.
+ *
+ * @param kind the entity kind
+ */
static size_t get_entity_struct_size(entity_kind_t kind)
{
static const size_t sizes[] = {
[ENTITY_VARIABLE] = sizeof(variable_t),
- [ENTITY_COMPOUND_MEMBER] = sizeof(variable_t),
+ [ENTITY_PARAMETER] = sizeof(parameter_t),
+ [ENTITY_COMPOUND_MEMBER] = sizeof(compound_member_t),
[ENTITY_FUNCTION] = sizeof(function_t),
[ENTITY_TYPEDEF] = sizeof(typedef_t),
[ENTITY_STRUCT] = sizeof(compound_t),
[ENTITY_LOCAL_LABEL] = sizeof(label_t),
[ENTITY_NAMESPACE] = sizeof(namespace_t)
};
- assert(kind < sizeof(sizes) / sizeof(sizes[0]));
+ assert(kind < lengthof(sizes));
assert(sizes[kind] != 0);
return sizes[kind];
}
+/**
+ * Allocate an entity of given kind and initialize all
+ * fields with zero.
+ */
static entity_t *allocate_entity_zero(entity_kind_t kind)
{
size_t size = get_entity_struct_size(kind);
[STATEMENT_COMPOUND] = sizeof(compound_statement_t),
[STATEMENT_RETURN] = sizeof(return_statement_t),
[STATEMENT_DECLARATION] = sizeof(declaration_statement_t),
- [STATEMENT_LOCAL_LABEL] = sizeof(local_label_statement_t),
[STATEMENT_IF] = sizeof(if_statement_t),
[STATEMENT_SWITCH] = sizeof(switch_statement_t),
[STATEMENT_EXPRESSION] = sizeof(expression_statement_t),
[STATEMENT_MS_TRY] = sizeof(ms_try_statement_t),
[STATEMENT_LEAVE] = sizeof(leave_statement_t)
};
- assert(kind < sizeof(sizes) / sizeof(sizes[0]));
+ assert(kind < lengthof(sizes));
assert(sizes[kind] != 0);
return sizes[kind];
}
if (kind >= EXPR_BINARY_FIRST && kind <= EXPR_BINARY_LAST) {
return sizes[EXPR_BINARY_FIRST];
}
- assert(kind < sizeof(sizes) / sizeof(sizes[0]));
+ assert(kind < lengthof(sizes));
assert(sizes[kind] != 0);
return sizes[kind];
}
/**
* Allocate a statement node of given kind and initialize all
- * fields with zero.
+ * fields with zero. Sets its source position to the position
+ * of the current token.
*/
static statement_t *allocate_statement_zero(statement_kind_t kind)
{
}
/**
- * Creates a new invalid expression.
+ * Creates a new invalid expression at the source position
+ * of the current token.
*/
static expression_t *create_invalid_expression(void)
{
[TYPE_TYPEDEF] = sizeof(typedef_type_t),
[TYPE_TYPEOF] = sizeof(typeof_type_t),
};
- assert(sizeof(sizes) / sizeof(sizes[0]) == (int) TYPE_TYPEOF + 1);
+ assert(lengthof(sizes) == (int)TYPE_TYPEOF + 1);
assert(kind <= TYPE_TYPEOF);
assert(sizes[kind] != 0);
return sizes[kind];
[INITIALIZER_LIST] = sizeof(initializer_list_t),
[INITIALIZER_DESIGNATOR] = sizeof(initializer_designator_t)
};
- assert(kind < sizeof(sizes) / sizeof(*sizes));
+ assert(kind < lengthof(sizes));
assert(sizes[kind] != 0);
return sizes[kind];
}
return result;
}
-/**
- * Free a type from the type obstack.
- */
-static void free_type(void *type)
-{
- obstack_free(type_obst, type);
-}
-
/**
* Returns the index of the top element of the environment stack.
*/
lookahead_buffer[lookahead_bufpos] = lexer_token;
lexer_next_token();
- lookahead_bufpos = (lookahead_bufpos+1) % MAX_LOOKAHEAD;
+ lookahead_bufpos = (lookahead_bufpos + 1) % MAX_LOOKAHEAD;
#ifdef PRINT_TOKENS
print_token(stderr, &token);
/**
* Return the next token with a given lookahead.
*/
-static inline const token_t *look_ahead(int num)
+static inline const token_t *look_ahead(size_t num)
{
- assert(num > 0 && num <= MAX_LOOKAHEAD);
- int pos = (lookahead_bufpos+num-1) % MAX_LOOKAHEAD;
+ assert(0 < num && num <= MAX_LOOKAHEAD);
+ size_t pos = (lookahead_bufpos + num - 1) % MAX_LOOKAHEAD;
return &lookahead_buffer[pos];
}
/**
- * Adds a token to the token anchor set (a multi-set).
+ * Adds a token type to the token type anchor set (a multi-set).
*/
static void add_anchor_token(int token_type)
{
++token_anchor_set[token_type];
}
+/**
+ * Set the number of tokens types of the given type
+ * to zero and return the old count.
+ */
static int save_and_reset_anchor_state(int token_type)
{
assert(0 <= token_type && token_type < T_LAST_TOKEN);
return count;
}
+/**
+ * Restore the number of token types to the given count.
+ */
static void restore_anchor_state(int token_type, int count)
{
assert(0 <= token_type && token_type < T_LAST_TOKEN);
}
/**
- * Remove a token from the token anchor set (a multi-set).
+ * Remove a token type from the token type anchor set (a multi-set).
*/
static void rem_anchor_token(int token_type)
{
--token_anchor_set[token_type];
}
+/**
+ * Return true if the token type of the current token is
+ * in the anchor set.
+ */
static bool at_anchor(void)
{
if (token.type < 0)
}
/**
- * Eat tokens until a matching token is found.
+ * Eat tokens until a matching token type is found.
*/
static void eat_until_matching_token(int type)
{
}
}
+/**
+ * Eat a whole block from input tokens.
+ */
static void eat_block(void)
{
eat_until_matching_token('{');
next_token();
}
-#define eat(token_type) do { assert(token.type == (token_type)); next_token(); } while (0)
+#define eat(token_type) (assert(token.type == (token_type)), next_token())
/**
* Report a parse error because an expected token was not found.
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.
*/
}
/**
- * Expect the the current token is the expected token.
+ * Expect the current token is the expected token.
* If not, generate an error, eat the current statement,
* and goto the end_error label.
*/
-#define expect(expected) \
+#define expect(expected, error_label) \
do { \
if (UNLIKELY(token.type != (expected))) { \
parse_error_expected(NULL, (expected), NULL); \
if (token.type == expected) \
next_token(); \
rem_anchor_token(expected); \
- goto end_error; \
+ goto error_label; \
} \
next_token(); \
} while (0)
-static void scope_push(scope_t *new_scope)
+/**
+ * Push a given scope on the scope stack and make it the
+ * current scope
+ */
+static scope_t *scope_push(scope_t *new_scope)
{
if (current_scope != NULL) {
new_scope->depth = current_scope->depth + 1;
}
- new_scope->parent = current_scope;
- current_scope = new_scope;
+
+ scope_t *old_scope = current_scope;
+ current_scope = new_scope;
+ return old_scope;
}
-static void scope_pop(void)
+/**
+ * Pop the current scope from the scope stack.
+ */
+static void scope_pop(scope_t *old_scope)
{
- current_scope = current_scope->parent;
+ current_scope = old_scope;
}
/**
namespace_tag_t namespc)
{
entity_t *entity = symbol->entity;
- for( ; entity != NULL; entity = entity->base.symbol_next) {
+ for (; entity != NULL; entity = entity->base.symbol_next) {
if (entity->base.namespc == namespc)
return entity;
}
if (new_top == top)
return;
- for(i = top; i > new_top; --i) {
+ for (i = top; i > new_top; --i) {
stack_entry_t *entry = &stack[i - 1];
entity_t *old_entity = entry->old_entity;
return (int) akind;
}
+/**
+ * Return the type rank for an atomic type.
+ */
static int get_rank(const type_t *type)
{
assert(!is_typeref(type));
- /* The C-standard allows promoting enums to int or unsigned int (see § 7.2.2
- * and esp. footnote 108). However we can't fold constants (yet), so we
- * can't decide whether unsigned int is possible, while int always works.
- * (unsigned int would be preferable when possible... for stuff like
- * struct { enum { ... } bla : 4; } ) */
if (type->kind == TYPE_ENUM)
- return get_akind_rank(ATOMIC_TYPE_INT);
+ return get_akind_rank(type->enumt.akind);
assert(type->kind == TYPE_ATOMIC);
return get_akind_rank(type->atomic.akind);
}
+/**
+ * Do integer promotion for a given type.
+ *
+ * @param type the type to promote
+ * @return the promoted type
+ */
static type_t *promote_integer(type_t *type)
{
if (type->kind == TYPE_BITFIELD)
}
/**
- * Check if a given expression represents the 0 pointer constant.
+ * Check if a given expression represents a null pointer constant.
+ *
+ * @param expression the expression to check
*/
static bool is_null_pointer_constant(const expression_t *expression)
{
/* skip void* cast */
- if (expression->kind == EXPR_UNARY_CAST
- || expression->kind == EXPR_UNARY_CAST_IMPLICIT) {
- expression = expression->unary.value;
+ if (expression->kind == EXPR_UNARY_CAST ||
+ expression->kind == EXPR_UNARY_CAST_IMPLICIT) {
+ type_t *const type = skip_typeref(expression->base.type);
+ if (types_compatible(type, type_void_ptr))
+ expression = expression->unary.value;
}
- /* TODO: not correct yet, should be any constant integer expression
- * which evaluates to 0 */
- if (expression->kind != EXPR_CONST)
- return false;
-
type_t *const type = skip_typeref(expression->base.type);
- if (!is_type_integer(type))
- return false;
-
- return expression->conste.v.int_value == 0;
+ return
+ is_type_integer(type) &&
+ is_constant_expression(expression) &&
+ fold_constant(expression) == 0;
}
/**
if (!is_constant_expression(result)) {
errorf(&result->base.source_position,
- "expression '%E' is not constant\n", result);
+ "expression '%E' is not constant", result);
}
return result;
[GNU_AK_FASTCALL] = "fastcall",
[GNU_AK_DEPRECATED] = "deprecated",
[GNU_AK_NOINLINE] = "noinline",
+ [GNU_AK_RETURNS_TWICE] = "returns_twice",
[GNU_AK_NORETURN] = "noreturn",
[GNU_AK_NAKED] = "naked",
[GNU_AK_PURE] = "pure",
}
/**
- * Allocate a new gnu temporal attribute.
+ * Allocate a new gnu temporal attribute of given kind.
*/
static gnu_attribute_t *allocate_gnu_attribute(gnu_attribute_kind_t kind)
{
attribute->kind = kind;
attribute->next = NULL;
attribute->invalid = false;
- attribute->have_arguments = false;
+ attribute->has_arguments = false;
return attribute;
}
/**
- * parse one constant expression argument.
+ * Parse one constant expression argument of the given attribute.
*/
static void parse_gnu_attribute_const_arg(gnu_attribute_t *attribute)
{
add_anchor_token(')');
expression = parse_constant_expression();
rem_anchor_token(')');
- expect(')');
+ expect(')', end_error);
attribute->u.argument = fold_constant(expression);
return;
end_error:
}
/**
- * parse a list of constant expressions arguments.
+ * Parse a list of constant expressions arguments of the given attribute.
*/
static void parse_gnu_attribute_const_arg_list(gnu_attribute_t *attribute)
{
}
rem_anchor_token(',');
rem_anchor_token(')');
- expect(')');
+ expect(')', end_error);
return;
end_error:
attribute->invalid = true;
}
/**
- * parse one string literal argument.
+ * Parse one string literal argument of the given attribute.
*/
static void parse_gnu_attribute_string_arg(gnu_attribute_t *attribute,
string_t *string)
}
*string = parse_string_literals();
rem_anchor_token('(');
- expect(')');
+ expect(')', end_error);
return;
end_error:
attribute->invalid = true;
}
/**
- * parse one tls model.
+ * Parse one tls model of the given attribute.
*/
static void parse_gnu_attribute_tls_model_arg(gnu_attribute_t *attribute)
{
string_t string = { NULL, 0 };
parse_gnu_attribute_string_arg(attribute, &string);
if (string.begin != NULL) {
- for(size_t i = 0; i < 4; ++i) {
+ for (size_t i = 0; i < 4; ++i) {
if (strcmp(tls_models[i], string.begin) == 0) {
attribute->u.value = i;
return;
}
/**
- * parse one tls model.
+ * Parse one tls model of the given attribute.
*/
static void parse_gnu_attribute_visibility_arg(gnu_attribute_t *attribute)
{
string_t string = { NULL, 0 };
parse_gnu_attribute_string_arg(attribute, &string);
if (string.begin != NULL) {
- for(size_t i = 0; i < 4; ++i) {
+ for (size_t i = 0; i < 4; ++i) {
if (strcmp(visibilities[i], string.begin) == 0) {
attribute->u.value = i;
return;
}
/**
- * parse one (code) model.
+ * Parse one (code) model of the given attribute.
*/
static void parse_gnu_attribute_model_arg(gnu_attribute_t *attribute)
{
string_t string = { NULL, 0 };
parse_gnu_attribute_string_arg(attribute, &string);
if (string.begin != NULL) {
- for(int i = 0; i < 3; ++i) {
+ for (int i = 0; i < 3; ++i) {
if (strcmp(visibilities[i], string.begin) == 0) {
attribute->u.value = i;
return;
attribute->invalid = true;
}
+/**
+ * Parse one mode of the given attribute.
+ */
static void parse_gnu_attribute_mode_arg(gnu_attribute_t *attribute)
{
- /* TODO: find out what is allowed here... */
-
- /* at least: byte, word, pointer, list of machine modes
- * __XXX___ is interpreted as XXX */
add_anchor_token(')');
if (token.type != T_IDENTIFIER) {
- expect(T_IDENTIFIER);
+ expect(T_IDENTIFIER, end_error);
}
- /* 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 ||
- 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
- || 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) {
- attribute->u.akind = ATOMIC_TYPE_LONGLONG;
- } else {
- if (warning.other)
- warningf(HERE, "ignoring unknown mode '%s'", symbol_str);
- attribute->invalid = true;
- }
+ attribute->u.symbol = token.v.symbol;
next_token();
rem_anchor_token(')');
- expect(')');
+ expect(')', end_error);
return;
end_error:
attribute->invalid = true;
}
/**
- * parse one interrupt argument.
+ * Parse one interrupt argument of the given attribute.
*/
static void parse_gnu_attribute_interrupt_arg(gnu_attribute_t *attribute)
{
string_t string = { NULL, 0 };
parse_gnu_attribute_string_arg(attribute, &string);
if (string.begin != NULL) {
- for(size_t i = 0; i < 5; ++i) {
+ for (size_t i = 0; i < 5; ++i) {
if (strcmp(interrupts[i], string.begin) == 0) {
attribute->u.value = i;
return;
}
/**
- * parse ( identifier, const expression, const expression )
+ * Parse ( identifier, const expression, const expression )
*/
static void parse_gnu_attribute_format_args(gnu_attribute_t *attribute)
{
goto end_error;
}
const char *name = token.v.symbol->string;
- for(i = 0; i < 4; ++i) {
+ for (i = 0; i < 4; ++i) {
if (strcmp_underscore(format_names[i], name) == 0)
break;
}
}
next_token();
- expect(',');
+ expect(',', end_error);
add_anchor_token(')');
add_anchor_token(',');
parse_constant_expression();
rem_anchor_token(',');
rem_anchor_token(')');
- expect(',');
+ expect(',', end_error);
add_anchor_token(')');
parse_constant_expression();
rem_anchor_token(')');
- expect(')');
+ expect(')', end_error);
return;
end_error:
attribute->u.value = true;
}
+/**
+ * Check that a given GNU attribute has no arguments.
+ */
static void check_no_argument(gnu_attribute_t *attribute, const char *name)
{
- if (!attribute->have_arguments)
+ if (!attribute->has_arguments)
return;
/* should have no arguments */
gnu_attribute_t *attribute;
eat(T___attribute__);
- expect('(');
- expect('(');
+ expect('(', end_error);
+ expect('(', end_error);
if (token.type != ')') {
/* find the end of the list */
next_token();
int i;
- for(i = 0; i < GNU_AK_LAST; ++i) {
+ for (i = 0; i < GNU_AK_LAST; ++i) {
if (strcmp_underscore(gnu_attribute_names[i], name) == 0)
break;
}
/* empty args are allowed */
next_token();
} else
- attribute->have_arguments = true;
+ attribute->has_arguments = true;
}
switch (kind) {
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_RETURNS_TWICE: modifiers |= DM_RETURNS_TWICE; 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_ALIGNED:
/* __align__ may be used without an argument */
- if (attribute->have_arguments) {
+ if (attribute->has_arguments) {
parse_gnu_attribute_const_arg(attribute);
}
break;
case GNU_AK_FORMAT_ARG:
case GNU_AK_REGPARM:
case GNU_AK_TRAP_EXIT:
- if (!attribute->have_arguments) {
+ if (!attribute->has_arguments) {
/* should have arguments */
errorf(HERE, "wrong number of arguments specified for '%s' attribute", name);
attribute->invalid = true;
case GNU_AK_ALIAS:
case GNU_AK_SECTION:
case GNU_AK_SP_SWITCH:
- if (!attribute->have_arguments) {
+ if (!attribute->has_arguments) {
/* should have arguments */
errorf(HERE, "wrong number of arguments specified for '%s' attribute", name);
attribute->invalid = true;
parse_gnu_attribute_string_arg(attribute, &attribute->u.string);
break;
case GNU_AK_FORMAT:
- if (!attribute->have_arguments) {
+ if (!attribute->has_arguments) {
/* should have arguments */
errorf(HERE, "wrong number of arguments specified for '%s' attribute", name);
attribute->invalid = true;
break;
case GNU_AK_WEAKREF:
/* may have one string argument */
- if (attribute->have_arguments)
+ if (attribute->has_arguments)
parse_gnu_attribute_string_arg(attribute, &attribute->u.string);
break;
case GNU_AK_NONNULL:
- if (attribute->have_arguments)
+ if (attribute->has_arguments)
parse_gnu_attribute_const_arg_list(attribute);
break;
case GNU_AK_TLS_MODEL:
- if (!attribute->have_arguments) {
+ if (!attribute->has_arguments) {
/* should have arguments */
errorf(HERE, "wrong number of arguments specified for '%s' attribute", name);
} else
parse_gnu_attribute_tls_model_arg(attribute);
break;
case GNU_AK_VISIBILITY:
- if (!attribute->have_arguments) {
+ if (!attribute->has_arguments) {
/* should have arguments */
errorf(HERE, "wrong number of arguments specified for '%s' attribute", name);
} else
parse_gnu_attribute_visibility_arg(attribute);
break;
case GNU_AK_MODEL:
- if (!attribute->have_arguments) {
+ if (!attribute->has_arguments) {
/* should have arguments */
errorf(HERE, "wrong number of arguments specified for '%s' attribute", name);
} else {
}
break;
case GNU_AK_MODE:
- if (!attribute->have_arguments) {
+ if (!attribute->has_arguments) {
/* should have arguments */
errorf(HERE, "wrong number of arguments specified for '%s' attribute", name);
} else {
break;
case GNU_AK_INTERRUPT:
/* may have one string argument */
- if (attribute->have_arguments)
+ if (attribute->has_arguments)
parse_gnu_attribute_interrupt_arg(attribute);
break;
case GNU_AK_SENTINEL:
/* may have one string argument */
- if (attribute->have_arguments)
+ if (attribute->has_arguments)
parse_gnu_attribute_const_arg(attribute);
break;
case GNU_AK_LAST:
next_token();
}
}
- expect(')');
- expect(')');
+ expect(')', end_error);
+ expect(')', end_error);
end_error:
*attributes = head;
case T_asm:
next_token();
- expect('(');
+ expect('(', end_error);
if (token.type != T_STRING_LITERAL) {
parse_error_expected("while parsing assembler attribute",
T_STRING_LITERAL, NULL);
} else {
parse_string_literals();
}
- expect(')');
+ expect(')', end_error);
continue;
case T_cdecl: modifiers |= DM_CDECL; break;
}
}
-static void mark_vars_read(expression_t *expr, variable_t *lhs_var);
+static void mark_vars_read(expression_t *expr, entity_t *lhs_ent);
-static variable_t *determine_lhs_var(expression_t *const expr,
- variable_t *lhs_var)
+static entity_t *determine_lhs_ent(expression_t *const expr,
+ entity_t *lhs_ent)
{
switch (expr->kind) {
case EXPR_REFERENCE: {
entity_t *const entity = expr->reference.entity;
- /* we should only find variables as lavlues... */
- if (entity->base.kind != ENTITY_VARIABLE)
+ /* we should only find variables as lvalues... */
+ if (entity->base.kind != ENTITY_VARIABLE
+ && entity->base.kind != ENTITY_PARAMETER)
return NULL;
- return &entity->variable;
+ return entity;
}
case EXPR_ARRAY_ACCESS: {
- expression_t *const ref = expr->array_access.array_ref;
- variable_t * var = NULL;
+ expression_t *const ref = expr->array_access.array_ref;
+ entity_t * ent = NULL;
if (is_type_array(skip_typeref(revert_automatic_type_conversion(ref)))) {
- var = determine_lhs_var(ref, lhs_var);
- lhs_var = var;
+ ent = determine_lhs_ent(ref, lhs_ent);
+ lhs_ent = ent;
} else {
- mark_vars_read(expr->select.compound, lhs_var);
+ mark_vars_read(expr->select.compound, lhs_ent);
}
- mark_vars_read(expr->array_access.index, lhs_var);
- return var;
+ mark_vars_read(expr->array_access.index, lhs_ent);
+ return ent;
}
case EXPR_SELECT: {
if (is_type_compound(skip_typeref(expr->base.type))) {
- return determine_lhs_var(expr->select.compound, lhs_var);
+ return determine_lhs_ent(expr->select.compound, lhs_ent);
} else {
- mark_vars_read(expr->select.compound, lhs_var);
+ mark_vars_read(expr->select.compound, lhs_ent);
return NULL;
}
}
expression_t *const val = expr->unary.value;
if (val->kind == EXPR_UNARY_TAKE_ADDRESS) {
/* *&x is a NOP */
- return determine_lhs_var(val->unary.value, lhs_var);
+ return determine_lhs_ent(val->unary.value, lhs_ent);
} else {
mark_vars_read(val, NULL);
return NULL;
}
}
-#define VAR_ANY ((variable_t*)-1)
+#define ENT_ANY ((entity_t*)-1)
/**
- * Mark declarations, which are read. This is used to deted variables, which
+ * Mark declarations, which are read. This is used to detect variables, which
* are never read.
* Example:
* x = x + 1;
* x and y are not detected as "not read", because multiple variables are
* involved.
*/
-static void mark_vars_read(expression_t *const expr, variable_t *lhs_var)
+static void mark_vars_read(expression_t *const expr, entity_t *lhs_ent)
{
switch (expr->kind) {
case EXPR_REFERENCE: {
entity_t *const entity = expr->reference.entity;
- if (entity->kind != ENTITY_VARIABLE)
+ if (entity->kind != ENTITY_VARIABLE
+ && entity->kind != ENTITY_PARAMETER)
return;
- variable_t *variable = &entity->variable;
- if (lhs_var != variable && lhs_var != VAR_ANY) {
- variable->read = true;
+ if (lhs_ent != entity && lhs_ent != ENT_ANY) {
+ if (entity->kind == ENTITY_VARIABLE) {
+ entity->variable.read = true;
+ } else {
+ entity->parameter.read = true;
+ }
}
return;
}
// TODO lhs_decl should depend on whether true/false have an effect
mark_vars_read(expr->conditional.condition, NULL);
if (expr->conditional.true_expression != NULL)
- mark_vars_read(expr->conditional.true_expression, lhs_var);
- mark_vars_read(expr->conditional.false_expression, lhs_var);
+ mark_vars_read(expr->conditional.true_expression, lhs_ent);
+ mark_vars_read(expr->conditional.false_expression, lhs_ent);
return;
case EXPR_SELECT:
- if (lhs_var == VAR_ANY && !is_type_compound(skip_typeref(expr->base.type)))
- lhs_var = NULL;
- mark_vars_read(expr->select.compound, lhs_var);
+ if (lhs_ent == ENT_ANY
+ && !is_type_compound(skip_typeref(expr->base.type)))
+ lhs_ent = NULL;
+ mark_vars_read(expr->select.compound, lhs_ent);
return;
case EXPR_ARRAY_ACCESS: {
expression_t *const ref = expr->array_access.array_ref;
- mark_vars_read(ref, lhs_var);
- lhs_var = determine_lhs_var(ref, lhs_var);
- mark_vars_read(expr->array_access.index, lhs_var);
+ mark_vars_read(ref, lhs_ent);
+ lhs_ent = determine_lhs_ent(ref, lhs_ent);
+ mark_vars_read(expr->array_access.index, lhs_ent);
return;
}
case EXPR_VA_ARG:
- mark_vars_read(expr->va_arge.ap, lhs_var);
+ mark_vars_read(expr->va_arge.ap, lhs_ent);
return;
case EXPR_UNARY_CAST:
/* Special case: Use void cast to mark a variable as "read" */
if (is_type_atomic(skip_typeref(expr->base.type), ATOMIC_TYPE_VOID))
- lhs_var = NULL;
+ lhs_ent = NULL;
goto unary;
case EXPR_UNARY_DEREFERENCE:
case EXPR_UNARY_DELETE:
case EXPR_UNARY_DELETE_ARRAY:
- if (lhs_var == VAR_ANY)
- lhs_var = NULL;
+ if (lhs_ent == ENT_ANY)
+ lhs_ent = NULL;
goto unary;
case EXPR_UNARY_NEGATE:
case EXPR_UNARY_CAST_IMPLICIT:
case EXPR_UNARY_ASSUME:
unary:
- mark_vars_read(expr->unary.value, lhs_var);
+ mark_vars_read(expr->unary.value, lhs_ent);
return;
case EXPR_BINARY_ADD:
case EXPR_BINARY_ISLESSEQUAL:
case EXPR_BINARY_ISLESSGREATER:
case EXPR_BINARY_ISUNORDERED:
- mark_vars_read(expr->binary.left, lhs_var);
- mark_vars_read(expr->binary.right, lhs_var);
+ mark_vars_read(expr->binary.left, lhs_ent);
+ mark_vars_read(expr->binary.right, lhs_ent);
return;
case EXPR_BINARY_ASSIGN:
case EXPR_BINARY_BITWISE_AND_ASSIGN:
case EXPR_BINARY_BITWISE_XOR_ASSIGN:
case EXPR_BINARY_BITWISE_OR_ASSIGN: {
- if (lhs_var == VAR_ANY)
- lhs_var = NULL;
- lhs_var = determine_lhs_var(expr->binary.left, lhs_var);
- mark_vars_read(expr->binary.right, lhs_var);
+ if (lhs_ent == ENT_ANY)
+ lhs_ent = NULL;
+ lhs_ent = determine_lhs_ent(expr->binary.left, lhs_ent);
+ mark_vars_read(expr->binary.right, lhs_ent);
return;
}
case EXPR_VA_START:
- determine_lhs_var(expr->va_starte.ap, lhs_var);
+ determine_lhs_ent(expr->va_starte.ap, lhs_ent);
return;
case EXPR_UNKNOWN:
case EXPR_OFFSETOF:
case EXPR_STATEMENT: // TODO
case EXPR_LABEL_ADDRESS:
- case EXPR_BINARY_BUILTIN_EXPECT:
case EXPR_REFERENCE_ENUM_VALUE:
return;
}
add_anchor_token(']');
designator->array_index = parse_constant_expression();
rem_anchor_token(']');
- expect(']');
+ expect(']', end_error);
break;
case '.':
designator = allocate_ast_zero(sizeof(designator[0]));
next_token();
break;
default:
- expect('=');
+ expect('=', end_error);
return result;
}
return initializer_from_string(array_type,
&expression->string.value);
}
+ break;
case EXPR_WIDE_STRING_LITERAL: {
type_t *bare_wchar_type = skip_typeref(type_wchar_t);
return initializer_from_wide_string(array_type,
&expression->wide_string.value);
}
+ break;
}
default:
mark_vars_read(expression, NULL);
if (must_be_constant && !is_initializer_constant(expression)) {
errorf(&expression->base.source_position,
- "Initialisation expression '%E' is not constant\n",
+ "Initialisation expression '%E' is not constant",
expression);
}
{
size_t len = ARR_LEN(path->path);
- for(size_t i = 0; i < len; ++i) {
+ for (size_t i = 0; i < len; ++i) {
const type_path_entry_t *entry = & path->path[i];
type_t *type = skip_typeref(entry->type);
fprintf(stderr, ".%s",
entry->v.compound_entry->base.symbol->string);
} else if (is_type_array(type)) {
- fprintf(stderr, "[%zu]", entry->v.index);
+ fprintf(stderr, "[%u]", (unsigned) entry->v.index);
} else {
fprintf(stderr, "-INVALID-");
}
static bool walk_designator(type_path_t *path, const designator_t *designator,
bool used_in_offsetof)
{
- for( ; designator != NULL; designator = designator->next) {
+ for (; designator != NULL; designator = designator->next) {
type_path_entry_t *top = get_type_path_top(path);
type_t *orig_type = top->type;
} else {
compound_t *compound = type->compound.compound;
entity_t *iter = compound->members.entities;
- for( ; iter != NULL; iter = iter->base.next) {
+ for (; iter != NULL; iter = iter->base.next) {
if (iter->base.symbol == symbol) {
break;
}
if (type != NULL) {
ascend_from_subtype(path);
- expect('}');
+ expect('}', end_error);
} else {
- expect('}');
+ expect('}', end_error);
goto error_parse_next;
}
}
} else {
/* must be an expression */
expression_t *expression = parse_assignment_expression();
+ mark_vars_read(expression, NULL);
if (env->must_be_constant && !is_initializer_constant(expression)) {
errorf(&expression->base.source_position,
- "Initialisation expression '%E' is not constant\n",
+ "Initialisation expression '%E' is not constant",
expression);
}
if (token.type == '}') {
break;
}
- expect(',');
+ expect(',', end_error);
if (token.type == '}') {
break;
}
*/
static initializer_t *parse_initializer(parse_initializer_env_t *env)
{
- type_t *type = skip_typeref(env->type);
- initializer_t *result = NULL;
- size_t max_index;
+ type_t *type = skip_typeref(env->type);
+ size_t max_index = 0xdeadbeaf; // TODO: Resolve this uninitialized variable problem
+ initializer_t *result;
if (is_type_scalar(type)) {
result = parse_scalar_initializer(type, env->must_be_constant);
max_index = path.max_index;
DEL_ARR_F(path.path);
- expect('}');
+ expect('}', end_error);
} else {
/* parse_scalar_initializer() also works in this case: we simply
* have an expression without {} around it */
result = parse_scalar_initializer(type, env->must_be_constant);
}
- /* § 6.7.8 (22) array initializers for arrays with unknown size determine
+ /* § 6.7.8:22 array initializers for arrays with unknown size determine
* the array type size */
if (is_type_array(type) && type->array.size_expression == NULL
&& result != NULL) {
size_t size;
switch (result->kind) {
case INITIALIZER_LIST:
+ assert(max_index != 0xdeadbeaf);
size = max_index + 1;
break;
eat(T_union);
}
- symbol_t *symbol = NULL;
+ symbol_t *symbol = NULL;
compound_t *compound = NULL;
if (token.type == T___attribute__) {
compound = &entity->compound;
if (compound->base.parent_scope != current_scope &&
(token.type == '{' || token.type == ';')) {
- /* we're in an inner scope and have a definition. Override
- existing definition in outer scope */
+ /* we're in an inner scope and have a definition. Shadow
+ * existing definition in outer scope */
compound = NULL;
} else if (compound->complete && token.type == '{') {
assert(symbol != NULL);
}
if (token.type == '{') {
- compound->complete = true;
-
parse_compound_type_entries(compound);
modifiers |= parse_attributes(&attributes);
+
+ if (symbol == NULL) {
+ assert(anonymous_entity == NULL);
+ anonymous_entity = (entity_t*)compound;
+ }
}
compound->modifiers |= modifiers;
eat('{');
if (token.type == '}') {
- next_token();
errorf(HERE, "empty enum not allowed");
+ next_token();
return;
}
} while (token.type != '}');
rem_anchor_token('}');
- expect('}');
+ expect('}', end_error);
end_error:
;
next_token();
entity = get_entity(symbol, NAMESPACE_ENUM);
- assert(entity == NULL || entity->kind == ENTITY_ENUM);
+ if (entity != NULL) {
+ assert(entity->kind == ENTITY_ENUM);
+ if (entity->base.parent_scope != current_scope &&
+ (token.type == '{' || token.type == ';')) {
+ /* we're in an inner scope and have a definition. Shadow
+ * existing definition in outer scope */
+ entity = NULL;
+ } else if (entity->enume.complete && token.type == '{') {
+ errorf(HERE, "multiple definitions of 'enum %Y' (previous definition %P)",
+ symbol, &entity->base.source_position);
+ }
+ }
} else if (token.type != '{') {
parse_error_expected("while parsing enum type specifier",
T_IDENTIFIER, '{', NULL);
type_t *const type = allocate_type_zero(TYPE_ENUM);
type->enumt.enume = &entity->enume;
+ type->enumt.akind = ATOMIC_TYPE_INT;
if (token.type == '{') {
- if (entity->enume.complete) {
- errorf(HERE, "multiple definitions of enum %Y (previous definition %P)",
- symbol, &entity->base.source_position);
- }
if (symbol != NULL) {
environment_push(entity);
}
parse_enum_entries(type);
parse_attributes(&attributes);
- } else if(!entity->enume.complete && !(c_mode & _GNUC)) {
- errorf(HERE, "enum %Y used before definition (incomplete enumes are a GNU extension)",
+
+ if (symbol == NULL) {
+ assert(anonymous_entity == NULL);
+ anonymous_entity = entity;
+ }
+ } else if (!entity->enume.complete && !(c_mode & _GNUC)) {
+ errorf(HERE, "'enum %Y' used before definition (incomplete enums are a GNU extension)",
symbol);
}
type_t *type;
- expect('(');
+ expect('(', end_error);
add_anchor_token(')');
expression_t *expression = NULL;
in_gcc_extension = old_gcc_extension;
rem_anchor_token(')');
- expect(')');
+ expect(')', end_error);
type_t *typeof_type = allocate_type_zero(TYPE_TYPEOF);
typeof_type->typeoft.expression = expression;
SPECIFIER_INT = 1 << 3,
SPECIFIER_DOUBLE = 1 << 4,
SPECIFIER_CHAR = 1 << 5,
- SPECIFIER_SHORT = 1 << 6,
- SPECIFIER_LONG_LONG = 1 << 7,
- SPECIFIER_FLOAT = 1 << 8,
- SPECIFIER_BOOL = 1 << 9,
- SPECIFIER_VOID = 1 << 10,
- SPECIFIER_INT8 = 1 << 11,
- SPECIFIER_INT16 = 1 << 12,
- SPECIFIER_INT32 = 1 << 13,
- SPECIFIER_INT64 = 1 << 14,
- SPECIFIER_INT128 = 1 << 15,
- SPECIFIER_COMPLEX = 1 << 16,
- SPECIFIER_IMAGINARY = 1 << 17,
+ SPECIFIER_WCHAR_T = 1 << 6,
+ SPECIFIER_SHORT = 1 << 7,
+ SPECIFIER_LONG_LONG = 1 << 8,
+ SPECIFIER_FLOAT = 1 << 9,
+ SPECIFIER_BOOL = 1 << 10,
+ SPECIFIER_VOID = 1 << 11,
+ SPECIFIER_INT8 = 1 << 12,
+ SPECIFIER_INT16 = 1 << 13,
+ SPECIFIER_INT32 = 1 << 14,
+ SPECIFIER_INT64 = 1 << 15,
+ SPECIFIER_INT128 = 1 << 16,
+ SPECIFIER_COMPLEX = 1 << 17,
+ SPECIFIER_IMAGINARY = 1 << 18,
} specifiers_t;
static type_t *create_builtin_type(symbol_t *const symbol,
type_t *type = allocate_type_zero(TYPE_BUILTIN);
type->builtin.symbol = symbol;
type->builtin.real_type = real_type;
-
- type_t *result = typehash_insert(type);
- if (type != result) {
- free_type(type);
- }
-
- return result;
+ return identify_new_type(type);
}
static type_t *get_typedef_type(symbol_t *symbol)
symbol_t *symbol = token.v.symbol;
if (symbol == sym_align) {
next_token();
- expect('(');
+ expect('(', end_error);
if (token.type != T_INTEGER)
goto end_error;
if (check_alignment_value(token.v.intvalue)) {
specifiers->alignment = (unsigned char)token.v.intvalue;
}
next_token();
- expect(')');
+ expect(')', end_error);
} else if (symbol == sym_allocate) {
next_token();
- expect('(');
+ expect('(', end_error);
if (token.type != T_IDENTIFIER)
goto end_error;
(void)token.v.symbol;
- expect(')');
+ expect(')', end_error);
} else if (symbol == sym_dllimport) {
next_token();
DET_MOD(dllimport, DM_DLLIMPORT);
} else if (symbol == sym_noinline) {
next_token();
DET_MOD(noinline, DM_NOINLINE);
+ } else if (symbol == sym_returns_twice) {
+ next_token();
+ DET_MOD(returns_twice, DM_RETURNS_TWICE);
} else if (symbol == sym_noreturn) {
next_token();
DET_MOD(noreturn, DM_NORETURN);
DET_MOD(novtable, DM_NOVTABLE);
} else if (symbol == sym_property) {
next_token();
- expect('(');
+ expect('(', end_error);
for (;;) {
bool is_get = false;
if (token.type != T_IDENTIFIER)
goto end_error;
}
next_token();
- expect('=');
+ expect('=', end_error);
if (token.type != T_IDENTIFIER)
goto end_error;
if (is_get) {
}
break;
}
- expect(')');
+ expect(')', end_error);
} else if (symbol == sym_selectany) {
next_token();
DET_MOD(selectany, DM_SELECTANY);
} else if (symbol == sym_uuid) {
next_token();
- expect('(');
+ expect('(', end_error);
if (token.type != T_STRING_LITERAL)
goto end_error;
next_token();
- expect(')');
+ expect(')', end_error);
} else if (symbol == sym_deprecated) {
next_token();
if (specifiers->deprecated != 0 && warning.other)
} else {
errorf(HERE, "string literal expected");
}
- expect(')');
+ expect(')', end_error);
}
} else if (symbol == sym_noalias) {
next_token();
DET_MOD(noalias, DM_NOALIAS);
} else {
if (warning.other)
- warningf(HERE, "Unknown modifier %Y ignored", token.v.symbol);
+ warningf(HERE, "Unknown modifier '%Y' ignored", token.v.symbol);
next_token();
if (token.type == '(')
skip_until(')');
entity->declaration.type = type_error_type;
entity->declaration.implicit = true;
} else if (kind == ENTITY_TYPEDEF) {
- entity->typedefe.type = type_error_type;
+ entity->typedefe.type = type_error_type;
+ entity->typedefe.builtin = true;
}
- record_entity(entity, false);
+ if (kind != ENTITY_COMPOUND_MEMBER)
+ record_entity(entity, false);
return entity;
}
+static void parse_microsoft_based(based_spec_t *based_spec)
+{
+ if (token.type != T_IDENTIFIER) {
+ parse_error_expected("while parsing __based", T_IDENTIFIER, NULL);
+ return;
+ }
+ symbol_t *symbol = token.v.symbol;
+ entity_t *entity = get_entity(symbol, NAMESPACE_NORMAL);
+
+ if (entity == NULL || entity->base.kind != ENTITY_VARIABLE) {
+ errorf(HERE, "'%Y' is not a variable name.", symbol);
+ entity = create_error_entity(symbol, ENTITY_VARIABLE);
+ } else {
+ variable_t *variable = &entity->variable;
+
+ if (based_spec->base_variable != NULL) {
+ errorf(HERE, "__based type qualifier specified more than once");
+ }
+ based_spec->source_position = token.source_position;
+ based_spec->base_variable = variable;
+
+ type_t *const type = variable->base.type;
+
+ if (is_type_valid(type)) {
+ if (! is_type_pointer(skip_typeref(type))) {
+ errorf(HERE, "variable in __based modifier must have pointer type instead of '%T'", type);
+ }
+ if (variable->base.base.parent_scope != file_scope) {
+ errorf(HERE, "a nonstatic local variable may not be used in a __based specification");
+ }
+ }
+ }
+ next_token();
+}
+
/**
* Finish the construction of a struct type by calculating
* its size, offsets, alignment.
if (offset > size)
need_pad = true;
- if (warning.padded && need_pad) {
- warningf(&compound->base.source_position,
- "'%#T' needs padding", type, compound->base.symbol);
- }
- if (warning.packed && !need_pad) {
- warningf(&compound->base.source_position,
- "superfluous packed attribute on '%#T'",
- type, compound->base.symbol);
+ if (need_pad) {
+ if (warning.padded) {
+ warningf(&compound->base.source_position, "'%T' needs padding", type);
+ }
+ } else {
+ if (compound->modifiers & DM_PACKED && warning.packed) {
+ warningf(&compound->base.source_position,
+ "superfluous packed attribute on '%T'", type);
+ }
}
type->base.size = offset;
type->base.alignment = alignment;
}
+static type_t *handle_attribute_mode(const gnu_attribute_t *attribute,
+ type_t *orig_type)
+{
+ type_t *type = skip_typeref(orig_type);
+
+ /* at least: byte, word, pointer, list of machine modes
+ * __XXX___ is interpreted as XXX */
+
+ /* 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 = attribute->u.symbol->string;
+ bool sign = is_type_signed(type);
+ atomic_type_kind_t akind;
+ if (strcmp_underscore("QI", symbol_str) == 0 ||
+ strcmp_underscore("byte", symbol_str) == 0) {
+ akind = sign ? ATOMIC_TYPE_CHAR : ATOMIC_TYPE_UCHAR;
+ } else if (strcmp_underscore("HI", symbol_str) == 0) {
+ akind = sign ? ATOMIC_TYPE_SHORT : ATOMIC_TYPE_USHORT;
+ } else if (strcmp_underscore("SI", symbol_str) == 0
+ || strcmp_underscore("word", symbol_str) == 0
+ || strcmp_underscore("pointer", symbol_str) == 0) {
+ akind = sign ? ATOMIC_TYPE_INT : ATOMIC_TYPE_UINT;
+ } else if (strcmp_underscore("DI", symbol_str) == 0) {
+ akind = sign ? ATOMIC_TYPE_LONGLONG : ATOMIC_TYPE_ULONGLONG;
+ } else {
+ if (warning.other)
+ warningf(HERE, "ignoring unknown mode '%s'", symbol_str);
+ return orig_type;
+ }
+
+ if (type->kind == TYPE_ATOMIC) {
+ type_t *copy = duplicate_type(type);
+ copy->atomic.akind = akind;
+ return identify_new_type(copy);
+ } else if (type->kind == TYPE_ENUM) {
+ type_t *copy = duplicate_type(type);
+ copy->enumt.akind = akind;
+ return identify_new_type(copy);
+ } else if (is_type_pointer(type)) {
+ warningf(HERE, "__attribute__((mode)) on pointers not implemented yet (ignored)");
+ return type;
+ }
+
+ errorf(HERE, "__attribute__((mode)) only allowed on integer, enum or pointer type");
+ return orig_type;
+}
+
+static type_t *handle_type_attributes(const gnu_attribute_t *attributes,
+ type_t *type)
+{
+ const gnu_attribute_t *attribute = attributes;
+ for ( ; attribute != NULL; attribute = attribute->next) {
+ if (attribute->invalid)
+ continue;
+
+ if (attribute->kind == GNU_AK_MODE) {
+ type = handle_attribute_mode(attribute, type);
+ } else if (attribute->kind == GNU_AK_ALIGNED) {
+ int alignment = 32; /* TODO: fill in maximum useful alignment for
+ target machine */
+ if (attribute->has_arguments)
+ alignment = attribute->u.argument;
+
+ type_t *copy = duplicate_type(type);
+ copy->base.alignment = attribute->u.argument;
+ type = identify_new_type(copy);
+ }
+ }
+
+ return type;
+}
+
static void parse_declaration_specifiers(declaration_specifiers_t *specifiers)
{
type_t *type = NULL;
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 */
#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;
case T__declspec:
next_token();
- expect('(');
+ expect('(', end_error);
add_anchor_token(')');
parse_microsoft_extended_decl_modifier(specifiers);
rem_anchor_token(')');
- expect(')');
+ expect(')', end_error);
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;
MATCH_SPECIFIER(T_signed, SPECIFIER_SIGNED, "signed");
MATCH_SPECIFIER(T_unsigned, SPECIFIER_UNSIGNED, "unsigned");
MATCH_SPECIFIER(T_void, SPECIFIER_VOID, "void");
+ MATCH_SPECIFIER(T_wchar_t, SPECIFIER_WCHAR_T, "wchar_t");
case T__forceinline:
/* only in microsoft mode */
break;
case T_long:
- next_token();
if (type_specifiers & SPECIFIER_LONG_LONG) {
errorf(HERE, "multiple type specifiers given");
} else if (type_specifiers & SPECIFIER_LONG) {
} else {
type_specifiers |= SPECIFIER_LONG;
}
+ next_token();
break;
case T_struct: {
case T_inline:
case T__forceinline: /* ^ DECLARATION_START except for __attribute__ */
case T_IDENTIFIER:
+ case '&':
case '*':
errorf(HERE, "discarding stray %K in declaration specifier", &token);
next_token();
switch (la1_type) {
DECLARATION_START
case T_IDENTIFIER:
+ case '&':
case '*': {
errorf(HERE, "%K does not name a type", &token);
next_token();
saw_error = true;
- if (la1_type == '*')
+ if (la1_type == '&' || la1_type == '*')
goto finish_specifiers;
continue;
}
}
finish_specifiers:
+ specifiers->modifiers
+ |= parse_attributes(&specifiers->gnu_attributes);
+
in_gcc_extension = old_gcc_extension;
if (type == NULL || (saw_error && type_specifiers != 0)) {
case SPECIFIER_VOID:
atomic_type = ATOMIC_TYPE_VOID;
break;
+ case SPECIFIER_WCHAR_T:
+ atomic_type = ATOMIC_TYPE_WCHAR_T;
+ break;
case SPECIFIER_CHAR:
atomic_type = ATOMIC_TYPE_CHAR;
break;
type = allocate_type_zero(TYPE_IMAGINARY);
type->imaginary.akind = atomic_type;
} else {
- type = allocate_type_zero(TYPE_ATOMIC);
- type->atomic.akind = atomic_type;
+ type = allocate_type_zero(TYPE_ATOMIC);
+ type->atomic.akind = atomic_type;
}
+ type->base.alignment = get_atomic_type_alignment(atomic_type);
+ unsigned const size = get_atomic_type_size(atomic_type);
+ type->base.size =
+ type_specifiers & SPECIFIER_COMPLEX ? size * 2 : size;
newtype = true;
} else if (type_specifiers != 0) {
errorf(HERE, "multiple datatypes in declaration");
/* FIXME: check type qualifiers here */
+ if (specifiers->modifiers & DM_TRANSPARENT_UNION)
+ modifiers |= TYPE_MODIFIER_TRANSPARENT_UNION;
type->base.qualifiers = qualifiers;
type->base.modifiers = modifiers;
- type_t *result = typehash_insert(type);
- if (newtype && result != type) {
- free_type(type);
+ if (newtype) {
+ type = identify_new_type(type);
+ } else {
+ type = typehash_insert(type);
}
- specifiers->type = result;
+ type = handle_type_attributes(specifiers->gnu_attributes, type);
+ specifiers->type = type;
return;
end_error:
static void parse_identifier_list(scope_t *scope)
{
do {
- entity_t *entity = allocate_entity_zero(ENTITY_VARIABLE);
+ entity_t *entity = allocate_entity_zero(ENTITY_PARAMETER);
entity->base.source_position = token.source_position;
entity->base.namespc = NAMESPACE_NORMAL;
entity->base.symbol = token.v.symbol;
/* a K&R parameter has no type, yet */
next_token();
- append_entity(scope, entity);
+ if (scope != NULL)
+ append_entity(scope, entity);
if (token.type != ',') {
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 */
- switch (declaration->declared_storage_class) {
- /* Allowed storage classes */
- case STORAGE_CLASS_NONE:
- case STORAGE_CLASS_REGISTER:
- break;
-
- default:
- errorf(pos, "parameter may only have none or register storage class");
- 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;
-
- if (is_type_incomplete(skip_typeref(type))) {
- errorf(pos, "parameter '%#T' is of incomplete type",
- orig_type, declaration->base.symbol);
- }
-}
-
static entity_t *parse_parameter(void)
{
declaration_specifiers_t specifiers;
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;
}
+static void semantic_parameter_incomplete(const entity_t *entity)
+{
+ assert(entity->kind == ENTITY_PARAMETER);
+
+ /* §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 = skip_typeref(entity->declaration.type);
+ if (is_type_incomplete(type)) {
+ errorf(&entity->base.source_position,
+ "parameter '%Y' has incomplete type '%T'", entity->base.symbol,
+ entity->declaration.type);
+ }
+}
+
/**
* Parses function type parameters (and optionally creates variable_t entities
* for them in a scope)
!is_typedef_symbol(token.v.symbol)) {
token_type_t la1_type = (token_type_t)look_ahead(1)->type;
if (la1_type == ',' || la1_type == ')') {
- type->kr_style_parameters = true;
+ type->kr_style_parameters = true;
+ type->unspecified_parameters = true;
parse_identifier_list(scope);
goto parameters_finished;
}
&& skip_typeref(entity->declaration.type) == type_void) {
goto parameters_finished;
}
- semantic_parameter(&entity->declaration);
+ semantic_parameter_incomplete(entity);
parameter = obstack_alloc(type_obst, sizeof(parameter[0]));
memset(parameter, 0, sizeof(parameter[0]));
parameters_finished:
rem_anchor_token(')');
- expect(')');
+ expect(')', end_error);
end_error:
restore_anchor_state(',', saved_comma_state);
typedef enum construct_type_kind_t {
CONSTRUCT_INVALID,
CONSTRUCT_POINTER,
+ CONSTRUCT_REFERENCE,
CONSTRUCT_FUNCTION,
CONSTRUCT_ARRAY
} construct_type_kind_t;
struct parsed_pointer_t {
construct_type_t construct_type;
type_qualifiers_t type_qualifiers;
+ variable_t *base_variable; /**< MS __based extension. */
+};
+
+typedef struct parsed_reference_t parsed_reference_t;
+struct parsed_reference_t {
+ construct_type_t construct_type;
};
typedef struct construct_function_type_t construct_function_type_t;
type_t *type;
};
-static construct_type_t *parse_pointer_declarator(void)
+static construct_type_t *parse_pointer_declarator(variable_t *base_variable)
{
eat('*');
memset(pointer, 0, sizeof(pointer[0]));
pointer->construct_type.kind = CONSTRUCT_POINTER;
pointer->type_qualifiers = parse_type_qualifiers();
+ pointer->base_variable = base_variable;
+
+ return &pointer->construct_type;
+}
+
+static construct_type_t *parse_reference_declarator(void)
+{
+ eat('&');
+
+ parsed_reference_t *reference = obstack_alloc(&temp_obst, sizeof(reference[0]));
+ memset(reference, 0, sizeof(reference[0]));
+ reference->construct_type.kind = CONSTRUCT_REFERENCE;
- return (construct_type_t*) pointer;
+ return (construct_type_t*)reference;
}
static construct_type_t *parse_array_declarator(void)
array->is_variable = true;
next_token();
} else if (token.type != ']') {
- array->size = parse_assignment_expression();
+ expression_t *const size = parse_assignment_expression();
+ array->size = size;
+ mark_vars_read(size, NULL);
}
rem_anchor_token(']');
- expect(']');
+ expect(']', end_error);
end_error:
- return (construct_type_t*) array;
+ return &array->construct_type;
}
-static construct_type_t *parse_function_declarator(scope_t *scope)
+static construct_type_t *parse_function_declarator(scope_t *scope,
+ decl_modifiers_t modifiers)
{
- type_t *type = allocate_type_zero(TYPE_FUNCTION);
+ type_t *type = allocate_type_zero(TYPE_FUNCTION);
+ function_type_t *ftype = &type->function;
- type->function.linkage = current_linkage;
+ ftype->linkage = current_linkage;
- /* TODO: revive this... once we know exactly how to do it */
-#if 0
- decl_modifiers_t modifiers = entity->declaration.modifiers;
-
- unsigned mask = modifiers & (DM_CDECL|DM_STDCALL|DM_FASTCALL|DM_THISCALL);
+ switch (modifiers & (DM_CDECL | DM_STDCALL | DM_FASTCALL | DM_THISCALL)) {
+ case DM_NONE: break;
+ case DM_CDECL: ftype->calling_convention = CC_CDECL; break;
+ case DM_STDCALL: ftype->calling_convention = CC_STDCALL; break;
+ case DM_FASTCALL: ftype->calling_convention = CC_FASTCALL; break;
+ case DM_THISCALL: ftype->calling_convention = CC_THISCALL; break;
- if (mask & (mask-1)) {
- const char *first = NULL, *second = NULL;
-
- /* more than one calling convention set */
- if (modifiers & DM_CDECL) {
- if (first == NULL) first = "cdecl";
- else if (second == NULL) second = "cdecl";
- }
- if (modifiers & DM_STDCALL) {
- if (first == NULL) first = "stdcall";
- else if (second == NULL) second = "stdcall";
- }
- if (modifiers & DM_FASTCALL) {
- if (first == NULL) first = "fastcall";
- else if (second == NULL) second = "fastcall";
- }
- if (modifiers & DM_THISCALL) {
- if (first == NULL) first = "thiscall";
- else if (second == NULL) second = "thiscall";
- }
- errorf(&entity->base.source_position,
- "%s and %s attributes are not compatible", first, second);
+ default:
+ errorf(HERE, "multiple calling conventions in declaration");
+ break;
}
- if (modifiers & DM_CDECL)
- type->function.calling_convention = CC_CDECL;
- else if (modifiers & DM_STDCALL)
- type->function.calling_convention = CC_STDCALL;
- else if (modifiers & DM_FASTCALL)
- type->function.calling_convention = CC_FASTCALL;
- else if (modifiers & DM_THISCALL)
- type->function.calling_convention = CC_THISCALL;
-#endif
-
- parse_parameters(&type->function, scope);
+ parse_parameters(ftype, scope);
construct_function_type_t *construct_function_type =
obstack_alloc(&temp_obst, sizeof(construct_function_type[0]));
decl_modifiers_t modifiers = parse_attributes(&attributes);
- /* pointers */
- while (token.type == '*') {
- construct_type_t *type = parse_pointer_declarator();
+ /* MS __based extension */
+ based_spec_t base_spec;
+ base_spec.base_variable = NULL;
+
+ for (;;) {
+ construct_type_t *type;
+ switch (token.type) {
+ case '&':
+ if (!(c_mode & _CXX))
+ errorf(HERE, "references are only available for C++");
+ 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;
+ break;
+
+ case '*':
+ type = parse_pointer_declarator(base_spec.base_variable);
+ /* consumed */
+ base_spec.base_variable = NULL;
+ break;
+
+ case T__based:
+ next_token();
+ expect('(', end_error);
+ add_anchor_token(')');
+ parse_microsoft_based(&base_spec);
+ rem_anchor_token(')');
+ expect(')', end_error);
+ continue;
+
+ default:
+ goto ptr_operator_end;
+ }
if (last == NULL) {
first = type;
/* TODO: find out if this is correct */
modifiers |= parse_attributes(&attributes);
}
+ptr_operator_end:
+ if (base_spec.base_variable != NULL && warning.other) {
+ warningf(&base_spec.source_position,
+ "__based does not precede a pointer operator, ignored");
+ }
- if (env != NULL)
- env->modifiers |= modifiers;
+ if (env != NULL) {
+ modifiers |= env->modifiers;
+ env->modifiers = modifiers;
+ }
construct_type_t *inner_types = NULL;
next_token();
break;
case '(':
- next_token();
- add_anchor_token(')');
- inner_types = parse_inner_declarator(env, may_be_abstract);
- if (inner_types != NULL) {
- /* All later declarators only modify the return type */
- env = NULL;
+ /* §6.7.6:2 footnote 126: Empty parentheses in a type name are
+ * interpreted as ``function with no parameter specification'', rather
+ * than redundant parentheses around the omitted identifier. */
+ if (look_ahead(1)->type != ')') {
+ next_token();
+ add_anchor_token(')');
+ inner_types = parse_inner_declarator(env, may_be_abstract);
+ if (inner_types != NULL) {
+ /* All later declarators only modify the return type */
+ env = NULL;
+ }
+ rem_anchor_token(')');
+ expect(')', end_error);
}
- rem_anchor_token(')');
- expect(')');
break;
default:
if (may_be_abstract)
construct_type_t *p = last;
- while(true) {
+ while (true) {
construct_type_t *type;
switch (token.type) {
case '(': {
if (env != NULL)
scope = &env->parameters;
- type = parse_function_declarator(scope);
+ type = parse_function_declarator(scope, modifiers);
break;
}
case '[':
if (type == NULL)
return;
- /* handle these strange/stupid mode attributes */
gnu_attribute_t *attribute = attributes;
for ( ; attribute != NULL; attribute = attribute->next) {
- if (attribute->kind != GNU_AK_MODE || attribute->invalid)
+ if (attribute->invalid)
continue;
- atomic_type_kind_t akind = attribute->u.akind;
- if (!is_type_signed(type)) {
- switch (akind) {
- case ATOMIC_TYPE_CHAR: akind = ATOMIC_TYPE_UCHAR; break;
- case ATOMIC_TYPE_SHORT: akind = ATOMIC_TYPE_USHORT; break;
- case ATOMIC_TYPE_INT: akind = ATOMIC_TYPE_UINT; break;
- case ATOMIC_TYPE_LONGLONG: akind = ATOMIC_TYPE_ULONGLONG; break;
- default:
- panic("invalid akind in mode attribute");
- }
- } else {
- switch (akind) {
- case ATOMIC_TYPE_CHAR: akind = ATOMIC_TYPE_SCHAR; break;
- case ATOMIC_TYPE_SHORT: akind = ATOMIC_TYPE_SHORT; break;
- case ATOMIC_TYPE_INT: akind = ATOMIC_TYPE_INT; break;
- case ATOMIC_TYPE_LONGLONG: akind = ATOMIC_TYPE_LONGLONG; break;
- default:
- panic("invalid akind in mode attribute");
+ if (attribute->kind == GNU_AK_MODE) {
+ type = handle_attribute_mode(attribute, type);
+ } else if (attribute->kind == GNU_AK_ALIGNED) {
+ int alignment = 32; /* TODO: fill in maximum usefull alignment for target machine */
+ if (attribute->has_arguments)
+ alignment = attribute->u.argument;
+
+ if (entity->kind == ENTITY_TYPEDEF) {
+ type_t *copy = duplicate_type(type);
+ copy->base.alignment = attribute->u.argument;
+ type = identify_new_type(copy);
+ } else if(entity->kind == ENTITY_VARIABLE) {
+ entity->variable.alignment = alignment;
+ } else if(entity->kind == ENTITY_COMPOUND_MEMBER) {
+ entity->compound_member.alignment = alignment;
}
}
-
- type = make_atomic_type(akind, type->base.qualifiers);
}
type_modifiers_t type_modifiers = type->base.modifiers;
if (modifiers & DM_TRANSPARENT_UNION)
- modifiers |= TYPE_MODIFIER_TRANSPARENT_UNION;
+ type_modifiers |= TYPE_MODIFIER_TRANSPARENT_UNION;
if (type->base.modifiers != type_modifiers) {
- type_t *copy = duplicate_type(type);
+ type_t *copy = duplicate_type(type);
copy->base.modifiers = type_modifiers;
-
- type = typehash_insert(copy);
- if (type != copy) {
- obstack_free(type_obst, copy);
- }
+ type = identify_new_type(copy);
}
if (entity->kind == ENTITY_TYPEDEF) {
}
}
-static type_t *construct_declarator_type(construct_type_t *construct_list,
- type_t *type)
+static type_t *construct_declarator_type(construct_type_t *construct_list, type_t *type)
{
construct_type_t *iter = construct_list;
- for( ; iter != NULL; iter = iter->next) {
+ for (; iter != NULL; iter = iter->next) {
switch (iter->kind) {
case CONSTRUCT_INVALID:
internal_errorf(HERE, "invalid type construction found");
function_type->function.return_type = type;
type_t *skipped_return_type = skip_typeref(type);
- /* §6.7.5.3(1) */
+ /* §6.7.5.3:1 */
if (is_type_function(skipped_return_type)) {
errorf(HERE, "function returning function is not allowed");
} else if (is_type_array(skipped_return_type)) {
}
case CONSTRUCT_POINTER: {
+ if (is_type_reference(skip_typeref(type)))
+ errorf(HERE, "cannot declare a pointer to reference");
+
parsed_pointer_t *parsed_pointer = (parsed_pointer_t*) iter;
- type = make_pointer_type(type, parsed_pointer->type_qualifiers);
+ type = make_based_pointer_type(type, parsed_pointer->type_qualifiers, parsed_pointer->base_variable);
continue;
}
+ case CONSTRUCT_REFERENCE:
+ if (is_type_reference(skip_typeref(type)))
+ errorf(HERE, "cannot declare a reference to reference");
+
+ type = make_reference_type(type);
+ continue;
+
case CONSTRUCT_ARRAY: {
+ if (is_type_reference(skip_typeref(type)))
+ errorf(HERE, "cannot declare an array of references");
+
parsed_array_t *parsed_array = (parsed_array_t*) iter;
type_t *array_type = allocate_type_zero(TYPE_ARRAY);
}
type_t *skipped_type = skip_typeref(type);
- /* §6.7.5.2(1) */
+ /* §6.7.5.2:1 */
if (is_type_incomplete(skipped_type)) {
errorf(HERE, "array of incomplete type '%T' is not allowed", type);
} else if (is_type_function(skipped_type)) {
}
}
- type_t *hashed_type = typehash_insert(type);
- if (hashed_type != type) {
- /* the function type was constructed earlier freeing it here will
- * destroy other types... */
- if (iter->kind != CONSTRUCT_FUNCTION) {
- free_type(type);
- }
- type = hashed_type;
+ /* The function type was constructed earlier. Freeing it here will
+ * destroy other types. */
+ if (iter->kind == CONSTRUCT_FUNCTION) {
+ type = typehash_insert(type);
+ } else {
+ type = identify_new_type(type);
}
}
return type;
}
+static type_t *automatic_type_conversion(type_t *orig_type);
+
+static type_t *semantic_parameter(const source_position_t *pos,
+ type_t *type,
+ const declaration_specifiers_t *specifiers,
+ symbol_t *symbol)
+{
+ /* §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 = automatic_type_conversion(type);
+
+ if (specifiers->is_inline && is_type_valid(type)) {
+ errorf(pos, "parameter '%Y' declared 'inline'", symbol);
+ }
+
+ /* §6.9.1:6 The declarations in the declaration list shall contain
+ * no storage-class specifier other than register and no
+ * initializations. */
+ if (specifiers->thread_local || (
+ specifiers->storage_class != STORAGE_CLASS_NONE &&
+ specifiers->storage_class != STORAGE_CLASS_REGISTER)
+ ) {
+ errorf(pos, "invalid storage class for parameter '%Y'", symbol);
+ }
+
+ /* delay test for incomplete type, because we might have (void)
+ * which is legal but incomplete... */
+
+ return 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)) {
+ assert(anonymous_entity->compound.alias == NULL);
+ assert(anonymous_entity->kind == ENTITY_STRUCT ||
+ anonymous_entity->kind == ENTITY_UNION);
+ anonymous_entity->compound.alias = entity;
+ anonymous_entity = NULL;
+ } else if (is_type_enum(type)) {
+ assert(anonymous_entity->enume.alias == NULL);
+ assert(anonymous_entity->kind == ENTITY_ENUM);
+ anonymous_entity->enume.alias = entity;
+ anonymous_entity = NULL;
+ }
+ }
} else {
- if (create_compound_member) {
+ /* create a declaration type entity */
+ if (flags & DECL_CREATE_COMPOUND_MEMBER) {
entity = allocate_entity_zero(ENTITY_COMPOUND_MEMBER);
- } else if (is_type_function(skip_typeref(type))) {
+
+ if (env.symbol != NULL) {
+ 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) {
+ orig_type = semantic_parameter(&env.source_position, orig_type,
+ specifiers, env.symbol);
+
+ entity = allocate_entity_zero(ENTITY_PARAMETER);
+ } 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 (env.symbol != NULL) {
+ 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 {
entity = allocate_entity_zero(ENTITY_VARIABLE);
entity->variable.get_property_sym = specifiers->get_property_sym;
entity->variable.put_property_sym = specifiers->put_property_sym;
- if (specifiers->alignment != 0) {
- /* TODO: add checks here */
- 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);
+ entity->variable.thread_local = specifiers->thread_local;
+
+ if (env.symbol != NULL) {
+ if (specifiers->is_inline && is_type_valid(type)) {
+ errorf(&env.source_position,
+ "variable '%Y' declared 'inline'", env.symbol);
+ }
+
+ 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.modifiers = env.modifiers | specifiers->modifiers;
+ if (env.symbol != NULL) {
+ entity->base.symbol = env.symbol;
+ entity->base.source_position = env.source_position;
+ } else {
+ entity->base.source_position = specifiers->source_position;
+ }
+ entity->base.namespc = NAMESPACE_NORMAL;
+ entity->declaration.type = orig_type;
+ entity->declaration.modifiers = env.modifiers;
entity->declaration.deprecated_string = specifiers->deprecated_string;
storage_class_t storage_class = specifiers->storage_class;
entity->declaration.declared_storage_class = storage_class;
- if (storage_class == STORAGE_CLASS_NONE
- && current_scope != file_scope) {
+ if (storage_class == STORAGE_CLASS_NONE && current_scope != file_scope)
storage_class = STORAGE_CLASS_AUTO;
- }
entity->declaration.storage_class = storage_class;
}
return strcmp(sym->string, "main") == 0;
}
-static const char *get_entity_kind_name(entity_kind_t kind)
-{
- switch ((entity_kind_tag_t) kind) {
- case ENTITY_FUNCTION: return "function";
- case ENTITY_VARIABLE: return "variable";
- case ENTITY_COMPOUND_MEMBER: return "compound type member";
- case ENTITY_STRUCT: return "struct";
- case ENTITY_UNION: return "union";
- case ENTITY_ENUM: return "enum";
- case ENTITY_ENUM_VALUE: return "enum value";
- case ENTITY_LABEL: return "label";
- case ENTITY_LOCAL_LABEL: return "local label";
- case ENTITY_TYPEDEF: return "typedef";
- case ENTITY_NAMESPACE: return "namespace";
- case ENTITY_INVALID: break;
- }
-
- panic("Invalid entity kind encountered in get_entity_kind_name");
-}
-
static void error_redefined_as_different_kind(const source_position_t *pos,
const entity_t *old, entity_kind_t new_kind)
{
const namespace_tag_t namespc = (namespace_tag_t)entity->base.namespc;
const source_position_t *pos = &entity->base.source_position;
- assert(symbol != NULL);
+ /* can happen in error cases */
+ if (symbol == NULL)
+ return entity;
+
entity_t *previous_entity = get_entity(symbol, namespc);
/* pushing the same entity twice will break the stack structure */
assert(previous_entity != entity);
}
}
- if (is_declaration(entity)) {
- if (warning.nested_externs
- && entity->declaration.storage_class == STORAGE_CLASS_EXTERN
- && current_scope != file_scope) {
- warningf(pos, "nested extern declaration of '%#T'",
- entity->declaration.type, symbol);
- }
+ if (is_declaration(entity) &&
+ warning.nested_externs &&
+ entity->declaration.storage_class == STORAGE_CLASS_EXTERN &&
+ current_scope != file_scope) {
+ warningf(pos, "nested extern declaration of '%#T'",
+ entity->declaration.type, symbol);
}
- if (previous_entity != NULL
- && previous_entity->base.parent_scope == ¤t_function->parameters
- && current_scope->depth == previous_entity->base.parent_scope->depth+1){
-
- assert(previous_entity->kind == ENTITY_VARIABLE);
+ if (previous_entity != NULL &&
+ previous_entity->base.parent_scope == ¤t_function->parameters &&
+ previous_entity->base.parent_scope->depth + 1 == current_scope->depth) {
+ assert(previous_entity->kind == ENTITY_PARAMETER);
errorf(pos,
"declaration '%#T' redeclares the parameter '%#T' (declared %P)",
- entity->declaration.type, symbol,
- previous_entity->declaration.type, symbol,
- &previous_entity->base.source_position);
+ entity->declaration.type, symbol,
+ previous_entity->declaration.type, symbol,
+ &previous_entity->base.source_position);
goto finish;
}
- if (previous_entity != NULL
- && previous_entity->base.parent_scope == current_scope) {
-
+ if (previous_entity != NULL &&
+ previous_entity->base.parent_scope == current_scope) {
if (previous_entity->kind != entity->kind) {
error_redefined_as_different_kind(pos, previous_entity,
entity->kind);
goto finish;
}
if (previous_entity->kind == ENTITY_ENUM_VALUE) {
- errorf(pos,
- "redeclaration of enum entry '%Y' (declared %P)",
+ errorf(pos, "redeclaration of enum entry '%Y' (declared %P)",
symbol, &previous_entity->base.source_position);
goto finish;
}
if (previous_entity->kind == ENTITY_TYPEDEF) {
/* TODO: C++ allows this for exactly the same type */
- errorf(pos,
- "redefinition of typedef '%Y' (declared %P)",
+ errorf(pos, "redefinition of typedef '%Y' (declared %P)",
symbol, &previous_entity->base.source_position);
goto finish;
}
/* at this point we should have only VARIABLES or FUNCTIONS */
assert(is_declaration(previous_entity) && is_declaration(entity));
+ declaration_t *const prev_decl = &previous_entity->declaration;
+ declaration_t *const decl = &entity->declaration;
+
/* can happen for K&R style declarations */
- if (previous_entity->kind == ENTITY_VARIABLE
- && previous_entity->declaration.type == NULL
- && entity->kind == ENTITY_VARIABLE) {
- previous_entity->declaration.type = entity->declaration.type;
- previous_entity->declaration.storage_class
- = entity->declaration.storage_class;
- previous_entity->declaration.declared_storage_class
- = entity->declaration.declared_storage_class;
- previous_entity->declaration.modifiers
- = entity->declaration.modifiers;
- previous_entity->declaration.deprecated_string
- = entity->declaration.deprecated_string;
- }
- assert(entity->declaration.type != NULL);
-
- declaration_t *const previous_declaration
- = &previous_entity->declaration;
- declaration_t *const declaration = &entity->declaration;
- type_t *const orig_type = entity->declaration.type;
+ if (prev_decl->type == NULL &&
+ previous_entity->kind == ENTITY_PARAMETER &&
+ entity->kind == ENTITY_PARAMETER) {
+ prev_decl->type = decl->type;
+ prev_decl->storage_class = decl->storage_class;
+ prev_decl->declared_storage_class = decl->declared_storage_class;
+ prev_decl->modifiers = decl->modifiers;
+ prev_decl->deprecated_string = decl->deprecated_string;
+ return previous_entity;
+ }
+
+ type_t *const orig_type = decl->type;
+ assert(orig_type != NULL);
type_t *const type = skip_typeref(orig_type);
-
- type_t *prev_type = skip_typeref(previous_declaration->type);
+ type_t *const prev_type = skip_typeref(prev_decl->type);
if (!types_compatible(type, prev_type)) {
errorf(pos,
"declaration '%#T' is incompatible with '%#T' (declared %P)",
- orig_type, symbol, previous_declaration->type, symbol,
+ orig_type, symbol, prev_decl->type, symbol,
&previous_entity->base.source_position);
} else {
- unsigned old_storage_class = previous_declaration->storage_class;
- if (warning.redundant_decls && is_definition
- && previous_declaration->storage_class == STORAGE_CLASS_STATIC
- && !(previous_declaration->modifiers & DM_USED)
- && !previous_declaration->used) {
+ unsigned old_storage_class = prev_decl->storage_class;
+ if (warning.redundant_decls &&
+ is_definition &&
+ !prev_decl->used &&
+ !(prev_decl->modifiers & DM_USED) &&
+ prev_decl->storage_class == STORAGE_CLASS_STATIC) {
warningf(&previous_entity->base.source_position,
"unnecessary static forward declaration for '%#T'",
- previous_declaration->type, symbol);
+ prev_decl->type, symbol);
}
- unsigned new_storage_class = declaration->storage_class;
- if (is_type_incomplete(prev_type)) {
- previous_declaration->type = type;
- prev_type = type;
- }
+ storage_class_tag_t new_storage_class = decl->storage_class;
/* pretend no storage class means extern for function
* declarations (except if the previous declaration is neither
* none nor extern) */
if (entity->kind == ENTITY_FUNCTION) {
- if (prev_type->function.unspecified_parameters) {
- previous_declaration->type = type;
- prev_type = type;
- }
+ if (prev_type->function.unspecified_parameters)
+ prev_decl->type = type;
switch (old_storage_class) {
case STORAGE_CLASS_NONE:
default:
break;
}
+ } else if (is_type_incomplete(prev_type)) {
+ prev_decl->type = type;
}
if (old_storage_class == STORAGE_CLASS_EXTERN &&
if (!is_definition &&
warning.redundant_decls &&
is_type_valid(prev_type) &&
- strcmp(previous_entity->base.source_position.input_name, "<builtin>") != 0) {
+ strcmp(previous_entity->base.source_position.input_name,
+ "<builtin>") != 0) {
warningf(pos,
"redundant declaration for '%Y' (declared %P)",
symbol, &previous_entity->base.source_position);
"static declaration of '%Y' follows non-static declaration (declared %P)",
symbol, &previous_entity->base.source_position);
} else if (old_storage_class == STORAGE_CLASS_EXTERN) {
- previous_declaration->storage_class = STORAGE_CLASS_NONE;
- previous_declaration->declared_storage_class = STORAGE_CLASS_NONE;
+ prev_decl->storage_class = STORAGE_CLASS_NONE;
+ prev_decl->declared_storage_class = STORAGE_CLASS_NONE;
} else {
/* ISO/IEC 14882:1998(E) §C.1.2:1 */
if (c_mode & _CXX)
}
}
- previous_declaration->modifiers |= declaration->modifiers;
+ prev_decl->modifiers |= decl->modifiers;
if (entity->kind == ENTITY_FUNCTION) {
previous_entity->function.is_inline |= entity->function.is_inline;
}
entity->declaration.type, symbol);
}
}
- } else if (warning.missing_declarations
- && entity->kind == ENTITY_VARIABLE
- && current_scope == file_scope) {
+ } else if (warning.missing_declarations &&
+ 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);
}
static void parser_error_multiple_definition(entity_t *entity,
const source_position_t *source_position)
{
- errorf(source_position, "multiple definition of symbol '%Y' (declared %P)",
+ errorf(source_position, "multiple definition of '%Y' (declared %P)",
entity->base.symbol, &entity->base.source_position);
}
}
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;
}
current_init_decl = NULL;
if (entity->kind == ENTITY_VARIABLE) {
- /* § 6.7.5 (22) array initializers for arrays with unknown size
+ /* § 6.7.5:22 array initializers for arrays with unknown size
* determine the array type size */
declaration->type = env.type;
entity->variable.initializer = initializer;
const declaration_specifiers_t *specifiers)
{
eat(';');
+ 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");
}
}
}
+static void check_variable_type_complete(entity_t *ent)
+{
+ 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_NONE)
+ return;
+
+ type_t *const orig_type = decl->type;
+ type_t *const type = skip_typeref(orig_type);
+ if (!is_type_incomplete(type))
+ return;
+
+ /* §6.9.2:2 and §6.9.2:5: At the end of the translation incomplete arrays
+ * are given length one. */
+ if (is_type_array(type) && ent->base.parent_scope == file_scope) {
+ ARR_APP1(declaration_t*, incomplete_arrays, decl);
+ return;
+ }
+
+ errorf(&ent->base.source_position, "variable '%#T' has incomplete type",
+ orig_type, ent->base.symbol);
+}
+
+
static void parse_declaration_rest(entity_t *ndeclaration,
const declaration_specifiers_t *specifiers,
- parsed_declaration_func finished_declaration)
+ parsed_declaration_func finished_declaration,
+ declarator_flags_t flags)
{
add_anchor_token(';');
add_anchor_token(',');
- while(true) {
+ while (true) {
entity_t *entity = finished_declaration(ndeclaration, token.type == '=');
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);
+
if (token.type != ',')
break;
eat(',');
add_anchor_token('=');
- ndeclaration = parse_declarator(specifiers, /*may_be_abstract=*/false, false);
+ ndeclaration = parse_declarator(specifiers, flags);
rem_anchor_token('=');
}
- expect(';');
+ expect(';', end_error);
end_error:
+ anonymous_entity = NULL;
rem_anchor_token(';');
rem_anchor_token(',');
}
}
if (is_definition) {
- errorf(HERE, "parameter %Y is initialised", entity->base.symbol);
+ errorf(HERE, "parameter '%Y' is initialised", entity->base.symbol);
}
return record_entity(entity, false);
}
-static void parse_declaration(parsed_declaration_func finished_declaration)
+static void parse_declaration(parsed_declaration_func finished_declaration,
+ declarator_flags_t flags)
{
declaration_specifiers_t specifiers;
memset(&specifiers, 0, sizeof(specifiers));
if (token.type == ';') {
parse_anonymous_declaration_rest(&specifiers);
} else {
- entity_t *entity = parse_declarator(&specifiers, /*may_be_abstract=*/false, false);
- parse_declaration_rest(entity, &specifiers, finished_declaration);
+ entity_t *entity = parse_declarator(&specifiers, flags);
+ parse_declaration_rest(entity, &specifiers, finished_declaration, flags);
}
}
add_anchor_token('{');
/* push function parameters */
- size_t const top = environment_top();
- scope_push(&entity->function.parameters);
+ size_t const top = environment_top();
+ scope_t *old_scope = scope_push(&entity->function.parameters);
entity_t *parameter = entity->function.parameters.entities;
for ( ; parameter != NULL; parameter = parameter->base.next) {
}
/* parse declaration list */
- while (is_declaration_specifier(&token, false)) {
- parse_declaration(finished_kr_declaration);
+ for (;;) {
+ switch (token.type) {
+ DECLARATION_START
+ case T___extension__:
+ /* This covers symbols, which are no type, too, and results in
+ * better error messages. The typical cases are misspelled type
+ * names and missing includes. */
+ case T_IDENTIFIER:
+ parse_declaration(finished_kr_declaration, DECL_IS_PARAMETER);
+ break;
+ default:
+ goto decl_list_end;
+ }
}
+decl_list_end:
/* pop function parameters */
assert(current_scope == &entity->function.parameters);
- scope_pop();
+ scope_pop(old_scope);
environment_pop_to(top);
/* update function type */
function_parameter_t *parameters = NULL;
function_parameter_t *last_parameter = NULL;
- entity_t *parameter_declaration = entity->function.parameters.entities;
- for( ; parameter_declaration != NULL;
- parameter_declaration = parameter_declaration->base.next) {
- type_t *parameter_type = parameter_declaration->declaration.type;
+ parameter = entity->function.parameters.entities;
+ for (; parameter != NULL; parameter = parameter->base.next) {
+ type_t *parameter_type = parameter->declaration.type;
if (parameter_type == NULL) {
if (strict_mode) {
errorf(HERE, "no type specified for function parameter '%Y'",
- parameter_declaration->base.symbol);
+ parameter->base.symbol);
} else {
if (warning.implicit_int) {
warningf(HERE, "no type specified for function parameter '%Y', using 'int'",
- parameter_declaration->base.symbol);
+ parameter->base.symbol);
}
- parameter_type = type_int;
- parameter_declaration->declaration.type = parameter_type;
+ parameter_type = type_int;
+ parameter->declaration.type = parameter_type;
}
}
- semantic_parameter(¶meter_declaration->declaration);
- parameter_type = parameter_declaration->declaration.type;
+ semantic_parameter_incomplete(parameter);
+ parameter_type = parameter->declaration.type;
/*
* we need the default promoted types for the function type
new_type->function.parameters = parameters;
new_type->function.unspecified_parameters = true;
- type = typehash_insert(new_type);
- if (type != new_type) {
- obstack_free(type_obst, new_type);
- }
+ new_type = identify_new_type(new_type);
- entity->declaration.type = type;
+ entity->declaration.type = new_type;
rem_anchor_token('{');
}
"label '%Y' used but not defined", label->base.symbol);
}
}
- goto_first = NULL;
- goto_last = NULL;
if (warning.unused_label) {
for (const label_statement_t *label_statement = label_first;
}
}
}
- label_first = label_last = NULL;
}
-static void warn_unused_decl(entity_t *entity, entity_t *end,
- char const *const what)
+static void warn_unused_entity(entity_t *entity, entity_t *last)
{
- for (; entity != NULL; entity = entity->base.next) {
+ entity_t const *const end = last != NULL ? last->base.next : NULL;
+ for (; entity != end; entity = entity->base.next) {
if (!is_declaration(entity))
continue;
if (!declaration->used) {
print_in_function();
+ const char *what = get_entity_kind_name(entity->kind);
warningf(&entity->base.source_position, "%s '%Y' is unused",
what, entity->base.symbol);
} else if (entity->kind == ENTITY_VARIABLE && !entity->variable.read) {
print_in_function();
+ const char *what = get_entity_kind_name(entity->kind);
warningf(&entity->base.source_position, "%s '%Y' is never read",
what, entity->base.symbol);
}
-
- if (entity == end)
- break;
}
}
switch (stmt->kind) {
case STATEMENT_DECLARATION: {
declaration_statement_t const *const decls = &stmt->declaration;
- warn_unused_decl(decls->declarations_begin, decls->declarations_end,
- "variable");
+ warn_unused_entity(decls->declarations_begin,
+ decls->declarations_end);
return;
}
case STATEMENT_FOR:
- warn_unused_decl(stmt->fors.scope.entities, NULL, "variable");
+ warn_unused_entity(stmt->fors.scope.entities, NULL);
return;
default:
/* do not issue unused warnings for main */
if (!is_sym_main(current_function->base.base.symbol)) {
- warn_unused_decl(scope->entities, NULL, "parameter");
+ warn_unused_entity(scope->entities, NULL);
}
}
if (warning.unused_variable) {
-1;
}
+static void check_reachable(statement_t *);
+static bool reaches_end;
+
static bool expression_returns(expression_t const *const expr)
{
switch (expr->kind) {
case EXPR_BUILTIN_PREFETCH:
case EXPR_OFFSETOF:
case EXPR_INVALID:
- case EXPR_STATEMENT: // TODO implement
return true;
+ case EXPR_STATEMENT: {
+ bool old_reaches_end = reaches_end;
+ reaches_end = false;
+ check_reachable(expr->statement.statement);
+ bool returns = reaches_end;
+ reaches_end = old_reaches_end;
+ return returns;
+ }
+
case EXPR_CONDITIONAL:
// TODO handle constant expression
- return
- expression_returns(expr->conditional.condition) && (
- expression_returns(expr->conditional.true_expression) ||
- expression_returns(expr->conditional.false_expression)
- );
+
+ if (!expression_returns(expr->conditional.condition))
+ return false;
+
+ if (expr->conditional.true_expression != NULL
+ && expression_returns(expr->conditional.true_expression))
+ return true;
+
+ return expression_returns(expr->conditional.false_expression);
case EXPR_SELECT:
return expression_returns(expr->select.compound);
panic("unhandled expression");
}
+static bool initializer_returns(initializer_t const *const init)
+{
+ switch (init->kind) {
+ case INITIALIZER_VALUE:
+ return expression_returns(init->value.value);
+
+ case INITIALIZER_LIST: {
+ initializer_t * const* i = init->list.initializers;
+ initializer_t * const* const end = i + init->list.len;
+ bool returns = true;
+ for (; i != end; ++i) {
+ if (!initializer_returns(*i))
+ returns = false;
+ }
+ return returns;
+ }
+
+ case INITIALIZER_STRING:
+ case INITIALIZER_WIDE_STRING:
+ case INITIALIZER_DESIGNATOR: // designators have no payload
+ return true;
+ }
+ panic("unhandled initializer");
+}
+
static bool noreturn_candidate;
static void check_reachable(statement_t *const stmt)
switch (stmt->kind) {
case STATEMENT_INVALID:
case STATEMENT_EMPTY:
- case STATEMENT_DECLARATION:
- case STATEMENT_LOCAL_LABEL:
case STATEMENT_ASM:
next = stmt->base.next;
break;
+ case STATEMENT_DECLARATION: {
+ declaration_statement_t const *const decl = &stmt->declaration;
+ entity_t const * ent = decl->declarations_begin;
+ entity_t const *const last = decl->declarations_end;
+ if (ent != NULL) {
+ for (;; ent = ent->base.next) {
+ if (ent->kind == ENTITY_VARIABLE &&
+ ent->variable.initializer != NULL &&
+ !initializer_returns(ent->variable.initializer)) {
+ return;
+ }
+ if (ent == last)
+ break;
+ }
+ }
+ next = stmt->base.next;
+ break;
+ }
+
case STATEMENT_COMPOUND:
next = stmt->compound.statements;
+ if (next == NULL)
+ next = stmt->base.next;
break;
- case STATEMENT_RETURN:
- noreturn_candidate = false;
+ case STATEMENT_RETURN: {
+ expression_t const *const val = stmt->returns.value;
+ if (val == NULL || expression_returns(val))
+ noreturn_candidate = false;
return;
+ }
case STATEMENT_IF: {
- if_statement_t const* const ifs = &stmt->ifs;
- int const val = determine_truth(ifs->condition);
+ if_statement_t const *const ifs = &stmt->ifs;
+ expression_t const *const cond = ifs->condition;
+
+ if (!expression_returns(cond))
+ return;
+
+ int const val = determine_truth(cond);
if (val >= 0)
check_reachable(ifs->true_statement);
switch_statement_t const *const switchs = &stmt->switchs;
expression_t const *const expr = switchs->expression;
+ if (!expression_returns(expr))
+ return;
+
if (is_constant_expression(expr)) {
long const val = fold_constant(expr);
case_label_statement_t * defaults = NULL;
case STATEMENT_GOTO:
if (stmt->gotos.expression) {
+ if (!expression_returns(stmt->gotos.expression))
+ return;
+
statement_t *parent = stmt->base.parent;
if (parent == NULL) /* top level goto */
return;
case STATEMENT_WHILE: {
while_statement_t const *const whiles = &stmt->whiles;
- int const val = determine_truth(whiles->condition);
+ expression_t const *const cond = whiles->condition;
+
+ if (!expression_returns(cond))
+ return;
+
+ int const val = determine_truth(cond);
if (val >= 0)
check_reachable(whiles->body);
fors->condition_reachable = true;
expression_t const *const cond = fors->condition;
- int const val =
- cond == NULL ? 1 : determine_truth(cond);
+
+ int val;
+ if (cond == NULL) {
+ val = 1;
+ } else if (expression_returns(cond)) {
+ val = determine_truth(cond);
+ } else {
+ return;
+ }
if (val >= 0)
check_reachable(fors->body);
}
break;
}
+
+ default:
+ panic("invalid statement kind");
}
while (next == NULL) {
if (next == NULL) {
noreturn_candidate = false;
- type_t *const type = current_function->base.type;
+ type_t *const type = skip_typeref(current_function->base.type);
assert(is_type_function(type));
type_t *const ret = skip_typeref(type->function.return_type);
if (warning.return_type &&
case STATEMENT_INVALID:
case STATEMENT_EMPTY:
case STATEMENT_DECLARATION:
- case STATEMENT_LOCAL_LABEL:
case STATEMENT_EXPRESSION:
case STATEMENT_ASM:
case STATEMENT_RETURN:
panic("invalid control flow in function");
case STATEMENT_COMPOUND:
+ if (next->compound.stmt_expr) {
+ reaches_end = true;
+ return;
+ }
+ /* FALLTHROUGH */
case STATEMENT_IF:
case STATEMENT_SWITCH:
case STATEMENT_LABEL:
next->base.reachable = true;
while_statement_t const *const whiles = &next->whiles;
- int const val = determine_truth(whiles->condition);
+ expression_t const *const cond = whiles->condition;
+
+ if (!expression_returns(cond))
+ return;
+
+ int const val = determine_truth(cond);
if (val >= 0)
check_reachable(whiles->body);
return;
next->base.reachable = true;
- do_while_statement_t const *const dw = &next->do_while;
- int const val = determine_truth(dw->condition);
+ do_while_statement_t const *const dw = &next->do_while;
+ expression_t const *const cond = dw->condition;
+
+ if (!expression_returns(cond))
+ return;
+
+ int const val = determine_truth(cond);
if (val >= 0)
check_reachable(dw->body);
fors->condition_reachable = true;
expression_t const *const cond = fors->condition;
- int const val =
- cond == NULL ? 1 : determine_truth(cond);
+
+ int val;
+ if (cond == NULL) {
+ val = 1;
+ } else if (expression_returns(cond)) {
+ val = determine_truth(cond);
+ } else {
+ return;
+ }
if (val >= 0)
check_reachable(fors->body);
case STATEMENT_COMPOUND:
if (stmt->compound.statements != NULL)
return;
- /* FALLTHROUGH*/
+ goto warn_unreachable;
+
+ case STATEMENT_DECLARATION: {
+ /* Only warn if there is at least one declarator with an initializer.
+ * This typically occurs in switch statements. */
+ declaration_statement_t const *const decl = &stmt->declaration;
+ entity_t const * ent = decl->declarations_begin;
+ entity_t const *const last = decl->declarations_end;
+ if (ent != NULL) {
+ for (;; ent = ent->base.next) {
+ if (ent->kind == ENTITY_VARIABLE &&
+ ent->variable.initializer != NULL) {
+ goto warn_unreachable;
+ }
+ if (ent == last)
+ return;
+ }
+ }
+ }
default:
+warn_unreachable:
if (!stmt->base.reachable)
warningf(&stmt->base.source_position, "statement is unreachable");
return;
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(';');
case ',':
case ';':
case '=':
- parse_declaration_rest(ndeclaration, &specifiers, record_entity);
+ parse_declaration_rest(ndeclaration, &specifiers, record_entity,
+ DECL_FLAGS_NONE);
return;
}
}
assert(is_declaration(ndeclaration));
- type_t *type = skip_typeref(ndeclaration->declaration.type);
+ type_t *const orig_type = ndeclaration->declaration.type;
+ type_t * type = skip_typeref(orig_type);
if (!is_type_function(type)) {
if (is_type_valid(type)) {
}
eat_block();
return;
+ } else if (is_typeref(orig_type)) {
+ /* §6.9.1:2 */
+ errorf(&ndeclaration->base.source_position,
+ "type of function definition '%#T' is a typedef",
+ orig_type, ndeclaration->base.symbol);
}
if (warning.aggregate_return &&
ndeclaration->base.symbol);
}
- /* § 6.7.5.3 (14) a function definition with () means no
+ /* § 6.7.5.3:14 a function definition with () means no
* parameters (and not unspecified parameters) */
- if (type->function.unspecified_parameters
- && type->function.parameters == NULL
- && !type->function.kr_style_parameters) {
- type_t *duplicate = duplicate_type(type);
- duplicate->function.unspecified_parameters = false;
+ if (type->function.unspecified_parameters &&
+ type->function.parameters == NULL &&
+ !type->function.kr_style_parameters) {
+ type_t *copy = duplicate_type(type);
+ copy->function.unspecified_parameters = false;
+ type = identify_new_type(copy);
- type = typehash_insert(duplicate);
- if (type != duplicate) {
- obstack_free(type_obst, duplicate);
- }
ndeclaration->declaration.type = type;
}
type = skip_typeref(entity->declaration.type);
/* push function parameters and switch scope */
- size_t const top = environment_top();
- scope_push(&function->parameters);
+ size_t const top = environment_top();
+ scope_t *old_scope = scope_push(&function->parameters);
entity_t *parameter = function->parameters.entities;
- for( ; parameter != NULL; parameter = parameter->base.next) {
+ for (; parameter != NULL; parameter = parameter->base.next) {
if (parameter->base.parent_scope == &ndeclaration->function.parameters) {
parameter->base.parent_scope = current_scope;
}
current_function = function;
current_parent = NULL;
- statement_t *const body = parse_compound_statement(false);
+ goto_first = NULL;
+ goto_anchor = &goto_first;
+ label_first = NULL;
+ label_anchor = &label_first;
+
+ statement_t *const body = parse_compound_statement(false);
function->statement = body;
first_err = true;
check_labels();
}
assert(current_scope == &function->parameters);
- scope_pop();
+ scope_pop(old_scope);
environment_pop_to(top);
}
long v = fold_constant(size);
if (v < 0) {
- errorf(source_position, "negative width in bit-field '%Y'",
- symbol);
+ errorf(source_position, "negative width in bit-field '%Y'", symbol);
} else if (v == 0) {
- errorf(source_position, "zero width for bit-field '%Y'",
- symbol);
+ errorf(source_position, "zero width for bit-field '%Y'", symbol);
} else if (bit_size > 0 && (il_size_t)v > bit_size) {
- errorf(source_position, "width of '%Y' exceeds its type",
- symbol);
+ errorf(source_position, "width of '%Y' exceeds its type", symbol);
} else {
type->bitfield.bit_size = v;
}
static entity_t *find_compound_entry(compound_t *compound, symbol_t *symbol)
{
entity_t *iter = compound->members.entities;
- for( ; iter != NULL; iter = iter->base.next) {
+ for (; iter != NULL; iter = iter->base.next) {
if (iter->kind != ENTITY_COMPOUND_MEMBER)
continue;
- if (iter->base.symbol == NULL) {
+ if (iter->base.symbol == symbol) {
+ return iter;
+ } else if (iter->base.symbol == NULL) {
type_t *type = skip_typeref(iter->declaration.type);
if (is_type_compound(type)) {
entity_t *result
}
continue;
}
-
- if (iter->base.symbol == symbol) {
- return iter;
- }
}
return NULL;
entity->declaration.storage_class = STORAGE_CLASS_NONE;
entity->declaration.modifiers = specifiers->modifiers;
entity->declaration.type = type;
+ append_entity(&compound->members, entity);
} else {
- entity = parse_declarator(specifiers,/*may_be_abstract=*/true, true);
- assert(entity->kind == ENTITY_COMPOUND_MEMBER);
-
- if (token.type == ':') {
- source_position_t source_position = *HERE;
- next_token();
- expression_t *size = parse_constant_expression();
+ entity = parse_declarator(specifiers,
+ DECL_MAY_BE_ABSTRACT | DECL_CREATE_COMPOUND_MEMBER);
+ if (entity->kind == ENTITY_TYPEDEF) {
+ errorf(&entity->base.source_position,
+ "typedef not allowed as compound member");
+ } else {
+ assert(entity->kind == ENTITY_COMPOUND_MEMBER);
+
+ /* make sure we don't define a symbol multiple times */
+ symbol_t *symbol = entity->base.symbol;
+ if (symbol != NULL) {
+ entity_t *prev = find_compound_entry(compound, symbol);
+ if (prev != NULL) {
+ errorf(&entity->base.source_position,
+ "multiple declarations of symbol '%Y' (declared %P)",
+ symbol, &prev->base.source_position);
+ }
+ }
- type_t *type = entity->declaration.type;
- type_t *bitfield_type = make_bitfield_type(type, size,
- &source_position, entity->base.symbol);
- entity->declaration.type = bitfield_type;
- }
- }
+ if (token.type == ':') {
+ source_position_t source_position = *HERE;
+ next_token();
+ expression_t *size = parse_constant_expression();
- /* make sure we don't define a symbol multiple times */
- symbol_t *symbol = entity->base.symbol;
- if (symbol != NULL) {
- entity_t *prev = find_compound_entry(compound, symbol);
+ type_t *type = entity->declaration.type;
+ type_t *bitfield_type = make_bitfield_type(type, size,
+ &source_position, entity->base.symbol);
+ entity->declaration.type = bitfield_type;
+ } else {
+ type_t *orig_type = entity->declaration.type;
+ type_t *type = skip_typeref(orig_type);
+ if (is_type_function(type)) {
+ errorf(&entity->base.source_position,
+ "compound member '%Y' must not have function type '%T'",
+ entity->base.symbol, orig_type);
+ } else if (is_type_incomplete(type)) {
+ /* §6.7.2.1:16 flexible array member */
+ if (is_type_array(type) &&
+ token.type == ';' &&
+ look_ahead(1)->type == '}') {
+ compound->has_flexible_member = true;
+ } else {
+ errorf(&entity->base.source_position,
+ "compound member '%Y' has incomplete type '%T'",
+ entity->base.symbol, orig_type);
+ }
+ }
+ }
- if (prev != NULL) {
- assert(prev->base.symbol == symbol);
- errorf(&entity->base.source_position,
- "multiple declarations of symbol '%Y' (declared %P)",
- symbol, &prev->base.source_position);
+ append_entity(&compound->members, entity);
}
}
- append_entity(&compound->members, entity);
-
if (token.type != ',')
break;
- next_token();
- }
- expect(';');
-
-end_error:
- ;
-}
-
-static void semantic_compound(compound_t *compound)
-{
- entity_t *entity = compound->members.entities;
- for ( ; entity != NULL; entity = entity->base.next) {
- assert(entity->kind == ENTITY_COMPOUND_MEMBER);
-
- type_t *orig_type = entity->declaration.type;
- type_t *type = skip_typeref(orig_type);
-
- if (is_type_function(type)) {
- errorf(HERE,
- "compound member '%Y' must not have function type '%T'",
- entity->base.symbol, orig_type);
- } else if (is_type_incomplete(type)) {
- /* §6.7.2.1 (16) flexible array member */
- if (is_type_array(type) && entity->base.next == NULL) {
- compound->has_flexible_member = true;
- } else {
- errorf(HERE,
- "compound member '%Y' has incomplete type '%T'",
- entity->base.symbol, orig_type);
- }
- }
+ next_token();
}
+ expect(';', end_error);
+
+end_error:
+ anonymous_entity = NULL;
}
static void parse_compound_type_entries(compound_t *compound)
parse_compound_declarators(compound, &specifiers);
}
- semantic_compound(compound);
rem_anchor_token('}');
next_token();
+
+ /* §6.7.2.1:7 */
+ compound->complete = true;
}
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...
*/
typedef struct expression_parser_function_t expression_parser_function_t;
struct expression_parser_function_t {
parse_expression_function parser;
- unsigned infix_precedence;
+ precedence_t infix_precedence;
parse_expression_infix_function infix_parser;
};
{
/* skip the error message if the error token was read */
if (token.type != T_ERROR) {
- errorf(HERE, "expected expression, got token '%K'", &token);
+ errorf(HERE, "expected expression, got token %K", &token);
}
next_token();
cnst->conste.v.character = token.v.string;
if (cnst->conste.v.character.size != 1) {
- if (warning.multichar && GNU_MODE) {
+ if (!GNU_MODE) {
+ errorf(HERE, "more than 1 character in character constant");
+ } else if (warning.multichar) {
warningf(HERE, "multi-character character constant");
- } else {
- errorf(HERE, "more than 1 characters in character constant");
}
}
next_token();
cnst->conste.v.wide_character = token.v.wide_string;
if (cnst->conste.v.wide_character.size != 1) {
- if (warning.multichar && GNU_MODE) {
+ if (!GNU_MODE) {
+ errorf(HERE, "more than 1 character in character constant");
+ } else if (warning.multichar) {
warningf(HERE, "multi-character character constant");
- } else {
- errorf(HERE, "more than 1 characters in character constant");
}
}
next_token();
type_t *ntype = allocate_type_zero(TYPE_FUNCTION);
ntype->function.return_type = type_int;
ntype->function.unspecified_parameters = true;
-
- type_t *type = typehash_insert(ntype);
- if (type != ntype) {
- free_type(ntype);
- }
+ ntype->function.linkage = LINKAGE_C;
+ type_t *type = identify_new_type(ntype);
entity_t *entity = allocate_entity_zero(ENTITY_FUNCTION);
entity->declaration.storage_class = STORAGE_CLASS_EXTERN;
type->function.return_type = return_type;
type->function.parameters = parameter1;
- type_t *result = typehash_insert(type);
- if (result != type) {
- free_type(type);
- }
-
- return result;
+ return identify_new_type(type);
}
/**
type->function.return_type = return_type;
type->function.parameters = parameter;
- type_t *result = typehash_insert(type);
- if (result != type) {
- free_type(type);
- }
-
- return result;
+ return identify_new_type(type);
}
static type_t *make_function_0_type(type_t *return_type)
type->function.return_type = return_type;
type->function.parameters = NULL;
- type_t *result = typehash_insert(type);
- if (result != type) {
- free_type(type);
- }
-
- return result;
+ return identify_new_type(type);
}
/**
case T___builtin_expect:
return make_function_2_type(type_long, type_long, type_long);
default:
- internal_errorf(HERE, "not implemented builtin symbol found");
+ internal_errorf(HERE, "not implemented builtin identifier found");
}
}
assert(is_declaration(entity));
type_t *type = entity->declaration.type;
return get_qualified_type(type,
- expression->base.type->base.qualifiers);
+ expression->base.type->base.qualifiers);
}
case EXPR_UNARY_DEREFERENCE: {
const expression_t *const value = expression->unary.value;
type_t *const type = skip_typeref(value->base.type);
- assert(is_type_pointer(type));
+ if (!is_type_pointer(type))
+ return type_error_type;
return type->pointer.points_to;
}
case EXPR_ARRAY_ACCESS: {
const expression_t *array_ref = expression->array_access.array_ref;
type_t *type_left = skip_typeref(array_ref->base.type);
- if (!is_type_valid(type_left))
- return type_left;
- assert(is_type_pointer(type_left));
+ if (!is_type_pointer(type_left))
+ return type_error_type;
return type_left->pointer.points_to;
}
case EXPR_COMPOUND_LITERAL:
return expression->compound_literal.type;
- default: break;
+ default:
+ return expression->base.type;
}
-
- return expression->base.type;
}
static expression_t *parse_reference(void)
if (entity == NULL) {
if (!strict_mode && look_ahead(1)->type == '(') {
/* an implicitly declared function */
- if (warning.implicit_function_declaration) {
- warningf(HERE, "implicit declaration of function '%Y'",
- symbol);
+ if (warning.error_implicit_function_declaration) {
+ errorf(HERE, "implicit declaration of function '%Y'", symbol);
+ } else if (warning.implicit_function_declaration) {
+ warningf(HERE, "implicit declaration of function '%Y'", symbol);
}
entity = create_implicit_function(symbol, HERE);
} else {
- errorf(HERE, "unknown symbol '%Y' found.", symbol);
+ errorf(HERE, "unknown identifier '%Y' found.", symbol);
entity = create_error_entity(symbol, ENTITY_VARIABLE);
}
}
if (entity->kind == ENTITY_VARIABLE) {
/* access of a variable from an outer function */
entity->variable.address_taken = true;
+ } else if (entity->kind == ENTITY_PARAMETER) {
+ entity->parameter.address_taken = true;
}
current_function->need_closure = true;
}
type_t *type = parse_typename();
rem_anchor_token(')');
- expect(')');
+ expect(')', end_error);
if (token.type == '{') {
return parse_compound_literal(type);
expression_t *expression = allocate_expression_zero(EXPR_STATEMENT);
statement_t *statement = parse_compound_statement(true);
+ statement->compound.stmt_expr = true;
expression->statement.statement = statement;
/* find last statement and use its type */
expression->base.type = type;
rem_anchor_token(')');
- expect(')');
+ expect(')', end_error);
end_error:
return expression;
add_anchor_token(')');
expression_t *result = parse_expression();
+ result->base.parenthesized = true;
rem_anchor_token(')');
- expect(')');
+ expect(')', end_error);
end_error:
return result;
next_token();
designator_t *last_designator = result;
- while(true) {
+ while (true) {
if (token.type == '.') {
next_token();
if (token.type != T_IDENTIFIER) {
designator->source_position = *HERE;
designator->array_index = parse_expression();
rem_anchor_token(']');
- expect(']');
+ expect(']', end_error);
if (designator->array_index == NULL) {
return NULL;
}
eat(T___builtin_offsetof);
- expect('(');
+ expect('(', end_error);
add_anchor_token(',');
type_t *type = parse_typename();
rem_anchor_token(',');
- expect(',');
+ expect(',', end_error);
add_anchor_token(')');
designator_t *designator = parse_designator();
rem_anchor_token(')');
- expect(')');
+ expect(')', end_error);
expression->offsetofe.type = type;
expression->offsetofe.designator = designator;
eat(T___builtin_va_start);
- expect('(');
+ expect('(', end_error);
add_anchor_token(',');
expression->va_starte.ap = parse_assignment_expression();
rem_anchor_token(',');
- expect(',');
+ expect(',', end_error);
expression_t *const expr = parse_assignment_expression();
if (expr->kind == EXPR_REFERENCE) {
entity_t *const entity = expr->reference.entity;
if (entity->base.parent_scope != ¤t_function->parameters
|| entity->base.next != NULL
- || entity->kind != ENTITY_VARIABLE) {
+ || entity->kind != ENTITY_PARAMETER) {
errorf(&expr->base.source_position,
"second argument of 'va_start' must be last parameter of the current function");
} else {
expression->va_starte.parameter = &entity->variable;
}
- expect(')');
+ expect(')', end_error);
return expression;
}
- expect(')');
+ expect(')', end_error);
end_error:
return create_invalid_expression();
}
eat(T___builtin_va_arg);
- expect('(');
+ expect('(', end_error);
expression->va_arge.ap = parse_assignment_expression();
- expect(',');
+ expect(',', end_error);
expression->base.type = parse_typename();
- expect(')');
+ expect(')', end_error);
return expression;
end_error:
eat(T___builtin_constant_p);
- expect('(');
+ expect('(', end_error);
add_anchor_token(')');
expression->builtin_constant.value = parse_assignment_expression();
rem_anchor_token(')');
- expect(')');
+ expect(')', end_error);
expression->base.type = type_int;
return expression;
eat(T___builtin_prefetch);
- expect('(');
+ expect('(', end_error);
add_anchor_token(')');
expression->builtin_prefetch.adr = parse_assignment_expression();
if (token.type == ',') {
expression->builtin_prefetch.locality = parse_assignment_expression();
}
rem_anchor_token(')');
- expect(')');
+ expect(')', end_error);
expression->base.type = type_void;
return expression;
expression->base.source_position = *HERE;
next_token();
- expect('(');
+ expect('(', end_error);
expression->binary.left = parse_assignment_expression();
- expect(',');
+ expect(',', end_error);
expression->binary.right = parse_assignment_expression();
- expect(')');
+ expect(')', end_error);
type_t *const orig_type_left = expression->binary.left->base.type;
type_t *const orig_type_right = expression->binary.right->base.type;
#if 0
/**
- * Parses a __builtin_expect() expression.
+ * Parses a __builtin_expect(, end_error) expression.
*/
-static expression_t *parse_builtin_expect(void)
+static expression_t *parse_builtin_expect(void, end_error)
{
expression_t *expression
= allocate_expression_zero(EXPR_BINARY_BUILTIN_EXPECT);
eat(T___builtin_expect);
- expect('(');
+ expect('(', end_error);
expression->binary.left = parse_assignment_expression();
- expect(',');
+ expect(',', end_error);
expression->binary.right = parse_constant_expression();
- expect(')');
+ expect(')', end_error);
expression->base.type = expression->binary.left->base.type;
eat(T__assume);
- expect('(');
+ expect('(', end_error);
add_anchor_token(')');
expression->unary.value = parse_assignment_expression();
rem_anchor_token(')');
- expect(')');
+ expect(')', end_error);
expression->base.type = type_void;
return expression;
add_anchor_token(',');
if (token.type != ')') {
- while(true) {
+ while (true) {
(void)parse_assignment_expression();
if (token.type != ',')
break;
}
rem_anchor_token(',');
rem_anchor_token(')');
- expect(')');
+ expect(')', end_error);
end_error:
return cnst;
expression->base.type = automatic_type_conversion(return_type);
rem_anchor_token(']');
- expect(']');
+ expect(']', end_error);
end_error:
return expression;
}
eat(kind == EXPR_SIZEOF ? T_sizeof : T___alignof__);
- char const* const what = kind == EXPR_SIZEOF ? "sizeof" : "alignof";
-
/* we only refer to a type property, mark this case */
bool old = in_type_prop;
in_type_prop = true;
add_anchor_token(')');
orig_type = parse_typename();
rem_anchor_token(')');
- expect(')');
+ expect(')', end_error);
if (token.type == '{') {
/* It was not sizeof(type) after all. It is sizeof of an expression
tp_expression->typeprop.type = orig_type;
type_t const* const type = skip_typeref(orig_type);
char const* const wrong_type =
- is_type_incomplete(type) ? "incomplete" :
- type->kind == TYPE_FUNCTION ? "function designator" :
- type->kind == TYPE_BITFIELD ? "bitfield" :
+ GNU_MODE && is_type_atomic(type, ATOMIC_TYPE_VOID) ? NULL :
+ is_type_incomplete(type) ? "incomplete" :
+ type->kind == TYPE_FUNCTION ? "function designator" :
+ type->kind == TYPE_BITFIELD ? "bitfield" :
NULL;
if (wrong_type != NULL) {
+ char const* const what = kind == EXPR_SIZEOF ? "sizeof" : "alignof";
errorf(&tp_expression->base.source_position,
"operand of %s expression must not be of %s type '%T'",
what, wrong_type, orig_type);
symbol, type_left);
}
create_error_entry:
- return create_invalid_expression();
+ entry = create_error_entity(symbol, ENTITY_COMPOUND_MEMBER);
}
assert(is_declaration(entry));
}
rem_anchor_token(',');
rem_anchor_token(')');
- expect(')');
+ expect(')', end_error);
if (function_type == NULL)
return result;
}
/* do default promotion */
- for( ; argument != NULL; argument = argument->next) {
+ for (; argument != NULL; argument = argument->next) {
type_t *type = argument->expression->base.type;
type = get_default_promoted_type(type);
type1->compound.compound == type2->compound.compound;
}
+static expression_t const *get_reference_address(expression_t const *expr)
+{
+ bool regular_take_address = true;
+ for (;;) {
+ if (expr->kind == EXPR_UNARY_TAKE_ADDRESS) {
+ expr = expr->unary.value;
+ } else {
+ regular_take_address = false;
+ }
+
+ if (expr->kind != EXPR_UNARY_DEREFERENCE)
+ break;
+
+ expr = expr->unary.value;
+ }
+
+ if (expr->kind != EXPR_REFERENCE)
+ return NULL;
+
+ /* special case for functions which are automatically converted to a
+ * pointer to function without an extra TAKE_ADDRESS operation */
+ if (!regular_take_address &&
+ expr->reference.entity->kind != ENTITY_FUNCTION) {
+ return NULL;
+ }
+
+ return expr;
+}
+
+static void warn_reference_address_as_bool(expression_t const* expr)
+{
+ if (!warning.address)
+ return;
+
+ expr = get_reference_address(expr);
+ if (expr != NULL) {
+ warningf(&expr->base.source_position,
+ "the address of '%Y' will always evaluate as 'true'",
+ expr->reference.entity->base.symbol);
+ }
+}
+
+static void warn_assignment_in_condition(const expression_t *const expr)
+{
+ if (!warning.parentheses)
+ return;
+ if (expr->base.kind != EXPR_BINARY_ASSIGN)
+ return;
+ if (expr->base.parenthesized)
+ return;
+ warningf(&expr->base.source_position,
+ "suggest parentheses around assignment used as truth value");
+}
+
+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);
+ warn_assignment_in_condition(expr);
+ } else if (is_type_valid(type)) {
+ errorf(&expr->base.source_position,
+ "%s must have scalar type", context);
+ }
+}
+
/**
* Parse a conditional expression, ie. '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;
true_expression = parse_expression();
}
rem_anchor_token(':');
- expect(':');
+ expect(':', end_error);
+end_error:;
expression_t *false_expression =
parse_sub_expression(c_mode & _CXX ? PREC_ASSIGNMENT : PREC_CONDITIONAL);
= create_implicit_cast(false_expression, result_type);
conditional->base.type = result_type;
return result;
-end_error:
- return create_invalid_expression();
}
/**
eat(T___builtin_classify_type);
- expect('(');
+ expect('(', end_error);
add_anchor_token(')');
expression_t *expression = parse_expression();
rem_anchor_token(')');
- expect(')');
+ expect(')', end_error);
result->classify_type.type_expression = expression;
return result;
if (token.type == '[') {
next_token();
result->kind = EXPR_UNARY_DELETE_ARRAY;
- expect(']');
+ expect(']', end_error);
end_error:;
}
type_t *const type = skip_typeref(value->base.type);
if (!is_type_pointer(type)) {
- errorf(&value->base.source_position,
- "operand of delete must have pointer type");
+ if (is_type_valid(type)) {
+ errorf(&value->base.source_position,
+ "operand of delete must have pointer type");
+ }
} else if (warning.other &&
is_type_atomic(skip_typeref(type->pointer.points_to), ATOMIC_TYPE_VOID)) {
warningf(&value->base.source_position,
static bool is_lvalue(const expression_t *expression)
{
- /* TODO: doesn't seem to be consistent with §6.3.2.1 (1) */
+ /* TODO: doesn't seem to be consistent with §6.3.2.1:1 */
switch (expression->kind) {
- case EXPR_REFERENCE:
case EXPR_ARRAY_ACCESS:
+ case EXPR_COMPOUND_LITERAL:
+ case EXPR_REFERENCE:
case EXPR_SELECT:
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);
+ }
}
}
"traditional C rejects the unary plus operator");
}
-static expression_t const *get_reference_address(expression_t const *expr)
-{
- bool regular_take_address = true;
- for (;;) {
- if (expr->kind == EXPR_UNARY_TAKE_ADDRESS) {
- expr = expr->unary.value;
- } else {
- regular_take_address = false;
- }
-
- if (expr->kind != EXPR_UNARY_DEREFERENCE)
- break;
-
- expr = expr->unary.value;
- }
-
- /* special case for functions which are automatically converted to a
- * pointer to function without an extra TAKE_ADDRESS operation */
- if (!regular_take_address && expr->kind == EXPR_REFERENCE
- && expr->reference.entity->kind == ENTITY_FUNCTION) {
- return expr;
- }
-
- return NULL;
-}
-
-static void warn_function_address_as_bool(expression_t const* expr)
-{
- if (!warning.address)
- return;
-
- expr = get_reference_address(expr);
- if (expr != NULL) {
- warningf(&expr->base.source_position,
- "the address of '%Y' will always evaluate as 'true'",
- expr->reference.entity->base.symbol);
- }
-}
-
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_function_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;
}
entity_t *const entity = expression->reference.entity;
- if (entity->kind != ENTITY_VARIABLE)
+ if (entity->kind != ENTITY_VARIABLE && entity->kind != ENTITY_PARAMETER)
return;
if (entity->declaration.storage_class == STORAGE_CLASS_REGISTER
&& !may_be_register) {
errorf(&expression->base.source_position,
- "address of register variable '%Y' requested",
- entity->base.symbol);
+ "address of register %s '%Y' requested",
+ get_entity_kind_name(entity->kind), entity->base.symbol);
}
- entity->variable.address_taken = true;
+ if (entity->kind == ENTITY_VARIABLE) {
+ entity->variable.address_taken = true;
+ } else {
+ assert(entity->kind == ENTITY_PARAMETER);
+ entity->parameter.address_taken = true;
+ }
}
/**
return;
/* §6.5.3.2 */
- if (value->kind != EXPR_ARRAY_ACCESS
- && value->kind != EXPR_UNARY_DEREFERENCE
- && !is_lvalue(value)) {
- errorf(&expression->base.source_position,
- "'&' requires an lvalue");
+ if (!is_lvalue(value)) {
+ errorf(&expression->base.source_position, "'&' requires an lvalue");
}
if (type->kind == TYPE_BITFIELD) {
errorf(&expression->base.source_position,
/**
* Check the semantic restrictions for a div/mod expression.
*/
-static void semantic_divmod_arithmetic(binary_expression_t *expression) {
+static void semantic_divmod_arithmetic(binary_expression_t *expression)
+{
semantic_binexpr_arithmetic(expression);
warn_div_by_zero(expression);
}
+static void warn_addsub_in_shift(const expression_t *const expr)
+{
+ if (expr->base.parenthesized)
+ return;
+
+ char op;
+ switch (expr->kind) {
+ case EXPR_BINARY_ADD: op = '+'; break;
+ case EXPR_BINARY_SUB: op = '-'; break;
+ default: return;
+ }
+
+ warningf(&expr->base.source_position,
+ "suggest parentheses around '%c' inside shift", op);
+}
+
static void semantic_shift_op(binary_expression_t *expression)
{
expression_t *const left = expression->left;
return;
}
+ if (warning.parentheses) {
+ warn_addsub_in_shift(left);
+ warn_addsub_in_shift(right);
+ }
+
type_left = promote_integer(type_left);
type_right = promote_integer(type_right);
expression->left = create_implicit_cast(left, arithmetic_type);
expression->right = create_implicit_cast(right, arithmetic_type);
expression->base.type = arithmetic_type;
- return;
} else if (is_type_pointer(type_left) && is_type_integer(type_right)) {
check_pointer_arithmetic(&expression->base.source_position,
type_left, orig_type_left);
expression->left = create_implicit_cast(left, arithmetic_type);
expression->right = create_implicit_cast(right, arithmetic_type);
expression->base.type = arithmetic_type;
- return;
} else if (is_type_pointer(type_left) && is_type_integer(type_right)) {
check_pointer_arithmetic(&expression->base.source_position,
type_left, orig_type_left);
}
}
+static void warn_comparison_in_comparison(const expression_t *const expr)
+{
+ if (expr->base.parenthesized)
+ return;
+ switch (expr->base.kind) {
+ case EXPR_BINARY_LESS:
+ case EXPR_BINARY_GREATER:
+ case EXPR_BINARY_LESSEQUAL:
+ case EXPR_BINARY_GREATEREQUAL:
+ case EXPR_BINARY_NOTEQUAL:
+ case EXPR_BINARY_EQUAL:
+ warningf(&expr->base.source_position,
+ "comparisons like 'x <= y < z' do not have their mathematical meaning");
+ break;
+ default:
+ break;
+ }
+}
+
+static bool maybe_negative(expression_t const *const expr)
+{
+ return
+ !is_constant_expression(expr) ||
+ fold_constant(expr) < 0;
+}
+
/**
* Check the semantics of comparison expressions.
*
}
}
+ if (warning.parentheses) {
+ warn_comparison_in_comparison(left);
+ warn_comparison_in_comparison(right);
+ }
+
type_t *orig_type_left = left->base.type;
type_t *orig_type_right = right->base.type;
type_t *type_left = skip_typeref(orig_type_left);
/* TODO non-arithmetic types */
if (is_type_arithmetic(type_left) && is_type_arithmetic(type_right)) {
- /* test for signed vs unsigned compares */
- if (warning.sign_compare &&
- (expression->base.kind != EXPR_BINARY_EQUAL &&
- expression->base.kind != EXPR_BINARY_NOTEQUAL) &&
- (is_type_signed(type_left) != is_type_signed(type_right))) {
-
- /* check if 1 of the operands is a constant, in this case we just
- * check wether we can safely represent the resulting constant in
- * the type of the other operand. */
- expression_t *const_expr = NULL;
- expression_t *other_expr = NULL;
-
- if (is_constant_expression(left)) {
- const_expr = left;
- other_expr = right;
- } else if (is_constant_expression(right)) {
- const_expr = right;
- other_expr = left;
- }
+ type_t *arithmetic_type = semantic_arithmetic(type_left, type_right);
- if (const_expr != NULL) {
- type_t *other_type = skip_typeref(other_expr->base.type);
- long val = fold_constant(const_expr);
- /* TODO: check if val can be represented by other_type */
- (void) other_type;
- (void) val;
+ /* test for signed vs unsigned compares */
+ if (warning.sign_compare && is_type_integer(arithmetic_type)) {
+ bool const signed_left = is_type_signed(type_left);
+ bool const signed_right = is_type_signed(type_right);
+ if (signed_left != signed_right) {
+ /* FIXME long long needs better const folding magic */
+ /* TODO check whether constant value can be represented by other type */
+ if ((signed_left && maybe_negative(left)) ||
+ (signed_right && maybe_negative(right))) {
+ warningf(&expression->base.source_position,
+ "comparison between signed and unsigned");
+ }
}
- warningf(&expression->base.source_position,
- "comparison between signed and unsigned");
}
- type_t *arithmetic_type = semantic_arithmetic(type_left, type_right);
+
expression->left = create_implicit_cast(left, arithmetic_type);
expression->right = create_implicit_cast(right, arithmetic_type);
expression->base.type = arithmetic_type;
return false;
}
+ if (left->kind == EXPR_REFERENCE
+ && left->reference.entity->kind == ENTITY_FUNCTION) {
+ errorf(HERE, "cannot assign to function '%E'", left);
+ return false;
+ }
+
if (is_type_array(type_left)) {
- errorf(HERE, "cannot assign to arrays ('%E')", left);
+ errorf(HERE, "cannot assign to array '%E'", left);
return false;
}
if (type_left->base.qualifiers & TYPE_QUALIFIER_CONST) {
}
}
+static void warn_logical_and_within_or(const expression_t *const expr)
+{
+ if (expr->base.kind != EXPR_BINARY_LOGICAL_AND)
+ return;
+ if (expr->base.parenthesized)
+ return;
+ warningf(&expr->base.source_position,
+ "suggest parentheses around && within ||");
+}
+
/**
* Check the semantic restrictions of a logical expression.
*/
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_function_address_as_bool(left);
- warn_function_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");
+ if (expression->base.kind == EXPR_BINARY_LOGICAL_OR &&
+ warning.parentheses) {
+ warn_logical_and_within_or(expression->left);
+ warn_logical_and_within_or(expression->right);
}
-
expression->base.type = c_mode & _CXX ? type_bool : type_int;
}
case EXPR_BINARY_COMMA:
return expression_has_effect(expr->binary.right);
- case EXPR_BINARY_BUILTIN_EXPECT: return true;
case EXPR_BINARY_ISGREATER: return false;
case EXPR_BINARY_ISGREATEREQUAL: return false;
case EXPR_BINARY_ISLESS: return false;
assert(left != NULL);
left->base.source_position = source_position;
- while(true) {
+ while (true) {
if (token.type < 0) {
return expected_expression_error();
}
* @param precedence the precedence of the operator
*/
static void register_infix_parser(parse_expression_infix_function parser,
- int token_type, unsigned precedence)
+ int token_type, precedence_t precedence)
{
expression_parser_function_t *entry = &expression_parsers[token_type];
*/
static asm_argument_t *parse_asm_arguments(bool is_out)
{
- asm_argument_t *result = NULL;
- asm_argument_t *last = NULL;
+ asm_argument_t *result = NULL;
+ asm_argument_t **anchor = &result;
while (token.type == T_STRING_LITERAL || token.type == '[') {
asm_argument_t *argument = allocate_ast_zero(sizeof(argument[0]));
}
argument->symbol = token.v.symbol;
- expect(']');
+ expect(']', end_error);
}
argument->constraints = parse_string_literals();
- expect('(');
+ expect('(', end_error);
add_anchor_token(')');
expression_t *expression = parse_expression();
rem_anchor_token(')');
mark_vars_read(expression, NULL);
}
argument->expression = expression;
- expect(')');
+ expect(')', end_error);
set_address_taken(expression, true);
- if (last != NULL) {
- last->next = argument;
- } else {
- result = argument;
- }
- last = argument;
+ *anchor = argument;
+ anchor = &argument->next;
if (token.type != ',')
break;
asm_clobber_t *result = NULL;
asm_clobber_t *last = NULL;
- while(token.type == T_STRING_LITERAL) {
+ while (token.type == T_STRING_LITERAL) {
asm_clobber_t *clobber = allocate_ast_zero(sizeof(clobber[0]));
clobber->clobber = parse_string_literals();
asm_statement->is_volatile = true;
}
- expect('(');
+ expect('(', end_error);
add_anchor_token(')');
add_anchor_token(':');
asm_statement->asm_text = parse_string_literals();
end_of_asm:
rem_anchor_token(')');
- expect(')');
- expect(';');
+ expect(')', end_error);
+ expect(';', end_error);
if (asm_statement->outputs == NULL) {
/* GCC: An 'asm' instruction without any output operands will be treated
PUSH_PARENT(statement);
- expect(':');
+ expect(':', end_error);
+end_error:
if (current_switch != NULL) {
if (! statement->case_label.is_bad) {
POP_PARENT;
return statement;
-end_error:
- POP_PARENT;
- return create_invalid_statement();
}
/**
PUSH_PARENT(statement);
- expect(':');
+ expect(':', end_error);
if (current_switch != NULL) {
const case_label_statement_t *def_label = current_switch->default_label;
if (def_label != NULL) {
}
/* remember the labels in a list for later checking */
- if (label_last == NULL) {
- label_first = &statement->label;
- } else {
- label_last->next = &statement->label;
- }
- label_last = &statement->label;
+ *label_anchor = &statement->label;
+ label_anchor = &statement->label.next;
POP_PARENT;
return statement;
add_anchor_token('{');
- expect('(');
+ expect('(', end_error);
add_anchor_token(')');
expression_t *const expr = parse_expression();
statement->ifs.condition = 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(')');
+ expect(')', end_error);
end_error:
rem_anchor_token('{');
add_anchor_token(T_else);
- statement->ifs.true_statement = parse_statement();
+ statement_t *const true_stmt = parse_statement();
+ statement->ifs.true_statement = true_stmt;
rem_anchor_token(T_else);
if (token.type == T_else) {
next_token();
statement->ifs.false_statement = parse_statement();
+ } else if (warning.parentheses &&
+ true_stmt->kind == STATEMENT_IF &&
+ true_stmt->ifs.false_statement != NULL) {
+ warningf(&true_stmt->base.source_position,
+ "suggest explicit braces to avoid ambiguous 'else'");
}
POP_PARENT;
*
* @param statement the switch statement to check
*/
-static void check_enum_cases(const switch_statement_t *statement) {
+static void check_enum_cases(const switch_statement_t *statement)
+{
const type_t *type = skip_typeref(statement->expression->base.type);
if (! is_type_enum(type))
return;
PUSH_PARENT(statement);
- expect('(');
+ expect('(', end_error);
add_anchor_token(')');
expression_t *const expr = parse_expression();
mark_vars_read(expr, NULL);
type = type_error_type;
}
statement->switchs.expression = create_implicit_cast(expr, type);
- expect(')');
+ expect(')', end_error);
rem_anchor_token(')');
switch_statement_t *rem = current_switch;
PUSH_PARENT(statement);
- expect('(');
+ expect('(', end_error);
add_anchor_token(')');
expression_t *const cond = parse_expression();
statement->whiles.condition = 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(')');
+ expect(')', end_error);
statement->whiles.body = parse_loop_body(statement);
statement->do_while.body = parse_loop_body(statement);
rem_anchor_token(T_while);
- expect(T_while);
- expect('(');
+ expect(T_while, end_error);
+ expect('(', end_error);
add_anchor_token(')');
expression_t *const cond = parse_expression();
statement->do_while.condition = 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(')');
- expect(';');
+ expect(')', end_error);
+ expect(';', end_error);
POP_PARENT;
return statement;
eat(T_for);
- PUSH_PARENT(statement);
+ expect('(', end_error1);
+ add_anchor_token(')');
- size_t const top = environment_top();
- scope_push(&statement->fors.scope);
+ PUSH_PARENT(statement);
- expect('(');
- add_anchor_token(')');
+ size_t const top = environment_top();
+ scope_t *old_scope = scope_push(&statement->fors.scope);
- if (token.type != ';') {
- if (is_declaration_specifier(&token, false)) {
- parse_declaration(record_entity);
- } else {
- add_anchor_token(';');
- expression_t *const init = parse_expression();
- statement->fors.initialisation = init;
- mark_vars_read(init, VAR_ANY);
- if (warning.unused_value && !expression_has_effect(init)) {
- warningf(&init->base.source_position,
- "initialisation of 'for'-statement has no effect");
- }
- rem_anchor_token(';');
- expect(';');
- }
+ if (token.type == ';') {
+ next_token();
+ } else if (is_declaration_specifier(&token, false)) {
+ parse_declaration(record_entity, DECL_FLAGS_NONE);
} else {
- expect(';');
+ add_anchor_token(';');
+ expression_t *const init = parse_expression();
+ statement->fors.initialisation = init;
+ mark_vars_read(init, ENT_ANY);
+ if (warning.unused_value && !expression_has_effect(init)) {
+ warningf(&init->base.source_position,
+ "initialisation of 'for'-statement has no effect");
+ }
+ rem_anchor_token(';');
+ expect(';', end_error2);
}
if (token.type != ';') {
add_anchor_token(';');
expression_t *const cond = parse_expression();
statement->fors.condition = 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(';');
}
- expect(';');
+ expect(';', end_error2);
if (token.type != ')') {
expression_t *const step = parse_expression();
statement->fors.step = step;
- mark_vars_read(step, VAR_ANY);
+ mark_vars_read(step, ENT_ANY);
if (warning.unused_value && !expression_has_effect(step)) {
warningf(&step->base.source_position,
"step of 'for'-statement has no effect");
}
}
- expect(')');
+ expect(')', end_error2);
rem_anchor_token(')');
statement->fors.body = parse_loop_body(statement);
assert(current_scope == &statement->fors.scope);
- scope_pop();
+ scope_pop(old_scope);
environment_pop_to(top);
POP_PARENT;
return statement;
-end_error:
+end_error2:
POP_PARENT;
rem_anchor_token(')');
assert(current_scope == &statement->fors.scope);
- scope_pop();
+ scope_pop(old_scope);
environment_pop_to(top);
+ /* fallthrough */
+end_error1:
return create_invalid_statement();
}
expression_t *expression = parse_expression();
mark_vars_read(expression, NULL);
- /* Argh: although documentation say the expression must be of type void *,
- * gcc excepts anything that can be casted into void * without error */
+ /* Argh: although documentation says the expression must be of type void*,
+ * gcc accepts anything that can be casted into void* without error */
type_t *type = expression->base.type;
if (type != type_error_type) {
}
/* remember the goto's in a list for later checking */
- if (goto_last == NULL) {
- goto_first = &statement->gotos;
- } else {
- goto_last->next = &statement->gotos;
- }
- goto_last = &statement->gotos;
+ *goto_anchor = &statement->gotos;
+ goto_anchor = &statement->gotos.next;
- expect(';');
+ expect(';', end_error);
return statement;
end_error:
statement_t *statement = allocate_statement_zero(STATEMENT_CONTINUE);
eat(T_continue);
- expect(';');
+ expect(';', end_error);
end_error:
return statement;
statement_t *statement = allocate_statement_zero(STATEMENT_BREAK);
eat(T_break);
- expect(';');
+ expect(';', end_error);
end_error:
return statement;
statement_t *statement = allocate_statement_zero(STATEMENT_LEAVE);
eat(T___leave);
- expect(';');
+ expect(';', end_error);
end_error:
return statement;
assert(is_type_function(func_type));
type_t *const return_type = skip_typeref(func_type->function.return_type);
+ source_position_t const *const pos = &statement->base.source_position;
if (return_value != NULL) {
type_t *return_value_type = skip_typeref(return_value->base.type);
- if (is_type_atomic(return_type, ATOMIC_TYPE_VOID) &&
- !is_type_atomic(return_value_type, ATOMIC_TYPE_VOID)) {
- if (warning.other) {
- warningf(&statement->base.source_position,
- "'return' with a value, in function returning void");
+ if (is_type_atomic(return_type, ATOMIC_TYPE_VOID)) {
+ if (is_type_atomic(return_value_type, ATOMIC_TYPE_VOID)) {
+ /* ISO/IEC 14882:1998(E) §6.6.3:2 */
+ /* Only warn in C mode, because GCC does the same */
+ if (c_mode & _CXX || strict_mode) {
+ errorf(pos,
+ "'return' with a value, in function returning 'void'");
+ } else if (warning.other) {
+ warningf(pos,
+ "'return' with a value, in function returning 'void'");
+ }
+ } else if (!(c_mode & _CXX)) { /* ISO/IEC 14882:1998(E) §6.6.3:3 */
+ /* Only warn in C mode, because GCC does the same */
+ if (strict_mode) {
+ errorf(pos,
+ "'return' with expression in function return 'void'");
+ } else if (warning.other) {
+ warningf(pos,
+ "'return' with expression in function return 'void'");
+ }
}
- return_value = NULL;
} else {
assign_error_t error = semantic_assign(return_type, return_value);
report_assign_error(error, return_type, return_value, "'return'",
- &statement->base.source_position);
- return_value = create_implicit_cast(return_value, return_type);
+ pos);
}
+ return_value = create_implicit_cast(return_value, return_type);
/* check for returning address of a local var */
if (warning.other && return_value != NULL
&& return_value->base.kind == EXPR_UNARY_TAKE_ADDRESS) {
const expression_t *expression = return_value->unary.value;
if (expression_is_local_variable(expression)) {
- warningf(&statement->base.source_position,
- "function returns address of local variable");
+ warningf(pos, "function returns address of local variable");
}
}
} else if (warning.other && !is_type_atomic(return_type, ATOMIC_TYPE_VOID)) {
- warningf(&statement->base.source_position,
- "'return' without value, in function returning non-void");
+ /* ISO/IEC 14882:1998(E) §6.6.3:3 */
+ if (c_mode & _CXX || strict_mode) {
+ errorf(pos,
+ "'return' without value, in function returning non-void");
+ } else {
+ warningf(pos,
+ "'return' without value, in function returning non-void");
+ }
}
statement->returns.value = return_value;
- expect(';');
+ expect(';', end_error);
end_error:
return statement;
statement_t *statement = allocate_statement_zero(STATEMENT_DECLARATION);
entity_t *before = current_scope->last_entity;
- if (GNU_MODE)
+ if (GNU_MODE) {
parse_external_declaration();
- else
- parse_declaration(record_entity);
+ } else {
+ parse_declaration(record_entity, DECL_FLAGS_NONE);
+ }
if (before == NULL) {
statement->declaration.declarations_begin = current_scope->entities;
expression_t *const expr = parse_expression();
statement->expression.expression = expr;
- mark_vars_read(expr, VAR_ANY);
+ mark_vars_read(expr, ENT_ANY);
- expect(';');
+ expect(';', end_error);
end_error:
return statement;
if (token.type == T___except) {
eat(T___except);
- expect('(');
+ expect('(', end_error);
add_anchor_token(')');
expression_t *const expr = parse_expression();
mark_vars_read(expr, NULL);
}
statement->ms_try.except_expression = create_implicit_cast(expr, type);
rem_anchor_token(')');
- expect(')');
+ expect(')', end_error);
statement->ms_try.final_statement = parse_compound_statement(false);
} else if (token.type == T__finally) {
eat(T___finally);
environment_push(entity);
append_entity(current_scope, entity);
- size_t const top = environment_top();
- scope_push(&entity->namespacee.members);
+ size_t const top = environment_top();
+ scope_t *old_scope = scope_push(&entity->namespacee.members);
- expect('{');
+ expect('{', end_error);
parse_externals();
- expect('}');
+ expect('}', end_error);
end_error:
assert(current_scope == &entity->namespacee.members);
- scope_pop();
+ scope_pop(old_scope);
environment_pop_to(top);
}
* declaration types, so we guess a bit here to improve robustness
* for incorrect programs */
switch (la1_type) {
+ case '&':
case '*':
if (get_entity(token.v.symbol, NAMESPACE_NORMAL) != NULL)
goto expression_statment;
} 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;
eat('{');
add_anchor_token('}');
+ /* tokens, which can start a statement */
+ /* TODO MS, __builtin_FOO */
+ add_anchor_token('!');
+ add_anchor_token('&');
+ add_anchor_token('(');
+ add_anchor_token('*');
+ add_anchor_token('+');
+ add_anchor_token('-');
+ add_anchor_token('{');
+ add_anchor_token('~');
+ add_anchor_token(T_CHARACTER_CONSTANT);
+ add_anchor_token(T_COLONCOLON);
+ add_anchor_token(T_FLOATINGPOINT);
+ add_anchor_token(T_IDENTIFIER);
+ add_anchor_token(T_INTEGER);
+ add_anchor_token(T_MINUSMINUS);
+ add_anchor_token(T_PLUSPLUS);
+ add_anchor_token(T_STRING_LITERAL);
+ add_anchor_token(T_WIDE_CHARACTER_CONSTANT);
+ add_anchor_token(T_WIDE_STRING_LITERAL);
+ add_anchor_token(T__Bool);
+ add_anchor_token(T__Complex);
+ add_anchor_token(T__Imaginary);
+ add_anchor_token(T___FUNCTION__);
+ add_anchor_token(T___PRETTY_FUNCTION__);
+ add_anchor_token(T___alignof__);
+ add_anchor_token(T___attribute__);
+ add_anchor_token(T___builtin_va_start);
+ add_anchor_token(T___extension__);
+ add_anchor_token(T___func__);
+ add_anchor_token(T___imag__);
+ add_anchor_token(T___label__);
+ add_anchor_token(T___real__);
+ add_anchor_token(T___thread);
+ add_anchor_token(T_asm);
+ add_anchor_token(T_auto);
+ add_anchor_token(T_bool);
+ add_anchor_token(T_break);
+ add_anchor_token(T_case);
+ add_anchor_token(T_char);
+ add_anchor_token(T_class);
+ add_anchor_token(T_const);
+ add_anchor_token(T_const_cast);
+ add_anchor_token(T_continue);
+ add_anchor_token(T_default);
+ add_anchor_token(T_delete);
+ add_anchor_token(T_double);
+ add_anchor_token(T_do);
+ add_anchor_token(T_dynamic_cast);
+ add_anchor_token(T_enum);
+ add_anchor_token(T_extern);
+ add_anchor_token(T_false);
+ add_anchor_token(T_float);
+ add_anchor_token(T_for);
+ add_anchor_token(T_goto);
+ add_anchor_token(T_if);
+ add_anchor_token(T_inline);
+ add_anchor_token(T_int);
+ add_anchor_token(T_long);
+ add_anchor_token(T_new);
+ add_anchor_token(T_operator);
+ add_anchor_token(T_register);
+ add_anchor_token(T_reinterpret_cast);
+ add_anchor_token(T_restrict);
+ add_anchor_token(T_return);
+ add_anchor_token(T_short);
+ add_anchor_token(T_signed);
+ add_anchor_token(T_sizeof);
+ add_anchor_token(T_static);
+ add_anchor_token(T_static_cast);
+ add_anchor_token(T_struct);
+ add_anchor_token(T_switch);
+ add_anchor_token(T_template);
+ add_anchor_token(T_this);
+ add_anchor_token(T_throw);
+ add_anchor_token(T_true);
+ add_anchor_token(T_try);
+ add_anchor_token(T_typedef);
+ add_anchor_token(T_typeid);
+ add_anchor_token(T_typename);
+ add_anchor_token(T_typeof);
+ add_anchor_token(T_union);
+ add_anchor_token(T_unsigned);
+ add_anchor_token(T_using);
+ add_anchor_token(T_void);
+ add_anchor_token(T_volatile);
+ add_anchor_token(T_wchar_t);
+ add_anchor_token(T_while);
- size_t const top = environment_top();
- scope_push(&statement->compound.scope);
+ size_t const top = environment_top();
+ scope_t *old_scope = scope_push(&statement->compound.scope);
statement_t **anchor = &statement->compound.statements;
bool only_decls_so_far = true;
/* look over all statements again to produce no effect warnings */
if (warning.unused_value) {
statement_t *sub_statement = statement->compound.statements;
- for( ; sub_statement != NULL; sub_statement = sub_statement->base.next) {
+ for (; sub_statement != NULL; sub_statement = sub_statement->base.next) {
if (sub_statement->kind != STATEMENT_EXPRESSION)
continue;
/* don't emit a warning for the last expression in an expression
}
end_error:
+ rem_anchor_token(T_while);
+ rem_anchor_token(T_wchar_t);
+ rem_anchor_token(T_volatile);
+ rem_anchor_token(T_void);
+ rem_anchor_token(T_using);
+ rem_anchor_token(T_unsigned);
+ rem_anchor_token(T_union);
+ rem_anchor_token(T_typeof);
+ rem_anchor_token(T_typename);
+ rem_anchor_token(T_typeid);
+ rem_anchor_token(T_typedef);
+ rem_anchor_token(T_try);
+ rem_anchor_token(T_true);
+ rem_anchor_token(T_throw);
+ rem_anchor_token(T_this);
+ rem_anchor_token(T_template);
+ rem_anchor_token(T_switch);
+ rem_anchor_token(T_struct);
+ rem_anchor_token(T_static_cast);
+ rem_anchor_token(T_static);
+ rem_anchor_token(T_sizeof);
+ rem_anchor_token(T_signed);
+ rem_anchor_token(T_short);
+ rem_anchor_token(T_return);
+ rem_anchor_token(T_restrict);
+ rem_anchor_token(T_reinterpret_cast);
+ rem_anchor_token(T_register);
+ rem_anchor_token(T_operator);
+ rem_anchor_token(T_new);
+ rem_anchor_token(T_long);
+ rem_anchor_token(T_int);
+ rem_anchor_token(T_inline);
+ rem_anchor_token(T_if);
+ rem_anchor_token(T_goto);
+ rem_anchor_token(T_for);
+ rem_anchor_token(T_float);
+ rem_anchor_token(T_false);
+ rem_anchor_token(T_extern);
+ rem_anchor_token(T_enum);
+ rem_anchor_token(T_dynamic_cast);
+ rem_anchor_token(T_do);
+ rem_anchor_token(T_double);
+ rem_anchor_token(T_delete);
+ rem_anchor_token(T_default);
+ rem_anchor_token(T_continue);
+ rem_anchor_token(T_const_cast);
+ rem_anchor_token(T_const);
+ rem_anchor_token(T_class);
+ rem_anchor_token(T_char);
+ rem_anchor_token(T_case);
+ rem_anchor_token(T_break);
+ rem_anchor_token(T_bool);
+ rem_anchor_token(T_auto);
+ rem_anchor_token(T_asm);
+ rem_anchor_token(T___thread);
+ rem_anchor_token(T___real__);
+ rem_anchor_token(T___label__);
+ rem_anchor_token(T___imag__);
+ rem_anchor_token(T___func__);
+ rem_anchor_token(T___extension__);
+ rem_anchor_token(T___builtin_va_start);
+ rem_anchor_token(T___attribute__);
+ rem_anchor_token(T___alignof__);
+ rem_anchor_token(T___PRETTY_FUNCTION__);
+ rem_anchor_token(T___FUNCTION__);
+ rem_anchor_token(T__Imaginary);
+ rem_anchor_token(T__Complex);
+ rem_anchor_token(T__Bool);
+ rem_anchor_token(T_WIDE_STRING_LITERAL);
+ rem_anchor_token(T_WIDE_CHARACTER_CONSTANT);
+ rem_anchor_token(T_STRING_LITERAL);
+ rem_anchor_token(T_PLUSPLUS);
+ rem_anchor_token(T_MINUSMINUS);
+ rem_anchor_token(T_INTEGER);
+ rem_anchor_token(T_IDENTIFIER);
+ rem_anchor_token(T_FLOATINGPOINT);
+ rem_anchor_token(T_COLONCOLON);
+ rem_anchor_token(T_CHARACTER_CONSTANT);
+ rem_anchor_token('~');
+ rem_anchor_token('{');
+ rem_anchor_token('-');
+ rem_anchor_token('+');
+ rem_anchor_token('*');
+ rem_anchor_token('(');
+ rem_anchor_token('&');
+ rem_anchor_token('!');
rem_anchor_token('}');
assert(current_scope == &statement->compound.scope);
- scope_pop();
+ scope_pop(old_scope);
environment_pop_to(top);
POP_PARENT;
statement_t *statement = allocate_statement_zero(STATEMENT_ASM);
eat(T_asm);
- expect('(');
+ expect('(', end_error);
statement->asms.asm_text = parse_string_literals();
statement->base.next = unit->global_asm;
unit->global_asm = statement;
- expect(')');
- expect(';');
+ expect(')', end_error);
+ expect(';', end_error);
end_error:;
}
if (token.type == '{') {
next_token();
parse_externals();
- expect('}');
+ expect('}', end_error);
} else {
parse_external();
}
DECLARATION_START_NO_EXTERN
case T_IDENTIFIER:
case T___extension__:
- case '(': /* for function declarations with implicit return type and
- * parenthesized declarator, i.e. (f)(void); */
+ /* tokens below are for implicit int */
+ case '&': /* & x; -> int& x; (and error later, because C++ has no
+ implicit int) */
+ case '*': /* * x; -> int* x; */
+ case '(': /* (x); -> int (x); */
parse_external_declaration();
return;
translation_unit_t *finish_parsing(void)
{
- /* do NOT use scope_pop() here, this will crash, will it by hand */
assert(current_scope == &unit->scope);
- current_scope = NULL;
+ scope_pop(NULL);
assert(file_scope == &unit->scope);
check_unused_globals();
return result;
}
+/* §6.9.2:2 and §6.9.2:5: At the end of the translation incomplete arrays
+ * are given length one. */
+static void complete_incomplete_arrays(void)
+{
+ size_t n = ARR_LEN(incomplete_arrays);
+ for (size_t i = 0; i != n; ++i) {
+ declaration_t *const decl = incomplete_arrays[i];
+ type_t *const orig_type = decl->type;
+ type_t *const type = skip_typeref(orig_type);
+
+ if (!is_type_incomplete(type))
+ continue;
+
+ if (warning.other) {
+ warningf(&decl->base.source_position,
+ "array '%#T' assumed to have one element",
+ orig_type, decl->base.symbol);
+ }
+
+ type_t *const new_type = duplicate_type(type);
+ new_type->array.size_constant = true;
+ new_type->array.has_implicit_size = true;
+ new_type->array.size = 1;
+
+ type_t *const result = identify_new_type(new_type);
+
+ decl->type = result;
+ }
+}
+
void parse(void)
{
lookahead_bufpos = 0;
for (int i = 0; i < MAX_LOOKAHEAD + 2; ++i) {
next_token();
}
- current_linkage = c_mode & _CXX ? LINKAGE_CXX : LINKAGE_C;
+ current_linkage = c_mode & _CXX ? LINKAGE_CXX : LINKAGE_C;
+ incomplete_arrays = NEW_ARR_F(declaration_t*, 0);
parse_translation_unit();
+ complete_incomplete_arrays();
+ DEL_ARR_F(incomplete_arrays);
+ incomplete_arrays = NULL;
}
/**
if (c_mode & _MS) {
/* add predefined symbols for extended-decl-modifier */
- sym_align = symbol_table_insert("align");
- sym_allocate = symbol_table_insert("allocate");
- sym_dllimport = symbol_table_insert("dllimport");
- sym_dllexport = symbol_table_insert("dllexport");
- sym_naked = symbol_table_insert("naked");
- sym_noinline = symbol_table_insert("noinline");
- sym_noreturn = symbol_table_insert("noreturn");
- sym_nothrow = symbol_table_insert("nothrow");
- sym_novtable = symbol_table_insert("novtable");
- sym_property = symbol_table_insert("property");
- sym_get = symbol_table_insert("get");
- sym_put = symbol_table_insert("put");
- sym_selectany = symbol_table_insert("selectany");
- sym_thread = symbol_table_insert("thread");
- sym_uuid = symbol_table_insert("uuid");
- sym_deprecated = symbol_table_insert("deprecated");
- sym_restrict = symbol_table_insert("restrict");
- sym_noalias = symbol_table_insert("noalias");
+ sym_align = symbol_table_insert("align");
+ sym_allocate = symbol_table_insert("allocate");
+ sym_dllimport = symbol_table_insert("dllimport");
+ sym_dllexport = symbol_table_insert("dllexport");
+ sym_naked = symbol_table_insert("naked");
+ sym_noinline = symbol_table_insert("noinline");
+ sym_returns_twice = symbol_table_insert("returns_twice");
+ sym_noreturn = symbol_table_insert("noreturn");
+ sym_nothrow = symbol_table_insert("nothrow");
+ sym_novtable = symbol_table_insert("novtable");
+ sym_property = symbol_table_insert("property");
+ sym_get = symbol_table_insert("get");
+ sym_put = symbol_table_insert("put");
+ sym_selectany = symbol_table_insert("selectany");
+ sym_thread = symbol_table_insert("thread");
+ sym_uuid = symbol_table_insert("uuid");
+ sym_deprecated = symbol_table_insert("deprecated");
+ sym_restrict = symbol_table_insert("restrict");
+ sym_noalias = symbol_table_insert("noalias");
}
memset(token_anchor_set, 0, sizeof(token_anchor_set));