fix for error44
[cparser] / parser.c
index ac0a255..8e89ce8 100644 (file)
--- a/parser.c
+++ b/parser.c
@@ -67,7 +67,7 @@ struct declaration_specifiers_t {
        unsigned char      alignment;         /**< Alignment, 0 if not set. */
        unsigned int       is_inline : 1;
        unsigned int       deprecated : 1;
-       decl_modifiers_t   decl_modifiers;    /**< MS __declspec extended modifier mask */
+       decl_modifiers_t   modifiers;         /**< declaration modifiers */
        gnu_attribute_t   *gnu_attributes;    /**< list of GNU attributes */
        const char        *deprecated_string; /**< can be set if declaration was marked deprecated. */
        symbol_t          *get_property_sym;  /**< the name of the get property if set. */
@@ -163,7 +163,8 @@ static void semantic_comparison(binary_expression_t *expression);
        case T_restrict:        \
        case T_volatile:        \
        case T_inline:          \
-       case T__forceinline:
+       case T__forceinline:    \
+       case T___attribute__:
 
 #ifdef PROVIDE_COMPLEX
 #define COMPLEX_SPECIFIERS  \
@@ -872,11 +873,74 @@ static expression_t *create_implicit_cast(expression_t *expression,
        return create_cast_expression(expression, dest_type);
 }
 
+typedef enum assign_error_t {
+       ASSIGN_SUCCESS,
+       ASSIGN_ERROR_INCOMPATIBLE,
+       ASSIGN_ERROR_POINTER_QUALIFIER_MISSING,
+       ASSIGN_WARNING_POINTER_INCOMPATIBLE,
+       ASSIGN_WARNING_POINTER_FROM_INT,
+       ASSIGN_WARNING_INT_FROM_POINTER
+} assign_error_t;
+
+static void report_assign_error(assign_error_t error, type_t *orig_type_left,
+                                const expression_t *const right,
+                                const char *context,
+                                const source_position_t *source_position)
+{
+       type_t *const orig_type_right = right->base.type;
+       type_t *const type_left       = skip_typeref(orig_type_left);
+       type_t *const type_right      = skip_typeref(orig_type_right);
+
+       switch (error) {
+       case ASSIGN_SUCCESS:
+               return;
+       case ASSIGN_ERROR_INCOMPATIBLE:
+               errorf(source_position,
+                      "destination type '%T' in %s is incompatible with type '%T'",
+                      orig_type_left, context, orig_type_right);
+               return;
+
+       case ASSIGN_ERROR_POINTER_QUALIFIER_MISSING: {
+               type_t *points_to_left
+                       = skip_typeref(type_left->pointer.points_to);
+               type_t *points_to_right
+                       = skip_typeref(type_right->pointer.points_to);
+
+               /* the left type has all qualifiers from the right type */
+               unsigned missing_qualifiers
+                       = points_to_right->base.qualifiers & ~points_to_left->base.qualifiers;
+               errorf(source_position,
+                      "destination type '%T' in %s from type '%T' lacks qualifiers '%Q' in pointed-to type",
+                      orig_type_left, context, orig_type_right, missing_qualifiers);
+               return;
+       }
+
+       case ASSIGN_WARNING_POINTER_INCOMPATIBLE:
+               warningf(source_position,
+                        "destination type '%T' in %s is incompatible with '%E' of type '%T'",
+                                orig_type_left, context, right, orig_type_right);
+               return;
+
+       case ASSIGN_WARNING_POINTER_FROM_INT:
+               warningf(source_position,
+                        "%s makes integer '%T' from pointer '%T' without a cast",
+                                context, orig_type_left, orig_type_right);
+               return;
+
+       case ASSIGN_WARNING_INT_FROM_POINTER:
+               warningf(source_position,
+                               "%s makes integer '%T' from pointer '%T' without a cast",
+                               context, orig_type_left, orig_type_right);
+               return;
+
+       default:
+               panic("invalid error value");
+       }
+}
+
 /** Implements the rules from ยง 6.5.16.1 */
-static type_t *semantic_assign(type_t *orig_type_left,
-                            const expression_t *const right,
-                            const char *context,
-                            const source_position_t *source_position)
+static assign_error_t semantic_assign(type_t *orig_type_left,
+                                      const expression_t *const right)
 {
        type_t *const orig_type_right = right->base.type;
        type_t *const type_left       = skip_typeref(orig_type_left);
@@ -884,7 +948,7 @@ static type_t *semantic_assign(type_t *orig_type_left,
 
        if(is_type_pointer(type_left)) {
                if(is_null_pointer_constant(right)) {
-                       return orig_type_left;
+                       return ASSIGN_SUCCESS;
                } else if(is_type_pointer(type_right)) {
                        type_t *points_to_left
                                = skip_typeref(type_left->pointer.points_to);
@@ -895,9 +959,7 @@ static type_t *semantic_assign(type_t *orig_type_left,
                        unsigned missing_qualifiers
                                = points_to_right->base.qualifiers & ~points_to_left->base.qualifiers;
                        if(missing_qualifiers != 0) {
-                               errorf(source_position,
-                                               "destination type '%T' in %s from type '%T' lacks qualifiers '%Q' in pointed-to type", type_left, context, type_right, missing_qualifiers);
-                               return orig_type_left;
+                               return ASSIGN_ERROR_POINTER_QUALIFIER_MISSING;
                        }
 
                        points_to_left  = get_unqualified_type(points_to_left);
@@ -905,47 +967,36 @@ static type_t *semantic_assign(type_t *orig_type_left,
 
                        if (is_type_atomic(points_to_left, ATOMIC_TYPE_VOID) ||
                                        is_type_atomic(points_to_right, ATOMIC_TYPE_VOID)) {
-                               return orig_type_left;
+                               return ASSIGN_SUCCESS;
                        }
 
                        if (!types_compatible(points_to_left, points_to_right)) {
-                               warningf(source_position,
-                                       "destination type '%T' in %s is incompatible with '%E' of type '%T'",
-                                       orig_type_left, context, right, orig_type_right);
+                               return ASSIGN_WARNING_POINTER_INCOMPATIBLE;
                        }
 
-                       return orig_type_left;
+                       return ASSIGN_SUCCESS;
                } else if(is_type_integer(type_right)) {
-                       warningf(source_position,
-                                       "%s makes pointer '%T' from integer '%T' without a cast",
-                                       context, orig_type_left, orig_type_right);
-                       return 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))) {
-               return orig_type_left;
+               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))) {
                type_t *const unqual_type_left  = get_unqualified_type(type_left);
                type_t *const unqual_type_right = get_unqualified_type(type_right);
                if (types_compatible(unqual_type_left, unqual_type_right)) {
-                       return orig_type_left;
+                       return ASSIGN_SUCCESS;
                }
        } else if (is_type_integer(type_left) && is_type_pointer(type_right)) {
-               warningf(source_position,
-                               "%s makes integer '%T' from pointer '%T' without a cast",
-                               context, orig_type_left, orig_type_right);
-               return orig_type_left;
+               return ASSIGN_WARNING_INT_FROM_POINTER;
        }
 
-       if (!is_type_valid(type_left))
-               return type_left;
-
-       if (!is_type_valid(type_right))
-               return orig_type_right;
+       if (!is_type_valid(type_left) || !is_type_valid(type_right))
+               return ASSIGN_SUCCESS;
 
-       return NULL;
+       return ASSIGN_ERROR_INCOMPATIBLE;
 }
 
 static expression_t *parse_constant_expression(void)
@@ -1020,7 +1071,7 @@ static const char *gnu_attribute_names[GNU_AK_LAST] = {
        [GNU_AK_DESTRUCTOR]             = "destructor",
        [GNU_AK_NOTHROW]                = "nothrow",
        [GNU_AK_TRANSPARENT_UNION]      = "transparent_union",
-       [GNU_AK_COMMON]                 = "coommon",
+       [GNU_AK_COMMON]                 = "common",
        [GNU_AK_NOCOMMON]               = "nocommon",
        [GNU_AK_PACKED]                 = "packed",
        [GNU_AK_SHARED]                 = "shared",
@@ -1075,14 +1126,13 @@ static const char *gnu_attribute_names[GNU_AK_LAST] = {
  */
 static int strcmp_underscore(const char *s1, const char *s2) {
        if(s2[0] == '_' && s2[1] == '_') {
-               s2 += 2;
-               size_t l1 = strlen(s1);
-               if(l1 + 2 != strlen(s2)) {
-                       /* not equal */
-                       return 1;
+               size_t len2 = strlen(s2);
+               size_t len1 = strlen(s1);
+               if(len1 == len2-4 && s2[len2-2] == '_' && s2[len2-1] == '_') {
+                       return strncmp(s1, s2+2, len2-4);
                }
-               return strncmp(s1, s2, l1);
        }
+
        return strcmp(s1, s2);
 }
 
@@ -1175,8 +1225,8 @@ static void parse_gnu_attribute_tls_model_arg(gnu_attribute_t *attribute) {
                                return;
                        }
                }
+               errorf(HERE, "'%s' is an unrecognized tls model", string.begin);
        }
-       errorf(HERE, "'%s' is an unrecognized tls model", string.begin);
        attribute->invalid = true;
 }
 
@@ -1199,8 +1249,8 @@ static void parse_gnu_attribute_visibility_arg(gnu_attribute_t *attribute) {
                                return;
                        }
                }
