public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
From: Martin Sebor <msebor@gmail.com>
To: Joseph Myers <joseph@codesourcery.com>
Cc: Jason Merrill <jason@redhat.com>,
	Gcc Patch List <gcc-patches@gcc.gnu.org>
Subject: Re: [PATCH] detect attribute mismatches in alias declarations (PR 81824)
Date: Tue, 23 Oct 2018 07:29:00 -0000	[thread overview]
Message-ID: <c2d6b0d8-a4d0-9f96-8a5f-716a7ef303d4@gmail.com> (raw)
In-Reply-To: <alpine.DEB.2.21.1810012252180.14173@digraph.polyomino.org.uk>

[-- Attachment #1: Type: text/plain, Size: 3863 bytes --]

On 10/01/2018 05:00 PM, Joseph Myers wrote:
> On Mon, 1 Oct 2018, Martin Sebor wrote:
>
>> Testing the patch with Glibc triggers thousands of warnings of
>> both kinds.  After reviewing a small subset it became apparent
>
> Thousands of warnings suggests initially having the warning outside -Wall
> (though one might hope to move it into -Wall at some point, depending on
> how hard the warnings are to address and to what extent they appear at all
> for other packages - most don't make heavy use of aliases like that - or
> failing that, to enable it explicitly for glibc once all the warnings are
> fixed, since this is certainly a useful warning for glibc showing issues
> we want to fix) - it's not like the typical case of a new warning where
> you can quickly and easily fix all the instances in glibc, for all
> architectures, to keep it building with mainline GCC.

I have improved the Glibc patch to avoid many of the warnings.
To avoid most of the rest I have adjusted the GCC patch to make
-Wattribute-alias a two-level warning, and to disregard mismatches
between aliases and ifunc resolvers.  With -Wattribute-alias=1
that reduced the number of unique instances of the warnings for
a Glibc build to just 27.  Of those, all but one of
the -Wattributes instances are of the form:

   warning: ‘leaf’ attribute has no effect on unit local functions

All the -Wmissing-attributes instances are due to a missing
nonnull attribute on the __EI__ kinds of functions, like:

   warning: ‘__EI_vfprintf’ specifies less restrictive attribute than 
its target ‘vfprintf’: ‘nonnull’

I think both sets might be caused either by a bug/missing handling
in my Glibc patch, or by a bug in the GCC patch, but I'm a little
lost in the maze of Glibc ifunc macro to tell which just yet.
I can keep looking into it but it would make it easier if you
could apply it and see if the Glibc macros need tweaking.

Enabling -Wattribute-alias=2 shows the remaining attribute
mismatches (those highlighting "potential bugs," although
I suspect few, if any, are real bugs; more than likely they
are benign.)

>
>> attribute called copy.  The attribute copies attributes from
>> one declaration (or type) to another.  The attribute doesn't
>> resolve all the warnings but it helps.
>
> (For actual use in glibc that use would of course need to be conditional
> on a GCC version supporting the attribute.)

Sure.  The patch is just to show what I did to get the warnings.
(Attached is an update with the changes I mentioned above and
the Glibc warning breakdown with it.)

>
>> The class of warnings I noticed that can't be so easily handled
>> are due to inconsistencies between ifuncs and their resolvers.
>> One way to solve it might be to have resolvers automatically
>> "inherit" all the attributes of their targets (and enhance
>> GCC to warn for violations).  Another way might be to expect
>> resolvers to be explicitly declared with attribute copy to copy
>> the attributes of all the targets (and also warn for violations).
>
> I'm not sure we should care about the attributes on IFUNC resolvers at
> all; no normal code will see a declaration of the resolver and also call
> the function, whereas lots of code calls functions under internal alias
> names that currently lack the same attributes as the public declaration
> has.  It's also not obvious whether there might be more cases of
> attributes for a function that are inapplicable to IFUNC resolvers than
> just the attributes relating to a symbol rather than the function itself
> which are hardcoded as excluded.

I agree that checking attributes on ifunc resolvers probably doesn't
make much sense.  It would be helpful to check their targets against
the aliases, but that's a whole other project.

Attached is the latest update along with the Glibc warning breakdown.
Tested on x86_64-linux.

Martin


[-- Attachment #2: gcc-81824.diff --]
[-- Type: text/x-patch, Size: 47788 bytes --]

PR middle-end/81824 - Warn for missing attributes with function aliases

gcc/c-family/ChangeLog:

	PR middle-end/81824
	* c-attribs.c (handle_copy_attribute_impl): New function.
	(handle_copy_attribute): Same.

gcc/cp/ChangeLog:

	PR middle-end/81824
	* pt.c (warn_spec_missing_attributes): Move code to attribs.c.
	Call decls_mismatched_attributes.

gcc/ChangeLog:

	PR middle-end/81824
	* attribs.c (has_attribute): New helper function.
	(decls_mismatched_attributes, maybe_diag_alias_attributes): Same.
	* attribs.h (decls_mismatched_attributes): Declare.
	* cgraphunit.c (handle_alias_pairs): Call maybe_diag_alias_attributes.
	(maybe_diag_incompatible_alias): Use OPT_Wattribute_alias_.
	* common.opt (-Wattribute-alias): Take an argument.
	(-Wno-attribute-alias): New option.
	* doc/extend.texi (Common Function Attributes): Document copy.
	(Common Variable Attributes): Same.
	* doc/invoke.texi (-Wmissing-attributes): Document enhancement.
	(-Wattribute-alias): Document new option argument.

libgomp/ChangeLog:

	PR c/81824
	* libgomp.h (strong_alias, ialias, ialias_redirect): Use attribute
	copy.

gcc/testsuite/ChangeLog:

	PR middle-end/81824
	* gcc.dg/Wattribute-alias.c: New test.
	* gcc.dg/Wmissing-attributes.c: New test.
	* gcc.dg/attr-copy.c: New test.
	* gcc.dg/attr-copy-2.c: New test.
	* gcc.dg/attr-copy-3.c: New test.
	* gcc.dg/attr-copy-4.c: New test.

diff --git a/gcc/attribs.c b/gcc/attribs.c
index 8b72127..03a7d49 100644
--- a/gcc/attribs.c
+++ b/gcc/attribs.c
@@ -30,6 +30,9 @@ along with GCC; see the file COPYING3.  If not see
 #include "plugin.h"
 #include "selftest.h"
 #include "hash-set.h"
+#include "diagnostic.h"
+#include "pretty-print.h"
+#include "intl.h"
 
 /* Table of the tables of attributes (common, language, format, machine)
    searched.  */
@@ -1812,6 +1815,188 @@ private_lookup_attribute (const char *attr_name, size_t attr_len, tree list)
   return list;
 }
 
+/* Return true if the function decl or type NODE has been declared
+   with attribute ANAME among attributes ATTRS.  */
+
+static bool
+has_attribute (tree node, tree attrs, const char *aname)
+{
+  if (!strcmp (aname, "const"))
+    {
+      if (DECL_P (node) && TREE_READONLY (node))
+	return true;
+    }
+  else if (!strcmp (aname, "malloc"))
+    {
+      if (DECL_P (node) && DECL_IS_MALLOC (node))
+	return true;
+    }
+  else if (!strcmp (aname, "noreturn"))
+    {
+      if (DECL_P (node) && TREE_THIS_VOLATILE (node))
+	return true;
+    }
+  else if (!strcmp (aname, "nothrow"))
+    {
+      if (TREE_NOTHROW (node))
+	return true;
+    }
+  else if (!strcmp (aname, "pure"))
+    {
+      if (DECL_P (node) && DECL_PURE_P (node))
+	return true;
+    }
+
+  return lookup_attribute (aname, attrs);
+}
+
+/* Return the number of mismatched function or type attributes between
+   the "template" function declaration TMPL and DECL.  The word "template"
+   doesn't necessarily refer to a C++ template but rather a declaration
+   whose attributes should be matched by those on DECL.  For a non-zero
+   return value set *ATTRSTR to a string representation of the list of
+   mismatched attributes with quoted names.
+   ATTRLIST is a list of additional attributes that SPEC should be
+   taken to ultimately be declared with.  */
+
+unsigned
+decls_mismatched_attributes (tree tmpl, tree decl, tree attrlist,
+			     const char* const blacklist[],
+			     pretty_printer *attrstr)
+{
+  if (TREE_CODE (tmpl) != FUNCTION_DECL)
+    return 0;
+
+  /* Avoid warning if either declaration or its type is deprecated.  */
+  if (TREE_DEPRECATED (tmpl)
+      || TREE_DEPRECATED (decl))
+    return 0;
+
+  const tree tmpls[] = { tmpl, TREE_TYPE (tmpl) };
+  const tree decls[] = { decl, TREE_TYPE (decl) };
+
+  if (TREE_DEPRECATED (tmpls[1])
+      || TREE_DEPRECATED (decls[1])
+      || TREE_DEPRECATED (TREE_TYPE (tmpls[1]))
+      || TREE_DEPRECATED (TREE_TYPE (decls[1])))
+    return 0;
+
+  tree tmpl_attrs[] = { DECL_ATTRIBUTES (tmpl), TYPE_ATTRIBUTES (tmpls[1]) };
+  tree decl_attrs[] = { DECL_ATTRIBUTES (decl), TYPE_ATTRIBUTES (decls[1]) };
+
+  if (!decl_attrs[0])
+    decl_attrs[0] = attrlist;
+  else if (!decl_attrs[1])
+    decl_attrs[1] = attrlist;
+
+  /* Avoid warning if the template has no attributes.  */
+  if (!tmpl_attrs[0] && !tmpl_attrs[1])
+    return 0;
+
+  /* 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], decl_attrs[i]))
+	return 0;
+
+  /* Put together a list of the black-listed attributes that the template
+     is declared with and the declaration is not, in case it's not apparent
+     from the most recent declaration of the template.  */
+  unsigned nattrs = 0;
+
+  for (unsigned i = 0; blacklist[i]; ++i)
+    {
+      for (unsigned j = 0; j != 2; ++j)
+	{
+	  if (!has_attribute (tmpls[j], tmpl_attrs[j], blacklist[i]))
+	    continue;
+
+	  for (unsigned k = 0; k != 1 + !!decl_attrs[1]; ++k)
+	    {
+	      if (has_attribute (decls[k], decl_attrs[k], blacklist[i]))
+		break;
+
+	      if (nattrs)
+		pp_string (attrstr, ", ");
+	      pp_begin_quote (attrstr, pp_show_color (global_dc->printer));
+	      pp_string (attrstr, blacklist[i]);
+	      pp_end_quote (attrstr, pp_show_color (global_dc->printer));
+	      ++nattrs;
+	    }
+	}
+    }
+
+  return nattrs;
+}
+
+/* Issue a warning for the declaration ALIAS for TARGET where ALIAS
+   specifies either attributes that are incompatible with those of
+   TARGET, or attributes that are missing and that declaring ALIAS
+   with would benefit.  */
+
+void
+maybe_diag_alias_attributes (tree alias, tree target)
+{
+  /* Do not expect attributes to match between aliases and ifunc
+     resolvers.  There is no obvious correspondence between them.  */
+  if (lookup_attribute ("ifunc", DECL_ATTRIBUTES (alias)))
+    return;
+
+  const char* const blacklist[] = {
+    "alloc_align", "alloc_size", "cold", "const", "hot", "leaf", "malloc",
+    "nonnull", "noreturn", "nothrow", "pure", "returns_nonnull",
+    "returns_twice", NULL
+  };
+
+  pretty_printer attrnames;
+  if (warn_attribute_alias > 1)
+    {
+      /* With -Wattribute-alias=2 detect alias declarations that are more
+	 restrictive than their targets first.  Those indicate potential
+	 codegen bugs.  */
+      if (unsigned n = decls_mismatched_attributes (alias, target, NULL_TREE,
+						    blacklist, &attrnames))
+	{
+	  auto_diagnostic_group d;
+	  if (warning_n (DECL_SOURCE_LOCATION (alias),
+			 OPT_Wattribute_alias_, n,
+			 "%qD specifies more restrictive attribute than "
+			 "its target %qD: %s",
+			 "%qD specifies more restrictive attributes than "
+			 "its target %qD: %s",
+			 alias, target, pp_formatted_text (&attrnames)))
+	    inform (DECL_SOURCE_LOCATION (target),
+		    "%qD target declared here", alias);
+	  return;
+	}
+    }
+
+  /* Detect alias declarations that are less restrictive than their
+     targets.  Those suggest potential optimization opportunities
+     (solved by adding the missing attribute(s) to the alias).  */
+  if (unsigned n = decls_mismatched_attributes (target, alias, NULL_TREE,
+						blacklist, &attrnames))
+    {
+      auto_diagnostic_group d;
+      if (warning_n (DECL_SOURCE_LOCATION (alias),
+		     OPT_Wmissing_attributes, n,
+		     "%qD specifies less restrictive attribute than "
+		     "its target %qD: %s",
+		     "%qD specifies less restrictive attributes than "
+		     "its target %qD: %s",
+		     alias, target, pp_formatted_text (&attrnames)))
+	inform (DECL_SOURCE_LOCATION (target),
+		"%qD target declared here", alias);
+    }
+}
+
+
 #if CHECKING_P
 
 namespace selftest
diff --git a/gcc/attribs.h b/gcc/attribs.h
index c277e1b..5b76c4c 100644
--- a/gcc/attribs.h
+++ b/gcc/attribs.h
@@ -105,6 +105,12 @@ extern int attribute_list_contained (const_tree, const_tree);
 extern tree private_lookup_attribute (const char *attr_name, size_t attr_len,
 				      tree list);
 
+extern unsigned decls_mismatched_attributes (tree, tree, tree,
+					     const char* const[],
+					     pretty_printer*);
+
+extern void maybe_diag_alias_attributes (tree, tree);
+
 /* For a given IDENTIFIER_NODE, strip leading and trailing '_' characters
    so that we have a canonical form of attribute names.  */
 
diff --git a/gcc/c-family/c-attribs.c b/gcc/c-family/c-attribs.c
index 5454e09..3a88766 100644
--- a/gcc/c-family/c-attribs.c
+++ b/gcc/c-family/c-attribs.c
@@ -146,6 +146,7 @@ static tree handle_designated_init_attribute (tree *, tree, tree, int, bool *);
 static tree handle_fallthrough_attribute (tree *, tree, tree, int, bool *);
 static tree handle_patchable_function_entry_attribute (tree *, tree, tree,
 						       int, bool *);
+static tree handle_copy_attribute (tree *, tree, tree, int, bool *);
 
 /* Helper to define attribute exclusions.  */
 #define ATTR_EXCL(name, function, type, variable)	\
@@ -455,6 +456,8 @@ const struct attribute_spec c_common_attribute_table[] =
 			      NULL },
   { "nocf_check",	      0, 0, false, true, true, true,
 			      handle_nocf_check_attribute, NULL },
