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:

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 a5fe00ed195..6aa04721075 100644
--- a/gcc/c-family/c.opt
+++ b/gcc/c-family/c.opt
@@ -524,6 +524,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 0d68af4d91e..ac2ec0c13ce 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 eb4ecb56dcc..cdbd9991da2 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -339,7 +339,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
@@ -5667,6 +5668,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
@@ -8503,6 +8505,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-isolate-paths.c b/gcc/gimple-ssa-isolate-paths.c
index 2dafe849ad3..3a6e28b6926 100644
--- a/gcc/gimple-ssa-isolate-paths.c
+++ b/gcc/gimple-ssa-isolate-paths.c
@@ -415,6 +415,7 @@ diag_returned_locals (bool maybe, const locmap_t &locmap)
 	{
 	  for (unsigned i = 0; i != nargs; ++i)
 	    inform (argsloc.locvec[i], "declared here");
+	  suppress_warning (stmt, OPT_Wreturn_local_addr);
 	}
     }
 }
diff --git a/gcc/gimple-ssa-warn-access.cc b/gcc/gimple-ssa-warn-access.cc
index 2065402a2b9..da88c2ad421 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,17 +2112,37 @@ 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);
 
-/* A pointer_query object and its cache to store information about
+  /* 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;
+  /* The current function.  */
+  function *m_func;
+  /* The 1-based invocation number of the pass.  */
+  unsigned m_run_number;
+  /* 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.  */
@@ -2125,10 +2150,25 @@ private:
 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_var_cache (),
+    m_clobbers (),
+    m_func (),
+    m_run_number (1),
+    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 ()
+{
+  pass_waccess *p = new pass_waccess (m_ctxt);
+  p->m_run_number = m_run_number + 1;
+  return p;
+}
+
 /* Release pointer_query cache.  */
 
 pass_waccess::~pass_waccess ()
@@ -2136,6 +2176,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
@@ -2324,6 +2372,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
@@ -2345,6 +2396,9 @@ 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;
@@ -2395,6 +2449,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;
 
@@ -2420,6 +2477,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;
 
@@ -2489,6 +2549,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);
 
@@ -2527,7 +2590,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);
@@ -2551,7 +2614,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);
@@ -2656,6 +2719,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
@@ -2677,7 +2743,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)))
@@ -2805,11 +2871,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:
@@ -3487,12 +3554,15 @@ gimple_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 */)
 {
@@ -3504,7 +3574,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
@@ -3538,6 +3608,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
@@ -3660,10 +3776,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);
 
@@ -3672,7 +3792,7 @@ pass_waccess::check_pointer_uses (gimple *stmt, tree ptr)
 
   /* If the deallocation (or clobber) statement dominates more than
      a single basic block issue a "maybe" kind of warning.  */
-  bool maybe = !single_succ_p (stmt_bb);
+  maybe |= !single_succ_p (stmt_bb);
 
   /* If STMT is a reallocation function set to the reallocated pointer
      and the LHS of the call, respectively.  */
@@ -3752,13 +3872,19 @@ pass_waccess::check_pointer_uses (gimple *stmt, tree ptr)
 	     of any other pointers derived from it can be checked.  */
 	  if (gimple_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)
 		{
-		  warn_invalid_pointer (*use_p->use, use_stmt, stmt,
-					maybe, equality);
-		  continue;
+		  tree lhs = gimple_phi_result (use_stmt);
+		  if (TREE_CODE (lhs) == SSA_NAME)
+		    {
+		      pointers.safe_push (lhs);
+		      continue;
+		    }
 		}
+		
+	      warn_invalid_pointer (*use_p->use, use_stmt, stmt, var,
+				    maybe, equality);
+	      continue;
 	    }
 
 	  if (is_gimple_assign (use_stmt))
@@ -3793,26 +3919,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 (!gimple_use_after_inval_p (*pclobber, stmt))
+	return;
+
+      warn_invalid_pointer (NULL_TREE, stmt, *pclobber, arg, false);
+    }
+}
+
 /* Check basic block BB for invalid accesses.  */
 
 void
@@ -3825,6 +4024,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);
     }
 }
 
@@ -3873,6 +4074,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 (!gimple_use_after_inval_p (*pclobber, call))
+	continue;
+
+      warn_invalid_pointer (NULL_TREE, call, *pclobber, arg, false);
+    }
+}
+
 /* Check function FUN for invalid accesses.  */
 
 unsigned
@@ -3882,11 +4309,23 @@ pass_waccess::execute (function *fun)
 
   /* Create a new ranger instance and associate it with FUN.  */
   m_ptr_qry.rvals = enable_ranger (fun);
+  m_func = fun;
+
+  /* Check for dangling pointers in the earliest run of the pass, either
+     the first run when optimization is disabled, or in the second run
+     when  it's enabled.  */
+  m_check_dangling_p = m_early_checks_p && (m_run_number == 1 + !!optimize);
 
   basic_block bb;
   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);
 
@@ -3897,6 +4336,8 @@ pass_waccess::execute (function *fun)
   disable_ranger (fun);
   m_ptr_qry.rvals = NULL;
 
+  m_clobbers.empty ();
+
   free_dominance_info (CDI_DOMINATORS);
   return 0;
 }
diff --git a/gcc/passes.def b/gcc/passes.def
index 0f541454e7f..1374302cab9 100644
--- a/gcc/passes.def
+++ b/gcc/passes.def
@@ -63,6 +63,8 @@ along with GCC; see the file COPYING3.  If not see
       NEXT_PASS (pass_ubsan);
       NEXT_PASS (pass_nothrow);
       NEXT_PASS (pass_rebuild_cgraph_edges);
+      /* This instance runs only when optimization is disabled.  */
+      NEXT_PASS (pass_warn_access, /*early=*/true);
   POP_INSERT_PASSES ()
 
   NEXT_PASS (pass_local_optimization_passes);
@@ -200,6 +202,9 @@ along with GCC; see the file COPYING3.  If not see
 	 form if possible.  */
       NEXT_PASS (pass_object_sizes);
       NEXT_PASS (pass_post_ipa_warn);
+      /* This early instance runs only with optimization enabled.
+      	 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);
@@ -421,7 +426,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..206aab6aa91
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/Wdangling-pointer-2.c
@@ -0,0 +1,352 @@
+/* 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 -fno-isolate-erroneous-paths-dereference -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);
+
+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_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" "pr63272" { xfail *-*-* } }
+      p = &k;
+    }
+
+  sink (p ? *p : 0);          // { dg-warning "using \(a \)?dangling pointer \('p' \)?to 'k'" "pr63272" { xfail *-*-* } }
+}
+
+
+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" }
+}
+
+
+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" "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 }
+}
+
+
+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);
+    }
+}
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..bf087df9571
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/Wdangling-pointer.c
@@ -0,0 +1,348 @@
+/* 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);
+
+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 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);
+    }
+}
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" }
+}
diff --git a/gcc/testsuite/gcc.dg/uninit-pr50476.c b/gcc/testsuite/gcc.dg/uninit-pr50476.c
index db4146db314..37201841ad5 100644
--- a/gcc/testsuite/gcc.dg/uninit-pr50476.c
+++ b/gcc/testsuite/gcc.dg/uninit-pr50476.c
@@ -7,7 +7,7 @@ int *x = 0;
 void f (void)
 {
   int y = 1;
-  x = &y;
+  x = &y;       // { dg-warning "\\\[-Wdangling-pointer" }
 }
 
 int g (void)
