Skip to content

Commit 6450f07

Browse files
author
Martin Sebor
committed
Infrastructure & C front end changes for array parameter checking (PR c/50584).
gcc/ChangeLog: PR c/50584 * attribs.c (decl_attributes): Also pass decl along with type attributes to handlers. (init_attr_rdwr_indices): Change second argument to attribute chain. Handle internal attribute representation in addition to external. (get_parm_access): New function. (attr_access::to_internal_string): Define new member function. (attr_access::to_external_string): Define new member function. (attr_access::vla_bounds): Define new member function. * attribs.h (struct attr_access): Declare new members. (attr_access::from_mode_char): Define new member function. (get_parm_access): Declare new function. * calls.c (initialize_argument_information): Pass function type attributes to init_attr_rdwr_indices. * doc/invoke.texi (-Warray-parameter, -Wvla-parameter): Document. * tree-pretty-print.c (dump_generic_node): Correct handling of qualifiers. * tree-ssa-uninit.c (maybe_warn_pass_by_reference): Same. * tree.h (access_mode): Add new enumerator. gcc/c-family/ChangeLog: PR c/50584 * c-attribs.c (c_common_attribute_table): Add "arg spec" attribute. (handle_argspec_attribute): New function. (get_argument, get_argument_type): New functions. (append_access_attrs): Add overload. Handle internal attribute representation in addition to external. (handle_access_attribute): Handle internal attribute representation in addition to external. (build_attr_access_from_parms): New function. gcc/c-family/ChangeLog: PR c/50584 * c-common.h (warn_parm_array_mismatch): Declare new function. (has_attribute): Move declaration of an existing function. (build_attr_access_from_parms): Declare new function. * c-warn.c (parm_array_as_string): Define new function. (plus_one): Define new function. (warn_parm_ptrarray_mismatch): Define new function. (warn_parm_array_mismatch): Define new function. (vla_bound_parm_decl): New function. * c.opt (-Warray-parameter, -Wvla-parameter): New options. * c-pretty-print.c (pp_c_type_qualifier_list): Don't print array type qualifiers here... (c_pretty_printer::direct_abstract_declarator): ...but instead print them in brackets here. Also print [static]. Strip extraneous expressions from VLA bounds. gcc/c/ChangeLog: PR c/50584 * c-decl.c (lookup_last_decl): Define new function. (c_decl_attributes): Call it. (start_decl): Add argument and use it. (finish_decl): Call build_attr_access_from_parms and decl_attributes. (get_parm_array_spec): Define new function. (push_parm_decl): Call get_parm_array_spec. (start_function): Call warn_parm_array_mismatch. Build attribute access and add it to current function. * c-parser.c (c_parser_declaration_or_fndef): Diagnose mismatches in forms of array parameters. * c-tree.h (start_decl): Add argument. gcc/testsuite/ChangeLog: PR c/50584 * gcc.dg/attr-access-read-write-2.c: Adjust text of expected message. * c-c++-common/Warray-bounds-6.c: Correct C++ declaration, adjust text of expected diagnostics. * gcc.dg/Wbuiltin-declaration-mismatch-9.c: Prune expected warning. * gcc.dg/Warray-parameter-2.c: New test. * gcc.dg/Warray-parameter-3.c: New test. * gcc.dg/Warray-parameter-4.c: New test. * gcc.dg/Warray-parameter-5.c: New test. * gcc.dg/Warray-parameter.c: New test. * gcc.dg/Wvla-parameter-2.c: New test. * gcc.dg/Wvla-parameter-3.c: New test. * gcc.dg/Wvla-parameter.c: New test.
1 parent 3696a50 commit 6450f07

27 files changed

+2588
-205
lines changed

gcc/attribs.c

+266-27
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ You should have received a copy of the GNU General Public License
1717
along with GCC; see the file COPYING3. If not see
1818
<http://www.gnu.org/licenses/>. */
1919

20+
#define INCLUDE_STRING
2021
#include "config.h"
2122
#include "system.h"
2223
#include "coretypes.h"
@@ -25,13 +26,15 @@ along with GCC; see the file COPYING3. If not see
2526
#include "stringpool.h"
2627
#include "diagnostic-core.h"
2728
#include "attribs.h"
29+
#include "fold-const.h"
2830
#include "stor-layout.h"
2931
#include "langhooks.h"
3032
#include "plugin.h"
3133
#include "selftest.h"
3234
#include "hash-set.h"
3335
#include "diagnostic.h"
3436
#include "pretty-print.h"
37+
#include "tree-pretty-print.h"
3538
#include "intl.h"
3639

