diff mbox series

warn on uninitialized accesses by function calls (PR 10138)

Message ID 0b609ebe-7ede-6333-5199-4113b70cd8c0@gmail.com
State New
Headers show
Series warn on uninitialized accesses by function calls (PR 10138) | expand

Commit Message

Martin Sebor May 15, 2020, 11:31 p.m. UTC
Besides better buffer overflow checking, the new GCC 10 attribute
access also provides an opportunity to detect other kinds of bugs,
including uninitialized accesses by user-defined functions.
The attached patch implements this enhancement.

In addition, the closely related PR 10138 requests that GCC warn when
passing the address of an uninitialized variable to a const-qualified
pointer function argument.  Const pointers almost always imply a read
access of the object, so the patch also enables the warning in these
cases.  (There are situations when a const pointer doesn't imply it
and the warning takes care not to trigger overly enthusiastically.)
Since pointers often point to allocated objects it seemed natural
(and was surprisingly easy) to also detect uninitialized reads from
those.

For optimum results I slightly enhanced the detection of the referenced
decls and allocations.  In the process, I also noticed and fixed a small
bug in the existing code.   This helps both find more uninitialized
variables and reduce the rate of false positives in existing warnings.

Besides the usual GCC bootstrap/regtest I validated the changes by
building a number of packages, including Binutils/GDB, Glibc, and
the Linux kernel.  It found a decent number of likely bugs (about
half a doze by my count) but also triggered a few false positives.
One class of such problems was due to the kernel's function

   __check_object_size (const void*, unsigned, bool)

used to validate the sizes of objects without ever accessing them.
To accommodate this idiom the patch adds a new  mode to attribute
access: none.

Martin

Comments

Martin Sebor May 29, 2020, 3:02 p.m. UTC | #1
Ping: https://gcc.gnu.org/pipermail/gcc-patches/2020-May/545856.html

On 5/15/20 5:31 PM, Martin Sebor wrote:
> Besides better buffer overflow checking, the new GCC 10 attribute
> access also provides an opportunity to detect other kinds of bugs,
> including uninitialized accesses by user-defined functions.
> The attached patch implements this enhancement.
> 
> In addition, the closely related PR 10138 requests that GCC warn when
> passing the address of an uninitialized variable to a const-qualified
> pointer function argument.  Const pointers almost always imply a read
> access of the object, so the patch also enables the warning in these
> cases.  (There are situations when a const pointer doesn't imply it
> and the warning takes care not to trigger overly enthusiastically.)
> Since pointers often point to allocated objects it seemed natural
> (and was surprisingly easy) to also detect uninitialized reads from
> those.
> 
> For optimum results I slightly enhanced the detection of the referenced
> decls and allocations.  In the process, I also noticed and fixed a small
> bug in the existing code.   This helps both find more uninitialized
> variables and reduce the rate of false positives in existing warnings.
> 
> Besides the usual GCC bootstrap/regtest I validated the changes by
> building a number of packages, including Binutils/GDB, Glibc, and
> the Linux kernel.  It found a decent number of likely bugs (about
> half a doze by my count) but also triggered a few false positives.
> One class of such problems was due to the kernel's function
> 
>    __check_object_size (const void*, unsigned, bool)
> 
> used to validate the sizes of objects without ever accessing them.
> To accommodate this idiom the patch adds a new  mode to attribute
> access: none.
> 
> Martin
Li, Pan2 via Gcc-patches June 4, 2020, 4:59 p.m. UTC | #2
On Fri, 2020-05-15 at 17:31 -0600, Martin Sebor via Gcc-patches wrote:
> Besides better buffer overflow checking, the new GCC 10 attribute
> access also provides an opportunity to detect other kinds of bugs,
> including uninitialized accesses by user-defined functions.
> The attached patch implements this enhancement.
> 
> In addition, the closely related PR 10138 requests that GCC warn when
> passing the address of an uninitialized variable to a const-qualified
> pointer function argument.  Const pointers almost always imply a read
> access of the object, so the patch also enables the warning in these
> cases.  (There are situations when a const pointer doesn't imply it
> and the warning takes care not to trigger overly enthusiastically.)
> Since pointers often point to allocated objects it seemed natural
> (and was surprisingly easy) to also detect uninitialized reads from
> those.
> 
> For optimum results I slightly enhanced the detection of the referenced
> decls and allocations.  In the process, I also noticed and fixed a small
> bug in the existing code.   This helps both find more uninitialized
> variables and reduce the rate of false positives in existing warnings.
> 
> Besides the usual GCC bootstrap/regtest I validated the changes by
> building a number of packages, including Binutils/GDB, Glibc, and
> the Linux kernel.  It found a decent number of likely bugs (about
> half a doze by my count) but also triggered a few false positives.
> One class of such problems was due to the kernel's function
> 
>    __check_object_size (const void*, unsigned, bool)
> 
> used to validate the sizes of objects without ever accessing them.
> To accommodate this idiom the patch adds a new  mode to attribute
> access: none.
There's a minor typo in a comment within initialize_argument_information
s/accewss/access/

I'm slightly concerned about the diagnostic wording change breaking scanners and
such, but that's well outside what we consider stable release-to-release.  So,
OK.

I think Bin has some work in tree-ssa-uninit.c that's been approved, but not
merged yet and there may be conflicts (though no fundamental conceptual
conflicts).  I suggest committing immediately rather than waiting for Bin though
-- I'll ping Bin on his change and let him know there's a notable potential for
conflicts.

jeff
>
Christophe Lyon June 5, 2020, 11:07 a.m. UTC | #3
On Thu, 4 Jun 2020 at 18:59, Jeff Law via Gcc-patches
<gcc-patches@gcc.gnu.org> wrote:
>
> On Fri, 2020-05-15 at 17:31 -0600, Martin Sebor via Gcc-patches wrote:
> > Besides better buffer overflow checking, the new GCC 10 attribute
> > access also provides an opportunity to detect other kinds of bugs,
> > including uninitialized accesses by user-defined functions.
> > The attached patch implements this enhancement.
> >
> > In addition, the closely related PR 10138 requests that GCC warn when
> > passing the address of an uninitialized variable to a const-qualified
> > pointer function argument.  Const pointers almost always imply a read
> > access of the object, so the patch also enables the warning in these
> > cases.  (There are situations when a const pointer doesn't imply it
> > and the warning takes care not to trigger overly enthusiastically.)
> > Since pointers often point to allocated objects it seemed natural
> > (and was surprisingly easy) to also detect uninitialized reads from
> > those.
> >
> > For optimum results I slightly enhanced the detection of the referenced
> > decls and allocations.  In the process, I also noticed and fixed a small
> > bug in the existing code.   This helps both find more uninitialized
> > variables and reduce the rate of false positives in existing warnings.
> >
> > Besides the usual GCC bootstrap/regtest I validated the changes by
> > building a number of packages, including Binutils/GDB, Glibc, and
> > the Linux kernel.  It found a decent number of likely bugs (about
> > half a doze by my count) but also triggered a few false positives.
> > One class of such problems was due to the kernel's function
> >
> >    __check_object_size (const void*, unsigned, bool)
> >
> > used to validate the sizes of objects without ever accessing them.
> > To accommodate this idiom the patch adds a new  mode to attribute
> > access: none.
> There's a minor typo in a comment within initialize_argument_information
> s/accewss/access/
>
> I'm slightly concerned about the diagnostic wording change breaking scanners and
> such, but that's well outside what we consider stable release-to-release.  So,
> OK.

Hi,

This is causing a regression in fortran tests:
FAIL: gfortran.dg/goacc/uninit-use-device-clause.f95   -O   (test for
warnings, line 7)
FAIL: gfortran.dg/goacc/uninit-use-device-clause.f95   -O  (test for
excess errors)
Excess errors:
/gcc/testsuite/gfortran.dg/goacc/uninit-use-device-clause.f95:7:0:
Warning: 'p' is used uninitialized [-Wuninitialized]

Christophe

>
> I think Bin has some work in tree-ssa-uninit.c that's been approved, but not
> merged yet and there may be conflicts (though no fundamental conceptual
> conflicts).  I suggest committing immediately rather than waiting for Bin though
> -- I'll ping Bin on his change and let him know there's a notable potential for
> conflicts.
>
> jeff
> >
>
Martin Sebor June 5, 2020, 3:58 p.m. UTC | #4
On 6/5/20 5:07 AM, Christophe Lyon wrote:
> On Thu, 4 Jun 2020 at 18:59, Jeff Law via Gcc-patches
> <gcc-patches@gcc.gnu.org> wrote:
>>
>> On Fri, 2020-05-15 at 17:31 -0600, Martin Sebor via Gcc-patches wrote:
>>> Besides better buffer overflow checking, the new GCC 10 attribute
>>> access also provides an opportunity to detect other kinds of bugs,
>>> including uninitialized accesses by user-defined functions.
>>> The attached patch implements this enhancement.
>>>
>>> In addition, the closely related PR 10138 requests that GCC warn when
>>> passing the address of an uninitialized variable to a const-qualified
>>> pointer function argument.  Const pointers almost always imply a read
>>> access of the object, so the patch also enables the warning in these
>>> cases.  (There are situations when a const pointer doesn't imply it
>>> and the warning takes care not to trigger overly enthusiastically.)
>>> Since pointers often point to allocated objects it seemed natural
>>> (and was surprisingly easy) to also detect uninitialized reads from
>>> those.
>>>
>>> For optimum results I slightly enhanced the detection of the referenced
>>> decls and allocations.  In the process, I also noticed and fixed a small
>>> bug in the existing code.   This helps both find more uninitialized
>>> variables and reduce the rate of false positives in existing warnings.
>>>
>>> Besides the usual GCC bootstrap/regtest I validated the changes by
>>> building a number of packages, including Binutils/GDB, Glibc, and
>>> the Linux kernel.  It found a decent number of likely bugs (about
>>> half a doze by my count) but also triggered a few false positives.
>>> One class of such problems was due to the kernel's function
>>>
>>>     __check_object_size (const void*, unsigned, bool)
>>>
>>> used to validate the sizes of objects without ever accessing them.
>>> To accommodate this idiom the patch adds a new  mode to attribute
>>> access: none.
>> There's a minor typo in a comment within initialize_argument_information
>> s/accewss/access/
>>
>> I'm slightly concerned about the diagnostic wording change breaking scanners and
>> such, but that's well outside what we consider stable release-to-release.  So,
>> OK.
> 
> Hi,
> 
> This is causing a regression in fortran tests:
> FAIL: gfortran.dg/goacc/uninit-use-device-clause.f95   -O   (test for
> warnings, line 7)
> FAIL: gfortran.dg/goacc/uninit-use-device-clause.f95   -O  (test for
> excess errors)
> Excess errors:
> /gcc/testsuite/gfortran.dg/goacc/uninit-use-device-clause.f95:7:0:
> Warning: 'p' is used uninitialized [-Wuninitialized]

That's probably because not all tests always run.  I grepped
the testsuite for the warning string, adjusted the remaining few that
still had the " in this function" part in them, and committed r11-999.

Double checking that commit, I also checked in a work-in-progress test
that I shouldn't have, so it's going to fail.

Martin

> 
> Christophe
> 
>>
>> I think Bin has some work in tree-ssa-uninit.c that's been approved, but not
>> merged yet and there may be conflicts (though no fundamental conceptual
>> conflicts).  I suggest committing immediately rather than waiting for Bin though
>> -- I'll ping Bin on his change and let him know there's a notable potential for
>> conflicts.
>>
>> jeff
>>>
>>
diff mbox series

Patch

PR middle-end/10138 - warn for uninitialized arrays passed as const arguments

gcc/c-family/ChangeLog:

	PR middle-end/10138
	* c-attribs.c (append_access_attrs): Handle attr_access::none.
	(handle_access_attribute): Same.

gcc/ChangeLog:

	PR middle-end/10138
	* attribs.c (init_attr_rdwr_indices): Move function here
	* attribs.h (attr_access): Add 'none'.
	(rdwr_map): Define.
	(init_attr_rdwr_indices): Declared function.
	* builtins.c (warn_for_access)): New function.
	(check_access): Call it.
	* builtins.h (checK-access): Add an optional argument.
	* calls.c (rdwr_access_hash, rdwr_map): Move to attribs.h.
	(append_attrname): Handle attr_access::none.
	(maybe_warn_rdwr_sizes): Same.
	(initialize_argument_information): Update comments.
	* doc/extend.texi (attribute access): Document 'none'.
	* tree-ssa-uninit.c (struct wlimits): New.
	(maybe_warn_operand): New function.
	(warn_uninitialized_vars): Refactor code into maybe_warn_operand.
	Also call for function calls.
	(pass_late_warn_uninitialized::execute): Adjust comments.
	(execute_early_warn_uninitialized): Same.

gcc/testsuite/ChangeLog:

	PR middle-end/10138
	* c-c++-common/Wsizeof-pointer-memaccess1.c: Prune out valid
	Wuninitialized.
	* c-c++-common/uninit-pr51010.c: Adjust expected warning format.
	* c-c++-common/goacc/uninit-dim-clause.c: Same.
	* c-c++-common/goacc/uninit-firstprivate-clause.c: Same.
	* c-c++-common/goacc/uninit-if-clause.c: Same.
	* c-c++-common/gomp/pr70550-1.c: Same.
	* g++.dg/20090107-1.C: Same.
	* gcc.dg/Warray-bounds-54.c: Prune out valid -Wuninitialized.
	* gcc.dg/Wstringop-overflow-33.c: New test.
	* gcc.dg/attr-access-none.c: New test.
	* gcc.dg/pr71581.c: Adjust text of expected warning.
	* gcc.dg/uninit-15.c: Same.
	* gcc.dg/uninit-32.c: New test.
	* gcc.dg/uninit-33.c: New test.
	* gcc.dg/uninit-34.c: New test.
	* gcc.dg/uninit-36.c: New test.
	* gcc.dg/uninit-B-O0.c: Adjust text of expected warning.
	* gcc.dg/uninit-I-O0.c: Same.
	* gcc.dg/uninit-pr19430-O0.c: Same.
	* gcc.dg/uninit-pr19430.c (foo): Same.
	* gcc.dg/uninit-pr95136.c: New test.

diff --git a/gcc/attribs.c b/gcc/attribs.c
index 7d0f4b5b7b4..71dae123af8 100644
--- a/gcc/attribs.c
+++ b/gcc/attribs.c
@@ -2017,6 +2017,65 @@  maybe_diag_alias_attributes (tree alias, tree target)
     }
 }
 
+/* Initialize a mapping for a call to function FNDECL declared with
+   attribute access.  Each attribute positional operand inserts one
+   entry into the mapping with the operand number as the key.  */
+
+void
+init_attr_rdwr_indices (rdwr_map *rwm, tree fntype)
+{
+  if (!fntype)
+    return;
+
+  for (tree access = TYPE_ATTRIBUTES (fntype);
+       (access = lookup_attribute ("access", access));
+       access = TREE_CHAIN (access))
+    {
+      /* The TREE_VALUE of an attribute is a TREE_LIST whose TREE_VALUE
+	 is the attribute argument's value.  */
+      tree mode = TREE_VALUE (access);
+      gcc_assert (TREE_CODE (mode) == TREE_LIST);
+      mode = TREE_VALUE (mode);
+      gcc_assert (TREE_CODE (mode) == STRING_CST);
+
+      const char *modestr = TREE_STRING_POINTER (mode);
+      for (const char *m = modestr; *m; )
+	{
+	  attr_access acc = { };
+
+	  switch (*m)
+	    {
+	    case 'r': acc.mode = acc.read_only; break;
+	    case 'w': acc.mode = acc.write_only; break;
+	    case 'x': acc.mode = acc.read_write; break;
+	    case '-': acc.mode = acc.none; break;
+	    default: gcc_unreachable ();
+	    }
+
+	  char *end;
+	  acc.ptrarg = strtoul (++m, &end, 10);
+	  m = end;
+	  if (*m == ',')
+	    {
+	      acc.sizarg = strtoul (++m, &end, 10);
+	      m = end;
+	    }
+	  else
+	    acc.sizarg = UINT_MAX;
+
+	  acc.ptr = NULL_TREE;
+	  acc.size = NULL_TREE;
+
+	  /* Unconditionally add an entry for the required pointer
+	     operand of the attribute, and one for the optional size
+	     operand when it's specified.  */
+	  rwm->put (acc.ptrarg, acc);
+	  if (acc.sizarg != UINT_MAX)
+	    rwm->put (acc.sizarg, acc);
+	}
+    }
+}
+
 
 #if CHECKING_P
 
diff --git a/gcc/attribs.h b/gcc/attribs.h
index 8d794649b3e..dea0b6c44e6 100644
--- a/gcc/attribs.h
+++ b/gcc/attribs.h
@@ -234,8 +234,18 @@  struct attr_access
   unsigned sizarg;
 
   /* The access mode.  */
-  enum access_mode { read_only, write_only, read_write };
+  enum access_mode { none, read_only, write_only, read_write };
   access_mode mode;
 };
 
+/* Used to define rdwr_map below.  */
+struct rdwr_access_hash: int_hash<int, -1> { };
+
+/* A mapping between argument number corresponding to attribute access
+   mode (read_only, write_only, or read_write) and operands.  */
+struct attr_access;
+typedef hash_map<rdwr_access_hash, attr_access> rdwr_map;
+
+extern void init_attr_rdwr_indices (rdwr_map *, tree);
+
 #endif // GCC_ATTRIBS_H
diff --git a/gcc/builtins.c b/gcc/builtins.c
index 53bae599d3e..eacc1aa87e5 100644
--- a/gcc/builtins.c
+++ b/gcc/builtins.c
@@ -3310,6 +3310,90 @@  determine_block_size (tree len, rtx len_rtx,
 			  GET_MODE_MASK (GET_MODE (len_rtx)));
 }
 
+/* For an expression EXP issue an access warning controlled by option OPT
+   with access to a region SLEN bytes in size in the RANGE of sizes.  */
+
+static bool
+warn_for_access (location_t loc, tree func, tree exp, int opt, tree range[2],
+		 tree slen, bool access)
+{
+  bool warned = false;
+
+  if (access)
+    {
+      if (tree_int_cst_equal (range[0], range[1]))
+	warned = (func
+		  ? warning_n (loc, opt, tree_to_uhwi (range[0]),
+			       "%K%qD reading %E byte from a region of size %E",
+			       "%K%qD reading %E bytes from a region of size %E",
+			       exp, func, range[0], slen)
+		  : warning_n (loc, opt, tree_to_uhwi (range[0]),
+			       "%Kreading %E byte from a region of size %E",
+			       "%Kreading %E bytes from a region of size %E",
+			       exp, range[0], slen));
+      else if (tree_int_cst_sign_bit (range[1]))
+	{
+	  /* Avoid printing the upper bound if it's invalid.  */
+	  warned = (func
+		    ? warning_at (loc, opt,
+				  "%K%qD reading %E or more bytes from a region "
+				  "of size %E",
+				  exp, func, range[0], slen)
+		    : warning_at (loc, opt,
+				  "%Kreading %E or more bytes from a region "
+				  "of size %E",
+				  exp, range[0], slen));
+	}
+      else
+	warned = (func
+		  ? warning_at (loc, opt,
+				"%K%qD reading between %E and %E bytes from "
+				"a region of size %E",
+				exp, func, range[0], range[1], slen)
+		  : warning_at (loc, opt,
+				"%Kreading between %E and %E bytes from "
+				"a region of size %E",
+				exp, range[0], range[1], slen));
+
+      return warned;
+    }
+
+  if (tree_int_cst_equal (range[0], range[1]))
+    warned = (func
+	      ? warning_n (loc, opt, tree_to_uhwi (range[0]),
+			   "%K%qD epecting %E byte in a region of size %E",
+			   "%K%qD expecting %E bytes in a region of size %E",
+			   exp, func, range[0], slen)
+	      : warning_n (loc, opt, tree_to_uhwi (range[0]),
+			   "%Kexpecting %E byte in a region of size %E",
+			   "%Kexpecting %E bytes in a region of size %E",
+			   exp, range[0], slen));
+  else if (tree_int_cst_sign_bit (range[1]))
+    {
+      /* Avoid printing the upper bound if it's invalid.  */
+      warned = (func
+		? warning_at (loc, opt,
+			      "%K%qD expecting %E or more bytes in a region "
+			      "of size %E",
+			      exp, func, range[0], slen)
+		: warning_at (loc, opt,
+			      "%Kexpecting %E or more bytes in a region "
+			      "of size %E",
+			      exp, range[0], slen));
+    }
+  else
+    warned = (func
+	      ? warning_at (loc, opt,
+			    "%K%qD expecting between %E and %E bytes in "
+			    "a region of size %E",
+			    exp, func, range[0], range[1], slen)
+	      : warning_at (loc, opt,
+			    "%Kexpectting between %E and %E bytes in "
+			    "a region of size %E",
+			    exp, range[0], range[1], slen));
+  return warned;
+}
+
 /* Try to verify that the sizes and lengths of the arguments to a string
    manipulation function given by EXP are within valid bounds and that
    the operation does not lead to buffer overflow or read past the end.
@@ -3336,12 +3420,16 @@  determine_block_size (tree len, rtx len_rtx,
    When DSTWRITE is null LEN is checked to verify that it doesn't exceed
    SIZE_MAX.
 
+   ACCESS is true for accesses, false for simple size checks in calls
+   to functions that neither read from nor write to the region.
+
    If the call is successfully verified as safe return true, otherwise
    return false.  */
 
 bool
 check_access (tree exp, tree, tree, tree dstwrite,
-	      tree maxread, tree srcstr, tree dstsize)
+	      tree maxread, tree srcstr, tree dstsize,
+	      bool access /* = true */)
 {
   int opt = OPT_Wstringop_overflow_;
 
@@ -3649,44 +3737,10 @@  check_access (tree exp, tree, tree, tree dstwrite,
       if (TREE_NO_WARNING (exp))
 	return false;
 
-      bool warned = false;
       location_t loc = tree_nonartificial_location (exp);
       loc = expansion_point_location_if_in_system_header (loc);
 
-      if (tree_int_cst_equal (range[0], range[1]))
-	warned = (func
-		  ? warning_n (loc, opt, tree_to_uhwi (range[0]),
-			       "%K%qD reading %E byte from a region of size %E",
-			       "%K%qD reading %E bytes from a region of size %E",
-			       exp, func, range[0], slen)
-		  : warning_n (loc, opt, tree_to_uhwi (range[0]),
-			       "%Kreading %E byte from a region of size %E",
-			       "%Kreading %E bytes from a region of size %E",
-			       exp, range[0], slen));
-      else if (tree_int_cst_sign_bit (range[1]))
-	{
-	  /* Avoid printing the upper bound if it's invalid.  */
-	  warned = (func
-		    ? warning_at (loc, opt,
-				  "%K%qD reading %E or more bytes from a region "
-				  "of size %E",
-				  exp, func, range[0], slen)
-		    : warning_at (loc, opt,
-				  "%Kreading %E or more bytes from a region "
-				  "of size %E",
-				  exp, range[0], slen));
-	}
-      else
-	warned = (func
-		  ? warning_at (loc, opt,
-				"%K%qD reading between %E and %E bytes from "
-				"a region of size %E",
-				exp, func, range[0], range[1], slen)
-		  : warning_at (loc, opt,
-				"%Kreading between %E and %E bytes from "
-				"a region of size %E",
-				exp, range[0], range[1], slen));
-      if (warned)
+      if (warn_for_access (loc, func, exp, opt, range, slen, access))
 	TREE_NO_WARNING (exp) = true;
 
       return false;
diff --git a/gcc/builtins.h b/gcc/builtins.h
index 7d8b9cddf73..73e85d66e9b 100644
--- a/gcc/builtins.h
+++ b/gcc/builtins.h
@@ -156,7 +156,8 @@  bool check_nul_terminated_array (tree, tree, tree = NULL_TREE);
 extern void warn_string_no_nul (location_t, const char *, tree, tree);
 extern tree unterminated_array (tree, tree * = NULL, bool * = NULL);
 extern bool builtin_with_linkage_p (tree);
-extern bool check_access (tree, tree, tree, tree, tree, tree, tree);
+extern bool check_access (tree, tree, tree, tree, tree, tree, tree,
+			  bool = true);
 
 
 #endif /* GCC_BUILTINS_H */
diff --git a/gcc/c-family/c-attribs.c b/gcc/c-family/c-attribs.c
index 7a6fb9af6da..193c4cd7cd0 100644
--- a/gcc/c-family/c-attribs.c
+++ b/gcc/c-family/c-attribs.c
@@ -3875,14 +3875,17 @@  append_access_attrs (tree t, tree attrs, const char *attrstr,
 	  /* Found a matching positional argument.  */
 	  if (*attrspec != pos[-1])
 	    {
+	      const char* const modestr
+		= (pos[-1] == 'r'
+		   ? "read_only"
+		   : (pos[-1] == 'w'
+		      ? "write_only"
+		      : (pos[-1] == 'x' ? "read_write" : "none")));
 	      /* Mismatch in access mode.  */
 	      auto_diagnostic_group d;
 	      if (warning (OPT_Wattributes,
 			   "attribute %qs mismatch with mode %qs",
-			   attrstr,
-			   (pos[-1] == 'r'
-			    ? "read_only"
-			    : (pos[-1] == 'w' ? "write_only" : "read_write")))
+			   attrstr, modestr)
 		  && DECL_P (t))
 		inform (DECL_SOURCE_LOCATION (t),
 			"previous declaration here");
@@ -4014,13 +4017,14 @@  handle_access_attribute (tree *node, tree name, tree args,
 	ps += 2;
     }
 
-  const bool read_only = strncmp (ps, "read_only", 9) == 0;
-  const bool write_only = strncmp (ps, "write_only", 10) == 0;
-  if (!read_only && !write_only && strncmp (ps, "read_write", 10))
+  const bool read_only = !strncmp (ps, "read_only", 9);
+  const bool write_only = !strncmp (ps, "write_only", 10);
+  const bool read_write = !strncmp (ps, "read_write", 10);
+  if (!read_only && !write_only && !read_write && strncmp (ps, "none", 4))
     {
       error ("attribute %qE invalid mode %qs; expected one of "
-	     "%qs, %qs, or %qs", name, access_str,
-	     "read_only", "read_write", "write_only");
+	     "%qs, %qs, %qs, or %qs", name, access_str,
+	     "read_only", "read_write", "write_only", "none");
       return NULL_TREE;
     }
 
@@ -4145,9 +4149,9 @@  handle_access_attribute (tree *node, tree name, tree args,
       }
   }
 
-  if (!read_only)
+  if (read_write || write_only)
     {
-      /* A read_write and write_only modes must reference non-const
+      /* Read_write and write_only modes must reference non-const
 	 arguments.  */
       if (TYPE_READONLY (TREE_TYPE (argtypes[0])))
 	{
@@ -4178,7 +4182,8 @@  handle_access_attribute (tree *node, tree name, tree args,
   /* Verify that the new attribute doesn't conflict with any existing
      attributes specified on previous declarations of the same type
      and if not, concatenate the two.  */
-  const char code = read_only ? 'r' : write_only ? 'w' : 'x';
+  const char code
+    = read_only ? 'r' : write_only ? 'w' : read_write ? 'x' : '-';
   tree new_attrs = append_access_attrs (node[0], attrs, attrstr, code, idxs);
   if (!new_attrs)
     return NULL_TREE;
diff --git a/gcc/calls.c b/gcc/calls.c
index 8041388c1d2..d1c9c0b159a 100644
--- a/gcc/calls.c
+++ b/gcc/calls.c
@@ -1865,70 +1865,6 @@  maybe_complain_about_tail_call (tree call_expr, const char *reason)
   error_at (EXPR_LOCATION (call_expr), "cannot tail-call: %s", reason);
 }
 
-/* Used to define rdwr_map below.  */
-struct rdwr_access_hash: int_hash<int, -1> { };
-
-/* A mapping between argument number corresponding to attribute access
-   mode (read_only, write_only, or read_write) and operands.  */
-typedef hash_map<rdwr_access_hash, attr_access> rdwr_map;
-
-/* Initialize a mapping for a call to function FNDECL declared with
-   attribute access.  Each attribute positional operand inserts one
-   entry into the mapping with the operand number as the key.  */
-
-static void
-init_attr_rdwr_indices (rdwr_map *rwm, tree fntype)
-{
-  if (!fntype)
-    return;
-
-  for (tree access = TYPE_ATTRIBUTES (fntype);
-       (access = lookup_attribute ("access", access));
-       access = TREE_CHAIN (access))
-    {
-      /* The TREE_VALUE of an attribute is a TREE_LIST whose TREE_VALUE
-	 is the attribute argument's value.  */
-      tree mode = TREE_VALUE (access);
-      gcc_assert (TREE_CODE (mode) == TREE_LIST);
-      mode = TREE_VALUE (mode);
-      gcc_assert (TREE_CODE (mode) == STRING_CST);
-
-      const char *modestr = TREE_STRING_POINTER (mode);
-      for (const char *m = modestr; *m; )
-	{
-	  attr_access acc = { };
-
-	  switch (*m)
-	    {
-	    case 'r': acc.mode = acc.read_only; break;
-	    case 'w': acc.mode = acc.write_only; break;
-	    default: acc.mode = acc.read_write; break;
-	    }
-
-	  char *end;
-	  acc.ptrarg = strtoul (++m, &end, 10);
-	  m = end;
-	  if (*m == ',')
-	    {
-	      acc.sizarg = strtoul (++m, &end, 10);
-	      m = end;
-	    }
-	  else
-	    acc.sizarg = UINT_MAX;
-
-	  acc.ptr = NULL_TREE;
-	  acc.size = NULL_TREE;
-
-	  /* Unconditionally add an entry for the required pointer
-	     operand of the attribute, and one for the optional size
-	     operand when it's specified.  */
-	  rwm->put (acc.ptrarg, acc);
-	  if (acc.sizarg != UINT_MAX)
-	    rwm->put (acc.sizarg, acc);
-	}
-    }
-}
-
 /* Returns the type of the argument ARGNO to function with type FNTYPE
    or null when the typoe cannot be determined or no such argument exists.  */
 
@@ -1959,11 +1895,13 @@  append_attrname (const std::pair<int, attr_access> &access,
      appends the attribute pointer operand even when none was specified.  */
   size_t len = strlen (attrstr);
 
-  const char *atname
+  const char* const atname
     = (access.second.mode == attr_access::read_only
        ? "read_only"
        : (access.second.mode == attr_access::write_only
-	  ? "write_only" : "read_write"));
+	  ? "write_only"
+	  : (access.second.mode == attr_access::read_write
+	     ? "read_write" : "none")));
 
   const char *sep = len ? ", " : "";
 
@@ -2131,11 +2069,13 @@  maybe_warn_rdwr_sizes (rdwr_map *rwm, tree exp)
 	  /* For read-only and read-write attributes also set the source
 	     size.  */
 	  srcsize = objsize;
-	  if (access.second.mode == attr_access::read_only)
+	  if (access.second.mode == attr_access::read_only
+	      || access.second.mode == attr_access::none)
 	    {
 	      /* For a read-only attribute there is no destination so
 		 clear OBJSIZE.  This emits "reading N bytes" kind of
-		 diagnostics instead of the "writing N bytes" kind.  */
+		 diagnostics instead of the "writing N bytes" kind,
+		 unless MODE is none.  */
 	      objsize = NULL_TREE;
 	    }
 	}
@@ -2145,7 +2085,7 @@  maybe_warn_rdwr_sizes (rdwr_map *rwm, tree exp)
 	 diagnosed.  */
       TREE_NO_WARNING (exp) = false;
       check_access (exp, NULL_TREE, NULL_TREE, size, /*maxread=*/ NULL_TREE,
-		    srcsize, objsize);
+		    srcsize, objsize, access.second.mode != attr_access::none);
 
       if (TREE_NO_WARNING (exp))
 	/* If check_access issued a warning above, append the relevant
@@ -2285,8 +2225,7 @@  initialize_argument_information (int num_actuals ATTRIBUTE_UNUSED,
   /* Array for up to the two attribute alloc_size arguments.  */
   tree alloc_args[] = { NULL_TREE, NULL_TREE };
 
