[libc++][memory] Applied [[nodiscard]] to more functions (#172131)

`[[nodiscard]]` should be applied to functions where discarding the
return value is most likely a correctness issue.

- https://libcxx.llvm.org/CodingGuidelines.html
- https://wg21.link/allocator.traits
- https://wg21.link/specialized.addressof
- https://wg21.link/ptr.align
- https://timsong-cpp.github.io/cppwp/n4659/depr.default.allocator
- https://timsong-cpp.github.io/cppwp/n4659/depr.storage.iterator

Towards #172124
This commit is contained in:
Hristo Hristov 2026-01-08 12:05:43 +02:00 committed by GitHub
parent 6c2c0b9da2
commit e593dbc184
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 124 additions and 68 deletions

View File

@ -19,7 +19,8 @@
_LIBCPP_BEGIN_NAMESPACE_STD
template <class _Tp>
inline _LIBCPP_CONSTEXPR_SINCE_CXX17 _LIBCPP_NO_CFI _LIBCPP_HIDE_FROM_ABI _Tp* addressof(_Tp& __x) _NOEXCEPT {
[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX17 _LIBCPP_NO_CFI _LIBCPP_HIDE_FROM_ABI _Tp*
addressof(_Tp& __x) _NOEXCEPT {
return __builtin_addressof(__x);
}
@ -27,24 +28,25 @@ inline _LIBCPP_CONSTEXPR_SINCE_CXX17 _LIBCPP_NO_CFI _LIBCPP_HIDE_FROM_ABI _Tp* a
// Objective-C++ Automatic Reference Counting uses qualified pointers
// that require special addressof() signatures.
template <class _Tp>
inline _LIBCPP_HIDE_FROM_ABI __strong _Tp* addressof(__strong _Tp& __x) _NOEXCEPT {
[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI __strong _Tp* addressof(__strong _Tp& __x) _NOEXCEPT {
return &__x;
}
# if __has_feature(objc_arc_weak)
template <class _Tp>
inline _LIBCPP_HIDE_FROM_ABI __weak _Tp* addressof(__weak _Tp& __x) _NOEXCEPT {
[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI __weak _Tp* addressof(__weak _Tp& __x) _NOEXCEPT {
return &__x;
}
# endif
template <class _Tp>
inline _LIBCPP_HIDE_FROM_ABI __autoreleasing _Tp* addressof(__autoreleasing _Tp& __x) _NOEXCEPT {
[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI __autoreleasing _Tp* addressof(__autoreleasing _Tp& __x) _NOEXCEPT {
return &__x;
}
template <class _Tp>
inline _LIBCPP_HIDE_FROM_ABI __unsafe_unretained _Tp* addressof(__unsafe_unretained _Tp& __x) _NOEXCEPT {
[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI __unsafe_unretained _Tp*
addressof(__unsafe_unretained _Tp& __x) _NOEXCEPT {
return &__x;
}
#endif

View File

@ -120,10 +120,11 @@ public:
typedef allocator<_Up> other;
};
_LIBCPP_DEPRECATED_IN_CXX17 _LIBCPP_HIDE_FROM_ABI pointer address(reference __x) const _NOEXCEPT {
[[__nodiscard__]] _LIBCPP_DEPRECATED_IN_CXX17 _LIBCPP_HIDE_FROM_ABI pointer address(reference __x) const _NOEXCEPT {
return std::addressof(__x);
}
_LIBCPP_DEPRECATED_IN_CXX17 _LIBCPP_HIDE_FROM_ABI const_pointer address(const_reference __x) const _NOEXCEPT {
[[__nodiscard__]] _LIBCPP_DEPRECATED_IN_CXX17 _LIBCPP_HIDE_FROM_ABI const_pointer
address(const_reference __x) const _NOEXCEPT {
return std::addressof(__x);
}
@ -131,7 +132,7 @@ public:
return allocate(__n);
}
_LIBCPP_DEPRECATED_IN_CXX17 _LIBCPP_HIDE_FROM_ABI size_type max_size() const _NOEXCEPT {
[[__nodiscard__]] _LIBCPP_DEPRECATED_IN_CXX17 _LIBCPP_HIDE_FROM_ABI size_type max_size() const _NOEXCEPT {
return size_type(~0) / sizeof(_Tp);
}

View File

@ -314,23 +314,25 @@ struct allocator_traits {
}
template <class _Ap = _Alloc, __enable_if_t<__has_max_size_v<const _Ap>, int> = 0>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 static size_type max_size(const allocator_type& __a) _NOEXCEPT {
[[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 static size_type
max_size(const allocator_type& __a) _NOEXCEPT {
_LIBCPP_SUPPRESS_DEPRECATED_PUSH
return __a.max_size();
_LIBCPP_SUPPRESS_DEPRECATED_POP
}
template <class _Ap = _Alloc, __enable_if_t<!__has_max_size_v<const _Ap>, int> = 0>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 static size_type max_size(const allocator_type&) _NOEXCEPT {
[[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 static size_type
max_size(const allocator_type&) _NOEXCEPT {
return numeric_limits<size_type>::max() / sizeof(value_type);
}
template <class _Ap = _Alloc, __enable_if_t<__has_select_on_container_copy_construction_v<const _Ap>, int> = 0>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 static allocator_type
[[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 static allocator_type
select_on_container_copy_construction(const allocator_type& __a) {
return __a.select_on_container_copy_construction();
}
template <class _Ap = _Alloc, __enable_if_t<!__has_select_on_container_copy_construction_v<const _Ap>, int> = 0>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 static allocator_type
[[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 static allocator_type
select_on_container_copy_construction(const allocator_type& __a) {
return __a;
}

View File

@ -23,7 +23,7 @@ _LIBCPP_BEGIN_NAMESPACE_STD
#if _LIBCPP_STD_VER >= 26
template <size_t _Alignment, class _Tp>
_LIBCPP_HIDE_FROM_ABI bool is_sufficiently_aligned(_Tp* __ptr) {
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI bool is_sufficiently_aligned(_Tp* __ptr) {
return reinterpret_cast<uintptr_t>(__ptr) % _Alignment == 0;
}

View File

@ -46,7 +46,7 @@ public:
typedef void reference;
_LIBCPP_HIDE_FROM_ABI explicit raw_storage_iterator(_OutputIterator __x) : __x_(__x) {}
_LIBCPP_HIDE_FROM_ABI raw_storage_iterator& operator*() { return *this; }
[[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI raw_storage_iterator& operator*() { return *this; }
_LIBCPP_HIDE_FROM_ABI raw_storage_iterator& operator=(const _Tp& __element) {
::new ((void*)std::addressof(*__x_)) _Tp(__element);
return *this;
@ -67,7 +67,7 @@ public:
return __t;
}
# if _LIBCPP_STD_VER >= 14
_LIBCPP_HIDE_FROM_ABI _OutputIterator base() const { return __x_; }
[[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _OutputIterator base() const { return __x_; }
# endif
};

View File

@ -6,37 +6,122 @@
//
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03
// <memory>
// check that <memory> functions are marked [[nodiscard]]
// Check that functions are marked [[nodiscard]]
// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_ENABLE_CXX20_REMOVED_RAW_STORAGE_ITERATOR
// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_ENABLE_CXX20_REMOVED_TEMPORARY_BUFFER
// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
// clang-format off
#include <memory>
#include "test_macros.h"
void test() {
std::get_temporary_buffer<int>(0); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
}
int i = 0;
void test_allocator_traits() {
std::allocator<int> allocator;
std::allocator_traits<std::allocator<int>> allocator_traits;
allocator_traits.allocate(allocator, 1); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
allocator_traits.allocate(allocator, 1, nullptr); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
}
void test_allocator() {
std::allocator<int> allocator;
allocator.allocate(1); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
#if TEST_STD_VER <= 17
allocator.allocate(1, nullptr); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
{
std::addressof(i); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
// expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}}
std::get_temporary_buffer<int>(0);
#if _LIBCPP_STD_VER >= 26
// expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}}
std::is_sufficiently_aligned<2>(&i);
#endif
}
{
struct Alloc {
using value_type = int;
value_type* allocate(std::size_t) { return nullptr; }
} allocator;
using AllocTraits = std::allocator_traits<Alloc>;
struct HintedAlloc {
using value_type = int;
using size_type = std::size_t;
using const_void_pointer = const void*;
value_type* allocate(size_type) { return nullptr; }
value_type* allocate(size_type, const_void_pointer) { return nullptr; }
} hintedAllocator;
using HintedAllocTraits = std::allocator_traits<HintedAlloc>;
// expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}}
AllocTraits::allocate(allocator, 1);
// expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}}
HintedAllocTraits::allocate(hintedAllocator, 1, nullptr);
// expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}}
AllocTraits::allocate(allocator, 1, nullptr);
#if TEST_STD_VER >= 23
allocator.allocate_at_least(1); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
// expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}}
AllocTraits::allocate_at_least(allocator, 1);
#endif
struct SizedAlloc {
using value_type = int;
using size_type = std::size_t;
value_type* allocate(std::size_t) { return nullptr; }
value_type* allocate(std::size_t, const void*) { return nullptr; }
size_type max_size() const { return 0; }
} sizedAllocator;
using SizedAllocTraits = std::allocator_traits<SizedAlloc>;
// expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}}
SizedAllocTraits::max_size(sizedAllocator);
// expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}}
AllocTraits::max_size(allocator);
struct SelectAlloc {
using value_type = int;
using const_void_pointer = const void*;
value_type* allocate(std::size_t) { return nullptr; }
value_type* allocate(std::size_t, const void*) { return nullptr; }
SelectAlloc select_on_container_copy_construction() const { return SelectAlloc(); };
} selectAllocator;
using SelectAllocTraits = std::allocator_traits<SelectAlloc>;
// expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}}
SelectAllocTraits::select_on_container_copy_construction(selectAllocator);
// expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}}
AllocTraits::select_on_container_copy_construction(allocator);
}
{
std::allocator<int> allocator;
allocator.allocate(1); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
#if TEST_STD_VER >= 23
// expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}}
allocator.allocate_at_least(1);
#endif
#if TEST_STD_VER <= 17
const int ci = 0;
allocator.address(i); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
allocator.address(ci); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
// expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}}
allocator.allocate(1, nullptr);
allocator.max_size(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
#endif
}
#if TEST_STD_VER >= 14
{
std::raw_storage_iterator<int*, int> it{nullptr};
*it; // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
it.base(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
}
#endif
}

View File

@ -1,34 +0,0 @@
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// <memory>
// template <class Alloc>
// struct allocator_traits
// {
// static constexpr pointer allocate(allocator_type& a, size_type n);
// ...
// };
// UNSUPPORTED: c++03, c++11, c++14, c++17
#include <cstddef>
#include <memory>
template <class T>
struct A {
typedef T value_type;
value_type* allocate(std::size_t n);
value_type* allocate(std::size_t n, const void* p);
};
void f() {
A<int> a;
std::allocator_traits<A<int> >::allocate(a, 10); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
std::allocator_traits<A<int> >::allocate(a, 10, nullptr); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
}