[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.
@@ -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
@@ -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
@@ -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. */
@@ -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 ());
+ }
+}
@@ -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++)
@@ -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;
@@ -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;