+               errorf(HERE, "'%s' is an unrecognized visibility", string.begin);
        }
-       errorf(HERE, "'%s' is an unrecognized visibility", string.begin);
        attribute->invalid = true;
 }
 
@@ -1222,8 +1272,8 @@ static void parse_gnu_attribute_model_arg(gnu_attribute_t *attribute) {
                                return;
                        }
                }
+               errorf(HERE, "'%s' is an unrecognized model", string.begin);
        }
-       errorf(HERE, "'%s' is an unrecognized model", string.begin);
        attribute->invalid = true;
 }
 
@@ -1262,8 +1312,8 @@ static void parse_gnu_attribute_interrupt_arg(gnu_attribute_t *attribute) {
                                return;
                        }
                }
+               errorf(HERE, "'%s' is not an interrupt", string.begin);
        }
-       errorf(HERE, "'%s' is an interrupt", string.begin);
        attribute->invalid = true;
 }
 
@@ -1311,6 +1361,19 @@ end_error:
        attribute->u.value = true;
 }
 
+static void check_no_argument(gnu_attribute_t *attribute, const char *name)
+{
+       if(!attribute->have_arguments)
+               return;
+
+       /* should have no arguments */
+       errorf(HERE, "wrong number of arguments specified for '%s' attribute", name);
+       eat_until_matching_token('(');
+       /* we have already consumed '(', so we stop before ')', eat it */
+       eat(')');
+       attribute->invalid = true;
+}
+
 /**
  * Parse one GNU attribute.
  *
@@ -1387,10 +1450,11 @@ end_error:
  *  interrupt( string literal )
  *  sentinel( constant expression )
  */
