aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPatrick Palka <ppalka@redhat.com>2024-07-25 09:02:13 -0400
committerPatrick Palka <ppalka@redhat.com>2024-07-25 09:02:13 -0400
commit1066a95aa33eee2d2bd9c8324f34dedb967f338c (patch)
tree2fc22ae52a110142a06e9a0748bb2d0c8e974d5a
parentd6849aa926665cbee8bf87822401ca44f881753f (diff)
downloadgcc-1066a95aa33eee2d2bd9c8324f34dedb967f338c.zip
gcc-1066a95aa33eee2d2bd9c8324f34dedb967f338c.tar.gz
gcc-1066a95aa33eee2d2bd9c8324f34dedb967f338c.tar.bz2
libstdc++: fix uses of explicit object parameter [PR116038]
The type of an implicit object parameter is always the current class. For an explicit object parameter however, its deduced type can be a derived class of the current class. So when combining multiple implicit-object overloads into a single explicit-object overload we need to account for this possibility. For example when accessing a member of the current class through an explicit object parameter, it may now be a derived class from which the member is not accessible, as in the below testcases. This pitfall is discussed[1] in the deducing this paper. The general solution is to cast the explicit object parameter to (a reference to) the current class rather than e.g. using std::forward which preserves the deduced type. This patch corrects the existing problematic uses of explicit object parameters in the library, all of which forward the parameter via std::forward, to instead cast the parameter to the current class via our __like_t alias template. Note that unlike the paper's like_t, ours always returns a reference so we can just write __like_t<Self, B>(self) instead of (_like_t<Self, B>&&)self as the paper does. [1]: https://wg21.link/P0847#name-lookup-within-member-functions (and the section after that) PR libstdc++/116038 libstdc++-v3/ChangeLog: * include/std/functional (_Bind_front::operator()): Use __like_t instead of std::forward when forwarding __self. (_Bind_back::operator()): Likewise. * include/std/ranges (_Partial::operator()): Likewise. (_Pipe::operator()): Likewise. * testsuite/20_util/function_objects/bind_back/116038.cc: New test. * testsuite/20_util/function_objects/bind_front/116038.cc: New test. * testsuite/std/ranges/adaptors/116038.cc: New test. Reviewed-by: Jonathan Wakely <jwakely@redhat.com>
-rw-r--r--libstdc++-v3/include/std/functional4
-rw-r--r--libstdc++-v3/include/std/ranges11
-rw-r--r--libstdc++-v3/testsuite/20_util/function_objects/bind_back/116038.cc27
-rw-r--r--libstdc++-v3/testsuite/20_util/function_objects/bind_front/116038.cc27
-rw-r--r--libstdc++-v3/testsuite/std/ranges/adaptors/116038.cc29
5 files changed, 92 insertions, 6 deletions
diff --git a/libstdc++-v3/include/std/functional b/libstdc++-v3/include/std/functional
index 9936428..7788a96 100644
--- a/libstdc++-v3/include/std/functional
+++ b/libstdc++-v3/include/std/functional
@@ -944,7 +944,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
noexcept(is_nothrow_invocable_v<__like_t<_Self, _Fd>,
__like_t<_Self, _BoundArgs>..., _CallArgs...>)
{
- return _S_call(std::forward<_Self>(__self), _BoundIndices(),
+ return _S_call(__like_t<_Self, _Bind_front>(__self), _BoundIndices(),
std::forward<_CallArgs>(__call_args)...);
}
#else
@@ -1072,7 +1072,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
noexcept(is_nothrow_invocable_v<__like_t<_Self, _Fd>,
_CallArgs..., __like_t<_Self, _BoundArgs>...>)
{
- return _S_call(std::forward<_Self>(__self), _BoundIndices(),
+ return _S_call(__like_t<_Self, _Bind_back>(__self), _BoundIndices(),
std::forward<_CallArgs>(__call_args)...);
}
diff --git a/libstdc++-v3/include/std/ranges b/libstdc++-v3/include/std/ranges
index 3f335b9..b7c7aa3 100644
--- a/libstdc++-v3/include/std/ranges
+++ b/libstdc++-v3/include/std/ranges
@@ -1033,7 +1033,7 @@ namespace views::__adaptor
return _Adaptor{}(std::forward<_Range>(__r),
std::forward<decltype(__args)>(__args)...);
};
- return std::apply(__forwarder, std::forward<_Self>(__self)._M_args);
+ return std::apply(__forwarder, __like_t<_Self, _Partial>(__self)._M_args);
}
#else
template<typename _Range>
@@ -1082,7 +1082,10 @@ namespace views::__adaptor
requires __adaptor_invocable<_Adaptor, _Range, __like_t<_Self, _Arg>>
constexpr auto
operator()(this _Self&& __self, _Range&& __r)
- { return _Adaptor{}(std::forward<_Range>(__r), std::forward<_Self>(__self)._M_arg); }
+ {
+ return _Adaptor{}(std::forward<_Range>(__r),
+ __like_t<_Self, _Partial>(__self)._M_arg);
+ }
#else
template<typename _Range>
requires __adaptor_invocable<_Adaptor, _Range, const _Arg&>
@@ -1185,8 +1188,8 @@ namespace views::__adaptor
constexpr auto
operator()(this _Self&& __self, _Range&& __r)
{
- return (std::forward<_Self>(__self)._M_rhs
- (std::forward<_Self>(__self)._M_lhs
+ return (__like_t<_Self, _Pipe>(__self)._M_rhs
+ (__like_t<_Self, _Pipe>(__self)._M_lhs
(std::forward<_Range>(__r))));
}
#else
diff --git a/libstdc++-v3/testsuite/20_util/function_objects/bind_back/116038.cc b/libstdc++-v3/testsuite/20_util/function_objects/bind_back/116038.cc
new file mode 100644
index 0000000..ed392b1
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/function_objects/bind_back/116038.cc
@@ -0,0 +1,27 @@
+// PR libstdc++/116038
+// { dg-do compile { target c++23 } }
+
+#include <functional>
+#include <utility>
+
+struct A { };
+struct B { };
+
+template<class... Ts>
+struct overloaded : private Ts... {
+ overloaded(Ts...);
+ using Ts::operator()...;
+};
+
+int apply_a(A, int);
+int apply_b(B, int);
+
+int main() {
+ overloaded o = { std::bind_back(apply_a, 1),
+ std::bind_back(apply_b, 2) };
+ A a;
+ o(a);
+ std::as_const(o)(a);
+ std::move(o)(a);
+ std::move(std::as_const(o))(a);
+}
diff --git a/libstdc++-v3/testsuite/20_util/function_objects/bind_front/116038.cc b/libstdc++-v3/testsuite/20_util/function_objects/bind_front/116038.cc
new file mode 100644
index 0000000..3bf1226
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/function_objects/bind_front/116038.cc
@@ -0,0 +1,27 @@
+// PR libstdc++/116038
+// { dg-do compile { target c++20 } }
+
+#include <functional>
+#include <utility>
+
+struct A { };
+struct B { };
+
+template<class... Ts>
+struct overloaded : private Ts... {
+ overloaded(Ts...);
+ using Ts::operator()...;
+};
+
+int apply_a(int, A);
+int apply_b(int, B);
+
+int main() {
+ overloaded o = { std::bind_front(apply_a, 1),
+ std::bind_front(apply_b, 2) };
+ A a;
+ o(a);
+ std::as_const(o)(a);
+ std::move(o)(a);
+ std::move(std::as_const(o))(a);
+}
diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/116038.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/116038.cc
new file mode 100644
index 0000000..1afdf3d
--- /dev/null
+++ b/libstdc++-v3/testsuite/std/ranges/adaptors/116038.cc
@@ -0,0 +1,29 @@
+// PR libstdc++/116038
+// { dg-do compile { target c++20 } }
+
+#include <ranges>
+#include <utility>
+
+struct A { };
+struct B { };
+
+template<class... Ts>
+struct overloaded : private Ts... {
+ overloaded(Ts...);
+ using Ts::operator()...;
+};
+
+int x[5];
+struct integralish { operator int() const; } i;
+
+int main() {
+ overloaded o1 = { std::views::drop(i) };
+ o1(x);
+ std::move(o1)(x);
+ std::as_const(o1)(x);
+
+ overloaded o2 = { std::views::drop(i) | std::views::take(i) };
+ o2(x);
+ std::move(o2)(x);
+ std::as_const(o1)(x);
+}