Create AST for select expressions even if the LHS has incomplete type.
[cparser] / parser.c
index 847478b..3c10013 100644 (file)
--- a/parser.c
+++ b/parser.c
@@ -103,17 +103,19 @@ typedef struct parse_initializer_env_t {
        bool           must_be_constant;
 } parse_initializer_env_t;
 
-typedef declaration_t* (*parsed_declaration_func) (declaration_t *declaration);
+typedef declaration_t* (*parsed_declaration_func) (declaration_t *declaration, bool is_definition);
 
 static token_t             token;
 static token_t             lookahead_buffer[MAX_LOOKAHEAD];
 static int                 lookahead_bufpos;
 static stack_entry_t      *environment_stack = NULL;
 static stack_entry_t      *label_stack       = NULL;
+static stack_entry_t      *local_label_stack = NULL;
 static scope_t            *global_scope      = NULL;
 static scope_t            *scope             = NULL;
 static declaration_t      *last_declaration  = NULL;
 static declaration_t      *current_function  = NULL;
+static declaration_t      *current_init_decl = NULL;
 static switch_statement_t *current_switch    = NULL;
 static statement_t        *current_loop      = NULL;
 static statement_t        *current_parent    = NULL;
@@ -170,7 +172,7 @@ static type_t       *parse_typename(void);
 static void parse_compound_type_entries(declaration_t *compound_declaration);
 static declaration_t *parse_declarator(
                const declaration_specifiers_t *specifiers, bool may_be_abstract);
-static declaration_t *record_declaration(declaration_t *declaration);
+static declaration_t *record_declaration(declaration_t *declaration, bool is_definition);
 
 static void semantic_comparison(binary_expression_t *expression);
 
@@ -314,6 +316,7 @@ static size_t get_expression_struct_size(expression_kind_t kind)
                [EXPR_VA_START]                = sizeof(va_start_expression_t),
                [EXPR_VA_ARG]                  = sizeof(va_arg_expression_t),
                [EXPR_STATEMENT]               = sizeof(statement_expression_t),
+               [EXPR_LABEL_ADDRESS]           = sizeof(label_address_expression_t),
        };
        if (kind >= EXPR_UNARY_FIRST && kind <= EXPR_UNARY_LAST) {
                return sizes[EXPR_UNARY_FIRST];
@@ -478,13 +481,21 @@ static size_t environment_top(void)
 }
 
 /**
- * Returns the index of the top element of the label stack.
+ * Returns the index of the top element of the global label stack.
  */
 static size_t label_top(void)
 {
        return ARR_LEN(label_stack);
 }
 
+/**
+ * Returns the index of the top element of the local label stack.
+ */
+static size_t local_label_top(void)
+{
+       return ARR_LEN(local_label_stack);
+}
+
 /**
  * Return the next token.
  */
@@ -763,6 +774,11 @@ static void stack_push(stack_entry_t **stack_ptr, declaration_t *declaration)
        ARR_APP1(stack_entry_t, *stack_ptr, entry);
 }
 
+/**
+ * Push a declaration on the environment stack.
+ *
+ * @param declaration  the declaration
+ */
 static void environment_push(declaration_t *declaration)
 {
        assert(declaration->source_position.input_name != NULL);
@@ -771,7 +787,7 @@ static void environment_push(declaration_t *declaration)
 }
 
 /**
- * Push a declaration of the label stack.
+ * Push a declaration on the global label stack.
  *
  * @param declaration  the declaration
  */
@@ -781,6 +797,17 @@ static void label_push(declaration_t *declaration)
        stack_push(&label_stack, declaration);
 }
 
+/**
+ * Push a declaration of the local label stack.
+ *
+ * @param declaration  the declaration
+ */
+static void local_label_push(declaration_t *declaration)
+{
+       assert(declaration->parent_scope != NULL);
+       stack_push(&local_label_stack, declaration);
+}
+
 /**
  * pops symbols from the environment stack until @p new_top is the top element
  */
@@ -831,13 +858,19 @@ static void stack_pop_to(stack_entry_t **stack_ptr, size_t new_top)
        ARR_SHRINKLEN(*stack_ptr, (int) new_top);
 }
 
+/**
+ * Pop all entries from the environment stack until the new_top
+ * is reached.
+ *
+ * @param new_top  the new stack top
+ */
 static void environment_pop_to(size_t new_top)
 {
        stack_pop_to(&environment_stack, new_top);
 }
 
 /**
- * Pop all entries on the label stack until the new_top
+ * Pop all entries from the global label stack until the new_top
  * is reached.
  *
  * @param new_top  the new stack top
@@ -847,6 +880,18 @@ static void label_pop_to(size_t new_top)
        stack_pop_to(&label_stack, new_top);
 }
 
+/**
+ * Pop all entries from the local label stack until the new_top
+ * is reached.
+ *
+ * @param new_top  the new stack top
+ */
+static void local_label_pop_to(size_t new_top)
+{
+       stack_pop_to(&local_label_stack, new_top);
+}
+
+
 static int get_akind_rank(atomic_type_kind_t akind)
 {
        return (int) akind;
@@ -1092,8 +1137,9 @@ static type_t *make_global_typedef(const char *name, type_t *type)
        declaration->type                   = type;
        declaration->symbol                 = symbol;
        declaration->source_position        = builtin_source_position;
+       declaration->implicit               = true;
 
-       record_declaration(declaration);
+       record_declaration(declaration, false);
 
        type_t *typedef_type               = allocate_type_zero(TYPE_TYPEDEF, &builtin_source_position);
        typedef_type->typedeft.declaration = declaration;
@@ -1807,7 +1853,7 @@ static decl_modifiers_t parse_attributes(gnu_attribute_t **attributes)
                                eat_until_matching_token('(');
                                break;
                        } else {
-                               string_t asm_string = parse_string_literals();
+                               parse_string_literals();
                        }
                        expect(')');
                        continue;
@@ -2725,7 +2771,7 @@ static void parse_enum_entries(type_t *const enum_type)
                        /* TODO semantic */
                }
 
-               record_declaration(entry);
+               record_declaration(entry, false);
 
                if (token.type != ',')
                        break;
