diff --git a/libcxx/include/__memory/pointer_traits.h b/libcxx/include/__memory/pointer_traits.h --- a/libcxx/include/__memory/pointer_traits.h +++ b/libcxx/include/__memory/pointer_traits.h @@ -164,64 +164,46 @@ // to_address -template struct __to_address_helper; - -template <> struct __to_address_helper { - template - using __return_type = decltype(pointer_traits<_Pointer>::to_address(_VSTD::declval())); - - template - _LIBCPP_CONSTEXPR - static __return_type<_Pointer> - __do_it(const _Pointer &__p) _NOEXCEPT { return pointer_traits<_Pointer>::to_address(__p); } -}; - -template -using __choose_to_address = __to_address_helper<_IsValidExpansion<__to_address_helper<_Dummy>::template __return_type, _Pointer>::value>; +template +struct __to_address_helper; template -inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR -_Tp* -__to_address(_Tp* __p) _NOEXCEPT -{ +_LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR +_Tp* __to_address(_Tp* __p) _NOEXCEPT { static_assert(!is_function<_Tp>::value, "_Tp is a function type"); return __p; } -template -inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR -typename __choose_to_address<_Pointer>::template __return_type<_Pointer> -__to_address(const _Pointer& __p) _NOEXCEPT -{ - return __choose_to_address<_Pointer>::__do_it(__p); +// enable_if is needed here to avoid instantiating checks for fancy pointers on raw pointers +template ::value> > +_LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR +typename decay::__call(declval()))>::type +__to_address(const _Pointer& __p) _NOEXCEPT { + return __to_address_helper<_Pointer>::__call(__p); } -template <> struct __to_address_helper { - template - using __return_type = typename pointer_traits<_Pointer>::element_type*; - - template - _LIBCPP_CONSTEXPR - static __return_type<_Pointer> - __do_it(const _Pointer &__p) _NOEXCEPT { return _VSTD::__to_address(__p.operator->()); } +template +struct __to_address_helper { + _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR + static decltype(_VSTD::__to_address(declval().operator->())) + __call(const _Pointer&__p) _NOEXCEPT { + return _VSTD::__to_address(__p.operator->()); + } }; +template +struct __to_address_helper<_Pointer, decltype((void)pointer_traits<_Pointer>::to_address(declval()))> { + _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR + static decltype(pointer_traits<_Pointer>::to_address(declval())) + __call(const _Pointer&__p) _NOEXCEPT { + return pointer_traits<_Pointer>::to_address(__p); + } +}; #if _LIBCPP_STD_VER > 17 -template -inline _LIBCPP_INLINE_VISIBILITY constexpr -_Tp* -to_address(_Tp* __p) _NOEXCEPT -{ - static_assert(!is_function_v<_Tp>, "_Tp is a function type"); - return __p; -} - template inline _LIBCPP_INLINE_VISIBILITY constexpr -auto -to_address(const _Pointer& __p) _NOEXCEPT -{ +auto to_address(const _Pointer& __p) noexcept { return _VSTD::__to_address(__p); } #endif diff --git a/libcxx/include/iterator b/libcxx/include/iterator --- a/libcxx/include/iterator +++ b/libcxx/include/iterator @@ -1356,7 +1356,7 @@ _LIBCPP_ASSERT(__get_const_db()->__dereferenceable(this), "Attempted to dereference a non-dereferenceable iterator"); #endif - return (pointer)_VSTD::addressof(*__i); + return _VSTD::__to_address(__i); } _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_IF_NODEBUG __wrap_iter& operator++() _NOEXCEPT { diff --git a/libcxx/test/libcxx/utilities/memory/pointer.conversion/to_address.pass.cpp b/libcxx/test/libcxx/utilities/memory/pointer.conversion/to_address.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/libcxx/utilities/memory/pointer.conversion/to_address.pass.cpp @@ -0,0 +1,149 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// + +// template constexpr T* __to_address(T* p) noexcept; +// template constexpr auto __to_address(const Ptr& p) noexcept; + +#include +#include +#include "test_macros.h" + +struct Irrelevant; + +struct P1 { + using element_type = Irrelevant; + TEST_CONSTEXPR explicit P1(int *p) : p_(p) { } + TEST_CONSTEXPR int *operator->() const { return p_; } + int *p_; +}; + +struct P2 { + using element_type = Irrelevant; + TEST_CONSTEXPR explicit P2(int *p) : p_(p) { } + TEST_CONSTEXPR P1 operator->() const { return p_; } + P1 p_; +}; + +struct P3 { + TEST_CONSTEXPR explicit P3(int *p) : p_(p) { } + int *p_; +}; + +template<> +struct std::pointer_traits { + static TEST_CONSTEXPR int *to_address(const P3& p) { return p.p_; } +}; + +struct P4 { + TEST_CONSTEXPR explicit P4(int *p) : p_(p) { } + int *operator->() const; // should never be called + int *p_; +}; + +template<> +struct std::pointer_traits { + static TEST_CONSTEXPR int *to_address(const P4& p) { return p.p_; } +}; + +struct P5 { + using element_type = Irrelevant; + int const* const& operator->() const; +}; + +struct P6 {}; + +template<> +struct std::pointer_traits { + static int const* const& to_address(const P6&); +}; + +// Taken from a build breakage caused in Clang +namespace P7 { + template struct CanProxy; + template + struct CanQual { + CanProxy operator->() const { return CanProxy(); } + }; + template + struct CanProxy { + const CanProxy *operator->() const { return nullptr; } + }; +} // namespace P7 + +namespace P8 { + template + struct FancyPtrA { + using element_type = Irrelevant; + T *p_; + TEST_CONSTEXPR FancyPtrA(T *p) : p_(p) {} + T& operator*() const; + TEST_CONSTEXPR T *operator->() const { return p_; } + }; + template + struct FancyPtrB { + T *p_; + TEST_CONSTEXPR FancyPtrB(T *p) : p_(p) {} + T& operator*() const; + }; +} // namespace P8 + +template +struct std::pointer_traits > { + static TEST_CONSTEXPR T *to_address(const P8::FancyPtrB& p) { return p.p_; } +}; + +struct Incomplete; +template struct Holder { T t; }; + + +TEST_CONSTEXPR_CXX14 bool test() { + int i = 0; + ASSERT_NOEXCEPT(std::__to_address(&i)); + assert(std::__to_address(&i) == &i); + P1 p1(&i); + ASSERT_NOEXCEPT(std::__to_address(p1)); + assert(std::__to_address(p1) == &i); + P2 p2(&i); + ASSERT_NOEXCEPT(std::__to_address(p2)); + assert(std::__to_address(p2) == &i); + P3 p3(&i); + ASSERT_NOEXCEPT(std::__to_address(p3)); + assert(std::__to_address(p3) == &i); + P4 p4(&i); + ASSERT_NOEXCEPT(std::__to_address(p4)); + assert(std::__to_address(p4) == &i); + + ASSERT_SAME_TYPE(decltype(std::__to_address(std::declval())), int const*); + ASSERT_SAME_TYPE(decltype(std::__to_address(std::declval())), int const*); + ASSERT_SAME_TYPE(decltype(std::__to_address(std::declval())), int const*); + + P7::CanQual* p7 = nullptr; + assert(std::__to_address(p7) == nullptr); + ASSERT_SAME_TYPE(decltype(std::__to_address(p7)), P7::CanQual*); + + Holder *p8_nil = nullptr; // for C++03 compatibility + P8::FancyPtrA > p8a = p8_nil; + assert(std::__to_address(p8a) == p8_nil); + ASSERT_SAME_TYPE(decltype(std::__to_address(p8a)), decltype(p8_nil)); + + P8::FancyPtrB > p8b = p8_nil; + assert(std::__to_address(p8b) == p8_nil); + ASSERT_SAME_TYPE(decltype(std::__to_address(p8b)), decltype(p8_nil)); + + return true; +} + +int main(int, char**) { + test(); +#if TEST_STD_VER >= 14 + static_assert(test(), ""); +#endif + return 0; +} diff --git a/libcxx/test/libcxx/utilities/memory/pointer.conversion/to_address_std_iterators.pass.cpp b/libcxx/test/libcxx/utilities/memory/pointer.conversion/to_address_std_iterators.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/libcxx/utilities/memory/pointer.conversion/to_address_std_iterators.pass.cpp @@ -0,0 +1,56 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// + +// template constexpr T* __to_address(T* p) noexcept; +// template constexpr auto __to_address(const Ptr& p) noexcept; + +#include + +#include +#include +#include +#include +#include +#include +#include +#include "test_macros.h" + +template +void test_container_iterators(C c) +{ + const C& cc = c; + assert(std::__to_address(c.begin()) == c.data()); + assert(std::__to_address(c.end()) == c.data() + c.size()); + assert(std::__to_address(cc.begin()) == cc.data()); + assert(std::__to_address(cc.end()) == cc.data() + cc.size()); +} + +void test_valarray_iterators() +{ + std::valarray v(100); + int *p = std::__to_address(std::begin(v)); + int *q = std::__to_address(std::end(v)); + assert(q - p == 100); +} + +int main(int, char**) { + test_container_iterators(std::array()); + test_container_iterators(std::vector(3)); + test_container_iterators(std::string("abc")); +#if TEST_STD_VER >= 17 + test_container_iterators(std::string_view("abc")); +#endif +#if TEST_STD_VER >= 20 + test_container_iterators(std::span("abc")); +#endif + test_valarray_iterators(); + + return 0; +} diff --git a/libcxx/test/std/utilities/memory/pointer.conversion/to_address.pass.cpp b/libcxx/test/std/utilities/memory/pointer.conversion/to_address.pass.cpp --- a/libcxx/test/std/utilities/memory/pointer.conversion/to_address.pass.cpp +++ b/libcxx/test/std/utilities/memory/pointer.conversion/to_address.pass.cpp @@ -17,110 +17,133 @@ #include #include "test_macros.h" -class P1 -{ -public: - using element_type = int; +struct Irrelevant; - constexpr explicit P1(int* p) - : p_(p) { } - - constexpr int* operator->() const noexcept - { return p_; } - -private: - int* p_; +struct P1 { + using element_type = Irrelevant; + constexpr explicit P1(int *p) : p_(p) { } + constexpr int *operator->() const { return p_; } + int *p_; }; -class P2 -{ -public: - using element_type = int; - - constexpr explicit P2(int* p) - : p_(p) { } - - constexpr P1 operator->() const noexcept - { return p_; } - -private: +struct P2 { + using element_type = Irrelevant; + constexpr explicit P2(int *p) : p_(p) { } + constexpr P1 operator->() const { return p_; } P1 p_; }; -class P3 -{ -public: - constexpr explicit P3(int* p) - : p_(p) { } +struct P3 { + constexpr explicit P3(int *p) : p_(p) { } + int *p_; +}; - constexpr int* get() const noexcept - { return p_; } +template<> +struct std::pointer_traits { + static constexpr int *to_address(const P3& p) { return p.p_; } +}; -private: - int* p_; +struct P4 { + constexpr explicit P4(int *p) : p_(p) { } + int *operator->() const; // should never be called + int *p_; }; -namespace std -{ template<> -struct pointer_traits<::P3> -{ - static constexpr int* to_address(const ::P3& p) noexcept - { return p.get(); } +struct std::pointer_traits { + static constexpr int *to_address(const P4& p) { return p.p_; } }; -} - -class P4 -{ -public: - constexpr explicit P4(int* p) - : p_(p) { } - constexpr int* operator->() const noexcept - { return nullptr; } +struct P5 { + using element_type = Irrelevant; + int const* const& operator->() const; +}; - constexpr int* get() const noexcept - { return p_; } +struct P6 {}; -private: - int* p_; +template<> +struct std::pointer_traits { + static int const* const& to_address(const P6&); }; -namespace std -{ -template<> -struct pointer_traits<::P4> -{ - constexpr static int* to_address(const ::P4& p) noexcept - { return p.get(); } +// Taken from a build breakage caused in Clang +namespace P7 { + template struct CanProxy; + template + struct CanQual { + CanProxy operator->() const { return CanProxy(); } + }; + template + struct CanProxy { + const CanProxy *operator->() const { return nullptr; } + }; +} // namespace P7 + +namespace P8 { + template + struct FancyPtrA { + using element_type = Irrelevant; + T *p_; + TEST_CONSTEXPR FancyPtrA(T *p) : p_(p) {} + T& operator*() const; + TEST_CONSTEXPR T *operator->() const { return p_; } + }; + template + struct FancyPtrB { + T *p_; + TEST_CONSTEXPR FancyPtrB(T *p) : p_(p) {} + T& operator*() const; + }; +} // namespace P8 + +template +struct std::pointer_traits > { + static TEST_CONSTEXPR T *to_address(const P8::FancyPtrB& p) { return p.p_; } }; -} -int n = 0; -static_assert(std::to_address(&n) == &n); +struct Incomplete; +template struct Holder { T t; }; + constexpr bool test() { - int i = 0; - ASSERT_NOEXCEPT(std::to_address(&i)); - assert(std::to_address(&i) == &i); - P1 p1(&i); - ASSERT_NOEXCEPT(std::to_address(p1)); - assert(std::to_address(p1) == &i); - P2 p2(&i); - ASSERT_NOEXCEPT(std::to_address(p2)); - assert(std::to_address(p2) == &i); - P3 p3(&i); - ASSERT_NOEXCEPT(std::to_address(p3)); - assert(std::to_address(p3) == &i); - P4 p4(&i); - ASSERT_NOEXCEPT(std::to_address(p4)); - assert(std::to_address(p4) == &i); - - return true; + int i = 0; + ASSERT_NOEXCEPT(std::to_address(&i)); + assert(std::to_address(&i) == &i); + P1 p1(&i); + ASSERT_NOEXCEPT(std::to_address(p1)); + assert(std::to_address(p1) == &i); + P2 p2(&i); + ASSERT_NOEXCEPT(std::to_address(p2)); + assert(std::to_address(p2) == &i); + P3 p3(&i); + ASSERT_NOEXCEPT(std::to_address(p3)); + assert(std::to_address(p3) == &i); + P4 p4(&i); + ASSERT_NOEXCEPT(std::to_address(p4)); + assert(std::to_address(p4) == &i); + + ASSERT_SAME_TYPE(decltype(std::to_address(std::declval())), int const*); + ASSERT_SAME_TYPE(decltype(std::to_address(std::declval())), int const*); + ASSERT_SAME_TYPE(decltype(std::to_address(std::declval())), int const*); + + P7::CanQual* p7 = nullptr; + assert(std::to_address(p7) == nullptr); + ASSERT_SAME_TYPE(decltype(std::to_address(p7)), P7::CanQual*); + + Holder *p8_nil = nullptr; // for C++03 compatibility + P8::FancyPtrA > p8a = p8_nil; + assert(std::to_address(p8a) == p8_nil); + ASSERT_SAME_TYPE(decltype(std::to_address(p8a)), decltype(p8_nil)); + + P8::FancyPtrB > p8b = p8_nil; + assert(std::to_address(p8b) == p8_nil); + ASSERT_SAME_TYPE(decltype(std::to_address(p8b)), decltype(p8_nil)); + + return true; } int main(int, char**) { - test(); - static_assert(test()); - return 0; + test(); + static_assert(test()); + return 0; } diff --git a/libcxx/test/std/utilities/memory/pointer.conversion/to_address_std_iterators.pass.cpp b/libcxx/test/std/utilities/memory/pointer.conversion/to_address_std_iterators.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/memory/pointer.conversion/to_address_std_iterators.pass.cpp @@ -0,0 +1,58 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 + +// TODO: We should enable this test in Debug mode once we fix __wrap_iter +// to be a proper contiguous_iterator. +// UNSUPPORTED: LIBCXX-DEBUG-FIXME + +// template constexpr T* to_address(T* p) noexcept; +// template constexpr auto to_address(const Ptr& p) noexcept; + +#include + +#include +#include +#include +#include +#include +#include +#include +#include "test_macros.h" + +template +void test_container_iterators(C c) +{ + const C& cc = c; + assert(std::to_address(c.begin()) == c.data()); + assert(std::to_address(c.end()) == c.data() + c.size()); + assert(std::to_address(cc.begin()) == cc.data()); + assert(std::to_address(cc.end()) == cc.data() + cc.size()); +} + +void test_valarray_iterators() +{ + std::valarray v(100); + int *p = std::to_address(std::begin(v)); + int *q = std::to_address(std::end(v)); + assert(q - p == 100); +} + +int main(int, char**) { + test_container_iterators(std::array()); + test_container_iterators(std::vector(3)); + test_container_iterators(std::string("abc")); + test_container_iterators(std::string_view("abc")); + test_container_iterators(std::span("abc")); + test_valarray_iterators(); + + return 0; +}