aboutsummaryrefslogtreecommitdiff
path: root/gcc/c-family
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
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')
-rw-r--r--gcc/c-family/c-attribs.c579
-rw-r--r--gcc/c-family/c-common.h5
-rw-r--r--gcc/c-family/c-pretty-print.c61
-rw-r--r--gcc/c-family/c-warn.c561
-rw-r--r--gcc/c-family/c.opt12
5 files changed, 1079 insertions, 139 deletions
diff --git a/gcc/c-family/c-attribs.c b/gcc/c-family/c-attribs.c
index 8c898ad..70b0003 100644
--- a/gcc/c-family/c-attribs.c
+++ b/gcc/c-family/c-attribs.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"
@@ -45,6 +46,7 @@ along with GCC; see the file COPYING3. If not see
#include "opts.h"
#include "gimplify.h"
#include "tree-pretty-print.h"
+#include "gcc-rich-location.h"
static tree handle_packed_attribute (tree *, tree, tree, int, bool *);
static tree handle_nocommon_attribute (tree *, tree, tree, int, bool *);
@@ -136,6 +138,7 @@ static tree handle_target_clones_attribute (tree *, tree, tree, int, bool *);
static tree handle_optimize_attribute (tree *, tree, tree, int, bool *);
static tree ignore_attribute (tree *, tree, tree, int, bool *);
static tree handle_no_split_stack_attribute (tree *, tree, tree, int, bool *);
+static tree handle_argspec_attribute (tree *, tree, tree, int, bool *);
static tree handle_fnspec_attribute (tree *, tree, tree, int, bool *);
static tree handle_warn_unused_attribute (tree *, tree, tree, int, bool *);
static tree handle_returns_nonnull_attribute (tree *, tree, tree, int, bool *);
@@ -434,6 +437,10 @@ const struct attribute_spec c_common_attribute_table[] =
ignore_attribute, NULL },
{ "no_split_stack", 0, 0, true, false, false, false,
handle_no_split_stack_attribute, NULL },
+ /* For internal use only (marking of function arguments).
+ The name contains a space to prevent its usage in source code. */
+ { "arg spec", 1, -1, true, false, false, false,
+ handle_argspec_attribute, NULL },
/* For internal use (marking of builtins and runtime functions) only.
The name contains space to prevent its usage in source code. */
{ "fn spec", 1, 1, false, true, true, false,
@@ -3180,8 +3187,22 @@ handle_assume_aligned_attribute (tree *node, tree name, tree args, int,
return NULL_TREE;
}
-/* Handle a "fn spec" attribute; arguments as in
- struct attribute_spec.handler. */
+/* Handle the internal-only "arg spec" attribute. */
+
+static tree
+handle_argspec_attribute (tree *, tree, tree args, int, bool *)
+{
+ /* Verify the attribute has one or two arguments and their kind. */
+ gcc_assert (args && TREE_CODE (TREE_VALUE (args)) == STRING_CST);
+ for (tree next = TREE_CHAIN (args); next; next = TREE_CHAIN (next))
+ {
+ tree val = TREE_VALUE (next);
+ gcc_assert (DECL_P (val) || EXPR_P (val));
+ }
+ return NULL_TREE;
+}
+
+/* Handle the internal-only "fn spec" attribute. */
static tree
handle_fnspec_attribute (tree *node ATTRIBUTE_UNUSED, tree ARG_UNUSED (name),
@@ -3967,7 +3988,8 @@ get_argument_type (tree functype, unsigned argno, unsigned *nargs)
tree argtype = function_args_iter_cond (&iter);
if (VOID_TYPE_P (argtype))
break;
- return argtype;
+ if (argtype != error_mark_node)
+ return argtype;
}
}
@@ -3975,143 +3997,271 @@ get_argument_type (tree functype, unsigned argno, unsigned *nargs)
return NULL_TREE;
}
-/* Appends ATTRSTR to the access string in ATTRS if one is there
- or creates a new one and returns the concatenated access string. */
+/* Given a function FNDECL return the function argument at the zero-
+ based position ARGNO or null if it can't be found. */
static tree
-append_access_attrs (tree t, tree attrs, const char *attrstr,
- char code, HOST_WIDE_INT idxs[2])
+get_argument (tree fndecl, unsigned argno)
{
- char attrspec[80];
- int n1 = sprintf (attrspec, "%c%u", code, (unsigned) idxs[0] - 1);
- int n2 = 0;
- if (idxs[1])
- n2 = sprintf (attrspec + n1 + 1, "%u", (unsigned) idxs[1] - 1);
+ if (!DECL_P (fndecl))
+ return NULL_TREE;
- size_t newlen = n1 + n2 + !!n2;
- char *newspec = attrspec;
+ unsigned i = 0;
+ for (tree arg = DECL_ARGUMENTS (fndecl); arg; arg = TREE_CHAIN (arg))
+ if (i++ == argno)
+ return arg;
- if (tree acs = lookup_attribute ("access", attrs))
- {
- /* The TREE_VALUE of an attribute is a TREE_LIST whose TREE_VALUE
- is the attribute argument's value. */
- acs = TREE_VALUE (acs);
- gcc_assert (TREE_CODE (acs) == TREE_LIST);
- acs = TREE_VALUE (acs);
- gcc_assert (TREE_CODE (acs) == STRING_CST);
+ return NULL_TREE;
+}
- /* Check to make sure ATTRSPEC doesn't conflict with another
- access attribute specified in ATTRS by searching the access
- string in ATTRS for the position string formatted above into
- ATTRSPEC, and if it's found, that the two match. */
+/* Attempt to append attribute access specification ATTRSPEC, optionally
+ described by the human-readable string ATTRSTR, for type T, to one in
+ ATTRS. VBLIST is an optional list of bounds of variable length array
+ parameters described by ATTRSTR.
+ Issue warning for conflicts and return null if any are found.
+ Return the concatenated access string on success. */
- const char *posstr = attrspec + 1;
- const char *str = TREE_STRING_POINTER (acs);
- const char *pos = str;
- for ( ; ; pos += n1)
+static tree
+append_access_attr (tree node[3], tree attrs, const char *attrstr,
+ const char *attrspec, tree vblist = NULL_TREE)
+{
+ tree argstr = build_string (strlen (attrspec) + 1, attrspec);
+ tree ataccess = tree_cons (NULL_TREE, argstr, vblist);
+ ataccess = tree_cons (get_identifier ("access"), ataccess, NULL_TREE);
+
+ /* The access specification being applied. This may be an implicit
+ access spec synthesized for array (or VLA) parameters even for
+ a declaration with an explicit access spec already applied, if
+ this call corresponds to the first declaration of the function. */
+ rdwr_map new_idxs;
+ init_attr_rdwr_indices (&new_idxs, ataccess);
+
+ /* The current access specification alrady applied. */
+ rdwr_map cur_idxs;
+ init_attr_rdwr_indices (&cur_idxs, attrs);
+
+ std::string spec;
+ for (auto it = new_idxs.begin (); it != new_idxs.end (); ++it)
+ {
+ const auto &newaxsref = *it;
+
+ /* The map has two equal entries for each pointer argument that
+ has an associated size argument. Process just the entry for
+ the former. */
+ if ((unsigned)newaxsref.first != newaxsref.second.ptrarg)
+ continue;
+
+ const attr_access* const cura = cur_idxs.get (newaxsref.first);
+ if (!cura)
{
- pos = strstr (pos, posstr);
- if (!pos)
- break;
+ /* The new attribute needs to be added. */
+ tree str = newaxsref.second.to_internal_string ();
+ spec += TREE_STRING_POINTER (str);
+ continue;
+ }
+
+ /* The new access spec refers to an array/pointer argument for
+ which an access spec already exists. Check and diagnose any
+ conflicts. If no conflicts are found, merge the two. */
+ const attr_access* const newa = &newaxsref.second;
+
+ if (!attrstr)
+ {
+ tree str = NULL_TREE;
+ if (newa->mode != access_deferred)
+ str = newa->to_external_string ();
+ else if (cura->mode != access_deferred)
+ str = cura->to_external_string ();
+ if (str)
+ attrstr = TREE_STRING_POINTER (str);
+ }
+
+ location_t curloc = input_location;
+ if (node[2] && DECL_P (node[2]))
+ curloc = DECL_SOURCE_LOCATION (node[2]);
+
+ location_t prevloc = UNKNOWN_LOCATION;
+ if (node[1] && DECL_P (node[1]))
+ prevloc = DECL_SOURCE_LOCATION (node[1]);
+
+ if (newa->mode != cura->mode
+ && newa->mode != access_deferred
+ && cura->mode != access_deferred
+ && newa->internal_p == cura->internal_p)
+ {
+ /* Mismatch in access mode. */
+ auto_diagnostic_group d;
+ if (warning_at (curloc, OPT_Wattributes,
+ "attribute %qs mismatch with mode %qs",
+ attrstr, cura->mode_names[cura->mode])
+ && prevloc != UNKNOWN_LOCATION)
+ inform (prevloc, "previous declaration here");
+ continue;
+ }
+
+ /* Set if PTRARG refers to a VLA with an unspecified bound (T[*]).
+ Be prepared for either CURA or NEWA to refer to it, depending
+ on which happens to come first in the declaration. */
+ const bool cur_vla_ub = (cura->internal_p
+ && cura->sizarg == UINT_MAX
+ && cura->minsize == HOST_WIDE_INT_M1U);
+ const bool new_vla_ub = (newa->internal_p
+ && newa->sizarg == UINT_MAX
+ && newa->minsize == HOST_WIDE_INT_M1U);
+
+ if (newa->sizarg != cura->sizarg
+ && attrstr
+ && (!(cur_vla_ub ^ new_vla_ub)
+ || (!cura->internal_p && !newa->internal_p)))
+ {
+ /* Avoid diagnosing redeclarations of functions with no explicit
+ attribute access that add one. */
+ if (newa->mode == access_deferred
+ && cura->mode != access_deferred
+ && newa->sizarg == UINT_MAX
+ && cura->sizarg != UINT_MAX)
+ continue;
- if (ISDIGIT (pos[-1]) || ISDIGIT (pos[n1 -1]))
+ if (cura->mode == access_deferred
+ && newa->mode != access_deferred
+ && cura->sizarg == UINT_MAX
+ && newa->sizarg != UINT_MAX)
continue;
- /* Found a matching positional argument. */
- if (*attrspec != pos[-1])
+ /* The two specs designate different size arguments. It's okay
+ for the explicit spec to specify a size where none is provided
+ by the implicit (VLA) one, as in:
+ __attribute__ ((access (read_write, 1, 2)))
+ void f (int*, int);
+ but not for two explicit access attributes to do that. */
+ bool warned = false;
+
+ auto_diagnostic_group d;
+
+ if (newa->sizarg == UINT_MAX)
+ /* Mismatch in the presence of the size argument. */
+ warned = warning_at (curloc, OPT_Wattributes,
+ "attribute %qs missing positional argument 2 "
+ "provided in previous designation by argument "
+ "%u", attrstr, cura->sizarg + 1);
+ else if (cura->sizarg == UINT_MAX)
+ /* Mismatch in the presence of the size argument. */
+ warned = warning_at (curloc, OPT_Wattributes,
+ "attribute %qs positional argument 2 "
+ "missing in previous designation",
+ attrstr);
+ else if (newa->internal_p || cura->internal_p)
{
- const char* const modestr
- = (pos[-1] == 'r'
- ? "read_only"
- : (pos[-1] == 'w'
- ? "write_only"
- : (pos[-1] == 'x' ? "read_write" : "none")));
- /* Mismatch in access mode. */
- auto_diagnostic_group d;
- if (warning (OPT_Wattributes,
- "attribute %qs mismatch with mode %qs",
- attrstr, modestr)
- && DECL_P (t))
- inform (DECL_SOURCE_LOCATION (t),
- "previous declaration here");
- return NULL_TREE;
+ /* Mismatch in the value of the size argument and a VLA
+ bound. */
+ location_t argloc = curloc;
+ if (tree arg = get_argument (node[2], newa->sizarg))
+ argloc = DECL_SOURCE_LOCATION (arg);
+ warned = warning_at (argloc, OPT_Wattributes,
+ "attribute %qs positional argument 2 "
+ "conflicts with previous designation "
+ "by argument %u",
+ attrstr, cura->sizarg + 1);
}
-
- if ((n2 && pos[n1 - 1] != ','))
+ else
+ /* Mismatch in the value of the size argument between two
+ explicit access attributes. */
+ warned = warning_at (curloc, OPT_Wattributes,
+ "attribute %qs mismatched positional argument "
+ "values %i and %i",
+ attrstr, newa->sizarg + 1, cura->sizarg + 1);
+
+ if (warned)
{
- /* Mismatch in the presence of the size argument. */
- auto_diagnostic_group d;
- if (warning (OPT_Wattributes,
- "attribute %qs positional argument 2 conflicts "
- "with previous designation",
- attrstr)
- && DECL_P (t))
- inform (DECL_SOURCE_LOCATION (t),
- "previous declaration here");
- return NULL_TREE;
- }
+ /* If the previous declaration is a function (as opposed
+ to a typedef of one), find the location of the array
+ or pointer argument that uses the conflicting VLA bound
+ and point to it in the note. */
+ const attr_access* const pa = cura->size ? cura : newa;
+ tree size = pa->size ? TREE_VALUE (pa->size) : NULL_TREE;
+ if (size && DECL_P (size))
+ {
+ location_t argloc = UNKNOWN_LOCATION;
+ if (tree arg = get_argument (node[2], pa->ptrarg))
+ argloc = DECL_SOURCE_LOCATION (arg);
- if (!n2 && pos[n1 - 1] == ',')
- {
- /* Mismatch in the presence of the size argument. */
- auto_diagnostic_group d;
- if (warning (OPT_Wattributes,
- "attribute %qs missing positional argument 2 "
- "provided in previous designation",
- attrstr)
- && DECL_P (t))
- inform (DECL_SOURCE_LOCATION (t),
- "previous declaration here");
- return NULL_TREE;
- }
+ gcc_rich_location richloc (DECL_SOURCE_LOCATION (size));
+ if (argloc != UNKNOWN_LOCATION)
+ richloc.add_range (argloc);
- if (n2 && strncmp (attrspec + n1 + 1, pos + n1, n2))
- {
- /* Mismatch in the value of the size argument. */
- auto_diagnostic_group d;
- if (warning (OPT_Wattributes,
- "attribute %qs mismatched positional argument "
- "values %i and %i",
- attrstr, atoi (attrspec + n1 + 1) + 1,
- atoi (pos + n1) + 1)
- && DECL_P (t))
- inform (DECL_SOURCE_LOCATION (t),
- "previous declaration here");
- return NULL_TREE;
+ inform (&richloc, "designating the bound of variable "
+ "length array argument %u",
+ pa->ptrarg + 1);
+ }
+ else if (prevloc != UNKNOWN_LOCATION)
+ inform (prevloc, "previous declaration here");
}
- /* Avoid adding the same attribute specification. */
- return NULL_TREE;
+ continue;
}
- /* Connect the two substrings formatted above into a single one. */
- if (idxs[1])
- attrspec[n1] = ',';
+ if (newa->internal_p == cura->internal_p)
+ continue;
- size_t len = strlen (str);
- newspec = XNEWVEC (char, newlen + len + 1);
- strcpy (newspec, str);
- strcpy (newspec + len, attrspec);
- newlen += len;
+ /* Merge the CURA and NEWA. */
+ attr_access merged = newaxsref.second;
+
+ /* VLA seen in a declaration takes precedence. */
+ if (cura->minsize == HOST_WIDE_INT_M1U)
+ merged.minsize = HOST_WIDE_INT_M1U;
+
+ /* Use the explicitly specified size positional argument. */
+ if (cura->sizarg != UINT_MAX)
+ merged.sizarg = cura->sizarg;
+
+ /* Use the explicitly specified mode. */
+ if (merged.mode == access_deferred)
+ merged.mode = cura->mode;
+
+ tree str = merged.to_internal_string ();
+ spec += TREE_STRING_POINTER (str);
}
- else if (idxs[1])
- /* Connect the two substrings formatted above into a single one. */
- attrspec[n1] = ',';
- tree ret = build_string (newlen + 1, newspec);
- if (newspec != attrspec)
- XDELETEVEC (newspec);
- return ret;
+ if (!spec.length ())
+ return NULL_TREE;
+
+ return build_string (spec.length (), spec.c_str ());
}
-/* Handle the access attribute (read_only, write_only, and read_write). */
+/* Convenience wrapper for the above. */
+
+tree
+append_access_attr (tree node[3], tree attrs, const char *attrstr,
+ char code, HOST_WIDE_INT idxs[2])
+{
+ char attrspec[80];
+ int n = sprintf (attrspec, "%c%u", code, (unsigned) idxs[0] - 1);
+ if (idxs[1])
+ n += sprintf (attrspec + n, ",%u", (unsigned) idxs[1] - 1);
+
+ return append_access_attr (node, attrs, attrstr, attrspec);
+}
+
+/* Handle the access attribute for function type NODE[0], with the function
+ DECL optionally in NODE[1]. The handler is called both in response to
+ an explict attribute access on a declaration with a mode and one or two
+ positional arguments, and for internally synthesized access specifications
+ with a string argument optionally followd by a DECL or expression
+ representing a VLA bound. To speed up parsing, the handler transforms
+ the attribute and its arguments into a string. */
static tree
-handle_access_attribute (tree *node, tree name, tree args,
+handle_access_attribute (tree node[3], tree name, tree args,
int ARG_UNUSED (flags), bool *no_add_attrs)
{
+ tree attrs = TYPE_ATTRIBUTES (*node);
tree type = *node;
- tree attrs = TYPE_ATTRIBUTES (type);
+ if (POINTER_TYPE_P (type))
+ {
+ tree ptype = TREE_TYPE (type);
+ if (FUNC_OR_METHOD_TYPE_P (ptype))
+ type = ptype;
+ }
*no_add_attrs = true;
@@ -4129,9 +4279,32 @@ handle_access_attribute (tree *node, tree name, tree args,
tree access_mode = TREE_VALUE (args);
if (TREE_CODE (access_mode) == STRING_CST)
{
- /* This must be a recursive call to handle the condensed internal
- form of the attribute (see below). Since all validation has
- been done simply return here, accepting the attribute as is. */
+ const char* const str = TREE_STRING_POINTER (access_mode);
+ if (*str == '+')
+ {
+ /* This is a request to merge an internal specification for
+ a function declaration involving arrays but no explicit
+ attribute access. */
+ tree vblist = TREE_CHAIN (args);
+ tree axstr = append_access_attr (node, attrs, NULL, str + 1,
+ vblist);
+ if (!axstr)
+ return NULL_TREE;
+
+ /* Replace any existing access attribute specification with
+ the concatenation above. */
+ tree axsat = tree_cons (NULL_TREE, axstr, vblist);
+ axsat = tree_cons (name, axsat, NULL_TREE);
+
+ /* Recursively call self to "replace" the documented/external
+ form of the attribute with the condensend internal form. */
+ decl_attributes (node, axsat, flags);
+ return NULL_TREE;
+ }
+
+ /* This is a recursive call to handle the condensed internal form
+ of the attribute (see below). Since all validation has been
+ done simply return here, accepting the attribute as is. */
*no_add_attrs = false;
return NULL_TREE;
}
@@ -4162,16 +4335,27 @@ handle_access_attribute (tree *node, tree name, tree args,
ps += 2;
}
- const bool read_only = !strncmp (ps, "read_only", 9);
- const bool write_only = !strncmp (ps, "write_only", 10);
- const bool read_write = !strncmp (ps, "read_write", 10);
- if (!read_only && !write_only && !read_write && strncmp (ps, "none", 4))
- {
- error ("attribute %qE invalid mode %qs; expected one of "
- "%qs, %qs, %qs, or %qs", name, access_str,
- "read_only", "read_write", "write_only", "none");
- return NULL_TREE;
- }
+ int imode;
+
+ {
+ const int nmodes =
+ sizeof attr_access::mode_names / sizeof *attr_access::mode_names;
+
+ for (imode = 0; imode != nmodes; ++imode)
+ if (!strncmp (ps, attr_access::mode_names[imode],
+ strlen (attr_access::mode_names[imode])))
+ break;
+
+ if (imode == nmodes)
+ {
+ error ("attribute %qE invalid mode %qs; expected one of "
+ "%qs, %qs, %qs, or %qs", name, access_str,
+ "read_only", "read_write", "write_only", "none");
+ return NULL_TREE;
+ }
+ }
+
+ const ::access_mode mode = static_cast<::access_mode>(imode);
if (funcall)
{
@@ -4294,7 +4478,7 @@ handle_access_attribute (tree *node, tree name, tree args,
}
}
- if (read_write || write_only)
+ if (mode == access_read_write || mode == access_write_only)
{
/* Read_write and write_only modes must reference non-const
arguments. */
@@ -4327,35 +4511,164 @@ handle_access_attribute (tree *node, tree name, tree args,
/* Verify that the new attribute doesn't conflict with any existing
attributes specified on previous declarations of the same type
and if not, concatenate the two. */
- const char code
- = read_only ? 'r' : write_only ? 'w' : read_write ? 'x' : '-';
- tree new_attrs = append_access_attrs (node[0], attrs, attrstr, code, idxs);
+ const char code = attr_access::mode_chars[mode];
+ tree new_attrs = append_access_attr (node, attrs, attrstr, code, idxs);
if (!new_attrs)
return NULL_TREE;
/* Replace any existing access attribute specification with
the concatenation above. */
new_attrs = tree_cons (NULL_TREE, new_attrs, NULL_TREE);
- new_attrs = tree_cons (name, new_attrs, attrs);
+ new_attrs = tree_cons (name, new_attrs, NULL_TREE);
if (node[1])
{
/* Repeat for the previously declared type. */
attrs = TYPE_ATTRIBUTES (TREE_TYPE (node[1]));
- tree attrs1 = append_access_attrs (node[1], attrs, attrstr, code, idxs);
- if (!attrs1)
+ new_attrs = append_access_attr (node, attrs, attrstr, code, idxs);
+ if (!new_attrs)
return NULL_TREE;
- attrs1 = tree_cons (NULL_TREE, attrs1, NULL_TREE);
- new_attrs = tree_cons (name, attrs1, attrs);
+ new_attrs = tree_cons (NULL_TREE, new_attrs, NULL_TREE);
+ new_attrs = tree_cons (name, new_attrs, NULL_TREE);
}
/* Recursively call self to "replace" the documented/external form
- of the attribute with the condensend internal form. */
+ of the attribute with the condensed internal form. */
decl_attributes (node, new_attrs, flags);
return NULL_TREE;
}
+/* Extract attribute "arg spec" from each FNDECL argument that has it,
+ build a single attribute access corresponding to all the arguments,
+ and return the result. SKIP_VOIDPTR set to ignore void* parameters
+ (used for user-defined functions for which, unlike in for built-ins,
+ void* cannot be relied on to determine anything about the access
+ through it or whether it even takes place).
+
+ For example, the parameters in the declaration:
+
+ void f (int x, int y, char [x][1][y][3], char [y][2][y][5]);
+
+ result in the following attribute access:
+
+ value: "+^2[*],$0$1^3[*],$1$1"
+ chain: <0, x> <1, y>
+
+ where each <node> on the chain corresponds to one VLA bound for each
+ of the two parameters. */
+
+tree
+build_attr_access_from_parms (tree parms, bool skip_voidptr)
+{
+ /* Maps each named integral argument DECL seen so far to its position
+ in the argument list; used to associate VLA sizes with arguments. */
+ hash_map<tree, unsigned> arg2pos;
+
+ /* The string representation of the access specification for all
+ arguments. */
+ std::string spec;
+ unsigned argpos = 0;
+
+ /* A TREE_LIST of VLA bounds. */
+ tree vblist = NULL_TREE;
+
+ for (tree arg = parms; arg; arg = TREE_CHAIN (arg), ++argpos)
+ {
+ if (!DECL_P (arg))
+ continue;
+
+ tree argtype = TREE_TYPE (arg);
+ if (DECL_NAME (arg) && INTEGRAL_TYPE_P (argtype))
+ arg2pos.put (arg, argpos);
+
+ tree argspec = DECL_ATTRIBUTES (arg);
+ if (!argspec)
+ continue;
+
+ if (POINTER_TYPE_P (argtype))
+ {
+ /* void* arguments in user-defined functions could point to
+ anything; skip them. */
+ tree reftype = TREE_TYPE (argtype);
+ if (skip_voidptr && VOID_TYPE_P (reftype))
+ continue;
+ }
+
+ /* Each parameter should have at most one "arg spec" attribute. */
+ argspec = lookup_attribute ("arg spec", argspec);
+ if (!argspec)
+ continue;
+
+ /* Attribute arg spec should have one or two arguments. */
+ argspec = TREE_VALUE (argspec);
+
+ /* The attribute arg spec string. */
+ tree str = TREE_VALUE (argspec);
+ const char *s = TREE_STRING_POINTER (str);
+
+ /* Create the attribute access string from the arg spec string,
+ optionally followed by position of the VLA bound argument if
+ it is one. */
+ char specbuf[80];
+ int len = snprintf (specbuf, sizeof specbuf, "%c%u%s",
+ attr_access::mode_chars[access_deferred],
+ argpos, s);
+ gcc_assert ((size_t) len < sizeof specbuf);
+
+ if (!spec.length ())
+ spec += '+';
+
+ spec += specbuf;
+
+ /* The (optional) list of expressions denoting the VLA bounds
+ N in ARGTYPE <arg>[Ni]...[Nj]...[Nk]. */
+ tree argvbs = TREE_CHAIN (argspec);
+ if (argvbs)
+ {
+ spec += ',';
+ /* Add ARGVBS to the list. Their presence is indicated by
+ appending a comma followed by the dollar sign and, when
+ it corresponds to a function parameter, the position of
+ each bound Ni, so it can be distinguished from
+ an unspecified bound (as in T[*]). The list is in reverse
+ order of arguments and needs to be reversed to access in
+ order. */
+ vblist = tree_cons (NULL_TREE, argvbs, vblist);
+
+ unsigned nelts = 0;
+ for (tree vb = argvbs; vb; vb = TREE_CHAIN (vb), ++nelts)
+ {
+ tree bound = TREE_VALUE (vb);
+ if (const unsigned *psizpos = arg2pos.get (bound))
+ {
+ /* BOUND previously seen in the parameter list. */
+ TREE_PURPOSE (vb) = size_int (*psizpos);
+ sprintf (specbuf, "$%u", *psizpos);
+ spec += specbuf;
+ }
+ else
+ {
+ /* BOUND doesn't name a parameter (it could be a global
+ variable or an expression such as a function call). */
+ spec += '$';
+ }
+ }
+ }
+ }
+
+ if (!spec.length ())
+ return NULL_TREE;
+
+ /* Build a single attribute access with the string describing all
+ array arguments and an optional list of any non-parameter VLA
+ bounds in order. */
+ tree str = build_string (spec.length (), spec.c_str ());
+ tree attrargs = tree_cons (NULL_TREE, str, vblist);
+ tree name = get_identifier ("access");
+ return tree_cons (name, attrargs, NULL_TREE);
+}
+
/* Handle a "nothrow" attribute; arguments as in
struct attribute_spec.handler. */
diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h
index 6abfe4b..3d96092 100644
--- a/gcc/c-family/c-common.h
+++ b/gcc/c-family/c-common.h
@@ -1372,6 +1372,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
@@ -1426,6 +1427,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);
@@ -1454,8 +1457,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 c364e1c..acffd7b 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 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 ());
+ }
+}
diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt
index c1d8fd3..7a61351 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.
@@ -1257,6 +1265,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=<SIZE_MAX> 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.