Author: vangthao95 Date: 2026-09-05T09:11:54+02:00 New Revision: 1943eaf4faa33a5eace0900f5aa1e6b92ef80624
URL: https://github.com/llvm/llvm-project/commit/1943eaf4faa33a5eace0900f5aa1e6b92ef80624 DIFF: https://github.com/llvm/llvm-project/commit/1943eaf4faa33a5eace0900f5aa1e6b92ef80624.diff LOG: [clang][Sema] Check exported default constructors in nested classes (#214275) Teach Clang to check exported default constructors declared in nested classes. This prepares Microsoft ABI constructor closures for non-dependent nested classes before CodeGen. This fixes a failure seen when compiling PyTorch on Windows. An exported nested class with defaults for every constructor parameter caused Clang to emit its constructor closure without cached default arguments and assert in MicrosoftCXXABI.cpp. Assisted by: GPT-5.6 Sol (cherry picked from commit 1b782da49ae976dbb59607b95b49a1465e2cfef9) Added: Modified: clang/lib/Sema/SemaDeclCXX.cpp clang/test/CodeGenCXX/dllexport-ctor-closure-nested.cpp clang/test/PCH/dllexport-default-arg-closure.cpp clang/test/SemaCXX/default-arg-closures.cpp clang/test/SemaCXX/dllexport.cpp Removed: ################################################################################ diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index 685412d681680..b0669c602d249 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -6383,8 +6383,19 @@ 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 here. + if (auto *NestedClass = dyn_cast<CXXRecordDecl>(Member)) { + 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..70e7f1398ad05 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 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'}} }; _______________________________________________ llvm-branch-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
