llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT--> @llvm/pr-subscribers-clang Author: Nikolas Klauser (philnik777) <details> <summary>Changes</summary> 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). --- Full diff: https://github.com/llvm/llvm-project/pull/101469.diff 7 Files Affected: - (modified) clang/include/clang/Basic/Attr.td (+12-1) - (modified) clang/include/clang/Basic/AttrDocs.td (+11-3) - (modified) clang/include/clang/Basic/DiagnosticGroups.td (+1) - (modified) clang/include/clang/Basic/DiagnosticSemaKinds.td (+3) - (modified) clang/lib/Sema/SemaDeclAttr.cpp (+9) - (modified) clang/lib/Sema/SemaTemplate.cpp (+6) - (added) clang/test/SemaCXX/attr-diagnose-specializations.cpp (+34) ``````````diff 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>; `````````` </details> https://github.com/llvm/llvm-project/pull/101469 _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits