forked from OSchip/llvm-project
[libc++] Support immovable return types in std::function.
LWG reflector consensus is that this was a bug in libc++. (In particular, MSVC also will fix it in their STL, soon.) Bug originally discovered by Logan Smith. Also fix `std::function<const void()>`, which should work the same way as `std::function<void()>` in terms of allowing "conversions" from non-void types. Differential Revision: https://reviews.llvm.org/D94452
This commit is contained in:
parent
70e251497c
commit
f9b6fd269b
|
@ -308,7 +308,7 @@ struct __invoke_return
|
|||
#endif // !defined(_LIBCPP_CXX03_LANG)
|
||||
|
||||
|
||||
template <class _Ret>
|
||||
template <class _Ret, bool = is_void<_Ret>::value>
|
||||
struct __invoke_void_return_wrapper
|
||||
{
|
||||
#ifndef _LIBCPP_CXX03_LANG
|
||||
|
@ -339,8 +339,8 @@ struct __invoke_void_return_wrapper
|
|||
#endif
|
||||
};
|
||||
|
||||
template <>
|
||||
struct __invoke_void_return_wrapper<void>
|
||||
template <class _Ret>
|
||||
struct __invoke_void_return_wrapper<_Ret, true>
|
||||
{
|
||||
#ifndef _LIBCPP_CXX03_LANG
|
||||
template <class ..._Args>
|
||||
|
|
|
@ -2348,9 +2348,9 @@ class _LIBCPP_TEMPLATE_VIS function<_Rp(_ArgTypes...)>
|
|||
template <class _Fp>
|
||||
struct __callable<_Fp, true>
|
||||
{
|
||||
static const bool value = is_same<void, _Rp>::value ||
|
||||
is_convertible<typename __invoke_of<_Fp, _ArgTypes...>::type,
|
||||
_Rp>::value;
|
||||
static const bool value = is_void<_Rp>::value ||
|
||||
__is_core_convertible<typename __invoke_of<_Fp, _ArgTypes...>::type,
|
||||
_Rp>::value;
|
||||
};
|
||||
template <class _Fp>
|
||||
struct __callable<_Fp, false>
|
||||
|
|
|
@ -1665,6 +1665,21 @@ _LIBCPP_INLINE_VAR _LIBCPP_CONSTEXPR bool is_base_of_v
|
|||
= is_base_of<_Bp, _Dp>::value;
|
||||
#endif
|
||||
|
||||
// __is_core_convertible
|
||||
|
||||
// [conv.general]/3 says "E is convertible to T" whenever "T t=E;" is well-formed.
|
||||
// We can't test for that, but we can test implicit convertibility by passing it
|
||||
// to a function. Notice that __is_core_convertible<void,void> is false,
|
||||
// and __is_core_convertible<immovable-type,immovable-type> is true in C++17 and later.
|
||||
|
||||
template <class _Tp, class _Up, class = void>
|
||||
struct __is_core_convertible : public false_type {};
|
||||
|
||||
template <class _Tp, class _Up>
|
||||
struct __is_core_convertible<_Tp, _Up, decltype(
|
||||
static_cast<void(*)(_Up)>(0) ( static_cast<_Tp(*)()>(0)() )
|
||||
)> : public true_type {};
|
||||
|
||||
// is_convertible
|
||||
|
||||
#if __has_feature(is_convertible_to) && !defined(_LIBCPP_USE_IS_CONVERTIBLE_FALLBACK)
|
||||
|
|
|
@ -0,0 +1,138 @@
|
|||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// UNSUPPORTED: c++03, c++11, c++14
|
||||
|
||||
// <functional>
|
||||
|
||||
#include <functional>
|
||||
#include <cassert>
|
||||
|
||||
#include "test_macros.h"
|
||||
|
||||
// Prevent warning on the `const NonCopyable()` function type.
|
||||
#if defined(__GNUC__) || defined(__clang__)
|
||||
#pragma GCC diagnostic ignored "-Wignored-qualifiers"
|
||||
#endif
|
||||
|
||||
struct NonCopyable {
|
||||
NonCopyable() = default;
|
||||
NonCopyable(NonCopyable&&) = delete;
|
||||
friend bool operator==(NonCopyable, NonCopyable) { return true; }
|
||||
};
|
||||
|
||||
struct LargeLambda {
|
||||
int a[100];
|
||||
NonCopyable operator()() const { return NonCopyable(); }
|
||||
NonCopyable operator()(int) const { return NonCopyable(); }
|
||||
NonCopyable f() const { return NonCopyable(); }
|
||||
};
|
||||
|
||||
void test()
|
||||
{
|
||||
std::function<NonCopyable()> f1a = []() { return NonCopyable(); };
|
||||
std::function<NonCopyable()> f2a = +[]() { return NonCopyable(); };
|
||||
std::function<NonCopyable()> f3a = LargeLambda();
|
||||
std::function<NonCopyable()> f4a = std::ref(f1a);
|
||||
std::function<NonCopyable(int)> f1b = [](int) { return NonCopyable(); };
|
||||
std::function<NonCopyable(int)> f2b = +[](int) { return NonCopyable(); };
|
||||
std::function<NonCopyable(int)> f3b = LargeLambda();
|
||||
std::function<NonCopyable(int)> f4b = std::ref(f1b);
|
||||
|
||||
assert(f1a() == f2a());
|
||||
assert(f3a() == f4a());
|
||||
assert(f1b(1) == f2b(1));
|
||||
assert(f3b(1) == f4b(1));
|
||||
}
|
||||
|
||||
void const_test()
|
||||
{
|
||||
std::function<const NonCopyable()> f1a = []() { return NonCopyable(); };
|
||||
std::function<const NonCopyable()> f2a = +[]() { return NonCopyable(); };
|
||||
std::function<const NonCopyable()> f3a = LargeLambda();
|
||||
std::function<const NonCopyable()> f4a = std::ref(f1a);
|
||||
std::function<const NonCopyable(int)> f1b = [](int) { return NonCopyable(); };
|
||||
std::function<const NonCopyable(int)> f2b = +[](int) { return NonCopyable(); };
|
||||
std::function<const NonCopyable(int)> f3b = LargeLambda();
|
||||
std::function<const NonCopyable(int)> f4b = std::ref(f1b);
|
||||
|
||||
assert(f1a() == f2a());
|
||||
assert(f3a() == f4a());
|
||||
assert(f1b(1) == f2b(1));
|
||||
assert(f3b(1) == f4b(1));
|
||||
}
|
||||
|
||||
void void_test()
|
||||
{
|
||||
std::function<void()> f1a = []() { return NonCopyable(); };
|
||||
std::function<void()> f2a = +[]() { return NonCopyable(); };
|
||||
std::function<void()> f3a = LargeLambda();
|
||||
std::function<void()> f4a = std::ref(f1a);
|
||||
std::function<void(int)> f1b = [](int) { return NonCopyable(); };
|
||||
std::function<void(int)> f2b = +[](int) { return NonCopyable(); };
|
||||
std::function<void(int)> f3b = LargeLambda();
|
||||
std::function<void(int)> f4b = std::ref(f1b);
|
||||
}
|
||||
|
||||
void const_void_test()
|
||||
{
|
||||
std::function<const void()> f1a = []() { return NonCopyable(); };
|
||||
std::function<const void()> f2a = +[]() { return NonCopyable(); };
|
||||
std::function<const void()> f3a = LargeLambda();
|
||||
std::function<const void()> f4a = std::ref(f1a);
|
||||
std::function<const void(int)> f1b = [](int) { return NonCopyable(); };
|
||||
std::function<const void(int)> f2b = +[](int) { return NonCopyable(); };
|
||||
std::function<const void(int)> f3b = LargeLambda();
|
||||
std::function<const void(int)> f4b = std::ref(f1b);
|
||||
}
|
||||
|
||||
void member_pointer_test()
|
||||
{
|
||||
std::function<NonCopyable(LargeLambda*)> f1a = &LargeLambda::f;
|
||||
std::function<NonCopyable(LargeLambda&)> f2a = &LargeLambda::f;
|
||||
LargeLambda ll;
|
||||
assert(f1a(&ll) == f2a(ll));
|
||||
|
||||
static_assert(std::is_convertible_v<NonCopyable (LargeLambda::*)(), std::function<NonCopyable(LargeLambda*)>>);
|
||||
static_assert(std::is_convertible_v<NonCopyable (LargeLambda::*)(), std::function<NonCopyable(LargeLambda&)>>);
|
||||
static_assert(std::is_convertible_v<NonCopyable (LargeLambda::*)() const, std::function<NonCopyable(LargeLambda*)>>);
|
||||
static_assert(std::is_convertible_v<NonCopyable (LargeLambda::*)() const, std::function<NonCopyable(LargeLambda&)>>);
|
||||
static_assert(std::is_convertible_v<NonCopyable (LargeLambda::*)() const, std::function<NonCopyable(const LargeLambda*)>>);
|
||||
static_assert(std::is_convertible_v<NonCopyable (LargeLambda::*)() const, std::function<NonCopyable(const LargeLambda&)>>);
|
||||
|
||||
// Verify we have SFINAE against invoking a pointer-to-data-member in a way that would have to copy the NonCopyable.
|
||||
static_assert(!std::is_convertible_v<NonCopyable LargeLambda::*, std::function<NonCopyable(LargeLambda*)>>);
|
||||
static_assert(!std::is_convertible_v<NonCopyable LargeLambda::*, std::function<NonCopyable(LargeLambda&)>>);
|
||||
static_assert(!std::is_convertible_v<NonCopyable LargeLambda::*, std::function<NonCopyable&(const LargeLambda&)>>);
|
||||
static_assert(std::is_convertible_v<NonCopyable LargeLambda::*, std::function<NonCopyable&(LargeLambda*)>>);
|
||||
static_assert(std::is_convertible_v<NonCopyable LargeLambda::*, std::function<NonCopyable&(LargeLambda&)>>);
|
||||
static_assert(std::is_convertible_v<NonCopyable LargeLambda::*, std::function<const NonCopyable&(const LargeLambda&)>>);
|
||||
}
|
||||
|
||||
void ctad_test()
|
||||
{
|
||||
std::function f1a = []() { return NonCopyable(); };
|
||||
std::function f2a = +[]() { return NonCopyable(); };
|
||||
std::function f1b = [](int) { return NonCopyable(); };
|
||||
std::function f2b = +[](int) { return NonCopyable(); };
|
||||
static_assert(std::is_same_v<decltype(f1a), std::function<NonCopyable()>>);
|
||||
static_assert(std::is_same_v<decltype(f2a), std::function<NonCopyable()>>);
|
||||
static_assert(std::is_same_v<decltype(f1b), std::function<NonCopyable(int)>>);
|
||||
static_assert(std::is_same_v<decltype(f2b), std::function<NonCopyable(int)>>);
|
||||
}
|
||||
|
||||
int main(int, char**)
|
||||
{
|
||||
test();
|
||||
const_test();
|
||||
void_test();
|
||||
const_void_test();
|
||||
member_pointer_test();
|
||||
ctad_test();
|
||||
return 0;
|
||||
}
|
Loading…
Reference in New Issue