[v2,2/2] add -Wdangling-pointer [PR #63272]

Message ID d726c622-d6e2-f6dd-ca9c-2e882c9e8547@gmail.com
State New
Headers
Series None |

Commit Message

Martin Sebor Nov. 30, 2021, 10:55 p.m. UTC
  Attached is a revision of this patch with adjustments for
the changes to the prerequisite patch 1 in the series and
a couple of minor simplifications and slightly improved
test coverage, rested on x86_64-linux.

On 11/1/21 4:18 PM, Martin Sebor wrote:
> Patch 2 in this series adds support for detecting the uses of
> dangling pointers: those to auto objects that have gone out of
> scope.  Like patch 1, to minimize false positives this detection
> is very simplistic.  However, thanks to the more deterministic
> nature of the problem (all local objects go out of scope) is able
> to detect more instances of it.  The approach I used is to simply
> search the IL for clobbers that dominate uses of pointers to
> the clobbered objects.  If such a use is found that's not
> followed by a clobber of the same object the warning triggers.
> Similar to -Wuse-after-free, the new -Wdangling-pointer option
> has multiple levels: level 1 to detect unconditional uses and
> level 2 to flag conditional ones.  Unlike with -Wuse-after-free
> there is no use case for testing dangling pointers for
> equality, so there is no level 3.
> 
> Tested on x86_64-linux and  by building Glibc and Binutils/GDB.
> It found no problems outside of the GCC test suite.
> 
> As with the first patch in this series, the tests contain a number
> of xfails due to known limitations marked with pr??????.  I'll
> open bugs for them before committing the patch if I don't resolve
> them first in a followup.
> 
> Martin
  

Comments

Martin Sebor Dec. 7, 2021, 12:51 a.m. UTC | #1
Ping:
https://gcc.gnu.org/pipermail/gcc-patches/2021-November/585819.html

On 11/30/21 3:55 PM, Martin Sebor wrote:
> Attached is a revision of this patch with adjustments for
> the changes to the prerequisite patch 1 in the series and
> a couple of minor simplifications and slightly improved
> test coverage, rested on x86_64-linux.
> 
> On 11/1/21 4:18 PM, Martin Sebor wrote:
>> Patch 2 in this series adds support for detecting the uses of
>> dangling pointers: those to auto objects that have gone out of
>> scope.  Like patch 1, to minimize false positives this detection
>> is very simplistic.  However, thanks to the more deterministic
>> nature of the problem (all local objects go out of scope) is able
>> to detect more instances of it.  The approach I used is to simply
>> search the IL for clobbers that dominate uses of pointers to
>> the clobbered objects.  If such a use is found that's not
>> followed by a clobber of the same object the warning triggers.
>> Similar to -Wuse-after-free, the new -Wdangling-pointer option
>> has multiple levels: level 1 to detect unconditional uses and
>> level 2 to flag conditional ones.  Unlike with -Wuse-after-free
>> there is no use case for testing dangling pointers for
>> equality, so there is no level 3.
>>
>> Tested on x86_64-linux and  by building Glibc and Binutils/GDB.
>> It found no problems outside of the GCC test suite.
>>
>> As with the first patch in this series, the tests contain a number
>> of xfails due to known limitations marked with pr??????.  I'll
>> open bugs for them before committing the patch if I don't resolve
>> them first in a followup.
>>
>> Martin
>
  
Martin Sebor Dec. 13, 2021, 4:50 p.m. UTC | #2
Ping.  This patch, originally submitted on Nov. 1, has not been
reviewed yet.

https://gcc.gnu.org/pipermail/gcc-patches/2021-November/585819.html

On 12/6/21 5:51 PM, Martin Sebor wrote:
> Ping:
> https://gcc.gnu.org/pipermail/gcc-patches/2021-November/585819.html
> 
> On 11/30/21 3:55 PM, Martin Sebor wrote:
>> Attached is a revision of this patch with adjustments for
>> the changes to the prerequisite patch 1 in the series and
>> a couple of minor simplifications and slightly improved
>> test coverage, rested on x86_64-linux.
>>
>> On 11/1/21 4:18 PM, Martin Sebor wrote:
>>> Patch 2 in this series adds support for detecting the uses of
>>> dangling pointers: those to auto objects that have gone out of
>>> scope.  Like patch 1, to minimize false positives this detection
>>> is very simplistic.  However, thanks to the more deterministic
>>> nature of the problem (all local objects go out of scope) is able
>>> to detect more instances of it.  The approach I used is to simply
>>> search the IL for clobbers that dominate uses of pointers to
>>> the clobbered objects.  If such a use is found that's not
>>> followed by a clobber of the same object the warning triggers.
>>> Similar to -Wuse-after-free, the new -Wdangling-pointer option
>>> has multiple levels: level 1 to detect unconditional uses and
>>> level 2 to flag conditional ones.  Unlike with -Wuse-after-free
>>> there is no use case for testing dangling pointers for
>>> equality, so there is no level 3.
>>>
>>> Tested on x86_64-linux and  by building Glibc and Binutils/GDB.
>>> It found no problems outside of the GCC test suite.
>>>
>>> As with the first patch in this series, the tests contain a number
>>> of xfails due to known limitations marked with pr??????.  I'll
>>> open bugs for them before committing the patch if I don't resolve
>>> them first in a followup.
>>>
>>> Martin
>>
>
  
Martin Sebor Jan. 4, 2022, 6:02 p.m. UTC | #3
Ping:
https://gcc.gnu.org/pipermail/gcc-patches/2021-November/585819.html

On 12/13/21 9:50 AM, Martin Sebor wrote:
> Ping.  This patch, originally submitted on Nov. 1, has not been
> reviewed yet.
> 
> https://gcc.gnu.org/pipermail/gcc-patches/2021-November/585819.html
> 
> On 12/6/21 5:51 PM, Martin Sebor wrote:
>> Ping:
>> https://gcc.gnu.org/pipermail/gcc-patches/2021-November/585819.html
>>
>> On 11/30/21 3:55 PM, Martin Sebor wrote:
>>> Attached is a revision of this patch with adjustments for
>>> the changes to the prerequisite patch 1 in the series and
>>> a couple of minor simplifications and slightly improved
>>> test coverage, rested on x86_64-linux.
>>>
>>> On 11/1/21 4:18 PM, Martin Sebor wrote:
>>>> Patch 2 in this series adds support for detecting the uses of
>>>> dangling pointers: those to auto objects that have gone out of
>>>> scope.  Like patch 1, to minimize false positives this detection
>>>> is very simplistic.  However, thanks to the more deterministic
>>>> nature of the problem (all local objects go out of scope) is able
>>>> to detect more instances of it.  The approach I used is to simply
>>>> search the IL for clobbers that dominate uses of pointers to
>>>> the clobbered objects.  If such a use is found that's not
>>>> followed by a clobber of the same object the warning triggers.
>>>> Similar to -Wuse-after-free, the new -Wdangling-pointer option
>>>> has multiple levels: level 1 to detect unconditional uses and
>>>> level 2 to flag conditional ones.  Unlike with -Wuse-after-free
>>>> there is no use case for testing dangling pointers for
>>>> equality, so there is no level 3.
>>>>
>>>> Tested on x86_64-linux and  by building Glibc and Binutils/GDB.
>>>> It found no problems outside of the GCC test suite.
>>>>
>>>> As with the first patch in this series, the tests contain a number
>>>> of xfails due to known limitations marked with pr??????.  I'll
>>>> open bugs for them before committing the patch if I don't resolve
>>>> them first in a followup.
>>>>
>>>> Martin
>>>
>>
>
  
Martin Sebor Jan. 10, 2022, 9:51 p.m. UTC | #4
Last ping for this stage 1 feature before stage 3 ends:
https://gcc.gnu.org/pipermail/gcc-patches/2021-November/585819.html

On 1/4/22 11:02, Martin Sebor wrote:
> Ping:
> https://gcc.gnu.org/pipermail/gcc-patches/2021-November/585819.html
> 
> On 12/13/21 9:50 AM, Martin Sebor wrote:
>> Ping.  This patch, originally submitted on Nov. 1, has not been
>> reviewed yet.
>>
>> https://gcc.gnu.org/pipermail/gcc-patches/2021-November/585819.html
>>
>> On 12/6/21 5:51 PM, Martin Sebor wrote:
>>> Ping:
>>> https://gcc.gnu.org/pipermail/gcc-patches/2021-November/585819.html
>>>
>>> On 11/30/21 3:55 PM, Martin Sebor wrote:
>>>> Attached is a revision of this patch with adjustments for
>>>> the changes to the prerequisite patch 1 in the series and
>>>> a couple of minor simplifications and slightly improved
>>>> test coverage, rested on x86_64-linux.
>>>>
>>>> On 11/1/21 4:18 PM, Martin Sebor wrote:
>>>>> Patch 2 in this series adds support for detecting the uses of
>>>>> dangling pointers: those to auto objects that have gone out of
>>>>> scope.  Like patch 1, to minimize false positives this detection
>>>>> is very simplistic.  However, thanks to the more deterministic
>>>>> nature of the problem (all local objects go out of scope) is able
>>>>> to detect more instances of it.  The approach I used is to simply
>>>>> search the IL for clobbers that dominate uses of pointers to
>>>>> the clobbered objects.  If such a use is found that's not
>>>>> followed by a clobber of the same object the warning triggers.
>>>>> Similar to -Wuse-after-free, the new -Wdangling-pointer option
>>>>> has multiple levels: level 1 to detect unconditional uses and
>>>>> level 2 to flag conditional ones.  Unlike with -Wuse-after-free
>>>>> there is no use case for testing dangling pointers for
>>>>> equality, so there is no level 3.
>>>>>
>>>>> Tested on x86_64-linux and  by building Glibc and Binutils/GDB.
>>>>> It found no problems outside of the GCC test suite.
>>>>>
>>>>> As with the first patch in this series, the tests contain a number
>>>>> of xfails due to known limitations marked with pr??????.  I'll
>>>>> open bugs for them before committing the patch if I don't resolve
>>>>> them first in a followup.
>>>>>
>>>>> Martin
>>>>
>>>
>>
>
  
Stephan Bergmann Jan. 17, 2022, 1:46 p.m. UTC | #5
On 10/01/2022 22:51, Martin Sebor via Gcc-patches wrote:
> Last ping for this stage 1 feature before stage 3 ends:
> https://gcc.gnu.org/pipermail/gcc-patches/2021-November/585819.html

This hits somewhat unexpectedly at (test case reduced from a hit in 
LibreOffice)

> $ cat test.cc
> #include <initializer_list>
> struct S1 {
>     S1(int);
>     ~S1();
> };
> struct S2 { S2(std::initializer_list<S1>); };
> S2 f1();
> S2 f2(bool b) { return b ? f1() : S2{0}; }

> $ g++ -Wdangling-pointer -c test.cc
> test.cc: In function ‘S2 f2(bool)’:
> test.cc:8:42: warning: dangling pointer to an unnamed temporary may be used [-Wdangling-pointer=]
>     8 | S2 f2(bool b) { return b ? f1() : S2{0}; }
>       |                                          ^
> test.cc:8:39: note: unnamed temporary defined here
>     8 | S2 f2(bool b) { return b ? f1() : S2{0}; }
>       |                                       ^
> test.cc:8:42: warning: dangling pointer to an unnamed temporary may be used [-Wdangling-pointer=]
>     8 | S2 f2(bool b) { return b ? f1() : S2{0}; }
>       |                                          ^
> test.cc:8:39: note: unnamed temporary defined here
>     8 | S2 f2(bool b) { return b ? f1() : S2{0}; }
>       |                                       ^
  
Martin Sebor Jan. 17, 2022, 7:14 p.m. UTC | #6
On 1/17/22 06:46, Stephan Bergmann wrote:
> On 10/01/2022 22:51, Martin Sebor via Gcc-patches wrote:
>> Last ping for this stage 1 feature before stage 3 ends:
>> https://gcc.gnu.org/pipermail/gcc-patches/2021-November/585819.html
> 
> This hits somewhat unexpectedly at (test case reduced from a hit in 
> LibreOffice)

Thanks for the small test case!  It seems like the PHI handling
(conditionals) might overly simplistic.  Let me look into it.

I tried to set up OpenOffice for testing with the latest GCC but
couldn't get the build to finish (it failed downloading some
unavailable prerequisites).  I don't remember what problem I ran
into with LibreOffice; it was before I upgraded to Fedora 35 just
a couple of weeks ago.  Let me retry again (the build is still
downloading tarballs).

