diff mbox

Ping [PATCH] c++/42121 - diagnose invalid flexible array members

Message ID 56670C61.6050200@gmail.com
State New
Headers show

Commit Message

Martin Sebor Dec. 8, 2015, 4:59 p.m. UTC
Thanks for the review and the helpful hints!

I've reworked and simplified the diagnostic part of the patch and
corrected the remaining issues I uncovered while testing the new
version (failing to reject some invalid flexible array members in
base classes).  Please find the new version in the attachment.

FWIW, in the patch, I tried to address only the reported problems
with flexible array members without changing how zero-length arrays
are treated.  That means that the latter are accepted in more cases
(with a pedantic warning) than the latter.  For example, the
following is accepted:

     struct A {
         int a[0];   // pedantic warning: zero-size array member
         int n;      // not at end of struct A
     };

while this is rejected

     struct B {
         int a[];    // hard error: flexible array member not at
         int n;      // end of struct B
     };

It would be easy to change the patch to treat zero-length arrays
more like flexible array members if that's viewed as desirable.

I also tried to avoid rejecting flexible array members that are
currently accepted and that are safe.  For example, GCC currently
accepts both flexible array members and zero-length arrays in base
classes (even polymorphic ones).  The patch continues to accept
those for compatibility with code that relies on it as long as
the flexible array members didn't overlap other members in derived
classes.  For example, this is still accepted:

     struct A { int x; };
     struct B { int n, a[]; };
     struct C: A, B { };

but this is rejected:

     struct D: B, A { };

My replies to your comments are below.

On 12/04/2015 10:51 AM, Jason Merrill wrote:
> On 12/03/2015 11:42 PM, Martin Sebor wrote:
>> +      if (next && TREE_CODE (next) == FIELD_DECL)
>
> This will break if there's a non-field between the array and the next
> field.

You're right, I missed that case in my testing.  Fixed.

>> @@ -4114,7 +4115,10 @@ walk_subobject_offsets (tree type,
>>
>>        /* Avoid recursing into objects that are not interesting.  */
>>        if (!CLASS_TYPE_P (element_type)
>> -      || !CLASSTYPE_CONTAINS_EMPTY_CLASS_P (element_type))
>> +      || !CLASSTYPE_CONTAINS_EMPTY_CLASS_P (element_type)
>> +      || !domain
>> +      /* Flexible array members have no upper bound.  */
>> +      || !TYPE_MAX_VALUE (domain))
>
> Why is this desirable?  We do want to avoid empty bases at the same
> address as a flexible array of the same type.

As we discussed on IRC, this bit is fine.  I added a few tests for
the layout to make sure the offset of flexible array members matches
the size of the containing class.  While adding these tests I found
a couple of regressions unrelated to my changes (68727 and 68711) so
it was time well spent.

>> +      && !tree_int_cst_equal (size_max_node, TYPE_MAX_VALUE (dom)))
>
> This can be integer_minus_onep or integer_all_onesp.

Thanks.

>
>> +         its fields.  The recursive call to the function will
>> +         either return 0 or the flexible array member whose
>
> Let's say NULL_TREE here rather than 0.

Sure.

>
>> +  {
>> +    bool dummy = false;
>> +    check_flexarrays (t, TYPE_FIELDS (t), &dummy);
>> +  }
>
> This should be called from check_bases_and_members, or even integrated
> into check_field_decls.

I tried moving it to check_bases_and_members there but with more
testing found out that calling it there was too early. In order
to detect invalid flexible array members in virtual base classes
without rejecting valid ones, the primary base class needs to
have been determined.  That happens in in layout_class_type()
called later on in finish_struct_1(). So I moved it just past
that call.

>
>> -      else if (name)
>> -        pedwarn (input_location, OPT_Wpedantic, "ISO C++ forbids
>> zero-size array %qD", name);
>
> Why?

At one point, the diagnostic would emit a badly messed up name
in some corner cases.  I think it might have been when I set
TYPE_DOMAIN to NULL_TREE rather than with the current approach
(I can't reproduce it anymore). I've restored the else block.

> Can we leave TYPE_DOMAIN null for flexible arrays so you don't need to
> add special new handling all over the place?

This was my initial approach until I noticed that it diverged
from C where TYPE_DOMAIN is set to the range [0, NULL_TREE], so
I redid it for consistency.

>
>> -    tree decl;
>> +    tree decl = NULL_TREE;
>
> Why?

To avoid an ICE later on.  I didn't spend too much time trying
to understand how the control flow changed to trigger it but my
guess is that it has to do with the change to the upper bound.

/home/msebor/scm/fsf/gcc-42121/gcc/testsuite/g++.dg/ext/flexary2.C:16:9: 
internal compiler error: tree check: expected tree that contains 'typed' 
structure, have '<invalid tree code>' in cp_apply_type_quals_to_decl, at 
cp/typeck.c:9134
0x13103fe tree_contains_struct_check_failed(tree_node const*, 
tree_node_structure_enum, char const*, int, char const*)
          /home/msebor/scm/fsf/gcc-42121/gcc/tree.c:9760
0x726b12 contains_struct_check(tree_node*, tree_node_structure_enum, 
char const*, int, char const*)
          /home/msebor/scm/fsf/gcc-42121/gcc/tree.h:3070
0x901d20 cp_apply_type_quals_to_decl(int, tree_node*)
          /home/msebor/scm/fsf/gcc-42121/gcc/cp/typeck.c:9134
0x77bf8b grokdeclarator(cp_declarator const*, cp_decl_specifier_seq*, 
decl_context, int, tree_node**)
          /home/msebor/scm/fsf/gcc-42121/gcc/cp/decl.c:11423

>
>> +++ b/gcc/testsuite/g++.dg/cpp0x/bad_array_new2.C
...
> Why?

This wasn't meant to be a part of the patch. I removed it.

Martin
diff mbox

Patch

gcc/testsuite/ChangeLog:
2015-12-02  Martin Sebor  <msebor@redhat.com>

	c++/42121
	c++/68478
	c++/68613
	c++/68689
	c++/68710
	* g++.dg/ext/flexary2.C: Expect a sole flexible array member
	to be rejected.  Add a test case exercising zero-length array.
	* g++.dg/ext/flexary3.C: Expect a sole flexible array member
	to be rejected.
	* g++.dg/ext/flexary.h: New file.
	* g++.dg/ext/flexary4.C: New file.
	* g++.dg/ext/flexary5.C: New file.
	* g++.dg/ext/flexary6.C: New file.
	* g++.dg/ext/flexary7.C: New file.
	* g++.dg/ext/flexary8.C: New file.
	* g++.dg/other/dump-ada-spec-2.C: Adjust to reflect flexible
	array members.
	* g++.dg/parse/pr43765.C: Add a member to make a struct with
	a flexible array member valid.  Adjust expected error message.
	* g++.dg/torture/pr64280.C: Expect a sole flexible array member
	to be rejected.
	* g++.dg/torture/pr64312.C: Add a member to make a struct with
	a flexible array member valid.
	* g++.dg/ubsan/object-size-1.C: Adjust expected diagnostic.

gcc/cp/ChangeLog:
2015-12-02  Martin Sebor  <msebor@redhat.com>

	c++/42121
	c++/68478
	c++/68613
	c++/68689
	c++/68710
	* class.c (walk_subobject_offsets): Avoid assuming type domain
	is non-null or has an upper bound.
	(layout_class_type): Include type size in error message.
	(flexmems_t): New type.
	(field_nonempty_p, find_flexarrays, diagnose_flexarrays)
	(check_flexarrays): New	functions.
	(finish_struct_1): Call check_flexarrays.
	* decl.c (compute_array_index_type): Distinguish flexible array
	members from zero-length arrays.
	(grokdeclarator): Reject flexible array members in unions.  Avoid
	rejecting members of incomplete types that are flexible array members.
	* error.c (dump_type_suffix): Handle flexible array members with null
	upper bound.
	* init.c (perform_member_init): Same.
	* pt.c (instantiate_class_template_1): Allow flexible array members.
	(tsubst): Handle flexible array members with null upper bound.
	* typeck2.c (digest_init_r): Warn for initialization of flexible
	array members.
	(process_init_constructor_record): Handle flexible array members.

gcc/ChangeLog:
2015-12-02  Martin Sebor  <msebor@redhat.com>

	c++/42121
	* tree-chkp.c (chkp_find_bound_slots_1): Handle flexible array
	members.
	* tree.c (type_contains_placeholder_1): Avoid assuming type has
	a non-null domain or an upper bound to handle flexible array
	members.
	* varasm.c (array_size_for_constructor): Same.
	(output_constructor_regular_field):  Same.
	(output_constructor): Set min_index to integer_zero_node rather
	than null when a type has no domain to avoid crashing later.

diff --git a/gcc/cp/class.c b/gcc/cp/class.c
index 216a301..ee2784c 100644
--- a/gcc/cp/class.c
+++ b/gcc/cp/class.c
@@ -35,6 +35,7 @@  along with GCC; see the file COPYING3.  If not see
 #include "convert.h"
 #include "dumpfile.h"
 #include "gimplify.h"
+#include "intl.h"
 
 /* The number of nested classes being processed.  If we are not in the
    scope of any class, this is zero.  */
@@ -145,6 +146,12 @@  static void build_base_fields (record_layout_info, splay_tree, tree *);
 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.  */
+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 check_bases (tree, int *, int *);
 static void check_bases_and_members (tree);
 static tree create_vtable_ptr (tree, tree *);
@@ -4114,7 +4121,10 @@  walk_subobject_offsets (tree type,
 
       /* Avoid recursing into objects that are not interesting.  */
       if (!CLASS_TYPE_P (element_type)
-	  || !CLASSTYPE_CONTAINS_EMPTY_CLASS_P (element_type))
+	  || !CLASSTYPE_CONTAINS_EMPTY_CLASS_P (element_type)
+	  || !domain
+	  /* Flexible array members have no upper bound.  */
+	  || !TYPE_MAX_VALUE (domain))
 	return 0;
 
       /* Step through each of the elements in the array.  */
@@ -5703,9 +5713,9 @@  check_bases_and_members (tree t)
   cant_have_const_ctor = 0;
   no_const_asn_ref = 0;
 
-  /* Check all the base-classes.  */
-  check_bases (t, &cant_have_const_ctor,
-	       &no_const_asn_ref);
+  /* Check all the base-classes and set FMEM members to point to arrays
+     of potential interest.  */
+  check_bases (t, &cant_have_const_ctor, &no_const_asn_ref);
 
   /* Deduce noexcept on destructors.  This needs to happen after we've set
      triviality flags appropriately for our bases.  */
@@ -6236,6 +6246,7 @@  layout_class_type (tree t, tree *virtuals_p)
   /* Build FIELD_DECLs for all of the non-virtual base-types.  */
   empty_base_offsets = splay_tree_new (splay_tree_compare_integer_csts,
 				       NULL, NULL);
+
   build_base_fields (rli, empty_base_offsets, next_field);
 
   /* Layout the non-static data members.  */
@@ -6531,7 +6542,7 @@  layout_class_type (tree t, tree *virtuals_p)
       && TREE_CODE (TYPE_SIZE_UNIT (t)) == INTEGER_CST
       && !TREE_OVERFLOW (TYPE_SIZE_UNIT (t))
       && !valid_constant_size_p (TYPE_SIZE_UNIT (t)))
-    error ("type %qT is too large", t);
+    error ("size of type %qT is too large (%qE bytes)", t, TYPE_SIZE_UNIT (t));
 
   /* Warn about bases that can't be talked about due to ambiguity.  */
   warn_about_ambiguous_bases (t);
@@ -6597,9 +6608,262 @@  sorted_fields_type_new (int n)
   return sft;
 }
 
