PR c/80806 - gcc does not warn if local array is memset only gcc/ChangeLog: PR c/80806 * builtin-attrs.def (DEF_ATTR_WRITE_ONLY): New. (ATTR_NOTHROW_WRONLY1_LEAF, ATTR_NOTHROW_WRONLY2_LEAF): New. (ATTR_RET1_NOTHROW_WRONLY1_LEAF): New. * builtins.def (bcopy, bzero, memcpy, memmove, memset): Update. (free, strcpy, strncpy, __memcpy_chk, __memmove_chk): Update. (__strcpy_chk, __strncpy_chk): Update. gcc/c/ChangeLog: PR c/80806 * c-parser.c (c_parser_attributes): Adjust. (c_parser_binary_expression): Same. (c_parser_unary_expression): Handle parser->no_set_read. (c_parser_sizeof_expression): Same. (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_oacc_wait_list): Adjust. * c-tree.h (parser_build_binary_op): Add argument. * c-typeck.c (default_conversion): Same. (parser_build_binary_op): Same. (build_binary_op): Same. gcc/c-family/ChangeLog: PR c/80806 * c-attribs.c (handle_write_only_attribute): New function. (c_common_attribute_table): Add attribute write_only. * c-common.c (nonnull_check_p): Update. (get_nonnull_operand): Rename... (get_attribute_operand): ...to this. * c-common.h (get_nonnull_operand): Rename... (get_attribute_operand): ...to this. (build_binary_op, default_conversion): Add argument. gcc/cp/ChangeLog: PR c/80806 * typeck.c (cp_default_conversion): Add argument. (build_binary_op): Same. gcc/testsuite/ChangeLog: PR c/80806 * gcc.dg/attr-write-only-2.c: New test. * gcc.dg/attr-write-only.c: New test. * c-c++-common/Wsizeof-pointer-memaccess2.c: Adjust. * gcc.dg/Wstrict-aliasing-bogus-vla-1.c: Same. * gcc.dg/attr-alloc_size.c: Same. * gcc.dg/pr40340-2.c: Same. * gcc.dg/pr78768.c: Same. * gcc.dg/pr79715.c: Same. diff --git a/gcc/builtin-attrs.def b/gcc/builtin-attrs.def index 38fb1bb8..24e6f6a 100644 --- a/gcc/builtin-attrs.def +++ b/gcc/builtin-attrs.def @@ -113,6 +113,7 @@ DEF_ATTR_IDENT (ATTR_TM_REGPARM, "*tm regparm") 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_WRITE_ONLY, "write_only") DEF_ATTR_TREE_LIST (ATTR_NOVOPS_LIST, ATTR_NOVOPS, ATTR_NULL, ATTR_NULL) @@ -196,6 +197,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) @@ -255,10 +257,21 @@ 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. */ 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_WRONLY2_LEAF, ATTR_WRITE_ONLY, \ + ATTR_LIST_2, ATTR_NOTHROW_LEAF_LIST) + +DEF_ATTR_TREE_LIST (ATTR_RET1_NOTHROW_WRONLY1_LEAF, ATTR_WRITE_ONLY, \ + ATTR_LIST_1, ATTR_RET1_NOTHROW_NONNULL_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, \ diff --git a/gcc/builtins.def b/gcc/builtins.def index 58d78db..59d1453 100644 --- a/gcc/builtins.def +++ b/gcc/builtins.def @@ -652,15 +652,15 @@ 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_BCOPY, "bcopy", BT_FN_VOID_CONST_PTR_PTR_SIZE, ATTR_NOTHROW_WRONLY2_LEAF) +DEF_EXT_LIB_BUILTIN (BUILT_IN_BZERO, "bzero", BT_FN_VOID_PTR_SIZE, ATTR_NOTHROW_WRONLY1_LEAF) 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_CHKP (BUILT_IN_MEMCPY, "memcpy", BT_FN_PTR_PTR_CONST_PTR_SIZE, ATTR_RET1_NOTHROW_NONNULL_LEAF) -DEF_LIB_BUILTIN_CHKP (BUILT_IN_MEMMOVE, "memmove", BT_FN_PTR_PTR_CONST_PTR_SIZE, ATTR_RET1_NOTHROW_NONNULL_LEAF) +DEF_LIB_BUILTIN_CHKP (BUILT_IN_MEMCPY, "memcpy", BT_FN_PTR_PTR_CONST_PTR_SIZE, ATTR_RET1_NOTHROW_WRONLY1_LEAF) +DEF_LIB_BUILTIN_CHKP (BUILT_IN_MEMMOVE, "memmove", BT_FN_PTR_PTR_CONST_PTR_SIZE, ATTR_RET1_NOTHROW_WRONLY1_LEAF) DEF_EXT_LIB_BUILTIN_CHKP (BUILT_IN_MEMPCPY, "mempcpy", BT_FN_PTR_PTR_CONST_PTR_SIZE, ATTR_RETNONNULL_NOTHROW_LEAF) -DEF_LIB_BUILTIN_CHKP (BUILT_IN_MEMSET, "memset", BT_FN_PTR_PTR_INT_SIZE, ATTR_RET1_NOTHROW_NONNULL_LEAF) +DEF_LIB_BUILTIN_CHKP (BUILT_IN_MEMSET, "memset", BT_FN_PTR_PTR_INT_SIZE, ATTR_RET1_NOTHROW_WRONLY1_LEAF) DEF_EXT_LIB_BUILTIN (BUILT_IN_RINDEX, "rindex", BT_FN_STRING_CONST_STRING_INT, ATTR_PURE_NOTHROW_NONNULL_LEAF) DEF_EXT_LIB_BUILTIN_CHKP (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) @@ -668,7 +668,7 @@ DEF_EXT_LIB_BUILTIN (BUILT_IN_STRCASECMP, "strcasecmp", BT_FN_INT_CONST_STRIN DEF_LIB_BUILTIN_CHKP (BUILT_IN_STRCAT, "strcat", BT_FN_STRING_STRING_CONST_STRING, ATTR_RET1_NOTHROW_NONNULL_LEAF) DEF_LIB_BUILTIN_CHKP (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_CHKP (BUILT_IN_STRCPY, "strcpy", BT_FN_STRING_STRING_CONST_STRING, ATTR_RET1_NOTHROW_NONNULL_LEAF) +DEF_LIB_BUILTIN_CHKP (BUILT_IN_STRCPY, "strcpy", BT_FN_STRING_STRING_CONST_STRING, ATTR_RET1_NOTHROW_WRONLY1_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_NOTHROW_NONNULL_LEAF) DEF_EXT_LIB_BUILTIN (BUILT_IN_STRNDUP, "strndup", BT_FN_STRING_CONST_STRING_SIZE, ATTR_MALLOC_NOTHROW_NONNULL_LEAF) @@ -676,7 +676,7 @@ DEF_LIB_BUILTIN_CHKP (BUILT_IN_STRLEN, "strlen", BT_FN_SIZE_CONST_STRING, ATTR 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_LIB_BUILTIN (BUILT_IN_STRNCPY, "strncpy", BT_FN_STRING_STRING_CONST_STRING_SIZE, ATTR_RET1_NOTHROW_WRONLY1_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) @@ -825,7 +825,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) @@ -916,16 +916,16 @@ DEF_BUILTIN_STUB (BUILT_IN_MEMCMP_EQ, "__builtin_memcmp_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_CHKP (BUILT_IN_MEMCPY_CHK, "__memcpy_chk", BT_FN_PTR_PTR_CONST_PTR_SIZE_SIZE, ATTR_RET1_NOTHROW_NONNULL_LEAF) -DEF_EXT_LIB_BUILTIN_CHKP (BUILT_IN_MEMMOVE_CHK, "__memmove_chk", BT_FN_PTR_PTR_CONST_PTR_SIZE_SIZE, ATTR_RET1_NOTHROW_NONNULL_LEAF) +DEF_EXT_LIB_BUILTIN_CHKP (BUILT_IN_MEMCPY_CHK, "__memcpy_chk", BT_FN_PTR_PTR_CONST_PTR_SIZE_SIZE, ATTR_RET1_NOTHROW_WRONLY1_LEAF) +DEF_EXT_LIB_BUILTIN_CHKP (BUILT_IN_MEMMOVE_CHK, "__memmove_chk", BT_FN_PTR_PTR_CONST_PTR_SIZE_SIZE, ATTR_RET1_NOTHROW_WRONLY1_LEAF) DEF_EXT_LIB_BUILTIN_CHKP (BUILT_IN_MEMPCPY_CHK, "__mempcpy_chk", BT_FN_PTR_PTR_CONST_PTR_SIZE_SIZE, ATTR_RETNONNULL_NOTHROW_LEAF) -DEF_EXT_LIB_BUILTIN_CHKP (BUILT_IN_MEMSET_CHK, "__memset_chk", BT_FN_PTR_PTR_INT_SIZE_SIZE, ATTR_RET1_NOTHROW_NONNULL_LEAF) +DEF_EXT_LIB_BUILTIN_CHKP (BUILT_IN_MEMSET_CHK, "__memset_chk", BT_FN_PTR_PTR_INT_SIZE_SIZE, ATTR_RET1_NOTHROW_WRONLY1_LEAF) DEF_EXT_LIB_BUILTIN_CHKP (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_CHKP (BUILT_IN_STRCAT_CHK, "__strcat_chk", BT_FN_STRING_STRING_CONST_STRING_SIZE, ATTR_RET1_NOTHROW_NONNULL_LEAF) -DEF_EXT_LIB_BUILTIN_CHKP (BUILT_IN_STRCPY_CHK, "__strcpy_chk", BT_FN_STRING_STRING_CONST_STRING_SIZE, ATTR_RET1_NOTHROW_NONNULL_LEAF) +DEF_EXT_LIB_BUILTIN_CHKP (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_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) diff --git a/gcc/c-family/c-attribs.c b/gcc/c-family/c-attribs.c index 695c58c..ad3d18f 100644 --- a/gcc/c-family/c-attribs.c +++ b/gcc/c-family/c-attribs.c @@ -44,6 +44,7 @@ along with GCC; see the file COPYING3. If not see #include "tree-iterator.h" #include "opts.h" #include "gimplify.h" +#include "bitmap.h" static tree handle_packed_attribute (tree *, tree, tree, int, bool *); static tree handle_nocommon_attribute (tree *, tree, tree, int, bool *); @@ -116,6 +117,7 @@ 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_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 *); @@ -345,6 +347,8 @@ const struct attribute_spec c_common_attribute_table[] = handle_bnd_instrument, false }, { "fallthrough", 0, 0, false, false, false, handle_fallthrough_attribute, false }, + { "write_only", 0, -1, false, true, true, + handle_write_only_attribute, false }, { NULL, 0, 0, false, false, false, NULL, false } }; @@ -2815,7 +2819,7 @@ handle_nonnull_attribute (tree *node, tree ARG_UNUSED (name), && TREE_CODE (arg) != FUNCTION_DECL) TREE_VALUE (args) = arg = default_conversion (arg); - if (!get_nonnull_operand (arg, &arg_num)) + if (!get_attribute_operand (arg, &arg_num)) { error ("nonnull argument has invalid operand number (argument %lu)", (unsigned long) attr_arg_num); @@ -2860,6 +2864,121 @@ handle_nonnull_attribute (tree *node, tree ARG_UNUSED (name), return NULL_TREE; } + +/* Handle the "write_only" attribute. */ + +static tree +handle_write_only_attribute (tree *node, tree name, + tree operands, int ARG_UNUSED (flags), + bool *no_add_attrs) +{ + tree type = *node; + + /* If no operands are specified, all pointer arguments should be + 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) + && (!TYPE_ATTRIBUTES (type) + || !lookup_attribute ("type generic", TYPE_ATTRIBUTES (type)))) + { + error ("attribute %qE without arguments on a non-prototype", name); + *no_add_attrs = true; + return NULL_TREE; + } + + /* True if operands were specified. */ + bool has_ops = operands; + + /* A bitmap of operand values already handled. Used to warn about + duplicates. */ + bitmap_head operset; + bitmap_initialize (&operset, 0); + + /* Attribute operands have been specified. Verify that each operand + value, OPERVAL, references a non-const pointer argument to the + function. */ + for (unsigned operno = 1; ; operno++, + operands = operands ? TREE_CHAIN (operands) : NULL_TREE) + { + /* The argument number the attribute operand corresponds to. */ + unsigned HOST_WIDE_INT operval; + + if (operands) + { + tree oper = TREE_VALUE (operands); + if (oper && TREE_CODE (oper) != IDENTIFIER_NODE + && TREE_CODE (oper) != FUNCTION_DECL) + TREE_VALUE (operands) = oper = default_conversion (oper); + + if (!get_attribute_operand (oper, &operval)) + { + error ("attribute %<%E(%E)%> invalid operand", name, oper); + *no_add_attrs = true; + break; + } + } + else if (!has_ops) + operval = operno; + else + break; + + if (bitmap_bit_p (&operset, operval)) + warning (OPT_Wattributes, "duplicate attribute %<%E(%wu)%>", + name, operval); + + bitmap_set_bit (&operset, operval); + + if (!prototype_p (type)) + continue; + + tree argtype; + + function_args_iterator iter; + function_args_iter_init (&iter, type); + + for (unsigned idx = 1; ; ++idx, function_args_iter_next (&iter)) + { + argtype = function_args_iter_cond (&iter); + if (!argtype || VOID_TYPE_P (argtype)) + { + if (has_ops) + { + error ("attribute %<%E(%wu)%> exceeds number of arguments %u", + name, operval, idx - 1); + *no_add_attrs = true; + } + bitmap_clear (&operset); + return NULL_TREE; + } + + if (idx == operval) + break; + } + + if (TREE_CODE (argtype) != POINTER_TYPE) + { + error ("attribute %<%E(%wu)%> references non-pointer argument " + "type %qT", name, operval, argtype); + *no_add_attrs = true; + break; + } + + if (TYPE_READONLY (TREE_TYPE (argtype))) + { + error ("attribute %<%E(%wu)%> references %qs-qualified argument " + "type %qT", name, operval, "const", argtype); + *no_add_attrs = true; + break; + } + } + + bitmap_clear (&operset); + return NULL_TREE; +} + /* Handle a "nothrow" attribute; arguments as in struct attribute_spec.handler. */ diff --git a/gcc/c-family/c-common.c b/gcc/c-family/c-common.c index 5f4488a..8f95bae 100644 --- a/gcc/c-family/c-common.c +++ b/gcc/c-family/c-common.c @@ -5277,7 +5277,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); @@ -5314,11 +5314,11 @@ check_nonnull_arg (void *ctx, tree param, unsigned HOST_WIDE_INT param_num) } } -/* Helper for nonnull attribute handling; fetch the operand number - from the attribute argument list. */ +/* Helper for attribute handling; fetch the operand number from + the attribute argument list. */ bool -get_nonnull_operand (tree arg_num_expr, unsigned HOST_WIDE_INT *valp) +get_attribute_operand (tree arg_num_expr, unsigned HOST_WIDE_INT *valp) { /* Verify the arg number is a small constant. */ if (tree_fits_uhwi_p (arg_num_expr)) diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h index 502dc2f..383604f 100644 --- a/gcc/c-family/c-common.h +++ b/gcc/c-family/c-common.h @@ -864,7 +864,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) @@ -960,13 +960,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, int); +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. */ diff --git a/gcc/c/c-parser.c b/gcc/c/c-parser.c index 2e01316..bb5be5f 100644 --- a/gcc/c/c-parser.c +++ b/gcc/c/c-parser.c @@ -178,6 +178,9 @@ 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; + /* False if taking the address of a DECL should not set its DECL_READ_P + flag. */ + BOOL_BITFIELD no_set_read : 1; /* Objective-C specific parser/lexer information. */ @@ -1253,7 +1256,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 *c_parser_expr_list (c_parser *, bool, bool, - vec **, location_t *, + vec **, tree, + location_t *, tree *, vec *, unsigned int * = NULL); static void c_parser_oacc_declare (c_parser *); @@ -4215,8 +4219,8 @@ c_parser_attributes (c_parser *parser) { tree tree_list; c_parser_consume_token (parser); - expr_list = c_parser_expr_list (parser, false, true, - NULL, NULL, NULL, NULL); + expr_list = c_parser_expr_list (parser, false, true, NULL, + NULL_TREE, 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); @@ -4228,8 +4232,8 @@ c_parser_attributes (c_parser *parser) attr_args = NULL_TREE; else { - expr_list = c_parser_expr_list (parser, false, true, - NULL, NULL, NULL, NULL); + expr_list = c_parser_expr_list (parser, false, true, NULL, + NULL_TREE, NULL, NULL, NULL); attr_args = build_tree_list_vec (expr_list); release_tree_vector (expr_list); } @@ -6659,6 +6663,9 @@ c_parser_binary_expression (c_parser *parser, struct c_expr *after, location_t loc; } 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 \ @@ -6678,10 +6685,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) \ @@ -6699,7 +6706,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 ()); @@ -6803,7 +6811,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 @@ -6814,7 +6822,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 @@ -6973,7 +6981,8 @@ 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); + if (!parser->no_set_read) + mark_exp_read (op.value); return parser_build_unary_op (op_loc, ADDR_EXPR, op); case CPP_MULT: { @@ -7130,7 +7139,7 @@ c_parser_sizeof_expression (c_parser *parser) sizeof_expr: c_inhibit_evaluation_warnings--; in_sizeof--; - mark_exp_read (expr.value); + // 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, "% applied to a bit-field"); @@ -8415,8 +8424,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; @@ -8658,6 +8669,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 @@ -8670,7 +8707,7 @@ c_parser_check_literal_zero (c_parser *parser, unsigned *literal_zero_mask, static vec * c_parser_expr_list (c_parser *parser, bool convert_p, bool fold_p, - vec **p_orig_types, + vec **p_orig_types, tree func, location_t *sizeof_arg_loc, tree *sizeof_arg, vec *locations, unsigned int *literal_zero_mask) @@ -8680,6 +8717,8 @@ c_parser_expr_list (c_parser *parser, bool convert_p, bool fold_p, struct c_expr expr; location_t loc = c_parser_peek_token (parser)->location; location_t cur_sizeof_arg_loc = UNKNOWN_LOCATION; + + /* Zero-based expression index within the list. */ unsigned int idx = 0; ret = make_tree_vector (); @@ -8693,9 +8732,40 @@ c_parser_expr_list (c_parser *parser, bool convert_p, bool fold_p, cur_sizeof_arg_loc = c_parser_peek_2nd_token (parser)->location; 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 + ? lookup_attribute ("write_only", TYPE_ATTRIBUTES (functype)) + : NULL_TREE); + + tree argtypes = attrs ? TYPE_ARG_TYPES (functype) : NULL_TREE; + + /* Determine if the expression in the list is declared write-only + and only mark it DECL_READ_P() if it isn't. */ + bool wronly + = argtypes && 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 (loc, 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 (loc, expr, true, !wronly); + } + if (fold_p) expr.value = c_fully_fold (expr.value, false, NULL); ret->quick_push (expr.value); @@ -8721,9 +8791,32 @@ c_parser_expr_list (c_parser *parser, bool convert_p, bool fold_p, cur_sizeof_arg_loc = UNKNOWN_LOCATION; 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 expression in the list is declared + write-only and only mark it DECL_READ_P() if it isn't. */ + if (argtypes && attrs) + { + argtypes = TREE_CHAIN (argtypes); + wronly = 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; + expr = c_parser_expr_no_commas (parser, NULL); + parser->no_set_read = save_set_read; + } + if (convert_p) - expr = convert_lvalue_to_rvalue (loc, expr, true, true); + { + wronly &= (!DECL_P (expr.value) + || !POINTER_TYPE_P (TREE_TYPE (expr.value))); + expr = convert_lvalue_to_rvalue (loc, expr, true, !wronly); + } + if (fold_p) expr.value = c_fully_fold (expr.value, false, NULL); vec_safe_push (ret, expr.value); @@ -8742,6 +8835,7 @@ c_parser_expr_list (c_parser *parser, bool convert_p, bool fold_p, } if (orig_types) *p_orig_types = orig_types; + return ret; } @@ -9831,8 +9925,8 @@ static tree c_parser_objc_keywordexpr (c_parser *parser) { tree ret; - vec *expr_list = c_parser_expr_list (parser, true, true, - NULL, NULL, NULL, NULL); + vec *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 @@ -10682,7 +10776,8 @@ c_parser_oacc_wait_list (c_parser *parser, location_t clause_loc, tree list) if (!c_parser_require (parser, CPP_OPEN_PAREN, "expected %<(%>")) return list; - args = c_parser_expr_list (parser, false, true, NULL, NULL, NULL, NULL); + args = c_parser_expr_list (parser, false, true, NULL, NULL_TREE, NULL, + NULL, NULL); if (args->length () == 0) { diff --git a/gcc/c/c-tree.h b/gcc/c/c-tree.h index 17a8897..88f1449 100644 --- a/gcc/c/c-tree.h +++ b/gcc/c/c-tree.h @@ -641,7 +641,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, tree, tree); extern tree build_compound_expr (location_t, tree, tree); diff --git a/gcc/c/c-typeck.c b/gcc/c/c-typeck.c index 25b7dd6..c129cad 100644 --- a/gcc/c/c-typeck.c +++ b/gcc/c/c-typeck.c @@ -2131,14 +2131,15 @@ 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) + mark_exp_read (exp); /* Functions and arrays have been converted during parsing. */ gcc_assert (code != FUNCTION_TYPE); @@ -3622,7 +3623,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; @@ -3636,7 +3638,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, 1); + arg1.value, arg2.value, true, read_p); result.original_code = code; result.original_type = NULL; @@ -10730,7 +10732,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, int convert_p) + tree orig_op0, tree orig_op1, bool convert_p, bool read_p) { tree type0, type1, orig_type0, orig_type1; tree eptype; @@ -10835,8 +10837,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); } /* When Cilk Plus is enabled and there are array notations inside op0, then diff --git a/gcc/cp/typeck.c b/gcc/cp/typeck.c index 13d90a6..5241b8d 100644 --- a/gcc/cp/typeck.c +++ b/gcc/cp/typeck.c @@ -2095,7 +2095,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); } @@ -4056,7 +4056,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, - int /*convert_p*/) + bool /*convert_p*/, bool /*read_p*/) { return cp_build_binary_op (location, code, op0, op1, tf_warning_or_error); } diff --git a/gcc/testsuite/c-c++-common/Wsizeof-pointer-memaccess2.c b/gcc/testsuite/c-c++-common/Wsizeof-pointer-memaccess2.c index 895a50e..1a15b00 100644 --- a/gcc/testsuite/c-c++-common/Wsizeof-pointer-memaccess2.c +++ b/gcc/testsuite/c-c++-common/Wsizeof-pointer-memaccess2.c @@ -481,5 +481,6 @@ f4 (char *x, char **y, int z, char w[64]) stpncpy (x, s3, sizeof (s3)); } +/* { dg-prune-output "-Wunused-but-set-variable" } */ /* { dg-prune-output "\[\n\r\]*writing\[\n\r\]*" } */ /* { dg-prune-output "\[\n\r\]*reading\[\n\r\]*" } */ diff --git a/gcc/testsuite/gcc.dg/Wstrict-aliasing-bogus-vla-1.c b/gcc/testsuite/gcc.dg/Wstrict-aliasing-bogus-vla-1.c index 87f5ef9..f7f7071 100644 --- a/gcc/testsuite/gcc.dg/Wstrict-aliasing-bogus-vla-1.c +++ b/gcc/testsuite/gcc.dg/Wstrict-aliasing-bogus-vla-1.c @@ -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" } */ diff --git a/gcc/testsuite/gcc.dg/attr-alloc_size.c b/gcc/testsuite/gcc.dg/attr-alloc_size.c index f50ba7c..2422acf 100644 --- a/gcc/testsuite/gcc.dg/attr-alloc_size.c +++ b/gcc/testsuite/gcc.dg/attr-alloc_size.c @@ -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 */ +} diff --git a/gcc/testsuite/gcc.dg/attr-write-only-2.c b/gcc/testsuite/gcc.dg/attr-write-only-2.c new file mode 100644 index 0000000..8176ac3 --- /dev/null +++ b/gcc/testsuite/gcc.dg/attr-write-only-2.c @@ -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 ar[8]; /* { dg-warning "-Wunused-but-set-variable" } */ + + bcopy (p, &a, N); + bcopy (p, &i, N); + bcopy (p, &ar, N); + + bcopy (p, ar, N); + bcopy (p, &ar, N); + bcopy (p, &ar + 1, N); + bcopy (p, &ar[0], N); + bcopy (p, &ar[1], N); +} + +void test_bzero (int a) /* { dg-warning "-Wunused-but-set-parameter" } */ +{ + int i; /* { dg-warning "-Wunused-but-set-variable" } */ + char ar[8]; /* { dg-warning "-Wunused-but-set-variable" } */ + + bzero (&a, N); + bzero (&a + 0, N); + bzero (&i, N); + bzero (&i + 0, N); + + bzero (ar, N); + bzero (ar + 1, N); + bzero (&ar, N); + bzero (&ar + 1, N); + bzero (&ar[0], N); + bzero (&ar[1], N); +} + +void test_memset (int a) /* { dg-warning "-Wunused-but-set-parameter" } */ +{ + int i; /* { dg-warning "-Wunused-but-set-variable" } */ + char ar[8]; /* { dg-warning "-Wunused-but-set-variable" } */ + + memset (&a, 0, N); + memset (&i, 0, N); + memset (&ar, 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 ar[8]; /* { dg-warning "-Wunused-but-set-variable" } */ + + memcpy (&a, p, N); + memcpy (&i, p, N); + memcpy (&ar, p, N); +} + +void test_strcat (const char *s) +{ + char c = '\0'; + char ar[8] = ""; + + strcat (&c, ""); + strcat (ar, 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 ar[8]; /* { dg-warning "-Wunused-but-set-variable" } */ + char ar2[8] = ""; /* { dg-warning "-Wunused-but-set-variable" } */ + + strcpy (&c, ""); + strcpy (&d, ""); + strcpy (ar, s); + strcpy (ar2, 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 ar[8]; /* { dg-warning "-Wunused-but-set-variable" } */ + char ar2[8] = ""; /* { dg-warning "-Wunused-but-set-variable" } */ + + strncpy (&c, "", 1); + strncpy (&d, "", n); + strncpy (ar, s, n); + strncpy (ar2, 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 ar[8]; /* { dg-warning "-Wunused-but-set-variable" } */ + + wronly_1 (&a); + wronly_1 (&i); + wronly_1 (&ar); + + wronly_1 (ar); + wronly_1 (ar + 1); + wronly_1 (&ar[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 ar[8]; /* { dg-warning "-Wunused-but-set-variable" } */ + + ps->pf (&a); + ps->pf (&i); + ps->pf (&ar); +} + +void test_funcptr2 (int a, /* { dg-warning "-Wunused-but-set-parameter" } */ + S *ps) +{ + int i; /* { dg-warning "-Wunused-but-set-variable" } */ + char ar[8]; /* { dg-warning "-Wunused-but-set-variable" } */ + + ps->s1.s2.pf (&a); + ps->s1.s2.pf (&i); + ps->s1.s2.pf (&ar); +} diff --git a/gcc/testsuite/gcc.dg/attr-write-only.c b/gcc/testsuite/gcc.dg/attr-write-only.c new file mode 100644 index 0000000..e238c81 --- /dev/null +++ b/gcc/testsuite/gcc.dg/attr-write-only.c @@ -0,0 +1,34 @@ +/* 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 wronly_v_all (void) __attribute__ ((write_only)); /* { dg-error "attribute .write_only\\(1\\). exceeds number of arguments 0" } */ + +void wronly_i_all (int) __attribute__ ((write_only)); /* { dg-error "attribute .write_only\\(1\\). references non-pointer argument type .int." } */ + +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_v_1_1 (void*) WRONLY (1, 1); /* { dg-warning "duplicate attribute .write_only\\(1\\)." } */ +void wronly_vv_1_2 (void*, void*) WRONLY (1, 2); +void wronly_vv_2_1 (void*, void*) WRONLY (2, 1); +void wronly_vv_all (void*, void*) __attribute__ ((write_only)); + +void wronly_vv_1_1 (void*, void*) WRONLY (1, 1); /* { dg-warning "duplicate attribute .write_only\\(1\\)." } */ +void wronly_vv_1_1 (void*, void*) WRONLY (2, 2); /* { dg-warning "duplicate attribute .write_only\\(2\\)." } */ + +void wronly_4_exceed (int, int, int) WRONLY (4); /* { dg-error "attribute .write_only\\(4\\). exceeds number of arguments 3" } */ + +void wronly_1_i (int) WRONLY (1); /* { dg-error "attribute .write_only\\(1\\). references non-pointer argument type .int." } */ + +void wronly_2_cst (int, const char*) WRONLY (2); /* { dg-error "attribute .write_only\\(2\\). references .const.-qualified argument type .const char *." } */ + +void wronly_m1_i (void*) WRONLY (-1); /* { dg-error "attribute .write_only\\(-1\\). invalid operand" } */ + +void wronly_str_i (void*) WRONLY ("blah"); /* { dg-error "attribute .write_only\\(\"blah\"\\). invalid operand" } */ diff --git a/gcc/testsuite/gcc.dg/pr40340-2.c b/gcc/testsuite/gcc.dg/pr40340-2.c index 1dc21d1..eb933c1 100644 --- a/gcc/testsuite/gcc.dg/pr40340-2.c +++ b/gcc/testsuite/gcc.dg/pr40340-2.c @@ -12,5 +12,6 @@ main (void) return 0; } +/* { dg-prune-output "-Wunused-but-set-variable" } */ /* { dg-warning "writing" "" { target *-*-* } 10 } */ /* { dg-message "file included" "In file included" { target *-*-* } 0 } */ diff --git a/gcc/testsuite/gcc.dg/pr78768.c b/gcc/testsuite/gcc.dg/pr78768.c index b6cda47..5e747c9 100644 --- a/gcc/testsuite/gcc.dg/pr78768.c +++ b/gcc/testsuite/gcc.dg/pr78768.c @@ -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" { xfail *-*-* } } */ + (void)&d; /* Suppress -Wunused-but-set-variable. */ + return 0; } diff --git a/gcc/testsuite/gcc.dg/pr79715.c b/gcc/testsuite/gcc.dg/pr79715.c index 0f0f90f..3f7b38d 100644 --- a/gcc/testsuite/gcc.dg/pr79715.c +++ b/gcc/testsuite/gcc.dg/pr79715.c @@ -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" } }