diff options
author | Eric Fiselier <eric@efcs.ca> | 2018-01-24 22:14:01 +0000 |
---|---|---|
committer | Eric Fiselier <eric@efcs.ca> | 2018-01-24 22:14:01 +0000 |
commit | 292617e7003efdbc4dd84bc49423dc662f856fb6 (patch) | |
tree | 653c72b1f4c5e7b84c7842f812cc0ebeb69a6dec /libcxx/test/std/utilities | |
parent | 67abf5396124384be2ad484abd8660bb10dcd94d (diff) | |
download | bcm5719-llvm-292617e7003efdbc4dd84bc49423dc662f856fb6.tar.gz bcm5719-llvm-292617e7003efdbc4dd84bc49423dc662f856fb6.zip |
[libc++] Fix PR20855 -- libc++ incorrectly diagnoses illegal reference binding in std::tuple.
Summary:
See https://bugs.llvm.org/show_bug.cgi?id=20855
Libc++ goes out of it's way to diagnose `std::tuple` constructions which are UB due to lifetime bugs caused by reference creation. For example:
```
// The 'const std::string&' is created *inside* the tuple constructor, and its lifetime is over before the end of the constructor call.
std::tuple<int, const std::string&> t(std::make_tuple(42, "abc"));
```
However, we are over-aggressive and we incorrectly diagnose cases such as:
```
void foo(std::tuple<int const&, int const&> const&);
foo(std::make_tuple(42, 42));
```
This patch fixes the incorrectly diagnosed cases, as well as converting the diagnostic to use the newly added Clang trait `__reference_binds_to_temporary`. The new trait allows us to diagnose cases we previously couldn't such as:
```
std::tuple<int, const std::string&> t(42, "abc");
```
Reviewers: rsmith, mclow.lists
Reviewed By: rsmith
Subscribers: cfe-commits
Differential Revision: https://reviews.llvm.org/D41977
llvm-svn: 323380
Diffstat (limited to 'libcxx/test/std/utilities')
-rw-r--r-- | libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/PR20855_tuple_ref_binding_diagnostics.pass.cpp | 136 |
1 files changed, 136 insertions, 0 deletions
diff --git a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/PR20855_tuple_ref_binding_diagnostics.pass.cpp b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/PR20855_tuple_ref_binding_diagnostics.pass.cpp new file mode 100644 index 00000000000..4680635441d --- /dev/null +++ b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/PR20855_tuple_ref_binding_diagnostics.pass.cpp @@ -0,0 +1,136 @@ +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// 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> + +// See llvm.org/PR20855 + +#include <tuple> +#include <string> +#include "test_macros.h" + +#if TEST_HAS_BUILTIN_IDENTIFIER(__reference_binds_to_temporary) +# define ASSERT_REFERENCE_BINDS_TEMPORARY(...) static_assert(__reference_binds_to_temporary(__VA_ARGS__), "") +# define ASSERT_NOT_REFERENCE_BINDS_TEMPORARY(...) static_assert(!__reference_binds_to_temporary(__VA_ARGS__), "") +#else +# define ASSERT_REFERENCE_BINDS_TEMPORARY(...) static_assert(true, "") +# define ASSERT_NOT_REFERENCE_BINDS_TEMPORARY(...) static_assert(true, "") +#endif + +template <class Tp> +struct ConvertsTo { + using RawTp = typename std::remove_cv< typename std::remove_reference<Tp>::type>::type; + + operator Tp() const { + return static_cast<Tp>(value); + } + + mutable RawTp value; +}; + +struct Base {}; +struct Derived : Base {}; + + +static_assert(std::is_same<decltype("abc"), decltype(("abc"))>::value, ""); +ASSERT_REFERENCE_BINDS_TEMPORARY(std::string const&, decltype("abc")); +ASSERT_REFERENCE_BINDS_TEMPORARY(std::string const&, decltype(("abc"))); +ASSERT_REFERENCE_BINDS_TEMPORARY(std::string const&, const char*&&); + +ASSERT_NOT_REFERENCE_BINDS_TEMPORARY(int&, const ConvertsTo<int&>&); +ASSERT_NOT_REFERENCE_BINDS_TEMPORARY(const int&, ConvertsTo<int&>&); +ASSERT_NOT_REFERENCE_BINDS_TEMPORARY(Base&, Derived&); + + +static_assert(std::is_constructible<int&, std::reference_wrapper<int>>::value, ""); +static_assert(std::is_constructible<int const&, std::reference_wrapper<int>>::value, ""); + +template <class T> struct CannotDeduce { + using type = T; +}; + +template <class ...Args> +void F(typename CannotDeduce<std::tuple<Args...>>::type const&) {} + +void compile_tests() { + { + F<int, int const&>(std::make_tuple(42, 42)); + } + { + F<int, int const&>(std::make_tuple<const int&, const int&>(42, 42)); + std::tuple<int, int const&> t(std::make_tuple<const int&, const int&>(42, 42)); + } + { + auto fn = &F<int, std::string const&>; + fn(std::tuple<int, std::string const&>(42, std::string("a"))); + fn(std::make_tuple(42, std::string("a"))); + } + { + Derived d; + std::tuple<Base&, Base const&> t(d, d); + } + { + ConvertsTo<int&> ct; + std::tuple<int, int&> t(42, ct); + } +} + +void allocator_tests() { + 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); + } +} + + +int main() { + compile_tests(); + allocator_tests(); +} |