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

Reply via email to