diff mbox

accept flexible arrays in struct in unions (c++/71912 - [6/7 regression])

Message ID afbaed37-0437-0efd-e332-33e201385fa1@gmail.com
State New
Headers show

Commit Message

Martin Sebor Oct. 6, 2016, 8:29 p.m. UTC
> I'm asking you to clarify the logic.  It seems that your change to
> fldtype affects these two tests:
>
>>           if (eltype == fldtype || TYPE_UNNAMED_P (eltype))
>
>>       if (TREE_CODE (fldtype) != ARRAY_TYPE)
>
> ...but this is extremely subtle.  It would be a lot clearer to check fld
> for FIELD_DECL or TYPE_DECL explicitly rather than relying on these
> places that treat fldtype as a type to do the right thing because you've
> obfuscated it.  But I'm tired of going back and forth on this, so here's
> a patch.
>
> And now that I notice it, there seems to be no reason to handle typedefs
> deep in the code for handling fields, it's simpler to handle them up top.

I'm sorry you're frustrated.  I have no problem changing the code to
the way you wrote it.  I agree it's more streamlined though I would
be hard pressed to consider the improvement to be worth the time and
effort we both put into it.  I'm also not sure I see how someone can
be expected to write the code exactly the way you want based on little
more than questions about why the code does what it does.  I had no
idea what your expectation was for the fldtype variable for instance.

Attached is the integrated patch.

Martin

Comments

Martin Sebor Oct. 12, 2016, 1:45 a.m. UTC | #1
Jason,

Are there any other changes you want me to make to the patch?
I leave this weekend for the WG14 meeting and would like to
get this change finalized and hopefully committed before then.

   https://gcc.gnu.org/ml/gcc-patches/2016-10/msg00410.html

Thanks
Martin

On 10/06/2016 02:29 PM, Martin Sebor wrote:
>> I'm asking you to clarify the logic.  It seems that your change to
>> fldtype affects these two tests:
>>
>>>           if (eltype == fldtype || TYPE_UNNAMED_P (eltype))
>>
>>>       if (TREE_CODE (fldtype) != ARRAY_TYPE)
>>
>> ...but this is extremely subtle.  It would be a lot clearer to check fld
>> for FIELD_DECL or TYPE_DECL explicitly rather than relying on these
>> places that treat fldtype as a type to do the right thing because you've
>> obfuscated it.  But I'm tired of going back and forth on this, so here's
>> a patch.
>>
>> And now that I notice it, there seems to be no reason to handle typedefs
>> deep in the code for handling fields, it's simpler to handle them up top.
>
> I'm sorry you're frustrated.  I have no problem changing the code to
> the way you wrote it.  I agree it's more streamlined though I would
> be hard pressed to consider the improvement to be worth the time and
> effort we both put into it.  I'm also not sure I see how someone can
> be expected to write the code exactly the way you want based on little
> more than questions about why the code does what it does.  I had no
> idea what your expectation was for the fldtype variable for instance.
>
> Attached is the integrated patch.
>
> Martin
>
Jason Merrill Oct. 12, 2016, 1:43 p.m. UTC | #2
On Tue, Oct 11, 2016 at 9:45 PM, Martin Sebor <msebor@gmail.com> wrote:
> Are there any other changes you want me to make to the patch?
> I leave this weekend for the WG14 meeting and would like to
> get this change finalized and hopefully committed before then.
>
>   https://gcc.gnu.org/ml/gcc-patches/2016-10/msg00410.html

OK, thanks.  Sorry I overlooked your earlier mail.

Jason
Martin Sebor Oct. 13, 2016, 10:40 p.m. UTC | #3
On 10/12/2016 07:43 AM, Jason Merrill wrote:
> On Tue, Oct 11, 2016 at 9:45 PM, Martin Sebor <msebor@gmail.com> wrote:
>> Are there any other changes you want me to make to the patch?
>> I leave this weekend for the WG14 meeting and would like to
>> get this change finalized and hopefully committed before then.
>>
>>   https://gcc.gnu.org/ml/gcc-patches/2016-10/msg00410.html
>
> OK, thanks.  Sorry I overlooked your earlier mail.

I forgot to also request approval to backport it to the 6 branch.
Are you fine with that as well?

Thanks
Martin
diff mbox

Patch

PR c++/71912 - [6/7 regression] flexible array in struct in union rejected

gcc/cp/ChangeLog:
	PR c++/71912
	* class.c (struct flexmems_t):  Add members.
	(find_flexarrays): Add arguments.  Correct handling of anonymous
	structs.
	(diagnose_flexarrays): Adjust to issue warnings in addition to errors.
	(check_flexarrays): Add argument.
	(diagnose_invalid_flexarray): New functions.

gcc/testsuite/ChangeLog:
	PR c++/71912
	* g++.dg/ext/flexary4.C: Adjust.
	* g++.dg/ext/flexary5.C: Same.
	* g++.dg/ext/flexary9.C: Same.
	* g++.dg/ext/flexary19.C: New test.
	* g++.dg/ext/flexary18.C: New test.
	* g++.dg/torture/pr64312.C: Add a dg-error directive to an ill-formed
	regression test.
        * g++.dg/compat/struct-layout-1_generate.c (subfield): Add argument.
        Avoid generating a flexible array member in an array.

diff --git a/gcc/cp/class.c b/gcc/cp/class.c
index 34d10ba..75c89cd 100644
--- a/gcc/cp/class.c
+++ b/gcc/cp/class.c
@@ -147,11 +147,12 @@  static void check_methods (tree);
 static void remove_zero_width_bit_fields (tree);
 static bool accessible_nvdtor_p (tree);
 
-/* Used by find_flexarrays and related.  */
+/* Used by find_flexarrays and related functions.  */
 struct flexmems_t;
-static void find_flexarrays (tree, flexmems_t *);
 static void diagnose_flexarrays (tree, const flexmems_t *);