@@ -3064,16 +3110,105 @@ static declaration_t *create_error_declaration(symbol_t *symbol, storage_class_t
 {
        declaration_t *const decl    = allocate_declaration_zero();
        decl->source_position        = *HERE;
+       decl->declared_storage_class = storage_class;
        decl->storage_class          =
                storage_class != STORAGE_CLASS_NONE || scope == global_scope ?
                        storage_class : STORAGE_CLASS_AUTO;
-       decl->declared_storage_class = decl->storage_class;
        decl->symbol                 = symbol;
        decl->implicit               = true;
-       record_declaration(decl);
+       record_declaration(decl, false);
        return decl;
 }
 
+/**
+ * Finish the construction of a struct type by calculating
+ * its size, offsets, alignment.
+ */
+static void finish_struct_type(compound_type_t *type) {
+       if (type->declaration == NULL)
+               return;
+       declaration_t *struct_decl = type->declaration;
+       if (! struct_decl->init.complete)
+               return;
+
+       il_size_t      size      = 0;
+       il_size_t      new_size;
+       il_alignment_t alignment = 1;
+       bool           need_pad  = false;
+
+       declaration_t *entry = struct_decl->scope.declarations;
+       for (; entry != NULL; entry = entry->next) {
+               if (entry->namespc != NAMESPACE_NORMAL)
+                       continue;
+
+               type_t         *m_type      = skip_typeref(entry->type);
+               il_alignment_t  m_alignment = m_type->base.alignment;
+
+               new_size = (size + m_alignment - 1) & -m_alignment;
+               if (m_alignment > alignment)
+                       alignment = m_alignment;
+               if (new_size > size)
+                       need_pad = true;
+               entry->offset = new_size;
+               size = new_size + m_type->base.size;
+       }
+       if (type->base.alignment != 0) {
+               alignment = type->base.alignment;
+       }
+
+       new_size = (size + alignment - 1) & -alignment;
+       if (new_size > size)
+               need_pad = true;
+
+       if (warning.padded && need_pad) {
+               warningf(&struct_decl->source_position,
+                       "'%#T' needs padding", type, struct_decl->symbol);
+       }
+       if (warning.packed && !need_pad) {
+               warningf(&struct_decl->source_position,
+                       "superfluous packed attribute on '%#T'",
+                       type, struct_decl->symbol);
+       }
+
+       type->base.size      = new_size;
+       type->base.alignment = alignment;
+}
+
+/**
+ * Finish the construction of an union type by calculating
+ * its size and alignment.
+ */
+static void finish_union_type(compound_type_t *type) {
+       if (type->declaration == NULL)
+               return;
+       declaration_t *union_decl = type->declaration;
+       if (! union_decl->init.complete)
+               return;
+
+       il_size_t      size      = 0;
+       il_alignment_t alignment = 1;
+
+       declaration_t *entry = union_decl->scope.declarations;
+       for (; entry != NULL; entry = entry->next) {
+               if (entry->namespc != NAMESPACE_NORMAL)
+                       continue;
+
+               type_t *m_type = skip_typeref(entry->type);
+
+               entry->offset = 0;
+               if (m_type->base.size > size)
+                       size = m_type->base.size;
+               if (m_type->base.alignment > alignment)
+                       alignment = m_type->base.alignment;
+       }
+       if (type->base.alignment != 0) {
+               alignment = type->base.alignment;
+       }
+       size = (size + alignment - 1) & -alignment;
+       type->base.size      = size;
+       type->base.alignment = alignment;
+}
+
 static void parse_declaration_specifiers(declaration_specifiers_t *specifiers)
 {
        type_t            *type            = NULL;
@@ -3213,6 +3348,7 @@ static void parse_declaration_specifiers(declaration_specifiers_t *specifiers)
                        type = allocate_type_zero(TYPE_COMPOUND_STRUCT, HERE);
 
                        type->compound.declaration = parse_compound_type_specifier(true);
+                       finish_struct_type(&type->compound);
                        break;
                }
                case T_union: {
@@ -3221,6 +3357,7 @@ static void parse_declaration_specifiers(declaration_specifiers_t *specifiers)
                        if (type->compound.declaration->modifiers & DM_TRANSPARENT_UNION)
                                modifiers |= TYPE_MODIFIER_TRANSPARENT_UNION;
                        break;
+                       finish_union_type(&type->compound);
                }
                case T_enum:
                        type = parse_enum_specifier();
@@ -3577,7 +3714,7 @@ static void semantic_parameter(declaration_t *declaration)
        declaration->type = type;
 
        if (is_type_incomplete(skip_typeref(type))) {
-               errorf(pos, "incomplete type '%T' not allowed for parameter '%Y'",
+               errorf(pos, "parameter '%#T' is of incomplete type",
                       orig_type, declaration->symbol);
        }
 }
@@ -3602,9 +3739,10 @@ static declaration_t *parse_parameters(function_type_t *type)
        add_anchor_token(')');
        int saved_comma_state = save_and_reset_anchor_state(',');
 
-       if (token.type == T_IDENTIFIER) {
-               symbol_t *symbol = token.v.symbol;
-               if (!is_typedef_symbol(symbol)) {
+       if (token.type == T_IDENTIFIER &&
+           !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;
                        declarations = parse_identifier_list();
                        goto parameters_finished;
@@ -3894,8 +4032,10 @@ static construct_type_t *parse_inner_declarator(declaration_t *declaration,
                next_token();
                add_anchor_token(')');
                inner_types = parse_inner_declarator(declaration, may_be_abstract);
-               /* All later declarators only modify the return type, not declaration */
-               declaration = NULL;
+               if (inner_types != NULL) {
+                       /* All later declarators only modify the return type, not declaration */
+                       declaration = NULL;
+               }
                rem_anchor_token(')');
                expect(')');
                break;
@@ -4204,7 +4344,7 @@ static bool is_sym_main(const symbol_t *const sym)
        return strcmp(sym->string, "main") == 0;
 }
 
-static declaration_t *internal_record_declaration(
+static declaration_t *record_declaration(
        declaration_t *const declaration,
        const bool is_definition)
 {
@@ -4377,16 +4517,6 @@ warn_redundant_declaration:
        return append_declaration(declaration);
 }
 
-static declaration_t *record_declaration(declaration_t *declaration)
-{
-       return internal_record_declaration(declaration, false);
-}
-
-static declaration_t *record_definition(declaration_t *declaration)
-{
-       return internal_record_declaration(declaration, true);
-}
-
 static void parser_error_multiple_definition(declaration_t *declaration,
                const source_position_t *source_position)
 {
@@ -4431,32 +4561,32 @@ static void parse_init_declarator_rest(declaration_t *declaration)
                must_be_constant = true;
        }
 
+       if (is_type_function(type)) {
+               errorf(&declaration->source_position,
+                      "function '%#T' is initialized like a variable",
+                      orig_type, declaration->symbol);
+               orig_type = type_error_type;
+       }
+
        parse_initializer_env_t env;
        env.type             = orig_type;
        env.must_be_constant = must_be_constant;
-       env.declaration      = declaration;
+       env.declaration      = current_init_decl = declaration;
 
        initializer_t *initializer = parse_initializer(&env);
+       current_init_decl = NULL;
 
-       if (env.type != orig_type) {
-               orig_type         = env.type;
-               type              = skip_typeref(orig_type);
-               declaration->type = env.type;
-       }
-
-       if (is_type_function(type)) {
-               errorf(&declaration->source_position,
-                      "initializers not allowed for function types at declator '%Y' (type '%T')",
-                      declaration->symbol, orig_type);
-       } else {
+       if (!is_type_function(type)) {
+               /* ยง 6.7.5 (22)  array initializers for arrays with unknown size determine
+                * the array type size */
+               declaration->type             = env.type;
                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)
+               const declaration_specifiers_t *specifiers)
 {
        eat(';');
 
@@ -4491,7 +4621,7 @@ static void parse_anonymous_declaration_rest(
                        break;
        }
 
-       finished_declaration(declaration);
+       append_declaration(declaration);
 }
 
 static void parse_declaration_rest(declaration_t *ndeclaration,
@@ -4502,7 +4632,8 @@ static void parse_declaration_rest(declaration_t *ndeclaration,
        add_anchor_token('=');
        add_anchor_token(',');
        while(true) {
-               declaration_t *declaration = finished_declaration(ndeclaration);
+               declaration_t *declaration =
+                       finished_declaration(ndeclaration, token.type == '=');
 
                type_t *orig_type = declaration->type;
                type_t *type      = skip_typeref(orig_type);
@@ -4532,7 +4663,7 @@ end_error:
        rem_anchor_token(',');
 }
 
-static declaration_t *finished_kr_declaration(declaration_t *declaration)
+static declaration_t *finished_kr_declaration(declaration_t *declaration, bool is_definition)
 {
        symbol_t *symbol  = declaration->symbol;
        if (symbol == NULL) {
@@ -4541,7 +4672,7 @@ static declaration_t *finished_kr_declaration(declaration_t *declaration)
        }
        namespace_t namespc = (namespace_t) declaration->namespc;
        if (namespc != NAMESPACE_NORMAL) {
-               return record_declaration(declaration);
+               return record_declaration(declaration, false);
        }
 
        declaration_t *previous_declaration = get_declaration(symbol, namespc);
@@ -4552,6 +4683,10 @@ static declaration_t *finished_kr_declaration(declaration_t *declaration)
                return declaration;
        }
 
+       if (is_definition) {
+               errorf(HERE, "parameter %Y is initialised", declaration->symbol);
+       }
+
        if (previous_declaration->type == NULL) {
                previous_declaration->type          = declaration->type;
                previous_declaration->declared_storage_class = declaration->declared_storage_class;
@@ -4559,7 +4694,7 @@ static declaration_t *finished_kr_declaration(declaration_t *declaration)
                previous_declaration->parent_scope  = scope;
                return previous_declaration;
        } else {
-               return record_declaration(declaration);
+               return record_declaration(declaration, false);
        }
 }
 
@@ -4570,7 +4705,7 @@ static void parse_declaration(parsed_declaration_func finished_declaration)
        parse_declaration_specifiers(&specifiers);
 
        if (token.type == ';') {
-               parse_anonymous_declaration_rest(&specifiers, append_declaration);
+               parse_anonymous_declaration_rest(&specifiers);
        } else {
                declaration_t *declaration = parse_declarator(&specifiers, /*may_be_abstract=*/false);
                parse_declaration_rest(declaration, &specifiers, finished_declaration);
@@ -4705,6 +4840,10 @@ static void check_labels(void)
        for (const goto_statement_t *goto_statement = goto_first;
            goto_statement != NULL;
            goto_statement = goto_statement->next) {
+               /* skip computed gotos */
+               if (goto_statement->expression != NULL)
+                       continue;
+
                declaration_t *label = goto_statement->label;
 
                label->used = true;
@@ -4910,9 +5049,16 @@ found_break_parent:
                }
 
                case STATEMENT_GOTO:
-                       next = stmt->gotos.label->init.statement;
-                       if (next == NULL) /* missing label */
-                               return;
+                       if (stmt->gotos.expression) {
+                               statement_t *parent = stmt->base.parent;
+                               if (parent == NULL) /* top level goto */
+                                       return;
+                               next = parent;
+                       } else {
+                               next = stmt->gotos.label->init.statement;
+                               if (next == NULL) /* missing label */
+                                       return;
+                       }
                        break;
 
                case STATEMENT_LABEL:
@@ -4962,9 +5108,28 @@ found_break_parent:
                        break;
                }
 
-               case STATEMENT_MS_TRY:
-               case STATEMENT_LEAVE:
-                       panic("unimplemented");
+               case STATEMENT_MS_TRY: {
+                       ms_try_statement_t const *const ms_try = &stmt->ms_try;
+                       check_reachable(ms_try->try_statement);
+                       next = ms_try->final_statement;
+                       break;
+               }
+
+               case STATEMENT_LEAVE: {
+                       statement_t *parent = stmt;
+                       for (;;) {
+                               parent = parent->base.parent;
+                               if (parent == NULL) /* __leave not within __try */
+                                       return;
+
+                               if (parent->kind == STATEMENT_MS_TRY) {
+                                       last = parent;
+                                       next = parent->ms_try.final_statement;
+                                       break;
+                               }
+                       }
+                       break;
+               }
        }
 
        while (next == NULL) {
@@ -5073,7 +5238,9 @@ continue_for:;
                        }
 
                        case STATEMENT_MS_TRY:
-                               panic("unimplemented");
+                               last = next;
+                               next = next->ms_try.final_statement;
+                               break;
                }
        }
 
@@ -5171,12 +5338,15 @@ static void check_unreachable(statement_t const* const stmt)
                                }
                        }
 
-                       check_unreachable(stmt->fors.body);
+                       check_unreachable(fors->body);
                        break;
                }
 
