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"
#include "stringpool.h"
#include "diagnostic-core.h"
#include "attribs.h"
+#include "fold-const.h"
#include "stor-layout.h"
#include "langhooks.h"
#include "plugin.h"
#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)
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);
}
}
-/* 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 = access_read_only; break;
- case 'w': acc.mode = access_write_only; break;
- case 'x': acc.mode = access_read_write; break;
- case '-': acc.mode = access_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 == '$');
+ }
+
+ 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
- acc.sizarg = UINT_MAX;
-
- acc.ptr = NULL_TREE;
- acc.size = NULL_TREE;
+ 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 != access_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 typstr;
+
+ if (type == error_mark_node)
+ return std::string ();
+
+ if (this->str)
+ {
+ /* For array parameters (but not pointers) create an array type
+ that corresponds to the form of the parameter including its
+ qualifiers even though they apply to the pointer, not the array
+ type. */
+ const bool vla_p = minsize == HOST_WIDE_INT_M1U;
+ tree eltype = TREE_TYPE (type);
+ tree artype;
+
+ tree index_type = NULL_TREE;
+ if (minsize == HOST_WIDE_INT_M1U)
+ {
+ /* Determine if this is a VLA (an array whose most significant
+ bound is nonconstant and whose access string has "$]" in it)
+ extract the bound expression from SIZE. */
+ const char *p = end;
+ for ( ; *p-- != ']'; );
+ if (*p == '$')
+ index_type = build_index_type (TREE_VALUE (size));
+ }
+ else if (minsize)
+ index_type = build_index_type (size_int (minsize - 1));
+
+ artype = build_array_type (eltype, index_type);
+
+ if (static_p || vla_p)
+ {
+ tree flag = static_p ? integer_one_node : NULL_TREE;
+ /* Hack: there's no language-independent way to encode
+ the "static" specifier or the "*" notation in an array type.
+ Temporarily add an attribute to have the pretty printer add
+ "static" or "*", and remove it later. The static notation
+ is only valid in the most significant bound but [*] can be
+ used for any bound. Because [*] is represented the same as
+ [0] this hack only works for the most significant bound like
+ static and the others are rendered as [0]. */
+ tree at = tree_cons (get_identifier ("array"), flag, NULL_TREE);
+ TYPE_ATTRIBUTES (artype) = at;
+ }
+
+ TYPE_ATOMIC (artype) = TYPE_ATOMIC (type);
+ TYPE_READONLY (artype) = TYPE_READONLY (type);
+ TYPE_RESTRICT (artype) = TYPE_RESTRICT (type);
+ TYPE_VOLATILE (artype) = TYPE_VOLATILE (type);
+ type = artype;
+ }
+
+ /* Format the type using the current pretty printer. The generic tree
+ printer does a terrible job. */
+ pretty_printer *pp = global_dc->printer->clone ();
+ pp_printf (pp, "%qT", type);
+ typstr = pp_formatted_text (pp);
+ delete pp;
+
+ if (this->str)
+ /* Remove the attribute that wasn't installed by decl_attributes. */
+ TYPE_ATTRIBUTES (type) = NULL_TREE;
+
+ return typstr;
+}
#if CHECKING_P
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. */
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 access_mode
+attr_access::from_mode_char (char c)
+{
+ switch (c)
+ {
+ case mode_chars[access_none]: return access_none;
+ case mode_chars[access_read_only]: return access_read_only;
+ case mode_chars[access_write_only]: return access_write_only;
+ case mode_chars[access_read_write]: return access_read_write;
+ case mode_chars[access_deferred]: return access_deferred;
+ }
+ gcc_unreachable ();
+}
+
/* Used to define rdwr_map below. */
struct rdwr_access_hash: int_hash<int, -1> { };
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
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"
#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 *);
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 *);
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,
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),
tree argtype = function_args_iter_cond (&iter);
if (VOID_TYPE_P (argtype))
break;
- return argtype;
+ if (argtype != error_mark_node)
+ return argtype;
}
}
return NULL_TREE;
}
-/* Appends ATTRSTR to the access string in ATTRS if one is there
- or creates a new one and returns the concatenated access string. */
+/* Given a function FNDECL return the function argument at the zero-
+ based position ARGNO or null if it can't be found. */
static tree
-append_access_attrs (tree t, tree attrs, const char *attrstr,
- char code, HOST_WIDE_INT idxs[2])
+get_argument (tree fndecl, unsigned argno)
{
- char attrspec[80];
- int n1 = sprintf (attrspec, "%c%u", code, (unsigned) idxs[0] - 1);
- int n2 = 0;
- if (idxs[1])
- n2 = sprintf (attrspec + n1 + 1, "%u", (unsigned) idxs[1] - 1);
+ if (!DECL_P (fndecl))
+ return NULL_TREE;
- size_t newlen = n1 + n2 + !!n2;
- char *newspec = attrspec;
+ unsigned i = 0;
+ for (tree arg = DECL_ARGUMENTS (fndecl); arg; arg = TREE_CHAIN (arg))
+ if (i++ == argno)
+ return arg;
- if (tree acs = lookup_attribute ("access", attrs))
- {
- /* The TREE_VALUE of an attribute is a TREE_LIST whose TREE_VALUE
- is the attribute argument's value. */
- acs = TREE_VALUE (acs);
- gcc_assert (TREE_CODE (acs) == TREE_LIST);
- acs = TREE_VALUE (acs);
- gcc_assert (TREE_CODE (acs) == STRING_CST);
+ return NULL_TREE;
+}
- /* Check to make sure ATTRSPEC doesn't conflict with another
- access attribute specified in ATTRS by searching the access
- string in ATTRS for the position string formatted above into
- ATTRSPEC, and if it's found, that the two match. */
+/* Attempt to append attribute access specification ATTRSPEC, optionally
+ described by the human-readable string ATTRSTR, for type T, to one in
+ ATTRS. VBLIST is an optional list of bounds of variable length array
+ parameters described by ATTRSTR.
+ Issue warning for conflicts and return null if any are found.
+ Return the concatenated access string on success. */
- const char *posstr = attrspec + 1;
- const char *str = TREE_STRING_POINTER (acs);
- const char *pos = str;
- for ( ; ; pos += n1)
+static tree
+append_access_attr (tree node[3], tree attrs, const char *attrstr,
+ const char *attrspec, tree vblist = NULL_TREE)
+{
+ tree argstr = build_string (strlen (attrspec) + 1, attrspec);
+ tree ataccess = tree_cons (NULL_TREE, argstr, vblist);
+ ataccess = tree_cons (get_identifier ("access"), ataccess, NULL_TREE);
+
+ /* The access specification being applied. This may be an implicit
+ access spec synthesized for array (or VLA) parameters even for
+ a declaration with an explicit access spec already applied, if
+ this call corresponds to the first declaration of the function. */
+ rdwr_map new_idxs;
+ init_attr_rdwr_indices (&new_idxs, ataccess);
+
+ /* The current access specification alrady applied. */
+ rdwr_map cur_idxs;
+ init_attr_rdwr_indices (&cur_idxs, attrs);
+
+ std::string spec;
+ for (auto it = new_idxs.begin (); it != new_idxs.end (); ++it)
+ {
+ const auto &newaxsref = *it;
+
+ /* The map has two equal entries for each pointer argument that
+ has an associated size argument. Process just the entry for
+ the former. */
+ if ((unsigned)newaxsref.first != newaxsref.second.ptrarg)
+ continue;
+
+ const attr_access* const cura = cur_idxs.get (newaxsref.first);
+ if (!cura)
{
- pos = strstr (pos, posstr);
- if (!pos)
- break;
+ /* The new attribute needs to be added. */
+ tree str = newaxsref.second.to_internal_string ();
+ spec += TREE_STRING_POINTER (str);
+ continue;
+ }
+
+ /* The new access spec refers to an array/pointer argument for
+ which an access spec already exists. Check and diagnose any
+ conflicts. If no conflicts are found, merge the two. */
+ const attr_access* const newa = &newaxsref.second;
+
+ if (!attrstr)
+ {
+ tree str = NULL_TREE;
+ if (newa->mode != access_deferred)
+ str = newa->to_external_string ();
+ else if (cura->mode != access_deferred)
+ str = cura->to_external_string ();
+ if (str)
+ attrstr = TREE_STRING_POINTER (str);
+ }
+
+ location_t curloc = input_location;
+ if (node[2] && DECL_P (node[2]))
+ curloc = DECL_SOURCE_LOCATION (node[2]);
+
+ location_t prevloc = UNKNOWN_LOCATION;
+ if (node[1] && DECL_P (node[1]))
+ prevloc = DECL_SOURCE_LOCATION (node[1]);
+
+ if (newa->mode != cura->mode
+ && newa->mode != access_deferred
+ && cura->mode != access_deferred
+ && newa->internal_p == cura->internal_p)
+ {
+ /* Mismatch in access mode. */
+ auto_diagnostic_group d;
+ if (warning_at (curloc, OPT_Wattributes,
+ "attribute %qs mismatch with mode %qs",
+ attrstr, cura->mode_names[cura->mode])
+ && prevloc != UNKNOWN_LOCATION)
+ inform (prevloc, "previous declaration here");
+ continue;
+ }
+
+ /* Set if PTRARG refers to a VLA with an unspecified bound (T[*]).
+ Be prepared for either CURA or NEWA to refer to it, depending
+ on which happens to come first in the declaration. */
+ const bool cur_vla_ub = (cura->internal_p
+ && cura->sizarg == UINT_MAX
+ && cura->minsize == HOST_WIDE_INT_M1U);
+ const bool new_vla_ub = (newa->internal_p
+ && newa->sizarg == UINT_MAX
+ && newa->minsize == HOST_WIDE_INT_M1U);
+
+ if (newa->sizarg != cura->sizarg
+ && attrstr
+ && (!(cur_vla_ub ^ new_vla_ub)
+ || (!cura->internal_p && !newa->internal_p)))
+ {
+ /* Avoid diagnosing redeclarations of functions with no explicit
+ attribute access that add one. */
+ if (newa->mode == access_deferred
+ && cura->mode != access_deferred
+ && newa->sizarg == UINT_MAX
+ && cura->sizarg != UINT_MAX)
+ continue;
- if (ISDIGIT (pos[-1]) || ISDIGIT (pos[n1 -1]))
+ if (cura->mode == access_deferred
+ && newa->mode != access_deferred
+ && cura->sizarg == UINT_MAX
+ && newa->sizarg != UINT_MAX)
continue;
- /* Found a matching positional argument. */
- if (*attrspec != pos[-1])
+ /* The two specs designate different size arguments. It's okay
+ for the explicit spec to specify a size where none is provided
+ by the implicit (VLA) one, as in:
+ __attribute__ ((access (read_write, 1, 2)))
+ void f (int*, int);
+ but not for two explicit access attributes to do that. */
+ bool warned = false;
+
+ auto_diagnostic_group d;
+
+ if (newa->sizarg == UINT_MAX)
+ /* Mismatch in the presence of the size argument. */
+ warned = warning_at (curloc, OPT_Wattributes,
+ "attribute %qs missing positional argument 2 "
+ "provided in previous designation by argument "
+ "%u", attrstr, cura->sizarg + 1);
+ else if (cura->sizarg == UINT_MAX)
+ /* Mismatch in the presence of the size argument. */
+ warned = warning_at (curloc, OPT_Wattributes,
+ "attribute %qs positional argument 2 "
+ "missing in previous designation",
+ attrstr);
+ else if (newa->internal_p || cura->internal_p)
{
- const char* const modestr
- = (pos[-1] == 'r'
- ? "read_only"
- : (pos[-1] == 'w'
- ? "write_only"
- : (pos[-1] == 'x' ? "read_write" : "none")));
- /* Mismatch in access mode. */
- auto_diagnostic_group d;
- if (warning (OPT_Wattributes,
- "attribute %qs mismatch with mode %qs",
- attrstr, modestr)
- && DECL_P (t))
- inform (DECL_SOURCE_LOCATION (t),
- "previous declaration here");
- return NULL_TREE;
+ /* Mismatch in the value of the size argument and a VLA
+ bound. */
+ location_t argloc = curloc;
+ if (tree arg = get_argument (node[2], newa->sizarg))
+ argloc = DECL_SOURCE_LOCATION (arg);
+ warned = warning_at (argloc, OPT_Wattributes,
+ "attribute %qs positional argument 2 "
+ "conflicts with previous designation "
+ "by argument %u",
+ attrstr, cura->sizarg + 1);
}
-
- if ((n2 && pos[n1 - 1] != ','))
+ else
+ /* Mismatch in the value of the size argument between two
+ explicit access attributes. */
+ warned = warning_at (curloc, OPT_Wattributes,
+ "attribute %qs mismatched positional argument "
+ "values %i and %i",
+ attrstr, newa->sizarg + 1, cura->sizarg + 1);
+
+ if (warned)
{
- /* Mismatch in the presence of the size argument. */
- auto_diagnostic_group d;
- if (warning (OPT_Wattributes,
- "attribute %qs positional argument 2 conflicts "
- "with previous designation",
- attrstr)
- && DECL_P (t))
- inform (DECL_SOURCE_LOCATION (t),
- "previous declaration here");
- return NULL_TREE;
- }
+ /* If the previous declaration is a function (as opposed
+ to a typedef of one), find the location of the array
+ or pointer argument that uses the conflicting VLA bound
+ and point to it in the note. */
+ const attr_access* const pa = cura->size ? cura : newa;
+ tree size = pa->size ? TREE_VALUE (pa->size) : NULL_TREE;
+ if (size && DECL_P (size))
+ {
+ location_t argloc = UNKNOWN_LOCATION;
+ if (tree arg = get_argument (node[2], pa->ptrarg))
+ argloc = DECL_SOURCE_LOCATION (arg);
- if (!n2 && pos[n1 - 1] == ',')
- {
- /* Mismatch in the presence of the size argument. */
- auto_diagnostic_group d;
- if (warning (OPT_Wattributes,
- "attribute %qs missing positional argument 2 "
- "provided in previous designation",
- attrstr)
- && DECL_P (t))
- inform (DECL_SOURCE_LOCATION (t),
- "previous declaration here");
- return NULL_TREE;
- }
+ gcc_rich_location richloc (DECL_SOURCE_LOCATION (size));
+ if (argloc != UNKNOWN_LOCATION)
+ richloc.add_range (argloc);
- if (n2 && strncmp (attrspec + n1 + 1, pos + n1, n2))
- {
- /* Mismatch in the value of the size argument. */
- auto_diagnostic_group d;
- if (warning (OPT_Wattributes,
- "attribute %qs mismatched positional argument "
- "values %i and %i",
- attrstr, atoi (attrspec + n1 + 1) + 1,
- atoi (pos + n1) + 1)
- && DECL_P (t))
- inform (DECL_SOURCE_LOCATION (t),
- "previous declaration here");
- return NULL_TREE;
+ inform (&richloc, "designating the bound of variable "
+ "length array argument %u",
+ pa->ptrarg + 1);
+ }
+ else if (prevloc != UNKNOWN_LOCATION)
+ inform (prevloc, "previous declaration here");
}
- /* Avoid adding the same attribute specification. */
- return NULL_TREE;
+ continue;
}
- /* Connect the two substrings formatted above into a single one. */
- if (idxs[1])
- attrspec[n1] = ',';
+ if (newa->internal_p == cura->internal_p)
+ continue;
- size_t len = strlen (str);
- newspec = XNEWVEC (char, newlen + len + 1);
- strcpy (newspec, str);
- strcpy (newspec + len, attrspec);
- newlen += len;
+ /* Merge the CURA and NEWA. */
+ attr_access merged = newaxsref.second;
+
+ /* VLA seen in a declaration takes precedence. */
+ if (cura->minsize == HOST_WIDE_INT_M1U)
+ merged.minsize = HOST_WIDE_INT_M1U;
+
+ /* Use the explicitly specified size positional argument. */
+ if (cura->sizarg != UINT_MAX)
+ merged.sizarg = cura->sizarg;
+
+ /* Use the explicitly specified mode. */
+ if (merged.mode == access_deferred)
+ merged.mode = cura->mode;
+
+ tree str = merged.to_internal_string ();
+ spec += TREE_STRING_POINTER (str);
}
- else if (idxs[1])
- /* Connect the two substrings formatted above into a single one. */
- attrspec[n1] = ',';
- tree ret = build_string (newlen + 1, newspec);
- if (newspec != attrspec)
- XDELETEVEC (newspec);
- return ret;
+ if (!spec.length ())
+ return NULL_TREE;
+
+ return build_string (spec.length (), spec.c_str ());
}
-/* Handle the access attribute (read_only, write_only, and read_write). */
+/* Convenience wrapper for the above. */
+
+tree
+append_access_attr (tree node[3], tree attrs, const char *attrstr,
+ char code, HOST_WIDE_INT idxs[2])
+{
+ char attrspec[80];
+ int n = sprintf (attrspec, "%c%u", code, (unsigned) idxs[0] - 1);
+ if (idxs[1])
+ n += sprintf (attrspec + n, ",%u", (unsigned) idxs[1] - 1);
+
+ return append_access_attr (node, attrs, attrstr, attrspec);
+}
+
+/* Handle the access attribute for function type NODE[0], with the function
+ DECL optionally in NODE[1]. The handler is called both in response to
+ an explict attribute access on a declaration with a mode and one or two
+ positional arguments, and for internally synthesized access specifications
+ with a string argument optionally followd by a DECL or expression
+ representing a VLA bound. To speed up parsing, the handler transforms
+ the attribute and its arguments into a string. */
static tree
-handle_access_attribute (tree *node, tree name, tree args,
+handle_access_attribute (tree node[3], tree name, tree args,
int ARG_UNUSED (flags), bool *no_add_attrs)
{
+ tree attrs = TYPE_ATTRIBUTES (*node);
tree type = *node;
- tree attrs = TYPE_ATTRIBUTES (type);
+ if (POINTER_TYPE_P (type))
+ {
+ tree ptype = TREE_TYPE (type);
+ if (FUNC_OR_METHOD_TYPE_P (ptype))
+ type = ptype;
+ }
*no_add_attrs = true;
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;
}
ps += 2;
}
- const bool read_only = !strncmp (ps, "read_only", 9);
- const bool write_only = !strncmp (ps, "write_only", 10);
- const bool read_write = !strncmp (ps, "read_write", 10);
- if (!read_only && !write_only && !read_write && strncmp (ps, "none", 4))
- {
- error ("attribute %qE invalid mode %qs; expected one of "
- "%qs, %qs, %qs, or %qs", name, access_str,
- "read_only", "read_write", "write_only", "none");
- return NULL_TREE;
- }
+ int imode;
+
+ {
+ const int nmodes =
+ sizeof attr_access::mode_names / sizeof *attr_access::mode_names;
+
+ for (imode = 0; imode != nmodes; ++imode)
+ if (!strncmp (ps, attr_access::mode_names[imode],
+ strlen (attr_access::mode_names[imode])))
+ break;
+
+ if (imode == nmodes)
+ {
+ error ("attribute %qE invalid mode %qs; expected one of "
+ "%qs, %qs, %qs, or %qs", name, access_str,
+ "read_only", "read_write", "write_only", "none");
+ return NULL_TREE;
+ }
+ }
+
+ const ::access_mode mode = static_cast<::access_mode>(imode);
if (funcall)
{
}
}
- if (read_write || write_only)
+ if (mode == access_read_write || mode == access_write_only)
{
/* Read_write and write_only modes must reference non-const
arguments. */
/* Verify that the new attribute doesn't conflict with any existing
attributes specified on previous declarations of the same type
and if not, concatenate the two. */
- const char code
- = read_only ? 'r' : write_only ? 'w' : read_write ? 'x' : '-';
- tree new_attrs = append_access_attrs (node[0], attrs, attrstr, code, idxs);
+ const char code = attr_access::mode_chars[mode];
+ tree new_attrs = append_access_attr (node, attrs, attrstr, code, idxs);
if (!new_attrs)
return NULL_TREE;
/* Replace any existing access attribute specification with
the concatenation above. */
new_attrs = tree_cons (NULL_TREE, new_attrs, NULL_TREE);
- new_attrs = tree_cons (name, new_attrs, attrs);
+ new_attrs = tree_cons (name, new_attrs, NULL_TREE);
if (node[1])
{
/* Repeat for the previously declared type. */
attrs = TYPE_ATTRIBUTES (TREE_TYPE (node[1]));
- tree attrs1 = append_access_attrs (node[1], attrs, attrstr, code, idxs);
- if (!attrs1)
+ new_attrs = append_access_attr (node, attrs, attrstr, code, idxs);
+ if (!new_attrs)
return NULL_TREE;
- attrs1 = tree_cons (NULL_TREE, attrs1, NULL_TREE);
- new_attrs = tree_cons (name, attrs1, attrs);
+ new_attrs = tree_cons (NULL_TREE, new_attrs, NULL_TREE);
+ new_attrs = tree_cons (name, new_attrs, NULL_TREE);
}
/* Recursively call self to "replace" the documented/external form
- of the attribute with the condensend internal form. */
+ of the attribute with the condensed internal form. */
decl_attributes (node, new_attrs, flags);
return NULL_TREE;
}
+/* Extract attribute "arg spec" from each FNDECL argument that has it,
+ build a single attribute access corresponding to all the arguments,
+ and return the result. SKIP_VOIDPTR set to ignore void* parameters
+ (used for user-defined functions for which, unlike in for built-ins,
+ void* cannot be relied on to determine anything about the access
+ through it or whether it even takes place).
+
+ For example, the parameters in the declaration:
+
+ void f (int x, int y, char [x][1][y][3], char [y][2][y][5]);
+
+ result in the following attribute access:
+
+ value: "+^2[*],$0$1^3[*],$1$1"
+ chain: <0, x> <1, y>
+
+ where each <node> on the chain corresponds to one VLA bound for each
+ of the two parameters. */
+
+tree
+build_attr_access_from_parms (tree parms, bool skip_voidptr)
+{
+ /* Maps each named integral argument DECL seen so far to its position
+ in the argument list; used to associate VLA sizes with arguments. */
+ hash_map<tree, unsigned> arg2pos;
+
+ /* The string representation of the access specification for all
+ arguments. */
+ std::string spec;
+ unsigned argpos = 0;
+
+ /* A TREE_LIST of VLA bounds. */
+ tree vblist = NULL_TREE;
+
+ for (tree arg = parms; arg; arg = TREE_CHAIN (arg), ++argpos)
+ {
+ if (!DECL_P (arg))
+ continue;
+
+ tree argtype = TREE_TYPE (arg);
+ if (DECL_NAME (arg) && INTEGRAL_TYPE_P (argtype))
+ arg2pos.put (arg, argpos);
+
+ tree argspec = DECL_ATTRIBUTES (arg);
+ if (!argspec)
+ continue;
+
+ if (POINTER_TYPE_P (argtype))
+ {
+ /* void* arguments in user-defined functions could point to
+ anything; skip them. */
+ tree reftype = TREE_TYPE (argtype);
+ if (skip_voidptr && VOID_TYPE_P (reftype))
+ continue;
+ }
+
+ /* Each parameter should have at most one "arg spec" attribute. */
+ argspec = lookup_attribute ("arg spec", argspec);
+ if (!argspec)
+ continue;
+
+ /* Attribute arg spec should have one or two arguments. */
+ argspec = TREE_VALUE (argspec);
+
+ /* The attribute arg spec string. */
+ tree str = TREE_VALUE (argspec);
+ const char *s = TREE_STRING_POINTER (str);
+
+ /* Create the attribute access string from the arg spec string,
+ optionally followed by position of the VLA bound argument if
+ it is one. */
+ char specbuf[80];
+ int len = snprintf (specbuf, sizeof specbuf, "%c%u%s",
+ attr_access::mode_chars[access_deferred],
+ argpos, s);
+ gcc_assert ((size_t) len < sizeof specbuf);
+
+ if (!spec.length ())
+ spec += '+';
+
+ spec += specbuf;
+
+ /* The (optional) list of expressions denoting the VLA bounds
+ N in ARGTYPE <arg>[Ni]...[Nj]...[Nk]. */
+ tree argvbs = TREE_CHAIN (argspec);
+ if (argvbs)
+ {
+ spec += ',';
+ /* Add ARGVBS to the list. Their presence is indicated by
+ appending a comma followed by the dollar sign and, when
+ it corresponds to a function parameter, the position of
+ each bound Ni, so it can be distinguished from
+ an unspecified bound (as in T[*]). The list is in reverse
+ order of arguments and needs to be reversed to access in
+ order. */
+ vblist = tree_cons (NULL_TREE, argvbs, vblist);
+
+ unsigned nelts = 0;
+ for (tree vb = argvbs; vb; vb = TREE_CHAIN (vb), ++nelts)
+ {
+ tree bound = TREE_VALUE (vb);
+ if (const unsigned *psizpos = arg2pos.get (bound))
+ {
+ /* BOUND previously seen in the parameter list. */
+ TREE_PURPOSE (vb) = size_int (*psizpos);
+ sprintf (specbuf, "$%u", *psizpos);
+ spec += specbuf;
+ }
+ else
+ {
+ /* BOUND doesn't name a parameter (it could be a global
+ variable or an expression such as a function call). */
+ spec += '$';
+ }
+ }
+ }
+ }
+
+ if (!spec.length ())
+ return NULL_TREE;
+
+ /* Build a single attribute access with the string describing all
+ array arguments and an optional list of any non-parameter VLA
+ bounds in order. */
+ tree str = build_string (spec.length (), spec.c_str ());
+ tree attrargs = tree_cons (NULL_TREE, str, vblist);
+ tree name = get_identifier ("access");
+ return tree_cons (name, attrargs, NULL_TREE);
+}
+
/* Handle a "nothrow" attribute; arguments as in
struct attribute_spec.handler. */
extern void warn_for_omitted_condop (location_t, tree);
extern bool warn_for_restrict (unsigned, tree *, unsigned);
extern void warn_for_address_or_pointer_of_packed_member (tree, tree);
+extern void warn_parm_array_mismatch (location_t, tree, tree);
/* Places where an lvalue, or modifiable lvalue, may be required.
Used to select diagnostic messages in lvalue_error and
extern const struct attribute_spec::exclusions attr_cold_hot_exclusions[];
extern const struct attribute_spec::exclusions attr_noreturn_exclusions[];
extern tree handle_noreturn_attribute (tree *, tree, tree, int, bool *);
+extern bool has_attribute (location_t, tree, tree, tree (*)(tree));
+extern tree build_attr_access_from_parms (tree, bool);
/* In c-format.c. */
extern bool valid_format_string_type_p (tree);
location_t prev_token_loc);
extern tree braced_lists_to_strings (tree, tree);
-extern bool has_attribute (location_t, tree, tree, tree (*)(tree));
-
#if CHECKING_P
namespace selftest {
/* Declarations for specific families of tests within c-family,
if (!TYPE_P (t))
t = TREE_TYPE (t);
- qualifiers = TYPE_QUALS (t);
- pp_c_cv_qualifiers (pp, qualifiers,
- TREE_CODE (t) == FUNCTION_TYPE);
+ if (TREE_CODE (t) != ARRAY_TYPE)
+ {
+ qualifiers = TYPE_QUALS (t);
+ pp_c_cv_qualifiers (pp, qualifiers,
+ TREE_CODE (t) == FUNCTION_TYPE);
+ }
if (!ADDR_SPACE_GENERIC_P (TYPE_ADDR_SPACE (t)))
{
void
c_pretty_printer::direct_abstract_declarator (tree t)
{
+ bool add_space = false;
+
switch (TREE_CODE (t))
{
case POINTER_TYPE:
case ARRAY_TYPE:
pp_c_left_bracket (this);
+
+ if (int quals = TYPE_QUALS (t))
+ {
+ /* Print the array qualifiers such as in "T[const restrict 3]". */
+ pp_c_cv_qualifiers (this, quals, false);
+ add_space = true;
+ }
+
+ if (tree arr = lookup_attribute ("array", TYPE_ATTRIBUTES (t)))
+ {
+ if (TREE_VALUE (arr))
+ {
+ /* Print the specifier as in "T[static 3]" that's not actually
+ part of the type but may be added by the front end. */
+ pp_c_ws_string (this, "static");
+ add_space = true;
+ }
+ else if (!TYPE_DOMAIN (t))
+ /* For arrays of unspecified bound using the [*] notation. */
+ pp_character (this, '*');
+ }
+
if (tree dom = TYPE_DOMAIN (t))
{
if (tree maxval = TYPE_MAX_VALUE (dom))
{
+ if (add_space)
+ pp_space (this);
+
tree type = TREE_TYPE (maxval);
if (tree_fits_shwi_p (maxval))
pp_wide_integer (this, tree_to_shwi (maxval) + 1);
- else
+ else if (TREE_CODE (maxval) == INTEGER_CST)
expression (fold_build2 (PLUS_EXPR, type, maxval,
build_int_cst (type, 1)));
+ else
+ {
+ /* Strip the expressions from around a VLA bound added
+ internally to make it fit the domain mold, including
+ any casts. */
+ if (TREE_CODE (maxval) == NOP_EXPR)
+ maxval = TREE_OPERAND (maxval, 0);
+ if (TREE_CODE (maxval) == PLUS_EXPR
+ && integer_all_onesp (TREE_OPERAND (maxval, 1)))
+ {
+ maxval = TREE_OPERAND (maxval, 0);
+ if (TREE_CODE (maxval) == NOP_EXPR)
+ maxval = TREE_OPERAND (maxval, 0);
+ }
+ if (TREE_CODE (maxval) == SAVE_EXPR)
+ {
+ maxval = TREE_OPERAND (maxval, 0);
+ if (TREE_CODE (maxval) == NOP_EXPR)
+ maxval = TREE_OPERAND (maxval, 0);
+ }
+
+ expression (maxval);
+ }
}
else if (TYPE_SIZE (t))
/* Print zero for zero-length arrays but not for flexible
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"
#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
check_and_warn_address_or_pointer_of_packed_member (type, rhs);
}
+
+/* Return EXPR + 1. Convenience helper used below. */
+
+static inline tree
+plus_one (tree expr)
+{
+ tree type = TREE_TYPE (expr);
+ return fold_build2 (PLUS_EXPR, type, expr, build_int_cst (type, 1));
+}
+
+/* Try to strip the expressions from around a VLA bound added internally
+ to make it fit the domain mold, including any casts, and return
+ the result. The goal is to obtain the PARM_DECL the VLA bound may
+ refer to. */
+
+static tree
+vla_bound_parm_decl (tree expr)
+{
+ if (!expr)
+ return NULL_TREE;
+
+ if (TREE_CODE (expr) == NOP_EXPR)
+ expr = TREE_OPERAND (expr, 0);
+ if (TREE_CODE (expr) == PLUS_EXPR
+ && integer_all_onesp (TREE_OPERAND (expr, 1)))
+ {
+ expr = TREE_OPERAND (expr, 0);
+ if (TREE_CODE (expr) == NOP_EXPR)
+ expr = TREE_OPERAND (expr, 0);
+ }
+ if (TREE_CODE (expr) == SAVE_EXPR)
+ {
+ expr = TREE_OPERAND (expr, 0);
+ if (TREE_CODE (expr) == NOP_EXPR)
+ expr = TREE_OPERAND (expr, 0);
+ }
+ return expr;
+}
+
+/* Diagnose mismatches in VLA bounds between function parameters NEWPARMS
+ of pointer types on a redeclaration os a function previously declared
+ with CURPARMS at ORIGLOC. */
+
+static void
+warn_parm_ptrarray_mismatch (location_t origloc, tree curparms, tree newparms)
+{
+ /* Maps each named integral parameter seen so far to its position
+ in the argument list; used to associate VLA sizes with arguments. */
+ hash_map<tree, unsigned> curparm2pos;
+ hash_map<tree, unsigned> newparm2pos;
+
+ unsigned parmpos = 1;
+ for (tree curp = curparms, newp = newparms; curp && newp;
+ curp = TREE_CHAIN (curp), newp = TREE_CHAIN (newp), ++parmpos)
+ {
+ tree curtyp = TREE_TYPE (curp), newtyp = TREE_TYPE (newp);
+ if (INTEGRAL_TYPE_P (curtyp))
+ {
+ /* Only add named parameters; unnamed ones cannot be referred
+ to in VLA bounds. */
+ if (DECL_NAME (curp))
+ curparm2pos.put (curp, parmpos);
+ if (DECL_NAME (newp))
+ newparm2pos.put (newp, parmpos);
+
+ continue;
+ }
+
+ /* The parameter types should match at this point so only test one. */
+ if (TREE_CODE (curtyp) != POINTER_TYPE)
+ continue;
+
+ do
+ {
+ curtyp = TREE_TYPE (curtyp);
+ newtyp = TREE_TYPE (newtyp);
+ }
+ while (TREE_CODE (curtyp) == POINTER_TYPE
+ && TREE_CODE (newtyp) == POINTER_TYPE);
+
+ if (TREE_CODE (curtyp) != ARRAY_TYPE
+ || TREE_CODE (newtyp) != ARRAY_TYPE)
+ {
+ if (curtyp == error_mark_node
+ || newtyp == error_mark_node)
+ return;
+
+ continue;
+ }
+
+ tree curdom = TYPE_DOMAIN (curtyp), newdom = TYPE_DOMAIN (newtyp);
+ tree curbnd = curdom ? TYPE_MAX_VALUE (curdom) : NULL_TREE;
+ tree newbnd = newdom ? TYPE_MAX_VALUE (newdom) : NULL_TREE;
+
+ if (DECL_P (curp))
+ origloc = DECL_SOURCE_LOCATION (curp);
+ else if (EXPR_P (curp) && EXPR_HAS_LOCATION (curp))
+ origloc = EXPR_LOCATION (curp);
+
+ /* The location of the parameter in the current redeclaration. */
+ location_t newloc = DECL_SOURCE_LOCATION (newp);
+ if (origloc == UNKNOWN_LOCATION)
+ origloc = newloc;
+
+ /* Issue -Warray-parameter onless one or more mismatches involves
+ a VLA bound; then issue -Wvla-parameter. */
+ int opt = OPT_Warray_parameter_;
+ /* Traverse the two array types looking for variable bounds and
+ comparing the two in each pair for mismatches either in their
+ positions in the function parameter list or lexicographically
+ for others. Record the 1-based parameter position of each
+ mismatch in BNDVEC, and the location of each parameter in
+ the mismatch in WARNLOC (for the new parameter list) and
+ NOTELOC (for the current parameter list). */
+ unsigned bndpos = 1;
+ auto_vec<int> bndvec;
+ gcc_rich_location warnloc (newloc);
+ gcc_rich_location noteloc (origloc);
+ for ( ; curtyp || newtyp;
+ ++bndpos,
+ curbnd = curdom ? TYPE_MAX_VALUE (curdom) : NULL_TREE,
+ newbnd = newdom ? TYPE_MAX_VALUE (newdom) : NULL_TREE)
+ {
+ /* Try to strip each bound down to the PARM_DECL if it does
+ correspond to one. Either bound can be null if it's
+ unspecified (i.e., has the [*] form). */
+ curbnd = vla_bound_parm_decl (curbnd);
+ newbnd = vla_bound_parm_decl (newbnd);
+
+ /* Peel the current bound off CURTYP and NEWTYP, skipping
+ over any subsequent pointer types. */
+ if (curtyp && TREE_CODE (curtyp) == ARRAY_TYPE)
+ {
+ do
+ curtyp = TREE_TYPE (curtyp);
+ while (TREE_CODE (curtyp) == POINTER_TYPE);
+ if (TREE_CODE (curtyp) == ARRAY_TYPE)
+ curdom = TYPE_DOMAIN (curtyp);
+ else
+ curdom = NULL_TREE;
+ }
+ else
+ curtyp = NULL_TREE;
+
+ if (newtyp && TREE_CODE (newtyp) == ARRAY_TYPE)
+ {
+ do
+ newtyp = TREE_TYPE (newtyp);
+ while (TREE_CODE (newtyp) == POINTER_TYPE);
+ if (TREE_CODE (newtyp) == ARRAY_TYPE)
+ newdom = TYPE_DOMAIN (newtyp);
+ else
+ newdom = NULL_TREE;
+ }
+ else
+ newtyp = NULL_TREE;
+
+ /* Move on to the next bound if this one is unspecified. */
+ if (!curbnd && !newbnd)
+ continue;
+
+ /* Try to find each bound in the parameter list. */
+ const unsigned* const pcurbndpos = curparm2pos.get (curbnd);
+ const unsigned* const pnewbndpos = newparm2pos.get (newbnd);
+ /* Move on if both bounds refer to the same parameter. */
+ if (pcurbndpos && pnewbndpos && *pcurbndpos == *pnewbndpos)
+ continue;
+
+ /* Move on if the bounds look the same. */
+ if (!pcurbndpos && !pnewbndpos
+ && curbnd && newbnd
+ && operand_equal_p (curbnd, newbnd, OEP_LEXICOGRAPHIC))
+ continue;
+
+ if ((curbnd && TREE_CODE (curbnd) != INTEGER_CST)
+ || (newbnd && TREE_CODE (newbnd) != INTEGER_CST))
+ opt = OPT_Wvla_parameter;
+
+ /* Record the mismatch. */
+ bndvec.safe_push (bndpos);
+ /* Underline the bounding parameter in the declaration. */
+ if (curbnd && TREE_CODE (curbnd) == PARM_DECL)
+ noteloc.add_range (DECL_SOURCE_LOCATION (curbnd));
+ if (newbnd && TREE_CODE (newbnd) == PARM_DECL)
+ warnloc.add_range (DECL_SOURCE_LOCATION (newbnd));
+ }
+
+ const unsigned nbnds = bndvec.length ();
+ if (!nbnds)
+ continue;
+
+ /* Use attr_access to format the parameter types. */
+ attr_access spec = { };
+ const std::string newparmstr = spec.array_as_string (TREE_TYPE (newp));
+ const std::string curparmstr = spec.array_as_string (TREE_TYPE (curp));
+
+ if (warning_n (&warnloc, opt, nbnds,
+ "mismatch in bound %Z of argument %u declared as %s",
+ "mismatch in bounds %Z of argument %u declared as %s",
+ bndvec.address (), nbnds, parmpos, newparmstr.c_str ()))
+ inform (¬eloc, "previously declared as %s", curparmstr.c_str ());
+ }
+}
+
+/* Detect and diagnose a mismatch between an attribute access specification
+ on the original declaration of FNDECL and that on the parameters NEWPARMS
+ from its refeclaration. ORIGLOC is the location of the first declaration
+ (FNDECL's is set to the location of the redeclaration). */
+
+void
+warn_parm_array_mismatch (location_t origloc, tree fndecl, tree newparms)
+{
+ /* The original parameter list (copied from the original declaration
+ into the current [re]declaration, FNDECL)). The two are equal if
+ and only if FNDECL is the first declaratation. */
+ tree curparms = DECL_ARGUMENTS (fndecl);
+ if (!curparms || !newparms || curparms == newparms)
+ return;
+
+ if (TREE_CODE (curparms) != PARM_DECL
+ || TREE_CODE (newparms) != PARM_DECL)
+ return;
+ /* Extract the (possibly empty) attribute access specification from
+ the declaration and its type (it doesn't yet reflect those created
+ in response to NEWPARMS). */
+ rdwr_map cur_idx;
+ tree fntype = TREE_TYPE (fndecl);
+ init_attr_rdwr_indices (&cur_idx, TYPE_ATTRIBUTES (fntype));
+
+ /* Build a (possibly null) chain of access attributes corresponding
+ to NEWPARMS. */
+ const bool builtin = fndecl_built_in_p (fndecl);
+ tree newattrs = build_attr_access_from_parms (newparms, builtin);
+
+ /* Extract the (possibly empty) attribute access specification from
+ NEWATTRS. */
+ rdwr_map new_idx;
+ init_attr_rdwr_indices (&new_idx, newattrs);
+
+ if (cur_idx.is_empty () && new_idx.is_empty ())
+ {
+ /* If both specs are empty check pointers to VLAs for mismatches. */
+ warn_parm_ptrarray_mismatch (origloc, curparms, newparms);
+ return;
+ }
+ /* ...otherwise, if at least one spec isn't empty there may be mismatches,
+ such as between f(T*) and f(T[1]), where the former mapping woud be
+ empty. */
+
+ /* Create an empty access specification and use it for pointers with
+ no spec of their own. */
+ attr_access ptr_spec = { };
+
+ /* Iterate over the two lists of function parameters, comparing their
+ respective mappings and diagnosing mismatches. */
+ unsigned parmpos = 0;
+ for (tree curp = curparms, newp = newparms; curp;
+ curp = TREE_CHAIN (curp), newp = TREE_CHAIN (newp), ++parmpos)
+ {
+ /* Only check pointers and C++ references. */
+ tree newptype = TREE_TYPE (newp);
+ if (!POINTER_TYPE_P (newptype))
+ continue;
+
+ {
+ /* Skip mismatches in __builtin_va_list that is commonly
+ an array but that in declarations of built-ins decays
+ to a pointer. */
+ if (builtin && TREE_TYPE (newptype) == TREE_TYPE (va_list_type_node))
+ continue;
+ }
+
+ /* Access specs for the argument on the current (previous) and
+ new (to replace the current) declarations. Either may be null,
+ indicating the parameter is an ordinary pointer with no size
+ associated with it. */
+ attr_access *cura = cur_idx.get (parmpos);
+ attr_access *newa = new_idx.get (parmpos);
+
+ if (!newa)
+ {
+ /* Continue of both parameters are pointers with no size
+ associated with it. */
+ if (!cura)
+ continue;
+
+ /* Otherwise point at PTR_SPEC and set its parameter pointer
+ and number. */
+ newa = &ptr_spec;
+ newa->ptr = newp;
+ newa->ptrarg = parmpos;
+ }
+ else if (!cura)
+ {
+ cura = &ptr_spec;
+ cura->ptr = curp;
+ cura->ptrarg = parmpos;
+ }
+
+ /* Set if the parameter is [re]declared as a VLA. */
+ const bool cur_vla_p = cura->size || cura->minsize == HOST_WIDE_INT_M1U;
+ const bool new_vla_p = newa->size || newa->minsize == HOST_WIDE_INT_M1U;
+
+ if (DECL_P (curp))
+ origloc = DECL_SOURCE_LOCATION (curp);
+ else if (EXPR_P (curp) && EXPR_HAS_LOCATION (curp))
+ origloc = EXPR_LOCATION (curp);
+
+ /* The location of the parameter in the current redeclaration. */
+ location_t newloc = DECL_SOURCE_LOCATION (newp);
+ if (origloc == UNKNOWN_LOCATION)
+ origloc = newloc;
+
+ tree curptype = TREE_TYPE (curp);
+ const std::string newparmstr = newa->array_as_string (newptype);
+ const std::string curparmstr = cura->array_as_string (curptype);
+ if (new_vla_p && !cur_vla_p)
+ {
+ if (warning_at (newloc, OPT_Wvla_parameter,
+ "argument %u of type %s declared as "
+ "a variable length array",
+ parmpos + 1, newparmstr.c_str ()))
+ inform (origloc,
+ (cura == &ptr_spec
+ ? G_("previously declared as a pointer %s")
+ : G_("previously declared as an ordinary array %s")),
+ curparmstr.c_str ());
+ continue;
+ }
+
+ if (newa == &ptr_spec)
+ {
+ /* The new declaration uses the pointer form. Detect mismatches
+ between the pointer and a previous array or VLA forms. */
+ if (cura->minsize == HOST_WIDE_INT_M1U)
+ {
+ /* Diagnose a pointer/VLA mismatch. */
+ if (warning_at (newloc, OPT_Wvla_parameter,
+ "argument %u of type %s declared "
+ "as a pointer",
+ parmpos + 1, newparmstr.c_str ()))
+ inform (origloc,
+ "previously declared as a variable length array %s",
+ curparmstr.c_str ());
+ continue;
+ }
+
+ if (cura->minsize && cura->minsize != HOST_WIDE_INT_M1U)
+ {
+ /* Diagnose mismatches between arrays with a constant
+ bound and pointers. */
+ if (warning_at (newloc, OPT_Warray_parameter_,
+ "argument %u of type %s declared "
+ "as a pointer",
+ parmpos + 1, newparmstr.c_str ()))
+ inform (origloc, "previously declared as an array %s",
+ curparmstr.c_str ());
+ continue;
+ }
+ }
+
+ if (!new_vla_p && cur_vla_p)
+ {
+ if (warning_at (newloc, OPT_Wvla_parameter,
+ "argument %u of type %s declared "
+ "as an ordinary array",
+ parmpos + 1, newparmstr.c_str ()))
+ inform (origloc,
+ "previously declared as a variable length array %s",
+ curparmstr.c_str ());
+ continue;
+ }
+
+ /* Move on to the next pair of parameters if both of the current
+ pair are VLAs with a single variable bound that refers to
+ a parameter at the same position. */
+ if (newa->size && cura->size
+ && newa->sizarg != UINT_MAX
+ && newa->sizarg == cura->sizarg
+ && newa->minsize == cura->minsize
+ && !TREE_CHAIN (newa->size) && !TREE_CHAIN (cura->size))
+ continue;
+
+ if (newa->size || cura->size)
+ {
+ unsigned newunspec, curunspec;
+ unsigned newbnds = newa->vla_bounds (&newunspec) + newunspec;
+ unsigned curbnds = cura->vla_bounds (&curunspec) + curunspec;
+
+ if (newbnds != curbnds)
+ {
+ if (warning_n (newloc, OPT_Wvla_parameter, newbnds,
+ "argument %u of type %s declared with "
+ "%u variable bound",
+ "argument %u of type %s declared with "
+ "%u variable bounds",
+ parmpos + 1, newparmstr.c_str (),
+ newbnds))
+ inform_n (origloc, curbnds,
+ "previously declared as %s with %u variable bound",
+ "previously declared as %s with %u variable bounds",
+ curparmstr.c_str (), curbnds);
+ continue;
+ }
+
+ if (newunspec != curunspec)
+ {
+ location_t warnloc = newloc, noteloc = origloc;
+ const char *warnparmstr = newparmstr.c_str ();
+ const char *noteparmstr = curparmstr.c_str ();
+ unsigned warnunspec = newunspec, noteunspec = curunspec;
+
+ if (newunspec < curunspec)
+ {
+ /* If the new declaration has fewer unspecified bounds
+ point the warning to the previous declaration to make
+ it clear that that's the one to change. Otherwise,
+ point it to the new decl. */
+ std::swap (warnloc, noteloc);
+ std::swap (warnparmstr, noteparmstr);
+ std::swap (warnunspec, noteunspec);
+ }
+ if (warning_n (warnloc, OPT_Wvla_parameter, warnunspec,
+ "argument %u of type %s declared with "
+ "%u unspecified variable bound",
+ "argument %u of type %s declared with "
+ "%u unspecified variable bounds",
+ parmpos + 1, warnparmstr, warnunspec))
+ {
+ if (warnloc == newloc)
+ inform_n (noteloc, noteunspec,
+ "previously declared as %s with %u unspecified "
+ "variable bound",
+ "previously declared as %s with %u unspecified "
+ "variable bounds",
+ noteparmstr, noteunspec);
+ else
+ inform_n (noteloc, noteunspec,
+ "subsequently declared as %s with %u unspecified "
+ "variable bound",
+ "subsequently declared as %s with %u unspecified "
+ "variable bounds",
+ noteparmstr, noteunspec);
+ }
+ continue;
+ }
+ }
+
+ /* Iterate over the lists of VLA variable bounds, comparing each
+ pair for equality, and diagnosing mismatches. The case of
+ the lists having different lengths is handled above so at
+ this point they do . */
+ for (tree newvbl = newa->size, curvbl = cura->size; newvbl;
+ newvbl = TREE_CHAIN (newvbl), curvbl = TREE_CHAIN (curvbl))
+ {
+ tree newpos = TREE_PURPOSE (newvbl);
+ tree curpos = TREE_PURPOSE (curvbl);
+
+ tree newbnd = vla_bound_parm_decl (TREE_VALUE (newvbl));
+ tree curbnd = vla_bound_parm_decl (TREE_VALUE (curvbl));
+
+ if (newpos == curpos && newbnd == curbnd)
+ /* In the expected case when both bounds either refer to
+ the same positional parameter or when neither does,
+ and both are the same expression they are necessarily
+ the same. */
+ continue;
+
+ const char* const newbndstr =
+ newbnd ? print_generic_expr_to_str (newbnd) : "*";
+ const char* const curbndstr =
+ curbnd ? print_generic_expr_to_str (curbnd) : "*";
+
+ if (!newpos != !curpos
+ || (newpos && !tree_int_cst_equal (newpos, curpos)))
+ {
+ /* Diagnose a mismatch between a specified VLA bound and
+ an unspecified one. This can only happen in the most
+ significant bound.
+
+ Distinguish between the common case of bounds that are
+ other function parameters such as in
+ f (int n, int[n]);
+ and others. */
+
+ gcc_rich_location richloc (newloc);
+ bool warned;
+ if (newpos)
+ {
+ /* Also underline the VLA bound argument. */
+ richloc.add_range (DECL_SOURCE_LOCATION (newbnd));
+ warned = warning_at (&richloc, OPT_Wvla_parameter,
+ "argument %u of type %s declared "
+ "with mismatched bound argument %E",
+ parmpos + 1, newparmstr.c_str (),
+ plus_one (newpos));
+ }
+ else
+ warned = warning_at (&richloc, OPT_Wvla_parameter,
+ "argument %u of type %s declared "
+ "with mismatched bound %<%s%>",
+ parmpos + 1, newparmstr.c_str (),
+ newbndstr);
+
+ if (warned)
+ {
+ gcc_rich_location richloc (origloc);
+ if (curpos)
+ {
+ /* Also underline the VLA bound argument. */
+ richloc.add_range (DECL_SOURCE_LOCATION (curbnd));
+ inform (&richloc, "previously declared as %s with "
+ "bound argument %E",
+ curparmstr.c_str (), plus_one (curpos));
+ }
+ else
+ inform (&richloc, "previously declared as %s with bound "
+ "%<%s%>", curparmstr.c_str (), curbndstr);
+
+ continue;
+ }
+ }
+
+ if (!newpos && newbnd && curbnd)
+ {
+ /* The VLA bounds don't refer to other function parameters.
+ Compare them lexicographically to detect gross mismatches
+ such as between T[foo()] and T[bar()]. */
+ if (operand_equal_p (newbnd, curbnd, OEP_LEXICOGRAPHIC))
+ continue;
+
+ if (warning_at (newloc, OPT_Wvla_parameter,
+ "argument %u of type %s declared with "
+ "mismatched bound %<%s%>",
+ parmpos + 1, newparmstr.c_str (), newbndstr))
+ inform (origloc, "previously declared as %s with bound %qs",
+ curparmstr.c_str (), curbndstr);
+ continue;
+ }
+ }
+
+ if (newa->minsize == cura->minsize
+ || (((newa->minsize == 0 && newa->mode != access_deferred)
+ || (cura->minsize == 0 && cura->mode != access_deferred))
+ && newa != &ptr_spec
+ && cura != &ptr_spec))
+ continue;
+
+ if (!newa->static_p && !cura->static_p && warn_array_parameter < 2)
+ /* Avoid warning about mismatches in ordinary (non-static) arrays
+ at levels below 2. */
+ continue;
+
+ if (warning_at (newloc, OPT_Warray_parameter_,
+ "argument %u of type %s with mismatched bound",
+ parmpos + 1, newparmstr.c_str ()))
+ inform (origloc, "previously declared as %s", curparmstr.c_str ());
+ }
+}
LangEnabledBy(C ObjC C++ LTO ObjC++,Wall,1,0)
; in common.opt
+Warray-parameter
+C ObjC C++ ObjC++ Warning Alias(Warray-parameter=, 2, 0)
+Warn about mismatched declarations of array parameters and unsafe accesses to them.
+
+Warray-parameter=
+C ObjC C++ ObjC++ Joined RejectNegative UInteger Var(warn_array_parameter) IntegerRange(0, 2) LangEnabledBy(C ObjC C++ ObjC++,Wall, 2, 0) Warning
+Warn about mismatched declarations of array parameters and unsafe accesses to them.
+
Wzero-length-bounds
C ObjC C++ ObjC++ Var(warn_zero_length_bounds) Warning LangEnabledBy(C ObjC C++ ObjC++,Wall)
Warn about accesses to interior zero-length array members.
C ObjC C++ LTO ObjC++ Alias(Wvla-larger-than=,18446744073709551615EiB,none) Warning
Disable Wvla-larger-than= warning. Equivalent to Wvla-larger-than=<SIZE_MAX> or larger.
+Wvla-parameter
+C ObjC C++ ObjC++ Var(warn_vla_parameter) Warning LangEnabledBy(C ObjC C++ ObjC++,Wall)
+Warn about mismatched declarations of VLA parameters.
+
Wvolatile
C++ ObjC++ Var(warn_volatile) Warning
Warn about deprecated uses of volatile qualifier.
line numbers. For example, the CONST_DECLs for enum values. */
#include "config.h"
+#define INCLUDE_STRING
#define INCLUDE_UNIQUE_PTR
#include "system.h"
#include "coretypes.h"
#include "c-family/known-headers.h"
#include "c-family/c-spellcheck.h"
+#include "tree-pretty-print.h"
+
/* In grokdeclarator, distinguish syntactic contexts of declarators. */
enum decl_context
{ NORMAL, /* Ordinary declaration */
return type;
}
+/* Looks up the most recent pushed declaration corresponding to DECL. */
+
+static tree
+lookup_last_decl (tree decl)
+{
+ tree last_decl = lookup_name (DECL_NAME (decl));
+ if (!last_decl)
+ last_decl = lookup_name_in_scope (DECL_NAME (decl), external_scope);
+ return last_decl;
+}
+
/* Wrapper for decl_attributes that adds some implicit attributes
to VAR_DECLs or FUNCTION_DECLs. */
so far so that attributes on the current declaration that's
about to be pushed that conflict with the former can be detected,
diagnosed, and rejected as appropriate. */
- tree last_decl = lookup_name (DECL_NAME (*node));
- if (!last_decl)
- last_decl = lookup_name_in_scope (DECL_NAME (*node), external_scope);
-
+ tree last_decl = lookup_last_decl (*node);
return decl_attributes (node, attributes, flags, last_decl);
}
have been parsed, before parsing the initializer if any.
Here we create the ..._DECL node, fill in its type,
and put it on the list of decls for the current context.
+ When nonnull, set *LASTLOC to the location of the prior declaration
+ of the same entity if one exists.
The ..._DECL node is returned as the value.
Exception: for arrays where the length is not specified,
tree
start_decl (struct c_declarator *declarator, struct c_declspecs *declspecs,
- bool initialized, tree attributes)
+ bool initialized, tree attributes, location_t *lastloc /* = NULL */)
{
tree decl;
tree tem;
if (!decl || decl == error_mark_node)
return NULL_TREE;
+ if (tree lastdecl = lastloc ? lookup_last_decl (decl) : NULL_TREE)
+ if (lastdecl != error_mark_node)
+ *lastloc = DECL_SOURCE_LOCATION (lastdecl);
+
if (expr)
add_stmt (fold_convert (void_type_node, expr));
if (asmspec && VAR_P (decl) && C_DECL_REGISTER (decl))
DECL_HARD_REGISTER (decl) = 1;
rest_of_decl_compilation (decl, true, 0);
+
+ if (TREE_CODE (decl) == FUNCTION_DECL)
+ {
+ tree parms = DECL_ARGUMENTS (decl);
+ const bool builtin = fndecl_built_in_p (decl);
+ if (tree access = build_attr_access_from_parms (parms, !builtin))
+ decl_attributes (&decl, access, 0);
+ }
}
else
{
return decl;
}
+/* Return attribute "arg spec" corresponding to an array/VLA parameter
+ described by PARM, concatenated onto attributes ATTRS.
+ The spec consists of one dollar symbol for each specified variable
+ bound, one asterisk for each unspecified variable bound, followed
+ by at most one specification of the most significant bound of
+ an ordinary array parameter. For ordinary arrays the specification
+ is either the constant bound itself, or the space character for
+ an array with an unspecified bound (the [] form). Finally, a chain
+ of specified variable bounds is appended to the spec, starting with
+ the most significant bound. For example, the PARM T a[2][m][3][n]
+ will produce __attribute__((arg spec ("[$$2]", m, n)).
+ For T a typedef for an array with variable bounds, the bounds are
+ included in the specification in the expected order.
+ No "arg spec" is created for parameters of pointer types, making
+ a distinction between T(*)[N] (or, equivalently, T[][N]) and
+ the T[M][N] form, all of which have the same type and are represented
+ the same, but only the last of which gets an "arg spec" describing
+ the most significant bound M. */
+
+static tree
+get_parm_array_spec (const struct c_parm *parm, tree attrs)
+{
+ /* The attribute specification string, minor bound first. */
+ std::string spec;
+
+ /* A list of VLA variable bounds, major first, or null if unspecified
+ or not a VLA. */
+ tree vbchain = NULL_TREE;
+ /* True for a pointer parameter. */
+ bool pointer = false;
+ /* True for an ordinary array with an unpecified bound. */
+ bool nobound = false;
+
+ /* Create a string representation for the bounds of the array/VLA. */
+ for (c_declarator *pd = parm->declarator, *next; pd; pd = next)
+ {
+ next = pd->declarator;
+ while (next && next->kind == cdk_attrs)
+ next = next->declarator;
+
+ /* Remember if a pointer has been seen to avoid storing the constant
+ bound. */
+ if (pd->kind == cdk_pointer)
+ pointer = true;
+
+ if ((pd->kind == cdk_pointer || pd->kind == cdk_function)
+ && (!next || next->kind == cdk_id))
+ {
+ /* Do nothing for the common case of a pointer. The fact that
+ the parameter is one can be deduced from the absence of
+ an arg spec for it. */
+ return attrs;
+ }
+
+ if (pd->kind == cdk_id)
+ {
+ if (pointer
+ || !parm->specs->type
+ || TREE_CODE (parm->specs->type) != ARRAY_TYPE
+ || !TYPE_DOMAIN (parm->specs->type)
+ || !TYPE_MAX_VALUE (TYPE_DOMAIN (parm->specs->type)))
+ continue;
+
+ tree max = TYPE_MAX_VALUE (TYPE_DOMAIN (parm->specs->type));
+ if (!vbchain
+ && TREE_CODE (max) == INTEGER_CST)
+ {
+ /* Extract the upper bound from a parameter of an array type
+ unless the parameter is an ordinary array of unspecified
+ bound in which case a next iteration of the loop will
+ exit. */
+ if (spec.empty () || spec.end ()[-1] != ' ')
+ {
+ if (!tree_fits_shwi_p (max))
+ continue;
+
+ /* The upper bound is the value of the largest valid
+ index. */
+ HOST_WIDE_INT n = tree_to_shwi (max) + 1;
+ char buf[40];
+ sprintf (buf, "%lu", (unsigned long)n);
+ spec += buf;
+ }
+ continue;
+ }
+
+ /* For a VLA typedef, create a list of its variable bounds and
+ append it in the expected order to VBCHAIN. */
+ tree tpbnds = NULL_TREE;
+ for (tree type = parm->specs->type; TREE_CODE (type) == ARRAY_TYPE;
+ type = TREE_TYPE (type))
+ {
+ tree nelts = array_type_nelts (type);
+ if (TREE_CODE (nelts) != INTEGER_CST)
+ {
+ /* Each variable VLA bound is represented by the dollar
+ sign. */
+ spec += "$";
+ tpbnds = tree_cons (NULL_TREE, nelts, tpbnds);
+ }
+ }
+ tpbnds = nreverse (tpbnds);
+ vbchain = chainon (vbchain, tpbnds);
+ continue;
+ }
+
+ if (pd->kind != cdk_array)
+ continue;
+
+ if (pd->u.array.vla_unspec_p)
+ {
+ /* Each unspecified bound is represented by a star. There
+ can be any number of these in a declaration (but none in
+ a definition). */
+ spec += '*';
+ continue;
+ }
+
+ tree nelts = pd->u.array.dimen;
+ if (!nelts)
+ {
+ /* Ordinary array of unspecified size. There can be at most
+ one for the most significant bound. Exit on the next
+ iteration which determines whether or not PARM is declared
+ as a pointer or an array. */
+ nobound = true;
+ continue;
+ }
+
+ if (TREE_CODE (nelts) == INTEGER_CST)
+ {
+ /* Skip all constant bounds except the most significant one.
+ The interior ones are included in the array type. */
+ if (next && (next->kind == cdk_array || next->kind == cdk_pointer))
+ continue;
+
+ if (!tree_fits_uhwi_p (nelts))
+ /* Bail completely on invalid bounds. */
+ return attrs;
+
+ char buf[40];
+ const char *code = pd->u.array.static_p ? "s" : "";
+ unsigned HOST_WIDE_INT n = tree_to_uhwi (nelts);
+ sprintf (buf, "%s%llu", code, (unsigned long long)n);
+ spec += buf;
+ break;
+ }
+
+ /* Each variable VLA bound is represented by a dollar sign. */
+ spec += "$";
+ vbchain = tree_cons (NULL_TREE, nelts, vbchain);
+ }
+
+ if (spec.empty () && !nobound)
+ return attrs;
+
+ spec.insert (0, "[");
+ if (nobound)
+ /* Ordinary array of unspecified bound is represented by a space.
+ It must be last in the spec. */
+ spec += ' ';
+ spec += ']';
+
+ tree acsstr = build_string (spec.length () + 1, spec.c_str ());
+ tree args = tree_cons (NULL_TREE, acsstr, vbchain);
+ tree name = get_identifier ("arg spec");
+ return tree_cons (name, args, attrs);
+}
+
/* Given a parsed parameter declaration, decode it into a PARM_DECL
and push that on the current scope. EXPR is a pointer to an
expression that needs to be evaluated for the side effects of array
push_parm_decl (const struct c_parm *parm, tree *expr)
{
tree attrs = parm->attrs;
- tree decl;
-
- decl = grokdeclarator (parm->declarator, parm->specs, PARM, false, NULL,
- &attrs, expr, NULL, DEPRECATED_NORMAL);
+ tree decl = grokdeclarator (parm->declarator, parm->specs, PARM, false, NULL,
+ &attrs, expr, NULL, DEPRECATED_NORMAL);
if (decl && DECL_P (decl))
DECL_SOURCE_LOCATION (decl) = parm->loc;
+
+ attrs = get_parm_array_spec (parm, attrs);
decl_attributes (&decl, attrs, 0);
decl = pushdecl (decl);
old_decl = lookup_name_in_scope (DECL_NAME (decl1), current_scope);
if (old_decl && TREE_CODE (old_decl) != FUNCTION_DECL)
old_decl = NULL_TREE;
+
current_function_prototype_locus = UNKNOWN_LOCATION;
current_function_prototype_built_in = false;
current_function_prototype_arg_types = NULL_TREE;
"%qD is normally a non-static function", decl1);
}
+ tree parms = current_function_arg_info->parms;
+ if (old_decl)
+ {
+ location_t origloc = DECL_SOURCE_LOCATION (old_decl);
+ warn_parm_array_mismatch (origloc, old_decl, parms);
+ }
+
/* Record the decl so that the function name is defined.
If we already have a decl for this name, and it is a FUNCTION_DECL,
use the old decl. */
current_function_decl = pushdecl (decl1);
+ if (tree access = build_attr_access_from_parms (parms, false))
+ decl_attributes (¤t_function_decl, access, 0, old_decl);
+
push_scope ();
declare_parm_level ();
#include "intl.h"
#include "c-family/name-hint.h"
#include "tree-iterator.h"
+#include "tree-pretty-print.h"
#include "memmodel.h"
#include "c-family/known-headers.h"
c_parser_skip_to_end_of_block_or_statement (parser);
return;
}
- tree d = start_decl (declarator, specs, false,
- chainon (postfix_attrs,
- all_prefix_attrs));
- if (d
- && TREE_CODE (d) == FUNCTION_DECL
- && DECL_ARGUMENTS (d) == NULL_TREE
- && DECL_INITIAL (d) == NULL_TREE)
+
+ location_t lastloc = UNKNOWN_LOCATION;
+ tree attrs = chainon (postfix_attrs, all_prefix_attrs);
+ tree d = start_decl (declarator, specs, false, attrs, &lastloc);
+ if (d && TREE_CODE (d) == FUNCTION_DECL)
{
/* Find the innermost declarator that is neither cdk_id
nor cdk_attrs. */
gcc_unreachable ();
}
- /* If it exists and is cdk_function, use its parameters. */
+ /* If it exists and is cdk_function declaration whose
+ arguments have not been set yet, use its arguments. */
if (last_non_id_attrs
&& last_non_id_attrs->kind == cdk_function)
- DECL_ARGUMENTS (d) = last_non_id_attrs->u.arg_info->parms;
+ {
+ tree parms = last_non_id_attrs->u.arg_info->parms;
+ if (DECL_ARGUMENTS (d) == NULL_TREE
+ && DECL_INITIAL (d) == NULL_TREE)
+ DECL_ARGUMENTS (d) = parms;
+
+ warn_parm_array_mismatch (lastloc, d, parms);
+ }
}
if (omp_declare_simd_clauses.exists ())
{
if (d)
finish_decl (d, UNKNOWN_LOCATION, NULL_TREE,
NULL_TREE, asm_name);
-
+
if (c_parser_next_token_is_keyword (parser, RID_IN))
{
if (d)
extern tree start_enum (location_t, struct c_enum_contents *, tree);
extern bool start_function (struct c_declspecs *, struct c_declarator *, tree);
extern tree start_decl (struct c_declarator *, struct c_declspecs *, bool,
- tree);
+ tree, location_t * = NULL);
extern tree start_struct (location_t, enum tree_code, tree,
class c_struct_parse_info **);
extern void store_parm_decls (void);
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;
/* 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++)
if (POINTER_TYPE_P (type))
{
access->ptr = args[i].tree_value;
- gcc_assert (access->size == NULL_TREE);
+ // A nonnull ACCESS->SIZE contains VLA bounds. */
}
else
{
@gccoptlist{-Waddress @gol
-Warray-bounds=1 @r{(only with} @option{-O2}@r{)} @gol
+-Warray-parameter=2 @r{(C and Objective-C only)} @gol
-Wbool-compare @gol
-Wbool-operation @gol
-Wc++11-compat -Wc++14-compat @gol
-Wunused-label @gol
-Wunused-value @gol
-Wunused-variable @gol
+-Wvla-parameter @r{(C and Objective-C only)} @gol
-Wvolatile-register-var @gol
-Wzero-length-bounds}
false positives and is deactivated by default.
@end table
+@item -Warray-parameter
+@itemx -Warray-parameter=@var{n}
+@opindex Wno-array-parameter
+Warn about redeclarations of functions involving arguments of array or
+pointer types of inconsistent kinds or forms, and enable the detection
+of out-of-bounds accesses to such parameters by warnings such as
+@option{-Warray-bounds}.
+
+If the first function declaration uses the array form the bound specified
+in the array is assumed to be the minimum number of elements expected to
+be provided in calls to the function and the maximum number of elements
+accessed by it. Failing to provide arguments of sufficient size or accessing
+more than the maximum number of elements may be diagnosed by warnings such
+as @option{-Warray-bounds}. At level 1 the warning diagnoses inconsistencies
+involving array parameters declared using the @code{T[static N]} form.
+
+For example, the warning triggers for the following redeclarations because
+the first one allows an array of any size to be passed to @code{f} while
+the second one with the keyword @code{static} specifies that the array
+argument must have at least four elements.
+
+@smallexample
+void f (int[static 4]);
+void f (int[]); // warning (inconsistent array form)
+
+void g (void)
+@{
+ int *p = (int *)malloc (4);
+ f (p); // warning (array too small)
+ @dots{}
+@}
+@end smallexample
+
+At level 2 the warning also triggers for redeclarations involving any other
+inconsistency in array or pointer argument forms denoting array sizes.
+Pointers and arrays of unspecified bound are considered equivalent and do
+not trigger a warning.
+
+@smallexample
+void g (int*);
+void g (int[]); // no warning
+void g (int[8]); // warning (inconsistent array bound)
+@end smallexample
+
+@option{-Warray-parameter=2} is included in @option{-Wall}. The
+@option{-Wvla-parameter} option triggers warnings for similar inconsistencies
+involving Variable Length Array arguments.
+
@item -Wattribute-alias=@var{n}
@itemx -Wno-attribute-alias
@opindex Wattribute-alias
Disable @option{-Wvla-larger-than=} warnings. The option is equivalent
to @option{-Wvla-larger-than=}@samp{SIZE_MAX} or larger.
+@item -Wvla-parameter
+@opindex Wno-vla-parameter
+Warn about redeclarations of functions involving arguments of Variable
+Length Array types of inconsistent kinds or forms, and enable the detection
+of out-of-bounds accesses to such parameters by warnings such as
+@option{-Warray-bounds}.
+
+If the first function declaration uses the VLA form the bound specified
+in the array is assumed to be the minimum number of elements expected to
+be provided in calls to the function and the maximum number of elements
+accessed by it. Failing to provide arguments of sufficient size or
+accessing more than the maximum number of elements may be diagnosed.
+
+For example, the warning triggers for the following redeclarations because
+the first one allows an array of any size to be passed to @code{f} while
+the second one specifies that the array argument must have at least @code{n}
+elements. In addition, calling @code{f} with the assotiated VLA bound
+parameter in excess of the actual VLA bound triggers a warning as well.
+
+@smallexample
+void f (int n, int[n]);
+void f (int, int[]); // warning: argument 2 previously declared as a VLA
+
+void g (int n)
+@{
+ if (n > 4)
+ return;
+ int a[n];
+ f (sizeof a, a); // warning: access to a by f may be out of bounds
+ @dots{}
+@}
+
+@end smallexample
+
+@option{-Wvla-parameter} is included in @option{-Wall}. The
+@option{-Warray-parameter} option triggers warnings for similar problems
+involving ordinary array arguments.
+
@item -Wvolatile-register-var
@opindex Wvolatile-register-var
@opindex Wno-volatile-register-var
-/* PR tree-optimization/86614 */
-/* { dg-do compile } */
-/* { dg-options "-O2 -Warray-bounds" } */
+/* PR tree-optimization/86614 - duplicate -Warray-bounds for a strncpy
+ call with out-of-bounds offset
+ { dg-do compile }
+ { dg-options "-O2 -Warray-bounds" } */
-extern char *strncpy (char *, const char *, __SIZE_TYPE__);
+#if __cplusplus
+extern "C"
+#endif
+char *strncpy (char *, const char *, __SIZE_TYPE__);
void sink (void *);
{
int i = (char *)a[1].b - (char *)a + 1;
char *d = a[1].b;
- /* Ensure the same bug is not diagnosed more than once. */
- strncpy (d + i, s, n); /* { dg-warning "array subscript \[0-9]+ is outside array bounds of" } */
- /* { dg-bogus "offset \[0-9]+ is out of the bounds \\\[0, \[0-9]+\\\] of object 'a' with type" "" { target *-*-* } .-1 } */
+ /* Verify the bug is diagnosed exactly once, using either form
+ of the warning. */
+ strncpy (d + i, s, n); /* { dg-warning "array subscript \[0-9]+ is outside array bounds|offset \[0-9]+ is out of the bounds" } */
+ /* { dg-bogus "offset \[0-9]+ is out of the bounds|array subscript \[0-9]+ is outside array bounds" "" { target *-*-* } .-1 } */
}
--- /dev/null
+/* PR c/50584 - No warning for passing small array to C99 static array
+ declarator
+ { dg-do compile }
+ { dg-options "-Wall" } */
+
+// Reduced from Glibc.
+
+typedef struct FILE FILE;
+
+int vfprintf (FILE*, const char*, __builtin_va_list);
+int vfprintf (FILE*, const char*, __builtin_va_list); // { dg-bogus "-Warray-parameter" }
+int vfprintf (FILE*, const char*, __builtin_va_list);
+
+int vfscanf (FILE*, const char*, __builtin_va_list);
+int vfscanf (FILE*, const char*, __builtin_va_list); // { dg-bogus "-Warray-parameter" }
+int vfscanf (FILE*, const char*, __builtin_va_list);
+
+
+/* Verify that mismatches in array/to pointer to va_list are still
+ diagnosed. */
+
+int fva (__builtin_va_list);
+int fva (__builtin_va_list);
+
+int fpva_a1 (__builtin_va_list*);
+int fpva_a1 (__builtin_va_list[1]); // { dg-warning "\\\[-Warray-parameter" }
+
+int fpva_a_ (__builtin_va_list*);
+int fpva_a_ (__builtin_va_list[]);
+int fpva_a_ (__builtin_va_list*);
+int fpva_a_ (__builtin_va_list[]);
+
+/* Also verify that a mismatch between a pointer and a one-element
+ array are diagnosed. This is pervasive in Glibc headers but
+ making an exception for it would leave no way to express
+ the requirement that a function take at least one argument
+ by reference. */
+
+struct __jmp_buf_tag;
+int __sigsetjmp (struct __jmp_buf_tag*, int);
+
+struct __jmp_buf_tag { };
+typedef struct __jmp_buf_tag jmp_buf[1];
+
+int __sigsetjmp (struct __jmp_buf_tag[1], int); // { dg-warning "\\\[-Warray-parameter" }
--- /dev/null
+/* PR c/50584 - No warning for passing small array to C99 static array
+ declarator
+ { dg-do compile }
+ { dg-options "-Wall -Warray-parameter=1" } */
+
+/* Verify that at level 1 mismatches in the bounds of ordinary array
+ parameters don't trigger -Warray-parameter. */
+void fax (int[]);
+void fax (int[0]);
+void fax (int[1]);
+void fax (int[2]);
+void fax (int[3]);
+
+/* Same as above but starting with an array with a specified bound. */
+void gax (int[3]);
+void gax (int[2]);
+void gax (int[1]);
+void gax (int[0]);
+void gax (int[]);
+
+/* Same for multidimensional arrays. */
+void fax_y (int[][3]);
+void fax_y (int[0][3]);
+void fax_y (int[1][3]);
+void fax_y (int[2][3]);
+void fax_y (int[3][3]);
+
+/* Same as above but starting with an array with a specified bound. */
+void gax_y (int[3][5]);
+void gax_y (int[2][5]);
+void gax_y (int[1][5]);
+void gax_y (int[0][5]);
+void gax_y (int[][5]);
+
+/* Exercise VLAs with a mismatch in the bound for an ordinary array. */
+void fvlax_y (int n, int[][n]);
+void fvlax_y (int n, int[0][n]);
+void fvlax_y (int n, int[1][n]);
+void fvlax_y (int n, int[2][n]);
+void fvlax_y (int n, int[3][n]);
+
+void fvlaxn_y (int n, int[][n]);
+void fvlaxn_y (int n, int[0][n]);
+void fvlaxn_y (int n, int[1][n]);
+void fvlaxn_y (int n, int[2][n]);
+void fvlaxn_y (int n, int[3][n]);
+
+void fvlaxx_y (int[][*]);
+void fvlaxx_y (int[0][*]);
+void fvlaxx_y (int[1][*]);
+void fvlaxx_y (int[2][*]);
+void fvlaxx_y (int[3][*]);
+
+/* Verify that mismatches in the bounds of array parameters declared
+ static do trigger -Warray-parameter. */
+void fas1 (int[static 1]); // { dg-message "previously declared as 'int\\\[static 1]'" }
+void fas1 (int[static 2]); // { dg-warning "\\\[-Warray-parameter=" }
+
+
+/* Also verify that -Warray-bounds doesn't trigger for ordinary array
+ parameters... */
+#pragma GCC optimize "2"
+
+__attribute__ ((noipa)) void
+gca3 (char a[3])
+{
+ a[0] = 0; a[1] = 1; a[2] = 2; a[3] = 3;
+}
+
+__attribute__ ((noipa)) void
+gia3 (int a[3])
+{
+ a[0] = 0; a[1] = 1; a[2] = 2; a[3] = 3;
+}
+
+/* ...but does for static arrays. */
+__attribute__ ((noipa)) void
+gcas3 (char a[static 3])
+{
+ a[0] = 0; a[1] = 1; a[2] = 2;
+ a[3] = 3; // { dg-warning "\\\[-Warray-bounds" }
+}
+
+__attribute__ ((noipa)) void
+gias3 (int a[static 3])
+{
+ a[0] = 0; a[1] = 1; a[2] = 2;
+ a[3] = 3; // { dg-warning "\\\[-Warray-bounds" }
+}
--- /dev/null
+/* PR c/50584 - No warning for passing small array to C99 static array
+ declarator
+ Verify warnings for multidimensional arrays, including mismatches
+ in bounds of arrays of VLAs. (Mismatches in variable bounds are
+ diagnosed by -Wvla-parameter.)
+ { dg-do compile }
+ { dg-options "-Wall -Warray-parameter=2" } */
+
+// Verify that equivalent forms don't tigger a warning.
+
+typedef int IA1[1];
+typedef int IA1x_[][1];
+typedef int IA1x0[0][1];
+
+void fia_x1 (int[][1]);
+void fia_x1 (IA1[0]);
+void fia_x1 (IA1x_);
+void fia_x1 (IA1x0);
+void fia_x1 (int[0][1]);
+void fia_x1 (int[static 0][1]);
+
+// Same as above one more time.
+void fia_x1 (int[][1]);
+void fia_x1 (IA1[0]);
+void fia_x1 (IA1x_);
+void fia_x1 (IA1x0);
+void fia_x1 (int[0][1]);
+void fia_x1 (int[static 0][1]);
+
+
+void fia1x1 (int[1][1]);
+void fia1x1 (int[1][1]);
+void fia1x1 (int[static 1][1]);
+
+void fia2x1 (int[2][1]);
+void fia2x1 (int[2][1]);
+void fia2x1 (int[static 2][1]);
+
+void fia1x2 (int[1][2]);
+void fia1x2 (int[1][2]);
+void fia1x2 (int[static 1][2]);
+
+void fia1x1_2x1 (int[1][1]); // { dg-message "previously declared as 'int\\\[1]\\\[1]'" }
+void fia1x1_2x1 (int[2][1]); // { dg-warning "\\\[-Warray-parameter" }
+void fia1x1_2x1 (int[static 1][1]);
+void fia1x1_2x1 (int[static 2][1]); // { dg-warning "\\\[-Warray-parameter" }
+
+
+void fia2x1_1x1 (int[2][1]); // { dg-message "previously declared as 'int\\\[2]\\\[1]'" }
+void fia2x1_1x1 (int[1][1]); // { dg-warning "\\\[-Warray-parameter" }
+void fia2x1_1x1 (int[2][1]);
+void fia2x1_1x1 (int[static 1][1]); // { dg-warning "\\\[-Warray-parameter" }
+void fia2x1_1x1 (int[static 2][1]);
+
+
+extern int n1, n2;
+
+void fca_xn1 (char[][n1]);
+void fca_xn1 (char[0][n1]);
+void fca_xn1 (char[static 0][n1]);
+
+void fca1xn1_2xn1 (char[1][n1]);
+void fca1xn1_2xn1 (char[2][n1]); // { dg-warning "\\\[-Warray-parameter" }
+void fca1xn1_2xn1 (char[1][n1]);
+void fca1xn1_2xn1 (char[static 1][n1]);
+void fca1xn1_2xn1 (char[static 2][n1]); // { dg-warning "\\\[-Warray-parameter" }
+
+
+/* Exercise VLAs with a mismatch in the bound for an ordinary array. */
+void fvlax_y (int n, int[][n]);
+void fvlax_y (int n, int[0][n]);
+void fvlax_y (int n, int[1][n]); // { dg-warning "argument 2 of type 'int\\\[1]\\\[n]' with mismatched bound" }
+void fvlax_y (int n, int[2][n]); // { dg-warning "argument 2 of type 'int\\\[2]\\\[n]' with mismatched bound" }
+void fvlax_y (int n, int[3][n]); // { dg-warning "argument 2 of type 'int\\\[3]\\\[n]' with mismatched bound" }
+
+void fvlaxn_y (int n, int[][n]);
+void fvlaxn_y (int n, int[0][n]);
+void fvlaxn_y (int n, int[1][n]); // { dg-warning "\\\[-Warray-parameter" }
+void fvlaxn_y (int n, int[2][n]); // { dg-warning "\\\[-Warray-parameter" }
+void fvlaxn_y (int n, int[3][n]); // { dg-warning "\\\[-Warray-parameter" }
+
+void fvlaxx_y (int[][*]);
+void fvlaxx_y (int[0][*]);
+void fvlaxx_y (int[1][*]); // { dg-warning "\\\[-Warray-parameter" }
+void fvlaxx_y (int[2][*]); // { dg-warning "\\\[-Warray-parameter" }
+void fvlaxx_y (int[3][*]); // { dg-warning "\\\[-Warray-parameter" }
+
+
+// Verify an array of pointers to an array of function pointers.
+
+void ffpa7_5 (void (* (* (* [7])[5])(void))(void));
+// { dg-message "previously declared as 'void \\\(\\\* ?\\\(\\\* ?\\\(\\\*\\\[7]\\\)\\\[5]\\\)\\\(void\\\)\\\)\\\(void\\\)'" "note" { target *-*-* } .-1 }
+void ffpa7_5 (void (* (* (* [6])[5])(void))(void));
+// { dg-warning "argument 1 of type 'void \\\(\\\* ?\\\(\\\* ?\\\(\\\*\\\[6]\\\)\\\[5]\\\)\\\(void\\\)\\\)\\\(void\\\)' with mismatched bound" "" { target *-*-* } .-1 }
+void ffpa7_5 (void (* (* (* [])[5])(void))(void));
+// { dg-warning "argument 1 of type 'void \\\(\\\* ?\\\(\\\* ?\\\(\\\*\\\[]\\\)\\\[5]\\\)\\\(void\\\)\\\)\\\(void\\\)' with mismatched bound" "" { target *-*-* } .-1 }
+void ffpa7_5 (void (* (* (* (*))[5])(void))(void));
+// { dg-warning "argument 1 of type 'void \\\(\\\* ?\\\(\\\* ?\\\(\\\*\\\*\\\)\\\[5]\\\)\\\(void\\\)\\\)\\\(void\\\)' declared as a pointer" "" { target *-*-* } .-1 }
+
+// Same as above but with array of pointers to a VLA of function pointers.
+void ffpa7_n1 (void (* (* (* [7])[n1])(void))(void));
+// { dg-message "previously declared as 'void \\\(\\\* ?\\\(\\\* ?\\\(\\\*\\\[7]\\\)\\\[n1]\\\)\\\(void\\\)\\\)\\\(void\\\)'" "note" { target *-*-* } .-1 }
+void ffpa7_n1 (void (* (* (* [8])[n1])(void))(void));
+// { dg-warning "argument 1 of type 'void \\\(\\\* ?\\\(\\\* ?\\\(\\\*\\\[8]\\\)\\\[n1]\\\)\\\(void\\\)\\\)\\\(void\\\)' with mismatched bound" "" { target *-*-* } .-1 }
+
+void ffpa9_x (void (* (* (* [9])[*])(void))(void));
+// { dg-message "previously declared as 'void \\\(\\\* ?\\\(\\\* ?\\\(\\\*\\\[9]\\\)\\\[\\\*]\\\)\\\(void\\\)\\\)\\\(void\\\)'" "pr?????" { xfail *-*-* } .-1 }
+// { dg-message "previously declared as 'void \\\(\\\* ?\\\(\\\* ?\\\(\\\*\\\[9]\\\)\\\[0]\\\)\\\(void\\\)\\\)\\\(void\\\)'" "" { target *-*-* } .-2 }
+void ffpa9_x (void (* (* (* [8])[*])(void))(void));
+// { dg-warning "argument 1 of type 'void \\\(\\\* ?\\\(\\\* ?\\\(\\\*\\\[8]\\\)\\\[\\\*]\\\)\\\(void\\\)\\\)\\\(void\\\)' with mismatched bound" "pr?????" { xfail *-*-* } .-1 }
+// { dg-warning "argument 1 of type 'void \\\(\\\* ?\\\(\\\* ?\\\(\\\*\\\[8]\\\)\\\[0]\\\)\\\(void\\\)\\\)\\\(void\\\)' with mismatched bound" "" { target *-*-* } .-2 }
+
+/* Verify a three-dimensional array of pointers to two-dimensional arrays
+ of pointers to function pointers. */
+
+void ffpa7_5_3 (void (* (* (* (* [7])[5])[3])(void))(void));
+// { dg-message "previously declared as 'void ?\\\(\\\* ?\\\(\\\* ?\\\(\\\* ?\\\(\\\* ?\\\[7]\\\)\\\[5]\\\)\\\[3]\\\)\\\(void\\\)\\\)\\\(void\\\)'" "note" { target *-*-* } .-1 }
+void ffpa7_5_3 (void (* (* (* (* [1])[5])[3])(void))(void));
+// { dg-warning "argument 1 of type 'void ?\\\(\\\* ?\\\(\\\* ?\\\(\\\* ?\\\(\\\* ?\\\[1]\\\)\\\[5]\\\)\\\[3]\\\)\\\(void\\\)\\\)\\\(void\\\)' with mismatched bound" "" { target *-*-* } .-1 }
--- /dev/null
+/* PR c/50584 - No warning for passing small array to C99 static array
+ declarator
+ Verify that -Warray-parameter diagnoses mismatches in bounds of
+ arrays between redeclarations of the same function and with pointer
+ parameters pointing to those arrays.
+ { dg-do compile }
+ { dg-options "-Wall -Warray-parameter" } */
+
+void fa_x (int (*)[]); // { dg-message "previously declared as 'int \\\(\\\*\\\)\\\[]'" }
+void fa_x (int (*)[2]); // { dg-warning "\\\[-Warray-parameter" }
+void fa_x (int (*)[2]); // { dg-warning "mismatch in bound 1 of argument 1 declared as 'int \\\(\\\*\\\)\\\[2]'" }
+
+void fa_2 (int (*)[2]); // { dg-message "previously declared as 'int \\\(\\\*\\\)\\\[2]'" }
+void fa_2 (int (*)[]); // { dg-warning "mismatch in bound 1 of argument 1 declared as 'int \\\(\\\*\\\)\\\[]'" }
--- /dev/null
+/* PR c/50584 - No warning for passing small array to C99 static array
+ declarator
+ Verify that -Warray-parameter diagnoses mismatches in array (and
+ pointer) arrguments between redeclarations of the same function.
+ Also verify that the array/pointer argument form in a mismatched
+ redeclaration doesn't override the form in the initial declaration.
+ { dg-do compile }
+ { dg-options "-Wall -Warray-parameter -Wno-vla-paramater" } */
+
+/* Redclarations with the same or equivalent array form should not
+ be dianosed. T[0] is diagnosed by -Wpedantic for being invalid
+ C so there's little point in also warning for the difference in
+ array form. */
+void f1vpp (void**);
+void f1vpp (void*[]);
+void f1vpp (void*[0]);
+
+void f1ia_ (int[]);
+void f1ia_ (int[]);
+void f1ia_ (int[0]);
+/* Verify the unused attribute still has an effect. */
+void f1ia_ (int a[0] __attribute__ ((unused))) { }
+void f1ia_ (int[]);
+
+void f1ia_p (int[]);
+void f1ia_p (int*);
+void f1ia_p (int *p __attribute__ ((unused))) { }
+void f1ia_p (int[]);
+
+void f1p_ia (const int*);
+void f1p_ia (const int[]);
+void f1p_ia (const int *p __attribute__ ((unused))) { }
+void f1p_ia (const int[]);
+
+void f1ia1 (int[1]);
+void f1ia1 (int[1]);
+void f1ia1 (int[2 - 1]);
+
+void f1ias2 (int[static 2]);
+void f1ias2 (int[static 2]);
+void f1ias2 (int[static 1 + 1]);
+void f1ias2 (int a[static 3 - 1]) { (void)&a; }
+
+void f1ipa_ (int*[]);
+void f1ipa_ (int*[]);
+void f1ipa_ (int*[0]);
+
+void f1ia1_x (int[1]); // { dg-message "previously declared as 'int\\\[1]'" }
+void f1ia1_x (int[]); // { dg-warning "argument 1 of type 'int\\\[]' with mismatched bound" }
+void f1ia1_x (int[]); // { dg-warning "argument 1 of type 'int\\\[]' with mismatched bound" }
+void f1ia1_x (int[1]);
+void f1ia1_x (int[2]); // { dg-warning "argument 1 of type 'int\\\[2]' with mismatched bound" }
+void f1ia1_x (int[1]);
+void f1ia1_x (int[3]); // { dg-warning "argument 1 of type 'int\\\[3]' with mismatched bound" }
+void f1ia1_x (int a[1] __attribute__ ((unused))) { }
+
+
+void f1ias2_s3 (int[static 2]); // { dg-message "previously declared as 'int\\\[static 2]'" }
+void f1ias2_s3 (int[static 3]); // { dg-warning "argument 1 of type 'int\\\[static 3]' with mismatched bound" }
+/* Verify the unused attribute still has an effect and doesn't interfere
+ with the warning. */
+void f1ias2_s3 (int a[static 3] __attribute__ ((unused))) { } // { dg-warning "argument 1 of type 'int\\\[static 3]' with mismatched bound" }
+
+
+/* Ordinary T[N] and T[static N] forms are both effectively treated
+ the same but strictly have different meanings so they are diagnosed.
+ It might be worth splitting the warning into two levels and having
+ only the higher level treat the ordinary form as T[static N]. */
+
+void f1ia3_s4 (int[3]); // { dg-message "previously declared as 'int\\\[3]'" }
+void f1ia3_s4 (int[static 4]); // { dg-warning "argument 1 of type 'int\\\[static 4]' with mismatched bound" }
+void f1ia3_s4 (int[3]);
+
+
+void f1ias4_5 (int[static 4]); // { dg-message "previously declared as 'int\\\[static 4]'" }
+void f1ias4_5 (int[5]); // { dg-warning "argument 1 of type 'int\\\[5]' with mismatched bound" }
+void f1ias4_5 (int[static 4]);
+
+
+void f1ia_1 (int[]); // { dg-message "previously declared as 'int\\\[]'" }
+void f1ia_1 (int[1]); // { dg-warning "argument 1 of type 'int\\\[1]' with mismatched bound" }
+void f1ia_1 (int[]);
+
+
+void f1ca_ (char[]); // { dg-message "previously declared as 'char\\\[]'" }
+void f1ca_ (char[2]); // { dg-warning "argument 1 of type 'char\\\[2]' with mismatched bound" }
+void f1ca_ (char[]);
+
+
+void f1csp (const short*); // { dg-message "previously declared as 'const short int ?\\\*'" }
+void f1csp (const short[3]); // { dg-warning "argument 1 of type 'const short int\\\[3]' with mismatched bound" }
+void f1csp (const short*);
+
+
+void f1ia2 (int[2]); // { dg-message "previously declared as 'int\\\[2]'" }
+void f1ia2 (int[1]); // { dg-warning "argument 1 of type 'int\\\[1]' with mismatched bound" }
+void f1ia2 (int[2]);
+
+
+void f1cvla2 (const volatile long[3]); // { dg-message "previously declared as 'const volatile long int\\\[3]'" }
+void f1cvla2 (const volatile long[2]); // { dg-warning "argument 1 of type 'const volatile long int\\\[2]' with mismatched bound" }
+void f1cvla2 (const volatile long[3]);
+void f1cvla2 (const volatile long[restrict 4]); // { dg-warning "argument 1 of type 'const volatile long int\\\[restrict 4]' with mismatched bound" }
+
+
+void f1afa4 (_Atomic float[3]); // { dg-message "previously declared as an array '_Atomic float ?\\\[3]'" }
+void f1afa4 (_Atomic float*); // { dg-warning "argument 1 of type '_Atomic float ?\\\*' declared as a pointer" }
+void f1afa4 (_Atomic float[3]);
+
+void f1ipa1_a2 (int*[1]); // { dg-message "previously declared as 'int \\\*\\\[1]'" }
+void f1ipa1_a2 (int*[2]); // { dg-warning "argument 1 of type 'int \\\*\\\[2]' with mismatched bound" }
+void f1ipa1_a2 (int*[1]);
+
+
+typedef int IAx[];
+typedef int IA1[1];
+typedef int IA2[2];
+typedef int IA3[3];
+
+// The message should differentiate between the [] form and *.
+void f1IAx_A1 (IAx); // { dg-message "previously declared as 'int\\\[]'" "pr?????" { xfail *-*-* } }
+ // { dg-message "previously declared as 'int *\\\*'" "note" { target *-*-* } .-1 }
+void f1IAx_A1 (IA1); // { dg-message "argument 1 of type 'int\\\[1]' with mismatched bound" }
+
+void f1IA1_A2 (IA1); // { dg-message "previously declared as 'int\\\[1]'" }
+void f1IA1_A2 (IA2); // { dg-warning "argument 1 of type 'int\\\[2]' with mismatched bound" }
+void f1IA1_A2 (IA1);
+void f1IA1_A2 (int[2]); // { dg-warning "argument 1 of type 'int\\\[2]' with mismatched bound" }
+
+
+void f1IA1_A3 (IA1 ia1); // { dg-message "previously declared as 'int\\\[1]'" }
+void f1IA1_A3 (IA3 ia3); // { dg-warning "argument 1 of type 'int\\\[3]' with mismatched bound" }
+void f1IA1_A3 (IA1 ia1);
+
+
+void f1IA2_A3 (IA2 a); // { dg-message "previously declared as 'int\\\[2]'" }
+void f1IA2_A3 (IA3 a); // { dg-warning "argument 1 of type 'int\\\[3]' with mismatched bound" }
+void f1IA2_A3 (IA2 a);
+
+
+// Verify multiple array arguments.
+
+void f2a2_a3_3_3 (int[2], int[3]); // { dg-message "previously declared as 'int\\\[2]'" }
+void f2a2_a3_3_3 (int[2], int[3]);
+void f2a2_a3_3_3 (int[3], int[3]); // { dg-warning "argument 1 of type 'int\\\[3]' with mismatched bound" }
+
+
+void f2a2_a3_2_4 (int[2], int[3]); // { dg-message "previously declared as 'int\\\[3]'" }
+void f2a2_a3_2_4 (int[2], int[4]); // { dg-warning "argument 2 of type 'int\\\[4]' with mismatched bound" }
+
+
+/* Verify that pointers to arrays and arrays of arrays are differentiated
+ the same way as pointers and arrays of other types. */
+typedef IA1 *PA1;
+
+void fpia1 (IA1*); // { dg-message "previously declared as 'int ?\\(\\\*\\)\\\[1]'" }
+void fpia1 (IA1[1]); // { dg-warning "argument 1 of type 'int\\\[1]\\\[1]' with mismatched bound" }
+void fpia1 (PA1);
+void fpia1 (int(*)[1]);
+void fpia1 (int[][1]);
+
+void f1vpa1 (void*[][1]);
+void f1vpa1 (void*[0][1]);
+
+/* Verify arrays of pointers. */
+void vaip1 (int (*[3])); // { dg-message "previously declared as 'int *\\\*\\\[3]'" }
+void vaip1 (int (*[5])); // { dg-warning "argument 1 of type 'int *\\\*\\\[5]' with mismatched bound" }
+void vaip1 (int (*[3]));
+void vaip1 (int (*[])); // { dg-warning "argument 1 of type 'int *\\\*\\\[]' with mismatched bound" }
+void vaip1 (int (*[3]));
+
+/* Verify that attributes with arrays don't cause unwanted warnings and
+ don't suppress intended ones. */
+
+#define ALIGN(N)__attribute__ ((aligned (__alignof__ (char[N]))))
+
+void fatipa2 (int (* ALIGN (3)[2])); // { dg-message "previously declared as 'int \\\*\\\[2]'" }
+void fatipa2 (int (* ALIGN (4)[2]));
+void fatipa2 (int (* ALIGN (5)[2]));
+void fatipa2 (int (* ALIGN (7)[3])); // { dg-warning "argument 1 of type 'int \\\*\\\[3]' with mismatched bound" }
+
+void fatiap (int (* ALIGN (3))[2]);
+void fatiap (int (* ALIGN (5))[2]);
+
+
+void fatipa3 (int (* ALIGN (1) (* ALIGN (2))[3]));
+void fatipa3 (int (* ALIGN (1) (* ALIGN (2))[3]));
"" /* { dg-warning "passing argument 2 of .sscanf. from incompatible pointer type" } */
);
}
+
+/* The scanf call may also trigger:
+ { dg-prune-output "-Wstringop-overflow" } */
--- /dev/null
+/* PR c/50584 - No warning for passing small array to C99 static array
+ declarator
+ Verify the -Wvla-parameter warnings correctly diagnose mismatches
+ between multimensional array arguments with one or more variable
+ bounds in redeclarations of the same function.
+ { dg-do compile }
+ { dg-options "-Wall -Wvla-parameter" } */
+
+void fmn_a1n_axn (int n, int[1][n]); // { dg-message "previously declared as 'int\\\[1]\\\[n]' with 1 variable bound" "note" }
+void fmn_a1n_axn (int n, int[*][n]); // { dg-warning "argument 2 of type 'int\\\[\\\*]\\\[n]' declared with 2 variable bounds" }
+
+
+void fmn_axn_a2n (int n, int[*][n]); // { dg-message "previously declared as 'int\\\[\\\*]\\\[n]' with 2 variable bounds" "note" }
+void fmn_axn_a2n (int n, int[2][n]); // { dg-warning "argument 2 of type 'int\\\[2]\\\[n]' declared with 1 variable bound" }
+
+
+void fmn_amn_axn (int m, int n, int[m][n]); // { dg-message "previously declared as 'int\\\[m]\\\[n]' with 0 unspecified variable bounds" "note" }
+void fmn_amn_axn (int m, int n, int[*][n]); // { dg-warning "argument 3 of type 'int\\\[\\\*]\\\[n]' declared with 1 unspecified variable bound" }
+
+// Same as above but a different function name.
+void gmn_amn_axn (int m, int n, int[m][n]); // { dg-message "previously declared as 'int\\\[m]\\\[n]' with 0 unspecified variable bounds" "note" }
+void gmn_amn_axn (int m, int n, int[*][n]); // { dg-warning "argument 3 of type 'int\\\[\\\*]\\\[n]' declared with 1 unspecified variable bound" }
+
+typedef int A7[7];
+
+void fm_A7_m_5 (int m, A7[m][5]); // { dg-message "previously declared as 'int\\\[m]\\\[5]\\\[7]' with bound argument 1" "note" }
+void fm_A7_m_5 (int n, A7[n][5]);
+
+void fm_A7_m_5 (int n, A7[n + 1][5]); // { dg-warning "argument 2 of type 'int\\\[n \\\+ 1]\\\[5]\\\[7]' declared with mismatched bound 'n \\\+ 1'" }
+
+
+int n1, n2, n3, n4, n5, n6, n7, n8, n9;
+void f (int[n1][2][n3][4][n5][6][n7][8][n9]); // { dg-message "previously declared as 'int\\\[n1]\\\[2]\\\[n3]\\\[4]\\\[n5]\\\[6]\\\[n7]\\\[8]\\\[n9]' with 0 unspecified variable bounds" "note" }
+ // { dg-message "with 5 variable bounds" "note" { target *-*-* } .-1 }
+void f (int[n1][2][n3][4][n5][6][n7][8][n9]);
+
+/* Due to a limitation and because [*] is represented the same as [0]
+ only the most significant array bound is rendered as [*]; the others
+ are rendered as [0]. */
+void f (int[n1][2][n3][4][n5][6][n7][8][*]); // { dg-warning "argument 1 of type 'int\\\[n1]\\\[2]\\\[n3]\\\[4]\\\[n5]\\\[6]\\\[n7]\\\[8]\\\[\\\*]' declared with 1 unspecified variable bound" "pr?????" { xfail *-*-* } }
+// { dg-warning "argument 1 of type 'int\\\[n1]\\\[2]\\\[n3]\\\[4]\\\[n5]\\\[6]\\\[n7]\\\[8]\\\[0]' declared with 1 unspecified variable bound" "pr?????" { target *-*-* } .-1 }
+void f (int[n1][2][n3][4][n5][6][*][8][n9]); // { dg-warning "argument 1 of type 'int\\\[n1]\\\[2]\\\[n3]\\\[4]\\\[n5]\\\[6]\\\[\\\*]\\\[8]\\\[n9]' declared with 1 unspecified variable bound" "pr?????" { xfail *-*-* } }
+// { dg-warning "argument 1 of type 'int\\\[n1]\\\[2]\\\[n3]\\\[4]\\\[n5]\\\[6]\\\[0]\\\[8]\\\[n9]' declared with 1 unspecified variable bound" "pr?????" { target *-*-* } .-1 }
+void f (int[n1][2][n3][4][*][6][n7][8][n9]); // { dg-warning "argument 1 of type 'int\\\[n1]\\\[2]\\\[n3]\\\[4]\\\[\\\*]\\\[6]\\\[n7]\\\[8]\\\[n9]' declared with 1 unspecified variable bound" "pr?????" { xfail *-*-*} }
+// { dg-warning "argument 1 of type 'int\\\[n1]\\\[2]\\\[n3]\\\[4]\\\[0]\\\[6]\\\[n7]\\\[8]\\\[n9]' declared with 1 unspecified variable bound" "pr?????" { target *-*-* } .-1 }
+void f (int[n1][2][*][4][n5][6][n7][8][n9]); // { dg-warning "argument 1 of type 'int\\\[n1]\\\[2]\\\[\\\*]\\\[4]\\\[n5]\\\[6]\\\[n7]\\\[8]\\\[n9]' declared with 1 unspecified variable bound" "pr?????" { xfail *-*-* } }
+// { dg-warning "argument 1 of type 'int\\\[n1]\\\[2]\\\[0]\\\[4]\\\[n5]\\\[6]\\\[n7]\\\[8]\\\[n9]' declared with 1 unspecified variable bound" "pr?????" { target *-*-* } .-1 }
+void f (int[*][2][n3][4][n5][6][n7][8][n9]); // { dg-warning "argument 1 of type 'int\\\[\\\*]\\\[2]\\\[n3]\\\[4]\\\[n5]\\\[6]\\\[n7]\\\[8]\\\[n9]' declared with 1 unspecified variable bound" }
+
+void f (int[n1][n2][n3][n4][n5][n6][n7][n8][n9]); // { dg-warning "argument 1 of type 'int\\\[n1]\\\[n2]\\\[n3]\\\[n4]\\\[n5]\\\[n6]\\\[n7]\\\[n8]\\\[n9]' declared with 9 variable bounds" }
+
+// Verify that arrays of pointers to arrays...etc are handled correctly.
+void a2pampan (int (*(*(*[2])[n1])[n2]));
+// { dg-message "previously declared as 'int \\\* \\\(\\\* \\\(\\\*\\\[2]\\\)\\\[n1]\\\)\\\[n2]'" "note" { target *-*-* } .-1 }
+void a2pampan (int (*(*(*[2])[n1])[1]));
+// { dg-warning "argument 1 of type 'int \\\* \\\(\\\* \\\(\\\*\\\[2]\\\)\\\[n1]\\\)\\\[1]' declared with 1 variable bound" "" { target *-*-* } .-1 }
+void a2pampan (int (*(*(*[2])[1])[n2]));
+// { dg-warning "argument 1 of type 'int \\\* \\\(\\\* \\\(\\\*\\\[2]\\\)\\\[1]\\\)\\\[n2]' declared with 1 variable bound" "" { target *-*-* } .-1 }
+void a2pampan (int (*(*(*[2])[n1])[n1]));
+// { dg-warning "argument 1 of type 'int \\\* \\\(\\\* \\\(\\\*\\\[2]\\\)\\\[n1]\\\)\\\[n1]' declared with mismatched bound 'n1'" "" { target *-*-* } .-1 }
+void a2pampan (int (*(*(*[2])[n1])[n2]));
+
+
+/* Verify that the presence or absence of static with VLA dooesn't cause
+ unwanted warnings. */
+
+int f2ia1_1 (int n, int [n][n]); // { sg-message "previously declared as 'int\\\[n]\\\[n]' with bound argument 1" }
+int f2ia1_1 (int n, int[static n][n]);
+int f2ia1_1 (int n, int a[static n][n]) { return sizeof *a; }
+int f2ia1_1 (int n, int[static n + 1][n]); // { dg-warning "argument 2 of type 'int\\\[n \\\+ 1]\\\[n]' declared with mismatched bound 'n \\\+ 1'" }
+
+int f2ias1_1 (int n, int [static n][n]); // { dg-message "previously declared as 'int\\\[n]\\\[n]' with bound argument 1" }
+int f2ias1_1 (int n, int[n][n]);
+int f2ias1_1 (int n, int a[++n][n]) // { dg-warning "argument 2 of type 'int\\\[\\\+\\\+n]\\\[n]' declared with mismatched bound ' ?\\+\\+n'" }
+{ return sizeof *a; }
--- /dev/null
+/* PR c/50584 - No warning for passing small array to C99 static array
+ declarator
+ Verify that redeclarations of functions with pointer parameters to
+ arrays with variable bounds are diagnosed if the bounds don't match
+ either in kind or in the variable expression.
+ { dg-do compile }
+ { dg-options "-Wall -Wvla-parameter" } */
+
+extern int m, n;
+
+void pa_ (int (*)[]); // { dg-message "previously declared as 'int \\\(\\\*\\\)\\\[]'" "note" }
+void pa_ (int (*)[n]); // { dg-warning "\\\[-Wvla-parameter" }
+void pa_ (int (*)[n + 1]); // { dg-warning "mismatch in bound 1 of argument 1 declared as 'int *\\\(\\\*\\\)\\\[n \\\+ 1\\\]'" }
+
+void ppa_ (int (**)[]); // { dg-message "previously declared as 'int \\\(\\\*\\\*\\\)\\\[]'" "note" }
+void ppa_ (int (**)[n]); // { dg-warning "\\\[-Wvla-parameter" }
+void ppa_ (int (**)[n + 1]); // { dg-warning "mismatch in bound 1 of argument 1 declared as 'int \\\(\\\*\\\*\\\)\\\[n \\\+ 1\\\]'" }
+
+void pa1 (int (*)[1]); // { dg-message "previously declared as 'int \\\(\\\*\\\)\\\[1]'" "note" }
+void pa1 (int (*)[n]); // { dg-warning "\\\[-Wvla-parameter" }
+void pa1 (int (*)[1]);
+void pa1 (int (*)[n + 1]); // { dg-warning "mismatch in bound 1 of argument 1 declared as 'int *\\\(\\\*\\\)\\\[n \\\+ 1\\\]'" }
+
+void ppax (int (**)[*]); // { dg-message "previously declared as 'int \\\(\\\*\\\*\\\)\\\[.]'" "note" }
+void ppax (int (**)[n]); // { dg-warning "\\\[-Wvla-parameter" }
+/* A VLA with an unspecified bound is represented the same as [0] so
+ so the pretty printer can't differentiate between the two forms. */
+void ppax (int (**)[1]); // { dg-bogus "\\\[-Warray-parameter" "pr?????" { xfail *-*-* } }
+ // { dg-warning "\\\[-Wvla-parameter" "pr?????" { xfail *-*-* } .-1 }
+void ppax (int (**)[n + 1]); // { dg-warning "mismatch in bound 1 of argument 1 declared as 'int *\\\(\\\*\\\*\\\)\\\[n \\\+ 1\\\]'" }
+
+
+void pa1_n (int (*)[1][n]);
+void pa1_n (int (*)[1][n]);
+void pa1_n (int (*)[*][n]); // { dg-warning "mismatch in bound 1 of argument 1 declared as 'int \\\(\\\*\\\)\\\[\\\*]\\\[n]'" "pr?????" { xfail *-*-*} }
+ // { dg-warning "mismatch in bound 1 of argument 1 declared as 'int \\\(\\\*\\\)\\\[0]\\\[n]'" "pr?????" { target *-*-* } .-1 }
+
+void pa1_n_2 (int (*)[1][n][2]);
+void pa1_n_2 (int (*)[1][n][*]); // { dg-warning "mismatch in bound 3 of argument 1 declared as 'int \\\(\\\*\\\)\\\[1]\\\[n]\\\[\\\*]'" "pr?????" { xfail *-*-* } }
+ // { dg-warning "mismatch in bound 3 of argument 1 declared as 'int \\\(\\\*\\\)\\\[1]\\\[n]\\\[0]'" "pr?????" { target *-*-* } .-1 }
+
+
+void pa1_n_2_a1_n_2 (int (*)[1][n][2], int (*)[1][n][2]);
+// { dg-message "previously declared as 'int \\\(\\\*\\\)\\\[1]\\\[n]\\\[2]'" "note" { target *-*-* } .-1 }
+void pa1_n_2_a1_n_2 (int (*)[1][n][2], int (*)[1][n][n]);
+// { dg-warning "mismatch in bound 3 of argument 2 declared as 'int \\\(\\\*\\\)\\\[1]\\\[n]\\\[n]'" "" { target *-*-* } .-1 }
+void pa1_n_2_a1_n_2 (int (*)[1][n][2], int (*)[1][3][2]);
+// { dg-warning "mismatch in bound 2 of argument 2 declared as 'int \\\(\\\*\\\)\\\[1]\\\[3]\\\[2]'" "" { target *-*-* } .-1 }
+void pa1_n_2_a1_n_2 (int (*)[1][n][2], int (*)[n][n][2]);
+// { dg-warning "mismatch in bound 1 of argument 2 declared as 'int \\\(\\\*\\\)\\\[n]\\\[n]\\\[2]'" "" { target *-*-* } .-1 }
+void pa1_n_2_a1_n_2 (int (*)[1][n][n], int (*)[1][n][2]);
+// { dg-warning "mismatch in bound 3 of argument 1 declared as 'int \\\(\\\*\\\)\\\[1]\\\[n]\\\[n]'" "" { target *-*-* } .-1 }
+void pa1_n_2_a1_n_2 (int (*)[n][n][2], int (*)[1][n][2]);
+// { dg-warning "mismatch in bound 1 of argument 1 declared as 'int \\\(\\\*\\\)\\\[n]\\\[n]\\\[2]'" "" { target *-*-* } .-1 }
+void pa1_n_2_a1_n_2 (int (*)[*][*][*], int (*)[*][*][2]);
+// { dg-warning "mismatch in bounds 1, 2, 3 of argument 1 declared as 'int \\\(\\\*\\\)\\\[.]\\\[.]\\\[.]'" "" { target *-*-* } .-1 }
+// { dg-warning "mismatch in bounds 1, 2 of argument 2 declared as 'int \\\(\\\*\\\)\\\[.]\\\[.]\\\[2]'" "" { target *-*-* } .-2 }
+void pa1_n_2_a1_n_2 (int (*)[1][n][2], int (*)[1][n][2]);
+
+/* Verify that pointers to arrays of pointers to arrays...etc are handled
+ correctly. */
+void pa2pampan (int (*(*(*(*)[2])[m])[n]));
+// { dg-message "previously declared as 'int \\\* \\\(\\\* \\\(\\\* \\\(\\\*\\\)\\\[2]\\\)\\\[m]\\\)\\\[n]'" "note" { target *-*-* } .-1 }
+void pa2pampan (int (*(*(*(*)[2])[m])[1]));
+// { dg-warning "mismatch in bound 3 of argument 1 declared as 'int \\\* \\\(\\\* \\\(\\\* \\\(\\\*\\\)\\\[2]\\\)\\\[m]\\\)\\\[1]'" "" { target *-*-* } .-1 }
+void pa2pampan (int (*(*(*(*)[2])[1])[n]));
+// { dg-warning "mismatch in bound 2 of argument 1 declared as 'int \\\* \\\(\\\* \\\(\\\* \\\(\\\*\\\)\\\[2]\\\)\\\[1]\\\)\\\[n]'" "" { target *-*-* } .-1 }
+void pa2pampan (int (*(*(*(*)[2])[m])[n]));
--- /dev/null
+/* PR c/50584 - No warning for passing small array to C99 static array
+ declarator
+ Verify warnings for redeclarations of functions with pointer parameters
+ to arrays with variable bounds involving typedefs.
+ { dg-do compile }
+ { dg-options "-Wall -Wvla-parameter" } */
+
+extern int m, n;
+
+typedef int IA3[3];
+
+/* Verify the warning points to the declaration with more unspecified
+ bounds, guiding the user to specify them rather than making them all
+ unspecified. */
+void* f_pIA3ax (IA3 *x[*]); // { dg-warning "argument 1 of type 'int \\\(\\\*\\\[\\\*]\\\)\\\[3]' .aka '\[^\n\r\}\]+'. declared with 1 unspecified variable bound" }
+void* f_pIA3ax (IA3 *x[*]);
+void* f_pIA3ax (IA3 *x[n]); // { dg-message "subsequently declared as 'int \\\(\\\*\\\[n]\\\)\\\[3]' with 0 unspecified variable bounds" "note" }
+void* f_pIA3ax (IA3 *x[n]) { return x; }
+
+
+void* f_pIA3an (IA3 *x[n]); // { dg-message "previously declared as 'int \\\(\\\*\\\[n]\\\)\\\[3]' with 0 unspecified variable bounds" "note" }
+void* f_pIA3an (IA3 *x[n]);
+void* f_pIA3an (IA3 *x[*]); // { dg-warning "argument 1 of type 'int \\\(\\\*\\\[\\\*]\\\)\\\[3]' .aka '\[^\n\r\}\]+'. declared with 1 unspecified variable bound" }
+void* f_pIA3an (IA3 *x[n]) { return x; }
+
+
+void nowarn_local_fndecl (void)
+{
+ typedef int IAm[m];
+
+ void* f_IAm (IAm);
+ void* f_IAm (int[m]);
+ void* f_IAm (IAm);
+
+ void* f_iam (int[m]);
+ void* f_iam (IAm);
+ void* f_iam (int[m]);
+
+ typedef int IA3[3];
+ typedef IA3 IAn_3[n];
+ typedef IAn_3 IA2_n_3[2];
+ typedef IA2_n_3 IAm_2_n_3[m];
+
+ void f_IAm_2_n_3 (IAm_2_n_3);
+ void f_IAm_2_n_3 (IA2_n_3[m]);
+ void f_IAm_2_n_3 (IAn_3[m][2]);
+ void f_IAm_2_n_3 (IA3[m][2][n]);
+ void f_IAm_2_n_3 (int[m][2][n][3]);
+
+ void f_iam_2_n_3 (int[m][2][n][3]);
+ void f_iam_2_n_3 (IA3[m][2][n]);
+ void f_iam_2_n_3 (IAn_3[m][2]);
+ void f_iam_2_n_3 (IAm_2_n_3);
+
+ void f_IAx_m_2_n_3 (IAm_2_n_3[*]);
+ void f_IAx_m_2_n_3 (IA2_n_3[*][m]);
+ void f_IAx_m_2_n_3 (IAn_3[*][m][2]);
+ void f_IAx_m_2_n_3 (IA3[*][m][2][n]);
+ void f_IAx_m_2_n_3 (int[*][m][2][n][3]);
+
+ void f_IA__m_2_n_3 (IAm_2_n_3[]);
+ void f_IA__m_2_n_3 (IA2_n_3[][m]);
+ void f_IA__m_2_n_3 (IAn_3[][m][2]);
+ void f_IA__m_2_n_3 (IA3[][m][2][n]);
+ void f_IA__m_2_n_3 (int[][m][2][n][3]);
+}
+
+
+void warn_local_fndecl (void)
+{
+ typedef int IAm[m];
+ typedef int IAn[n];
+
+ void* g_IAm (IAm); // { dg-message "previously declared as 'int\\\[m]' with bound 'm'" }
+ void* g_IAm (int[n]); // { dg-warning "argument 1 of type 'int\\\[n]' declared with mismatched bound 'n'" }
+ void* g_IAm (IAm);
+
+ void* g_iam (int[m]); // { dg-message "previously declared as 'int\\\[m]' with bound 'm'" }
+ void* g_iam (IAn); // { dg-warning "argument 1 of type 'int\\\[n]' declared with mismatched bound 'n'" }
+ void* g_iam (int[m]);
+
+
+ typedef int IA3[3];
+ typedef IA3 IAn_3[n];
+ typedef IAn_3 IA2_n_3[2];
+ typedef IA2_n_3 IAm_2_n_3[m];
+
+ typedef IA3 IAm_3[m];
+ typedef IAm_3 IA2_m_3[2];
+ typedef IA2_m_3 IAm_2_m_3[m];
+
+ void* g_IAm_2_n_3 (IAm_2_n_3);
+ void* g_IAm_2_n_3 (int[m][2][m][3]); // { dg-warning "argument 1 of type 'int\\\[m]\\\[2]\\\[m]\\\[3]' declared with mismatched bound 'm'" }
+ void* g_IAm_2_n_3 (IAm_2_n_3);
+
+ void* g_iam_2_n_2 (int[m][2][n][3]);
+ void* g_iam_2_n_2 (IAm_2_m_3); // { dg-warning "argument 1 of type 'int\\\[m]\\\[2]\\\[m]\\\[3]' declared with mismatched bound 'm'" }
+ void* g_iam_2_n_2 (int[m][2][n][3]);
+}
--- /dev/null
+/* PR c/50584 - No warning for passing small array to C99 static array
+ declarator
+ Verify the -Wvla-parameter warnings correctly diagnose mismatches
+ between one-dimensional VLA and non-VLA arguments in redeclarations
+ of the same function.
+ Also verify that the array/pointer argument form in a mismatched
+ redeclaration doesn't override the form in the initial declaration.
+ { dg-do compile }
+ { dg-options "-Wall -Wvla-parameter" } */
+
+/* Verify that redeclaring an argument as a VLA with an unspecified
+ bound that was first declared as an ordinary array with an unspecified
+ bound triggers a warning. */
+void f1ia_x (int[]); // { dg-message "previously declared as an ordinary array 'int\\\[]'" "note" }
+void f1ia_x (int[*]); // { dg-warning "argument 1 of type 'int\\\[\\\*]' declared as a variable length array" }
+void f1ia_x (int[]);
+void f1ia_x (int[*]); // { dg-warning "argument 1 of type 'int\\\[\\\*]' declared as a variable length array" }
+/* Also verify that a definition of the same form as the first declaration
+ doesn't trigger a warning and doesn't prevent warnings for subsequent
+ mismatches. */
+void f1ia_x (int a[]) { (void)&a;}
+void f1ia_x (int[*]); // { dg-warning "argument 1 of type 'int\\\[\\\*]' declared as a variable length array" }
+
+/* Repeat the above but starting with an ordinary array with a constant
+ bound. */
+void f1ia1x (int[1]); // { dg-message "previously declared as an ordinary array 'int\\\[1]'" "note" }
+void f1ia1x (int[*]); // { dg-warning "argument 1 of type 'int\\\[\\\*]' declared as a variable length array" }
+void f1ia1x (int a[1]) { (void)&a; }
+void f1ia1x (int[1]);
+void f1ia1x (int[*]); // { dg-warning "argument 1 of type 'int\\\[\\\*]' declared as a variable length array" }
+
+void f1ipx (int*); // { dg-message "previously declared as a pointer 'int ?\\\*'" "note" }
+void f1ipx (int[*]); // { dg-warning "argument 1 of type 'int\\\[\\\*]' declared as a variable length array" }
+void f1ipx (int*);
+void f1ipx (int *p) { (void)&p; }
+void f1ipx (int[*]); // { dg-warning "argument 1 of type 'int\\\[\\\*]' declared as a variable length array" }
+void f1ipx (int*);
+
+void f2ipx (int*, int*); // { dg-message "previously declared as a pointer 'int ?\\\*'" "note" }
+void f2ipx (int*, int[*]); // { dg-warning "argument 2 of type 'int\\\[\\\*]' declared as a variable length array" }
+void f2ipx (int*, int*);
+void f2ipx (int*, int[*]); // { dg-warning "argument 2 of type 'int\\\[\\\*]' declared as a variable length array" }
+void f2ipx (int *p, int *q) { (void)&p; (void)&q; }
+void f2ipx (int*, int[*]); // { dg-warning "argument 2 of type 'int\\\[\\\*]' declared as a variable length array" }
+
+void f1ias2x (int[static 2]); // { dg-message "previously declared as an ordinary array 'int\\\[static 2]'" }
+void f1ias2x (int[*]); // { dg-warning "argument 1 of type 'int\\\[\\\*]' declared as a variable length array" }
+void f1ias2x (int[static 2]);
+void f1ias2x (int[*]); // { dg-warning "argument 1 of type 'int\\\[\\\*]' declared as a variable length array" }
+void f1ias2x (int a[static 2]) { (void)&a; }
+void f1ias2x (int[*]); // { dg-warning "argument 1 of type 'int\\\[\\\*]' declared as a variable length array" }
+void f1ias2x (int[static 2]);
+
+extern int nelts;
+
+void f1sa_var (short[]); // { dg-message "previously declared as an ordinary array 'short int\\\[]'" }
+void f1sa_var (short[nelts]); // { dg-warning "argument 1 of type 'short int\\\[nelts]' declared as a variable length array" }
+void f1sa_var (short[]);
+void f1sa_var (short[nelts]); // { dg-warning "argument 1 of type 'short int\\\[nelts]' declared as a variable length array" }
+void f1sa_var (short a[]) { (void)&a; }
+void f1sa_var (short[nelts]); // { dg-warning "argument 1 of type 'short int\\\[nelts]' declared as a variable length array" }
+void f1sa_var (short[]);
+
+void f1sa_expr (int[]); // { dg-message "previously declared as an ordinary array 'int\\\[]'" }
+void f1sa_expr (int[nelts + 1]); // { dg-warning "argument 1 of type 'int\\\[nelts \\\+ 1]' declared as a variable length array" }
+void f1sa_expr (int[]);
+void f1sa_expr (int[nelts * 2]); // { dg-warning "argument 1 of type 'int\\\[nelts \\\* 2]' declared as a variable length array" }
+void f1sa_expr (int a[]) { (void)&a; }
+void f1sa_expr (int[nelts / 3]); // { dg-warning "argument 1 of type 'int\\\[nelts / 3]' declared as a variable length array" }
+void f1sa_expr (int[]);
+
+extern int f (int);
+
+void f1ia_f (int[]); // { dg-message "previously declared as an ordinary array 'int\\\[]'" }
+void f1ia_f (int[f (1)]); // { dg-warning "argument 1 of type 'int\\\[f *\\\(1\\\)]' declared as a variable length array" }
+void f1ia_f (int[]);
+void f1ia_f (int[f (2)]); // { dg-warning "argument 1 of type 'int\\\[f *\\\(2\\\)]' declared as a variable length array" }
+void f1ia_f (int a[]) { (void)&a; }
+void f1ia_f (int[f (3)]); // { dg-warning "argument 1 of type 'int\\\[f *\\\(3\\\)]' declared as a variable length array" }
+void f1ia_f (int[f (4)]); // { dg-warning "argument 1 of type 'int\\\[f *\\\(4\\\)]' declared as a variable length array" }
+void f1ia_f (int[]);
+
+void f1iaf0_f1 (int[f (0)]); // { dg-message "previously declared as 'int\\\[f *\\\(0\\\)]'" }
+void f1iaf0_f1 (int[f (1)]); // { dg-warning "argument 1 of type 'int\\\[f *\\\(1\\\)]' declared with mismatched bound" }
+void f1iaf0_f1 (int[f (0)]);
+void f1iaf0_f1 (int[f (1)]); // { dg-warning "argument 1 of type 'int\\\[f *\\\(1\\\)]' declared with mismatched bound" }
+void f1iaf0_f1 (int a[f (0)]) { (void)&a; }
+void f1iaf0_f1 (int[f (1)]); // { dg-warning "argument 1 of type 'int\\\[f *\\\(1\\\)]' declared with mismatched bound" }
+void f1iaf0_f1 (int[f (0)]);
+
+void f1la_ (long[]); // { dg-message "previously declared as an ordinary array 'long int\\\[]'" }
+void f1la_ (long[nelts]); // { dg-warning "argument 1 of type 'long int\\\[nelts]' declared as a variable length array" }
+void f1la_ (long[]);
+void f1la_ (long a[nelts]) // { dg-warning "argument 1 of type 'long int\\\[nelts]' declared as a variable length array" }
+{ (void)&a; }
+void f1la_ (long[]);
+
+void f2ca_ (int, char[]); // { dg-message "previously declared as an ordinary array 'char\\\[]'" }
+void f2ca_ (int n, char[n]); // { dg-warning "argument 2 of type 'char\\\[n]' declared as a variable length array" }
+void f2ca_ (int, char[]);
+void f2ca_ (int n, char a[n]) // { dg-warning "argument 2 of type 'char\\\[n]' declared as a variable length array" }
+{ (void)&n; (void)&a; }
+
+void f2ia1_f (int n, int[n]); // { dg-message "previously declared as 'int\\\[n]' with bound argument 1" }
+void f2ia1_f (int, int[f (0)]); // { dg-warning "argument 2 of type 'int\\\[f *\\\(0\\\)]' declared with mismatched bound 'f *\\\(0\\\)'" }
+void f2ia1_f (int m, int[m]);
+void f2ia1_f (int, int[f (1)]); // { dg-warning "argument 2 of type 'int\\\[f *\\\(1\\\)]' declared with mismatched bound 'f *\\\(1\\\)'" }
+void f2ia1_f (int x, int a[x]) { (void)&x; (void)&a; }
+void f2ia1_f (int, int[f (2)]); // { dg-warning "argument 2 of type 'int\\\[f *\\\(2\\\)]' declared with mismatched bound 'f *\\\(2\\\)'" }
+void f2ia1_f (int y, int[y]);
+
+void f2iaf_1 (int, int[f (0)]); // { dg-message "previously declared as 'int\\\[f *\\\(0\\\)]'" }
+void f2iaf_1 (int n, int[n]); // { dg-warning "argument 2 of type 'int\\\[n]' declared with mismatched bound argument 1" }
+void f2iaf_1 (int, int[f (0)]);
+void f2iaf_1 (int m, int[m]); // { dg-warning "argument 2 of type 'int\\\[m]' declared with mismatched bound argument 1" }
+void f2iaf_1 (int x, int a[f (0)]) { (void)&x; (void)&a; }
+void f2iaf_1 (int y, int[y]); // { dg-warning "argument 2 of type 'int\\\[y]' declared with mismatched bound argument 1" }
+
+
+void f3ia1 (int n, int, int[n]); // { dg-message "previously declared as 'int\\\[n]' with bound argument 1" }
+void f3ia1 (int, int n, int[n]); // { dg-warning "argument 3 of type 'int\\\[n]' declared with mismatched bound argument 2" }
+void f3ia1 (int n, int, int[n]);
+
+
+extern int g (int);
+
+void f1iaf_g (int[f (1)]); // { dg-message "previously declared as 'int\\\[f *\\\(1\\\)]'" }
+void f1iaf_g (int[g (1)]); // { dg-warning "argument 1 of type 'int\\\[g *\\\(1\\\)]' declared with mismatched bound" }
+void f1iaf_g (int[f (1)]);
+
+
+void nrf1iaf_g (int[f (1)]); // { dg-message "previously declared as 'int\\\[f *\\\(1\\\)]'" }
+__attribute__ ((nonnull))
+void nrf1iaf_g (int[g (1)]); // { dg-warning "argument 1 of type 'int\\\[g *\\\(1\\\)]' declared with mismatched bound" }
+__attribute__ ((noreturn))
+void nrf1iaf_g (int[f (1)]);
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;
pp_string (pp, "atomic ");
if (quals & TYPE_QUAL_CONST)
pp_string (pp, "const ");
- else if (quals & TYPE_QUAL_VOLATILE)
+ if (quals & TYPE_QUAL_VOLATILE)
pp_string (pp, "volatile ");
- else if (quals & TYPE_QUAL_RESTRICT)
+ if (quals & TYPE_QUAL_RESTRICT)
pp_string (pp, "restrict ");
if (!ADDR_SPACE_GENERIC_P (TYPE_ADDR_SPACE (node)))
/* 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;
access_none = 0,
access_read_only = 1,
access_write_only = 2,
- access_read_write = access_read_only | access_write_only
+ access_read_write = access_read_only | access_write_only,
+ access_deferred = 4
};
#define tree_map_eq tree_map_base_eq