diff options
3 files changed, 124 insertions, 36 deletions
diff --git a/libcxx/include/tuple b/libcxx/include/tuple index 6805d8c7635..31613d2b94f 100644 --- a/libcxx/include/tuple +++ b/libcxx/include/tuple @@ -192,6 +192,20 @@ class __tuple_leaf { _Hp value; + template <class _Tp> + static constexpr bool __can_bind_reference() { + using _RawTp = typename remove_reference<_Tp>::type; + using _RawHp = typename remove_reference<_Hp>::type; + using _CheckLValueArg = integral_constant<bool, + is_lvalue_reference<_Tp>::value + || is_same<_RawTp, reference_wrapper<_RawHp>>::value + || is_same<_RawTp, reference_wrapper<typename remove_const<_RawHp>::type>>::value + >; + return !is_reference<_Hp>::value + || (is_lvalue_reference<_Hp>::value && _CheckLValueArg::value) + || (is_rvalue_reference<_Hp>::value && !is_lvalue_reference<_Tp>::value); + } + __tuple_leaf& operator=(const __tuple_leaf&); public: _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR __tuple_leaf() @@ -231,59 +245,29 @@ public: _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX11 explicit __tuple_leaf(_Tp&& __t) _NOEXCEPT_((is_nothrow_constructible<_Hp, _Tp>::value)) : value(_VSTD::forward<_Tp>(__t)) - {static_assert(!is_reference<_Hp>::value || - (is_lvalue_reference<_Hp>::value && - (is_lvalue_reference<_Tp>::value || - is_same<typename remove_reference<_Tp>::type, - reference_wrapper< - typename remove_reference<_Hp>::type - > - >::value)) || - (is_rvalue_reference<_Hp>::value && - !is_lvalue_reference<_Tp>::value), + {static_assert(__can_bind_reference<_Tp>(), "Attempted to construct a reference element in a tuple with an rvalue");} template <class _Tp, class _Alloc> _LIBCPP_INLINE_VISIBILITY explicit __tuple_leaf(integral_constant<int, 0>, const _Alloc&, _Tp&& __t) : value(_VSTD::forward<_Tp>(__t)) - {static_assert(!is_lvalue_reference<_Hp>::value || - (is_lvalue_reference<_Hp>::value && - (is_lvalue_reference<_Tp>::value || - is_same<typename remove_reference<_Tp>::type, - reference_wrapper< - typename remove_reference<_Hp>::type - > - >::value)), + {static_assert(__can_bind_reference<_Tp>(), "Attempted to construct a reference element in a tuple with an rvalue");} template <class _Tp, class _Alloc> _LIBCPP_INLINE_VISIBILITY explicit __tuple_leaf(integral_constant<int, 1>, const _Alloc& __a, _Tp&& __t) : value(allocator_arg_t(), __a, _VSTD::forward<_Tp>(__t)) - {static_assert(!is_lvalue_reference<_Hp>::value || - (is_lvalue_reference<_Hp>::value && - (is_lvalue_reference<_Tp>::value || - is_same<typename remove_reference<_Tp>::type, - reference_wrapper< - typename remove_reference<_Hp>::type - > - >::value)), - "Attempted to construct a reference element in a tuple with an rvalue");} + {static_assert(!is_reference<_Hp>::value, + "Attempted to uses-allocator construct a reference element in a tuple");} template <class _Tp, class _Alloc> _LIBCPP_INLINE_VISIBILITY explicit __tuple_leaf(integral_constant<int, 2>, const _Alloc& __a, _Tp&& __t) : value(_VSTD::forward<_Tp>(__t), __a) - {static_assert(!is_lvalue_reference<_Hp>::value || - (is_lvalue_reference<_Hp>::value && - (is_lvalue_reference<_Tp>::value || - is_same<typename remove_reference<_Tp>::type, - reference_wrapper< - typename remove_reference<_Hp>::type - > - >::value)), - "Attempted to construct a reference element in a tuple with an rvalue");} + {static_assert(!is_reference<_Hp>::value, + "Attempted to uses-allocator construct a reference element in a tuple");} __tuple_leaf(const __tuple_leaf& __t) = default; __tuple_leaf(__tuple_leaf&& __t) = default; diff --git a/libcxx/test/libcxx/utilities/tuple/tuple.tuple/diagnose_reference_binding.fail.cpp b/libcxx/test/libcxx/utilities/tuple/tuple.tuple/diagnose_reference_binding.fail.cpp new file mode 100644 index 00000000000..a35dfd69625 --- /dev/null +++ b/libcxx/test/libcxx/utilities/tuple/tuple.tuple/diagnose_reference_binding.fail.cpp @@ -0,0 +1,33 @@ +//===----------------------------------------------------------------------===// +// +// 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> + +// Test the diagnostics libc++ generates for invalid reference binding. +// Libc++ attempts to diagnose the following cases: +// * Constructing an lvalue reference from an rvalue. +// * Constructing an rvalue reference from an lvalue. + +#include <tuple> +#include <string> + +int main() { + std::allocator<void> alloc; + + // expected-error@tuple:* 4 {{static_assert failed "Attempted to construct a reference element in a tuple with an rvalue"}} + + // bind lvalue to rvalue + std::tuple<int const&> t(42); // expected-note {{requested here}} + std::tuple<int const&> t1(std::allocator_arg, alloc, 42); // expected-note {{requested here}} + // bind rvalue to constructed non-rvalue + std::tuple<std::string &&> t2("hello"); // expected-note {{requested here}} + std::tuple<std::string &&> t3(std::allocator_arg, alloc, "hello"); // expected-note {{requested here}} +} diff --git a/libcxx/test/libcxx/utilities/tuple/tuple.tuple/diagnose_reference_binding.pass.cpp b/libcxx/test/libcxx/utilities/tuple/tuple.tuple/diagnose_reference_binding.pass.cpp new file mode 100644 index 00000000000..a90a2912d3a --- /dev/null +++ b/libcxx/test/libcxx/utilities/tuple/tuple.tuple/diagnose_reference_binding.pass.cpp @@ -0,0 +1,71 @@ +//===----------------------------------------------------------------------===// +// +// 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> + +// Test the diagnostics libc++ generates for invalid reference binding. +// Libc++ attempts to diagnose the following cases: +// * Constructing an lvalue reference from an rvalue. +// * Constructing an rvalue reference from an lvalue. + +#include <tuple> +#include <string> +#include <functional> +#include <cassert> + +static_assert(std::is_constructible<int&, std::reference_wrapper<int>>::value, ""); +static_assert(std::is_constructible<int const&, std::reference_wrapper<int>>::value, ""); + + +int main() { + std::allocator<void> alloc; + int x = 42; + { + std::tuple<int&> t(std::ref(x)); + assert(&std::get<0>(t) == &x); + std::tuple<int&> t1(std::allocator_arg, alloc, std::ref(x)); + assert(&std::get<0>(t1) == &x); + } + { + auto r = std::ref(x); + auto const& cr = r; + std::tuple<int&> t(r); + assert(&std::get<0>(t) == &x); + std::tuple<int&> t1(cr); + assert(&std::get<0>(t1) == &x); + std::tuple<int&> t2(std::allocator_arg, alloc, r); + assert(&std::get<0>(t2) == &x); + std::tuple<int&> t3(std::allocator_arg, alloc, cr); + assert(&std::get<0>(t3) == &x); + } + { + std::tuple<int const&> t(std::ref(x)); + assert(&std::get<0>(t) == &x); + std::tuple<int const&> t2(std::cref(x)); + assert(&std::get<0>(t2) == &x); + std::tuple<int const&> t3(std::allocator_arg, alloc, std::ref(x)); + assert(&std::get<0>(t3) == &x); + std::tuple<int const&> t4(std::allocator_arg, alloc, std::cref(x)); + assert(&std::get<0>(t4) == &x); + } + { + auto r = std::ref(x); + auto cr = std::cref(x); + std::tuple<int const&> t(r); + assert(&std::get<0>(t) == &x); + std::tuple<int const&> t2(cr); + assert(&std::get<0>(t2) == &x); + std::tuple<int const&> t3(std::allocator_arg, alloc, r); + assert(&std::get<0>(t3) == &x); + std::tuple<int const&> t4(std::allocator_arg, alloc, cr); + assert(&std::get<0>(t4) == &x); + } +}
\ No newline at end of file |