aboutsummaryrefslogtreecommitdiff
path: root/gcc/c/c-decl.c
diff options
context:
space:
mode:
authorMartin Sebor <msebor@redhat.com>2020-09-19 17:21:52 -0600
committerMartin Sebor <msebor@redhat.com>2020-09-19 17:34:31 -0600
commit6450f07388f9fe575a489c9309c36012b17b88b0 (patch)
tree929351afdf436f119f15a3d37da96d3a0e3ae27c /gcc/c/c-decl.c
parent3696a50beeb73f4ded8a584e76ee16f0bde109b9 (diff)
downloadgcc-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/c/c-decl.c')
-rw-r--r--gcc/c/c-decl.c223
1 files changed, 214 insertions, 9 deletions
diff --git a/gcc/c/c-decl.c b/gcc/c/c-decl.c
index 8204db2..81b9adb 100644
--- a/gcc/c/c-decl.c
+++ b/gcc/c/c-decl.c
@@ -25,6 +25,7 @@ along with GCC; see the file COPYING3. If not see
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"
@@ -58,6 +59,8 @@ along with GCC; see the file COPYING3. If not see
#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 */
@@ -4970,6 +4973,17 @@ groktypename (struct c_type_name *type_name, tree *expr,
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. */
@@ -4998,10 +5012,7 @@ c_decl_attributes (tree *node, tree attributes, int flags)
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);
}
@@ -5011,6 +5022,8 @@ c_decl_attributes (tree *node, tree attributes, int flags)
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,
@@ -5023,7 +5036,7 @@ c_decl_attributes (tree *node, tree attributes, int flags)
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;
@@ -5041,6 +5054,10 @@ start_decl (struct c_declarator *declarator, struct c_declspecs *declspecs,
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));
@@ -5478,6 +5495,14 @@ finish_decl (tree decl, location_t init_loc, tree init,
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
{
@@ -5630,6 +5655,175 @@ grokparm (const struct c_parm *parm, tree *expr)
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
@@ -5639,12 +5833,12 @@ void
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);
@@ -9228,6 +9422,7 @@ start_function (struct c_declspecs *declspecs, struct c_declarator *declarator,
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;
@@ -9358,12 +9553,22 @@ start_function (struct c_declspecs *declspecs, struct c_declarator *declarator,
"%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 (&current_function_decl, access, 0, old_decl);
+
push_scope ();
declare_parm_level ();