-static void check_flexarrays (tree, flexmems_t * = NULL);
+static void find_flexarrays (tree, flexmems_t *, bool = false,
+			     tree = NULL_TREE, tree = NULL_TREE);
+static void check_flexarrays (tree, flexmems_t * = NULL, bool = false);
 static void check_bases (tree, int *, int *);
 static void check_bases_and_members (tree);
 static tree create_vtable_ptr (tree, tree *);
@@ -6692,52 +6693,147 @@  field_nonempty_p (const_tree fld)
   return false;
 }
 
-/* Used by find_flexarrays and related.  */
-struct flexmems_t {
+/* Used by find_flexarrays and related functions.  */
+
+struct flexmems_t
+{
   /* The first flexible array member or non-zero array member found
-     in order of layout.  */
+     in the order of layout.  */
   tree array;
   /* First non-static non-empty data member in the class or its bases.  */
   tree first;
-  /* First non-static non-empty data member following either the flexible
-     array member, if found, or the zero-length array member.  */
-  tree after;
+  /* The first non-static non-empty data member following either
+     the flexible array member, if found, or the zero-length array member
+     otherwise.  AFTER[1] refers to the first such data member of a union
+     of which the struct containing the flexible array member or zero-length
+     array is a member, or NULL when no such union exists.  This element is
+     only used during searching, not for diagnosing problems.  AFTER[0]
+     refers to the first such data member that is not a member of such
+     a union.  */
+  tree after[2];
+
+  /* Refers to a struct (not union) in which the struct of which the flexible
+     array is member is defined.  Used to diagnose strictly (according to C)
+     invalid uses of the latter structs.  */
+  tree enclosing;
 };
 
 /* Find either the first flexible array member or the first zero-length
-   array, in that order or preference, among members of class T (but not
-   its base classes), and set members of FMEM accordingly.  */
+   array, in that order of preference, among members of class T (but not
+   its base classes), and set members of FMEM accordingly.
+   BASE_P is true if T is a base class of another class.
+   PUN is set to the outermost union in which the flexible array member
+   (or zero-length array) is defined if one such union exists, otherwise
+   to NULL.
+   Similarly, PSTR is set to a data member of the outermost struct of
+   which the flexible array is a member if one such struct exists,
+   otherwise to NULL.  */
 
 static void
-find_flexarrays (tree t, flexmems_t *fmem)
+find_flexarrays (tree t, flexmems_t *fmem, bool base_p,
+		 tree pun /* = NULL_TREE */,
+		 tree pstr /* = NULL_TREE */)
 {
-  for (tree fld = TYPE_FIELDS (t), next; fld; fld = next)
+  /* Set the "pointer" to the outermost enclosing union if not set
+     yet and maintain it for the remainder of the recursion.   */
+  if (!pun && TREE_CODE (t) == UNION_TYPE)
+    pun = t;
+
+  for (tree fld = TYPE_FIELDS (t); fld; fld = DECL_CHAIN (fld))
     {
-      /* Find the next non-static data member if it exists.  */
-      for (next = fld;
-	   (next = DECL_CHAIN (next))
-	     && TREE_CODE (next) != FIELD_DECL; );
+      if (fld == error_mark_node)
+	return;
 
-      tree fldtype = TREE_TYPE (fld);
-      if (TREE_CODE (fld) != TYPE_DECL
-	  && RECORD_OR_UNION_TYPE_P (fldtype)
-	  && TYPE_UNNAMED_P (fldtype))
-	{
-	  /* Members of anonymous structs and unions are treated as if
-	     they were members of the containing class.  Descend into
-	     the anonymous struct or union and find a flexible array
-	     member or zero-length array among its fields.  */
-	  find_flexarrays (fldtype, fmem);
+      /* Is FLD a typedef for an anonymous struct?  */
+
+      /* FIXME: Note that typedefs (as well as arrays) need to be fully
+	 handled elsewhere so that errors like the following are detected
+	 as well:
+	   typedef struct { int i, a[], j; } S;   // bug c++/72753
+           S s [2];                               // bug c++/68489
+      */
+      if (TREE_CODE (fld) == TYPE_DECL
+	  && DECL_IMPLICIT_TYPEDEF_P (fld)
+	  && CLASS_TYPE_P (TREE_TYPE (fld))
+	  && anon_aggrname_p (DECL_NAME (fld)))
+	{
+	  /* Check the nested unnamed type referenced via a typedef
+	     independently of FMEM (since it's not a data member of
+	     the enclosing class).  */
+	  check_flexarrays (TREE_TYPE (fld));
 	  continue;
 	}
 
-      /* Skip anything that's not a (non-static) data member.  */
-      if (TREE_CODE (fld) != FIELD_DECL)
+      /* Skip anything that's GCC-generated or not a (non-static) data
+	 member.  */
+      if (DECL_ARTIFICIAL (fld) || TREE_CODE (fld) != FIELD_DECL)
 	continue;
 
-      /* Skip virtual table pointers.  */
-      if (DECL_ARTIFICIAL (fld))
-	continue;
+      /* Type of the member.  */
+      tree fldtype = TREE_TYPE (fld);
+      if (fldtype == error_mark_node)
+	return;
+
+      /* Determine the type of the array element or object referenced
+	 by the member so that it can be checked for flexible array
+	 members if it hasn't been yet.  */
+      tree eltype = fldtype;
+      while (TREE_CODE (eltype) == ARRAY_TYPE
+	     || TREE_CODE (eltype) == POINTER_TYPE
+	     || TREE_CODE (eltype) == REFERENCE_TYPE)
+	eltype = TREE_TYPE (eltype);
+
+      if (RECORD_OR_UNION_TYPE_P (eltype))
+	{
+	  if (fmem->array && !fmem->after[bool (pun)])
+	    {
+	      /* Once the member after the flexible array has been found
+		 we're done.  */
+	      fmem->after[bool (pun)] = fld;
+	      break;
+	    }
+
+	  if (eltype == fldtype || TYPE_UNNAMED_P (eltype))
+	    {
+	      /* Descend into the non-static member struct or union and try
+		 to find a flexible array member or zero-length array among
+		 its members.  This is only necessary for anonymous types
+		 and types in whose context the current type T has not been
+		 defined (the latter must not be checked again because they
+		 are already in the process of being checked by one of the
+		 recursive calls).  */
+
+	      tree first = fmem->first;
+	      tree array = fmem->array;
+
+	      /* If this member isn't anonymous and a prior non-flexible array
+		 member has been seen in one of the enclosing structs, clear
+		 the FIRST member since it doesn't contribute to the flexible
+		 array struct's members.  */
+	      if (first && !array && !ANON_AGGR_TYPE_P (eltype))
+		fmem->first = NULL_TREE;
+
+	      find_flexarrays (eltype, fmem, false, pun,
+			       !pstr && TREE_CODE (t) == RECORD_TYPE ? fld : pstr);
+
+	      if (fmem->array != array)
+		continue;
+
+	      if (first && !array && !ANON_AGGR_TYPE_P (eltype))
+		{
+		  /* Restore the FIRST member reset above if no flexible
+		     array member has been found in this member's struct.  */
+		  fmem->first = first;
+		}
+
+	      /* If the member struct contains the first flexible array
+		 member, or if this member is a base class, continue to
+		 the next member and avoid setting the FMEM->NEXT pointer
+		 to point to it.  */
+	      if (base_p)
+		continue;
+	    }
+	}
 
       if (field_nonempty_p (fld))
 	{
@@ -6748,8 +6844,8 @@  find_flexarrays (tree t, flexmems_t *fmem)
 	  /* Remember the first non-static data member after the flexible
 	     array member, if one has been found, or the zero-length array
 	     if it has been found.  */
-	  if (!fmem->after && fmem->array)
-	    fmem->after = fld;
+	  if (fmem->array && !fmem->after[bool (pun)])
+	    fmem->after[bool (pun)] = fld;
 	}
 
       /* Skip non-arrays.  */
@@ -6765,13 +6861,16 @@  find_flexarrays (tree t, flexmems_t *fmem)
 		 such field or a flexible array member has been seen to
 		 handle the pathological and unlikely case of multiple
 		 such members.  */
-	      if (!fmem->after)
-		fmem->after = fld;
+	      if (!fmem->after[bool (pun)])
+		fmem->after[bool (pun)] = fld;
 	    }
 	  else if (integer_all_onesp (TYPE_MAX_VALUE (TYPE_DOMAIN (fldtype))))
-	    /* Remember the first zero-length array unless a flexible array
-	       member has already been seen.  */
-	    fmem->array = fld;
+	    {
+	      /* Remember the first zero-length array unless a flexible array
+		 member has already been seen.  */
+	      fmem->array = fld;
+	      fmem->enclosing = pstr;
+	    }
 	}
       else
 	{
@@ -6782,16 +6881,39 @@  find_flexarrays (tree t, flexmems_t *fmem)
 		 reset the after pointer.  */
 	      if (TYPE_DOMAIN (TREE_TYPE (fmem->array)))
 		{
+		  fmem->after[bool (pun)] = NULL_TREE;
 		  fmem->array = fld;
-		  fmem->after = NULL_TREE;
+		  fmem->enclosing = pstr;
 		}
 	    }
 	  else
-	    fmem->array = fld;
+	    {
+	      fmem->array = fld;
+	      fmem->enclosing = pstr;
+	    }
 	}
     }
 }
 