+  { "copy",                   1, 1, false, false, false, false,
+			      handle_copy_attribute, NULL },
   { NULL,                     0, 0, false, false, false, false, NULL, NULL }
 };
 
@@ -2134,6 +2137,172 @@ handle_alias_attribute (tree *node, tree name, tree args,
   return handle_alias_ifunc_attribute (true, node, name, args, no_add_attrs);
 }
 
+/* Handle the "copy" attribute by copying the set of attributes
+   from the symbol referenced by ARGS to the declaration of *NODE.  */
+
+static tree
+handle_copy_attribute_impl (tree *node, tree name, tree args,
+			    int flags, bool *no_add_attrs)
+{
+  /* Do not apply the copy attribute itself.  It serves no purpose
+     other than to copy other attributes.  */
+  *no_add_attrs = true;
+
+  tree decl = *node;
+
+  tree ref = TREE_VALUE (args);
+  if (ref == error_mark_node)
+    return NULL_TREE;
+
+  if (TREE_CODE (ref) == STRING_CST)
+    {
+      /* Explicitly handle this case since using a string literal
+	 as an argument is a likely mistake.  */
+      error_at (DECL_SOURCE_LOCATION (decl),
+		"%qE attribute argument cannot be a string",
+		name);
+      return NULL_TREE;
+    }
+
+  if (CONSTANT_CLASS_P (ref)
+      && (INTEGRAL_TYPE_P (TREE_TYPE (ref))
+	  || FLOAT_TYPE_P (TREE_TYPE (ref))))
+    {
+      /* Similar to the string case, since some function attributes
+	 accept literal numbers as arguments (e.g., alloc_size or
+	 nonnull) using one here is a likely mistake.  */
+      error_at (DECL_SOURCE_LOCATION (decl),
+		"%qE attribute argument cannot be a constant arithmetic "
+		"expression",
+		name);
+      return NULL_TREE;
+    }
+
+  if (ref == node[1])
+    {
+      /* Another possible mistake (but indirect self-references aren't
+	 and diagnosed and shouldn't be).  */
+      if (warning_at (DECL_SOURCE_LOCATION (decl), OPT_Wattributes,
+		      "%qE attribute ignored on a redeclaration "
+		      "of the referenced symbol",
+		      name))
+	inform (DECL_SOURCE_LOCATION (node[1]),
+		"previous declaration here");
+      return NULL_TREE;
+    }
+
+  /* Consider address-of expressions in the attribute argument
+     as requests to copy from the referenced entity.  For constant
+     expressions, consider those to be requests to copy from their
+     type, such as in:
+       struct __attribute__ (copy ((struct T *)0)) U { ... };
+     which copies type attributes from struct T to the declaration
+     of struct U.  */
+  if (TREE_CODE (ref) == ADDR_EXPR)
+    ref = TREE_OPERAND (ref, 0);
+  else if (CONSTANT_CLASS_P (ref))
+    ref = TREE_TYPE (ref);
+
+  if (DECL_P (decl))
+    {
+      if ((VAR_P (decl)
+	   && (TREE_CODE (ref) == FUNCTION_DECL
+	       || (EXPR_P (ref)
+		   && POINTER_TYPE_P (TREE_TYPE (ref))
+		   && FUNC_OR_METHOD_TYPE_P (TREE_TYPE (TREE_TYPE (ref))))))
+	  || (TREE_CODE (decl) == FUNCTION_DECL
+	      && (VAR_P (ref)
+		  || (EXPR_P (ref)
+		      && !FUNC_OR_METHOD_TYPE_P (TREE_TYPE (ref))))))
+	{
+	  /* It makes no sense to try to copy function attributes
+	     to a variable, or variable attributes to a function.  */
+	  if (warning (OPT_Wattributes,
+		       "%qE attribute ignored on a declaration of "
+		       "a different kind than referenced symbol",
+		       name)
+	      && DECL_P (ref))
+	    inform (DECL_SOURCE_LOCATION (ref),
+		    "symbol %qD referenced by %qD declared here", ref, decl);
+	  return NULL_TREE;
+	}
+	
+      tree attrs = NULL_TREE;
+      if (DECL_P (ref))
+	attrs = DECL_ATTRIBUTES (ref);
+      else if (TYPE_P (ref))
+	attrs = TYPE_ATTRIBUTES (ref);
+
+      /* Copy decl attributes from REF to DECL.  */
+      for (tree at = attrs; at; at = TREE_CHAIN (at))
+	{
+	  /* Avoid copying attributes that affect a symbol linkage or
+	     visibility since those in all likelihood only apply to
+	     the target.
+	     FIXME: make it possible to specify which attributes to
+	     copy or not to copy in the copy attribute itself.  */
+	  tree atname = get_attribute_name (at);
+	  if (is_attribute_p ("alias", atname)
+	      || is_attribute_p ("ifunc", atname)
+	      || is_attribute_p ("visibility", atname)
+	      || is_attribute_p ("weak", atname)
+	      || is_attribute_p ("weakref", atname))
+	    continue;
+
+	  tree atargs = TREE_VALUE (at);
+	  /* Create a copy of just the one attribute ar AT, including
+	     its argumentsm and add it to DECL.  */
+	  tree attr = tree_cons (atname, copy_list (atargs), NULL_TREE);
+	  decl_attributes (&decl, attr, flags);
+	}
+
+      /* Proceed to copy type attributes below.  */
+    }
+  else if (!TYPE_P (decl))
+    {
+      error_at (DECL_SOURCE_LOCATION (decl),
+		"%qE attribute must apply to a declaration",
+		name);
+      return NULL_TREE;
+    }
+
+  if (DECL_P (ref) || EXPR_P (ref))
+    ref = TREE_TYPE (ref);
+
+  if (POINTER_TYPE_P (ref))
+    ref = TREE_TYPE (ref);
+
+  tree attrs = TYPE_ATTRIBUTES (ref);
+
+  /* Copy type attributes from REF to DECL.  */
+  for (tree at = attrs; at; at = TREE_CHAIN (at))
+    decl_attributes (&decl, at, flags);
+
+  return NULL_TREE;
+}
+
+/* Handle the "copy" attribute by copying the set of attributes
+   from the symbol referenced by ARGS to the declaration of *NODE.  */
+
+static tree
+handle_copy_attribute (tree *node, tree name, tree args,
+		       int flags, bool *no_add_attrs)
+{
+  /* Break cycles in circular references.  */
+  static hash_set<tree> attr_copy_visited;
+
+  if (attr_copy_visited.contains (*node))
+    return NULL_TREE;
+
+  attr_copy_visited.add (*node);
+
+  tree ret = handle_copy_attribute_impl (node, name, args, flags, no_add_attrs);
+
+  attr_copy_visited.remove (*node);
+
+  return ret;
+}
+
 /* Handle a "weakref" attribute; arguments as in struct
    attribute_spec.handler.  */
 
