https://github.com/chinmaydd updated 
https://github.com/llvm/llvm-project/pull/182706

>From 457567bdc05916579c2c8c60f0596808fdaf6aec Mon Sep 17 00:00:00 2001
From: Chinmay Deshpande <[email protected]>
Date: Fri, 20 Feb 2026 16:20:05 -0800
Subject: [PATCH 1/7] [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 d650555d10f11..c7287791582c0 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -13013,10 +13013,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 bd03909024bcc789d01d8622dd7827a983a025d3 Mon Sep 17 00:00:00 2001
From: Chinmay Deshpande <[email protected]>
Date: Tue, 24 Feb 2026 12:49:43 -0500
Subject: [PATCH 2/7] [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);
   }

>From 7c326e1af831cf5d85854d4e0c54e0325a9f3fe5 Mon Sep 17 00:00:00 2001
From: Chinmay Deshpande <[email protected]>
Date: Tue, 24 Feb 2026 16:31:48 -0800
Subject: [PATCH 3/7] [Clang] Incorporate comments

---
 clang/lib/AST/ASTContext.cpp                    |  7 ++++++-
 clang/lib/CodeGen/ModuleBuilder.cpp             | 16 ++++++++++++++++
 clang/lib/Sema/SemaDeclCXX.cpp                  | 17 -----------------
 .../CodeGenCXX/dllexport-inherited-ctor.cpp     |  8 ++++++--
 4 files changed, 28 insertions(+), 20 deletions(-)

diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp
index c7287791582c0..394064edf4bf3 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -13018,8 +13018,13 @@ static GVALinkage basicGVALinkageForFunction(const 
ASTContext &Context,
     // 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.
+    // to match MSVC's behavior. This is ABI-compatible because the MS ABI
+    // mangles inherited constructors identically to regular constructors
+    // (only class name + parameter types, no "inherited from" encoding),
+    // and Clang's forwarding thunks produce an equivalent calling sequence
+    // for the common case (no virtual bases).
     return GVA_Internal;
 
   return GVA_DiscardableODR;
diff --git a/clang/lib/CodeGen/ModuleBuilder.cpp 
b/clang/lib/CodeGen/ModuleBuilder.cpp
index b4885d572c294..668e9ed2a0466 100644
--- a/clang/lib/CodeGen/ModuleBuilder.cpp
+++ b/clang/lib/CodeGen/ModuleBuilder.cpp
@@ -260,6 +260,22 @@ namespace {
           }
         }
       }
