fix incorrect type size for wide string literals
[cparser] / parser.c
index 4d1b44b..88ad628 100644 (file)
--- a/parser.c
+++ b/parser.c
@@ -1,6 +1,6 @@
 /*
  * This file is part of cparser.
- * Copyright (C) 2007-2008 Matthias Braun <matze@braunis.de>
+ * Copyright (C) 2007-2009 Matthias Braun <matze@braunis.de>
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
@@ -114,7 +114,7 @@ static declaration_t      **incomplete_arrays;
 #define POP_PARENT ((void)(current_parent = prev_parent))
 
 /** special symbol used for anonymous entities. */
-static const symbol_t *sym_anonymous = NULL;
+static symbol_t *sym_anonymous = NULL;
 
 /** The token anchor set */
 static unsigned char token_anchor_set[T_LAST_TOKEN];
@@ -569,6 +569,16 @@ static inline void next_token(void)
 #endif
 }
 
+static inline bool next_if(int const type)
+{
+       if (token.type == type) {
+               next_token();
+               return true;
+       } else {
+               return false;
+       }
+}
+
 /**
  * Return the next token with a given lookahead.
  */
@@ -702,8 +712,7 @@ static void eat_until_anchor(void)
 static void eat_block(void)
 {
        eat_until_matching_token('{');
-       if (token.type == '}')
-               next_token();
+       next_if('}');
 }
 
 #define eat(token_type) (assert(token.type == (token_type)), next_token())
@@ -747,8 +756,7 @@ static void type_error_incompatible(const char *msg,
                        parse_error_expected(NULL, (expected), NULL); \
                        add_anchor_token(expected);                   \
                        eat_until_anchor();                           \
-                       if (token.type == expected)                   \
-                               next_token();                             \
+                       next_if((expected));                          \
                        rem_anchor_token(expected);                   \
                        goto error_label;                             \
                }                                                 \
@@ -796,7 +804,7 @@ static entity_t *get_entity(const symbol_t *const symbol,
 /* §6.2.3:1 24)  There is only one name space for tags even though three are
  * possible. */
 static entity_t *get_tag(symbol_t const *const symbol,
-               entity_kind_tag_t const kind)
+                         entity_kind_tag_t const kind)
 {
        entity_t *entity = get_entity(symbol, NAMESPACE_TAG);
        if (entity != NULL && entity->kind != kind) {
@@ -1242,12 +1250,9 @@ static attribute_t *allocate_attribute_zero(attribute_kind_t kind)
  */
 static attribute_argument_t *parse_attribute_arguments(void)
 {
-       if (token.type == ')')
-               return NULL;
-
-       attribute_argument_t *first = NULL;
-       attribute_argument_t *last  = NULL;
-       while (true) {
+       attribute_argument_t  *first  = NULL;
+       attribute_argument_t **anchor = &first;
+       if (token.type != ')') do {
                attribute_argument_t *argument = allocate_ast_zero(sizeof(*argument));
 
                /* is it an identifier */
@@ -1266,20 +1271,10 @@ static attribute_argument_t *parse_attribute_arguments(void)
                }
 
                /* append argument */
-               if (last == NULL) {
-                       first = argument;
-               } else {
-                       last->next = argument;
-               }
-               last = argument;
-
-               if (token.type == ',') {
-                       next_token();
-                       continue;
-               }
-               expect(')', end_error);
-               break;
-       }
+               *anchor = argument;
+               anchor  = &argument->next;
+       } while (next_if(','));
+       expect(')', end_error);
 
        return first;
 
@@ -1373,10 +1368,8 @@ static attribute_t *parse_attribute_gnu_single(void)
        attribute_t *attribute = allocate_attribute_zero(kind);
 
        /* parse arguments */
-       if (token.type == '(') {
-               next_token();
+       if (next_if('('))
                attribute->a.arguments = parse_attribute_arguments();
-       }
 
        return attribute;
 
@@ -1386,37 +1379,22 @@ end_error:
 
 static attribute_t *parse_attribute_gnu(void)
 {
-       attribute_t *first = NULL;
-       attribute_t *last  = NULL;
+       attribute_t  *first  = NULL;
+       attribute_t **anchor = &first;
 
        eat(T___attribute__);
        expect('(', end_error);
        expect('(', end_error);
 
-       if (token.type == ')') {
-               next_token();
-               expect(')', end_error);
-               return first;
-       }
-
-       while (true) {
+       if (token.type != ')') do {
                attribute_t *attribute = parse_attribute_gnu_single();
                if (attribute == NULL)
                        goto end_error;
 
-               if (last == NULL) {
-                       first = attribute;
-               } else {
-                       last->next = attribute;
-               }
-               last = attribute;
-
-               if (token.type == ')') {
-                       next_token();
-                       break;
-               }
-               expect(',', end_error);
-       }
+               *anchor = attribute;
+               anchor  = &attribute->next;
+       } while (next_if(','));
+       expect(')', end_error);
        expect(')', end_error);
 
 end_error:
@@ -1426,12 +1404,10 @@ end_error:
 /** Parse attributes. */
 static attribute_t *parse_attributes(attribute_t *first)
 {
-       attribute_t *last = first;
-       while (true) {
-               if (last != NULL) {
-                       while (last->next != NULL)
-                               last = last->next;
-               }
+       attribute_t **anchor = &first;
+       for (;;) {
+               while (*anchor != NULL)
+                       anchor = &(*anchor)->next;
 
                attribute_t *attribute;
                switch (token.type) {
@@ -1475,12 +1451,8 @@ static attribute_t *parse_attributes(attribute_t *first)
                        return first;
                }
 
-               if (last == NULL) {
-                       first = attribute;
-               } else {
-                       last->next = attribute;
-               }
-               last = attribute;
+               *anchor = attribute;
+               anchor  = &attribute->next;
        }
 }
 
@@ -1721,10 +1693,10 @@ unary:
 
 static designator_t *parse_designation(void)
 {
-       designator_t *result = NULL;
-       designator_t *last   = NULL;
+       designator_t  *result = NULL;
+       designator_t **anchor = &result;
 
-       while (true) {
+       for (;;) {
                designator_t *designator;
                switch (token.type) {
                case '[':
@@ -1754,12 +1726,8 @@ static designator_t *parse_designation(void)
                }
 
                assert(designator != NULL);
-               if (last != NULL) {
-                       last->next = designator;
-               } else {
-                       result = designator;
-               }
-               last = designator;
+               *anchor = designator;
+               anchor  = &designator->next;
        }
 end_error:
        return NULL;
@@ -1840,11 +1808,6 @@ static initializer_t *initializer_from_expression(type_t *orig_type,
                            &expression->base.source_position);
 
        initializer_t *const result = allocate_initializer_zero(INITIALIZER_VALUE);
-#if 0
-       if (type->kind == TYPE_BITFIELD) {
-               type = type->bitfield.base_type;
-       }
-#endif
        result->value.value = create_implicit_cast(expression, type);
 
        return result;
@@ -1869,20 +1832,19 @@ static initializer_t *parse_scalar_initializer(type_t *type,
 {
        /* there might be extra {} hierarchies */
        int braces = 0;
-       if (token.type == '{') {
+       if (next_if('{')) {
                if (warning.other)
                        warningf(HERE, "extra curly braces around scalar initializer");
                do {
                        ++braces;
-                       next_token();
-               } while (token.type == '{');
+               } while (next_if('{'));
        }
 
        expression_t *expression = parse_assignment_expression();
        mark_vars_read(expression, NULL);
        if (must_be_constant && !is_initializer_constant(expression)) {
                errorf(&expression->base.source_position,
-                      "Initialisation expression '%E' is not constant",
+                      "initialisation expression '%E' is not constant",
                       expression);
        }
 
@@ -1898,9 +1860,7 @@ static initializer_t *parse_scalar_initializer(type_t *type,
 
        bool additional_warning_displayed = false;
        while (braces > 0) {
-               if (token.type == ',') {
-                       next_token();
-               }
+               next_if(',');
                if (token.type != '}') {
                        if (!additional_warning_displayed && warning.other) {
                                warningf(HERE, "additional elements in scalar initializer");
@@ -2198,8 +2158,7 @@ static void advance_current_object(type_path_t *path, size_t top_path_level)
  */
 static void skip_initializers(void)
 {
-       if (token.type == '{')
-               next_token();
+       next_if('{');
 
        while (token.type != '}') {
                if (token.type == T_EOF)
@@ -2316,6 +2275,8 @@ finish_designator:
 
                        if (type == NULL) {
                                /* we are already outside, ... */
+                               if (outer_type == NULL)
+                                       goto error_parse_next;
                                type_t *const outer_type_skip = skip_typeref(outer_type);
                                if (is_type_compound(outer_type_skip) &&
                                    !outer_type_skip->compound.compound->complete) {
@@ -2330,9 +2291,7 @@ finish_designator:
                                        && outer_type != NULL) {
                                sub = initializer_from_expression(outer_type, expression);
                                if (sub != NULL) {
-                                       if (token.type == ',') {
-                                               next_token();
-                                       }
+                                       next_if(',');
                                        if (token.type != '}' && warning.other) {
                                                warningf(HERE, "excessive elements in initializer for type '%T'",
                                                                 orig_type);
@@ -2382,10 +2341,10 @@ finish_designator:
 error_excess:
                        if (warning.other) {
                                if (env->entity != NULL) {
-                                       warningf(HERE, "excess elements in struct initializer for '%Y'",
-                                          env->entity->base.symbol);
+                                       warningf(HERE, "excess elements in initializer for '%Y'",
+                                                env->entity->base.symbol);
                                } else {
-                                       warningf(HERE, "excess elements in struct initializer");
+                                       warningf(HERE, "excess elements in initializer");
                                }
                        }
                }
@@ -2526,11 +2485,7 @@ static void append_entity(scope_t *scope, entity_t *entity)
 
 static compound_t *parse_compound_type_specifier(bool is_struct)
 {
-       if (is_struct) {
-               eat(T_struct);
-       } else {
-               eat(T_union);
-       }
+       eat(is_struct ? T_struct : T_union);
 
        symbol_t    *symbol   = NULL;
        compound_t  *compound = NULL;
@@ -2579,6 +2534,7 @@ static compound_t *parse_compound_type_specifier(bool is_struct)
                entity_t *entity = allocate_entity_zero(kind);
                compound         = &entity->compound;
 
+               compound->alignment            = 1;
                compound->base.namespc         = NAMESPACE_TAG;
                compound->base.source_position = token.source_position;
                compound->base.symbol          = symbol;
@@ -2631,8 +2587,7 @@ static void parse_enum_entries(type_t *const enum_type)
                entity->base.source_position = token.source_position;
                next_token();
 
-               if (token.type == '=') {
-                       next_token();
+               if (next_if('=')) {
                        expression_t *value = parse_constant_expression();
 
                        value = create_implicit_cast(value, enum_type);
@@ -2642,11 +2597,7 @@ static void parse_enum_entries(type_t *const enum_type)
                }
 
                record_entity(entity, false);
-
-               if (token.type != ',')
-                       break;
-               next_token();
-       } while (token.type != '}');
+       } while (next_if(',') && token.type != '}');
        rem_anchor_token('}');
 
        expect('}', end_error);
@@ -2657,33 +2608,38 @@ end_error:
 
 static type_t *parse_enum_specifier(void)
 {
-       entity_t        *entity;
-       symbol_t        *symbol;
+       entity_t *entity;
+       symbol_t *symbol;
 
        eat(T_enum);
-       if (token.type == T_IDENTIFIER) {
-               symbol = token.v.symbol;
-               next_token();
+       switch (token.type) {
+               case T_IDENTIFIER:
+                       symbol = token.v.symbol;
+                       next_token();
 
-               entity = get_tag(symbol, ENTITY_ENUM);
-               if (entity != NULL) {
-                       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);
+                       entity = get_tag(symbol, ENTITY_ENUM);
+                       if (entity != NULL) {
+                               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);
-               return NULL;
-       } else {
-               entity  = NULL;
-               symbol  = NULL;
+                       break;
+
+               case '{':
+                       entity = NULL;
+                       symbol = NULL;
+                       break;
+
+               default:
+                       parse_error_expected("while parsing enum type specifier",
+                                       T_IDENTIFIER, '{', NULL);
+                       return NULL;
        }
 
        if (entity == NULL) {
@@ -2745,9 +2701,8 @@ static type_t *parse_typeof(void)
        bool old_gcc_extension = in_gcc_extension;
        in_type_prop           = true;
 
-       while (token.type == T___extension__) {
+       while (next_if(T___extension__)) {
                /* This can be a prefix to a typename or an expression. */
-               next_token();
                in_gcc_extension = true;
        }
        switch (token.type) {
@@ -2834,7 +2789,7 @@ static attribute_t *parse_attribute_ms_property(attribute_t *attribute)
        attribute_property_argument_t *property
                = allocate_ast_zero(sizeof(*property));
 
-       while (true) {
+       do {
                if (token.type != T_IDENTIFIER) {
                        parse_error_expected("while parsing property declspec",
                                             T_IDENTIFIER, NULL);
@@ -2864,10 +2819,7 @@ static attribute_t *parse_attribute_ms_property(attribute_t *attribute)
                        property->get_symbol = token.v.symbol;
                }
                next_token();
-               if (token.type == ')')
-                       break;
-               expect(',', end_error);
-       }
+       } while (next_if(','));
 
        attribute->a.property = property;
 
@@ -2880,9 +2832,8 @@ end_error:
 static attribute_t *parse_microsoft_extended_decl_modifier_single(void)
 {
        attribute_kind_t kind = ATTRIBUTE_UNKNOWN;
-       if (token.type == T_restrict) {
+       if (next_if(T_restrict)) {
                kind = ATTRIBUTE_MS_RESTRICT;
-               next_token();
        } else if (token.type == T_IDENTIFIER) {
                const char *name = token.v.symbol->string;
                next_token();
@@ -2910,10 +2861,8 @@ static attribute_t *parse_microsoft_extended_decl_modifier_single(void)
        }
 
        /* parse arguments */
-       if (token.type == '(') {
-               next_token();
+       if (next_if('('))
                attribute->a.arguments = parse_attribute_arguments();
-       }
 
        return attribute;
 }
@@ -2924,37 +2873,24 @@ static attribute_t *parse_microsoft_extended_decl_modifier(attribute_t *first)
 
        expect('(', end_error);
 
-       if (token.type == ')') {
-               next_token();
+       if (next_if(')'))
                return NULL;
-       }
 
        add_anchor_token(')');
 
-       attribute_t *last = first;
-       while (true) {
-               if (last != NULL) {
-                       while (last->next != NULL)
-                               last = last->next;
-               }
+       attribute_t **anchor = &first;
+       do {
+               while (*anchor != NULL)
+                       anchor = &(*anchor)->next;
 
                attribute_t *attribute
                        = parse_microsoft_extended_decl_modifier_single();
                if (attribute == NULL)
                        goto end_error;
 
-               if (last == NULL) {
-                       first = attribute;
-               } else {
-                       last->next = attribute;
-               }
-               last = attribute;
-
-               if (token.type == ')') {
-                       break;
-               }
-               expect(',', end_error);
-       }
+               *anchor = attribute;
+               anchor  = &attribute->next;
+       } while (next_if(','));
 
        rem_anchor_token(')');
        expect(')', end_error);
@@ -3416,7 +3352,6 @@ warn_about_long_long:
 
 end_error:
        specifiers->type = type_error_type;
-       return;
 }
 
 static type_qualifiers_t parse_type_qualifiers(void)
@@ -3457,12 +3392,7 @@ static void parse_identifier_list(scope_t *scope)
 
                if (scope != NULL)
                        append_entity(scope, entity);
-
-               if (token.type != ',') {
-                       break;
-               }
-               next_token();
-       } while (token.type == T_IDENTIFIER);
+       } while (next_if(',') && token.type == T_IDENTIFIER);
 }
 
 static entity_t *parse_parameter(void)
@@ -3528,8 +3458,7 @@ static void parse_parameters(function_type_t *type, scope_t *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->unspecified_parameters = true;
+                       type->kr_style_parameters = true;
                        parse_identifier_list(scope);
                        goto parameters_finished;
                }
@@ -3544,7 +3473,7 @@ static void parse_parameters(function_type_t *type, scope_t *scope)
 
        if (has_parameters()) {
                function_parameter_t **anchor = &type->parameters;
-               for (;;) {
+               do {
                        switch (token.type) {
                        case T_DOTDOTDOT:
                                next_token();
@@ -3580,11 +3509,7 @@ static void parse_parameters(function_type_t *type, scope_t *scope)
                        default:
                                goto parameters_finished;
                        }
-                       if (token.type != ',') {
-                               goto parameters_finished;
-                       }
-                       next_token();
-               }
+               } while (next_if(','));
        }
 
 
@@ -3678,18 +3603,12 @@ static construct_type_t *parse_array_declarator(void)
        memset(array, 0, sizeof(*array));
        cons->kind = CONSTRUCT_ARRAY;
 
-       if (token.type == T_static) {
+       if (next_if(T_static))
                array->is_static = true;
-               next_token();
-       }
 
        type_qualifiers_t type_qualifiers = parse_type_qualifiers();
-       if (type_qualifiers != 0) {
-               if (token.type == T_static) {
+       if (type_qualifiers != 0 && next_if(T_static))
                        array->is_static = true;
-                       next_token();
-               }
-       }
        array->type_qualifiers = type_qualifiers;
 
        if (token.type == '*' && look_ahead(1)->type == ']') {
@@ -3723,21 +3642,8 @@ static construct_type_t *parse_function_declarator(scope_t *scope)
        type_t          *type  = allocate_type_zero(TYPE_FUNCTION);
        function_type_t *ftype = &type->function;
 
-       ftype->linkage = current_linkage;
-
-#if 0
-       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;
-
-               default:
-                       errorf(HERE, "multiple calling conventions in declaration");
-                       break;
-       }
-#endif
+       ftype->linkage            = current_linkage;
+       ftype->calling_convention = CC_DEFAULT;
 
        parse_parameters(ftype, scope);
 
@@ -4057,15 +3963,11 @@ static entity_t *parse_declarator(const declaration_specifiers_t *specifiers,
 
        attribute_t *attributes = parse_attributes(env.attributes);
        /* append (shared) specifier attribute behind attributes of this
-          declarator */
-       if (attributes != NULL) {
-               attribute_t *last = attributes;
-               while (last->next != NULL)
-                       last = last->next;
-               last->next = specifiers->attributes;
-       } else {
-               attributes = specifiers->attributes;
-       }
+        * declarator */
+       attribute_t **anchor = &attributes;
+       while (*anchor != NULL)
+               anchor = &(*anchor)->next;
+       *anchor = specifiers->attributes;
 
        entity_t *entity;
        if (specifiers->storage_class == STORAGE_CLASS_TYPEDEF) {
@@ -4176,7 +4078,7 @@ static entity_t *parse_declarator(const declaration_specifiers_t *specifiers,
                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_function != NULL)
                        storage_class = STORAGE_CLASS_AUTO;
                entity->declaration.storage_class = storage_class;
        }
@@ -4216,7 +4118,7 @@ static type_t *parse_abstract_declarator(type_t *base_type)
  * @param decl    the declaration to check
  * @param type    the function type of the declaration
  */
-static void check_type_of_main(const entity_t *entity)
+static void check_main(const entity_t *entity)
 {
        const source_position_t *pos = &entity->base.source_position;
        if (entity->kind != ENTITY_FUNCTION) {
@@ -4293,6 +4195,47 @@ static bool is_error_entity(entity_t *const ent)
        return false;
 }
 
+static bool contains_attribute(const attribute_t *list, const attribute_t *attr)
+{
+       for (const attribute_t *tattr = list; tattr != NULL; tattr = tattr->next) {
+               if (attributes_equal(tattr, attr))
+                       return true;
+       }
+       return false;
+}
+
+/**
+ * test wether new_list contains any attributes not included in old_list
+ */
+static bool has_new_attributes(const attribute_t *old_list,
+                               const attribute_t *new_list)
+{
+       for (const attribute_t *attr = new_list; attr != NULL; attr = attr->next) {
+               if (!contains_attribute(old_list, attr))
+                       return true;
+       }
+       return false;
+}
+
+/**
+ * Merge in attributes from an attribute list (probably from a previous
+ * declaration with the same name). Warning: destroys the old structure
+ * of the attribute list - don't reuse attributes after this call.
+ */
+static void merge_in_attributes(declaration_t *decl, attribute_t *attributes)
+{
+       attribute_t *next;
+       for (attribute_t *attr = attributes; attr != NULL; attr = next) {
+               next = attr->next;
+               if (contains_attribute(decl->attributes, attr))
+                       continue;
+
+               /* move attribute to new declarations attributes list */
+               attr->next       = decl->attributes;
+               decl->attributes = attr;
+       }
+}
+
 /**
  * record entities for the NAMESPACE_NORMAL, and produce error messages/warnings
  * for various problems that occur for multiple definitions
@@ -4325,7 +4268,7 @@ static entity_t *record_entity(entity_t *entity, const bool is_definition)
 
                if (warning.main && current_scope == file_scope
                                && is_sym_main(symbol)) {
-                       check_type_of_main(entity);
+                       check_main(entity);
                }
        }
 
@@ -4398,6 +4341,7 @@ static entity_t *record_entity(entity_t *entity, const bool is_definition)
                                                &previous_entity->base.source_position);
                        } else {
                                unsigned old_storage_class = prev_decl->storage_class;
+
                                if (warning.redundant_decls               &&
                                                is_definition                     &&
                                                !prev_decl->used                  &&
@@ -4446,8 +4390,14 @@ static entity_t *record_entity(entity_t *entity, const bool is_definition)
 
                                if (old_storage_class == STORAGE_CLASS_EXTERN &&
                                                new_storage_class == STORAGE_CLASS_EXTERN) {
-warn_redundant_declaration:
-                                       if (!is_definition           &&
+
+warn_redundant_declaration: ;
+                                       bool has_new_attrs
+                                               = has_new_attributes(prev_decl->attributes,
+                                                                    decl->attributes);
+                                       if (has_new_attrs) {
+                                               merge_in_attributes(decl, prev_decl->attributes);
+                                       } else if (!is_definition        &&
                                                        warning.redundant_decls  &&
                                                        is_type_valid(prev_type) &&
                                                        strcmp(previous_entity->base.source_position.input_name,
@@ -4695,9 +4645,8 @@ static void parse_declaration_rest(entity_t *ndeclaration,
 
                check_variable_type_complete(entity);
 
-               if (token.type != ',')
+               if (!next_if(','))
                        break;
-               eat(',');
 
                add_anchor_token('=');
                ndeclaration = parse_declarator(specifiers, flags);
@@ -4778,7 +4727,6 @@ static void parse_kr_declaration_list(entity_t *entity)
        if (!type->function.kr_style_parameters)
                return;
 
-
        add_anchor_token('{');
 
        /* push function parameters */
@@ -4820,8 +4768,30 @@ decl_list_end:
        function_parameter_t  *parameters = NULL;
        function_parameter_t **anchor     = &parameters;
 
+       /* did we have an earlier prototype? */
+       entity_t *proto_type = get_entity(entity->base.symbol, NAMESPACE_NORMAL);
+       if (proto_type != NULL && proto_type->kind != ENTITY_FUNCTION)
+               proto_type = NULL;
+
+       function_parameter_t *proto_parameter = NULL;
+       if (proto_type != NULL) {
+               type_t *proto_type_type = proto_type->declaration.type;
+               proto_parameter         = proto_type_type->function.parameters;
+               /* If a K&R function definition has a variadic prototype earlier, then
+                * make the function definition variadic, too. This should conform to
+                * §6.7.5.3:15 and §6.9.1:8. */
+               new_type->function.variadic = proto_type_type->function.variadic;
+       } else {
+               /* §6.9.1.7: A K&R style parameter list does NOT act as a function
+                * prototype */
+               new_type->function.unspecified_parameters = true;
+       }
+
+       bool need_incompatible_warning = false;
        parameter = entity->function.parameters.entities;
-       for (; parameter != NULL; parameter = parameter->base.next) {
+       for (; parameter != NULL; parameter = parameter->base.next,
+                       proto_parameter =
+                               proto_parameter == NULL ? NULL : proto_parameter->next) {
                if (parameter->kind != ENTITY_PARAMETER)
                        continue;
 
@@ -4843,25 +4813,42 @@ decl_list_end:
 
                semantic_parameter_incomplete(parameter);
 
-               /*
-                * we need the default promoted types for the function type
-                */
-               parameter_type = get_default_promoted_type(parameter_type);
-
-               function_parameter_t *const parameter =
-                       allocate_parameter(parameter_type);
+               /* we need the default promoted types for the function type */
+               type_t *not_promoted = parameter_type;
+               parameter_type       = get_default_promoted_type(parameter_type);
+
+               /* gcc special: if the type of the prototype matches the unpromoted
+                * type don't promote */
+               if (!strict_mode && proto_parameter != NULL) {
+                       type_t *proto_p_type = skip_typeref(proto_parameter->type);
+                       type_t *promo_skip   = skip_typeref(parameter_type);
+                       type_t *param_skip   = skip_typeref(not_promoted);
+                       if (!types_compatible(proto_p_type, promo_skip)
+                               && types_compatible(proto_p_type, param_skip)) {
+                               /* don't promote */
+                               need_incompatible_warning = true;
+                               parameter_type = not_promoted;
+                       }
+               }
+               function_parameter_t *const parameter
+                       = allocate_parameter(parameter_type);
 
                *anchor = parameter;
                anchor  = &parameter->next;
        }
 
-       /* §6.9.1.7: A K&R style parameter list does NOT act as a function
-        * prototype */
-       new_type->function.parameters             = parameters;
-       new_type->function.unspecified_parameters = true;
-
+       new_type->function.parameters = parameters;
        new_type = identify_new_type(new_type);
 
+       if (warning.other && need_incompatible_warning) {
+               type_t *proto_type_type = proto_type->declaration.type;
+               warningf(HERE,
+                        "declaration '%#T' is incompatible with '%#T' (declared %P)",
+                        proto_type_type, proto_type->base.symbol,
+                        new_type, entity->base.symbol,
+                        &proto_type->base.source_position);
+       }
+
        entity->declaration.type = new_type;
 
        rem_anchor_token('{');
@@ -5248,9 +5235,8 @@ static void check_reachable(statement_t *const stmt)
                        break;
                }
 
-               case STATEMENT_CONTINUE: {
-                       statement_t *parent = stmt;
-                       for (;;) {
+               case STATEMENT_CONTINUE:
+                       for (statement_t *parent = stmt;;) {
                                parent = parent->base.parent;
                                if (parent == NULL) /* continue not within loop */
                                        return;
@@ -5264,11 +5250,9 @@ static void check_reachable(statement_t *const stmt)
                                        default: break;
                                }
                        }
-               }
 
-               case STATEMENT_BREAK: {
-                       statement_t *parent = stmt;
-                       for (;;) {
+               case STATEMENT_BREAK:
+                       for (statement_t *parent = stmt;;) {
                                parent = parent->base.parent;
                                if (parent == NULL) /* break not within loop/switch */
                                        return;
@@ -5287,7 +5271,6 @@ static void check_reachable(statement_t *const stmt)
                        }
 found_break_parent:
                        break;
-               }
 
                case STATEMENT_GOTO:
                        if (stmt->gotos.expression) {
@@ -5683,8 +5666,7 @@ static void parse_external_declaration(void)
        /* §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->function.parameters == NULL) {
                type_t *copy                          = duplicate_type(type);
                copy->function.unspecified_parameters = false;
                type                                  = identify_new_type(copy);
@@ -5929,7 +5911,7 @@ static expression_t *find_create_select(const source_position_t *pos,
 static void parse_compound_declarators(compound_t *compound,
                const declaration_specifiers_t *specifiers)
 {
-       while (true) {
+       do {
                entity_t *entity;
 
                if (token.type == ':') {
@@ -5942,15 +5924,11 @@ static void parse_compound_declarators(compound_t *compound,
                        type_t *type = make_bitfield_type(base_type, size,
                                        &source_position, NULL);
 
-                       attribute_t *attributes = parse_attributes(NULL);
-                       if (attributes != NULL) {
-                               attribute_t *last = attributes;
-                               while (last->next != NULL)
-                                       last = last->next;
-                               last->next = specifiers->attributes;
-                       } else {
-                               attributes = specifiers->attributes;
-                       }
+                       attribute_t  *attributes = parse_attributes(NULL);
+                       attribute_t **anchor     = &attributes;
+                       while (*anchor != NULL)
+                               anchor = &(*anchor)->next;
+                       *anchor = specifiers->attributes;
 
                        entity = allocate_entity_zero(ENTITY_COMPOUND_MEMBER);
                        entity->base.namespc                       = NAMESPACE_NORMAL;
@@ -6018,11 +5996,7 @@ static void parse_compound_declarators(compound_t *compound,
                                append_entity(&compound->members, entity);
                        }
                }
-
-               if (token.type != ',')
-                       break;
-               next_token();
-       }
+       } while (next_if(','));
        expect(';', end_error);
 
 end_error:
@@ -6250,10 +6224,12 @@ static entity_t *create_implicit_function(symbol_t *symbol,
        entity->base.symbol                        = symbol;
        entity->base.source_position               = *source_position;
 
-       bool strict_prototypes_old = warning.strict_prototypes;
-       warning.strict_prototypes  = false;
-       record_entity(entity, false);
-       warning.strict_prototypes = strict_prototypes_old;
+       if (current_scope != NULL) {
+               bool strict_prototypes_old = warning.strict_prototypes;
+               warning.strict_prototypes  = false;
+               record_entity(entity, false);
+               warning.strict_prototypes = strict_prototypes_old;
+       }
 
        return entity;
 }
@@ -6294,11 +6270,23 @@ static type_t *make_function_1_type(type_t *return_type, type_t *argument_type)
        return identify_new_type(type);
 }
 
+/**
+ * Creates a return_type (func)(argument_type, ...) function type if not
+ * already exists.
+ *
+ * @param return_type    the return type
+ * @param argument_type  the argument type
+ */
 static type_t *make_function_1_type_variadic(type_t *return_type, type_t *argument_type)
 {
-       type_t *res = make_function_1_type(return_type, argument_type);
-       res->function.variadic = 1;
-       return res;
+       function_parameter_t *const parameter = allocate_parameter(argument_type);
+
+       type_t *type               = allocate_type_zero(TYPE_FUNCTION);
+       type->function.return_type = return_type;
+       type->function.parameters  = parameter;
+       type->function.variadic    = true;
+
+       return identify_new_type(type);
 }
 
 /**
@@ -6414,30 +6402,100 @@ type_t *revert_automatic_type_conversion(const expression_t *expression)
        }
 }
 
-static expression_t *parse_reference(void)
+/**
+ * Find an entity matching a symbol in a scope.
+ * Uses current scope if scope is NULL
+ */
+static entity_t *lookup_entity(const scope_t *scope, symbol_t *symbol,
+                               namespace_tag_t namespc)
 {
-       symbol_t *const symbol = token.v.symbol;
+       if (scope == NULL) {
+               return get_entity(symbol, namespc);
+       }
 
-       entity_t *entity = get_entity(symbol, NAMESPACE_NORMAL);
+       /* we should optimize here, if scope grows above a certain size we should
+          construct a hashmap here... */
+       entity_t *entity = scope->entities;
+       for ( ; entity != NULL; entity = entity->base.next) {
+               if (entity->base.symbol == symbol && entity->base.namespc == namespc)
+                       break;
+       }
+
+       return entity;
+}
+
+static entity_t *parse_qualified_identifier(void)
+{
+       /* namespace containing the symbol */
+       symbol_t          *symbol;
+       source_position_t  pos;
+       const scope_t     *lookup_scope = NULL;
+
+       if (next_if(T_COLONCOLON))
+               lookup_scope = &unit->scope;
+
+       entity_t *entity;
+       while (true) {
+               if (token.type != T_IDENTIFIER) {
+                       parse_error_expected("while parsing identifier", T_IDENTIFIER, NULL);
+                       return create_error_entity(sym_anonymous, ENTITY_VARIABLE);
+               }
+               symbol = token.v.symbol;
+               pos    = *HERE;
+               next_token();
+
+               /* lookup entity */
+               entity = lookup_entity(lookup_scope, symbol, NAMESPACE_NORMAL);
+
+               if (!next_if(T_COLONCOLON))
+                       break;
+
+               switch (entity->kind) {
+               case ENTITY_NAMESPACE:
+                       lookup_scope = &entity->namespacee.members;
+                       break;
+               case ENTITY_STRUCT:
+               case ENTITY_UNION:
+               case ENTITY_CLASS:
+                       lookup_scope = &entity->compound.members;
+                       break;
+               default:
+                       errorf(&pos, "'%Y' must be a namespace, class, struct or union (but is a %s)",
+                              symbol, get_entity_kind_name(entity->kind));
+                       goto end_error;
+               }
+       }
 
        if (entity == NULL) {
-               if (!strict_mode && look_ahead(1)->type == '(') {
+               if (!strict_mode && token.type == '(') {
                        /* an implicitly declared function */
                        if (warning.error_implicit_function_declaration) {
-                               errorf(HERE, "implicit declaration of function '%Y'", symbol);
+                               errorf(&pos, "implicit declaration of function '%Y'", symbol);
                        } else if (warning.implicit_function_declaration) {
-                               warningf(HERE, "implicit declaration of function '%Y'", symbol);
+                               warningf(&pos, "implicit declaration of function '%Y'", symbol);
                        }
 
-                       entity = create_implicit_function(symbol, HERE);
+                       entity = create_implicit_function(symbol, &pos);
                } else {
-                       errorf(HERE, "unknown identifier '%Y' found.", symbol);
+                       errorf(&pos, "unknown identifier '%Y' found.", symbol);
                        entity = create_error_entity(symbol, ENTITY_VARIABLE);
                }
        }
 
-       type_t *orig_type;
+       return entity;
+
+end_error:
+       /* skip further qualifications */
+       while (next_if(T_IDENTIFIER) && next_if(T_COLONCOLON)) {}
+
+       return create_error_entity(sym_anonymous, ENTITY_VARIABLE);
+}
 
+static expression_t *parse_reference(void)
+{
+       entity_t *entity = parse_qualified_identifier();
+
+       type_t *orig_type;
        if (is_declaration(entity)) {
                orig_type = entity->declaration.type;
        } else if (entity->kind == ENTITY_ENUM_VALUE) {
@@ -6464,8 +6522,9 @@ static expression_t *parse_reference(void)
        }
 
        if (entity->base.parent_scope != file_scope
-               && (current_function != NULL && entity->base.parent_scope->depth < current_function->parameters.depth)
-               && is_type_valid(orig_type) && !is_type_function(orig_type)) {
+               && (current_function != NULL
+                       && entity->base.parent_scope->depth < current_function->parameters.depth)
+               && (entity->kind == ENTITY_VARIABLE || entity->kind == ENTITY_PARAMETER)) {
                if (entity->kind == ENTITY_VARIABLE) {
                        /* access of a variable from an outer function */
                        entity->variable.address_taken = true;
@@ -6484,7 +6543,6 @@ static expression_t *parse_reference(void)
                         entity->declaration.type, entity->base.symbol);
        }
 
-       next_token();
        return expression;
 }
 
@@ -6731,8 +6789,7 @@ static designator_t *parse_designator(void)
 
        designator_t *last_designator = result;
        while (true) {
-               if (token.type == '.') {
-                       next_token();
+               if (next_if('.')) {
                        if (token.type != T_IDENTIFIER) {
                                parse_error_expected("while parsing member designator",
                                                     T_IDENTIFIER, NULL);
@@ -6747,8 +6804,7 @@ static designator_t *parse_designator(void)
                        last_designator       = designator;
                        continue;
                }
-               if (token.type == '[') {
-                       next_token();
+               if (next_if('[')) {
                        add_anchor_token(']');
                        designator_t *designator    = allocate_ast_zero(sizeof(result[0]));
                        designator->source_position = *HERE;
@@ -6829,9 +6885,12 @@ static expression_t *parse_va_start(void)
        expression_t *const expr = parse_assignment_expression();
        if (expr->kind == EXPR_REFERENCE) {
                entity_t *const entity = expr->reference.entity;
-               if (entity->base.parent_scope != &current_function->parameters
-                               || entity->base.next != NULL
-                               || entity->kind != ENTITY_PARAMETER) {
+               if (!current_function->base.type->function.variadic) {
+                       errorf(&expr->base.source_position,
+                                       "'va_start' used in non-variadic function");
+               } else if (entity->base.parent_scope != &current_function->parameters ||
+                               entity->base.next != NULL ||
+                               entity->kind != ENTITY_PARAMETER) {
                        errorf(&expr->base.source_position,
                               "second argument of 'va_start' must be last parameter of the current function");
                } else {
@@ -7001,31 +7060,6 @@ end_error:
        return create_invalid_expression();
 }
 
-#if 0
-/**
- * Parses a __builtin_expect(, end_error) expression.
- */
-static expression_t *parse_builtin_expect(void, end_error)
-{
-       expression_t *expression
-               = allocate_expression_zero(EXPR_BINARY_BUILTIN_EXPECT);
-
-       eat(T___builtin_expect);
-
-       expect('(', end_error);
-       expression->binary.left = parse_assignment_expression();
-       expect(',', end_error);
-       expression->binary.right = parse_constant_expression();
-       expect(')', end_error);
-
-       expression->base.type = expression->binary.left->base.type;
-
-       return expression;
-end_error:
-       return create_invalid_expression();
-}
-#endif
-
 /**
  * Parses a MS assume() expression.
  */
@@ -7133,14 +7167,9 @@ static expression_t *parse_noop_expression(void)
                add_anchor_token(')');
                add_anchor_token(',');
 
-               if (token.type != ')') {
-                       while (true) {
-                               (void)parse_assignment_expression();
-                               if (token.type != ',')
-                                       break;
-                               next_token();
-                       }
-               }
+               if (token.type != ')') do {
+                       (void)parse_assignment_expression();
+               } while (next_if(','));
        }
        rem_anchor_token(',');
        rem_anchor_token(')');
@@ -7191,6 +7220,8 @@ static expression_t *parse_primary_expression(void)
                case T___noop:                       return parse_noop_expression();
 
                /* Gracefully handle type names while parsing expressions. */
+               case T_COLONCOLON:
+                       return parse_reference();
                case T_IDENTIFIER:
                        if (!is_typedef_symbol(token.v.symbol)) {
                                return parse_reference();
@@ -7538,17 +7569,13 @@ static expression_t *parse_call_expression(expression_t *expression)
 
        if (token.type != ')') {
                call_argument_t **anchor = &call->arguments;
-               for (;;) {
+               do {
                        call_argument_t *argument = allocate_ast_zero(sizeof(*argument));
                        argument->expression = parse_assignment_expression();
 
                        *anchor = argument;
                        anchor  = &argument->next;
-
-                       if (token.type != ',')
-                               break;
-                       next_token();
-               }
+               } while (next_if(','));
        }
        rem_anchor_token(',');
        rem_anchor_token(')');
@@ -7864,8 +7891,7 @@ static expression_t *parse_delete(void)
 
        eat(T_delete);
 
-       if (token.type == '[') {
-               next_token();
+       if (next_if('[')) {
                result->kind = EXPR_UNARY_DELETE_ARRAY;
                expect(']', end_error);
 end_error:;
@@ -7973,14 +7999,14 @@ static bool is_lvalue(const expression_t *expression)
                return true;
 
        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. */
-               !is_type_valid(type);
+               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. */
+                       !is_type_valid(type);
        }
        }
 }
@@ -9142,8 +9168,7 @@ static asm_argument_t *parse_asm_arguments(bool is_out)
                asm_argument_t *argument = allocate_ast_zero(sizeof(argument[0]));
                memset(argument, 0, sizeof(argument[0]));
 
-               if (token.type == '[') {
-                       eat('[');
+               if (next_if('[')) {
                        if (token.type != T_IDENTIFIER) {
                                parse_error_expected("while parsing asm argument",
                                                     T_IDENTIFIER, NULL);
@@ -9222,9 +9247,8 @@ static asm_argument_t *parse_asm_arguments(bool is_out)
                *anchor = argument;
                anchor  = &argument->next;
 
-               if (token.type != ',')
+               if (!next_if(','))
                        break;
-               eat(',');
        }
 
        return result;
@@ -9237,23 +9261,18 @@ end_error:
  */
 static asm_clobber_t *parse_asm_clobbers(void)
 {
-       asm_clobber_t *result = NULL;
-       asm_clobber_t *last   = NULL;
+       asm_clobber_t *result  = NULL;
+       asm_clobber_t **anchor = &result;
 
        while (token.type == T_STRING_LITERAL) {
                asm_clobber_t *clobber = allocate_ast_zero(sizeof(clobber[0]));
                clobber->clobber       = parse_string_literals();
 
-               if (last != NULL) {
-                       last->next = clobber;
-               } else {
-                       result = clobber;
-               }
-               last = clobber;
+               *anchor = clobber;
+               anchor  = &clobber->next;
 
-               if (token.type != ',')
+               if (!next_if(','))
                        break;
-               eat(',');
        }
 
        return result;
@@ -9269,36 +9288,31 @@ static statement_t *parse_asm_statement(void)
 
        eat(T_asm);
 
-       if (token.type == T_volatile) {
-               next_token();
+       if (next_if(T_volatile))
                asm_statement->is_volatile = true;
-       }
 
        expect('(', end_error);
        add_anchor_token(')');
        add_anchor_token(':');
        asm_statement->asm_text = parse_string_literals();
 
-       if (token.type != ':') {
+       if (!next_if(':')) {
                rem_anchor_token(':');
                goto end_of_asm;
        }
-       eat(':');
 
        asm_statement->outputs = parse_asm_arguments(true);
-       if (token.type != ':') {
+       if (!next_if(':')) {
                rem_anchor_token(':');
                goto end_of_asm;
        }
-       eat(':');
 
        asm_statement->inputs = parse_asm_arguments(false);
-       if (token.type != ':') {
+       if (!next_if(':')) {
                rem_anchor_token(':');
                goto end_of_asm;
        }
        rem_anchor_token(':');
-       eat(':');
 
        asm_statement->clobbers = parse_asm_clobbers();
 
@@ -9345,8 +9359,7 @@ static statement_t *parse_case_statement(void)
        }
 
        if (GNU_MODE) {
-               if (token.type == T_DOTDOTDOT) {
-                       next_token();
+               if (next_if(T_DOTDOTDOT)) {
                        expression_t *const end_range   = parse_expression();
                        statement->case_label.end_range = end_range;
                        if (!is_constant_expression(end_range)) {
@@ -9549,8 +9562,7 @@ end_error:
        statement->ifs.true_statement = true_stmt;
        rem_anchor_token(T_else);
 
-       if (token.type == T_else) {
-               next_token();
+       if (next_if(T_else)) {
                statement->ifs.false_statement = parse_statement();
        } else if (warning.parentheses &&
                        true_stmt->kind == STATEMENT_IF &&
@@ -9753,13 +9765,11 @@ static statement_t *parse_for(void)
        scope_t      *old_scope = scope_push(&statement->fors.scope);
 
        bool old_gcc_extension = in_gcc_extension;
-       while (token.type == T___extension__) {
-               next_token();
+       while (next_if(T___extension__)) {
                in_gcc_extension = true;
        }
 
-       if (token.type == ';') {
-               next_token();
+       if (next_if(';')) {
        } else if (is_declaration_specifier(&token, false)) {
                parse_declaration(record_entity, DECL_FLAGS_NONE);
        } else {
@@ -9827,8 +9837,7 @@ static statement_t *parse_goto(void)
        statement_t *statement = allocate_statement_zero(STATEMENT_GOTO);
        eat(T_goto);
 
-       if (GNU_MODE && token.type == '*') {
-               next_token();
+       if (GNU_MODE && next_if('*')) {
                expression_t *expression = parse_expression();
                mark_vars_read(expression, NULL);
 
@@ -10111,8 +10120,7 @@ static statement_t *parse_ms_try_statment(void)
 
        POP_PARENT;
 
-       if (token.type == T___except) {
-               eat(T___except);
+       if (next_if(T___except)) {
                expect('(', end_error);
                add_anchor_token(')');
                expression_t *const expr = parse_expression();
@@ -10129,8 +10137,7 @@ static statement_t *parse_ms_try_statment(void)
                rem_anchor_token(')');
                expect(')', end_error);
                statement->ms_try.final_statement = parse_compound_statement(false);
-       } else if (token.type == T__finally) {
-               eat(T___finally);
+       } else if (next_if(T__finally)) {
                statement->ms_try.final_statement = parse_compound_statement(false);
        } else {
                parse_error_expected("while parsing __try statement", T___except, T___finally, NULL);
@@ -10159,7 +10166,7 @@ static statement_t *parse_local_label_declaration(void)
 
        entity_t *begin = NULL, *end = NULL;
 
-       while (true) {
+       do {
                if (token.type != T_IDENTIFIER) {
                        parse_error_expected("while parsing local label declaration",
                                T_IDENTIFIER, NULL);
@@ -10187,11 +10194,7 @@ static statement_t *parse_local_label_declaration(void)
                        environment_push(entity);
                }
                next_token();
-
-               if (token.type != ',')
-                       break;
-               next_token();
-       }
+       } while (next_if(','));
        eat(';');
 end_error:
        statement->declaration.declarations_begin = begin;
@@ -10298,9 +10301,7 @@ expression_statment:
        case T___extension__:
                /* This can be a prefix to a declaration or an expression statement.
                 * We simply eat it now and parse the rest with tail recursion. */
-               do {
-                       next_token();
-               } while (token.type == T___extension__);
+               while (next_if(T___extension__)) {}
                bool old_gcc_extension = in_gcc_extension;
                in_gcc_extension       = true;
                statement = intern_parse_statement();
@@ -10697,8 +10698,7 @@ static void parse_linkage_specification(void)
        }
        current_linkage = new_linkage;
 
-       if (token.type == '{') {
-               next_token();
+       if (next_if('{')) {
                parse_externals();
                expect('}', end_error);
        } else {
@@ -10891,6 +10891,36 @@ static void complete_incomplete_arrays(void)
        }
 }
 
+void prepare_main_collect2(entity_t *entity)
+{
+       // create call to __main
+       symbol_t *symbol         = symbol_table_insert("__main");
+       entity_t *subsubmain_ent
+               = create_implicit_function(symbol, &builtin_source_position);
+
+       expression_t *ref         = allocate_expression_zero(EXPR_REFERENCE);
+       type_t       *ftype       = subsubmain_ent->declaration.type;
+       ref->base.source_position = builtin_source_position;
+       ref->base.type            = make_pointer_type(ftype, TYPE_QUALIFIER_NONE);
+       ref->reference.entity     = subsubmain_ent;
+
+       expression_t *call = allocate_expression_zero(EXPR_CALL);
+       call->base.source_position = builtin_source_position;
+       call->base.type            = type_void;
+       call->call.function        = ref;
+
+       statement_t *expr_statement = allocate_statement_zero(STATEMENT_EXPRESSION);
+       expr_statement->base.source_position  = builtin_source_position;
+       expr_statement->expression.expression = call;
+
+       statement_t *statement = entity->function.statement;
+       assert(statement->kind == STATEMENT_COMPOUND);
+       compound_statement_t *compounds = &statement->compound;
+
+       expr_statement->base.next = compounds->statements;
+       compounds->statements     = expr_statement;
+}
+
 void parse(void)
 {
        lookahead_bufpos = 0;