From 9c747b9e8978a11701c8c6e1382f4e88a3fbec29 Mon Sep 17 00:00:00 2001 From: Eric Fiselier Date: Wed, 20 Jul 2016 02:57:39 +0000 Subject: [PATCH] Add tests for reference binding assertions in std::tuple. Libc++ provides static assertions to detect reference binding issues inside tuple. This patch adds tests for those diagnostics. It should be noted that these static assertions technically violate the standard since it allows these illegal bindings to occur. Also see https://llvm.org/bugs/show_bug.cgi?id=20855 git-svn-id: https://llvm.org/svn/llvm-project/libcxx/trunk@276078 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/tuple | 56 ++++++--------- .../diagnose_reference_binding.fail.cpp | 33 +++++++++ .../diagnose_reference_binding.pass.cpp | 71 +++++++++++++++++++ 3 files changed, 124 insertions(+), 36 deletions(-) create mode 100644 test/libcxx/utilities/tuple/tuple.tuple/diagnose_reference_binding.fail.cpp create mode 100644 test/libcxx/utilities/tuple/tuple.tuple/diagnose_reference_binding.pass.cpp diff --git a/include/tuple b/include/tuple index 6805d8c76..31613d2b9 100644 --- a/include/tuple +++ b/include/tuple @@ -192,6 +192,20 @@ class __tuple_leaf { _Hp value; + template + static constexpr bool __can_bind_reference() { + using _RawTp = typename remove_reference<_Tp>::type; + using _RawHp = typename remove_reference<_Hp>::type; + using _CheckLValueArg = integral_constant::value + || is_same<_RawTp, reference_wrapper<_RawHp>>::value + || is_same<_RawTp, reference_wrapper::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::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 _LIBCPP_INLINE_VISIBILITY explicit __tuple_leaf(integral_constant, 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::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 _LIBCPP_INLINE_VISIBILITY explicit __tuple_leaf(integral_constant, 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::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 _LIBCPP_INLINE_VISIBILITY explicit __tuple_leaf(integral_constant, 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::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/test/libcxx/utilities/tuple/tuple.tuple/diagnose_reference_binding.fail.cpp b/test/libcxx/utilities/tuple/tuple.tuple/diagnose_reference_binding.fail.cpp new file mode 100644 index 000000000..a35dfd696 --- /dev/null +++ b/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 + +// + +// 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 +#include + +int main() { + std::allocator 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 t(42); // expected-note {{requested here}} + std::tuple t1(std::allocator_arg, alloc, 42); // expected-note {{requested here}} + // bind rvalue to constructed non-rvalue + std::tuple t2("hello"); // expected-note {{requested here}} + std::tuple t3(std::allocator_arg, alloc, "hello"); // expected-note {{requested here}} +} diff --git a/test/libcxx/utilities/tuple/tuple.tuple/diagnose_reference_binding.pass.cpp b/test/libcxx/utilities/tuple/tuple.tuple/diagnose_reference_binding.pass.cpp new file mode 100644 index 000000000..a90a2912d --- /dev/null +++ b/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 + +// + +// 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 +#include +#include +#include + +static_assert(std::is_constructible>::value, ""); +static_assert(std::is_constructible>::value, ""); + + +int main() { + std::allocator alloc; + int x = 42; + { + std::tuple t(std::ref(x)); + assert(&std::get<0>(t) == &x); + std::tuple 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 t(r); + assert(&std::get<0>(t) == &x); + std::tuple t1(cr); + assert(&std::get<0>(t1) == &x); + std::tuple t2(std::allocator_arg, alloc, r); + assert(&std::get<0>(t2) == &x); + std::tuple t3(std::allocator_arg, alloc, cr); + assert(&std::get<0>(t3) == &x); + } + { + std::tuple t(std::ref(x)); + assert(&std::get<0>(t) == &x); + std::tuple t2(std::cref(x)); + assert(&std::get<0>(t2) == &x); + std::tuple t3(std::allocator_arg, alloc, std::ref(x)); + assert(&std::get<0>(t3) == &x); + std::tuple 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 t(r); + assert(&std::get<0>(t) == &x); + std::tuple t2(cr); + assert(&std::get<0>(t2) == &x); + std::tuple t3(std::allocator_arg, alloc, r); + assert(&std::get<0>(t3) == &x); + std::tuple t4(std::allocator_arg, alloc, cr); + assert(&std::get<0>(t4) == &x); + } +} \ No newline at end of file