Skip to content

[Clang] Minimal support for availability attributes on partial specializations #138426

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

cor3ntin
Copy link
Contributor

@cor3ntin cor3ntin commented May 3, 2025

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

…zations

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 llvm#44496
@llvmbot llvmbot added clang Clang issues not falling into any other category clang:frontend Language frontend issues, e.g. anything involving "Sema" labels May 3, 2025
@llvmbot
Copy link
Member

llvmbot commented May 3, 2025

@llvm/pr-subscribers-clang

Author: cor3ntin (cor3ntin)

Changes

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


Full diff: https://github.com/llvm/llvm-project/pull/138426.diff

5 Files Affected:

  • (modified) clang/docs/ReleaseNotes.rst (+3)
  • (modified) clang/include/clang/Sema/Sema.h (+10-6)
  • (modified) clang/lib/Sema/SemaAvailability.cpp (+10-11)
  • (modified) clang/lib/Sema/SemaTemplateInstantiate.cpp (+17-1)
  • (modified) clang/test/CXX/dcl.dcl/dcl.attr/dcl.attr.deprecated/p1.cpp (+56-1)
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}}
+}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
clang:frontend Language frontend issues, e.g. anything involving "Sema" clang Clang issues not falling into any other category
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Deprecated attribute not honoured on partial template specialization
2 participants