Skip to content

Commit

Permalink
[libcxx] Make allocator<T>:allocate throw bad_array_new_length
Browse files Browse the repository at this point in the history
Currently the member functions std::allocator<T>::allocate,
std::experimental::pmr::polymorphic_allocator::allocate and
std::resource_adaptor<T>::do_allocate throw an exception of type
std::length_error when the requested size exceeds the maximum size.

According to the C++ standard ([allocator.members]/4,
[mem.poly.allocator.mem]/1), std::allocator<T>::allocate and
std::pmr::polymorphic_allocator::allocate must throw a
std::bad_array_new_length exception in this case.

The patch fixes the issue with std::allocator<T>::allocate and changes
the type the exception thrown by
std::experimental::pmr::resource_adaptor<T>::do_allocate to
std::bad_array_new_length as well for consistency.

The patch resolves LWG 3237, LWG 3038 and LWG 3190.

Reviewed By: ldionne, #libc, Quuxplusone

Differential Revision: https://reviews.llvm.org/D110846
  • Loading branch information
miyuki committed Oct 18, 2021
1 parent 5b949a6 commit be10b1f
Show file tree
Hide file tree
Showing 9 changed files with 33 additions and 22 deletions.
6 changes: 6 additions & 0 deletions libcxx/docs/ReleaseNotes.rst
Expand Up @@ -72,6 +72,12 @@ API Changes
Calls to these functions where the template argument was deduced by the
compiler are unaffected by this change.

- The functions ``std::allocator<T>::allocate`` and
``std::experimental::pmr::polymorphic_allocator<T>::allocate`` now throw
an exception of type ``std::bad_array_new_length`` when the requested size
exceeds the maximum supported size, as required by the C++ standard.
Previously the type ``std::length_error`` was used.

Build System Changes
--------------------

