aboutsummaryrefslogtreecommitdiff
path: root/gcc
diff options
context:
space:
mode:
authorMartin Sebor <msebor@redhat.com>2022-01-15 16:41:40 -0700
committerMartin Sebor <msebor@redhat.com>2022-01-15 16:45:56 -0700
commit9d6a0f388eb048f8d87f47af78f07b5ce513bfe6 (patch)
tree2faf3aed7f6102a234d4afa5e4b727925a2249d8 /gcc
parent671a283636de75f7ed638ee6b01ed2d44361b8b6 (diff)
downloadgcc-9d6a0f388eb048f8d87f47af78f07b5ce513bfe6.zip
gcc-9d6a0f388eb048f8d87f47af78f07b5ce513bfe6.tar.gz
gcc-9d6a0f388eb048f8d87f47af78f07b5ce513bfe6.tar.bz2
Add -Wdangling-pointer [PR63272].
Resolves: PR c/63272 - GCC should warn when using pointer to dead scoped variable with in 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-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. * g++.dg/warn/ref-temp1.C: Prune expected warning. * 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-6.c: New test. * c-c++-common/Wdangling-pointer.c: New test. * g++.dg/warn/Wdangling-pointer-2.C: New test. * g++.dg/warn/Wdangling-pointer.C: New test. * gcc.dg/Wdangling-pointer-2.c: New test. * gcc.dg/Wdangling-pointer.c: New test.
Diffstat (limited to 'gcc')
-rw-r--r--gcc/c-family/c.opt8
-rw-r--r--gcc/diagnostic-spec.c1
-rw-r--r--gcc/doc/invoke.texi62
-rw-r--r--gcc/gimple-ssa-warn-access.cc635
-rw-r--r--gcc/passes.def5
-rw-r--r--gcc/testsuite/c-c++-common/Wdangling-pointer-2.c437
-rw-r--r--gcc/testsuite/c-c++-common/Wdangling-pointer-3.c64
-rw-r--r--gcc/testsuite/c-c++-common/Wdangling-pointer-4.c73
-rw-r--r--gcc/testsuite/c-c++-common/Wdangling-pointer-5.c90
-rw-r--r--gcc/testsuite/c-c++-common/Wdangling-pointer-6.c32
-rw-r--r--gcc/testsuite/c-c++-common/Wdangling-pointer.c434
-rw-r--r--gcc/testsuite/g++.dg/warn/Wdangling-pointer-2.C23
-rw-r--r--gcc/testsuite/g++.dg/warn/Wdangling-pointer.C74
-rw-r--r--gcc/testsuite/g++.dg/warn/Wfree-nonheap-object-6.C4
-rw-r--r--gcc/testsuite/g++.dg/warn/ref-temp1.C3
-rw-r--r--gcc/testsuite/gcc.dg/Wdangling-pointer-2.c82
-rw-r--r--gcc/testsuite/gcc.dg/Wdangling-pointer.c75
-rw-r--r--gcc/testsuite/gcc.dg/uninit-pr50476.c2
18 files changed, 2043 insertions, 61 deletions
diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt
index 2836364..db65c14 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 c9e1c1b..a8af229 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 121c8ea..7f2205e 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
@@ -4389,6 +4390,8 @@ Warn about overriding virtual functions that are not marked with the
@opindex Wno-use-after-free
Warn about uses of pointers to dynamically allocated objects that have
been rendered indeterminate by a call to a deallocation function.
+The warning is enabled at all optimization levels but may yield different
+results with optimization than without.
@table @gcctabopt
@item -Wuse-after-free=1
@@ -5714,6 +5717,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
@@ -8587,6 +8591,62 @@ 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 (or C++ references) to objects with automatic
+storage duration after their lifetime has ended. This includes local
+variables declared in nested blocks, compound literals and other unnamed
+temporary objects. In addition, warn about storing the address of such
+objects in escaped pointers. The warning is enabled at all optimization
+levels but may yield different results with optimization than without.
+
+@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
+In the following function the store of the address of the local variable
+@code{x} in the escaped pointer @code{*p} also triggers the warning.
+@smallexample
+void g (int **p)
+@{
+ int x = 7;
+ *p = &x; // warning: storing the address of a local variable in *p
+@}
+@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 8821291..f639807 100644
--- a/gcc/gimple-ssa-warn-access.cc
+++ b/gcc/gimple-ssa-warn-access.cc
@@ -2069,10 +2069,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:
@@ -2089,6 +2091,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);
@@ -2112,26 +2117,41 @@ private:
void check_atomic_memmodel (gimple *, tree, tree, const unsigned char *);
/* 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, 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. */
@@ -2140,11 +2160,22 @@ 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. */
pass_waccess::~pass_waccess ()
@@ -2152,6 +2183,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
@@ -2340,6 +2379,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
@@ -2361,6 +2403,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))
{
@@ -2413,6 +2462,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;
@@ -2438,6 +2490,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;
@@ -2507,6 +2562,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);
@@ -2545,7 +2603,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);
@@ -2569,7 +2627,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);
@@ -2674,6 +2732,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
@@ -2695,7 +2756,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)))
@@ -2938,7 +2999,7 @@ pass_waccess::check_atomic_memmodel (gimple *stmt, tree ord_sucs,
if (warning_suppressed_p (stmt, OPT_Winvalid_memory_model))
return;
- if (maybe_warn_memmodel (stmt, ord_sucs, ord_fail, valid))
+ if (!maybe_warn_memmodel (stmt, ord_sucs, ord_fail, valid))
return;
suppress_warning (stmt, OPT_Winvalid_memory_model);
@@ -3094,11 +3155,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:
@@ -3725,16 +3787,67 @@ 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 false;
+
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
@@ -3752,27 +3865,30 @@ pass_waccess::use_after_inval_p (gimple *inval_stmt, gimple *use_stmt)
return gimple_uid (inval_stmt) < gimple_uid (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. */
+/* Issue a warning for the USE_STMT of pointer or reference REF rendered
+ invalid by INVAL_STMT. REF may be null when it's been optimized away.
+ When nonnull, INVAL_STMT is the deallocation function that rendered
+ the pointer or reference dangling. Otherwise, VAR is the auto variable
+ (including an unnamed temporary such as a 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,
- bool maybe,
- bool equality /* = false */)
+pass_waccess::warn_invalid_pointer (tree ref, gimple *use_stmt,
+ gimple *inval_stmt, tree var,
+ bool maybe, bool equality /* = false */)
{
/* Avoid printing the unhelpful "<unknown>" in the diagnostics. */
- if (ptr && TREE_CODE (ptr) == SSA_NAME
- && (!SSA_NAME_VAR (ptr) || DECL_ARTIFICIAL (SSA_NAME_VAR (ptr))))
- ptr = NULL_TREE;
+ if (ref && TREE_CODE (ref) == SSA_NAME
+ && (!SSA_NAME_VAR (ref) || DECL_ARTIFICIAL (SSA_NAME_VAR (ref))))
+ ref = NULL_TREE;
location_t use_loc = gimple_location (use_stmt);
if (use_loc == UNKNOWN_LOCATION)
{
- use_loc = cfun->function_end_locus;
- if (!ptr)
+ use_loc = m_func->function_end_locus;
+ if (!ref)
/* Avoid issuing a warning with no context other than
the function. That would make it difficult to debug
in any but very simple cases. */
@@ -3788,12 +3904,12 @@ pass_waccess::warn_invalid_pointer (tree ptr, gimple *use_stmt,
const tree inval_decl = gimple_call_fndecl (inval_stmt);
- if ((ptr && warning_at (use_loc, OPT_Wuse_after_free,
+ if ((ref && warning_at (use_loc, OPT_Wuse_after_free,
(maybe
? G_("pointer %qE may be used after %qD")
: G_("pointer %qE used after %qD")),
- ptr, inval_decl))
- || (!ptr && warning_at (use_loc, OPT_Wuse_after_free,
+ ref, inval_decl))
+ || (!ref && warning_at (use_loc, OPT_Wuse_after_free,
(maybe
? G_("pointer may be used after %qD")
: G_("pointer used after %qD")),
@@ -3805,6 +3921,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 ((ref
+ && warning_at (use_loc, OPT_Wdangling_pointer_,
+ (maybe
+ ? G_("dangling pointer %qE to %qD may be used")
+ : G_("using dangling pointer %qE to %qD")),
+ ref, var))
+ || (!ref
+ && 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 ((ref
+ && warning_at (use_loc, OPT_Wdangling_pointer_,
+ (maybe
+ ? G_("dangling pointer %qE to an unnamed temporary "
+ "may be used")
+ : G_("using dangling pointer %qE to an unnamed "
+ "temporary")),
+ ref, var))
+ || (!ref
+ && warning_at (use_loc, OPT_Wdangling_pointer_,
+ (maybe
+ ? G_("dangling pointer to an unnamed temporary "
+ "may be used")
+ : G_("using a dangling pointer to an unnamed "
+ "temporary")),
+ var)))
+ {
+ inform (DECL_SOURCE_LOCATION (var),
+ "unnamed temporary defined here");
+ suppress_warning (use_stmt, OPT_Wdangling_pointer_);
+ }
}
/* If STMT is a call to either the standard realloc or to a user-defined
@@ -3927,10 +4089,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);
@@ -4013,18 +4179,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))
@@ -4059,26 +4232,100 @@ 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 unnamed temporaries such as 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
@@ -4091,6 +4338,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);
}
}
@@ -4139,6 +4388,262 @@ 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. OBJREF is true when VAR denotes
+ an access to a DECL that may have been clobbered. */
+
+void
+pass_waccess::check_dangling_uses (tree var, tree decl, bool maybe /* = false */,
+ bool objref /* = false */)
+{
+ if (!decl || !is_auto_decl (decl))
+ return;
+
+ gimple **pclob = m_clobbers.get (decl);
+ if (!pclob)
+ return;
+
+ if (!objref)
+ {
+ check_pointer_uses (*pclob, var, decl, maybe);
+ return;
+ }
+
+ gimple *use_stmt = SSA_NAME_DEF_STMT (var);
+ if (!use_after_inval_p (*pclob, use_stmt, true))
+ return;
+
+ basic_block use_bb = gimple_bb (use_stmt);
+ basic_block clob_bb = gimple_bb (*pclob);
+ maybe = maybe || !dominated_by_p (CDI_POST_DOMINATORS, use_bb, clob_bb);
+ warn_invalid_pointer (var, use_stmt, *pclob, decl, maybe, false);
+}
+
+/* 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;
+
+ /* FIXME: Handle stores of alloca() and VLA. */
+ 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)
+ {
+ /* 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)
+ {
+ if (!POINTER_TYPE_P (TREE_TYPE (var)))
+ continue;
+ decl = TREE_OPERAND (rhs, 0);
+ }
+ else
+ {
+ /* For other expressions, check the base DECL to see
+ if it's been clobbered, most likely as a result of
+ inlining a reference to it. */
+ decl = get_base_address (rhs);
+ if (DECL_P (decl))
+ check_dangling_uses (var, decl, false, true);
+ continue;
+ }
+ }
+ else if (POINTER_TYPE_P (TREE_TYPE (var)))
+ {
+ 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
@@ -4151,6 +4656,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 -Wdangling-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;
@@ -4160,6 +4674,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);
@@ -4170,6 +4690,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 1f77ab5..7880842 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 0000000..527e5e7
--- /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 0000000..d2f8f43
--- /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 0000000..e57e66f
--- /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 0000000..1f549ca
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/Wdangling-pointer-5.c
@@ -0,0 +1,90 @@
+/* PR middle-end/63272 - GCC should warn when using pointer to dead scoped
+ variable within the same function
+ Exercise -Wdangling-pointer for escaping stores of addreses of auto
+ variables.
+ { dg-do compile }
+ { dg-options "-O0 -Wall -ftrack-macro-expansion=0" } */
+
+void* alloca (__SIZE_TYPE__);
+
+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_alloca (int n)
+{
+ // This fails because of a bug in the warning.
+ void *p = alloca (n);
+ evp = p; // { dg-warning "storing the address of local variable 'x' in 'evp1'" "pr??????" { xfail *-*-* } }
+}
+
+
+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-6.c b/gcc/testsuite/c-c++-common/Wdangling-pointer-6.c
new file mode 100644
index 0000000..9c05891
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/Wdangling-pointer-6.c
@@ -0,0 +1,32 @@
+/* PR middle-end/63272 - GCC should warn when using pointer to dead scoped
+ variable within the same function
+ Exercise -Wdangling-pointer with inlining.
+ { dg-do compile }
+ { dg-options "-O1 -Wall" } */
+
+void* sink (void*, ...);
+
+extern int *eip; // { dg-message "'eip' declared here" }
+
+static inline void store (int **p, int *q)
+{
+ *p = q; // { dg-warning "storing the address of local variable 'auto_x' in 'eip'" }
+}
+
+void nowarn_inlined_store_extern (void)
+{
+ extern int extern_x;
+ store (&eip, &extern_x);
+}
+
+void nowarn_inlined_store_static (void)
+{
+ static int static_x;
+ store (&eip, &static_x);
+}
+
+void warn_inlined_store_auto (void)
+{
+ int auto_x; // { dg-message "'auto_x' declared here" }
+ store (&eip, &auto_x);
+}
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 0000000..394ff92
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/Wdangling-pointer.c
@@ -0,0 +1,434 @@
+/* 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__)
+
+/* Verify that integer assignments don't cause bogus warnings.
+ Reduced from GFlibc's s_nextafter.c. */
+
+int nowarn_integer (float x)
+{
+ int i;
+
+ {
+ union
+ {
+ float x;
+ int i;
+ } u;
+
+ u.x = x;
+ i = u.i;
+ }
+
+ return i;
+}
+
+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-2.C b/gcc/testsuite/g++.dg/warn/Wdangling-pointer-2.C
new file mode 100644
index 0000000..151418f
--- /dev/null
+++ b/gcc/testsuite/g++.dg/warn/Wdangling-pointer-2.C
@@ -0,0 +1,23 @@
+/* { dg-do compile }
+ { dg-options "-O1 -Wall -Wno-class-memaccess" } */
+
+struct A { A (); };
+
+const A& return_arg (const A &a)
+{
+ return a;
+}
+
+void sink (const void*);
+
+void nowarn_ref (int i)
+{
+ const A &a = return_arg (A ()); // { dg-note "unnamed temporary" }
+ sink (&a); // { dg-warning "-Wdangling-pointer" }
+}
+
+void warn_dangling_ref (int i)
+{
+ const A &a = return_arg (A ()); // { dg-note "unnamed temporary" }
+ sink (&a); // { dg-warning "-Wdangling-pointer" }
+}
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 0000000..22c559e
--- /dev/null
+++ b/gcc/testsuite/g++.dg/warn/Wdangling-pointer.C
@@ -0,0 +1,74 @@
+/* Exercise basic C++-only cases of -Wdangling-pointer.
+ { dg-do compile }
+ { dg-options "-Wall -Wno-class-memaccess" } */
+
+extern "C" void* memset (void*, int, __SIZE_TYPE__);
+
+void sink (const void*, ...);
+
+struct S { S (); };
+
+void nowarn_int_ref (int i)
+{
+ const S &sref = S ();
+ const int &iref = 1 + i;
+ sink (&sref, &iref);
+}
+
+void warn_init_ref_member ()
+{
+ struct AS
+ {
+ const S &sref;
+ AS ():
+ // The temporary S object is destroyed when AS::AS() returns.
+ sref (S ()) // { dg-warning "storing the address" }
+ { }
+ } as;
+
+ struct Ai
+ {
+ const int &iref;
+ Ai ():
+ // The temporary int is destroyed when Ai::Ai() returns.
+ iref (1 + 1) // { dg-warning "storing the address" }
+ { }
+ } ai;
+
+ sink (&as, &ai);
+}
+
+
+void default_ref_arg (const S& = S ());
+
+void nowarn_call_default_ref_arg ()
+{
+ default_ref_arg ();
+}
+
+
+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 83b6ff9..91a8778 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/g++.dg/warn/ref-temp1.C b/gcc/testsuite/g++.dg/warn/ref-temp1.C
index 26f1ca5..b09d921 100644
--- a/gcc/testsuite/g++.dg/warn/ref-temp1.C
+++ b/gcc/testsuite/g++.dg/warn/ref-temp1.C
@@ -9,3 +9,6 @@ struct Y {
};
Y::Y () : x(1) {} // { dg-warning "temporary" }
+
+/* The initialization of x with the temporary might also trigger:
+ { dg-prune-output "-Wdangling-pointer" } */
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 0000000..0170263
--- /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 an unnamed temporary 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 an unnamed temporary 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 0000000..d792d09
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/Wdangling-pointer.c
@@ -0,0 +1,75 @@
+/* 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 "unnamed temporary" },
+ }
+ sink (p); // { dg-warning "using dangling pointer 'p' to an unnamed temporary" }
+
+ {
+ int *q =
+ (int[]){ 1, 2, 3 }; // { dg-message "unnamed temporary" },
+ p = &q[1];
+ }
+ sink (p); // { dg-warning "using dangling pointer 'p' to an unnamed temporary" }
+ {
+ p = (int*)memchr (
+ (int[]){ 1, 2, 3 }, // { dg-message "unnamed temporary" }
+ 2, 3 * sizeof *p);
+ }
+ sink (p); // { dg-warning "using dangling pointer 'p' to an unnamed temporary" }
+
+ {
+ p = (int*)memchr (
+ (int[]){ i, i + 1 },// { dg-message "unnamed temporary" }
+ 3, 2 * sizeof *p);
+ }
+ sink (p); // { dg-warning "using dangling pointer 'p' to an unnamed temporary" }
+}
+
+
+void warn_store_compound_literal (int **p)
+{
+ int *q = (int[]) { 1, 2, 3 };
+ p[0] = q; // { dg-warning "storing the address" }
+}
+
+void warn_store_vla (int n, int **p)
+{
+ int a[n];
+ p[1] = &a[1]; // { dg-warning "-Wdangling-pointer" "pr??????" { xfail *-*-* } }
+}
diff --git a/gcc/testsuite/gcc.dg/uninit-pr50476.c b/gcc/testsuite/gcc.dg/uninit-pr50476.c
index db4146d..3720184 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)