27a972a699
(This relands 59337263ab
and makes sure comma operator
diagnostics are suppressed in a SFINAE context.)
While at it, add the diagnosis message "left operand of comma operator has no effect" (used by GCC) for comma operator.
This also makes Clang diagnose in the constant evaluation context which aligns with GCC/MSVC behavior. (https://godbolt.org/z/7zxb8Tx96)
Reviewed By: aaron.ballman
Differential Revision: https://reviews.llvm.org/D103938
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(((void)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(((void)A{Q().get()}, 123)));
|
|
}
|
|
#endif
|