-               case STATEMENT_MS_TRY:
-                       panic("unimplemented");
+               case STATEMENT_MS_TRY: {
+                       ms_try_statement_t const *const ms_try = &stmt->ms_try;
+                       check_unreachable(ms_try->try_statement);
+                       check_unreachable(ms_try->final_statement);
+               }
        }
 
        if (stmt->base.next)
@@ -5196,7 +5366,7 @@ static void parse_external_declaration(void)
 
        /* must be a declaration */
        if (token.type == ';') {
-               parse_anonymous_declaration_rest(&specifiers, append_declaration);
+               parse_anonymous_declaration_rest(&specifiers);
                return;
        }
 
@@ -5215,11 +5385,8 @@ static void parse_external_declaration(void)
        switch (token.type) {
                case ',':
                case ';':
-                       parse_declaration_rest(ndeclaration, &specifiers, record_declaration);
-                       return;
-
                case '=':
-                       parse_declaration_rest(ndeclaration, &specifiers, record_definition);
+                       parse_declaration_rest(ndeclaration, &specifiers, record_declaration);
                        return;
        }
 
@@ -5250,6 +5417,14 @@ static void parse_external_declaration(void)
                warningf(HERE, "function '%Y' returns an aggregate",
                         ndeclaration->symbol);
        }
