]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
c++: Implement clang::no_specializations attribute and -Winvalid-specialization warni...
authorJakub Jelinek <jakub@redhat.com>
Mon, 13 Jul 2026 16:33:37 +0000 (18:33 +0200)
committerJakub Jelinek <jakub@gcc.gnu.org>
Mon, 13 Jul 2026 16:37:33 +0000 (18:37 +0200)
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  <jakub@redhat.com>

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 <jason@redhat.com>
20 files changed:
gcc/c-family/c-lex.cc
gcc/c-family/c.opt
gcc/c-family/c.opt.urls
gcc/cp/decl2.cc
gcc/cp/parser.cc
gcc/cp/pt.cc
gcc/cp/tree.cc
gcc/doc/extend.texi
gcc/doc/invoke.texi
gcc/testsuite/g++.dg/ext/attr-no_specializations1.C [new file with mode: 0644]
gcc/testsuite/g++.dg/ext/attr-no_specializations10.C [new file with mode: 0644]
gcc/testsuite/g++.dg/ext/attr-no_specializations11.C [new file with mode: 0644]
gcc/testsuite/g++.dg/ext/attr-no_specializations2.C [new file with mode: 0644]
gcc/testsuite/g++.dg/ext/attr-no_specializations3.C [new file with mode: 0644]
gcc/testsuite/g++.dg/ext/attr-no_specializations4.C [new file with mode: 0644]
gcc/testsuite/g++.dg/ext/attr-no_specializations5.C [new file with mode: 0644]
gcc/testsuite/g++.dg/ext/attr-no_specializations6.C [new file with mode: 0644]
gcc/testsuite/g++.dg/ext/attr-no_specializations7.C [new file with mode: 0644]
gcc/testsuite/g++.dg/ext/attr-no_specializations8.C [new file with mode: 0644]
gcc/testsuite/g++.dg/ext/attr-no_specializations9.C [new file with mode: 0644]

index 3f3e9f30aa4ec578855477bd4257c35d069fd6b7..53c215ca9444b59e2ce1e6e0b079f9ebb5dde127 100644 (file)
@@ -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);
index 8bad323a4378a1f4b455d16cffee66f955f39053..0260b9dbf42dedb66713a2031e1aff910e073285 100644 (file)
@@ -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.
index 27a5f68acb38a5ebb744dcbdaa2e83b740ccf90b..47e6c58f495ccbb4ea972fc7854469270cb65d85 100644 (file)
@@ -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)
 
index 6e84b58e0b7ff45cb7a78d57bb957ef43ea72383..f3ca6cb7bdba59bb29f376ddfd6d4f60fcc11f26 100644 (file)
@@ -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;
index b57acc6cba592b65b321a41ca1b5c0afcba2e118..d53f74126ee18459df48df6685310faf19182a97 100644 (file)
@@ -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.  */
index d571d6918afbaecfd2392db4e8ee8fff3a181d89..1fe94a863102c4672772c2d82f0d87079501522c 100644 (file)
@@ -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;
index 59629b09fa52e551f05e9b530b2132782dacc738..e39a4a59b9afa23632598ab16276f268885c1d1f 100644 (file)
@@ -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.  */
 
index a49772c5a57de4cd713d04666ac807cde2cb2ce6..60fc5add3bcd852df4408919fdbfd85000cc87d9 100644 (file)
@@ -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
index 038b169d90e661e62577b659e769217cd5cca112..2e21053dae96c2197a215b10f2658b1823001107 100644 (file)
@@ -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 (file)
index 0000000..69dee86
--- /dev/null
@@ -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 (file)
index 0000000..fc3de89
--- /dev/null
@@ -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 (file)
index 0000000..4bb42ad
--- /dev/null
@@ -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 (file)
index 0000000..9ebee5e
--- /dev/null
@@ -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 (file)
index 0000000..07b3da0
--- /dev/null
@@ -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 (file)
index 0000000..c054452
--- /dev/null
@@ -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 (file)
index 0000000..b8009d3
--- /dev/null
@@ -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 (file)
index 0000000..81e435a
--- /dev/null
@@ -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 (file)
index 0000000..be88e76
--- /dev/null
@@ -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 (file)
index 0000000..7cef40c
--- /dev/null
@@ -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 (file)
index 0000000..72a19aa
--- /dev/null
@@ -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'" }