simplify testcase
[cparser] / parser.c
index 79122e5..fa04afb 100644 (file)
--- a/parser.c
+++ b/parser.c
@@ -67,7 +67,7 @@ struct declaration_specifiers_t {
        unsigned char      alignment;         /**< Alignment, 0 if not set. */
        unsigned int       is_inline : 1;
        unsigned int       deprecated : 1;
-       decl_modifiers_t   decl_modifiers;    /**< MS __declspec extended modifier mask */
+       decl_modifiers_t   modifiers;         /**< declaration modifiers */
        gnu_attribute_t   *gnu_attributes;    /**< list of GNU attributes */
        const char        *deprecated_string; /**< can be set if declaration was marked deprecated. */
        symbol_t          *get_property_sym;  /**< the name of the get property if set. */
@@ -104,6 +104,7 @@ static goto_statement_t   *goto_first        = NULL;
 static goto_statement_t   *goto_last         = NULL;
 static label_statement_t  *label_first       = NULL;
 static label_statement_t  *label_last        = NULL;
+static translation_unit_t *unit              = NULL;
 static struct obstack      temp_obst;
 
 static source_position_t null_position = { NULL, 0 };
@@ -1096,7 +1097,6 @@ static gnu_attribute_t *allocate_gnu_attribute(gnu_attribute_kind_t kind) {
        attribute->have_arguments  = false;
 
        return attribute;
-       return attribute;
 }
 
 /**
@@ -1311,6 +1311,19 @@ end_error:
        attribute->u.value = true;
 }
 
+static void check_no_argument(gnu_attribute_t *attribute, const char *name)
+{
+       if(!attribute->have_arguments)
+               return;
+
+       /* should have no arguments */
+       errorf(HERE, "wrong number of arguments specified for '%s' attribute", name);
+       eat_until_matching_token('(');
+       /* we have already consumed '(', so we stop before ')', eat it */
+       eat(')');
+       attribute->invalid = true;
+}
+
 /**
  * Parse one GNU attribute.
  *
@@ -1387,10 +1400,11 @@ end_error:
  *  interrupt( string literal )
  *  sentinel( constant expression )
  */
