https://gcc.gnu.org/g:3f14efbe47b0607ca9c0fac6de7345733b084be6
commit r17-2362-g3f14efbe47b0607ca9c0fac6de7345733b084be6 Author: Jakub Jelinek <[email protected]> Date: Mon Jul 13 18:33:37 2026 +0200 c++: Implement clang::no_specializations attribute and -Winvalid-specialization warning [PR120635] The following patch implements the clang::no_specializations attribute (in all of [[clang::no_specializations]] [[clang::no_specializations ("reason")]] __attribute__((no_specializations)) __attribute__((no_specializations ("reason"))) forms), which can appertain to class, variable and function templates (though, not to their specializations), and if the attribute is present on the template, there is an error (that is what clang++ implements, not warning) if one attempts to partially or fully specialize it. The error can be disabled either with -Wno-invalid-specialization on the command line or #pragma GCC diagnostic ignored "-Winvalid-specialization" around the specialization or turned into warning instead of error with -Wno-error=invalid-specialization. This is intended mainly for libstdc++, so that it can diagnose various nits in the standard like https://cplusplus.github.io/LWG/issue3990 etc. 2026-07-13 Jakub Jelinek <[email protected]> PR c++/120635 gcc/ * doc/extend.texi (no_specializations): Document new attribute. * doc/invoke.texi (-Wno-invalid-specialization): Document new warning. gcc/c-family/ * c.opt (Winvalid-specialization): New option. * c.opt.urls: Regenerate. * c-lex.cc (c_common_has_attribute): Don't advertise gnu::no_specializations attribute availability. gcc/cp/ * tree.cc (cxx_gnu_attributes): Add "no_specializations" entry. (cxx_clang_attributes): Likewise. (handle_no_specializations_attribute, handle_gnu_no_specializations_attribute): New functions. * decl2.cc (is_late_template_attribute): Return false for "no_specializations" attribute, even on dependent types. Don't differentiate between the completely unknown and dependent cases, for the 5 template attributes return always false, for others true if dependent type and false otherwise. * parser.cc (cp_parser_std_attribute): Use uneval_string_attr even for clang::no_specializations attribute. * pt.cc: Include "escaped_string.h". (maybe_diagnose_no_specializations): New function. (maybe_process_partial_specialization): Use it. (check_explicit_specialization): Likewise. gcc/testsuite/ * g++.dg/ext/attr-no_specializations1.C: New test. * g++.dg/ext/attr-no_specializations2.C: New test. * g++.dg/ext/attr-no_specializations3.C: New test. * g++.dg/ext/attr-no_specializations4.C: New test. * g++.dg/ext/attr-no_specializations5.C: New test. * g++.dg/ext/attr-no_specializations6.C: New test. * g++.dg/ext/attr-no_specializations7.C: New test. * g++.dg/ext/attr-no_specializations8.C: New test. * g++.dg/ext/attr-no_specializations9.C: New test. * g++.dg/ext/attr-no_specializations10.C: New test. * g++.dg/ext/attr-no_specializations11.C: New test. Reviewed-by: Jason Merrill <[email protected]> Diff: --- gcc/c-family/c-lex.cc | 6 +- gcc/c-family/c.opt | 4 + gcc/c-family/c.opt.urls | 3 + gcc/cp/decl2.cc | 23 ++- gcc/cp/parser.cc | 4 + gcc/cp/pt.cc | 51 +++++++ gcc/cp/tree.cc | 87 +++++++++++ gcc/doc/extend.texi | 35 +++++ gcc/doc/invoke.texi | 11 +- .../g++.dg/ext/attr-no_specializations1.C | 102 +++++++++++++ .../g++.dg/ext/attr-no_specializations10.C | 22 +++ .../g++.dg/ext/attr-no_specializations11.C | 11 ++ .../g++.dg/ext/attr-no_specializations2.C | 102 +++++++++++++ .../g++.dg/ext/attr-no_specializations3.C | 108 ++++++++++++++ .../g++.dg/ext/attr-no_specializations4.C | 108 ++++++++++++++ .../g++.dg/ext/attr-no_specializations5.C | 160 +++++++++++++++++++++ .../g++.dg/ext/attr-no_specializations6.C | 75 ++++++++++ .../g++.dg/ext/attr-no_specializations7.C | 54 +++++++ .../g++.dg/ext/attr-no_specializations8.C | 31 ++++ .../g++.dg/ext/attr-no_specializations9.C | 31 ++++ 20 files changed, 1011 insertions(+), 17 deletions(-) diff --git a/gcc/c-family/c-lex.cc b/gcc/c-family/c-lex.cc index 3f3e9f30aa4e..53c215ca9444 100644 --- a/gcc/c-family/c-lex.cc +++ b/gcc/c-family/c-lex.cc @@ -405,10 +405,12 @@ c_common_has_attribute (cpp_reader *pfile, bool std_syntax) attr_name = NULL_TREE; /* gnu::trivial_abi is in the attribute table just to error on it and suggest using [[clang::trivial_abi]] - or __attribute__((trivial_abi)). Don't advertise it. */ + or __attribute__((trivial_abi)). Don't advertise it. + Ditto for gnu::no_specializations. */ else if (c_dialect_cxx () && is_attribute_p ("gnu", attr_ns) - && is_attribute_p ("trivial_abi", attr_id)) + && (is_attribute_p ("trivial_abi", attr_id) + || is_attribute_p ("no_specializations", attr_id))) attr_name = NULL_TREE; else attr_name = build_tree_list (attr_ns, attr_id); diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt index 8bad323a4378..0260b9dbf42d 100644 --- a/gcc/c-family/c.opt +++ b/gcc/c-family/c.opt @@ -967,6 +967,10 @@ Winvalid-pch C ObjC C++ ObjC++ CPP(warn_invalid_pch) CppReason(CPP_W_INVALID_PCH) Var(cpp_warn_invalid_pch) Init(0) Warning Warn about PCH files that are found but not used. +Winvalid-specialization +C++ ObjC++ Var(warn_invalid_specialization) Init(1) Warning +Error about specializations of templates with \"clang::no_specializations\" attribute. + Winvalid-utf8 C ObjC C++ ObjC++ CPP(cpp_warn_invalid_utf8) CppReason(CPP_W_INVALID_UTF8) Var(warn_invalid_utf8) Init(0) Warning Warn about invalid UTF-8 characters. diff --git a/gcc/c-family/c.opt.urls b/gcc/c-family/c.opt.urls index 27a5f68acb38..47e6c58f495c 100644 --- a/gcc/c-family/c.opt.urls +++ b/gcc/c-family/c.opt.urls @@ -682,6 +682,9 @@ UrlSuffix(gcc/C_002b_002b-Dialect-Options.html#index-Winvalid-offsetof) Winvalid-pch UrlSuffix(gcc/Warning-Options.html#index-Winvalid-pch) +Winvalid-specialization +UrlSuffix(gcc/C_002b_002b-Dialect-Options.html#index-Winvalid-specialization) + Winvalid-utf8 UrlSuffix(gcc/Warning-Options.html#index-Winvalid-utf8) diff --git a/gcc/cp/decl2.cc b/gcc/cp/decl2.cc index 6e84b58e0b7f..f3ca6cb7bdba 100644 --- a/gcc/cp/decl2.cc +++ b/gcc/cp/decl2.cc @@ -1751,21 +1751,16 @@ is_late_template_attribute (tree attr, tree decl) if (!type) return true; - /* We can't apply any attributes to a completely unknown type until - instantiation time. */ - enum tree_code code = TREE_CODE (type); - if (code == TEMPLATE_TYPE_PARM - || code == BOUND_TEMPLATE_TEMPLATE_PARM - || code == TYPENAME_TYPE) - return true; - /* Also defer most attributes on dependent types. This is not + /* Some attributes specifically apply to templates. */ + if (is_attribute_p ("abi_tag", name) + || is_attribute_p ("deprecated", name) + || is_attribute_p ("unavailable", name) + || is_attribute_p ("visibility", name) + || is_attribute_p ("no_specializations", name)) + return false; + /* Defer most attributes on dependent types. This is not necessary in all cases, but is the better default. */ - else if (dependent_type_p (type) - /* But some attributes specifically apply to templates. */ - && !is_attribute_p ("abi_tag", name) - && !is_attribute_p ("deprecated", name) - && !is_attribute_p ("unavailable", name) - && !is_attribute_p ("visibility", name)) + else if (dependent_type_p (type)) return true; else return false; diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc index b57acc6cba59..d53f74126ee1 100644 --- a/gcc/cp/parser.cc +++ b/gcc/cp/parser.cc @@ -33424,6 +33424,10 @@ cp_parser_std_attribute (cp_parser *parser, tree attr_ns) && (is_attribute_p ("deprecated", attr_id) || is_attribute_p ("nodiscard", attr_id))) attr_flag = uneval_string_attr; + else if (attr_ns + && is_attribute_p ("clang", attr_ns) + && is_attribute_p ("no_specializations", attr_id)) + attr_flag = uneval_string_attr; /* If this is a fake attribute created to handle -Wno-attributes, we must skip parsing the arguments. */ diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc index d571d6918afb..1fe94a863102 100644 --- a/gcc/cp/pt.cc +++ b/gcc/cp/pt.cc @@ -50,6 +50,7 @@ along with GCC; see the file COPYING3. If not see #include "omp-general.h" #include "pretty-print-markup.h" #include "contracts.h" +#include "escaped_string.h" /* The type of functions taking a tree, and some additional data, and returning an int. */ @@ -1031,6 +1032,48 @@ maybe_new_partial_specialization (tree& type) return false; } +/* Diagnose if SPEC is a specialization of [[clang::no_specializations]] + template. */ + +static void +maybe_diagnose_no_specializations (tree spec) +{ + tree tmpl; + if (TREE_CODE (spec) == TYPE_DECL) + tmpl = CLASSTYPE_TI_TEMPLATE (TREE_TYPE (spec)); + else + tmpl = DECL_TI_TEMPLATE (spec); + tree t = DECL_TEMPLATE_RESULT (tmpl), attr; + if (TREE_CODE (t) == TYPE_DECL) + attr = lookup_attribute ("no_specializations", + TYPE_ATTRIBUTES (TREE_TYPE (t))); + else + attr = lookup_attribute ("no_specializations", DECL_ATTRIBUTES (t)); + if (!attr) + return; + + auto_diagnostic_group d; + escaped_string msg; + tree args = TREE_VALUE (attr); + bool complained; + if (args) + msg.escape (TREE_STRING_POINTER (TREE_VALUE (args))); + if (msg) + complained + = permerror_opt (DECL_SOURCE_LOCATION (spec), + OPT_Winvalid_specialization, + "%qD cannot be specialized: %qs", + spec, (const char *) msg); + else + complained + = permerror_opt (DECL_SOURCE_LOCATION (spec), + OPT_Winvalid_specialization, + "%qD cannot be specialized", spec); + if (complained) + inform (DECL_SOURCE_LOCATION (DECL_TEMPLATE_RESULT (tmpl)), + "declared %qs here", "clang::no_specializations"); +} + /* The TYPE is being declared. If it is a template type, that means it is a partial specialization. Do appropriate error-checking. */ @@ -1091,6 +1134,7 @@ maybe_process_partial_specialization (tree type) return error_mark_node; SET_CLASSTYPE_TEMPLATE_SPECIALIZATION (type); DECL_SOURCE_LOCATION (TYPE_MAIN_DECL (type)) = input_location; + maybe_diagnose_no_specializations (TYPE_NAME (type)); if (processing_template_decl) { tree decl = push_template_decl (TYPE_MAIN_DECL (type)); @@ -1203,6 +1247,7 @@ maybe_process_partial_specialization (tree type) DECL_SOURCE_LOCATION (TYPE_MAIN_DECL (type)) = input_location; CLASSTYPE_TI_ARGS (type) = INNERMOST_TEMPLATE_ARGS (CLASSTYPE_TI_ARGS (type)); + maybe_diagnose_no_specializations (TYPE_NAME (type)); } } else if (processing_specialization) @@ -3454,6 +3499,12 @@ check_explicit_specialization (tree declarator, || DECL_DESTRUCTOR_P (decl)) || DECL_CLONED_FUNCTION_P (DECL_CHAIN (decl))); } + + if (decl + && VAR_OR_FUNCTION_DECL_P (decl) + && DECL_LANG_SPECIFIC (decl) + && DECL_TEMPLATE_SPECIALIZATION (decl)) + maybe_diagnose_no_specializations (decl); } return decl; diff --git a/gcc/cp/tree.cc b/gcc/cp/tree.cc index 59629b09fa52..e39a4a59b9af 100644 --- a/gcc/cp/tree.cc +++ b/gcc/cp/tree.cc @@ -50,6 +50,10 @@ static tree handle_no_dangling_attribute (tree *, tree, tree, int, bool *); static tree handle_annotation_attribute (tree *, tree, tree, int, bool *); static tree handle_trivial_abi_attribute (tree *, tree, tree, int, bool *); static tree handle_gnu_trivial_abi_attribute (tree *, tree, tree, int, bool *); +static tree handle_no_specializations_attribute (tree *, tree, tree, int, + bool *); +static tree handle_gnu_no_specializations_attribute (tree *, tree, tree, int, + bool *); /* If REF is an lvalue, returns the kind of lvalue that REF is. Otherwise, returns clk_none. */ @@ -5620,6 +5624,8 @@ static const attribute_spec cxx_gnu_attributes[] = handle_abi_tag_attribute, NULL }, { "no_dangling", 0, 1, false, true, false, false, handle_no_dangling_attribute, NULL }, + { "no_specializations", 0, 1, false, false, false, false, + handle_gnu_no_specializations_attribute, NULL }, { "trivial_abi", 0, 0, false, true, false, true, handle_gnu_trivial_abi_attribute, NULL }, }; @@ -5678,6 +5684,8 @@ const scoped_attribute_specs internal_attribute_table = // clang-format off static const attribute_spec cxx_clang_attributes[] = { + { "no_specializations", 0, 1, false, false, false, false, + handle_no_specializations_attribute, NULL }, { "trivial_abi", 0, 0, false, true, false, true, handle_trivial_abi_attribute, NULL }, }; @@ -6086,6 +6094,85 @@ has_trivial_abi_attribute (tree type) return lookup_attribute ("trivial_abi", TYPE_ATTRIBUTES (type)); } +/* Handle a "no_specializations" attribute applied via + [[gnu::no_specializations]]. + We reject that spelling; suggest [[clang::no_specializations]] or + __attribute__((no_specializations)) instead. */ + +static tree +handle_gnu_no_specializations_attribute (tree *node, tree name, tree args, + int flags, bool *no_add_attrs) +{ + if (flags & ATTR_FLAG_CXX11) + { + warning (OPT_Wattributes, + "%<[[gnu::no_specializations]]%> is not supported; use " + "%<[[clang::no_specializations]]%> or " + "%<__attribute__((no_specializations))%> instead"); + *no_add_attrs = true; + return NULL_TREE; + } + return handle_no_specializations_attribute (node, name, args, flags, + no_add_attrs); +} + +/* Handle a "no_specializations" attribute. */ + +static tree +handle_no_specializations_attribute (tree *node, tree name, tree args, int, + bool *no_add_attrs) +{ + if (args && TREE_CODE (TREE_VALUE (args)) != STRING_CST) + { + error ("%qE attribute argument must be a string constant", name); + *no_add_attrs = true; + return NULL_TREE; + } + /* Only allow on class templates, variable templates + and function templates. */ + if (!CLASS_TYPE_P (*node) && !VAR_OR_FUNCTION_DECL_P (*node)) + { + invalid: + warning (OPT_Wattributes, "%qE attribute only applies to class, " + "function or variable templates", name); + *no_add_attrs = true; + return NULL_TREE; + } + tree ti = NULL_TREE; + if (TYPE_P (*node)) + { + if (!TYPE_NAME (*node) + || !DECL_IMPLICIT_TYPEDEF_P (TYPE_NAME (*node)) + || LAMBDA_TYPE_P (*node)) + goto invalid; + if (TYPE_LANG_SPECIFIC (*node)) + { + if (CLASSTYPE_TEMPLATE_SPECIALIZATION (*node) + || CLASSTYPE_EXPLICIT_INSTANTIATION (*node)) + goto invalid; + ti = CLASSTYPE_TEMPLATE_INFO (*node); + } + } + else if (DECL_LANG_SPECIFIC (*node)) + { + if (DECL_TEMPLATE_SPECIALIZATION (*node) + || DECL_EXPLICIT_INSTANTIATION (*node)) + goto invalid; + ti = DECL_TEMPLATE_INFO (*node); + } + if (ti) + { + if (!PRIMARY_TEMPLATE_P (TI_TEMPLATE (ti))) + goto invalid; + if (TYPE_P (*node) && DECL_ALIAS_TEMPLATE_P (TI_TEMPLATE (ti))) + goto invalid; + } + else if (!processing_template_decl || !template_parm_scope_p ()) + goto invalid; + + return NULL_TREE; +} + /* Validate the trivial_abi attribute on a completed class type. Called from finish_struct after the class is complete. */ diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi index a49772c5a57d..60fc5add3bcd 100644 --- a/gcc/doc/extend.texi +++ b/gcc/doc/extend.texi @@ -31662,6 +31662,41 @@ arrays of any of the above @end itemize @end itemize +@cindex @code{no_specializations} template attribute +@item no_specializations + +The @code{no_specializations} attribute can be applied to a C++ +class template, variable template or function template, though not +their specializations. It instructs the compiler to error if +such a template is explicitly specialized. The primary use case +for this attribute is the standard library where the standard +specifies it is undefined behavior to specialize some template. +The error can be disabled with @option{-Wno-invalid-specialization} +or @code{#pragma GCC ignored "-Winvalid-specialization"}. + +The attribute can be specified either as @code{__attribute__ +((no_specializations))} or @code{[[clang::no_specializations]]} for compatibility +with the original implementation in Clang. To avoid portability +complications, @code{[[gnu::no_specializations]]} is ignored with a warning. + +The attribute has a single optional argument, a string literal, which is +included in the diagnostics and can provide reason why some template should +not be specialized. + +@smallexample +template <typename T> +struct __attribute__ ((__no_specializations__)) A @{@}; +template <typename T, typename U> +[[clang::no_specializations]] int b = 1; +template <typename T> +[[__clang__::__no_specializations__ ("foo cannot be specialized")]] int +foo () @{ return 42; @} +template <> struct A <int> @{@}; // Error +template <typename T> int b <T, T> = 2; // Error +template <> int foo <int> () @{ return 43; @} // Error +@}; +@end smallexample + @end table @node Function Multiversioning diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi index 038b169d90e6..2e21053dae96 100644 --- a/gcc/doc/invoke.texi +++ b/gcc/doc/invoke.texi @@ -280,7 +280,7 @@ in the following sections. -Wextra-semi -Wno-global-module -Wno-inaccessible-base -Wno-inherited-variadic-ctor -Wno-init-list-lifetime -Winvalid-constexpr -Winvalid-imported-macros --Wno-invalid-offsetof -Wno-literal-suffix +-Wno-invalid-offsetof -Wno-invalid-specialization -Wno-literal-suffix -Wmismatched-new-delete -Wmismatched-tags -Wmultiple-inheritance -Wnamespaces -Wnarrowing -Wnoexcept -Wnoexcept-type -Wnon-virtual-dtor @@ -4304,6 +4304,15 @@ compilation. This is not enabled by default, as it requires additional processing to determine. It may be useful when preparing sets of header-units to ensure consistent macros. +@opindex Winvalid-specialization +@opindex Wno-invalid-specialization +@item -Wno-invalid-specialization @r{(C++ and Objective-C++ only)} +Do not error on explicit specializations of templates marked with the +@code{no_specializations} attribute. This diagnostics is enabled +by default, and @option{-Wno-invalid-specialization} or +@code{#pragma GCC ignored "-Winvalid-specializations"} can disable +that diagnostics. + @opindex Wliteral-suffix @opindex Wno-literal-suffix @item -Wno-literal-suffix @r{(C++ and Objective-C++ only)} diff --git a/gcc/testsuite/g++.dg/ext/attr-no_specializations1.C b/gcc/testsuite/g++.dg/ext/attr-no_specializations1.C new file mode 100644 index 000000000000..69dee86d3826 --- /dev/null +++ b/gcc/testsuite/g++.dg/ext/attr-no_specializations1.C @@ -0,0 +1,102 @@ +// PR c++/120635 +// { dg-do compile { target c++11 } } + +template <typename T, typename U> +struct [[clang::no_specializations]] A {}; // { dg-message "declared 'clang::no_specializations' here" } +#if __cpp_variable_templates >= 201304 +template <typename T, typename U> +[[clang::no_specializations]] int b = 1; // { dg-message "declared 'clang::no_specializations' here" "" { target c++14 } } +#endif +template <typename T, typename U> +[[__clang__::__no_specializations__]] int foo () { return 0; } // { dg-message "declared 'clang::no_specializations' here" } +template <typename T> +struct B { + struct [[clang::no_specializations]] C {}; // { dg-warning "'no_specializations' attribute only applies to class, function or variable templates" } + [[clang::no_specializations]] static int b; // { dg-warning "'no_specializations' attribute only applies to class, function or variable templates" } + [[clang::no_specializations]] int foo () { return 0; }// { dg-warning "'no_specializations' attribute only applies to class, function or variable templates" } + template <typename U> + class [[clang::no_specializations]] D {}; +}; +A <int, long> c; +#if __cpp_variable_templates >= 201304 +int d = b <int, long>; +#endif +int e = foo <int, long> (); +template <typename T> +struct D {}; +template <> +struct A <int, int> { int a; }; // { dg-error "'struct A<int, int>' cannot be specialized" } +template <typename T> +struct A <long, T> { long b; }; // { dg-error "'struct A<long int, T>' cannot be specialized" } +template <typename T> +struct A <D <T>, D <T>> { D<T> c; }; // { dg-error "'struct A<D<T>, D<T> >' cannot be specialized" } +#if __cpp_variable_templates >= 201304 +template <> +int b <int, int> = 2; // { dg-error "'b<int, int>' cannot be specialized" "" { target c++14 } } +template <typename T> +int b <long, T> = 3; // { dg-error "'b<long int, T>' cannot be specialized" "" { target c++14 } } +template <typename T> +int b <D <T>, D <T>> = 4; // { dg-error "'b<D<T>, D<T> >' cannot be specialized" "" { target c++14 } } +#endif +template <> +int foo <int, int> () { return 1; } // { dg-error "'int foo\\\(\\\) \\\[with T = int; U = int\\\]' cannot be specialized" } +struct [[clang::no_specializations]] E {}; // { dg-warning "'no_specializations' attribute only applies to class, function or variable templates" } +template <typename T> +using F [[clang::no_specializations]] = T; // { dg-warning "'no_specializations' attribute only applies to class, function or variable templates" } +[[clang::no_specializations]] int f = 1; // { dg-warning "'no_specializations' attribute only applies to class, function or variable templates" } +[[clang::no_specializations]] int bar () { return 0; } // { dg-warning "'no_specializations' attribute only applies to class, function or variable templates" } +template <typename T, typename U> +struct G {}; +#if __cpp_variable_templates >= 201304 +template <typename T, typename U> +int g = 1; +#endif +template <typename T, typename U> +int baz () { return 0; } +template <> +struct [[clang::no_specializations]] G <int, int> { int a; }; // { dg-warning "'no_specializations' attribute only applies to class, function or variable templates" } +template <typename T> +struct [[clang::no_specializations]] G <long, T> { long b; }; // { dg-warning "'no_specializations' attribute only applies to class, function or variable templates" } +template <typename T> +struct [[clang::no_specializations]] G <D <T>, D <T>> { D<T> c; }; // { dg-warning "'no_specializations' attribute only applies to class, function or variable templates" } +#if __cpp_variable_templates >= 201304 +template <> +[[clang::no_specializations]] int g <int, int> = 2; // { dg-warning "'no_specializations' attribute only applies to class, function or variable templates" "" { target c++14 } } +template <typename T> +[[clang::no_specializations]] int g <long, T> = 3; // { dg-warning "'no_specializations' attribute only applies to class, function or variable templates" "" { target c++14 } } +template <typename T> +[[clang::no_specializations]] int g <D <T>, D <T>> = 4; // { dg-warning "'no_specializations' attribute only applies to class, function or variable templates" "" { target c++14 } } +#endif +template <> +[[clang::no_specializations]] int baz <int, int> () { return 1; } // { dg-warning "'no_specializations' attribute only applies to class, function or variable templates" } +template <typename T> +using H [[clang::no_specializations]] = int; // { dg-warning "'no_specializations' attribute only applies to class, function or variable templates" } +template struct A <double, double>; +extern template struct A <float, float>; +#if __cpp_variable_templates >= 201304 +template int b <double, double>; +extern template int b <float, float>; +#endif +template int foo <double, double> (); +extern template int foo <float, float> (); +template <typename T> +struct D2 {}; +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Winvalid-specialization" +template <> +struct A <short, int> { int a; }; // { dg-bogus "'struct A<int, int>' cannot be specialized" } +template <typename T> +struct A <long long, T> { long b; }; // { dg-bogus "'struct A<long int, T>' cannot be specialized" } +template <typename T> +struct A <D2 <T>, D2 <T>> { D2<T> c; }; // { dg-bogus "'struct A<D<T>, D<T> >' cannot be specialized" } +#if __cpp_variable_templates >= 201304 +template <> +constexpr int b <short, int> = 2; // { dg-bogus "'b<short, int>' cannot be specialized" } +template <typename T> +constexpr int b <long long, T> = 3; // { dg-bogus "'b<long long int, T>' cannot be specialized" } +template <typename T> +constexpr int b <D2 <T>, D2 <T>> = 4; // { dg-bogus "'b<D2<T>, D2<T> >' cannot be specialized" } +#endif +template <> +int foo <short, int> () { return 1; } // { dg-bogus "'int foo\\\(\\\) \\\[with T = short; U = int\\\]' cannot be specialized" } +#pragma GCC diagnostic pop diff --git a/gcc/testsuite/g++.dg/ext/attr-no_specializations10.C b/gcc/testsuite/g++.dg/ext/attr-no_specializations10.C new file mode 100644 index 000000000000..fc3de8969f10 --- /dev/null +++ b/gcc/testsuite/g++.dg/ext/attr-no_specializations10.C @@ -0,0 +1,22 @@ +// PR c++/120635 +// { dg-do preprocess { target c++11 } } + +#if __has_cpp_attribute (clang::no_specializations) != 1 +#error +#endif +#if __has_cpp_attribute (gnu::no_specializations) != 0 +#error +#endif +#if __has_cpp_attribute (no_specializations) != 0 +#error This fails for now +// { dg-bogus "This fails for now" "" { xfail *-*-* } .-1 } +#endif +#if __has_attribute (clang::no_specializations) != 1 +#error +#endif +#if __has_attribute (gnu::no_specializations) != 0 +#error +#endif +#if __has_attribute (no_specializations) != 1 +#error +#endif diff --git a/gcc/testsuite/g++.dg/ext/attr-no_specializations11.C b/gcc/testsuite/g++.dg/ext/attr-no_specializations11.C new file mode 100644 index 000000000000..4bb42adb18e4 --- /dev/null +++ b/gcc/testsuite/g++.dg/ext/attr-no_specializations11.C @@ -0,0 +1,11 @@ +// PR c++/120635 +// { dg-do compile { target c++11 } } + +template <typename T, typename U> +struct [[gnu::no_specializations]] A {}; // { dg-warning "'\\\[\\\[gnu::no_specializations\\\]\\\]' is not supported; use '\\\[\\\[clang::no_specializations\\\]\\\]' or '__attribute__\\\(\\\(no_specializations\\\)\\\)' instead" } +#if __cpp_variable_templates >= 201304 +template <typename T, typename U> +[[gnu::no_specializations ("foo")]] int b = 1; // { dg-warning "'\\\[\\\[gnu::no_specializations\\\]\\\]' is not supported; use '\\\[\\\[clang::no_specializations\\\]\\\]' or '__attribute__\\\(\\\(no_specializations\\\)\\\)' instead" "" { target c++14 } } +#endif +template <typename T, typename U> +[[__gnu__::__no_specializations__]] int foo () { return 0; } // { dg-warning "'\\\[\\\[gnu::no_specializations\\\]\\\]' is not supported; use '\\\[\\\[clang::no_specializations\\\]\\\]' or '__attribute__\\\(\\\(no_specializations\\\)\\\)' instead" } diff --git a/gcc/testsuite/g++.dg/ext/attr-no_specializations2.C b/gcc/testsuite/g++.dg/ext/attr-no_specializations2.C new file mode 100644 index 000000000000..9ebee5e94e3f --- /dev/null +++ b/gcc/testsuite/g++.dg/ext/attr-no_specializations2.C @@ -0,0 +1,102 @@ +// PR c++/120635 +// { dg-do compile { target c++11 } } + +template <typename T, typename U> +union [[clang::no_specializations ("reason1")]] A {}; // { dg-message "declared 'clang::no_specializations' here" } +#if __cpp_variable_templates >= 201304 +template <typename T, typename U> +[[__clang__::__no_specializations__ ("reason2")]] int b = 1; // { dg-message "declared 'clang::no_specializations' here" "" { target c++14 } } +#endif +template <typename T, typename U> +[[clang::no_specializations ("reason3")]] int foo () { return 0; } // { dg-message "declared 'clang::no_specializations' here" } +template <typename T> +struct B { + struct [[clang::no_specializations ("reason4")]] C {}; // { dg-warning "'no_specializations' attribute only applies to class, function or variable templates" } + [[clang::no_specializations ("reason5")]] static int b; // { dg-warning "'no_specializations' attribute only applies to class, function or variable templates" } + [[clang::no_specializations ("reason6")]] int foo () { return 0; } // { dg-warning "'no_specializations' attribute only applies to class, function or variable templates" } + template <typename U> + class [[clang::no_specializations ("reason7")]] D {}; +}; +A <int, long> c; +#if __cpp_variable_templates >= 201304 +int d = b <int, long>; +#endif +int e = foo <int, long> (); +template <typename T> +struct D {}; +template <> +union A <int, int> { int a; }; // { dg-error "'union A<int, int>' cannot be specialized: 'reason1'" } +template <typename T> +union A <long, T> { long b; }; // { dg-error "'union A<long int, T>' cannot be specialized: 'reason1'" } +template <typename T> +union A <D <T>, D <T>> { D<T> c; }; // { dg-error "'union A<D<T>, D<T> >' cannot be specialized: 'reason1'" } +#if __cpp_variable_templates >= 201304 +template <> +int b <int, int> = 2; // { dg-error "'b<int, int>' cannot be specialized: 'reason2'" "" { target c++14 } } +template <typename T> +int b <long, T> = 3; // { dg-error "'b<long int, T>' cannot be specialized: 'reason2'" "" { target c++14 } } +template <typename T> +int b <D <T>, D <T>> = 4; // { dg-error "'b<D<T>, D<T> >' cannot be specialized: 'reason2'" "" { target c++14 } } +#endif +template <> +int foo <int, int> () { return 1; } // { dg-error "'int foo\\\(\\\) \\\[with T = int; U = int\\\]' cannot be specialized: 'reason3'" } +struct [[clang::no_specializations ("reason8")]] E {}; // { dg-warning "'no_specializations' attribute only applies to class, function or variable templates" } +template <typename T> +using F [[clang::no_specializations ("reason9")]] = T; // { dg-warning "'no_specializations' attribute only applies to class, function or variable templates" } +[[clang::no_specializations ("reason10")]] int f = 1; // { dg-warning "'no_specializations' attribute only applies to class, function or variable templates" } +[[clang::no_specializations ("reason11")]] int bar () { return 0; } // { dg-warning "'no_specializations' attribute only applies to class, function or variable templates" } +template <typename T, typename U> +union G {}; +#if __cpp_variable_templates >= 201304 +template <typename T, typename U> +int g = 1; +#endif +template <typename T, typename U> +int baz () { return 0; } +template <> +union [[clang::no_specializations ("reason12")]] G <int, int> { int a; }; // { dg-warning "'no_specializations' attribute only applies to class, function or variable templates" } +template <typename T> +union [[clang::no_specializations ("reason13")]] G <long, T> { long b; }; // { dg-warning "'no_specializations' attribute only applies to class, function or variable templates" } +template <typename T> +union [[clang::no_specializations ("reason14")]] G <D <T>, D <T>> { D<T> c; }; // { dg-warning "'no_specializations' attribute only applies to class, function or variable templates" } +#if __cpp_variable_templates >= 201304 +template <> +[[clang::no_specializations ("reason15")]] int g <int, int> = 2; // { dg-warning "'no_specializations' attribute only applies to class, function or variable templates" "" { target c++14 } } +template <typename T> +[[clang::no_specializations ("reason16")]] int g <long, T> = 3; // { dg-warning "'no_specializations' attribute only applies to class, function or variable templates" "" { target c++14 } } +template <typename T> +[[clang::no_specializations ("reason17")]] int g <D <T>, D <T>> = 4; // { dg-warning "'no_specializations' attribute only applies to class, function or variable templates" "" { target c++14 } } +#endif +template <> +[[clang::no_specializations ("reason18")]] int baz <int, int> () { return 1; } // { dg-warning "'no_specializations' attribute only applies to class, function or variable templates" } +template <typename T> +using H [[clang::no_specializations ("reason19")]] = int; // { dg-warning "'no_specializations' attribute only applies to class, function or variable templates" } +template union A <double, double>; +extern template union A <float, float>; +#if __cpp_variable_templates >= 201304 +template int b <double, double>; +extern template int b <float, float>; +#endif +template int foo <double, double> (); +extern template int foo <float, float> (); +template <typename T> +struct D2 {}; +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Winvalid-specialization" +template <> +union A <short, int> { int a; }; // { dg-bogus "'union A<int, int>' cannot be specialized: 'reason1'" } +template <typename T> +union A <long long, T> { long b; }; // { dg-bogus "'union A<long int, T>' cannot be specialized: 'reason1'" } +template <typename T> +union A <D2 <T>, D2 <T>> { D2<T> c; }; // { dg-bogus "'union A<D<T>, D<T> >' cannot be specialized: 'reason1'" } +#if __cpp_variable_templates >= 201304 +template <> +constexpr int b <short, int> = 2; // { dg-bogus "'b<short, int>' cannot be specialized: 'reason2'" } +template <typename T> +constexpr int b <long long, T> = 3; // { dg-bogus "'b<long long int, T>' cannot be specialized: 'reason2'" } +template <typename T> +constexpr int b <D2 <T>, D2 <T>> = 4; // { dg-bogus "'b<D2<T>, D2<T> >' cannot be specialized: 'reason2'" } +#endif +template <> +int foo <short, int> () { return 1; } // { dg-bogus "'int foo\\\(\\\) \\\[with T = short; U = int\\\]' cannot be specialized: 'reason3'" } +#pragma GCC diagnostic pop diff --git a/gcc/testsuite/g++.dg/ext/attr-no_specializations3.C b/gcc/testsuite/g++.dg/ext/attr-no_specializations3.C new file mode 100644 index 000000000000..07b3da067ee7 --- /dev/null +++ b/gcc/testsuite/g++.dg/ext/attr-no_specializations3.C @@ -0,0 +1,108 @@ +// PR c++/120635 +// { dg-do compile } + +template <typename T, typename U> +struct __attribute__((no_specializations)) A {}; // { dg-message "declared 'clang::no_specializations' here" } +#if __cpp_variable_templates >= 201304 +template <typename T, typename U> +__attribute__((__no_specializations__)) int b = 1; // { dg-message "declared 'clang::no_specializations' here" "" { target c++14 } } +#endif +template <typename T, typename U> +__attribute__((no_specializations)) int foo () { return 0; } // { dg-message "declared 'clang::no_specializations' here" } +template <typename T> +struct B { + struct __attribute__((no_specializations)) C {}; // { dg-warning "'no_specializations' attribute only applies to class, function or variable templates" } + __attribute__((no_specializations)) static int b; // { dg-warning "'no_specializations' attribute only applies to class, function or variable templates" } + __attribute__((no_specializations)) int foo () { return 0; } // { dg-warning "'no_specializations' attribute only applies to class, function or variable templates" } + template <typename U> + class __attribute__((no_specializations)) D {}; +}; +A <int, long> c; +#if __cpp_variable_templates >= 201304 +int d = b <int, long>; +#endif +int e = foo <int, long> (); +template <typename T> +struct D {}; +template <> +struct A <int, int> { int a; }; // { dg-error "'struct A<int, int>' cannot be specialized" } +template <typename T> +struct A <long, T> { long b; }; // { dg-error "'struct A<long int, T>' cannot be specialized" } +template <typename T> +struct A <D <T>, D <T> > { D<T> c; }; // { dg-error "'struct A<D<T>, D<T> >' cannot be specialized" } +#if __cpp_variable_templates >= 201304 +template <> +int b <int, int> = 2; // { dg-error "'b<int, int>' cannot be specialized" "" { target c++14 } } +template <typename T> +int b <long, T> = 3; // { dg-error "'b<long int, T>' cannot be specialized" "" { target c++14 } } +template <typename T> +int b <D <T>, D <T>> = 4; // { dg-error "'b<D<T>, D<T> >' cannot be specialized" "" { target c++14 } } +#endif +template <> +int foo <int, int> () { return 1; } // { dg-error "'int foo\\\(\\\) \\\[with T = int; U = int\\\]' cannot be specialized" } +struct __attribute__((no_specializations)) E {}; // { dg-warning "'no_specializations' attribute only applies to class, function or variable templates" } +#if __cplusplus >= 201103 +template <typename T> +using F __attribute__((no_specializations)) = T; // { dg-warning "'no_specializations' attribute only applies to class, function or variable templates" "" { target c++11 } } +#endif +__attribute__((no_specializations)) int f = 1; // { dg-warning "'no_specializations' attribute only applies to class, function or variable templates" } +__attribute__((no_specializations)) int bar () { return 0; } // { dg-warning "'no_specializations' attribute only applies to class, function or variable templates" } +template <typename T, typename U> +struct G {}; +#if __cpp_variable_templates >= 201304 +template <typename T, typename U> +int g = 1; +#endif +template <typename T, typename U> +int baz () { return 0; } +template <> +struct __attribute__((no_specializations)) G <int, int> { int a; }; // { dg-warning "'no_specializations' attribute only applies to class, function or variable templates" } +template <typename T> +struct __attribute__((no_specializations)) G <long, T> { long b; }; // { dg-warning "'no_specializations' attribute only applies to class, function or variable templates" } +template <typename T> +struct __attribute__((no_specializations)) G <D <T>, D <T> > { D<T> c; }; // { dg-warning "'no_specializations' attribute only applies to class, function or variable templates" } +#if __cpp_variable_templates >= 201304 +template <> +__attribute__((no_specializations)) int g <int, int> = 2; // { dg-warning "'no_specializations' attribute only applies to class, function or variable templates" "" { target c++14 } } +template <typename T> +__attribute__((no_specializations)) int g <long, T> = 3; // { dg-warning "'no_specializations' attribute only applies to class, function or variable templates" "" { target c++14 } } +template <typename T> +__attribute__((no_specializations)) int g <D <T>, D <T>> = 4; // { dg-warning "'no_specializations' attribute only applies to class, function or variable templates" "" { target c++14 } } +#endif +template <> +__attribute__((no_specializations)) int baz <int, int> () { return 1; } // { dg-warning "'no_specializations' attribute only applies to class, function or variable templates" } +#if __cplusplus >= 201103 +template <typename T> +using H __attribute__((no_specializations)) = int; // { dg-warning "'no_specializations' attribute only applies to class, function or variable templates" "" { target c++11 } } +template struct A <double, double>; +extern template struct A <float, float>; +#endif +#if __cpp_variable_templates >= 201304 +template int b <double, double>; +extern template int b <float, float>; +#endif +template int foo <double, double> (); +#if __cplusplus >= 201103 +extern template int foo <float, float> (); +#endif +template <typename T> +struct D2 {}; +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Winvalid-specialization" +template <> +struct A <short, int> { int a; }; // { dg-bogus "'struct A<int, int>' cannot be specialized" } +template <typename T> +struct A <long long, T> { long b; }; // { dg-bogus "'struct A<long int, T>' cannot be specialized" } +template <typename T> +struct A <D2 <T>, D2 <T> > { D2<T> c; }; // { dg-bogus "'struct A<D<T>, D<T> >' cannot be specialized" } +#if __cpp_variable_templates >= 201304 +template <> +constexpr int b <short, int> = 2; // { dg-bogus "'b<short, int>' cannot be specialized" } +template <typename T> +constexpr int b <long long, T> = 3; // { dg-bogus "'b<long long int, T>' cannot be specialized" } +template <typename T> +constexpr int b <D2 <T>, D2 <T>> = 4; // { dg-bogus "'b<D2<T>, D2<T> >' cannot be specialized" } +#endif +template <> +int foo <short, int> () { return 1; } // { dg-bogus "'int foo\\\(\\\) \\\[with T = short; U = int\\\]' cannot be specialized" } +#pragma GCC diagnostic pop diff --git a/gcc/testsuite/g++.dg/ext/attr-no_specializations4.C b/gcc/testsuite/g++.dg/ext/attr-no_specializations4.C new file mode 100644 index 000000000000..c0544525f5a3 --- /dev/null +++ b/gcc/testsuite/g++.dg/ext/attr-no_specializations4.C @@ -0,0 +1,108 @@ +// PR c++/120635 +// { dg-do compile } + +template <typename T, typename U> +struct __attribute__((no_specializations ("reason1"))) A {}; // { dg-message "declared 'clang::no_specializations' here" } +#if __cpp_variable_templates >= 201304 +template <typename T, typename U> +__attribute__((__no_specializations__ ("reason2"))) int b = 1; // { dg-message "declared 'clang::no_specializations' here" "" { target c++14 } } +#endif +template <typename T, typename U> +__attribute__((no_specializations ("reason3"))) int foo () { return 0; }// { dg-message "declared 'clang::no_specializations' here" } +template <typename T> +struct B { + struct __attribute__((no_specializations ("reason4"))) C {}; // { dg-warning "'no_specializations' attribute only applies to class, function or variable templates" } + __attribute__((no_specializations ("reason5"))) static int b; // { dg-warning "'no_specializations' attribute only applies to class, function or variable templates" } + __attribute__((no_specializations ("reason6"))) int foo () { return 0; } // { dg-warning "'no_specializations' attribute only applies to class, function or variable templates" } + template <typename U> + class __attribute__((no_specializations ("reason7"))) D {}; +}; +A <int, long> c; +#if __cpp_variable_templates >= 201304 +int d = b <int, long>; +#endif +int e = foo <int, long> (); +template <typename T> +struct D {}; +template <> +struct A <int, int> { int a; }; // { dg-error "'struct A<int, int>' cannot be specialized: 'reason1'" } +template <typename T> +struct A <long, T> { long b; }; // { dg-error "'struct A<long int, T>' cannot be specialized: 'reason1'" } +template <typename T> +struct A <D <T>, D <T> > { D<T> c; }; // { dg-error "'struct A<D<T>, D<T> >' cannot be specialized: 'reason1'" } +#if __cpp_variable_templates >= 201304 +template <> +int b <int, int> = 2; // { dg-error "'b<int, int>' cannot be specialized: 'reason2'" "" { target c++14 } } +template <typename T> +int b <long, T> = 3; // { dg-error "'b<long int, T>' cannot be specialized: 'reason2'" "" { target c++14 } } +template <typename T> +int b <D <T>, D <T> > = 4; // { dg-error "'b<D<T>, D<T> >' cannot be specialized: 'reason2'" "" { target c++14 } } +#endif +template <> +int foo <int, int> () { return 1; } // { dg-error "'int foo\\\(\\\) \\\[with T = int; U = int\\\]' cannot be specialized: 'reason3'" } +struct __attribute__((no_specializations ("reason8"))) E {}; // { dg-warning "'no_specializations' attribute only applies to class, function or variable templates" } +#if __cplusplus >= 201103 +template <typename T> +using F __attribute__((no_specializations ("reason9"))) = T; // { dg-warning "'no_specializations' attribute only applies to class, function or variable templates" "" { target c++11 } } +#endif +__attribute__((no_specializations ("reason10"))) int f = 1; // { dg-warning "'no_specializations' attribute only applies to class, function or variable templates" } +__attribute__((no_specializations ("reason11"))) int bar () { return 0; } // { dg-warning "'no_specializations' attribute only applies to class, function or variable templates" } +template <typename T, typename U> +struct G {}; +#if __cpp_variable_templates >= 201304 +template <typename T, typename U> +int g = 1; +#endif +template <typename T, typename U> +int baz () { return 0; } +template <> +struct __attribute__((no_specializations ("reason12"))) G <int, int> { int a; }; // { dg-warning "'no_specializations' attribute only applies to class, function or variable templates" } +template <typename T> +struct __attribute__((no_specializations ("reason13"))) G <long, T> { long b; }; // { dg-warning "'no_specializations' attribute only applies to class, function or variable templates" } +template <typename T> +struct __attribute__((no_specializations ("reason14"))) G <D <T>, D <T> > { D<T> c; }; // { dg-warning "'no_specializations' attribute only applies to class, function or variable templates" } +#if __cpp_variable_templates >= 201304 +template <> +__attribute__((no_specializations ("reason15"))) int g <int, int> = 2; // { dg-warning "'no_specializations' attribute only applies to class, function or variable templates" "" { target c++14 } } +template <typename T> +__attribute__((no_specializations ("reason16"))) int g <long, T> = 3; // { dg-warning "'no_specializations' attribute only applies to class, function or variable templates" "" { target c++14 } } +template <typename T> +__attribute__((no_specializations ("reason17"))) int g <D <T>, D <T> > = 4; // { dg-warning "'no_specializations' attribute only applies to class, function or variable templates" "" { target c++14 } } +#endif +template <> +__attribute__((no_specializations ("reason18"))) int baz <int, int> () { return 1; } // { dg-warning "'no_specializations' attribute only applies to class, function or variable templates" } +#if __cplusplus >= 201103 +template <typename T> +using H __attribute__((no_specializations ("reason19"))) = int; // { dg-warning "'no_specializations' attribute only applies to class, function or variable templates" "" { target c++11 } } +template struct A <double, double>; +extern template struct A <float, float>; +#endif +#if __cpp_variable_templates >= 201304 +template int b <double, double>; +extern template int b <float, float>; +#endif +template int foo <double, double> (); +#if __cplusplus >= 201103 +extern template int foo <float, float> (); +#endif +template <typename T> +struct D2 {}; +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Winvalid-specialization" +template <> +struct A <short, int> { int a; }; // { dg-bogus "'struct A<int, int>' cannot be specialized: 'reason1'" } +template <typename T> +struct A <long long, T> { long b; }; // { dg-bogus "'struct A<long int, T>' cannot be specialized: 'reason1'" } +template <typename T> +struct A <D2 <T>, D2 <T> > { D2<T> c; }; // { dg-bogus "'struct A<D<T>, D<T> >' cannot be specialized: 'reason1'" } +#if __cpp_variable_templates >= 201304 +template <> +constexpr int b <short, int> = 2; // { dg-bogus "'b<short, int>' cannot be specialized: 'reason2'" } +template <typename T> +constexpr int b <long long, T> = 3; // { dg-bogus "'b<long long int, T>' cannot be specialized: 'reason2'" } +template <typename T> +constexpr int b <D2 <T>, D2 <T> > = 4; // { dg-bogus "'b<D2<T>, D2<T> >' cannot be specialized: 'reason2'" } +#endif +template <> +int foo <short, int> () { return 1; } // { dg-bogus "'int foo\\\(\\\) \\\[with T = short; U = int\\\]' cannot be specialized: 'reason3'" } +#pragma GCC diagnostic pop diff --git a/gcc/testsuite/g++.dg/ext/attr-no_specializations5.C b/gcc/testsuite/g++.dg/ext/attr-no_specializations5.C new file mode 100644 index 000000000000..b8009d3f7c36 --- /dev/null +++ b/gcc/testsuite/g++.dg/ext/attr-no_specializations5.C @@ -0,0 +1,160 @@ +// PR c++/120635 +// { dg-do compile { target c++11 } } + +int arr[2]; +struct S { int a, b; }; +S arr2[2]; + +template <typename T> +struct [[clang::no_specializations]] S1 {}; +template <typename T> +struct [[clang::no_specializations ("foobar")]] S2 {}; +template <typename T> +struct [[clang::no_specializations (0)]] S3 {}; // { dg-error "'no_specializations' attribute argument must be a string constant|expected string-literal" } +template <typename T> +struct [[clang::no_specializations ("foo", "bar", "baz")]] S4 {};// { dg-error "wrong number of arguments specified for 'no_specializations' attribute" } +template <typename T> +struct [[clang::no_specializations (0, 1, 2)]] S5 {}; // { dg-error "wrong number of arguments specified for 'no_specializations' attribute|expected string-literal" } + +void +foo (int n) +{ + auto a = [] [[clang::no_specializations]] () {}; // { dg-warning "'no_specializations' attribute only applies to class, function or variable templates" } + auto b = [] constexpr [[clang::no_specializations]] {}; // { dg-warning "'no_specializations' attribute only applies to class, function or variable templates" } + // { dg-error "parameter declaration before lambda declaration specifiers only optional with" "" { target c++20_down } .-1 } + // { dg-error "'constexpr' lambda only available with" "" { target c++14_down } .-2 } + auto c = [] noexcept [[clang::no_specializations]] {}; // { dg-warning "'no_specializations' attribute only applies to class, function or variable templates" } + // { dg-error "parameter declaration before lambda exception specification only optional with" "" { target c++20_down } .-1 } + auto d = [] () [[clang::no_specializations]] {}; // { dg-warning "'no_specializations' attribute only applies to class, function or variable templates" } + auto e = new int [n] [[clang::no_specializations]]; // { dg-warning "attributes ignored on outermost array type in new expression" } + auto e2 = new int [n] [[clang::no_specializations]] [42]; // { dg-warning "attributes ignored on outermost array type in new expression" } + auto f = new int [n][42] [[clang::no_specializations]]; // { dg-warning "'no_specializations' attribute only applies to class, function or variable templates" } + [[clang::no_specializations]]; // { dg-warning "attributes at the beginning of statement are ignored" } + [[clang::no_specializations]] {} // { dg-warning "attributes at the beginning of statement are ignored" } + [[clang::no_specializations]] if (true) {} // { dg-warning "attributes at the beginning of statement are ignored" } + [[clang::no_specializations]] while (false) {} // { dg-warning "attributes at the beginning of statement are ignored" } + [[clang::no_specializations]] goto lab; // { dg-warning "attributes at the beginning of statement are ignored" } + [[clang::no_specializations]] lab:; // { dg-warning "'no_specializations' attribute only applies to class, function or variable templates" } + [[clang::no_specializations]] try {} catch (int) {} // { dg-warning "attributes at the beginning of statement are ignored" } + if ([[clang::no_specializations]] int x = 0) {} // { dg-warning "'no_specializations' attribute only applies to class, function or variable templates" } + switch (n) + { + [[clang::no_specializations]] case 1: // { dg-warning "'no_specializations' attribute only applies to class, function or variable templates" } + [[clang::no_specializations]] break; // { dg-warning "attributes at the beginning of statement are ignored" } + [[clang::no_specializations]] default: // { dg-warning "'no_specializations' attribute only applies to class, function or variable templates" } + break; + } + for ([[clang::no_specializations]] auto a : arr) {} // { dg-warning "'no_specializations' attribute only applies to class, function or variable templates" } + for ([[clang::no_specializations]] auto [a, b] : arr2) {} // { dg-warning "'no_specializations' attribute only applies to class, function or variable templates" } + // { dg-error "structured bindings only available with" "" { target c++14_down } .-1 } + [[clang::no_specializations]] asm (""); // { dg-warning "attributes ignored on 'asm' declaration" } + try {} catch ([[clang::no_specializations]] int x) {} // { dg-warning "'no_specializations' attribute only applies to class, function or variable templates" } + try {} catch ([[clang::no_specializations]] int) {} // { dg-warning "'no_specializations' attribute only applies to class, function or variable templates" } + try {} catch (int [[clang::no_specializations]] x) {} // { dg-warning "attribute ignored" } + try {} catch (int [[clang::no_specializations]]) {} // { dg-warning "attribute ignored" } + try {} catch (int x [[clang::no_specializations]]) {} // { dg-warning "'no_specializations' attribute only applies to class, function or variable templates" } +} + +[[clang::no_specializations]] int bar (); // { dg-warning "'no_specializations' attribute only applies to class, function or variable templates" } +using foobar [[clang::no_specializations]] = int; // { dg-warning "'no_specializations' attribute only applies to class, function or variable templates" } +[[clang::no_specializations]] int a; // { dg-warning "'no_specializations' attribute only applies to class, function or variable templates" } +[[clang::no_specializations]] auto [b, c] = arr; // { dg-warning "'no_specializations' attribute only applies to class, function or variable templates" } + // { dg-error "structured bindings only available with" "" { target c++14_down } .-1 } +[[clang::no_specializations]]; // { dg-warning "attribute ignored" } +inline [[clang::no_specializations]] void baz () {} // { dg-warning "attribute ignored" } + // { dg-error "standard attributes in middle of decl-specifiers" "" { target *-*-* } .-1 } +constexpr [[clang::no_specializations]] int qux () { return 0; }// { dg-warning "attribute ignored" } + // { dg-error "standard attributes in middle of decl-specifiers" "" { target *-*-* } .-1 } +int [[clang::no_specializations]] d; // { dg-warning "attribute ignored" } +int const [[clang::no_specializations]] e = 1; // { dg-warning "attribute ignored" } +struct A {} [[clang::no_specializations]]; // { dg-warning "attribute ignored in declaration of 'struct A'" } +struct A [[clang::no_specializations]]; // { dg-warning "attribute ignored" } +struct A [[clang::no_specializations]] a1; // { dg-warning "attribute ignored" } +A [[clang::no_specializations]] a2; // { dg-warning "attribute ignored" } +enum B { B0 } [[clang::no_specializations]]; // { dg-warning "attribute ignored in declaration of 'enum B'" } +enum B [[clang::no_specializations]]; // { dg-warning "attribute ignored" } +enum B [[clang::no_specializations]] b1; // { dg-warning "attribute ignored" } +B [[clang::no_specializations]] b2; // { dg-warning "attribute ignored" } +struct [[clang::no_specializations]] C {}; // { dg-warning "'no_specializations' attribute only applies to class, function or variable templates" } +int f [[clang::no_specializations]]; // { dg-warning "'no_specializations' attribute only applies to class, function or variable templates" } +int g[2] [[clang::no_specializations]]; // { dg-warning "'no_specializations' attribute only applies to class, function or variable templates" } +int g2 [[clang::no_specializations]] [2]; // { dg-warning "'no_specializations' attribute only applies to class, function or variable templates" } +int corge () [[clang::no_specializations]]; // { dg-warning "'no_specializations' attribute only applies to class, function or variable templates" } +int *[[clang::no_specializations]] h; // { dg-warning "'no_specializations' attribute only applies to class, function or variable templates" } +int & [[clang::no_specializations]] i = f; // { dg-warning "'no_specializations' attribute only applies to class, function or variable templates" } +int && [[clang::no_specializations]] j = 0; // { dg-warning "'no_specializations' attribute only applies to class, function or variable templates" } +int S::* [[clang::no_specializations]] k; // { dg-warning "'no_specializations' attribute only applies to class, function or variable templates" } +auto l = sizeof (int [2] [[clang::no_specializations]]); // { dg-warning "'no_specializations' attribute only applies to class, function or variable templates" } +int freddy ([[clang::no_specializations]] int a, // { dg-warning "'no_specializations' attribute only applies to class, function or variable templates" } + [[clang::no_specializations]] int, // { dg-warning "'no_specializations' attribute only applies to class, function or variable templates" } + [[clang::no_specializations]] int c = 0, // { dg-warning "'no_specializations' attribute only applies to class, function or variable templates" } + [[clang::no_specializations]] int = 0); // { dg-warning "'no_specializations' attribute only applies to class, function or variable templates" } +void +corge ([[clang::no_specializations]] int a, // { dg-warning "'no_specializations' attribute only applies to class, function or variable templates" } + [[clang::no_specializations]] int, // { dg-warning "'no_specializations' attribute only applies to class, function or variable templates" } + [[clang::no_specializations]] int c = 0, // { dg-warning "'no_specializations' attribute only applies to class, function or variable templates" } + [[clang::no_specializations]] int = 0) // { dg-warning "'no_specializations' attribute only applies to class, function or variable templates" } +{ +} +[[clang::no_specializations]] void +garply () // { dg-warning "'no_specializations' attribute only applies to class, function or variable templates" } +{ +} +[[clang::no_specializations]] int +xyzzyy () // { dg-warning "'no_specializations' attribute only applies to class, function or variable templates" } +{ + return 0; +} +int grault (int [[clang::no_specializations]] a, // { dg-warning "attribute ignored" } + int [[clang::no_specializations]], // { dg-warning "attribute ignored" } + int [[clang::no_specializations]] c = 0, // { dg-warning "attribute ignored" } + int [[clang::no_specializations]] = 0); // { dg-warning "attribute ignored" } +void +waldo (int [[clang::no_specializations]] a, // { dg-warning "attribute ignored" } + int [[clang::no_specializations]], // { dg-warning "attribute ignored" } + int [[clang::no_specializations]] c = 0, // { dg-warning "attribute ignored" } + int [[clang::no_specializations]] = 0) // { dg-warning "attribute ignored" } +{ +} +int plugh (int a [[clang::no_specializations]], // { dg-warning "'no_specializations' attribute only applies to class, function or variable templates" } + int b [[clang::no_specializations]] = 0); // { dg-warning "'no_specializations' attribute only applies to class, function or variable templates" } +void +thud (int a [[clang::no_specializations]], // { dg-warning "'no_specializations' attribute only applies to class, function or variable templates" } + int b [[clang::no_specializations]] = 0) // { dg-warning "'no_specializations' attribute only applies to class, function or variable templates" } +{ +} +enum [[clang::no_specializations]] D { D0 }; // { dg-warning "'no_specializations' attribute only applies to class, function or variable templates" } +enum class [[clang::no_specializations]] E { E0 }; // { dg-warning "'no_specializations' attribute only applies to class, function or variable templates" } +enum F {}; +enum [[clang::no_specializations]] F; // { dg-warning "type attributes ignored after type is already defined" } +enum G { + G0 [[clang::no_specializations]], // { dg-warning "'no_specializations' attribute only applies to class, function or variable templates" } + G1 [[clang::no_specializations]] = 2 // { dg-warning "'no_specializations' attribute only applies to class, function or variable templates" } +}; +namespace [[clang::no_specializations]] H { using H0 = int; } // { dg-warning "'no_specializations' attribute directive ignored" } +namespace [[clang::no_specializations]] {} // { dg-warning "'no_specializations' attribute directive ignored" } +[[clang::no_specializations]] using namespace H; // { dg-warning "'no_specializations' attribute directive ignored" } +struct [[clang::no_specializations]] I // { dg-warning "'no_specializations' attribute only applies to class, function or variable templates" } +{ + [[clang::no_specializations]]; // { dg-error "declaration does not declare anything" } + [[clang::no_specializations]] int i; // { dg-warning "'no_specializations' attribute only applies to class, function or variable templates" } + [[clang::no_specializations]] int foo (); // { dg-warning "'no_specializations' attribute only applies to class, function or variable templates" } + [[clang::no_specializations]] int bar () { return 1; } // { dg-warning "'no_specializations' attribute only applies to class, function or variable templates" } + [[clang::no_specializations]] int : 0; // { dg-warning "'no_specializations' attribute only applies to class, function or variable templates" } + [[clang::no_specializations]] int i2 : 5; // { dg-warning "'no_specializations' attribute only applies to class, function or variable templates" } + [[clang::no_specializations]] static int i3; // { dg-warning "'no_specializations' attribute only applies to class, function or variable templates" } + static int i4; +}; +[[clang::no_specializations]] int I::i4 = 0; // { dg-warning "'no_specializations' attribute only applies to class, function or variable templates" } +struct J : [[clang::no_specializations]] C {}; // { dg-warning "attributes on base specifiers are ignored" } +#if __cpp_concepts >= 201907L +template <typename T> +concept K [[clang::no_specializations]] = requires { true; }; // { dg-warning "'no_specializations' attribute only applies to class, function or variable templates" "" { target c++20 } } +#endif +typedef int L [[clang::no_specializations]]; // { dg-warning "'no_specializations' attribute only applies to class, function or variable templates" } +template <typename T> +struct M {}; +template <> +struct [[clang::no_specializations]] M<int> { int m; }; // { dg-warning "'no_specializations' attribute only applies to class, function or variable templates" } +typedef int N[2] [[clang::no_specializations]]; // { dg-warning "'no_specializations' attribute only applies to class, function or variable templates" } +typedef int O [[clang::no_specializations]] [2]; // { dg-warning "'no_specializations' attribute only applies to class, function or variable templates" } diff --git a/gcc/testsuite/g++.dg/ext/attr-no_specializations6.C b/gcc/testsuite/g++.dg/ext/attr-no_specializations6.C new file mode 100644 index 000000000000..81e435a3455e --- /dev/null +++ b/gcc/testsuite/g++.dg/ext/attr-no_specializations6.C @@ -0,0 +1,75 @@ +// PR c++/120635 +// { dg-do compile { target c++11 } } +// { dg-options "-Winvalid-specialization" } + +template <typename T, typename U> +class [[clang::no_specializations]] A {}; // { dg-message "declared 'clang::no_specializations' here" } +#if __cpp_variable_templates >= 201304 +template <typename T, typename U> +[[clang::no_specializations]] constexpr int b = 1; // { dg-message "declared 'clang::no_specializations' here" "" { target c++14 } } +#endif +template <typename T, typename U> +[[__clang__::__no_specializations__]] int foo () { return 0; } // { dg-message "declared 'clang::no_specializations' here" } +template <typename T> +struct D {}; +template <> +struct A <int, int> { int a; }; // { dg-error "'class A<int, int>' cannot be specialized" } +template <typename T> +struct A <long, T> { long b; }; // { dg-error "'class A<long int, T>' cannot be specialized" } +template <typename T> +struct A <D <T>, D <T>> { D<T> c; }; // { dg-error "'class A<D<T>, D<T> >' cannot be specialized" } +#if __cpp_variable_templates >= 201304 +template <> +constexpr int b <int, int> = 2; // { dg-error "'b<int, int>' cannot be specialized" "" { target c++14 } } +template <typename T> +constexpr int b <long, T> = 3; // { dg-error "'b<long int, T>' cannot be specialized" "" { target c++14 } } +template <typename T> +constexpr int b <D <T>, D <T>> = 4; // { dg-error "'b<D<T>, D<T> >' cannot be specialized" "" { target c++14 } } +#endif +template <> +int foo <int, int> () { return 1; } // { dg-error "'int foo\\\(\\\) \\\[with T = int; U = int\\\]' cannot be specialized" } +template <typename T, typename U> +struct [[clang::no_specializations ("this is why")]] B {}; // { dg-message "declared 'clang::no_specializations' here" } +#if __cpp_variable_templates >= 201304 +template <typename T, typename U> +[[clang::no_specializations ("my other reason")]] int c = 1; // { dg-message "declared 'clang::no_specializations' here" "" { target c++14 } } +#endif +template <typename T, typename U> +[[__clang__::__no_specializations__ ("bar cannot be specialized")]] int bar () { return 0; } // { dg-message "declared 'clang::no_specializations' here" } +template <> +struct B <int, int> { int a; }; // { dg-error "'struct B<int, int>' cannot be specialized: 'this is why'" } +template <typename T> +struct B <long, T> { long c; }; // { dg-error "'struct B<long int, T>' cannot be specialized: 'this is why'" } +template <typename T> +struct B <D <T>, D <T>> { D<T> c; }; // { dg-error "'struct B<D<T>, D<T> >' cannot be specialized: 'this is why'" } +#if __cpp_variable_templates >= 201304 +template <> +int c <int, int> = 2; // { dg-error "'c<int, int>' cannot be specialized: 'my other reason'" "" { target c++14 } } +template <typename T> +int c <long, T> = 3; // { dg-error "'c<long int, T>' cannot be specialized: 'my other reason'" "" { target c++14 } } +template <typename T> +int c <D <T>, D <T>> = 4; // { dg-error "'c<D<T>, D<T> >' cannot be specialized: 'my other reason'" "" { target c++14 } } +#endif +template <> +int bar <int, int> () { return 1; } // { dg-error "'int bar\\\(\\\) \\\[with T = int; U = int\\\]' cannot be specialized: 'bar cannot be specialized'" } +template <typename T> +struct D2 {}; +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Winvalid-specialization" +template <> +struct A <short, int> { int a; }; // { dg-bogus "'class A<int, int>' cannot be specialized" } +template <typename T> +struct A <long long, T> { long b; }; // { dg-bogus "'class A<long int, T>' cannot be specialized" } +template <typename T> +struct A <D2 <T>, D2 <T>> { D2<T> c; }; // { dg-bogus "'class A<D<T>, D<T> >' cannot be specialized" } +#if __cpp_variable_templates >= 201304 +template <> +constexpr int b <short, int> = 2; // { dg-bogus "'b<short, int>' cannot be specialized" } +template <typename T> +constexpr int b <long long, T> = 3; // { dg-bogus "'b<long long int, T>' cannot be specialized" } +template <typename T> +constexpr int b <D2 <T>, D2 <T>> = 4; // { dg-bogus "'b<D2<T>, D2<T> >' cannot be specialized" } +#endif +template <> +int foo <short, int> () { return 1; } // { dg-bogus "'int foo\\\(\\\) \\\[with T = short; U = int\\\]' cannot be specialized" } +#pragma GCC diagnostic pop diff --git a/gcc/testsuite/g++.dg/ext/attr-no_specializations7.C b/gcc/testsuite/g++.dg/ext/attr-no_specializations7.C new file mode 100644 index 000000000000..be88e76e587f --- /dev/null +++ b/gcc/testsuite/g++.dg/ext/attr-no_specializations7.C @@ -0,0 +1,54 @@ +// PR c++/120635 +// { dg-do compile { target c++11 } } +// { dg-options "-Wno-invalid-specialization" } + +template <typename T, typename U> +class [[clang::no_specializations]] A {}; // { dg-bogus "declared 'clang::no_specializations' here" } +#if __cpp_variable_templates >= 201304 +template <typename T, typename U> +[[clang::no_specializations]] constexpr int b = 1; // { dg-bogus "declared 'clang::no_specializations' here" } +#endif +template <typename T, typename U> +[[__clang__::__no_specializations__]] int foo () { return 0; } // { dg-bogus "declared 'clang::no_specializations' here" } +template <typename T> +struct D {}; +template <> +struct A <int, int> { int a; }; // { dg-bogus "'class A<int, int>' cannot be specialized" } +template <typename T> +struct A <long, T> { long b; }; // { dg-bogus "'class A<long int, T>' cannot be specialized" } +template <typename T> +struct A <D <T>, D <T>> { D<T> c; }; // { dg-bogus "'class A<D<T>, D<T> >' cannot be specialized" } +#if __cpp_variable_templates >= 201304 +template <> +constexpr int b <int, int> = 2; // { dg-bogus "'b<int, int>' cannot be specialized" } +template <typename T> +constexpr int b <long, T> = 3; // { dg-bogus "'b<long int, T>' cannot be specialized" } +template <typename T> +constexpr int b <D <T>, D <T>> = 4; // { dg-bogus "'b<D<T>, D<T> >' cannot be specialized" } +#endif +template <> +int foo <int, int> () { return 1; } // { dg-bogus "'int foo\\\(\\\) \\\[with T = int; U = int\\\]' cannot be specialized" } +template <typename T, typename U> +struct [[clang::no_specializations ("this is why")]] B {}; // { dg-bogus "declared 'clang::no_specializations' here" } +#if __cpp_variable_templates >= 201304 +template <typename T, typename U> +[[clang::no_specializations ("my other reason")]] int c = 1; // { dg-bogus "declared 'clang::no_specializations' here" } +#endif +template <typename T, typename U> +[[__clang__::__no_specializations__ ("bar cannot be specialized")]] int bar () { return 0; } // { dg-bogus "declared 'clang::no_specializations' here" } +template <> +struct B <int, int> { int a; }; // { dg-bogus "'struct B<int, int>' cannot be specialized: 'this is why'" } +template <typename T> +struct B <long, T> { long c; }; // { dg-bogus "'struct B<long int, T>' cannot be specialized: 'this is why'" } +template <typename T> +struct B <D <T>, D <T>> { D<T> c; }; // { dg-bogus "'struct B<D<T>, D<T> >' cannot be specialized: 'this is why'" } +#if __cpp_variable_templates >= 201304 +template <> +int c <int, int> = 2; // { dg-bogus "'c<int, int>' cannot be specialized: 'my other reason'" } +template <typename T> +int c <long, T> = 3; // { dg-bogus "'c<long int, T>' cannot be specialized: 'my other reason'" } +template <typename T> +int c <D <T>, D <T>> = 4; // { dg-bogus "'c<D<T>, D<T> >' cannot be specialized: 'my other reason'" } +#endif +template <> +int bar <int, int> () { return 1; } // { dg-bogus "'int bar\\\(\\\) \\\[with T = int; U = int\\\]' cannot be specialized: 'bar cannot be specialized'" } diff --git a/gcc/testsuite/g++.dg/ext/attr-no_specializations8.C b/gcc/testsuite/g++.dg/ext/attr-no_specializations8.C new file mode 100644 index 000000000000..7cef40c3dffc --- /dev/null +++ b/gcc/testsuite/g++.dg/ext/attr-no_specializations8.C @@ -0,0 +1,31 @@ +// PR c++/120635 +// { dg-do compile { target c++11 } } +// { dg-require-iconv "IBM1047" } +// { dg-options "-fexec-charset=IBM1047" } + +template <typename T, typename U> +struct [[clang::no_specializations ("this is why")]] B {}; // { dg-message "declared 'clang::no_specializations' here" } +#if __cpp_variable_templates >= 201304 +template <typename T, typename U> +[[clang::no_specializations ("my other reason")]] int c = 1; // { dg-message "declared 'clang::no_specializations' here" "" { target c++14 } } +#endif +template <typename T, typename U> +[[__clang__::__no_specializations__ ("bar cannot be specialized")]] int bar () { return 0; } // { dg-message "declared 'clang::no_specializations' here" } +template <typename T> +struct D {}; +template <> +struct B <int, int> { int a; }; // { dg-error "'struct B<int, int>' cannot be specialized: 'this is why'" } +template <typename T> +struct B <long, T> { long c; }; // { dg-error "'struct B<long int, T>' cannot be specialized: 'this is why'" } +template <typename T> +struct B <D <T>, D <T>> { D<T> c; }; // { dg-error "'struct B<D<T>, D<T> >' cannot be specialized: 'this is why'" } +#if __cpp_variable_templates >= 201304 +template <> +int c <int, int> = 2; // { dg-error "'c<int, int>' cannot be specialized: 'my other reason'" "" { target c++14 } } +template <typename T> +int c <long, T> = 3; // { dg-error "'c<long int, T>' cannot be specialized: 'my other reason'" "" { target c++14 } } +template <typename T> +int c <D <T>, D <T>> = 4; // { dg-error "'c<D<T>, D<T> >' cannot be specialized: 'my other reason'" "" { target c++14 } } +#endif +template <> +int bar <int, int> () { return 1; } // { dg-error "'int bar\\\(\\\) \\\[with T = int; U = int\\\]' cannot be specialized: 'bar cannot be specialized'" } diff --git a/gcc/testsuite/g++.dg/ext/attr-no_specializations9.C b/gcc/testsuite/g++.dg/ext/attr-no_specializations9.C new file mode 100644 index 000000000000..72a19aa4fc7d --- /dev/null +++ b/gcc/testsuite/g++.dg/ext/attr-no_specializations9.C @@ -0,0 +1,31 @@ +// PR c++/120635 +// { dg-do compile { target c++11 } } +// { dg-require-iconv "IBM1047" } +// { dg-options "-fexec-charset=IBM1047" } + +template <typename T, typename U> +struct __attribute__((no_specializations ("this is why"))) B {}; // { dg-message "declared 'clang::no_specializations' here" } +#if __cpp_variable_templates >= 201304 +template <typename T, typename U> +__attribute__((no_specializations ("my other reason"))) int c = 1; // { dg-message "declared 'clang::no_specializations' here" "" { target c++14 } } +#endif +template <typename T, typename U> +__attribute__((__no_specializations__ ("bar cannot be specialized"))) int bar () { return 0; } // { dg-message "declared 'clang::no_specializations' here" } +template <typename T> +struct D {}; +template <> +struct B <int, int> { int a; }; // { dg-error "'struct B<int, int>' cannot be specialized: 'this is why'" } +template <typename T> +struct B <long, T> { long c; }; // { dg-error "'struct B<long int, T>' cannot be specialized: 'this is why'" } +template <typename T> +struct B <D <T>, D <T>> { D<T> c; }; // { dg-error "'struct B<D<T>, D<T> >' cannot be specialized: 'this is why'" } +#if __cpp_variable_templates >= 201304 +template <> +int c <int, int> = 2; // { dg-error "'c<int, int>' cannot be specialized: 'my other reason'" "" { target c++14 } } +template <typename T> +int c <long, T> = 3; // { dg-error "'c<long int, T>' cannot be specialized: 'my other reason'" "" { target c++14 } } +template <typename T> +int c <D <T>, D <T>> = 4; // { dg-error "'c<D<T>, D<T> >' cannot be specialized: 'my other reason'" "" { target c++14 } } +#endif +template <> +int bar <int, int> () { return 1; } // { dg-error "'int bar\\\(\\\) \\\[with T = int; U = int\\\]' cannot be specialized: 'bar cannot be specialized'" }
