[libcxx] r323380 - [libc++] Fix PR20855 -- libc++ incorrectly diagnoses illegal reference binding in std::tuple.

Richard Trieu via cfe-commits cfe-commits at lists.llvm.org
Wed Jan 24 14:52:01 PST 2018


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 at 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.
> cnstr/PR20855_tuple_ref_binding_diagnostics.fail.cpp
>     libcxx/trunk/test/std/utilities/tuple/tuple.tuple/tuple.
> cnstr/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 at 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 at tuple:* 0+ {{binding reference member '__value_'
> to a temporary value}}
> -    // expected-error at 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.
> cnstr/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_
> binding_diagnostics.fail.cpp?rev=323380&view=auto
> ============================================================
> ==================
> --- libcxx/trunk/test/libcxx/utilities/tuple/tuple.tuple/tuple.
> cnstr/PR20855_tuple_ref_binding_diagnostics.fail.cpp (added)
> +++ libcxx/trunk/test/libcxx/utilities/tuple/tuple.tuple/tuple.
> cnstr/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 at 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 at -1 {{force failure}}
> +#endif
> +}
>
> Added: libcxx/trunk/test/std/utilities/tuple/tuple.tuple/tuple.
> cnstr/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.
> cnstr/PR20855_tuple_ref_binding_diagnostics.pass.cpp (added)
> +++ libcxx/trunk/test/std/utilities/tuple/tuple.tuple/tuple.
> cnstr/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 at lists.llvm.org
> http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/cfe-commits/attachments/20180124/fc708395/attachment-0001.html>


More information about the cfe-commits mailing list