From c1be3c519363583609673695e72d151027d96f55 Mon Sep 17 00:00:00 2001 From: Corentin Jabot Date: Sat, 3 May 2025 23:49:17 +0200 Subject: [PATCH 1/2] [Clang] Minimal support for avaibility attributes on partial specializations There are some limitations. Because we only know which partial specialization to refer to when instantiating, and because we can't instantiate the class before we require a complete type, we can only use the partial specialization once we have a complete class. Similarly, because we don't know if a class is ever going to be complete, we always always warn on avaibility of the primary. Therefore we only warn fr the partial specialization if we did not warn on the primary. I considered alternatives to address that second limitation: - Delay warnings to the end of the TU - Tracking where each avaibility attribute originally comes from. However, both of these have drawbacks, and the use case is probably less motivated than wanting to deprecate the use of a specific specialization. Fixes #44496 --- clang/docs/ReleaseNotes.rst | 3 + clang/include/clang/Sema/Sema.h | 16 ++++-- clang/lib/Sema/SemaAvailability.cpp | 21 ++++--- clang/lib/Sema/SemaTemplateInstantiate.cpp | 18 +++++- .../dcl.attr/dcl.attr.deprecated/p1.cpp | 57 ++++++++++++++++++- 5 files changed, 96 insertions(+), 19 deletions(-) diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 4bd9d904e1ea9..a02cca2b9122b 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 completete 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..eed799852d9bd 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -2385,12 +2385,16 @@ class Sema final : public SemaBase { /// potential availability violations. sema::FunctionScopeInfo *getCurFunctionAvailabilityContext(); - void DiagnoseAvailabilityOfDecl(NamedDecl *D, ArrayRef Locs, - const ObjCInterfaceDecl *UnknownObjCClass, - bool ObjCPropertyAccess, - bool AvoidPartialAvailabilityChecks = false, - ObjCInterfaceDecl *ClassReceiver = nullptr); - + void DiagnoseAvailabilityOfDecl( + NamedDecl *D, ArrayRef Locs, + const ObjCInterfaceDecl *UnknownObjCClass = nullptr, + bool ObjCPropertyAccess = false, + bool AvoidPartialAvailabilityChecks = false, + ObjCInterfaceDecl *ClassReceiver = nullptr); + + 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..b3c9dd9242fd5 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; 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..13bc833ede8b5 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,58 @@ template struct B { template struct B; // expected-note {{requested here}} } // namespace GH58547 + + +namespace GH44496 { + 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}} +} From 964ede0a4ef3cb9934688b05780ebfdcbaf8d56d Mon Sep 17 00:00:00 2001 From: Corentin Jabot Date: Mon, 5 May 2025 17:37:27 +0200 Subject: [PATCH 2/2] address feedback --- clang/docs/ReleaseNotes.rst | 2 +- clang/include/clang/Sema/Sema.h | 13 +++++++------ clang/lib/Sema/SemaAvailability.cpp | 8 ++++++++ .../dcl.dcl/dcl.attr/dcl.attr.deprecated/p1.cpp | 16 ++++++++++++++-- 4 files changed, 30 insertions(+), 9 deletions(-) diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index a02cca2b9122b..af9883f83a0c2 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -586,7 +586,7 @@ 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 completete type specializes a deprecated partial specialization. +- 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 eed799852d9bd..8195f2c9eb39a 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -2385,12 +2385,13 @@ class Sema final : public SemaBase { /// potential availability violations. sema::FunctionScopeInfo *getCurFunctionAvailabilityContext(); - void DiagnoseAvailabilityOfDecl( - NamedDecl *D, ArrayRef Locs, - const ObjCInterfaceDecl *UnknownObjCClass = nullptr, - bool ObjCPropertyAccess = false, - bool AvoidPartialAvailabilityChecks = false, - ObjCInterfaceDecl *ClassReceiver = nullptr); + void DiagnoseAvailabilityOfDecl(NamedDecl *D, ArrayRef Locs, + const ObjCInterfaceDecl *UnknownObjCClass, + bool ObjCPropertyAccess, + bool AvoidPartialAvailabilityChecks, + ObjCInterfaceDecl *ClassReceiver); + + void DiagnoseAvailabilityOfDecl(NamedDecl *D, ArrayRef Locs); std::pair ShouldDiagnoseAvailabilityOfDecl(const NamedDecl *D, std::string *Message, diff --git a/clang/lib/Sema/SemaAvailability.cpp b/clang/lib/Sema/SemaAvailability.cpp index b3c9dd9242fd5..2e97035d29743 100644 --- a/clang/lib/Sema/SemaAvailability.cpp +++ b/clang/lib/Sema/SemaAvailability.cpp @@ -1145,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/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 13bc833ede8b5..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 @@ -75,8 +75,20 @@ template struct B; // expected-note {{requested here}} namespace GH44496 { - template struct my_template { - using type = void; + + +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