Skip to content

Commit

Permalink
[clang] Reject flexible array member in a union in C++
Browse files Browse the repository at this point in the history
It was rejected in C, and in a strange way accepted in C++. However, the
support was never properly tested and fully implemented, so just reject
it in C++ mode as well.

This change also fixes crash on attempt to initialize union with flexible
array member. Due to missing check on union, there was a null expression
added to init list that caused crash later.

Fixes #61746

Reviewed By: aaron.ballman

Differential Revision: https://reviews.llvm.org/D147626
  • Loading branch information
Fznamznon committed May 3, 2023
1 parent 15723e6 commit 22e2db6
Show file tree
Hide file tree
Showing 11 changed files with 22 additions and 100 deletions.
1 change: 1 addition & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ C++ Specific Potentially Breaking Changes
-----------------------------------------
- Clang won't search for coroutine_traits in std::experimental namespace any more.
Clang will only search for std::coroutine_traits for coroutines then.
- Clang now rejects unions containing a flexible array member.

ABI Changes in This Version
---------------------------
Expand Down
3 changes: 1 addition & 2 deletions clang/include/clang/Basic/DiagnosticGroups.td
Original file line number Diff line number Diff line change
Expand Up @@ -267,7 +267,6 @@ def ExtraSemi : DiagGroup<"extra-semi", [CXX98CompatExtraSemi,
CXX11ExtraSemi]>;

