Author: Akash Manna Date: 2026-08-28T14:05:11+02:00 New Revision: f29af2982b3f526bfaa21abff9835bc7c2f3cc3a
URL: https://github.com/llvm/llvm-project/commit/f29af2982b3f526bfaa21abff9835bc7c2f3cc3a DIFF: https://github.com/llvm/llvm-project/commit/f29af2982b3f526bfaa21abff9835bc7c2f3cc3a.diff LOG: [Clang] Fix "Unions cannot be dynamic classes" assertion (#217942) Fixes #213854 An ill-formed qualified member definition like `void B::foo() {}` inside a union gets diagnosed, but recovery keeps the declaration around: it belongs to `B` semantically while sitting lexically inside the union. Since `B::foo` overrides a virtual function, it counted as a virtual member of the union when it was added to it, so the union was marked polymorphic and record layout later asserted with `"Unions cannot be dynamic classes."`. A non-union enclosing class hits the same bug silently and just gets a bogus vtable pointer. The fix is in `HandleDeclarator`: once the qualifier is diagnosed as naming a different class, the declaration is marked invalid, the same way we already handle `virtual` in a union. Invalid declarations don't contribute to the properties of the class they're added to, so the class can't become polymorphic, while the body is still parsed and the node stays in the AST. As a side effect, the qualified specializations in cwg727 no longer get a bogus follow-up "no function template matches" error, so that expectation is updated. Also added a regression test (reduced union/struct cases plus the verbatim reproducer) and a release note. LLM tools were used for this contribution. I've reviewed, built, and tested the change myself before pushing to GitHub. Added: clang/test/SemaCXX/GH213854.cpp Modified: clang/docs/ReleaseNotes.md clang/lib/Sema/SemaDecl.cpp clang/test/CXX/drs/cwg7xx.cpp Removed: ################################################################################ diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md index b28898e95d32e..3487c6eff744d 100644 --- a/clang/docs/ReleaseNotes.md +++ b/clang/docs/ReleaseNotes.md @@ -536,6 +536,10 @@ 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 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 - Fixed a non-deterministic ordering of unused local typedefs that made diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 0a8b8bb7ec61a..07c6157ab8f31 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -6584,6 +6584,8 @@ NamedDecl *Sema::HandleDeclarator(Scope *S, Declarator &D, if (DC->isRecord()) return nullptr; + D.setInvalidType(); + } else if (CurContext->isRecord() && !CurContext->Equals(DC)) { 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}} diff --git a/clang/test/SemaCXX/GH213854.cpp b/clang/test/SemaCXX/GH213854.cpp new file mode 100644 index 0000000000000..ac15ca5081e32 --- /dev/null +++ b/clang/test/SemaCXX/GH213854.cpp @@ -0,0 +1,48 @@ +// 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), ""); + +class A { + virtual void A::foo() {} // expected-error {{extra qualification on member 'foo'}} +}; + +static_assert(__is_polymorphic(A), ""); +} // 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}} _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
