Add the helper function create_Const_from_bool() to create 0/1 constant from a boolea...
[cparser] / parser.c
index b364768..205aed1 100644 (file)
--- a/parser.c
+++ b/parser.c
@@ -1,6 +1,6 @@
 /*
  * This file is part of cparser.
- * Copyright (C) 2007-2008 Matthias Braun <matze@braunis.de>
+ * Copyright (C) 2007-2009 Matthias Braun <matze@braunis.de>
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
 #include "type_hash.h"
 #include "ast_t.h"
 #include "entity_t.h"
+#include "attribute_t.h"
 #include "lang_features.h"
 #include "walk_statements.h"
 #include "warning.h"
+#include "printer.h"
 #include "adt/bitfiddle.h"
 #include "adt/error.h"
 #include "adt/array.h"
@@ -50,27 +52,6 @@ typedef struct {
        entity_namespace_t  namespc;
 } stack_entry_t;
 
-typedef struct argument_list_t argument_list_t;
-struct argument_list_t {
-       long              argument;
-       argument_list_t  *next;
-};
-
-typedef struct gnu_attribute_t gnu_attribute_t;
-struct gnu_attribute_t {
-       gnu_attribute_kind_t kind;          /**< The kind of the GNU attribute. */
-       gnu_attribute_t     *next;
-       bool                 invalid;       /**< Set if this attribute had argument errors, */
-       bool                 has_arguments; /**< True, if this attribute has arguments. */
-       union {
-               size_t              value;
-               string_t            string;
-               symbol_t           *symbol;
-               long                argument;  /**< Single argument. */
-               argument_list_t    *arguments; /**< List of argument expressions. */
-       } u;
-};
-
 typedef struct declaration_specifiers_t  declaration_specifiers_t;
 struct declaration_specifiers_t {
        source_position_t  source_position;
@@ -78,12 +59,7 @@ struct declaration_specifiers_t {
        unsigned char      alignment;         /**< Alignment, 0 if not set. */
        bool               is_inline    : 1;
        bool               thread_local : 1;  /**< GCC __thread */
-       bool               deprecated   : 1;
-       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. */
-       symbol_t          *put_property_sym;  /**< the name of the put property if set. */
+       attribute_t       *attributes;        /**< list of attributes */
        type_t            *type;
 };
 
@@ -112,6 +88,7 @@ static scope_t             *file_scope        = NULL;
 static scope_t             *current_scope     = NULL;
 /** Point to the current function declaration if inside a function. */
 static function_t          *current_function  = NULL;
+static entity_t            *current_entity    = NULL;
 static entity_t            *current_init_decl = NULL;
 static switch_statement_t  *current_switch    = NULL;
 static statement_t         *current_loop      = NULL;
@@ -124,9 +101,9 @@ static label_statement_t   *label_first       = NULL;
 static label_statement_t  **label_anchor      = NULL;
 /** current translation unit. */
 static translation_unit_t  *unit              = NULL;
-/** true if we are in a type property context (evaluation only for type. */
+/** true if we are in a type property context (evaluation only for type) */
 static bool                 in_type_prop      = false;
-/** true in we are in a __extension__ context. */
+/** true if we are in an __extension__ context. */
 static bool                 in_gcc_extension  = false;
 static struct obstack       temp_obst;
 static entity_t            *anonymous_entity;
@@ -139,28 +116,7 @@ static declaration_t      **incomplete_arrays;
 #define POP_PARENT ((void)(current_parent = prev_parent))
 
 /** special symbol used for anonymous entities. */
-static const symbol_t *sym_anonymous = NULL;
-
-/* symbols for Microsoft extended-decl-modifier */
-static const symbol_t *sym_align         = NULL;
-static const symbol_t *sym_allocate      = NULL;
-static const symbol_t *sym_dllimport     = NULL;
-static const symbol_t *sym_dllexport     = NULL;
-static const symbol_t *sym_naked         = NULL;
-static const symbol_t *sym_noinline      = NULL;
-static const symbol_t *sym_returns_twice = NULL;
-static const symbol_t *sym_noreturn      = NULL;
-static const symbol_t *sym_nothrow       = NULL;
-static const symbol_t *sym_novtable      = NULL;
-static const symbol_t *sym_property      = NULL;
-static const symbol_t *sym_get           = NULL;
-static const symbol_t *sym_put           = NULL;
-static const symbol_t *sym_selectany     = NULL;
-static const symbol_t *sym_thread        = NULL;
-static const symbol_t *sym_uuid          = NULL;
-static const symbol_t *sym_deprecated    = NULL;
-static const symbol_t *sym_restrict      = NULL;
-static const symbol_t *sym_noalias       = NULL;
+static symbol_t *sym_anonymous = NULL;
 
 /** The token anchor set */
 static unsigned char token_anchor_set[T_LAST_TOKEN];
@@ -171,12 +127,10 @@ static unsigned char token_anchor_set[T_LAST_TOKEN];
 /** true if we are in GCC mode. */
 #define GNU_MODE ((c_mode & _GNUC) || in_gcc_extension)
 
-static type_t *type_valist;
-
 static statement_t *parse_compound_statement(bool inside_expression_statement);
 static statement_t *parse_statement(void);
 
-static expression_t *parse_sub_expression(precedence_t);
+static expression_t *parse_subexpression(precedence_t);
 static expression_t *parse_expression(void);
 static type_t       *parse_typename(void);
 static void          parse_externals(void);
@@ -184,6 +138,9 @@ static void          parse_external(void);
 
 static void parse_compound_type_entries(compound_t *compound_declaration);
 
+static void check_call_argument(type_t          *expected_type,
+                                                               call_argument_t *argument, unsigned pos);
+
 typedef enum declarator_flags_t {
        DECL_FLAGS_NONE             = 0,
        DECL_MAY_BE_ABSTRACT        = 1U << 0,
@@ -194,13 +151,8 @@ typedef enum declarator_flags_t {
 static entity_t *parse_declarator(const declaration_specifiers_t *specifiers,
                                   declarator_flags_t flags);
 
-static entity_t *record_entity(entity_t *entity, bool is_definition);
-
 static void semantic_comparison(binary_expression_t *expression);
 
-static void create_gnu_builtins(void);
-static void create_microsoft_intrinsics(void);
-
 #define STORAGE_CLASSES       \
        STORAGE_CLASSES_NO_EXTERN \
        case T_extern:
@@ -244,6 +196,11 @@ static void create_microsoft_intrinsics(void);
        case T_unsigned:          \
        case T_void:              \
        case T_wchar_t:           \
+       case T__int8:             \
+       case T__int16:            \
+       case T__int32:            \
+       case T__int64:            \
+       case T__int128:           \
        COMPLEX_SPECIFIERS        \
        IMAGINARY_SPECIFIERS
 
@@ -261,97 +218,52 @@ static void create_microsoft_intrinsics(void);
        TYPE_QUALIFIERS         \
        TYPE_SPECIFIERS
 
-#define EXPRESSION_START           \
-       case '!':                        \
-       case '&':                        \
-       case '(':                        \
-       case '*':                        \
-       case '+':                        \
-       case '-':                        \
-       case '~':                        \
-       case T_ANDAND:                   \
-       case T_CHARACTER_CONSTANT:       \
-       case T_FLOATINGPOINT:            \
-       case T_INTEGER:                  \
-       case T_MINUSMINUS:               \
-       case T_PLUSPLUS:                 \
-       case T_STRING_LITERAL:           \
-       case T_WIDE_CHARACTER_CONSTANT:  \
-       case T_WIDE_STRING_LITERAL:      \
-       case T___FUNCDNAME__:            \
-       case T___FUNCSIG__:              \
-       case T___FUNCTION__:             \
-       case T___PRETTY_FUNCTION__:      \
-       case T___alignof__:              \
-       case T___builtin_classify_type:  \
-       case T___builtin_constant_p:     \
-       case T___builtin_isgreater:      \
-       case T___builtin_isgreaterequal: \
-       case T___builtin_isless:         \
-       case T___builtin_islessequal:    \
-       case T___builtin_islessgreater:  \
-       case T___builtin_isunordered:    \
-       case T___builtin_offsetof:       \
-       case T___builtin_va_arg:         \
-       case T___builtin_va_start:       \
-       case T___func__:                 \
-       case T___noop:                   \
-       case T__assume:                  \
-       case T_delete:                   \
-       case T_false:                    \
-       case T_sizeof:                   \
-       case T_throw:                    \
+#define EXPRESSION_START              \
+       case '!':                         \
+       case '&':                         \
+       case '(':                         \
+       case '*':                         \
+       case '+':                         \
+       case '-':                         \
+       case '~':                         \
+       case T_ANDAND:                    \
+       case T_CHARACTER_CONSTANT:        \
+       case T_FLOATINGPOINT:             \
+       case T_FLOATINGPOINT_HEXADECIMAL: \
+       case T_INTEGER:                   \
+       case T_INTEGER_HEXADECIMAL:       \
+       case T_INTEGER_OCTAL:             \
+       case T_MINUSMINUS:                \
+       case T_PLUSPLUS:                  \
+       case T_STRING_LITERAL:            \
+       case T_WIDE_CHARACTER_CONSTANT:   \
+       case T_WIDE_STRING_LITERAL:       \
+       case T___FUNCDNAME__:             \
+       case T___FUNCSIG__:               \
+       case T___FUNCTION__:              \
+       case T___PRETTY_FUNCTION__:       \
+       case T___alignof__:               \
+       case T___builtin_classify_type:   \
+       case T___builtin_constant_p:      \
+       case T___builtin_isgreater:       \
+       case T___builtin_isgreaterequal:  \
+       case T___builtin_isless:          \
+       case T___builtin_islessequal:     \
+       case T___builtin_islessgreater:   \
+       case T___builtin_isunordered:     \
+       case T___builtin_offsetof:        \
+       case T___builtin_va_arg:          \
+       case T___builtin_va_copy:         \
+       case T___builtin_va_start:        \
+       case T___func__:                  \
+       case T___noop:                    \
+       case T__assume:                   \
+       case T_delete:                    \
+       case T_false:                     \
+       case T_sizeof:                    \
+       case T_throw:                     \
        case T_true:
 
-/**
- * Allocate an AST node with given size and
- * initialize all fields with zero.
- */
-static void *allocate_ast_zero(size_t size)
-{
-       void *res = allocate_ast(size);
-       memset(res, 0, size);
-       return res;
-}
-
-/**
- * Returns the size of an entity node.
- *
- * @param kind  the entity kind
- */
-static size_t get_entity_struct_size(entity_kind_t kind)
-{
-       static const size_t sizes[] = {
-               [ENTITY_VARIABLE]        = sizeof(variable_t),
-               [ENTITY_PARAMETER]       = sizeof(parameter_t),
-               [ENTITY_COMPOUND_MEMBER] = sizeof(compound_member_t),
-               [ENTITY_FUNCTION]        = sizeof(function_t),
-               [ENTITY_TYPEDEF]         = sizeof(typedef_t),
-               [ENTITY_STRUCT]          = sizeof(compound_t),
-               [ENTITY_UNION]           = sizeof(compound_t),
-               [ENTITY_ENUM]            = sizeof(enum_t),
-               [ENTITY_ENUM_VALUE]      = sizeof(enum_value_t),
-               [ENTITY_LABEL]           = sizeof(label_t),
-               [ENTITY_LOCAL_LABEL]     = sizeof(label_t),
-               [ENTITY_NAMESPACE]       = sizeof(namespace_t)
-       };
-       assert(kind < lengthof(sizes));
-       assert(sizes[kind] != 0);
-       return sizes[kind];
-}
-
-/**
- * Allocate an entity of given kind and initialize all
- * fields with zero.
- */
-static entity_t *allocate_entity_zero(entity_kind_t kind)
-{
-       size_t    size   = get_entity_struct_size(kind);
-       entity_t *entity = allocate_ast_zero(size);
-       entity->kind     = kind;
-       return entity;
-}
-
 /**
  * Returns the size of a statement node.
  *
@@ -396,11 +308,15 @@ static size_t get_expression_struct_size(expression_kind_t kind)
                [EXPR_INVALID]                    = sizeof(expression_base_t),
                [EXPR_REFERENCE]                  = sizeof(reference_expression_t),
                [EXPR_REFERENCE_ENUM_VALUE]       = sizeof(reference_expression_t),
-               [EXPR_CONST]                      = sizeof(const_expression_t),
-               [EXPR_CHARACTER_CONSTANT]         = sizeof(const_expression_t),
-               [EXPR_WIDE_CHARACTER_CONSTANT]    = sizeof(const_expression_t),
+               [EXPR_LITERAL_INTEGER]            = sizeof(literal_expression_t),
+               [EXPR_LITERAL_INTEGER_OCTAL]      = sizeof(literal_expression_t),
+               [EXPR_LITERAL_INTEGER_HEXADECIMAL]= sizeof(literal_expression_t),
+               [EXPR_LITERAL_FLOATINGPOINT]      = sizeof(literal_expression_t),
+               [EXPR_LITERAL_FLOATINGPOINT_HEXADECIMAL] = sizeof(literal_expression_t),
+               [EXPR_LITERAL_CHARACTER]          = sizeof(literal_expression_t),
+               [EXPR_LITERAL_WIDE_CHARACTER]     = sizeof(literal_expression_t),
                [EXPR_STRING_LITERAL]             = sizeof(string_literal_expression_t),
-               [EXPR_WIDE_STRING_LITERAL]        = sizeof(wide_string_literal_expression_t),
+               [EXPR_WIDE_STRING_LITERAL]        = sizeof(string_literal_expression_t),
                [EXPR_COMPOUND_LITERAL]           = sizeof(compound_literal_expression_t),
                [EXPR_CALL]                       = sizeof(call_expression_t),
                [EXPR_UNARY_FIRST]                = sizeof(unary_expression_t),
@@ -417,6 +333,7 @@ static size_t get_expression_struct_size(expression_kind_t kind)
                [EXPR_OFFSETOF]                   = sizeof(offsetof_expression_t),
                [EXPR_VA_START]                   = sizeof(va_start_expression_t),
                [EXPR_VA_ARG]                     = sizeof(va_arg_expression_t),
+               [EXPR_VA_COPY]                    = sizeof(va_copy_expression_t),
                [EXPR_STATEMENT]                  = sizeof(statement_expression_t),
                [EXPR_LABEL_ADDRESS]              = sizeof(label_address_expression_t),
        };
@@ -450,6 +367,8 @@ static statement_t *allocate_statement_zero(statement_kind_t kind)
 /**
  * Allocate an expression node of given kind and initialize all
  * fields with zero.
+ *
+ * @param kind  the kind of the expression to allocate
  */
 static expression_t *allocate_expression_zero(expression_kind_t kind)
 {
@@ -487,53 +406,10 @@ static statement_t *create_empty_statement(void)
        return allocate_statement_zero(STATEMENT_EMPTY);
 }
 
-/**
- * Returns the size of a type node.
- *
- * @param kind  the type kind
- */
-static size_t get_type_struct_size(type_kind_t kind)
-{
-       static const size_t sizes[] = {
-               [TYPE_ATOMIC]          = sizeof(atomic_type_t),
-               [TYPE_COMPLEX]         = sizeof(complex_type_t),
-               [TYPE_IMAGINARY]       = sizeof(imaginary_type_t),
-               [TYPE_BITFIELD]        = sizeof(bitfield_type_t),
-               [TYPE_COMPOUND_STRUCT] = sizeof(compound_type_t),
-               [TYPE_COMPOUND_UNION]  = sizeof(compound_type_t),
-               [TYPE_ENUM]            = sizeof(enum_type_t),
-               [TYPE_FUNCTION]        = sizeof(function_type_t),
-               [TYPE_POINTER]         = sizeof(pointer_type_t),
-               [TYPE_ARRAY]           = sizeof(array_type_t),
-               [TYPE_BUILTIN]         = sizeof(builtin_type_t),
-               [TYPE_TYPEDEF]         = sizeof(typedef_type_t),
-               [TYPE_TYPEOF]          = sizeof(typeof_type_t),
-       };
-       assert(lengthof(sizes) == (int)TYPE_TYPEOF + 1);
-       assert(kind <= TYPE_TYPEOF);
-       assert(sizes[kind] != 0);
-       return sizes[kind];
-}
-
-/**
- * Allocate a type node of given kind and initialize all
- * fields with zero.
- *
- * @param kind             type kind to allocate
- */
-static type_t *allocate_type_zero(type_kind_t kind)
-{
-       size_t  size = get_type_struct_size(kind);
-       type_t *res  = obstack_alloc(type_obst, size);
-       memset(res, 0, size);
-       res->base.kind = kind;
-
-       return res;
-}
-
 static function_parameter_t *allocate_parameter(type_t *const type)
 {
-       function_parameter_t *const param = obstack_alloc(type_obst, sizeof(*param));
+       function_parameter_t *const param
+               = obstack_alloc(type_obst, sizeof(*param));
        memset(param, 0, sizeof(*param));
        param->type = type;
        return param;
@@ -603,6 +479,16 @@ static inline void next_token(void)
 #endif
 }
 
+static inline bool next_if(int const type)
+{
+       if (token.type == type) {
+               next_token();
+               return true;
+       } else {
+               return false;
+       }
+}
+
 /**
  * Return the next token with a given lookahead.
  */
@@ -736,8 +622,7 @@ static void eat_until_anchor(void)
 static void eat_block(void)
 {
        eat_until_matching_token('{');
-       if (token.type == '}')
-               next_token();
+       next_if('}');
 }
 
 #define eat(token_type) (assert(token.type == (token_type)), next_token())
@@ -773,7 +658,7 @@ static void type_error_incompatible(const char *msg,
 /**
  * Expect the current token is the expected token.
  * If not, generate an error, eat the current statement,
- * and goto the end_error label.
+ * and goto the error_label label.
  */
 #define expect(expected, error_label)                     \
        do {                                                  \
@@ -781,8 +666,7 @@ static void type_error_incompatible(const char *msg,
                        parse_error_expected(NULL, (expected), NULL); \
                        add_anchor_token(expected);                   \
                        eat_until_anchor();                           \
-                       if (token.type == expected)                   \
-                               next_token();                             \
+                       next_if((expected));                          \
                        rem_anchor_token(expected);                   \
                        goto error_label;                             \
                }                                                 \
@@ -827,6 +711,21 @@ static entity_t *get_entity(const symbol_t *const symbol,
        return NULL;
 }
 
+/* Â§6.2.3:1 24)  There is only one name space for tags even though three are
+ * possible. */
+static entity_t *get_tag(symbol_t const *const symbol,
+                         entity_kind_tag_t const kind)
+{
+       entity_t *entity = get_entity(symbol, NAMESPACE_TAG);
+       if (entity != NULL && entity->kind != kind) {
+               errorf(HERE,
+                               "'%Y' defined as wrong kind of tag (previous definition %P)",
+                               symbol, &entity->base.source_position);
+               entity = NULL;
+       }
+       return entity;
+}
+
 /**
  * pushs an entity on the environment stack and links the corresponding symbol
  * it.
@@ -924,7 +823,7 @@ static void stack_pop_to(stack_entry_t **stack_ptr, size_t new_top)
                }
        }
 
-       ARR_SHRINKLEN(*stack_ptr, (int) new_top);
+       ARR_SHRINKLEN(*stack_ptr, new_top);
 }
 
 /**
@@ -968,7 +867,7 @@ static int get_rank(const type_t *type)
 }
 
 /**
- * Do integer promotion for a given type.
+ * Â§6.3.1.1:2  Do integer promotion for a given type.
  *
  * @param type  the type to promote
  * @return the promoted type
@@ -1017,10 +916,13 @@ static bool is_null_pointer_constant(const expression_t *expression)
        }
 
        type_t *const type = skip_typeref(expression->base.type);
-       return
-               is_type_integer(type)              &&
-               is_constant_expression(expression) &&
-               fold_constant(expression) == 0;
+       if (!is_type_integer(type))
+               return false;
+       switch (is_constant_expression(expression)) {
+               case EXPR_CLASS_ERROR:    return true;
+               case EXPR_CLASS_CONSTANT: return !fold_constant_to_bool(expression);
+               default:                  return false;
+       }
 }
 
 /**
@@ -1156,8 +1058,8 @@ static assign_error_t semantic_assign(type_t *orig_type_left,
                        return ASSIGN_WARNING_POINTER_FROM_INT;
                }
        } else if ((is_type_arithmetic(type_left) && is_type_arithmetic(type_right)) ||
-           (is_type_atomic(type_left, ATOMIC_TYPE_BOOL)
-               && is_type_pointer(type_right))) {
+                       (is_type_atomic(type_left, ATOMIC_TYPE_BOOL)
+                               && is_type_pointer(type_right))) {
                return ASSIGN_SUCCESS;
        } else if ((is_type_compound(type_left)  && is_type_compound(type_right))
                        || (is_type_builtin(type_left) && is_type_builtin(type_right))) {
@@ -1178,9 +1080,9 @@ static assign_error_t semantic_assign(type_t *orig_type_left,
 
 static expression_t *parse_constant_expression(void)
 {
-       expression_t *result = parse_sub_expression(PREC_CONDITIONAL);
+       expression_t *result = parse_subexpression(PREC_CONDITIONAL);
 
-       if (!is_constant_expression(result)) {
+       if (is_constant_expression(result) == EXPR_CLASS_VARIABLE) {
                errorf(&result->base.source_position,
                       "expression '%E' is not constant", result);
        }
@@ -1190,93 +1092,32 @@ static expression_t *parse_constant_expression(void)
 
 static expression_t *parse_assignment_expression(void)
 {
-       return parse_sub_expression(PREC_ASSIGNMENT);
+       return parse_subexpression(PREC_ASSIGNMENT);
+}
+
+static void warn_string_concat(const source_position_t *pos)
+{
+       if (warning.traditional) {
+               warningf(pos, "traditional C rejects string constant concatenation");
+       }
 }
 
 static string_t parse_string_literals(void)
 {
        assert(token.type == T_STRING_LITERAL);
-       string_t result = token.v.string;
+       string_t result = token.literal;
 
        next_token();
 
        while (token.type == T_STRING_LITERAL) {
-               result = concat_strings(&result, &token.v.string);
+               warn_string_concat(&token.source_position);
+               result = concat_strings(&result, &token.literal);
                next_token();
        }
 
        return result;
 }
 
-static const char *const gnu_attribute_names[GNU_AK_LAST] = {
-       [GNU_AK_CONST]                  = "const",
-       [GNU_AK_VOLATILE]               = "volatile",
-       [GNU_AK_CDECL]                  = "cdecl",
-       [GNU_AK_STDCALL]                = "stdcall",
-       [GNU_AK_FASTCALL]               = "fastcall",
-       [GNU_AK_DEPRECATED]             = "deprecated",
-       [GNU_AK_NOINLINE]               = "noinline",
-       [GNU_AK_RETURNS_TWICE]          = "returns_twice",
-       [GNU_AK_NORETURN]               = "noreturn",
-       [GNU_AK_NAKED]                  = "naked",
-       [GNU_AK_PURE]                   = "pure",
-       [GNU_AK_ALWAYS_INLINE]          = "always_inline",
-       [GNU_AK_MALLOC]                 = "malloc",
-       [GNU_AK_WEAK]                   = "weak",
-       [GNU_AK_CONSTRUCTOR]            = "constructor",
-       [GNU_AK_DESTRUCTOR]             = "destructor",
-       [GNU_AK_NOTHROW]                = "nothrow",
-       [GNU_AK_TRANSPARENT_UNION]      = "transparent_union",
-       [GNU_AK_COMMON]                 = "common",
-       [GNU_AK_NOCOMMON]               = "nocommon",
-       [GNU_AK_PACKED]                 = "packed",
-       [GNU_AK_SHARED]                 = "shared",
-       [GNU_AK_NOTSHARED]              = "notshared",
-       [GNU_AK_USED]                   = "used",
-       [GNU_AK_UNUSED]                 = "unused",
-       [GNU_AK_NO_INSTRUMENT_FUNCTION] = "no_instrument_function",
-       [GNU_AK_WARN_UNUSED_RESULT]     = "warn_unused_result",
-       [GNU_AK_LONGCALL]               = "longcall",
-       [GNU_AK_SHORTCALL]              = "shortcall",
-       [GNU_AK_LONG_CALL]              = "long_call",
-       [GNU_AK_SHORT_CALL]             = "short_call",
-       [GNU_AK_FUNCTION_VECTOR]        = "function_vector",
-       [GNU_AK_INTERRUPT]              = "interrupt",
-       [GNU_AK_INTERRUPT_HANDLER]      = "interrupt_handler",
-       [GNU_AK_NMI_HANDLER]            = "nmi_handler",
-       [GNU_AK_NESTING]                = "nesting",
-       [GNU_AK_NEAR]                   = "near",
-       [GNU_AK_FAR]                    = "far",
-       [GNU_AK_SIGNAL]                 = "signal",
-       [GNU_AK_EIGTHBIT_DATA]          = "eightbit_data",
-       [GNU_AK_TINY_DATA]              = "tiny_data",
-       [GNU_AK_SAVEALL]                = "saveall",
-       [GNU_AK_FLATTEN]                = "flatten",
-       [GNU_AK_SSEREGPARM]             = "sseregparm",
-       [GNU_AK_EXTERNALLY_VISIBLE]     = "externally_visible",
-       [GNU_AK_RETURN_TWICE]           = "return_twice",
-       [GNU_AK_MAY_ALIAS]              = "may_alias",
-       [GNU_AK_MS_STRUCT]              = "ms_struct",
-       [GNU_AK_GCC_STRUCT]             = "gcc_struct",
-       [GNU_AK_DLLIMPORT]              = "dllimport",
-       [GNU_AK_DLLEXPORT]              = "dllexport",
-       [GNU_AK_ALIGNED]                = "aligned",
-       [GNU_AK_ALIAS]                  = "alias",
-       [GNU_AK_SECTION]                = "section",
-       [GNU_AK_FORMAT]                 = "format",
-       [GNU_AK_FORMAT_ARG]             = "format_arg",
-       [GNU_AK_WEAKREF]                = "weakref",
-       [GNU_AK_NONNULL]                = "nonnull",
-       [GNU_AK_TLS_MODEL]              = "tls_model",
-       [GNU_AK_VISIBILITY]             = "visibility",
-       [GNU_AK_REGPARM]                = "regparm",
-       [GNU_AK_MODE]                   = "mode",
-       [GNU_AK_MODEL]                  = "model",
-       [GNU_AK_TRAP_EXIT]              = "trap_exit",
-       [GNU_AK_SP_SWITCH]              = "sp_switch",
-       [GNU_AK_SENTINEL]               = "sentinel"
-};
-
 /**
  * compare two string, ignoring double underscores on the second.
  */
@@ -1293,619 +1134,243 @@ static int strcmp_underscore(const char *s1, const char *s2)
        return strcmp(s1, s2);
 }
 
-/**
- * Allocate a new gnu temporal attribute of given kind.
- */
-static gnu_attribute_t *allocate_gnu_attribute(gnu_attribute_kind_t kind)
+static attribute_t *allocate_attribute_zero(attribute_kind_t kind)
 {
-       gnu_attribute_t *attribute = obstack_alloc(&temp_obst, sizeof(*attribute));
-       attribute->kind            = kind;
-       attribute->next            = NULL;
-       attribute->invalid         = false;
-       attribute->has_arguments   = false;
-
+       attribute_t *attribute = allocate_ast_zero(sizeof(*attribute));
+       attribute->kind        = kind;
        return attribute;
 }
 
 /**
- * Parse one constant expression argument of the given attribute.
- */
-static void parse_gnu_attribute_const_arg(gnu_attribute_t *attribute)
-{
-       expression_t *expression;
-       add_anchor_token(')');
-       expression = parse_constant_expression();
-       rem_anchor_token(')');
-       expect(')', end_error);
-       attribute->u.argument = fold_constant(expression);
-       return;
-end_error:
-       attribute->invalid = true;
-}
+ * Parse (gcc) attribute argument. From gcc comments in gcc source:
+ *
+ *  attribute:
+ *    __attribute__ ( ( attribute-list ) )
+ *
+ *  attribute-list:
+ *    attrib
+ *    attribute_list , attrib
+ *
+ *  attrib:
+ *    empty
+ *    any-word
+ *    any-word ( identifier )
+ *    any-word ( identifier , nonempty-expr-list )
+ *    any-word ( expr-list )
+ *
+ *  where the "identifier" must not be declared as a type, and
+ *  "any-word" may be any identifier (including one declared as a
+ *  type), a reserved word storage class specifier, type specifier or
+ *  type qualifier.  ??? This still leaves out most reserved keywords
+ *  (following the old parser), shouldn't we include them, and why not
+ *  allow identifiers declared as types to start the arguments?
+ *
+ *  Matze: this all looks confusing and little systematic, so we're even less
+ *  strict and parse any list of things which are identifiers or
+ *  (assignment-)expressions.
+ */
+static attribute_argument_t *parse_attribute_arguments(void)
+{
+       attribute_argument_t  *first  = NULL;
+       attribute_argument_t **anchor = &first;
+       if (token.type != ')') do {
+               attribute_argument_t *argument = allocate_ast_zero(sizeof(*argument));
+
+               /* is it an identifier */
+               if (token.type == T_IDENTIFIER
+                               && (look_ahead(1)->type == ',' || look_ahead(1)->type == ')')) {
+                       symbol_t *symbol   = token.symbol;
+                       argument->kind     = ATTRIBUTE_ARGUMENT_SYMBOL;
+                       argument->v.symbol = symbol;
+                       next_token();
+               } else {
+                       /* must be an expression */
+                       expression_t *expression = parse_assignment_expression();
 
-/**
- * Parse a list of constant expressions arguments of the given attribute.
- */
-static void parse_gnu_attribute_const_arg_list(gnu_attribute_t *attribute)
-{
-       argument_list_t **list = &attribute->u.arguments;
-       argument_list_t  *entry;
-       expression_t     *expression;
-       add_anchor_token(')');
-       add_anchor_token(',');
-       while (true) {
-               expression = parse_constant_expression();
-               entry = obstack_alloc(&temp_obst, sizeof(entry));
-               entry->argument = fold_constant(expression);
-               entry->next     = NULL;
-               *list = entry;
-               list = &entry->next;
-               if (token.type != ',')
-                       break;
-               next_token();
-       }
-       rem_anchor_token(',');
-       rem_anchor_token(')');
-       expect(')', end_error);
-       return;
-end_error:
-       attribute->invalid = true;
-}
+                       argument->kind         = ATTRIBUTE_ARGUMENT_EXPRESSION;
+                       argument->v.expression = expression;
+               }
 
-/**
- * Parse one string literal argument of the given attribute.
- */
-static void parse_gnu_attribute_string_arg(gnu_attribute_t *attribute,
-                                           string_t *string)
-{
-       add_anchor_token('(');
-       if (token.type != T_STRING_LITERAL) {
-               parse_error_expected("while parsing attribute directive",
-                                    T_STRING_LITERAL, NULL);
-               goto end_error;
-       }
-       *string = parse_string_literals();
-       rem_anchor_token('(');
+               /* append argument */
+               *anchor = argument;
+               anchor  = &argument->next;
+       } while (next_if(','));
        expect(')', end_error);
-       return;
+
+       return first;
+
 end_error:
-       attribute->invalid = true;
+       /* TODO... */
+       return first;
 }
 
-/**
- * Parse one tls model of the given attribute.
- */
-static void parse_gnu_attribute_tls_model_arg(gnu_attribute_t *attribute)
+static attribute_t *parse_attribute_asm(void)
 {
-       static const char *const tls_models[] = {
-               "global-dynamic",
-               "local-dynamic",
-               "initial-exec",
-               "local-exec"
-       };
-       string_t string = { NULL, 0 };
-       parse_gnu_attribute_string_arg(attribute, &string);
-       if (string.begin != NULL) {
-               for (size_t i = 0; i < 4; ++i) {
-                       if (strcmp(tls_models[i], string.begin) == 0) {
-                               attribute->u.value = i;
-                               return;
-                       }
-               }
-               errorf(HERE, "'%s' is an unrecognized tls model", string.begin);
-       }
-       attribute->invalid = true;
-}
+       eat(T_asm);
 
-/**
- * Parse one tls model of the given attribute.
- */
-static void parse_gnu_attribute_visibility_arg(gnu_attribute_t *attribute)
-{
-       static const char *const visibilities[] = {
-               "default",
-               "protected",
-               "hidden",
-               "internal"
-       };
-       string_t string = { NULL, 0 };
-       parse_gnu_attribute_string_arg(attribute, &string);
-       if (string.begin != NULL) {
-               for (size_t i = 0; i < 4; ++i) {
-                       if (strcmp(visibilities[i], string.begin) == 0) {
-                               attribute->u.value = i;
-                               return;
-                       }
-               }
-               errorf(HERE, "'%s' is an unrecognized visibility", string.begin);
-       }
-       attribute->invalid = true;
+       attribute_t *attribute = allocate_attribute_zero(ATTRIBUTE_GNU_ASM);
+
+       expect('(', end_error);
+       attribute->a.arguments = parse_attribute_arguments();
+       return attribute;
+
+end_error:
+       return NULL;
 }
 
-/**
- * Parse one (code) model of the given attribute.
- */
-static void parse_gnu_attribute_model_arg(gnu_attribute_t *attribute)
+static symbol_t *get_symbol_from_token(void)
 {
-       static const char *const visibilities[] = {
-               "small",
-               "medium",
-               "large"
-       };
-       string_t string = { NULL, 0 };
-       parse_gnu_attribute_string_arg(attribute, &string);
-       if (string.begin != NULL) {
-               for (int i = 0; i < 3; ++i) {
-                       if (strcmp(visibilities[i], string.begin) == 0) {
-                               attribute->u.value = i;
-                               return;
-                       }
-               }
-               errorf(HERE, "'%s' is an unrecognized model", string.begin);
+       switch(token.type) {
+       case T_IDENTIFIER:
+               return token.symbol;
+       case T_auto:
+       case T_char:
+       case T_double:
+       case T_enum:
+       case T_extern:
+       case T_float:
+       case T_int:
+       case T_long:
+       case T_register:
+       case T_short:
+       case T_static:
+       case T_struct:
+       case T_union:
+       case T_unsigned:
+       case T_void:
+       case T_bool:
+       case T__Bool:
+       case T_class:
+       case T_explicit:
+       case T_export:
+       case T_wchar_t:
+       case T_const:
+       case T_signed:
+       case T___real__:
+       case T___imag__:
+       case T_restrict:
+       case T_volatile:
+       case T_inline:
+               /* maybe we need more tokens ... add them on demand */
+               return get_token_symbol(&token);
+       default:
+               return NULL;
        }
-       attribute->invalid = true;
 }
 
-/**
- * Parse one mode of the given attribute.
- */
-static void parse_gnu_attribute_mode_arg(gnu_attribute_t *attribute)
+static attribute_t *parse_attribute_gnu_single(void)
 {
-       add_anchor_token(')');
-
-       if (token.type != T_IDENTIFIER) {
-               expect(T_IDENTIFIER, end_error);
+       /* parse "any-word" */
+       symbol_t *symbol = get_symbol_from_token();
+       if (symbol == NULL) {
+               parse_error_expected("while parsing attribute((", T_IDENTIFIER, NULL);
+               return NULL;
        }
 
-       attribute->u.symbol = token.v.symbol;
+       const char *name = symbol->string;
        next_token();
 
-       rem_anchor_token(')');
-       expect(')', end_error);
-       return;
-end_error:
-       attribute->invalid = true;
-}
+       attribute_kind_t kind;
+       for (kind = ATTRIBUTE_GNU_FIRST; kind <= ATTRIBUTE_GNU_LAST; ++kind) {
+               const char *attribute_name = get_attribute_name(kind);
+               if (attribute_name != NULL
+                               && strcmp_underscore(attribute_name, name) == 0)
+                       break;
+       }
 
-/**
- * Parse one interrupt argument of the given attribute.
- */
-static void parse_gnu_attribute_interrupt_arg(gnu_attribute_t *attribute)
-{
-       static const char *const interrupts[] = {
-               "IRQ",
-               "FIQ",
-               "SWI",
-               "ABORT",
-               "UNDEF"
-       };
-       string_t string = { NULL, 0 };
-       parse_gnu_attribute_string_arg(attribute, &string);
-       if (string.begin != NULL) {
-               for (size_t i = 0; i < 5; ++i) {
-                       if (strcmp(interrupts[i], string.begin) == 0) {
-                               attribute->u.value = i;
-                               return;
-                       }
+       if (kind >= ATTRIBUTE_GNU_LAST) {
+               if (warning.attribute) {
+                       warningf(HERE, "unknown attribute '%s' ignored", name);
                }
-               errorf(HERE, "'%s' is not an interrupt", string.begin);
+               /* TODO: we should still save the attribute in the list... */
+               kind = ATTRIBUTE_UNKNOWN;
        }
-       attribute->invalid = true;
+
+       attribute_t *attribute = allocate_attribute_zero(kind);
+
+       /* parse arguments */
+       if (next_if('('))
+               attribute->a.arguments = parse_attribute_arguments();
+
+       return attribute;
 }
 
-/**
- * Parse ( identifier, const expression, const expression )
- */
-static void parse_gnu_attribute_format_args(gnu_attribute_t *attribute)
+static attribute_t *parse_attribute_gnu(void)
 {
-       static const char *const format_names[] = {
-               "printf",
-               "scanf",
-               "strftime",
-               "strfmon"
-       };
-       int i;
+       attribute_t  *first  = NULL;
+       attribute_t **anchor = &first;
 
-       if (token.type != T_IDENTIFIER) {
-               parse_error_expected("while parsing format attribute directive", T_IDENTIFIER, NULL);
-               goto end_error;
-       }
-       const char *name = token.v.symbol->string;
-       for (i = 0; i < 4; ++i) {
-               if (strcmp_underscore(format_names[i], name) == 0)
-                       break;
-       }
-       if (i >= 4) {
-               if (warning.attribute)
-                       warningf(HERE, "'%s' is an unrecognized format function type", name);
-       }
-       next_token();
+       eat(T___attribute__);
+       expect('(', end_error);
+       expect('(', end_error);
 
-       expect(',', end_error);
-       add_anchor_token(')');
-       add_anchor_token(',');
-       parse_constant_expression();
-       rem_anchor_token(',');
-       rem_anchor_token(')');
+       if (token.type != ')') do {
+               attribute_t *attribute = parse_attribute_gnu_single();
+               if (attribute == NULL)
+                       goto end_error;
 
-       expect(',', end_error);
-       add_anchor_token(')');
-       parse_constant_expression();
-       rem_anchor_token(')');
+               *anchor = attribute;
+               anchor  = &attribute->next;
+       } while (next_if(','));
        expect(')', end_error);
-       return;
+       expect(')', end_error);
+
 end_error:
-       attribute->u.value = true;
+       return first;
 }
 
-/**
- * Check that a given GNU attribute has no arguments.
- */
-static void check_no_argument(gnu_attribute_t *attribute, const char *name)
+/** Parse attributes. */
+static attribute_t *parse_attributes(attribute_t *first)
 {
-       if (!attribute->has_arguments)
-               return;
+       attribute_t **anchor = &first;
+       for (;;) {
+               while (*anchor != NULL)
+                       anchor = &(*anchor)->next;
 
-       /* 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;
-}
+               attribute_t *attribute;
+               switch (token.type) {
+               case T___attribute__:
+                       attribute = parse_attribute_gnu();
+                       break;
 
-/**
- * Parse one GNU attribute.
- *
- * Note that attribute names can be specified WITH or WITHOUT
- * double underscores, ie const or __const__.
- *
- * The following attributes are parsed without arguments
- *  const
- *  volatile
- *  cdecl
- *  stdcall
- *  fastcall
- *  deprecated
- *  noinline
- *  noreturn
- *  naked
- *  pure
- *  always_inline
- *  malloc
- *  weak
- *  constructor
- *  destructor
- *  nothrow
- *  transparent_union
- *  common
- *  nocommon
- *  packed
- *  shared
- *  notshared
- *  used
- *  unused
- *  no_instrument_function
- *  warn_unused_result
- *  longcall
- *  shortcall
- *  long_call
- *  short_call
- *  function_vector
- *  interrupt_handler
- *  nmi_handler
- *  nesting
- *  near
- *  far
- *  signal
- *  eightbit_data
- *  tiny_data
- *  saveall
- *  flatten
- *  sseregparm
- *  externally_visible
- *  return_twice
- *  may_alias
- *  ms_struct
- *  gcc_struct
- *  dllimport
- *  dllexport
- *
- * The following attributes are parsed with arguments
- *  aligned( const expression )
- *  alias( string literal )
- *  section( string literal )
- *  format( identifier, const expression, const expression )
- *  format_arg( const expression )
- *  tls_model( string literal )
- *  visibility( string literal )
- *  regparm( const expression )
- *  model( string leteral )
- *  trap_exit( const expression )
- *  sp_switch( string literal )
- *
- * The following attributes might have arguments
- *  weak_ref( string literal )
- *  non_null( const expression // ',' )
- *  interrupt( string literal )
- *  sentinel( constant expression )
- */
-static decl_modifiers_t parse_gnu_attribute(gnu_attribute_t **attributes)
-{
-       gnu_attribute_t *head      = *attributes;
-       gnu_attribute_t *last      = *attributes;
-       decl_modifiers_t modifiers = 0;
-       gnu_attribute_t *attribute;
+               case T_asm:
+                       attribute = parse_attribute_asm();
+                       break;
 
-       eat(T___attribute__);
-       expect('(', end_error);
-       expect('(', end_error);
+               case T_cdecl:
+                       next_token();
+                       attribute = allocate_attribute_zero(ATTRIBUTE_MS_CDECL);
+                       break;
 
-       if (token.type != ')') {
-               /* find the end of the list */
-               if (last != NULL) {
-                       while (last->next != NULL)
-                               last = last->next;
-               }
-
-               /* non-empty attribute list */
-               while (true) {
-                       const char *name;
-                       if (token.type == T_const) {
-                               name = "const";
-                       } else if (token.type == T_volatile) {
-                               name = "volatile";
-                       } else if (token.type == T_cdecl) {
-                               /* __attribute__((cdecl)), WITH ms mode */
-                               name = "cdecl";
-                       } else if (token.type == T_IDENTIFIER) {
-                               const symbol_t *sym = token.v.symbol;
-                               name = sym->string;
-                       } else {
-                               parse_error_expected("while parsing GNU attribute", T_IDENTIFIER, NULL);
-                               break;
-                       }
+               case T__fastcall:
+                       next_token();
+                       attribute = allocate_attribute_zero(ATTRIBUTE_MS_FASTCALL);
+                       break;
 
+               case T__forceinline:
                        next_token();
+                       attribute = allocate_attribute_zero(ATTRIBUTE_MS_FORCEINLINE);
+                       break;
 
-                       int i;
-                       for (i = 0; i < GNU_AK_LAST; ++i) {
-                               if (strcmp_underscore(gnu_attribute_names[i], name) == 0)
-                                       break;
-                       }
-                       gnu_attribute_kind_t kind = (gnu_attribute_kind_t)i;
+               case T__stdcall:
+                       next_token();
+                       attribute = allocate_attribute_zero(ATTRIBUTE_MS_STDCALL);
+                       break;
 
-                       attribute = NULL;
-                       if (kind == GNU_AK_LAST) {
-                               if (warning.attribute)
-                                       warningf(HERE, "'%s' attribute directive ignored", name);
+               case T___thiscall:
+                       next_token();
+                       /* TODO record modifier */
+                       if (warning.other)
+                               warningf(HERE, "Ignoring declaration modifier %K", &token);
+                       attribute = allocate_attribute_zero(ATTRIBUTE_MS_THISCALL);
+                       break;
 
-                               /* skip possible arguments */
-                               if (token.type == '(') {
-                                       eat_until_matching_token(')');
-                               }
-                       } else {
-                               /* check for arguments */
-                               attribute = allocate_gnu_attribute(kind);
-                               if (token.type == '(') {
-                                       next_token();
-                                       if (token.type == ')') {
-                                               /* empty args are allowed */
-                                               next_token();
-                                       } else
-                                               attribute->has_arguments = true;
-                               }
-
-                               switch (kind) {
-                               case GNU_AK_VOLATILE:
-                               case GNU_AK_NAKED:
-                               case GNU_AK_MALLOC:
-                               case GNU_AK_WEAK:
-                               case GNU_AK_COMMON:
-                               case GNU_AK_NOCOMMON:
-                               case GNU_AK_SHARED:
-                               case GNU_AK_NOTSHARED:
-                               case GNU_AK_NO_INSTRUMENT_FUNCTION:
-                               case GNU_AK_WARN_UNUSED_RESULT:
-                               case GNU_AK_LONGCALL:
-                               case GNU_AK_SHORTCALL:
-                               case GNU_AK_LONG_CALL:
-                               case GNU_AK_SHORT_CALL:
-                               case GNU_AK_FUNCTION_VECTOR:
-                               case GNU_AK_INTERRUPT_HANDLER:
-                               case GNU_AK_NMI_HANDLER:
-                               case GNU_AK_NESTING:
-                               case GNU_AK_NEAR:
-                               case GNU_AK_FAR:
-                               case GNU_AK_SIGNAL:
-                               case GNU_AK_EIGTHBIT_DATA:
-                               case GNU_AK_TINY_DATA:
-                               case GNU_AK_SAVEALL:
-                               case GNU_AK_FLATTEN:
-                               case GNU_AK_SSEREGPARM:
-                               case GNU_AK_EXTERNALLY_VISIBLE:
-                               case GNU_AK_RETURN_TWICE:
-                               case GNU_AK_MAY_ALIAS:
-                               case GNU_AK_MS_STRUCT:
-                               case GNU_AK_GCC_STRUCT:
-                                       goto no_arg;
-
-                               case GNU_AK_CDECL:             modifiers |= DM_CDECL;             goto no_arg;
-                               case GNU_AK_FASTCALL:          modifiers |= DM_FASTCALL;          goto no_arg;
-                               case GNU_AK_STDCALL:           modifiers |= DM_STDCALL;           goto no_arg;
-                               case GNU_AK_UNUSED:            modifiers |= DM_UNUSED;            goto no_arg;
-                               case GNU_AK_USED:              modifiers |= DM_USED;              goto no_arg;
-                               case GNU_AK_PURE:              modifiers |= DM_PURE;              goto no_arg;
-                               case GNU_AK_CONST:             modifiers |= DM_CONST;             goto no_arg;
-                               case GNU_AK_ALWAYS_INLINE:     modifiers |= DM_FORCEINLINE;       goto no_arg;
-                               case GNU_AK_DLLIMPORT:         modifiers |= DM_DLLIMPORT;         goto no_arg;
-                               case GNU_AK_DLLEXPORT:         modifiers |= DM_DLLEXPORT;         goto no_arg;
-                               case GNU_AK_PACKED:            modifiers |= DM_PACKED;            goto no_arg;
-                               case GNU_AK_NOINLINE:          modifiers |= DM_NOINLINE;          goto no_arg;
-                               case GNU_AK_RETURNS_TWICE:     modifiers |= DM_RETURNS_TWICE;     goto no_arg;
-                               case GNU_AK_NORETURN:          modifiers |= DM_NORETURN;          goto no_arg;
-                               case GNU_AK_NOTHROW:           modifiers |= DM_NOTHROW;           goto no_arg;
-                               case GNU_AK_TRANSPARENT_UNION: modifiers |= DM_TRANSPARENT_UNION; goto no_arg;
-                               case GNU_AK_CONSTRUCTOR:       modifiers |= DM_CONSTRUCTOR;       goto no_arg;
-                               case GNU_AK_DESTRUCTOR:        modifiers |= DM_DESTRUCTOR;        goto no_arg;
-                               case GNU_AK_DEPRECATED:        modifiers |= DM_DEPRECATED;        goto no_arg;
-
-                               case GNU_AK_ALIGNED:
-                                       /* __align__ may be used without an argument */
-                                       if (attribute->has_arguments) {
-                                               parse_gnu_attribute_const_arg(attribute);
-                                       }
-                                       break;
-
-                               case GNU_AK_FORMAT_ARG:
-                               case GNU_AK_REGPARM:
-                               case GNU_AK_TRAP_EXIT:
-                                       if (!attribute->has_arguments) {
-                                               /* should have arguments */
-                                               errorf(HERE, "wrong number of arguments specified for '%s' attribute", name);
-                                               attribute->invalid = true;
-                                       } else
-                                               parse_gnu_attribute_const_arg(attribute);
-                                       break;
-                               case GNU_AK_ALIAS:
-                               case GNU_AK_SECTION:
-                               case GNU_AK_SP_SWITCH:
-                                       if (!attribute->has_arguments) {
-                                               /* should have arguments */
-                                               errorf(HERE, "wrong number of arguments specified for '%s' attribute", name);
-                                               attribute->invalid = true;
-                                       } else
-                                               parse_gnu_attribute_string_arg(attribute, &attribute->u.string);
-                                       break;
-                               case GNU_AK_FORMAT:
-                                       if (!attribute->has_arguments) {
-                                               /* should have arguments */
-                                               errorf(HERE, "wrong number of arguments specified for '%s' attribute", name);
-                                               attribute->invalid = true;
-                                       } else
-                                               parse_gnu_attribute_format_args(attribute);
-                                       break;
-                               case GNU_AK_WEAKREF:
-                                       /* may have one string argument */
-                                       if (attribute->has_arguments)
-                                               parse_gnu_attribute_string_arg(attribute, &attribute->u.string);
-                                       break;
-                               case GNU_AK_NONNULL:
-                                       if (attribute->has_arguments)
-                                               parse_gnu_attribute_const_arg_list(attribute);
-                                       break;
-                               case GNU_AK_TLS_MODEL:
-                                       if (!attribute->has_arguments) {
-                                               /* should have arguments */
-                                               errorf(HERE, "wrong number of arguments specified for '%s' attribute", name);
-                                       } else
-                                               parse_gnu_attribute_tls_model_arg(attribute);
-                                       break;
-                               case GNU_AK_VISIBILITY:
-                                       if (!attribute->has_arguments) {
-                                               /* should have arguments */
-                                               errorf(HERE, "wrong number of arguments specified for '%s' attribute", name);
-                                       } else
-                                               parse_gnu_attribute_visibility_arg(attribute);
-                                       break;
-                               case GNU_AK_MODEL:
-                                       if (!attribute->has_arguments) {
-                                               /* should have arguments */
-                                               errorf(HERE, "wrong number of arguments specified for '%s' attribute", name);
-                                       } else {
-                                               parse_gnu_attribute_model_arg(attribute);
-                                       }
-                                       break;
-                               case GNU_AK_MODE:
-                                       if (!attribute->has_arguments) {
-                                               /* should have arguments */
-                                               errorf(HERE, "wrong number of arguments specified for '%s' attribute", name);
-                                       } else {
-                                               parse_gnu_attribute_mode_arg(attribute);
-                                       }
-                                       break;
-                               case GNU_AK_INTERRUPT:
-                                       /* may have one string argument */
-                                       if (attribute->has_arguments)
-                                               parse_gnu_attribute_interrupt_arg(attribute);
-                                       break;
-                               case GNU_AK_SENTINEL:
-                                       /* may have one string argument */
-                                       if (attribute->has_arguments)
-                                               parse_gnu_attribute_const_arg(attribute);
-                                       break;
-                               case GNU_AK_LAST:
-                                       /* already handled */
-                                       break;
-
-no_arg:
-                                       check_no_argument(attribute, name);
-                               }
-                       }
-                       if (attribute != NULL) {
-                               if (last != NULL) {
-                                       last->next = attribute;
-                                       last       = attribute;
-                               } else {
-                                       head = last = attribute;
-                               }
-                       }
-
-                       if (token.type != ',')
-                               break;
-                       next_token();
-               }
-       }
-       expect(')', end_error);
-       expect(')', end_error);
-end_error:
-       *attributes = head;
-
-       return modifiers;
-}
-
-/**
- * Parse GNU attributes.
- */
-static decl_modifiers_t parse_attributes(gnu_attribute_t **attributes)
-{
-       decl_modifiers_t modifiers = 0;
-
-       while (true) {
-               switch (token.type) {
-               case T___attribute__:
-                       modifiers |= parse_gnu_attribute(attributes);
-                       continue;
-
-               case T_asm:
-                       next_token();
-                       expect('(', end_error);
-                       if (token.type != T_STRING_LITERAL) {
-                               parse_error_expected("while parsing assembler attribute",
-                                                    T_STRING_LITERAL, NULL);
-                               eat_until_matching_token('(');
-                               break;
-                       } else {
-                               parse_string_literals();
-                       }
-                       expect(')', end_error);
-                       continue;
-
-               case T_cdecl:     modifiers |= DM_CDECL;    break;
-               case T__fastcall: modifiers |= DM_FASTCALL; break;
-               case T__stdcall:  modifiers |= DM_STDCALL;  break;
-
-               case T___thiscall:
-                       /* TODO record modifier */
-                       if (warning.other)
-                               warningf(HERE, "Ignoring declaration modifier %K", &token);
-                       break;
-
-end_error:
-               default: return modifiers;
+               default:
+                       return first;
                }
 
-               next_token();
+               *anchor = attribute;
+               anchor  = &attribute->next;
        }
 }
 
@@ -2032,6 +1497,10 @@ static void mark_vars_read(expression_t *const expr, entity_t *lhs_ent)
                        mark_vars_read(expr->va_arge.ap, lhs_ent);
                        return;
 
+               case EXPR_VA_COPY:
+                       mark_vars_read(expr->va_copye.src, lhs_ent);
+                       return;
+
                case EXPR_UNARY_CAST:
                        /* Special case: Use void cast to mark a variable as "read" */
                        if (is_type_atomic(skip_typeref(expr->base.type), ATOMIC_TYPE_VOID))
@@ -2116,11 +1585,9 @@ unary:
                        determine_lhs_ent(expr->va_starte.ap, lhs_ent);
                        return;
 
+               EXPR_LITERAL_CASES
                case EXPR_UNKNOWN:
                case EXPR_INVALID:
-               case EXPR_CONST:
-               case EXPR_CHARACTER_CONSTANT:
-               case EXPR_WIDE_CHARACTER_CONSTANT:
                case EXPR_STRING_LITERAL:
                case EXPR_WIDE_STRING_LITERAL:
                case EXPR_COMPOUND_LITERAL: // TODO init?
@@ -2142,10 +1609,10 @@ unary:
 
 static designator_t *parse_designation(void)
 {
-       designator_t *result = NULL;
-       designator_t *last   = NULL;
+       designator_t  *result = NULL;
+       designator_t **anchor = &result;
 
-       while (true) {
+       for (;;) {
                designator_t *designator;
                switch (token.type) {
                case '[':
@@ -2166,7 +1633,7 @@ static designator_t *parse_designation(void)
                                                     T_IDENTIFIER, NULL);
                                return NULL;
                        }
-                       designator->symbol = token.v.symbol;
+                       designator->symbol = token.symbol;
                        next_token();
                        break;
                default:
@@ -2175,18 +1642,14 @@ static designator_t *parse_designation(void)
                }
 
                assert(designator != NULL);
-               if (last != NULL) {
-                       last->next = designator;
-               } else {
-                       result = designator;
-               }
-               last = designator;
+               *anchor = designator;
+               anchor  = &designator->next;
        }
 end_error:
        return NULL;
 }
 
-static initializer_t *initializer_from_string(array_type_t *type,
+static initializer_t *initializer_from_string(array_type_t *const type,
                                               const string_t *const string)
 {
        /* TODO: check len vs. size of array type */
@@ -2199,7 +1662,7 @@ static initializer_t *initializer_from_string(array_type_t *type,
 }
 
 static initializer_t *initializer_from_wide_string(array_type_t *const type,
-                                                   wide_string_t *const string)
+                                                   const string_t *const string)
 {
        /* TODO: check len vs. size of array type */
        (void) type;
@@ -2223,6 +1686,7 @@ static initializer_t *initializer_from_expression(type_t *orig_type,
        type_t *type           = skip_typeref(orig_type);
        type_t *expr_type_orig = expression->base.type;
        type_t *expr_type      = skip_typeref(expr_type_orig);
+
        if (is_type_array(type) && expr_type->kind == TYPE_POINTER) {
                array_type_t *const array_type   = &type->array;
                type_t       *const element_type = skip_typeref(array_type->element_type);
@@ -2230,26 +1694,26 @@ static initializer_t *initializer_from_expression(type_t *orig_type,
                if (element_type->kind == TYPE_ATOMIC) {
                        atomic_type_kind_t akind = element_type->atomic.akind;
                        switch (expression->kind) {
-                               case EXPR_STRING_LITERAL:
-                                       if (akind == ATOMIC_TYPE_CHAR
-                                                       || akind == ATOMIC_TYPE_SCHAR
-                                                       || akind == ATOMIC_TYPE_UCHAR) {
-                                               return initializer_from_string(array_type,
-                                                       &expression->string.value);
-                                       }
-                                       break;
+                       case EXPR_STRING_LITERAL:
+                               if (akind == ATOMIC_TYPE_CHAR
+                                               || akind == ATOMIC_TYPE_SCHAR
+                                               || akind == ATOMIC_TYPE_UCHAR) {
+                                       return initializer_from_string(array_type,
+                                                       &expression->string_literal.value);
+                               }
+                               break;
 
-                               case EXPR_WIDE_STRING_LITERAL: {
-                                       type_t *bare_wchar_type = skip_typeref(type_wchar_t);
-                                       if (get_unqualified_type(element_type) == bare_wchar_type) {
-                                               return initializer_from_wide_string(array_type,
-                                                       &expression->wide_string.value);
-                                       }
-                                       break;
+                       case EXPR_WIDE_STRING_LITERAL: {
+                               type_t *bare_wchar_type = skip_typeref(type_wchar_t);
+                               if (get_unqualified_type(element_type) == bare_wchar_type) {
+                                       return initializer_from_wide_string(array_type,
+                                                       &expression->string_literal.value);
                                }
+                               break;
+                       }
 
-                               default:
-                                       break;
+                       default:
+                               break;
                        }
                }
        }
@@ -2261,11 +1725,6 @@ static initializer_t *initializer_from_expression(type_t *orig_type,
                            &expression->base.source_position);
 
        initializer_t *const result = allocate_initializer_zero(INITIALIZER_VALUE);
-#if 0
-       if (type->kind == TYPE_BITFIELD) {
-               type = type->bitfield.base_type;
-       }
-#endif
        result->value.value = create_implicit_cast(expression, type);
 
        return result;
@@ -2276,8 +1735,9 @@ static initializer_t *initializer_from_expression(type_t *orig_type,
  */
 static bool is_initializer_constant(const expression_t *expression)
 {
-       return is_constant_expression(expression)
-               || is_address_constant(expression);
+       return
+               is_constant_expression(expression) != EXPR_CLASS_VARIABLE ||
+               is_address_constant(expression)    != EXPR_CLASS_VARIABLE;
 }
 
 /**
@@ -2290,20 +1750,19 @@ static initializer_t *parse_scalar_initializer(type_t *type,
 {
        /* there might be extra {} hierarchies */
        int braces = 0;
-       if (token.type == '{') {
+       if (next_if('{')) {
                if (warning.other)
                        warningf(HERE, "extra curly braces around scalar initializer");
                do {
                        ++braces;
-                       next_token();
-               } while (token.type == '{');
+               } while (next_if('{'));
        }
 
        expression_t *expression = parse_assignment_expression();
        mark_vars_read(expression, NULL);
        if (must_be_constant && !is_initializer_constant(expression)) {
                errorf(&expression->base.source_position,
-                      "Initialisation expression '%E' is not constant",
+                      "initialisation expression '%E' is not constant",
                       expression);
        }
 
@@ -2319,9 +1778,7 @@ static initializer_t *parse_scalar_initializer(type_t *type,
 
        bool additional_warning_displayed = false;
        while (braces > 0) {
-               if (token.type == ',') {
-                       next_token();
-               }
+               next_if(',');
                if (token.type != '}') {
                        if (!additional_warning_displayed && warning.other) {
                                warningf(HERE, "additional elements in scalar initializer");
@@ -2511,7 +1968,7 @@ static bool walk_designator(type_path_t *path, const designator_t *designator,
                                        type_t *real_type = skip_typeref(iter->declaration.type);
                                        if (real_type->kind == TYPE_BITFIELD) {
                                                errorf(&designator->source_position,
-                                                      "offsetof designator '%Y' may not specify bitfield",
+                                                      "offsetof designator '%Y' must not specify bitfield",
                                                       symbol);
                                                goto failed;
                                        }
@@ -2534,7 +1991,7 @@ static bool walk_designator(type_path_t *path, const designator_t *designator,
                                goto failed;
                        }
 
-                       long index = fold_constant(array_index);
+                       long index = fold_constant_to_int(array_index);
                        if (!used_in_offsetof) {
                                if (index < 0) {
                                        errorf(&designator->source_position,
@@ -2543,8 +2000,8 @@ static bool walk_designator(type_path_t *path, const designator_t *designator,
                                        long array_size = type->array.size;
                                        if (index >= array_size) {
                                                errorf(&designator->source_position,
-                                                      "designator [%E] (%d) exceeds array size %d",
-                                                      array_index, index, array_size);
+                                                      "designator [%E] (%d) exceeds array size %d",
+                                                      array_index, index, array_size);
                                        }
                                }
                        }
@@ -2614,25 +2071,12 @@ static void advance_current_object(type_path_t *path, size_t top_path_level)
        }
 }
 
-/**
- * skip until token is found.
- */
-static void skip_until(int type)
-{
-       while (token.type != type) {
-               if (token.type == T_EOF)
-                       return;
-               next_token();
-       }
-}
-
 /**
  * skip any {...} blocks until a closing bracket is reached.
  */
 static void skip_initializers(void)
 {
-       if (token.type == '{')
-               next_token();
+       next_if('{');
 
        while (token.type != '}') {
                if (token.type == T_EOF)
@@ -2684,7 +2128,7 @@ static initializer_t *parse_sub_initializer(type_path_t *path,
                        /* GNU-style designator ("identifier: value") */
                        designator = allocate_ast_zero(sizeof(designator[0]));
                        designator->source_position = token.source_position;
-                       designator->symbol          = token.v.symbol;
+                       designator->symbol          = token.symbol;
                        eat(T_IDENTIFIER);
                        eat(':');
 
@@ -2749,6 +2193,8 @@ finish_designator:
 
                        if (type == NULL) {
                                /* we are already outside, ... */
+                               if (outer_type == NULL)
+                                       goto error_parse_next;
                                type_t *const outer_type_skip = skip_typeref(outer_type);
                                if (is_type_compound(outer_type_skip) &&
                                    !outer_type_skip->compound.compound->complete) {
@@ -2763,9 +2209,7 @@ finish_designator:
                                        && outer_type != NULL) {
                                sub = initializer_from_expression(outer_type, expression);
                                if (sub != NULL) {
-                                       if (token.type == ',') {
-                                               next_token();
-                                       }
+                                       next_if(',');
                                        if (token.type != '}' && warning.other) {
                                                warningf(HERE, "excessive elements in initializer for type '%T'",
                                                                 orig_type);
@@ -2815,10 +2259,10 @@ finish_designator:
 error_excess:
                        if (warning.other) {
                                if (env->entity != NULL) {
-                                       warningf(HERE, "excess elements in struct initializer for '%Y'",
-                                          env->entity->base.symbol);
+                                       warningf(HERE, "excess elements in initializer for '%Y'",
+                                                env->entity->base.symbol);
                                } else {
-                                       warningf(HERE, "excess elements in struct initializer");
+                                       warningf(HERE, "excess elements in initializer");
                                }
                        }
                }
@@ -2863,6 +2307,18 @@ end_error:
        return NULL;
 }
 
+static expression_t *make_size_literal(size_t value)
+{
+       expression_t *literal = allocate_expression_zero(EXPR_LITERAL_INTEGER);
+       literal->base.type    = type_size_t;
+
+       char buf[128];
+       snprintf(buf, sizeof(buf), "%u", (unsigned) value);
+       literal->literal.value = make_string(buf);
+
+       return literal;
+}
+
 /**
  * Parses an initializer. Parsers either a compound literal
  * (env->declaration == NULL) or an initializer of a declaration.
@@ -2870,7 +2326,7 @@ end_error:
 static initializer_t *parse_initializer(parse_initializer_env_t *env)
 {
        type_t        *type      = skip_typeref(env->type);
-       size_t         max_index = 0xdeadbeaf;   // TODO: Resolve this uninitialized variable problem
+       size_t         max_index = 0;
        initializer_t *result;
 
        if (is_type_scalar(type)) {
@@ -2928,13 +2384,9 @@ static initializer_t *parse_initializer(parse_initializer_env_t *env)
                        internal_errorf(HERE, "invalid initializer type");
                }
 
-               expression_t *cnst       = allocate_expression_zero(EXPR_CONST);
-               cnst->base.type          = type_size_t;
-               cnst->conste.v.int_value = size;
-
                type_t *new_type = duplicate_type(type);
 
-               new_type->array.size_expression   = cnst;
+               new_type->array.size_expression   = make_size_literal(size);
                new_type->array.size_constant     = true;
                new_type->array.has_implicit_size = true;
                new_type->array.size              = size;
@@ -2953,36 +2405,31 @@ static void append_entity(scope_t *scope, entity_t *entity)
        } else {
                scope->entities = entity;
        }
-       scope->last_entity = entity;
+       entity->base.parent_entity = current_entity;
+       scope->last_entity         = entity;
 }
 
 
 static compound_t *parse_compound_type_specifier(bool is_struct)
 {
-       gnu_attribute_t  *attributes = NULL;
-       decl_modifiers_t  modifiers  = 0;
-       if (is_struct) {
-               eat(T_struct);
-       } else {
-               eat(T_union);
-       }
+       eat(is_struct ? T_struct : T_union);
 
-       symbol_t   *symbol   = NULL;
-       compound_t *compound = NULL;
+       symbol_t    *symbol   = NULL;
+       compound_t  *compound = NULL;
+       attribute_t *attributes = NULL;
 
        if (token.type == T___attribute__) {
-               modifiers |= parse_attributes(&attributes);
+               attributes = parse_attributes(NULL);
        }
 
+       entity_kind_tag_t const kind = is_struct ? ENTITY_STRUCT : ENTITY_UNION;
        if (token.type == T_IDENTIFIER) {
-               symbol = token.v.symbol;
+               /* the compound has a name, check if we have seen it already */
+               symbol = token.symbol;
                next_token();
 
-               namespace_tag_t const namespc =
-                       is_struct ? NAMESPACE_STRUCT : NAMESPACE_UNION;
-               entity_t *entity = get_entity(symbol, namespc);
+               entity_t *entity = get_tag(symbol, kind);
                if (entity != NULL) {
-                       assert(entity->kind == (is_struct ? ENTITY_STRUCT : ENTITY_UNION));
                        compound = &entity->compound;
                        if (compound->base.parent_scope != current_scope &&
                            (token.type == '{' || token.type == ';')) {
@@ -3011,12 +2458,11 @@ static compound_t *parse_compound_type_specifier(bool is_struct)
        }
 
        if (compound == NULL) {
-               entity_kind_t  kind   = is_struct ? ENTITY_STRUCT : ENTITY_UNION;
-               entity_t      *entity = allocate_entity_zero(kind);
-               compound              = &entity->compound;
+               entity_t *entity = allocate_entity_zero(kind);
+               compound         = &entity->compound;
 
-               compound->base.namespc =
-                       (is_struct ? NAMESPACE_STRUCT : NAMESPACE_UNION);
+               compound->alignment            = 1;
+               compound->base.namespc         = NAMESPACE_TAG;
                compound->base.source_position = token.source_position;
                compound->base.symbol          = symbol;
                compound->base.parent_scope    = current_scope;
@@ -3028,7 +2474,6 @@ static compound_t *parse_compound_type_specifier(bool is_struct)
 
        if (token.type == '{') {
                parse_compound_type_entries(compound);
-               modifiers |= parse_attributes(&attributes);
 
                /* ISO/IEC 14882:1998(E) Â§7.1.3:5 */
                if (symbol == NULL) {
@@ -3037,7 +2482,10 @@ static compound_t *parse_compound_type_specifier(bool is_struct)
                }
        }
 
-       compound->modifiers |= modifiers;
+       if (attributes != NULL) {
+               handle_entity_attributes(attributes, (entity_t*) compound);
+       }
+
        return compound;
 }
 
@@ -3062,12 +2510,11 @@ static void parse_enum_entries(type_t *const enum_type)
 
                entity_t *entity             = allocate_entity_zero(ENTITY_ENUM_VALUE);
                entity->enum_value.enum_type = enum_type;
-               entity->base.symbol          = token.v.symbol;
+               entity->base.symbol          = token.symbol;
                entity->base.source_position = token.source_position;
                next_token();
 
-               if (token.type == '=') {
-                       next_token();
+               if (next_if('=')) {
                        expression_t *value = parse_constant_expression();
 
                        value = create_implicit_cast(value, enum_type);
@@ -3077,11 +2524,7 @@ static void parse_enum_entries(type_t *const enum_type)
                }
 
                record_entity(entity, false);
-
-               if (token.type != ',')
-                       break;
-               next_token();
-       } while (token.type != '}');
+       } while (next_if(',') && token.type != '}');
        rem_anchor_token('}');
 
        expect('}', end_error);
@@ -3092,40 +2535,43 @@ end_error:
 
 static type_t *parse_enum_specifier(void)
 {
-       gnu_attribute_t *attributes = NULL;
-       entity_t        *entity;
-       symbol_t        *symbol;
+       entity_t *entity;
+       symbol_t *symbol;
 
        eat(T_enum);
-       if (token.type == T_IDENTIFIER) {
-               symbol = token.v.symbol;
-               next_token();
+       switch (token.type) {
+               case T_IDENTIFIER:
+                       symbol = token.symbol;
+                       next_token();
 
-               entity = get_entity(symbol, NAMESPACE_ENUM);
-               if (entity != NULL) {
-                       assert(entity->kind == ENTITY_ENUM);
-                       if (entity->base.parent_scope != current_scope &&
-                                       (token.type == '{' || token.type == ';')) {
-                               /* we're in an inner scope and have a definition. Shadow
-                                * existing definition in outer scope */
-                               entity = NULL;
-                       } else if (entity->enume.complete && token.type == '{') {
-                               errorf(HERE, "multiple definitions of 'enum %Y' (previous definition %P)",
-                                               symbol, &entity->base.source_position);
+                       entity = get_tag(symbol, ENTITY_ENUM);
+                       if (entity != NULL) {
+                               if (entity->base.parent_scope != current_scope &&
+                                               (token.type == '{' || token.type == ';')) {
+                                       /* we're in an inner scope and have a definition. Shadow
+                                        * existing definition in outer scope */
+                                       entity = NULL;
+                               } else if (entity->enume.complete && token.type == '{') {
+                                       errorf(HERE, "multiple definitions of 'enum %Y' (previous definition %P)",
+                                                       symbol, &entity->base.source_position);
+                               }
                        }
-               }
-       } else if (token.type != '{') {
-               parse_error_expected("while parsing enum type specifier",
-                                    T_IDENTIFIER, '{', NULL);
-               return NULL;
-       } else {
-               entity  = NULL;
-               symbol  = NULL;
+                       break;
+
+               case '{':
+                       entity = NULL;
+                       symbol = NULL;
+                       break;
+
+               default:
+                       parse_error_expected("while parsing enum type specifier",
+                                       T_IDENTIFIER, '{', NULL);
+                       return NULL;
        }
 
        if (entity == NULL) {
                entity                       = allocate_entity_zero(ENTITY_ENUM);
-               entity->base.namespc         = NAMESPACE_ENUM;
+               entity->base.namespc         = NAMESPACE_TAG;
                entity->base.source_position = token.source_position;
                entity->base.symbol          = symbol;
                entity->base.parent_scope    = current_scope;
@@ -3143,7 +2589,7 @@ static type_t *parse_enum_specifier(void)
                entity->enume.complete = true;
 
                parse_enum_entries(type);
-               parse_attributes(&attributes);
+               parse_attributes(NULL);
 
                /* ISO/IEC 14882:1998(E) Â§7.1.3:5 */
                if (symbol == NULL) {
@@ -3182,29 +2628,21 @@ static type_t *parse_typeof(void)
        bool old_gcc_extension = in_gcc_extension;
        in_type_prop           = true;
 
-       while (token.type == T___extension__) {
+       while (next_if(T___extension__)) {
                /* This can be a prefix to a typename or an expression. */
-               next_token();
                in_gcc_extension = true;
        }
        switch (token.type) {
        case T_IDENTIFIER:
-               if (is_typedef_symbol(token.v.symbol)) {
+               if (is_typedef_symbol(token.symbol)) {
+       TYPENAME_START
                        type = parse_typename();
                } else {
+       default:
                        expression = parse_expression();
                        type       = revert_automatic_type_conversion(expression);
                }
                break;
-
-       TYPENAME_START
-               type = parse_typename();
-               break;
-
-       default:
-               expression = parse_expression();
-               type       = expression->base.type;
-               break;
        }
        in_type_prop     = old_type_prop;
        in_gcc_extension = old_gcc_extension;
@@ -3264,392 +2702,146 @@ static type_t *get_typedef_type(symbol_t *symbol)
        return type;
 }
 
-/**
- * check for the allowed MS alignment values.
- */
-static bool check_alignment_value(long long intvalue)
+static attribute_t *parse_attribute_ms_property(attribute_t *attribute)
 {
-       if (intvalue < 1 || intvalue > 8192) {
-               errorf(HERE, "illegal alignment value");
-               return false;
-       }
-       unsigned v = (unsigned)intvalue;
-       for (unsigned i = 1; i <= 8192; i += i) {
-               if (i == v)
-                       return true;
-       }
-       errorf(HERE, "alignment must be power of two");
-       return false;
-}
+       expect('(', end_error);
 
-#define DET_MOD(name, tag) do { \
-       if (*modifiers & tag && warning.other) warningf(HERE, #name " used more than once"); \
-       *modifiers |= tag; \
-} while (0)
+       attribute_property_argument_t *property
+               = allocate_ast_zero(sizeof(*property));
 
-static void parse_microsoft_extended_decl_modifier(declaration_specifiers_t *specifiers)
-{
-       decl_modifiers_t *modifiers = &specifiers->modifiers;
+       do {
+               if (token.type != T_IDENTIFIER) {
+                       parse_error_expected("while parsing property declspec",
+                                            T_IDENTIFIER, NULL);
+                       goto end_error;
+               }
 
-       while (true) {
-               if (token.type == T_restrict) {
-                       next_token();
-                       DET_MOD(restrict, DM_RESTRICT);
-                       goto end_loop;
-               } else if (token.type != T_IDENTIFIER)
-                       break;
-               symbol_t *symbol = token.v.symbol;
-               if (symbol == sym_align) {
-                       next_token();
-                       expect('(', end_error);
-                       if (token.type != T_INTEGER)
-                               goto end_error;
-                       if (check_alignment_value(token.v.intvalue)) {
-                               if (specifiers->alignment != 0 && warning.other)
-                                       warningf(HERE, "align used more than once");
-                               specifiers->alignment = (unsigned char)token.v.intvalue;
-                       }
-                       next_token();
-                       expect(')', end_error);
-               } else if (symbol == sym_allocate) {
-                       next_token();
-                       expect('(', end_error);
-                       if (token.type != T_IDENTIFIER)
-                               goto end_error;
-                       (void)token.v.symbol;
-                       expect(')', end_error);
-               } else if (symbol == sym_dllimport) {
-                       next_token();
-                       DET_MOD(dllimport, DM_DLLIMPORT);
-               } else if (symbol == sym_dllexport) {
-                       next_token();
-                       DET_MOD(dllexport, DM_DLLEXPORT);
-               } else if (symbol == sym_thread) {
-                       next_token();
-                       DET_MOD(thread, DM_THREAD);
-               } else if (symbol == sym_naked) {
-                       next_token();
-                       DET_MOD(naked, DM_NAKED);
-               } else if (symbol == sym_noinline) {
-                       next_token();
-                       DET_MOD(noinline, DM_NOINLINE);
-               } else if (symbol == sym_returns_twice) {
-                       next_token();
-                       DET_MOD(returns_twice, DM_RETURNS_TWICE);
-               } else if (symbol == sym_noreturn) {
-                       next_token();
-                       DET_MOD(noreturn, DM_NORETURN);
-               } else if (symbol == sym_nothrow) {
-                       next_token();
-                       DET_MOD(nothrow, DM_NOTHROW);
-               } else if (symbol == sym_novtable) {
-                       next_token();
-                       DET_MOD(novtable, DM_NOVTABLE);
-               } else if (symbol == sym_property) {
-                       next_token();
-                       expect('(', end_error);
-                       for (;;) {
-                               bool is_get = false;
-                               if (token.type != T_IDENTIFIER)
-                                       goto end_error;
-                               if (token.v.symbol == sym_get) {
-                                       is_get = true;
-                               } else if (token.v.symbol == sym_put) {
-                               } else {
-                                       errorf(HERE, "Bad property name '%Y'", token.v.symbol);
-                                       goto end_error;
-                               }
-                               next_token();
-                               expect('=', end_error);
-                               if (token.type != T_IDENTIFIER)
-                                       goto end_error;
-                               if (is_get) {
-                                       if (specifiers->get_property_sym != NULL) {
-                                               errorf(HERE, "get property name already specified");
-                                       } else {
-                                               specifiers->get_property_sym = token.v.symbol;
-                                       }
-                               } else {
-                                       if (specifiers->put_property_sym != NULL) {
-                                               errorf(HERE, "put property name already specified");
-                                       } else {
-                                               specifiers->put_property_sym = token.v.symbol;
-                                       }
-                               }
-                               next_token();
-                               if (token.type == ',') {
-                                       next_token();
-                                       continue;
-                               }
-                               break;
-                       }
-                       expect(')', end_error);
-               } else if (symbol == sym_selectany) {
-                       next_token();
-                       DET_MOD(selectany, DM_SELECTANY);
-               } else if (symbol == sym_uuid) {
-                       next_token();
-                       expect('(', end_error);
-                       if (token.type != T_STRING_LITERAL)
-                               goto end_error;
-                       next_token();
-                       expect(')', end_error);
-               } else if (symbol == sym_deprecated) {
-                       next_token();
-                       if (specifiers->deprecated != 0 && warning.other)
-                               warningf(HERE, "deprecated used more than once");
-                       specifiers->deprecated = true;
-                       if (token.type == '(') {
-                               next_token();
-                               if (token.type == T_STRING_LITERAL) {
-                                       specifiers->deprecated_string = token.v.string.begin;
-                                       next_token();
-                               } else {
-                                       errorf(HERE, "string literal expected");
-                               }
-                               expect(')', end_error);
-                       }
-               } else if (symbol == sym_noalias) {
-                       next_token();
-                       DET_MOD(noalias, DM_NOALIAS);
+               bool is_put;
+               symbol_t *symbol = token.symbol;
+               next_token();
+               if (strcmp(symbol->string, "put") == 0) {
+                       is_put = true;
+               } else if (strcmp(symbol->string, "get") == 0) {
+                       is_put = false;
                } else {
-                       if (warning.other)
-                               warningf(HERE, "Unknown modifier '%Y' ignored", token.v.symbol);
-                       next_token();
-                       if (token.type == '(')
-                               skip_until(')');
+                       errorf(HERE, "expected put or get in property declspec");
+                       goto end_error;
                }
-end_loop:
-               if (token.type == ',')
-                       next_token();
-       }
+               expect('=', end_error);
+               if (token.type != T_IDENTIFIER) {
+                       parse_error_expected("while parsing property declspec",
+                                            T_IDENTIFIER, NULL);
+                       goto end_error;
+               }
+               if (is_put) {
+                       property->put_symbol = token.symbol;
+               } else {
+                       property->get_symbol = token.symbol;
+               }
+               next_token();
+       } while (next_if(','));
+
+       attribute->a.property = property;
+
+       expect(')', end_error);
+
 end_error:
-       return;
+       return attribute;
 }
 
-static entity_t *create_error_entity(symbol_t *symbol, entity_kind_tag_t kind)
+static attribute_t *parse_microsoft_extended_decl_modifier_single(void)
 {
-       entity_t *entity             = allocate_entity_zero(kind);
-       entity->base.source_position = *HERE;
-       entity->base.symbol          = symbol;
-       if (is_declaration(entity)) {
-               entity->declaration.type     = type_error_type;
-               entity->declaration.implicit = true;
-       } else if (kind == ENTITY_TYPEDEF) {
-               entity->typedefe.type    = type_error_type;
-               entity->typedefe.builtin = true;
-       }
-       if (kind != ENTITY_COMPOUND_MEMBER)
-               record_entity(entity, false);
-       return entity;
-}
+       attribute_kind_t kind = ATTRIBUTE_UNKNOWN;
+       if (next_if(T_restrict)) {
+               kind = ATTRIBUTE_MS_RESTRICT;
+       } else if (token.type == T_IDENTIFIER) {
+               const char *name = token.symbol->string;
+               next_token();
+               for (attribute_kind_t k = ATTRIBUTE_MS_FIRST; k <= ATTRIBUTE_MS_LAST;
+                    ++k) {
+                       const char *attribute_name = get_attribute_name(k);
+                       if (attribute_name != NULL && strcmp(attribute_name, name) == 0) {
+                               kind = k;
+                               break;
+                       }
+               }
 
-static variable_t *parse_microsoft_based(void)
-{
-       if (token.type != T_IDENTIFIER) {
-               parse_error_expected("while parsing __based", T_IDENTIFIER, NULL);
-               return NULL;
-       }
-       symbol_t *symbol = token.v.symbol;
-       entity_t *entity = get_entity(symbol, NAMESPACE_NORMAL);
-
-       variable_t *variable;
-       if (entity == NULL || entity->base.kind != ENTITY_VARIABLE) {
-               errorf(HERE, "'%Y' is not a variable name.", symbol);
-               variable = &create_error_entity(symbol, ENTITY_VARIABLE)->variable;
-       } else {
-               variable = &entity->variable;
-
-               type_t *const type = variable->base.type;
-               if (is_type_valid(type)) {
-                       if (! is_type_pointer(skip_typeref(type))) {
-                               errorf(HERE, "variable in __based modifier must have pointer type instead of '%T'", type);
-                       }
-                       if (variable->base.base.parent_scope != file_scope) {
-                               errorf(HERE, "a nonstatic local variable may not be used in a __based specification");
-                       }
+               if (kind == ATTRIBUTE_UNKNOWN && warning.attribute) {
+                       warningf(HERE, "unknown __declspec '%s' ignored", name);
                }
+       } else {
+               parse_error_expected("while parsing __declspec", T_IDENTIFIER, NULL);
+               return NULL;
        }
-       next_token();
-       return variable;
-}
-
-/**
- * Finish the construction of a struct type by calculating
- * its size, offsets, alignment.
- */
-static void finish_struct_type(compound_type_t *type)
-{
-       assert(type->compound != NULL);
-
-       compound_t *compound = type->compound;
-       if (!compound->complete)
-               return;
-
-       il_size_t      size           = 0;
-       il_size_t      offset;
-       il_alignment_t alignment      = 1;
-       bool           need_pad       = false;
-
-       entity_t *entry = compound->members.entities;
-       for (; entry != NULL; entry = entry->base.next) {
-               if (entry->kind != ENTITY_COMPOUND_MEMBER)
-                       continue;
-
-               type_t *m_type = skip_typeref(entry->declaration.type);
-               if (! is_type_valid(m_type)) {
-                       /* simply ignore errors here */
-                       continue;
-               }
-               il_alignment_t m_alignment = m_type->base.alignment;
-               if (m_alignment > alignment)
-                       alignment = m_alignment;
 
-               offset = (size + m_alignment - 1) & -m_alignment;
+       attribute_t *attribute = allocate_attribute_zero(kind);
 
-               if (offset > size)
-                       need_pad = true;
-               entry->compound_member.offset = offset;
-               size = offset + m_type->base.size;
+       if (kind == ATTRIBUTE_MS_PROPERTY) {
+               return parse_attribute_ms_property(attribute);
        }
-       if (type->base.alignment != 0) {
-               alignment = type->base.alignment;
-       }
-
-       offset = (size + alignment - 1) & -alignment;
-       if (offset > size)
-               need_pad = true;
 
-       if (need_pad) {
-               if (warning.padded) {
-                       warningf(&compound->base.source_position, "'%T' needs padding", type);
-               }
-       } else {
-               if (compound->modifiers & DM_PACKED && warning.packed) {
-                       warningf(&compound->base.source_position,
-                                       "superfluous packed attribute on '%T'", type);
-               }
-       }
+       /* parse arguments */
+       if (next_if('('))
+               attribute->a.arguments = parse_attribute_arguments();
 
-       type->base.size      = offset;
-       type->base.alignment = alignment;
+       return attribute;
 }
 
-/**
- * Finish the construction of an union type by calculating
- * its size and alignment.
- */
-static void finish_union_type(compound_type_t *type)
+static attribute_t *parse_microsoft_extended_decl_modifier(attribute_t *first)
 {
-       assert(type->compound != NULL);
+       eat(T__declspec);
 
-       compound_t *compound = type->compound;
-       if (! compound->complete)
-               return;
+       expect('(', end_error);
 
-       il_size_t      size      = 0;
-       il_alignment_t alignment = 1;
+       if (next_if(')'))
+               return NULL;
 
-       entity_t *entry = compound->members.entities;
-       for (; entry != NULL; entry = entry->base.next) {
-               if (entry->kind != ENTITY_COMPOUND_MEMBER)
-                       continue;
+       add_anchor_token(')');
 
-               type_t *m_type = skip_typeref(entry->declaration.type);
-               if (! is_type_valid(m_type))
-                       continue;
+       attribute_t **anchor = &first;
+       do {
+               while (*anchor != NULL)
+                       anchor = &(*anchor)->next;
 
-               entry->compound_member.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;
-}
+               attribute_t *attribute
+                       = parse_microsoft_extended_decl_modifier_single();
+               if (attribute == NULL)
+                       goto end_error;
 
-static type_t *handle_attribute_mode(const gnu_attribute_t *attribute,
-                                     type_t *orig_type)
-{
-       type_t *type = skip_typeref(orig_type);
+               *anchor = attribute;
+               anchor  = &attribute->next;
+       } while (next_if(','));
 
-       /* at least: byte, word, pointer, list of machine modes
-        * __XXX___ is interpreted as XXX */
-
-       /* This isn't really correct, the backend should provide a list of machine
-        * specific modes (according to gcc philosophy that is...) */
-       const char         *symbol_str = attribute->u.symbol->string;
-       bool                sign       = is_type_signed(type);
-       atomic_type_kind_t  akind;
-       if (strcmp_underscore("QI",   symbol_str) == 0 ||
-           strcmp_underscore("byte", symbol_str) == 0) {
-               akind = sign ? ATOMIC_TYPE_CHAR : ATOMIC_TYPE_UCHAR;
-       } else if (strcmp_underscore("HI", symbol_str) == 0) {
-               akind = sign ? ATOMIC_TYPE_SHORT : ATOMIC_TYPE_USHORT;
-       } else if (strcmp_underscore("SI",      symbol_str) == 0
-               || strcmp_underscore("word",    symbol_str) == 0
-               || strcmp_underscore("pointer", symbol_str) == 0) {
-               akind = sign ? ATOMIC_TYPE_INT : ATOMIC_TYPE_UINT;
-       } else if (strcmp_underscore("DI", symbol_str) == 0) {
-               akind = sign ? ATOMIC_TYPE_LONGLONG : ATOMIC_TYPE_ULONGLONG;
-       } else {
-               if (warning.other)
-                       warningf(HERE, "ignoring unknown mode '%s'", symbol_str);
-               return orig_type;
-       }
-
-       if (type->kind == TYPE_ATOMIC) {
-               type_t *copy       = duplicate_type(type);
-               copy->atomic.akind = akind;
-               return identify_new_type(copy);
-       } else if (type->kind == TYPE_ENUM) {
-               type_t *copy      = duplicate_type(type);
-               copy->enumt.akind = akind;
-               return identify_new_type(copy);
-       } else if (is_type_pointer(type)) {
-               warningf(HERE, "__attribute__((mode)) on pointers not implemented yet (ignored)");
-               return type;
-       }
+       rem_anchor_token(')');
+       expect(')', end_error);
+       return first;
 
-       errorf(HERE, "__attribute__((mode)) only allowed on integer, enum or pointer type");
-       return orig_type;
+end_error:
+       rem_anchor_token(')');
+       return first;
 }
 
-static type_t *handle_type_attributes(const gnu_attribute_t *attributes,
-                                      type_t *type)
+static entity_t *create_error_entity(symbol_t *symbol, entity_kind_tag_t kind)
 {
-       const gnu_attribute_t *attribute = attributes;
-       for ( ; attribute != NULL; attribute = attribute->next) {
-               if (attribute->invalid)
-                       continue;
-
-               if (attribute->kind == GNU_AK_MODE) {
-                       type = handle_attribute_mode(attribute, type);
-               } else if (attribute->kind == GNU_AK_ALIGNED) {
-                       int alignment = 32; /* TODO: fill in maximum useful alignment for
-                                              target machine */
-                       if (attribute->has_arguments)
-                               alignment = attribute->u.argument;
-
-                       type_t *copy         = duplicate_type(type);
-                       copy->base.alignment = attribute->u.argument;
-                       type                 = identify_new_type(copy);
-               }
+       entity_t *entity             = allocate_entity_zero(kind);
+       entity->base.source_position = *HERE;
+       entity->base.symbol          = symbol;
+       if (is_declaration(entity)) {
+               entity->declaration.type     = type_error_type;
+               entity->declaration.implicit = true;
+       } else if (kind == ENTITY_TYPEDEF) {
+               entity->typedefe.type    = type_error_type;
+               entity->typedefe.builtin = true;
        }
-
-       return type;
+       if (kind != ENTITY_COMPOUND_MEMBER)
+               record_entity(entity, false);
+       return entity;
 }
 
 static void parse_declaration_specifiers(declaration_specifiers_t *specifiers)
 {
        type_t            *type              = NULL;
        type_qualifiers_t  qualifiers        = TYPE_QUALIFIER_NONE;
-       type_modifiers_t   modifiers         = TYPE_MODIFIER_NONE;
        unsigned           type_specifiers   = 0;
        bool               newtype           = false;
        bool               saw_error         = false;
@@ -3658,8 +2850,7 @@ static void parse_declaration_specifiers(declaration_specifiers_t *specifiers)
        specifiers->source_position = token.source_position;
 
        while (true) {
-               specifiers->modifiers
-                       |= parse_attributes(&specifiers->gnu_attributes);
+               specifiers->attributes = parse_attributes(specifiers->attributes);
 
                switch (token.type) {
                /* storage class */
@@ -3681,12 +2872,8 @@ static void parse_declaration_specifiers(declaration_specifiers_t *specifiers)
                MATCH_STORAGE_CLASS(T_register, STORAGE_CLASS_REGISTER)
 
                case T__declspec:
-                       next_token();
-                       expect('(', end_error);
-                       add_anchor_token(')');
-                       parse_microsoft_extended_decl_modifier(specifiers);
-                       rem_anchor_token(')');
-                       expect(')', end_error);
+                       specifiers->attributes
+                               = parse_microsoft_extended_decl_modifier(specifiers->attributes);
                        break;
 
                case T___thread:
@@ -3702,10 +2889,10 @@ check_thread_storage_class:
                                                break;
 
                                                char const* wrong;
-                                       case STORAGE_CLASS_AUTO:     wrong = "auto";     goto wrong_thread_stoarge_class;
-                                       case STORAGE_CLASS_REGISTER: wrong = "register"; goto wrong_thread_stoarge_class;
-                                       case STORAGE_CLASS_TYPEDEF:  wrong = "typedef";  goto wrong_thread_stoarge_class;
-wrong_thread_stoarge_class:
+                                       case STORAGE_CLASS_AUTO:     wrong = "auto";     goto wrong_thread_storage_class;
+                                       case STORAGE_CLASS_REGISTER: wrong = "register"; goto wrong_thread_storage_class;
+                                       case STORAGE_CLASS_TYPEDEF:  wrong = "typedef";  goto wrong_thread_storage_class;
+wrong_thread_storage_class:
                                                errorf(HERE, "'__thread' used with '%s'", wrong);
                                                break;
                                }
@@ -3764,16 +2951,18 @@ wrong_thread_stoarge_class:
                MATCH_SPECIFIER(T_void,       SPECIFIER_VOID,      "void");
                MATCH_SPECIFIER(T_wchar_t,    SPECIFIER_WCHAR_T,   "wchar_t");
 
-               case T__forceinline:
-                       /* only in microsoft mode */
-                       specifiers->modifiers |= DM_FORCEINLINE;
-                       /* FALLTHROUGH */
-
                case T_inline:
                        next_token();
                        specifiers->is_inline = true;
                        break;
 
+#if 0
+               case T__forceinline:
+                       next_token();
+                       specifiers->modifiers |= DM_FORCEINLINE;
+                       break;
+#endif
+
                case T_long:
                        if (type_specifiers & SPECIFIER_LONG_LONG) {
                                errorf(HERE, "multiple type specifiers given");
@@ -3785,28 +2974,31 @@ wrong_thread_stoarge_class:
                        next_token();
                        break;
 
-               case T_struct: {
+#define CHECK_DOUBLE_TYPE()        \
+                       if ( type != NULL)     \
+                               errorf(HERE, "multiple data types in declaration specifiers");
+
+               case T_struct:
+                       CHECK_DOUBLE_TYPE();
                        type = allocate_type_zero(TYPE_COMPOUND_STRUCT);
 
                        type->compound.compound = parse_compound_type_specifier(true);
-                       finish_struct_type(&type->compound);
                        break;
-               }
-               case T_union: {
+               case T_union:
+                       CHECK_DOUBLE_TYPE();
                        type = allocate_type_zero(TYPE_COMPOUND_UNION);
                        type->compound.compound = parse_compound_type_specifier(false);
-                       if (type->compound.compound->modifiers & DM_TRANSPARENT_UNION)
-                               modifiers |= TYPE_MODIFIER_TRANSPARENT_UNION;
-                       finish_union_type(&type->compound);
                        break;
-               }
                case T_enum:
+                       CHECK_DOUBLE_TYPE();
                        type = parse_enum_specifier();
                        break;
                case T___typeof__:
+                       CHECK_DOUBLE_TYPE();
                        type = parse_typeof();
                        break;
                case T___builtin_va_list:
+                       CHECK_DOUBLE_TYPE();
                        type = duplicate_type(type_valist);
                        next_token();
                        break;
@@ -3837,7 +3029,7 @@ wrong_thread_stoarge_class:
                                }
                        }
 
-                       type_t *const typedef_type = get_typedef_type(token.v.symbol);
+                       type_t *const typedef_type = get_typedef_type(token.symbol);
                        if (typedef_type == NULL) {
                                /* Be somewhat resilient to typos like 'vodi f()' at the beginning of a
                                 * declaration, so it doesn't generate 'implicit int' followed by more
@@ -3851,7 +3043,7 @@ wrong_thread_stoarge_class:
                                                errorf(HERE, "%K does not name a type", &token);
 
                                                entity_t *entity =
-                                                       create_error_entity(token.v.symbol, ENTITY_TYPEDEF);
+                                                       create_error_entity(token.symbol, ENTITY_TYPEDEF);
 
                                                type = allocate_type_zero(TYPE_TYPEDEF);
                                                type->typedeft.typedefe = &entity->typedefe;
@@ -3880,8 +3072,7 @@ wrong_thread_stoarge_class:
        }
 
 finish_specifiers:
-       specifiers->modifiers
-               |= parse_attributes(&specifiers->gnu_attributes);
+       specifiers->attributes = parse_attributes(specifiers->attributes);
 
        in_gcc_extension = old_gcc_extension;
 
@@ -4060,21 +3251,13 @@ warn_about_long_long:
                        type                 = allocate_type_zero(TYPE_ATOMIC);
                        type->atomic.akind   = atomic_type;
                }
-               type->base.alignment = get_atomic_type_alignment(atomic_type);
-               unsigned const size  = get_atomic_type_size(atomic_type);
-               type->base.size      =
-                       type_specifiers & SPECIFIER_COMPLEX ? size * 2 : size;
                newtype = true;
        } else if (type_specifiers != 0) {
                errorf(HERE, "multiple datatypes in declaration");
        }
 
        /* FIXME: check type qualifiers here */
-
-       if (specifiers->modifiers & DM_TRANSPARENT_UNION)
-               modifiers |= TYPE_MODIFIER_TRANSPARENT_UNION;
        type->base.qualifiers = qualifiers;
-       type->base.modifiers  = modifiers;
 
        if (newtype) {
                type = identify_new_type(type);
@@ -4082,13 +3265,13 @@ warn_about_long_long:
                type = typehash_insert(type);
        }
 
-       type = handle_type_attributes(specifiers->gnu_attributes, type);
+       if (specifiers->attributes != NULL)
+               type = handle_type_attributes(specifiers->attributes, type);
        specifiers->type = type;
        return;
 
 end_error:
        specifiers->type = type_error_type;
-       return;
 }
 
 static type_qualifiers_t parse_type_qualifiers(void)
@@ -4123,18 +3306,13 @@ static void parse_identifier_list(scope_t *scope)
                entity_t *entity = allocate_entity_zero(ENTITY_PARAMETER);
                entity->base.source_position = token.source_position;
                entity->base.namespc         = NAMESPACE_NORMAL;
-               entity->base.symbol          = token.v.symbol;
+               entity->base.symbol          = token.symbol;
                /* a K&R parameter has no type, yet */
                next_token();
 
                if (scope != NULL)
                        append_entity(scope, entity);
-
-               if (token.type != ',') {
-                       break;
-               }
-               next_token();
-       } while (token.type == T_IDENTIFIER);
+       } while (next_if(',') && token.type == T_IDENTIFIER);
 }
 
 static entity_t *parse_parameter(void)
@@ -4170,7 +3348,9 @@ static bool has_parameters(void)
 {
        /* func(void) is not a parameter */
        if (token.type == T_IDENTIFIER) {
-               entity_t const *const entity = get_entity(token.v.symbol, NAMESPACE_NORMAL);
+               entity_t const *const entity = get_entity(token.symbol, NAMESPACE_NORMAL);
+               if (entity == NULL)
+                       return true;
                if (entity->kind != ENTITY_TYPEDEF)
                        return true;
                if (skip_typeref(entity->typedefe.type) != type_void)
@@ -4195,11 +3375,10 @@ static void parse_parameters(function_type_t *type, scope_t *scope)
        int saved_comma_state = save_and_reset_anchor_state(',');
 
        if (token.type == T_IDENTIFIER &&
-           !is_typedef_symbol(token.v.symbol)) {
+           !is_typedef_symbol(token.symbol)) {
                token_type_t la1_type = (token_type_t)look_ahead(1)->type;
                if (la1_type == ',' || la1_type == ')') {
-                       type->kr_style_parameters    = true;
-                       type->unspecified_parameters = true;
+                       type->kr_style_parameters = true;
                        parse_identifier_list(scope);
                        goto parameters_finished;
                }
@@ -4214,7 +3393,7 @@ static void parse_parameters(function_type_t *type, scope_t *scope)
 
        if (has_parameters()) {
                function_parameter_t **anchor = &type->parameters;
-               for (;;) {
+               do {
                        switch (token.type) {
                        case T_DOTDOTDOT:
                                next_token();
@@ -4250,11 +3429,7 @@ static void parse_parameters(function_type_t *type, scope_t *scope)
                        default:
                                goto parameters_finished;
                        }
-                       if (token.type != ',') {
-                               goto parameters_finished;
-                       }
-                       next_token();
-               }
+               } while (next_if(','));
        }
 
 
@@ -4284,7 +3459,7 @@ typedef struct construct_type_base_t {
 typedef struct parsed_pointer_t {
        construct_type_base_t  base;
        type_qualifiers_t      type_qualifiers;
-       variable_t             *base_variable;  /**< MS __based extension. */
+       variable_t            *base_variable;  /**< MS __based extension. */
 } parsed_pointer_t;
 
 typedef struct parsed_reference_t {
@@ -4313,65 +3488,81 @@ union construct_type_t {
        parsed_array_t            array;
 };
 
-static construct_type_t *parse_pointer_declarator(variable_t *base_variable)
+static construct_type_t *allocate_declarator_zero(construct_type_kind_t const kind, size_t const size)
+{
+       construct_type_t *const cons = obstack_alloc(&temp_obst, size);
+       memset(cons, 0, size);
+       cons->kind = kind;
+       return cons;
+}
+
+/* Â§6.7.5.1 */
+static construct_type_t *parse_pointer_declarator(void)
 {
        eat('*');
 
-       construct_type_t *cons    = obstack_alloc(&temp_obst, sizeof(cons->pointer));
-       parsed_pointer_t *pointer = &cons->pointer;
-       memset(pointer, 0, sizeof(*pointer));
-       cons->kind               = CONSTRUCT_POINTER;
-       pointer->type_qualifiers = parse_type_qualifiers();
-       pointer->base_variable   = base_variable;
+       construct_type_t *const cons = allocate_declarator_zero(CONSTRUCT_POINTER, sizeof(parsed_pointer_t));
+       cons->pointer.type_qualifiers = parse_type_qualifiers();
+       //cons->pointer.base_variable   = base_variable;
 
        return cons;
 }
 
+/* ISO/IEC 14882:1998(E) Â§8.3.2 */
 static construct_type_t *parse_reference_declarator(void)
 {
        eat('&');
 
-       construct_type_t   *cons      = obstack_alloc(&temp_obst, sizeof(cons->reference));
-       parsed_reference_t *reference = &cons->reference;
-       memset(reference, 0, sizeof(*reference));
-       cons->kind = CONSTRUCT_REFERENCE;
+       if (!(c_mode & _CXX))
+               errorf(HERE, "references are only available for C++");
+
+       construct_type_t *const cons = allocate_declarator_zero(CONSTRUCT_REFERENCE, sizeof(parsed_reference_t));
 
        return cons;
 }
 
+/* Â§6.7.5.2 */
 static construct_type_t *parse_array_declarator(void)
 {
        eat('[');
        add_anchor_token(']');
 
-       construct_type_t *cons  = obstack_alloc(&temp_obst, sizeof(cons->array));
-       parsed_array_t   *array = &cons->array;
-       memset(array, 0, sizeof(*array));
-       cons->kind = CONSTRUCT_ARRAY;
+       construct_type_t *const cons  = allocate_declarator_zero(CONSTRUCT_ARRAY, sizeof(parsed_array_t));
+       parsed_array_t   *const array = &cons->array;
 
-       if (token.type == T_static) {
-               array->is_static = true;
-               next_token();
-       }
+       bool is_static = next_if(T_static);
 
        type_qualifiers_t type_qualifiers = parse_type_qualifiers();
-       if (type_qualifiers != 0) {
-               if (token.type == T_static) {
-                       array->is_static = true;
-                       next_token();
-               }
-       }
+
+       if (!is_static)
+               is_static = next_if(T_static);
+
        array->type_qualifiers = type_qualifiers;
+       array->is_static       = is_static;
 
+       expression_t *size = NULL;
        if (token.type == '*' && look_ahead(1)->type == ']') {
                array->is_variable = true;
                next_token();
        } else if (token.type != ']') {
-               expression_t *const size = parse_assignment_expression();
+               size = parse_assignment_expression();
+
+               /* Â§6.7.5.2:1  Array size must have integer type */
+               type_t *const orig_type = size->base.type;
+               type_t *const type      = skip_typeref(orig_type);
+               if (!is_type_integer(type) && is_type_valid(type)) {
+                       errorf(&size->base.source_position,
+                              "array size '%E' must have integer type but has type '%T'",
+                              size, orig_type);
+               }
+
                array->size = size;
                mark_vars_read(size, NULL);
        }
 
+       if (is_static && size == NULL)
+               errorf(HERE, "static array parameters require a size");
+
        rem_anchor_token(']');
        expect(']', end_error);
 
@@ -4379,87 +3570,58 @@ end_error:
        return cons;
 }
 
-static construct_type_t *parse_function_declarator(scope_t *scope,
-                                                   decl_modifiers_t modifiers)
+/* Â§6.7.5.3 */
+static construct_type_t *parse_function_declarator(scope_t *scope)
 {
        type_t          *type  = allocate_type_zero(TYPE_FUNCTION);
        function_type_t *ftype = &type->function;
 
-       ftype->linkage = current_linkage;
-
-       switch (modifiers & (DM_CDECL | DM_STDCALL | DM_FASTCALL | DM_THISCALL)) {
-               case DM_NONE:     break;
-               case DM_CDECL:    ftype->calling_convention = CC_CDECL;    break;
-               case DM_STDCALL:  ftype->calling_convention = CC_STDCALL;  break;
-               case DM_FASTCALL: ftype->calling_convention = CC_FASTCALL; break;
-               case DM_THISCALL: ftype->calling_convention = CC_THISCALL; break;
-
-               default:
-                       errorf(HERE, "multiple calling conventions in declaration");
-                       break;
-       }
+       ftype->linkage            = current_linkage;
+       ftype->calling_convention = CC_DEFAULT;
 
        parse_parameters(ftype, scope);
 
-       construct_type_t          *cons     = obstack_alloc(&temp_obst, sizeof(cons->function));
-       construct_function_type_t *function = &cons->function;
-       memset(function, 0, sizeof(*function));
-       cons->kind              = CONSTRUCT_FUNCTION;
-       function->function_type = type;
+       construct_type_t *const cons = allocate_declarator_zero(CONSTRUCT_FUNCTION, sizeof(construct_function_type_t));
+       cons->function.function_type = type;
 
        return cons;
 }
 
 typedef struct parse_declarator_env_t {
+       bool               may_be_abstract : 1;
+       bool               must_be_abstract : 1;
        decl_modifiers_t   modifiers;
        symbol_t          *symbol;
        source_position_t  source_position;
        scope_t            parameters;
+       attribute_t       *attributes;
 } parse_declarator_env_t;
 
-static construct_type_t *parse_inner_declarator(parse_declarator_env_t *env,
-               bool may_be_abstract)
+/* Â§6.7.5 */
+static construct_type_t *parse_inner_declarator(parse_declarator_env_t *env)
 {
        /* construct a single linked list of construct_type_t's which describe
         * how to construct the final declarator type */
        construct_type_t  *first      = NULL;
        construct_type_t **anchor     = &first;
-       gnu_attribute_t   *attributes = NULL;
 
-       decl_modifiers_t modifiers = parse_attributes(&attributes);
+       env->attributes = parse_attributes(env->attributes);
 
        for (;;) {
                construct_type_t *type;
-               variable_t       *based = NULL; /* MS __based extension */
+               //variable_t       *based = NULL; /* MS __based extension */
                switch (token.type) {
                        case '&':
-                               if (!(c_mode & _CXX))
-                                       errorf(HERE, "references are only available for C++");
                                type = parse_reference_declarator();
                                break;
 
                        case T__based: {
-                               source_position_t const pos = *HERE;
-                               next_token();
-                               expect('(', end_error);
-                               add_anchor_token(')');
-                               based = parse_microsoft_based();
-                               rem_anchor_token(')');
-                               expect(')', end_error);
-                               if (token.type != '*') {
-                                       if (token.type == T__based) {
-                                               errorf(&pos, "__based type modifier specified more than once");
-                                       } else if (warning.other) {
-                                               warningf(&pos,
-                                                               "__based does not precede a pointer declarator, ignored");
-                                       }
-                                       continue;
-                               }
+                               panic("based not supported anymore");
                                /* FALLTHROUGH */
                        }
 
                        case '*':
-                               type = parse_pointer_declarator(based);
+                               type = parse_pointer_declarator();
                                break;
 
                        default:
@@ -4470,23 +3632,18 @@ static construct_type_t *parse_inner_declarator(parse_declarator_env_t *env,
                anchor  = &type->base.next;
 
                /* TODO: find out if this is correct */
-               modifiers |= parse_attributes(&attributes);
-       }
-ptr_operator_end:
-
-       if (env != NULL) {
-               modifiers      |= env->modifiers;
-               env->modifiers  = modifiers;
+               env->attributes = parse_attributes(env->attributes);
        }
 
+ptr_operator_end: ;
        construct_type_t *inner_types = NULL;
 
        switch (token.type) {
        case T_IDENTIFIER:
-               if (env == NULL) {
+               if (env->must_be_abstract) {
                        errorf(HERE, "no identifier expected in typename");
                } else {
-                       env->symbol          = token.v.symbol;
+                       env->symbol          = token.symbol;
                        env->source_position = token.source_position;
                }
                next_token();
@@ -4498,19 +3655,23 @@ ptr_operator_end:
                if (look_ahead(1)->type != ')') {
                        next_token();
                        add_anchor_token(')');
-                       inner_types = parse_inner_declarator(env, may_be_abstract);
+                       inner_types = parse_inner_declarator(env);
                        if (inner_types != NULL) {
                                /* All later declarators only modify the return type */
-                               env = NULL;
+                               env->must_be_abstract = true;
                        }
                        rem_anchor_token(')');
                        expect(')', end_error);
+               } else if (!env->may_be_abstract) {
+                       errorf(HERE, "declarator must have a name");
+                       goto error_out;
                }
                break;
        default:
-               if (may_be_abstract)
+               if (env->may_be_abstract)
                        break;
                parse_error_expected("while parsing declarator", T_IDENTIFIER, '(', NULL);
+error_out:
                eat_until_anchor();
                return NULL;
        }
@@ -4522,10 +3683,11 @@ ptr_operator_end:
                switch (token.type) {
                case '(': {
                        scope_t *scope = NULL;
-                       if (env != NULL)
+                       if (!env->must_be_abstract) {
                                scope = &env->parameters;
+                       }
 
-                       type = parse_function_declarator(scope, modifiers);
+                       type = parse_function_declarator(scope);
                        break;
                }
                case '[':
@@ -4552,70 +3714,8 @@ end_error:
        return NULL;
 }
 
-static void parse_declaration_attributes(entity_t *entity)
-{
-       gnu_attribute_t  *attributes = NULL;
-       decl_modifiers_t  modifiers  = parse_attributes(&attributes);
-
-       if (entity == NULL)
-               return;
-
-       type_t *type;
-       if (entity->kind == ENTITY_TYPEDEF) {
-               modifiers |= entity->typedefe.modifiers;
-               type       = entity->typedefe.type;
-       } else {
-               assert(is_declaration(entity));
-               modifiers |= entity->declaration.modifiers;
-               type       = entity->declaration.type;
-       }
-       if (type == NULL)
-               return;
-
-       gnu_attribute_t *attribute = attributes;
-       for ( ; attribute != NULL; attribute = attribute->next) {
-               if (attribute->invalid)
-                       continue;
-
-               if (attribute->kind == GNU_AK_MODE) {
-                       type = handle_attribute_mode(attribute, type);
-               } else if (attribute->kind == GNU_AK_ALIGNED) {
-                       int alignment = 32; /* TODO: fill in maximum usefull alignment for target machine */
-                       if (attribute->has_arguments)
-                               alignment = attribute->u.argument;
-
-                       if (entity->kind == ENTITY_TYPEDEF) {
-                               type_t *copy         = duplicate_type(type);
-                               copy->base.alignment = attribute->u.argument;
-                               type                 = identify_new_type(copy);
-                       } else if(entity->kind == ENTITY_VARIABLE) {
-                               entity->variable.alignment = alignment;
-                       } else if(entity->kind == ENTITY_COMPOUND_MEMBER) {
-                               entity->compound_member.alignment = alignment;
-                       }
-               }
-       }
-
-       type_modifiers_t type_modifiers = type->base.modifiers;
-       if (modifiers & DM_TRANSPARENT_UNION)
-               type_modifiers |= TYPE_MODIFIER_TRANSPARENT_UNION;
-
-       if (type->base.modifiers != type_modifiers) {
-               type_t *copy         = duplicate_type(type);
-               copy->base.modifiers = type_modifiers;
-               type                 = identify_new_type(copy);
-       }
-
-       if (entity->kind == ENTITY_TYPEDEF) {
-               entity->typedefe.type      = type;
-               entity->typedefe.modifiers = modifiers;
-       } else {
-               entity->declaration.type      = type;
-               entity->declaration.modifiers = modifiers;
-       }
-}
-
-static type_t *construct_declarator_type(construct_type_t *construct_list, type_t *type)
+static type_t *construct_declarator_type(construct_type_t *construct_list,
+                                         type_t *type)
 {
        construct_type_t *iter = construct_list;
        for (; iter != NULL; iter = iter->base.next) {
@@ -4683,23 +3783,31 @@ static type_t *construct_declarator_type(construct_type_t *construct_list, type_
                        array_type->array.size_expression = size_expression;
 
                        if (size_expression != NULL) {
-                               if (is_constant_expression(size_expression)) {
-                                       long const size                 = fold_constant(size_expression);
-                                       array_type->array.size          = size;
-                                       array_type->array.size_constant = true;
-                                       /* Â§6.7.5.2:1  If the expression is a constant expression, it shall
-                                        * have a value greater than zero. */
-                                       if (size <= 0) {
-                                               if (size < 0 || !GNU_MODE) {
-                                                       errorf(&size_expression->base.source_position,
-                                                                       "size of array must be greater than zero");
-                                               } else if (warning.other) {
-                                                       warningf(&size_expression->base.source_position,
-                                                                       "zero length arrays are a GCC extension");
+                               switch (is_constant_expression(size_expression)) {
+                                       case EXPR_CLASS_CONSTANT: {
+                                               long const size = fold_constant_to_int(size_expression);
+                                               array_type->array.size          = size;
+                                               array_type->array.size_constant = true;
+                                               /* Â§6.7.5.2:1  If the expression is a constant expression, it shall
+                                                * have a value greater than zero. */
+                                               if (size <= 0) {
+                                                       if (size < 0 || !GNU_MODE) {
+                                                               errorf(&size_expression->base.source_position,
+                                                                               "size of array must be greater than zero");
+                                                       } else if (warning.other) {
+                                                               warningf(&size_expression->base.source_position,
+                                                                               "zero length arrays are a GCC extension");
+                                                       }
                                                }
+                                               break;
                                        }
-                               } else {
-                                       array_type->array.is_vla = true;
+
+                                       case EXPR_CLASS_VARIABLE:
+                                               array_type->array.is_vla = true;
+                                               break;
+
+                                       case EXPR_CLASS_ERROR:
+                                               break;
                                }
                        }
 
@@ -4760,10 +3868,9 @@ static entity_t *parse_declarator(const declaration_specifiers_t *specifiers,
 {
        parse_declarator_env_t env;
        memset(&env, 0, sizeof(env));
-       env.modifiers = specifiers->modifiers;
+       env.may_be_abstract = (flags & DECL_MAY_BE_ABSTRACT) != 0;
 
-       construct_type_t *construct_type =
-               parse_inner_declarator(&env, (flags & DECL_MAY_BE_ABSTRACT) != 0);
+       construct_type_t *construct_type = parse_inner_declarator(&env);
        type_t           *orig_type      =
                construct_declarator_type(construct_type, specifiers->type);
        type_t           *type           = skip_typeref(orig_type);
@@ -4772,6 +3879,14 @@ static entity_t *parse_declarator(const declaration_specifiers_t *specifiers,
                obstack_free(&temp_obst, construct_type);
        }
 
+       attribute_t *attributes = parse_attributes(env.attributes);
+       /* append (shared) specifier attribute behind attributes of this
+        * declarator */
+       attribute_t **anchor = &attributes;
+       while (*anchor != NULL)
+               anchor = &(*anchor)->next;
+       *anchor = specifiers->attributes;
+
        entity_t *entity;
        if (specifiers->storage_class == STORAGE_CLASS_TYPEDEF) {
                entity                       = allocate_entity_zero(ENTITY_TYPEDEF);
@@ -4823,10 +3938,13 @@ static entity_t *parse_declarator(const declaration_specifiers_t *specifiers,
                        entity->function.parameters = env.parameters;
 
                        if (env.symbol != NULL) {
+                               /* this needs fixes for C++ */
+                               bool in_function_scope = current_function != NULL;
+
                                if (specifiers->thread_local || (
                                                        specifiers->storage_class != STORAGE_CLASS_EXTERN &&
                                                        specifiers->storage_class != STORAGE_CLASS_NONE   &&
-                                                       specifiers->storage_class != STORAGE_CLASS_STATIC
+                                                       (in_function_scope || specifiers->storage_class != STORAGE_CLASS_STATIC)
                                                )) {
                                        errorf(&env.source_position,
                                                        "invalid storage class for function '%Y'", env.symbol);
@@ -4835,9 +3953,6 @@ static entity_t *parse_declarator(const declaration_specifiers_t *specifiers,
                } else {
                        entity = allocate_entity_zero(ENTITY_VARIABLE);
 
-                       entity->variable.get_property_sym = specifiers->get_property_sym;
-                       entity->variable.put_property_sym = specifiers->put_property_sym;
-
                        entity->variable.thread_local = specifiers->thread_local;
 
                        if (env.symbol != NULL) {
@@ -4872,32 +3987,41 @@ static entity_t *parse_declarator(const declaration_specifiers_t *specifiers,
                } else {
                        entity->base.source_position = specifiers->source_position;
                }
-               entity->base.namespc                  = NAMESPACE_NORMAL;
-               entity->declaration.type              = orig_type;
-               entity->declaration.modifiers         = env.modifiers;
-               entity->declaration.deprecated_string = specifiers->deprecated_string;
+               entity->base.namespc           = NAMESPACE_NORMAL;
+               entity->declaration.type       = orig_type;
+               entity->declaration.alignment  = get_type_alignment(orig_type);
+               entity->declaration.modifiers  = env.modifiers;
+               entity->declaration.attributes = attributes;
 
                storage_class_t storage_class = specifiers->storage_class;
                entity->declaration.declared_storage_class = storage_class;
 
-               if (storage_class == STORAGE_CLASS_NONE && current_scope != file_scope)
+               if (storage_class == STORAGE_CLASS_NONE && current_function != NULL)
                        storage_class = STORAGE_CLASS_AUTO;
                entity->declaration.storage_class = storage_class;
        }
 
-       parse_declaration_attributes(entity);
+       if (attributes != NULL) {
+               handle_entity_attributes(attributes, entity);
+       }
 
        return entity;
 }
 
 static type_t *parse_abstract_declarator(type_t *base_type)
 {
-       construct_type_t *construct_type = parse_inner_declarator(NULL, 1);
+       parse_declarator_env_t env;
+       memset(&env, 0, sizeof(env));
+       env.may_be_abstract = true;
+       env.must_be_abstract = true;
+
+       construct_type_t *construct_type = parse_inner_declarator(&env);
 
        type_t *result = construct_declarator_type(construct_type, base_type);
        if (construct_type != NULL) {
                obstack_free(&temp_obst, construct_type);
        }
+       result = handle_type_attributes(env.attributes, result);
 
        return result;
 }
@@ -4912,7 +4036,7 @@ static type_t *parse_abstract_declarator(type_t *base_type)
  * @param decl    the declaration to check
  * @param type    the function type of the declaration
  */
-static void check_type_of_main(const entity_t *entity)
+static void check_main(const entity_t *entity)
 {
        const source_position_t *pos = &entity->base.source_position;
        if (entity->kind != ENTITY_FUNCTION) {
@@ -4989,11 +4113,52 @@ static bool is_error_entity(entity_t *const ent)
        return false;
 }
 
+static bool contains_attribute(const attribute_t *list, const attribute_t *attr)
+{
+       for (const attribute_t *tattr = list; tattr != NULL; tattr = tattr->next) {
+               if (attributes_equal(tattr, attr))
+                       return true;
+       }
+       return false;
+}
+
+/**
+ * test wether new_list contains any attributes not included in old_list
+ */
+static bool has_new_attributes(const attribute_t *old_list,
+                               const attribute_t *new_list)
+{
+       for (const attribute_t *attr = new_list; attr != NULL; attr = attr->next) {
+               if (!contains_attribute(old_list, attr))
+                       return true;
+       }
+       return false;
+}
+
+/**
+ * Merge in attributes from an attribute list (probably from a previous
+ * declaration with the same name). Warning: destroys the old structure
+ * of the attribute list - don't reuse attributes after this call.
+ */
+static void merge_in_attributes(declaration_t *decl, attribute_t *attributes)
+{
+       attribute_t *next;
+       for (attribute_t *attr = attributes; attr != NULL; attr = next) {
+               next = attr->next;
+               if (contains_attribute(decl->attributes, attr))
+                       continue;
+
+               /* move attribute to new declarations attributes list */
+               attr->next       = decl->attributes;
+               decl->attributes = attr;
+       }
+}
+
 /**
  * record entities for the NAMESPACE_NORMAL, and produce error messages/warnings
  * for various problems that occur for multiple definitions
  */
-static entity_t *record_entity(entity_t *entity, const bool is_definition)
+entity_t *record_entity(entity_t *entity, const bool is_definition)
 {
        const symbol_t *const    symbol  = entity->base.symbol;
        const namespace_tag_t    namespc = (namespace_tag_t)entity->base.namespc;
@@ -5021,7 +4186,7 @@ static entity_t *record_entity(entity_t *entity, const bool is_definition)
 
                if (warning.main && current_scope == file_scope
                                && is_sym_main(symbol)) {
-                       check_type_of_main(entity);
+                       check_main(entity);
                }
        }
 
@@ -5079,7 +4244,6 @@ static entity_t *record_entity(entity_t *entity, const bool is_definition)
                                prev_decl->storage_class          = decl->storage_class;
                                prev_decl->declared_storage_class = decl->declared_storage_class;
                                prev_decl->modifiers              = decl->modifiers;
-                               prev_decl->deprecated_string      = decl->deprecated_string;
                                return previous_entity;
                        }
 
@@ -5095,6 +4259,7 @@ static entity_t *record_entity(entity_t *entity, const bool is_definition)
                                                &previous_entity->base.source_position);
                        } else {
                                unsigned old_storage_class = prev_decl->storage_class;
+
                                if (warning.redundant_decls               &&
                                                is_definition                     &&
                                                !prev_decl->used                  &&
@@ -5143,22 +4308,28 @@ static entity_t *record_entity(entity_t *entity, const bool is_definition)
 
                                if (old_storage_class == STORAGE_CLASS_EXTERN &&
                                                new_storage_class == STORAGE_CLASS_EXTERN) {
-warn_redundant_declaration:
-                                       if (!is_definition           &&
+
+warn_redundant_declaration: ;
+                                       bool has_new_attrs
+                                               = has_new_attributes(prev_decl->attributes,
+                                                                    decl->attributes);
+                                       if (has_new_attrs) {
+                                               merge_in_attributes(decl, prev_decl->attributes);
+                                       } else if (!is_definition        &&
                                                        warning.redundant_decls  &&
                                                        is_type_valid(prev_type) &&
                                                        strcmp(previous_entity->base.source_position.input_name,
                                                                "<builtin>") != 0) {
                                                warningf(pos,
-                                                               "redundant declaration for '%Y' (declared %P)",
-                                                               symbol, &previous_entity->base.source_position);
+                                                        "redundant declaration for '%Y' (declared %P)",
+                                                        symbol, &previous_entity->base.source_position);
                                        }
                                } else if (current_function == NULL) {
                                        if (old_storage_class != STORAGE_CLASS_STATIC &&
                                                        new_storage_class == STORAGE_CLASS_STATIC) {
                                                errorf(pos,
-                                                               "static declaration of '%Y' follows non-static declaration (declared %P)",
-                                                               symbol, &previous_entity->base.source_position);
+                                                      "static declaration of '%Y' follows non-static declaration (declared %P)",
+                                                      symbol, &previous_entity->base.source_position);
                                        } else if (old_storage_class == STORAGE_CLASS_EXTERN) {
                                                prev_decl->storage_class          = STORAGE_CLASS_NONE;
                                                prev_decl->declared_storage_class = STORAGE_CLASS_NONE;
@@ -5244,7 +4415,7 @@ static bool is_declaration_specifier(const token_t *token,
                TYPE_QUALIFIERS
                        return true;
                case T_IDENTIFIER:
-                       return is_typedef_symbol(token->v.symbol);
+                       return is_typedef_symbol(token->symbol);
 
                case T___extension__:
                STORAGE_CLASSES
@@ -5392,9 +4563,8 @@ static void parse_declaration_rest(entity_t *ndeclaration,
 
                check_variable_type_complete(entity);
 
-               if (token.type != ',')
+               if (!next_if(','))
                        break;
-               eat(',');
 
                add_anchor_token('=');
                ndeclaration = parse_declarator(specifiers, flags);
@@ -5450,6 +4620,7 @@ static void parse_declaration(parsed_declaration_func finished_declaration,
        }
 }
 
+/* Â§6.5.2.2:6 */
 static type_t *get_default_promoted_type(type_t *orig_type)
 {
        type_t *result = orig_type;
@@ -5474,7 +4645,6 @@ static void parse_kr_declaration_list(entity_t *entity)
        if (!type->function.kr_style_parameters)
                return;
 
-
        add_anchor_token('{');
 
        /* push function parameters */
@@ -5516,8 +4686,30 @@ decl_list_end:
        function_parameter_t  *parameters = NULL;
        function_parameter_t **anchor     = &parameters;
 
+       /* did we have an earlier prototype? */
+       entity_t *proto_type = get_entity(entity->base.symbol, NAMESPACE_NORMAL);
+       if (proto_type != NULL && proto_type->kind != ENTITY_FUNCTION)
+               proto_type = NULL;
+
+       function_parameter_t *proto_parameter = NULL;
+       if (proto_type != NULL) {
+               type_t *proto_type_type = proto_type->declaration.type;
+               proto_parameter         = proto_type_type->function.parameters;
+               /* If a K&R function definition has a variadic prototype earlier, then
+                * make the function definition variadic, too. This should conform to
+                * Â§6.7.5.3:15 and Â§6.9.1:8. */
+               new_type->function.variadic = proto_type_type->function.variadic;
+       } else {
+               /* Â§6.9.1.7: A K&R style parameter list does NOT act as a function
+                * prototype */
+               new_type->function.unspecified_parameters = true;
+       }
+
+       bool need_incompatible_warning = false;
        parameter = entity->function.parameters.entities;
-       for (; parameter != NULL; parameter = parameter->base.next) {
+       for (; parameter != NULL; parameter = parameter->base.next,
+                       proto_parameter =
+                               proto_parameter == NULL ? NULL : proto_parameter->next) {
                if (parameter->kind != ENTITY_PARAMETER)
                        continue;
 
@@ -5526,38 +4718,55 @@ decl_list_end:
                        if (strict_mode) {
                                errorf(HERE, "no type specified for function parameter '%Y'",
                                       parameter->base.symbol);
+                               parameter_type = type_error_type;
                        } else {
                                if (warning.implicit_int) {
                                        warningf(HERE, "no type specified for function parameter '%Y', using 'int'",
                                                 parameter->base.symbol);
                                }
-                               parameter_type              = type_int;
-                               parameter->declaration.type = parameter_type;
+                               parameter_type = type_int;
                        }
+                       parameter->declaration.type = parameter_type;
                }
 
                semantic_parameter_incomplete(parameter);
-               parameter_type = parameter->declaration.type;
 
-               /*
-                * we need the default promoted types for the function type
-                */
-               parameter_type = get_default_promoted_type(parameter_type);
+               /* we need the default promoted types for the function type */
+               type_t *not_promoted = parameter_type;
+               parameter_type       = get_default_promoted_type(parameter_type);
 
-               function_parameter_t *const parameter =
-                       allocate_parameter(parameter_type);
+               /* gcc special: if the type of the prototype matches the unpromoted
+                * type don't promote */
+               if (!strict_mode && proto_parameter != NULL) {
+                       type_t *proto_p_type = skip_typeref(proto_parameter->type);
+                       type_t *promo_skip   = skip_typeref(parameter_type);
+                       type_t *param_skip   = skip_typeref(not_promoted);
+                       if (!types_compatible(proto_p_type, promo_skip)
+                               && types_compatible(proto_p_type, param_skip)) {
+                               /* don't promote */
+                               need_incompatible_warning = true;
+                               parameter_type = not_promoted;
+                       }
+               }
+               function_parameter_t *const parameter
+                       = allocate_parameter(parameter_type);
 
                *anchor = parameter;
                anchor  = &parameter->next;
        }
 
-       /* Â§6.9.1.7: A K&R style parameter list does NOT act as a function
-        * prototype */
-       new_type->function.parameters             = parameters;
-       new_type->function.unspecified_parameters = true;
-
+       new_type->function.parameters = parameters;
        new_type = identify_new_type(new_type);
 
+       if (warning.other && need_incompatible_warning) {
+               type_t *proto_type_type = proto_type->declaration.type;
+               warningf(HERE,
+                        "declaration '%#T' is incompatible with '%#T' (declared %P)",
+                        proto_type_type, proto_type->base.symbol,
+                        new_type, entity->base.symbol,
+                        &proto_type->base.source_position);
+       }
+
        entity->declaration.type = new_type;
 
        rem_anchor_token('{');
@@ -5685,8 +4894,8 @@ static void check_declarations(void)
 static int determine_truth(expression_t const* const cond)
 {
        return
-               !is_constant_expression(cond) ? 0 :
-               fold_constant(cond) != 0      ? 1 :
+               is_constant_expression(cond) != EXPR_CLASS_CONSTANT ? 0 :
+               fold_constant_to_bool(cond)                         ? 1 :
                -1;
 }
 
@@ -5718,9 +4927,7 @@ static bool expression_returns(expression_t const *const expr)
 
                case EXPR_REFERENCE:
                case EXPR_REFERENCE_ENUM_VALUE:
-               case EXPR_CONST:
-               case EXPR_CHARACTER_CONSTANT:
-               case EXPR_WIDE_CHARACTER_CONSTANT:
+               EXPR_LITERAL_CASES
                case EXPR_STRING_LITERAL:
                case EXPR_WIDE_STRING_LITERAL:
                case EXPR_COMPOUND_LITERAL: // TODO descend into initialisers
@@ -5770,6 +4977,9 @@ static bool expression_returns(expression_t const *const expr)
                case EXPR_VA_ARG:
                        return expression_returns(expr->va_arge.ap);
 
+               case EXPR_VA_COPY:
+                       return expression_returns(expr->va_copye.src);
+
                EXPR_UNARY_CASES_MANDATORY
                        return expression_returns(expr->unary.value);
 
@@ -5895,8 +5105,8 @@ static void check_reachable(statement_t *const stmt)
                        if (!expression_returns(expr))
                                return;
 
-                       if (is_constant_expression(expr)) {
-                               long                    const val      = fold_constant(expr);
+                       if (is_constant_expression(expr) == EXPR_CLASS_CONSTANT) {
+                               long                    const val      = fold_constant_to_int(expr);
                                case_label_statement_t *      defaults = NULL;
                                for (case_label_statement_t *i = switchs->first_case; i != NULL; i = i->next) {
                                        if (i->expression == NULL) {
@@ -5941,9 +5151,8 @@ static void check_reachable(statement_t *const stmt)
                        break;
                }
 
-               case STATEMENT_CONTINUE: {
-                       statement_t *parent = stmt;
-                       for (;;) {
+               case STATEMENT_CONTINUE:
+                       for (statement_t *parent = stmt;;) {
                                parent = parent->base.parent;
                                if (parent == NULL) /* continue not within loop */
                                        return;
@@ -5957,11 +5166,9 @@ static void check_reachable(statement_t *const stmt)
                                        default: break;
                                }
                        }
-               }
 
-               case STATEMENT_BREAK: {
-                       statement_t *parent = stmt;
-                       for (;;) {
+               case STATEMENT_BREAK:
+                       for (statement_t *parent = stmt;;) {
                                parent = parent->base.parent;
                                if (parent == NULL) /* break not within loop/switch */
                                        return;
@@ -5980,7 +5187,6 @@ static void check_reachable(statement_t *const stmt)
                        }
 found_break_parent:
                        break;
-               }
 
                case STATEMENT_GOTO:
                        if (stmt->gotos.expression) {
@@ -6376,8 +5582,7 @@ static void parse_external_declaration(void)
        /* Â§6.7.5.3:14 a function definition with () means no
         * parameters (and not unspecified parameters) */
        if (type->function.unspecified_parameters &&
-                       type->function.parameters == NULL     &&
-                       !type->function.kr_style_parameters) {
+                       type->function.parameters == NULL) {
                type_t *copy                          = duplicate_type(type);
                copy->function.unspecified_parameters = false;
                type                                  = identify_new_type(copy);
@@ -6422,7 +5627,9 @@ static void parse_external_declaration(void)
                /* parse function body */
                int         label_stack_top      = label_top();
                function_t *old_current_function = current_function;
+               entity_t   *old_current_entity   = current_entity;
                current_function                 = function;
+               current_entity                   = (entity_t*) function;
                current_parent                   = NULL;
 
                goto_first   = NULL;
@@ -6454,6 +5661,8 @@ static void parse_external_declaration(void)
 
                assert(current_parent   == NULL);
                assert(current_function == function);
+               assert(current_entity   == (entity_t*) function);
+               current_entity   = old_current_entity;
                current_function = old_current_function;
                label_pop_to(label_stack_top);
        }
@@ -6476,21 +5685,25 @@ static type_t *make_bitfield_type(type_t *base_type, expression_t *size,
        type_t *skipped_type = skip_typeref(base_type);
        if (!is_type_integer(skipped_type)) {
                errorf(HERE, "bitfield base type '%T' is not an integer type",
-                       base_type);
+                      base_type);
                bit_size = 0;
        } else {
-               bit_size = skipped_type->base.size * 8;
+               bit_size = get_type_size(base_type) * 8;
        }
 
-       if (is_constant_expression(size)) {
-               long v = fold_constant(size);
+       if (is_constant_expression(size) == EXPR_CLASS_CONSTANT) {
+               long v = fold_constant_to_int(size);
+               const symbol_t *user_symbol = symbol == NULL ? sym_anonymous : symbol;
 
                if (v < 0) {
-                       errorf(source_position, "negative width in bit-field '%Y'", symbol);
-               } else if (v == 0) {
-                       errorf(source_position, "zero width for bit-field '%Y'", symbol);
+                       errorf(source_position, "negative width in bit-field '%Y'",
+                              user_symbol);
+               } else if (v == 0 && symbol != NULL) {
+                       errorf(source_position, "zero width for bit-field '%Y'",
+                              user_symbol);
                } else if (bit_size > 0 && (il_size_t)v > bit_size) {
-                       errorf(source_position, "width of '%Y' exceeds its type", symbol);
+                       errorf(source_position, "width of '%Y' exceeds its type",
+                              user_symbol);
                } else {
                        type->bitfield.bit_size = v;
                }
@@ -6509,12 +5722,12 @@ static entity_t *find_compound_entry(compound_t *compound, symbol_t *symbol)
                if (iter->base.symbol == symbol) {
                        return iter;
                } else if (iter->base.symbol == NULL) {
+                       /* search in anonymous structs and unions */
                        type_t *type = skip_typeref(iter->declaration.type);
                        if (is_type_compound(type)) {
-                               entity_t *result
-                                       = find_compound_entry(type->compound.compound, symbol);
-                               if (result != NULL)
-                                       return result;
+                               if (find_compound_entry(type->compound.compound, symbol)
+                                               != NULL)
+                                       return iter;
                        }
                        continue;
                }
@@ -6523,36 +5736,138 @@ static entity_t *find_compound_entry(compound_t *compound, symbol_t *symbol)
        return NULL;
 }
 
-static void parse_compound_declarators(compound_t *compound,
-               const declaration_specifiers_t *specifiers)
+static void check_deprecated(const source_position_t *source_position,
+                             const entity_t *entity)
 {
-       while (true) {
-               entity_t *entity;
+       if (!warning.deprecated_declarations)
+               return;
+       if (!is_declaration(entity))
+               return;
+       if ((entity->declaration.modifiers & DM_DEPRECATED) == 0)
+               return;
 
-               if (token.type == ':') {
-                       source_position_t source_position = *HERE;
-                       next_token();
+       char const *const prefix = get_entity_kind_name(entity->kind);
+       const char *deprecated_string
+                       = get_deprecated_string(entity->declaration.attributes);
+       if (deprecated_string != NULL) {
+               warningf(source_position, "%s '%Y' is deprecated (declared %P): \"%s\"",
+                                prefix, entity->base.symbol, &entity->base.source_position,
+                                deprecated_string);
+       } else {
+               warningf(source_position, "%s '%Y' is deprecated (declared %P)", prefix,
+                                entity->base.symbol, &entity->base.source_position);
+       }
+}
 
-                       type_t *base_type = specifiers->type;
-                       expression_t *size = parse_constant_expression();
 
-                       type_t *type = make_bitfield_type(base_type, size,
-                                       &source_position, sym_anonymous);
+static expression_t *create_select(const source_position_t *pos,
+                                   expression_t *addr,
+                                   type_qualifiers_t qualifiers,
+                                                                  entity_t *entry)
+{
+       assert(entry->kind == ENTITY_COMPOUND_MEMBER);
 
-                       entity = allocate_entity_zero(ENTITY_COMPOUND_MEMBER);
-                       entity->base.namespc                       = NAMESPACE_NORMAL;
-                       entity->base.source_position               = source_position;
-                       entity->declaration.declared_storage_class = STORAGE_CLASS_NONE;
-                       entity->declaration.storage_class          = STORAGE_CLASS_NONE;
-                       entity->declaration.modifiers              = specifiers->modifiers;
-                       entity->declaration.type                   = type;
-                       append_entity(&compound->members, entity);
-               } else {
-                       entity = parse_declarator(specifiers,
-                                       DECL_MAY_BE_ABSTRACT | DECL_CREATE_COMPOUND_MEMBER);
-                       if (entity->kind == ENTITY_TYPEDEF) {
-                               errorf(&entity->base.source_position,
-                                               "typedef not allowed as compound member");
+       check_deprecated(pos, entry);
+
+       expression_t *select          = allocate_expression_zero(EXPR_SELECT);
+       select->select.compound       = addr;
+       select->select.compound_entry = entry;
+
+       type_t *entry_type = entry->declaration.type;
+       type_t *res_type   = get_qualified_type(entry_type, qualifiers);
+
+       /* we always do the auto-type conversions; the & and sizeof parser contains
+        * code to revert this! */
+       select->base.type = automatic_type_conversion(res_type);
+       if (res_type->kind == TYPE_BITFIELD) {
+               select->base.type = res_type->bitfield.base_type;
+       }
+
+       return select;
+}
+
+/**
+ * Find entry with symbol in compound. Search anonymous structs and unions and
+ * creates implicit select expressions for them.
+ * Returns the adress for the innermost compound.
+ */
+static expression_t *find_create_select(const source_position_t *pos,
+                                        expression_t *addr,
+                                        type_qualifiers_t qualifiers,
+                                        compound_t *compound, symbol_t *symbol)
+{
+       entity_t *iter = compound->members.entities;
+       for (; iter != NULL; iter = iter->base.next) {
+               if (iter->kind != ENTITY_COMPOUND_MEMBER)
+                       continue;
+
+               symbol_t *iter_symbol = iter->base.symbol;
+               if (iter_symbol == NULL) {
+                       type_t *type = iter->declaration.type;
+                       if (type->kind != TYPE_COMPOUND_STRUCT
+                                       && type->kind != TYPE_COMPOUND_UNION)
+                               continue;
+
+                       compound_t *sub_compound = type->compound.compound;
+
+                       if (find_compound_entry(sub_compound, symbol) == NULL)
+                               continue;
+
+                       expression_t *sub_addr = create_select(pos, addr, qualifiers, iter);
+                       sub_addr->base.source_position = *pos;
+                       sub_addr->select.implicit      = true;
+                       return find_create_select(pos, sub_addr, qualifiers, sub_compound,
+                                                 symbol);
+               }
+
+               if (iter_symbol == symbol) {
+                       return create_select(pos, addr, qualifiers, iter);
+               }
+       }
+
+       return NULL;
+}
+
+static void parse_compound_declarators(compound_t *compound,
+               const declaration_specifiers_t *specifiers)
+{
+       do {
+               entity_t *entity;
+
+               if (token.type == ':') {
+                       source_position_t source_position = *HERE;
+                       next_token();
+
+                       type_t *base_type = specifiers->type;
+                       expression_t *size = parse_constant_expression();
+
+                       type_t *type = make_bitfield_type(base_type, size,
+                                       &source_position, NULL);
+
+                       attribute_t  *attributes = parse_attributes(NULL);
+                       attribute_t **anchor     = &attributes;
+                       while (*anchor != NULL)
+                               anchor = &(*anchor)->next;
+                       *anchor = specifiers->attributes;
+
+                       entity = allocate_entity_zero(ENTITY_COMPOUND_MEMBER);
+                       entity->base.namespc                       = NAMESPACE_NORMAL;
+                       entity->base.source_position               = source_position;
+                       entity->declaration.declared_storage_class = STORAGE_CLASS_NONE;
+                       entity->declaration.storage_class          = STORAGE_CLASS_NONE;
+                       entity->declaration.type                   = type;
+                       entity->declaration.attributes             = attributes;
+
+                       if (attributes != NULL) {
+                               handle_entity_attributes(attributes, entity);
+                       }
+                       append_entity(&compound->members, entity);
+               } else {
+                       entity = parse_declarator(specifiers,
+                                       DECL_MAY_BE_ABSTRACT | DECL_CREATE_COMPOUND_MEMBER);
+                       if (entity->kind == ENTITY_TYPEDEF) {
+                               errorf(&entity->base.source_position,
+                                               "typedef not allowed as compound member");
                        } else {
                                assert(entity->kind == ENTITY_COMPOUND_MEMBER);
 
@@ -6575,13 +5890,16 @@ static void parse_compound_declarators(compound_t *compound,
                                        type_t *type          = entity->declaration.type;
                                        type_t *bitfield_type = make_bitfield_type(type, size,
                                                        &source_position, entity->base.symbol);
+
+                                       attribute_t *attributes = parse_attributes(NULL);
                                        entity->declaration.type = bitfield_type;
+                                       handle_entity_attributes(attributes, entity);
                                } else {
                                        type_t *orig_type = entity->declaration.type;
                                        type_t *type      = skip_typeref(orig_type);
                                        if (is_type_function(type)) {
                                                errorf(&entity->base.source_position,
-                                                               "compound member '%Y' must not have function type '%T'",
+                                                      "compound member '%Y' must not have function type '%T'",
                                                                entity->base.symbol, orig_type);
                                        } else if (is_type_incomplete(type)) {
                                                /* Â§6.7.2.1:16 flexible array member */
@@ -6589,7 +5907,7 @@ static void parse_compound_declarators(compound_t *compound,
                                                                token.type          != ';' ||
                                                                look_ahead(1)->type != '}') {
                                                        errorf(&entity->base.source_position,
-                                                                       "compound member '%Y' has incomplete type '%T'",
+                                                              "compound member '%Y' has incomplete type '%T'",
                                                                        entity->base.symbol, orig_type);
                                                }
                                        }
@@ -6598,11 +5916,7 @@ static void parse_compound_declarators(compound_t *compound,
                                append_entity(&compound->members, entity);
                        }
                }
-
-               if (token.type != ',')
-                       break;
-               next_token();
-       }
+       } while (next_if(','));
        expect(';', end_error);
 
 end_error:
@@ -6637,12 +5951,12 @@ static type_t *parse_typename(void)
        declaration_specifiers_t specifiers;
        memset(&specifiers, 0, sizeof(specifiers));
        parse_declaration_specifiers(&specifiers);
-       if (specifiers.storage_class != STORAGE_CLASS_NONE ||
-                       specifiers.thread_local) {
+       if (specifiers.storage_class != STORAGE_CLASS_NONE
+                       || specifiers.thread_local) {
                /* TODO: improve error message, user does probably not know what a
                 * storage class is...
                 */
-               errorf(HERE, "typename may not have a storage class");
+               errorf(HERE, "typename must not have a storage class");
        }
 
        type_t *result = parse_abstract_declarator(specifiers.type);
@@ -6679,82 +5993,184 @@ static expression_t *expected_expression_error(void)
        return create_invalid_expression();
 }
 
+static type_t *get_string_type(void)
+{
+       return warning.write_strings ? type_const_char_ptr : type_char_ptr;
+}
+
+static type_t *get_wide_string_type(void)
+{
+       return warning.write_strings ? type_const_wchar_t_ptr : type_wchar_t_ptr;
+}
+
 /**
  * Parse a string constant.
  */
-static expression_t *parse_string_const(void)
+static expression_t *parse_string_literal(void)
 {
-       wide_string_t wres;
-       if (token.type == T_STRING_LITERAL) {
-               string_t res = token.v.string;
+       source_position_t begin   = token.source_position;
+       string_t          res     = token.literal;
+       bool              is_wide = (token.type == T_WIDE_STRING_LITERAL);
+
+       next_token();
+       while (token.type == T_STRING_LITERAL
+                       || token.type == T_WIDE_STRING_LITERAL) {
+               warn_string_concat(&token.source_position);
+               res = concat_strings(&res, &token.literal);
                next_token();
-               while (token.type == T_STRING_LITERAL) {
-                       res = concat_strings(&res, &token.v.string);
-                       next_token();
-               }
-               if (token.type != T_WIDE_STRING_LITERAL) {
-                       expression_t *const cnst = allocate_expression_zero(EXPR_STRING_LITERAL);
-                       /* note: that we use type_char_ptr here, which is already the
-                        * automatic converted type. revert_automatic_type_conversion
-                        * will construct the array type */
-                       cnst->base.type    = warning.write_strings ? type_const_char_ptr : type_char_ptr;
-                       cnst->string.value = res;
-                       return cnst;
-               }
+               is_wide |= token.type == T_WIDE_STRING_LITERAL;
+       }
 
-               wres = concat_string_wide_string(&res, &token.v.wide_string);
+       expression_t *literal;
+       if (is_wide) {
+               literal = allocate_expression_zero(EXPR_WIDE_STRING_LITERAL);
+               literal->base.type = get_wide_string_type();
        } else {
-               wres = token.v.wide_string;
+               literal = allocate_expression_zero(EXPR_STRING_LITERAL);
+               literal->base.type = get_string_type();
        }
+       literal->base.source_position = begin;
+       literal->literal.value        = res;
+
+       return literal;
+}
+
+/**
+ * Parse a boolean constant.
+ */
+static expression_t *parse_boolean_literal(bool value)
+{
+       expression_t *literal = allocate_expression_zero(EXPR_LITERAL_BOOLEAN);
+       literal->base.source_position = token.source_position;
+       literal->base.type            = type_bool;
+       literal->literal.value.begin  = value ? "true" : "false";
+       literal->literal.value.size   = value ? 4 : 5;
+
        next_token();
+       return literal;
+}
 
-       for (;;) {
-               switch (token.type) {
-                       case T_WIDE_STRING_LITERAL:
-                               wres = concat_wide_strings(&wres, &token.v.wide_string);
-                               break;
+static void warn_traditional_suffix(void)
+{
+       if (!warning.traditional)
+               return;
+       warningf(&token.source_position, "traditional C rejects the '%Y' suffix",
+                token.symbol);
+}
 
-                       case T_STRING_LITERAL:
-                               wres = concat_wide_string_string(&wres, &token.v.string);
-                               break;
+static void check_integer_suffix(void)
+{
+       symbol_t *suffix = token.symbol;
+       if (suffix == NULL)
+               return;
 
-                       default: {
-                               expression_t *const cnst = allocate_expression_zero(EXPR_WIDE_STRING_LITERAL);
-                               cnst->base.type         = warning.write_strings ? type_const_wchar_t_ptr : type_wchar_t_ptr;
-                               cnst->wide_string.value = wres;
-                               return cnst;
+       bool not_traditional = false;
+       const char *c = suffix->string;
+       if (*c == 'l' || *c == 'L') {
+               ++c;
+               if (*c == *(c-1)) {
+                       not_traditional = true;
+                       ++c;
+                       if (*c == 'u' || *c == 'U') {
+                               ++c;
                        }
+               } else if (*c == 'u' || *c == 'U') {
+                       not_traditional = true;
+                       ++c;
                }
-               next_token();
+       } else if (*c == 'u' || *c == 'U') {
+               not_traditional = true;
+               ++c;
+               if (*c == 'l' || *c == 'L') {
+                       ++c;
+                       if (*c == *(c-1)) {
+                               ++c;
+                       }
+               }
+       }
+       if (*c != '\0') {
+               errorf(&token.source_position,
+                      "invalid suffix '%s' on integer constant", suffix->string);
+       } else if (not_traditional) {
+               warn_traditional_suffix();
        }
 }
 
-/**
- * Parse a boolean constant.
- */
-static expression_t *parse_bool_const(bool value)
+static type_t *check_floatingpoint_suffix(void)
 {
-       expression_t *cnst       = allocate_expression_zero(EXPR_CONST);
-       cnst->base.type          = type_bool;
-       cnst->conste.v.int_value = value;
+       symbol_t *suffix = token.symbol;
+       type_t   *type   = type_double;
+       if (suffix == NULL)
+               return type;
 
-       next_token();
+       bool not_traditional = false;
+       const char *c = suffix->string;
+       if (*c == 'f' || *c == 'F') {
+               ++c;
+               type = type_float;
+       } else if (*c == 'l' || *c == 'L') {
+               ++c;
+               type = type_long_double;
+       }
+       if (*c != '\0') {
+               errorf(&token.source_position,
+                      "invalid suffix '%s' on floatingpoint constant", suffix->string);
+       } else if (not_traditional) {
+               warn_traditional_suffix();
+       }
 
-       return cnst;
+       return type;
 }
 
 /**
  * Parse an integer constant.
  */
-static expression_t *parse_int_const(void)
+static expression_t *parse_number_literal(void)
 {
-       expression_t *cnst       = allocate_expression_zero(EXPR_CONST);
-       cnst->base.type          = token.datatype;
-       cnst->conste.v.int_value = token.v.intvalue;
+       expression_kind_t  kind;
+       type_t            *type;
+
+       switch (token.type) {
+       case T_INTEGER:
+               kind = EXPR_LITERAL_INTEGER;
+               check_integer_suffix();
+               type = type_int;
+               break;
+       case T_INTEGER_OCTAL:
+               kind = EXPR_LITERAL_INTEGER_OCTAL;
+               check_integer_suffix();
+               type = type_int;
+               break;
+       case T_INTEGER_HEXADECIMAL:
+               kind = EXPR_LITERAL_INTEGER_HEXADECIMAL;
+               check_integer_suffix();
+               type = type_int;
+               break;
+       case T_FLOATINGPOINT:
+               kind = EXPR_LITERAL_FLOATINGPOINT;
+               type = check_floatingpoint_suffix();
+               break;
+       case T_FLOATINGPOINT_HEXADECIMAL:
+               kind = EXPR_LITERAL_FLOATINGPOINT_HEXADECIMAL;
+               type = check_floatingpoint_suffix();
+               break;
+       default:
+               panic("unexpected token type in parse_number_literal");
+       }
 
+       expression_t *literal = allocate_expression_zero(kind);
+       literal->base.source_position = token.source_position;
+       literal->base.type            = type;
+       literal->literal.value        = token.literal;
+       literal->literal.suffix       = token.symbol;
        next_token();
 
-       return cnst;
+       /* integer type depends on the size of the number and the size
+        * representable by the types. The backend/codegeneration has to determine
+        * that
+        */
+       determine_literal_type(&literal->literal);
+       return literal;
 }
 
 /**
@@ -6762,20 +6178,23 @@ static expression_t *parse_int_const(void)
  */
 static expression_t *parse_character_constant(void)
 {
-       expression_t *cnst = allocate_expression_zero(EXPR_CHARACTER_CONSTANT);
-       cnst->base.type          = token.datatype;
-       cnst->conste.v.character = token.v.string;
+       expression_t *literal = allocate_expression_zero(EXPR_LITERAL_CHARACTER);
+       literal->base.source_position = token.source_position;
+       literal->base.type            = c_mode & _CXX ? type_char : type_int;
+       literal->literal.value        = token.literal;
 
-       if (cnst->conste.v.character.size != 1) {
-               if (!GNU_MODE) {
+       size_t len = literal->literal.value.size;
+       if (len != 1) {
+               if (!GNU_MODE && !(c_mode & _C99)) {
                        errorf(HERE, "more than 1 character in character constant");
                } else if (warning.multichar) {
+                       literal->base.type = type_int;
                        warningf(HERE, "multi-character character constant");
                }
        }
-       next_token();
 
-       return cnst;
+       next_token();
+       return literal;
 }
 
 /**
@@ -6783,34 +6202,18 @@ static expression_t *parse_character_constant(void)
  */
 static expression_t *parse_wide_character_constant(void)
 {
-       expression_t *cnst = allocate_expression_zero(EXPR_WIDE_CHARACTER_CONSTANT);
-       cnst->base.type               = token.datatype;
-       cnst->conste.v.wide_character = token.v.wide_string;
+       expression_t *literal = allocate_expression_zero(EXPR_LITERAL_WIDE_CHARACTER);
+       literal->base.source_position = token.source_position;
+       literal->base.type            = type_int;
+       literal->literal.value        = token.literal;
 
-       if (cnst->conste.v.wide_character.size != 1) {
-               if (!GNU_MODE) {
-                       errorf(HERE, "more than 1 character in character constant");
-               } else if (warning.multichar) {
-                       warningf(HERE, "multi-character character constant");
-               }
+       size_t len = wstrlen(&literal->literal.value);
+       if (len != 1) {
+               warningf(HERE, "multi-character character constant");
        }
-       next_token();
-
-       return cnst;
-}
-
-/**
- * Parse a float constant.
- */
-static expression_t *parse_float_const(void)
-{
-       expression_t *cnst         = allocate_expression_zero(EXPR_CONST);
-       cnst->base.type            = token.datatype;
-       cnst->conste.v.float_value = token.v.floatvalue;
 
        next_token();
-
-       return cnst;
+       return literal;
 }
 
 static entity_t *create_implicit_function(symbol_t *symbol,
@@ -6830,89 +6233,16 @@ static entity_t *create_implicit_function(symbol_t *symbol,
        entity->base.symbol                        = symbol;
        entity->base.source_position               = *source_position;
 
-       bool strict_prototypes_old = warning.strict_prototypes;
-       warning.strict_prototypes  = false;
-       record_entity(entity, false);
-       warning.strict_prototypes = strict_prototypes_old;
+       if (current_scope != NULL) {
+               bool strict_prototypes_old = warning.strict_prototypes;
+               warning.strict_prototypes  = false;
+               record_entity(entity, false);
+               warning.strict_prototypes = strict_prototypes_old;
+       }
 
        return entity;
 }
 
-/**
- * Creates a return_type (func)(argument_type) function type if not
- * already exists.
- */
-static type_t *make_function_2_type(type_t *return_type, type_t *argument_type1,
-                                    type_t *argument_type2)
-{
-       function_parameter_t *const parameter2 = allocate_parameter(argument_type2);
-       function_parameter_t *const parameter1 = allocate_parameter(argument_type1);
-       parameter1->next = parameter2;
-
-       type_t *type               = allocate_type_zero(TYPE_FUNCTION);
-       type->function.return_type = return_type;
-       type->function.parameters  = parameter1;
-
-       return identify_new_type(type);
-}
-
-/**
- * Creates a return_type (func)(argument_type) function type if not
- * already exists.
- *
- * @param return_type    the return type
- * @param argument_type  the argument type
- */
-static type_t *make_function_1_type(type_t *return_type, type_t *argument_type)
-{
-       function_parameter_t *const parameter = allocate_parameter(argument_type);
-
-       type_t *type               = allocate_type_zero(TYPE_FUNCTION);
-       type->function.return_type = return_type;
-       type->function.parameters  = parameter;
-
-       return identify_new_type(type);
-}
-
-static type_t *make_function_1_type_variadic(type_t *return_type, type_t *argument_type)
-{
-       type_t *res = make_function_1_type(return_type, argument_type);
-       res->function.variadic = 1;
-       return res;
-}
-
-/**
- * Creates a return_type (func)(void) function type if not
- * already exists.
- *
- * @param return_type    the return type
- */
-static type_t *make_function_0_type(type_t *return_type)
-{
-       type_t *type               = allocate_type_zero(TYPE_FUNCTION);
-       type->function.return_type = return_type;
-       type->function.parameters  = NULL;
-
-       return identify_new_type(type);
-}
-
-/**
- * Creates a NO_RETURN return_type (func)(void) function type if not
- * already exists.
- *
- * @param return_type    the return type
- */
-static type_t *make_function_0_type_noreturn(type_t *return_type)
-{
-       type_t *type               = allocate_type_zero(TYPE_FUNCTION);
-       type->function.return_type = return_type;
-       type->function.parameters  = NULL;
-       type->function.base.modifiers |= DM_NORETURN;
-       return type;
-
-       return identify_new_type(type);
-}
-
 /**
  * Performs automatic type cast as described in Â§6.3.2.1.
  *
@@ -6943,92 +6273,158 @@ static type_t *automatic_type_conversion(type_t *orig_type)
 type_t *revert_automatic_type_conversion(const expression_t *expression)
 {
        switch (expression->kind) {
-               case EXPR_REFERENCE: {
-                       entity_t *entity = expression->reference.entity;
-                       if (is_declaration(entity)) {
-                               return entity->declaration.type;
-                       } else if (entity->kind == ENTITY_ENUM_VALUE) {
-                               return entity->enum_value.enum_type;
-                       } else {
-                               panic("no declaration or enum in reference");
-                       }
+       case EXPR_REFERENCE: {
+               entity_t *entity = expression->reference.entity;
+               if (is_declaration(entity)) {
+                       return entity->declaration.type;
+               } else if (entity->kind == ENTITY_ENUM_VALUE) {
+                       return entity->enum_value.enum_type;
+               } else {
+                       panic("no declaration or enum in reference");
                }
+       }
 
-               case EXPR_SELECT: {
-                       entity_t *entity = expression->select.compound_entry;
-                       assert(is_declaration(entity));
-                       type_t   *type   = entity->declaration.type;
-                       return get_qualified_type(type,
-                                       expression->base.type->base.qualifiers);
-               }
+       case EXPR_SELECT: {
+               entity_t *entity = expression->select.compound_entry;
+               assert(is_declaration(entity));
+               type_t   *type   = entity->declaration.type;
+               return get_qualified_type(type,
+                               expression->base.type->base.qualifiers);
+       }
 
-               case EXPR_UNARY_DEREFERENCE: {
-                       const expression_t *const value = expression->unary.value;
-                       type_t             *const type  = skip_typeref(value->base.type);
-                       if (!is_type_pointer(type))
-                               return type_error_type;
-                       return type->pointer.points_to;
-               }
+       case EXPR_UNARY_DEREFERENCE: {
+               const expression_t *const value = expression->unary.value;
+               type_t             *const type  = skip_typeref(value->base.type);
+               if (!is_type_pointer(type))
+                       return type_error_type;
+               return type->pointer.points_to;
+       }
 
-               case EXPR_ARRAY_ACCESS: {
-                       const expression_t *array_ref = expression->array_access.array_ref;
-                       type_t             *type_left = skip_typeref(array_ref->base.type);
-                       if (!is_type_pointer(type_left))
-                               return type_error_type;
-                       return type_left->pointer.points_to;
-               }
+       case EXPR_ARRAY_ACCESS: {
+               const expression_t *array_ref = expression->array_access.array_ref;
+               type_t             *type_left = skip_typeref(array_ref->base.type);
+               if (!is_type_pointer(type_left))
+                       return type_error_type;
+               return type_left->pointer.points_to;
+       }
 
-               case EXPR_STRING_LITERAL: {
-                       size_t size = expression->string.value.size;
-                       return make_array_type(type_char, size, TYPE_QUALIFIER_NONE);
-               }
+       case EXPR_STRING_LITERAL: {
+               size_t size = expression->string_literal.value.size;
+               return make_array_type(type_char, size, TYPE_QUALIFIER_NONE);
+       }
 
-               case EXPR_WIDE_STRING_LITERAL: {
-                       size_t size = expression->wide_string.value.size;
-                       return make_array_type(type_wchar_t, size, TYPE_QUALIFIER_NONE);
-               }
+       case EXPR_WIDE_STRING_LITERAL: {
+               size_t size = wstrlen(&expression->string_literal.value);
+               return make_array_type(type_wchar_t, size, TYPE_QUALIFIER_NONE);
+       }
 
-               case EXPR_COMPOUND_LITERAL:
-                       return expression->compound_literal.type;
+       case EXPR_COMPOUND_LITERAL:
+               return expression->compound_literal.type;
 
-               default:
-                       return expression->base.type;
+       default:
+               break;
        }
+       return expression->base.type;
 }
 
-static expression_t *parse_reference(void)
+/**
+ * Find an entity matching a symbol in a scope.
+ * Uses current scope if scope is NULL
+ */
+static entity_t *lookup_entity(const scope_t *scope, symbol_t *symbol,
+                               namespace_tag_t namespc)
+{
+       if (scope == NULL) {
+               return get_entity(symbol, namespc);
+       }
+
+       /* we should optimize here, if scope grows above a certain size we should
+          construct a hashmap here... */
+       entity_t *entity = scope->entities;
+       for ( ; entity != NULL; entity = entity->base.next) {
+               if (entity->base.symbol == symbol && entity->base.namespc == namespc)
+                       break;
+       }
+
+       return entity;
+}
+
+static entity_t *parse_qualified_identifier(void)
 {
-       symbol_t *const symbol = token.v.symbol;
+       /* namespace containing the symbol */
+       symbol_t          *symbol;
+       source_position_t  pos;
+       const scope_t     *lookup_scope = NULL;
 
-       entity_t *entity = get_entity(symbol, NAMESPACE_NORMAL);
+       if (next_if(T_COLONCOLON))
+               lookup_scope = &unit->scope;
+
+       entity_t *entity;
+       while (true) {
+               if (token.type != T_IDENTIFIER) {
+                       parse_error_expected("while parsing identifier", T_IDENTIFIER, NULL);
+                       return create_error_entity(sym_anonymous, ENTITY_VARIABLE);
+               }
+               symbol = token.symbol;
+               pos    = *HERE;
+               next_token();
+
+               /* lookup entity */
+               entity = lookup_entity(lookup_scope, symbol, NAMESPACE_NORMAL);
+
+               if (!next_if(T_COLONCOLON))
+                       break;
+
+               switch (entity->kind) {
+               case ENTITY_NAMESPACE:
+                       lookup_scope = &entity->namespacee.members;
+                       break;
+               case ENTITY_STRUCT:
+               case ENTITY_UNION:
+               case ENTITY_CLASS:
+                       lookup_scope = &entity->compound.members;
+                       break;
+               default:
+                       errorf(&pos, "'%Y' must be a namespace, class, struct or union (but is a %s)",
+                              symbol, get_entity_kind_name(entity->kind));
+                       goto end_error;
+               }
+       }
 
        if (entity == NULL) {
-               if (!strict_mode && look_ahead(1)->type == '(') {
+               if (!strict_mode && token.type == '(') {
                        /* an implicitly declared function */
                        if (warning.error_implicit_function_declaration) {
-                               errorf(HERE, "implicit declaration of function '%Y'", symbol);
+                               errorf(&pos, "implicit declaration of function '%Y'", symbol);
                        } else if (warning.implicit_function_declaration) {
-                               warningf(HERE, "implicit declaration of function '%Y'", symbol);
+                               warningf(&pos, "implicit declaration of function '%Y'", symbol);
                        }
 
-                       entity = create_implicit_function(symbol, HERE);
+                       entity = create_implicit_function(symbol, &pos);
                } else {
-                       errorf(HERE, "unknown identifier '%Y' found.", symbol);
+                       errorf(&pos, "unknown identifier '%Y' found.", symbol);
                        entity = create_error_entity(symbol, ENTITY_VARIABLE);
                }
        }
 
-       type_t *orig_type;
+       return entity;
 
+end_error:
+       /* skip further qualifications */
+       while (next_if(T_IDENTIFIER) && next_if(T_COLONCOLON)) {}
+
+       return create_error_entity(sym_anonymous, ENTITY_VARIABLE);
+}
+
+static expression_t *parse_reference(void)
+{
+       entity_t *entity = parse_qualified_identifier();
+
+       type_t *orig_type;
        if (is_declaration(entity)) {
                orig_type = entity->declaration.type;
        } else if (entity->kind == ENTITY_ENUM_VALUE) {
                orig_type = entity->enum_value.enum_type;
-       } else if (entity->kind == ENTITY_TYPEDEF) {
-               errorf(HERE, "encountered typedef name '%Y' while parsing expression",
-                       symbol);
-               next_token();
-               return create_invalid_expression();
        } else {
                panic("expected declaration or enum value in reference");
        }
@@ -7051,8 +6447,9 @@ static expression_t *parse_reference(void)
        }
 
        if (entity->base.parent_scope != file_scope
-               && entity->base.parent_scope->depth < current_function->parameters.depth
-               && is_type_valid(orig_type) && !is_type_function(orig_type)) {
+               && (current_function != NULL
+                       && entity->base.parent_scope->depth < current_function->parameters.depth)
+               && (entity->kind == ENTITY_VARIABLE || entity->kind == ENTITY_PARAMETER)) {
                if (entity->kind == ENTITY_VARIABLE) {
                        /* access of a variable from an outer function */
                        entity->variable.address_taken = true;
@@ -7062,24 +6459,7 @@ static expression_t *parse_reference(void)
                current_function->need_closure = true;
        }
 
-       /* check for deprecated functions */
-       if (warning.deprecated_declarations
-               && is_declaration(entity)
-               && entity->declaration.modifiers & DM_DEPRECATED) {
-               declaration_t *declaration = &entity->declaration;
-
-               char const *const prefix = entity->kind == ENTITY_FUNCTION ?
-                       "function" : "variable";
-
-               if (declaration->deprecated_string != NULL) {
-                       warningf(HERE, "%s '%Y' is deprecated (declared %P): \"%s\"",
-                                prefix, entity->base.symbol, &entity->base.source_position,
-                                declaration->deprecated_string);
-               } else {
-                       warningf(HERE, "%s '%Y' is deprecated (declared %P)", prefix,
-                                entity->base.symbol, &entity->base.source_position);
-               }
-       }
+       check_deprecated(HERE, entity);
 
        if (warning.init_self && entity == current_init_decl && !in_type_prop
            && entity->kind == ENTITY_VARIABLE) {
@@ -7088,7 +6468,6 @@ static expression_t *parse_reference(void)
                         entity->declaration.type, entity->base.symbol);
        }
 
-       next_token();
        return expression;
 }
 
@@ -7179,7 +6558,7 @@ static expression_t *parse_cast(void)
        expression_t *cast = allocate_expression_zero(EXPR_UNARY_CAST);
        cast->base.source_position = source_position;
 
-       expression_t *value = parse_sub_expression(PREC_CAST);
+       expression_t *value = parse_subexpression(PREC_CAST);
        cast->base.type   = type;
        cast->unary.value = value;
 
@@ -7243,7 +6622,7 @@ static expression_t *parse_parenthesized_expression(void)
        TYPE_SPECIFIERS
                return parse_cast();
        case T_IDENTIFIER:
-               if (is_typedef_symbol(token.v.symbol)) {
+               if (is_typedef_symbol(token.symbol)) {
                        return parse_cast();
                }
        }
@@ -7330,13 +6709,12 @@ static designator_t *parse_designator(void)
                                     T_IDENTIFIER, NULL);
                return NULL;
        }
-       result->symbol = token.v.symbol;
+       result->symbol = token.symbol;
        next_token();
 
        designator_t *last_designator = result;
        while (true) {
-               if (token.type == '.') {
-                       next_token();
+               if (next_if('.')) {
                        if (token.type != T_IDENTIFIER) {
                                parse_error_expected("while parsing member designator",
                                                     T_IDENTIFIER, NULL);
@@ -7344,15 +6722,14 @@ static designator_t *parse_designator(void)
                        }
                        designator_t *designator    = allocate_ast_zero(sizeof(result[0]));
                        designator->source_position = *HERE;
-                       designator->symbol          = token.v.symbol;
+                       designator->symbol          = token.symbol;
                        next_token();
 
                        last_designator->next = designator;
                        last_designator       = designator;
                        continue;
                }
-               if (token.type == '[') {
-                       next_token();
+               if (next_if('[')) {
                        add_anchor_token(']');
                        designator_t *designator    = allocate_ast_zero(sizeof(result[0]));
                        designator->source_position = *HERE;
@@ -7433,9 +6810,12 @@ static expression_t *parse_va_start(void)
        expression_t *const expr = parse_assignment_expression();
        if (expr->kind == EXPR_REFERENCE) {
                entity_t *const entity = expr->reference.entity;
-               if (entity->base.parent_scope != &current_function->parameters
-                               || entity->base.next != NULL
-                               || entity->kind != ENTITY_PARAMETER) {
+               if (!current_function->base.type->function.variadic) {
+                       errorf(&expr->base.source_position,
+                                       "'va_start' used in non-variadic function");
+               } else if (entity->base.parent_scope != &current_function->parameters ||
+                               entity->base.next != NULL ||
+                               entity->kind != ENTITY_PARAMETER) {
                        errorf(&expr->base.source_position,
                               "second argument of 'va_start' must be last parameter of the current function");
                } else {
@@ -7450,7 +6830,7 @@ end_error:
 }
 
 /**
- * Parses a _builtin_va_arg() expression.
+ * Parses a __builtin_va_arg() expression.
  */
 static expression_t *parse_va_arg(void)
 {
@@ -7459,7 +6839,11 @@ static expression_t *parse_va_arg(void)
        eat(T___builtin_va_arg);
 
        expect('(', end_error);
-       expression->va_arge.ap = parse_assignment_expression();
+       call_argument_t ap;
+       ap.expression = parse_assignment_expression();
+       expression->va_arge.ap = ap.expression;
+       check_call_argument(type_valist, &ap, 1);
+
        expect(',', end_error);
        expression->base.type = parse_typename();
        expect(')', end_error);
@@ -7469,6 +6853,35 @@ end_error:
        return create_invalid_expression();
 }
 
+/**
+ * Parses a __builtin_va_copy() expression.
+ */
+static expression_t *parse_va_copy(void)
+{
+       expression_t *expression = allocate_expression_zero(EXPR_VA_COPY);
+
+       eat(T___builtin_va_copy);
+
+       expect('(', end_error);
+       expression_t *dst = parse_assignment_expression();
+       assign_error_t error = semantic_assign(type_valist, dst);
+       report_assign_error(error, type_valist, dst, "call argument 1",
+                           &dst->base.source_position);
+       expression->va_copye.dst = dst;
+
+       expect(',', end_error);
+
+       call_argument_t src;
+       src.expression = parse_assignment_expression();
+       check_call_argument(type_valist, &src, 2);
+       expression->va_copye.src = src.expression;
+       expect(')', end_error);
+
+       return expression;
+end_error:
+       return create_invalid_expression();
+}
+
 /**
  * Parses a __builtin_constant_p() expression.
  */
@@ -7553,49 +6966,24 @@ static expression_t *parse_compare_builtin(void)
        expression->binary.right = parse_assignment_expression();
        expect(')', end_error);
 
-       type_t *const orig_type_left  = expression->binary.left->base.type;
-       type_t *const orig_type_right = expression->binary.right->base.type;
-
-       type_t *const type_left  = skip_typeref(orig_type_left);
-       type_t *const type_right = skip_typeref(orig_type_right);
-       if (!is_type_float(type_left) && !is_type_float(type_right)) {
-               if (is_type_valid(type_left) && is_type_valid(type_right)) {
-                       type_error_incompatible("invalid operands in comparison",
-                               &expression->base.source_position, orig_type_left, orig_type_right);
-               }
-       } else {
-               semantic_comparison(&expression->binary);
-       }
-
-       return expression;
-end_error:
-       return create_invalid_expression();
-}
-
-#if 0
-/**
- * Parses a __builtin_expect(, end_error) expression.
- */
-static expression_t *parse_builtin_expect(void, end_error)
-{
-       expression_t *expression
-               = allocate_expression_zero(EXPR_BINARY_BUILTIN_EXPECT);
-
-       eat(T___builtin_expect);
-
-       expect('(', end_error);
-       expression->binary.left = parse_assignment_expression();
-       expect(',', end_error);
-       expression->binary.right = parse_constant_expression();
-       expect(')', end_error);
-
-       expression->base.type = expression->binary.left->base.type;
+       type_t *const orig_type_left  = expression->binary.left->base.type;
+       type_t *const orig_type_right = expression->binary.right->base.type;
+
+       type_t *const type_left  = skip_typeref(orig_type_left);
+       type_t *const type_right = skip_typeref(orig_type_right);
+       if (!is_type_float(type_left) && !is_type_float(type_right)) {
+               if (is_type_valid(type_left) && is_type_valid(type_right)) {
+                       type_error_incompatible("invalid operands in comparison",
+                               &expression->base.source_position, orig_type_left, orig_type_right);
+               }
+       } else {
+               semantic_comparison(&expression->binary);
+       }
 
        return expression;
 end_error:
        return create_invalid_expression();
 }
-#endif
 
 /**
  * Parses a MS assume() expression.
@@ -7667,7 +7055,7 @@ static expression_t *parse_label_address(void)
                parse_error_expected("while parsing label address", T_IDENTIFIER, NULL);
                goto end_error;
        }
-       symbol_t *symbol = token.v.symbol;
+       symbol_t *symbol = token.symbol;
        next_token();
 
        label_t *label       = get_label(symbol);
@@ -7691,10 +7079,11 @@ end_error:
 static expression_t *parse_noop_expression(void)
 {
        /* the result is a (int)0 */
-       expression_t *cnst         = allocate_expression_zero(EXPR_CONST);
-       cnst->base.type            = type_int;
-       cnst->conste.v.int_value   = 0;
-       cnst->conste.is_ms_noop    = true;
+       expression_t *literal = allocate_expression_zero(EXPR_LITERAL_MS_NOOP);
+       literal->base.type            = type_int;
+       literal->base.source_position = token.source_position;
+       literal->literal.value.begin  = "__noop";
+       literal->literal.value.size   = 6;
 
        eat(T___noop);
 
@@ -7704,21 +7093,16 @@ static expression_t *parse_noop_expression(void)
                add_anchor_token(')');
                add_anchor_token(',');
 
-               if (token.type != ')') {
-                       while (true) {
-                               (void)parse_assignment_expression();
-                               if (token.type != ',')
-                                       break;
-                               next_token();
-                       }
-               }
+               if (token.type != ')') do {
+                       (void)parse_assignment_expression();
+               } while (next_if(','));
        }
        rem_anchor_token(',');
        rem_anchor_token(')');
        expect(')', end_error);
 
 end_error:
-       return cnst;
+       return literal;
 }
 
 /**
@@ -7727,42 +7111,61 @@ end_error:
 static expression_t *parse_primary_expression(void)
 {
        switch (token.type) {
-               case T_false:                        return parse_bool_const(false);
-               case T_true:                         return parse_bool_const(true);
-               case T_INTEGER:                      return parse_int_const();
-               case T_CHARACTER_CONSTANT:           return parse_character_constant();
-               case T_WIDE_CHARACTER_CONSTANT:      return parse_wide_character_constant();
-               case T_FLOATINGPOINT:                return parse_float_const();
-               case T_STRING_LITERAL:
-               case T_WIDE_STRING_LITERAL:          return parse_string_const();
-               case T_IDENTIFIER:                   return parse_reference();
-               case T___FUNCTION__:
-               case T___func__:                     return parse_function_keyword();
-               case T___PRETTY_FUNCTION__:          return parse_pretty_function_keyword();
-               case T___FUNCSIG__:                  return parse_funcsig_keyword();
-               case T___FUNCDNAME__:                return parse_funcdname_keyword();
-               case T___builtin_offsetof:           return parse_offsetof();
-               case T___builtin_va_start:           return parse_va_start();
-               case T___builtin_va_arg:             return parse_va_arg();
-               case T___builtin_isgreater:
-               case T___builtin_isgreaterequal:
-               case T___builtin_isless:
-               case T___builtin_islessequal:
-               case T___builtin_islessgreater:
-               case T___builtin_isunordered:        return parse_compare_builtin();
-               case T___builtin_constant_p:         return parse_builtin_constant();
-               case T___builtin_types_compatible_p: return parse_builtin_types_compatible();
-               case T__assume:                      return parse_assume();
-               case T_ANDAND:
-                       if (GNU_MODE)
-                               return parse_label_address();
-                       break;
+       case T_false:                        return parse_boolean_literal(false);
+       case T_true:                         return parse_boolean_literal(true);
+       case T_INTEGER:
+       case T_INTEGER_OCTAL:
+       case T_INTEGER_HEXADECIMAL:
+       case T_FLOATINGPOINT:
+       case T_FLOATINGPOINT_HEXADECIMAL:    return parse_number_literal();
+       case T_CHARACTER_CONSTANT:           return parse_character_constant();
+       case T_WIDE_CHARACTER_CONSTANT:      return parse_wide_character_constant();
+       case T_STRING_LITERAL:
+       case T_WIDE_STRING_LITERAL:          return parse_string_literal();
+       case T___FUNCTION__:
+       case T___func__:                     return parse_function_keyword();
+       case T___PRETTY_FUNCTION__:          return parse_pretty_function_keyword();
+       case T___FUNCSIG__:                  return parse_funcsig_keyword();
+       case T___FUNCDNAME__:                return parse_funcdname_keyword();
+       case T___builtin_offsetof:           return parse_offsetof();
+       case T___builtin_va_start:           return parse_va_start();
+       case T___builtin_va_arg:             return parse_va_arg();
+       case T___builtin_va_copy:            return parse_va_copy();
+       case T___builtin_isgreater:
+       case T___builtin_isgreaterequal:
+       case T___builtin_isless:
+       case T___builtin_islessequal:
+       case T___builtin_islessgreater:
+       case T___builtin_isunordered:        return parse_compare_builtin();
+       case T___builtin_constant_p:         return parse_builtin_constant();
+       case T___builtin_types_compatible_p: return parse_builtin_types_compatible();
+       case T__assume:                      return parse_assume();
+       case T_ANDAND:
+               if (GNU_MODE)
+                       return parse_label_address();
+               break;
 
-               case '(':                            return parse_parenthesized_expression();
-               case T___noop:                       return parse_noop_expression();
+       case '(':                            return parse_parenthesized_expression();
+       case T___noop:                       return parse_noop_expression();
+
+       /* Gracefully handle type names while parsing expressions. */
+       case T_COLONCOLON:
+               return parse_reference();
+       case T_IDENTIFIER:
+               if (!is_typedef_symbol(token.symbol)) {
+                       return parse_reference();
+               }
+               /* FALLTHROUGH */
+       TYPENAME_START {
+               source_position_t  const pos  = *HERE;
+               type_t const      *const type = parse_typename();
+               errorf(&pos, "encountered type '%T' while parsing expression", type);
+               return create_invalid_expression();
+       }
        }
 
        errorf(HERE, "unexpected token %K, expected an expression", &token);
+       eat_until_anchor();
        return create_invalid_expression();
 }
 
@@ -7855,7 +7258,7 @@ static expression_t *parse_typeprop(expression_kind_t const kind)
                        goto typeprop_expression;
                }
        } else {
-               expression = parse_sub_expression(PREC_UNARY);
+               expression = parse_subexpression(PREC_UNARY);
 
 typeprop_expression:
                tp_expression->typeprop.tp_expression = expression;
@@ -7894,29 +7297,26 @@ static expression_t *parse_alignof(void)
        return parse_typeprop(EXPR_ALIGNOF);
 }
 
-static expression_t *parse_select_expression(expression_t *compound)
+static expression_t *parse_select_expression(expression_t *addr)
 {
-       expression_t *select    = allocate_expression_zero(EXPR_SELECT);
-       select->select.compound = compound;
-
        assert(token.type == '.' || token.type == T_MINUSGREATER);
-       bool is_pointer = (token.type == T_MINUSGREATER);
+       bool select_left_arrow = (token.type == T_MINUSGREATER);
        next_token();
 
        if (token.type != T_IDENTIFIER) {
                parse_error_expected("while parsing select", T_IDENTIFIER, NULL);
-               return select;
+               return create_invalid_expression();
        }
-       symbol_t *symbol = token.v.symbol;
+       symbol_t *symbol = token.symbol;
        next_token();
 
-       type_t *const orig_type = compound->base.type;
+       type_t *const orig_type = addr->base.type;
        type_t *const type      = skip_typeref(orig_type);
 
        type_t *type_left;
        bool    saw_error = false;
        if (is_type_pointer(type)) {
-               if (!is_pointer) {
+               if (!select_left_arrow) {
                        errorf(HERE,
                               "request for member '%Y' in something not a struct or union, but '%T'",
                               symbol, orig_type);
@@ -7924,62 +7324,46 @@ static expression_t *parse_select_expression(expression_t *compound)
                }
                type_left = skip_typeref(type->pointer.points_to);
        } else {
-               if (is_pointer && is_type_valid(type)) {
+               if (select_left_arrow && is_type_valid(type)) {
                        errorf(HERE, "left hand side of '->' is not a pointer, but '%T'", orig_type);
                        saw_error = true;
                }
                type_left = type;
        }
 
-       entity_t *entry;
-       if (type_left->kind == TYPE_COMPOUND_STRUCT ||
-           type_left->kind == TYPE_COMPOUND_UNION) {
-               compound_t *compound = type_left->compound.compound;
+       if (type_left->kind != TYPE_COMPOUND_STRUCT &&
+           type_left->kind != TYPE_COMPOUND_UNION) {
 
-               if (!compound->complete) {
-                       errorf(HERE, "request for member '%Y' of incomplete type '%T'",
-                              symbol, type_left);
-                       goto create_error_entry;
-               }
-
-               entry = find_compound_entry(compound, 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 = create_error_entity(symbol, ENTITY_COMPOUND_MEMBER);
+               return create_invalid_expression();
        }
 
-       assert(is_declaration(entry));
-       select->select.compound_entry = entry;
-
-       type_t *entry_type = entry->declaration.type;
-       type_t *res_type
-               = get_qualified_type(entry_type, type_left->base.qualifiers);
+       compound_t *compound = type_left->compound.compound;
+       if (!compound->complete) {
+               errorf(HERE, "request for member '%Y' in incomplete type '%T'",
+                      symbol, type_left);
+               return create_invalid_expression();
+       }
 
-       /* we always do the auto-type conversions; the & and sizeof parser contains
-        * code to revert this! */
-       select->base.type = automatic_type_conversion(res_type);
+       type_qualifiers_t  qualifiers = type_left->base.qualifiers;
+       expression_t      *result
+               = find_create_select(HERE, addr, qualifiers, compound, symbol);
 
-       type_t *skipped = skip_typeref(res_type);
-       if (skipped->kind == TYPE_BITFIELD) {
-               select->base.type = skipped->bitfield.base_type;
+       if (result == NULL) {
+               errorf(HERE, "'%T' has no member named '%Y'", orig_type, symbol);
+               return create_invalid_expression();
        }
 
-       return select;
+       return result;
 }
 
-static void check_call_argument(const function_parameter_t *parameter,
+static void check_call_argument(type_t          *expected_type,
                                 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;
@@ -7987,8 +7371,7 @@ static void check_call_argument(const function_parameter_t *parameter,
 
        /* handle transparent union gnu extension */
        if (is_type_union(expected_type_skip)
-                       && (expected_type_skip->base.modifiers
-                               & TYPE_MODIFIER_TRANSPARENT_UNION)) {
+                       && (get_type_modifiers(expected_type) & DM_TRANSPARENT_UNION)) {
                compound_t *union_decl  = expected_type_skip->compound.compound;
                type_t     *best_type   = NULL;
                entity_t   *entry       = union_decl->members.entities;
@@ -8013,8 +7396,7 @@ static void check_call_argument(const function_parameter_t *parameter,
        }
 
        error                = semantic_assign(expected_type, arg_expr);
-       argument->expression = create_implicit_cast(argument->expression,
-                                                   expected_type);
+       argument->expression = create_implicit_cast(arg_expr, expected_type);
 
        if (error != ASSIGN_SUCCESS) {
                /* report exact scope in error messages (like "in argument 3") */
@@ -8045,20 +7427,33 @@ static void handle_builtin_argument_restrictions(call_expression_t *call) {
                        /* argument must be constant */
                        call_argument_t *argument = call->arguments;
 
-                       if (! is_constant_expression(argument->expression)) {
+                       if (is_constant_expression(argument->expression) == EXPR_CLASS_VARIABLE) {
                                errorf(&call->base.source_position,
                                       "argument of '%Y' must be a constant expression",
                                       call->function->reference.entity->base.symbol);
                        }
                        break;
                }
-               case bk_gnu_builtin_prefetch: {
+               case bk_gnu_builtin_object_size:
+                       if (call->arguments == NULL)
+                               break;
+
+                       call_argument_t *arg = call->arguments->next;
+                       if (arg != NULL && is_constant_expression(arg->expression) == EXPR_CLASS_VARIABLE) {
+                               errorf(&call->base.source_position,
+                                          "second argument of '%Y' must be a constant expression",
+                                          call->function->reference.entity->base.symbol);
+                       }
+                       break;
+               case bk_gnu_builtin_prefetch:
                        /* second and third argument must be constant if existent */
+                       if (call->arguments == NULL)
+                               break;
                        call_argument_t *rw = call->arguments->next;
                        call_argument_t *locality = NULL;
 
                        if (rw != NULL) {
-                               if (! is_constant_expression(rw->expression)) {
+                               if (is_constant_expression(rw->expression) == EXPR_CLASS_VARIABLE) {
                                        errorf(&call->base.source_position,
                                               "second argument of '%Y' must be a constant expression",
                                               call->function->reference.entity->base.symbol);
@@ -8066,7 +7461,7 @@ static void handle_builtin_argument_restrictions(call_expression_t *call) {
                                locality = rw->next;
                        }
                        if (locality != NULL) {
-                               if (! is_constant_expression(locality->expression)) {
+                               if (is_constant_expression(locality->expression) == EXPR_CLASS_VARIABLE) {
                                        errorf(&call->base.source_position,
                                               "third argument of '%Y' must be a constant expression",
                                               call->function->reference.entity->base.symbol);
@@ -8074,7 +7469,6 @@ static void handle_builtin_argument_restrictions(call_expression_t *call) {
                                locality = rw->next;
                        }
                        break;
-               }
                default:
                        break;
        }
@@ -8105,7 +7499,9 @@ static expression_t *parse_call_expression(expression_t *expression)
        }
 
        if (function_type == NULL && is_type_valid(type)) {
-               errorf(HERE, "called object '%E' (type '%T') is not a pointer to a function", expression, orig_type);
+               errorf(HERE,
+                      "called object '%E' (type '%T') is not a pointer to a function",
+                      expression, orig_type);
        }
 
        /* parse arguments */
@@ -8115,17 +7511,13 @@ static expression_t *parse_call_expression(expression_t *expression)
 
        if (token.type != ')') {
                call_argument_t **anchor = &call->arguments;
-               for (;;) {
+               do {
                        call_argument_t *argument = allocate_ast_zero(sizeof(*argument));
                        argument->expression = parse_assignment_expression();
 
                        *anchor = argument;
                        anchor  = &argument->next;
-
-                       if (token.type != ',')
-                               break;
-                       next_token();
-               }
+               } while (next_if(','));
        }
        rem_anchor_token(',');
        rem_anchor_token(')');
@@ -8134,12 +7526,13 @@ static expression_t *parse_call_expression(expression_t *expression)
        if (function_type == NULL)
                return result;
 
+       /* check type and count of call arguments */
        function_parameter_t *parameter = function_type->parameters;
        call_argument_t      *argument  = call->arguments;
        if (!function_type->unspecified_parameters) {
                for (unsigned pos = 0; parameter != NULL && argument != NULL;
                                parameter = parameter->next, argument = argument->next) {
-                       check_call_argument(parameter, argument, ++pos);
+                       check_call_argument(parameter->type, argument, ++pos);
                }
 
                if (parameter != NULL) {
@@ -8149,9 +7542,13 @@ static expression_t *parse_call_expression(expression_t *expression)
                }
        }
 
-       /* do default promotion */
+       /* do default promotion for other arguments */
        for (; argument != NULL; argument = argument->next) {
                type_t *type = argument->expression->base.type;
+               if (!is_type_object(skip_typeref(type))) {
+                       errorf(&argument->expression->base.source_position,
+                              "call argument '%E' must not be void", argument->expression);
+               }
 
                type = get_default_promoted_type(type);
 
@@ -8284,7 +7681,7 @@ static expression_t *parse_conditional_expression(expression_t *expression)
        expect(':', end_error);
 end_error:;
        expression_t *false_expression =
-               parse_sub_expression(c_mode & _CXX ? PREC_ASSIGNMENT : PREC_CONDITIONAL);
+               parse_subexpression(c_mode & _CXX ? PREC_ASSIGNMENT : PREC_CONDITIONAL);
 
        type_t *const orig_true_type  = true_expression->base.type;
        type_t *const orig_false_type = false_expression->base.type;
@@ -8313,13 +7710,6 @@ end_error:;
        } else if (is_type_arithmetic(true_type)
                   && is_type_arithmetic(false_type)) {
                result_type = semantic_arithmetic(true_type, false_type);
-
-               true_expression  = create_implicit_cast(true_expression, result_type);
-               false_expression = create_implicit_cast(false_expression, result_type);
-
-               conditional->true_expression  = true_expression;
-               conditional->false_expression = false_expression;
-               conditional->base.type        = result_type;
        } else if (same_compound_type(true_type, false_type)) {
                /* just take 1 of the 2 types */
                result_type = true_type;
@@ -8371,7 +7761,7 @@ end_error:;
                        result_type = pointer_type;
                } else {
                        if (is_type_valid(other_type)) {
-                               type_error_incompatible("while parsing conditional",
+                               type_error_incompatible("while parsing conditional",
                                                &expression->base.source_position, true_type, false_type);
                        }
                        result_type = type_error_type;
@@ -8402,7 +7792,7 @@ static expression_t *parse_extension(void)
 
        bool old_gcc_extension   = in_gcc_extension;
        in_gcc_extension         = true;
-       expression_t *expression = parse_sub_expression(PREC_UNARY);
+       expression_t *expression = parse_subexpression(PREC_UNARY);
        in_gcc_extension         = old_gcc_extension;
        return expression;
 }
@@ -8440,14 +7830,13 @@ static expression_t *parse_delete(void)
 
        eat(T_delete);
 
-       if (token.type == '[') {
-               next_token();
+       if (next_if('[')) {
                result->kind = EXPR_UNARY_DELETE_ARRAY;
                expect(']', end_error);
 end_error:;
        }
 
-       expression_t *const value = parse_sub_expression(PREC_CAST);
+       expression_t *const value = parse_subexpression(PREC_CAST);
        result->unary.value = value;
 
        type_t *const type = skip_typeref(value->base.type);
@@ -8549,14 +7938,14 @@ static bool is_lvalue(const expression_t *expression)
                return true;
 
        default: {
-         type_t *type = skip_typeref(expression->base.type);
-         return
-               /* ISO/IEC 14882:1998(E) Â§3.10:3 */
-               is_type_reference(type) ||
-               /* Claim it is an lvalue, if the type is invalid.  There was a parse
-                * error before, which maybe prevented properly recognizing it as
-                * lvalue. */
-               !is_type_valid(type);
+               type_t *type = skip_typeref(expression->base.type);
+               return
+                       /* ISO/IEC 14882:1998(E) Â§3.10:3 */
+                       is_type_reference(type) ||
+                       /* Claim it is an lvalue, if the type is invalid.  There was a parse
+                        * error before, which maybe prevented properly recognizing it as
+                        * lvalue. */
+                       !is_type_valid(type);
        }
        }
 }
@@ -8711,7 +8100,7 @@ static expression_t *parse_##unexpression_type(void)                         \
        expression_t *unary_expression                                           \
                = allocate_expression_zero(unexpression_type);                       \
        eat(token_type);                                                         \
-       unary_expression->unary.value = parse_sub_expression(PREC_UNARY);        \
+       unary_expression->unary.value = parse_subexpression(PREC_UNARY);         \
                                                                                 \
        sfunc(&unary_expression->unary);                                         \
                                                                                 \
@@ -8855,9 +8244,9 @@ static void warn_div_by_zero(binary_expression_t const *const expression)
 
        expression_t const *const right = expression->right;
        /* The type of the right operand can be different for /= */
-       if (is_type_integer(right->base.type) &&
-           is_constant_expression(right)     &&
-           fold_constant(right) == 0) {
+       if (is_type_integer(right->base.type)                    &&
+           is_constant_expression(right) == EXPR_CLASS_CONSTANT &&
+           !fold_constant_to_bool(right)) {
                warningf(&expression->base.source_position, "division by zero");
        }
 }
@@ -8887,7 +8276,7 @@ static void warn_addsub_in_shift(const expression_t *const expr)
                        "suggest parentheses around '%c' inside shift", op);
 }
 
-static void semantic_shift_op(binary_expression_t *expression)
+static bool semantic_shift(binary_expression_t *expression)
 {
        expression_t *const left            = expression->left;
        expression_t *const right           = expression->right;
@@ -8902,19 +8291,47 @@ static void semantic_shift_op(binary_expression_t *expression)
                        errorf(&expression->base.source_position,
                               "operands of shift operation must have integer types");
                }
-               return;
+               return false;
+       }
+
+       type_left = promote_integer(type_left);
+
+       if (is_constant_expression(right) == EXPR_CLASS_CONSTANT) {
+               long count = fold_constant_to_int(right);
+               if (count < 0) {
+                       warningf(&right->base.source_position,
+                                       "shift count must be non-negative");
+               } else if ((unsigned long)count >=
+                               get_atomic_type_size(type_left->atomic.akind) * 8) {
+                       warningf(&right->base.source_position,
+                                       "shift count must be less than type width");
+               }
        }
 
+       type_right        = promote_integer(type_right);
+       expression->right = create_implicit_cast(right, type_right);
+
+       return true;
+}
+
+static void semantic_shift_op(binary_expression_t *expression)
+{
+       expression_t *const left  = expression->left;
+       expression_t *const right = expression->right;
+
+       if (!semantic_shift(expression))
+               return;
+
        if (warning.parentheses) {
                warn_addsub_in_shift(left);
                warn_addsub_in_shift(right);
        }
 
-       type_left  = promote_integer(type_left);
-       type_right = promote_integer(type_right);
+       type_t *const orig_type_left = left->base.type;
+       type_t *      type_left      = skip_typeref(orig_type_left);
 
+       type_left             = promote_integer(type_left);
        expression->left      = create_implicit_cast(left, type_left);
-       expression->right     = create_implicit_cast(right, type_right);
        expression->base.type = type_left;
 }
 
@@ -8999,8 +8416,8 @@ static void warn_string_literal_address(expression_t const* expr)
                expr = expr->unary.value;
        }
 
-       if (expr->kind == EXPR_STRING_LITERAL ||
-           expr->kind == EXPR_WIDE_STRING_LITERAL) {
+       if (expr->kind == EXPR_STRING_LITERAL
+                       || expr->kind == EXPR_WIDE_STRING_LITERAL) {
                warningf(&expr->base.source_position,
                        "comparison with string literal results in unspecified behaviour");
        }
@@ -9027,9 +8444,11 @@ static void warn_comparison_in_comparison(const expression_t *const expr)
 
 static bool maybe_negative(expression_t const *const expr)
 {
-       return
-               !is_constant_expression(expr) ||
-               fold_constant(expr) < 0;
+       switch (is_constant_expression(expr)) {
+               case EXPR_CLASS_ERROR:    return false;
+               case EXPR_CLASS_CONSTANT: return fold_constant_to_int(expr) < 0;
+               default:                  return true;
+       }
 }
 
 /**
@@ -9242,6 +8661,50 @@ static void semantic_arithmetic_addsubb_assign(binary_expression_t *expression)
        }
 }
 
+static void semantic_integer_assign(binary_expression_t *expression)
+{
+       expression_t *left            = expression->left;
+       expression_t *right           = expression->right;
+       type_t       *orig_type_left  = left->base.type;
+       type_t       *orig_type_right = right->base.type;
+
+       if (!is_valid_assignment_lhs(left))
+               return;
+
+       type_t *type_left  = skip_typeref(orig_type_left);
+       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,
+                              "operation needs integer types");
+               }
+               return;
+       }
+
+       /* combined instructions are tricky. We can't create an implicit cast on
+        * the left side, because we need the uncasted form for the store.
+        * The ast2firm pass has to know that left_type must be right_type
+        * for the arithmetic operation and create a cast by itself */
+       type_t *arithmetic_type = semantic_arithmetic(type_left, type_right);
+       expression->right       = create_implicit_cast(right, arithmetic_type);
+       expression->base.type   = type_left;
+}
+
+static void semantic_shift_assign(binary_expression_t *expression)
+{
+       expression_t *left           = expression->left;
+
+       if (!is_valid_assignment_lhs(left))
+               return;
+
+       if (!semantic_shift(expression))
+               return;
+
+       expression->base.type = skip_typeref(left->base.type);
+}
+
 static void warn_logical_and_within_or(const expression_t *const expr)
 {
        if (expr->base.kind != EXPR_BINARY_LOGICAL_AND)
@@ -9306,13 +8769,20 @@ static bool expression_has_effect(const expression_t *const expr)
                case EXPR_INVALID:                    return true; /* do NOT warn */
                case EXPR_REFERENCE:                  return false;
                case EXPR_REFERENCE_ENUM_VALUE:       return false;
+               case EXPR_LABEL_ADDRESS:              return false;
+
                /* suppress the warning for microsoft __noop operations */
-               case EXPR_CONST:                      return expr->conste.is_ms_noop;
-               case EXPR_CHARACTER_CONSTANT:         return false;
-               case EXPR_WIDE_CHARACTER_CONSTANT:    return false;
+               case EXPR_LITERAL_MS_NOOP:            return true;
+               case EXPR_LITERAL_BOOLEAN:
+               case EXPR_LITERAL_CHARACTER:
+               case EXPR_LITERAL_WIDE_CHARACTER:
+               case EXPR_LITERAL_INTEGER:
+               case EXPR_LITERAL_INTEGER_OCTAL:
+               case EXPR_LITERAL_INTEGER_HEXADECIMAL:
+               case EXPR_LITERAL_FLOATINGPOINT:
+               case EXPR_LITERAL_FLOATINGPOINT_HEXADECIMAL: 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;
@@ -9347,6 +8817,7 @@ static bool expression_has_effect(const expression_t *const expr)
                case EXPR_OFFSETOF:                   return false;
                case EXPR_VA_START:                   return true;
                case EXPR_VA_ARG:                     return true;
+               case EXPR_VA_COPY:                    return true;
                case EXPR_STATEMENT:                  return true; // TODO
                case EXPR_COMPOUND_LITERAL:           return false;
 
@@ -9445,7 +8916,7 @@ static expression_t *parse_##binexpression_type(expression_t *left)          \
        binexpr->binary.left  = left;                                            \
        eat(token_type);                                                         \
                                                                              \
-       expression_t *right = parse_sub_expression(prec_r);                      \
+       expression_t *right = parse_subexpression(prec_r);                       \
                                                                              \
        binexpr->binary.right = right;                                           \
        sfunc(&binexpr->binary);                                                 \
@@ -9477,15 +8948,15 @@ CREATE_BINEXPR_PARSER(T_MINUSEQUAL,           EXPR_BINARY_SUB_ASSIGN,         PR
 CREATE_BINEXPR_PARSER(T_ASTERISKEQUAL,        EXPR_BINARY_MUL_ASSIGN,         PREC_ASSIGNMENT,     semantic_arithmetic_assign)
 CREATE_BINEXPR_PARSER(T_SLASHEQUAL,           EXPR_BINARY_DIV_ASSIGN,         PREC_ASSIGNMENT,     semantic_divmod_assign)
 CREATE_BINEXPR_PARSER(T_PERCENTEQUAL,         EXPR_BINARY_MOD_ASSIGN,         PREC_ASSIGNMENT,     semantic_divmod_assign)
-CREATE_BINEXPR_PARSER(T_LESSLESSEQUAL,        EXPR_BINARY_SHIFTLEFT_ASSIGN,   PREC_ASSIGNMENT,     semantic_arithmetic_assign)
-CREATE_BINEXPR_PARSER(T_GREATERGREATEREQUAL,  EXPR_BINARY_SHIFTRIGHT_ASSIGN,  PREC_ASSIGNMENT,     semantic_arithmetic_assign)
-CREATE_BINEXPR_PARSER(T_ANDEQUAL,             EXPR_BINARY_BITWISE_AND_ASSIGN, PREC_ASSIGNMENT,     semantic_arithmetic_assign)
-CREATE_BINEXPR_PARSER(T_PIPEEQUAL,            EXPR_BINARY_BITWISE_OR_ASSIGN,  PREC_ASSIGNMENT,     semantic_arithmetic_assign)
-CREATE_BINEXPR_PARSER(T_CARETEQUAL,           EXPR_BINARY_BITWISE_XOR_ASSIGN, PREC_ASSIGNMENT,     semantic_arithmetic_assign)
+CREATE_BINEXPR_PARSER(T_LESSLESSEQUAL,        EXPR_BINARY_SHIFTLEFT_ASSIGN,   PREC_ASSIGNMENT,     semantic_shift_assign)
+CREATE_BINEXPR_PARSER(T_GREATERGREATEREQUAL,  EXPR_BINARY_SHIFTRIGHT_ASSIGN,  PREC_ASSIGNMENT,     semantic_shift_assign)
+CREATE_BINEXPR_PARSER(T_ANDEQUAL,             EXPR_BINARY_BITWISE_AND_ASSIGN, PREC_ASSIGNMENT,     semantic_integer_assign)
+CREATE_BINEXPR_PARSER(T_PIPEEQUAL,            EXPR_BINARY_BITWISE_OR_ASSIGN,  PREC_ASSIGNMENT,     semantic_integer_assign)
+CREATE_BINEXPR_PARSER(T_CARETEQUAL,           EXPR_BINARY_BITWISE_XOR_ASSIGN, PREC_ASSIGNMENT,     semantic_integer_assign)
 CREATE_BINEXPR_PARSER(',',                    EXPR_BINARY_COMMA,              PREC_ASSIGNMENT,     semantic_comma)
 
 
-static expression_t *parse_sub_expression(precedence_t precedence)
+static expression_t *parse_subexpression(precedence_t precedence)
 {
        if (token.type < 0) {
                return expected_expression_error();
@@ -9530,7 +9001,7 @@ static expression_t *parse_sub_expression(precedence_t precedence)
  */
 static expression_t *parse_expression(void)
 {
-       return parse_sub_expression(PREC_EXPRESSION);
+       return parse_subexpression(PREC_EXPRESSION);
 }
 
 /**
@@ -9645,14 +9116,13 @@ static asm_argument_t *parse_asm_arguments(bool is_out)
                asm_argument_t *argument = allocate_ast_zero(sizeof(argument[0]));
                memset(argument, 0, sizeof(argument[0]));
 
-               if (token.type == '[') {
-                       eat('[');
+               if (next_if('[')) {
                        if (token.type != T_IDENTIFIER) {
                                parse_error_expected("while parsing asm argument",
                                                     T_IDENTIFIER, NULL);
                                return NULL;
                        }
-                       argument->symbol = token.v.symbol;
+                       argument->symbol = token.symbol;
 
                        expect(']', end_error);
                }
@@ -9712,7 +9182,9 @@ static asm_argument_t *parse_asm_arguments(bool is_out)
                                       "asm output argument is not an lvalue");
                        }
 
-                       if (argument->constraints.begin[0] == '+')
+                       if (argument->constraints.begin[0] == '=')
+                               determine_lhs_ent(expression, NULL);
+                       else
                                mark_vars_read(expression, NULL);
                } else {
                        mark_vars_read(expression, NULL);
@@ -9725,9 +9197,8 @@ static asm_argument_t *parse_asm_arguments(bool is_out)
                *anchor = argument;
                anchor  = &argument->next;
 
-               if (token.type != ',')
+               if (!next_if(','))
                        break;
-               eat(',');
        }
 
        return result;
@@ -9740,23 +9211,18 @@ end_error:
  */
 static asm_clobber_t *parse_asm_clobbers(void)
 {
-       asm_clobber_t *result = NULL;
-       asm_clobber_t *last   = NULL;
+       asm_clobber_t *result  = NULL;
+       asm_clobber_t **anchor = &result;
 
        while (token.type == T_STRING_LITERAL) {
                asm_clobber_t *clobber = allocate_ast_zero(sizeof(clobber[0]));
                clobber->clobber       = parse_string_literals();
 
-               if (last != NULL) {
-                       last->next = clobber;
-               } else {
-                       result = clobber;
-               }
-               last = clobber;
+               *anchor = clobber;
+               anchor  = &clobber->next;
 
-               if (token.type != ',')
+               if (!next_if(','))
                        break;
-               eat(',');
        }
 
        return result;
@@ -9772,36 +9238,35 @@ static statement_t *parse_asm_statement(void)
 
        eat(T_asm);
 
-       if (token.type == T_volatile) {
-               next_token();
+       if (next_if(T_volatile))
                asm_statement->is_volatile = true;
-       }
 
        expect('(', end_error);
        add_anchor_token(')');
-       add_anchor_token(':');
+       if (token.type != T_STRING_LITERAL) {
+               parse_error_expected("after asm(", T_STRING_LITERAL, NULL);
+               goto end_of_asm;
+       }
        asm_statement->asm_text = parse_string_literals();
 
-       if (token.type != ':') {
+       add_anchor_token(':');
+       if (!next_if(':')) {
                rem_anchor_token(':');
                goto end_of_asm;
        }
-       eat(':');
 
        asm_statement->outputs = parse_asm_arguments(true);
-       if (token.type != ':') {
+       if (!next_if(':')) {
                rem_anchor_token(':');
                goto end_of_asm;
        }
-       eat(':');
 
        asm_statement->inputs = parse_asm_arguments(false);
-       if (token.type != ':') {
+       if (!next_if(':')) {
                rem_anchor_token(':');
                goto end_of_asm;
        }
        rem_anchor_token(':');
-       eat(':');
 
        asm_statement->clobbers = parse_asm_clobbers();
 
@@ -9821,6 +9286,36 @@ end_error:
        return create_invalid_statement();
 }
 
+static statement_t *parse_label_inner_statement(char const *const label, bool const eat_empty_stmt)
+{
+       statement_t *inner_stmt;
+       switch (token.type) {
+               case '}':
+                       errorf(HERE, "%s at end of compound statement", label);
+                       inner_stmt = create_invalid_statement();
+                       break;
+
+               case ';':
+                       if (eat_empty_stmt) {
+                               /* Eat an empty statement here, to avoid the warning about an empty
+                                * statement after a label.  label:; is commonly used to have a label
+                                * before a closing brace. */
+                               inner_stmt = create_empty_statement();
+                               next_token();
+                               break;
+                       }
+                       /* FALLTHROUGH */
+
+               default:
+                       inner_stmt = parse_statement();
+                       if (inner_stmt->kind == STATEMENT_DECLARATION) {
+                               errorf(&inner_stmt->base.source_position, "declaration after %s", label);
+                       }
+                       break;
+       }
+       return inner_stmt;
+}
+
 /**
  * Parse a case statement.
  */
@@ -9833,35 +9328,30 @@ static statement_t *parse_case_statement(void)
 
        expression_t *const expression   = parse_expression();
        statement->case_label.expression = expression;
-       if (!is_constant_expression(expression)) {
-               /* This check does not prevent the error message in all cases of an
-                * prior error while parsing the expression.  At least it catches the
-                * common case of a mistyped enum entry. */
-               if (is_type_valid(skip_typeref(expression->base.type))) {
+       expression_classification_t const expr_class = is_constant_expression(expression);
+       if (expr_class != EXPR_CLASS_CONSTANT) {
+               if (expr_class != EXPR_CLASS_ERROR) {
                        errorf(pos, "case label does not reduce to an integer constant");
                }
                statement->case_label.is_bad = true;
        } else {
-               long const val = fold_constant(expression);
+               long const val = fold_constant_to_int(expression);
                statement->case_label.first_case = val;
                statement->case_label.last_case  = val;
        }
 
        if (GNU_MODE) {
-               if (token.type == T_DOTDOTDOT) {
-                       next_token();
+               if (next_if(T_DOTDOTDOT)) {
                        expression_t *const end_range   = parse_expression();
                        statement->case_label.end_range = end_range;
-                       if (!is_constant_expression(end_range)) {
-                               /* This check does not prevent the error message in all cases of an
-                                * prior error while parsing the expression.  At least it catches the
-                                * common case of a mistyped enum entry. */
-                               if (is_type_valid(skip_typeref(end_range->base.type))) {
+                       expression_classification_t const end_class = is_constant_expression(end_range);
+                       if (end_class != EXPR_CLASS_CONSTANT) {
+                               if (end_class != EXPR_CLASS_ERROR) {
                                        errorf(pos, "case range does not reduce to an integer constant");
                                }
                                statement->case_label.is_bad = true;
                        } else {
-                               long const val = fold_constant(end_range);
+                               long const val = fold_constant_to_int(end_range);
                                statement->case_label.last_case = val;
 
                                if (warning.other && val < statement->case_label.first_case) {
@@ -9904,11 +9394,7 @@ end_error:
                errorf(pos, "case label not within a switch statement");
        }
 
-       statement_t *const inner_stmt = parse_statement();
-       statement->case_label.statement = inner_stmt;
-       if (inner_stmt->kind == STATEMENT_DECLARATION) {
-               errorf(&inner_stmt->base.source_position, "declaration after case label");
-       }
+       statement->case_label.statement = parse_label_inner_statement("case label", false);
 
        POP_PARENT;
        return statement;
@@ -9926,6 +9412,8 @@ static statement_t *parse_default_statement(void)
        PUSH_PARENT(statement);
 
        expect(':', end_error);
+end_error:
+
        if (current_switch != NULL) {
                const case_label_statement_t *def_label = current_switch->default_label;
                if (def_label != NULL) {
@@ -9947,17 +9435,10 @@ static statement_t *parse_default_statement(void)
                        "'default' label not within a switch statement");
        }
 
-       statement_t *const inner_stmt = parse_statement();
-       statement->case_label.statement = inner_stmt;
-       if (inner_stmt->kind == STATEMENT_DECLARATION) {
-               errorf(&inner_stmt->base.source_position, "declaration after default label");
-       }
+       statement->case_label.statement = parse_label_inner_statement("default label", false);
 
        POP_PARENT;
        return statement;
-end_error:
-       POP_PARENT;
-       return create_invalid_statement();
 }
 
 /**
@@ -9966,7 +9447,7 @@ end_error:
 static statement_t *parse_label_statement(void)
 {
        assert(token.type == T_IDENTIFIER);
-       symbol_t *symbol = token.v.symbol;
+       symbol_t *symbol = token.symbol;
        label_t  *label  = get_label(symbol);
 
        statement_t *const statement = allocate_statement_zero(STATEMENT_LABEL);
@@ -9989,28 +9470,7 @@ static statement_t *parse_label_statement(void)
 
        eat(':');
 
-       if (token.type == '}') {
-               /* TODO only warn? */
-               if (warning.other && false) {
-                       warningf(HERE, "label at end of compound statement");
-                       statement->label.statement = create_empty_statement();
-               } else {
-                       errorf(HERE, "label at end of compound statement");
-                       statement->label.statement = create_invalid_statement();
-               }
-       } else if (token.type == ';') {
-               /* Eat an empty statement here, to avoid the warning about an empty
-                * statement after a label.  label:; is commonly used to have a label
-                * before a closing brace. */
-               statement->label.statement = create_empty_statement();
-               next_token();
-       } else {
-               statement_t *const inner_stmt = parse_statement();
-               statement->label.statement = inner_stmt;
-               if (inner_stmt->kind == STATEMENT_DECLARATION) {
-                       errorf(&inner_stmt->base.source_position, "declaration after label");
-               }
-       }
+       statement->label.statement = parse_label_inner_statement("label", true);
 
        /* remember the labels in a list for later checking */
        *label_anchor = &statement->label;
@@ -10052,8 +9512,7 @@ end_error:
        statement->ifs.true_statement = true_stmt;
        rem_anchor_token(T_else);
 
-       if (token.type == T_else) {
-               next_token();
+       if (next_if(T_else)) {
                statement->ifs.false_statement = parse_statement();
        } else if (warning.parentheses &&
                        true_stmt->kind == STATEMENT_IF &&
@@ -10089,7 +9548,7 @@ static void check_enum_cases(const switch_statement_t *statement)
        for (; entry != NULL && entry->kind == ENTITY_ENUM_VALUE;
             entry = entry->base.next) {
                const expression_t *expression = entry->enum_value.value;
-               long                value      = expression != NULL ? fold_constant(expression) : last_value + 1;
+               long                value      = expression != NULL ? fold_constant_to_int(expression) : last_value + 1;
                bool                found      = false;
                for (const case_label_statement_t *l = statement->first_case; l != NULL; l = l->next) {
                        if (l->expression == NULL)
@@ -10255,8 +9714,12 @@ static statement_t *parse_for(void)
        size_t const  top       = environment_top();
        scope_t      *old_scope = scope_push(&statement->fors.scope);
 
-       if (token.type == ';') {
-               next_token();
+       bool old_gcc_extension = in_gcc_extension;
+       while (next_if(T___extension__)) {
+               in_gcc_extension = true;
+       }
+
+       if (next_if(';')) {
        } else if (is_declaration_specifier(&token, false)) {
                parse_declaration(record_entity, DECL_FLAGS_NONE);
        } else {
@@ -10271,6 +9734,7 @@ static statement_t *parse_for(void)
                rem_anchor_token(';');
                expect(';', end_error2);
        }
+       in_gcc_extension = old_gcc_extension;
 
        if (token.type != ';') {
                add_anchor_token(';');
@@ -10323,8 +9787,7 @@ static statement_t *parse_goto(void)
        statement_t *statement = allocate_statement_zero(STATEMENT_GOTO);
        eat(T_goto);
 
-       if (GNU_MODE && token.type == '*') {
-               next_token();
+       if (GNU_MODE && next_if('*')) {
                expression_t *expression = parse_expression();
                mark_vars_read(expression, NULL);
 
@@ -10344,19 +9807,17 @@ static statement_t *parse_goto(void)
                }
 
                statement->gotos.expression = expression;
-       } else {
-               if (token.type != T_IDENTIFIER) {
-                       if (GNU_MODE)
-                               parse_error_expected("while parsing goto", T_IDENTIFIER, '*', NULL);
-                       else
-                               parse_error_expected("while parsing goto", T_IDENTIFIER, NULL);
-                       eat_until_anchor();
-                       goto end_error;
-               }
-               symbol_t *symbol = token.v.symbol;
+       } else if (token.type == T_IDENTIFIER) {
+               symbol_t *symbol = token.symbol;
                next_token();
-
                statement->gotos.label = get_label(symbol);
+       } else {
+               if (GNU_MODE)
+                       parse_error_expected("while parsing goto", T_IDENTIFIER, '*', NULL);
+               else
+                       parse_error_expected("while parsing goto", T_IDENTIFIER, NULL);
+               eat_until_anchor();
+               return create_invalid_statement();
        }
 
        /* remember the goto's in a list for later checking */
@@ -10365,9 +9826,8 @@ static statement_t *parse_goto(void)
 
        expect(';', end_error);
 
-       return statement;
 end_error:
-       return create_invalid_statement();
+       return statement;
 }
 
 /**
@@ -10513,10 +9973,10 @@ static statement_t *parse_return(void)
                                /* Only warn in C mode, because GCC does the same */
                                if (strict_mode) {
                                        errorf(pos,
-                                                       "'return' with expression in function return 'void'");
+                                                       "'return' with expression in function returning 'void'");
                                } else if (warning.other) {
                                        warningf(pos,
-                                                       "'return' with expression in function return 'void'");
+                                                       "'return' with expression in function returning 'void'");
                                }
                        }
                } else {
@@ -10609,8 +10069,7 @@ static statement_t *parse_ms_try_statment(void)
 
        POP_PARENT;
 
-       if (token.type == T___except) {
-               eat(T___except);
+       if (next_if(T___except)) {
                expect('(', end_error);
                add_anchor_token(')');
                expression_t *const expr = parse_expression();
@@ -10627,8 +10086,7 @@ static statement_t *parse_ms_try_statment(void)
                rem_anchor_token(')');
                expect(')', end_error);
                statement->ms_try.final_statement = parse_compound_statement(false);
-       } else if (token.type == T__finally) {
-               eat(T___finally);
+       } else if (next_if(T__finally)) {
                statement->ms_try.final_statement = parse_compound_statement(false);
        } else {
                parse_error_expected("while parsing __try statement", T___except, T___finally, NULL);
@@ -10655,15 +10113,16 @@ static statement_t *parse_local_label_declaration(void)
 
        eat(T___label__);
 
-       entity_t *begin = NULL, *end = NULL;
-
-       while (true) {
+       entity_t *begin   = NULL;
+       entity_t *end     = NULL;
+       entity_t **anchor = &begin;
+       do {
                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;
+               symbol_t *symbol = token.symbol;
                entity_t *entity = get_entity(symbol, NAMESPACE_LABEL);
                if (entity != NULL && entity->base.parent_scope == current_scope) {
                        errorf(HERE, "multiple definitions of '__label__ %Y' (previous definition %P)",
@@ -10676,21 +10135,15 @@ static statement_t *parse_local_label_declaration(void)
                        entity->base.source_position = token.source_position;
                        entity->base.symbol          = symbol;
 
-                       if (end != NULL)
-                               end->base.next = entity;
-                       end = entity;
-                       if (begin == NULL)
-                               begin = entity;
+                       *anchor = entity;
+                       anchor  = &entity->base.next;
+                       end     = entity;
 
                        environment_push(entity);
                }
                next_token();
-
-               if (token.type != ',')
-                       break;
-               next_token();
-       }
-       eat(';');
+       } while (next_if(','));
+       expect(';', end_error);
 end_error:
        statement->declaration.declarations_begin = begin;
        statement->declaration.declarations_end   = end;
@@ -10705,13 +10158,13 @@ static void parse_namespace_definition(void)
        symbol_t *symbol = NULL;
 
        if (token.type == T_IDENTIFIER) {
-               symbol = token.v.symbol;
+               symbol = token.symbol;
                next_token();
 
                entity = get_entity(symbol, NAMESPACE_NORMAL);
-               if (entity       != NULL             &&
-                               entity->kind != ENTITY_NAMESPACE &&
-                               entity->base.parent_scope == current_scope) {
+               if (entity != NULL
+                               && entity->kind != ENTITY_NAMESPACE
+                               && entity->base.parent_scope == current_scope) {
                        if (!is_error_entity(entity)) {
                                error_redefined_as_different_kind(&token.source_position,
                                                entity, ENTITY_NAMESPACE);
@@ -10739,12 +10192,17 @@ static void parse_namespace_definition(void)
        size_t const  top       = environment_top();
        scope_t      *old_scope = scope_push(&entity->namespacee.members);
 
+       entity_t     *old_current_entity = current_entity;
+       current_entity = entity;
+
        expect('{', end_error);
        parse_externals();
        expect('}', end_error);
 
 end_error:
        assert(current_scope == &entity->namespacee.members);
+       assert(current_entity == entity);
+       current_entity = old_current_entity;
        scope_pop(old_scope);
        environment_pop_to(top);
 }
@@ -10765,7 +10223,7 @@ static statement_t *intern_parse_statement(void)
                token_type_t la1_type = (token_type_t)look_ahead(1)->type;
                if (la1_type == ':') {
                        statement = parse_label_statement();
-               } else if (is_typedef_symbol(token.v.symbol)) {
+               } else if (is_typedef_symbol(token.symbol)) {
                        statement = parse_declaration_statement();
                } else {
                        /* it's an identifier, the grammar says this must be an
@@ -10775,18 +10233,14 @@ static statement_t *intern_parse_statement(void)
                        switch (la1_type) {
                        case '&':
                        case '*':
-                               if (get_entity(token.v.symbol, NAMESPACE_NORMAL) != NULL)
-                                       goto expression_statment;
-                               /* FALLTHROUGH */
-
+                               if (get_entity(token.symbol, NAMESPACE_NORMAL) != NULL) {
+                       default:
+                                       statement = parse_expression_statement();
+                               } else {
                        DECLARATION_START
                        case T_IDENTIFIER:
-                               statement = parse_declaration_statement();
-                               break;
-
-                       default:
-expression_statment:
-                               statement = parse_expression_statement();
+                                       statement = parse_declaration_statement();
+                               }
                                break;
                        }
                }
@@ -10796,9 +10250,7 @@ expression_statment:
        case T___extension__:
                /* This can be a prefix to a declaration or an expression statement.
                 * We simply eat it now and parse the rest with tail recursion. */
-               do {
-                       next_token();
-               } while (token.type == T___extension__);
+               while (next_if(T___extension__)) {}
                bool old_gcc_extension = in_gcc_extension;
                in_gcc_extension       = true;
                statement = intern_parse_statement();
@@ -11195,8 +10647,7 @@ static void parse_linkage_specification(void)
        }
        current_linkage = new_linkage;
 
-       if (token.type == '{') {
-               next_token();
+       if (next_if('{')) {
                parse_externals();
                expect('}', end_error);
        } else {
@@ -11325,8 +10776,7 @@ void start_parsing(void)
        error_count       = 0;
        warning_count     = 0;
 
-       type_set_output(stderr);
-       ast_set_output(stderr);
+       print_to_file(stderr);
 
        assert(unit == NULL);
        unit = allocate_ast_zero(sizeof(unit[0]));
@@ -11389,6 +10839,36 @@ static void complete_incomplete_arrays(void)
        }
 }
 
+void prepare_main_collect2(entity_t *entity)
+{
+       // create call to __main
+       symbol_t *symbol         = symbol_table_insert("__main");
+       entity_t *subsubmain_ent
+               = create_implicit_function(symbol, &builtin_source_position);
+
+       expression_t *ref         = allocate_expression_zero(EXPR_REFERENCE);
+       type_t       *ftype       = subsubmain_ent->declaration.type;
+       ref->base.source_position = builtin_source_position;
+       ref->base.type            = make_pointer_type(ftype, TYPE_QUALIFIER_NONE);
+       ref->reference.entity     = subsubmain_ent;
+
+       expression_t *call = allocate_expression_zero(EXPR_CALL);
+       call->base.source_position = builtin_source_position;
+       call->base.type            = type_void;
+       call->call.function        = ref;
+
+       statement_t *expr_statement = allocate_statement_zero(STATEMENT_EXPRESSION);
+       expr_statement->base.source_position  = builtin_source_position;
+       expr_statement->expression.expression = call;
+
+       statement_t *statement = entity->function.statement;
+       assert(statement->kind == STATEMENT_COMPOUND);
+       compound_statement_t *compounds = &statement->compound;
+
+       expr_statement->base.next = compounds->statements;
+       compounds->statements     = expr_statement;
+}
+
 void parse(void)
 {
        lookahead_bufpos = 0;
@@ -11403,103 +10883,6 @@ void parse(void)
        incomplete_arrays = NULL;
 }
 
-/**
- * create a builtin function.
- */
-static entity_t *create_builtin_function(builtin_kind_t kind, const char *name, type_t *function_type)
-{
-       symbol_t *symbol = symbol_table_insert(name);
-       entity_t *entity = allocate_entity_zero(ENTITY_FUNCTION);
-       entity->declaration.storage_class          = STORAGE_CLASS_EXTERN;
-       entity->declaration.declared_storage_class = STORAGE_CLASS_EXTERN;
-       entity->declaration.type                   = function_type;
-       entity->declaration.implicit               = true;
-       entity->base.symbol                        = symbol;
-       entity->base.source_position               = builtin_source_position;
-
-       entity->function.btk                       = kind;
-
-       record_entity(entity, /*is_definition=*/false);
-       return entity;
-}
-
-
-/**
- * Create predefined gnu builtins.
- */
-static void create_gnu_builtins(void)
-{
-#define GNU_BUILTIN(a, b) create_builtin_function(bk_gnu_builtin_##a, "__builtin_" #a, b)
-
-       GNU_BUILTIN(alloca,         make_function_1_type(type_void_ptr, type_size_t));
-       GNU_BUILTIN(huge_val,       make_function_0_type(type_double));
-       GNU_BUILTIN(inf,            make_function_0_type(type_double));
-       GNU_BUILTIN(inff,           make_function_0_type(type_float));
-       GNU_BUILTIN(infl,           make_function_0_type(type_long_double));
-       GNU_BUILTIN(nan,            make_function_1_type(type_double, type_char_ptr));
-       GNU_BUILTIN(nanf,           make_function_1_type(type_float, type_char_ptr));
-       GNU_BUILTIN(nanl,           make_function_1_type(type_long_double, type_char_ptr));
-       GNU_BUILTIN(va_end,         make_function_1_type(type_void, type_valist));
-       GNU_BUILTIN(expect,         make_function_2_type(type_long, type_long, type_long));
-       GNU_BUILTIN(return_address, make_function_1_type(type_void_ptr, type_unsigned_int));
-       GNU_BUILTIN(frame_address,  make_function_1_type(type_void_ptr, type_unsigned_int));
-       GNU_BUILTIN(ffs,            make_function_1_type(type_int, type_unsigned_int));
-       GNU_BUILTIN(clz,            make_function_1_type(type_int, type_unsigned_int));
-       GNU_BUILTIN(ctz,            make_function_1_type(type_int, type_unsigned_int));
-       GNU_BUILTIN(popcount,       make_function_1_type(type_int, type_unsigned_int));
-       GNU_BUILTIN(parity,         make_function_1_type(type_int, type_unsigned_int));
-       GNU_BUILTIN(prefetch,       make_function_1_type_variadic(type_float, type_void_ptr));
-       GNU_BUILTIN(trap,           make_function_0_type_noreturn(type_void));
-
-#undef GNU_BUILTIN
-}
-
-/**
- * Create predefined MS intrinsics.
- */
-static void create_microsoft_intrinsics(void)
-{
-#define MS_BUILTIN(a, b) create_builtin_function(bk_ms##a, #a, b)
-
-       /* intrinsics for all architectures */
-       MS_BUILTIN(_rotl,                  make_function_2_type(type_unsigned_int,   type_unsigned_int, type_int));
-       MS_BUILTIN(_rotr,                  make_function_2_type(type_unsigned_int,   type_unsigned_int, type_int));
-       MS_BUILTIN(_rotl64,                make_function_2_type(type_unsigned_int64, type_unsigned_int64, type_int));
-       MS_BUILTIN(_rotr64,                make_function_2_type(type_unsigned_int64, type_unsigned_int64, type_int));
-       MS_BUILTIN(_byteswap_ushort,       make_function_1_type(type_unsigned_short, type_unsigned_short));
-       MS_BUILTIN(_byteswap_ulong,        make_function_1_type(type_unsigned_long,  type_unsigned_long));
-       MS_BUILTIN(_byteswap_uint64,       make_function_1_type(type_unsigned_int64, type_unsigned_int64));
-
-       MS_BUILTIN(__debugbreak,    make_function_0_type(type_void));
-       MS_BUILTIN(_ReturnAddress,  make_function_0_type(type_void_ptr));
-       MS_BUILTIN(__popcount,      make_function_1_type(type_unsigned_int, type_unsigned_int));
-
-       /* x86/x64 only */
-       MS_BUILTIN(_enable,                make_function_0_type(type_void));
-       MS_BUILTIN(_disable,               make_function_0_type(type_void));
-       MS_BUILTIN(__inbyte,               make_function_1_type(type_unsigned_char, type_unsigned_short));
-       MS_BUILTIN(__inword,               make_function_1_type(type_unsigned_short, type_unsigned_short));
-       MS_BUILTIN(__indword,              make_function_1_type(type_unsigned_long, type_unsigned_short));
-       MS_BUILTIN(__outbyte,              make_function_2_type(type_void, type_unsigned_short, type_unsigned_char));
-       MS_BUILTIN(__outword,              make_function_2_type(type_void, type_unsigned_short, type_unsigned_short));
-       MS_BUILTIN(__outdword,             make_function_2_type(type_void, type_unsigned_short, type_unsigned_long));
-       MS_BUILTIN(__ud2,                  make_function_0_type_noreturn(type_void));
-       MS_BUILTIN(_BitScanForward,        make_function_2_type(type_unsigned_char, type_unsigned_long_ptr, type_unsigned_long));
-       MS_BUILTIN(_BitScanReverse,        make_function_2_type(type_unsigned_char, type_unsigned_long_ptr, type_unsigned_long));
-       MS_BUILTIN(_InterlockedExchange,   make_function_2_type(type_long, type_long_ptr, type_long));
-       MS_BUILTIN(_InterlockedExchange64, make_function_2_type(type_int64, type_int64_ptr, type_int64));
-
-       if (machine_size <= 32) {
-               MS_BUILTIN(__readeflags,           make_function_0_type(type_unsigned_int));
-               MS_BUILTIN(__writeeflags,          make_function_1_type(type_void, type_unsigned_int));
-       } else {
-               MS_BUILTIN(__readeflags,           make_function_0_type(type_unsigned_int64));
-               MS_BUILTIN(__writeeflags,          make_function_1_type(type_void, type_unsigned_int64));
-       }
-
-#undef MS_BUILTIN
-}
-
 /**
  * Initialize the parser.
  */
@@ -11507,28 +10890,6 @@ void init_parser(void)
 {
        sym_anonymous = symbol_table_insert("<anonymous>");
 
-       if (c_mode & _MS) {
-               /* add predefined symbols for extended-decl-modifier */
-               sym_align         = symbol_table_insert("align");
-               sym_allocate      = symbol_table_insert("allocate");
-               sym_dllimport     = symbol_table_insert("dllimport");
-               sym_dllexport     = symbol_table_insert("dllexport");
-               sym_naked         = symbol_table_insert("naked");
-               sym_noinline      = symbol_table_insert("noinline");
-               sym_returns_twice = symbol_table_insert("returns_twice");
-               sym_noreturn      = symbol_table_insert("noreturn");
-               sym_nothrow       = symbol_table_insert("nothrow");
-               sym_novtable      = symbol_table_insert("novtable");
-               sym_property      = symbol_table_insert("property");
-               sym_get           = symbol_table_insert("get");
-               sym_put           = symbol_table_insert("put");
-               sym_selectany     = symbol_table_insert("selectany");
-               sym_thread        = symbol_table_insert("thread");
-               sym_uuid          = symbol_table_insert("uuid");
-               sym_deprecated    = symbol_table_insert("deprecated");
-               sym_restrict      = symbol_table_insert("restrict");
-               sym_noalias       = symbol_table_insert("noalias");
-       }
        memset(token_anchor_set, 0, sizeof(token_anchor_set));
 
        init_expression_parsers();