https://github.com/chinmaydd updated https://github.com/llvm/llvm-project/pull/182706
>From b43ed3e475771f8524b0524d6702a8741918e187 Mon Sep 17 00:00:00 2001 From: Chinmay Deshpande <[email protected]> Date: Fri, 20 Feb 2026 16:20:05 -0800 Subject: [PATCH 1/2] [Clang] Ensure child classes export inherited constructors from dllexport-ed base classes --- clang/lib/AST/ASTContext.cpp | 5 +- clang/lib/Sema/SemaDeclCXX.cpp | 30 ++++++++++ .../CodeGenCXX/dllexport-inherited-ctor.cpp | 58 +++++++++++++++++++ 3 files changed, 92 insertions(+), 1 deletion(-) create mode 100644 clang/test/CodeGenCXX/dllexport-inherited-ctor.cpp diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp index d97a97091e4eb..f9234c03db932 100644 --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -13010,10 +13010,13 @@ static GVALinkage basicGVALinkageForFunction(const ASTContext &Context, if (Context.getTargetInfo().getCXXABI().isMicrosoft() && isa<CXXConstructorDecl>(FD) && - cast<CXXConstructorDecl>(FD)->isInheritingConstructor()) + cast<CXXConstructorDecl>(FD)->isInheritingConstructor() && + !FD->hasAttr<DLLExportAttr>()) // Our approach to inheriting constructors is fundamentally different from // that used by the MS ABI, so keep our inheriting constructor thunks // internal rather than trying to pick an unambiguous mangling for them. + // However, dllexport inherited constructors must be externally visible + // to match MSVC's behavior. return GVA_Internal; return GVA_DiscardableODR; diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index 5837ecd6b9163..34f9e159b4355 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -6312,6 +6312,12 @@ static void ReferenceDllExportedMembers(Sema &S, CXXRecordDecl *Class) { S.MarkFunctionReferenced(Class->getLocation(), MD); + // Inherited constructors are synthesized, not written in source, so + // their definitions won't be encountered later. + if (auto *CD = dyn_cast<CXXConstructorDecl>(MD)) + if (CD->getInheritedConstructor()) + S.Consumer.HandleTopLevelDecl(DeclGroupRef(MD)); + // The function will be passed to the consumer when its definition is // encountered. } else if (MD->isExplicitlyDefaulted()) { @@ -6588,6 +6594,19 @@ void Sema::checkClassLevelDLLAttribute(CXXRecordDecl *Class) { // Force declaration of implicit members so they can inherit the attribute. ForceDeclarationOfImplicitMembers(Class); + // Inherited constructors are created lazily; force their creation now so the + // loop below can propagate the DLL attribute to them. + if (ClassExported) { + SmallVector<ConstructorUsingShadowDecl *, 4> Shadows; + for (auto *D : Class->decls()) + if (auto *S = dyn_cast<ConstructorUsingShadowDecl>(D)) + Shadows.push_back(S); + for (auto *S : Shadows) + if (auto *BC = dyn_cast<CXXConstructorDecl>(S->getTargetDecl()); + BC && !BC->isDeleted()) + findInheritingConstructor(Class->getLocation(), BC, S); + } + // FIXME: MSVC's docs say all bases must be exportable, but this doesn't // seem to be true in practice? @@ -14367,6 +14386,17 @@ Sema::findInheritingConstructor(SourceLocation Loc, DerivedCtor->setParams(ParamDecls); Derived->addDecl(DerivedCtor); + // Propagate the class-level DLLExport attribute to the new inherited + // constructor so it gets exported. DLLImport is not propagated because the + // inherited ctor is an inline definition synthesized by the compiler. + if (Derived->hasAttr<DLLExportAttr>() && + !DerivedCtor->hasAttr<DLLExportAttr>()) { + auto *NewAttr = ::new (getASTContext()) + DLLExportAttr(getASTContext(), *Derived->getAttr<DLLExportAttr>()); + NewAttr->setInherited(true); + DerivedCtor->addAttr(NewAttr); + } + if (ShouldDeleteSpecialMember(DerivedCtor, CXXSpecialMemberKind::DefaultConstructor, &ICI)) SetDeclDeleted(DerivedCtor, UsingLoc); diff --git a/clang/test/CodeGenCXX/dllexport-inherited-ctor.cpp b/clang/test/CodeGenCXX/dllexport-inherited-ctor.cpp new file mode 100644 index 0000000000000..6317b18886f2d --- /dev/null +++ b/clang/test/CodeGenCXX/dllexport-inherited-ctor.cpp @@ -0,0 +1,58 @@ +// RUN: %clang_cc1 -no-enable-noundef-analysis -triple x86_64-windows-msvc -emit-llvm -std=c++17 -fms-extensions -O0 -o - %s | FileCheck --check-prefix=MSVC %s +// RUN: %clang_cc1 -no-enable-noundef-analysis -triple i686-windows-msvc -emit-llvm -std=c++17 -fms-extensions -O0 -o - %s | FileCheck --check-prefix=M32 %s +// RUN: %clang_cc1 -no-enable-noundef-analysis -triple x86_64-windows-gnu -emit-llvm -std=c++17 -fms-extensions -O0 -o - %s | FileCheck --check-prefix=GNU %s + +// Test that inherited constructors via 'using Base::Base' in a dllexport +// class are properly exported (https://github.com/llvm/llvm-project/issues/162640). + +struct __declspec(dllexport) Base { + Base(int); + Base(double); +}; + +struct __declspec(dllexport) Child : public Base { + using Base::Base; +}; + +// The inherited constructors Child(int) and Child(double) should be exported. + +// MSVC-DAG: define weak_odr dso_local dllexport {{.*}} @"??0Child@@QEAA@H@Z" +// MSVC-DAG: define weak_odr dso_local dllexport {{.*}} @"??0Child@@QEAA@N@Z" + +// M32-DAG: define weak_odr dso_local dllexport {{.*}} @"??0Child@@QAE@H@Z" +// M32-DAG: define weak_odr dso_local dllexport {{.*}} @"??0Child@@QAE@N@Z" + +// GNU-DAG: define {{.*}}dso_local dllexport {{.*}} @_ZN5ChildCI14BaseEi( +// GNU-DAG: define {{.*}}dso_local dllexport {{.*}} @_ZN5ChildCI14BaseEd( + +// Also test that a non-exported derived class does not export inherited ctors. +struct NonExportedBase { + NonExportedBase(int); +}; + +struct NonExportedChild : public NonExportedBase { + using NonExportedBase::NonExportedBase; +}; + +// MSVC-NOT: dllexport{{.*}}NonExportedChild +// M32-NOT: dllexport{{.*}}NonExportedChild +// GNU-NOT: dllexport{{.*}}NonExportedChild + +// Test that only the derived class is dllexport, base is not. +struct PlainBase { + PlainBase(int); + PlainBase(float); +}; + +struct __declspec(dllexport) ExportedChild : public PlainBase { + using PlainBase::PlainBase; +}; + +// MSVC-DAG: define weak_odr dso_local dllexport {{.*}} @"??0ExportedChild@@QEAA@H@Z" +// MSVC-DAG: define weak_odr dso_local dllexport {{.*}} @"??0ExportedChild@@QEAA@M@Z" + +// M32-DAG: define weak_odr dso_local dllexport {{.*}} @"??0ExportedChild@@QAE@H@Z" +// M32-DAG: define weak_odr dso_local dllexport {{.*}} @"??0ExportedChild@@QAE@M@Z" + +// GNU-DAG: define {{.*}}dso_local dllexport {{.*}} @_ZN13ExportedChildCI19PlainBaseEi( +// GNU-DAG: define {{.*}}dso_local dllexport {{.*}} @_ZN13ExportedChildCI19PlainBaseEf( >From 3469945b77558423b04cc6db97ba80e4070c8cfb Mon Sep 17 00:00:00 2001 From: Chinmay Deshpande <[email protected]> Date: Tue, 24 Feb 2026 12:49:43 -0500 Subject: [PATCH 2/2] [Clang] Incorporate comments Change-Id: I5ed2513f80010e4bf9e5a0687ea32248c74dcaa4 --- clang/lib/Sema/SemaDeclCXX.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index 34f9e159b4355..85be44ac15651 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -6598,10 +6598,10 @@ void Sema::checkClassLevelDLLAttribute(CXXRecordDecl *Class) { // loop below can propagate the DLL attribute to them. if (ClassExported) { SmallVector<ConstructorUsingShadowDecl *, 4> Shadows; - for (auto *D : Class->decls()) + for (Decl *D : Class->decls()) if (auto *S = dyn_cast<ConstructorUsingShadowDecl>(D)) Shadows.push_back(S); - for (auto *S : Shadows) + for (ConstructorUsingShadowDecl *S : Shadows) if (auto *BC = dyn_cast<CXXConstructorDecl>(S->getTargetDecl()); BC && !BC->isDeleted()) findInheritingConstructor(Class->getLocation(), BC, S); @@ -14391,8 +14391,8 @@ Sema::findInheritingConstructor(SourceLocation Loc, // inherited ctor is an inline definition synthesized by the compiler. if (Derived->hasAttr<DLLExportAttr>() && !DerivedCtor->hasAttr<DLLExportAttr>()) { - auto *NewAttr = ::new (getASTContext()) - DLLExportAttr(getASTContext(), *Derived->getAttr<DLLExportAttr>()); + auto *NewAttr = DLLExportAttr::CreateImplicit( + getASTContext(), *Derived->getAttr<DLLExportAttr>()); NewAttr->setInherited(true); DerivedCtor->addAttr(NewAttr); } _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