+/* Helper of find_flexarrays.  Return true when FLD refers to a non-static
+   class data member of non-zero size, otherwise false.  */
+
+static inline bool
+field_nonempty_p (const_tree fld)
+{
+  if (TREE_CODE (fld) == ERROR_MARK)
+    return false;
+
+  tree type = TREE_TYPE (fld);
+  if (TREE_CODE (fld) == FIELD_DECL
+      && TREE_CODE (type) != ERROR_MARK
+      && (DECL_NAME (fld) || RECORD_OR_UNION_TYPE_P (type)))
+    {
+      return TYPE_SIZE (type)
+	&& (TREE_CODE (TYPE_SIZE (type)) != INTEGER_CST
+	    || !tree_int_cst_equal (size_zero_node, TYPE_SIZE (type)));
+    }
+
+  return false;
+}
+
+/* Used by find_flexarrays and related.  */
+struct flexmems_t {
+  /* The first flexible array member or non-zero array member found
+     in 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;
+};
+
+/* 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.  */
+
+static void
+find_flexarrays (tree t, flexmems_t *fmem)
+{
+  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; );
+      
+      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;
+	}
+
+      /* Skip anything that's not a (non-static) data member.  */
+      if (TREE_CODE (fld) != FIELD_DECL)
+	continue;
+
+      /* Skip virtual table pointers.  */
+      if (DECL_ARTIFICIAL (fld))
+	continue;
+
+      if (field_nonempty_p (fld))
+	{
+	  /* Remember the first non-static data member.  */
+	  if (!fmem->first)
+	    fmem->first = 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->after && fmem->array)
+	    fmem->after = fld;
+	}
+	    
+      /* Skip non-arrays.  */
+      if (TREE_CODE (fldtype) != ARRAY_TYPE)
+	continue;
+
+      /* Determine the upper bound of the array if it has one.  */
+      tree dom = TYPE_DOMAIN (fldtype);
+
+      if (dom && TYPE_MAX_VALUE (dom))
+	{
+	  if (fmem->array)
+	    {
+	      /* Make a record of the zero-length array if either one
+		 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;
+	    }
+	  else if (integer_all_onesp (TYPE_MAX_VALUE (dom)))
+	    /* Remember the first zero-length array unless a flexible array
+	       member has already been seen.  */
+	    fmem->array = fld;
+	}
+      else
+	{
+	  /* Flexible array members have no upper bound.  */
+	  if (fmem->array)
+	    {
+	      /* Replace the zero-length array if it's been stored and
+		 reset the after pointer.  */
+	      dom = TYPE_DOMAIN (TREE_TYPE (fmem->array));
+	      if (dom && TYPE_MAX_VALUE (dom))
+		{
+		  fmem->array = fld;
+		  fmem->after = NULL_TREE;
+		}
+	    }
+	  else	
+	    fmem->array = fld;
+	}
+    }
+}
+
+/* Issue diagnostics for invalid flexible array members or zero-length
+   arrays that are not the last elements of the containg class or its
+   base classes or that are its sole members.  */
+
+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_ANONYMOUS_P (t) || !fmem->array)
+    return;
+
+  const char *msg = 0;
+
+  const_tree dom = TYPE_DOMAIN (TREE_TYPE (fmem->array));
+  if (dom && TYPE_MAX_VALUE (dom))
+    {
+      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 (msg && pedwarn (DECL_SOURCE_LOCATION (fmem->array),
+			  OPT_Wpedantic, msg, fmem->array, t))
+
+	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 (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);
+	}
+    }
+}
+
+
+/* Recursively check to make sure that any flexible array or zero-length
+   array members of class T or its bases are valid (i.e., not the sole
+   non-static data member of T and, if one exists, that it is the last
+   non-static data member of T and its base classes.  FMEM is expected
+   to be initially null and is used internally by recursive calls to
+   the function.  Issue the appropriate diagnostics for the array member
+   that fails the checks.  */
+
+static void
+check_flexarrays (tree t, 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
+     have already been populated.  */
+  flexmems_t flexmems = flexmems_t ();
+  if (!fmem)
+    fmem = &flexmems;
+  else if (fmem->array && fmem->first && fmem->after)
+    return;
+
+  /* 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);
+    }
+
+  /* Recursively check the base classes.  */
+  int nbases = BINFO_N_BASE_BINFOS (TYPE_BINFO (t));
+  for (int i = 0; i < nbases; ++i)
+    {
+      tree base_binfo = BINFO_BASE_BINFO (TYPE_BINFO (t), i);
+
+      /* The primary base class was already checked above.  */
+      if (base_binfo == CLASSTYPE_PRIMARY_BINFO (t))
+	continue;
+
+      /* Virtual base classes are at the end.  */
+      if (BINFO_VIRTUAL_P (base_binfo))
+	continue;
+
+      /* Check the base class.  */
+      check_flexarrays (BINFO_TYPE (base_binfo), fmem);
+    }
+
+  if (fmem == &flexmems)
+    {
+      /* Check virtual base classes only once per derived class.
+	 I.e., this check is not performed recursively for base
+	 classes.  */
+      int i;
+      tree base_binfo;
+      vec<tree, va_gc> *vbases;
+      for (vbases = CLASSTYPE_VBASECLASSES (t), i = 0;
+	   vec_safe_iterate (vbases, i, &base_binfo); i++)
+	{
+	  /* Check the virtual base class.  */
+	  tree basetype = TREE_TYPE (base_binfo);
+
+	  check_flexarrays (basetype, fmem);
+	}
+    }
+
+  /* Search the members of the current (derived) class.  */
+  find_flexarrays (t, fmem);
+
+  if (fmem == &flexmems)
+    { 
+      /* Issue diagnostics for invalid flexible and zero-length array members
+	 found in base classes or among the members of the current class.  */
+      diagnose_flexarrays (t, fmem);
+    }
+}
 
 /* Perform processing required when the definition of T (a class type)
-   is complete.  */
+   is complete.  Diagnose invalid definitions of flexible array members
+   and zero-size arrays.  */
 
 void
 finish_struct_1 (tree t)