3740
/* Table of the tables of attributes (common, language, format, machine)
@@ -707,10 +710,16 @@ decl_attributes (tree *node, tree attributes, int flags,
707710
int cxx11_flag = (cxx11_attr_p ? ATTR_FLAG_CXX11 : 0);
708711

709712
/* Pass in an array of the current declaration followed
710-
by the last pushed/merged declaration if one exists.
713+
by the last pushed/merged declaration if one exists.
714+
For calls that modify the type attributes of a DECL
715+
and for which *ANODE is *NODE's type, also pass in
716+
the DECL as the third element to use in diagnostics.
711717
If the handler changes CUR_AND_LAST_DECL[0] replace
712718
*ANODE with its value. */
713-
tree cur_and_last_decl[] = { *anode, last_decl };
719+
tree cur_and_last_decl[3] = { *anode, last_decl };
720+
if (anode != node && DECL_P (*node))
721+
cur_and_last_decl[2] = *node;
722+
714723
tree ret = (spec->handler) (cur_and_last_decl, name, args,
715724
flags|cxx11_flag, &no_add_attrs);
716725

@@ -2017,65 +2026,295 @@ maybe_diag_alias_attributes (tree alias, tree target)
20172026
}
20182027
}
20192028

2020-
/* Initialize a mapping for a call to function FNDECL declared with
2021-
attribute access. Each attribute positional operand inserts one
2022-
entry into the mapping with the operand number as the key. */
2029+
/* Initialize a mapping RWM for a call to a function declared with
2030+
attribute access in ATTRS. Each attribute positional operand
2031+
inserts one entry into the mapping with the operand number as
2032+
the key. */
20232033

