This patch makes the function_ref non-dangling for the statless
wrappers:
* any functor for which operator() selected for arguments
is static,
* standard functors, including pre-C++20 ones
We adjust the function_ref generic constructor from any functor, to adjust
using more specializated invoker:
* _S_static (newly added) if the called operator() overload is static,
after changes r16-5624-g0ea9d760fbf44c, includes all post-c++20 functors
* _S_nttp<_Fd{}> for pre-C++20 standard functors
In both above cases the value of _M_ptrs is ignored and simply set to nullptr.
This follows same technique (checking _Fd::operator()(args...)), and support
the same set of types, as for one used for the transform views iterators in
r16-5625-g9ed821d107f7a1.
As after this change we provide well-defined behavior for the code, that
previous was undefined, this changes is pure quality-of-implementation.
As illustrated by the test cases, it has observable side effects, where
non-longer dangling construct can be used to initialize constexpr
function_ref. However, the standared does not define when the constructors
defined constexpr are actually usable at compile time, and the already have
precendence in form of SSO string for such constructs being implementation
specific.
libstdc++-v3/ChangeLog:
* include/bits/funcref_impl.h (function_ref::function_ref(_Fn&&)):
Use _S_static and _S_nttp invokers.
* include/bits/funcwrap.h (_Base_invoker::_S_static):
Define.
* include/bits/stl_function.h (std::__is_std_op_template)
(std::__is_std_op_wrapper) [__cplusplus > 201703L]:
Moved from std/ranges.
* include/std/ranges (__detail::__is_std_op_template):
(__detail::__is_std_op_wrapper): Moved to bits/stl_function.h.
* testsuite/20_util/function_ref/dangling.cc: New test.
* testsuite/20_util/function_ref/dangling_neg.cc: New test.
---
All the function_wrapper/constant_wrapper discussion made me realize,
that we do not actually need to special case any specific wrapper for
_Fn&& constructor (binding is different), and can support any that
define is operator() as static (so we know this is trully stateless).
I believe we are allowed to make this change anyway, as it only defines
behavior for UB, and we do not nail down when constexpr constructor
are usuable at compile time.
Tested on x86_64-linux locally. Or 20_util/*function* test passed
with all standard modes and with/without -m32. OK for trunk, as
this is C++26 feature?
libstdc++-v3/include/bits/funcref_impl.h | 20 ++++-
libstdc++-v3/include/bits/funcwrap.h | 8 ++
libstdc++-v3/include/bits/stl_function.h | 50 ++++++++++++
libstdc++-v3/include/std/ranges | 50 ------------
.../20_util/function_ref/dangling.cc | 74 ++++++++++++++++++
.../20_util/function_ref/dangling_neg.cc | 76 +++++++++++++++++++
6 files changed, 226 insertions(+), 52 deletions(-)
create mode 100644 libstdc++-v3/testsuite/20_util/function_ref/dangling.cc
create mode 100644 libstdc++-v3/testsuite/20_util/function_ref/dangling_neg.cc
diff --git a/libstdc++-v3/include/bits/funcref_impl.h
b/libstdc++-v3/include/bits/funcref_impl.h
index 3fe2fb1b3f5..3d55c8406b0 100644
--- a/libstdc++-v3/include/bits/funcref_impl.h
+++ b/libstdc++-v3/include/bits/funcref_impl.h
@@ -106,8 +106,24 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
constexpr
function_ref(_Fn&& __f) noexcept
{
- _M_invoke = _Invoker::template _S_ptrs<_Vt _GLIBCXX_MOF_CV&>();
- _M_init(std::addressof(__f));
+ using _Fd = remove_cv_t<_Vt>;
+ if constexpr (__is_std_op_wrapper<_Fd>)
+ {
+ _M_invoke = _Invoker::template _S_nttp<_Fd{}>;
+ _M_ptrs._M_obj = nullptr;
+ }
+ else if constexpr (requires (_ArgTypes&&... __args) {
+ _Fd::operator()(std::forward<_ArgTypes>(__args)...);
+ })
+ {
+ _M_invoke = _Invoker::template _S_static<_Fd>;
+ _M_ptrs._M_obj = nullptr;
+ }
+ else
+ {
+ _M_invoke = _Invoker::template _S_ptrs<_Vt _GLIBCXX_MOF_CV&>();
+ _M_init(std::addressof(__f));
+ }
}
// _GLIBCXX_RESOLVE_LIB_DEFECTS
diff --git a/libstdc++-v3/include/bits/funcwrap.h
b/libstdc++-v3/include/bits/funcwrap.h
index b8dd6fb7aea..6441893d213 100644
--- a/libstdc++-v3/include/bits/funcwrap.h
+++ b/libstdc++-v3/include/bits/funcwrap.h
@@ -41,6 +41,9 @@
#include <bits/invoke.h>
#include <bits/utility.h>
+#if __glibcxx_function_ref
+#include <bits/stl_function.h>
+#endif
namespace std _GLIBCXX_VISIBILITY(default)
{
@@ -139,6 +142,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
{ return &_S_call_ptrs<_Adjust_target<_Tp>>; }
#ifdef __glibcxx_function_ref // C++ >= 26
+ template<typename _Fn>
+ static _Ret
+ _S_static(_Ptrs, _Args... __args) noexcept(_Noex)
+ { return _Fn::operator()(std::forward<_Args>(__args)...); }
+
template<auto __fn>
static _Ret
_S_nttp(_Ptrs, _Args... __args) noexcept(_Noex)
diff --git a/libstdc++-v3/include/bits/stl_function.h
b/libstdc++-v3/include/bits/stl_function.h
index 0600de72b10..6609fe3199b 100644
--- a/libstdc++-v3/include/bits/stl_function.h
+++ b/libstdc++-v3/include/bits/stl_function.h
@@ -1525,6 +1525,56 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
#endif
#endif
+#if __cplusplus > 201703L
+ template<template<typename> class>
+ constexpr bool __is_std_op_template = false;
+
+ template<>
+ inline constexpr bool __is_std_op_template<std::equal_to> = true;
+ template<>
+ inline constexpr bool __is_std_op_template<std::not_equal_to> = true;
+ template<>
+ inline constexpr bool __is_std_op_template<std::greater> = true;
+ template<>
+ inline constexpr bool __is_std_op_template<std::less> = true;
+ template<>
+ inline constexpr bool __is_std_op_template<std::greater_equal> = true;
+ template<>
+ inline constexpr bool __is_std_op_template<std::less_equal> = true;
+ template<>
+ inline constexpr bool __is_std_op_template<std::plus> = true;
+ template<>
+ inline constexpr bool __is_std_op_template<std::minus> = true;
+ template<>
+ inline constexpr bool __is_std_op_template<std::multiplies> = true;
+ template<>
+ inline constexpr bool __is_std_op_template<std::divides> = true;
+ template<>
+ inline constexpr bool __is_std_op_template<std::modulus> = true;
+ template<>
+ inline constexpr bool __is_std_op_template<std::negate> = true;
+ template<>
+ inline constexpr bool __is_std_op_template<std::logical_and> = true;
+ template<>
+ inline constexpr bool __is_std_op_template<std::logical_or> = true;
+ template<>
+ inline constexpr bool __is_std_op_template<std::logical_not> = true;
+ template<>
+ inline constexpr bool __is_std_op_template<std::bit_and> = true;
+ template<>
+ inline constexpr bool __is_std_op_template<std::bit_or> = true;
+ template<>
+ inline constexpr bool __is_std_op_template<std::bit_xor> = true;
+ template<>
+ inline constexpr bool __is_std_op_template<std::bit_not> = true;
+
+ template<typename _Fn>
+ constexpr bool __is_std_op_wrapper = false;
+
+ template<template<typename> class _Ft, typename _Tp>
+ constexpr bool __is_std_op_wrapper<_Ft<_Tp>>
+ = __is_std_op_template<_Ft>;
+#endif
_GLIBCXX_END_NAMESPACE_VERSION
} // namespace
diff --git a/libstdc++-v3/include/std/ranges b/libstdc++-v3/include/std/ranges
index 430f4108204..8365bed17a6 100644
--- a/libstdc++-v3/include/std/ranges
+++ b/libstdc++-v3/include/std/ranges
@@ -287,56 +287,6 @@ namespace ranges
{ return std::__addressof(_M_value); }
};
- template<template<typename> class>
- constexpr bool __is_std_op_template = false;
-
- template<>
- inline constexpr bool __is_std_op_template<std::equal_to> = true;
- template<>
- inline constexpr bool __is_std_op_template<std::not_equal_to> = true;
- template<>
- inline constexpr bool __is_std_op_template<std::greater> = true;
- template<>
- inline constexpr bool __is_std_op_template<std::less> = true;
- template<>
- inline constexpr bool __is_std_op_template<std::greater_equal> = true;
- template<>
- inline constexpr bool __is_std_op_template<std::less_equal> = true;
- template<>
- inline constexpr bool __is_std_op_template<std::plus> = true;
- template<>
- inline constexpr bool __is_std_op_template<std::minus> = true;
- template<>
- inline constexpr bool __is_std_op_template<std::multiplies> = true;
- template<>
- inline constexpr bool __is_std_op_template<std::divides> = true;
- template<>
- inline constexpr bool __is_std_op_template<std::modulus> = true;
- template<>
- inline constexpr bool __is_std_op_template<std::negate> = true;
- template<>
- inline constexpr bool __is_std_op_template<std::logical_and> = true;
- template<>
- inline constexpr bool __is_std_op_template<std::logical_or> = true;
- template<>
- inline constexpr bool __is_std_op_template<std::logical_not> = true;
- template<>
- inline constexpr bool __is_std_op_template<std::bit_and> = true;
- template<>
- inline constexpr bool __is_std_op_template<std::bit_or> = true;
- template<>
- inline constexpr bool __is_std_op_template<std::bit_xor> = true;
- template<>
- inline constexpr bool __is_std_op_template<std::bit_not> = true;
-
-
- template<typename _Fn>
- constexpr bool __is_std_op_wrapper = false;
-
- template<template<typename> class _Ft, typename _Tp>
- constexpr bool __is_std_op_wrapper<_Ft<_Tp>>
- = __is_std_op_template<_Ft>;
-
namespace __func_handle
{
template<typename _Fn>
diff --git a/libstdc++-v3/testsuite/20_util/function_ref/dangling.cc
b/libstdc++-v3/testsuite/20_util/function_ref/dangling.cc
new file mode 100644
index 00000000000..3cc782524f6
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/function_ref/dangling.cc
@@ -0,0 +1,74 @@
+// { dg-do compile { target c++26 } }
+
+#include <functional>
+
+template<typename F>
+constexpr std::function_ref<int(int, int) const>
+create(F f)
+{
+ std::function_ref<int(int, int) const> fr(f);
+ return fr;
+}
+
+constexpr auto vLambda = create([](int x, int y) static { return x + y; });
+
+constexpr auto vTgreater = create(std::greater<int>{});
+constexpr auto vDgreater = create(std::greater<>{});
+constexpr auto vRgreater = create(std::ranges::greater{});
+
+constexpr auto vTplus = create(std::plus<int>{});
+constexpr auto vDplus = create(std::plus<>{});
+
+struct Empty
+{
+ static int
+ operator()(int x, int y)
+ { return x + y; }
+};
+
+constexpr auto vEmpty = create(Empty{});
+
+struct NonEmpty {
+ int v;
+
+ static int
+ operator()(int x, int y)
+ { return x + y; }
+};
+
+constexpr auto vNonEmpty = create(NonEmpty{3});
+
+struct NonStatic {
+ int v;
+
+ int
+ operator()(int x, int y) const
+ { return x + y + v; }
+};
+
+constexpr auto vNonType = create(std::nontype<NonStatic{3}>);
+
+struct StaticWins {
+ static int
+ operator()(int x, int y)
+ { return x + y; }
+
+ int
+ operator()(float x, float y) const
+ { return x + y; }
+};
+
+constexpr auto vStaticWins = create(StaticWins{});
+
+struct StaticWinsET {
+ static int
+ operator()(int x, int y)
+ { return x + y; }
+
+ int
+ operator()(this StaticWinsET, float x, float y)
+ { return x + y; }
+};
+
+constexpr auto vStaticWinsET = create(StaticWinsET{});
+
diff --git a/libstdc++-v3/testsuite/20_util/function_ref/dangling_neg.cc
b/libstdc++-v3/testsuite/20_util/function_ref/dangling_neg.cc
new file mode 100644
index 00000000000..16033014703
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/function_ref/dangling_neg.cc
@@ -0,0 +1,76 @@
+// { dg-do compile { target c++26 } }
+
+#include <functional>
+
+template<typename F>
+constexpr std::function_ref<int(int, int) const>
+create(F f)
+{
+ std::function_ref<int(int, int) const> fr(f);
+ return fr;
+}
+
+constexpr auto vLambda = create([](int x, int y) { return x + y; }); // {
dg-error "is not a constant expression" }
+
+struct Empty
+{
+ int
+ operator()(int x, int y) const
+ { return x + y; }
+};
+
+constexpr auto vEmpty = create(Empty{}); // { dg-error "is not a constant
expression" }
+
+struct NonEmpty {
+ int v;
+
+ int
+ operator()(int x, int y) const
+ { return x + y + v; }
+};
+
+constexpr auto vNonEmpty = create(NonEmpty{3}); // { dg-error "is not a
constant expression" }
+
+struct InstanceWins {
+ int
+ operator()(int x, int y) const
+ { return x + y; }
+
+ static int
+ operator()(float x, float y)
+ { return x + y; }
+};
+
+constexpr auto vInstanceWins = create(InstanceWins{}); // { dg-error "is not a
constant expression" }
+
+struct EmptyET
+{
+ int
+ operator()(this EmptyET, int x, int y)
+ { return x + y; }
+};
+
+constexpr auto vEmptyET = create(EmptyET{}); // { dg-error "is not a constant
expression" }
+
+struct NonEmptyET {
+ int v;
+
+ int
+ operator()(this NonEmptyET s, int x, int y)
+ { return x + y + s.v; }
+};
+
+constexpr auto vNonEmptyET = create(NonEmptyET{3}); // { dg-error "is not a
constant expression" }
+
+struct InstanceWinsET {
+ int
+ operator()(this InstanceWinsET, int x, int y)
+ { return x + y; }
+
+ static int
+ operator()(float x, float y)
+ { return x + y; }
+};
+
+constexpr auto vInstanceWinsET = create(InstanceWinsET{}); // { dg-error "is
not a constant expression" }
+
--
2.52.0