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
