[libc++] Addresses LWG3764.
authorMark de Wever <koraq@xs4all.nl>
Sat, 21 Jan 2023 12:38:41 +0000 (13:38 +0100)
committerMark de Wever <koraq@xs4all.nl>
Tue, 31 Jan 2023 18:59:16 +0000 (19:59 +0100)
  LWG3764 reference_wrapper::operator() should propagate noexcept

As drive-by adds constexpr to the synopsis, since it has already been
implemented.

Reviewed By: #libc, ldionne

Differential Revision: https://reviews.llvm.org/D142814

libcxx/docs/Status/Cxx2bIssues.csv
libcxx/include/__functional/reference_wrapper.h
libcxx/include/functional
libcxx/test/std/utilities/function.objects/refwrap/refwrap.invoke/invoke.pass.cpp

index e612ffe..0dd84d7 100644 (file)
 "`3760 <https://wg21.link/LWG3760>`__","``cartesian_product_view::iterator``'s ``parent_`` is never valid", "November 2022","","","|ranges|"
 "`3761 <https://wg21.link/LWG3761>`__","``cartesian_product_view::iterator::operator-`` should pass by reference", "November 2022","","","|ranges|"
 "`3762 <https://wg21.link/LWG3762>`__","``generator::iterator::operator==`` should pass by reference", "November 2022","","",""
-"`3764 <https://wg21.link/LWG3764>`__","``reference_wrapper::operator()`` should propagate noexcept", "November 2022","","",""
+"`3764 <https://wg21.link/LWG3764>`__","``reference_wrapper::operator()`` should propagate noexcept", "November 2022","17.0","|Complete|",""
 "`3765 <https://wg21.link/LWG3765>`__","``const_sentinel`` should be constrained", "November 2022","","","|ranges|"
 "`3766 <https://wg21.link/LWG3766>`__","``view_interface::cbegin`` is underconstrained", "November 2022","","","|ranges|"
 "`3770 <https://wg21.link/LWG3770>`__","``const_sentinel_t`` is missing", "November 2022","","","|ranges|"
index c377b64..eed51cc 100644 (file)
@@ -55,7 +55,13 @@ public:
     template <class... _ArgTypes>
     _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_SINCE_CXX20
     typename __invoke_of<type&, _ArgTypes...>::type
-    operator() (_ArgTypes&&... __args) const {
+    operator() (_ArgTypes&&... __args) const
+#if _LIBCPP_STD_VER > 14
+        // Since is_nothrow_invocable requires C++17 LWG3764 is not backported
+        // to earlier versions.
+        noexcept(is_nothrow_invocable_v<_Tp&, _ArgTypes...>)
+#endif
+    {
         return std::__invoke(get(), std::forward<_ArgTypes>(__args)...);
     }
 };
index 8589d3a..4dce034 100644 (file)
@@ -43,20 +43,22 @@ public:
 
     // construct/copy/destroy
     template<class U>
-      reference_wrapper(U&&);
-    reference_wrapper(const reference_wrapper<T>& x) noexcept;
+      constexpr reference_wrapper(U&&);                                   // constexpr since C++20
+    constexpr reference_wrapper(const reference_wrapper<T>& x) noexcept;  // constexpr since C++20
 
     // assignment
-    reference_wrapper& operator=(const reference_wrapper<T>& x) noexcept;
+    constexpr reference_wrapper&
+    operator=(const reference_wrapper<T>& x) noexcept;                    // constexpr since C++20
 
     // access
-    operator T& () const noexcept;
-    T& get() const noexcept;
+    constexpr operator T& () const noexcept;                              // constexpr since C++20
+    constexpr T& get() const noexcept;                                    // constexpr since C++20
 
     // invoke
     template <class... ArgTypes>
-      typename result_of<T&(ArgTypes&&...)>::type
-          operator() (ArgTypes&&...) const;
+      constexpr typename result_of<T&(ArgTypes&&...)>::type               // constexpr since C++20
+          operator() (ArgTypes&&...) const
+              noexcept(is_nothrow_invocable_v<T&, ArgTypes...>);          // noexcept since C++17
 };
 
 template <class T>
