On 02/05/2018 02:52 PM, Jason Merrill wrote:
On 02/04/2018 07:07 PM, Martin Sebor wrote:
To resolve the underlying root cause of the P1 bug c++/83503
- bogus -Wattributes for const and pure on function template
specialization, that we discussed last week, I've taken a stab
at making the change to avoid applying primary template's
attributes to its explicit specializations.  (The bug tracking
the underlying root cause is 83871 - wrong code for attribute
const and pure on distinct template specializations).

The attached patch is what I have so far.  It's incomplete
and not all the tests pass for a couple of reasons:

1) it only handles function templates (not class templates),
    and I have no tests for those yet,

Class templates may already work the way you expect; at least aligned
does, though that doesn't involve TYPE_ATTRIBUTES.

Hmm, it seems that we currently don't propagate unused even to implicit
instantiations, a bug in the other direction:

template <class T> struct [[gnu::unused]] A { };

int main()
{
  A<int> a; // shouldn't warn
}

I opened bug 84221 to track it.  It's a regression WRT 4.7.

For types, it's not completely clear to me what should be
expected for attribute deprecated.  Not inheriting the
attribute means that users would be able to explicitly
specialize a deprecated primary template which is in most
cases contrary to the intent of the attribute.

On the other hand, inheriting it means that there would be
no good way to deprecate the primary without also deprecating
its explicit specializations (because declaring the explicit
specializations would trigger the warning).  The use case
for this was mentioned by Richard in the core discussion
(deprecating the std::numeric_limits primary).

I can't think of any way to make it work.  The only solution
that comes to mind is to use the name of the source file (or
header) in which the primary is defined and allow explicit
specializations to be defined in it while issuing the warning
for those defined in other files.  But this definitely seems
like GCC 9 material.


2) it isn't effective for the nonnull and returns_nonnull
    attributes because they are treated as type attributes,
3) the change to deal with attributes on function arguments
    may be unnecessary (I couldn't come up with any that would
    be propagated from the primary -- are there some?).

Yes, I think this is unnecessary.

Okay, thanks for confirming that.


Before I proceed with it I first want to make sure that it should
be fixed for GCC 8,

Some of it, I think.  Probably not the whole issue.

that duplicate_decls is the right place for these changes

I think so.

and that (2) should be fixed by treating those
and other such attributes by applying them to function decls.

This seems out of bounds for GCC 8.  It would also mean that we couldn't
use such attributes on pointers to functions.

+          TREE_NOTHROW (newdecl) |= TREE_NOTHROW (olddecl);

TREE_NOTHROW is mostly a non-attribute property, so I'd leave it out of
this.

__attribute__ ((nothrow))?  The patch includes a test case with
wrong-code due to inheriting the attribute.  With exception
specifications having to match between the primary and its
specializations it's the only way to make them different.
I've left this unchanged but let me know if I'm missing
something.


+          DECL_IS_MALLOC (olddecl) = DECL_IS_MALLOC (newdecl);

If a template is declared to be malloc, IMO we should really warn if a
specialization is missing that attribute, it seems certain to be a mistake.

I tend to agree that it's likely a mistake.  Though warning
in the front-end will lead to false positives if the function
isn't malloc.  Ideally, this would be detected in the middle-
end (where -Wsuggest-attribute=malloc is handled) but I suspect
it's too late for that.  I've added a simple warning for it.

In general, I think we should (optionally) warn if a template has
attributes and a specialization doesn't, as a user might have been
relying on the current behavior.

I've added a new option, -Wmissing-attribute.  In bug 81824
Joseph asked for such a warning for C (for function resolvers
and aliases) and so I'll use the same option for both (I expect
it's too late to handle 81824 in GCC 8 but I'll finish it in
GCC 9).  Adding the warning required passing some additional
attributes around and so more churn.

