[PATCH] [libcxx] Correct SFINAE version of is_convertible to match spec and avoid triggering unnecessary compiler diagnostics

Albert Wong ajwong at google.com
Sat Jun 28 02:24:51 PDT 2014


Hi mclow.lists,

The SFINAE forumulation used to test convertibility did not
correctly hide the conversion failure in ane expression that would
avoid a compiler diagnostic. Switched the idiom to add one more
layer of indirection and use enable_if which fixes the problem.

Also there were a few errors with const-volatile matching causing the
partial specializaitons that handled N2255 to not trigger correctly
for array types.

The following tests now compile in gcc-4.7.
  futures.async/async.pass.cpp
  map.cons/move_alloc.pass.cpp
  map.cons/move_assign.pass.cpp
  map.modifiers/insert_iter_rv.pass.cpp
  map.modifiers/insert_rv.pass.cpp
  meta.rel/is_convertible.pass.cpp
  multimap.cons/move_alloc.pass.cpp
  multimap.cons/move_assign.pass.cpp
  multimap.modifiers/insert_iter_rv.pass.cpp
  multimap.modifiers/insert_rv.pass.cpp
  pairs.pair/rv_pair_U_V.pass.cpp
  pairs.spec/make_pair.pass.cpp
  thread.once.callonce/call_once.pass.cpp
  unique.ptr.runtime.ctor/default02.pass.cpp
  unique.ptr.runtime.ctor/pointer02.pass.cpp
  unique.ptr.single.ctor/default02.pass.cpp
  unique.ptr.single.ctor/pointer02.pass.cpp
  unorder.map.modifiers/emplace_hint.pass.cpp
  unorder.map.modifiers/emplace.pass.cpp
  unorder.map.modifiers/insert_hint_rvalue.pass.cpp
  unorder.map.modifiers/insert_rvalue.pass.cpp
  unord.multimap.modifiers/emplace_hint.pass.cpp
  unord.multimap.modifiers/emplace.pass.cpp
  unord.multimap.modifiers/insert_hint_rvalue.pass.cpp
  unord.multimap.modifiers/insert_rvalue.pass.cpp

http://reviews.llvm.org/D4341

Files:
  include/type_traits

