Skip to content

Commit 6b8e3c0

Browse files
committed
[c++2a] P0683R1: Permit default member initializers for bit-fields.
This would be trivial, except that our in-memory and serialized representations for FieldDecls assumed that this can't happen. llvm-svn: 311867
1 parent bebcbfb commit 6b8e3c0

File tree

10 files changed

+146
-87
lines changed

10 files changed

+146
-87
lines changed

clang/include/clang/AST/Decl.h

Lines changed: 55 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -2364,9 +2364,9 @@ class FunctionDecl : public DeclaratorDecl, public DeclContext,
23642364
/// FieldDecl - An instance of this class is created by Sema::ActOnField to
23652365
/// represent a member of a struct/union/class.
23662366
class FieldDecl : public DeclaratorDecl, public Mergeable<FieldDecl> {
2367-
// FIXME: This can be packed into the bitfields in Decl.
2367+
unsigned BitField : 1;
23682368
unsigned Mutable : 1;
2369-
mutable unsigned CachedFieldIndex : 31;
2369+
mutable unsigned CachedFieldIndex : 30;
23702370

23712371
/// The kinds of value we can store in InitializerOrBitWidth.
23722372
///
@@ -2376,7 +2376,7 @@ class FieldDecl : public DeclaratorDecl, public Mergeable<FieldDecl> {
23762376
/// If the pointer is null, there's nothing special. Otherwise,
23772377
/// this is a bitfield and the pointer is the Expr* storing the
23782378
/// bit-width.
2379-
ISK_BitWidthOrNothing = (unsigned) ICIS_NoInit,
2379+
ISK_NoInit = (unsigned) ICIS_NoInit,
23802380

23812381
/// The pointer is an (optional due to delayed parsing) Expr*
23822382
/// holding the copy-initializer.
@@ -2391,27 +2391,34 @@ class FieldDecl : public DeclaratorDecl, public Mergeable<FieldDecl> {
23912391
ISK_CapturedVLAType,
23922392
};
23932393

2394-
/// \brief Storage for either the bit-width, the in-class
2395-
/// initializer, or the captured variable length array bound.
2396-
///
2397-
/// We can safely combine these because in-class initializers are
2398-
/// not permitted for bit-fields, and both are exclusive with VLA
2399-
/// captures.
2394+
/// If this is a bitfield with a default member initializer, this
2395+
/// structure is used to represent the two expressions.
2396+
struct InitAndBitWidth {
2397+
Expr *Init;
2398+
Expr *BitWidth;
2399+
};
2400+
2401+
/// \brief Storage for either the bit-width, the in-class initializer, or
2402+
/// both (via InitAndBitWidth), or the captured variable length array bound.
24002403
///
24012404
/// If the storage kind is ISK_InClassCopyInit or
24022405
/// ISK_InClassListInit, but the initializer is null, then this
2403-
/// field has an in-class initializer which has not yet been parsed
2406+
/// field has an in-class initializer that has not yet been parsed
24042407
/// and attached.
2408+
// FIXME: Tail-allocate this to reduce the size of FieldDecl in the
2409+
// overwhelmingly common case that we have none of these things.
24052410
llvm::PointerIntPair<void *, 2, InitStorageKind> InitStorage;
2411+
24062412
protected:
24072413
FieldDecl(Kind DK, DeclContext *DC, SourceLocation StartLoc,
24082414
SourceLocation IdLoc, IdentifierInfo *Id,
24092415
QualType T, TypeSourceInfo *TInfo, Expr *BW, bool Mutable,
24102416
InClassInitStyle InitStyle)
24112417
: DeclaratorDecl(DK, DC, IdLoc, Id, T, TInfo, StartLoc),
2412-
Mutable(Mutable), CachedFieldIndex(0),
2413-
InitStorage(BW, (InitStorageKind) InitStyle) {
2414-
assert((!BW || InitStyle == ICIS_NoInit) && "got initializer for bitfield");
2418+
BitField(false), Mutable(Mutable), CachedFieldIndex(0),
2419+
InitStorage(nullptr, (InitStorageKind) InitStyle) {
2420+
if (BW)
2421+
setBitWidth(BW);
24152422
}
24162423

24172424
public:
@@ -2431,10 +2438,7 @@ class FieldDecl : public DeclaratorDecl, public Mergeable<FieldDecl> {
24312438
bool isMutable() const { return Mutable; }
24322439

24332440
/// \brief Determines whether this field is a bitfield.
2434-
bool isBitField() const {
2435-
return InitStorage.getInt() == ISK_BitWidthOrNothing &&
2436-
InitStorage.getPointer() != nullptr;
2437-
}
2441+
bool isBitField() const { return BitField; }
24382442

24392443
/// @brief Determines whether this is an unnamed bitfield.
24402444
bool isUnnamedBitfield() const { return isBitField() && !getDeclName(); }
@@ -2446,66 +2450,76 @@ class FieldDecl : public DeclaratorDecl, public Mergeable<FieldDecl> {
24462450
bool isAnonymousStructOrUnion() const;
24472451

24482452
Expr *getBitWidth() const {
2449-
return isBitField()
2450-
? static_cast<Expr *>(InitStorage.getPointer())
2451-
: nullptr;
2453+
if (!BitField)
2454+
return nullptr;
2455+
void *Ptr = InitStorage.getPointer();
2456+
if (getInClassInitStyle())
2457+
return static_cast<InitAndBitWidth*>(Ptr)->BitWidth;
2458+
return static_cast<Expr*>(Ptr);
24522459
}
24532460
unsigned getBitWidthValue(const ASTContext &Ctx) const;
24542461

24552462
/// setBitWidth - Set the bit-field width for this member.
24562463
// Note: used by some clients (i.e., do not remove it).
24572464
void setBitWidth(Expr *Width) {
2458-
assert(InitStorage.getInt() == ISK_BitWidthOrNothing &&
2459-
InitStorage.getPointer() == nullptr &&
2460-
"bit width, initializer or captured type already set");
2461-
InitStorage.setPointerAndInt(Width, ISK_BitWidthOrNothing);
2465+
assert(!hasCapturedVLAType() && !BitField &&
2466+
"bit width or captured type already set");
2467+
assert(Width && "no bit width specified");
2468+
InitStorage.setPointer(
2469+
InitStorage.getInt()
2470+
? new (getASTContext())
2471+
InitAndBitWidth{getInClassInitializer(), Width}
2472+
: static_cast<void*>(Width));
2473+
BitField = true;
24622474
}
24632475

24642476
/// removeBitWidth - Remove the bit-field width from this member.
24652477
// Note: used by some clients (i.e., do not remove it).
24662478
void removeBitWidth() {
24672479
assert(isBitField() && "no bitfield width to remove");
2468-
InitStorage.setPointerAndInt(nullptr, ISK_BitWidthOrNothing);
2480+
InitStorage.setPointer(getInClassInitializer());
2481+
BitField = false;
24692482
}
24702483

2471-
/// getInClassInitStyle - Get the kind of (C++11) in-class initializer which
2472-
/// this field has.
2484+
/// Get the kind of (C++11) default member initializer that this field has.
24732485
InClassInitStyle getInClassInitStyle() const {
24742486
InitStorageKind storageKind = InitStorage.getInt();
24752487
return (storageKind == ISK_CapturedVLAType
24762488
? ICIS_NoInit : (InClassInitStyle) storageKind);
24772489
}
24782490

2479-
/// hasInClassInitializer - Determine whether this member has a C++11 in-class
2480-
/// initializer.
2491+
/// Determine whether this member has a C++11 default member initializer.
24812492
bool hasInClassInitializer() const {
24822493
return getInClassInitStyle() != ICIS_NoInit;
24832494
}
24842495

2485-
/// getInClassInitializer - Get the C++11 in-class initializer for this
2486-
/// member, or null if one has not been set. If a valid declaration has an
2487-
/// in-class initializer, but this returns null, then we have not parsed and
2488-
/// attached it yet.
2496+
/// Get the C++11 default member initializer for this member, or null if one
2497+
/// has not been set. If a valid declaration has a default member initializer,
2498+
/// but this returns null, then we have not parsed and attached it yet.
24892499
Expr *getInClassInitializer() const {
2490-
return hasInClassInitializer()
2491-
? static_cast<Expr *>(InitStorage.getPointer())
2492-
: nullptr;
2500+
if (!hasInClassInitializer())
2501+
return nullptr;
2502+
void *Ptr = InitStorage.getPointer();
2503+
if (BitField)
2504+
return static_cast<InitAndBitWidth*>(Ptr)->Init;
2505+
return static_cast<Expr*>(Ptr);
24932506
}
24942507

24952508
/// setInClassInitializer - Set the C++11 in-class initializer for this
24962509
/// member.
24972510
void setInClassInitializer(Expr *Init) {
2498-
assert(hasInClassInitializer() &&
2499-
InitStorage.getPointer() == nullptr &&
2500-
"bit width, initializer or captured type already set");
2501-
InitStorage.setPointer(Init);
2511+
assert(hasInClassInitializer() && !getInClassInitializer());
2512+
if (BitField)
2513+
static_cast<InitAndBitWidth*>(InitStorage.getPointer())->Init = Init;
2514+
else
2515+
InitStorage.setPointer(Init);
25022516
}
25032517

25042518
/// removeInClassInitializer - Remove the C++11 in-class initializer from this
25052519
/// member.
25062520
void removeInClassInitializer() {
25072521
assert(hasInClassInitializer() && "no initializer to remove");
2508-
InitStorage.setPointerAndInt(nullptr, ISK_BitWidthOrNothing);
2522+
InitStorage.setPointerAndInt(getBitWidth(), ISK_NoInit);
25092523
}
25102524

25112525
/// \brief Determine whether this member captures the variable length array

clang/include/clang/Basic/DiagnosticParseKinds.td

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -730,8 +730,12 @@ def ext_nonstatic_member_init : ExtWarn<
730730
def warn_cxx98_compat_nonstatic_member_init : Warning<
731731
"in-class initialization of non-static data members is incompatible with C++98">,
732732
InGroup<CXX98Compat>, DefaultIgnore;
733-
def err_bitfield_member_init: Error<
734-
"bit-field member cannot have an in-class initializer">;
733+
def ext_bitfield_member_init: ExtWarn<
734+
"default member initializer for bit-field is a C++2a extension">,
735+
InGroup<CXX2a>;
736+
def warn_cxx17_compat_bitfield_member_init: Warning<
737+
"default member initializer for bit-field is incompatible with "
738+
"C++ standards before C++2a">, InGroup<CXXPre2aCompat>, DefaultIgnore;
735739
def err_incomplete_array_member_init: Error<
736740
"array bound cannot be deduced from an in-class initializer">;
737741

clang/lib/AST/Decl.cpp

Lines changed: 8 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3553,8 +3553,7 @@ bool FieldDecl::isAnonymousStructOrUnion() const {
35533553

35543554
unsigned FieldDecl::getBitWidthValue(const ASTContext &Ctx) const {
35553555
assert(isBitField() && "not a bitfield");
3556-
auto *BitWidth = static_cast<Expr *>(InitStorage.getPointer());
3557-
return BitWidth->EvaluateKnownConstInt(Ctx).getZExtValue();
3556+
return getBitWidth()->EvaluateKnownConstInt(Ctx).getZExtValue();
35583557
}
35593558

35603559
unsigned FieldDecl::getFieldIndex() const {
@@ -3577,25 +3576,18 @@ unsigned FieldDecl::getFieldIndex() const {
35773576
}
35783577

35793578
SourceRange FieldDecl::getSourceRange() const {
3580-
switch (InitStorage.getInt()) {
3581-
// All three of these cases store an optional Expr*.
3582-
case ISK_BitWidthOrNothing:
3583-
case ISK_InClassCopyInit:
3584-
case ISK_InClassListInit:
3585-
if (const auto *E = static_cast<const Expr *>(InitStorage.getPointer()))
3586-
return SourceRange(getInnerLocStart(), E->getLocEnd());
3587-
// FALLTHROUGH
3588-
3589-
case ISK_CapturedVLAType:
3590-
return DeclaratorDecl::getSourceRange();
3591-
}
3592-
llvm_unreachable("bad init storage kind");
3579+
const Expr *FinalExpr = getInClassInitializer();
3580+
if (!FinalExpr)
3581+
FinalExpr = getBitWidth();
3582+
if (FinalExpr)
3583+
return SourceRange(getInnerLocStart(), FinalExpr->getLocEnd());
3584+
return DeclaratorDecl::getSourceRange();
35933585
}
35943586

35953587
void FieldDecl::setCapturedVLAType(const VariableArrayType *VLAType) {
35963588
assert((getParent()->isLambda() || getParent()->isCapturedRecord()) &&
35973589
"capturing type in non-lambda or captured record.");
3598-
assert(InitStorage.getInt() == ISK_BitWidthOrNothing &&
3590+
assert(InitStorage.getInt() == ISK_NoInit &&
35993591
InitStorage.getPointer() == nullptr &&
36003592
"bit width, initializer or captured type already set");
36013593
InitStorage.setPointerAndInt(const_cast<VariableArrayType *>(VLAType),

clang/lib/Parse/ParseDeclCXX.cpp

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2720,10 +2720,7 @@ Parser::ParseCXXClassMemberDeclaration(AccessSpecifier AS,
27202720
InClassInitStyle HasInClassInit = ICIS_NoInit;
27212721
bool HasStaticInitializer = false;
27222722
if (Tok.isOneOf(tok::equal, tok::l_brace) && PureSpecLoc.isInvalid()) {
2723-
if (BitfieldSize.get()) {
2724-
Diag(Tok, diag::err_bitfield_member_init);
2725-
SkipUntil(tok::comma, StopAtSemi | StopBeforeMatch);
2726-
} else if (DeclaratorInfo.isDeclarationOfFunction()) {
2723+
if (DeclaratorInfo.isDeclarationOfFunction()) {
27272724
// It's a pure-specifier.
27282725
if (!TryConsumePureSpecifier(/*AllowFunctionDefinition*/ false))
27292726
// Parse it as an expression so that Sema can diagnose it.
@@ -2734,6 +2731,10 @@ Parser::ParseCXXClassMemberDeclaration(AccessSpecifier AS,
27342731
DeclSpec::SCS_typedef &&
27352732
!DS.isFriendSpecified()) {
27362733
// It's a default member initializer.
2734+
if (BitfieldSize.get())
2735+
Diag(Tok, getLangOpts().CPlusPlus2a
2736+
? diag::warn_cxx17_compat_bitfield_member_init
2737+
: diag::ext_bitfield_member_init);
27372738
HasInClassInit = Tok.is(tok::equal) ? ICIS_CopyInit : ICIS_ListInit;
27382739
} else {
27392740
HasStaticInitializer = true;

clang/lib/Serialization/ASTReaderDecl.cpp

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1219,16 +1219,17 @@ void ASTDeclReader::VisitObjCPropertyImplDecl(ObjCPropertyImplDecl *D) {
12191219
void ASTDeclReader::VisitFieldDecl(FieldDecl *FD) {
12201220
VisitDeclaratorDecl(FD);
12211221
FD->Mutable = Record.readInt();
1222-
if (int BitWidthOrInitializer = Record.readInt()) {
1223-
FD->InitStorage.setInt(
1224-
static_cast<FieldDecl::InitStorageKind>(BitWidthOrInitializer - 1));
1225-
if (FD->InitStorage.getInt() == FieldDecl::ISK_CapturedVLAType) {
1226-
// Read captured variable length array.
1227-
FD->InitStorage.setPointer(Record.readType().getAsOpaquePtr());
1228-
} else {
1229-
FD->InitStorage.setPointer(Record.readExpr());
1230-
}
1222+
1223+
if (auto ISK = static_cast<FieldDecl::InitStorageKind>(Record.readInt())) {
1224+
FD->InitStorage.setInt(ISK);
1225+
FD->InitStorage.setPointer(ISK == FieldDecl::ISK_CapturedVLAType
1226+
? Record.readType().getAsOpaquePtr()
1227+
: Record.readExpr());
12311228
}
1229+
1230+
if (auto *BW = Record.readExpr())
1231+
FD->setBitWidth(BW);
1232+
12321233
if (!FD->getDeclName()) {
12331234
if (FieldDecl *Tmpl = ReadDeclAs<FieldDecl>())
12341235
Reader.getContext().setInstantiatedFromUnnamedFieldDecl(FD, Tmpl);

clang/lib/Serialization/ASTWriterDecl.cpp

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -849,17 +849,16 @@ void ASTDeclWriter::VisitObjCPropertyImplDecl(ObjCPropertyImplDecl *D) {
849849
void ASTDeclWriter::VisitFieldDecl(FieldDecl *D) {
850850
VisitDeclaratorDecl(D);
851851
Record.push_back(D->isMutable());
852-
if (D->InitStorage.getInt() == FieldDecl::ISK_BitWidthOrNothing &&
853-
D->InitStorage.getPointer() == nullptr) {
854-
Record.push_back(0);
855-
} else if (D->InitStorage.getInt() == FieldDecl::ISK_CapturedVLAType) {
856-
Record.push_back(D->InitStorage.getInt() + 1);
857-
Record.AddTypeRef(
858-
QualType(static_cast<Type *>(D->InitStorage.getPointer()), 0));
859-
} else {
860-
Record.push_back(D->InitStorage.getInt() + 1);
861-
Record.AddStmt(static_cast<Expr *>(D->InitStorage.getPointer()));
862-
}
852+
853+
FieldDecl::InitStorageKind ISK = D->InitStorage.getInt();
854+
Record.push_back(ISK);
855+
if (ISK == FieldDecl::ISK_CapturedVLAType)
856+
Record.AddTypeRef(QualType(D->getCapturedVLAType(), 0));
857+
else if (ISK)
858+
Record.AddStmt(D->getInClassInitializer());
859+
860+
Record.AddStmt(D->getBitWidth());
861+
863862
if (!D->getDeclName())
864863
Record.AddDeclRef(Context.getInstantiatedFromUnnamedFieldDecl(D));
865864

@@ -873,6 +872,7 @@ void ASTDeclWriter::VisitFieldDecl(FieldDecl *D) {
873872
!D->isModulePrivate() &&
874873
!D->getBitWidth() &&
875874
!D->hasInClassInitializer() &&
875+
!D->hasCapturedVLAType() &&
876876
!D->hasExtInfo() &&
877877
!ObjCIvarDecl::classofKind(D->getKind()) &&
878878
!ObjCAtDefsFieldDecl::classofKind(D->getKind()) &&
@@ -1741,7 +1741,7 @@ void ASTWriter::WriteDeclAbbrevs() {
17411741
Abv->Add(BitCodeAbbrevOp(0)); // hasExtInfo
17421742
// FieldDecl
17431743
Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // isMutable
1744-
Abv->Add(BitCodeAbbrevOp(0)); //getBitWidth
1744+
Abv->Add(BitCodeAbbrevOp(0)); // InitStyle
17451745
// Type Source Info
17461746
Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6));
17471747
Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Array));
@@ -1774,7 +1774,7 @@ void ASTWriter::WriteDeclAbbrevs() {
17741774
Abv->Add(BitCodeAbbrevOp(0)); // hasExtInfo
17751775
// FieldDecl
17761776
Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // isMutable
1777-
Abv->Add(BitCodeAbbrevOp(0)); //getBitWidth
1777+
Abv->Add(BitCodeAbbrevOp(0)); // InitStyle
17781778
// ObjC Ivar
17791779
Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); // getAccessControl
17801780
Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); // getSynthesize
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// RUN: %clang_cc1 -std=c++2a -include %s -verify %s
2+
// RUN: %clang_cc1 -std=c++2a -emit-pch %s -o %t
3+
// RUN: %clang_cc1 -std=c++2a -include-pch %t -verify %s -DPCH
4+
5+
#ifndef HEADER
6+
#define HEADER
7+
8+
struct S {
9+
unsigned int n : 5 = 49; // expected-warning {{changes value}}
10+
};
11+
12+
int a;
13+
template<bool B> struct T {
14+
int m : B ? 8 : a = 42;
15+
};
16+
17+
#else
18+
19+
// expected-error@-5 {{constant expression}} expected-note@-5 {{cannot modify}}
20+
21+
static_assert(S().n == 17);
22+
static_assert(T<true>().m == 0);
23+
int q = T<false>().m; // expected-note {{instantiation of}}
24+
25+
#endif
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// RUN: %clang_cc1 -std=c++2a -verify %s
2+
3+
namespace std_example {
4+
int a;
5+
const int b = 0; // expected-note {{here}}
6+
struct S {
7+
int x1 : 8 = 42;
8+
int x2 : 8 { 42 };
9+
int y1 : true ? 8 : a = 42;
10+
int y3 : (true ? 8 : b) = 42;
11+
int z : 1 || new int { 1 };
12+
};
13+
static_assert(S{}.x1 == 42);
14+
static_assert(S{}.x2 == 42);
15+
static_assert(S{}.y1 == 0);
16+
static_assert(S{}.y3 == 42);
17+
static_assert(S{}.z == 0);
18+
19+
struct T {
20+
int y2 : true ? 8 : b = 42; // expected-error {{cannot assign to variable 'b'}}
21+
};
22+
}

clang/test/SemaCXX/member-init.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// RUN: %clang_cc1 -fsyntax-only -fcxx-exceptions -verify -std=c++11 -Wall %s
22

33
struct Bitfield {
4-
int n : 3 = 7; // expected-error {{bit-field member cannot have an in-class initializer}}
4+
int n : 3 = 7; // expected-warning {{C++2a extension}} expected-warning {{changes value from 7 to -1}}
55
};
66

77
int a;

0 commit comments

Comments
 (0)