diff --git a/gcc/cgraphunit.c b/gcc/cgraphunit.c
index ec490d7..f8da813 100644
--- a/gcc/cgraphunit.c
+++ b/gcc/cgraphunit.c
@@ -1360,7 +1360,7 @@ maybe_diag_incompatible_alias (tree alias, tree target)
 
 	  auto_diagnostic_group d;
 	  if (warning_at (DECL_SOURCE_LOCATION (target),
-			  OPT_Wattribute_alias,
+			  OPT_Wattribute_alias_,
 			  "%<ifunc%> resolver for %qD should return %qT",
 			  alias, funcptr))
 	    inform (DECL_SOURCE_LOCATION (alias),
@@ -1370,11 +1370,11 @@ maybe_diag_incompatible_alias (tree alias, tree target)
 	{
 	  auto_diagnostic_group d;
 	  if (warning_at (DECL_SOURCE_LOCATION (alias),
-			    OPT_Wattribute_alias,
+			    OPT_Wattribute_alias_,
 			    "%qD alias between functions of incompatible "
 			    "types %qT and %qT", alias, altype, targtype))
 	    inform (DECL_SOURCE_LOCATION (target),
-		      "aliased declaration here");
+		    "aliased declaration here");
 	}
     }
 }
@@ -1437,6 +1437,8 @@ handle_alias_pairs (void)
 	{
 	  maybe_diag_incompatible_alias (p->decl, target_node->decl);
 
+	  maybe_diag_alias_attributes (p->decl, target_node->decl);
+
 	  cgraph_node *src_node = cgraph_node::get (p->decl);
 	  if (src_node && src_node->definition)
 	    src_node->reset ();
diff --git a/gcc/common.opt b/gcc/common.opt
index 53aac19..6382e75 100644
--- a/gcc/common.opt
+++ b/gcc/common.opt
@@ -550,9 +550,13 @@ Wattributes
 Common Var(warn_attributes) Init(1) Warning
 Warn about inappropriate attribute usage.
 
-Wattribute-alias
-Common Var(warn_attributes) Init(1) Warning
-Warn about type safety and similar errors in attribute alias and related.
+Wattribute-alias=
+Common Joined RejectNegative UInteger Var(warn_attribute_alias) Init(1) Warning IntegerRange(0, 2)
+Warn about type safety and similar errors and mismatches in attribute alias and related.
+
+Wno-attribute-alias
+Common Alias(Wattribute_alias=, 0, 0) Warning
+Disable -Wattribute-alias.
 
 Wcannot-profile
 Common Var(warn_cannot_profile) Init(1) Warning
diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index b8b6545..cf4a6cd 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -2647,81 +2647,19 @@ 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"
+    "format_arg", "malloc", "nonnull", NULL
   };
 
   /* 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;
   pretty_printer 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 (nattrs)
-		pp_string (&str, ", ");
-	      pp_begin_quote (&str, pp_show_color (global_dc->printer));
-	      pp_string (&str, blacklist[i]);
-	      pp_end_quote (&str, pp_show_color (global_dc->printer));
-	      ++nattrs;
-	    }
-	}
-    }
+  unsigned nattrs = decls_mismatched_attributes (tmpl, spec, attrlist,
+						 blacklist, &str);
 
   if (!nattrs)
     return;
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index cfe6a8e..8ffb0cd 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -2548,6 +2548,40 @@ decorated with attribute @code{constructor} are invoked is unspecified.
 In mixed declarations, attribute @code{init_priority} can be used to
 impose a specific ordering.
 
+@item copy
+@itemx copy (@var{function})
+@cindex @code{copy} function attribute
+The @code{copy} attribute applies the set of attributes with which
+@var{function} has been declared to the declaration of the function
+to which the attribute is applied.  The attribute is designed for
+libraries that define aliases or function resolvers that are expected
+to specify the same set of attributes as their targets.  The @code{copy}
+attribute can be used with functions, variables, or types.  However,
+the kind of symbol to which the attribute is applied (either function
+or variable) must match the kind of symbol to which the argument refers.
+The @code{copy} attribute copies only syntaxtic and semantic attributes
+but attributes that affect a symbol's linkage or visibility such as
+@code{alias}, @code{visibility}, or @code{weak}.  The @code{deprecated}
+attribute is also not copied.  @xref{Common Type Attributes}.
+@xref{Common Variable Attributes}.
+
+For example, the @var{StrongAlias} macro below makes use of the @code{alias}
+and @code{copy} attributes to define an alias named @var{alloc} for function
+@var{allocate} declated with attributes @var{alloc_size}, @var{malloc}, and
+@var{nothrow}.  Thanks to the @code{__typeof__} operator the alias has
+the same type as the target function.  As a result of the @code{copy}
+attribute the alias also shares the same attributes as the target.
+
+@smallexample
+#define StrongAlias(TagetFunc, AliasDecl)   \
+  extern __typeof__ (TargetFunc) AliasDecl  \
+    __attribute__ ((alias (#TargetFunc), copy (TargetFunc)));
+
+extern __attribute__ ((alloc_size (1), malloc, nothrow))
+  void* allocate (size_t);
+StrongAlias (allocate, alloc);
+@end smallexample
+
 @item deprecated
 @itemx deprecated (@var{msg})
 @cindex @code{deprecated} function attribute
@@ -6133,6 +6167,23 @@ opposite---to allocate space for it directly.
 These attributes override the default chosen by the
 @option{-fno-common} and @option{-fcommon} flags respectively.
 
+@item copy
+@itemx copy (@var{variable})
+@cindex @code{copy} variable attribute
+The @code{copy} attribute applies the set of attributes with which
+@var{variable} has been declared to the declaration of the variable
+to which the attribute is applied.  The attribute is designed for
+libraries that define aliases that are expected to specify the same
+set of attributes as the aliased symbols.  The @code{copy} attribute
+can be used with variables, functions or types.  However, the kind
+of symbol to which the attribute is applied (either varible or
+function) must match the kind of symbol to which the argument refers.
+The @code{copy} attribute copies only syntaxtic and semantic attributes
+but attributes that affect a symbol's linkage or visibility such as
+@code{alias}, @code{visibility}, or @code{weak}.  The @code{deprecated}
+attribute is also not copied.  @xref{Common Function Attributes}.
+@xref{Common Type Attributes}.
+
 @item deprecated
 @itemx deprecated (@var{msg})
 @cindex @code{deprecated} variable attribute
@@ -7060,6 +7111,38 @@ struct foo
 
 This warning can be disabled by @option{-Wno-if-not-aligned}.
 
+@item copy
+@itemx copy (@var{expression})
+@cindex @code{copy} type attribute
+The @code{copy} attribute applies the set of attributes with which
+the type of the @var{expression} has been declared to the declaration
+of the type to which the attribute is applied.  The attribute is
+designed for libraries that define aliases that are expected to
+specify the same set of attributes as the aliased symbols.
+The @code{copy} attribute can be used with types, variables, or
+functions.  However, the kind of symbol to which the attribute is
+applied (either varible or function) must match the kind of symbol
+to which the argument refers.
+The @code{copy} attribute copies only syntaxtic and semantic attributes
+but attributes that affect a symbol's linkage or visibility such as
+@code{alias}, @code{visibility}, or @code{weak}.  The @code{deprecated}
+attribute is also not copied.  @xref{Common Function Attributes}.
+@xref{Common Variable Attributes}.
+
+For example, suppose @code{struct A} below is defined in some third
+partly library header to have the alignment requirement @code{N} and
+to force a warning whenever a variable of the type is not so aligned
+due to attribute @code{packed}.  Specifying the @code{copy} attribute
+on the definition on the unrelated @code{struct B} has the effect of
+copying all relevant attributes from the type referenced by the pointer
+expression to @code{struct B}.
+
+@smallexample
+struct __attribute__ ((aligned (N), warn_if_not_aligned (N)))
+A @{ /* @r{@dots{}} */ @};
+struct __attribute__ ((copy ( (struct A *)0)) B @{ /* @r{@dots{}} */ @};
+@end smallexample
+
 @item deprecated
 @itemx deprecated (@var{msg})
 @cindex @code{deprecated} type attribute
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index 5c95f67..c027acd 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -282,7 +282,8 @@ Objective-C and Objective-C++ Dialects}.
 -Walloc-zero  -Walloc-size-larger-than=@var{byte-size}
 -Walloca  -Walloca-larger-than=@var{byte-size} @gol
 -Wno-aggressive-loop-optimizations  -Warray-bounds  -Warray-bounds=@var{n} @gol
