fix cases where compoundlits are constant/get an entity
[cparser] / parser.c
index 8b0aadb..a9a4041 100644 (file)
--- a/parser.c
+++ b/parser.c
@@ -133,7 +133,7 @@ static elf_visibility_tag_t default_visibility = ELF_VISIBILITY_DEFAULT;
 #define PUSH_EXTENSION() \
        (void)0; \
        bool const old_gcc_extension = in_gcc_extension; \
-       while (next_if(T___extension__)) { \
+       while (accept(T___extension__)) { \
                in_gcc_extension = true; \
        } \
        do {} while (0)
@@ -486,7 +486,13 @@ static inline void eat(token_kind_t const kind)
        next_token();
 }
 
-static inline bool next_if(token_kind_t const kind)
+/**
+ * Consume the current token, if it is of the expected kind.
+ *
+ * @param  kind  The kind of token to consume.
+ * @return Whether the token was consumed.
+ */
+static inline bool accept(token_kind_t const kind)
 {
        if (token.kind == kind) {
                eat(kind);
@@ -597,7 +603,7 @@ static void eat_until_anchor(void)
 static void eat_block(void)
 {
        eat_until_matching_token('{');
-       next_if('}');
+       accept('}');
 }
 
 /**
@@ -1155,7 +1161,7 @@ static attribute_argument_t *parse_attribute_arguments(void)
                /* append argument */
                *anchor = argument;
                anchor  = &argument->next;
-       } while (next_if(','));
+       } while (accept(','));
        expect(')');
        return first;
 }
@@ -1203,7 +1209,7 @@ static attribute_t *parse_attribute_gnu_single(void)
        next_token();
 
        /* parse arguments */
-       if (next_if('('))
+       if (accept('('))
                attribute->a.arguments = parse_attribute_arguments();
 
        return attribute;
@@ -1226,7 +1232,7 @@ static attribute_t *parse_attribute_gnu(void)
                        *anchor = attribute;
                        anchor  = &attribute->next;
                }
-       } while (next_if(','));
+       } while (accept(','));
        rem_anchor_token(',');
        rem_anchor_token(')');
 
@@ -1646,7 +1652,7 @@ static initializer_t *parse_scalar_initializer(type_t *type,
 
        bool additional_warning_displayed = false;
        while (braces > 0) {
-               next_if(',');
+               accept(',');
                if (token.kind != '}') {
                        if (!additional_warning_displayed) {
                                warningf(WARN_OTHER, HERE, "additional elements in scalar initializer");
@@ -1938,7 +1944,7 @@ static void advance_current_object(type_path_t *path, size_t top_path_level)
  */
 static void skip_initializers(void)
 {
-       next_if('{');
+       accept('{');
 
        while (token.kind != '}') {
                if (token.kind == T_EOF)
@@ -2077,7 +2083,7 @@ finish_designator:
                        if (expression->kind == EXPR_STRING_LITERAL && outer_type != NULL) {
                                result = initializer_from_expression(outer_type, expression);
                                if (result != NULL) {
-                                       next_if(',');
+                                       accept(',');
                                        if (token.kind != '}') {
                                                warningf(WARN_OTHER, HERE, "excessive elements in initializer for type '%T'", outer_type);
                                        }
@@ -2123,7 +2129,7 @@ finish_designator:
                ARR_APP1(initializer_t*, initializers, sub);
 
 error_parse_next:
-               if (!next_if(','))
+               if (!accept(','))
                        break;
                if (token.kind == '}') {
                        break;
@@ -2345,7 +2351,7 @@ static void parse_enum_entries(type_t *const enum_type)
                entity->enum_value.enum_type = enum_type;
                rem_anchor_token('=');
 
-               if (next_if('=')) {
+               if (accept('=')) {
                        expression_t *value = parse_constant_expression();
 
                        value = create_implicit_cast(value, enum_type);
@@ -2355,7 +2361,7 @@ static void parse_enum_entries(type_t *const enum_type)
                }
 
                record_entity(entity, false);
-       } while (next_if(',') && token.kind != '}');
+       } while (accept(',') && token.kind != '}');
        rem_anchor_token(',');
        rem_anchor_token('}');
 
@@ -2540,7 +2546,7 @@ static attribute_t *parse_attribute_ms_property(attribute_t *attribute)
                symbol_t *const sym = expect_identifier("while parsing property declspec", NULL);
                if (prop != NULL)
                        *prop = sym ? sym : sym_anonymous;
-       } while (next_if(','));
+       } while (accept(','));
        rem_anchor_token(',');
        rem_anchor_token(')');
 
@@ -2553,7 +2559,7 @@ static attribute_t *parse_attribute_ms_property(attribute_t *attribute)
 static attribute_t *parse_microsoft_extended_decl_modifier_single(void)
 {
        attribute_kind_t kind = ATTRIBUTE_UNKNOWN;
-       if (next_if(T_restrict)) {
+       if (accept(T_restrict)) {
                kind = ATTRIBUTE_MS_RESTRICT;
        } else if (token.kind == T_IDENTIFIER) {
                char const *const name = token.base.symbol->string;
@@ -2582,7 +2588,7 @@ static attribute_t *parse_microsoft_extended_decl_modifier_single(void)
        }
 
        /* parse arguments */
-       if (next_if('('))
+       if (accept('('))
                attribute->a.arguments = parse_attribute_arguments();
 
        return attribute;
@@ -2607,7 +2613,7 @@ static attribute_t *parse_microsoft_extended_decl_modifier(attribute_t *first)
 
                        *anchor = attribute;
                        anchor  = &attribute->next;
-               } while (next_if(','));
+               } while (accept(','));
        }
        rem_anchor_token(')');
        expect(')');
@@ -3081,7 +3087,7 @@ static void parse_identifier_list(scope_t *scope)
 
                if (scope != NULL)
                        append_entity(scope, entity);
-       } while (next_if(',') && token.kind == T_IDENTIFIER);
+       } while (accept(',') && token.kind == T_IDENTIFIER);
 }
 
 static entity_t *parse_parameter(void)
@@ -3194,7 +3200,7 @@ static void parse_parameters(function_type_t *type, scope_t *scope)
                        default:
                                goto parameters_finished;
                        }
-               } while (next_if(','));
+               } while (accept(','));
 parameters_finished:
                rem_anchor_token(',');
        }