-static void parse_gnu_attribute(gnu_attribute_t **attributes)
+static decl_modifiers_t parse_gnu_attribute(gnu_attribute_t **attributes)
 {
-       gnu_attribute_t *head = *attributes;
-       gnu_attribute_t *last = *attributes;
+       gnu_attribute_t *head      = *attributes;
+       gnu_attribute_t *last      = *attributes;
+       decl_modifiers_t modifiers = 0;
        gnu_attribute_t *attribute;
 
        eat(T___attribute__);
@@ -1407,19 +1471,21 @@ static void parse_gnu_attribute(gnu_attribute_t **attributes)
                /* non-empty attribute list */
                while(true) {
                        const char *name;
-                       if(token.type == T_const) {
+                       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) {
+                       } 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;
                        }
-                       const symbol_t *sym = token.v.symbol;
-                       name = sym->string;
+
                        next_token();
 
                        int i;
@@ -1457,20 +1523,11 @@ static void parse_gnu_attribute(gnu_attribute_t **attributes)
                                case GNU_AK_STDCALL:
                                case GNU_AK_FASTCALL:
                                case GNU_AK_DEPRECATED:
-                               case GNU_AK_NOINLINE:
-                               case GNU_AK_NORETURN:
                                case GNU_AK_NAKED:
-                               case GNU_AK_PURE:
-                               case GNU_AK_ALWAYS_INLINE:
                                case GNU_AK_MALLOC:
                                case GNU_AK_WEAK:
-                               case GNU_AK_CONSTRUCTOR:
-                               case GNU_AK_DESTRUCTOR:
-                               case GNU_AK_NOTHROW:
-                               case GNU_AK_TRANSPARENT_UNION:
                                case GNU_AK_COMMON:
                                case GNU_AK_NOCOMMON:
-                               case GNU_AK_PACKED:
                                case GNU_AK_SHARED:
                                case GNU_AK_NOTSHARED:
                                case GNU_AK_USED:
@@ -1498,16 +1555,62 @@ static void parse_gnu_attribute(gnu_attribute_t **attributes)
                                case GNU_AK_MAY_ALIAS:
                                case GNU_AK_MS_STRUCT:
                                case GNU_AK_GCC_STRUCT:
+                                       check_no_argument(attribute, name);
+                                       break;
+
+                               case GNU_AK_PURE:
+                                       check_no_argument(attribute, name);
+                                       modifiers |= DM_PURE;
+                                       break;
+
+                               case GNU_AK_ALWAYS_INLINE:
+                                       check_no_argument(attribute, name);
+                                       modifiers |= DM_FORCEINLINE;
+                                       break;
+
                                case GNU_AK_DLLIMPORT:
+                                       check_no_argument(attribute, name);
+                                       modifiers |= DM_DLLIMPORT;
+                                       break;
+
                                case GNU_AK_DLLEXPORT:
-                                       if(attribute->have_arguments) {
-                                               /* should have no arguments */
-                                               errorf(HERE, "wrong number of arguments specified for '%s' attribute", name);
-                                               eat_until_matching_token('(');
-                                               /* we have already consumed '(', so we stop before ')', eat it */
-                                               eat(')');
-                                               attribute->invalid = true;
-                                       }
+                                       check_no_argument(attribute, name);
+                                       modifiers |= DM_DLLEXPORT;
+                                       break;
+
+                               case GNU_AK_PACKED:
+                                       check_no_argument(attribute, name);
+                                       modifiers |= DM_PACKED;
+                                       break;
+
+                               case GNU_AK_NOINLINE:
+                                       check_no_argument(attribute, name);
+                                       modifiers |= DM_NOINLINE;
+                                       break;
+
+                               case GNU_AK_NORETURN:
+                                       check_no_argument(attribute, name);
+                                       modifiers |= DM_NORETURN;
+                                       break;
+
+                               case GNU_AK_NOTHROW:
+                                       check_no_argument(attribute, name);
+                                       modifiers |= DM_NOTHROW;
+                                       break;
+
+                               case GNU_AK_TRANSPARENT_UNION:
+                                       check_no_argument(attribute, name);
+                                       modifiers |= DM_TRANSPARENT_UNION;
+                                       break;
+
+                               case GNU_AK_CONSTRUCTOR:
+                                       check_no_argument(attribute, name);
+                                       modifiers |= DM_CONSTRUCTOR;
+                                       break;
+
+                               case GNU_AK_DESTRUCTOR:
+                                       check_no_argument(attribute, name);
+                                       modifiers |= DM_DESTRUCTOR;
                                        break;
 
                                case GNU_AK_ALIGNED:
@@ -1611,17 +1714,21 @@ static void parse_gnu_attribute(gnu_attribute_t **attributes)
        expect(')');
 end_error:
        *attributes = head;
+
+       return modifiers;
 }
 
 /**
  * Parse GNU attributes.
  */
