diff options
author | Dimitry Andric <dim@FreeBSD.org> | 2016-07-23 20:47:26 +0000 |
---|---|---|
committer | Dimitry Andric <dim@FreeBSD.org> | 2016-07-23 20:47:26 +0000 |
commit | 51072bd6bf79ef2bc6a922079bff57c31c1effbc (patch) | |
tree | 91a2effbc9e6f80bdbbf9eb70e06c51ad0867ea0 /test/std/utilities/tuple/tuple.tuple/tuple.cnstr | |
parent | bb5e33f003797b67974a8893f7f2930fc51b8210 (diff) |
Notes
Diffstat (limited to 'test/std/utilities/tuple/tuple.tuple/tuple.cnstr')
21 files changed, 820 insertions, 68 deletions
diff --git a/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/PR22806_constrain_tuple_like_ctor.pass.cpp b/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/PR22806_constrain_tuple_like_ctor.pass.cpp new file mode 100644 index 000000000000..4ddfb463385c --- /dev/null +++ b/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/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/PR23256_constrain_UTypes_ctor.pass.cpp b/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/PR23256_constrain_UTypes_ctor.pass.cpp new file mode 100644 index 000000000000..ed3cafadbf08 --- /dev/null +++ b/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/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/PR27684_contains_ref_to_incomplete_type.pass.cpp b/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/PR27684_contains_ref_to_incomplete_type.pass.cpp new file mode 100644 index 000000000000..c8b722f836c1 --- /dev/null +++ b/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/PR27684_contains_ref_to_incomplete_type.pass.cpp @@ -0,0 +1,51 @@ +//===----------------------------------------------------------------------===// +// +// 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 Alloc> tuple(allocator_arg_t, Alloc const&) + +// Libc++ has to deduce the 'allocator_arg_t' parameter for this constructor +// as 'AllocArgT'. Previously libc++ has tried to support tags derived from +// 'allocator_arg_t' by using 'is_base_of<AllocArgT, allocator_arg_t>'. +// However this breaks whenever a 2-tuple contains a reference to an incomplete +// type as its first parameter. See PR27684. + +#include <tuple> +#include <cassert> + +struct IncompleteType; +extern IncompleteType inc1; +extern IncompleteType inc2; +IncompleteType const& cinc1 = inc1; +IncompleteType const& cinc2 = inc2; + +int main() { + using IT = IncompleteType; + { // try calling tuple(Tp const&...) + using Tup = std::tuple<const IT&, const IT&>; + Tup t(cinc1, cinc2); + assert(&std::get<0>(t) == &inc1); + assert(&std::get<1>(t) == &inc2); + } + { // try calling tuple(Up&&...) + using Tup = std::tuple<const IT&, const IT&>; + Tup t(inc1, inc2); + assert(&std::get<0>(t) == &inc1); + assert(&std::get<1>(t) == &inc2); + } +} + +struct IncompleteType {}; +IncompleteType inc1; +IncompleteType inc2; diff --git a/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/UTypes.pass.cpp b/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/UTypes.pass.cpp index 817cc8f10990..6ab303c735be 100644 --- a/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/UTypes.pass.cpp +++ b/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/UTypes.pass.cpp @@ -82,6 +82,8 @@ void test_default_constructible_extension_sfinae() MoveOnly, Tuple, MoveOnly, MoveOnly >::value, ""); } + // testing extensions +#ifdef _LIBCPP_VERSION { typedef std::tuple<MoveOnly, int> Tuple; typedef std::tuple<MoveOnly, Tuple, MoveOnly, MoveOnly> NestedTuple; @@ -96,6 +98,7 @@ void test_default_constructible_extension_sfinae() MoveOnly, Tuple, MoveOnly, MoveOnly >::value, ""); } +#endif } int main() @@ -118,6 +121,7 @@ int main() assert(std::get<2>(t) == 2); } // extensions +#ifdef _LIBCPP_VERSION { std::tuple<MoveOnly, MoveOnly, MoveOnly> t(MoveOnly(0), MoveOnly(1)); @@ -131,7 +135,8 @@ int main() assert(std::get<1>(t) == MoveOnly()); assert(std::get<2>(t) == MoveOnly()); } -#if _LIBCPP_STD_VER > 11 +#endif +#if TEST_STD_VER > 11 { constexpr std::tuple<Empty> t0{Empty()}; } diff --git a/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/alloc.pass.cpp b/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/alloc.pass.cpp index 39776822cbda..4da5fc7f8397 100644 --- a/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/alloc.pass.cpp +++ b/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/alloc.pass.cpp @@ -7,6 +7,8 @@ // //===----------------------------------------------------------------------===// +// UNSUPPORTED: c++98, c++03 + // <tuple> // template <class... Types> class tuple; @@ -14,7 +16,9 @@ // template <class Alloc> // tuple(allocator_arg_t, const Alloc& a); -// UNSUPPORTED: c++98, c++03 +// NOTE: this constructor does not currently support tags derived from +// allocator_arg_t because libc++ has to deduce the parameter as a template +// argument. See PR27684 (https://llvm.org/bugs/show_bug.cgi?id=27684) #include <tuple> #include <cassert> @@ -24,6 +28,18 @@ #include "../alloc_first.h" #include "../alloc_last.h" +template <class T = void> +struct NonDefaultConstructible { + constexpr NonDefaultConstructible() { + static_assert(!std::is_same<T, T>::value, "Default Ctor instantiated"); + } + + explicit constexpr NonDefaultConstructible(int) {} +}; + + +struct DerivedFromAllocArgT : std::allocator_arg_t {}; + int main() { { @@ -78,4 +94,14 @@ int main() assert(!alloc_last::allocator_constructed); assert(std::get<2>(t) == alloc_last()); } + { + // Test that the uses-allocator default constructor does not evaluate + // it's SFINAE when it otherwise shouldn't be selected. Do this by + // using 'NonDefaultConstructible' which will cause a compile error + // if std::is_default_constructible is evaluated on it. + using T = NonDefaultConstructible<>; + T v(42); + std::tuple<T, T> t(v, v); + std::tuple<T, T> t2(42, 42); + } } diff --git a/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/alloc_UTypes.pass.cpp b/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/alloc_UTypes.pass.cpp index 3929965cd273..e174e9b321b8 100644 --- a/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/alloc_UTypes.pass.cpp +++ b/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/alloc_UTypes.pass.cpp @@ -24,16 +24,29 @@ #include "../alloc_first.h" #include "../alloc_last.h" -struct NoDefault { NoDefault() = delete; }; +template <class T = void> +struct DefaultCtorBlowsUp { + constexpr DefaultCtorBlowsUp() { + static_assert(!std::is_same<T, T>::value, "Default Ctor instantiated"); + } -// Make sure the _Up... constructor SFINAEs out when the types that -// are not explicitly initialized are not all default constructible. -// Otherwise, std::is_constructible would return true but instantiating -// the constructor would fail. -void test_default_constructible_extension_sfinae() + explicit constexpr DefaultCtorBlowsUp(int x) : value(x) {} + + int value; +}; + + +struct DerivedFromAllocArgT : std::allocator_arg_t {}; + + +// Make sure the _Up... constructor SFINAEs out when the number of initializers +// is less that the number of elements in the tuple. Previously libc++ would +// offer these constructers as an extension but they broke conforming code. +void test_uses_allocator_sfinae_evaluation() { + using BadDefault = DefaultCtorBlowsUp<>; { - typedef std::tuple<MoveOnly, NoDefault> Tuple; + typedef std::tuple<MoveOnly, MoveOnly, BadDefault> Tuple; static_assert(!std::is_constructible< Tuple, @@ -42,11 +55,11 @@ void test_default_constructible_extension_sfinae() static_assert(std::is_constructible< Tuple, - std::allocator_arg_t, A1<int>, MoveOnly, NoDefault + std::allocator_arg_t, A1<int>, MoveOnly, MoveOnly, BadDefault >::value, ""); } { - typedef std::tuple<MoveOnly, MoveOnly, NoDefault> Tuple; + typedef std::tuple<MoveOnly, MoveOnly, BadDefault, BadDefault> Tuple; static_assert(!std::is_constructible< Tuple, @@ -55,53 +68,44 @@ void test_default_constructible_extension_sfinae() static_assert(std::is_constructible< Tuple, - std::allocator_arg_t, A1<int>, MoveOnly, MoveOnly, NoDefault - >::value, ""); - } - { - // Same idea as above but with a nested tuple - typedef std::tuple<MoveOnly, NoDefault> Tuple; - typedef std::tuple<MoveOnly, Tuple, MoveOnly, MoveOnly> NestedTuple; - - static_assert(!std::is_constructible< - NestedTuple, - std::allocator_arg_t, A1<int>, MoveOnly, MoveOnly, MoveOnly, MoveOnly - >::value, ""); - - static_assert(std::is_constructible< - NestedTuple, - std::allocator_arg_t, A1<int>, MoveOnly, Tuple, MoveOnly, MoveOnly - >::value, ""); - } - { - typedef std::tuple<MoveOnly, int> Tuple; - typedef std::tuple<MoveOnly, Tuple, MoveOnly, MoveOnly> NestedTuple; - - static_assert(std::is_constructible< - NestedTuple, - std::allocator_arg_t, A1<int>, MoveOnly, MoveOnly, MoveOnly, MoveOnly - >::value, ""); - - static_assert(std::is_constructible< - NestedTuple, - std::allocator_arg_t, A1<int>, MoveOnly, Tuple, MoveOnly, MoveOnly + std::allocator_arg_t, A1<int>, MoveOnly, MoveOnly, BadDefault, BadDefault >::value, ""); } } +struct Explicit { + int value; + explicit Explicit(int x) : value(x) {} +}; + int main() { { + std::tuple<Explicit> t{std::allocator_arg, std::allocator<void>{}, 42}; + assert(std::get<0>(t).value == 42); + } + { std::tuple<MoveOnly> t(std::allocator_arg, A1<int>(), MoveOnly(0)); assert(std::get<0>(t) == 0); } { + using T = DefaultCtorBlowsUp<>; + std::tuple<T> t(std::allocator_arg, A1<int>(), T(42)); + assert(std::get<0>(t).value == 42); + } + { std::tuple<MoveOnly, MoveOnly> t(std::allocator_arg, A1<int>(), MoveOnly(0), MoveOnly(1)); assert(std::get<0>(t) == 0); assert(std::get<1>(t) == 1); } { + using T = DefaultCtorBlowsUp<>; + std::tuple<T, T> t(std::allocator_arg, A1<int>(), T(42), T(43)); + assert(std::get<0>(t).value == 42); + assert(std::get<1>(t).value == 43); + } + { std::tuple<MoveOnly, MoveOnly, MoveOnly> t(std::allocator_arg, A1<int>(), MoveOnly(0), 1, 2); @@ -110,6 +114,13 @@ int main() assert(std::get<2>(t) == 2); } { + using T = DefaultCtorBlowsUp<>; + std::tuple<T, T, T> t(std::allocator_arg, A1<int>(), T(1), T(2), T(3)); + assert(std::get<0>(t).value == 1); + assert(std::get<1>(t).value == 2); + assert(std::get<2>(t).value == 3); + } + { alloc_first::allocator_constructed = false; alloc_last::allocator_constructed = false; std::tuple<int, alloc_first, alloc_last> t(std::allocator_arg, @@ -120,22 +131,22 @@ int main() assert(alloc_last::allocator_constructed); assert(std::get<2>(t) == alloc_last(3)); } - // extensions - { - std::tuple<MoveOnly, MoveOnly, MoveOnly> t(std::allocator_arg, A1<int>(), - 0, 1); - assert(std::get<0>(t) == 0); - assert(std::get<1>(t) == 1); - assert(std::get<2>(t) == MoveOnly()); - } { - std::tuple<MoveOnly, MoveOnly, MoveOnly> t(std::allocator_arg, A1<int>(), - 0); - assert(std::get<0>(t) == 0); - assert(std::get<1>(t) == MoveOnly()); - assert(std::get<2>(t) == MoveOnly()); + // Check that uses-allocator construction is still selected when + // given a tag type that derives from allocator_arg_t. + DerivedFromAllocArgT tag; + alloc_first::allocator_constructed = false; + alloc_last::allocator_constructed = false; + std::tuple<int, alloc_first, alloc_last> t(tag, + A1<int>(5), 1, 2, 3); + assert(std::get<0>(t) == 1); + assert(alloc_first::allocator_constructed); + assert(std::get<1>(t) == alloc_first(2)); + assert(alloc_last::allocator_constructed); + assert(std::get<2>(t) == alloc_last(3)); } - // Check that SFINAE is properly applied with the default reduced arity - // constructor extensions. - test_default_constructible_extension_sfinae(); + // Stress test the SFINAE on the uses-allocator constructors and + // ensure that the "reduced-arity-initialization" extension is not offered + // for these constructors. + test_uses_allocator_sfinae_evaluation(); } diff --git a/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/alloc_const_Types.fail.cpp b/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/alloc_const_Types.fail.cpp new file mode 100644 index 000000000000..b28ad6dab5ab --- /dev/null +++ b/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/alloc_const_Types.fail.cpp @@ -0,0 +1,43 @@ +//===----------------------------------------------------------------------===// +// +// 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. +// +//===----------------------------------------------------------------------===// + +// <tuple> + +// template <class... Types> class tuple; + +// template <class Alloc> +// EXPLICIT tuple(allocator_arg_t, const Alloc& a, const Types&...); + +// UNSUPPORTED: c++98, c++03 + +#include <tuple> +#include <memory> +#include <cassert> + +struct ExplicitCopy { + explicit ExplicitCopy(ExplicitCopy const&) {} + explicit ExplicitCopy(int) {} +}; + +std::tuple<ExplicitCopy> const_explicit_copy_test() { + const ExplicitCopy e(42); + return {std::allocator_arg, std::allocator<void>{}, e}; + // expected-error@-1 {{chosen constructor is explicit in copy-initialization}} +} + +std::tuple<ExplicitCopy> non_const_explicity_copy_test() { + ExplicitCopy e(42); + return {std::allocator_arg, std::allocator<void>{}, e}; + // expected-error@-1 {{chosen constructor is explicit in copy-initialization}} +} +int main() +{ + const_explicit_copy_test(); + non_const_explicity_copy_test(); +} diff --git a/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/alloc_const_Types.pass.cpp b/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/alloc_const_Types.pass.cpp index 0f68926376f2..73d53a4c0e26 100644 --- a/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/alloc_const_Types.pass.cpp +++ b/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/alloc_const_Types.pass.cpp @@ -17,15 +17,37 @@ // UNSUPPORTED: c++98, c++03 #include <tuple> +#include <memory> #include <cassert> #include "allocators.h" #include "../alloc_first.h" #include "../alloc_last.h" +struct ImplicitCopy { + explicit ImplicitCopy(int) {} + ImplicitCopy(ImplicitCopy const&) {} +}; + +// Test that tuple(std::allocator_arg, Alloc, Types const&...) allows implicit +// copy conversions in return value expressions. +std::tuple<ImplicitCopy> testImplicitCopy1() { + ImplicitCopy i(42); + return {std::allocator_arg, std::allocator<void>{}, i}; +} + +std::tuple<ImplicitCopy> testImplicitCopy2() { + const ImplicitCopy i(42); + return {std::allocator_arg, std::allocator<void>{}, i}; +} + int main() { { + // check that the literal '0' can implicitly initialize a stored pointer. + std::tuple<int*> t = {std::allocator_arg, std::allocator<void>{}, 0}; + } + { std::tuple<int> t(std::allocator_arg, A1<int>(), 3); assert(std::get<0>(t) == 3); } diff --git a/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/alloc_convert_copy.fail.cpp b/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/alloc_convert_copy.fail.cpp new file mode 100644 index 000000000000..ccf08833b537 --- /dev/null +++ b/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/alloc_convert_copy.fail.cpp @@ -0,0 +1,43 @@ +//===----------------------------------------------------------------------===// +// +// 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. +// +//===----------------------------------------------------------------------===// + +// <tuple> + +// template <class... Types> class tuple; + +// template <class Alloc, class ...UTypes> +// tuple(allocator_arg_t, const Alloc& a, tuple<UTypes...> const&); + +// UNSUPPORTED: c++98, c++03 + +#include <tuple> +#include <memory> + +struct ExplicitCopy { + explicit ExplicitCopy(int) {} + explicit ExplicitCopy(ExplicitCopy const&) {} + +}; + +std::tuple<ExplicitCopy> const_explicit_copy_test() { + const std::tuple<int> t1(42); + return {std::allocator_arg, std::allocator<void>{}, t1}; + // expected-error@-1 {{chosen constructor is explicit in copy-initialization}} +} + +std::tuple<ExplicitCopy> non_const_explicit_copy_test() { + std::tuple<int> t1(42); + return {std::allocator_arg, std::allocator<void>{}, t1}; + // expected-error@-1 {{chosen constructor is explicit in copy-initialization}} +} + +int main() +{ + +} diff --git a/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/alloc_convert_copy.pass.cpp b/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/alloc_convert_copy.pass.cpp index 8acfde7a98eb..36d9f32879cb 100644 --- a/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/alloc_convert_copy.pass.cpp +++ b/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/alloc_convert_copy.pass.cpp @@ -17,12 +17,23 @@ // UNSUPPORTED: c++98, c++03 #include <tuple> +#include <memory> #include <cassert> #include "allocators.h" #include "../alloc_first.h" #include "../alloc_last.h" +struct Explicit { + int value; + explicit Explicit(int x) : value(x) {} +}; + +struct Implicit { + int value; + Implicit(int x) : value(x) {} +}; + int main() { { @@ -66,4 +77,14 @@ int main() assert(std::get<1>(t1) == 2); assert(std::get<2>(t1) == 3); } + { + const std::tuple<int> t1(42); + std::tuple<Explicit> t2{std::allocator_arg, std::allocator<void>{}, t1}; + assert(std::get<0>(t2).value == 42); + } + { + const std::tuple<int> t1(42); + std::tuple<Implicit> t2 = {std::allocator_arg, std::allocator<void>{}, t1}; + assert(std::get<0>(t2).value == 42); + } } diff --git a/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/alloc_convert_move.fail.cpp b/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/alloc_convert_move.fail.cpp new file mode 100644 index 000000000000..d3539cebf950 --- /dev/null +++ b/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/alloc_convert_move.fail.cpp @@ -0,0 +1,36 @@ +//===----------------------------------------------------------------------===// +// +// 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. +// +//===----------------------------------------------------------------------===// + +// <tuple> + +// template <class... Types> class tuple; + +// template <class Alloc, class ...UTypes> +// tuple(allocator_arg_t, const Alloc& a, tuple<UTypes...>&&); + +// UNSUPPORTED: c++98, c++03 + +#include <tuple> +#include <memory> + +struct ExplicitCopy { + explicit ExplicitCopy(int) {} + explicit ExplicitCopy(ExplicitCopy const&) {} +}; + +std::tuple<ExplicitCopy> explicit_move_test() { + std::tuple<int> t1(42); + return {std::allocator_arg, std::allocator<void>{}, std::move(t1)}; + // expected-error@-1 {{chosen constructor is explicit in copy-initialization}} +} + +int main() +{ + +} diff --git a/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/alloc_convert_move.pass.cpp b/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/alloc_convert_move.pass.cpp index c862d3b64d56..d3a6add5da6a 100644 --- a/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/alloc_convert_move.pass.cpp +++ b/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/alloc_convert_move.pass.cpp @@ -40,6 +40,16 @@ struct D explicit D(int i) : B(i) {} }; +struct Explicit { + int value; + explicit Explicit(int x) : value(x) {} +}; + +struct Implicit { + int value; + Implicit(int x) : value(x) {} +}; + int main() { { @@ -81,4 +91,14 @@ int main() assert(std::get<1>(t1) == 2); assert(std::get<2>(t1)->id_ == 3); } + { + std::tuple<int> t1(42); + std::tuple<Explicit> t2{std::allocator_arg, std::allocator<void>{}, std::move(t1)}; + assert(std::get<0>(t2).value == 42); + } + { + std::tuple<int> t1(42); + std::tuple<Implicit> t2 = {std::allocator_arg, std::allocator<void>{}, std::move(t1)}; + assert(std::get<0>(t2).value == 42); + } } diff --git a/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/alloc_copy.pass.cpp b/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/alloc_copy.pass.cpp index 14e127e59ba9..7c9f60cbf901 100644 --- a/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/alloc_copy.pass.cpp +++ b/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/alloc_copy.pass.cpp @@ -52,6 +52,8 @@ int main() assert(alloc_last::allocator_constructed); assert(std::get<0>(t) == 2); } +// testing extensions +#ifdef _LIBCPP_VERSION { typedef std::tuple<alloc_first, alloc_last> T; T t0(2, 3); @@ -75,4 +77,5 @@ int main() assert(std::get<1>(t) == 2); assert(std::get<2>(t) == 3); } +#endif } diff --git a/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/alloc_move.pass.cpp b/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/alloc_move.pass.cpp index 54d3f7ee0c07..a3e1a9de6195 100644 --- a/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/alloc_move.pass.cpp +++ b/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/alloc_move.pass.cpp @@ -53,6 +53,8 @@ int main() assert(alloc_last::allocator_constructed); assert(std::get<0>(t) == 1); } +// testing extensions +#ifdef _LIBCPP_VERSION { typedef std::tuple<MoveOnly, alloc_first> T; T t0(0 ,1); @@ -74,4 +76,5 @@ int main() assert(std::get<1>(t) == 2); assert(std::get<2>(t) == 3); } +#endif } diff --git a/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/const_Types.fail.cpp b/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/const_Types.fail.cpp index 00e2af265b36..b72f0fc2efec 100644 --- a/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/const_Types.fail.cpp +++ b/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/const_Types.fail.cpp @@ -19,9 +19,30 @@ #include <string> #include <cassert> +struct ExplicitCopy { + ExplicitCopy(int) {} + explicit ExplicitCopy(ExplicitCopy const&) {} +}; + +std::tuple<ExplicitCopy> const_explicit_copy() { + const ExplicitCopy e(42); + return {e}; + // expected-error@-1 {{chosen constructor is explicit in copy-initialization}} +} + + +std::tuple<ExplicitCopy> non_const_explicit_copy() { + ExplicitCopy e(42); + return {e}; + // expected-error@-1 {{chosen constructor is explicit in copy-initialization}} +} + +std::tuple<ExplicitCopy> const_explicit_copy_no_brace() { + const ExplicitCopy e(42); + return e; + // expected-error@-1 {{no viable conversion}} +} + int main() { - { - std::tuple<int*> t = 0; - } } diff --git a/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/const_Types.pass.cpp b/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/const_Types.pass.cpp index bbadf8de1600..367f19e5d8dd 100644 --- a/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/const_Types.pass.cpp +++ b/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/const_Types.pass.cpp @@ -53,13 +53,40 @@ struct NoValueCtorEmpty { static_assert(never<T>::value, "This should not be instantiated"); } }; + +struct ImplicitCopy { + explicit ImplicitCopy(int) {} + ImplicitCopy(ImplicitCopy const&) {} +}; + +// Test that tuple(std::allocator_arg, Alloc, Types const&...) allows implicit +// copy conversions in return value expressions. +std::tuple<ImplicitCopy> testImplicitCopy1() { + ImplicitCopy i(42); + return {i}; +} + +std::tuple<ImplicitCopy> testImplicitCopy2() { + const ImplicitCopy i(42); + return {i}; +} + +std::tuple<ImplicitCopy> testImplicitCopy3() { + const ImplicitCopy i(42); + return i; +} + int main() { { + // check that the literal '0' can implicitly initialize a stored pointer. + std::tuple<int*> t = 0; + } + { std::tuple<int> t(2); assert(std::get<0>(t) == 2); } -#if _LIBCPP_STD_VER > 11 +#if _LIBCPP_STD_VER > 11 { constexpr std::tuple<int> t(2); static_assert(std::get<0>(t) == 2, ""); @@ -74,7 +101,7 @@ int main() assert(std::get<0>(t) == 2); assert(std::get<1>(t) == nullptr); } -#if _LIBCPP_STD_VER > 11 +#if _LIBCPP_STD_VER > 11 { constexpr std::tuple<int, char*> t(2, nullptr); static_assert(std::get<0>(t) == 2, ""); @@ -109,7 +136,8 @@ int main() assert(std::get<2>(t) == 2); assert(std::get<3>(t) == 3); } - // extensions +// extensions +#ifdef _LIBCPP_VERSION { std::tuple<int, char*, std::string> t(2); assert(std::get<0>(t) == 2); @@ -129,4 +157,5 @@ int main() assert(std::get<2>(t) == "text"); assert(std::get<3>(t) == 0.0); } +#endif } diff --git a/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/const_pair.pass.cpp b/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/const_pair.pass.cpp index 740b6589e511..d6d489fd0ea4 100644 --- a/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/const_pair.pass.cpp +++ b/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/const_pair.pass.cpp @@ -29,7 +29,7 @@ int main() assert(std::get<0>(t1) == 2); assert(std::get<1>(t1) == short('a')); } -#if _LIBCPP_STD_VER > 11 +#if _LIBCPP_STD_VER > 11 { typedef std::pair<double, char> P0; typedef std::tuple<int, short> T1; diff --git a/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/convert_copy.pass.cpp b/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/convert_copy.pass.cpp index 5ad4f9227f48..b7fa2e3a03cc 100644 --- a/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/convert_copy.pass.cpp +++ b/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/convert_copy.pass.cpp @@ -20,6 +20,16 @@ #include <string> #include <cassert> +struct Explicit { + int value; + explicit Explicit(int x) : value(x) {} +}; + +struct Implicit { + int value; + Implicit(int x) : value(x) {} +}; + struct B { int id_; @@ -33,7 +43,7 @@ struct D explicit D(int i) : B(i) {} }; -#if _LIBCPP_STD_VER > 11 +#if _LIBCPP_STD_VER > 11 struct A { @@ -62,7 +72,7 @@ int main() T1 t1 = t0; assert(std::get<0>(t1) == 2); } -#if _LIBCPP_STD_VER > 11 +#if _LIBCPP_STD_VER > 11 { typedef std::tuple<double> T0; typedef std::tuple<A> T1; @@ -115,4 +125,14 @@ int main() assert(std::get<1>(t1) == int('a')); assert(std::get<2>(t1).id_ == 3); } + { + const std::tuple<int> t1(42); + std::tuple<Explicit> t2(t1); + assert(std::get<0>(t2).value == 42); + } + { + const std::tuple<int> t1(42); + std::tuple<Implicit> t2 = t1; + assert(std::get<0>(t2).value == 42); + } } diff --git a/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/convert_move.pass.cpp b/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/convert_move.pass.cpp index 3a6abd3a95af..8423f5d0145f 100644 --- a/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/convert_move.pass.cpp +++ b/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/convert_move.pass.cpp @@ -20,6 +20,16 @@ #include <memory> #include <cassert> +struct Explicit { + int value; + explicit Explicit(int x) : value(x) {} +}; + +struct Implicit { + int value; + Implicit(int x) : value(x) {} +}; + struct B { int id_; @@ -81,4 +91,14 @@ int main() assert(std::get<1>(t1) == int('a')); assert(std::get<2>(t1)->id_ == 3); } + { + std::tuple<int> t1(42); + std::tuple<Explicit> t2(std::move(t1)); + assert(std::get<0>(t2).value == 42); + } + { + std::tuple<int> t1(42); + std::tuple<Implicit> t2 = std::move(t1); + assert(std::get<0>(t2).value == 42); + } } diff --git a/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/move.pass.cpp b/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/move.pass.cpp index 0cda96846f71..1bd7d6d4e8a8 100644 --- a/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/move.pass.cpp +++ b/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() diff --git a/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/test_lazy_sfinae.pass.cpp b/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/test_lazy_sfinae.pass.cpp new file mode 100644 index 000000000000..76f7e794a8e8 --- /dev/null +++ b/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/test_lazy_sfinae.pass.cpp @@ -0,0 +1,102 @@ +//===----------------------------------------------------------------------===// +// +// 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. +// +//===----------------------------------------------------------------------===// + +// <tuple> + +// template <class... Types> class tuple; + +// UNSUPPORTED: c++98, c++03 + +#include <tuple> +#include <utility> +#include <cassert> + +template <class ConstructFrom> +struct ConstructibleFromT { + ConstructibleFromT() = default; + ConstructibleFromT(ConstructFrom v) : value(v) {} + ConstructFrom value; +}; + +template <class AssertOn> +struct CtorAssertsT { + bool defaulted; + CtorAssertsT() : defaulted(true) {} + template <class T> + constexpr CtorAssertsT(T) : defaulted(false) { + static_assert(!std::is_same<T, AssertOn>::value, ""); + } +}; + +template <class AllowT, class AssertT> +struct AllowAssertT { + AllowAssertT() = default; + AllowAssertT(AllowT) {} + template <class U> + constexpr AllowAssertT(U) { + static_assert(!std::is_same<U, AssertT>::value, ""); + } +}; + +// Construct a tuple<T1, T2> from pair<int, int> where T1 and T2 +// are not constructible from ints but T1 is constructible from std::pair. +// This considers the following constructors: +// (1) tuple(TupleLike) -> checks is_constructible<Tn, int> +// (2) tuple(UTypes...) -> checks is_constructible<T1, pair<int, int>> +// and is_default_constructible<T2> +// The point of this test is to ensure that the consideration of (1) +// short circuits before evaluating is_constructible<T2, int>, which +// will cause a static assertion. +void test_tuple_like_lazy_sfinae() { +#if defined(_LIBCPP_VERSION) + // This test requires libc++'s reduced arity initialization. + using T1 = ConstructibleFromT<std::pair<int, int>>; + using T2 = CtorAssertsT<int>; + std::pair<int, int> p(42, 100); + std::tuple<T1, T2> t(p); + assert(std::get<0>(t).value == p); + assert(std::get<1>(t).defaulted); +#endif +} + + +struct NonConstCopyable { + NonConstCopyable() = default; + explicit NonConstCopyable(int v) : value(v) {} + NonConstCopyable(NonConstCopyable&) = default; + NonConstCopyable(NonConstCopyable const&) = delete; + int value; +}; + +template <class T> +struct BlowsUpOnConstCopy { + BlowsUpOnConstCopy() = default; + constexpr BlowsUpOnConstCopy(BlowsUpOnConstCopy const&) { + static_assert(!std::is_same<T, T>::value, ""); + } + BlowsUpOnConstCopy(BlowsUpOnConstCopy&) = default; +}; + +// Test the following constructors: +// (1) tuple(Types const&...) +// (2) tuple(UTypes&&...) +// Test that (1) short circuits before evaluating the copy constructor of the +// second argument. Constructor (2) should be selected. +void test_const_Types_lazy_sfinae() +{ + NonConstCopyable v(42); + BlowsUpOnConstCopy<int> b; + std::tuple<NonConstCopyable, BlowsUpOnConstCopy<int>> t(v, b); + assert(std::get<0>(t).value == 42); +} + +int main() { + test_tuple_like_lazy_sfinae(); + test_const_Types_lazy_sfinae(); +} |