+       if (warning.traditional && !type->function.unspecified_parameters) {
+               warningf(HERE, "traditional C rejects ISO C style function definition of function '%Y'",
+                       ndeclaration->symbol);
+       }
+       if (warning.old_style_definition && type->function.unspecified_parameters) {
+               warningf(HERE, "old-style function definition '%Y'",
+                       ndeclaration->symbol);
+       }
 
        /* ยง 6.7.5.3 (14) a function definition with () means no
         * parameters (and not unspecified parameters) */
@@ -5266,7 +5441,7 @@ static void parse_external_declaration(void)
                ndeclaration->type = type;
        }
 
-       declaration_t *const declaration = record_definition(ndeclaration);
+       declaration_t *const declaration = record_declaration(ndeclaration, true);
        if (ndeclaration != declaration) {
                declaration->scope = ndeclaration->scope;
        }
@@ -5377,12 +5552,12 @@ static void parse_compound_declarators(declaration_t *struct_declaration,
 {
        declaration_t *last_declaration = struct_declaration->scope.declarations;
        if (last_declaration != NULL) {
-               while(last_declaration->next != NULL) {
+               while (last_declaration->next != NULL) {
                        last_declaration = last_declaration->next;
                }
        }
 
-       while(1) {
+       while (true) {
                declaration_t *declaration;
 
                if (token.type == ':') {
@@ -5418,8 +5593,8 @@ static void parse_compound_declarators(declaration_t *struct_declaration,
                                expression_t *size = parse_constant_expression();
 
                                if (!is_type_integer(type)) {
-                                       errorf(HERE, "bitfield base type '%T' is not an "
-                                              "integer type", orig_type);
+                                       errorf(HERE, "bitfield base type '%T' is not an integer type",
+                                              orig_type);
                                }
 
                                type_t *bitfield_type = make_bitfield_type(orig_type, size, &source_position);
@@ -5432,8 +5607,8 @@ static void parse_compound_declarators(declaration_t *struct_declaration,
                                               "compound member '%Y' has incomplete type '%T'",
                                               declaration->symbol, orig_type);
                                } else if (is_type_function(type)) {
-                                       errorf(HERE, "compound member '%Y' must not have function "
-                                              "type '%T'", declaration->symbol, orig_type);
+                                       errorf(HERE, "compound member '%Y' must not have function type '%T'",
+                                              declaration->symbol, orig_type);
                                }
                        }
                }
@@ -5475,7 +5650,7 @@ static void parse_compound_type_entries(declaration_t *compound_declaration)
        eat('{');
        add_anchor_token('}');
 
-       while(token.type != '}' && token.type != T_EOF) {
+       while (token.type != '}' && token.type != T_EOF) {
                declaration_specifiers_t specifiers;
                memset(&specifiers, 0, sizeof(specifiers));
                parse_declaration_specifiers(&specifiers);
@@ -5687,7 +5862,7 @@ static declaration_t *create_implicit_function(symbol_t *symbol,
 
        bool strict_prototypes_old = warning.strict_prototypes;
        warning.strict_prototypes  = false;
-       record_declaration(declaration);
+       record_declaration(declaration, false);
        warning.strict_prototypes = strict_prototypes_old;
 
        return declaration;
@@ -5821,7 +5996,10 @@ type_t *revert_automatic_type_conversion(const expression_t *expression)
 {
        switch (expression->kind) {
                case EXPR_REFERENCE: return expression->reference.declaration->type;
-               case EXPR_SELECT:    return expression->select.compound_entry->type;
+
+               case EXPR_SELECT:
+                       return get_qualified_type(expression->select.compound_entry->type,
+                                                 expression->base.type->base.qualifiers);
 
                case EXPR_UNARY_DEREFERENCE: {
                        const expression_t *const value = expression->unary.value;
@@ -5920,6 +6098,12 @@ static expression_t *parse_reference(void)
                                declaration->symbol, &declaration->source_position);
                }
        }
+       if (warning.init_self && declaration == current_init_decl) {
+               current_init_decl = NULL;
+               warningf(&source_position,
+                       "variable '%#T' is initialized by itself",
+                       declaration->type, declaration->symbol);
+       }
 
        return expression;
 }
@@ -6467,6 +6651,71 @@ end_error:
        return create_invalid_expression();
 }
 
+/**
+ * Return the declaration for a given label symbol or create a new one.
+ *
+ * @param symbol  the symbol of the label
+ */
+static declaration_t *get_label(symbol_t *symbol)
+{
+       declaration_t *candidate;
+       assert(current_function != NULL);
+
+       candidate = get_declaration(symbol, NAMESPACE_LOCAL_LABEL);
+       /* if we found a local label, we already created the declaration */
+       if (candidate != NULL) {
+               assert(candidate->parent_scope == scope);
+               return candidate;
+       }
+
+       candidate = get_declaration(symbol, NAMESPACE_LABEL);
+       /* if we found a label in the same function, then we already created the
+        * declaration */
+       if (candidate != NULL
+                       && candidate->parent_scope == &current_function->scope) {
+               return candidate;
+       }
+
+       /* otherwise we need to create a new one */
+       declaration_t *const declaration = allocate_declaration_zero();
+       declaration->namespc       = NAMESPACE_LABEL;
+       declaration->symbol        = symbol;
+
+       label_push(declaration);
+
+       return declaration;
+}
+
+/**
+ * Parses a GNU && label address expression.
+ */
+static expression_t *parse_label_address(void)
+{
+       source_position_t source_position = token.source_position;
+       eat(T_ANDAND);
+       if (token.type != T_IDENTIFIER) {
+               parse_error_expected("while parsing label address", T_IDENTIFIER, NULL);
+               goto end_error;
+       }
+       symbol_t *symbol = token.v.symbol;
+       next_token();
+
+       declaration_t *label = get_label(symbol);
+
+       label->used          = true;
+       label->address_taken = true;
+
+       expression_t *expression = allocate_expression_zero(EXPR_LABEL_ADDRESS);
+       expression->base.source_position = source_position;
+
+       /* label address is threaten as a void pointer */
+       expression->base.type                 = type_void_ptr;
+       expression->label_address.declaration = label;
+       return expression;
+end_error:
+       return create_invalid_expression();
+}
+
 /**
  * Parse a microsoft __noop expression.
  */
@@ -6544,6 +6793,10 @@ static expression_t *parse_primary_expression(void)
                case T___builtin_constant_p:     return parse_builtin_constant();
                case T___builtin_prefetch:       return parse_builtin_prefetch();
                case T__assume:                  return parse_assume();
+               case T_ANDAND:
+                       if (c_mode & _GNUC)
+                               return parse_label_address();
+                       break;
 
                case '(':                        return parse_parenthesized_expression();
                case T___noop:                   return parse_noop_expression();
@@ -6707,56 +6960,67 @@ static expression_t *parse_select_expression(unsigned precedence,
                parse_error_expected("while parsing select", T_IDENTIFIER, NULL);
                return select;
        }
-       symbol_t *symbol      = token.v.symbol;
-       select->select.symbol = symbol;
+       symbol_t *symbol = token.v.symbol;
        next_token();
 
        type_t *const orig_type = compound->base.type;
        type_t *const type      = skip_typeref(orig_type);
 
-       type_t *type_left = type;
-       if (is_pointer) {
-               if (!is_type_pointer(type)) {
-                       if (is_type_valid(type)) {
-                               errorf(HERE, "left hand side of '->' is not a pointer, but '%T'", orig_type);
-                       }
-                       return create_invalid_expression();
+       type_t *type_left;
+       bool    saw_error = false;
+       if (is_type_pointer(type)) {
+               if (!is_pointer) {
+                       errorf(HERE,
+                              "request for member '%Y' in something not a struct or union, but '%T'",
+                              symbol, orig_type);
+                       saw_error = true;
                }
-               type_left = type->pointer.points_to;
-       }
-       type_left = skip_typeref(type_left);
-
-       if (type_left->kind != TYPE_COMPOUND_STRUCT &&
-           type_left->kind != TYPE_COMPOUND_UNION) {
-               if (is_type_valid(type_left)) {
-                       errorf(HERE, "request for member '%Y' in something not a struct or "
-                              "union, but '%T'", symbol, type_left);
+               type_left = skip_typeref(type->pointer.points_to);
+       } else {
+               if (is_pointer && is_type_valid(type)) {
+                       errorf(HERE, "left hand side of '->' is not a pointer, but '%T'", orig_type);
+                       saw_error = true;
                }
-               return create_invalid_expression();
+               type_left = type;
        }
 
-       declaration_t *const declaration = type_left->compound.declaration;
+       declaration_t *entry;
+       if (type_left->kind == TYPE_COMPOUND_STRUCT ||
+           type_left->kind == TYPE_COMPOUND_UNION) {
+               declaration_t *const declaration = type_left->compound.declaration;
 
-       if (!declaration->init.complete) {
-               errorf(HERE, "request for member '%Y' of incomplete type '%T'",
-                      symbol, type_left);
-               return create_invalid_expression();
-       }
+               if (!declaration->init.complete) {
+                       errorf(HERE, "request for member '%Y' of incomplete type '%T'",
+                              symbol, type_left);
+                       goto create_error_entry;
+               }
 
-       declaration_t *iter = find_compound_entry(declaration, symbol);
-       if (iter == NULL) {
-               errorf(HERE, "'%T' has no member named '%Y'", orig_type, symbol);
-               return create_invalid_expression();
+               entry = find_compound_entry(declaration, symbol);
+               if (entry == NULL) {
+                       errorf(HERE, "'%T' has no member named '%Y'", orig_type, symbol);
+                       goto create_error_entry;
+               }
+       } else {
+               if (is_type_valid(type_left) && !saw_error) {
+                       errorf(HERE,
+                              "request for member '%Y' in something not a struct or union, but '%T'",
+                              symbol, type_left);
+               }
+create_error_entry:
+               entry         = allocate_declaration_zero();
+               entry->symbol = symbol;
        }
 
+       select->select.compound_entry = entry;
+
+       type_t *const res_type =
+               get_qualified_type(entry->type, type_left->base.qualifiers);
+
        /* we always do the auto-type conversions; the & and sizeof parser contains
         * code to revert this! */
-       type_t *expression_type = automatic_type_conversion(iter->type);
+       select->base.type = automatic_type_conversion(res_type);
 
-       select->select.compound_entry = iter;
-       select->base.type             = expression_type;
-
-       type_t *skipped = skip_typeref(iter->type);
+       type_t *skipped = skip_typeref(res_type);
        if (skipped->kind == TYPE_BITFIELD) {
                select->base.type = skipped->bitfield.base_type;
        }
@@ -6765,12 +7029,13 @@ static expression_t *parse_select_expression(unsigned precedence,
 }
 
 static void check_call_argument(const function_parameter_t *parameter,
-                                call_argument_t *argument)
+                                call_argument_t *argument, unsigned pos)
 {
        type_t         *expected_type      = parameter->type;
        type_t         *expected_type_skip = skip_typeref(expected_type);
        assign_error_t  error              = ASSIGN_ERROR_INCOMPATIBLE;
        expression_t   *arg_expr           = argument->expression;
+       type_t         *arg_type           = skip_typeref(arg_expr->base.type);
 
        /* handle transparent union gnu extension */
        if (is_type_union(expected_type_skip)
@@ -6803,9 +7068,34 @@ static void check_call_argument(const function_parameter_t *parameter,
        argument->expression = create_implicit_cast(argument->expression,
                                                    expected_type);
 
-       /* TODO report exact scope in error messages (like "in 3rd parameter") */
-       report_assign_error(error, expected_type, arg_expr,     "function call",
-                           &arg_expr->base.source_position);
+       if (error != ASSIGN_SUCCESS) {
+               /* report exact scope in error messages (like "in argument 3") */
+               char buf[64];
+               snprintf(buf, sizeof(buf), "call argument %u", pos);
+               report_assign_error(error, expected_type, arg_expr,     buf,
+                                                       &arg_expr->base.source_position);
+       } else if (warning.traditional | warning.conversion) {
+               if (
+                   /* passing as integer instead of float or complex */
+                   (is_type_integer(expected_type) &&
+                    (is_type_float(arg_type) || is_type_complex(arg_type))) ||
+                   /* passing as complex instead of integer or float */
+                   (is_type_complex(expected_type) &&
+                    (is_type_integer(arg_type) || is_type_float(arg_type))) ||
+                   /* passing as float instead of integer or complex */
+                   (is_type_float(expected_type) &&
+                    (is_type_integer(arg_type) || is_type_complex(arg_type))) ||
+                   /* passing as float instead of double */
+                   (is_type_float(expected_type) && expected_type != type_double &&
+                    is_type_float(arg_type))) {
+                       warningf(&arg_expr->base.source_position,
+                               "passing call argument %u as '%T' rather than '%T' due to prototype",
+                               pos, expected_type, arg_type);
+               }
+               if (is_type_integer(expected_type) && is_type_integer(arg_type)) {
+                       /* TODO check for size HERE */
+               }
+       }
 }
 
 /**
@@ -6848,7 +7138,7 @@ static expression_t *parse_call_expression(unsigned precedence,
        if (token.type != ')') {
                call_argument_t *last_argument = NULL;
 
-               while(true) {
+               while (true) {
                        call_argument_t *argument = allocate_ast_zero(sizeof(argument[0]));
 
                        argument->expression = parse_assignment_expression();
@@ -6874,9 +7164,9 @@ static expression_t *parse_call_expression(unsigned precedence,
        function_parameter_t *parameter = function_type->parameters;
        call_argument_t      *argument  = call->arguments;
        if (!function_type->unspecified_parameters) {
-               for; parameter != NULL && argument != NULL;
+               for (unsigned pos = 0; parameter != NULL && argument != NULL;
                                parameter = parameter->next, argument = argument->next) {
-                       check_call_argument(parameter, argument);
+                       check_call_argument(parameter, argument, ++pos);
                }
 
                if (parameter != NULL) {
@@ -7017,13 +7307,8 @@ static expression_t *parse_conditional_expression(unsigned precedence,
                                to = type_void;
                        }
 
-                       type_t *const copy = duplicate_type(to);
-                       copy->base.qualifiers = to1->base.qualifiers | to2->base.qualifiers;
-
-                       type_t *const type = typehash_insert(copy);
-                       if (type != copy)
-                               free_type(copy);
-
+                       type_t *const type =
+                               get_qualified_type(to, to1->base.qualifiers | to2->base.qualifiers);
                        result_type = make_pointer_type(type, TYPE_QUALIFIER_NONE);
                } else if (is_type_integer(other_type)) {
                        warningf(&conditional->base.source_position,
@@ -7123,6 +7408,20 @@ static bool check_pointer_arithmetic(const source_position_t *source_position,
        return true;
 }
 
+static bool is_lvalue(const expression_t *expression)
+{
+       switch (expression->kind) {
+       case EXPR_REFERENCE:
+       case EXPR_ARRAY_ACCESS:
+       case EXPR_SELECT:
+       case EXPR_UNARY_DEREFERENCE:
+               return true;
+
+       default:
+               return false;
+       }
+}
+
 static void semantic_incdec(unary_expression_t *expression)
 {
        type_t *const orig_type = expression->value->base.type;
@@ -7138,6 +7437,10 @@ static void semantic_incdec(unary_expression_t *expression)
                       "operation needs an arithmetic or pointer type");
                return;
        }
+       if (!is_lvalue(expression->value)) {
+               /* TODO: improve error message */
+               errorf(&expression->base.source_position, "lvalue required as operand");
+       }
        expression->base.type = orig_type;
 }
 
@@ -7149,7 +7452,7 @@ static void semantic_unexpr_arithmetic(unary_expression_t *expression)
                if (is_type_valid(type)) {
                        /* TODO: improve error message */
                        errorf(&expression->base.source_position,
-                              "operation needs an arithmetic type");
+                               "operation needs an arithmetic type");
                }
                return;
        }
@@ -7157,6 +7460,14 @@ static void semantic_unexpr_arithmetic(unary_expression_t *expression)
        expression->base.type = orig_type;
 }
 
+static void semantic_unexpr_plus(unary_expression_t *expression)
+{
+       semantic_unexpr_arithmetic(expression);
+       if (warning.traditional)
+               warningf(&expression->base.source_position,
+                       "traditional C rejects the unary plus operator");
+}
+
 static void semantic_not(unary_expression_t *expression)
 {
        type_t *const orig_type = expression->value->base.type;
@@ -7191,7 +7502,7 @@ static void semantic_dereference(unary_expression_t *expression)
        if (!is_type_pointer(type)) {
                if (is_type_valid(type)) {
                        errorf(&expression->base.source_position,
-                              "Unary '*' needs pointer or arrray type, but type '%T' given", orig_type);
+                              "Unary '*' needs pointer or array type, but type '%T' given", orig_type);
                }
                return;
        }
@@ -7201,6 +7512,12 @@ static void semantic_dereference(unary_expression_t *expression)
        expression->base.type = result_type;
 }
 
+/**
+ * Record that an address is taken (expression represents an lvalue).
+ *
+ * @param expression       the expression
+ * @param may_be_register  if true, the expression might be an register
+ */
 static void set_address_taken(expression_t *expression, bool may_be_register)
 {
        if (expression->kind != EXPR_REFERENCE)
@@ -7254,7 +7571,7 @@ static expression_t *parse_##unexpression_type(unsigned precedence)            \
 CREATE_UNARY_EXPRESSION_PARSER('-', EXPR_UNARY_NEGATE,
                                semantic_unexpr_arithmetic)
 CREATE_UNARY_EXPRESSION_PARSER('+', EXPR_UNARY_PLUS,
-                               semantic_unexpr_arithmetic)
+                               semantic_unexpr_plus)
 CREATE_UNARY_EXPRESSION_PARSER('!', EXPR_UNARY_NOT,
                                semantic_not)
 CREATE_UNARY_EXPRESSION_PARSER('*', EXPR_UNARY_DEREFERENCE,
@@ -7591,20 +7908,6 @@ static bool has_const_fields(const compound_type_t *type)
        return false;
 }
 
-static bool is_lvalue(const expression_t *expression)
-{
-       switch (expression->kind) {
-       case EXPR_REFERENCE:
-       case EXPR_ARRAY_ACCESS:
-       case EXPR_SELECT:
-       case EXPR_UNARY_DEREFERENCE:
-               return true;
-
-       default:
-               return false;
-       }
-}
-
 static bool is_valid_assignment_lhs(expression_t const* const left)
 {
        type_t *const orig_type_left = revert_automatic_type_conversion(left);
@@ -7739,9 +8042,6 @@ static void semantic_binexpr_assign(binary_expression_t *expression)
        expression_t *left           = expression->left;
        type_t       *orig_type_left = left->base.type;
 
-       type_t *type_left = revert_automatic_type_conversion(left);
-       type_left         = skip_typeref(orig_type_left);
-
        if (!is_valid_assignment_lhs(left))
                return;
 
@@ -7776,6 +8076,7 @@ static bool expression_has_effect(const expression_t *const expr)
                case EXPR_WIDE_CHARACTER_CONSTANT:   return false;
                case EXPR_STRING_LITERAL:            return false;
                case EXPR_WIDE_STRING_LITERAL:       return false;
+               case EXPR_LABEL_ADDRESS:             return false;
 
                case EXPR_CALL: {
                        const call_expression_t *const call = &expr->call;
@@ -8448,32 +8749,6 @@ end_error:
        return create_invalid_statement();
 }
 
-/**
- * Return the declaration for a given label symbol or create a new one.
- *
- * @param symbol  the symbol of the label
- */
-static declaration_t *get_label(symbol_t *symbol)
-{
-       declaration_t *candidate = get_declaration(symbol, NAMESPACE_LABEL);
-       assert(current_function != NULL);
-       /* if we found a label in the same function, then we already created the
-        * declaration */
-       if (candidate != NULL
-                       && candidate->parent_scope == &current_function->scope) {
-               return candidate;
-       }
-
-       /* otherwise we need to create a new one */
-       declaration_t *const declaration = allocate_declaration_zero();
-       declaration->namespc       = NAMESPACE_LABEL;
-       declaration->symbol        = symbol;
-
-       label_push(declaration);
-
-       return declaration;
-}
-
 /**
  * Parse a label statement.
  */
@@ -8491,9 +8766,9 @@ static statement_t *parse_label_statement(void)
 
        PUSH_PARENT(statement);
 
-       /* if source position is already set then the label is defined twice,
-        * otherwise it was just mentioned in a goto so far */
-       if (label->source_position.input_name != NULL) {
+       /* if statement is already set then the label is defined twice,
+        * otherwise it was just mentioned in a goto/local label declaration so far */
+       if (label->init.statement != NULL) {
                errorf(HERE, "duplicate label '%Y' (declared %P)",
                       symbol, &label->source_position);
        } else {
@@ -8630,6 +8905,13 @@ static statement_t *parse_switch(void)
        type_t       *      type = skip_typeref(expr->base.type);
        if (is_type_integer(type)) {
                type = promote_integer(type);
+               if (warning.traditional) {
+                       if (get_rank(type) >= get_akind_rank(ATOMIC_TYPE_LONG)) {
+                               warningf(&expr->base.source_position,
+                                       "'%T' switch expression not converted to '%T' in ISO C",
+                                       type, type_int);
+                       }
+               }
        } else if (is_type_valid(type)) {
                errorf(&expr->base.source_position,
                       "switch quantity is not an integer, but '%T'", type);
@@ -8804,22 +9086,47 @@ end_error:
  */
 static statement_t *parse_goto(void)
 {
+       source_position_t source_position = token.source_position;
        eat(T_goto);
 
-       if (token.type != T_IDENTIFIER) {
-               parse_error_expected("while parsing goto", T_IDENTIFIER, NULL);
-               eat_statement();
-               goto end_error;
-       }
-       symbol_t *symbol = token.v.symbol;
-       next_token();
+       statement_t *statement;
+       if (c_mode & _GNUC && token.type == '*') {
+               next_token();
+               expression_t *expression = parse_expression();
 
-       declaration_t *label = get_label(symbol);
+               /* Argh: although documentation say the expression must be of type void *,
+                * gcc excepts anything that can be casted into void * without error */
+               type_t *type = expression->base.type;
 
-       statement_t *statement          = allocate_statement_zero(STATEMENT_GOTO);
-       statement->base.source_position = token.source_position;
+               if (type != type_error_type) {
+                       if (!is_type_pointer(type) && !is_type_integer(type)) {
+                               errorf(&source_position, "cannot convert to a pointer type");
+                       } else if (type != type_void_ptr) {
+                               warningf(&source_position,
+                                       "type of computed goto expression should be 'void*' not '%T'", type);
+                       }
+                       expression = create_implicit_cast(expression, type_void_ptr);
+               }
+
+               statement                       = allocate_statement_zero(STATEMENT_GOTO);
+               statement->base.source_position = source_position;
+               statement->gotos.expression     = expression;
+       } else {
+               if (token.type != T_IDENTIFIER) {
+                       if (c_mode & _GNUC)
+                               parse_error_expected("while parsing goto", T_IDENTIFIER, '*', NULL);
+                       else
+                               parse_error_expected("while parsing goto", T_IDENTIFIER, NULL);
+                       eat_statement();
+                       goto end_error;
+               }
+               symbol_t *symbol = token.v.symbol;
+               next_token();
 
-       statement->gotos.label = label;
+               statement                       = allocate_statement_zero(STATEMENT_GOTO);
+               statement->base.source_position = source_position;
+               statement->gotos.label          = get_label(symbol);
+       }
 
        /* remember the goto's in a list for later checking */
        if (goto_last == NULL) {
@@ -8841,22 +9148,18 @@ end_error:
  */
 static statement_t *parse_continue(void)
 {
-       statement_t *statement;
        if (current_loop == NULL) {
                errorf(HERE, "continue statement not within loop");
-               statement = create_invalid_statement();
-       } else {
-               statement = allocate_statement_zero(STATEMENT_CONTINUE);
-
-               statement->base.source_position = token.source_position;
        }
 
+       statement_t *statement = allocate_statement_zero(STATEMENT_CONTINUE);
+       statement->base.source_position = token.source_position;
+
        eat(T_continue);
        expect(';');
 
-       return statement;
 end_error:
-       return create_invalid_statement();
+       return statement;
 }
 
 /**
@@ -8864,45 +9167,37 @@ end_error:
  */
 static statement_t *parse_break(void)
 {
-       statement_t *statement;
        if (current_switch == NULL && current_loop == NULL) {
                errorf(HERE, "break statement not within loop or switch");
-               statement = create_invalid_statement();
-       } else {
-               statement = allocate_statement_zero(STATEMENT_BREAK);
-
-               statement->base.source_position = token.source_position;
        }
 
+       statement_t *statement = allocate_statement_zero(STATEMENT_BREAK);
+       statement->base.source_position = token.source_position;
+
        eat(T_break);
        expect(';');
 
-       return statement;
 end_error:
-       return create_invalid_statement();
+       return statement;
 }
 
 /**
  * Parse a __leave statement.
  */
-static statement_t *parse_leave(void)
+static statement_t *parse_leave_statement(void)
 {
-       statement_t *statement;
        if (current_try == NULL) {
                errorf(HERE, "__leave statement not within __try");
-               statement = create_invalid_statement();
-       } else {
-               statement = allocate_statement_zero(STATEMENT_LEAVE);
-
-               statement->base.source_position = token.source_position;
        }
 
+       statement_t *statement = allocate_statement_zero(STATEMENT_LEAVE);
+       statement->base.source_position = token.source_position;
+
        eat(T___leave);
        expect(';');
 
-       return statement;
 end_error:
-       return create_invalid_statement();
+       return statement;
 }
 
 /**
@@ -8978,7 +9273,6 @@ static statement_t *parse_return(void)
        if (token.type != ';') {
                return_value = parse_expression();
        }
-       expect(';');
 
        const type_t *const func_type = current_function->type;
        assert(is_type_function(func_type));
@@ -9015,9 +9309,10 @@ static statement_t *parse_return(void)
        }
        statement->returns.value = return_value;
 
-       return statement;
+       expect(';');
+
 end_error:
-       return create_invalid_statement();
+       return statement;
 }
 
 /**
@@ -9055,9 +9350,8 @@ static statement_t *parse_expression_statement(void)
 
        expect(';');
 
-       return statement;
 end_error:
-       return create_invalid_statement();
+       return statement;
 }
 
 /**
@@ -9067,15 +9361,18 @@ end_error:
 static statement_t *parse_ms_try_statment(void)
 {
        statement_t *statement = allocate_statement_zero(STATEMENT_MS_TRY);
-
        statement->base.source_position  = token.source_position;
        eat(T___try);
 
+       PUSH_PARENT(statement);
+
        ms_try_statement_t *rem = current_try;
        current_try = &statement->ms_try;
        statement->ms_try.try_statement = parse_compound_statement(false);
        current_try = rem;
 
+       POP_PARENT;
+
        if (token.type == T___except) {
                eat(T___except);
                expect('(');
@@ -9115,6 +9412,54 @@ static statement_t *parse_empty_statement(void)
        return statement;
 }
 
+static statement_t *parse_local_label_declaration(void) {
+       statement_t *statement = allocate_statement_zero(STATEMENT_DECLARATION);
+       statement->base.source_position = token.source_position;
+
+       eat(T___label__);
+
+       declaration_t *begin = NULL, *end = NULL;
+
+       while (true) {
+               if (token.type != T_IDENTIFIER) {
+                       parse_error_expected("while parsing local label declaration",
+                               T_IDENTIFIER, NULL);
+                       goto end_error;
+               }
+               symbol_t      *symbol      = token.v.symbol;
+               declaration_t *declaration = get_declaration(symbol, NAMESPACE_LOCAL_LABEL);
+               if (declaration != NULL) {
+                       errorf(HERE, "multiple definitions of '__label__ %Y' (previous definition at %P)",
+                               symbol, &declaration->source_position);
+               } else {
+                       declaration = allocate_declaration_zero();
+                       declaration->namespc         = NAMESPACE_LOCAL_LABEL;
+                       declaration->source_position = token.source_position;
+                       declaration->symbol          = symbol;
+                       declaration->parent_scope    = scope;
+                       declaration->init.statement  = NULL;
+
+                       if (end != NULL)
+                               end->next = declaration;
+                       end = declaration;
+                       if (begin == NULL)
+                               begin = declaration;
+
+                       local_label_push(declaration);
+               }
+               next_token();
+
+               if (token.type != ',')
+                       break;
+               next_token();
+       }
+       eat(';');
+end_error:
+       statement->declaration.declarations_begin = begin;
+       statement->declaration.declarations_end   = end;
+       return statement;
+}
+
 /**
  * Parse a statement.
  * There's also parse_statement() which additionally checks for
@@ -9160,9 +9505,13 @@ static statement_t *intern_parse_statement(void)
                statement = parse_declaration_statement();
                break;
 
+       case T___label__:
+               statement = parse_local_label_declaration();
+               break;
+
        case ';':        statement = parse_empty_statement();         break;
        case '{':        statement = parse_compound_statement(false); break;
-       case T___leave:  statement = parse_leave();                   break;
+       case T___leave:  statement = parse_leave_statement();                   break;
        case T___try:    statement = parse_ms_try_statment();         break;
        case T_asm:      statement = parse_asm_statement();           break;
        case T_break:    statement = parse_break();                   break;
@@ -9221,6 +9570,7 @@ static statement_t *parse_compound_statement(bool inside_expression_statement)
        add_anchor_token('}');
 
        int      top        = environment_top();
+       int      top_local  = local_label_top();
        scope_t *last_scope = scope;
        set_scope(&statement->compound.scope);
 
@@ -9283,6 +9633,7 @@ end_error:
        assert(scope == &statement->compound.scope);
        set_scope(last_scope);
        environment_pop_to(top);
+       local_label_pop_to(top_local);
 
        POP_PARENT;
        return statement;
@@ -9409,6 +9760,7 @@ void start_parsing(void)
 {
        environment_stack = NEW_ARR_F(stack_entry_t, 0);
        label_stack       = NEW_ARR_F(stack_entry_t, 0);
+       local_label_stack = NEW_ARR_F(stack_entry_t, 0);
        diagnostic_count  = 0;
        error_count       = 0;
        warning_count     = 0;
@@ -9440,6 +9792,7 @@ translation_unit_t *finish_parsing(void)
 
        DEL_ARR_F(environment_stack);
        DEL_ARR_F(label_stack);
+       DEL_ARR_F(local_label_stack);
 
        translation_unit_t *result = unit;
        unit = NULL;
@@ -9449,7 +9802,7 @@ translation_unit_t *finish_parsing(void)
 void parse(void)
 {
        lookahead_bufpos = 0;
-       for(int i = 0; i < MAX_LOOKAHEAD + 2; ++i) {
+       for (int i = 0; i < MAX_LOOKAHEAD + 2; ++i) {
                next_token();
        }
        parse_translation_unit();