+      if (!merge_attr)
+    {
+      /* Remove the two function attributes that are, in fact,
+         treated as (quasi) type attributes.  */
+      tree attrs = TYPE_ATTRIBUTES (newtype);
+      tree newattrs = remove_attribute ("nonnull", attrs);
+      newattrs = remove_attribute ("returns_nonnull", attrs);
+      if (newattrs != attrs)
+        TYPE_ATTRIBUTES (newtype) = newattrs;
+    }

Instead of this, we should avoid calling merge_types and just use
TREE_TYPE (newdecl) for newtype.

Ah, great, thanks.  That works and fixes the outstanding FAILs
in the tests.

Attached is an updated patch.  It hasn't gone through full
testing yet but please let me know if you'd like me to make
some changes.

Martin
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-attribute): New option.

gcc/c-family/ChangeLog:

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

gcc/cp/ChangeLog:

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

gcc/testsuite/ChangeLog:

	PR c++/83871
	PR c++/83503
	* g++.dg/ext/attr-const-pure.C: New test.
	* g++.dg/ext/attr-malloc.C: New test.
	* g++.dg/ext/attr-nonnull.C: New test.
	* g++.dg/ext/attr-nothrow.C: New test.
	* g++.dg/ext/attr-returns-nonnull.C: New test.
	* g++.dg/Wmissing-attribute.C: New test.

diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt
index 9c71726..a4d5e61 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-attribute
+C ObjC C++ ObjC++ Var(warn_missing_attribute) 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 a53f4fd..fe75a9b 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -6137,7 +6137,8 @@ extern void note_iteration_stmt_body_end	(bool);
 extern tree make_lambda_name			(void);
 extern int decls_match				(tree, tree, bool = true);
 extern bool maybe_version_functions		(tree, tree);
-extern tree duplicate_decls			(tree, tree, bool);
+extern tree duplicate_decls			(tree, tree, bool,
+						 tree = NULL_TREE);
 extern tree declare_local_label			(tree);
 extern tree define_label			(location_t, tree);
 extern void check_goto				(tree);
@@ -6470,7 +6471,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 244a3ef..f6461b6 100644
--- a/gcc/cp/decl.c
+++ b/gcc/cp/decl.c
@@ -51,6 +51,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "builtins.h"
 #include "gimplify.h"
 #include "asan.h"
+#include <string>
 
 /* Possible cases of bad specifiers type used by bad_specifiers. */
 enum bad_spec_place {
@@ -1357,6 +1358,104 @@ check_redeclaration_no_default_args (tree decl)
       }
 }
 
+/* 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)
+{
+  /* 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", "noreturn", "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", "warn_unused_result"
+  };
+
+  /* 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_attribute,
+		  "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 ());
+    }
+
+}
+
 #define GNU_INLINE_P(fn) (DECL_DECLARED_INLINE_P (fn)			\
 			  && lookup_attribute ("gnu_inline",		\
 					       DECL_ATTRIBUTES (fn)))
@@ -1368,10 +1467,14 @@ check_redeclaration_no_default_args (tree decl)
    If NEWDECL is not a redeclaration of OLDDECL, NULL_TREE is
    returned.
 
-   NEWDECL_IS_FRIEND is true if NEWDECL was declared as a friend.  */
+   NEWDECL_IS_FRIEND is true if NEWDECL was declared as a friend.
+
+   ATTRLIST is an optional list of attributes that NEWDECL is being
+   declared with.  */
 
 tree
-duplicate_decls (tree newdecl, tree olddecl, bool newdecl_is_friend)
+duplicate_decls (tree newdecl, tree olddecl, bool newdecl_is_friend,
+		 tree attrlist /* = NULL_TREE */)
 {
   unsigned olddecl_uid = DECL_UID (olddecl);
   int olddecl_friend = 0, types_match = 0, hidden_friend = 0;
@@ -1971,10 +2074,25 @@ 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);
+  /* 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));
+
+  /* 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
+    {
+      warn_spec_missing_attributes (olddecl, newdecl, attrlist);
+
+      DECL_ATTRIBUTES (olddecl) = DECL_ATTRIBUTES (newdecl);
+    }
 
   if (DECL_DECLARES_FUNCTION_P (olddecl) && DECL_DECLARES_FUNCTION_P (newdecl))
     {
@@ -2103,7 +2221,7 @@ next_arg:;
 	}
       else
 	/* Merge the data types specified in the two decls.  */
