diff options
Diffstat (limited to 'libcxx/test/std/utilities/tuple/tuple.tuple')
3 files changed, 278 insertions, 2 deletions
diff --git a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/PR22806_constrain_tuple_like_ctor.pass.cpp b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/PR22806_constrain_tuple_like_ctor.pass.cpp new file mode 100644 index 00000000000..4ddfb463385 --- /dev/null +++ b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/PR22806_constrain_tuple_like_ctor.pass.cpp @@ -0,0 +1,178 @@ +//===----------------------------------------------------------------------===// +// +// 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); + } +} diff --git a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/PR23256_constrain_UTypes_ctor.pass.cpp b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/PR23256_constrain_UTypes_ctor.pass.cpp new file mode 100644 index 00000000000..ed3cafadbf0 --- /dev/null +++ b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/PR23256_constrain_UTypes_ctor.pass.cpp @@ -0,0 +1,96 @@ +//===----------------------------------------------------------------------===// +// +// 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{})); + } +} diff --git a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/move.pass.cpp b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/move.pass.cpp index 0cda96846f7..1bd7d6d4e8a 100644 --- a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/move.pass.cpp +++ b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/move.pass.cpp @@ -29,8 +29,10 @@ struct ConstructsWithTupleLeaf 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() |