reference, declarationdefinition
definition → references, declarations, derived classes, virtual overrides
reference to multiple definitions → definitions
unreferenced
    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
  150
  151
  152
  153
  154
  155
  156
  157
  158
  159
  160
  161
  162
  163
  164
  165
  166
  167
  168
  169
  170
  171
  172
  173
  174
  175
  176
  177
  178
// RUN: %clang_cc1 -std=c++2a -verify %s -DNEW=__builtin_operator_new -DDELETE=__builtin_operator_delete
// RUN: %clang_cc1 -std=c++2a -verify %s "-DNEW=operator new" "-DDELETE=operator delete"
// RUN: %clang_cc1 -std=c++2a -verify %s "-DNEW=::operator new" "-DDELETE=::operator delete"

constexpr bool alloc_from_user_code() {
  void *p = NEW(sizeof(int)); // expected-note {{cannot allocate untyped memory in a constant expression; use 'std::allocator<T>::allocate'}}
  DELETE(p);
  return true;
}
static_assert(alloc_from_user_code()); // expected-error {{constant expression}} expected-note {{in call}}

namespace std {
  using size_t = decltype(sizeof(0));
  // FIXME: It would be preferable to point these notes at the location of the call to allocator<...>::[de]allocate instead
  template<typename T> struct allocator {
    constexpr T *allocate(size_t N) {
      return (T*)NEW(sizeof(T) * N); // expected-note 3{{heap allocation}} expected-note {{not deallocated}}
    }
    constexpr void deallocate(void *p) {
      DELETE(p); // expected-note 2{{'std::allocator<...>::deallocate' used to delete pointer to object allocated with 'new'}}
    }
  };
}

constexpr bool alloc_via_std_allocator() {
  std::allocator<int> alloc;
  int *p = alloc.allocate(1);
  alloc.deallocate(p);
  return true;
}
static_assert(alloc_via_std_allocator());

template<> struct std::allocator<void()> {
  constexpr void *allocate() { return NEW(8); } // expected-note {{cannot allocate memory of function type 'void ()'}}
};
constexpr void *fn = std::allocator<void()>().allocate(); // expected-error {{constant expression}} expected-note {{in call}}

struct Incomplete;
template<> struct std::allocator<Incomplete> {
  constexpr void *allocate() { return NEW(8); } // expected-note {{cannot allocate memory of incomplete type 'Incomplete'}}
};
constexpr void *incomplete = std::allocator<Incomplete>().allocate(); // expected-error {{constant expression}} expected-note {{in call}}

struct WrongSize { char x[5]; };
static_assert(sizeof(WrongSize) == 5);
template<> struct std::allocator<WrongSize> {
  constexpr void *allocate() { return NEW(7); } // expected-note {{allocated size 7 is not a multiple of size 5 of element type 'WrongSize'}}
};
constexpr void *wrong_size = std::allocator<WrongSize>().allocate(); // expected-error {{constant expression}} expected-note {{in call}}

constexpr bool mismatched(int alloc_kind, int dealloc_kind) {
  int *p;
  switch (alloc_kind) {
  case 0:
    p = new int; // expected-note {{heap allocation}}
    break;
  case 1:
    p = new int[1]; // expected-note {{heap allocation}}
    break;
  case 2:
    p = std::allocator<int>().allocate(1);
    break;
  }
  switch (dealloc_kind) {
  case 0:
    delete p; // expected-note {{'delete' used to delete pointer to object allocated with 'std::allocator<...>::allocate'}}
    break;
  case 1:
    delete[] p; // expected-note {{'delete' used to delete pointer to object allocated with 'std::allocator<...>::allocate'}}
    break;
  case 2:
    std::allocator<int>().deallocate(p); // expected-note 2{{in call}}
    break;
  }
  return true;
}
static_assert(mismatched(0, 2)); // expected-error {{constant expression}} expected-note {{in call}}
static_assert(mismatched(1, 2)); // expected-error {{constant expression}} expected-note {{in call}}
static_assert(mismatched(2, 0)); // expected-error {{constant expression}} expected-note {{in call}}
static_assert(mismatched(2, 1)); // expected-error {{constant expression}} expected-note {{in call}}
static_assert(mismatched(2, 2));