-static void parse_gnu_attribute(gnu_attribute_t **attributes)
+static decl_modifiers_t parse_gnu_attribute(gnu_attribute_t **attributes)
 {
-       gnu_attribute_t *head = *attributes;
-       gnu_attribute_t *last = *attributes;
+       gnu_attribute_t *head      = *attributes;
+       gnu_attribute_t *last      = *attributes;
+       decl_modifiers_t modifiers = 0;
        gnu_attribute_t *attribute;
 
        eat(T___attribute__);
@@ -1457,20 +1471,11 @@ static void parse_gnu_attribute(gnu_attribute_t **attributes)
                                case GNU_AK_STDCALL:
                                case GNU_AK_FASTCALL:
                                case GNU_AK_DEPRECATED:
-                               case GNU_AK_NOINLINE:
-                               case GNU_AK_NORETURN:
                                case GNU_AK_NAKED:
-                               case GNU_AK_PURE:
-                               case GNU_AK_ALWAYS_INLINE:
                                case GNU_AK_MALLOC:
                                case GNU_AK_WEAK:
-                               case GNU_AK_CONSTRUCTOR:
-                               case GNU_AK_DESTRUCTOR:
-                               case GNU_AK_NOTHROW:
-                               case GNU_AK_TRANSPARENT_UNION:
                                case GNU_AK_COMMON:
                                case GNU_AK_NOCOMMON:
-                               case GNU_AK_PACKED:
                                case GNU_AK_SHARED:
                                case GNU_AK_NOTSHARED:
                                case GNU_AK_USED:
@@ -1498,16 +1503,62 @@ static void parse_gnu_attribute(gnu_attribute_t **attributes)
                                case GNU_AK_MAY_ALIAS:
                                case GNU_AK_MS_STRUCT:
                                case GNU_AK_GCC_STRUCT:
+                                       check_no_argument(attribute, name);
+                                       break;
+
+                               case GNU_AK_PURE:
+                                       check_no_argument(attribute, name);
+                                       modifiers |= DM_PURE;
+                                       break;
+
+                               case GNU_AK_ALWAYS_INLINE:
+                                       check_no_argument(attribute, name);
+                                       modifiers |= DM_FORCEINLINE;
+                                       break;
+
                                case GNU_AK_DLLIMPORT:
+                                       check_no_argument(attribute, name);
+                                       modifiers |= DM_DLLIMPORT;
+                                       break;
+
                                case GNU_AK_DLLEXPORT:
-                                       if(attribute->have_arguments) {
-                                               /* should have no arguments */
-                                               errorf(HERE, "wrong number of arguments specified for '%s' attribute", name);
-                                               eat_until_matching_token('(');
-                                               /* we have already consumed '(', so we stop before ')', eat it */
-                                               eat(')');
-                                               attribute->invalid = true;
-                                       }
+                                       check_no_argument(attribute, name);
+                                       modifiers |= DM_DLLEXPORT;
+                                       break;
+
+                               case GNU_AK_PACKED:
+                                       check_no_argument(attribute, name);
+                                       modifiers |= DM_PACKED;
+                                       break;
+
+                               case GNU_AK_NOINLINE:
+                                       check_no_argument(attribute, name);
+                                       modifiers |= DM_NOINLINE;
+                                       break;
+
+                               case GNU_AK_NORETURN:
+                                       check_no_argument(attribute, name);
+                                       modifiers |= DM_NORETURN;
+                                       break;
+
+                               case GNU_AK_NOTHROW:
+                                       check_no_argument(attribute, name);
+                                       modifiers |= DM_NOTHROW;
+                                       break;
+
+                               case GNU_AK_TRANSPARENT_UNION:
+                                       check_no_argument(attribute, name);
+                                       modifiers |= DM_TRANSPARENT_UNION;
+                                       break;
+
+                               case GNU_AK_CONSTRUCTOR:
+                                       check_no_argument(attribute, name);
+                                       modifiers |= DM_CONSTRUCTOR;
+                                       break;
+
+                               case GNU_AK_DESTRUCTOR:
+                                       check_no_argument(attribute, name);
+                                       modifiers |= DM_DESTRUCTOR;
                                        break;
 
                                case GNU_AK_ALIGNED:
@@ -1611,17 +1662,21 @@ static void parse_gnu_attribute(gnu_attribute_t **attributes)
        expect(')');
 end_error:
        *attributes = head;
+
+       return modifiers;
 }
 
 /**
  * Parse GNU attributes.
  */