In the meantime, do you have any tips or suggestions getting it
set up that aren't on the instructions page below?  (Especially
for using an alternate compiler and non-default options.)

https://wiki.documentfoundation.org/Development/BuildingOnLinux#Fedora.2FRedHat

Martin

> 
>> $ cat test.cc
>> #include <initializer_list>
>> struct S1 {
>>     S1(int);
>>     ~S1();
>> };
>> struct S2 { S2(std::initializer_list<S1>); };
>> S2 f1();
>> S2 f2(bool b) { return b ? f1() : S2{0}; }
> 
>> $ g++ -Wdangling-pointer -c test.cc
>> test.cc: In function ‘S2 f2(bool)’:
>> test.cc:8:42: warning: dangling pointer to an unnamed temporary may be 
>> used [-Wdangling-pointer=]
>>     8 | S2 f2(bool b) { return b ? f1() : S2{0}; }
>>       |                                          ^
>> test.cc:8:39: note: unnamed temporary defined here
>>     8 | S2 f2(bool b) { return b ? f1() : S2{0}; }
>>       |                                       ^
>> test.cc:8:42: warning: dangling pointer to an unnamed temporary may be 
>> used [-Wdangling-pointer=]
>>     8 | S2 f2(bool b) { return b ? f1() : S2{0}; }
>>       |                                          ^
>> test.cc:8:39: note: unnamed temporary defined here
>>     8 | S2 f2(bool b) { return b ? f1() : S2{0}; }
>>       |                                       ^
>
  
Stephan Bergmann Jan. 19, 2022, 2:03 p.m. UTC | #7
On 17/01/2022 20:14, Martin Sebor wrote:
> I tried to set up OpenOffice for testing with the latest GCC but
> couldn't get the build to finish (it failed downloading some
> unavailable prerequisites).  I don't remember what problem I ran
> into with LibreOffice; it was before I upgraded to Fedora 35 just
> a couple of weeks ago.  Let me retry again (the build is still
> downloading tarballs).
> 
> In the meantime, do you have any tips or suggestions getting it
> set up that aren't on the instructions page below?  (Especially
> for using an alternate compiler and non-default options.)
> 
> https://wiki.documentfoundation.org/Development/BuildingOnLinux#Fedora.2FRedHat 

Building LibreOffice from source should be relatively easy these days, 
esp. on Linux.  Let me know if you have any specific issues.  What I do 
to build against a GCC other than the system one is to include the two lines

CC=/path/to/gcc
CXX=/path/to/g++

in autogen.input.

(And if you have any issues building LibreOffice, I guess you would have 
an even worse experience trying to build OpenOffice.  I for one never 
looked back.)
  

Patch

Add -Wdangling-pointer [PR63272].
Resolves:

PR c/63272 - GCC should warn when using pointer to dead scoped variable within the same function

gcc/c-family/ChangeLog:

	PR c/63272
	* c.opt (-Wdangling-pointer): New option.

gcc/ChangeLog:

	PR c/63272
	* diagnostic-spec.c (nowarn_spec_t::nowarn_spec_t): Handle
	-Wdangling-pointer.
	* doc/invoke.texi (-Wdangling-pointer): Document new option.
	* gimple-ssa-isolate-paths.c (diag_returned_locals): Suppress
	warning after issuing it.
	* gimple-ssa-warn-access.cc (pass_waccess::clone): Set new member.
	(pass_waccess::check_pointer_uses): New function.
	(pass_waccess::gimple_call_return_arg): New function.
	(pass_waccess::gimple_call_return_arg_ref): New function.
	(pass_waccess::check_call_dangling): New function.
	(pass_waccess::check_dangling_uses): New function overloads.
	(pass_waccess::check_dangling_stores): New function.
	(pass_waccess::check_dangling_stores): New function.
	(pass_waccess::m_clobbers): New data member.
	(pass_waccess::m_func): New data member.
	(pass_waccess::m_run_number): New data member.
	(pass_waccess::m_check_dangling_p): New data member.
	(pass_waccess::check_alloca): Check m_early_checks_p.
	(pass_waccess::check_alloc_size_call): Same.
	(pass_waccess::check_strcat): Same.
	(pass_waccess::check_strncat): Same.
	(pass_waccess::check_stxcpy): Same.
	(pass_waccess::check_stxncpy): Same.
	(pass_waccess::check_strncmp): Same.
	(pass_waccess::check_memop_access): Same.
	(pass_waccess::check_read_access): Same.
	(pass_waccess::check_builtin): Call check_pointer_uses.
	(pass_waccess::warn_invalid_pointer): Add arguments.
	(is_auto_decl): New function.
	(pass_waccess::check_stmt): New function.
	(pass_waccess::check_block): Call check_stmt.
	(pass_waccess::execute): Call check_dangling_uses,
	check_dangling_stores.  Empty m_clobbers.
	* passes.def (pass_warn_access): Invoke pass two more times.

gcc/testsuite/ChangeLog:

	PR c/63272
	* g++.dg/warn/Wfree-nonheap-object-6.C: Disable valid warnings.
	* gcc.dg/uninit-pr50476.c: Expect a new warning.
	* c-c++-common/Wdangling-pointer-2.c: New test.
	* c-c++-common/Wdangling-pointer-3.c: New test.
	* c-c++-common/Wdangling-pointer-4.c: New test.
	* c-c++-common/Wdangling-pointer-5.c: New test.
	* c-c++-common/Wdangling-pointer.c: New test.
	* gcc.dg/Wdangling-pointer-2.c: New test.
	* gcc.dg/Wdangling-pointer.c: New test.

diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt
index fb1abc0de4c..2e978ae9071 100644
--- a/gcc/c-family/c.opt
+++ b/gcc/c-family/c.opt
@@ -548,6 +548,14 @@  Wdangling-else
 C ObjC C++ ObjC++ Var(warn_dangling_else) Warning LangEnabledBy(C ObjC C++ ObjC++,Wparentheses)
 Warn about dangling else.
 
+Wdangling-pointer
+C ObjC C++ LTO ObjC++ Alias(Wdangling-pointer=, 2, 0) Warning
+Warn for uses of pointers to auto variables whose lifetime has ended.
+
+Wdangling-pointer=
+C ObjC C++ ObjC++ Joined RejectNegative UInteger Var(warn_dangling_pointer) Warning LangEnabledBy(C ObjC C++ ObjC++,Wall, 2, 0) IntegerRange(0, 2)
+Warn for uses of pointers to auto variables whose lifetime has ended.
+
 Wdate-time
 C ObjC C++ ObjC++ CPP(warn_date_time) CppReason(CPP_W_DATE_TIME) Var(cpp_warn_date_time) Init(0) Warning
 Warn about __TIME__, __DATE__ and __TIMESTAMP__ usage.
diff --git a/gcc/diagnostic-spec.c b/gcc/diagnostic-spec.c
index 921e7ab7423..3b1e37a6836 100644
--- a/gcc/diagnostic-spec.c
+++ b/gcc/diagnostic-spec.c
@@ -99,6 +99,7 @@  nowarn_spec_t::nowarn_spec_t (opt_code opt)
 	m_bits = NW_UNINIT;
       break;
 
+    case OPT_Wdangling_pointer_:
     case OPT_Wreturn_local_addr:
     case OPT_Wuse_after_free_:
       m_bits = NW_DANGLING;
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index 46bc8046436..dc44eadb36a 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -341,7 +341,8 @@  Objective-C and Objective-C++ Dialects}.
 -Wchar-subscripts @gol
 -Wclobbered  -Wcomment @gol
 -Wconversion  -Wno-coverage-mismatch  -Wno-cpp @gol
--Wdangling-else  -Wdate-time @gol
+-Wdangling-else  -Wdangling-pointer  -Wdangling-pointer=@var{n}  @gol
+-Wdate-time @gol
 -Wno-deprecated  -Wno-deprecated-declarations  -Wno-designated-init @gol
 -Wdisabled-optimization @gol
 -Wno-discarded-array-qualifiers  -Wno-discarded-qualifiers @gol
@@ -5691,6 +5692,7 @@  Options} and @ref{Objective-C and Objective-C++ Dialect Options}.
 -Wcatch-value @r{(C++ and Objective-C++ only)}  @gol
 -Wchar-subscripts  @gol
 -Wcomment  @gol
+-Wdangling-pointer=2  @gol
 -Wduplicate-decl-specifier @r{(C and Objective-C only)} @gol
 -Wenum-compare @r{(in C/ObjC; this is on by default in C++)} @gol
 -Wformat   @gol
@@ -8564,6 +8566,50 @@  looks like this:
 
 This warning is enabled by @option{-Wparentheses}.
 
+@item -Wdangling-pointer
+@itemx -Wdangling-pointer=@var{n}
+@opindex Wdangling-pointer
+@opindex Wno-dangling-pointer
+Warn about uses of pointers to objects with automatic storage duration after
+their lifetime has ended.  This includes local variables declared in nested
+blocks and compound literals.
+
+@table @gcctabopt
+@item -Wdangling-pointer=1
+At level 1 the warning diagnoses only unconditional uses of dangling pointers.
+For example
+@smallexample
+int f (int c1, int c2, x)
+@{
+  char *p = strchr ((char[])@{ c1, c2 @}, c3);
+  return p ? *p : 'x';   // warning: dangling pointer to a compound literal
+@}
+@end smallexample
+
+@item -Wdangling-pointer=2
+At level 2, in addition to unconditional uses the warning also diagnoses
+conditional uses of dangling pointers.
+
+For example, because the array @var{a} in the following function is out of
+scope when the pointer @var{s} that was set to point is used, the warning
+triggers at this level.
+
+@smallexample
+void f (char *s)
+@{
+  if (!s)
+    @{
+      char a[12] = "tmpname";
+      s = a;
+    @}
+  strcat (s, ".tmp");   // warning: dangling pointer to a may be used
+  ...
+@}
+@end smallexample
+@end table
+
+@option{-Wdangling-pointer=2} is included in @option{-Wall}.
+
 @item -Wdate-time
 @opindex Wdate-time
 @opindex Wno-date-time
