[clang] [Clang] Add [[clang::no_specializations]] (PR #101469)
Nikolas Klauser via cfe-commits
cfe-commits at lists.llvm.org
Fri Nov 22 15:13:27 PST 2024
https://github.com/philnik777 updated https://github.com/llvm/llvm-project/pull/101469
>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 1/7] [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 8ac2079099c854..e074cc8b285a95 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 94c284fc731589..4ca67a63714d4b 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 19c3f1e0433496..d6f6111f708684 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 581434d33c5c9a..5972d630347ec4 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 9011fa547638e5..eb0a705fef0449 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 c22e329bef2b90..ddcc3272cff1ff 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 00000000000000..bf9ea2c56c18a6
--- /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>;
>From 9d1b83b509acf7525281cb400136071177f2c397 Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Thu, 1 Aug 2024 14:59:03 +0200
Subject: [PATCH 2/7] Address comments
---
clang/include/clang/Basic/AttrDocs.td | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td
index 4ca67a63714d4b..876902f1a276fb 100644
--- a/clang/include/clang/Basic/AttrDocs.td
+++ b/clang/include/clang/Basic/AttrDocs.td
@@ -978,9 +978,9 @@ 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.
+``[[clang::diagnose_specializations]]`` can be appied to class or variable
+templates which should not be specialized by users. This is primarily used to
+diagnose user specializations of standard library type traits.
}];
}
>From 328874bce6725cd09f7a99e08ec63046727439ad Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Thu, 1 Aug 2024 15:15:24 +0200
Subject: [PATCH 3/7] Fix typo
---
clang/include/clang/Basic/AttrDocs.td | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td
index 876902f1a276fb..393a4b68ad4b2c 100644
--- a/clang/include/clang/Basic/AttrDocs.td
+++ b/clang/include/clang/Basic/AttrDocs.td
@@ -978,7 +978,7 @@ 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 or variable
+``[[clang::diagnose_specializations]]`` can be applied to class or variable
templates which should not be specialized by users. This is primarily used to
diagnose user specializations of standard library type traits.
}];
>From dce818ff483085a019f141318335ffdf0ab0a4ee Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Fri, 2 Aug 2024 09:37:01 +0200
Subject: [PATCH 4/7] Address comments
---
clang/docs/ReleaseNotes.rst | 7 ++++++-
clang/include/clang/Basic/DiagnosticSemaKinds.td | 4 ++--
clang/lib/Sema/SemaTemplate.cpp | 4 ++--
clang/test/SemaCXX/attr-diagnose-specializations.cpp | 8 ++++----
4 files changed, 14 insertions(+), 9 deletions(-)
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 3c2e0282d1c72d..1190607b98c608 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -124,13 +124,18 @@ 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::diagnose_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.
+
Improvements to Clang's diagnostics
-----------------------------------
- Some template related diagnostics have been improved.
.. code-block:: c++
-
+
void foo() { template <typename> int i; } // error: templates can only be declared in namespace or class scope
struct S {
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 5972d630347ec4..4d91b6671186dd 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -5407,8 +5407,8 @@ 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">,
+def warn_invalid_specialization : Warning<
+ "%0 should not be specialized">,
DefaultError, InGroup<InvalidSpecialization>;
// C++ class template specializations and out-of-line definitions
diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
index ddcc3272cff1ff..027ed9140d726c 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -3977,7 +3977,7 @@ DeclResult Sema::ActOnVarTemplateSpecialization(
}
if (VarTemplate->hasAttr<DiagnoseSpecializationsAttr>())
- Diag(TemplateNameLoc, diag::warn_diag_specialization);
+ Diag(TemplateNameLoc, diag::warn_invalid_specialization) << VarTemplate;
// Check for unexpanded parameter packs in any of the template arguments.
for (unsigned I = 0, N = TemplateArgs.size(); I != N; ++I)
@@ -8089,7 +8089,7 @@ DeclResult Sema::ActOnClassTemplateSpecialization(
}
if (ClassTemplate->hasAttr<DiagnoseSpecializationsAttr>())
- Diag(TemplateNameLoc, diag::warn_diag_specialization);
+ Diag(TemplateNameLoc, diag::warn_invalid_specialization) << ClassTemplate;
bool isMemberSpecialization = false;
bool isPartialSpecialization = false;
diff --git a/clang/test/SemaCXX/attr-diagnose-specializations.cpp b/clang/test/SemaCXX/attr-diagnose-specializations.cpp
index bf9ea2c56c18a6..a2d4021f947482 100644
--- a/clang/test/SemaCXX/attr-diagnose-specializations.cpp
+++ b/clang/test/SemaCXX/attr-diagnose-specializations.cpp
@@ -12,13 +12,13 @@ struct [[clang::diagnose_specializations]] is_same {
};
template <>
-struct is_same<int, char> {}; // expected-error {{specializing a template which should not be specialized}}
+struct is_same<int, char> {}; // expected-error {{'is_same' 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}}
+struct is_same<Template<T>, Template <T>> {}; // expected-error {{'is_same' should not be specialized}}
bool test_instantiation1 = is_same<int, int>::value;
@@ -26,9 +26,9 @@ 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}}
+inline constexpr bool is_same_v<int, char> = false; // expected-error {{'is_same_v' 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}}
+inline constexpr bool is_same_v<Template <T>, Template <T>> = true; // expected-error {{'is_same_v' should not be specialized}}
bool test_instantiation2 = is_same_v<int, int>;
>From 919835cc70311451a27547bc29182847993b8631 Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Sat, 10 Aug 2024 12:27:57 +0200
Subject: [PATCH 5/7] Add an optional message argument
---
clang/include/clang/Basic/Attr.td | 1 +
.../clang/Basic/DiagnosticSemaKinds.td | 5 ++++-
clang/lib/Sema/SemaDeclAttr.cpp | 5 ++++-
clang/lib/Sema/SemaTemplate.cpp | 20 +++++++++++++----
.../SemaCXX/attr-diagnose-specializations.cpp | 22 +++++++++++++++----
5 files changed, 43 insertions(+), 10 deletions(-)
diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td
index e074cc8b285a95..412883c7df4730 100644
--- a/clang/include/clang/Basic/Attr.td
+++ b/clang/include/clang/Basic/Attr.td
@@ -3332,6 +3332,7 @@ def DiagnoseIf : InheritableAttr {
def DiagnoseSpecializations : InheritableAttr {
let Spellings = [Clang<"diagnose_specializations", /*AllowInC*/0>];
+ let Args = [StringArgument<"Message", 1>];
let Subjects = SubjectList<[ClassTmpl, VarTmpl]>;
let Documentation = [DiagnoseSpecializationsDocs];
let MeaningfulToClassTemplateDefinition = 1;
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 4d91b6671186dd..75046a8eedcccb 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -5408,7 +5408,10 @@ def note_dependent_function_template_spec_discard_reason : Note<
"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 should not be specialized">,
+ "%0 cannot be specialized">,
+ DefaultError, InGroup<InvalidSpecialization>;
+def warn_invalid_specialization_message : Warning<
+ "%0 cannot be specialized: %1">,
DefaultError, InGroup<InvalidSpecialization>;
// C++ class template specializations and out-of-line definitions
diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp
index eb0a705fef0449..442d9562df3fa5 100644
--- a/clang/lib/Sema/SemaDeclAttr.cpp
+++ b/clang/lib/Sema/SemaDeclAttr.cpp
@@ -1217,8 +1217,11 @@ static void handlePreferredName(Sema &S, Decl *D, const ParsedAttr &AL) {
static void handleDiagnoseSpecializations(Sema &S, Decl *D,
const ParsedAttr &AL) {
+ StringRef Message;
+ if (AL.getNumArgs() != 0)
+ S.checkStringLiteralArgumentAttr(AL, 0, Message);
D->getDescribedTemplate()->addAttr(
- DiagnoseSpecializationsAttr::Create(S.Context, AL));
+ DiagnoseSpecializationsAttr::Create(S.Context, Message, AL));
}
bool Sema::isValidPointerAttrType(QualType T, bool RefOkay) {
diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
index 027ed9140d726c..a8ad65fb1b6fed 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -3976,8 +3976,14 @@ DeclResult Sema::ActOnVarTemplateSpecialization(
<< IsPartialSpecialization;
}
- if (VarTemplate->hasAttr<DiagnoseSpecializationsAttr>())
- Diag(TemplateNameLoc, diag::warn_invalid_specialization) << VarTemplate;
+ if (const auto *DSA = VarTemplate->getAttr<DiagnoseSpecializationsAttr>()) {
+ if (auto Message = DSA->getMessage(); !Message.empty()) {
+ Diag(TemplateNameLoc, diag::warn_invalid_specialization_message)
+ << VarTemplate << Message;
+ } else {
+ Diag(TemplateNameLoc, diag::warn_invalid_specialization) << VarTemplate;
+ }
+ }
// Check for unexpanded parameter packs in any of the template arguments.
for (unsigned I = 0, N = TemplateArgs.size(); I != N; ++I)
@@ -8088,8 +8094,14 @@ DeclResult Sema::ActOnClassTemplateSpecialization(
return true;
}
- if (ClassTemplate->hasAttr<DiagnoseSpecializationsAttr>())
- Diag(TemplateNameLoc, diag::warn_invalid_specialization) << ClassTemplate;
+ if (const auto *DSA = ClassTemplate->getAttr<DiagnoseSpecializationsAttr>()) {
+ if (auto Message = DSA->getMessage(); !Message.empty()) {
+ Diag(TemplateNameLoc, diag::warn_invalid_specialization_message)
+ << ClassTemplate << Message;
+ } else {
+ Diag(TemplateNameLoc, diag::warn_invalid_specialization) << ClassTemplate;
+ }
+ }
bool isMemberSpecialization = false;
bool isPartialSpecialization = false;
diff --git a/clang/test/SemaCXX/attr-diagnose-specializations.cpp b/clang/test/SemaCXX/attr-diagnose-specializations.cpp
index a2d4021f947482..7dfdf6fdd1a5b4 100644
--- a/clang/test/SemaCXX/attr-diagnose-specializations.cpp
+++ b/clang/test/SemaCXX/attr-diagnose-specializations.cpp
@@ -12,13 +12,13 @@ struct [[clang::diagnose_specializations]] is_same {
};
template <>
-struct is_same<int, char> {}; // expected-error {{'is_same' should not be specialized}}
+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' should not be specialized}}
+struct is_same<Template<T>, Template <T>> {}; // expected-error {{'is_same' cannot be specialized}}
bool test_instantiation1 = is_same<int, int>::value;
@@ -26,9 +26,23 @@ 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 {{'is_same_v' should not be specialized}}
+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' should not be specialized}}
+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::diagnose_specializations("specializing type traits results in undefined behaviour")]] is_trivial {
+ 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::diagnose_specializations("specializing type traits results in undefined behaviour")]] inline constexpr bool is_trivial_v = __is_trivial(T);
+
+template <>
+inline constexpr bool is_trivial_v<int> = false; // expected-error {{'is_trivial_v' cannot be specialized: specializing type traits results in undefined behaviour}}
>From 003983f992e05357ddf338e757f53d0e4c9e5959 Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Sun, 25 Aug 2024 23:26:23 +0200
Subject: [PATCH 6/7] Address comments
---
clang/docs/ReleaseNotes.rst | 2 +-
clang/include/clang/Basic/Attr.td | 6 +++---
clang/include/clang/Basic/AttrDocs.td | 20 +++++++++---------
.../clang/Basic/DiagnosticSemaKinds.td | 6 ++----
clang/lib/Sema/SemaTemplate.cpp | 20 +++++++-----------
.../SemaCXX/attr-diagnose-specializations.cpp | 21 +++++++++++++------
6 files changed, 39 insertions(+), 36 deletions(-)
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 38a2e34a9a3f4c..c18aadb5bfa361 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -210,7 +210,7 @@ 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::diagnose_specializations]]`` has been added to warn
+- 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.
diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td
index 7d19fb1577388b..06fecbec8d55f5 100644
--- a/clang/include/clang/Basic/Attr.td
+++ b/clang/include/clang/Basic/Attr.td
@@ -104,7 +104,7 @@ def NonLocalVar : SubsetSubject<Var,
[{!S->hasLocalStorage()}],
"variables with non-local storage">;
def VarTmpl : SubsetSubject<Var, [{S->getDescribedVarTemplate()}],
- "variable template">;
+ "variable templates">;
def NonBitField : SubsetSubject<Field,
[{!S->isBitField()}],
@@ -3340,7 +3340,7 @@ def DiagnoseIf : InheritableAttr {
}
def DiagnoseSpecializations : InheritableAttr {
- let Spellings = [Clang<"diagnose_specializations", /*AllowInC*/0>];
+ let Spellings = [Clang<"no_specializations", /*AllowInC*/0>];
let Args = [StringArgument<"Message", 1>];
let Subjects = SubjectList<[ClassTmpl, VarTmpl]>;
let Documentation = [DiagnoseSpecializationsDocs];
@@ -4628,7 +4628,7 @@ def HLSLResource : InheritableAttr {
def HLSLROV : InheritableAttr {
let Spellings = [CXX11<"hlsl", "is_rov">];
let Subjects = SubjectList<[Struct]>;
- let LangOpts = [HLSL];
+ let LangOpts = [HLSL];
let Documentation = [InternalOnly];
}
diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td
index 27bc60731fc321..22f157cdf9530c 100644
--- a/clang/include/clang/Basic/AttrDocs.td
+++ b/clang/include/clang/Basic/AttrDocs.td
@@ -978,9 +978,9 @@ Query for this feature with ``__has_attribute(diagnose_if)``.
def DiagnoseSpecializationsDocs : Documentation {
let Category = DocCatDecl;
let Content = [{
-``[[clang::diagnose_specializations]]`` can be applied to class or variable
-templates which should not be specialized by users. This is primarily used to
-diagnose user specializations of standard library type traits.
+``[[clang::no_specializations]]`` can be applied to 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.
}];
}
@@ -6839,8 +6839,8 @@ the field it is attached to, and it may also lead to emission of automatic fix-i
hints which would help the user replace the use of unsafe functions(/fields) with safe
alternatives, though the attribute can be used even when the fix can't be automated.
-* Attribute attached to functions: The attribute does not suppress
- ``-Wunsafe-buffer-usage`` inside the function to which it is attached.
+* Attribute attached to functions: The attribute does not suppress
+ ``-Wunsafe-buffer-usage`` inside the function to which it is attached.
These warnings still need to be addressed.
The attribute is warranted even if the only way a function can overflow
@@ -6903,10 +6903,10 @@ alternatives, though the attribute can be used even when the fix can't be automa
and then use the attribute on the original ``baz()`` to help the users
update their code to use the new function.
-* Attribute attached to fields: The attribute should only be attached to
- struct fields, if the fields can not be updated to a safe type with bounds
- check, such as std::span. In other words, the buffers prone to unsafe accesses
- should always be updated to use safe containers/views and attaching the attribute
+* Attribute attached to fields: The attribute should only be attached to
+ struct fields, if the fields can not be updated to a safe type with bounds
+ check, such as std::span. In other words, the buffers prone to unsafe accesses
+ should always be updated to use safe containers/views and attaching the attribute
must be last resort when such an update is infeasible.
The attribute can be placed on individual fields or a set of them as shown below.
@@ -6924,7 +6924,7 @@ alternatives, though the attribute can be used even when the fix can't be automa
size_t sz;
};
- Here, every read/write to the fields ptr1, ptr2, buf and sz will trigger a warning
+ Here, every read/write to the fields ptr1, ptr2, buf and sz will trigger a warning
that the field has been explcitly marked as unsafe due to unsafe-buffer operations.
}];
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 44f580ade10626..b112b4a60d57f5 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -5417,11 +5417,9 @@ def note_dependent_function_template_spec_discard_reason : Note<
"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">,
- DefaultError, InGroup<InvalidSpecialization>;
-def warn_invalid_specialization_message : Warning<
- "%0 cannot be specialized: %1">,
+ "%0 cannot be specialized%select{|: %2}1">,
DefaultError, InGroup<InvalidSpecialization>;
+def note_marked_here : Note<"marked %0 here">;
// C++ class template specializations and out-of-line definitions
def err_template_spec_needs_header : Error<
diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
index 70999ec86e7699..d6852be05ee66e 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -3980,12 +3980,10 @@ DeclResult Sema::ActOnVarTemplateSpecialization(
}
if (const auto *DSA = VarTemplate->getAttr<DiagnoseSpecializationsAttr>()) {
- if (auto Message = DSA->getMessage(); !Message.empty()) {
- Diag(TemplateNameLoc, diag::warn_invalid_specialization_message)
- << VarTemplate << Message;
- } else {
- Diag(TemplateNameLoc, diag::warn_invalid_specialization) << VarTemplate;
- }
+ 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.
@@ -8099,12 +8097,10 @@ DeclResult Sema::ActOnClassTemplateSpecialization(
}
if (const auto *DSA = ClassTemplate->getAttr<DiagnoseSpecializationsAttr>()) {
- if (auto Message = DSA->getMessage(); !Message.empty()) {
- Diag(TemplateNameLoc, diag::warn_invalid_specialization_message)
- << ClassTemplate << Message;
- } else {
- Diag(TemplateNameLoc, diag::warn_invalid_specialization) << ClassTemplate;
- }
+ auto Message = DSA->getMessage();
+ Diag(TemplateNameLoc, diag::warn_invalid_specialization)
+ << ClassTemplate << !Message.empty() << Message;
+ Diag(DSA->getLoc(), diag::note_marked_here) << DSA;
}
DeclContext *DC = ClassTemplate->getDeclContext();
diff --git a/clang/test/SemaCXX/attr-diagnose-specializations.cpp b/clang/test/SemaCXX/attr-diagnose-specializations.cpp
index 7dfdf6fdd1a5b4..ba8ec6bea58e69 100644
--- a/clang/test/SemaCXX/attr-diagnose-specializations.cpp
+++ b/clang/test/SemaCXX/attr-diagnose-specializations.cpp
@@ -1,16 +1,19 @@
// RUN: %clang_cc1 %s -verify
-#if !__has_cpp_attribute(clang::diagnose_specializations)
+#if !__has_cpp_attribute(clang::no_specializations)
# error
#endif
-struct [[clang::diagnose_specializations]] S {}; // expected-warning {{'diagnose_specializations' attribute only applies to class templates}}
+struct [[clang::no_specializations]] S {}; // expected-warning {{'no_specializations' attribute only applies to class templates and variable templates}}
template <class T, class U>
-struct [[clang::diagnose_specializations]] is_same {
+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 and variable templates}}
+
template <>
struct is_same<int, char> {}; // expected-error {{'is_same' cannot be specialized}}
@@ -23,7 +26,7 @@ struct is_same<Template<T>, Template <T>> {}; // expected-error {{'is_same' cann
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);
+[[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}}
@@ -34,7 +37,7 @@ inline constexpr bool is_same_v<Template <T>, Template <T>> = true; // expected-
bool test_instantiation2 = is_same_v<int, int>;
template <class T>
-struct [[clang::diagnose_specializations("specializing type traits results in undefined behaviour")]] is_trivial {
+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);
};
@@ -42,7 +45,13 @@ template <>
struct is_trivial<int> {}; // expected-error {{'is_trivial' cannot be specialized: specializing type traits results in undefined behaviour}}
template <class T>
-[[clang::diagnose_specializations("specializing type traits results in undefined behaviour")]] inline constexpr bool is_trivial_v = __is_trivial(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 and variable templates}}
>From 87ae233df143277ebcf20399a00859c46e22b95a Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Sat, 23 Nov 2024 00:12:51 +0100
Subject: [PATCH 7/7] Address comments
---
clang/include/clang/Basic/AttrDocs.td | 2 +-
clang/lib/Sema/SemaDeclAttr.cpp | 3 +--
2 files changed, 2 insertions(+), 3 deletions(-)
diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td
index 36f74986496514..09442cbe03b55d 100644
--- a/clang/include/clang/Basic/AttrDocs.td
+++ b/clang/include/clang/Basic/AttrDocs.td
@@ -984,7 +984,7 @@ Query for this feature with ``__has_attribute(diagnose_if)``.
def NoSpecializationsDocs : Documentation {
let Category = DocCatDecl;
let Content = [{
-``[[clang::no_specializations]]`` can be applied to class or variable
+``[[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.
}];
diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp
index b8d9a3ebe84c60..ad5a6f51bd8971 100644
--- a/clang/lib/Sema/SemaDeclAttr.cpp
+++ b/clang/lib/Sema/SemaDeclAttr.cpp
@@ -1215,8 +1215,7 @@ static void handlePreferredName(Sema &S, Decl *D, const ParsedAttr &AL) {
<< TT->getDecl();
}
-static void handleNoSpecializations(Sema &S, Decl *D,
- const ParsedAttr &AL) {
+static void handleNoSpecializations(Sema &S, Decl *D, const ParsedAttr &AL) {
StringRef Message;
if (AL.getNumArgs() != 0)
S.checkStringLiteralArgumentAttr(AL, 0, Message);
More information about the cfe-commits
mailing list