public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* [PATCH] accept flexible arrays in struct in unions (c++/71912 - [6/7 regression])
@ 2016-07-22 19:14 Martin Sebor
  2016-07-23 17:18 ` Martin Sebor
  0 siblings, 1 reply; 27+ messages in thread
From: Martin Sebor @ 2016-07-22 19:14 UTC (permalink / raw)
  To: Gcc Patch List, Jason Merrill

[-- Attachment #1: Type: text/plain, Size: 1145 bytes --]

r231665 committed in the 6.0 cycle tightened up the checking of
flexible array members in C++ with the goal of rejecting code
that could lead to hard to find bugs, while at the same time
accepting (with a warning) benign extensions also accepted in
C mode.

c++/71912 is a complaint that G++ rejects one such extension:
defining structs with flexible array members in a union.  When
reviewing the code that causes it to be rejected I realized
that it was, in fact, meant to be accepted and is rejected due
to a bug.

The attached patch fixes that bug and implements more robust
checking for this class of problems.  As in C mode, it accepts
"benign" instances of this idiom (with a pedantic warning) while
still rejecting the erroneous cases.

There are outstanding problems with flexible array members in
C++, such as c++/68489 - arrays of flexible array members are
silently accepted.  In a follow-on patch I will address at
least a part of the problem.  I submit this one separately
since it's considered a regression and as such might be
a candidate for backporting to the 6.x branch.

Is this patch okay for trunk?  For 6.x?

Thanks
Martin

[-- Attachment #2: gcc-71912.diff --]
[-- Type: text/x-patch, Size: 21576 bytes --]

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

gcc/cp/ChangeLog:
2016-07-22  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-22  Martin Sebor  <msebor@redhat.com>

	PR c++/71912
	* g++.dg/ext/flexary18.C: New test.
	* g++.dg/ext/flexary4.C: Correct the handling of anonymous structs.

diff --git a/gcc/cp/class.c b/gcc/cp/class.c
index b2db7f8..65b373f 100644
--- a/gcc/cp/class.c
+++ b/gcc/cp/class.c
@@ -149,9 +149,9 @@ static bool accessible_nvdtor_p (tree);
 
 /* Used by find_flexarrays and related.  */
 struct flexmems_t;
-static void find_flexarrays (tree, flexmems_t *);
+static void find_flexarrays (tree, flexmems_t *, bool = false, tree = NULL_TREE);
 static void diagnose_flexarrays (tree, const flexmems_t *);
-static void check_flexarrays (tree, flexmems_t * = NULL);
+static void check_flexarrays (tree, bool = false, flexmems_t * = NULL);
 static void check_bases (tree, int *, int *);
 static void check_bases_and_members (tree);
 static tree create_vtable_ptr (tree, tree *);
@@ -6688,25 +6688,45 @@ field_nonempty_p (const_tree fld)
   return false;
 }
 
-/* Used by find_flexarrays and related.  */
-struct flexmems_t {
+/* Used by find_flexarrays and related members.  */
+
+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.  */
+  tree after[2];
+
+  /* The type in which an anonymous struct or union containing ARRAY
+     is defined..  */
+  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, otherwsie 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 */)
 {
+  /* Set the "pointer" to the outsermost 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), next; fld; fld = next)
     {
       /* Find the next non-static data member if it exists.  */
@@ -6714,17 +6734,48 @@ find_flexarrays (tree t, flexmems_t *fmem)
 	   (next = DECL_CHAIN (next))
 	     && TREE_CODE (next) != FIELD_DECL; );
 
+      /* Type of the member.  */
       tree fldtype = TREE_TYPE (fld);
+
       if (TREE_CODE (fld) != TYPE_DECL
 	  && RECORD_OR_UNION_TYPE_P (fldtype)
-	  && TYPE_ANONYMOUS_P (fldtype))
+	  && (FIELD_DECL != TREE_CODE (fld) || !DECL_FIELD_IS_BASE (fld)))
 	{
-	  /* 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;
+	  /* Descend into the member struct or union and try to find
+	     a flexible array member or zero-length array among its
+	     members.  */
+
+	  tree first = fmem->first;
+	  tree array = fmem->array;
+
+	  find_flexarrays (fldtype, 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 it isn't anonymous and a prior 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 (TYPE_ANONYMOUS_P (fldtype))
+		{
+		  if (!DECL_NAME (fld) || anon_aggrname_p (DECL_NAME (fld)))
+		    fmem->anonctx = t;
+		  else if (fmem->first == first)
+		    fmem->first = NULL_TREE;
+		}
+
+	      continue;
+	    }
+
+	  /* 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;
 	}
 
       /* Skip anything that's not a (non-static) data member.  */
@@ -6744,8 +6795,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 +6812,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 +6829,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 +6848,94 @@ 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");
+    /* If the The declaration context */
+    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 && pedwarn (DECL_SOURCE_LOCATION (fmem->array),
+			    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 the following where
+	       U::U1::X::a1 would otherwise be diagnosed first followed by
+	       S::U1::X::a1:
+	         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);
 
-      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);
-	}
-    }
+	    /* 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)
+	      {
+		if (!TREE_NO_WARNING (fmem->array))
+		  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, point to it.
+	       Otherwise it should be obvious.  */
+	    if (fmem->after[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);
+
+	    /* Avoid issuing further duiagnostics after the error above.  */
+	    if (!in_union)
+	      break;
+	  }
+      }
+  }
 }
 
 
@@ -6850,7 +6948,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, bool base_p /* = false */,
+		  flexmems_t *fmem /* = NULL */)
 {
   /* 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,14 +6957,17 @@ 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, true, fmem);
     }
 
   /* Recursively check the base classes.  */
@@ -6883,7 +6985,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), true, fmem);
     }
 
   if (fmem == &flexmems)
@@ -6900,17 +7002,21 @@ 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, true, fmem);
 	}
     }
 
-  /* Search the members of the current (derived) class.  */
-  find_flexarrays (t, fmem);
+  /* Search the members of the current (possibly derived) class.  */
+  find_flexarrays (t, fmem, base_p || fam != fmem->array);
 
-  if (fmem == &flexmems)
+  if (fmem == &flexmems
+      && !TYPE_ANONYMOUS_P (t) && !anon_aggrname_p (TYPE_IDENTIFIER (t)))
     {
-      /* 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/ext/flexary18.C b/gcc/testsuite/g++.dg/ext/flexary18.C
new file mode 100644
index 0000000..ddf55a8
--- /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 {
+  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 {
+      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;
+  } a2;
+};
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[];

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH] accept flexible arrays in struct in unions (c++/71912 - [6/7 regression])
  2016-07-22 19:14 [PATCH] accept flexible arrays in struct in unions (c++/71912 - [6/7 regression]) Martin Sebor
@ 2016-07-23 17:18 ` Martin Sebor
  2016-07-26 18:53   ` Jason Merrill
  0 siblings, 1 reply; 27+ messages in thread
From: Martin Sebor @ 2016-07-23 17:18 UTC (permalink / raw)
  To: Gcc Patch List, Jason Merrill

[-- Attachment #1: Type: text/plain, Size: 1673 bytes --]

On 07/22/2016 01:14 PM, Martin Sebor wrote:
> r231665 committed in the 6.0 cycle tightened up the checking of
> flexible array members in C++ with the goal of rejecting code
> that could lead to hard to find bugs, while at the same time
> accepting (with a warning) benign extensions also accepted in
> C mode.
>
> c++/71912 is a complaint that G++ rejects one such extension:
> defining structs with flexible array members in a union.  When
> reviewing the code that causes it to be rejected I realized
> that it was, in fact, meant to be accepted and is rejected due
> to a bug.
>
> The attached patch fixes that bug and implements more robust
> checking for this class of problems.  As in C mode, it accepts
> "benign" instances of this idiom (with a pedantic warning) while
> still rejecting the erroneous cases.
>
> There are outstanding problems with flexible array members in
> C++, such as c++/68489 - arrays of flexible array members are
> silently accepted.  In a follow-on patch I will address at
> least a part of the problem.  I submit this one separately
> since it's considered a regression and as such might be
> a candidate for backporting to the 6.x branch.
>
> Is this patch okay for trunk?  For 6.x?

I had looked at the wrong set of test results before posting
the first patch and missed a bunch of test suite failures due
to a bug that let the find_flexarray function descend into
static data members when looking for flexible array members,
causing infinite recursion.

The attached patch fixes that, adds a new test to exercise
the previously untested static data members, and annotates
an invalid regression test with a gd-error directive.

Martin

[-- Attachment #2: gcc-71912.diff --]
[-- Type: text/x-patch, Size: 24482 bytes --]

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

gcc/cp/ChangeLog:
2016-07-23  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-23  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.
diff --git a/gcc/cp/class.c b/gcc/cp/class.c
index b2db7f8..c2c4219 100644
--- a/gcc/cp/class.c
+++ b/gcc/cp/class.c
@@ -149,9 +149,9 @@ static bool accessible_nvdtor_p (tree);
 
 /* Used by find_flexarrays and related.  */
 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,25 +6688,45 @@ 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.  */
+  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 */)
 {
+  /* 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), next; fld; fld = next)
     {
       /* Find the next non-static data member if it exists.  */
@@ -6714,17 +6734,51 @@ find_flexarrays (tree t, flexmems_t *fmem)
 	   (next = DECL_CHAIN (next))
 	     && TREE_CODE (next) != FIELD_DECL; );
 
+      /* Type of the member.  */
       tree fldtype = TREE_TYPE (fld);
+
       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)))
 	{
-	  /* 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;
+	  /* Descend into the non-static member struct or union and try
+	     to find a flexible array member or zero-length array among
+	     its members.  */
+
+	  tree first = fmem->first;
+	  tree array = fmem->array;
+
+	  /* Is the member an anonymous struct or union?  */
+	  bool anon_p = (!TYPE_ANONYMOUS_P (fldtype)
+			 || !DECL_NAME (fld)
+			 || anon_aggrname_p (DECL_NAME (fld)));
+
+	  /* 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 (fldtype, 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;
+	    }
+
+	  /* 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;
 	}
 
       /* Skip anything that's not a (non-static) data member.  */
@@ -6744,8 +6798,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 +6815,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 +6832,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 +6851,94 @@ 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");
+    /* If the The declaration context */
+    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 && pedwarn (DECL_SOURCE_LOCATION (fmem->array),
+			    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 the following where
+	       U::U1::X::a1 would otherwise be diagnosed first followed by
+	       S::U1::X::a1:
+	         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);
 
-      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);
-	}
-    }
+	    /* 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)
+	      {
+		if (!TREE_NO_WARNING (fmem->array))
+		  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, point to it.
+	       Otherwise it should be obvious.  */
+	    if (fmem->after[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);
+
+	    /* Avoid issuing further duiagnostics after the error above.  */
+	    if (!in_union)
+	      break;
+	  }
+      }
+  }
 }
 
 
@@ -6850,7 +6951,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,14 +6960,17 @@ 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.  */
@@ -6883,7 +6988,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 +7005,21 @@ 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);
+  /* Search the members of the current (possibly derived) class.  */
+  find_flexarrays (t, fmem, base_p || fam != fmem->array);
 
-  if (fmem == &flexmems)
+  if (fmem == &flexmems
+      && !TYPE_ANONYMOUS_P (t) && !anon_aggrname_p (TYPE_IDENTIFIER (t)))
     {
-      /* 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/ext/flexary18.C b/gcc/testsuite/g++.dg/ext/flexary18.C
new file mode 100644
index 0000000..ddf55a8
--- /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 {
+  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 {
+      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;
+  } 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..5fd236a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/flexary19.C
@@ -0,0 +1,117 @@
+// { dg-do compile }
+// { dg-additional-options "-Wpedantic -Wno-error=pedantic" }
+
+struct S1
+{
+  int i;
+
+  struct {
+    int a[];        // { dg-error "in an otherwise empty" }
+  } s;
+};
+
+struct S2
+{
+  int i;
+
+  struct {
+    int a[];
+  };                // { dg-warning "anonymous struct" }
+};
+
+struct S3
+{
+  int i;
+
+  union {          // anonymous union
+    int a[];       // { dg-error "flexible array member in union" }
+  };
+};
+
+struct S4
+{
+  int i;
+
+  struct {
+    int j, a[];
+  } s;
+};
+
+struct S5
+{
+  int i;
+
+  struct {
+    int j, a[];
+  };                // { dg-warning "anonymous struct" }
+};
+
+struct S6
+{
+  static int i;
+
+  struct {
+    int j, a[];
+  } s;
+};
+
+struct S7
+{
+  static int i;
+
+  struct {
+    int j, a[];
+  };                // { dg-warning "anonymous struct" }
+};
+
+struct S8
+{
+  struct S8S {
+    static int i;
+
+    int a[];        // { dg-error "in an otherwise empty" }
+  } s;
+};
+
+struct S9
+{
+  struct {
+    static int i;   // { dg-error "static data member" }
+
+    int a[];        // { dg-error "in an otherwise empty" }
+  };                // { dg-warning "anonymous struct" }
+};
+
+struct S10
+{
+  static int i;
+
+  struct {
+    int a[];        // { dg-error "in an otherwise empty" }
+  } s;
+};
+
+struct S11
+{
+  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 S11 s2;
+};
+
+struct S12
+{
+  template <class>
+  struct S12S {
+    static int a;
+  };
+
+  struct {
+    int a[];        // { dg-error "in an otherwise empty" }
+  } s;
+};
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> >
 {

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH] accept flexible arrays in struct in unions (c++/71912 - [6/7 regression])
  2016-07-23 17:18 ` Martin Sebor