diff --git a/gcc/gimple-ssa-warn-access.cc b/gcc/gimple-ssa-warn-access.cc
index e396266088f..7ea34df86f6 100644
--- a/gcc/gimple-ssa-warn-access.cc
+++ b/gcc/gimple-ssa-warn-access.cc
@@ -2066,10 +2066,12 @@  class pass_waccess : public gimple_opt_pass
 
   ~pass_waccess ();
 
-  opt_pass *clone () { return new pass_waccess (m_ctxt); }
+  opt_pass *clone ();
 
   virtual bool gate (function *);
 
+  void set_pass_param (unsigned, bool);
+
   virtual unsigned int execute (function *);
 
 private:
@@ -2086,6 +2088,9 @@  private:
   /* Check a call to an ordinary function for invalid accesses.  */
   bool check_call_access (gcall *);
 
+  /* Check a non-call statement.  */
+  void check_stmt (gimple *);
+
   /* Check statements in a basic block.  */
   void check_block (basic_block);
 
@@ -2107,26 +2112,41 @@  private:
   void maybe_check_access_sizes (rdwr_map *, tree, tree, gimple *);
 
   /* Check for uses of indeterminate pointers.  */
-  void check_pointer_uses (gimple *, tree);
+  void check_pointer_uses (gimple *, tree, tree = NULL_TREE, bool = false);
 
   /* Return the argument that a call returns.  */
   tree gimple_call_return_arg (gcall *);
+  tree gimple_call_return_arg_ref (gcall *);
+
+  /* Check a call for uses of a dangling pointer arguments.  */
+  void check_call_dangling (gcall *);
+
+  /* Check uses of a dangling pointer or those derived from it.  */
+  void check_dangling_uses (tree, tree, bool = false);
+  void check_dangling_uses ();
+  void check_dangling_stores ();
+  void check_dangling_stores (basic_block, hash_set<tree> &, auto_bitmap &);
 
-  void warn_invalid_pointer (tree, gimple *, gimple *, bool, bool = false);
+  void warn_invalid_pointer (tree, gimple *, gimple *, tree, bool, bool = false);
 
   /* Return true if use follows an invalidating statement.  */
-  bool use_after_inval_p (gimple *, gimple *);
+  bool use_after_inval_p (gimple *, gimple *, bool = false);
 
   /* A pointer_query object and its cache to store information about
      pointers and their targets in.  */
   pointer_query m_ptr_qry;
   pointer_query::cache_type m_var_cache;
-
+  /* Mapping from DECLs and their clobber statements in the function.  */
+  hash_map<tree, gimple *> m_clobbers;
   /* A bit is set for each basic block whose statements have been assigned
      valid UIDs.  */
   bitmap m_bb_uids_set;
   /* The current function.  */
   function *m_func;
+  /* True to run checks for uses of dangling pointers.  */
+  bool m_check_dangling_p;
+  /* True to run checks early on in the optimization pipeline.  */
+  bool m_early_checks_p;
 };
 
 /* Construct the pass.  */
@@ -2135,9 +2155,20 @@  pass_waccess::pass_waccess (gcc::context *ctxt)
   : gimple_opt_pass (pass_data_waccess, ctxt),
     m_ptr_qry (NULL, &m_var_cache),
     m_var_cache (),
+    m_clobbers (),
     m_bb_uids_set (),
-    m_func ()
+    m_func (),
+    m_check_dangling_p (),
+    m_early_checks_p ()
+{
+}
+
+/* Return a copy of the pass with RUN_NUMBER one greater than THIS.  */
+
+opt_pass*
+pass_waccess::clone ()
 {
+  return new pass_waccess (m_ctxt);
 }
 
 /* Release pointer_query cache.  */
@@ -2147,6 +2178,14 @@  pass_waccess::~pass_waccess ()
   m_ptr_qry.flush_cache ();
 }
 
+void
+pass_waccess::set_pass_param (unsigned int n, bool early)
+{
+  gcc_assert (n == 0);
+
+  m_early_checks_p = early;
+}
+
 /* Return true when any checks performed by the pass are enabled.  */
 
 bool
