diff options
author | Martin Sebor <msebor@redhat.com> | 2020-09-19 17:21:52 -0600 |
---|---|---|
committer | Martin Sebor <msebor@redhat.com> | 2020-09-19 17:34:31 -0600 |
commit | 6450f07388f9fe575a489c9309c36012b17b88b0 (patch) | |
tree | 929351afdf436f119f15a3d37da96d3a0e3ae27c /gcc/c-family | |
parent | 3696a50beeb73f4ded8a584e76ee16f0bde109b9 (diff) | |
download | gcc-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.c | 579 | ||||
-rw-r--r-- | gcc/c-family/c-common.h | 5 | ||||
-rw-r--r-- | gcc/c-family/c-pretty-print.c | 61 | ||||
-rw-r--r-- | gcc/c-family/c-warn.c | 561 | ||||
-rw-r--r-- | gcc/c-family/c.opt | 12 |
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 (¬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 + (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. |