index e5f3b13..f4743e1 100644 (file)
 
 // reference_wrapper
 
-// template <class... ArgTypes>
-//   requires Callable<T, ArgTypes&&...>
-//   Callable<T, ArgTypes&&...>::result_type
-//   operator()(ArgTypes&&... args) const;
+//  template <class... ArgTypes>
+//    constexpr typename result_of<T&(ArgTypes&&...)>::type               // constexpr since C++20
+//        operator() (ArgTypes&&...) const
+//            noexcept(is_nothrow_invocable_v<T&, ArgTypes...>);          // noexcept since C++17
 
 #include <functional>
 #include <cassert>
 
 #include "test_macros.h"
 
+#if TEST_STD_VER > 14
+#  define INVOKE_NOEXCEPT(expected, ...) static_assert(noexcept(__VA_ARGS__) == expected)
+#else
+#  define INVOKE_NOEXCEPT(expected, ...)
+#endif
+
 int count = 0;
 
 // 1 arg, return void
@@ -49,6 +55,7 @@ test_void_1()
     std::reference_wrapper<void (int)> r1(f_void_1);
     int i = 2;
     r1(i);
+    INVOKE_NOEXCEPT(false, r1(i));
     assert(count == save_count+2);
     save_count = count;
     }
@@ -58,6 +65,7 @@ test_void_1()
     std::reference_wrapper<void (*)(int)> r1(fp);
     int i = 3;
     r1(i);
+    INVOKE_NOEXCEPT(false, r1(i));
     assert(count == save_count+3);
     save_count = count;
     }
@@ -67,6 +75,7 @@ test_void_1()
     std::reference_wrapper<A_void_1> r1(a0);
     int i = 4;
     r1(i);
+    INVOKE_NOEXCEPT(false, r1(i));
     assert(count == save_count+4);
     save_count = count;
     }
@@ -76,10 +85,12 @@ test_void_1()
     std::reference_wrapper<void (A_void_1::*)()> r1(fp);
     A_void_1 a;
     r1(a);
+    INVOKE_NOEXCEPT(false, r1(a));
     assert(count == save_count+1);
     save_count = count;
     A_void_1* ap = &a;
     r1(ap);
+    INVOKE_NOEXCEPT(false, r1(ap));
     assert(count == save_count+1);
     save_count = count;
     }
@@ -89,10 +100,12 @@ test_void_1()
     std::reference_wrapper<void (A_void_1::*)() const> r1(fp);
     A_void_1 a;
     r1(a);
+    INVOKE_NOEXCEPT(false, r1(a));
     assert(count == save_count+1);
     save_count = count;
     A_void_1* ap = &a;
     r1(ap);
+    INVOKE_NOEXCEPT(false, r1(ap));
     assert(count == save_count+1);
     save_count = count;
     }
@@ -126,6 +139,7 @@ test_int_1()
     std::reference_wrapper<int (int)> r1(f_int_1);
     int i = 2;
     assert(r1(i) == 3);
