Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[libc++][functional] Implement not_fn<NTTP> #86133

Merged
merged 12 commits into from
Jan 10, 2025

Conversation

JMazurkiewicz
Copy link
Contributor

@JMazurkiewicz JMazurkiewicz commented Mar 21, 2024

Implement not_fn<NTTP> function from "P2714R1 Bind front and back to NTTP callables".

@JMazurkiewicz JMazurkiewicz requested a review from a team as a code owner March 21, 2024 15:28
@llvmbot llvmbot added the libc++ libc++ C++ Standard Library. Not GNU libstdc++. Not libc++abi. label Mar 21, 2024
@llvmbot
Copy link
Member

llvmbot commented Mar 21, 2024

@llvm/pr-subscribers-libcxx

Author: Jakub Mazurkiewicz (JMazurkiewicz)

Changes

Implement not_fn&lt;NTTP&gt; function from P2714R1 Bind front and back to NTTP callables.

Current implementation does not use std::__perfect_forward because not_fn&lt;NTTP&gt; does not have state entities.


Patch is 22.79 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/86133.diff

13 Files Affected:

  • (modified) libcxx/docs/FeatureTestMacroTable.rst (+2)
  • (modified) libcxx/docs/Status/Cxx2c.rst (+1)
  • (modified) libcxx/docs/Status/Cxx2cPapers.csv (+1-1)
  • (modified) libcxx/include/__functional/not_fn.h (+23)
  • (modified) libcxx/include/functional (+3-1)
  • (modified) libcxx/include/version (+4-1)
  • (added) libcxx/test/libcxx/utilities/function.objects/func.not.fn/not_fn.compile.pass.cpp (+29)
  • (added) libcxx/test/libcxx/utilities/function.objects/func.not.fn/not_fn.nttp.nodiscard.verify.cpp (+21)
  • (modified) libcxx/test/std/language.support/support.limits/support.limits.general/functional.version.compile.pass.cpp (+3-2)
  • (modified) libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp (+3-2)
  • (added) libcxx/test/std/utilities/function.objects/func.not_fn/not_fn.nttp.pass.cpp (+294)
  • (added) libcxx/test/std/utilities/function.objects/func.not_fn/not_fn.nttp.verify.cpp (+29)
  • (modified) libcxx/utils/generate_feature_test_macro_components.py (+2-2)
diff --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst
index b213f430aa5922..bdf9e57890f90c 100644
--- a/libcxx/docs/FeatureTestMacroTable.rst
+++ b/libcxx/docs/FeatureTestMacroTable.rst
@@ -428,6 +428,8 @@ Status
     --------------------------------------------------- -----------------
     ``__cpp_lib_linalg``                                *unimplemented*
     --------------------------------------------------- -----------------
+    ``__cpp_lib_not_fn``                                ``202306L``
+    --------------------------------------------------- -----------------
     ``__cpp_lib_out_ptr``                               *unimplemented*
     --------------------------------------------------- -----------------
     ``__cpp_lib_ratio``                                 ``202306L``
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 4a5443dea115c8..a085dc899852f9 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 3bd296e34aa4e3..8fda253f4051d3 100644
--- a/libcxx/include/version
+++ b/libcxx/include/version
@@ -154,7 +154,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
@@ -507,6 +508,8 @@ __cpp_lib_within_lifetime                               202306L <type_traits>
 // # define __cpp_lib_function_ref                         202306L
 // # 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_ratio                                202306L
diff --git a/libcxx/test/libcxx/utilities/function.objects/func.not.fn/not_fn.compile.pass.cpp b/libcxx/test/libcxx/utilities/function.objects/func.not.fn/not_fn.compile.pass.cpp
new file mode 100644
index 00000000000000..8390bd76f5723d
--- /dev/null
+++ b/libcxx/test/libcxx/utilities/function.objects/func.not.fn/not_fn.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 72c96c62b64c45..a1252288159a1c 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_result_of_sfinae         201210L [C++14]
     __cpp_lib_transparent_operators    201210L [C++14]
@@ -516,8 +517,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 5501587915ffa0..72192a7673a038 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
@@ -144,6 +144,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]
@@ -6968,8 +6969,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:...
[truncated]

Copy link

github-actions bot commented Mar 21, 2024

✅ With the latest revision this PR passed the Python code formatter.

@JMazurkiewicz JMazurkiewicz force-pushed the libcxx/functional/nttp_not_fn branch from 332c68c to 19ea48f Compare March 21, 2024 21:49
@JMazurkiewicz JMazurkiewicz force-pushed the libcxx/functional/nttp_not_fn branch from 19ea48f to 228c8ea Compare April 9, 2024 08:56
@JMazurkiewicz JMazurkiewicz force-pushed the libcxx/functional/nttp_not_fn branch 2 times, most recently from 99f59e4 to fc15d9f Compare May 14, 2024 14:50
@JMazurkiewicz JMazurkiewicz force-pushed the libcxx/functional/nttp_not_fn branch from fc15d9f to a350117 Compare November 14, 2024 18:36
Copy link
Member

@ldionne ldionne left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the patch and sorry for this falling under the radar. I have a few comments but this looks mostly good. The tests are really comprehensive, thanks for that!

@JMazurkiewicz
Copy link
Contributor Author

@ldionne I've pushed series of fine-grained commits to address your comments. Also, it looks like GitHub CI is failing for some unknown reason, can you restart it later?

Copy link
Member

@ldionne ldionne left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, thanks!

@ldionne
Copy link
Member

ldionne commented Dec 5, 2024

You probably want to rebase onto main and re-push to get the CI going again. Once it's green this is good to merge.

@JMazurkiewicz JMazurkiewicz force-pushed the libcxx/functional/nttp_not_fn branch from ccbd70d to e2587ff Compare December 5, 2024 20:18
@ldionne ldionne added the pending-ci Merging the PR is only pending completion of CI label Jan 10, 2025
@ldionne ldionne merged commit c91d805 into llvm:main Jan 10, 2025
66 checks passed
@JMazurkiewicz JMazurkiewicz deleted the libcxx/functional/nttp_not_fn branch January 10, 2025 23:30
BaiXilin pushed a commit to BaiXilin/llvm-fix-vnni-instr-types that referenced this pull request Jan 12, 2025
Implement `std::not_fn<NTTP>` from "P2714R1 Bind front and back to NTTP callables".
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
libc++ libc++ C++ Standard Library. Not GNU libstdc++. Not libc++abi. pending-ci Merging the PR is only pending completion of CI
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants