// Copyright 2022 The Chromium 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 "base/memory/raw_ref.h" #include #include #include "base/test/gtest_util.h" #include "testing/gtest/include/gtest/gtest.h" namespace { class BaseClass {}; class SubClass : public BaseClass {}; // raw_ref just defers to the superclass for implementations, so it // can't add more data types. static_assert(sizeof(raw_ref) == sizeof(raw_ptr)); // Since it can't hold null, raw_ref is not default-constructible. static_assert(!std::is_default_constructible_v>); static_assert(!std::is_default_constructible_v>); // A mutable reference can only be constructed from a mutable lvalue reference. static_assert(!std::is_constructible_v, const int>); static_assert(!std::is_constructible_v, int>); static_assert(!std::is_constructible_v, const int&>); static_assert(std::is_constructible_v, int&>); static_assert(!std::is_constructible_v, const int*>); static_assert(!std::is_constructible_v, int*>); static_assert(!std::is_constructible_v, const int&&>); static_assert(!std::is_constructible_v, int&&>); // Same for assignment. static_assert(!std::is_assignable_v, const int>); static_assert(!std::is_assignable_v, int>); static_assert(!std::is_assignable_v, const int&>); static_assert(std::is_assignable_v, int&>); static_assert(!std::is_assignable_v, const int*>); static_assert(!std::is_assignable_v, int*>); static_assert(!std::is_assignable_v, const int&&>); static_assert(!std::is_assignable_v, int&&>); // A const reference can be constructed from a const or mutable lvalue // reference. static_assert(!std::is_constructible_v, const int>); static_assert(!std::is_constructible_v, int>); static_assert(std::is_constructible_v, const int&>); static_assert(std::is_constructible_v, int&>); static_assert(!std::is_constructible_v, const int*>); static_assert(!std::is_constructible_v, int*>); static_assert(!std::is_constructible_v, const int&&>); static_assert(!std::is_constructible_v, int&&>); // Same for assignment. static_assert(!std::is_assignable_v, const int>); static_assert(!std::is_assignable_v, int>); static_assert(std::is_assignable_v, const int&>); static_assert(std::is_assignable_v, int&>); static_assert(!std::is_assignable_v, const int*>); static_assert(!std::is_assignable_v, int*>); static_assert(!std::is_assignable_v, const int&&>); static_assert(!std::is_assignable_v, int&&>); // Same trivial operations (or not) as raw_ptr. static_assert(std::is_trivially_constructible_v, const int&> == std::is_trivially_constructible_v, const int&>); static_assert(std::is_trivially_destructible_v> == std::is_trivially_destructible_v>); // But constructing from another raw_ref must check if it's internally null // (which indicates use-after-move). static_assert(!std::is_trivially_move_constructible_v>); static_assert(!std::is_trivially_move_assignable_v>); static_assert(!std::is_trivially_copy_constructible_v>); static_assert(!std::is_trivially_copy_assignable_v>); // A raw_ref can be copied or moved. static_assert(std::is_move_constructible_v>); static_assert(std::is_copy_constructible_v>); static_assert(std::is_move_assignable_v>); static_assert(std::is_copy_assignable_v>); // A SubClass can be converted to a BaseClass. static_assert(std::is_constructible_v, raw_ref>); static_assert( std::is_constructible_v, const raw_ref&>); static_assert(std::is_constructible_v, raw_ref&&>); static_assert(std::is_assignable_v, raw_ref>); static_assert( std::is_assignable_v, const raw_ref&>); static_assert(std::is_assignable_v, raw_ref&&>); // A BaseClass can't be implicitly downcasted. static_assert(!std::is_constructible_v, raw_ref>); static_assert( !std::is_constructible_v, const raw_ref&>); static_assert( !std::is_constructible_v, raw_ref&&>); static_assert(!std::is_assignable_v, raw_ref>); static_assert( !std::is_assignable_v, const raw_ref&>); static_assert(!std::is_assignable_v, raw_ref&&>); // A raw_ref can be constructed directly from a SubClass. static_assert(std::is_constructible_v, SubClass&>); static_assert(std::is_assignable_v, SubClass&>); static_assert(std::is_constructible_v, SubClass&>); static_assert(std::is_assignable_v, SubClass&>); static_assert( std::is_constructible_v, const SubClass&>); static_assert(std::is_assignable_v, const SubClass&>); // But a raw_ref can't be constructed from an implicit downcast from a // BaseClass. static_assert(!std::is_constructible_v, BaseClass&>); static_assert(!std::is_assignable_v, BaseClass&>); static_assert(!std::is_constructible_v, BaseClass&>); static_assert(!std::is_assignable_v, BaseClass&>); static_assert( !std::is_constructible_v, const BaseClass&>); static_assert(!std::is_assignable_v, const BaseClass&>); // A mutable reference can be converted to const reference. static_assert(std::is_constructible_v, raw_ref>); static_assert(std::is_assignable_v, raw_ref>); // A const reference can't be converted to mutable. static_assert(!std::is_constructible_v, raw_ref>); static_assert(!std::is_assignable_v, raw_ref>); // The deref operator gives the internal reference. static_assert(std::is_same_v>())>); static_assert( std::is_same_v>())>); static_assert(std::is_same_v&>())>); static_assert( std::is_same_v&>())>); static_assert(std::is_same_v&&>())>); static_assert( std::is_same_v&&>())>); // A const T is always returned as const. static_assert( std::is_same_v>())>); // The arrow operator gives a (non-null) pointer to the internal reference. static_assert( std::is_same_v>().operator->())>); static_assert( std::is_same_v>().operator->())>); TEST(RawRef, Construct) { int i = 1; auto r = raw_ref(i); EXPECT_EQ(&*r, &i); auto cr = raw_ref(i); EXPECT_EQ(&*cr, &i); const int ci = 1; auto cci = raw_ref(ci); EXPECT_EQ(&*cci, &ci); } TEST(RawRef, CopyConstruct) { { int i = 1; auto r = raw_ref(i); EXPECT_EQ(&*r, &i); auto r2 = raw_ref(r); EXPECT_EQ(&*r2, &i); } { int i = 1; auto r = raw_ref(i); EXPECT_EQ(&*r, &i); auto r2 = raw_ref(r); EXPECT_EQ(&*r2, &i); } } TEST(RawRefDeathTest, CopyConstructAfterMove) { int i = 1; auto r = raw_ref(i); auto r2 = std::move(r); EXPECT_CHECK_DEATH({ [[maybe_unused]] auto r3 = r; }); } TEST(RawRef, MoveConstruct) { { int i = 1; auto r = raw_ref(i); EXPECT_EQ(&*r, &i); auto r2 = raw_ref(std::move(r)); EXPECT_EQ(&*r2, &i); } { int i = 1; auto r = raw_ref(i); EXPECT_EQ(&*r, &i); auto r2 = raw_ref(std::move(r)); EXPECT_EQ(&*r2, &i); } } TEST(RawRefDeathTest, MoveConstructAfterMove) { int i = 1; auto r = raw_ref(i); auto r2 = std::move(r); EXPECT_CHECK_DEATH({ [[maybe_unused]] auto r3 = std::move(r); }); } TEST(RawRef, CopyAssign) { { int i = 1; auto r = raw_ref(i); EXPECT_EQ(&*r, &i); int j = 2; auto rj = raw_ref(j); r = rj; EXPECT_EQ(&*r, &j); } { int i = 1; auto r = raw_ref(i); EXPECT_EQ(&*r, &i); int j = 2; auto rj = raw_ref(j); r = rj; EXPECT_EQ(&*r, &j); } { int i = 1; auto r = raw_ref(i); EXPECT_EQ(&*r, &i); int j = 2; auto rj = raw_ref(j); r = rj; EXPECT_EQ(&*r, &j); } } TEST(RawRefDeathTest, CopyAssignAfterMove) { int i = 1; auto r = raw_ref(i); auto r2 = std::move(r); EXPECT_CHECK_DEATH({ r2 = r; }); } TEST(RawRef, CopyReassignAfterMove) { int i = 1; auto r = raw_ref(i); auto r2 = std::move(r); int j = 1; r2 = raw_ref(j); // Reassign to the moved-from `r` so it can be used again. r = r2; EXPECT_EQ(&*r, &j); } TEST(RawRef, MoveAssign) { { int i = 1; auto r = raw_ref(i); EXPECT_EQ(&*r, &i); int j = 2; r = raw_ref(j); EXPECT_EQ(&*r, &j); } { int i = 1; auto r = raw_ref(i); EXPECT_EQ(&*r, &i); int j = 2; r = raw_ref(j); EXPECT_EQ(&*r, &j); } { int i = 1; auto r = raw_ref(i); EXPECT_EQ(&*r, &i); int j = 2; r = raw_ref(j); EXPECT_EQ(&*r, &j); } } TEST(RawRefDeathTest, MoveAssignAfterMove) { int i = 1; auto r = raw_ref(i); auto r2 = std::move(r); EXPECT_CHECK_DEATH({ r2 = std::move(r); }); } TEST(RawRef, MoveReassignAfterMove) { int i = 1; auto r = raw_ref(i); auto r2 = std::move(r); int j = 1; // Reassign to the moved-from `r` so it can be used again. r = raw_ref(j); EXPECT_EQ(&*r, &j); } TEST(RawRef, CopyConstructUpCast) { { auto s = SubClass(); auto r = raw_ref(s); EXPECT_EQ(&*r, &s); auto r2 = raw_ref(r); EXPECT_EQ(&*r2, &s); } { auto s = SubClass(); auto r = raw_ref(s); EXPECT_EQ(&*r, &s); auto r2 = raw_ref(r); EXPECT_EQ(&*r2, &s); } } TEST(RawRefDeathTest, CopyConstructAfterMoveUpCast) { auto s = SubClass(); auto r = raw_ref(s); auto moved = std::move(r); EXPECT_CHECK_DEATH({ [[maybe_unused]] auto r2 = raw_ref(r); }); } TEST(RawRef, MoveConstructUpCast) { { auto s = SubClass(); auto r = raw_ref(s); EXPECT_EQ(&*r, &s); auto r2 = raw_ref(std::move(r)); EXPECT_EQ(&*r2, &s); } { auto s = SubClass(); auto r = raw_ref(s); EXPECT_EQ(&*r, &s); auto r2 = raw_ref(std::move(r)); EXPECT_EQ(&*r2, &s); } } TEST(RawRefDeathTest, MoveConstructAfterMoveUpCast) { auto s = SubClass(); auto r = raw_ref(s); auto moved = std::move(r); EXPECT_CHECK_DEATH( { [[maybe_unused]] auto r2 = raw_ref(std::move(r)); }); } TEST(RawRef, CopyAssignUpCast) { { auto s = SubClass(); auto r = raw_ref(s); auto t = BaseClass(); auto rt = raw_ref(t); rt = r; EXPECT_EQ(&*rt, &s); } { auto s = SubClass(); auto r = raw_ref(s); auto t = BaseClass(); auto rt = raw_ref(t); rt = r; EXPECT_EQ(&*rt, &s); } { auto s = SubClass(); auto r = raw_ref(s); auto t = BaseClass(); auto rt = raw_ref(t); rt = r; EXPECT_EQ(&*rt, &s); } } TEST(RawRefDeathTest, CopyAssignAfterMoveUpCast) { auto s = SubClass(); auto r = raw_ref(s); auto t = BaseClass(); auto rt = raw_ref(t); auto moved = std::move(r); EXPECT_CHECK_DEATH({ rt = r; }); } TEST(RawRef, MoveAssignUpCast) { { auto s = SubClass(); auto r = raw_ref(s); auto t = BaseClass(); auto rt = raw_ref(t); rt = std::move(r); EXPECT_EQ(&*rt, &s); } { auto s = SubClass(); auto r = raw_ref(s); auto t = BaseClass(); auto rt = raw_ref(t); rt = std::move(r); EXPECT_EQ(&*rt, &s); } { auto s = SubClass(); auto r = raw_ref(s); auto t = BaseClass(); auto rt = raw_ref(t); rt = std::move(r); EXPECT_EQ(&*rt, &s); } } TEST(RawRefDeathTest, MoveAssignAfterMoveUpCast) { auto s = SubClass(); auto r = raw_ref(s); auto t = BaseClass(); auto rt = raw_ref(t); auto moved = std::move(r); EXPECT_CHECK_DEATH({ rt = std::move(r); }); } TEST(RawRef, Deref) { int i; auto r = raw_ref(i); EXPECT_EQ(&*r, &i); } TEST(RawRefDeathTest, DerefAfterMove) { int i; auto r = raw_ref(i); auto moved = std::move(r); EXPECT_CHECK_DEATH({ r.operator*(); }); } TEST(RawRef, Arrow) { int i; auto r = raw_ref(i); EXPECT_EQ(r.operator->(), &i); } TEST(RawRefDeathTest, ArrowAfterMove) { int i; auto r = raw_ref(i); auto moved = std::move(r); EXPECT_CHECK_DEATH({ r.operator->(); }); } TEST(RawRef, Swap) { int i; auto ri = raw_ref(i); int j; auto rj = raw_ref(j); swap(ri, rj); EXPECT_EQ(&*ri, &j); EXPECT_EQ(&*rj, &i); } TEST(RawRefDeathTest, SwapAfterMove) { { int i; auto ri = raw_ref(i); int j; auto rj = raw_ref(j); auto moved = std::move(ri); EXPECT_CHECK_DEATH({ swap(ri, rj); }); } { int i; auto ri = raw_ref(i); int j; auto rj = raw_ref(j); auto moved = std::move(rj); EXPECT_CHECK_DEATH({ swap(ri, rj); }); } } TEST(RawRef, Equals) { int i = 1; auto r1 = raw_ref(i); auto r2 = raw_ref(i); EXPECT_TRUE(r1 == r1); EXPECT_TRUE(r1 == r2); EXPECT_TRUE(r1 == i); EXPECT_TRUE(i == r1); int j = 1; auto r3 = raw_ref(j); EXPECT_FALSE(r1 == r3); EXPECT_FALSE(r1 == j); EXPECT_FALSE(j == r1); } TEST(RawRefDeathTest, EqualsAfterMove) { { int i = 1; auto r1 = raw_ref(i); auto r2 = raw_ref(i); auto moved = std::move(r1); EXPECT_CHECK_DEATH({ [[maybe_unused]] bool b = r1 == r2; }); } { int i = 1; auto r1 = raw_ref(i); auto r2 = raw_ref(i); auto moved = std::move(r2); EXPECT_CHECK_DEATH({ [[maybe_unused]] bool b = r1 == r2; }); } { int i = 1; auto r1 = raw_ref(i); auto moved = std::move(r1); EXPECT_CHECK_DEATH({ [[maybe_unused]] bool b = r1 == r1; }); } } TEST(RawRef, NotEquals) { int i = 1; auto r1 = raw_ref(i); int j = 1; auto r2 = raw_ref(j); EXPECT_TRUE(r1 != r2); EXPECT_TRUE(r1 != j); EXPECT_TRUE(j != r1); EXPECT_FALSE(r1 != r1); EXPECT_FALSE(r2 != j); EXPECT_FALSE(j != r2); } TEST(RawRefDeathTest, NotEqualsAfterMove) { { int i = 1; auto r1 = raw_ref(i); auto r2 = raw_ref(i); auto moved = std::move(r1); EXPECT_CHECK_DEATH({ [[maybe_unused]] bool b = r1 != r2; }); } { int i = 1; auto r1 = raw_ref(i); auto r2 = raw_ref(i); auto moved = std::move(r2); EXPECT_CHECK_DEATH({ [[maybe_unused]] bool b = r1 != r2; }); } { int i = 1; auto r1 = raw_ref(i); auto moved = std::move(r1); EXPECT_CHECK_DEATH({ [[maybe_unused]] bool b = r1 != r1; }); } } TEST(RawRef, LessThan) { int i[] = {1, 1}; auto r1 = raw_ref(i[0]); auto r2 = raw_ref(i[1]); EXPECT_TRUE(r1 < r2); EXPECT_TRUE(r1 < i[1]); EXPECT_FALSE(i[1] < r1); EXPECT_FALSE(r2 < r1); EXPECT_FALSE(r2 < i[0]); EXPECT_TRUE(i[0] < r2); EXPECT_FALSE(r1 < r1); EXPECT_FALSE(r1 < i[0]); EXPECT_FALSE(i[0] < r1); } TEST(RawRefDeathTest, LessThanAfterMove) { { int i = 1; auto r1 = raw_ref(i); auto r2 = raw_ref(i); auto moved = std::move(r1); EXPECT_CHECK_DEATH({ [[maybe_unused]] bool b = r1 < r2; }); } { int i = 1; auto r1 = raw_ref(i); auto r2 = raw_ref(i); auto moved = std::move(r2); EXPECT_CHECK_DEATH({ [[maybe_unused]] bool b = r1 < r2; }); } { int i = 1; auto r1 = raw_ref(i); auto moved = std::move(r1); EXPECT_CHECK_DEATH({ [[maybe_unused]] bool b = r1 < r1; }); } } TEST(RawRef, GreaterThan) { int i[] = {1, 1}; auto r1 = raw_ref(i[0]); auto r2 = raw_ref(i[1]); EXPECT_TRUE(r2 > r1); EXPECT_FALSE(r1 > r2); EXPECT_FALSE(r1 > i[1]); EXPECT_TRUE(i[1] > r1); EXPECT_FALSE(r2 > r2); EXPECT_FALSE(r2 > i[1]); EXPECT_FALSE(i[1] > r2); } TEST(RawRefDeathTest, GreaterThanAfterMove) { { int i = 1; auto r1 = raw_ref(i); auto r2 = raw_ref(i); auto moved = std::move(r1); EXPECT_CHECK_DEATH({ [[maybe_unused]] bool b = r1 > r2; }); } { int i = 1; auto r1 = raw_ref(i); auto r2 = raw_ref(i); auto moved = std::move(r2); EXPECT_CHECK_DEATH({ [[maybe_unused]] bool b = r1 > r2; }); } { int i = 1; auto r1 = raw_ref(i); auto moved = std::move(r1); EXPECT_CHECK_DEATH({ [[maybe_unused]] bool b = r1 > r1; }); } } TEST(RawRef, LessThanOrEqual) { int i[] = {1, 1}; auto r1 = raw_ref(i[0]); auto r2 = raw_ref(i[1]); EXPECT_TRUE(r1 <= r2); EXPECT_TRUE(r1 <= r1); EXPECT_TRUE(r2 <= r2); EXPECT_FALSE(r2 <= r1); EXPECT_TRUE(r1 <= i[1]); EXPECT_TRUE(r1 <= i[0]); EXPECT_TRUE(r2 <= i[1]); EXPECT_FALSE(r2 <= i[0]); EXPECT_FALSE(i[1] <= r1); EXPECT_TRUE(i[0] <= r1); EXPECT_TRUE(i[1] <= r2); EXPECT_TRUE(i[0] <= r2); } TEST(RawRefDeathTest, LessThanOrEqualAfterMove) { { int i = 1; auto r1 = raw_ref(i); auto r2 = raw_ref(i); auto moved = std::move(r1); EXPECT_CHECK_DEATH({ [[maybe_unused]] bool b = r1 <= r2; }); } { int i = 1; auto r1 = raw_ref(i); auto r2 = raw_ref(i); auto moved = std::move(r2); EXPECT_CHECK_DEATH({ [[maybe_unused]] bool b = r1 <= r2; }); } { int i = 1; auto r1 = raw_ref(i); auto moved = std::move(r1); EXPECT_CHECK_DEATH({ [[maybe_unused]] bool b = r1 <= r1; }); } } TEST(RawRef, GreaterThanOrEqual) { int i[] = {1, 1}; auto r1 = raw_ref(i[0]); auto r2 = raw_ref(i[1]); EXPECT_TRUE(r2 >= r1); EXPECT_TRUE(r1 >= r1); EXPECT_TRUE(r2 >= r2); EXPECT_FALSE(r1 >= r2); EXPECT_TRUE(r2 >= i[0]); EXPECT_TRUE(r1 >= i[0]); EXPECT_TRUE(r2 >= i[1]); EXPECT_FALSE(r1 >= i[1]); EXPECT_FALSE(i[0] >= r2); EXPECT_TRUE(i[0] >= r1); EXPECT_TRUE(i[1] >= r2); EXPECT_TRUE(i[1] >= r1); } TEST(RawRefDeathTest, GreaterThanOrEqualAfterMove) { { int i = 1; auto r1 = raw_ref(i); auto r2 = raw_ref(i); auto moved = std::move(r1); EXPECT_CHECK_DEATH({ [[maybe_unused]] bool b = r1 >= r2; }); } { int i = 1; auto r1 = raw_ref(i); auto r2 = raw_ref(i); auto moved = std::move(r2); EXPECT_CHECK_DEATH({ [[maybe_unused]] bool b = r1 >= r2; }); } { int i = 1; auto r1 = raw_ref(i); auto moved = std::move(r1); EXPECT_CHECK_DEATH({ [[maybe_unused]] bool b = r1 >= r1; }); } } TEST(RawRef, CTAD) { int i = 1; auto r = raw_ref(i); EXPECT_EQ(&*r, &i); } using RawPtrCountingImpl = base::internal::RawPtrCountingImplWrapperForTest; template using CountingRawRef = raw_ref; TEST(RawRef, StdLess) { int i[] = {1, 1}; { RawPtrCountingImpl::ClearCounters(); auto r1 = CountingRawRef(i[0]); auto r2 = CountingRawRef(i[1]); EXPECT_TRUE(std::less>()(r1, r2)); EXPECT_FALSE(std::less>()(r2, r1)); EXPECT_EQ(2, RawPtrCountingImpl::wrapped_ptr_less_cnt); } { RawPtrCountingImpl::ClearCounters(); const auto r1 = CountingRawRef(i[0]); const auto r2 = CountingRawRef(i[1]); EXPECT_TRUE(std::less>()(r1, r2)); EXPECT_FALSE(std::less>()(r2, r1)); EXPECT_EQ(2, RawPtrCountingImpl::wrapped_ptr_less_cnt); } { RawPtrCountingImpl::ClearCounters(); auto r1 = CountingRawRef(i[0]); auto r2 = CountingRawRef(i[1]); EXPECT_TRUE(std::less>()(r1, r2)); EXPECT_FALSE(std::less>()(r2, r1)); EXPECT_EQ(2, RawPtrCountingImpl::wrapped_ptr_less_cnt); } { RawPtrCountingImpl::ClearCounters(); auto r1 = CountingRawRef(i[0]); auto r2 = CountingRawRef(i[1]); EXPECT_TRUE(std::less>()(r1, i[1])); EXPECT_FALSE(std::less>()(r2, i[0])); EXPECT_EQ(2, RawPtrCountingImpl::wrapped_ptr_less_cnt); } { RawPtrCountingImpl::ClearCounters(); const auto r1 = CountingRawRef(i[0]); const auto r2 = CountingRawRef(i[1]); EXPECT_TRUE(std::less>()(r1, i[1])); EXPECT_FALSE(std::less>()(r2, i[0])); EXPECT_EQ(2, RawPtrCountingImpl::wrapped_ptr_less_cnt); } { RawPtrCountingImpl::ClearCounters(); auto r1 = CountingRawRef(i[0]); auto r2 = CountingRawRef(i[1]); EXPECT_TRUE(std::less>()(r1, i[1])); EXPECT_FALSE(std::less>()(r2, i[0])); EXPECT_EQ(2, RawPtrCountingImpl::wrapped_ptr_less_cnt); } } } // namespace