builtin-constant-p.cpp
5.06 KB
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
// 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