[libc++] Enable [[nodiscard]] extensions by default

Adding `[[nodiscard]]` to functions is a conforming extension and done extensively in the MSVC STL.

Reviewed By: ldionne, EricWF, #libc

Spies: #libc_vendors, cjdb, mgrang, jloser, libcxx-commits

Differential Revision: https://reviews.llvm.org/D128267
This commit is contained in:
Nikolas Klauser
2022-09-01 12:02:58 +02:00
parent 7691b69d5b
commit 3c355e2881
16 changed files with 82 additions and 108 deletions

View File

@@ -75,7 +75,7 @@ API Changes
- ``_LIBCPP_ENABLE_NODISCARD`` and ``_LIBCPP_DISABLE_NODISCARD_AFTER_CXX17`` are no longer respected.
Any standards-required ``[[nodiscard]]`` applications in C++20 are now always enabled. Any extended applications
can now be enabled by defining ``_LIBCPP_ENABLE_NODISCARD_EXT``.
are now enabled by default and can be disabled by defining ``_LIBCPP_DISABLE_NODISCARD_EXT``.
ABI Affecting Changes
---------------------

View File

@@ -248,8 +248,8 @@ thread safety annotations.
replacement scenarios from working, e.g. replacing `operator new` and
expecting a non-replaced `operator new[]` to call the replaced `operator new`.
**_LIBCPP_ENABLE_NODISCARD_EXT**:
This macro allows the library to apply ``[[nodiscard]]`` to entities as an extension.
**_LIBCPP_DISABLE_NODISCARD_EXT**:
This macro disables library-extensions of ``[[nodiscard]]``.
See :ref:`Extended Applications of [[nodiscard]] <nodiscard extension>` for more information.
**_LIBCPP_DISABLE_DEPRECATION_WARNINGS**:
@@ -340,8 +340,8 @@ Users who want help diagnosing misuses of STL functions may desire a more
liberal application of ``[[nodiscard]]``.
For this reason libc++ provides an extension that does just that! The
extension must be enabled by defining ``_LIBCPP_ENABLE_NODISCARD_EXT``. The extended
applications of ``[[nodiscard]]`` takes two forms:
extension is enabled by default and can be disabled by defining ``_LIBCPP_DISABLE_NODISCARD_EXT``.
The extended applications of ``[[nodiscard]]`` takes two forms:
1. Backporting ``[[nodiscard]]`` to entities declared as such by the
standard in newer dialects, but not in the present one.

View File

@@ -858,13 +858,13 @@ _LIBCPP_BEGIN_NAMESPACE_STD _LIBCPP_END_NAMESPACE_STD
// _LIBCPP_NODISCARD_EXT may be used to apply [[nodiscard]] to entities not
// specified as such as an extension.
# if defined(_LIBCPP_ENABLE_NODISCARD_EXT)
# if !defined(_LIBCPP_DISABLE_NODISCARD_EXT)
# define _LIBCPP_NODISCARD_EXT _LIBCPP_NODISCARD
# else
# define _LIBCPP_NODISCARD_EXT
# endif
# if _LIBCPP_STD_VER > 17 || defined(_LIBCPP_ENABLE_NODISCARD_EXT)
# if _LIBCPP_STD_VER > 17 || !defined(_LIBCPP_DISABLE_NODISCARD_EXT)
# define _LIBCPP_NODISCARD_AFTER_CXX17 _LIBCPP_NODISCARD
# else
# define _LIBCPP_NODISCARD_AFTER_CXX17

View File

@@ -24,7 +24,7 @@ int main() {
};
S a[] = {1, 2, 3, 4};
std::lower_bound(a, a + 4, 0, &S::compare); // expected-error@*:* {{The comparator has to be callable}}
std::minmax({S{1}}, &S::compare); // expected-error@*:* {{The comparator has to be callable}}
std::minmax_element(a, a + 4, &S::compare); // expected-error@*:* {{The comparator has to be callable}}
(void) std::lower_bound(a, a + 4, 0, &S::compare); // expected-error@*:* {{The comparator has to be callable}}
(void) std::minmax({S{1}}, &S::compare); // expected-error@*:* {{The comparator has to be callable}}
(void) std::minmax_element(a, a + 4, &S::compare); // expected-error@*:* {{The comparator has to be callable}}
}

