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

Reply via email to