Expand Down
6 changes: 3 additions & 3 deletions libcxx/docs/Status/Cxx20Issues.csv
Expand Up @@ -108,7 +108,7 @@
"`3025 <https://wg21.link/LWG3025>`__","Map-like container deduction guides should use ``pair<Key, T>``\ , not ``pair<const Key, T>``\ ","San Diego","|Complete|",""
"`3031 <https://wg21.link/LWG3031>`__","Algorithms and predicates with non-const reference arguments","San Diego","",""
"`3037 <https://wg21.link/LWG3037>`__","``polymorphic_allocator``\ and incomplete types","San Diego","",""
"`3038 <https://wg21.link/LWG3038>`__","``polymorphic_allocator::allocate``\ should not allow integer overflow to create vulnerabilities","San Diego","",""
"`3038 <https://wg21.link/LWG3038>`__","``polymorphic_allocator::allocate``\ should not allow integer overflow to create vulnerabilities","San Diego","|Complete|","14.0"
"`3054 <https://wg21.link/LWG3054>`__","``uninitialized_copy``\ appears to not be able to meet its exception-safety guarantee","San Diego","",""
"`3065 <https://wg21.link/LWG3065>`__","LWG 2989 missed that all ``path``\ 's other operators should be hidden friends as well","San Diego","|Complete|",""
"`3096 <https://wg21.link/LWG3096>`__","``path::lexically_relative``\ is confused by trailing slashes","San Diego","|Complete|",""
Expand Down Expand Up @@ -162,7 +162,7 @@
"","","","",""
"`3231 <https://wg21.link/LWG3231>`__","``year_month_day_last::day``\ specification does not cover ``!ok()``\ values","Belfast","|Nothing To Do|",""
"`3225 <https://wg21.link/LWG3225>`__","``zoned_time``\ converting constructor shall not be ``noexcept``\ ","Belfast","","","|chrono|"
"`3190 <https://wg21.link/LWG3190>`__","``std::allocator::allocate``\ sometimes returns too little storage","Belfast","",""
"`3190 <https://wg21.link/LWG3190>`__","``std::allocator::allocate``\ sometimes returns too little storage","Belfast","|Complete|","14.0"
"`3218 <https://wg21.link/LWG3218>`__","Modifier for ``%d``\ parse flag does not match POSIX and ``format``\ specification","Belfast","","","|chrono| |format|"
"`3224 <https://wg21.link/LWG3224>`__","``zoned_time``\ constructor from ``TimeZonePtr``\ does not specify initialization of ``tp_``\ ","Belfast","","","|chrono|"
"`3230 <https://wg21.link/LWG3230>`__","Format specifier ``%y/%Y``\ is missing locale alternative versions","Belfast","","","|chrono| |format|"
Expand Down Expand Up @@ -200,7 +200,7 @@
"`3201 <https://wg21.link/LWG3201>`__","``lerp``\ should be marked as ``noexcept``\ ","Prague","|Complete|",""
"`3226 <https://wg21.link/LWG3226>`__","``zoned_time``\ constructor from ``string_view``\ should accept ``zoned_time<Duration2, TimeZonePtr2>``\ ","Prague","","","|chrono|"
"`3233 <https://wg21.link/LWG3233>`__","Broken requirements for ``shared_ptr``\ converting constructors","Prague","",""
"`3237 <https://wg21.link/LWG3237>`__","LWG 3038 and 3190 have inconsistent PRs","Prague","",""
"`3237 <https://wg21.link/LWG3237>`__","LWG 3038 and 3190 have inconsistent PRs","Prague","|Complete|","14.0"
"`3238 <https://wg21.link/LWG3238>`__","Insufficiently-defined behavior of ``std::function``\ deduction guides","Prague","",""
"`3242 <https://wg21.link/LWG3242>`__","``std::format``\ : missing rules for ``arg-id``\ in ``width``\ and ``precision``\ ","Prague","|Complete|","Clang 14","|format|"
"`3243 <https://wg21.link/LWG3243>`__","``std::format``\ and negative zeroes","Prague","","","|format|"
Expand Down
6 changes: 2 additions & 4 deletions libcxx/include/__memory/allocator.h
Expand Up @@ -98,8 +98,7 @@ class _LIBCPP_TEMPLATE_VIS allocator
_LIBCPP_NODISCARD_AFTER_CXX17 _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17
_Tp* allocate(size_t __n) {
if (__n > allocator_traits<allocator>::max_size(*this))
__throw_length_error("allocator<T>::allocate(size_t n)"
" 'n' exceeds maximum supported size");
__throw_bad_array_new_length();
if (__libcpp_is_constant_evaluated()) {
return static_cast<_Tp*>(::operator new(__n * sizeof(_Tp)));
} else {
Expand Down Expand Up @@ -181,8 +180,7 @@ class _LIBCPP_TEMPLATE_VIS allocator<const _Tp>
_LIBCPP_NODISCARD_AFTER_CXX17 _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17
const _Tp* allocate(size_t __n) {
if (__n > allocator_traits<allocator>::max_size(*this))
__throw_length_error("allocator<const T>::allocate(size_t n)"
" 'n' exceeds maximum supported size");
__throw_bad_array_new_length();
if (__libcpp_is_constant_evaluated()) {
return static_cast<const _Tp*>(::operator new(__n * sizeof(_Tp)));
} else {
Expand Down
14 changes: 4 additions & 10 deletions libcxx/include/experimental/memory_resource
Expand Up @@ -183,11 +183,8 @@ public:
// 8.6.3, memory.polymorphic.allocator.mem
_LIBCPP_INLINE_VISIBILITY
_ValueType* allocate(size_t __n) {
if (__n > __max_size()) {
__throw_length_error(
"std::experimental::pmr::polymorphic_allocator<T>::allocate(size_t n)"
" 'n' exceeds maximum supported size");
}
if (__n > __max_size())
__throw_bad_array_new_length();
return static_cast<_ValueType*>(
__res_->allocate(__n * sizeof(_ValueType), _LIBCPP_ALIGNOF(_ValueType))
);
Expand Down Expand Up @@ -384,11 +381,8 @@ public:
private:
virtual void * do_allocate(size_t __bytes, size_t)
{
if (__bytes > __max_size()) {
__throw_length_error(
"std::experimental::pmr::resource_adaptor<T>::do_allocate(size_t bytes, size_t align)"
" 'bytes' exceeds maximum supported size");
}
if (__bytes > __max_size())
__throw_bad_array_new_length();
size_t __s = __aligned_allocation_size(__bytes, _MaxAlign) / _MaxAlign;
return __alloc_.allocate(__s);
}
Expand Down
10 changes: 10 additions & 0 deletions libcxx/include/new
Expand Up @@ -149,6 +149,16 @@ _LIBCPP_FUNC_VIS new_handler get_new_handler() _NOEXCEPT;

_LIBCPP_NORETURN _LIBCPP_FUNC_VIS void __throw_bad_alloc(); // not in C++ spec

_LIBCPP_NORETURN inline _LIBCPP_INLINE_VISIBILITY
void __throw_bad_array_new_length()
{
#ifndef _LIBCPP_NO_EXCEPTIONS
throw bad_array_new_length();
#else
_VSTD::abort();
#endif
}

#if !defined(_LIBCPP_HAS_NO_LIBRARY_ALIGNED_ALLOCATION) && \
!defined(_LIBCPP_ABI_VCRUNTIME)
#ifndef _LIBCPP_CXX03_LANG
Expand Down
Expand Up @@ -7,6 +7,8 @@
{'is_defined': False, 'name': '_ZNSt13runtime_errorD2Ev', 'type': 'FUNC'}
{'is_defined': False, 'name': '_ZNSt14overflow_errorD1Ev', 'type': 'FUNC'}
{'is_defined': False, 'name': '_ZNSt16invalid_argumentD1Ev', 'type': 'FUNC'}
{'is_defined': False, 'name': '_ZNSt20bad_array_new_lengthC1Ev', 'type': 'FUNC'}
{'is_defined': False, 'name': '_ZNSt20bad_array_new_lengthD1Ev', 'type': 'FUNC'}
{'is_defined': False, 'name': '_ZNSt8bad_castC1Ev', 'type': 'FUNC'}
{'is_defined': False, 'name': '_ZNSt8bad_castD1Ev', 'type': 'FUNC'}
{'is_defined': False, 'name': '_ZNSt8bad_castD2Ev', 'type': 'FUNC'}
Expand All @@ -20,6 +22,7 @@
{'is_defined': False, 'name': '_ZTISt13runtime_error', 'size': 0, 'type': 'OBJECT'}
{'is_defined': False, 'name': '_ZTISt14overflow_error', 'size': 0, 'type': 'OBJECT'}
{'is_defined': False, 'name': '_ZTISt16invalid_argument', 'size': 0, 'type': 'OBJECT'}
{'is_defined': False, 'name': '_ZTISt20bad_array_new_length', 'size': 0, 'type': 'OBJECT'}
{'is_defined': False, 'name': '_ZTISt8bad_cast', 'size': 0, 'type': 'OBJECT'}
{'is_defined': False, 'name': '_ZTISt9bad_alloc', 'size': 0, 'type': 'OBJECT'}
{'is_defined': False, 'name': '_ZTISt9exception', 'size': 0, 'type': 'OBJECT'}
Expand Down
Expand Up @@ -61,19 +61,19 @@ void testAllocForSizeThrows() {
size_t sizeTypeMax = std::numeric_limits<std::size_t>::max();
if (maxSize != sizeTypeMax)
{
// Test that allocating size_t(~0) throws bad alloc.
// Test that allocating size_t(~0) throws bad_array_new_length.
try {
a.allocate(sizeTypeMax);
assert(false);
} catch (std::exception const&) {
} catch (std::bad_array_new_length const&) {
}

// Test that allocating even one more than the max size does throw.
size_t overSize = maxSize + 1;
try {
a.allocate(overSize);
assert(false);
} catch (std::exception const&) {
} catch (std::bad_array_new_length const&) {
}
}
}
Expand Down
Expand Up @@ -99,7 +99,7 @@ void check_alloc_max_size() {
try {
m1.allocate(size);
assert(false);
} catch (std::exception const&) {
} catch (std::bad_array_new_length const&) {
}
}
#endif
Expand Down
Expand Up @@ -24,7 +24,7 @@ void test_max(size_t count)
try {
TEST_IGNORE_NODISCARD a.allocate(count);
assert(false);
} catch (const std::exception &) {
} catch (const std::bad_array_new_length &) {
}
}

Expand Down

0 comments on commit be10b1f

Please sign in to comment.