diff mbox

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

Message ID 5f633d7d-e730-cbc1-5c71-b7ab5dea7840@gmail.com
State New
Headers show

Commit Message

Martin Sebor Sept. 20, 2016, 5:01 p.m. UTC
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

Comments

Jason Merrill Sept. 21, 2016, 8:43 p.m. UTC | #1
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
Martin Sebor Sept. 22, 2016, 11:39 p.m. UTC | #2
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
Jason Merrill Sept. 23, 2016, 6 p.m. UTC | #3
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
Martin Sebor Oct. 5, 2016, 9:43 p.m. UTC | #4
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
diff mbox

Patch

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

gcc/cp/ChangeLog:
	PR c++/71912
	* class.c (struct flexmems_t):  Add members.
	(find_flexarrays): Add arguments.  Correct handling of anonymous
	structs.
	(diagnose_flexarrays): Adjust to issue warnings in addition to errors.
	(check_flexarrays): Add argument.
	(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> >
 {