+
+      // Emit dllexport inherited constructors. These are synthesized during
+      // Sema (in checkClassLevelDLLAttribute) but have no written definition,
+      // so they must be emitted now while visiting the class definition.
+      if (auto *RD = dyn_cast<CXXRecordDecl>(D)) {
+        if (RD->hasAttr<DLLExportAttr>()) {
+          for (Decl *Member : RD->decls()) {
+            if (auto *CD = dyn_cast<CXXConstructorDecl>(Member)) {
+              if (CD->getInheritedConstructor() &&
+                  CD->hasAttr<DLLExportAttr>() && !CD->isDeleted())
+                Builder->EmitTopLevelDecl(CD);
+            }
+          }
+        }
+      }
+
       // For OpenMP emit declare reduction functions, if required.
       if (Ctx->getLangOpts().OpenMP) {
         for (Decl *Member : D->decls()) {
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index 85be44ac15651..e0c19cb073ab7 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -6312,12 +6312,6 @@ 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()) {
@@ -14386,17 +14380,6 @@ 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 = DLLExportAttr::CreateImplicit(
-        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
index 6317b18886f2d..299e2be42c8c5 100644
--- a/clang/test/CodeGenCXX/dllexport-inherited-ctor.cpp
+++ b/clang/test/CodeGenCXX/dllexport-inherited-ctor.cpp
@@ -15,9 +15,13 @@ struct __declspec(dllexport) Child : public Base {
 };
 
 // The inherited constructors Child(int) and Child(double) should be exported.
+// Verify the thunk bodies delegate to the base constructor with the correct
+// arguments, ensuring ABI compatibility with MSVC-compiled callers.
 
-// MSVC-DAG: define weak_odr dso_local dllexport {{.*}} @"??0Child@@QEAA@H@Z"
-// MSVC-DAG: define weak_odr dso_local dllexport {{.*}} @"??0Child@@QEAA@N@Z"
+// MSVC-DAG: define weak_odr dso_local dllexport {{.*}} 
@"??0Child@@QEAA@H@Z"(ptr {{.*}} %this, i32 %0)
+// MSVC-DAG: call {{.*}} @"??0Base@@QEAA@H@Z"(ptr {{.*}} %this{{.*}}, i32
+// MSVC-DAG: define weak_odr dso_local dllexport {{.*}} 
@"??0Child@@QEAA@N@Z"(ptr {{.*}} %this, double %0)
+// MSVC-DAG: call {{.*}} @"??0Base@@QEAA@N@Z"(ptr {{.*}} %this{{.*}}, double
 
 // M32-DAG: define weak_odr dso_local dllexport {{.*}} @"??0Child@@QAE@H@Z"
 // M32-DAG: define weak_odr dso_local dllexport {{.*}} @"??0Child@@QAE@N@Z"

>From f084baa724b0df714fc48c528f8d8be1879b154a Mon Sep 17 00:00:00 2001
From: Chinmay Deshpande <[email protected]>
Date: Tue, 24 Feb 2026 17:01:01 -0800
Subject: [PATCH 4/7] [Clang] fix tests

---
 clang/lib/Sema/SemaDeclCXX.cpp | 10 +++++++---
 1 file changed, 7 insertions(+), 3 deletions(-)

diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index e0c19cb073ab7..d03a361e1e83e 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -6619,11 +6619,15 @@ void Sema::checkClassLevelDLLAttribute(CXXRecordDecl 
*Class) {
 
       if (MD->isInlined()) {
         // MinGW does not import or export inline methods. But do it for
-        // template instantiations.
+        // template instantiations and inherited constructors (which are
+        // marked inline but must be exported to match MSVC behavior).
         if (!Context.getTargetInfo().shouldDLLImportComdatSymbols() &&
             TSK != TSK_ExplicitInstantiationDeclaration &&
-            TSK != TSK_ExplicitInstantiationDefinition)
-          continue;
+            TSK != TSK_ExplicitInstantiationDefinition) {
+          if (auto *CD = dyn_cast<CXXConstructorDecl>(MD);
+              !CD || !CD->getInheritedConstructor())
+            continue;
+        }
 
         // MSVC versions before 2015 don't export the move assignment operators
         // and move constructor, so don't attempt to import/export them if

>From b71a238b570e1946ed95e4ef556ff34393773677 Mon Sep 17 00:00:00 2001
From: Chinmay Deshpande <[email protected]>
Date: Wed, 25 Feb 2026 12:39:55 -0800
Subject: [PATCH 5/7] [Clang] Update comment

---
 clang/lib/AST/ASTContext.cpp | 15 ++++++---------
 1 file changed, 6 insertions(+), 9 deletions(-)

diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp
index 394064edf4bf3..5c209f23855dd 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -13015,16 +13015,13 @@ static GVALinkage basicGVALinkageForFunction(const 
ASTContext &Context,
       isa<CXXConstructorDecl>(FD) &&
       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.
+    // Both Clang and MSVC implement inherited constructors as forwarding
+    // thunks that delegate to the base constructor with the same mangled
+    // names and calling convention. Keep non-dllexport inheriting constructor
+    // thunks internal since they are not needed outside the translation unit.
     //
-    // However, dllexport inherited constructors must be externally visible
-    // to match MSVC's behavior. This is ABI-compatible because the MS ABI
-    // mangles inherited constructors identically to regular constructors
-    // (only class name + parameter types, no "inherited from" encoding),
-    // and Clang's forwarding thunks produce an equivalent calling sequence
-    // for the common case (no virtual bases).
+    // But, dllexport inherited constructors must be externally visible to 
+    // match MSVC's behavior.
     return GVA_Internal;
 
   return GVA_DiscardableODR;

>From fee0952197bb528faa6a0c937af4060672867120 Mon Sep 17 00:00:00 2001
From: Chinmay Deshpande <[email protected]>
Date: Wed, 25 Feb 2026 13:11:43 -0800
Subject: [PATCH 6/7] Apply suggestion from @chinmaydd

---
 clang/lib/AST/ASTContext.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp
index 5c209f23855dd..f5109e133c81b 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -13020,7 +13020,7 @@ static GVALinkage basicGVALinkageForFunction(const 
ASTContext &Context,
     // names and calling convention. Keep non-dllexport inheriting constructor
     // thunks internal since they are not needed outside the translation unit.
     //
-    // But, dllexport inherited constructors must be externally visible to 
+    // But, dllexport inherited constructors must be externally visible to
     // match MSVC's behavior.
     return GVA_Internal;
 

>From 2132a4c955e12750776a35003e8a1af412fed3bf Mon Sep 17 00:00:00 2001
From: Chinmay Deshpande <[email protected]>
Date: Thu, 26 Feb 2026 15:41:47 -0800
Subject: [PATCH 7/7] [Clang] Make change for corner cases, update tests

---
 clang/lib/AST/ASTContext.cpp                  |  10 +-
 clang/lib/Sema/SemaDeclCXX.cpp                |   8 +-
 .../CodeGenCXX/dllexport-inherited-ctor.cpp   | 140 +++++++++++++++++-
 3 files changed, 150 insertions(+), 8 deletions(-)

diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp
index f5109e133c81b..428ce24fcf016 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -13015,13 +13015,13 @@ static GVALinkage basicGVALinkageForFunction(const 
ASTContext &Context,
       isa<CXXConstructorDecl>(FD) &&
       cast<CXXConstructorDecl>(FD)->isInheritingConstructor() &&
       !FD->hasAttr<DLLExportAttr>())
-    // Both Clang and MSVC implement inherited constructors as forwarding
-    // thunks that delegate to the base constructor with the same mangled
-    // names and calling convention. Keep non-dllexport inheriting constructor
+    // Clang's inheriting constructor thunks use a forwarding model that
+    // differs from MSVC's implementation in some edge cases (e.g. inalloca
+    // argument forwarding on i686). Keep non-dllexport inheriting constructor
     // thunks internal since they are not needed outside the translation unit.
     //
-    // But, dllexport inherited constructors must be externally visible to
-    // match MSVC's behavior.
+    // dllexport inherited constructors are exempted so they are externally
+    // visible, matching MSVC's export behavior for the common cases.
     return GVA_Internal;
 
   return GVA_DiscardableODR;
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index d03a361e1e83e..530b96019b782 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -6660,8 +6660,14 @@ void Sema::checkClassLevelDLLAttribute(CXXRecordDecl 
*Class) {
 
       // Do not export/import inline function when -fno-dllexport-inlines is
       // passed. But add attribute for later local static var check.
+      // Inherited constructors are marked inline but must still be exported
+      // to match MSVC behavior, so exclude them from this override.
+      bool IsInheritedCtor = false;
+      if (MD)
+        if (auto *CD = dyn_cast<CXXConstructorDecl>(MD))
+          IsInheritedCtor = (bool)CD->getInheritedConstructor();
       if (!getLangOpts().DllExportInlines && MD && MD->isInlined() &&
-          TSK != TSK_ExplicitInstantiationDeclaration &&
+          !IsInheritedCtor && TSK != TSK_ExplicitInstantiationDeclaration &&
           TSK != TSK_ExplicitInstantiationDefinition) {
         if (ClassExported) {
           NewAttr = ::new (getASTContext())
diff --git a/clang/test/CodeGenCXX/dllexport-inherited-ctor.cpp 
b/clang/test/CodeGenCXX/dllexport-inherited-ctor.cpp
index 299e2be42c8c5..06feae7eb42eb 100644
--- a/clang/test/CodeGenCXX/dllexport-inherited-ctor.cpp
+++ b/clang/test/CodeGenCXX/dllexport-inherited-ctor.cpp
@@ -1,10 +1,15 @@
 // 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
+// RUN: %clang_cc1 -no-enable-noundef-analysis -triple x86_64-windows-msvc 
-emit-llvm -std=c++17 -fms-extensions -fno-dllexport-inlines -O0 -o - %s | 
FileCheck --check-prefix=NOINLINE %s
 
 // Test that inherited constructors via 'using Base::Base' in a dllexport
 // class are properly exported 
(https://github.com/llvm/llvm-project/issues/162640).
 
+//===----------------------------------------------------------------------===//
+// Basic: both base and child exported, simple parameter types.
+//===----------------------------------------------------------------------===//
+
 struct __declspec(dllexport) Base {
   Base(int);
   Base(double);
@@ -29,7 +34,10 @@ struct __declspec(dllexport) Child : public Base {
 // 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.
+//===----------------------------------------------------------------------===//
+// Non-exported class should not export inherited ctors.
+//===----------------------------------------------------------------------===//
+
 struct NonExportedBase {
   NonExportedBase(int);
 };
@@ -42,7 +50,10 @@ struct NonExportedChild : public NonExportedBase {
 // M32-NOT: dllexport{{.*}}NonExportedChild
 // GNU-NOT: dllexport{{.*}}NonExportedChild
 
-// Test that only the derived class is dllexport, base is not.
+//===----------------------------------------------------------------------===//
+// Only the derived class is dllexport, base is not.
+//===----------------------------------------------------------------------===//
+
 struct PlainBase {
   PlainBase(int);
   PlainBase(float);
@@ -60,3 +71,128 @@ struct __declspec(dllexport) ExportedChild : public 
PlainBase {
 
 // GNU-DAG: define {{.*}}dso_local dllexport {{.*}} 
@_ZN13ExportedChildCI19PlainBaseEi(
 // GNU-DAG: define {{.*}}dso_local dllexport {{.*}} 
@_ZN13ExportedChildCI19PlainBaseEf(
+
+//===----------------------------------------------------------------------===//
+// Multi-level inheritance: A -> B -> C with using at each level.
+//===----------------------------------------------------------------------===//
+
+struct MLBase {
+  MLBase(int);
+};
+
+struct MLMiddle : MLBase {
+  using MLBase::MLBase;
+};
+
+struct __declspec(dllexport) MLChild : MLMiddle {
+  using MLMiddle::MLMiddle;
+};
+
+// MSVC-DAG: define weak_odr dso_local dllexport {{.*}} @"??0MLChild@@QEAA@H@Z"
+// M32-DAG: define weak_odr dso_local dllexport {{.*}} @"??0MLChild@@QAE@H@Z"
+// GNU-DAG: define {{.*}}dso_local dllexport {{.*}} @_ZN7MLChildCI16MLBaseEi(
+
+//===----------------------------------------------------------------------===//
+// Class template specialization with inherited constructors.
+//===----------------------------------------------------------------------===//
+
+template <typename T>
+struct TplBase {
+  TplBase(T);
+};
+
+struct __declspec(dllexport) TplChild : TplBase<int> {
+  using TplBase<int>::TplBase;
+};
+
+// MSVC-DAG: define weak_odr dso_local dllexport {{.*}} 
@"??0TplChild@@QEAA@H@Z"
+// M32-DAG: define weak_odr dso_local dllexport {{.*}} @"??0TplChild@@QAE@H@Z"
+// GNU-DAG: define {{.*}}dso_local dllexport {{.*}} 
@_ZN8TplChildCI17TplBaseIiEEi(
+
+//===----------------------------------------------------------------------===//
+// Default arguments: thunk takes the full parameter list.
+//===----------------------------------------------------------------------===//
+
+struct DefArgBase {
+  DefArgBase(int a, int b = 10, int c = 20);
+};
+
+struct __declspec(dllexport) DefArgChild : DefArgBase {
+  using DefArgBase::DefArgBase;
+};
+
+// MSVC-DAG: define weak_odr dso_local dllexport {{.*}} 
@"??0DefArgChild@@QEAA@HHH@Z"(ptr {{.*}} %this, i32 %0, i32 %1, i32 %2)
+// MSVC-DAG: call {{.*}} @"??0DefArgBase@@QEAA@HHH@Z"(ptr {{.*}}, i32 {{.*}}, 
i32 {{.*}}, i32
+
+// M32-DAG: define weak_odr dso_local dllexport {{.*}} 
@"??0DefArgChild@@QAE@HHH@Z"
+// M32-DAG: call {{.*}} @"??0DefArgBase@@QAE@HHH@Z"(ptr {{.*}}, i32 {{.*}}, 
i32 {{.*}}, i32
+
+// GNU-DAG: define {{.*}}dso_local dllexport {{.*}} 
@_ZN11DefArgChildCI110DefArgBaseEiii(
+// GNU-DAG: call {{.*}} @_ZN10DefArgBaseC2Eiii(ptr {{.*}}, i32 {{.*}}, i32 
{{.*}}, i32
+
+//===----------------------------------------------------------------------===//
+// Default arguments with mixed types.
+//===----------------------------------------------------------------------===//
+
+struct MixedDefBase {
+  MixedDefBase(int a, double b = 3.14);
+};
+
+struct __declspec(dllexport) MixedDefChild : MixedDefBase {
+  using MixedDefBase::MixedDefBase;
+};
+
+// MSVC-DAG: define weak_odr dso_local dllexport {{.*}} 
@"??0MixedDefChild@@QEAA@HN@Z"(ptr {{.*}} %this, i32 %0, double %1)
+// MSVC-DAG: call {{.*}} @"??0MixedDefBase@@QEAA@HN@Z"(ptr {{.*}}, i32 {{.*}}, 
double
+
+// M32-DAG: define weak_odr dso_local dllexport {{.*}} 
@"??0MixedDefChild@@QAE@HN@Z"
+// M32-DAG: call {{.*}} @"??0MixedDefBase@@QAE@HN@Z"(ptr {{.*}}, i32 {{.*}}, 
double
+
+// GNU-DAG: define {{.*}}dso_local dllexport {{.*}} 
@_ZN13MixedDefChildCI112MixedDefBaseEid(
+// GNU-DAG: call {{.*}} @_ZN12MixedDefBaseC2Eid(ptr {{.*}}, i32 {{.*}}, double
+
+//===----------------------------------------------------------------------===//
+// All parameters have defaults. The inherited constructor takes the full
+// parameter list. The implicit default constructor also gets exported, with
+// default argument values baked in.
+//===----------------------------------------------------------------------===//
+
+struct AllDefBase {
+  AllDefBase(int a = 1, int b = 2);
+};
+
+struct __declspec(dllexport) AllDefChild : AllDefBase {
+  using AllDefBase::AllDefBase;
+};
+
+// MSVC-DAG: define weak_odr dso_local dllexport {{.*}} 
@"??0AllDefChild@@QEAA@HH@Z"(ptr {{.*}} %this, i32 %0, i32 %1)
+// MSVC-DAG: define weak_odr dso_local dllexport {{.*}} 
@"??0AllDefChild@@QEAA@XZ"(ptr {{.*}} %this)
+// MSVC-DAG: call {{.*}} @"??0AllDefBase@@QEAA@HH@Z"(ptr {{.*}}, i32 1, i32 2)
+
+// M32-DAG: define weak_odr dso_local dllexport {{.*}} 
@"??0AllDefChild@@QAE@HH@Z"
+// M32-DAG: call {{.*}} @"??0AllDefBase@@QAE@HH@Z"(ptr {{.*}}, i32 %
+// M32-DAG: define weak_odr dso_local dllexport {{.*}} 
@"??0AllDefChild@@QAE@XZ"
+// M32-DAG: call {{.*}} @"??0AllDefBase@@QAE@HH@Z"(ptr {{.*}}, i32 1, i32 2)
+
+// GNU does not emit an implicit default constructor unless it is used.
+// Only the inherited constructor with the full parameter list is exported.
+// GNU-DAG: define {{.*}}dso_local dllexport {{.*}} 
@_ZN11AllDefChildCI110AllDefBaseEii(
+// GNU-DAG: call {{.*}} @_ZN10AllDefBaseC2Eii(ptr {{.*}}, i32 %{{.*}}, i32 %
+
+//===----------------------------------------------------------------------===//
+// -fno-dllexport-inlines should still export inherited constructors.
+// Inherited constructors are marked inline internally but must be exported.
+//===----------------------------------------------------------------------===//
+
+// NOINLINE-DAG: define weak_odr dso_local dllexport {{.*}} 
@"??0Child@@QEAA@H@Z"
+// NOINLINE-DAG: define weak_odr dso_local dllexport {{.*}} 
@"??0Child@@QEAA@N@Z"
+// NOINLINE-DAG: define weak_odr dso_local dllexport {{.*}} 
@"??0ExportedChild@@QEAA@H@Z"
+// NOINLINE-DAG: define weak_odr dso_local dllexport {{.*}} 
@"??0ExportedChild@@QEAA@M@Z"
+// NOINLINE-DAG: define weak_odr dso_local dllexport {{.*}} 
@"??0MLChild@@QEAA@H@Z"
+// NOINLINE-DAG: define weak_odr dso_local dllexport {{.*}} 
@"??0TplChild@@QEAA@H@Z"
+// NOINLINE-DAG: define weak_odr dso_local dllexport {{.*}} 
@"??0DefArgChild@@QEAA@HHH@Z"
+// NOINLINE-DAG: define weak_odr dso_local dllexport {{.*}} 
@"??0MixedDefChild@@QEAA@HN@Z"
+// NOINLINE-DAG: define weak_odr dso_local dllexport {{.*}} 
@"??0AllDefChild@@QEAA@HH@Z"
+// The implicit default ctor is a regular inline method, NOT an inherited
+// constructor, so -fno-dllexport-inlines correctly suppresses it.
+// NOINLINE-NOT: define {{.*}}dllexport{{.*}} @"??0AllDefChild@@QEAA@XZ"

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

Reply via email to