diff mbox

c++/42121 - diagnose invalid flexible array members

Message ID 5650ED6B.2040404@gmail.com
State New
Headers show

Commit Message

Martin Sebor Nov. 21, 2015, 10:17 p.m. UTC
Bug 42121 - g++ should warn or error on internal 0 size array in
struct, is a request to diagnose declarations of flexible array
members that aren't last in the enclosing struct, such as in the
following:

     struct S
     {
         int a;
         char b[];   // invalid
         int c;
     };

The C front end diagnoses such cases because they are invalid in
standard C.  Comment 8 on the bug points out that flexible array
members should not be treated identically to zero-size arrays
(they're not in C).

The attached patch implements the requested diagnostic, keeping
comment 8 in mind.  It also issues a diagnostic for flexible array
members in unions (which are also diagnosed as invalid in C mode).
The patch found a number of instances of invalid flexible array
members in the C++ test suites.  I corrected them.

Since the C++ front end doesn't distinguish between flexible array
members and zero-size arrays (both are considered to have an upper
bound of SIZE_MAX), and since determining whether or not
a declaration of such a member is valid cannot be done until
the whole containing struct has been processed, the patch makes
use one of the DECL_LANG_FLAGs to temporarily remember which is
which (I somewhat arbitrarily picked DECL_LANG_FLAG_1), before
clearing it. There might be a better flag to use, and it might
be appropriate to define a descriptive macro for this purpose
in cp-tree.h, along the same lines as the macros already defined
for other such purposes.

Martin
diff mbox

Patch

gcc/
2015-11-20  Martin Sebor  <msebor@redhat.com>

	PR c++/42121
	* c/c-decl.c (grokdeclarator): Mention type size in a diagnostic.
	(finish_struct): Same.

gcc/cp/
2015-11-20  Martin Sebor  <msebor@redhat.com>

	PR c++/42121
	* cp/class.c (layout_class_type): Mention type size in a diagnostic.
	(all_bases_empty_p, field_nonempty_p, check_flexarrays): New helper
	functions.
	(finish_struct_1): Call check_flexarrays.
	* cp/decl.c (compute_array_index_type): Add argument. Enhance
	diagnostic.
	(grokdeclarator): Reject flexible array members in unions.
	Distinguish flexible array members from zero-size arrays.

gcc/testsuite/
2015-11-20  Martin Sebor  <msebor@redhat.com>

	PR c++/42121
	* g++.dg/ext/flexary2.C: Adjust and enhance.
	* g++.dg/ext/flexary3.C: Adjust.
	* g++.dg/ext/flexary3.C: New test.
	* g++.dg/torture/pr64280.C: Adjust.
	* g++.dg/torture/pr64312.C: Adjust.
	* g++.dg/parse/pr43765.C: Adjust.
	* g++.dg/torture/pr64280.C: Adjust.

diff --git a/gcc/c/c-decl.c b/gcc/c/c-decl.c
index 7b9ab8a..e55471f 100644
--- a/gcc/c/c-decl.c
+++ b/gcc/c/c-decl.c
@@ -5868,10 +5868,12 @@  grokdeclarator (const struct c_declarator *declarator,
 			&& !int_fits_type_p (size, index_type))
 		      {
 			if (name)
-			  error_at (loc, "size of array %qE is too large",
-			            name);
+			  error_at (loc, "size of array %qE is too large "
+				    "(%qE bytes)",
+			            name, size);
 			else
-			  error_at (loc, "size of unnamed array is too large");
+			  error_at (loc, "size of unnamed array is too large"
+				    " (%qE bytes)", size);
 			type = error_mark_node;
 			continue;
 		      }
@@ -7701,7 +7703,7 @@  finish_struct (location_t loc, tree t, tree fieldlist, tree attributes,
       && TREE_CODE (TYPE_SIZE_UNIT (t)) == INTEGER_CST
       && !TREE_OVERFLOW (TYPE_SIZE_UNIT (t))
       && !valid_constant_size_p (TYPE_SIZE_UNIT (t)))
-    error ("type %qT is too large", t);
+    error ("size of type %qT is too large (%qE bytes)", t, TYPE_SIZE_UNIT (t));

   /* Give bit-fields their proper types and rewrite the type of array fields
      with scalar component if the enclosing type has reverse storage order.  */
diff --git a/gcc/cp/class.c b/gcc/cp/class.c
index 216a301..4803f55 100644
--- a/gcc/cp/class.c
+++ b/gcc/cp/class.c
@@ -35,6 +35,7 @@  along with GCC; see the file COPYING3.  If not see
 #include "convert.h"
 #include "dumpfile.h"
 #include "gimplify.h"
+#include "intl.h"

 /* The number of nested classes being processed.  If we are not in the
    scope of any class, this is zero.  */
@@ -6531,7 +6532,7 @@  layout_class_type (tree t, tree *virtuals_p)
       && TREE_CODE (TYPE_SIZE_UNIT (t)) == INTEGER_CST
       && !TREE_OVERFLOW (TYPE_SIZE_UNIT (t))
       && !valid_constant_size_p (TYPE_SIZE_UNIT (t)))
