Author: keinflue Date: 2025-08-15T09:43:29-07:00 New Revision: af96ed6bf6e6a1ba0cbb36cb3925dd44f41c301e
URL: https://github.com/llvm/llvm-project/commit/af96ed6bf6e6a1ba0cbb36cb3925dd44f41c301e DIFF: https://github.com/llvm/llvm-project/commit/af96ed6bf6e6a1ba0cbb36cb3925dd44f41c301e.diff LOG: [clang] Inject IndirectFieldDecl even if name conflicts. (#153140) This modifies InjectAnonymousStructOrUnionMembers to inject an IndirectFieldDecl and mark it invalid even if its name conflicts with another name in the scope. This resolves a crash on a further diagnostic diag::err_multiple_mem_union_initialization which via findDefaultInitializer relies on these declarations being present. Fixes #149985 Added: Modified: clang/docs/ReleaseNotes.rst clang/lib/Sema/SemaDecl.cpp clang/lib/Sema/SemaDeclCXX.cpp clang/test/CXX/class/class.union/class.union.anon/p4.cpp Removed: ################################################################################ diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 42571a833ded5..604b4c3f714b7 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -77,6 +77,9 @@ 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 -------------------------------------------------- @@ -217,6 +220,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). - Fix a crash when using ``explicit(bool)`` in pre-C++11 language modes. (#GH152729) - Fix the parsing of variadic member functions when the ellipis immediately follows a default argument.(#GH153445) diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 6581d4c604eb2..5001e080f946b 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -5503,48 +5503,54 @@ InjectAnonymousStructOrUnionMembers(Sema &SemaRef, Scope *S, DeclContext *Owner, 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. + + 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); - IndirectFieldDecl *IndirectField = IndirectFieldDecl::Create( - SemaRef.Context, Owner, VD->getLocation(), VD->getIdentifier(), - VD->getType(), {NamedChain, Chaining.size()}); + assert(Chaining.size() >= 2); + NamedDecl **NamedChain = + new (SemaRef.Context) NamedDecl *[Chaining.size()]; + for (unsigned i = 0; i < Chaining.size(); i++) + NamedChain[i] = Chaining[i]; - for (const auto *Attr : VD->attrs()) - IndirectField->addAttr(Attr->clone(SemaRef.Context)); + IndirectFieldDecl *IndirectField = IndirectFieldDecl::Create( + SemaRef.Context, Owner, VD->getLocation(), VD->getIdentifier(), + VD->getType(), {NamedChain, Chaining.size()}); - IndirectField->setAccess(AS); - IndirectField->setImplicit(); - SemaRef.PushOnScopeChains(IndirectField, S); + for (const auto *Attr : VD->attrs()) + IndirectField->addAttr(Attr->clone(SemaRef.Context)); - // That includes picking up the appropriate access specifier. - if (AS != AS_none) IndirectField->setAccess(AS); + IndirectField->setAccess(AS); + IndirectField->setImplicit(); + IndirectField->setInvalidDecl(FieldInvalid); + SemaRef.PushOnScopeChains(IndirectField, S); - Chaining.resize(OldChainingSize); - } + // That includes picking up the appropriate access specifier. + if (AS != AS_none) + IndirectField->setAccess(AS); + + Chaining.resize(OldChainingSize); } } diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index 0477d37cac4c5..dd66a5f15a970 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -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; diff --git a/clang/test/CXX/class/class.union/class.union.anon/p4.cpp b/clang/test/CXX/class/class.union/class.union.anon/p4.cpp index a12ec38503fa8..f124ac1052b7e 100644 --- a/clang/test/CXX/class/class.union/class.union.anon/p4.cpp +++ b/clang/test/CXX/class/class.union/class.union.anon/p4.cpp @@ -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}} + }; +} _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits