This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

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


On 07/26/2016 12:53 PM, Jason Merrill wrote:
On 07/23/2016 01:18 PM, Martin Sebor wrote:
+  /* A pair of the first non-static non-empty data members 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 that the struct containing the flexible array
+     member or zero-length array is a member, or NULL when no such
+     union exists.  AFTER[0] refers to the first such data member
+     that is not a member of such a union.  */

This is pretty hard to follow, could you add an example?  Why do we want
to track these separately rather than look at DECL_CONTEXT at diagnostic
time?

Sure, I've added an example.

Whether or not a given flexible array member is valid depends not
only on the context in which it's defined (its enclosing class) but
also on the members of other classes whose objects may be defined
in the same or other contexts (e.g., enclosing structs).  I don't
know of a way to reach those other members from the context of
the ARRAY.


+  /* The type in which an anonymous struct or union containing ARRAY
+     is defined or null if no such anonymous struct or union exists.  */
+  tree anonctx;

It seems clearer to find this at diagnostic time by following TYPE_CONTEXT.

I tried this approach and while it's doable (with recursion) I'm
not happy with the results.  The diagnostics point to places that
I think are unclear.  For example, given:

  struct A { int i, a[]; };
  struct B { long j, b[]; };
  struct D: A, B { };

the current patch prints:

  error: flexible array member ‘A::a’ not at end of ‘struct D’
   struct A { int i, a[]; };
                       ^
  note: next member ‘long int B::j’ declared here
   struct B { long j, b[]; };
                   ^
  note: in the definition of ‘struct D’
   struct D: A, B { };
          ^

while using the suggested approach and determining the context from
ARRAY the error is:

 error: flexible array member ‘A::a’ not at end of ‘struct B’

That looks confusing to me.  I might be able to tweak the function
I wrote to do this (below) to get the same results but that would
likely come at the expense of clarity and more effort for (IMO)
little gain.

  static tree
  anon_context (tree t)
  {
    if (TREE_CODE (t) == FIELD_DECL)
      return anon_context (DECL_CONTEXT (t));

    return ANON_AGGR_TYPE_P (t) ? TYPE_CONTEXT (t) : t;
  }


      /* Find the next non-static data member if it exists.  */
      for (next = fld;
           (next = DECL_CHAIN (next))
             && TREE_CODE (next) != FIELD_DECL; );

I think next_initializable_field would be useful here.

The loop above advances to the next data member.  When FLD is (for
example) a flexible array member, next_initializable_field returns
the same argument again.  So I'm not sure the function would help
simplify the code (assuming that's what you were thinking).  But
the latest patch also looks for types and so it's even less likely
to be useful.

With more testing I realized that there were other invalid cases
that the function was missing so I enhanced it to cover those
(arrays, pointers, and references to member structs containing
flexible array members, and typedefs aliasing unnamed members
structs).


       if (TREE_CODE (fld) != TYPE_DECL
          && RECORD_OR_UNION_TYPE_P (fldtype)
-         && TYPE_ANONYMOUS_P (fldtype))
+         && VAR_DECL != TREE_CODE (fld)
+         && (FIELD_DECL != TREE_CODE (fld) || !DECL_FIELD_IS_BASE
(fld)))

Please put the constant on the RHS of comparisons.

Done.


It seems that you're only interested in FIELD_DECL here, so maybe move
up the

      /* Skip anything that's not a (non-static) data member.  */
      if (TREE_CODE (fld) != FIELD_DECL)
        continue;

and remove the checks for TYPE_DECL and VAR_DECL?  For that matter, you
could also move up the DECL_ARTIFICIAL check so you don't need to check
DECL_FIELD_IS_BASE.

Yes, that's clearer, thanks.


+         /* Is the member an anonymous struct or union?  */
+         bool anon_p = (!TYPE_ANONYMOUS_P (fldtype)
+                        || !DECL_NAME (fld)
+                        || anon_aggrname_p (DECL_NAME (fld)));

This looks like anon_p will be true for any non-static data member of
non-anonymous type?  Maybe you want ANON_AGGR_TYPE_P?

Yes, that works, thanks.


