on a structure with a C99 flexible array member being nested in
another structure. (PR77650)

"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 or a union with a C99 flexible array member 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 or a union with a C99 flexible array member is the
     middle 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, 'mid_flex.flex_data.data[]' has undefined behavior.
     Compilers do not handle such case consistently, Any code relying on
     such case should be modified to ensure that flexible array members
     only end up at the ends of structures.

     Please use warning option '-Wgnu-variable-sized-type-not-at-end' to
     identify all such cases in the source code and modify them.  This
     extension will be deprecated from gcc in the next release.
"

gcc/c-family/ChangeLog:

        * c.opt: New option -Wgnu-variable-sized-type-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                               |  8 ++++
 gcc/doc/extend.texi                           | 45 ++++++++++++++++++-
 .../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 3333cddeece..660ac07f3d4 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.
 
+Wgnu-variable-sized-type-not-at-end
+C C++ Var(warn_variable_sized_type_not_at_end) Warning
+Warn about structures or unions with C99 flexible array members are not
+at the end of a 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 14c54809b9d..1632043a8ff 100644
--- a/gcc/c/c-decl.cc
+++ b/gcc/c/c-decl.cc
@@ -9269,6 +9269,14 @@ finish_struct (location_t loc, tree t, tree fieldlist, 
tree attributes,
        TYPE_INCLUDE_FLEXARRAY (t)
          = is_last_field && TYPE_INCLUDE_FLEXARRAY (TREE_TYPE (x));
 
+      if (warn_variable_sized_type_not_at_end
+         && !is_last_field
+         && RECORD_OR_UNION_TYPE_P (TREE_TYPE (x))
+         && TYPE_INCLUDE_FLEXARRAY (TREE_TYPE (x)))
+       warning_at (DECL_SOURCE_LOCATION (x),
+                   OPT_Wgnu_variable_sized_type_not_at_end,
+                   "variable sized type not at the end of a struct");
+
       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 fd3745c5608..0928b962a60 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -1748,7 +1748,50 @@ 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
+
+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 or a union with a C99 flexible array member 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 or a union with a C99 flexible array member is the middle 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, @code{mid_flex.flex_data.data[]} has undefined behavior.
+Compilers do not handle such case consistently, Any code relying on
+such case should be modified to ensure that flexible array members
+only end up at the ends of structures.
+
+Please use warning option  @option{-Wgnu-variable-sized-type-not-at-end} to
+identify all such cases in the source code and modify them.  This extension
+will be deprecated from gcc in the next release.
 @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..e3f65c5ed07
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/variable-sized-type-flex-array.c
@@ -0,0 +1,31 @@
+/* Test for -Wgnu-variable-sized-type-not-at-end on structure/union with 
+   C99 flexible array members being embedded into another structure.  */
+/* { dg-do compile } */
+/* { dg-options "-Wgnu-variable-sized-type-not-at-end" } */
+
+struct flex { int n; int data[]; };
+struct out_flex_end { int m; struct flex flex_data; }; /* { dg-bogus "variable 
sized type not at the end of a struct" } */
+struct out_flex_mid { struct flex flex_data; int m; };  /* { dg-warning 
"variable sized type not at the end of a struct" } */
+/* 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 "variable sized type not at the end of a struct" } */
+union flex_union_mid { int a; struct outer_flex_mid b; }; /* { dg-bogus 
"variable sized type not at the end of a struct" } */
+
+
+struct flex0 { int n; int data[0]; };
+struct out_flex_end0 { int m; struct flex0 flex_data; }; /* { dg-bogus 
"variable sized type not at the end of a struct" } */
+struct out_flex_mid0 { struct flex0 flex_data; int m; };  /* { dg-bogus 
"variable sized type not at the end of a struct" } */
+struct outer_flex_mid0 { struct out_flex_mid0 out_flex_data; int p; }; /* { 
dg-bogus "variable sized type not at the end of a struct" } */
+union flex_union_mid0 { int a; struct outer_flex_mid0 b; }; /* { dg-bogus 
"variable sized type not at the end of a struct" } */
+
+struct flex1 { int n; int data[1]; };
+struct out_flex_end1 { int m; struct flex1 flex_data; }; /* { dg-bogus 
"variable sized type not at the end of a struct" } */
+struct out_flex_mid1 { struct flex1 flex_data; int m; }; /* { dg-bogus 
"variable sized type not at the end of a struct" } */ 
+struct outer_flex_mid1 { struct out_flex_mid1 out_flex_data; int p; }; /* { 
dg-bogus "variable sized type not at the end of a struct" } */
+union flex_union_mid1 { int a; struct outer_flex_mid1 b; }; /* { dg-bogus 
"variable sized type not at the end of a struct" } */
+
+struct flexn { int n; int data[8]; }; 
+struct out_flex_endn { int m; struct flexn flex_data; }; /* { dg-bogus 
"variable sized type not at the end of a struct" } */
+struct out_flex_midn { struct flexn flex_data; int m; }; /* { 
dg-bogus"variable sized type not at the end of a struct" } */ 
+struct outer_flex_midn { struct out_flex_midn out_flex_data; int p; }; /* { 
dg-bogus"variable sized type not at the end of a struct" } */
+union flex_union_midn { int a; struct outer_flex_midn b; }; /* { dg-bogus 
"variable sized type not at the end of a struct" } */
-- 
2.31.1

Reply via email to