+    INVOKE_NOEXCEPT(false, r1(i));
     }
     // function pointer
     {
@@ -133,6 +147,7 @@ test_int_1()
     std::reference_wrapper<int (*)(int)> r1(fp);
     int i = 3;
     assert(r1(i) == 4);
+    INVOKE_NOEXCEPT(false, r1(i));
     }
     // functor
     {
@@ -140,6 +155,7 @@ test_int_1()
     std::reference_wrapper<A_int_1> r1(a0);
     int i = 4;
     assert(r1(i) == 3);
+    INVOKE_NOEXCEPT(false, r1(i));
     }
     // member function pointer
     {
@@ -147,8 +163,10 @@ test_int_1()
     std::reference_wrapper<int (A_int_1::*)()> r1(fp);
     A_int_1 a;
     assert(r1(a) == 3);
+    INVOKE_NOEXCEPT(false, r1(a));
     A_int_1* ap = &a;
     assert(r1(ap) == 3);
+    INVOKE_NOEXCEPT(false, r1(ap));
     }
     // const member function pointer
     {
@@ -156,8 +174,10 @@ test_int_1()
     std::reference_wrapper<int (A_int_1::*)() const> r1(fp);
     A_int_1 a;
     assert(r1(a) == 4);
+    INVOKE_NOEXCEPT(false, r1(a));
     A_int_1* ap = &a;
     assert(r1(ap) == 4);
+    INVOKE_NOEXCEPT(false, r1(ap));
     }
     // member data pointer
     {
@@ -165,12 +185,14 @@ test_int_1()
     std::reference_wrapper<int A_int_1::*> r1(fp);
     A_int_1 a;
     assert(r1(a) == 5);
+    INVOKE_NOEXCEPT(true, r1(a));
     r1(a) = 6;
     assert(r1(a) == 6);
     A_int_1* ap = &a;
     assert(r1(ap) == 6);
     r1(ap) = 7;
     assert(r1(ap) == 7);
+    INVOKE_NOEXCEPT(true, r1(ap));
     }
 }
 
@@ -202,6 +224,7 @@ test_void_2()
     int i = 2;
     int j = 3;
     r1(i, j);
+    INVOKE_NOEXCEPT(false, r1(i, j));
     assert(count == save_count+5);
     save_count = count;
     }
@@ -212,6 +235,7 @@ test_void_2()
     int i = 3;
     int j = 4;
     r1(i, j);
+    INVOKE_NOEXCEPT(false, r1(i, j));
     assert(count == save_count+7);
     save_count = count;
     }
@@ -222,6 +246,7 @@ test_void_2()
     int i = 4;
     int j = 5;
     r1(i, j);
+    INVOKE_NOEXCEPT(false, r1(i, j));
     assert(count == save_count+9);
     save_count = count;
     }
@@ -236,6 +261,7 @@ test_void_2()
     save_count = count;
     A_void_2* ap = &a;
     r1(ap, i);
+    INVOKE_NOEXCEPT(false, r1(ap, i));
     assert(count == save_count+3);
     save_count = count;
     }
@@ -246,10 +272,12 @@ test_void_2()
     A_void_2 a;
     int i = 4;
     r1(a, i);
+    INVOKE_NOEXCEPT(false, r1(a, i));
     assert(count == save_count+4);
     save_count = count;
     A_void_2* ap = &a;
     r1(ap, i);
+    INVOKE_NOEXCEPT(false, r1(ap, i));
     assert(count == save_count+4);
     save_count = count;
     }
@@ -274,7 +302,7 @@ struct A_int_2
 };
 
 void