-static void parse_attributes(gnu_attribute_t **attributes)
+static decl_modifiers_t parse_attributes(gnu_attribute_t **attributes)
 {
+       decl_modifiers_t modifiers = 0;
+
        while(true) {
                switch(token.type) {
                case T___attribute__: {
-                       parse_gnu_attribute(attributes);
+                       modifiers |= parse_gnu_attribute(attributes);
                        break;
                }
                case T_asm:
@@ -1644,7 +1751,7 @@ static void parse_attributes(gnu_attribute_t **attributes)
 
 attributes_finished:
 end_error:
-       return;
+       return modifiers;
 }
 
 static designator_t *parse_designation(void)
@@ -1759,13 +1866,14 @@ static initializer_t *initializer_from_expression(type_t *orig_type,
                }
        }
 
-       type_t *const res_type = semantic_assign(type, expression, "initializer",
-                                                &expression->base.source_position);
-       if (res_type == NULL)
+       assign_error_t error = semantic_assign(type, expression);
+       if (error == ASSIGN_ERROR_INCOMPATIBLE)
                return NULL;
+       report_assign_error(error, type, expression, "initializer",
+                           &expression->base.source_position);
 
        initializer_t *const result = allocate_initializer_zero(INITIALIZER_VALUE);
-       result->value.value = create_implicit_cast(expression, res_type);
+       result->value.value = create_implicit_cast(expression, type);
 
        return result;
 }
@@ -2393,6 +2501,12 @@ static initializer_t *parse_initializer(parse_initializer_env_t *env)
                        size = result->wide_string.string.size;
                        break;
 
+               case INITIALIZER_DESIGNATOR:
+               case INITIALIZER_VALUE:
+                       /* can happen for parse errors */
+                       size = 0;
+                       break;
+
                default:
                        internal_errorf(HERE, "invalid initializer type");
                }
