public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* [PATCH] diagnose more new/delete mismatches (PR 101791)
@ 2021-08-05 22:02 Martin Sebor
  2021-08-09 15:03 ` Jeff Law
  0 siblings, 1 reply; 2+ messages in thread
From: Martin Sebor @ 2021-08-05 22:02 UTC (permalink / raw)
  To: gcc-patches

[-- Attachment #1: Type: text/plain, Size: 733 bytes --]

-Wmismatched-new-delete partly relies on valid_new_delete_pair_p()
to detect matching calls to operator new and delete.  The function
returns a conservative result which, when indicating a mismatch,
the warning then works to make more accurate before it triggers.
As it turns out, the logic is inadvertently overly permissive in
the most common case of a mismatch: between the array and scalar
forms of the global operators new and delete such as in:

   delete new int[2];   // should be delete[]

The attached patch solves the problem by adding a new argument to
valid_new_delete_pair_p() for the function to set to indicate when
its negative result is definitive rather than a conservative guess.

Tested on x86_64-linux.

Martin

[-- Attachment #2: gcc-101791.diff --]
[-- Type: text/x-patch, Size: 10600 bytes --]

Diagnose mismatches between array and scalar new and delete [PR101791].

Resolves:
PR middle-end/101791 - missing warning on a mismatch between scalar and array forms of new and delete


gcc/ChangeLog:

	PR middle-end/101791
	* gimple-ssa-warn-access.cc (new_delete_mismatch_p): Use new argument
	to valid_new_delete_pair_p.
	* tree.c (valid_new_delete_pair_p): Add argument.
	* tree.h (valid_new_delete_pair_p): Same.

gcc/testsuite/ChangeLog:

	PR middle-end/101791
	* g++.dg/warn/Wmismatched-new-delete-6.C: New test.
	* g++.dg/warn/Wmismatched-new-delete-7.C: New test.

diff --git a/gcc/gimple-ssa-warn-access.cc b/gcc/gimple-ssa-warn-access.cc
index e4d98b2ec28..c1ced98780f 100644
--- a/gcc/gimple-ssa-warn-access.cc
+++ b/gcc/gimple-ssa-warn-access.cc
@@ -1233,9 +1782,14 @@ new_delete_mismatch_p (tree new_decl, tree delete_decl)
 
   /* valid_new_delete_pair_p() returns a conservative result (currently
      it only handles global operators).  A true result is reliable but
-     a false result doesn't necessarily mean the operators don't match.  */
-  if (valid_new_delete_pair_p (new_name, delete_name))
+     a false result doesn't necessarily mean the operators don't match
+     unless CERTAIN is set.  */
+  bool certain;
+  if (valid_new_delete_pair_p (new_name, delete_name, &certain))
     return false;
+  /* CERTAIN is set when the negative result is certain.  */
+  if (certain)
+    return true;
 
   /* For anything not handled by valid_new_delete_pair_p() such as member
      operators compare the individual demangled components of the mangled
diff --git a/gcc/tree.c b/gcc/tree.c
index e923e67b694..5c747321002 100644
--- a/gcc/tree.c
+++ b/gcc/tree.c
@@ -14311,17 +14311,28 @@ verify_type_context (location_t loc, type_context_kind context,
 	  || targetm.verify_type_context (loc, context, type, silent_p));
 }
 
-/* Return that NEW_ASM and DELETE_ASM name a valid pair of new and
-   delete operators.  */
+/* Return true if NEW_ASM and DELETE_ASM name a valid pair of new and
+   delete operators.  Return false if they may or may not name such
+   a pair and, when nonnull, set *PCERTAIN to true if they certainly
+   do not.  */
 
 bool
-valid_new_delete_pair_p (tree new_asm, tree delete_asm)
+valid_new_delete_pair_p (tree new_asm, tree delete_asm,
+			 bool *pcertain /* = NULL */)
 {
+  bool certain;
+  if (!pcertain)
+    pcertain = &certain;
+
   const char *new_name = IDENTIFIER_POINTER (new_asm);
   const char *delete_name = IDENTIFIER_POINTER (delete_asm);
   unsigned int new_len = IDENTIFIER_LENGTH (new_asm);
   unsigned int delete_len = IDENTIFIER_LENGTH (delete_asm);
 
+  /* The following failures are due to invalid names so they're not
+     considered certain mismatches.  */
+  *pcertain = false;
+
   if (new_len < 5 || delete_len < 6)
     return false;
   if (new_name[0] == '_')
@@ -14334,11 +14345,19 @@ valid_new_delete_pair_p (tree new_asm, tree delete_asm)
     ++delete_name, --delete_len;
   if (new_len < 4 || delete_len < 5)
     return false;
+
+  /* The following failures are due to names of user-defined operators
+     so they're also not considered certain mismatches.  */
+
   /* *_len is now just the length after initial underscores.  */
   if (new_name[0] != 'Z' || new_name[1] != 'n')
     return false;
   if (delete_name[0] != 'Z' || delete_name[1] != 'd')
     return false;
+
+  /* The following failures are certain mismatches.  */
+  *pcertain = true;
+
   /* _Znw must match _Zdl, _Zna must match _Zda.  */
   if ((new_name[2] != 'w' || delete_name[2] != 'l')
       && (new_name[2] != 'a' || delete_name[2] != 'a'))
@@ -14377,6 +14396,9 @@ valid_new_delete_pair_p (tree new_asm, tree delete_asm)
 	  && !memcmp (delete_name + 5, "St11align_val_tRKSt9nothrow_t", 29))
 	return true;
     }
+
+  /* The negative result is conservative.  */
+  *pcertain = false;
   return false;
 }
 
diff --git a/gcc/tree.h b/gcc/tree.h
index 972ceb370f8..69d12193686 100644
--- a/gcc/tree.h
+++ b/gcc/tree.h
@@ -5396,7 +5396,9 @@ extern bool gimple_canonical_types_compatible_p (const_tree, const_tree,
 extern bool type_with_interoperable_signedness (const_tree);
 extern bitmap get_nonnull_args (const_tree);
 extern int get_range_pos_neg (tree);
-extern bool valid_new_delete_pair_p (tree, tree);
+
+/* Return true for a valid pair of new and delete operators.  */
+extern bool valid_new_delete_pair_p (tree, tree, bool * = NULL);
 
 /* Return simplified tree code of type that is used for canonical type
    merging.  */
diff --git a/gcc/testsuite/g++.dg/warn/Wmismatched-new-delete-6.C b/gcc/testsuite/g++.dg/warn/Wmismatched-new-delete-6.C
new file mode 100644
index 00000000000..e19d00058b3
--- /dev/null
+++ b/gcc/testsuite/g++.dg/warn/Wmismatched-new-delete-6.C
@@ -0,0 +1,158 @@
+/* PR middle-end/101791 - missing warning on a mismatch between scalar
+   and array forms of new and delete
+   { dg-do compile }
+   { dg-options "-Wall" } */
+
+typedef __SIZE_TYPE__ size_t;
+
+namespace std
+{
+#if __cplusplus >= 201703L
+enum class align_val_t: size_t { };
+#else
+enum align_val_t { };
+#endif
+
+struct nothrow_t { };
+const nothrow_t nothrow = { };
+
+}
+
+void* operator new (size_t);
+void* operator new (size_t, std::align_val_t);
+void* operator new (size_t, std::nothrow_t) throw ();
+void* operator new (size_t, std::align_val_t, std::nothrow_t) throw ();
+
+void* operator new[] (size_t);
+void* operator new[] (size_t, std::align_val_t);
+void* operator new[] (size_t, std::nothrow_t) throw ();
+void* operator new[] (size_t, std::align_val_t, std::nothrow_t) throw ();
+
+void operator delete (void*);
+void operator delete (void*, size_t);
+void operator delete (void*, std::align_val_t);
+void operator delete (void*, size_t, std::align_val_t);
+void operator delete (void*, std::nothrow_t) throw ();
+void operator delete (void*, std::align_val_t, std::nothrow_t) throw ();
+
+void operator delete[] (void*);
+void operator delete[] (void*, size_t);
+void operator delete[] (void*, std::align_val_t);
+void operator delete[] (void*, size_t, std::align_val_t);
+void operator delete[] (void*, std::nothrow_t) throw ();
+void operator delete[] (void*, std::align_val_t, std::nothrow_t) throw ();
+
+
+void sink (void*, ...);
+
+
+void nowarn_scalar_scalar ()
+{
+  {
+    int *p = new int;
+    sink (p);
+    delete p;
+  }
+
+  {
+    int *p = new (std::align_val_t (8)) int;
+    sink (p);
+    delete p;
+  }
+
+  {
+    int *p = new (std::nothrow) int;
+    sink (p);
+    delete p;
+  }
+
+  {
+    int *p = new (std::align_val_t (8), std::nothrow) int;
+    sink (p);
+    delete p;
+  }
+}
+
+void nowarn_array_array ()
+{
+  {
+    int *p = new int[__LINE__];
+    sink (p);
+    delete[] p;
+  }
+
+  {
+    int *p = new (std::align_val_t (8)) int[__LINE__];
+    sink (p);
+    delete[] p;
+  }
+
+  {
+    int *p = new (std::nothrow) int[__LINE__];
+    sink (p);
+    delete[] p;
+  }
+
+  {
+    int *p = new (std::align_val_t (8), std::nothrow) int[__LINE__];
+    sink (p);
+    delete[] p;
+  }
+}
+
+
+
+void nowarn_scalar_array ()
+{
+  {
+    int *p = new int;   // { dg-message "returned from" }
+    sink (p);
+    delete[] p;         // { dg-warning "\\\[-Wmismatched-new-delete" }
+  }
+
+  {
+    int *p = new (std::align_val_t (8)) int;
+    sink (p);
+    delete[] p;         // { dg-warning "\\\[-Wmismatched-new-delete" }
+  }
+
+  {
+    int *p = new (std::nothrow) int;
+    sink (p);
+    delete[] p;         // { dg-warning "\\\[-Wmismatched-new-delete" }
+  }
+
+  {
+    int *p = new (std::align_val_t (8), std::nothrow) int;
+    sink (p);
+    delete[] p;         // { dg-warning "\\\[-Wmismatched-new-delete" }
+  }
+}
+
+
+void nowarn_array_scalar ()
+{
+  {
+    int *p = new int[__LINE__];
+    sink (p);
+    delete p;           // { dg-warning "\\\[-Wmismatched-new-delete" }
+  }
+
+  {
+    int *p = new (std::align_val_t (8)) int[__LINE__];
+    sink (p);
+    delete p;           // { dg-warning "\\\[-Wmismatched-new-delete" }
+  }
+
+  {
+    int *p = new (std::nothrow) int[__LINE__];
+    sink (p);
+    delete p;           // { dg-warning "\\\[-Wmismatched-new-delete" }
+  }
+
+  {
+    int *p = new (std::align_val_t (8), std::nothrow) int[__LINE__];
+    sink (p);
+    delete p;           // { dg-warning "\\\[-Wmismatched-new-delete" }
+  }
+}
diff --git a/gcc/testsuite/g++.dg/warn/Wmismatched-new-delete-7.C b/gcc/testsuite/g++.dg/warn/Wmismatched-new-delete-7.C
new file mode 100644
index 00000000000..67a5354b91d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/warn/Wmismatched-new-delete-7.C
@@ -0,0 +1,91 @@
+/* PR middle-end/101791 - missing warning on a mismatch between scalar
+   and array forms of new and delete
+   Verify that likely safe calls to technically mismatched member operator
+   new and delete are not diagnosed.  This test might need to be adjusted
+   if it turns out the assumptions GCC makes are overly conservative.
+   { dg-do compile }
+   { dg-options "-Wall" } */
+
+typedef __SIZE_TYPE__ size_t;
+
+namespace std
+{
+#if __cplusplus >= 201703L
+enum class align_val_t: size_t { };
+#else
+enum align_val_t { };
+#endif
+
+struct nothrow_t { };
+const nothrow_t nothrow = { };
+
+}
+
+void sink (void*, ...);
+
+struct X { } x;
+struct Y { } y;
+
+struct A
+{
+  void* operator new (size_t);
+  void* operator new (size_t, std::align_val_t);
+
+  void* operator new (size_t, X);
+  void* operator new (size_t, Y);
+
+  void* operator new (size_t, std::align_val_t, X);
+  void* operator new (size_t, std::nothrow_t, Y);
+
+  /* A single operator delete callable on the result of calls to any
+     of the operator new overloads above (this may be too optimistic).  */
+  void operator delete (void*);
+};
+
+A* nowarn_align ()
+{
+  /* The following are likely okay given A's definition above but would
+     not be if A also defined an align_val_t overload of operator delete.  */
+  A *p = new (std::align_val_t (8)) A;
+  delete p;
+
+  return new (std::align_val_t (16)) A;
+}
+
+A* nowarn_X ()
+{
+  /* The following are also likely okay given A's definition above but
+     also would not be if A also defined an overload of operator delete
+     for X.  */
+  A *p = new (x) A;
+  delete p;
+  return new (x) A;
+}
+
+A* nowarn_Y ()
+{
+  // Same as above.
+  A *p = new (y) A;
+  delete p;
+  return new (y) A;
+}
+
+
+A* nowarn_align_X ()
+{
+  // Same as above.
+  A *p = new (std::align_val_t (32), x) A;
+  delete p;
+
+  return new (std::align_val_t (64), x) A;
+}
+
+
+A* nowarn_nothrow_Y ()
+{
+  // Same as above.
+  A *p = new (std::nothrow, y) A;
+  delete p;
+  return new (std::nothrow, y) A;
+}
+

^ permalink raw reply	[flat|nested] 2+ messages in thread

* Re: [PATCH] diagnose more new/delete mismatches (PR 101791)
  2021-08-05 22:02 [PATCH] diagnose more new/delete mismatches (PR 101791) Martin Sebor
@ 2021-08-09 15:03 ` Jeff Law
  0 siblings, 0 replies; 2+ messages in thread
From: Jeff Law @ 2021-08-09 15:03 UTC (permalink / raw)
  To: Martin Sebor, gcc-patches



On 8/5/2021 4:02 PM, Martin Sebor via Gcc-patches wrote:
> -Wmismatched-new-delete partly relies on valid_new_delete_pair_p()
> to detect matching calls to operator new and delete.  The function
> returns a conservative result which, when indicating a mismatch,
> the warning then works to make more accurate before it triggers.
> As it turns out, the logic is inadvertently overly permissive in
> the most common case of a mismatch: between the array and scalar
> forms of the global operators new and delete such as in:
>
>   delete new int[2];   // should be delete[]
>
> The attached patch solves the problem by adding a new argument to
> valid_new_delete_pair_p() for the function to set to indicate when
> its negative result is definitive rather than a conservative guess.
>
> Tested on x86_64-linux.
>
> Martin
>
> gcc-101791.diff
>
> Diagnose mismatches between array and scalar new and delete [PR101791].
>
> Resolves:
> PR middle-end/101791 - missing warning on a mismatch between scalar and array forms of new and delete
>
>
> gcc/ChangeLog:
>
> 	PR middle-end/101791
> 	* gimple-ssa-warn-access.cc (new_delete_mismatch_p): Use new argument
> 	to valid_new_delete_pair_p.
> 	* tree.c (valid_new_delete_pair_p): Add argument.
> 	* tree.h (valid_new_delete_pair_p): Same.
>
> gcc/testsuite/ChangeLog:
>
> 	PR middle-end/101791
> 	* g++.dg/warn/Wmismatched-new-delete-6.C: New test.
> 	* g++.dg/warn/Wmismatched-new-delete-7.C: New test.
OK
jeff


^ permalink raw reply	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2021-08-09 15:03 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-08-05 22:02 [PATCH] diagnose more new/delete mismatches (PR 101791) Martin Sebor
2021-08-09 15:03 ` Jeff Law

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).