@ 2016-07-26 18:53   ` Jason Merrill
  2016-07-29 23:22     ` Martin Sebor
  0 siblings, 1 reply; 27+ messages in thread
From: Jason Merrill @ 2016-07-26 18:53 UTC (permalink / raw)
  To: Martin Sebor, Gcc Patch List

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?

> +  /* 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.

>       /* 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.

>        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.

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.

> +         /* 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?

> +	  /* 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.

>               /* Replace the zero-length array if it's been stored and
>                  reset the after pointer.  */
>               if (TYPE_DOMAIN (TREE_TYPE (fmem->array)))
>                 {
>                   fmem->after[bool (pun)] = NULL_TREE;
>                   fmem->array = fld;
>                 }

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

> +	  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, 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 the following where
> +	       U::U1::X::a1 would otherwise be diagnosed first followed by
> +	       S::U1::X::a1:
> +	         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;
> +	  }

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

> +	    /* 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.

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

Jason

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH] accept flexible arrays in struct in unions (c++/71912 - [6/7 regression])
  2016-07-26 18:53   ` Jason Merrill
@ 2016-07-29 23:22     ` Martin Sebor
  2016-07-31 16:28       ` Jason Merrill
  0 siblings, 1 reply; 27+ messages in thread
From: Martin Sebor @ 2016-07-29 23:22 UTC (permalink / raw)
  To: Jason Merrill, Gcc Patch List

[-- Attachment #1: Type: text/plain, Size: 7349 bytes --]

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

[-- Attachment #2: gcc-71912.diff --]
[-- Type: text/x-patch, Size: 34264 bytes --]

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> >
 {

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH] accept flexible arrays in struct in unions (c++/71912 - [6/7 regression])
  2016-07-29 23:22     ` Martin Sebor
@ 2016-07-31 16:28       ` Jason Merrill
  2016-07-31 20:27         ` Martin Sebor
  0 siblings, 1 reply; 27+ messages in thread
From: Jason Merrill @ 2016-07-31 16:28 UTC (permalink / raw)
  To: Martin Sebor; +Cc: Gcc Patch List

On Fri, Jul 29, 2016 at 7:22 PM, Martin Sebor <msebor@gmail.com> wrote:
> 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.

Not from the context of the array, but when we see a field following the 
flexible array (or aggregate ending in a flexible array), can't we look 
at the DECL_CONTEXT of that field and see whether it's a union or not?

> +     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


"independent"

> +     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;

> +       };

Hmm, I'm not convinced that the first problem is really a problem.  Only 
one of the union members is active at any time, so overlapping with 
another union member seems is irrelevant.

I also can't find the language in the C standard that prohibits nesting 
a struct ending with a flexible array in another struct or union, do you 
have a citation?

>>> +  /* 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 (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[]; }; };

Ah, it hasn't been set yet here because we don't know yet whether we're 
declaring a named member or an anonymous aggregate.  So yes, 
TYPE_ANONYMOUS_P is the right test here.  But why do you also check 
anon_aggrname_p directly, when it's used to implement TYPE_ANONYMOUS_P?

>> 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.

Makes sense.

Jason

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH] accept flexible arrays in struct in unions (c++/71912 - [6/7 regression])
  2016-07-31 16:28       ` Jason Merrill
@ 2016-07-31 20:27         ` Martin Sebor
  2016-08-01 14:22           ` Jason Merrill
  0 siblings, 1 reply; 27+ messages in thread
From: Martin Sebor @ 2016-07-31 20:27 UTC (permalink / raw)
  To: Jason Merrill; +Cc: Gcc Patch List

On 07/31/2016 10:28 AM, Jason Merrill wrote:
> On Fri, Jul 29, 2016 at 7:22 PM, Martin Sebor <msebor@gmail.com> wrote:
>> 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.
>
> Not from the context of the array, but when we see a field following the
> flexible array (or aggregate ending in a flexible array), can't we look
> at the DECL_CONTEXT of that field and see whether it's a union or not?

I don't think that would work in cases like this:

   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;
   };

>
>> +     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
>
>
> "independent"
>
>> +     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;
>> +       };
>
> Hmm, I'm not convinced that the first problem is really a problem.  Only
> one of the union members is active at any time, so overlapping with
> another union member seems is irrelevant.
>
> I also can't find the language in the C standard that prohibits nesting
> a struct ending with a flexible array in another struct or union, do you
> have a citation?

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.

>>>> +  /* 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.  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?

>>>>   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[]; }; };
>
> Ah, it hasn't been set yet here because we don't know yet whether we're
> declaring a named member or an anonymous aggregate.  So yes,
> TYPE_ANONYMOUS_P is the right test here.  But why do you also check
> anon_aggrname_p directly, when it's used to implement TYPE_ANONYMOUS_P?

Because TYPE_ANONYMOUS_P is set for unnamed structs that are not
anonymous, as in

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

It looks like what I actually need here is anon_aggrname_p alone.
Let me change that before committing the patch or posting a new
version if one is still needed.

FWIW, I spent lots of time last week learning how these different
macros map on to language features and under what circumstances.
I find some of them quite hard to work with because they add
a layer of complexity between concepts in the language and their
implementation in GCC.  In cases like this one when their names
are deceptively close to the language concepts but they don't
quite map 1-to-1, they can be downright misleading.

Martin

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH] accept flexible arrays in struct in unions (c++/71912 - [6/7 regression])
  2016-07-31 20:27         ` Martin Sebor
@ 2016-08-01 14:22           ` Jason Merrill
  2016-08-02 21:00             ` Martin Sebor
  0 siblings, 1 reply; 27+ messages in thread
From: Jason Merrill @ 2016-08-01 14:22 UTC (permalink / raw)
  To: Martin Sebor; +Cc: Gcc Patch List

On Sun, Jul 31, 2016 at 4:27 PM, Martin Sebor <msebor@gmail.com> wrote:
> On 07/31/2016 10:28 AM, Jason Merrill wrote:
>> On Fri, Jul 29, 2016 at 7:22 PM, Martin Sebor <msebor@gmail.com> wrote:
>>> 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.
>>
>> Not from the context of the array, but when we see a field following the
>> flexible array (or aggregate ending in a flexible array), can't we look
>> at the DECL_CONTEXT of that field and see whether it's a union or not?
>
> I don't think that would work in cases like this:
>
>   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?

>>> +     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 independent 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;
>>> +       };
>>
>>
>> Hmm, I'm not convinced that the first problem is really a problem.  Only
>> one of the union members is active at any time, so overlapping with
>> another union member seems is irrelevant.
>>
>> I also can't find the language in the C standard that prohibits nesting
>> a struct ending with a flexible array in another struct or union, do you
>> have a citation?
>
> 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.

>>>>> +  /* 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.

> 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.

>>>>>   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[]; }; };
>>
>> Ah, it hasn't been set yet here because we don't know yet whether we're
>> declaring a named member or an anonymous aggregate.  So yes,
>> TYPE_ANONYMOUS_P is the right test here.  But why do you also check
>> anon_aggrname_p directly, when it's used to implement TYPE_ANONYMOUS_P?
>
> Because TYPE_ANONYMOUS_P is set for unnamed structs that are not
> anonymous, as in
>
>   struct S { int i; struct { int a[]; } s; };

Yes, but so is anon_aggrname_p (TYPE_IDENTIFIER).  And isn't that what
you want here?  When we call check_flexarrays for the nested struct
itself, we've only seen as far as the closing brace, so we don't know
if the struct will declare a named member or not.

> 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.

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.

> Let me change that before committing the patch or posting a new
> version if one is still needed.
>
> FWIW, I spent lots of time last week learning how these different
> macros map on to language features and under what circumstances.
> I find some of them quite hard to work with because they add
> a layer of complexity between concepts in the language and their
> implementation in GCC.  In cases like this one when their names
> are deceptively close to the language concepts but they don't
> quite map 1-to-1, they can be downright misleading.

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

Jason

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH] accept flexible arrays in struct in unions (c++/71912 - [6/7 regression])
  2016-08-01 14:22           ` Jason Merrill
@ 2016-08-02 21:00             ` Martin Sebor
  2016-08-02 22:26               ` Jason Merrill
  0 siblings, 1 reply; 27+ messages in thread
From: Martin Sebor @ 2016-08-02 21:00 UTC (permalink / raw)
  To: Jason Merrill; +Cc: Gcc Patch List

[-- Attachment #1: Type: text/plain, Size: 9369 bytes --]

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

[-- Attachment #2: gcc-71912.diff --]
[-- Type: text/x-patch, Size: 37062 bytes --]

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> >
 {

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH] accept flexible arrays in struct in unions (c++/71912 - [6/7 regression])
  2016-08-02 21:00             ` Martin Sebor
@ 2016-08-02 22:26               ` Jason Merrill
  2016-08-03  2:13                 ` Martin Sebor
  2016-08-03 19:10                 ` Martin Sebor
  0 siblings, 2 replies; 27+ messages in thread
From: Jason Merrill @ 2016-08-02 22:26 UTC (permalink / raw)
  To: Martin Sebor; +Cc: Gcc Patch List

On Tue, Aug 2, 2016 at 5:00 PM, Martin Sebor <msebor@gmail.com> wrote:
>>>    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.

Agreed, thanks.

>>> 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.

I definitely agree that following the lead of the C front end is the
right thing to do here.  But the C front end doesn't complain about
the definition of union U, only about the use of union U in struct S.
So I still think the first problem mentioned in your comment isn't
actually a problem.

>>>>>>> +  /* 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).

ANON_AGGR_TYPE_P should never be true for an unnamed class that is not
an anonymous aggregate (union or struct).  The glitch you mentioned
before was that it is sometimes false for an anonymous struct, which
happens when if we check ANON_AGGR_TYPE_P before we see the ; and so
we don't yet know that it's an anonymous struct, we only know it's
unnamed.

When we're looking at it in the context of the enclosing class,
ANON_AGGR_TYPE_P should always be correct.  If it isn't, let's fix
that bug.

> 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.

You should be able to rely on ANON_AGGR_TYPE_P and use TYPE_CONTEXT.

> I have not removed anonctx because of the problem mentioned above.
> If you're convinced it can be done please show me how.

Does the above help?

>>> 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?" like TYPE_ANONYMOUS_P, but I'm happy using whichever
> you prefer.

Let's use the macro.

>> 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.

Hmm, doesn't that mean that

typedef struct { int a[]; } B;

is never handled?  Perhaps you want to do this check from
cp_parser_simple_declaration, when we know whether or not there's a
declarator?

>> 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.

Or perhaps TYPE_UNNAMED_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 suppose DECL_SELF_REFERENCE_P could be renamed to
DECL_INJECTED_CLASS_NAME_P, but that's what it means.  I'll add that
term to the comment.

> 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 would suggest looking for the FIELD_DECL for the anonymous aggregate
instead, i.e.

TREE_CODE (fld) == FIELD_DECL && ANON_AGGR_TYPE_P (TREE_TYPE (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.

Sure.  Ideas about improvement are welcome.

Jason

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH] accept flexible arrays in struct in unions (c++/71912 - [6/7 regression])
  2016-08-02 22:26               ` Jason Merrill
@ 2016-08-03  2:13                 ` Martin Sebor
  2016-08-03 18:05                   ` Jason Merrill
  2016-09-14 17:05                   ` Martin Sebor
  2016-08-03 19:10                 ` Martin Sebor
  1 sibling, 2 replies; 27+ messages in thread
From: Martin Sebor @ 2016-08-03  2:13 UTC (permalink / raw)
  To: Jason Merrill; +Cc: Gcc Patch List

[-- Attachment #1: Type: text/plain, Size: 3313 bytes --]

>> 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.
>
> I definitely agree that following the lead of the C front end is the
> right thing to do here.  But the C front end doesn't complain about
> the definition of union U, only about the use of union U in struct S.
> So I still think the first problem mentioned in your comment isn't
> actually a problem.

Hmm.  I see what you mean.  That the C wording makes union U below
valid

   union U {
     struct X { int i, a[]; } x;
     struct Y { long i, a[]; } y;
   };

but its use in struct S below invalid:

   struct S { union U u; };

C does seem to say that though I confess it doesn't make sense
to me.  Let me see what it would take to match C here.

> ANON_AGGR_TYPE_P should never be true for an unnamed class that is not
> an anonymous aggregate (union or struct).  The glitch you mentioned
> before was that it is sometimes false for an anonymous struct, which
> happens when if we check ANON_AGGR_TYPE_P before we see the ; and so
> we don't yet know that it's an anonymous struct, we only know it's
> unnamed.
>
> When we're looking at it in the context of the enclosing class,
> ANON_AGGR_TYPE_P should always be correct.  If it isn't, let's fix
> that bug.

ANON_AGGR_TYPE_P is correct in the context of the enclosing class,
but it isn't "correct" when the struct is being finished, before
the enclosing class.  That's why check_flexarrays skips unnamed
structs, but it doesn't mean diagnose_flexarrays couldn't use it.
I hadn't realized that.

>
>> 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.
>
> You should be able to rely on ANON_AGGR_TYPE_P and use TYPE_CONTEXT.
>
>> I have not removed anonctx because of the problem mentioned above.
>> If you're convinced it can be done please show me how.
>
> Does the above help?

Yes.  I actually had it mostly working before (the anon_context
function I had pasted into one my earlier replies).  After restoring
the function and correcting a silly bug in it, it works for simple
cases of structs and their members, and with some additional
conditioning it also works for base and derived classes.  Attached
is the delta.

The change let me replace an if statement and the ANONCTX data
member with a new function plus a conditional.  I'll let you be
the judge but to me it doesn't seem like an improvement.

> Hmm, doesn't that mean that
>
> typedef struct { int a[]; } B;
>
> is never handled?  Perhaps you want to do this check from
> cp_parser_simple_declaration, when we know whether or not there's a
> declarator?

Yes, it does mean that and it's one of the outstanding bugs that
still need fixing (in 7.0).  Same way top-level arrays aren't
handled.

Martin

[-- Attachment #2: gcc-71912-1.diff --]
[-- Type: text/x-patch, Size: 3349 bytes --]

diff --git a/gcc/cp/class.c b/gcc/cp/class.c
index 05d97a7..6232070 100644
--- a/gcc/cp/class.c
+++ b/gcc/cp/class.c
@@ -6721,10 +6721,6 @@ struct flexmems_t
        };
   */
   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