@@ -2418,7 +2532,8 @@ static declaration_t *append_declaration(declaration_t *declaration);
 
 static declaration_t *parse_compound_type_specifier(bool is_struct)
 {
-       gnu_attribute_t *attributes = NULL;
+       gnu_attribute_t  *attributes = NULL;
+       decl_modifiers_t  modifiers  = 0;
        if(is_struct) {
                eat(T_struct);
        } else {
@@ -2429,7 +2544,7 @@ static declaration_t *parse_compound_type_specifier(bool is_struct)
        declaration_t *declaration = NULL;
 
        if (token.type == T___attribute__) {
-               parse_attributes(&attributes);
+               modifiers |= parse_attributes(&attributes);
        }
 
        if(token.type == T_IDENTIFIER) {
@@ -2469,16 +2584,18 @@ static declaration_t *parse_compound_type_specifier(bool is_struct)
        if(token.type == '{') {
                if (declaration->init.complete) {
                        assert(symbol != NULL);
-                       errorf(HERE, "multiple definitions of '%s %Y'",
-                              is_struct ? "struct" : "union", symbol);
+                       errorf(HERE, "multiple definitions of '%s %Y' (previous definition at %P)",
+                              is_struct ? "struct" : "union", symbol,
+                              &declaration->source_position);
                        declaration->scope.declarations = NULL;
                }
                declaration->init.complete = true;
 
                parse_compound_type_entries(declaration);
-               parse_attributes(&attributes);
+               modifiers |= parse_attributes(&attributes);
        }
 
+       declaration->modifiers |= modifiers;
        return declaration;
 }
 
@@ -2718,7 +2835,7 @@ static bool check_elignment_value(long long intvalue) {
 
 static void parse_microsoft_extended_decl_modifier(declaration_specifiers_t *specifiers)
 {
-       decl_modifiers_t *modifiers = &specifiers->decl_modifiers;
+       decl_modifiers_t *modifiers = &specifiers->modifiers;
 
        while(true) {
                if(token.type == T_restrict) {
@@ -2854,10 +2971,11 @@ end_error:
 
 static void parse_declaration_specifiers(declaration_specifiers_t *specifiers)
 {
-       type_t   *type            = NULL;
-       unsigned  type_qualifiers = 0;
-       unsigned  type_specifiers = 0;
-       int       newtype         = 0;
+       type_t            *type            = NULL;
+       type_qualifiers_t  qualifiers      = TYPE_QUALIFIER_NONE;
+       type_modifiers_t   modifiers       = TYPE_MODIFIER_NONE;
+       unsigned           type_specifiers = 0;
+       int                newtype         = 0;
 
        specifiers->source_position = token.source_position;
 
@@ -2913,7 +3031,7 @@ static void parse_declaration_specifiers(declaration_specifiers_t *specifiers)
                /* type qualifiers */
 #define MATCH_TYPE_QUALIFIER(token, qualifier)                          \
                case token:                                                     \
-                       type_qualifiers |= qualifier;                               \
+                       qualifiers |= qualifier;                                    \
                        next_token();                                               \
                        break;
 
@@ -2961,7 +3079,7 @@ static void parse_declaration_specifiers(declaration_specifiers_t *specifiers)
 
                case T__forceinline:
                        /* only in microsoft mode */
-                       specifiers->decl_modifiers |= DM_FORCEINLINE;
+                       specifiers->modifiers |= DM_FORCEINLINE;
 
                case T_inline:
                        next_token();
@@ -2987,8 +3105,9 @@ static void parse_declaration_specifiers(declaration_specifiers_t *specifiers)
                }
                case T_union: {
                        type = allocate_type_zero(TYPE_COMPOUND_UNION, HERE);
-
                        type->compound.declaration = parse_compound_type_specifier(false);
+                       if (type->compound.declaration->modifiers & DM_TRANSPARENT_UNION)
+                               modifiers |= TYPE_MODIFIER_TRANSPARENT_UNION;
                        break;
                }
                case T_enum:
@@ -3003,7 +3122,10 @@ static void parse_declaration_specifiers(declaration_specifiers_t *specifiers)
                        break;
 
                case T___attribute__:
-                       parse_attributes(&specifiers->gnu_attributes);
+                       specifiers->modifiers
+                               |= parse_attributes(&specifiers->gnu_attributes);
+                       if (specifiers->modifiers & DM_TRANSPARENT_UNION)
+                               modifiers |= TYPE_MODIFIER_TRANSPARENT_UNION;
                        break;
 
                case T_IDENTIFIER: {
@@ -3199,9 +3321,11 @@ finish_specifiers:
                }
        }
 
-       type->base.qualifiers = type_qualifiers;
        /* FIXME: check type qualifiers here */
 
+       type->base.qualifiers = qualifiers;
+       type->base.modifiers  = modifiers;
+
        type_t *result = typehash_insert(type);
        if(newtype && result != type) {
                free_type(type);
@@ -3214,7 +3338,7 @@ end_error:
 
 static type_qualifiers_t parse_type_qualifiers(void)
 {
-       type_qualifiers_t type_qualifiers = TYPE_QUALIFIER_NONE;
+       type_qualifiers_t qualifiers = TYPE_QUALIFIER_NONE;
 
        while(true) {
                switch(token.type) {
@@ -3230,7 +3354,7 @@ static type_qualifiers_t parse_type_qualifiers(void)
                MATCH_TYPE_QUALIFIER(T___sptr,   TYPE_QUALIFIER_SPTR);
 
                default:
-                       return type_qualifiers;
+                       return qualifiers;
                }
        }
 }
@@ -3301,8 +3425,6 @@ static declaration_t *parse_parameter(void)
 
        declaration_t *declaration = parse_declarator(&specifiers, /*may_be_abstract=*/true);
 
-       semantic_parameter(declaration);
-
        return declaration;
 }
 
@@ -3327,10 +3449,6 @@ static declaration_t *parse_parameters(function_type_t *type)
                type->unspecified_parameters = 1;
                goto parameters_finished;
        }
-       if(token.type == T_void && look_ahead(1)->type == ')') {
-               next_token();
-               goto parameters_finished;
-       }
 
        declaration_t        *declaration;
        declaration_t        *last_declaration = NULL;
@@ -3349,6 +3467,14 @@ static declaration_t *parse_parameters(function_type_t *type)
                DECLARATION_START
                        declaration = parse_parameter();
 
+                       /* func(void) is not a parameter */
+                       if (last_parameter == NULL
+                                       && token.type == ')'
+                                       && skip_typeref(declaration->type) == type_void) {
+                               goto parameters_finished;
+                       }
+                       semantic_parameter(declaration);
+
                        parameter       = obstack_alloc(type_obst, sizeof(parameter[0]));
                        memset(parameter, 0, sizeof(parameter[0]));
                        parameter->type = declaration->type;
@@ -3499,6 +3625,28 @@ static construct_type_t *parse_function_declarator(declaration_t *declaration)
        return (construct_type_t*) construct_function_type;
 }
 
+static void fix_declaration_type(declaration_t *declaration)
+{
+       decl_modifiers_t declaration_modifiers = declaration->modifiers;
+       type_modifiers_t type_modifiers        = declaration->type->base.modifiers;
+
+       if (declaration_modifiers & DM_TRANSPARENT_UNION)
+               type_modifiers |= TYPE_MODIFIER_TRANSPARENT_UNION;
+
+       if (declaration->type->base.modifiers == type_modifiers)
+               return;
+
+       type_t *copy = duplicate_type(declaration->type);
+       copy->base.modifiers = type_modifiers;
+
+       type_t *result = typehash_insert(copy);
+       if (result != copy) {
+               obstack_free(type_obst, copy);
+       }
+
+       declaration->type = result;
+}
+
 static construct_type_t *parse_inner_declarator(declaration_t *declaration,
                bool may_be_abstract)
 {
@@ -3522,7 +3670,7 @@ static construct_type_t *parse_inner_declarator(declaration_t *declaration,
        }
 
        /* TODO: find out if this is correct */
-       parse_attributes(&attributes);
+       decl_modifiers_t modifiers = parse_attributes(&attributes);
 
        construct_type_t *inner_types = NULL;
 
@@ -3586,7 +3734,10 @@ static construct_type_t *parse_inner_declarator(declaration_t *declaration,
        }
 
 declarator_finished:
-       parse_attributes(&attributes);
+       modifiers = parse_attributes(&attributes);
+       if (declaration != NULL) {
+               declaration->modifiers |= modifiers;
+       }
 
        /* append inner_types at the end of the list, we don't to set last anymore
         * as it's not needed anymore */
@@ -3697,7 +3848,7 @@ static declaration_t *parse_declarator(
 {
        declaration_t *const declaration    = allocate_declaration_zero();
        declaration->declared_storage_class = specifiers->declared_storage_class;
-       declaration->decl_modifiers         = specifiers->decl_modifiers;
+       declaration->modifiers              = specifiers->modifiers;
        declaration->deprecated             = specifiers->deprecated;
        declaration->deprecated_string      = specifiers->deprecated_string;
        declaration->get_property_sym       = specifiers->get_property_sym;
@@ -3720,6 +3871,8 @@ static declaration_t *parse_declarator(
        type_t *const type = specifiers->type;
        declaration->type = construct_declarator_type(construct_type, type);
 
+       fix_declaration_type(declaration);
+
        if(construct_type != NULL) {
                obstack_free(&temp_obst, construct_type);
        }
@@ -3878,6 +4031,7 @@ static declaration_t *internal_record_declaration(
                                switch (old_storage_class) {
                                case STORAGE_CLASS_NONE:
                                        old_storage_class = STORAGE_CLASS_EXTERN;
+                                       /* FALLTHROUGH */
 
                                case STORAGE_CLASS_EXTERN:
                                        if (is_function_definition) {
@@ -3915,9 +4069,9 @@ warn_redundant_declaration:
                                } else if (old_storage_class != STORAGE_CLASS_EXTERN
                                                && !is_function_definition) {
                                        goto warn_redundant_declaration;
-                               } else if (new_storage_class == STORAGE_CLASS_NONE) {
-                                               previous_declaration->storage_class = STORAGE_CLASS_NONE;
-                                               previous_declaration->declared_storage_class = STORAGE_CLASS_NONE;
+                               } else if (old_storage_class == STORAGE_CLASS_EXTERN) {
+                                       previous_declaration->storage_class          = STORAGE_CLASS_NONE;
+                                       previous_declaration->declared_storage_class = STORAGE_CLASS_NONE;
                                }
                        } else if (old_storage_class == new_storage_class) {
                                errorf(&declaration->source_position,
@@ -4050,7 +4204,7 @@ static void parse_anonymous_declaration_rest(
        declaration->type                   = specifiers->type;
        declaration->declared_storage_class = specifiers->declared_storage_class;
        declaration->source_position        = specifiers->source_position;
-       declaration->decl_modifiers         = specifiers->decl_modifiers;
+       declaration->modifiers              = specifiers->modifiers;
 
        if (declaration->declared_storage_class != STORAGE_CLASS_NONE) {
                warningf(&declaration->source_position,
@@ -4428,6 +4582,10 @@ static void parse_external_declaration(void)
                assert(parameter->parent_scope == NULL
                                || parameter->parent_scope == scope);
                parameter->parent_scope = scope;
+               if (parameter->symbol == NULL) {
+                       errorf(&ndeclaration->source_position, "parameter name omitted");
+                       continue;
+               }
                environment_push(parameter);
        }
 
@@ -4527,7 +4685,7 @@ static void parse_compound_declarators(declaration_t *struct_declaration,
                        declaration->declared_storage_class = STORAGE_CLASS_NONE;
                        declaration->storage_class          = STORAGE_CLASS_NONE;
                        declaration->source_position        = source_position;
-                       declaration->decl_modifiers         = specifiers->decl_modifiers;
+                       declaration->modifiers              = specifiers->modifiers;
                        declaration->type                   = type;
                } else {
                        declaration = parse_declarator(specifiers,/*may_be_abstract=*/true);
@@ -4815,6 +4973,36 @@ static declaration_t *create_implicit_function(symbol_t *symbol,
        return declaration;
 }
 
+/**
+ * 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 *parameter2
+               = obstack_alloc(type_obst, sizeof(parameter2[0]));
+       memset(parameter2, 0, sizeof(parameter2[0]));
+       parameter2->type = argument_type2;
+
+       function_parameter_t *parameter1
+               = obstack_alloc(type_obst, sizeof(parameter1[0]));
+       memset(parameter1, 0, sizeof(parameter1[0]));
+       parameter1->type = argument_type1;
+       parameter1->next = parameter2;
+
+       type_t *type               = allocate_type_zero(TYPE_FUNCTION, &builtin_source_position);
+       type->function.return_type = return_type;
+       type->function.parameters  = parameter1;
+
+       type_t *result = typehash_insert(type);
+       if(result != type) {
+               free_type(type);
+       }
+
+       return result;
+}
+
 /**
  * Creates a return_type (func)(argument_type) function type if not
  * already exists.
@@ -4875,6 +5063,8 @@ static type_t *get_builtin_symbol_type(symbol_t *symbol)
                return make_function_1_type(type_long_double, type_char_ptr);
        case T___builtin_va_end:
                return make_function_1_type(type_void, type_valist);
+       case T___builtin_expect:
+               return make_function_2_type(type_long, type_long, type_long);
        default:
                internal_errorf(HERE, "not implemented builtin symbol found");
        }
@@ -5464,6 +5654,7 @@ end_error:
        return create_invalid_expression();
 }
 
+#if 0
 /**
  * Parses a __builtin_expect() expression.
  */
@@ -5486,6 +5677,7 @@ static expression_t *parse_builtin_expect(void)
 end_error:
        return create_invalid_expression();
 }
+#endif
 
 /**
  * Parses a MS assume() expression.
@@ -5568,7 +5760,7 @@ static expression_t *parse_primary_expression(void)
                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_expect:         return parse_builtin_expect();
+               case T___builtin_expect:
                case T___builtin_alloca:
                case T___builtin_nan:
                case T___builtin_nand:
@@ -5803,6 +5995,50 @@ static expression_t *parse_select_expression(unsigned precedence,
        return select;
 }
 
+static void check_call_argument(const function_parameter_t *parameter,
+                                call_argument_t *argument)
+{
+       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;
+
+       /* handle transparent union gnu extension */
+       if (is_type_union(expected_type_skip)
+                       && (expected_type_skip->base.modifiers
+                               & TYPE_MODIFIER_TRANSPARENT_UNION)) {
+               declaration_t  *union_decl = expected_type_skip->compound.declaration;
+
+               declaration_t *declaration = union_decl->scope.declarations;
+               type_t        *best_type   = NULL;
+               for ( ; declaration != NULL; declaration = declaration->next) {
+                       type_t *decl_type = declaration->type;
+                       error = semantic_assign(decl_type, arg_expr);
+                       if (error == ASSIGN_ERROR_INCOMPATIBLE
+                               || error == ASSIGN_ERROR_POINTER_QUALIFIER_MISSING)
+                               continue;
+
+                       if (error == ASSIGN_SUCCESS) {
+                               best_type = decl_type;
+                       } else if (best_type == NULL) {
+                               best_type = decl_type;
+                       }
+               }
+
+               if (best_type != NULL) {
+                       expected_type = best_type;
+               }
+       }
+
+       error                = semantic_assign(expected_type, arg_expr);
+       argument->expression = create_implicit_cast(argument->expression,
+                                                   expected_type);
+
+       /* TODO report exact scope in error messages (like "in 3rd parameter") */
+       report_assign_error(error, expected_type, arg_expr,     "function call",
+                           &arg_expr->base.source_position);
+}
+
 /**
  * Parse a call expression, ie. expression '( ... )'.
  *
@@ -5871,20 +6107,7 @@ static expression_t *parse_call_expression(unsigned precedence,
        if (!function_type->unspecified_parameters) {
                for( ; parameter != NULL && argument != NULL;
                                parameter = parameter->next, argument = argument->next) {
-                       type_t *expected_type = parameter->type;
-                       /* TODO report scope in error messages */
-                       expression_t *const arg_expr = argument->expression;
-                       type_t       *const res_type = semantic_assign(expected_type, arg_expr,
-                                                                                                                  "function call",
-                                                                                                                  &arg_expr->base.source_position);
-                       if (res_type == NULL) {
-                               /* TODO improve error message */
-                               errorf(&arg_expr->base.source_position,
-                                       "Cannot call function with argument '%E' of type '%T' where type '%T' is expected",
-                                       arg_expr, arg_expr->base.type, expected_type);
-                       } else {
-                               argument->expression = create_implicit_cast(argument->expression, expected_type);
-                       }
+                       check_call_argument(parameter, argument);
                }
 
                if (parameter != NULL) {
@@ -5992,12 +6215,33 @@ static expression_t *parse_conditional_expression(unsigned precedence,
                        other_expression = true_expression;
                }
 
-               if(is_type_pointer(other_type)) {
-                       if(!pointers_compatible(true_type, false_type)) {
+               /* TODO Treat (void*)0 as null pointer constant */
+               if (is_type_pointer(other_type)) {
+                       type_t *to1 = skip_typeref(pointer_type->pointer.points_to);
+                       type_t *to2 = skip_typeref(other_type->pointer.points_to);
+
+                       type_t *to;
+                       if (is_type_atomic(to1, ATOMIC_TYPE_VOID) ||
+                           is_type_atomic(to2, ATOMIC_TYPE_VOID)) {
+                               to = type_void;
+                       } else if (types_compatible(get_unqualified_type(to1),
+                                                   get_unqualified_type(to2))) {
+                               to = to1;
+                       } else {
                                warningf(&expression->base.source_position,
-                                               "pointer types '%T' and '%T' in conditional expression are incompatible", true_type, false_type);
+                                       "pointer types '%T' and '%T' in conditional expression are incompatible",
+                                       true_type, false_type);
+                               to = type_void;
                        }
-                       result_type = true_type;
+
+                       type_t *const copy = duplicate_type(to);
+                       copy->base.qualifiers = to1->base.qualifiers | to2->base.qualifiers;
+
+                       type_t *const type = typehash_insert(copy);
+                       if (type != copy)
+                               free_type(copy);
+
+                       result_type = make_pointer_type(type, TYPE_QUALIFIER_NONE);
                } else if(is_null_pointer_constant(other_expression)) {
                        result_type = pointer_type;
                } else if(is_type_integer(other_type)) {
@@ -6158,6 +6402,25 @@ static void semantic_dereference(unary_expression_t *expression)
        expression->base.type = result_type;
 }
 
+static void set_address_taken(expression_t *expression, bool may_be_register)
+{
+       if(expression->kind != EXPR_REFERENCE)
+               return;
+
+       declaration_t *const declaration = expression->reference.declaration;
+       /* happens for parse errors */
+       if(declaration == NULL)
+               return;
+
+       if (declaration->storage_class == STORAGE_CLASS_REGISTER && !may_be_register) {
+               errorf(&expression->base.source_position,
+                               "address of register variable '%Y' requested",
+                               declaration->symbol);
+       } else {
+               declaration->address_taken = 1;
+       }
+}
+
 /**
  * Check the semantic of the address taken expression.
  */
@@ -6170,17 +6433,7 @@ static void semantic_take_addr(unary_expression_t *expression)
        if(!is_type_valid(orig_type))
                return;
 
-       if(value->kind == EXPR_REFERENCE) {
-               declaration_t *const declaration = value->reference.declaration;
-               if(declaration != NULL) {
-                       if (declaration->storage_class == STORAGE_CLASS_REGISTER) {
-                               errorf(&expression->base.source_position,
-                                      "address of register variable '%Y' requested",
-                                      declaration->symbol);
-                       }
-                       declaration->address_taken = 1;
-               }
-       }
+       set_address_taken(value, false);
 
        expression->base.type = make_pointer_type(orig_type, TYPE_QUALIFIER_NONE);
 }
@@ -6382,15 +6635,25 @@ static void semantic_sub(binary_expression_t *expression)
                                         type_left, orig_type_left);
                expression->base.type = type_left;
        } else if(is_type_pointer(type_left) && is_type_pointer(type_right)) {
-               if(!pointers_compatible(type_left, type_right)) {
-                       errorf(HERE,
-                              "pointers to incompatible objects to binary '-' ('%T', '%T')",
+               type_t *const unqual_left  = get_unqualified_type(skip_typeref(type_left->pointer.points_to));
+               type_t *const unqual_right = get_unqualified_type(skip_typeref(type_right->pointer.points_to));
+               if (!types_compatible(unqual_left, unqual_right)) {
+                       errorf(&expression->base.source_position,
+                              "subtracting pointers to incompatible types '%T' and '%T'",
                               orig_type_left, orig_type_right);
-               } else {
-                       expression->base.type = type_ptrdiff_t;
+               } else if (!is_type_object(unqual_left)) {
+                       if (is_type_atomic(unqual_left, ATOMIC_TYPE_VOID)) {
+                               warningf(&expression->base.source_position,
+                                        "subtracting pointers to void");
+                       } else {
+                               errorf(&expression->base.source_position,
+                                      "subtracting pointers to non-object types '%T'",
+                                      orig_type_left);
+                       }
                }
+               expression->base.type = type_ptrdiff_t;
        } else if (is_type_valid(type_left) && is_type_valid(type_right)) {
-               errorf(HERE, "invalid operands to binary '-' ('%T', '%T')",
+               errorf(HERE, "invalid operands of types '%T' and '%T' to binary '-'",
                       orig_type_left, orig_type_right);
        }
 }
@@ -6631,16 +6894,10 @@ static void semantic_binexpr_assign(binary_expression_t *expression)
        if (!is_valid_assignment_lhs(left))
                return;
 
-       type_t *const res_type = semantic_assign(orig_type_left, expression->right,
+       assign_error_t error = semantic_assign(orig_type_left, expression->right);
+       report_assign_error(error, orig_type_left, expression->right,
                        "assignment", &left->base.source_position);
-       if (res_type == NULL) {
-               errorf(&expression->base.source_position,
-                       "cannot assign to '%T' from '%T'",
-                       orig_type_left, expression->right->base.type);
-       } else {
-               expression->right = create_implicit_cast(expression->right, res_type);
-       }
-
+       expression->right = create_implicit_cast(expression->right, orig_type_left);
        expression->base.type = orig_type_left;
 }
 
@@ -7029,13 +7286,13 @@ static asm_argument_t *parse_asm_arguments(bool is_out)
        asm_argument_t *result = NULL;
        asm_argument_t *last   = NULL;
 
-       while(token.type == T_STRING_LITERAL || token.type == '[') {
+       while (token.type == T_STRING_LITERAL || token.type == '[') {
                asm_argument_t *argument = allocate_ast_zero(sizeof(argument[0]));
                memset(argument, 0, sizeof(argument[0]));
 
-               if(token.type == '[') {
+               if (token.type == '[') {
                        eat('[');
-                       if(token.type != T_IDENTIFIER) {
+                       if (token.type != T_IDENTIFIER) {
                                parse_error_expected("while parsing asm argument",
                                                     T_IDENTIFIER, NULL);
                                return NULL;
@@ -7047,21 +7304,24 @@ static asm_argument_t *parse_asm_arguments(bool is_out)
 
                argument->constraints = parse_string_literals();
                expect('(');
-               argument->expression = parse_expression();
-               if (is_out && !is_lvalue(argument->expression)) {
-                       errorf(&argument->expression->base.source_position,
+               expression_t *expression = parse_expression();
+               argument->expression     = expression;
+               if (is_out && !is_lvalue(expression)) {
+                       errorf(&expression->base.source_position,
                               "asm output argument is not an lvalue");
                }
                expect(')');
 
-               if(last != NULL) {
+               set_address_taken(expression, true);
+
+               if (last != NULL) {
                        last->next = argument;
                } else {
                        result = argument;
                }
                last = argument;
 
-               if(token.type != ',')
+               if (token.type != ',')
                        break;
                eat(',');
        }
@@ -7723,15 +7983,10 @@ static statement_t *parse_return(void)
                                 "'return' with a value, in function returning void");
                        return_value = NULL;
                } else {
-                       type_t *const res_type = semantic_assign(return_type,
-                               return_value, "'return'", &statement->base.source_position);
-                       if (res_type == NULL) {
-                               errorf(&statement->base.source_position,
-                                      "cannot return something of type '%T' in function returning '%T'",
-                                      return_value->base.type, return_type);
-                       } else {
-                               return_value = create_implicit_cast(return_value, res_type);
-                       }
+                       assign_error_t error = semantic_assign(return_type, return_value);
+                       report_assign_error(error, return_type, return_value, "'return'",
+                                           &statement->base.source_position);
+                       return_value = create_implicit_cast(return_value, return_type);
                }
                /* check for returning address of a local var */
                if (return_value != NULL &&