+/* Diagnose a strictly (by the C standard) invalid use of a struct with
+   a flexible array member (or the zero-length array extension).  */
+
+static void
+diagnose_invalid_flexarray (const flexmems_t *fmem)
+{
+  if (fmem->array && fmem->enclosing
+      && pedwarn (location_of (fmem->enclosing), OPT_Wpedantic,
+		  TYPE_DOMAIN (TREE_TYPE (fmem->array))
+		  ? G_("invalid use of %q#T with a zero-size array "
+		       "in %q#D")
+		  : G_("invalid use of %q#T with a flexible array member "
+		       "in %q#T"),
+		  DECL_CONTEXT (fmem->array),
+		  DECL_CONTEXT (fmem->enclosing)))
+    inform (DECL_SOURCE_LOCATION (fmem->array),
+	    "array member %q#D declared here", fmem->array);
+}
+
 /* Issue diagnostics for invalid flexible array members or zero-length
    arrays that are not the last elements of the containing class or its
    base classes or that are its sole members.  */
@@ -6799,49 +6921,70 @@  find_flexarrays (tree t, flexmems_t *fmem)
 static void
 diagnose_flexarrays (tree t, const flexmems_t *fmem)
 {
-  /* Members of anonymous structs and unions are considered to be members
-     of the containing struct or union.  */
-  if (TYPE_UNNAMED_P (t) || !fmem->array)
+  if (!fmem->array)
     return;
 
+  if (fmem->first && !fmem->after[0])
+    {
+      diagnose_invalid_flexarray (fmem);
+      return;
+    }
+
+  /* Has a diagnostic been issued?  */
+  bool diagd = false;
+
   const char *msg = 0;
 
   if (TYPE_DOMAIN (TREE_TYPE (fmem->array)))
     {
-      if (fmem->after)
+      if (fmem->after[0])
 	msg = G_("zero-size array member %qD not at end of %q#T");
       else if (!fmem->first)
 	msg = G_("zero-size array member %qD in an otherwise empty %q#T");
 
-      if (msg && pedwarn (DECL_SOURCE_LOCATION (fmem->array),
-			  OPT_Wpedantic, msg, fmem->array, t))
+      if (msg)
+	{
+	  location_t loc = DECL_SOURCE_LOCATION (fmem->array);
 
-	inform (location_of (t), "in the definition of %q#T", t);
+	  if (pedwarn (loc, OPT_Wpedantic, msg, fmem->array, t))
+	    {
+	      inform (location_of (t), "in the definition of %q#T", t);
+	      diagd = true;
+	    }
+	}
     }
   else
     {
-      if (fmem->after)
+      if (fmem->after[0])
 	msg = G_("flexible array member %qD not at end of %q#T");
       else if (!fmem->first)
 	msg = G_("flexible array member %qD in an otherwise empty %q#T");
 
       if (msg)
 	{
-	  error_at (DECL_SOURCE_LOCATION (fmem->array), msg,
-		    fmem->array, t);
+	  location_t loc = DECL_SOURCE_LOCATION (fmem->array);
+	  diagd = true;
+
+	  error_at (loc, msg, fmem->array, t);
 
 	  /* In the unlikely event that the member following the flexible
-	     array member is declared in a different class, point to it.
+	     array member is declared in a different class, or the member
+	     overlaps another member of a common union, point to it.
 	     Otherwise it should be obvious.  */
-	  if (fmem->after
-	      && (DECL_CONTEXT (fmem->after) != DECL_CONTEXT (fmem->array)))
-	      inform (DECL_SOURCE_LOCATION (fmem->after),
+	  if (fmem->after[0]
+	      && ((DECL_CONTEXT (fmem->after[0])
+		   != DECL_CONTEXT (fmem->array))))
+	    {
+	      inform (DECL_SOURCE_LOCATION (fmem->after[0]),
 		      "next member %q#D declared here",
-		      fmem->after);
-
-	  inform (location_of (t), "in the definition of %q#T", t);
+		      fmem->after[0]);
+	      inform (location_of (t), "in the definition of %q#T", t);
+	    }
 	}
     }
+
+  if (!diagd && fmem->array && fmem->enclosing)
+    diagnose_invalid_flexarray (fmem);
 }
 
 