+      /* 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_p)
+        fmem->first = NULL_TREE;

Why is this needed?  For a non-anonymous member, presumably we'll give
an error when analyzing the type of the member on its own, so we
shouldn't need to complain again here.

Consider the definitions below.  In each case the unnamed struct
is processed first, before the enclosing struct and its members.
As a result, whether or not a member of its type has a name is
not known until the enclosing struct is processed.  This bit of
code is there to distinguish and correctly handle these cases.

  struct A {
    int i;
    struct { int a[]; };   // valid (in G++)
  };

and

  struct B {
    int i;
    struct { int a[]; } s;   // invalid in C or G++
  };


So we silently allow a zero-length array followed by a flexible array?
Why?

You mean as in the following?

  struct S {
    int i, a [0], b[];
  };

That's been accepted in both C and C++ and this patch doesn't change
it.  If you'd like to see it changed that's fine with me but I would
prefer to do it independently of this bug fix.

+        TREE_NO_WARNING (fmem->array) = 1;
+      }

There doesn't seem to be anything that checks TREE_NO_WARNING for the
zero-size array case.

Well spotted, thanks.


+        /* Avoid issuing further duiagnostics after the error above.  */

Typo.

  if (fmem == &flexmems
      && !TYPE_ANONYMOUS_P (t) && !anon_aggrname_p (TYPE_IDENTIFIER (t)))

I think you want ANON_AGGR_TYPE_P here, too.

Here, ANON_AGGR_TYPE_P(t) returns false for anonymous structs such
as the one below and using it would cause false positives:

  struct S { int i; struct { int a[]; }; };


I also wonder about integrating this with layout_class_type, since that
is also looking at layout issues.

I haven't investigated this but unless you are suggesting to simply
move the call to check_flexarrays to the end of layout_class_type,
it sounds like a bigger change than I would feel comfortable making
in 6.2.

I would prefer to keep the scope of this patch minimal so it can
be safely backported to 6.2.  For 7.0, given enough time in the
schedule, I'd like to fix 68489 - arrays of flexible array members
are silently accepted, and also the related 72753 and 72754 that
I just raised.  I suspect that will likely involve changing how
or from where these functions are invoked from (possibly also
from finish_decl) so it seems like a good time to look into
integrating it into layout_class_type.

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

gcc/cp/ChangeLog:
2016-07-29  Martin Sebor  <msebor@redhat.com>

	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.

gcc/testsuite/ChangeLog:
2016-07-29  Martin Sebor  <msebor@redhat.com>

	PR c++/71912
	* g++.dg/ext/flexary19.C: New test.
	* g++.dg/ext/flexary18.C: New test.
	* g++.dg/ext/flexary4.C: Correct the handling of anonymous structs.
	* 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 b2db7f8..ebaee58 100644
--- a/gcc/cp/class.c
+++ b/gcc/cp/class.c
@@ -147,11 +147,11 @@ 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);
+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 *);
@@ -6688,53 +6688,161 @@ 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;
+  /* A pair of the first non-static non-empty data members 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 that the struct containing the flexible array
+     member or zero-length array is a member, or NULL when no such
+     union exists.  AFTER[0] refers to the first such data member that
+     is not a member of such a union.
+     For example, in the following, the flexible array member
+     S::U::X::a overlaps S::U::Y::i and so AFTER[1] is set to refer to
+     the latter.  This potential problem is indepenent of union U's
+     membership in struct S.  In addition, in the definition of struct
+     S, S::U::x::a is followed by S::z, and so AFTER[0] is set to refer
+     to the latter.  The two problems result in two diagnostics, the
+     first one being a pedantic warning and the second a hard error.
+
+       struct S {
+         union U {
+           struct X { int i, a[]; } x;
+	   struct Y { long i, a[]; } y;
+	 } u;
+	 int z;
+       };
+  */
+  tree after[2];
+
+  /* The type in which an anonymous struct or union containing ARRAY
+     is defined or null if no such anonymous struct or union exists.  */
+  tree anonctx;
 };
 
 /* 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.  */
+   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 of which T is a member if one such
+   union 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 */)
 {
-  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_ANONYMOUS_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);
-	  continue;
-	}
+      /* Is FLD a typedef for an anonymous struct?  */
+      bool anon_type_p
+	= (TREE_CODE (fld) == TYPE_DECL
+	   && DECL_IMPLICIT_TYPEDEF_P (fld)
+	   && anon_aggrname_p (DECL_NAME (fld)));
 
-      /* 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 or typedef.  */
+      if ((DECL_ARTIFICIAL (fld) && !anon_type_p)
+	  || (TREE_CODE (fld) != FIELD_DECL && TREE_CODE (fld) != TYPE_DECL))
 	continue;
 