-  /* Map of attribute read_only, write_only, or read_write specifications
-     for function arguments.  */
+  /* Map of attribute accewss specifications for function arguments.  */
   rdwr_map rdwr_idx;
   init_attr_rdwr_indices (&rdwr_idx, fntype);
 
@@ -2559,7 +2498,7 @@  initialize_argument_information (int num_actuals ATTRIBUTE_UNUSED,
      nul-terminated strings.  */
   maybe_warn_nonstring_arg (fndecl, exp);
 
-  /* Check read_only, write_only, and read_write arguments.  */
+  /* Check attribute access arguments.  */
   maybe_warn_rdwr_sizes (&rdwr_idx, exp);
 }
 
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index c80848e9061..5495881b45b 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -2497,8 +2497,8 @@  may be diagnosed by warnings such as @option{-Wstringop-overflow},
 The @code{access} attribute specifies that a function to whose by-reference
 arguments the attribute applies accesses the referenced object according to
 @var{access-mode}.  The @var{access-mode} argument is required and must be
-one of three names: @code{read_only}, @code{read_write}, or @code{write_only}.
-The remaining two are positional arguments.
+one of four names: @code{read_only}, @code{read_write}, @code{write_only},
+or @code{none}.  The remaining two are positional arguments.
 
 The required @var{ref-index} positional argument  denotes a function
 argument of pointer (or in C++, reference) type that is subject to
@@ -2555,6 +2555,14 @@  __attribute__ ((access (write_only, 1), access (read_only, 2))) char* strcpy (ch
 __attribute__ ((access (write_only, 1, 2), access (read_write, 3))) int fgets (char*, int, FILE*);
 @end smallexample
 
+The access mode @code{none} specifies that the pointer to which it applies
+is not used to access the referenced object at all.  Unless the pointer is
+null the pointed-to object must exist and have at least the size as denoted
+by the @var{size-index} argument.  The object need not be initialized.
+The mode is intended to be used as a means to help validate the expected
+object size, for example in functions that call @code{__builtin_object_size}.
+@xref{Object Size Checking}.
+
 @item alias ("@var{target}")
 @cindex @code{alias} function attribute
 The @code{alias} attribute causes the declaration to be emitted as an alias
diff --git a/gcc/testsuite/c-c++-common/Wsizeof-pointer-memaccess1.c b/gcc/testsuite/c-c++-common/Wsizeof-pointer-memaccess1.c
index 4281e3b5a8e..c4127b805ab 100644
--- a/gcc/testsuite/c-c++-common/Wsizeof-pointer-memaccess1.c
+++ b/gcc/testsuite/c-c++-common/Wsizeof-pointer-memaccess1.c
@@ -161,3 +161,5 @@  f4 (char x[64], char *y, __builtin_va_list ap)
   snprintf (p, sizeof (buf), "%s", y);
   vsnprintf (p, sizeof (buf), "%s", ap);
 }
+
+/* { dg-prune-output "-Wuninitialized" } */
diff --git a/gcc/testsuite/c-c++-common/goacc/uninit-dim-clause.c b/gcc/testsuite/c-c++-common/goacc/uninit-dim-clause.c
index 9f11196bdbe..827dac71519 100644
--- a/gcc/testsuite/c-c++-common/goacc/uninit-dim-clause.c
+++ b/gcc/testsuite/c-c++-common/goacc/uninit-dim-clause.c
@@ -4,13 +4,13 @@  void acc_parallel()
 {
   int i, j, k;
 
-  #pragma acc parallel num_gangs(i) /* { dg-warning "is used uninitialized in this function" } */
+  #pragma acc parallel num_gangs(i) /* { dg-warning "is used uninitialized" } */
   ;
 
-  #pragma acc parallel num_workers(j) /* { dg-warning "is used uninitialized in this function" } */
+  #pragma acc parallel num_workers(j) /* { dg-warning "is used uninitialized" } */
   ;
 
-  #pragma acc parallel vector_length(k) /* { dg-warning "is used uninitialized in this function" } */
+  #pragma acc parallel vector_length(k) /* { dg-warning "is used uninitialized" } */
   ;
 }
 
@@ -18,12 +18,12 @@  void acc_kernels()
 {
   int i, j, k;
 
-  #pragma acc kernels num_gangs(i) /* { dg-warning "is used uninitialized in this function" } */
+  #pragma acc kernels num_gangs(i) /* { dg-warning "is used uninitialized" } */
   ;
 
-  #pragma acc kernels num_workers(j) /* { dg-warning "is used uninitialized in this function" } */
+  #pragma acc kernels num_workers(j) /* { dg-warning "is used uninitialized" } */
   ;
 
-  #pragma acc kernels vector_length(k) /* { dg-warning "is used uninitialized in this function" } */
+  #pragma acc kernels vector_length(k) /* { dg-warning "is used uninitialized" } */
   ;
 }
