-
Notifications
You must be signed in to change notification settings - Fork 13.4k
[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
base: main
Are you sure you want to change the base?
Conversation
…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
@llvm/pr-subscribers-clang Author: cor3ntin (cor3ntin) ChangesThere 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:
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:
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}}
+}
|
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:
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