-testint_2()
+test_int_2()
 {
     // function
     {
@@ -282,6 +310,7 @@ testint_2()
     int i = 2;
     int j = 3;
     assert(r1(i, j) == i+j);
+    INVOKE_NOEXCEPT(false, r1(i, j));
     }
     // function pointer
     {
@@ -290,6 +319,7 @@ testint_2()
     int i = 3;
     int j = 4;
     assert(r1(i, j) == i+j);
+    INVOKE_NOEXCEPT(false, r1(i, j));
     }
     // functor
     {
@@ -298,6 +328,7 @@ testint_2()
     int i = 4;
     int j = 5;
     assert(r1(i, j) == i+j);
+    INVOKE_NOEXCEPT(false, r1(i, j));
     }
     // member function pointer
     {
@@ -306,8 +337,10 @@ testint_2()
     A_int_2 a;
     int i = 3;
     assert(r1(a, i) == i+1);
+    INVOKE_NOEXCEPT(false, r1(a, i));
     A_int_2* ap = &a;
     assert(r1(ap, i) == i+1);
+    INVOKE_NOEXCEPT(false, r1(ap, i));
     }
     // const member function pointer
     {
@@ -316,17 +349,357 @@ testint_2()
     A_int_2 a;
     int i = 4;
     assert(r1(a, i) == i+2);
+    INVOKE_NOEXCEPT(false, r1(a, i));
     A_int_2* ap = &a;
     assert(r1(ap, i) == i+2);
+    INVOKE_NOEXCEPT(false, r1(ap, i));
+    }
+}
+
+#if TEST_STD_VER >= 11
+
+// 1 arg, return void, noexcept
+
+void f_void_1_noexcept(int i) noexcept
+{
+    count += i;
+}
+
+struct A_void_1_noexcept
+{
+    void operator()(int i) noexcept
+    {
+        count += i;
+    }
+
+    void mem1() noexcept {++count;}
+    void mem2() const noexcept {++count;}
+};
+
+void
+test_void_1_noexcept()
+{
+    int save_count = count;
+    // function
+    {
+    std::reference_wrapper<void (int) noexcept> r1(f_void_1_noexcept);
+    int i = 2;
+    r1(i);
+    INVOKE_NOEXCEPT(true, r1(i));
+    assert(count == save_count+2);
+    save_count = count;
+    }
+    // function pointer
+    {
+    void (*fp)(int) noexcept = f_void_1_noexcept;
+    std::reference_wrapper<void (*)(int) noexcept> r1(fp);
+    int i = 3;
+    r1(i);
+    INVOKE_NOEXCEPT(true, r1(i));
+    assert(count == save_count+3);
+    save_count = count;
+    }
+    // functor
+    {
+    A_void_1_noexcept a0;
+    std::reference_wrapper<A_void_1_noexcept> r1(a0);
+    int i = 4;
+    r1(i);
+    INVOKE_NOEXCEPT(true, r1(i));
+    assert(count == save_count+4);
+    save_count = count;
+    }
+    // member function pointer
+    {
+    void (A_void_1_noexcept::*fp)() noexcept = &A_void_1_noexcept::mem1;
+    std::reference_wrapper<void (A_void_1_noexcept::*)() noexcept> r1(fp);
+    A_void_1_noexcept a;
+    r1(a);
+    INVOKE_NOEXCEPT(true, r1(a));
+    assert(count == save_count+1);
+    save_count = count;
+    A_void_1_noexcept* ap = &a;
+    r1(ap);
+    INVOKE_NOEXCEPT(true, r1(ap));
+    assert(count == save_count+1);
+    save_count = count;
+    }
+    // const member function pointer
+    {
+    void (A_void_1_noexcept::*fp)() const noexcept = &A_void_1_noexcept::mem2;
+    std::reference_wrapper<void (A_void_1_noexcept::*)() const noexcept> r1(fp);
+    A_void_1_noexcept a;
+    r1(a);
+    INVOKE_NOEXCEPT(true, r1(a));
+    assert(count == save_count+1);
+    save_count = count;
+    A_void_1_noexcept* ap = &a;
+    r1(ap);
+    INVOKE_NOEXCEPT(true, r1(ap));
+    assert(count == save_count+1);
+    save_count = count;
+    }
+}
+
+// 1 arg, return int, noexcept
+
+int f_int_1_noexcept(int i) noexcept
+{
+    return i + 1;
+}
+
+struct A_int_1_noexcept
+{
+    A_int_1_noexcept() : data_(5) {}
+    int operator()(int i) noexcept
+    {
+        return i - 1;
+    }
+
+    int mem1() noexcept {return 3;}
+    int mem2() const noexcept {return 4;}
+    int data_;
+};
+
+void
+test_int_1_noexcept()
+{
+    // function
+    {
+    std::reference_wrapper<int (int) noexcept> r1(f_int_1_noexcept);
+    int i = 2;
+    assert(r1(i) == 3);
+    INVOKE_NOEXCEPT(true, r1(i));
+    }
+    // function pointer
+    {
+    int (*fp)(int) noexcept = f_int_1_noexcept;
+    std::reference_wrapper<int (*)(int) noexcept> r1(fp);
+    int i = 3;
+    assert(r1(i) == 4);
+    INVOKE_NOEXCEPT(true, r1(i));
+    }
+    // functor
+    {
+    A_int_1_noexcept a0;
+    std::reference_wrapper<A_int_1_noexcept> r1(a0);
+    int i = 4;
+    assert(r1(i) == 3);
+    INVOKE_NOEXCEPT(true, r1(i));
+    }
+    // member function pointer
+    {
+    int (A_int_1_noexcept::*fp)() noexcept = &A_int_1_noexcept::mem1;
+    std::reference_wrapper<int (A_int_1_noexcept::*)() noexcept> r1(fp);
+    A_int_1_noexcept a;
+    assert(r1(a) == 3);
+    INVOKE_NOEXCEPT(true, r1(a));
+    A_int_1_noexcept* ap = &a;
+    assert(r1(ap) == 3);
+    INVOKE_NOEXCEPT(true, r1(ap));
+    }
+    // const member function pointer
+    {
+    int (A_int_1_noexcept::*fp)() const noexcept = &A_int_1_noexcept::mem2;
+    std::reference_wrapper<int (A_int_1_noexcept::*)() const noexcept> r1(fp);
+    A_int_1_noexcept a;
+    assert(r1(a) == 4);
+    INVOKE_NOEXCEPT(true, r1(a));
+    A_int_1_noexcept* ap = &a;
+    assert(r1(ap) == 4);
+    INVOKE_NOEXCEPT(true, r1(ap));
+    }
+    // member data pointer
+    {
+    int A_int_1_noexcept::*fp = &A_int_1_noexcept::data_;
+    std::reference_wrapper<int A_int_1_noexcept::*> r1(fp);
+    A_int_1_noexcept a;
+    assert(r1(a) == 5);
+    INVOKE_NOEXCEPT(true, r1(a));
+    r1(a) = 6;
+    assert(r1(a) == 6);
+    A_int_1_noexcept* ap = &a;
+    assert(r1(ap) == 6);
+    r1(ap) = 7;
+    assert(r1(ap) == 7);
+    INVOKE_NOEXCEPT(true, r1(ap));
     }
 }
 