View File

@@ -1,29 +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
//
//===----------------------------------------------------------------------===//
// Test that _LIBCPP_NODISCARD_EXT and _LIBCPP_NODISCARD_AFTER_CXX17 are defined
// to the appropriate warning-generating attribute when _LIBCPP_ENABLE_NODISCARD
// is explicitly provided.
// UNSUPPORTED: c++03
// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_ENABLE_NODISCARD_EXT
#include <__config>
_LIBCPP_NODISCARD_EXT int foo() { return 42; }
_LIBCPP_NODISCARD_AFTER_CXX17 int bar() { return 42; }
int main(int, char**) {
foo(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
bar(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
(void)foo(); // OK. void casts disable the diagnostic.
(void)bar();
return 0;
}

View File

@@ -6,13 +6,13 @@
//
//===----------------------------------------------------------------------===//
// Test that _LIBCPP_NODISCARD_EXT is not defined to [[nodiscard]] unless
// explicitly enabled by _LIBCPP_ENABLE_NODISCARD
// Test that _LIBCPP_NODISCARD_EXT is not defined to [[nodiscard]] when
// _LIBCPP_DISABLE_NODISCARD_EXT is defined
// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_NODISCARD_EXT
#include <__config>
#include "test_macros.h"
_LIBCPP_NODISCARD_EXT int foo() { return 42; }
int main(int, char**) {

View File

@@ -9,7 +9,8 @@
// Test that _LIBCPP_NODISCARD_AFTER_CXX17 works
// #define _LIBCPP_NODISCARD_AFTER_CXX17 [[nodiscard]]
// UNSUPPORTED: c++03, c++11, c++14, c++17
// UNSUPPORTED: c++03
// UNSUPPORTED: (c++11 || c++14 || c++17) && !stdlib=libc++
#include <__config>

View File

@@ -7,7 +7,7 @@
//===----------------------------------------------------------------------===//
// Test that entities declared [[nodiscard]] as at extension by libc++, are
// only actually declared such when _LIBCPP_ENABLE_NODISCARD_EXT is specified.
// declared as such when _LIBCPP_DISABLE_NODISCARD_EXT is specified.
// This test intentionally leaks memory, so it is unsupported under ASAN.
// UNSUPPORTED: asan
@@ -20,7 +20,7 @@
// trigger -Wunused-value warnings.
// ADDITIONAL_COMPILE_FLAGS: -fno-builtin
// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS -D_LIBCPP_DISABLE_NODISCARD_EXT
#include <algorithm>
#include <bit> // bit_cast

View File

@@ -9,13 +9,12 @@
// UNSUPPORTED: c++03
// Test that entities declared [[nodiscard]] as an extension by libc++, are
// only actually declared such when _LIBCPP_ENABLE_NODISCARD_EXT is specified.
// actually declared as such when _LIBCPP_DISABLE_NODISCARD_EXT is not specified.
// All entities to which libc++ applies [[nodiscard]] as an extension should
// be tested here and in nodiscard_extensions.pass.cpp. They should also
// be listed in `UsingLibcxx.rst` in the documentation for the extension.
// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_ENABLE_NODISCARD_EXT
// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
#include <algorithm>

View File

@@ -21,8 +21,6 @@
// Test that we properly apply [[nodiscard]] to lock_guard's constructors,
// which is a libc++ extension.
// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_ENABLE_NODISCARD_EXT
#include <mutex>
int main(int, char**) {

View File

@@ -87,9 +87,9 @@ static_assert(!HasInplaceMergeIter<R<const int*>, const int*>);
template <class In, template <class> class SentWrapper, std::size_t N1, std::size_t N2>
void testInplaceMergeImpl(std::array<int, N1> input, int midIdx, std::array<int, N2> expected) {
std::is_sorted(input.begin(), input.begin() + midIdx);
std::is_sorted(input.begin() + midIdx, input.end());
std::is_sorted(expected.begin(), expected.end());
assert(std::is_sorted(input.begin(), input.begin() + midIdx));
assert(std::is_sorted(input.begin() + midIdx, input.end()));
assert(std::is_sorted(expected.begin(), expected.end()));
using Sent = SentWrapper<In>;

View File

@@ -22,15 +22,15 @@ int main(int, char**) {
typedef cpp17_input_iterator<const int*> Iter;
{
// expected-error@*:* {{std::min_element requires a ForwardIterator}}
std::min_element(Iter(b), Iter(e));
(void) std::min_element(Iter(b), Iter(e));
}
{
// expected-error@*:* {{std::max_element requires a ForwardIterator}}
std::max_element(Iter(b), Iter(e));
(void) std::max_element(Iter(b), Iter(e));
}
{
// expected-error@*:* {{std::minmax_element requires a ForwardIterator}}
std::minmax_element(Iter(b), Iter(e));
(void) std::minmax_element(Iter(b), Iter(e));
}

View File

@@ -663,28 +663,28 @@ TEST_CONSTEXPR_CXX20 bool test() {
using I = Iter;
test(simple_in, [&](I b, I e) { std::any_of(b, e, is_neg); });
test(simple_in, [&](I b, I e) { std::all_of(b, e, is_neg); });
test(simple_in, [&](I b, I e) { std::none_of(b, e, is_neg); });
test(simple_in, [&](I b, I e) { std::find(b, e, T{1}); });
test(simple_in, [&](I b, I e) { std::find_if(b, e, is_neg); });
test(simple_in, [&](I b, I e) { std::find_if_not(b, e, is_neg); });
test(simple_in, [&](I b, I e) { (void) std::any_of(b, e, is_neg); });
test(simple_in, [&](I b, I e) { (void) std::all_of(b, e, is_neg); });
test(simple_in, [&](I b, I e) { (void) std::none_of(b, e, is_neg); });
test(simple_in, [&](I b, I e) { (void) std::find(b, e, T{1}); });
test(simple_in, [&](I b, I e) { (void) std::find_if(b, e, is_neg); });
test(simple_in, [&](I b, I e) { (void) std::find_if_not(b, e, is_neg); });
// TODO: find_first_of
test(simple_in, [&](I b, I e) { std::adjacent_find(b, e); });
test(simple_in, [&](I b, I e) { (void) std::adjacent_find(b, e); });
// TODO: mismatch
// TODO: equal
// TODO: lexicographical_compare
// TODO: partition_point
test(sorted_in, [&](I b, I e) { std::lower_bound(b, e, x); });
test(sorted_in, [&](I b, I e) { std::upper_bound(b, e, x); });
test(sorted_in, [&](I b, I e) { std::equal_range(b, e, x); });
test(sorted_in, [&](I b, I e) { std::binary_search(b, e, x); });
test(sorted_in, [&](I b, I e) { (void) std::lower_bound(b, e, x); });
test(sorted_in, [&](I b, I e) { (void) std::upper_bound(b, e, x); });
test(sorted_in, [&](I b, I e) { (void) std::equal_range(b, e, x); });
test(sorted_in, [&](I b, I e) { (void) std::binary_search(b, e, x); });
// `min`, `max` and `minmax` don't use iterators.
test(simple_in, [&](I b, I e) { std::min_element(b, e); });
test(simple_in, [&](I b, I e) { std::max_element(b, e); });
test(simple_in, [&](I b, I e) { std::minmax_element(b, e); });
test(simple_in, [&](I b, I e) { std::count(b, e, x); });
test(simple_in, [&](I b, I e) { std::count_if(b, e, is_neg); });
test(simple_in, [&](I b, I e) { (void) std::min_element(b, e); });
test(simple_in, [&](I b, I e) { (void) std::max_element(b, e); });
test(simple_in, [&](I b, I e) { (void) std::minmax_element(b, e); });
test(simple_in, [&](I b, I e) { (void) std::count(b, e, x); });
test(simple_in, [&](I b, I e) { (void) std::count_if(b, e, is_neg); });
// TODO: search
// TODO: search_n
// TODO: find_end
@@ -696,27 +696,27 @@ TEST_CONSTEXPR_CXX20 bool test() {
// TODO: is_heap_until
// `clamp` doesn't use iterators.
// TODO: is_permutation
test(simple_in, [&](I b, I e) { std::for_each(b, e, is_neg); });
test(simple_in, [&](I b, I e) { (void) std::for_each(b, e, is_neg); });
#if TEST_STD_VER > 14
test_n(simple_in, [&](I b, size_t n) { std::for_each_n(b, n, is_neg); });
test_n(simple_in, [&](I b, size_t n) { (void) std::for_each_n(b, n, is_neg); });
#endif
test(simple_in, [&](I b, I e) { std::copy(b, e, out); });
test_n(simple_in, [&](I b, size_t n) { std::copy_n(b, n, out); });
test(simple_in, [&](I b, I e) { std::copy_backward(b, e, out + N); });
test(simple_in, [&](I b, I e) { std::copy_if(b, e, out, is_neg); });
test(simple_in, [&](I b, I e) { std::move(b, e, out); });
test(simple_in, [&](I b, I e) { std::move_backward(b, e, out + N); });
test(simple_in, [&](I b, I e) { std::transform(b, e, out, identity); });
test(simple_in, [&](I b, I e) { std::generate(b, e, gen); });
test_n(simple_in, [&](I b, size_t n) { std::generate_n(b, n, gen); });
test(simple_in, [&](I b, I e) { std::remove_copy(b, e, out, x); });
test(simple_in, [&](I b, I e) { std::remove_copy_if(b, e, out, is_neg); });
test(simple_in, [&](I b, I e) { std::replace(b, e, x, y); });
test(simple_in, [&](I b, I e) { std::replace_if(b, e, is_neg, y); });
test(simple_in, [&](I b, I e) { std::replace_copy(b, e, out, x, y); });
test(simple_in, [&](I b, I e) { std::replace_copy_if(b, e, out, is_neg, y); });
test(simple_in, [&](I b, I e) { (void) std::copy(b, e, out); });
test_n(simple_in, [&](I b, size_t n) { (void) std::copy_n(b, n, out); });
test(simple_in, [&](I b, I e) { (void) std::copy_backward(b, e, out + N); });
test(simple_in, [&](I b, I e) { (void) std::copy_if(b, e, out, is_neg); });
test(simple_in, [&](I b, I e) { (void) std::move(b, e, out); });
test(simple_in, [&](I b, I e) { (void) std::move_backward(b, e, out + N); });
test(simple_in, [&](I b, I e) { (void) std::transform(b, e, out, identity); });
test(simple_in, [&](I b, I e) { (void) std::generate(b, e, gen); });
test_n(simple_in, [&](I b, size_t n) { (void) std::generate_n(b, n, gen); });
test(simple_in, [&](I b, I e) { (void) std::remove_copy(b, e, out, x); });
test(simple_in, [&](I b, I e) { (void) std::remove_copy_if(b, e, out, is_neg); });
test(simple_in, [&](I b, I e) { (void) std::replace(b, e, x, y); });
test(simple_in, [&](I b, I e) { (void) std::replace_if(b, e, is_neg, y); });
test(simple_in, [&](I b, I e) { (void) std::replace_copy(b, e, out, x, y); });
test(simple_in, [&](I b, I e) { (void) std::replace_copy_if(b, e, out, is_neg, y); });
// TODO: swap_ranges
test(simple_in, [&](I b, I e) { std::reverse_copy(b, e, out); });
test(simple_in, [&](I b, I e) { (void) std::reverse_copy(b, e, out); });
// TODO: rotate_copy
// TODO: sample
// TODO: unique_copy
@@ -727,29 +727,29 @@ TEST_CONSTEXPR_CXX20 bool test() {
// TODO: set_intersection
// TODO: set_symmetric_difference
// TODO: set_union
test(simple_in, [&](I b, I e) { std::remove(b, e, x); });
test(simple_in, [&](I b, I e) { std::remove_if(b, e, is_neg); });
test(simple_in, [&](I b, I e) { std::reverse(b, e); });
test(simple_in, [&](I b, I e) { (void) std::remove(b, e, x); });
test(simple_in, [&](I b, I e) { (void) std::remove_if(b, e, is_neg); });
test(simple_in, [&](I b, I e) { (void) std::reverse(b, e); });
// TODO: rotate
if (!TEST_IS_CONSTANT_EVALUATED)
test(simple_in, [&](I b, I e) { std::shuffle(b, e, rand_gen()); });
test(simple_in, [&](I b, I e) { (void) std::shuffle(b, e, rand_gen()); });
// TODO: unique
test(simple_in, [&](I b, I e) { std::partition(b, e, is_neg); });
test(simple_in, [&](I b, I e) { (void) std::partition(b, e, is_neg); });
if (!TEST_IS_CONSTANT_EVALUATED)
test(simple_in, [&](I b, I e) { std::stable_partition(b, e, is_neg); });
test(simple_in, [&](I b, I e) { (void) std::stable_partition(b, e, is_neg); });
if (!TEST_IS_CONSTANT_EVALUATED)
test(sort_test_in, [&](I b, I e) { std::sort(b, e); });
test(sort_test_in, [&](I b, I e) { (void) std::sort(b, e); });
if (!TEST_IS_CONSTANT_EVALUATED)
test(sort_test_in, [&](I b, I e) { std::stable_sort(b, e); });
test(sort_test_in, [&](I b, I e) { (void) std::stable_sort(b, e); });
// TODO: partial_sort
// TODO: nth_element
// TODO: inplace_merge
test(simple_in, [&](I b, I e) { std::make_heap(b, e); });
test(simple_in, [&](I b, I e) { (void) std::make_heap(b, e); });
// TODO: push_heap
// TODO: pop_heap
// TODO: sort_heap
test(simple_in, [&](I b, I e) { std::prev_permutation(b, e); });
test(simple_in, [&](I b, I e) { std::next_permutation(b, e); });
test(simple_in, [&](I b, I e) { (void) std::prev_permutation(b, e); });
test(simple_in, [&](I b, I e) { (void) std::next_permutation(b, e); });
// TODO: algorithms in `<numeric>`
// TODO: algorithms in `<memory>`

View File

@@ -6,7 +6,8 @@
//
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14, c++17
// UNSUPPORTED: c++03
// UNSUPPORTED: (c++11 || c++14 || c++17) && !stdlib=libc++
// Make sure the various node handles mark their .empty() method with
// [[nodiscard]] starting with C++20
@@ -16,7 +17,10 @@
#include <unordered_map>
#include <unordered_set>
#include "test_macros.h"
void test() {
#if TEST_STD_VER >= 17
{
std::map<int, int>::node_type node;
node.empty(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
@@ -25,6 +29,7 @@ void test() {
std::multimap<int, int>::node_type node;
node.empty(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
}
#endif // TEST_STD_VER >= 17
{
std::set<int> node;
node.empty(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}

View File

@@ -10,7 +10,8 @@
// template <class T> constexpr T* launder(T* p) noexcept;
// UNSUPPORTED: c++03, c++11, c++14, c++17
// UNSUPPORTED: c++03, c++11, c++14
// UNSUPPORETD: c++17 && !stdlib=libc++
#include <new>
#include <cassert>

View File

@@ -1,13 +1,12 @@
//===----------------------------------------------------------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is dual licensed under the MIT and the University of Illinois Open
// Source Licenses. See LICENSE.TXT for details.
// 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
//
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++98, c++03, c++11, c++14, c++17
// UNSUPPORTED: c++03, c++11, c++14, c++17
// #include <memory>