Skip to content

Commit abd1057

Browse files
authored
[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
1 parent fc0f074 commit abd1057

File tree

5 files changed

+113
-15
lines changed

5 files changed

+113
-15
lines changed

clang/docs/ReleaseNotes.rst

+3
Original file line numberDiff line numberDiff line change
@@ -594,6 +594,9 @@ Bug Fixes to Attribute Support
594594
``__attribute__((unused))`` are still ignored after the definition, though
595595
this behavior may be relaxed in the future). (#GH135481)
596596

597+
- Clang will warn if a complete type specializes a deprecated partial specialization.
598+
(#GH44496)
599+
597600
Bug Fixes to C++ Support
598601
^^^^^^^^^^^^^^^^^^^^^^^^
599602

clang/include/clang/Sema/Sema.h

+7-2
Original file line numberDiff line numberDiff line change
@@ -2388,9 +2388,14 @@ class Sema final : public SemaBase {
23882388
void DiagnoseAvailabilityOfDecl(NamedDecl *D, ArrayRef<SourceLocation> Locs,
23892389
const ObjCInterfaceDecl *UnknownObjCClass,
23902390
bool ObjCPropertyAccess,
2391-
bool AvoidPartialAvailabilityChecks = false,
2392-
ObjCInterfaceDecl *ClassReceiver = nullptr);
2391+
bool AvoidPartialAvailabilityChecks,
2392+
ObjCInterfaceDecl *ClassReceiver);
23932393

2394+
void DiagnoseAvailabilityOfDecl(NamedDecl *D, ArrayRef<SourceLocation> Locs);
2395+
2396+
std::pair<AvailabilityResult, const NamedDecl *>
2397+
ShouldDiagnoseAvailabilityOfDecl(const NamedDecl *D, std::string *Message,
2398+
ObjCInterfaceDecl *ClassReceiver);
23942399
///@}
23952400

23962401
//

clang/lib/Sema/SemaAvailability.cpp

+18-11
Original file line numberDiff line numberDiff line change
@@ -90,10 +90,9 @@ static const AvailabilityAttr *getAttrForPlatform(ASTContext &Context,
9090
/// the availability attribute that is selected.
9191
/// \param ClassReceiver If we're checking the method of a class message
9292
/// send, the class. Otherwise nullptr.
93-
static std::pair<AvailabilityResult, const NamedDecl *>
94-
ShouldDiagnoseAvailabilityOfDecl(Sema &S, const NamedDecl *D,
95-
std::string *Message,
96-
ObjCInterfaceDecl *ClassReceiver) {
93+
std::pair<AvailabilityResult, const NamedDecl *>
94+
Sema::ShouldDiagnoseAvailabilityOfDecl(const NamedDecl *D, std::string *Message,
95+
ObjCInterfaceDecl *ClassReceiver) {
9796
AvailabilityResult Result = D->getAvailability(Message);
9897

9998
// For typedefs, if the typedef declaration appears available look
@@ -147,12 +146,12 @@ ShouldDiagnoseAvailabilityOfDecl(Sema &S, const NamedDecl *D,
147146

148147
// For +new, infer availability from -init.
149148
if (const auto *MD = dyn_cast<ObjCMethodDecl>(D)) {
150-
if (S.ObjC().NSAPIObj && ClassReceiver) {
149+
if (ObjC().NSAPIObj && ClassReceiver) {
151150
ObjCMethodDecl *Init = ClassReceiver->lookupInstanceMethod(
152-
S.ObjC().NSAPIObj->getInitSelector());
151+
ObjC().NSAPIObj->getInitSelector());
153152
if (Init && Result == AR_Available && MD->isClassMethod() &&
154-
MD->getSelector() == S.ObjC().NSAPIObj->getNewSelector() &&
155-
MD->definedInNSObject(S.getASTContext())) {
153+
MD->getSelector() == ObjC().NSAPIObj->getNewSelector() &&
154+
MD->definedInNSObject(getASTContext())) {
156155
Result = Init->getAvailability(Message);
157156
D = Init;
158157
}
@@ -162,7 +161,6 @@ ShouldDiagnoseAvailabilityOfDecl(Sema &S, const NamedDecl *D,
162161
return {Result, D};
163162
}
164163

165-
166164
/// whether we should emit a diagnostic for \c K and \c DeclVersion in
167165
/// the context of \c Ctx. For example, we should emit an unavailable diagnostic
168166
/// in a deprecated context, but not the other way around.
@@ -876,7 +874,7 @@ void DiagnoseUnguardedAvailability::DiagnoseDeclAvailability(
876874
AvailabilityResult Result;
877875
const NamedDecl *OffendingDecl;
878876
std::tie(Result, OffendingDecl) =
879-
ShouldDiagnoseAvailabilityOfDecl(SemaRef, D, nullptr, ReceiverClass);
877+
SemaRef.ShouldDiagnoseAvailabilityOfDecl(D, nullptr, ReceiverClass);
880878
if (Result != AR_Available) {
881879
// All other diagnostic kinds have already been handled in
882880
// DiagnoseAvailabilityOfDecl.
@@ -1112,12 +1110,13 @@ void Sema::DiagnoseAvailabilityOfDecl(NamedDecl *D,
11121110
bool ObjCPropertyAccess,
11131111
bool AvoidPartialAvailabilityChecks,
11141112
ObjCInterfaceDecl *ClassReceiver) {
1113+
11151114
std::string Message;
11161115
AvailabilityResult Result;
11171116
const NamedDecl* OffendingDecl;
11181117
// See if this declaration is unavailable, deprecated, or partial.
11191118
std::tie(Result, OffendingDecl) =
1120-
ShouldDiagnoseAvailabilityOfDecl(*this, D, &Message, ClassReceiver);
1119+
ShouldDiagnoseAvailabilityOfDecl(D, &Message, ClassReceiver);
11211120
if (Result == AR_Available)
11221121
return;
11231122

@@ -1146,3 +1145,11 @@ void Sema::DiagnoseAvailabilityOfDecl(NamedDecl *D,
11461145
EmitAvailabilityWarning(*this, Result, D, OffendingDecl, Message, Locs,
11471146
UnknownObjCClass, ObjCPDecl, ObjCPropertyAccess);
11481147
}
1148+
1149+
void Sema::DiagnoseAvailabilityOfDecl(NamedDecl *D,
1150+
ArrayRef<SourceLocation> Locs) {
1151+
DiagnoseAvailabilityOfDecl(D, Locs, /*UnknownObjCClass=*/nullptr,
1152+
/*ObjCPropertyAccess=*/false,
1153+
/*AvoidPartialAvailabilityChecks=*/false,
1154+
/*ClassReceiver=*/nullptr);
1155+
}

clang/lib/Sema/SemaTemplateInstantiate.cpp

+17-1
Original file line numberDiff line numberDiff line change
@@ -4094,16 +4094,32 @@ bool Sema::InstantiateClassTemplateSpecialization(
40944094
if (ClassTemplateSpec->isInvalidDecl())
40954095
return true;
40964096

4097+
bool HadAvaibilityWarning =
4098+
ShouldDiagnoseAvailabilityOfDecl(ClassTemplateSpec, nullptr, nullptr)
4099+
.first != AR_Available;
4100+
40974101
ActionResult<CXXRecordDecl *> Pattern =
40984102
getPatternForClassTemplateSpecialization(*this, PointOfInstantiation,
40994103
ClassTemplateSpec, TSK,
41004104
PrimaryStrictPackMatch);
4105+
41014106
if (!Pattern.isUsable())
41024107
return Pattern.isInvalid();
41034108

4104-
return InstantiateClass(
4109+
bool Err = InstantiateClass(
41054110
PointOfInstantiation, ClassTemplateSpec, Pattern.get(),
41064111
getTemplateInstantiationArgs(ClassTemplateSpec), TSK, Complain);
4112+
4113+
// If we haven't already warn on avaibility, consider the avaibility
4114+
// attributes of the partial specialization.
4115+
// Note that - because we need to have deduced the partial specialization -
4116+
// We can only emit these warnings when the specialization is instantiated.
4117+
if (!Err && !HadAvaibilityWarning) {
4118+
assert(ClassTemplateSpec->getTemplateSpecializationKind() !=
4119+
TSK_Undeclared);
4120+
DiagnoseAvailabilityOfDecl(ClassTemplateSpec, PointOfInstantiation);
4121+
}
4122+
return Err;
41074123
}
41084124

41094125
void

clang/test/CXX/dcl.dcl/dcl.attr/dcl.attr.deprecated/p1.cpp

+68-1
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ template <typename T> struct [[deprecated]] B;//expected-note {{'B<int>' has bee
4545
B<int> *q2; // expected-warning {{'B<int>' is deprecated}}
4646
B<float> *r2; // expected-warning {{'B<float>' is deprecated}}
4747

48-
template <typename T>
48+
template <typename T>
4949
T some_func(T t) {
5050
struct [[deprecated]] FunS{}; // expected-note {{'FunS' has been explicitly marked deprecated here}}
5151
FunS f;// expected-warning {{'FunS' is deprecated}}
@@ -72,3 +72,70 @@ template <class B1> struct B {
7272

7373
template struct B<A>; // expected-note {{requested here}}
7474
} // namespace GH58547
75+
76+
77+
namespace GH44496 {
78+
79+
80+
template <typename T>
81+
class function { };
82+
template <typename A>
83+
class __attribute__((deprecated)) function<void(A)> { };
84+
// expected-note@-1 {{'function<void (int)>' has been explicitly marked deprecated here}}
85+
86+
void test() {
87+
[[maybe_unused]] function<void(int)> f; // expected-warning{{'function<void (int)>' is deprecated}}
88+
}
89+
90+
template <class> struct my_template {
91+
using type = void;
92+
};
93+
94+
template <class T>
95+
struct [[deprecated("primary")]] deprecated { // #deprecated-primary-marked-here
96+
using type = T;
97+
};
98+
99+
template <class T>
100+
struct my_template<volatile T> : deprecated<T> {}; // #deprecated-primary-base
101+
102+
template <class T>
103+
struct [[deprecated("specialization")]] my_template<const T> : deprecated<const T> {}; // #my_template-explicit-here
104+
105+
106+
template <class T> using my_template_t = typename my_template<T>::type; // #deprecated-my-template-alias
107+
108+
// We cannot warn on X because no instantiation has taken place yet
109+
using X = my_template<volatile int>;
110+
111+
// Because we already warn on the attribute on the plimary template, we ignore the attribute on the specialization
112+
using Y = my_template_t<const int>;
113+
// expected-warning@#deprecated-primary-base {{'deprecated<int>' is deprecated: primary}} \
114+
// expected-note@-1 {{in instantiation of template type alias}} \
115+
// expected-note@#deprecated-primary-marked-here {{has been explicitly marked deprecated here}}
116+
117+
using Z = my_template_t<volatile int>;
118+
// expected-warning@#deprecated-my-template-alias {{'my_template<const int>' is deprecated: specialization}} \
119+
// expected-note@#my_template-explicit-here {{'my_template<const int>' has been explicitly marked deprecated here}} \
120+
// expected-note@#deprecated-my-template-alias {{in instantiation of template class 'GH44496::my_template<volatile int>' requested here}} \
121+
// expected-note@-1 {{in instantiation of template type alias 'my_template_t' requested here}}
122+
123+
template <class T>
124+
struct primary_not_deprecated {
125+
using type = T;
126+
};
127+
template <class T>
128+
struct [[deprecated("specialization")]] primary_not_deprecated<volatile T> : deprecated<T> {};
129+
// expected-note@-1 {{'primary_not_deprecated<volatile int>' has been explicitly marked deprecated here}}
130+
131+
// We cannot warn on S1 because no instantiation has taken place yet
132+
using S1 = primary_not_deprecated<volatile int>;
133+
134+
135+
using S2 = primary_not_deprecated<volatile int>;
136+
137+
X x;
138+
Z z;
139+
S2 s2;
140+
// expected-warning@-1 {{'primary_not_deprecated<volatile int>' is deprecated: specialization}}
141+
}

0 commit comments

Comments
 (0)