Skip to content

Commit

Permalink
[libc++] Make std::bind constexpr-friendly
Browse files Browse the repository at this point in the history
std::bind is supposed to be constexpr-friendly since C++20 and it was
marked as such in our synopsis. However, the tests were not actually
testing any of it and as it happens, std::bind was not really constexpr
friendly. This fixes the issue and makes sure that at least some of the
tests are running in constexpr mode.

Some tests for std::bind check functions that return void, and those
use global variables. These tests haven't been made constexpr-friendly,
however the coverage added by this patch should be sufficient to get
decent confidence.

Differential Revision: https://reviews.llvm.org/D149295
  • Loading branch information
ldionne committed Apr 27, 2023
1 parent 77ac365 commit 901a22b
Show file tree
Hide file tree
Showing 7 changed files with 130 additions and 103 deletions.
12 changes: 6 additions & 6 deletions libcxx/include/__functional/bind.h
Original file line number Diff line number Diff line change
Expand Up @@ -80,23 +80,23 @@ struct is_placeholder<placeholders::__ph<_Np> >
#ifndef _LIBCPP_CXX03_LANG

template <class _Tp, class _Uj>
inline _LIBCPP_INLINE_VISIBILITY
inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_SINCE_CXX20
_Tp&
__mu(reference_wrapper<_Tp> __t, _Uj&)
{
return __t.get();
}

template <class _Ti, class ..._Uj, size_t ..._Indx>
inline _LIBCPP_INLINE_VISIBILITY
inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_SINCE_CXX20
typename __invoke_of<_Ti&, _Uj...>::type
__mu_expand(_Ti& __ti, tuple<_Uj...>& __uj, __tuple_indices<_Indx...>)
{
return __ti(_VSTD::forward<_Uj>(_VSTD::get<_Indx>(__uj))...);
}

template <class _Ti, class ..._Uj>
inline _LIBCPP_INLINE_VISIBILITY
inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_SINCE_CXX20
typename __enable_if_t
<
is_bind_expression<_Ti>::value,
Expand All @@ -118,7 +118,7 @@ struct __mu_return2<true, _Ti, _Uj>
};

template <class _Ti, class _Uj>
inline _LIBCPP_INLINE_VISIBILITY
inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_SINCE_CXX20
typename enable_if
<
0 < is_placeholder<_Ti>::value,
Expand All @@ -131,7 +131,7 @@ __mu(_Ti&, _Uj& __uj)
}

template <class _Ti, class _Uj>
inline _LIBCPP_INLINE_VISIBILITY
inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_SINCE_CXX20
typename enable_if
<
!is_bind_expression<_Ti>::value &&
Expand Down Expand Up @@ -249,7 +249,7 @@ struct __bind_return<_Fp, const tuple<_BoundArgs...>, _TupleUj, true>
};