@@ -6854,7 +6997,8 @@  diagnose_flexarrays (tree t, const flexmems_t *fmem)
    that fails the checks.  */
 
 static void
-check_flexarrays (tree t, flexmems_t *fmem /* = NULL */)
+check_flexarrays (tree t, flexmems_t *fmem /* = NULL */,
+		  bool base_p /* = false */)
 {
   /* Initialize the result of a search for flexible array and zero-length
      array members.  Avoid doing any work if the most interesting FMEM data
@@ -6862,18 +7006,20 @@  check_flexarrays (tree t, flexmems_t *fmem /* = NULL */)
   flexmems_t flexmems = flexmems_t ();
   if (!fmem)
     fmem = &flexmems;
-  else if (fmem->array && fmem->first && fmem->after)
+  else if (fmem->array && fmem->first && fmem->after[0])
     return;
 
+  tree fam = fmem->array;
+
   /* Recursively check the primary base class first.  */
   if (CLASSTYPE_HAS_PRIMARY_BASE_P (t))
     {
       tree basetype = BINFO_TYPE (CLASSTYPE_PRIMARY_BINFO (t));
-      check_flexarrays (basetype, fmem);
+      check_flexarrays (basetype, fmem, true);
     }
 
   /* Recursively check the base classes.  */
-  int nbases = BINFO_N_BASE_BINFOS (TYPE_BINFO (t));
+  int nbases = TYPE_BINFO (t) ? BINFO_N_BASE_BINFOS (TYPE_BINFO (t)) : 0;
   for (int i = 0; i < nbases; ++i)
     {
       tree base_binfo = BINFO_BASE_BINFO (TYPE_BINFO (t), i);
@@ -6887,7 +7033,7 @@  check_flexarrays (tree t, flexmems_t *fmem /* = NULL */)
 	continue;
 
       /* Check the base class.  */
-      check_flexarrays (BINFO_TYPE (base_binfo), fmem);
+      check_flexarrays (BINFO_TYPE (base_binfo), fmem, /*base_p=*/true);
     }
 
   if (fmem == &flexmems)
@@ -6904,17 +7050,26 @@  check_flexarrays (tree t, flexmems_t *fmem /* = NULL */)
 	  /* Check the virtual base class.  */
 	  tree basetype = TREE_TYPE (base_binfo);
 
-	  check_flexarrays (basetype, fmem);
+	  check_flexarrays (basetype, fmem, /*base_p=*/true);
 	}
     }
 
-  /* Search the members of the current (derived) class.  */
-  find_flexarrays (t, fmem);
+  /* Is the type unnamed (and therefore a member of it potentially
+     an anonymous struct or union)?  */
+  bool maybe_anon_p = TYPE_UNNAMED_P (t);
 
-  if (fmem == &flexmems)
+  /* Search the members of the current (possibly derived) class, skipping
+     unnamed structs and unions since those could be anonymous.  */
+  if (fmem != &flexmems || !maybe_anon_p)
+    find_flexarrays (t, fmem, base_p || fam != fmem->array);
+
+  if (fmem == &flexmems && !maybe_anon_p)
     {
-      /* Issue diagnostics for invalid flexible and zero-length array members
-	 found in base classes or among the members of the current class.  */
+      /* Issue diagnostics for invalid flexible and zero-length array
+	 members found in base classes or among the members of the current
+	 class.  Ignore anonymous structs and unions whose members are
+	 considered to be members of the enclosing class and thus will
+	 be diagnosed when checking it.  */
       diagnose_flexarrays (t, fmem);
     }
 }
diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog
index f2e83f8..bd674b0 100644
--- a/gcc/testsuite/ChangeLog
+++ b/gcc/testsuite/ChangeLog
@@ -1324,6 +1324,12 @@ 
 	* gcc.target/msp430/function-attributes-2.c: New test.
 	* gcc.target/msp430/function-attributes-3.c: New test.
 
+2015-04-18  Martin Sebor  <msebor@redhat.com>
+
+	* gfortran.dg/pr32627.f03 (strptr): Change size to match the number
+	of non-nul characters.
+	* gfortran.dg/substr_6.f90: Make the NUL character visible on stdout
+
 2016-09-13  Jakub Jelinek  <jakub@redhat.com>
 
 	* g++.dg/cpp0x/gen-attrs-61.C: New test.
