[clang] [Clang] Add [[clang::diagnose_specializations]] (PR #101469)
Nikolas Klauser via cfe-commits
cfe-commits at lists.llvm.org
Thu Aug 1 03:37:59 PDT 2024
https://github.com/philnik777 created https://github.com/llvm/llvm-project/pull/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).
>From 82ab798fc72c6de64ae527d96393f0dc67307e98 Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Thu, 1 Aug 2024 12:30:22 +0200
Subject: [PATCH] [Clang] Add [[clang::diagnose_specializations]]
---
clang/include/clang/Basic/Attr.td | 13 ++++++-
clang/include/clang/Basic/AttrDocs.td | 14 ++++++--
clang/include/clang/Basic/DiagnosticGroups.td | 1 +
.../clang/Basic/DiagnosticSemaKinds.td | 3 ++
clang/lib/Sema/SemaDeclAttr.cpp | 9 +++++
clang/lib/Sema/SemaTemplate.cpp | 6 ++++
.../SemaCXX/attr-diagnose-specializations.cpp | 34 +++++++++++++++++++
7 files changed, 76 insertions(+), 4 deletions(-)
create mode 100644 clang/test/SemaCXX/attr-diagnose-specializations.cpp
diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td
index 8ac2079099c85..e074cc8b285a9 100644
--- a/clang/include/clang/Basic/Attr.td
+++ b/clang/include/clang/Basic/Attr.td
@@ -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 template">;
+
def NonBitField : SubsetSubject<Field,
[{!S->isBitField()}],
"non-bit-field non-static data members">;
@@ -3327,6 +3330,14 @@ def DiagnoseIf : InheritableAttr {
let Documentation = [DiagnoseIfDocs];
}
+def DiagnoseSpecializations : InheritableAttr {
+ let Spellings = [Clang<"diagnose_specializations", /*AllowInC*/0>];
+ let Subjects = SubjectList<[ClassTmpl, VarTmpl]>;
+ let Documentation = [DiagnoseSpecializationsDocs];
+ let MeaningfulToClassTemplateDefinition = 1;
+ let TemplateDependent = 1;
+}
+
def ArcWeakrefUnavailable : InheritableAttr {
let Spellings = [Clang<"objc_arc_weak_reference_unavailable">];
let Subjects = SubjectList<[ObjCInterface], ErrorDiag>;
@@ -4581,7 +4592,7 @@ def HLSLResource : InheritableAttr {
let Spellings = [];
let Subjects = SubjectList<[Struct]>;
let LangOpts = [HLSL];
- let Args = [
+ let Args = [
EnumArgument<
"ResourceKind", "llvm::hlsl::ResourceKind",
/*is_string=*/0,
diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td
index 94c284fc73158..4ca67a63714d4 100644
--- a/clang/include/clang/Basic/AttrDocs.td
+++ b/clang/include/clang/Basic/AttrDocs.td
@@ -975,6 +975,15 @@ Query for this feature with ``__has_attribute(diagnose_if)``.
}];
}
+def DiagnoseSpecializationsDocs : Documentation {
+ let Category = DocCatDecl;
+ let Content = [{
+``clang::diagnose_specializations`` can be appied to class templates which
+should not be 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";
@@ -7388,10 +7397,10 @@ def HLSLLoopHintDocs : Documentation {
let Content = [{
The ``[loop]`` directive allows loop optimization hints to be
specified for the subsequent loop. The directive allows unrolling to
-be disabled and is not compatible with [unroll(x)].
+be disabled and is not compatible with [unroll(x)].
Specifying the parameter, ``[loop]``, directs the
-unroller to not unroll the loop.
+unroller to not unroll the loop.
.. code-block:: hlsl
@@ -8306,4 +8315,3 @@ Declares that a function potentially allocates heap memory, and prevents any pot
of ``nonallocating`` by the compiler.
}];
}
-
diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td
index 19c3f1e043349..d6f6111f70868 100644
--- a/clang/include/clang/Basic/DiagnosticGroups.td
+++ b/clang/include/clang/Basic/DiagnosticGroups.td
@@ -472,6 +472,7 @@ def ExpansionToDefined : DiagGroup<"expansion-to-defined">;
def FlagEnum : DiagGroup<"flag-enum">;
def IncrementBool : DiagGroup<"increment-bool", [DeprecatedIncrementBool]>;
def InfiniteRecursion : DiagGroup<"infinite-recursion">;
+def InvalidSpecialization : DiagGroup<"invalid-specialization">;
def PureVirtualCallFromCtorDtor: DiagGroup<"call-to-pure-virtual-from-ctor-dtor">;
def GNUImaginaryConstant : DiagGroup<"gnu-imaginary-constant">;
def IgnoredGCH : DiagGroup<"ignored-gch">;
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 581434d33c5c9..5972d630347ec 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -5407,6 +5407,9 @@ 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_diag_specialization : Warning<
+ "specializing a template which should not be specialized">,
+ DefaultError, InGroup<InvalidSpecialization>;
// C++ class template specializations and out-of-line definitions
def err_template_spec_needs_header : Error<
diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp
index 9011fa547638e..eb0a705fef044 100644
--- a/clang/lib/Sema/SemaDeclAttr.cpp
+++ b/clang/lib/Sema/SemaDeclAttr.cpp
@@ -1215,6 +1215,12 @@ static void handlePreferredName(Sema &S, Decl *D, const ParsedAttr &AL) {
<< TT->getDecl();
}
+static void handleDiagnoseSpecializations(Sema &S, Decl *D,
+ const ParsedAttr &AL) {
+ D->getDescribedTemplate()->addAttr(
+ DiagnoseSpecializationsAttr::Create(S.Context, AL));
+}
+
bool Sema::isValidPointerAttrType(QualType T, bool RefOkay) {
if (RefOkay) {
if (T->isReferenceType())
@@ -6700,6 +6706,9 @@ ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, const ParsedAttr &AL,
case ParsedAttr::AT_PreferredName:
handlePreferredName(S, D, AL);
break;
+ case ParsedAttr::AT_DiagnoseSpecializations:
+ handleDiagnoseSpecializations(S, D, AL);
+ break;
case ParsedAttr::AT_Section:
handleSectionAttr(S, D, AL);
break;
diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
index c22e329bef2b9..ddcc3272cff1f 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -3976,6 +3976,9 @@ DeclResult Sema::ActOnVarTemplateSpecialization(
<< IsPartialSpecialization;
}
+ if (VarTemplate->hasAttr<DiagnoseSpecializationsAttr>())
+ Diag(TemplateNameLoc, diag::warn_diag_specialization);
+
// 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],
@@ -8085,6 +8088,9 @@ DeclResult Sema::ActOnClassTemplateSpecialization(
return true;
}
+ if (ClassTemplate->hasAttr<DiagnoseSpecializationsAttr>())
+ Diag(TemplateNameLoc, diag::warn_diag_specialization);
+
bool isMemberSpecialization = false;
bool isPartialSpecialization = false;
diff --git a/clang/test/SemaCXX/attr-diagnose-specializations.cpp b/clang/test/SemaCXX/attr-diagnose-specializations.cpp
new file mode 100644
index 0000000000000..bf9ea2c56c18a
--- /dev/null
+++ b/clang/test/SemaCXX/attr-diagnose-specializations.cpp
@@ -0,0 +1,34 @@
+// RUN: %clang_cc1 %s -verify
+
+#if !__has_cpp_attribute(clang::diagnose_specializations)
+# error
+#endif
+
+struct [[clang::diagnose_specializations]] S {}; // expected-warning {{'diagnose_specializations' attribute only applies to class templates}}
+
+template <class T, class U>
+struct [[clang::diagnose_specializations]] is_same {
+ static constexpr bool value = __is_same(T, U);
+};
+
+template <>
+struct is_same<int, char> {}; // expected-error {{specializing a template which should not be specialized}}
+
+template <class>
+struct Template {};
+
+template <class T>
+struct is_same<Template<T>, Template <T>> {}; // expected-error {{specializing a template which should not be specialized}}
+
+bool test_instantiation1 = is_same<int, int>::value;
+
+template <class T, class U>
+[[clang::diagnose_specializations]] inline constexpr bool is_same_v = __is_same(T, U);
+
+template <>
+inline constexpr bool is_same_v<int, char> = false; // expected-error {{specializing a template which should not be specialized}}
+
+template <class T>
+inline constexpr bool is_same_v<Template <T>, Template <T>> = true; // expected-error {{specializing a template which should not be specialized}}
+
+bool test_instantiation2 = is_same_v<int, int>;
More information about the cfe-commits
mailing list