diff mbox series

[2/3] libstdc++: Fix std::bind_front perfect forwarding [PR111327]

Message ID 20230912010852.1027184-2-ppalka@redhat.com
State New
Headers show
Series [1/3] libstdc++: Remove std::bind_front specialization for no bound args | expand

Commit Message

Patrick Palka Sept. 12, 2023, 1:08 a.m. UTC
In order to properly implement a perfect forwarding call wrapper
(before 'deducing this' at least) we need a total of 8 operator()
overloads, 4 main ones and 4 deleted ones for each const/ref qual pair,
as described in section 5.5 of P0847R6.  Otherwise the wrapper may
not perfectly forward according to the value category and constness
of the wrapped object.  This patch fixes this bug in std::bind_front.

	PR libstdc++/111327

libstdc++-v3/ChangeLog:

	* include/std/functional (_Bind_front::operator()): Add deleted
	fallback overloads for each const/ref qualifier pair.  Give the
	main overloads dummy constraints to make them more specialized
	than the deleted overloads.
	* testsuite/20_util/function_objects/bind_front/111327.cc: New test.
---
 libstdc++-v3/include/std/functional           | 16 ++++++++
 .../function_objects/bind_front/111327.cc     | 41 +++++++++++++++++++
 2 files changed, 57 insertions(+)
 create mode 100644 libstdc++-v3/testsuite/20_util/function_objects/bind_front/111327.cc

Comments

Jonathan Wakely Sept. 12, 2023, 1:16 p.m. UTC | #1
On Tue, 12 Sept 2023 at 02:09, Patrick Palka via Libstdc++
<libstdc++@gcc.gnu.org> wrote:
>
> In order to properly implement a perfect forwarding call wrapper
> (before 'deducing this' at least) we need a total of 8 operator()
> overloads, 4 main ones and 4 deleted ones for each const/ref qual pair,
> as described in section 5.5 of P0847R6.  Otherwise the wrapper may
> not perfectly forward according to the value category and constness
> of the wrapped object.  This patch fixes this bug in std::bind_front.

OK for trunk, thanks.

>
>         PR libstdc++/111327
>
> libstdc++-v3/ChangeLog:
>
>         * include/std/functional (_Bind_front::operator()): Add deleted
>         fallback overloads for each const/ref qualifier pair.  Give the
>         main overloads dummy constraints to make them more specialized
>         than the deleted overloads.
>         * testsuite/20_util/function_objects/bind_front/111327.cc: New test.
> ---
>  libstdc++-v3/include/std/functional           | 16 ++++++++
>  .../function_objects/bind_front/111327.cc     | 41 +++++++++++++++++++
>  2 files changed, 57 insertions(+)
>  create mode 100644 libstdc++-v3/testsuite/20_util/function_objects/bind_front/111327.cc
>
> diff --git a/libstdc++-v3/include/std/functional b/libstdc++-v3/include/std/functional
> index 7d1b890bb4e..c50b9e4d365 100644
> --- a/libstdc++-v3/include/std/functional
> +++ b/libstdc++-v3/include/std/functional
> @@ -938,6 +938,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>        ~_Bind_front() = default;
>
>        template<typename... _CallArgs>
> +       requires true
>         constexpr
>         invoke_result_t<_Fd&, _BoundArgs&..., _CallArgs...>
>         operator()(_CallArgs&&... __call_args) &
> @@ -948,6 +949,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>         }
>
>        template<typename... _CallArgs>
> +       requires true
>         constexpr
>         invoke_result_t<const _Fd&, const _BoundArgs&..., _CallArgs...>
>         operator()(_CallArgs&&... __call_args) const &
> @@ -959,6 +961,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>         }
>
>        template<typename... _CallArgs>
> +       requires true
>         constexpr
>         invoke_result_t<_Fd, _BoundArgs..., _CallArgs...>
>         operator()(_CallArgs&&... __call_args) &&
> @@ -969,6 +972,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>         }
>
>        template<typename... _CallArgs>
> +       requires true
>         constexpr
>         invoke_result_t<const _Fd, const _BoundArgs..., _CallArgs...>
>         operator()(_CallArgs&&... __call_args) const &&
> @@ -979,6 +983,18 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>               std::forward<_CallArgs>(__call_args)...);
>         }
>
> +      template<typename... _CallArgs>
> +       void operator()(_CallArgs&&...) & = delete;
> +
> +      template<typename... _CallArgs>
> +       void operator()(_CallArgs&&...) const & = delete;
> +
> +      template<typename... _CallArgs>
> +       void operator()(_CallArgs&&...) && = delete;
> +
> +      template<typename... _CallArgs>
> +       void operator()(_CallArgs&&...) const && = delete;
> +
>      private:
>        using _BoundIndices = index_sequence_for<_BoundArgs...>;
>
> diff --git a/libstdc++-v3/testsuite/20_util/function_objects/bind_front/111327.cc b/libstdc++-v3/testsuite/20_util/function_objects/bind_front/111327.cc
> new file mode 100644
> index 00000000000..6eb51994476
> --- /dev/null
> +++ b/libstdc++-v3/testsuite/20_util/function_objects/bind_front/111327.cc
> @@ -0,0 +1,41 @@
> +// PR libstdc++/111327 - std::bind_front doesn't perfectly forward according
> +// to value category of the call wrapper object
> +// { dg-options "-std=gnu++20" }
> +// { dg-do compile { target c++20 } }
> +
> +#include <functional>
> +#include <utility>
> +
> +struct F {
> +  void operator()(...) & = delete;
> +  void operator()(...) const &;
> +};
> +
> +struct G {
> +  void operator()(...) && = delete;
> +  void operator()(...) const &&;
> +};
> +
> +int main() {
> +  auto f0 = std::bind_front(F{});
> +  f0(); // { dg-error "deleted" }
> +  std::move(f0)();
> +  std::as_const(f0)();
> +  std::move(std::as_const(f0))();
> +
> +  auto g0 = std::bind_front(G{});
> +  g0(); // { dg-error "deleted" }
> +  std::move(g0)(); // { dg-error "deleted" }
> +  std::move(std::as_const(g0))();
> +
> +  auto f1 = std::bind_front(F{}, 42);
> +  f1(); // { dg-error "deleted" }
> +  std::move(f1)();
> +  std::as_const(f1)();
> +  std::move(std::as_const(f1))();
> +
> +  auto g1 = std::bind_front(G{}, 42);
> +  g1(); // { dg-error "deleted" }
> +  std::move(g1)(); // { dg-error "deleted" }
> +  std::move(std::as_const(g1))();
> +}
> --
> 2.42.0.158.g94e83dcf5b
>
diff mbox series

