While reviewing other related bugs I noticed 83502.  This patch
doesn't fix the first test case in the bug (attribute noinline
vs always_inline).  Somehow those are still copied from
the primary to the specialization and can cause conflicts.

Hmm, that's odd.  Why is that?

Because duplicate_decl calls diagnose_mismatched_attributes()
on the NEWDECL and OLDDECL.  (Attribute optimize would do the
same thing.)  I was trying to keep the fix small but it makes
sense to take care of this as well so I have in this revision.

It does fix the second test case but with the noreturn change
it would issue a bogus -Wmissing-attributes warning for the
explicit specialization below.  Adding the warn_unused_result
attribute to it would then make GCC complain about a conflict
between the added attribute and noreturn, while removing it
would lead to worse code.

   template <class T>
   int __attribute__ ((warn_unused_result)) f (T) { return 0; }

   template <>
   int __attribute__ ((noreturn)) f<int> (int) { throw 0; }

   void fi () { f (0); }

I continue to disagree with this use of attribute noreturn.
+      /* Merge the function-has-side-effects bit.  */
+      if (TREE_THIS_VOLATILE (newdecl))
+        TREE_THIS_VOLATILE (olddecl) = 1;
+
+      if (merge_attr)

TREE_THIS_VOLATILE means attribute noreturn, not whether the function
has side-effects; it should be handled in the blocks controlled by
merge_attr.

Whoops.  That was a silly goof.  I must have misread the comment
above the macro definition.  I also didn't have a test for it (or
some of the other changes I've made) so I didn't see the problem.

Attached is an enhanced version of the patch that handles (and
tests) more of the commonly used attributes.  I'm not sure why
in the merge_attr block I have to merge TREE_THIS_VOLATILE and
TREE_NOTHROW back and forth but not also READONLY, PURE, or
MALLOC, but without it tests fail.

Martin

PS Would it be possible to add a new macro with "noreturn" in
the name to make it more intuitive?  (And ditto perhaps also
for TREE_READONLY for "const" functions, though for whatever
reason that seems easier to decipher.  I know you're all used
to it but it's far from intuitive.)

PPS Duplicate_decls is over 1,400 lines long.  If there is more
work to do here in stage 1 (I suspect there might be), would you
mind if I broke it up into two or more, say one for functions,
another for types, or whatever grouping makes most sense to make
it easier to follow?
PR c++/83871 - wrong code for attribute const and pure on distinct template specializations
PR c++/83503 - [8 Regression] bogus -Wattributes for const and pure on function template specialization

gcc/ChangeLog:

	PR c++/83871
	* gcc/doc/invoke.texi (-Wmissing-attributes): New option.

gcc/c-family/ChangeLog:

	PR c++/83871
	* c.opt (-Wmissing-attributes): New option.

gcc/cp/ChangeLog:

	PR c++/83871
	PR c++/83503
	* cp-tree.h (warn_spec_missing_attributes): New function.
	((check_explicit_specialization): Add an argument.  Call the above
	function.
	* decl.c (duplicate_decls): Avoid applying primary function template's
	attributes to its explicit specializations.
	cp/pt.c (warn_spec_missing_attributes): Define.

gcc/testsuite/ChangeLog:

	PR c++/83871
	PR c++/83503
	* g++.dg/Wmissing-attributes.C: New test.
	* g++.dg/ext/attr-const-pure.C: New test.
	* g++.dg/ext/attr-const.C: New test.
	* g++.dg/ext/attr-deprecated-2.C: New test.
	* g++.dg/ext/attr-malloc-2.C: New test.
	* g++.dg/ext/attr-malloc.C: New test.
	* g++.dg/ext/attr-noinline-2.C: New test.
	* g++.dg/ext/attr-noinline.C: New test.
	* g++.dg/ext/attr-nonnull.C: New test.
	* g++.dg/ext/attr-noreturn-2.C: New test.
	* g++.dg/ext/attr-noreturn.C: New test.
	* g++.dg/ext/attr-nothrow-2.C: New test.
	* g++.dg/ext/attr-nothrow.C: New test.
	* g++.dg/ext/attr-optimize.C: New test.
	* g++.dg/ext/attr-pure.C: New test.
	* g++.dg/ext/attr-returns-nonnull.C: New test.
	* g++.dg/ext/attr-warning.C: New test.

diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt
index 421b146..a4c8c8f 100644
--- a/gcc/c-family/c.opt
+++ b/gcc/c-family/c.opt
@@ -781,6 +781,11 @@ Wtemplates
 C++ ObjC++ Var(warn_templates) Warning
 Warn on primary template declaration.
 
+Wmissing-attributes
+C ObjC C++ ObjC++ Var(warn_missing_attributes) Warning LangEnabledBy(C ObjC C++ ObjC++,Wall)
+Warn about declarations of entities that may be missing attributes
+that related entities have been declared with it.
+
 Wmissing-format-attribute
 C ObjC C++ ObjC++ Warning Alias(Wsuggest-attribute=format)
 ;
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index aef022f..abcd1a6 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -6463,7 +6463,8 @@ extern void end_specialization			(void);
 extern void begin_explicit_instantiation	(void);
 extern void end_explicit_instantiation		(void);
 extern void check_unqualified_spec_or_inst	(tree, location_t);
-extern tree check_explicit_specialization	(tree, tree, int, int);
+extern tree check_explicit_specialization	(tree, tree, int, int,
+						 tree = NULL_TREE);
 extern int num_template_headers_for_class	(tree);
 extern void check_template_variable		(tree);
 extern tree make_auto				(void);
diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c
index cf91773..b922493 100644
--- a/gcc/cp/decl.c
+++ b/gcc/cp/decl.c
@@ -1405,9 +1405,18 @@ duplicate_decls (tree newdecl, tree olddecl, bool newdecl_is_friend)
 	       " literal operator template %qD", newdecl, olddecl);
     }
 
+  /* True to merge attributes between the declarations, false to
+     set OLDDECL's attributes to those of NEWDECL (for template
+     explicit specializations that specify their own attributes
+     independent of those specified for the primary template).  */
+  const bool merge_attr = (TREE_CODE (newdecl) != FUNCTION_DECL
+			   || !DECL_TEMPLATE_SPECIALIZATION (newdecl)
+			   || DECL_TEMPLATE_SPECIALIZATION (olddecl));
+
   if (DECL_P (olddecl)
       && TREE_CODE (newdecl) == FUNCTION_DECL
       && TREE_CODE (olddecl) == FUNCTION_DECL
+      && merge_attr
       && diagnose_mismatched_attributes (olddecl, newdecl))
     {
       if (DECL_INITIAL (olddecl))
@@ -1969,10 +1978,13 @@ next_arg:;
       DECL_ORIGINAL_TYPE (newdecl) = DECL_ORIGINAL_TYPE (olddecl);
     }
 
-  /* Copy all the DECL_... slots specified in the new decl
-     except for any that we copy here from the old type.  */
-  DECL_ATTRIBUTES (newdecl)
-    = (*targetm.merge_decl_attributes) (olddecl, newdecl);
+  /* Copy all the DECL_... slots specified in the new decl except for
+     any that we copy here from the old type.  */
+  if (merge_attr)
+    DECL_ATTRIBUTES (newdecl)
+      = (*targetm.merge_decl_attributes) (olddecl, newdecl);
+  else
+    DECL_ATTRIBUTES (olddecl) = DECL_ATTRIBUTES (newdecl);
 
   if (DECL_DECLARES_FUNCTION_P (olddecl) && DECL_DECLARES_FUNCTION_P (newdecl))
     {
@@ -2099,9 +2111,10 @@ next_arg:;
 		  }
 	    }
 	}
-      else
-	/* Merge the data types specified in the two decls.  */
+      else if (merge_attr)
 	newtype = merge_types (TREE_TYPE (newdecl), TREE_TYPE (olddecl));
+      else
+	newtype = TREE_TYPE (newdecl);
 
       if (VAR_P (newdecl))
 	{
@@ -2165,14 +2178,6 @@ next_arg:;
 	  && !(processing_template_decl && uses_template_parms (newdecl)))
 	layout_decl (newdecl, 0);
 
