| // Copyright 2020 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include <lib/stdcompat/memory.h> |
| #include <lib/stdcompat/optional.h> |
| |
| #include <array> |
| #include <memory> |
| #include <type_traits> |
| |
| #include <gtest/gtest.h> |
| |
| struct arrow { |
| struct inner { |
| } value; |
| const inner* operator->() const { return &value; } |
| }; |
| |
| template <typename T> |
| struct weird_ptr { |
| const T& operator->() const { return value; } |
| T value; |
| }; |
| |
| namespace std { |
| template <> |
| struct pointer_traits<arrow> { |
| using element_type = const typename arrow::inner; |
| }; |
| template <typename T> |
| struct pointer_traits<weird_ptr<T>> { |
| using element_type = const T; |
| }; |
| } // namespace std |
| |
| namespace { |
| |
| constexpr bool IsAddressOfConstexpr() { |
| int a = 1; |
| return cpp17::addressof(a) == &a; |
| } |
| |
| TEST(MemoryTest, AddressOfReturnsCorrectPointer) { |
| static_assert(IsAddressOfConstexpr() == true, |
| "cpp17::addressof should be evaluated in constexpr."); |
| } |
| |
| TEST(MemoryTest, AddressOfReturnsAddressNotOverridenOperator) { |
| struct Misleading { |
| constexpr Misleading* operator&() const { return nullptr; } |
| }; |
| |
| alignas(Misleading) std::array<char, sizeof(Misleading)> buffer; |
| const Misleading* ptr = new (static_cast<void*>(buffer.data())) Misleading(); |
| const Misleading& misleading = *ptr; |
| |
| ASSERT_EQ(&misleading, nullptr); |
| ASSERT_EQ(cpp17::addressof(misleading), ptr); |
| } |
| |
| // TODO(fxbug.dev/98561) Disable these tests until we can avoid taking the address of std:: |
| // functions #if __cpp_lib_addressof_constexpr >= 201603L && !defined(LIB_STDCOMPAT_USE_POLYFILLS) |
| |
| // template <typename T> |
| // constexpr void check_addressof_alias() { |
| //// Need so the compiler picks the right overload. |
| // constexpr T* (*cpp17_addressof)(T&) = &cpp17::addressof<T>; |
| // constexpr T* (*std_addressof)(T&) = &std::addressof<T>; |
| // static_assert(cpp17_addressof == std_addressof); |
| //} |
| |
| // struct UserType { |
| // int var; |
| //}; |
| |
| // TEST(MemoryTest, AddressOfIsAliasForStdWhenAvailable) { |
| // check_addressof_alias<int>(); |
| // check_addressof_alias<UserType>(); |
| //} |
| // #endif |
| |
| TEST(MemoryTest, ToAddressWithRawReturnsRightPointer) { |
| constexpr int* a = nullptr; |
| static_assert(cpp20::to_address(a) == nullptr, "To Address returns right raw ptr."); |
| } |
| |
| TEST(MemoryTest, ToAddressWithFancyReturnsRightPointer) { |
| std::unique_ptr<int> a = std::make_unique<int>(1); |
| EXPECT_EQ(a.get(), cpp20::to_address(a)); |
| } |
| |
| TEST(MemoryTest, ToAddressWithArrowReturnsRightPointer) { |
| cpp17::optional<const int> a(13); |
| EXPECT_EQ(&*a, cpp20::to_address(a)); |
| |
| arrow b; |
| EXPECT_EQ(&b.value, cpp20::to_address(b)); |
| |
| // We only go one level because cpp17::optional<T>::operator->() returns T*, which goes to the |
| // first overload of cpp20::to_address and there is no recursion. |
| cpp17::optional<const arrow> c; |
| EXPECT_EQ(&*c, cpp20::to_address(c)); |
| |
| // TODO(fxbug.dev/70523): libc++ and libstdc++ currently both have broken implementations of |
| // std::to_address that require specializing std::pointer_traits and don't allow operator-> to |
| // return non-raw-pointers, so the chaining in this test case (which seems to be intentionally |
| // allowed by the standard, given that there is a recursive path through the function) is |
| // impossible. |
| // |
| // Here we do go two levels because weird_ptr<T>::operator->() returns const T&, which goes back |
| // to the overload for operator->() and then we get a raw pointer. |
| // weird_ptr<cpp17::optional<const int>> e; |
| // EXPECT_EQ(&*e.value, cpp20::to_address(e)); |
| } |
| |
| // TODO(fxbug.dev/70523): This is only to be bug-compatible with the standard library |
| // implementations; change asserts when the linked bug is resolved. |
| TEST(MemoryTest, BannedUses) { |
| struct banned { |
| const int* operator->() const { return &value; } |
| int value; |
| }; |
| |
| // These tests must *not* compile to keep compatibility with the curent standard library |
| // implementations. Uncomment them to verify that they error out both for the polyfill and the |
| // standard version. |
| |
| // No std::pointer_traits specialization |
| // banned a; |
| // EXPECT_EQ(&a.value, cpp20::to_address(a)); |
| |
| // Doesn't ever get to a raw pointer |
| // weird_ptr<const int> b; |
| // EXPECT_EQ(&b.value, cpp20::to_address(b)); |
| |
| // Incorrect attempt at chaining |
| // cpp17::optional<const cpp17::optional<int>> c(13); |
| // EXPECT_EQ(&**c, cpp20::to_address(c)); |
| |
| // No chaining |
| // weird_ptr<cpp17::optional<const int>> d; |
| // EXPECT_EQ(&*d.value, cpp20::to_address(d)); |
| } |
| |
| // TODO(fxbug.dev/98561) Disable these tests until we can avoid taking the address of std:: |
| // functions #if __cpp_lib_to_address >= 201711L && !defined(LIB_STDCOMPAT_USE_POLYFILLS) |
| |
| // template <typename T> |
| // constexpr void check_to_address_alias() { |
| //// Need so the compiler picks the right overload. |
| // constexpr auto (*cpp20_to_address)(const T&) = &cpp20::to_address<T>; |
| // constexpr auto (*std_to_address)(const T&) = &std::to_address<T>; |
| // static_assert(cpp20_to_address == std_to_address); |
| //} |
| |
| // TEST(MemoryTest, ToAddressIsAliasForStdWhenAvailable) { |
| // check_to_address_alias<int*>(); |
| // check_to_address_alias<std::unique_ptr<int>>(); |
| //} |
| |
| // #endif |
| |
| } // namespace |