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])


   struct S1 {
     struct S2 { int i, a[]; } s2;
     union U { int x; } u;
   };

that need to be treated differently from this one:

   union U1 {
     struct S { int i, a[]; } s;
     union U2 { int x; } u2;
   };

Ah, I'm thinking of the following field as u/u2 rather than x.  Why
does it improve clarity to look inside U/U2 for a following field?

Are you asking if it makes for clearer diagnostics to have AFTER
point to the instance of the struct in which the overlapping member
is declared as opposed to the member itself?  I.e., instead of the
note for the second case above:

  warning: flexible array member ‘U1::S::a’ belonging to ‘union U1’
     struct S { int i, a[]; } s;
  note: overlaps member ‘int U1::U2::x’ declared here
     union U2 { int x; } u2;

print:

  note: overlaps member ‘U1::u2’ declared here
      union U2 { int x; } u2;

It wasn't a deliberate decision on my part and I don't know how much
it matters but I've added code to avoid descending into the struct
when a flexible array member has already been found.  It does point
to the whole struct as I think you were suggesting but it doesn't
obviate having to look inside nested structs otherwise or storing
the AFTER member.

6.7.2.1, p3:

   A structure or union shall not contain a member with incomplete
   or function type [...], except that the last member of a structure
   with more than one named member may have incomplete array type; such
   a structure (and any union containing, possibly recursively, a member
   that is such a structure) shall not be a member of a structure or
   an element of an array.

Ah, thanks.  I note that this says "structure or union" at the
beginning of the paragraph but not at the end, which suggests strongly
to me that such a structure can be a member of a union.

It would seem reasonable to allow (and the patch still does
in the very simple cases like the one below) but it's not my
understanding of the C11 constraints, nor is it apparently how
writers of other compilers have interpreted them.

Besides C11, I've been trying to make G++ diagnostic-compatible
with GCC (i.e, issue warnings or errors for the same code).  In
cases where the rules aren't crystal clear I've also been cross-
checking other compilers (usually Clang and EDG, and sometimes
also IBM XLC/C++, Intel ICC, and Oracle CC). All but XLC diagnose
this usage in C:

  struct S {
    union U {
      struct X { int i, a[]; } x;
    } u;
  };

Clang, for example, issues the following warning in both C and C++:

  'u' may not be nested in a struct due to flexible array member
      [-Wflexible-array-extensions]

Similarly, Oracle c99 issues warning:

  type of struct member "u" can not be derived from structure with
      flexible array member

It could be raised with WG14 for clarification or even proposed
as a change but given existing practice I don't think it would
be likely to gain committee support, or worth trying.  It seems
simpler and, IMO, more useful for portability to do what others
do, at least in the potentially problematic cases like the one
in c++/71912 where the members overlap.