-      /* Merge the type qualifiers.  */
-      if (TREE_READONLY (newdecl))
-	TREE_READONLY (olddecl) = 1;
-      if (TREE_THIS_VOLATILE (newdecl))
-	TREE_THIS_VOLATILE (olddecl) = 1;
-      if (TREE_NOTHROW (newdecl))
-	TREE_NOTHROW (olddecl) = 1;
-
       /* Merge deprecatedness.  */
       if (TREE_DEPRECATED (newdecl))
 	TREE_DEPRECATED (olddecl) = 1;
@@ -2190,6 +2195,15 @@ next_arg:;
 	    DECL_FUNCTION_SPECIFIC_OPTIMIZATION (newdecl)
 	      = DECL_FUNCTION_SPECIFIC_OPTIMIZATION (olddecl);
 	}
+      else
+	{
+	  /* Merge the const type qualifier.  */
+	  if (TREE_READONLY (newdecl))
+	    TREE_READONLY (olddecl) = 1;
+	  /* Merge the volatile type qualifier.  */
+	  if (TREE_THIS_VOLATILE (newdecl))
+	    TREE_THIS_VOLATILE (olddecl) = 1;
+	}
 
       /* Merge the initialization information.  */
       if (DECL_INITIAL (newdecl) == NULL_TREE
@@ -2209,14 +2223,29 @@ next_arg:;
 	  DECL_NO_INSTRUMENT_FUNCTION_ENTRY_EXIT (newdecl)
 	    |= DECL_NO_INSTRUMENT_FUNCTION_ENTRY_EXIT (olddecl);
 	  DECL_NO_LIMIT_STACK (newdecl) |= DECL_NO_LIMIT_STACK (olddecl);
-	  TREE_THIS_VOLATILE (newdecl) |= TREE_THIS_VOLATILE (olddecl);
-	  TREE_NOTHROW (newdecl) |= TREE_NOTHROW (olddecl);
-	  DECL_IS_MALLOC (newdecl) |= DECL_IS_MALLOC (olddecl);
 	  DECL_IS_OPERATOR_NEW (newdecl) |= DECL_IS_OPERATOR_NEW (olddecl);
-	  DECL_PURE_P (newdecl) |= DECL_PURE_P (olddecl);
-	  TREE_READONLY (newdecl) |= TREE_READONLY (olddecl);
-	  DECL_LOOPING_CONST_OR_PURE_P (newdecl) 
+	  DECL_LOOPING_CONST_OR_PURE_P (newdecl)
 	    |= DECL_LOOPING_CONST_OR_PURE_P (olddecl);
+
+	  if (merge_attr)
+	    {	
+	      TREE_THIS_VOLATILE (newdecl) |= TREE_THIS_VOLATILE (olddecl);
+	      TREE_THIS_VOLATILE (olddecl) |= TREE_THIS_VOLATILE (newdecl);
+	      TREE_NOTHROW (newdecl) |= TREE_NOTHROW (olddecl);
+	      TREE_NOTHROW (olddecl) |= TREE_NOTHROW (newdecl);
+	      TREE_READONLY (newdecl) |= TREE_READONLY (olddecl);	
+	      DECL_IS_MALLOC (newdecl) |= DECL_IS_MALLOC (olddecl);
+	      DECL_PURE_P (newdecl) |= DECL_PURE_P (olddecl);
+	    }
+	  else
+	    {
+	      /* Merge the noreturn bit.  */
+	      TREE_THIS_VOLATILE (olddecl) = TREE_THIS_VOLATILE (newdecl);
+	      TREE_READONLY (olddecl) = TREE_READONLY (newdecl);
+	      TREE_NOTHROW (olddecl) = TREE_NOTHROW (newdecl);
+	      DECL_IS_MALLOC (olddecl) = DECL_IS_MALLOC (newdecl);
+	      DECL_PURE_P (olddecl) = DECL_PURE_P (newdecl);
+	    }
 	  /* Keep the old RTL.  */
 	  COPY_DECL_RTL (olddecl, newdecl);
 	}
@@ -2381,17 +2410,33 @@ next_arg:;
 	  /* [temp.expl.spec/14] We don't inline explicit specialization
 	     just because the primary template says so.  */
 
-	  /* But still keep DECL_DISREGARD_INLINE_LIMITS in sync with
-	     the always_inline attribute.  */
-	  if (DECL_DISREGARD_INLINE_LIMITS (olddecl)
-	      && !DECL_DISREGARD_INLINE_LIMITS (newdecl))
+	  if (merge_attr)
+	    {
+	      /* But still keep DECL_DISREGARD_INLINE_LIMITS in sync with
+		 the always_inline attribute.  */
+	      if (DECL_DISREGARD_INLINE_LIMITS (olddecl)
+		  && !DECL_DISREGARD_INLINE_LIMITS (newdecl))
+		{
+		  if (DECL_DECLARED_INLINE_P (newdecl))
+		    DECL_DISREGARD_INLINE_LIMITS (newdecl) = true;
+		  else
+		    DECL_ATTRIBUTES (newdecl)
+		      = remove_attribute ("always_inline",
+					  DECL_ATTRIBUTES (newdecl));
+		}
+	    }
+	  else
 	    {
 	      if (DECL_DECLARED_INLINE_P (newdecl))
 		DECL_DISREGARD_INLINE_LIMITS (newdecl) = true;
-	      else
-		DECL_ATTRIBUTES (newdecl)
-		  = remove_attribute ("always_inline",
-				      DECL_ATTRIBUTES (newdecl));
+
+	      DECL_DECLARED_INLINE_P (olddecl)
+		= DECL_DECLARED_INLINE_P (newdecl);
+
+	      DECL_DISREGARD_INLINE_LIMITS (olddecl)
+		= DECL_DISREGARD_INLINE_LIMITS (newdecl);
+
+	      DECL_UNINLINABLE (olddecl) = DECL_UNINLINABLE (newdecl);
 	    }
 	}
       else if (new_defines_function && DECL_INITIAL (olddecl))
@@ -8917,7 +8962,8 @@ grokfndecl (tree ctype,
 					template_count,
 					2 * funcdef_flag +
 					4 * (friendp != 0) +
-                                        8 * concept_p);
+                                        8 * concept_p,
+					*attrlist);
   if (decl == error_mark_node)
     return NULL_TREE;
 
diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index 42fd872..9c2e5e6 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -24,6 +24,7 @@ along with GCC; see the file COPYING3.  If not see
      all methods must be provided in header files; can't use a source
      file that contains only the method templates and "just win".  */
 
+#include <string>
 #include "config.h"
 #include "system.h"
 #include "coretypes.h"
@@ -1473,8 +1474,10 @@ is_specialization_of_friend (tree decl, tree friend_decl)
 
 /* Register the specialization SPEC as a specialization of TMPL with
    the indicated ARGS.  IS_FRIEND indicates whether the specialization
-   is actually just a friend declaration.  Returns SPEC, or an
-   equivalent prior declaration, if available.
+   is actually just a friend declaration.  ATTRLIST is the list of
+   attributes that the specialization is declared with or NULL when
+   it isn't.  Returns SPEC, or an equivalent prior declaration, if
+   available.
 
    We also store instantiations of field packs in the hash table, even
    though they are not themselves templates, to make lookup easier.  */
@@ -2615,6 +2618,110 @@ check_unqualified_spec_or_inst (tree t, location_t loc)
     }
 }
 
+/* Warn for a template specialization SPEC that is missing some of a set
+   of function or type attributes that the template TEMPL is declared with.
+   ATTRLIST is a list of additional attributes that SPEC should be taken
+   to ultimately be declared with.  */
+
+static void
+warn_spec_missing_attributes (tree tmpl, tree spec, tree attrlist)
+{
+  if (DECL_FUNCTION_TEMPLATE_P (tmpl))
+    tmpl = DECL_TEMPLATE_RESULT (tmpl);
+
+  if (TREE_CODE (tmpl) != FUNCTION_DECL)
+    return;
+
+  /* Avoid warning if either declaration or its type is deprecated.  */
+  if (TREE_DEPRECATED (tmpl)
+      || TREE_DEPRECATED (spec))
+    return;
+
+  tree tmpl_type = TREE_TYPE (tmpl);
+  tree spec_type = TREE_TYPE (spec);
+
+  if (TREE_DEPRECATED (tmpl_type)
+      || TREE_DEPRECATED (spec_type)
+      || TREE_DEPRECATED (TREE_TYPE (tmpl_type))
+      || TREE_DEPRECATED (TREE_TYPE (spec_type)))
+    return;
+
+  tree tmpl_attrs[] = { DECL_ATTRIBUTES (tmpl), TYPE_ATTRIBUTES (tmpl_type) };
+  tree spec_attrs[] = { DECL_ATTRIBUTES (spec), TYPE_ATTRIBUTES (spec_type) };
+
+  if (!spec_attrs[0])
+    spec_attrs[0] = attrlist;
+  else if (!spec_attrs[1])
+    spec_attrs[1] = attrlist;
+
+  /* Avoid warning if the primary has no attributes.  */
+  if (!tmpl_attrs[0] && !tmpl_attrs[1])
+    return;
+
+  /* Avoid warning if either declaration contains an attribute on
+     the white list below.  */
+  const char* const whitelist[] = {
+    "error", "warning"
+  };
+
+  for (unsigned i = 0; i != 2; ++i)
+    for (unsigned j = 0; j != sizeof whitelist / sizeof *whitelist; ++j)
+      if (lookup_attribute (whitelist[j], tmpl_attrs[i])
+	  || lookup_attribute (whitelist[j], spec_attrs[i]))
+	return;
+
+  /* Avoid warning if the difference between the primary and
+     the specialization is not in one of the attributes below.  */
+  const char* const blacklist[] = {
+    "alloc_align", "alloc_size", "assume_aligned", "format",
+    "format_arg", "malloc", "nonnull"
+  };
+
+  /* Put together a list of the black listed attributes that the primary
+     template is declared with that the specialization is not, in case
+     it's not apparent from the most recent declaration of the primary.  */
+  unsigned nattrs = 0;
+  std::string str;
+
+  for (unsigned i = 0; i != sizeof blacklist / sizeof *blacklist; ++i)
+    {
+      for (unsigned j = 0; j != 2; ++j)
+	{
+	  if (!lookup_attribute (blacklist[i], tmpl_attrs[j]))
+	    continue;
+
+	  for (unsigned k = 0; k != 1 + !!spec_attrs[1]; ++k)
+	    {
+	      if (lookup_attribute (blacklist[i], spec_attrs[k]))
+		break;
+
+	      if (str.size ())
+		str += ", ";
+	      str += "%<";
+	      str += blacklist[i];
+	      str += "%>";
+	      ++nattrs;
+	    }
+	}
+    }
+
+  if (!nattrs)
+    return;
+
+  if (warning_at (DECL_SOURCE_LOCATION (spec), OPT_Wmissing_attributes,
+		  "explicit specialization %q#D may be missing attributes",
+		  spec))
+    {
+      if (nattrs > 1)
+	str = G_("missing primary template attributes ") + str;
+      else
+	str = G_("missing primary template attribute ") + str;
+
+      inform (DECL_SOURCE_LOCATION (tmpl), str.c_str ());
+    }
+
+}
+
 /* Check to see if the function just declared, as indicated in
    DECLARATOR, and in DECL, is a specialization of a function
    template.  We may also discover that the declaration is an explicit
@@ -2656,7 +2763,8 @@ tree
 check_explicit_specialization (tree declarator,
 			       tree decl,
 			       int template_count,
-			       int flags)
+			       int flags,
+			       tree attrlist)
 {
   int have_def = flags & 2;
   int is_friend = flags & 4;
@@ -3113,8 +3221,13 @@ check_explicit_specialization (tree declarator,
 	     it again.  Partial specializations will be registered in
 	     process_partial_specialization.  */
 	  if (!processing_template_decl)
-	    decl = register_specialization (decl, gen_tmpl, targs,
-					    is_friend, 0);
+	    {
+	      warn_spec_missing_attributes (gen_tmpl, decl, attrlist);
+
+	      decl = register_specialization (decl, gen_tmpl, targs,
+					      is_friend, 0);
+	    }
+
 
 	  /* A 'structor should already have clones.  */
 	  gcc_assert (decl == error_mark_node
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index e70e2ba..8d366c6 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -295,7 +295,7 @@ Objective-C and Objective-C++ Dialects}.
 -Winvalid-pch  -Wlarger-than=@var{len} @gol
 -Wlogical-op  -Wlogical-not-parentheses  -Wlong-long @gol
 -Wmain  -Wmaybe-uninitialized  -Wmemset-elt-size  -Wmemset-transposed-args @gol
--Wmisleading-indentation  -Wmissing-braces @gol
+-Wmisleading-indentation  -Wmissing-attributes -Wmissing-braces @gol
 -Wmissing-field-initializers  -Wmissing-include-dirs @gol
 -Wno-multichar  -Wmultistatement-macros  -Wnonnull  -Wnonnull-compare @gol
 -Wnormalized=@r{[}none@r{|}id@r{|}nfc@r{|}nfkc@r{]} @gol
@@ -3928,6 +3928,7 @@ Options} and @ref{Objective-C and Objective-C++ Dialect Options}.
 -Wmemset-elt-size @gol
 -Wmemset-transposed-args @gol
 -Wmisleading-indentation @r{(only for C/C++)} @gol
+-Wmissing-attributes @gol
 -Wmissing-braces @r{(only for C/ObjC)} @gol
 -Wmultistatement-macros  @gol
 -Wnarrowing @r{(only for C++)}  @gol
@@ -4591,6 +4592,36 @@ about the layout of the file that the directive references.
 
 This warning is enabled by @option{-Wall} in C and C++.
 
+@item -Wmissing-attributes
+@opindex Wmissing-attributes
+@opindex Wno-missing-attributes
+Warn when a declaration of a function is missing one or more attributes
+that a related function is declared with and whose absence may adversely
+affect the correctness or efficiency of generated code.  For example, in
+C++, the warning is issued when an explicit specialization of a primary
+template declared with attribute @code{alloc_align}, @code{alloc_size},
+@code{assume_aligned}, @code{format}, @code{format_arg}, @code{malloc},
+or @code{nonnull} is declared without it.  Attributes @code{deprecated},
+@code{error}, and @code{warning} suppress the warning.
+(@pxref{Function Attributes}).
+
+@option{-Wmissing-attributes} is enabled by @option{-Wall}.
+
+For example, since the declaration of the primary function template
+below makes use of both attribute @code{malloc} and @code{alloc_size}
+the declaration of the explicit specialization of the template is
+diagnosed because it is missing one of the attributes.
+
+@smallexample
+template <class T>
+T* __attribute__ ((malloc, alloc_size (1)))
+allocate (size_t);
+
+template <>
+void* __attribute__ ((malloc))   // missing alloc_size
+allocate<void> (size_t);
+@end smallexample
+
 @item -Wmissing-braces
 @opindex Wmissing-braces
 @opindex Wno-missing-braces