--Wno-attributes  -Wbool-compare  -Wbool-operation @gol
+-Wno-attributes -Wno-attribute-alias @gol
+-Wbool-compare  -Wbool-operation @gol
 -Wno-builtin-declaration-mismatch @gol
 -Wno-builtin-macro-redefined  -Wc90-c99-compat  -Wc99-c11-compat @gol
 -Wc++-compat  -Wc++11-compat  -Wc++14-compat  -Wc++17-compat  @gol
@@ -314,7 +315,7 @@ Objective-C and Objective-C++ Dialects}.
 -Winvalid-pch  -Wlarger-than=@var{byte-size} @gol
 -Wlogical-op  -Wlogical-not-parentheses  -Wlong-long @gol
 -Wmain  -Wmaybe-uninitialized  -Wmemset-elt-size  -Wmemset-transposed-args @gol
--Wmisleading-indentation  -Wmissing-attributes -Wmissing-braces @gol
+-Wmisleading-indentation  -Wno-missing-attributes -Wmissing-braces @gol
 -Wmissing-field-initializers  -Wmissing-include-dirs  -Wmissing-profile @gol
 -Wno-multichar  -Wmultistatement-macros  -Wnonnull  -Wnonnull-compare @gol
 -Wnormalized=@r{[}none@r{|}id@r{|}nfc@r{|}nfkc@r{]} @gol