+// 2 arg, return void, noexcept
+
+void f_void_2_noexcept(int i, int j) noexcept
+{
+    count += i+j;
+}
+
+struct A_void_2_noexcept
+{
+    void operator()(int i, int j) noexcept
+    {
+        count += i+j;
+    }
+
+    void mem1(int i) noexcept {count += i;}
+    void mem2(int i) const noexcept {count += i;}
+};
+
+void
+test_void_2_noexcept()
+{
+    int save_count = count;
+    // function
+    {
+    std::reference_wrapper<void (int, int) noexcept> r1(f_void_2_noexcept);
+    int i = 2;
+    int j = 3;
+    r1(i, j);
+    INVOKE_NOEXCEPT(true, r1(i, j));
+    assert(count == save_count+5);
+    save_count = count;
+    }
+    // function pointer
+    {
+    void (*fp)(int, int) noexcept = f_void_2_noexcept;
+    std::reference_wrapper<void (*)(int, int) noexcept> r1(fp);
+    int i = 3;
+    int j = 4;
+    r1(i, j);
+    INVOKE_NOEXCEPT(true, r1(i, j));
+    assert(count == save_count+7);
+    save_count = count;
+    }
+    // functor
+    {
+    A_void_2_noexcept a0;
+    std::reference_wrapper<A_void_2_noexcept> r1(a0);
+    int i = 4;
+    int j = 5;
+    r1(i, j);
+    INVOKE_NOEXCEPT(true, r1(i, j));
+    assert(count == save_count+9);
+    save_count = count;
+    }
+    // member function pointer
+    {
+    void (A_void_2_noexcept::*fp)(int) noexcept = &A_void_2_noexcept::mem1;
+    std::reference_wrapper<void (A_void_2_noexcept::*)(int) noexcept> r1(fp);
+    A_void_2_noexcept a;
+    int i = 3;
+    r1(a, i);
+    assert(count == save_count+3);
+    save_count = count;
+    A_void_2_noexcept* ap = &a;
+    r1(ap, i);
+    INVOKE_NOEXCEPT(true, r1(ap, i));
+    assert(count == save_count+3);
+    save_count = count;
+    }
+    // const member function pointer
+    {
+    void (A_void_2_noexcept::*fp)(int) const noexcept = &A_void_2_noexcept::mem2;
+    std::reference_wrapper<void (A_void_2_noexcept::*)(int) const noexcept> r1(fp);
+    A_void_2_noexcept a;
+    int i = 4;
+    r1(a, i);
+    INVOKE_NOEXCEPT(true, r1(a, i));
+    assert(count == save_count+4);
+    save_count = count;
+    A_void_2_noexcept* ap = &a;
+    r1(ap, i);
+    INVOKE_NOEXCEPT(true, r1(ap, i));
+    assert(count == save_count+4);
+    save_count = count;
+    }
+}
+
+// 2 arg, return int, noexcept
+
+int f_int_2_noexcept(int i, int j) noexcept
+{
+    return i+j;
+}
+
+struct A_int_2_noexcept
+{
+    int operator()(int i, int j) noexcept
+    {
+        return i+j;
+    }
+
+    int mem1(int i) noexcept {return i+1;}
+    int mem2(int i) const noexcept {return i+2;}
+};
+
+void
+test_int_2_noexcept()
+{
+    // function
+    {
+    std::reference_wrapper<int (int, int) noexcept> r1(f_int_2_noexcept);
+    int i = 2;
+    int j = 3;
+    assert(r1(i, j) == i+j);
+    INVOKE_NOEXCEPT(true, r1(i, j));
+    }
+    // function pointer
+    {
+    int (*fp)(int, int) noexcept = f_int_2_noexcept;
+    std::reference_wrapper<int (*)(int, int) noexcept> r1(fp);
+    int i = 3;
+    int j = 4;
+    assert(r1(i, j) == i+j);
+    INVOKE_NOEXCEPT(true, r1(i, j));
+    }
+    // functor
+    {
+    A_int_2_noexcept a0;
+    std::reference_wrapper<A_int_2_noexcept> r1(a0);
+    int i = 4;
+    int j = 5;
+    assert(r1(i, j) == i+j);
+    INVOKE_NOEXCEPT(true, r1(i, j));
+    }
+    // member function pointer
+    {
+    int(A_int_2_noexcept::*fp)(int) noexcept = &A_int_2_noexcept::mem1;
+    std::reference_wrapper<int (A_int_2_noexcept::*)(int) noexcept> r1(fp);
+    A_int_2_noexcept a;
+    int i = 3;
+    assert(r1(a, i) == i+1);
+    INVOKE_NOEXCEPT(true, r1(a, i));
+    A_int_2_noexcept* ap = &a;
+    assert(r1(ap, i) == i+1);
+    INVOKE_NOEXCEPT(true, r1(ap, i));
+    }
+    // const member function pointer
+    {
+    int (A_int_2_noexcept::*fp)(int) const noexcept = &A_int_2_noexcept::mem2;
+    std::reference_wrapper<int (A_int_2_noexcept::*)(int) const noexcept> r1(fp);
+    A_int_2_noexcept a;
+    int i = 4;
+    assert(r1(a, i) == i+2);
+    INVOKE_NOEXCEPT(true, r1(a, i));
+    A_int_2_noexcept* ap = &a;
+    assert(r1(ap, i) == i+2);
+    INVOKE_NOEXCEPT(true, r1(ap, i));
+    }
+}
+
+#endif // TEST_STD_VER >= 11
+
 int main(int, char**)
 {
     test_void_1();
     test_int_1();
     test_void_2();
-    testint_2();
+    test_int_2();
+#if TEST_STD_VER >= 11
+    test_void_1_noexcept();
+    test_int_1_noexcept();
+    test_void_2_noexcept();
+    test_int_2_noexcept();
+#endif // TEST_STD_VER >= 11
 
   return 0;
 }