1
    2
    3
    4
    5
    6
    7
    8
    9
   10
   11
   12
   13
   14
   15
   16
   17
   18
   19
   20
   21
   22
   23
   24
   25
   26
   27
   28
   29
   30
   31
   32
   33
   34
   35
   36
   37
   38
   39
   40
   41
   42
   43
   44
   45
   46
   47
   48
   49
   50
   51
   52
   53
   54
   55
   56
   57
   58
   59
   60
   61
   62
   63
   64
   65
   66
   67
   68
   69
   70
   71
   72
   73
   74
   75
   76
   77
   78
   79
   80
   81
   82
   83
   84
   85
   86
   87
   88
   89
   90
   91
   92
   93
   94
   95
   96
   97
   98
   99
  100
  101
  102
  103
  104
  105
  106
  107
  108
  109
  110
  111
  112
  113
  114
  115
  116
  117
  118
  119
  120
  121
  122
  123
  124
  125
  126
  127
  128
  129
  130
  131
  132
  133
  134
  135
  136
  137
  138
  139
  140
  141
  142
  143
  144
  145
  146
  147
  148
  149

base / allocator / partition_allocator / src / partition_alloc / pointers / raw_ptr_nocompile.nc [blame]

// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// This is a "No Compile Test" suite.
// http://dev.chromium.org/developers/testing/no-compile-tests

#include <memory>
#include <utility>

#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "partition_alloc/pointers/raw_ptr.h"
#include "partition_alloc/buildflags.h"

