Skip to content

Commit 4207ad5

Browse files
[libc++] Fix noexcept behaviour of operator new helper functions (#74337)
This patch removes the noexcept specifier introduced in #69407 since the Standard allows a new handler to throw an exception of type bad_alloc (or derived from it). With the noexcept specifier on the helper functions, we would immediately terminate the program. The patch also adds tests for the case that had regressed. Co-authored-by: Alison Zhang <[email protected]>
1 parent 5ce2868 commit 4207ad5

10 files changed

+356
-4
lines changed

libcxx/src/new.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
// in this shared library, so that they can be overridden by programs
2121
// that define non-weak copies of the functions.
2222

23-
static void* operator_new_impl(std::size_t size) noexcept {
23+
static void* operator_new_impl(std::size_t size) {
2424
if (size == 0)
2525
size = 1;
2626
void* p;
@@ -87,7 +87,7 @@ _LIBCPP_WEAK void operator delete[](void* ptr, size_t) noexcept { ::operator del
8787

8888
# if !defined(_LIBCPP_HAS_NO_LIBRARY_ALIGNED_ALLOCATION)
8989

90-
static void* operator_new_aligned_impl(std::size_t size, std::align_val_t alignment) noexcept {
90+
static void* operator_new_aligned_impl(std::size_t size, std::align_val_t alignment) {
9191
if (size == 0)
9292
size = 1;
9393
if (static_cast<size_t>(alignment) < sizeof(void*))
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
// UNSUPPORTED: no-exceptions
10+
// UNSUPPORTED: sanitizer-new-delete
11+
12+
#include <new>
13+
#include <cassert>
14+
#include <limits>
15+
#include <cstdlib>
16+
17+
struct construction_key {};
18+
struct my_bad_alloc : std::bad_alloc {
19+
my_bad_alloc(const my_bad_alloc&) : self(this) { std::abort(); }
20+
my_bad_alloc(construction_key) : self(this) {}
21+
const my_bad_alloc* const self;
22+
};
23+
24+
int new_handler_called = 0;
25+
26+
void my_new_handler() {
27+
++new_handler_called;
28+
throw my_bad_alloc(construction_key());
29+
}
30+
31+
int main(int, char**) {
32+
std::set_new_handler(my_new_handler);
33+
try {
34+
void* x = operator new[](std::numeric_limits<std::size_t>::max());
35+
(void)x;
36+
assert(false);
37+
} catch (my_bad_alloc const& e) {
38+
assert(new_handler_called == 1);
39+
assert(e.self == &e);
40+
} catch (...) {
41+
assert(false);
42+
}
43+
return 0;
44+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
// UNSUPPORTED: no-exceptions
10+
// UNSUPPORTED: sanitizer-new-delete
11+
12+
#include <new>
13+
#include <cassert>
14+
#include <limits>
15+
#include <cstdlib>
16+
17+
struct construction_key {};
18+
struct my_bad_alloc : std::bad_alloc {
19+
my_bad_alloc(const my_bad_alloc&) : self(this) { std::abort(); }
20+
my_bad_alloc(construction_key) : self(this) {}
21+
const my_bad_alloc* const self;
22+
};
23+
24+
int new_handler_called = 0;
25+
26+
void my_new_handler() {
27+
++new_handler_called;
28+
throw my_bad_alloc(construction_key());
29+
}
30+
31+
int main(int, char**) {
32+
std::set_new_handler(my_new_handler);
33+
try {
34+
void* x = operator new[](std::numeric_limits<std::size_t>::max(), static_cast<std::align_val_t>(32));
35+
(void)x;
36+
assert(false);
37+
} catch (my_bad_alloc const& e) {
38+
assert(new_handler_called == 1);
39+
assert(e.self == &e);
40+
} catch (...) {
41+
assert(false);
42+
}
43+
return 0;
44+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
// UNSUPPORTED: no-exceptions
10+
// UNSUPPORTED: sanitizer-new-delete
11+
12+
#include <new>
13+
#include <cassert>
14+
#include <limits>
15+
#include <cstdlib>
16+
17+
struct construction_key {};
18+
struct my_bad_alloc : std::bad_alloc {
19+
my_bad_alloc(const my_bad_alloc&) : self(this) { std::abort(); }
20+
my_bad_alloc(construction_key) : self(this) {}
21+
const my_bad_alloc* const self;
22+
};
23+
24+
int new_handler_called = 0;
25+
26+
void my_new_handler() {
27+
++new_handler_called;
28+
throw my_bad_alloc(construction_key());
29+
}
30+
31+
int main(int, char**) {
32+
std::set_new_handler(my_new_handler);
33+
try {
34+
void* x = operator new[](std::numeric_limits<std::size_t>::max(), static_cast<std::align_val_t>(32), std::nothrow);
35+
(void)x;
36+
assert(x == NULL);
37+
} catch (my_bad_alloc const& e) {
38+
assert(new_handler_called == 1);
39+
assert(e.self == &e);
40+
} catch (...) {
41+
assert(false);
42+
}
43+
return 0;
44+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
// UNSUPPORTED: no-exceptions
10+
// UNSUPPORTED: sanitizer-new-delete
11+
12+
#include <new>
13+
#include <cassert>
14+
#include <limits>
15+
#include <cstdlib>
16+
17+
struct construction_key {};
18+
struct my_bad_alloc : std::bad_alloc {
19+
my_bad_alloc(const my_bad_alloc&) : self(this) { std::abort(); }
20+
my_bad_alloc(construction_key) : self(this) {}
21+
const my_bad_alloc* const self;
22+
};
23+
24+
int new_handler_called = 0;
25+
26+
void my_new_handler() {
27+
++new_handler_called;
28+
throw my_bad_alloc(construction_key());
29+
}
30+
31+
int main(int, char**) {
32+
std::set_new_handler(my_new_handler);
33+
try {
34+
void* x = operator new[](std::numeric_limits<std::size_t>::max(), std::nothrow);
35+
(void)x;
36+
assert(x == NULL);
37+
} catch (my_bad_alloc const& e) {
38+
assert(new_handler_called == 1);
39+
assert(e.self == &e);
40+
} catch (...) {
41+
assert(false);
42+
}
43+
return 0;
44+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
// UNSUPPORTED: no-exceptions
10+
// UNSUPPORTED: sanitizer-new-delete
11+
12+
#include <new>
13+
#include <cassert>
14+
#include <limits>
15+
#include <cstdlib>
16+
17+
struct construction_key {};
18+
struct my_bad_alloc : std::bad_alloc {
19+
my_bad_alloc(const my_bad_alloc&) : self(this) { std::abort(); }
20+
my_bad_alloc(construction_key) : self(this) {}
21+
const my_bad_alloc* const self;
22+
};
23+
24+
int new_handler_called = 0;
25+
26+
void my_new_handler() {
27+
++new_handler_called;
28+
throw my_bad_alloc(construction_key());
29+
}
30+
31+
int main(int, char**) {
32+
std::set_new_handler(my_new_handler);
33+
try {
34+
void* x = operator new(std::numeric_limits<std::size_t>::max());
35+
(void)x;
36+
assert(false);
37+
} catch (my_bad_alloc const& e) {
38+
assert(new_handler_called == 1);
39+
assert(e.self == &e);
40+
} catch (...) {
41+
assert(false);
42+
}
43+
return 0;
44+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
// UNSUPPORTED: no-exceptions
10+
// UNSUPPORTED: sanitizer-new-delete
11+
12+
#include <new>
13+
#include <cassert>
14+
#include <limits>
15+
#include <cstdlib>
16+
17+
struct construction_key {};
18+
struct my_bad_alloc : std::bad_alloc {
19+
my_bad_alloc(const my_bad_alloc&) : self(this) { std::abort(); }
20+
my_bad_alloc(construction_key) : self(this) {}
21+
const my_bad_alloc* const self;
22+
};
23+
24+
int new_handler_called = 0;
25+
26+
void my_new_handler() {
27+
++new_handler_called;
28+
throw my_bad_alloc(construction_key());
29+
}
30+
31+
int main(int, char**) {
32+
std::set_new_handler(my_new_handler);
33+
try {
34+
void* x = operator new(std::numeric_limits<std::size_t>::max(), static_cast<std::align_val_t>(32));
35+
(void)x;
36+
assert(false);
37+
} catch (my_bad_alloc const& e) {
38+
assert(new_handler_called == 1);
39+
assert(e.self == &e);
40+
} catch (...) {
41+
assert(false);
42+
}
43+
return 0;
44+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
// UNSUPPORTED: no-exceptions
10+
// UNSUPPORTED: sanitizer-new-delete
11+
12+
#include <new>
13+
#include <cassert>
14+
#include <limits>
15+
#include <cstdlib>
16+
17+
struct construction_key {};
18+
struct my_bad_alloc : std::bad_alloc {
19+
my_bad_alloc(const my_bad_alloc&) : self(this) { std::abort(); }
20+
my_bad_alloc(construction_key) : self(this) {}
21+
const my_bad_alloc* const self;
22+
};
23+
24+
int new_handler_called = 0;
25+
26+
void my_new_handler() {
27+
++new_handler_called;
28+
throw my_bad_alloc(construction_key());
29+
}
30+
31+
int main(int, char**) {
32+
std::set_new_handler(my_new_handler);
33+
try {
34+
void* x = operator new(std::numeric_limits<std::size_t>::max(), static_cast<std::align_val_t>(32), std::nothrow);
35+
(void)x;
36+
assert(x == NULL);
37+
} catch (my_bad_alloc const& e) {
38+
assert(new_handler_called == 1);
39+
assert(e.self == &e);
40+
} catch (...) {
41+
assert(false);
42+
}
43+
return 0;
44+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
// UNSUPPORTED: no-exceptions
10+
// UNSUPPORTED: sanitizer-new-delete
11+
12+
#include <new>
13+
#include <cassert>
14+
#include <limits>
15+
#include <cstdlib>
16+
17+
struct construction_key {};
18+
struct my_bad_alloc : std::bad_alloc {
19+
my_bad_alloc(const my_bad_alloc&) : self(this) { std::abort(); }
20+
my_bad_alloc(construction_key) : self(this) {}
21+
const my_bad_alloc* const self;
22+
};
23+
24+
int new_handler_called = 0;
25+
26+
void my_new_handler() {
27+
++new_handler_called;
28+
throw my_bad_alloc(construction_key());
29+
}
30+
31+
int main(int, char**) {
32+
std::set_new_handler(my_new_handler);
33+
try {
34+
void* x = operator new(std::numeric_limits<std::size_t>::max(), std::nothrow);
35+
(void)x;
36+
assert(x == NULL);
37+
} catch (my_bad_alloc const& e) {
38+
assert(new_handler_called == 1);
39+
assert(e.self == &e);
40+
} catch (...) {
41+
assert(false);
42+
}
43+
return 0;
44+
}

libcxxabi/src/stdlib_new_delete.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
// in this shared library, so that they can be overridden by programs
3131
// that define non-weak copies of the functions.
3232

33-
static void* operator_new_impl(std::size_t size) noexcept {
33+
static void* operator_new_impl(std::size_t size) {
3434
if (size == 0)
3535
size = 1;
3636
void* p;
@@ -107,7 +107,7 @@ void operator delete[](void* ptr, size_t) noexcept { ::operator delete[](ptr); }
107107

108108
#if !defined(_LIBCPP_HAS_NO_LIBRARY_ALIGNED_ALLOCATION)
109109

110-
static void* operator_new_aligned_impl(std::size_t size, std::align_val_t alignment) noexcept {
110+
static void* operator_new_aligned_impl(std::size_t size, std::align_val_t alignment) {
111111
if (size == 0)
112112
size = 1;
113113
if (static_cast<size_t>(alignment) < sizeof(void*))

0 commit comments

Comments
 (0)