From 7698bae699639892d639b8a9c270efdb8c46725c Mon Sep 17 00:00:00 2001 From: John Keeping Date: Sun, 9 Mar 2014 11:32:51 +0000 Subject: [PATCH] Support GCC's transparent unions This stops warnings in code using socket operations with a modern glibc, which otherwise result in warnings of the form: warning: incorrect type in argument 2 (invalid types) expected union __CONST_SOCKADDR_ARG [usertype] __addr got struct sockaddr * Since transparent unions are only applicable to function arguments, we create a new function to check that the types are compatible specifically in this context. Also change the wording of the existing warning slightly since sparse does now support them. The warning is left in case people want to avoid using transparent unions. Signed-off-by: John Keeping Signed-off-by: Christopher Li --- evaluate.c | 28 +++++++++++++++++++++++++++- parse.c | 7 ++++++- symbol.h | 3 ++- validation/transparent-union.c | 25 +++++++++++++++++++++++++ 4 files changed, 60 insertions(+), 3 deletions(-) create mode 100644 validation/transparent-union.c diff --git a/evaluate.c b/evaluate.c index 8f6c07ee..98fc52b8 100644 --- a/evaluate.c +++ b/evaluate.c @@ -1406,6 +1406,32 @@ static int compatible_assignment_types(struct expression *expr, struct symbol *t return 1; } +static int compatible_transparent_union(struct symbol *target, + struct expression **rp) +{ + struct symbol *t, *member; + classify_type(target, &t); + if (t->type != SYM_UNION || !t->transparent_union) + return 0; + + FOR_EACH_PTR(t->symbol_list, member) { + const char *typediff; + if (check_assignment_types(member, rp, &typediff)) + return 1; + } END_FOR_EACH_PTR(member); + + return 0; +} + +static int compatible_argument_type(struct expression *expr, struct symbol *target, + struct expression **rp, const char *where) +{ + if (compatible_transparent_union(target, rp)) + return 1; + + return compatible_assignment_types(expr, target, rp, where); +} + static void mark_assigned(struct expression *expr) { struct symbol *sym; @@ -2173,7 +2199,7 @@ static int evaluate_arguments(struct symbol *f, struct symbol *fn, struct expres static char where[30]; examine_symbol_type(target); sprintf(where, "argument %d", i); - compatible_assignment_types(expr, target, p, where); + compatible_argument_type(expr, target, p, where); } i++; diff --git a/parse.c b/parse.c index 9cc5f656..785630ad 100644 --- a/parse.c +++ b/parse.c @@ -1208,7 +1208,12 @@ static struct token *attribute_designated_init(struct token *token, struct symbo static struct token *attribute_transparent_union(struct token *token, struct symbol *attr, struct decl_state *ctx) { if (Wtransparent_union) - warning(token->pos, "ignoring attribute __transparent_union__"); + warning(token->pos, "attribute __transparent_union__"); + + if (ctx->ctype.base_type && ctx->ctype.base_type->type == SYM_UNION) + ctx->ctype.base_type->transparent_union = 1; + else + warning(token->pos, "attribute __transparent_union__ applied to non-union type"); return token; } diff --git a/symbol.h b/symbol.h index 43c165be..ccb5dcb9 100644 --- a/symbol.h +++ b/symbol.h @@ -174,7 +174,8 @@ struct symbol { evaluated:1, string:1, designated_init:1, - forced_arg:1; + forced_arg:1, + transparent_union:1; struct expression *array_size; struct ctype ctype; struct symbol_list *arguments; diff --git a/validation/transparent-union.c b/validation/transparent-union.c new file mode 100644 index 00000000..149c7d94 --- /dev/null +++ b/validation/transparent-union.c @@ -0,0 +1,25 @@ +struct a { + int field; +}; +struct b { + int field; +}; + +typedef union { + struct a *a; + struct b *b; +} transparent_arg __attribute__((__transparent_union__)); + +static void foo(transparent_arg arg) +{ +} + +static void bar(void) +{ + struct b arg = { 0 }; + foo((struct a *) &arg); +} + +/* + * check-name: Transparent union attribute. + */ -- 2.11.4.GIT