https://github.com/akash-manna-sky updated https://github.com/llvm/llvm-project/pull/217942
>From d282ef1337f3f30e5b13628c913274a30a6c23bb Mon Sep 17 00:00:00 2001 From: Akash Manna <[email protected]> Date: Fri, 21 Aug 2026 20:25:14 +0530 Subject: [PATCH 1/4] [clang] Fix "Unions cannot be dynamic classes" assertion on ill-formed qualified member definitions (#213854) A qualified member function definition inside another class body, like void B::foo() {} in a union, is diagnosed but kept for recovery with its semantic parent B while lexically nested in the union. When the method is implicitly virtual, CXXRecordDecl::addedMember marked the lexical class polymorphic, so the union reached record layout as a dynamic class and tripped the assertion. Skip members of a different class in addedMember; only friends (already skipped) and these recovery leftovers can be in that position. Fixes #213854 --- clang/docs/ReleaseNotes.md | 6 +++++ clang/lib/AST/DeclCXX.cpp | 5 ++++ clang/test/SemaCXX/GH213854.cpp | 42 +++++++++++++++++++++++++++++++++ 3 files changed, 53 insertions(+) create mode 100644 clang/test/SemaCXX/GH213854.cpp diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md index 8c9467ca7b742..b4410f52d87e4 100644 --- a/clang/docs/ReleaseNotes.md +++ b/clang/docs/ReleaseNotes.md @@ -528,6 +528,12 @@ features cannot lower the translation-unit ABI level; parameter that follows a parameter pack (e.g. `template <typename... T> S::S(T..., int = 10) {}`). (#GH216211) +- Fixed a crash when an ill-formed qualified member function definition written + inside a class (e.g. a definition of a virtual member of a nested class) + incorrectly caused the enclosing class to be treated as a polymorphic class, + which asserted during record layout when the enclosing class was a union. + (#GH213854) + #### Bug Fixes to AST Handling - Fixed a non-deterministic ordering of unused local typedefs that made diff --git a/clang/lib/AST/DeclCXX.cpp b/clang/lib/AST/DeclCXX.cpp index f0da56542ae7e..ed0c9512f8b6f 100644 --- a/clang/lib/AST/DeclCXX.cpp +++ b/clang/lib/AST/DeclCXX.cpp @@ -758,6 +758,11 @@ void CXXRecordDecl::addedMember(Decl *D) { if (D->getFriendObjectKind() || D->isInvalidDecl()) return; + // Ignore members of a different class, which can appear here during error + // recovery for an ill-formed qualified member declaration. + if (!D->getDeclContext()->Equals(this)) + return; + auto *FunTmpl = dyn_cast<FunctionTemplateDecl>(D); if (FunTmpl) D = FunTmpl->getTemplatedDecl(); diff --git a/clang/test/SemaCXX/GH213854.cpp b/clang/test/SemaCXX/GH213854.cpp new file mode 100644 index 0000000000000..96c4e6049aa1d --- /dev/null +++ b/clang/test/SemaCXX/GH213854.cpp @@ -0,0 +1,42 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s + +namespace reduced { +union Union { + class A { + virtual void foo(); + }; + class B : public A { + }; + void B::foo() {} // expected-error {{non-friend class member 'foo' cannot have a qualified name}} +}; + +static_assert(!__is_polymorphic(Union), ""); + +void uni(void (*fn)(Union), Union arg1) { + fn(arg1); +} + +struct Struct { + class A { + virtual void foo(); + }; + class B : public A { + }; + void B::foo() {} // expected-error {{non-friend class member 'foo' cannot have a qualified name}} +}; + +static_assert(!__is_polymorphic(Struct), ""); +} // namespace reduced + +// Verbatim reproducer from GH213854; the missing closing brace is intentional. +union Union { // expected-note {{to match this '{'}} + class A { + virtual void foo(); + }; + class B : public A { + }; + void B::foo() {} // expected-error {{non-friend class member 'foo' cannot have a qualified name}} +void uni(void (*fn)(union Union), union Union arg1) { + fn(arg1); +} +// expected-error {{expected '}'}} expected-error@-1 {{expected ';' after union}} \ No newline at end of file >From 001d3130e998c000e12521a47455735989f2c901 Mon Sep 17 00:00:00 2001 From: Akash Manna <[email protected]> Date: Sat, 22 Aug 2026 15:03:29 +0530 Subject: [PATCH 2/4] shorten this sentences in RealeaseNotes.md and add new lines at the end of the test file --- clang/docs/ReleaseNotes.md | 6 ++---- clang/test/SemaCXX/GH213854.cpp | 2 +- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md index b4410f52d87e4..4a158838563a9 100644 --- a/clang/docs/ReleaseNotes.md +++ b/clang/docs/ReleaseNotes.md @@ -528,10 +528,8 @@ features cannot lower the translation-unit ABI level; parameter that follows a parameter pack (e.g. `template <typename... T> S::S(T..., int = 10) {}`). (#GH216211) -- Fixed a crash when an ill-formed qualified member function definition written - inside a class (e.g. a definition of a virtual member of a nested class) - incorrectly caused the enclosing class to be treated as a polymorphic class, - which asserted during record layout when the enclosing class was a union. +- Fixed an assertion when an ill-formed qualified member function definition + inside a union caused the union to be treated as a polymorphic class. (#GH213854) #### Bug Fixes to AST Handling diff --git a/clang/test/SemaCXX/GH213854.cpp b/clang/test/SemaCXX/GH213854.cpp index 96c4e6049aa1d..82dc81914298b 100644 --- a/clang/test/SemaCXX/GH213854.cpp +++ b/clang/test/SemaCXX/GH213854.cpp @@ -39,4 +39,4 @@ union Union { // expected-note {{to match this '{'}} void uni(void (*fn)(union Union), union Union arg1) { fn(arg1); } -// expected-error {{expected '}'}} expected-error@-1 {{expected ';' after union}} \ No newline at end of file +// expected-error {{expected '}'}} expected-error@-1 {{expected ';' after union}} >From f735eb9d44291df3b518e117320e5b678102e80d Mon Sep 17 00:00:00 2001 From: Akash Manna <[email protected]> Date: Wed, 26 Aug 2026 12:48:02 +0530 Subject: [PATCH 3/4] [clang] Fix "Unions cannot be dynamic classes" assertion (#213854) A qualified member function definition inside another class, such as void B::foo() {} in a union, is diagnosed but kept for recovery with semantic parent B while lexically inside the union. When the method is implicitly virtual, CXXRecordDecl::addedMember marked the union polymorphic and record layout asserted. Mark such declarations invalid in HandleDeclarator after the qualifier is diagnosed, so addedMember ignores them. This also drops the bogus follow-up "no function template matches" error for the qualified specializations in cwg727. Fixes #213854 --- clang/lib/AST/DeclCXX.cpp | 5 ----- clang/lib/Sema/SemaDecl.cpp | 3 +++ clang/test/CXX/drs/cwg7xx.cpp | 1 - 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/clang/lib/AST/DeclCXX.cpp b/clang/lib/AST/DeclCXX.cpp index ed0c9512f8b6f..f0da56542ae7e 100644 --- a/clang/lib/AST/DeclCXX.cpp +++ b/clang/lib/AST/DeclCXX.cpp @@ -758,11 +758,6 @@ void CXXRecordDecl::addedMember(Decl *D) { if (D->getFriendObjectKind() || D->isInvalidDecl()) return; - // Ignore members of a different class, which can appear here during error - // recovery for an ill-formed qualified member declaration. - if (!D->getDeclContext()->Equals(this)) - return; - auto *FunTmpl = dyn_cast<FunctionTemplateDecl>(D); if (FunTmpl) D = FunTmpl->getTemplatedDecl(); diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index cc1eb5c2eff96..8107650ea7a91 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -6585,6 +6585,9 @@ NamedDecl *Sema::HandleDeclarator(Scope *S, Declarator &D, return nullptr; D.setInvalidType(); + } else if (CurContext->isRecord() && !CurContext->Equals(DC)) { + // Keep the diagnosed declaration for recovery, but not as a valid one. + D.setInvalidType(); } } diff --git a/clang/test/CXX/drs/cwg7xx.cpp b/clang/test/CXX/drs/cwg7xx.cpp index ca712333f9410..8dc9e1f292b93 100644 --- a/clang/test/CXX/drs/cwg7xx.cpp +++ b/clang/test/CXX/drs/cwg7xx.cpp @@ -138,7 +138,6 @@ namespace cwg727 { // cwg727: partial // expected-note@#cwg727-C {{explicitly specialized declaration is here}} template<> void A::f<double>(); // expected-error@-1 {{non-friend class member 'f' cannot have a qualified name}} - // expected-error@-2 {{no function template matches function template specialization 'f'}} template<> int A::N<double>; // expected-error@-1 {{non-friend class member 'N' cannot have a qualified name}} // expected-error@-2 {{variable template specialization of 'N' not in class 'A' or an enclosing namespace}} >From 9f65a2caacedbb2f01f6906cf790b1d14c9441fc Mon Sep 17 00:00:00 2001 From: Akash Manna <[email protected]> Date: Wed, 26 Aug 2026 14:12:15 +0530 Subject: [PATCH 4/4] [clang] Fix "Unions cannot be dynamic classes" assertion (#213854) A qualified member function definition inside another class, such as void B::foo() {} in a union, is diagnosed but kept for recovery with semantic parent B while lexically inside the union. When the method is implicitly virtual, CXXRecordDecl::addedMember marked the union polymorphic and record layout asserted. Mark such declarations invalid in HandleDeclarator once the qualifier is diagnosed, so addedMember ignores them. This also drops the bogus follow-up "no function template matches" error for the qualified specializations in cwg727. Fixes #213854 --- clang/lib/Sema/SemaDecl.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 8107650ea7a91..e8d6d64686abb 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -6586,7 +6586,6 @@ NamedDecl *Sema::HandleDeclarator(Scope *S, Declarator &D, D.setInvalidType(); } else if (CurContext->isRecord() && !CurContext->Equals(DC)) { - // Keep the diagnosed declaration for recovery, but not as a valid one. D.setInvalidType(); } } _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
