+static declaration_t *internal_record_declaration(
+ declaration_t *const declaration,
+ const bool is_function_definition)
+{
+ const symbol_t *const symbol = declaration->symbol;
+ const namespace_t namespc = (namespace_t)declaration->namespc;
+
+ const type_t *const type = skip_typeref(declaration->type);
+ if (is_type_function(type) && type->function.unspecified_parameters) {
+ warningf(declaration->source_position,
+ "function declaration '%#T' is not a prototype",
+ type, declaration->symbol);
+ }
+
+ declaration_t *const previous_declaration = get_declaration(symbol, namespc);
+ assert(declaration != previous_declaration);
+ if (previous_declaration != NULL
+ && previous_declaration->parent_context == context) {
+ /* can happen for K&R style declarations */
+ if(previous_declaration->type == NULL) {
+ previous_declaration->type = declaration->type;
+ }
+
+ const type_t *const prev_type = skip_typeref(previous_declaration->type);
+ if (!types_compatible(type, prev_type)) {
+ errorf(declaration->source_position,
+ "declaration '%#T' is incompatible with previous declaration '%#T'",
+ type, symbol, previous_declaration->type, symbol);
+ errorf(previous_declaration->source_position, "previous declaration of '%Y' was here", symbol);
+ } else {
+ unsigned old_storage_class = previous_declaration->storage_class;
+ unsigned new_storage_class = declaration->storage_class;
+
+ /* pretend no storage class means extern for function declarations
+ * (except if the previous declaration is neither none nor extern) */
+ if (is_type_function(type)) {
+ switch (old_storage_class) {
+ case STORAGE_CLASS_NONE:
+ old_storage_class = STORAGE_CLASS_EXTERN;
+
+ case STORAGE_CLASS_EXTERN:
+ if (new_storage_class == STORAGE_CLASS_NONE && !is_function_definition) {
+ new_storage_class = STORAGE_CLASS_EXTERN;
+ }
+ break;
+
+ default: break;
+ }
+ }
+
+ if (old_storage_class == STORAGE_CLASS_EXTERN &&
+ new_storage_class == STORAGE_CLASS_EXTERN) {
+warn_redundant_declaration:
+ warningf(declaration->source_position, "redundant declaration for '%Y'", symbol);
+ warningf(previous_declaration->source_position, "previous declaration of '%Y' was here", symbol);
+ } else if (current_function == NULL) {
+ if (old_storage_class != STORAGE_CLASS_STATIC &&
+ new_storage_class == STORAGE_CLASS_STATIC) {
+ errorf(declaration->source_position, "static declaration of '%Y' follows non-static declaration", symbol);
+ errorf(previous_declaration->source_position, "previous declaration of '%Y' was here", symbol);
+ } else {
+ if (old_storage_class != STORAGE_CLASS_EXTERN) {
+ goto warn_redundant_declaration;
+ }
+ if (new_storage_class == STORAGE_CLASS_NONE) {
+ previous_declaration->storage_class = STORAGE_CLASS_NONE;
+ }
+ }
+ } else {
+ if (old_storage_class == new_storage_class) {
+ errorf(declaration->source_position, "redeclaration of '%Y'", symbol);
+ } else {
+ errorf(declaration->source_position, "redeclaration of '%Y' with different linkage", symbol);
+ }
+ errorf(previous_declaration->source_position, "previous declaration of '%Y' was here", symbol);
+ }
+ }
+ return previous_declaration;
+ }
+
+ assert(declaration->parent_context == NULL);
+ assert(declaration->symbol != NULL);
+ assert(context != NULL);
+
+ declaration->parent_context = context;
+
+ environment_push(declaration);
+ return append_declaration(declaration);
+}
+
+static declaration_t *record_declaration(declaration_t *declaration)
+{
+ return internal_record_declaration(declaration, false);
+}
+
+static declaration_t *record_function_definition(declaration_t *const declaration)
+{
+ return internal_record_declaration(declaration, true);
+}
+
+static void parser_error_multiple_definition(declaration_t *declaration,
+ const source_position_t source_position)
+{
+ errorf(source_position, "multiple definition of symbol '%Y'",
+ declaration->symbol);
+ errorf(declaration->source_position,
+ "this is the location of the previous definition.");
+}
+
+static bool is_declaration_specifier(const token_t *token,
+ bool only_type_specifiers)
+{
+ switch(token->type) {
+ TYPE_SPECIFIERS
+ return true;
+ case T_IDENTIFIER:
+ return is_typedef_symbol(token->v.symbol);
+
+ case T___extension__:
+ STORAGE_CLASSES
+ TYPE_QUALIFIERS
+ return !only_type_specifiers;
+
+ default:
+ return false;
+ }
+}
+
+static void parse_init_declarator_rest(declaration_t *declaration)
+{
+ eat('=');
+
+ type_t *orig_type = declaration->type;
+ type_t *type = NULL;
+ if(orig_type != NULL)
+ type = skip_typeref(orig_type);
+
+ if(declaration->init.initializer != NULL) {
+ parser_error_multiple_definition(declaration, token.source_position);
+ }
+
+ initializer_t *initializer = parse_initializer(type);
+
+ /* § 6.7.5 (22) array initializers for arrays with unknown size determine
+ * the array type size */
+ if(type != NULL && is_type_array(type) && initializer != NULL) {
+ array_type_t *array_type = &type->array;
+
+ if(array_type->size == NULL) {
+ expression_t *cnst = allocate_expression_zero(EXPR_CONST);
+
+ cnst->base.datatype = type_size_t;
+
+ switch (initializer->kind) {
+ case INITIALIZER_LIST: {
+ initializer_list_t *const list = &initializer->list;
+ cnst->conste.v.int_value = list->len;
+ break;
+ }
+
+ case INITIALIZER_STRING: {
+ initializer_string_t *const string = &initializer->string;
+ cnst->conste.v.int_value = strlen(string->string) + 1;
+ break;
+ }
+
+ case INITIALIZER_WIDE_STRING: {
+ initializer_wide_string_t *const string = &initializer->wide_string;
+ cnst->conste.v.int_value = string->string.size;
+ break;
+ }
+
+ default:
+ panic("invalid initializer type");
+ }
+
+ array_type->size = cnst;
+ }
+ }
+
+ if(type != NULL && is_type_function(type)) {
+ errorf(declaration->source_position,
+ "initializers not allowed for function types at declator '%Y' (type '%T')",
+ declaration->symbol, orig_type);
+ } else {
+ declaration->init.initializer = initializer;
+ }
+}
+
+/* parse rest of a declaration without any declarator */
+static void parse_anonymous_declaration_rest(
+ const declaration_specifiers_t *specifiers,
+ parsed_declaration_func finished_declaration)