Patch 1 adds the basic infrastructure to support array/VLA bounds in attribute access. It extends the access string specification to describe function parameters of array types (including VLAs), extends the attr_access class to parse the string and store the data in a form that's easy to work with, and implements checking of various kinds f mismatches between redeclarations. It doesn't actually enable anything new so no new tests are added.
[1/5] - Infrastructure to detect out-of-bounds accesses to array parameters.
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. * tree-ssa-uninit.c (maybe_warn_pass_by_reference): Same. 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. * c-warn.c (parm_array_as_string): Define new function. (plus_one): Define new function. (warn_parm_array_mismatch): Define new function. gcc/testsuite/ChangeLog: PR c/50584 * gcc.dg/attr-access-read-write-2.c: Adjust text of expected messages. diff --git a/gcc/attribs.c b/gcc/attribs.c index 71dae123af8..3e951a4751c 100644 --- a/gcc/attribs.c +++ b/gcc/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" @@ -25,6 +26,7 @@ along with GCC; see the file COPYING3. If not see #include "stringpool.h" #include "diagnostic-core.h" #include "attribs.h" +#include "fold-const.h" #include "stor-layout.h" #include "langhooks.h" #include "plugin.h" @@ -32,6 +34,7 @@ along with GCC; see the file COPYING3. If not see #include "hash-set.h" #include "diagnostic.h" #include "pretty-print.h" +#include "tree-pretty-print.h" #include "intl.h" /* Table of the tables of attributes (common, language, format, machine) @@ -707,10 +710,16 @@ decl_attributes (tree *node, tree attributes, int flags, int cxx11_flag = (cxx11_attr_p ? ATTR_FLAG_CXX11 : 0); /* Pass in an array of the current declaration followed - by the last pushed/merged declaration if one exists. + by the last pushed/merged declaration if one exists. + For calls that modify the type attributes of a DECL + and for which *ANODE is *NODE's type, also pass in + the DECL as the third element to use in diagnostics. If the handler changes CUR_AND_LAST_DECL[0] replace *ANODE with its value. */ - tree cur_and_last_decl[] = { *anode, last_decl }; + tree cur_and_last_decl[3] = { *anode, last_decl }; + if (anode != node && DECL_P (*node)) + cur_and_last_decl[2] = *node; + tree ret = (spec->handler) (cur_and_last_decl, name, args, flags|cxx11_flag, &no_add_attrs); @@ -2017,65 +2026,323 @@ maybe_diag_alias_attributes (tree alias, tree target) } } -/* Initialize a mapping for a call to function FNDECL declared with - attribute access. Each attribute positional operand inserts one - entry into the mapping with the operand number as the key. */ +/* Initialize a mapping RWM for a call to a function declared with + attribute access in ATTRS. Each attribute positional operand + inserts one entry into the mapping with the operand number as + the key. */ void -init_attr_rdwr_indices (rdwr_map *rwm, tree fntype) +init_attr_rdwr_indices (rdwr_map *rwm, tree attrs) { - if (!fntype) + if (!attrs) return; - for (tree access = TYPE_ATTRIBUTES (fntype); + for (tree access = attrs; (access = lookup_attribute ("access", access)); access = TREE_CHAIN (access)) { /* The TREE_VALUE of an attribute is a TREE_LIST whose TREE_VALUE is the attribute argument's value. */ tree mode = TREE_VALUE (access); - gcc_assert (TREE_CODE (mode) == TREE_LIST); + if (!mode) + return; + + /* The (optional) list of VLA bounds. */ + tree vblist = TREE_CHAIN (mode); + mode = TREE_VALUE (mode); + if (TREE_CODE (mode) != STRING_CST) + continue; gcc_assert (TREE_CODE (mode) == STRING_CST); - const char *modestr = TREE_STRING_POINTER (mode); - for (const char *m = modestr; *m; ) + for (const char *m = TREE_STRING_POINTER (mode); *m; ) { attr_access acc = { }; - switch (*m) + /* Skip the internal-only plus sign. */ + if (*m == '+') + ++m; + + acc.str = m; + acc.mode = acc.from_mode_char (*m); + acc.sizarg = UINT_MAX; + + const char *end; + acc.ptrarg = strtoul (++m, const_cast<char**>(&end), 10); + m = end; + + if (*m == '[') { - case 'r': acc.mode = acc.read_only; break; - case 'w': acc.mode = acc.write_only; break; - case 'x': acc.mode = acc.read_write; break; - case '-': acc.mode = acc.none; break; - default: gcc_unreachable (); + /* Forms containing the square bracket are internal-only + (not specified by an attribute declaration), and used + for various forms of array and VLA parameters. */ + acc.internal_p = true; + + /* Search to the closing bracket and look at the preceding + code: it determines the form of the most significant + bound of the array. Others prior to it encode the form + of interior VLA bounds. They're not of interest here. */ + end = strchr (m, ']'); + const char *p = end; + gcc_assert (p); + + while (ISDIGIT (p[-1])) + --p; + + if (ISDIGIT (*p)) + { + /* A digit denotes a constant bound (as in T[3]). */ + acc.static_p = p[-1] == 's'; + acc.minsize = strtoull (p, NULL, 10); + } + else if (' ' == p[-1]) + { + /* A space denotes an ordinary array of unspecified bound + (as in T[]). */ + acc.minsize = 0; + } + else if ('*' == p[-1] || '$' == p[-1]) + { + /* An asterisk denotes a VLA. When the closing bracket + is followed by a comma and a dollar sign its bound is + on the list. Otherwise it's a VLA with an unspecified + bound. */ + acc.minsize = HOST_WIDE_INT_M1U; + } + + m = end + 1; } - char *end; - acc.ptrarg = strtoul (++m, &end, 10); - m = end; if (*m == ',') { - acc.sizarg = strtoul (++m, &end, 10); - m = end; + ++m; + do + { + if (*m == '$') + { + ++m; + if (!acc.size) + { + /* Extract the list of VLA bounds for the current + parameter, store it in ACC.SIZE, and advance + to the list of bounds for the next VLA parameter. + */ + acc.size = TREE_VALUE (vblist); + vblist = TREE_CHAIN (vblist); + } + } + + if (ISDIGIT (*m)) + { + /* Extract the positional argument. It's absent + for VLAs whose bound doesn't name a function + parameter. */ + unsigned pos = strtoul (m, const_cast<char**>(&end), 10); + if (acc.sizarg == UINT_MAX) + acc.sizarg = pos; + m = end; + } + } + while (*m == '$'); } - else - acc.sizarg = UINT_MAX; - acc.ptr = NULL_TREE; - acc.size = NULL_TREE; + acc.end = m; + + bool existing; + auto &ref = rwm->get_or_insert (acc.ptrarg, &existing); + if (existing) + { + /* Merge the new spec with the existing. */ + if (acc.minsize == HOST_WIDE_INT_M1U) + ref.minsize = HOST_WIDE_INT_M1U; + + if (acc.sizarg != UINT_MAX) + ref.sizarg = acc.sizarg; + + if (acc.mode) + ref.mode = acc.mode; + } + else + ref = acc; /* Unconditionally add an entry for the required pointer operand of the attribute, and one for the optional size operand when it's specified. */ - rwm->put (acc.ptrarg, acc); if (acc.sizarg != UINT_MAX) rwm->put (acc.sizarg, acc); } } } +/* Return the access specification for a function parameter PARM + or null if the current function has no such specification. */ + +attr_access * +get_parm_access (rdwr_map &rdwr_idx, tree parm, + tree fndecl /* = current_function_decl */) +{ + tree fntype = TREE_TYPE (fndecl); + init_attr_rdwr_indices (&rdwr_idx, TYPE_ATTRIBUTES (fntype)); + + if (rdwr_idx.is_empty ()) + return NULL; + + unsigned argpos = 0; + tree fnargs = DECL_ARGUMENTS (fndecl); + for (tree arg = fnargs; arg; arg = TREE_CHAIN (arg), ++argpos) + if (arg == parm) + return rdwr_idx.get (argpos); + + return NULL; +} + +/* Return the internal representation as STRING_CST. Internal positional + arguments are zero-based. */ + +tree +attr_access::to_internal_string () const +{ + return build_string (end - str, str); +} + +/* Return the human-readable representation of the external attribute + specification (as it might appear in the source code) as STRING_CST. + External positional arguments are one-based. */ + +tree +attr_access::to_external_string () const +{ + char buf[80]; + gcc_assert (mode != deferred); + int len = snprintf (buf, sizeof buf, "access (%s, %u", + mode_names[mode], ptrarg + 1); + if (sizarg != UINT_MAX) + len += snprintf (buf + len, sizeof buf - len, ", %u", sizarg + 1); + strcpy (buf + len, ")"); + return build_string (len + 2, buf); +} + +/* Return the number of specified VLA bounds and set *nunspec to + the number of unspecified ones (those designated by [*]). */ + +unsigned +attr_access::vla_bounds (unsigned *nunspec) const +{ + *nunspec = 0; + for (const char* p = strrchr (str, ']'); p && *p != '['; --p) + if (*p == '*') + ++*nunspec; + return list_length (size); +} + + +/* Defined in attr_access. */ +constexpr char attr_access::mode_chars[]; +constexpr char attr_access::mode_names[][11]; + +/* Format an array, including a VLA, pointed to by TYPE and used as + a function parameter as a human-readable string. ACC describes + an access to the parameter and is used to determine the outermost + form of the array including its bound which is otherwise obviated + by its decay to pointer. Return the formatted string. */ + +std::string +attr_access::array_as_string (tree type) const +{ + std::string str; + + if (type == error_mark_node) + return std::string (); + + bool restrict_p = TYPE_RESTRICT (type); + if (TREE_CODE (type) == POINTER_TYPE) + type = TREE_TYPE (type); + + if (this->str == NULL) + str = "*"; + else + { + str = '['; + if (restrict_p) + str += "restrict "; + if (this->minsize == 0) + ; /* Empty brackets. */ + else if (this->minsize == HOST_WIDE_INT_M1U) + { + const char *p = strrchr (this->str, ']'); + if (p && p[-1] == '$' && this->size) + { + tree bound = TREE_VALUE (this->size); + const char *bndstr = print_generic_expr_to_str (bound); + str += bndstr; + } + else + str += '*'; + } + else + { + char buf[22]; + sprintf (buf, "%llu", (unsigned long long)this->minsize); + if (this->static_p) + str += "static "; + str += buf; + } + + if (!str.empty () && str.end ()[-1] == ' ') + /* Strip any trailing space. */ + str.resize (str.length () - 1); + str += ']'; + } + + for (; TREE_CODE (type) == ARRAY_TYPE; type = TREE_TYPE (type)) + { + tree dom = TYPE_DOMAIN (type); + if (!dom) + { + str += "[*]"; + continue; + } + + tree max = TYPE_MAX_VALUE (dom); + if (!max) + { + str += "[*]"; + continue; + } + + const char *maxstr; + if (TREE_CODE (max) == INTEGER_CST) + { + tree t = TREE_TYPE (max); + max = fold_build2 (PLUS_EXPR, t, max, build_int_cst (t, 1)); + maxstr = print_generic_expr_to_str (max); + } + else + { + if (TREE_CODE (max) == NOP_EXPR) + max = TREE_OPERAND (max, 0); + if (TREE_CODE (max) == PLUS_EXPR + && TREE_CODE (TREE_OPERAND (max, 1)) == INTEGER_CST + && integer_all_onesp (TREE_OPERAND (max, 1))) + max = TREE_OPERAND (max, 0); + if (TREE_CODE (max) == NOP_EXPR) + max = TREE_OPERAND (max, 0); + if (TREE_CODE (max) == SAVE_EXPR) + max = TREE_OPERAND (max, 0); + maxstr = print_generic_expr_to_str (max); + } + + str += '['; + str += maxstr; + str += ']'; + } + + pretty_printer pp; + dump_generic_node (&pp, type, 0, TDF_VOPS|TDF_MEMSYMS, false); + const char *eltypestr = pp_formatted_text (&pp); + str.insert (str.begin (), eltypestr, eltypestr + strlen (eltypestr)); + return str; +} #if CHECKING_P diff --git a/gcc/attribs.h b/gcc/attribs.h index dea0b6c44e6..d86672795fe 100644 --- a/gcc/attribs.h +++ b/gcc/attribs.h @@ -224,20 +224,91 @@ lookup_attribute_by_prefix (const char *attr_name, tree list) struct attr_access { + /* The beginning and end of the internal string representation. */ + const char *str, *end; /* The attribute pointer argument. */ tree ptr; - /* The size of the pointed-to object or NULL when not specified. */ + /* For a declaration, a TREE_CHAIN of VLA bound expressions stored + in TREE_VALUE and their positions in the argument list (stored + in TREE_PURPOSE). Each expression may be a PARM_DECL or some + other DECL (for ordinary variables), or an EXPR for other + expressions (e.g., funcion calls). */ tree size; - /* The zero-based number of each of the formal function arguments. */ + /* The zero-based position of each of the formal function arguments. + For the optional SIZARG, UINT_MAX when not specified. For VLAs + with multiple variable bounds, SIZARG is the position corresponding + to the most significant bound in the argument list. Positions of + subsequent bounds are in the TREE_PURPOSE field of the SIZE chain. */ unsigned ptrarg; unsigned sizarg; + /* For internal specifications only, the constant minimum size of + the array, zero if not specified, and HWI_M1U for the unspecified + VLA [*] notation. Meaningless for external (explicit) access + specifications. */ + unsigned HOST_WIDE_INT minsize; /* The access mode. */ - enum access_mode { none, read_only, write_only, read_write }; + enum access_mode + { + none = 0, + read_only = 1, + write_only = 2, + read_write = read_only | write_only, + /* In an internal representation defers to the presence of + the const qualifier (treated as likely read_only) or to + an external/explicit specification of the attribute. */ + deferred + }; access_mode mode; + + /* Set for an attribute added internally rather than by an explicit + declaration. */ + bool internal_p; + /* Set for the T[static MINSIZE] array notation for nonzero MINSIZE + less than HWI_M1U. */ + bool static_p; + + /* Return the number of specified VLA bounds. */ + unsigned vla_bounds (unsigned *) const; + + /* Return internal representation as STRING_CST. */ + tree to_internal_string () const; + + /* Return the human-readable representation of the external attribute + specification (as it might appear in the source code) as STRING_CST. */ + tree to_external_string () const; + + /* Return argument of array type formatted as a readable string. */ + std::string array_as_string (tree) const; + + /* Return the access mode corresponding to the character code. */ + static access_mode from_mode_char (char); + + /* The character codes corresponding to all the access modes. */ + static constexpr char mode_chars[5] = { '-', 'r', 'w', 'x', '^' }; + + /* The strings corresponding to just the external access modes. */ + static constexpr char mode_names[4][11] = + { + "none", "read_only", "write_only", "read_write" + }; }; +inline attr_access::access_mode +attr_access::from_mode_char (char c) +{ + switch (c) + { + case mode_chars[none]: return none; + case mode_chars[read_only]: return read_only; + case mode_chars[write_only]: return write_only; + case mode_chars[read_write]: return read_write; + case mode_chars[deferred]: return deferred; + } + gcc_unreachable (); +} + /* Used to define rdwr_map below. */ struct rdwr_access_hash: int_hash<int, -1> { }; @@ -247,5 +318,7 @@ struct attr_access; typedef hash_map<rdwr_access_hash, attr_access> rdwr_map; extern void init_attr_rdwr_indices (rdwr_map *, tree); +extern attr_access *get_parm_access (rdwr_map &, tree, + tree = current_function_decl); #endif // GCC_ATTRIBS_H diff --git a/gcc/c-family/c-attribs.c b/gcc/c-family/c-attribs.c index 37214831538..452600cbfc6 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, @@ -3035,8 +3042,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), @@ -3822,7 +3843,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; } } @@ -3830,143 +3852,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 != attr_access::deferred) + str = newa->to_external_string (); + else if (cura->mode != attr_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 != attr_access::deferred + && cura->mode != attr_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 == attr_access::deferred + && cura->mode != attr_access::deferred + && newa->sizarg == UINT_MAX + && cura->sizarg != UINT_MAX) + continue; - if (ISDIGIT (pos[-1]) || ISDIGIT (pos[n1 -1])) + if (cura->mode == attr_access::deferred + && newa->mode != attr_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 == attr_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; @@ -3984,9 +4134,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; } @@ -4017,16 +4190,28 @@ 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 attr_access::access_mode mode = + static_cast<attr_access::access_mode>(imode); if (funcall) { @@ -4149,7 +4334,7 @@ handle_access_attribute (tree *node, tree name, tree args, } } - if (read_write || write_only) + if (mode == attr_access::read_write || mode == attr_access::write_only) { /* Read_write and write_only modes must reference non-const arguments. */ @@ -4182,35 +4367,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[attr_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-warn.c b/gcc/c-family/c-warn.c index c32d8228b5c..fea8885bf35 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,340 @@ 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)); +} + +/* 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 both are empty there's nothing to do. If at least one isn't + empty there may be mismatches, such as between f(T*) and f(T[1]), + where the former mapping woud be empty. */ + if (cur_idx.is_empty () && new_idx.is_empty ()) + return; + + /* 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 %qs declared as " + "a variable length array", + parmpos + 1, newparmstr.c_str ())) + inform (origloc, + (cura == &ptr_spec + ? G_("previously declared as a pointer %qs") + : G_("previously declared as an ordinary array %qs")), + 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 %qs declared " + "as a pointer", + parmpos + 1, newparmstr.c_str ())) + inform (origloc, + "previously declared as a variable length array %qs", + 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 %qs declared " + "as a pointer", + parmpos + 1, newparmstr.c_str ())) + inform (origloc, "previously declared as an array %qs", + curparmstr.c_str ()); + continue; + } + } + + if (!new_vla_p && cur_vla_p) + { + if (warning_at (newloc, OPT_Wvla_parameter, + "argument %u of type %qs declared " + "as an ordinary array", + parmpos + 1, newparmstr.c_str ())) + inform (origloc, + "previously declared as a variable length array %qs", + 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 %qs declared with " + "%u variable bound", + "argument %u of type %qs declared with " + "%u variable bounds", + parmpos + 1, newparmstr.c_str (), + newbnds)) + inform_n (origloc, curbnds, + "previously declared as %qs with %u variable bound", + "previously declared as %qs with %u variable bounds", + curparmstr.c_str (), curbnds); + continue; + } + + if (newunspec != curunspec) + { + if (warning_n (newloc, OPT_Wvla_parameter, newunspec, + "argument %u of type %qs declared with " + "%u unspecified variable bound", + "argument %u of type %qs declared with " + "%u unspecified variable bounds", + parmpos + 1, newparmstr.c_str (), newunspec)) + inform_n (origloc, curunspec, + "previously declared as %qs with %u unspecified " + "variable bound", + "previously declared as %qs with %u unspecified " + "variable bounds", + curparmstr.c_str (), curunspec); + 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 = TREE_VALUE (newvbl); + tree curbnd = 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 %qs declared " + "with mismatched bound argument %E", + parmpos + 1, newparmstr.c_str (), + plus_one (newpos)); + } + else + warned = warning_at (&richloc, OPT_Wvla_parameter, + "argument %u of type %qs declared " + "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 %qs with " + "bound argument %E", + curparmstr.c_str (), plus_one (curpos)); + } + else + inform (&richloc, "previously declared as %qs 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 %qs declared with " + "mismatched bound %<%s%>", + parmpos + 1, newparmstr.c_str (), + newbndstr)) + inform (origloc, "previously declared as %qs with bound %qs", + curparmstr.c_str (), curbndstr); + continue; + } + } + + if (newa->minsize == cura->minsize + || (((newa->minsize == 0 && newa->mode != attr_access::deferred) + || (cura->minsize == 0 && cura->mode != attr_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 %qs with mismatched bound", + parmpos + 1, newparmstr.c_str ())) + inform (origloc, "previously declared as %qs", curparmstr.c_str ()); + } +} diff --git a/gcc/calls.c b/gcc/calls.c index 72f89011584..400dd4fbc92 100644 --- a/gcc/calls.c +++ b/gcc/calls.c @@ -2210,13 +2210,13 @@ initialize_argument_information (int num_actuals ATTRIBUTE_UNUSED, bitmap_obstack_release (NULL); + tree fntypeattrs = TYPE_ATTRIBUTES (fntype); /* Extract attribute alloc_size from the type of the called expression (which could be a function or a function pointer) and if set, store the indices of the corresponding arguments in ALLOC_IDX, and then the actual argument(s) at those indices in ALLOC_ARGS. */ int alloc_idx[2] = { -1, -1 }; - if (tree alloc_size = lookup_attribute ("alloc_size", - TYPE_ATTRIBUTES (fntype))) + if (tree alloc_size = lookup_attribute ("alloc_size", fntypeattrs)) { tree args = TREE_VALUE (alloc_size); alloc_idx[0] = TREE_INT_CST_LOW (TREE_VALUE (args)) - 1; @@ -2229,7 +2229,7 @@ initialize_argument_information (int num_actuals ATTRIBUTE_UNUSED, /* Map of attribute accewss specifications for function arguments. */ rdwr_map rdwr_idx; - init_attr_rdwr_indices (&rdwr_idx, fntype); + init_attr_rdwr_indices (&rdwr_idx, fntypeattrs); /* I counts args in order (to be) pushed; ARGPOS counts in order written. */ for (argpos = 0; argpos < num_actuals; i--, argpos++) diff --git a/gcc/testsuite/gcc.dg/attr-access-read-write-2.c b/gcc/testsuite/gcc.dg/attr-access-read-write-2.c index c2ac6c344a5..deeee736eb8 100644 --- a/gcc/testsuite/gcc.dg/attr-access-read-write-2.c +++ b/gcc/testsuite/gcc.dg/attr-access-read-write-2.c @@ -22,12 +22,12 @@ int RW (1) grdwr1_wr1 (void*, void*); // { dg-message "previous declar int WO (1) grdwr1_wr1 (void*, void*); // { dg-warning "attribute 'access\\(write_only, 1\\)' mismatch with mode 'read_write'" } -int RW (1) RW (1, 2) frdwr1_rdwr1_1 (void*, int); // { dg-warning "attribute 'access\\(read_write, 1, 2\\)' positional argument 2 conflicts with previous designation" } +int RW (1) RW (1, 2) frdwr1_rdwr1_1 (void*, int); // { dg-warning "attribute 'access\\(read_write, 1, 2\\)' positional argument 2 missing in previous designation" } int RW (1, 2) RW (1) frdwr1_1_rdwr1 (void*, int); // { dg-warning "attribute 'access\\(read_write, 1\\)' missing positional argument 2 provided in previous designation" } int RW (1) grdwr1_rdwr1_1 (void*, int); // { dg-message "previous declaration here" } -int RW (1, 2) grdwr1_rdwr1_1 (void*, int); // { dg-warning "attribute 'access\\(read_write, 1, 2\\)' positional argument 2 conflicts with previous designation" } +int RW (1, 2) grdwr1_rdwr1_1 (void*, int); // { dg-warning "attribute 'access\\(read_write, 1, 2\\)' positional argument 2 missing in previous designation" } typedef int *P; diff --git a/gcc/tree-ssa-uninit.c b/gcc/tree-ssa-uninit.c index 2f0ff724cde..a1ea1b6c532 100644 --- a/gcc/tree-ssa-uninit.c +++ b/gcc/tree-ssa-uninit.c @@ -472,7 +472,7 @@ maybe_warn_pass_by_reference (gimple *stmt, wlimits &wlims) /* Map of attribute access specifications for function arguments. */ rdwr_map rdwr_idx; - init_attr_rdwr_indices (&rdwr_idx, fntype); + init_attr_rdwr_indices (&rdwr_idx, TYPE_ATTRIBUTES (fntype)); tree argtype; unsigned argno = 0;