+  /* 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 { };

I don't see any anonymous aggregates in this example, so how does
anonctx come into it?

If there is no anonctx then diagnose_flexrrays will have to figure
out whether the array is a member of an anonymous struct and if so,
find the struct of which it's effectively a member, and otherwise
use the array's immediate context.  The above was an example of
the result of a simple implementation of your suggestion.

And I don't understand why it would change the output on this testcase.

It doesn't seem to with the latest patch.  I don't know which among
the changes in it has had this effect.


As I said, the code could probably be tweaked to produce the same result
as it does now in both cases (anonymous and not), but at the cost
of additional complexity and, IMO, to the detriment of clarity.
What aspect of clarity do you find lacking in the current approach?

It just seems unnecessarily complicated.  anonctx is set to the
innermost non-anonymous-aggregate class containing the flexible array,
yes?  That is trivially derived from the array decl.

Looking at this more closely, it seems that the problem is that
"innermost non-anon class" isn't what you want for the example above;
for a that is A, and a *is* at the end of A.  So it seems that if a
were inside an anonymous struct inside A, you would get the same wrong
diagnostic output you were seeing with your anon_context function.

After looking into it some more I don't think it's possible to
determine whether a struct is anonymous from just the declaration
context of one of its members because of the ANON_AGGR_TYPE_P
"glitch" I mentioned before (returning true for unnamed structs).
It seems to me that to do that I would need to somehow follow the
path from an array's DECL_CONTEXT to the DECL_CONTEXT of each of
the "members" (anonymous structs, not just their types) it's
recursively defined in.  But given the "glitch", how would I tell
whether the struct is anonymous from just its type?  I can't think
of a way to do that.

But there was a subtle bug here that for the test case below made
the first warning say that A::...::a belonged to union A which was
wrong:

  union A {
    struct { struct { int i, a[]; } c; } d;
    int j;
  };

  union B {
    struct { struct { int i, a[]; }; };
    int j;
  };
t.C:2:30: warning: flexible array member ‘A::<anonymous struct>::<anonymous struct>::a’ belonging to ‘union A’ [-Wpedantic]
     struct { struct { int i, a[]; } c; } d;

t.C:7:30: warning: flexible array member ‘B::<anonymous struct>::<anonymous struct>::a’ belonging to ‘union B’ [-Wpedantic]
     struct { struct { int i, a[]; }; };

I've corrected the bug in the latest revision of the patch but
I have not removed anonctx because of the problem mentioned above.
If you're convinced it can be done please show me how.

Alternatively, if you're still bothered by this code I'd prefer to
remove the note rather than to keep trying to come up with ways to
detect the context somewhere else.

It looks like what I actually need here is anon_aggrname_p alone.

Why is that better than the anon_aggrname_p call inside
TYPE_ANONYMOUS_P?  The difference between TYPE_ANONYMOUS_P and
anon_aggrname_p (TYPE_IDENTIFIER (t)) is in its handling of

typedef struct { ... } name;

where TYPE_ANONYMOUS_P is false (because it has a name for linkage
purposes) but anon_aggrname_p (TYPE_IDENTIFIER (t)) is true.  I don't
see why you would care about this distinction.  The typedef doesn't
declare a member, so it seems uninteresting to check_flexarrays.

They both seem to work for the purposes of the patch.  Based on
what you said it sounds like anon_aggrname_p is more appropriate
because it answers the question I ask: "is the type unnamed?" and
not "is the type a class type or an enumerated type or is it
unnamed?" lile TYPE_ANONYMOUS_P, but I'm happy using whichever
you prefer.

Speaking of which, why does your latest patch look at typedefs?  What
are you missing by looking just at FIELD_DECLs?  Even anonymous unions
have a FIELD_DECL, though you may need to exempt them from the
DECL_ARTIFICIAL check.

It looks at typedefs in order to detect the problem in unnamed
structs like the one below:

  struct A { typedef struct { int a[]; } B; };

The unnamed struct is skipped in check_flexarrays (because it could
be anonymous) and so it's handled when struct A is.

Do you have ideas about how to improve the naming?  Perhaps change
TYPE_ANONYMOUS_P to TYPE_NO_LINKAGE_NAME?

I haven't thought about changing names but TYPE_NO_LINKAGE_NAME
seems better than TYPE_ANONYMOUS_P.  What I think might help in
general is a set of APIs whose names match well-known concepts
in the language.  As another example, besides TYPE_ANONYMOUS_P
it took me a while to grok the subtleties of macros like
DECL_ARTIFICIAL, DECL_SELF_REFERENCE_P, and especially
DECL_IMPLICIT_TYPEDEF_P when figuring out if a typedef is
an alias for an unnamed struct.  I had hoped for a more
straightforward way than this:

      /* 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)));

I realize that the front end's representation of the program is
populated piece by piece and that not all the pieces are in place
at every point, so maybe it's just a matter of a learning curve.
It just seems more steep than it should be.

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

gcc/cp/ChangeLog:
2016-08-02  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-08-02  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..05d97a7 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,169 @@ 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 enclosing class).  */
+	      check_flexarrays (eltype);
+	      continue;
+	    }
+
+	  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_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 +6860,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 +6877,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 +6894,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 +6913,113 @@ 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
+		 ? (fmem->anonctx
+		    ? G_("zero-size array member %qD belonging to %q#T")
+		    : G_("zero-size array member %qD"))
+		 : 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
+		 ? (fmem->anonctx
+		    ? G_("flexible array member %qD belonging to %q#T")
+		    : G_("flexible array member %qD"))
+		 : 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 +7032,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 +7041,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 +7069,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 +7086,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, 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 = 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 || !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/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..3d13f81
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/flexary18.C
@@ -0,0 +1,201 @@
+// 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;         // { Xdg-message "overlaps member" }
+  char t[];
+};
+
+struct baz {        // { dg-message "in the definition" }
+  union {
+    struct foo f;
+    struct bar b;   // { dg-message "overlaps member" }
+  } u;
+};
+
+struct xyyzy {      // { dg-message "in the definition" }
+  union {
+    struct {
+      int a;
+      char s[];     // { dg-warning "flexible array member" }
+    } f;
+    struct {
+      double d;     // { Xdg-message "overlaps member" }
+      char t[];
+    } b;            // { dg-message "overlaps member" }
+  } 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 { int n2, a2[]; } s2; } s3; // { dg-message "overlaps" }
+  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 { int n2, a2[]; } s3; } s4; // { dg-message "overlaps" }
+  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 { int n2, a2[]; } s2; } s3; // { dg-message "overlaps" }
+  struct { struct { int n3, a3[]; } s4; } s5;
+};
+
+union UnionStruct9 {
+  struct { struct { int n1, a1[]; } s1; } s2; // { dg-warning "flexible array" }
+  struct { struct { int n2, a2[]; } s3; } s4; // { dg-message "overlaps" }
+  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 { int n2, a2[]; } s2; } u2; // { dg-message "overlaps" }
+  } u;
+};
+
+struct StructUnion6 {
+  union {
+    struct { int n1, a1[]; } s1;               // { dg-warning "flexible array" }
+    union { struct { int n2, a2[]; } s2; } u2; // { dg-message "overlaps" }
+  } 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; } 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" }
+	// { dg-warning "flexible array" "" { target *-*-*-* } 185 }
+      } 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; } d2;// { dg-message "overlaps member" }
+    } b2; } 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..9311969
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/flexary19.C
@@ -0,0 +1,340 @@
+// { 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 {
+    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[];
+};
+
+// 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                         // { dg-message "in the definition of .union Notes::A." }
+{
+  struct {
+    struct {
+      int i, a[];               // { dg-warning "flexible array member .Notes::A::<anonymous struct>::<anonymous struct>::a." }
+      // { dg-bogus "belonging to" "bogus note" { target *-*-*-* } 287 }
+    } c;
+  } d;
+  int j;                        // { dg-message "overlaps member .int Notes::A::j. declared here" }
+};
+
+union B                         // { dg-message "in the definition of .union Notes::B." }
+{
+  struct {
+    struct {
+      int i, a[];               // { dg-warning "flexible array member .Notes::B::<anonymous struct>::<anonymous struct>::a. belonging to .union Notes::B." }
+    };                          // { dg-warning "anonymous structs" }
+  };                            // { dg-warning "anonymous structs" }
+  int j;                        // { dg-message "overlaps member .int Notes::B::j. declared here" }
+};
+
+}
+
+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..5cb34b3 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 { };
 
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]