diff options
| author | Eric Fiselier <eric@efcs.ca> | 2016-11-02 03:57:34 +0000 |
|---|---|---|
| committer | Eric Fiselier <eric@efcs.ca> | 2016-11-02 03:57:34 +0000 |
| commit | 0b460e6bf8e0c418558401acc4832538ed8b662b (patch) | |
| tree | 410814dca6b97572b4a309bdab1883b86e3ec439 /libcxx/test | |
| parent | 76a3ace1f0897906f59166b341008cf386b9e1f9 (diff) | |
| download | bcm5719-llvm-0b460e6bf8e0c418558401acc4832538ed8b662b.tar.gz bcm5719-llvm-0b460e6bf8e0c418558401acc4832538ed8b662b.zip | |
Fix __libcpp_is_constructible for source types with explicit conversion operators.
Previously __libcpp_is_constructible checked the validity of reference
construction using 'eat<To>(declval<From>())' but this doesn't consider
From's explicit conversion operators. This patch teaches __libcpp_is_constructible
how to handle these cases. To do this we need to check the validity
using 'static_cast<To>(declval<From>())'. Unfortunately static_cast allows
additional base-to-derived and lvalue-to-rvalue conversions, which have to be
checked for and manually rejected.
While implementing these changes I discovered that Clang incorrectly
rejects `static_cast<int&&>(declval<float&>())` even though
`int &&X(declval<float&>())` is well formed. In order to tolerate this bug
the `__eat<T>(...)` needs to be left in-place. Otherwise it could be replaced
entirely with the new static_cast implementation.
Thanks to Walter Brown for providing the test cases.
llvm-svn: 285786
Diffstat (limited to 'libcxx/test')
| -rw-r--r-- | libcxx/test/std/utilities/meta/meta.unary/meta.unary.prop/is_constructible.pass.cpp | 120 |
1 files changed, 118 insertions, 2 deletions
diff --git a/libcxx/test/std/utilities/meta/meta.unary/meta.unary.prop/is_constructible.pass.cpp b/libcxx/test/std/utilities/meta/meta.unary/meta.unary.prop/is_constructible.pass.cpp index 7f5ab7214fd..eb942a52a5b 100644 --- a/libcxx/test/std/utilities/meta/meta.unary/meta.unary.prop/is_constructible.pass.cpp +++ b/libcxx/test/std/utilities/meta/meta.unary/meta.unary.prop/is_constructible.pass.cpp @@ -14,9 +14,17 @@ // template <class T, class... Args> // struct is_constructible; +#define _LIBCPP_TESTING_FALLBACK_IS_CONSTRUCTIBLE #include <type_traits> #include "test_macros.h" +#if TEST_STD_VER >= 11 && defined(_LIBCPP_VERSION) +#define LIBCPP11_STATIC_ASSERT(...) static_assert(__VA_ARGS__) +#else +#define LIBCPP11_STATIC_ASSERT(...) ((void)0) +#endif + + struct A { explicit A(int); @@ -51,14 +59,27 @@ struct S { #if TEST_STD_VER >= 11 explicit #endif - operator T () const { return T(); } + operator T () const; +}; + +template <class To> +struct ImplicitTo { + operator To(); +}; + +#if TEST_STD_VER >= 11 +template <class To> +struct ExplicitTo { + explicit operator To (); }; +#endif template <class T> void test_is_constructible() { static_assert( (std::is_constructible<T>::value), ""); + LIBCPP11_STATIC_ASSERT((std::__libcpp_is_constructible<T>::type::value), ""); #if TEST_STD_VER > 14 static_assert( std::is_constructible_v<T>, ""); #endif @@ -68,6 +89,7 @@ template <class T, class A0> void test_is_constructible() { static_assert(( std::is_constructible<T, A0>::value), ""); + LIBCPP11_STATIC_ASSERT((std::__libcpp_is_constructible<T, A0>::type::value), ""); #if TEST_STD_VER > 14 static_assert(( std::is_constructible_v<T, A0>), ""); #endif @@ -77,6 +99,7 @@ template <class T, class A0, class A1> void test_is_constructible() { static_assert(( std::is_constructible<T, A0, A1>::value), ""); + LIBCPP11_STATIC_ASSERT((std::__libcpp_is_constructible<T, A0, A1>::type::value), ""); #if TEST_STD_VER > 14 static_assert(( std::is_constructible_v<T, A0, A1>), ""); #endif @@ -86,6 +109,7 @@ template <class T> void test_is_not_constructible() { static_assert((!std::is_constructible<T>::value), ""); + LIBCPP11_STATIC_ASSERT((!std::__libcpp_is_constructible<T>::type::value), ""); #if TEST_STD_VER > 14 static_assert((!std::is_constructible_v<T>), ""); #endif @@ -95,13 +119,28 @@ template <class T, class A0> void test_is_not_constructible() { static_assert((!std::is_constructible<T, A0>::value), ""); + LIBCPP11_STATIC_ASSERT((!std::__libcpp_is_constructible<T, A0>::type::value), ""); #if TEST_STD_VER > 14 static_assert((!std::is_constructible_v<T, A0>), ""); #endif } +#if TEST_STD_VER >= 11 +template <class T = int, class = decltype(static_cast<T&&>(std::declval<double&>()))> +constexpr bool clang_disallows_valid_static_cast_test(int) { return false; }; + +constexpr bool clang_disallows_valid_static_cast_test(long) { return true; } + +static constexpr bool clang_disallows_valid_static_cast_bug = + clang_disallows_valid_static_cast_test(0); +#endif + + int main() { + typedef Base B; + typedef Derived D; + test_is_constructible<int> (); test_is_constructible<int, const int> (); test_is_constructible<A, int> (); @@ -115,6 +154,14 @@ int main() test_is_constructible<A, char> (); #endif test_is_not_constructible<A, void> (); + test_is_not_constructible<int, void()>(); + test_is_not_constructible<int, void(&)()>(); + test_is_not_constructible<int, void() const>(); + test_is_not_constructible<int&, void>(); + test_is_not_constructible<int&, void()>(); + test_is_not_constructible<int&, void() const>(); + test_is_not_constructible<int&, void(&)()>(); + test_is_not_constructible<void> (); test_is_not_constructible<const void> (); // LWG 2738 test_is_not_constructible<volatile void> (); @@ -125,10 +172,21 @@ int main() test_is_constructible<int, S>(); test_is_not_constructible<int&, S>(); + test_is_constructible<void(&)(), void(&)()>(); + test_is_constructible<void(&)(), void()>(); +#if TEST_STD_VER >= 11 + test_is_constructible<void(&&)(), void(&&)()>(); + test_is_constructible<void(&&)(), void()>(); + test_is_constructible<void(&&)(), void(&)()>(); +#endif + #if TEST_STD_VER >= 11 test_is_constructible<int const&, int>(); test_is_constructible<int const&, int&&>(); + test_is_constructible<int&&, double&>(); + test_is_constructible<void(&)(), void(&&)()>(); + test_is_not_constructible<int&, int>(); test_is_not_constructible<int&, int const&>(); test_is_not_constructible<int&, int&&>(); @@ -157,6 +215,64 @@ int main() test_is_not_constructible<void() const, void() const>(); test_is_not_constructible<void() const, void*>(); + test_is_constructible<int&, ImplicitTo<int&>>(); + test_is_constructible<const int&, ImplicitTo<int&&>>(); + test_is_constructible<int&&, ImplicitTo<int&&>>(); + test_is_constructible<const int&, ImplicitTo<int>>(); + + test_is_not_constructible<B&&, B&>(); + test_is_not_constructible<B&&, D&>(); + test_is_constructible<B&&, ImplicitTo<D&&>>(); + test_is_constructible<B&&, ImplicitTo<D&&>&>(); + test_is_constructible<int&&, double&>(); + test_is_constructible<const int&, ImplicitTo<int&>&>(); + test_is_constructible<const int&, ImplicitTo<int&>>(); + test_is_constructible<const int&, ExplicitTo<int&>&>(); + test_is_constructible<const int&, ExplicitTo<int&>>(); + + test_is_constructible<const int&, ExplicitTo<int&>&>(); + test_is_constructible<const int&, ExplicitTo<int&>>(); + test_is_constructible<int&, ExplicitTo<int&>>(); + test_is_constructible<const int&, ExplicitTo<int&&>>(); + + // Binding through reference-compatible type is required to perform + // direct-initialization as described in [over.match.ref] p. 1 b. 1: + test_is_constructible<int&, ExplicitTo<int&>>(); + test_is_constructible<const int&, ExplicitTo<int&&>>(); + + static_assert(std::is_constructible<int&&, ExplicitTo<int&&>>::value, ""); +#ifdef __clang__ +#if defined(CLANG_TEST_VER) && CLANG_TEST_VER < 400 + static_assert(clang_disallows_valid_static_cast_bug, "bug still exists"); +#endif + // FIXME Clang disallows this construction because it thinks that + // 'static_cast<int&&>(declval<ExplicitTo<int&&>>())' is ill-formed. + LIBCPP_STATIC_ASSERT( + clang_disallows_valid_static_cast_bug != + std::__libcpp_is_constructible<int&&, ExplicitTo<int&&>>::value, ""); +#else + static_assert(clang_disallows_valid_static_cast_bug == false, ""); + LIBCPP_STATIC_ASSERT(std::__libcpp_is_constructible<int&&, ExplicitTo<int&&>>::value, ""); +#endif + +#ifdef __clang__ + // FIXME Clang and GCC disagree on the validity of this expression. + test_is_constructible<const int&, ExplicitTo<int>>(); + static_assert(std::is_constructible<int&&, ExplicitTo<int>>::value, ""); + LIBCPP_STATIC_ASSERT( + clang_disallows_valid_static_cast_bug != + std::__libcpp_is_constructible<int&&, ExplicitTo<int>>::value, ""); +#else + test_is_not_constructible<const int&, ExplicitTo<int>>(); + test_is_not_constructible<int&&, ExplicitTo<int>>(); +#endif + + // Binding through temporary behaves like copy-initialization, + // see [dcl.init.ref] p. 5, very last sub-bullet: + test_is_not_constructible<const int&, ExplicitTo<double&&>>(); + test_is_not_constructible<int&&, ExplicitTo<double&&>>(); + + // TODO: Remove this workaround once Clang <= 3.7 are no longer used regularly. // In those compiler versions the __is_constructible builtin gives the wrong // results for abominable function types. @@ -171,5 +287,5 @@ int main() test_is_not_constructible<void() &> (); test_is_not_constructible<void() &&> (); #endif -#endif +#endif // TEST_STD_VER >= 11 } |

