[libcxx-commits] [libcxx] [libc++][functional] Implement `not_fn<NTTP>` (PR #86133)
Jakub Mazurkiewicz via libcxx-commits
libcxx-commits at lists.llvm.org
Tue Apr 9 01:56:12 PDT 2024
https://github.com/JMazurkiewicz updated https://github.com/llvm/llvm-project/pull/86133
>From 228c8ea2f0f20b325229762c642a86fc5160fa57 Mon Sep 17 00:00:00 2001
From: Jakub Mazurkiewicz <mazkuba3 at gmail.com>
Date: Tue, 19 Mar 2024 21:08:30 +0100
Subject: [PATCH] [libc++] Implement `not_fn<NTTP>`
Implement `not_fn<NTTP>` function from P2714R1 Bind front and back to NTTP callables.
---
libcxx/docs/FeatureTestMacroTable.rst | 2 +
libcxx/docs/Status/Cxx2c.rst | 1 +
libcxx/docs/Status/Cxx2cPapers.csv | 2 +-
libcxx/include/__functional/not_fn.h | 23 ++
libcxx/include/functional | 4 +-
libcxx/include/version | 5 +-
.../func.not.fn/not_fn.nttp.compile.pass.cpp | 29 ++
.../not_fn.nttp.nodiscard.verify.cpp | 21 ++
.../functional.version.compile.pass.cpp | 5 +-
.../version.version.compile.pass.cpp | 5 +-
.../func.not_fn/not_fn.nttp.pass.cpp | 294 ++++++++++++++++++
.../func.not_fn/not_fn.nttp.verify.cpp | 29 ++
.../generate_feature_test_macro_components.py | 4 +-
13 files changed, 415 insertions(+), 9 deletions(-)
create mode 100644 libcxx/test/libcxx/utilities/function.objects/func.not.fn/not_fn.nttp.compile.pass.cpp
create mode 100644 libcxx/test/libcxx/utilities/function.objects/func.not.fn/not_fn.nttp.nodiscard.verify.cpp
create mode 100644 libcxx/test/std/utilities/function.objects/func.not_fn/not_fn.nttp.pass.cpp
create mode 100644 libcxx/test/std/utilities/function.objects/func.not_fn/not_fn.nttp.verify.cpp
diff --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst
index 014ac1c31e6308..c44a980683f12a 100644
--- a/libcxx/docs/FeatureTestMacroTable.rst
+++ b/libcxx/docs/FeatureTestMacroTable.rst
@@ -438,6 +438,8 @@ Status
---------------------------------------------------------- -----------------
``__cpp_lib_linalg`` *unimplemented*
---------------------------------------------------------- -----------------
+ ``__cpp_lib_not_fn`` ``202306L``
+ ---------------------------------------------------------- -----------------
``__cpp_lib_out_ptr`` *unimplemented*
---------------------------------------------------------- -----------------
``__cpp_lib_ranges_concat`` *unimplemented*
diff --git a/libcxx/docs/Status/Cxx2c.rst b/libcxx/docs/Status/Cxx2c.rst
index a7ebc4662f517c..81eeee3367d8c2 100644
--- a/libcxx/docs/Status/Cxx2c.rst
+++ b/libcxx/docs/Status/Cxx2c.rst
@@ -40,6 +40,7 @@ Paper Status
.. note::
.. [#note-P2510R3] This paper is applied as DR against C++20. (MSVC STL and libstdc++ will do the same.)
+ .. [#note-P2714R1] ``not_fn`` is done.
.. _issues-status-cxx2c:
diff --git a/libcxx/docs/Status/Cxx2cPapers.csv b/libcxx/docs/Status/Cxx2cPapers.csv
index fa11da62bc080e..96b23afc599a04 100644
--- a/libcxx/docs/Status/Cxx2cPapers.csv
+++ b/libcxx/docs/Status/Cxx2cPapers.csv
@@ -24,7 +24,7 @@
"`P1383R2 <https://wg21.link/P1383R2>`__","LWG","More ``constexpr`` for ``<cmath>`` and ``<complex>``","Varna June 2023","","",""
"`P2734R0 <https://wg21.link/P2734R0>`__","LWG","Adding the new SI prefixes","Varna June 2023","|Complete|","17.0",""
"`P2548R6 <https://wg21.link/P2548R6>`__","LWG","``copyable_function``","Varna June 2023","","",""
-"`P2714R1 <https://wg21.link/P2714R1>`__","LWG","Bind front and back to NTTP callables","Varna June 2023","","",""
+"`P2714R1 <https://wg21.link/P2714R1>`__","LWG","Bind front and back to NTTP callables","Varna June 2023","|Partial| [#note-P2714R1]_","19.0",""
"`P2630R4 <https://wg21.link/P2630R4>`__","LWG","``submdspan``","Varna June 2023","","",""
"","","","","","",""
"`P0543R3 <https://wg21.link/P0543R3>`__","LWG","Saturation arithmetic","Kona November 2023","|Complete|","18.0",""
diff --git a/libcxx/include/__functional/not_fn.h b/libcxx/include/__functional/not_fn.h
index 4b3ce5524a7434..d5e6d5f9939d2d 100644
--- a/libcxx/include/__functional/not_fn.h
+++ b/libcxx/include/__functional/not_fn.h
@@ -16,6 +16,8 @@
#include <__type_traits/decay.h>
#include <__type_traits/enable_if.h>
#include <__type_traits/is_constructible.h>
+#include <__type_traits/is_member_pointer.h>
+#include <__type_traits/is_pointer.h>
#include <__utility/forward.h>
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
@@ -48,6 +50,27 @@ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 auto not_fn(_Fn&& __f) {
#endif // _LIBCPP_STD_VER >= 17
+#if _LIBCPP_STD_VER >= 26
+
+template <auto _Fn>
+struct __nttp_not_fn_t {
+ template <class... _Args>
+ _LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Args&&... __args) const
+ noexcept(noexcept(!std::invoke(_Fn, std::forward<_Args>(__args)...)))
+ -> decltype(!std::invoke(_Fn, std::forward<_Args>(__args)...)) {
+ return !std::invoke(_Fn, std::forward<_Args>(__args)...);
+ }
+};
+
+template <auto _Fn>
+_LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI constexpr auto not_fn() noexcept {
+ if constexpr (using _Ty = decltype(_Fn); is_pointer_v<_Ty> || is_member_pointer_v<_Ty>)
+ static_assert(_Fn != nullptr, "f cannot be equal to nullptr");
+ return __nttp_not_fn_t<_Fn>();
+}
+
+#endif // _LIBCPP_STD_VER >= 26
+
_LIBCPP_END_NAMESPACE_STD
#endif // _LIBCPP___FUNCTIONAL_NOT_FN_H
diff --git a/libcxx/include/functional b/libcxx/include/functional
index a2774a48bda0ee..faf97f17daec3d 100644
--- a/libcxx/include/functional
+++ b/libcxx/include/functional
@@ -205,7 +205,9 @@ template <class Predicate> // deprecated in C++17, removed in C++20
binary_negate<Predicate> not2(const Predicate& pred);
template <class F>
-constexpr unspecified not_fn(F&& f); // C++17, constexpr in C++20
+ constexpr unspecified not_fn(F&& f); // C++17, constexpr in C++20
+template <auto f>
+ constexpr unspecified not_fn() noexcept; // C++26
template<class T> struct is_bind_expression;
template<class T> struct is_placeholder;
diff --git a/libcxx/include/version b/libcxx/include/version
index 90dc1b279c6c25..8823c316a053bc 100644
--- a/libcxx/include/version
+++ b/libcxx/include/version
@@ -162,7 +162,8 @@ __cpp_lib_nonmember_container_access 201411L <array> <deque>
<iterator> <list> <map>
<regex> <set> <string>
<unordered_map> <unordered_set> <vector>
-__cpp_lib_not_fn 201603L <functional>
+__cpp_lib_not_fn 202306L <functional>
+ 201603L // C++17
__cpp_lib_null_iterators 201304L <iterator>
__cpp_lib_optional 202110L <optional>
201606L // C++17
@@ -522,6 +523,8 @@ __cpp_lib_within_lifetime 202306L <type_traits>
// # define __cpp_lib_generate_random 202403L
// # define __cpp_lib_hazard_pointer 202306L
// # define __cpp_lib_linalg 202311L
+# undef __cpp_lib_not_fn
+# define __cpp_lib_not_fn 202306L
# undef __cpp_lib_out_ptr
// # define __cpp_lib_out_ptr 202311L
// # define __cpp_lib_ranges_concat 202403L
diff --git a/libcxx/test/libcxx/utilities/function.objects/func.not.fn/not_fn.nttp.compile.pass.cpp b/libcxx/test/libcxx/utilities/function.objects/func.not.fn/not_fn.nttp.compile.pass.cpp
new file mode 100644
index 00000000000000..8390bd76f5723d
--- /dev/null
+++ b/libcxx/test/libcxx/utilities/function.objects/func.not.fn/not_fn.nttp.compile.pass.cpp
@@ -0,0 +1,29 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++03, c++11, c++14, c++17, c++20, c++23
+
+// <functional>
+
+// Test implementation-defined properties of std::not_fn<NTTP>.
+
+#include <functional>
+#include <type_traits>
+
+struct NotEmptyFunctionObject {
+ bool val = true;
+ bool operator()() const; // not defined
+};
+
+void test() {
+ using ResultWithEmptyFuncObject = decltype(std::not_fn<std::false_type{}>());
+ static_assert(std::is_empty_v<ResultWithEmptyFuncObject>);
+
+ using ResultWithNotEmptyFuncObject = decltype(std::not_fn<NotEmptyFunctionObject{}>());
+ static_assert(std::is_empty_v<ResultWithNotEmptyFuncObject>);
+}
diff --git a/libcxx/test/libcxx/utilities/function.objects/func.not.fn/not_fn.nttp.nodiscard.verify.cpp b/libcxx/test/libcxx/utilities/function.objects/func.not.fn/not_fn.nttp.nodiscard.verify.cpp
new file mode 100644
index 00000000000000..50d956e73fc09d
--- /dev/null
+++ b/libcxx/test/libcxx/utilities/function.objects/func.not.fn/not_fn.nttp.nodiscard.verify.cpp
@@ -0,0 +1,21 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++03, c++11, c++14, c++17, c++20, c++23
+
+// <functional>
+
+// Test the libc++ extension that std::not_fn<NTTP> is marked as [[nodiscard]].
+
+#include <functional>
+#include <type_traits>
+
+void test() {
+ using F = std::true_type;
+ std::not_fn<F{}>(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+}
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/functional.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/functional.version.compile.pass.cpp
index fa4d9baa283727..505b8efa72ac7d 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/functional.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/functional.version.compile.pass.cpp
@@ -28,6 +28,7 @@
__cpp_lib_invoke_r 202106L [C++23]
__cpp_lib_move_only_function 202110L [C++23]
__cpp_lib_not_fn 201603L [C++17]
+ 202306L [C++26]
__cpp_lib_ranges 202207L [C++20]
__cpp_lib_reference_wrapper 202403L [C++26]
__cpp_lib_result_of_sfinae 201210L [C++14]
@@ -537,8 +538,8 @@
# ifndef __cpp_lib_not_fn
# error "__cpp_lib_not_fn should be defined in c++26"
# endif
-# if __cpp_lib_not_fn != 201603L
-# error "__cpp_lib_not_fn should have the value 201603L in c++26"
+# if __cpp_lib_not_fn != 202306L
+# error "__cpp_lib_not_fn should have the value 202306L in c++26"
# endif
# ifndef __cpp_lib_ranges
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp
index 5055786c2d4589..26bcfee093e352 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp
@@ -149,6 +149,7 @@
__cpp_lib_node_extract 201606L [C++17]
__cpp_lib_nonmember_container_access 201411L [C++17]
__cpp_lib_not_fn 201603L [C++17]
+ 202306L [C++26]
__cpp_lib_null_iterators 201304L [C++14]
__cpp_lib_optional 201606L [C++17]
202110L [C++23]
@@ -7189,8 +7190,8 @@
# ifndef __cpp_lib_not_fn
# error "__cpp_lib_not_fn should be defined in c++26"
# endif
-# if __cpp_lib_not_fn != 201603L
-# error "__cpp_lib_not_fn should have the value 201603L in c++26"
+# if __cpp_lib_not_fn != 202306L
+# error "__cpp_lib_not_fn should have the value 202306L in c++26"
# endif
# ifndef __cpp_lib_null_iterators
diff --git a/libcxx/test/std/utilities/function.objects/func.not_fn/not_fn.nttp.pass.cpp b/libcxx/test/std/utilities/function.objects/func.not_fn/not_fn.nttp.pass.cpp
new file mode 100644
index 00000000000000..29df597704e1e8
--- /dev/null
+++ b/libcxx/test/std/utilities/function.objects/func.not_fn/not_fn.nttp.pass.cpp
@@ -0,0 +1,294 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++03, c++11, c++14, c++17, c++20, c++23
+
+// <functional>
+
+// template<auto f> constexpr unspecified not_fn() noexcept;
+
+#include <functional>
+
+#include <bit>
+#include <cassert>
+#include <concepts>
+#include <type_traits>
+#include <utility>
+
+#include "test_macros.h"
+
+class BooleanTestable {
+ bool val_;
+
+public:
+ constexpr explicit BooleanTestable(bool val) : val_(val) {}
+ constexpr operator bool() const { return val_; }
+ constexpr BooleanTestable operator!() const { return BooleanTestable{!val_}; }
+};
+
+LIBCPP_STATIC_ASSERT(std::__boolean_testable<BooleanTestable>);
+
+class FakeBool {
+ int val_;
+
+public:
+ constexpr FakeBool(int val) : val_(val) {}
+ constexpr FakeBool operator!() const { return FakeBool{-val_}; }
+ constexpr bool operator==(int other) const { return val_ == other; }
+};
+
+template <bool IsNoexcept>
+struct MaybeNoexceptFn {
+ bool operator()() const noexcept(IsNoexcept); // not defined
+};
+
+constexpr void basic_tests() {
+ { // Test constant functions
+ auto false_fn = std::not_fn<std::false_type{}>();
+ assert(false_fn());
+
+ auto true_fn = std::not_fn<std::true_type{}>();
+ assert(!true_fn());
+
+ static_assert(noexcept(std::not_fn<std::false_type{}>()));
+ static_assert(noexcept(std::not_fn<std::true_type{}>()));
+ }
+
+ { // Test function with one argument
+ auto is_odd = std::not_fn<[](auto x) { return x % 2 == 0; }>();
+ assert(is_odd(1));
+ assert(!is_odd(2));
+ assert(is_odd(3));
+ assert(!is_odd(4));
+ assert(is_odd(5));
+ }
+
+ { // Test function with multiple arguments
+ auto at_least_10 = [](auto... vals) { return (vals + ... + 0) >= 10; };
+ auto at_most_9 = std::not_fn<at_least_10>();
+ assert(at_most_9());
+ assert(at_most_9(1));
+ assert(at_most_9(1, 2, 3, 4, -1));
+ assert(at_most_9(3, 3, 2, 1, -2));
+ assert(!at_most_9(10, -1, 2));
+ assert(!at_most_9(5, 5));
+ static_assert(noexcept(std::not_fn<at_least_10>()));
+ }
+
+ { // Test function that returns boolean-testable type other than bool
+ auto is_product_even = [](auto... vals) { return BooleanTestable{(vals * ... * 1) % 2 == 0}; };
+ auto is_product_odd = std::not_fn<is_product_even>();
+ assert(is_product_odd());
+ assert(is_product_odd(1, 3, 5, 9));
+ assert(is_product_odd(3, 3, 3, 3));
+ assert(!is_product_odd(3, 5, 9, 11, 0));
+ assert(!is_product_odd(11, 7, 5, 3, 2));
+ static_assert(noexcept(std::not_fn<is_product_even>()));
+ }
+
+ { // Test function that returns non-boolean-testable type
+ auto sum = [](auto... vals) -> FakeBool { return (vals + ... + 0); };
+ auto negated_sum = std::not_fn<sum>();
+ assert(negated_sum() == 0);
+ assert(negated_sum(3) == -3);
+ assert(negated_sum(4, 5, 1, 3) == -13);
+ assert(negated_sum(4, 2, 5, 6, 1) == -18);
+ assert(negated_sum(-1, 3, 2, -8) == 4);
+ static_assert(noexcept(std::not_fn<sum>()));
+ }
+
+ { // Test member pointers
+ struct MemberPointerTester {
+ bool value = true;
+ constexpr bool not_value() const { return !value; }
+ constexpr bool value_and(bool other) noexcept { return value && other; }
+ };
+
+ MemberPointerTester tester;
+
+ auto not_mem_object = std::not_fn<&MemberPointerTester::value>();
+ assert(!not_mem_object(tester));
+ assert(!not_mem_object(std::as_const(tester)));
+ static_assert(noexcept(not_mem_object(tester)));
+ static_assert(noexcept(not_mem_object(std::as_const(tester))));
+
+ auto not_nullary_mem_fn = std::not_fn<&MemberPointerTester::not_value>();
+ assert(not_nullary_mem_fn(tester));
+ static_assert(!noexcept(not_nullary_mem_fn(tester)));
+
+ auto not_unary_mem_fn = std::not_fn<&MemberPointerTester::value_and>();
+ assert(not_unary_mem_fn(tester, false));
+ static_assert(noexcept(not_unary_mem_fn(tester, false)));
+ static_assert(!std::is_invocable_v<decltype(not_unary_mem_fn), const MemberPointerTester&, bool>);
+ }
+}
+
+constexpr void test_perfect_forwarding_call_wrapper() {
+ { // Make sure we call the correctly cv-ref qualified operator()
+ // based on the value category of the not_fn<NTTP> unspecified-type.
+ struct X {
+ constexpr FakeBool operator()() & { return 1; }
+ constexpr FakeBool operator()() const& { return 2; }
+ constexpr FakeBool operator()() && { return 3; }
+ constexpr FakeBool operator()() const&& { return 4; }
+ };
+
+ auto f = std::not_fn<X{}>();
+ using F = decltype(f);
+ assert(static_cast<F&>(f)() == -2);
+ assert(static_cast<const F&>(f)() == -2);
+ assert(static_cast<F&&>(f)() == -2);
+ assert(static_cast<const F&&>(f)() == -2);
+ }
+
+ // Call to `not_fn<NTTP>` unspecified-type's operator() should always result in call to const& overload of .
+ {
+ { // Make sure unspecified-type is still callable when we delete & overload.
+ struct X {
+ FakeBool operator()() & = delete;
+ FakeBool operator()() const&;
+ FakeBool operator()() &&;
+ FakeBool operator()() const&&;
+ };
+
+ using F = decltype(std::not_fn<X{}>());
+ static_assert(std::invocable<F&>);
+ static_assert(std::invocable<const F&>);
+ static_assert(std::invocable<F>);
+ static_assert(std::invocable<const F>);
+ }
+
+ { // Make sure unspecified-type is not callable when we delete const& overload.
+ struct X {
+ FakeBool operator()() &;
+ FakeBool operator()() const& = delete;
+ FakeBool operator()() &&;
+ FakeBool operator()() const&&;
+ };
+
+ using F = decltype(std::not_fn<X{}>());
+ static_assert(!std::invocable<F&>);
+ static_assert(!std::invocable<const F&>);
+ static_assert(!std::invocable<F>);
+ static_assert(!std::invocable<const F>);
+ }
+
+ { // Make sure unspecified-type is still callable when we delete && overload.
+ struct X {
+ FakeBool operator()() &;
+ FakeBool operator()() const&;
+ FakeBool operator()() && = delete;
+ FakeBool operator()() const&&;
+ };
+
+ using F = decltype(std::not_fn<X{}>());
+ static_assert(std::invocable<F&>);
+ static_assert(std::invocable<const F&>);
+ static_assert(std::invocable<F>);
+ static_assert(std::invocable<const F>);
+ }
+
+ { // Make sure unspecified-type is still callable when we delete const&& overload.
+ struct X {
+ FakeBool operator()() &;
+ FakeBool operator()() const&;
+ FakeBool operator()() &&;
+ FakeBool operator()() const&& = delete;
+ };
+
+ using F = decltype(std::not_fn<X{}>());
+ static_assert(std::invocable<F&>);
+ static_assert(std::invocable<const F&>);
+ static_assert(std::invocable<F>);
+ static_assert(std::invocable<const F>);
+ }
+ }
+
+ { // Test perfect forwarding
+ auto f = [](int& val) {
+ val = 5;
+ return false;
+ };
+
+ auto not_f = std::not_fn<f>();
+ int val = 0;
+ assert(not_f(val));
+ assert(val == 5);
+
+ using NotF = decltype(not_f);
+ static_assert(std::invocable<NotF, int&>);
+ static_assert(!std::invocable<NotF, int>);
+ }
+}
+
+constexpr void test_return_type() {
+ { // Test constructors and assignment operators
+ struct IsPowerOfTwo {
+ constexpr bool operator()(unsigned int x) const { return std::has_single_bit(x); }
+ };
+
+ auto is_not_power_of_2 = std::not_fn<IsPowerOfTwo{}>();
+ assert(is_not_power_of_2(5));
+ assert(!is_not_power_of_2(4));
+
+ auto moved = std::move(is_not_power_of_2);
+ assert(moved(5));
+ assert(!moved(4));
+
+ auto copied = is_not_power_of_2;
+ assert(copied(7));
+ assert(!copied(8));
+
+ moved = std::move(copied);
+ assert(copied(9));
+ assert(!copied(16));
+
+ copied = moved;
+ assert(copied(11));
+ assert(!copied(32));
+ }
+
+ { // Make sure `not_fn<NTTP>` unspecified type's operator() is SFINAE-friendly.
+ using F = decltype(std::not_fn<[](int x) { return !x; }>());
+ static_assert(!std::is_invocable<F>::value);
+ static_assert(std::is_invocable<F, int>::value);
+ static_assert(!std::is_invocable<F, void*>::value);
+ static_assert(!std::is_invocable<F, int, int>::value);
+ }
+
+ { // Test noexceptness
+ auto always_noexcept = std::not_fn<MaybeNoexceptFn<true>{}>();
+ static_assert(noexcept(always_noexcept()));
+
+ auto never_noexcept = std::not_fn<MaybeNoexceptFn<false>{}>();
+ static_assert(!noexcept(never_noexcept()));
+ }
+
+ { // Test calling volatile wrapper
+ using NotFn = decltype(std::not_fn<std::false_type{}>());
+ static_assert(!std::invocable<volatile NotFn&>);
+ static_assert(!std::invocable<const volatile NotFn&>);
+ static_assert(!std::invocable<volatile NotFn>);
+ static_assert(!std::invocable<const volatile NotFn>);
+ }
+}
+
+constexpr bool test() {
+ basic_tests();
+ test_perfect_forwarding_call_wrapper();
+ test_return_type();
+
+ return true;
+}
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+
+ return 0;
+}
diff --git a/libcxx/test/std/utilities/function.objects/func.not_fn/not_fn.nttp.verify.cpp b/libcxx/test/std/utilities/function.objects/func.not_fn/not_fn.nttp.verify.cpp
new file mode 100644
index 00000000000000..f4ebea7e8927eb
--- /dev/null
+++ b/libcxx/test/std/utilities/function.objects/func.not_fn/not_fn.nttp.verify.cpp
@@ -0,0 +1,29 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++03, c++11, c++14, c++17, c++20, c++23
+
+// <functional>
+
+// template<auto f> constexpr unspecified not_fn() noexcept;
+// Mandates: If is_pointer_v<F> || is_member_pointer_v<F> is true, then f != nullptr is true.
+
+#include <functional>
+
+struct X {};
+
+void test() {
+ auto not_fn1 = std::not_fn<static_cast<bool (*)()>(nullptr)>();
+ // expected-error@*:* {{static assertion failed due to requirement 'nullptr != nullptr': f cannot be equal to nullptr}}
+
+ auto not_fn2 = std::not_fn<static_cast<bool X::*>(nullptr)>();
+ // expected-error@*:* {{static assertion failed due to requirement 'nullptr != nullptr': f cannot be equal to nullptr}}
+
+ auto not_fn3 = std::not_fn<static_cast<bool (X::*)()>(nullptr)>();
+ // expected-error@*:* {{static assertion failed due to requirement 'nullptr != nullptr': f cannot be equal to nullptr}}
+}
diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py
index 759e49055be5c4..b14b056d1d9a08 100755
--- a/libcxx/utils/generate_feature_test_macro_components.py
+++ b/libcxx/utils/generate_feature_test_macro_components.py
@@ -868,8 +868,8 @@ def add_version_header(tc):
{
"name": "__cpp_lib_not_fn",
"values": {
- "c++17": 201603,
- # "c++26": 202306, # P2714R1 Bind front and back to NTTP callables
+ "c++17": 201603, # P0005R4 Adopt not_fn from Library Fundamentals 2 for C++17
+ "c++26": 202306, # P2714R1 Bind front and back to NTTP callables
},
"headers": ["functional"],
},
More information about the libcxx-commits
mailing list