namespace {

// Using distinct enum types causes distinct `raw_ptr` template instantiations,
// so we get assertion failures below where we expect.
enum TypeA {};
enum TypeB {};

void UnknownTraits() {
  constexpr auto InvalidRawPtrTrait = static_cast<base::RawPtrTraits>(-1);
  raw_ptr<TypeA, InvalidRawPtrTrait> ptr_a;                                // expected-error@*:* {{Unknown raw_ptr trait(s)}}
  raw_ptr<TypeB, DisableDanglingPtrDetection | InvalidRawPtrTrait> ptr_b;  // expected-error@*:* {{Unknown raw_ptr trait(s)}}
}

void DifferentTypeAssignment() {
  struct Unrelated {};
  struct Producer {} p;
  struct DerivedProducer : public Producer {} dp;
  raw_ptr<Producer> ptr_p = &p;
  raw_ptr<DerivedProducer> ptr_dp1 = &dp;

  // Conversion
  raw_ptr<DerivedProducer> ptr_dp2 = ptr_p;  // expected-error {{no viable conversion from 'raw_ptr<Producer>' to 'raw_ptr<DerivedProducer>'}}
  raw_ptr<DerivedProducer> ptr_dp3 =
      static_cast<raw_ptr<DerivedProducer>>(ptr_p);  // expected-error {{no matching conversion for static_cast from 'raw_ptr<Producer>' to 'raw_ptr<DerivedProducer>'}}
  raw_ptr<DerivedProducer> ptr_dp4 = &p;             // expected-error {{no viable conversion from 'struct Producer *' to 'raw_ptr<DerivedProducer>'}}
  raw_ptr<Unrelated> ptr_u1 = &dp;                   // expected-error {{no viable conversion from 'struct DerivedProducer *' to 'raw_ptr<Unrelated>'}}

  // Reference binding
  raw_ptr<DerivedProducer>& ptr_dp5 = ptr_p;  // expected-error {{non-const lvalue reference to type 'raw_ptr<DerivedProducer>' cannot bind to a value of unrelated type 'raw_ptr<Producer>'}}
  raw_ptr<DerivedProducer>& ptr_dp6 =
      static_cast<raw_ptr<DerivedProducer>&>(ptr_p);  // expected-error {{non-const lvalue reference to type 'raw_ptr<DerivedProducer>' cannot bind to a value of unrelated type 'raw_ptr<Producer>'}}

  // Casting
  auto* ptr_u2 = static_cast<Unrelated*>(ptr_dp1);  // expected-error@*:* {{static_cast from 'DerivedProducer *' to 'Unrelated *', which are not related by inheritance, is not allowed}}
}

void DereferenceVoidPtr() {
  constexpr char kFoo[] = "42";
  raw_ptr<const void> ptr = kFoo;
  *ptr;  // expected-error {{indirection requires pointer operand ('raw_ptr<const void>' invalid)}}
}

void FunctionPointerType() {
  raw_ptr<void(int)> ptr;  // expected-error@*:* {{raw_ptr<T> doesn't work with this kind of pointee type T}}
}

void Dangling() {
  [[maybe_unused]] raw_ptr<int> ptr = std::make_unique<int>(2).get();  // expected-error {{object backing the pointer will be destroyed at the end of the full-expression}}
}

void BindRawPtrParam() {
  // `raw_ptr` is not intended to be used as a function param type, so trying to
  // bind to a function with a `raw_ptr<T>` param should error out.
  raw_ptr<int> ptr = new int(3);
  base::BindOnce([](raw_ptr<int> ptr) {}, ptr);  // expected-error@*:* {{Use T* or T& instead of raw_ptr<T> for function parameters, unless you must mark the parameter as MayBeDangling<T>.}}
}

void PointerArithmetic() {
  using PtrCanDoArithmetic =
      raw_ptr<int, base::RawPtrTraits::kAllowPtrArithmetic>;

  PtrCanDoArithmetic ptr1 = new int(3);
  struct {} s;
  ptr1 += s;                           // expected-error@*:* {{no viable overloaded '+='}}
  ptr1 -= s;                           // expected-error@*:* {{no viable overloaded '-='}}
  PtrCanDoArithmetic ptr2 = ptr1 + s;  // expected-error@*:* {{no matching function for call to 'Advance'}}
  ptr2 = ptr1 - s;                     // expected-error@*:* {{no matching function for call to 'Retreat'}}

#if !PA_BUILDFLAG(HAS_64_BIT_POINTERS)
  ptr1 += uint64_t{2};        // expected-error@*:* {{no viable overloaded '+='}}
  ptr1 -= uint64_t{2};        // expected-error@*:* {{no viable overloaded '-='}}
  ptr2 = ptr1 + uint64_t{2};  // expected-error@*:* {{no matching function for call to 'Advance'}}
  ptr2 = ptr1 - uint64_t{2};  // expected-error@*:* {{no matching function for call to 'Retreat'}}
#endif  // !PA_BUILDFLAG(HAS_64_BIT_POINTERS)
}

#if PA_BUILDFLAG(ENABLE_POINTER_ARITHMETIC_TRAIT_CHECK)
void PointerArithmeticDisabled() {
  raw_ptr<TypeA> ptr_a1 = new TypeA();
  ptr_a1++;                            // expected-error@*:* {{cannot increment raw_ptr unless AllowPtrArithmetic trait is present.}}
  ptr_a1--;                            // expected-error@*:* {{cannot decrement raw_ptr unless AllowPtrArithmetic trait is present.}}
  ++ptr_a1;                            // expected-error@*:* {{cannot increment raw_ptr unless AllowPtrArithmetic trait is present.}}
  --ptr_a1;                            // expected-error@*:* {{cannot decrement raw_ptr unless AllowPtrArithmetic trait is present.}}
  raw_ptr<TypeA> ptr_a2 = ptr_a1 + 1;  // expected-error@*:* {{cannot add to raw_ptr unless AllowPtrArithmetic trait is present.}}
  ptr_a2 = ptr_a1 - 1;                 // expected-error@*:* {{cannot subtract from raw_ptr unless AllowPtrArithmetic trait is present.}}
  raw_ptr<TypeB> ptr_b1 = new TypeB();
  raw_ptr<TypeB> ptr_b2 = 1 + ptr_b1;  // expected-error@*:* {{cannot add to raw_ptr unless AllowPtrArithmetic trait is present.}}
  ptr_b2 - ptr_b1;                     // expected-error@*:* {{cannot subtract raw_ptrs unless AllowPtrArithmetic trait is present.}}
}

void Indexing() {
  raw_ptr<int> ptr = new int(3);
  [[maybe_unused]] int val = ptr[1];  // expected-error@*:* {{cannot index raw_ptr unless AllowPtrArithmetic trait is present.}}
}
#endif

using DanglingPtrA = raw_ptr<TypeA, base::RawPtrTraits::kMayDangle>;
using DanglingPtrB = raw_ptr<TypeB, base::RawPtrTraits::kMayDangle>;

void CrossKindConversionFromMayDangle() {
  // Conversions may add the `kMayDangle` trait, but not remove it.
  DanglingPtrA ptr_a1 = new TypeA();
  DanglingPtrB ptr_b1 = new TypeB();
  raw_ptr<TypeA> ptr_a2 = ptr_a1;             // expected-error {{no viable conversion from 'raw_ptr<[...], base::RawPtrTraits::kMayDangle aka 1>' to 'raw_ptr<[...], (default) RawPtrTraits::kEmpty aka 0>'}}
  raw_ptr<TypeA> ptr_a3(ptr_a1);              // expected-error@*:* {{static assertion failed due to requirement 'Traits == (raw_ptr<(anonymous namespace)::TypeA, partition_alloc::internal::RawPtrTraits::kMayDangle>::Traits | RawPtrTraits::kMayDangle)'}}
  raw_ptr<TypeA> ptr_a4 = std::move(ptr_a1);  // expected-error {{no viable conversion from '__libcpp_remove_reference_t<raw_ptr<TypeA, partition_alloc::internal::RawPtrTraits::kMayDangle> &>' (aka 'base::raw_ptr<(anonymous namespace)::TypeA, partition_alloc::internal::RawPtrTraits::kMayDangle>') to 'raw_ptr<TypeA>'}}
  raw_ptr<TypeB> ptr_b2(std::move(ptr_b1));   // expected-error@*:* {{static assertion failed due to requirement 'Traits == (raw_ptr<(anonymous namespace)::TypeB, partition_alloc::internal::RawPtrTraits::kMayDangle>::Traits | RawPtrTraits::kMayDangle)'}}
}

void CrossKindConversionFromDummy() {
  // Only the `kMayDangle` trait can change in an implicit conversion.
  raw_ptr<TypeA, base::RawPtrTraits::kDummyForTest> ptr_a1 = new TypeA();
  raw_ptr<TypeB, base::RawPtrTraits::kDummyForTest> ptr_b1 = new TypeB();
  DanglingPtrA ptr_a2 = ptr_a1;             // expected-error {{no viable conversion from 'raw_ptr<[...], base::RawPtrTraits::kDummyForTest aka 2048>' to 'raw_ptr<[...], base::RawPtrTraits::kMayDangle aka 1>'}}
  DanglingPtrA ptr_a3(ptr_a1);              // expected-error@*:* {{static assertion failed due to requirement 'Traits == (raw_ptr<(anonymous namespace)::TypeA, partition_alloc::internal::RawPtrTraits::kDummyForTest>::Traits | RawPtrTraits::kMayDangle)'}}
  DanglingPtrA ptr_a4 = std::move(ptr_a1);  // expected-error {{no viable conversion from '__libcpp_remove_reference_t<raw_ptr<TypeA, partition_alloc::internal::RawPtrTraits::kDummyForTest> &>' (aka 'base::raw_ptr<(anonymous namespace)::TypeA, partition_alloc::internal::RawPtrTraits::kDummyForTest>') to 'DanglingPtrA' (aka 'raw_ptr<TypeA, base::RawPtrTraits::kMayDangle>')}}
  DanglingPtrB ptr_b2(std::move(ptr_b1));   // expected-error@*:* {{static assertion failed due to requirement 'Traits == (raw_ptr<(anonymous namespace)::TypeB, partition_alloc::internal::RawPtrTraits::kDummyForTest>::Traits | RawPtrTraits::kMayDangle)'}}
}

void CantStorePointerObtainedFromEphemeralRawAddr() {
   int v = 123;
   raw_ptr<int> ptr = &v;
   int** wont_work = &ptr.AsEphemeralRawAddr();  // expected-error {{temporary whose address is used as value of local variable 'wont_work' will be destroyed at the end of the full-expression}}
   *wont_work = nullptr;
}

void CantStoreReferenceObtainedFromEphemeralRawAddr() {
   int v = 123;
   raw_ptr<int> ptr = &v;
   int*& wont_work = ptr.AsEphemeralRawAddr();  // expected-error {{temporary bound to local reference 'wont_work' will be destroyed at the end of the full-expression}}
   wont_work = nullptr;
}

}  // namespace