Skip to content

Commit 14ba782

Browse files
authored
[Clang][Sema] Allow flexible arrays in unions and alone in structs (llvm#84428)
GNU and MSVC have extensions where flexible array members (or their equivalent) can be in unions or alone in structs. This is already fully supported in Clang through the 0-sized array ("fake flexible array") extension or when C99 flexible array members have been syntactically obfuscated. Clang needs to explicitly allow these extensions directly for C99 flexible arrays, since they are common code patterns in active use by the Linux kernel (and other projects). Such projects have been using either 0-sized arrays (which is considered deprecated in favor of C99 flexible array members) or via obfuscated syntax, both of which complicate their code bases. For example, these do not error by default: ``` union one { int a; int b[0]; }; union two { int a; struct { struct { } __empty; int b[]; }; }; ``` But this does: ``` union three { int a; int b[]; }; ``` Remove the default error diagnostics for this but continue to provide warnings under Microsoft or GNU extensions checks. This will allow for a seamless transition for code bases away from 0-sized arrays without losing existing code patterns. Add explicit checking for the warnings under various constructions. Additionally fixes a CodeGen bug with flexible array members in unions in C++, which was found when adding a testcase for: ``` union { char x[]; } z = {0}; ``` which only had Sema tests originally. Fixes llvm#84565
1 parent 0a17eed commit 14ba782

File tree

9 files changed

+303
-30
lines changed

9 files changed

+303
-30
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -304,6 +304,9 @@ Improvements to Clang's diagnostics
304304
annotated with the ``clang::always_destroy`` attribute.
305305
Fixes #GH68686, #GH86486
306306

307+
- ``-Wmicrosoft``, ``-Wgnu``, or ``-pedantic`` is now required to diagnose C99
308+
flexible array members in a union or alone in a struct. Fixes GH#84565.
309+
307310
Improvements to Clang's time-trace
308311
----------------------------------
309312

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6464,9 +6464,6 @@ def ext_c99_flexible_array_member : Extension<
64646464
def err_flexible_array_virtual_base : Error<
64656465
"flexible array member %0 not allowed in "
64666466
"%select{struct|interface|union|class|enum}1 which has a virtual base class">;
6467-
def err_flexible_array_empty_aggregate : Error<
6468-
"flexible array member %0 not allowed in otherwise empty "
6469-
"%select{struct|interface|union|class|enum}1">;
64706467
def err_flexible_array_has_nontrivial_dtor : Error<
64716468
"flexible array member %0 of type %1 with non-trivial destruction">;
64726469
def ext_flexible_array_in_struct : Extension<
@@ -6481,8 +6478,6 @@ def ext_flexible_array_empty_aggregate_ms : Extension<
64816478
"flexible array member %0 in otherwise empty "
64826479
"%select{struct|interface|union|class|enum}1 is a Microsoft extension">,
64836480
InGroup<MicrosoftFlexibleArray>;
6484-
def err_flexible_array_union : Error<
6485-
"flexible array member %0 in a union is not allowed">;
64866481
def ext_flexible_array_union_ms : Extension<
64876482
"flexible array member %0 in a union is a Microsoft extension">,
64886483
InGroup<MicrosoftFlexibleArray>;

clang/lib/Sema/SemaDecl.cpp

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19429,15 +19429,11 @@ void Sema::ActOnFields(Scope *S, SourceLocation RecLoc, Decl *EnclosingDecl,
1942919429
} else if (Record->isUnion())
1943019430
DiagID = getLangOpts().MicrosoftExt
1943119431
? diag::ext_flexible_array_union_ms
19432-
: getLangOpts().CPlusPlus
19433-
? diag::ext_flexible_array_union_gnu
19434-
: diag::err_flexible_array_union;
19432+
: diag::ext_flexible_array_union_gnu;
1943519433
else if (NumNamedMembers < 1)
1943619434
DiagID = getLangOpts().MicrosoftExt
1943719435
? diag::ext_flexible_array_empty_aggregate_ms
19438-
: getLangOpts().CPlusPlus
19439-
? diag::ext_flexible_array_empty_aggregate_gnu
19440-
: diag::err_flexible_array_empty_aggregate;
19436+
: diag::ext_flexible_array_empty_aggregate_gnu;
1944119437

1944219438
if (DiagID)
1944319439
Diag(FD->getLocation(), DiagID)

clang/lib/Sema/SemaInit.cpp

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2329,11 +2329,11 @@ void InitListChecker::CheckStructUnionTypes(
23292329
break;
23302330
}
23312331

2332-
// We've already initialized a member of a union. We're done.
2332+
// We've already initialized a member of a union. We can stop entirely.
23332333
if (InitializedSomething && RD->isUnion())
2334-
break;
2334+
return;
23352335

2336-
// If we've hit the flexible array member at the end, we're done.
2336+
// Stop if we've hit a flexible array member.
23372337
if (Field->getType()->isIncompleteArrayType())
23382338
break;
23392339

@@ -2456,6 +2456,11 @@ void InitListChecker::CheckStructUnionTypes(
24562456
else
24572457
CheckImplicitInitList(MemberEntity, IList, Field->getType(), Index,
24582458
StructuredList, StructuredIndex);
2459+
2460+
if (RD->isUnion() && StructuredList) {
2461+
// Initialize the first field within the union.
2462+
StructuredList->setInitializedFieldInUnion(*Field);
2463+
}
24592464
}
24602465

24612466
/// Expand a field designator that refers to a member of an

clang/test/C/drs/dr5xx.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ void dr502(void) {
2929
*/
3030
struct t {
3131
int i;
32-
struct { int a[]; }; /* expected-error {{flexible array member 'a' not allowed in otherwise empty struct}}
32+
struct { int a[]; }; /* expected-warning {{flexible array member 'a' in otherwise empty struct is a GNU extension}}
3333
c89only-warning {{flexible array members are a C99 feature}}
3434
expected-warning {{'' may not be nested in a struct due to flexible array member}}
3535
*/

clang/test/CodeGen/flexible-array-init.c

Lines changed: 82 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,15 @@
33
struct { int x; int y[]; } a = { 1, 7, 11 };
44
// CHECK: @a ={{.*}} global { i32, [2 x i32] } { i32 1, [2 x i32] [i32 7, i32 11] }
55

6+
struct { int y[]; } a1 = { 8, 12 };
7+
// CHECK: @a1 ={{.*}} global { [2 x i32] } { [2 x i32] [i32 8, i32 12] }
8+
69
struct { int x; int y[]; } b = { 1, { 13, 15 } };
710
// CHECK: @b ={{.*}} global { i32, [2 x i32] } { i32 1, [2 x i32] [i32 13, i32 15] }
811

12+
struct { int y[]; } b1 = { { 14, 16 } };
13+
// CHECK: @b1 ={{.*}} global { [2 x i32] } { [2 x i32] [i32 14, i32 16] }
14+
915
// sizeof(c) == 8, so this global should be at least 8 bytes.
1016
struct { int x; char c; char y[]; } c = { 1, 2, { 13, 15 } };
1117
// CHECK: @c ={{.*}} global { i32, i8, [2 x i8] } { i32 1, i8 2, [2 x i8] c"\0D\0F" }
@@ -21,10 +27,79 @@ struct __attribute((packed, aligned(4))) { char a; int x; char z[]; } e = { 1, 2
2127
struct { int x; char y[]; } f = { 1, { 13, 15 } };
2228
// CHECK: @f ={{.*}} global <{ i32, [2 x i8] }> <{ i32 1, [2 x i8] c"\0D\0F" }>
2329

24-
union {
25-
struct {
26-
int a;
27-
char b[];
28-
} x;
29-
} in_union = {};
30-
// CHECK: @in_union ={{.*}} global %union.anon zeroinitializer
30+
struct __attribute((packed)) { short a; char z[]; } g = { 2, { 11, 13, 15 } };
31+
// CHECK: @g ={{.*}} <{ i16, [3 x i8] }> <{ i16 2, [3 x i8] c"\0B\0D\0F" }>,
32+
33+
// Last member is the potential flexible array, unnamed initializer skips it.
34+
struct { int a; union { int b; short x; }; int c; int d; } h = {1, 2, {}, 3};
35+
// CHECK: @h = global %struct.anon{{.*}} { i32 1, %union.anon{{.*}} { i32 2 }, i32 0, i32 3 }
36+
struct { int a; union { int b; short x[0]; }; int c; int d; } h0 = {1, 2, {}, 3};
37+
// CHECK: @h0 = global %struct.anon{{.*}} { i32 1, %union.anon{{.*}} { i32 2 }, i32 0, i32 3 }
38+
struct { int a; union { int b; short x[1]; }; int c; int d; } h1 = {1, 2, {}, 3};
39+
// CHECK: @h1 = global %struct.anon{{.*}} { i32 1, %union.anon{{.*}} { i32 2 }, i32 0, i32 3 }
40+
struct {
41+
int a;
42+
union {
43+
int b;
44+
struct {
45+
struct { } __ununsed;
46+
short x[];
47+
};
48+
};
49+
int c;
50+
int d;
51+
} hiding = {1, 2, {}, 3};
52+
// CHECK: @hiding = global %struct.anon{{.*}} { i32 1, %union.anon{{.*}} { i32 2 }, i32 0, i32 3 }
53+
struct { int a; union { int b; short x[]; }; int c; int d; } hf = {1, 2, {}, 3};
54+
// CHECK: @hf = global %struct.anon{{.*}} { i32 1, %union.anon{{.*}} { i32 2 }, i32 0, i32 3 }
55+
56+
// First member is the potential flexible array, initialization requires braces.
57+
struct { int a; union { short x; int b; }; int c; int d; } i = {1, 2, {}, 3};
58+
// CHECK: @i = global { i32, { i16, [2 x i8] }, i32, i32 } { i32 1, { i16, [2 x i8] } { i16 2, [2 x i8] undef }, i32 0, i32 3 }
59+
struct { int a; union { short x[0]; int b; }; int c; int d; } i0 = {1, {}, 2, 3};
60+
// CHECK: @i0 = global { i32, { [0 x i16], [4 x i8] }, i32, i32 } { i32 1, { [0 x i16], [4 x i8] } { [0 x i16] zeroinitializer, [4 x i8] undef }, i32 2, i32 3 }
61+
struct { int a; union { short x[1]; int b; }; int c; int d; } i1 = {1, {2}, {}, 3};
62+
// CHECK: @i1 = global { i32, { [1 x i16], [2 x i8] }, i32, i32 } { i32 1, { [1 x i16], [2 x i8] } { [1 x i16] [i16 2], [2 x i8] undef }, i32 0, i32 3 }
63+
struct { int a; union { short x[]; int b; }; int c; int d; } i_f = {4, {}, {}, 6};
64+
// CHECK: @i_f = global { i32, { [0 x i16], [4 x i8] }, i32, i32 } { i32 4, { [0 x i16], [4 x i8] } { [0 x i16] zeroinitializer, [4 x i8] undef }, i32 0, i32 6 }
65+
66+
// Named initializers; order doesn't matter.
67+
struct { int a; union { int b; short x; }; int c; int d; } hn = {.a = 1, .x = 2, .c = 3};
68+
// CHECK: @hn = global { i32, { i16, [2 x i8] }, i32, i32 } { i32 1, { i16, [2 x i8] } { i16 2, [2 x i8] undef }, i32 3, i32 0 }
69+
struct { int a; union { int b; short x[0]; }; int c; int d; } hn0 = {.a = 1, .x = {2}, .c = 3};
70+
// CHECK: @hn0 = global { i32, { [0 x i16], [4 x i8] }, i32, i32 } { i32 1, { [0 x i16], [4 x i8] } { [0 x i16] zeroinitializer, [4 x i8] undef }, i32 3, i32 0 }
71+
struct { int a; union { int b; short x[1]; }; int c; int d; } hn1 = {.a = 1, .x = {2}, .c = 3};
72+
// CHECK: @hn1 = global { i32, { [1 x i16], [2 x i8] }, i32, i32 } { i32 1, { [1 x i16], [2 x i8] } { [1 x i16] [i16 2], [2 x i8] undef }, i32 3, i32 0 }
73+
74+
struct { char a[]; } empty_struct = {};
75+
// CHECK: @empty_struct ={{.*}} global %struct.anon{{.*}} zeroinitializer, align 1
76+
77+
struct { char a[]; } empty_struct0 = {0};
78+
// CHECK: @empty_struct0 = global { [1 x i8] } zeroinitializer, align 1
79+
80+
union { struct { int a; char b[]; }; } struct_in_union = {};
81+
// CHECK: @struct_in_union = global %union.anon{{.*}} zeroinitializer, align 4
82+
83+
union { struct { int a; char b[]; }; } struct_in_union0 = {0};
84+
// CHECK: @struct_in_union0 = global %union.anon{{.*}} zeroinitializer, align 4
85+
86+
union { int a; char b[]; } trailing_in_union = {};
87+
// CHECK: @trailing_in_union = global %union.anon{{.*}} zeroinitializer, align 4
88+
89+
union { int a; char b[]; } trailing_in_union0 = {0};
90+
// CHECK: @trailing_in_union0 = global %union.anon{{.*}} zeroinitializer, align 4
91+
92+
union { char a[]; } only_in_union = {};
93+
// CHECK: @only_in_union = global %union.anon{{.*}} zeroinitializer, align 1
94+
95+
union { char a[]; } only_in_union0 = {0};
96+
// CHECK: @only_in_union0 = global { [1 x i8] } zeroinitializer, align 1
97+
98+
union { char a[]; int b; } first_in_union = {};
99+
// CHECK: @first_in_union = global { [0 x i8], [4 x i8] } { [0 x i8] zeroinitializer, [4 x i8] undef }, align 4
100+
101+
union { char a[]; int b; } first_in_union0 = {0};
102+
// CHECK: @first_in_union0 = global { [1 x i8], [3 x i8] } { [1 x i8] zeroinitializer, [3 x i8] undef }, align 4
103+
104+
union { char a[]; int b; } first_in_union123 = { {1, 2, 3} };
105+
// CHECK: @first_in_union123 = global { [3 x i8], i8 } { [3 x i8] c"\01\02\03", i8 undef }, align 4
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// RUN: %clang_cc1 -triple i386-unknown-unknown -x c++ -emit-llvm -o - %s | FileCheck %s
2+
3+
union _u { char a[]; } u = {};
4+
union _u0 { char a[]; } u0 = {0};
5+
6+
// CHECK: %union._u = type { [0 x i8] }
7+
8+
// CHECK: @u = global %union._u zeroinitializer, align 1
9+
// CHECK: @u0 = global { [1 x i8] } zeroinitializer, align 1
10+
11+
union { char a[]; } z = {};
12+
// CHECK: @z = internal global %union.{{.*}} zeroinitializer, align 1
13+
union { char a[]; } z0 = {0};
14+
// CHECK: @z0 = internal global { [1 x i8] } zeroinitializer, align 1
15+
16+
/* C++ requires global anonymous unions have static storage, so we have to
17+
reference them to keep them in the IR output. */
18+
char keep(int pick)
19+
{
20+
if (pick)
21+
return z.a[0];
22+
else
23+
return z0.a[0];
24+
}

0 commit comments

Comments
 (0)