Sorry for the breakage. Fixed in r323389. On Wed, Jan 24, 2018 at 4:08 PM, Eric Fiselier <e...@efcs.ca> wrote:
> Looking. > > On Wed, Jan 24, 2018 at 3:52 PM, Richard Trieu <rtr...@google.com> wrote: > >> Hi Eric, >> >> I am getting a build failure after this revision: >> >> llvm/projects/libcxx/include/tuple:175:27: error: no return statement in >> constexpr function >> static constexpr bool __can_bind_reference() { >> ^ >> 1 error generated. >> >> It looks like if the #if in __can_bind_reference is false, the function >> will be empty. Can you take a look? >> >> Richard >> >> On Wed, Jan 24, 2018 at 2:14 PM, Eric Fiselier via cfe-commits < >> cfe-commits@lists.llvm.org> wrote: >> >>> Author: ericwf >>> Date: Wed Jan 24 14:14:01 2018 >>> New Revision: 323380 >>> >>> URL: http://llvm.org/viewvc/llvm-project?rev=323380&view=rev >>> Log: >>> [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 >>> >>> Added: >>> libcxx/trunk/test/libcxx/utilities/tuple/tuple.tuple/tuple.c >>> nstr/PR20855_tuple_ref_binding_diagnostics.fail.cpp >>> libcxx/trunk/test/std/utilities/tuple/tuple.tuple/tuple.cnst >>> r/PR20855_tuple_ref_binding_diagnostics.pass.cpp >>> Removed: >>> libcxx/trunk/test/libcxx/utilities/tuple/tuple.tuple/diagnos >>> e_reference_binding.fail.cpp >>> libcxx/trunk/test/libcxx/utilities/tuple/tuple.tuple/diagnos >>> e_reference_binding.pass.cpp >>> Modified: >>> libcxx/trunk/include/tuple >>> >>> Modified: libcxx/trunk/include/tuple >>> URL: http://llvm.org/viewvc/llvm-project/libcxx/trunk/include/tup >>> le?rev=323380&r1=323379&r2=323380&view=diff >>> ============================================================ >>> ================== >>> --- libcxx/trunk/include/tuple (original) >>> +++ libcxx/trunk/include/tuple Wed Jan 24 14:14:01 2018 >>> @@ -173,16 +173,9 @@ class __tuple_leaf >>> >>> 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); >>> +#if __has_keyword(__reference_binds_to_temporary) >>> + return !__reference_binds_to_temporary(_Hp, _Tp); >>> +#endif >>> } >>> >>> __tuple_leaf& operator=(const __tuple_leaf&); >>> @@ -224,15 +217,15 @@ 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(__can_bind_reference<_Tp>(), >>> - "Attempted to construct a reference element in a tuple with an >>> rvalue");} >>> + {static_assert(__can_bind_reference<_Tp&&>(), >>> + "Attempted construction of reference element binds to a >>> temporary whose lifetime has ended");} >>> >>> 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(__can_bind_reference<_Tp>(), >>> - "Attempted to construct a reference element in a tuple with an >>> rvalue");} >>> + {static_assert(__can_bind_reference<_Tp&&>(), >>> + "Attempted construction of reference element binds to a >>> temporary whose lifetime has ended");} >>> >>> template <class _Tp, class _Alloc> >>> _LIBCPP_INLINE_VISIBILITY >>> >>> Removed: libcxx/trunk/test/libcxx/utilities/tuple/tuple.tuple/diagnos >>> e_reference_binding.fail.cpp >>> URL: http://llvm.org/viewvc/llvm-project/libcxx/trunk/test/libcxx >>> /utilities/tuple/tuple.tuple/diagnose_reference_binding.fail >>> .cpp?rev=323379&view=auto >>> ============================================================ >>> ================== >>> --- >>> libcxx/trunk/test/libcxx/utilities/tuple/tuple.tuple/diagnose_reference_binding.fail.cpp >>> (original) >>> +++ >>> libcxx/trunk/test/libcxx/utilities/tuple/tuple.tuple/diagnose_reference_binding.fail.cpp >>> (removed) >>> @@ -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}} >>> - } >>> -} >>> >>> Removed: libcxx/trunk/test/libcxx/utilities/tuple/tuple.tuple/diagnos >>> e_reference_binding.pass.cpp >>> URL: http://llvm.org/viewvc/llvm-project/libcxx/trunk/test/libcxx >>> /utilities/tuple/tuple.tuple/diagnose_reference_binding.pass >>> .cpp?rev=323379&view=auto >>> ============================================================ >>> ================== >>> --- >>> libcxx/trunk/test/libcxx/utilities/tuple/tuple.tuple/diagnose_reference_binding.pass.cpp >>> (original) >>> +++ >>> libcxx/trunk/test/libcxx/utilities/tuple/tuple.tuple/diagnose_reference_binding.pass.cpp >>> (removed) >>> @@ -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); >>> - } >>> -} >>> >>> Added: libcxx/trunk/test/libcxx/utilities/tuple/tuple.tuple/tuple.c >>> nstr/PR20855_tuple_ref_binding_diagnostics.fail.cpp >>> URL: http://llvm.org/viewvc/llvm-project/libcxx/trunk/test/libcxx >>> /utilities/tuple/tuple.tuple/tuple.cnstr/PR20855_tuple_ref_b >>> inding_diagnostics.fail.cpp?rev=323380&view=auto >>> ============================================================ >>> ================== >>> --- libcxx/trunk/test/libcxx/utilities/tuple/tuple.tuple/tuple.c >>> nstr/PR20855_tuple_ref_binding_diagnostics.fail.cpp (added) >>> +++ libcxx/trunk/test/libcxx/utilities/tuple/tuple.tuple/tuple.c >>> nstr/PR20855_tuple_ref_binding_diagnostics.fail.cpp Wed Jan 24 14:14:01 >>> 2018 >>> @@ -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 >>> +} >>> >>> Added: libcxx/trunk/test/std/utilities/tuple/tuple.tuple/tuple.cnst >>> r/PR20855_tuple_ref_binding_diagnostics.pass.cpp >>> URL: http://llvm.org/viewvc/llvm-project/libcxx/trunk/test/std/ut >>> ilities/tuple/tuple.tuple/tuple.cnstr/PR20855_tuple_ref_bind >>> ing_diagnostics.pass.cpp?rev=323380&view=auto >>> ============================================================ >>> ================== >>> --- libcxx/trunk/test/std/utilities/tuple/tuple.tuple/tuple.cnst >>> r/PR20855_tuple_ref_binding_diagnostics.pass.cpp (added) >>> +++ libcxx/trunk/test/std/utilities/tuple/tuple.tuple/tuple.cnst >>> r/PR20855_tuple_ref_binding_diagnostics.pass.cpp Wed Jan 24 14:14:01 >>> 2018 >>> @@ -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(); >>> +} >>> >>> >>> _______________________________________________ >>> cfe-commits mailing list >>> cfe-commits@lists.llvm.org >>> http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits >>> >> >> >
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits