either don't or their relationship cannot be determined. */
static bool
-pointers_related_p (gimple *stmt, tree p, tree q, pointer_query &qry)
+pointers_related_p (gimple *stmt, tree p, tree q, pointer_query &qry,
+ auto_bitmap &visited)
{
if (!ptr_derefs_may_alias_p (p, q))
return false;
it involves a self-referential PHI. Return a conservative result. */
return false;
- return pref.ref == qref.ref;
+ if (pref.ref == qref.ref)
+ return true;
+
+ /* If either pointer is a PHI, iterate over all its operands and
+ return true if they're all related to the other pointer. */
+ tree ptr = q;
+ unsigned version;
+ gphi *phi = pref.phi ();
+ if (phi)
+ version = SSA_NAME_VERSION (pref.ref);
+ else
+ {
+ phi = qref.phi ();
+ if (!phi)
+ return false;
+
+ ptr = p;
+ version = SSA_NAME_VERSION (qref.ref);
+ }
+
+ if (!bitmap_set_bit (visited, version))
+ return true;
+
+ unsigned nargs = gimple_phi_num_args (phi);
+ for (unsigned i = 0; i != nargs; ++i)
+ {
+ tree arg = gimple_phi_arg_def (phi, i);
+ if (!pointers_related_p (stmt, arg, ptr, qry, visited))
+ return false;
+ }
+
+ return true;
+}
+
+/* Convenience wrapper for the above. */
+
+static bool
+pointers_related_p (gimple *stmt, tree p, tree q, pointer_query &qry)
+{
+ auto_bitmap visited;
+ return pointers_related_p (stmt, p, q, qry, visited);
}
/* For a STMT either a call to a deallocation function or a clobber, warn
{
if (gimple_code (use_stmt) == GIMPLE_PHI)
{
+ /* Only add a PHI result to POINTERS if all its
+ operands are related to PTR, otherwise continue. */
tree lhs = gimple_phi_result (use_stmt);
+ if (!pointers_related_p (stmt, lhs, ptr, m_ptr_qry))
+ continue;
+
if (TREE_CODE (lhs) == SSA_NAME)
{
pointers.safe_push (lhs);
--- /dev/null
+/* PR middle-end/104232 - spurious -Wuse-after-free after conditional free
+ { dg-do compile }
+ { dg-options "-O2 -Wall" } */
+
+char* f (void);
+
+struct A
+{
+ char *p;
+ A (): p () { }
+ ~A ()
+ {
+ __builtin_free (p); // { dg-bogus "-Wuse-after-free" }
+ }
+};
+
+int test_no_warn (void)
+{
+ A px, qx;
+
+ qx.p = f ();
+ if (!qx.p)
+ return 0;
+
+ px.p = f ();
+ return 1;
+}
--- /dev/null
+/* PR middle-end/104232 - spurious -Wuse-after-free after conditional free
+ { dg-do compile }
+ { dg-options "-O2 -Wall" } */
+
+void free (void*);
+
+void sink (void*);
+
+void nowarn_cond_2 (char *p0, char *q0, int i)
+{
+ char *r = i ? p0 : q0;
+
+ free (p0);
+
+ /* The use of a PHI operand could be diagnosed using the "maybe" form
+ of the warning at level 2 but it's not done. If it ever changes
+ this test and those below will need to be updated. */
+ sink (r);
+}
+
+void nowarn_cond_2_null (char *p0, int i)
+{
+ char *r = i ? p0 : 0;
+
+ free (p0);
+ sink (r);
+}
+
+void nowarn_cond_3 (char *p0, char *q0, int i)
+{
+ char *r = i < 0 ? p0 - 1 : 0 < i ? p0 + 1 : q0;
+
+ free (p0);
+ sink (r);
+}
+
+void nowarn_cond_3_null (char *p0, int i)
+{
+ char *r = i < 0 ? p0 - 1 : 0 < i ? p0 + 1 : 0;
+
+ free (p0);
+ sink (r);
+}
+
+void nowarn_cond_4 (char *p0, char *q0, int i)
+{
+ char *r = i < -1 ? p0 - 2 : i < 0 ? p0 - 1 : 1 < i ? p0 + 1 : q0;
+
+ free (p0);
+ sink (r);
+}
+
+int nowarn_cond_loop (char *p)
+{
+ char *q = p;
+ while (*q)
+ {
+ if (*q == 'x')
+ {
+ q = "";
+ break;
+ }
+ ++q;
+ }
+
+ free (p);
+ return *q;
+}
+
+
+void warn_cond_2_cst (char *p, int i)
+{
+ /* Same as nowarn_cond_2() above but with R being derived only from
+ P, which means that any R's use after P has been freed should be
+ diagnosed. */
+ char *r = i ? p + 1 : p + 2;
+
+ free (p); // { dg-message "call to 'free'" }
+ sink (r); // { dg-warning "pointer used after 'free'" }
+}
+
+void warn_cond_2_var (char *p, int i, int j)
+{
+ char *r = i ? p + i : p + j;
+
+ free (p); // { dg-message "call to 'free'" }
+ sink (r); // { dg-warning "pointer used after 'free'" }
+}
+
+void warn_cond_3_var (char *p0, int i, int j)
+{
+ char *r = i < 0 ? p0 - i : 0 < i ? p0 + j : p0 + i + j;
+
+ free (p0); // { dg-message "call to 'free'" }
+ sink (r + 1); // { dg-warning "pointer used after 'free'" }
+}
+
+int warn_cond_4 (char *p0, char *q0, int i)
+{
+ char *r = i < -1 ? p0 - 2 : i < 0 ? p0 - 1 : 1 < i ? p0 + 2 : p0 + 1;
+
+ free (p0); // { dg-message "call to 'free'" }
+ return *r; // { dg-warning "pointer used after 'free'" }
+}
+
+int warn_cond_loop (char *p)
+{
+ char *q = p;
+
+ while (*q)
+ ++q;
+
+ free (p); // { dg-message "call to 'free'" }
+ return *q; // { dg-warning "pointer 'q' used after 'free'" }
+}
--- /dev/null
+/* PR middle-end/104232 - spurious -Wuse-after-free after conditional free
+ { dg-do compile }
+ { dg-options "-O2 -Wall" } */
+
+char* f (void);
+
+static inline void freep (void *p)
+{
+ __builtin_free (*(void**)p); // { dg-bogus "-Wuse-after-free" }
+}
+
+int test_no_warn (void)
+{
+ __attribute__ ((__cleanup__ (freep))) char *s = 0, *t = 0;
+
+ t = f ();
+ if (!t)
+ return 0;
+
+ s = f ();
+ return 1;
+}