blob: c6ad7b83182a35936458e7aa5f83c3bbcc954e42 [file]
// Copyright 2026 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// raw_ptr_unittest.cpp: Tests for raw_ptr.
//
// These tests are run against both the no-op implementation and
// PartitionAlloc's one, so that the two do not diverge.
#include "common/partition_alloc/raw_ptr.h"
#include "common/unsafe_buffers.h"
#include <gtest/gtest.h>
#include <cstddef>
#include <memory>
#include <set>
#include <tuple>
#include <type_traits>
#include <unordered_set>
#include <utility>
namespace
{
struct Base
{
virtual ~Base() = default;
int baseValue = 1;
};
struct Derived : public Base
{
int derivedValue = 2;
};
// Tests that default construction initializes to nullptr.
TEST(RawPtrTest, DefaultConstruction)
{
raw_ptr<int> p;
EXPECT_EQ(p.get(), nullptr);
EXPECT_FALSE(p);
}
// Tests that construction from nullptr works.
TEST(RawPtrTest, NullptrConstruction)
{
raw_ptr<int> p = nullptr;
EXPECT_EQ(p.get(), nullptr);
EXPECT_FALSE(p);
}
// Tests that construction from a raw pointer works.
TEST(RawPtrTest, RawPointerConstruction)
{
int value = 42;
raw_ptr<int> p = &value;
EXPECT_EQ(p.get(), &value);
EXPECT_TRUE(p);
EXPECT_EQ(*p, 42);
}
// Tests copy and move construction. Both the no-op implementation and
// PartitionAlloc's one zero the moved-from pointer.
TEST(RawPtrTest, CopyAndMoveConstruction)
{
int value = 42;
raw_ptr<int> p1 = &value;
raw_ptr<int> p2 = p1;
EXPECT_EQ(p2.get(), &value);
EXPECT_EQ(p1.get(), &value);
raw_ptr<int> p3 = std::move(p2);
EXPECT_EQ(p3.get(), &value);
EXPECT_EQ(p2.get(), nullptr); // NOLINT(bugprone-use-after-move)
}
// Tests assignment from raw_ptr, raw pointer and nullptr.
TEST(RawPtrTest, Assignment)
{
int value1 = 42;
int value2 = 84;
raw_ptr<int> p1 = &value1;
raw_ptr<int> p2 = &value2;
p1 = p2;
EXPECT_EQ(p1.get(), &value2);
p1 = nullptr;
EXPECT_EQ(p1.get(), nullptr);
p1 = &value1;
EXPECT_EQ(p1.get(), &value1);
raw_ptr<int> p3;
p3 = std::move(p1);
EXPECT_EQ(p3.get(), &value1);
EXPECT_EQ(p1.get(), nullptr); // NOLINT(bugprone-use-after-move)
}
// Tests that self assignment does not clobber the pointer.
TEST(RawPtrTest, SelfAssignment)
{
int value = 42;
raw_ptr<int> p = &value;
// Going through a pointer avoids the compiler's self-assignment warning.
raw_ptr<int> *alias = &p;
p = *alias;
EXPECT_EQ(p.get(), &value);
p = std::move(*alias);
EXPECT_EQ(p.get(), &value);
}
// Tests implicit conversion from raw_ptr<Derived> to raw_ptr<Base>.
TEST(RawPtrTest, Upcasting)
{
Derived derived;
raw_ptr<Derived> pDerived = &derived;
raw_ptr<Base> pBase(pDerived);
EXPECT_EQ(pBase.get(), &derived);
EXPECT_EQ(pBase->baseValue, 1);
raw_ptr<Base> pBaseAssigned;
pBaseAssigned = pDerived;
EXPECT_EQ(pBaseAssigned.get(), &derived);
raw_ptr<Derived> pDerivedMoved = &derived;
raw_ptr<Base> pBaseMoved(std::move(pDerivedMoved));
EXPECT_EQ(pBaseMoved.get(), &derived);
EXPECT_EQ(pDerivedMoved.get(), nullptr); // NOLINT(bugprone-use-after-move)
raw_ptr<Derived> pDerivedMoved2 = &derived;
raw_ptr<Base> pBaseMoveAssigned;
pBaseMoveAssigned = std::move(pDerivedMoved2);
EXPECT_EQ(pBaseMoveAssigned.get(), &derived);
EXPECT_EQ(pDerivedMoved2.get(), nullptr); // NOLINT(bugprone-use-after-move)
}
// Tests dereferencing and member access.
TEST(RawPtrTest, DereferenceAndMemberAccess)
{
struct S
{
int x = 42;
};
S s = {};
raw_ptr<S> p = &s;
EXPECT_EQ(p->x, 42);
EXPECT_EQ((*p).x, 42);
EXPECT_EQ(&*p, &s);
}
// Tests pointer arithmetic, which requires the AllowPtrArithmetic trait.
TEST(RawPtrTest, PointerArithmetic)
{
// SAFETY: Exercising pointer arithmetic is the point of this test. Every access below stays
// within the bounds of `values`.
ANGLE_UNSAFE_BUFFERS({
int values[3] = {10, 20, 30};
raw_ptr<int, AllowPtrArithmetic> p = &values[0];
EXPECT_EQ(p, &values[0]);
// Pre-increment returns a reference to the incremented pointer.
raw_ptr<int, AllowPtrArithmetic> &preIncremented = ++p;
EXPECT_EQ(&preIncremented, &p);
EXPECT_EQ(p, &values[1]);
// Post-increment returns the value from before the increment.
raw_ptr<int, AllowPtrArithmetic> previous = p++;
EXPECT_EQ(previous, &values[1]);
EXPECT_EQ(p, &values[2]);
// Pre-decrement returns a reference to the decremented pointer.
raw_ptr<int, AllowPtrArithmetic> &preDecremented = --p;
EXPECT_EQ(&preDecremented, &p);
EXPECT_EQ(p, &values[1]);
// Post-decrement returns the value from before the decrement.
previous = p--;
EXPECT_EQ(previous, &values[1]);
EXPECT_EQ(p, &values[0]);
static_assert(std::is_same_v<decltype(p + 1), raw_ptr<int, AllowPtrArithmetic>>);
static_assert(std::is_same_v<decltype(1 + p), raw_ptr<int, AllowPtrArithmetic>>);
static_assert(std::is_same_v<decltype(p - 1), raw_ptr<int, AllowPtrArithmetic>>);
static_assert(std::is_same_v<decltype(p - p), ptrdiff_t>);
EXPECT_EQ(p + 1, &values[1]);
EXPECT_EQ(1 + p, &values[1]);
EXPECT_EQ(p + 2, &values[2]);
raw_ptr<int, AllowPtrArithmetic> last = &values[2];
EXPECT_EQ(last - 1, &values[1]);
EXPECT_EQ(last - 2, &values[0]);
// Difference between two raw_ptr.
EXPECT_EQ(last - p, 2);
p += 2;
EXPECT_EQ(p, &values[2]);
p -= 2;
EXPECT_EQ(p, &values[0]);
EXPECT_EQ(p[1], 20);
});
}
// Tests equality comparisons against raw_ptr, raw pointers and nullptr, in both
// operand orders.
TEST(RawPtrTest, EqualityComparisons)
{
int value1 = 42;
int value2 = 84;
raw_ptr<int> p1 = &value1;
raw_ptr<int> p2 = &value2;
raw_ptr<int> null = nullptr;
EXPECT_TRUE(p1 == p1);
EXPECT_FALSE(p1 == p2);
EXPECT_TRUE(p1 != p2);
EXPECT_TRUE(p1 == &value1);
EXPECT_TRUE(&value1 == p1);
EXPECT_FALSE(p1 == &value2);
EXPECT_FALSE(&value2 == p1);
EXPECT_TRUE(null == nullptr);
EXPECT_TRUE(nullptr == null);
EXPECT_FALSE(p1 == nullptr);
EXPECT_FALSE(nullptr == p1);
}
// Tests relational comparisons (<, <=, >, >=) in both operand orders.
TEST(RawPtrTest, RelationalComparisons)
{
int values[2] = {42, 84};
raw_ptr<int> p0 = &values[0];
raw_ptr<int> p1 = &values[1];
EXPECT_TRUE(p0 < p1);
EXPECT_TRUE(p0 <= p1);
EXPECT_TRUE(p1 > p0);
EXPECT_TRUE(p1 >= p0);
EXPECT_FALSE(p1 < p0);
EXPECT_FALSE(p1 <= p0);
EXPECT_FALSE(p0 > p1);
EXPECT_FALSE(p0 >= p1);
EXPECT_TRUE(p0 <= p0);
EXPECT_TRUE(p0 >= p0);
EXPECT_TRUE(p0 < &values[1]);
EXPECT_TRUE(p0 <= &values[1]);
EXPECT_TRUE(&values[1] > p0);
EXPECT_TRUE(&values[1] >= p0);
}
// Tests const pointee types.
TEST(RawPtrTest, ConstPointee)
{
const int value = 100;
raw_ptr<const int> p = &value;
EXPECT_EQ(p.get(), &value);
EXPECT_EQ(*p, 100);
}
// Tests void pointee types, which do not support dereferencing.
TEST(RawPtrTest, Void)
{
int value = 42;
raw_ptr<void> p = &value;
EXPECT_EQ(p.get(), &value);
EXPECT_TRUE(p);
raw_ptr<const void> constPointer = &value;
EXPECT_EQ(constPointer.get(), &value);
}
// Tests interoperability with the standard library: std::less (through
// std::set), std::hash (through std::unordered_set) and std::to_address.
TEST(RawPtrTest, StandardLibrary)
{
int value1 = 1;
int value2 = 2;
raw_ptr<int> p1 = &value1;
raw_ptr<int> p2 = &value2;
std::set<raw_ptr<int>> set;
set.insert(p1);
set.insert(p2);
EXPECT_EQ(set.size(), 2u);
EXPECT_NE(set.find(p1), set.end());
// std::less is transparent, so lookup with a raw pointer creates no
// temporary raw_ptr.
EXPECT_NE(set.find(&value1), set.end());
std::unordered_set<raw_ptr<int>> unorderedSet;
unorderedSet.insert(p1);
unorderedSet.insert(p2);
EXPECT_EQ(unorderedSet.size(), 2u);
EXPECT_NE(unorderedSet.find(p1), unorderedSet.end());
EXPECT_EQ(std::to_address(p1), &value1);
}
// Tests that a raw_ptr marked with DisableDanglingPtrDetection is allowed to
// dangle.
TEST(RawPtrTest, DisableDanglingPtrDetection)
{
std::unique_ptr<int> owner = std::make_unique<int>(42);
raw_ptr<int, DisableDanglingPtrDetection> danger = owner.get();
std::ignore = danger;
owner.reset();
}
// Tests ExtractAsDangling, which clears the pointer and returns a raw_ptr
// allowed to dangle.
TEST(RawPtrTest, ExtractAsDangling)
{
int value = 42;
raw_ptr<int> p = &value;
raw_ptr<int, DisableDanglingPtrDetection> dangling = p.ExtractAsDangling();
EXPECT_EQ(dangling.get(), &value);
EXPECT_EQ(p.get(), nullptr);
}
} // namespace