-static void parse_attributes(gnu_attribute_t **attributes)
+static decl_modifiers_t parse_attributes(gnu_attribute_t **attributes)
 {
+       decl_modifiers_t modifiers = 0;
+
        while(true) {
                switch(token.type) {
                case T___attribute__: {
-                       parse_gnu_attribute(attributes);
+                       modifiers |= parse_gnu_attribute(attributes);
                        break;
                }
                case T_asm:
@@ -1644,7 +1699,7 @@ static void parse_attributes(gnu_attribute_t **attributes)
 
 attributes_finished:
 end_error:
-       return;
+       return modifiers;
 }
 
 static designator_t *parse_designation(void)
@@ -2187,6 +2242,9 @@ static initializer_t *parse_sub_initializer(type_path_t *path,
                                = allocate_initializer_zero(INITIALIZER_DESIGNATOR);
                        designator_initializer->designator.designator = designator;
                        ARR_APP1(initializer_t*, initializers, designator_initializer);
+
+                       orig_type = path->top_type;
+                       type      = orig_type != NULL ? skip_typeref(orig_type) : NULL;
                }
 
                initializer_t *sub;
@@ -2390,6 +2448,12 @@ static initializer_t *parse_initializer(parse_initializer_env_t *env)
                        size = result->wide_string.string.size;
                        break;
 
+               case INITIALIZER_DESIGNATOR:
+               case INITIALIZER_VALUE:
+                       /* can happen for parse errors */
+                       size = 0;
+                       break;
+
                default:
                        internal_errorf(HERE, "invalid initializer type");
                }
@@ -2415,7 +2479,8 @@ static declaration_t *append_declaration(declaration_t *declaration);
 
 static declaration_t *parse_compound_type_specifier(bool is_struct)
 {
-       gnu_attribute_t *attributes = NULL;
+       gnu_attribute_t  *attributes = NULL;
+       decl_modifiers_t  modifiers  = 0;
        if(is_struct) {
                eat(T_struct);
        } else {
@@ -2426,7 +2491,7 @@ static declaration_t *parse_compound_type_specifier(bool is_struct)
        declaration_t *declaration = NULL;
 
        if (token.type == T___attribute__) {
-               parse_attributes(&attributes);
+               modifiers |= parse_attributes(&attributes);
        }
 
        if(token.type == T_IDENTIFIER) {
@@ -2466,16 +2531,18 @@ static declaration_t *parse_compound_type_specifier(bool is_struct)
        if(token.type == '{') {
                if (declaration->init.complete) {
                        assert(symbol != NULL);
-                       errorf(HERE, "multiple definitions of '%s %Y'",
-                              is_struct ? "struct" : "union", symbol);
+                       errorf(HERE, "multiple definitions of '%s %Y' (previous definition at %P)",
+                              is_struct ? "struct" : "union", symbol,
+                              &declaration->source_position);
                        declaration->scope.declarations = NULL;
                }
                declaration->init.complete = true;
 
                parse_compound_type_entries(declaration);
-               parse_attributes(&attributes);
+               modifiers |= parse_attributes(&attributes);
        }
 
+       declaration->modifiers |= modifiers;
        return declaration;
 }
 
@@ -2715,7 +2782,7 @@ static bool check_elignment_value(long long intvalue) {
 
 static void parse_microsoft_extended_decl_modifier(declaration_specifiers_t *specifiers)
 {
-       decl_modifiers_t *modifiers = &specifiers->decl_modifiers;
+       decl_modifiers_t *modifiers = &specifiers->modifiers;
 
        while(true) {
                if(token.type == T_restrict) {
@@ -2958,7 +3025,7 @@ static void parse_declaration_specifiers(declaration_specifiers_t *specifiers)
 
                case T__forceinline:
                        /* only in microsoft mode */
-                       specifiers->decl_modifiers |= DM_FORCEINLINE;
+                       specifiers->modifiers |= DM_FORCEINLINE;
 
                case T_inline:
                        next_token();
@@ -3000,7 +3067,8 @@ static void parse_declaration_specifiers(declaration_specifiers_t *specifiers)
                        break;
 
                case T___attribute__:
-                       parse_attributes(&specifiers->gnu_attributes);
+                       specifiers->modifiers
+                               |= parse_attributes(&specifiers->gnu_attributes);
                        break;
 
                case T_IDENTIFIER: {
@@ -3519,7 +3587,9 @@ static construct_type_t *parse_inner_declarator(declaration_t *declaration,
        }
 
        /* TODO: find out if this is correct */
-       parse_attributes(&attributes);
+       decl_modifiers_t modifiers = parse_attributes(&attributes);
+       if (declaration != NULL)
+               declaration->modifiers |= modifiers;
 
        construct_type_t *inner_types = NULL;
 
@@ -3583,7 +3653,9 @@ static construct_type_t *parse_inner_declarator(declaration_t *declaration,
        }
 
 declarator_finished:
-       parse_attributes(&attributes);
+       modifiers = parse_attributes(&attributes);
+       if (declaration != NULL)
+               declaration->modifiers |= modifiers;
 
        /* append inner_types at the end of the list, we don't to set last anymore
         * as it's not needed anymore */
@@ -3694,7 +3766,7 @@ static declaration_t *parse_declarator(
 {
        declaration_t *const declaration    = allocate_declaration_zero();
        declaration->declared_storage_class = specifiers->declared_storage_class;
-       declaration->decl_modifiers         = specifiers->decl_modifiers;
+       declaration->modifiers              = specifiers->modifiers;
        declaration->deprecated             = specifiers->deprecated;
        declaration->deprecated_string      = specifiers->deprecated_string;
        declaration->get_property_sym       = specifiers->get_property_sym;
@@ -3867,6 +3939,11 @@ static declaration_t *internal_record_declaration(
                         * declarations (except if the previous declaration is neither
                         * none nor extern) */
                        if (is_type_function(type)) {
+                               if (prev_type->function.unspecified_parameters) {
+                                       previous_declaration->type = type;
+                                       prev_type                  = type;
+                               }
+
                                switch (old_storage_class) {
                                case STORAGE_CLASS_NONE:
                                        old_storage_class = STORAGE_CLASS_EXTERN;
@@ -3893,7 +3970,7 @@ static declaration_t *internal_record_declaration(
                        if (old_storage_class == STORAGE_CLASS_EXTERN &&
                                        new_storage_class == STORAGE_CLASS_EXTERN) {
 warn_redundant_declaration:
-                               if (warning.redundant_decls) {
+                               if (warning.redundant_decls && strcmp(previous_declaration->source_position.input_name, "<builtin>") != 0) {
                                        warningf(&declaration->source_position,
                                                         "redundant declaration for '%Y' (declared %P)",
                                                         symbol, &previous_declaration->source_position);
@@ -4042,7 +4119,7 @@ static void parse_anonymous_declaration_rest(
        declaration->type                   = specifiers->type;
        declaration->declared_storage_class = specifiers->declared_storage_class;
        declaration->source_position        = specifiers->source_position;
-       declaration->decl_modifiers         = specifiers->decl_modifiers;
+       declaration->modifiers              = specifiers->modifiers;
 
        if (declaration->declared_storage_class != STORAGE_CLASS_NONE) {
                warningf(&declaration->source_position,
@@ -4420,6 +4497,10 @@ static void parse_external_declaration(void)
                assert(parameter->parent_scope == NULL
                                || parameter->parent_scope == scope);
                parameter->parent_scope = scope;
+               if (parameter->symbol == NULL) {
+                       errorf(&ndeclaration->source_position, "parameter name omitted");
+                       continue;
+               }
                environment_push(parameter);
        }
 
@@ -4519,7 +4600,7 @@ static void parse_compound_declarators(declaration_t *struct_declaration,
                        declaration->declared_storage_class = STORAGE_CLASS_NONE;
                        declaration->storage_class          = STORAGE_CLASS_NONE;
                        declaration->source_position        = source_position;
-                       declaration->decl_modifiers         = specifiers->decl_modifiers;
+                       declaration->modifiers              = specifiers->modifiers;
                        declaration->type                   = type;
                } else {
                        declaration = parse_declarator(specifiers,/*may_be_abstract=*/true);
@@ -5984,12 +6065,33 @@ static expression_t *parse_conditional_expression(unsigned precedence,
                        other_expression = true_expression;
                }
 
-               if(is_type_pointer(other_type)) {
-                       if(!pointers_compatible(true_type, false_type)) {
+               /* TODO Treat (void*)0 as null pointer constant */
+               if (is_type_pointer(other_type)) {
+                       type_t *to1 = pointer_type->pointer.points_to;
+                       type_t *to2 = other_type->pointer.points_to;
+
+                       type_t *to;
+                       if (is_type_atomic(to1, ATOMIC_TYPE_VOID) ||
+                           is_type_atomic(to2, ATOMIC_TYPE_VOID)) {
+                               to = type_void;
+                       } else if (types_compatible(get_unqualified_type(to1),
+                                                   get_unqualified_type(to2))) {
+                               to = to1;
+                       } else {
                                warningf(&expression->base.source_position,
-                                               "pointer types '%T' and '%T' in conditional expression are incompatible", true_type, false_type);
+                                       "pointer types '%T' and '%T' in conditional expression are incompatible",
+                                       true_type, false_type);
+                               to = type_void;
                        }
-                       result_type = true_type;
+
+                       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);
+
+                       result_type = make_pointer_type(type, TYPE_QUALIFIER_NONE);
                } else if(is_null_pointer_constant(other_expression)) {
                        result_type = pointer_type;
                } else if(is_type_integer(other_type)) {
@@ -6057,19 +6159,37 @@ end_error:
        return create_invalid_expression();
 }
 
+static void check_pointer_arithmetic(const source_position_t *source_position,
+                                     type_t *pointer_type,
+                                     type_t *orig_pointer_type)
+{
+       type_t *points_to = pointer_type->pointer.points_to;
+       points_to = skip_typeref(points_to);
+
+       if (is_type_incomplete(points_to) &&
+                       (! (c_mode & _GNUC)
+                        || !is_type_atomic(points_to, ATOMIC_TYPE_VOID))) {
+               errorf(source_position,
+                          "arithmetic with pointer to incomplete type '%T' not allowed",
+                          orig_pointer_type);
+       } else if (is_type_function(points_to)) {
+               errorf(source_position,
+                          "arithmetic with pointer to function type '%T' not allowed",
+                          orig_pointer_type);
+       }
+}
+
 static void semantic_incdec(unary_expression_t *expression)
 {
        type_t *const orig_type = expression->value->base.type;
        type_t *const type      = skip_typeref(orig_type);
-       /* TODO !is_type_real && !is_type_pointer */
-       if(!is_type_arithmetic(type) && type->kind != TYPE_POINTER) {
-               if (is_type_valid(type)) {
-                       /* TODO: improve error message */
-                       errorf(HERE, "operation needs an arithmetic or pointer type");
-               }
-               return;
+       if (is_type_pointer(type)) {
+               check_pointer_arithmetic(&expression->base.source_position,
+                                        type, orig_type);
+       } else if (!is_type_real(type) && is_type_valid(type)) {
+               /* TODO: improve error message */
+               errorf(HERE, "operation needs an arithmetic or pointer type");
        }
-
        expression->base.type = orig_type;
 }
 
@@ -6313,19 +6433,25 @@ static void semantic_add(binary_expression_t *expression)
        type_t       *const type_left       = skip_typeref(orig_type_left);
        type_t       *const type_right      = skip_typeref(orig_type_right);
 
-       /* § 5.6.5 */
-       if(is_type_arithmetic(type_left) && is_type_arithmetic(type_right)) {
+       /* § 6.5.6 */
+       if (is_type_arithmetic(type_left) && is_type_arithmetic(type_right)) {
                type_t *arithmetic_type = semantic_arithmetic(type_left, type_right);
                expression->left  = create_implicit_cast(left, arithmetic_type);
                expression->right = create_implicit_cast(right, arithmetic_type);
                expression->base.type = arithmetic_type;
                return;
-       } else if(is_type_pointer(type_left) && is_type_integer(type_right)) {
+       } else if (is_type_pointer(type_left) && is_type_integer(type_right)) {
+               check_pointer_arithmetic(&expression->base.source_position,
+                                        type_left, orig_type_left);
                expression->base.type = type_left;
-       } else if(is_type_pointer(type_right) && is_type_integer(type_left)) {
+       } else if (is_type_pointer(type_right) && is_type_integer(type_left)) {
+               check_pointer_arithmetic(&expression->base.source_position,
+                                        type_right, orig_type_right);
                expression->base.type = type_right;
        } else if (is_type_valid(type_left) && is_type_valid(type_right)) {
-               errorf(HERE, "invalid operands to binary + ('%T', '%T')", orig_type_left, orig_type_right);
+               errorf(&expression->base.source_position,
+                      "invalid operands to binary + ('%T', '%T')",
+                      orig_type_left, orig_type_right);
        }
 }
 
@@ -6346,17 +6472,29 @@ static void semantic_sub(binary_expression_t *expression)
                expression->base.type =  arithmetic_type;
                return;
        } else if(is_type_pointer(type_left) && is_type_integer(type_right)) {
+               check_pointer_arithmetic(&expression->base.source_position,
+                                        type_left, orig_type_left);
                expression->base.type = type_left;
        } else if(is_type_pointer(type_left) && is_type_pointer(type_right)) {
-               if(!pointers_compatible(type_left, type_right)) {
-                       errorf(HERE,
-                              "pointers to incompatible objects to binary '-' ('%T', '%T')",
+               type_t *const unqual_left  = get_unqualified_type(skip_typeref(type_left->pointer.points_to));
+               type_t *const unqual_right = get_unqualified_type(skip_typeref(type_right->pointer.points_to));
+               if (!types_compatible(unqual_left, unqual_right)) {
+                       errorf(&expression->base.source_position,
+                              "subtracting pointers to incompatible types '%T' and '%T'",
                               orig_type_left, orig_type_right);
-               } else {
-                       expression->base.type = type_ptrdiff_t;
+               } else if (!is_type_object(unqual_left)) {
+                       if (is_type_atomic(unqual_left, ATOMIC_TYPE_VOID)) {
+                               warningf(&expression->base.source_position,
+                                        "subtracting pointers to void");
+                       } else {
+                               errorf(&expression->base.source_position,
+                                      "subtracting pointers to non-object types '%T'",
+                                      orig_type_left);
+                       }
                }
+               expression->base.type = type_ptrdiff_t;
        } else if (is_type_valid(type_left) && is_type_valid(type_right)) {
-               errorf(HERE, "invalid operands to binary '-' ('%T', '%T')",
+               errorf(HERE, "invalid operands of types '%T' and '%T' to binary '-'",
                       orig_type_left, orig_type_right);
        }
 }
@@ -6453,21 +6591,29 @@ 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);
        type_t *const type_left      = skip_typeref(orig_type_left);
 
-       switch (left->kind)  {
-               case EXPR_REFERENCE:
-               case EXPR_ARRAY_ACCESS:
-               case EXPR_SELECT:
-               case EXPR_UNARY_DEREFERENCE:
-                       break;
-
-               default:
-                       errorf(HERE, "left hand side '%E' of assignment is not an lvalue", left);
-                       return false;
+       if (!is_lvalue(left)) {
+               errorf(HERE, "left hand side '%E' of assignment is not an lvalue",
+                      left);
+               return false;
        }
 
        if (is_type_array(type_left)) {
@@ -6544,6 +6690,8 @@ static void semantic_arithmetic_addsubb_assign(binary_expression_t *expression)
                expression->right     = create_implicit_cast(right, arithmetic_type);
                expression->base.type = type_left;
        } else if (is_type_pointer(type_left) && is_type_integer(type_right)) {
+               check_pointer_arithmetic(&expression->base.source_position,
+                                        type_left, orig_type_left);
                expression->base.type = type_left;
        } else if (is_type_valid(type_left) && is_type_valid(type_right)) {
                errorf(HERE, "incompatible types '%T' and '%T' in assignment", orig_type_left, orig_type_right);
@@ -6978,40 +7126,44 @@ static void init_expression_parsers(void)
 }
 
 /**
- * Parse a asm statement constraints specification.
+ * Parse a asm statement arguments specification.
  */
-static asm_constraint_t *parse_asm_constraints(void)
+static asm_argument_t *parse_asm_arguments(bool is_out)
 {
-       asm_constraint_t *result = NULL;
-       asm_constraint_t *last   = NULL;
+       asm_argument_t *result = NULL;
+       asm_argument_t *last   = NULL;
 
        while(token.type == T_STRING_LITERAL || token.type == '[') {
-               asm_constraint_t *constraint = allocate_ast_zero(sizeof(constraint[0]));
-               memset(constraint, 0, sizeof(constraint[0]));
+               asm_argument_t *argument = allocate_ast_zero(sizeof(argument[0]));
+               memset(argument, 0, sizeof(argument[0]));
 
                if(token.type == '[') {
                        eat('[');
                        if(token.type != T_IDENTIFIER) {
-                               parse_error_expected("while parsing asm constraint",
+                               parse_error_expected("while parsing asm argument",
                                                     T_IDENTIFIER, NULL);
                                return NULL;
                        }
-                       constraint->symbol = token.v.symbol;
+                       argument->symbol = token.v.symbol;
 
                        expect(']');
                }
 
-               constraint->constraints = parse_string_literals();
+               argument->constraints = parse_string_literals();
                expect('(');
-               constraint->expression = parse_expression();
+               argument->expression = parse_expression();
+               if (is_out && !is_lvalue(argument->expression)) {
+                       errorf(&argument->expression->base.source_position,
+                              "asm output argument is not an lvalue");
+               }
                expect(')');
 
                if(last != NULL) {
-                       last->next = constraint;
+                       last->next = argument;
                } else {
-                       result = constraint;
+                       result = argument;
                }
-               last = constraint;
+               last = argument;
 
                if(token.type != ',')
                        break;
@@ -7078,14 +7230,14 @@ static statement_t *parse_asm_statement(void)
        }
        eat(':');
 
-       asm_statement->inputs = parse_asm_constraints();
+       asm_statement->outputs = parse_asm_arguments(true);
        if(token.type != ':') {
                rem_anchor_token(':');
                goto end_of_asm;
        }
        eat(':');
 
-       asm_statement->outputs = parse_asm_constraints();
+       asm_statement->inputs = parse_asm_arguments(false);
        if(token.type != ':') {
                rem_anchor_token(':');
                goto end_of_asm;
@@ -7493,7 +7645,7 @@ static statement_t *parse_goto(void)
        if(token.type != T_IDENTIFIER) {
                parse_error_expected("while parsing goto", T_IDENTIFIER, NULL);
                eat_statement();
-               return NULL;
+               goto end_error;
        }
        symbol_t *symbol = token.v.symbol;
        next_token();
@@ -7686,7 +7838,8 @@ static statement_t *parse_return(void)
                        }
                }
                /* check for returning address of a local var */
-               if (return_value->base.kind == EXPR_UNARY_TAKE_ADDRESS) {
+               if (return_value != NULL &&
+                               return_value->base.kind == EXPR_UNARY_TAKE_ADDRESS) {
                        const expression_t *expression = return_value->unary.value;
                        if (is_local_variable(expression)) {
                                warningf(&statement->base.source_position,
@@ -8058,18 +8211,8 @@ static void check_unused_globals(void)
 /**
  * Parse a translation unit.
  */
-static translation_unit_t *parse_translation_unit(void)
+static void parse_translation_unit(void)
 {
-       translation_unit_t *unit = allocate_ast_zero(sizeof(unit[0]));
-
-       assert(global_scope == NULL);
-       global_scope = &unit->scope;
-
-       assert(scope == NULL);
-       set_scope(&unit->scope);
-
-       initialize_builtin_types();
-
        while(token.type != T_EOF) {
                if (token.type == ';') {
                        /* TODO error in strict mode */
@@ -8079,16 +8222,6 @@ static translation_unit_t *parse_translation_unit(void)
                        parse_external_declaration();
                }
        }
-
-       assert(scope == &unit->scope);
-       scope          = NULL;
-       last_declaration = NULL;
-
-       assert(global_scope == &unit->scope);
-       check_unused_globals();
-       global_scope = NULL;
-
-       return unit;
 }
 
 /**
@@ -8096,7 +8229,7 @@ static translation_unit_t *parse_translation_unit(void)
  *
  * @return  the translation unit or NULL if errors occurred.
  */
-translation_unit_t *parse(void)
+void start_parsing(void)
 {
        environment_stack = NEW_ARR_F(stack_entry_t, 0);
        label_stack       = NEW_ARR_F(stack_entry_t, 0);
@@ -8107,16 +8240,43 @@ translation_unit_t *parse(void)
        type_set_output(stderr);
        ast_set_output(stderr);
 
-       lookahead_bufpos = 0;
-       for(int i = 0; i < MAX_LOOKAHEAD + 2; ++i) {
-               next_token();
-       }
-       translation_unit_t *unit = parse_translation_unit();
+       assert(unit == NULL);
+       unit = allocate_ast_zero(sizeof(unit[0]));
+
+       assert(global_scope == NULL);
+       global_scope = &unit->scope;
+
+       assert(scope == NULL);
+       set_scope(&unit->scope);
+
+       initialize_builtin_types();
+}
+
+translation_unit_t *finish_parsing(void)
+{
+       assert(scope == &unit->scope);
+       scope          = NULL;
+       last_declaration = NULL;
+
+       assert(global_scope == &unit->scope);
+       check_unused_globals();
+       global_scope = NULL;
 
        DEL_ARR_F(environment_stack);
        DEL_ARR_F(label_stack);
 
-       return unit;
+       translation_unit_t *result = unit;
+       unit = NULL;
+       return result;
+}
+
+void parse(void)
+{
+       lookahead_bufpos = 0;
+       for(int i = 0; i < MAX_LOOKAHEAD + 2; ++i) {
+               next_token();
+       }
+       parse_translation_unit();
 }
 
 /**