@@ -6661,6 +6925,11 @@  finish_struct_1 (tree t)
        needs a mode.  */
     compute_record_mode (CLASSTYPE_AS_BASE (t));
 
+  /* With the layout complete and the primary base class determined,
+     check for flexible array members and zero-length arrays that
+     might overlap other members in the final layout.  */
+  check_flexarrays (t);
+
   virtuals = modify_all_vtables (t, nreverse (virtuals));
 
   /* If necessary, create the primary vtable for this class.  */
diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c
index 675342e..44e7440 100644
--- a/gcc/cp/decl.c
+++ b/gcc/cp/decl.c
@@ -8610,8 +8610,9 @@  fold_sizeof_expr (tree t)
 }
 
 /* Given the SIZE (i.e., number of elements) in an array, compute an
-   appropriate index type for the array.  If non-NULL, NAME is the
-   name of the thing being declared.  */
+   appropriate index type for the array.  When SIZE is null, the array
+   is a flexible array member.  If non-NULL, NAME is the name of
+   the entity being declared.  */
 
 tree
 compute_array_index_type (tree name, tree size, tsubst_flags_t complain)
@@ -8619,6 +8620,9 @@  compute_array_index_type (tree name, tree size, tsubst_flags_t complain)
   tree itype;
   tree osize = size;
 
+  if (NULL_TREE == size)
+    return build_index_type (NULL_TREE);
+
   if (error_operand_p (size))
     return error_mark_node;
 
@@ -8744,6 +8748,7 @@  compute_array_index_type (tree name, tree size, tsubst_flags_t complain)
  	  else
 	    pedwarn (input_location, OPT_Wpedantic, "ISO C++ forbids zero-size array");
 	}
+
     }
   else if (TREE_CONSTANT (size)
 	   /* We don't allow VLAs at non-function scopes, or during
@@ -10888,7 +10893,7 @@  grokdeclarator (const cp_declarator *declarator,
     }
 
   {
-    tree decl;
+    tree decl = NULL_TREE;
 
     if (decl_context == PARM)
       {
@@ -10912,11 +10917,19 @@  grokdeclarator (const cp_declarator *declarator,
 	if (!staticp && TREE_CODE (type) == ARRAY_TYPE
 	    && TYPE_DOMAIN (type) == NULL_TREE)
 	  {
-	    tree itype = compute_array_index_type (dname, integer_zero_node,
+	    if (TREE_CODE (ctype) == UNION_TYPE
+		|| TREE_CODE (ctype) == QUAL_UNION_TYPE)
+	      {
+		error ("flexible array member in union");
+		type = error_mark_node;
+	      }
+	    else
+	      {
+		tree itype = compute_array_index_type (dname, NULL_TREE,
 						       tf_warning_or_error);
 		type = build_cplus_array_type (TREE_TYPE (type), itype);
 	      }
-
+	  }
 	if (type == error_mark_node)
 	  {
 	    /* Happens when declaring arrays of sizes which
@@ -11082,6 +11095,10 @@  grokdeclarator (const cp_declarator *declarator,
 		     || !COMPLETE_TYPE_P (TREE_TYPE (type))
 		     || initialized == 0))
 	  {
+	    if (TREE_CODE (type) != ARRAY_TYPE
+		|| !COMPLETE_TYPE_P (TREE_TYPE (type)))
+	      {
+
 		if (unqualified_id)
 		  {
 		    error ("field %qD has incomplete type %qT",
@@ -11094,6 +11111,7 @@  grokdeclarator (const cp_declarator *declarator,
 		type = error_mark_node;
 		decl = NULL_TREE;
 	      }
+	  }
 	else
 	  {
 	    if (friendp)
diff --git a/gcc/cp/error.c b/gcc/cp/error.c
index 38548c7..56ce14b 100644
--- a/gcc/cp/error.c
+++ b/gcc/cp/error.c
@@ -875,7 +875,7 @@  dump_type_suffix (cxx_pretty_printer *pp, tree t, int flags)
     case ARRAY_TYPE:
       pp_maybe_space (pp);
       pp_cxx_left_bracket (pp);
-      if (TYPE_DOMAIN (t))
+      if (TYPE_DOMAIN (t) && TYPE_MAX_VALUE (TYPE_DOMAIN (t)))
 	{
 	  tree dtype = TYPE_DOMAIN (t);
 	  tree max = TYPE_MAX_VALUE (dtype);
diff --git a/gcc/cp/init.c b/gcc/cp/init.c
index fccd289..6b93504 100644
--- a/gcc/cp/init.c
+++ b/gcc/cp/init.c
@@ -729,10 +729,15 @@  perform_member_init (tree member, tree init)
 	      || same_type_ignoring_top_level_qualifiers_p (type,
 							    TREE_TYPE (init)))
 	    {
+	      if (TYPE_DOMAIN (type) && TYPE_MAX_VALUE (TYPE_DOMAIN (type)))
+		{
+		  /* Initialize the array only if it's not a flexible
+		     array member (i.e., if it has an upper bound).  */
 		  init = build_vec_init_expr (type, init, tf_warning_or_error);
 		  init = build2 (INIT_EXPR, type, decl, init);
 		  finish_expr_stmt (init);
 		}
+	    }
 	  else
 	    error ("invalid initializer for array member %q#D", member);
 	}
diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index 2904657..2bfaaee 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -10005,7 +10005,16 @@  instantiate_class_template_1 (tree type)
 			  if (can_complete_type_without_circularity (rtype))
 			    complete_type (rtype);
 
