From 76804e89b34333e52c8000efc44217ccd4fd7fca Mon Sep 17 00:00:00 2001 From: Mark de Wever Date: Sat, 21 Jan 2023 13:38:41 +0100 Subject: [PATCH] [libc++] Addresses LWG3764. 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 | 2 +- libcxx/include/__functional/reference_wrapper.h | 8 +- libcxx/include/functional | 16 +- .../refwrap/refwrap.invoke/invoke.pass.cpp | 385 ++++++++++++++++++++- 4 files changed, 396 insertions(+), 15 deletions(-) diff --git a/libcxx/docs/Status/Cxx2bIssues.csv b/libcxx/docs/Status/Cxx2bIssues.csv index e612ffed..0dd84d7d 100644 --- a/libcxx/docs/Status/Cxx2bIssues.csv +++ b/libcxx/docs/Status/Cxx2bIssues.csv @@ -218,7 +218,7 @@ "`3760 `__","``cartesian_product_view::iterator``'s ``parent_`` is never valid", "November 2022","","","|ranges|" "`3761 `__","``cartesian_product_view::iterator::operator-`` should pass by reference", "November 2022","","","|ranges|" "`3762 `__","``generator::iterator::operator==`` should pass by reference", "November 2022","","","" -"`3764 `__","``reference_wrapper::operator()`` should propagate noexcept", "November 2022","","","" +"`3764 `__","``reference_wrapper::operator()`` should propagate noexcept", "November 2022","17.0","|Complete|","" "`3765 `__","``const_sentinel`` should be constrained", "November 2022","","","|ranges|" "`3766 `__","``view_interface::cbegin`` is underconstrained", "November 2022","","","|ranges|" "`3770 `__","``const_sentinel_t`` is missing", "November 2022","","","|ranges|" diff --git a/libcxx/include/__functional/reference_wrapper.h b/libcxx/include/__functional/reference_wrapper.h index c377b643..eed51cc 100644 --- a/libcxx/include/__functional/reference_wrapper.h +++ b/libcxx/include/__functional/reference_wrapper.h @@ -55,7 +55,13 @@ public: template _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_SINCE_CXX20 typename __invoke_of::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)...); } }; diff --git a/libcxx/include/functional b/libcxx/include/functional index 8589d3a..4dce034 100644 --- a/libcxx/include/functional +++ b/libcxx/include/functional @@ -43,20 +43,22 @@ public: // construct/copy/destroy template - reference_wrapper(U&&); - reference_wrapper(const reference_wrapper& x) noexcept; + constexpr reference_wrapper(U&&); // constexpr since C++20 + constexpr reference_wrapper(const reference_wrapper& x) noexcept; // constexpr since C++20 // assignment - reference_wrapper& operator=(const reference_wrapper& x) noexcept; + constexpr reference_wrapper& + operator=(const reference_wrapper& 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 - typename result_of::type - operator() (ArgTypes&&...) const; + constexpr typename result_of::type // constexpr since C++20 + operator() (ArgTypes&&...) const + noexcept(is_nothrow_invocable_v); // noexcept since C++17 }; template diff --git a/libcxx/test/std/utilities/function.objects/refwrap/refwrap.invoke/invoke.pass.cpp b/libcxx/test/std/utilities/function.objects/refwrap/refwrap.invoke/invoke.pass.cpp index e5f3b13..f4743e1 100644 --- a/libcxx/test/std/utilities/function.objects/refwrap/refwrap.invoke/invoke.pass.cpp +++ b/libcxx/test/std/utilities/function.objects/refwrap/refwrap.invoke/invoke.pass.cpp @@ -10,16 +10,22 @@ // reference_wrapper -// template -// requires Callable -// Callable::result_type -// operator()(ArgTypes&&... args) const; +// template +// constexpr typename result_of::type // constexpr since C++20 +// operator() (ArgTypes&&...) const +// noexcept(is_nothrow_invocable_v); // noexcept since C++17 #include #include #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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 r1(fp); + int i = 3; + assert(r1(i) == 4); + INVOKE_NOEXCEPT(true, r1(i)); + } + // functor + { + A_int_1_noexcept a0; + std::reference_wrapper 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 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 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 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 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 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 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 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 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 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 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 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 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 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; } -- 2.7.4