https://github.com/kikairoya updated https://github.com/llvm/llvm-project/pull/191392
>From 996b78b19a6657ef0cf9e40d23c125b779fddb4d Mon Sep 17 00:00:00 2001 From: kikairoya <[email protected]> Date: Sat, 28 Mar 2026 12:34:19 +0900 Subject: [PATCH 1/4] pretest --- clang/test/SemaCXX/dllexport.cpp | 50 ++++++++++++++++++++++++++++++++ clang/test/SemaCXX/dllimport.cpp | 45 ++++++++++++++++++++++++++++ 2 files changed, 95 insertions(+) diff --git a/clang/test/SemaCXX/dllexport.cpp b/clang/test/SemaCXX/dllexport.cpp index 70e7f1398ad05..24f39d813b498 100644 --- a/clang/test/SemaCXX/dllexport.cpp +++ b/clang/test/SemaCXX/dllexport.cpp @@ -1063,6 +1063,56 @@ template<typename T> __declspec(dllexport) constexpr int CTMR<T>::ConstexprField // dllexport. template <> void ExportClassTmplMembers<int>::normalDecl() = delete; // non-gnu-error {{attribute 'dllexport' cannot be applied to a deleted function}} +struct InstTrig { + struct Spec; + struct Impl; + struct Decl; +}; +template<bool InstDef, typename... Triggers> +struct ClassTmplSpecializedMember { // gnu-note 5 {{'dllexport' attribute is missing on previous declaration}} + void specializedMember1(); + void specializedMember2(); + void instantiatedMember1(); + void instantiatedMember2(); + void member() {} +}; + +template <> void ClassTmplSpecializedMember<false, InstTrig::Spec>::specializedMember1(); +extern template struct __declspec(dllexport) ClassTmplSpecializedMember<false, InstTrig::Spec>; // non-gnu-warning{{explicit instantiation declaration should not be 'dllexport'}} \ + non-gnu-note{{attribute is here}} +template <> void ClassTmplSpecializedMember<true, InstTrig::Spec>::specializedMember1(); +template struct __declspec(dllexport) ClassTmplSpecializedMember<true, InstTrig::Spec>; // gnu-warning{{'dllexport' attribute ignored on explicit instantiation definition}} + +void anchor(ClassTmplSpecializedMember<false, InstTrig::Impl> &x) { x.instantiatedMember1(); } +extern template struct __declspec(dllexport) ClassTmplSpecializedMember<false, InstTrig::Impl>; // non-gnu-warning{{explicit instantiation declaration should not be 'dllexport'}} \ + non-gnu-note{{attribute is here}} +void anchor(ClassTmplSpecializedMember<true, InstTrig::Impl> &x) { x.instantiatedMember1(); } +template struct __declspec(dllexport) ClassTmplSpecializedMember<true, InstTrig::Impl>; // gnu-warning{{'dllexport' attribute ignored on explicit instantiation definition}} + +template <> void ClassTmplSpecializedMember<false, InstTrig::Spec, InstTrig::Spec>::specializedMember1(); +template <> void ClassTmplSpecializedMember<false, InstTrig::Spec, InstTrig::Spec>::specializedMember2(); +extern template struct __declspec(dllexport) ClassTmplSpecializedMember<false, InstTrig::Spec, InstTrig::Spec>; // non-gnu-warning{{explicit instantiation declaration should not be 'dllexport'}} \ + non-gnu-note{{attribute is here}} +template <> void ClassTmplSpecializedMember<true, InstTrig::Spec, InstTrig::Spec>::specializedMember1(); +template <> void ClassTmplSpecializedMember<true, InstTrig::Spec, InstTrig::Spec>::specializedMember2(); +template struct __declspec(dllexport) ClassTmplSpecializedMember<true, InstTrig::Spec, InstTrig::Spec>; // gnu-warning{{'dllexport' attribute ignored on explicit instantiation definition}} + +template <> void ClassTmplSpecializedMember<false, InstTrig::Spec, InstTrig::Impl>::specializedMember1(); +void anchor(ClassTmplSpecializedMember<false, InstTrig::Spec, InstTrig::Impl> &x) { x.instantiatedMember1(); } +extern template struct __declspec(dllexport) ClassTmplSpecializedMember<false, InstTrig::Spec, InstTrig::Impl>; // non-gnu-warning{{explicit instantiation declaration should not be 'dllexport'}} \ + non-gnu-note{{attribute is here}} +template <> void ClassTmplSpecializedMember<true, InstTrig::Spec, InstTrig::Impl>::specializedMember1(); +void anchor(ClassTmplSpecializedMember<true, InstTrig::Spec, InstTrig::Impl> &x) { x.instantiatedMember1(); } +template struct __declspec(dllexport) ClassTmplSpecializedMember<true, InstTrig::Spec, InstTrig::Impl>; // gnu-warning{{'dllexport' attribute ignored on explicit instantiation definition}} + +void anchor(ClassTmplSpecializedMember<false, InstTrig::Impl, InstTrig::Spec> &x) { x.instantiatedMember1(); } +template <> void ClassTmplSpecializedMember<false, InstTrig::Impl, InstTrig::Spec>::specializedMember1(); +extern template struct __declspec(dllexport) ClassTmplSpecializedMember<false, InstTrig::Impl, InstTrig::Spec>; // non-gnu-warning{{explicit instantiation declaration should not be 'dllexport'}} \ + non-gnu-note{{attribute is here}} +void anchor(ClassTmplSpecializedMember<true, InstTrig::Impl, InstTrig::Spec> &x) { x.instantiatedMember1(); } +template <> void ClassTmplSpecializedMember<true, InstTrig::Impl, InstTrig::Spec>::specializedMember1(); +template struct __declspec(dllexport) ClassTmplSpecializedMember<true, InstTrig::Impl, InstTrig::Spec>; // gnu-warning{{'dllexport' attribute ignored on explicit instantiation definition}} + //===----------------------------------------------------------------------===// // Class template member templates diff --git a/clang/test/SemaCXX/dllimport.cpp b/clang/test/SemaCXX/dllimport.cpp index deb0574cb5e68..aa8c823bfe85c 100644 --- a/clang/test/SemaCXX/dllimport.cpp +++ b/clang/test/SemaCXX/dllimport.cpp @@ -999,6 +999,51 @@ template<> void ClassTmpl<int>::importedStatic() {} // non-gnu-error{{cannot def template <> void ImportClassTmplMembers<int>::normalDecl() = delete; // non-gnu-error{{cannot define non-inline dllimport template specialization}} \ non-gnu-error{{attribute 'dllimport' cannot be applied to a deleted function}} +struct InstTrig { + struct Spec; + struct Impl; + struct Decl; +}; +template<bool InstDef, typename... Triggers> +struct ClassTmplSpecializedMember { + void specializedMember1(); + void specializedMember2(); + void instantiatedMember1(); + void instantiatedMember2(); + void member() {} +}; + +template <> void ClassTmplSpecializedMember<false, InstTrig::Spec>::specializedMember1(); +extern template struct __declspec(dllimport) ClassTmplSpecializedMember<false, InstTrig::Spec>; +template <> void ClassTmplSpecializedMember<true, InstTrig::Spec>::specializedMember1(); +template struct __declspec(dllimport) ClassTmplSpecializedMember<true, InstTrig::Spec>; + +void anchor(ClassTmplSpecializedMember<false, InstTrig::Impl> &x) { x.instantiatedMember1(); } +extern template struct __declspec(dllimport) ClassTmplSpecializedMember<false, InstTrig::Impl>; +void anchor(ClassTmplSpecializedMember<true, InstTrig::Impl> &x) { x.instantiatedMember1(); } +template struct __declspec(dllimport) ClassTmplSpecializedMember<true, InstTrig::Impl>; + +template <> void ClassTmplSpecializedMember<false, InstTrig::Spec, InstTrig::Spec>::specializedMember1(); +template <> void ClassTmplSpecializedMember<false, InstTrig::Spec, InstTrig::Spec>::specializedMember2(); +extern template struct __declspec(dllimport) ClassTmplSpecializedMember<false, InstTrig::Spec, InstTrig::Spec>; +template <> void ClassTmplSpecializedMember<true, InstTrig::Spec, InstTrig::Spec>::specializedMember1(); +template <> void ClassTmplSpecializedMember<true, InstTrig::Spec, InstTrig::Spec>::specializedMember2(); +template struct __declspec(dllimport) ClassTmplSpecializedMember<true, InstTrig::Spec, InstTrig::Spec>; + +template <> void ClassTmplSpecializedMember<false, InstTrig::Spec, InstTrig::Impl>::specializedMember1(); +void anchor(ClassTmplSpecializedMember<false, InstTrig::Spec, InstTrig::Impl> &x) { x.instantiatedMember1(); } +extern template struct __declspec(dllimport) ClassTmplSpecializedMember<false, InstTrig::Spec, InstTrig::Impl>; +template <> void ClassTmplSpecializedMember<true, InstTrig::Spec, InstTrig::Impl>::specializedMember1(); +void anchor(ClassTmplSpecializedMember<true, InstTrig::Spec, InstTrig::Impl> &x) { x.instantiatedMember1(); } +template struct __declspec(dllimport) ClassTmplSpecializedMember<true, InstTrig::Spec, InstTrig::Impl>; + +void anchor(ClassTmplSpecializedMember<false, InstTrig::Impl, InstTrig::Spec> &x) { x.instantiatedMember1(); } +template <> void ClassTmplSpecializedMember<false, InstTrig::Impl, InstTrig::Spec>::specializedMember1(); +extern template struct __declspec(dllimport) ClassTmplSpecializedMember<false, InstTrig::Impl, InstTrig::Spec>; +void anchor(ClassTmplSpecializedMember<true, InstTrig::Impl, InstTrig::Spec> &x) { x.instantiatedMember1(); } +template <> void ClassTmplSpecializedMember<true, InstTrig::Impl, InstTrig::Spec>::specializedMember1(); +template struct __declspec(dllimport) ClassTmplSpecializedMember<true, InstTrig::Impl, InstTrig::Spec>; + //===----------------------------------------------------------------------===// // Class template member templates >From 9a13f16300c0021969b0532062dffc478dbb22d6 Mon Sep 17 00:00:00 2001 From: kikairoya <[email protected]> Date: Sun, 15 Mar 2026 18:19:23 +0900 Subject: [PATCH 2/4] [Clang] Warn about ignored dllimport on explicit instantiations Diagnose about a non-effective dllimport attribute on an explicit instantiation declaration for a specialization which was already (implicitly) instantiated somewhere, rather than ignoring it silently. related to #21132 --- .../clang/Basic/DiagnosticSemaKinds.td | 6 ++ clang/lib/Sema/SemaTemplate.cpp | 99 ++++++++++++------- 2 files changed, 69 insertions(+), 36 deletions(-) diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 9bd0a526654c7..96f390226bec5 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -3874,6 +3874,9 @@ def warn_dllimport_dropped_from_inline_function : Warning< def warn_nothrow_attribute_ignored : Warning<"'nothrow' attribute conflicts with" " exception specification; attribute ignored">, InGroup<IgnoredAttributes>; +def warn_dllattr_ignored_already_instantiated : Warning< + "%0 attribute ignored; class template is already instantiated">, + InGroup<IgnoredAttributes>; def warn_dllattr_ignored_exclusion_takes_precedence : Warning< "%0 attribute ignored; %1 takes precedence">, InGroup<IgnoredAttributes>; @@ -3997,6 +4000,9 @@ def err_attribute_dllimport_static_field_definition : Error< def warn_attribute_dllimport_static_field_definition : Warning< "definition of dllimport static field">, InGroup<DiagGroup<"dllimport-static-field-def">>; +def warn_attribute_dllimport_explicit_instantiation_def : Warning< + "'dllimport' attribute ignored on explicit instantiation definition">, + InGroup<IgnoredAttributes>; def warn_attribute_dllexport_explicit_instantiation_decl : Warning< "explicit instantiation declaration should not be 'dllexport'">, InGroup<DllexportExplicitInstantiationDecl>; diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp index b8b0c71894daa..9db77f8387337 100644 --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -10294,23 +10294,32 @@ DeclResult Sema::ActOnExplicitInstantiation( ? TSK_ExplicitInstantiationDefinition : TSK_ExplicitInstantiationDeclaration; + bool DLLAttrAffected = false; + const ParsedAttr *AttachedExportAttr = nullptr; + const ParsedAttr *AttachedImportAttr = nullptr; + for (const ParsedAttr &AL : Attr) { + if (AL.getKind() == ParsedAttr::AT_DLLExport) + AttachedExportAttr = &AL; + else if (AL.getKind() == ParsedAttr::AT_DLLImport) + AttachedImportAttr = &AL; + } + if (TSK == TSK_ExplicitInstantiationDeclaration && !Context.getTargetInfo().getTriple().isOSCygMing()) { // Check for dllexport class template instantiation declarations, // except for MinGW mode. - for (const ParsedAttr &AL : Attr) { - if (AL.getKind() == ParsedAttr::AT_DLLExport) { - Diag(ExternLoc, - diag::warn_attribute_dllexport_explicit_instantiation_decl); - Diag(AL.getLoc(), diag::note_attribute); - break; - } + if (AttachedExportAttr) { + Diag(ExternLoc, + diag::warn_attribute_dllexport_explicit_instantiation_decl); + Diag(AttachedExportAttr->getLoc(), diag::note_attribute); + DLLAttrAffected = true; } if (auto *A = ClassTemplate->getTemplatedDecl()->getAttr<DLLExportAttr>()) { Diag(ExternLoc, diag::warn_attribute_dllexport_explicit_instantiation_decl); Diag(A->getLocation(), diag::note_attribute); + DLLAttrAffected = true; } } @@ -10318,20 +10327,11 @@ DeclResult Sema::ActOnExplicitInstantiation( // instantiation declarations for most purposes. bool DLLImportExplicitInstantiationDef = false; if (TSK == TSK_ExplicitInstantiationDefinition && - Context.getTargetInfo().getCXXABI().isMicrosoft()) { + Context.getTargetInfo().shouldDLLImportComdatSymbols()) { // Check for dllimport class template instantiation definitions. bool DLLImport = ClassTemplate->getTemplatedDecl()->getAttr<DLLImportAttr>(); - for (const ParsedAttr &AL : Attr) { - if (AL.getKind() == ParsedAttr::AT_DLLImport) - DLLImport = true; - if (AL.getKind() == ParsedAttr::AT_DLLExport) { - // dllexport trumps dllimport here. - DLLImport = false; - break; - } - } - if (DLLImport) { + if ((DLLImport || AttachedImportAttr) && !AttachedExportAttr) { TSK = TSK_ExplicitInstantiationDeclaration; DLLImportExplicitInstantiationDef = true; } @@ -10363,28 +10363,30 @@ DeclResult Sema::ActOnExplicitInstantiation( Context.getTargetInfo().getTriple().isOSCygMing()) { // Check for dllexport class template instantiation definitions in MinGW // mode, if a previous declaration of the instantiation was seen. - for (const ParsedAttr &AL : Attr) { - if (AL.getKind() == ParsedAttr::AT_DLLExport) { - if (PrevDecl->hasAttr<DLLExportAttr>()) { - Diag(AL.getLoc(), diag::warn_attr_dllexport_explicit_inst_def); - } else { - Diag(AL.getLoc(), - diag::warn_attr_dllexport_explicit_inst_def_mismatch); - Diag(PrevDecl->getLocation(), diag::note_prev_decl_missing_dllexport); - } - break; + if (AttachedExportAttr) { + if (PrevDecl->hasAttr<DLLExportAttr>()) { + Diag(AttachedExportAttr->getLoc(), + diag::warn_attr_dllexport_explicit_inst_def); + } else { + Diag(AttachedExportAttr->getLoc(), + diag::warn_attr_dllexport_explicit_inst_def_mismatch); + Diag(PrevDecl->getLocation(), diag::note_prev_decl_missing_dllexport); } + DLLAttrAffected = true; + } else if (AttachedImportAttr) { + Diag(AttachedImportAttr->getLoc(), + diag::warn_attribute_dllimport_explicit_instantiation_def); + DLLAttrAffected = true; } } if (TSK == TSK_ExplicitInstantiationDefinition && PrevDecl && !Context.getTargetInfo().getTriple().isWindowsGNUEnvironment() && - llvm::none_of(Attr, [](const ParsedAttr &AL) { - return AL.getKind() == ParsedAttr::AT_DLLExport; - })) { + !AttachedExportAttr) { if (const auto *DEA = PrevDecl->getAttr<DLLExportOnDeclAttr>()) { Diag(TemplateLoc, diag::warn_dllexport_on_decl_ignored); Diag(DEA->getLoc(), diag::note_dllexport_on_decl); + DLLAttrAffected = true; } } @@ -10456,7 +10458,10 @@ DeclResult Sema::ActOnExplicitInstantiation( Specialization->setTemplateKeywordLoc(TemplateLoc); Specialization->setBraceRange(SourceRange()); - bool PreviouslyDLLExported = Specialization->hasAttr<DLLExportAttr>(); + bool PreviouslyDLLExported = Specialization->hasAttr<DLLExportAttr>() || + (PrevDecl && PrevDecl->hasAttr<DLLExportAttr>()); + bool PreviouslyDLLImported = Specialization->hasAttr<DLLImportAttr>() || + (PrevDecl && PrevDecl->hasAttr<DLLImportAttr>()); ProcessDeclAttributeList(S, Specialization, Attr); ProcessAPINotes(Specialization); @@ -10492,11 +10497,12 @@ DeclResult Sema::ActOnExplicitInstantiation( ClassTemplateSpecializationDecl *Def = cast_or_null<ClassTemplateSpecializationDecl>( Specialization->getDefinition()); - if (!Def) + if (!Def) { InstantiateClassTemplateSpecialization(TemplateNameLoc, Specialization, TSK, /*Complain=*/true, CTAI.StrictPackMatch); - else if (TSK == TSK_ExplicitInstantiationDefinition) { + DLLAttrAffected = true; + } else if (TSK == TSK_ExplicitInstantiationDefinition) { MarkVTableUsed(TemplateNameLoc, Specialization, true); Specialization->setPointOfInstantiation(Def->getPointOfInstantiation()); } @@ -10524,13 +10530,16 @@ DeclResult Sema::ActOnExplicitInstantiation( A->setInherited(true); Def->addAttr(A); dllExportImportClassTemplateSpecialization(*this, Def); + DLLAttrAffected = true; } } // Fix a TSK_ImplicitInstantiation followed by a // TSK_ExplicitInstantiationDefinition - bool NewlyDLLExported = - !PreviouslyDLLExported && Specialization->hasAttr<DLLExportAttr>(); + bool NewlyDLLExported = !PreviouslyDLLExported && AttachedExportAttr && + Specialization->hasAttr<DLLExportAttr>(); + bool NewlyDLLImported = !PreviouslyDLLImported && AttachedImportAttr && + Specialization->hasAttr<DLLImportAttr>(); if (Old_TSK == TSK_ImplicitInstantiation && NewlyDLLExported && Context.getTargetInfo().shouldDLLImportComdatSymbols()) { // An explicit instantiation definition can add a dll attribute to a @@ -10548,6 +10557,7 @@ DeclResult Sema::ActOnExplicitInstantiation( assert(Def == Specialization && "Def and Specialization should match for implicit instantiation"); dllExportImportClassTemplateSpecialization(*this, Def); + DLLAttrAffected = true; } // In MinGW mode, export the template instantiation if the declaration @@ -10556,6 +10566,23 @@ DeclResult Sema::ActOnExplicitInstantiation( Context.getTargetInfo().getTriple().isOSCygMing() && PrevDecl->hasAttr<DLLExportAttr>()) { dllExportImportClassTemplateSpecialization(*this, Def); + DLLAttrAffected = true; + } + + if (!DLLAttrAffected && (NewlyDLLExported || NewlyDLLImported)) { + if (Context.getTargetInfo().getTriple().isOSCygMing() && + TSK == TSK_ExplicitInstantiationDeclaration && NewlyDLLImported) { + // In MinGW mode, all undefined symbols are also searched from DLLs + // even if they were not declared with dllimport, so doesn't warn + // about ignoring dllimport. + } else { + const ParsedAttr *A = + AttachedExportAttr ? AttachedExportAttr : AttachedImportAttr; + Diag(A->getLoc(), diag::warn_dllattr_ignored_already_instantiated) << A; + Diag(Def->getPointOfInstantiation(), + diag::note_instantiation_required_here) + << /*implicit|explicit=*/0; + } } // Set the template specialization kind. Make sure it is set before >From 8244a27297d6ab5843072a90e2ab342b6b4c6f96 Mon Sep 17 00:00:00 2001 From: kikairoya <[email protected]> Date: Thu, 19 Mar 2026 21:47:26 +0900 Subject: [PATCH 3/4] update test --- clang/test/SemaCXX/dllexport.cpp | 25 +++++++++------- clang/test/SemaCXX/dllimport.cpp | 49 ++++++++++++++++++-------------- 2 files changed, 42 insertions(+), 32 deletions(-) diff --git a/clang/test/SemaCXX/dllexport.cpp b/clang/test/SemaCXX/dllexport.cpp index 24f39d813b498..51c37e0d53eda 100644 --- a/clang/test/SemaCXX/dllexport.cpp +++ b/clang/test/SemaCXX/dllexport.cpp @@ -1077,38 +1077,43 @@ struct ClassTmplSpecializedMember { // gnu-note 5 {{'dllexport' attribute is mis void member() {} }; -template <> void ClassTmplSpecializedMember<false, InstTrig::Spec>::specializedMember1(); +template <> void ClassTmplSpecializedMember<false, InstTrig::Spec>::specializedMember1(); // gnu-note{{implicit instantiation first required here}} extern template struct __declspec(dllexport) ClassTmplSpecializedMember<false, InstTrig::Spec>; // non-gnu-warning{{explicit instantiation declaration should not be 'dllexport'}} \ - non-gnu-note{{attribute is here}} + non-gnu-note{{attribute is here}} \ + gnu-warning{{'dllexport' attribute ignored; class template is already instantiated}} template <> void ClassTmplSpecializedMember<true, InstTrig::Spec>::specializedMember1(); template struct __declspec(dllexport) ClassTmplSpecializedMember<true, InstTrig::Spec>; // gnu-warning{{'dllexport' attribute ignored on explicit instantiation definition}} -void anchor(ClassTmplSpecializedMember<false, InstTrig::Impl> &x) { x.instantiatedMember1(); } +void anchor(ClassTmplSpecializedMember<false, InstTrig::Impl> &x) { x.instantiatedMember1(); } // gnu-note{{implicit instantiation first required here}} extern template struct __declspec(dllexport) ClassTmplSpecializedMember<false, InstTrig::Impl>; // non-gnu-warning{{explicit instantiation declaration should not be 'dllexport'}} \ - non-gnu-note{{attribute is here}} + non-gnu-note{{attribute is here}} \ + gnu-warning{{'dllexport' attribute ignored; class template is already instantiated}} void anchor(ClassTmplSpecializedMember<true, InstTrig::Impl> &x) { x.instantiatedMember1(); } template struct __declspec(dllexport) ClassTmplSpecializedMember<true, InstTrig::Impl>; // gnu-warning{{'dllexport' attribute ignored on explicit instantiation definition}} -template <> void ClassTmplSpecializedMember<false, InstTrig::Spec, InstTrig::Spec>::specializedMember1(); +template <> void ClassTmplSpecializedMember<false, InstTrig::Spec, InstTrig::Spec>::specializedMember1(); // gnu-note{{implicit instantiation first required here}} template <> void ClassTmplSpecializedMember<false, InstTrig::Spec, InstTrig::Spec>::specializedMember2(); extern template struct __declspec(dllexport) ClassTmplSpecializedMember<false, InstTrig::Spec, InstTrig::Spec>; // non-gnu-warning{{explicit instantiation declaration should not be 'dllexport'}} \ - non-gnu-note{{attribute is here}} + non-gnu-note{{attribute is here}} \ + gnu-warning{{'dllexport' attribute ignored; class template is already instantiated}} template <> void ClassTmplSpecializedMember<true, InstTrig::Spec, InstTrig::Spec>::specializedMember1(); template <> void ClassTmplSpecializedMember<true, InstTrig::Spec, InstTrig::Spec>::specializedMember2(); template struct __declspec(dllexport) ClassTmplSpecializedMember<true, InstTrig::Spec, InstTrig::Spec>; // gnu-warning{{'dllexport' attribute ignored on explicit instantiation definition}} -template <> void ClassTmplSpecializedMember<false, InstTrig::Spec, InstTrig::Impl>::specializedMember1(); +template <> void ClassTmplSpecializedMember<false, InstTrig::Spec, InstTrig::Impl>::specializedMember1(); // gnu-note{{implicit instantiation first required here}} void anchor(ClassTmplSpecializedMember<false, InstTrig::Spec, InstTrig::Impl> &x) { x.instantiatedMember1(); } extern template struct __declspec(dllexport) ClassTmplSpecializedMember<false, InstTrig::Spec, InstTrig::Impl>; // non-gnu-warning{{explicit instantiation declaration should not be 'dllexport'}} \ - non-gnu-note{{attribute is here}} + non-gnu-note{{attribute is here}} \ + gnu-warning{{'dllexport' attribute ignored; class template is already instantiated}} template <> void ClassTmplSpecializedMember<true, InstTrig::Spec, InstTrig::Impl>::specializedMember1(); void anchor(ClassTmplSpecializedMember<true, InstTrig::Spec, InstTrig::Impl> &x) { x.instantiatedMember1(); } template struct __declspec(dllexport) ClassTmplSpecializedMember<true, InstTrig::Spec, InstTrig::Impl>; // gnu-warning{{'dllexport' attribute ignored on explicit instantiation definition}} -void anchor(ClassTmplSpecializedMember<false, InstTrig::Impl, InstTrig::Spec> &x) { x.instantiatedMember1(); } +void anchor(ClassTmplSpecializedMember<false, InstTrig::Impl, InstTrig::Spec> &x) { x.instantiatedMember1(); } // gnu-note{{implicit instantiation first required here}} template <> void ClassTmplSpecializedMember<false, InstTrig::Impl, InstTrig::Spec>::specializedMember1(); extern template struct __declspec(dllexport) ClassTmplSpecializedMember<false, InstTrig::Impl, InstTrig::Spec>; // non-gnu-warning{{explicit instantiation declaration should not be 'dllexport'}} \ - non-gnu-note{{attribute is here}} + non-gnu-note{{attribute is here}} \ + gnu-warning{{'dllexport' attribute ignored; class template is already instantiated}} void anchor(ClassTmplSpecializedMember<true, InstTrig::Impl, InstTrig::Spec> &x) { x.instantiatedMember1(); } template <> void ClassTmplSpecializedMember<true, InstTrig::Impl, InstTrig::Spec>::specializedMember1(); template struct __declspec(dllexport) ClassTmplSpecializedMember<true, InstTrig::Impl, InstTrig::Spec>; // gnu-warning{{'dllexport' attribute ignored on explicit instantiation definition}} diff --git a/clang/test/SemaCXX/dllimport.cpp b/clang/test/SemaCXX/dllimport.cpp index aa8c823bfe85c..c0266be95cd62 100644 --- a/clang/test/SemaCXX/dllimport.cpp +++ b/clang/test/SemaCXX/dllimport.cpp @@ -1013,36 +1013,41 @@ struct ClassTmplSpecializedMember { void member() {} }; -template <> void ClassTmplSpecializedMember<false, InstTrig::Spec>::specializedMember1(); -extern template struct __declspec(dllimport) ClassTmplSpecializedMember<false, InstTrig::Spec>; -template <> void ClassTmplSpecializedMember<true, InstTrig::Spec>::specializedMember1(); -template struct __declspec(dllimport) ClassTmplSpecializedMember<true, InstTrig::Spec>; - -void anchor(ClassTmplSpecializedMember<false, InstTrig::Impl> &x) { x.instantiatedMember1(); } -extern template struct __declspec(dllimport) ClassTmplSpecializedMember<false, InstTrig::Impl>; -void anchor(ClassTmplSpecializedMember<true, InstTrig::Impl> &x) { x.instantiatedMember1(); } -template struct __declspec(dllimport) ClassTmplSpecializedMember<true, InstTrig::Impl>; - -template <> void ClassTmplSpecializedMember<false, InstTrig::Spec, InstTrig::Spec>::specializedMember1(); +template <> void ClassTmplSpecializedMember<false, InstTrig::Spec>::specializedMember1(); // non-gnu-note{{implicit instantiation first required here}} +extern template struct __declspec(dllimport) ClassTmplSpecializedMember<false, InstTrig::Spec>; // non-gnu-warning{{'dllimport' attribute ignored; class template is already instantiated}} +template <> void ClassTmplSpecializedMember<true, InstTrig::Spec>::specializedMember1(); // non-gnu-note{{implicit instantiation first required here}} +template struct __declspec(dllimport) ClassTmplSpecializedMember<true, InstTrig::Spec>; // non-gnu-warning{{'dllimport' attribute ignored; class template is already instantiated}} \ + gnu-warning{{'dllimport' attribute ignored on explicit instantiation definition}} + +void anchor(ClassTmplSpecializedMember<false, InstTrig::Impl> &x) { x.instantiatedMember1(); } // non-gnu-note{{implicit instantiation first required here}} +extern template struct __declspec(dllimport) ClassTmplSpecializedMember<false, InstTrig::Impl>; // non-gnu-warning{{'dllimport' attribute ignored; class template is already instantiated}} +void anchor(ClassTmplSpecializedMember<true, InstTrig::Impl> &x) { x.instantiatedMember1(); } // non-gnu-note{{implicit instantiation first required here}} +template struct __declspec(dllimport) ClassTmplSpecializedMember<true, InstTrig::Impl>; // non-gnu-warning{{'dllimport' attribute ignored; class template is already instantiated}} \ + gnu-warning{{'dllimport' attribute ignored on explicit instantiation definition}} + +template <> void ClassTmplSpecializedMember<false, InstTrig::Spec, InstTrig::Spec>::specializedMember1(); // non-gnu-note{{implicit instantiation first required here}} template <> void ClassTmplSpecializedMember<false, InstTrig::Spec, InstTrig::Spec>::specializedMember2(); -extern template struct __declspec(dllimport) ClassTmplSpecializedMember<false, InstTrig::Spec, InstTrig::Spec>; -template <> void ClassTmplSpecializedMember<true, InstTrig::Spec, InstTrig::Spec>::specializedMember1(); +extern template struct __declspec(dllimport) ClassTmplSpecializedMember<false, InstTrig::Spec, InstTrig::Spec>; // non-gnu-warning{{'dllimport' attribute ignored; class template is already instantiated}} +template <> void ClassTmplSpecializedMember<true, InstTrig::Spec, InstTrig::Spec>::specializedMember1(); // non-gnu-note{{implicit instantiation first required here}} template <> void ClassTmplSpecializedMember<true, InstTrig::Spec, InstTrig::Spec>::specializedMember2(); -template struct __declspec(dllimport) ClassTmplSpecializedMember<true, InstTrig::Spec, InstTrig::Spec>; +template struct __declspec(dllimport) ClassTmplSpecializedMember<true, InstTrig::Spec, InstTrig::Spec>; // non-gnu-warning{{'dllimport' attribute ignored; class template is already instantiated}} \ + gnu-warning{{'dllimport' attribute ignored on explicit instantiation definition}} -template <> void ClassTmplSpecializedMember<false, InstTrig::Spec, InstTrig::Impl>::specializedMember1(); +template <> void ClassTmplSpecializedMember<false, InstTrig::Spec, InstTrig::Impl>::specializedMember1(); // non-gnu-note{{implicit instantiation first required here}} void anchor(ClassTmplSpecializedMember<false, InstTrig::Spec, InstTrig::Impl> &x) { x.instantiatedMember1(); } -extern template struct __declspec(dllimport) ClassTmplSpecializedMember<false, InstTrig::Spec, InstTrig::Impl>; -template <> void ClassTmplSpecializedMember<true, InstTrig::Spec, InstTrig::Impl>::specializedMember1(); +extern template struct __declspec(dllimport) ClassTmplSpecializedMember<false, InstTrig::Spec, InstTrig::Impl>; // non-gnu-warning{{'dllimport' attribute ignored; class template is already instantiated}} +template <> void ClassTmplSpecializedMember<true, InstTrig::Spec, InstTrig::Impl>::specializedMember1(); // non-gnu-note{{implicit instantiation first required here}} void anchor(ClassTmplSpecializedMember<true, InstTrig::Spec, InstTrig::Impl> &x) { x.instantiatedMember1(); } -template struct __declspec(dllimport) ClassTmplSpecializedMember<true, InstTrig::Spec, InstTrig::Impl>; +template struct __declspec(dllimport) ClassTmplSpecializedMember<true, InstTrig::Spec, InstTrig::Impl>; // non-gnu-warning{{'dllimport' attribute ignored; class template is already instantiated}} \ + gnu-warning{{'dllimport' attribute ignored on explicit instantiation definition}} -void anchor(ClassTmplSpecializedMember<false, InstTrig::Impl, InstTrig::Spec> &x) { x.instantiatedMember1(); } +void anchor(ClassTmplSpecializedMember<false, InstTrig::Impl, InstTrig::Spec> &x) { x.instantiatedMember1(); } // non-gnu-note{{implicit instantiation first required here}} template <> void ClassTmplSpecializedMember<false, InstTrig::Impl, InstTrig::Spec>::specializedMember1(); -extern template struct __declspec(dllimport) ClassTmplSpecializedMember<false, InstTrig::Impl, InstTrig::Spec>; -void anchor(ClassTmplSpecializedMember<true, InstTrig::Impl, InstTrig::Spec> &x) { x.instantiatedMember1(); } +extern template struct __declspec(dllimport) ClassTmplSpecializedMember<false, InstTrig::Impl, InstTrig::Spec>; // non-gnu-warning{{'dllimport' attribute ignored; class template is already instantiated}} +void anchor(ClassTmplSpecializedMember<true, InstTrig::Impl, InstTrig::Spec> &x) { x.instantiatedMember1(); } // non-gnu-note{{implicit instantiation first required here}} template <> void ClassTmplSpecializedMember<true, InstTrig::Impl, InstTrig::Spec>::specializedMember1(); -template struct __declspec(dllimport) ClassTmplSpecializedMember<true, InstTrig::Impl, InstTrig::Spec>; +template struct __declspec(dllimport) ClassTmplSpecializedMember<true, InstTrig::Impl, InstTrig::Spec>; // non-gnu-warning{{'dllimport' attribute ignored; class template is already instantiated}} \ + gnu-warning{{'dllimport' attribute ignored on explicit instantiation definition}} //===----------------------------------------------------------------------===// >From 1213a7c256c8eb4a896a634e7e8f54c2cddffa8a Mon Sep 17 00:00:00 2001 From: kikairoya <[email protected]> Date: Wed, 9 Sep 2026 06:14:01 +0900 Subject: [PATCH 4/4] restore a comment --- clang/lib/Sema/SemaTemplate.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp index 9db77f8387337..50ff56ff7811e 100644 --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -10331,6 +10331,7 @@ DeclResult Sema::ActOnExplicitInstantiation( // Check for dllimport class template instantiation definitions. bool DLLImport = ClassTemplate->getTemplatedDecl()->getAttr<DLLImportAttr>(); + // dllexport trumps dllimport. if ((DLLImport || AttachedImportAttr) && !AttachedExportAttr) { TSK = TSK_ExplicitInstantiationDeclaration; DLLImportExplicitInstantiationDef = true; _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
