aboutsummaryrefslogtreecommitdiff
path: root/gcc/c-family/c-warn.c
diff options
context:
space:
mode:
authorMartin Sebor <msebor@redhat.com>2020-09-19 17:21:52 -0600
committerMartin Sebor <msebor@redhat.com>2020-09-19 17:34:31 -0600
commit6450f07388f9fe575a489c9309c36012b17b88b0 (patch)
tree929351afdf436f119f15a3d37da96d3a0e3ae27c /gcc/c-family/c-warn.c
parent3696a50beeb73f4ded8a584e76ee16f0bde109b9 (diff)
downloadgcc-6450f07388f9fe575a489c9309c36012b17b88b0.zip
gcc-6450f07388f9fe575a489c9309c36012b17b88b0.tar.gz
gcc-6450f07388f9fe575a489c9309c36012b17b88b0.tar.bz2
Infrastructure & C front end changes for array parameter checking (PR c/50584).
gcc/ChangeLog: PR c/50584 * attribs.c (decl_attributes): Also pass decl along with type attributes to handlers. (init_attr_rdwr_indices): Change second argument to attribute chain. Handle internal attribute representation in addition to external. (get_parm_access): New function. (attr_access::to_internal_string): Define new member function. (attr_access::to_external_string): Define new member function. (attr_access::vla_bounds): Define new member function. * attribs.h (struct attr_access): Declare new members. (attr_access::from_mode_char): Define new member function. (get_parm_access): Declare new function. * calls.c (initialize_argument_information): Pass function type attributes to init_attr_rdwr_indices. * doc/invoke.texi (-Warray-parameter, -Wvla-parameter): Document. * tree-pretty-print.c (dump_generic_node): Correct handling of qualifiers. * tree-ssa-uninit.c (maybe_warn_pass_by_reference): Same. * tree.h (access_mode): Add new enumerator. gcc/c-family/ChangeLog: PR c/50584 * c-attribs.c (c_common_attribute_table): Add "arg spec" attribute. (handle_argspec_attribute): New function. (get_argument, get_argument_type): New functions. (append_access_attrs): Add overload. Handle internal attribute representation in addition to external. (handle_access_attribute): Handle internal attribute representation in addition to external. (build_attr_access_from_parms): New function. 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/testsuite/ChangeLog: PR c/50584 * gcc.dg/attr-access-read-write-2.c: Adjust text of expected message. * 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.c: New test.
Diffstat (limited to 'gcc/c-family/c-warn.c')
-rw-r--r--gcc/c-family/c-warn.c561
1 files changed, 561 insertions, 0 deletions
diff --git a/gcc/c-family/c-warn.c b/gcc/c-family/c-warn.c
index c32d822..d6db85b 100644
--- a/gcc/c-family/c-warn.c
+++ b/gcc/c-family/c-warn.c
@@ -17,6 +17,7 @@ You should have received a copy of the GNU General Public License
along with GCC; see the file COPYING3. If not see
<http://www.gnu.org/licenses/>. */
+#define INCLUDE_STRING
#include "config.h"
#include "system.h"
#include "coretypes.h"
@@ -37,6 +38,7 @@ along with GCC; see the file COPYING3. If not see
#include "c-family/c-spellcheck.h"
#include "calls.h"
#include "stor-layout.h"
+#include "tree-pretty-print.h"
/* Print a warning if a constant expression had overflow in folding.
Invoke this function on every expression that the language
@@ -3099,3 +3101,562 @@ warn_for_address_or_pointer_of_packed_member (tree type, tree rhs)
check_and_warn_address_or_pointer_of_packed_member (type, rhs);
}
+
+/* Return EXPR + 1. Convenience helper used below. */
+
+static inline tree
+plus_one (tree expr)
+{
+ tree type = TREE_TYPE (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<tree, unsigned> curparm2pos;
+ hash_map<tree, unsigned> 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<int> 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 (&noteloc, "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
+ (FNDECL's is set to the location of the redeclaration). */
+
+void
+warn_parm_array_mismatch (location_t origloc, tree fndecl, tree newparms)
+{
+ /* The original parameter list (copied from the original declaration
+ into the current [re]declaration, FNDECL)). The two are equal if
+ and only if FNDECL is the first declaratation. */
+ tree curparms = DECL_ARGUMENTS (fndecl);
+ if (!curparms || !newparms || curparms == newparms)
+ return;
+
+ if (TREE_CODE (curparms) != PARM_DECL
+ || TREE_CODE (newparms) != PARM_DECL)
+ return;
+ /* Extract the (possibly empty) attribute access specification from
+ the declaration and its type (it doesn't yet reflect those created
+ in response to NEWPARMS). */
+ rdwr_map cur_idx;
+ tree fntype = TREE_TYPE (fndecl);
+ init_attr_rdwr_indices (&cur_idx, TYPE_ATTRIBUTES (fntype));
+
+ /* Build a (possibly null) chain of access attributes corresponding
+ to NEWPARMS. */
+ const bool builtin = fndecl_built_in_p (fndecl);
+ tree newattrs = build_attr_access_from_parms (newparms, builtin);
+
+ /* Extract the (possibly empty) attribute access specification from
+ NEWATTRS. */
+ rdwr_map new_idx;
+ init_attr_rdwr_indices (&new_idx, newattrs);
+
+ if (cur_idx.is_empty () && new_idx.is_empty ())
+ {
+ /* 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. */
+ attr_access ptr_spec = { };
+
+ /* Iterate over the two lists of function parameters, comparing their
+ respective mappings and diagnosing mismatches. */
+ unsigned parmpos = 0;
+ for (tree curp = curparms, newp = newparms; curp;
+ curp = TREE_CHAIN (curp), newp = TREE_CHAIN (newp), ++parmpos)
+ {
+ /* Only check pointers and C++ references. */
+ tree newptype = TREE_TYPE (newp);
+ if (!POINTER_TYPE_P (newptype))
+ continue;
+
+ {
+ /* Skip mismatches in __builtin_va_list that is commonly
+ an array but that in declarations of built-ins decays
+ to a pointer. */
+ if (builtin && TREE_TYPE (newptype) == TREE_TYPE (va_list_type_node))
+ continue;
+ }
+
+ /* Access specs for the argument on the current (previous) and
+ new (to replace the current) declarations. Either may be null,
+ indicating the parameter is an ordinary pointer with no size
+ associated with it. */
+ attr_access *cura = cur_idx.get (parmpos);
+ attr_access *newa = new_idx.get (parmpos);
+
+ if (!newa)
+ {
+ /* Continue of both parameters are pointers with no size
+ associated with it. */
+ if (!cura)
+ continue;
+
+ /* Otherwise point at PTR_SPEC and set its parameter pointer
+ and number. */
+ newa = &ptr_spec;
+ newa->ptr = newp;
+ newa->ptrarg = parmpos;
+ }
+ else if (!cura)
+ {
+ cura = &ptr_spec;
+ cura->ptr = curp;
+ cura->ptrarg = parmpos;
+ }
+
+ /* Set if the parameter is [re]declared as a VLA. */
+ const bool cur_vla_p = cura->size || cura->minsize == HOST_WIDE_INT_M1U;
+ const bool new_vla_p = newa->size || newa->minsize == HOST_WIDE_INT_M1U;
+
+ 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;
+
+ tree curptype = TREE_TYPE (curp);
+ const std::string newparmstr = newa->array_as_string (newptype);
+ const std::string curparmstr = cura->array_as_string (curptype);
+ if (new_vla_p && !cur_vla_p)
+ {
+ if (warning_at (newloc, OPT_Wvla_parameter,
+ "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 %s")
+ : G_("previously declared as an ordinary array %s")),
+ curparmstr.c_str ());
+ continue;
+ }
+
+ if (newa == &ptr_spec)
+ {
+ /* The new declaration uses the pointer form. Detect mismatches
+ between the pointer and a previous array or VLA forms. */
+ if (cura->minsize == HOST_WIDE_INT_M1U)
+ {
+ /* Diagnose a pointer/VLA mismatch. */
+ if (warning_at (newloc, OPT_Wvla_parameter,
+ "argument %u of type %s declared "
+ "as a pointer",
+ parmpos + 1, newparmstr.c_str ()))
+ inform (origloc,
+ "previously declared as a variable length array %s",
+ curparmstr.c_str ());
+ continue;
+ }
+
+ if (cura->minsize && cura->minsize != HOST_WIDE_INT_M1U)
+ {
+ /* Diagnose mismatches between arrays with a constant
+ bound and pointers. */
+ if (warning_at (newloc, OPT_Warray_parameter_,
+ "argument %u of type %s declared "
+ "as a pointer",
+ parmpos + 1, newparmstr.c_str ()))
+ inform (origloc, "previously declared as an array %s",
+ curparmstr.c_str ());
+ continue;
+ }
+ }
+
+ if (!new_vla_p && cur_vla_p)
+ {
+ if (warning_at (newloc, OPT_Wvla_parameter,
+ "argument %u of type %s declared "
+ "as an ordinary array",
+ parmpos + 1, newparmstr.c_str ()))
+ inform (origloc,
+ "previously declared as a variable length array %s",
+ curparmstr.c_str ());
+ continue;
+ }
+
+ /* Move on to the next pair of parameters if both of the current
+ pair are VLAs with a single variable bound that refers to
+ a parameter at the same position. */
+ if (newa->size && cura->size
+ && newa->sizarg != UINT_MAX
+ && newa->sizarg == cura->sizarg
+ && newa->minsize == cura->minsize
+ && !TREE_CHAIN (newa->size) && !TREE_CHAIN (cura->size))
+ continue;
+
+ if (newa->size || cura->size)
+ {
+ unsigned newunspec, curunspec;
+ unsigned newbnds = newa->vla_bounds (&newunspec) + newunspec;
+ unsigned curbnds = cura->vla_bounds (&curunspec) + curunspec;
+
+ if (newbnds != curbnds)
+ {
+ if (warning_n (newloc, OPT_Wvla_parameter, newbnds,
+ "argument %u of type %s declared with "
+ "%u variable bound",
+ "argument %u of type %s declared with "
+ "%u variable bounds",
+ parmpos + 1, newparmstr.c_str (),
+ newbnds))
+ inform_n (origloc, curbnds,
+ "previously declared as %s with %u variable bound",
+ "previously declared as %s with %u variable bounds",
+ curparmstr.c_str (), curbnds);
+ continue;
+ }
+
+ if (newunspec != curunspec)
+ {
+ 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 %s declared with "
+ "%u unspecified variable bounds",
+ 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;
+ }
+ }
+
+ /* Iterate over the lists of VLA variable bounds, comparing each
+ pair for equality, and diagnosing mismatches. The case of
+ the lists having different lengths is handled above so at
+ this point they do . */
+ for (tree newvbl = newa->size, curvbl = cura->size; newvbl;
+ newvbl = TREE_CHAIN (newvbl), curvbl = TREE_CHAIN (curvbl))
+ {
+ tree newpos = TREE_PURPOSE (newvbl);
+ tree curpos = TREE_PURPOSE (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
+ the same positional parameter or when neither does,
+ and both are the same expression they are necessarily
+ the same. */
+ continue;
+
+ const char* const newbndstr =
+ newbnd ? print_generic_expr_to_str (newbnd) : "*";
+ const char* const curbndstr =
+ curbnd ? print_generic_expr_to_str (curbnd) : "*";
+
+ if (!newpos != !curpos
+ || (newpos && !tree_int_cst_equal (newpos, curpos)))
+ {
+ /* Diagnose a mismatch between a specified VLA bound and
+ an unspecified one. This can only happen in the most
+ significant bound.
+
+ Distinguish between the common case of bounds that are
+ other function parameters such as in
+ f (int n, int[n]);
+ and others. */
+
+ gcc_rich_location richloc (newloc);
+ bool warned;
+ if (newpos)
+ {
+ /* Also underline the VLA bound argument. */
+ richloc.add_range (DECL_SOURCE_LOCATION (newbnd));
+ warned = warning_at (&richloc, OPT_Wvla_parameter,
+ "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 %s declared "
+ "with mismatched bound %<%s%>",
+ parmpos + 1, newparmstr.c_str (),
+ newbndstr);
+
+ if (warned)
+ {
+ gcc_rich_location richloc (origloc);
+ if (curpos)
+ {
+ /* Also underline the VLA bound argument. */
+ richloc.add_range (DECL_SOURCE_LOCATION (curbnd));
+ inform (&richloc, "previously declared as %s with "
+ "bound argument %E",
+ curparmstr.c_str (), plus_one (curpos));
+ }
+ else
+ inform (&richloc, "previously declared as %s with bound "
+ "%<%s%>", curparmstr.c_str (), curbndstr);
+
+ continue;
+ }
+ }
+
+ if (!newpos && newbnd && curbnd)
+ {
+ /* The VLA bounds don't refer to other function parameters.
+ Compare them lexicographically to detect gross mismatches
+ such as between T[foo()] and T[bar()]. */
+ if (operand_equal_p (newbnd, curbnd, OEP_LEXICOGRAPHIC))
+ continue;
+
+ if (warning_at (newloc, OPT_Wvla_parameter,
+ "argument %u of type %s declared with "
+ "mismatched bound %<%s%>",
+ parmpos + 1, newparmstr.c_str (), newbndstr))
+ inform (origloc, "previously declared as %s with bound %qs",
+ curparmstr.c_str (), curbndstr);
+ continue;
+ }
+ }
+
+ if (newa->minsize == cura->minsize
+ || (((newa->minsize == 0 && newa->mode != access_deferred)
+ || (cura->minsize == 0 && cura->mode != access_deferred))
+ && newa != &ptr_spec
+ && cura != &ptr_spec))
+ continue;
+
+ if (!newa->static_p && !cura->static_p && warn_array_parameter < 2)
+ /* Avoid warning about mismatches in ordinary (non-static) arrays
+ at levels below 2. */
+ continue;
+
+ if (warning_at (newloc, OPT_Warray_parameter_,
+ "argument %u of type %s with mismatched bound",
+ parmpos + 1, newparmstr.c_str ()))
+ inform (origloc, "previously declared as %s", curparmstr.c_str ());
+ }
+}