@@ -6826,14 +6822,9 @@ find_flexarrays (tree t, flexmems_t *fmem, bool base_p,
 	      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)
+		continue;
+
+	      if (first && !array && !anon_p)
 		{
 		  /* Restore the FIRST member reset above if no flexible
 		     array member has been found in this member's struct.  */
@@ -6904,6 +6895,22 @@ find_flexarrays (tree t, flexmems_t *fmem, bool base_p,
     }
 }
 
+/* Return the type in which the member T is effectively declared in.
+   This is either the member's enclosing struct for ordinary members,
+   or for anonymous structs and unions, the first non-anonymou struct
+   or union they are declared in.  */
+
+static tree
+anon_context (tree t)
+{
+  if (TREE_CODE (t) == FIELD_DECL)
+    return anon_context (DECL_CONTEXT (t));
+
+  bool anon_p = ANON_AGGR_TYPE_P (t);
+
+  return anon_p ? anon_context (TYPE_CONTEXT (t)) : t;
+}
+
 /* 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.  */
@@ -6913,7 +6920,7 @@ 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 (!fmem->array)
+  if (!fmem->array || (fmem->first && !fmem->after[0] && !fmem->after[1]))
     return;
 
   /* Issue errors first, and when no errors are found, then warnings
@@ -6923,7 +6930,10 @@ diagnose_flexarrays (tree t, const flexmems_t *fmem)
     /* 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;
+    tree fmemctx = anon_context (fmem->array);
+    bool anon_p = fmemctx != DECL_CONTEXT (fmem->array);
+    if (!anon_p)
+      fmemctx = t;
 
     const char *msg = 0;
 
@@ -6931,7 +6941,7 @@ diagnose_flexarrays (tree t, const flexmems_t *fmem)
       {
 	if (fmem->after[in_union])
 	  msg = (in_union
-		 ? (fmem->anonctx
+		 ? (anon_p
 		    ? 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"));
@@ -6970,7 +6980,7 @@ diagnose_flexarrays (tree t, const flexmems_t *fmem)
       {
 	if (fmem->after[in_union])
 	  msg = (in_union
-		 ? (fmem->anonctx
+		 ? (anon_p
 		    ? 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"));

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH] accept flexible arrays in struct in unions (c++/71912 - [6/7 regression])
  2016-08-03  2:13                 ` Martin Sebor
@ 2016-08-03 18:05                   ` Jason Merrill
  2016-09-14 17:05                   ` Martin Sebor
  1 sibling, 0 replies; 27+ messages in thread
From: Jason Merrill @ 2016-08-03 18:05 UTC (permalink / raw)
  To: Martin Sebor; +Cc: Gcc Patch List

On Tue, Aug 2, 2016 at 10:13 PM, Martin Sebor <msebor@gmail.com> wrote:
> The change let me replace an if statement and the ANONCTX data
> member with a new function plus a conditional.  I'll let you be
> the judge but to me it doesn't seem like an improvement.

You can use the existing context_for_name_lookup instead of a new function.

>> Hmm, doesn't that mean that
>>
>> typedef struct { int a[]; } B;
>>
>> is never handled?  Perhaps you want to do this check from
>> cp_parser_simple_declaration, when we know whether or not there's a
>> declarator?
>
> Yes, it does mean that and it's one of the outstanding bugs that
> still need fixing (in 7.0).  Same way top-level arrays aren't
> handled.

If you're going to temporarily handle member typedefs here, please add
a prominent FIXME about removing that handling when typedefs get fixed
properly.

Jason

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH] accept flexible arrays in struct in unions (c++/71912 - [6/7 regression])
  2016-08-02 22:26               ` Jason Merrill
  2016-08-03  2:13                 ` Martin Sebor
@ 2016-08-03 19:10                 ` Martin Sebor
  2016-08-03 20:02                   ` Jason Merrill
  1 sibling, 1 reply; 27+ messages in thread
From: Martin Sebor @ 2016-08-03 19:10 UTC (permalink / raw)
  To: Jason Merrill; +Cc: Gcc Patch List

>>> 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.
>
> Or perhaps TYPE_UNNAMED_P.

TYPE_UNNAMED_P would work but it wouldn't be a replacement for
TYPE_ANONYMOUS_P.

It sounds like TYPE_ANONYMOUS_P is the right name and the problem
is that the value it returns isn't accurate until the full context
to which it applies has been seen.

I wonder if the right solution to this class of problems (which
are probably unavoidable in the front end as the tree is being
constructed), is to design an API that prevents using these
"unreliable" queries until they can return a reliable result.
With this approach, as each tree node is being constructed, its
"dynamic" type would reflect only the most specific entity that
has been determined so far (e.g., here, the type would be STRUCT
but not ANONYMOUS_STRUCT, with the latter "derived" from the
former). I suspect that implementing this model using C++
polymorphism would be far too big and slow to be practical but
it could be done via some lighter-weight mechanism that would
avoid these problems.

Martin

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH] accept flexible arrays in struct in unions (c++/71912 - [6/7 regression])
  2016-08-03 19:10                 ` Martin Sebor
@ 2016-08-03 20:02                   ` Jason Merrill
  2016-08-03 20:23                     ` Martin Sebor
  0 siblings, 1 reply; 27+ messages in thread
From: Jason Merrill @ 2016-08-03 20:02 UTC (permalink / raw)
  To: Martin Sebor; +Cc: Gcc Patch List

On Wed, Aug 3, 2016 at 3:10 PM, Martin Sebor <msebor@gmail.com> wrote:
>>>> 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.
>>
>> Or perhaps TYPE_UNNAMED_P.
>
> TYPE_UNNAMED_P would work but it wouldn't be a replacement for
> TYPE_ANONYMOUS_P.
>
> It sounds like TYPE_ANONYMOUS_P is the right name and the problem
> is that the value it returns isn't accurate until the full context
> to which it applies has been seen.

I think you're thinking of ANON_AGGR_TYPE_P, which identifies
anonymous structs/unions; TYPE_ANONYMOUS_P identifies unnamed classes.

> I wonder if the right solution to this class of problems (which
> are probably unavoidable in the front end as the tree is being
> constructed), is to design an API that prevents using these
> "unreliable" queries until they can return a reliable result.

It would be possible to change ANON_AGGR_TYPE_P to require
COMPLETE_TYPE_P, but a lot of uses will need to be adjusted to avoid
crashing.

Jason

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH] accept flexible arrays in struct in unions (c++/71912 - [6/7 regression])
  2016-08-03 20:02                   ` Jason Merrill
@ 2016-08-03 20:23                     ` Martin Sebor
  2016-08-03 21:53                       ` Jason Merrill
  0 siblings, 1 reply; 27+ messages in thread
From: Martin Sebor @ 2016-08-03 20:23 UTC (permalink / raw)
  To: Jason Merrill; +Cc: Gcc Patch List

On 08/03/2016 02:01 PM, Jason Merrill wrote:
> On Wed, Aug 3, 2016 at 3:10 PM, Martin Sebor <msebor@gmail.com> wrote:
>>>>> 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.
>>>
>>> Or perhaps TYPE_UNNAMED_P.
>>
>> TYPE_UNNAMED_P would work but it wouldn't be a replacement for
>> TYPE_ANONYMOUS_P.
>>
>> It sounds like TYPE_ANONYMOUS_P is the right name and the problem
>> is that the value it returns isn't accurate until the full context
>> to which it applies has been seen.
>
> I think you're thinking of ANON_AGGR_TYPE_P, which identifies
> anonymous structs/unions; TYPE_ANONYMOUS_P identifies unnamed classes.

Doh!  You're right.  I let the name confuse me again. Clearly
TYPE_ANONYMOUS_P isn't the best name since it doesn't correspond
to the C/C++ concept of an anonymous struct or union.  TYPE_UNNAMED
would be better (the same can be said about the C++ diagnostics that
refer to unnamed structs as <anonymous struct>.)

>
>> I wonder if the right solution to this class of problems (which
>> are probably unavoidable in the front end as the tree is being
>> constructed), is to design an API that prevents using these
>> "unreliable" queries until they can return a reliable result.
>
> It would be possible to change ANON_AGGR_TYPE_P to require
> COMPLETE_TYPE_P, but a lot of uses will need to be adjusted to avoid
> crashing.

No, crashing shouldn't happen.  It shouldn't be possible to call
the function unless/until the node that represents the concept
has been fully constructed. Using C++ syntax:

   void foo (tree *t)
   {
     if (ANONYMOUS_STRUCT *anon = dynamic_cast<ANONYMOUS_STRUCT*>(t))
       anon->function_only_defined_in_anonymous_struct ();
   }

I was hoping something like this was close to what someone (Andrew?)
has been working on.

Martin

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH] accept flexible arrays in struct in unions (c++/71912 - [6/7 regression])
  2016-08-03 20:23                     ` Martin Sebor
@ 2016-08-03 21:53                       ` Jason Merrill
  0 siblings, 0 replies; 27+ messages in thread
From: Jason Merrill @ 2016-08-03 21:53 UTC (permalink / raw)
  To: Martin Sebor; +Cc: Gcc Patch List

On Wed, Aug 3, 2016 at 4:23 PM, Martin Sebor <msebor@gmail.com> wrote:
> On 08/03/2016 02:01 PM, Jason Merrill wrote:
>> On Wed, Aug 3, 2016 at 3:10 PM, Martin Sebor <msebor@gmail.com> wrote:
>>>>>>
>>>>>> 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.
>>>>
>>>> Or perhaps TYPE_UNNAMED_P.
>>>
>>> TYPE_UNNAMED_P would work but it wouldn't be a replacement for
>>> TYPE_ANONYMOUS_P.
>>>
>>> It sounds like TYPE_ANONYMOUS_P is the right name and the problem
>>> is that the value it returns isn't accurate until the full context
>>> to which it applies has been seen.
>>
>> I think you're thinking of ANON_AGGR_TYPE_P, which identifies
>> anonymous structs/unions; TYPE_ANONYMOUS_P identifies unnamed classes.
>
> Doh!  You're right.  I let the name confuse me again. Clearly
> TYPE_ANONYMOUS_P isn't the best name since it doesn't correspond
> to the C/C++ concept of an anonymous struct or union.  TYPE_UNNAMED
> would be better (the same can be said about the C++ diagnostics that
> refer to unnamed structs as <anonymous struct>.)

I'll make this change; sorry for the merge conflict it will cause.

>>> I wonder if the right solution to this class of problems (which
>>> are probably unavoidable in the front end as the tree is being
>>> constructed), is to design an API that prevents using these
>>> "unreliable" queries until they can return a reliable result.
>>
>> It would be possible to change ANON_AGGR_TYPE_P to require
>> COMPLETE_TYPE_P, but a lot of uses will need to be adjusted to avoid
>> crashing.
>
> No, crashing shouldn't happen.  It shouldn't be possible to call
> the function unless/until the node that represents the concept
> has been fully constructed. Using C++ syntax:
>
>   void foo (tree *t)
>   {
>     if (ANONYMOUS_STRUCT *anon = dynamic_cast<ANONYMOUS_STRUCT*>(t))
>       anon->function_only_defined_in_anonymous_struct ();
>   }
>
> I was hoping something like this was close to what someone (Andrew?)
> has been working on.

I think for a while he was working on changing trees to use
inheritance rather than a discriminated union, but don't think he is
anymore.

Jason

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH] accept flexible arrays in struct in unions (c++/71912 - [6/7 regression])
  2016-08-03  2:13                 ` Martin Sebor
  2016-08-03 18:05                   ` Jason Merrill
@ 2016-09-14 17:05                   ` Martin Sebor
  2016-09-16 18:40                     ` Jason Merrill
  1 sibling, 1 reply; 27+ messages in thread
From: Martin Sebor @ 2016-09-14 17:05 UTC (permalink / raw)
  To: Jason Merrill; +Cc: Gcc Patch List

[-- Attachment #1: Type: text/plain, Size: 1496 bytes --]

Attached is an updated patch that does away with the "overlapping"
warning and instead issues the same warnings as the C front end
(though with more context).

In struct flexmems_t I've retained the NEXT array even though only
the first element is used to diagnose problems.  The second element
is used by the find_flexarrays function to differentiate structs
with flexible array members in unions (which are valid) from those
in structs (which are not).

FWIW, I think this C restriction on structs is unnecessary and will
propose to remove it so that's valid to define a struct that contains
another struct (possibly recursively) with a flexible array member.
I.e., I think this should be made valid in C (and accepted without
the pedantic warning that GCC, and with this patch also G++, issues):

   struct X { int i, a[]; };
   struct S1 { struct X x; };
   struct S2 { struct S1 s1; };

z.C:2:22: warning: invalid use of ‘struct X’ with a flexible array 
member in ‘struct S1’ [-Wpedantic]
  struct S1 { struct X x; };
                       ^
z.C:1:21: note: array member ‘int X::a []’ declared here
  struct X { int i, a[]; };
                      ^
z.C:3:23: warning: invalid use of ‘struct X’ with a flexible array 
member in ‘struct S2’ [-Wpedantic]
  struct S2 { struct S1 s1; };
                        ^~
z.C:1:21: note: array member ‘int X::a []’ declared here
  struct X { int i, a[]; };
                      ^

Martin

[-- Attachment #2: gcc-71912.diff --]
[-- Type: text/x-patch, Size: 38762 bytes --]

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.
	(anon_context, 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 f7147e6..6ae4fe2 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 *);
@@ -6711,53 +6712,155 @@ 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 the outermost struct of which T 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);
-	  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_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;
+
+	      /* 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,
+			       !pstr && TREE_CODE (t) == RECORD_TYPE ? fld : pstr);
+
+	      if (fmem->array != array)
+		continue;
+
+	      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.  */
@@ -6767,8 +6870,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.  */
@@ -6784,13 +6887,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
 	{
@@ -6801,16 +6907,55 @@ 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;
+	    }
 	}
     }
 }
 
