[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 git-svn-id: https://llvm.org/svn/llvm-project/libcxx/trunk@323380 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
@@ -1,40 +0,0 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// 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-re@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}}
|
||||
|
||||
// FIXME: The below warnings may get emitted as an error, a warning, or not emitted at all
|
||||
// depending on the flags used to compile this test.
|
||||
{
|
||||
// expected-warning@tuple:* 0+ {{binding reference member '__value_' to a temporary value}}
|
||||
// expected-error@tuple:* 0+ {{binding reference member '__value_' to a temporary value}}
|
||||
}
|
||||
}
|
||||
@@ -1,71 +0,0 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
// -*- 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
|
||||
|
||||
#ifdef __clang__
|
||||
#pragma clang diagnostic ignored "-Wdangling-field"
|
||||
#endif
|
||||
|
||||
#include <tuple>
|
||||
#include <string>
|
||||
#include "test_macros.h"
|
||||
|
||||
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 {};
|
||||
|
||||
template <class T> struct CannotDeduce {
|
||||
using type = T;
|
||||
};
|
||||
|
||||
template <class ...Args>
|
||||
void F(typename CannotDeduce<std::tuple<Args...>>::type const&) {}
|
||||
|
||||
|
||||
int main() {
|
||||
#if TEST_HAS_BUILTIN_IDENTIFIER(__reference_binds_to_temporary)
|
||||
// expected-error@tuple:* 8 {{"Attempted construction of reference element binds to a temporary whose lifetime has ended"}}
|
||||
{
|
||||
F<int, const std::string&>(std::make_tuple(1, "abc")); // expected-note 1 {{requested here}}
|
||||
}
|
||||
{
|
||||
std::tuple<int, const std::string&> t(1, "a"); // expected-note 1 {{requested here}}
|
||||
}
|
||||
{
|
||||
F<int, const std::string&>(std::tuple<int, const std::string&>(1, "abc")); // expected-note 1 {{requested here}}
|
||||
}
|
||||
{
|
||||
ConvertsTo<int&> ct;
|
||||
std::tuple<const long&, int> t(ct, 42); // expected-note {{requested here}}
|
||||
}
|
||||
{
|
||||
ConvertsTo<int> ct;
|
||||
std::tuple<int const&, void*> t(ct, nullptr); // expected-note {{requested here}}
|
||||
}
|
||||
{
|
||||
ConvertsTo<Derived> ct;
|
||||
std::tuple<Base const&, int> t(ct, 42); // expected-note {{requested here}}
|
||||
}
|
||||
{
|
||||
std::allocator<void> alloc;
|
||||
std::tuple<std::string &&> t2("hello"); // expected-note {{requested here}}
|
||||
std::tuple<std::string &&> t3(std::allocator_arg, alloc, "hello"); // expected-note {{requested here}}
|
||||
}
|
||||
#else
|
||||
#error force failure
|
||||
// expected-error@-1 {{force failure}}
|
||||
#endif
|
||||
}
|
||||
Reference in New Issue
Block a user