-      /* Skip virtual table pointers.  */
-      if (DECL_ARTIFICIAL (fld))
+      /* Type of the member.  */
+      tree fldtype = TREE_CODE (fld) == FIELD_DECL ? TREE_TYPE (fld) : 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 = TREE_CODE (fld) == FIELD_DECL ? fldtype : TREE_TYPE (fld);
+      if (eltype == error_mark_node)
+	return;
+
+      while (TREE_CODE (eltype) == ARRAY_TYPE
+	     || TREE_CODE (eltype) == POINTER_TYPE
+	     || TREE_CODE (eltype) == REFERENCE_TYPE)
+	eltype = TREE_TYPE (eltype);
+
+      if (TREE_CODE (fld) == TYPE_DECL
+	  && TYPE_CANONICAL (eltype) == TYPE_CANONICAL (t))
 	continue;
 
+      if (RECORD_OR_UNION_TYPE_P (eltype))
+	{
+	  if (anon_type_p)
+	    {
+	      /* Check the nested unnamed type referenced via a typedef
+		 independently of FMEM (since it's not a data member of
+		 the enclising class).  */
+	      check_flexarrays (eltype);
+	      continue;
+	    }
+
+	  if (eltype == fldtype || TYPE_ANONYMOUS_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;
+
+	      /* Does the field represent an anonymous struct?  */
+	      bool anon_p = !DECL_NAME (fld) && ANON_AGGR_TYPE_P (eltype);
+
+	      /* 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_p)
+		fmem->first = NULL_TREE;
+
+	      find_flexarrays (eltype, fmem, false, pun);
+
+	      if (fmem->array != array)
+		{
+		  /* If the member struct contains the first flexible array
+		     member, store the enclosing struct if it is anonymous.  */
+		  if (anon_p)
+		    fmem->anonctx = t;
+		  continue;
+		}
+	      else if (first && !array && !anon_p)
+		{
+		  /* 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;
+	    }
+	  else if (TREE_CODE (fld) == TYPE_DECL)
+	    continue;
+	}
+
       if (field_nonempty_p (fld))
 	{
 	  /* Remember the first non-static data member.  */
@@ -6744,8 +6852,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.  */
@@ -6761,8 +6869,8 @@ 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
@@ -6778,8 +6886,8 @@ 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;
 		}
 	    }
 	  else
@@ -6797,47 +6905,109 @@ 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_ANONYMOUS_P (t) || !fmem->array)
+  if (!fmem->array)
     return;
 
