Bug 99851 - Warn about operator new that takes std::nothrow_t but is potentially-throwing
Summary: Warn about operator new that takes std::nothrow_t but is potentially-throwing
Status: NEW
Alias: None
Product: gcc
Classification: Unclassified
Component: c++ (show other bugs)
Version: 11.0
: P3 enhancement
Target Milestone: ---
Assignee: Not yet assigned to anyone
URL:
Keywords: diagnostic
Depends on:
Blocks: new-warning, new_warning
  Show dependency treegraph
 
Reported: 2021-03-31 17:23 UTC by Jonathan Wakely
Modified: 2021-10-27 05:23 UTC (History)
3 users (show)

See Also:
Host:
Target:
Build:
Known to work:
Known to fail:
Last reconfirmed: 2021-03-31 00:00:00


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Jonathan Wakely 2021-03-31 17:23:21 UTC
This program crashes with a segfault:

#include <new>

void* null() { return nullptr; }

struct X
{
  void* operator new[](std::size_t, const std::nothrow_t&) {
    return null();
  }

  unsigned data = 0;
};

int main()
{
  new(std::nothrow) X[2];
}

The problem is that the new overload is not noexcept, so the compiler assumes it can't return null. The user probably intended it to be a non-throwing form of operator new (as implied by the nothrow_t parameter), so we should warn that it isn't noexcept.

N.B. if the function return nullptr directly then we warn:

new.C: In static member function ‘static void* X::operator new [](std::size_t, const std::nothrow_t&)’:
new.C:6:12: warning: ‘operator new’ must not return NULL unless it is declared ‘throw()’ (or ‘-fcheck-new’ is in effect)
    6 |     return nullptr;
      |            ^~~~~~~

That should be updated to say 'noexcept' not 'throw()' and we might want the two warnigns to use similar phrasing. That warning should also say "a null pointer" not NULL.
Comment 1 Martin Sebor 2021-03-31 18:34:18 UTC
Confirmed, thanks!  Just to make sure I understand: we want a warning for the operator new declaration (irrespective of its definition) because the nothrow_t argument suggests it doesn't throw but the absence of noexcept implies it might.  I.e., the warning can be emitted as early as in the C++ front end.

The following modified test case shows that the the null test in main() is eliminated on the basis of the missing noexcept:

$ cat pr99851.C && g++ -O1 -S -Wall -fdump-tree-optimized=/dev/stdout pr99851.C
namespace std {
  typedef __SIZE_TYPE__ size_t;
  const struct nothrow_t { } nothrow;
}

struct X
{
  void* operator new[](std::size_t n, const std::nothrow_t&) {
    return __builtin_malloc (n);
  }

  unsigned data = 0;
};

int main()
{
  void *p = new (std::nothrow) X[2];
  if (!p)
    __builtin_abort ();
  __builtin_printf ("%p\n", p);
}

;; Function main (main, funcdef_no=1, decl_uid=2393, cgraph_uid=5, symbol_order=5) (executed once)

int main ()
{
  void * _9;

  <bb 2> [local count: 357878154]:
  _9 = __builtin_malloc (8);
  MEM[(struct X *)_9].data = 0;
  MEM[(struct X *)_9 + 4B].data = 0;
  __builtin_printf ("%p\n", _9);
  return 0;

}
Comment 2 Jonathan Wakely 2021-03-31 21:35:01 UTC
(In reply to Martin Sebor from comment #1)
> Confirmed, thanks!  Just to make sure I understand: we want a warning for
> the operator new declaration (irrespective of its definition) because the
> nothrow_t argument suggests it doesn't throw but the absence of noexcept
> implies it might.  I.e., the warning can be emitted as early as in the C++
> front end.

Yes, you understood exactly.

Additionally, we should not warn if the function has an explicit noexcept(false).

I think it's reasonable to add this to -Wall. In the unlikely event that the user really does want a throwing operator new that takes a std::nothrow_t parameter, I think requiring noexcept(false) to suppress the warning is reasonable. It's confusing/misleading otherwise.
Comment 3 Jonathan Wakely 2021-03-31 21:36:20 UTC
And just to be clear, this should apply to operator new and operator new[]. The examples above both use the array form, but there's no reason this shouldn't apply to the single object form too.