Skip to content

Commit 8079a2c

Browse files
authored
[clang][bytecode] Diagnose reference to non-constexpr variable of const type in C23 constexpr (#112211)
```cpp const int V33 = 4; const int V34 = 0; const int V35 = 2; constexpr int V36 = V33 / V34; // expected-error@-1 {{constexpr variable 'V36' must be initialized by a constant expression}} constexpr int V37 = V33 / V35; // expected-error@-1 {{constexpr variable 'V37' must be initialized by a constant expression}} ``` --------- Signed-off-by: yronglin <[email protected]>
1 parent 8287fa8 commit 8079a2c

File tree

2 files changed

+377
-0
lines changed

2 files changed

+377
-0
lines changed

clang/lib/AST/ByteCode/Interp.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -348,6 +348,13 @@ bool CheckConstant(InterpState &S, CodePtr OpPC, const Descriptor *Desc) {
348348
if (D->isConstexpr())
349349
return true;
350350

351+
// If we're evaluating the initializer for a constexpr variable in C23, we may
352+
// only read other contexpr variables. Abort here since this one isn't
353+
// constexpr.
354+
if (const auto *VD = dyn_cast_if_present<VarDecl>(S.EvaluatingDecl);
355+
VD && VD->isConstexpr() && S.getLangOpts().C23)
356+
return Invalid(S, OpPC);
357+
351358
QualType T = D->getType();
352359
bool IsConstant = T.isConstant(S.getASTContext());
353360
if (T->isIntegralOrEnumerationType()) {

clang/test/AST/ByteCode/constexpr.c

Lines changed: 370 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,370 @@
1+
// RUN: %clang_cc1 -std=c23 -verify=ref,both -triple x86_64 -pedantic -Wno-conversion -Wno-constant-conversion -Wno-div-by-zero %s
2+
// RUN: %clang_cc1 -std=c23 -verify=expected,both -triple x86_64 -pedantic -Wno-conversion -Wno-constant-conversion -Wno-div-by-zero -fexperimental-new-constant-interpreter %s
3+
4+
// Check that constexpr only applies to variables.
5+
constexpr void f0() {} // both-error {{'constexpr' can only be used in variable declarations}}
6+
constexpr const int f1() { return 0; } // both-error {{'constexpr' can only be used in variable declarations}}
7+
8+
constexpr struct S1 { int f; }; //both-error {{struct cannot be marked constexpr}}
9+
constexpr struct S2 ; // both-error {{struct cannot be marked constexpr}}
10+
constexpr union U1; // both-error {{union cannot be marked constexpr}}
11+
constexpr union U2 {int a; float b;}; // both-error {{union cannot be marked constexpr}}
12+
constexpr enum E1 {A = 1, B = 2} ; // both-error {{enum cannot be marked constexpr}}
13+
struct S3 {
14+
static constexpr int f = 0; // both-error {{type name does not allow storage class}}
15+
// both-error@-1 {{type name does not allow constexpr}}
16+
// both-error@-2 {{expected ';' at end}}
17+
constexpr int f1 = 0;
18+
// both-error@-1 {{type name does not allow constexpr}}
19+
// both-error@-2 {{expected ';' at end}}
20+
};
21+
22+
constexpr; // both-error {{'constexpr' can only be used in variable declarations}}
23+
constexpr int V1 = 3;
24+
constexpr float V2 = 7.0;
25+
int V3 = (constexpr)3; // both-error {{expected expression}}
26+
27+
void f2() {
28+
constexpr int a = 0;
29+
constexpr float b = 1.7f;
30+
}
31+
32+
// Check how constexpr works with other storage-class specifiers.
33+
constexpr auto V4 = 1;
34+
constexpr static auto V5 = 1;
35+
constexpr static const auto V6 = 1;
36+
constexpr static const int V7 = 1;
37+
constexpr static int V8 = 1;
38+
constexpr auto Ulong = 1L;
39+
constexpr auto CompoundLiteral = (int){13};
40+
constexpr auto DoubleCast = (double)(1 / 3);
41+
constexpr auto String = "this is a string"; // both-error {{constexpr pointer initializer is not null}}
42+
constexpr signed auto Long = 1L; // both-error {{'auto' cannot be signed or unsigned}}
43+
_Static_assert(_Generic(Ulong, long : 1));
44+
_Static_assert(_Generic(CompoundLiteral, int : 1));
45+
_Static_assert(_Generic(DoubleCast, double : 1));
46+
_Static_assert(_Generic(String, char* : 1));
47+
48+
typedef constexpr int Foo; // both-error {{typedef cannot be constexpr}}
49+
constexpr typedef int Bar; // both-error {{typedef cannot be constexpr}}
50+
51+
void f3(constexpr register int P1) { // both-error {{function parameter cannot be constexpr}}
52+
constexpr register int V9 = 0;
53+
constexpr register auto V10 = 0.0;
54+
}
55+
56+
constexpr thread_local int V11 = 38; // both-error {{cannot combine with previous '_Thread_local' declaration specifier}}
57+
constexpr static thread_local double V12 = 38; // both-error {{cannot combine with previous '_Thread_local' declaration specifier}}
58+
constexpr extern thread_local char V13; // both-error {{cannot combine with previous '_Thread_local' declaration specifier}}
59+
// both-error@-1 {{cannot combine with previous 'extern' declaration specifier}}
60+
// both-error@-2 {{constexpr variable declaration must be a definition}}
61+
constexpr thread_local short V14 = 38; // both-error {{cannot combine with previous '_Thread_local' declaration specifier}}
62+
63+
// Check how constexpr works with qualifiers.
64+
constexpr _Atomic int V15 = 0; // both-error {{constexpr variable cannot have type 'const _Atomic(int)'}}
65+
constexpr _Atomic(int) V16 = 0; // both-error {{constexpr variable cannot have type 'const _Atomic(int)'}}
66+
67+
constexpr volatile int V17 = 0; // both-error {{constexpr variable cannot have type 'const volatile int'}}
68+
69+
constexpr int * restrict V18 = 0; // both-error {{constexpr variable cannot have type 'int *const restrict'}}
70+
71+
constexpr extern char Oops = 1; // both-error {{cannot combine with previous 'extern' declaration specifier}} \
72+
// both-warning {{'extern' variable has an initializer}}
73+
74+
constexpr int * restrict * Oops1 = 0;
75+
76+
typedef _Atomic(int) TheA;
77+
typedef volatile short TheV;
78+
typedef float * restrict TheR;
79+
80+
constexpr TheA V19[3] = {};
81+
// both-error@-1 {{constexpr variable cannot have type 'const TheA[3]' (aka 'const _Atomic(int)[3]')}}
82+
constexpr TheV V20[3] = {};
83+
// both-error@-1 {{constexpr variable cannot have type 'const TheV[3]' (aka 'const volatile short[3]')}}
84+
constexpr TheR V21[3] = {};
85+
// both-error@-1 {{constexpr variable cannot have type 'const TheR[3]' (aka 'float *restrict const[3]')}}
86+
87+
struct HasA {
88+
TheA f;
89+
int b;
90+
};
91+
92+
struct HasV {
93+
float b;
94+
TheV f;
95+
};
96+
97+
struct HasR {
98+
short b;
99+
int a;
100+
TheR f;
101+
};
102+
103+
constexpr struct HasA V22[2] = {};
104+
// both-error@-1 {{constexpr variable cannot have type 'TheA' (aka '_Atomic(int)')}}
105+
constexpr struct HasV V23[2] = {};
106+
// both-error@-1 {{constexpr variable cannot have type 'TheV' (aka 'volatile short')}}
107+
constexpr struct HasR V24[2] = {};
108+
// both-error@-1 {{constexpr variable cannot have type 'TheR' (aka 'float *restrict')}}
109+
110+
union U3 {
111+
float a;
112+
union {
113+
struct HasA f;
114+
struct HasR f1;
115+
};
116+
};
117+
118+
constexpr union U3 V25 = {};
119+
// both-error@-1 {{constexpr variable cannot have type 'TheA' (aka '_Atomic(int)')}}
120+
constexpr union U3 V26[8] = {};
121+
// both-error@-1 {{constexpr variable cannot have type 'TheA' (aka '_Atomic(int)')}}
122+
123+
struct S4 {
124+
union U3 f[3];
125+
};
126+
127+
constexpr struct S4 V27 = {};
128+
// both-error@-1 {{constexpr variable cannot have type 'TheA' (aka '_Atomic(int)')}}
129+
constexpr const int V28 = 28;
130+
131+
struct S {
132+
union {
133+
volatile int i;
134+
};
135+
int j;
136+
};
137+
138+
constexpr struct S s = {}; // both-error {{constexpr variable cannot have type 'volatile int'}}
139+
140+
// Check that constexpr variable must have a valid initializer which is a
141+
// constant expression.
142+
constexpr int V29;
143+
// both-error@-1 {{constexpr variable 'V29' must be initialized by a constant expression}}
144+
145+
struct S5 {
146+
int f;
147+
};
148+
149+
constexpr struct S5 V30;
150+
// both-error@-1 {{constexpr variable 'V30' must be initialized by a constant expression}}
151+
constexpr struct S5 V31 = {};
152+
153+
int randomFoo() { return 7; }
154+
155+
constexpr float V32 = randomFoo();
156+
// both-error@-1 {{constexpr variable 'V32' must be initialized by a constant expression}}
157+
158+
const int V33 = 4;
159+
const int V34 = 0;
160+
const int V35 = 2;
161+
162+
constexpr int V36 = V33 / V34;
163+
// both-error@-1 {{constexpr variable 'V36' must be initialized by a constant expression}}
164+
constexpr int V37 = V33 / V35;
165+
// both-error@-1 {{constexpr variable 'V37' must be initialized by a constant expression}}
166+
constexpr int V38 = 3;
167+
constexpr int V39 = V38 / V38;
168+
constexpr int V40 = V38 / 2;
169+
constexpr int V41 = V38 / 0;
170+
// both-error@-1 {{constexpr variable 'V41' must be initialized by a constant expression}}
171+
// both-note@-2 {{division by zero}}
172+
constexpr int V42 = V38 & 0;
173+
174+
constexpr struct S5 V43 = { randomFoo() };
175+
// both-error@-1 {{constexpr variable 'V43' must be initialized by a constant expression}}
176+
constexpr struct S5 V44 = { 0 };
177+
constexpr struct S5 V45 = { V38 / 0 };
178+
// both-error@-1 {{constexpr variable 'V45' must be initialized by a constant expression}}
179+
// both-note@-2 {{division by zero}}
180+
181+
constexpr float V46[3] = {randomFoo() };
182+
// both-error@-1 {{constexpr variable 'V46' must be initialized by a constant expression}}
183+
constexpr struct S5 V47[3] = {randomFoo() };
184+
// both-error@-1 {{constexpr variable 'V47' must be initialized by a constant expression}}
185+
186+
const static int V48 = V38;
187+
constexpr static int V49 = V48;
188+
// both-error@-1 {{constexpr variable 'V49' must be initialized by a constant expression}}
189+
190+
void f4(const int P1) {
191+
constexpr int V = P1;
192+
// both-error@-1 {{constexpr variable 'V' must be initialized by a constant expression}}
193+
194+
constexpr int V1 = 12;
195+
constexpr const int *V2 = &V1;
196+
// both-error@-1 {{constexpr variable 'V2' must be initialized by a constant expression}}
197+
}
198+
199+
// Check that initializer for constexpr variable should match the type of the
200+
// variable and is exactly representable int the variable's type.
201+
202+
struct S6 {
203+
unsigned char a;
204+
};
205+
206+
struct S7 {
207+
union {
208+
float a;
209+
};
210+
unsigned int b;
211+
};
212+
213+
struct S8 {
214+
unsigned char a[3];
215+
unsigned int b[3];
216+
};
217+
218+
constexpr struct S8 DesigInit = {.b = {299, 7, 8}, .a = {-1, 7, 8}};
219+
// both-error@-1 {{constexpr initializer evaluates to -1 which is not exactly representable in type 'unsigned char'}}
220+
221+
void f5() {
222+
constexpr char V50 = 300;
223+
// both-error@-1 {{constexpr initializer evaluates to 300 which is not exactly representable in type 'const char'}}
224+
constexpr float V51 = 1.0 / 3.0;
225+
// both-error@-1 {{constexpr initializer evaluates to 3.333333e-01 which is not exactly representable in type 'const float'}}
226+
constexpr float V52 = 0.7;
227+
// both-error@-1 {{constexpr initializer evaluates to 7.000000e-01 which is not exactly representable in type 'const float'}}
228+
constexpr float V53 = 1.0f / 3.0f;
229+
constexpr float V54 = 432000000000;
230+
// both-error@-1 {{constexpr initializer evaluates to 432000000000 which is not exactly representable in type 'const float'}}
231+
constexpr unsigned char V55[] = {
232+
"\xAF",
233+
// both-error@-1 {{constexpr initializer evaluates to -81 which is not exactly representable in type 'const unsigned char'}}
234+
};
235+
236+
constexpr unsigned char V56[] = {
237+
u8"\xAF",
238+
};
239+
constexpr struct S6 V57 = {299};
240+
// both-error@-1 {{constexpr initializer evaluates to 299 which is not exactly representable in type 'unsigned char'}}
241+
constexpr struct S6 V58 = {-299};
242+
// both-error@-1 {{constexpr initializer evaluates to -299 which is not exactly representable in type 'unsigned char'}}
243+
constexpr double V59 = 0.5;
244+
constexpr double V60 = 1.0;
245+
constexpr float V61 = V59 / V60;
246+
constexpr double V62 = 1.7;
247+
constexpr float V63 = V59 / V62;
248+
// both-error@-1 {{constexpr initializer evaluates to 2.941176e-01 which is not exactly representable in type 'const float'}}
249+
250+
constexpr unsigned char V64 = '\xAF';
251+
// both-error@-1 {{constexpr initializer evaluates to -81 which is not exactly representable in type 'const unsigned char'}}
252+
constexpr unsigned char V65 = u8'\xAF';
253+
254+
constexpr char V66[3] = {300};
255+
// both-error@-1 {{constexpr initializer evaluates to 300 which is not exactly representable in type 'const char'}}
256+
constexpr struct S6 V67[3] = {300};
257+
// both-error@-1 {{constexpr initializer evaluates to 300 which is not exactly representable in type 'unsigned char'}}
258+
259+
constexpr struct S7 V68 = {0.3, -1 };
260+
// both-error@-1 {{constexpr initializer evaluates to 3.000000e-01 which is not exactly representable in type 'float'}}
261+
// both-error@-2 {{constexpr initializer evaluates to -1 which is not exactly representable in type 'unsigned int'}}
262+
constexpr struct S7 V69 = {0.5, -1 };
263+
// both-error@-1 {{constexpr initializer evaluates to -1 which is not exactly representable in type 'unsigned int'}}
264+
constexpr struct S7 V70[3] = {{123456789}};
265+
// both-error@-1 {{constexpr initializer evaluates to 123456789 which is not exactly representable in type 'float'}}
266+
267+
constexpr int V71 = 0.3;
268+
// both-error@-1 {{constexpr initializer for type 'const int' is of type 'double'}}
269+
constexpr int V72 = V59;
270+
// both-error@-1 {{constexpr initializer for type 'const int' is of type 'const double'}}
271+
constexpr struct S6 V73 = {V59};
272+
// both-error@-1 {{constexpr initializer for type 'unsigned char' is of type 'const double'}}
273+
274+
constexpr float V74 = 1;
275+
constexpr float V75 = V59;
276+
constexpr unsigned int V76[3] = {0.5};
277+
// both-error@-1 {{constexpr initializer for type 'const unsigned int' is of type 'double'}}
278+
279+
constexpr _Complex float V77 = 0;
280+
constexpr float V78 = V77;
281+
// both-error@-1 {{constexpr initializer for type 'const float' is of type 'const _Complex float'}}
282+
constexpr int V79 = V77;
283+
// both-error@-1 {{constexpr initializer for type 'const int' is of type 'const _Complex float'}}
284+
285+
}
286+
287+
constexpr char string[] = "test""ing this out\xFF";
288+
constexpr unsigned char ustring[] = "test""ing this out\xFF";
289+
// both-error@-1 {{constexpr initializer evaluates to -1 which is not exactly representable in type 'const unsigned char'}}
290+
constexpr char u8string[] = u8"test"u8"ing this out\xFF";
291+
// both-error@-1 {{constexpr initializer evaluates to 255 which is not exactly representable in type 'const char'}}
292+
constexpr unsigned char u8ustring[] = u8"test"u8"ing this out\xFF";
293+
constexpr unsigned short uustring[] = u"test"u"ing this out\xFF";
294+
constexpr unsigned int Ustring[] = U"test"U"ing this out\xFF";
295+
constexpr unsigned char Arr2[6][6] = {
296+
{"ek\xFF"}, {"ek\xFF"}
297+
// both-error@-1 2{{constexpr initializer evaluates to -1 which is not exactly representable in type 'const unsigned char'}}
298+
};
299+
300+
constexpr int i = (12);
301+
constexpr int j = (i);
302+
constexpr unsigned jneg = (-i);
303+
// both-error@-1 {{constexpr initializer evaluates to -12 which is not exactly representable in type 'const unsigned int'}}
304+
305+
// Check that initializer for pointer constexpr variable should be null.
306+
constexpr int V80 = 3;
307+
constexpr const int *V81 = &V80;
308+
// both-error@-1 {{constexpr pointer initializer is not null}}
309+
constexpr int *V82 = 0;
310+
constexpr int *V83 = V82;
311+
constexpr int *V84 = 42;
312+
// ref-error@-1 {{constexpr variable 'V84' must be initialized by a constant expression}}
313+
// ref-note@-2 {{this conversion is not allowed in a constant expression}}
314+
// both-error@-3 {{constexpr pointer initializer is not null}}
315+
constexpr int *V85 = nullptr;
316+
317+
// Check that constexpr variables should not be VLAs.
318+
void f6(const int P1) {
319+
constexpr int V86[P1] = {};
320+
// both-error@-1 {{constexpr variable cannot have type 'const int[P1]'}}
321+
const int V87 = 3;
322+
constexpr int V88[V87] = {};
323+
// both-warning@-1 {{variable length array folded to constant array as an extension}}
324+
int V89 = 7;
325+
constexpr int V90[V89] = {};
326+
// both-error@-1 {{constexpr variable cannot have type 'const int[V89]'}}
327+
}
328+
329+
void f7(int n, int array[n]) {
330+
constexpr typeof(array) foo = 0; // Accepted because array is a pointer type, not a VLA type
331+
int (*(*fp)(int n))[n];
332+
constexpr typeof(fp) bar = 0; // both-error {{constexpr variable cannot have type 'const typeof (fp)' (aka 'int (*(*const)(int))[n]')}}
333+
}
334+
335+
// Check how constexpr works with NaNs and infinities.
336+
#define FLT_NAN __builtin_nanf("1")
337+
#define DBL_NAN __builtin_nan("1")
338+
#define LD_NAN __builtin_nanf("1")
339+
#define FLT_SNAN __builtin_nansf("1")
340+
#define DBL_SNAN __builtin_nans("1")
341+
#define LD_SNAN __builtin_nansl("1")
342+
#define INF __builtin_inf()
343+
void infsNaNs() {
344+
// Inf and quiet NaN is always fine, signaling NaN must have the same type.
345+
constexpr float fl0 = INF;
346+
constexpr float fl1 = (long double)INF;
347+
constexpr float fl2 = (long double)FLT_NAN;
348+
constexpr float fl3 = FLT_NAN;
349+
constexpr float fl5 = DBL_NAN;
350+
constexpr float fl6 = LD_NAN;
351+
constexpr float fl7 = DBL_SNAN; // both-error {{constexpr initializer evaluates to nan which is not exactly representable in type 'const float'}}
352+
constexpr float fl8 = LD_SNAN; // both-error {{constexpr initializer evaluates to nan which is not exactly representable in type 'const float'}}
353+
354+
constexpr double db0 = FLT_NAN;
355+
constexpr double db2 = DBL_NAN;
356+
constexpr double db3 = DBL_SNAN;
357+
constexpr double db4 = FLT_SNAN; // both-error {{constexpr initializer evaluates to nan which is not exactly representable in type 'const double'}}
358+
constexpr double db5 = LD_SNAN; // both-error {{constexpr initializer evaluates to nan which is not exactly representable in type 'const double'}}
359+
constexpr double db6 = INF;
360+
}
361+
362+
constexpr struct S9 s9 = { }; // both-error {{variable has incomplete type 'const struct S9'}} \
363+
// both-note {{forward declaration of 'struct S9'}}
364+
365+
struct S10 {
366+
signed long long i : 8;
367+
};
368+
constexpr struct S10 c = { 255 };
369+
// FIXME-expected-error@-1 {{constexpr initializer evaluates to 255 which is not exactly representable in 'long long' bit-field with width 8}}
370+
// See: GH#101299

0 commit comments

Comments
 (0)