on a structure with a C99 flexible array member being nested in another structure.
"The GCC extension accepts a structure containing an ISO C99 "flexible array member", or a union containing such a structure (possibly recursively) to be a member of a structure. There are two situations: * A structure containing a C99 flexible array member, or a union containing such a structure, is the last field of another structure, for example: struct flex { int length; char data[]; }; union union_flex { int others; struct flex f; }; struct out_flex_struct { int m; struct flex flex_data; }; struct out_flex_union { int n; union union_flex flex_data; }; In the above, both 'out_flex_struct.flex_data.data[]' and 'out_flex_union.flex_data.f.data[]' are considered as flexible arrays too. * A structure containing a C99 flexible array member, or a union containing such a structure, is not the last field of another structure, for example: struct flex { int length; char data[]; }; struct mid_flex { int m; struct flex flex_data; int n; }; In the above, accessing a member of the array 'mid_flex.flex_data.data[]' might have undefined behavior. Compilers do not handle such a case consistently, Any code relying on this case should be modified to ensure that flexible array members only end up at the ends of structures. Please use the warning option '-Wflex-array-member-not-at-end' to identify all such cases in the source code and modify them. This extension is now deprecated. " PR C/77650 gcc/c-family/ChangeLog: * c.opt: New option -Wflex-array-member-not-at-end. gcc/c/ChangeLog: * c-decl.cc (finish_struct): Issue warnings for new option. gcc/ChangeLog: * doc/extend.texi: Document GCC extension on a structure containing a flexible array member to be a member of another structure. gcc/testsuite/ChangeLog: * gcc.dg/variable-sized-type-flex-array.c: New test. --- gcc/c-family/c.opt | 5 +++ gcc/c/c-decl.cc | 9 ++++ gcc/doc/extend.texi | 44 ++++++++++++++++++- .../gcc.dg/variable-sized-type-flex-array.c | 31 +++++++++++++ 4 files changed, 88 insertions(+), 1 deletion(-) create mode 100644 gcc/testsuite/gcc.dg/variable-sized-type-flex-array.c diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt index cead1995561..fce66b1d225 100644 --- a/gcc/c-family/c.opt +++ b/gcc/c-family/c.opt @@ -737,6 +737,11 @@ Wformat-truncation= C ObjC C++ LTO ObjC++ Joined RejectNegative UInteger Var(warn_format_trunc) Warning LangEnabledBy(C ObjC C++ LTO ObjC++,Wformat=, warn_format >= 1, 0) IntegerRange(0, 2) Warn about calls to snprintf and similar functions that truncate output. +Wflex-array-member-not-at-end +C C++ Var(warn_flex_array_member_not_at_end) Warning +Warn when a structure containing a C99 flexible array member as the last +field is not at the end of another structure. + Wif-not-aligned C ObjC C++ ObjC++ Var(warn_if_not_aligned) Init(1) Warning Warn when the field in a struct is not aligned. diff --git a/gcc/c/c-decl.cc b/gcc/c/c-decl.cc index e14f514cb6e..ecd10ebb69c 100644 --- a/gcc/c/c-decl.cc +++ b/gcc/c/c-decl.cc @@ -9278,6 +9278,15 @@ finish_struct (location_t loc, tree t, tree fieldlist, tree attributes, TYPE_INCLUDES_FLEXARRAY (t) = is_last_field && TYPE_INCLUDES_FLEXARRAY (TREE_TYPE (x)); + if (warn_flex_array_member_not_at_end + && !is_last_field + && RECORD_OR_UNION_TYPE_P (TREE_TYPE (x)) + && TYPE_INCLUDES_FLEXARRAY (TREE_TYPE (x))) + warning_at (DECL_SOURCE_LOCATION (x), + OPT_Wflex_array_member_not_at_end, + "structure containing a flexible array member" + " is not at the end of another structure"); + if (DECL_NAME (x) || RECORD_OR_UNION_TYPE_P (TREE_TYPE (x))) saw_named_field = true; diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi index cdbd4b34a35..fbb1d9708ba 100644 --- a/gcc/doc/extend.texi +++ b/gcc/doc/extend.texi @@ -1751,7 +1751,49 @@ Flexible array members may only appear as the last member of a A structure containing a flexible array member, or a union containing such a structure (possibly recursively), may not be a member of a structure or an element of an array. (However, these uses are -permitted by GCC as extensions.) +permitted by GCC as extensions, see details below.) +@end itemize + +The GCC extension accepts a structure containing an ISO C99 @dfn{flexible array +member}, or a union containing such a structure (possibly recursively) +to be a member of a structure. + +There are two situations: + +@itemize @bullet +@item +A structure containing a C99 flexible array member, or a union containing +such a structure, is the last field of another structure, for example: + +@smallexample +struct flex @{ int length; char data[]; @}; +union union_flex @{ int others; struct flex f; @}; + +struct out_flex_struct @{ int m; struct flex flex_data; @}; +struct out_flex_union @{ int n; union union_flex flex_data; @}; +@end smallexample + +In the above, both @code{out_flex_struct.flex_data.data[]} and +@code{out_flex_union.flex_data.f.data[]} are considered as flexible arrays too. + +@item +A structure containing a C99 flexible array member, or a union containing +such a structure, is not the last field of another structure, for example: + +@smallexample +struct flex @{ int length; char data[]; @}; + +struct mid_flex @{ int m; struct flex flex_data; int n; @}; +@end smallexample + +In the above, accessing a member of the array @code{mid_flex.flex_data.data[]} +might have undefined behavior. Compilers do not handle such a case +consistently. Any code relying on this case should be modified to ensure +that flexible array members only end up at the ends of structures. + +Please use the warning option @option{-Wflex-array-member-not-at-end} to +identify all such cases in the source code and modify them. This extension +is now deprecated. @end itemize Non-empty initialization of zero-length diff --git a/gcc/testsuite/gcc.dg/variable-sized-type-flex-array.c b/gcc/testsuite/gcc.dg/variable-sized-type-flex-array.c new file mode 100644 index 00000000000..3924937bad4 --- /dev/null +++ b/gcc/testsuite/gcc.dg/variable-sized-type-flex-array.c @@ -0,0 +1,31 @@ +/* Test for -Wflex-array-member-not-at-end on structure/union with + C99 flexible array members being embedded into another structure. */ +/* { dg-do compile } */ +/* { dg-options "-Wflex-array-member-not-at-end" } */ + +struct flex { int n; int data[]; }; +struct out_flex_end { int m; struct flex flex_data; }; /* { dg-bogus "structure containing a flexible array member is not at the end of another structure" } */ +struct out_flex_mid { struct flex flex_data; int m; }; /* { dg-warning "structure containing a flexible array member is not at the end of another structure" } */ +/* since the warning has been issued for out_flex_mid, no need to + issue warning again when it is included in another structure/union. */ +struct outer_flex_mid { struct out_flex_mid out_flex_data; int p; }; /* { dg-bogus "structure containing a flexible array member is not at the end of another structure" } */ +union flex_union_mid { int a; struct outer_flex_mid b; }; /* { dg-bogus "structure containing a flexible array member is not at the end of another structure" } */ + + +struct flex0 { int n; int data[0]; }; +struct out_flex_end0 { int m; struct flex0 flex_data; }; /* { dg-bogus "structure containing a flexible array member is not at the end of another structure" } */ +struct out_flex_mid0 { struct flex0 flex_data; int m; }; /* { dg-bogus "structure containing a flexible array member is not at the end of another structure" } */ +struct outer_flex_mid0 { struct out_flex_mid0 out_flex_data; int p; }; /* { dg-bogus "structure containing a flexible array member is not at the end of another structure" } */ +union flex_union_mid0 { int a; struct outer_flex_mid0 b; }; /* { dg-bogus "structure containing a flexible array member is not at the end of another structure" } */ + +struct flex1 { int n; int data[1]; }; +struct out_flex_end1 { int m; struct flex1 flex_data; }; /* { dg-bogus "structure containing a flexible array member is not at the end of another structure" } */ +struct out_flex_mid1 { struct flex1 flex_data; int m; }; /* { dg-bogus "structure containing a flexible array member is not at the end of another structure" } */ +struct outer_flex_mid1 { struct out_flex_mid1 out_flex_data; int p; }; /* { dg-bogus "structure containing a flexible array member is not at the end of another structure" } */ +union flex_union_mid1 { int a; struct outer_flex_mid1 b; }; /* { dg-bogus "structure containing a flexible array member is not at the end of another structure" } */ + +struct flexn { int n; int data[8]; }; +struct out_flex_endn { int m; struct flexn flex_data; }; /* { dg-bogus "structure containing a flexible array member is not at the end of another structure" } */ +struct out_flex_midn { struct flexn flex_data; int m; }; /* { dg-bogus"structure containing a flexible array member is not at the end of another structure" } */ +struct outer_flex_midn { struct out_flex_midn out_flex_data; int p; }; /* { dg-bogus"structure containing a flexible array member is not at the end of another structure" } */ +union flex_union_midn { int a; struct outer_flex_midn b; }; /* { dg-bogus "structure containing a flexible array member is not at the end of another structure" } */ -- 2.31.1