This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: [RFC PATCH] avoid applying attributes to explicit specializations (PR 83871)


On 02/09/2018 12:52 PM, Jason Merrill wrote:
On 02/08/2018 04:52 PM, Martin Sebor wrote:
I took me a while to find DECL_TEMPLATE_RESULT.  Hopefully
that's the right way to get the primary from a TEMPLATE_DECL.

Yes.

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.

+  const char* const whitelist[] = {
+    "error", "noreturn", "warning"
+  };

Why whitelist noreturn?  I would expect to want that to be consistent.

I expect noreturn to be used on a primary whose definition
is provided but that's not meant to be used the way the API
is otherwise expected to be.  As in:

   template <class T>
   T [[noreturn]] foo () { throw "not implemented"; }

   template <> int foo<int>();   // implemented elsewhere

Marking that template as noreturn seems pointless, and possibly harmful;
the deprecated, warning, or error attributes would be better for this
situation.

I meant either:

  template <class T>
  T __attribute__ ((noreturn)) foo () { throw "not implemented"; }

  template <> int foo<int>();   // implemented elsewhere

or (sigh)

  template <class T>
  [[noreturn]] T foo () { throw "not implemented"; }

  template <> int foo<int>();   // implemented elsewhere

It lets code like this

  int bar ()
  {
     return foo<char>();
  }

be diagnosed because it's likely a bug (as Clang does with
-Wunreachable-code).  It doesn't stop code like the following
from compiling (which is good) but it instead lets them throw
at runtime which is what foo's author wants.

  void bar ()
  {
     foo<char>();
  }

It's the same as having an "unimplemented" base virtual function
throw an exception when it's called rather than making it pure
and having calls to it abort.  Declaring the base virtual function
noreturn is useful for the same reason (and also diagnosed by
Clang).  I should remember to add the same warning in GCC 9.

I actually had some misgivings about both warning and deprecated
for the white-listing, but not for noreturn.  My (only mild)
concern is that both warning and deprecated functions can and
likely will in some cases still be called, and so using them to
suppress the warning runs the risk that their calls might be
wrong and no one will notice.  Warning cannot be suppressed
so it seems unlikely to be ignored, but deprecated can be.
So I wonder if the white-listing for deprecated should be
conditional on -Wdeprecated being enabled.

-      /* 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;
+    }
+      else
+    {
+      /* Set the bits that correspond to the const function
attributes.  */
+      TREE_READONLY (olddecl) = TREE_READONLY (newdecl);
+    }

Let's limit the const/volatile handling here to non-functions, and
handle the const/noreturn attributes for functions in the later hunk
along with nothrow/malloc/pure.

I had to keep the TREE_HAS_VOLATILE handling as is since it
applies to functions too (has side-effects).  Otherwise the
attr-nothrow-2.C test fails.


I try to avoid reformatting code that I'm not actually changing, to
avoid noise in e.g. git blame.

Okay.

Attached is an updated patch.  Besides the changes above
I also used a plural "attributes" in the name of the option
for consistency with other attribute options, expanded
the documentation, and enhanced one of the tests.

Bootstrapped on x86_64-linux (tests still running).

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 (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.

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-nothrow-2.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..1039274 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 a53f4fd..87b7916 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -6470,7 +6470,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..2ea115e 100644
--- a/gcc/cp/decl.c
+++ b/gcc/cp/decl.c
@@ -1971,10 +1971,21 @@ 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
+    DECL_ATTRIBUTES (olddecl) = DECL_ATTRIBUTES (newdecl);
 
   if (DECL_DECLARES_FUNCTION_P (olddecl) && DECL_DECLARES_FUNCTION_P (newdecl))
     {
@@ -2101,9 +2112,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))
 	{
@@ -2167,14 +2179,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;
@@ -2192,6 +2196,17 @@ 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 and the function has
+	 side-effects bit.  */
+      if (TREE_THIS_VOLATILE (newdecl))
+	TREE_THIS_VOLATILE (olddecl) = 1;
 
       /* Merge the initialization information.  */
       if (DECL_INITIAL (newdecl) == NULL_TREE
@@ -2212,13 +2227,24 @@ 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 (newdecl)
 	    |= DECL_LOOPING_CONST_OR_PURE_P (olddecl);
+
+	  if (merge_attr)
+	    {
+	      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
+	    {
+	      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);
 	}
@@ -8912,7 +8938,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..dfdb3eb 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.  */
@@ -2610,6 +2613,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", "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_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
@@ -2651,7 +2758,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;
@@ -3108,8 +3216,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 f3d9336..4208647 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
@@ -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-attributes @gol
 -Wmissing-braces @r{(only for C/ObjC)} @gol
 -Wmultistatement-macros  @gol
 -Wnarrowing @r{(only for C++)}  @gol
@@ -4550,6 +4551,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},
+@code{nonnull}, or @code{warn_unused_result} is declared without it.
+Attributes @code{deprecated}, @code{error}, @code{noreturn}, 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/testsuite/g++.dg/Wmissing-attribute.C b/gcc/testsuite/g++.dg/Wmissing-attribute.C
new file mode 100644
index 0000000..5ce97cc
--- /dev/null
+++ b/gcc/testsuite/g++.dg/Wmissing-attribute.C
@@ -0,0 +1,122 @@
+// 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);            // { 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 ((noreturn)) missing_all<long>(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 ((noreturn)) missing_malloc<float>(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 ((noreturn)) missing_nonnull<float>(void*);
+template <> void* ATTR ((warning (""))) missing_nonnull<double>(void*);
+
+
+template <class T>
+void*
+ATTR ((warn_unused_result))
+missing_wur ();                       // { dg-message "missing primary template attribute .warn_unused_result." }
+
+template <>
+void*
+ATTR ((returns_nonnull))
+missing_wur<char>();                  // { dg-warning "explicit specialization .\[^\n\r\]+. may be missing attributes" }
+
+template <> void* ATTR ((warn_unused_result)) missing_wur<short>();
+template <> void* ATTR ((deprecated)) missing_wur<int>();
+template <> void* ATTR ((error (""))) missing_wur<long>();
+template <> void* ATTR ((noreturn)) missing_wur<float>();
+template <> void* ATTR ((warning (""))) missing_wur<double>();
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-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-2.C b/gcc/testsuite/g++.dg/ext/attr-nothrow-2.C
new file mode 100644
index 0000000..4061f82
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/attr-nothrow-2.C
@@ -0,0 +1,36 @@
+/*  PR c++/83871 - wrong code for attribute const and pure on distinct
+    template specializations
+    { dg-do compile }
+    { dg-options "-O -fdump-tree-eh" } */
+
+void __attribute__ ((nothrow)) f ();
+
+void ff () throw ()
+{
+  // No exception handling necessary around the call to f().
+  f ();
+}
+
+void __attribute__ ((nothrow)) g ();
+void g ();
+
+void gg () throw ()
+{
+  // No exception handling necessary around the call to g().
+  g ();
+}
+
+int __attribute__ ((nothrow)) h ();
+int __attribute__ ((noreturn)) h ();
+int h ();
+
+int hh () throw ()
+{
+  // No exception handling necessary around the call to h().
+  // No -Wreturn-value should be emitted because h is noreturn.
+  h ();
+}
+
+// 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..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" } }

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]