https://github.com/cor3ntin created https://github.com/llvm/llvm-project/pull/138426
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 warn on availability of the primary. Therefore, we only warn for 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 availability 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 >From c1be3c519363583609673695e72d151027d96f55 Mon Sep 17 00:00:00 2001 From: Corentin Jabot <corentinja...@gmail.com> Date: Sat, 3 May 2025 23:49:17 +0200 Subject: [PATCH] [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<SourceLocation> Locs, - const ObjCInterfaceDecl *UnknownObjCClass, - bool ObjCPropertyAccess, - bool AvoidPartialAvailabilityChecks = false, - ObjCInterfaceDecl *ClassReceiver = nullptr); - + void DiagnoseAvailabilityOfDecl( + NamedDecl *D, ArrayRef<SourceLocation> Locs, + const ObjCInterfaceDecl *UnknownObjCClass = nullptr, + bool ObjCPropertyAccess = false, + bool AvoidPartialAvailabilityChecks = false, + ObjCInterfaceDecl *ClassReceiver = nullptr); + + std::pair<AvailabilityResult, const NamedDecl *> + 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<AvailabilityResult, const NamedDecl *> -ShouldDiagnoseAvailabilityOfDecl(Sema &S, const NamedDecl *D, - std::string *Message, - ObjCInterfaceDecl *ClassReceiver) { +std::pair<AvailabilityResult, const NamedDecl *> +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<ObjCMethodDecl>(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<CXXRecordDecl *> 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 <typename T> struct [[deprecated]] B;//expected-note {{'B<int>' has bee B<int> *q2; // expected-warning {{'B<int>' is deprecated}} B<float> *r2; // expected-warning {{'B<float>' is deprecated}} -template <typename T> +template <typename T> 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 <class B1> struct B { template struct B<A>; // expected-note {{requested here}} } // namespace GH58547 + + +namespace GH44496 { + template <class> struct my_template { + using type = void; +}; + +template <class T> +struct [[deprecated("primary")]] deprecated { // #deprecated-primary-marked-here + using type = T; +}; + +template <class T> +struct my_template<volatile T> : deprecated<T> {}; // #deprecated-primary-base + +template <class T> +struct [[deprecated("specialization")]] my_template<const T> : deprecated<const T> {}; // #my_template-explicit-here + + +template <class T> using my_template_t = typename my_template<T>::type; // #deprecated-my-template-alias + +// We cannot warn on X because no instantiation has taken place yet +using X = my_template<volatile int>; + +// Because we already warn on the attribute on the plimary template, we ignore the attribute on the specialization +using Y = my_template_t<const int>; +// expected-warning@#deprecated-primary-base {{'deprecated<int>' 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<volatile int>; +// expected-warning@#deprecated-my-template-alias {{'my_template<const int>' is deprecated: specialization}} \ +// expected-note@#my_template-explicit-here {{'my_template<const int>' has been explicitly marked deprecated here}} \ +// expected-note@#deprecated-my-template-alias {{in instantiation of template class 'GH44496::my_template<volatile int>' requested here}} \ +// expected-note@-1 {{in instantiation of template type alias 'my_template_t' requested here}} + +template <class T> +struct primary_not_deprecated { + using type = T; +}; +template <class T> +struct [[deprecated("specialization")]] primary_not_deprecated<volatile T> : deprecated<T> {}; +// expected-note@-1 {{'primary_not_deprecated<volatile int>' has been explicitly marked deprecated here}} + +// We cannot warn on S1 because no instantiation has taken place yet +using S1 = primary_not_deprecated<volatile int>; + + +using S2 = primary_not_deprecated<volatile int>; + +X x; +Z z; +S2 s2; +// expected-warning@-1 {{'primary_not_deprecated<volatile int>' is deprecated: specialization}} +} _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits