Skip to content

Commit 610ec95

Browse files
[clang] allow const structs/unions/arrays to be constant expressions for C
For code like: struct foo { ... }; struct bar { struct foo foo; }; const struct foo my_foo = { ... }; struct bar my_bar = { .foo = my_foo }; Eli Friedman points out the relevant part of the C standard seems to have some flexibility in what is considered a constant expression: 6.6 paragraph 10: An implementation may accept other forms of constant expressions. GCC 8 added support for these, so clang not supporting them has been a constant thorn in the side of source code portability within the Linux kernel. Fixes: #44502 Reviewed By: efriedma Differential Revision: https://reviews.llvm.org/D76096
1 parent ab202aa commit 610ec95

File tree

6 files changed

+77
-19
lines changed

6 files changed

+77
-19
lines changed

clang/docs/ReleaseNotes.rst

+3
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,9 @@ Resolutions to C++ Defect Reports
7171

7272
C Language Changes
7373
------------------
74+
- ``structs``, ``unions``, and ``arrays`` that are const may now be used as
75+
constant expressions. This change is more consistent with the behavior of
76+
GCC.
7477

7578
C2x Feature Support
7679
^^^^^^^^^^^^^^^^^^^

clang/lib/AST/ExprConstant.cpp

-14
Original file line numberDiff line numberDiff line change
@@ -15261,14 +15261,6 @@ static bool FastEvaluateAsRValue(const Expr *Exp, Expr::EvalResult &Result,
1526115261
return true;
1526215262
}
1526315263

15264-
// FIXME: Evaluating values of large array and record types can cause
15265-
// performance problems. Only do so in C++11 for now.
15266-
if (Exp->isPRValue() &&
15267-
(Exp->getType()->isArrayType() || Exp->getType()->isRecordType()) &&
15268-
!Ctx.getLangOpts().CPlusPlus11) {
15269-
IsConst = false;
15270-
return true;
15271-
}
1527215264
return false;
1527315265
}
1527415266

@@ -15510,12 +15502,6 @@ bool Expr::EvaluateAsInitializer(APValue &Value, const ASTContext &Ctx,
1551015502
return Name;
1551115503
});
1551215504

15513-
// FIXME: Evaluating initializers for large array and record types can cause
15514-
// performance problems. Only do so in C++11 for now.
15515-
if (isPRValue() && (getType()->isArrayType() || getType()->isRecordType()) &&
15516-
!Ctx.getLangOpts().CPlusPlus11)
15517-
return false;
15518-
1551915505
Expr::EvalStatus EStatus;
1552015506
EStatus.Diag = &Notes;
1552115507

