mirror of
https://github.com/llvm/llvm-project.git
synced 2025-04-18 19:06:44 +00:00
[Clang] Add [[clang::no_specializations]] (#101469)
This can be used to inform users when a template should not be specialized. For example, this is the case for the standard type traits (except for `common_type` and `common_reference`, which have more complicated rules).
This commit is contained in:
parent
71648a4ef9
commit
0604d13790
@ -473,6 +473,11 @@ Attribute Changes in Clang
|
||||
- The ``hybrid_patchable`` attribute is now supported on ARM64EC targets. It can be used to specify
|
||||
that a function requires an additional x86-64 thunk, which may be patched at runtime.
|
||||
|
||||
- The attribute ``[[clang::no_specializations]]`` has been added to warn
|
||||
users that a specific template shouldn't be specialized. This is useful for
|
||||
e.g. standard library type traits, where adding a specialization results in
|
||||
undefined behaviour.
|
||||
|
||||
- ``[[clang::lifetimebound]]`` is now explicitly disallowed on explicit object member functions
|
||||
where they were previously silently ignored.
|
||||
|
||||
|
@ -103,6 +103,9 @@ def NonParmVar : SubsetSubject<Var,
|
||||
def NonLocalVar : SubsetSubject<Var,
|
||||
[{!S->hasLocalStorage()}],
|
||||
"variables with non-local storage">;
|
||||
def VarTmpl : SubsetSubject<Var, [{S->getDescribedVarTemplate()}],
|
||||
"variable templates">;
|
||||
|
||||
def NonBitField : SubsetSubject<Field,
|
||||
[{!S->isBitField()}],
|
||||
"non-bit-field non-static data members">;
|
||||
@ -3428,6 +3431,15 @@ def DiagnoseIf : InheritableAttr {
|
||||
let Documentation = [DiagnoseIfDocs];
|
||||
}
|
||||
|
||||
def NoSpecializations : InheritableAttr {
|
||||
let Spellings = [Clang<"no_specializations", /*AllowInC*/0>];
|
||||
let Args = [StringArgument<"Message", 1>];
|
||||
let Subjects = SubjectList<[ClassTmpl, FunctionTmpl, VarTmpl]>;
|
||||
let Documentation = [NoSpecializationsDocs];
|
||||
let MeaningfulToClassTemplateDefinition = 1;
|
||||
let TemplateDependent = 1;
|
||||
}
|
||||
|
||||
def ArcWeakrefUnavailable : InheritableAttr {
|
||||
let Spellings = [Clang<"objc_arc_weak_reference_unavailable">];
|
||||
let Subjects = SubjectList<[ObjCInterface], ErrorDiag>;
|
||||
|
@ -1155,6 +1155,15 @@ Query for this feature with ``__has_attribute(diagnose_if)``.
|
||||
}];
|
||||
}
|
||||
|
||||
def NoSpecializationsDocs : Documentation {
|
||||
let Category = DocCatDecl;
|
||||
let Content = [{
|
||||
``[[clang::no_specializations]]`` can be applied to function, class, or variable
|
||||
templates which should not be explicitly specialized by users. This is primarily
|
||||
used to diagnose user specializations of standard library type traits.
|
||||
}];
|
||||
}
|
||||
|
||||
def PassObjectSizeDocs : Documentation {
|
||||
let Category = DocCatVariable; // Technically it's a parameter doc, but eh.
|
||||
let Heading = "pass_object_size, pass_dynamic_object_size";
|
||||
|
@ -1589,4 +1589,3 @@ def ExplicitSpecializationStorageClass : DiagGroup<"explicit-specialization-stor
|
||||
|
||||
// A warning for options that enable a feature that is not yet complete
|
||||
def ExperimentalOption : DiagGroup<"experimental-option">;
|
||||
|
||||
|
@ -5445,6 +5445,10 @@ def note_dependent_function_template_spec_discard_reason : Note<
|
||||
"candidate ignored: %select{not a function template|"
|
||||
"not a member of the enclosing %select{class template|"
|
||||
"namespace; did you mean to explicitly qualify the specialization?}1}0">;
|
||||
def warn_invalid_specialization : Warning<
|
||||
"%0 cannot be specialized%select{|: %2}1">,
|
||||
DefaultError, InGroup<DiagGroup<"invalid-specialization">>;
|
||||
def note_marked_here : Note<"marked %0 here">;
|
||||
|
||||
// C++ class template specializations and out-of-line definitions
|
||||
def err_template_spec_needs_header : Error<
|
||||
|
@ -1212,6 +1212,14 @@ static void handlePreferredName(Sema &S, Decl *D, const ParsedAttr &AL) {
|
||||
<< TT->getDecl();
|
||||
}
|
||||
|
||||
static void handleNoSpecializations(Sema &S, Decl *D, const ParsedAttr &AL) {
|
||||
StringRef Message;
|
||||
if (AL.getNumArgs() != 0)
|
||||
S.checkStringLiteralArgumentAttr(AL, 0, Message);
|
||||
D->getDescribedTemplate()->addAttr(
|
||||
NoSpecializationsAttr::Create(S.Context, Message, AL));
|
||||
}
|
||||
|
||||
bool Sema::isValidPointerAttrType(QualType T, bool RefOkay) {
|
||||
if (T->isDependentType())
|
||||
return true;
|
||||
@ -6913,6 +6921,9 @@ ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, const ParsedAttr &AL,
|
||||
case ParsedAttr::AT_PreferredName:
|
||||
handlePreferredName(S, D, AL);
|
||||
break;
|
||||
case ParsedAttr::AT_NoSpecializations:
|
||||
handleNoSpecializations(S, D, AL);
|
||||
break;
|
||||
case ParsedAttr::AT_Section:
|
||||
handleSectionAttr(S, D, AL);
|
||||
break;
|
||||
|
@ -4157,6 +4157,13 @@ DeclResult Sema::ActOnVarTemplateSpecialization(
|
||||
<< IsPartialSpecialization;
|
||||
}
|
||||
|
||||
if (const auto *DSA = VarTemplate->getAttr<NoSpecializationsAttr>()) {
|
||||
auto Message = DSA->getMessage();
|
||||
Diag(TemplateNameLoc, diag::warn_invalid_specialization)
|
||||
<< VarTemplate << !Message.empty() << Message;
|
||||
Diag(DSA->getLoc(), diag::note_marked_here) << DSA;
|
||||
}
|
||||
|
||||
// Check for unexpanded parameter packs in any of the template arguments.
|
||||
for (unsigned I = 0, N = TemplateArgs.size(); I != N; ++I)
|
||||
if (DiagnoseUnexpandedParameterPack(TemplateArgs[I],
|
||||
@ -8291,6 +8298,13 @@ DeclResult Sema::ActOnClassTemplateSpecialization(
|
||||
return true;
|
||||
}
|
||||
|
||||
if (const auto *DSA = ClassTemplate->getAttr<NoSpecializationsAttr>()) {
|
||||
auto Message = DSA->getMessage();
|
||||
Diag(TemplateNameLoc, diag::warn_invalid_specialization)
|
||||
<< ClassTemplate << !Message.empty() << Message;
|
||||
Diag(DSA->getLoc(), diag::note_marked_here) << DSA;
|
||||
}
|
||||
|
||||
if (S->isTemplateParamScope())
|
||||
EnterTemplatedContext(S, ClassTemplate->getTemplatedDecl());
|
||||
|
||||
@ -9175,6 +9189,14 @@ bool Sema::CheckFunctionTemplateSpecialization(
|
||||
// Ignore access information; it doesn't figure into redeclaration checking.
|
||||
FunctionDecl *Specialization = cast<FunctionDecl>(*Result);
|
||||
|
||||
if (const auto *PT = Specialization->getPrimaryTemplate();
|
||||
const auto *DSA = PT->getAttr<NoSpecializationsAttr>()) {
|
||||
auto Message = DSA->getMessage();
|
||||
Diag(FD->getLocation(), diag::warn_invalid_specialization)
|
||||
<< PT << !Message.empty() << Message;
|
||||
Diag(DSA->getLoc(), diag::note_marked_here) << DSA;
|
||||
}
|
||||
|
||||
// C++23 [except.spec]p13:
|
||||
// An exception specification is considered to be needed when:
|
||||
// - [...]
|
||||
|
62
clang/test/SemaCXX/attr-no-specializations.cpp
Normal file
62
clang/test/SemaCXX/attr-no-specializations.cpp
Normal file
@ -0,0 +1,62 @@
|
||||
// RUN: %clang_cc1 %s -verify
|
||||
|
||||
#if !__has_cpp_attribute(clang::no_specializations)
|
||||
# error
|
||||
#endif
|
||||
|
||||
struct [[clang::no_specializations]] S {}; // expected-warning {{'no_specializations' attribute only applies to class templates, function templates, and variable templates}}
|
||||
|
||||
template <class T, class U>
|
||||
struct [[clang::no_specializations]] is_same { // expected-note 2 {{marked 'no_specializations' here}}
|
||||
static constexpr bool value = __is_same(T, U);
|
||||
};
|
||||
|
||||
template <class T>
|
||||
using alias [[clang::no_specializations]] = T; // expected-warning {{'no_specializations' attribute only applies to class templates, function templates, and variable templates}}
|
||||
|
||||
template <>
|
||||
struct is_same<int, char> {}; // expected-error {{'is_same' cannot be specialized}}
|
||||
|
||||
template <class>
|
||||
struct Template {};
|
||||
|
||||
template <class T>
|
||||
struct is_same<Template<T>, Template <T>> {}; // expected-error {{'is_same' cannot be specialized}}
|
||||
|
||||
bool test_instantiation1 = is_same<int, int>::value;
|
||||
|
||||
template <class T, class U>
|
||||
[[clang::no_specializations]] inline constexpr bool is_same_v = __is_same(T, U); // expected-note 2 {{marked 'no_specializations' here}}
|
||||
|
||||
template <>
|
||||
inline constexpr bool is_same_v<int, char> = false; // expected-error {{'is_same_v' cannot be specialized}}
|
||||
|
||||
template <class T>
|
||||
inline constexpr bool is_same_v<Template <T>, Template <T>> = true; // expected-error {{'is_same_v' cannot be specialized}}
|
||||
|
||||
bool test_instantiation2 = is_same_v<int, int>;
|
||||
|
||||
template <class T>
|
||||
struct [[clang::no_specializations("specializing type traits results in undefined behaviour")]] is_trivial { // expected-note {{marked 'no_specializations' here}}
|
||||
static constexpr bool value = __is_trivial(T);
|
||||
};
|
||||
|
||||
template <>
|
||||
struct is_trivial<int> {}; // expected-error {{'is_trivial' cannot be specialized: specializing type traits results in undefined behaviour}}
|
||||
|
||||
template <class T>
|
||||
[[clang::no_specializations("specializing type traits results in undefined behaviour")]] inline constexpr bool is_trivial_v = __is_trivial(T); // expected-note {{marked 'no_specializations' here}}
|
||||
|
||||
template <>
|
||||
inline constexpr bool is_trivial_v<int> = false; // expected-error {{'is_trivial_v' cannot be specialized: specializing type traits results in undefined behaviour}}
|
||||
|
||||
template <class T>
|
||||
struct Partial {};
|
||||
|
||||
template <class T>
|
||||
struct [[clang::no_specializations]] Partial<Template <T>> {}; // expected-warning {{'no_specializations' attribute only applies to class templates, function templates, and variable templates}}
|
||||
|
||||
template <class T>
|
||||
[[clang::no_specializations]] void func(); // expected-note {{marked 'no_specializations' here}}
|
||||
|
||||
template <> void func<int>(); // expected-error {{'func' cannot be specialized}}
|
Loading…
x
Reference in New Issue
Block a user