diff --git a/gcc/print-tree.c b/gcc/print-tree.c
index cba8bac..caf5f26 100644
--- a/gcc/print-tree.c
+++ b/gcc/print-tree.c
@@ -377,6 +377,8 @@ print_node (FILE *file, const char *prefix, tree node, int indent,
 	fputs (" function-specific-opt", file);
       if (code == FUNCTION_DECL && DECL_DECLARED_INLINE_P (node))
 	fputs (" autoinline", file);
+      if (code == FUNCTION_DECL && DECL_UNINLINABLE (node))
+	fputs (" uninlinable", file);
       if (code == FUNCTION_DECL && DECL_BUILT_IN (node))
 	fputs (" built-in", file);
       if (code == FUNCTION_DECL && DECL_STATIC_CHAIN (node))
diff --git a/gcc/testsuite/g++.dg/Wmissing-attributes.C b/gcc/testsuite/g++.dg/Wmissing-attributes.C
new file mode 100644
index 0000000..f4ebce1
--- /dev/null
+++ b/gcc/testsuite/g++.dg/Wmissing-attributes.C
@@ -0,0 +1,102 @@
+// PR c++/83871 - wrong code for attribute const and pure on distinct
+// template specializations
+// Test to verify that a declaration of an explicit specialization with
+// no attributes is diagnosed when the primary template is declared with
+// one or more attributes.  The warning helps highlight a change in GCC
+// 8 from previous versions that copied the attributes from the primary
+// to the specialization.  It also helps point out simply forgetting to
+// declare the specialization with an attribute.
+// { dg-do compile }
+// { dg-options "-Wmissing-attributes" }
+
+#define ATTR(list)   __attribute__ (list)
+
+
+// Verify that a primary without attributes doesn't cause warnings.
+template <class T> void fnoattr ();
+
+template <> void fnoattr<void>();
+template <> void ATTR ((cold)) fnoattr<int>();
+template <> void ATTR ((hot)) fnoattr<double>();
+
+// Verify that a noreturn primary also doesn't cause warnings.
+template <class T> int ATTR ((noreturn)) fnoreturn ();
+
+template <> int fnoreturn<void>();
+template <> int ATTR ((cold)) fnoreturn<int>();
+template <> int ATTR ((hot)) fnoreturn<double>();
+
+
+template <class T>
+void*
+ATTR ((malloc, alloc_size (1)))
+missing_all (int);            // { dg-message "missing primary template attributes \(.malloc., .alloc_size.|.alloc_size., .malloc.\)" }
+
+template <>
+void*
+missing_all<char>(int);       // { dg-warning "explicit specialization .\[^\n\r\]+. may be missing attributes" }
+
+// Verify that specifying the same attributes in whatever order
+// doesn't trigger the warning, even when other attributes are
+// added.
+template <>
+void*
+ATTR ((alloc_size (1), malloc))
+missing_all<char>(int);
+
+template <>
+void*
+ATTR ((alloc_size (1))) ATTR ((malloc)) ATTR ((returns_nonnull))
+missing_all<char>(int);   // T = char, same as above
+
+template <>
+void*
+ATTR ((hot)) ATTR ((alloc_size (1))) ATTR ((malloc))
+missing_all<char>(int);   // T = char, same as above
+
+// Verify that the following attributes suppress the warning.
+template <> void* ATTR ((error (""))) missing_all<short>(int);
+template <> void* ATTR ((deprecated)) missing_all<int>(int);
+template <> void* ATTR ((warning (""))) missing_all<double>(int);
+
+
+template <class T>
+void*
+ATTR ((malloc, alloc_size (1)))
+missing_malloc (int);             // { dg-message "missing primary template attribute .malloc." }
+
+template <>
+void*
+ATTR ((alloc_size (1)))
+missing_malloc<char>(int);            // { dg-warning "explicit specialization .\[^\n\r\]+. may be missing attributes" }
+
+template <> void* ATTR ((malloc, alloc_size (1))) missing_malloc<short>(int);
+template <> void* ATTR ((deprecated)) missing_malloc<int>(int);
+template <> void* ATTR ((error (""))) missing_malloc<long>(int);
+template <> void* ATTR ((warning (""))) missing_malloc<double>(int);
+
+template <class T>
+void*
+ATTR ((malloc, alloc_size (1)))
+missing_alloc_size (int, int);        // { dg-message "missing primary template attribute .alloc_size." }
+
+template <>
+void*
+ATTR ((malloc))
+missing_alloc_size<char>(int, int);   // { dg-warning "explicit specialization .\[^\n\r\]+. may be missing attributes" }
+
+
+template <class T>
+void*
+ATTR ((nonnull (1)))
+missing_nonnull (void*);              // { dg-message "missing primary template attribute .nonnull." }
+
+template <>
+void*
+ATTR ((malloc))
+missing_nonnull<char>(void*);         // { dg-warning "explicit specialization .\[^\n\r\]+. may be missing attributes" }
+
+template <> void* ATTR ((nonnull (1))) missing_nonnull<short>(void*);
+template <> void* ATTR ((deprecated)) missing_nonnull<int>(void*);
+template <> void* ATTR ((error (""))) missing_nonnull<long>(void*);
+template <> void* ATTR ((warning (""))) missing_nonnull<double>(void*);
diff --git a/gcc/testsuite/g++.dg/ext/attr-const-pure.C b/gcc/testsuite/g++.dg/ext/attr-const-pure.C
new file mode 100644
index 0000000..f7c6f3b
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/attr-const-pure.C
@@ -0,0 +1,144 @@
+// Bug c++/83503 - bogus -Wattributes for const and pure on function template
+// specialization
+// { dg-do compile }
+// { dg-options "-O -Wall -fdump-tree-optimized" }
+
+int global;
+
+template <class T>
+int __attribute__ ((pure))
+f (T);
+
+template <>
+int __attribute__ ((const)) f<int> (int);   // { dg-bogus "ignoring attribute .const." }
+
+void f_pure_primary_elim ();
+void f_pure_primary_keep ();
+void f_const_spec_elim ();
+
+void call_pure_primary_elim (double x)
+{
+  // Only the first call to f(x) must be emitted, the second one
+  // is expected to be eliminated because the primary template
+  // is pure.
+  int i0 = f (x);
+  int i1 = f (x);
+  if (i0 != i1)
+    f_pure_primary_elim ();
+}
+
+void call_pure_primary_keep (const char *s)
+{
+  // Both calls to f(x) must be emitted because the primary is
+  // pure and may read global.
+  int i0 = f (s);
+  global = 123;
+  int i1 = f (s);
+  if (i0 != i1)
+    f_pure_primary_keep ();
+}
+
+void call_const_spec_elim (int i)
+{
+  // Only the first call to f(x) must be emitted, the second
+  // one is expected to be eliminated again, this time because
+  // unlike the pure primary, the specialization is const.
+  int i0 = f (i);
+  global = 123;
+  int i1 = f (i);
+  if (i0 != i1)
+    f_const_spec_elim ();
+}
+
+template <class T>
+int __attribute__ ((const))
+g (T);
+
+template <>
+int __attribute__ ((pure)) g<int> (int);   // { dg-bogus "ignoring attribute .const." }
+
+template <class T>
+int __attribute__ ((const))
+h (T);
+
+template <class T>
+int __attribute__ ((pure))
+h (const T*);
+
+template <>
+int h<int> (int);
+
+template <>
+int h<int*> (int*);
+
+extern void h_const_primary_elim ();
+extern void h_pure_cstptr_elim ();
+extern void h_cstptr_keep ();
+extern void h_int_keep ();
+extern void h_intptr_keep ();
+
+void call_const_primary_elim (double x)
+{
+  // Only the first call to h(x) must be emitted, the second one
+  // is expected to be eliminated.
+  int i0 = h (x);
+  int i1 = h (x);
+
+  if (i0 != i1)                   // must be folded into false
+    h_const_primary_elim ();        // must be eliminated
+}
+
+void call_pure_cstptr_elim (const void *p)
+{
+  // Only the first call to h(x) must be emitted, the second one
+  // is expected to be eliminated.  This verifies that h<const
+  // void*>*() is treated as const in this context.
+  int i0 = h (p);
+  int i1 = h (p);
+
+  if (i0 != i1)                   // must be folded into false
+    h_pure_cstptr_elim ();          // must be eliminated
+}
+
+void call_cstptr_keep (const void *p)
+{
+  // Because of the store to the global, both calls to h(p) must
+  // be emitted.  This verifies that h<const void*>*() is not
+  // treated as const.
+  int i0 = h (p);
+  global = 123;
+  int i1 = h (p);
+
+  if (i0 != i1)                   // must not be folded
+    h_cstptr_keep ();             // must be emitted
+}
+
+void call_int_keep (int i)
+{
+  // Both calls to h(i) must be emitted.
+  int i0 = h (i);
+  int i1 = h (i);
+
+  if (i0 != i1)                   // must not be folded
+    h_int_keep ();                // must be emitted
+}
+
+void call_intptr_keep (int *ip)
+{
+  // Both calls to h(ip) must be emitted.
+  int i0 = h (ip);
+  int i1 = h (ip);
+
+  if (i0 != i1)                   // must not be folded
+    h_intptr_keep ();             // must be emitted
+}
+
+// { dg-final { scan-tree-dump-not "f_pure_primary_elim" "optimized" } }
+// { dg-final { scan-tree-dump-not "f_const_primary_elim" "optimized" } }
+// { dg-final { scan-tree-dump-not "f_const_spec_elim" "optimized" } }
+// { dg-final { scan-tree-dump-not "h_pure_cstptr_elim" "optimized" } }
+
+// { dg-final { scan-tree-dump-times "f_pure_primary_keep" 1 "optimized" } }
+// { dg-final { scan-tree-dump-times "h_cstptr_keep" 1 "optimized" } }
+// { dg-final { scan-tree-dump-times "h_int_keep" 1 "optimized" } }
+// { dg-final { scan-tree-dump-times "h_intptr_keep" 1 "optimized" } }
diff --git a/gcc/testsuite/g++.dg/ext/attr-const.C b/gcc/testsuite/g++.dg/ext/attr-const.C
new file mode 100644
index 0000000..a9884db
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/attr-const.C
@@ -0,0 +1,69 @@
+/*  PR c++/83871 - wrong code for attribute const and pure on distinct
+    template specializations
+    { dg-do compile }
+    { dg-options "-O -Wall" } */
+
+int __attribute__ ((const)) fconst_none ();
+int fconst_none ();
+
+void test_const_none_failed ();
+
+void func_const_none ()
+{
+  int i0 = fconst_none ();
+  int i1 = fconst_none ();
+  if (i0 != i1)
+    test_const_none_failed ();
+
+  // { dg-final { scan-tree-dump-not "test_const_none_failed" "optimized" } }
+}
+
+
+int fnone_const ();
+int __attribute__ ((const)) fnone_const ();
+
+void test_none_const_failed ();
+
+void func_none_const ()
+{
+  int i0 = fnone_const ();
+  int i1 = fnone_const ();
+  if (i0 != i1)
+    test_none_const_failed ();
+
+  // { dg-final { scan-tree-dump-not "test_none_const_failed" "optimized" } }
+}
+
+
+template <class T>
+int __attribute__ ((const)) fconst_none (T);
+
+template <class T>
+int fconst_none (T);
+
+void template_const_none ()
+{
+  int i0 = fconst_none<int> (0);
+  int i1 = fconst_none<int> (0);
+  if (i0 != i1)
+    test_const_none_failed ();
+
+  // { dg-final { scan-tree-dump-not "test_const_none_failed" "optimized" } }
+}
+
+
+template <class T>
+int fnone_const (T);
+
+template <class T>
+int __attribute__ ((const)) fnone_const (T);
+
+void test_fnone_const ()
+{
+  int i0 = fnone_const<int> (0);
+  int i1 = fnone_const<int> (0);
+  if (i0 != i1)
+    test_none_const_failed ();
+
+  // { dg-final { scan-tree-dump-not "test_none_const_failed" "optimized" } }
+}
diff --git a/gcc/testsuite/g++.dg/ext/attr-deprecated-2.C b/gcc/testsuite/g++.dg/ext/attr-deprecated-2.C
new file mode 100644
index 0000000..bf90415
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/attr-deprecated-2.C
@@ -0,0 +1,35 @@
+// Bug c++/83871 - wrong code due to attributes on distinct template
+// specializations
+// Test to verify that an explicit template specifialization does not
+// "inherit" attribute deprecated from a primary template declared
+// with it.
+// { dg-do compile }
+// { dg-options "-Wall -fdump-tree-optimized" }
+
+struct Special;
+
+template <class T>
+void fdeprecated_primary ();
+
+// The primary isn't deprecated at this point so the declaration
+// of its specialization should not be diagnosed.
+template <>
+void fdeprecated_primary<Special> ();   // { dg-bogus "deprecated" }
+
+template <class T>
+void __attribute__ ((deprecated))
+fdeprecated_primary ();
+
+void use_primary ()
+{
+  // Verify that uses of the now deprecacted primary are diagnosed.
+  fdeprecated_primary<void>();          // { dg-warning "deprecated" "bug 84542" { xfail *-*-* } }
+  fdeprecated_primary<int>();           // { dg-warning "deprecated" "bug 84542" { xfail *-*-* } }
+}
+
+void use_special ()
+{
+  // Verify that the use of the non-deprecated specializatoin
+  // is not diagnosed.
+  fdeprecated_primary<Special>();
+}
diff --git a/gcc/testsuite/g++.dg/ext/attr-malloc-2.C b/gcc/testsuite/g++.dg/ext/attr-malloc-2.C
new file mode 100644
index 0000000..600d430
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/attr-malloc-2.C
@@ -0,0 +1,49 @@
+// Bug c++/83503 - bogus -Wattributes for const and pure on function template
+// specialization
+// Test to verify that attribute malloc on multiple declarations of
+// the same ordinary function are merged.
+// { dg-do compile }
+// { dg-options "-O -Wall -fdump-tree-optimized" }
+
+void* __attribute__ ((malloc))
+fmalloc_none (unsigned);
+
+void*
+fmalloc_none (unsigned);
+
+static char a[8];
+
+void fmalloc_none_failed ();
+
+void test_fmalloc_none (void)
+{
+  void *p = fmalloc_none (1);
+  if (!p)
+    return;
+
+  if (p == a)                     // must be false
+    fmalloc_none_failed ();       // should be eliminated
+
+  // Verify that the call to fmalloc_none() is eliminated.
+  // { dg-final { scan-tree-dump-not "fmalloc_none_failed" "optimized" } }
+}
+
+void* fnone_malloc (unsigned);
+
+void* __attribute__ ((malloc))
+fnone_malloc (unsigned);
+
+void fnone_malloc_failed ();
+
+void test_fnone_malloc (void)
+{
+  void *p = fnone_malloc (1);
+  if (!p)
+    return;
+
+  if (p == a)                     // must be false
+    fnone_malloc_failed ();       // should be eliminated
+
+  // Verify that the call to fnone_malloc() is eliminated.
+  // { dg-final { scan-tree-dump-not "fnone_malloc_failed" "optimized" } }
+}
diff --git a/gcc/testsuite/g++.dg/ext/attr-malloc.C b/gcc/testsuite/g++.dg/ext/attr-malloc.C
new file mode 100644
index 0000000..3cbb414
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/attr-malloc.C
@@ -0,0 +1,69 @@
+// Bug c++/83503 - bogus -Wattributes for const and pure on function template
+// specialization
+// Test to verify that an explicit template specifialization does not
+// "inherit" attribute malloc from a primary template declared with one.
+// { dg-do compile }
+// { dg-options "-O -Wall -fdump-tree-optimized" }
+
+template <class>
+void* __attribute__ ((malloc))
+fmalloc (unsigned);
+
+template <>
+void*
+fmalloc<int>(unsigned);       // { dg-warning "may be missing attributes" }
+
+static char a[8];
+
+void fmalloc_void_malloc ();
+void fmalloc_int_not_malloc ();
+
+void test_fmalloc_primary (void)
+{
+  void *p = fmalloc<void>(1);
+  if (!p)
+    return;
+
+  if (p == a)                     // must be false
+    fmalloc_void_malloc ();       // should be eliminated
+
+  // Verify that the call to fmalloc_void_malloc() is eliminated.
+  // { dg-final { scan-tree-dump-not "fmalloc_void_malloc" "optimized" } }
+}
+
+
+void test_fmalloc_spec_none (void)
+{
+  void *p = fmalloc<int>(1);
+  if (!p)
+    return;
+
+  if (p == a)                     // can be true
+    fmalloc_int_not_malloc ();    // must not be eliminated
+
+  // Verify that the call to fmalloc_int_not_malloc() is retained.
+  // { dg-final { scan-tree-dump-times "fmalloc_int_not_malloc" 1 "optimized" } }
+}
+
+template <>
+void*
+fmalloc<long>(unsigned);          // { dg-warning "may be missing attributes" }
+
+template <>
+void* __attribute__ ((malloc))
+fmalloc<long>(unsigned);
+
+void fmalloc_long_malloc ();
+
+void test_fmalloc_spec_malloc (void)
+{
+  void *p = fmalloc<long>(1);
+  if (!p)
+    return;
+
+  if (p == a)                     // can be true
+    fmalloc_long_malloc ();       // must not be eliminated
+
+  // Verify that the call to fmalloc_long_malloc() is eliminated.
+  // { dg-final { scan-tree-dump-not "fmalloc_long_malloc" "optimized" } }
+}
diff --git a/gcc/testsuite/g++.dg/ext/attr-noinline-2.C b/gcc/testsuite/g++.dg/ext/attr-noinline-2.C
new file mode 100644
index 0000000..4aab4f1
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/attr-noinline-2.C
@@ -0,0 +1,73 @@
+// Bug c++/83871 - wrong code due to attributes on distinct template
+// specializations
+// Test to verify that an explicit template specifialization does not
+// "inherit" attributes always_inline or noinline from a primary template
+// declared with either.  Unlike attr-noinline.C, this test enables
+// optimization to verify that noinline prevents inlining.
+// { dg-do compile }
+// { dg-options "-O2 -Wall -fdump-tree-optimized" }
+
+enum Special { };
+
+int global;
+
+template <class T>
+inline void __attribute__ ((always_inline))
+falways_inline_noinline ()
+{
+  // Create a side-effect that's unique to this function.
+  global = __LINE__;
+}
+
+template <>
+void __attribute__ ((noinline))
+falways_inline_noinline<Special>()
+{
+  global = __LINE__;
+}
+
+// Verify that a call to the primary is inlined but one to
+// the explicit specialization is not.
+
+void test_elim_primary_1 (void)
+{
+  // Should be inlined.
+  falways_inline_noinline<void>();
+// { dg-final { scan-tree-dump-not "falways_inline_noinline<void> *\\(\\)" "optimized" } }
+}
+
+void test_keep_special_1 (void)
+{
+  // Should not be inlined.
+  falways_inline_noinline<Special>();
+// { dg-final { scan-tree-dump-times "falways_inline_noinline<Special> *\\(\\);" 1 "optimized" } }
+}
+
+
+template <class T>
+void __attribute__ ((noinline))
+fnoinline_always_inline ()
+{
+  global = __LINE__;
+}
+
+template <>
+inline void __attribute__ ((always_inline))
+fnoinline_always_inline<Special>()    // { dg-bogus "follows declaration" }
+{
+  global = __LINE__;
+}
+
+void test_keep_primary_2 (void)
+{
+  // Should not be inlined.
+  fnoinline_always_inline<void>();
+// { dg-final { scan-tree-dump-times "fnoinline_always_inline<void> *\\(\\);" 1 "optimized" } }
+}
+
+void test_elim_special_2 (void)
+{
+  // Should be inlined.
+  fnoinline_always_inline<Special>();
+// { dg-final { scan-tree-dump-not "fnoinline_always_inline<Special> *\\(\\);" optimized" } }
+}
diff --git a/gcc/testsuite/g++.dg/ext/attr-noinline.C b/gcc/testsuite/g++.dg/ext/attr-noinline.C
new file mode 100644
index 0000000..b09037b
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/attr-noinline.C
@@ -0,0 +1,128 @@
+// Bug c++/83871 - wrong code due to attributes on distinct template
+// specializations
+// Test to verify that an explicit template specifialization does not
+// "inherit" attributes always_inline or noinline from a primary template
+// declared with either.  The test disables optimization to verify that
+// always_inline forces inlining.
+// { dg-do compile }
+// { dg-options "-O0 -Wall -fdump-tree-optimized" }
+
+enum Special { };
+
+template <class T>
+inline void __attribute__ ((always_inline))
+falways_inline_none ()
+{
+  // Primary template should always be inlined, even without optimization.
+  asm ("");   // induce a no-op "side-effect"
+}
+
+template <>
+inline void
+falways_inline_none<Special>()
+{
+  // The specialization should not be inlined without optimization, even
+  // though it's declared inline.
+  asm ("");
+}
+
+// Verify that a call to the primary is inlined but one to
+// the explicit specialization is not.
+
+void test_elim_primary_1 (void)
+{
+  // Should be inlined.
+  falways_inline_none<void>();
+// { dg-final { scan-tree-dump-not "falways_inline_none<void> *\\(\\)" "optimized" } }
+}
+
+void test_keep_special_1 (void)
+{
+  // Should not be inlined.
+  falways_inline_none<Special>();
+// { dg-final { scan-tree-dump-times "falways_inline_none<Special> *\\(\\);" 1 "optimized" } }
+}
+
+
+template <class T>
+inline void __attribute__ ((always_inline))
+falways_inline_noinline ()
+{
+  asm ("");   // induce a no-op "side-effect"
+}
+
+template <>
+void __attribute__ ((noinline))
+falways_inline_noinline<Special>() { asm (""); }
+
+// Verify that a call to the primary is inlined but one to
+// the explicit specialization is not.
+
+void test_elim_primary_2 (void)
+{
+  falways_inline_noinline<void>();
+// { dg-final { scan-tree-dump-not "falways_inline_noinline<void> *\\(\\)" "optimized" } }
+}
+
+void test_keep_special_2 (void)
+{
+  falways_inline_noinline<Special>();
+// { dg-final { scan-tree-dump-times "falways_inline_noinline<Special> *\\(\\);" 1 "optimized" } }
+}
+
+
+template <class T>
+inline void
+fnone_always_inline ()
+{
+  asm ("");   // induce a no-op "side-effect"
+}
+
+template <>
+inline void __attribute__ ((always_inline))
+fnone_always_inline<Special>() { asm (""); }
+
+// Verify that a call to the primary is not inlined but one to
+// the explicit specialization is.
+
+void test_keep_primary_3 (void)
+{
+  fnone_always_inline<void>();
+// { dg-final { scan-tree-dump-times "fnone_always_inline<void> *\\(\\);" 1 "optimized" } }
+}
+
+void test_elim_special_3 (void)
+{
+  fnone_always_inline<Special>();
+// { dg-final { scan-tree-dump-not "fnone_always_inline<Special> *\\(\\);" optimized" } }
+}
+
+
+template <class T>
+void __attribute__ ((noinline))
+fnoinline_always_inline ()
+{
+  asm ("");   // induce a no-op "side-effect"
+}
+
+template <>
+inline void __attribute__ ((always_inline))
+fnoinline_always_inline<Special>()    // { dg-bogus "follows declaration" }
+{
+  asm ("");
+}
+
+// Verify that a call to the primary is not inlined but one to
+// the explicit specialization is.
+
+void test_keep_primary_4 (void)
+{
+  fnoinline_always_inline<void>();
+// { dg-final { scan-tree-dump-times "fnoinline_always_inline<void> *\\(\\);" 1 "optimized" } }
+}
+
+void test_elim_special_4 (void)
+{
+  fnoinline_always_inline<Special>();
+// { dg-final { scan-tree-dump-not "fnoinline_always_inline<Special> *\\(\\);" optimized" } }
+}
diff --git a/gcc/testsuite/g++.dg/ext/attr-nonnull.C b/gcc/testsuite/g++.dg/ext/attr-nonnull.C
new file mode 100644
index 0000000..57d2cb0
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/attr-nonnull.C
@@ -0,0 +1,31 @@
+// Bug c++/83871 - wrong code due to attributes on distinct template
+// specializations
+// Test to verify that an explicit function template specifialization
+// does not "inherit" attribute nonnull from an argument declared with
+// one in the primary template.
+// { dg-do compile }
+// { dg-options "-O -Wall -fdump-tree-optimized" }
+
+template <class T>
+void __attribute__ ((nonnull (1)))
+f (T*, T*, T*);
+
+template <>
+void
+f<int>(int*, int*, int*);     // { dg-warning "may be missing attributes" }
+
+template <>
+void __attribute__ ((nonnull (3)))
+f<float>(float*, float*, float*);
+
+
+void test_nonnull (void)
+{
+  f<void>(0, 0, 0);           // { dg-warning "null argument where non-null required \\\(argument 1\\\)" }
+
+  f<int>(0, 0, 0);            // { dg-bogus "null argument" }
+
+  f<float>(0, 0, 0);
+  // { dg-bogus "null argument where non-null required \\\(argument 1\\\)" "" { target *-*-* } .-1 }
+  // { dg-warning "null argument where non-null required \\\(argument 3\\\)" "" { target *-*-* } .-2 }
+}
diff --git a/gcc/testsuite/g++.dg/ext/attr-noreturn-2.C b/gcc/testsuite/g++.dg/ext/attr-noreturn-2.C
new file mode 100644
index 0000000..cf70ba1
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/attr-noreturn-2.C
@@ -0,0 +1,47 @@
+/*  PR c++/83871 - wrong code for attribute const and pure on distinct
+    template specializations
+    Test to verify that attributes noreturn on multiple declarations of
+    the same function are merged.
+    { dg-do compile }
+    { dg-options "-O -fdump-tree-eh" } */
+
+int __attribute__ ((noreturn)) fnoreturn ();
+
+void fnoreturn_failed ();
+
+int test_noreturn () throw ()
+{
+  fnoreturn ();
+  fnoreturn_failed ();
+  // Verify that the call to fnoreturn_failed() is eliminated.
+  // { dg-final { scan-tree-dump-not "fnoreturn_failed" "optimized" } }
+
+  // Expect no -Wreturn-type warning despite the absence of a return
+  // statement in a non-void function.
+}
+
+
+int __attribute__ ((noreturn)) fnoreturn_none ();
+int fnoreturn_none ();
+
+void fnoreturn_none_failed ();
+
+
+int test_noreturn_none ()
+{
+  fnoreturn_none ();
+  fnoreturn_none_failed ();
+  // { dg-final { scan-tree-dump-not "fnoreturn_none_failed" "optimized" } }
+}
+
+int fnone_noreturn ();
+int __attribute__ ((noreturn)) fnone_noreturn ();
+
+void fnone_noreturn_failed ();
+
+int test_none_noreturn () throw ()
+{
+  fnone_noreturn ();
+  fnone_noreturn_failed ();
+  // { dg-final { scan-tree-dump-not "fnone_noreturn_failed" "optimized" } }
+}
diff --git a/gcc/testsuite/g++.dg/ext/attr-noreturn.C b/gcc/testsuite/g++.dg/ext/attr-noreturn.C
new file mode 100644
index 0000000..7d053d8
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/attr-noreturn.C
@@ -0,0 +1,80 @@
+// Bug c++/83871 - wrong code due to attributes on distinct template
+// specializations
+// Test to verify that an explicit template specifialization does not
+// "inherit" attribute noreturn from a primary template declared with
+// one.
+// { dg-do compile }
+// { dg-options "-O -Wall -fdump-tree-optimized" }
+
+struct Noreturn { };
+struct Returns { };
+
+// Primary declared noreturn but explicit specialization is not.
+template <class T> int __attribute__ ((noreturn)) f ();
+template <>        int                            f<Returns>();
+
+// Explicit specialization is noreturn but primary is not.
+template <class T> int g ();
+template <>        int __attribute__ ((noreturn)) g<Noreturn>();
+
+int val;
+
+int test_primary_noreturn (char, short)
+{
+  // Only the first call should be emitted, the second one should
+  // be eliminated because the first one doesn't return.
+  val = f<char>() + f<short>();
+}   // expect no -Wreturn-type warning here
+
+int test_noreturn (int)
+{
+  // Call should be retained.
+  f<int>();
+}   // expect no -Wreturn-type warning here
+
+int test_special_return (int)
+{
+  // Both calls must be emitted.
+  int val = f<Returns>() + f<Returns>();
+  (void)&val;
+}   // { dg-warning "no return statement in function returning non-void" }
+
+
+int test_primary_return (void)
+{
+  int val = g<char>() + g<int>();
+  (void)&val;
+}   // { dg-warning "no return statement in function returning non-void" }
+
+
+int test_special_noreturn (int, long)
+{
+  g<Noreturn>();
+}   // expect no -Wreturn-type warning here
+
+
+// Verify that the call to f<short>() above is eliminated but the call
+// to f<int>() and the two calls to f<Returns>() are retained.
+// { dg-final { scan-tree-dump-not "f<short>" "optimized" } }
+// { dg-final { scan-tree-dump-times "f<Returns>" 2 "optimized" } }
+
+// Verify that the second call to f<Returns>() in test_special_return()
+// is followed by __builtin_unreachable() because there is no return
+// statement in the function.
+// { dg-final { scan-tree-dump-times "f<Returns> \\(\\);\[\n\r \]+__builtin_unreachable" 1 "optimized" } }
+
+
+// Verify that the call to g<short>() above is eliminated but the call
+// to g<char>() and to g<Noreturn>() are both retained.
+// { dg-final { scan-tree-dump-not "g<short>" "optimized" } }
+// { dg-final { scan-tree-dump-times "g<char>" 1 "optimized" } }
+// { dg-final { scan-tree-dump-times "g<Noreturn>" 1 "optimized" } }
+
+// Verify that the call to g<int>() in test_primary_return() is
+// followed by __builtin_unreachable() because there is no return
+// statement in the function.
+// { dg-final { scan-tree-dump-times "g<int> *\\(\\);\[\n\r \]+__builtin_unreachable" 1 "optimized" } }
+// Verify that the call to g<Noreturn>() in test_special_noreturn()
+// is not followed by __builtin_unreachable() even though there is no
+// return statement in the function.
+// { dg-final { scan-tree-dump-times "g<Noreturn> *\\(\\);\[\n\r \]+__builtin_unreachable" 0 "optimized" } }
diff --git a/gcc/testsuite/g++.dg/ext/attr-nothrow-2.C b/gcc/testsuite/g++.dg/ext/attr-nothrow-2.C
new file mode 100644
index 0000000..8f1d7af
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/attr-nothrow-2.C
@@ -0,0 +1,48 @@
+/*  PR c++/83871 - wrong code for attribute const and pure on distinct
+    template specializations
+    Test to verify that attributes nothrow on multiple declarations of
+    the same function are merged.
+    { dg-do compile }
+    { dg-options "-O -fdump-tree-eh" } */
+
+void __attribute__ ((nothrow)) fnothrow ();
+
+void test_nothrow () throw ()
+{
+  // No exception handling necessary around the call to fnothrow().
+  fnothrow ();
+}
+
+void __attribute__ ((nothrow)) fnothrow_none ();
+void fnothrow_none ();
+
+void test_nothrow_none () throw ()
+{
+  // No exception handling necessary around the call to fnothrow_none().
+  fnothrow_none ();
+}
+
+void fnone_nothrow ();
+void __attribute__ ((nothrow)) fnone_nothrow ();
+
+void test_none_nothrow () throw ()
+{
+  // No exception handling necessary around the call to fnone_nothrow().
+  fnone_nothrow ();
+}
+
+int __attribute__ ((nothrow)) fnothrow_noreturn_none ();
+int __attribute__ ((noreturn)) fnothrow_noreturn_none ();
+int fnothrow_noreturn_none ();
+
+int test_nothrow_noreturn_none () throw ()
+{
+  // No exception handling necessary around the call().
+  // No -Wreturn-value should be emitted because the function is
+  // declared noreturn.
+  fnothrow_noreturn_none ();
+}
+
+// Verify that no exception handling code was emitted.
+// { dg-final { scan-tree-dump-not "eh_dispatch" "eh" } }
+// { dg-final { scan-tree-dump-not "resx" "eh" } }
diff --git a/gcc/testsuite/g++.dg/ext/attr-nothrow.C b/gcc/testsuite/g++.dg/ext/attr-nothrow.C
new file mode 100644
index 0000000..1d3d715
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/attr-nothrow.C
@@ -0,0 +1,46 @@
+// Bug c++/83871 - wrong code due to attributes on distinct template
+// specializations
+// Test to verify that an explicit template specifialization does not
+// "inherit" attribute nothrow from a primary template declared with one.
+// { dg-do compile }
+// { dg-options "-O -Wall -fdump-tree-optimized" }
+
+template <class T>
+void __attribute__ ((nothrow))
+f ();
+
+template <>
+void f<int>();
+
+void f_void_nothrow ();
+void f_int_maythrow ();
+
+void fv (void)
+{
+  try
+    {
+      f<void>();
+    }
+  catch (...)                    // cannot be be reached
+    {
+      f_void_nothrow ();         // should be eliminated
+    }
+}
+
+
+void fi (void)
+{
+  try
+    {
+      f<int>();
+    }
+  catch (...)                    // may be reached
+    {
+      f_int_maythrow ();         // must not be eliminated
+    }
+}
+
+// Verify that the call to f_void_nothrow() is eliminated but
+// the call to f_int_maythrow() is retained.
+// { dg-final { scan-tree-dump-not "f_void_nothrow" "optimized" } }
+// { dg-final { scan-tree-dump-times "f_int_maythrow" 1 "optimized" } }
diff --git a/gcc/testsuite/g++.dg/ext/attr-optimize.C b/gcc/testsuite/g++.dg/ext/attr-optimize.C
new file mode 100644
index 0000000..481cf3f
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/attr-optimize.C
@@ -0,0 +1,46 @@
+// Bug c++/83871 - wrong code due to attributes on distinct template
+// specializations
+// Test to verify that an explicit template specifialization does not
+// "inherit" attribute optimize from a primary template declared with
+// one.
+// { dg-do compile }
+// { dg-options "-O2 -Wall -fdump-tree-optimized" }
+
+enum Special { };
+
+void foptimize_none_primary_failed ();
+
+template <class T>
+void __attribute__ ((optimize ("no-printf-return-value")))
+foptimize_none ()
+{
+  // The call to snprintf and the test should be retained.
+  if (2 != __builtin_snprintf (0, 0, "%hhx", 0x12))
+    foptimize_none_primary_failed ();
+}
+
+void foptimize_none_special_failed ();
+
+template <>
+inline void
+foptimize_none<Special>()
+{
+  // The whole if statement should be eliminated.
+  if (3 != __builtin_snprintf (0, 0, "1%hhx", 0x12))
+    foptimize_none_special_failed ();
+}
+
+void test_primary ()
+{
+  foptimize_none<void>();
+  // { dg-final { scan-tree-dump-times "foptimize_none_primary_failed *\\(\\)" 1 "optimized" } }
+}
+
+void test_special ()
+{
+  // Should be eliminated.
+  foptimize_none<Special>();
+// { dg-final { scan-tree-dump-not "foptimize_none_special_failed *\\(\\)" "optimized" } }
+}
+
+// { dg-final { scan-tree-dump-times "__builtin_snprintf" 1 "optimized" } }
diff --git a/gcc/testsuite/g++.dg/ext/attr-pure.C b/gcc/testsuite/g++.dg/ext/attr-pure.C
new file mode 100644
index 0000000..af36a31
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/attr-pure.C
@@ -0,0 +1,69 @@
+/*  PR c++/83871 - wrong code for attribute const and pure on distinct
+    template specializations
+    { dg-do compile }
+    { dg-options "-O -Wall" } */
+
+int __attribute__ ((pure)) fpure_none ();
+int fpure_none ();
+
+void test_pure_none_failed ();
+
+void func_pure_none ()
+{
+  int i0 = fpure_none ();
+  int i1 = fpure_none ();
+  if (i0 != i1)
+    test_pure_none_failed ();
+
+  // { dg-final { scan-tree-dump-not "test_pure_none_failed" "optimized" } }
+}
+
+
+int fnone_pure ();
+int __attribute__ ((pure)) fnone_pure ();
+
+void test_none_pure_failed ();
+
+void func_none_pure ()
+{
+  int i0 = fnone_pure ();
+  int i1 = fnone_pure ();
+  if (i0 != i1)
+    test_none_pure_failed ();
+
+  // { dg-final { scan-tree-dump-not "test_none_pure_failed" "optimized" } }
+}
+
+
+template <class T>
+int __attribute__ ((pure)) fpure_none (T);
+
+template <class T>
+int fpure_none (T);
+
+void template_pure_none ()
+{
+  int i0 = fpure_none<int> (0);
+  int i1 = fpure_none<int> (0);
+  if (i0 != i1)
+    test_pure_none_failed ();
+
+  // { dg-final { scan-tree-dump-not "test_pure_none_failed" "optimized" } }
+}
+
+
+template <class T>
+int fnone_pure (T);
+
+template <class T>
+int __attribute__ ((pure)) fnone_pure (T);
+
+void test_fnone_pure ()
+{
+  int i0 = fnone_pure<int> (0);
+  int i1 = fnone_pure<int> (0);
+  if (i0 != i1)
+    test_none_pure_failed ();
+
+  // { dg-final { scan-tree-dump-not "test_none_pure_failed" "optimized" } }
+}
diff --git a/gcc/testsuite/g++.dg/ext/attr-returns-nonnull.C b/gcc/testsuite/g++.dg/ext/attr-returns-nonnull.C
new file mode 100644
index 0000000..f75f32e
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/attr-returns-nonnull.C
@@ -0,0 +1,42 @@
+// Bug c++/83871 - wrong code due to attributes on distinct template
+// specializations
+// Test to verify that an explicit function template specifialization
+// does not "inherit" attribute nonnull from an argument declared with
+// one in the primary template.
+// { dg-do compile }
+// { dg-options "-O -Wall -fdump-tree-optimized" }
+
+template <class T>
+void* __attribute__ ((returns_nonnull))
+g ();
+
+template <>
+void*
+g<int>();
+
+extern void g_void_returns_nonnull ();
+extern void g_int_may_return_null ();
+
+void test_returns_nonnull ()
+{
+  void *p = g<void>();
+  if (!p)
+    g_void_returns_nonnull ();
+
+  (void)&p;
+}
+
+void test_may_return_null ()
+{
+  void *p = g<int>();
+  if (!p)
+    g_int_may_return_null ();
+
+  (void)&p;
+}
+
+
+// Verify that the call to g_void_returns_nonnull() is eliminated but
+// the call to g_int_may_return_null() is retained.
+// { dg-final { scan-tree-dump-not "g_void_returns_nonnull" "optimized" } }
+// { dg-final { scan-tree-dump-times "g_int_may_return_null" 1 "optimized" } }
diff --git a/gcc/testsuite/g++.dg/ext/attr-warning.C b/gcc/testsuite/g++.dg/ext/attr-warning.C
new file mode 100644
index 0000000..8369bac
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/attr-warning.C
@@ -0,0 +1,49 @@
+// Bug c++/83871 - wrong code due to attributes on distinct template
+// specializations
+// Test to verify that an explicit template specifialization does not
+// "inherit" attribute warning from a primary template declared with
+// it.
+// { dg-do compile }
+// { dg-options "-Wall -fdump-tree-optimized" }
+
+struct Special;
+
+// Primary has no attributes here.
+template <class T>
+void fwarn_primary ();
+
+// Uses of the primary template, including declarations of its
+// specializations, should not be diagnosed until after it has
+// been redeclared with attribute warning.
+template <>
+void fwarn_primary<Special> ();
+
+void use_primary_before_warning ()
+{
+  // Verify that uses of the primary are not diagnosed.
+  fwarn_primary<char>();
+  fwarn_primary<short>();
+}
+
+// Redeclare the primary with attribute warning.
+template <class T>
+void __attribute__ ((warning ("primary")))
+fwarn_primary ();
+
+// Attribute warning is special in that it only warns for functions
+// that are actually used, not those that are only declared.
+template <>
+void fwarn_primary<double> ();
+
+void use_primary_after_warning ()
+{
+  // Verify that uses of the redeclared primary are diagnosed.
+  fwarn_primary<int>();           // { dg-warning "primary" }
+  fwarn_primary<long>();          // { dg-warning "primary" }
+}
+
+void use_special ()
+{
+  // Verify that the use of the specializatoin is not diagnosed.
+  fwarn_primary<Special>();
+}

Reply via email to