aboutsummaryrefslogtreecommitdiff
path: root/gcc/c-family
diff options
context:
space:
mode:
authorMartin Sebor <msebor@redhat.com>2019-11-22 17:14:17 +0000
committerMartin Sebor <msebor@gcc.gnu.org>2019-11-22 10:14:17 -0700
commit54aa6b58fe2fe73bbe67e0485777e0c410a18673 (patch)
tree11281ae8b5d5a5f2a57ffc60d7d8cfe951a953d8 /gcc/c-family
parentb5338fb359ea3480d6ed37bbc52fe2df49b82fb9 (diff)
downloadgcc-54aa6b58fe2fe73bbe67e0485777e0c410a18673.zip
gcc-54aa6b58fe2fe73bbe67e0485777e0c410a18673.tar.gz
gcc-54aa6b58fe2fe73bbe67e0485777e0c410a18673.tar.bz2
PR middle-end/83859 - attributes to associate pointer arguments and sizes
gcc/ChangeLog: PR middle-end/83859 * attribs.h (struct attr_access): New. * attribs.c (decl_attributes): Add an informational note. * builtins.c (check_access): Make extern. Consistently set no-warning after issuing a warning. Handle calls through function pointers. Set no-warning. * builtins.h (check_access): Declare. * calls.c (rdwr_access_hash): New type. (rdwr_map): Same. (init_attr_rdwr_indices): New function. (maybe_warn_rdwr_sizes): Same. (initialize_argument_information): Call init_attr_rdwr_indices. Call maybe_warn_rdwr_sizes. (get_size_range): Avoid null argument. * doc/extend.texi (attribute access): Document new attribute. gcc/c-family/ChangeLog: PR middle-end/83859 * c-attribs.c (handle_access_attribute): New function. (c_common_attribute_table): Add new attribute. (get_argument_type): New function. (append_access_attrs): New function. (get_nonnull_operand): Rename... (get_attribute_operand): ...to this. * c-common.c (get_nonnull_operand): Rename... (get_attribute_operand): ...to this. gcc/testsuite/ChangeLog: PR middle-end/83859 * c-c++-common/attr-nonstring-8.c: Adjust text of expected warning. * gcc.dg/Wstringop-overflow-23.c: New test. * gcc.dg/Wstringop-overflow-24.c: New test. * gcc.dg/attr-access-read-only.c: New test. * gcc.dg/attr-access-read-write.c: New test. * gcc.dg/attr-access-read-write-2.c: New test. * gcc.dg/attr-access-write-only.c: New test. From-SVN: r278624
Diffstat (limited to 'gcc/c-family')
-rw-r--r--gcc/c-family/ChangeLog12
-rw-r--r--gcc/c-family/c-attribs.c388
-rw-r--r--gcc/c-family/c-common.c8
-rw-r--r--gcc/c-family/c-common.h2
4 files changed, 404 insertions, 6 deletions
diff --git a/gcc/c-family/ChangeLog b/gcc/c-family/ChangeLog
index b1dc0bb..9c93f7f 100644
--- a/gcc/c-family/ChangeLog
+++ b/gcc/c-family/ChangeLog
@@ -1,3 +1,15 @@
+2019-11-22 Martin Sebor <msebor@redhat.com>
+
+ PR middle-end/83859
+ * c-attribs.c (handle_access_attribute): New function.
+ (c_common_attribute_table): Add new attribute.
+ (get_argument_type): New function.
+ (append_access_attrs): New function.
+ (get_nonnull_operand): Rename...
+ (get_attribute_operand): ...to this.
+ * c-common.c (get_nonnull_operand): Rename...
+ (get_attribute_operand): ...to this.
+
2019-11-21 Joseph Myers <joseph@codesourcery.com>
* c-attribs.c (handle_fallthrough_attribute): Use pedwarn instead
diff --git a/gcc/c-family/c-attribs.c b/gcc/c-family/c-attribs.c
index b727f66..e307160 100644
--- a/gcc/c-family/c-attribs.c
+++ b/gcc/c-family/c-attribs.c
@@ -123,6 +123,8 @@ static tree handle_nothrow_attribute (tree *, tree, tree, int, bool *);
static tree handle_cleanup_attribute (tree *, tree, tree, int, bool *);
static tree handle_warn_unused_result_attribute (tree *, tree, tree, int,
bool *);
+static tree handle_access_attribute (tree *, tree, tree, int, bool *);
+
static tree handle_sentinel_attribute (tree *, tree, tree, int, bool *);
static tree handle_type_generic_attribute (tree *, tree, tree, int, bool *);
static tree handle_alloc_size_attribute (tree *, tree, tree, int, bool *);
@@ -477,6 +479,8 @@ const struct attribute_spec c_common_attribute_table[] =
handle_copy_attribute, NULL },
{ "noinit", 0, 0, true, false, false, false,
handle_noinit_attribute, attr_noinit_exclusions },
+ { "access", 1, 3, false, true, true, false,
+ handle_access_attribute, NULL },
{ NULL, 0, 0, false, false, false, false, NULL, NULL }
};
@@ -508,7 +512,8 @@ attribute_takes_identifier_p (const_tree attr_id)
return true;
else if (!strcmp ("mode", spec->name)
|| !strcmp ("format", spec->name)
- || !strcmp ("cleanup", spec->name))
+ || !strcmp ("cleanup", spec->name)
+ || !strcmp ("access", spec->name))
return true;
else
return targetm.attribute_takes_identifier_p (attr_id);
@@ -3795,6 +3800,387 @@ handle_nonstring_attribute (tree *node, tree name, tree ARG_UNUSED (args),
return NULL_TREE;
}
+/* Given a function type FUNCTYPE, returns the type of the parameter
+ ARGNO or null if ARGNO exceeds the number of parameters. On failure
+ set *NARGS to the number of function parameters. */
+
+static tree
+get_argument_type (tree functype, unsigned argno, unsigned *nargs)
+{
+ function_args_iterator iter;
+ function_args_iter_init (&iter, functype);
+
+ unsigned count = 0;
+
+ for ( ; iter.next; ++count, function_args_iter_next (&iter))
+ {
+ if (count + 1 == argno)
+ {
+ tree argtype = function_args_iter_cond (&iter);
+ if (VOID_TYPE_P (argtype))
+ break;
+ return argtype;
+ }
+ }
+
+ *nargs = count;
+ 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. */
+
+static tree
+append_access_attrs (tree t, tree attrs, const char *attrstr,
+ char code, HOST_WIDE_INT idxs[2])
+{
+ 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);
+
+ size_t newlen = n1 + n2;
+ char *newspec = attrspec;
+
+ if (tree acs = lookup_attribute ("access", attrs))
+ {
+ acs = TREE_VALUE (acs);
+ gcc_assert (TREE_CODE (acs) == STRING_CST);
+
+ /* 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. */
+
+ const char *posstr = attrspec + 1;
+ const char *str = TREE_STRING_POINTER (acs);
+ const char *pos = str;
+ for ( ; ; pos += n1)
+ {
+ pos = strstr (pos, posstr);
+ if (!pos)
+ break;
+
+ if (ISDIGIT (pos[-1]) || ISDIGIT (pos[n1 -1]))
+ continue;
+
+ /* Found a matching positional argument. */
+ if (*attrspec != pos[-1])
+ {
+ /* Mismatch in access mode. */
+ if (warning (OPT_Wattributes,
+ "attribute %qs mismatch with mode %qs",
+ attrstr,
+ (pos[-1] == 'r'
+ ? "read_only"
+ : (pos[-1] == 'w' ? "write_only" : "read_write")))
+ && DECL_P (t))
+ inform (DECL_SOURCE_LOCATION (t),
+ "previous declaration here");
+ return NULL_TREE;
+ }
+
+ if ((n2 && pos[n1 - 1] != ','))
+ {
+ /* Mismatch in the presence of the size argument. */
+ 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 (!n2 && pos[n1 - 1] == ',')
+ {
+ /* Mismatch in the presence of the size argument. */
+ 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;
+ }
+
+ if (n2 && strncmp (attrstr + n1 + 1, pos + n1, n2))
+ {
+ /* Mismatch in the value of the size argument. */
+ if (warning (OPT_Wattributes,
+ "attribute %qs mismatch positional argument "
+ "values %i and %i",
+ attrstr, atoi (attrstr + n1 + 1), atoi (pos + n1))
+ && DECL_P (t))
+ inform (DECL_SOURCE_LOCATION (t),
+ "previous declaration here");
+ return NULL_TREE;
+ }
+
+ /* Avoid adding the same attribute specification. */
+ return NULL_TREE;
+ }
+
+ /* Connect the two substrings formatted above into a single one. */
+ if (idxs[1])
+ attrspec[n1] = ',';
+
+ size_t len = strlen (str);
+ newspec = (char *) xmalloc (newlen + len + 1);
+ strcpy (newspec, str);
+ strcpy (newspec + len, attrspec);
+ newlen += len;
+ }
+ else if (idxs[1])
+ /* Connect the two substrings formatted above into a single one. */
+ attrspec[n1] = ',';
+
+ return build_string (newlen + 1, newspec);
+}
+
+/* Handle the access attribute (read_only, write_only, and read_write). */
+
+static tree
+handle_access_attribute (tree *node, tree name, tree args,
+ int ARG_UNUSED (flags), bool *no_add_attrs)
+{
+ tree type = *node;
+ tree attrs = TYPE_ATTRIBUTES (type);
+
+ *no_add_attrs = true;
+
+ /* Verify a full prototype is provided so that the argument types
+ can be validated. Avoid diagnosing type-generic built-ins since
+ those have no prototype. */
+ if (!args
+ && !prototype_p (type)
+ && (!attrs || !lookup_attribute ("type generic", attrs)))
+ {
+ error ("attribute %qE without arguments on a non-prototype", name);
+ return NULL_TREE;
+ }
+
+ /* Set to true when the access mode has the form of a function call
+ as in 'attribute (read_only (1, 2))'. That's an easy mistake to
+ make and so worth a special diagnostic. */
+ bool funcall = false;
+ tree access_mode = TREE_VALUE (args);
+ if (TREE_CODE (access_mode) == CALL_EXPR)
+ {
+ access_mode = CALL_EXPR_FN (access_mode);
+ if (TREE_CODE (access_mode) != ADDR_EXPR)
+ {
+ error ("attribute %qE invalid mode", name);
+ return NULL_TREE;
+ }
+ access_mode = TREE_OPERAND (access_mode, 0);
+ access_mode = DECL_NAME (access_mode);
+ funcall = true;
+ }
+
+ const char* const access_str = IDENTIFIER_POINTER (access_mode);
+ const char *ps = access_str;
+ if (ps[0] == '_' && ps[1] == '_')
+ {
+ size_t len = strlen (ps);
+ if (ps[len - 1] == '_' && ps[len - 2] == '_')
+ ps += 2;
+ }
+
+ const bool read_only = strncmp (ps, "read_only", 9) == 0;
+ const bool write_only = strncmp (ps, "write_only", 9) == 0;
+ if (!read_only && !write_only && strncmp (ps, "read_write", 9))
+ {
+ error ("attribute %qE invalid mode %qs; expected one of "
+ "%qs, %qs, or %qs", name, access_str,
+ "read_only", "read_write", "write_only");
+ return NULL_TREE;
+ }
+
+ if (funcall)
+ {
+ error ("attribute %qE unexpected %<(%> after mode %qs; expected "
+ "a positional argument or %<)%>",
+ name, access_str);
+ return NULL_TREE;
+ }
+
+ args = TREE_CHAIN (args);
+ if (!args)
+ {
+ /* The first positional argument is required. It may be worth
+ dropping the requirement at some point and having read_only
+ apply to all const-qualified pointers and read_write or
+ write_only to the rest. */
+ error ("attribute %<%E(%s)%> missing an argument",
+ name, access_str);
+ return NULL_TREE;
+ }
+
+ /* One or more positional arguments have been specified. Validate
+ them. */
+ tree idxnodes[2] = { NULL_TREE, NULL_TREE };
+ tree argtypes[2] = { NULL_TREE, NULL_TREE };
+ /* 1-based attribute positional arguments or zero if not specified.
+ Invalid negative or excessive values are also stored but used
+ only in diagnostics. */
+ HOST_WIDE_INT idxs[2] = { 0, 0 };
+
+ /* Number of function formal arguments (used in diagnostics). */
+ unsigned nfuncargs = 0;
+ /* Number of (optional) attribute positional arguments. */
+ unsigned nattrargs = 0;
+
+ for (unsigned i = 0; i != 2; ++i, args = TREE_CHAIN (args), ++nattrargs)
+ {
+ if (!args)
+ break;
+
+ idxnodes[i] = TREE_VALUE (args);
+
+ if (TREE_CODE (idxnodes[i]) != IDENTIFIER_NODE
+ && TREE_CODE (idxnodes[i]) != FUNCTION_DECL)
+ idxnodes[i] = default_conversion (idxnodes[i]);
+
+ if (tree_fits_shwi_p (idxnodes[i]))
+ {
+ idxs[i] = tree_to_shwi (idxnodes[i]);
+ argtypes[i] = get_argument_type (type, idxs[i], &nfuncargs);
+ }
+ }
+
+ if ((nattrargs == 1 && !idxs[0])
+ || (nattrargs == 2 && (!idxs[0] || !idxs[1])))
+ {
+ if (idxnodes[1])
+ error ("attribute %<%E(%s, %E, %E)%> invalid positional argument %i",
+ name, access_str, idxnodes[0], idxnodes[1], idxs[0] ? 2 : 1);
+ else
+ error ("attribute %<%E(%s, %E)%> invalid positional argument %i",
+ name, access_str, idxnodes[0], idxs[0] ? 2 : 1);
+ return NULL_TREE;
+ }
+
+ /* Format the attribute specification to include in diagnostics. */
+ char attrstr[80];
+ if (idxnodes[1])
+ snprintf (attrstr, sizeof attrstr, "%s(%s, %lli, %lli)",
+ IDENTIFIER_POINTER (name), access_str,
+ (long long) idxs[0], (long long) idxs[1]);
+ else if (idxnodes[0])
+ snprintf (attrstr, sizeof attrstr, "%s(%s, %lli)",
+ IDENTIFIER_POINTER (name), access_str,
+ (long long) idxs[0]);
+ else
+ snprintf (attrstr, sizeof attrstr, "%s(%s)",
+ IDENTIFIER_POINTER (name), access_str);
+
+ /* Verify the positional argument values are in range. */
+ if (!argtypes[0] || (idxnodes[1] && !argtypes[1]))
+ {
+ if (idxnodes[0])
+ {
+ if (idxs[0] < 0 || idxs[1] < 0)
+ error ("attribute %qs positional argument %i invalid value %wi",
+ attrstr, idxs[0] < 0 ? 1 : 2,
+ idxs[0] < 0 ? idxs[0] : idxs[1]);
+ else
+ error ("attribute %qs positional argument %i value %wi exceeds "
+ "number of function arguments %u",
+ attrstr, idxs[0] ? 1 : 2,
+ idxs[0] ? idxs[0] : idxs[1],
+ nfuncargs);
+ }
+ else
+ error ("attribute %qs invalid positional argument", attrstr);
+
+ return NULL_TREE;
+ }
+
+ if (!POINTER_TYPE_P (argtypes[0]))
+ {
+ /* The first argument must have a pointer or reference type. */
+ error ("attribute %qs positional argument 1 references "
+ "non-pointer argument type %qT",
+ attrstr, argtypes[0]);
+ return NULL_TREE;
+ }
+
+ {
+ /* Pointers to functions are not allowed. */
+ tree ptrtype = TREE_TYPE (argtypes[0]);
+ if (FUNC_OR_METHOD_TYPE_P (ptrtype))
+ {
+ error ("attribute %qs positional argument 1 references "
+ "argument of function type %qT",
+ attrstr, ptrtype);
+ return NULL_TREE;
+ }
+ }
+
+ if (!read_only)
+ {
+ /* A read_write and write_only modes must reference non-const
+ arguments. */
+ if (TYPE_READONLY (TREE_TYPE (argtypes[0])))
+ {
+ error ("attribute %qs positional argument 1 references "
+ "%qs-qualified argument type %qT",
+ attrstr, "const", argtypes[0]);
+ return NULL_TREE;
+ }
+ }
+ else if (!TYPE_READONLY (TREE_TYPE (argtypes[0])))
+ {
+ /* A read_only mode should ideally reference const-qualified
+ arguments but it's not diagnosed error if one doesn't.
+ This makes it possible to annotate legacy, const-incorrect
+ APIs. It might be worth a diagnostic along the lines of
+ -Wsuggest-const. */
+ ;
+ }
+
+ if (argtypes[1] && !INTEGRAL_TYPE_P (argtypes[1]))
+ {
+ error ("attribute %qs positional argument 2 references "
+ "non-integer argument type %qT",
+ attrstr, argtypes[1]);
+ return NULL_TREE;
+ }
+
+ /* 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' : 'x';
+ tree new_attrs = append_access_attrs (node[0], attrs, attrstr, code, idxs);
+ if (!new_attrs)
+ return NULL_TREE;
+
+ /* Replace any existing access attribute specification with
+ the concatenation above. */
+ attrs = remove_attribute (IDENTIFIER_POINTER (name), attrs);
+ new_attrs = tree_cons (name, new_attrs, attrs);
+
+ if (node[1])
+ {
+ /* Repeat for the previously declared type. */
+ attrs = TYPE_ATTRIBUTES (TREE_TYPE (node[1]));
+ tree new_attrs = append_access_attrs (node[1], attrs, attrstr, code, idxs);
+ if (!new_attrs)
+ return NULL_TREE;
+
+ attrs = remove_attribute (IDENTIFIER_POINTER (name), attrs);
+ new_attrs = tree_cons (name, new_attrs, attrs);
+ TYPE_ATTRIBUTES (TREE_TYPE (node[1])) = new_attrs;
+ }
+
+ TYPE_ATTRIBUTES (*node) = new_attrs;
+ return NULL_TREE;
+}
+
/* Handle a "nothrow" attribute; arguments as in
struct attribute_spec.handler. */
diff --git a/gcc/c-family/c-common.c b/gcc/c-family/c-common.c
index f779acc..3e70b6d 100644
--- a/gcc/c-family/c-common.c
+++ b/gcc/c-family/c-common.c
@@ -5483,7 +5483,7 @@ nonnull_check_p (tree args, unsigned HOST_WIDE_INT param_num)
for (; args; args = TREE_CHAIN (args))
{
- bool found = get_nonnull_operand (TREE_VALUE (args), &arg_num);
+ bool found = get_attribute_operand (TREE_VALUE (args), &arg_num);
gcc_assert (found);
@@ -5518,11 +5518,11 @@ check_nonnull_arg (void *ctx, tree param, unsigned HOST_WIDE_INT param_num)
}
}
-/* Helper for nonnull attribute handling; fetch the operand number
- from the attribute argument list. */
+/* Helper for attribute handling; fetch the operand number from
+ the attribute argument list. */
bool
-get_nonnull_operand (tree arg_num_expr, unsigned HOST_WIDE_INT *valp)
+get_attribute_operand (tree arg_num_expr, unsigned HOST_WIDE_INT *valp)
{
/* Verify the arg number is a small constant. */
if (tree_fits_uhwi_p (arg_num_expr))
diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h
index f3478d3..bed4d0c 100644
--- a/gcc/c-family/c-common.h
+++ b/gcc/c-family/c-common.h
@@ -880,7 +880,7 @@ extern bool pointer_to_zero_sized_aggr_p (tree);
extern bool bool_promoted_to_int_p (tree);
extern tree fold_for_warn (tree);
extern tree c_common_get_narrower (tree, int *);
-extern bool get_nonnull_operand (tree, unsigned HOST_WIDE_INT *);
+extern bool get_attribute_operand (tree, unsigned HOST_WIDE_INT *);
#define c_sizeof(LOC, T) c_sizeof_or_alignof_type (LOC, T, true, false, 1)
#define c_alignof(LOC, T) c_sizeof_or_alignof_type (LOC, T, false, false, 1)