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 + + * 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 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 + 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 K : public I > {