diff --git a/gcc/testsuite/c-c++-common/goacc/uninit-firstprivate-clause.c b/gcc/testsuite/c-c++-common/goacc/uninit-firstprivate-clause.c
index 2584033a8c5..334567d3337 100644
--- a/gcc/testsuite/c-c++-common/goacc/uninit-firstprivate-clause.c
+++ b/gcc/testsuite/c-c++-common/goacc/uninit-firstprivate-clause.c
@@ -18,7 +18,7 @@  foo2 (void)
 {
   int i;
 
-#pragma acc parallel firstprivate (i) /* { dg-warning "is used uninitialized in this function" } */
+#pragma acc parallel firstprivate (i) /* { dg-warning "is used uninitialized" } */
   {
     i = 1;
   }
diff --git a/gcc/testsuite/c-c++-common/goacc/uninit-if-clause.c b/gcc/testsuite/c-c++-common/goacc/uninit-if-clause.c
index 55caa4c2c7f..7f78d729681 100644
--- a/gcc/testsuite/c-c++-common/goacc/uninit-if-clause.c
+++ b/gcc/testsuite/c-c++-common/goacc/uninit-if-clause.c
@@ -11,28 +11,28 @@  main (void)
   bool b, b2, b3, b4;
   int i, i2;
 
-  #pragma acc parallel if(l) /* { dg-warning "is used uninitialized in this function" } */
+  #pragma acc parallel if(l) /* { dg-warning "is used uninitialized" } */
   ;
 
-  #pragma acc parallel if(b) /* { dg-warning "is used uninitialized in this function" "" { xfail c++ } } */
+  #pragma acc parallel if(b) /* { dg-warning "is used uninitialized" "" { xfail c++ } } */
   ;
 
-  #pragma acc kernels if(l2) /* { dg-warning "is used uninitialized in this function" } */
+  #pragma acc kernels if(l2) /* { dg-warning "is used uninitialized" } */
   ;
 
-  #pragma acc kernels if(b2) /* { dg-warning "is used uninitialized in this function" "" { xfail c++ } } */
+  #pragma acc kernels if(b2) /* { dg-warning "is used uninitialized" "" { xfail c++ } } */
   ;
 
-  #pragma acc data if(l3) /* { dg-warning "is used uninitialized in this function" } */
+  #pragma acc data if(l3) /* { dg-warning "is used uninitialized" } */
   ;
 
-  #pragma acc data if(b3) /* { dg-warning "is used uninitialized in this function" "" { xfail c++ } } */
+  #pragma acc data if(b3) /* { dg-warning "is used uninitialized" "" { xfail c++ } } */
   ;
 
-  #pragma acc update if(l4) self(i) /* { dg-warning "is used uninitialized in this function" } */
+  #pragma acc update if(l4) self(i) /* { dg-warning "is used uninitialized" } */
   ;
 
-  #pragma acc update if(b4) self(i2) /* { dg-warning "is used uninitialized in this function" "" { xfail c++ } } */
+  #pragma acc update if(b4) self(i2) /* { dg-warning "is used uninitialized" "" { xfail c++ } } */
   ;
 
 }