+/* Return the type in which the member T is effectively declared.
+   This is either the member's enclosing struct for ordinary members,
+   or for anonymous structs and unions, the first non-anonymous struct
+   or union they are declared in.  */
+
+static tree
+anon_context (tree t)
+{
+  if (TREE_CODE (t) == FIELD_DECL)
+    return anon_context (DECL_CONTEXT (t));
+
+  bool anon_p = ANON_AGGR_TYPE_P (t);
+
+  return anon_p ? anon_context (TYPE_CONTEXT (t)) : t;
+}
+
+/* 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.  */
@@ -6818,49 +6963,79 @@ 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;
+    }
+
+  /* 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 = anon_context (fmem->array);
+  bool anon_p = fmemctx != DECL_CONTEXT (fmem->array);
+  if (!anon_p)
+    fmemctx = t;
+
+  /* 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, fmemctx))
+	    {
+	      inform (location_of (t), "in the definition of %q#T", fmemctx);
+	      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, fmemctx);
 
 	  /* 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);
 }
 
 
@@ -6873,7 +7048,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
@@ -6881,18 +7057,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);
@@ -6906,7 +7084,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)
@@ -6923,17 +7101,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/flexary17.C b/gcc/testsuite/g++.dg/ext/flexary17.C
new file mode 100644
index 0000000..b44e512
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/flexary17.C
@@ -0,0 +1,63 @@
+
+// PR c++/68489 - arrays of flexible array members are silently accepted
+// { dg-do compile }
+// { dg-options "-Wno-error=pedantic" }
+
+struct S1 {
+  struct X1 { int n, a []; } x [1];
+};
+
+struct S2 {
+  struct X2 { int n, a []; } x [2];     // { dg-error "not at end" }
+};
+
+struct S3 {
+  struct X3 { int n, a []; } x [0];     // { dg-error "not at end" }
+};
+
+struct S4 {
+  struct Y4 {
+    struct X4 { int n, a []; } x;
+  } y [1];
+};
+
+struct S5 {
+  struct Y5 {
+    struct X5 { int n, a []; } x;       // { dg-error "not at end" }
+  } y [2];
+};
+
+struct S6 {
+  struct Y6 {
+    struct X6 { int n, a []; } x;       // { dg-error "not at end" }
+  } y [0];
+};
+
+struct S7 {
+  struct Z7 {
+    struct Y7 {
+      struct X7 { int n, a []; } x;
+    } y;
+  } z [1];
+};
+
+struct S8 {
+  struct Z8 {
+    struct Y8 {
+      struct X8 { int n, a []; } x;     // { dg-error "not at end" }
+    } y;
+  } z [2];
+};
+
+struct S9 {
+  struct Z9 {
+    struct Y9 {
+      struct X9 { int n, a []; } x;     // { dg-error "not at end" }
+    } y;
+  } z [0];
+};
+
+
+struct X {
+  int n, a[];
+} x [2];   // { dg-error "not at end" "PR c++/68489" { xfail *-*-*-* } }
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..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/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> >
 {

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH] accept flexible arrays in struct in unions (c++/71912 - [6/7 regression])
  2016-09-14 17:05                   ` Martin Sebor
@ 2016-09-16 18:40                     ` Jason Merrill
  2016-09-20 17:12                       ` Martin Sebor
  0 siblings, 1 reply; 27+ messages in thread
From: Jason Merrill @ 2016-09-16 18:40 UTC (permalink / raw)
  To: Martin Sebor; +Cc: Gcc Patch List

On 09/14/2016 01:03 PM, Martin Sebor wrote:
> Attached is an updated patch that does away with the "overlapping"
> warning and instead issues the same warnings as the C front end
> (though with more context).
>
> In struct flexmems_t I've retained the NEXT array even though only
> the first element is used to diagnose problems.  The second element
> is used by the find_flexarrays function to differentiate structs
> with flexible array members in unions (which are valid) from those
> in structs (which are not).
>
> FWIW, I think this C restriction on structs is unnecessary and will
> propose to remove it so that's valid to define a struct that contains
> another struct (possibly recursively) with a flexible array member.
> I.e., I think this should be made valid in C (and accepted without
> the pedantic warning that GCC, and with this patch also G++, issues):

Agreed.

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

We talked earlier about handling typedefs in 
cp_parser_{simple,single}_declaration, so that we catch

typedef struct { int a[]; } B;

or at least adding a FIXME comment here explaining that looking at 
typedefs is a temporary hack.

> +      /* Type of the member.  */
> +      tree fldtype = TREE_CODE (fld) == FIELD_DECL ? TREE_TYPE (fld) : fld;