constexpr int *escape = std::allocator<int>().allocate(3); // expected-error {{constant expression}} expected-note {{pointer to subobject of heap-allocated}}
constexpr int leak = (std::allocator<int>().allocate(3), 0); // expected-error {{constant expression}}
constexpr int no_lifetime_start = (*std::allocator<int>().allocate(1) = 1); // expected-error {{constant expression}} expected-note {{assignment to object outside its lifetime}}

void *operator new(std::size_t, void *p) { return p; }
constexpr bool no_placement_new_in_user_code() { // expected-error {{never produces a constant expression}}
  int a;
  new (&a) int(42); // expected-note {{call to placement 'operator new'}}
  return a == 42;
}

namespace std {
  constexpr bool placement_new_in_stdlib() {
    int a;
    new (&a) int(42);
    return a == 42;
  }
}
static_assert(std::placement_new_in_stdlib());

namespace std {
  template<typename T, typename ...Args>
  constexpr void construct_at(void *p, Args &&...args) {
    new (p) T((Args&&)args...); // #new
  }
}

constexpr bool call_std_construct_at() {
  int *p = std::allocator<int>().allocate(3);
  std::construct_at<int>(p, 1);
  std::construct_at<int>(p + 1, 2);
  std::construct_at<int>(p + 2, 3);
  bool good = p[0] + p[1] + p[2] == 6;
  std::allocator<int>().deallocate(p);
  return good;
}
static_assert(call_std_construct_at());

constexpr bool bad_construct_at_type() {
  int a;
  // expected-note@#new {{placement new would change type of storage from 'int' to 'float'}}
  std::construct_at<float>(&a, 1.0f); // expected-note {{in call}}
  return true;
}
static_assert(bad_construct_at_type()); // expected-error{{}} expected-note {{in call}}

constexpr bool bad_construct_at_subobject() {
  struct X { int a, b; };
  union A {
    int a;
    X x;
  };
  A a = {1};
  // expected-note@#new {{construction of subobject of member 'x' of union with active member 'a' is not allowed in a constant expression}}
  std::construct_at<int>(&a.x.a, 1); // expected-note {{in call}}
  return true;
}
static_assert(bad_construct_at_subobject()); // expected-error{{}} expected-note {{in call}}

constexpr bool change_union_member() {
  union U {
    int a;
    int b;
  };
  U u = {.a = 1};
  std::construct_at<int>(&u.b, 2);
  return u.b == 2;
}
static_assert(change_union_member());

int external;
// expected-note@#new {{visible outside}}
static_assert((std::construct_at<int>(&external, 1), true)); // expected-error{{}} expected-note {{in call}}

constexpr int &&temporary = 0; // expected-note {{created here}}
// expected-note@#new {{construction of temporary is not allowed in a constant expression outside the expression that created the temporary}}
static_assert((std::construct_at<int>(&temporary, 1), true)); // expected-error{{}} expected-note {{in call}}

constexpr bool construct_after_lifetime() {
  int *p = new int;
  delete p;
  // expected-note@#new {{construction of heap allocated object that has been deleted}}
  std::construct_at<int>(p); // expected-note {{in call}}
  return true;
}
static_assert(construct_after_lifetime()); // expected-error {{}} expected-note {{in call}}

constexpr bool construct_after_lifetime_2() {
  struct A { struct B {} b; };
  A a;
  a.~A();
  std::construct_at<A::B>(&a.b); // expected-note {{in call}}
  // expected-note@#new {{construction of subobject of object outside its lifetime is not allowed in a constant expression}}
  return true;
}
static_assert(construct_after_lifetime_2()); // expected-error {{}} expected-note {{in call}}