diff --git a/gcc/testsuite/c-c++-common/gomp/pr70550-1.c b/gcc/testsuite/c-c++-common/gomp/pr70550-1.c
index 493d4175993..d0d2fa53e03 100644
--- a/gcc/testsuite/c-c++-common/gomp/pr70550-1.c
+++ b/gcc/testsuite/c-c++-common/gomp/pr70550-1.c
@@ -16,7 +16,7 @@  foo (void)
 {
   {
     int i;
-    #pragma omp target defaultmap(tofrom:scalar)	/* { dg-bogus "is used uninitialized in this function" } */
+    #pragma omp target defaultmap(tofrom:scalar)	/* { dg-bogus "is used uninitialized" } */
     {
       i = 26;
       bar (i);
@@ -24,7 +24,7 @@  foo (void)
   }
   {
     T j;
-    #pragma omp target defaultmap(tofrom:scalar)	/* { dg-bogus "is used uninitialized in this function" } */
+    #pragma omp target defaultmap(tofrom:scalar)	/* { dg-bogus "is used uninitialized" } */
     {
       j = 37;
       bar (j);
@@ -32,7 +32,7 @@  foo (void)
   }
   {
     int i;
-    #pragma omp target					/* { dg-bogus "is used uninitialized in this function" } */
+    #pragma omp target					/* { dg-bogus "is used uninitialized" } */
     {
       i = 26;
       bar (i);
@@ -40,7 +40,7 @@  foo (void)
   }
   {
     T j;
-    #pragma omp target					/* { dg-bogus "is used uninitialized in this function" } */
+    #pragma omp target					/* { dg-bogus "is used uninitialized" } */
     {
       j = 37;
       bar (j);
@@ -48,7 +48,7 @@  foo (void)
   }
   {
     int i;
-    #pragma omp target firstprivate (i)			/* { dg-warning "is used uninitialized in this function" } */
+    #pragma omp target firstprivate (i)			/* { dg-warning "is used uninitialized" } */
     {
       i = 26;
       bar (i);
@@ -56,7 +56,7 @@  foo (void)
   }
   {
     T j;
-    #pragma omp target firstprivate (j)			/* { dg-warning "is used uninitialized in this function" } */
+    #pragma omp target firstprivate (j)			/* { dg-warning "is used uninitialized" } */
     {
       j = 37;
       bar (j);
@@ -64,7 +64,7 @@  foo (void)
   }
   {
     int i;
-    #pragma omp target private (i)			/* { dg-bogus "is used uninitialized in this function" } */
+    #pragma omp target private (i)			/* { dg-bogus "is used uninitialized" } */
     {
       i = 26;
       bar (i);
@@ -72,7 +72,7 @@  foo (void)
   }
   {
     T j;
-    #pragma omp target private (j)			/* { dg-bogus "is used uninitialized in this function" } */
+    #pragma omp target private (j)			/* { dg-bogus "is used uninitialized" } */
     {
       j = 37;
       bar (j);
diff --git a/gcc/testsuite/c-c++-common/uninit-pr51010.c b/gcc/testsuite/c-c++-common/uninit-pr51010.c
index f28da46562c..9fd1ea3a262 100644
--- a/gcc/testsuite/c-c++-common/uninit-pr51010.c
+++ b/gcc/testsuite/c-c++-common/uninit-pr51010.c
@@ -4,10 +4,10 @@ 
 int f (int j)
 {
   int a [10];
-  return a [j]; /* { dg-warning "a\\\[j\\\]. is used uninitialized" } */
+  return a [j]; /* { dg-warning "a|a\\\[j\\\]. is used uninitialized" } */
 }
 int g (int j)
 {
   int a [10];
-  return a [j+1]; /* { dg-warning "a\\\[<unknown>\\\]. is used uninitialized" } */
+  return a [j+1]; /* { dg-warning "a|a\\\[<unknown>\\\]. is used uninitialized" } */
 }
diff --git a/gcc/testsuite/g++.dg/20090107-1.C b/gcc/testsuite/g++.dg/20090107-1.C
index ff586e81767..80b88f02dcc 100644
--- a/gcc/testsuite/g++.dg/20090107-1.C
+++ b/gcc/testsuite/g++.dg/20090107-1.C
@@ -6,7 +6,7 @@  template <typename T> struct Q1 { typedef int x; };
 template <typename T> struct Q2 {
   typename Q1<T>::x f() {
     int k;
-    return k; /* { dg-warning "'k' is used uninitialized in this function" }  */
+    return k; /* { dg-warning "'k' is used uninitialized" }  */
   }
 };
 int foo() { return Q2<int>().f(); }
diff --git a/gcc/testsuite/gcc.dg/Warray-bounds-54.c b/gcc/testsuite/gcc.dg/Warray-bounds-54.c
index 644fcd02cb6..5df5710c1d9 100644
--- a/gcc/testsuite/gcc.dg/Warray-bounds-54.c
+++ b/gcc/testsuite/gcc.dg/Warray-bounds-54.c
@@ -10,9 +10,9 @@  int f0 (void)
   return p[2];      // { dg-warning "-Warray-bounds" }
 }
 
-int f1 (void)
+int f1 (int j)
 {
-  int i;
+  int i = j;
   int *p = &i;
   return p[2];      // { dg-warning "-Warray-bounds" }
 }
@@ -22,3 +22,5 @@  int f2 (int i)
   int *p = &i;
   return p[2];      // { dg-warning "-Warray-bounds" }
 }
+
+/* { dg-prune-output "-Wuninitialized" } */
diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-33.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-33.c
new file mode 100644
index 00000000000..cb8aeb9b5d9
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/Wstringop-overflow-33.c
@@ -0,0 +1,40 @@ 
+/* PR middle-end/82456 - missing -Wstringop-overflow on strcpy reading past
+   the end of an array
+   { dg-do compile }
+   { dg-options "-O2 -Wall" } */
+
+void fcst (char *d)
+{
+  char a[2] = "0";
+
+  __builtin_strcpy (d, a + 3);    // { dg-warning "\\\[-W(array-bounds|stringop-overflow)" }
+}
+
+void frng (char *d, int i)
+{
+  char a[2] = "0";
+
+  if (i < 3)
+    i = 3;
+
+  __builtin_strcpy (d, a + i);    // { dg-warning "\\\[-W(array-bounds|stringop-overflow)" }
+}
+
+void gcst (char *d)
+{
+  char a[2] = "0";
+
+  __builtin_strcpy (d, a + 2);    // { dg-warning "\\\[-W(array-bounds|stringop-overflow)" }
+}
+
+void grng (char *d, int i)
+{
+  char a[2] = "0";
+
+  if (i < 2)
+    i = 2;
+
+  __builtin_strcpy (d, a + i);    // { dg-warning "\\\[-W(array-bounds|stringop-overflow)" }
+}
+
+/* { dg-prune-output "-Wuninitialized" } */
diff --git a/gcc/testsuite/gcc.dg/attr-access-none.c b/gcc/testsuite/gcc.dg/attr-access-none.c
new file mode 100644
index 00000000000..d62d3107309
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/attr-access-none.c
@@ -0,0 +1,38 @@ 
+/* Test to verify the handling of attribute access (none).
+   { dg-do compile }
+   { dg-options "-O -Wall -ftrack-macro-expansion=0" } */
+
+int __attribute__ ((access (none, 1)))
+fnone_pv1 (void*);
+
+void nowarn_fnone_pv1 (void)
+{
+  int x;
+  fnone_pv1 (&x);
+}
+
+
+int __attribute__ ((access (none, 1)))
+fnone_pcv1 (const void*);
+
+void nowarn_fnone_pcv1 (void)
+{
+  char a[2];
+  fnone_pcv1 (a);
+}
+
+
+int __attribute__ ((access (none, 1, 2)))
+fnone_pcv1_2 (const void*, int);  // { dg-message "in a call to function 'fnone_pcv1_2' declared with attribute 'none \\\(1, 2)'" }
+
+void nowarn_fnone_pcv1_2 (void)
+{
+  char a[2];
+  fnone_pcv1_2 (a, 2);
+}
+
+void warn_fnone_pcv1_2 (void)
+{
+  char a[3];
+  fnone_pcv1_2 (a, 4);        // { dg-warning "expecting 4 bytes in a region of size 3" }
+}
diff --git a/gcc/testsuite/gcc.dg/pr71581.c b/gcc/testsuite/gcc.dg/pr71581.c
index d82eb1ed5c9..dd71dde9eac 100644
--- a/gcc/testsuite/gcc.dg/pr71581.c
+++ b/gcc/testsuite/gcc.dg/pr71581.c
@@ -6,19 +6,19 @@  _Complex float
 f1 (void)
 {
   float x;
-  return x;	/* { dg-warning "is used uninitialized in this function" } */
+  return x;	/* { dg-warning "is used uninitialized" } */
 }
 
 _Complex double
 f2 (void)
 {
   double x;
-  return x;	/* { dg-warning "is used uninitialized in this function" } */
+  return x;	/* { dg-warning "is used uninitialized" } */
 }
 
 _Complex int
 f3 (void)
 {
   int x;
-  return x;	/* { dg-warning "is used uninitialized in this function" } */
+  return x;	/* { dg-warning "is used uninitialized" } */
 }
diff --git a/gcc/testsuite/gcc.dg/uninit-15.c b/gcc/testsuite/gcc.dg/uninit-15.c
index 67aac41d49e..8ee10c27aba 100644
--- a/gcc/testsuite/gcc.dg/uninit-15.c
+++ b/gcc/testsuite/gcc.dg/uninit-15.c
@@ -10,7 +10,7 @@ 
 inline int
 foo (int i)
 {
-  if (i) /* { dg-warning "used uninitialized in this function" "" } */
+  if (i) /* { dg-warning "used uninitialized" } */
     return 1;
   return 0;
 }
diff --git a/gcc/testsuite/gcc.dg/uninit-32.c b/gcc/testsuite/gcc.dg/uninit-32.c
new file mode 100644
index 00000000000..cdc05128dfd
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/uninit-32.c
@@ -0,0 +1,312 @@ 
+/* PR middle-end/10138 - warn for uninitialized arrays passed as const*
+   arguments
+   { dg-do compile }
+   { dg-options "-O -Wall" } */
+
+typedef __SIZE_TYPE__ size_t;
+
+void* alloca (size_t);
+void* malloc (size_t);
+void* realloc (void*, size_t);
+
+void fpi (int*);
+void fpci (const int*);
+void fpcv (const void*);
+
+
+void nowarn_scalar_fpi (void)
+{
+  int x;
+  fpi (&x);
+}
+
+void nowarn_scalar_plus_cst_fpi (void)
+{
+  int x;
+  // This deserves a warning other than -Wuninitialized.
+  fpi (&x + 1);
+}
+
+void nowarn_scalar_plus_var_fpi (int i)
+{
+  int x;
+  // Same as above, this deserves a warning other than -Wuninitialized.
+  fpi (&x + i);
+}
+
+void nowarn_array_assign_fpci (void)
+{
+  int a[2];
+  a[0] = 0;
+  fpci (a);
+}
+
+void nowarn_array_assign_plus_cst_fpci (void)
+{
+  int a[4];
+  a[1] = 0;
+  a[2] = 1;
+  fpci (a + 1);
+}
+
+void nowarn_array_init_fpci (void)
+{
+  int a[4] = { 0 };
+  fpci (a);
+}
+
+void nowarn_array_compound_fpi (void)
+{
+  fpi ((int[2]){ 1 });
+}
+
+void nowarn_array_compound_fpci (void)
+{
+  fpci ((int[3]){ 1 });
+}
+
+void warn_array_fpci (void)
+{
+  int a[4];                   // { dg-message "declared here" }"
+  fpci (a);                   // { dg-warning "\\\[-Wmaybe-uninitialized" }
+}
+
+void warn_array_plus_cst_fpci (void)
+{
+  int a[4];
+  fpci (a + 1);               // { dg-warning "\\\[-Wmaybe-uninitialized" }
+}
+
+void warn_array_plus_var_fpci (int i)
+{
+  int a[4];
+  fpci (a + i);               // { dg-warning "\\\[-Wmaybe-uninitialized" }
+}
+
+void nowarn_array_end_fpci (void)
+{
+  int a[4];
+  /* This should be diagnosed by a warning other than -Wuninitialized
+     because the just-past-the-end pointer cannot be dereferenced and
+     the function doesn't take any other pointer to tell where the start
+     of the array is.  -Wuninitialized isn't appropriate because there
+     is nothing to initialize at that offset.  */
+  fpci (a + 4);
+}
+
+void warn_matrix_fpcv (void)
+{
+  int a[2][2];
+  fpci (a[1]);                // { dg-warning "\\\[-Wmaybe-uninitialized" }
+}
+
+void warn_scalar_fpcv (void)
+{
+  int i;
+  fpci (&i);                  // { dg-warning "\\\[-Wmaybe-uninitialized" }
+}
+
+void warn_scalar_plus_cst_fpcv (void)
+{
+  int x;
+  /* Same as above, this deserves a warning other than -Wuninitialized
+     for passing the function a past-the-end pointer with no other
+     argument.  */
+  fpci (&x + 1);
+}
+
+void warn_scalar_plus_var_fpcv (int i)
+{
+  int x;
+  fpci (&x + i);              // { dg-warning "\\\[-Wmaybe-uninitialized" }
+}
+
+void nowarn_struct_assign_fpci (void)
+{
+  struct { int a, b; } s;
+  s.a = 0;
+  fpci (&s.a);
+}
+
+void warn_struct_assign_fpci (void)
+{
+  struct { int a, b; } s;
+  s.a = 0;
+  fpci (&s.b);                // { dg-warning "\\\[-Wmaybe-uninitialized" }
+}
+
+void nowarn_struct_init_fpci (void)
+{
+  struct { int a, b; } s = { 0 };
+  fpci (&s.a);
+  fpci (&s.b);
+}
+
+void nowarn_struct_compound_fpci (void)
+{
+  struct S { int a, b; };
+  fpci (&(struct S){ }.a);
+  fpci (&(struct S){ }.b);
+}
+
+/* Verify that passing a just-past-the-end pointer to a const pointer
+   argument to a function that takes another argument is not diagnosed
+   since the two arguments together could outline a range.  */
+void nowarn_fp_p (void)
+{
+  extern void fpi_pci (int*, const int*);
+
+  {
+    int i;
+    fpi_pci (&i, &i + 1);
+  }
+  {
+    int j;
+    fpi_pci (&j + 1, &j + 1);
+  }
+
+  extern void fpc_pcc (char*, const char*);
+
+  {
+    char a[2];
+    fpc_pcc (a, a + 2);
+  }
+  {
+    char a[3];
+    fpc_pcc (a, a + 3);
+  }
+
+  extern void fpcc_pcc (const char*, const char*);
+
+  {
+    char a[4];
+    fpcc_pcc (a + 4, a + 4);
+  }
+}
+
+
+/* Verify passing addresses of empty uninitialized objects doesn't
+   trigger a warning.  */
+void nowarn_fpcEmpty (void)
+{
+  struct Empty { };
+  extern void fpcEmpty (const struct Empty*);
+
+  /* Since Empty has no members warning for it isn't really necessary.
+     See also PR 38908.  */
+  struct Empty s;
+  fpcEmpty (&s);
+}
+
+
+/* Verify passing addresses of uninitialized objects to functions
+   declared without a proptotype doesn't trigger a warning.  */
+void nowarn_noproto (void)
+{
+  extern void fnoproto ();
+  int i, a[2];
+
+  fnoproto (&i, a, a + 2);
+}
+
+
+/* Verify passing addresses of uninitialized objects to variadic
+   functions doesn't trigger a warning.  */
+void nowarn_vararg (void)
+{
+  extern void fvararg (int, ...);
+
+  int i, a[2];
+
+  fvararg (0, &i, a, a + 2);
+}
+
+
+void nowarn_alloca_assign_fpci (unsigned n)
+{
+  int *p = (int*)alloca (n);
+  p[0] = 0;
+  fpci (p);
+}
+
+void nowarn_alloca_assign_plus_cst_fpci (unsigned n)
+{
+  int *p = (int*)alloca (n);
+  p[1] = 0;
+  p[2] = 1;
+  fpci (p + 1);
+}
+
+void warn_alloca_fpci (unsigned n)
+{
+  int *p = (int*)alloca (n);
+  fpci (p);                   // { dg-warning "\\\[-Wmaybe-uninitialized" }
+}
+
+void warn_alloca_assign_plus_cst_fpci (unsigned n)
+{
+  int *p = (int*)alloca (n);
+  p[1] = 0;
+  p[2] = 1;
+  fpci (p + 3);               // { dg-warning "\\\[-Wmaybe-uninitialized" }
+}
+
+
+void nowarn_vla_assign_fpci (unsigned n)
+{
+  int a[n];
+  a[0] = 0;
+  fpci (a);
+}
+
+void nowarn_vla_assign_plus_cst_fpci (unsigned n)
+{
+  int vla[n];
+  vla[1] = 0;
+  vla[2] = 1;
+  fpci (vla + 1);
+}
+
+void warn_vla_fpci (unsigned n)
+{
+  int vla[n];                 // { dg-message "declared here" "pr?????" { xfail *-*-* } }"
+  fpci (vla);                 // { dg-warning "\\\[-Wmaybe-uninitialized" }
+}
+
+void warn_vla_assign_plus_cst_fpci (unsigned n)
+{
+  int vla[n];                 // { dg-message "declared here" "pr?????" { xfail *-*-* } }"
+  vla[1] = 0;
+  vla[2] = 1;
+  fpci (vla + 3);             // { dg-warning "\\\[-Wmaybe-uninitialized" }
+}
+
+
+void nowarn_malloc_assign_fpci (unsigned n)
+{
+  int *p = (int*)malloc (n);
+  p[0] = 0;
+  fpci (p);
+}
+
+void nowarn_malloc_assign_plus_cst_fpci (unsigned n)
+{
+  int *p = (int*)malloc (n);
+  p[1] = 0;
+  p[2] = 1;
+  fpci (p + 1);
+}
+
+void warn_malloc_fpci (unsigned n)
+{
+  int *p = (int*)malloc (n);
+  fpci (p);                   // { dg-warning "\\\[-Wmaybe-uninitialized" }
+}
+
+void warn_malloc_assign_plus_cst_fpci (unsigned n)
+{
+  int *p = (int*)malloc (n);  // { dg-message "allocated here" "pr?????" { xfail *-*-* } }"
+  p[1] = 0;
+  p[2] = 1;
+  fpci (p + 3);               // { dg-warning "\\\[-Wmaybe-uninitialized" }
+}
diff --git a/gcc/testsuite/gcc.dg/uninit-33.c b/gcc/testsuite/gcc.dg/uninit-33.c
new file mode 100644
index 00000000000..a45f18dd6ce
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/uninit-33.c
@@ -0,0 +1,145 @@ 
+/* PR middle-end/10138 - warn for uninitialized arrays passed as const*
+   arguments
+   Verify that passing pointers to uninitialized objects to arguments
+   to functions declared with attribute access is diagnosed where expected.
+   { dg-do compile }
+   { dg-options "-O -Wall" } */
+
+#define RO(...) __attribute__ ((access (read_only, __VA_ARGS__)))
+#define RW(...) __attribute__ ((access (read_write, __VA_ARGS__)))
+#define WO(...) __attribute__ ((access (write_only, __VA_ARGS__)))
+
+RO (1) void fpri (int*);      // { dg-message "in a call to 'fpri' declared with attribute 'access \\\(read_only, 1\\\)' here" }
+
+RO (1) void fpcri (const int*);
+
+RO (1, 2) void fpcri1_2 (const int*, int);
+
+
+void warn_scalar_fpri (void)
+{
+  int i;                      // { dg-message "declared here" }
+  fpri (&i);                  // { dg-warning "'i' is used uninitialized" }
+}
+
+void nowarn_scalar_plus_fpri (void)
+{
+  int i;
+  /* This gets a -Wstringop-overflow for reading past the end but not
+     -Wuninitialized because there's nothing to initialize there.  */
+  fpri (&i + 1);              // { dg-warning "\\\[-Wstringop-overflow" }
+}
+
+void nowarn_array_assign_fpcri (void)
+{
+  int a[2];
+  a[0] = 0;
+  fpcri (a);
+}
+
+void nowarn_array_init_fpcri (void)
+{
+  int a[4] = { 0 };
+  fpcri (a);
+}
+
+void nowarn_array_compound_fpri (void)
+{
+  fpri ((int[2]){ 0 });
+}
+
+void nowarn_array_compound_fpcri (void)
+{
+  fpcri ((int[3]){ 1 });
+}
+
+void warn_scalar_fpcri (void)
+{
+  int i;
+  fpcri (&i);                 // { dg-warning "\\\[-Wuninitialized" }
+}
+
+void warn_array_fpcri (void)
+{
+  int a[4];
+  fpcri (a);                  // { dg-warning "\\\[-Wuninitialized" }
+}
+
+void warn_array_plus_cst_fpcri (void)
+{
+  int a[4];
+  fpcri (a + 1);              // { dg-warning "\\\[-Wuninitialized" }
+}
+
+void warn_array_plus_var_fpcri (int i)
+{
+  int a[4];
+  fpcri (a + i);              // { dg-warning "\\\[-Wuninitialized" }
+}
+
+void nowarn_struct_assign_fpcri (void)
+{
+  struct { int a, b; } s;
+  s.a = 0;
+  fpcri (&s.a);
+}
+
+void warn_struct_assign_fpcri (void)
+{
+  struct { int a, b; } s;
+  s.a = 0;
+  fpcri (&s.b);               // { dg-warning "\\\[-Wuninitialized" }
+}
+
+void nowarn_struct_init_fpcri (void)
+{
+  struct { int a, b; } s = { 0 };
+  fpcri (&s.a);
+  fpcri (&s.b);
+}
+
+void nowarn_struct_compound_fpcri (void)
+{
+  struct S { int a, b; };
+  fpcri (&(struct S){ }.a);
+  fpcri (&(struct S){ }.b);
+}
+
+
+void nowarn_scalar_fpcri1_2 (void)
+{
+  int i;
+  fpcri1_2 (&i, 0);
+}
+
+void nowarn_array_assign_fpcri1_2 (void)
+{
+  int a[2];
+  a[0] = 0;
+  fpcri1_2 (a, 1);
+}
+
+void nowarn_array_assign_fpcri1_2_plus_cst (void)
+{
+  int a[3];
+  a[1] = 0;
+  fpcri1_2 (a + 1, 1);
+}
+
+void nowarn_array_init_fpcri1_2 (void)
+{
+  int a[4] = { 0 };
+  fpcri1_2 (a, 2);
+}
+
+void warn_array_fpcri1_2_rd1 (void)
+{
+  int a[4];
+  fpcri1_2 (a, 1);            // { dg-warning "\\\[-Wuninitialized" }
+}
+
+void warn_array_fpcri1_2_rd2 (void)
+{
+  int a[4];
+  fpcri1_2 (a, 2);            // { dg-warning "\\\[-Wuninitialized" }
+}
diff --git a/gcc/testsuite/gcc.dg/uninit-34.c b/gcc/testsuite/gcc.dg/uninit-34.c
new file mode 100644
index 00000000000..9de618347eb
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/uninit-34.c
@@ -0,0 +1,58 @@ 
+/* PR middle-end/10138 - warn for uninitialized arrays passed as const*
+   arguments
+   Verify that passing pointers to uninitialized objects to arguments
+   to functions declared with attribute access is diagnosed where expected.
+   { dg-do compile }
+   { dg-options "-O -Wall" } */
+
+#define RW(...) __attribute__ ((access (read_write, __VA_ARGS__)))
+
+RW (1) RW (3) void
+f4pi (int*, int*, int*, int*);    // { dg-message "in a call to 'f4pi' declared with attribute 'access \\\(read_write, \[13\]\\\)'" }
+
+
+void nowarn_scalar (void)
+{
+  int i1 = 0, i2, i3 = 1, i4;
+  f4pi (&i1, &i2, &i3, &i4);
+}
+
+void warn_scalar_1 (void)
+{
+  int i1;                         // { dg-message "declared here" }
+  int i2, i3 = 1, i4;
+
+  f4pi (&i1, &i2, &i3, &i4);      // { dg-warning "'i1' may be used uninitialized" }
+}
+
+void warn_scalar_2 (void)
+{
+  int j1 = 0, j2, j4;
+  int j3;
+
+  f4pi (&j1, &j2, &j3, &j4);      // { dg-warning "'j3' may be used uninitialized" }
+}
+
+
+void nowarn_array_init (void)
+{
+  int a1[4] = { 0 }, a2[5], a3[6] = { 0 }, a4[7];
+
+  f4pi (a1, a2, a3, a4);
+}
+
+void warn_array_1 (void)
+{
+  int a1[4];                  // { dg-message "'a1' declared here" }
+  int a2[5], a3[6] = { 0 }, a4[7];
+
+  f4pi (a1, a2, a3, a4);      // { dg-warning "'a1' may be used uninitialized" }
+}
+
+void warn_array_2 (void)
+{
+  int a1[4] = { 0 }, a2[5], a4[7];
+  int a3[6];                  // { dg-message "'a3' declared here" }
+
+  f4pi (a1, a2, a3, a4);      // { dg-warning "'a3' may be used uninitialized" }
+}
diff --git a/gcc/testsuite/gcc.dg/uninit-36.c b/gcc/testsuite/gcc.dg/uninit-36.c
new file mode 100644
index 00000000000..9524e7ad1b9
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/uninit-36.c
@@ -0,0 +1,237 @@ 
+/* PR middle-end/10138 - warn for uninitialized arrays passed as const*
+   arguments
+   Verify that passing pointers to uninitialized objects to const
+   arguments to built-ins is diagnosed where expected.
+   { dg-do compile }
+   { dg-options "-O -Wall" } */
+
+typedef __SIZE_TYPE__ size_t;
+
+void* alloca (size_t);
+void* malloc (size_t);
+void* realloc (void*, size_t);
+
+void* memcpy (void*, const void*, size_t);
+char* strcpy (char*, const char*);
+size_t strlen (const char*);
+
+void sink (void*);
+
+void nowarn_array_memcpy (void *d, unsigned n)
+{
+  int a[2];
+  /* Diagnose this?  */
+  memcpy (d, a, n /* Non-constant to avoid folding into MEM_REF.  */);
+}
+
+void nowarn_array_plus_cst_memcpy (void *d, unsigned n)
+{
+  int a[3];
+  /* Diagnose this?  */
+  memcpy (d, a + 1, n);
+}
+
+void nowarn_array_plus_var_memcpy (void *d, unsigned n, int i)
+{
+  int a[4];
+  /* Diagnose this?  */
+  memcpy (d, a + i, n);
+}
+
+void nowarn_array_assign_memcpy (char *d, unsigned n)
+{
+  int a[3];
+  a[1] = 3;
+  memcpy (d, a, n);
+}
+
+void nowarn_array_init_memcpy (char *d, unsigned n)
+{
+  int a[4] = { 0 };
+  memcpy (d, a, n);
+}
+
+void nowarn_array_compound_memcpy (void *d, unsigned n)
+{
+  memcpy (d, (int[2]){ 0 }, n);
+}
+
+void nowarn_struct_assign_memcpy (void *d, unsigned n)
+{
+  struct S { int a, b, c, d; } s;
+  s.b = 1;
+  s.d = 2;
+  memcpy (d, &s, n);
+}
+
+
+void nowarn_array_init_strcpy (char *d[], unsigned n)
+{
+  char a[8] = "012";
+
+  strcpy (d[0], a);
+  strcpy (d[1], a + 1);
+  strcpy (d[1], a + 2);
+  strcpy (d[1], a + 3);
+  strcpy (d[1], a + 4);
+  strcpy (d[1], a + 5);
+  strcpy (d[1], a + 6);
+  strcpy (d[1], a + 7);
+}
+
+
+void nowarn_array_assign_strcpy (char *d[], unsigned n)
+{
+  char a[8];
+  a[0] = '0';
+  a[1] = '1';
+  a[2] = '2';
+  a[3] = '\0';
+
+  strcpy (d[0], a);
+  strcpy (d[1], a + 1);
+  strcpy (d[1], a + 2);
+  strcpy (d[1], a + 3);
+}
+
+void warn_array_plus_cst_strcpy (char *d, unsigned n)
+{
+  char a[8];
+  a[0] = '1';
+  a[1] = '2';
+  a[2] = '3';
+  a[3] = '\0';
+
+  strcpy (d, a + 4);          // { dg-warning "\\\[-Wuninitialized" }
+  strcpy (d, a + 5);          // { dg-warning "\\\[-Wuninitialized" }
+  strcpy (d, a + 6);          // { dg-warning "\\\[-Wuninitialized" }
+  strcpy (d, a + 7);          // { dg-warning "\\\[-Wuninitialized" }
+}
+
+void nowarn_array_plus_var_strcpy (char *d, int i)
+{
+  char a[8];
+  a[0] = '1';
+  a[1] = '2';
+  a[2] = '3';
+  a[3] = '\0';
+
+  strcpy (d, a + i);
+}
+
+
+size_t nowarn_array_assign_strlen (const char *s)
+{
+  char a[8];
+  a[0] = s[0];
+  a[1] = s[1];
+  a[2] = s[2];
+  a[3] = s[3];
+
+  size_t n = 0;
+
+  n += strlen (a);
+  n += strlen (a + 1);
+  n += strlen (a + 2);
+  n += strlen (a + 3);
+  return n;
+}
+
+size_t warn_array_plus_cst_strlen (const char *s)
+{
+  char a[8];
+  a[0] = s[0];
+  a[1] = s[1];
+  a[2] = s[2];
+  a[3] = s[3];
+
+  return strlen (a + 4);      // { dg-warning "\\\[-Wuninitialized" }
+}
+
+size_t nowarn_array_plus_var_strlen (const char *s, int i)
+{
+  char a[8];
+  a[0] = s[0];
+  a[1] = s[1];
+  a[2] = s[2];
+  a[3] = s[3];
+
+  return strlen (a + i);
+}
+
+
+size_t nowarn_alloca_assign_strlen (int i)
+{
+  char *p = (char*)alloca (8);
+  p[i] = '\0';
+  return strlen (p);
+}
+
+size_t nowarn_alloca_escape_strlen (int i)
+{
+  char *p = (char*)alloca (8);
+  sink (p);
+  return strlen (p);
+}
+
+size_t warn_alloca_strlen (void)
+{
+  char *p = (char*)alloca (8);
+  return strlen (p);          // { dg-warning "\\\[-Wuninitialized" }
+}
+
+
+size_t nowarn_malloc_assign_strlen (int i)
+{
+  char *p = (char*)malloc (8);
+  p[i] = '\0';
+  return strlen (p);
+}
+
+size_t nowarn_malloc_escape_strlen (int i)
+{
+  char *p = (char*)malloc (8);
+  sink (p);
+  return strlen (p);
+}
+
+size_t warn_malloc_strlen (void)
+{
+  char *p = (char*)malloc (8);
+  return strlen (p);          // { dg-warning "\\\[-Wuninitialized" }
+}
+
+
+size_t nowarn_realloc_strlen (void *p)
+{
+  char *q = (char*)realloc (p, 8);
+  return strlen (q);
+}
+
+
+size_t nowarn_vla_assign_strlen (int n, int i)
+{
+  char vla[n];
+  vla[i] = '\0';
+  return strlen (vla);
+}
+
+size_t nowarn_vla_strcpy_strlen (int n, const char *s, int i)
+{
+  char vla[n];
+  strcpy (vla, s);
+  return strlen (vla + i);
+}
+
+size_t nowarn_vla_escape_strlen (int n, int i)
+{
+  char vla[n];
+  sink (vla);
+  return strlen (vla);
+}
+
+size_t warn_vla_strlen (unsigned n)
+{
+  char vla[n];
+  return strlen (vla);        // { dg-warning "\\\[-Wuninitialized" }
+}
diff --git a/gcc/testsuite/gcc.dg/uninit-B-O0.c b/gcc/testsuite/gcc.dg/uninit-B-O0.c
index 5557ace6f8d..b01e4fa945a 100644
--- a/gcc/testsuite/gcc.dg/uninit-B-O0.c
+++ b/gcc/testsuite/gcc.dg/uninit-B-O0.c
@@ -9,7 +9,7 @@  void
 baz (void)
 {
   int i;
-  if (i) /* { dg-warning "'i' is used uninitialized in this function" } */
+  if (i) /* { dg-warning "'i' is used uninitialized" } */
     bar (i);
   foo (&i);
 }
diff --git a/gcc/testsuite/gcc.dg/uninit-I-O0.c b/gcc/testsuite/gcc.dg/uninit-I-O0.c
index 761f65b485b..e4b68ba47b1 100644
--- a/gcc/testsuite/gcc.dg/uninit-I-O0.c
+++ b/gcc/testsuite/gcc.dg/uninit-I-O0.c
@@ -4,5 +4,5 @@ 
 int sys_msgctl (void)
 {
   struct { int mode; } setbuf;
-  return setbuf.mode; /* { dg-warning "'setbuf\.mode' is used uninitialized in this function" } */
+  return setbuf.mode; /* { dg-warning "'setbuf\.mode' is used uninitialized" } */
 }
diff --git a/gcc/testsuite/gcc.dg/uninit-pr19430-O0.c b/gcc/testsuite/gcc.dg/uninit-pr19430-O0.c
index 832aeb30bec..e9e264deb5a 100644
--- a/gcc/testsuite/gcc.dg/uninit-pr19430-O0.c
+++ b/gcc/testsuite/gcc.dg/uninit-pr19430-O0.c
@@ -6,7 +6,7 @@  extern void baz (int *);
 int
 foo (int i)
 {
-  int j; /* { dg-warning "'j' may be used uninitialized in this function" "uninitialized" { xfail *-*-* } } */
+  int j; /* { dg-warning "'j' may be used uninitialized" "uninitialized" { xfail *-*-* } } */
 
   if (bar (i)) {
     baz (&j);
@@ -18,7 +18,7 @@  foo (int i)
 
 int foo2( void ) {
   int rc;
-  return rc; /* { dg-warning "'rc' is used uninitialized in this function" } */
+  return rc; /* { dg-warning "'rc' is used uninitialized" } */
   *&rc = 0;
 }
 
@@ -27,16 +27,16 @@  void frob(int *pi);
 
 int main(void)
 {
-  int i; 
-  printf("i = %d\n", i); /* { dg-warning "'i' is used uninitialized in this function" } */
+  int i;
+  printf("i = %d\n", i); /* { dg-warning "'i' is used uninitialized" } */
   frob(&i);
 
   return 0;
 }
 
 void foo3(int*);
-void bar3(void) { 
-  int x; 
-  if(x) /* { dg-warning "'x' is used uninitialized in this function" } */
-    foo3(&x); 
+void bar3(void) {
+  int x;
+  if(x) /* { dg-warning "'x' is used uninitialized" } */
+    foo3(&x);
 }
diff --git a/gcc/testsuite/gcc.dg/uninit-pr19430.c b/gcc/testsuite/gcc.dg/uninit-pr19430.c
index e00f3138045..417cdc63314 100644
--- a/gcc/testsuite/gcc.dg/uninit-pr19430.c
+++ b/gcc/testsuite/gcc.dg/uninit-pr19430.c
@@ -5,9 +5,9 @@  extern void baz (int *);
 int
 foo (int i)
 {
-  int j; /* { dg-warning "'j' may be used uninitialized in this function" "uninitialized" { xfail *-*-* } } */
+  int j; /* { dg-warning "'j' may be used uninitialized" "uninitialized" { xfail *-*-* } } */
 
-  if (bar (i)) { 
+  if (bar (i)) {
     baz (&j);
   } else {
   }
@@ -19,7 +19,7 @@  foo (int i)
 
 int foo2( void ) {
   int rc;
-  return rc;  /* { dg-warning "'rc' is used uninitialized in this function" } */
+  return rc;  /* { dg-warning "'rc' is used uninitialized" } */
   *&rc = 0;
 }
 
@@ -28,16 +28,16 @@  void frob(int *pi);
 
 int main(void)
 {
-  int i; 
-  printf("i = %d\n", i); /* { dg-warning "'i' is used uninitialized in this function" } */
+  int i;
+  printf("i = %d\n", i); /* { dg-warning "'i' is used uninitialized" } */
   frob(&i);
 
   return 0;
 }
 
 void foo3(int*);
-void bar3(void) { 
-  int x; 
-  if(x) /* { dg-warning "'x' is used uninitialized in this function" "uninitialized" } */
-    foo3(&x); 
+void bar3(void) {
+  int x;
+  if(x) /* { dg-warning "'x' is used uninitialized" "uninitialized" } */
+    foo3(&x);
 }
diff --git a/gcc/testsuite/gcc.dg/uninit-pr95136.c b/gcc/testsuite/gcc.dg/uninit-pr95136.c
new file mode 100644
index 00000000000..47d16c5bbba
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/uninit-pr95136.c
@@ -0,0 +1,63 @@ 
+/* PR middle-end/95136 - missing -Wuninitialized on an array access with
+   a variable offset
+   { dg-do compile }
+   { dg-options "-O -Wall" } */
+
+#define NOIPA __attribute__ ((noipa))
+
+NOIPA int a1_addr_varidx_plus_cst (int i)
+{
+  int a[4];              // { dg-message "'a' declared here" }
+  int *p = &a[i + 1];
+  return *p;             // { dg-warning "'a|a\\\[<unknown>]' is used uninitialized" }
+}
+
+NOIPA int a1_plus_addr_varidx_cst (int i)
+{
+  int a[4];              // { dg-message "'a' declared here" }
+  int *p = &a[i] + 1;
+  return *p;             // { dg-warning "'a' is used uninitialized" }
+}
+
+NOIPA int a1_plus_addr_cstidx_var (int i)
+{
+  int a[4];              // { dg-message "'a' declared here" }
+  int *p = &a[1] + i;
+  return *p;             // { dg-warning "'a' is used uninitialized" }
+}
+
+NOIPA int a1_plus_addr_varidx_var (int i, int j)
+{
+  int a[4];              // { dg-message "'a' declared here" }
+  int *p = &a[i] + j;
+  return *p;             // { dg-warning "'a' is used uninitialized" }
+}
+
+
+NOIPA int a2_addr_varidx_plus_cst (int i, int j)
+{
+  int a[4][4];           // { dg-message "'a' declared here" }
+  int *p = &a[i + 1][j + 1];
+  return *p;             // { dg-warning "'a|a\\\[<unknown>]\\\[<unknown>]' is used uninitialized" }
+}
+
+NOIPA int a2_plus_addr_varidx_cst (int i, int j)
+{
+  int a[4][4];           // { dg-message "'a' declared here" }
+  int *p = &a[i][j] + 1;
+  return *p;             // { dg-warning "'a' is used uninitialized" }
+}
+
+NOIPA int a2_plus_addr_cstidx_var (int i)
+{
+  int a[4][4];           // { dg-message "'a' declared here" }
+  int *p = &a[1][1] + i;
+  return *p;             // { dg-warning "'a' is used uninitialized" }
+}
+
+NOIPA int a2_plus_addr_varidx_var (int i, int j, int k)
+{
+  int a[4][4];           // { dg-message "'a' declared here" }
+  int *p = &a[i][j] + k;
+  return *p;             // { dg-warning "'a' is used uninitialized" }
+}
diff --git a/gcc/tree-ssa-uninit.c b/gcc/tree-ssa-uninit.c
index cc785bd9d8c..d03507202f2 100644
--- a/gcc/tree-ssa-uninit.c
+++ b/gcc/tree-ssa-uninit.c
@@ -33,6 +33,9 @@  along with GCC; see the file COPYING3.  If not see
 #include "tree-ssa.h"
 #include "tree-cfg.h"
 #include "cfghooks.h"
+#include "attribs.h"
+#include "builtins.h"
+#include "calls.h"
 
 /* This implements the pass that does predicate aware warning on uses of
    possibly uninitialized variables.  The pass first collects the set of
@@ -217,19 +220,372 @@  check_defs (ao_ref *ref, tree vdef, void *data_)
   return true;
 }
 
+/* Counters and limits controlling the the depth of analysis and
+   strictness of the warning.  */
+struct wlimits
+{
+  /* Number of VDEFs encountered.  */
+  unsigned int vdef_cnt;
+  /* Number of statements examined by walk_aliased_vdefs.  */
+  unsigned int oracle_cnt;
+  /* Limit on the number of statements visited by walk_aliased_vdefs.  */
+  unsigned limit;
+  /* Set when basic block with statement is executed unconditionally.  */
+  bool always_executed;
+  /* Set to issue -Wmaybe-uninitialized.  */
+  bool wmaybe_uninit;
+};
+
+/* Determine if REF references an uninitialized operand and diagnose
+   it if so.  */
+
+static tree
+maybe_warn_operand (ao_ref &ref, gimple *stmt, tree lhs, tree rhs,
+		    wlimits &wlims)
+{
+  bool has_bit_insert = false;
+  use_operand_p luse_p;
+  imm_use_iterator liter;
+
+  if (TREE_NO_WARNING (rhs))
+    return NULL_TREE;
+
+  /* Do not warn if the base was marked so or this is a
+     hard register var.  */
+  tree base = ao_ref_base (&ref);
+  if ((VAR_P (base)
+       && DECL_HARD_REGISTER (base))
+      || TREE_NO_WARNING (base))
+    return NULL_TREE;
+
+  /* Do not warn if the access is fully outside of the variable.  */
+  poly_int64 decl_size;
+  if (DECL_P (base)
+      && ((known_size_p (ref.size)
+	   && known_eq (ref.max_size, ref.size)
+	   && known_le (ref.offset + ref.size, 0))
+	  || (known_ge (ref.offset, 0)
+	      && DECL_SIZE (base)
+	      && poly_int_tree_p (DECL_SIZE (base), &decl_size)
+	      && known_le (decl_size, ref.offset))))
+    return NULL_TREE;
+
+  /* Do not warn if the result of the access is then used for
+     a BIT_INSERT_EXPR. */
+  if (lhs && TREE_CODE (lhs) == SSA_NAME)
+    FOR_EACH_IMM_USE_FAST (luse_p, liter, lhs)
+      {
+	gimple *use_stmt = USE_STMT (luse_p);
+	/* BIT_INSERT_EXPR first operand should not be considered
+	   a use for the purpose of uninit warnings.  */
+	if (gassign *ass = dyn_cast <gassign *> (use_stmt))
+	  {
+	    if (gimple_assign_rhs_code (ass) == BIT_INSERT_EXPR
+		&& luse_p->use == gimple_assign_rhs1_ptr (ass))
+	      {
+		has_bit_insert = true;
+		break;
+	      }
+	  }
+      }
+
+  if (has_bit_insert)
+    return NULL_TREE;
+
+  /* Limit the walking to a constant number of stmts after
+     we overcommit quadratic behavior for small functions
+     and O(n) behavior.  */
+  if (wlims.oracle_cnt > 128 * 128
+      && wlims.oracle_cnt > wlims.vdef_cnt * 2)
+    wlims.limit = 32;
+
+  check_defs_data data;
+  bool fentry_reached = false;
+  data.found_may_defs = false;
+  tree use = gimple_vuse (stmt);
+  if (!use)
+    return NULL_TREE;
+  int res = walk_aliased_vdefs (&ref, use,
+				check_defs, &data, NULL,
+				&fentry_reached, wlims.limit);
+  if (res == -1)
+    {
+      wlims.oracle_cnt += wlims.limit;
+      return NULL_TREE;
+    }
+
+  wlims.oracle_cnt += res;
+  if (data.found_may_defs)
+    return NULL_TREE;
+
+  bool found_alloc = false;
+
+  if (fentry_reached)
+    {
+      if (TREE_CODE (base) == MEM_REF)
+	base = TREE_OPERAND (base, 0);
+
+      /* Follow the chain of SSA_NAME assignments looking for an alloca
+	 call (or VLA) or malloc/realloc, or for decls.  If any is found
+	 (and in the latter case, the operand is a local variable) issue
+	 a warning.  */
+      while (TREE_CODE (base) == SSA_NAME)
+	{
+	  gimple *def_stmt = SSA_NAME_DEF_STMT (base);
+
+	  if (is_gimple_call (def_stmt)
+	      && gimple_call_builtin_p (def_stmt))
+	    {
+	      /* Detect uses of uninitialized alloca/VLAs.  */
+	      tree fndecl = gimple_call_fndecl (def_stmt);
+	      const built_in_function fncode = DECL_FUNCTION_CODE (fndecl);
+	      if (fncode == BUILT_IN_ALLOCA
+		  || fncode  == BUILT_IN_ALLOCA_WITH_ALIGN
+		  || fncode  == BUILT_IN_MALLOC)
+		found_alloc = true;
+	      break;
+	    }
+
+	  if (!is_gimple_assign (def_stmt))
+	    break;
+
+	  tree_code code = gimple_assign_rhs_code (def_stmt);
+	  if (code != ADDR_EXPR && code != POINTER_PLUS_EXPR)
+	    break;
+
+	  base = gimple_assign_rhs1 (def_stmt);
+	  if (TREE_CODE (base) == ADDR_EXPR)
+	    base = TREE_OPERAND (base, 0);
+
+	  if (DECL_P (base)
+	      || TREE_CODE (base) == COMPONENT_REF)
+	    rhs = base;
+
+	  if (TREE_CODE (base) == MEM_REF)
+	    base = TREE_OPERAND (base, 0);
+
+	  if (tree ba = get_base_address (base))
+	    base = ba;
+	}
+
+      /* Replace the RHS expression with BASE so that it
+	 refers to it in the diagnostic (instead of to
+	 '<unknown>').  */
+      if (DECL_P (base)
+	  && EXPR_P (rhs)
+	  && TREE_CODE (rhs) != COMPONENT_REF)
+	rhs = base;
+    }
+
+  /* Do not warn if it can be initialized outside this function.
+     If we did not reach function entry then we found killing
+     clobbers on all paths to entry.  */
+  if (!found_alloc
+      && fentry_reached
+      /* ???  We'd like to use ref_may_alias_global_p but that
+	 excludes global readonly memory and thus we get bogus
+	 warnings from p = cond ? "a" : "b" for example.  */
+      && (!VAR_P (base)
+	  || is_global_var (base)))
+    return NULL_TREE;
+
+  /* Strip the address-of expression from arrays passed to functions. */
+  if (TREE_CODE (rhs) == ADDR_EXPR)
+    rhs = TREE_OPERAND (rhs, 0);
+
+  /* Check again since RHS may have changed above.  */
+  if (TREE_NO_WARNING (rhs))
+    return NULL_TREE;
+
+  /* Avoid warning about empty types such as structs with no members.
+     The first_field() test is important for C++ where the predicate
+     alone isn't always sufficient.  */
+  tree rhstype = TREE_TYPE (rhs);
+  if (TYPE_EMPTY_P (rhstype)
+      || (RECORD_OR_UNION_TYPE_P (rhstype)
+	  && !first_field (rhstype)))
+    return NULL_TREE;
+
+  bool warned = false;
+  /* We didn't find any may-defs so on all paths either
+     reached function entry or a killing clobber.  */
+  location_t location
+    = linemap_resolve_location (line_table, gimple_location (stmt),
+				LRK_SPELLING_LOCATION, NULL);
+  if (wlims.always_executed)
+    {
+      if (warning_at (location, OPT_Wuninitialized,
+		      "%G%qE is used uninitialized", stmt, rhs))
+	{
+	  /* ???  This is only effective for decls as in
+	     gcc.dg/uninit-B-O0.c.  Avoid doing this for maybe-uninit
+	     uses or accesses by functions as it may hide important
+	     locations.  */
+	  if (lhs)
+	    TREE_NO_WARNING (rhs) = 1;
+	  warned = true;
+	}
+    }
+  else if (wlims.wmaybe_uninit)
+    warned = warning_at (location, OPT_Wmaybe_uninitialized,
+			 "%G%qE may be used uninitialized", stmt, rhs);
+
+  return warned ? base : NULL_TREE;
+}
+
+
+/* Diagnose passing addresses of uninitialized objects to either const
+   pointer arguments to functions, or to functions declared with attribute
+   access implying read access to those objects.  */
+
+static void
+maybe_warn_pass_by_reference (gimple *stmt, wlimits &wlims)
+{
+  if (!wlims.wmaybe_uninit)
+    return;
+
+  unsigned nargs = gimple_call_num_args (stmt);
+  if (!nargs)
+    return;
+
+  tree fndecl = gimple_call_fndecl (stmt);
+  tree fntype = gimple_call_fntype (stmt);
+  if (!fntype)
+    return;
+
+  const built_in_function fncode
+    = (fndecl && gimple_call_builtin_p (stmt, BUILT_IN_NORMAL)
+       ? DECL_FUNCTION_CODE (fndecl) : (built_in_function)BUILT_IN_LAST);
+
+  if (fncode == BUILT_IN_MEMCPY || fncode == BUILT_IN_MEMMOVE)
+    /* Avoid diagnosing calls to raw memory functions (this is overly
+       permissive; consider tightening it up).  */
+    return;
+
+  /* Save the current warning setting and replace it either a "maybe"
+     when passing addresses of uninitialized variables to const-qualified
+     pointers or arguments declared with attribute read_write, or with
+     a "certain" when passing them to arguments declared with attribute
+     read_only.  */
+  const bool save_always_executed = wlims.always_executed;
+
+  /* Map of attribute access specifications for function arguments.  */
+  rdwr_map rdwr_idx;
+  init_attr_rdwr_indices (&rdwr_idx, fntype);
+
+  tree argtype;
+  unsigned argno = 0;
+  function_args_iterator it;
+
+  FOREACH_FUNCTION_ARGS (fntype, argtype, it)
+    {
+      ++argno;
+
+      if (!POINTER_TYPE_P (argtype))
+	continue;
+
+      tree access_size = NULL_TREE;
+      attr_access *access = rdwr_idx.get (argno - 1);
+      if (access)
+	{
+	  if (access->mode == attr_access::none
+	      || access->mode == attr_access::write_only)
+	    continue;
+	  if (save_always_executed && access->mode == attr_access::read_only)
+	    /* Attribute read_only arguments imply read access.  */
+	    wlims.always_executed = true;
+	  else
+	    /* Attribute read_write arguments are documented as requiring
+	       initialized objects but it's expected that aggregates may
+	       be only partially initialized regardless.  */
+	    wlims.always_executed = false;
+
+	  if (access->sizarg < nargs)
+	    access_size = gimple_call_arg (stmt, access->sizarg);
+	}
+      else if (!TYPE_READONLY (TREE_TYPE (argtype)))
+	continue;
+      else if (save_always_executed && fncode != BUILT_IN_LAST)
+	/* Const-qualified arguments to built-ins imply read access.  */
+	wlims.always_executed = true;
+      else
+	/* Const-qualified arguments to ordinary functions imply a likely
+	   (but not definitive) read access.  */
+	wlims.always_executed = false;
+
+      tree arg = gimple_call_arg (stmt, argno - 1);
+
+      ao_ref ref;
+      ao_ref_init_from_ptr_and_size (&ref, arg, access_size);
+      tree argbase = maybe_warn_operand (ref, stmt, NULL_TREE, arg, wlims);
+      if (!argbase)
+	continue;
+
+      if (access)
+	{
+	  const char* const mode = (access->mode == attr_access::read_only
+				    ? "read_only" : "read_write");
+	  char attrstr[80];
+	  int n = sprintf (attrstr, "access (%s, %u", mode, argno);
+	  if (access->sizarg < UINT_MAX)
+	    sprintf (attrstr + n, ", %u)", access->sizarg);
+	  else
+	    strcpy (attrstr + n, ")");
+
+	  if (fndecl)
+	    {
+	      location_t loc = DECL_SOURCE_LOCATION (fndecl);
+	      inform (loc, "in a call to %qD declared "
+		      "with attribute %<access (%s, %u)%> here",
+		      fndecl, mode, argno);
+	    }
+	  else
+	    {
+	      /* Handle calls through function pointers.  */
+	      location_t loc = gimple_location (stmt);
+	      inform (loc, "in a call to %qT declared with "
+		      "attribute %<access (%s, %u)%>",
+		      fntype, mode, argno);
+	    }
+	}
+      else if (fndecl)
+	{
+	  location_t loc = DECL_SOURCE_LOCATION (fndecl);
+	  inform (loc, "by argument %u of type %qT to %qD declared here",
+		  argno, argtype, fndecl);
+	}
+      else
+	{
+	  /* Handle calls through function pointers.  */
+	  location_t loc = gimple_location (stmt);
+	  inform (loc, "by argument %u of type %qT to %qT",
+		  argno, argtype, fntype);
+	}
+
+      if (DECL_P (argbase))
+	{
+	  location_t loc = DECL_SOURCE_LOCATION (argbase);
+	  inform (loc, "%qD declared here", argbase);
+	}
+    }
+
+  wlims.always_executed = save_always_executed;
+}
+
+
 static unsigned int
-warn_uninitialized_vars (bool warn_possibly_uninitialized)
+warn_uninitialized_vars (bool wmaybe_uninit)
 {
+  /* Counters and limits controlling the the depth of the warning.  */
+  wlimits wlims = { };
+  wlims.wmaybe_uninit = wmaybe_uninit;
+
   gimple_stmt_iterator gsi;
   basic_block bb;
-  unsigned int vdef_cnt = 0;
-  unsigned int oracle_cnt = 0;
-  unsigned limit = 0;
-
   FOR_EACH_BB_FN (bb, cfun)
     {
       basic_block succ = single_succ (ENTRY_BLOCK_PTR_FOR_FN (cfun));
-      bool always_executed = dominated_by_p (CDI_POST_DOMINATORS, succ, bb);
+      wlims.always_executed = dominated_by_p (CDI_POST_DOMINATORS, succ, bb);
       for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi))
 	{
 	  gimple *stmt = gsi_stmt (gsi);
@@ -253,131 +609,42 @@  warn_uninitialized_vars (bool warn_possibly_uninitialized)
 		    continue;
 		}
 	      use = USE_FROM_PTR (use_p);
-	      if (always_executed)
+	      if (wlims.always_executed)
 		warn_uninit (OPT_Wuninitialized, use, SSA_NAME_VAR (use),
 			     SSA_NAME_VAR (use),
-			     "%qD is used uninitialized in this function", stmt,
+			     "%qD is used uninitialized", stmt,
 			     UNKNOWN_LOCATION);
-	      else if (warn_possibly_uninitialized)
+	      else if (wmaybe_uninit)
 		warn_uninit (OPT_Wmaybe_uninitialized, use, SSA_NAME_VAR (use),
 			     SSA_NAME_VAR (use),
-			     "%qD may be used uninitialized in this function",
+			     "%qD may be used uninitialized",
 			     stmt, UNKNOWN_LOCATION);
 	    }
 
 	  /* For limiting the alias walk below we count all
 	     vdefs in the function.  */
 	  if (gimple_vdef (stmt))
-	    vdef_cnt++;
+	    wlims.vdef_cnt++;
 
-	  if (gimple_assign_load_p (stmt)
-	      && gimple_has_location (stmt))
+	  if (is_gimple_call (stmt))
+	    maybe_warn_pass_by_reference (stmt, wlims);
+	  else if (gimple_assign_load_p (stmt)
+		   && gimple_has_location (stmt))
 	    {
 	      tree rhs = gimple_assign_rhs1 (stmt);
 	      tree lhs = gimple_assign_lhs (stmt);
-	      bool has_bit_insert = false;
-	      use_operand_p luse_p;
-	      imm_use_iterator liter;
-
-	      if (TREE_NO_WARNING (rhs))
-		continue;
 
 	      ao_ref ref;
 	      ao_ref_init (&ref, rhs);
-
-	      /* Do not warn if the base was marked so or this is a
-	         hard register var.  */
-	      tree base = ao_ref_base (&ref);
-	      if ((VAR_P (base)
-		   && DECL_HARD_REGISTER (base))
-		  || TREE_NO_WARNING (base))
-		continue;
-
-	      /* Do not warn if the access is fully outside of the
-	         variable.  */
-	      poly_int64 decl_size;
-	      if (DECL_P (base)
-		  && known_size_p (ref.size)
-		  && ((known_eq (ref.max_size, ref.size)
-		       && known_le (ref.offset + ref.size, 0))
-		      || (known_ge (ref.offset, 0)
-			  && DECL_SIZE (base)
-			  && poly_int_tree_p (DECL_SIZE (base), &decl_size)
-			  && known_le (decl_size, ref.offset))))
-		continue;
-
-	      /* Do not warn if the access is then used for a BIT_INSERT_EXPR. */
-	      if (TREE_CODE (lhs) == SSA_NAME)
-	        FOR_EACH_IMM_USE_FAST (luse_p, liter, lhs)
-		  {
-		    gimple *use_stmt = USE_STMT (luse_p);
-                    /* BIT_INSERT_EXPR first operand should not be considered
-		       a use for the purpose of uninit warnings.  */
-		    if (gassign *ass = dyn_cast <gassign *> (use_stmt))
-		      {
-			if (gimple_assign_rhs_code (ass) == BIT_INSERT_EXPR
-			    && luse_p->use == gimple_assign_rhs1_ptr (ass))
-			  {
-			    has_bit_insert = true;
-			    break;
-			  }
-		      }
-		  }
-	      if (has_bit_insert)
-		continue;
-
-	      /* Limit the walking to a constant number of stmts after
-	         we overcommit quadratic behavior for small functions
-		 and O(n) behavior.  */
-	      if (oracle_cnt > 128 * 128
-		  && oracle_cnt > vdef_cnt * 2)
-		limit = 32;
-	      check_defs_data data;
-	      bool fentry_reached = false;
-	      data.found_may_defs = false;
-	      use = gimple_vuse (stmt);
-	      int res = walk_aliased_vdefs (&ref, use,
-					    check_defs, &data, NULL,
-					    &fentry_reached, limit);
-	      if (res == -1)
-		{
-		  oracle_cnt += limit;
-		  continue;
-		}
-	      oracle_cnt += res;
-	      if (data.found_may_defs)
-		continue;
-	      /* Do not warn if it can be initialized outside this function.
-	         If we did not reach function entry then we found killing
-		 clobbers on all paths to entry.  */
-	      if (fentry_reached
-		  /* ???  We'd like to use ref_may_alias_global_p but that
-		     excludes global readonly memory and thus we get bougs
-		     warnings from p = cond ? "a" : "b" for example.  */
-		  && (!VAR_P (base)
-		      || is_global_var (base)))
+	      tree var = maybe_warn_operand (ref, stmt, lhs, rhs, wlims);
+	      if (!var)
 		continue;
 
-	      /* We didn't find any may-defs so on all paths either
-	         reached function entry or a killing clobber.  */
-	      location_t location
-		= linemap_resolve_location (line_table, gimple_location (stmt),
-					    LRK_SPELLING_LOCATION, NULL);
-	      if (always_executed)
+	      if (DECL_P (var))
 		{
-		  if (warning_at (location, OPT_Wuninitialized,
-				  "%qE is used uninitialized in this function",
-				  rhs))
-		    /* ???  This is only effective for decls as in
-		       gcc.dg/uninit-B-O0.c.  Avoid doing this for
-		       maybe-uninit uses as it may hide important
-		       locations.  */
-		    TREE_NO_WARNING (rhs) = 1;
+		  location_t loc = DECL_SOURCE_LOCATION (var);
+		  inform (loc, "%qD declared here", var);
 		}
-	      else if (warn_possibly_uninitialized)
-		warning_at (location, OPT_Wmaybe_uninitialized,
-			    "%qE may be used uninitialized in this function",
-			    rhs);
 	    }
 	}
     }
@@ -2665,7 +2932,7 @@  pass_late_warn_uninitialized::execute (function *fun)
   /* Re-do the plain uninitialized variable check, as optimization may have
      straightened control flow.  Do this first so that we don't accidentally
      get a "may be" warning when we'd have seen an "is" warning later.  */
-  warn_uninitialized_vars (/*warn_possibly_uninitialized=*/1);
+  warn_uninitialized_vars (/*warn_maybe_uninitialized=*/1);
 
   timevar_push (TV_TREE_UNINIT);
 
@@ -2735,7 +3002,7 @@  execute_early_warn_uninitialized (void)
      optimization we need to warn here about "may be uninitialized".  */
   calculate_dominance_info (CDI_POST_DOMINATORS);
 
-  warn_uninitialized_vars (/*warn_possibly_uninitialized=*/!optimize);
+  warn_uninitialized_vars (/*warn_maybe_uninitialized=*/!optimize);
 
   /* Post-dominator information cannot be reliably updated.  Free it
      after the use.  */