@@ -4770,13 +4771,24 @@ about the layout of the file that the directive references.
 
 This warning is enabled by @option{-Wall} in C and C++.
 
-@item -Wmissing-attributes
+@item -Wno-missing-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
+affect the correctness or efficiency of generated code.  For example,
+the warning is issued for declarations of aliases that use attributes
+to specify less restrictive requirements than those of their targets.
+This typically represents a potential optimization oportunity rather
+than a hidden bug.  The @option{-Wattribute-alias} option controls warnings
+issued for mismatches between declarations of aliases and their targets
+that might be indicative of code generation bugs.
+Attributes considered include @code{alloc_align}, @code{alloc_size},
+@code{cold}, @code{const}, @code{hot}, @code{leaf}, @code{malloc},
+@code{nonnull}, @code{noreturn}, @code{nothrow}, @code{pure},
+@code{returns_nonnull}, and @code{returns_twice}.
+
+In C++, the warning is issued when an explicitcspecialization 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},
@@ -5786,10 +5798,28 @@ pointers. This warning level may give a larger number of
 false positives and is deactivated by default.
 @end table
 
-@item -Wattribute-alias
+@item -Wattribute-alias=@var{n}
+@itemx -Wno-attribute-alias
+@opindex -Wattribute-alias
+@opindex -Wno-attribute-alias
 Warn about declarations using the @code{alias} and similar attributes whose
-target is incompatible with the type of the alias.  @xref{Function Attributes,
-,Declaring Attributes of Functions}.
+target is incompatible with the type of the alias.
+@xref{Function Attributes,,Declaring Attributes of Functions}.
+The @option{-Wattribute-alias=1} is  enabled by @option{-Wall}.
+
+@table @gcctabopt
+@item -Wattribute-alias=1
+The default warning level of the @option{-Wattribute-alias} option diagnoses
+incompatibilities between the type of the alias declaration and that of its
+target.  Such incompatibilities are typically indicative of bugs.
+
+@item -Wattribute-alias=2
+At this level @option{-Wattribute-alias} also diagnoses mismatches between
+the set of attributes of the alias declaration and the attributes applied
+to its target.  Although in some cases such mismatches may indicate bugs,
+in other cases they may be benign and could be resolved simply by adding
+the missing attribute to the target.
+@end table
 
 @item -Wbool-compare
 @opindex Wno-bool-compare