Why set "fldtype" to be a TYPE_DECL rather than its type?

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

Given the above, this seems equivalent to

tree eltype = TREE_TYPE (fld);

> +      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 (field_nonempty_p (fld))
>         {
...
>           /* 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->array && !fmem->after[bool (pun)])
>             fmem->after[bool (pun)] = fld;
>         }

Can we do this in one place rather than two?

> +         if (eltype == fldtype || TYPE_UNNAMED_P (eltype))

This is excluding arrays, so we don't diagnose, say,

> struct A
> {
>   int i;
>   int ar[];
> };
>
> struct B {
>   A a[2];
> };

Should we complain elsewhere about an array of a class with a flexible 
array member?

> +             /* Does the field represent an anonymous struct?  */
> +             bool anon_p = !DECL_NAME (fld) && ANON_AGGR_TYPE_P (eltype);

You don't need to check DECL_NAME; ANON_AGGR_TYPE_P by itself indicates 
that we're dealing with an anonymous struct/union.

>    Similarly, PSTR is set to the outermost struct of which T is a member
>    if one such struct exists, otherwise to NULL.  */
...
>               find_flexarrays (eltype, fmem, false, pun,
>                                !pstr && TREE_CODE (t) == RECORD_TYPE ? fld : pstr);
...
> +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)))

PSTR is documented to be the outermost struct, but it (and thus 
fmem->enclosing) end up being a data member of that outermost struct, 
which is why you need to take its DECL_CONTEXT.  What's the advantage of 
doing that over passing t itself to the recursive call?

> +  /* 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 = anon_context (fmem->array);
> +  bool anon_p = fmemctx != DECL_CONTEXT (fmem->array);
> +  if (!anon_p)
> +    fmemctx = t;

Why do you need to do something different here based on anon_p?  I don't 
see why that would affect whether we want to look through intermediate 
non-anonymous classes.

> +      check_flexarrays (basetype, fmem, true);

Please decorate boolean literal arguments like this with the name of the 
parameter, e.g. /*base_p*/true.

> +  bool maybe_anon_p = anon_aggrname_p (TYPE_IDENTIFIER (t));

Now we can use TYPE_UNNAMED_P.

Jason

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH] accept flexible arrays in struct in unions (c++/71912 - [6/7 regression])
  2016-09-16 18:40                     ` Jason Merrill
@ 2016-09-20 17:12                       ` Martin Sebor
  2016-09-21 20:55                         ` Jason Merrill
  0 siblings, 1 reply; 27+ messages in thread
From: Martin Sebor @ 2016-09-20 17:12 UTC (permalink / raw)
  To: Jason Merrill; +Cc: Gcc Patch List

[-- Attachment #1: Type: text/plain, Size: 8609 bytes --]

On 09/16/2016 12:19 PM, Jason Merrill wrote:
> On 09/14/2016 01:03 PM, Martin Sebor wrote:
>> Attached is an updated patch that does away with the "overlapping"
>> warning and instead issues the same warnings as the C front end
>> (though with more context).
>>
>> In struct flexmems_t I've retained the NEXT array even though only
>> the first element is used to diagnose problems.  The second element
>> is used by the find_flexarrays function to differentiate structs
>> with flexible array members in unions (which are valid) from those
>> in structs (which are not).
>>
>> FWIW, I think this C restriction on structs is unnecessary and will
>> propose to remove it so that's valid to define a struct that contains
>> another struct (possibly recursively) with a flexible array member.
>> I.e., I think this should be made valid in C (and accepted without
>> the pedantic warning that GCC, and with this patch also G++, issues):
>
> Agreed.

This is now N2083:
   http://www.open-std.org/jtc1/sc22/wg14/www/docs/n2083.htm

>
>> +      /* 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)));
>
> We talked earlier about handling typedefs in
> cp_parser_{simple,single}_declaration, so that we catch
>
> typedef struct { int a[]; } B;
>
> or at least adding a FIXME comment here explaining that looking at
> typedefs is a temporary hack.

I've added a FIXME comment.

>
>> +      /* Type of the member.  */
>> +      tree fldtype = TREE_CODE (fld) == FIELD_DECL ? TREE_TYPE (fld)
>> : fld;
>
> Why set "fldtype" to be a TYPE_DECL rather than its type?

I'm not sure I understand the question but (IIRC) the purpose of
this code is to detect invalid uses of flexible array members in
typedefs such as this:

    struct S { typedef struct { int i, a[]; } X2 [2]; };

Sadly, it doesn't do anything for

    struct X { int i, a[]; };
    struct S { typedef struct X X2 [2]; };

>> +      /* 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);
>
> Given the above, this seems equivalent to
>
> tree eltype = TREE_TYPE (fld);

Yes.

>
>> +      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 (field_nonempty_p (fld))
>>         {
> ...
>>           /* 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->array && !fmem->after[bool (pun)])
>>             fmem->after[bool (pun)] = fld;
>>         }
>
> Can we do this in one place rather than two?

You mean merge the two assignments to fmem->after[bool (pun)] into
one?  I'm not sure.  There's quite a bit of conditional logic in
between them, including a recursive call that might set fmem.  What
would be gained by rewriting it all to merge the two assignments?
If it could be done I worry that I'd just end up duplicating the
conditions instead.

>
>> +         if (eltype == fldtype || TYPE_UNNAMED_P (eltype))
>
> This is excluding arrays, so we don't diagnose, say,
>
>> struct A
>> {
>>   int i;
>>   int ar[];
>> };
>>
>> struct B {
>>   A a[2];
>> };
>
> Should we complain elsewhere about an array of a class with a flexible
> array member?

Yes, there are outstanding gaps/bugs that remain to be fixed.  I tried
to limit this fix to not much more than the reported regression.  I did
end up tightening other things up as I found more gaps during testing
(though I probably should have resisted).  Sometimes I find it hard to
know where to stop but I feel like I need to draw the line somewhere.
I of course agree that the outstanding bugs should be fixed as well
but I'd prefer to do it in a separate change.

>> +             /* Does the field represent an anonymous struct?  */
>> +             bool anon_p = !DECL_NAME (fld) && ANON_AGGR_TYPE_P
>> (eltype);
>
> You don't need to check DECL_NAME; ANON_AGGR_TYPE_P by itself indicates
> that we're dealing with an anonymous struct/union.

Thanks, I've removed the variable.

>
>>    Similarly, PSTR is set to the outermost struct of which T is a member
>>    if one such struct exists, otherwise to NULL.  */
> ...
>>               find_flexarrays (eltype, fmem, false, pun,
>>                                !pstr && TREE_CODE (t) == RECORD_TYPE ?
>> fld : pstr);
> ...
>> +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)))
>
> PSTR is documented to be the outermost struct,

PSTR is set to a data member of the outermost struct of which
the flexible array is a member.  I've updated the comment to make
it clearer.

> but it (and thus
> fmem->enclosing) end up being a data member of that outermost struct,
> which is why you need to take its DECL_CONTEXT.  What's the advantage of
> doing that over passing t itself to the recursive call?

I need the data member because I want to point the diagnostic at
it, not at the struct of which it's a member.  For example, in

   struct S {
     struct X {
       int i, a[];
     }
     *p,
     x;
   };

I want to point at the x because it is the problem, like this:

   warning: invalid use of ‘struct S::X’ with a flexible array member in 
struct S’ [-Wpedantic]
       x;
       ^

not at struct X, which is not incorrect in and of itself, and neither
is p.  The diagnostic makes use of x's DECL_CONTEXT to print the name
of x's type because that's where the flexible array is defined (or in
which it's nested).

>
>> +  /* 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 = anon_context (fmem->array);
>> +  bool anon_p = fmemctx != DECL_CONTEXT (fmem->array);
>> +  if (!anon_p)
>> +    fmemctx = t;
>
> Why do you need to do something different here based on anon_p?  I don't
> see why that would affect whether we want to look through intermediate
> non-anonymous classes.

In code like this:

    struct X { int n, a[]; };
    struct Y { int n, b[]; };

    struct D: X, Y { };

The test above make the diagnostic point to the context of the invalid
flexible array member, or Y::b, rather than that of X. Without it, we
end up with:

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

rather than with

    error: flexible array member ‘X::a’ not at end of ‘struct D’

>
>> +      check_flexarrays (basetype, fmem, true);
>
> Please decorate boolean literal arguments like this with the name of the
> parameter, e.g. /*base_p*/true.

Sure.  I should mention that Jeff recently advised against it in
another review, so it would be nice to adopt the same convention
throughout and document it (I'm happy to add it to the Wiki once
it has been agreed on):

    https://gcc.gnu.org/ml/gcc-patches/2016-08/msg00354.html

FWIW, I haven't found mentioning the formal argument in a comment
a useful or consistent convention.  Other arguments' names aren't
mentioned, and the bool argument's name cannot be mentioned when
it has a default value.

On the other hand, a convention I do find useful (though not one
that seems to be used in GCC) is indicating in a comment in the
definition of a function the value of the default argument in
functions declared to take one.

>
>> +  bool maybe_anon_p = anon_aggrname_p (TYPE_IDENTIFIER (t));
>
> Now we can use TYPE_UNNAMED_P.

Okay, thanks.

Attached is a revision of the patch with the changes discussed
above.

Martin

[-- Attachment #2: gcc-71912.diff --]
[-- Type: text/x-patch, Size: 39493 bytes --]

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.
	(anon_context, 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 f7147e6..28171ea 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 *);
@@ -6711,53 +6712,160 @@ 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)
-    {
-      /* Find the next non-static data member if it exists.  */
-      for (next = fld;
-	   (next = DECL_CHAIN (next))
-	     && TREE_CODE (next) != FIELD_DECL; );
+  /* 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;
 
-      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);
-	  continue;
-	}
+  for (tree fld = TYPE_FIELDS (t); fld; fld = DECL_CHAIN (fld))
+    {
+      if (fld == error_mark_node)
+	return;
 
-      /* Skip anything that's not a (non-static) data member.  */
-      if (TREE_CODE (fld) != FIELD_DECL)
+      /* 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
+      */
+      bool anon_type_p
+	= (TREE_CODE (fld) == TYPE_DECL
+	   && DECL_IMPLICIT_TYPEDEF_P (fld)
+	   && anon_aggrname_p (DECL_NAME (fld)));
+
+      /* 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_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_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;
+	    }
+	  else if (TREE_CODE (fld) == TYPE_DECL)
+	    continue;
+	}
+
       if (field_nonempty_p (fld))
 	{
 	  /* Remember the first non-static data member.  */
@@ -6767,8 +6875,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.  */
@@ -6784,13 +6892,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
 	{
@@ -6801,16 +6912,55 @@ 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;
+	    }
 	}
     }
 }
 
+/* Return the type in which the member T is effectively declared.
+   This is either the member's enclosing struct for ordinary members,
+   or for anonymous structs and unions, the first non-anonymous struct
+   or union they are declared in.  */
+
+static tree
+anon_context (tree t)
+{
+  if (TREE_CODE (t) == FIELD_DECL)
+    return anon_context (DECL_CONTEXT (t));
+
+  bool anon_p = ANON_AGGR_TYPE_P (t);
+
+  return anon_p ? anon_context (TYPE_CONTEXT (t)) : t;
+}
+
+/* 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.  */
@@ -6818,49 +6968,78 @@ 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;
+    }
+
+  /* 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 = anon_context (fmem->array);
+  if (fmemctx == DECL_CONTEXT (fmem->array))
+    fmemctx = t;
+
+  /* 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, fmemctx))
+	    {
+	      inform (location_of (t), "in the definition of %q#T", fmemctx);
+	      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, fmemctx);
 
 	  /* 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);
 }
 
 
@@ -6873,7 +7052,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
@@ -6881,18 +7061,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);
@@ -6906,7 +7088,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)
@@ -6923,17 +7105,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 531cd21e..4571fe7 100644
--- a/gcc/testsuite/ChangeLog
+++ b/gcc/testsuite/ChangeLog
@@ -4,6 +4,12 @@
 	of non-nul characters.
 	* gfortran.dg/substr_6.f90: Make the NUL character visible on stdout
 
+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>
 
 	PR c++/77553
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/flexary17.C b/gcc/testsuite/g++.dg/ext/flexary17.C
new file mode 100644
index 0000000..b44e512
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/flexary17.C
@@ -0,0 +1,63 @@
+
+// PR c++/68489 - arrays of flexible array members are silently accepted
+// { dg-do compile }
+// { dg-options "-Wno-error=pedantic" }
+
+struct S1 {
+  struct X1 { int n, a []; } x [1];
+};
+
+struct S2 {
+  struct X2 { int n, a []; } x [2];     // { dg-error "not at end" }
+};
+
+struct S3 {
+  struct X3 { int n, a []; } x [0];     // { dg-error "not at end" }
+};
+
+struct S4 {
+  struct Y4 {
+    struct X4 { int n, a []; } x;
+  } y [1];
+};
+
+struct S5 {
+  struct Y5 {
+    struct X5 { int n, a []; } x;       // { dg-error "not at end" }
+  } y [2];
+};
+
+struct S6 {
+  struct Y6 {
+    struct X6 { int n, a []; } x;       // { dg-error "not at end" }
+  } y [0];
+};
+
+struct S7 {
+  struct Z7 {
+    struct Y7 {
+      struct X7 { int n, a []; } x;
+    } y;
+  } z [1];
+};
+
+struct S8 {
+  struct Z8 {
+    struct Y8 {
+      struct X8 { int n, a []; } x;     // { dg-error "not at end" }
+    } y;
+  } z [2];
+};
+
+struct S9 {
+  struct Z9 {
+    struct Y9 {
+      struct X9 { int n, a []; } x;     // { dg-error "not at end" }
+    } y;
+  } z [0];
+};
+
+
+struct X {
+  int n, a[];
+} x [2];   // { dg-error "not at end" "PR c++/68489" { xfail *-*-*-* } }
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..db1b060 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/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> >
 {

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH] accept flexible arrays in struct in unions (c++/71912 - [6/7 regression])
  2016-09-20 17:12                       ` Martin Sebor