-    error ("type %qT is too large", t);
+    error ("size of type %qT is too large (%qE bytes)", t, TYPE_SIZE_UNIT (t));

   /* Warn about bases that can't be talked about due to ambiguity.  */
   warn_about_ambiguous_bases (t);
@@ -6598,12 +6599,196 @@  sorted_fields_type_new (int n)
 }


+/* Return true when all base classes of class T (a class type) are
+   empty, false otherwise.  */
+
+static bool
+all_bases_empty_p (tree t)
+{
+  int i;
+  tree binfo;
+  tree base_binfo;
+
+  for (binfo = TYPE_BINFO (t), i = 0;
+       BINFO_BASE_ITERATE (binfo, i, base_binfo); i++)
+    {
+      tree basetype = TREE_TYPE (base_binfo);
+      gcc_assert (COMPLETE_TYPE_P (basetype));
+
+      if (!is_empty_class (basetype))
+	return false;
+    }
+
+  return true;
+}
+
+/* Helper of finish_struct_1.  Return true when FLD refers to a non-static
+   class data member of non-zero size, otherwise false.  */
+
+static inline bool
+field_nonempty_p (const_tree fld)
+{
+  if (TREE_CODE (fld) == ERROR_MARK)
+    return false;
+
+  tree type = TREE_TYPE (fld);
+  if (TREE_CODE (fld) == FIELD_DECL
+      && TREE_CODE (type) != ERROR_MARK
+      && (DECL_NAME (fld) || RECORD_OR_UNION_TYPE_P (type)))
+    {
+      return TREE_CODE (TYPE_SIZE (type)) != INTEGER_CST
+	|| !tree_int_cst_equal (size_zero_node, TYPE_SIZE (type));
+    }
+  return false;
+}
+
+/* Check to make sure any definitions of flexible array members
+   and zero-size arrays in a chain of FIELDS are valid, and
+   diagnose those that aren't.  T denotes a struct, class, or
+   union whose fields are being checked.  SEEN_FIELD is used
+   internally by recursive invocations of the function to set
+   the pointed-to object to true when a declaration of a non-
+   empty non-static data member precedes the declaration of
+   a flexible array member or a zero-size array.
+*/
+
+static tree
+check_flexarrays (tree t, tree fields, bool *seen_field)
+{
+  /* inform (DECL_SOURCE_LOCATION (fields), */
+  /* 	  "entering %s with fields = %qD", __func__, fields); */
+
+  /* Members of anonymous structs and unions are considered to be members
+     of the containing struct or union.  */
+  if (TYPE_ANONYMOUS_P (t))
+    return NULL_TREE;
+
+  /* Unlike the C front end, the C++ front end sets the upper bound
+     of a flexible array member to SIZE_MAX.  Precompute that value
+     up front to avoid recomputing it on each iteration.  */
+  tree size_max_node =
+    int_const_binop (MINUS_EXPR, size_zero_node, size_one_node);
+
+  /* Iterate over struct members, looking for definitions of non-static
+     flexible array members and zero-size arrays and determine whether
+     each is valid.  */
+  for (tree fld = fields, next, f; fld;
+       *seen_field = *seen_field || field_nonempty_p (f), fld = next)
+    {
+      next = DECL_CHAIN (fld);
+
+      /* Use f below to check the current field, or to substitute
+	 for it the flexible array member or zero-size array of
+	 the anonymous struct it represents.  */
+      f = fld;
+
+      const_tree fldtype = TREE_TYPE (f);
+      if (TREE_CODE (f) != TYPE_DECL
+	  && RECORD_OR_UNION_TYPE_P (fldtype)
+	  && TYPE_ANONYMOUS_P (fldtype))
+	{
+	  /* Descend into the anonymous struct or union and check
+	     its fields.  The recursive call to the function will
+	     either return 0 or the flexible array member whose
+	     validity depends on whether any non-static data members
+	     are declared in the enclosing struct.  */
+	  tree flexarray =
+	    check_flexarrays (t, TYPE_FIELDS (fldtype), seen_field);
+	  if (flexarray)
+	    {
+	      f = flexarray;
+	      fldtype = TREE_TYPE (flexarray);
+	    }
+	}
+
+      /* Skip anything that's not a (non-static) data member.  */
+      if (TREE_CODE (f) != FIELD_DECL)
+	continue;
+
+      /* Skip non-arrays.  */
+      if (TREE_CODE (fldtype) != ARRAY_TYPE || !DECL_CHAIN (f))
+	continue;
+
+      /* Determine the array's domain type.  */
+      const_tree dom = TYPE_DOMAIN (fldtype);
+      if (!dom)
+	continue;
+
+      /* Determine the upper bound of the array.  */
+      const_tree upbnd = TYPE_MAX_VALUE (dom);
+      if (!upbnd)
+	continue;
+
+      /* In C++, flexible array members and zero-size arrays have
+	 an upper bound of SIZE_MAX.  Skip those whose upper bound
+	 is less than that.  */
+      if (!tree_int_cst_equal (size_max_node, upbnd))
+	continue;
+
+      /* Flag is clear for zero-size arrays, set for flexible array
+	 members.  */
+      if (0 == DECL_LANG_FLAG_1 (f))
+	{
+	  const char *msg = 0;
+
+	  if (next && TREE_CODE (next) == FIELD_DECL)
+	    msg = G_("zero-size array member %qD not at end of %q#T");
+	  else if (!*seen_field && all_bases_empty_p (t))
+	    msg = G_("zero-size array member %qD in an otherwise empty %q#T");
+
+	  if (msg && pedwarn (DECL_SOURCE_LOCATION (f), OPT_Wpedantic,
+			      msg, f, t))
+	    inform (input_location, "%q#T declared here",
+		    DECL_FIELD_CONTEXT (f));
+	}
+      else
+	{
+	  const char *msg = 0;
+
+	  if (next && TREE_CODE (next) == FIELD_DECL)
+	    msg = G_("flexible array member %qD not at end of %q#T");
+	  else if (!*seen_field && all_bases_empty_p (t))
+	    {
+	      /* When f is a member of anonymous struct, return
+		 the (possibly invalid) flexible array member to
+		 the (recursive) caller to determine whether it
+		 is followed by any other data members in the
+		 containing struct.  */
+	      if (DECL_CONTEXT (fld) != t)
+		return f;
+
+	      msg = G_("flexible array member %qD in an otherwise empty %q#T");
+	    }
+
+	  if (msg)
+	    {
+	      /* Clear the flag specifically set for this purpose
+		 only when a diagnostic is issued but leave it set
+		 for prior (recursive) calls to the function so that
+		 the appropriate message can be issued.  */
+	      DECL_LANG_FLAG_1 (f) = 0;
+	      error_at (DECL_SOURCE_LOCATION (f), msg, f, t);
+	      inform (input_location, "%q#T declared here",
+		      DECL_FIELD_CONTEXT (f));
+	    }
+	}
+    }
+
+  return NULL_TREE;
+}
+
 /* Perform processing required when the definition of T (a class type)
-   is complete.  */
+   is complete.  Diagnose invalid definitions of flexible array members
+   and zero-size arrays.  */

 void
 finish_struct_1 (tree t)
 {
+  {
+    bool dummy = false;
+    check_flexarrays (t, TYPE_FIELDS (t), &dummy);
+  }
+
   tree x;
   /* A TREE_LIST.  The TREE_VALUE of each node is a FUNCTION_DECL.  */
   tree virtuals = NULL_TREE;
diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c
index 675342e..ba3daf3 100644
--- a/gcc/cp/decl.c
+++ b/gcc/cp/decl.c
@@ -8614,10 +8614,18 @@  fold_sizeof_expr (tree t)
    name of the thing being declared.  */

 tree
-compute_array_index_type (tree name, tree size, tsubst_flags_t complain)
+compute_array_index_type (tree name, tree size,
+			  tsubst_flags_t complain)
 {
   tree itype;
   tree osize = size;
+  bool flexarray = false;
+
+  if (NULL_TREE == size)
+    {
+      flexarray = true;
+      size = size_zero_node;
+    }

   if (error_operand_p (size))
     return error_mark_node;
@@ -8739,11 +8747,12 @@  compute_array_index_type (tree name, tree size, tsubst_flags_t complain)
 	    return error_mark_node;
 	  else if (in_system_header_at (input_location))
 	    /* Allow them in system headers because glibc uses them.  */;
-	  else if (name)
-	    pedwarn (input_location, OPT_Wpedantic, "ISO C++ forbids zero-size array %qD", name);
 	  else
-	    pedwarn (input_location, OPT_Wpedantic, "ISO C++ forbids zero-size array");
+	    pedwarn (input_location, OPT_Wpedantic,
+		     flexarray ? "flexible array members are a C++ extension"
+		     : "ISO C++ forbids zero-size arrays");
 	}
+
     }
   else if (TREE_CONSTANT (size)
 	   /* We don't allow VLAs at non-function scopes, or during
@@ -9182,6 +9191,8 @@  grokdeclarator (const cp_declarator *declarator,
   source_location saved_loc = input_location;
   const char *errmsg;
   tree reqs = NULL_TREE;
+  /* Is the declarator a flexible array member?  */
+  bool flexarray = false;

   signed_p = decl_spec_seq_has_spec_p (declspecs, ds_signed);
   unsigned_p = decl_spec_seq_has_spec_p (declspecs, ds_unsigned);
@@ -10888,7 +10899,7 @@  grokdeclarator (const cp_declarator *declarator,
     }

   {
-    tree decl;
+    tree decl = NULL_TREE;

     if (decl_context == PARM)
       {
@@ -10912,11 +10923,20 @@  grokdeclarator (const cp_declarator *declarator,
 	if (!staticp && TREE_CODE (type) == ARRAY_TYPE
 	    && TYPE_DOMAIN (type) == NULL_TREE)
 	  {
-	    tree itype = compute_array_index_type (dname, integer_zero_node,
+	    if (TREE_CODE (ctype) == UNION_TYPE
+		|| TREE_CODE (ctype) == QUAL_UNION_TYPE)
+	      {
+		error ("flexible array member in union");
+		type = error_mark_node;
+	      }
+	    else
+	      {
+		flexarray = true;
+		tree itype = compute_array_index_type (dname, NULL_TREE,
 						       tf_warning_or_error);
 		type = build_cplus_array_type (TREE_TYPE (type), itype);
 	      }
-
+	  }
 	if (type == error_mark_node)
 	  {
 	    /* Happens when declaring arrays of sizes which
@@ -11404,6 +11424,9 @@  grokdeclarator (const cp_declarator *declarator,
     if (!processing_template_decl)
       cp_apply_type_quals_to_decl (type_quals, decl);

+    if (flexarray)
+      DECL_LANG_FLAG_1 (decl) = 1;
+
     return decl;
   }
 }
diff --git a/gcc/testsuite/g++.dg/ext/flexary2.C b/gcc/testsuite/g++.dg/ext/flexary2.C
index 4855b3f..dcd1bde 100644
--- a/gcc/testsuite/g++.dg/ext/flexary2.C
+++ b/gcc/testsuite/g++.dg/ext/flexary2.C
@@ -1,4 +1,10 @@ 
-// PR c++/46688
+// PR c++/46688 - [4.6 Regression] g++ requires a function declaration
+// when it should not
+// Note that although the definition of struct B in the test case for
+// c++/46688 was thought to be valid, it is, in fact, invalid, in C and
+// as noted in c++/42121, should be treated as invalid in C++ as well.
+// The test verifies that gcc detects and reports the right error.
+
 // { dg-options "" }

 struct A {
@@ -7,5 +13,10 @@  struct A {

 struct B {
     B() {}
-   A a[];
+    A a[];   // { dg-error "extension|flexible array .* in an otherwise empty" }
+};
+
+struct C {
+    C() {}
+    A a[0];  // -Wpedantic warning: ISO C++ forbids zero-size arrays
 };
diff --git a/gcc/testsuite/g++.dg/ext/flexary3.C b/gcc/testsuite/g++.dg/ext/flexary3.C
index 906877b..5c4a658 100644
--- a/gcc/testsuite/g++.dg/ext/flexary3.C
+++ b/gcc/testsuite/g++.dg/ext/flexary3.C
@@ -1,10 +1,19 @@ 
-// PR c++/54441
+// PR c++/54441 - [4.7/4.8 Regression] Infinite loop with brace initializer
+//                on zero-length array
+// Note that although the definition of struct s in the test case for
+// c++/54441 was accepted as valid, it is, in fact, invalid in C, and
+// as noted in c++/42121, should be treated as invalid in C++ as well.
+// The test verifies that gcc detects, reports, and handles both errors
+// gracefully.
+
 // { dg-options "" }

-struct s { char c[]; };
+struct s {
+    char c[];   // { dg-error "flexible array member .* in an otherwise empty" }
+};

 int main()
 {
-    struct s s = { .c = 0 };	// { dg-error "initializer" }
+    struct s s = { .c = 0 };	// { dg-error "invalid initializer" }
     return 0;
 }
diff --git a/gcc/testsuite/g++.dg/ext/flexary4.C b/gcc/testsuite/g++.dg/ext/flexary4.C
new file mode 100644
index 0000000..dd001f3
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/flexary4.C
@@ -0,0 +1,370 @@ 
+// PR c++/42121 - g++ should warn or error on internal 0 size array in struct
+// { dg-do compile }
+// { dg-options "-Wno-error=pedantic" }
+
+// Flexible array members are a feature of C99 (and newer) not provided
+// by C++ 2014 and prior.  G++ supports both the C99/C11 kind of flexible
+// array members and pre-C99 zero-size arrays (defining an array of size
+// zero).  Since both features are provided for compatibility with C,
+// G++ allows them in the same contexts as in C.
+
+struct Sx {
+    int a[];                  // { dg-error "flexible array member" }
+};
+
+// Verify that non-data members or static data members don't suppress
+// the diagnostic.
+struct Sx2 {
+    int a[];                  // { dg-error "flexible array member" }
+    typedef int I;
+};
+
+struct Sx3 {
+    typedef int I;
+    int a[];                  // { dg-error "flexible array member" }
+};
+
+struct Sx4 {
+    int a[];                  // { dg-error "flexible array member" }
+    enum E { e };
+};
+
+struct Sx5 {
+    enum E { e };
+    int a[];                  // { dg-error "flexible array member" }
+};
+
+struct Sx6 {
+    int a[];                  // { dg-error "flexible array member" }
+    static int i;
+};
+
+int Sx6::i;
+
+struct Sx7 {
+    static int i;
+    int a[];                  // { dg-error "flexible array member" }
+};
+
+int Sx7::i;
+
+struct Sx8 {
+    int a[];                  // { dg-error "flexible array member" }
+    Sx8 () { }
+};
+
+struct Sx9 {
+    Sx9 () { }
+    int a[];                  // { dg-error "flexible array member" }
+};
+
+struct Sx10 {
+    int a[];                  // { dg-error "flexible array member" }
+    virtual ~Sx10 () { }
+};
+
+struct Sx11 {
+    virtual ~Sx11 () { }
+    int a[];                  // { dg-error "flexible array member" }
+};
+
+struct Sx12 {
+    int a[];                  // { dg-error "flexible array member" }
+    virtual void foo () = 0;
+};
+
+struct Sx13 {
+    virtual void foo () = 0;
+    int a[];                  // { dg-error "flexible array member" }
+};
+
+struct Sx14 {
+    int a[][1];               // { dg-error "flexible array member" }
+};
+
+struct Sx15 {
+    typedef int A[];
+    A a;                      // { dg-error "flexible array member" }
+};
+
+// Verify also that a zero-size array doesn't suppress the diagnostic.
+struct Sx16 {
+    // a_0 below is diagnosed with -Wpedantic only and emits
+    // warning: ISO C++ forbids zero-size arrays
+    int a_0 [0];
+    int a_x [];               // { dg-error "flexible array member" }
+};
+
+struct Sx17 {
+    int a_x [];               // { dg-error "flexible array member" }
+
+    // a_0 below is diagnosed with -Wpedantic only and emits
+    // warning: ISO C++ forbids zero-size arrays
+    int a_0 [0];
+};
+
+struct Sx18 {
+    int a_x [];               // { dg-error "flexible array member" }
+    struct S { };
+};
+
+struct Sx19 {
+    struct S { };
+    int a_x [];               // { dg-error "flexible array member" }
+};
+
+struct Sx20 {
+    struct S { } s;
+    int a_x [];
+};
+
+struct Sx21 {
+    int a_x [];               // { dg-error "not at end" }
+    struct S { } s;
+};
+
+struct Sx22 {
+    int a_x [];               // { dg-error "not at end" }
+    union { int i; };
+};
+
+struct Sx23 {
+    union { int i; };
+    int a_x [];
+};
+
+struct Sx24 {
+    struct S;
+    S a_x [];                 // { dg-error "incomplete type" }
+};
+
+struct Sx25 {
+    struct S { };
+    S a_x [];                 // { dg-error "flexible array member" }
+};
+
+struct Sx26 {
+    struct { }
+    a_x [];                   // { dg-error "flexible array member" }
+};
+
+struct Sx27 {
+    int i;
+    struct { }
+    a_x [];
+};
+
+struct Sx28 {
+    struct { }
+    a_x [];                   // { dg-error "not at end" }
+    int i;
+};
+
+struct Sx29 {
+    // Pointer to an array of unknown size.
+    int (*a_x)[];
+};
+
+struct Sx30 {
+    // Reference to an array of unknown size.
+    int (&a_x)[];
+};
+
+struct Sx31 {
+    int a [];                 // { dg-error "not at end" }
+    unsigned i: 1;
+};
+
+struct Sx32 {
+    unsigned i: 1;
+    int a [];
+};
+
+struct S_S_S_x {
+    struct A {
+        struct B {
+            int a[];          // { dg-error "flexible array member" }
+        } b;
+    } a;
+};
+
+// Since members of an anonymous struct or union are considered to be
+// members of the enclosing union the below defintions are valid and
+// must be accepted.
+
+struct Anon1 {
+    int n;
+    struct {
+        int good[];
+    };
+};
+
+struct Anon2 {
+    struct {
+        int n;
+        struct {
+            int good[];
+        };
+    };
+};
+
+struct Anon3 {
+    struct {
+        struct {
+            int n;
+            int good[];
+        };
+    };
+};
+
+struct Anon4 {
+    struct {
+        int in_empty_struct[];// { dg-error "in an otherwise empty" }
+    };
+};
+
+struct Anon5 {
+    struct {
+        int not_at_end[];     // { dg-error "not at end" }
+    };
+    int n;
+};
+
+struct Anon6 {
+    struct {
+        struct {
+            int not_at_end[]; // { dg-error "not at end" }
+        };
+        int n;
+    };
+};
+
+
+struct Anon7 {
+    struct {
+        struct {
+            int not_at_end[]; // { dg-error "not at end" }
+        };
+    };
+    int n;
+};
+
+
+struct Six {
+    int i;
+    int a[];
+};
+
+class Cx {
+    int a[];                  // { dg-error "flexible array member" }
+};
+
+class Cix {
+    int i;
+    int a[];
+};
+
+struct Sxi {
+    int a[];                  // { dg-error "not at end" }
+    int i;
+};
+
+struct S0 {
+    int a[0];
+};
+
+struct S0i {
+    int a[0];
+    int i;
+};
+
+struct S_a0_ax {
+    int a1[0];
+    int ax[];                 // { dg-error "flexible array member" }
+};
+
+struct S_a0_i_ax {
+    int a1[0];
+    int i;
+    int ax[];
+};
+
+struct Si_a0_ax {
+    int i;
+    int a1[0];
+    int ax[];
+};
+
+struct S_u_ax {
+    struct {
+        // An empty struct is in C++ treated as if it had a single
+        // member of type char.  Arguably, though, since neither
+        // the struct nor the member has a name, the flexible array
+        // member should be diagnosed because its size cannot easily
+        // be stored in the contaning object.  This seems like too
+        // much of a corner case to worry about.
+    };
+    int ax[];                 // no warning because of the above
+};
+
+struct S_u0_ax {
+    union { } u[0];
+    int ax[];                 // { dg-error "flexible array member" }
+};
+
+struct S_a1_s2 {
+    int a[1];
+    int b[2];
+};
+
+union U_i_ax {
+    int i;
+    int a[];                  // { dg-error "flexible array member in union" }
+};
+
+union U_i_a0 {
+    int i;
+    int a[0];
+};
+
+template <class T>
+struct ST: T {
+    char a[];   // cannot be diagnosed unless/until T is known
+};
+
+template <class T>
+struct STx_1: T {
+    char a[];   // { dg-error "flexible array member" }
+};
+
+template <class T, int I>
+struct STI: T {
+    char a[I];   // cannot be diagnosed unless/until T and I are known
+};
+
+template <class T, int I>
+struct STIx: T {
+    char a[I];
+};
+
+template <int>
+struct Empty { };
+
+STx_1<Empty<0> > stx_empty_1;
+STIx<Empty<0>, 0> stix_empty_1;
+
+struct EmptyBase1: Empty<0>, Empty<1> { };
+struct EmptyBase2: Empty<2>, Empty<3> { };
+struct EmptyDerived: EmptyBase1, EmptyBase2
+{
+    char a[];   // { dg-error "flexible array member" }
+};
+
+struct NonEmpty { int i; };
+struct NonEmptyBase: NonEmpty { };
+struct NonEmptyDerived: NonEmptyBase { };
+
+struct FinalDerived: EmptyBase1, EmptyBase2, NonEmpty, Empty<4>
+{
+    char a[];   // okay
+};
diff --git a/gcc/testsuite/g++.dg/parse/pr43765.C b/gcc/testsuite/g++.dg/parse/pr43765.C
index 0b341dd..e115aa5 100644
--- a/gcc/testsuite/g++.dg/parse/pr43765.C
+++ b/gcc/testsuite/g++.dg/parse/pr43765.C
@@ -3,12 +3,13 @@ 

 struct SomeType
 {
+    int n;
     const char *values[];
 };
 const char *temp[] = {"607", "612", 0};

 SomeType vals[] =
     {
-        { values : temp, },
+        { 0, values : temp, },
         0
     };          // { dg-error "invalid" }
diff --git a/gcc/testsuite/g++.dg/torture/pr64280.C b/gcc/testsuite/g++.dg/torture/pr64280.C
index 6ea3148..e756e02 100644
--- a/gcc/testsuite/g++.dg/torture/pr64280.C
+++ b/gcc/testsuite/g++.dg/torture/pr64280.C
@@ -15,7 +15,7 @@  public:
 typedef int jmp_buf[];
 struct C
 {
-  jmp_buf cond_;
+  jmp_buf cond_;   // { dg-error "flexible array member" }
 };
 class F
 {
diff --git a/gcc/testsuite/g++.dg/torture/pr64312.C b/gcc/testsuite/g++.dg/torture/pr64312.C
index dc3e95d..85211f2 100644
--- a/gcc/testsuite/g++.dg/torture/pr64312.C
+++ b/gcc/testsuite/g++.dg/torture/pr64312.C
@@ -43,6 +43,7 @@  protected:
 class F
 {
 public:
+  int nelems;
   int elems[];
   int *
   m_fn1 ()