diff options
author | Martin Sebor <msebor@redhat.com> | 2020-09-19 17:21:52 -0600 |
---|---|---|
committer | Martin Sebor <msebor@redhat.com> | 2020-09-19 17:34:31 -0600 |
commit | 6450f07388f9fe575a489c9309c36012b17b88b0 (patch) | |
tree | 929351afdf436f119f15a3d37da96d3a0e3ae27c /gcc/attribs.c | |
parent | 3696a50beeb73f4ded8a584e76ee16f0bde109b9 (diff) | |
download | gcc-6450f07388f9fe575a489c9309c36012b17b88b0.zip gcc-6450f07388f9fe575a489c9309c36012b17b88b0.tar.gz gcc-6450f07388f9fe575a489c9309c36012b17b88b0.tar.bz2 |
Infrastructure & C front end changes for array parameter checking (PR c/50584).
gcc/ChangeLog:
PR c/50584
* attribs.c (decl_attributes): Also pass decl along with type
attributes to handlers.
(init_attr_rdwr_indices): Change second argument to attribute chain.
Handle internal attribute representation in addition to external.
(get_parm_access): New function.
(attr_access::to_internal_string): Define new member function.
(attr_access::to_external_string): Define new member function.
(attr_access::vla_bounds): Define new member function.
* attribs.h (struct attr_access): Declare new members.
(attr_access::from_mode_char): Define new member function.
(get_parm_access): Declare new function.
* calls.c (initialize_argument_information): Pass function type
attributes to init_attr_rdwr_indices.
* doc/invoke.texi (-Warray-parameter, -Wvla-parameter): Document.
* tree-pretty-print.c (dump_generic_node): Correct handling of
qualifiers.
* tree-ssa-uninit.c (maybe_warn_pass_by_reference): Same.
* tree.h (access_mode): Add new enumerator.
gcc/c-family/ChangeLog:
PR c/50584
* c-attribs.c (c_common_attribute_table): Add "arg spec" attribute.
(handle_argspec_attribute): New function.
(get_argument, get_argument_type): New functions.
(append_access_attrs): Add overload. Handle internal attribute
representation in addition to external.
(handle_access_attribute): Handle internal attribute representation
in addition to external.
(build_attr_access_from_parms): New function.
gcc/c-family/ChangeLog:
PR c/50584
* c-common.h (warn_parm_array_mismatch): Declare new function.
(has_attribute): Move declaration of an existing function.
(build_attr_access_from_parms): Declare new function.
* c-warn.c (parm_array_as_string): Define new function.
(plus_one): Define new function.
(warn_parm_ptrarray_mismatch): Define new function.
(warn_parm_array_mismatch): Define new function.
(vla_bound_parm_decl): New function.
* c.opt (-Warray-parameter, -Wvla-parameter): New options.
* c-pretty-print.c (pp_c_type_qualifier_list): Don't print array type
qualifiers here...
(c_pretty_printer::direct_abstract_declarator): ...but instead print
them in brackets here. Also print [static]. Strip extraneous
expressions from VLA bounds.
gcc/c/ChangeLog:
PR c/50584
* c-decl.c (lookup_last_decl): Define new function.
(c_decl_attributes): Call it.
(start_decl): Add argument and use it.
(finish_decl): Call build_attr_access_from_parms and decl_attributes.
(get_parm_array_spec): Define new function.
(push_parm_decl): Call get_parm_array_spec.
(start_function): Call warn_parm_array_mismatch. Build attribute
access and add it to current function.
* c-parser.c (c_parser_declaration_or_fndef): Diagnose mismatches
in forms of array parameters.
* c-tree.h (start_decl): Add argument.
gcc/testsuite/ChangeLog:
PR c/50584
* gcc.dg/attr-access-read-write-2.c: Adjust text of expected message.
* c-c++-common/Warray-bounds-6.c: Correct C++ declaration, adjust
text of expected diagnostics.
* gcc.dg/Wbuiltin-declaration-mismatch-9.c: Prune expected warning.
* gcc.dg/Warray-parameter-2.c: New test.
* gcc.dg/Warray-parameter-3.c: New test.
* gcc.dg/Warray-parameter-4.c: New test.
* gcc.dg/Warray-parameter-5.c: New test.
* gcc.dg/Warray-parameter.c: New test.
* gcc.dg/Wvla-parameter-2.c: New test.
* gcc.dg/Wvla-parameter-3.c: New test.
* gcc.dg/Wvla-parameter.c: New test.
Diffstat (limited to 'gcc/attribs.c')
-rw-r--r-- | gcc/attribs.c | 293 |
1 files changed, 266 insertions, 27 deletions
diff --git a/gcc/attribs.c b/gcc/attribs.c index 7c0c18f..abc7536 100644 --- a/gcc/attribs.c +++ b/gcc/attribs.c @@ -17,6 +17,7 @@ You should have received a copy of the GNU General Public License along with GCC; see the file COPYING3. If not see <http://www.gnu.org/licenses/>. */ +#define INCLUDE_STRING #include "config.h" #include "system.h" #include "coretypes.h" @@ -25,6 +26,7 @@ along with GCC; see the file COPYING3. If not see #include "stringpool.h" #include "diagnostic-core.h" #include "attribs.h" +#include "fold-const.h" #include "stor-layout.h" #include "langhooks.h" #include "plugin.h" @@ -32,6 +34,7 @@ along with GCC; see the file COPYING3. If not see #include "hash-set.h" #include "diagnostic.h" #include "pretty-print.h" +#include "tree-pretty-print.h" #include "intl.h" /* Table of the tables of attributes (common, language, format, machine) @@ -707,10 +710,16 @@ decl_attributes (tree *node, tree attributes, int flags, int cxx11_flag = (cxx11_attr_p ? ATTR_FLAG_CXX11 : 0); /* Pass in an array of the current declaration followed - by the last pushed/merged declaration if one exists. + by the last pushed/merged declaration if one exists. + For calls that modify the type attributes of a DECL + and for which *ANODE is *NODE's type, also pass in + the DECL as the third element to use in diagnostics. If the handler changes CUR_AND_LAST_DECL[0] replace *ANODE with its value. */ - tree cur_and_last_decl[] = { *anode, last_decl }; + tree cur_and_last_decl[3] = { *anode, last_decl }; + if (anode != node && DECL_P (*node)) + cur_and_last_decl[2] = *node; + tree ret = (spec->handler) (cur_and_last_decl, name, args, flags|cxx11_flag, &no_add_attrs); @@ -2017,65 +2026,295 @@ maybe_diag_alias_attributes (tree alias, tree target) } } -/* Initialize a mapping for a call to function FNDECL declared with - attribute access. Each attribute positional operand inserts one - entry into the mapping with the operand number as the key. */ +/* Initialize a mapping RWM for a call to a function declared with + attribute access in ATTRS. Each attribute positional operand + inserts one entry into the mapping with the operand number as + the key. */ void -init_attr_rdwr_indices (rdwr_map *rwm, tree fntype) +init_attr_rdwr_indices (rdwr_map *rwm, tree attrs) { - if (!fntype) + if (!attrs) return; - for (tree access = TYPE_ATTRIBUTES (fntype); + for (tree access = attrs; (access = lookup_attribute ("access", access)); access = TREE_CHAIN (access)) { /* The TREE_VALUE of an attribute is a TREE_LIST whose TREE_VALUE is the attribute argument's value. */ tree mode = TREE_VALUE (access); - gcc_assert (TREE_CODE (mode) == TREE_LIST); + if (!mode) + return; + + /* The (optional) list of VLA bounds. */ + tree vblist = TREE_CHAIN (mode); + mode = TREE_VALUE (mode); + if (TREE_CODE (mode) != STRING_CST) + continue; gcc_assert (TREE_CODE (mode) == STRING_CST); - const char *modestr = TREE_STRING_POINTER (mode); - for (const char *m = modestr; *m; ) + for (const char *m = TREE_STRING_POINTER (mode); *m; ) { attr_access acc = { }; - switch (*m) + /* Skip the internal-only plus sign. */ + if (*m == '+') + ++m; + + acc.str = m; + acc.mode = acc.from_mode_char (*m); + acc.sizarg = UINT_MAX; + + const char *end; + acc.ptrarg = strtoul (++m, const_cast<char**>(&end), 10); + m = end; + + if (*m == '[') { - case 'r': acc.mode = 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 |