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