@ 2016-09-21 20:55                         ` Jason Merrill
  2016-09-22 23:57                           ` Martin Sebor
  0 siblings, 1 reply; 27+ messages in thread
From: Jason Merrill @ 2016-09-21 20:55 UTC (permalink / raw)
  To: Martin Sebor; +Cc: Gcc Patch List

On Tue, Sep 20, 2016 at 1:01 PM, Martin Sebor <msebor@gmail.com> wrote:
> On 09/16/2016 12:19 PM, Jason Merrill wrote:
>> On 09/14/2016 01:03 PM, Martin Sebor wrote:
>>>
>>> +      /* Type of the member.  */
>>> +      tree fldtype = TREE_CODE (fld) == FIELD_DECL ? TREE_TYPE (fld)
>>> : fld;
>>
>> Why set "fldtype" to be a TYPE_DECL rather than its type?
>
> I'm not sure I understand the question but (IIRC) the purpose of
> this code is to detect invalid uses of flexible array members in
> typedefs such as this:
>
>    struct S { typedef struct { int i, a[]; } X2 [2]; };
>
> Sadly, it doesn't do anything for
>
>    struct X { int i, a[]; };
>    struct S { typedef struct X X2 [2]; };

The issue is I don't see anything that uses fldtype as a decl, and
it's strange to have a variable called "*type" that can either be a
type or a decl, which later code still assumes will be a type.

>>> +  /* 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 = anon_context (fmem->array);
>>> +  bool anon_p = fmemctx != DECL_CONTEXT (fmem->array);
>>> +  if (!anon_p)
>>> +    fmemctx = t;
>>
>> Why do you need to do something different here based on anon_p?  I don't
>> see why that would affect whether we want to look through intermediate
>> non-anonymous classes.
>
> In code like this:
>
>    struct X { int n, a[]; };
>    struct Y { int n, b[]; };
>
>    struct D: X, Y { };
>
> The test above make the diagnostic point to the context of the invalid
> flexible array member, or Y::b, rather than that of X. Without it, we
> end up with:
>
>    error: flexible array member ‘X::a’ not at end of ‘struct X’
>
> rather than with
>
>    error: flexible array member ‘X::a’ not at end of ‘struct D’

Yes, but why does whether we want to talk about X or D change if a is
wrapped in struct { }?

>>> +      check_flexarrays (basetype, fmem, true);
>>
>> Please decorate boolean literal arguments like this with the name of the
>> parameter, e.g. /*base_p*/true.
>
> Sure.  I should mention that Jeff recently advised against it in
> another review, so it would be nice to adopt the same convention
> throughout and document it (I'm happy to add it to the Wiki once
> it has been agreed on):
>
>    https://gcc.gnu.org/ml/gcc-patches/2016-08/msg00354.html

Interesting.  It's pretty common in the C++ front end.

> FWIW, I haven't found mentioning the formal argument in a comment
> a useful or consistent convention.  Other arguments' names aren't
> mentioned, and the bool argument's name cannot be mentioned when
> it has a default value.

True, but other arguments usually have more implied meaning and so I
think they are less of a barrier to reading.

> On the other hand, a convention I do find useful (though not one
> that seems to be used in GCC) is indicating in a comment in the
> definition of a function the value of the default argument in
> functions declared to take one.

I agree that would be a good convention.

