PR c/80806 - gcc does not warn if local array is memset only
PR middle-end/83859 - attribute to associate buffer and its size
gcc/ChangeLog:
PR c/80806
PR middle-end/83859
* builtin-attrs.def (ATTR_NO_SIDE_EFFECT): New.
(ATTR_READ_ONLY, ATTR_READ_WRITE, ATTR_WRITE_ONLY): New.
(ATTR_NOTHROW_WRONLY1_LEAF, ATTR_NOTHROW_WRONLY1_2_LEAF): New.
(ATTR_NOTHROW_WRONLY1_3_LEAF, ATTR_NOTHROW_WRONLY2_3_LEAF): New.
(ATTR_RET1_NOTHROW_WRONLY1_LEAF, ATTR_RET1_NOTHROW_WRONLY1_3_LEAF): New.
(ATTR_RET1_NOTHROW_NONNULL_RDONLY2_LEAF): New.
(ATTR_RET1_NOTHROW_NONNULL_RDWR1_RDONLY2_LEAF): New.
(ATTR_RET1_NOTHROW_WRONLY1_3_RDONLY2_3_LEAF): New.
(ATTR_RET1_NOTHROW_WRONLY1_RDONLY2_LEAF): New.
(ATTR_RET1_NOTHROW_WRONLY1_3_RDONLY2_LEAF): New.
(ATTR_MALLOC_NOTHROW_NONNULL_RDONLY1_LEAF): New.
(ATTR_PURE_NOTHROW_NONNULL_RDONLY1_LEAF): New.
(ATTR_PURE_NOTHROW_NONNULL_RDONLY1_RDONLY2_LEAF): New.
(ATTR_RETNONNULL_RDONLY2_3_NOTHROW_LEAF): New.
(ATTR_RETNONNULL_WRONLY1_3_RDONLY2_3_NOTHROW_LEAF): New.
(ATTR_PURE_NOTHROW_NONNULL_RDONLY1_3_LEAF): New.
(ATTR_PURE_NOTHROW_NONNULL_RDONLY1_2_3_LEAF): New.
(ATTR_RETNONNULL_RDONLY2_NOTHROW_LEAF): New.
(ATTR_RETNONNULL_WRONLY1_RDONLY2_NOTHROW_LEAF): New.
(ATTR_RETNONNULL_WRONLY1_3_RDONLY2_NOTHROW_LEAF): New.
* builtins.c (check_access): Make extern. Consistently set
the no-warning bit after issuing a warning.
* builtins.h (check_access): Declare.
* builtins.def (bcopy, bzero, index, memchr, memcmp, memcpy): Add
read_only and write_only attributes.
(memset, rindex, stpcpy, stpncpy, strcasecmp, strcat): Same.
(strchr, strcmp, strcpy, strcspn, strdup, strndup, strlen): Same.
(strncasecmp, strncat, strncmp, strncpy, strrchr, strspn, strstr): Same.
(free, __memcpy_chk, __memmove_chk, __memset_chk): Same.
(__strcpy_chk, __strncpy_chk): Same.
* 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.
* doc/extend.texi (no_side_effect): Document new attribute.
(read_only, write_only, read_write): Same.
* tree-ssa-uninit.c (maybe_warn_uninit_accesss): New functions.
(warn_uninitialized_vars): Rename argument. Factor out code into
maybe_warn_uninit_accesss. Call it.
gcc/c/ChangeLog:
PR c/80806
PR middle-end/83859
* c-parser.c (c_parser::no_set_read, no_init, in_arg): New members.
(c_parser_expr_list): Add argument.
(c_parser_attribute): Pass a new argument to get_nonnull_operand.
(c_parser_initializer): Set parser->in_init.
(c_parser_binary_expression): Use parser->no_set_read.
(c_parser_unary_expression): Same.
(c_parser_sizeof_expression): Use parser->in_arg.
(c_parser_postfix_expression_after_primary): Adjust.
(is_write_only_p): New function.
(c_parser_expr_list): Add argument.
Avoid setting DECL_READ_P for decls passed to write-only function
arguments.
(c_parser_objc_keywordexpr): Pass a new argument to c_parser_expr_list.
(c_parser_oacc_wait_list): Same.
* c-tree.h (parser_build_binary_op): Add argument.
* c-typeck.c (default_conversion): Same. Use it.
(parser_build_binary_op): Same.
(build_binary_op): Same.
gcc/c-family/ChangeLog:
PR c/80806
PR middle-end/83859
* c-attribs.c (handle_no_side_effect_attribute): New function.
(handle_read_only_attribute, handle_write_only_attribute): Same.
(handle_read_write_attribute): Same.
(c_common_attribute_table): Add new attributes.
(get_argument_type): New function.
(handle_rdwr_attributes): Same.
(has_attribute): Add argument to callback signature. Pass to it
a new value.
(get_nonnull_operand): Rename...
(get_attribute_operand): ...to this.
* c-common.c (get_nonnull_operand): Rename...
(get_attribute_operand): ...to this.
(build_binary_op): Add a new argument.
(default_conversion): Same.
(has_attribute): Adjust argument type.
gcc/cp/ChangeLog:
PR c/80806
PR middle-end/83859
* typeck.c (cp_default_conversion): Add argument.
(build_binary_op): Same.
gcc/testsuite/ChangeLog:
PR c/80806
PR middle-end/83859
* c-c++-common/Wsizeof-pointer-memaccess1.c: Adjust.
* c-c++-common/Wsizeof-pointer-memaccess2.c: Adjust.
* gcc.dg/Wstrict-aliasing-bogus-vla-1.c: Adjust.
* gcc/testsuite/gcc.dg/Wunused-but-set-var.c: New test.
* gcc.dg/attr-alloc_size.c: Adjust.
* gcc/testsuite/gcc.dg/attr-read-only-2.c: New test.
* gcc/testsuite/gcc.dg/attr-read-only.c: New test.
* gcc/testsuite/gcc.dg/attr-write-only-2.c: New test.
* gcc/testsuite/gcc.dg/attr-write-only.c: New test.
* gcc.dg/nonnull-3.c: Adjust.
* gcc.dg/pr40340-2.c: Adjust.
* gcc.dg/pr78768.c: Adjust.
* gcc.dg/pr79715.c: Adjust.
* gcc.dg/tree-ssa/builtin-snprintf-7.c: Adjust.
* gcc/testsuite/gcc.dg/uninit-builtin.c: New test.
@@ -33,6 +33,7 @@ along with GCC; see the file COPYING3. If not see
#include "diagnostic.h"
#include "pretty-print.h"
#include "intl.h"
+#include "gimple.h"
/* Table of the tables of attributes (common, language, format, machine)
searched. */
@@ -2021,6 +2022,135 @@ maybe_diag_alias_attributes (tree alias, tree target)
}
}
+/* Given function type FNTYPE, initialize ACCESS based on the function's
+ first access attribute specifier, if one exists, or advance it to the
+ next specifier if it's already been initialized. Return true if
+ ACCESS has been initialized or advanced to a valid access specifier,
+ false otherwise. */
+
+bool
+get_attr_access_ptr_and_size (tree fntype, attr_access *access)
+{
+ if (!access->attrs)
+ access->attrs = TYPE_ATTRIBUTES (fntype);
+ else if (TREE_CODE (access->attrs) != TREE_LIST)
+ {
+ /* The last call sets ACCESS->ATTRS to something other than
+ TREE_LIST to indicate it's reached the end. */
+ return false;
+ }
+
+ if (!access->attrs)
+ return false;
+
+ const char *attr_name;
+ switch (access->kind)
+ {
+ case attr_access::read_only:
+ attr_name = "read_only";
+ break;
+
+ case attr_access::read_write:
+ attr_name = "read_write";
+ break;
+
+ case attr_access::write_only:
+ attr_name = "write_only";
+ break;
+
+ default:
+ gcc_unreachable ();
+ }
+
+ access->attrs = lookup_attribute (attr_name, access->attrs);
+ if (!access->attrs)
+ return false;
+
+ /* When operands are specified, iterate over them. */
+ tree opers = TREE_VALUE (access->attrs);
+ if (opers)
+ access->ptrarg = TREE_INT_CST_LOW (TREE_VALUE (opers)) - 1;
+ else
+ {
+ /* When no operands are specified, the attribute applies to every
+ pointer argument. Use ARGCHAIN to find the next such argument
+ if it exists. */
+ if (access->argchain)
+ access->argchain = TREE_CHAIN (access->argchain);
+ else
+ {
+ access->ptrarg = 0;
+ access->argchain = TYPE_ARG_TYPES (fntype);
+ }
+
+ while (!POINTER_TYPE_P (TREE_VALUE (access->argchain)))
+ {
+ access->argchain = TREE_CHAIN (access->argchain);
+ if (!access->argchain)
+ return false;
+ ++access->ptrarg;
+ }
+ }
+
+ if (opers)
+ opers = TREE_CHAIN (opers);
+
+ if (opers)
+ access->sizarg = TREE_INT_CST_LOW (TREE_VALUE (opers)) - 1;
+ else
+ access->sizarg = UINT_MAX;
+
+ access->ptr = NULL_TREE;
+ access->size = NULL_TREE;
+ access->attrs = TREE_CHAIN (access->attrs);
+
+ /* When the last attribute/argument has been reached set ACCESS->ATTRS
+ to something other than TREE_LIST to indicate that to the next call. */
+ if (!access->attrs && !access->argchain)
+ access->attrs = void_type_node;
+
+ return true;
+}
+
+/* Given a Gimple statement STMT that's a function call, initialize ACCESS
+ based on the called function's first access attribute specifier, if one
+ exists, or advance it to the next specifier if it's already been
+ initialized. Return true if ACCESS has been initialized or advanced
+ to a valid access specifier, false otherwise. */
+
+bool
+get_attr_access_ptr_and_size (const gimple *stmt, attr_access *access)
+{
+ const gcall *gc = as_a <const gcall *> (stmt);
+ if (!gc)
+ return false;
+
+ tree fntype = gimple_call_fntype (gc);
+ if (!fntype)
+ return false;
+
+ if (!get_attr_access_ptr_and_size (fntype, access))
+ return false;
+
+ if (access->ptrarg < gimple_call_num_args (gc))
+ {
+ tree ptr = gimple_call_arg (gc, access->ptrarg);
+ access->ptr = POINTER_TYPE_P (TREE_TYPE (ptr)) ? ptr : NULL_TREE;
+ }
+ else
+ access->ptr = NULL_TREE;
+
+ if (access->sizarg <= gimple_call_num_args (gc))
+ {
+ tree size = gimple_call_arg (gc, access->sizarg);
+ access->size = INTEGRAL_TYPE_P (TREE_TYPE (size)) ? size : NULL_TREE;
+ }
+ else
+ access->size = NULL_TREE;
+
+ return access->ptr;
+}
+
#if CHECKING_P
@@ -218,4 +218,36 @@ lookup_attribute_by_prefix (const char *attr_name, tree list)
}
}
+/* Description of a function argument declared attribute read_only,
+ read_write, or write_only. Used as an "iterator" over all such
+ arguments in a function declaration or call. */
+
+struct attr_access
+{
+ /* Attribute chain for the given function declaration. */
+ tree attrs;
+
+ /* The attribute pointer argument. */
+ tree ptr;
+ /* The size of the pointed-to object or NULL when not specified. */
+ tree size;
+
+ /* The tree node corresponding to the current argument in the chain
+ of formal function arguments in a call to the given function.
+ Used by attributes that specify that relevant arguments are of
+ the given kind, as in
+ strcmp (const char*, const char*) __attribute__ ((read_only)) */
+ tree argchain;
+
+ /* The zero-based number of each of the formal function arguments. */
+ unsigned ptrarg;
+ unsigned sizarg;
+ enum Kind { read_only, write_only, read_write };
+
+ Kind kind;
+};
+
+extern bool get_attr_access_ptr_and_size (tree, attr_access *);
+extern bool get_attr_access_ptr_and_size (const gimple *, attr_access *);
+
#endif // GCC_ATTRIBS_H
@@ -119,6 +119,10 @@ DEF_ATTR_IDENT (ATTR_TM_TMPURE, "transaction_pure")
DEF_ATTR_IDENT (ATTR_RETURNS_TWICE, "returns_twice")
DEF_ATTR_IDENT (ATTR_RETURNS_NONNULL, "returns_nonnull")
DEF_ATTR_IDENT (ATTR_WARN_UNUSED_RESULT, "warn_unused_result")
+DEF_ATTR_IDENT (ATTR_NO_SIDE_EFFECT, "no_side_effect")
+DEF_ATTR_IDENT (ATTR_READ_ONLY, "read_only")
+DEF_ATTR_IDENT (ATTR_READ_WRITE, "read_write")
+DEF_ATTR_IDENT (ATTR_WRITE_ONLY, "write_only")
DEF_ATTR_TREE_LIST (ATTR_NOVOPS_LIST, ATTR_NOVOPS, ATTR_NULL, ATTR_NULL)
@@ -212,6 +216,7 @@ DEF_ATTR_TREE_LIST (ATTR_NOTHROW_NONNULL, ATTR_NONNULL, ATTR_NULL, \
DEF_ATTR_TREE_LIST (ATTR_NOTHROW_NONNULL_LEAF, ATTR_NONNULL, ATTR_NULL, \
ATTR_NOTHROW_LEAF_LIST)
DEF_ATTR_TREE_LIST (ATTR_NOTHROW_NONNULL_LEAF_LIST, ATTR_LEAF, ATTR_NULL, ATTR_NOTHROW_NONNULL_LEAF)
+
/* Nothrow functions whose first parameter is a nonnull pointer. */
DEF_ATTR_TREE_LIST (ATTR_NOTHROW_NONNULL_1, ATTR_NONNULL, ATTR_LIST_1, \
ATTR_NOTHROW_LIST)
@@ -271,10 +276,54 @@ DEF_ATTR_TREE_LIST (ATTR_NOTHROW_NONNULL_TYPEGENERIC_LEAF,
/* Nothrow const functions whose pointer parameter(s) are all nonnull. */
DEF_ATTR_TREE_LIST (ATTR_CONST_NOTHROW_NONNULL, ATTR_CONST, ATTR_NULL, \
ATTR_NOTHROW_NONNULL)
+
/* Nothrow leaf functions whose pointer parameter(s) are all nonnull,
- and which return their first argument. */
+ which return their first argument, and which access their arguments
+ for reading and writing as indicated by RDONLY and WRONLY. */
DEF_ATTR_TREE_LIST (ATTR_RET1_NOTHROW_NONNULL_LEAF, ATTR_FNSPEC, ATTR_LIST_STR1, \
ATTR_NOTHROW_NONNULL_LEAF)
+
+DEF_ATTR_TREE_LIST (ATTR_NOTHROW_WRONLY1_LEAF, ATTR_WRITE_ONLY, \
+ ATTR_LIST_1, ATTR_NOTHROW_LEAF_LIST)
+
+DEF_ATTR_TREE_LIST (ATTR_NOTHROW_WRONLY1_2_LEAF, ATTR_WRITE_ONLY, \
+ ATTR_LIST_1_2, ATTR_NOTHROW_LEAF_LIST)
+
+DEF_ATTR_TREE_LIST (ATTR_NOTHROW_WRONLY1_3_LEAF, ATTR_WRITE_ONLY, \
+ ATTR_LIST_1_3, ATTR_NOTHROW_LEAF_LIST)
+
+DEF_ATTR_TREE_LIST (ATTR_NOTHROW_WRONLY2_3_LEAF, ATTR_WRITE_ONLY, \
+ ATTR_LIST_2_3, ATTR_NOTHROW_NONNULL_LEAF)
+
+DEF_ATTR_TREE_LIST (ATTR_RET1_NOTHROW_WRONLY1_LEAF, ATTR_WRITE_ONLY, \
+ ATTR_LIST_1, ATTR_RET1_NOTHROW_NONNULL_LEAF)
+
+DEF_ATTR_TREE_LIST (ATTR_RET1_NOTHROW_WRONLY1_3_LEAF, ATTR_WRITE_ONLY, \
+ ATTR_LIST_1_3, ATTR_RET1_NOTHROW_NONNULL_LEAF)
+
+/* strcat. */
+DEF_ATTR_TREE_LIST (ATTR_RET1_NOTHROW_NONNULL_RDONLY2_LEAF,
+ ATTR_READ_ONLY, ATTR_LIST_2,
+ ATTR_RET1_NOTHROW_NONNULL_LEAF)
+DEF_ATTR_TREE_LIST (ATTR_RET1_NOTHROW_NONNULL_RDWR1_RDONLY2_LEAF,
+ ATTR_READ_WRITE, ATTR_LIST_1,
+ ATTR_RET1_NOTHROW_NONNULL_RDONLY2_LEAF)
+
+/* memcpy, memmove. */
+DEF_ATTR_TREE_LIST (ATTR_RET1_NOTHROW_WRONLY1_3_RDONLY2_3_LEAF,
+ ATTR_READ_ONLY, ATTR_LIST_2_3,
+ ATTR_RET1_NOTHROW_WRONLY1_3_LEAF)
+
+/* strcpy. */
+DEF_ATTR_TREE_LIST (ATTR_RET1_NOTHROW_WRONLY1_RDONLY2_LEAF,
+ ATTR_READ_ONLY, ATTR_LIST_2,
+ ATTR_RET1_NOTHROW_WRONLY1_LEAF)
+
+/* strncpy. */
+DEF_ATTR_TREE_LIST (ATTR_RET1_NOTHROW_WRONLY1_3_RDONLY2_LEAF,
+ ATTR_READ_ONLY, ATTR_LIST_2,
+ ATTR_RET1_NOTHROW_WRONLY1_3_LEAF)
+
/* Nothrow leaf functions whose pointer parameter(s) are all nonnull,
and return value is also nonnull. */
DEF_ATTR_TREE_LIST (ATTR_RETNONNULL_NOTHROW_LEAF, ATTR_RETURNS_NONNULL, ATTR_NULL, \
@@ -305,6 +354,52 @@ DEF_ATTR_TREE_LIST (ATTR_WARN_UNUSED_RESULT_NOTHROW_NONNULL_LEAF, ATTR_WARN_UNUS
DEF_ATTR_TREE_LIST (ATTR_MALLOC_WARN_UNUSED_RESULT_NOTHROW_NONNULL_LEAF, ATTR_MALLOC, ATTR_NULL, \
ATTR_WARN_UNUSED_RESULT_NOTHROW_NONNULL_LEAF)
+/* strdup, strndup. */
+DEF_ATTR_TREE_LIST (ATTR_MALLOC_NOTHROW_NONNULL_RDONLY1_LEAF,
+ ATTR_READ_ONLY, ATTR_LIST_1,
+ ATTR_MALLOC_WARN_UNUSED_RESULT_NOTHROW_NONNULL_LEAF)
+
+/* strchr, strlen. */
+DEF_ATTR_TREE_LIST (ATTR_PURE_NOTHROW_NONNULL_RDONLY1_LEAF,
+ ATTR_READ_ONLY, ATTR_LIST_1,
+ ATTR_PURE_NOTHROW_NONNULL_LEAF)
+
+/* strcmp, strstr. */
+DEF_ATTR_TREE_LIST (ATTR_PURE_NOTHROW_NONNULL_RDONLY1_RDONLY2_LEAF,
+ ATTR_READ_ONLY, ATTR_LIST_2,
+ ATTR_PURE_NOTHROW_NONNULL_RDONLY1_LEAF)
+
+/* mempcpy. */
+DEF_ATTR_TREE_LIST (ATTR_RETNONNULL_RDONLY2_3_NOTHROW_LEAF,
+ ATTR_READ_ONLY, ATTR_LIST_2_3,
+ ATTR_RETNONNULL_NOTHROW_LEAF)
+DEF_ATTR_TREE_LIST (ATTR_RETNONNULL_WRONLY1_3_RDONLY2_3_NOTHROW_LEAF,
+ ATTR_WRITE_ONLY, ATTR_LIST_1_3,
+ ATTR_RETNONNULL_RDONLY2_3_NOTHROW_LEAF)
+
+/* memchr. */
+DEF_ATTR_TREE_LIST (ATTR_PURE_NOTHROW_NONNULL_RDONLY1_3_LEAF,
+ ATTR_READ_ONLY, ATTR_LIST_1_3,
+ ATTR_NOTHROW_NONNULL_LEAF)
+
+/* memcmp. */
+DEF_ATTR_TREE_LIST (ATTR_PURE_NOTHROW_NONNULL_RDONLY1_2_3_LEAF,
+ ATTR_READ_ONLY, ATTR_LIST_2_3, \
+ ATTR_PURE_NOTHROW_NONNULL_RDONLY1_3_LEAF)
+
+/* stpcpy. */
+DEF_ATTR_TREE_LIST (ATTR_RETNONNULL_RDONLY2_NOTHROW_LEAF,
+ ATTR_READ_ONLY, ATTR_LIST_2,
+ ATTR_RETNONNULL_NOTHROW_LEAF)
+DEF_ATTR_TREE_LIST (ATTR_RETNONNULL_WRONLY1_RDONLY2_NOTHROW_LEAF,
+ ATTR_WRITE_ONLY, ATTR_LIST_1,
+ ATTR_RETNONNULL_RDONLY2_NOTHROW_LEAF)
+
+/* stpncpy. */
+DEF_ATTR_TREE_LIST (ATTR_RETNONNULL_WRONLY1_3_RDONLY2_NOTHROW_LEAF,
+ ATTR_READ_ONLY, ATTR_LIST_2,
+ ATTR_RETNONNULL_RDONLY2_NOTHROW_LEAF)
+
/* Construct a tree for the format attribute (and implicitly nonnull). */
#define DEF_FORMAT_ATTRIBUTE(TYPE, FA, VALUES) \
DEF_ATTR_TREE_LIST (ATTR_##TYPE##_##VALUES, ATTR_NULL, \
@@ -3299,7 +3299,7 @@ determine_block_size (tree len, rtx len_rtx,
If the call is successfully verified as safe return true, otherwise
return false. */
-static bool
+bool
check_access (tree exp, tree, tree, tree dstwrite,
tree maxread, tree srcstr, tree dstsize)
{
@@ -3433,37 +3433,42 @@ check_access (tree exp, tree, tree, tree dstwrite,
location_t loc = tree_nonartificial_location (exp);
loc = expansion_point_location_if_in_system_header (loc);
+ bool warned = false;
if (dstwrite == slen && at_least_one)
{
/* This is a call to strcpy with a destination of 0 size
and a source of unknown length. The call will write
at least one byte past the end of the destination. */
- warning_at (loc, opt,
- "%K%qD writing %E or more bytes into a region "
- "of size %E overflows the destination",
- exp, func, range[0], dstsize);
+ warned = warning_at (loc, opt,
+ "%K%qD writing %E or more bytes into "
+ "a region of size %E overflows "
+ "the destination",
+ exp, func, range[0], dstsize);
}
else if (tree_int_cst_equal (range[0], range[1]))
- warning_n (loc, opt, tree_to_uhwi (range[0]),
- "%K%qD writing %E byte into a region "
- "of size %E overflows the destination",
- "%K%qD writing %E bytes into a region "
- "of size %E overflows the destination",
- exp, func, range[0], dstsize);
+ warned = warning_n (loc, opt, tree_to_uhwi (range[0]),
+ "%K%qD writing %E byte into a region "
+ "of size %E overflows the destination",
+ "%K%qD writing %E bytes into a region "
+ "of size %E overflows the destination",
+ exp, func, range[0], dstsize);
else if (tree_int_cst_sign_bit (range[1]))
{
/* Avoid printing the upper bound if it's invalid. */
- warning_at (loc, opt,
- "%K%qD writing %E or more bytes into a region "
- "of size %E overflows the destination",
- exp, func, range[0], dstsize);
+ warned = warning_at (loc, opt,
+ "%K%qD writing %E or more bytes into "
+ "a region of size %E overflows "
+ "the destination",
+ exp, func, range[0], dstsize);
}
else
- warning_at (loc, opt,
- "%K%qD writing between %E and %E bytes into "
- "a region of size %E overflows the destination",
- exp, func, range[0], range[1],
- dstsize);
+ warned = warning_at (loc, opt,
+ "%K%qD writing between %E and %E bytes into "
+ "a region of size %E overflows the destination",
+ exp, func, range[0], range[1],
+ dstsize);
+ if (warned)
+ TREE_NO_WARNING (exp) = true;
/* Return error when an overflow has been detected. */
return false;
@@ -3486,21 +3491,26 @@ check_access (tree exp, tree, tree, tree dstwrite,
if (TREE_NO_WARNING (exp))
return false;
+ bool warned = false;
+
/* Warn about crazy big sizes first since that's more
likely to be meaningful than saying that the bound
is greater than the object size if both are big. */
if (range[0] == range[1])
- warning_at (loc, opt,
- "%K%qD specified bound %E "
- "exceeds maximum object size %E",
- exp, func,
- range[0], maxobjsize);
+ warned = warning_at (loc, opt,
+ "%K%qD specified bound %E "
+ "exceeds maximum object size %E",
+ exp, func,
+ range[0], maxobjsize);
else
- warning_at (loc, opt,
- "%K%qD specified bound between %E and %E "
- "exceeds maximum object size %E",
- exp, func,
- range[0], range[1], maxobjsize);
+ warned = warning_at (loc, opt,
+ "%K%qD specified bound between %E and %E "
+ "exceeds maximum object size %E",
+ exp, func,
+ range[0], range[1], maxobjsize);
+
+ if (warned)
+ TREE_NO_WARNING (exp) = true;
return false;
}
@@ -3510,18 +3520,23 @@ check_access (tree exp, tree, tree, tree dstwrite,
if (TREE_NO_WARNING (exp))
return false;
+ bool warned = false;
+
if (tree_int_cst_equal (range[0], range[1]))
- warning_at (loc, opt,
- "%K%qD specified bound %E "
- "exceeds destination size %E",
- exp, func,
- range[0], dstsize);
+ warned = warning_at (loc, opt,
+ "%K%qD specified bound %E "
+ "exceeds destination size %E",
+ exp, func,
+ range[0], dstsize);
else
- warning_at (loc, opt,
- "%K%qD specified bound between %E and %E "
- "exceeds destination size %E",
- exp, func,
- range[0], range[1], dstsize);
+ warned = warning_at (loc, opt,
+ "%K%qD specified bound between %E and %E "
+ "exceeds destination size %E",
+ exp, func,
+ range[0], range[1], dstsize);
+ if (warned)
+ TREE_NO_WARNING (exp) = true;
+
return false;
}
}
@@ -3536,26 +3551,31 @@ check_access (tree exp, tree, tree, tree dstwrite,
if (TREE_NO_WARNING (exp))
return false;
+ bool warned = false;
location_t loc = tree_nonartificial_location (exp);
+ loc = expansion_point_location_if_in_system_header (loc);
if (tree_int_cst_equal (range[0], range[1]))
- warning_n (loc, opt, tree_to_uhwi (range[0]),
- "%K%qD reading %E byte from a region of size %E",
- "%K%qD reading %E bytes from a region of size %E",
- exp, func, range[0], slen);
+ warned = warning_n (loc, opt, tree_to_uhwi (range[0]),
+ "%K%qD reading %E byte from a region of size %E",
+ "%K%qD reading %E bytes from a region of size %E",
+ exp, func, range[0], slen);
else if (tree_int_cst_sign_bit (range[1]))
{
/* Avoid printing the upper bound if it's invalid. */
- warning_at (loc, opt,
- "%K%qD reading %E or more bytes from a region "
- "of size %E",
- exp, func, range[0], slen);
+ warned = warning_at (loc, opt,
+ "%K%qD reading %E or more bytes from a region "
+ "of size %E",
+ exp, func, range[0], slen);
}
else
- warning_at (loc, opt,
- "%K%qD reading between %E and %E bytes from a region "
- "of size %E",
- exp, func, range[0], range[1], slen);
+ warned = warning_at (loc, opt,
+ "%K%qD reading between %E and %E bytes from "
+ "a region of size %E",
+ exp, func, range[0], range[1], slen);
+ if (warned)
+ TREE_NO_WARNING (exp) = true;
+
return false;
}
@@ -690,36 +690,36 @@ DEF_C99_COMPL_BUILTIN (BUILT_IN_CTANL, "ctanl", BT_FN_COMPLEX_LONGDOUBLE_
/* bcmp, bcopy and bzero have traditionally accepted NULL pointers
when the length parameter is zero, so don't apply attribute "nonnull". */
DEF_EXT_LIB_BUILTIN (BUILT_IN_BCMP, "bcmp", BT_FN_INT_CONST_PTR_CONST_PTR_SIZE, ATTR_PURE_NOTHROW_LEAF_LIST)
-DEF_EXT_LIB_BUILTIN (BUILT_IN_BCOPY, "bcopy", BT_FN_VOID_CONST_PTR_PTR_SIZE, ATTR_NOTHROW_LEAF_LIST)
-DEF_EXT_LIB_BUILTIN (BUILT_IN_BZERO, "bzero", BT_FN_VOID_PTR_SIZE, ATTR_NOTHROW_LEAF_LIST)
-DEF_EXT_LIB_BUILTIN (BUILT_IN_INDEX, "index", BT_FN_STRING_CONST_STRING_INT, ATTR_PURE_NOTHROW_NONNULL_LEAF)
-DEF_LIB_BUILTIN (BUILT_IN_MEMCHR, "memchr", BT_FN_PTR_CONST_PTR_INT_SIZE, ATTR_PURE_NOTHROW_NONNULL_LEAF)
-DEF_LIB_BUILTIN (BUILT_IN_MEMCMP, "memcmp", BT_FN_INT_CONST_PTR_CONST_PTR_SIZE, ATTR_PURE_NOTHROW_NONNULL_LEAF)
-DEF_LIB_BUILTIN (BUILT_IN_MEMCPY, "memcpy", BT_FN_PTR_PTR_CONST_PTR_SIZE, ATTR_RET1_NOTHROW_NONNULL_LEAF)
-DEF_LIB_BUILTIN (BUILT_IN_MEMMOVE, "memmove", BT_FN_PTR_PTR_CONST_PTR_SIZE, ATTR_RET1_NOTHROW_NONNULL_LEAF)
-DEF_EXT_LIB_BUILTIN (BUILT_IN_MEMPCPY, "mempcpy", BT_FN_PTR_PTR_CONST_PTR_SIZE, ATTR_RETNONNULL_NOTHROW_LEAF)
-DEF_LIB_BUILTIN (BUILT_IN_MEMSET, "memset", BT_FN_PTR_PTR_INT_SIZE, ATTR_RET1_NOTHROW_NONNULL_LEAF)
-DEF_EXT_LIB_BUILTIN (BUILT_IN_RINDEX, "rindex", BT_FN_STRING_CONST_STRING_INT, ATTR_PURE_NOTHROW_NONNULL_LEAF)
-DEF_EXT_LIB_BUILTIN (BUILT_IN_STPCPY, "stpcpy", BT_FN_STRING_STRING_CONST_STRING, ATTR_RETNONNULL_NOTHROW_LEAF)
-DEF_EXT_LIB_BUILTIN (BUILT_IN_STPNCPY, "stpncpy", BT_FN_STRING_STRING_CONST_STRING_SIZE, ATTR_RETNONNULL_NOTHROW_LEAF)
-DEF_EXT_LIB_BUILTIN (BUILT_IN_STRCASECMP, "strcasecmp", BT_FN_INT_CONST_STRING_CONST_STRING, ATTR_PURE_NOTHROW_NONNULL_LEAF)
-DEF_LIB_BUILTIN (BUILT_IN_STRCAT, "strcat", BT_FN_STRING_STRING_CONST_STRING, ATTR_RET1_NOTHROW_NONNULL_LEAF)
-DEF_LIB_BUILTIN (BUILT_IN_STRCHR, "strchr", BT_FN_STRING_CONST_STRING_INT, ATTR_PURE_NOTHROW_NONNULL_LEAF)
-DEF_LIB_BUILTIN (BUILT_IN_STRCMP, "strcmp", BT_FN_INT_CONST_STRING_CONST_STRING, ATTR_PURE_NOTHROW_NONNULL_LEAF)
-DEF_LIB_BUILTIN (BUILT_IN_STRCPY, "strcpy", BT_FN_STRING_STRING_CONST_STRING, ATTR_RET1_NOTHROW_NONNULL_LEAF)
-DEF_LIB_BUILTIN (BUILT_IN_STRCSPN, "strcspn", BT_FN_SIZE_CONST_STRING_CONST_STRING, ATTR_PURE_NOTHROW_NONNULL_LEAF)
-DEF_EXT_LIB_BUILTIN (BUILT_IN_STRDUP, "strdup", BT_FN_STRING_CONST_STRING, ATTR_MALLOC_WARN_UNUSED_RESULT_NOTHROW_NONNULL_LEAF)
-DEF_EXT_LIB_BUILTIN (BUILT_IN_STRNDUP, "strndup", BT_FN_STRING_CONST_STRING_SIZE, ATTR_MALLOC_WARN_UNUSED_RESULT_NOTHROW_NONNULL_LEAF)
-DEF_LIB_BUILTIN (BUILT_IN_STRLEN, "strlen", BT_FN_SIZE_CONST_STRING, ATTR_PURE_NOTHROW_NONNULL_LEAF)
-DEF_EXT_LIB_BUILTIN (BUILT_IN_STRNCASECMP, "strncasecmp", BT_FN_INT_CONST_STRING_CONST_STRING_SIZE, ATTR_PURE_NOTHROW_NONNULL_LEAF)
-DEF_LIB_BUILTIN (BUILT_IN_STRNCAT, "strncat", BT_FN_STRING_STRING_CONST_STRING_SIZE, ATTR_RET1_NOTHROW_NONNULL_LEAF)
-DEF_LIB_BUILTIN (BUILT_IN_STRNCMP, "strncmp", BT_FN_INT_CONST_STRING_CONST_STRING_SIZE, ATTR_PURE_NOTHROW_NONNULL_LEAF)
-DEF_LIB_BUILTIN (BUILT_IN_STRNCPY, "strncpy", BT_FN_STRING_STRING_CONST_STRING_SIZE, ATTR_RET1_NOTHROW_NONNULL_LEAF)
+DEF_EXT_LIB_BUILTIN (BUILT_IN_BCOPY, "bcopy", BT_FN_VOID_CONST_PTR_PTR_SIZE, ATTR_NOTHROW_WRONLY2_3_LEAF)
+DEF_EXT_LIB_BUILTIN (BUILT_IN_BZERO, "bzero", BT_FN_VOID_PTR_SIZE, ATTR_NOTHROW_WRONLY1_2_LEAF)
+DEF_EXT_LIB_BUILTIN (BUILT_IN_INDEX, "index", BT_FN_STRING_CONST_STRING_INT, ATTR_PURE_NOTHROW_NONNULL_RDONLY1_LEAF)
+DEF_LIB_BUILTIN (BUILT_IN_MEMCHR, "memchr", BT_FN_PTR_CONST_PTR_INT_SIZE, ATTR_PURE_NOTHROW_NONNULL_RDONLY1_3_LEAF)
+DEF_LIB_BUILTIN (BUILT_IN_MEMCMP, "memcmp", BT_FN_INT_CONST_PTR_CONST_PTR_SIZE, ATTR_PURE_NOTHROW_NONNULL_RDONLY1_2_3_LEAF)
+DEF_LIB_BUILTIN (BUILT_IN_MEMCPY, "memcpy", BT_FN_PTR_PTR_CONST_PTR_SIZE, ATTR_RET1_NOTHROW_WRONLY1_3_RDONLY2_3_LEAF)
+DEF_LIB_BUILTIN (BUILT_IN_MEMMOVE, "memmove", BT_FN_PTR_PTR_CONST_PTR_SIZE, ATTR_RET1_NOTHROW_WRONLY1_3_RDONLY2_3_LEAF)
+DEF_EXT_LIB_BUILTIN (BUILT_IN_MEMPCPY, "mempcpy", BT_FN_PTR_PTR_CONST_PTR_SIZE, ATTR_RETNONNULL_RDONLY2_3_NOTHROW_LEAF)
+DEF_LIB_BUILTIN (BUILT_IN_MEMSET, "memset", BT_FN_PTR_PTR_INT_SIZE, ATTR_RET1_NOTHROW_WRONLY1_3_LEAF)
+DEF_EXT_LIB_BUILTIN (BUILT_IN_RINDEX, "rindex", BT_FN_STRING_CONST_STRING_INT, ATTR_PURE_NOTHROW_NONNULL_RDONLY1_LEAF)
+DEF_EXT_LIB_BUILTIN (BUILT_IN_STPCPY, "stpcpy", BT_FN_STRING_STRING_CONST_STRING, ATTR_RETNONNULL_WRONLY1_RDONLY2_NOTHROW_LEAF)
+DEF_EXT_LIB_BUILTIN (BUILT_IN_STPNCPY, "stpncpy", BT_FN_STRING_STRING_CONST_STRING_SIZE, ATTR_RETNONNULL_WRONLY1_3_RDONLY2_NOTHROW_LEAF)
+DEF_EXT_LIB_BUILTIN (BUILT_IN_STRCASECMP, "strcasecmp", BT_FN_INT_CONST_STRING_CONST_STRING, ATTR_PURE_NOTHROW_NONNULL_RDONLY1_RDONLY2_LEAF)
+DEF_LIB_BUILTIN (BUILT_IN_STRCAT, "strcat", BT_FN_STRING_STRING_CONST_STRING, ATTR_RET1_NOTHROW_NONNULL_RDWR1_RDONLY2_LEAF)
+DEF_LIB_BUILTIN (BUILT_IN_STRCHR, "strchr", BT_FN_STRING_CONST_STRING_INT, ATTR_PURE_NOTHROW_NONNULL_RDONLY1_LEAF)
+DEF_LIB_BUILTIN (BUILT_IN_STRCMP, "strcmp", BT_FN_INT_CONST_STRING_CONST_STRING, ATTR_PURE_NOTHROW_NONNULL_RDONLY1_RDONLY2_LEAF)
+DEF_LIB_BUILTIN (BUILT_IN_STRCPY, "strcpy", BT_FN_STRING_STRING_CONST_STRING, ATTR_RET1_NOTHROW_WRONLY1_RDONLY2_LEAF)
+DEF_LIB_BUILTIN (BUILT_IN_STRCSPN, "strcspn", BT_FN_SIZE_CONST_STRING_CONST_STRING, ATTR_PURE_NOTHROW_NONNULL_RDONLY1_RDONLY2_LEAF)
+DEF_EXT_LIB_BUILTIN (BUILT_IN_STRDUP, "strdup", BT_FN_STRING_CONST_STRING, ATTR_MALLOC_NOTHROW_NONNULL_RDONLY1_LEAF)
+DEF_EXT_LIB_BUILTIN (BUILT_IN_STRNDUP, "strndup", BT_FN_STRING_CONST_STRING_SIZE, ATTR_MALLOC_NOTHROW_NONNULL_RDONLY1_LEAF)
+DEF_LIB_BUILTIN (BUILT_IN_STRLEN, "strlen", BT_FN_SIZE_CONST_STRING, ATTR_PURE_NOTHROW_NONNULL_RDONLY1_LEAF)
+DEF_EXT_LIB_BUILTIN (BUILT_IN_STRNCASECMP, "strncasecmp", BT_FN_INT_CONST_STRING_CONST_STRING_SIZE, ATTR_PURE_NOTHROW_NONNULL_RDONLY1_RDONLY2_LEAF)
+DEF_LIB_BUILTIN (BUILT_IN_STRNCAT, "strncat", BT_FN_STRING_STRING_CONST_STRING_SIZE, ATTR_RET1_NOTHROW_NONNULL_RDWR1_RDONLY2_LEAF)
+DEF_LIB_BUILTIN (BUILT_IN_STRNCMP, "strncmp", BT_FN_INT_CONST_STRING_CONST_STRING_SIZE, ATTR_PURE_NOTHROW_NONNULL_RDONLY1_RDONLY2_LEAF)
+DEF_LIB_BUILTIN (BUILT_IN_STRNCPY, "strncpy", BT_FN_STRING_STRING_CONST_STRING_SIZE, ATTR_RET1_NOTHROW_WRONLY1_3_RDONLY2_LEAF)
DEF_EXT_LIB_BUILTIN (BUILT_IN_STRNLEN, "strnlen", BT_FN_SIZE_CONST_STRING_SIZE, ATTR_PURE_NOTHROW_NONNULL_LEAF)
DEF_LIB_BUILTIN (BUILT_IN_STRPBRK, "strpbrk", BT_FN_STRING_CONST_STRING_CONST_STRING, ATTR_PURE_NOTHROW_NONNULL_LEAF)
-DEF_LIB_BUILTIN (BUILT_IN_STRRCHR, "strrchr", BT_FN_STRING_CONST_STRING_INT, ATTR_PURE_NOTHROW_NONNULL_LEAF)
-DEF_LIB_BUILTIN (BUILT_IN_STRSPN, "strspn", BT_FN_SIZE_CONST_STRING_CONST_STRING, ATTR_PURE_NOTHROW_NONNULL_LEAF)
-DEF_LIB_BUILTIN (BUILT_IN_STRSTR, "strstr", BT_FN_STRING_CONST_STRING_CONST_STRING, ATTR_PURE_NOTHROW_NONNULL_LEAF)
+DEF_LIB_BUILTIN (BUILT_IN_STRRCHR, "strrchr", BT_FN_STRING_CONST_STRING_INT, ATTR_PURE_NOTHROW_NONNULL_RDONLY1_LEAF)
+DEF_LIB_BUILTIN (BUILT_IN_STRSPN, "strspn", BT_FN_SIZE_CONST_STRING_CONST_STRING, ATTR_PURE_NOTHROW_NONNULL_RDONLY1_RDONLY2_LEAF)
+DEF_LIB_BUILTIN (BUILT_IN_STRSTR, "strstr", BT_FN_STRING_CONST_STRING_CONST_STRING, ATTR_PURE_NOTHROW_NONNULL_RDONLY1_RDONLY2_LEAF)
/* Category: stdio builtins. */
DEF_LIB_BUILTIN (BUILT_IN_FPRINTF, "fprintf", BT_FN_INT_FILEPTR_CONST_STRING_VAR, ATTR_NONNULL_1_FORMAT_PRINTF_2_3)
@@ -865,7 +865,7 @@ DEF_EXT_LIB_BUILTIN (BUILT_IN_FFSLL, "ffsll", BT_FN_INT_LONGLONG, ATTR_CONST_
DEF_EXT_LIB_BUILTIN (BUILT_IN_FORK, "fork", BT_FN_PID, ATTR_NOTHROW_LIST)
DEF_GCC_BUILTIN (BUILT_IN_FRAME_ADDRESS, "frame_address", BT_FN_PTR_UINT, ATTR_NULL)
/* [trans-mem]: Adjust BUILT_IN_TM_FREE if BUILT_IN_FREE is changed. */
-DEF_LIB_BUILTIN (BUILT_IN_FREE, "free", BT_FN_VOID_PTR, ATTR_NOTHROW_LEAF_LIST)
+DEF_LIB_BUILTIN (BUILT_IN_FREE, "free", BT_FN_VOID_PTR, ATTR_NOTHROW_WRONLY1_LEAF)
DEF_GCC_BUILTIN (BUILT_IN_FROB_RETURN_ADDR, "frob_return_addr", BT_FN_PTR_PTR, ATTR_NULL)
DEF_EXT_LIB_BUILTIN (BUILT_IN_GETTEXT, "gettext", BT_FN_STRING_CONST_STRING, ATTR_FORMAT_ARG_1)
DEF_C99_BUILTIN (BUILT_IN_IMAXABS, "imaxabs", BT_FN_INTMAX_INTMAX, ATTR_CONST_NOTHROW_LEAF_LIST)
@@ -962,16 +962,16 @@ DEF_BUILTIN_STUB (BUILT_IN_STRNCMP_EQ, "__builtin_strncmp_eq")
/* Object size checking builtins. */
DEF_GCC_BUILTIN (BUILT_IN_OBJECT_SIZE, "object_size", BT_FN_SIZE_CONST_PTR_INT, ATTR_PURE_NOTHROW_LEAF_LIST)
-DEF_EXT_LIB_BUILTIN (BUILT_IN_MEMCPY_CHK, "__memcpy_chk", BT_FN_PTR_PTR_CONST_PTR_SIZE_SIZE, ATTR_RET1_NOTHROW_NONNULL_LEAF)
-DEF_EXT_LIB_BUILTIN (BUILT_IN_MEMMOVE_CHK, "__memmove_chk", BT_FN_PTR_PTR_CONST_PTR_SIZE_SIZE, ATTR_RET1_NOTHROW_NONNULL_LEAF)
+DEF_EXT_LIB_BUILTIN (BUILT_IN_MEMCPY_CHK, "__memcpy_chk", BT_FN_PTR_PTR_CONST_PTR_SIZE_SIZE, ATTR_RET1_NOTHROW_WRONLY1_3_LEAF)
+DEF_EXT_LIB_BUILTIN (BUILT_IN_MEMMOVE_CHK, "__memmove_chk", BT_FN_PTR_PTR_CONST_PTR_SIZE_SIZE, ATTR_RET1_NOTHROW_WRONLY1_3_LEAF)
DEF_EXT_LIB_BUILTIN (BUILT_IN_MEMPCPY_CHK, "__mempcpy_chk", BT_FN_PTR_PTR_CONST_PTR_SIZE_SIZE, ATTR_RETNONNULL_NOTHROW_LEAF)
-DEF_EXT_LIB_BUILTIN (BUILT_IN_MEMSET_CHK, "__memset_chk", BT_FN_PTR_PTR_INT_SIZE_SIZE, ATTR_RET1_NOTHROW_NONNULL_LEAF)
+DEF_EXT_LIB_BUILTIN (BUILT_IN_MEMSET_CHK, "__memset_chk", BT_FN_PTR_PTR_INT_SIZE_SIZE, ATTR_RET1_NOTHROW_WRONLY1_3_LEAF)
DEF_EXT_LIB_BUILTIN (BUILT_IN_STPCPY_CHK, "__stpcpy_chk", BT_FN_STRING_STRING_CONST_STRING_SIZE, ATTR_RETNONNULL_NOTHROW_LEAF)
DEF_EXT_LIB_BUILTIN (BUILT_IN_STPNCPY_CHK, "__stpncpy_chk", BT_FN_STRING_STRING_CONST_STRING_SIZE_SIZE, ATTR_RETNONNULL_NOTHROW_LEAF)
DEF_EXT_LIB_BUILTIN (BUILT_IN_STRCAT_CHK, "__strcat_chk", BT_FN_STRING_STRING_CONST_STRING_SIZE, ATTR_RET1_NOTHROW_NONNULL_LEAF)
-DEF_EXT_LIB_BUILTIN (BUILT_IN_STRCPY_CHK, "__strcpy_chk", BT_FN_STRING_STRING_CONST_STRING_SIZE, ATTR_RET1_NOTHROW_NONNULL_LEAF)
+DEF_EXT_LIB_BUILTIN (BUILT_IN_STRCPY_CHK, "__strcpy_chk", BT_FN_STRING_STRING_CONST_STRING_SIZE, ATTR_RET1_NOTHROW_WRONLY1_LEAF)
DEF_EXT_LIB_BUILTIN (BUILT_IN_STRNCAT_CHK, "__strncat_chk", BT_FN_STRING_STRING_CONST_STRING_SIZE_SIZE, ATTR_RET1_NOTHROW_NONNULL_LEAF)
-DEF_EXT_LIB_BUILTIN (BUILT_IN_STRNCPY_CHK, "__strncpy_chk", BT_FN_STRING_STRING_CONST_STRING_SIZE_SIZE, ATTR_RET1_NOTHROW_NONNULL_LEAF)
+DEF_EXT_LIB_BUILTIN (BUILT_IN_STRNCPY_CHK, "__strncpy_chk", BT_FN_STRING_STRING_CONST_STRING_SIZE_SIZE, ATTR_RET1_NOTHROW_WRONLY1_3_LEAF)
DEF_EXT_LIB_BUILTIN (BUILT_IN_SNPRINTF_CHK, "__snprintf_chk", BT_FN_INT_STRING_SIZE_INT_SIZE_CONST_STRING_VAR, ATTR_FORMAT_PRINTF_NOTHROW_5_6)
DEF_EXT_LIB_BUILTIN (BUILT_IN_SPRINTF_CHK, "__sprintf_chk", BT_FN_INT_STRING_INT_SIZE_CONST_STRING_VAR, ATTR_NOTHROW_NONNULL_1_FORMAT_PRINTF_4_5)
DEF_EXT_LIB_BUILTIN (BUILT_IN_VSNPRINTF_CHK, "__vsnprintf_chk", BT_FN_INT_STRING_SIZE_INT_SIZE_CONST_STRING_VALIST_ARG, ATTR_FORMAT_PRINTF_NOTHROW_5_0)
@@ -151,5 +151,7 @@ extern internal_fn replacement_internal_fn (gcall *);
extern void warn_string_no_nul (location_t, const char *, tree, tree);
extern tree unterminated_array (tree, tree * = NULL, bool * = NULL);
extern bool builtin_with_linkage_p (tree);
+extern bool check_access (tree, tree, tree, tree, tree, tree, tree);
+
#endif /* GCC_BUILTINS_H */
@@ -125,6 +125,10 @@ 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_read_only_attribute (tree *, tree, tree, int, bool *);
+static tree handle_read_write_attribute (tree *, tree, tree, int, bool *);
+static tree handle_write_only_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 *);
@@ -468,6 +472,12 @@ 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 },
+ { "read_only", 0, 2, false, true, true, false,
+ handle_read_only_attribute, NULL },
+ { "write_only", 0, 2, false, true, true, false,
+ handle_write_only_attribute, NULL },
+ { "read_write", 0, 2, false, true, true, false,
+ handle_read_write_attribute, NULL },
{ NULL, 0, 0, false, false, false, false, NULL, NULL }
};
@@ -3775,6 +3785,228 @@ 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 ( ; ; ++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;
+}
+
+/* Handle attributes read_only, write_only, and read_write. */
+
+static tree
+handle_rdwr_attributes (bool writeable, tree *node, tree name,
+ tree operands, int ARG_UNUSED (flags),
+ bool *no_add_attrs)
+{
+ tree type = *node;
+ tree attrs = TYPE_ATTRIBUTES (type);
+
+ /* If no operands are specified, all non-const pointer arguments are
+ treated as write-only. Verify a full prototype is given so that
+ the arguments will have the correct types when we actually check
+ them later. Avoid diagnosing type-generic built-ins since those
+ have no prototype. */
+ if (!operands
+ && !prototype_p (type)
+ && (!attrs || !lookup_attribute ("type generic", attrs)))
+ {
+ error ("attribute %qE without arguments on a non-prototype", name);
+ *no_add_attrs = true;
+ return NULL_TREE;
+ }
+
+ /* Attribute operands have been specified. Verify that each operand
+ value references a non-const pointer argument to the function. */
+ tree idxnodes[2] = { NULL_TREE, NULL_TREE };
+ tree argtypes[2] = { NULL_TREE, NULL_TREE };
+ unsigned HOST_WIDE_INT idxs[2] = { 0, 0 };
+
+ /* Number of function formal arguments (used in diagnostics). */
+ unsigned argcount = 0;
+ /* Number of attribute operands. */
+ unsigned opcount = 0;
+
+ for (unsigned i = 0; i != 2; ++i, operands = TREE_CHAIN (operands), ++opcount)
+ {
+ if (!operands)
+ break;
+
+ idxnodes[i] = TREE_VALUE (operands);
+
+ if (TREE_CODE (idxnodes[i]) != IDENTIFIER_NODE
+ && TREE_CODE (idxnodes[i]) != FUNCTION_DECL)
+ idxnodes[i] = default_conversion (idxnodes[i]);
+
+ if (!get_attribute_operand (idxnodes[i], idxs + i))
+ {
+ *no_add_attrs = true;
+ idxs[i] = i + 1;
+ }
+
+ argtypes[i] = get_argument_type (type, idxs[i], &argcount);
+ }
+
+ if (*no_add_attrs)
+ {
+ /* Dignose an invalid operand and bail. */
+ if (idxnodes[1])
+ error ("attribute %<%E(%E, %E)%> invalid operand %wu",
+ name, idxnodes[0], idxnodes[1], idxs[0] ? idxs[0] : idxs[1]);
+ else
+ error ("attribute %<%E(%E)%> invalid operand",
+ name, idxnodes[0]);
+
+ return NULL_TREE;
+ }
+
+ if (!argtypes[0] || (idxnodes[1] && !argtypes[1]))
+ {
+ if (idxnodes[1])
+ error ("attribute %<%E(%E, %E)%> operand exceeds number "
+ "of arguments %u",
+ name, idxnodes[0], idxnodes[1], argcount);
+ else if (idxnodes[0])
+ error ("attribute %<%E(%E)%> operand exceeds number of arguments %u",
+ name, idxnodes[0], argcount);
+ else if (!opcount)
+ {
+ /* FIXME: reject empty attribute write_only on function
+ declarations with no non-const object pointer arguments. */
+ tree arg;
+ function_args_iterator it;
+ FOREACH_FUNCTION_ARGS (*node, arg, it)
+ if (POINTER_TYPE_P (arg))
+ return NULL_TREE;
+
+ error ("attribute %<%E%> on a function with no pointer arguments",
+ name);
+ *no_add_attrs = true;
+ return NULL_TREE;
+ }
+ else
+ error ("attribute %<%E%> invalid operand",
+ name);
+
+ *no_add_attrs = true;
+ return NULL_TREE;
+ }
+
+ if (TREE_CODE (argtypes[0]) != POINTER_TYPE)
+ {
+ if (idxnodes[1])
+ error ("attribute %<%E(%E, %E)%> first operand references "
+ "non-pointer argument type %qT",
+ name, idxnodes[0], idxnodes[1], argtypes[0]);
+ else
+ error ("attribute %<%E(%E)%> references non-pointer argument "
+ "type %qT",
+ name, idxnodes[0], argtypes[0]);
+ *no_add_attrs = true;
+ return NULL_TREE;
+ }
+
+ if (writeable)
+ {
+ /* A read_write and write_only attributes must reference non-const
+ arguments. */
+ if (TYPE_READONLY (TREE_TYPE (argtypes[0])))
+ {
+ if (idxnodes[1])
+ error ("attribute %<%E(%E, %E)%> first operand references "
+ "%qs-qualified argument type %qT",
+ name, idxnodes[0], idxnodes[1], "const", argtypes[0]);
+ else
+ error ("attribute %<%E(%E)%> operand references %qs-qualified "
+ "argument type %qT",
+ name, idxnodes[0], "const", argtypes[0]);
+ *no_add_attrs = true;
+ return NULL_TREE;
+ }
+ }
+ else if (!TYPE_READONLY (TREE_TYPE (argtypes[0])))
+ {
+ /* A read_only attributes should reference const-qualified arguments
+ but it's not an error if one doesn't. */
+ if (idxnodes[1])
+ warning (OPT_Wattributes,
+ "attribute %<%E(%E, %E)%> first operand references "
+ "non-%qs argument type %qT",
+ name, idxnodes[0], idxnodes[1], "const", argtypes[0]);
+ else
+ warning (OPT_Wattributes,
+ "attribute %<%E(%E)%> operand references non-%qs "
+ "argument type %qT",
+ name, idxnodes[0], "const", argtypes[0]);
+ }
+
+ if (argtypes[1] && !INTEGRAL_TYPE_P (argtypes[1]))
+ {
+ if (idxnodes[1])
+ error ("attribute %<%E(%E, %E)%> second operand references "
+ "non-integer argument type %qT",
+ name, idxnodes[0], idxnodes[1], argtypes[1]);
+ else
+ error ("attribute %<%E(%E)%> second operand references "
+ "non-integer argument type %qT",
+ name, idxnodes[0], argtypes[1]);
+ *no_add_attrs = true;
+ return NULL_TREE;
+ }
+
+ return NULL_TREE;
+}
+
+/* Handle attribute read_only ([ptrarg [, sizarg]]). */
+
+static tree
+handle_read_only_attribute (tree *node, tree name, tree operands, int flags,
+ bool *no_add_attrs)
+{
+ return handle_rdwr_attributes (false, node, name, operands, flags,
+ no_add_attrs);
+}
+
+/* Handle attribute write_only ([ptrarg [, sizarg]]). */
+
+static tree
+handle_write_only_attribute (tree *node, tree name, tree operands, int flags,
+ bool *no_add_attrs)
+{
+ return handle_rdwr_attributes (true, node, name, operands, flags,
+ no_add_attrs);
+}
+
+/* Handle attribute read_write ([ptrarg [, sizarg]]). */
+
+static tree
+handle_read_write_attribute (tree *node, tree name,
+ tree operands, int flags, bool *no_add_attrs)
+{
+ return handle_rdwr_attributes (true, node, name, operands, flags,
+ no_add_attrs);
+}
+
/* Handle a "nothrow" attribute; arguments as in
struct attribute_spec.handler. */
@@ -4215,7 +4447,7 @@ validate_attribute (location_t atloc, tree oper, tree attr)
consider just its type. */
bool
-has_attribute (location_t atloc, tree t, tree attr, tree (*convert)(tree))
+has_attribute (location_t atloc, tree t, tree attr, tree (*convert)(tree, bool))
{
if (!attr || !t || t == error_mark_node)
return false;
@@ -4348,8 +4580,8 @@ has_attribute (location_t atloc, tree t, tree attr, tree (*convert)(tree))
return false;
/* Convert to make them equality-comparable. */
- v1 = convert (v1);
- v2 = convert (v2);
+ v1 = convert (v1, false);
+ v2 = convert (v2, false);
/* A positive value indicates equality, negative means
"don't know." */
@@ -4369,7 +4601,7 @@ has_attribute (location_t atloc, tree t, tree attr, tree (*convert)(tree))
{
if (tree arg = TREE_VALUE (attr))
{
- arg = convert (TREE_VALUE (arg));
+ arg = convert (TREE_VALUE (arg), false);
if (!tree_fits_uhwi_p (arg))
/* Invalid argument. */;
else if (expr && DECL_P (expr)
@@ -4416,7 +4648,7 @@ has_attribute (location_t atloc, tree t, tree attr, tree (*convert)(tree))
if (tree arg = TREE_VALUE (attr))
{
/* Compare the vector size argument for equality. */
- arg = convert (TREE_VALUE (arg));
+ arg = convert (TREE_VALUE (arg), false);
return tree_int_cst_equal (arg, TYPE_SIZE_UNIT (type)) == 1;
}
else
@@ -4426,7 +4658,7 @@ has_attribute (location_t atloc, tree t, tree attr, tree (*convert)(tree))
{
if (tree arg = TREE_VALUE (attr))
{
- arg = convert (TREE_VALUE (arg));
+ arg = convert (TREE_VALUE (arg), false);
if (expr && DECL_P (expr))
found_match = (DECL_WARN_IF_NOT_ALIGN (expr)
== tree_to_uhwi (arg) * BITS_PER_UNIT);
@@ -5479,7 +5479,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);
@@ -5514,11 +5514,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))
@@ -869,7 +869,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)
@@ -969,13 +969,14 @@ extern tree build_real_imag_expr (location_t, enum tree_code, tree);
a variant of the C language. They are used in c-common.c. */
extern tree build_unary_op (location_t, enum tree_code, tree, bool);
-extern tree build_binary_op (location_t, enum tree_code, tree, tree, bool);
+extern tree build_binary_op (location_t, enum tree_code, tree, tree, bool,
+ bool = true);
extern tree perform_integral_promotions (tree);
/* These functions must be defined by each front-end which implements
a variant of the C language. They are used by port files. */
-extern tree default_conversion (tree);
+extern tree default_conversion (tree, bool = true);
/* Given two integer or real types, return the type for their sum.
Given two compatible ANSI C types, returns the merged type. */
@@ -1376,7 +1377,7 @@ extern void maybe_suggest_missing_token_insertion (rich_location *richloc,
location_t prev_token_loc);
extern tree braced_lists_to_strings (tree, tree);
-extern bool has_attribute (location_t, tree, tree, tree (*)(tree));
+extern bool has_attribute (location_t, tree, tree, tree (*)(tree, bool));
#if CHECKING_P
namespace selftest {
@@ -187,6 +187,14 @@ struct GTY(()) c_parser {
BOOL_BITFIELD in_if_block : 1;
/* True if we want to lex an untranslated string. */
BOOL_BITFIELD lex_untranslated_string : 1;
+ /* True if taking the address of a DECL should not set its DECL_READ_P
+ flag. */
+ BOOL_BITFIELD no_set_read : 1;
+ /* True when parsing an initializer expression. */
+ BOOL_BITFIELD in_init : 1;
+ /* True when parsing a non-empty expression list such as a list of
+ arguments in a function call. */
+ BOOL_BITFIELD in_expr_list : 1;
/* Objective-C specific parser/lexer information. */
@@ -1447,7 +1455,8 @@ static tree c_parser_transaction_cancel (c_parser *);
static struct c_expr c_parser_expression (c_parser *);
static struct c_expr c_parser_expression_conv (c_parser *);
static vec<tree, va_gc> *c_parser_expr_list (c_parser *, bool, bool,
- vec<tree, va_gc> **, location_t *,
+ vec<tree, va_gc> **, tree,
+ location_t *,
tree *, vec<location_t> *,
unsigned int * = NULL);
static struct c_expr c_parser_has_attribute_expression (c_parser *);
@@ -4431,7 +4440,7 @@ c_parser_attribute (c_parser *parser, tree attrs,
tree tree_list;
c_parser_consume_token (parser);
expr_list = c_parser_expr_list (parser, false, true,
- NULL, NULL, NULL, NULL);
+ NULL, NULL, NULL, NULL, NULL);
tree_list = build_tree_list_vec (expr_list);
attr_args = tree_cons (NULL_TREE, arg1, tree_list);
release_tree_vector (expr_list);
@@ -4444,7 +4453,7 @@ c_parser_attribute (c_parser *parser, tree attrs,
else
{
expr_list = c_parser_expr_list (parser, false, true,
- NULL, NULL, NULL, NULL);
+ NULL, NULL, NULL, NULL, NULL);
attr_args = build_tree_list_vec (expr_list);
release_tree_vector (expr_list);
}
@@ -4624,12 +4633,17 @@ c_parser_initializer (c_parser *parser)
return c_parser_braced_init (parser, NULL_TREE, false, NULL);
else
{
+ const bool saved_in_init = parser->in_init;
+ parser->in_init = true;
+
struct c_expr ret;
location_t loc = c_parser_peek_token (parser)->location;
ret = c_parser_expr_no_commas (parser, NULL);
if (TREE_CODE (ret.value) != STRING_CST
&& TREE_CODE (ret.value) != COMPOUND_LITERAL_EXPR)
ret = convert_lvalue_to_rvalue (loc, ret, true, true);
+
+ parser->in_init = saved_in_init;
return ret;
}
}
@@ -7002,6 +7016,9 @@ c_parser_binary_expression (c_parser *parser, struct c_expr *after,
tree sizeof_arg;
} stack[NUM_PRECS];
int sp;
+
+ bool read_p = !parser->no_set_read;
+
/* Location of the binary operator. */
location_t binary_loc = UNKNOWN_LOCATION; /* Quiet warning. */
#define POP \
@@ -7050,10 +7067,10 @@ c_parser_binary_expression (c_parser *parser, struct c_expr *after,
} \
stack[sp - 1].expr \
= convert_lvalue_to_rvalue (stack[sp - 1].loc, \
- stack[sp - 1].expr, true, true); \
+ stack[sp - 1].expr, true, read_p); \
stack[sp].expr \
= convert_lvalue_to_rvalue (stack[sp].loc, \
- stack[sp].expr, true, true); \
+ stack[sp].expr, true, read_p); \
if (__builtin_expect (omp_atomic_lhs != NULL_TREE, 0) && sp == 1 \
&& c_parser_peek_token (parser)->type == CPP_SEMICOLON \
&& ((1 << stack[sp].prec) \
@@ -7071,7 +7088,8 @@ c_parser_binary_expression (c_parser *parser, struct c_expr *after,
stack[sp - 1].expr = parser_build_binary_op (stack[sp].loc, \
stack[sp].op, \
stack[sp - 1].expr, \
- stack[sp].expr); \
+ stack[sp].expr, \
+ read_p); \
sp--; \
} while (0)
gcc_assert (!after || c_dialect_objc ());
@@ -7176,7 +7194,7 @@ c_parser_binary_expression (c_parser *parser, struct c_expr *after,
src_range = stack[sp].expr.src_range;
stack[sp].expr
= convert_lvalue_to_rvalue (stack[sp].loc,
- stack[sp].expr, true, true);
+ stack[sp].expr, true, read_p);
stack[sp].expr.value = c_objc_common_truthvalue_conversion
(stack[sp].loc, default_conversion (stack[sp].expr.value));
c_inhibit_evaluation_warnings += (stack[sp].expr.value
@@ -7187,7 +7205,7 @@ c_parser_binary_expression (c_parser *parser, struct c_expr *after,
src_range = stack[sp].expr.src_range;
stack[sp].expr
= convert_lvalue_to_rvalue (stack[sp].loc,
- stack[sp].expr, true, true);
+ stack[sp].expr, true, read_p);
stack[sp].expr.value = c_objc_common_truthvalue_conversion
(stack[sp].loc, default_conversion (stack[sp].expr.value));
c_inhibit_evaluation_warnings += (stack[sp].expr.value
@@ -7339,7 +7357,10 @@ c_parser_unary_expression (c_parser *parser)
case CPP_AND:
c_parser_consume_token (parser);
op = c_parser_cast_expression (parser, NULL);
- mark_exp_read (op.value);
+ /* Avoid setting DECL_READ_P of a referenced object when its
+ address is being passed to a write-only function parameter. */
+ if (!parser->no_set_read)
+ mark_exp_read (op.value);
return parser_build_unary_op (op_loc, ADDR_EXPR, op);
case CPP_MULT:
{
@@ -7502,7 +7523,10 @@ c_parser_sizeof_expression (c_parser *parser)
sizeof_expr:
c_inhibit_evaluation_warnings--;
in_sizeof--;
- mark_exp_read (expr.value);
+ /* Avoid setting DECL_READ_P of a sizeof operand when the result
+ is passed to a function with a write-only function parameter. */
+ if (!parser->in_expr_list)
+ mark_exp_read (expr.value);
if (TREE_CODE (expr.value) == COMPONENT_REF
&& DECL_C_BIT_FIELD (TREE_OPERAND (expr.value, 1)))
error_at (expr_loc, "%<sizeof%> applied to a bit-field");
@@ -9618,8 +9642,10 @@ c_parser_postfix_expression_after_primary (c_parser *parser,
exprlist = NULL;
else
exprlist = c_parser_expr_list (parser, true, false, &origtypes,
+ expr.value,
sizeof_arg_loc, sizeof_arg,
&arg_loc, &literal_zero_mask);
+
c_parser_skip_until_found (parser, CPP_CLOSE_PAREN,
"expected %<)%>");
orig_expr = expr;
@@ -9851,6 +9877,32 @@ c_parser_check_literal_zero (c_parser *parser, unsigned *literal_zero_mask,
}
}
+/* Return true if expression number IDX and type ARGTYPE eclared with
+ the write-only attribute in ATTRS is itself declared write-only.
+ ATTRS may be null. */
+
+static bool
+is_write_only_p (tree attrs, tree argtype, unsigned idx)
+{
+ if (!attrs)
+ return false;
+
+ if (!POINTER_TYPE_P (argtype))
+ return false;
+
+ if (TYPE_READONLY (TREE_TYPE (argtype)))
+ return false;
+
+ for (tree a = attrs; a; a = TREE_CHAIN (a))
+ {
+ unsigned HOST_WIDE_INT wronlyidx;
+ if (!get_attribute_operand (a, &wronlyidx) || wronlyidx == idx)
+ return true;
+ }
+ return false;
+}
+
+
/* Parse a non-empty list of expressions. If CONVERT_P, convert
functions and arrays to pointers and lvalues to rvalues. If
FOLD_P, fold the expressions. If LOCATIONS is non-NULL, save the
@@ -9863,7 +9915,7 @@ c_parser_check_literal_zero (c_parser *parser, unsigned *literal_zero_mask,
static vec<tree, va_gc> *
c_parser_expr_list (c_parser *parser, bool convert_p, bool fold_p,
- vec<tree, va_gc> **p_orig_types,
+ vec<tree, va_gc> **p_orig_types, tree func,
location_t *sizeof_arg_loc, tree *sizeof_arg,
vec<location_t> *locations,
unsigned int *literal_zero_mask)
@@ -9881,9 +9933,44 @@ c_parser_expr_list (c_parser *parser, bool convert_p, bool fold_p,
if (literal_zero_mask)
c_parser_check_literal_zero (parser, literal_zero_mask, 0);
- expr = c_parser_expr_no_commas (parser, NULL);
+
+ tree functype = func ? TREE_TYPE (func) : NULL_TREE;
+ if (functype)
+ {
+ while (POINTER_TYPE_P (functype))
+ functype = TREE_TYPE (functype);
+ }
+
+ tree attrs = (functype && functype != error_mark_node
+ ? lookup_attribute ("write_only", TYPE_ATTRIBUTES (functype))
+ : NULL_TREE);
+
+ tree argtypes = attrs ? TYPE_ARG_TYPES (functype) : NULL_TREE;
+
+ /* Determine if the first function parameter in the list is declared
+ write-only and only mark the corresponding argument DECL_READ_P()
+ if it either isn't write-only or doesn't reference the address of
+ a declared object. */
+ bool wronly = (argtypes
+ && !parser->in_init
+ && is_write_only_p (attrs, TREE_VALUE (argtypes), idx + 1));
+
+ {
+ /* Avoid setting the read bit for write-only decls. */
+ bool save_set_read = parser->no_set_read;
+ parser->no_set_read = wronly;
+ expr = c_parser_expr_no_commas (parser, NULL);
+ parser->no_set_read = save_set_read;
+ }
+
if (convert_p)
- expr = convert_lvalue_to_rvalue (expr.get_location (), expr, true, true);
+ {
+ /* Allow DECL_READ_P to be set if the expression is a pointer. */
+ wronly &= !DECL_P (expr.value) || !POINTER_TYPE_P (TREE_TYPE (expr.value));
+ expr = convert_lvalue_to_rvalue (expr.get_location (), expr, true,
+ !wronly);
+ }
+
if (fold_p)
expr.value = c_fully_fold (expr.value, false, NULL);
ret->quick_push (expr.value);
@@ -9902,10 +9989,41 @@ c_parser_expr_list (c_parser *parser, bool convert_p, bool fold_p,
c_parser_consume_token (parser);
if (literal_zero_mask)
c_parser_check_literal_zero (parser, literal_zero_mask, idx + 1);
- expr = c_parser_expr_no_commas (parser, NULL);
+
+ /* Determine if the next function parameter in the list is declared
+ write-only and only mark the corresponding argument DECL_READ_P()
+ if it either isn't write-only or doesn't reference the address of
+ a declared object. */
+ if (argtypes && attrs)
+ {
+ argtypes = TREE_CHAIN (argtypes);
+ wronly = (!parser->in_init
+ && is_write_only_p (attrs, TREE_VALUE (argtypes), idx + 2));
+ }
+ else
+ wronly = false;
+
+ {
+ /* Avoid setting the read bit for write-only decls. */
+ bool save_set_read = parser->no_set_read;
+ parser->no_set_read = wronly;
+ /* Indicate a an expression list is being parsed. */
+ bool save_in_expr_list = parser->in_expr_list;
+ parser->in_expr_list = true;
+ expr = c_parser_expr_no_commas (parser, NULL);
+ /* Restore both bits. */
+ parser->no_set_read = save_set_read;
+ parser->in_expr_list = save_in_expr_list;
+ }
+
if (convert_p)
- expr = convert_lvalue_to_rvalue (expr.get_location (), expr, true,
- true);
+ {
+ wronly &= (!DECL_P (expr.value)
+ || !POINTER_TYPE_P (TREE_TYPE (expr.value)));
+ expr = convert_lvalue_to_rvalue (expr.get_location (), expr, true,
+ !wronly);
+ }
+
if (fold_p)
expr.value = c_fully_fold (expr.value, false, NULL);
vec_safe_push (ret, expr.value);
@@ -9923,6 +10041,7 @@ c_parser_expr_list (c_parser *parser, bool convert_p, bool fold_p,
}
if (orig_types)
*p_orig_types = orig_types;
+
return ret;
}
@@ -11016,8 +11135,8 @@ static tree
c_parser_objc_keywordexpr (c_parser *parser)
{
tree ret;
- vec<tree, va_gc> *expr_list = c_parser_expr_list (parser, true, true,
- NULL, NULL, NULL, NULL);
+ vec<tree, va_gc> *expr_list = c_parser_expr_list (parser, true, true, NULL,
+ NULL_TREE, NULL, NULL, NULL);
if (vec_safe_length (expr_list) == 1)
{
/* Just return the expression, remove a level of
@@ -11948,7 +12067,7 @@ c_parser_oacc_wait_list (c_parser *parser, location_t clause_loc, tree list)
if (!parens.require_open (parser))
return list;
- args = c_parser_expr_list (parser, false, true, NULL, NULL, NULL, NULL);
+ args = c_parser_expr_list (parser, false, true, NULL, NULL, NULL, NULL, NULL);
args_tree = build_tree_list_vec (args);
for (t = args_tree; t; t = TREE_CHAIN (t))
@@ -665,7 +665,7 @@ extern struct c_expr parser_build_unary_op (location_t, enum tree_code,
struct c_expr);
extern struct c_expr parser_build_binary_op (location_t,
enum tree_code, struct c_expr,
- struct c_expr);
+ struct c_expr, bool = true);
extern tree build_conditional_expr (location_t, tree, bool, tree, tree,
location_t, tree, tree, location_t);
extern tree build_compound_expr (location_t, tree, tree);
@@ -2146,14 +2146,30 @@ perform_integral_promotions (tree exp)
In addition, manifest constants symbols are replaced by their values. */
tree
-default_conversion (tree exp)
+default_conversion (tree exp, bool read_p /* = true */)
{
tree orig_exp;
tree type = TREE_TYPE (exp);
enum tree_code code = TREE_CODE (type);
tree promoted_type;
- mark_exp_read (exp);
+ if (!read_p)
+ {
+ /* Set READ_P for expressions that don't refer to declared objects. */
+ tree tmp = exp;
+ STRIP_NOPS (tmp);
+ if (TREE_CODE (tmp) == ADDR_EXPR)
+ tmp = TREE_OPERAND (tmp, 0);
+
+ while (handled_component_p (tmp))
+ tmp = TREE_OPERAND (tmp, 0);
+
+ if (!DECL_P (tmp))
+ read_p = true;
+ }
+
+ if (read_p)
+ mark_exp_read (exp);
/* Functions and arrays have been converted during parsing. */
gcc_assert (code != FUNCTION_TYPE);
@@ -3718,7 +3734,8 @@ char_type_p (tree type)
struct c_expr
parser_build_binary_op (location_t location, enum tree_code code,
- struct c_expr arg1, struct c_expr arg2)
+ struct c_expr arg1, struct c_expr arg2,
+ bool read_p /* = true */)
{
struct c_expr result;
@@ -3732,7 +3749,7 @@ parser_build_binary_op (location_t location, enum tree_code code,
: TREE_TYPE (arg2.value));
result.value = build_binary_op (location, code,
- arg1.value, arg2.value, true);
+ arg1.value, arg2.value, true, read_p);
result.original_code = code;
result.original_type = NULL;
@@ -11354,7 +11371,7 @@ build_vec_cmp (tree_code code, tree type,
tree
build_binary_op (location_t location, enum tree_code code,
- tree orig_op0, tree orig_op1, bool convert_p)
+ tree orig_op0, tree orig_op1, bool convert_p, bool read_p)
{
tree type0, type1, orig_type0, orig_type1;
tree eptype;
@@ -11459,8 +11476,8 @@ build_binary_op (location_t location, enum tree_code code,
if (convert_p
&& VECTOR_TYPE_P (TREE_TYPE (op0)) == VECTOR_TYPE_P (TREE_TYPE (op1)))
{
- op0 = default_conversion (op0);
- op1 = default_conversion (op1);
+ op0 = default_conversion (op0, read_p);
+ op1 = default_conversion (op1, read_p);
}
orig_type0 = type0 = TREE_TYPE (op0);
@@ -52,6 +52,8 @@ along with GCC; see the file COPYING3. If not see
#include "tree-ssa-strlen.h"
#include "intl.h"
#include "stringpool.h"
+#include "hash-map.h"
+#include "hash-traits.h"
#include "attribs.h"
#include "builtins.h"
#include "gimple-fold.h"
@@ -1862,6 +1864,109 @@ maybe_complain_about_tail_call (tree call_expr, const char *reason)
error_at (EXPR_LOCATION (call_expr), "cannot tail-call: %s", reason);
}
+/* Used to define rdwr_map below. */
+struct rdwr_access_hash: int_hash<int, -1> { };
+
+/* A mapping between argument number corresponding to attribute read_only,
+ write_only, or read_write operand and the read/write specification. */
+typedef hash_map<rdwr_access_hash, attr_access> rdwr_map;
+
+/* Initialize a mapping for a call to function FNDECL declared with
+ attributes read_only, write_only, or read_write. Each attribute
+ operand inserts one entry into the mapping with the operand number
+ as the key. */
+
+static void
+init_attr_rdwr_indices (rdwr_map *rwm, tree fndecl)
+{
+ if (!fndecl)
+ return;
+
+ /* Iterate over arguments to functions declared with attribute
+ read_only or read_write and diagnose those that are not
+ initialized. */
+ const attr_access::Kind access_kinds[] = {
+ attr_access::read_only, attr_access::read_write, attr_access::write_only
+ };
+
+ for (unsigned i = 0; i != sizeof access_kinds / sizeof *access_kinds; ++i)
+ {
+ attr_access acc = { };
+ acc.kind = access_kinds[i];
+ while (get_attr_access_ptr_and_size (TREE_TYPE (fndecl), &acc))
+ {
+ rwm->put (acc.ptrarg, acc);
+ if (acc.ptrarg != UINT_MAX)
+ rwm->put (acc.sizarg, acc);
+ }
+ }
+}
+
+/* Iterate over read-only, read-write, and write-only arguments
+ and diagnose past-the-end accesses in the function call EXP. */
+
+static void
+maybe_warn_rdwr_sizes (rdwr_map *rwm, tree exp)
+{
+ for (rdwr_map::iterator it = rwm->begin (); it != rwm->end (); ++it)
+ {
+ std::pair<int, attr_access> access = *it;
+
+ /* Get the function call arguments corresponding to the attribute's
+ first and (optionally) second operands. When both operands have
+ been specified there will be two entries in *RWM, one for each.
+ They are cross-referenced by their respective argument numbers
+ in ACCESS.PTRARG and ACCESS.SIZARG. */
+ int ptridx = access.second.ptrarg;
+ int sizidx = access.second.sizarg;
+
+ gcc_assert (ptridx != -1);
+ gcc_assert (access.first == ptridx || access.first == sizidx);
+
+ /* The pointer is set to null for the entry corresponding to
+ the size argument. Skip it. It's handled when the entry
+ corresponding to the pointer argument comes up. */
+ if (!access.second.ptr)
+ continue;
+
+ /* If only one attribute operand was specified the size isn't
+ available and so continue to the next one. */
+
+ /* FIXME: determine the size using some heuristic -- e.g., for
+ complete types, by assuming it's equal to the size of the type
+ the pointer points to. */
+ if (sizidx == -1)
+ continue;
+
+ tree ptr = access.second.ptr;
+ tree size = rwm->get (sizidx)->size;
+ tree objsize = compute_objsize (ptr, 0);
+
+ tree srcsize;
+ if (access.second.kind == attr_access::write_only)
+ {
+ /* For a write-only argument there is no source. */
+ srcsize = NULL_TREE;
+ }
+ else
+ {
+ /* For read-only and read-write attributes also set the source
+ size. */
+ srcsize = objsize;
+ if (access.second.kind == attr_access::read_only)
+ {
+ /* For a read-only attribute there is no destination so
+ clear OBJSIZE. This emits "reading N bytes" kind of
+ diagnostics instead of the "writing N bytes" kind. */
+ objsize = NULL_TREE;
+ }
+ }
+
+ check_access (exp, NULL_TREE, NULL_TREE, size, /*maxread=*/ NULL_TREE,
+ srcsize, objsize);
+ }
+}
+
/* Fill in ARGS_SIZE and ARGS array based on the parameters found in
CALL_EXPR EXP.
@@ -1978,6 +2083,11 @@ initialize_argument_information (int num_actuals ATTRIBUTE_UNUSED,
/* Array for up to the two attribute alloc_size arguments. */
tree alloc_args[] = { NULL_TREE, NULL_TREE };
+ /* Map of attribute read_only, write_only, or read_write specifications
+ for function arguments. */
+ rdwr_map rdwr_idx;
+ init_attr_rdwr_indices (&rdwr_idx, fndecl);
+
/* I counts args in order (to be) pushed; ARGPOS counts in order written. */
for (argpos = 0; argpos < num_actuals; i--, argpos++)
{
@@ -2218,6 +2328,22 @@ initialize_argument_information (int num_actuals ATTRIBUTE_UNUSED,
alloc_args[0] = args[i].tree_value;
else if (argpos == alloc_idx[1])
alloc_args[1] = args[i].tree_value;
+
+ /* Save the actual argument that corresponds to the access attribute
+ operand for later processing. */
+ if (attr_access *access = rdwr_idx.get (argpos))
+ {
+ if (POINTER_TYPE_P (type))
+ {
+ access->ptr = args[i].tree_value;
+ gcc_assert (access->size == NULL_TREE);
+ }
+ else
+ {
+ access->size = args[i].tree_value;
+ gcc_assert (access->ptr == NULL_TREE);
+ }
+ }
}
if (alloc_args[0])
@@ -2230,6 +2356,9 @@ initialize_argument_information (int num_actuals ATTRIBUTE_UNUSED,
/* Detect passing non-string arguments to functions expecting
nul-terminated strings. */
maybe_warn_nonstring_arg (fndecl, exp);
+
+ /* Check read_only, write_only, and read_write arguments. */
+ maybe_warn_rdwr_sizes (&rdwr_idx, exp);
}
/* Update ARGS_SIZE to contain the total size for the argument block.
@@ -2191,7 +2191,7 @@ cp_default_conversion (tree exp, tsubst_flags_t complain)
/* C version. */
tree
-default_conversion (tree exp)
+default_conversion (tree exp, bool /*read_p*/)
{
return cp_default_conversion (exp, tf_warning_or_error);
}
@@ -4309,7 +4309,7 @@ enum_cast_to_int (tree op)
/* For the c-common bits. */
tree
build_binary_op (location_t location, enum tree_code code, tree op0, tree op1,
- bool /*convert_p*/)
+ bool /*convert_p*/, bool /*read_p*/)
{
return cp_build_binary_op (location, code, op0, op1, tf_warning_or_error);
}
@@ -3850,6 +3850,86 @@ At present, a declaration to which @code{weakref} is attached can
only be @code{static}.
+@item read_only
+@itemx read_only
+@itemx read_only (@var{arg-index})
+@itemx read_only (@var{ref-index}, @var{size-index})
+@cindex @code{read_only} function attribute
+The @code{read_only} attribute specifies that a function whose by-reference
+arguments of @code{const}-qualified types denoted by @var{ref-index} (or
+all such arguments if no @var{ref-index} is specified) are only used to read
+from the objects referenced by the argument but not write to them. Passing
+in the address of an uninitialized object is undefined.
+
+For example, the declaration below indicates that the @code{get_time} function
+only reads the @code{struct tm} object pointed-to by the argument but doesn't
+modify it. Casting away the constness and modifying the argument is undefined.
+This information may be relied on to emit more efficient code at the call site,
+by one of the @option{-Wunused} options. Built-in functions such as
+@code{strlen} declare their const pointer arguments with the @code{read_only}
+attribute.
+
+@smallexample
+time_t get_time (const struct tm *) __attribute__ ((read_only));
+@end smallexample
+
+The @var{size-index} argument optionally specifies the number of elements
+of the array referenced by @var{ref-index} the function may read. Passing
+in an array with fewer elements is diagnosed by @option{-Wstringop-overflow}.
+For example, the declaration below indicates that the function reads as many
+elements of the array referenced by the first argument as specified by
+the second argument.
+
+@smallexample
+int sum (const int *, unsigned) __attribute__ ((read_only (1, 2)));
+@end smallexample
+
+
+@item read_write
+@itemx read_write
+@itemx read_write (@var{arg-index})
+@itemx read_write (@var{ref-index}, @var{size-index})
+@cindex @code{read_write} function attribute
+The @code{read_write} attribute specifies that a function whose by-reference
+arguments denoted by @var{ref-index} (or all such arguments if no
+@var{ref-index} is specified) are used to both read from the objects
+referenced by the argument and to write to them. Passing in the address
+of an uninitialized object is undefined.
+
+
+@item write_only
+@itemx write_only
+@itemx write_only (@var{arg-index})
+@itemx write_only (@var{ref-index}, @var{size-index})
+@cindex @code{write_only} function attribute
+The @code{write_only} attribute specifies that a function whose by-reference
+arguments of non-@code{const}-qualified types denoted by @var{ref-index} (or
+all such arguments if no @var{ref-index} is specified) are only used to write
+into the objects referenced by the argument but not read from it. For example,
+the declaration below indicates that the @code{init_tm} function only writes
+to the @code{struct tm} object pointed-to by the argument but doesn't read
+from it. This information may be relied on to emit more efficient code at
+the call site, and diagnose initialized but otherwise unused objects by one
+of the @option{-Wunused} options. Built-in functions such as @code{memset}
+and @code{strcpy} declare their non-const pointer argument with
+the @code{write_only} attribute.
+
+@smallexample
+int init_tm (struct tm *) __attribute__ ((write_only));
+@end smallexample
+
+The @var{size-index} argument optionally specifies the number of elements
+of the array referenced by @var{ref-index} the function may write. Passing
+in an array with fewer elements is diagnosed by @option{-Wstringop-overflow}.
+For example, the declaration below indicates that the function writes as many
+elements of the array referenced by the first argument as specified by
+the second argument.
+
+@smallexample
+void init (int *, unsigned, int) __attribute__ ((write_only (1, 2)));
+@end smallexample
+
+
@end table
@c This is the end of the target-independent attribute table
@@ -24,6 +24,8 @@ along with GCC; see the file COPYING3. If not see
#include "tree-ssa-alias.h"
#include "gimple-expr.h"
+#include "function.h"
+#include "basic-block.h"
typedef gimple *gimple_seq_node;
@@ -1,7 +1,7 @@
/* Test -Wsizeof-pointer-memaccess warnings. */
/* { dg-do compile } */
-/* { dg-options "-Wall -Wno-array-bounds -Wno-sizeof-array-argument" } */
-/* { dg-options "-Wall -Wno-array-bounds -Wno-sizeof-array-argument -Wno-c++-compat" { target c } } */
+/* { dg-options "-Wall -Wno-array-bounds -Wno-sizeof-array-argument -Wno-stringop-overflow" } */
+/* { dg-options "-Wall -Wno-array-bounds -Wno-sizeof-array-argument -Wno-c++-compat -Wno-stringop-overflow" { target c } } */
/* { dg-require-effective-target alloca } */
typedef __SIZE_TYPE__ size_t;
@@ -30,7 +30,7 @@ void foo (void **);
void
f1 (void *x)
{
- struct A a, *pa1 = &a;
+ struct A a = { }, *pa1 = &a;
TA *pa2 = &a;
PA pa3 = &a;
PTA pa4 = &a;
@@ -484,5 +484,6 @@ f4 (char *x, char **y, int z, char w[64])
stpncpy (x, s3, sizeof (s3)); /* { dg-warning "call is the same expression as the source; did you mean to use the size of the destination?" } */
}
+/* { dg-prune-output "-Wunused-but-set-variable" } */
/* { dg-prune-output "\[\n\r\]*writing\[\n\r\]*" } */
/* { dg-prune-output "\[\n\r\]*reading\[\n\r\]*" } */
@@ -9,3 +9,5 @@ int main(int argc, char *argv[])
float y[argc];
return 0 == __builtin_memcpy(y, x, argc * sizeof(*x));
}
+
+/* { dg-prune-output "-Wunused-but-set-variable" } */
new file mode 100644
@@ -0,0 +1,225 @@
+/* PR c/80806 - gcc does not warn if local array is memset only
+ Verify that attribute write_only implicitly applied to common
+ built-ins or explicitly specified on user-defined functions is
+ used by -Wunused-but-set-variable and -Wunused-but-set-parameter.
+ { dg-do compile }
+ { dg-options "-Wunused-but-set-variable -Wunused-but-set-parameter" } */
+
+typedef __SIZE_TYPE__ size_t;
+
+void bcopy (const void*, void*, size_t);
+void bzero (void*, size_t);
+void* memcpy (void*, const void*, size_t);
+void* memset (void*, int, size_t);
+
+char* strcat (char*, const char*);
+char* strcpy (char*, const char*);
+char* strncat (char*, const char*, size_t);
+char* strncpy (char*, const char*, size_t);
+
+void* malloc (size_t);
+void free (void*);
+
+#define WRONLY(...) __attribute__ ((write_only (__VA_ARGS__)))
+
+void wronly_1 (void*) WRONLY (1);
+
+struct MemArrays
+{
+ int a[2], b[3][3];
+};
+
+enum { N = sizeof (int) };
+
+void test_bcopy (int a, /* { dg-warning "-Wunused-but-set-parameter" } */
+ void *p,
+ struct MemArrays *pma)
+{
+ int i; /* { dg-warning "-Wunused-but-set-variable" } */
+
+ bcopy (p, &a, N);
+ bcopy (p, &i, N);
+
+ char arr[8]; /* { dg-warning "-Wunused-but-set-variable" } */
+
+ bcopy (p, &arr, N);
+ bcopy (p, &arr, sizeof arr);
+ bcopy (p, arr, N);
+ bcopy (p, arr + 0, N);
+ bcopy (p, arr + 1, N);
+ bcopy (p, &arr[0], N);
+ bcopy (p, &arr[1], N);
+ bcopy (p, &arr[2] + 1, N);
+
+ struct MemArrays s; /* { dg-warning "-Wunused-but-set-variable" } */
+
+ bcopy (p, s.a, N);
+ bcopy (p, s.a, sizeof s.a);
+ bcopy (p, &s.a[0], N);
+ bcopy (p, &s.a[1], N);
+ bcopy (p, &s.b[0], N);
+ bcopy (p, &s.b[0] + 1, N);
+ bcopy (p, &s.b[0][0], N);
+ bcopy (p, &s.b[0][0] + 0, N);
+ bcopy (p, &s.b[0][0] + 1, N);
+ bcopy (p, &s.b[0][1], N);
+ bcopy (p, &s.b[1][1] + 1, N);
+
+ struct MemArrays t;
+
+ bcopy (p, &t.b[0] + 1, 1);
+ bcopy (&t, p, 4);
+
+ bcopy (p, pma->a, N);
+}
+
+
+void test_bzero (int a) /* { dg-warning "-Wunused-but-set-parameter" } */
+{
+ int i; /* { dg-warning "-Wunused-but-set-variable" } */
+ char arr[8]; /* { dg-warning "-Wunused-but-set-variable" } */
+
+ bzero (&a, N);
+ bzero (&a + 0, N);
+ bzero (&i, N);
+ bzero (&i + 0, N);
+
+ bzero (arr, N);
+ bzero (arr + 0, N);
+ bzero (arr + 1, N);
+ bzero (&arr[0] + 1, N);
+ bzero (&arr[0], N);
+ bzero (&arr[1], N);
+}
+
+void test_memset (int a) /* { dg-warning "-Wunused-but-set-parameter" } */
+{
+ int i; /* { dg-warning "-Wunused-but-set-variable" } */
+ char arr[8]; /* { dg-warning "-Wunused-but-set-variable" } */
+
+ memset (&a, 0, N);
+ memset (&i, 0, N);
+ memset (&arr, 0, N);
+}
+
+void test_memcpy (int a, /* { dg-warning "-Wunused-but-set-parameter" } */
+ const void *p)
+{
+ int i; /* { dg-warning "-Wunused-but-set-variable" } */
+ char arr[8]; /* { dg-warning "-Wunused-but-set-variable" } */
+
+ memcpy (&a, p, N);
+ memcpy (&i, p, N);
+ memcpy (&arr, p, N);
+}
+
+void test_strcat (const char *s)
+{
+ char c = '\0';
+ char arr[8] = "";
+
+ strcat (&c, "");
+ strcat (arr, s);
+}
+
+void test_strcpy (const char *s)
+{
+ char c; /* { dg-warning "-Wunused-but-set-variable" } */
+ char d = '\0'; /* { dg-warning "-Wunused-but-set-variable" } */
+ char arr[8]; /* { dg-warning "-Wunused-but-set-variable" } */
+ char arr2[8] = ""; /* { dg-warning "-Wunused-but-set-variable" } */
+
+ strcpy (&c, "");
+ strcpy (&d, "");
+ strcpy (arr, s);
+ strcpy (arr2, s);
+}
+
+void test_strncpy (const char *s, unsigned n)
+{
+ char c; /* { dg-warning "-Wunused-but-set-variable" } */
+ char d = '\0'; /* { dg-warning "-Wunused-but-set-variable" } */
+ char arr[8]; /* { dg-warning "-Wunused-but-set-variable" } */
+ char arr2[8] = ""; /* { dg-warning "-Wunused-but-set-variable" } */
+
+ strncpy (&c, "", 1);
+ strncpy (&d, "", n);
+ strncpy (arr, s, n);
+ strncpy (arr2, s, n);
+}
+
+/* Verify that passing a local pointer initialized to the result of
+ malloc() (anything else should work as well) to free() on doesn't
+ trigger a warning because the pointer value is obviously read. */
+
+void test_malloc_and_free (size_t n)
+{
+ void *q = malloc (n);
+ free (q);
+}
+
+/* Verify that setting a local variable to a copy of an argument and
+ calling free() on the copy doesn't trigger a warning. */
+
+void test_free_argcpy (void *p)
+{
+ void *q = p;
+ free (q);
+}
+
+/* Similar to the free test above, verify that setting a local variable
+ to a copy of an argument and calling free() on the copy doesn't trigger
+ a warning. */
+
+void test_memset_argcpy (void *p, size_t n)
+{
+ void *q = p;
+ memset (q, 0, n);
+}
+
+void test_usrdef (int a) /* { dg-warning "-Wunused-but-set-parameter" } */
+{
+ int i; /* { dg-warning "-Wunused-but-set-variable" } */
+ char arr[8]; /* { dg-warning "-Wunused-but-set-variable" } */
+
+ wronly_1 (&a);
+ wronly_1 (&i);
+ wronly_1 (&arr);
+
+ wronly_1 (arr);
+ wronly_1 (arr + 1);
+ wronly_1 (&arr[1]);
+}
+
+/* Verify that using a pointer to a function works. */
+typedef struct S
+{
+ void (*pf)(void*) WRONLY (1);
+ struct {
+ struct {
+ void (*pf)(void*) WRONLY (1);
+ } s2;
+ } s1;
+} S;
+
+void test_funcptr (int a, /* { dg-warning "-Wunused-but-set-parameter" } */
+ S *ps)
+{
+ int i; /* { dg-warning "-Wunused-but-set-variable" } */
+ char arr[8]; /* { dg-warning "-Wunused-but-set-variable" } */
+
+ ps->pf (&a);
+ ps->pf (&i);
+ ps->pf (&arr);
+}
+
+void test_funcptr2 (int a, /* { dg-warning "-Wunused-but-set-parameter" } */
+ S *ps)
+{
+ int i; /* { dg-warning "-Wunused-but-set-variable" } */
+ char arr[8]; /* { dg-warning "-Wunused-but-set-variable" } */
+
+ ps->s1.s2.pf (&a);
+ ps->s1.s2.pf (&i);
+ ps->s1.s2.pf (&arr);
+}
@@ -32,5 +32,6 @@ test (void)
p = calloc2 (2, __INT_MAX__ >= 1700000 ? 424242 : __INT_MAX__ / 4, 5);
strcpy (p, "World");
strcpy (p, "Hello World"); /* { dg-warning "writing" "strcpy" } */
-}
+ (void)&p; /* suppress -Wunused-but-set-variable */
+}
new file mode 100644
@@ -0,0 +1,85 @@
+/* Test to verify that attributes read_only and read_write are used
+ by -Wuninitialized to issue warnings.
+ { dg-do compile }
+ { dg-options "-Wuninitialized" } */
+
+typedef __SIZE_TYPE__ size_t;
+
+extern char* memcpy (void* restrict, const void* restrict, size_t);
+extern char* memmove (void*, const void*, size_t);
+
+extern char* stpcpy (char* restrict, const char* restrict);
+extern char* stpncpy (char* restrict, const char* restrict, size_t);
+extern char* strcpy (char* restrict, const char* restrict);
+extern char* strncpy (char* restrict, const char* restrict, size_t);
+
+void* malloc (size_t);
+void free (void*);
+
+#define RDONLY(...) __attribute__ ((read_only (__VA_ARGS__)))
+
+void sink (void*);
+
+/* Due to PR 81809 only the first uninitialized read in a function
+ is diagnosed. The subsequent ones are not because the variable's
+ address is considered to have escaped. */
+
+void test_memcpy (char *d)
+{
+ char a7[7];
+ memcpy (d, a7, sizeof a7); /* { dg-warning ".a7. is used uninitialized" } */
+}
+
+void* test_memmove (void)
+{
+ char a7[7];
+ enum { n = sizeof a7 - 1 };
+ memmove (a7, a7 + 1, n); /* { dg-warning ".a7. is used uninitialized" } */
+ sink (a7);
+}
+
+void test_strcpy (char *d)
+{
+ char a7[7];
+ strcpy (d, a7); /* { dg-warning ".a7. is used uninitialized" } */
+}
+
+void test_stpncpy (char *d)
+{
+ char a7[7];
+ stpncpy (d, a7, sizeof a7); /* { dg-warning ".a7. is used uninitialized" } */
+}
+
+void test_strncpy (char *d)
+{
+ char a7[7];
+ strncpy (d, a7, sizeof a7); /* { dg-warning ".a7. is used uninitialized" } */
+}
+
+
+int rdonly_pv_1 (const void*) RDONLY (1);
+
+void test_rdonly_1 (void)
+{
+ int i;
+ rdonly_pv_1 (&i); /* { dg-warning ".i. is used uninitialized" } */
+}
+
+void test_rdonly_2 (void)
+{
+ int a[2];
+ rdonly_pv_1 (a); /* { dg-warning ".a. is used uninitialized" } */
+}
+
+
+int rdonly_pv_s_1_2 (const void*, size_t) RDONLY (1, 2);
+
+void test_rdonly_1_2 (void)
+{
+ int a[4];
+ a[2] = 0;
+
+ enum { n = sizeof a[1] };
+ rdonly_pv_s_1_2 (a + 1, n); /* { dg-warning ".a. is used uninitialized" } */
+}
+
new file mode 100644
@@ -0,0 +1,63 @@
+/* Test to verify the handling of attribute read_only syntax.
+ { dg-do compile }
+ { dg-options "-Wall -ftrack-macro-expansion=0" } */
+
+#define RDONLY(...) __attribute__ ((read_only (__VA_ARGS__)))
+
+int __attribute__ ((read_only))
+rdonly_v_all (void); /* { dg-error "attribute .read_only. on a function with no pointer arguments" } */
+
+int __attribute__ ((read_only))
+rdonly_i_all (int); /* { dg-error "attribute .read_only. on a function with no pointer arguments" } */
+
+int __attribute__ ((read_only))
+rdonly_pi_all (const int*);
+
+int __attribute__ ((read_only))
+rdonly_pv_pi_all (const void*, const int*);
+
+int __attribute__ ((read_only))
+rdonly_i_pc_i_pd_all (int, const char*, int, const double*);
+
+int RDONLY (1)
+rdonly_pcv_1 (const void*);
+int RDONLY (2)
+rdonly_i_pcv_2 (int, const void*);
+int RDONLY (3)
+rdonly_i_i_pcv_3 (int, int, const void*);
+
+int RDONLY (0 + 1)
+rdonly_pcv_0p1 (const void*);
+
+int RDONLY (2 - 1)
+rdonly_pcv_2m1 (const void*);
+
+int RDONLY (1, 1)
+rdonly_pv_pi_1_1 (const void*, const int*); /* { dg-error "attribute .read_only\\(1, 1\\). second operand references non-integer argument type .const void *." } */
+
+int RDONLY (1, 2)
+rdonly_pcv_pc_1_2 (const void*, char*); /* { dg-error "attribute .read_only\\(1, 2\\). second operand references non-integer argument type .char *." } */
+
+int RDONLY (2, 1)
+rdonly_pcd_pcv_2_1 (const double*, const void*); /* { dg-error "attribute .read_only\\(2, 1\\). second operand references non-integer argument type .const double *." } */
+
+int __attribute__ ((read_only))
+rdonly_pcv_pcv_all (const void*, const void*);
+
+int RDONLY (2, 2)
+rdonly_pi_pcv_2_2 (int*, const void*); /* { dg-error "second operand references non-integer argument type .const void *." } */
+
+int RDONLY (4)
+rdonly_i_i_i_4 (int, int, int); /* { dg-error "attribute .read_only\\(4\\). operand exceeds number of arguments 3" } */
+
+int RDONLY (1)
+rdonly_i_1 (int); /* { dg-error "attribute .read_only\\(1\\). references non-pointer argument type .int." } */
+
+int RDONLY (2)
+rdonly_i_pc (int, char*); /* { dg-warning "attribute .read_only\\(2\\). operand references non-.const. argument type .char \\\*." } */
+
+int RDONLY (-1)
+rdonly_pcv_m1 (const void*); /* { dg-error "attribute .read_only\\(-1\\). invalid operand" } */
+
+int RDONLY ("blah")
+rdonly_pcv_str (const void*); /* { dg-error "attribute .read_only\\(\"blah\"\\). invalid operand" } */
new file mode 100644
@@ -0,0 +1,192 @@
+/* Test to verify that attribute write_only is recognized by
+ -Wunused-but-set-variable.
+ { dg-do compile }
+ { dg-options "-Wunused-but-set-variable -Wunused-but-set-parameter" } */
+
+typedef __SIZE_TYPE__ size_t;
+
+void bcopy (const void*, void*, size_t);
+void bzero (void*, size_t);
+void* memcpy (void*, const void*, size_t);
+void* memset (void*, int, size_t);
+
+char* strcat (char*, const char*);
+char* strcpy (char*, const char*);
+char* strncat (char*, const char*, size_t);
+char* strncpy (char*, const char*, size_t);
+
+void* malloc (size_t);
+void free (void*);
+
+#define WRONLY(...) __attribute__ ((write_only (__VA_ARGS__)))
+
+void wronly_1 (void*) WRONLY (1);
+
+enum { N = sizeof (int) };
+
+void test_bcopy (int a, /* { dg-warning "-Wunused-but-set-parameter" } */
+ const void *p)
+{
+ int i; /* { dg-warning "-Wunused-but-set-variable" } */
+ char arr[8]; /* { dg-warning "-Wunused-but-set-variable" } */
+
+ bcopy (p, &a, N);
+ bcopy (p, &i, N);
+
+ bcopy (p, &arr, N);
+ bcopy (p, arr, N);
+ bcopy (p, arr + 0, N);
+ bcopy (p, arr + 1, N);
+ bcopy (p, &arr[0], N);
+ bcopy (p, &arr[1], N);
+}
+
+void test_bzero (int a) /* { dg-warning "-Wunused-but-set-parameter" } */
+{
+ int i; /* { dg-warning "-Wunused-but-set-variable" } */
+ char arr[8]; /* { dg-warning "-Wunused-but-set-variable" } */
+
+ bzero (&a, N);
+ bzero (&a + 0, N);
+ bzero (&i, N);
+ bzero (&i + 0, N);
+
+ bzero (arr, N);
+ bzero (arr + 0, N);
+ bzero (arr + 1, N);
+ bzero (&arr[0] + 1, N);
+ bzero (&arr[0], N);
+ bzero (&arr[1], N);
+}
+
+void test_memset (int a) /* { dg-warning "-Wunused-but-set-parameter" } */
+{
+ int i; /* { dg-warning "-Wunused-but-set-variable" } */
+ char arr[8]; /* { dg-warning "-Wunused-but-set-variable" } */
+
+ memset (&a, 0, N);
+ memset (&i, 0, N);
+ memset (&arr, 0, N);
+}
+
+void test_memcpy (int a, /* { dg-warning "-Wunused-but-set-parameter" } */
+ const void *p)
+{
+ int i; /* { dg-warning "-Wunused-but-set-variable" } */
+ char arr[8]; /* { dg-warning "-Wunused-but-set-variable" } */
+
+ memcpy (&a, p, N);
+ memcpy (&i, p, N);
+ memcpy (&arr, p, N);
+}
+
+void test_strcat (const char *s)
+{
+ char c = '\0';
+ char arr[8] = "";
+
+ strcat (&c, "");
+ strcat (arr, s);
+}
+
+void test_strcpy (const char *s)
+{
+ char c; /* { dg-warning "-Wunused-but-set-variable" } */
+ char d = '\0'; /* { dg-warning "-Wunused-but-set-variable" } */
+ char arr[8]; /* { dg-warning "-Wunused-but-set-variable" } */
+ char arr2[8] = ""; /* { dg-warning "-Wunused-but-set-variable" } */
+
+ strcpy (&c, "");
+ strcpy (&d, "");
+ strcpy (arr, s);
+ strcpy (arr2, s);
+}
+
+void test_strncpy (const char *s, unsigned n)
+{
+ char c; /* { dg-warning "-Wunused-but-set-variable" } */
+ char d = '\0'; /* { dg-warning "-Wunused-but-set-variable" } */
+ char arr[8]; /* { dg-warning "-Wunused-but-set-variable" } */
+ char arr2[8] = ""; /* { dg-warning "-Wunused-but-set-variable" } */
+
+ strncpy (&c, "", 1);
+ strncpy (&d, "", n);
+ strncpy (arr, s, n);
+ strncpy (arr2, s, n);
+}
+
+/* Verify that passing a local pointer initialized to the result of
+ malloc() (anything else should work as well) to free() on doesn't
+ trigger a warning because the pointer value is obviously read. */
+
+void test_malloc_and_free (size_t n)
+{
+ void *q = malloc (n);
+ free (q);
+}
+
+/* Verify that setting a local variable to a copy of an argument and
+ calling free() on the copy doesn't trigger a warning. */
+
+void test_free_argcpy (void *p)
+{
+ void *q = p;
+ free (q);
+}
+
+/* Similar to the free test above, verify that setting a local variable
+ to a copy of an argument and calling free() on the copy doesn't trigger
+ a warning. */
+
+void test_memset_argcpy (void *p, size_t n)
+{
+ void *q = p;
+ memset (q, 0, n);
+}
+
+void test_usrdef (int a) /* { dg-warning "-Wunused-but-set-parameter" } */
+{
+ int i; /* { dg-warning "-Wunused-but-set-variable" } */
+ char arr[8]; /* { dg-warning "-Wunused-but-set-variable" } */
+
+ wronly_1 (&a);
+ wronly_1 (&i);
+ wronly_1 (&arr);
+
+ wronly_1 (arr);
+ wronly_1 (arr + 1);
+ wronly_1 (&arr[1]);
+}
+
+/* Verify that using a pointer to a function works. */
+typedef struct S
+{
+ void (*pf)(void*) WRONLY (1);
+ struct {
+ struct {
+ void (*pf)(void*) WRONLY (1);
+ } s2;
+ } s1;
+} S;
+
+void test_funcptr (int a, /* { dg-warning "-Wunused-but-set-parameter" } */
+ S *ps)
+{
+ int i; /* { dg-warning "-Wunused-but-set-variable" } */
+ char arr[8]; /* { dg-warning "-Wunused-but-set-variable" } */
+
+ ps->pf (&a);
+ ps->pf (&i);
+ ps->pf (&arr);
+}
+
+void test_funcptr2 (int a, /* { dg-warning "-Wunused-but-set-parameter" } */
+ S *ps)
+{
+ int i; /* { dg-warning "-Wunused-but-set-variable" } */
+ char arr[8]; /* { dg-warning "-Wunused-but-set-variable" } */
+
+ ps->s1.s2.pf (&a);
+ ps->s1.s2.pf (&i);
+ ps->s1.s2.pf (&arr);
+}
new file mode 100644
@@ -0,0 +1,51 @@
+/* Test to verify the handling of attribute write_only syntax.
+ { dg-do compile }
+ { dg-options "-Wall -ftrack-macro-expansion=0" } */
+
+#define WRONLY(...) __attribute__ ((write_only (__VA_ARGS__)))
+
+void __attribute__ ((write_only))
+wronly_v_all (void); /* { dg-error "attribute .write_only. on a function with no pointer arguments" } */
+
+void __attribute__ ((write_only))
+wronly_i_all (int); /* { dg-error "attribute .write_only. on a function with no pointer arguments" } */
+
+void wronly_v_1 (void*) WRONLY (1);
+void wronly_iv_2 (int, void*) WRONLY (2);
+void wronly_iiv_3 (int, int, void*) WRONLY (3);
+
+void wronly_v_0p1 (void*) WRONLY (0 + 1);
+void wronly_v_2m1 (void*) WRONLY (2 - 1);
+
+void WRONLY (1, 1)
+wronly_v_1_1 (void*); /* { dg-error "attribute .write_only\\(1, 1\\). second operand references non-integer argument type .void *." } */
+
+void WRONLY (1, 2)
+wronly_vv_1_2 (void*, void*); /* { dg-error "attribute .write_only\\(1, 2\\). second operand references non-integer argument type .void *." } */
+
+void WRONLY (2, 1)
+wronly_vv_2_1 (void*, void*); /* { dg-error "attribute .write_only\\(2, 1\\). second operand references non-integer argument type .void *." } */
+
+void __attribute__ ((write_only))
+wronly_vv_all (void*, void*);
+
+void WRONLY (1, 1)
+wronly_vv_1_1 (void*, void*); /* { dg-error "attribute .write_only\\(1, 1\\). second operand references non-integer argument type .void *." } */
+
+void WRONLY (2, 2)
+wronly_vv_1_1 (void*, void*); /* { dg-error "second operand references non-integer argument type .void *." } */
+
+void WRONLY (4)
+wronly_4_exceed (int, int, int); /* { dg-error "attribute .write_only\\(4\\). operand exceeds number of arguments 3" } */
+
+void WRONLY (1)
+wronly_1_i (int); /* { dg-error "attribute .write_only\\(1\\). references non-pointer argument type .int." } */
+
+void WRONLY (2)
+wronly_2_cst (int, const char*); /* { dg-error "attribute .write_only\\(2\\). operand references .const.-qualified argument type .const char *." } */
+
+void WRONLY (-1)
+wronly_m1_i (void*); /* { dg-error "attribute .write_only\\(-1\\). invalid operand" } */
+
+void WRONLY ("blah")
+wronly_str_i (void*); /* { dg-error "attribute .write_only\\(\"blah\"\\). invalid operand" } */
@@ -10,8 +10,8 @@ void
foo (void *p, char *s)
{
__builtin_bzero (NULL, 0);
- __builtin_bcopy (NULL, p, 0);
- __builtin_bcopy (p, NULL, 0);
+ __builtin_bcopy (NULL, p, 0); /* { dg-warning "null" "null pointer check" } */
+ __builtin_bcopy (p, NULL, 0); /* { dg-warning "null" "null pointer check" } */
__builtin_bcmp (NULL, p, 0);
__builtin_bcmp (p, NULL, 0);
__builtin_index (NULL, 16); /* { dg-warning "null" "null pointer check" } */
@@ -1,6 +1,6 @@
-/* PR middle-end/40340 */
+/* PR middle-end/40340 - Fortification warning no longer emitted in inlines */
/* { dg-do compile } */
-/* { dg-options "-O2 -Wall -Wno-system-headers -fno-tree-dse" } */
+/* { dg-options "-O2 -Wall -Wno-system-headers -Wno-unused -fno-tree-dse" } */
#include "pr40340.h"
@@ -11,5 +11,7 @@ int main (void)
__builtin_sprintf (d, "%32s", "x"); /* { dg-warning "directive writing 32 bytes into a region of size 12" "-Wformat-overflow" } */
+ (void)&d; /* Suppress -Wunused-but-set-variable. */
+
return 0;
}
@@ -9,6 +9,8 @@ void f (const char *s)
char *p = __builtin_malloc (n);
__builtin_memcpy (p, s, n);
__builtin_free (p);
+
+ (void)&p; /* Suppress -Wunused-but-set-variable. */
}
void g (const char *s)
@@ -17,6 +19,8 @@ void g (const char *s)
char *p = __builtin_malloc (n);
__builtin_strcpy (p, s);
__builtin_free (p);
+
+ (void)&p; /* Suppress -Wunused-but-set-variable. */
}
/* { dg-final { scan-tree-dump-not "free" "optimized" } }
@@ -129,6 +129,7 @@ ELIM (5 ==, ga4, "123", "%.3s%.2s", ga4, ga4);
char a1[N1], a2[N2]; \
memcpy (a1, init1, sizeof (init1) - 1); \
memcpy (a2, init2, sizeof (init2) - 1); \
+ (void)a1; (void)a2; \
const int res = snprintf (0, 0, fmt, __VA_ARGS__); \
VERIFY_ELIM (expect res); \
} typedef void dummy_typedef
new file mode 100644
@@ -0,0 +1,207 @@
+/* Test to verify that -Wuninitialized detects uninitialized reads
+ by built-in functions.
+ { dg-do compile }
+ { dg-options "-O2 -Wuninitialized" } */
+
+typedef __SIZE_TYPE__ size_t;
+
+void* malloc (size_t);
+void free (void*);
+
+void sink (void*);
+
+/* Due to PR 81809 only the first uninitialized read in a function
+ is diagnosed. The subsequent ones are not because the variable's
+ address is considered to have escaped. */
+
+int test_memchr (int c)
+{
+ extern char* memchr (const void*, int, size_t);
+
+ char a3[3];
+ char *p;
+ enum { n = sizeof a3 };
+ p = memchr (a3, c, n); /* { dg-warning ".a3. is used uninitialized" "memchr" } */
+ return p != 0;
+}
+
+void test_memcpy (char *d)
+{
+ extern char* memcpy (void* restrict, const void* restrict, size_t);
+
+ char a5[5];
+ enum { n = sizeof a5 };
+ memcpy (d, a5, n); /* { dg-warning ".a5. is used uninitialized" "memcpy" } */
+}
+
+void test_memmove (void)
+{
+ extern char* memmove (void*, const void*, size_t);
+
+ char a7[7];
+ enum { n = sizeof a7 - 1 };
+ memmove (a7, a7 + 1, n); /* { dg-warning ".a7. is used uninitialized" "memmove" } */
+ sink (a7);
+}
+
+int test_strcat_1 (const char *s)
+{
+ extern char* strcat (char* restrict, const char* restrict);
+
+ char a7[7];
+ strcat (a7, s); /* { dg-warning ".a7. is used uninitialized" "strcat 1" } */
+ return a7[0];
+}
+
+void test_strcat_2 (char *d)
+{
+ extern char* strcat (char* restrict, const char* restrict);
+
+ char a7[7];
+ strcat (d, a7); /* { dg-warning ".a7. is used uninitialized" "strcat 2" } */
+}
+
+int test_strchr (const char *s, int c)
+{
+ extern char* strchr (const char*, int);
+
+ char a7[7];
+ s = strchr (a7, c); /* { dg-warning ".a7. is used uninitialized" "strchr" } */
+ return s != 0;
+}
+
+char* test_strdup (void)
+{
+ extern char* strdup (const char*);
+
+ char a7[7];
+ char *s;
+ s = strdup (a7); /* { dg-warning ".a7. is used uninitialized" "strdup" } */
+ return s;
+}
+
+int test_strstr_1 (const char *s)
+{
+ extern char* strstr (const char*, const char*);
+
+ char a7[7];
+ s = strstr (a7, s); /* { dg-warning ".a7. is used uninitialized" "strstr 1" } */
+ return s != 0;
+}
+
+int test_strstr_2 (const char *s)
+{
+ extern char* strstr (const char*, const char*);
+
+ char a7[7];
+ s = strstr (s, a7); /* { dg-warning ".a7. is used uninitialized" "strstr 2" } */
+ return s != 0;
+}
+
+void test_strcpy (char *d)
+{
+ extern char* strcpy (char* restrict, const char* restrict);
+
+ char a7[7];
+ strcpy (d, a7); /* { dg-warning ".a7. is used uninitialized" "strcpy" } */
+}
+
+void test_stpcpy (char *d)
+{
+ extern char* stpcpy (char* restrict, const char* restrict);
+
+ char a7[7];
+ stpcpy (d, a7); /* { dg-warning ".a7. is used uninitialized" "stpcpy" } */
+}
+
+void test_stpncpy (char *d)
+{
+ extern char* stpncpy (char* restrict, const char* restrict, size_t);
+
+ char a7[7];
+ stpncpy (d, a7, sizeof a7); /* { dg-warning ".a7. is used uninitialized" "stpncpy" } */
+}
+
+int test_strcmp_1 (const char *s)
+{
+ extern int strcmp (const char*, const char*);
+
+ char a7[7];
+ int i;
+ i = strcmp (a7, s); /* { dg-warning ".a7. is used uninitialized" "strcmp" } */
+ return i;
+}
+
+int test_strcmp_2 (const char *s)
+{
+ extern int strcmp (const char*, const char*);
+
+ char a7[7];
+ int i;
+ i = strcmp (s, a7); /* { dg-warning ".a7. is used uninitialized" "strcmp" } */
+ return i;
+}
+
+int test_strncat_1 (const char *s, size_t n)
+{
+ extern char* strncat (char* restrict, const char* restrict, size_t);
+
+ char a7[7];
+ s = strncat (a7, s, n); /* { dg-warning ".a7. is used uninitialized" "strncat 1" } */
+ return s != 0;
+}
+
+int test_strncat_2 (char *d, size_t n)
+{
+ extern char* strncat (char* restrict, const char* restrict, size_t);
+
+ char a7[7];
+ d = strncat (d, a7, n); /* { dg-warning ".a7. is used uninitialized" "strncat 2" } */
+ return d != 0;
+}
+
+int test_strncmp_1 (const char *s, size_t n)
+{
+ extern int strncmp (const char*, const char*, size_t);
+
+ char a7[7];
+ int i;
+ i = strncmp (a7, s, n); /* { dg-warning ".a7. is used uninitialized" "strncmp 1" } */
+ return i;
+}
+
+int test_strncmp_2 (const char *s, size_t n)
+{
+ extern int strncmp (const char*, const char*, size_t);
+
+ char a7[7];
+ int i;
+ i = strncmp (s, a7, n); /* { dg-warning ".a7. is used uninitialized" "strncmp 2" } */
+ return i;
+}
+
+void test_strncpy (char *d)
+{
+ extern char* strncpy (char* restrict, const char* restrict, size_t);
+
+ char a7[7];
+ strncpy (d, a7, sizeof a7); /* { dg-warning ".a7. is used uninitialized" "strncpy" } */
+}
+
+char* test_strndup (size_t n)
+{
+ extern char* strndup (const char*, size_t);
+
+ char a7[7];
+ char *s;
+ s = strndup (a7, n); /* { dg-warning ".a7. is used uninitialized" "strndup" } */
+ return s;
+}
+
+size_t test_strlen (void)
+{
+ extern size_t strlen (const char*);
+
+ char a7[7];
+ return strlen (a7); /* { dg-warning ".a7. is used uninitialized" "strlen" } */
+}
@@ -34,6 +34,7 @@ along with GCC; see the file COPYING3. If not see
#include "params.h"
#include "tree-cfg.h"
#include "cfghooks.h"
+#include "attribs.h"
/* This implements the pass that does predicate aware warning on uses of
possibly uninitialized variables. The pass first collects the set of
@@ -218,8 +219,172 @@ check_defs (ao_ref *ref, tree vdef, void *data_)
return true;
}
+/* Helper of warn_uninitialized_vars. Determine if Gimple statement
+ STMT reads or may read any uninitialized operand and if so, diagnose
+ the access. */
+
+static void
+maybe_warn_uninit_access (gimple *stmt, tree arg, ao_ref &ref,
+ bool always_executed,
+ unsigned *vdef_cnt, unsigned *oracle_cnt,
+ unsigned *limit, bool maybe_uninit)
+{
+ /* Do not warn if the base was marked so or this is a
+ hard register var. */
+ tree base = ao_ref_base (&ref);
+ if ((VAR_P (base)
+ && DECL_HARD_REGISTER (base))
+ || TREE_NO_WARNING (base))
+ return;
+
+ /* Do not warn if the access is fully outside of the
+ variable. */
+ if (DECL_P (base)
+ && known_ne (ref.size, -1)
+ && known_eq (ref.max_size, ref.size)
+ && (known_le (ref.offset + ref.size, 0)
+ || (known_ge (ref.offset, 0)
+ && DECL_SIZE (base)
+ && TREE_CODE (DECL_SIZE (base)) == INTEGER_CST
+ && tree_int_cst_le (DECL_SIZE (base),
+ build_int_cstu (sizetype, ref.offset)))))
+ return;
+
+ /* Limit the walking to a constant number of stmts after
+ we overcommit quadratic behavior for small functions
+ and O(n) behavior. */
+ if (*oracle_cnt > 128 * 128
+ && *oracle_cnt > *vdef_cnt * 2)
+ *limit = 32;
+ check_defs_data data;
+ bool fentry_reached = false;
+ data.found_may_defs = false;
+
+ int res = walk_aliased_vdefs (&ref, gimple_vuse (stmt),
+ check_defs, &data, NULL,
+ &fentry_reached, *limit);
+ if (res == -1)
+ {
+ *oracle_cnt += *limit;
+ return;
+ }
+ oracle_cnt += res;
+ if (data.found_may_defs)
+ return;
+ /* Do not warn if it can be initialized outside this function.
+ If we did not reach function entry then we found killing
+ clobbers on all paths to entry. */
+ if (fentry_reached
+ /* ??? We'd like to use ref_may_alias_global_p but that
+ excludes global readonly memory and thus we get bougs
+ warnings from p = cond ? "a" : "b" for example. */
+ && (!VAR_P (base)
+ || is_global_var (base)))
+ return;
+
+ /* We didn't find any may-defs so on all paths either
+ reached function entry or a killing clobber. */
+ location_t location
+ = linemap_resolve_location (line_table, gimple_location (stmt),
+ LRK_SPELLING_LOCATION, NULL);
+ if (always_executed)
+ {
+ if (warning_at (location, OPT_Wuninitialized,
+ "%qE is used uninitialized in this function",
+ arg))
+ /* ??? This is only effective for decls as in
+ gcc.dg/uninit-B-O0.c. Avoid doing this for
+ maybe-uninit uses as it may hide important
+ locations. */
+ TREE_NO_WARNING (arg) = 1;
+ }
+ else if (maybe_uninit)
+ warning_at (location, OPT_Wmaybe_uninitialized,
+ "%qE may be used uninitialized in this function",
+ arg);
+}
+
+/* Helper of warn_uninitialized_vars. Determine if Gimple statement
+ STMT reads any uninitialized operand. */
+
+static void
+maybe_warn_uninit_access (gimple *stmt, bool always_executed,
+ unsigned *vdef_cnt, unsigned *oracle_cnt,
+ unsigned *limit, bool maybe_uninit)
+{
+ if (gimple_assign_load_p (stmt)
+ && gimple_has_location (stmt))
+ {
+ bool has_bit_insert = false;
+
+ tree lhs = gimple_assign_lhs (stmt);
+
+ /* Do not warn if the access is then used for a BIT_INSERT_EXPR. */
+ if (TREE_CODE (lhs) == SSA_NAME)
+ {
+ use_operand_p luse_p;
+ imm_use_iterator liter;
+
+ FOR_EACH_IMM_USE_FAST (luse_p, liter, lhs)
+ {
+ gimple *use_stmt = USE_STMT (luse_p);
+ /* BIT_INSERT_EXPR first operand should not be considered
+ a use for the purpose of uninit warnings. */
+ if (gassign *ass = dyn_cast <gassign *> (use_stmt))
+ {
+ if (gimple_assign_rhs_code (ass) == BIT_INSERT_EXPR
+ && luse_p->use == gimple_assign_rhs1_ptr (ass))
+ {
+ has_bit_insert = true;
+ break;
+ }
+ }
+ }
+ }
+
+ if (!has_bit_insert)
+ {
+ tree arg = gimple_assign_rhs1 (stmt);
+ if (TREE_NO_WARNING (arg))
+ return;
+
+ ao_ref ref;
+ ao_ref_init (&ref, arg);
+
+ maybe_warn_uninit_access (stmt, arg, ref, always_executed, vdef_cnt,
+ oracle_cnt, limit, maybe_uninit);
+ return;
+ }
+ }
+
+ if (!is_gimple_call (stmt))
+ return;
+
+ /* Iterate over arguments to functions declared with attribute
+ read_only or read_write and diagnose those that are not
+ initialized. */
+ const attr_access::Kind access_kinds[] = {
+ attr_access::read_only, attr_access::read_write
+ };
+
+ for (unsigned i = 0; i != sizeof access_kinds / sizeof *access_kinds; ++i)
+ {
+ attr_access acc = { };
+ acc.kind = access_kinds[i];
+ while (get_attr_access_ptr_and_size (stmt, &acc))
+ {
+ ao_ref ref;
+ ao_ref_init_from_ptr_and_size (&ref, acc.ptr, acc.size);
+ tree arg = ref.base;
+
+ maybe_warn_uninit_access (stmt, arg, ref, always_executed, vdef_cnt,
+ oracle_cnt, limit, maybe_uninit);
+ }
+ }
+}
+
static unsigned int
-warn_uninitialized_vars (bool warn_possibly_uninitialized)
+warn_uninitialized_vars (bool maybe_uninit)
{
gimple_stmt_iterator gsi;
basic_block bb;
@@ -236,7 +401,6 @@ warn_uninitialized_vars (bool warn_possibly_uninitialized)
gimple *stmt = gsi_stmt (gsi);
use_operand_p use_p;
ssa_op_iter op_iter;
- tree use;
if (is_gimple_debug (stmt))
continue;
@@ -253,13 +417,13 @@ warn_uninitialized_vars (bool warn_possibly_uninitialized)
&& use_p->use == gimple_assign_rhs1_ptr (ass))
continue;
}
- use = USE_FROM_PTR (use_p);
+ tree use = USE_FROM_PTR (use_p);
if (always_executed)
warn_uninit (OPT_Wuninitialized, use, SSA_NAME_VAR (use),
SSA_NAME_VAR (use),
"%qD is used uninitialized in this function", stmt,
UNKNOWN_LOCATION);
- else if (warn_possibly_uninitialized)
+ else if (maybe_uninit)
warn_uninit (OPT_Wmaybe_uninitialized, use, SSA_NAME_VAR (use),
SSA_NAME_VAR (use),
"%qD may be used uninitialized in this function",
@@ -271,115 +435,9 @@ warn_uninitialized_vars (bool warn_possibly_uninitialized)
if (gimple_vdef (stmt))
vdef_cnt++;
- if (gimple_assign_load_p (stmt)
- && gimple_has_location (stmt))
- {
- tree rhs = gimple_assign_rhs1 (stmt);
- tree lhs = gimple_assign_lhs (stmt);
- bool has_bit_insert = false;
- use_operand_p luse_p;
- imm_use_iterator liter;
-
- if (TREE_NO_WARNING (rhs))
- continue;
-
- ao_ref ref;
- ao_ref_init (&ref, rhs);
-
- /* Do not warn if the base was marked so or this is a
- hard register var. */
- tree base = ao_ref_base (&ref);
- if ((VAR_P (base)
- && DECL_HARD_REGISTER (base))
- || TREE_NO_WARNING (base))
- continue;
-
- /* Do not warn if the access is fully outside of the
- variable. */
- poly_int64 decl_size;
- if (DECL_P (base)
- && known_size_p (ref.size)
- && ((known_eq (ref.max_size, ref.size)
- && known_le (ref.offset + ref.size, 0))
- || (known_ge (ref.offset, 0)
- && DECL_SIZE (base)
- && poly_int_tree_p (DECL_SIZE (base), &decl_size)
- && known_le (decl_size, ref.offset))))
- continue;
-
- /* Do not warn if the access is then used for a BIT_INSERT_EXPR. */
- if (TREE_CODE (lhs) == SSA_NAME)
- FOR_EACH_IMM_USE_FAST (luse_p, liter, lhs)
- {
- gimple *use_stmt = USE_STMT (luse_p);
- /* BIT_INSERT_EXPR first operand should not be considered
- a use for the purpose of uninit warnings. */
- if (gassign *ass = dyn_cast <gassign *> (use_stmt))
- {
- if (gimple_assign_rhs_code (ass) == BIT_INSERT_EXPR
- && luse_p->use == gimple_assign_rhs1_ptr (ass))
- {
- has_bit_insert = true;
- break;
- }
- }
- }
- if (has_bit_insert)
- continue;
-
- /* Limit the walking to a constant number of stmts after
- we overcommit quadratic behavior for small functions
- and O(n) behavior. */
- if (oracle_cnt > 128 * 128
- && oracle_cnt > vdef_cnt * 2)
- limit = 32;
- check_defs_data data;
- bool fentry_reached = false;
- data.found_may_defs = false;
- use = gimple_vuse (stmt);
- int res = walk_aliased_vdefs (&ref, use,
- check_defs, &data, NULL,
- &fentry_reached, limit);
- if (res == -1)
- {
- oracle_cnt += limit;
- continue;
- }
- oracle_cnt += res;
- if (data.found_may_defs)
- continue;
- /* Do not warn if it can be initialized outside this function.
- If we did not reach function entry then we found killing
- clobbers on all paths to entry. */
- if (fentry_reached
- /* ??? We'd like to use ref_may_alias_global_p but that
- excludes global readonly memory and thus we get bougs
- warnings from p = cond ? "a" : "b" for example. */
- && (!VAR_P (base)
- || is_global_var (base)))
- continue;
-
- /* We didn't find any may-defs so on all paths either
- reached function entry or a killing clobber. */
- location_t location
- = linemap_resolve_location (line_table, gimple_location (stmt),
- LRK_SPELLING_LOCATION, NULL);
- if (always_executed)
- {
- if (warning_at (location, OPT_Wuninitialized,
- "%qE is used uninitialized in this function",
- rhs))
- /* ??? This is only effective for decls as in
- gcc.dg/uninit-B-O0.c. Avoid doing this for
- maybe-uninit uses as it may hide important
- locations. */
- TREE_NO_WARNING (rhs) = 1;
- }
- else if (warn_possibly_uninitialized)
- warning_at (location, OPT_Wmaybe_uninitialized,
- "%qE may be used uninitialized in this function",
- rhs);
- }
+ maybe_warn_uninit_access (stmt, always_executed,
+ &vdef_cnt, &oracle_cnt, &limit,
+ maybe_uninit);
}
}
@@ -2666,7 +2724,7 @@ pass_late_warn_uninitialized::execute (function *fun)
/* Re-do the plain uninitialized variable check, as optimization may have
straightened control flow. Do this first so that we don't accidentally
get a "may be" warning when we'd have seen an "is" warning later. */
- warn_uninitialized_vars (/*warn_possibly_uninitialized=*/1);
+ warn_uninitialized_vars (/*maybe_uninit=*/1);
timevar_push (TV_TREE_UNINIT);
@@ -2736,7 +2794,7 @@ execute_early_warn_uninitialized (void)
optimization we need to warn here about "may be uninitialized". */
calculate_dominance_info (CDI_POST_DOMINATORS);
- warn_uninitialized_vars (/*warn_possibly_uninitialized=*/!optimize);
+ warn_uninitialized_vars (/*maybe_uninit=*/!optimize);
/* Post-dominator information cannot be reliably updated. Free it
after the use. */