20242034
void
2025-
init_attr_rdwr_indices (rdwr_map *rwm, tree fntype)
2035+
init_attr_rdwr_indices (rdwr_map *rwm, tree attrs)
20262036
{
2027-
if (!fntype)
2037+
if (!attrs)
20282038
return;
20292039

2030-
for (tree access = TYPE_ATTRIBUTES (fntype);
2040+
for (tree access = attrs;
20312041
(access = lookup_attribute ("access", access));
20322042
access = TREE_CHAIN (access))
20332043
{
20342044
/* The TREE_VALUE of an attribute is a TREE_LIST whose TREE_VALUE
20352045
is the attribute argument's value. */
20362046
tree mode = TREE_VALUE (access);
2037-
gcc_assert (TREE_CODE (mode) == TREE_LIST);
2047+
if (!mode)
2048+
return;
2049+
2050+
/* The (optional) list of VLA bounds. */
2051+
tree vblist = TREE_CHAIN (mode);
2052+
20382053
mode = TREE_VALUE (mode);
2054+
if (TREE_CODE (mode) != STRING_CST)
2055+
continue;
20392056
gcc_assert (TREE_CODE (mode) == STRING_CST);
20402057

2041-
const char *modestr = TREE_STRING_POINTER (mode);
2042-
for (const char *m = modestr; *m; )
2058+
for (const char *m = TREE_STRING_POINTER (mode); *m; )
20432059
{
20442060
attr_access acc = { };
20452061

2046-
switch (*m)
2062+
/* Skip the internal-only plus sign. */
2063+
if (*m == '+')
2064+
++m;
2065+
2066+
acc.str = m;
2067+
acc.mode = acc.from_mode_char (*m);
2068+
acc.sizarg = UINT_MAX;
2069+
2070+
const char *end;
2071+
acc.ptrarg = strtoul (++m, const_cast<char**>(&end), 10);
2072+
m = end;
2073+
2074+
if (*m == '[')
20472075
{
2048-
case 'r': acc.mode = access_read_only; break;
2049-
case 'w': acc.mode = access_write_only; break;
2050-
case 'x': acc.mode = access_read_write; break;
2051-
case '-': acc.mode = access_none; break;
2052-
default: gcc_unreachable ();
2076+
/* Forms containing the square bracket are internal-only
2077+
(not specified by an attribute declaration), and used
2078+
for various forms of array and VLA parameters. */
2079+
acc.internal_p = true;
2080+
2081+
/* Search to the closing bracket and look at the preceding
2082+
code: it determines the form of the most significant
2083+
bound of the array. Others prior to it encode the form
2084+
of interior VLA bounds. They're not of interest here. */
2085+
end = strchr (m, ']');
2086+
const char *p = end;
2087+
gcc_assert (p);
2088+
2089+
while (ISDIGIT (p[-1]))
2090+
--p;
2091+
2092+
if (ISDIGIT (*p))
2093+
{
2094+
/* A digit denotes a constant bound (as in T[3]). */
2095+
acc.static_p = p[-1] == 's';
2096+
acc.minsize = strtoull (p, NULL, 10);
2097+
}
2098+
else if (' ' == p[-1])
2099+
{
2100+
/* A space denotes an ordinary array of unspecified bound
2101+
(as in T[]). */
2102+
acc.minsize = 0;
2103+
}
2104+
else if ('*' == p[-1] || '$' == p[-1])
2105+
{
2106+
/* An asterisk denotes a VLA. When the closing bracket
2107+
is followed by a comma and a dollar sign its bound is
2108+
on the list. Otherwise it's a VLA with an unspecified
2109+
bound. */
2110+
acc.minsize = HOST_WIDE_INT_M1U;
2111+
}
2112+
2113+
m = end + 1;
20532114
}
20542115

2055-
char *end;
2056-
acc.ptrarg = strtoul (++m, &end, 10);
2057-
m = end;
20582116
if (*m == ',')
20592117
{
2060-
acc.sizarg = strtoul (++m, &end, 10);
2061-
m = end;
2118+
++m;
2119+
do
2120+
{
2121+
if (*m == '$')
2122+
{
2123+
++m;
2124+
if (!acc.size)
2125+
{
2126+
/* Extract the list of VLA bounds for the current
2127+
parameter, store it in ACC.SIZE, and advance
2128+
to the list of bounds for the next VLA parameter.
2129+
*/
2130+
acc.size = TREE_VALUE (vblist);
2131+
vblist = TREE_CHAIN (vblist);
2132+
}
2133+
}
2134+
2135+
if (ISDIGIT (*m))
2136+
{
2137+
/* Extract the positional argument. It's absent
2138+
for VLAs whose bound doesn't name a function
2139+
parameter. */
2140+
unsigned pos = strtoul (m, const_cast<char**>(&end), 10);
2141+
if (acc.sizarg == UINT_MAX)
2142+
acc.sizarg = pos;
2143+
m = end;
2144+
}
2145+
}
2146+
while (*m == '$');
2147+
}
2148+
2149+
acc.end = m;
2150+
2151+
bool existing;
2152+
auto &ref = rwm->get_or_insert (acc.ptrarg, &existing);
2153+
if (existing)
2154+
{
2155+
/* Merge the new spec with the existing. */
2156+
if (acc.minsize == HOST_WIDE_INT_M1U)
2157+
ref.minsize = HOST_WIDE_INT_M1U;
2158+
2159+
if (acc.sizarg != UINT_MAX)
2160+
ref.sizarg = acc.sizarg;
2161+
2162+
if (acc.mode)
2163+
ref.mode = acc.mode;
20622164
}
20632165
else
2064-
acc.sizarg = UINT_MAX;
2065-
2066-
acc.ptr = NULL_TREE;
2067-
acc.size = NULL_TREE;
2166+
ref = acc;
20682167

20692168
/* Unconditionally add an entry for the required pointer
20702169
operand of the attribute, and one for the optional size
20712170
operand when it's specified. */
2072-
rwm->put (acc.ptrarg, acc);
20732171
if (acc.sizarg != UINT_MAX)
20742172
rwm->put (acc.sizarg, acc);
20752173
}
20762174
}
20772175
}
20782176

2177+
/* Return the access specification for a function parameter PARM
2178+
or null if the current function has no such specification. */
2179+
2180+
attr_access *
2181+
get_parm_access (rdwr_map &rdwr_idx, tree parm,
2182+
tree fndecl /* = current_function_decl */)
2183+
{
2184+
tree fntype = TREE_TYPE (fndecl);
2185+
init_attr_rdwr_indices (&rdwr_idx, TYPE_ATTRIBUTES (fntype));
2186+
2187+
if (rdwr_idx.is_empty ())
2188+
return NULL;
2189+
2190+
unsigned argpos = 0;
2191+
tree fnargs = DECL_ARGUMENTS (fndecl);
2192+
for (tree arg = fnargs; arg; arg = TREE_CHAIN (arg), ++argpos)
2193+
if (arg == parm)
2194+
return rdwr_idx.get (argpos);
2195+
2196+
return NULL;
2197+
}
2198+
2199+
/* Return the internal representation as STRING_CST. Internal positional
2200+
arguments are zero-based. */
2201+
2202+
tree
2203+
attr_access::to_internal_string () const
2204+
{
2205+
return build_string (end - str, str);
2206+
}
2207+
2208+
/* Return the human-readable representation of the external attribute
2209+
specification (as it might appear in the source code) as STRING_CST.
2210+
External positional arguments are one-based. */
2211+
2212+
tree
2213+
attr_access::to_external_string () const
2214+
{
2215+
char buf[80];
2216+
gcc_assert (mode != access_deferred);
2217+
int len = snprintf (buf, sizeof buf, "access (%s, %u",
2218+
mode_names[mode], ptrarg + 1);
2219+
if (sizarg != UINT_MAX)
2220+
len += snprintf (buf + len, sizeof buf - len, ", %u", sizarg + 1);
2221+
strcpy (buf + len, ")");
2222+
return build_string (len + 2, buf);
2223+
}
2224+
2225+
/* Return the number of specified VLA bounds and set *nunspec to
2226+
the number of unspecified ones (those designated by [*]). */
2227+
2228+
unsigned
2229+
attr_access::vla_bounds (unsigned *nunspec) const
2230+
{
2231+
*nunspec = 0;
2232+
for (const char* p = strrchr (str, ']'); p && *p != '['; --p)
2233+
if (*p == '*')
2234+
++*nunspec;
2235+
return list_length (size);
2236+
}
2237+
2238+
2239+
/* Defined in attr_access. */
2240+
constexpr char attr_access::mode_chars[];
2241+
constexpr char attr_access::mode_names[][11];
2242+
2243+
/* Format an array, including a VLA, pointed to by TYPE and used as
2244+
a function parameter as a human-readable string. ACC describes
2245+
an access to the parameter and is used to determine the outermost
2246+
form of the array including its bound which is otherwise obviated
2247+
by its decay to pointer. Return the formatted string. */
2248+
2249+
std::string
2250+
attr_access::array_as_string (tree type) const
2251+
{
2252+
std::string typstr;
2253+
2254+
if (type == error_mark_node)
2255+
return std::string ();
2256+
2257+
if (this->str)
2258+
{
2259+
/* For array parameters (but not pointers) create an array type
2260+
that corresponds to the form of the parameter including its
2261+
qualifiers even though they apply to the pointer, not the array
2262+
type. */
2263+
const bool vla_p = minsize == HOST_WIDE_INT_M1U;
2264+
tree eltype = TREE_TYPE (type);
2265+
tree artype;
2266+
2267+
tree index_type = NULL_TREE;
2268+
if (minsize == HOST_WIDE_INT_M1U)
2269+
{
2270+
/* Determine if this is a VLA (an array whose most significant
2271+
bound is nonconstant and whose access string has "$]" in it)
2272+
extract the bound expression from SIZE. */
2273+
const char *p = end;
2274+
for ( ; *p-- != ']'; );
2275+
if (*p == '$')
2276+
index_type = build_index_type (TREE_VALUE (size));
2277+
}
2278+
else if (minsize)
2279+
index_type = build_index_type (size_int (minsize - 1));
2280+
2281+
artype = build_array_type (eltype, index_type);
2282+
2283+
if (static_p || vla_p)
2284+
{
2285+
tree flag = static_p ? integer_one_node : NULL_TREE;
2286+
/* Hack: there's no language-independent way to encode
2287+
the "static" specifier or the "*" notation in an array type.
2288+
Temporarily add an attribute to have the pretty printer add
2289+
"static" or "*", and remove it later. The static notation
2290+
is only valid in the most significant bound but [*] can be
2291+
used for any bound. Because [*] is represented the same as
2292+
[0] this hack only works for the most significant bound like
2293+
static and the others are rendered as [0]. */
2294+
tree at = tree_cons (get_identifier ("array"), flag, NULL_TREE);
2295+
TYPE_ATTRIBUTES (artype) = at;
2296+
}
2297+
2298+
TYPE_ATOMIC (artype) = TYPE_ATOMIC (type);
2299+
TYPE_READONLY (artype) = TYPE_READONLY (type);
2300+
TYPE_RESTRICT (artype) = TYPE_RESTRICT (type);
2301+
TYPE_VOLATILE (artype) = TYPE_VOLATILE (type);
2302+
type = artype;
2303+
}
2304+
2305+
/* Format the type using the current pretty printer. The generic tree
2306+
printer does a terrible job. */
2307+
pretty_printer *pp = global_dc->printer->clone ();
2308+
pp_printf (pp, "%qT", type);
2309+
typstr = pp_formatted_text (pp);
2310+
delete pp;
2311+
2312+
if (this->str)
2313+
/* Remove the attribute that wasn't installed by decl_attributes. */
2314+
TYPE_ATTRIBUTES (type) = NULL_TREE;
2315+
2316+
return typstr;
2317+
}
20792318

20802319
#if CHECKING_P
20812320

0 commit comments

Comments
 (0)