169 lines
5.1 KiB
C++
169 lines
5.1 KiB
C++
|
// RUN: %clang_cc1 -std=c++17 -verify %s
|
||
|
// RUN: %clang_cc1 -std=c++20 -verify %s
|
||
|
|
||
|
using intptr_t = __INTPTR_TYPE__;
|
||
|
|
||
|
// Test interaction of constexpr and __builtin_constant_p.
|
||
|
|
||
|
template<typename T> constexpr bool bcp(T t) {
|
||
|
return __builtin_constant_p(t);
|
||
|
}
|
||
|
template<typename T> constexpr bool bcp_fold(T t) {
|
||
|
return __builtin_constant_p(((void)(intptr_t)&t, t));
|
||
|
}
|
||
|
|
||
|
constexpr intptr_t ensure_fold_is_generally_not_enabled = // expected-error {{constant expression}}
|
||
|
(intptr_t)&ensure_fold_is_generally_not_enabled; // expected-note {{cast}}
|
||
|
|
||
|
constexpr intptr_t ptr_to_int(const void *p) {
|
||
|
return __builtin_constant_p(1) ? (intptr_t)p : (intptr_t)p;
|
||
|
}
|
||
|
|
||
|
constexpr int *int_to_ptr(intptr_t n) {
|
||
|
return __builtin_constant_p(1) ? (int*)n : (int*)n;
|
||
|
}
|
||
|
|
||
|
int x;
|
||
|
|
||
|
// Integer and floating point constants encountered during constant expression
|
||
|
// evaluation are considered constant. So is nullptr_t.
|
||
|
static_assert(bcp(1));
|
||
|
static_assert(bcp_fold(1));
|
||
|
static_assert(bcp(1.0));
|
||
|
static_assert(bcp_fold(1.0));
|
||
|
static_assert(bcp(nullptr));
|
||
|
static_assert(bcp_fold(nullptr));
|
||
|
|
||
|
// Pointers to the start of strings are considered constant.
|
||
|
static_assert(bcp("foo"));
|
||
|
static_assert(bcp_fold("foo"));
|
||
|
|
||
|
// Null pointers are considered constant.
|
||
|
static_assert(bcp<int*>(nullptr));
|
||
|
static_assert(bcp_fold<int*>(nullptr));
|
||
|
static_assert(bcp<const char*>(nullptr));
|
||
|
static_assert(bcp_fold<const char*>(nullptr));
|
||
|
|
||
|
// Other pointers are not.
|
||
|
static_assert(!bcp(&x));
|
||
|
static_assert(!bcp_fold(&x));
|
||
|
|
||
|
// Pointers cast to integers follow the rules for pointers.
|
||
|
static_assert(bcp(ptr_to_int("foo")));
|
||
|
static_assert(bcp_fold(ptr_to_int("foo")));
|
||
|
static_assert(!bcp(ptr_to_int(&x)));
|
||
|
static_assert(!bcp_fold(ptr_to_int(&x)));
|
||
|
|
||
|
// Integers cast to pointers follow the integer rules.
|
||
|
static_assert(bcp(int_to_ptr(0)));
|
||
|
static_assert(bcp_fold(int_to_ptr(0)));
|
||
|
static_assert(bcp(int_to_ptr(123))); // GCC rejects these due to not recognizing
|
||
|
static_assert(bcp_fold(int_to_ptr(123))); // the bcp conditional in 'int_to_ptr' ...
|
||
|
static_assert(__builtin_constant_p((int*)123)); // ... but GCC accepts this
|
||
|
|
||
|
// State mutations in the operand are not permitted.
|
||
|
//
|
||
|
// The rule GCC uses for this is not entirely understood, but seems to depend
|
||
|
// in some way on what local state is mentioned in the operand of
|
||
|
// __builtin_constant_p and where.
|
||
|
//
|
||
|
// We approximate GCC's rule by evaluating the operand in a speculative
|
||
|
// evaluation context; only state created within the evaluation can be
|
||
|
// modified.
|
||
|
constexpr int mutate1() {
|
||
|
int n = 1;
|
||
|
int m = __builtin_constant_p(++n);
|
||
|
return n * 10 + m;
|
||
|
}
|
||
|
static_assert(mutate1() == 10);
|
||
|
|
||
|
// FIXME: GCC treats this as being non-constant because of the "n = 2", even
|
||
|
// though evaluation in the context of the enclosing constant expression
|
||
|
// succeeds without mutating any state.
|
||
|
constexpr int mutate2() {
|
||
|
int n = 1;
|
||
|
int m = __builtin_constant_p(n ? n + 1 : n = 2);
|
||
|
return n * 10 + m;
|
||
|
}
|
||
|
static_assert(mutate2() == 11);
|
||
|
|
||
|
constexpr int internal_mutation(int unused) {
|
||
|
int x = 1;
|
||
|
++x;
|
||
|
return x;
|
||
|
}
|
||
|
|
||
|
constexpr int mutate3() {
|
||
|
int n = 1;
|
||
|
int m = __builtin_constant_p(internal_mutation(0));
|
||
|
return n * 10 + m;
|
||
|
}
|
||
|
static_assert(mutate3() == 11);
|
||
|
|
||
|
constexpr int mutate4() {
|
||
|
int n = 1;
|
||
|
int m = __builtin_constant_p(n ? internal_mutation(0) : 0);
|
||
|
return n * 10 + m;
|
||
|
}
|
||
|
static_assert(mutate4() == 11);
|
||
|
|
||
|
// FIXME: GCC treats this as being non-constant because of something to do with
|
||
|
// the 'n' in the argument to internal_mutation.
|
||
|
constexpr int mutate5() {
|
||
|
int n = 1;
|
||
|
int m = __builtin_constant_p(n ? internal_mutation(n) : 0);
|
||
|
return n * 10 + m;
|
||
|
}
|
||
|
static_assert(mutate5() == 11);
|
||
|
|
||
|
constexpr int mutate_param(bool mutate, int ¶m) {
|
||
|
mutate = mutate; // Mutation of internal state is OK
|
||
|
if (mutate)
|
||
|
++param;
|
||
|
return param;
|
||
|
}
|
||
|
constexpr int mutate6(bool mutate) {
|
||
|
int n = 1;
|
||
|
int m = __builtin_constant_p(mutate_param(mutate, n));
|
||
|
return n * 10 + m;
|
||
|
}
|
||
|
// No mutation of state outside __builtin_constant_p: evaluates to true.
|
||
|
static_assert(mutate6(false) == 11);
|
||
|
// Mutation of state outside __builtin_constant_p: evaluates to false.
|
||
|
static_assert(mutate6(true) == 10);
|
||
|
|
||
|
// GCC strangely returns true for the address of a type_info object, despite it
|
||
|
// not being a pointer to the start of a string literal.
|
||
|
namespace std { struct type_info; }
|
||
|
static_assert(__builtin_constant_p(&typeid(int)));
|
||
|
|
||
|
void mutate_as_side_effect() {
|
||
|
int a;
|
||
|
static_assert(!__builtin_constant_p(((void)++a, 1)));
|
||
|
}
|
||
|
|
||
|
namespace dtor_side_effect {
|
||
|
struct A {
|
||
|
constexpr A() {}
|
||
|
~A();
|
||
|
};
|
||
|
static_assert(!__builtin_constant_p((A{}, 123)));
|
||
|
}
|
||
|
|
||
|
#if __cplusplus >= 202002L
|
||
|
namespace constexpr_dtor {
|
||
|
struct A {
|
||
|
int *p;
|
||
|
constexpr ~A() { *p = 0; }
|
||
|
};
|
||
|
struct Q { int n; constexpr int *get() { return &n; } };
|
||
|
static_assert(!__builtin_constant_p((A{}, 123)));
|
||
|
// FIXME: We should probably accept this. GCC does.
|
||
|
// However, GCC appears to do so by running the destructors at the end of the
|
||
|
// enclosing full-expression, which seems broken; running them at the end of
|
||
|
// the evaluation of the __builtin_constant_p argument would be more
|
||
|
// defensible.
|
||
|
static_assert(!__builtin_constant_p((A{Q().get()}, 123)));
|
||
|
}
|
||
|
#endif
|