Casts to union type are a GCC extension and are similar to compound literals. However, sparse doesn't know about them and treats them like other casts to non-scalars. So, teach sparse about them, convert them to the corresponding compound literal and add a warning flag to enable/disable the associated warning: -W[no-]union-cast. Note: a difference between union casts and compound literals is that the union casts yield rvalues while compound literals are lvalues but this distinction is not yet done in this series. Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@xxxxxxxxx> --- Documentation/release-notes/v0.6.3.rst | 5 +++ evaluate.c | 47 +++++++++++++++++++++++++- options.c | 2 ++ options.h | 1 + sparse.1 | 6 ++++ validation/eval/union-cast-no.c | 1 - validation/eval/union-cast.c | 1 - 7 files changed, 60 insertions(+), 3 deletions(-) diff --git a/Documentation/release-notes/v0.6.3.rst b/Documentation/release-notes/v0.6.3.rst new file mode 100644 index 000000000000..1aae742e42de --- /dev/null +++ b/Documentation/release-notes/v0.6.3.rst @@ -0,0 +1,5 @@ +v0.6.3 (2020-xx-xy) +=================== + +* Changes in warnings: + "warning: cast to union type" [disable with -Wno-union-cast] diff --git a/evaluate.c b/evaluate.c index 0563be939f23..847137250d05 100644 --- a/evaluate.c +++ b/evaluate.c @@ -2948,6 +2948,26 @@ static int cast_flags(struct expression *expr, struct expression *old) return flags; } +/// +// check if a type matches one of the members of a union type +// @utype: the union type +// @type: to type to check +// @return: to identifier of the matching type in the union. +static struct symbol *find_member_type(struct symbol *utype, struct symbol *type) +{ + struct symbol *t, *member; + + if (utype->type != SYM_UNION) + return NULL; + + FOR_EACH_PTR(utype->symbol_list, member) { + classify_type(member, &t); + if (type == t) + return member; + } END_FOR_EACH_PTR(member); + return NULL; +} + static struct symbol *evaluate_compound_literal(struct expression *expr, struct expression *source) { struct expression *addr = alloc_expression(expr->pos, EXPR_SYMBOL); @@ -2973,6 +2993,7 @@ static struct symbol *evaluate_cast(struct expression *expr) struct expression *source = expr->cast_expression; struct symbol *ctype; struct symbol *ttype, *stype; + struct symbol *member; int tclass, sclass; struct ident *tas = NULL, *sas = NULL; @@ -3022,8 +3043,32 @@ static struct symbol *evaluate_cast(struct expression *expr) if (expr->type == EXPR_FORCE_CAST) goto out; - if (tclass & (TYPE_COMPOUND | TYPE_FN)) + if (tclass & (TYPE_COMPOUND | TYPE_FN)) { + /* + * Special case: cast to union type (GCC extension) + * The effect is similar to a compound literal except + * that the result is a rvalue. + */ + if ((member = find_member_type(ttype, stype))) { + struct expression *item, *init; + + if (Wunion_cast) + warning(expr->pos, "cast to union type"); + + item = alloc_expression(source->pos, EXPR_IDENTIFIER); + item->expr_ident = member->ident; + item->ident_expression = source; + + init = alloc_expression(source->pos, EXPR_INITIALIZER); + add_expression(&init->expr_list, item); + + // FIXME: this should be a rvalue + evaluate_compound_literal(expr, init); + return ctype; + } + warning(expr->pos, "cast to non-scalar"); + } if (sclass & TYPE_COMPOUND) warning(expr->pos, "cast from non-scalar"); diff --git a/options.c b/options.c index f7e81b84d749..b25f715042f8 100644 --- a/options.c +++ b/options.c @@ -129,6 +129,7 @@ int Wtransparent_union = 0; int Wtypesign = 0; int Wundef = 0; int Wuninitialized = 1; +int Wunion_cast = 0; int Wuniversal_initializer = 0; int Wunknown_attribute = 0; int Wvla = 1; @@ -866,6 +867,7 @@ static const struct flag warnings[] = { { "typesign", &Wtypesign }, { "undef", &Wundef }, { "uninitialized", &Wuninitialized }, + { "union-cast", &Wunion_cast }, { "universal-initializer", &Wuniversal_initializer }, { "unknown-attribute", &Wunknown_attribute }, { "vla", &Wvla }, diff --git a/options.h b/options.h index 070c0dd87183..40945e287a4a 100644 --- a/options.h +++ b/options.h @@ -128,6 +128,7 @@ extern int Wtransparent_union; extern int Wtypesign; extern int Wundef; extern int Wuninitialized; +extern int Wunion_cast; extern int Wuniversal_initializer; extern int Wunknown_attribute; extern int Wvla; diff --git a/sparse.1 b/sparse.1 index 60203d5a6463..fecd6dd90d42 100644 --- a/sparse.1 +++ b/sparse.1 @@ -451,6 +451,12 @@ The concerned warnings are, for example, those triggered by Sparse does not issue these warnings by default, processing '{\ 0\ }' the same as '{\ }'. . +.TP +.B -Wunion-cast +Warn on casts to union types. + +Sparse does not issues these warnings by default. +. .SH MISC OPTIONS .TP .B \-\-arch=\fIARCH\fR diff --git a/validation/eval/union-cast-no.c b/validation/eval/union-cast-no.c index d06b348d209e..6ba38db8e3f3 100644 --- a/validation/eval/union-cast-no.c +++ b/validation/eval/union-cast-no.c @@ -16,7 +16,6 @@ static union u bar(long l) /* * check-name: union-cast-no * check-command: sparse -Wno-union-cast $file - * check-known-to-fail * * check-error-start eval/union-cast-no.c:13:17: warning: cast to non-scalar diff --git a/validation/eval/union-cast.c b/validation/eval/union-cast.c index 1d8167531081..5bee9e0dc705 100644 --- a/validation/eval/union-cast.c +++ b/validation/eval/union-cast.c @@ -16,7 +16,6 @@ static union u bar(long a) /* * check-name: union-cast * check-command: sparse -Wunion-cast $file - * check-known-to-fail * * check-error-start eval/union-cast.c:8:17: warning: cast to union type -- 2.28.0