Jason

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH] accept flexible arrays in struct in unions (c++/71912 - [6/7 regression])
  2016-09-21 20:55                         ` Jason Merrill
@ 2016-09-22 23:57                           ` Martin Sebor
  2016-09-23 18:05                             ` Jason Merrill
  0 siblings, 1 reply; 27+ messages in thread
From: Martin Sebor @ 2016-09-22 23:57 UTC (permalink / raw)
  To: Jason Merrill; +Cc: Gcc Patch List

On 09/21/2016 02:43 PM, Jason Merrill wrote:
> On Tue, Sep 20, 2016 at 1:01 PM, Martin Sebor <msebor@gmail.com> wrote:
>> On 09/16/2016 12:19 PM, Jason Merrill wrote:
>>> On 09/14/2016 01:03 PM, Martin Sebor wrote:
>>>>
>>>> +      /* Type of the member.  */
>>>> +      tree fldtype = TREE_CODE (fld) == FIELD_DECL ? TREE_TYPE (fld)
>>>> : fld;
>>>
>>> Why set "fldtype" to be a TYPE_DECL rather than its type?
>>
>> I'm not sure I understand the question but (IIRC) the purpose of
>> this code is to detect invalid uses of flexible array members in
>> typedefs such as this:
>>
>>    struct S { typedef struct { int i, a[]; } X2 [2]; };
>>
>> Sadly, it doesn't do anything for
>>
>>    struct X { int i, a[]; };
>>    struct S { typedef struct X X2 [2]; };
>
> The issue is I don't see anything that uses fldtype as a decl, and
> it's strange to have a variable called "*type" that can either be a
> type or a decl, which later code still assumes will be a type.

I suspect I'm simply looking at it from a different point of view.
The definition

   tree fldtype = TREE_CODE (fld) == FIELD_DECL ? TREE_TYPE (fld) : fld

denotes the type of the field if fld is a data member.  Otherwise,
it denotes a type (like a typedef).  Fldtype seems as a good a name
as any but I'll gladly rename it to something else if that lets us
move forward.  What would you prefer?

>>>> +  /* 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 = anon_context (fmem->array);
>>>> +  bool anon_p = fmemctx != DECL_CONTEXT (fmem->array);
>>>> +  if (!anon_p)
>>>> +    fmemctx = t;
>>>
>>> Why do you need to do something different here based on anon_p?  I don't
>>> see why that would affect whether we want to look through intermediate
>>> non-anonymous classes.
>>
>> In code like this:
>>
>>    struct X { int n, a[]; };
>>    struct Y { int n, b[]; };
>>
>>    struct D: X, Y { };
>>
>> The test above make the diagnostic point to the context of the invalid
>> flexible array member, or Y::b, rather than that of X. Without it, we
>> end up with:
>>
>>    error: flexible array member ‘X::a’ not at end of ‘struct X’
>>
>> rather than with
>>
>>    error: flexible array member ‘X::a’ not at end of ‘struct D’
>
> Yes, but why does whether we want to talk about X or D change if a is
> wrapped in struct { }?

Sorry, I don't understand the question.  A flexible array is always
"wrapped in struct { }" so I'm not sure what you mean by that.  And
"we want to talk about D" because that's where the next member after
a is defined (we could also say it's defined in struct Y and I think
I may have been down that path at one point and decided this was fine
or perhaps even better or simpler but I don't recall which for sure.)

Btw., I used quotes above only to explain how I read your question,
not to mock what you said in any way.

Going back and looking at some of the older patches, this hunk above
was changed from the original patch like so:

@@ -6923,7 +6930,10 @@ diagnose_flexarrays (tree t, const flexmems_t *fmem)
      /* 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;
+    tree fmemctx = anon_context (fmem->array);
+    bool anon_p = fmemctx != DECL_CONTEXT (fmem->array);
+    if (!anon_p)
+      fmemctx = t;

I made this change because you preferred deriving the fmemctx value
in a new function rather than storing it the fmem->anonctx member.
I don't know if this helps clarify it.

   https://gcc.gnu.org/ml/gcc-patches/2016-08/msg00153.html

(FWIW, it's been a stressful couple of days with the regressions
that my recent commit caused, so I hope you can bear with me if
I seem slow or dense on these two points.)

>
>>>> +      check_flexarrays (basetype, fmem, true);
>>>
>>> Please decorate boolean literal arguments like this with the name of the
>>> parameter, e.g. /*base_p*/true.
>>
>> Sure.  I should mention that Jeff recently advised against it in
>> another review, so it would be nice to adopt the same convention
>> throughout and document it (I'm happy to add it to the Wiki once
>> it has been agreed on):
>>
>>    https://gcc.gnu.org/ml/gcc-patches/2016-08/msg00354.html
>
> Interesting.  It's pretty common in the C++ front end.
>
>> FWIW, I haven't found mentioning the formal argument in a comment
>> a useful or consistent convention.  Other arguments' names aren't
>> mentioned, and the bool argument's name cannot be mentioned when
>> it has a default value.
>
> True, but other arguments usually have more implied meaning and so I
> think they are less of a barrier to reading.
>
>> On the other hand, a convention I do find useful (though not one
>> that seems to be used in GCC) is indicating in a comment in the
>> definition of a function the value of the default argument in
>> functions declared to take one.
>
> I agree that would be a good convention.

I'm happy to start a separate discussion on these two topics unless
you would prefer to.  Please let me know.

Martin

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH] accept flexible arrays in struct in unions (c++/71912 - [6/7 regression])
  2016-09-22 23:57                           ` Martin Sebor
@ 2016-09-23 18:05                             ` Jason Merrill
  2016-10-05 21:43                               ` Martin Sebor
  0 siblings, 1 reply; 27+ messages in thread
From: Jason Merrill @ 2016-09-23 18:05 UTC (permalink / raw)
  To: Martin Sebor; +Cc: Gcc Patch List

On Thu, Sep 22, 2016 at 7:39 PM, Martin Sebor <msebor@gmail.com> wrote:
> On 09/21/2016 02:43 PM, Jason Merrill wrote:
>> On Tue, Sep 20, 2016 at 1:01 PM, Martin Sebor <msebor@gmail.com> wrote:
>>> On 09/16/2016 12:19 PM, Jason Merrill wrote:
>>>> On 09/14/2016 01:03 PM, Martin Sebor wrote:
>>>>>
>>>>> +      /* Type of the member.  */
>>>>> +      tree fldtype = TREE_CODE (fld) == FIELD_DECL ? TREE_TYPE (fld) : fld;
>>>>
>>>> Why set "fldtype" to be a TYPE_DECL rather than its type?
>>>
>>> I'm not sure I understand the question but (IIRC) the purpose of
>>> this code is to detect invalid uses of flexible array members in
>>> typedefs such as this:
>>>
>>>    struct S { typedef struct { int i, a[]; } X2 [2]; };
>>>
>>> Sadly, it doesn't do anything for
>>>
>>>    struct X { int i, a[]; };
>>>    struct S { typedef struct X X2 [2]; };
>>
>> The issue is I don't see anything that uses fldtype as a decl, and
>> it's strange to have a variable called "*type" that can either be a
>> type or a decl, which later code still assumes will be a type.
>
> I suspect I'm simply looking at it from a different point of view.
> The definition
>
>   tree fldtype = TREE_CODE (fld) == FIELD_DECL ? TREE_TYPE (fld) : fld
>
> denotes the type of the field if fld is a data member.  Otherwise,
> it denotes a type (like a typedef).

FLD is always a _DECL.  The result of this assignment is that fldtype
refers to either a _TYPE or a _DECL.  This creates a strange
asymmetry, and none of the following code seems to depend on it.

> Fldtype seems as a good a name
> as any but I'll gladly rename it to something else if that lets us
> move forward.  What would you prefer?

I would prefer

tree fldtype = TREE_TYPE (fld);

so that it's always a _TYPE.

>>>>> +  /* 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 = anon_context (fmem->array);
>>>>> +  bool anon_p = fmemctx != DECL_CONTEXT (fmem->array);
>>>>> +  if (!anon_p)
>>>>> +    fmemctx = t;
>>>>
>>>> Why do you need to do something different here based on anon_p?  I don't
>>>> see why that would affect whether we want to look through intermediate
>>>> non-anonymous classes.
>>>
>>> In code like this:
>>>
>>>    struct X { int n, a[]; };
>>>    struct Y { int n, b[]; };
>>>
>>>    struct D: X, Y { };
>>>
>>> The test above make the diagnostic point to the context of the invalid
>>> flexible array member, or Y::b, rather than that of X. Without it, we
>>> end up with:
>>>
>>>    error: flexible array member ‘X::a’ not at end of ‘struct X’
>>>
>>> rather than with
>>>
>>>    error: flexible array member ‘X::a’ not at end of ‘struct D’
>>
>> Yes, but why does whether we want to talk about X or D change if a is
>> wrapped in struct { }?
>
> Sorry, I don't understand the question.  A flexible array is always
> "wrapped in struct { }" so I'm not sure what you mean by that.  And
> "we want to talk about D" because that's where the next member after
> a is defined (we could also say it's defined in struct Y and I think
> I may have been down that path at one point and decided this was fine
> or perhaps even better or simpler but I don't recall which for sure.)
>
> Btw., I used quotes above only to explain how I read your question,
> not to mock what you said in any way.
>
> Going back and looking at some of the older patches, this hunk above
> was changed from the original patch like so:
>
> @@ -6923,7 +6930,10 @@ diagnose_flexarrays (tree t, const flexmems_t *fmem)
>      /* 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;
> +    tree fmemctx = anon_context (fmem->array);
> +    bool anon_p = fmemctx != DECL_CONTEXT (fmem->array);
> +    if (!anon_p)
> +      fmemctx = t;
>
> I made this change because you preferred deriving the fmemctx value
> in a new function rather than storing it the fmem->anonctx member.
> I don't know if this helps clarify it.
>
>   https://gcc.gnu.org/ml/gcc-patches/2016-08/msg00153.html

My point is that for

struct X { int n, a[]; };
struct X2 { struct { int n, a[]; }; };
struct Y { int n, b[]; };

struct D: X, Y { };
struct D2: X2, Y { };

We say

wa3.C:1:21: error: flexible array member ‘X::a’ not at end of ‘struct D’
wa3.C:2:31: error: flexible array member ‘X2::<unnamed struct>::a’ not
at end of ‘struct X2’

If we want to say "struct D", why don't we also want to say "struct
D2"?  It seems that you could unconditionally set fmemctx to t and not
bother calling anon_context.

> (FWIW, it's been a stressful couple of days with the regressions
> that my recent commit caused, so I hope you can bear with me if
> I seem slow or dense on these two points.)

No worries.

> [comments with parameter names/default arguments]

> I'm happy to start a separate discussion on these two topics unless
> you would prefer to.  Please let me know.

Please do.

Jason

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH] accept flexible arrays in struct in unions (c++/71912 - [6/7 regression])
  2016-09-23 18:05                             ` Jason Merrill
@ 2016-10-05 21:43                               ` Martin Sebor
  2016-10-06  2:26                                 ` Jason Merrill
  0 siblings, 1 reply; 27+ messages in thread
From: Martin Sebor @ 2016-10-05 21:43 UTC (permalink / raw)
  To: Jason Merrill; +Cc: Gcc Patch List

On 09/23/2016 12:00 PM, Jason Merrill wrote:
> On Thu, Sep 22, 2016 at 7:39 PM, Martin Sebor <msebor@gmail.com> wrote:
>> On 09/21/2016 02:43 PM, Jason Merrill wrote:
>>> On Tue, Sep 20, 2016 at 1:01 PM, Martin Sebor <msebor@gmail.com> wrote:
>>>> On 09/16/2016 12:19 PM, Jason Merrill wrote:
>>>>> On 09/14/2016 01:03 PM, Martin Sebor wrote:
>>>>>>
>>>>>> +      /* Type of the member.  */
>>>>>> +      tree fldtype = TREE_CODE (fld) == FIELD_DECL ? TREE_TYPE (fld) : fld;
>>>>>
>>>>> Why set "fldtype" to be a TYPE_DECL rather than its type?
>>>>
>>>> I'm not sure I understand the question but (IIRC) the purpose of
>>>> this code is to detect invalid uses of flexible array members in
>>>> typedefs such as this:
>>>>
>>>>    struct S { typedef struct { int i, a[]; } X2 [2]; };
>>>>
>>>> Sadly, it doesn't do anything for
>>>>
>>>>    struct X { int i, a[]; };
>>>>    struct S { typedef struct X X2 [2]; };
>>>
>>> The issue is I don't see anything that uses fldtype as a decl, and
>>> it's strange to have a variable called "*type" that can either be a
>>> type or a decl, which later code still assumes will be a type.
>>
>> I suspect I'm simply looking at it from a different point of view.
>> The definition
>>
>>   tree fldtype = TREE_CODE (fld) == FIELD_DECL ? TREE_TYPE (fld) : fld
>>
>> denotes the type of the field if fld is a data member.  Otherwise,
>> it denotes a type (like a typedef).
>
> FLD is always a _DECL.  The result of this assignment is that fldtype
> refers to either a _TYPE or a _DECL.  This creates a strange
> asymmetry, and none of the following code seems to depend on it.
> I would prefer
>
> tree fldtype = TREE_TYPE (fld);
>
> so that it's always a _TYPE.

