There are two main fixes in this patch.
First the constructor SFINAE was changed so that it's evaluated in two stages
where the first stage evaluates the "safe" SFINAE conditions and the second
evaluates the "dangerous" ones. The key is that the second stage is lazily
evaluated only if the first stage passes. This helps fix PR23256
(https://llvm.org/bugs/show_bug.cgi?id=23256).
The second fix is for PR22806 and LWG issue 2549. This fix applies
the suggested resolution to the LWG issue in order to prevent the construction
of dangling references. The SFINAE for this check is contained within
the _PreferTupleLikeConstructor alias template. The tuple-like constructors
are disabled whenever that trait returns false.
(https://llvm.org/bugs/show_bug.cgi?id=22806)
(http://cplusplus.github.io/LWG/lwg-active.html#2549)
llvm-svn: 266461
}
};
+template <bool _IsTuple, class _SizeTrait, size_t _Expected>
+struct __tuple_like_with_size_imp : false_type {};
+
+template <class _SizeTrait, size_t _Expected>
+struct __tuple_like_with_size_imp<true, _SizeTrait, _Expected>
+ : integral_constant<bool, _SizeTrait::value == _Expected> {};
+
+template <class _Tuple, size_t _ExpectedSize,
+ class _RawTuple = typename __uncvref<_Tuple>::type>
+using __tuple_like_with_size = __tuple_like_with_size_imp<
+ __tuple_like<_RawTuple>::value,
+ tuple_size<_RawTuple>, _ExpectedSize
+ >;
+
+
+struct _LIBCPP_TYPE_VIS __check_tuple_constructor_fail {
+ template <class ...>
+ static constexpr bool __enable_explicit() { return false; }
+ template <class ...>
+ static constexpr bool __enable_implicit() { return false; }
+};
+
template <class ..._Tp>
class _LIBCPP_TYPE_VIS_ONLY tuple
{
base base_;
+ template <class ..._Args>
+ struct _PackExpandsToThisTuple : false_type {};
+
+ template <class _Arg>
+ struct _PackExpandsToThisTuple<_Arg>
+ : is_same<typename __uncvref<_Arg>::type, tuple> {};
+
+ template <bool _MaybeEnable, class _Dummy = void>
+ struct _CheckArgsConstructor : __check_tuple_constructor_fail {};
+
+ template <class _Dummy>
+ struct _CheckArgsConstructor<true, _Dummy>
+ {
+ template <class ..._Args>
+ static constexpr bool __enable_explicit() {
+ return
+ __tuple_constructible<
+ tuple<_Args...>,
+ typename __make_tuple_types<tuple,
+ sizeof...(_Args) < sizeof...(_Tp) ?
+ sizeof...(_Args) :
+ sizeof...(_Tp)>::type
+ >::value &&
+ !__tuple_convertible<
+ tuple<_Args...>,
+ typename __make_tuple_types<tuple,
+ sizeof...(_Args) < sizeof...(_Tp) ?
+ sizeof...(_Args) :
+ sizeof...(_Tp)>::type
+ >::value &&
+ __all_default_constructible<
+ typename __make_tuple_types<tuple, sizeof...(_Tp),
+ sizeof...(_Args) < sizeof...(_Tp) ?
+ sizeof...(_Args) :
+ sizeof...(_Tp)>::type
+ >::value;
+ }
+
+ template <class ..._Args>
+ static constexpr bool __enable_implicit() {
+ return
+ __tuple_convertible<
+ tuple<_Args...>,
+ typename __make_tuple_types<tuple,
+ sizeof...(_Args) < sizeof...(_Tp) ?
+ sizeof...(_Args) :
+ sizeof...(_Tp)>::type
+ >::value &&
+ __all_default_constructible<
+ typename __make_tuple_types<tuple, sizeof...(_Tp),
+ sizeof...(_Args) < sizeof...(_Tp) ?
+ sizeof...(_Args) :
+ sizeof...(_Tp)>::type
+ >::value;
+ }
+ };
+
+ template <bool _MaybeEnable,
+ bool = sizeof...(_Tp) == 1,
+ class _Dummy = void>
+ struct _CheckTupleLikeConstructor : __check_tuple_constructor_fail {};
+
+ template <class _Dummy>
+ struct _CheckTupleLikeConstructor<true, false, _Dummy>
+ {
+ template <class _Tuple>
+ static constexpr bool __enable_implicit() {
+ return __tuple_convertible<_Tuple, tuple>::value;
+ }
+
+ template <class _Tuple>
+ static constexpr bool __enable_explicit() {
+ return __tuple_constructible<_Tuple, tuple>::value
+ && !__tuple_convertible<_Tuple, tuple>::value;
+ }
+ };
+
+ template <class _Dummy>
+ struct _CheckTupleLikeConstructor<true, true, _Dummy>
+ {
+ // This trait is used to disable the tuple-like constructor when
+ // the UTypes... constructor should be selected instead.
+ // See LWG issue #2549.
+ template <class _Tuple>
+ using _PreferTupleLikeConstructor = __lazy_or<
+ // Don't attempt the two checks below if the tuple we are given
+ // has the same type as this tuple.
+ is_same<typename __uncvref<_Tuple>::type, tuple>,
+ __lazy_and<
+ __lazy_not<is_constructible<_Tp..., _Tuple>>,
+ __lazy_not<is_convertible<_Tuple, _Tp...>>
+ >
+ >;
+
+ template <class _Tuple>
+ static constexpr bool __enable_implicit() {
+ return __lazy_and<
+ __tuple_convertible<_Tuple, tuple>,
+ _PreferTupleLikeConstructor<_Tuple>
+ >::value;
+ }
+
+ template <class _Tuple>
+ static constexpr bool __enable_explicit() {
+ return __lazy_and<
+ __tuple_constructible<_Tuple, tuple>,
+ _PreferTupleLikeConstructor<_Tuple>,
+ __lazy_not<__tuple_convertible<_Tuple, tuple>>
+ >::value;
+ }
+ };
+
template <size_t _Jp, class ..._Up> friend _LIBCPP_CONSTEXPR_AFTER_CXX11
typename tuple_element<_Jp, tuple<_Up...> >::type& get(tuple<_Up...>&) _NOEXCEPT;
template <size_t _Jp, class ..._Up> friend _LIBCPP_CONSTEXPR_AFTER_CXX11
template <class ..._Up,
typename enable_if
<
- sizeof...(_Up) <= sizeof...(_Tp) &&
- __tuple_convertible
- <
- tuple<_Up...>,
- typename __make_tuple_types<tuple,
- sizeof...(_Up) < sizeof...(_Tp) ?
- sizeof...(_Up) :
- sizeof...(_Tp)>::type
- >::value &&
- __all_default_constructible<
- typename __make_tuple_types<tuple, sizeof...(_Tp),
- sizeof...(_Up) < sizeof...(_Tp) ?
- sizeof...(_Up) :
- sizeof...(_Tp)>::type
- >::value,
+ _CheckArgsConstructor<
+ sizeof...(_Up) <= sizeof...(_Tp)
+ && !_PackExpandsToThisTuple<_Up...>::value
+ >::template __enable_implicit<_Up...>(),
bool
>::type = false
>
template <class ..._Up,
typename enable_if
<
- sizeof...(_Up) <= sizeof...(_Tp) &&
- __tuple_constructible
- <
- tuple<_Up...>,
- typename __make_tuple_types<tuple,
- sizeof...(_Up) < sizeof...(_Tp) ?
- sizeof...(_Up) :
- sizeof...(_Tp)>::type
- >::value &&
- !__tuple_convertible
- <
- tuple<_Up...>,
- typename __make_tuple_types<tuple,
- sizeof...(_Up) < sizeof...(_Tp) ?
- sizeof...(_Up) :
- sizeof...(_Tp)>::type
- >::value &&
- __all_default_constructible<
- typename __make_tuple_types<tuple, sizeof...(_Tp),
- sizeof...(_Up) < sizeof...(_Tp) ?
- sizeof...(_Up) :
- sizeof...(_Tp)>::type
- >::value,
+ _CheckArgsConstructor<
+ sizeof...(_Up) <= sizeof...(_Tp)
+ && !_PackExpandsToThisTuple<_Up...>::value
+ >::template __enable_explicit<_Up...>(),
bool
- >::type =false
+ >::type = false
>
_LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX11
explicit
template <class _Alloc, class ..._Up,
class = typename enable_if
<
- sizeof...(_Up) == sizeof...(_Tp) &&
- __tuple_convertible<tuple<_Up...>, tuple>::value
+ _CheckArgsConstructor<
+ sizeof...(_Up) == sizeof...(_Tp) &&
+ !_PackExpandsToThisTuple<_Up...>::value
+ >::template __enable_implicit<_Up...>()
>::type
>
_LIBCPP_INLINE_VISIBILITY
template <class _Tuple,
typename enable_if
<
- __tuple_convertible<_Tuple, tuple>::value,
+ _CheckTupleLikeConstructor<
+ __tuple_like_with_size<_Tuple, sizeof...(_Tp)>::value
+ && !_PackExpandsToThisTuple<_Tuple>::value
+ >::template __enable_implicit<_Tuple>(),
bool
>::type = false
>
template <class _Tuple,
typename enable_if
<
- __tuple_constructible<_Tuple, tuple>::value &&
- !__tuple_convertible<_Tuple, tuple>::value,
+ _CheckTupleLikeConstructor<
+ __tuple_like_with_size<_Tuple, sizeof...(_Tp)>::value
+ && !_PackExpandsToThisTuple<_Tuple>::value
+ >::template __enable_explicit<_Tuple>(),
bool
>::type = false
>
template <class _Alloc, class _Tuple,
class = typename enable_if
<
- __tuple_convertible<_Tuple, tuple>::value
+ _CheckTupleLikeConstructor<
+ __tuple_like_with_size<_Tuple, sizeof...(_Tp)>::value
+ >::template __enable_implicit<_Tuple>()
>::type
>
_LIBCPP_INLINE_VISIBILITY
--- /dev/null
+//===----------------------------------------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is dual licensed under the MIT and the University of Illinois Open
+// Source Licenses. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// UNSUPPORTED: c++98, c++03
+
+// <tuple>
+
+// template <class... Types> class tuple;
+
+// template <class TupleLike>
+// tuple(TupleLike&&);
+// template <class Alloc, class TupleLike>
+// tuple(std::allocator_arg_t, Alloc const&, TupleLike&&);
+
+// Check that the tuple-like ctors are properly disabled when the UTypes...
+// constructor should be selected. See PR22806.
+
+#include <tuple>
+#include <memory>
+#include <cassert>
+
+template <class Tp>
+using uncvref_t = typename std::remove_cv<typename std::remove_reference<Tp>::type>::type;
+
+template <class Tuple, class = uncvref_t<Tuple>>
+struct IsTuple : std::false_type {};
+
+template <class Tuple, class ...Args>
+struct IsTuple<Tuple, std::tuple<Args...>> : std::true_type {};
+
+struct ConstructibleFromTupleAndInt {
+ enum State { FromTuple, FromInt, Copied, Moved };
+ State state;
+
+ ConstructibleFromTupleAndInt(ConstructibleFromTupleAndInt const&) : state(Copied) {}
+ ConstructibleFromTupleAndInt(ConstructibleFromTupleAndInt &&) : state(Moved) {}
+
+ template <class Tuple, class = typename std::enable_if<IsTuple<Tuple>::value>::type>
+ explicit ConstructibleFromTupleAndInt(Tuple&&) : state(FromTuple) {}
+
+ explicit ConstructibleFromTupleAndInt(int) : state(FromInt) {}
+};
+
+struct ConvertibleFromTupleAndInt {
+ enum State { FromTuple, FromInt, Copied, Moved };
+ State state;
+
+ ConvertibleFromTupleAndInt(ConvertibleFromTupleAndInt const&) : state(Copied) {}
+ ConvertibleFromTupleAndInt(ConvertibleFromTupleAndInt &&) : state(Moved) {}
+
+ template <class Tuple, class = typename std::enable_if<IsTuple<Tuple>::value>::type>
+ ConvertibleFromTupleAndInt(Tuple&&) : state(FromTuple) {}
+
+ ConvertibleFromTupleAndInt(int) : state(FromInt) {}
+};
+
+struct ConstructibleFromInt {
+ enum State { FromInt, Copied, Moved };
+ State state;
+
+ ConstructibleFromInt(ConstructibleFromInt const&) : state(Copied) {}
+ ConstructibleFromInt(ConstructibleFromInt &&) : state(Moved) {}
+
+ explicit ConstructibleFromInt(int) : state(FromInt) {}
+};
+
+struct ConvertibleFromInt {
+ enum State { FromInt, Copied, Moved };
+ State state;
+
+ ConvertibleFromInt(ConvertibleFromInt const&) : state(Copied) {}
+ ConvertibleFromInt(ConvertibleFromInt &&) : state(Moved) {}
+ ConvertibleFromInt(int) : state(FromInt) {}
+};
+
+int main()
+{
+ // Test for the creation of dangling references when a tuple is used to
+ // store a reference to another tuple as its only element.
+ // Ex std::tuple<std::tuple<int>&&>.
+ // In this case the constructors 1) 'tuple(UTypes&&...)'
+ // and 2) 'tuple(TupleLike&&)' need to be manually disambiguated because
+ // when both #1 and #2 participate in partial ordering #2 will always
+ // be chosen over #1.
+ // See PR22806 and LWG issue #2549 for more information.
+ // (https://llvm.org/bugs/show_bug.cgi?id=22806)
+ using T = std::tuple<int>;
+ std::allocator<int> A;
+ { // rvalue reference
+ T t1(42);
+ std::tuple< T&& > t2(std::move(t1));
+ assert(&std::get<0>(t2) == &t1);
+ }
+ { // const lvalue reference
+ T t1(42);
+
+ std::tuple< T const & > t2(t1);
+ assert(&std::get<0>(t2) == &t1);
+
+ std::tuple< T const & > t3(static_cast<T const&>(t1));
+ assert(&std::get<0>(t3) == &t1);
+ }
+ { // lvalue reference
+ T t1(42);
+
+ std::tuple< T & > t2(t1);
+ assert(&std::get<0>(t2) == &t1);
+ }
+ { // const rvalue reference
+ T t1(42);
+
+ std::tuple< T const && > t2(std::move(t1));
+ assert(&std::get<0>(t2) == &t1);
+ }
+ { // rvalue reference via uses-allocator
+ T t1(42);
+ std::tuple< T&& > t2(std::allocator_arg, A, std::move(t1));
+ assert(&std::get<0>(t2) == &t1);
+ }
+ { // const lvalue reference via uses-allocator
+ T t1(42);
+
+ std::tuple< T const & > t2(std::allocator_arg, A, t1);
+ assert(&std::get<0>(t2) == &t1);
+
+ std::tuple< T const & > t3(std::allocator_arg, A, static_cast<T const&>(t1));
+ assert(&std::get<0>(t3) == &t1);
+ }
+ { // lvalue reference via uses-allocator
+ T t1(42);
+
+ std::tuple< T & > t2(std::allocator_arg, A, t1);
+ assert(&std::get<0>(t2) == &t1);
+ }
+ { // const rvalue reference via uses-allocator
+ T const t1(42);
+ std::tuple< T const && > t2(std::allocator_arg, A, std::move(t1));
+ assert(&std::get<0>(t2) == &t1);
+ }
+ // Test constructing a 1-tuple of the form tuple<UDT> from another 1-tuple
+ // 'tuple<T>' where UDT *can* be constructed from 'tuple<T>' In this case
+ // the 'tuple(UTypes...)' ctor should be choosen and 'UDT' constructed frow
+ // 'tuple<T>'.
+ {
+ using VT = ConstructibleFromTupleAndInt;
+ std::tuple<int> t1(42);
+ std::tuple<VT> t2(t1);
+ assert(std::get<0>(t2).state == VT::FromTuple);
+ }
+ {
+ using VT = ConvertibleFromTupleAndInt;
+ std::tuple<int> t1(42);
+ std::tuple<VT> t2 = {t1};
+ assert(std::get<0>(t2).state == VT::FromTuple);
+ }
+ // Test constructing a 1-tuple of the form tuple<UDT> from another 1-tuple
+ // 'tuple<T>' where UDT cannot be constructed from 'tuple<T>' but can
+ // be constructed from 'T'. In this case the tuple-like ctor should be
+ // chosen and 'UDT' constructed from 'T'
+ {
+ using VT = ConstructibleFromInt;
+ std::tuple<int> t1(42);
+ std::tuple<VT> t2(t1);
+ assert(std::get<0>(t2).state == VT::FromInt);
+ }
+ {
+ using VT = ConvertibleFromInt;
+ std::tuple<int> t1(42);
+ std::tuple<VT> t2 = {t1};
+ assert(std::get<0>(t2).state == VT::FromInt);
+ }
+}
--- /dev/null
+//===----------------------------------------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is dual licensed under the MIT and the University of Illinois Open
+// Source Licenses. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// UNSUPPORTED: c++98, c++03
+
+// <tuple>
+
+// template <class... Types> class tuple;
+
+// template <class ...UTypes>
+// EXPLICIT(...) tuple(UTypes&&...)
+
+// Check that the UTypes... ctor is properly disabled before evaluating any
+// SFINAE when the tuple-like copy/move ctor should *clearly* be selected
+// instead. This happens 'sizeof...(UTypes) == 1' and the first element of
+// 'UTypes...' is an instance of the tuple itself. See PR23256.
+
+#include <tuple>
+#include <memory>
+#include <type_traits>
+
+
+struct UnconstrainedCtor {
+ int value_;
+
+ UnconstrainedCtor() : value_(0) {}
+
+ // Blows up when instantiated for any type other than int. Because the ctor
+ // is constexpr it is instantiated by 'is_constructible' and 'is_convertible'
+ // for Clang based compilers. GCC does not instantiate the ctor body
+ // but it does instantiate the noexcept specifier and it will blow up there.
+ template <typename T>
+ constexpr UnconstrainedCtor(T value) noexcept(noexcept(value_ = value))
+ : value_(static_cast<int>(value))
+ {
+ static_assert(std::is_same<int, T>::value, "");
+ }
+};
+
+struct ExplicitUnconstrainedCtor {
+ int value_;
+
+ ExplicitUnconstrainedCtor() : value_(0) {}
+
+ template <typename T>
+ constexpr explicit ExplicitUnconstrainedCtor(T value)
+ noexcept(noexcept(value_ = value))
+ : value_(static_cast<int>(value))
+ {
+ static_assert(std::is_same<int, T>::value, "");
+ }
+
+};
+
+int main() {
+ typedef UnconstrainedCtor A;
+ typedef ExplicitUnconstrainedCtor ExplicitA;
+ {
+ static_assert(std::is_copy_constructible<std::tuple<A>>::value, "");
+ static_assert(std::is_move_constructible<std::tuple<A>>::value, "");
+ static_assert(std::is_copy_constructible<std::tuple<ExplicitA>>::value, "");
+ static_assert(std::is_move_constructible<std::tuple<ExplicitA>>::value, "");
+ }
+ {
+ static_assert(std::is_constructible<
+ std::tuple<A>,
+ std::allocator_arg_t, std::allocator<void>,
+ std::tuple<A> const&
+ >::value, "");
+ static_assert(std::is_constructible<
+ std::tuple<A>,
+ std::allocator_arg_t, std::allocator<void>,
+ std::tuple<A> &&
+ >::value, "");
+ static_assert(std::is_constructible<
+ std::tuple<ExplicitA>,
+ std::allocator_arg_t, std::allocator<void>,
+ std::tuple<ExplicitA> const&
+ >::value, "");
+ static_assert(std::is_constructible<
+ std::tuple<ExplicitA>,
+ std::allocator_arg_t, std::allocator<void>,
+ std::tuple<ExplicitA> &&
+ >::value, "");
+ }
+ {
+ std::tuple<A&&> t(std::forward_as_tuple(A{}));
+ std::tuple<ExplicitA&&> t2(std::forward_as_tuple(ExplicitA{}));
+ }
+}
ConstructsWithTupleLeaf(ConstructsWithTupleLeaf &&) {}
template <class T>
- ConstructsWithTupleLeaf(T t)
- { assert(false); }
+ ConstructsWithTupleLeaf(T t) {
+ static_assert(!std::is_same<T, T>::value,
+ "Constructor instantiated for type other than int");
+ }
};
int main()