diff --git a/gcc/testsuite/g++.dg/compat/struct-layout-1_generate.c b/gcc/testsuite/g++.dg/compat/struct-layout-1_generate.c
index 71d4af9..8b620e4 100644
--- a/gcc/testsuite/g++.dg/compat/struct-layout-1_generate.c
+++ b/gcc/testsuite/g++.dg/compat/struct-layout-1_generate.c
@@ -495,7 +495,16 @@  struct types attrib_array_types[] = {
 #define HASH_SIZE 32749
 static struct entry *hash_table[HASH_SIZE];
 
-static int idx, limidx, output_one, short_enums;
+/* The index of the current type being output.  */
+static int idx;
+
+/* The maximum index of the type(s) to output.  */
+static int limidx;
+
+/* Set to non-zero to output a single type in response to the -i option
+   (which sets LIMIDX to the index of the type to output.  */
+static int output_one;
+static int short_enums;
 static const char *destdir;
 static const char *srcdir;
 static const char *srcdir_safe;
@@ -535,6 +544,7 @@  switchfiles (int fields)
       fputs ("failed to create test files\n", stderr);
       exit (1);
     }
+
   for (i = 0; i < NDG_OPTIONS; i++)
     fprintf (outfile, dg_options[i], "", srcdir_safe);
   fprintf (outfile, "\n\
@@ -607,9 +617,14 @@  getrandll (void)
 
 /* Generate a subfield.  The object pointed to by FLEX is set to a non-zero
    value when the generated field is a flexible array member.  When set, it
-   prevents subsequent fields from being generated (a flexible array mem*/
+   prevents subsequent fields from being generated (a flexible array member
+   must be the last member of the struct it's defined in).  ARRAY is non-
+   zero when the enclosing structure is part of an array.  In that case,
+   avoid generating a flexible array member as a subfield (such a member
+   would be invalid).  */
+
 int
-subfield (struct entry *e, char *letter, int *flex)
+subfield (struct entry *e, char *letter, int *flex, int array)
 {
   int i, type;
   char buf[20];
@@ -664,7 +679,14 @@  subfield (struct entry *e, char *letter, int *flex)
 	}
 
       for (i = 1; !*flex && i <= e[0].len; )
-	i += subfield (e + i, letter, flex);
+	{
+	  /* Avoid generating flexible array members if the enclosing
+	     type is an array.  */
+	  int array
+	    = (e[0].etype == ETYPE_STRUCT_ARRAY
+	       || e[0].etype == ETYPE_UNION_ARRAY);
+	    i += subfield (e + i, letter, flex, array);
+	}
 
       switch (type)
 	{
@@ -685,7 +707,7 @@  subfield (struct entry *e, char *letter, int *flex)
     case ETYPE_ARRAY:
       if (e[0].etype == ETYPE_ARRAY)
 	{
-	  if (e[0].arr_len == 255)
+	  if (!array && e[0].arr_len == 255)
 	    {
 	      *flex = 1;
  	      snprintf (buf, 20, "%c[]", *letter);
@@ -1141,6 +1163,7 @@  e_insert (struct entry *e)
   hash_table[hval % HASH_SIZE] = e;
 }
 
+/* Output a single type.  */
 void
 output (struct entry *e)
 {
@@ -1169,7 +1192,7 @@  output (struct entry *e)
 
   int flex = 0;
   for (i = 1; i <= e[0].len; )
-    i += subfield (e + i, &c, &flex);
+    i += subfield (e + i, &c, &flex, 0);
   
   fputs (",", outfile);
   c = 'a';
diff --git a/gcc/testsuite/g++.dg/ext/flexary18.C b/gcc/testsuite/g++.dg/ext/flexary18.C
new file mode 100644
index 0000000..4353425
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/flexary18.C
@@ -0,0 +1,213 @@ 
+// PR c++/71912 - [6/7 regression] flexible array in struct in union rejected
+// { dg-do compile }
+// { dg-additional-options "-Wpedantic -Wno-error=pedantic" }
+
+#if __cplusplus
+
+namespace pr71912 {
+
+#endif
+
+struct foo {
+  int a;
+  char s[];                             // { dg-message "array member .char pr71912::foo::s \\\[\\\]. declared here" }
+};
+
+struct bar {
+  double d;
+  char t[];
+};
+
+struct baz {
+  union {
+    struct foo f;
+    struct bar b;
+  }
+  // The definition of struct foo is fine but the use of struct foo
+  // in the definition of u below is what's invalid and must be clearly
+  // diagnosed.
+    u;                                  // { dg-warning "invalid use of .struct pr71912::foo. with a flexible array member in .struct pr71912::baz." }
+};
+
+struct xyyzy {
+  union {
+    struct {
+      int a;
+      char s[];                         // { dg-message "declared here" }
+    } f;
+    struct {
+      double d;
+      char t[];
+    } b;
+  } u;                                  // { dg-warning "invalid use" }
+};
+
+struct baz b;
+struct xyyzy x;
+
+#if __cplusplus
+
+}
+
+#endif
+
+// The following definitions aren't strictly valid but, like those above,
+// are accepted for compatibility with GCC (in C mode).  They are benign
+// in that the flexible array member is at the highest offset within
+// the outermost type and doesn't overlap with other members except for
+// those of the union.
+union UnionStruct1 {
+  struct { int n1, a[]; } s;
+  int n2;
+};
+
+union UnionStruct2 {
+  struct { int n1, a1[]; } s1;
+  struct { int n2, a2[]; } s2;
+  int n3;
+};
+
+union UnionStruct3 {
+  struct { int n1, a1[]; } s1;
+  struct { double n2, a2[]; } s2;
+  char n3;
+};
+
+union UnionStruct4 {
+  struct { int n1, a1[]; } s1;
+  struct { struct { int n2, a2[]; } s2; } s3;
+  char n3;
+};
+
+union UnionStruct5 {
+  struct { struct { int n1, a1[]; } s1; } s2;   // { dg-warning "invalid use" }
+  struct { double n2, a2[]; } s3;
+  char n3;
+};
+
+union UnionStruct6 {
+  struct { struct { int n1, a1[]; } s1; } s2;   // { dg-warning "invalid use" }
+  struct { struct { int n2, a2[]; } s3; } s4;
+  char n3;
+};
+
+union UnionStruct7 {
+  struct { int n1, a1[]; } s1;
+  struct { double n2, a2[]; } s2;
+  struct { struct { int n3, a3[]; } s3; } s4;
+};
+
+union UnionStruct8 {
+  struct { int n1, a1[]; } s1;
+  struct { struct { int n2, a2[]; } s2; } s3;
+  struct { struct { int n3, a3[]; } s4; } s5;
+};
+
+union UnionStruct9 {
+  struct { struct { int n1, a1[]; } s1; } s2;   // { dg-warning "invalid use" }
+  struct { struct { int n2, a2[]; } s3; } s4;
+  struct { struct { int n3, a3[]; } s5; } s6;
+};
+
+struct StructUnion1 {
+  union {
+    struct { int n1, a1[]; } s1;        // { dg-message "declared here" }
+    struct { double n2, a2[]; } s2;
+    char n3;
+  } u;                                  // { dg-warning "invalid use" }
+};
+
+// The following are invalid and rejected.
+struct StructUnion2 {
+  union {
+    struct { int n1, a1[]; } s1;        // { dg-error "not at end" }
+  } u;
+  char n3;                              // { dg-message "next member" }
+};
+
+struct StructUnion3 {
+  union {
+    struct { int n1, a1[]; } s1;        // { dg-error "not at end" }
+    struct { double n2, a2[]; } s2;
+  } u;
+  char n3;                              // { dg-message "next member" }
+};
+
+struct StructUnion4 {
+  union {
+    struct { int n1, a1[]; } s1;        // { dg-error "not at end" }
+  } u1;
+  union {
+    struct { double n2, a2[]; } s2;
+  } u2;                                 // { dg-message "next member" }
+};
+
+struct StructUnion5 {
+  union {
+    union {
+      struct { int n1, a1[]; } s1;      // { dg-message "declared here" }
+    } u1;
+    union { struct { int n2, a2[]; } s2; } u2;
+  } u;                                  // { dg-warning "invalid use" }
+};
+
+struct StructUnion6 {
+  union {
+    struct { int n1, a1[]; } s1;        // { dg-message "declared here" }
+    union { struct { int n2, a2[]; } s2; } u2;
+  } u;                                  // { dg-warning "invalid use" }
+};
+
+struct StructUnion7 {
+  union {
+    union {
+      struct { double n2, a2[]; } s2;   // { dg-message "declared here" }
+    } u2;
+    struct { int n1, a1[]; } s1;
+  } u;                                  // { dg-warning "invalid use" }
+};
+
+struct StructUnion8 {
+  struct {
+    union {
+      union {
+	struct { int n1, a1[]; } s1;    // { dg-error "not at end" }
+      } u1;
+      union {
+	struct { double n2, a2[]; } s2;
+      } u2;
+    } u;
+  } s1;
+
+  struct {
+    union {
+      union {
+	struct { int n1, a1[]; } s1;
+      } u1;
+      union {
+	struct { double n2, a2[]; } s2;
+      } u2;
+    } u; } s2;                              // { dg-message "next member" }
+};
+
+struct StructUnion9 {                       // { dg-message "in the definition" }
+  struct A1 {
+    union B1 {
+      union C1 {
+	struct Sx1 { int n1, a1[]; } sx1;   // { dg-error "not at end" }
+      } c1;
+      union D1 {
+	struct Sx2 { double n2, a2[]; } sx2;
+      } d1;
+    } b1;                                   // { dg-warning "invalid use" }
+  } a1;
+
+  struct A2 {
+    union B2 {
+      union C2 {
+	struct Sx3 { int n3, a3[]; } sx3;   // { dg-message "declared here" }
+      } c2;
+      union D2 { struct Sx4 { double n4, a4[]; } sx4; } d2;
+    } b2;                                   // { dg-warning "invalid use" }
+  } a2;                                     // { dg-message "next member" }
+};
diff --git a/gcc/testsuite/g++.dg/ext/flexary19.C b/gcc/testsuite/g++.dg/ext/flexary19.C
new file mode 100644
index 0000000..27d08ec
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/flexary19.C
@@ -0,0 +1,343 @@ 
+// { dg-do compile }
+// { dg-additional-options "-Wpedantic -Wno-error=pedantic" }
+
+// Verify that flexible array members are recognized as either valid
+// or invalid in anonymous structs (a G++ extension) and C++ anonymous
+// unions as well as in structs and unions that look anonymous but
+// aren't.
+struct S1
+{
+  int i;
+
+  // The following declares a named data member of an unnamed struct
+  // (i.e., it is not an anonymous struct).
+  struct {
+    int a[];        // { dg-error "in an otherwise empty" }
+  } s;
+};
+
+struct S2
+{
+  int i;
+
+  struct {
+    int a[];        // { dg-error "in an otherwise empty" }
+  } s[1];
+};
+
+struct S3
+{
+  int i;
+
+  struct {
+    int a[];        // { dg-error "in an otherwise empty" }
+  } s[];
+};
+
+struct S4
+{
+  int i;
+
+  struct {
+    int a[];        // { dg-error "in an otherwise empty" }
+  } s[2];
+};
+
+struct S5
+{
+  int i;
+
+  struct {
+    int a[];        // { dg-error "in an otherwise empty" }
+  } s[1][2];
+};
+
+struct S6
+{
+  int i;
+
+  struct {
+    int a[];        // { dg-error "in an otherwise empty" }
+  } s[][2];
+};
+
+struct S7
+{
+  int i;
+
+  struct {
+    int a[];        // { dg-error "in an otherwise empty" }
+  } *s;
+};
+
+struct S8
+{
+  int i;
+
+  struct {
+    int a[];        // { dg-error "in an otherwise empty" }
+  } **s;
+};
+
+struct S9
+{
+  int i;
+
+  struct {
+    int a[];        // { dg-error "in an otherwise empty" }
+  } *s[1];
+};
+
+struct S10
+{
+  int i;
+
+  struct {
+    int a[];        // { dg-error "in an otherwise empty" }
+  } *s[];
+};
+
+struct S11
+{
+  int i;
+
+  struct {
+    int a[];        // { dg-error "in an otherwise empty" }
+  } **s[1];
+};
+
+struct S12
+{
+  int i;
+
+  struct {
+    int a[];        // { dg-error "in an otherwise empty" }
+  } **s[];
+};
+
+struct S13
+{
+  int i;
+
+  struct {
+    int a[];        // { dg-error "in an otherwise empty" }
+  } **s[2];
+};
+
+struct S14
+{
+  int i;
+
+  struct {
+    int a[];        // { dg-error "in an otherwise empty" }
+  } &s;
+};
+
+struct S15
+{
+  int i;
+
+  typedef struct {
+    int a[];        // { dg-error "in an otherwise empty" }
+  } T15;
+};
+
+struct S16
+{
+  int i;
+
+  struct {          // { dg-warning "invalid use" }
+    // A flexible array as a sole member of an anonymous struct is
+    // rejected with an error in C mode but emits just a pedantic
+    // warning in C++.  Other than excessive pedantry there is no
+    // reason to reject it.
+    int a[];
+  };                // { dg-warning "anonymous struct" }
+};
+
+struct S17
+{
+  int i;
+
+  union {           // anonymous union
+    int a[];        // { dg-error "flexible array member in union" }
+  };
+};
+
+struct S18
+{
+  int i;
+
+  struct {
+    int j, a[];     // { dg-message "declared here" }
+  } s;              // { dg-warning "invalid use" }
+};
+
+struct S19
+{
+  int i;
+
+  struct {          // { dg-warning "invalid use" }
+    int j, a[];     // { dg-message "declared here" }
+  };                // { dg-warning "anonymous struct" }
+};
+
+struct S20
+{
+  static int i;
+  typedef int A[];
+
+  struct {
+    int j;
+    A a;            // { dg-message "declared here" }
+  } s;              // { dg-warning "invalid use" }
+};
+
+struct S21
+{
+  static int i;
+  typedef int A[];
+
+  struct {          // { dg-warning "invalid use" }
+    int j;
+    A a;            // { dg-message "declared here" }
+  };                // { dg-warning "anonymous struct" }
+};
+
+struct S22
+{
+  struct S22S {
+    static int i;
+
+    int a[];        // { dg-error "in an otherwise empty" }
+  } s;
+};
+
+struct S23
+{
+  struct {
+    static int i;   // { dg-error "static data member" }
+
+    int a[];        // { dg-error "in an otherwise empty" }
+  };                // { dg-warning "anonymous struct" }
+};
+
+struct S24
+{
+  static int i;
+
+  struct {
+    int a[];        // { dg-error "in an otherwise empty" }
+  } s;
+};
+
+struct S25
+{
+  int i;
+
+  struct {
+    int j, a[];     // { dg-message "declared here" }
+  } s;              // { dg-warning "invalid use" }
+
+  // Verify that a static data member of the enclosing class doesn't
+  // cause infinite recursion or some such badness.
+  static S25 s2;
+};
+
+struct S26
+{
+  template <class>
+  struct S26S {
+    static int a;
+  };
+
+  struct {
+    int a[];        // { dg-error "in an otherwise empty" }
+  } s;
+};
+
+struct S27
+{
+  S27 *p;
+  int a[];
+};
+
+struct S28
+{
+  struct A {
+    struct B {
+      S28 *ps28;
+      A   *pa;
+      B   *pb;
+    } b, *pb;
+    A *pa;
+  } a, *pa;
+
+  S28::A *pa2;
+  S28::A::B *pb;
+
+  int flexarray[];
+};
+
+// Verify that the notes printed along with the warnings point to the types
+// or members they should point to and mention the correct relationships
+// with the flexible array members.
+namespace Notes
+{
+union A
+{
+  struct {
+    struct {
+      int i, a[];   // { dg-message "declared here" }
+    } c;            // { dg-warning "invalid use" }
+  } d;
+  int j;
+};
+
+union B
+{
+  struct {
+    struct {        // { dg-warning "invalid use" }
+      int i, a[];   // { dg-message "declared here" }
+    };              // { dg-warning "anonymous struct" }
+  };                // { dg-warning "anonymous struct" }
+  int j;
+};
+
+}
+
+typedef struct Opaque* P29;
+struct S30 { P29 p; };
+struct S31 { S30 s; };
+
+typedef struct { } S32;
+typedef struct { S32 *ps32; } S33;
+typedef struct
+{
+  S33 *ps33;
+} S34;
+
+struct S35
+{
+  struct A {
+    int i1, a1[];
+  };
+
+  struct B {
+    int i2, a2[];
+  };
+
+  typedef struct {
+    int i3, a3[];
+  } C;
+
+  typedef struct {
+    int i4, a4[];
+  } D;
+
+  typedef A A2;
+  typedef B B2;
+  typedef C C2;
+  typedef D D2;
+};
+
diff --git a/gcc/testsuite/g++.dg/ext/flexary4.C b/gcc/testsuite/g++.dg/ext/flexary4.C
index 97ec625..29d6bdd 100644
--- a/gcc/testsuite/g++.dg/ext/flexary4.C
+++ b/gcc/testsuite/g++.dg/ext/flexary4.C
@@ -102,31 +102,28 @@  struct Sx17 {
   int a_0 [0];
 };
 
-// Empty structs are a GCC extension that (in C++ only) is treated
-// as if it had a single member of type char.  Therefore, a struct
+// An empty struct is treated as if it had a single member of type
+// char but the member cannot be accessed.  Therefore, a struct
 // containing a flexible array member followed by an empty struct
 // is diagnosed to prevent the former subobject from sharing space
 // with the latter.
 struct Sx18 {
   int a_x [];               // { dg-error "flexible array member" }
-  struct S { };
+  struct { /* empty */ } s;
 };
 
-// Anonymous structs and unions are another GCC extension.  Since
-// they cannot be named and thus used to store the size of a flexible
-// array member, a struct containing both is diagnosed as if
-// the flexible array member appeared alone.
+// Anonymous structs are a G++ extension.  Members of anonymous structs
+// are treated as if they were declared in the enclosing class.
 struct Sx19 {
-  struct S { };
-  union U { };
-  int a_x [];               // { dg-error "in an otherwise empty" }
+  struct { int i; };        // anonymous struct
+  int a_x [];
 };
 
-// Unlike in the case above, a named member of an anonymous struct
-// prevents a subsequent flexible array member from being diagnosed.
+// Unlike in the case above, a named struct is not anonymous and
+// so doesn't contribute its member to that of the enclosing struct.
 struct Sx20 {
-  struct S { } s;
-  int a_x [];
+  struct S { int i; };
+  int a_x [];               // { dg-error "in an otherwise empty" }
 };
 
 struct Sx21 {
@@ -298,6 +295,15 @@  struct Anon1 {
 
 ASSERT_AT_END (Anon1, good);
 
+struct NotAnon1 {
+  int n;
+  // The following is not an anonymous struct -- the type is unnamed
+  // but the object has a name.
+  struct {
+    int bad[];              // { dg-error "otherwise empty" }
+  } name;
+};
+
 struct Anon2 {
   struct {
     int n;
@@ -352,7 +358,6 @@  struct Anon7 {
   int n;
 };
 
-
 struct Six {
   int i;
   int a[];
diff --git a/gcc/testsuite/g++.dg/ext/flexary5.C b/gcc/testsuite/g++.dg/ext/flexary5.C
index 3e76d3e..d6d8e32 100644
--- a/gcc/testsuite/g++.dg/ext/flexary5.C
+++ b/gcc/testsuite/g++.dg/ext/flexary5.C
@@ -64,19 +64,29 @@  struct D5: E1, E2, NE { char a[]; };
 
 ASSERT_AT_END (D5, a);   // { dg-warning "offsetof within non-standard-layout" }
 
-struct A2x {
+struct A2x_1 {
   size_t n;
-  size_t a[];   // { dg-error "not at end of .struct D6.| D7.| D8." }
+  size_t a[];   // { dg-error "not at end of .struct D6." }
+};
+
+struct A2x_2 {
+  size_t n;
+  size_t a[];   // { dg-error "not at end of .struct D7." }
+};
+
+struct A2x_3 {
+  size_t n;
+  size_t a[];   // { dg-error "not at end of .struct D8." }
 };
 
 // Verify that the flexible array member in A2x above is diagnosed
 // for each of the three struct defintions below which also derive
 // from another struct with a flexible array member.
-struct D6: A2x, E1, A1x { };
-struct D7: E1, A2x, E2, A1x { };
-struct D8: E1, E2, A2x, A1x { };
+struct D6: A2x_1, E1, A1x { };
+struct D7: E1, A2x_2, E2, A1x { };
+struct D8: E1, E2, A2x_3, A1x { };
 
-struct DA2x: A2x { };
+struct DA2x: A2x_1 { };
 
 struct D9: DA2x, E1, E2 { };
 
@@ -194,16 +204,27 @@  struct NE2: NE { };
 struct D28: NE1, AA6x { };
 struct D29: AA6x, NE1 { };
 
-// Verify that a flexible array member in a virtual base class is not
-// diagnosed.
 struct A7x {
   size_t n;
-  size_t a[];
+  size_t a[];               // { dg-error "flexible array member .A7x::a. not at end of .struct D33." }
 };
 
+// Verify that a flexible array member in a virtual base class is not
+// diagnosed.
 struct DA7xV1: virtual A7x { };
 struct DA7xV2: virtual A7x { };
 
 struct D30: DA7xV1, DA7xV2 { };
 struct D31: DA7xV1, DA7xV2 { };
 struct D32: D30, D31 { };
+
+// Verify the diagnostic when the flexible array is in an anonymous struct.
+struct A8x {
+  struct {                  // { dg-message "next member .A8x::<unnamed struct> A8x::<anonymous>. declared here" }
+    size_t n;
+    size_t a[];
+  };
+};
+
+struct D33:                 // { dg-message "in the definition of .struct D33." }
+  A7x, A8x { };
diff --git a/gcc/testsuite/g++.dg/ext/flexary9.C b/gcc/testsuite/g++.dg/ext/flexary9.C
index 3228542..07eb966 100644
--- a/gcc/testsuite/g++.dg/ext/flexary9.C
+++ b/gcc/testsuite/g++.dg/ext/flexary9.C
@@ -281,15 +281,15 @@  struct S_S_S_x {
 
 struct Anon1 {
   int n;
-  struct {
-    int good[0];            // { dg-warning "zero-size array" }
+  struct {                  // { dg-warning "invalid use \[^\n\r\]* with a zero-size array" }
+    int good[0];            // { dg-warning "forbids zero-size array" }
   };                        // { dg-warning "anonymous struct" }
 };
 
 ASSERT_AT_END (Anon1, good);
 
 struct Anon2 {
-  struct {
+  struct {                  // { dg-warning "invalid use" }
     int n;
     struct {
       int good[0];          // { dg-warning "zero-size array" }
@@ -300,7 +300,7 @@  struct Anon2 {
 ASSERT_AT_END (Anon2, good);
 
 struct Anon3 {
-  struct {
+  struct {                  // { dg-warning "invalid use" }
     struct {
       int n;
       int good[0];          // { dg-warning "zero-size array" }
diff --git a/gcc/testsuite/g++.dg/torture/pr64312.C b/gcc/testsuite/g++.dg/torture/pr64312.C
index 85211f2..c7a56d7 100644
--- a/gcc/testsuite/g++.dg/torture/pr64312.C
+++ b/gcc/testsuite/g++.dg/torture/pr64312.C
@@ -44,7 +44,7 @@  class F
 {
 public:
   int nelems;
-  int elems[];
+  int elems[];   // { dg-error "not at end" }
   int *
   m_fn1 ()
   {
@@ -88,7 +88,7 @@  public:
       m_impl->~any_incrementable_iterator_interface ();
   }
   G m_buffer;
-  any_incrementable_iterator_interface *m_impl;
+  any_incrementable_iterator_interface *m_impl;   // { dg-message "next member" }
 };
 template <class Reference> class K : public I<any_iterator<Reference> >
 {