clang/test/CodeGen/builtin-constant-p.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ int test6(void) {
7676

7777
int test7(void) {
7878
// CHECK-LABEL: test7
79-
// CHECK: call i1 @llvm.is.constant.i32
79+
// CHECK: ret i32 1
8080
return __builtin_constant_p(c_arr[2]);
8181
}
8282

clang/test/CodeGen/const-init.c

+25
Original file line numberDiff line numberDiff line change
@@ -190,3 +190,28 @@ void g31(void) {
190190
struct { const float *floats; } compoundliteral = {
191191
(float[1]) { 0.1, },
192192
};
193+
194+
struct PR4517_foo {
195+
int x;
196+
};
197+
struct PR4517_bar {
198+
struct PR4517_foo foo;
199+
};
200+
const struct PR4517_foo my_foo = {.x = 42};
201+
struct PR4517_bar my_bar = {.foo = my_foo};
202+
struct PR4517_bar my_bar2 = (struct PR4517_bar){.foo = my_foo};
203+
struct PR4517_bar my_bar3 = {my_foo};
204+
struct PR4517_bar my_bar4 = (struct PR4517_bar){my_foo};
205+
// CHECK: @my_foo = constant %struct.PR4517_foo { i32 42 }, align 4
206+
// CHECK: @my_bar = global %struct.PR4517_bar { %struct.PR4517_foo { i32 42 } }, align 4
207+
// CHECK: @my_bar2 = global %struct.PR4517_bar { %struct.PR4517_foo { i32 42 } }, align 4
208+
// CHECK: @my_bar3 = global %struct.PR4517_bar { %struct.PR4517_foo { i32 42 } }, align 4
209+
// CHECK: @my_bar4 = global %struct.PR4517_bar { %struct.PR4517_foo { i32 42 } }, align 4
210+
const int PR4517_arrc[2] = {41, 42};
211+
int PR4517_x = PR4517_arrc[1];
212+
const int PR4517_idx = 1;
213+
int PR4517_x2 = PR4517_arrc[PR4517_idx];
214+
// CHECK: @PR4517_arrc = constant [2 x i32] [i32 41, i32 42], align 4
215+
// CHECK: @PR4517_x = global i32 42, align 4
216+
// CHECK: @PR4517_idx = constant i32 1, align 4
217+
// CHECK: @PR4517_x2 = global i32 42, align 4

clang/test/Sema/builtins.c

+5-4
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ struct foo x = (struct foo) { __builtin_constant_p(42) ? 37 : 927 };
131131

132132
const int test17_n = 0;
133133
const char test17_c[] = {1, 2, 3, 0};
134-
const char test17_d[] = {1, 2, 3, 4};
134+
const char test17_d[] = {1, 2, 3, 4}; // Like test17_c but not NUL-terminated.
135135
typedef int __attribute__((vector_size(16))) IntVector;
136136
struct Aggregate { int n; char c; };
137137
enum Enum { EnumValue1, EnumValue2 };
@@ -178,9 +178,10 @@ void test17(void) {
178178
ASSERT(!OPT("abcd"));
179179
// In these cases, the strlen is non-constant, but the __builtin_constant_p
180180
// is 0: the array size is not an ICE but is foldable.
181-
ASSERT(!OPT(test17_c)); // expected-warning {{folding}}
182-
ASSERT(!OPT(&test17_c[0])); // expected-warning {{folding}}
183-
ASSERT(!OPT((char*)test17_c)); // expected-warning {{folding}}
181+
ASSERT(!OPT(test17_c));
182+
ASSERT(!OPT(&test17_c[0]));
183+
ASSERT(!OPT((char*)test17_c));
184+
// NOTE: test17_d is not NUL-termintated, so calling strlen on it is UB.
184185
ASSERT(!OPT(test17_d)); // expected-warning {{folding}}
185186
ASSERT(!OPT(&test17_d[0])); // expected-warning {{folding}}
186187
ASSERT(!OPT((char*)test17_d)); // expected-warning {{folding}}

clang/test/Sema/init.c

+43
Original file line numberDiff line numberDiff line change
@@ -164,3 +164,46 @@ struct vortexstruct vortexvar = { "asdf" };
164164

165165
typedef struct { uintptr_t x : 2; } StructWithBitfield;
166166
StructWithBitfield bitfieldvar = { (uintptr_t)&bitfieldvar }; // expected-error {{initializer element is not a compile-time constant}}
167+
168+
// PR45157
169+
struct PR4517_foo {
170+
int x;
171+
};
172+
struct PR4517_bar {
173+
struct PR4517_foo foo;
174+
};
175+
const struct PR4517_foo my_foo = {.x = 42};
176+
struct PR4517_bar my_bar = {
177+
.foo = my_foo, // no-warning
178+
};
179+
struct PR4517_bar my_bar2 = (struct PR4517_bar){
180+
.foo = my_foo, // no-warning
181+
};
182+
struct PR4517_bar my_bar3 = {
183+
my_foo, // no-warning
184+
};
185+
struct PR4517_bar my_bar4 = (struct PR4517_bar){
186+
my_foo // no-warning
187+
};
188+
extern const struct PR4517_foo my_foo2;
189+
struct PR4517_bar my_bar5 = {
190+
.foo = my_foo2, // expected-error {{initializer element is not a compile-time constant}}
191+
};
192+
const struct PR4517_foo my_foo3 = {.x = my_foo.x};
193+
int PR4517_a[2] = {0, 1};
194+
const int PR4517_ca[2] = {0, 1};
195+
int PR4517_idx = 0;
196+
const int PR4517_idxc = 1;
197+
int PR4517_x1 = PR4517_a[PR4517_idx]; // expected-error {{initializer element is not a compile-time constant}}
198+
int PR4517_x2 = PR4517_a[PR4517_idxc]; // expected-error {{initializer element is not a compile-time constant}}
199+
int PR4517_x3 = PR4517_a[0]; // expected-error {{initializer element is not a compile-time constant}}
200+
int PR4517_y1 = PR4517_ca[PR4517_idx]; // expected-error {{initializer element is not a compile-time constant}}
201+
int PR4517_y2 = PR4517_ca[PR4517_idxc]; // no-warning
202+
int PR4517_y3 = PR4517_ca[0]; // no-warning
203+
union PR4517_u {
204+
int x;
205+
float y;
206+
};
207+
const union PR4517_u u1 = {4.0f};
208+
const union PR4517_u u2 = u1; // no-warning
209+
const union PR4517_u u3 = {u1.y}; // expected-error {{initializer element is not a compile-time constant}}

0 commit comments

Comments
 (0)