@@ -2335,6 +2374,9 @@  maybe_warn_alloc_args_overflow (gimple *stmt, const tree args[2],
 void
 pass_waccess::check_alloca (gcall *stmt)
 {
+  if (m_early_checks_p)
+    return;
+
   if ((warn_vla_limit >= HOST_WIDE_INT_MAX
        && warn_alloc_size_limit < warn_vla_limit)
       || (warn_alloca_limit >= HOST_WIDE_INT_MAX
@@ -2356,6 +2398,13 @@  pass_waccess::check_alloca (gcall *stmt)
 void
 pass_waccess::check_alloc_size_call (gcall *stmt)
 {
+  if (m_early_checks_p)
+    return;
+
+  if (gimple_call_num_args (stmt) < 1)
+    /* Avoid invalid calls to functions without a prototype.  */
+    return;
+
   tree fndecl = gimple_call_fndecl (stmt);
   if (fndecl && gimple_call_builtin_p (stmt, BUILT_IN_NORMAL))
     {
@@ -2408,6 +2457,9 @@  pass_waccess::check_alloc_size_call (gcall *stmt)
 void
 pass_waccess::check_strcat (gcall *stmt)
 {
+  if (m_early_checks_p)
+    return;
+
   if (!warn_stringop_overflow && !warn_stringop_overread)
     return;
 
@@ -2433,6 +2485,9 @@  pass_waccess::check_strcat (gcall *stmt)
 void
 pass_waccess::check_strncat (gcall *stmt)
 {
+  if (m_early_checks_p)
+    return;
+
   if (!warn_stringop_overflow && !warn_stringop_overread)
     return;
 
@@ -2502,6 +2557,9 @@  pass_waccess::check_strncat (gcall *stmt)
 void
 pass_waccess::check_stxcpy (gcall *stmt)
 {
+  if (m_early_checks_p)
+    return;
+
   tree dst = call_arg (stmt, 0);
   tree src = call_arg (stmt, 1);
 
@@ -2540,7 +2598,7 @@  pass_waccess::check_stxcpy (gcall *stmt)
 void
 pass_waccess::check_stxncpy (gcall *stmt)
 {
-  if (!warn_stringop_overflow)
+  if (m_early_checks_p || !warn_stringop_overflow)
     return;
 
   tree dst = call_arg (stmt, 0);
@@ -2564,7 +2622,7 @@  pass_waccess::check_stxncpy (gcall *stmt)
 void
 pass_waccess::check_strncmp (gcall *stmt)
 {
-  if (!warn_stringop_overread)
+  if (m_early_checks_p || !warn_stringop_overread)
     return;
 
   tree arg1 = call_arg (stmt, 0);
@@ -2669,6 +2727,9 @@  pass_waccess::check_strncmp (gcall *stmt)
 void
 pass_waccess::check_memop_access (gimple *stmt, tree dest, tree src, tree size)
 {
+  if (m_early_checks_p)
+    return;
+
   /* For functions like memset and memcpy that operate on raw memory
      try to determine the size of the largest source and destination
      object using type-0 Object Size regardless of the object size
@@ -2690,7 +2751,7 @@  pass_waccess::check_read_access (gimple *stmt, tree src,
 				 tree bound /* = NULL_TREE */,
 				 int ost /* = 1 */)
 {
-  if (!warn_stringop_overread)
+  if (m_early_checks_p || !warn_stringop_overread)
     return;
 
   if (bound && !useless_type_conversion_p (size_type_node, TREE_TYPE (bound)))
@@ -2818,11 +2879,12 @@  pass_waccess::check_builtin (gcall *stmt)
 
     case BUILT_IN_FREE:
     case BUILT_IN_REALLOC:
-      {
-	tree arg = call_arg (stmt, 0);
-	if (TREE_CODE (arg) == SSA_NAME)
-	  check_pointer_uses (stmt, arg);
-      }
+      if (!m_early_checks_p)
+	{
+	  tree arg = call_arg (stmt, 0);
+	  if (TREE_CODE (arg) == SSA_NAME)
+	    check_pointer_uses (stmt, arg);
+	}
       return true;
 
     case BUILT_IN_GETTEXT:
@@ -3445,16 +3507,64 @@  pass_waccess::maybe_check_dealloc_call (gcall *call)
 
 /* Return true if either USE_STMT's basic block (that of a pointer's use)
    is dominated by INVAL_STMT's (that of a pointer's invalidating statement,
-   or if they're in the same block, USE_STMT follows INVAL_STMT.  */
+   which is either a clobber or a deallocation call), or if they're in
+   the same block, USE_STMT follows INVAL_STMT.  */
 
 bool
-pass_waccess::use_after_inval_p (gimple *inval_stmt, gimple *use_stmt)
+pass_waccess::use_after_inval_p (gimple *inval_stmt, gimple *use_stmt,
+				 bool last_block /* = false */)
 {
+  tree clobvar =
+    gimple_clobber_p (inval_stmt) ? gimple_assign_lhs (inval_stmt) : NULL_TREE;
+
   basic_block inval_bb = gimple_bb (inval_stmt);
   basic_block use_bb = gimple_bb (use_stmt);
 
   if (inval_bb != use_bb)
-    return dominated_by_p (CDI_DOMINATORS, use_bb, inval_bb);
+    {
+      if (dominated_by_p (CDI_DOMINATORS, use_bb, inval_bb))
+	return true;
+
+      if (!clobvar || !last_block)
+	return false;
+
+      /* Proceed only when looking for uses of dangling pointers.  */
+      auto gsi = gsi_for_stmt (use_stmt);
+
+      auto_bitmap visited;
+
+      /* A use statement in the last basic block in a function or one that
+	 falls through to it is after any other prior clobber of the used
+	 variable unless it's followed by a clobber of the same variable. */
+      basic_block bb = use_bb;
+      while (bb != inval_bb
+	     && single_succ_p (bb)
+	     && !(single_succ_edge (bb)->flags & (EDGE_EH|EDGE_DFS_BACK)))
+	{
+	  if (!bitmap_set_bit (visited, bb->index))
+	    /* Avoid cycles. */
+	    return true;
+
+	  for (; !gsi_end_p (gsi); gsi_next_nondebug (&gsi))
+	    {
+	      gimple *stmt = gsi_stmt (gsi);
+	      if (gimple_clobber_p (stmt))
+		{
+		  if (clobvar == gimple_assign_lhs (stmt))
+		    /* The use is followed by a clobber.  */
+		    return false;
+		}
+	    }
+
+	  bb = single_succ (bb);
+	  gsi = gsi_start_bb (bb);
+	}
+
+      /* The use is one of a dangling pointer if a clobber of the variable
+	 [the pointer points to] has not been found before the function exit
+	 point.  */
+      return bb == EXIT_BLOCK_PTR_FOR_FN (cfun);
+    }
 
   if (bitmap_set_bit (m_bb_uids_set, inval_bb->index))
     /* The first time this basic block is visited assign increasing ids
@@ -3474,12 +3584,15 @@  pass_waccess::use_after_inval_p (gimple *inval_stmt, gimple *use_stmt)
 
 /* Issue a warning for the USE_STMT of pointer PTR rendered invalid
    by INVAL_STMT.  PTR may be null when it's been optimized away.
-   MAYBE is true to issue the "maybe" kind of warning.  EQUALITY is
-   true when the pointer is used in an equality expression.  */
+   When nonnull, CALLEE is the deallocation function that rendered
+   the pointer dangling.  Otherwise, VAR is the auto variable or
+   compound literal whose lifetime's rended it dangling.  MAYBE is
+   true to issue the "maybe" kind of warning.  EQUALITY is true when
+   the pointer is used in an equality expression.  */
 
 void
 pass_waccess::warn_invalid_pointer (tree ptr, gimple *use_stmt,
-				    gimple *inval_stmt,
+				    gimple *inval_stmt, tree var,
 				    bool maybe,
 				    bool equality /* = false */)
 {
@@ -3491,7 +3604,7 @@  pass_waccess::warn_invalid_pointer (tree ptr, gimple *use_stmt,
   location_t use_loc = gimple_location (use_stmt);
   if (use_loc == UNKNOWN_LOCATION)
     {
-      use_loc = cfun->function_end_locus;
+      use_loc = m_func->function_end_locus;
       if (!ptr)
 	/* Avoid issuing a warning with no context other than
 	   the function.  That would make it difficult to debug
@@ -3525,6 +3638,52 @@  pass_waccess::warn_invalid_pointer (tree ptr, gimple *use_stmt,
 	}
       return;
     }
+
+  if ((maybe && warn_dangling_pointer < 2)
+      || warning_suppressed_p (use_stmt, OPT_Wdangling_pointer_))
+    return;
+
+  if (DECL_NAME (var))
+    {
+      if ((ptr
+	   && warning_at (use_loc, OPT_Wdangling_pointer_,
+			  (maybe
+			   ? G_("dangling pointer %qE to %qD may be used")
+			   : G_("using dangling pointer %qE to %qD")),
+			  ptr, var))
+	  || (!ptr
+	      && warning_at (use_loc, OPT_Wdangling_pointer_,
+			     (maybe
+			      ? G_("dangling pointer to %qD may be used")
+			      : G_("using a dangling pointer to %qD")),
+			     var)))
+	inform (DECL_SOURCE_LOCATION (var),
+		"%qD declared here", var);
+      suppress_warning (use_stmt, OPT_Wdangling_pointer_);
+      return;
+    }
+
+  if ((ptr
+       && warning_at (use_loc, OPT_Wdangling_pointer_,
+		      (maybe
+		       ? G_("dangling pointer %qE to a compound literal "
+			    "may be used")
+		       : G_("using dangling pointer %qE to a compound "
+			    "literal")),
+		      ptr, var))
+      || (!ptr
+	  && warning_at (use_loc, OPT_Wdangling_pointer_,
+			 (maybe
+			  ? G_("dangling pointer to a compound literal "
+			       "may be used")
+			  : G_("using a dangling pointer to a compound "
+			       "literal")),
+			 var)))
+    {
+      inform (DECL_SOURCE_LOCATION (var),
+	      "compound literal defined here");
+      suppress_warning (use_stmt, OPT_Wdangling_pointer_);
+    }
 }
 
 /* If STMT is a call to either the standard realloc or to a user-defined
@@ -3647,10 +3806,14 @@  pointers_related_p (gimple *stmt, tree p, tree q, pointer_query &qry)
 
 /* For a STMT either a call to a deallocation function or a clobber, warn
    for uses of the pointer PTR it was called with (including its copies
-   or others derived from it by pointer arithmetic).  */
+   or others derived from it by pointer arithmetic).  If STMT is a clobber,
+   VAR is the decl of the clobbered variable.  When MAYBE is true use
+   a "maybe" form of diagnostic.  */
 
 void
-pass_waccess::check_pointer_uses (gimple *stmt, tree ptr)
+pass_waccess::check_pointer_uses (gimple *stmt, tree ptr,
+				  tree var /* = NULL_TREE */,
+				  bool maybe /* = false */)
 {
   gcc_assert (TREE_CODE (ptr) == SSA_NAME);
 
@@ -3733,18 +3896,25 @@  pass_waccess::check_pointer_uses (gimple *stmt, tree ptr)
 	  /* Warn if USE_STMT is dominated by the deallocation STMT.
 	     Otherwise, add the pointer to POINTERS so that the uses
 	     of any other pointers derived from it can be checked.  */
-	  if (use_after_inval_p (stmt, use_stmt))
+	  if (use_after_inval_p (stmt, use_stmt, check_dangling))
 	    {
-	      /* TODO: Handle PHIs but careful of false positives.  */
-	      if (gimple_code (use_stmt) != GIMPLE_PHI)
+	      if (gimple_code (use_stmt) == GIMPLE_PHI)
 		{
-		  basic_block use_bb = gimple_bb (use_stmt);
-		  bool this_maybe
-		    = !dominated_by_p (CDI_POST_DOMINATORS, use_bb, stmt_bb);
-		  warn_invalid_pointer (*use_p->use, use_stmt, stmt,
-					this_maybe, equality);
-		  continue;
+		  tree lhs = gimple_phi_result (use_stmt);
+		  if (TREE_CODE (lhs) == SSA_NAME)
+		    {
+		      pointers.safe_push (lhs);
+		      continue;
+		    }
 		}
+
+	      basic_block use_bb = gimple_bb (use_stmt);
+	      bool this_maybe
+		= (maybe
+		   || !dominated_by_p (CDI_POST_DOMINATORS, use_bb, stmt_bb));
+	      warn_invalid_pointer (*use_p->use, use_stmt, stmt, var,
+				    this_maybe, equality);
+	      continue;
 	    }
 
 	  if (is_gimple_assign (use_stmt))
@@ -3779,26 +3949,99 @@  pass_waccess::check_call (gcall *stmt)
   if (gimple_call_builtin_p (stmt, BUILT_IN_NORMAL))
     check_builtin (stmt);
 
-  if (tree callee = gimple_call_fndecl (stmt))
-    {
-      /* Check for uses of the pointer passed to either a standard
-	 or a user-defined deallocation function.  */
-      unsigned argno = fndecl_dealloc_argno (callee);
-      if (argno < (unsigned) call_nargs (stmt))
-	{
-	  tree arg = call_arg (stmt, argno);
-	  if (TREE_CODE (arg) == SSA_NAME)
-	    check_pointer_uses (stmt, arg);
-	}
-    }
+  if (!m_early_checks_p)
+    if (tree callee = gimple_call_fndecl (stmt))
+      {
+	/* Check for uses of the pointer passed to either a standard
+	   or a user-defined deallocation function.  */
+	unsigned argno = fndecl_dealloc_argno (callee);
+	if (argno < (unsigned) call_nargs (stmt))
+	  {
+	    tree arg = call_arg (stmt, argno);
+	    if (TREE_CODE (arg) == SSA_NAME)
+	      check_pointer_uses (stmt, arg);
+	  }
+      }
 
   check_call_access (stmt);
+  check_call_dangling (stmt);
+
+  if (m_early_checks_p)
+    return;
 
   maybe_check_dealloc_call (stmt);
   check_nonstring_args (stmt);
 }
 
 
+/* Return true of X is a DECL with automatic storage duration.  */
+
+static inline bool
+is_auto_decl (tree x)
+{
+  return DECL_P (x) && !DECL_EXTERNAL (x) && !TREE_STATIC (x);
+}
+
+/* Check non-call STMT for invalid accesses.  */
+
+void
+pass_waccess::check_stmt (gimple *stmt)
+{
+  if (m_check_dangling_p && gimple_clobber_p (stmt))
+    {
+      /* Ignore clobber statemts in blocks with exceptional edges.  */
+      basic_block bb = gimple_bb (stmt);
+      edge e = EDGE_PRED (bb, 0);
+      if (e->flags & EDGE_EH)
+	return;
+
+      tree var = gimple_assign_lhs (stmt);
+      m_clobbers.put (var, stmt);
+      return;
+    }
+
+  if (is_gimple_assign (stmt))
+    {
+      /* Clobbered compound literals can be revived.  Check for
+	 an assignment to one and remove it from M_CLOBBERS.  */
+      tree lhs = gimple_assign_lhs (stmt);
+      while (handled_component_p (lhs))
+	lhs = TREE_OPERAND (lhs, 0);
+
+      if (is_auto_decl (lhs))
+	m_clobbers.remove (lhs);
+      return;
+    }
+
+  if (greturn *ret = dyn_cast <greturn *> (stmt))
+    {
+      if (optimize && flag_isolate_erroneous_paths_dereference)
+	/* Avoid interfering with -Wreturn-local-addr (which runs only
+	   with optimization enabled).  */
+	return;
+
+      tree arg = gimple_return_retval (ret);
+      if (!arg || TREE_CODE (arg) != ADDR_EXPR)
+	return;
+
+      arg = TREE_OPERAND (arg, 0);
+      while (handled_component_p (arg))
+	arg = TREE_OPERAND (arg, 0);
+
+      if (!is_auto_decl (arg))
+	return;
+
+      gimple **pclobber = m_clobbers.get (arg);
+      if (!pclobber)
+	return;
+
+      if (!use_after_inval_p (*pclobber, stmt))
+	return;
+
+      warn_invalid_pointer (NULL_TREE, stmt, *pclobber, arg, false);
+    }
+}
+
 /* Check basic block BB for invalid accesses.  */
 
 void
@@ -3811,6 +4054,8 @@  pass_waccess::check_block (basic_block bb)
       gimple *stmt = gsi_stmt (si);
       if (gcall *call = dyn_cast <gcall *> (stmt))
 	check_call (call);
+      else
+	check_stmt (stmt);
     }
 }
 
@@ -3859,6 +4104,232 @@  pass_waccess::gimple_call_return_arg (gcall *call)
   return gimple_call_arg (call, argno);
 }
 
+/* Return the decl referenced by the argument that the call STMT to
+   a built-in function returns (including with an offset) or null if
+   it doesn't.  */
+
+tree
+pass_waccess::gimple_call_return_arg_ref (gcall *call)
+{
+  if (tree arg = gimple_call_return_arg (call))
+    {
+      access_ref aref;
+      if (m_ptr_qry.get_ref (arg, call, &aref, 0)
+	  && DECL_P (aref.ref))
+	return aref.ref;
+    }
+
+  return NULL_TREE;
+}
+
+/* Check for and diagnose all uses of the dangling pointer VAR to
+   the auto object DECL whose lifetime has ended.  */
+
+void
+pass_waccess::check_dangling_uses (tree var, tree decl, bool maybe /* = false */)
+{
+  if (!decl || !is_auto_decl (decl))
+    return;
+
+  gimple **pclob = m_clobbers.get (decl);
+  if (!pclob)
+    return;
+
+  check_pointer_uses (*pclob, var, decl, maybe);
+}
+
+/* Diagnose stores in BB and (recursively) its predecessors of the addresses
+   of local variables into nonlocal pointers that are left dangling after
+   the function returns.  BBS is a bitmap of basic blocks visited.  */
+
+void
+pass_waccess::check_dangling_stores (basic_block bb,
+				     hash_set<tree> &stores,
+				     auto_bitmap &bbs)
+{
+  if (!bitmap_set_bit (bbs, bb->index))
+    /* Avoid cycles. */
+    return;
+
+  /* Iterate backwards over the statements looking for a store of
+     the address of a local variable into a nonlocal pointer.  */
+  for (auto gsi = gsi_last_nondebug_bb (bb); ; gsi_prev_nondebug (&gsi))
+    {
+      gimple *stmt = gsi_stmt (gsi);
+      if (!stmt)
+	break;
+
+      if (is_gimple_call (stmt)
+	  && !(gimple_call_flags (stmt) & (ECF_CONST | ECF_PURE)))
+	/* Avoid looking before nonconst, nonpure calls since those might
+	   use the escaped locals.  */
+	return;
+
+      if (!is_gimple_assign (stmt) || gimple_clobber_p (stmt))
+	continue;
+
+      access_ref lhs_ref;
+      tree lhs = gimple_assign_lhs (stmt);
+      if (!m_ptr_qry.get_ref (lhs, stmt, &lhs_ref, 0))
+	continue;
+
+      if (is_auto_decl (lhs_ref.ref))
+	continue;
+
+      if (DECL_P (lhs_ref.ref))
+	{
+	  if (!POINTER_TYPE_P (TREE_TYPE (lhs_ref.ref))
+	      || lhs_ref.deref > 0)
+	    continue;
+	}
+      else if (TREE_CODE (lhs_ref.ref) == SSA_NAME)
+	{
+	  /* Avoid looking at or before stores into unknown objects.  */
+	  gimple *def_stmt = SSA_NAME_DEF_STMT (lhs_ref.ref);
+	  if (!gimple_nop_p (def_stmt))
+	    return;
+	}
+      else if (TREE_CODE (lhs_ref.ref) == MEM_REF)
+	{
+	  tree arg = TREE_OPERAND (lhs_ref.ref, 0);
+	  if (TREE_CODE (arg) == SSA_NAME)
+	    {
+	      gimple *def_stmt = SSA_NAME_DEF_STMT (arg);
+	      if (!gimple_nop_p (def_stmt))
+		return;
+	    }
+	}
+      else
+	continue;
+
+      if (stores.add (lhs_ref.ref))
+	continue;
+
+      access_ref rhs_ref;
+      tree rhs = gimple_assign_rhs1 (stmt);
+      if (!m_ptr_qry.get_ref (rhs, stmt, &rhs_ref, 0)
+	  || rhs_ref.deref != -1)
+	continue;
+
+      if (!is_auto_decl (rhs_ref.ref))
+	continue;
+
+      location_t loc = gimple_location (stmt);
+      if (warning_at (loc, OPT_Wdangling_pointer_,
+		      "storing the address of local variable %qD in %qE",
+		      rhs_ref.ref, lhs))
+	{
+	  location_t loc = DECL_SOURCE_LOCATION (rhs_ref.ref);
+	  inform (loc, "%qD declared here", rhs_ref.ref);
+
+	  if (DECL_P (lhs_ref.ref))
+	    loc = DECL_SOURCE_LOCATION (lhs_ref.ref);
+	  else if (EXPR_HAS_LOCATION (lhs_ref.ref))
+	    loc = EXPR_LOCATION (lhs_ref.ref);
+
+	  if (loc != UNKNOWN_LOCATION)
+	    inform (loc, "%qE declared here", lhs_ref.ref);
+	}
+    }
+
+  edge e;
+  edge_iterator ei;
+  FOR_EACH_EDGE (e, ei, bb->preds)
+    {
+      basic_block pred = e->src;
+      check_dangling_stores (pred, stores, bbs);
+    }
+}
+
+/* Diagnose stores of the addresses of local variables into nonlocal
+   pointers that are left dangling after the function returns.  */
+
+void
+pass_waccess::check_dangling_stores ()
+{
+  auto_bitmap bbs;
+  hash_set<tree> stores;
+  check_dangling_stores (EXIT_BLOCK_PTR_FOR_FN (m_func), stores, bbs);
+}
+
+/* Check for and diagnose uses of dangling pointers to auto objects
+   whose lifetime has ended.  */
+
+void
+pass_waccess::check_dangling_uses ()
+{
+  tree var;
+  unsigned i;
+  FOR_EACH_SSA_NAME (i, var, m_func)
+    {
+      if (TREE_CODE (TREE_TYPE (var)) != POINTER_TYPE)
+	continue;
+
+      /* For each SSA_NAME pointer VAR find the DECL it points to.
+	 If the DECL is a clobbered local variable, check to see
+	 if any of VAR's uses (or those of other pointers derived
+	 from VAR) happens after the clobber.  If so, warn.  */
+      tree decl = NULL_TREE;
+
+      gimple *def_stmt = SSA_NAME_DEF_STMT (var);
+      if (is_gimple_assign (def_stmt))
+	{
+	  tree rhs = gimple_assign_rhs1 (def_stmt);
+	  if (TREE_CODE (rhs) == ADDR_EXPR)
+	    decl = TREE_OPERAND (rhs, 0);
+	}
+      else if (gcall *call = dyn_cast<gcall *>(def_stmt))
+	decl = gimple_call_return_arg_ref (call);
+      else if (gphi *phi = dyn_cast <gphi *>(def_stmt))
+	{
+	  unsigned nargs = gimple_phi_num_args (phi);
+	  for (unsigned i = 0; i != nargs; ++i)
+	    {
+	      access_ref aref;
+	      tree arg = gimple_phi_arg_def (phi, i);
+	      if (!m_ptr_qry.get_ref (arg, phi, &aref, 0)
+		  || (aref.deref == 0
+		      && POINTER_TYPE_P (TREE_TYPE (aref.ref))))
+		continue;
+	      check_dangling_uses (var, aref.ref, true);
+	    }
+	  continue;
+	}
+      else
+	continue;
+
+      check_dangling_uses (var, decl);
+    }
+}
+
+/* Check CALL arguments for dangling pointers (those that have been
+   clobbered) and warn if found.  */
+
+void
+pass_waccess::check_call_dangling (gcall *call)
+{
+  unsigned nargs = gimple_call_num_args (call);
+  for (unsigned i = 0; i != nargs; ++i)
+    {
+      tree arg = gimple_call_arg (call, i);
+      if (TREE_CODE (arg) != ADDR_EXPR)
+	continue;
+
+      arg = TREE_OPERAND (arg, 0);
+      if (!DECL_P (arg))
+	continue;
+
+      gimple **pclobber = m_clobbers.get (arg);
+      if (!pclobber)
+	continue;
+
+      if (!use_after_inval_p (*pclobber, call))
+	continue;
+
+      warn_invalid_pointer (NULL_TREE, call, *pclobber, arg, false);
+    }
+}
+
 /* Check function FUN for invalid accesses.  */
 
 unsigned
@@ -3871,6 +4342,15 @@  pass_waccess::execute (function *fun)
   m_ptr_qry.rvals = enable_ranger (fun);
   m_func = fun;
 
+  /* Check for dangling pointers in the earliest run of the pass.
+     The latest point -Wdanglinng-pointer should run is just before
+     loop unrolling which introduces uses after clobbers.  Most cases
+     can be detected without optimization; cases where the address of
+     the local variable is passed to and then returned from a user-
+     defined function before its lifetime ends and the returned pointer
+     becomes dangling depend on inlining.  */
+  m_check_dangling_p = m_early_checks_p;
+
   auto_bitmap bb_uids_set (&bitmap_default_obstack);
   m_bb_uids_set = bb_uids_set;
 
@@ -3880,6 +4360,12 @@  pass_waccess::execute (function *fun)
   FOR_EACH_BB_FN (bb, fun)
     check_block (bb);
 
+  if (m_check_dangling_p)
+    {
+      check_dangling_uses ();
+      check_dangling_stores ();
+    }
+
   if (dump_file)
     m_ptr_qry.dump (dump_file, (dump_flags & TDF_DETAILS) != 0);
 
@@ -3890,6 +4376,7 @@  pass_waccess::execute (function *fun)
   disable_ranger (fun);
   m_ptr_qry.rvals = NULL;
 
+  m_clobbers.empty ();
   m_bb_uids_set = NULL;
 
   free_dominance_info (CDI_POST_DOMINATORS);
diff --git a/gcc/passes.def b/gcc/passes.def
index 37ea0d318d1..edf9c5be6a0 100644
--- a/gcc/passes.def
+++ b/gcc/passes.def
@@ -63,6 +63,7 @@  along with GCC; see the file COPYING3.  If not see
       NEXT_PASS (pass_ubsan);
       NEXT_PASS (pass_nothrow);
       NEXT_PASS (pass_rebuild_cgraph_edges);
+      NEXT_PASS (pass_warn_access, /*early=*/true);
   POP_INSERT_PASSES ()
 
   NEXT_PASS (pass_local_optimization_passes);
@@ -201,6 +202,8 @@  along with GCC; see the file COPYING3.  If not see
 	 form if possible.  */
       NEXT_PASS (pass_object_sizes);
       NEXT_PASS (pass_post_ipa_warn);
+      /* Must run before loop unrolling.  */
+      NEXT_PASS (pass_warn_access, /*early=*/true);
       NEXT_PASS (pass_complete_unrolli);
       NEXT_PASS (pass_backprop);
       NEXT_PASS (pass_phiprop);
@@ -426,7 +429,7 @@  along with GCC; see the file COPYING3.  If not see
   NEXT_PASS (pass_harden_compares);
   NEXT_PASS (pass_cleanup_cfg_post_optimizing);
   NEXT_PASS (pass_warn_function_noreturn);
-  NEXT_PASS (pass_warn_access);
+  NEXT_PASS (pass_warn_access, /*early=*/false);
 
   NEXT_PASS (pass_expand);
 
diff --git a/gcc/testsuite/c-c++-common/Wdangling-pointer-2.c b/gcc/testsuite/c-c++-common/Wdangling-pointer-2.c
new file mode 100644
index 00000000000..527e5e7b2c6
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/Wdangling-pointer-2.c
@@ -0,0 +1,437 @@ 
+/* PR middle-end/63272 - GCC should warn when using pointer to dead scoped
+   variable within the same function
+   Exercise basic cases of -Wdangling-pointer with optimization.
+   { dg-do compile }
+   { dg-options "-O2 -Wall -Wno-uninitialized -Wno-return-local-addr -ftrack-macro-expansion=0" } */
+
+typedef __INTPTR_TYPE__ intptr_t;
+typedef __SIZE_TYPE__   size_t;
+
+#if __cplusplus
+#  define EXTERN_C extern "C"
+#else
+#  define EXTERN_C extern
+#endif
+
+#define NOIPA __attribute__ ((noipa))
+
+EXTERN_C void* alloca (size_t);
+EXTERN_C void* malloc (size_t);
+EXTERN_C void* memchr (const void*, int, size_t);
+EXTERN_C char* strchr (const char*, int);
+
+int sink (const void*, ...);
+#define sink(...) sink (0, __VA_ARGS__)
+
+
+NOIPA void nowarn_addr (void)
+{
+  int *p;
+  {
+    int a[] = { 1, 2, 3 };
+    p = a;
+  }
+
+  // This is suspect but not a clear error.
+  sink (&p);
+}
+
+
+NOIPA char* nowarn_ptr (void)
+{
+  char *p;
+  sink (&p);
+  return p;
+}
+
+
+NOIPA char* nowarn_cond_ptr (void)
+{
+  // Distilled from a false positive in Glibc dlerror.c.
+  char *q;
+  if (sink (&q))
+    return q;
+
+  return 0;
+}
+
+
+NOIPA void nowarn_loop_ptr (int n, int *p)
+{
+  // Distilled from a false positive in Glibc td_thr_get_info.c.
+  for (int i = 0; i != 2; ++i)
+    {
+      int x;
+      sink (&x);
+      *p++ = x;
+    }
+
+  /* With the loop unrolled, Q is clobbered just before the call to
+     sink(), making it indistinguishable from passing it a pointer
+     to an out-of-scope variable.  Verify that the warning doesn't
+     suffer from false positives due to this.
+     int * q;
+     int * q.1_17;
+     int * q.1_26;
+
+     <bb 2>:
+     f (&q);
+     q.1_17 = q;
+     *p_5(D) = q.1_17;
+     q ={v} {CLOBBER};
+     f (&q);
+     q.1_26 = q;
+     MEM[(void * *)p_5(D) + 8B] = q.1_26;
+     q ={v} {CLOBBER};
+     return;
+  */
+}
+
+
+NOIPA void nowarn_intptr_t (void)
+{
+  intptr_t ip;
+  {
+    int a[] = { 1, 2, 3 };
+    ip = (intptr_t)a;
+  }
+
+  // Using an intptr_t is not diagnosed.
+  sink (0, ip);
+}
+
+
+NOIPA void nowarn_string_literal (void)
+{
+  const char *s;
+  {
+    s = "123";
+  }
+
+  sink (s);
+}
+
+
+NOIPA void nowarn_extern_array (int x)
+{
+  {
+    /* This is a silly sanity check.  */
+    extern int eia[];
+    int *p;
+    {
+      p = eia;
+    }
+    sink (p);
+  }
+}
+
+
+NOIPA void nowarn_static_array (int x)
+{
+  {
+    const char *s;
+    {
+      static const char sca[] = "123";
+      s = sca;
+    }
+
+    sink (s);
+  }
+  {
+    const int *p;
+    {
+      static const int sia[] = { 1, 2, 3 };
+      p = sia;
+    }
+
+    sink (p);
+  }
+  {
+    const int *p;
+    {
+      static const int sia[] = { 1, 2, 3 };
+      p = (const int*)memchr (sia, x, sizeof sia);
+    }
+
+    sink (p);
+  }
+}
+
+
+NOIPA void nowarn_alloca (unsigned n)
+{
+  {
+    char *p;
+    {
+      p = (char*)alloca (n);
+    }
+    sink (p);
+  }
+  {
+    int *p;
+    {
+      p = (int*)alloca (n * sizeof *p);
+      sink (p);
+    }
+    sink (p);
+  }
+  {
+    long *p;
+    {
+      p = (long*)alloca (n * sizeof *p);
+      sink (p);
+      p = p + 1;
+    }
+    sink (p);
+  }
+}
+
+
+#pragma GCC diagnostic push
+/* Verify that -Wdangling-pointer works with #pragma diagnostic.  */
+#pragma GCC diagnostic ignored "-Wdangling-pointer"
+
+
+NOIPA void* nowarn_return_local_addr (void)
+{
+  int a[] = { 1, 2, 3 };
+  int *p = a;
+
+  /* This is a likely bug but it's not really one of using a dangling
+     pointer but rather of returning the address of a local variable
+     which is diagnosed by -Wreturn-local-addr.  */
+  return p;
+}
+
+NOIPA void* warn_return_local_addr (void)
+{
+  int *p = 0;
+  {
+    int a[] = { 1, 2, 3 };
+    sink (a);
+    p = a;
+  }
+
+  /* Unlike the above case, here the pointer is dangling when it's
+     used.  */
+  return p;                   // { dg-warning "using dangling pointer 'p' to 'a'" "pr??????" { xfail *-*-* } }
+}
+
+
+NOIPA void* nowarn_return_alloca (int n)
+{
+  int *p = (int*)alloca (n);
+  sink (p);
+
+  /* This is a likely bug but it's not really one of using a dangling
+     pointer but rather of returning the address of a local variable
+     which is diagnosed by -Wreturn-local-addr.  */
+  return p;
+}
+
+
+NOIPA void nowarn_scalar_call_ignored (void *vp)
+{
+  int *p;
+  {
+    int i;
+    p = &i;
+  }
+  sink (p);
+}
+
+#pragma GCC diagnostic pop
+
+NOIPA void warn_scalar_call (void)
+{
+  int *p;
+  {
+    int i;                    // { dg-message "'i' declared" "note" }
+    p = &i;
+  }
+  // When the 'p' is optimized away it's not mentioned in the warning.
+  sink (p);                   // { dg-warning "using \(a \)?dangling pointer \('p' \)?to 'i'" "array" }
+}
+
+
+NOIPA void warn_array_call (void)
+{
+  int *p;
+  {
+    int a[] = { 1, 2, 3 };    // { dg-message "'a' declared" "note" }
+    p = a;
+  }
+  sink (p);                   // { dg-warning "using \(a \)?dangling pointer \('p' \)?to 'a'" "array" }
+}
+
+
+NOIPA void* warn_array_return (void)
+{
+  int *p;
+  {
+    int a[] = { 1, 2, 3 };    // { dg-message "'a' declared" "note" }
+    p = a;
+  }
+
+  return p;                   // { dg-warning "using \(a \)?dangling pointer \('p' \)?to 'a'" "array" }
+}
+
+
+NOIPA void warn_pr63272_c1 (int i)
+{
+  int *p = 0;
+
+  if (i)
+    {
+      int k = i;              // { dg-message "'k' declared" "note" }
+      p = &k;
+    }
+
+  sink (p ? *p : 0);          // { dg-warning "dangling pointer 'p' to 'k' may be used" }
+}
+
+
+NOIPA void warn_pr63272_c4 (void)
+{
+  int *p = 0;
+
+  {
+    int b;                    // { dg-message "'b' declared" "note" }
+    p = &b;
+  }
+
+  sink (p);                   // { dg-warning "using \(a \)?dangling pointer \('p' \)?to 'b'" "scalar" }
+}
+
+
+NOIPA void warn_cond_if (int i, int n)
+{
+  int *p;
+  if (i)
+    {
+      int a[] = { 1, 2 };     // { dg-message "'a' declared" "note" }
+      sink (a);
+      p = a;
+    }
+  else
+   {
+     int *b = (int*)malloc (n);
+     sink (b);
+     p = b;
+   }
+
+  sink (p);                   // { dg-warning "dangling pointer 'p' to 'a' may be used" }
+}
+
+
+NOIPA void warn_cond_else (int i, int n)
+{
+  int *p;
+  if (i)
+    {
+      int *a = (int*)malloc (n);
+      sink (a);
+      p = a;
+    }
+  else
+   {
+     int b[] = { 2, 3 };
+     sink (b);
+     p = b;
+   }
+
+  sink (p);                   // { dg-warning "dangling pointer 'p' to 'b' may be used" }
+}
+
+
+NOIPA void warn_cond_if_else (int i)
+{
+  int *p;
+  if (i)
+    {
+      int a[] = { 1, 2 };     // { dg-message "'a' declared" "note" }
+      sink (a);
+      p = a;
+    }
+  else
+   {
+     int b[] = { 3, 4 };      // { dg-message "'b' declared" "pr??????" { xfail *-*-* } }
+     sink (b);
+     p = b;
+   }
+
+  /* With a PHI with more than invalid argument, only one use is diagnosed
+     because after the first diagnostic the code suppresses subsequent
+     ones for the same use.  This needs to be fixed.  */
+  sink (p);                   // { dg-warning "dangling pointer 'p' to 'a' may be used" }
+                              // { dg-warning "dangling pointer 'p' to 'b' may be used" "pr??????" { xfail *-*-* } .-1 }
+}
+
+
+NOIPA void nowarn_gcc_i386 (int i)
+{
+  // Regression test reduced from gcc's i386.c.
+  char a[32], *p;
+
+  if (i != 1)
+    p = a;
+  else
+    p = 0;
+
+  if (i == 2)
+    sink (p);
+  else
+    {
+      if (p)
+	{
+	  sink (p);
+	  return;
+	}
+      sink (p);
+    }
+}
+
+
+NOIPA void warn_memchr (char c1, char c2, char c3, char c4)
+{
+  char *p = 0;
+  {
+    char a[] = { c1, c2, c3 };// { dg-message "'a' declared" "note" }
+    p = (char*)memchr (a, c4, 3);
+    if (!p)
+      return;
+  }
+
+  sink (p);                   // { dg-warning "using dangling pointer 'p' to 'a'" }
+}
+
+
+NOIPA void warn_strchr (char c1, char c2, char c3, char c4)
+{
+  char *p = 0;
+  {
+    char a[] = { c1, c2, c3 }; // { dg-message "'a' declared" "note" }
+    p = (char*)strchr (a, c4);
+    if (!p)
+      return;
+  }
+
+  sink (p);                   // { dg-warning "using dangling pointer 'p' to 'a'" }
+}
+
+
+static inline int* return_arg (int *p)
+{
+  return p;
+}
+
+NOIPA void warn_inline (int i1, int i2, int i3)
+{
+  int *p;
+  {
+    int a[] = { i1, i2, i3 }; // { dg-message "'a' declared" "note" }
+    p = return_arg (a);
+  }
+
+  sink (p);                   // { dg-warning "using \(a \)?dangling pointer \('p' \)?to 'a'" "inline" }
+}
diff --git a/gcc/testsuite/c-c++-common/Wdangling-pointer-3.c b/gcc/testsuite/c-c++-common/Wdangling-pointer-3.c
new file mode 100644
index 00000000000..d2f8f432eba
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/Wdangling-pointer-3.c
@@ -0,0 +1,64 @@ 
+/* PR middle-end/63272 - GCC should warn when using pointer to dead scoped
+   variable within the same function
+   Exercise conditional uses dangling pointers with optimization.
+   { dg-do compile }
+   { dg-options "-O2 -Wall -Wno-maybe-uninitialized" } */
+
+typedef __INTPTR_TYPE__ intptr_t;
+typedef __SIZE_TYPE__   size_t;
+
+#if __cplusplus
+#  define EXTERN_C extern "C"
+#else
+#  define EXTERN_C extern
+#endif
+
+EXTERN_C void* memcpy (void*, const void*, size_t);
+
+void sink (const void*, ...);
+
+char* nowarn_conditional (char *s)
+{
+  // Reduced from Glibc's tmpnam.c.
+  extern char a[5];
+  char b[5];
+  char *p = s ? s : b;
+
+  sink (p);
+
+  if (s == 0)
+    return a;
+
+  return s;
+}
+
+
+char* nowarn_conditional_memcpy (char *s)
+{
+  // Reduced from Glibc's tmpnam.c.
+  extern char a[5];
+  char b[5];
+  char *p = s ? s : b;
+
+  sink (p);
+
+  if (s == 0)
+    return (char*)memcpy (a, p, 5);
+
+  return s;
+}
+
+
+int warn_conditional_block (int i)
+{
+  int *p;
+  if (i)
+  {
+    int a[] = { 1, 2, 3 };
+    p = &a[i];
+  }
+  else
+    p = &i;
+
+  return *p;        // { dg-warning "dangling pointer \('p' \)to 'a' may be used" }
+}
diff --git a/gcc/testsuite/c-c++-common/Wdangling-pointer-4.c b/gcc/testsuite/c-c++-common/Wdangling-pointer-4.c
new file mode 100644
index 00000000000..e57e66f8336
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/Wdangling-pointer-4.c
@@ -0,0 +1,73 @@ 
+/* PR middle-end/63272 - GCC should warn when using pointer to dead scoped
+   variable within the same function
+   Exercise -Wdangling-pointer for VLAs.
+   { dg-do compile }
+   { dg-options "-O0 -Wall -ftrack-macro-expansion=0" } */
+
+void sink (void*, ...);
+
+void nowarn_vla (int n)
+{
+  {
+    int vla1[n];
+    int *p1 = vla1;
+    sink (p1);
+
+    {
+      int vla2[n];
+      int *p2 = vla2;
+      sink (p1, p2);
+
+      {
+	int vla3[n];
+	int *p3 = vla3;
+	sink (p1, p2, p3);
+      }
+      sink (p1, p2);
+    }
+    sink (p1);
+  }
+}
+
+void warn_one_vla (int n)
+{
+  int *p;
+  {
+    int vla[n];               // { dg-message "'vla' declared" "pr??????" { xfail *-*-* } }
+    p = vla;
+  }
+  sink (p);                   // { dg-warning "using a dangling pointer to 'vla'" "vla" { xfail *-*-* } }
+}
+
+
+void warn_two_vlas_same_block (int n)
+{
+  int *p, *q;
+  {
+    int vla1[n];              // { dg-message "'vla1' declared" "pr??????" { xfail *-*-* } }
+    int vla2[n];              // { dg-message "'vla2' declared" "pr??????" { xfail *-*-* } }
+    p = vla1;
+    q = vla2;
+  }
+
+  sink (p);                   // { dg-warning "using a dangling pointer to 'vla1'" "vla" { xfail *-*-* } }
+  sink (q);                   // { dg-warning "using a dangling pointer to 'vla2'" "vla" { xfail *-*-* } }
+}
+
+
+void warn_two_vlas_in_series (int n)
+{
+  int *p;
+  {
+    int vla1[n];              // { dg-message "'vla1' declared" "pr??????" { xfail *-*-* } }
+    p = vla1;
+  }
+  sink (p);                   // { dg-warning "using a dangling pointer to 'vla1'" "vla" { xfail *-*-* } }
+
+  int *q;
+  {
+    int vla2[n];              // { dg-message "'vla2' declared" "pr??????" { xfail *-*-* } }
+    q = vla2;
+  }
+  sink (q);                   // { dg-warning "using a dangling pointer to 'vla2'" "vla" { xfail *-*-* } }
+}
diff --git a/gcc/testsuite/c-c++-common/Wdangling-pointer-5.c b/gcc/testsuite/c-c++-common/Wdangling-pointer-5.c
new file mode 100644
index 00000000000..23d37960fad
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/Wdangling-pointer-5.c
@@ -0,0 +1,79 @@ 
+/* PR middle-end/63272 - GCC should warn when using pointer to dead scoped
+   variable within the same function
+   Exercise -Wdangling-pointer for VLAs.
+   { dg-do compile }
+   { dg-options "-O0 -Wall -ftrack-macro-expansion=0" } */
+
+void* sink (void*, ...);
+
+extern void *evp;
+
+void nowarn_store_extern_call (void)
+{
+  int x;
+  evp = &x;
+  sink (0);
+}
+
+void nowarn_store_extern_ovrwrite (void)
+{
+  int x;
+  evp = &x;
+  evp = 0;
+}
+
+void nowarn_store_extern_store (void)
+{
+  int x;
+  void **p = (void**)sink (&evp);
+  evp = &x;
+  *p = 0;
+}
+
+
+void warn_store_extern (void)
+{
+  extern void *evp1;  // { dg-message "'evp1' declared here" }
+  int x;              // { dg-message "'x' declared here" }
+  evp1 = &x;          // { dg-warning "storing the address of local variable 'x' in 'evp1'" }
+}
+
+
+void nowarn_store_arg_call (void **vpp)
+{
+  int x;
+  *vpp = &x;
+  sink (0);
+}
+
+void nowarn_store_arg_ovrwrite (void **vpp)
+{
+  int x;
+  *vpp = &x;
+  *vpp = 0;
+}
+
+void nowarn_store_arg_store (void **vpp)
+{
+  int x;
+  void **p = (void**)sink (0);
+  *vpp = &x;
+  *p = 0;
+}
+
+void* nowarn_store_arg_store_arg (void **vpp1, void **vpp2)
+{
+  int x;
+  void **p = (void**)sink (0);
+  *vpp1 = &x;         // warn here?
+  *vpp2 = 0;          // might overwrite *vpp1
+  return p;
+}
+
+void warn_store_arg (void **vpp)
+{
+  int x;              // { dg-message "'x' declared here" }
+  *vpp = &x;          // { dg-warning "storing the address of local variable 'x' in '\\*vpp'" }
+}
+
+
diff --git a/gcc/testsuite/c-c++-common/Wdangling-pointer.c b/gcc/testsuite/c-c++-common/Wdangling-pointer.c
new file mode 100644
index 00000000000..a65404f05c2
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/Wdangling-pointer.c
@@ -0,0 +1,414 @@ 
+/* PR middle-end/63272 - GCC should warn when using pointer to dead scoped
+   variable within the same function
+   Exercise basic cases of -Wdangling-pointer without optimization.
+   { dg-do compile }
+   { dg-options "-O0 -Wall -Wno-uninitialized -ftrack-macro-expansion=0" } */
+
+typedef __INTPTR_TYPE__ intptr_t;
+typedef __SIZE_TYPE__   size_t;
+
+#if __cplusplus
+#  define EXTERN_C extern "C"
+#else
+#  define EXTERN_C extern
+#endif
+
+EXTERN_C void* alloca (size_t);
+EXTERN_C void* malloc (size_t);
+EXTERN_C void* memchr (const void*, int, size_t);
+EXTERN_C char* strchr (const char*, int);
+
+int sink (const void*, ...);
+#define sink(...) sink (0, __VA_ARGS__)
+
+
+void nowarn_addr (void)
+{
+  int *p;
+  {
+    int a[] = { 1, 2, 3 };
+    p = a;
+  }
+
+  // This is suspect but not a clear error.
+  sink (&p);
+}
+
+
+char* nowarn_ptr (void)
+{
+  char *p;
+  sink (&p);
+  return p;
+}
+
+
+char* nowarn_cond_ptr (void)
+{
+  // Distilled from a false positive in Glibc dlerror.c.
+  char *q;
+  if (sink (&q))
+    return q;
+
+  return 0;
+}
+
+
+void nowarn_loop_ptr (int n, int *p)
+{
+  // Distilled from a false positive in Glibc td_thr_get_info.c.
+  for (int i = 0; i != 2; ++i)
+    {
+      int x;
+      sink (&x);
+      *p++ = x;
+    }
+}
+
+
+void nowarn_intptr_t (void)
+{
+  intptr_t ip;
+  {
+    int a[] = { 1, 2, 3 };
+    ip = (intptr_t)a;
+  }
+
+  // Using an intptr_t is not diagnosed.
+  sink (0, ip);
+}
+
+
+void nowarn_string_literal (void)
+{
+  const char *s;
+  {
+    s = "123";
+  }
+
+  sink (s);
+}
+
+
+void nowarn_extern_array (int x)
+{
+  {
+    /* This is a silly sanity check.  */
+    extern int eia[];
+    int *p;
+    {
+      p = eia;
+    }
+    sink (p);
+  }
+}
+
+
+void nowarn_static_array (int x)
+{
+  {
+    const char *s;
+    {
+      static const char sca[] = "123";
+      s = sca;
+    }
+
+    sink (s);
+  }
+  {
+    const int *p;
+    {
+      static const int sia[] = { 1, 2, 3 };
+      p = sia;
+    }
+
+    sink (p);
+  }
+  {
+    const int *p;
+    {
+      static const int sia[] = { 1, 2, 3 };
+      p = (const int*)memchr (sia, x, sizeof sia);
+    }
+
+    sink (p);
+  }
+}
+
+
+void nowarn_alloca (unsigned n)
+{
+  {
+    char *p;
+    {
+      p = (char*)alloca (n);
+    }
+    sink (p);
+  }
+  {
+    int *p;
+    {
+      p = (int*)alloca (n * sizeof *p);
+      sink (p);
+    }
+    sink (p);
+  }
+  {
+    long *p;
+    {
+      p = (long*)alloca (n * sizeof *p);
+      sink (p);
+      p = p + 1;
+    }
+    sink (p);
+  }
+}
+
+
+#pragma GCC diagnostic push
+/* Verify that -Wdangling-pointer works with #pragma diagnostic.  */
+#pragma GCC diagnostic ignored "-Wdangling-pointer"
+
+void nowarn_scalar_call_ignored (void *vp)
+{
+  int *p;
+  {
+    int i;
+    p = &i;
+  }
+  sink (p);
+}
+
+#pragma GCC diagnostic pop
+
+
+void* nowarn_return_local_addr (void)
+{
+  int a[] = { 1, 2, 3 };
+  int *p = a;
+
+  /* This is a likely bug but it's not really one of using a dangling
+     pointer but rather of returning the address of a local variable
+     which is diagnosed by -Wreturn-local-addr.  */
+  return p;
+}
+
+void* warn_return_local_addr (void)
+{
+  int *p = 0;
+  {
+    int a[] = { 1, 2, 3 };
+    p = a;
+  }
+
+  /* Unlike the above case, here the pointer is dangling when it's
+     used.  */
+  return p;                   // { dg-warning "using dangling pointer 'p' to 'a'" "array" }
+}
+
+
+void* nowarn_return_alloca (int n)
+{
+  int *p = (int*)alloca (n);
+  sink (p);
+
+  /* This is a likely bug but it's not really one of using a dangling
+     pointer but rather of returning the address of a local variable
+     which is diagnosed by -Wreturn-local-addr.  */
+  return p;
+}
+
+
+void warn_scalar_call (void)
+{
+  int *p;
+  {
+    int i;                    // { dg-message "'i' declared" "note" }
+    p = &i;
+  }
+  sink (p);                   // { dg-warning "using dangling pointer 'p' to 'i'" "array" }
+}
+
+
+void warn_array_call (void)
+{
+  int *p;
+  {
+    int a[] = { 1, 2, 3 };    // { dg-message "'a' declared" "note" }
+    p = a;
+  }
+  sink (p);                   // { dg-warning "using dangling pointer 'p' to 'a'" "array" }
+}
+
+
+void* warn_array_return (void)
+{
+  int *p;
+  {
+    int a[] = { 1, 2, 3 };    // { dg-message "'a' declared" "note" }
+    p = a;
+  }
+  return p;                   // { dg-warning "using dangling pointer 'p' to 'a'" "array" }
+}
+
+
+void warn_pr63272_c1 (int i)
+{
+  int *p = 0;
+
+  if (i)
+    {
+      int k = i;              // { dg-message "'k' declared" "note" }
+      p = &k;
+    }
+
+  sink (p ? *p : 0);          // { dg-warning "dangling pointer 'p' to 'k' may be used" }
+}
+
+
+void warn_pr63272_c4 (void)
+{
+  int *p = 0;
+
+  {
+    int b;                    // { dg-message "'b' declared" "note" }
+    p = &b;
+  }
+
+  sink (p);                   // { dg-warning "using dangling pointer 'p' to 'b'" "scalar" }
+}
+
+void nowarn_cond_if (int i, int n)
+{
+  int *p;
+  if (i)
+    {
+      int a[] = { 1, 2 };
+      p = a;
+      sink (p);
+    }
+  else
+   {
+     int *b = (int*)malloc (n);
+     p = b;
+     sink (p);
+   }
+
+  p = 0;
+}
+
+
+void warn_cond_if (int i, int n)
+{
+  int *p;
+  if (i)
+    {
+      int a[] = { 1, 2 };     // { dg-message "'a' declared" "note" }
+      sink (a);
+      p = a;
+    }
+  else
+   {
+     int *b = (int*)malloc (n);
+     sink (b);
+     p = b;
+   }
+
+  sink (p);                   // { dg-warning "dangling pointer 'p' to 'a' may be used" }
+}
+
+
+void warn_cond_else (int i, int n)
+{
+  int *p;
+  if (i)
+    {
+      int *a = (int*)malloc (n);
+      sink (a);
+      p = a;
+    }
+  else
+   {
+     int b[] = { 2, 3 };
+     sink (b);
+     p = b;
+   }
+
+  sink (p);                   // { dg-warning "dangling pointer 'p' to 'b' may be used" }
+}
+
+
+void warn_cond_if_else (int i)
+{
+  int *p;
+  if (i)
+    {
+      int a[] = { 1, 2 };     // { dg-message "'a' declared" "note" }
+      sink (a);
+      p = a;
+    }
+  else
+   {
+     int b[] = { 3, 4 };      // { dg-message "'b' declared" "note" { xfail *-*-* } }
+     sink (b);
+     p = b;
+   }
+
+  /* With a PHI with more than invalid argument, only one use is diagnosed
+     because after the first diagnostic the code suppresses subsequent
+     ones for the same use.  This needs to be fixed.  */
+  sink (p);                   // { dg-warning "dangling pointer 'p' to 'a' may be used" }
+                              // { dg-warning "dangling pointer 'p' to 'b' may be used" "pr??????" { xfail *-*-* } .-1 }
+}
+
+
+void nowarn_gcc_i386 (int i)
+{
+  // Regression test reduced from gcc's i386.c.
+  char a[32], *p;
+
+  if (i != 1)
+    p = a;
+  else
+    p = 0;
+
+  if (i == 2)
+    sink (p);
+  else
+    {
+      if (p)
+	{
+	  sink (p);
+	  return;
+	}
+      sink (p);
+    }
+}
+
+
+void warn_memchr (char c1, char c2, char c3, char c4)
+{
+  char *p = 0;
+  {
+    char a[] = { c1, c2, c3 };// { dg-message "'a' declared" "note" }
+    p = (char*)memchr (a, c4, 3);
+    if (!p)
+      return;
+  }
+
+  sink (p);                   // { dg-warning "using dangling pointer 'p' to 'a'" }
+}
+
+
+void warn_strchr (char c1, char c2, char c3, char c4)
+{
+  char *p = 0;
+  {
+    char a[] = { c1, c2, c3 }; // { dg-message "'a' declared" "note" }
+    p = (char*)strchr (a, c4);
+    if (!p)
+      return;
+  }
+
+  sink (p);                   // { dg-warning "using dangling pointer 'p' to 'a'" }
+}
diff --git a/gcc/testsuite/g++.dg/warn/Wdangling-pointer.C b/gcc/testsuite/g++.dg/warn/Wdangling-pointer.C
new file mode 100644
index 00000000000..54950bfb0e2
--- /dev/null
+++ b/gcc/testsuite/g++.dg/warn/Wdangling-pointer.C
@@ -0,0 +1,34 @@ 
+/* { dg-do compile }
+   { dg-options "-Wall -Wno-class-memaccess" } */
+
+extern "C" void* memset (void*, int, __SIZE_TYPE__);
+
+void sink (void*);
+
+struct S { S (); };
+
+void nowarn_array_access ()
+{
+  /* Verify that the clobber in the exceptional basic block doesn't
+     cause bogus warnings.  */
+  S a[1];
+  memset (a, 0, sizeof a);
+  sink (a);
+}
+
+
+void nowarn_array_access_cond (int i)
+{
+  if (i)
+    {
+      S a1[1];
+      memset (a1, 0, sizeof a1);
+      sink (a1);
+    }
+  else
+    {
+      S a2[2];
+      memset (a2, 0, sizeof a2);
+      sink (a2);
+    }
+}
diff --git a/gcc/testsuite/g++.dg/warn/Wfree-nonheap-object-6.C b/gcc/testsuite/g++.dg/warn/Wfree-nonheap-object-6.C
index 83b6ff9157c..91a87786ae0 100644
--- a/gcc/testsuite/g++.dg/warn/Wfree-nonheap-object-6.C
+++ b/gcc/testsuite/g++.dg/warn/Wfree-nonheap-object-6.C
@@ -1,5 +1,5 @@ 
 /* { dg-do compile }
-   { dg-options "-O0 -Wall" } */
+   { dg-options "-O0 -Wall -Wno-dangling-pointer -Wno-return-local-address" } */
 
 #if __cplusplus < 201103L
 # define noexcept throw ()
@@ -18,6 +18,8 @@  extern void *p;
 void nowarn_placement_new ()
 {
   char a[sizeof (A)];
+  /* The store to the global p might trigger -Wdangling pointer or
+     -Wreturn-local-address (if/when it runs without optimization).  */
   p = new (a) A ();           // { dg-bogus "-Wfree-nonheap-object" }
 }
 
diff --git a/gcc/testsuite/gcc.dg/Wdangling-pointer-2.c b/gcc/testsuite/gcc.dg/Wdangling-pointer-2.c
new file mode 100644
index 00000000000..b5882fef69d
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/Wdangling-pointer-2.c
@@ -0,0 +1,82 @@ 
+/* Exercise conditional C-only uses of dangling pointers with optimization.
+   { dg-do compile }
+   { dg-options "-O2 -Wall" } */
+
+typedef __SIZE_TYPE__ size_t;
+
+extern void* memchr (const void*, int, size_t);
+extern char* strchr (const char*, int);
+
+void sink (void*, ...);
+
+
+void nowarn_compound_literal (int i, int j)
+{
+  {
+    int *p = i ? (int[]){ 1, 2, 3 } : (int[]){ 4, 5, 6 };
+    sink (p);
+  }
+  {
+    int a[] = { 1, 2, 3 };
+    int *q = i ? (int[]){ 4, 5, 6 } : a;
+    int *p = &q[1];
+    sink (p);
+  }
+  {
+    int *p = i ? (int[]){ 1, 2, 3 } : (int[]){ 4, 5, 6 };
+    int *q = __builtin_memchr (p, 2, 3 * sizeof *p);
+    sink (q);
+  }
+  {
+    int a[] = { i, i + 1, i + 2, 3 };
+    int *p = i ? (int[]){ j, j + 1, j + 2, 3 } : a;
+    int *q = __builtin_memchr (p, 3, 4 * sizeof *p);
+    sink (q);
+  }
+}
+
+
+void warn_maybe_compound_literal (int i, int j)
+{
+  int a[] = { 1, 2, 3 }, *p;
+  {
+    p = i ? (int[]){ 4, 5, 6 } : a;
+  }
+  // When the 'p' is optimized away it's not mentioned in the warning.
+  sink (p);         // { dg-warning "dangling pointer \('p' \)?to a compound literal may be used" }
+}
+
+
+void warn_maybe_compound_literal_memchr (int i, int j, int x)
+{
+  int a[] = { 1, 2, 3 }, *p;
+  {
+    int *q = i ? (int[]){ 4, 5, 6 } : a;
+    p = memchr (q, x, 3 * sizeof *q);
+  }
+  sink (p);         // { dg-warning "dangling pointer 'p' to a compound literal may be used" }
+}
+
+
+void warn_maybe_array (int i, int j)
+{
+  int a[] = { 1, 2, 3 }, *p;
+  {
+    int b[] = { 4, 5, 6 };
+    p = i ? a : b;
+  }
+  // When the 'p' is optimized away it's not mentioned in the warning.
+  sink (p);         // { dg-warning "dangling pointer \('p' \)?to 'b' may be used" }
+}
+
+
+void warn_maybe_array_memchr (int i, int j, int x)
+{
+  int a[] = { 1, 2, 3 }, *p;
+  {
+    int b[] = { 4, 5, 6 };
+    int *q = i ? a : b;
+    p = memchr (q, x, 3 * sizeof *q);
+  }
+  sink (p);         // { dg-warning "dangling pointer 'p' to 'b' may be used" }
+}
diff --git a/gcc/testsuite/gcc.dg/Wdangling-pointer.c b/gcc/testsuite/gcc.dg/Wdangling-pointer.c
new file mode 100644
index 00000000000..e32ac7d7a46
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/Wdangling-pointer.c
@@ -0,0 +1,62 @@ 
+/* Exercise basic C-only cases of -Wdangling-pointer.
+   { dg-do compile }
+   { dg-options "-O0 -Wall" } */
+
+typedef __SIZE_TYPE__ size_t;
+
+extern void* memchr (const void*, int, size_t);
+extern char* strchr (const char*, int);
+
+void sink (const void*, ...);
+
+
+void nowarn_compound_literal (int i)
+{
+  {
+    int *p = (int[]){ 1, 2, 3 };
+    sink (p);
+  }
+  {
+    int *q = (int[]){ 1, 2, 3 };
+    int *p = &q[1];
+    sink (p);
+  }
+  {
+    int *p = __builtin_memchr ((int[]){ 1, 2, 3 }, 2, 3 * sizeof *p);
+    sink (p);
+  }
+  {
+    int *p = __builtin_memchr ((int[]){ i, i + 1 }, 3, 2 * sizeof *p);
+    sink (p);
+  }
+}
+
+
+void warn_compound_literal (int i)
+{
+  int *p;
+  {
+    p = (int[]){ 1, 2, 3 };   // { dg-message "compound literal" },
+  }
+  sink (p);                   // { dg-warning "using dangling pointer 'p' to a compound literal" }
+
+  {
+    int *q =
+      (int[]){ 1, 2, 3 };     // { dg-message "compound literal" },
+    p = &q[1];
+  }
+  sink (p);                   // { dg-warning "using dangling pointer 'p' to a compound literal" }
+  {
+    p = (int*)memchr (
+	  (int[]){ 1, 2, 3 }, // { dg-message "compound literal" }
+	  2, 3 * sizeof *p);
+  }
+  sink (p);                   // { dg-warning "using dangling pointer 'p' to a compound literal" }
+
+  {
+    p = (int*)memchr (
+	  (int[]){ i, i + 1 },// { dg-message "compound literal" }
+	  3, 2 * sizeof *p);
+  }
+  sink (p);                   // { dg-warning "using dangling pointer 'p' to a compound literal" }
+}