Patch

diff --git a/libstdc++-v3/include/std/functional b/libstdc++-v3/include/std/functional
index 7d1b890bb4e..c50b9e4d365 100644
--- a/libstdc++-v3/include/std/functional
+++ b/libstdc++-v3/include/std/functional
@@ -938,6 +938,7 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
       ~_Bind_front() = default;
 
       template<typename... _CallArgs>
+	requires true
 	constexpr
 	invoke_result_t<_Fd&, _BoundArgs&..., _CallArgs...>
 	operator()(_CallArgs&&... __call_args) &
@@ -948,6 +949,7 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	}
 
       template<typename... _CallArgs>
+	requires true
 	constexpr
 	invoke_result_t<const _Fd&, const _BoundArgs&..., _CallArgs...>
 	operator()(_CallArgs&&... __call_args) const &
@@ -959,6 +961,7 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	}
 
       template<typename... _CallArgs>
+	requires true
 	constexpr
 	invoke_result_t<_Fd, _BoundArgs..., _CallArgs...>
 	operator()(_CallArgs&&... __call_args) &&
@@ -969,6 +972,7 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	}
 
       template<typename... _CallArgs>
+	requires true
 	constexpr
 	invoke_result_t<const _Fd, const _BoundArgs..., _CallArgs...>
 	operator()(_CallArgs&&... __call_args) const &&
@@ -979,6 +983,18 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	      std::forward<_CallArgs>(__call_args)...);
 	}
 
+      template<typename... _CallArgs>
+	void operator()(_CallArgs&&...) & = delete;
+
+      template<typename... _CallArgs>
+	void operator()(_CallArgs&&...) const & = delete;
+
+      template<typename... _CallArgs>
+	void operator()(_CallArgs&&...) && = delete;
+
+      template<typename... _CallArgs>
+	void operator()(_CallArgs&&...) const && = delete;
+
     private:
       using _BoundIndices = index_sequence_for<_BoundArgs...>;
 
diff --git a/libstdc++-v3/testsuite/20_util/function_objects/bind_front/111327.cc b/libstdc++-v3/testsuite/20_util/function_objects/bind_front/111327.cc
new file mode 100644
index 00000000000..6eb51994476
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/function_objects/bind_front/111327.cc
@@ -0,0 +1,41 @@ 
+// PR libstdc++/111327 - std::bind_front doesn't perfectly forward according
+// to value category of the call wrapper object
+// { dg-options "-std=gnu++20" }
+// { dg-do compile { target c++20 } }
+
+#include <functional>
+#include <utility>
+
+struct F {
+  void operator()(...) & = delete;
+  void operator()(...) const &;
+};
+
+struct G {
+  void operator()(...) && = delete;
+  void operator()(...) const &&;
+};
+
+int main() {
+  auto f0 = std::bind_front(F{});
+  f0(); // { dg-error "deleted" }
+  std::move(f0)();
+  std::as_const(f0)();
+  std::move(std::as_const(f0))();
+
+  auto g0 = std::bind_front(G{});
+  g0(); // { dg-error "deleted" }
+  std::move(g0)(); // { dg-error "deleted" }
+  std::move(std::as_const(g0))();
+
+  auto f1 = std::bind_front(F{}, 42);
+  f1(); // { dg-error "deleted" }
+  std::move(f1)();
+  std::as_const(f1)();
+  std::move(std::as_const(f1))();
+
+  auto g1 = std::bind_front(G{}, 42);
+  g1(); // { dg-error "deleted" }
+  std::move(g1)(); // { dg-error "deleted" }
+  std::move(std::as_const(g1))();
+}