https://github.com/vangthao95 updated 
https://github.com/llvm/llvm-project/pull/214275

>From e731d1a4d0ba96ef3e638626faca06e43ff0bee2 Mon Sep 17 00:00:00 2001
From: Vang Thao <[email protected]>
Date: Fri, 24 Jul 2026 18:56:04 -0400
Subject: [PATCH 1/4] [clang][Sema] Check exported default constructors in
 nested classes

Teach Clang to check exported default constructors declared in nested classes
and nested class templates. This ensures their Microsoft ABI constructor
closures are prepared before CodeGen.

Assisted by: GPT-5.6 Sol
---
 clang/lib/Sema/SemaDeclCXX.cpp                     | 14 ++++++++++++++
 .../CodeGenCXX/dllexport-ctor-closure-nested.cpp   | 11 +++++++++++
 clang/test/PCH/dllexport-default-arg-closure.cpp   |  9 +++++++++
 clang/test/SemaCXX/default-arg-closures.cpp        |  9 +++++++++
 clang/test/SemaCXX/dllexport.cpp                   | 14 ++++++++++++++
 5 files changed, 57 insertions(+)

diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index 47b01b913b428..cb1b6daa022da 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -6387,8 +6387,22 @@ static void 
checkForMultipleExportedDefaultConstructors(Sema &S,
   if (!S.Context.getTargetInfo().getCXXABI().isMicrosoft())
     return;
 
+  if (Class->isInvalidDecl())
+    return;
+
   CXXConstructorDecl *LastExportedDefaultCtor = nullptr;
   for (Decl *Member : Class->decls()) {
+    // Nested classes finish delayed default argument parsing with the 
outermost
+    // class, so check each nested definition and class template pattern here.
+    CXXRecordDecl *NestedClass = dyn_cast<CXXRecordDecl>(Member);
+    if (auto *NestedTemplate = dyn_cast<ClassTemplateDecl>(Member))
+      NestedClass = NestedTemplate->getTemplatedDecl();
+    if (NestedClass) {
+      if (NestedClass->isThisDeclarationADefinition())
+        checkForMultipleExportedDefaultConstructors(S, NestedClass);
+      continue;
+    }
+
     // Look for exported default constructors.
     auto *CD = dyn_cast<CXXConstructorDecl>(Member);
     if (!CD || !CD->isDefaultConstructor())
diff --git a/clang/test/CodeGenCXX/dllexport-ctor-closure-nested.cpp 
b/clang/test/CodeGenCXX/dllexport-ctor-closure-nested.cpp
index 36dab8600e21b..e949bc82686c7 100644
--- a/clang/test/CodeGenCXX/dllexport-ctor-closure-nested.cpp
+++ b/clang/test/CodeGenCXX/dllexport-ctor-closure-nested.cpp
@@ -18,3 +18,14 @@ struct __declspec(dllexport) CtorClosureOuter {
 
 // CHECK-LABEL: $"??1HasImplicitDtor1@@QAE@XZ" = comdat any
 // CHECK-LABEL: define weak_odr dso_local dllexport x86_thiscallcc void 
@"??_FCtorClosureInner@CtorClosureOuter@@QAEXXZ"({{.*}}) {{#[0-9]+}} comdat
+
+// Member-level dllexport on a nested default constructor needs constructor
+// closure default arguments before the enclosing class is emitted.
+struct MemberExportedCtorClosureOuter {
+  struct MemberExportedCtorClosureInner {
+    __declspec(dllexport) MemberExportedCtorClosureInner(
+        const HasImplicitDtor1 &v = {}) {}
+  };
+};
+
+// CHECK-LABEL: define weak_odr dso_local dllexport x86_thiscallcc void 
@"??_FMemberExportedCtorClosureInner@MemberExportedCtorClosureOuter@@QAEXXZ"({{.*}})
 {{#[0-9]+}} comdat
diff --git a/clang/test/PCH/dllexport-default-arg-closure.cpp 
b/clang/test/PCH/dllexport-default-arg-closure.cpp
index b5ac7815b8111..b56d166e8d9f3 100644
--- a/clang/test/PCH/dllexport-default-arg-closure.cpp
+++ b/clang/test/PCH/dllexport-default-arg-closure.cpp
@@ -20,6 +20,15 @@ struct __declspec(dllexport) Foo {
 // CHECK: define weak_odr dso_local dllexport void 
@"??_FFoo@@QEAAXXZ"(ptr{{.*}})
 // CHECK:   call noundef ptr @"??0Foo@@QEAA@W4E@0@@Z"(ptr {{.*}}, i32 noundef 
0)
 
+struct PCHMemberExportOuter {
+  struct Inner {
+    enum E { E0 } e;
+    __declspec(dllexport) Inner(E e = E0) : e(e) {}
+  };
+};
+
+// CHECK: define weak_odr dso_local dllexport void 
@"??_FInner@PCHMemberExportOuter@@QEAAXXZ"(ptr{{.*}})
+
 #else
 
 
diff --git a/clang/test/SemaCXX/default-arg-closures.cpp 
b/clang/test/SemaCXX/default-arg-closures.cpp
index 676bd486105f9..86c44f0ead9f7 100644
--- a/clang/test/SemaCXX/default-arg-closures.cpp
+++ b/clang/test/SemaCXX/default-arg-closures.cpp
@@ -25,6 +25,15 @@ struct DependentDefaultCopyArg {
 struct HasMember {
   enum { member = 0 };
 };
+
+template <typename T>
+struct NestedDependentDefaultCtorArg {
+  struct Inner {
+    __declspec(dllexport) Inner(int n = T::member) {}
+  };
+};
+NestedDependentDefaultCtorArg<HasMember>::Inner ValidNestedDependentArg;
+
 void UseDependentArg() { throw DependentDefaultCopyArg<HasMember>(); }
 
 void ErrorInDependentArg() {
diff --git a/clang/test/SemaCXX/dllexport.cpp b/clang/test/SemaCXX/dllexport.cpp
index d625b15557762..262612db5835d 100644
--- a/clang/test/SemaCXX/dllexport.cpp
+++ b/clang/test/SemaCXX/dllexport.cpp
@@ -746,6 +746,20 @@ struct ClassTemplateWithMultipleDefaultCtors {
   __declspec(dllexport) ClassTemplateWithMultipleDefaultCtors(int = 30, ...) 
{} // ms-note{{declared here}}
 };
 
+struct ClassWithNestedMultipleDefaultCtors {
+  struct Nested {
+    __declspec(dllexport) Nested(int = 40) {}      // 
ms-error{{'__declspec(dllexport)' cannot be applied to more than one default 
constructor}}
+    __declspec(dllexport) Nested(int = 30, ...) {} // ms-note{{declared here}}
+  };
+};
+
+struct ClassWithNestedClassTemplateMultipleDefaultCtors {
+  template <typename T> struct Nested {
+    __declspec(dllexport) Nested(int = 40) {}      // 
ms-error{{'__declspec(dllexport)' cannot be applied to more than one default 
constructor}}
+    __declspec(dllexport) Nested(int = 30, ...) {} // ms-note{{declared here}}
+  };
+};
+
 template <typename T> struct HasDefaults {
   HasDefaults(int x = sizeof(T)) {} // ms-error {{invalid application of 
'sizeof'}}
 };

>From 38a33809e626718afeaf6baea23be9f289661f19 Mon Sep 17 00:00:00 2001
From: Vang Thao <[email protected]>
Date: Thu, 6 Aug 2026 19:57:12 -0400
Subject: [PATCH 2/4] Add nested template test

---
 clang/test/SemaCXX/dllexport.cpp | 13 +++++++++++++
 1 file changed, 13 insertions(+)

diff --git a/clang/test/SemaCXX/dllexport.cpp b/clang/test/SemaCXX/dllexport.cpp
index 262612db5835d..ed38c7de9cf5f 100644
--- a/clang/test/SemaCXX/dllexport.cpp
+++ b/clang/test/SemaCXX/dllexport.cpp
@@ -759,6 +759,19 @@ struct ClassWithNestedClassTemplateMultipleDefaultCtors {
     __declspec(dllexport) Nested(int = 30, ...) {} // ms-note{{declared here}}
   };
 };
+template struct ClassWithNestedClassTemplateMultipleDefaultCtors::Nested<int>;
+template struct 
ClassWithNestedClassTemplateMultipleDefaultCtors::Nested<double>;
+
+template <typename T>
+struct ClassTemplateWithNestedClassTemplateMultipleDefaultCtors {
+  __declspec(dllexport)
+  ClassTemplateWithNestedClassTemplateMultipleDefaultCtors(int = 40) {}
+  template <typename R> struct Nested {
+    __declspec(dllexport) Nested(int = 30) {}      // 
ms-error{{'__declspec(dllexport)' cannot be applied to more than one default 
constructor}}
+    __declspec(dllexport) Nested(int = 20, ...) {} // ms-note{{declared here}}
+  };
+};
+template struct ClassTemplateWithNestedClassTemplateMultipleDefaultCtors<int>;
 
 template <typename T> struct HasDefaults {
   HasDefaults(int x = sizeof(T)) {} // ms-error {{invalid application of 
'sizeof'}}

>From dbbbfae740791ceee4b9297b74b3b5355da8e9c5 Mon Sep 17 00:00:00 2001
From: Vang Thao <[email protected]>
Date: Fri, 7 Aug 2026 19:39:22 -0400
Subject: [PATCH 3/4] Remove template handling

---
 clang/lib/Sema/SemaDeclCXX.cpp   |  7 ++-----
 clang/test/SemaCXX/dllexport.cpp | 20 --------------------
 2 files changed, 2 insertions(+), 25 deletions(-)

diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index cb1b6daa022da..6eb03bf572a0a 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -6393,11 +6393,8 @@ static void 
checkForMultipleExportedDefaultConstructors(Sema &S,
   CXXConstructorDecl *LastExportedDefaultCtor = nullptr;
   for (Decl *Member : Class->decls()) {
     // Nested classes finish delayed default argument parsing with the 
outermost
-    // class, so check each nested definition and class template pattern here.
-    CXXRecordDecl *NestedClass = dyn_cast<CXXRecordDecl>(Member);
-    if (auto *NestedTemplate = dyn_cast<ClassTemplateDecl>(Member))
-      NestedClass = NestedTemplate->getTemplatedDecl();
-    if (NestedClass) {
+    // class, so check each nested definition here.
+    if (auto *NestedClass = dyn_cast<CXXRecordDecl>(Member)) {
       if (NestedClass->isThisDeclarationADefinition())
         checkForMultipleExportedDefaultConstructors(S, NestedClass);
       continue;
diff --git a/clang/test/SemaCXX/dllexport.cpp b/clang/test/SemaCXX/dllexport.cpp
index ed38c7de9cf5f..6a1441be50bc5 100644
--- a/clang/test/SemaCXX/dllexport.cpp
+++ b/clang/test/SemaCXX/dllexport.cpp
@@ -753,26 +753,6 @@ struct ClassWithNestedMultipleDefaultCtors {
   };
 };
 
-struct ClassWithNestedClassTemplateMultipleDefaultCtors {
-  template <typename T> struct Nested {
-    __declspec(dllexport) Nested(int = 40) {}      // 
ms-error{{'__declspec(dllexport)' cannot be applied to more than one default 
constructor}}
-    __declspec(dllexport) Nested(int = 30, ...) {} // ms-note{{declared here}}
-  };
-};
-template struct ClassWithNestedClassTemplateMultipleDefaultCtors::Nested<int>;
-template struct 
ClassWithNestedClassTemplateMultipleDefaultCtors::Nested<double>;
-
-template <typename T>
-struct ClassTemplateWithNestedClassTemplateMultipleDefaultCtors {
-  __declspec(dllexport)
-  ClassTemplateWithNestedClassTemplateMultipleDefaultCtors(int = 40) {}
-  template <typename R> struct Nested {
-    __declspec(dllexport) Nested(int = 30) {}      // 
ms-error{{'__declspec(dllexport)' cannot be applied to more than one default 
constructor}}
-    __declspec(dllexport) Nested(int = 20, ...) {} // ms-note{{declared here}}
-  };
-};
-template struct ClassTemplateWithNestedClassTemplateMultipleDefaultCtors<int>;
-
 template <typename T> struct HasDefaults {
   HasDefaults(int x = sizeof(T)) {} // ms-error {{invalid application of 
'sizeof'}}
 };

>From 0765f0e6ef8284242a8cb649aaf78ce92419f015 Mon Sep 17 00:00:00 2001
From: Vang Thao <[email protected]>
Date: Wed, 12 Aug 2026 00:27:40 -0400
Subject: [PATCH 4/4] Add test with no delayed constructor

---
 clang/test/SemaCXX/dllexport.cpp | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/clang/test/SemaCXX/dllexport.cpp b/clang/test/SemaCXX/dllexport.cpp
index 6a1441be50bc5..70e7f1398ad05 100644
--- a/clang/test/SemaCXX/dllexport.cpp
+++ b/clang/test/SemaCXX/dllexport.cpp
@@ -753,6 +753,13 @@ struct ClassWithNestedMultipleDefaultCtors {
   };
 };
 
+struct ClassWithNestedObviousMultipleDefaultCtors {
+  struct Nested {
+    __declspec(dllexport) Nested() {}    // ms-error{{'__declspec(dllexport)' 
cannot be applied to more than one default constructor}}
+    __declspec(dllexport) Nested(...) {} // ms-note{{declared here}}
+  };
+};
+
 template <typename T> struct HasDefaults {
   HasDefaults(int x = sizeof(T)) {} // ms-error {{invalid application of 
'sizeof'}}
 };

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to