[clang] [clang] Inject IndirectFieldDecl even if name conflicts. (PR #153140)
via cfe-commits
cfe-commits at lists.llvm.org
Thu Aug 14 06:53:40 PDT 2025
https://github.com/keinflue updated https://github.com/llvm/llvm-project/pull/153140
>From cfca176239f48de13897b6d1a59eab74b55055af Mon Sep 17 00:00:00 2001
From: keinflue <keinflue at posteo.de>
Date: Mon, 11 Aug 2025 15:51:14 +0200
Subject: [PATCH 1/4] [clang] Inject IndirectFieldDecl even if name conflicts.
This modifes 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_multople_mem_union_initialization
which via findDefaultInitializer relies on these declarations being present.
Fixes #149985
---
clang/docs/ReleaseNotes.rst | 2 +
clang/lib/Sema/SemaDecl.cpp | 60 ++++++++++---------
clang/test/CXX/class/class.mem/p13.cpp | 2 +-
.../class/class.union/class.union.anon/p4.cpp | 10 ++++
4 files changed, 45 insertions(+), 29 deletions(-)
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 0e9fcaa5fac6a..00b21471f3731 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -184,6 +184,8 @@ Bug Fixes to C++ Support
(``[[assume(expr)]]``) creates temporary objects.
- Fix the dynamic_cast to final class optimization to correctly handle
casts that are guaranteed to fail (#GH137518).
+- Fix a crash if errors "member of anonymous [...] redeclares" and
+ "intializing multiple members of union" coincide (#GH149985).
Bug Fixes to AST Handling
^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index b5eb825eb52cc..052e026fa3ee3 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -5463,40 +5463,44 @@ InjectAnonymousStructOrUnionMembers(Sema &SemaRef, Scope *S, DeclContext *Owner,
// distinct from the names of any other entity in the
// scope in which the anonymous union is declared.
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);
+ }
+ // Inject the IndirectFieldDecl even if invalid, because later
+ // diagnostics may depend on it being present.
+
+ // 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];
+ 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(Invalid);
+ 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);
}
}
diff --git a/clang/test/CXX/class/class.mem/p13.cpp b/clang/test/CXX/class/class.mem/p13.cpp
index d947586c41940..4dc959cd0beb8 100644
--- a/clang/test/CXX/class/class.mem/p13.cpp
+++ b/clang/test/CXX/class/class.mem/p13.cpp
@@ -63,7 +63,7 @@ struct X4 { // expected-note{{previous}}
int X;
union {
float Y;
- unsigned X4; // expected-error{{redeclares 'X4'}}
+ unsigned X4; // expected-error{{redeclares 'X4'}} expected-error {{'X4' has the same name as its class}}
};
};
};
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..710636d2235db 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,13 @@ union U {
int y = 1; // expected-error {{initializing multiple members of union}}
};
};
+
+namespace GH149985 {
+ 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}}
+ };
+}
>From c1a5c2cc50dfa5955f9be53bba5cf7a45d56d154 Mon Sep 17 00:00:00 2001
From: keinflue <keinflue at posteo.de>
Date: Thu, 14 Aug 2025 14:44:52 +0200
Subject: [PATCH 2/4] Implement review suggestions
Disable duplicate diagnostic, include original test case and fix setting only affected fields invalid.
---
clang/lib/Sema/SemaDecl.cpp | 23 +++++++++++--------
clang/lib/Sema/SemaDeclCXX.cpp | 5 +++-
clang/test/CXX/class/class.mem/p13.cpp | 2 +-
.../class/class.union/class.union.anon/p4.cpp | 15 ++++++++++++
4 files changed, 33 insertions(+), 12 deletions(-)
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 008a25fe739e3..343d4c78e401c 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -5494,6 +5494,7 @@ 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.
@@ -5501,17 +5502,19 @@ 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.
+
+ const bool FieldInvalid = CheckAnonMemberRedeclaration(
+ SemaRef, S, Owner, VD->getDeclName(), VD->getLocation(),
+ AnonRecord->isUnion(), SC);
+ if (FieldInvalid)
Invalid = true;
- }
+
// Inject the IndirectFieldDecl even if invalid, because later
- // diagnostics may depend on it being present.
+ // diagnostics may depend on it being present, see findDefaultInitializer.
// C++ [class.union]p2:
// For the purpose of name lookup, after the anonymous union
@@ -5539,7 +5542,7 @@ InjectAnonymousStructOrUnionMembers(Sema &SemaRef, Scope *S, DeclContext *Owner,
IndirectField->setAccess(AS);
IndirectField->setImplicit();
- IndirectField->setInvalidDecl(Invalid);
+ IndirectField->setInvalidDecl(FieldInvalid);
SemaRef.PushOnScopeChains(IndirectField, S);
// That includes picking up the appropriate access specifier.
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.mem/p13.cpp b/clang/test/CXX/class/class.mem/p13.cpp
index 4dc959cd0beb8..d947586c41940 100644
--- a/clang/test/CXX/class/class.mem/p13.cpp
+++ b/clang/test/CXX/class/class.mem/p13.cpp
@@ -63,7 +63,7 @@ struct X4 { // expected-note{{previous}}
int X;
union {
float Y;
- unsigned X4; // expected-error{{redeclares 'X4'}} expected-error {{'X4' has the same name as its class}}
+ unsigned X4; // expected-error{{redeclares 'X4'}}
};
};
};
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 710636d2235db..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
@@ -10,6 +10,21 @@ union U {
};
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 {
>From 16f7cb8100b39b80a245748adc31248f896f2005 Mon Sep 17 00:00:00 2001
From: keinflue <keinflue at posteo.de>
Date: Thu, 14 Aug 2025 14:58:13 +0200
Subject: [PATCH 3/4] Extend release notes
---
clang/docs/ReleaseNotes.rst | 1 +
1 file changed, 1 insertion(+)
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 9bb25fc182fa7..118d7d7838c25 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -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
--------------------------------------------------
>From 3e65d514af25038055a5894ccd8f3cb28774df9b Mon Sep 17 00:00:00 2001
From: keinflue <keinflue at posteo.de>
Date: Thu, 14 Aug 2025 15:48:27 +0200
Subject: [PATCH 4/4] Implement further stlye-related review suggestions.
---
clang/docs/ReleaseNotes.rst | 4 +++-
clang/lib/Sema/SemaDecl.cpp | 3 +--
2 files changed, 4 insertions(+), 3 deletions(-)
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 118d7d7838c25..1f89328ee8c13 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -74,7 +74,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.
+- 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
--------------------------------------------------
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 343d4c78e401c..fb73bd7db286b 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -5494,7 +5494,6 @@ 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.
@@ -5507,7 +5506,7 @@ InjectAnonymousStructOrUnionMembers(Sema &SemaRef, Scope *S, DeclContext *Owner,
// distinct from the names of any other entity in the
// scope in which the anonymous union is declared.
- const bool FieldInvalid = CheckAnonMemberRedeclaration(
+ bool FieldInvalid = CheckAnonMemberRedeclaration(
SemaRef, S, Owner, VD->getDeclName(), VD->getLocation(),
AnonRecord->isUnion(), SC);
if (FieldInvalid)
More information about the cfe-commits
mailing list