Hi Andrew,

Would you pls take a look on my patch? I followed you comments on Bugzilla
to fix it.
Thanks a lot.

Best,
Yifan

yifan.dong <[email protected]> 于2026年3月4日周三 23:02写道:

> Hi!
>
> I am trying to submit my first patch to gcc. I hope I made everything
> right.
>
> This patch fixes two false positives related to C++20 destroying operator
> delete in -Wmismatched-new-delete and -Wfree-nonheap-object.
>
> Tested on x86_64-pc-linux-gnu:
> - make -k check
> - bootstrapped
>
> 2026-03-04 Yifan Dong <[email protected]>
>
> ChangeLog:
> * gimple-ssa-warn-access.cc: Handle destroying operator delete in matching
> and offset-warning paths.
> * testsuite/g++.dg/warn/pr100861.C: New test.
> * testsuite/g++.dg/warn/pr121717.C: New test.
>
>
> diff --git a/gcc/gimple-ssa-warn-access.cc b/gcc/gimple-ssa-warn-access.cc
> index c8f10cae32f..fce84f4b025 100644
> --- a/gcc/gimple-ssa-warn-access.cc
> +++ b/gcc/gimple-ssa-warn-access.cc
> @@ -1810,6 +1810,40 @@ new_delete_mismatch_p (tree new_decl, tree
> delete_decl)
>    return mismatch;
>  }
>
> +/* Return true if TYPE is std::destroying_delete_t.  */
> +
> +static bool
> +std_destroying_delete_t_p (tree type)
> +{
> +  if (!type)
> +    return false;
> +
> +  type = TYPE_MAIN_VARIANT (type);
> +  return (TYPE_CONTEXT (type)
> +  && TREE_CODE (TYPE_CONTEXT (type)) == NAMESPACE_DECL
> +  && DECL_NAME (TYPE_CONTEXT (type))
> +  && id_equal (DECL_NAME (TYPE_CONTEXT (type)), "std")
> +  && TYPE_IDENTIFIER (type)
> +  && id_equal (TYPE_IDENTIFIER (type), "destroying_delete_t"));
> +}
> +
> +/* Return true if FNDECL is a destroying operator delete.
> +   Such a function has at least two parameters and its second one has
> +   type std::destroying_delete_t.  */
> +
> +static bool
> +destroying_operator_delete_p (tree fndecl)
> +{
> +  if (!DECL_IS_OPERATOR_DELETE_P (fndecl))
> +    return false;
> +
> +  tree args = TYPE_ARG_TYPES (TREE_TYPE (fndecl));
> +  if (!args || !TREE_CHAIN (args))
> +    return false;
> +
> +  return std_destroying_delete_t_p (TREE_VALUE (TREE_CHAIN (args)));
> +}
> +
>  /* ALLOC_DECL and DEALLOC_DECL are pair of allocation and deallocation
>     functions.  Return true if the latter is suitable to deallocate objects
>     allocated by calls to the former.  */
> @@ -1825,9 +1859,16 @@ matching_alloc_calls_p (tree alloc_decl, tree
> dealloc_decl)
>    if (DECL_IS_OPERATOR_NEW_P (alloc_decl))
>      {
>        if (DECL_IS_OPERATOR_DELETE_P (dealloc_decl))
> - /* Return true iff both functions are of the same array or
> -   singleton form and false otherwise.  */
> - return !new_delete_mismatch_p (alloc_decl, dealloc_decl);
> + {
> +  /* Return true iff both functions are of the same array or
> +     singleton form and false otherwise.  Destroying delete has no
> +     matching operator new counterpart and so should never mismatch
> +     with one.  */
> +  if (destroying_operator_delete_p (dealloc_decl))
> +    return true;
> +
> +  return !new_delete_mismatch_p (alloc_decl, dealloc_decl);
> + }
>
>        /* Return false for deallocation functions that are known not
>   to match.  */
> @@ -2052,6 +2093,11 @@ warn_dealloc_offset (location_t loc, gimple *call,
> const access_ref &aref)
>    if (!dealloc_decl)
>      return false;
>
> +  /* A destroying operator delete can legitimately be invoked with an
> +     adjusted base-subobject pointer.  */
> +  if (destroying_operator_delete_p (dealloc_decl))
> +    return false;
> +
>    if (DECL_IS_OPERATOR_DELETE_P (dealloc_decl)
>        && !DECL_IS_REPLACEABLE_OPERATOR (dealloc_decl))
>      {
> diff --git a/gcc/testsuite/g++.dg/warn/pr100861.C
> b/gcc/testsuite/g++.dg/warn/pr100861.C
> new file mode 100644
> index 00000000000..37ea5129c56
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/warn/pr100861.C
> @@ -0,0 +1,31 @@
> +/* PR c++/100861 */
> +/* { dg-do compile { target c++20 } } */
> +/* { dg-additional-options "-Wmismatched-new-delete" } */
> +
> +typedef __SIZE_TYPE__ size_t;
> +
> +namespace std {
> +struct destroying_delete_t
> +{
> +  explicit destroying_delete_t () = default;
> +};
> +} // namespace std
> +
> +void *operator new (size_t);
> +void operator delete (void *) noexcept;
> +
> +struct Widget
> +{
> +  void operator delete (Widget *p, std::destroying_delete_t)
> +  {
> +    p->~Widget ();
> +    ::operator delete (p);
> +  }
> +};
> +
> +void
> +f ()
> +{
> +  Widget *p = new Widget;
> +  delete p; // { dg-bogus "Wmismatched-new-delete" }
> +}
> diff --git a/gcc/testsuite/g++.dg/warn/pr121717.C
> b/gcc/testsuite/g++.dg/warn/pr121717.C
> new file mode 100644
> index 00000000000..86a0362b8c2
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/warn/pr121717.C
> @@ -0,0 +1,30 @@
> +/* PR middle-end/121717 */
> +/* { dg-do compile { target c++20 } } */
> +/* { dg-options "-O2 -Wfree-nonheap-object" } */
> +
> +typedef __SIZE_TYPE__ size_t;
> +
> +namespace std {
> +struct destroying_delete_t
> +{
> +  explicit destroying_delete_t () = default;
> +};
> +} // namespace std
> +
> +struct A
> +{
> +  virtual ~A ();
> +  void operator delete (A *, std::destroying_delete_t, size_t) noexcept;
> +};
> +
> +struct B
> +{
> +  virtual void b () = 0;
> +};
> +
> +class X : private B, public A
> +{
> +  ~X () override;
> +};
> +
> +X::~X () {} // { dg-bogus "-Wfree-nonheap-object" }
>
>
> Best,
> Yifan
>

Reply via email to