diff --git a/libcxx/include/__functional_base b/libcxx/include/__functional_base --- a/libcxx/include/__functional_base +++ b/libcxx/include/__functional_base @@ -308,7 +308,7 @@ #endif // !defined(_LIBCPP_CXX03_LANG) -template +template ::value> struct __invoke_void_return_wrapper { #ifndef _LIBCPP_CXX03_LANG @@ -339,8 +339,8 @@ #endif }; -template <> -struct __invoke_void_return_wrapper +template +struct __invoke_void_return_wrapper<_Ret, true> { #ifndef _LIBCPP_CXX03_LANG template diff --git a/libcxx/include/functional b/libcxx/include/functional --- a/libcxx/include/functional +++ b/libcxx/include/functional @@ -2348,9 +2348,9 @@ template struct __callable<_Fp, true> { - static const bool value = is_same::value || - is_convertible::type, - _Rp>::value; + static const bool value = is_void<_Rp>::value || + __is_core_convertible::type, + _Rp>::value; }; template struct __callable<_Fp, false> diff --git a/libcxx/include/type_traits b/libcxx/include/type_traits --- a/libcxx/include/type_traits +++ b/libcxx/include/type_traits @@ -1665,6 +1665,21 @@ = 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 is false, +// and __is_core_convertible is true in C++17 and later. + +template +struct __is_core_convertible : public false_type {}; + +template +struct __is_core_convertible<_Tp, _Up, decltype( + static_cast(0) ( static_cast<_Tp(*)()>(0)() ) +)> : public true_type {}; + // is_convertible #if __has_feature(is_convertible_to) && !defined(_LIBCPP_USE_IS_CONVERTIBLE_FALLBACK) diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.func/noncopyable_return_type.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.func/noncopyable_return_type.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.func/noncopyable_return_type.pass.cpp @@ -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 + +// + +#include +#include + +#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 f1a = []() { return NonCopyable(); }; + std::function f2a = +[]() { return NonCopyable(); }; + std::function f3a = LargeLambda(); + std::function f4a = std::ref(f1a); + std::function f1b = [](int) { return NonCopyable(); }; + std::function f2b = +[](int) { return NonCopyable(); }; + std::function f3b = LargeLambda(); + std::function 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 f1a = []() { return NonCopyable(); }; + std::function f2a = +[]() { return NonCopyable(); }; + std::function f3a = LargeLambda(); + std::function f4a = std::ref(f1a); + std::function f1b = [](int) { return NonCopyable(); }; + std::function f2b = +[](int) { return NonCopyable(); }; + std::function f3b = LargeLambda(); + std::function 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 f1a = []() { return NonCopyable(); }; + std::function f2a = +[]() { return NonCopyable(); }; + std::function f3a = LargeLambda(); + std::function f4a = std::ref(f1a); + std::function f1b = [](int) { return NonCopyable(); }; + std::function f2b = +[](int) { return NonCopyable(); }; + std::function f3b = LargeLambda(); + std::function f4b = std::ref(f1b); +} + +void const_void_test() +{ + std::function f1a = []() { return NonCopyable(); }; + std::function f2a = +[]() { return NonCopyable(); }; + std::function f3a = LargeLambda(); + std::function f4a = std::ref(f1a); + std::function f1b = [](int) { return NonCopyable(); }; + std::function f2b = +[](int) { return NonCopyable(); }; + std::function f3b = LargeLambda(); + std::function f4b = std::ref(f1b); +} + +void member_pointer_test() +{ + std::function f1a = &LargeLambda::f; + std::function f2a = &LargeLambda::f; + LargeLambda ll; + assert(f1a(&ll) == f2a(ll)); + + static_assert(std::is_convertible_v>); + static_assert(std::is_convertible_v>); + static_assert(std::is_convertible_v>); + static_assert(std::is_convertible_v>); + static_assert(std::is_convertible_v>); + static_assert(std::is_convertible_v>); + + // 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>); + static_assert(!std::is_convertible_v>); + static_assert(!std::is_convertible_v>); + static_assert(std::is_convertible_v>); + static_assert(std::is_convertible_v>); + static_assert(std::is_convertible_v>); +} + +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>); + static_assert(std::is_same_v>); + static_assert(std::is_same_v>); + static_assert(std::is_same_v>); +} + +int main(int, char**) +{ + test(); + const_test(); + void_test(); + const_void_test(); + member_pointer_test(); + ctad_test(); + return 0; +}