diff --git a/gcc/testsuite/gcc.dg/Wattribute-alias.c b/gcc/testsuite/gcc.dg/Wattribute-alias.c
new file mode 100644
index 0000000..175e40b
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/Wattribute-alias.c
@@ -0,0 +1,49 @@
+/* PR middle-end/81824 - Warn for missing attributes with function aliases
+   { dg-do compile }
+   { dg-options "-Wall -Wattribute-alias=2" } */
+
+#define ATTR(...)   __attribute__ ((__VA_ARGS__))
+
+
+void
+target_no_nothrow (void)        /* { dg-message ".alias_nothrow. target declared here" } */
+{ }
+
+ATTR (alias ("target_no_nothrow"), nothrow) void
+alias_nothrow (void);           /* { dg-warning ".alias_nothrow. specifies more restrictive attribute than its target .target_no_nothrow.: .nothrow." } */
+
+
+ATTR (pure) int
+alias_pure (void);
+
+int
+target_no_pure (void)           /* { dg-message ".alias_pure. target declared here" } */
+{ return 0; }
+
+ATTR (alias ("target_no_pure")) int
+alias_pure (void);              /* { dg-warning ".alias_pure. specifies more restrictive attribute than its target .target_no_pure.: .pure." } */
+
+
+ATTR (const) int
+alias_const (void);
+
+int
+target_pure (void)              /* { dg-message ".alias_const. target declared here" } */
+{ return 0; }
+
+ATTR (alias ("target_pure")) int
+alias_const (void);             /* { dg-warning ".alias_const. specifies more restrictive attribute than its target .target_pure.: .const." } */
+
+
+/* There is no obvious relationship between the attributes on an ifunc
+   resolver and those on its aliases.  Verify that mismatches between
+   aliases and ifunc resolvers do not trigger warnings.  */
+
+typedef int F (void);
+
+ATTR (pure, leaf) F* resolve_to_const (void)
+{ return alias_const; }
+
+ATTR (ifunc ("resolve_to_const")) F alias_no_const_ifunc;
+ATTR (const, ifunc ("resolve_to_const")) F alias_const_ifunc;
+ATTR (ifunc ("resolve_to_const")) int alias_no_leaf_ifunc (void);
diff --git a/gcc/testsuite/gcc.dg/Wmissing-attributes.c b/gcc/testsuite/gcc.dg/Wmissing-attributes.c
new file mode 100644
index 0000000..2a98182
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/Wmissing-attributes.c
@@ -0,0 +1,95 @@
+/* PR middle-end/81824 - Warn for missing attributes with function aliases
+   { dg-do compile }
+   { dg-options "-Wall" } */
+
+#define ATTR(list)   __attribute__ (list)
+
+
+int alias_no_const (void);
+
+ATTR ((const)) int
+target_const (void)             /* { dg-message ".alias_no_const. target declared here" } */
+{ return 0; }
+
+ATTR ((alias ("target_const"))) int
+alias_no_const (void);          /* { dg-warning ".alias_no_const. specifies less restrictive attribute than its target .target_const.: .const." } */
+
+
+ATTR ((alloc_size (1), malloc)) void*
+target_malloc (int n)           /* { dg-message ".alias_no_malloc. target declared here" } */
+{ return __builtin_malloc (n); }
+
+ATTR ((alias ("target_malloc"))) void*
+alias_no_malloc (int);          /* { dg-warning ".alias_no_malloc. specifies less restrictive attributes than its target .target_malloc.: .alloc_size., .malloc." } */
+
+
+ATTR ((leaf)) int
+target_leaf (void)              /* { dg-message ".alias_no_leaf. target declared here" } */
+{ return 0; }
+
+ATTR ((alias ("target_leaf"))) int
+alias_no_leaf (void);           /* { dg-warning ".alias_no_leaf. specifies less restrictive attribute than its target .target_leaf.: .leaf." } */
+
+
+/* Verify that attributes noclone, noinline, and noipa on the target
+   don't cause a warning for aliases without the attribute.  */
+
+ATTR ((noclone)) int
+target_noclone (void)
+{ return 0; }
+
+ATTR ((alias ("target_noclone"))) int
+alias_no_noclone (void);
+
+
+ATTR ((noipa)) int
+target_noipa (void)
+{ return 0; }
+
+ATTR ((alias ("target_noipa"))) int
+alias_no_noipa (void);
+
+
+ATTR ((noinline)) int
+target_noinline (void)
+{ return 0; }
+
+ATTR ((alias ("target_noinline"))) int
+alias_no_noinline (void);
+
+
+ATTR ((nothrow)) int
+target_nothrow (void)           /* { dg-message ".alias_no_nothrow. target declared here" } */
+{ return 0; }
+
+ATTR ((alias ("target_nothrow"))) int
+alias_no_nothrow (void);        /* { dg-warning ".alias_no_nothrow. specifies less restrictive attribute than its target .target_nothrow.: .nothrow." } */
+
+
+/* Verify that attribute weak on the target doesn't cause and isn't
+   mentioned in a warning for aliases without the attribute.  */
+
+ATTR ((weak)) int
+target_weak (void)
+{ return 0; }
+
+ATTR ((alias ("target_weak"))) int
+alias_no_weak (void);
+
+
+ATTR ((nothrow, weak)) int
+target_nothrow_weak (void)      /* { dg-message ".alias_nothrow_no_weak. target declared here" } */
+{ return 0; }
+
+ATTR ((alias ("target_nothrow_weak"))) int
+alias_nothrow_no_weak (void);        /* { dg-warning ".alias_nothrow_no_weak. specifies less restrictive attribute than its target .target_nothrow_weak.: .nothrow." } */
+
+
+/* Verify that __typeof__ doesn't include attributes.  */
+
+ATTR ((cold)) int
+target_cold (void)
+{ return 0; }
+
+__typeof__ (target_cold) ATTR ((alias ("target_cold")))
+alias_cold;                   /* { dg-warning ".alias_cold. specifies less restrictive attribute than its target .target_cold.: .cold." } */
diff --git a/gcc/testsuite/gcc.dg/attr-copy-2.c b/gcc/testsuite/gcc.dg/attr-copy-2.c
new file mode 100644
index 0000000..1ac156e
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/attr-copy-2.c
@@ -0,0 +1,149 @@
+/* PR middle-end/81824 - Warn for missing attributes with function aliases
+   Exercise attribute copy for functions.
+   { dg-do compile }
+   { dg-options "-O2 -Wall" } */
+
+#define Assert(expr)   typedef char AssertExpr[2 * !!(expr) - 1]
+
+#define ATTR(list)   __attribute__ (list)
+
+/* Verify that referencing a symbol with no attributes is accepted
+   with no diagnostics.  */
+
+void ref0 (void);
+
+ATTR ((copy (ref0))) void
+f0 (void);
+
+/* Verify that referencing a symbol using the address-of and dereferencing
+   operators is also accepted with no diagnostics.  */
+
+ATTR ((copy (&ref0))) void f1 (void);
+ATTR ((copy (*ref0))) void f2 (void);
+
+/* Verify that referencing a symbol of a different kind than that
+   of the one the attribute is applied to is diagnosed.  */
+
+int v0;                       /* { dg-message "symbol .v0. referenced by .f3. declared here" } */
+
+ATTR ((copy (v0))) void
+f3 (void);                    /* { dg-warning ".copy. attribute ignored on a declaration of a different kind than referenced symbol" } */
+
+void f4 (void);              /* { dg-message "symbol .f4. referenced by .v1. declared here" } */
+
+ATTR ((copy (f4))) int
+v1;                           /* { dg-warning ".copy. attribute ignored on a declaration of a different kind than referenced symbol" } */
+
+
+ATTR ((copy (v0 + 1)))
+void f5 (void);               /* { dg-warning ".copy. attribute ignored on a declaration of a different kind than referenced symbol" } */
+
+void f6 (void);
+
+ATTR ((copy (f6 - 1)))
+int v1;                       /* { dg-warning ".copy. attribute ignored on a declaration of a different kind than referenced symbol" } */
+
+
+
+/* Verify that circular references of the copy function attribute
+   are handled gracefully (i.e., not by getting into an infinite
+   recursion) by issuing a diagnostic.  */
+
+void xref1 (void);
+ATTR ((copy (xref1))) void
+xref1 (void);                 /* { dg-warning ".copy. attribute ignored on a redeclaration of the referenced symbol" } */
+ATTR ((copy (xref1))) void
+xref1 (void);                 /* { dg-warning ".copy. attribute ignored on a redeclaration of the referenced symbol" } */
+ATTR ((copy (xref1), copy (xref1))) void
+xref1 (void);                 /* { dg-warning ".copy. attribute ignored on a redeclaration of the referenced symbol" } */
+
+
+/* Use attribute noreturn to verify that circular references propagate
+   atttibutes as expected, and unlike in the self-referential instances
+   above, without a warning.  Also use the address-of operator to make
+   sure it doesn't change anything.  */
+
+ATTR ((noreturn))      void xref2 (void);
+ATTR ((copy (xref2)))  void xref3 (void);
+ATTR ((copy (&xref3))) void xref4 (void);
+ATTR ((copy (xref4)))  void xref5 (void);
+ATTR ((copy (&xref5))) void xref6 (void);
+ATTR ((copy (xref6)))  void xref7 (void);
+ATTR ((copy (&xref7))) void xref8 (void);
+ATTR ((copy (xref8)))  void xref9 (void);
+ATTR ((copy (&xref9))) void xref2 (void);
+
+int call_ref2 (void) { xref2 (); }
+int call_ref3 (void) { xref3 (); }
+int call_ref4 (void) { xref4 (); }
+int call_ref5 (void) { xref5 (); }
+int call_ref6 (void) { xref6 (); }
+int call_ref7 (void) { xref7 (); }
+int call_ref8 (void) { xref8 (); }
+int call_ref9 (void) { xref9 (); }
+
+
+/* Verify that copying attributes from multiple symbols into one works
+   as expected.  */
+
+ATTR ((malloc)) void*
+xref10 (void);
+
+ATTR ((alloc_size (1)))
+void* xref11 (int);
+
+ATTR ((copy (xref10), copy (xref11)))
+void* xref12 (int);
+
+void* call_xref12 (void)
+{
+  void *p = xref12 (3);
+  __builtin___strcpy_chk (p, "123", __builtin_object_size (p, 0));   /* { dg-warning "\\\[-Wstringop-overflow=]" } */
+  return p;
+}
+
+
+/* Verify that attribute exclusions apply.  */
+
+ATTR ((const)) int
+fconst (void);
+
+ATTR ((pure)) int
+fpure (void);
+
+ATTR ((copy (fconst), copy (fpure))) int
+fconst_pure (void);           /* { dg-warning "ignoring attribute .pure. because it conflicts with attribute .const." } */
+
+
+/* Verify that attribute deprecated isn't copied (but referencing
+   the deprecated declaration still triggers a warning).  */
+
+ATTR ((deprecated)) void fdeprecated (void);
+
+ATTR ((copy (fdeprecated))) int fcurrent (void);  /* { dg-warning "\\\[-Wdeprecated-declarations]" } */
+ATTR ((copy (fcurrent))) int
+fcurrent2 (void);
+
+int call_fcurrent (void) { return fcurrent () + fcurrent2 (); }
+
+
+/* Verify that attributes are copied on a declaration using __typeof__
+   and that -Wmissing-attributes is not issued.  */
+
+ATTR ((cold)) int
+target_cold (void)
+{ return 0; }
+
+__typeof__ (target_cold) ATTR ((copy (target_cold), alias ("target_cold")))
+alias_cold;                   /* { dg-bogus "\\\[-Wmissing-attributes]." } */
+
+
+/* Verify that attribute alias is not copied.  This also indirectly
+   verifies that attribute copy itself isn't copied.  */
+
+ATTR ((noreturn)) void fnoret (void) { __builtin_abort (); }
+ATTR ((alias ("fnoret"), copy (fnoret))) void fnoret_alias (void);
+ATTR ((copy (fnoret_alias))) void fnoret2 (void) { __builtin_exit (1); }
+
+/* Expect no warning below.  */
+int call_noret (void) { fnoret2 (); }
diff --git a/gcc/testsuite/gcc.dg/attr-copy-3.c b/gcc/testsuite/gcc.dg/attr-copy-3.c
new file mode 100644
index 0000000..88e5e5e
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/attr-copy-3.c
@@ -0,0 +1,75 @@
+/* PR middle-end/81824 - Warn for missing attributes with function aliases
+   Exercise attribute copy for variables.
+   { dg-do compile }
+   { dg-options "-O2 -Wall" } */
+
+#define ATTR(list)   __attribute__ (list)
+
+/* Verify that referencing a symbol with no attributes is accepted
+   with no diagnostics.  */
+
+int ref0;
+
+ATTR ((copy (ref0))) long
+var0;
+
+/* Verify that referencing a symbol using the address-of and dereferencing
+   operators is also accepted with no diagnostics.  */
+
+ATTR ((copy (&ref0))) void* ptr0;
+ATTR ((copy (*&ref0))) int arr[1];
+
+/* Verify that referencing a symbol of a different kind than that
+   of the one the attribute is applied to is diagnosed.  */
+
+int ref1;                     /* { dg-message "previous declaration here" } */
+
+ATTR ((copy (ref1))) int
+ref1;                         /* { dg-warning ".copy. attribute ignored on a redeclaration of the referenced symbol " } */
+
+
+/* Verify that circular references of the copy variable attribute
+   are handled gracefully (i.e., not by getting into an infinite
+   recursion) by issuing a diagnostic.  */
+
+char xref1;
+ATTR ((copy (xref1))) char
+xref1;                        /* { dg-warning ".copy. attribute ignored on a redeclaration of the referenced symbol" } */
+ATTR ((copy (xref1))) char
+xref1;                        /* { dg-warning ".copy. attribute ignored on a redeclaration of the referenced symbol" } */
+ATTR ((copy (xref1), copy (xref1))) char
+xref1;                        /* { dg-warning ".copy. attribute ignored on a redeclaration of the referenced symbol" } */
+
+
+/* Use attribute unused to verify that circular references propagate
+   atttibutes as expected (expect no warnings the circular reference
+   or for any of the unused symbols).  Also use the address-of operator
+   to make sure it doesn't change anything.  */
+
+static ATTR ((unused))        int xref2;
+static ATTR ((copy (xref2)))  int xref3;
+static ATTR ((copy (&xref3))) int xref4;
+static ATTR ((copy (xref4)))  int xref5;
+static ATTR ((copy (&xref5))) int xref6;
+static ATTR ((copy (xref6)))  int xref7;
+static ATTR ((copy (&xref7))) int xref8;
+static ATTR ((copy (xref8)))  int xref9;
+static ATTR ((copy (&xref9))) int xref2;
+
+/* Verify that attribute exclusions apply.  */
+
+ATTR ((common)) int common_var;
+ATTR ((nocommon)) double nocommon_var;
+
+ATTR ((copy (common_var), copy (nocommon_var))) long
+common_copy;                  /* { dg-warning "ignoring attribute .nocommon. because it conflicts with attribute .common." } */
+
+
+/* Verify that attribute deprecated isn't copied.  */
+
+ATTR ((deprecated)) char deprecated_var;
+
+ATTR ((copy (deprecated_var))) int current_var;  /* { dg-warning "\\\[-Wdeprecated-declarations]" } */
+ATTR ((copy (current_var))) int current_var_2;
+
+int return_current_vars (void) { return current_var + current_var_2; }
diff --git a/gcc/testsuite/gcc.dg/attr-copy-4.c b/gcc/testsuite/gcc.dg/attr-copy-4.c
new file mode 100644
index 0000000..7020bad
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/attr-copy-4.c
@@ -0,0 +1,61 @@
+/* PR middle-end/81824 - Warn for missing attributes with function aliases
+   Exercise attribute copy for types.
+   { dg-do compile }
+   { dg-options "-O2 -Wall" } */
+
+#define Assert(expr)   typedef char AssertExpr[2 * !!(expr) - 1]
+
+#define ATTR(list)   __attribute__ (list)
+
+/* Use attribute packed to verify that type attributes are copied
+   from one type to another.  */
+
+struct ATTR ((packed)) PackedA { int i; char c; };
+
+Assert (__alignof (struct PackedA) == 1);
+
+struct ATTR ((copy ((struct PackedA*)0))) PackedB { long i; char c; };
+
+Assert (__alignof (struct PackedA) == __alignof (struct PackedB));
+
+struct PackedMember
+{
+  char c;
+  ATTR ((copy ((struct PackedB*)0))) double packed_mem;
+};
+
+Assert (__alignof (struct PackedMember) == 1);
+
+
+extern const struct PackedA packed;
+
+struct Unpacked { int i; char c; };
+Assert (__alignof (struct Unpacked) > 1);
+
+/* Verify that copying the packed attribute to the declaration
+   of an object is ignored with a warning.  (There should be
+   a way to copy just the subset of attributes from a type that
+   aren't ignored and won't cause a warning, maybe via attribute
+   copy_except or something like that.)  */
+extern ATTR ((copy ((struct PackedA*)0))) const struct Unpacked
+  unpacked;                   /* { dg-warning ".packed. attribute ignored" } */
+
+Assert (__alignof (packed) == 1);
+Assert (__alignof (unpacked) == __alignof (struct Unpacked));
+
+
+
+/* Verify that attribute deprecated isn't copied (but referencing
+   the deprecated type in the copy attribute still triggers a warning).  */
+
+struct ATTR ((aligned (8), deprecated))
+AlignedDeprecated { char c; };
+
+struct ATTR ((copy ((struct AlignedDeprecated *)0)))        /* { dg-warning "\\\[-Wdeprecated-declarations]" } */
+AlignedCopy { short s; };
+
+Assert (__alignof (struct AlignedCopy) == 8);
+
+struct AlignedCopy aligned_copy;
+
+Assert (__alignof (aligned_copy) == 8);
diff --git a/gcc/testsuite/gcc.dg/attr-copy.c b/gcc/testsuite/gcc.dg/attr-copy.c
new file mode 100644
index 0000000..27cd7bc
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/attr-copy.c
@@ -0,0 +1,33 @@
+/* PR middle-end/81824 - Warn for missing attributes with function aliases
+   Exercise error handling for attribute copy.
+   { dg-do compile }
+   { dg-options "-O2 -Wall" } */
+
+#define ATTR(list)   __attribute__ (list)
+
+/* Verify incorrect numbers of arguments.  */
+ATTR ((copy)) void
+fno_args (void);              /* { dg-error "wrong number of arguments specified for .copy. attribute" } */
+
+ATTR ((copy ())) void
+fno_args2 (void);             /* { dg-error "wrong number of arguments specified for .copy. attribute" } */
+
+ATTR ((copy (fno_args, fno_args))) void
+fmlti_args (void);            /* { dg-error "wrong number of arguments specified for .copy. attribute" } */
+
+/* Verify that referencing an undeclared symbol is rejected with an error.  */
+
+ATTR ((copy (foobar)))        /* { dg-error ".foobar. undeclared" } */
+void fundeclared (void);
+
+/* Verify that using a string argument triggers a descriptive error
+   (given attributes like alias and weakref using a string is a likely
+   mistake).  */
+
+ATTR ((copy ("foobar")))
+void fstring (void);          /* { dg-error ".copy. attribute argument cannot be a string" } */
+
+/* Ditto for an integer.  */
+
+ATTR ((copy (123)))
+void fnumber (void);          /* { dg-error ".copy. attribute argument cannot be a constant arithmetic expression" } */
diff --git a/libgomp/libgomp.h b/libgomp/libgomp.h
index 3a8cc2b..1a01d13 100644
--- a/libgomp/libgomp.h
+++ b/libgomp/libgomp.h
@@ -1089,16 +1089,26 @@ extern int gomp_test_nest_lock_25 (omp_nest_lock_25_t *) __GOMP_NOTHROW;
 # define attribute_hidden
 #endif
 
