Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ AST Dumping Potentially Breaking Changes

Clang Frontend Potentially Breaking Changes
-------------------------------------------
- Members of anonymous unions/structs are now injected as ``IndirectFieldDecl`` into the enclosing record even if their names conflict with other names in the scope. These ``IndirectFieldDecl`` are marked invalid.

Clang Python Bindings Potentially Breaking Changes
--------------------------------------------------
Expand Down Expand Up @@ -192,6 +193,8 @@ Bug Fixes to C++ Support
- Fix the dynamic_cast to final class optimization to correctly handle
casts that are guaranteed to fail (#GH137518).
- Fix bug rejecting partial specialization of variable templates with auto NTTPs (#GH118190).
- Fix a crash if errors "member of anonymous [...] redeclares" and
"intializing multiple members of union" coincide (#GH149985).

Bug Fixes to AST Handling
^^^^^^^^^^^^^^^^^^^^^^^^^
Expand Down
77 changes: 42 additions & 35 deletions clang/lib/Sema/SemaDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5494,55 +5494,62 @@ InjectAnonymousStructOrUnionMembers(Sema &SemaRef, Scope *S, DeclContext *Owner,
RecordDecl *AnonRecord, AccessSpecifier AS,
StorageClass SC,
SmallVectorImpl<NamedDecl *> &Chaining) {

bool Invalid = false;

// Look every FieldDecl and IndirectFieldDecl with a name.
for (auto *D : AnonRecord->decls()) {
if ((isa<FieldDecl>(D) || isa<IndirectFieldDecl>(D)) &&
cast<NamedDecl>(D)->getDeclName()) {
ValueDecl *VD = cast<ValueDecl>(D);
if (CheckAnonMemberRedeclaration(SemaRef, S, Owner, VD->getDeclName(),
VD->getLocation(), AnonRecord->isUnion(),
SC)) {
// C++ [class.union]p2:
// The names of the members of an anonymous union shall be
// distinct from the names of any other entity in the
// scope in which the anonymous union is declared.
// C++ [class.union]p2:
// The names of the members of an anonymous union shall be
// distinct from the names of any other entity in the
// scope in which the anonymous union is declared.

const bool FieldInvalid = CheckAnonMemberRedeclaration(
SemaRef, S, Owner, VD->getDeclName(), VD->getLocation(),
AnonRecord->isUnion(), SC);
if (FieldInvalid)
Invalid = true;
} else {
// C++ [class.union]p2:
// For the purpose of name lookup, after the anonymous union
// definition, the members of the anonymous union are
// considered to have been defined in the scope in which the
// anonymous union is declared.
unsigned OldChainingSize = Chaining.size();
if (IndirectFieldDecl *IF = dyn_cast<IndirectFieldDecl>(VD))
Chaining.append(IF->chain_begin(), IF->chain_end());
else
Chaining.push_back(VD);

assert(Chaining.size() >= 2);
NamedDecl **NamedChain =
new (SemaRef.Context)NamedDecl*[Chaining.size()];
for (unsigned i = 0; i < Chaining.size(); i++)
NamedChain[i] = Chaining[i];
// Inject the IndirectFieldDecl even if invalid, because later
// diagnostics may depend on it being present, see findDefaultInitializer.

// C++ [class.union]p2:
// For the purpose of name lookup, after the anonymous union
// definition, the members of the anonymous union are
// considered to have been defined in the scope in which the
// anonymous union is declared.
unsigned OldChainingSize = Chaining.size();
if (IndirectFieldDecl *IF = dyn_cast<IndirectFieldDecl>(VD))
Chaining.append(IF->chain_begin(), IF->chain_end());
else
Chaining.push_back(VD);

assert(Chaining.size() >= 2);
NamedDecl **NamedChain =
new (SemaRef.Context) NamedDecl *[Chaining.size()];
for (unsigned i = 0; i < Chaining.size(); i++)
NamedChain[i] = Chaining[i];

IndirectFieldDecl *IndirectField = IndirectFieldDecl::Create(
SemaRef.Context, Owner, VD->getLocation(), VD->getIdentifier(),
VD->getType(), {NamedChain, Chaining.size()});
IndirectFieldDecl *IndirectField = IndirectFieldDecl::Create(
SemaRef.Context, Owner, VD->getLocation(), VD->getIdentifier(),
VD->getType(), {NamedChain, Chaining.size()});

for (const auto *Attr : VD->attrs())
IndirectField->addAttr(Attr->clone(SemaRef.Context));
for (const auto *Attr : VD->attrs())
IndirectField->addAttr(Attr->clone(SemaRef.Context));

IndirectField->setAccess(AS);
IndirectField->setImplicit();
SemaRef.PushOnScopeChains(IndirectField, S);
IndirectField->setAccess(AS);
IndirectField->setImplicit();
IndirectField->setInvalidDecl(FieldInvalid);
SemaRef.PushOnScopeChains(IndirectField, S);

// That includes picking up the appropriate access specifier.
if (AS != AS_none) IndirectField->setAccess(AS);
// That includes picking up the appropriate access specifier.
if (AS != AS_none)
IndirectField->setAccess(AS);

Chaining.resize(OldChainingSize);
}
Chaining.resize(OldChainingSize);
}
}

Expand Down
5 changes: 4 additions & 1 deletion clang/lib/Sema/SemaDeclCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7012,9 +7012,12 @@ void Sema::CheckCompletedCXXClass(Scope *S, CXXRecordDecl *Record) {
for (DeclContext::lookup_iterator I = R.begin(), E = R.end(); I != E;
++I) {
NamedDecl *D = (*I)->getUnderlyingDecl();
// Invalid IndirectFieldDecls have already been diagnosed with
// err_anonymous_record_member_redecl in
// SemaDecl.cpp:CheckAnonMemberRedeclaration.
if (((isa<FieldDecl>(D) || isa<UnresolvedUsingValueDecl>(D)) &&
Record->hasUserDeclaredConstructor()) ||
isa<IndirectFieldDecl>(D)) {
(isa<IndirectFieldDecl>(D) && !D->isInvalidDecl())) {
Diag((*I)->getLocation(), diag::err_member_name_of_class)
<< D->getDeclName();
break;
Expand Down
25 changes: 25 additions & 0 deletions clang/test/CXX/class/class.union/class.union.anon/p4.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,28 @@ union U {
int y = 1; // expected-error {{initializing multiple members of union}}
};
};

namespace GH149985 {
union X {
enum {
csize = 42,
cs = sizeof(int) // expected-note {{previous declaration is here}}
};
struct {
int data; // expected-note {{previous declaration is here}}
union X *cs[csize] = {}; // expected-error {{member of anonymous struct redeclares}} expected-note {{previous initialization is here}}
};
struct {
int data; // expected-error {{member of anonymous struct redeclares}}
union X *ds[2] = {}; // expected-error {{initializing multiple members of union}}
};
};

union U {
int x; // expected-note {{previous declaration is here}}
union {
int x = {}; // expected-error {{member of anonymous union redeclares}} expected-note {{previous initialization is here}}
};
int y = {}; // expected-error {{initializing multiple members of union}}
};
}