-  const char *msg = 0;
+  /* Issue errors first, and when no errors are found, then warnings
+     for flexible array members of structs in unions.  */
+  for (int in_union = false; in_union != 2; ++in_union) {
 
-  if (TYPE_DOMAIN (TREE_TYPE (fmem->array)))
-    {
-      if (fmem->after)
-	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");
+    /* The context of the flexible array member.  Either the struct
+       in which it's declared or, for anonymous structs and unions,
+       the struct/union of which the array is effectively a member.  */
+    tree fmemctx = fmem->anonctx ? fmem->anonctx : t;
 
-      if (msg && pedwarn (DECL_SOURCE_LOCATION (fmem->array),
-			  OPT_Wpedantic, msg, fmem->array, t))
+    const char *msg = 0;
 
-	inform (location_of (t), "in the definition of %q#T", t);
-    }
-  else
-    {
-      if (fmem->after)
-	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 (TYPE_DOMAIN (TREE_TYPE (fmem->array)))
+      {
+	if (fmem->after[in_union])
+	  msg = (in_union
+		 ? G_("zero-size array member %qD belonging to %q#T")
+		 : 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)
+	  {
+	    location_t loc = DECL_SOURCE_LOCATION (fmem->array);
 
-      if (msg)
-	{
-	  error_at (DECL_SOURCE_LOCATION (fmem->array), 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.
-	     Otherwise it should be obvious.  */
-	  if (fmem->after
-	      && (DECL_CONTEXT (fmem->after) != DECL_CONTEXT (fmem->array)))
-	      inform (DECL_SOURCE_LOCATION (fmem->after),
-		      "next member %q#D declared here",
-		      fmem->after);
-
-	  inform (location_of (t), "in the definition of %q#T", t);
-	}
-    }
+	    if (!TREE_NO_WARNING (fmem->array)
+		&& pedwarn (loc, OPT_Wpedantic,
+			    msg, fmem->array, fmemctx))
+	      {
+		inform (location_of (t), "in the definition of %q#T", fmemctx);
+
+		/* Prevent the same flexible array member from being diagnosed
+		   more than once if it happens to be nested in more than one
+		   union and overlap with another member.  This avoids multiple
+		   warnings for perverse cases like the following where
+		   U::U1::X::a1 would otherwise be diagnosed first when finishing
+		   the definition of U, followed by S::U1::X::a1 when completing
+		   the definition of S:
+		     struct S {
+		       union U {
+	                 union U1 { struct X { int n1, a1[]; } x1; } u1;
+			 union U2 { struct X { int n2, a2[]; } x1; } u2;
+		       } u;
+		     } s;
+		*/
+		TREE_NO_WARNING (fmem->array) = 1;
+	      }
+	  }
+      }
+    else
+      {
+	if (fmem->after[in_union])
+	  msg = (in_union
+		 ? G_("flexible array member %qD belonging to %q#T")
+		 : 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)
+	  {
+	    location_t loc = DECL_SOURCE_LOCATION (fmem->array);
+
+	    /* Has a diagnostic been issued?  */
+	    bool diagd = true;
+
+	    /* A union containing a struct with a flexible array member,
+	       followed by another member (of the union) is diagnosed
+	       with a warning for compatibility with GCC (C mode), even
+	       though it's not valid accoding to C11.  */
+	    if (in_union)
+	      {
+		diagd = (TREE_NO_WARNING (fmem->array)
+			 ? false : pedwarn (loc, OPT_Wpedantic,
+					    msg, fmem->array, fmemctx));
+	      }
+	    else
+	      error_at (loc, msg, fmem->array, fmemctx);
+
+	    TREE_NO_WARNING (fmem->array) = 1;
+
+	    /* In the unlikely event that the member following the flexible
+	       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 (diagd)
+	      {
+		if (fmem->after[in_union]
+		    && (in_union
+			|| (DECL_CONTEXT (fmem->after[in_union])
+			    != DECL_CONTEXT (fmem->array))))
+		  {
+		    inform (DECL_SOURCE_LOCATION (fmem->after[in_union]),
+			    (in_union ? "overlaps member %q#D declared here"
+			     : "next member %q#D declared here"),
+			    fmem->after[in_union]);
+		    inform (location_of (t), "in the definition of %q#T", t);
+		  }
+	      }
+	  }
+      }
+  }
 }
 
 
@@ -6850,7 +7020,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
@@ -6858,18 +7029,21 @@ 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[false] && fmem->after[true])
     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);
@@ -6883,7 +7057,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, true);
     }
 
   if (fmem == &flexmems)
@@ -6900,17 +7074,24 @@ 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, true);
 	}
     }
 
-  /* Search the members of the current (derived) class.  */
-  find_flexarrays (t, fmem);
+  bool anon_p = TYPE_ANONYMOUS_P (t) || anon_aggrname_p (TYPE_IDENTIFIER (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 || !anon_p)
+    find_flexarrays (t, fmem, base_p || fam != fmem->array);
+
+  if (fmem == &flexmems && !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/g++.dg/compat/struct-layout-1_generate.c b/gcc/testsuite/g++.dg/compat/struct-layout-1_generate.c
index 9fab3a8..b102306 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..5478a87
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/flexary18.C
@@ -0,0 +1,217 @@
+// PR c++/71912 - [6/7 regression] flexible array in struct in union rejected
+// { dg-do compile }
+// { dg-additional-options "-Wpedantic -Wno-error=pedantic" }
+
+namespace pr71912 {
+
+struct foo {
+  int a;
+  char s[];         // { dg-warning "flexible array member" }
+};
+
+struct bar {
+  double d;         // { dg-message "overlaps member" }
+  char t[];
+};
+
+struct baz {        // { dg-message "in the definition" }
+  union {
+    struct foo f;
+    struct bar b;
+  } u;
+};
+
+struct xyyzy {      // { dg-message "in the definition" }
+  union {
+    struct {
+      int a;
+      char s[];     // { dg-warning "flexible array member" }
+    } f;
+    struct {
+      double d;     // { dg-message "overlaps member" }
+      char t[];
+    } b;
+  } u;
+};
+
+struct baz b;
+struct xyyzy x;
+
+}
+
+// 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;        // { dg-warning "flexible array member" }
+  int n2;                           // { dg-message "overlaps" }
+};
+
+union UnionStruct2 {
+  struct { int n1, a1[]; } s1;      // { dg-warning "flexible array member" }
+  struct { int n2, a2[]; } s2;      // { dg-message "overlaps" }
+  int n3;
+};
+
+union UnionStruct3 {
+  struct { int n1, a1[]; } s1;      // { dg-warning "flexible array member" }
+  struct { double n2, a2[]; } s2;   // { dg-message "overlaps" }
+  char n3;
+};
+
+union UnionStruct4 {
+  struct { int n1, a1[]; } s1;      // { dg-warning "flexible array member" }
+  struct {
+    struct { double n2, a2[]; } s2; // { dg-message "overlaps" }
+  } s3;
+  char n3;
+};
+
+union UnionStruct5 {
+  struct { struct { int n1, a1[]; } s1; } s2; // { dg-warning "flexible array" }
+  struct { double n2, a2[]; } s3;             // { dg-message "overlaps" }
+  char n3;
+};
+
+union UnionStruct6 {
+  struct { struct { int n1, a1[]; } s1; } s2; // { dg-warning "flexible array" }
+  struct {
+    struct { double n2, a2[]; } s3;           // { dg-message "overlaps" }
+  } s4;
+  char n3;
+};
+
+union UnionStruct7 {
+  struct { int n1, a1[]; } s1;                // { dg-warning "flexible array" }
+  struct { double n2, a2[]; } s2;             // { dg-message "overlaps" }
+  struct { struct { int n3, a3[]; } s3; } s4;
+};
+
+union UnionStruct8 {
+  struct { int n1, a1[]; } s1;                // { dg-warning "flexible array" }
+  struct {
+    struct { double n2, a2[]; } s2;           // { dg-message "overlaps" }
+  } s3;
+  struct { struct { int n3, a3[]; } s4; } s5;
+};
+
+union UnionStruct9 {
+  struct { struct { int n1, a1[]; } s1; } s2;// { dg-warning "flexible array" }
+  struct {
+    struct { double n2, a2[]; } s3;          // { dg-message "overlaps" }
+  } s4;
+  struct { struct { int n3, a3[]; } s5; } s6;
+};
+
+struct StructUnion1 {
+  union {
+    struct { int n1, a1[]; } s1;              // { dg-warning "flexible array" }
+    struct { double n2, a2[]; } s2;           // { dg-message "overlaps" }
+    char n3;
+  } u;
+};
+
+// 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-warning "flexible array" }
+    } u1;
+    union {
+      struct { double n2, a2[]; } s2;   // { dg-message "overlaps" }
+    } u2;
+  } u;
+};
+
+struct StructUnion6 {
+  union {
+    struct { int n1, a1[]; } s1;        // { dg-warning "flexible array" }
+    union {
+      struct { double n2, a2[]; } s2;   // { dg-message "overlaps" }
+    } u2;
+  } u;
+};
+
+struct StructUnion7 {
+  union {
+    union {
+      struct { double n2, a2[]; } s2;   // { dg-warning "flexible array" }
+    } u2;
+    struct { int n1, a1[]; } s1;        // { dg-message "overlaps" }
+  } u;
+};
+
+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;                                // { dg-message "next member" }
+  } s2;
+};
+
+struct StructUnion9 {                       // { dg-message "in the definition" }
+  struct A1 {
+    union B1 {
+      union C1 {
+	struct Sx1 { int n1, a1[]; } sx1;   // { dg-error "not at end" }
+	// { dg-warning "flexible array" "" { target *-*-*-* } 198 }
+      } c1;
+      union D1 {
+	struct Sx2 { double n2, a2[]; } sx2;
+      } d1;
+    } b1;
+  } a1;
+
+  struct A2 {
+    union B2 {                              // { dg-message "in the definition" }
+      union C2 {
+	struct Sx3 { int n3, a3[]; } sx3;   // { dg-warning "flexible array" }
+      } c2;
+      union D2 {
+	struct Sx3 { double n4, a4[]; } sx4;// { dg-message "overlaps member" }
+      } d2;
+    } b2;                                   // { dg-message "next member" }
+  } a2;
+};
diff --git a/gcc/testsuite/g++.dg/ext/flexary19.C b/gcc/testsuite/g++.dg/ext/flexary19.C
new file mode 100644
index 0000000..dfc72d4
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/flexary19.C
@@ -0,0 +1,312 @@
+// { 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++Nanonymous
+// 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 {
+    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[];
+  } s;
+};
+
+struct S19
+{
+  int i;
+
+  struct {
+    int j, a[];
+  };                // { dg-warning "anonymous struct" }
+};
+
+struct S20
+{
+  static int i;
+  typedef int A[];
+
+  struct {
+    int j;
+    A a;
+  } s;
+};
+
+struct S21
+{
+  static int i;
+  typedef int A[];
+
+  struct {
+    int j;
+    A a;
+  };                // { 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[];
+  } s;
+
+  // 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[];
+};
+
+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/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> >
 {

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]