+#if __GNUC__ >= 9
+#  define HAVE_ATTRIBUTE_COPY
+#endif
+
+#ifdef HAVE_ATTRIBUTE_COPY
+# define attribute_copy(arg) __attribute__ ((copy (arg)))
+#else
+# define attribute_copy(arg)
+#endif
+
 #ifdef HAVE_ATTRIBUTE_ALIAS
 # define strong_alias(fn, al) \
-  extern __typeof (fn) al __attribute__ ((alias (#fn)));
+  extern __typeof (fn) al __attribute__ ((alias (#fn))) attribute_copy (fn);
 
 # define ialias_ulp	ialias_str1(__USER_LABEL_PREFIX__)
 # define ialias_str1(x)	ialias_str2(x)
 # define ialias_str2(x)	#x
 # define ialias(fn) \
   extern __typeof (fn) gomp_ialias_##fn \
-    __attribute__ ((alias (#fn))) attribute_hidden;
+    __attribute__ ((alias (#fn))) attribute_hidden attribute_copy (fn);
 # define ialias_redirect(fn) \
   extern __typeof (fn) fn __asm__ (ialias_ulp "gomp_ialias_" #fn) attribute_hidden;
 # define ialias_call(fn) gomp_ialias_ ## fn

[-- Attachment #3: glibc-attribute-copy.diff --]
[-- Type: text/x-patch, Size: 2574 bytes --]

diff --git a/include/libc-symbols.h b/include/libc-symbols.h
index 8b9273c..cb50192 100644
--- a/include/libc-symbols.h
+++ b/include/libc-symbols.h
@@ -132,7 +132,7 @@
 /* Define ALIASNAME as a strong alias for NAME.  */
 # define strong_alias(name, aliasname) _strong_alias(name, aliasname)
 # define _strong_alias(name, aliasname) \
-  extern __typeof (name) aliasname __attribute__ ((alias (#name)));
+  extern __typeof (name) aliasname __attribute__ ((alias (#name), copy (name)));
 
 /* This comes between the return type and function name in
    a function definition to make that definition weak.  */
@@ -143,14 +143,14 @@
    If weak aliases are not available, this defines a strong alias.  */
 # define weak_alias(name, aliasname) _weak_alias (name, aliasname)
 # define _weak_alias(name, aliasname) \
-  extern __typeof (name) aliasname __attribute__ ((weak, alias (#name)));
+  extern __typeof (name) aliasname __attribute__ ((weak, alias (#name), copy (name)));
 
 /* Same as WEAK_ALIAS, but mark symbol as hidden.  */
 # define weak_hidden_alias(name, aliasname) \
   _weak_hidden_alias (name, aliasname)
 # define _weak_hidden_alias(name, aliasname) \
   extern __typeof (name) aliasname \
-    __attribute__ ((weak, alias (#name), __visibility__ ("hidden")));
+    __attribute__ ((weak, alias (#name), __visibility__ ("hidden"), copy (name)));
 
 /* Declare SYMBOL as weak undefined symbol (resolved to 0 if not defined).  */
 # define weak_extern(symbol) _weak_extern (weak symbol)
@@ -532,7 +532,7 @@ for linking")
 #  define __hidden_ver1(local, internal, name) \
   extern __typeof (name) __EI_##name __asm__(__hidden_asmname (#internal)); \
   extern __typeof (name) __EI_##name \
-	__attribute__((alias (__hidden_asmname (#local))))
+    __attribute__((alias (__hidden_asmname (#local)), copy (name)))
 #  define hidden_ver(local, name)	__hidden_ver1(local, __GI_##name, name);
 #  define hidden_data_ver(local, name)	hidden_ver(local, name)
 #  define hidden_def(name)		__hidden_ver1(__GI_##name, name, name);
@@ -545,7 +545,8 @@ for linking")
 #  define __hidden_nolink1(local, internal, name, version) \
   __hidden_nolink2 (local, internal, name, version)
 #  define __hidden_nolink2(local, internal, name, version) \
-  extern __typeof (name) internal __attribute__ ((alias (#local))); \
+  extern __typeof (name) internal __attribute__ ((alias (#local), \
+						    copy (name)));	\
   __hidden_nolink3 (local, internal, #name "@" #version)
 #  define __hidden_nolink3(local, internal, vername) \
   __asm__ (".symver " #internal ", " vername);

[-- Attachment #4: glibc-gcc-81824-summary.log --]
[-- Type: text/x-log, Size: 1331 bytes --]

Diagnostic                        Count   Unique    Files
-Wmissing-attributes                 55        8        8
-Wattributes                         34       19       19

-Wattributes Instances:
  ../sysdeps/x86_64/multiarch/memchr.c:29
  ../sysdeps/x86_64/multiarch/memcmp.c:29
  ../sysdeps/x86_64/multiarch/memcpy_chk.c:29
  ../sysdeps/x86_64/multiarch/memmove_chk.c:29
  ../sysdeps/x86_64/multiarch/mempcpy_chk.c:29
  ../sysdeps/x86_64/multiarch/memset.c:29
  ../sysdeps/x86_64/multiarch/memset_chk.c:29
  ../sysdeps/x86_64/multiarch/strcat.c:29
  ../sysdeps/x86_64/multiarch/strchr.c:49
  ../sysdeps/x86_64/multiarch/strcmp.c:53
  ../sysdeps/x86_64/multiarch/strcpy.c:29
  ../sysdeps/x86_64/multiarch/strcspn.c:29
  ../sysdeps/x86_64/multiarch/strlen.c:29
  ../sysdeps/x86_64/multiarch/strncat.c:29
  ../sysdeps/x86_64/multiarch/strncmp.c:54
  ../sysdeps/x86_64/multiarch/strncpy.c:29
  ../sysdeps/x86_64/multiarch/strpbrk.c:29
  ../sysdeps/x86_64/multiarch/strrchr.c:28
  ../sysdeps/x86_64/multiarch/strspn.c:29

-Wmissing-attributes Instances:
  ./../include/libc-symbols.h:534
  iofputs_u.c:42
  putc_u.c:29
  ../sysdeps/x86_64/multiarch/memchr.c:30
  ../sysdeps/x86_64/multiarch/memcmp.c:31
  ../sysdeps/x86_64/multiarch/strchr.c:50
  ../sysdeps/x86_64/multiarch/strncat.c:30
  ../sysdeps/x86_64/multiarch/strrchr.c:29

  reply	other threads:[~2018-10-23  3:26 UTC|newest]

Thread overview: 16+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2018-10-01 21:01 Martin Sebor
2018-10-01 23:47 ` Joseph Myers
2018-10-23  7:29   ` Martin Sebor [this message]
2018-10-23 22:55     ` Joseph Myers
2018-10-24  9:06       ` Martin Sebor
2018-10-24 12:50         ` Joseph Myers
2018-10-24 17:00           ` Martin Sebor
2018-10-24 19:00             ` Joseph Myers
2018-10-24 21:29               ` Martin Sebor
2018-10-31 16:35                 ` Martin Sebor
2018-10-24 13:04         ` Joseph Myers
2018-10-24 18:33           ` Martin Sebor
2018-11-07 21:59         ` Jeff Law
2018-11-09 17:33           ` Martin Sebor
2018-11-12 18:29             ` Matthew Malcomson
2018-11-12 18:38               ` Martin Sebor

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=c2d6b0d8-a4d0-9f96-8a5f-716a7ef303d4@gmail.com \
    --to=msebor@gmail.com \
    --cc=gcc-patches@gcc.gnu.org \
    --cc=jason@redhat.com \
    --cc=joseph@codesourcery.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).