I mentioned that this code is important to avoid diagnosing typedefs, 
but the example I showed wasn't the right one.  The examples that do
depend on it are these two:

   struct S1 {
     typedef int A [0];
     A a;
   };

   struct S2 {
     typedef struct { int i, a[]; } A1;
     typedef struct { int i, a[]; } A2;
   };

The expected (pedantic) warning is:

   warning: zero-size array member ‘S1::a’ in an otherwise empty ‘struct 
S1’

With the change you're asking we end up with:

   warning: zero-size array member ‘S1::a’ not at end of ‘struct S1’

followed by a bogus

   error: flexible array member ‘S2::A1::a’ not at end of ‘struct S2’

So I still don't understand what you're looking for.  It sounds to
me like you're asking me to rewrite the subsequent code that uses
fldtype to use the expression above because you don't like that
fldtype can be either a _DECL or _TYPE.  But repeating that check
four times doesn't make sense, so what am I missing?

> My point is that for
>
> struct X { int n, a[]; };
> struct X2 { struct { int n, a[]; }; };
> struct Y { int n, b[]; };
>
> struct D: X, Y { };
> struct D2: X2, Y { };
>
> We say
>
> wa3.C:1:21: error: flexible array member ‘X::a’ not at end of ‘struct D’
> wa3.C:2:31: error: flexible array member ‘X2::<unnamed struct>::a’ not
> at end of ‘struct X2’
>
> If we want to say "struct D", why don't we also want to say "struct
> D2"?  It seems that you could unconditionally set fmemctx to t and not
> bother calling anon_context.

I see.  It looks like it doesn't matter anymore.  I suspect the
whole anon_context business became unnecessary with the removal
of the overlapping union member code. I've removed the code and
added a test case to verify that the error references the same
parent class in both cases.

Martin

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH] accept flexible arrays in struct in unions (c++/71912 - [6/7 regression])
  2016-10-05 21:43                               ` Martin Sebor
@ 2016-10-06  2:26                                 ` Jason Merrill
  2016-10-06 20:29                                   ` Martin Sebor
  0 siblings, 1 reply; 27+ messages in thread
From: Jason Merrill @ 2016-10-06  2:26 UTC (permalink / raw)
  To: Martin Sebor; +Cc: Gcc Patch List

[-- Attachment #1: Type: text/plain, Size: 3527 bytes --]

On 10/05/2016 05:43 PM, Martin Sebor wrote:
> On 09/23/2016 12:00 PM, Jason Merrill wrote:
>> On Thu, Sep 22, 2016 at 7:39 PM, Martin Sebor <msebor@gmail.com> wrote:
>>> On 09/21/2016 02:43 PM, Jason Merrill wrote:
>>>> On Tue, Sep 20, 2016 at 1:01 PM, Martin Sebor <msebor@gmail.com> wrote:
>>>>> On 09/16/2016 12:19 PM, Jason Merrill wrote:
>>>>>> On 09/14/2016 01:03 PM, Martin Sebor wrote:
>>>>>>>
>>>>>>> +      /* Type of the member.  */
>>>>>>> +      tree fldtype = TREE_CODE (fld) == FIELD_DECL ? TREE_TYPE
>>>>>>> (fld) : fld;
>>>>>>
>>>>>> Why set "fldtype" to be a TYPE_DECL rather than its type?
>>>>>
>>>>> I'm not sure I understand the question but (IIRC) the purpose of
>>>>> this code is to detect invalid uses of flexible array members in
>>>>> typedefs such as this:
>>>>>
>>>>>    struct S { typedef struct { int i, a[]; } X2 [2]; };
>>>>>
>>>>> Sadly, it doesn't do anything for
>>>>>
>>>>>    struct X { int i, a[]; };
>>>>>    struct S { typedef struct X X2 [2]; };
>>>>
>>>> The issue is I don't see anything that uses fldtype as a decl, and
>>>> it's strange to have a variable called "*type" that can either be a
>>>> type or a decl, which later code still assumes will be a type.
>>>
>>> I suspect I'm simply looking at it from a different point of view.
>>> The definition
>>>
>>>   tree fldtype = TREE_CODE (fld) == FIELD_DECL ? TREE_TYPE (fld) : fld
>>>
>>> denotes the type of the field if fld is a data member.  Otherwise,
>>> it denotes a type (like a typedef).
>>
>> FLD is always a _DECL.  The result of this assignment is that fldtype
>> refers to either a _TYPE or a _DECL.  This creates a strange
>> asymmetry, and none of the following code seems to depend on it.
>> I would prefer
>>
>> tree fldtype = TREE_TYPE (fld);
>>
>> so that it's always a _TYPE.
>
> I mentioned that this code is important to avoid diagnosing typedefs,
> but the example I showed wasn't the right one.  The examples that do
> depend on it are these two:
>
>   struct S1 {
>     typedef int A [0];
>     A a;
>   };
>
>   struct S2 {
>     typedef struct { int i, a[]; } A1;
>     typedef struct { int i, a[]; } A2;
>   };
>
> The expected (pedantic) warning is:
>
>   warning: zero-size array member ‘S1::a’ in an otherwise empty ‘struct S1’
>
> With the change you're asking we end up with:
>
>   warning: zero-size array member ‘S1::a’ not at end of ‘struct S1’
>
> followed by a bogus
>
>   error: flexible array member ‘S2::A1::a’ not at end of ‘struct S2’
>
> So I still don't understand what you're looking for.  It sounds to
> me like you're asking me to rewrite the subsequent code that uses
> fldtype to use the expression above because you don't like that
> fldtype can be either a _DECL or _TYPE.  But repeating that check
> four times doesn't make sense, so what am I missing?

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.

[-- Attachment #2: typedef.diff --]
[-- Type: text/x-patch, Size: 2580 bytes --]

diff --git a/gcc/cp/class.c b/gcc/cp/class.c
index 03cf2eb..e6f7167 100644
--- a/gcc/cp/class.c
+++ b/gcc/cp/class.c
@@ -6752,49 +6752,39 @@ find_flexarrays (tree t, flexmems_t *fmem, bool base_p,
 	   typedef struct { int i, a[], j; } S;   // bug c++/72753
            S s [2];                               // bug c++/68489
       */
-      bool anon_type_p
-	= (TREE_CODE (fld) == TYPE_DECL
-	   && DECL_IMPLICIT_TYPEDEF_P (fld)
-	   && anon_aggrname_p (DECL_NAME (fld)));
+      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 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))
+	 member.  */
+      if (DECL_ARTIFICIAL (fld) || TREE_CODE (fld) != FIELD_DECL)
 	continue;
 
       /* Type of the member.  */
-      tree fldtype = TREE_CODE (fld) == FIELD_DECL ? TREE_TYPE (fld) : fld;
+      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 = TREE_TYPE (fld);
-      if (eltype == error_mark_node)
-	return;
-
+      tree eltype = fldtype;
       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
@@ -6843,8 +6833,6 @@ find_flexarrays (tree t, flexmems_t *fmem, bool base_p,
 	      if (base_p)
 		continue;
 	    }
-	  else if (TREE_CODE (fld) == TYPE_DECL)
-	    continue;
 	}
 
       if (field_nonempty_p (fld))

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH] accept flexible arrays in struct in unions (c++/71912 - [6/7 regression])
  2016-10-06  2:26                                 ` Jason Merrill
@ 2016-10-06 20:29                                   ` Martin Sebor
  2016-10-12  1:46                                     ` PING " Martin Sebor
  0 siblings, 1 reply; 27+ messages in thread
From: Martin Sebor @ 2016-10-06 20:29 UTC (permalink / raw)
  To: Jason Merrill; +Cc: Gcc Patch List

[-- Attachment #1: Type: text/plain, Size: 1209 bytes --]

> 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


[-- Attachment #2: gcc-71912.diff --]
[-- Type: text/x-patch, Size: 37851 bytes --]

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> >
 {

^ permalink raw reply	[flat|nested] 27+ messages in thread

* PING [PATCH] accept flexible arrays in struct in unions (c++/71912 - [6/7 regression])
  2016-10-06 20:29                                   ` Martin Sebor
@ 2016-10-12  1:46                                     ` Martin Sebor
  2016-10-12 13:43                                       ` Jason Merrill
  0 siblings, 1 reply; 27+ messages in thread
From: Martin Sebor @ 2016-10-12  1:46 UTC (permalink / raw)
  To: Jason Merrill; +Cc: Gcc Patch List

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
>

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: PING [PATCH] accept flexible arrays in struct in unions (c++/71912 - [6/7 regression])
  2016-10-12  1:46                                     ` PING " Martin Sebor
@ 2016-10-12 13:43                                       ` Jason Merrill
  2016-10-13 22:40                                         ` Martin Sebor
  0 siblings, 1 reply; 27+ messages in thread
From: Jason Merrill @ 2016-10-12 13:43 UTC (permalink / raw)
  To: Martin Sebor; +Cc: Gcc Patch List

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

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: PING [PATCH] accept flexible arrays in struct in unions (c++/71912 - [6/7 regression])
  2016-10-12 13:43                                       ` Jason Merrill
@ 2016-10-13 22:40                                         ` Martin Sebor
  0 siblings, 0 replies; 27+ messages in thread
From: Martin Sebor @ 2016-10-13 22:40 UTC (permalink / raw)
  To: Jason Merrill; +Cc: Gcc Patch List

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

^ permalink raw reply	[flat|nested] 27+ messages in thread

end of thread, other threads:[~2016-10-13 22:40 UTC | newest]

Thread overview: 27+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-07-22 19:14 [PATCH] accept flexible arrays in struct in unions (c++/71912 - [6/7 regression]) Martin Sebor
2016-07-23 17:18 ` Martin Sebor
2016-07-26 18:53   ` Jason Merrill
2016-07-29 23:22     ` Martin Sebor
2016-07-31 16:28       ` Jason Merrill
2016-07-31 20:27         ` Martin Sebor
2016-08-01 14:22           ` Jason Merrill
2016-08-02 21:00             ` Martin Sebor
2016-08-02 22:26               ` Jason Merrill
2016-08-03  2:13                 ` Martin Sebor
2016-08-03 18:05                   ` Jason Merrill
2016-09-14 17:05                   ` Martin Sebor
2016-09-16 18:40                     ` Jason Merrill
2016-09-20 17:12                       ` Martin Sebor
2016-09-21 20:55                         ` Jason Merrill
2016-09-22 23:57                           ` Martin Sebor
2016-09-23 18:05                             ` Jason Merrill
2016-10-05 21:43                               ` Martin Sebor
2016-10-06  2:26                                 ` Jason Merrill
2016-10-06 20:29                                   ` Martin Sebor
2016-10-12  1:46                                     ` PING " Martin Sebor
2016-10-12 13:43                                       ` Jason Merrill
2016-10-13 22:40                                         ` Martin Sebor
2016-08-03 19:10                 ` Martin Sebor
2016-08-03 20:02                   ` Jason Merrill
2016-08-03 20:23                     ` Martin Sebor
2016-08-03 21:53                       ` Jason Merrill

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).