template <class _Fp, class _BoundArgs, size_t ..._Indx, class _Args>
inline _LIBCPP_INLINE_VISIBILITY
inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_SINCE_CXX20
typename __bind_return<_Fp, _BoundArgs, _Args>::type
__apply_functor(_Fp& __f, _BoundArgs& __bound_args, __tuple_indices<_Indx...>,
_Args&& __args)
Expand Down
4 changes: 2 additions & 2 deletions libcxx/include/__functional/invoke.h
Original file line number Diff line number Diff line change
Expand Up @@ -474,7 +474,7 @@ template <class _Ret, bool = is_void<_Ret>::value>
struct __invoke_void_return_wrapper
{
template <class ..._Args>
_LIBCPP_HIDE_FROM_ABI static _Ret __call(_Args&&... __args) {
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 static _Ret __call(_Args&&... __args) {
return std::__invoke(std::forward<_Args>(__args)...);
}
};
Expand All @@ -483,7 +483,7 @@ template <class _Ret>
struct __invoke_void_return_wrapper<_Ret, true>
{
template <class ..._Args>
_LIBCPP_HIDE_FROM_ABI static void __call(_Args&&... __args) {
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 static void __call(_Args&&... __args) {
std::__invoke(std::forward<_Args>(__args)...);
}
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,28 +11,33 @@
// <functional>

// template<CopyConstructible Fn, CopyConstructible... Types>
// unspecified bind(Fn, Types...);
// unspecified bind(Fn, Types...); // constexpr since C++20
// template<Returnable R, CopyConstructible Fn, CopyConstructible... Types>
// unspecified bind(Fn, Types...);
// unspecified bind(Fn, Types...); // constexpr since C++20

// https://llvm.org/PR23141
#include <functional>
#include <type_traits>

#include "test_macros.h"

struct Fun
{
struct Fun {
template<typename T, typename U>
void operator()(T &&, U &&) const
{
TEST_CONSTEXPR_CXX20 void operator()(T &&, U &&) const {
static_assert(std::is_same<U, int &>::value, "");
}
};

int main(int, char**)
{
std::bind(Fun{}, std::placeholders::_1, 42)("hello");
TEST_CONSTEXPR_CXX20 bool test() {
std::bind(Fun{}, std::placeholders::_1, 42)("hello");
return true;
}

int main(int, char**) {
test();
#if TEST_STD_VER >= 20
static_assert(test());
#endif

return 0;
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,39 +11,35 @@
// <functional>

// template<CopyConstructible Fn, CopyConstructible... Types>
// unspecified bind(Fn, Types...);
// unspecified bind(Fn, Types...); // constexpr since C++20
// template<Returnable R, CopyConstructible Fn, CopyConstructible... Types>
// unspecified bind(Fn, Types...);
// unspecified bind(Fn, Types...); // constexpr since C++20

#include <functional>
#include <cassert>

#include "test_macros.h"

template <class R, class F>
void
test(F f, R expected)
{
TEST_CONSTEXPR_CXX20
void test(F f, R expected) {
assert(f() == expected);
}

template <class R, class F>
void
test_const(const F& f, R expected)
{
TEST_CONSTEXPR_CXX20
void test_const(const F& f, R expected) {
assert(f() == expected);
}

int f() {return 1;}
TEST_CONSTEXPR_CXX20 int f() {return 1;}

struct A_int_0
{
int operator()() {return 4;}
int operator()() const {return 5;}
struct A_int_0 {
TEST_CONSTEXPR_CXX20 int operator()() {return 4;}
TEST_CONSTEXPR_CXX20 int operator()() const {return 5;}
};

int main(int, char**)
{
TEST_CONSTEXPR_CXX20 bool test_all() {
test(std::bind(f), 1);
test(std::bind(&f), 1);
test(std::bind(A_int_0()), 4);
Expand All @@ -53,6 +49,14 @@ int main(int, char**)
test(std::bind<int>(&f), 1);
test(std::bind<int>(A_int_0()), 4);
test_const(std::bind<int>(A_int_0()), 5);
return true;
}

int main(int, char**) {
test_all();
#if TEST_STD_VER >= 20
static_assert(test_all());
#endif

return 0;
return 0;
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@
// <functional>

// template<CopyConstructible Fn, CopyConstructible... Types>
// unspecified bind(Fn, Types...);
// unspecified bind(Fn, Types...); // constexpr since C++20
// template<Returnable R, CopyConstructible Fn, CopyConstructible... Types>
// unspecified bind(Fn, Types...);
// unspecified bind(Fn, Types...); // constexpr since C++20

#include <stdio.h>

Expand Down Expand Up @@ -140,27 +140,22 @@ test_void_1()

// 1 arg, return int

int f_int_1(int i)
{
TEST_CONSTEXPR_CXX20 int f_int_1(int i) {
return i + 1;
}

struct A_int_1
{
A_int_1() : data_(5) {}
int operator()(int i)
{
struct A_int_1 {
TEST_CONSTEXPR_CXX20 A_int_1() : data_(5) {}
TEST_CONSTEXPR_CXX20 int operator()(int i) {
return i - 1;
}

int mem1() {return 3;}
int mem2() const {return 4;}
TEST_CONSTEXPR_CXX20 int mem1() { return 3; }
TEST_CONSTEXPR_CXX20 int mem2() const { return 4; }
int data_;
};

void
test_int_1()
{
TEST_CONSTEXPR_CXX20 bool test_int_1() {
using namespace std::placeholders;
// function
{
Expand Down Expand Up @@ -212,6 +207,8 @@ test_int_1()
std::bind(&A_int_1::data_, _1)(ap) = 7;
assert(std::bind(&A_int_1::data_, _1)(ap) == 7);
}

return true;
}

// 2 arg, return void
Expand Down Expand Up @@ -263,31 +260,45 @@ test_void_2()
}
}

struct TFENode
{
bool foo(unsigned long long) const
{
struct ConstQualifiedMemberFunction {
TEST_CONSTEXPR_CXX20 bool foo(unsigned long long) const {
return true;
}
};

void
test3()
{
TEST_CONSTEXPR_CXX20 bool test_const_qualified_member() {
using namespace std;
using namespace std::placeholders;
const auto f = bind(&TFENode::foo, _1, 0UL);
const TFENode n = TFENode{};
const auto f = bind(&ConstQualifiedMemberFunction::foo, _1, 0UL);
const ConstQualifiedMemberFunction n = ConstQualifiedMemberFunction{};
bool b = f(n);
assert(b);
return true;
}

int main(int, char**)
{
TEST_CONSTEXPR_CXX20 bool test_many_args() {
using namespace std::placeholders;
auto f = [](int& a, char&, float&, long&) -> int& { return a; };
auto bound = std::bind(f, _4, _3, _2, _1);
int a = 3; char b = '2'; float c = 1.0f; long d = 0l;
int& result = bound(d, c, b, a);
assert(&result == &a);
return true;
}

int main(int, char**) {
test_void_1();
test_int_1();
test_void_2();
test3();
test_const_qualified_member();
test_many_args();

// The other tests are not constexpr-friendly since they need to use a global variable
#if TEST_STD_VER >= 20
static_assert(test_int_1());
static_assert(test_const_qualified_member());
static_assert(test_many_args());
#endif

return 0;
return 0;
}

0 comments on commit 901a22b

Please sign in to comment.