[clang] abd1057 - [Clang] Minimal support for availability attributes on partial specializations (#138426)

via cfe-commits cfe-commits at lists.llvm.org
Mon May 5 10:31:40 PDT 2025


Author: cor3ntin
Date: 2025-05-05T19:31:36+02:00
New Revision: abd10578653a4f16c81c6a164f6367af64f21194

URL: https://github.com/llvm/llvm-project/commit/abd10578653a4f16c81c6a164f6367af64f21194
DIFF: https://github.com/llvm/llvm-project/commit/abd10578653a4f16c81c6a164f6367af64f21194.diff

LOG: [Clang] Minimal support for availability attributes on partial specializations (#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

Added: 
    

Modified: 
    clang/docs/ReleaseNotes.rst
    clang/include/clang/Sema/Sema.h
    clang/lib/Sema/SemaAvailability.cpp
    clang/lib/Sema/SemaTemplateInstantiate.cpp
    clang/test/CXX/dcl.dcl/dcl.attr/dcl.attr.deprecated/p1.cpp

Removed: 
    


################################################################################
diff  --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index eaf777cd048c1..203958dab7430 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -594,6 +594,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 741951cb9ea0e..dd3c7b487aa29 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<SourceLocation> Locs,
                                   const ObjCInterfaceDecl *UnknownObjCClass,
                                   bool ObjCPropertyAccess,
-                                  bool AvoidPartialAvailabilityChecks = false,
-                                  ObjCInterfaceDecl *ClassReceiver = nullptr);
+                                  bool AvoidPartialAvailabilityChecks,
+                                  ObjCInterfaceDecl *ClassReceiver);
 
+  void DiagnoseAvailabilityOfDecl(NamedDecl *D, ArrayRef<SourceLocation> Locs);
+
+  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..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<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;
 
@@ -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<SourceLocation> 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<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..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 <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,70 @@ template <class B1> struct B {
 
 template struct B<A>; // expected-note {{requested here}}
 } // namespace GH58547
+
+
+namespace GH44496 {
+
+
+template <typename T>
+class function { };
+template <typename A>
+class __attribute__((deprecated)) function<void(A)> { };
+// expected-note at -1 {{'function<void (int)>' has been explicitly marked deprecated here}}
+
+void test() {
+    [[maybe_unused]] function<void(int)> f; // expected-warning{{'function<void (int)>' is deprecated}}
+}
+
+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 at -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 at -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 at -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 at -1 {{'primary_not_deprecated<volatile int>' is deprecated: specialization}}
+}


        


More information about the cfe-commits mailing list