diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 4bd9d904e1ea9..af9883f83a0c2 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -586,6 +586,9 @@ Bug Fixes to Attribute Support ``__attribute__((unused))`` are still ignored after the definition, though this behavior may be relaxed in the future). (#GH135481) +- Clang will warn if a complete type specializes a deprecated partial specialization. + (#GH44496) + Bug Fixes to C++ Support ^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 19343eb0af092..8195f2c9eb39a 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -2388,9 +2388,14 @@ class Sema final : public SemaBase { void DiagnoseAvailabilityOfDecl(NamedDecl *D, ArrayRef Locs, const ObjCInterfaceDecl *UnknownObjCClass, bool ObjCPropertyAccess, - bool AvoidPartialAvailabilityChecks = false, - ObjCInterfaceDecl *ClassReceiver = nullptr); + bool AvoidPartialAvailabilityChecks, + ObjCInterfaceDecl *ClassReceiver); + void DiagnoseAvailabilityOfDecl(NamedDecl *D, ArrayRef Locs); + + std::pair + ShouldDiagnoseAvailabilityOfDecl(const NamedDecl *D, std::string *Message, + ObjCInterfaceDecl *ClassReceiver); ///@} // diff --git a/clang/lib/Sema/SemaAvailability.cpp b/clang/lib/Sema/SemaAvailability.cpp index 96aa65412906c..2e97035d29743 100644 --- a/clang/lib/Sema/SemaAvailability.cpp +++ b/clang/lib/Sema/SemaAvailability.cpp @@ -90,10 +90,9 @@ static const AvailabilityAttr *getAttrForPlatform(ASTContext &Context, /// the availability attribute that is selected. /// \param ClassReceiver If we're checking the method of a class message /// send, the class. Otherwise nullptr. -static std::pair -ShouldDiagnoseAvailabilityOfDecl(Sema &S, const NamedDecl *D, - std::string *Message, - ObjCInterfaceDecl *ClassReceiver) { +std::pair +Sema::ShouldDiagnoseAvailabilityOfDecl(const NamedDecl *D, std::string *Message, + ObjCInterfaceDecl *ClassReceiver) { AvailabilityResult Result = D->getAvailability(Message); // For typedefs, if the typedef declaration appears available look @@ -147,12 +146,12 @@ ShouldDiagnoseAvailabilityOfDecl(Sema &S, const NamedDecl *D, // For +new, infer availability from -init. if (const auto *MD = dyn_cast(D)) { - if (S.ObjC().NSAPIObj && ClassReceiver) { + if (ObjC().NSAPIObj && ClassReceiver) { ObjCMethodDecl *Init = ClassReceiver->lookupInstanceMethod( - S.ObjC().NSAPIObj->getInitSelector()); + ObjC().NSAPIObj->getInitSelector()); if (Init && Result == AR_Available && MD->isClassMethod() && - MD->getSelector() == S.ObjC().NSAPIObj->getNewSelector() && - MD->definedInNSObject(S.getASTContext())) { + MD->getSelector() == ObjC().NSAPIObj->getNewSelector() && + MD->definedInNSObject(getASTContext())) { Result = Init->getAvailability(Message); D = Init; } @@ -162,7 +161,6 @@ ShouldDiagnoseAvailabilityOfDecl(Sema &S, const NamedDecl *D, return {Result, D}; } - /// whether we should emit a diagnostic for \c K and \c DeclVersion in /// the context of \c Ctx. For example, we should emit an unavailable diagnostic /// in a deprecated context, but not the other way around. @@ -876,7 +874,7 @@ void DiagnoseUnguardedAvailability::DiagnoseDeclAvailability( AvailabilityResult Result; const NamedDecl *OffendingDecl; std::tie(Result, OffendingDecl) = - ShouldDiagnoseAvailabilityOfDecl(SemaRef, D, nullptr, ReceiverClass); + SemaRef.ShouldDiagnoseAvailabilityOfDecl(D, nullptr, ReceiverClass); if (Result != AR_Available) { // All other diagnostic kinds have already been handled in // DiagnoseAvailabilityOfDecl. @@ -1112,12 +1110,13 @@ void Sema::DiagnoseAvailabilityOfDecl(NamedDecl *D, bool ObjCPropertyAccess, bool AvoidPartialAvailabilityChecks, ObjCInterfaceDecl *ClassReceiver) { + std::string Message; AvailabilityResult Result; const NamedDecl* OffendingDecl; // See if this declaration is unavailable, deprecated, or partial. std::tie(Result, OffendingDecl) = - ShouldDiagnoseAvailabilityOfDecl(*this, D, &Message, ClassReceiver); + ShouldDiagnoseAvailabilityOfDecl(D, &Message, ClassReceiver); if (Result == AR_Available) return; @@ -1146,3 +1145,11 @@ void Sema::DiagnoseAvailabilityOfDecl(NamedDecl *D, EmitAvailabilityWarning(*this, Result, D, OffendingDecl, Message, Locs, UnknownObjCClass, ObjCPDecl, ObjCPropertyAccess); } + +void Sema::DiagnoseAvailabilityOfDecl(NamedDecl *D, + ArrayRef Locs) { + DiagnoseAvailabilityOfDecl(D, Locs, /*UnknownObjCClass=*/nullptr, + /*ObjCPropertyAccess=*/false, + /*AvoidPartialAvailabilityChecks=*/false, + /*ClassReceiver=*/nullptr); +} diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp index 390ff3ef02df5..fb490bcac6e91 100644 --- a/clang/lib/Sema/SemaTemplateInstantiate.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp @@ -4094,16 +4094,32 @@ bool Sema::InstantiateClassTemplateSpecialization( if (ClassTemplateSpec->isInvalidDecl()) return true; + bool HadAvaibilityWarning = + ShouldDiagnoseAvailabilityOfDecl(ClassTemplateSpec, nullptr, nullptr) + .first != AR_Available; + ActionResult Pattern = getPatternForClassTemplateSpecialization(*this, PointOfInstantiation, ClassTemplateSpec, TSK, PrimaryStrictPackMatch); + if (!Pattern.isUsable()) return Pattern.isInvalid(); - return InstantiateClass( + bool Err = InstantiateClass( PointOfInstantiation, ClassTemplateSpec, Pattern.get(), getTemplateInstantiationArgs(ClassTemplateSpec), TSK, Complain); + + // If we haven't already warn on avaibility, consider the avaibility + // attributes of the partial specialization. + // Note that - because we need to have deduced the partial specialization - + // We can only emit these warnings when the specialization is instantiated. + if (!Err && !HadAvaibilityWarning) { + assert(ClassTemplateSpec->getTemplateSpecializationKind() != + TSK_Undeclared); + DiagnoseAvailabilityOfDecl(ClassTemplateSpec, PointOfInstantiation); + } + return Err; } void diff --git a/clang/test/CXX/dcl.dcl/dcl.attr/dcl.attr.deprecated/p1.cpp b/clang/test/CXX/dcl.dcl/dcl.attr/dcl.attr.deprecated/p1.cpp index 26738583da506..91c0927ca0aa9 100644 --- a/clang/test/CXX/dcl.dcl/dcl.attr/dcl.attr.deprecated/p1.cpp +++ b/clang/test/CXX/dcl.dcl/dcl.attr/dcl.attr.deprecated/p1.cpp @@ -45,7 +45,7 @@ template struct [[deprecated]] B;//expected-note {{'B' has bee B *q2; // expected-warning {{'B' is deprecated}} B *r2; // expected-warning {{'B' is deprecated}} -template +template T some_func(T t) { struct [[deprecated]] FunS{}; // expected-note {{'FunS' has been explicitly marked deprecated here}} FunS f;// expected-warning {{'FunS' is deprecated}} @@ -72,3 +72,70 @@ template struct B { template struct B; // expected-note {{requested here}} } // namespace GH58547 + + +namespace GH44496 { + + +template +class function { }; +template +class __attribute__((deprecated)) function { }; +// expected-note@-1 {{'function' has been explicitly marked deprecated here}} + +void test() { + [[maybe_unused]] function f; // expected-warning{{'function' is deprecated}} +} + +template struct my_template { + using type = void; +}; + +template +struct [[deprecated("primary")]] deprecated { // #deprecated-primary-marked-here + using type = T; +}; + +template +struct my_template : deprecated {}; // #deprecated-primary-base + +template +struct [[deprecated("specialization")]] my_template : deprecated {}; // #my_template-explicit-here + + +template using my_template_t = typename my_template::type; // #deprecated-my-template-alias + +// We cannot warn on X because no instantiation has taken place yet +using X = my_template; + +// Because we already warn on the attribute on the plimary template, we ignore the attribute on the specialization +using Y = my_template_t; +// expected-warning@#deprecated-primary-base {{'deprecated' is deprecated: primary}} \ +// expected-note@-1 {{in instantiation of template type alias}} \ +// expected-note@#deprecated-primary-marked-here {{has been explicitly marked deprecated here}} + +using Z = my_template_t; +// expected-warning@#deprecated-my-template-alias {{'my_template' is deprecated: specialization}} \ +// expected-note@#my_template-explicit-here {{'my_template' has been explicitly marked deprecated here}} \ +// expected-note@#deprecated-my-template-alias {{in instantiation of template class 'GH44496::my_template' requested here}} \ +// expected-note@-1 {{in instantiation of template type alias 'my_template_t' requested here}} + +template +struct primary_not_deprecated { + using type = T; +}; +template +struct [[deprecated("specialization")]] primary_not_deprecated : deprecated {}; +// expected-note@-1 {{'primary_not_deprecated' has been explicitly marked deprecated here}} + +// We cannot warn on S1 because no instantiation has taken place yet +using S1 = primary_not_deprecated; + + +using S2 = primary_not_deprecated; + +X x; +Z z; +S2 s2; +// expected-warning@-1 {{'primary_not_deprecated' is deprecated: specialization}} +}