@@ -3291,12 +3297,12 @@ static construct_type_t *parse_array_declarator(void)
        eat('[');
        add_anchor_token(']');
 
-       bool is_static = next_if(T_static);
+       bool is_static = accept(T_static);
 
        type_qualifiers_t type_qualifiers = parse_type_qualifiers();
 
        if (!is_static)
-               is_static = next_if(T_static);
+               is_static = accept(T_static);
 
        array->type_qualifiers = type_qualifiers;
        array->is_static       = is_static;
@@ -4292,7 +4298,7 @@ static void parse_declaration_rest(entity_t *ndeclaration,
 
                check_variable_type_complete(entity);
 
-               if (!next_if(','))
+               if (!accept(','))
                        break;
 
                add_anchor_token('=');
@@ -5570,7 +5576,7 @@ static void parse_compound_declarators(compound_t *compound,
                                append_entity(&compound->members, entity);
                        }
                }
-       } while (next_if(','));
+       } while (accept(','));
        rem_anchor_token(',');
        rem_anchor_token(';');
        expect(';');
@@ -6042,7 +6048,7 @@ static entity_t *parse_qualified_identifier(void)
        source_position_t  pos;
        const scope_t     *lookup_scope = NULL;
 
-       if (next_if(T_COLONCOLON))
+       if (accept(T_COLONCOLON))
                lookup_scope = &unit->scope;
 
        entity_t *entity;
@@ -6054,7 +6060,7 @@ static entity_t *parse_qualified_identifier(void)
                /* lookup entity */
                entity = lookup_entity(lookup_scope, symbol, NAMESPACE_NORMAL);
 
-               if (!next_if(T_COLONCOLON))
+               if (!accept(T_COLONCOLON))
                        break;
 
                switch (entity->kind) {
@@ -6071,7 +6077,7 @@ static entity_t *parse_qualified_identifier(void)
                               symbol, get_entity_kind_name(entity->kind));
 
                        /* skip further qualifications */
-                       while (next_if(T_IDENTIFIER) && next_if(T_COLONCOLON)) {}
+                       while (accept(T_IDENTIFIER) && accept(T_COLONCOLON)) {}
 
                        return create_error_entity(sym_anonymous, ENTITY_VARIABLE);
                }
@@ -6181,21 +6187,24 @@ static bool semantic_cast(expression_t *cast)
        return true;
 }
 