Index: include/type_traits
===================================================================
--- include/type_traits
+++ include/type_traits
@@ -832,14 +832,20 @@
 
 namespace __is_convertible_imp
 {
-template <class _Tp> char  __test(_Tp);
-template <class _Tp> __two __test(...);
+// Test taken directly from definition of is_convertible predicate in [meta.rel]p4.
 #ifndef _LIBCPP_HAS_NO_RVALUE_REFERENCES
-template <class _Tp> _Tp&& __source();
+template <class _Tp> typename add_rvalue_reference<_Tp>::type __create();
 #else
-template <class _Tp> typename remove_reference<_Tp>::type& __source();
+template <class _Tp> typename remove_reference<_Tp>::type& __create();
 #endif
 
+template <class _Tp> char helper(_Tp);
+
+template <class _Tp, class _Tf>
+typename enable_if<sizeof(helper<_Tp>(__create<_Tf>())) == 1, char>::type
+    __test(int);
+template <class _Tp, class _Tf> __two __test(...);
+
 template <class _Tp, bool _IsArray =    is_array<_Tp>::value,
                      bool _IsFunction = is_function<_Tp>::value,
                      bool _IsVoid =     is_void<_Tp>::value>
@@ -868,9 +874,9 @@
 struct __is_convertible
     : public integral_constant<bool,
 #ifndef _LIBCPP_HAS_NO_RVALUE_REFERENCES
-        sizeof(__is_convertible_imp::__test<_T2>(__is_convertible_imp::__source<_T1>())) == 1
+        sizeof(__is_convertible_imp::__test<_T2, _T1>(1)) == 1
 #else
-        sizeof(__is_convertible_imp::__test<_T2>(__is_convertible_imp::__source<_T1>())) == 1
+        sizeof(__is_convertible_imp::__test<_T2, _T1>(1)) == 1
          && !(!is_function<_T1>::value && !is_reference<_T1>::value && is_reference<_T2>::value
               && (!is_const<typename remove_reference<_T2>::type>::value
                   || is_volatile<typename remove_reference<_T2>::type>::value)
@@ -883,12 +889,12 @@
 
 template <class _T1, class _T2> struct __is_convertible<_T1, _T2, 1, 0> : false_type {};
 
-template <class _T1> struct __is_convertible<_T1, const _T1&, 1, 0> : true_type {};
+template <class _T1> struct __is_convertible<_T1, const typename remove_const<_T1>::type&, 1, 0> : true_type {};
 #ifndef _LIBCPP_HAS_NO_RVALUE_REFERENCES
 template <class _T1> struct __is_convertible<_T1, _T1&&, 1, 0> : true_type {};
-template <class _T1> struct __is_convertible<_T1, const _T1&&, 1, 0> : true_type {};
-template <class _T1> struct __is_convertible<_T1, volatile _T1&&, 1, 0> : true_type {};
-template <class _T1> struct __is_convertible<_T1, const volatile _T1&&, 1, 0> : true_type {};
+template <class _T1> struct __is_convertible<_T1, const typename remove_const<_T1>::type&&, 1, 0> : true_type {};
+template <class _T1> struct __is_convertible<_T1, volatile typename remove_volatile<_T1>::type&&, 1, 0> : true_type {};
+template <class _T1> struct __is_convertible<_T1, const volatile typename remove_cv<_T1>::type&&, 1, 0> : true_type {};
 #endif  // _LIBCPP_HAS_NO_RVALUE_REFERENCES
 
 template <class _T1, class _T2> struct __is_convertible<_T1, _T2*, 1, 0>
@@ -913,18 +919,22 @@
 template <class _T1>            struct __is_convertible<_T1, _T1*volatile, 2, 0>       : public true_type {};
 template <class _T1>            struct __is_convertible<_T1, _T1*const volatile, 2, 0> : public true_type {};
 
+// Per N2255 on is_convertible, void -> !void is not convertible.
 template <class _T1, class _T2> struct __is_convertible<_T1, _T2, 3, 0> : public false_type {};
 
+// Per N2255 on is_convertible, * -> array is not converitble.
 template <class _T1, class _T2> struct __is_convertible<_T1, _T2, 0, 1> : public false_type {};
 template <class _T1, class _T2> struct __is_convertible<_T1, _T2, 1, 1> : public false_type {};
 template <class _T1, class _T2> struct __is_convertible<_T1, _T2, 2, 1> : public false_type {};
 template <class _T1, class _T2> struct __is_convertible<_T1, _T2, 3, 1> : public false_type {};
 
+// Per N2255 on is_convertible, * -> function is not converitble.
 template <class _T1, class _T2> struct __is_convertible<_T1, _T2, 0, 2> : public false_type {};
 template <class _T1, class _T2> struct __is_convertible<_T1, _T2, 1, 2> : public false_type {};
 template <class _T1, class _T2> struct __is_convertible<_T1, _T2, 2, 2> : public false_type {};
 template <class _T1, class _T2> struct __is_convertible<_T1, _T2, 3, 2> : public false_type {};
 
+// Per N2255 on is_convertible, only void -> void is convertible.
 template <class _T1, class _T2> struct __is_convertible<_T1, _T2, 0, 3> : public false_type {};
 template <class _T1, class _T2> struct __is_convertible<_T1, _T2, 1, 3> : public false_type {};
 template <class _T1, class _T2> struct __is_convertible<_T1, _T2, 2, 3> : public false_type {};
-------------- next part --------------
A non-text attachment was scrubbed...
Name: D4341.10962.patch
Type: text/x-patch
Size: 4667 bytes
Desc: not available
URL: <http://lists.llvm.org/pipermail/cfe-commits/attachments/20140628/e78826fd/attachment.bin>


More information about the cfe-commits mailing list