[libcxx] r285786 - Fix __libcpp_is_constructible for source types with explicit conversion operators.
Eric Fiselier via cfe-commits
cfe-commits at lists.llvm.org
Tue Nov 1 20:57:34 PDT 2016
Author: ericwf
Date: Tue Nov 1 22:57:34 2016
New Revision: 285786
URL: http://llvm.org/viewvc/llvm-project?rev=285786&view=rev
Log:
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.
Modified:
libcxx/trunk/include/type_traits
libcxx/trunk/test/std/utilities/meta/meta.unary/meta.unary.prop/is_constructible.pass.cpp
Modified: libcxx/trunk/include/type_traits
URL: http://llvm.org/viewvc/llvm-project/libcxx/trunk/include/type_traits?rev=285786&r1=285785&r2=285786&view=diff
==============================================================================
--- libcxx/trunk/include/type_traits (original)
+++ libcxx/trunk/include/type_traits Tue Nov 1 22:57:34 2016
@@ -2894,26 +2894,64 @@ namespace __is_construct
struct __nat {};
}
-#if __has_feature(is_constructible)
+#if !defined(_LIBCPP_CXX03_LANG) && (!__has_feature(is_constructible) || \
+ defined(_LIBCPP_TESTING_FALLBACK_IS_CONSTRUCTIBLE))
-template <class _Tp, class ..._Args>
-struct _LIBCPP_TYPE_VIS_ONLY is_constructible
- : public integral_constant<bool, __is_constructible(_Tp, _Args...)>
- {};
-
-#else
+template <class _Tp, class... _Args>
+struct __libcpp_is_constructible;
-#ifndef _LIBCPP_HAS_NO_VARIADICS
+template <class _To, class _From>
+struct __is_invalid_base_to_derived_cast {
+ static_assert(is_reference<_To>::value, "Wrong specialization");
+ using _RawFrom = __uncvref_t<_From>;
+ using _RawTo = __uncvref_t<_To>;
+ static const bool value = __lazy_and<
+ __lazy_not<is_same<_RawFrom, _RawTo>>,
+ is_base_of<_RawFrom, _RawTo>,
+ __lazy_not<__libcpp_is_constructible<_RawTo, _From>>
+ >::value;
+};
-// main is_constructible test
+template <class _To, class _From>
+struct __is_invalid_lvalue_to_rvalue_cast : false_type {
+ static_assert(is_reference<_To>::value, "Wrong specialization");
+};
+template <class _ToRef, class _FromRef>
+struct __is_invalid_lvalue_to_rvalue_cast<_ToRef&&, _FromRef&> {
+ using _RawFrom = __uncvref_t<_FromRef>;
+ using _RawTo = __uncvref_t<_ToRef>;
+ static const bool value = __lazy_and<
+ __lazy_not<is_function<_RawTo>>,
+ __lazy_or<
+ is_same<_RawFrom, _RawTo>,
+ is_base_of<_RawTo, _RawFrom>>
+ >::value;
+};
struct __is_constructible_helper
{
- template <class _Tp>
- static true_type __test_ref(_Tp);
- template <class>
- static false_type __test_ref(...);
+ template <class _To>
+ static void __eat(_To);
+
+ // This overload is needed to work around a Clang bug that disallows
+ // static_cast<T&&>(e) for non-reference-compatible types.
+ // Example: static_cast<int&&>(declval<double>());
+ // NOTE: The static_cast implementation below is required to support
+ // classes with explicit conversion operators.
+ template <class _To, class _From,
+ class = decltype(__eat<_To>(_VSTD::declval<_From>()))>
+ static true_type __test_cast(int);
+
+ template <class _To, class _From,
+ class = decltype(static_cast<_To>(_VSTD::declval<_From>()))>
+ static integral_constant<bool,
+ !__is_invalid_base_to_derived_cast<_To, _From>::value &&
+ !__is_invalid_lvalue_to_rvalue_cast<_To, _From>::value
+ > __test_cast(long);
+
+ template <class, class>
+ static false_type __test_cast(...);
template <class _Tp, class ..._Args,
class = decltype(_Tp(_VSTD::declval<_Args>()...))>
@@ -2961,24 +2999,27 @@ struct __libcpp_is_constructible<_Tp, _A
template <class _Tp, class _A0>
struct __libcpp_is_constructible<_Tp&, _A0>
: public decltype(__is_constructible_helper::
- __test_ref<_Tp&>(_VSTD::declval<_A0>()))
+ __test_cast<_Tp&, _A0>(0))
{};
template <class _Tp, class _A0>
struct __libcpp_is_constructible<_Tp&&, _A0>
: public decltype(__is_constructible_helper::
- __test_ref<_Tp&&>(_VSTD::declval<_A0>()))
+ __test_cast<_Tp&&, _A0>(0))
{};
-// is_constructible entry point
+#endif
+#if __has_feature(is_constructible)
+template <class _Tp, class ..._Args>
+struct _LIBCPP_TYPE_VIS_ONLY is_constructible
+ : public integral_constant<bool, __is_constructible(_Tp, _Args...)>
+ {};
+#elif !defined(_LIBCPP_CXX03_LANG)
template <class _Tp, class... _Args>
struct _LIBCPP_TYPE_VIS_ONLY is_constructible
: public __libcpp_is_constructible<_Tp, _Args...>::type {};
-
-
-#else // _LIBCPP_HAS_NO_VARIADICS
-
+#else
// template <class T> struct is_constructible0;
// main is_constructible0 test
@@ -3151,8 +3192,8 @@ struct __is_constructible2_imp<false, _A
: public false_type
{};
-#endif // _LIBCPP_HAS_NO_VARIADICS
-#endif // __has_feature(is_constructible)
+#endif // __has_feature(is_constructible)
+
#if _LIBCPP_STD_VER > 14 && !defined(_LIBCPP_HAS_NO_VARIABLE_TEMPLATES) && !defined(_LIBCPP_HAS_NO_VARIADICS)
template <class _Tp, class ..._Args> _LIBCPP_CONSTEXPR bool is_constructible_v
Modified: libcxx/trunk/test/std/utilities/meta/meta.unary/meta.unary.prop/is_constructible.pass.cpp
URL: http://llvm.org/viewvc/llvm-project/libcxx/trunk/test/std/utilities/meta/meta.unary/meta.unary.prop/is_constructible.pass.cpp?rev=285786&r1=285785&r2=285786&view=diff
==============================================================================
--- libcxx/trunk/test/std/utilities/meta/meta.unary/meta.unary.prop/is_constructible.pass.cpp (original)
+++ libcxx/trunk/test/std/utilities/meta/meta.unary/meta.unary.prop/is_constructible.pass.cpp Tue Nov 1 22:57:34 2016
@@ -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
}
More information about the cfe-commits
mailing list