-static expression_t *parse_compound_literal(source_position_t const *const pos, type_t *type)
+static expression_t *parse_compound_literal(source_position_t const *const pos,
+                                            type_t *type)
 {
        expression_t *expression = allocate_expression_zero(EXPR_COMPOUND_LITERAL);
        expression->base.source_position = *pos;
+       bool global_scope = current_scope == file_scope;
 
        parse_initializer_env_t env;
        env.type             = type;
        env.entity           = NULL;
-       env.must_be_constant = false;
+       env.must_be_constant = global_scope;
        initializer_t *initializer = parse_initializer(&env);
        type = env.type;
 
-       expression->compound_literal.initializer = initializer;
-       expression->compound_literal.type        = type;
-       expression->base.type                    = automatic_type_conversion(type);
+       expression->base.type                     = automatic_type_conversion(type);
+       expression->compound_literal.initializer  = initializer;
+       expression->compound_literal.type         = type;
+       expression->compound_literal.global_scope = global_scope;
 
        return expression;
 }
@@ -6299,7 +6308,7 @@ static expression_t *parse_parenthesized_expression(void)
 static expression_t *parse_function_keyword(funcname_kind_t const kind)
 {
        if (current_function == NULL) {
-               errorf(HERE, "'%K' used outside of a function", &token);
+               errorf(HERE, "%K used outside of a function", &token);
        }
 
        expression_t *expression  = allocate_expression_zero(EXPR_FUNCNAME);
@@ -6320,7 +6329,7 @@ static designator_t *parse_designator(void)
 
        designator_t *last_designator = result;
        while (true) {
-               if (next_if('.')) {
+               if (accept('.')) {
                        designator_t *const designator = allocate_ast_zero(sizeof(result[0]));
                        designator->symbol = expect_identifier("while parsing member designator", &designator->source_position);
                        if (!designator->symbol)
@@ -6330,7 +6339,7 @@ static designator_t *parse_designator(void)
                        last_designator       = designator;
                        continue;
                }
-               if (next_if('[')) {
+               if (accept('[')) {
                        add_anchor_token(']');
                        designator_t *designator    = allocate_ast_zero(sizeof(result[0]));
                        designator->source_position = *HERE;
@@ -6674,7 +6683,7 @@ static expression_t *parse_noop_expression(void)
 
                if (token.kind != ')') do {
                        (void)parse_assignment_expression();
-               } while (next_if(','));
+               } while (accept(','));
 
                rem_anchor_token(',');
                rem_anchor_token(')');
@@ -6778,7 +6787,8 @@ static expression_t *parse_array_expression(expression_t *left)
 check_idx:
                res_type = automatic_type_conversion(res_type);
                if (!is_type_integer(idx_type)) {
-                       errorf(&idx->base.source_position, "array subscript must have integer type");
+                       if (is_type_valid(idx_type))
+                               errorf(&idx->base.source_position, "array subscript must have integer type");
                } else if (is_type_atomic(idx_type, ATOMIC_TYPE_CHAR)) {
                        source_position_t const *const pos = &idx->base.source_position;
                        warningf(WARN_CHAR_SUBSCRIPTS, pos, "array subscript has char type");
@@ -7105,7 +7115,7 @@ static expression_t *parse_call_expression(expression_t *expression)
 
                        *anchor = argument;
                        anchor  = &argument->next;
-               } while (next_if(','));
+               } while (accept(','));
        }
        rem_anchor_token(',');
        rem_anchor_token(')');
@@ -7388,7 +7398,7 @@ static expression_t *parse_delete(void)
 
        eat(T_delete);
 
-       if (next_if('[')) {
+       if (accept('[')) {
                result->kind = EXPR_UNARY_DELETE_ARRAY;
                expect(']');
        }
@@ -7536,9 +7546,8 @@ static void semantic_unexpr_arithmetic(unary_expression_t *expression)
        type_t *const type      = skip_typeref(orig_type);
        if (!is_type_arithmetic(type)) {
                if (is_type_valid(type)) {
-                       /* TODO: improve error message */
-                       errorf(&expression->base.source_position,
-                               "operation needs an arithmetic type");
+                       source_position_t const *const pos = &expression->base.source_position;
+                       errorf(pos, "operand of unary expression must have arithmetic type, but is '%T'", orig_type);
                }
                return;
        } else if (is_type_integer(type)) {
@@ -7773,10 +7782,9 @@ static void semantic_binexpr_arithmetic(binary_expression_t *expression)
        type_t       *const type_right      = skip_typeref(orig_type_right);
 
        if (!is_type_arithmetic(type_left) || !is_type_arithmetic(type_right)) {
-               /* TODO: improve error message */
                if (is_type_valid(type_left) && is_type_valid(type_right)) {
-                       errorf(&expression->base.source_position,
-                              "operation needs arithmetic types");
+                       source_position_t const *const pos = &expression->base.source_position;
+                       errorf(pos, "operands of binary expression must have arithmetic types, but are '%T' and '%T'", orig_type_left, orig_type_right);
                }
                return;
        }
@@ -7797,10 +7805,9 @@ static void semantic_binexpr_integer(binary_expression_t *const expression)
        type_t       *const type_right      = skip_typeref(orig_type_right);
 
        if (!is_type_integer(type_left) || !is_type_integer(type_right)) {
-               /* TODO: improve error message */
                if (is_type_valid(type_left) && is_type_valid(type_right)) {
-                       errorf(&expression->base.source_position,
-                              "operation needs integer types");
+                       source_position_t const *const pos = &expression->base.source_position;
+                       errorf(pos, "operands of binary expression must have integer types, but are '%T' and '%T'", orig_type_left, orig_type_right);
                }
                return;
        }
@@ -7827,14 +7834,23 @@ static void warn_div_by_zero(binary_expression_t const *const expression)
 }
 
 /**
- * Check the semantic restrictions for a div/mod expression.
+ * Check the semantic restrictions for a div expression.
  */
-static void semantic_divmod_arithmetic(binary_expression_t *expression)
+static void semantic_div(binary_expression_t *expression)
 {
        semantic_binexpr_arithmetic(expression);
        warn_div_by_zero(expression);
 }
 
+/**
+ * Check the semantic restrictions for a mod expression.
+ */
+static void semantic_mod(binary_expression_t *expression)
+{
+       semantic_binexpr_integer(expression);
+       warn_div_by_zero(expression);
+}
+
 static void warn_addsub_in_shift(const expression_t *const expr)
 {
        if (expr->base.parenthesized)
@@ -7861,10 +7877,9 @@ static bool semantic_shift(binary_expression_t *expression)
        type_t       *      type_right      = skip_typeref(orig_type_right);
 
        if (!is_type_integer(type_left) || !is_type_integer(type_right)) {
-               /* TODO: improve error message */
                if (is_type_valid(type_left) && is_type_valid(type_right)) {
-                       errorf(&expression->base.source_position,
-                              "operands of shift operation must have integer types");
+                       source_position_t const *const pos = &expression->base.source_position;
+                       errorf(pos, "operands of shift expression must have integer types, but are '%T' and '%T'", orig_type_left, orig_type_right);
                }
                return false;
        }
@@ -8468,8 +8483,8 @@ static expression_t *parse_##binexpression_type(expression_t *left)          \
 }
 
 CREATE_BINEXPR_PARSER('*',                    EXPR_BINARY_MUL,                PREC_CAST,           semantic_binexpr_arithmetic)
-CREATE_BINEXPR_PARSER('/',                    EXPR_BINARY_DIV,                PREC_CAST,           semantic_divmod_arithmetic)
-CREATE_BINEXPR_PARSER('%',                    EXPR_BINARY_MOD,                PREC_CAST,           semantic_divmod_arithmetic)
+CREATE_BINEXPR_PARSER('/',                    EXPR_BINARY_DIV,                PREC_CAST,           semantic_div)
+CREATE_BINEXPR_PARSER('%',                    EXPR_BINARY_MOD,                PREC_CAST,           semantic_mod)
 CREATE_BINEXPR_PARSER('+',                    EXPR_BINARY_ADD,                PREC_MULTIPLICATIVE, semantic_add)
 CREATE_BINEXPR_PARSER('-',                    EXPR_BINARY_SUB,                PREC_MULTIPLICATIVE, semantic_sub)
 CREATE_BINEXPR_PARSER(T_LESSLESS,             EXPR_BINARY_SHIFTLEFT,          PREC_ADDITIVE,       semantic_shift_op)
@@ -8631,120 +8646,131 @@ static void init_expression_parsers(void)
 /**
  * Parse a asm statement arguments specification.
  */
-static asm_argument_t *parse_asm_arguments(bool is_out)
+static void parse_asm_arguments(asm_argument_t **anchor, bool const is_out)
 {
-       asm_argument_t  *result = NULL;
-       asm_argument_t **anchor = &result;
-
-       while (token.kind == T_STRING_LITERAL || token.kind == '[') {
-               asm_argument_t *argument = allocate_ast_zero(sizeof(argument[0]));
+       if (token.kind == T_STRING_LITERAL || token.kind == '[') {
+               add_anchor_token(',');
+               do {
+                       asm_argument_t *argument = allocate_ast_zero(sizeof(argument[0]));
 
-               if (next_if('[')) {
-                       add_anchor_token(']');
-                       argument->symbol = expect_identifier("while parsing asm argument", NULL);
-                       rem_anchor_token(']');
-                       expect(']');
-                       if (!argument->symbol)
-                               return NULL;
-               }
+                       add_anchor_token(')');
+                       add_anchor_token('(');
+                       add_anchor_token(T_STRING_LITERAL);
 
-               argument->constraints = parse_string_literals("asm argument");
-               add_anchor_token(')');
-               expect('(');
-               expression_t *expression = parse_expression();
-               rem_anchor_token(')');
-               if (is_out) {
-                       /* Ugly GCC stuff: Allow lvalue casts.  Skip casts, when they do not
-                        * change size or type representation (e.g. int -> long is ok, but
-                        * int -> float is not) */
-                       if (expression->kind == EXPR_UNARY_CAST) {
-                               type_t      *const type = expression->base.type;
-                               type_kind_t  const kind = type->kind;
-                               if (kind == TYPE_ATOMIC || kind == TYPE_POINTER) {
-                                       unsigned flags;
-                                       unsigned size;
-                                       if (kind == TYPE_ATOMIC) {
-                                               atomic_type_kind_t const akind = type->atomic.akind;
-                                               flags = get_atomic_type_flags(akind) & ~ATOMIC_TYPE_FLAG_SIGNED;
-                                               size  = get_atomic_type_size(akind);
-                                       } else {
-                                               flags = ATOMIC_TYPE_FLAG_INTEGER | ATOMIC_TYPE_FLAG_ARITHMETIC;
-                                               size  = get_type_size(type_void_ptr);
-                                       }
+                       if (accept('[')) {
+                               add_anchor_token(']');
+                               argument->symbol = expect_identifier("while parsing asm argument", NULL);
+                               rem_anchor_token(']');
+                               expect(']');
+                       }
 
-                                       do {
-                                               expression_t *const value      = expression->unary.value;
-                                               type_t       *const value_type = value->base.type;
-                                               type_kind_t   const value_kind = value_type->kind;
-
-                                               unsigned value_flags;
-                                               unsigned value_size;
-                                               if (value_kind == TYPE_ATOMIC) {
-                                                       atomic_type_kind_t const value_akind = value_type->atomic.akind;
-                                                       value_flags = get_atomic_type_flags(value_akind) & ~ATOMIC_TYPE_FLAG_SIGNED;
-                                                       value_size  = get_atomic_type_size(value_akind);
-                                               } else if (value_kind == TYPE_POINTER) {
-                                                       value_flags = ATOMIC_TYPE_FLAG_INTEGER | ATOMIC_TYPE_FLAG_ARITHMETIC;
-                                                       value_size  = get_type_size(type_void_ptr);
+                       rem_anchor_token(T_STRING_LITERAL);
+                       argument->constraints = parse_string_literals("asm argument");
+                       rem_anchor_token('(');
+                       expect('(');
+                       expression_t *expression = parse_expression();
+                       if (is_out) {
+                               /* Ugly GCC stuff: Allow lvalue casts.  Skip casts, when they do not
+                                * change size or type representation (e.g. int -> long is ok, but
+                                * int -> float is not) */
+                               if (expression->kind == EXPR_UNARY_CAST) {
+                                       type_t      *const type = expression->base.type;
+                                       type_kind_t  const kind = type->kind;
+                                       if (kind == TYPE_ATOMIC || kind == TYPE_POINTER) {
+                                               unsigned flags;
+                                               unsigned size;
+                                               if (kind == TYPE_ATOMIC) {
+                                                       atomic_type_kind_t const akind = type->atomic.akind;
+                                                       flags = get_atomic_type_flags(akind) & ~ATOMIC_TYPE_FLAG_SIGNED;
+                                                       size  = get_atomic_type_size(akind);
                                                } else {
-                                                       break;
+                                                       flags = ATOMIC_TYPE_FLAG_INTEGER | ATOMIC_TYPE_FLAG_ARITHMETIC;
+                                                       size  = get_type_size(type_void_ptr);
                                                }
 
-                                               if (value_flags != flags || value_size != size)
-                                                       break;
+                                               do {
+                                                       expression_t *const value      = expression->unary.value;
+                                                       type_t       *const value_type = value->base.type;
+                                                       type_kind_t   const value_kind = value_type->kind;
+
+                                                       unsigned value_flags;
+                                                       unsigned value_size;
+                                                       if (value_kind == TYPE_ATOMIC) {
+                                                               atomic_type_kind_t const value_akind = value_type->atomic.akind;
+                                                               value_flags = get_atomic_type_flags(value_akind) & ~ATOMIC_TYPE_FLAG_SIGNED;
+                                                               value_size  = get_atomic_type_size(value_akind);
+                                                       } else if (value_kind == TYPE_POINTER) {
+                                                               value_flags = ATOMIC_TYPE_FLAG_INTEGER | ATOMIC_TYPE_FLAG_ARITHMETIC;
+                                                               value_size  = get_type_size(type_void_ptr);
+                                                       } else {
+                                                               break;
+                                                       }
 
-                                               expression = value;
-                                       } while (expression->kind == EXPR_UNARY_CAST);
+                                                       if (value_flags != flags || value_size != size)
+                                                               break;
+
+                                                       expression = value;
+                                               } while (expression->kind == EXPR_UNARY_CAST);
+                                       }
                                }
-                       }
 
-                       if (!is_lvalue(expression)) {
-                               errorf(&expression->base.source_position,
-                                      "asm output argument is not an lvalue");
-                       }
+                               if (!is_lvalue(expression))
+                                       errorf(&expression->base.source_position, "asm output argument is not an lvalue");
 
-                       if (argument->constraints.begin[0] == '=')
-                               determine_lhs_ent(expression, NULL);
-                       else
+                               if (argument->constraints.begin[0] == '=')
+                                       determine_lhs_ent(expression, NULL);
+                               else
+                                       mark_vars_read(expression, NULL);
+                       } else {
                                mark_vars_read(expression, NULL);
-               } else {
-                       mark_vars_read(expression, NULL);
-               }
-               argument->expression = expression;
-               expect(')');
-
-               set_address_taken(expression, true);
+                       }
+                       argument->expression = expression;
+                       rem_anchor_token(')');
+                       expect(')');
 
-               *anchor = argument;
-               anchor  = &argument->next;
+                       set_address_taken(expression, true);
 
-               if (!next_if(','))
-                       break;
+                       *anchor = argument;
+                       anchor  = &argument->next;
+               } while (accept(','));
+               rem_anchor_token(',');
        }
-
-       return result;
 }
 
 /**
  * Parse a asm statement clobber specification.
  */
-static asm_clobber_t *parse_asm_clobbers(void)
+static void parse_asm_clobbers(asm_clobber_t **anchor)
 {
-       asm_clobber_t *result  = NULL;
-       asm_clobber_t **anchor = &result;
+       if (token.kind == T_STRING_LITERAL) {
+               add_anchor_token(',');
+               do {
+                       asm_clobber_t *clobber = allocate_ast_zero(sizeof(clobber[0]));
+                       clobber->clobber       = parse_string_literals(NULL);
 
-       while (token.kind == T_STRING_LITERAL) {
-               asm_clobber_t *clobber = allocate_ast_zero(sizeof(clobber[0]));
-               clobber->clobber       = parse_string_literals(NULL);
+                       *anchor = clobber;
+                       anchor  = &clobber->next;
+               } while (accept(','));
+               rem_anchor_token(',');
+       }
+}
 
-               *anchor = clobber;
-               anchor  = &clobber->next;
+static void parse_asm_labels(asm_label_t **anchor)
+{
+       if (token.kind == T_IDENTIFIER) {
+               add_anchor_token(',');
+               do {
+                       label_t *const label = get_label("while parsing 'asm goto' labels");
+                       if (label) {
+                               asm_label_t *const asm_label = allocate_ast_zero(sizeof(*asm_label));
+                               asm_label->label = label;
 
-               if (!next_if(','))
-                       break;
+                               *anchor = asm_label;
+                               anchor  = &asm_label->next;
+                       }
+               } while (accept(','));
+               rem_anchor_token(',');
        }
-
-       return result;
 }
 
 /**
@@ -8760,22 +8786,30 @@ static statement_t *parse_asm_statement(void)
        add_anchor_token(':');
        add_anchor_token(T_STRING_LITERAL);
 
-       if (next_if(T_volatile))
+       if (accept(T_volatile))
                asm_statement->is_volatile = true;
 
+       bool const asm_goto = accept(T_goto);
+
        expect('(');
        rem_anchor_token(T_STRING_LITERAL);
        asm_statement->asm_text = parse_string_literals("asm statement");
 
-       if (next_if(':'))
-               asm_statement->outputs = parse_asm_arguments(true);
-
-       if (next_if(':'))
-               asm_statement->inputs = parse_asm_arguments(false);
+       if (accept(':')) parse_asm_arguments(&asm_statement->outputs, true);
+       if (accept(':')) parse_asm_arguments(&asm_statement->inputs, false);
+       if (accept(':')) parse_asm_clobbers( &asm_statement->clobbers);
 
        rem_anchor_token(':');
-       if (next_if(':'))
-               asm_statement->clobbers = parse_asm_clobbers();
+       if (accept(':')) {
+               if (!asm_goto)
+                       warningf(WARN_OTHER, &statement->base.source_position, "assembler statement with labels should be 'asm goto'");
+               parse_asm_labels(&asm_statement->labels);
+               if (asm_statement->labels)
+                       errorf(&statement->base.source_position, "'asm goto' not supported");
+       } else {
+               if (asm_goto)
+                       warningf(WARN_OTHER, &statement->base.source_position, "'asm goto' without labels");
+       }
 
        rem_anchor_token(')');
        expect(')');
@@ -8863,7 +8897,7 @@ static statement_t *parse_case_statement(void)
        }
 
        if (GNU_MODE) {
-               if (next_if(T_DOTDOTDOT)) {
+               if (accept(T_DOTDOTDOT)) {
                        expression_t *end_range = parse_expression();
                        expression_type = expression->base.type;
                        skipped         = skip_typeref(expression_type);
@@ -9063,7 +9097,7 @@ static statement_t *parse_if(void)
                        "suggest braces around empty body in an ‘if’ statement");
        }
 
-       if (next_if(T_else)) {
+       if (accept(T_else)) {
                statement->ifs.false_statement = parse_inner_statement();
 
                if (statement->ifs.false_statement->kind == STATEMENT_EMPTY) {
@@ -9250,7 +9284,7 @@ static statement_t *parse_for(void)
 
        PUSH_EXTENSION();
 
-       if (next_if(';')) {
+       if (accept(';')) {
        } else if (is_declaration_specifier(&token)) {
                parse_declaration(record_entity, DECL_FLAGS_NONE);
        } else {
@@ -9548,7 +9582,7 @@ static statement_t *parse_ms_try_statment(void)
 
        POP_PARENT();
 
-       if (next_if(T___except)) {
+       if (accept(T___except)) {
                expression_t *const expr = parse_condition();
                type_t       *      type = skip_typeref(expr->base.type);
                if (is_type_integer(type)) {
@@ -9559,7 +9593,7 @@ static statement_t *parse_ms_try_statment(void)
                        type = type_error_type;
                }
                statement->ms_try.except_expression = create_implicit_cast(expr, type);
-       } else if (!next_if(T__finally)) {
+       } else if (!accept(T__finally)) {
                parse_error_expected("while parsing __try statement", T___except, T___finally, NULL);
        }
        statement->ms_try.final_statement = parse_compound_statement(false);
@@ -9604,7 +9638,7 @@ static statement_t *parse_local_label_declaration(void)
                                environment_push(entity);
                        }
                }
-       } while (next_if(','));
+       } while (accept(','));
        rem_anchor_token(',');
        rem_anchor_token(';');
        expect(';');
@@ -10068,7 +10102,7 @@ static void parse_linkage_specification(void)
        }
        current_linkage = new_linkage;
 
-       if (next_if('{')) {
+       if (accept('{')) {
                parse_externals();
                expect('}');
        } else {