-			  if (!COMPLETE_TYPE_P (rtype))
+                          if (TREE_CODE (r) == FIELD_DECL
+                              && TREE_CODE (rtype) == ARRAY_TYPE
+                              && COMPLETE_TYPE_P (TREE_TYPE (rtype))
+                              && !COMPLETE_TYPE_P (rtype))
+                            {
+                              /* Flexible array mmembers of elements
+                                 of complete type have an incomplete type
+                                 and that's okay.  */
+                            }
+                          else if (!COMPLETE_TYPE_P (rtype))
 			    {
 			      cxx_incomplete_type_error (r, rtype);
 			      TREE_TYPE (r) = error_mark_node;
@@ -12730,9 +12739,14 @@  tsubst (tree t, tree args, tsubst_flags_t complain, tree in_decl)
       if (t == integer_type_node)
 	return t;
 
-      if (TREE_CODE (TYPE_MIN_VALUE (t)) == INTEGER_CST
-	  && TREE_CODE (TYPE_MAX_VALUE (t)) == INTEGER_CST)
+      if (TREE_CODE (TYPE_MIN_VALUE (t)) == INTEGER_CST)
+        {
+          if (!TYPE_MAX_VALUE (t))
+            return compute_array_index_type (NULL_TREE, NULL_TREE, complain);
+          
+          if (TREE_CODE (TYPE_MAX_VALUE (t)) == INTEGER_CST)
             return t;
+        }
 
       {
 	tree max, omax = TREE_OPERAND (TYPE_MAX_VALUE (t), 0);
diff --git a/gcc/cp/typeck2.c b/gcc/cp/typeck2.c
index 839091c..68d2e71 100644
--- a/gcc/cp/typeck2.c
+++ b/gcc/cp/typeck2.c
@@ -1013,6 +1013,14 @@  digest_init_r (tree type, tree init, bool nested, int flags,
      them if they were present.  */
   if (code == ARRAY_TYPE)
     {
+      if (nested
+	  && (!TYPE_DOMAIN (type) || !TYPE_MAX_VALUE (TYPE_DOMAIN (type))))
+	{
+	  /* Flexible array members do not have an upper bound.  */
+	  pedwarn (EXPR_LOC_OR_LOC (init, input_location), OPT_Wpedantic,
+		   "initialization of a flexible array member");
+	}
+      
       tree typ1 = TYPE_MAIN_VARIANT (TREE_TYPE (type));
       if (char_type_p (typ1)
 	  /*&& init */
@@ -1051,8 +1059,11 @@  digest_init_r (tree type, tree init, bool nested, int flags,
 	      init = copy_node (init);
 	      TREE_TYPE (init) = type;
 	    }
-	  if (TYPE_DOMAIN (type) != 0 && TREE_CONSTANT (TYPE_SIZE (type)))
+	  if (TYPE_DOMAIN (type)
+	      && TYPE_MAX_VALUE (TYPE_DOMAIN (type))
+	      && TREE_CONSTANT (TYPE_SIZE (type)))
 	    {
+	      /* Not a flexible array member.  */
 	      int size = TREE_INT_CST_LOW (TYPE_SIZE (type));
 	      size = (size + BITS_PER_UNIT - 1) / BITS_PER_UNIT;
 	      /* In C it is ok to subtract 1 from the length of the string
@@ -1240,8 +1251,10 @@  process_init_constructor_array (tree type, tree init,
   if (TREE_CODE (type) == ARRAY_TYPE)
     {
       tree domain = TYPE_DOMAIN (type);
-      if (domain && TREE_CONSTANT (TYPE_MAX_VALUE (domain)))
-	len = wi::ext (wi::to_offset (TYPE_MAX_VALUE (domain))
+      /* Flexible array members have no upper bound.  */
+      tree maxval = domain ? TYPE_MAX_VALUE (domain) : NULL_TREE;
+      if (domain && maxval && TREE_CONSTANT (maxval))
+	len = wi::ext (wi::to_offset (maxval)
 		       - wi::to_offset (TYPE_MIN_VALUE (domain)) + 1,
 		       TYPE_PRECISION (TREE_TYPE (domain)),
 		       TYPE_SIGN (TREE_TYPE (domain))).to_uhwi ();
@@ -1417,14 +1430,15 @@  process_init_constructor_record (tree type, tree init,
 	}
       else
 	{
-	  if (TREE_CODE (TREE_TYPE (field)) == REFERENCE_TYPE)
+	  const_tree fldtype = TREE_TYPE (field);
+	  if (TREE_CODE (fldtype) == REFERENCE_TYPE)
 	    {
 	      if (complain & tf_error)
 		error ("member %qD is uninitialized reference", field);
 	      else
 		return PICFLAG_ERRONEOUS;
 	    }
-	  else if (CLASSTYPE_REF_FIELDS_NEED_INIT (TREE_TYPE (field)))
+	  else if (CLASSTYPE_REF_FIELDS_NEED_INIT (fldtype))
 	    {
 	      if (complain & tf_error)
 		error ("member %qD with uninitialized reference fields", field);
@@ -1433,13 +1447,17 @@  process_init_constructor_record (tree type, tree init,
 	    }
 
 	  /* Warn when some struct elements are implicitly initialized
-	     to zero.  */
-	  if ((complain & tf_warning)
+	     to zero.  However, avoid issuing the warning for flexible
+	     array members since they need not have any elements.  */
+	  if ((TREE_CODE (fldtype) != ARRAY_TYPE
+	       || (TYPE_DOMAIN (fldtype)
+		   && TYPE_MAX_VALUE (TYPE_DOMAIN (fldtype))))
+	      && (complain & tf_warning)
 	      && !EMPTY_CONSTRUCTOR_P (init))
 	    warning (OPT_Wmissing_field_initializers,
 		     "missing initializer for member %qD", field);
 
-	  if (!zero_init_p (TREE_TYPE (field))
+	  if (!zero_init_p (fldtype)
 	      || skipped < 0)
 	    next = build_zero_init (TREE_TYPE (field), /*nelts=*/NULL_TREE,
 				    /*static_storage_p=*/false);
diff --git a/gcc/testsuite/g++.dg/ext/flexary.h b/gcc/testsuite/g++.dg/ext/flexary.h
new file mode 100644
index 0000000..a8dff7d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/flexary.h
@@ -0,0 +1,22 @@ 
+// Definitions of helper macros for tests of flexible array members.
+
+#if __cplusplus < 201102L
+#  define _CAT(x, y)  x ## y
+#  define CAT(x, y)  _CAT (x, y)
+
+// Generate a struct with a unique name containing a bitfield
+// of size that must evaluate to a non-zero value, otherwise
+// generate a compiler error.
+#  define ASSERT(expr)                                                  \
+  struct CAT (FAM_Assert, __LINE__) { unsigned asrt: 0 != (expr); }
+#else
+// In C++ 11 and beyond, use static_assert.
+# define ASSERT(expr) static_assert (expr, #expr)
+#endif
+
+// Macro to verify that a flexible array member is allocated
+// at the very end of the containing struct.
+#define ASSERT_AT_END(T, m)                             \
+  ASSERT (__builtin_offsetof (T, m) == sizeof (T))
+
+typedef __SIZE_TYPE__ size_t;
diff --git a/gcc/testsuite/g++.dg/ext/flexary2.C b/gcc/testsuite/g++.dg/ext/flexary2.C
index 4855b3f..dcd1bde 100644
--- a/gcc/testsuite/g++.dg/ext/flexary2.C
+++ b/gcc/testsuite/g++.dg/ext/flexary2.C
@@ -1,4 +1,10 @@ 
-// PR c++/46688
+// PR c++/46688 - [4.6 Regression] g++ requires a function declaration
+// when it should not
+// Note that although the definition of struct B in the test case for
+// c++/46688 was thought to be valid, it is, in fact, invalid, in C and
+// as noted in c++/42121, should be treated as invalid in C++ as well.
+// The test verifies that gcc detects and reports the right error.
+
 // { dg-options "" }
 
 struct A {
@@ -7,5 +13,10 @@  struct A {
 
 struct B {
     B() {}
-   A a[];
+    A a[];   // { dg-error "extension|flexible array .* in an otherwise empty" }
+};
+
+struct C {
+    C() {}
+    A a[0];  // -Wpedantic warning: ISO C++ forbids zero-size arrays
 };
diff --git a/gcc/testsuite/g++.dg/ext/flexary3.C b/gcc/testsuite/g++.dg/ext/flexary3.C
index 906877b..c7c0e79 100644
--- a/gcc/testsuite/g++.dg/ext/flexary3.C
+++ b/gcc/testsuite/g++.dg/ext/flexary3.C
@@ -1,7 +1,18 @@ 
-// PR c++/54441
+// PR c++/54441 - [4.7/4.8 Regression] Infinite loop with brace initializer
+//                on zero-length array
+// Note that although the definition of struct s in the test case for
+// c++/54441 was accepted as valid, it is, in fact, invalid in C, and
+// as noted in c++/42121, should be treated as invalid in C++ as well.
+// The test verifies that gcc detects, reports, and handles both errors
+// gracefully.
+// Note also that the error(s) issued for the invalid initializer depend
+// on c++/55606.
+
 // { dg-options "" }
 
-struct s { char c[]; };
+struct s {
+    char c[];   // { dg-error "flexible array member .* in an otherwise empty" }
+};
 
 int main()
 {
diff --git a/gcc/testsuite/g++.dg/ext/flexary4.C b/gcc/testsuite/g++.dg/ext/flexary4.C
new file mode 100644
index 0000000..97ec625
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/flexary4.C
@@ -0,0 +1,421 @@ 
+// PR c++/42121 - g++ should warn or error on internal 0 size array in struct
+// { dg-do compile }
+// { dg-options "-Wno-error=pedantic" }
+
+// Flexible array members are a feature of C99 (and newer) not provided
+// by C++ 2014 and prior.  G++ supports both the C99/C11 kind of flexible
+// array members and pre-C99 zero-size arrays (defining an array of size
+// zero).  Since both features are provided for compatibility with C,
+// G++ allows them in the same contexts as in C.
+
+#include "flexary.h"
+
+struct Sx {
+  int a[];                  // { dg-error "in an otherwise empty" }
+};
+
+// Verify that non-data members or static data members either before
+// or after a flexible array member in an otherwise empty struct don't
+// suppress the diagnostic.
+struct Sx2 {
+  int a[];                  // { dg-error "in an otherwise empty" }
+  typedef int I;
+};
+
+struct Sx3 {
+  typedef int I;
+  int a[];                  // { dg-error "in an otherwise empty" }
+};
+
+struct Sx4 {
+  int a[];                  // { dg-error "in an otherwise empty" }
+  enum E { e };
+};
+
+struct Sx5 {
+  enum E { e };
+  int a[];                  // { dg-error "in an otherwise empty" }
+};
+
+struct Sx6 {
+  int a[];                  // { dg-error "in an otherwise empty" }
+  static int i;
+};
+
+struct Sx7 {
+  static int i;
+  int a[];                  // { dg-error "in an otherwise empty" }
+};
+
+struct Sx8 {
+  int a[];                  // { dg-error "in an otherwise empty" }
+  Sx8 () { }
+};
+
+struct Sx9 {
+  Sx9 () { }
+  int a[];                  // { dg-error "in an otherwise empty" }
+};
+
+struct Sx10 {
+  int a[];                  // { dg-error "in an otherwise empty" }
+  virtual ~Sx10 () { }
+};
+
+struct Sx11 {
+  virtual ~Sx11 () { }
+  int a[];                  // { dg-error "in an otherwise empty" }
+};
+
+struct Sx12 {
+  int a[];                  // { dg-error "in an otherwise empty" }
+  virtual void foo () = 0;
+};
+
+struct Sx13 {
+  virtual void foo () = 0;
+  int a[];                  // { dg-error "in an otherwise empty" }
+};
+
+struct Sx14 {
+  int a[][1];               // { dg-error "in an otherwise empty" }
+};
+
+struct Sx15 {
+  typedef int A[];
+  A a;                      // { dg-error "in an otherwise empty" }
+};
+
+// Verify also that a zero-size array doesn't suppress the diagnostic.
+struct Sx16 {
+  // a_0 below is diagnosed with -Wpedantic only and emits
+  // warning: ISO C++ forbids zero-size arrays
+  int a_0 [0];
+  int a_x [];               // { dg-error "in an otherwise empty" }
+};
+
+struct Sx17 {
+  int a_x [];               // { dg-error "flexible array member" }
+
+  // a_0 below is diagnosed with -Wpedantic only and emits
+  // warning: ISO C++ forbids zero-size arrays
+  int a_0 [0];
+};
+
+// 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
+// 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 { };
+};
+
+// 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.
+struct Sx19 {
+  struct S { };
+  union U { };
+  int a_x [];               // { dg-error "in an otherwise empty" }
+};
+
+// Unlike in the case above, a named member of an anonymous struct
+// prevents a subsequent flexible array member from being diagnosed.
+struct Sx20 {
+  struct S { } s;
+  int a_x [];
+};
+
+struct Sx21 {
+  int a_x [];               // { dg-error "not at end" }
+  struct S { } s;
+};
+
+struct Sx22 {
+  int a_x [];               // { dg-error "not at end" }
+  union { int i; };
+};
+
+struct Sx23 {
+  union { int i; };
+  int a_x [];
+};
+
+struct Sx24 {
+  struct S;
+  S a_x [];                 // { dg-error "incomplete type" }
+};
+
+struct Sx25 {
+  struct S { };
+  S a_x [];                 // { dg-error "flexible array member" }
+};
+
+struct Sx26 {
+  struct { }
+    a_x [];                   // { dg-error "flexible array member" }
+};
+
+struct Sx27 {
+  int i;
+  struct { }
+    a_x [];
+};
+
+ASSERT_AT_END (Sx27, a_x);
+
+struct Sx28 {
+  struct { }
+    a_x [];                   // { dg-error "not at end" }
+  int i;
+};
+
+struct Sx29 {
+  // Pointer to an array of unknown size.
+  int (*a_x)[];
+};
+
+struct Sx30 {
+  // Reference to an array of unknown size.
+  int (&a_x)[];
+};
+
+struct Sx31 {
+  int a [];                 // { dg-error "not at end" }
+  unsigned i: 1;
+};
+
+struct Sx32 {
+  unsigned i: 1;
+  int a [];
+};
+
+ASSERT_AT_END (Sx32, a);
+
+struct Sx33 {
+  int a [];                 // { dg-error "otherwise empty" }
+  friend int foo ();
+};
+
+struct Sx34 {
+  friend int foo ();
+  int a [];                 // { dg-error "otherwise empty" }
+};
+
+// Verify that intervening non-field declarations of members other
+// than non-static data members don't affect the diagnostics.
+struct Sx35 {
+  int a[];                  // { dg-error "not at end" }
+  typedef int I;
+  int n;
+};
+
+struct Sx36 {
+  int n;
+  typedef int I;
+  int a[];
+};
+
+ASSERT_AT_END (Sx36, a);
+
+struct Sx37 {
+  int a[];                  // { dg-error "not at end" }
+  enum E { };
+  int n;
+};
+
+struct Sx38 {
+  int n;
+  enum E { };
+  int a[];
+};
+
+ASSERT_AT_END (Sx38, a);
+
+struct Sx39 {
+  int a[];                  // { dg-error "not at end" }
+  struct S;
+  int n;
+};
+
+struct Sx40 {
+  int n;
+  struct S;
+  int a[];
+};
+
+ASSERT_AT_END (Sx40, a);
+
+struct Sx41 {
+  int a[];                  // { dg-error "not at end" }
+  static int i;
+  int n;
+};
+
+struct Sx42 {
+  int n;
+  static int i;
+  int a[];
+};
+
+ASSERT_AT_END (Sx42, a);
+
+struct Sx43 {
+  int a[];                  // { dg-error "not at end" }
+  Sx43 ();
+  int n;
+};
+
+struct Sx44 {
+  int n;
+  Sx44 ();
+  int a[];
+};
+
+ASSERT_AT_END (Sx44, a);
+
+struct S_S_S_x {
+  struct A {
+    struct B {
+      int a[];              // { dg-error "flexible array member" }
+    } b;
+  } a;
+};
+
+// Since members of an anonymous struct or union are considered to be
+// members of the enclosing union the below defintions are valid and
+// must be accepted.
+
+struct Anon1 {
+  int n;
+  struct {
+    int good[];
+  };
+};
+
+ASSERT_AT_END (Anon1, good);
+
+struct Anon2 {
+  struct {
+    int n;
+    struct {
+      int good[];
+    };
+  };
+};
+
+ASSERT_AT_END (Anon2, good);
+
+struct Anon3 {
+  struct {
+    struct {
+      int n;
+      int good[];
+    };
+  };
+};
+
+ASSERT_AT_END (Anon3, good);
+
+struct Anon4 {
+  struct {
+    int in_empty_struct[];  // { dg-error "in an otherwise empty" }
+  };
+};
+
+struct Anon5 {
+  struct {
+    int not_at_end[];       // { dg-error "not at end" }
+  };
+  int n;
+};
+
+struct Anon6 {
+  struct {
+    struct {
+      int not_at_end[];     // { dg-error "not at end" }
+    };
+    int n;
+  };
+};
+
+
+struct Anon7 {
+  struct {
+    struct {
+      int not_at_end[];     // { dg-error "not at end" }
+    };
+  };
+  int n;
+};
+
+
+struct Six {
+  int i;
+  int a[];
+};
+
+ASSERT_AT_END (Six, a);
+
+class Cx {
+  int a[];                  // { dg-error "flexible array member" }
+};
+
+class Cix {
+  int i;
+  int a[];
+};
+
+struct Sxi {
+  int a[];                  // { dg-error "not at end" }
+  int i;
+};
+
+struct S0 {
+  int a[0];
+};
+
+struct S0i {
+  int a[0];
+  int i;
+};
+
+struct S_a0_ax {
+  int a0[0];
+  int ax[];                 // { dg-error "flexible array member" }
+};
+
+struct S_a0_i_ax {
+  int a0[0];
+  int i;
+  int ax[];
+};
+
+ASSERT_AT_END (S_a0_i_ax, ax);
+
+struct Si_a0_ax {
+  int i;
+  int a0[0];
+  int ax[];
+};
+
+ASSERT_AT_END (Si_a0_ax, ax);
+
+struct Si_ax_a0 {
+  int i;
+  int ax[];                 // { dg-error "not at end" }
+  int a0[0];
+};
+
+struct S_u0_ax {
+  union { } u[0];
+  int ax[];                 // { dg-error "flexible array member" }
+};
+
+struct S_a1_s2 {
+  int a[1];
+  int b[2];
+};
diff --git a/gcc/testsuite/g++.dg/ext/flexary5.C b/gcc/testsuite/g++.dg/ext/flexary5.C
new file mode 100644
index 0000000..3e76d3e
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/flexary5.C
@@ -0,0 +1,209 @@ 
+// { dg-do compile }
+// { dg-options "-Wno-error=pedantic" }
+
+// Test to verify flexible array members handling in base and derived
+// classes.
+
+#include "flexary.h"
+
+template <class T>
+struct S_no_diag: T {
+  char a[];   // cannot be diagnosed unless/until T is known
+};
+
+template <class T>
+struct STx_1: T {
+  char a[];   // { dg-error "flexible array member" }
+};
+
+template <class T, int I>
+struct STI: T {
+  char a[I];   // cannot be diagnosed unless/until T and I are known
+};
+
+template <class T, int I>
+struct STIx: T {
+  char a[I];
+};
+
+template <int> struct E { };
+
+STx_1<E<0> > stx_empty_1;
+STIx<E<0>, 0> stix_empty_1;
+
+// Verify that a sole flexible array member in a class with all empty
+// base classes is diagnosed.
+struct E1: E<0>, E<1> { };
+struct E2: E<2>, E<3> { };
+struct D1: E1, E2
+{
+    char a[];   // { dg-error "flexible array member" }
+};
+
+struct NE { size_t i; };
+
+struct A1x { int n, a[]; };
+struct D2: A1x, E1, E2 { };
+
+// Verify that the offset of the flexible array member is equal
+// to the size of each of the valid structs.
+ASSERT_AT_END (D2, a);
+
+struct D3: E1, A1x, E2 { };
+
+ASSERT_AT_END (D3, a);
+
+struct D4: E1, E2, A1x { };
+
+ASSERT_AT_END (D4, a);
+
+// Class with non-static data members and at least one base class
+// with such a member is not a standard layout class.  The warning
+// below is benign since GCC computes the expected value.
+struct D5: E1, E2, NE { char a[]; };
+
+ASSERT_AT_END (D5, a);   // { dg-warning "offsetof within non-standard-layout" }
+
+struct A2x {
+  size_t n;
+  size_t a[];   // { dg-error "not at end of .struct D6.| D7.| 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 DA2x: A2x { };
+
+struct D9: DA2x, E1, E2 { };
+
+ASSERT_AT_END (D9, a);
+
+struct D10: E1, DA2x, E2 { };
+
+ASSERT_AT_END (D10, a);
+
+struct D11: E1, E2, DA2x { };
+
+ASSERT_AT_END (D11, a);
+
+struct A3x {
+  size_t n;
+  size_t a[];   // { dg-error "not at end of .struct D12.| D13.| D14.| D15." }
+};
+
+// Verify that the flexible array member in A3x above is diagnosed
+// for each of the three struct defintions below which also derive
+// from another struct with a non-static member.
+struct D12: A3x, E1, NE { };
+struct D13: E1, A3x, NE { };
+struct D14: E1, E2, A3x, NE { };
+struct D15: E1, E2, NE, A3x { };
+
+struct A4x {
+  A4x ();
+  ~A4x ();
+
+  size_t n;
+  struct AS {
+    AS (int);
+    ~AS ();
+    size_t i;
+  } a[];
+};
+
+struct D16: A4x, E1, E2 { };
+
+ASSERT_AT_END (D16, a);
+
+struct D17: E1, A4x, E2 { };
+
+ASSERT_AT_END (D17, a);
+
+struct D18: E1, E2, A4x { };
+
+ASSERT_AT_END (D18, a);
+
+struct DA4x: A4x { };
+
+struct D19: DA4x, E1, E2 { };
+
+ASSERT_AT_END (D19, a);
+
+struct D20: E1, DA4x, E2 { };
+
+ASSERT_AT_END (D20, a);
+
+struct D21: E1, E2, DA4x { };
+
+ASSERT_AT_END (D21, a);
+
+
+struct A5x {
+  A5x (int);
+  virtual ~A5x ();
+
+  size_t n;
+  struct AS {
+    AS (int);
+    ~AS ();
+    size_t i;
+  } a[];
+};
+
+struct D22: A5x, E1, E2 { };
+
+ASSERT_AT_END (D22, a);   // { dg-warning "offsetof within non-standard-layout" }
+
+struct D23: E1, A5x, E2 { };
+
+ASSERT_AT_END (D23, a);   // { dg-warning "offsetof within non-standard-layout" }
+
+struct D24: E1, E2, A5x { };
+
+ASSERT_AT_END (D24, a);   // { dg-warning "offsetof within non-standard-layout" }
+
+struct DA5x: A5x { };
+
+struct D25: DA5x, E1, E2 { };
+
+ASSERT_AT_END (D25, a);   // { dg-warning "offsetof within non-standard-layout" }
+
+struct D26: E1, DA5x, E2 { };
+
+ASSERT_AT_END (D26, a);   // { dg-warning "offsetof within non-standard-layout" }
+
+struct D27: E1, E2, DA5x { };
+
+ASSERT_AT_END (D27, a);   // { dg-warning "offsetof within non-standard-layout" }
+
+// Verfify that a flexible array member is diagnosed even when deep
+// in the base class hierarchy.
+struct A6x {
+  size_t n;
+  size_t a[];               // { dg-error "not at end of .struct D28.| D29." }
+};
+
+struct AA6x: A6x { };
+struct NE1: NE { };
+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[];
+};
+
+struct DA7xV1: virtual A7x { };
+struct DA7xV2: virtual A7x { };
+
+struct D30: DA7xV1, DA7xV2 { };
+struct D31: DA7xV1, DA7xV2 { };
+struct D32: D30, D31 { };
diff --git a/gcc/testsuite/g++.dg/ext/flexary6.C b/gcc/testsuite/g++.dg/ext/flexary6.C
new file mode 100644
index 0000000..92677cd
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/flexary6.C
@@ -0,0 +1,23 @@ 
+// PR c++/68478 - flexible array members have complete type
+// { dg-do compile }
+// { dg-options "-Wno-error=pedantic" }
+
+// Test to verify that attempting to use a flexible array member where
+// a complete type is required is rejected.
+
+struct A {
+  int n;
+  int a[];
+  enum {
+    e = sizeof a   // { dg-error "invalid application of .sizeof. to incomplete type" }
+  };
+};
+
+struct B {
+  int n;
+  typedef int A[];
+  A a;
+  enum {
+    e = sizeof a   // { dg-error "invalid application of .sizeof. to incomplete type" }
+  };
+};
diff --git a/gcc/testsuite/g++.dg/ext/flexary7.C b/gcc/testsuite/g++.dg/ext/flexary7.C
new file mode 100644
index 0000000..fdea4d4
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/flexary7.C
@@ -0,0 +1,57 @@ 
+// PR c++/68613 - initializer-string for array of chars is too long error
+// on flexible array member
+// { dg-do compile }
+// { dg-options "-Wpedantic -Wno-error=pedantic" }
+
+struct FlexChar {
+    int n;
+    char a[];
+};
+
+struct FlexChar ac =
+  { 4, { "abc" } }; // { dg-warning "initialization of a flexible array member" }
+
+
+#if !__cplusplus
+typedef __WCHAR_TYPE__ wchar_t;
+#endif
+
+struct FlexWchar {
+    int n;
+    wchar_t a[];
+};
+
+struct FlexWchar awc =
+  { 3, { L"ab" } }; // { dg-warning "initialization of a flexible array member" }
+
+
+struct FlexInt {
+    int n;
+    int a[];
+};
+
+// Verify that no warning is issued for the case when a flexible array
+// member is not initialized (i.e., that a -Wmissing-field-initializer
+// isn't issued) because such arrays need not have any elements.
+struct FlexInt ai0 =
+  { 0 };
+
+struct FlexInt ai0_ =
+  { 0, { } };      // { dg-warning "initialization of a flexible array member" }
+
+struct FlexInt ai2 =
+  { 2, { 1, 2 } }; // { dg-warning "initialization of a flexible array member" }
+
+
+#if __cplusplus
+
+template <class T>
+struct FlexT {
+    int n;
+    T a[];
+};
+
+struct FlexT<char> atc =
+  { 4, { "abc" } }; // { dg-warning "initialization of a flexible array member" }
+
+#endif
diff --git a/gcc/testsuite/g++.dg/ext/flexary8.C b/gcc/testsuite/g++.dg/ext/flexary8.C
new file mode 100644
index 0000000..7a1811d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/flexary8.C
@@ -0,0 +1,33 @@ 
+// 68689 - flexible array members in unions accepted in C++
+// { dg-do compile }
+// { dg-options "-Wno-error=pedantic" }
+
+union U_i_ax {
+    int i;
+    int a[];                  // { dg-error "flexible array member in union" }
+};
+
+struct SU1 {
+  union {
+    int a[];                  // { dg-error "flexible array member in union" }
+  };
+};
+
+struct SU2 {
+  int n;
+  union {
+    int a[];                  // { dg-error "flexible array member in union" }
+  };
+};
+
+struct SU3 {
+  union {
+    int n;
+    int a[];                  // { dg-error "flexible array member in union" }
+  };
+};
+
+union U_i_a0 {
+    int i;
+    int a[0];
+};
diff --git a/gcc/testsuite/g++.dg/ext/flexary9.C b/gcc/testsuite/g++.dg/ext/flexary9.C
new file mode 100644
index 0000000..3228542
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/flexary9.C
@@ -0,0 +1,405 @@ 
+// { dg-do compile }
+// { dg-options "-Wpedantic -Wno-error=pedantic" }
+
+#include "flexary.h"
+
+struct Sx {
+  int a[0];                 // { dg-warning "zero-size|in an otherwise empty" }
+};
+
+// Verify that non-data members or static data members either before
+// or after a zero-length array in an otherwise empty struct don't
+// suppress the diagnostic.
+struct Sx2 {
+  int a[0];                 // { dg-warning "zero-size|in an otherwise empty" }
+  typedef int I;
+};
+
+struct Sx3 {
+  typedef int I;
+  int a[0];                 // { dg-warning "zero-size|in an otherwise empty" }
+};
+
+struct Sx4 {
+  int a[0];                 // { dg-warning "zero-size|in an otherwise empty" }
+  enum E { e };
+};
+
+struct Sx5 {
+  enum E { e };
+  int a[0];                 // { dg-warning "zero-size|in an otherwise empty" }
+};
+
+struct Sx6 {
+  int a[0];                 // { dg-warning "zero-size|in an otherwise empty" }
+  static int i;
+};
+
+struct Sx7 {
+  static int i;
+  int a[0];                 // { dg-warning "zero-size|in an otherwise empty" }
+};
+
+struct Sx8 {
+  int a[0];                 // { dg-warning "zero-size|in an otherwise empty" }
+  Sx8 () { }
+};
+
+struct Sx9 {
+  Sx9 () { }
+  int a[0];                 // { dg-warning "zero-size|in an otherwise empty" }
+};
+
+struct Sx10 {
+  int a[0];                 // { dg-warning "zero-size|in an otherwise empty" }
+  virtual ~Sx10 () { }
+};
+
+struct Sx11 {
+  virtual ~Sx11 () { }
+  int a[0];                 // { dg-warning "zero-size|in an otherwise empty" }
+};
+
+struct Sx12 {
+  int a[0];                 // { dg-warning "zero-size|in an otherwise empty" }
+  virtual void foo () = 0;
+};
+
+struct Sx13 {
+  virtual void foo () = 0;
+  int a[0];                 // { dg-warning "zero-size|in an otherwise empty" }
+};
+
+struct Sx14 {
+  int a[0][1];             // { dg-warning "zero-size|in an otherwise empty" }
+};
+
+struct Sx15 {
+  typedef int A[0];         // { dg-warning "zero-size" }
+  A a;                      // { dg-warning "in an otherwise empty" }
+};
+
+// Verify also that a zero-size array doesn't suppress the diagnostic.
+struct Sx16 {
+  int a_0 [0];              // { dg-warning "zero-size|in an otherwise empty" }
+  int a_x [0];              // { dg-warning "zero-size array" }
+};
+
+struct Sx17 {
+  int a_x [0];              // { dg-warning "zero-size|in an otherwise empty" }
+  int a_0 [0];              // { dg-warning "zero-size array" }
+};
+
+// 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
+// containing a zero-length array followed by an empty struct
+// is diagnosed to prevent the former subobject from sharing space
+// with the latter.
+struct Sx18 {
+  int a_x [0];              // { dg-warning "zero-size array" }
+  struct S { };
+};
+
+// Anonymous structs and unions are another GCC extension.  Since
+// they cannot be named and thus used to store the size of a zero
+// length array member, a struct containing both is diagnosed as
+// if the zero-length array appeared alone.
+struct Sx19 {
+  struct S { };
+  union U { };
+  int a_x [0];              // { dg-warning "zero-size|in an otherwise empty" }
+};
+
+// Unlike in the case above, a named member of an anonymous struct
+// prevents a subsequent zero-length array from being diagnosed.
+struct Sx20 {
+  struct S { } s;
+  int a_x [0];              // { dg-warning "zero-size array" }
+};
+
+struct Sx21 {
+  int a_x [0];              // { dg-warning "zero-size array|not at end" }
+  struct S { } s;
+};
+
+struct Sx22 {
+  int a_x [0];              // { dg-warning "zero-size array|not at end" }
+  union { int i; };
+};
+
+struct Sx23 {
+  union { int i; };
+  int a_x [0];              // { dg-warning "zero-size array" }
+};
+
+// The following causes an incomplete type error error and a zero-size
+// array warning.
+struct Sx24 {
+  struct S;
+  S a_x [0];                // { dg-message "incomplete type|zero-size array" }
+};
+
+struct Sx25 {
+  struct S { };
+  S a_x [0];                // { dg-warning "zero-size array" }
+};
+
+struct Sx26 {
+  struct { }
+    a_x [0];                // { dg-warning "zero-size array" }
+};
+
+struct Sx27 {
+  int i;
+  struct { }
+    a_x [0];                // { dg-warning "zero-size array" }
+};
+
+ASSERT_AT_END (Sx27, a_x);
+
+struct Sx28 {
+  struct { }
+    a_x [0];                // { dg-warning "zero-size array|not at end" }
+  int i;
+};
+
+struct Sx29 {
+  // Pointer to an array of zero size.
+  int (*a_x)[0];            // { dg-warning "zero-size array" }
+};
+
+struct Sx30 {
+  // Reference to an array of zero size.
+  int (&a_x)[0];            // { dg-warning "zero-size array" }
+};
+
+struct Sx31 {
+  int a [0];                // { dg-warning "zero-size array|not at end" }
+  unsigned i: 1;
+};
+
+struct Sx32 {
+  unsigned i: 1;
+  int a [0];                // { dg-warning "zero-size array" }
+};
+
+ASSERT_AT_END (Sx32, a);
+
+struct Sx33 {
+  int a [0];                // { dg-warning "zero-size array|otherwise empty" }
+  friend int foo ();
+};
+
+struct Sx34 {
+  friend int foo ();
+  int a [0];                // { dg-warning "zero-size array|otherwise empty" }
+};
+
+// Verify that intervening non-field declarations of members other
+// than non-static data members don't affect the diagnostics.
+struct Sx35 {
+  int a[0];                 // { dg-warning "zero-size array|not at end" }
+  typedef int I;
+  int n;
+};
+
+struct Sx36 {
+  int n;
+  typedef int I;
+  int a[0];                 // { dg-warning "zero-size array" }
+};
+
+ASSERT_AT_END (Sx36, a);
+
+struct Sx37 {
+  int a[0];                 // { dg-warning "zero-size array|not at end" }
+  enum E { };
+  int n;
+};
+
+struct Sx38 {
+  int n;
+  enum E { };
+  int a[0];                 // { dg-warning "zero-size array" }
+};
+
+ASSERT_AT_END (Sx38, a);
+
+struct Sx39 {
+  int a[0];                 // { dg-warning "zero-size array|not at end" }
+  struct S;
+  int n;
+};
+
+struct Sx40 {
+  int n;
+  struct S;
+  int a[0];                 // { dg-warning "zero-size array" }
+};
+
+ASSERT_AT_END (Sx40, a);
+
+struct Sx41 {
+  int a[0];                 // { dg-warning "zero-size array|not at end" }
+  static int i;
+  int n;
+};
+
+struct Sx42 {
+  int n;
+  static int i;
+  int a[0];                 // { dg-warning "zero-size array" }
+};
+
+ASSERT_AT_END (Sx42, a);
+
+struct Sx43 {
+  int a[0];                 // { dg-warning "zero-size array|not at end" }
+  Sx43 ();
+  int n;
+};
+
+struct Sx44 {
+  int n;
+  Sx44 ();
+  int a[0];                 // { dg-warning "zero-size array" }
+};
+
+ASSERT_AT_END (Sx44, a);
+
+struct S_S_S_x {
+  struct A {
+    struct B {
+      int a[0];             // { dg-warning "zero-size array" }
+    } b;
+  } a;
+};
+
+// Since members of an anonymous struct or union are considered to be
+// members of the enclosing union the below defintions are valid and
+// must be accepted.
+
+struct Anon1 {
+  int n;
+  struct {
+    int good[0];            // { dg-warning "zero-size array" }
+  };                        // { dg-warning "anonymous struct" }
+};
+
+ASSERT_AT_END (Anon1, good);
+
+struct Anon2 {
+  struct {
+    int n;
+    struct {
+      int good[0];          // { dg-warning "zero-size array" }
+    };                      // { dg-warning "anonymous struct" }
+  };                        // { dg-warning "anonymous struct" }
+};
+
+ASSERT_AT_END (Anon2, good);
+
+struct Anon3 {
+  struct {
+    struct {
+      int n;
+      int good[0];          // { dg-warning "zero-size array" }
+    };                      // { dg-warning "anonymous struct" }
+  };                        // { dg-warning "anonymous struct" }
+};
+
+ASSERT_AT_END (Anon3, good);
+
+struct Anon4 {
+  struct {
+    int in_empty_struct[0]; // { dg-warning "zero-size array|in an otherwise empty" }
+  };                        // { dg-warning "anonymous struct" }
+};
+
+struct Anon5 {
+  struct {
+    int not_at_end[0];      // { dg-warning "zero-size array|not at end" }
+  };                        // { dg-warning "anonymous struct" }
+  int n;
+};
+
+struct Anon6 {
+  struct {
+    struct {
+      int not_at_end[0];    // { dg-warning "zero-size array|not at end" }
+    };                      // { dg-warning "anonymous struct" }
+    int n;
+  };                        // { dg-warning "anonymous struct" }
+};
+
+
+struct Anon7 {
+  struct {
+    struct {
+      int not_at_end[0];    // { dg-warning "zero-size array|not at end" }
+    };                      // { dg-warning "anonymous struct" }
+  };                        // { dg-warning "anonymous struct" }
+  int n;
+};
+
+
+struct Six {
+  int i;
+  int a[0];                 // { dg-warning "zero-size array" }
+};
+
+ASSERT_AT_END (Six, a);
+
+class Cx {
+  int a[0];                 // { dg-warning "zero-size array" }
+};
+
+class Cix {
+  int i;
+  int a[0];                 // { dg-warning "zero-size array" }
+};
+
+struct Sxi {
+  int a[0];                 // { dg-warning "zero-size array|not at end" }
+  int i;
+};
+
+struct S0 {
+  int a[0];                 // { dg-warning "zero-size array" }
+};
+
+struct S0i {
+  int a[0];                 // { dg-warning "zero-size array" }
+  int i;
+};
+
+struct S_a0_ax {
+  int a1[0];                // { dg-warning "zero-size array" }
+  int ax[0];                // { dg-warning "zero-size array" }
+};
+
+struct S_a0_i_ax {
+  int a1[0];                // { dg-warning "zero-size array" }
+  int i;
+  int ax[0];                // { dg-warning "zero-size array" }
+};
+
+ASSERT_AT_END (S_a0_i_ax, ax);
+
+struct Si_a0_ax {
+  int i;
+  int a1[0];                // { dg-warning "zero-size array" }
+  int ax[0];                // { dg-warning "zero-size array" }
+};
+
+ASSERT_AT_END (Si_a0_ax, ax);
+
+struct S_u0_ax {
+  union { } u[0];           // { dg-warning "zero-size array" }
+  int ax[0];                // { dg-warning "zero-size array" }
+};
+
+struct S_a1_s2 {
+  int a[1];
+  int b[2];
+};
diff --git a/gcc/testsuite/g++.dg/other/dump-ada-spec-2.C b/gcc/testsuite/g++.dg/other/dump-ada-spec-2.C
index 87c183a..d1af7e0 100644
--- a/gcc/testsuite/g++.dg/other/dump-ada-spec-2.C
+++ b/gcc/testsuite/g++.dg/other/dump-ada-spec-2.C
@@ -7,5 +7,5 @@  struct S
   __extension__ unsigned char data[];
 };
 
-/* { dg-final { scan-ada-spec "array \\(0 .. -1\\)" } } */
+/* { dg-final { scan-ada-spec "array \\(0 .. 0\\)" } } */
 /* { dg-final { cleanup-ada-spec } } */
diff --git a/gcc/testsuite/g++.dg/parse/pr43765.C b/gcc/testsuite/g++.dg/parse/pr43765.C
index 0b341dd..800f2c7 100644
--- a/gcc/testsuite/g++.dg/parse/pr43765.C
+++ b/gcc/testsuite/g++.dg/parse/pr43765.C
@@ -3,12 +3,15 @@ 
 
 struct SomeType
 {
+    int n;
     const char *values[];
 };
 const char *temp[] = {"607", "612", 0};
 
 SomeType vals[] =
     {
-        { values : temp, },
+        { 0, values : temp, },
         0
-    };          // { dg-error "invalid" }
+    };   // { dg-error "GNU-style designated initializer for an array|cannot convert" }
+// (note the error above is on the wrong line)
+ 
diff --git a/gcc/testsuite/g++.dg/torture/pr64280.C b/gcc/testsuite/g++.dg/torture/pr64280.C
index 6ea3148..e756e02 100644
--- a/gcc/testsuite/g++.dg/torture/pr64280.C
+++ b/gcc/testsuite/g++.dg/torture/pr64280.C
@@ -15,7 +15,7 @@  public:
 typedef int jmp_buf[];
 struct C
 {
-  jmp_buf cond_;
+  jmp_buf cond_;   // { dg-error "flexible array member" }
 };
 class F
 {
diff --git a/gcc/testsuite/g++.dg/torture/pr64312.C b/gcc/testsuite/g++.dg/torture/pr64312.C
index dc3e95d..85211f2 100644
--- a/gcc/testsuite/g++.dg/torture/pr64312.C
+++ b/gcc/testsuite/g++.dg/torture/pr64312.C
@@ -43,6 +43,7 @@  protected:
 class F
 {
 public:
+  int nelems;
   int elems[];
   int *
   m_fn1 ()
diff --git a/gcc/testsuite/g++.dg/ubsan/object-size-1.C b/gcc/testsuite/g++.dg/ubsan/object-size-1.C
index e2aad46..e6cdefc 100644
--- a/gcc/testsuite/g++.dg/ubsan/object-size-1.C
+++ b/gcc/testsuite/g++.dg/ubsan/object-size-1.C
@@ -1,9 +1,9 @@ 
 // { dg-do compile }
-// { dg-options "-fsanitize=undefined -fpermissive" }
+// { dg-options "-Wpedantic -Wno-error=pedantic -fsanitize=undefined -fpermissive" }
 
 struct T { int c; char d[]; };
 
-struct T t = { 1, "a" }; // { dg-warning "initializer-string for array of chars is too long" }
+struct T t = { 1, "a" }; // { dg-warning "initialization of a flexible array member " }
 
 int
 baz (int i)
diff --git a/gcc/tree-chkp.c b/gcc/tree-chkp.c
index 34d9dfc..6a6b07b 100644
--- a/gcc/tree-chkp.c
+++ b/gcc/tree-chkp.c
@@ -1664,8 +1664,10 @@  chkp_find_bound_slots_1 (const_tree type, bitmap have_bound,
 				     offs + field_offs);
 	  }
     }
-  else if (TREE_CODE (type) == ARRAY_TYPE)
+  else if (TREE_CODE (type) == ARRAY_TYPE && TYPE_DOMAIN (type))
     {
+      /* The object type is an array of complete type, i.e., other
+	 than a flexible array.  */
       tree maxval = TYPE_MAX_VALUE (TYPE_DOMAIN (type));
       tree etype = TREE_TYPE (type);
       HOST_WIDE_INT esize = TREE_INT_CST_LOW (TYPE_SIZE (etype));
diff --git a/gcc/tree.c b/gcc/tree.c
index 1d770c3..2ba786f 100644
--- a/gcc/tree.c
+++ b/gcc/tree.c
@@ -3588,9 +3588,10 @@  type_contains_placeholder_1 (const_tree type)
 	      || CONTAINS_PLACEHOLDER_P (TYPE_MAX_VALUE (type)));
 
     case ARRAY_TYPE:
-      /* We have already checked the component type above, so just check the
-	 domain type.  */
-      return type_contains_placeholder_p (TYPE_DOMAIN (type));
+      /* We have already checked the component type above, so just check
+	 the domain type.  Flexible array members have a null domain.  */
+      return TYPE_DOMAIN (type) ?
+	type_contains_placeholder_p (TYPE_DOMAIN (type)) : false;
 
     case RECORD_TYPE:
     case UNION_TYPE:
diff --git a/gcc/varasm.c b/gcc/varasm.c
index ec6aabf..606de4d 100644
--- a/gcc/varasm.c
+++ b/gcc/varasm.c
@@ -4821,8 +4821,7 @@  array_size_for_constructor (tree val)
 {
   tree max_index;
   unsigned HOST_WIDE_INT cnt;
-  tree index, value, tmp;
-  offset_int i;
+  tree index, value;
 
   /* This code used to attempt to handle string constants that are not
      arrays of single-bytes, but nothing else does, so there's no point in
@@ -4842,9 +4841,14 @@  array_size_for_constructor (tree val)
   if (max_index == NULL_TREE)
     return 0;
 
+  offset_int i = wi::to_offset (max_index) + 1;
+
+  if (TYPE_DOMAIN (TREE_TYPE (val)))
+    {
       /* Compute the total number of array elements.  */
-  tmp = TYPE_MIN_VALUE (TYPE_DOMAIN (TREE_TYPE (val)));
-  i = wi::to_offset (max_index) - wi::to_offset (tmp) + 1;
+      tree tmp = TYPE_MIN_VALUE (TYPE_DOMAIN (TREE_TYPE (val)));
+      i -= wi::to_offset (tmp);
+    }
   
   /* Multiply by the array element unit size to find number of bytes.  */
   i *= wi::to_offset (TYPE_SIZE_UNIT (TREE_TYPE (TREE_TYPE (val))));
@@ -4974,13 +4978,15 @@  output_constructor_regular_field (oc_local_state *local)
 	 but we cannot do this until the deprecated support for
 	 initializing zero-length array members is removed.  */
       if (TREE_CODE (TREE_TYPE (local->field)) == ARRAY_TYPE
-	  && TYPE_DOMAIN (TREE_TYPE (local->field))
-	  && ! TYPE_MAX_VALUE (TYPE_DOMAIN (TREE_TYPE (local->field))))
+	  && (!TYPE_DOMAIN (TREE_TYPE (local->field))
+	      || !TYPE_MAX_VALUE (TYPE_DOMAIN (TREE_TYPE (local->field)))))
 	{
 	  fieldsize = array_size_for_constructor (local->val);
-	  /* Given a non-empty initialization, this field had
-	     better be last.  */
-	  gcc_assert (!fieldsize || !DECL_CHAIN (local->field));
+	  /* Given a non-empty initialization, this field had better
+	     be last.  Given a flexible array member, the next field
+	     on the chain is a TYPE_DECL of the enclosing struct.  */
+	  const_tree next = DECL_CHAIN (local->field);
+	  gcc_assert (!fieldsize || !next || TREE_CODE (next) != FIELD_DECL);
 	}
       else
 	fieldsize = tree_to_uhwi (DECL_SIZE_UNIT (local->field));
@@ -5196,7 +5202,7 @@  output_constructor (tree exp, unsigned HOST_WIDE_INT size, unsigned int align,
   if (TREE_CODE (local.type) == ARRAY_TYPE && TYPE_DOMAIN (local.type))
     local.min_index = TYPE_MIN_VALUE (TYPE_DOMAIN (local.type));
   else
-    local.min_index = NULL_TREE;
+    local.min_index = integer_zero_node;
 
   local.total_bytes = 0;
   local.byte_buffer_in_use = outer != NULL;