public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
From: Martin Sebor <msebor@gmail.com>
To: Prathamesh Kulkarni <prathamesh.kulkarni@linaro.org>,
	gcc Patches <gcc-patches@gcc.gnu.org>,
	Richard Biener <rguenther@suse.de>
Subject: Re: PR80806
Date: Wed, 24 May 2017 05:42:00 -0000	[thread overview]
Message-ID: <c3cbf742-fc78-9aac-59de-d755664700ba@gmail.com> (raw)
In-Reply-To: <2dae09d9-8b97-d178-6af0-a5c56a66b9a8@gmail.com>

[-- Attachment #1: Type: text/plain, Size: 5438 bytes --]

I attach an updated patch that actually bootstraps and (with
one minor exception) passes regression tests.  It's a C front
end-only proof of concept for now.  The complete patch will
include attributes read-only and read-write and support C++
of course.  I think the optimization bits can be done in
a subsequent pass.

Martin

On 05/23/2017 09:58 AM, Martin Sebor wrote:
> On 05/18/2017 12:55 PM, Prathamesh Kulkarni wrote:
>> Hi,
>> The attached patch tries to fix PR80806 by warning when a variable is
>> set using memset (and friends) but not used. I chose to warn in dse
>> pass since dse would detect if the variable passed as 1st argument is
>> a dead store. Does this approach look OK ?
>
> Detecting -Wunused-but-set-variable in the optimizer means that
> the warning will not be issued without optimization.  It also
> means that the warning will trigger in cases where the variable
> is used conditionally and the condition is subject to constant
> propagation.  For instance:
>
>   void sink (void*);
>
>   void test (int i)
>   {
>       char buf[10];   // -Wunused-but-set-variable
>       memset (buf, 0, sizeof(buf));
>
>       if (i)
>         sink (buf);
>   }
>
>   void f (void)
>   {
>       test (0);
>   }
>
> I suspect this would be considered a false positive by most users.
> In my view, it would be more in line with the design of the warning
> to enhance the front end to detect this case, and it would avoid
> these issues.
>
> I have a patch that does that.  Rather than checking the finite
> set of known built-in functions like memset that are known not
> to read the referenced object, I took the approach of adding
> a new  function attribute (I call it write-only) and avoiding
> setting the DECL_READ_P flag for DECLs that are passed to
> function arguments decorated with the attribute.  That makes
> it possible to issue the warning even if the variable is passed
> to ordinary (non-built-in) functions like getline(), and should
> open up optimization opportunities beyond built-ins.  The only
> wrinkle is that the front end sets DECL_READ_P even for uses that
> aren't reads such as a sizeof expression, so while an otherwise
> unused buf is diagnosed given a call to memset(buf, 0, 10), it
> isn't diagnosed if a call is made to memset(buf, 0, sizeof buf).
> I am yet to see what impact not setting DECL_READ_P would have
> when the decl is used without being evaluated.  (In any event,
> setting DECL_READ_P on a use that doesn't involve reading the
> DECL doesn't seem right.)
>
> I attach what I have so far in case you would like to check it
> out.  I think you have more experience with DSE than me so I'd
> be interested in your thoughts on making use of the attribute
> for optimization.  (Another couple attributes I'm considering
> to complement write-only is read-only and read-write, also
> with the hope of improving both warnings and code generation.
> Ideas on those would be welcome as well.)
>
>>
>> There were following fallouts observed during bootstrap build:
>>
>> * double-int.c (div_and_round_double):
>> Warning emitted 'den' set but not used for following call to memset:
>> memset (den, 0, sizeof den);
>>
>> I assume the warning is correct since there's immediately call to:
>> encode (den, lden, hden);
>>
>> and encode overwrites all the contents of den.
>> Should the above call to memset be removed from the source ?
>
> IIUC, this seems to be a more involved example of the simple one
> above.  AFAICS, the buffer is subsequently read in the function,
> but only conditionally.
>
> That said, since encode() overwrites the whole buffer right after
> it has been cleared by memset, I would think that a warning pointing
> that out could be helpful (although I'm not sure -Wunused is the
> right warning to issue).
>
>>
>> * tree-streamer.c (streamer_check_handled_ts_structures)
>> The function defines a local array bool handled_p[LAST_TS_ENUM];
>> and the warning is emitted for:
>> memset (&handled_p, 0, sizeof (handled_p));
>>
>> That's because the function then initializes each element of the array
>> handled_p to true
>> making the memset call redundant.
>> I am not sure if warning for the above case is a good idea ? The call
>> to memset() seems deliberate, to initialize all elements to 0, and
>> later assert checks if all the elements were explicitly set to true.
>
> Right.  Warning on this function doesn't seem right since
> the variable is subsequently used in the test loop.
>
>>
>> * value-prof.c (free_hist):
>> Warns for the call to memset:
>>
>> static int
>> free_hist (void **slot, void *data ATTRIBUTE_UNUSED)
>> {
>>   histogram_value hist = *(histogram_value *) slot;
>>   free (hist->hvalue.counters);
>>   if (flag_checking)
>>     memset (hist, 0xab, sizeof (*hist));
>>   free (hist);
>>   return 1;
>> }
>>
>> Assuming flag_checking was true, the call to memset would be dead
>> anyway since it would be immediately freed ? Um, I don't understand
>> the purpose of memset in the above function.
>
> I'm guessing it's an effort to invalidate the memory to detect
> subsequent accesses to its contents.  Warning on such code would
> be valuable because it's a common misconception that a memset
> can be used to wipe sensitive data (e.g., passwords) from memory
> before freeing it, to prevent them from leaking into compromised
> stack frames (or core files).  But it should be a warning that's
> distinct from -Wunused.
>
> Martin


[-- Attachment #2: gcc-80806.diff --]
[-- Type: text/x-patch, Size: 44158 bytes --]

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<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 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, "%<sizeof%> 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<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)
@@ -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;
 }
 \f
@@ -9831,8 +9925,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
@@ -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" } }

  reply	other threads:[~2017-05-24  3:31 UTC|newest]

Thread overview: 11+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2017-05-18 18:56 PR80806 Prathamesh Kulkarni
2017-05-22  4:50 ` PR80806 Jeff Law
2017-05-23 13:50   ` PR80806 Prathamesh Kulkarni
2017-05-23 15:59 ` PR80806 Martin Sebor
2017-05-24  5:42   ` Martin Sebor [this message]
2017-06-29 17:57   ` PR80806 Jeff Law
2017-06-29 18:05     ` PR80806 Jeff Law
2017-06-29 21:45       ` PR80806 Martin Sebor
2017-06-29 18:20 ` PR80806 Jeff Law
2017-06-30  8:25   ` PR80806 Richard Biener
2017-06-30  8:33     ` PR80806 Jakub Jelinek

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=c3cbf742-fc78-9aac-59de-d755664700ba@gmail.com \
    --to=msebor@gmail.com \
    --cc=gcc-patches@gcc.gnu.org \
    --cc=prathamesh.kulkarni@linaro.org \
    --cc=rguenther@suse.de \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).