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..46ab8f0 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(%u)%>", + 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(%u)%> 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(%u)%> references non-pointer argument " + "type %qT", name, operval, argtype); + *no_add_attrs = true; + break; + } + + if (TYPE_READONLY (TREE_TYPE (argtype))) + { + error ("attribute %<%E(%u)%> 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..266313c 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) diff --git a/gcc/c/c-parser.c b/gcc/c/c-parser.c index 2e01316..95baad6 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); } @@ -6973,7 +6977,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: { @@ -8415,8 +8420,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 +8665,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 (TREE_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 +8703,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 +8713,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 +8728,30 @@ 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; + tree attrs = (functype + ? lookup_attribute ("write_only", TYPE_ATTRIBUTES (functype)) + : NULL_TREE); + + tree argtypes = func ? TYPE_ARG_TYPES (TREE_TYPE (func)) : 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); + 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 +8777,28 @@ 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); + 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 +8817,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 +9907,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 +10758,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/testsuite/gcc.dg/attr-write-only-2.c b/gcc/testsuite/gcc.dg/attr-write-only-2.c new file mode 100644 index 0000000..a15bd55 --- /dev/null +++ b/gcc/testsuite/gcc.dg/attr-write-only-2.c @@ -0,0 +1,109 @@ +/* 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); + +#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); +} + +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 (&i, N); + bzero (&ar, 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); +} + +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); +} 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" } */