def GNUFlexibleArrayInitializer : DiagGroup<"gnu-flexible-array-initializer">;
def GNUFlexibleArrayUnionMember : DiagGroup<"gnu-flexible-array-union-member">;
def GNUFoldingConstant : DiagGroup<"gnu-folding-constant">;
def FormatInsufficientArgs : DiagGroup<"format-insufficient-args">;
def FormatExtraArgs : DiagGroup<"format-extra-args">;
Expand Down Expand Up @@ -1137,7 +1136,7 @@ def GNU : DiagGroup<"gnu", [GNUAlignofExpression, GNUAnonymousStruct,
GNUConditionalOmittedOperand, GNUDesignator,
GNUEmptyStruct,
VLAExtension, GNUFlexibleArrayInitializer,
GNUFlexibleArrayUnionMember, GNUFoldingConstant,
GNUFoldingConstant,
GNUImaginaryConstant, GNUIncludeNext,
GNULabelsAsValue, GNULineMarker, GNUNullPointerArithmetic,
GNUOffsetofExtensions, GNUPointerArith,
Expand Down
8 changes: 3 additions & 5 deletions clang/include/clang/Basic/DiagnosticSemaKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -6232,6 +6232,9 @@ def ext_variable_sized_type_in_struct : ExtWarn<

def ext_c99_flexible_array_member : Extension<
"flexible array members are a C99 feature">, InGroup<C99>;
// Flexible array members in unions are not supported, but union case is still
// present in the diagnostic so it matches TagTypeKind enum and can be emitted
// with Diag(...) << ... << SomeTagDecl->getTagKind().
def err_flexible_array_virtual_base : Error<
"flexible array member %0 not allowed in "
"%select{struct|interface|union|class|enum}1 which has a virtual base class">;
Expand All @@ -6254,15 +6257,10 @@ def ext_flexible_array_empty_aggregate_ms : Extension<
InGroup<MicrosoftFlexibleArray>;
def err_flexible_array_union : Error<
"flexible array member %0 in a union is not allowed">;
def ext_flexible_array_union_ms : Extension<
"flexible array member %0 in a union is a Microsoft extension">,
InGroup<MicrosoftFlexibleArray>;
def ext_flexible_array_empty_aggregate_gnu : Extension<
"flexible array member %0 in otherwise empty "
"%select{struct|interface|union|class|enum}1 is a GNU extension">,
InGroup<GNUEmptyStruct>;
def ext_flexible_array_union_gnu : Extension<
"flexible array member %0 in a union is a GNU extension">, InGroup<GNUFlexibleArrayUnionMember>;

def err_flexible_array_not_at_end : Error<
"flexible array member %0 with type %1 is not at the end of"
Expand Down
9 changes: 2 additions & 7 deletions clang/lib/Sema/SemaDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18690,8 +18690,7 @@ void Sema::ActOnFields(Scope *S, SourceLocation RecLoc, Decl *EnclosingDecl,
if (Record) {
// Flexible array member.
// Microsoft and g++ is more permissive regarding flexible array.
// It will accept flexible array in union and also
// as the sole element of a struct/class.
// It will accept flexible array as the sole element of a struct/class.
unsigned DiagID = 0;
if (!Record->isUnion() && !IsLastField) {
Diag(FD->getLocation(), diag::err_flexible_array_not_at_end)
Expand All @@ -18701,11 +18700,7 @@ void Sema::ActOnFields(Scope *S, SourceLocation RecLoc, Decl *EnclosingDecl,
EnclosingDecl->setInvalidDecl();
continue;
} else if (Record->isUnion())
DiagID = getLangOpts().MicrosoftExt
? diag::ext_flexible_array_union_ms
: getLangOpts().CPlusPlus
? diag::ext_flexible_array_union_gnu
: diag::err_flexible_array_union;
DiagID = diag::err_flexible_array_union;
else if (NumNamedMembers < 1)
DiagID = getLangOpts().MicrosoftExt
? diag::ext_flexible_array_empty_aggregate_ms
Expand Down
2 changes: 1 addition & 1 deletion clang/lib/Sema/SemaInit.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -805,7 +805,7 @@ InitListChecker::FillInEmptyInitializations(const InitializedEntity &Entity,
// order to leave them uninitialized, the ILE is expanded and the extra
// fields are then filled with NoInitExpr.
unsigned NumElems = numStructUnionElements(ILE->getType());
if (RDecl->hasFlexibleArrayMember())
if (!RDecl->isUnion() && RDecl->hasFlexibleArrayMember())
++NumElems;
if (!VerifyOnly && ILE->getNumInits() < NumElems)
ILE->resizeInits(SemaRef.Context, NumElems);
Expand Down
54 changes: 0 additions & 54 deletions clang/test/Layout/aix-power-alignment-typedef.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,57 +19,3 @@ int b = sizeof(A);

} // namespace test1

namespace test2 {
typedef double Dbl __attribute__((__aligned__(2)));
typedef Dbl DblArr[];

union U {
DblArr da;
char x;
};

int x = sizeof(U);

// CHECK: 0 | union test2::U
// CHECK-NEXT: 0 | DblArr da
// CHECK-NEXT: 0 | char x
// CHECK-NEXT: | [sizeof=2, dsize=2, align=2, preferredalign=2,
// CHECK-NEXT: | nvsize=2, nvalign=2, preferrednvalign=2]

} // namespace test2

namespace test3 {
typedef double DblArr[] __attribute__((__aligned__(2)));

union U {
DblArr da;
char x;
};

int x = sizeof(U);

// CHECK: 0 | union test3::U
// CHECK-NEXT: 0 | DblArr da
// CHECK-NEXT: 0 | char x
// CHECK-NEXT: | [sizeof=2, dsize=2, align=2, preferredalign=2,
// CHECK-NEXT: | nvsize=2, nvalign=2, preferrednvalign=2]

} // namespace test3

namespace test4 {
typedef double Dbl __attribute__((__aligned__(2)));

union U {
Dbl DblArr[];
char x;
};

int x = sizeof(U);

// CHECK: 0 | union test4::U
// CHECK-NEXT: 0 | Dbl[] DblArr
// CHECK-NEXT: 0 | char x
// CHECK-NEXT: | [sizeof=2, dsize=2, align=2, preferredalign=2,
// CHECK-NEXT: | nvsize=2, nvalign=2, preferrednvalign=2]

} // namespace test4
4 changes: 2 additions & 2 deletions clang/test/Sema/MicrosoftExtensions.c
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ struct PR28407
struct C {
int l;
union {
int c1[]; /* expected-warning {{flexible array member 'c1' in a union is a Microsoft extension}} */
char c2[]; /* expected-warning {{flexible array member 'c2' in a union is a Microsoft extension}} */
int c1[]; /* expected-error {{flexible array member 'c1' in a union is not allowed}} */
char c2[]; /* expected-error {{flexible array member 'c2' in a union is not allowed}} */
};
};

Expand Down
3 changes: 3 additions & 0 deletions clang/test/Sema/init.c
Original file line number Diff line number Diff line change
Expand Up @@ -164,3 +164,6 @@ struct vortexstruct vortexvar = { "asdf" };

typedef struct { uintptr_t x : 2; } StructWithBitfield;
StructWithBitfield bitfieldvar = { (uintptr_t)&bitfieldvar }; // expected-error {{initializer element is not a compile-time constant}}

// GH61746
union { char x[]; } r = {0}; // expected-error {{flexible array member 'x' in a union is not allowed}}
4 changes: 2 additions & 2 deletions clang/test/SemaCXX/flexible-array-test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ void QMap<Key, T>::insert(const Key &, const T &avalue)

struct Rec {
union { // expected-warning-re {{variable sized type '{{.*}}' not at the end of a struct or class is a GNU extension}}
int u0[];
int u0[]; // expected-error {{flexible array member 'u0' in a union is not allowed}}
};
int x;
} rec;
Expand Down Expand Up @@ -63,7 +63,7 @@ class A {

union B {
int s;
char c[];
char c[]; // expected-error {{flexible array member 'c' in a union is not allowed}}
};

class C {
Expand Down
32 changes: 6 additions & 26 deletions clang/test/SemaCXX/gnu-flags.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,34 +8,27 @@

// RUN: %clang_cc1 -fsyntax-only -verify %s -DALL -Wno-gnu \
// RUN: -Wgnu-anonymous-struct -Wredeclared-class-member \
// RUN: -Wgnu-flexible-array-union-member -Wgnu-folding-constant \
// RUN: -Wgnu-empty-struct
// RUN: -Wgnu-folding-constant -Wgnu-empty-struct
// RUN: %clang_cc1 -fsyntax-only -verify -std=c++98 %s -DALL -Wno-gnu \
// RUN: -Wgnu-anonymous-struct -Wredeclared-class-member \
// RUN: -Wgnu-flexible-array-union-member -Wgnu-folding-constant \
// RUN: -Wgnu-empty-struct
// RUN: -Wgnu-folding-constant -Wgnu-empty-struct
// RUN: %clang_cc1 -fsyntax-only -verify -std=c++11 %s -DALL -Wno-gnu \
// RUN: -Wgnu-anonymous-struct -Wredeclared-class-member \
// RUN: -Wgnu-flexible-array-union-member -Wgnu-folding-constant \
// RUN: -Wgnu-empty-struct
// RUN: -Wgnu-folding-constant -Wgnu-empty-struct

// RUN: %clang_cc1 -fsyntax-only -verify %s -DNONE -Wgnu \
// RUN: -Wno-gnu-anonymous-struct -Wno-redeclared-class-member \
// RUN: -Wno-gnu-flexible-array-union-member -Wno-gnu-folding-constant \
// RUN: -Wno-gnu-empty-struct
// RUN: -Wno-gnu-folding-constant -Wno-gnu-empty-struct
// RUN: %clang_cc1 -fsyntax-only -verify -std=c++98 %s -DNONE -Wgnu \
// RUN: -Wno-gnu-anonymous-struct -Wno-redeclared-class-member \
// RUN: -Wno-gnu-flexible-array-union-member -Wno-gnu-folding-constant \
// RUN: -Wno-gnu-empty-struct
// RUN: -Wno-gnu-folding-constant -Wno-gnu-empty-struct
// RUN: %clang_cc1 -fsyntax-only -verify -std=c++11 %s -DNONE -Wgnu \
// RUN: -Wno-gnu-anonymous-struct -Wno-redeclared-class-member \
// RUN: -Wno-gnu-flexible-array-union-member -Wno-gnu-folding-constant \
// RUN: -Wno-gnu-empty-struct
// RUN: -Wno-gnu-folding-constant -Wno-gnu-empty-struct

// Additional disabled tests:
// %clang_cc1 -fsyntax-only -verify %s -DANONYMOUSSTRUCT -Wno-gnu -Wgnu-anonymous-struct
// %clang_cc1 -fsyntax-only -verify %s -DREDECLAREDCLASSMEMBER -Wno-gnu -Wredeclared-class-member
// %clang_cc1 -fsyntax-only -verify %s -DFLEXIBLEARRAYUNIONMEMBER -Wno-gnu -Wgnu-flexible-array-union-member
// %clang_cc1 -fsyntax-only -verify %s -DFOLDINGCONSTANT -Wno-gnu -Wgnu-folding-constant
// %clang_cc1 -fsyntax-only -verify %s -DEMPTYSTRUCT -Wno-gnu -Wgnu-empty-struct

Expand Down Expand Up @@ -70,19 +63,6 @@ namespace rcm {
};
}


#if ALL || FLEXIBLEARRAYUNIONMEMBER
// expected-warning@+6 {{flexible array member 'c1' in a union is a GNU extension}}
#endif

struct faum {
int l;
union {
int c1[];
};
};


#if (ALL || FOLDINGCONSTANT) && (__cplusplus <= 199711L) // C++03 or earlier modes
// expected-warning@+4 {{in-class initializer for static data member is not a constant expression; folding it to a constant is a GNU extension}}
#endif
Expand Down
2 changes: 1 addition & 1 deletion clang/test/SemaObjCXX/flexible-array.mm
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

union VariableSizeUnion {
int s;
char c[];
char c[]; //expected-error {{flexible array member 'c' in a union is not allowed}}
};

@interface LastUnionIvar {
Expand Down

0 comments on commit 22e2db6

Please sign in to comment.