brintos

brintos / llvm-project-archived public Read only

0
0
Text · 5.1 KiB · 71c38c5 Raw
169 lines · cpp
1// RUN: %clang_cc1 -std=c++17 -verify %s2// RUN: %clang_cc1 -std=c++20 -verify %s3 4using intptr_t = __INTPTR_TYPE__;5 6// Test interaction of constexpr and __builtin_constant_p.7 8template<typename T> constexpr bool bcp(T t) {9  return __builtin_constant_p(t);10}11template<typename T> constexpr bool bcp_fold(T t) {12  return __builtin_constant_p(((void)(intptr_t)&t, t));13}14 15constexpr intptr_t ensure_fold_is_generally_not_enabled = // expected-error {{constant expression}}16    (intptr_t)&ensure_fold_is_generally_not_enabled; // expected-note {{cast}}17 18constexpr intptr_t ptr_to_int(const void *p) {19  return __builtin_constant_p(1) ? (intptr_t)p : (intptr_t)p;20}21 22constexpr int *int_to_ptr(intptr_t n) {23  return __builtin_constant_p(1) ? (int*)n : (int*)n;24}25 26int x;27 28// Integer and floating point constants encountered during constant expression29// evaluation are considered constant. So is nullptr_t.30static_assert(bcp(1));31static_assert(bcp_fold(1));32static_assert(bcp(1.0));33static_assert(bcp_fold(1.0));34static_assert(bcp(nullptr));35static_assert(bcp_fold(nullptr));36 37// Pointers to the start of strings are considered constant.38static_assert(bcp("foo"));39static_assert(bcp_fold("foo"));40 41// Null pointers are considered constant.42static_assert(bcp<int*>(nullptr));43static_assert(bcp_fold<int*>(nullptr));44static_assert(bcp<const char*>(nullptr));45static_assert(bcp_fold<const char*>(nullptr));46 47// Other pointers are not.48static_assert(!bcp(&x));49static_assert(!bcp_fold(&x));50 51// Pointers cast to integers follow the rules for pointers.52static_assert(bcp(ptr_to_int("foo")));53static_assert(bcp_fold(ptr_to_int("foo")));54static_assert(!bcp(ptr_to_int(&x)));55static_assert(!bcp_fold(ptr_to_int(&x)));56 57// Integers cast to pointers follow the integer rules.58static_assert(bcp(int_to_ptr(0)));59static_assert(bcp_fold(int_to_ptr(0)));60static_assert(bcp(int_to_ptr(123)));      // GCC rejects these due to not recognizing61static_assert(bcp_fold(int_to_ptr(123))); // the bcp conditional in 'int_to_ptr' ...62static_assert(__builtin_constant_p((int*)123)); // ... but GCC accepts this63 64// State mutations in the operand are not permitted.65//66// The rule GCC uses for this is not entirely understood, but seems to depend67// in some way on what local state is mentioned in the operand of68// __builtin_constant_p and where.69//70// We approximate GCC's rule by evaluating the operand in a speculative71// evaluation context; only state created within the evaluation can be72// modified.73constexpr int mutate1() {74  int n = 1;75  int m = __builtin_constant_p(++n);76  return n * 10 + m;77}78static_assert(mutate1() == 10);79 80// FIXME: GCC treats this as being non-constant because of the "n = 2", even81// though evaluation in the context of the enclosing constant expression82// succeeds without mutating any state.83constexpr int mutate2() {84  int n = 1;85  int m = __builtin_constant_p(n ? n + 1 : n = 2);86  return n * 10 + m;87}88static_assert(mutate2() == 11);89 90constexpr int internal_mutation(int unused) {91  int x = 1;92  ++x;93  return x;94}95 96constexpr int mutate3() {97  int n = 1;98  int m = __builtin_constant_p(internal_mutation(0));99  return n * 10 + m;100}101static_assert(mutate3() == 11);102 103constexpr int mutate4() {104  int n = 1;105  int m = __builtin_constant_p(n ? internal_mutation(0) : 0);106  return n * 10 + m;107}108static_assert(mutate4() == 11);109 110// FIXME: GCC treats this as being non-constant because of something to do with111// the 'n' in the argument to internal_mutation.112constexpr int mutate5() {113  int n = 1;114  int m = __builtin_constant_p(n ? internal_mutation(n) : 0);115  return n * 10 + m;116}117static_assert(mutate5() == 11);118 119constexpr int mutate_param(bool mutate, int &param) {120  mutate = mutate; // Mutation of internal state is OK121  if (mutate)122    ++param;123  return param;124}125constexpr int mutate6(bool mutate) {126  int n = 1;127  int m = __builtin_constant_p(mutate_param(mutate, n));128  return n * 10 + m;129}130// No mutation of state outside __builtin_constant_p: evaluates to true.131static_assert(mutate6(false) == 11);132// Mutation of state outside __builtin_constant_p: evaluates to false.133static_assert(mutate6(true) == 10);134 135// GCC strangely returns true for the address of a type_info object, despite it136// not being a pointer to the start of a string literal.137namespace std { struct type_info; }138static_assert(__builtin_constant_p(&typeid(int)));139 140void mutate_as_side_effect() {141  int a;142  static_assert(!__builtin_constant_p(((void)++a, 1)));143}144 145namespace dtor_side_effect {146  struct A {147    constexpr A() {}148    ~A();149  };150  static_assert(!__builtin_constant_p((A{}, 123)));151}152 153#if __cplusplus >= 202002L154namespace constexpr_dtor {155  struct A {156    int *p;157    constexpr ~A() { *p = 0; }158  };159  struct Q { int n; constexpr int *get() { return &n; } };160  static_assert(!__builtin_constant_p(((void)A{}, 123)));161  // FIXME: We should probably accept this. GCC does.162  // However, GCC appears to do so by running the destructors at the end of the163  // enclosing full-expression, which seems broken; running them at the end of164  // the evaluation of the __builtin_constant_p argument would be more165  // defensible.166  static_assert(!__builtin_constant_p(((void)A{Q().get()}, 123)));167}168#endif169