[CC Jason for the C++ changes and Joseph for the one C change.]
Attached is a reworked and expanded patch for the bug plus three others in the same area that I uncovered while developing and testing the former patch: c++/68689 - flexible array members in unions accepted in C++ c++/68478 - flexible array members have complete type c++/68613 - initializer-string for array of chars is too long error on flexible array member The patch should bring C++ support for flexible array members closer to C (most of the same constructs should be accepted and rejected). The only C change in this patch is to include the size of excessively large types in diagnostics (I found knowing the size helpful when adding tests and I think it might be helpful to others as well). Unlike in my first attempt, this patch distinguishes flexible array members from zero-length arrays by setting the upper bound of the former to null. This seems to be in line with what the C front end does but has required bigger changes than I had hoped. Hopefully, the result is a more consistent treatment of the extension between the two front ends (for example, both C and C++ now emit the same ADA specification for flexible array members). Tested by bootstrapping and running C and C++ tests (including libstdc++) on x86_64. I'm not sure if this is appropriate for this stage or if it needs to wait until after the release. Either is fine with me. Martin On 11/21/2015 03:17 PM, Martin Sebor wrote:
Bug 42121 - g++ should warn or error on internal 0 size array in struct, is a request to diagnose declarations of flexible array members that aren't last in the enclosing struct, such as in the following: struct S { int a; char b[]; // invalid int c; }; The C front end diagnoses such cases because they are invalid in standard C. Comment 8 on the bug points out that flexible array members should not be treated identically to zero-size arrays (they're not in C). The attached patch implements the requested diagnostic, keeping comment 8 in mind. It also issues a diagnostic for flexible array members in unions (which are also diagnosed as invalid in C mode). The patch found a number of instances of invalid flexible array members in the C++ test suites. I corrected them. Since the C++ front end doesn't distinguish between flexible array members and zero-size arrays (both are considered to have an upper bound of SIZE_MAX), and since determining whether or not a declaration of such a member is valid cannot be done until the whole containing struct has been processed, the patch makes use one of the DECL_LANG_FLAGs to temporarily remember which is which (I somewhat arbitrarily picked DECL_LANG_FLAG_1), before clearing it. There might be a better flag to use, and it might be appropriate to define a descriptive macro for this purpose in cp-tree.h, along the same lines as the macros already defined for other such purposes. Martin
gcc/testsuite/ChangeLog: 2015-12-02 Martin Sebor <mse...@redhat.com> c++/42121 c++/68478 c++/68613 c++/68689 * g++.dg/ext/flexary2.C: Expect a sole flexible array member to be rejected. Add a test case exercising zero-length array. * g++.dg/ext/flexary3.C: Expect a sole flexible array member to be rejected. * g++.dg/ext/flexary4.C: New file. * g++.dg/ext/flexary5.C: New file. * g++.dg/ext/flexary6.C: New file. * g++.dg/ext/flexary7.C: New file. * g++.dg/other/dump-ada-spec-2.C: Adjust to reflect flexible array members. * g++.dg/parse/pr43765.C: Add a member to make a struct with a flexible array member valid. Adjust expected error message. * g++.dg/torture/pr64280.C: Expect a sole flexible array member to be rejected. * g++.dg/torture/pr64312.C: Add a member to make a struct with a flexible array member valid. * g++.dg/ubsan/object-size-1.C: Adjust expected diagnostic. * g++.dg/other/dump-ada-spec-2.C: Adjust expected type. gcc/cp/ChangeLog: 2015-12-02 Martin Sebor <mse...@redhat.com> c++/42121 c++/68478 c++/68613 c++/68689 * class.c (walk_subobject_offsets): Avoid assuming type domain is non-null or has an upper bound. (layout_class_type): Include type size in error message. (all_bases_empty_p, field_nonempty_p): New helper functions. (check_flexarrays): New function. (finish_struct_1): Call check_flexarrays. * decl.c (compute_array_index_type): Distinguish flexible array members from zero-length arrays. (grokdeclarator): Reject flexible array members in unions. Avoid rejecting members of incomplete types that are flexible array members. * error.c (dump_type_suffix): Handle flexible array members with null upper bound. * init.c (perform_member_init): Same. * pt.c (instantiate_class_template_1): Allow flexible array members. (tsubst): Handle flexible array members with null upper bound. * typeck2.c (digest_init_r): Warn for initialization of flexible array members. (process_init_constructor_record): Handle flexible array members. gcc/c/ChangeLog: 2015-12-02 Martin Sebor <mse...@redhat.com> c++/42121 * c-decl.c (grokdeclarator): Include type size in error message. (finish_struct): Same. gcc/ChangeLog: 2015-12-02 Martin Sebor <mse...@redhat.com> c++/42121 * tree-chkp.c (chkp_find_bound_slots_1): Handle flexible array members. * tree.c (type_contains_placeholder_1): Avoid assuming type has a non-null domain or an upper bound to handle flexible array members. * varasm.c (array_size_for_constructor): Same. (output_constructor_regular_field): Same. (output_constructor): Set min_index to integer_zero_node rather than null when a type has no domain to avoid crashing later. diff --git a/gcc/c/c-decl.c b/gcc/c/c-decl.c index 7b9ab8a..e55471f 100644 --- a/gcc/c/c-decl.c +++ b/gcc/c/c-decl.c @@ -5868,10 +5868,12 @@ grokdeclarator (const struct c_declarator *declarator, && !int_fits_type_p (size, index_type)) { if (name) - error_at (loc, "size of array %qE is too large", - name); + error_at (loc, "size of array %qE is too large " + "(%qE bytes)", + name, size); else - error_at (loc, "size of unnamed array is too large"); + error_at (loc, "size of unnamed array is too large" + " (%qE bytes)", size); type = error_mark_node; continue; } @@ -7701,7 +7703,7 @@ finish_struct (location_t loc, tree t, tree fieldlist, tree attributes, && TREE_CODE (TYPE_SIZE_UNIT (t)) == INTEGER_CST && !TREE_OVERFLOW (TYPE_SIZE_UNIT (t)) && !valid_constant_size_p (TYPE_SIZE_UNIT (t))) - error ("type %qT is too large", t); + error ("size of type %qT is too large (%qE bytes)", t, TYPE_SIZE_UNIT (t)); /* Give bit-fields their proper types and rewrite the type of array fields with scalar component if the enclosing type has reverse storage order. */ diff --git a/gcc/cp/class.c b/gcc/cp/class.c index 216a301..e0c6758 100644 --- a/gcc/cp/class.c +++ b/gcc/cp/class.c @@ -35,6 +35,7 @@ along with GCC; see the file COPYING3. If not see #include "convert.h" #include "dumpfile.h" #include "gimplify.h" +#include "intl.h" /* The number of nested classes being processed. If we are not in the scope of any class, this is zero. */ @@ -4114,7 +4115,10 @@ walk_subobject_offsets (tree type, /* Avoid recursing into objects that are not interesting. */ if (!CLASS_TYPE_P (element_type) - || !CLASSTYPE_CONTAINS_EMPTY_CLASS_P (element_type)) + || !CLASSTYPE_CONTAINS_EMPTY_CLASS_P (element_type) + || !domain + /* Flexible array members have no upper bound. */ + || !TYPE_MAX_VALUE (domain)) return 0; /* Step through each of the elements in the array. */ @@ -6531,7 +6535,7 @@ layout_class_type (tree t, tree *virtuals_p) && TREE_CODE (TYPE_SIZE_UNIT (t)) == INTEGER_CST && !TREE_OVERFLOW (TYPE_SIZE_UNIT (t)) && !valid_constant_size_p (TYPE_SIZE_UNIT (t))) - error ("type %qT is too large", t); + error ("size of type %qT is too large (%qE bytes)", t, TYPE_SIZE_UNIT (t)); /* Warn about bases that can't be talked about due to ambiguity. */ warn_about_ambiguous_bases (t); @@ -6598,12 +6602,184 @@ sorted_fields_type_new (int n) } +/* Return true when all base classes of class T (a class type) are + empty, false otherwise. */ + +static bool +all_bases_empty_p (tree t) +{ + int i; + tree binfo; + tree base_binfo; + + for (binfo = TYPE_BINFO (t), i = 0; + BINFO_BASE_ITERATE (binfo, i, base_binfo); i++) + { + tree basetype = TREE_TYPE (base_binfo); + gcc_assert (COMPLETE_TYPE_P (basetype)); + + if (!is_empty_class (basetype)) + return false; + } + + return true; +} + +/* Helper of finish_struct_1. Return true when FLD refers to a non-static + class data member of non-zero size, otherwise false. */ + +static inline bool +field_nonempty_p (const_tree fld) +{ + if (TREE_CODE (fld) == ERROR_MARK) + return false; + + tree type = TREE_TYPE (fld); + if (TREE_CODE (fld) == FIELD_DECL + && TREE_CODE (type) != ERROR_MARK + && (DECL_NAME (fld) || RECORD_OR_UNION_TYPE_P (type))) + { + + return TYPE_SIZE (type) + && (TREE_CODE (TYPE_SIZE (type)) != INTEGER_CST + || !tree_int_cst_equal (size_zero_node, TYPE_SIZE (type))); + } + + return false; +} + +/* Check to make sure any definitions of flexible array members + and zero-size arrays in a chain of FIELDS are valid, and + diagnose those that aren't. T denotes a struct, class, or + union whose fields are being checked. SEEN_FIELD is used + internally by recursive invocations of the function to set + the pointed-to object to true when a declaration of a non- + empty non-static data member precedes the declaration of + a flexible array member or a zero-size array. +*/ + +static tree +check_flexarrays (tree t, tree fields, bool *seen_field) +{ + /* Members of anonymous structs and unions are considered to be members + of the containing struct or union. */ + if (TYPE_ANONYMOUS_P (t)) + return NULL_TREE; + + /* Unlike the C front end, the C++ front end sets the upper bound + of a flexible array member to SIZE_MAX. Precompute that value + up front to avoid recomputing it on each iteration. */ + tree size_max_node = + int_const_binop (MINUS_EXPR, size_zero_node, size_one_node); + + /* Iterate over struct members, looking for definitions of non-static + flexible array members and zero-size arrays and determine whether + each is valid. */ + for (tree fld = fields, next, f; fld; + *seen_field = *seen_field || field_nonempty_p (f), fld = next) + { + next = DECL_CHAIN (fld); + + /* Use f below to check the current field, or to substitute + for it the flexible array member or zero-size array of + the anonymous struct it represents. */ + f = fld; + + const_tree fldtype = TREE_TYPE (f); + if (TREE_CODE (f) != TYPE_DECL + && RECORD_OR_UNION_TYPE_P (fldtype) + && TYPE_ANONYMOUS_P (fldtype)) + { + /* Descend into the anonymous struct or union and check + its fields. The recursive call to the function will + either return 0 or the flexible array member whose + validity depends on whether any non-static data members + are declared in the enclosing struct. */ + tree flexarray = + check_flexarrays (t, TYPE_FIELDS (fldtype), seen_field); + if (flexarray) + { + f = flexarray; + fldtype = TREE_TYPE (flexarray); + } + } + + /* Skip anything that's not a (non-static) data member. */ + if (TREE_CODE (f) != FIELD_DECL) + continue; + + /* Skip non-arrays. */ + if (TREE_CODE (fldtype) != ARRAY_TYPE || !DECL_CHAIN (f)) + continue; + + /* Determine the upper bound of the array if it has one. */ + const_tree dom = TYPE_DOMAIN (fldtype); + + /* Skip arrays whose upper bound is less than SIZE_MAX. */ + if (dom && TYPE_MAX_VALUE (dom) + && !tree_int_cst_equal (size_max_node, TYPE_MAX_VALUE (dom))) + continue; + + if (dom && TYPE_MAX_VALUE (dom)) + { + /* Zero-length arrays have a domain with an upper bound + of SIZE_MAX. */ + const char *msg = 0; + + if (next && TREE_CODE (next) == FIELD_DECL) + msg = G_("zero-size array member %qD not at end of %q#T"); + else if (!*seen_field && all_bases_empty_p (t)) + msg = G_("zero-size array member %qD in an otherwise empty %q#T"); + + if (msg && pedwarn (DECL_SOURCE_LOCATION (f), OPT_Wpedantic, + msg, f, t)) + inform (input_location, "%q#T declared here", + DECL_FIELD_CONTEXT (f)); + } + else + { + /* Flexible array members don't have an upper bound. */ + const char *msg = 0; + + if (next && TREE_CODE (next) == FIELD_DECL) + msg = G_("flexible array member %qD not at end of %q#T"); + else if (!*seen_field && all_bases_empty_p (t)) + { + /* When f is a member of anonymous struct, return + the (possibly invalid) flexible array member to + the (recursive) caller to determine whether it + is followed by any other data members in the + containing struct. */ + if (DECL_CONTEXT (fld) != t) + return f; + + msg = G_("flexible array member %qD in an otherwise empty %q#T"); + } + + if (msg) + { + error_at (DECL_SOURCE_LOCATION (f), msg, f, t); + inform (input_location, "%q#T declared here", + DECL_FIELD_CONTEXT (f)); + } + } + } + + return NULL_TREE; +} + /* Perform processing required when the definition of T (a class type) - is complete. */ + is complete. Diagnose invalid definitions of flexible array members + and zero-size arrays. */ void finish_struct_1 (tree t) { + { + bool dummy = false; + check_flexarrays (t, TYPE_FIELDS (t), &dummy); + } + tree x; /* A TREE_LIST. The TREE_VALUE of each node is a FUNCTION_DECL. */ tree virtuals = NULL_TREE; diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c index 675342e..79c6299 100644 --- a/gcc/cp/decl.c +++ b/gcc/cp/decl.c @@ -8610,8 +8610,9 @@ fold_sizeof_expr (tree t) } /* Given the SIZE (i.e., number of elements) in an array, compute an - appropriate index type for the array. If non-NULL, NAME is the - name of the thing being declared. */ + appropriate index type for the array. When SIZE is null, the array + is a flexible array member. If non-NULL, NAME is the name of + the entity being declared. */ tree compute_array_index_type (tree name, tree size, tsubst_flags_t complain) @@ -8619,6 +8620,9 @@ compute_array_index_type (tree name, tree size, tsubst_flags_t complain) tree itype; tree osize = size; + if (NULL_TREE == size) + return build_index_type (NULL_TREE); + if (error_operand_p (size)) return error_mark_node; @@ -8739,11 +8743,11 @@ compute_array_index_type (tree name, tree size, tsubst_flags_t complain) return error_mark_node; else if (in_system_header_at (input_location)) /* Allow them in system headers because glibc uses them. */; - else if (name) - pedwarn (input_location, OPT_Wpedantic, "ISO C++ forbids zero-size array %qD", name); else - pedwarn (input_location, OPT_Wpedantic, "ISO C++ forbids zero-size array"); + pedwarn (input_location, OPT_Wpedantic, + "ISO C++ forbids zero-size arrays"); } + } else if (TREE_CONSTANT (size) /* We don't allow VLAs at non-function scopes, or during @@ -10888,7 +10892,7 @@ grokdeclarator (const cp_declarator *declarator, } { - tree decl; + tree decl = NULL_TREE; if (decl_context == PARM) { @@ -10912,11 +10916,19 @@ grokdeclarator (const cp_declarator *declarator, if (!staticp && TREE_CODE (type) == ARRAY_TYPE && TYPE_DOMAIN (type) == NULL_TREE) { - tree itype = compute_array_index_type (dname, integer_zero_node, + if (TREE_CODE (ctype) == UNION_TYPE + || TREE_CODE (ctype) == QUAL_UNION_TYPE) + { + error ("flexible array member in union"); + type = error_mark_node; + } + else + { + tree itype = compute_array_index_type (dname, NULL_TREE, tf_warning_or_error); type = build_cplus_array_type (TREE_TYPE (type), itype); } - + } if (type == error_mark_node) { /* Happens when declaring arrays of sizes which @@ -11082,6 +11094,10 @@ grokdeclarator (const cp_declarator *declarator, || !COMPLETE_TYPE_P (TREE_TYPE (type)) || initialized == 0)) { + if (TREE_CODE (type) != ARRAY_TYPE + || !COMPLETE_TYPE_P (TREE_TYPE (type))) + { + if (unqualified_id) { error ("field %qD has incomplete type %qT", @@ -11094,6 +11110,7 @@ grokdeclarator (const cp_declarator *declarator, type = error_mark_node; decl = NULL_TREE; } + } else { if (friendp) diff --git a/gcc/cp/error.c b/gcc/cp/error.c index 38548c7..56ce14b 100644 --- a/gcc/cp/error.c +++ b/gcc/cp/error.c @@ -875,7 +875,7 @@ dump_type_suffix (cxx_pretty_printer *pp, tree t, int flags) case ARRAY_TYPE: pp_maybe_space (pp); pp_cxx_left_bracket (pp); - if (TYPE_DOMAIN (t)) + if (TYPE_DOMAIN (t) && TYPE_MAX_VALUE (TYPE_DOMAIN (t))) { tree dtype = TYPE_DOMAIN (t); tree max = TYPE_MAX_VALUE (dtype); diff --git a/gcc/cp/init.c b/gcc/cp/init.c index fccd289..6b93504 100644 --- a/gcc/cp/init.c +++ b/gcc/cp/init.c @@ -729,10 +729,15 @@ perform_member_init (tree member, tree init) || same_type_ignoring_top_level_qualifiers_p (type, TREE_TYPE (init))) { + if (TYPE_DOMAIN (type) && TYPE_MAX_VALUE (TYPE_DOMAIN (type))) + { + /* Initialize the array only if it's not a flexible + array member (i.e., if it has an upper bound). */ init = build_vec_init_expr (type, init, tf_warning_or_error); init = build2 (INIT_EXPR, type, decl, init); finish_expr_stmt (init); } + } else error ("invalid initializer for array member %q#D", member); } diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c index 2904657..2bfaaee 100644 --- a/gcc/cp/pt.c +++ b/gcc/cp/pt.c @@ -10005,7 +10005,16 @@ instantiate_class_template_1 (tree type) if (can_complete_type_without_circularity (rtype)) complete_type (rtype); - if (!COMPLETE_TYPE_P (rtype)) + if (TREE_CODE (r) == FIELD_DECL + && TREE_CODE (rtype) == ARRAY_TYPE + && COMPLETE_TYPE_P (TREE_TYPE (rtype)) + && !COMPLETE_TYPE_P (rtype)) + { + /* Flexible array mmembers of elements + of complete type have an incomplete type + and that's okay. */ + } + else if (!COMPLETE_TYPE_P (rtype)) { cxx_incomplete_type_error (r, rtype); TREE_TYPE (r) = error_mark_node; @@ -12730,9 +12739,14 @@ tsubst (tree t, tree args, tsubst_flags_t complain, tree in_decl) if (t == integer_type_node) return t; - if (TREE_CODE (TYPE_MIN_VALUE (t)) == INTEGER_CST - && TREE_CODE (TYPE_MAX_VALUE (t)) == INTEGER_CST) + if (TREE_CODE (TYPE_MIN_VALUE (t)) == INTEGER_CST) + { + if (!TYPE_MAX_VALUE (t)) + return compute_array_index_type (NULL_TREE, NULL_TREE, complain); + + if (TREE_CODE (TYPE_MAX_VALUE (t)) == INTEGER_CST) return t; + } { tree max, omax = TREE_OPERAND (TYPE_MAX_VALUE (t), 0); diff --git a/gcc/cp/typeck2.c b/gcc/cp/typeck2.c index 839091c..68d2e71 100644 --- a/gcc/cp/typeck2.c +++ b/gcc/cp/typeck2.c @@ -1013,6 +1013,14 @@ digest_init_r (tree type, tree init, bool nested, int flags, them if they were present. */ if (code == ARRAY_TYPE) { + if (nested + && (!TYPE_DOMAIN (type) || !TYPE_MAX_VALUE (TYPE_DOMAIN (type)))) + { + /* Flexible array members do not have an upper bound. */ + pedwarn (EXPR_LOC_OR_LOC (init, input_location), OPT_Wpedantic, + "initialization of a flexible array member"); + } + tree typ1 = TYPE_MAIN_VARIANT (TREE_TYPE (type)); if (char_type_p (typ1) /*&& init */ @@ -1051,8 +1059,11 @@ digest_init_r (tree type, tree init, bool nested, int flags, init = copy_node (init); TREE_TYPE (init) = type; } - if (TYPE_DOMAIN (type) != 0 && TREE_CONSTANT (TYPE_SIZE (type))) + if (TYPE_DOMAIN (type) + && TYPE_MAX_VALUE (TYPE_DOMAIN (type)) + && TREE_CONSTANT (TYPE_SIZE (type))) { + /* Not a flexible array member. */ int size = TREE_INT_CST_LOW (TYPE_SIZE (type)); size = (size + BITS_PER_UNIT - 1) / BITS_PER_UNIT; /* In C it is ok to subtract 1 from the length of the string @@ -1240,8 +1251,10 @@ process_init_constructor_array (tree type, tree init, if (TREE_CODE (type) == ARRAY_TYPE) { tree domain = TYPE_DOMAIN (type); - if (domain && TREE_CONSTANT (TYPE_MAX_VALUE (domain))) - len = wi::ext (wi::to_offset (TYPE_MAX_VALUE (domain)) + /* Flexible array members have no upper bound. */ + tree maxval = domain ? TYPE_MAX_VALUE (domain) : NULL_TREE; + if (domain && maxval && TREE_CONSTANT (maxval)) + len = wi::ext (wi::to_offset (maxval) - wi::to_offset (TYPE_MIN_VALUE (domain)) + 1, TYPE_PRECISION (TREE_TYPE (domain)), TYPE_SIGN (TREE_TYPE (domain))).to_uhwi (); @@ -1417,14 +1430,15 @@ process_init_constructor_record (tree type, tree init, } else { - if (TREE_CODE (TREE_TYPE (field)) == REFERENCE_TYPE) + const_tree fldtype = TREE_TYPE (field); + if (TREE_CODE (fldtype) == REFERENCE_TYPE) { if (complain & tf_error) error ("member %qD is uninitialized reference", field); else return PICFLAG_ERRONEOUS; } - else if (CLASSTYPE_REF_FIELDS_NEED_INIT (TREE_TYPE (field))) + else if (CLASSTYPE_REF_FIELDS_NEED_INIT (fldtype)) { if (complain & tf_error) error ("member %qD with uninitialized reference fields", field); @@ -1433,13 +1447,17 @@ process_init_constructor_record (tree type, tree init, } /* Warn when some struct elements are implicitly initialized - to zero. */ - if ((complain & tf_warning) + to zero. However, avoid issuing the warning for flexible + array members since they need not have any elements. */ + if ((TREE_CODE (fldtype) != ARRAY_TYPE + || (TYPE_DOMAIN (fldtype) + && TYPE_MAX_VALUE (TYPE_DOMAIN (fldtype)))) + && (complain & tf_warning) && !EMPTY_CONSTRUCTOR_P (init)) warning (OPT_Wmissing_field_initializers, "missing initializer for member %qD", field); - if (!zero_init_p (TREE_TYPE (field)) + if (!zero_init_p (fldtype) || skipped < 0) next = build_zero_init (TREE_TYPE (field), /*nelts=*/NULL_TREE, /*static_storage_p=*/false); diff --git a/gcc/testsuite/g++.dg/cpp0x/bad_array_new2.C b/gcc/testsuite/g++.dg/cpp0x/bad_array_new2.C index bcc9b9a..d56410b 100644 --- a/gcc/testsuite/g++.dg/cpp0x/bad_array_new2.C +++ b/gcc/testsuite/g++.dg/cpp0x/bad_array_new2.C @@ -1,7 +1,16 @@ // Test for throwing bad_array_new_length on invalid array length // { dg-do run { target c++11 } } -#include <new> +// #include <new> + +namespace std { +struct exception { + virtual ~exception () { } +}; + +struct bad_alloc: exception { }; +struct bad_array_new_length { }; +} void * f(int i) { diff --git a/gcc/testsuite/g++.dg/ext/flexary2.C b/gcc/testsuite/g++.dg/ext/flexary2.C index 4855b3f..dcd1bde 100644 --- a/gcc/testsuite/g++.dg/ext/flexary2.C +++ b/gcc/testsuite/g++.dg/ext/flexary2.C @@ -1,4 +1,10 @@ -// PR c++/46688 +// PR c++/46688 - [4.6 Regression] g++ requires a function declaration +// when it should not +// Note that although the definition of struct B in the test case for +// c++/46688 was thought to be valid, it is, in fact, invalid, in C and +// as noted in c++/42121, should be treated as invalid in C++ as well. +// The test verifies that gcc detects and reports the right error. + // { dg-options "" } struct A { @@ -7,5 +13,10 @@ struct A { struct B { B() {} - A a[]; + A a[]; // { dg-error "extension|flexible array .* in an otherwise empty" } +}; + +struct C { + C() {} + A a[0]; // -Wpedantic warning: ISO C++ forbids zero-size arrays }; diff --git a/gcc/testsuite/g++.dg/ext/flexary3.C b/gcc/testsuite/g++.dg/ext/flexary3.C index 906877b..c7c0e79 100644 --- a/gcc/testsuite/g++.dg/ext/flexary3.C +++ b/gcc/testsuite/g++.dg/ext/flexary3.C @@ -1,7 +1,18 @@ -// PR c++/54441 +// PR c++/54441 - [4.7/4.8 Regression] Infinite loop with brace initializer +// on zero-length array +// Note that although the definition of struct s in the test case for +// c++/54441 was accepted as valid, it is, in fact, invalid in C, and +// as noted in c++/42121, should be treated as invalid in C++ as well. +// The test verifies that gcc detects, reports, and handles both errors +// gracefully. +// Note also that the error(s) issued for the invalid initializer depend +// on c++/55606. + // { dg-options "" } -struct s { char c[]; }; +struct s { + char c[]; // { dg-error "flexible array member .* in an otherwise empty" } +}; int main() { diff --git a/gcc/testsuite/g++.dg/ext/flexary4.C b/gcc/testsuite/g++.dg/ext/flexary4.C new file mode 100644 index 0000000..fefce47 --- /dev/null +++ b/gcc/testsuite/g++.dg/ext/flexary4.C @@ -0,0 +1,328 @@ +// PR c++/42121 - g++ should warn or error on internal 0 size array in struct +// { dg-do compile } +// { dg-options "-Wno-error=pedantic" } + +// Flexible array members are a feature of C99 (and newer) not provided +// by C++ 2014 and prior. G++ supports both the C99/C11 kind of flexible +// array members and pre-C99 zero-size arrays (defining an array of size +// zero). Since both features are provided for compatibility with C, +// G++ allows them in the same contexts as in C. + +struct Sx { + int a[]; // { dg-error "flexible array member" } +}; + +// Verify that non-data members or static data members don't suppress +// the diagnostic. +struct Sx2 { + int a[]; // { dg-error "flexible array member" } + typedef int I; +}; + +struct Sx3 { + typedef int I; + int a[]; // { dg-error "flexible array member" } +}; + +struct Sx4 { + int a[]; // { dg-error "flexible array member" } + enum E { e }; +}; + +struct Sx5 { + enum E { e }; + int a[]; // { dg-error "flexible array member" } +}; + +struct Sx6 { + int a[]; // { dg-error "flexible array member" } + static int i; +}; + +int Sx6::i; + +struct Sx7 { + static int i; + int a[]; // { dg-error "flexible array member" } +}; + +int Sx7::i; + +struct Sx8 { + int a[]; // { dg-error "flexible array member" } + Sx8 () { } +}; + +struct Sx9 { + Sx9 () { } + int a[]; // { dg-error "flexible array member" } +}; + +struct Sx10 { + int a[]; // { dg-error "flexible array member" } + virtual ~Sx10 () { } +}; + +struct Sx11 { + virtual ~Sx11 () { } + int a[]; // { dg-error "flexible array member" } +}; + +struct Sx12 { + int a[]; // { dg-error "flexible array member" } + virtual void foo () = 0; +}; + +struct Sx13 { + virtual void foo () = 0; + int a[]; // { dg-error "flexible array member" } +}; + +struct Sx14 { + int a[][1]; // { dg-error "flexible array member" } +}; + +struct Sx15 { + typedef int A[]; + A a; // { dg-error "flexible array member" } +}; + +// Verify also that a zero-size array doesn't suppress the diagnostic. +struct Sx16 { + // a_0 below is diagnosed with -Wpedantic only and emits + // warning: ISO C++ forbids zero-size arrays + int a_0 [0]; + int a_x []; // { dg-error "flexible array member" } +}; + +struct Sx17 { + int a_x []; // { dg-error "flexible array member" } + + // a_0 below is diagnosed with -Wpedantic only and emits + // warning: ISO C++ forbids zero-size arrays + int a_0 [0]; +}; + +struct Sx18 { + int a_x []; // { dg-error "flexible array member" } + struct S { }; +}; + +struct Sx19 { + struct S { }; + int a_x []; // { dg-error "flexible array member" } +}; + +struct Sx20 { + struct S { } s; + int a_x []; +}; + +struct Sx21 { + int a_x []; // { dg-error "not at end" } + struct S { } s; +}; + +struct Sx22 { + int a_x []; // { dg-error "not at end" } + union { int i; }; +}; + +struct Sx23 { + union { int i; }; + int a_x []; +}; + +struct Sx24 { + struct S; + S a_x []; // { dg-error "incomplete type" } +}; + +struct Sx25 { + struct S { }; + S a_x []; // { dg-error "flexible array member" } +}; + +struct Sx26 { + struct { } + a_x []; // { dg-error "flexible array member" } +}; + +struct Sx27 { + int i; + struct { } + a_x []; +}; + +struct Sx28 { + struct { } + a_x []; // { dg-error "not at end" } + int i; +}; + +struct Sx29 { + // Pointer to an array of unknown size. + int (*a_x)[]; +}; + +struct Sx30 { + // Reference to an array of unknown size. + int (&a_x)[]; +}; + +struct Sx31 { + int a []; // { dg-error "not at end" } + unsigned i: 1; +}; + +struct Sx32 { + unsigned i: 1; + int a []; +}; + +struct S_S_S_x { + struct A { + struct B { + int a[]; // { dg-error "flexible array member" } + } b; + } a; +}; + +// Since members of an anonymous struct or union are considered to be +// members of the enclosing union the below defintions are valid and +// must be accepted. + +struct Anon1 { + int n; + struct { + int good[]; + }; +}; + +struct Anon2 { + struct { + int n; + struct { + int good[]; + }; + }; +}; + +struct Anon3 { + struct { + struct { + int n; + int good[]; + }; + }; +}; + +struct Anon4 { + struct { + int in_empty_struct[];// { dg-error "in an otherwise empty" } + }; +}; + +struct Anon5 { + struct { + int not_at_end[]; // { dg-error "not at end" } + }; + int n; +}; + +struct Anon6 { + struct { + struct { + int not_at_end[]; // { dg-error "not at end" } + }; + int n; + }; +}; + + +struct Anon7 { + struct { + struct { + int not_at_end[]; // { dg-error "not at end" } + }; + }; + int n; +}; + + +struct Six { + int i; + int a[]; +}; + +class Cx { + int a[]; // { dg-error "flexible array member" } +}; + +class Cix { + int i; + int a[]; +}; + +struct Sxi { + int a[]; // { dg-error "not at end" } + int i; +}; + +struct S0 { + int a[0]; +}; + +struct S0i { + int a[0]; + int i; +}; + +struct S_a0_ax { + int a1[0]; + int ax[]; // { dg-error "flexible array member" } +}; + +struct S_a0_i_ax { + int a1[0]; + int i; + int ax[]; +}; + +struct Si_a0_ax { + int i; + int a1[0]; + int ax[]; +}; + +struct S_u_ax { + struct { + // An empty struct is in C++ treated as if it had a single + // member of type char. Arguably, though, since neither + // the struct nor the member has a name, the flexible array + // member should be diagnosed because its size cannot easily + // be stored in the contaning object. This seems like too + // much of a corner case to worry about. + }; + int ax[]; // no warning because of the above +}; + +struct S_u0_ax { + union { } u[0]; + int ax[]; // { dg-error "flexible array member" } +}; + +struct S_a1_s2 { + int a[1]; + int b[2]; +}; + +union U_i_ax { + int i; + int a[]; // { dg-error "flexible array member in union" } +}; + +union U_i_a0 { + int i; + int a[0]; +}; diff --git a/gcc/testsuite/g++.dg/ext/flexary5.C b/gcc/testsuite/g++.dg/ext/flexary5.C new file mode 100644 index 0000000..91c9a4c --- /dev/null +++ b/gcc/testsuite/g++.dg/ext/flexary5.C @@ -0,0 +1,52 @@ +// { dg-do compile } +// { dg-options "-Wno-error=pedantic" } + +// Flexible array members are a feature of C99 (and newer) not provided +// by C++ 2014 and prior. G++ supports both the C99/C11 kind of flexible +// array members and pre-C99 zero-size arrays (defining an array of size +// zero). Since both features are provided for compatibility with C, +// G++ allows them in the same contexts as in C. This test verifies +// that invalid flexible array members are diagnosed in templates that +// are instantiated and not otherwise. + +template <class T> +struct S_no_diag: T { + char a[]; // cannot be diagnosed unless/until T is known +}; + +template <class T> +struct STx_1: T { + char a[]; // { dg-error "flexible array member" } +}; + +template <class T, int I> +struct STI: T { + char a[I]; // cannot be diagnosed unless/until T and I are known +}; + +template <class T, int I> +struct STIx: T { + char a[I]; +}; + +template <int> +struct Empty { }; + +STx_1<Empty<0> > stx_empty_1; +STIx<Empty<0>, 0> stix_empty_1; + +struct EmptyBase1: Empty<0>, Empty<1> { }; +struct EmptyBase2: Empty<2>, Empty<3> { }; +struct EmptyDerived: EmptyBase1, EmptyBase2 +{ + char a[]; // { dg-error "flexible array member" } +}; + +struct NonEmpty { int i; }; +struct NonEmptyBase: NonEmpty { }; +struct NonEmptyDerived: NonEmptyBase { }; + +struct FinalDerived: EmptyBase1, EmptyBase2, NonEmpty, Empty<4> +{ + char a[]; // okay +}; diff --git a/gcc/testsuite/g++.dg/ext/flexary6.C b/gcc/testsuite/g++.dg/ext/flexary6.C new file mode 100644 index 0000000..b3fc456 --- /dev/null +++ b/gcc/testsuite/g++.dg/ext/flexary6.C @@ -0,0 +1,25 @@ +// PR c++/68478 - flexible array members have complete type +// { dg-do compile } +// { dg-options "-Wno-error=pedantic" } + +// Test to verify that attempting to use a flexible array member where +// a complete type is required is rejected. + +struct A { + int n; + int a[]; + enum { + e = sizeof a // { dg-error "invalid application of .sizeof. to incomplete type" } + }; +}; + +struct B { + int n; + typedef int A[]; + A a; + enum { + // Verify that attempting to use a flexible array member where + // a complete type is required is rejected. + e = sizeof a // { dg-error "invalid application of .sizeof. to incomplete type" } + }; +}; diff --git a/gcc/testsuite/g++.dg/ext/flexary7.C b/gcc/testsuite/g++.dg/ext/flexary7.C new file mode 100644 index 0000000..fdea4d4 --- /dev/null +++ b/gcc/testsuite/g++.dg/ext/flexary7.C @@ -0,0 +1,57 @@ +// PR c++/68613 - initializer-string for array of chars is too long error +// on flexible array member +// { dg-do compile } +// { dg-options "-Wpedantic -Wno-error=pedantic" } + +struct FlexChar { + int n; + char a[]; +}; + +struct FlexChar ac = + { 4, { "abc" } }; // { dg-warning "initialization of a flexible array member" } + + +#if !__cplusplus +typedef __WCHAR_TYPE__ wchar_t; +#endif + +struct FlexWchar { + int n; + wchar_t a[]; +}; + +struct FlexWchar awc = + { 3, { L"ab" } }; // { dg-warning "initialization of a flexible array member" } + + +struct FlexInt { + int n; + int a[]; +}; + +// Verify that no warning is issued for the case when a flexible array +// member is not initialized (i.e., that a -Wmissing-field-initializer +// isn't issued) because such arrays need not have any elements. +struct FlexInt ai0 = + { 0 }; + +struct FlexInt ai0_ = + { 0, { } }; // { dg-warning "initialization of a flexible array member" } + +struct FlexInt ai2 = + { 2, { 1, 2 } }; // { dg-warning "initialization of a flexible array member" } + + +#if __cplusplus + +template <class T> +struct FlexT { + int n; + T a[]; +}; + +struct FlexT<char> atc = + { 4, { "abc" } }; // { dg-warning "initialization of a flexible array member" } + +#endif diff --git a/gcc/testsuite/g++.dg/other/dump-ada-spec-2.C b/gcc/testsuite/g++.dg/other/dump-ada-spec-2.C index 87c183a..d1af7e0 100644 --- a/gcc/testsuite/g++.dg/other/dump-ada-spec-2.C +++ b/gcc/testsuite/g++.dg/other/dump-ada-spec-2.C @@ -7,5 +7,5 @@ struct S __extension__ unsigned char data[]; }; -/* { dg-final { scan-ada-spec "array \\(0 .. -1\\)" } } */ +/* { dg-final { scan-ada-spec "array \\(0 .. 0\\)" } } */ /* { dg-final { cleanup-ada-spec } } */ diff --git a/gcc/testsuite/g++.dg/parse/pr43765.C b/gcc/testsuite/g++.dg/parse/pr43765.C index 0b341dd..800f2c7 100644 --- a/gcc/testsuite/g++.dg/parse/pr43765.C +++ b/gcc/testsuite/g++.dg/parse/pr43765.C @@ -3,12 +3,15 @@ struct SomeType { + int n; const char *values[]; }; const char *temp[] = {"607", "612", 0}; SomeType vals[] = { - { values : temp, }, + { 0, values : temp, }, 0 - }; // { dg-error "invalid" } + }; // { dg-error "GNU-style designated initializer for an array|cannot convert" } +// (note the error above is on the wrong line) + diff --git a/gcc/testsuite/g++.dg/torture/pr64280.C b/gcc/testsuite/g++.dg/torture/pr64280.C index 6ea3148..e756e02 100644 --- a/gcc/testsuite/g++.dg/torture/pr64280.C +++ b/gcc/testsuite/g++.dg/torture/pr64280.C @@ -15,7 +15,7 @@ public: typedef int jmp_buf[]; struct C { - jmp_buf cond_; + jmp_buf cond_; // { dg-error "flexible array member" } }; class F { diff --git a/gcc/testsuite/g++.dg/torture/pr64312.C b/gcc/testsuite/g++.dg/torture/pr64312.C index dc3e95d..85211f2 100644 --- a/gcc/testsuite/g++.dg/torture/pr64312.C +++ b/gcc/testsuite/g++.dg/torture/pr64312.C @@ -43,6 +43,7 @@ protected: class F { public: + int nelems; int elems[]; int * m_fn1 () diff --git a/gcc/testsuite/g++.dg/ubsan/object-size-1.C b/gcc/testsuite/g++.dg/ubsan/object-size-1.C index e2aad46..e6cdefc 100644 --- a/gcc/testsuite/g++.dg/ubsan/object-size-1.C +++ b/gcc/testsuite/g++.dg/ubsan/object-size-1.C @@ -1,9 +1,9 @@ // { dg-do compile } -// { dg-options "-fsanitize=undefined -fpermissive" } +// { dg-options "-Wpedantic -Wno-error=pedantic -fsanitize=undefined -fpermissive" } struct T { int c; char d[]; }; -struct T t = { 1, "a" }; // { dg-warning "initializer-string for array of chars is too long" } +struct T t = { 1, "a" }; // { dg-warning "initialization of a flexible array member " } int baz (int i) diff --git a/gcc/tree-chkp.c b/gcc/tree-chkp.c index 34d9dfc..6a6b07b 100644 --- a/gcc/tree-chkp.c +++ b/gcc/tree-chkp.c @@ -1664,8 +1664,10 @@ chkp_find_bound_slots_1 (const_tree type, bitmap have_bound, offs + field_offs); } } - else if (TREE_CODE (type) == ARRAY_TYPE) + else if (TREE_CODE (type) == ARRAY_TYPE && TYPE_DOMAIN (type)) { + /* The object type is an array of complete type, i.e., other + than a flexible array. */ tree maxval = TYPE_MAX_VALUE (TYPE_DOMAIN (type)); tree etype = TREE_TYPE (type); HOST_WIDE_INT esize = TREE_INT_CST_LOW (TYPE_SIZE (etype)); diff --git a/gcc/tree.c b/gcc/tree.c index 1d770c3..2ba786f 100644 --- a/gcc/tree.c +++ b/gcc/tree.c @@ -3588,9 +3588,10 @@ type_contains_placeholder_1 (const_tree type) || CONTAINS_PLACEHOLDER_P (TYPE_MAX_VALUE (type))); case ARRAY_TYPE: - /* We have already checked the component type above, so just check the - domain type. */ - return type_contains_placeholder_p (TYPE_DOMAIN (type)); + /* We have already checked the component type above, so just check + the domain type. Flexible array members have a null domain. */ + return TYPE_DOMAIN (type) ? + type_contains_placeholder_p (TYPE_DOMAIN (type)) : false; case RECORD_TYPE: case UNION_TYPE: diff --git a/gcc/varasm.c b/gcc/varasm.c index ec6aabf..606de4d 100644 --- a/gcc/varasm.c +++ b/gcc/varasm.c @@ -4821,8 +4821,7 @@ array_size_for_constructor (tree val) { tree max_index; unsigned HOST_WIDE_INT cnt; - tree index, value, tmp; - offset_int i; + tree index, value; /* This code used to attempt to handle string constants that are not arrays of single-bytes, but nothing else does, so there's no point in @@ -4842,9 +4841,14 @@ array_size_for_constructor (tree val) if (max_index == NULL_TREE) return 0; + offset_int i = wi::to_offset (max_index) + 1; + + if (TYPE_DOMAIN (TREE_TYPE (val))) + { /* Compute the total number of array elements. */ - tmp = TYPE_MIN_VALUE (TYPE_DOMAIN (TREE_TYPE (val))); - i = wi::to_offset (max_index) - wi::to_offset (tmp) + 1; + tree tmp = TYPE_MIN_VALUE (TYPE_DOMAIN (TREE_TYPE (val))); + i -= wi::to_offset (tmp); + } /* Multiply by the array element unit size to find number of bytes. */ i *= wi::to_offset (TYPE_SIZE_UNIT (TREE_TYPE (TREE_TYPE (val)))); @@ -4974,13 +4978,15 @@ output_constructor_regular_field (oc_local_state *local) but we cannot do this until the deprecated support for initializing zero-length array members is removed. */ if (TREE_CODE (TREE_TYPE (local->field)) == ARRAY_TYPE - && TYPE_DOMAIN (TREE_TYPE (local->field)) - && ! TYPE_MAX_VALUE (TYPE_DOMAIN (TREE_TYPE (local->field)))) + && (!TYPE_DOMAIN (TREE_TYPE (local->field)) + || !TYPE_MAX_VALUE (TYPE_DOMAIN (TREE_TYPE (local->field))))) { fieldsize = array_size_for_constructor (local->val); - /* Given a non-empty initialization, this field had - better be last. */ - gcc_assert (!fieldsize || !DECL_CHAIN (local->field)); + /* Given a non-empty initialization, this field had better + be last. Given a flexible array member, the next field + on the chain is a TYPE_DECL of the enclosing struct. */ + const_tree next = DECL_CHAIN (local->field); + gcc_assert (!fieldsize || !next || TREE_CODE (next) != FIELD_DECL); } else fieldsize = tree_to_uhwi (DECL_SIZE_UNIT (local->field)); @@ -5196,7 +5202,7 @@ output_constructor (tree exp, unsigned HOST_WIDE_INT size, unsigned int align, if (TREE_CODE (local.type) == ARRAY_TYPE && TYPE_DOMAIN (local.type)) local.min_index = TYPE_MIN_VALUE (TYPE_DOMAIN (local.type)); else - local.min_index = NULL_TREE; + local.min_index = integer_zero_node; local.total_bytes = 0; local.byte_buffer_in_use = outer != NULL;