-	newtype = merge_types (TREE_TYPE (newdecl), TREE_TYPE (olddecl));
+	newtype = TREE_TYPE (newdecl);
 
       if (VAR_P (newdecl))
 	{
@@ -2167,13 +2285,24 @@ 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;
+
+      if (merge_attr)
+	{
+	  /* Merge the type qualifiers.  */
+	  if (TREE_READONLY (newdecl))
+	    TREE_READONLY (olddecl) = 1;
+	  if (TREE_NOTHROW (newdecl))
+	    TREE_NOTHROW (olddecl) = 1;
+	}
+      else
+	{
+	  /* Set the bits that correspond to the const and nothrow
+	     function attributes.  */
+	  TREE_READONLY (olddecl) = TREE_READONLY (newdecl);
+	  TREE_NOTHROW (olddecl) = TREE_NOTHROW (newdecl);
+	}
 
       /* Merge deprecatedness.  */
       if (TREE_DEPRECATED (newdecl))
@@ -2212,13 +2341,22 @@ next_arg:;
 	    |= 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 (olddecl);
+	  if (merge_attr)
+	    {
+	      TREE_NOTHROW (newdecl) |= TREE_NOTHROW (olddecl);
+	      DECL_IS_MALLOC (newdecl) |= DECL_IS_MALLOC (olddecl);
+	      DECL_PURE_P (newdecl) |= DECL_PURE_P (olddecl);
+	    }
+	  else
+	    {
+	      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);
 	}
@@ -2340,17 +2478,18 @@ next_arg:;
     {
       tree parm;
 
-      /* Merge parameter attributes. */
+      /* Merge or assign parameter attributes. */
       tree oldarg, newarg;
-      for (oldarg = DECL_ARGUMENTS(olddecl), 
-               newarg = DECL_ARGUMENTS(newdecl);
-           oldarg && newarg;
-           oldarg = DECL_CHAIN(oldarg), newarg = DECL_CHAIN(newarg)) {
-          DECL_ATTRIBUTES (newarg)
-              = (*targetm.merge_decl_attributes) (oldarg, newarg);
-          DECL_ATTRIBUTES (oldarg) = DECL_ATTRIBUTES (newarg);
-      }
-      
+      for (oldarg = DECL_ARGUMENTS (olddecl),
+	     newarg = DECL_ARGUMENTS (newdecl);
+	   oldarg && newarg;
+	   oldarg = DECL_CHAIN (oldarg), newarg = DECL_CHAIN (newarg))
+	{
+	  DECL_ATTRIBUTES (newarg)
+	    = (*targetm.merge_decl_attributes) (oldarg, newarg);
+	  DECL_ATTRIBUTES (oldarg) = DECL_ATTRIBUTES (newarg);
+	}
+
       if (DECL_TEMPLATE_INSTANTIATION (olddecl)
 	  && !DECL_TEMPLATE_INSTANTIATION (newdecl))
 	{
@@ -8912,7 +9051,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 ca73bb1..8b3cb54 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -1473,15 +1473,17 @@ 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.  */
 
 static tree
 register_specialization (tree spec, tree tmpl, tree args, bool is_friend,
-			 hashval_t hash)
+			 hashval_t hash, tree attrlist = NULL_TREE)
 {
   tree fn;
   spec_entry **slot = NULL;
@@ -1572,7 +1574,7 @@ register_specialization (tree spec, tree tmpl, tree args, bool is_friend,
 		 for the specialization, we want this to look as if
 		 there were no definition, and vice versa.  */
 	      DECL_INITIAL (fn) = NULL_TREE;
-	      duplicate_decls (spec, fn, is_friend);
+	      duplicate_decls (spec, fn, is_friend, attrlist);
 	      /* The call to duplicate_decls will have applied
 		 [temp.expl.spec]:
 
@@ -2651,7 +2653,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;
@@ -3109,7 +3112,7 @@ check_explicit_specialization (tree declarator,
 	     process_partial_specialization.  */
 	  if (!processing_template_decl)
 	    decl = register_specialization (decl, gen_tmpl, targs,
-					    is_friend, 0);
+					    is_friend, 0, attrlist);
 
 	  /* 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 f3d9336..f50e3f3 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-attribute -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
@@ -3887,6 +3887,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-attribute @gol
 -Wmissing-braces @r{(only for C/ObjC)} @gol
 -Wmultistatement-macros  @gol
 -Wnarrowing @r{(only for C++)}  @gol
@@ -4550,6 +4551,17 @@ about the layout of the file that the directive references.
 
 This warning is enabled by @option{-Wall} in C and C++.
 
+@item -Wmissing-attribute
+@opindex Wmissing-attribute
+@opindex Wno-missing-attribute
+Warn when a declaration of an entity is missing attributes that a related
+entity is declared with whose absence may adversely affect the correctness
+of generated code.  For example, in C++, the warning is issued when
+an explicit specialization of a primary template declared with attribute
+@code{malloc} is declared without the attribute.
+
+This warning is enabled by @option{-Wall}.
+
 @item -Wmissing-braces
 @opindex Wmissing-braces
 @opindex Wno-missing-braces
diff --git a/gcc/testsuite/g++.dg/Wmissing-attribute.C b/gcc/testsuite/g++.dg/Wmissing-attribute.C
new file mode 100644
index 0000000..0915a90
--- /dev/null
+++ b/gcc/testsuite/g++.dg/Wmissing-attribute.C
@@ -0,0 +1,121 @@
+// 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-attribute" }
+
+#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, int);         // { dg-message "missing primary template attributes \(.malloc., .alloc_size.|.alloc_size., .malloc.\)" }
+
+template <>
+void*
+missing_all<char>(int, 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
+template <>
+void*
+ATTR ((alloc_size (1), malloc))
+missing_all<char>(int, int);
+
+template <>
+void*
+ATTR ((alloc_size (1))) ATTR ((malloc)) ATTR ((returns_nonnull))
+missing_all<signed char>(int, int);
+
+template <>
+void*
+ATTR ((hot)) ATTR ((alloc_size (1))) ATTR ((malloc))
+missing_all<signed char>(int, int);
+
+// Verify that the following attributes suppress the warning.
+template <> void* ATTR ((error (""))) missing_all<short>(int, int);
+template <> void* ATTR ((deprecated)) missing_all<int>(int, int);
+template <> void* ATTR ((noreturn)) missing_all<long>(int, int);
+template <> void* ATTR ((warning (""))) missing_all<double>(int, int);
+
+
+template <class T>
+void*
+ATTR ((malloc, alloc_size (1)))
+missing_malloc (int, int);            // { dg-message "missing primary template attribute .malloc." }
+
+template <>
+void*
+ATTR ((alloc_size (1)))
+missing_malloc<char>(int, int);       // { dg-warning "explicit specialization .\[^\n\r\]+. may be missing attributes" }
+
+template <> void* ATTR ((malloc, alloc_size (1))) missing_malloc<int>(int, int);
+template <> void* ATTR ((deprecated)) missing_malloc<char>(int, int);
+template <> void* ATTR ((error (""))) missing_malloc<short>(int, int);
+template <> void* ATTR ((noreturn)) missing_malloc<long>(int, int);
+template <> void* ATTR ((warning (""))) missing_malloc<float>(int, 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 (int*, int*);         // { dg-message "missing primary template attribute .nonnull." }
+
+template <>
+void*
+ATTR ((malloc))
+missing_nonnull<char>(int*, int*);    // { dg-warning "explicit specialization .\[^\n\r\]+. may be missing attributes" }
+
+template <> void* ATTR ((nonnull (1))) missing_nonnull<short>(int*, int*);
+template <> void* ATTR ((deprecated)) missing_nonnull<int>(int*, int*);
+template <> void* ATTR ((error (""))) missing_nonnull<long>(int*, int*);
+template <> void* ATTR ((noreturn)) missing_nonnull<float>(int*, int*);
+template <> void* ATTR ((warning (""))) missing_nonnull<void>(int*, int*);
+
+
+template <class T>
+void*
+ATTR ((warn_unused_result))
+missing_wur (int*, int*);             // { dg-message "missing primary template attribute .warn_unused_result." }
+
+template <>
+void*
+ATTR ((returns_nonnull))
+missing_wur<char>(int*, int*);        // { dg-warning "explicit specialization .\[^\n\r\]+. may be missing attributes" }
+
+template <> void* ATTR ((warn_unused_result)) missing_nonnull<int>(int*, int*);
+template <> void* ATTR ((deprecated)) missing_nonnull<long>(int*, int*);
+template <> void* ATTR ((error (""))) missing_nonnull<float>(int*, int*);
+template <> void* ATTR ((noreturn)) missing_nonnull<double>(int*, int*);
+template <> void* ATTR ((warning (""))) missing_nonnull<void>(int*, int*);
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..6afef6d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/attr-const-pure.C
@@ -0,0 +1,103 @@
+// Bug c++/83503 - bogus -Wattributes for const and pure on function template
+// specialization
+// { dg-do compile }
+// { dg-options "-O -Wall -fdump-tree-optimized" }
+
+template <class T>
+int __attribute__ ((pure))
+f (T);
+
+template <>
+int __attribute__ ((const)) f<int> (int);   // { dg-bogus "ignoring attribute .const." }
+
+
+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*);
+
+int global;
+
+extern void h_primary_elim ();
+extern void h_cstptr_elim ();
+extern void h_cstptr_keep ();
+extern void h_int_keep ();
+extern void h_intptr_keep ();
+
+void call_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_primary_elim ();           // must be eliminated
+}
+
+void call_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_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 "h_primary_elim" "optimized" } }
+// { dg-final { scan-tree-dump-not "h_cstptr_elim" "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-malloc.C b/gcc/testsuite/g++.dg/ext/attr-malloc.C
new file mode 100644
index 0000000..ce76abe
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/attr-malloc.C
@@ -0,0 +1,45 @@
+// 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 T>
+void* __attribute__ ((malloc))
+f (unsigned);
+
+template <>
+void*
+f<int>(unsigned);             // { dg-warning "may be missing attributes" }
+
+static char a[8];
+
+void f_void_malloc ();
+void f_int_not_malloc ();
+
+void fv (void)
+{
+  void *p = f<void>(1);
+  if (!p)
+    return;
+
+  if (p == a)                 // must be false
+    f_void_malloc ();         // should be eliminated
+}
+
+
+void fi (void)
+{
+  void *p = f<int>(1);
+  if (!p)
+    return;
+
+  if (p == a)                 // can be true
+    f_int_not_malloc ();      // must not be eliminated
+}
+
+// Verify that the call to f_void_not_malloc() is eliminated but
+// the call to f_int_not_malloc() is retained.
+// { dg-final { scan-tree-dump-not "f_void_malloc" "optimized" } }
+// { dg-final { scan-tree-dump-times "f_int_not_malloc" 1 "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-nothrow.C b/gcc/testsuite/g++.dg/ext/attr-nothrow.C
new file mode 100644
index 0000000..ae6b674
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/attr-nothrow.C
@@ -0,0 +1,47 @@
+// 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-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" } }

Reply via email to