Author: Tomohiro Kashiwada Date: 2026-09-16T13:40:03+02:00 New Revision: de1d4bafa475f2206fffe878789dd29d22197b2f
URL: https://github.com/llvm/llvm-project/commit/de1d4bafa475f2206fffe878789dd29d22197b2f DIFF: https://github.com/llvm/llvm-project/commit/de1d4bafa475f2206fffe878789dd29d22197b2f.diff LOG: [Clang] Warn about ignored dllimport on explicit instantiations (#191392) 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 Added: Modified: clang/include/clang/Basic/DiagnosticSemaKinds.td clang/lib/Sema/SemaTemplate.cpp clang/test/SemaCXX/dllexport.cpp clang/test/SemaCXX/dllimport.cpp Removed: ################################################################################ diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 7aee8d3f306ee..485145addad01 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -3880,6 +3880,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>; @@ -4003,6 +4006,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..50ff56ff7811e 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,12 @@ 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) { + // dllexport trumps dllimport. + if ((DLLImport || AttachedImportAttr) && !AttachedExportAttr) { TSK = TSK_ExplicitInstantiationDeclaration; DLLImportExplicitInstantiationDef = true; } @@ -10363,28 +10364,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 +10459,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 +10498,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 +10531,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 +10558,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 +10567,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 diff --git a/clang/test/SemaCXX/dllexport.cpp b/clang/test/SemaCXX/dllexport.cpp index 70e7f1398ad05..51c37e0d53eda 100644 --- a/clang/test/SemaCXX/dllexport.cpp +++ b/clang/test/SemaCXX/dllexport.cpp @@ -1063,6 +1063,61 @@ 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(); // 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}} \ + 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(); } // 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}} \ + 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(); // 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}} \ + 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(); // 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}} \ + 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(); } // 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}} \ + 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}} + //===----------------------------------------------------------------------===// // Class template member templates diff --git a/clang/test/SemaCXX/dllimport.cpp b/clang/test/SemaCXX/dllimport.cpp index deb0574cb5e68..c0266be95cd62 100644 --- a/clang/test/SemaCXX/dllimport.cpp +++ b/clang/test/SemaCXX/dllimport.cpp @@ -999,6 +999,56 @@ 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(); // 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>; // 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>; // 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(); // 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>; // 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>; // 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(); } // 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>; // 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>; // non-gnu-warning{{'dllimport' attribute ignored; class template is already instantiated}} \ + gnu-warning{{'dllimport' attribute ignored on explicit instantiation definition}} + //===----------------------------------------------------------------------===// // Class template member templates _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
