[2/5] - C front end support to detect out-of-bounds accesses to array parameters. gcc/c-family/ChangeLog: PR c/50584 * c-common.h (warn_parm_array_mismatch): Declare new function. (has_attribute): Move declaration of an existing function. (build_attr_access_from_parms): Declare new function. * c-warn.c (parm_array_as_string): Define new function. (plus_one): Define new function. (warn_parm_ptrarray_mismatch): Define new function. (warn_parm_array_mismatch): Define new function. (vla_bound_parm_decl): New function. * c.opt (-Warray-parameter, -Wvla-parameter): New options. * c-pretty-print.c (pp_c_type_qualifier_list): Don't print array type qualifiers here... (c_pretty_printer::direct_abstract_declarator): ...but instead print them in brackets here. Also print [static]. Strip extraneous expressions from VLA bounds. gcc/c/ChangeLog: PR c/50584 * c-decl.c (lookup_last_decl): Define new function. (c_decl_attributes): Call it. (start_decl): Add argument and use it. (finish_decl): Call build_attr_access_from_parms and decl_attributes. (get_parm_array_spec): Define new function. (push_parm_decl): Call get_parm_array_spec. (start_function): Call warn_parm_array_mismatch. Build attribute access and add it to current function. * c-parser.c (c_parser_declaration_or_fndef): Diagnose mismatches in forms of array parameters. * c-tree.h (start_decl): Add argument. gcc/ChangeLog: PR c/50584 * calls.c (initialize_argument_information): Remove assertion. * doc/invoke.texi (-Warray-parameter, -Wvla-parameter): Document. * tree-pretty-print.c (dump_generic_node): Correct handling of qualifiers. gcc/testsuite/ChangeLog: PR c/50584 * c-c++-common/Warray-bounds-6.c: Correct C++ declaration, adjust text of expected diagnostics. * gcc.dg/Wbuiltin-declaration-mismatch-9.c: Prune expected warning. * gcc.dg/Warray-parameter-2.c: New test. * gcc.dg/Warray-parameter-3.c: New test. * gcc.dg/Warray-parameter-4.c: New test. * gcc.dg/Warray-parameter-5.c: New test. * gcc.dg/Warray-parameter.c: New test. * gcc.dg/Wvla-parameter-2.c: New test. * gcc.dg/Wvla-parameter-3.c: New test. * gcc.dg/Wvla-parameter-4.c: New test. * gcc.dg/Wvla-parameter.c: New test. diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h index 4fc64bc4aa6..a61b25f95f1 100644 --- a/gcc/c-family/c-common.h +++ b/gcc/c-family/c-common.h @@ -1321,6 +1321,7 @@ extern void c_do_switch_warnings (splay_tree, location_t, tree, tree, bool); extern void warn_for_omitted_condop (location_t, tree); extern bool warn_for_restrict (unsigned, tree *, unsigned); extern void warn_for_address_or_pointer_of_packed_member (tree, tree); +extern void warn_parm_array_mismatch (location_t, tree, tree); /* Places where an lvalue, or modifiable lvalue, may be required. Used to select diagnostic messages in lvalue_error and @@ -1375,6 +1376,8 @@ extern tree find_tm_attribute (tree); extern const struct attribute_spec::exclusions attr_cold_hot_exclusions[]; extern const struct attribute_spec::exclusions attr_noreturn_exclusions[]; extern tree handle_noreturn_attribute (tree *, tree, tree, int, bool *); +extern bool has_attribute (location_t, tree, tree, tree (*)(tree)); +extern tree build_attr_access_from_parms (tree, bool); /* In c-format.c. */ extern bool valid_format_string_type_p (tree); @@ -1403,8 +1406,6 @@ extern void maybe_suggest_missing_token_insertion (rich_location *richloc, location_t prev_token_loc); extern tree braced_lists_to_strings (tree, tree); -extern bool has_attribute (location_t, tree, tree, tree (*)(tree)); - #if CHECKING_P namespace selftest { /* Declarations for specific families of tests within c-family, diff --git a/gcc/c-family/c-pretty-print.c b/gcc/c-family/c-pretty-print.c index ec0bafe1010..03c89385386 100644 --- a/gcc/c-family/c-pretty-print.c +++ b/gcc/c-family/c-pretty-print.c @@ -248,9 +248,12 @@ pp_c_type_qualifier_list (c_pretty_printer *pp, tree t) if (!TYPE_P (t)) t = TREE_TYPE (t); - qualifiers = TYPE_QUALS (t); - pp_c_cv_qualifiers (pp, qualifiers, - TREE_CODE (t) == FUNCTION_TYPE); + if (TREE_CODE (t) != ARRAY_TYPE) + { + qualifiers = TYPE_QUALS (t); + pp_c_cv_qualifiers (pp, qualifiers, + TREE_CODE (t) == FUNCTION_TYPE); + } if (!ADDR_SPACE_GENERIC_P (TYPE_ADDR_SPACE (t))) { @@ -572,6 +575,8 @@ c_pretty_printer::abstract_declarator (tree t) void c_pretty_printer::direct_abstract_declarator (tree t) { + bool add_space = false; + switch (TREE_CODE (t)) { case POINTER_TYPE: @@ -585,17 +590,65 @@ c_pretty_printer::direct_abstract_declarator (tree t) case ARRAY_TYPE: pp_c_left_bracket (this); + + if (int quals = TYPE_QUALS (t)) + { + /* Print the array qualifiers such as in "T[const restrict 3]". */ + pp_c_cv_qualifiers (this, quals, false); + add_space = true; + } + + if (tree arr = lookup_attribute ("array", TYPE_ATTRIBUTES (t))) + { + if (TREE_VALUE (arr)) + { + /* Print the specifier as in "T[static 3]" that's not actually + part of the type but may be added by the front end. */ + pp_c_ws_string (this, "static"); + add_space = true; + } + else if (!TYPE_DOMAIN (t)) + /* For arrays of unspecified bound using the [*] notation. */ + pp_character (this, '*'); + } + if (tree dom = TYPE_DOMAIN (t)) { if (tree maxval = TYPE_MAX_VALUE (dom)) { + if (add_space) + pp_space (this); + tree type = TREE_TYPE (maxval); if (tree_fits_shwi_p (maxval)) pp_wide_integer (this, tree_to_shwi (maxval) + 1); - else + else if (TREE_CODE (maxval) == INTEGER_CST) expression (fold_build2 (PLUS_EXPR, type, maxval, build_int_cst (type, 1))); + else + { + /* Strip the expressions from around a VLA bound added + internally to make it fit the domain mold, including + any casts. */ + if (TREE_CODE (maxval) == NOP_EXPR) + maxval = TREE_OPERAND (maxval, 0); + if (TREE_CODE (maxval) == PLUS_EXPR + && integer_all_onesp (TREE_OPERAND (maxval, 1))) + { + maxval = TREE_OPERAND (maxval, 0); + if (TREE_CODE (maxval) == NOP_EXPR) + maxval = TREE_OPERAND (maxval, 0); + } + if (TREE_CODE (maxval) == SAVE_EXPR) + { + maxval = TREE_OPERAND (maxval, 0); + if (TREE_CODE (maxval) == NOP_EXPR) + maxval = TREE_OPERAND (maxval, 0); + } + + expression (maxval); + } } else if (TYPE_SIZE (t)) /* Print zero for zero-length arrays but not for flexible diff --git a/gcc/c-family/c-warn.c b/gcc/c-family/c-warn.c index fea8885bf35..cd19c568168 100644 --- a/gcc/c-family/c-warn.c +++ b/gcc/c-family/c-warn.c @@ -3111,6 +3111,200 @@ plus_one (tree expr) return fold_build2 (PLUS_EXPR, type, expr, build_int_cst (type, 1)); } +/* Try to strip the expressions from around a VLA bound added internally + to make it fit the domain mold, including any casts, and return + the result. The goal is to obtain the PARM_DECL the VLA bound may + refer to. */ + +static tree +vla_bound_parm_decl (tree expr) +{ + if (!expr) + return NULL_TREE; + + if (TREE_CODE (expr) == NOP_EXPR) + expr = TREE_OPERAND (expr, 0); + if (TREE_CODE (expr) == PLUS_EXPR + && integer_all_onesp (TREE_OPERAND (expr, 1))) + { + expr = TREE_OPERAND (expr, 0); + if (TREE_CODE (expr) == NOP_EXPR) + expr = TREE_OPERAND (expr, 0); + } + if (TREE_CODE (expr) == SAVE_EXPR) + { + expr = TREE_OPERAND (expr, 0); + if (TREE_CODE (expr) == NOP_EXPR) + expr = TREE_OPERAND (expr, 0); + } + return expr; +} + +/* Diagnose mismatches in VLA bounds between function parameters NEWPARMS + of pointer types on a redeclaration os a function previously declared + with CURPARMS at ORIGLOC. */ + +static void +warn_parm_ptrarray_mismatch (location_t origloc, tree curparms, tree newparms) +{ + /* Maps each named integral parameter seen so far to its position + in the argument list; used to associate VLA sizes with arguments. */ + hash_map curparm2pos; + hash_map newparm2pos; + + unsigned parmpos = 1; + for (tree curp = curparms, newp = newparms; curp && newp; + curp = TREE_CHAIN (curp), newp = TREE_CHAIN (newp), ++parmpos) + { + tree curtyp = TREE_TYPE (curp), newtyp = TREE_TYPE (newp); + if (INTEGRAL_TYPE_P (curtyp)) + { + /* Only add named parameters; unnamed ones cannot be referred + to in VLA bounds. */ + if (DECL_NAME (curp)) + curparm2pos.put (curp, parmpos); + if (DECL_NAME (newp)) + newparm2pos.put (newp, parmpos); + + continue; + } + + /* The parameter types should match at this point so only test one. */ + if (TREE_CODE (curtyp) != POINTER_TYPE) + continue; + + do + { + curtyp = TREE_TYPE (curtyp); + newtyp = TREE_TYPE (newtyp); + } + while (TREE_CODE (curtyp) == POINTER_TYPE + && TREE_CODE (newtyp) == POINTER_TYPE); + + if (TREE_CODE (curtyp) != ARRAY_TYPE + || TREE_CODE (newtyp) != ARRAY_TYPE) + { + if (curtyp == error_mark_node + || newtyp == error_mark_node) + return; + + continue; + } + + tree curdom = TYPE_DOMAIN (curtyp), newdom = TYPE_DOMAIN (newtyp); + tree curbnd = curdom ? TYPE_MAX_VALUE (curdom) : NULL_TREE; + tree newbnd = newdom ? TYPE_MAX_VALUE (newdom) : NULL_TREE; + + if (DECL_P (curp)) + origloc = DECL_SOURCE_LOCATION (curp); + else if (EXPR_P (curp) && EXPR_HAS_LOCATION (curp)) + origloc = EXPR_LOCATION (curp); + + /* The location of the parameter in the current redeclaration. */ + location_t newloc = DECL_SOURCE_LOCATION (newp); + if (origloc == UNKNOWN_LOCATION) + origloc = newloc; + + /* Issue -Warray-parameter onless one or more mismatches involves + a VLA bound; then issue -Wvla-parameter. */ + int opt = OPT_Warray_parameter_; + /* Traverse the two array types looking for variable bounds and + comparing the two in each pair for mismatches either in their + positions in the function parameter list or lexicographically + for others. Record the 1-based parameter position of each + mismatch in BNDVEC, and the location of each parameter in + the mismatch in WARNLOC (for the new parameter list) and + NOTELOC (for the current parameter list). */ + unsigned bndpos = 1; + auto_vec bndvec; + gcc_rich_location warnloc (newloc); + gcc_rich_location noteloc (origloc); + for ( ; curtyp || newtyp; + ++bndpos, + curbnd = curdom ? TYPE_MAX_VALUE (curdom) : NULL_TREE, + newbnd = newdom ? TYPE_MAX_VALUE (newdom) : NULL_TREE) + { + /* Try to strip each bound down to the PARM_DECL if it does + correspond to one. Either bound can be null if it's + unspecified (i.e., has the [*] form). */ + curbnd = vla_bound_parm_decl (curbnd); + newbnd = vla_bound_parm_decl (newbnd); + + /* Peel the current bound off CURTYP and NEWTYP, skipping + over any subsequent pointer types. */ + if (curtyp && TREE_CODE (curtyp) == ARRAY_TYPE) + { + do + curtyp = TREE_TYPE (curtyp); + while (TREE_CODE (curtyp) == POINTER_TYPE); + if (TREE_CODE (curtyp) == ARRAY_TYPE) + curdom = TYPE_DOMAIN (curtyp); + else + curdom = NULL_TREE; + } + else + curtyp = NULL_TREE; + + if (newtyp && TREE_CODE (newtyp) == ARRAY_TYPE) + { + do + newtyp = TREE_TYPE (newtyp); + while (TREE_CODE (newtyp) == POINTER_TYPE); + if (TREE_CODE (newtyp) == ARRAY_TYPE) + newdom = TYPE_DOMAIN (newtyp); + else + newdom = NULL_TREE; + } + else + newtyp = NULL_TREE; + + /* Move on to the next bound if this one is unspecified. */ + if (!curbnd && !newbnd) + continue; + + /* Try to find each bound in the parameter list. */ + const unsigned* const pcurbndpos = curparm2pos.get (curbnd); + const unsigned* const pnewbndpos = newparm2pos.get (newbnd); + /* Move on if both bounds refer to the same parameter. */ + if (pcurbndpos && pnewbndpos && *pcurbndpos == *pnewbndpos) + continue; + + /* Move on if the bounds look the same. */ + if (!pcurbndpos && !pnewbndpos + && curbnd && newbnd + && operand_equal_p (curbnd, newbnd, OEP_LEXICOGRAPHIC)) + continue; + + if ((curbnd && TREE_CODE (curbnd) != INTEGER_CST) + || (newbnd && TREE_CODE (newbnd) != INTEGER_CST)) + opt = OPT_Wvla_parameter; + + /* Record the mismatch. */ + bndvec.safe_push (bndpos); + /* Underline the bounding parameter in the declaration. */ + if (curbnd && TREE_CODE (curbnd) == PARM_DECL) + noteloc.add_range (DECL_SOURCE_LOCATION (curbnd)); + if (newbnd && TREE_CODE (newbnd) == PARM_DECL) + warnloc.add_range (DECL_SOURCE_LOCATION (newbnd)); + } + + const unsigned nbnds = bndvec.length (); + if (!nbnds) + continue; + + /* Use attr_access to format the parameter types. */ + attr_access spec = { }; + const std::string newparmstr = spec.array_as_string (TREE_TYPE (newp)); + const std::string curparmstr = spec.array_as_string (TREE_TYPE (curp)); + + if (warning_n (&warnloc, opt, nbnds, + "mismatch in bound %Z of argument %u declared as %s", + "mismatch in bounds %Z of argument %u declared as %s", + bndvec.address (), nbnds, parmpos, newparmstr.c_str ())) + inform (¬eloc, "previously declared as %s", curparmstr.c_str ()); + } +} + /* Detect and diagnose a mismatch between an attribute access specification on the original declaration of FNDECL and that on the parameters NEWPARMS from its refeclaration. ORIGLOC is the location of the first declaration @@ -3146,11 +3340,15 @@ warn_parm_array_mismatch (location_t origloc, tree fndecl, tree newparms) rdwr_map new_idx; init_attr_rdwr_indices (&new_idx, newattrs); - /* If both are empty there's nothing to do. If at least one isn't - empty there may be mismatches, such as between f(T*) and f(T[1]), - where the former mapping woud be empty. */ if (cur_idx.is_empty () && new_idx.is_empty ()) - return; + { + /* If both specs are empty check pointers to VLAs for mismatches. */ + warn_parm_ptrarray_mismatch (origloc, curparms, newparms); + return; + } + /* ...otherwise, if at least one spec isn't empty there may be mismatches, + such as between f(T*) and f(T[1]), where the former mapping woud be + empty. */ /* Create an empty access specification and use it for pointers with no spec of their own. */ @@ -3222,13 +3420,13 @@ warn_parm_array_mismatch (location_t origloc, tree fndecl, tree newparms) if (new_vla_p && !cur_vla_p) { if (warning_at (newloc, OPT_Wvla_parameter, - "argument %u of type %qs declared as " + "argument %u of type %s declared as " "a variable length array", parmpos + 1, newparmstr.c_str ())) inform (origloc, (cura == &ptr_spec - ? G_("previously declared as a pointer %qs") - : G_("previously declared as an ordinary array %qs")), + ? G_("previously declared as a pointer %s") + : G_("previously declared as an ordinary array %s")), curparmstr.c_str ()); continue; } @@ -3241,11 +3439,11 @@ warn_parm_array_mismatch (location_t origloc, tree fndecl, tree newparms) { /* Diagnose a pointer/VLA mismatch. */ if (warning_at (newloc, OPT_Wvla_parameter, - "argument %u of type %qs declared " + "argument %u of type %s declared " "as a pointer", parmpos + 1, newparmstr.c_str ())) inform (origloc, - "previously declared as a variable length array %qs", + "previously declared as a variable length array %s", curparmstr.c_str ()); continue; } @@ -3255,10 +3453,10 @@ warn_parm_array_mismatch (location_t origloc, tree fndecl, tree newparms) /* Diagnose mismatches between arrays with a constant bound and pointers. */ if (warning_at (newloc, OPT_Warray_parameter_, - "argument %u of type %qs declared " + "argument %u of type %s declared " "as a pointer", parmpos + 1, newparmstr.c_str ())) - inform (origloc, "previously declared as an array %qs", + inform (origloc, "previously declared as an array %s", curparmstr.c_str ()); continue; } @@ -3267,11 +3465,11 @@ warn_parm_array_mismatch (location_t origloc, tree fndecl, tree newparms) if (!new_vla_p && cur_vla_p) { if (warning_at (newloc, OPT_Wvla_parameter, - "argument %u of type %qs declared " + "argument %u of type %s declared " "as an ordinary array", parmpos + 1, newparmstr.c_str ())) inform (origloc, - "previously declared as a variable length array %qs", + "previously declared as a variable length array %s", curparmstr.c_str ()); continue; } @@ -3295,33 +3493,58 @@ warn_parm_array_mismatch (location_t origloc, tree fndecl, tree newparms) if (newbnds != curbnds) { if (warning_n (newloc, OPT_Wvla_parameter, newbnds, - "argument %u of type %qs declared with " + "argument %u of type %s declared with " "%u variable bound", - "argument %u of type %qs declared with " + "argument %u of type %s declared with " "%u variable bounds", parmpos + 1, newparmstr.c_str (), newbnds)) inform_n (origloc, curbnds, - "previously declared as %qs with %u variable bound", - "previously declared as %qs with %u variable bounds", + "previously declared as %s with %u variable bound", + "previously declared as %s with %u variable bounds", curparmstr.c_str (), curbnds); continue; } if (newunspec != curunspec) { - if (warning_n (newloc, OPT_Wvla_parameter, newunspec, - "argument %u of type %qs declared with " + location_t warnloc = newloc, noteloc = origloc; + const char *warnparmstr = newparmstr.c_str (); + const char *noteparmstr = curparmstr.c_str (); + unsigned warnunspec = newunspec, noteunspec = curunspec; + + if (newunspec < curunspec) + { + /* If the new declaration has fewer unspecified bounds + point the warning to the previous declaration to make + it clear that that's the one to change. Otherwise, + point it to the new decl. */ + std::swap (warnloc, noteloc); + std::swap (warnparmstr, noteparmstr); + std::swap (warnunspec, noteunspec); + } + if (warning_n (warnloc, OPT_Wvla_parameter, warnunspec, + "argument %u of type %s declared with " "%u unspecified variable bound", - "argument %u of type %qs declared with " + "argument %u of type %s declared with " "%u unspecified variable bounds", - parmpos + 1, newparmstr.c_str (), newunspec)) - inform_n (origloc, curunspec, - "previously declared as %qs with %u unspecified " - "variable bound", - "previously declared as %qs with %u unspecified " - "variable bounds", - curparmstr.c_str (), curunspec); + parmpos + 1, warnparmstr, warnunspec)) + { + if (warnloc == newloc) + inform_n (noteloc, noteunspec, + "previously declared as %s with %u unspecified " + "variable bound", + "previously declared as %s with %u unspecified " + "variable bounds", + noteparmstr, noteunspec); + else + inform_n (noteloc, noteunspec, + "subsequently declared as %s with %u unspecified " + "variable bound", + "subsequently declared as %s with %u unspecified " + "variable bounds", + noteparmstr, noteunspec); + } continue; } } @@ -3336,8 +3559,8 @@ warn_parm_array_mismatch (location_t origloc, tree fndecl, tree newparms) tree newpos = TREE_PURPOSE (newvbl); tree curpos = TREE_PURPOSE (curvbl); - tree newbnd = TREE_VALUE (newvbl); - tree curbnd = TREE_VALUE (curvbl); + tree newbnd = vla_bound_parm_decl (TREE_VALUE (newvbl)); + tree curbnd = vla_bound_parm_decl (TREE_VALUE (curvbl)); if (newpos == curpos && newbnd == curbnd) /* In the expected case when both bounds either refer to @@ -3370,14 +3593,14 @@ warn_parm_array_mismatch (location_t origloc, tree fndecl, tree newparms) /* Also underline the VLA bound argument. */ richloc.add_range (DECL_SOURCE_LOCATION (newbnd)); warned = warning_at (&richloc, OPT_Wvla_parameter, - "argument %u of type %qs declared " + "argument %u of type %s declared " "with mismatched bound argument %E", parmpos + 1, newparmstr.c_str (), plus_one (newpos)); } else warned = warning_at (&richloc, OPT_Wvla_parameter, - "argument %u of type %qs declared " + "argument %u of type %s declared " "with mismatched bound %<%s%>", parmpos + 1, newparmstr.c_str (), newbndstr); @@ -3389,12 +3612,12 @@ warn_parm_array_mismatch (location_t origloc, tree fndecl, tree newparms) { /* Also underline the VLA bound argument. */ richloc.add_range (DECL_SOURCE_LOCATION (curbnd)); - inform (&richloc, "previously declared as %qs with " + inform (&richloc, "previously declared as %s with " "bound argument %E", curparmstr.c_str (), plus_one (curpos)); } else - inform (&richloc, "previously declared as %qs with bound " + inform (&richloc, "previously declared as %s with bound " "%<%s%>", curparmstr.c_str (), curbndstr); continue; @@ -3410,11 +3633,10 @@ warn_parm_array_mismatch (location_t origloc, tree fndecl, tree newparms) continue; if (warning_at (newloc, OPT_Wvla_parameter, - "argument %u of type %qs declared with " + "argument %u of type %s declared with " "mismatched bound %<%s%>", - parmpos + 1, newparmstr.c_str (), - newbndstr)) - inform (origloc, "previously declared as %qs with bound %qs", + parmpos + 1, newparmstr.c_str (), newbndstr)) + inform (origloc, "previously declared as %s with bound %qs", curparmstr.c_str (), curbndstr); continue; } @@ -3433,8 +3655,8 @@ warn_parm_array_mismatch (location_t origloc, tree fndecl, tree newparms) continue; if (warning_at (newloc, OPT_Warray_parameter_, - "argument %u of type %qs with mismatched bound", + "argument %u of type %s with mismatched bound", parmpos + 1, newparmstr.c_str ())) - inform (origloc, "previously declared as %qs", curparmstr.c_str ()); + inform (origloc, "previously declared as %s", curparmstr.c_str ()); } } diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt index 2b1aca16eb4..6f3997405a1 100644 --- a/gcc/c-family/c.opt +++ b/gcc/c-family/c.opt @@ -338,6 +338,14 @@ Warray-bounds= LangEnabledBy(C ObjC C++ LTO ObjC++,Wall,1,0) ; in common.opt +Warray-parameter +C ObjC C++ ObjC++ Warning Alias(Warray-parameter=, 2, 0) +Warn about mismatched declarations of array parameters and unsafe accesses to them. + +Warray-parameter= +C ObjC C++ ObjC++ Joined RejectNegative UInteger Var(warn_array_parameter) IntegerRange(0, 2) LangEnabledBy(C ObjC C++ ObjC++,Wall, 2, 0) Warning +Warn about mismatched declarations of array parameters and unsafe accesses to them. + Wzero-length-bounds C ObjC C++ ObjC++ Var(warn_zero_length_bounds) Warning LangEnabledBy(C ObjC C++ ObjC++,Wall) Warn about accesses to interior zero-length array members. @@ -1253,6 +1261,10 @@ Wno-vla-larger-than C ObjC C++ LTO ObjC++ Alias(Wvla-larger-than=,18446744073709551615EiB,none) Warning Disable Wvla-larger-than= warning. Equivalent to Wvla-larger-than= or larger. +Wvla-parameter +C ObjC C++ ObjC++ Var(warn_vla_parameter) Warning LangEnabledBy(C ObjC C++ ObjC++,Wall) +Warn about mismatched declarations of VLA parameters. + Wvolatile C++ ObjC++ Var(warn_volatile) Warning Warn about deprecated uses of volatile qualifier. diff --git a/gcc/c/c-decl.c b/gcc/c/c-decl.c index 5d6b504fe78..2aba39d779c 100644 --- a/gcc/c/c-decl.c +++ b/gcc/c/c-decl.c @@ -25,6 +25,7 @@ along with GCC; see the file COPYING3. If not see line numbers. For example, the CONST_DECLs for enum values. */ #include "config.h" +#define INCLUDE_STRING #define INCLUDE_UNIQUE_PTR #include "system.h" #include "coretypes.h" @@ -58,6 +59,8 @@ along with GCC; see the file COPYING3. If not see #include "c-family/known-headers.h" #include "c-family/c-spellcheck.h" +#include "tree-pretty-print.h" + /* In grokdeclarator, distinguish syntactic contexts of declarators. */ enum decl_context { NORMAL, /* Ordinary declaration */ @@ -4967,6 +4970,17 @@ groktypename (struct c_type_name *type_name, tree *expr, return type; } +/* Looks up the most recent pushed declaration corresponding to DECL. */ + +static tree +lookup_last_decl (tree decl) +{ + tree last_decl = lookup_name (DECL_NAME (decl)); + if (!last_decl) + last_decl = lookup_name_in_scope (DECL_NAME (decl), external_scope); + return last_decl; +} + /* Wrapper for decl_attributes that adds some implicit attributes to VAR_DECLs or FUNCTION_DECLs. */ @@ -4995,10 +5009,7 @@ c_decl_attributes (tree *node, tree attributes, int flags) so far so that attributes on the current declaration that's about to be pushed that conflict with the former can be detected, diagnosed, and rejected as appropriate. */ - tree last_decl = lookup_name (DECL_NAME (*node)); - if (!last_decl) - last_decl = lookup_name_in_scope (DECL_NAME (*node), external_scope); - + tree last_decl = lookup_last_decl (*node); return decl_attributes (node, attributes, flags, last_decl); } @@ -5008,6 +5019,8 @@ c_decl_attributes (tree *node, tree attributes, int flags) have been parsed, before parsing the initializer if any. Here we create the ..._DECL node, fill in its type, and put it on the list of decls for the current context. + When nonnull, set *LASTLOC to the location of the prior declaration + of the same entity if one exists. The ..._DECL node is returned as the value. Exception: for arrays where the length is not specified, @@ -5020,7 +5033,7 @@ c_decl_attributes (tree *node, tree attributes, int flags) tree start_decl (struct c_declarator *declarator, struct c_declspecs *declspecs, - bool initialized, tree attributes) + bool initialized, tree attributes, location_t *lastloc /* = NULL */) { tree decl; tree tem; @@ -5038,6 +5051,10 @@ start_decl (struct c_declarator *declarator, struct c_declspecs *declspecs, if (!decl || decl == error_mark_node) return NULL_TREE; + if (tree lastdecl = lastloc ? lookup_last_decl (decl) : NULL_TREE) + if (lastdecl != error_mark_node) + *lastloc = DECL_SOURCE_LOCATION (lastdecl); + if (expr) add_stmt (fold_convert (void_type_node, expr)); @@ -5475,6 +5492,14 @@ finish_decl (tree decl, location_t init_loc, tree init, if (asmspec && VAR_P (decl) && C_DECL_REGISTER (decl)) DECL_HARD_REGISTER (decl) = 1; rest_of_decl_compilation (decl, true, 0); + + if (TREE_CODE (decl) == FUNCTION_DECL) + { + tree parms = DECL_ARGUMENTS (decl); + const bool builtin = fndecl_built_in_p (decl); + if (tree access = build_attr_access_from_parms (parms, !builtin)) + decl_attributes (&decl, access, 0); + } } else { @@ -5627,6 +5652,175 @@ grokparm (const struct c_parm *parm, tree *expr) return decl; } +/* Return attribute "arg spec" corresponding to an array/VLA parameter + described by PARM, concatenated onto attributes ATTRS. + The spec consists of one dollar symbol for each specified variable + bound, one asterisk for each unspecified variable bound, followed + by at most one specification of the most significant bound of + an ordinary array parameter. For ordinary arrays the specification + is either the constant bound itself, or the space character for + an array with an unspecified bound (the [] form). Finally, a chain + of specified variable bounds is appended to the spec, starting with + the most significant bound. For example, the PARM T a[2][m][3][n] + will produce __attribute__((arg spec ("[$$2]", m, n)). + For T a typedef for an array with variable bounds, the bounds are + included in the specification in the expected order. + No "arg spec" is created for parameters of pointer types, making + a distinction between T(*)[N] (or, equivalently, T[][N]) and + the T[M][N] form, all of which have the same type and are represented + the same, but only the last of which gets an "arg spec" describing + the most significant bound M. */ + +static tree +get_parm_array_spec (const struct c_parm *parm, tree attrs) +{ + /* The attribute specification string, minor bound first. */ + std::string spec; + + /* A list of VLA variable bounds, major first, or null if unspecified + or not a VLA. */ + tree vbchain = NULL_TREE; + /* True for a pointer parameter. */ + bool pointer = false; + /* True for an ordinary array with an unpecified bound. */ + bool nobound = false; + + /* Create a string representation for the bounds of the array/VLA. */ + for (c_declarator *pd = parm->declarator, *next; pd; pd = next) + { + next = pd->declarator; + if (next && next->kind == cdk_attrs) + next = next->declarator; + + /* Remember if a pointer has been seen to avoid storing the constant + bound. */ + if (pd->kind == cdk_pointer) + pointer = true; + + if ((pd->kind == cdk_pointer || pd->kind == cdk_function) + && (!next || next->kind == cdk_id)) + { + /* Do nothing for the common case of a pointer. The fact that + the parameter is one can be deduced from the absence of + an arg spec for it. */ + return attrs; + } + + if (pd->kind == cdk_id) + { + if (pointer + || !parm->specs->type + || TREE_CODE (parm->specs->type) != ARRAY_TYPE + || !TYPE_DOMAIN (parm->specs->type) + || !TYPE_MAX_VALUE (TYPE_DOMAIN (parm->specs->type))) + continue; + + tree max = TYPE_MAX_VALUE (TYPE_DOMAIN (parm->specs->type)); + if (!vbchain + && TREE_CODE (max) == INTEGER_CST) + { + /* Extract the upper bound from a parameter of an array type + unless the parameter is an ordinary array of unspecified + bound in which case a next iteration of the loop will + exit. */ + if (spec.empty () || spec.end ()[-1] != ' ') + { + if (!tree_fits_shwi_p (max)) + continue; + + /* The upper bound is the value of the largest valid + index. */ + HOST_WIDE_INT n = tree_to_shwi (max) + 1; + char buf[40]; + sprintf (buf, "%lu", (unsigned long)n); + spec += buf; + } + continue; + } + + /* For a VLA typedef, create a list of its variable bounds and + append it in the expected order to VBCHAIN. */ + tree tpbnds = NULL_TREE; + for (tree type = parm->specs->type; TREE_CODE (type) == ARRAY_TYPE; + type = TREE_TYPE (type)) + { + tree nelts = array_type_nelts (type); + if (TREE_CODE (nelts) != INTEGER_CST) + { + /* Each variable VLA bound is represented by the dollar + sign. */ + spec += "$"; + tpbnds = tree_cons (NULL_TREE, nelts, tpbnds); + } + } + tpbnds = nreverse (tpbnds); + vbchain = chainon (vbchain, tpbnds); + continue; + } + + if (pd->kind != cdk_array) + continue; + + if (pd->u.array.vla_unspec_p) + { + /* Each unspecified bound is represented by a star. There + can be any number of these in a declaration (but none in + a definition). */ + spec += '*'; + continue; + } + + tree nelts = pd->u.array.dimen; + if (!nelts) + { + /* Ordinary array of unspecified size. There can be at most + one for the most significant bound. Exit on the next + iteration which determines whether or not PARM is declared + as a pointer or an array. */ + nobound = true; + continue; + } + + if (TREE_CODE (nelts) == INTEGER_CST) + { + /* Skip all constant bounds except the most significant one. + The interior ones are included in the array type. */ + if (next && (next->kind == cdk_array || next->kind == cdk_pointer)) + continue; + + if (!tree_fits_uhwi_p (nelts)) + /* Bail completely on invalid bounds. */ + return attrs; + + char buf[40]; + const char *code = pd->u.array.static_p ? "s" : ""; + unsigned HOST_WIDE_INT n = tree_to_uhwi (nelts); + sprintf (buf, "%s%llu", code, (unsigned long long)n); + spec += buf; + break; + } + + /* Each variable VLA bound is represented by a dollar sign. */ + spec += "$"; + vbchain = tree_cons (NULL_TREE, nelts, vbchain); + } + + if (spec.empty () && !nobound) + return attrs; + + spec.insert (0, "["); + if (nobound) + /* Ordinary array of unspecified bound is represented by a space. + It must be last in the spec. */ + spec += ' '; + spec += ']'; + + tree acsstr = build_string (spec.length () + 1, spec.c_str ()); + tree args = tree_cons (NULL_TREE, acsstr, vbchain); + tree name = get_identifier ("arg spec"); + return tree_cons (name, args, attrs); +} + /* Given a parsed parameter declaration, decode it into a PARM_DECL and push that on the current scope. EXPR is a pointer to an expression that needs to be evaluated for the side effects of array @@ -5636,12 +5830,12 @@ void push_parm_decl (const struct c_parm *parm, tree *expr) { tree attrs = parm->attrs; - tree decl; - - decl = grokdeclarator (parm->declarator, parm->specs, PARM, false, NULL, - &attrs, expr, NULL, DEPRECATED_NORMAL); + tree decl = grokdeclarator (parm->declarator, parm->specs, PARM, false, NULL, + &attrs, expr, NULL, DEPRECATED_NORMAL); if (decl && DECL_P (decl)) DECL_SOURCE_LOCATION (decl) = parm->loc; + + attrs = get_parm_array_spec (parm, attrs); decl_attributes (&decl, attrs, 0); decl = pushdecl (decl); @@ -9227,6 +9421,7 @@ start_function (struct c_declspecs *declspecs, struct c_declarator *declarator, old_decl = lookup_name_in_scope (DECL_NAME (decl1), current_scope); if (old_decl && TREE_CODE (old_decl) != FUNCTION_DECL) old_decl = NULL_TREE; + current_function_prototype_locus = UNKNOWN_LOCATION; current_function_prototype_built_in = false; current_function_prototype_arg_types = NULL_TREE; @@ -9357,12 +9552,22 @@ start_function (struct c_declspecs *declspecs, struct c_declarator *declarator, "%qD is normally a non-static function", decl1); } + tree parms = current_function_arg_info->parms; + if (old_decl) + { + location_t origloc = DECL_SOURCE_LOCATION (old_decl); + warn_parm_array_mismatch (origloc, old_decl, parms); + } + /* Record the decl so that the function name is defined. If we already have a decl for this name, and it is a FUNCTION_DECL, use the old decl. */ current_function_decl = pushdecl (decl1); + if (tree access = build_attr_access_from_parms (parms, false)) + decl_attributes (¤t_function_decl, access, 0, old_decl); + push_scope (); declare_parm_level (); diff --git a/gcc/c/c-parser.c b/gcc/c/c-parser.c index 7961cbc98bb..f5d18195c14 100644 --- a/gcc/c/c-parser.c +++ b/gcc/c/c-parser.c @@ -68,6 +68,7 @@ along with GCC; see the file COPYING3. If not see #include "intl.h" #include "c-family/name-hint.h" #include "tree-iterator.h" +#include "tree-pretty-print.h" #include "memmodel.h" #include "c-family/known-headers.h" @@ -2296,13 +2297,11 @@ c_parser_declaration_or_fndef (c_parser *parser, bool fndef_ok, c_parser_skip_to_end_of_block_or_statement (parser); return; } - tree d = start_decl (declarator, specs, false, - chainon (postfix_attrs, - all_prefix_attrs)); - if (d - && TREE_CODE (d) == FUNCTION_DECL - && DECL_ARGUMENTS (d) == NULL_TREE - && DECL_INITIAL (d) == NULL_TREE) + + location_t lastloc = UNKNOWN_LOCATION; + tree attrs = chainon (postfix_attrs, all_prefix_attrs); + tree d = start_decl (declarator, specs, false, attrs, &lastloc); + if (d && TREE_CODE (d) == FUNCTION_DECL) { /* Find the innermost declarator that is neither cdk_id nor cdk_attrs. */ @@ -2331,10 +2330,18 @@ c_parser_declaration_or_fndef (c_parser *parser, bool fndef_ok, gcc_unreachable (); } - /* If it exists and is cdk_function, use its parameters. */ + /* If it exists and is cdk_function declaration whose + arguments have not been set yet, use its arguments. */ if (last_non_id_attrs && last_non_id_attrs->kind == cdk_function) - DECL_ARGUMENTS (d) = last_non_id_attrs->u.arg_info->parms; + { + tree parms = last_non_id_attrs->u.arg_info->parms; + if (DECL_ARGUMENTS (d) == NULL_TREE + && DECL_INITIAL (d) == NULL_TREE) + DECL_ARGUMENTS (d) = parms; + + warn_parm_array_mismatch (lastloc, d, parms); + } } if (omp_declare_simd_clauses.exists ()) { @@ -2363,7 +2370,7 @@ c_parser_declaration_or_fndef (c_parser *parser, bool fndef_ok, if (d) finish_decl (d, UNKNOWN_LOCATION, NULL_TREE, NULL_TREE, asm_name); - + if (c_parser_next_token_is_keyword (parser, RID_IN)) { if (d) diff --git a/gcc/c/c-tree.h b/gcc/c/c-tree.h index 10938cf0857..3adc29dafc4 100644 --- a/gcc/c/c-tree.h +++ b/gcc/c/c-tree.h @@ -609,7 +609,7 @@ extern void shadow_tag_warned (const struct c_declspecs *, int); extern tree start_enum (location_t, struct c_enum_contents *, tree); extern bool start_function (struct c_declspecs *, struct c_declarator *, tree); extern tree start_decl (struct c_declarator *, struct c_declspecs *, bool, - tree); + tree, location_t * = NULL); extern tree start_struct (location_t, enum tree_code, tree, class c_struct_parse_info **); extern void store_parm_decls (void); diff --git a/gcc/calls.c b/gcc/calls.c index 77ab8647a10..79aa2c09b29 100644 --- a/gcc/calls.c +++ b/gcc/calls.c @@ -2463,7 +2463,7 @@ initialize_argument_information (int num_actuals ATTRIBUTE_UNUSED, if (POINTER_TYPE_P (type)) { access->ptr = args[i].tree_value; - gcc_assert (access->size == NULL_TREE); + // A nonnull ACCESS->SIZE contains VLA bounds. */ } else { diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi index 70dc1ab73a1..0243d060994 100644 --- a/gcc/doc/invoke.texi +++ b/gcc/doc/invoke.texi @@ -5214,6 +5214,7 @@ Options} and @ref{Objective-C and Objective-C++ Dialect Options}. @gccoptlist{-Waddress @gol -Warray-bounds=1 @r{(only with} @option{-O2}@r{)} @gol +-Warray-parameter=2 @r{(C and Objective-C only)} @gol -Wbool-compare @gol -Wbool-operation @gol -Wc++11-compat -Wc++14-compat @gol @@ -5265,6 +5266,7 @@ Options} and @ref{Objective-C and Objective-C++ Dialect Options}. -Wunused-label @gol -Wunused-value @gol -Wunused-variable @gol +-Wvla-parameter @r{(C and Objective-C only)} @gol -Wvolatile-register-var @gol -Wzero-length-bounds} @@ -7108,6 +7110,54 @@ pointers. This warning level may give a larger number of false positives and is deactivated by default. @end table +@item -Warray-parameter +@itemx -Warray-parameter=@var{n} +@opindex Wno-array-parameter +Warn about redeclarations of functions involving arguments of array or +pointer types of inconsistent kinds or forms, and enable the detection +of out-of-bounds accesses to such parameters by warnings such as +@option{-Warray-bounds}. + +If the first function declaration uses the array form the bound specified +in the array is assumed to be the minimum number of elements expected to +be provided in calls to the function and the maximum number of elements +accessed by it. Failing to provide arguments of sufficient size or accessing +more than the maximum number of elements may be diagnosed by warnings such +as @option{-Warray-bounds}. At level 1 the warning diagnoses inconsistencies +involving array parameters declared using the @code{T[static N]} form. + +For example, the warning triggers for the following redeclarations because +the first one allows an array of any size to be passed to @code{f} while +the second one with the keyword @code{static} specifies that the array +argument must have at least four elements. + +@smallexample +void f (int[static 4]); +void f (int[]); // warning (inconsistent array form) + +void g (void) +@{ + int *p = (int *)malloc (4); + f (p); // warning (array too small) + @dots{} +@} +@end smallexample + +At level 2 the warning also triggers for redeclarations involving any other +inconsistency in array or pointer argument forms denoting array sizes. +Pointers and arrays of unspecified bound are considered equivalent and do +not trigger a warning. + +@smallexample +void g (int*); +void g (int[]); // no warning +void g (int[8]); // warning (inconsistent array bound) +@end smallexample + +@option{-Warray-parameter=2} is included in @option{-Wall}. The +@option{-Wvla-parameter} option triggers warnings for similar inconsistencies +involving Variable Length Array arguments. + @item -Wattribute-alias=@var{n} @itemx -Wno-attribute-alias @opindex Wattribute-alias @@ -8531,6 +8581,44 @@ See also @option{-Walloca-larger-than=@var{byte-size}}. Disable @option{-Wvla-larger-than=} warnings. The option is equivalent to @option{-Wvla-larger-than=}@samp{SIZE_MAX} or larger. +@item -Wvla-parameter +@opindex Wno-vla-parameter +Warn about redeclarations of functions involving arguments of Variable +Length Array types of inconsistent kinds or forms, and enable the detection +of out-of-bounds accesses to such parameters by warnings such as +@option{-Warray-bounds}. + +If the first function declaration uses the VLA form the bound specified +in the array is assumed to be the minimum number of elements expected to +be provided in calls to the function and the maximum number of elements +accessed by it. Failing to provide arguments of sufficient size or +accessing more than the maximum number of elements may be diagnosed. + +For example, the warning triggers for the following redeclarations because +the first one allows an array of any size to be passed to @code{f} while +the second one specifies that the array argument must have at least @code{n} +elements. In addition, calling @code{f} with the assotiated VLA bound +parameter in excess of the actual VLA bound triggers a warning as well. + +@smallexample +void f (int n, int[n]); +void f (int, int[]); // warning: argument 2 previously declared as a VLA + +void g (int n) +@{ + if (n > 4) + return; + int a[n]; + f (sizeof a, a); // warning: access to a by f may be out of bounds + @dots{} +@} + +@end smallexample + +@option{-Wvla-parameter} is included in @option{-Wall}. The +@option{-Warray-parameter} option triggers warnings for similar problems +involving ordinary array arguments. + @item -Wvolatile-register-var @opindex Wvolatile-register-var @opindex Wno-volatile-register-var diff --git a/gcc/testsuite/c-c++-common/Warray-bounds-6.c b/gcc/testsuite/c-c++-common/Warray-bounds-6.c index 6611d5cd916..5e787360314 100644 --- a/gcc/testsuite/c-c++-common/Warray-bounds-6.c +++ b/gcc/testsuite/c-c++-common/Warray-bounds-6.c @@ -1,8 +1,12 @@ -/* PR tree-optimization/86614 */ -/* { dg-do compile } */ -/* { dg-options "-O2 -Warray-bounds" } */ +/* PR tree-optimization/86614 - duplicate -Warray-bounds for a strncpy + call with out-of-bounds offset + { dg-do compile } + { dg-options "-O2 -Warray-bounds" } */ -extern char *strncpy (char *, const char *, __SIZE_TYPE__); +#if __cplusplus +extern "C" +#endif +char *strncpy (char *, const char *, __SIZE_TYPE__); void sink (void *); @@ -12,7 +16,8 @@ void g (const char *s, unsigned n) { int i = (char *)a[1].b - (char *)a + 1; char *d = a[1].b; - /* Ensure the same bug is not diagnosed more than once. */ - strncpy (d + i, s, n); /* { dg-warning "array subscript \[0-9]+ is outside array bounds of" } */ - /* { dg-bogus "offset \[0-9]+ is out of the bounds \\\[0, \[0-9]+\\\] of object 'a' with type" "" { target *-*-* } .-1 } */ + /* Verify the bug is diagnosed exactly once, using either form + of the warning. */ + strncpy (d + i, s, n); /* { dg-warning "array subscript \[0-9]+ is outside array bounds|offset \[0-9]+ is out of the bounds" } */ + /* { dg-bogus "offset \[0-9]+ is out of the bounds|array subscript \[0-9]+ is outside array bounds" "" { target *-*-* } .-1 } */ } diff --git a/gcc/testsuite/gcc.dg/Warray-parameter-2.c b/gcc/testsuite/gcc.dg/Warray-parameter-2.c new file mode 100644 index 00000000000..88f20e203ce --- /dev/null +++ b/gcc/testsuite/gcc.dg/Warray-parameter-2.c @@ -0,0 +1,45 @@ +/* PR c/50584 - No warning for passing small array to C99 static array + declarator + { dg-do compile } + { dg-options "-Wall" } */ + +// Reduced from Glibc. + +typedef struct FILE FILE; + +int vfprintf (FILE*, const char*, __builtin_va_list); +int vfprintf (FILE*, const char*, __builtin_va_list); // { dg-bogus "-Warray-parameter" } +int vfprintf (FILE*, const char*, __builtin_va_list); + +int vfscanf (FILE*, const char*, __builtin_va_list); +int vfscanf (FILE*, const char*, __builtin_va_list); // { dg-bogus "-Warray-parameter" } +int vfscanf (FILE*, const char*, __builtin_va_list); + + +/* Verify that mismatches in array/to pointer to va_list are still + diagnosed. */ + +int fva (__builtin_va_list); +int fva (__builtin_va_list); + +int fpva_a1 (__builtin_va_list*); +int fpva_a1 (__builtin_va_list[1]); // { dg-warning "\\\[-Warray-parameter" } + +int fpva_a_ (__builtin_va_list*); +int fpva_a_ (__builtin_va_list[]); +int fpva_a_ (__builtin_va_list*); +int fpva_a_ (__builtin_va_list[]); + +/* Also verify that a mismatch between a pointer and a one-element + array are diagnosed. This is pervasive in Glibc headers but + making an exception for it would leave no way to express + the requirement that a function take at least one argument + by reference. */ + +struct __jmp_buf_tag; +int __sigsetjmp (struct __jmp_buf_tag*, int); + +struct __jmp_buf_tag { }; +typedef struct __jmp_buf_tag jmp_buf[1]; + +int __sigsetjmp (struct __jmp_buf_tag[1], int); // { dg-warning "\\\[-Warray-parameter" } diff --git a/gcc/testsuite/gcc.dg/Warray-parameter-3.c b/gcc/testsuite/gcc.dg/Warray-parameter-3.c new file mode 100644 index 00000000000..cbf3e9339f5 --- /dev/null +++ b/gcc/testsuite/gcc.dg/Warray-parameter-3.c @@ -0,0 +1,89 @@ +/* PR c/50584 - No warning for passing small array to C99 static array + declarator + { dg-do compile } + { dg-options "-Wall -Warray-parameter=1" } */ + +/* Verify that at level 1 mismatches in the bounds of ordinary array + parameters don't trigger -Warray-parameter. */ +void fax (int[]); +void fax (int[0]); +void fax (int[1]); +void fax (int[2]); +void fax (int[3]); + +/* Same as above but starting with an array with a specified bound. */ +void gax (int[3]); +void gax (int[2]); +void gax (int[1]); +void gax (int[0]); +void gax (int[]); + +/* Same for multidimensional arrays. */ +void fax_y (int[][3]); +void fax_y (int[0][3]); +void fax_y (int[1][3]); +void fax_y (int[2][3]); +void fax_y (int[3][3]); + +/* Same as above but starting with an array with a specified bound. */ +void gax_y (int[3][5]); +void gax_y (int[2][5]); +void gax_y (int[1][5]); +void gax_y (int[0][5]); +void gax_y (int[][5]); + +/* Exercise VLAs with a mismatch in the bound for an ordinary array. */ +void fvlax_y (int n, int[][n]); +void fvlax_y (int n, int[0][n]); +void fvlax_y (int n, int[1][n]); +void fvlax_y (int n, int[2][n]); +void fvlax_y (int n, int[3][n]); + +void fvlaxn_y (int n, int[][n]); +void fvlaxn_y (int n, int[0][n]); +void fvlaxn_y (int n, int[1][n]); +void fvlaxn_y (int n, int[2][n]); +void fvlaxn_y (int n, int[3][n]); + +void fvlaxx_y (int[][*]); +void fvlaxx_y (int[0][*]); +void fvlaxx_y (int[1][*]); +void fvlaxx_y (int[2][*]); +void fvlaxx_y (int[3][*]); + +/* Verify that mismatches in the bounds of array parameters declared + static do trigger -Warray-parameter. */ +void fas1 (int[static 1]); // { dg-message "previously declared as 'int\\\[static 1]'" } +void fas1 (int[static 2]); // { dg-warning "\\\[-Warray-parameter=" } + + +/* Also verify that -Warray-bounds doesn't trigger for ordinary array + parameters... */ +#pragma GCC optimize "2" + +__attribute__ ((noipa)) void +gca3 (char a[3]) +{ + a[0] = 0; a[1] = 1; a[2] = 2; a[3] = 3; +} + +__attribute__ ((noipa)) void +gia3 (int a[3]) +{ + a[0] = 0; a[1] = 1; a[2] = 2; a[3] = 3; +} + +/* ...but does for static arrays. */ +__attribute__ ((noipa)) void +gcas3 (char a[static 3]) +{ + a[0] = 0; a[1] = 1; a[2] = 2; + a[3] = 3; // { dg-warning "\\\[-Warray-bounds" } +} + +__attribute__ ((noipa)) void +gias3 (int a[static 3]) +{ + a[0] = 0; a[1] = 1; a[2] = 2; + a[3] = 3; // { dg-warning "\\\[-Warray-bounds" } +} diff --git a/gcc/testsuite/gcc.dg/Warray-parameter-4.c b/gcc/testsuite/gcc.dg/Warray-parameter-4.c new file mode 100644 index 00000000000..b702d730a13 --- /dev/null +++ b/gcc/testsuite/gcc.dg/Warray-parameter-4.c @@ -0,0 +1,119 @@ +/* PR c/50584 - No warning for passing small array to C99 static array + declarator + Verify warnings for multidimensional arrays, including mismatches + in bounds of arrays of VLAs. (Mismatches in variable bounds are + diagnosed by -Wvla-parameter.) + { dg-do compile } + { dg-options "-Wall -Warray-parameter=2" } */ + +// Verify that equivalent forms don't tigger a warning. + +typedef int IA1[1]; +typedef int IA1x_[][1]; +typedef int IA1x0[0][1]; + +void fia_x1 (int[][1]); +void fia_x1 (IA1[0]); +void fia_x1 (IA1x_); +void fia_x1 (IA1x0); +void fia_x1 (int[0][1]); +void fia_x1 (int[static 0][1]); + +// Same as above one more time. +void fia_x1 (int[][1]); +void fia_x1 (IA1[0]); +void fia_x1 (IA1x_); +void fia_x1 (IA1x0); +void fia_x1 (int[0][1]); +void fia_x1 (int[static 0][1]); + + +void fia1x1 (int[1][1]); +void fia1x1 (int[1][1]); +void fia1x1 (int[static 1][1]); + +void fia2x1 (int[2][1]); +void fia2x1 (int[2][1]); +void fia2x1 (int[static 2][1]); + +void fia1x2 (int[1][2]); +void fia1x2 (int[1][2]); +void fia1x2 (int[static 1][2]); + +void fia1x1_2x1 (int[1][1]); // { dg-message "previously declared as 'int\\\[1]\\\[1]'" } +void fia1x1_2x1 (int[2][1]); // { dg-warning "\\\[-Warray-parameter" } +void fia1x1_2x1 (int[static 1][1]); +void fia1x1_2x1 (int[static 2][1]); // { dg-warning "\\\[-Warray-parameter" } + + +void fia2x1_1x1 (int[2][1]); // { dg-message "previously declared as 'int\\\[2]\\\[1]'" } +void fia2x1_1x1 (int[1][1]); // { dg-warning "\\\[-Warray-parameter" } +void fia2x1_1x1 (int[2][1]); +void fia2x1_1x1 (int[static 1][1]); // { dg-warning "\\\[-Warray-parameter" } +void fia2x1_1x1 (int[static 2][1]); + + +extern int n1, n2; + +void fca_xn1 (char[][n1]); +void fca_xn1 (char[0][n1]); +void fca_xn1 (char[static 0][n1]); + +void fca1xn1_2xn1 (char[1][n1]); +void fca1xn1_2xn1 (char[2][n1]); // { dg-warning "\\\[-Warray-parameter" } +void fca1xn1_2xn1 (char[1][n1]); +void fca1xn1_2xn1 (char[static 1][n1]); +void fca1xn1_2xn1 (char[static 2][n1]); // { dg-warning "\\\[-Warray-parameter" } + + +/* Exercise VLAs with a mismatch in the bound for an ordinary array. */ +void fvlax_y (int n, int[][n]); +void fvlax_y (int n, int[0][n]); +void fvlax_y (int n, int[1][n]); // { dg-warning "argument 2 of type 'int\\\[1]\\\[n]' with mismatched bound" } +void fvlax_y (int n, int[2][n]); // { dg-warning "argument 2 of type 'int\\\[2]\\\[n]' with mismatched bound" } +void fvlax_y (int n, int[3][n]); // { dg-warning "argument 2 of type 'int\\\[3]\\\[n]' with mismatched bound" } + +void fvlaxn_y (int n, int[][n]); +void fvlaxn_y (int n, int[0][n]); +void fvlaxn_y (int n, int[1][n]); // { dg-warning "\\\[-Warray-parameter" } +void fvlaxn_y (int n, int[2][n]); // { dg-warning "\\\[-Warray-parameter" } +void fvlaxn_y (int n, int[3][n]); // { dg-warning "\\\[-Warray-parameter" } + +void fvlaxx_y (int[][*]); +void fvlaxx_y (int[0][*]); +void fvlaxx_y (int[1][*]); // { dg-warning "\\\[-Warray-parameter" } +void fvlaxx_y (int[2][*]); // { dg-warning "\\\[-Warray-parameter" } +void fvlaxx_y (int[3][*]); // { dg-warning "\\\[-Warray-parameter" } + + +// Verify an array of pointers to an array of function pointers. + +void ffpa7_5 (void (* (* (* [7])[5])(void))(void)); +// { dg-message "previously declared as 'void \\\(\\\* ?\\\(\\\* ?\\\(\\\*\\\[7]\\\)\\\[5]\\\)\\\(void\\\)\\\)\\\(void\\\)'" "note" { target *-*-* } .-1 } +void ffpa7_5 (void (* (* (* [6])[5])(void))(void)); +// { dg-warning "argument 1 of type 'void \\\(\\\* ?\\\(\\\* ?\\\(\\\*\\\[6]\\\)\\\[5]\\\)\\\(void\\\)\\\)\\\(void\\\)' with mismatched bound" "" { target *-*-* } .-1 } +void ffpa7_5 (void (* (* (* [])[5])(void))(void)); +// { dg-warning "argument 1 of type 'void \\\(\\\* ?\\\(\\\* ?\\\(\\\*\\\[]\\\)\\\[5]\\\)\\\(void\\\)\\\)\\\(void\\\)' with mismatched bound" "" { target *-*-* } .-1 } +void ffpa7_5 (void (* (* (* (*))[5])(void))(void)); +// { dg-warning "argument 1 of type 'void \\\(\\\* ?\\\(\\\* ?\\\(\\\*\\\*\\\)\\\[5]\\\)\\\(void\\\)\\\)\\\(void\\\)' declared as a pointer" "" { target *-*-* } .-1 } + +// Same as above but with array of pointers to a VLA of function pointers. +void ffpa7_n1 (void (* (* (* [7])[n1])(void))(void)); +// { dg-message "previously declared as 'void \\\(\\\* ?\\\(\\\* ?\\\(\\\*\\\[7]\\\)\\\[n1]\\\)\\\(void\\\)\\\)\\\(void\\\)'" "note" { target *-*-* } .-1 } +void ffpa7_n1 (void (* (* (* [8])[n1])(void))(void)); +// { dg-warning "argument 1 of type 'void \\\(\\\* ?\\\(\\\* ?\\\(\\\*\\\[8]\\\)\\\[n1]\\\)\\\(void\\\)\\\)\\\(void\\\)' with mismatched bound" "" { target *-*-* } .-1 } + +void ffpa9_x (void (* (* (* [9])[*])(void))(void)); +// { dg-message "previously declared as 'void \\\(\\\* ?\\\(\\\* ?\\\(\\\*\\\[9]\\\)\\\[\\\*]\\\)\\\(void\\\)\\\)\\\(void\\\)'" "pr?????" { xfail *-*-* } .-1 } +// { dg-message "previously declared as 'void \\\(\\\* ?\\\(\\\* ?\\\(\\\*\\\[9]\\\)\\\[0]\\\)\\\(void\\\)\\\)\\\(void\\\)'" "" { target *-*-* } .-2 } +void ffpa9_x (void (* (* (* [8])[*])(void))(void)); +// { dg-warning "argument 1 of type 'void \\\(\\\* ?\\\(\\\* ?\\\(\\\*\\\[8]\\\)\\\[\\\*]\\\)\\\(void\\\)\\\)\\\(void\\\)' with mismatched bound" "pr?????" { xfail *-*-* } .-1 } +// { dg-warning "argument 1 of type 'void \\\(\\\* ?\\\(\\\* ?\\\(\\\*\\\[8]\\\)\\\[0]\\\)\\\(void\\\)\\\)\\\(void\\\)' with mismatched bound" "" { target *-*-* } .-2 } + +/* Verify a three-dimensional array of pointers to two-dimensional arrays + of pointers to function pointers. */ + +void ffpa7_5_3 (void (* (* (* (* [7])[5])[3])(void))(void)); +// { dg-message "previously declared as 'void ?\\\(\\\* ?\\\(\\\* ?\\\(\\\* ?\\\(\\\* ?\\\[7]\\\)\\\[5]\\\)\\\[3]\\\)\\\(void\\\)\\\)\\\(void\\\)'" "note" { target *-*-* } .-1 } +void ffpa7_5_3 (void (* (* (* (* [1])[5])[3])(void))(void)); +// { dg-warning "argument 1 of type 'void ?\\\(\\\* ?\\\(\\\* ?\\\(\\\* ?\\\(\\\* ?\\\[1]\\\)\\\[5]\\\)\\\[3]\\\)\\\(void\\\)\\\)\\\(void\\\)' with mismatched bound" "" { target *-*-* } .-1 } diff --git a/gcc/testsuite/gcc.dg/Warray-parameter-5.c b/gcc/testsuite/gcc.dg/Warray-parameter-5.c new file mode 100644 index 00000000000..6e89bf0c801 --- /dev/null +++ b/gcc/testsuite/gcc.dg/Warray-parameter-5.c @@ -0,0 +1,14 @@ +/* PR c/50584 - No warning for passing small array to C99 static array + declarator + Verify that -Warray-parameter diagnoses mismatches in bounds of + arrays between redeclarations of the same function and with pointer + parameters pointing to those arrays. + { dg-do compile } + { dg-options "-Wall -Warray-parameter" } */ + +void fa_x (int (*)[]); // { dg-message "previously declared as 'int \\\(\\\*\\\)\\\[]'" } +void fa_x (int (*)[2]); // { dg-warning "\\\[-Warray-parameter" } +void fa_x (int (*)[2]); // { dg-warning "mismatch in bound 1 of argument 1 declared as 'int \\\(\\\*\\\)\\\[2]'" } + +void fa_2 (int (*)[2]); // { dg-message "previously declared as 'int \\\(\\\*\\\)\\\[2]'" } +void fa_2 (int (*)[]); // { dg-warning "mismatch in bound 1 of argument 1 declared as 'int \\\(\\\*\\\)\\\[]'" } diff --git a/gcc/testsuite/gcc.dg/Warray-parameter.c b/gcc/testsuite/gcc.dg/Warray-parameter.c new file mode 100644 index 00000000000..42be3100e45 --- /dev/null +++ b/gcc/testsuite/gcc.dg/Warray-parameter.c @@ -0,0 +1,187 @@ +/* PR c/50584 - No warning for passing small array to C99 static array + declarator + Verify that -Warray-parameter diagnoses mismatches in array (and + pointer) arrguments between redeclarations of the same function. + Also verify that the array/pointer argument form in a mismatched + redeclaration doesn't override the form in the initial declaration. + { dg-do compile } + { dg-options "-Wall -Warray-parameter -Wno-vla-paramater" } */ + +/* Redclarations with the same or equivalent array form should not + be dianosed. T[0] is diagnosed by -Wpedantic for being invalid + C so there's little point in also warning for the difference in + array form. */ +void f1vpp (void**); +void f1vpp (void*[]); +void f1vpp (void*[0]); + +void f1ia_ (int[]); +void f1ia_ (int[]); +void f1ia_ (int[0]); +/* Verify the unused attribute still has an effect. */ +void f1ia_ (int a[0] __attribute__ ((unused))) { } +void f1ia_ (int[]); + +void f1ia_p (int[]); +void f1ia_p (int*); +void f1ia_p (int *p __attribute__ ((unused))) { } +void f1ia_p (int[]); + +void f1p_ia (const int*); +void f1p_ia (const int[]); +void f1p_ia (const int *p __attribute__ ((unused))) { } +void f1p_ia (const int[]); + +void f1ia1 (int[1]); +void f1ia1 (int[1]); +void f1ia1 (int[2 - 1]); + +void f1ias2 (int[static 2]); +void f1ias2 (int[static 2]); +void f1ias2 (int[static 1 + 1]); +void f1ias2 (int a[static 3 - 1]) { (void)&a; } + +void f1ipa_ (int*[]); +void f1ipa_ (int*[]); +void f1ipa_ (int*[0]); + +void f1ia1_x (int[1]); // { dg-message "previously declared as 'int\\\[1]'" } +void f1ia1_x (int[]); // { dg-warning "argument 1 of type 'int\\\[]' with mismatched bound" } +void f1ia1_x (int[]); // { dg-warning "argument 1 of type 'int\\\[]' with mismatched bound" } +void f1ia1_x (int[1]); +void f1ia1_x (int[2]); // { dg-warning "argument 1 of type 'int\\\[2]' with mismatched bound" } +void f1ia1_x (int[1]); +void f1ia1_x (int[3]); // { dg-warning "argument 1 of type 'int\\\[3]' with mismatched bound" } +void f1ia1_x (int a[1] __attribute__ ((unused))) { } + + +void f1ias2_s3 (int[static 2]); // { dg-message "previously declared as 'int\\\[static 2]'" } +void f1ias2_s3 (int[static 3]); // { dg-warning "argument 1 of type 'int\\\[static 3]' with mismatched bound" } +/* Verify the unused attribute still has an effect and doesn't interfere + with the warning. */ +void f1ias2_s3 (int a[static 3] __attribute__ ((unused))) { } // { dg-warning "argument 1 of type 'int\\\[static 3]' with mismatched bound" } + + +/* Ordinary T[N] and T[static N] forms are both effectively treated + the same but strictly have different meanings so they are diagnosed. + It might be worth splitting the warning into two levels and having + only the higher level treat the ordinary form as T[static N]. */ + +void f1ia3_s4 (int[3]); // { dg-message "previously declared as 'int\\\[3]'" } +void f1ia3_s4 (int[static 4]); // { dg-warning "argument 1 of type 'int\\\[static 4]' with mismatched bound" } +void f1ia3_s4 (int[3]); + + +void f1ias4_5 (int[static 4]); // { dg-message "previously declared as 'int\\\[static 4]'" } +void f1ias4_5 (int[5]); // { dg-warning "argument 1 of type 'int\\\[5]' with mismatched bound" } +void f1ias4_5 (int[static 4]); + + +void f1ia_1 (int[]); // { dg-message "previously declared as 'int\\\[]'" } +void f1ia_1 (int[1]); // { dg-warning "argument 1 of type 'int\\\[1]' with mismatched bound" } +void f1ia_1 (int[]); + + +void f1ca_ (char[]); // { dg-message "previously declared as 'char\\\[]'" } +void f1ca_ (char[2]); // { dg-warning "argument 1 of type 'char\\\[2]' with mismatched bound" } +void f1ca_ (char[]); + + +void f1csp (const short*); // { dg-message "previously declared as 'const short int ?\\\*'" } +void f1csp (const short[3]); // { dg-warning "argument 1 of type 'const short int\\\[3]' with mismatched bound" } +void f1csp (const short*); + + +void f1ia2 (int[2]); // { dg-message "previously declared as 'int\\\[2]'" } +void f1ia2 (int[1]); // { dg-warning "argument 1 of type 'int\\\[1]' with mismatched bound" } +void f1ia2 (int[2]); + + +void f1cvla2 (const volatile long[3]); // { dg-message "previously declared as 'const volatile long int\\\[3]'" } +void f1cvla2 (const volatile long[2]); // { dg-warning "argument 1 of type 'const volatile long int\\\[2]' with mismatched bound" } +void f1cvla2 (const volatile long[3]); +void f1cvla2 (const volatile long[restrict 4]); // { dg-warning "argument 1 of type 'const volatile long int\\\[restrict 4]' with mismatched bound" } + + +void f1afa4 (_Atomic float[3]); // { dg-message "previously declared as an array '_Atomic float ?\\\[3]'" } +void f1afa4 (_Atomic float*); // { dg-warning "argument 1 of type '_Atomic float ?\\\*' declared as a pointer" } +void f1afa4 (_Atomic float[3]); + +void f1ipa1_a2 (int*[1]); // { dg-message "previously declared as 'int \\\*\\\[1]'" } +void f1ipa1_a2 (int*[2]); // { dg-warning "argument 1 of type 'int \\\*\\\[2]' with mismatched bound" } +void f1ipa1_a2 (int*[1]); + + +typedef int IAx[]; +typedef int IA1[1]; +typedef int IA2[2]; +typedef int IA3[3]; + +// The message should differentiate between the [] form and *. +void f1IAx_A1 (IAx); // { dg-message "previously declared as 'int\\\[]'" "pr?????" { xfail *-*-* } } + // { dg-message "previously declared as 'int *\\\*'" "note" { target *-*-* } .-1 } +void f1IAx_A1 (IA1); // { dg-message "argument 1 of type 'int\\\[1]' with mismatched bound" } + +void f1IA1_A2 (IA1); // { dg-message "previously declared as 'int\\\[1]'" } +void f1IA1_A2 (IA2); // { dg-warning "argument 1 of type 'int\\\[2]' with mismatched bound" } +void f1IA1_A2 (IA1); +void f1IA1_A2 (int[2]); // { dg-warning "argument 1 of type 'int\\\[2]' with mismatched bound" } + + +void f1IA1_A3 (IA1 ia1); // { dg-message "previously declared as 'int\\\[1]'" } +void f1IA1_A3 (IA3 ia3); // { dg-warning "argument 1 of type 'int\\\[3]' with mismatched bound" } +void f1IA1_A3 (IA1 ia1); + + +void f1IA2_A3 (IA2 a); // { dg-message "previously declared as 'int\\\[2]'" } +void f1IA2_A3 (IA3 a); // { dg-warning "argument 1 of type 'int\\\[3]' with mismatched bound" } +void f1IA2_A3 (IA2 a); + + +// Verify multiple array arguments. + +void f2a2_a3_3_3 (int[2], int[3]); // { dg-message "previously declared as 'int\\\[2]'" } +void f2a2_a3_3_3 (int[2], int[3]); +void f2a2_a3_3_3 (int[3], int[3]); // { dg-warning "argument 1 of type 'int\\\[3]' with mismatched bound" } + + +void f2a2_a3_2_4 (int[2], int[3]); // { dg-message "previously declared as 'int\\\[3]'" } +void f2a2_a3_2_4 (int[2], int[4]); // { dg-warning "argument 2 of type 'int\\\[4]' with mismatched bound" } + + +/* Verify that pointers to arrays and arrays of arrays are differentiated + the same way as pointers and arrays of other types. */ +typedef IA1 *PA1; + +void fpia1 (IA1*); // { dg-message "previously declared as 'int ?\\(\\\*\\)\\\[1]'" } +void fpia1 (IA1[1]); // { dg-warning "argument 1 of type 'int\\\[1]\\\[1]' with mismatched bound" } +void fpia1 (PA1); +void fpia1 (int(*)[1]); +void fpia1 (int[][1]); + +void f1vpa1 (void*[][1]); +void f1vpa1 (void*[0][1]); + +/* Verify arrays of pointers. */ +void vaip1 (int (*[3])); // { dg-message "previously declared as 'int *\\\*\\\[3]'" } +void vaip1 (int (*[5])); // { dg-warning "argument 1 of type 'int *\\\*\\\[5]' with mismatched bound" } +void vaip1 (int (*[3])); +void vaip1 (int (*[])); // { dg-warning "argument 1 of type 'int *\\\*\\\[]' with mismatched bound" } +void vaip1 (int (*[3])); + +/* Verify that attributes with arrays don't cause unwanted warnings and + don't suppress intended ones. */ + +#define ALIGN(N)__attribute__ ((aligned (__alignof__ (char[N])))) + +void fatipa2 (int (* ALIGN (3)[2])); // { dg-message "previously declared as 'int \\\*\\\[2]'" } +void fatipa2 (int (* ALIGN (4)[2])); +void fatipa2 (int (* ALIGN (5)[2])); +void fatipa2 (int (* ALIGN (7)[3])); // { dg-warning "argument 1 of type 'int \\\*\\\[3]' with mismatched bound" } + +void fatiap (int (* ALIGN (3))[2]); +void fatiap (int (* ALIGN (5))[2]); + + +void fatipa3 (int (* ALIGN (1) (* ALIGN (2))[3])); +void fatipa3 (int (* ALIGN (1) (* ALIGN (2))[3])); diff --git a/gcc/testsuite/gcc.dg/Wbuiltin-declaration-mismatch-9.c b/gcc/testsuite/gcc.dg/Wbuiltin-declaration-mismatch-9.c index f0c1ce33267..da767b87700 100644 --- a/gcc/testsuite/gcc.dg/Wbuiltin-declaration-mismatch-9.c +++ b/gcc/testsuite/gcc.dg/Wbuiltin-declaration-mismatch-9.c @@ -10,3 +10,6 @@ void a (void) "" /* { dg-warning "passing argument 2 of .sscanf. from incompatible pointer type" } */ ); } + +/* The scanf call may also trigger: + { dg-prune-output "-Wstringop-overflow" } */ diff --git a/gcc/testsuite/gcc.dg/Wvla-parameter-2.c b/gcc/testsuite/gcc.dg/Wvla-parameter-2.c new file mode 100644 index 00000000000..ba9324143b4 --- /dev/null +++ b/gcc/testsuite/gcc.dg/Wvla-parameter-2.c @@ -0,0 +1,75 @@ +/* PR c/50584 - No warning for passing small array to C99 static array + declarator + Verify the -Wvla-parameter warnings correctly diagnose mismatches + between multimensional array arguments with one or more variable + bounds in redeclarations of the same function. + { dg-do compile } + { dg-options "-Wall -Wvla-parameter" } */ + +void fmn_a1n_axn (int n, int[1][n]); // { dg-message "previously declared as 'int\\\[1]\\\[n]' with 1 variable bound" "note" } +void fmn_a1n_axn (int n, int[*][n]); // { dg-warning "argument 2 of type 'int\\\[\\\*]\\\[n]' declared with 2 variable bounds" } + + +void fmn_axn_a2n (int n, int[*][n]); // { dg-message "previously declared as 'int\\\[\\\*]\\\[n]' with 2 variable bounds" "note" } +void fmn_axn_a2n (int n, int[2][n]); // { dg-warning "argument 2 of type 'int\\\[2]\\\[n]' declared with 1 variable bound" } + + +void fmn_amn_axn (int m, int n, int[m][n]); // { dg-message "previously declared as 'int\\\[m]\\\[n]' with 0 unspecified variable bounds" "note" } +void fmn_amn_axn (int m, int n, int[*][n]); // { dg-warning "argument 3 of type 'int\\\[\\\*]\\\[n]' declared with 1 unspecified variable bound" } + +// Same as above but a different function name. +void gmn_amn_axn (int m, int n, int[m][n]); // { dg-message "previously declared as 'int\\\[m]\\\[n]' with 0 unspecified variable bounds" "note" } +void gmn_amn_axn (int m, int n, int[*][n]); // { dg-warning "argument 3 of type 'int\\\[\\\*]\\\[n]' declared with 1 unspecified variable bound" } + +typedef int A7[7]; + +void fm_A7_m_5 (int m, A7[m][5]); // { dg-message "previously declared as 'int\\\[m]\\\[5]\\\[7]' with bound argument 1" "note" } +void fm_A7_m_5 (int n, A7[n][5]); + +void fm_A7_m_5 (int n, A7[n + 1][5]); // { dg-warning "argument 2 of type 'int\\\[n \\\+ 1]\\\[5]\\\[7]' declared with mismatched bound 'n \\\+ 1'" } + + +int n1, n2, n3, n4, n5, n6, n7, n8, n9; +void f (int[n1][2][n3][4][n5][6][n7][8][n9]); // { dg-message "previously declared as 'int\\\[n1]\\\[2]\\\[n3]\\\[4]\\\[n5]\\\[6]\\\[n7]\\\[8]\\\[n9]' with 0 unspecified variable bounds" "note" } + // { dg-message "with 5 variable bounds" "note" { target *-*-* } .-1 } +void f (int[n1][2][n3][4][n5][6][n7][8][n9]); + +/* Due to a limitation and because [*] is represented the same as [0] + only the most significant array bound is rendered as [*]; the others + are rendered as [0]. */ +void f (int[n1][2][n3][4][n5][6][n7][8][*]); // { dg-warning "argument 1 of type 'int\\\[n1]\\\[2]\\\[n3]\\\[4]\\\[n5]\\\[6]\\\[n7]\\\[8]\\\[\\\*]' declared with 1 unspecified variable bound" "pr?????" { xfail *-*-* } } +// { dg-warning "argument 1 of type 'int\\\[n1]\\\[2]\\\[n3]\\\[4]\\\[n5]\\\[6]\\\[n7]\\\[8]\\\[0]' declared with 1 unspecified variable bound" "pr?????" { target *-*-* } .-1 } +void f (int[n1][2][n3][4][n5][6][*][8][n9]); // { dg-warning "argument 1 of type 'int\\\[n1]\\\[2]\\\[n3]\\\[4]\\\[n5]\\\[6]\\\[\\\*]\\\[8]\\\[n9]' declared with 1 unspecified variable bound" "pr?????" { xfail *-*-* } } +// { dg-warning "argument 1 of type 'int\\\[n1]\\\[2]\\\[n3]\\\[4]\\\[n5]\\\[6]\\\[0]\\\[8]\\\[n9]' declared with 1 unspecified variable bound" "pr?????" { target *-*-* } .-1 } +void f (int[n1][2][n3][4][*][6][n7][8][n9]); // { dg-warning "argument 1 of type 'int\\\[n1]\\\[2]\\\[n3]\\\[4]\\\[\\\*]\\\[6]\\\[n7]\\\[8]\\\[n9]' declared with 1 unspecified variable bound" "pr?????" { xfail *-*-*} } +// { dg-warning "argument 1 of type 'int\\\[n1]\\\[2]\\\[n3]\\\[4]\\\[0]\\\[6]\\\[n7]\\\[8]\\\[n9]' declared with 1 unspecified variable bound" "pr?????" { target *-*-* } .-1 } +void f (int[n1][2][*][4][n5][6][n7][8][n9]); // { dg-warning "argument 1 of type 'int\\\[n1]\\\[2]\\\[\\\*]\\\[4]\\\[n5]\\\[6]\\\[n7]\\\[8]\\\[n9]' declared with 1 unspecified variable bound" "pr?????" { xfail *-*-* } } +// { dg-warning "argument 1 of type 'int\\\[n1]\\\[2]\\\[0]\\\[4]\\\[n5]\\\[6]\\\[n7]\\\[8]\\\[n9]' declared with 1 unspecified variable bound" "pr?????" { target *-*-* } .-1 } +void f (int[*][2][n3][4][n5][6][n7][8][n9]); // { dg-warning "argument 1 of type 'int\\\[\\\*]\\\[2]\\\[n3]\\\[4]\\\[n5]\\\[6]\\\[n7]\\\[8]\\\[n9]' declared with 1 unspecified variable bound" } + +void f (int[n1][n2][n3][n4][n5][n6][n7][n8][n9]); // { dg-warning "argument 1 of type 'int\\\[n1]\\\[n2]\\\[n3]\\\[n4]\\\[n5]\\\[n6]\\\[n7]\\\[n8]\\\[n9]' declared with 9 variable bounds" } + +// Verify that arrays of pointers to arrays...etc are handled correctly. +void a2pampan (int (*(*(*[2])[n1])[n2])); +// { dg-message "previously declared as 'int \\\* \\\(\\\* \\\(\\\*\\\[2]\\\)\\\[n1]\\\)\\\[n2]'" "note" { target *-*-* } .-1 } +void a2pampan (int (*(*(*[2])[n1])[1])); +// { dg-warning "argument 1 of type 'int \\\* \\\(\\\* \\\(\\\*\\\[2]\\\)\\\[n1]\\\)\\\[1]' declared with 1 variable bound" "" { target *-*-* } .-1 } +void a2pampan (int (*(*(*[2])[1])[n2])); +// { dg-warning "argument 1 of type 'int \\\* \\\(\\\* \\\(\\\*\\\[2]\\\)\\\[1]\\\)\\\[n2]' declared with 1 variable bound" "" { target *-*-* } .-1 } +void a2pampan (int (*(*(*[2])[n1])[n1])); +// { dg-warning "argument 1 of type 'int \\\* \\\(\\\* \\\(\\\*\\\[2]\\\)\\\[n1]\\\)\\\[n1]' declared with mismatched bound 'n1'" "" { target *-*-* } .-1 } +void a2pampan (int (*(*(*[2])[n1])[n2])); + + +/* Verify that the presence or absence of static with VLA dooesn't cause + unwanted warnings. */ + +int f2ia1_1 (int n, int [n][n]); // { sg-message "previously declared as 'int\\\[n]\\\[n]' with bound argument 1" } +int f2ia1_1 (int n, int[static n][n]); +int f2ia1_1 (int n, int a[static n][n]) { return sizeof *a; } +int f2ia1_1 (int n, int[static n + 1][n]); // { dg-warning "argument 2 of type 'int\\\[n \\\+ 1]\\\[n]' declared with mismatched bound 'n \\\+ 1'" } + +int f2ias1_1 (int n, int [static n][n]); // { dg-message "previously declared as 'int\\\[n]\\\[n]' with bound argument 1" } +int f2ias1_1 (int n, int[n][n]); +int f2ias1_1 (int n, int a[++n][n]) // { dg-warning "argument 2 of type 'int\\\[\\\+\\\+n]\\\[n]' declared with mismatched bound ' ?\\+\\+n'" } +{ return sizeof *a; } diff --git a/gcc/testsuite/gcc.dg/Wvla-parameter-3.c b/gcc/testsuite/gcc.dg/Wvla-parameter-3.c new file mode 100644 index 00000000000..51f01729b1d --- /dev/null +++ b/gcc/testsuite/gcc.dg/Wvla-parameter-3.c @@ -0,0 +1,68 @@ +/* PR c/50584 - No warning for passing small array to C99 static array + declarator + Verify that redeclarations of functions with pointer parameters to + arrays with variable bounds are diagnosed if the bounds don't match + either in kind or in the variable expression. + { dg-do compile } + { dg-options "-Wall -Wvla-parameter" } */ + +extern int m, n; + +void pa_ (int (*)[]); // { dg-message "previously declared as 'int \\\(\\\*\\\)\\\[]'" "note" } +void pa_ (int (*)[n]); // { dg-warning "\\\[-Wvla-parameter" } +void pa_ (int (*)[n + 1]); // { dg-warning "mismatch in bound 1 of argument 1 declared as 'int *\\\(\\\*\\\)\\\[n \\\+ 1\\\]'" } + +void ppa_ (int (**)[]); // { dg-message "previously declared as 'int \\\(\\\*\\\*\\\)\\\[]'" "note" } +void ppa_ (int (**)[n]); // { dg-warning "\\\[-Wvla-parameter" } +void ppa_ (int (**)[n + 1]); // { dg-warning "mismatch in bound 1 of argument 1 declared as 'int \\\(\\\*\\\*\\\)\\\[n \\\+ 1\\\]'" } + +void pa1 (int (*)[1]); // { dg-message "previously declared as 'int \\\(\\\*\\\)\\\[1]'" "note" } +void pa1 (int (*)[n]); // { dg-warning "\\\[-Wvla-parameter" } +void pa1 (int (*)[1]); +void pa1 (int (*)[n + 1]); // { dg-warning "mismatch in bound 1 of argument 1 declared as 'int *\\\(\\\*\\\)\\\[n \\\+ 1\\\]'" } + +void ppax (int (**)[*]); // { dg-message "previously declared as 'int \\\(\\\*\\\*\\\)\\\[.]'" "note" } +void ppax (int (**)[n]); // { dg-warning "\\\[-Wvla-parameter" } +/* A VLA with an unspecified bound is represented the same as [0] so + so the pretty printer can't differentiate between the two forms. */ +void ppax (int (**)[1]); // { dg-bogus "\\\[-Warray-parameter" "pr?????" { xfail *-*-* } } + // { dg-warning "\\\[-Wvla-parameter" "pr?????" { xfail *-*-* } .-1 } +void ppax (int (**)[n + 1]); // { dg-warning "mismatch in bound 1 of argument 1 declared as 'int *\\\(\\\*\\\*\\\)\\\[n \\\+ 1\\\]'" } + + +void pa1_n (int (*)[1][n]); +void pa1_n (int (*)[1][n]); +void pa1_n (int (*)[*][n]); // { dg-warning "mismatch in bound 1 of argument 1 declared as 'int \\\(\\\*\\\)\\\[\\\*]\\\[n]'" "pr?????" { xfail *-*-*} } + // { dg-warning "mismatch in bound 1 of argument 1 declared as 'int \\\(\\\*\\\)\\\[0]\\\[n]'" "pr?????" { target *-*-* } .-1 } + +void pa1_n_2 (int (*)[1][n][2]); +void pa1_n_2 (int (*)[1][n][*]); // { dg-warning "mismatch in bound 3 of argument 1 declared as 'int \\\(\\\*\\\)\\\[1]\\\[n]\\\[\\\*]'" "pr?????" { xfail *-*-* } } + // { dg-warning "mismatch in bound 3 of argument 1 declared as 'int \\\(\\\*\\\)\\\[1]\\\[n]\\\[0]'" "pr?????" { target *-*-* } .-1 } + + +void pa1_n_2_a1_n_2 (int (*)[1][n][2], int (*)[1][n][2]); +// { dg-message "previously declared as 'int \\\(\\\*\\\)\\\[1]\\\[n]\\\[2]'" "note" { target *-*-* } .-1 } +void pa1_n_2_a1_n_2 (int (*)[1][n][2], int (*)[1][n][n]); +// { dg-warning "mismatch in bound 3 of argument 2 declared as 'int \\\(\\\*\\\)\\\[1]\\\[n]\\\[n]'" "" { target *-*-* } .-1 } +void pa1_n_2_a1_n_2 (int (*)[1][n][2], int (*)[1][3][2]); +// { dg-warning "mismatch in bound 2 of argument 2 declared as 'int \\\(\\\*\\\)\\\[1]\\\[3]\\\[2]'" "" { target *-*-* } .-1 } +void pa1_n_2_a1_n_2 (int (*)[1][n][2], int (*)[n][n][2]); +// { dg-warning "mismatch in bound 1 of argument 2 declared as 'int \\\(\\\*\\\)\\\[n]\\\[n]\\\[2]'" "" { target *-*-* } .-1 } +void pa1_n_2_a1_n_2 (int (*)[1][n][n], int (*)[1][n][2]); +// { dg-warning "mismatch in bound 3 of argument 1 declared as 'int \\\(\\\*\\\)\\\[1]\\\[n]\\\[n]'" "" { target *-*-* } .-1 } +void pa1_n_2_a1_n_2 (int (*)[n][n][2], int (*)[1][n][2]); +// { dg-warning "mismatch in bound 1 of argument 1 declared as 'int \\\(\\\*\\\)\\\[n]\\\[n]\\\[2]'" "" { target *-*-* } .-1 } +void pa1_n_2_a1_n_2 (int (*)[*][*][*], int (*)[*][*][2]); +// { dg-warning "mismatch in bounds 1, 2, 3 of argument 1 declared as 'int \\\(\\\*\\\)\\\[.]\\\[.]\\\[.]'" "" { target *-*-* } .-1 } +// { dg-warning "mismatch in bounds 1, 2 of argument 2 declared as 'int \\\(\\\*\\\)\\\[.]\\\[.]\\\[2]'" "" { target *-*-* } .-2 } +void pa1_n_2_a1_n_2 (int (*)[1][n][2], int (*)[1][n][2]); + +/* Verify that pointers to arrays of pointers to arrays...etc are handled + correctly. */ +void pa2pampan (int (*(*(*(*)[2])[m])[n])); +// { dg-message "previously declared as 'int \\\* \\\(\\\* \\\(\\\* \\\(\\\*\\\)\\\[2]\\\)\\\[m]\\\)\\\[n]'" "note" { target *-*-* } .-1 } +void pa2pampan (int (*(*(*(*)[2])[m])[1])); +// { dg-warning "mismatch in bound 3 of argument 1 declared as 'int \\\* \\\(\\\* \\\(\\\* \\\(\\\*\\\)\\\[2]\\\)\\\[m]\\\)\\\[1]'" "" { target *-*-* } .-1 } +void pa2pampan (int (*(*(*(*)[2])[1])[n])); +// { dg-warning "mismatch in bound 2 of argument 1 declared as 'int \\\* \\\(\\\* \\\(\\\* \\\(\\\*\\\)\\\[2]\\\)\\\[1]\\\)\\\[n]'" "" { target *-*-* } .-1 } +void pa2pampan (int (*(*(*(*)[2])[m])[n])); diff --git a/gcc/testsuite/gcc.dg/Wvla-parameter-4.c b/gcc/testsuite/gcc.dg/Wvla-parameter-4.c new file mode 100644 index 00000000000..599ad19a3e4 --- /dev/null +++ b/gcc/testsuite/gcc.dg/Wvla-parameter-4.c @@ -0,0 +1,99 @@ +/* PR c/50584 - No warning for passing small array to C99 static array + declarator + Verify warnings for redeclarations of functions with pointer parameters + to arrays with variable bounds involving typedefs. + { dg-do compile } + { dg-options "-Wall -Wvla-parameter" } */ + +extern int m, n; + +typedef int IA3[3]; + +/* Verify the warning points to the declaration with more unspecified + bounds, guiding the user to specify them rather than making them all + unspecified. */ +void* f_pIA3ax (IA3 *x[*]); // { dg-warning "argument 1 of type 'int \\\(\\\*\\\[\\\*]\\\)\\\[3]' .aka '\[^\n\r\}\]+'. declared with 1 unspecified variable bound" } +void* f_pIA3ax (IA3 *x[*]); +void* f_pIA3ax (IA3 *x[n]); // { dg-message "subsequently declared as 'int \\\(\\\*\\\[n]\\\)\\\[3]' with 0 unspecified variable bounds" "note" } +void* f_pIA3ax (IA3 *x[n]) { return x; } + + +void* f_pIA3an (IA3 *x[n]); // { dg-message "previously declared as 'int \\\(\\\*\\\[n]\\\)\\\[3]' with 0 unspecified variable bounds" "note" } +void* f_pIA3an (IA3 *x[n]); +void* f_pIA3an (IA3 *x[*]); // { dg-warning "argument 1 of type 'int \\\(\\\*\\\[\\\*]\\\)\\\[3]' .aka '\[^\n\r\}\]+'. declared with 1 unspecified variable bound" } +void* f_pIA3an (IA3 *x[n]) { return x; } + + +void nowarn_local_fndecl (void) +{ + typedef int IAm[m]; + + void* f_IAm (IAm); + void* f_IAm (int[m]); + void* f_IAm (IAm); + + void* f_iam (int[m]); + void* f_iam (IAm); + void* f_iam (int[m]); + + typedef int IA3[3]; + typedef IA3 IAn_3[n]; + typedef IAn_3 IA2_n_3[2]; + typedef IA2_n_3 IAm_2_n_3[m]; + + void f_IAm_2_n_3 (IAm_2_n_3); + void f_IAm_2_n_3 (IA2_n_3[m]); + void f_IAm_2_n_3 (IAn_3[m][2]); + void f_IAm_2_n_3 (IA3[m][2][n]); + void f_IAm_2_n_3 (int[m][2][n][3]); + + void f_iam_2_n_3 (int[m][2][n][3]); + void f_iam_2_n_3 (IA3[m][2][n]); + void f_iam_2_n_3 (IAn_3[m][2]); + void f_iam_2_n_3 (IAm_2_n_3); + + void f_IAx_m_2_n_3 (IAm_2_n_3[*]); + void f_IAx_m_2_n_3 (IA2_n_3[*][m]); + void f_IAx_m_2_n_3 (IAn_3[*][m][2]); + void f_IAx_m_2_n_3 (IA3[*][m][2][n]); + void f_IAx_m_2_n_3 (int[*][m][2][n][3]); + + void f_IA__m_2_n_3 (IAm_2_n_3[]); + void f_IA__m_2_n_3 (IA2_n_3[][m]); + void f_IA__m_2_n_3 (IAn_3[][m][2]); + void f_IA__m_2_n_3 (IA3[][m][2][n]); + void f_IA__m_2_n_3 (int[][m][2][n][3]); +} + + +void warn_local_fndecl (void) +{ + typedef int IAm[m]; + typedef int IAn[n]; + + void* g_IAm (IAm); // { dg-message "previously declared as 'int\\\[m]' with bound 'm'" } + void* g_IAm (int[n]); // { dg-warning "argument 1 of type 'int\\\[n]' declared with mismatched bound 'n'" } + void* g_IAm (IAm); + + void* g_iam (int[m]); // { dg-message "previously declared as 'int\\\[m]' with bound 'm'" } + void* g_iam (IAn); // { dg-warning "argument 1 of type 'int\\\[n]' declared with mismatched bound 'n'" } + void* g_iam (int[m]); + + + typedef int IA3[3]; + typedef IA3 IAn_3[n]; + typedef IAn_3 IA2_n_3[2]; + typedef IA2_n_3 IAm_2_n_3[m]; + + typedef IA3 IAm_3[m]; + typedef IAm_3 IA2_m_3[2]; + typedef IA2_m_3 IAm_2_m_3[m]; + + void* g_IAm_2_n_3 (IAm_2_n_3); + void* g_IAm_2_n_3 (int[m][2][m][3]); // { dg-warning "argument 1 of type 'int\\\[m]\\\[2]\\\[m]\\\[3]' declared with mismatched bound 'm'" } + void* g_IAm_2_n_3 (IAm_2_n_3); + + void* g_iam_2_n_2 (int[m][2][n][3]); + void* g_iam_2_n_2 (IAm_2_m_3); // { dg-warning "argument 1 of type 'int\\\[m]\\\[2]\\\[m]\\\[3]' declared with mismatched bound 'm'" } + void* g_iam_2_n_2 (int[m][2][n][3]); +} diff --git a/gcc/testsuite/gcc.dg/Wvla-parameter.c b/gcc/testsuite/gcc.dg/Wvla-parameter.c new file mode 100644 index 00000000000..6e4df02969c --- /dev/null +++ b/gcc/testsuite/gcc.dg/Wvla-parameter.c @@ -0,0 +1,136 @@ +/* PR c/50584 - No warning for passing small array to C99 static array + declarator + Verify the -Wvla-parameter warnings correctly diagnose mismatches + between one-dimensional VLA and non-VLA arguments in redeclarations + of the same function. + Also verify that the array/pointer argument form in a mismatched + redeclaration doesn't override the form in the initial declaration. + { dg-do compile } + { dg-options "-Wall -Wvla-parameter" } */ + +/* Verify that redeclaring an argument as a VLA with an unspecified + bound that was first declared as an ordinary array with an unspecified + bound triggers a warning. */ +void f1ia_x (int[]); // { dg-message "previously declared as an ordinary array 'int\\\[]'" "note" } +void f1ia_x (int[*]); // { dg-warning "argument 1 of type 'int\\\[\\\*]' declared as a variable length array" } +void f1ia_x (int[]); +void f1ia_x (int[*]); // { dg-warning "argument 1 of type 'int\\\[\\\*]' declared as a variable length array" } +/* Also verify that a definition of the same form as the first declaration + doesn't trigger a warning and doesn't prevent warnings for subsequent + mismatches. */ +void f1ia_x (int a[]) { (void)&a;} +void f1ia_x (int[*]); // { dg-warning "argument 1 of type 'int\\\[\\\*]' declared as a variable length array" } + +/* Repeat the above but starting with an ordinary array with a constant + bound. */ +void f1ia1x (int[1]); // { dg-message "previously declared as an ordinary array 'int\\\[1]'" "note" } +void f1ia1x (int[*]); // { dg-warning "argument 1 of type 'int\\\[\\\*]' declared as a variable length array" } +void f1ia1x (int a[1]) { (void)&a; } +void f1ia1x (int[1]); +void f1ia1x (int[*]); // { dg-warning "argument 1 of type 'int\\\[\\\*]' declared as a variable length array" } + +void f1ipx (int*); // { dg-message "previously declared as a pointer 'int ?\\\*'" "note" } +void f1ipx (int[*]); // { dg-warning "argument 1 of type 'int\\\[\\\*]' declared as a variable length array" } +void f1ipx (int*); +void f1ipx (int *p) { (void)&p; } +void f1ipx (int[*]); // { dg-warning "argument 1 of type 'int\\\[\\\*]' declared as a variable length array" } +void f1ipx (int*); + +void f2ipx (int*, int*); // { dg-message "previously declared as a pointer 'int ?\\\*'" "note" } +void f2ipx (int*, int[*]); // { dg-warning "argument 2 of type 'int\\\[\\\*]' declared as a variable length array" } +void f2ipx (int*, int*); +void f2ipx (int*, int[*]); // { dg-warning "argument 2 of type 'int\\\[\\\*]' declared as a variable length array" } +void f2ipx (int *p, int *q) { (void)&p; (void)&q; } +void f2ipx (int*, int[*]); // { dg-warning "argument 2 of type 'int\\\[\\\*]' declared as a variable length array" } + +void f1ias2x (int[static 2]); // { dg-message "previously declared as an ordinary array 'int\\\[static 2]'" } +void f1ias2x (int[*]); // { dg-warning "argument 1 of type 'int\\\[\\\*]' declared as a variable length array" } +void f1ias2x (int[static 2]); +void f1ias2x (int[*]); // { dg-warning "argument 1 of type 'int\\\[\\\*]' declared as a variable length array" } +void f1ias2x (int a[static 2]) { (void)&a; } +void f1ias2x (int[*]); // { dg-warning "argument 1 of type 'int\\\[\\\*]' declared as a variable length array" } +void f1ias2x (int[static 2]); + +extern int nelts; + +void f1sa_var (short[]); // { dg-message "previously declared as an ordinary array 'short int\\\[]'" } +void f1sa_var (short[nelts]); // { dg-warning "argument 1 of type 'short int\\\[nelts]' declared as a variable length array" } +void f1sa_var (short[]); +void f1sa_var (short[nelts]); // { dg-warning "argument 1 of type 'short int\\\[nelts]' declared as a variable length array" } +void f1sa_var (short a[]) { (void)&a; } +void f1sa_var (short[nelts]); // { dg-warning "argument 1 of type 'short int\\\[nelts]' declared as a variable length array" } +void f1sa_var (short[]); + +void f1sa_expr (int[]); // { dg-message "previously declared as an ordinary array 'int\\\[]'" } +void f1sa_expr (int[nelts + 1]); // { dg-warning "argument 1 of type 'int\\\[nelts \\\+ 1]' declared as a variable length array" } +void f1sa_expr (int[]); +void f1sa_expr (int[nelts * 2]); // { dg-warning "argument 1 of type 'int\\\[nelts \\\* 2]' declared as a variable length array" } +void f1sa_expr (int a[]) { (void)&a; } +void f1sa_expr (int[nelts / 3]); // { dg-warning "argument 1 of type 'int\\\[nelts / 3]' declared as a variable length array" } +void f1sa_expr (int[]); + +extern int f (int); + +void f1ia_f (int[]); // { dg-message "previously declared as an ordinary array 'int\\\[]'" } +void f1ia_f (int[f (1)]); // { dg-warning "argument 1 of type 'int\\\[f *\\\(1\\\)]' declared as a variable length array" } +void f1ia_f (int[]); +void f1ia_f (int[f (2)]); // { dg-warning "argument 1 of type 'int\\\[f *\\\(2\\\)]' declared as a variable length array" } +void f1ia_f (int a[]) { (void)&a; } +void f1ia_f (int[f (3)]); // { dg-warning "argument 1 of type 'int\\\[f *\\\(3\\\)]' declared as a variable length array" } +void f1ia_f (int[f (4)]); // { dg-warning "argument 1 of type 'int\\\[f *\\\(4\\\)]' declared as a variable length array" } +void f1ia_f (int[]); + +void f1iaf0_f1 (int[f (0)]); // { dg-message "previously declared as 'int\\\[f *\\\(0\\\)]'" } +void f1iaf0_f1 (int[f (1)]); // { dg-warning "argument 1 of type 'int\\\[f *\\\(1\\\)]' declared with mismatched bound" } +void f1iaf0_f1 (int[f (0)]); +void f1iaf0_f1 (int[f (1)]); // { dg-warning "argument 1 of type 'int\\\[f *\\\(1\\\)]' declared with mismatched bound" } +void f1iaf0_f1 (int a[f (0)]) { (void)&a; } +void f1iaf0_f1 (int[f (1)]); // { dg-warning "argument 1 of type 'int\\\[f *\\\(1\\\)]' declared with mismatched bound" } +void f1iaf0_f1 (int[f (0)]); + +void f1la_ (long[]); // { dg-message "previously declared as an ordinary array 'long int\\\[]'" } +void f1la_ (long[nelts]); // { dg-warning "argument 1 of type 'long int\\\[nelts]' declared as a variable length array" } +void f1la_ (long[]); +void f1la_ (long a[nelts]) // { dg-warning "argument 1 of type 'long int\\\[nelts]' declared as a variable length array" } +{ (void)&a; } +void f1la_ (long[]); + +void f2ca_ (int, char[]); // { dg-message "previously declared as an ordinary array 'char\\\[]'" } +void f2ca_ (int n, char[n]); // { dg-warning "argument 2 of type 'char\\\[n]' declared as a variable length array" } +void f2ca_ (int, char[]); +void f2ca_ (int n, char a[n]) // { dg-warning "argument 2 of type 'char\\\[n]' declared as a variable length array" } +{ (void)&n; (void)&a; } + +void f2ia1_f (int n, int[n]); // { dg-message "previously declared as 'int\\\[n]' with bound argument 1" } +void f2ia1_f (int, int[f (0)]); // { dg-warning "argument 2 of type 'int\\\[f *\\\(0\\\)]' declared with mismatched bound 'f *\\\(0\\\)'" } +void f2ia1_f (int m, int[m]); +void f2ia1_f (int, int[f (1)]); // { dg-warning "argument 2 of type 'int\\\[f *\\\(1\\\)]' declared with mismatched bound 'f *\\\(1\\\)'" } +void f2ia1_f (int x, int a[x]) { (void)&x; (void)&a; } +void f2ia1_f (int, int[f (2)]); // { dg-warning "argument 2 of type 'int\\\[f *\\\(2\\\)]' declared with mismatched bound 'f *\\\(2\\\)'" } +void f2ia1_f (int y, int[y]); + +void f2iaf_1 (int, int[f (0)]); // { dg-message "previously declared as 'int\\\[f *\\\(0\\\)]'" } +void f2iaf_1 (int n, int[n]); // { dg-warning "argument 2 of type 'int\\\[n]' declared with mismatched bound argument 1" } +void f2iaf_1 (int, int[f (0)]); +void f2iaf_1 (int m, int[m]); // { dg-warning "argument 2 of type 'int\\\[m]' declared with mismatched bound argument 1" } +void f2iaf_1 (int x, int a[f (0)]) { (void)&x; (void)&a; } +void f2iaf_1 (int y, int[y]); // { dg-warning "argument 2 of type 'int\\\[y]' declared with mismatched bound argument 1" } + + +void f3ia1 (int n, int, int[n]); // { dg-message "previously declared as 'int\\\[n]' with bound argument 1" } +void f3ia1 (int, int n, int[n]); // { dg-warning "argument 3 of type 'int\\\[n]' declared with mismatched bound argument 2" } +void f3ia1 (int n, int, int[n]); + + +extern int g (int); + +void f1iaf_g (int[f (1)]); // { dg-message "previously declared as 'int\\\[f *\\\(1\\\)]'" } +void f1iaf_g (int[g (1)]); // { dg-warning "argument 1 of type 'int\\\[g *\\\(1\\\)]' declared with mismatched bound" } +void f1iaf_g (int[f (1)]); + + +void nrf1iaf_g (int[f (1)]); // { dg-message "previously declared as 'int\\\[f *\\\(1\\\)]'" } +__attribute__ ((nonnull)) +void nrf1iaf_g (int[g (1)]); // { dg-warning "argument 1 of type 'int\\\[g *\\\(1\\\)]' declared with mismatched bound" } +__attribute__ ((noreturn)) +void nrf1iaf_g (int[f (1)]); diff --git a/gcc/tree-pretty-print.c b/gcc/tree-pretty-print.c index 655061c174d..ad3dc59bcfe 100644 --- a/gcc/tree-pretty-print.c +++ b/gcc/tree-pretty-print.c @@ -1682,9 +1682,9 @@ dump_generic_node (pretty_printer *pp, tree node, int spc, dump_flags_t flags, pp_string (pp, "atomic "); if (quals & TYPE_QUAL_CONST) pp_string (pp, "const "); - else if (quals & TYPE_QUAL_VOLATILE) + if (quals & TYPE_QUAL_VOLATILE) pp_string (pp, "volatile "); - else if (quals & TYPE_QUAL_RESTRICT) + if (quals & TYPE_QUAL_RESTRICT) pp_string (pp, "restrict "); if (!ADDR_SPACE_GENERIC_P (TYPE_ADDR_SPACE (node)))