diff mbox series

handle attribute positional arguments consistently (PR 87541, 87542)

Message ID 81124e9d-8845-bd5b-1a27-2abd1c5d35e6@gmail.com
State New
Headers show
Series handle attribute positional arguments consistently (PR 87541, 87542) | expand

Commit Message

Martin Sebor Oct. 7, 2018, 10:38 p.m. UTC
While still testing an enhancement in the area of attributes
I ran across bugs and inconsistencies in how different handlers
deal with positional arguments.  The bugs are either an ICE due
to the handlers not consistently converting positional arguments
to constants (i.e., CONST_DEL to INTEGER_CST in C++) for which
downstream code is unprepared (PR 87541), or errors for valid
code (PR 87542), or failing to diagnose invalid arguments.
The other inconsistencies are simply in responding to the same
invalid condition differently for different attributes .

The attached patch introduces a new function to do validate
positional arguments in a uniform way and replaces the existing
handling with it.

As a consequence of the handling being made consistent a number
of tests needed adjusting. In addition, some invalid arguments
that were previously rejected with a hard error are diagnosed
with just a warning (invalid argument values in attribute format),
and in one other instance what previously triggered a warning is
now accepted without one (attribute alloc_size on a function
without a prototype).  I'd be up tightening things up if that's
preferable as long it's done consistently.

Tested on x86_64-linux.

Martin

PS It would be nice to have a general policy for how to respond
to invalid attribute arguments (warning or error).  Even better,
it would be ideal to move the validation from the front-ends and
back-ends into the middle-end.  I.e., describe attribute arguments
in more detail in struct attribute_spec and have decl_attributes
validate nut just the number of arguments but also their types
and, when appropriate, expected values.

Comments

Joseph Myers Oct. 8, 2018, 11:33 a.m. UTC | #1
On Sun, 7 Oct 2018, Martin Sebor wrote:

> While still testing an enhancement in the area of attributes
> I ran across bugs and inconsistencies in how different handlers
> deal with positional arguments.  The bugs are either an ICE due
> to the handlers not consistently converting positional arguments
> to constants (i.e., CONST_DEL to INTEGER_CST in C++) for which
> downstream code is unprepared (PR 87541), or errors for valid
> code (PR 87542), or failing to diagnose invalid arguments.
> The other inconsistencies are simply in responding to the same
> invalid condition differently for different attributes .

Note there's also a common bug that, where an integer constant expression 
should be required, an INTEGER_CST of pointer type is wrongly accepted.  
There's an INTEGRAL_TYPE_P check for sentinel attributes, and for 
constructor / destructor (see gcc.dg/initpri2.c for that being tested), 
but not for others.

> PS It would be nice to have a general policy for how to respond
> to invalid attribute arguments (warning or error).  Even better,
> it would be ideal to move the validation from the front-ends and
> back-ends into the middle-end.  I.e., describe attribute arguments
> in more detail in struct attribute_spec and have decl_attributes
> validate nut just the number of arguments but also their types
> and, when appropriate, expected values.

I don't think the middle-end can handle the default_conversion part of 
things (although attribute_spec could have the information for generic 
code in the front ends, rather than every attribute handler, to do that).
Manuel López-Ibáñez Oct. 8, 2018, 1:27 p.m. UTC | #2
On 07/10/18 23:38, Martin Sebor wrote:
> +  pretty_printer posval;
> +  if (pos != error_mark_node)
> +    {
> +      /* Only format the position value when it's valid.  By convention
> +	 do not quote constant integers.  */
> +      pp_space (&posval);
> +      if (TREE_CODE (pos) != INTEGER_CST)
> +	pp_begin_quote (&posval, pp_show_color (global_dc->printer));
> +      dump_generic_node (&posval, pos, 0, TDF_NONE, false);
> +      if (TREE_CODE (pos) != INTEGER_CST)
> +	pp_end_quote (&posval, pp_show_color (global_dc->printer));
> +    }

Sorry for the bike-shedding but is this really necessary?

First, you handle != INTEGER_CST separately, so you can simply use %qE for that 
case and %E for the rest. Nevertheless, I think the convention is 
(https://gcc.gnu.org/wiki/DiagnosticsGuidelines#Quoting): "elements such as 
numbers that do no refer to numeric constants that appear in the source code 
should not be quoted". Since this is a integer constant that appears in the 
source code, then it should be quoted.

Also, "value%s" where %s can be empty, will not translate correctly.

Perhaps you need a small function:

warn_attributes(const char * msg_no_value_no_arg,
                 const char * msg_no_value_arg,
		const char * msg_value_no_arg,
                 const char * msg_value_arg,
		tree atname, int argno, tree pos, ...)


> +  if (TREE_CODE (pos) != INTEGER_CST)
> +    {
> +      /* Only mention the argument number when it's non-zero.  */
> +      if (argno < 1)
> +	warning (OPT_Wattributes,
> +		 "%qE attribute argument value%s is not an integer "
> +		 "constant",
> +		 atname, pp_formatted_text (&posval));
> +      else
> +	warning (OPT_Wattributes,
> +		 "%qE attribute argument %i value%s is not an integer "
> +		 "constant",
> +		 atname, argno, pp_formatted_text (&posval));
> +	
> +      return NULL_TREE;
> +    }

So that in the code above you can say:

if (TREE_CODE (pos) != INTEGER_CST)
{
warn_attributes("%qE attribute argument value is not an integer",
		"%qE attribute argument %i value is not an integer",
		"%qE attribute argument value %qE is not an integer",
		"%qE attribute argument %i value %qE is not an integer",
                  atname, argno, pos);
return NULL_TREE;
}

Also, I wonder where input_location is pointing at for these warnings. There 
may be a better location. Clang is doing:

<source>:5:1: error: 'alloc_align' attribute requires parameter 1 to be an 
integer constant
ALIGN ("1") void*
^      ~~~
<source>:1:36: note: expanded from macro 'ALIGN'
#define ALIGN(N)   __attribute__ ((alloc_align (N)))
                                    ^            ~

while GCC does:

<source>:6:16: warning: alloc_align parameter outside range [-Wattributes]
6 | fpvi_str_1 (int);
   |                ^

Apart from the above, this seems a major improvement, so I hope it goes in.

Cheers,

Manuel.
Martin Sebor Oct. 8, 2018, 5:43 p.m. UTC | #3
On 10/08/2018 05:33 AM, Joseph Myers wrote:
> On Sun, 7 Oct 2018, Martin Sebor wrote:
>
>> While still testing an enhancement in the area of attributes
>> I ran across bugs and inconsistencies in how different handlers
>> deal with positional arguments.  The bugs are either an ICE due
>> to the handlers not consistently converting positional arguments
>> to constants (i.e., CONST_DEL to INTEGER_CST in C++) for which
>> downstream code is unprepared (PR 87541), or errors for valid
>> code (PR 87542), or failing to diagnose invalid arguments.
>> The other inconsistencies are simply in responding to the same
>> invalid condition differently for different attributes .
>
> Note there's also a common bug that, where an integer constant expression
> should be required, an INTEGER_CST of pointer type is wrongly accepted.
> There's an INTEGRAL_TYPE_P check for sentinel attributes, and for
> constructor / destructor (see gcc.dg/initpri2.c for that being tested),
> but not for others.

Right, thanks.

>
>> PS It would be nice to have a general policy for how to respond
>> to invalid attribute arguments (warning or error).  Even better,
>> it would be ideal to move the validation from the front-ends and
>> back-ends into the middle-end.  I.e., describe attribute arguments
>> in more detail in struct attribute_spec and have decl_attributes
>> validate nut just the number of arguments but also their types
>> and, when appropriate, expected values.
>
> I don't think the middle-end can handle the default_conversion part of
> things (although attribute_spec could have the information for generic
> code in the front ends, rather than every attribute handler, to do that).

I think it could be handled by each front-end passing the handler
a callback function (e.g., as a new argument to decl_attributes).
Built-ins wouldn't benefit from it but they can all be trusted
to specify integer literals as arguments.

Martin
Martin Sebor Oct. 8, 2018, 5:54 p.m. UTC | #4
On 10/08/2018 07:27 AM, Manuel López-Ibáñez wrote:
> On 07/10/18 23:38, Martin Sebor wrote:
>> +  pretty_printer posval;
>> +  if (pos != error_mark_node)
>> +    {
>> +      /* Only format the position value when it's valid.  By convention
>> +     do not quote constant integers.  */
>> +      pp_space (&posval);
>> +      if (TREE_CODE (pos) != INTEGER_CST)
>> +    pp_begin_quote (&posval, pp_show_color (global_dc->printer));
>> +      dump_generic_node (&posval, pos, 0, TDF_NONE, false);
>> +      if (TREE_CODE (pos) != INTEGER_CST)
>> +    pp_end_quote (&posval, pp_show_color (global_dc->printer));
>> +    }
>
> Sorry for the bike-shedding but is this really necessary?
>
> First, you handle != INTEGER_CST separately, so you can simply use %qE
> for that case and %E for the rest. Nevertheless, I think the convention
> is (https://gcc.gnu.org/wiki/DiagnosticsGuidelines#Quoting): "elements
> such as numbers that do no refer to numeric constants that appear in the
> source code should not be quoted". Since this is a integer constant that
> appears in the source code, then it should be quoted.

Ah, I forgot about this.  I'm happy to quote it.

>
> Also, "value%s" where %s can be empty, will not translate correctly.

Why not?  (I had thought about it but convinced myself it would
not be a problem, but it's certainly possible I missed something.)

>
> Perhaps you need a small function:
>
> warn_attributes(const char * msg_no_value_no_arg,
>                 const char * msg_no_value_arg,
>         const char * msg_value_no_arg,
>                 const char * msg_value_arg,
>         tree atname, int argno, tree pos, ...)
>
>
>> +  if (TREE_CODE (pos) != INTEGER_CST)
>> +    {
>> +      /* Only mention the argument number when it's non-zero.  */
>> +      if (argno < 1)
>> +    warning (OPT_Wattributes,
>> +         "%qE attribute argument value%s is not an integer "
>> +         "constant",
>> +         atname, pp_formatted_text (&posval));
>> +      else
>> +    warning (OPT_Wattributes,
>> +         "%qE attribute argument %i value%s is not an integer "
>> +         "constant",
>> +         atname, argno, pp_formatted_text (&posval));
>> +
>> +      return NULL_TREE;
>> +    }
>
> So that in the code above you can say:
>
> if (TREE_CODE (pos) != INTEGER_CST)
> {
> warn_attributes("%qE attribute argument value is not an integer",
>         "%qE attribute argument %i value is not an integer",
>         "%qE attribute argument value %qE is not an integer",
>         "%qE attribute argument %i value %qE is not an integer",
>                  atname, argno, pos);
> return NULL_TREE;
> }

Thanks.  If it it turns out I'm wrong about the translation I'll
see if I can use something like it.  The concern raised with this
approach in the past was that GCC's -Wformat can't check the format
strings but I can't think of a better way.

>
> Also, I wonder where input_location is pointing at for these warnings.
> There may be a better location. Clang is doing:
>
> <source>:5:1: error: 'alloc_align' attribute requires parameter 1 to be
> an integer constant
> ALIGN ("1") void*
> ^      ~~~
> <source>:1:36: note: expanded from macro 'ALIGN'
> #define ALIGN(N)   __attribute__ ((alloc_align (N)))
>                                    ^            ~
>
> while GCC does:
>
> <source>:6:16: warning: alloc_align parameter outside range [-Wattributes]
> 6 | fpvi_str_1 (int);
>   |                ^
>

Unfortunately, there is no location information associated with
attribute arguments -- they are locationless constants -- so
the warnings point at (or just past) the current function
declaration.  David would know how difficult it might be to
add it to make the diagnostics look better.

> Apart from the above, this seems a major improvement, so I hope it goes in.

Thanks
Martin
Martin Sebor Oct. 8, 2018, 11:22 p.m. UTC | #5
Attached is an updated patch with the INTEGRAL_TYPE_P test added
to detect constant non-integer arguments like (void*)0, and with
quoting made unconditional.  I also removed the pretty printer
business to avoid the "value%s" format, and modified the manual
to clarify when each of the attributes are applicable and what
their meaningful argument values are.

On 10/07/2018 04:38 PM, Martin Sebor wrote:
> While still testing an enhancement in the area of attributes
> I ran across bugs and inconsistencies in how different handlers
> deal with positional arguments.  The bugs are either an ICE due
> to the handlers not consistently converting positional arguments
> to constants (i.e., CONST_DEL to INTEGER_CST in C++) for which
> downstream code is unprepared (PR 87541), or errors for valid
> code (PR 87542), or failing to diagnose invalid arguments.
> The other inconsistencies are simply in responding to the same
> invalid condition differently for different attributes .
>
> The attached patch introduces a new function to do validate
> positional arguments in a uniform way and replaces the existing
> handling with it.
>
> As a consequence of the handling being made consistent a number
> of tests needed adjusting. In addition, some invalid arguments
> that were previously rejected with a hard error are diagnosed
> with just a warning (invalid argument values in attribute format),
> and in one other instance what previously triggered a warning is
> now accepted without one (attribute alloc_size on a function
> without a prototype).  I'd be up tightening things up if that's
> preferable as long it's done consistently.
>
> Tested on x86_64-linux.
>
> Martin
>
> PS It would be nice to have a general policy for how to respond
> to invalid attribute arguments (warning or error).  Even better,
> it would be ideal to move the validation from the front-ends and
> back-ends into the middle-end.  I.e., describe attribute arguments
> in more detail in struct attribute_spec and have decl_attributes
> validate nut just the number of arguments but also their types
> and, when appropriate, expected values.
PR c++/87541 - ICE using a constant decl as an attribute alloc_size argument
PR c++/87542 - bogus error on attribute format with a named constant argument

gcc/ChangeLog:

	PR c++/87541
	PR c++/87542
	* tree.c (type_argument_type): New function.
	* tree.h (type_argument_type): Declare it.
	* gcc/doc/extend.texi (alloc_align): Update and clarify.
	(alloc_size, nonnull, sentinel): Same.

gcc/c-family/ChangeLog:

	PR c++/87541
	PR c++/87542
	* c-attribs.c (positional_argument): New function.
	(handle_alloc_size_attribute): Use it and simplify.
	(handle_alloc_align_attribute): Same.
	(handle_assume_aligned_attribute): Same.
	(handle_nonnull_attribute): Same.
	* c-common.c (check_function_arguments): Pass fntype to
	check_function_format.
	* c-common.h (check_function_format): Add an argument.
	(PosArgFlags, positional_argument): Declare new type and function.
	* c-format.c (decode_format_attr): Add arguments.
	(check_format_string, get_constant): Same.
	(convert_format_name_to_system_name): Adjust.

gcc/testsuite/ChangeLog:

	PR c++/87541
	PR c++/87542
	* g++.dg/ext/attr-alloc_size.C: New test.
	* c-c++-common/pr71574.c: Adjust diagnostics.
	* c-c++-common/attributes-1.c: Same.
	* gcc.dg/attr-alloc_align-2.c: Same.
	* gcc.dg/attr-alloc_align-4.c: New test.
	* gcc.dg/attr-alloc_size-2.c: Adjust diagnostics.
	* gcc.dg/attr-alloc_size.c: Same.
	* gcc.dg/attr-assume_aligned-4.c: New test.
	* gcc.dg/format/attr-3.c: Adjust diagnostics.
	* gcc.dg/nonnull-2.c: Same.
	* obj-c++.dg/attributes/method-format-1.mm: Same.
	* obj-c++.dg/attributes/method-nonnull-1.mm: Same.
	* objc.dg/attributes/method-format-1.m: same.
	* objc.dg/attributes/method-nonnull-1.m: Same.

Index: gcc/c-family/c-attribs.c
===================================================================
--- gcc/c-family/c-attribs.c	(revision 264941)
+++ gcc/c-family/c-attribs.c	(working copy)
@@ -44,6 +44,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "tree-iterator.h"
 #include "opts.h"
 #include "gimplify.h"
+#include "tree-pretty-print.h"
 
 static tree handle_packed_attribute (tree *, tree, tree, int, bool *);
 static tree handle_nocommon_attribute (tree *, tree, tree, int, bool *);
@@ -492,6 +493,188 @@ attribute_takes_identifier_p (const_tree attr_id)
     return targetm.attribute_takes_identifier_p (attr_id);
 }
 
+/* Verify that argument value POS at position ARGNO to attribute NAME
+   applied to function TYPE refers to a function parameter at position
+   POS and the expected type CODE.  If so, return POS after default
+   conversions, if any.  Otherwise, issue appropriate warnings and
+   return null.  A non-zero 1-based ARGNO should be passed ib by
+   callers only for attributes with more than one argument.  */
+
+tree
+positional_argument (const_tree fntype, const_tree atname, tree pos,
+		     tree_code code, int argno /* = 0 */,
+		     int flags /* = PosArgFlags () */)
+{
+  if (pos && TREE_CODE (pos) != IDENTIFIER_NODE
+      && TREE_CODE (pos) != FUNCTION_DECL)
+    pos = default_conversion (pos);
+
+  tree postype = TREE_TYPE (pos);
+  if (pos == error_mark_node || !postype)
+    {
+      /* Only mention the positional argument number when it's non-zero.  */
+      if (argno < 1)
+	warning (OPT_Wattributes,
+		 "%qE attribute argument is invalid", atname);
+      else
+	warning (OPT_Wattributes,
+		 "%qE attribute argument %i is invalid", atname, argno);
+	
+      return NULL_TREE;
+    }
+
+  if (!INTEGRAL_TYPE_P (postype))
+    {
+      /* Handle this case specially to avoid mentioning the value
+	 of pointer constants in diagnostics.  Only mention
+	 the positional argument number when it's non-zero.  */
+      if (argno < 1)
+	warning (OPT_Wattributes,
+		 "%qE attribute argument has type %qT",
+		 atname, postype);
+      else
+	warning (OPT_Wattributes,
+		 "%qE attribute argument %i has type %qT",
+		 atname, argno, postype);
+	
+      return NULL_TREE;
+    }
+
+  if (TREE_CODE (pos) != INTEGER_CST)
+    {
+      /* Only mention the argument number when it's non-zero.  */
+      if (argno < 1)
+	warning (OPT_Wattributes,
+		 "%qE attribute argument value %qE is not an integer "
+		 "constant",
+		 atname, pos);
+      else
+	warning (OPT_Wattributes,
+		 "%qE attribute argument %i value %qE is not an integer "
+		 "constant",
+		 atname, argno, pos);
+	
+      return NULL_TREE;
+    }
+
+  /* Argument positions are 1-based.  */
+  if (integer_zerop (pos))
+    {
+      if (flags & posarg_zero)
+	/* Zero is explicitly allowed.  */
+	return pos;
+
+      if (argno < 1)
+	warning (OPT_Wattributes,
+		 "%qE attribute argument value %qE does not refer to "
+		 "a function parameter",
+		 atname, pos);
+      else
+	warning (OPT_Wattributes,
+		 "%qE attribute argument %i value %qE does not refer to "
+		 "a function parameter",
+		 atname, argno, pos);
+	
+      return NULL_TREE;
+    }
+
+  if (!prototype_p (fntype))
+    return pos;
+
+  /* Verify that the argument position does not exceed the number
+     of formal arguments to the function.  When POSARG_ELLIPSIS
+     is set, ARGNO may be beyond the last argument of a vararg
+     function.  */
+  unsigned nargs = type_num_arguments (fntype);
+  if (!nargs
+      || !tree_fits_uhwi_p (pos)
+      || ((flags & posarg_ellipsis) == 0
+	  && !IN_RANGE (tree_to_uhwi (pos), 1, nargs)))
+    {
+
+      if (argno < 1)
+	warning (OPT_Wattributes,
+		 "%qE attribute argument value %qE exceeds the number "
+		 "of function parameters %u",
+		 atname, pos, nargs);
+      else
+	warning (OPT_Wattributes,
+		 "%qE attribute argument %i value %qE exceeds the number "
+		 "of function parameters %u",
+		 atname, argno, pos, nargs);
+      return NULL_TREE;
+    }
+
+  /* Verify that the type of the referenced formal argument matches
+     the expected type.  */
+  unsigned HOST_WIDE_INT ipos = tree_to_uhwi (pos);
+
+  /* Zero was handled above.  */
+  gcc_assert (ipos != 0);
+
+  if (tree argtype = type_argument_type (fntype, ipos))
+    {
+      if (flags & posarg_ellipsis)
+	{
+	  if (argno < 1)
+	    error ("%qE attribute argument value %qE does not refer to "
+		   "a variable argument list",
+		   atname, pos);
+	  else
+	    error ("%qE attribute argument %i value %qE does not refer to "
+		   "a variable argument list",
+		   atname, argno, pos);
+	  return NULL_TREE;
+	}
+
+      /* Where the expected code is STRING_CST accept any pointer
+	 to a narrow character type, qualified or otherwise.  */
+      bool type_match;
+      if (code == STRING_CST && POINTER_TYPE_P (argtype))
+	{
+	  tree type = TREE_TYPE (argtype);
+	  type = TYPE_MAIN_VARIANT (type);
+	  type_match = (type == char_type_node
+			|| type == signed_char_type_node
+			|| type == unsigned_char_type_node);
+	}
+      else
+	type_match = TREE_CODE (argtype) == code;
+
+      if (!type_match)
+	{
+	  if (argno < 1)
+	    warning (OPT_Wattributes,
+		     "%qE attribute argument value %qE refers to "
+		     "parameter type %qT",
+		     atname, pos, argtype);
+	  else
+	    warning (OPT_Wattributes,
+		   "%qE attribute argument %i value %qE refers to "
+		     "parameter type %qT",
+		     atname, argno, pos, argtype);
+	  return NULL_TREE;
+	}
+    }
+  else if (!(flags & posarg_ellipsis))
+    {
+      if (argno < 1)
+	warning (OPT_Wattributes,
+		 "%qE attribute argument value %qE refers to "
+		 "a variadic function parameter of unknown type",
+		 atname, pos);
+      else
+	warning (OPT_Wattributes,
+		 "%qE attribute argument %i value %qE refers to "
+		 "a variadic function parameter of unknown type",
+		 atname, argno, pos);
+      return NULL_TREE;
+    }
+
+  return pos;
+}
+
+
 /* Attribute handlers common to C front ends.  */
 
 /* Handle a "packed" attribute; arguments as in
@@ -2398,27 +2581,40 @@ handle_malloc_attribute (tree *node, tree name, tr
    struct attribute_spec.handler.  */
 
 static tree
-handle_alloc_size_attribute (tree *node, tree ARG_UNUSED (name), tree args,
+handle_alloc_size_attribute (tree *node, tree name, tree args,
 			     int ARG_UNUSED (flags), bool *no_add_attrs)
 {
-  unsigned arg_count = type_num_arguments (*node);
-  for (; args; args = TREE_CHAIN (args))
+  tree decl = *node;
+  tree rettype = TREE_TYPE (decl);
+  if (!POINTER_TYPE_P (rettype))
     {
-      tree position = TREE_VALUE (args);
-      if (position && TREE_CODE (position) != IDENTIFIER_NODE
-	  && TREE_CODE (position) != FUNCTION_DECL)
-	position = default_conversion (position);
+      warning (OPT_Wattributes,
+	       "%qE attribute ignored on a function returning %qT",
+	       name, rettype);
+      *no_add_attrs = true;
+      return NULL_TREE;
+    }
 
-      if (!tree_fits_uhwi_p (position)
-	  || !arg_count
-	  || !IN_RANGE (tree_to_uhwi (position), 1, arg_count))
+  for (int i = 1; args; ++i)
+    {
+      tree pos = TREE_VALUE (args);
+      /* NEXT is null when the attribute includes just one argument.
+	 That's used to tell positional_argument to avoid mentioning
+	 the argument number in diagnostics (since there's just one
+	 mentioning it is unnecessary and coule be confusing).  */
+      tree next = TREE_CHAIN (args);
+      if (tree val = positional_argument (decl, name, pos, INTEGER_TYPE,
+					  next || i > 1 ? i : 0))
+	TREE_VALUE (args) = val;
+      else
 	{
-	  warning (OPT_Wattributes,
-	           "alloc_size parameter outside range");
 	  *no_add_attrs = true;
-	  return NULL_TREE;
+	  break;
 	}
+
+      args = next;
     }
+
   return NULL_TREE;
 }
 
@@ -2426,24 +2622,23 @@ static tree
    struct attribute_spec.handler.  */
 
 static tree
-handle_alloc_align_attribute (tree *node, tree, tree args, int,
+handle_alloc_align_attribute (tree *node, tree name, tree args, int,
 			      bool *no_add_attrs)
 {
-  unsigned arg_count = type_num_arguments (*node);
-  tree position = TREE_VALUE (args);
-  if (position && TREE_CODE (position) != IDENTIFIER_NODE
-      && TREE_CODE (position) != FUNCTION_DECL)
-    position = default_conversion (position);
-
-  if (!tree_fits_uhwi_p (position)
-      || !arg_count
-      || !IN_RANGE (tree_to_uhwi (position), 1, arg_count))
+  tree decl = *node;
+  tree rettype = TREE_TYPE (decl);
+  if (!POINTER_TYPE_P (rettype))
     {
       warning (OPT_Wattributes,
-	       "alloc_align parameter outside range");
+	       "%qE attribute ignored on a function returning %qT",
+	       name, rettype);
       *no_add_attrs = true;
       return NULL_TREE;
     }
+
+  if (!positional_argument (*node, name, TREE_VALUE (args), INTEGER_TYPE))
+    *no_add_attrs = true;
+
   return NULL_TREE;
 }
 
@@ -2451,23 +2646,63 @@ static tree
    struct attribute_spec.handler.  */
 
 static tree
-handle_assume_aligned_attribute (tree *, tree, tree args, int,
+handle_assume_aligned_attribute (tree *node, tree name, tree args, int,
 				 bool *no_add_attrs)
 {
+  tree decl = *node;
+  tree rettype = TREE_TYPE (decl);
+  if (TREE_CODE (rettype) != POINTER_TYPE)
+    {
+      warning (OPT_Wattributes,
+	       "%qE attribute ignored on a function returning %qT",
+	       name, rettype);
+      *no_add_attrs = true;
+      return NULL_TREE;
+    }
+
+  /* The alignment specified by the first argument.  */
+  tree align = NULL_TREE;
+
   for (; args; args = TREE_CHAIN (args))
     {
-      tree position = TREE_VALUE (args);
-      if (position && TREE_CODE (position) != IDENTIFIER_NODE
-	  && TREE_CODE (position) != FUNCTION_DECL)
-	position = default_conversion (position);
+      tree val = TREE_VALUE (args);
+      if (val && TREE_CODE (val) != IDENTIFIER_NODE
+	  && TREE_CODE (val) != FUNCTION_DECL)
+	val = default_conversion (val);
 
-      if (TREE_CODE (position) != INTEGER_CST)
+      if (!tree_fits_shwi_p (val))
 	{
 	  warning (OPT_Wattributes,
-		   "assume_aligned parameter not integer constant");
+		   "%qE attribute %E is not an integer constant",
+		   name, val);
 	  *no_add_attrs = true;
 	  return NULL_TREE;
 	}
+
+      if (!align)
+	{
+	  /* Validate and save the alignment.  */
+	  if (!integer_pow2p (val))
+	    {
+	      warning (OPT_Wattributes,
+		       "%qE attribute argument %E is not a power of 2",
+		       name, val);
+	      *no_add_attrs = true;
+	      return NULL_TREE;
+	    }
+
+	  align = val;
+	}
+      else if (tree_int_cst_sgn (val) < 0 || tree_int_cst_le (align, val))
+	{
+	  /* The misalignment specified by the second argument
+	     must be non-negative and less than the alignment.  */
+	  warning (OPT_Wattributes,
+		   "%qE attribute argument %E is not in the range [0, %E)",
+		   name, val, align);
+	  *no_add_attrs = true;
+	  return NULL_TREE;
+	}
     }
   return NULL_TREE;
 }
@@ -3049,12 +3284,11 @@ handle_vector_size_attribute (tree *node, tree nam
 /* Handle the "nonnull" attribute.  */
 
 static tree
-handle_nonnull_attribute (tree *node, tree ARG_UNUSED (name),
+handle_nonnull_attribute (tree *node, tree name,
 			  tree args, int ARG_UNUSED (flags),
 			  bool *no_add_attrs)
 {
   tree type = *node;
-  unsigned HOST_WIDE_INT attr_arg_num;
 
   /* If no arguments are specified, all pointer arguments should be
      non-null.  Verify a full prototype is given so that the arguments
@@ -3073,57 +3307,23 @@ static tree
       return NULL_TREE;
     }
 
-  /* Argument list specified.  Verify that each argument number references
-     a pointer argument.  */
-  for (attr_arg_num = 1; args; attr_arg_num++, args = TREE_CHAIN (args))
+  for (int i = 1; args; ++i)
     {
-      unsigned HOST_WIDE_INT arg_num = 0, ck_num;
-
-      tree arg = TREE_VALUE (args);
-      if (arg && TREE_CODE (arg) != IDENTIFIER_NODE
-	  && TREE_CODE (arg) != FUNCTION_DECL)
-	TREE_VALUE (args) = arg = default_conversion (arg);
-
-      if (!get_nonnull_operand (arg, &arg_num))
+      tree pos = TREE_VALUE (args);
+      /* NEXT is null when the attribute includes just one argument.
+	 That's used to tell positional_argument to avoid mentioning
+	 the argument number in diagnostics (since there's just one
+	 mentioning it is unnecessary and coule be confusing).  */
+      tree next = TREE_CHAIN (args);
+      if (tree val = positional_argument (type, name, pos, POINTER_TYPE,
+					  next || i > 1 ? i : 0))
+	TREE_VALUE (args) = val;
+      else
 	{
-	  error ("nonnull argument has invalid operand number (argument %lu)",
-		 (unsigned long) attr_arg_num);
 	  *no_add_attrs = true;
-	  return NULL_TREE;
+	  break;
 	}
-
-      if (prototype_p (type))
-	{
-	  function_args_iterator iter;
-	  tree argument;
-
-	  function_args_iter_init (&iter, type);
-	  for (ck_num = 1; ; ck_num++, function_args_iter_next (&iter))
-	    {
-	      argument = function_args_iter_cond (&iter);
-	      if (argument == NULL_TREE || ck_num == arg_num)
-		break;
-	    }
-
-	  if (!argument
-	      || TREE_CODE (argument) == VOID_TYPE)
-	    {
-	      error ("nonnull argument with out-of-range operand number "
-		     "(argument %lu, operand %lu)",
-		     (unsigned long) attr_arg_num, (unsigned long) arg_num);
-	      *no_add_attrs = true;
-	      return NULL_TREE;
-	    }
-
-	  if (TREE_CODE (argument) != POINTER_TYPE)
-	    {
-	      error ("nonnull argument references non-pointer operand "
-		     "(argument %lu, operand %lu)",
-		     (unsigned long) attr_arg_num, (unsigned long) arg_num);
-	      *no_add_attrs = true;
-	      return NULL_TREE;
-	    }
-	}
+      args = next;
     }
 
   return NULL_TREE;
Index: gcc/c-family/c-common.c
===================================================================
--- gcc/c-family/c-common.c	(revision 264941)
+++ gcc/c-family/c-common.c	(working copy)
@@ -5622,7 +5622,8 @@ check_function_arguments (location_t loc, const_tr
   /* Check for errors in format strings.  */
 
   if (warn_format || warn_suggest_attribute_format)
-    check_function_format (TYPE_ATTRIBUTES (fntype), nargs, argarray, arglocs);
+    check_function_format (fntype, TYPE_ATTRIBUTES (fntype), nargs, argarray,
+			   arglocs);
 
   if (warn_format)
     check_function_sentinel (fntype, nargs, argarray);
Index: gcc/c-family/c-common.h
===================================================================
--- gcc/c-family/c-common.h	(revision 264941)
+++ gcc/c-family/c-common.h	(working copy)
@@ -804,7 +804,8 @@ extern void check_function_arguments_recurse (void
 					      unsigned HOST_WIDE_INT);
 extern bool check_builtin_function_arguments (location_t, vec<location_t>,
 					      tree, int, tree *);
-extern void check_function_format (tree, int, tree *, vec<location_t> *);
+extern void check_function_format (const_tree, tree, int, tree *,
+				   vec<location_t> *);
 extern bool attribute_fallthrough_p (tree);
 extern tree handle_format_attribute (tree *, tree, tree, int, bool *);
 extern tree handle_format_arg_attribute (tree *, tree, tree, int, bool *);
@@ -1322,6 +1323,18 @@ extern int tm_attr_to_mask (tree);
 extern tree tm_mask_to_attr (int);
 extern tree find_tm_attribute (tree);
 
+/* A bitmap of flags to positional_argument.  */
+enum PosArgFlags {
+  /* Consider positional attribute argument value zero valid.  */
+  posarg_zero = 1,
+  /* Consider positional attribute argument value valid if it refers
+     to the ellipsis (i.e., beyond the last typed argument).  */
+  posarg_ellipsis = 2
+};
+
+extern tree positional_argument (const_tree, const_tree, tree, tree_code,
+				 int = 0, int = PosArgFlags ());
+
 extern enum flt_eval_method
 excess_precision_mode_join (enum flt_eval_method, enum flt_eval_method);
 
Index: gcc/c-family/c-format.c
===================================================================
--- gcc/c-family/c-format.c	(revision 264941)
+++ gcc/c-family/c-format.c	(working copy)
@@ -62,15 +62,17 @@ static GTY(()) tree local_tree_type_node;
 static GTY(()) tree local_gimple_ptr_node;
 static GTY(()) tree locus;
 
-static bool decode_format_attr (tree, function_format_info *, int);
+static bool decode_format_attr (const_tree, tree, tree, function_format_info *,
+				bool);
 static int decode_format_type (const char *);
 
-static bool check_format_string (tree argument,
+static bool check_format_string (const_tree argument,
 				 unsigned HOST_WIDE_INT format_num,
 				 int flags, bool *no_add_attrs,
 				 int expected_format_type);
-static bool get_constant (tree expr, unsigned HOST_WIDE_INT *value,
-			  int validated_p);
+static tree get_constant (const_tree fntype, const_tree atname, tree expr,
+			  int argno, unsigned HOST_WIDE_INT *value,
+			  int flags, bool validated_p);
 static const char *convert_format_name_to_system_name (const char *attr_name);
 
 static int first_target_format_type;
@@ -132,16 +134,18 @@ valid_stringptr_type_p (tree strref)
 /* Handle a "format_arg" attribute; arguments as in
    struct attribute_spec.handler.  */
 tree
-handle_format_arg_attribute (tree *node, tree ARG_UNUSED (name),
+handle_format_arg_attribute (tree *node, tree atname,
 			     tree args, int flags, bool *no_add_attrs)
 {
   tree type = *node;
-  tree format_num_expr = TREE_VALUE (args);
+  tree *format_num_expr = &TREE_VALUE (args);
   unsigned HOST_WIDE_INT format_num = 0;
 
-  if (!get_constant (format_num_expr, &format_num, 0))
+  if (tree val = get_constant (type, atname, *format_num_expr, 0, &format_num,
+			       0, false))
+    *format_num_expr = val;
+  else
     {
-      error ("format string has invalid operand number");
       *no_add_attrs = true;
       return NULL_TREE;
     }
@@ -170,7 +174,7 @@ tree
    error).  When we know the specific reference type expected, this is also 
    checked.  */
 static bool
-check_format_string (tree fntype, unsigned HOST_WIDE_INT format_num,
+check_format_string (const_tree fntype, unsigned HOST_WIDE_INT format_num,
 		     int flags, bool *no_add_attrs, int expected_format_type)
 {
   unsigned HOST_WIDE_INT i;
@@ -263,19 +267,20 @@ static bool
 
 /* Verify EXPR is a constant, and store its value.
    If validated_p is true there should be no errors.
-   Returns true on success, false otherwise.  */
-static bool
-get_constant (tree expr, unsigned HOST_WIDE_INT *value, int validated_p)
+   Returns the converted constant value on success, null otherwise.  */
+static tree
+get_constant (const_tree fntype, const_tree atname, tree expr, int argno,
+	      unsigned HOST_WIDE_INT *value, int flags, bool validated_p)
 {
-  if (!tree_fits_uhwi_p (expr))
+  if (tree val = positional_argument (fntype, atname, expr, STRING_CST,
+				      argno, flags))
     {
-      gcc_assert (!validated_p);
-      return false;
+      *value = TREE_INT_CST_LOW (val);
+      return val;
     }
 
-  *value = TREE_INT_CST_LOW (expr);
-
-  return true;
+  gcc_assert (!validated_p);
+  return NULL_TREE;
 }
 
 /* Decode the arguments to a "format" attribute into a
@@ -286,12 +291,12 @@ static bool
    attributes are successfully decoded, false otherwise.  */
 
 static bool
-decode_format_attr (tree args, function_format_info *info, int validated_p)
+decode_format_attr (const_tree fntype, tree atname, tree args,
+		    function_format_info *info, bool validated_p)
 {
   tree format_type_id = TREE_VALUE (args);
-  tree format_num_expr = TREE_VALUE (TREE_CHAIN (args));
-  tree first_arg_num_expr
-    = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (args)));
+  tree *format_num_expr = &TREE_VALUE (TREE_CHAIN (args));
+  tree *first_arg_num_expr = &TREE_VALUE (TREE_CHAIN (TREE_CHAIN (args)));
 
   if (TREE_CODE (format_type_id) != IDENTIFIER_NODE)
     {
@@ -326,17 +331,18 @@ static bool
 	}
     }
 
-  if (!get_constant (format_num_expr, &info->format_num, validated_p))
-    {
-      error ("format string has invalid operand number");
-      return false;
-    }
+  if (tree val = get_constant (fntype, atname, *format_num_expr,
+			       2, &info->format_num, 0, validated_p))
+    *format_num_expr = val;
+  else
+    return false;
 
-  if (!get_constant (first_arg_num_expr, &info->first_arg_num, validated_p))
-    {
-      error ("%<...%> has invalid operand number");
-      return false;
-    }
+  if (tree val = get_constant (fntype, atname, *first_arg_num_expr,
+			       3, &info->first_arg_num,
+			       (posarg_zero | posarg_ellipsis), validated_p))
+    *first_arg_num_expr = val;
+  else
+    return false;
 
   if (info->first_arg_num != 0 && info->first_arg_num <= info->format_num)
     {
@@ -1076,11 +1082,13 @@ decode_format_type (const char *s)
    attribute themselves.  */
 
 void
-check_function_format (tree attrs, int nargs, tree *argarray,
-		       vec<location_t> *arglocs)
+check_function_format (const_tree fntype, tree attrs, int nargs,
+		       tree *argarray, vec<location_t> *arglocs)
 {
   tree a;
 
+  tree atname = get_identifier ("format");
+
   /* See if this function has any format attributes.  */
   for (a = attrs; a; a = TREE_CHAIN (a))
     {
@@ -1088,7 +1096,8 @@ void
 	{
 	  /* Yup; check it.  */
 	  function_format_info info;
-	  decode_format_attr (TREE_VALUE (a), &info, /*validated=*/true);
+	  decode_format_attr (fntype, atname, TREE_VALUE (a), &info,
+			      /*validated=*/true);
 	  if (warn_format)
 	    {
 	      /* FIXME: Rewrite all the internal functions in this file
@@ -4100,10 +4109,10 @@ convert_format_name_to_system_name (const char *at
 /* Handle a "format" attribute; arguments as in
    struct attribute_spec.handler.  */
 tree
-handle_format_attribute (tree *node, tree ARG_UNUSED (name), tree args,
+handle_format_attribute (tree *node, tree atname, tree args,
 			 int flags, bool *no_add_attrs)
 {
-  tree type = *node;
+  const_tree type = *node;
   function_format_info info;
 
 #ifdef TARGET_FORMAT_TYPES
@@ -4129,7 +4138,7 @@ tree
   if (TREE_CODE (TREE_VALUE (args)) == IDENTIFIER_NODE)
     TREE_VALUE (args) = canonicalize_attr_name (TREE_VALUE (args));
 
-  if (!decode_format_attr (args, &info, 0))
+  if (!decode_format_attr (type, atname, args, &info, /* validated_p = */false))
     {
       *no_add_attrs = true;
       return NULL_TREE;
Index: gcc/doc/extend.texi
===================================================================
--- gcc/doc/extend.texi	(revision 264941)
+++ gcc/doc/extend.texi	(working copy)
@@ -2401,21 +2401,23 @@ further information.
 The @code{aligned} attribute can also be used for variables and fields
 (@pxref{Variable Attributes}.)
 
-@item alloc_align
+@item alloc_align (@var{position})
 @cindex @code{alloc_align} function attribute
-The @code{alloc_align} attribute is used to tell the compiler that the
-function return value points to memory, where the returned pointer minimum
-alignment is given by one of the functions parameters.  GCC uses this
-information to improve pointer alignment analysis.
+The @code{alloc_align} attribute may be applied to a function that
+returns a pointer and takes at least one argument of an integer type.
+It indicates that the returned pointer is aligned on a boundary given
+by the function argument at @var{position}.  Meaningful alignments are
+powers of 2 greater than one.  GCC uses this information to improve
+pointer alignment analysis.
 
 The function parameter denoting the allocated alignment is specified by
-one integer argument, whose number is the argument of the attribute.
+one constant integer argument whose number is the argument of the attribute.
 Argument numbering starts at one.
 
 For instance,
 
 @smallexample
-void* my_memalign(size_t, size_t) __attribute__((alloc_align(1)))
+void* my_memalign (size_t, size_t) __attribute__ ((alloc_align (1)));
 @end smallexample
 
 @noindent
@@ -2422,12 +2424,16 @@ For instance,
 declares that @code{my_memalign} returns memory with minimum alignment
 given by parameter 1.
 
-@item alloc_size
+@item alloc_size (@var{position})
+@itemx alloc_size (@var{position-1}, @var{position-2})
 @cindex @code{alloc_size} function attribute
-The @code{alloc_size} attribute is used to tell the compiler that the
-function return value points to memory, where the size is given by
-one or two of the functions parameters.  GCC uses this
-information to improve the correctness of @code{__builtin_object_size}.
+The @code{alloc_size} attribute may be applied to a function that
+returns a pointer and takes at least one argument of an integer type.
+It indicates that the returned pointer points to memory whose size is
+given by the function argument at @var{position-1}, or by the product
+of the arguments at @var{position-1} and @var{position-2}.  Meaningful
+sizes are positive values less than @code{PTRDIFF_MAX}.  GCC uses this
+information to improve the results of @code{__builtin_object_size}.
 
 The function parameter(s) denoting the allocated size are specified by
 one or two integer arguments supplied to the attribute.  The allocated size
@@ -2438,8 +2444,8 @@ one.
 For instance,
 
 @smallexample
-void* my_calloc(size_t, size_t) __attribute__((alloc_size(1,2)))
-void* my_realloc(void*, size_t) __attribute__((alloc_size(2)))
+void* my_calloc (size_t, size_t) __attribute__ ((alloc_size (1, 2)));
+void* my_realloc (void*, size_t) __attribute__ ((alloc_size (2)));
 @end smallexample
 
 @noindent
@@ -2465,22 +2471,25 @@ info format it either means marking the function a
 or using the caller location for all instructions within the inlined
 body.
 
-@item assume_aligned
+@item assume_aligned (@var{alignment})
+@itemx assume_aligned (@var{alignment}, @var{offset})
 @cindex @code{assume_aligned} function attribute
-The @code{assume_aligned} attribute is used to tell the compiler that the
-function return value points to memory, where the returned pointer minimum
-alignment is given by the first argument.
-If the attribute has two arguments, the second argument is misalignment offset.
+The @code{assume_aligned} attribute may be applied to a function that
+returns a pointer.  It indicates that the returned pointer is aligned
+on a boundary given by @var{alignment}.  If the attribute has two
+arguments, the second argument is misalignment @var{offset}.  Meaningful
+values of @var{alignment} are powers of 2 greater than one.  Meaningful
+values of @var{offset} are greater than zero and less than @var{alignment}.
 
 For instance
 
 @smallexample
-void* my_alloc1(size_t) __attribute__((assume_aligned(16)))
-void* my_alloc2(size_t) __attribute__((assume_aligned(32, 8)))
+void* my_alloc1 (size_t) __attribute__((assume_aligned (16)));
+void* my_alloc2 (size_t) __attribute__((assume_aligned (32, 8)));
 @end smallexample
 
 @noindent
-declares that @code{my_alloc1} returns 16-byte aligned pointer and
+declares that @code{my_alloc1} returns 16-byte aligned pointers and
 that @code{my_alloc2} returns a pointer whose value modulo 32 is equal
 to 8.
 
@@ -3066,11 +3075,13 @@ including those that do not have an attribute suit
 them individually.  This attribute is supported mainly for the purpose
 of testing the compiler.
 
-@item nonnull (@var{arg-index}, @dots{})
+@item nonnull
+@itemx nonnull (@var{arg-index}, @dots{})
 @cindex @code{nonnull} function attribute
 @cindex functions with non-null pointer arguments
-The @code{nonnull} attribute specifies that some function parameters should
-be non-null pointers.  For instance, the declaration:
+The @code{nonnull} attribute may be applied to a function that takes at
+least one argument of a pointer type.  It indicates that the referenced
+arguments must be non-null pointers.  For instance, the declaration:
 
 @smallexample
 extern void *
@@ -3289,13 +3300,14 @@ If you need to map the entire contents of a module
 section, consider using the facilities of the linker instead.
 
 @item sentinel
+@itemx sentinel (@var{position})
 @cindex @code{sentinel} function attribute
-This function attribute ensures that a parameter in a function call is
-an explicit @code{NULL}.  The attribute is only valid on variadic
-functions.  By default, the sentinel is located at position zero, the
-last parameter of the function call.  If an optional integer position
-argument P is supplied to the attribute, the sentinel must be located at
-position P counting backwards from the end of the argument list.
+This function attribute indicates that an argument in a call to the function
+is expected to be an explicit @code{NULL}.  The attribute is only valid on
+variadic functions.  By default, the sentinel is expected to be the last
+argument of the function call.  If the optional @var{position} argument
+is specified to the attribute, the sentinel must be located at
+@var{position} counting backwards from the end of the argument list.
 
 @smallexample
 __attribute__ ((sentinel))
@@ -3307,10 +3319,11 @@ The attribute is automatically set with a position
 functions @code{execl} and @code{execlp}.  The built-in function
 @code{execle} has the attribute set with a position of 1.
 
-A valid @code{NULL} in this context is defined as zero with any pointer
-type.  If your system defines the @code{NULL} macro with an integer type
-then you need to add an explicit cast.  GCC replaces @code{stddef.h}
-with a copy that redefines NULL appropriately.
+A valid @code{NULL} in this context is defined as zero with any object
+pointer type.  If your system defines the @code{NULL} macro with
+an integer type then you need to add an explicit cast.  During
+installation GCC replaces the system @code{<stddef.h>} header with
+a copy that redefines NULL appropriately.
 
 The warnings for missing or incorrect sentinels are enabled with
 @option{-Wformat}.
Index: gcc/testsuite/c-c++-common/attributes-1.c
===================================================================
--- gcc/testsuite/c-c++-common/attributes-1.c	(revision 264941)
+++ gcc/testsuite/c-c++-common/attributes-1.c	(working copy)
@@ -1,21 +1,22 @@
 /* { dg-do compile } */
 /* { dg-prune-output "undeclared here \\(not in a function\\)|\[^\n\r\]* was not declared in this scope" } */
 
-void* my_calloc(unsigned, unsigned) __attribute__((alloc_size(1,bar))); /* { dg-warning "outside range" } */
-void* my_realloc(void*, unsigned) __attribute__((alloc_size(bar))); /* { dg-warning "outside range" } */
+void* my_calloc(unsigned, unsigned) __attribute__((alloc_size(1,bar))); /* { dg-warning ".alloc_size. attribute argument 2 is invalid" } */
+void* my_realloc(void*, unsigned) __attribute__((alloc_size(bar))); /* { dg-warning ".alloc_size. attribute argument is invalid" } */
 
 typedef char vec __attribute__((vector_size(bar))); /* { dg-warning "ignored" } */
 
-void f1(char*) __attribute__((nonnull(bar))); /* { dg-error "invalid operand" } */
-void f2(char*) __attribute__((nonnull(1,bar))); /* { dg-error "invalid operand" } */
+void f1(char*) __attribute__((nonnull(bar))); /* { dg-warning ".nonnull. attribute argument is invalid" } */
 
-void foo(void);
-void* my_calloc(unsigned, unsigned) __attribute__((alloc_size(1,foo))); /* { dg-warning "outside range" } */
-void* my_realloc(void*, unsigned) __attribute__((alloc_size(foo))); /* { dg-warning "outside range" } */
+void f2(char*) __attribute__((nonnull(1,bar))); /* { dg-warning ".nonnull. attribute argument 2 is invalid" } */
 
+void foo(int);
+void* my_calloc(unsigned, unsigned) __attribute__((alloc_size(1,foo))); /* { dg-warning ".alloc_size. attribute argument 2 has type .void\\\(int\\\)." } */
+void* my_realloc(void*, unsigned) __attribute__((alloc_size(foo))); /* { dg-warning ".alloc_size. attribute argument has type .void ?\\\(int\\\)" } */
+
 typedef char vec __attribute__((vector_size(foo))); /* { dg-warning "ignored" } */
 
-void f1(char*) __attribute__((nonnull(foo))); /* { dg-error "invalid operand" } */
-void f2(char*) __attribute__((nonnull(1,foo))); /* { dg-error "invalid operand" } */
+void f1(char*) __attribute__((nonnull(foo))); /* { dg-warning ".nonnull. attribute argument has type .void ?\\\(int\\\)." } */
+void f2(char*) __attribute__((nonnull(1,foo))); /* { dg-warning ".nonnull. attribute argument 2 has type .void ?\\\(int\\\)." } */
 
 void g() __attribute__((aligned(foo))); /* { dg-error "invalid value|not an integer" } */
Index: gcc/testsuite/c-c++-common/pr71574.c
===================================================================
--- gcc/testsuite/c-c++-common/pr71574.c	(revision 264941)
+++ gcc/testsuite/c-c++-common/pr71574.c	(working copy)
@@ -1,12 +1,15 @@
-/* PR c/71574 */
+/* PR c/71574 - ICE on code with alloc_align attribute */
 /* { dg-do compile } */
 
-int fn1 (void);
-int fn2 (void) __attribute__ ((alloc_align (fn1))); /* { dg-warning "parameter outside range" } */
-int fn3 (void) __attribute__ ((alloc_size (fn1))); /* { dg-warning "parameter outside range" } */
-int fn4 (void) __attribute__ ((assume_aligned (fn1))); /* { dg-warning "not integer constant" } */
-int fn5 (char *, char *) __attribute__((nonnull (fn1))); /* { dg-error "nonnull argument has invalid operand" } */
+int fn1 (int);
+int fn2 (void) __attribute__ ((alloc_align (fn1))); /* { dg-warning ".alloc_align. attribute ignored on a function returning .int." } */
+int fn3 (void) __attribute__ ((alloc_size (fn1))); /* { dg-warning ".alloc_size. attribute ignored on a function returning .int." } */
+int fn4 (void) __attribute__ ((assume_aligned (fn1))); /* { dg-warning ".assume_aligned. attribute ignored on a function returning .int." } */
+int fn5 (char *, char *) __attribute__((nonnull (fn1))); /* { dg-warning ".nonnull. attribute argument has type .int\\\(int\\\)." } */
 int fn6 (const char *, ...) __attribute__ ((sentinel (fn1))); /* { dg-warning "not an integer constant" } */
 
+void* fn7 (void) __attribute__ ((alloc_align (fn1))); /* { dg-warning ".alloc_align. attribute argument has type .int\\\(int\\\)." } */
+void* fn8 (void) __attribute__ ((assume_aligned (fn1))); /* { dg-warning "not an integer constant" } */
+
 typedef int __attribute__((vector_size (fn1))) v4si; /* { dg-warning "attribute ignored" } */
 typedef int T __attribute__((aligned (fn1))); /* { dg-error "requested alignment is not" } */
Index: gcc/testsuite/g++.dg/ext/attr-alloc_size.C
===================================================================
--- gcc/testsuite/g++.dg/ext/attr-alloc_size.C	(nonexistent)
+++ gcc/testsuite/g++.dg/ext/attr-alloc_size.C	(working copy)
@@ -0,0 +1,53 @@
+/* PR c++/87541 - ICE using a constant decl as an attribute alloc_size argument
+   { dg-do compile }
+   { dg-options "-O2 -Wall" } */
+
+#define ALLOC_SIZE(N)   __attribute__ ((alloc_size (N)))
+
+const int i1 = 1;
+ALLOC_SIZE (i1) void* fcst (int);
+
+void* call_fcst (void)
+{
+  void *p = fcst (1);
+  __builtin___memset_chk (p, 0, 2, __builtin_object_size (p, 1));   // { dg-warning "\\\[-Wstringop-overflow=" }
+  return p;
+}
+
+
+enum { e1 = 1 };
+ALLOC_SIZE (e1) void* fenum (int);
+
+void* call_fenum (void)
+{
+  void *p = fenum (1);
+  __builtin___memset_chk (p, 0, 2, __builtin_object_size (p, 1));   // { dg-warning "\\\[-Wstringop-overflow=" }
+  return p;
+}
+
+
+template <class T>
+struct A
+{
+  ALLOC_SIZE (T::N1) static void* ftemplarg_1 (int);
+  ALLOC_SIZE (T::N2) static void*
+  ftemplarg_2 (int); // { dg-warning "attribute argument value .2. exceeds the number of function parameters 1" }
+};
+
+struct B { static const int N1 = 1; static const int N2 = 1; };
+
+void* call_ftemplarg_1 (A<B> *pa)
+{
+  void *p = pa->ftemplarg_1 (1);
+  __builtin___memset_chk (p, 0, 2, __builtin_object_size (p, 1));   // { dg-warning "\\\[-Wstringop-overflow=" }
+  return p;
+}
+
+struct C { static const int N1 = 1; static const int N2 = 2; };
+
+void* call_ftemplarg_2 (A<C> *pa)
+{
+  void *p = pa->ftemplarg_2 (1);
+  __builtin___memset_chk (p, 0, 2, __builtin_object_size (p, 1));
+  return p;
+}
Index: gcc/testsuite/gcc.dg/attr-alloc_align-2.c
===================================================================
--- gcc/testsuite/gcc.dg/attr-alloc_align-2.c	(revision 264941)
+++ gcc/testsuite/gcc.dg/attr-alloc_align-2.c	(working copy)
@@ -5,6 +5,6 @@ void *f1 (int) __attribute__((alloc_align (1)));
 void *f2 (int, int, int) __attribute__((alloc_align (3)));
 void *f3 (void) __attribute__((alloc_align)); /* { dg-error "wrong number of arguments specified" } */
 void *f4 (int, int) __attribute__((alloc_align (1, 2))); /* { dg-error "wrong number of arguments specified" } */
-void *f5 (void) __attribute__((alloc_align (i))); /* { dg-warning "outside range" } */
-void *f6 (int) __attribute__((alloc_align (0))); /* { dg-warning "outside range" } */
-void *f7 (int) __attribute__((alloc_align (2))); /* { dg-warning "outside range" } */
+void *f5 (void) __attribute__((alloc_align (i))); /* { dg-warning ".alloc_align. attribute argument value .i. is not an integer constant" } */
+void *f6 (int) __attribute__((alloc_align (0))); /* { dg-warning ".alloc_align. attribute argument value .0. does not refer to a function parameter" } */
+void *f7 (int) __attribute__((alloc_align (2))); /* { dg-warning ".alloc_align. attribute argument value .2. exceeds the number of function parameters 1" } */
Index: gcc/testsuite/gcc.dg/attr-alloc_align-4.c
===================================================================
--- gcc/testsuite/gcc.dg/attr-alloc_align-4.c	(nonexistent)
+++ gcc/testsuite/gcc.dg/attr-alloc_align-4.c	(working copy)
@@ -0,0 +1,43 @@
+/* PR middle-end/81871 - bogus attribute alloc_align accepted
+   { dg-do compile }
+   { dg-options "-Wall" } */
+
+#define ALIGN(N)   __attribute__ ((alloc_align (N)))
+#define SIZE_MAX  __SIZE_MAX__
+
+ALIGN (1) void fvv_m1 (void);     /* { dg-warning ".alloc_align. attribute ignored on a function returning .void." } */
+
+ALIGN (1) int fiv_1 (void);       /* { dg-warning ".alloc_align. attribute ignored on a function returning .int." } */
+
+ALIGN (0) void* fpvv_0 (void);    /* { dg-warning ".alloc_align. attribute argument value .0. does not refer to a function parameter" } */
+
+ALIGN (1) void* fpvv_1 (void);    /* { dg-warning ".alloc_align. attribute argument value .1. exceeds the number of function parameters 0" } */
+
+ALIGN (2) void* fii_2 (int);      /* { dg-warning ".alloc_align. attribute argument value .2. exceeds the number of function parameters 1" } */
+
+ALIGN (1) void fvi_1 (int);       /* { dg-warning ".alloc_align. attribute ignored on a function returning .void." } */
+
+/* Using alloc_align with a function returning a pointer to a function
+   should perhaps trigger a warning.  */
+typedef void (F)(void);
+ALIGN (1) F* fpF_i_1 (int);
+
+ALIGN (SIZE_MAX) void*
+fpvi_szmax (int);                 /* { dg-warning ".alloc_align. attribute argument value .\[0-9\]+. exceeds the number of function parameters 1" } */
+
+ALIGN ("1") void*
+fpvi_str_1 (int);                 /* { dg-warning ".alloc_align. attribute argument has type .char\\\[2]" } */
+
+ALIGN ((void*)0) void*
+fpvi_pv0 (int);                   /* { dg-warning ".alloc_align. attribute argument has type .void \\\*." } */
+
+ALIGN ((double*)1) void*
+fpvi_pd1 (int);                   /* { dg-warning ".alloc_align. attribute argument has type .double \\\*." } */
+
+ALIGN (1) void*
+fpvi_pv_1 (void*);                /* { dg-warning ".alloc_align. attribute argument value .1. refers to parameter type .void \\\*." } */
+
+struct S { int i; };
+ALIGN (2) void*
+fpvi_S_2 (int, struct S);         /* { dg-warning ".alloc_align. attribute argument value .2. refers to parameter type .struct S." } */
+
Index: gcc/testsuite/gcc.dg/attr-alloc_size-12.c
===================================================================
--- gcc/testsuite/gcc.dg/attr-alloc_size-12.c	(nonexistent)
+++ gcc/testsuite/gcc.dg/attr-alloc_size-12.c	(working copy)
@@ -0,0 +1,60 @@
+/* PR middle-end/81871 - bogus attribute alloc_align accepted
+   Test exercising the problem with attribute alloc_size.
+   { dg-do compile }
+   { dg-options "-Wall" } */
+
+#define ASIZE(...)   __attribute__ ((alloc_size (__VA_ARGS__)))
+#define SIZE_MAX  __SIZE_MAX__
+
+ASIZE (-1) void fvv_m1 (void);    /* { dg-warning ".alloc_size. attribute ignored on a function returning .void." } */
+
+ASIZE (1) int fiv_1 (void);       /* { dg-warning ".alloc_size. attribute ignored on a function returning .int." } */
+
+ASIZE (1, 2) int fiv_1_2 (void);  /* { dg-warning ".alloc_size. attribute ignored on a function returning .int." } */
+
+ASIZE (0) void* fpvv_0 (void);    /* { dg-warning ".alloc_size. attribute argument value .0. does not refer to a function parameter" } */
+
+ASIZE (1, 0) void*
+fpvv_1_0 (int);                   /* { dg-warning ".alloc_size. attribute argument 2 value .0. does not refer to a function parameter" } */
+
+ASIZE (1) void* fpvv_1 (void);    /* { dg-warning ".alloc_size. attribute argument value .1. exceeds the number of function parameters 0" } */
+
+ASIZE (1, 9) void*
+fpvv_1_9 (int);                   /* { dg-warning ".alloc_size. attribute argument 2 value .9. exceeds the number of function parameters 1" } */
+
+ASIZE (2) void* fii_2 (int);      /* { dg-warning ".alloc_size. attribute argument value .2. exceeds the number of function parameters 1" } */
+
+ASIZE (1) void fvi_1 (int);       /* { dg-warning ".alloc_size. attribute ignored on a function returning .void." } */
+
+/* Using alloc_size with a function returning a pointer to a function
+   should perhaps trigger a warning.  */
+typedef void (F)(void);
+ASIZE (1) F* fpF_i_1 (int);
+
+ASIZE (SIZE_MAX) void*
+fpvi_szmax (int);                 /* { dg-warning ".alloc_size. attribute argument value .\[0-9\]+. exceeds the number of function parameters 1" } */
+
+ASIZE ("12") void*
+fpvi_str_1 (int);                 /* { dg-warning ".alloc_size. attribute argument has type .char\\\[3]." } */
+
+ASIZE (1, "123") void*
+fpvi_str_2 (int, int);            /* { dg-warning ".alloc_size. attribute argument 2 has type .char\\\[4]." } */
+
+ASIZE ((void*)0) void*
+fpvi_pv0 (int);                   /* { dg-warning ".alloc_size. attribute argument has type .void \\\*." } */
+
+ASIZE ((double*)sizeof (double)) void*
+fpvi_pd1 (int);                   /* { dg-warning ".alloc_size. attribute argument has type .double \\\*." } */
+
+ASIZE (1) void*
+fpvi_pv_1 (void*);                /* { dg-warning ".alloc_size. attribute argument value .1. refers to parameter type .void \\\*." } */
+
+struct S { int i; };
+ASIZE (2) void*
+fpvi_S_2 (int, struct S);         /* { dg-warning ".alloc_size. attribute argument value .2. refers to parameter type .struct S." } */
+
+ASIZE ((struct S){ 1 }) void*
+fpvi_S (int);                     /* { dg-warning ".alloc_size. attribute argument has type .struct S." } */
+
+ASIZE (1, (struct S){ 1 }) void*
+fpvi_1_S (int);                   /* { dg-warning ".alloc_size. attribute argument 2 has type .struct S." } */
Index: gcc/testsuite/gcc.dg/attr-alloc_size-2.c
===================================================================
--- gcc/testsuite/gcc.dg/attr-alloc_size-2.c	(revision 264941)
+++ gcc/testsuite/gcc.dg/attr-alloc_size-2.c	(working copy)
@@ -1,4 +1,5 @@
-/* { dg-do compile } */
+/* PR c/36021 - __attribute__((alloc_size(n))) with function of no
+   arguments causes gcc to segfault
+   { dg-do compile } */
 
-char *foo() __attribute__((alloc_size(1))); /* { dg-warning "outside range" } */
-
+char *foo() __attribute__((alloc_size(1)));
Index: gcc/testsuite/gcc.dg/attr-alloc_size.c
===================================================================
--- gcc/testsuite/gcc.dg/attr-alloc_size.c	(revision 264941)
+++ gcc/testsuite/gcc.dg/attr-alloc_size.c	(working copy)
@@ -5,13 +5,13 @@ extern void abort (void);
 
 #include "../gcc.c-torture/execute/builtins/chk.h"
 
-extern char *mallocminus1(int size) __attribute__((alloc_size(-1))); /* { dg-warning "parameter outside range" } */
-extern char *malloc0(int size) __attribute__((alloc_size(0))); /* { dg-warning "parameter outside range" } */
+extern char *mallocminus1(int size) __attribute__((alloc_size(-1))); /* { dg-warning ".alloc_size. attribute argument value .-1. exceeds the number of function parameters 1" } */
+extern char *malloc0(int size) __attribute__((alloc_size(0))); /* { dg-warning ".alloc_size. attribute argument value .0. does not refer to a function parameter" } */
 extern char *malloc1(int size) __attribute__((alloc_size(1)));
 extern char *malloc2(int empty, int size) __attribute__((alloc_size(2)));
 extern char *calloc1(int size, int elements) __attribute__((alloc_size(1,2)));
 extern char *calloc2(int size, int empty, int elements) __attribute__((alloc_size(1,3)));
-extern char *balloc1(void *size) __attribute__((alloc_size(1)));
+extern char *balloc1(void *size) __attribute__((alloc_size(1)));   /* { dg-warning ".alloc_size. attribute argument value .1. refers to parameter type .void *." } */
 
 void
 test (void)
Index: gcc/testsuite/gcc.dg/attr-assume_aligned-4.c
===================================================================
--- gcc/testsuite/gcc.dg/attr-assume_aligned-4.c	(nonexistent)
+++ gcc/testsuite/gcc.dg/attr-assume_aligned-4.c	(working copy)
@@ -0,0 +1,36 @@
+/* PR middle-end/87533 - bogus assume_aligned attribute silently accepted
+   { dg-do compile }
+   { dg-options "-Wall" } */
+
+#define A(...)  __attribute__ ((assume_aligned (__VA_ARGS__)))
+
+A (1) void fv_1 (void);       /* { dg-warning ".assume_aligned. attribute ignored on a function returning .void." } */
+
+A (1) int fi_1 (void);        /* { dg-warning ".assume_aligned. attribute ignored on a function returning .int." } */
+
+A (-1) void* fpv_m1 (void);   /* { dg-warning ".assume_aligned. attribute argument -1 is not a power of 2" } */
+
+A (0) void* fpv_0 (void);     /* { dg-warning ".assume_aligned. attribute argument 0 is not a power of 2" } */
+
+/* Alignment of 1 is fine, it just doesn't offer any benefits.  */
+A (1) void* fpv_1 (void);
+
+A (3) void* fpv_3 (void);     /* { dg-warning ".assume_aligned. attribute argument 3 is not a power of 2" } */
+
+A (16383) void* fpv_16km1 (void);     /* { dg-warning ".assume_aligned. attribute argument 16383 is not a power of 2" } */
+A (16384) void* fpv_16k (void);
+A (16385) void* fpv_16kp1 (void);    /* { dg-warning ".assume_aligned. attribute argument 16385 is not a power of 2" } */
+
+A (32767) void* fpv_32km1 (void);     /* { dg-warning ".assume_aligned. attribute argument 32767 is not a power of 2" } */
+
+A (4, -1) void* fpv_4_m1 (void);      /* { dg-warning ".assume_aligned. attribute argument -1 is not in the range \\\[0, 4\\\)" } */
+
+A (4, 0) void* fpv_4_0 (void);
+A (4, 1) void* fpv_4_1 (void);
+A (4, 2) void* fpv_4_2 (void);
+A (4, 3) void* fpv_4_3 (void);
+
+A (4, 4) void* fpv_4_3 (void);        /* { dg-warning ".assume_aligned. attribute argument 4 is not in the range \\\[0, 4\\\)" } */
+
+A (4) void* gpv_4_3 (void);
+A (2) void* gpv_4_3 (void);
Index: gcc/testsuite/gcc.dg/format/attr-3.c
===================================================================
--- gcc/testsuite/gcc.dg/format/attr-3.c	(revision 264941)
+++ gcc/testsuite/gcc.dg/format/attr-3.c	(working copy)
@@ -41,10 +41,10 @@ extern void fe1 (const char *, ...) __attribute__(
 /* Both the numbers must be integer constant expressions.  */
 extern void ff0 (const char *, ...) __attribute__((format(gnu_attr_printf, 3-2, (long long)(10/5))));
 int foo;
-extern void ff1 (const char *, ...) __attribute__((format(gnu_attr_printf, foo, 10/5))); /* { dg-error "invalid operand" "bad number" } */
-extern void ff2 (const char *, ...) __attribute__((format(gnu_attr_printf, 3-2, foo))); /* { dg-error "invalid operand" "bad number" } */
+extern void ff1 (const char *, ...) __attribute__((format(gnu_attr_printf, foo, 10/5))); /* { dg-warning ".format. attribute argument 2 value .foo. is not an integer constant" "bad number" } */
+extern void ff2 (const char *, ...) __attribute__((format(gnu_attr_printf, 3-2, foo))); /* { dg-warning ".format. attribute argument 3 value .foo. is not an integer constant" "bad number" } */
 extern char *ff3 (const char *) __attribute__((format_arg(3-2)));
-extern char *ff4 (const char *) __attribute__((format_arg(foo))); /* { dg-error "invalid operand" "bad format_arg number" } */
+extern char *ff4 (const char *) __attribute__((format_arg(foo))); /* { dg-warning ".format_arg. attribute argument value .foo. is not an integer constant" "bad format_arg number" } */
 
 /* The format string argument must precede the arguments to be formatted.
    This includes if no parameter types are specified (which is not valid ISO
@@ -56,14 +56,14 @@ extern void fg3 () __attribute__((format(gnu_attr_
 
 /* The format string argument must be a string type, and the arguments to
    be formatted must be the "...".  */
-extern void fh0 (int, ...) __attribute__((format(gnu_attr_printf, 1, 2))); /* { dg-error "not a string" "format int string" } */
+extern void fh0 (int, ...) __attribute__((format(gnu_attr_printf, 1, 2))); /* { dg-warning ".format. attribute argument 2 value .1. refers to parameter type .int." "format int string" } */
 extern void fh1 (signed char *, ...) __attribute__((format(gnu_attr_printf, 1, 2))); /* { dg-error "not a string" "signed char string" } */
 extern void fh2 (unsigned char *, ...) __attribute__((format(gnu_attr_printf, 1, 2))); /* { dg-error "not a string" "unsigned char string" } */
 extern void fh3 (const char *, ...) __attribute__((format(gnu_attr_printf, 1, 3))); /* { dg-error "is not" "not ..." } */
-extern void fh4 (const char *, int, ...) __attribute__((format(gnu_attr_printf, 1, 2))); /* { dg-error "is not" "not ..." } */
+extern void fh4 (const char *, int, ...) __attribute__((format(gnu_attr_printf, 1, 2))); /* { dg-error ".format. attribute argument 3 value .2. does not refer to a variable argument list" "not ..." } */
 
 /* format_arg formats must take and return a string.  */
-extern char *fi0 (int) __attribute__((format_arg(1))); /* { dg-error "not a string" "format_arg int string" } */
+extern char *fi0 (int) __attribute__((format_arg(1))); /* { dg-warning ".format_arg. attribute argument value .1. refers to parameter type .int." } */
 extern char *fi1 (signed char *) __attribute__((format_arg(1))); /* { dg-error "not a string" "format_arg signed char string" } */
 extern char *fi2 (unsigned char *) __attribute__((format_arg(1))); /* { dg-error "not a string" "format_arg unsigned char string" } */
 extern int fi3 (const char *) __attribute__((format_arg(1))); /* { dg-error "not return string" "format_arg ret int string" } */
Index: gcc/testsuite/gcc.dg/nonnull-2.c
===================================================================
--- gcc/testsuite/gcc.dg/nonnull-2.c	(revision 264941)
+++ gcc/testsuite/gcc.dg/nonnull-2.c	(working copy)
@@ -4,11 +4,12 @@
 
 extern void func1 () __attribute__((nonnull)); /* { dg-error "without arguments" } */
 
-extern void func2 (char *) __attribute__((nonnull(2))); /* { dg-error "out-of-range operand" } */
+extern void func2 (char *) __attribute__((nonnull(2))); /* { dg-warning ".nonnull. attribute argument value .2. exceeds the number of function parameters 1" } */
 
-extern void func3 (char *) __attribute__((nonnull(foo))); /* { dg-error "invalid operand number|undeclared" } */
+extern void func3 (char *) __attribute__((nonnull (foo))); /* { dg-warning ".nonnull. attribute argument is invalid" } */
+/* { dg-error ".foo. undeclared" "undeclared argument" { target *-*-* } .-1 } */
 
-extern void func4 (int) __attribute__((nonnull(1))); /* { dg-error "references non-pointer" } */
+extern void func4 (int) __attribute__((nonnull(1))); /* { dg-warning ".nonnull. attribute argument value .1. refers to parameter type .int." } */
 
 void
 foo (void)
Index: gcc/testsuite/obj-c++.dg/attributes/method-format-1.mm
===================================================================
--- gcc/testsuite/obj-c++.dg/attributes/method-format-1.mm	(revision 264941)
+++ gcc/testsuite/obj-c++.dg/attributes/method-format-1.mm	(working copy)
@@ -19,8 +19,8 @@
 - (void) log2: (int)level  message: (const char *) my_format, ...  __attribute__ ((format (printf, 2)));    /* { dg-error "wrong" } */
 + (void) debug2: (const char *) my_format, ...  __attribute__ ((format (printf))); /* { dg-error "wrong" } */
 - (void) debug2: (const char *) my_format, ...  __attribute__ ((format (printf))); /* { dg-error "wrong" } */
-+ (void) alert: (const char *) my_format __attribute__ ((format (printf, 1, 2))); /* { dg-error "args to be formatted is not ..." } */
-- (void) alert: (const char *) my_format __attribute__ ((format (printf, 1, 2))); /* { dg-error "args to be formatted is not ..." } */
++ (void) alert: (const char *) my_format __attribute__ ((format (printf, 1, 2))); /* { dg-error "does not refer to a variable argument list" } */
+- (void) alert: (const char *) my_format __attribute__ ((format (printf, 1, 2))); /* { dg-error "does not refer to a variable argument list" } */
 @end
 
 void test (LogObject *object)
Index: gcc/testsuite/obj-c++.dg/attributes/method-nonnull-1.mm
===================================================================
--- gcc/testsuite/obj-c++.dg/attributes/method-nonnull-1.mm	(revision 264941)
+++ gcc/testsuite/obj-c++.dg/attributes/method-nonnull-1.mm	(working copy)
@@ -19,17 +19,19 @@
 - (void) insertObject: (id)object  atIndex: (size_t)index  andObject: (id)anotherObject  atIndex: (size_t)anotherIndex __attribute__ ((nonnull (1, 3)));
 
 /* Test the behavior with invalid code.  */
-+ (void) removeObject: (id)object __attribute__ ((nonnull (0))); /* { dg-error "out-of-range" } */
-- (void) removeObject: (id)object __attribute__ ((nonnull (0))); /* { dg-error "out-of-range" } */
++ (void) removeObject: (id)object __attribute__ ((nonnull (0))); /* { dg-warning "does not refer to a function parameter" } */
+- (void) removeObject: (id)object __attribute__ ((nonnull (0))); /* { dg-warning "does not refer to a function parameter" } */
 
-+ (void) removeObject: (id)object __attribute__ ((nonnull (2))); /* { dg-error "out-of-range" } */
-- (void) removeObject: (id)object __attribute__ ((nonnull (2))); /* { dg-error "out-of-range" } */
++ (void) removeObject: (id)object __attribute__ ((nonnull (2))); /* { dg-warning "exceeds the number of function parameters 3" } */
+- (void) removeObject: (id)object __attribute__ ((nonnull (2))); /* { dg-warning "exceeds the number of function parameters 3" } */
 
-+ (void) removeObjectAtIndex: (size_t)object __attribute__ ((nonnull (1))); /* { dg-error "non-pointer operand" } */
-- (void) removeObjectAtIndex: (size_t)object __attribute__ ((nonnull (1))); /* { dg-error "non-pointer operand" } */
++ (void) removeObjectAtIndex: (size_t)object __attribute__ ((nonnull (1))); /* { dg-warning "refers to parameter type .size_t." } */
+- (void) removeObjectAtIndex: (size_t)object __attribute__ ((nonnull (1))); /* { dg-warning "refers to parameter type .size_t." } */
 
 + (void) removeObject: (id)object __attribute__ ((nonnull (MyArray))); /* { dg-error "" } */
+  /* { dg-warning "attribute argument is invalid" "" { target *-*-* } .-1 } */
 - (void) removeObject: (id)object __attribute__ ((nonnull (MyArray))); /* { dg-error "" } */
+  /* { dg-warning "attribute argument is invalid" "" { target *-*-* } .-1 } */
 @end
 
 void test (MyArray *object)
Index: gcc/testsuite/objc.dg/attributes/method-format-1.m
===================================================================
--- gcc/testsuite/objc.dg/attributes/method-format-1.m	(revision 264941)
+++ gcc/testsuite/objc.dg/attributes/method-format-1.m	(working copy)
@@ -19,8 +19,8 @@
 - (void) log2: (int)level  message: (const char *) my_format, ...  __attribute__ ((format (printf, 2)));    /* { dg-error "wrong" } */
 + (void) debug2: (const char *) my_format, ...  __attribute__ ((format (printf))); /* { dg-error "wrong" } */
 - (void) debug2: (const char *) my_format, ...  __attribute__ ((format (printf))); /* { dg-error "wrong" } */
-+ (void) alert: (const char *) my_format __attribute__ ((format (printf, 1, 2))); /* { dg-error "args to be formatted is not ..." } */
-- (void) alert: (const char *) my_format __attribute__ ((format (printf, 1, 2))); /* { dg-error "args to be formatted is not ..." } */
++ (void) alert: (const char *) my_format __attribute__ ((format (printf, 1, 2))); /* { dg-error "does not refer to a variable argument list" } */
+- (void) alert: (const char *) my_format __attribute__ ((format (printf, 1, 2))); /* { dg-error "does not refer to a variable argument list" } */
 @end
 
 void test (LogObject *object)
Index: gcc/testsuite/objc.dg/attributes/method-nonnull-1.m
===================================================================
--- gcc/testsuite/objc.dg/attributes/method-nonnull-1.m	(revision 264941)
+++ gcc/testsuite/objc.dg/attributes/method-nonnull-1.m	(working copy)
@@ -19,17 +19,17 @@
 - (void) insertObject: (id)object  atIndex: (size_t)index  andObject: (id)anotherObject  atIndex: (size_t)anotherIndex __attribute__ ((nonnull (1, 3)));
 
 /* Test the behavior with invalid code.  */
-+ (void) removeObject: (id)object __attribute__ ((nonnull (0))); /* { dg-error "out-of-range" } */
-- (void) removeObject: (id)object __attribute__ ((nonnull (0))); /* { dg-error "out-of-range" } */
++ (void) removeObject: (id)object __attribute__ ((nonnull (0))); /* { dg-warning "does not refer to a function parameter" } */
+- (void) removeObject: (id)object __attribute__ ((nonnull (0))); /* { dg-warning "does not refer to a function parameter" } */
 
-+ (void) removeObject: (id)object __attribute__ ((nonnull (2))); /* { dg-error "out-of-range" } */
-- (void) removeObject: (id)object __attribute__ ((nonnull (2))); /* { dg-error "out-of-range" } */
++ (void) removeObject: (id)object __attribute__ ((nonnull (2))); /* { dg-warning "exceeds the number of function parameters 3" } */
+- (void) removeObject: (id)object __attribute__ ((nonnull (2))); /* { dg-warning "exceeds the number of function parameters 3" } */
 
-+ (void) removeObjectAtIndex: (size_t)object __attribute__ ((nonnull (1))); /* { dg-error "non-pointer operand" } */
-- (void) removeObjectAtIndex: (size_t)object __attribute__ ((nonnull (1))); /* { dg-error "non-pointer operand" } */
++ (void) removeObjectAtIndex: (size_t)object __attribute__ ((nonnull (1))); /* { dg-warning "refers to parameter type .size_t." } */
+- (void) removeObjectAtIndex: (size_t)object __attribute__ ((nonnull (1))); /* { dg-warning "refers to parameter type .size_t." } */
 
-+ (void) removeObject: (id)object __attribute__ ((nonnull (MyArray))); /* { dg-error "invalid operand" } */
-- (void) removeObject: (id)object __attribute__ ((nonnull (MyArray))); /* { dg-error "invalid operand" } */
++ (void) removeObject: (id)object __attribute__ ((nonnull (MyArray))); /* { dg-warning "is not an integer constant" } */
+- (void) removeObject: (id)object __attribute__ ((nonnull (MyArray))); /* { dg-warning "is not an integer constant" } */
 @end
 
 void test (MyArray *object)
Index: gcc/tree.c
===================================================================
--- gcc/tree.c	(revision 264941)
+++ gcc/tree.c	(working copy)
@@ -4947,7 +4947,8 @@ build_nt_call_vec (tree fn, vec<tree, va_gc> *args
   return ret;
 }
 
-/* Create a DECL_... node of code CODE, name NAME and data type TYPE.
+/* Create a DECL_... node of code CODE, name NAME  (if non-null)
+   and data type TYPE.
    We do NOT enter this node in any sort of symbol table.
 
    LOC is the location of the decl.
@@ -6749,6 +6750,37 @@ type_num_arguments (const_tree type)
   return i;
 }
 
+/* Return the type of the function TYPE's argument ARGNO if known.
+   For vararg function's where ARGNO refers to one of the variadic
+   arguments return null.  Otherwise, return a void_type_node for
+   out-of-bounds ARGNO.  */
+
+tree
+type_argument_type (const_tree type, unsigned argno)
+{
+  /* Treat zero the same as an out-of-bounds argument number.  */
+  if (!argno)
+    return void_type_node;
+
+  unsigned i = 1;
+
+  for (tree t = TYPE_ARG_TYPES (type); ; t = TREE_CHAIN (t), ++i)
+    {
+      /* A vararg function's argument list ends in a null.  Otheriwse,
+	 an ordinary function's argument list ends with void.  Return
+	 null if ARGNO refers to a vararg argument, void_type_node if
+	 it's out of bounds, and the formal argument type otherwise.  */
+      if (!t)
+	break;
+
+      tree argtype = TREE_VALUE (t);
+      if (i == argno || VOID_TYPE_P (argtype))
+	return argtype;
+    }
+
+  return NULL_TREE;
+}
+
 /* Nonzero if integer constants T1 and T2
    represent the same constant value.  */
 
Index: gcc/tree.h
===================================================================
--- gcc/tree.h	(revision 264941)
+++ gcc/tree.h	(working copy)
@@ -4798,6 +4798,7 @@ extern tree get_file_function_name (const char *);
 extern tree get_callee_fndecl (const_tree);
 extern combined_fn get_call_combined_fn (const_tree);
 extern int type_num_arguments (const_tree);
+extern tree type_argument_type (const_tree, unsigned) ATTRIBUTE_NONNULL (1);
 extern bool associative_tree_code (enum tree_code);
 extern bool commutative_tree_code (enum tree_code);
 extern bool commutative_ternary_tree_code (enum tree_code);
Martin Sebor Oct. 16, 2018, 5:42 p.m. UTC | #6
Ping: https://gcc.gnu.org/ml/gcc-patches/2018-10/msg00473.html

On 10/08/2018 05:22 PM, Martin Sebor wrote:
> Attached is an updated patch with the INTEGRAL_TYPE_P test added
> to detect constant non-integer arguments like (void*)0, and with
> quoting made unconditional.  I also removed the pretty printer
> business to avoid the "value%s" format, and modified the manual
> to clarify when each of the attributes are applicable and what
> their meaningful argument values are.
> 
> On 10/07/2018 04:38 PM, Martin Sebor wrote:
>> While still testing an enhancement in the area of attributes
>> I ran across bugs and inconsistencies in how different handlers
>> deal with positional arguments.  The bugs are either an ICE due
>> to the handlers not consistently converting positional arguments
>> to constants (i.e., CONST_DEL to INTEGER_CST in C++) for which
>> downstream code is unprepared (PR 87541), or errors for valid
>> code (PR 87542), or failing to diagnose invalid arguments.
>> The other inconsistencies are simply in responding to the same
>> invalid condition differently for different attributes .
>>
>> The attached patch introduces a new function to do validate
>> positional arguments in a uniform way and replaces the existing
>> handling with it.
>>
>> As a consequence of the handling being made consistent a number
>> of tests needed adjusting. In addition, some invalid arguments
>> that were previously rejected with a hard error are diagnosed
>> with just a warning (invalid argument values in attribute format),
>> and in one other instance what previously triggered a warning is
>> now accepted without one (attribute alloc_size on a function
>> without a prototype).  I'd be up tightening things up if that's
>> preferable as long it's done consistently.
>>
>> Tested on x86_64-linux.
>>
>> Martin
>>
>> PS It would be nice to have a general policy for how to respond
>> to invalid attribute arguments (warning or error).  Even better,
>> it would be ideal to move the validation from the front-ends and
>> back-ends into the middle-end.  I.e., describe attribute arguments
>> in more detail in struct attribute_spec and have decl_attributes
>> validate nut just the number of arguments but also their types
>> and, when appropriate, expected values.
>
Jeff Law Oct. 16, 2018, 10:35 p.m. UTC | #7
On 10/8/18 5:22 PM, Martin Sebor wrote:
> Attached is an updated patch with the INTEGRAL_TYPE_P test added
> to detect constant non-integer arguments like (void*)0, and with
> quoting made unconditional.  I also removed the pretty printer
> business to avoid the "value%s" format, and modified the manual
> to clarify when each of the attributes are applicable and what
> their meaningful argument values are.
> 
> On 10/07/2018 04:38 PM, Martin Sebor wrote:
>> While still testing an enhancement in the area of attributes
>> I ran across bugs and inconsistencies in how different handlers
>> deal with positional arguments.  The bugs are either an ICE due
>> to the handlers not consistently converting positional arguments
>> to constants (i.e., CONST_DEL to INTEGER_CST in C++) for which
>> downstream code is unprepared (PR 87541), or errors for valid
>> code (PR 87542), or failing to diagnose invalid arguments.
>> The other inconsistencies are simply in responding to the same
>> invalid condition differently for different attributes .
>>
>> The attached patch introduces a new function to do validate
>> positional arguments in a uniform way and replaces the existing
>> handling with it.
>>
>> As a consequence of the handling being made consistent a number
>> of tests needed adjusting. In addition, some invalid arguments
>> that were previously rejected with a hard error are diagnosed
>> with just a warning (invalid argument values in attribute format),
>> and in one other instance what previously triggered a warning is
>> now accepted without one (attribute alloc_size on a function
>> without a prototype).  I'd be up tightening things up if that's
>> preferable as long it's done consistently.
>>
>> Tested on x86_64-linux.
>>
>> Martin
>>
>> PS It would be nice to have a general policy for how to respond
>> to invalid attribute arguments (warning or error).  Even better,
>> it would be ideal to move the validation from the front-ends and
>> back-ends into the middle-end.  I.e., describe attribute arguments
>> in more detail in struct attribute_spec and have decl_attributes
>> validate nut just the number of arguments but also their types
>> and, when appropriate, expected values.
> 
> 
> gcc-87541.diff
> 
> PR c++/87541 - ICE using a constant decl as an attribute alloc_size argument
> PR c++/87542 - bogus error on attribute format with a named constant argument
> 
> gcc/ChangeLog:
> 
> 	PR c++/87541
> 	PR c++/87542
> 	* tree.c (type_argument_type): New function.
> 	* tree.h (type_argument_type): Declare it.
> 	* gcc/doc/extend.texi (alloc_align): Update and clarify.
> 	(alloc_size, nonnull, sentinel): Same.
> 
> gcc/c-family/ChangeLog:
> 
> 	PR c++/87541
> 	PR c++/87542
> 	* c-attribs.c (positional_argument): New function.
> 	(handle_alloc_size_attribute): Use it and simplify.
> 	(handle_alloc_align_attribute): Same.
> 	(handle_assume_aligned_attribute): Same.
> 	(handle_nonnull_attribute): Same.
> 	* c-common.c (check_function_arguments): Pass fntype to
> 	check_function_format.
> 	* c-common.h (check_function_format): Add an argument.
> 	(PosArgFlags, positional_argument): Declare new type and function.
> 	* c-format.c (decode_format_attr): Add arguments.
> 	(check_format_string, get_constant): Same.
> 	(convert_format_name_to_system_name): Adjust.
> 
> gcc/testsuite/ChangeLog:
> 
> 	PR c++/87541
> 	PR c++/87542
> 	* g++.dg/ext/attr-alloc_size.C: New test.
> 	* c-c++-common/pr71574.c: Adjust diagnostics.
> 	* c-c++-common/attributes-1.c: Same.
> 	* gcc.dg/attr-alloc_align-2.c: Same.
> 	* gcc.dg/attr-alloc_align-4.c: New test.
> 	* gcc.dg/attr-alloc_size-2.c: Adjust diagnostics.
> 	* gcc.dg/attr-alloc_size.c: Same.
> 	* gcc.dg/attr-assume_aligned-4.c: New test.
> 	* gcc.dg/format/attr-3.c: Adjust diagnostics.
> 	* gcc.dg/nonnull-2.c: Same.
> 	* obj-c++.dg/attributes/method-format-1.mm: Same.
> 	* obj-c++.dg/attributes/method-nonnull-1.mm: Same.
> 	* objc.dg/attributes/method-format-1.m: same.
> 	* objc.dg/attributes/method-nonnull-1.m: Same.



> Index: gcc/c-family/c-common.c
> @@ -1322,6 +1323,18 @@ extern int tm_attr_to_mask (tree);
>  extern tree tm_mask_to_attr (int);
>  extern tree find_tm_attribute (tree);
>  
> +/* A bitmap of flags to positional_argument.  */
> +enum PosArgFlags {
> +  /* Consider positional attribute argument value zero valid.  */
> +  posarg_zero = 1,
> +  /* Consider positional attribute argument value valid if it refers
> +     to the ellipsis (i.e., beyond the last typed argument).  */
> +  posarg_ellipsis = 2
> +};
> +
> +extern tree positional_argument (const_tree, const_tree, tree, tree_code,
> +				 int = 0, int = PosArgFlags ());
> +
>  extern enum flt_eval_method
>  excess_precision_mode_join (enum flt_eval_method, enum flt_eval_method);
No camel case.  Make the enum type lower case and its values upper case.


> @@ -326,17 +331,18 @@ static bool
>  	}
>      }
>  
> -  if (!get_constant (format_num_expr, &info->format_num, validated_p))
> -    {
> -      error ("format string has invalid operand number");
> -      return false;
> -    }
> +  if (tree val = get_constant (fntype, atname, *format_num_expr,
> +			       2, &info->format_num, 0, validated_p))
> +    *format_num_expr = val;
> +  else
> +    return false;
Is it really a good idea to be modifying something inside of ARGS like
this?  At the very least the function comments neeed updating.


>  
> -  if (!get_constant (first_arg_num_expr, &info->first_arg_num, validated_p))
> -    {
> -      error ("%<...%> has invalid operand number");
> -      return false;
> -    }
> +  if (tree val = get_constant (fntype, atname, *first_arg_num_expr,
> +			       3, &info->first_arg_num,
> +			       (posarg_zero | posarg_ellipsis), validated_p))
> +    *first_arg_num_expr = val;
> +  else
> +    return false;
Similarly.



>  
> +/* Return the type of the function TYPE's argument ARGNO if known.
> +   For vararg function's where ARGNO refers to one of the variadic
> +   arguments return null.  Otherwise, return a void_type_node for
> +   out-of-bounds ARGNO.  */
> +
> +tree
> +type_argument_type (const_tree type, unsigned argno)
You might consider calling the argument "fntype".  That makes it a
little clearer what you're iterating over.

> +{
> +  /* Treat zero the same as an out-of-bounds argument number.  */
> +  if (!argno)
> +    return void_type_node;
> +
> +  unsigned i = 1;
> +
> +  for (tree t = TYPE_ARG_TYPES (type); ; t = TREE_CHAIN (t), ++i)
There's already iterators to walk over TYPE_ARG_TYPES.  See
FOREACH_FUNCTION_ARGS


Mostly good, probably requires one round of updating and we'll be good
to go.

jeff
Martin Sebor Oct. 25, 2018, 2:02 a.m. UTC | #8
On 10/16/2018 04:35 PM, Jeff Law wrote:
> On 10/8/18 5:22 PM, Martin Sebor wrote:
>> Attached is an updated patch with the INTEGRAL_TYPE_P test added
>> to detect constant non-integer arguments like (void*)0, and with
>> quoting made unconditional.  I also removed the pretty printer
>> business to avoid the "value%s" format, and modified the manual
>> to clarify when each of the attributes are applicable and what
>> their meaningful argument values are.
>>
>> On 10/07/2018 04:38 PM, Martin Sebor wrote:
>>> While still testing an enhancement in the area of attributes
>>> I ran across bugs and inconsistencies in how different handlers
>>> deal with positional arguments.  The bugs are either an ICE due
>>> to the handlers not consistently converting positional arguments
>>> to constants (i.e., CONST_DEL to INTEGER_CST in C++) for which
>>> downstream code is unprepared (PR 87541), or errors for valid
>>> code (PR 87542), or failing to diagnose invalid arguments.
>>> The other inconsistencies are simply in responding to the same
>>> invalid condition differently for different attributes .
>>>
>>> The attached patch introduces a new function to do validate
>>> positional arguments in a uniform way and replaces the existing
>>> handling with it.
>>>
>>> As a consequence of the handling being made consistent a number
>>> of tests needed adjusting. In addition, some invalid arguments
>>> that were previously rejected with a hard error are diagnosed
>>> with just a warning (invalid argument values in attribute format),
>>> and in one other instance what previously triggered a warning is
>>> now accepted without one (attribute alloc_size on a function
>>> without a prototype).  I'd be up tightening things up if that's
>>> preferable as long it's done consistently.
>>>
>>> Tested on x86_64-linux.
>>>
>>> Martin
>>>
>>> PS It would be nice to have a general policy for how to respond
>>> to invalid attribute arguments (warning or error).  Even better,
>>> it would be ideal to move the validation from the front-ends and
>>> back-ends into the middle-end.  I.e., describe attribute arguments
>>> in more detail in struct attribute_spec and have decl_attributes
>>> validate nut just the number of arguments but also their types
>>> and, when appropriate, expected values.
>>
>>
>> gcc-87541.diff
>>
>> PR c++/87541 - ICE using a constant decl as an attribute alloc_size argument
>> PR c++/87542 - bogus error on attribute format with a named constant argument
>>
>> gcc/ChangeLog:
>>
>> 	PR c++/87541
>> 	PR c++/87542
>> 	* tree.c (type_argument_type): New function.
>> 	* tree.h (type_argument_type): Declare it.
>> 	* gcc/doc/extend.texi (alloc_align): Update and clarify.
>> 	(alloc_size, nonnull, sentinel): Same.
>>
>> gcc/c-family/ChangeLog:
>>
>> 	PR c++/87541
>> 	PR c++/87542
>> 	* c-attribs.c (positional_argument): New function.
>> 	(handle_alloc_size_attribute): Use it and simplify.
>> 	(handle_alloc_align_attribute): Same.
>> 	(handle_assume_aligned_attribute): Same.
>> 	(handle_nonnull_attribute): Same.
>> 	* c-common.c (check_function_arguments): Pass fntype to
>> 	check_function_format.
>> 	* c-common.h (check_function_format): Add an argument.
>> 	(PosArgFlags, positional_argument): Declare new type and function.
>> 	* c-format.c (decode_format_attr): Add arguments.
>> 	(check_format_string, get_constant): Same.
>> 	(convert_format_name_to_system_name): Adjust.
>>
>> gcc/testsuite/ChangeLog:
>>
>> 	PR c++/87541
>> 	PR c++/87542
>> 	* g++.dg/ext/attr-alloc_size.C: New test.
>> 	* c-c++-common/pr71574.c: Adjust diagnostics.
>> 	* c-c++-common/attributes-1.c: Same.
>> 	* gcc.dg/attr-alloc_align-2.c: Same.
>> 	* gcc.dg/attr-alloc_align-4.c: New test.
>> 	* gcc.dg/attr-alloc_size-2.c: Adjust diagnostics.
>> 	* gcc.dg/attr-alloc_size.c: Same.
>> 	* gcc.dg/attr-assume_aligned-4.c: New test.
>> 	* gcc.dg/format/attr-3.c: Adjust diagnostics.
>> 	* gcc.dg/nonnull-2.c: Same.
>> 	* obj-c++.dg/attributes/method-format-1.mm: Same.
>> 	* obj-c++.dg/attributes/method-nonnull-1.mm: Same.
>> 	* objc.dg/attributes/method-format-1.m: same.
>> 	* objc.dg/attributes/method-nonnull-1.m: Same.
>
>
>
>> Index: gcc/c-family/c-common.c
>> @@ -1322,6 +1323,18 @@ extern int tm_attr_to_mask (tree);
>>  extern tree tm_mask_to_attr (int);
>>  extern tree find_tm_attribute (tree);
>>
>> +/* A bitmap of flags to positional_argument.  */
>> +enum PosArgFlags {
>> +  /* Consider positional attribute argument value zero valid.  */
>> +  posarg_zero = 1,
>> +  /* Consider positional attribute argument value valid if it refers
>> +     to the ellipsis (i.e., beyond the last typed argument).  */
>> +  posarg_ellipsis = 2
>> +};
>> +
>> +extern tree positional_argument (const_tree, const_tree, tree, tree_code,
>> +				 int = 0, int = PosArgFlags ());
>> +
>>  extern enum flt_eval_method
>>  excess_precision_mode_join (enum flt_eval_method, enum flt_eval_method);
> No camel case.  Make the enum type lower case and its values upper case.

Done.

As an aside, I almost thought that after nearly fours years
I've adjusted to most reviewers' preferences but I'm clearly
not quite there yet.  As usual, this is not mentioned in
the coding conventions (except for C++ template parameters
where it is the preferred spelling), and there are also
examples of different styles in GCC, including the one
I chose. For instance, in c-format.c the format_type enum
uses all lowercase enumerators.  There are also quite a few
(over a hundred in fact) examples of CamelCase enums in
various front-ends, back-ends, and other parts of GCC.

>> @@ -326,17 +331,18 @@ static bool
>>  	}
>>      }
>>
>> -  if (!get_constant (format_num_expr, &info->format_num, validated_p))
>> -    {
>> -      error ("format string has invalid operand number");
>> -      return false;
>> -    }
>> +  if (tree val = get_constant (fntype, atname, *format_num_expr,
>> +			       2, &info->format_num, 0, validated_p))
>> +    *format_num_expr = val;
>> +  else
>> +    return false;
> Is it really a good idea to be modifying something inside of ARGS like
> this?  At the very least the function comments neeed updating.

That's what the code does.  My patch doesn't change it, it just
adds a new variable.  I added a comment to mention it nonetheless.

>> -  if (!get_constant (first_arg_num_expr, &info->first_arg_num, validated_p))
>> -    {
>> -      error ("%<...%> has invalid operand number");
>> -      return false;
>> -    }
>> +  if (tree val = get_constant (fntype, atname, *first_arg_num_expr,
>> +			       3, &info->first_arg_num,
>> +			       (posarg_zero | posarg_ellipsis), validated_p))
>> +    *first_arg_num_expr = val;
>> +  else
>> +    return false;
> Similarly.

Same as above.  The original code passed &info->first_arg_num
to get_constant and changed info->first_arg_num there.  I just
introduced a temporary, first_arg_num_expr, and set it to
the address of info->first_arg_num.

>> +/* Return the type of the function TYPE's argument ARGNO if known.
>> +   For vararg function's where ARGNO refers to one of the variadic
>> +   arguments return null.  Otherwise, return a void_type_node for
>> +   out-of-bounds ARGNO.  */
>> +
>> +tree
>> +type_argument_type (const_tree type, unsigned argno)
> You might consider calling the argument "fntype".  That makes it a
> little clearer what you're iterating over.

Done.  I copied the function, including the argument name, from
from type_num_arguments (const_tree type), so I've changed that
one to match.

>
>> +{
>> +  /* Treat zero the same as an out-of-bounds argument number.  */
>> +  if (!argno)
>> +    return void_type_node;
>> +
>> +  unsigned i = 1;
>> +
>> +  for (tree t = TYPE_ARG_TYPES (type); ; t = TREE_CHAIN (t), ++i)
> There's already iterators to walk over TYPE_ARG_TYPES.  See
> FOREACH_FUNCTION_ARGS

As I explained above, I just copied another function just above
the one I added.

Besides the one in the function I copied there are six other
loops in this file and just one use of FOREACH_FUNCTION_ARGS.
I also think the macro is harder to understand and poor style.
It requires declaring an extra variable outside the scope of
the loop even if it isn't used.  So I kept the loop as is.

Retested on x86_64-linux.

Martin
PR c++/87541 - ICE using a constant decl as an attribute alloc_size argument
PR c++/87542 - bogus error on attribute format with a named constant argument

gcc/ChangeLog:

	PR c++/87541
	PR c++/87542
	* tree.c (type_argument_type): New function.
	* tree.h (type_argument_type): Declare it.
	* gcc/doc/extend.texi (alloc_align): Update and clarify.
	(alloc_size, nonnull, sentinel): Same.

gcc/c-family/ChangeLog:

	PR c++/87541
	PR c++/87542
	* c-attribs.c (positional_argument): New function.
	(handle_alloc_size_attribute): Use it and simplify.
	(handle_alloc_align_attribute): Same.
	(handle_assume_aligned_attribute): Same.
	(handle_nonnull_attribute): Same.
	* c-common.c (check_function_arguments): Pass fntype to
	check_function_format.
	* c-common.h (check_function_format): Add an argument.
	(PosArgFlags, positional_argument): Declare new type and function.
	* c-format.c (decode_format_attr): Add arguments.
	(check_format_string, get_constant): Same.
	(convert_format_name_to_system_name): Adjust.

gcc/testsuite/ChangeLog:

	PR c++/87541
	PR c++/87542
	* g++.dg/ext/attr-alloc_size.C: New test.
	* c-c++-common/pr71574.c: Adjust diagnostics.
	* c-c++-common/attributes-1.c: Same.
	* gcc.dg/attr-alloc_align-2.c: Same.
	* gcc.dg/attr-alloc_align-4.c: New test.
	* gcc.dg/attr-alloc_size-2.c: Adjust diagnostics.
	* gcc.dg/attr-alloc_size.c: Same.
	* gcc.dg/attr-assume_aligned-4.c: New test.
	* gcc.dg/format/attr-3.c: Adjust diagnostics.
	* gcc.dg/nonnull-2.c: Same.
	* gcc.dg/torture/pr80612.c: Same.
	* obj-c++.dg/attributes/method-format-1.mm: Same.
	* obj-c++.dg/attributes/method-nonnull-1.mm: Same.
	* objc.dg/attributes/method-format-1.m: same.
	* objc.dg/attributes/method-nonnull-1.m: Same.

diff --git a/gcc/c-family/c-attribs.c b/gcc/c-family/c-attribs.c
index 4416b50..1719401 100644
--- a/gcc/c-family/c-attribs.c
+++ b/gcc/c-family/c-attribs.c
@@ -44,6 +44,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "tree-iterator.h"
 #include "opts.h"
 #include "gimplify.h"
+#include "tree-pretty-print.h"
 
 static tree handle_packed_attribute (tree *, tree, tree, int, bool *);
 static tree handle_nocommon_attribute (tree *, tree, tree, int, bool *);
@@ -492,6 +493,188 @@ attribute_takes_identifier_p (const_tree attr_id)
     return targetm.attribute_takes_identifier_p (attr_id);
 }
 
+/* Verify that argument value POS at position ARGNO to attribute NAME
+   applied to function TYPE refers to a function parameter at position
+   POS and the expected type CODE.  If so, return POS after default
+   conversions, if any.  Otherwise, issue appropriate warnings and
+   return null.  A non-zero 1-based ARGNO should be passed ib by
+   callers only for attributes with more than one argument.  */
+
+tree
+positional_argument (const_tree fntype, const_tree atname, tree pos,
+		     tree_code code, int argno /* = 0 */,
+		     int flags /* = posargflags () */)
+{
+  if (pos && TREE_CODE (pos) != IDENTIFIER_NODE
+      && TREE_CODE (pos) != FUNCTION_DECL)
+    pos = default_conversion (pos);
+
+  tree postype = TREE_TYPE (pos);
+  if (pos == error_mark_node || !postype)
+    {
+      /* Only mention the positional argument number when it's non-zero.  */
+      if (argno < 1)
+	warning (OPT_Wattributes,
+		 "%qE attribute argument is invalid", atname);
+      else
+	warning (OPT_Wattributes,
+		 "%qE attribute argument %i is invalid", atname, argno);
+	
+      return NULL_TREE;
+    }
+
+  if (!INTEGRAL_TYPE_P (postype))
+    {
+      /* Handle this case specially to avoid mentioning the value
+	 of pointer constants in diagnostics.  Only mention
+	 the positional argument number when it's non-zero.  */
+      if (argno < 1)
+	warning (OPT_Wattributes,
+		 "%qE attribute argument has type %qT",
+		 atname, postype);
+      else
+	warning (OPT_Wattributes,
+		 "%qE attribute argument %i has type %qT",
+		 atname, argno, postype);
+	
+      return NULL_TREE;
+    }
+
+  if (TREE_CODE (pos) != INTEGER_CST)
+    {
+      /* Only mention the argument number when it's non-zero.  */
+      if (argno < 1)
+	warning (OPT_Wattributes,
+		 "%qE attribute argument value %qE is not an integer "
+		 "constant",
+		 atname, pos);
+      else
+	warning (OPT_Wattributes,
+		 "%qE attribute argument %i value %qE is not an integer "
+		 "constant",
+		 atname, argno, pos);
+	
+      return NULL_TREE;
+    }
+
+  /* Argument positions are 1-based.  */
+  if (integer_zerop (pos))
+    {
+      if (flags & POSARG_ZERO)
+	/* Zero is explicitly allowed.  */
+	return pos;
+
+      if (argno < 1)
+	warning (OPT_Wattributes,
+		 "%qE attribute argument value %qE does not refer to "
+		 "a function parameter",
+		 atname, pos);
+      else
+	warning (OPT_Wattributes,
+		 "%qE attribute argument %i value %qE does not refer to "
+		 "a function parameter",
+		 atname, argno, pos);
+	
+      return NULL_TREE;
+    }
+
+  if (!prototype_p (fntype))
+    return pos;
+
+  /* Verify that the argument position does not exceed the number
+     of formal arguments to the function.  When POSARG_ELLIPSIS
+     is set, ARGNO may be beyond the last argument of a vararg
+     function.  */
+  unsigned nargs = type_num_arguments (fntype);
+  if (!nargs
+      || !tree_fits_uhwi_p (pos)
+      || ((flags & POSARG_ELLIPSIS) == 0
+	  && !IN_RANGE (tree_to_uhwi (pos), 1, nargs)))
+    {
+
+      if (argno < 1)
+	warning (OPT_Wattributes,
+		 "%qE attribute argument value %qE exceeds the number "
+		 "of function parameters %u",
+		 atname, pos, nargs);
+      else
+	warning (OPT_Wattributes,
+		 "%qE attribute argument %i value %qE exceeds the number "
+		 "of function parameters %u",
+		 atname, argno, pos, nargs);
+      return NULL_TREE;
+    }
+
+  /* Verify that the type of the referenced formal argument matches
+     the expected type.  */
+  unsigned HOST_WIDE_INT ipos = tree_to_uhwi (pos);
+
+  /* Zero was handled above.  */
+  gcc_assert (ipos != 0);
+
+  if (tree argtype = type_argument_type (fntype, ipos))
+    {
+      if (flags & POSARG_ELLIPSIS)
+	{
+	  if (argno < 1)
+	    error ("%qE attribute argument value %qE does not refer to "
+		   "a variable argument list",
+		   atname, pos);
+	  else
+	    error ("%qE attribute argument %i value %qE does not refer to "
+		   "a variable argument list",
+		   atname, argno, pos);
+	  return NULL_TREE;
+	}
+
+      /* Where the expected code is STRING_CST accept any pointer
+	 to a narrow character type, qualified or otherwise.  */
+      bool type_match;
+      if (code == STRING_CST && POINTER_TYPE_P (argtype))
+	{
+	  tree type = TREE_TYPE (argtype);
+	  type = TYPE_MAIN_VARIANT (type);
+	  type_match = (type == char_type_node
+			|| type == signed_char_type_node
+			|| type == unsigned_char_type_node);
+	}
+      else
+	type_match = TREE_CODE (argtype) == code;
+
+      if (!type_match)
+	{
+	  if (argno < 1)
+	    warning (OPT_Wattributes,
+		     "%qE attribute argument value %qE refers to "
+		     "parameter type %qT",
+		     atname, pos, argtype);
+	  else
+	    warning (OPT_Wattributes,
+		   "%qE attribute argument %i value %qE refers to "
+		     "parameter type %qT",
+		     atname, argno, pos, argtype);
+	  return NULL_TREE;
+	}
+    }
+  else if (!(flags & POSARG_ELLIPSIS))
+    {
+      if (argno < 1)
+	warning (OPT_Wattributes,
+		 "%qE attribute argument value %qE refers to "
+		 "a variadic function parameter of unknown type",
+		 atname, pos);
+      else
+	warning (OPT_Wattributes,
+		 "%qE attribute argument %i value %qE refers to "
+		 "a variadic function parameter of unknown type",
+		 atname, argno, pos);
+      return NULL_TREE;
+    }
+
+  return pos;
+}
+
+
 /* Attribute handlers common to C front ends.  */
 
 /* Handle a "packed" attribute; arguments as in
@@ -2398,27 +2581,40 @@ handle_malloc_attribute (tree *node, tree name, tree ARG_UNUSED (args),
    struct attribute_spec.handler.  */
 
 static tree
-handle_alloc_size_attribute (tree *node, tree ARG_UNUSED (name), tree args,
+handle_alloc_size_attribute (tree *node, tree name, tree args,
 			     int ARG_UNUSED (flags), bool *no_add_attrs)
 {
-  unsigned arg_count = type_num_arguments (*node);
-  for (; args; args = TREE_CHAIN (args))
+  tree decl = *node;
+  tree rettype = TREE_TYPE (decl);
+  if (!POINTER_TYPE_P (rettype))
     {
-      tree position = TREE_VALUE (args);
-      if (position && TREE_CODE (position) != IDENTIFIER_NODE
-	  && TREE_CODE (position) != FUNCTION_DECL)
-	position = default_conversion (position);
+      warning (OPT_Wattributes,
+	       "%qE attribute ignored on a function returning %qT",
+	       name, rettype);
+      *no_add_attrs = true;
+      return NULL_TREE;
+    }
 
-      if (!tree_fits_uhwi_p (position)
-	  || !arg_count
-	  || !IN_RANGE (tree_to_uhwi (position), 1, arg_count))
+  for (int i = 1; args; ++i)
+    {
+      tree pos = TREE_VALUE (args);
+      /* NEXT is null when the attribute includes just one argument.
+	 That's used to tell positional_argument to avoid mentioning
+	 the argument number in diagnostics (since there's just one
+	 mentioning it is unnecessary and coule be confusing).  */
+      tree next = TREE_CHAIN (args);
+      if (tree val = positional_argument (decl, name, pos, INTEGER_TYPE,
+					  next || i > 1 ? i : 0))
+	TREE_VALUE (args) = val;
+      else
 	{
-	  warning (OPT_Wattributes,
-	           "alloc_size parameter outside range");
 	  *no_add_attrs = true;
-	  return NULL_TREE;
+	  break;
 	}
+
+      args = next;
     }
+
   return NULL_TREE;
 }
 
@@ -2426,24 +2622,23 @@ handle_alloc_size_attribute (tree *node, tree ARG_UNUSED (name), tree args,
    struct attribute_spec.handler.  */
 
 static tree
-handle_alloc_align_attribute (tree *node, tree, tree args, int,
+handle_alloc_align_attribute (tree *node, tree name, tree args, int,
 			      bool *no_add_attrs)
 {
-  unsigned arg_count = type_num_arguments (*node);
-  tree position = TREE_VALUE (args);
-  if (position && TREE_CODE (position) != IDENTIFIER_NODE
-      && TREE_CODE (position) != FUNCTION_DECL)
-    position = default_conversion (position);
-
-  if (!tree_fits_uhwi_p (position)
-      || !arg_count
-      || !IN_RANGE (tree_to_uhwi (position), 1, arg_count))
+  tree decl = *node;
+  tree rettype = TREE_TYPE (decl);
+  if (!POINTER_TYPE_P (rettype))
     {
       warning (OPT_Wattributes,
-	       "alloc_align parameter outside range");
+	       "%qE attribute ignored on a function returning %qT",
+	       name, rettype);
       *no_add_attrs = true;
       return NULL_TREE;
     }
+
+  if (!positional_argument (*node, name, TREE_VALUE (args), INTEGER_TYPE))
+    *no_add_attrs = true;
+
   return NULL_TREE;
 }
 
@@ -2451,20 +2646,60 @@ handle_alloc_align_attribute (tree *node, tree, tree args, int,
    struct attribute_spec.handler.  */
 
 static tree
-handle_assume_aligned_attribute (tree *, tree, tree args, int,
+handle_assume_aligned_attribute (tree *node, tree name, tree args, int,
 				 bool *no_add_attrs)
 {
+  tree decl = *node;
+  tree rettype = TREE_TYPE (decl);
+  if (TREE_CODE (rettype) != POINTER_TYPE)
+    {
+      warning (OPT_Wattributes,
+	       "%qE attribute ignored on a function returning %qT",
+	       name, rettype);
+      *no_add_attrs = true;
+      return NULL_TREE;
+    }
+
+  /* The alignment specified by the first argument.  */
+  tree align = NULL_TREE;
+
   for (; args; args = TREE_CHAIN (args))
     {
-      tree position = TREE_VALUE (args);
-      if (position && TREE_CODE (position) != IDENTIFIER_NODE
-	  && TREE_CODE (position) != FUNCTION_DECL)
-	position = default_conversion (position);
+      tree val = TREE_VALUE (args);
+      if (val && TREE_CODE (val) != IDENTIFIER_NODE
+	  && TREE_CODE (val) != FUNCTION_DECL)
+	val = default_conversion (val);
 
-      if (TREE_CODE (position) != INTEGER_CST)
+      if (!tree_fits_shwi_p (val))
 	{
 	  warning (OPT_Wattributes,
-		   "assume_aligned parameter not integer constant");
+		   "%qE attribute %E is not an integer constant",
+		   name, val);
+	  *no_add_attrs = true;
+	  return NULL_TREE;
+	}
+
+      if (!align)
+	{
+	  /* Validate and save the alignment.  */
+	  if (!integer_pow2p (val))
+	    {
+	      warning (OPT_Wattributes,
+		       "%qE attribute argument %E is not a power of 2",
+		       name, val);
+	      *no_add_attrs = true;
+	      return NULL_TREE;
+	    }
+
+	  align = val;
+	}
+      else if (tree_int_cst_sgn (val) < 0 || tree_int_cst_le (align, val))
+	{
+	  /* The misalignment specified by the second argument
+	     must be non-negative and less than the alignment.  */
+	  warning (OPT_Wattributes,
+		   "%qE attribute argument %E is not in the range [0, %E)",
+		   name, val, align);
 	  *no_add_attrs = true;
 	  return NULL_TREE;
 	}
@@ -3049,12 +3284,11 @@ handle_vector_size_attribute (tree *node, tree name, tree args,
 /* Handle the "nonnull" attribute.  */
 
 static tree
-handle_nonnull_attribute (tree *node, tree ARG_UNUSED (name),
+handle_nonnull_attribute (tree *node, tree name,
 			  tree args, int ARG_UNUSED (flags),
 			  bool *no_add_attrs)
 {
   tree type = *node;
-  unsigned HOST_WIDE_INT attr_arg_num;
 
   /* If no arguments are specified, all pointer arguments should be
      non-null.  Verify a full prototype is given so that the arguments
@@ -3073,57 +3307,23 @@ handle_nonnull_attribute (tree *node, tree ARG_UNUSED (name),
       return NULL_TREE;
     }
 
-  /* Argument list specified.  Verify that each argument number references
-     a pointer argument.  */
-  for (attr_arg_num = 1; args; attr_arg_num++, args = TREE_CHAIN (args))
+  for (int i = 1; args; ++i)
     {
-      unsigned HOST_WIDE_INT arg_num = 0, ck_num;
-
-      tree arg = TREE_VALUE (args);
-      if (arg && TREE_CODE (arg) != IDENTIFIER_NODE
-	  && TREE_CODE (arg) != FUNCTION_DECL)
-	TREE_VALUE (args) = arg = default_conversion (arg);
-
-      if (!get_nonnull_operand (arg, &arg_num))
+      tree pos = TREE_VALUE (args);
+      /* NEXT is null when the attribute includes just one argument.
+	 That's used to tell positional_argument to avoid mentioning
+	 the argument number in diagnostics (since there's just one
+	 mentioning it is unnecessary and coule be confusing).  */
+      tree next = TREE_CHAIN (args);
+      if (tree val = positional_argument (type, name, pos, POINTER_TYPE,
+					  next || i > 1 ? i : 0))
+	TREE_VALUE (args) = val;
+      else
 	{
-	  error ("nonnull argument has invalid operand number (argument %lu)",
-		 (unsigned long) attr_arg_num);
 	  *no_add_attrs = true;
-	  return NULL_TREE;
-	}
-
-      if (prototype_p (type))
-	{
-	  function_args_iterator iter;
-	  tree argument;
-
-	  function_args_iter_init (&iter, type);
-	  for (ck_num = 1; ; ck_num++, function_args_iter_next (&iter))
-	    {
-	      argument = function_args_iter_cond (&iter);
-	      if (argument == NULL_TREE || ck_num == arg_num)
-		break;
-	    }
-
-	  if (!argument
-	      || TREE_CODE (argument) == VOID_TYPE)
-	    {
-	      error ("nonnull argument with out-of-range operand number "
-		     "(argument %lu, operand %lu)",
-		     (unsigned long) attr_arg_num, (unsigned long) arg_num);
-	      *no_add_attrs = true;
-	      return NULL_TREE;
-	    }
-
-	  if (TREE_CODE (argument) != POINTER_TYPE)
-	    {
-	      error ("nonnull argument references non-pointer operand "
-		     "(argument %lu, operand %lu)",
-		     (unsigned long) attr_arg_num, (unsigned long) arg_num);
-	      *no_add_attrs = true;
-	      return NULL_TREE;
-	    }
+	  break;
 	}
+      args = next;
     }
 
   return NULL_TREE;
diff --git a/gcc/c-family/c-common.c b/gcc/c-family/c-common.c
index f10cf89..453f569 100644
--- a/gcc/c-family/c-common.c
+++ b/gcc/c-family/c-common.c
@@ -5626,7 +5626,8 @@ check_function_arguments (location_t loc, const_tree fndecl, const_tree fntype,
   /* Check for errors in format strings.  */
 
   if (warn_format || warn_suggest_attribute_format)
-    check_function_format (TYPE_ATTRIBUTES (fntype), nargs, argarray, arglocs);
+    check_function_format (fntype, TYPE_ATTRIBUTES (fntype), nargs, argarray,
+			   arglocs);
 
   if (warn_format)
     check_function_sentinel (fntype, nargs, argarray);
diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h
index 641fe57..3a849d3 100644
--- a/gcc/c-family/c-common.h
+++ b/gcc/c-family/c-common.h
@@ -808,7 +808,8 @@ extern void check_function_arguments_recurse (void (*)
 					      unsigned HOST_WIDE_INT);
 extern bool check_builtin_function_arguments (location_t, vec<location_t>,
 					      tree, int, tree *);
-extern void check_function_format (tree, int, tree *, vec<location_t> *);
+extern void check_function_format (const_tree, tree, int, tree *,
+				   vec<location_t> *);
 extern bool attribute_fallthrough_p (tree);
 extern tree handle_format_attribute (tree *, tree, tree, int, bool *);
 extern tree handle_format_arg_attribute (tree *, tree, tree, int, bool *);
@@ -1327,6 +1328,18 @@ extern int tm_attr_to_mask (tree);
 extern tree tm_mask_to_attr (int);
 extern tree find_tm_attribute (tree);
 
+/* A bitmap of flags to positional_argument.  */
+enum posargflags {
+  /* Consider positional attribute argument value zero valid.  */
+  POSARG_ZERO = 1,
+  /* Consider positional attribute argument value valid if it refers
+     to the ellipsis (i.e., beyond the last typed argument).  */
+  POSARG_ELLIPSIS = 2
+};
+
+extern tree positional_argument (const_tree, const_tree, tree, tree_code,
+				 int = 0, int = posargflags ());
+
 extern enum flt_eval_method
 excess_precision_mode_join (enum flt_eval_method, enum flt_eval_method);
 
diff --git a/gcc/c-family/c-format.c b/gcc/c-family/c-format.c
index a1133c7..8d50726 100644
--- a/gcc/c-family/c-format.c
+++ b/gcc/c-family/c-format.c
@@ -62,15 +62,17 @@ static GTY(()) tree local_tree_type_node;
 static GTY(()) tree local_gimple_ptr_node;
 static GTY(()) tree locus;
 
-static bool decode_format_attr (tree, function_format_info *, int);
+static bool decode_format_attr (const_tree, tree, tree, function_format_info *,
+				bool);
 static int decode_format_type (const char *);
 
-static bool check_format_string (tree argument,
+static bool check_format_string (const_tree argument,
 				 unsigned HOST_WIDE_INT format_num,
 				 int flags, bool *no_add_attrs,
 				 int expected_format_type);
-static bool get_constant (tree expr, unsigned HOST_WIDE_INT *value,
-			  int validated_p);
+static tree get_constant (const_tree fntype, const_tree atname, tree expr,
+			  int argno, unsigned HOST_WIDE_INT *value,
+			  int flags, bool validated_p);
 static const char *convert_format_name_to_system_name (const char *attr_name);
 
 static int first_target_format_type;
@@ -132,16 +134,19 @@ valid_stringptr_type_p (tree strref)
 /* Handle a "format_arg" attribute; arguments as in
    struct attribute_spec.handler.  */
 tree
-handle_format_arg_attribute (tree *node, tree ARG_UNUSED (name),
+handle_format_arg_attribute (tree *node, tree atname,
 			     tree args, int flags, bool *no_add_attrs)
 {
   tree type = *node;
-  tree format_num_expr = TREE_VALUE (args);
+  /* Note that TREE_VALUE (args) is changed in place below.  */
+  tree *format_num_expr = &TREE_VALUE (args);
   unsigned HOST_WIDE_INT format_num = 0;
 
-  if (!get_constant (format_num_expr, &format_num, 0))
+  if (tree val = get_constant (type, atname, *format_num_expr, 0, &format_num,
+			       0, false))
+    *format_num_expr = val;
+  else
     {
-      error ("format string has invalid operand number");
       *no_add_attrs = true;
       return NULL_TREE;
     }
@@ -170,7 +175,7 @@ handle_format_arg_attribute (tree *node, tree ARG_UNUSED (name),
    error).  When we know the specific reference type expected, this is also 
    checked.  */
 static bool
-check_format_string (tree fntype, unsigned HOST_WIDE_INT format_num,
+check_format_string (const_tree fntype, unsigned HOST_WIDE_INT format_num,
 		     int flags, bool *no_add_attrs, int expected_format_type)
 {
   unsigned HOST_WIDE_INT i;
@@ -263,19 +268,20 @@ check_format_string (tree fntype, unsigned HOST_WIDE_INT format_num,
 
 /* Verify EXPR is a constant, and store its value.
    If validated_p is true there should be no errors.
-   Returns true on success, false otherwise.  */
-static bool
-get_constant (tree expr, unsigned HOST_WIDE_INT *value, int validated_p)
+   Returns the converted constant value on success, null otherwise.  */
+static tree
+get_constant (const_tree fntype, const_tree atname, tree expr, int argno,
+	      unsigned HOST_WIDE_INT *value, int flags, bool validated_p)
 {
-  if (!tree_fits_uhwi_p (expr))
+  if (tree val = positional_argument (fntype, atname, expr, STRING_CST,
+				      argno, flags))
     {
-      gcc_assert (!validated_p);
-      return false;
+      *value = TREE_INT_CST_LOW (val);
+      return val;
     }
 
-  *value = TREE_INT_CST_LOW (expr);
-
-  return true;
+  gcc_assert (!validated_p);
+  return NULL_TREE;
 }
 
 /* Decode the arguments to a "format" attribute into a
@@ -286,12 +292,14 @@ get_constant (tree expr, unsigned HOST_WIDE_INT *value, int validated_p)
    attributes are successfully decoded, false otherwise.  */
 
 static bool
-decode_format_attr (tree args, function_format_info *info, int validated_p)
+decode_format_attr (const_tree fntype, tree atname, tree args,
+		    function_format_info *info, bool validated_p)
 {
   tree format_type_id = TREE_VALUE (args);
-  tree format_num_expr = TREE_VALUE (TREE_CHAIN (args));
-  tree first_arg_num_expr
-    = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (args)));
+  /* Note that TREE_VALUE (args) is changed in place below.  Ditto
+     for the value of the next element on the list.  */
+  tree *format_num_expr = &TREE_VALUE (TREE_CHAIN (args));
+  tree *first_arg_num_expr = &TREE_VALUE (TREE_CHAIN (TREE_CHAIN (args)));
 
   if (TREE_CODE (format_type_id) != IDENTIFIER_NODE)
     {
@@ -326,17 +334,18 @@ decode_format_attr (tree args, function_format_info *info, int validated_p)
 	}
     }
 
-  if (!get_constant (format_num_expr, &info->format_num, validated_p))
-    {
-      error ("format string has invalid operand number");
-      return false;
-    }
+  if (tree val = get_constant (fntype, atname, *format_num_expr,
+			       2, &info->format_num, 0, validated_p))
+    *format_num_expr = val;
+  else
+    return false;
 
-  if (!get_constant (first_arg_num_expr, &info->first_arg_num, validated_p))
-    {
-      error ("%<...%> has invalid operand number");
-      return false;
-    }
+  if (tree val = get_constant (fntype, atname, *first_arg_num_expr,
+			       3, &info->first_arg_num,
+			       (POSARG_ZERO | POSARG_ELLIPSIS), validated_p))
+    *first_arg_num_expr = val;
+  else
+    return false;
 
   if (info->first_arg_num != 0 && info->first_arg_num <= info->format_num)
     {
@@ -1076,11 +1085,13 @@ decode_format_type (const char *s)
    attribute themselves.  */
 
 void
-check_function_format (tree attrs, int nargs, tree *argarray,
-		       vec<location_t> *arglocs)
+check_function_format (const_tree fntype, tree attrs, int nargs,
+		       tree *argarray, vec<location_t> *arglocs)
 {
   tree a;
 
+  tree atname = get_identifier ("format");
+
   /* See if this function has any format attributes.  */
   for (a = attrs; a; a = TREE_CHAIN (a))
     {
@@ -1088,7 +1099,8 @@ check_function_format (tree attrs, int nargs, tree *argarray,
 	{
 	  /* Yup; check it.  */
 	  function_format_info info;
-	  decode_format_attr (TREE_VALUE (a), &info, /*validated=*/true);
+	  decode_format_attr (fntype, atname, TREE_VALUE (a), &info,
+			      /*validated=*/true);
 	  if (warn_format)
 	    {
 	      /* FIXME: Rewrite all the internal functions in this file
@@ -4100,10 +4112,10 @@ convert_format_name_to_system_name (const char *attr_name)
 /* Handle a "format" attribute; arguments as in
    struct attribute_spec.handler.  */
 tree
-handle_format_attribute (tree *node, tree ARG_UNUSED (name), tree args,
+handle_format_attribute (tree *node, tree atname, tree args,
 			 int flags, bool *no_add_attrs)
 {
-  tree type = *node;
+  const_tree type = *node;
   function_format_info info;
 
 #ifdef TARGET_FORMAT_TYPES
@@ -4129,7 +4141,7 @@ handle_format_attribute (tree *node, tree ARG_UNUSED (name), tree args,
   if (TREE_CODE (TREE_VALUE (args)) == IDENTIFIER_NODE)
     TREE_VALUE (args) = canonicalize_attr_name (TREE_VALUE (args));
 
-  if (!decode_format_attr (args, &info, 0))
+  if (!decode_format_attr (type, atname, args, &info, /* validated_p = */false))
     {
       *no_add_attrs = true;
       return NULL_TREE;
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index 25c821d..56738d2 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -2401,33 +2401,39 @@ further information.
 The @code{aligned} attribute can also be used for variables and fields
 (@pxref{Variable Attributes}.)
 
-@item alloc_align
+@item alloc_align (@var{position})
 @cindex @code{alloc_align} function attribute
-The @code{alloc_align} attribute is used to tell the compiler that the
-function return value points to memory, where the returned pointer minimum
-alignment is given by one of the functions parameters.  GCC uses this
-information to improve pointer alignment analysis.
+The @code{alloc_align} attribute may be applied to a function that
+returns a pointer and takes at least one argument of an integer type.
+It indicates that the returned pointer is aligned on a boundary given
+by the function argument at @var{position}.  Meaningful alignments are
+powers of 2 greater than one.  GCC uses this information to improve
+pointer alignment analysis.
 
 The function parameter denoting the allocated alignment is specified by
-one integer argument, whose number is the argument of the attribute.
+one constant integer argument whose number is the argument of the attribute.
 Argument numbering starts at one.
 
 For instance,
 
 @smallexample
-void* my_memalign(size_t, size_t) __attribute__((alloc_align(1)))
+void* my_memalign (size_t, size_t) __attribute__ ((alloc_align (1)));
 @end smallexample
 
 @noindent
 declares that @code{my_memalign} returns memory with minimum alignment
 given by parameter 1.
 
-@item alloc_size
+@item alloc_size (@var{position})
+@itemx alloc_size (@var{position-1}, @var{position-2})
 @cindex @code{alloc_size} function attribute
-The @code{alloc_size} attribute is used to tell the compiler that the
-function return value points to memory, where the size is given by
-one or two of the functions parameters.  GCC uses this
-information to improve the correctness of @code{__builtin_object_size}.
+The @code{alloc_size} attribute may be applied to a function that
+returns a pointer and takes at least one argument of an integer type.
+It indicates that the returned pointer points to memory whose size is
+given by the function argument at @var{position-1}, or by the product
+of the arguments at @var{position-1} and @var{position-2}.  Meaningful
+sizes are positive values less than @code{PTRDIFF_MAX}.  GCC uses this
+information to improve the results of @code{__builtin_object_size}.
 
 The function parameter(s) denoting the allocated size are specified by
 one or two integer arguments supplied to the attribute.  The allocated size
@@ -2438,8 +2444,8 @@ one.
 For instance,
 
 @smallexample
-void* my_calloc(size_t, size_t) __attribute__((alloc_size(1,2)))
-void* my_realloc(void*, size_t) __attribute__((alloc_size(2)))
+void* my_calloc (size_t, size_t) __attribute__ ((alloc_size (1, 2)));
+void* my_realloc (void*, size_t) __attribute__ ((alloc_size (2)));
 @end smallexample
 
 @noindent
@@ -2465,22 +2471,25 @@ info format it either means marking the function as artificial
 or using the caller location for all instructions within the inlined
 body.
 
-@item assume_aligned
+@item assume_aligned (@var{alignment})
+@itemx assume_aligned (@var{alignment}, @var{offset})
 @cindex @code{assume_aligned} function attribute
-The @code{assume_aligned} attribute is used to tell the compiler that the
-function return value points to memory, where the returned pointer minimum
-alignment is given by the first argument.
-If the attribute has two arguments, the second argument is misalignment offset.
+The @code{assume_aligned} attribute may be applied to a function that
+returns a pointer.  It indicates that the returned pointer is aligned
+on a boundary given by @var{alignment}.  If the attribute has two
+arguments, the second argument is misalignment @var{offset}.  Meaningful
+values of @var{alignment} are powers of 2 greater than one.  Meaningful
+values of @var{offset} are greater than zero and less than @var{alignment}.
 
 For instance
 
 @smallexample
-void* my_alloc1(size_t) __attribute__((assume_aligned(16)))
-void* my_alloc2(size_t) __attribute__((assume_aligned(32, 8)))
+void* my_alloc1 (size_t) __attribute__((assume_aligned (16)));
+void* my_alloc2 (size_t) __attribute__((assume_aligned (32, 8)));
 @end smallexample
 
 @noindent
-declares that @code{my_alloc1} returns 16-byte aligned pointer and
+declares that @code{my_alloc1} returns 16-byte aligned pointers and
 that @code{my_alloc2} returns a pointer whose value modulo 32 is equal
 to 8.
 
@@ -3079,8 +3088,9 @@ of testing the compiler.
 @itemx nonnull (@var{arg-index}, @dots{})
 @cindex @code{nonnull} function attribute
 @cindex functions with non-null pointer arguments
-The @code{nonnull} attribute specifies that some function parameters should
-be non-null pointers.  For instance, the declaration:
+The @code{nonnull} attribute may be applied to a function that takes at
+least one argument of a pointer type.  It indicates that the referenced
+arguments must be non-null pointers.  For instance, the declaration:
 
 @smallexample
 extern void *
@@ -3304,13 +3314,14 @@ If you need to map the entire contents of a module to a particular
 section, consider using the facilities of the linker instead.
 
 @item sentinel
+@itemx sentinel (@var{position})
 @cindex @code{sentinel} function attribute
-This function attribute ensures that a parameter in a function call is
-an explicit @code{NULL}.  The attribute is only valid on variadic
-functions.  By default, the sentinel is located at position zero, the
-last parameter of the function call.  If an optional integer position
-argument P is supplied to the attribute, the sentinel must be located at
-position P counting backwards from the end of the argument list.
+This function attribute indicates that an argument in a call to the function
+is expected to be an explicit @code{NULL}.  The attribute is only valid on
+variadic functions.  By default, the sentinel is expected to be the last
+argument of the function call.  If the optional @var{position} argument
+is specified to the attribute, the sentinel must be located at
+@var{position} counting backwards from the end of the argument list.
 
 @smallexample
 __attribute__ ((sentinel))
@@ -3322,10 +3333,11 @@ The attribute is automatically set with a position of 0 for the built-in
 functions @code{execl} and @code{execlp}.  The built-in function
 @code{execle} has the attribute set with a position of 1.
 
-A valid @code{NULL} in this context is defined as zero with any pointer
-type.  If your system defines the @code{NULL} macro with an integer type
-then you need to add an explicit cast.  GCC replaces @code{stddef.h}
-with a copy that redefines NULL appropriately.
+A valid @code{NULL} in this context is defined as zero with any object
+pointer type.  If your system defines the @code{NULL} macro with
+an integer type then you need to add an explicit cast.  During
+installation GCC replaces the system @code{<stddef.h>} header with
+a copy that redefines NULL appropriately.
 
 The warnings for missing or incorrect sentinels are enabled with
 @option{-Wformat}.
diff --git a/gcc/testsuite/c-c++-common/attributes-1.c b/gcc/testsuite/c-c++-common/attributes-1.c
index 1657da1..c4b232d 100644
--- a/gcc/testsuite/c-c++-common/attributes-1.c
+++ b/gcc/testsuite/c-c++-common/attributes-1.c
@@ -1,21 +1,22 @@
 /* { dg-do compile } */
 /* { dg-prune-output "undeclared here \\(not in a function\\)|\[^\n\r\]* was not declared in this scope" } */
 
-void* my_calloc(unsigned, unsigned) __attribute__((alloc_size(1,bar))); /* { dg-warning "outside range" } */
-void* my_realloc(void*, unsigned) __attribute__((alloc_size(bar))); /* { dg-warning "outside range" } */
+void* my_calloc(unsigned, unsigned) __attribute__((alloc_size(1,bar))); /* { dg-warning ".alloc_size. attribute argument 2 is invalid" } */
+void* my_realloc(void*, unsigned) __attribute__((alloc_size(bar))); /* { dg-warning ".alloc_size. attribute argument is invalid" } */
 
 typedef char vec __attribute__((vector_size(bar))); /* { dg-warning "ignored" } */
 
-void f1(char*) __attribute__((nonnull(bar))); /* { dg-error "invalid operand" } */
-void f2(char*) __attribute__((nonnull(1,bar))); /* { dg-error "invalid operand" } */
+void f1(char*) __attribute__((nonnull(bar))); /* { dg-warning ".nonnull. attribute argument is invalid" } */
 
-void foo(void);
-void* my_calloc(unsigned, unsigned) __attribute__((alloc_size(1,foo))); /* { dg-warning "outside range" } */
-void* my_realloc(void*, unsigned) __attribute__((alloc_size(foo))); /* { dg-warning "outside range" } */
+void f2(char*) __attribute__((nonnull(1,bar))); /* { dg-warning ".nonnull. attribute argument 2 is invalid" } */
+
+void foo(int);
+void* my_calloc(unsigned, unsigned) __attribute__((alloc_size(1,foo))); /* { dg-warning ".alloc_size. attribute argument 2 has type .void\\\(int\\\)." } */
+void* my_realloc(void*, unsigned) __attribute__((alloc_size(foo))); /* { dg-warning ".alloc_size. attribute argument has type .void ?\\\(int\\\)" } */
 
 typedef char vec __attribute__((vector_size(foo))); /* { dg-warning "ignored" } */
 
-void f1(char*) __attribute__((nonnull(foo))); /* { dg-error "invalid operand" } */
-void f2(char*) __attribute__((nonnull(1,foo))); /* { dg-error "invalid operand" } */
+void f1(char*) __attribute__((nonnull(foo))); /* { dg-warning ".nonnull. attribute argument has type .void ?\\\(int\\\)." } */
+void f2(char*) __attribute__((nonnull(1,foo))); /* { dg-warning ".nonnull. attribute argument 2 has type .void ?\\\(int\\\)." } */
 
 void g() __attribute__((aligned(foo))); /* { dg-error "invalid value|not an integer" } */
diff --git a/gcc/testsuite/c-c++-common/pr71574.c b/gcc/testsuite/c-c++-common/pr71574.c
index 320ae38..f06624c 100644
--- a/gcc/testsuite/c-c++-common/pr71574.c
+++ b/gcc/testsuite/c-c++-common/pr71574.c
@@ -1,12 +1,15 @@
-/* PR c/71574 */
+/* PR c/71574 - ICE on code with alloc_align attribute */
 /* { dg-do compile } */
 
-int fn1 (void);
-int fn2 (void) __attribute__ ((alloc_align (fn1))); /* { dg-warning "parameter outside range" } */
-int fn3 (void) __attribute__ ((alloc_size (fn1))); /* { dg-warning "parameter outside range" } */
-int fn4 (void) __attribute__ ((assume_aligned (fn1))); /* { dg-warning "not integer constant" } */
-int fn5 (char *, char *) __attribute__((nonnull (fn1))); /* { dg-error "nonnull argument has invalid operand" } */
+int fn1 (int);
+int fn2 (void) __attribute__ ((alloc_align (fn1))); /* { dg-warning ".alloc_align. attribute ignored on a function returning .int." } */
+int fn3 (void) __attribute__ ((alloc_size (fn1))); /* { dg-warning ".alloc_size. attribute ignored on a function returning .int." } */
+int fn4 (void) __attribute__ ((assume_aligned (fn1))); /* { dg-warning ".assume_aligned. attribute ignored on a function returning .int." } */
+int fn5 (char *, char *) __attribute__((nonnull (fn1))); /* { dg-warning ".nonnull. attribute argument has type .int\\\(int\\\)." } */
 int fn6 (const char *, ...) __attribute__ ((sentinel (fn1))); /* { dg-warning "not an integer constant" } */
 
+void* fn7 (void) __attribute__ ((alloc_align (fn1))); /* { dg-warning ".alloc_align. attribute argument has type .int\\\(int\\\)." } */
+void* fn8 (void) __attribute__ ((assume_aligned (fn1))); /* { dg-warning "not an integer constant" } */
+
 typedef int __attribute__((vector_size (fn1))) v4si; /* { dg-warning "attribute ignored" } */
 typedef int T __attribute__((aligned (fn1))); /* { dg-error "requested alignment is not" } */
diff --git a/gcc/testsuite/g++.dg/ext/attr-alloc_size.C b/gcc/testsuite/g++.dg/ext/attr-alloc_size.C
new file mode 100644
index 0000000..9a42110
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/attr-alloc_size.C
@@ -0,0 +1,53 @@
+/* PR c++/87541 - ICE using a constant decl as an attribute alloc_size argument
+   { dg-do compile }
+   { dg-options "-O2 -Wall" } */
+
+#define ALLOC_SIZE(N)   __attribute__ ((alloc_size (N)))
+
+const int i1 = 1;
+ALLOC_SIZE (i1) void* fcst (int);
+
+void* call_fcst (void)
+{
+  void *p = fcst (1);
+  __builtin___memset_chk (p, 0, 2, __builtin_object_size (p, 1));   // { dg-warning "\\\[-Wstringop-overflow=" }
+  return p;
+}
+
+
+enum { e1 = 1 };
+ALLOC_SIZE (e1) void* fenum (int);
+
+void* call_fenum (void)
+{
+  void *p = fenum (1);
+  __builtin___memset_chk (p, 0, 2, __builtin_object_size (p, 1));   // { dg-warning "\\\[-Wstringop-overflow=" }
+  return p;
+}
+
+
+template <class T>
+struct A
+{
+  ALLOC_SIZE (T::N1) static void* ftemplarg_1 (int);
+  ALLOC_SIZE (T::N2) static void*
+  ftemplarg_2 (int); // { dg-warning "attribute argument value .2. exceeds the number of function parameters 1" }
+};
+
+struct B { static const int N1 = 1; static const int N2 = 1; };
+
+void* call_ftemplarg_1 (A<B> *pa)
+{
+  void *p = pa->ftemplarg_1 (1);
+  __builtin___memset_chk (p, 0, 2, __builtin_object_size (p, 1));   // { dg-warning "\\\[-Wstringop-overflow=" }
+  return p;
+}
+
+struct C { static const int N1 = 1; static const int N2 = 2; };
+
+void* call_ftemplarg_2 (A<C> *pa)
+{
+  void *p = pa->ftemplarg_2 (1);
+  __builtin___memset_chk (p, 0, 2, __builtin_object_size (p, 1));
+  return p;
+}
diff --git a/gcc/testsuite/gcc.dg/attr-alloc_align-2.c b/gcc/testsuite/gcc.dg/attr-alloc_align-2.c
index 3dc7a21..01321e7 100644
--- a/gcc/testsuite/gcc.dg/attr-alloc_align-2.c
+++ b/gcc/testsuite/gcc.dg/attr-alloc_align-2.c
@@ -5,6 +5,6 @@ void *f1 (int) __attribute__((alloc_align (1)));
 void *f2 (int, int, int) __attribute__((alloc_align (3)));
 void *f3 (void) __attribute__((alloc_align)); /* { dg-error "wrong number of arguments specified" } */
 void *f4 (int, int) __attribute__((alloc_align (1, 2))); /* { dg-error "wrong number of arguments specified" } */
-void *f5 (void) __attribute__((alloc_align (i))); /* { dg-warning "outside range" } */
-void *f6 (int) __attribute__((alloc_align (0))); /* { dg-warning "outside range" } */
-void *f7 (int) __attribute__((alloc_align (2))); /* { dg-warning "outside range" } */
+void *f5 (void) __attribute__((alloc_align (i))); /* { dg-warning ".alloc_align. attribute argument value .i. is not an integer constant" } */
+void *f6 (int) __attribute__((alloc_align (0))); /* { dg-warning ".alloc_align. attribute argument value .0. does not refer to a function parameter" } */
+void *f7 (int) __attribute__((alloc_align (2))); /* { dg-warning ".alloc_align. attribute argument value .2. exceeds the number of function parameters 1" } */
diff --git a/gcc/testsuite/gcc.dg/attr-alloc_align-4.c b/gcc/testsuite/gcc.dg/attr-alloc_align-4.c
new file mode 100644
index 0000000..7cdfc7d
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/attr-alloc_align-4.c
@@ -0,0 +1,43 @@
+/* PR middle-end/81871 - bogus attribute alloc_align accepted
+   { dg-do compile }
+   { dg-options "-Wall" } */
+
+#define ALIGN(N)   __attribute__ ((alloc_align (N)))
+#define SIZE_MAX  __SIZE_MAX__
+
+ALIGN (1) void fvv_m1 (void);     /* { dg-warning ".alloc_align. attribute ignored on a function returning .void." } */
+
+ALIGN (1) int fiv_1 (void);       /* { dg-warning ".alloc_align. attribute ignored on a function returning .int." } */
+
+ALIGN (0) void* fpvv_0 (void);    /* { dg-warning ".alloc_align. attribute argument value .0. does not refer to a function parameter" } */
+
+ALIGN (1) void* fpvv_1 (void);    /* { dg-warning ".alloc_align. attribute argument value .1. exceeds the number of function parameters 0" } */
+
+ALIGN (2) void* fii_2 (int);      /* { dg-warning ".alloc_align. attribute argument value .2. exceeds the number of function parameters 1" } */
+
+ALIGN (1) void fvi_1 (int);       /* { dg-warning ".alloc_align. attribute ignored on a function returning .void." } */
+
+/* Using alloc_align with a function returning a pointer to a function
+   should perhaps trigger a warning.  */
+typedef void (F)(void);
+ALIGN (1) F* fpF_i_1 (int);
+
+ALIGN (SIZE_MAX) void*
+fpvi_szmax (int);                 /* { dg-warning ".alloc_align. attribute argument value .\[0-9\]+. exceeds the number of function parameters 1" } */
+
+ALIGN ("1") void*
+fpvi_str_1 (int);                 /* { dg-warning ".alloc_align. attribute argument has type .char\\\[2]" } */
+
+ALIGN ((void*)0) void*
+fpvi_pv0 (int);                   /* { dg-warning ".alloc_align. attribute argument has type .void \\\*." } */
+
+ALIGN ((double*)1) void*
+fpvi_pd1 (int);                   /* { dg-warning ".alloc_align. attribute argument has type .double \\\*." } */
+
+ALIGN (1) void*
+fpvi_pv_1 (void*);                /* { dg-warning ".alloc_align. attribute argument value .1. refers to parameter type .void \\\*." } */
+
+struct S { int i; };
+ALIGN (2) void*
+fpvi_S_2 (int, struct S);         /* { dg-warning ".alloc_align. attribute argument value .2. refers to parameter type .struct S." } */
+
diff --git a/gcc/testsuite/gcc.dg/attr-alloc_size-12.c b/gcc/testsuite/gcc.dg/attr-alloc_size-12.c
new file mode 100644
index 0000000..2e9be1e
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/attr-alloc_size-12.c
@@ -0,0 +1,60 @@
+/* PR middle-end/81871 - bogus attribute alloc_align accepted
+   Test exercising the problem with attribute alloc_size.
+   { dg-do compile }
+   { dg-options "-Wall" } */
+
+#define ASIZE(...)   __attribute__ ((alloc_size (__VA_ARGS__)))
+#define SIZE_MAX  __SIZE_MAX__
+
+ASIZE (-1) void fvv_m1 (void);    /* { dg-warning ".alloc_size. attribute ignored on a function returning .void." } */
+
+ASIZE (1) int fiv_1 (void);       /* { dg-warning ".alloc_size. attribute ignored on a function returning .int." } */
+
+ASIZE (1, 2) int fiv_1_2 (void);  /* { dg-warning ".alloc_size. attribute ignored on a function returning .int." } */
+
+ASIZE (0) void* fpvv_0 (void);    /* { dg-warning ".alloc_size. attribute argument value .0. does not refer to a function parameter" } */
+
+ASIZE (1, 0) void*
+fpvv_1_0 (int);                   /* { dg-warning ".alloc_size. attribute argument 2 value .0. does not refer to a function parameter" } */
+
+ASIZE (1) void* fpvv_1 (void);    /* { dg-warning ".alloc_size. attribute argument value .1. exceeds the number of function parameters 0" } */
+
+ASIZE (1, 9) void*
+fpvv_1_9 (int);                   /* { dg-warning ".alloc_size. attribute argument 2 value .9. exceeds the number of function parameters 1" } */
+
+ASIZE (2) void* fii_2 (int);      /* { dg-warning ".alloc_size. attribute argument value .2. exceeds the number of function parameters 1" } */
+
+ASIZE (1) void fvi_1 (int);       /* { dg-warning ".alloc_size. attribute ignored on a function returning .void." } */
+
+/* Using alloc_size with a function returning a pointer to a function
+   should perhaps trigger a warning.  */
+typedef void (F)(void);
+ASIZE (1) F* fpF_i_1 (int);
+
+ASIZE (SIZE_MAX) void*
+fpvi_szmax (int);                 /* { dg-warning ".alloc_size. attribute argument value .\[0-9\]+. exceeds the number of function parameters 1" } */
+
+ASIZE ("12") void*
+fpvi_str_1 (int);                 /* { dg-warning ".alloc_size. attribute argument has type .char\\\[3]." } */
+
+ASIZE (1, "123") void*
+fpvi_str_2 (int, int);            /* { dg-warning ".alloc_size. attribute argument 2 has type .char\\\[4]." } */
+
+ASIZE ((void*)0) void*
+fpvi_pv0 (int);                   /* { dg-warning ".alloc_size. attribute argument has type .void \\\*." } */
+
+ASIZE ((double*)sizeof (double)) void*
+fpvi_pd1 (int);                   /* { dg-warning ".alloc_size. attribute argument has type .double \\\*." } */
+
+ASIZE (1) void*
+fpvi_pv_1 (void*);                /* { dg-warning ".alloc_size. attribute argument value .1. refers to parameter type .void \\\*." } */
+
+struct S { int i; };
+ASIZE (2) void*
+fpvi_S_2 (int, struct S);         /* { dg-warning ".alloc_size. attribute argument value .2. refers to parameter type .struct S." } */
+
+ASIZE ((struct S){ 1 }) void*
+fpvi_S (int);                     /* { dg-warning ".alloc_size. attribute argument has type .struct S." } */
+
+ASIZE (1, (struct S){ 1 }) void*
+fpvi_1_S (int);                   /* { dg-warning ".alloc_size. attribute argument 2 has type .struct S." } */
diff --git a/gcc/testsuite/gcc.dg/attr-alloc_size-2.c b/gcc/testsuite/gcc.dg/attr-alloc_size-2.c
index 3cac807..3bbd3e2 100644
--- a/gcc/testsuite/gcc.dg/attr-alloc_size-2.c
+++ b/gcc/testsuite/gcc.dg/attr-alloc_size-2.c
@@ -1,4 +1,5 @@
-/* { dg-do compile } */
-
-char *foo() __attribute__((alloc_size(1))); /* { dg-warning "outside range" } */
+/* PR c/36021 - __attribute__((alloc_size(n))) with function of no
+   arguments causes gcc to segfault
+   { dg-do compile } */
 
+char *foo() __attribute__((alloc_size(1)));
diff --git a/gcc/testsuite/gcc.dg/attr-alloc_size.c b/gcc/testsuite/gcc.dg/attr-alloc_size.c
index f50ba7c..88a7771 100644
--- a/gcc/testsuite/gcc.dg/attr-alloc_size.c
+++ b/gcc/testsuite/gcc.dg/attr-alloc_size.c
@@ -5,13 +5,13 @@ extern void abort (void);
 
 #include "../gcc.c-torture/execute/builtins/chk.h"
 
-extern char *mallocminus1(int size) __attribute__((alloc_size(-1))); /* { dg-warning "parameter outside range" } */
-extern char *malloc0(int size) __attribute__((alloc_size(0))); /* { dg-warning "parameter outside range" } */
+extern char *mallocminus1(int size) __attribute__((alloc_size(-1))); /* { dg-warning ".alloc_size. attribute argument value .-1. exceeds the number of function parameters 1" } */
+extern char *malloc0(int size) __attribute__((alloc_size(0))); /* { dg-warning ".alloc_size. attribute argument value .0. does not refer to a function parameter" } */
 extern char *malloc1(int size) __attribute__((alloc_size(1)));
 extern char *malloc2(int empty, int size) __attribute__((alloc_size(2)));
 extern char *calloc1(int size, int elements) __attribute__((alloc_size(1,2)));
 extern char *calloc2(int size, int empty, int elements) __attribute__((alloc_size(1,3)));
-extern char *balloc1(void *size) __attribute__((alloc_size(1)));
+extern char *balloc1(void *size) __attribute__((alloc_size(1)));   /* { dg-warning ".alloc_size. attribute argument value .1. refers to parameter type .void *." } */
 
 void
 test (void)
diff --git a/gcc/testsuite/gcc.dg/attr-assume_aligned-4.c b/gcc/testsuite/gcc.dg/attr-assume_aligned-4.c
new file mode 100644
index 0000000..84f4523
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/attr-assume_aligned-4.c
@@ -0,0 +1,36 @@
+/* PR middle-end/87533 - bogus assume_aligned attribute silently accepted
+   { dg-do compile }
+   { dg-options "-Wall" } */
+
+#define A(...)  __attribute__ ((assume_aligned (__VA_ARGS__)))
+
+A (1) void fv_1 (void);       /* { dg-warning ".assume_aligned. attribute ignored on a function returning .void." } */
+
+A (1) int fi_1 (void);        /* { dg-warning ".assume_aligned. attribute ignored on a function returning .int." } */
+
+A (-1) void* fpv_m1 (void);   /* { dg-warning ".assume_aligned. attribute argument -1 is not a power of 2" } */
+
+A (0) void* fpv_0 (void);     /* { dg-warning ".assume_aligned. attribute argument 0 is not a power of 2" } */
+
+/* Alignment of 1 is fine, it just doesn't offer any benefits.  */
+A (1) void* fpv_1 (void);
+
+A (3) void* fpv_3 (void);     /* { dg-warning ".assume_aligned. attribute argument 3 is not a power of 2" } */
+
+A (16383) void* fpv_16km1 (void);     /* { dg-warning ".assume_aligned. attribute argument 16383 is not a power of 2" } */
+A (16384) void* fpv_16k (void);
+A (16385) void* fpv_16kp1 (void);    /* { dg-warning ".assume_aligned. attribute argument 16385 is not a power of 2" } */
+
+A (32767) void* fpv_32km1 (void);     /* { dg-warning ".assume_aligned. attribute argument 32767 is not a power of 2" } */
+
+A (4, -1) void* fpv_4_m1 (void);      /* { dg-warning ".assume_aligned. attribute argument -1 is not in the range \\\[0, 4\\\)" } */
+
+A (4, 0) void* fpv_4_0 (void);
+A (4, 1) void* fpv_4_1 (void);
+A (4, 2) void* fpv_4_2 (void);
+A (4, 3) void* fpv_4_3 (void);
+
+A (4, 4) void* fpv_4_3 (void);        /* { dg-warning ".assume_aligned. attribute argument 4 is not in the range \\\[0, 4\\\)" } */
+
+A (4) void* gpv_4_3 (void);
+A (2) void* gpv_4_3 (void);
diff --git a/gcc/testsuite/gcc.dg/format/attr-3.c b/gcc/testsuite/gcc.dg/format/attr-3.c
index bee5ff4..31cc05ec 100644
--- a/gcc/testsuite/gcc.dg/format/attr-3.c
+++ b/gcc/testsuite/gcc.dg/format/attr-3.c
@@ -41,10 +41,10 @@ extern void fe1 (const char *, ...) __attribute__((format(nosuch, 1, 2))); /* {
 /* Both the numbers must be integer constant expressions.  */
 extern void ff0 (const char *, ...) __attribute__((format(gnu_attr_printf, 3-2, (long long)(10/5))));
 int foo;
-extern void ff1 (const char *, ...) __attribute__((format(gnu_attr_printf, foo, 10/5))); /* { dg-error "invalid operand" "bad number" } */
-extern void ff2 (const char *, ...) __attribute__((format(gnu_attr_printf, 3-2, foo))); /* { dg-error "invalid operand" "bad number" } */
+extern void ff1 (const char *, ...) __attribute__((format(gnu_attr_printf, foo, 10/5))); /* { dg-warning ".format. attribute argument 2 value .foo. is not an integer constant" "bad number" } */
+extern void ff2 (const char *, ...) __attribute__((format(gnu_attr_printf, 3-2, foo))); /* { dg-warning ".format. attribute argument 3 value .foo. is not an integer constant" "bad number" } */
 extern char *ff3 (const char *) __attribute__((format_arg(3-2)));
-extern char *ff4 (const char *) __attribute__((format_arg(foo))); /* { dg-error "invalid operand" "bad format_arg number" } */
+extern char *ff4 (const char *) __attribute__((format_arg(foo))); /* { dg-warning ".format_arg. attribute argument value .foo. is not an integer constant" "bad format_arg number" } */
 
 /* The format string argument must precede the arguments to be formatted.
    This includes if no parameter types are specified (which is not valid ISO
@@ -56,14 +56,14 @@ extern void fg3 () __attribute__((format(gnu_attr_printf, 2, 1))); /* { dg-error
 
 /* The format string argument must be a string type, and the arguments to
    be formatted must be the "...".  */
-extern void fh0 (int, ...) __attribute__((format(gnu_attr_printf, 1, 2))); /* { dg-error "not a string" "format int string" } */
+extern void fh0 (int, ...) __attribute__((format(gnu_attr_printf, 1, 2))); /* { dg-warning ".format. attribute argument 2 value .1. refers to parameter type .int." "format int string" } */
 extern void fh1 (signed char *, ...) __attribute__((format(gnu_attr_printf, 1, 2))); /* { dg-error "not a string" "signed char string" } */
 extern void fh2 (unsigned char *, ...) __attribute__((format(gnu_attr_printf, 1, 2))); /* { dg-error "not a string" "unsigned char string" } */
 extern void fh3 (const char *, ...) __attribute__((format(gnu_attr_printf, 1, 3))); /* { dg-error "is not" "not ..." } */
-extern void fh4 (const char *, int, ...) __attribute__((format(gnu_attr_printf, 1, 2))); /* { dg-error "is not" "not ..." } */
+extern void fh4 (const char *, int, ...) __attribute__((format(gnu_attr_printf, 1, 2))); /* { dg-error ".format. attribute argument 3 value .2. does not refer to a variable argument list" "not ..." } */
 
 /* format_arg formats must take and return a string.  */
-extern char *fi0 (int) __attribute__((format_arg(1))); /* { dg-error "not a string" "format_arg int string" } */
+extern char *fi0 (int) __attribute__((format_arg(1))); /* { dg-warning ".format_arg. attribute argument value .1. refers to parameter type .int." } */
 extern char *fi1 (signed char *) __attribute__((format_arg(1))); /* { dg-error "not a string" "format_arg signed char string" } */
 extern char *fi2 (unsigned char *) __attribute__((format_arg(1))); /* { dg-error "not a string" "format_arg unsigned char string" } */
 extern int fi3 (const char *) __attribute__((format_arg(1))); /* { dg-error "not return string" "format_arg ret int string" } */
diff --git a/gcc/testsuite/gcc.dg/nonnull-2.c b/gcc/testsuite/gcc.dg/nonnull-2.c
index d570a46..4e3e48d 100644
--- a/gcc/testsuite/gcc.dg/nonnull-2.c
+++ b/gcc/testsuite/gcc.dg/nonnull-2.c
@@ -4,11 +4,12 @@
 
 extern void func1 () __attribute__((nonnull)); /* { dg-error "without arguments" } */
 
-extern void func2 (char *) __attribute__((nonnull(2))); /* { dg-error "out-of-range operand" } */
+extern void func2 (char *) __attribute__((nonnull(2))); /* { dg-warning ".nonnull. attribute argument value .2. exceeds the number of function parameters 1" } */
 
-extern void func3 (char *) __attribute__((nonnull(foo))); /* { dg-error "invalid operand number|undeclared" } */
+extern void func3 (char *) __attribute__((nonnull (foo))); /* { dg-warning ".nonnull. attribute argument is invalid" } */
+/* { dg-error ".foo. undeclared" "undeclared argument" { target *-*-* } .-1 } */
 
-extern void func4 (int) __attribute__((nonnull(1))); /* { dg-error "references non-pointer" } */
+extern void func4 (int) __attribute__((nonnull(1))); /* { dg-warning ".nonnull. attribute argument value .1. refers to parameter type .int." } */
 
 void
 foo (void)
diff --git a/gcc/testsuite/gcc.dg/torture/pr80612.c b/gcc/testsuite/gcc.dg/torture/pr80612.c
index 225b811..e648e82 100644
--- a/gcc/testsuite/gcc.dg/torture/pr80612.c
+++ b/gcc/testsuite/gcc.dg/torture/pr80612.c
@@ -13,3 +13,5 @@ struct obstack {
 }
 void fn2(int) __attribute__((__alloc_size__(1)));
 void fn3() { fn1(fn2); }
+
+/* { dg-prune-output "attribute ignored" } */
diff --git a/gcc/testsuite/obj-c++.dg/attributes/method-format-1.mm b/gcc/testsuite/obj-c++.dg/attributes/method-format-1.mm
index 11ce8ee..9ff34f9 100644
--- a/gcc/testsuite/obj-c++.dg/attributes/method-format-1.mm
+++ b/gcc/testsuite/obj-c++.dg/attributes/method-format-1.mm
@@ -19,8 +19,8 @@
 - (void) log2: (int)level  message: (const char *) my_format, ...  __attribute__ ((format (printf, 2)));    /* { dg-error "wrong" } */
 + (void) debug2: (const char *) my_format, ...  __attribute__ ((format (printf))); /* { dg-error "wrong" } */
 - (void) debug2: (const char *) my_format, ...  __attribute__ ((format (printf))); /* { dg-error "wrong" } */
-+ (void) alert: (const char *) my_format __attribute__ ((format (printf, 1, 2))); /* { dg-error "args to be formatted is not ..." } */
-- (void) alert: (const char *) my_format __attribute__ ((format (printf, 1, 2))); /* { dg-error "args to be formatted is not ..." } */
++ (void) alert: (const char *) my_format __attribute__ ((format (printf, 1, 2))); /* { dg-error "does not refer to a variable argument list" } */
+- (void) alert: (const char *) my_format __attribute__ ((format (printf, 1, 2))); /* { dg-error "does not refer to a variable argument list" } */
 @end
 
 void test (LogObject *object)
diff --git a/gcc/testsuite/obj-c++.dg/attributes/method-nonnull-1.mm b/gcc/testsuite/obj-c++.dg/attributes/method-nonnull-1.mm
index 58b8f36..f83c853 100644
--- a/gcc/testsuite/obj-c++.dg/attributes/method-nonnull-1.mm
+++ b/gcc/testsuite/obj-c++.dg/attributes/method-nonnull-1.mm
@@ -19,17 +19,19 @@
 - (void) insertObject: (id)object  atIndex: (size_t)index  andObject: (id)anotherObject  atIndex: (size_t)anotherIndex __attribute__ ((nonnull (1, 3)));
 
 /* Test the behavior with invalid code.  */
-+ (void) removeObject: (id)object __attribute__ ((nonnull (0))); /* { dg-error "out-of-range" } */
-- (void) removeObject: (id)object __attribute__ ((nonnull (0))); /* { dg-error "out-of-range" } */
++ (void) removeObject: (id)object __attribute__ ((nonnull (0))); /* { dg-warning "does not refer to a function parameter" } */
+- (void) removeObject: (id)object __attribute__ ((nonnull (0))); /* { dg-warning "does not refer to a function parameter" } */
 
-+ (void) removeObject: (id)object __attribute__ ((nonnull (2))); /* { dg-error "out-of-range" } */
-- (void) removeObject: (id)object __attribute__ ((nonnull (2))); /* { dg-error "out-of-range" } */
++ (void) removeObject: (id)object __attribute__ ((nonnull (2))); /* { dg-warning "exceeds the number of function parameters 3" } */
+- (void) removeObject: (id)object __attribute__ ((nonnull (2))); /* { dg-warning "exceeds the number of function parameters 3" } */
 
-+ (void) removeObjectAtIndex: (size_t)object __attribute__ ((nonnull (1))); /* { dg-error "non-pointer operand" } */
-- (void) removeObjectAtIndex: (size_t)object __attribute__ ((nonnull (1))); /* { dg-error "non-pointer operand" } */
++ (void) removeObjectAtIndex: (size_t)object __attribute__ ((nonnull (1))); /* { dg-warning "refers to parameter type .size_t." } */
+- (void) removeObjectAtIndex: (size_t)object __attribute__ ((nonnull (1))); /* { dg-warning "refers to parameter type .size_t." } */
 
 + (void) removeObject: (id)object __attribute__ ((nonnull (MyArray))); /* { dg-error "" } */
+  /* { dg-warning "attribute argument is invalid" "" { target *-*-* } .-1 } */
 - (void) removeObject: (id)object __attribute__ ((nonnull (MyArray))); /* { dg-error "" } */
+  /* { dg-warning "attribute argument is invalid" "" { target *-*-* } .-1 } */
 @end
 
 void test (MyArray *object)
diff --git a/gcc/testsuite/objc.dg/attributes/method-format-1.m b/gcc/testsuite/objc.dg/attributes/method-format-1.m
index 11ce8ee..9ff34f9 100644
--- a/gcc/testsuite/objc.dg/attributes/method-format-1.m
+++ b/gcc/testsuite/objc.dg/attributes/method-format-1.m
@@ -19,8 +19,8 @@
 - (void) log2: (int)level  message: (const char *) my_format, ...  __attribute__ ((format (printf, 2)));    /* { dg-error "wrong" } */
 + (void) debug2: (const char *) my_format, ...  __attribute__ ((format (printf))); /* { dg-error "wrong" } */
 - (void) debug2: (const char *) my_format, ...  __attribute__ ((format (printf))); /* { dg-error "wrong" } */
-+ (void) alert: (const char *) my_format __attribute__ ((format (printf, 1, 2))); /* { dg-error "args to be formatted is not ..." } */
-- (void) alert: (const char *) my_format __attribute__ ((format (printf, 1, 2))); /* { dg-error "args to be formatted is not ..." } */
++ (void) alert: (const char *) my_format __attribute__ ((format (printf, 1, 2))); /* { dg-error "does not refer to a variable argument list" } */
+- (void) alert: (const char *) my_format __attribute__ ((format (printf, 1, 2))); /* { dg-error "does not refer to a variable argument list" } */
 @end
 
 void test (LogObject *object)
diff --git a/gcc/testsuite/objc.dg/attributes/method-nonnull-1.m b/gcc/testsuite/objc.dg/attributes/method-nonnull-1.m
index b60d5a6..e1974aa 100644
--- a/gcc/testsuite/objc.dg/attributes/method-nonnull-1.m
+++ b/gcc/testsuite/objc.dg/attributes/method-nonnull-1.m
@@ -19,17 +19,17 @@
 - (void) insertObject: (id)object  atIndex: (size_t)index  andObject: (id)anotherObject  atIndex: (size_t)anotherIndex __attribute__ ((nonnull (1, 3)));
 
 /* Test the behavior with invalid code.  */
-+ (void) removeObject: (id)object __attribute__ ((nonnull (0))); /* { dg-error "out-of-range" } */
-- (void) removeObject: (id)object __attribute__ ((nonnull (0))); /* { dg-error "out-of-range" } */
++ (void) removeObject: (id)object __attribute__ ((nonnull (0))); /* { dg-warning "does not refer to a function parameter" } */
+- (void) removeObject: (id)object __attribute__ ((nonnull (0))); /* { dg-warning "does not refer to a function parameter" } */
 
-+ (void) removeObject: (id)object __attribute__ ((nonnull (2))); /* { dg-error "out-of-range" } */
-- (void) removeObject: (id)object __attribute__ ((nonnull (2))); /* { dg-error "out-of-range" } */
++ (void) removeObject: (id)object __attribute__ ((nonnull (2))); /* { dg-warning "exceeds the number of function parameters 3" } */
+- (void) removeObject: (id)object __attribute__ ((nonnull (2))); /* { dg-warning "exceeds the number of function parameters 3" } */
 
-+ (void) removeObjectAtIndex: (size_t)object __attribute__ ((nonnull (1))); /* { dg-error "non-pointer operand" } */
-- (void) removeObjectAtIndex: (size_t)object __attribute__ ((nonnull (1))); /* { dg-error "non-pointer operand" } */
++ (void) removeObjectAtIndex: (size_t)object __attribute__ ((nonnull (1))); /* { dg-warning "refers to parameter type .size_t." } */
+- (void) removeObjectAtIndex: (size_t)object __attribute__ ((nonnull (1))); /* { dg-warning "refers to parameter type .size_t." } */
 
-+ (void) removeObject: (id)object __attribute__ ((nonnull (MyArray))); /* { dg-error "invalid operand" } */
-- (void) removeObject: (id)object __attribute__ ((nonnull (MyArray))); /* { dg-error "invalid operand" } */
++ (void) removeObject: (id)object __attribute__ ((nonnull (MyArray))); /* { dg-warning "is invalid" } */
+- (void) removeObject: (id)object __attribute__ ((nonnull (MyArray))); /* { dg-warning "is invalid" } */
 @end
 
 void test (MyArray *object)
diff --git a/gcc/tree.c b/gcc/tree.c
index f87bc37..2bc8354 100644
--- a/gcc/tree.c
+++ b/gcc/tree.c
@@ -4947,7 +4947,8 @@ build_nt_call_vec (tree fn, vec<tree, va_gc> *args)
   return ret;
 }
 
-/* Create a DECL_... node of code CODE, name NAME and data type TYPE.
+/* Create a DECL_... node of code CODE, name NAME  (if non-null)
+   and data type TYPE.
    We do NOT enter this node in any sort of symbol table.
 
    LOC is the location of the decl.
@@ -6733,12 +6734,11 @@ type_list_equal (const_tree l1, const_tree l2)
    then this function counts only the ordinary arguments.  */
 
 int
-type_num_arguments (const_tree type)
+type_num_arguments (const_tree fntype)
 {
   int i = 0;
-  tree t;
 
-  for (t = TYPE_ARG_TYPES (type); t; t = TREE_CHAIN (t))
+  for (tree t = TYPE_ARG_TYPES (fntype); t; t = TREE_CHAIN (t))
     /* If the function does not take a variable number of arguments,
        the last element in the list will have type `void'.  */
     if (VOID_TYPE_P (TREE_VALUE (t)))
@@ -6749,6 +6749,37 @@ type_num_arguments (const_tree type)
   return i;
 }
 
+/* Return the type of the function TYPE's argument ARGNO if known.
+   For vararg function's where ARGNO refers to one of the variadic
+   arguments return null.  Otherwise, return a void_type_node for
+   out-of-bounds ARGNO.  */
+
+tree
+type_argument_type (const_tree fntype, unsigned argno)
+{
+  /* Treat zero the same as an out-of-bounds argument number.  */
+  if (!argno)
+    return void_type_node;
+
+  unsigned i = 1;
+
+  for (tree t = TYPE_ARG_TYPES (fntype); ; t = TREE_CHAIN (t), ++i)
+    {
+      /* A vararg function's argument list ends in a null.  Otheriwse,
+	 an ordinary function's argument list ends with void.  Return
+	 null if ARGNO refers to a vararg argument, void_type_node if
+	 it's out of bounds, and the formal argument type otherwise.  */
+      if (!t)
+	break;
+
+      tree argtype = TREE_VALUE (t);
+      if (i == argno || VOID_TYPE_P (argtype))
+	return argtype;
+    }
+
+  return NULL_TREE;
+}
+
 /* Nonzero if integer constants T1 and T2
    represent the same constant value.  */
 
diff --git a/gcc/tree.h b/gcc/tree.h
index 2e45f9d..94e4329 100644
--- a/gcc/tree.h
+++ b/gcc/tree.h
@@ -4798,6 +4798,7 @@ extern tree get_file_function_name (const char *);
 extern tree get_callee_fndecl (const_tree);
 extern combined_fn get_call_combined_fn (const_tree);
 extern int type_num_arguments (const_tree);
+extern tree type_argument_type (const_tree, unsigned) ATTRIBUTE_NONNULL (1);
 extern bool associative_tree_code (enum tree_code);
 extern bool commutative_tree_code (enum tree_code);
 extern bool commutative_ternary_tree_code (enum tree_code);
Richard Sandiford Oct. 25, 2018, 7:17 a.m. UTC | #9
Martin Sebor <msebor@gmail.com> writes:
> On 10/16/2018 04:35 PM, Jeff Law wrote:
>> On 10/8/18 5:22 PM, Martin Sebor wrote:
>>> @@ -1322,6 +1323,18 @@ extern int tm_attr_to_mask (tree);
>>>  extern tree tm_mask_to_attr (int);
>>>  extern tree find_tm_attribute (tree);
>>>
>>> +/* A bitmap of flags to positional_argument.  */
>>> +enum PosArgFlags {
>>> +  /* Consider positional attribute argument value zero valid.  */
>>> +  posarg_zero = 1,
>>> +  /* Consider positional attribute argument value valid if it refers
>>> +     to the ellipsis (i.e., beyond the last typed argument).  */
>>> +  posarg_ellipsis = 2
>>> +};
>>> +
>>> +extern tree positional_argument (const_tree, const_tree, tree, tree_code,
>>> +				 int = 0, int = PosArgFlags ());
>>> +
>>>  extern enum flt_eval_method
>>>  excess_precision_mode_join (enum flt_eval_method, enum flt_eval_method);
>> No camel case.  Make the enum type lower case and its values upper case.
>
> Done.
>
> As an aside, I almost thought that after nearly fours years
> I've adjusted to most reviewers' preferences but I'm clearly
> not quite there yet.  As usual, this is not mentioned in
> the coding conventions (except for C++ template parameters
> where it is the preferred spelling),

FWIW it's:

  https://www.gnu.org/prep/standards/html_node/Names.html

  Please use underscores to separate words in a name, so that the Emacs
  word commands can be useful within them. Stick to lower case; reserve
  upper case for macros and enum constants, and for name-prefixes that
  follow a uniform convention.

  For example, you should use names like ignore_space_change_flag; don’t
  use names like iCantReadThis.

It doesn't actually say you *have* to use upper case for enum constants,
it only implicitly recommends it by reserving upper case for that purpose.
But it is pretty clear about CamelCase.

> and there are also examples of different styles in GCC, including the
> one I chose. For instance, in c-format.c the format_type enum uses all
> lowercase enumerators.  There are also quite a few (over a hundred in
> fact) examples of CamelCase enums in various front-ends, back-ends,
> and other parts of GCC.

Frontends and backends can be a bit of a law unto themselves :-)

If a reviewer picks up something that doesn't follow the coding conventions,
it seems fair enough to raise it, even if there are already instances of
the problem in the codebase.  This isn't just Jeff's personal preference.

Thanks,
Richard
Martin Sebor Oct. 31, 2018, 4:09 p.m. UTC | #10
Ping.  The latest patch is here:
   https://gcc.gnu.org/ml/gcc-patches/2018-10/msg01551.html

On 10/24/2018 08:02 PM, Martin Sebor wrote:
> On 10/16/2018 04:35 PM, Jeff Law wrote:
>> On 10/8/18 5:22 PM, Martin Sebor wrote:
>>> Attached is an updated patch with the INTEGRAL_TYPE_P test added
>>> to detect constant non-integer arguments like (void*)0, and with
>>> quoting made unconditional.  I also removed the pretty printer
>>> business to avoid the "value%s" format, and modified the manual
>>> to clarify when each of the attributes are applicable and what
>>> their meaningful argument values are.
>>>
>>> On 10/07/2018 04:38 PM, Martin Sebor wrote:
>>>> While still testing an enhancement in the area of attributes
>>>> I ran across bugs and inconsistencies in how different handlers
>>>> deal with positional arguments.  The bugs are either an ICE due
>>>> to the handlers not consistently converting positional arguments
>>>> to constants (i.e., CONST_DEL to INTEGER_CST in C++) for which
>>>> downstream code is unprepared (PR 87541), or errors for valid
>>>> code (PR 87542), or failing to diagnose invalid arguments.
>>>> The other inconsistencies are simply in responding to the same
>>>> invalid condition differently for different attributes .
>>>>
>>>> The attached patch introduces a new function to do validate
>>>> positional arguments in a uniform way and replaces the existing
>>>> handling with it.
>>>>
>>>> As a consequence of the handling being made consistent a number
>>>> of tests needed adjusting. In addition, some invalid arguments
>>>> that were previously rejected with a hard error are diagnosed
>>>> with just a warning (invalid argument values in attribute format),
>>>> and in one other instance what previously triggered a warning is
>>>> now accepted without one (attribute alloc_size on a function
>>>> without a prototype).  I'd be up tightening things up if that's
>>>> preferable as long it's done consistently.
>>>>
>>>> Tested on x86_64-linux.
>>>>
>>>> Martin
>>>>
>>>> PS It would be nice to have a general policy for how to respond
>>>> to invalid attribute arguments (warning or error).  Even better,
>>>> it would be ideal to move the validation from the front-ends and
>>>> back-ends into the middle-end.  I.e., describe attribute arguments
>>>> in more detail in struct attribute_spec and have decl_attributes
>>>> validate nut just the number of arguments but also their types
>>>> and, when appropriate, expected values.
>>>
>>>
>>> gcc-87541.diff
>>>
>>> PR c++/87541 - ICE using a constant decl as an attribute alloc_size
>>> argument
>>> PR c++/87542 - bogus error on attribute format with a named constant
>>> argument
>>>
>>> gcc/ChangeLog:
>>>
>>>     PR c++/87541
>>>     PR c++/87542
>>>     * tree.c (type_argument_type): New function.
>>>     * tree.h (type_argument_type): Declare it.
>>>     * gcc/doc/extend.texi (alloc_align): Update and clarify.
>>>     (alloc_size, nonnull, sentinel): Same.
>>>
>>> gcc/c-family/ChangeLog:
>>>
>>>     PR c++/87541
>>>     PR c++/87542
>>>     * c-attribs.c (positional_argument): New function.
>>>     (handle_alloc_size_attribute): Use it and simplify.
>>>     (handle_alloc_align_attribute): Same.
>>>     (handle_assume_aligned_attribute): Same.
>>>     (handle_nonnull_attribute): Same.
>>>     * c-common.c (check_function_arguments): Pass fntype to
>>>     check_function_format.
>>>     * c-common.h (check_function_format): Add an argument.
>>>     (PosArgFlags, positional_argument): Declare new type and function.
>>>     * c-format.c (decode_format_attr): Add arguments.
>>>     (check_format_string, get_constant): Same.
>>>     (convert_format_name_to_system_name): Adjust.
>>>
>>> gcc/testsuite/ChangeLog:
>>>
>>>     PR c++/87541
>>>     PR c++/87542
>>>     * g++.dg/ext/attr-alloc_size.C: New test.
>>>     * c-c++-common/pr71574.c: Adjust diagnostics.
>>>     * c-c++-common/attributes-1.c: Same.
>>>     * gcc.dg/attr-alloc_align-2.c: Same.
>>>     * gcc.dg/attr-alloc_align-4.c: New test.
>>>     * gcc.dg/attr-alloc_size-2.c: Adjust diagnostics.
>>>     * gcc.dg/attr-alloc_size.c: Same.
>>>     * gcc.dg/attr-assume_aligned-4.c: New test.
>>>     * gcc.dg/format/attr-3.c: Adjust diagnostics.
>>>     * gcc.dg/nonnull-2.c: Same.
>>>     * obj-c++.dg/attributes/method-format-1.mm: Same.
>>>     * obj-c++.dg/attributes/method-nonnull-1.mm: Same.
>>>     * objc.dg/attributes/method-format-1.m: same.
>>>     * objc.dg/attributes/method-nonnull-1.m: Same.
>>
>>
>>
>>> Index: gcc/c-family/c-common.c
>>> @@ -1322,6 +1323,18 @@ extern int tm_attr_to_mask (tree);
>>>  extern tree tm_mask_to_attr (int);
>>>  extern tree find_tm_attribute (tree);
>>>
>>> +/* A bitmap of flags to positional_argument.  */
>>> +enum PosArgFlags {
>>> +  /* Consider positional attribute argument value zero valid.  */
>>> +  posarg_zero = 1,
>>> +  /* Consider positional attribute argument value valid if it refers
>>> +     to the ellipsis (i.e., beyond the last typed argument).  */
>>> +  posarg_ellipsis = 2
>>> +};
>>> +
>>> +extern tree positional_argument (const_tree, const_tree, tree,
>>> tree_code,
>>> +                 int = 0, int = PosArgFlags ());
>>> +
>>>  extern enum flt_eval_method
>>>  excess_precision_mode_join (enum flt_eval_method, enum
>>> flt_eval_method);
>> No camel case.  Make the enum type lower case and its values upper case.
>
> Done.
>
> As an aside, I almost thought that after nearly fours years
> I've adjusted to most reviewers' preferences but I'm clearly
> not quite there yet.  As usual, this is not mentioned in
> the coding conventions (except for C++ template parameters
> where it is the preferred spelling), and there are also
> examples of different styles in GCC, including the one
> I chose. For instance, in c-format.c the format_type enum
> uses all lowercase enumerators.  There are also quite a few
> (over a hundred in fact) examples of CamelCase enums in
> various front-ends, back-ends, and other parts of GCC.
>
>>> @@ -326,17 +331,18 @@ static bool
>>>      }
>>>      }
>>>
>>> -  if (!get_constant (format_num_expr, &info->format_num, validated_p))
>>> -    {
>>> -      error ("format string has invalid operand number");
>>> -      return false;
>>> -    }
>>> +  if (tree val = get_constant (fntype, atname, *format_num_expr,
>>> +                   2, &info->format_num, 0, validated_p))
>>> +    *format_num_expr = val;
>>> +  else
>>> +    return false;
>> Is it really a good idea to be modifying something inside of ARGS like
>> this?  At the very least the function comments neeed updating.
>
> That's what the code does.  My patch doesn't change it, it just
> adds a new variable.  I added a comment to mention it nonetheless.
>
>>> -  if (!get_constant (first_arg_num_expr, &info->first_arg_num,
>>> validated_p))
>>> -    {
>>> -      error ("%<...%> has invalid operand number");
>>> -      return false;
>>> -    }
>>> +  if (tree val = get_constant (fntype, atname, *first_arg_num_expr,
>>> +                   3, &info->first_arg_num,
>>> +                   (posarg_zero | posarg_ellipsis), validated_p))
>>> +    *first_arg_num_expr = val;
>>> +  else
>>> +    return false;
>> Similarly.
>
> Same as above.  The original code passed &info->first_arg_num
> to get_constant and changed info->first_arg_num there.  I just
> introduced a temporary, first_arg_num_expr, and set it to
> the address of info->first_arg_num.
>
>>> +/* Return the type of the function TYPE's argument ARGNO if known.
>>> +   For vararg function's where ARGNO refers to one of the variadic
>>> +   arguments return null.  Otherwise, return a void_type_node for
>>> +   out-of-bounds ARGNO.  */
>>> +
>>> +tree
>>> +type_argument_type (const_tree type, unsigned argno)
>> You might consider calling the argument "fntype".  That makes it a
>> little clearer what you're iterating over.
>
> Done.  I copied the function, including the argument name, from
> from type_num_arguments (const_tree type), so I've changed that
> one to match.
>
>>
>>> +{
>>> +  /* Treat zero the same as an out-of-bounds argument number.  */
>>> +  if (!argno)
>>> +    return void_type_node;
>>> +
>>> +  unsigned i = 1;
>>> +
>>> +  for (tree t = TYPE_ARG_TYPES (type); ; t = TREE_CHAIN (t), ++i)
>> There's already iterators to walk over TYPE_ARG_TYPES.  See
>> FOREACH_FUNCTION_ARGS
>
> As I explained above, I just copied another function just above
> the one I added.
>
> Besides the one in the function I copied there are six other
> loops in this file and just one use of FOREACH_FUNCTION_ARGS.
> I also think the macro is harder to understand and poor style.
> It requires declaring an extra variable outside the scope of
> the loop even if it isn't used.  So I kept the loop as is.
>
> Retested on x86_64-linux.
>
> Martin
Jeff Law Nov. 7, 2018, 10:14 p.m. UTC | #11
On 10/24/18 8:02 PM, Martin Sebor wrote:

>> No camel case.  Make the enum type lower case and its values upper case.
> 
> Done.
> 
> As an aside, I almost thought that after nearly fours years
> I've adjusted to most reviewers' preferences but I'm clearly
> not quite there yet.  As usual, this is not mentioned in
> the coding conventions (except for C++ template parameters
> where it is the preferred spelling), and there are also
> examples of different styles in GCC, including the one
> I chose. For instance, in c-format.c the format_type enum
> uses all lowercase enumerators.  There are also quite a few
> (over a hundred in fact) examples of CamelCase enums in
> various front-ends, back-ends, and other parts of GCC.
It happens.  Avoiding camel case is more important than the upper vs
lower case on the enum constants (and I thought avoiding camel case was
mentioned somewhere, but I could be wrong).  It it wasn't for the  camel
case I probably wouldn't have said anything about the enum constants.

Jeff


> 
>>> @@ -326,17 +331,18 @@ static bool
>>>      }
>>>      }
>>>
>>> -  if (!get_constant (format_num_expr, &info->format_num, validated_p))
>>> -    {
>>> -      error ("format string has invalid operand number");
>>> -      return false;
>>> -    }
>>> +  if (tree val = get_constant (fntype, atname, *format_num_expr,
>>> +                   2, &info->format_num, 0, validated_p))
>>> +    *format_num_expr = val;
>>> +  else
>>> +    return false;
>> Is it really a good idea to be modifying something inside of ARGS like
>> this?  At the very least the function comments neeed updating.
> 
> That's what the code does.  My patch doesn't change it, it just
> adds a new variable.  I added a comment to mention it nonetheless.
Ah.  Must have missed that in the original.  In that case ignore my
comment.  Similarly for the other instance.
.
> 
>>
>>> +{
>>> +  /* Treat zero the same as an out-of-bounds argument number.  */
>>> +  if (!argno)
>>> +    return void_type_node;
>>> +
>>> +  unsigned i = 1;
>>> +
>>> +  for (tree t = TYPE_ARG_TYPES (type); ; t = TREE_CHAIN (t), ++i)
>> There's already iterators to walk over TYPE_ARG_TYPES.  See
>> FOREACH_FUNCTION_ARGS
> 
> As I explained above, I just copied another function just above
> the one I added.
> 
> Besides the one in the function I copied there are six other
> loops in this file and just one use of FOREACH_FUNCTION_ARGS.
> I also think the macro is harder to understand and poor style.
> It requires declaring an extra variable outside the scope of
> the loop even if it isn't used.  So I kept the loop as is.
We're generally trying to use iterators more than open coding loops all
the time.  The existence of code that doesn't use iterators (when
iterators exist) isn't a justification for adding new open coded loops.

Please use the iterator.  OK with that change.

jeff
diff mbox series

Patch

PR c++/87541 - ICE using a constant decl as an attribute alloc_size argument

gcc/ChangeLog:

	PR c++/87541
	* tree.c (type_argument_type): New function.
	* tree.h (type_argument_type): Declare it.

gcc/c-family/ChangeLog:

	PR c++/87541
	* c-attribs.c (positional_argument): New function.
	(handle_alloc_size_attribute): Use it and simplify.
	(handle_alloc_align_attribute): Same.
	(handle_assume_aligned_attribute): Same.
	(handle_nonnull_attribute): Same.
	* c-common.c (check_function_arguments): Pass fntype to
	check_function_format.
	* c-common.h (check_function_format): Add an argument.
	(PosArgFlags, positional_argument): Declare new type and function.
	* c-format.c (decode_format_attr): Add arguments.
	(check_format_string, get_constant): Same.
	(convert_format_name_to_system_name): Adjust.

gcc/testsuite/ChangeLog:

	PR c++/87541
	* g++.dg/ext/attr-alloc_size.C: New test.
	* c-c++-common/pr71574.c: Adjust diagnostics.
	* c-c++-common/attributes-1.c: Same.
	* gcc.dg/attr-alloc_align-2.c: Same.
	* gcc.dg/attr-alloc_align-4.c: New test.
	* gcc.dg/attr-alloc_size-2.c: Adjust diagnostics.
	* gcc.dg/attr-alloc_size.c: Same.
	* gcc.dg/attr-assume_aligned-4.c: New test.
	* gcc.dg/format/attr-3.c: Adjust diagnostics.
	* gcc.dg/nonnull-2.c: Same.

Index: gcc/c-family/c-attribs.c
===================================================================
--- gcc/c-family/c-attribs.c	(revision 264901)
+++ gcc/c-family/c-attribs.c	(working copy)
@@ -44,6 +44,7 @@  along with GCC; see the file COPYING3.  If not see
 #include "tree-iterator.h"
 #include "opts.h"
 #include "gimplify.h"
+#include "tree-pretty-print.h"
 
 static tree handle_packed_attribute (tree *, tree, tree, int, bool *);
 static tree handle_nocommon_attribute (tree *, tree, tree, int, bool *);
@@ -492,6 +493,170 @@  attribute_takes_identifier_p (const_tree attr_id)
     return targetm.attribute_takes_identifier_p (attr_id);
 }
 
+/* Verify that argument value POS at position ARGNO to attribute NAME
+   applied to function TYPE refers to a function parameter at position
+   POS and the expected type CODE.  If so, return POS after default
+   conversions, if any.  Otherwise, issue appropriate warnings and
+   return null.  A non-zero 1-based ARGNO should be passed ib by
+   callers only for attributes with more than one argument.  */
+
+tree
+positional_argument (const_tree fntype, const_tree atname, tree pos,
+		     tree_code code, int argno /* = 0 */,
+		     int flags /* = PosArgFlags () */)
+{
+  if (pos && TREE_CODE (pos) != IDENTIFIER_NODE
+      && TREE_CODE (pos) != FUNCTION_DECL)
+    pos = default_conversion (pos);
+
+  pretty_printer posval;
+  if (pos != error_mark_node)
+    {
+      /* Only format the position value when it's valid.  By convention
+	 do not quote constant integers.  */
+      pp_space (&posval);
+      if (TREE_CODE (pos) != INTEGER_CST)
+	pp_begin_quote (&posval, pp_show_color (global_dc->printer));
+      dump_generic_node (&posval, pos, 0, TDF_NONE, false);
+      if (TREE_CODE (pos) != INTEGER_CST)
+	pp_end_quote (&posval, pp_show_color (global_dc->printer));
+    }
+
+  if (TREE_CODE (pos) != INTEGER_CST)
+    {
+      /* Only mention the argument number when it's non-zero.  */
+      if (argno < 1)
+	warning (OPT_Wattributes,
+		 "%qE attribute argument value%s is not an integer "
+		 "constant",
+		 atname, pp_formatted_text (&posval));
+      else
+	warning (OPT_Wattributes,
+		 "%qE attribute argument %i value%s is not an integer "
+		 "constant",
+		 atname, argno, pp_formatted_text (&posval));
+	
+      return NULL_TREE;
+    }
+
+  /* Argument positions are 1-based.  */
+  if (integer_zerop (pos))
+    {
+      if (flags & posarg_zero)
+	/* Zero is explicitly allowed.  */
+	return pos;
+
+      if (argno < 1)
+	warning (OPT_Wattributes,
+		 "%qE attribute argument value%s does not refer to "
+		 "a function parameter",
+		 atname, pp_formatted_text (&posval));
+      else
+	warning (OPT_Wattributes,
+		 "%qE attribute argument %i value%s does not refer to "
+		 "a function parameter",
+		 atname, argno, pp_formatted_text (&posval));
+	
+      return NULL_TREE;
+    }
+
+  if (!prototype_p (fntype))
+    return pos;
+
+  /* Verify that the argument position does not exceed the number
+     of formal arguments to the function.  When POSARG_ELLIPSIS
+     is set, ARGNO may be beyond the last argument of a vararg
+     function.  */
+  unsigned nargs = type_num_arguments (fntype);
+  if (!nargs
+      || !tree_fits_uhwi_p (pos)
+      || ((flags & posarg_ellipsis) == 0
+	  && !IN_RANGE (tree_to_uhwi (pos), 1, nargs)))
+    {
+
+      if (argno < 1)
+	warning (OPT_Wattributes,
+		 "%qE attribute argument value%s exceeds the number "
+		 "of function parameters %u",
+		 atname, pp_formatted_text (&posval), nargs);
+      else
+	warning (OPT_Wattributes,
+		 "%qE attribute argument %i value%s exceeds the number "
+		 "of function parameters %u",
+		 atname, argno, pp_formatted_text (&posval), nargs);
+      return NULL_TREE;
+    }
+
+  /* Verify that the type of the referenced formal argument matches
+     the expected type.  */
+  unsigned HOST_WIDE_INT ipos = tree_to_uhwi (pos);
+
+  /* Zero was handled above.  */
+  gcc_assert (ipos != 0);
+
+  if (tree argtype = type_argument_type (fntype, ipos))
+    {
+      if (flags & posarg_ellipsis)
+	{
+	  if (argno < 1)
+	    error ("%qE attribute argument value%s does not refer to "
+		   "a variable argument list",
+		   atname, pp_formatted_text (&posval));
+	  else
+	    error ("%qE attribute argument %i value%s does not refer to "
+		   "a variable argument list",
+		   atname, argno, pp_formatted_text (&posval));
+	  return NULL_TREE;
+	}
+
+      /* Where the expected code is STRING_CST accept any pointer
+	 to a narrow character type, qualified or otherwise.  */
+      bool type_match;
+      if (code == STRING_CST && POINTER_TYPE_P (argtype))
+	{
+	  tree type = TREE_TYPE (argtype);
+	  type = TYPE_MAIN_VARIANT (type);
+	  type_match = (type == char_type_node
+			|| type == signed_char_type_node
+			|| type == unsigned_char_type_node);
+	}
+      else
+	type_match = TREE_CODE (argtype) == code;
+
+      if (!type_match)
+	{
+	  if (argno < 1)
+	    warning (OPT_Wattributes,
+		     "%qE attribute argument value%s refers to "
+		     "parameter type %qT",
+		     atname, pp_formatted_text (&posval), argtype);
+	  else
+	    warning (OPT_Wattributes,
+		   "%qE attribute argument %i value%s refers to "
+		     "parameter type %qT",
+		     atname, argno, pp_formatted_text (&posval), argtype);
+	  return NULL_TREE;
+	}
+    }
+  else if (!(flags & posarg_ellipsis))
+    {
+      if (argno < 1)
+	warning (OPT_Wattributes,
+		 "%qE attribute argument value%s refers to "
+		 "a variadic function parameter of unknown type",
+		 atname, pp_formatted_text (&posval));
+      else
+	warning (OPT_Wattributes,
+		 "%qE attribute argument %i value%s refers to "
+		 "a variadic function parameter of unknown type",
+		 atname, argno, pp_formatted_text (&posval));
+      return NULL_TREE;
+    }
+
+  return pos;
+}
+
+
 /* Attribute handlers common to C front ends.  */
 
 /* Handle a "packed" attribute; arguments as in
@@ -2398,27 +2563,21 @@  handle_malloc_attribute (tree *node, tree name, tr
    struct attribute_spec.handler.  */
 
 static tree
-handle_alloc_size_attribute (tree *node, tree ARG_UNUSED (name), tree args,
+handle_alloc_size_attribute (tree *node, tree name, tree args,
 			     int ARG_UNUSED (flags), bool *no_add_attrs)
 {
-  unsigned arg_count = type_num_arguments (*node);
-  for (; args; args = TREE_CHAIN (args))
+  for (int i = 1; args; args = TREE_CHAIN (args), ++i)
     {
-      tree position = TREE_VALUE (args);
-      if (position && TREE_CODE (position) != IDENTIFIER_NODE
-	  && TREE_CODE (position) != FUNCTION_DECL)
-	position = default_conversion (position);
-
-      if (!tree_fits_uhwi_p (position)
-	  || !arg_count
-	  || !IN_RANGE (tree_to_uhwi (position), 1, arg_count))
+      tree pos = TREE_VALUE (args);
+      if (tree val = positional_argument (*node, name, pos, INTEGER_TYPE, i))
+	TREE_VALUE (args) = val;
+      else
 	{
-	  warning (OPT_Wattributes,
-	           "alloc_size parameter outside range");
 	  *no_add_attrs = true;
-	  return NULL_TREE;
+	  break;
 	}
     }
+
   return NULL_TREE;
 }
 
@@ -2426,24 +2585,23 @@  static tree
    struct attribute_spec.handler.  */
 
 static tree
-handle_alloc_align_attribute (tree *node, tree, tree args, int,
+handle_alloc_align_attribute (tree *node, tree name, tree args, int,
 			      bool *no_add_attrs)
 {
-  unsigned arg_count = type_num_arguments (*node);
-  tree position = TREE_VALUE (args);
-  if (position && TREE_CODE (position) != IDENTIFIER_NODE
-      && TREE_CODE (position) != FUNCTION_DECL)
-    position = default_conversion (position);
-
-  if (!tree_fits_uhwi_p (position)
-      || !arg_count
-      || !IN_RANGE (tree_to_uhwi (position), 1, arg_count))
+  tree decl = *node;
+  tree rettype = TREE_TYPE (decl);
+  if (!POINTER_TYPE_P (rettype))
     {
       warning (OPT_Wattributes,
-	       "alloc_align parameter outside range");
+	       "%qE attribute ignored on a function returning %qT",
+	       name, rettype);
       *no_add_attrs = true;
       return NULL_TREE;
     }
+
+  if (!positional_argument (*node, name, TREE_VALUE (args), INTEGER_TYPE))
+    *no_add_attrs = true;
+
   return NULL_TREE;
 }
 
@@ -2451,23 +2609,63 @@  static tree
    struct attribute_spec.handler.  */
 
 static tree
-handle_assume_aligned_attribute (tree *, tree, tree args, int,
+handle_assume_aligned_attribute (tree *node, tree name, tree args, int,
 				 bool *no_add_attrs)
 {
+  tree decl = *node;
+  tree rettype = TREE_TYPE (decl);
+  if (TREE_CODE (rettype) != POINTER_TYPE)
+    {
+      warning (OPT_Wattributes,
+	       "%qE attribute ignored on a function returning %qT",
+	       name, rettype);
+      *no_add_attrs = true;
+      return NULL_TREE;
+    }
+
+  /* The alignment specified by the first argument.  */
+  tree align = NULL_TREE;
+
   for (; args; args = TREE_CHAIN (args))
     {
-      tree position = TREE_VALUE (args);
-      if (position && TREE_CODE (position) != IDENTIFIER_NODE
-	  && TREE_CODE (position) != FUNCTION_DECL)
-	position = default_conversion (position);
+      tree val = TREE_VALUE (args);
+      if (val && TREE_CODE (val) != IDENTIFIER_NODE
+	  && TREE_CODE (val) != FUNCTION_DECL)
+	val = default_conversion (val);
 
-      if (TREE_CODE (position) != INTEGER_CST)
+      if (!tree_fits_shwi_p (val))
 	{
 	  warning (OPT_Wattributes,
-		   "assume_aligned parameter not integer constant");
+		   "%qE attribute %E is not an integer constant",
+		   name, val);
 	  *no_add_attrs = true;
 	  return NULL_TREE;
 	}
+
+      if (!align)
+	{
+	  /* Validate and save the alignment.  */
+	  if (!integer_pow2p (val))
+	    {
+	      warning (OPT_Wattributes,
+		       "%qE attribute argument %E is not a power of 2",
+		       name, val);
+	      *no_add_attrs = true;
+	      return NULL_TREE;
+	    }
+
+	  align = val;
+	}
+      else if (tree_int_cst_sgn (val) < 0 || tree_int_cst_le (align, val))
+	{
+	  /* The misalignment specified by the second argument
+	     must be non-negative and less than the alignment.  */
+	  warning (OPT_Wattributes,
+		   "%qE attribute argument %E is not in the range [0, %E)",
+		   name, val, align);
+	  *no_add_attrs = true;
+	  return NULL_TREE;
+	}
     }
   return NULL_TREE;
 }
@@ -3049,12 +3247,11 @@  handle_vector_size_attribute (tree *node, tree nam
 /* Handle the "nonnull" attribute.  */
 
 static tree
-handle_nonnull_attribute (tree *node, tree ARG_UNUSED (name),
+handle_nonnull_attribute (tree *node, tree name,
 			  tree args, int ARG_UNUSED (flags),
 			  bool *no_add_attrs)
 {
   tree type = *node;
-  unsigned HOST_WIDE_INT attr_arg_num;
 
   /* If no arguments are specified, all pointer arguments should be
      non-null.  Verify a full prototype is given so that the arguments
@@ -3073,57 +3270,16 @@  static tree
       return NULL_TREE;
     }
 
-  /* Argument list specified.  Verify that each argument number references
-     a pointer argument.  */
-  for (attr_arg_num = 1; args; attr_arg_num++, args = TREE_CHAIN (args))
+  for (int i = 1; args; args = TREE_CHAIN (args), ++i)
     {
-      unsigned HOST_WIDE_INT arg_num = 0, ck_num;
-
-      tree arg = TREE_VALUE (args);
-      if (arg && TREE_CODE (arg) != IDENTIFIER_NODE
-	  && TREE_CODE (arg) != FUNCTION_DECL)
-	TREE_VALUE (args) = arg = default_conversion (arg);
-
-      if (!get_nonnull_operand (arg, &arg_num))
+      tree pos = TREE_VALUE (args);
+      if (tree val = positional_argument (type, name, pos, POINTER_TYPE, i))
+	TREE_VALUE (args) = val;
+      else
 	{
-	  error ("nonnull argument has invalid operand number (argument %lu)",
-		 (unsigned long) attr_arg_num);
 	  *no_add_attrs = true;
-	  return NULL_TREE;
+	  break;
 	}
-
-      if (prototype_p (type))
-	{
-	  function_args_iterator iter;
-	  tree argument;
-
-	  function_args_iter_init (&iter, type);
-	  for (ck_num = 1; ; ck_num++, function_args_iter_next (&iter))
-	    {
-	      argument = function_args_iter_cond (&iter);
-	      if (argument == NULL_TREE || ck_num == arg_num)
-		break;
-	    }
-
-	  if (!argument
-	      || TREE_CODE (argument) == VOID_TYPE)
-	    {
-	      error ("nonnull argument with out-of-range operand number "
-		     "(argument %lu, operand %lu)",
-		     (unsigned long) attr_arg_num, (unsigned long) arg_num);
-	      *no_add_attrs = true;
-	      return NULL_TREE;
-	    }
-
-	  if (TREE_CODE (argument) != POINTER_TYPE)
-	    {
-	      error ("nonnull argument references non-pointer operand "
-		     "(argument %lu, operand %lu)",
-		     (unsigned long) attr_arg_num, (unsigned long) arg_num);
-	      *no_add_attrs = true;
-	      return NULL_TREE;
-	    }
-	}
     }
 
   return NULL_TREE;
Index: gcc/c-family/c-common.c
===================================================================
--- gcc/c-family/c-common.c	(revision 264901)
+++ gcc/c-family/c-common.c	(working copy)
@@ -5622,7 +5622,8 @@  check_function_arguments (location_t loc, const_tr
   /* Check for errors in format strings.  */
 
   if (warn_format || warn_suggest_attribute_format)
-    check_function_format (TYPE_ATTRIBUTES (fntype), nargs, argarray, arglocs);
+    check_function_format (fntype, TYPE_ATTRIBUTES (fntype), nargs, argarray,
+			   arglocs);
 
   if (warn_format)
     check_function_sentinel (fntype, nargs, argarray);
Index: gcc/c-family/c-common.h
===================================================================
--- gcc/c-family/c-common.h	(revision 264901)
+++ gcc/c-family/c-common.h	(working copy)
@@ -804,7 +804,8 @@  extern void check_function_arguments_recurse (void
 					      unsigned HOST_WIDE_INT);
 extern bool check_builtin_function_arguments (location_t, vec<location_t>,
 					      tree, int, tree *);
-extern void check_function_format (tree, int, tree *, vec<location_t> *);
+extern void check_function_format (const_tree, tree, int, tree *,
+				   vec<location_t> *);
 extern bool attribute_fallthrough_p (tree);
 extern tree handle_format_attribute (tree *, tree, tree, int, bool *);
 extern tree handle_format_arg_attribute (tree *, tree, tree, int, bool *);
@@ -1322,6 +1323,18 @@  extern int tm_attr_to_mask (tree);
 extern tree tm_mask_to_attr (int);
 extern tree find_tm_attribute (tree);
 
+/* A bitmap of flags to positional_argument.  */
+enum PosArgFlags {
+  /* Consider positional attribute argument value zero valid.  */
+  posarg_zero = 1,
+  /* Consider positional attribute argument value valid if it refers
+     to the ellipsis (i.e., beyond the last typed argument).  */
+  posarg_ellipsis = 2
+};
+
+extern tree positional_argument (const_tree, const_tree, tree, tree_code,
+				 int = 0, int = PosArgFlags ());
+
 extern enum flt_eval_method
 excess_precision_mode_join (enum flt_eval_method, enum flt_eval_method);
 
Index: gcc/c-family/c-format.c
===================================================================
--- gcc/c-family/c-format.c	(revision 264901)
+++ gcc/c-family/c-format.c	(working copy)
@@ -62,15 +62,17 @@  static GTY(()) tree local_tree_type_node;
 static GTY(()) tree local_gimple_ptr_node;
 static GTY(()) tree locus;
 
-static bool decode_format_attr (tree, function_format_info *, int);
+static bool decode_format_attr (const_tree, tree, tree, function_format_info *,
+				bool);
 static int decode_format_type (const char *);
 
-static bool check_format_string (tree argument,
+static bool check_format_string (const_tree argument,
 				 unsigned HOST_WIDE_INT format_num,
 				 int flags, bool *no_add_attrs,
 				 int expected_format_type);
-static bool get_constant (tree expr, unsigned HOST_WIDE_INT *value,
-			  int validated_p);
+static tree get_constant (const_tree fntype, const_tree atname, tree expr,
+			  int argno, unsigned HOST_WIDE_INT *value,
+			  int flags, bool validated_p);
 static const char *convert_format_name_to_system_name (const char *attr_name);
 
 static int first_target_format_type;
@@ -132,16 +134,18 @@  valid_stringptr_type_p (tree strref)
 /* Handle a "format_arg" attribute; arguments as in
    struct attribute_spec.handler.  */
 tree
-handle_format_arg_attribute (tree *node, tree ARG_UNUSED (name),
+handle_format_arg_attribute (tree *node, tree atname,
 			     tree args, int flags, bool *no_add_attrs)
 {
   tree type = *node;
-  tree format_num_expr = TREE_VALUE (args);
+  tree *format_num_expr = &TREE_VALUE (args);
   unsigned HOST_WIDE_INT format_num = 0;
 
-  if (!get_constant (format_num_expr, &format_num, 0))
+  if (tree val = get_constant (type, atname, *format_num_expr, 0, &format_num,
+			       0, false))
+    *format_num_expr = val;
+  else
     {
-      error ("format string has invalid operand number");
       *no_add_attrs = true;
       return NULL_TREE;
     }
@@ -170,7 +174,7 @@  tree
    error).  When we know the specific reference type expected, this is also 
    checked.  */
 static bool
-check_format_string (tree fntype, unsigned HOST_WIDE_INT format_num,
+check_format_string (const_tree fntype, unsigned HOST_WIDE_INT format_num,
 		     int flags, bool *no_add_attrs, int expected_format_type)
 {
   unsigned HOST_WIDE_INT i;
@@ -263,19 +267,20 @@  static bool
 
 /* Verify EXPR is a constant, and store its value.
    If validated_p is true there should be no errors.
-   Returns true on success, false otherwise.  */
-static bool
-get_constant (tree expr, unsigned HOST_WIDE_INT *value, int validated_p)
+   Returns the converted constant value on success, null otherwise.  */
+static tree
+get_constant (const_tree fntype, const_tree atname, tree expr, int argno,
+	      unsigned HOST_WIDE_INT *value, int flags, bool validated_p)
 {
-  if (!tree_fits_uhwi_p (expr))
+  if (tree val = positional_argument (fntype, atname, expr, STRING_CST,
+				      argno, flags))
     {
-      gcc_assert (!validated_p);
-      return false;
+      *value = TREE_INT_CST_LOW (val);
+      return val;
     }
 
-  *value = TREE_INT_CST_LOW (expr);
-
-  return true;
+  gcc_assert (!validated_p);
+  return NULL_TREE;
 }
 
 /* Decode the arguments to a "format" attribute into a
@@ -286,12 +291,12 @@  static bool
    attributes are successfully decoded, false otherwise.  */
 
 static bool
-decode_format_attr (tree args, function_format_info *info, int validated_p)
+decode_format_attr (const_tree fntype, tree atname, tree args,
+		    function_format_info *info, bool validated_p)
 {
   tree format_type_id = TREE_VALUE (args);
-  tree format_num_expr = TREE_VALUE (TREE_CHAIN (args));
-  tree first_arg_num_expr
-    = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (args)));
+  tree *format_num_expr = &TREE_VALUE (TREE_CHAIN (args));
+  tree *first_arg_num_expr = &TREE_VALUE (TREE_CHAIN (TREE_CHAIN (args)));
 
   if (TREE_CODE (format_type_id) != IDENTIFIER_NODE)
     {
@@ -326,17 +331,18 @@  static bool
 	}
     }
 
-  if (!get_constant (format_num_expr, &info->format_num, validated_p))
-    {
-      error ("format string has invalid operand number");
-      return false;
-    }
+  if (tree val = get_constant (fntype, atname, *format_num_expr,
+			       2, &info->format_num, 0, validated_p))
+    *format_num_expr = val;
+  else
+    return false;
 
-  if (!get_constant (first_arg_num_expr, &info->first_arg_num, validated_p))
-    {
-      error ("%<...%> has invalid operand number");
-      return false;
-    }
+  if (tree val = get_constant (fntype, atname, *first_arg_num_expr,
+			       3, &info->first_arg_num,
+			       (posarg_zero | posarg_ellipsis), validated_p))
+    *first_arg_num_expr = val;
+  else
+    return false;
 
   if (info->first_arg_num != 0 && info->first_arg_num <= info->format_num)
     {
@@ -1076,11 +1082,13 @@  decode_format_type (const char *s)
    attribute themselves.  */
 
 void
-check_function_format (tree attrs, int nargs, tree *argarray,
-		       vec<location_t> *arglocs)
+check_function_format (const_tree fntype, tree attrs, int nargs,
+		       tree *argarray, vec<location_t> *arglocs)
 {
   tree a;
 
+  tree atname = get_identifier ("format");
+
   /* See if this function has any format attributes.  */
   for (a = attrs; a; a = TREE_CHAIN (a))
     {
@@ -1088,7 +1096,8 @@  void
 	{
 	  /* Yup; check it.  */
 	  function_format_info info;
-	  decode_format_attr (TREE_VALUE (a), &info, /*validated=*/true);
+	  decode_format_attr (fntype, atname, TREE_VALUE (a), &info,
+			      /*validated=*/true);
 	  if (warn_format)
 	    {
 	      /* FIXME: Rewrite all the internal functions in this file
@@ -4100,10 +4109,10 @@  convert_format_name_to_system_name (const char *at
 /* Handle a "format" attribute; arguments as in
    struct attribute_spec.handler.  */
 tree
-handle_format_attribute (tree *node, tree ARG_UNUSED (name), tree args,
+handle_format_attribute (tree *node, tree atname, tree args,
 			 int flags, bool *no_add_attrs)
 {
-  tree type = *node;
+  const_tree type = *node;
   function_format_info info;
 
 #ifdef TARGET_FORMAT_TYPES
@@ -4129,7 +4138,7 @@  tree
   if (TREE_CODE (TREE_VALUE (args)) == IDENTIFIER_NODE)
     TREE_VALUE (args) = canonicalize_attr_name (TREE_VALUE (args));
 
-  if (!decode_format_attr (args, &info, 0))
+  if (!decode_format_attr (type, atname, args, &info, /* validated_p = */false))
     {
       *no_add_attrs = true;
       return NULL_TREE;
Index: gcc/testsuite/c-c++-common/attributes-1.c
===================================================================
--- gcc/testsuite/c-c++-common/attributes-1.c	(revision 264901)
+++ gcc/testsuite/c-c++-common/attributes-1.c	(working copy)
@@ -1,21 +1,22 @@ 
 /* { dg-do compile } */
 /* { dg-prune-output "undeclared here \\(not in a function\\)|\[^\n\r\]* was not declared in this scope" } */
 
-void* my_calloc(unsigned, unsigned) __attribute__((alloc_size(1,bar))); /* { dg-warning "outside range" } */
-void* my_realloc(void*, unsigned) __attribute__((alloc_size(bar))); /* { dg-warning "outside range" } */
+void* my_calloc(unsigned, unsigned) __attribute__((alloc_size(1,bar))); /* { dg-warning ".alloc_size. attribute argument 2 value is not an integer constant" } */
+void* my_realloc(void*, unsigned) __attribute__((alloc_size(bar))); /* { dg-warning ".alloc_size. attribute argument 1 value is not an integer constant" } */
 
 typedef char vec __attribute__((vector_size(bar))); /* { dg-warning "ignored" } */
 
-void f1(char*) __attribute__((nonnull(bar))); /* { dg-error "invalid operand" } */
-void f2(char*) __attribute__((nonnull(1,bar))); /* { dg-error "invalid operand" } */
+void f1(char*) __attribute__((nonnull(bar))); /* { dg-warning ".nonnull. attribute argument 1 value is not an integer constant" } */
 
+void f2(char*) __attribute__((nonnull(1,bar))); /* { dg-warning ".nonnull. attribute argument 2 value is not an integer constant" } */
+
 void foo(void);
-void* my_calloc(unsigned, unsigned) __attribute__((alloc_size(1,foo))); /* { dg-warning "outside range" } */
-void* my_realloc(void*, unsigned) __attribute__((alloc_size(foo))); /* { dg-warning "outside range" } */
+void* my_calloc(unsigned, unsigned) __attribute__((alloc_size(1,foo))); /* { dg-warning ".alloc_size. attribute argument 2 value .foo. is not an integer constant" } */
+void* my_realloc(void*, unsigned) __attribute__((alloc_size(foo))); /* { dg-warning ".alloc_size. attribute argument 1 value .foo. is not an integer constant" } */
 
 typedef char vec __attribute__((vector_size(foo))); /* { dg-warning "ignored" } */
 
-void f1(char*) __attribute__((nonnull(foo))); /* { dg-error "invalid operand" } */
-void f2(char*) __attribute__((nonnull(1,foo))); /* { dg-error "invalid operand" } */
+void f1(char*) __attribute__((nonnull(foo))); /* { dg-warning ".nonnull. attribute argument 1 value .foo. is not an integer constant" } */
+void f2(char*) __attribute__((nonnull(1,foo))); /* { dg-warning ".nonnull. attribute argument 2 value .foo. is not an integer constant" } */
 
 void g() __attribute__((aligned(foo))); /* { dg-error "invalid value|not an integer" } */
Index: gcc/testsuite/g++.dg/ext/attr-alloc_size.C
===================================================================
--- gcc/testsuite/g++.dg/ext/attr-alloc_size.C	(nonexistent)
+++ gcc/testsuite/g++.dg/ext/attr-alloc_size.C	(working copy)
@@ -0,0 +1,53 @@ 
+/* PR c++/87541 - ICE using a constant decl as an attribute alloc_size argument
+   { dg-do compile }
+   { dg-options "-O2 -Wall" } */
+
+#define ALLOC_SIZE(N)   __attribute__ ((alloc_size (N)))
+
+const int i1 = 1;
+ALLOC_SIZE (i1) void* fcst (int);
+
+void* call_fcst (void)
+{
+  void *p = fcst (1);
+  __builtin___memset_chk (p, 0, 2, __builtin_object_size (p, 1));   // { dg-warning "\\\[-Wstringop-overflow=" }
+  return p;
+}
+
+
+enum { e1 = 1 };
+ALLOC_SIZE (e1) void* fenum (int);
+
+void* call_fenum (void)
+{
+  void *p = fenum (1);
+  __builtin___memset_chk (p, 0, 2, __builtin_object_size (p, 1));   // { dg-warning "\\\[-Wstringop-overflow=" }
+  return p;
+}
+
+
+template <class T>
+struct A
+{
+  ALLOC_SIZE (T::N1) static void* ftemplarg_1 (int);
+  ALLOC_SIZE (T::N2) static void*
+  ftemplarg_2 (int); // { dg-warning "attribute argument 1 value 2 exceeds the number of function parameters 1" }
+};
+
+struct B { static const int N1 = 1; static const int N2 = 1; };
+
+void* call_ftemplarg_1 (A<B> *pa)
+{
+  void *p = pa->ftemplarg_1 (1);
+  __builtin___memset_chk (p, 0, 2, __builtin_object_size (p, 1));   // { dg-warning "\\\[-Wstringop-overflow=" }
+  return p;
+}
+
+struct C { static const int N1 = 1; static const int N2 = 2; };
+
+void* call_ftemplarg_2 (A<C> *pa)
+{
+  void *p = pa->ftemplarg_2 (1);
+  __builtin___memset_chk (p, 0, 2, __builtin_object_size (p, 1));
+  return p;
+}
Index: gcc/testsuite/gcc.dg/attr-alloc_align-2.c
===================================================================
--- gcc/testsuite/gcc.dg/attr-alloc_align-2.c	(revision 264901)
+++ gcc/testsuite/gcc.dg/attr-alloc_align-2.c	(working copy)
@@ -5,6 +5,6 @@  void *f1 (int) __attribute__((alloc_align (1)));
 void *f2 (int, int, int) __attribute__((alloc_align (3)));
 void *f3 (void) __attribute__((alloc_align)); /* { dg-error "wrong number of arguments specified" } */
 void *f4 (int, int) __attribute__((alloc_align (1, 2))); /* { dg-error "wrong number of arguments specified" } */
-void *f5 (void) __attribute__((alloc_align (i))); /* { dg-warning "outside range" } */
-void *f6 (int) __attribute__((alloc_align (0))); /* { dg-warning "outside range" } */
-void *f7 (int) __attribute__((alloc_align (2))); /* { dg-warning "outside range" } */
+void *f5 (void) __attribute__((alloc_align (i))); /* { dg-warning ".alloc_align. attribute argument value .i. is not an integer constant" } */
+void *f6 (int) __attribute__((alloc_align (0))); /* { dg-warning ".alloc_align. attribute argument value 0 does not refer to a function parameter" } */
+void *f7 (int) __attribute__((alloc_align (2))); /* { dg-warning ".alloc_align. attribute argument value 2 exceeds the number of function parameters 1" } */
Index: gcc/testsuite/gcc.dg/attr-alloc_align-4.c
===================================================================
--- gcc/testsuite/gcc.dg/attr-alloc_align-4.c	(nonexistent)
+++ gcc/testsuite/gcc.dg/attr-alloc_align-4.c	(working copy)
@@ -0,0 +1,37 @@ 
+/* PR middle-end/81871 - bogus attribute alloc_align accepted
+   { dg-do compile }
+   { dg-options "-Wall" } */
+
+#define ALIGN(N)   __attribute__ ((alloc_align (N)))
+#define SIZE_MAX  __SIZE_MAX__
+
+ALIGN (1) void fvv_m1 (void);     /* { dg-warning ".alloc_align. attribute ignored on a function returning .void." } */
+
+ALIGN (1) int fiv_1 (void);       /* { dg-warning ".alloc_align. attribute ignored on a function returning .int." } */
+
+ALIGN (0) void* fpvv_0 (void);    /* { dg-warning ".alloc_align. attribute argument value 0 does not refer to a function parameter" } */
+
+ALIGN (1) void* fpvv_1 (void);    /* { dg-warning ".alloc_align. attribute argument value 1 exceeds the number of function parameters 0" } */
+
+ALIGN (2) void* fii_2 (int);      /* { dg-warning ".alloc_align. attribute argument value 2 exceeds the number of function parameters 1" } */
+
+ALIGN (1) void fvi_1 (int);       /* { dg-warning ".alloc_align. attribute ignored on a function returning .void." } */
+
+/* Using alloc_align with a function returning a pointer to a function
+   should perhaps trigger a warning.  */
+typedef void (F)(void);
+ALIGN (1) F* fpF_i_1 (int);
+
+ALIGN (SIZE_MAX) void*
+fpvi_szmax (int);                 /* { dg-warning ".alloc_align. attribute argument value \[0-9\]+ exceeds the number of function parameters 1" } */
+
+ALIGN ("1") void*
+fpvi_str_1 (int);                 /* { dg-warning ".alloc_align. attribute argument value .\"1\". is not an integer constant" } */
+
+ALIGN (1) void*
+fpvi_pv_1 (void*);                /* { dg-warning ".alloc_align. attribute argument value 1 refers to parameter type .void \\\*." } */
+
+struct S { int i; };
+ALIGN (2) void*
+fpvi_S_2 (int, struct S);         /* { dg-warning ".alloc_align. attribute argument value 2 refers to parameter type .struct S." } */
+
Index: gcc/testsuite/gcc.dg/attr-alloc_size-2.c
===================================================================
--- gcc/testsuite/gcc.dg/attr-alloc_size-2.c	(revision 264901)
+++ gcc/testsuite/gcc.dg/attr-alloc_size-2.c	(working copy)
@@ -1,4 +1,5 @@ 
-/* { dg-do compile } */
+/* PR c/36021 - __attribute__((alloc_size(n))) with function of no
+   arguments causes gcc to segfault
+   { dg-do compile } */
 
-char *foo() __attribute__((alloc_size(1))); /* { dg-warning "outside range" } */
-
+char *foo() __attribute__((alloc_size(1)));
Index: gcc/testsuite/gcc.dg/attr-alloc_size.c
===================================================================
--- gcc/testsuite/gcc.dg/attr-alloc_size.c	(revision 264901)
+++ gcc/testsuite/gcc.dg/attr-alloc_size.c	(working copy)
@@ -5,13 +5,13 @@  extern void abort (void);
 
 #include "../gcc.c-torture/execute/builtins/chk.h"
 
-extern char *mallocminus1(int size) __attribute__((alloc_size(-1))); /* { dg-warning "parameter outside range" } */
-extern char *malloc0(int size) __attribute__((alloc_size(0))); /* { dg-warning "parameter outside range" } */
+extern char *mallocminus1(int size) __attribute__((alloc_size(-1))); /* { dg-warning ".alloc_size. attribute argument 1 value -1 exceeds the number of function parameters 1" } */
+extern char *malloc0(int size) __attribute__((alloc_size(0))); /* { dg-warning ".alloc_size. attribute argument 1 value 0 does not refer to a function parameter" } */
 extern char *malloc1(int size) __attribute__((alloc_size(1)));
 extern char *malloc2(int empty, int size) __attribute__((alloc_size(2)));
 extern char *calloc1(int size, int elements) __attribute__((alloc_size(1,2)));
 extern char *calloc2(int size, int empty, int elements) __attribute__((alloc_size(1,3)));
-extern char *balloc1(void *size) __attribute__((alloc_size(1)));
+extern char *balloc1(void *size) __attribute__((alloc_size(1)));   /* { dg-warning ".alloc_size. attribute argument 1 value 1 refers to parameter type .void *." } */
 
 void
 test (void)
Index: gcc/testsuite/gcc.dg/attr-assume_aligned-4.c
===================================================================
--- gcc/testsuite/gcc.dg/attr-assume_aligned-4.c	(nonexistent)
+++ gcc/testsuite/gcc.dg/attr-assume_aligned-4.c	(working copy)
@@ -0,0 +1,36 @@ 
+/* PR middle-end/87533 - bogus assume_aligned attribute silently accepted
+   { dg-do compile }
+   { dg-options "-Wall" } */
+
+#define A(...)  __attribute__ ((assume_aligned (__VA_ARGS__)))
+
+A (1) void fv_1 (void);       /* { dg-warning ".assume_aligned. attribute ignored on a function returning .void." } */
+
+A (1) int fi_1 (void);        /* { dg-warning ".assume_aligned. attribute ignored on a function returning .int." } */
+
+A (-1) void* fpv_m1 (void);   /* { dg-warning ".assume_aligned. attribute argument -1 is not a power of 2" } */
+
+A (0) void* fpv_0 (void);     /* { dg-warning ".assume_aligned. attribute argument 0 is not a power of 2" } */
+
+/* Alignment of 1 is fine, it just doesn't offer any benefits.  */
+A (1) void* fpv_1 (void);
+
+A (3) void* fpv_3 (void);     /* { dg-warning ".assume_aligned. attribute argument 3 is not a power of 2" } */
+
+A (16383) void* fpv_16km1 (void);     /* { dg-warning ".assume_aligned. attribute argument 16383 is not a power of 2" } */
+A (16384) void* fpv_16k (void);
+A (16385) void* fpv_16kp1 (void);    /* { dg-warning ".assume_aligned. attribute argument 16385 is not a power of 2" } */
+
+A (32767) void* fpv_32km1 (void);     /* { dg-warning ".assume_aligned. attribute argument 32767 is not a power of 2" } */
+
+A (4, -1) void* fpv_4_m1 (void);      /* { dg-warning ".assume_aligned. attribute argument -1 is not in the range \\\[0, 4\\\)" } */
+
+A (4, 0) void* fpv_4_0 (void);
+A (4, 1) void* fpv_4_1 (void);
+A (4, 2) void* fpv_4_2 (void);
+A (4, 3) void* fpv_4_3 (void);
+
+A (4, 4) void* fpv_4_3 (void);        /* { dg-warning ".assume_aligned. attribute argument 4 is not in the range \\\[0, 4\\\)" } */
+
+A (4) void* gpv_4_3 (void);
+A (2) void* gpv_4_3 (void);
Index: gcc/testsuite/gcc.dg/format/attr-3.c
===================================================================
--- gcc/testsuite/gcc.dg/format/attr-3.c	(revision 264901)
+++ gcc/testsuite/gcc.dg/format/attr-3.c	(working copy)
@@ -41,10 +41,10 @@  extern void fe1 (const char *, ...) __attribute__(
 /* Both the numbers must be integer constant expressions.  */
 extern void ff0 (const char *, ...) __attribute__((format(gnu_attr_printf, 3-2, (long long)(10/5))));
 int foo;
-extern void ff1 (const char *, ...) __attribute__((format(gnu_attr_printf, foo, 10/5))); /* { dg-error "invalid operand" "bad number" } */
-extern void ff2 (const char *, ...) __attribute__((format(gnu_attr_printf, 3-2, foo))); /* { dg-error "invalid operand" "bad number" } */
+extern void ff1 (const char *, ...) __attribute__((format(gnu_attr_printf, foo, 10/5))); /* { dg-warning ".format. attribute argument 2 value .foo. is not an integer constant" "bad number" } */
+extern void ff2 (const char *, ...) __attribute__((format(gnu_attr_printf, 3-2, foo))); /* { dg-warning ".format. attribute argument 3 value .foo. is not an integer constant" "bad number" } */
 extern char *ff3 (const char *) __attribute__((format_arg(3-2)));
-extern char *ff4 (const char *) __attribute__((format_arg(foo))); /* { dg-error "invalid operand" "bad format_arg number" } */
+extern char *ff4 (const char *) __attribute__((format_arg(foo))); /* { dg-warning ".format_arg. attribute argument value .foo. is not an integer constant" "bad format_arg number" } */
 
 /* The format string argument must precede the arguments to be formatted.
    This includes if no parameter types are specified (which is not valid ISO
@@ -56,14 +56,14 @@  extern void fg3 () __attribute__((format(gnu_attr_
 
 /* The format string argument must be a string type, and the arguments to
    be formatted must be the "...".  */
-extern void fh0 (int, ...) __attribute__((format(gnu_attr_printf, 1, 2))); /* { dg-error "not a string" "format int string" } */
+extern void fh0 (int, ...) __attribute__((format(gnu_attr_printf, 1, 2))); /* { dg-warning ".format. attribute argument 2 value 1 refers to parameter type .int." "format int string" } */
 extern void fh1 (signed char *, ...) __attribute__((format(gnu_attr_printf, 1, 2))); /* { dg-error "not a string" "signed char string" } */
 extern void fh2 (unsigned char *, ...) __attribute__((format(gnu_attr_printf, 1, 2))); /* { dg-error "not a string" "unsigned char string" } */
 extern void fh3 (const char *, ...) __attribute__((format(gnu_attr_printf, 1, 3))); /* { dg-error "is not" "not ..." } */
-extern void fh4 (const char *, int, ...) __attribute__((format(gnu_attr_printf, 1, 2))); /* { dg-error "is not" "not ..." } */
+extern void fh4 (const char *, int, ...) __attribute__((format(gnu_attr_printf, 1, 2))); /* { dg-error ".format. attribute argument 3 value 2 does not refer to a variable argument list" "not ..." } */
 
 /* format_arg formats must take and return a string.  */
-extern char *fi0 (int) __attribute__((format_arg(1))); /* { dg-error "not a string" "format_arg int string" } */
+extern char *fi0 (int) __attribute__((format_arg(1))); /* { dg-warning ".format_arg. attribute argument value 1 refers to parameter type .int." } */
 extern char *fi1 (signed char *) __attribute__((format_arg(1))); /* { dg-error "not a string" "format_arg signed char string" } */
 extern char *fi2 (unsigned char *) __attribute__((format_arg(1))); /* { dg-error "not a string" "format_arg unsigned char string" } */
 extern int fi3 (const char *) __attribute__((format_arg(1))); /* { dg-error "not return string" "format_arg ret int string" } */
Index: gcc/testsuite/gcc.dg/nonnull-2.c
===================================================================
--- gcc/testsuite/gcc.dg/nonnull-2.c	(revision 264901)
+++ gcc/testsuite/gcc.dg/nonnull-2.c	(working copy)
@@ -4,11 +4,12 @@ 
 
 extern void func1 () __attribute__((nonnull)); /* { dg-error "without arguments" } */
 
-extern void func2 (char *) __attribute__((nonnull(2))); /* { dg-error "out-of-range operand" } */
+extern void func2 (char *) __attribute__((nonnull(2))); /* { dg-warning ".nonnull. attribute argument 1 value 2 exceeds the number of function parameters 1" } */
 
-extern void func3 (char *) __attribute__((nonnull(foo))); /* { dg-error "invalid operand number|undeclared" } */
+extern void func3 (char *) __attribute__((nonnull (foo))); /* { dg-warning ".nonnull. attribute argument 1 value is not an integer constant" } */
+/* { dg-error ".foo. undeclared" "undeclared argument" { target *-*-* } .-1 } */
 
-extern void func4 (int) __attribute__((nonnull(1))); /* { dg-error "references non-pointer" } */
+extern void func4 (int) __attribute__((nonnull(1))); /* { dg-warning ".nonnull. attribute argument 1 value 1 refers to parameter type .int." } */
 
 void
 foo (void)
Index: gcc/tree.c
===================================================================
--- gcc/tree.c	(revision 264901)
+++ gcc/tree.c	(working copy)
@@ -4947,7 +4947,8 @@  build_nt_call_vec (tree fn, vec<tree, va_gc> *args
   return ret;
 }
 
-/* Create a DECL_... node of code CODE, name NAME and data type TYPE.
+/* Create a DECL_... node of code CODE, name NAME  (if non-null)
+   and data type TYPE.
    We do NOT enter this node in any sort of symbol table.
 
    LOC is the location of the decl.
@@ -6749,6 +6750,37 @@  type_num_arguments (const_tree type)
   return i;
 }
 
+/* Return the type of the function TYPE's argument ARGNO if known.
+   For vararg function's where ARGNO refers to one of the variadic
+   arguments return null.  Otherwise, return a void_type_node for
+   out-of-bounds ARGNO.  */
+
+tree
+type_argument_type (const_tree type, unsigned argno)
+{
+  /* Treat zero the same as an out-of-bounds argument number.  */
+  if (!argno)
+    return void_type_node;
+
+  unsigned i = 1;
+
+  for (tree t = TYPE_ARG_TYPES (type); ; t = TREE_CHAIN (t), ++i)
+    {
+      /* A vararg function's argument list ends in a null.  Otheriwse,
+	 an ordinary function's argument list ends with void.  Return
+	 null if ARGNO refers to a vararg argument, void_type_node if
+	 it's out of bounds, and the formal argument type otherwise.  */
+      if (!t)
+	break;
+
+      tree argtype = TREE_VALUE (t);
+      if (i == argno || VOID_TYPE_P (argtype))
+	return argtype;
+    }
+
+  return NULL_TREE;
+}
+
 /* Nonzero if integer constants T1 and T2
    represent the same constant value.  */
 
Index: gcc/tree.h
===================================================================
--- gcc/tree.h	(revision 264901)
+++ gcc/tree.h	(working copy)
@@ -4798,6 +4798,7 @@  extern tree get_file_function_name (const char *);
 extern tree get_callee_fndecl (const_tree);
 extern combined_fn get_call_combined_fn (const_tree);
 extern int type_num_arguments (const_tree);
+extern tree type_argument_type (const_tree, unsigned) ATTRIBUTE_NONNULL (1);
 extern bool associative_tree_code (enum tree_code);
 extern bool commutative_tree_code (enum tree_code);
 extern bool commutative_ternary_tree_code (enum tree_code);
Index: gcc/testsuite/c-c++-common/pr71574.c
===================================================================
--- gcc/testsuite/c-c++-common/pr71574.c	(revision 264901)
+++ gcc/testsuite/c-c++-common/pr71574.c	(working copy)
@@ -1,12 +1,15 @@ 
-/* PR c/71574 */
+/* PR c/71574 - ICE on code with alloc_align attribute */
 /* { dg-do compile } */
 
 int fn1 (void);
-int fn2 (void) __attribute__ ((alloc_align (fn1))); /* { dg-warning "parameter outside range" } */
-int fn3 (void) __attribute__ ((alloc_size (fn1))); /* { dg-warning "parameter outside range" } */
-int fn4 (void) __attribute__ ((assume_aligned (fn1))); /* { dg-warning "not integer constant" } */
-int fn5 (char *, char *) __attribute__((nonnull (fn1))); /* { dg-error "nonnull argument has invalid operand" } */
+int fn2 (void) __attribute__ ((alloc_align (fn1))); /* { dg-warning ".alloc_align. attribute ignored on a function returning .int." } */
+int fn3 (void) __attribute__ ((alloc_size (fn1))); /* { dg-warning ".alloc_size. attribute argument 1 value .fn1. is not an integer constant" } */
+int fn4 (void) __attribute__ ((assume_aligned (fn1))); /* { dg-warning ".assume_aligned. attribute ignored on a function returning .int." } */
+int fn5 (char *, char *) __attribute__((nonnull (fn1))); /* { dg-warning ".nonnull. attribute argument 1 value .fn1. is not an integer constant" } */
 int fn6 (const char *, ...) __attribute__ ((sentinel (fn1))); /* { dg-warning "not an integer constant" } */
 
+void* fn7 (void) __attribute__ ((alloc_align (fn1))); /* { dg-warning ".alloc_align. attribute argument value .fn1. is not an integer constant" } */
+void* fn8 (void) __attribute__ ((assume_aligned (fn1))); /* { dg-warning "not an integer constant" } */
+
 typedef int __attribute__((vector_size (fn1))) v4si; /* { dg-warning "attribute ignored" } */
 typedef int T __attribute__((aligned (fn1))); /* { dg-error "requested alignment is not" } */
Index: gcc/testsuite/obj-c++.dg/attributes/method-format-1.mm
===================================================================
--- gcc/testsuite/obj-c++.dg/attributes/method-format-1.mm	(revision 264901)
+++ gcc/testsuite/obj-c++.dg/attributes/method-format-1.mm	(working copy)
@@ -19,8 +19,8 @@ 
 - (void) log2: (int)level  message: (const char *) my_format, ...  __attribute__ ((format (printf, 2)));    /* { dg-error "wrong" } */
 + (void) debug2: (const char *) my_format, ...  __attribute__ ((format (printf))); /* { dg-error "wrong" } */
 - (void) debug2: (const char *) my_format, ...  __attribute__ ((format (printf))); /* { dg-error "wrong" } */
-+ (void) alert: (const char *) my_format __attribute__ ((format (printf, 1, 2))); /* { dg-error "args to be formatted is not ..." } */
-- (void) alert: (const char *) my_format __attribute__ ((format (printf, 1, 2))); /* { dg-error "args to be formatted is not ..." } */
++ (void) alert: (const char *) my_format __attribute__ ((format (printf, 1, 2))); /* { dg-warning "does not refer to a variable argument list" } */
+- (void) alert: (const char *) my_format __attribute__ ((format (printf, 1, 2))); /* { dg-warning "does not refer to a variable argument list" } */
 @end
 
 void test (LogObject *object)
Index: gcc/testsuite/obj-c++.dg/attributes/method-nonnull-1.mm
===================================================================
--- gcc/testsuite/obj-c++.dg/attributes/method-nonnull-1.mm	(revision 264901)
+++ gcc/testsuite/obj-c++.dg/attributes/method-nonnull-1.mm	(working copy)
@@ -19,14 +19,14 @@ 
 - (void) insertObject: (id)object  atIndex: (size_t)index  andObject: (id)anotherObject  atIndex: (size_t)anotherIndex __attribute__ ((nonnull (1, 3)));
 
 /* Test the behavior with invalid code.  */
-+ (void) removeObject: (id)object __attribute__ ((nonnull (0))); /* { dg-error "out-of-range" } */
-- (void) removeObject: (id)object __attribute__ ((nonnull (0))); /* { dg-error "out-of-range" } */
++ (void) removeObject: (id)object __attribute__ ((nonnull (0))); /* { dg-warning "does not refer to a function parameter" } */
+- (void) removeObject: (id)object __attribute__ ((nonnull (0))); /* { dg-warning "does not refer to a function parameter" } */
 
-+ (void) removeObject: (id)object __attribute__ ((nonnull (2))); /* { dg-error "out-of-range" } */
-- (void) removeObject: (id)object __attribute__ ((nonnull (2))); /* { dg-error "out-of-range" } */
++ (void) removeObject: (id)object __attribute__ ((nonnull (2))); /* { dg-warning "exceeds the number of function parameters 3" } */
+- (void) removeObject: (id)object __attribute__ ((nonnull (2))); /* { dg-warning "exceeds the number of function parameters 3" } */
 
-+ (void) removeObjectAtIndex: (size_t)object __attribute__ ((nonnull (1))); /* { dg-error "non-pointer operand" } */
-- (void) removeObjectAtIndex: (size_t)object __attribute__ ((nonnull (1))); /* { dg-error "non-pointer operand" } */
++ (void) removeObjectAtIndex: (size_t)object __attribute__ ((nonnull (1))); /* { dg-warning "refers to parameter type .size_t." } */
+- (void) removeObjectAtIndex: (size_t)object __attribute__ ((nonnull (1))); /* { dg-warning "refers to parameter type .size_t." } */
 
 + (void) removeObject: (id)object __attribute__ ((nonnull (MyArray))); /* { dg-error "" } */
 - (void) removeObject: (id)object __attribute__ ((nonnull (MyArray))); /* { dg-error "" } */
Index: gcc/testsuite/objc.dg/attributes/method-format-1.m
===================================================================
--- gcc/testsuite/objc.dg/attributes/method-format-1.m	(revision 264901)
+++ gcc/testsuite/objc.dg/attributes/method-format-1.m	(working copy)
@@ -19,8 +19,8 @@ 
 - (void) log2: (int)level  message: (const char *) my_format, ...  __attribute__ ((format (printf, 2)));    /* { dg-error "wrong" } */
 + (void) debug2: (const char *) my_format, ...  __attribute__ ((format (printf))); /* { dg-error "wrong" } */
 - (void) debug2: (const char *) my_format, ...  __attribute__ ((format (printf))); /* { dg-error "wrong" } */
-+ (void) alert: (const char *) my_format __attribute__ ((format (printf, 1, 2))); /* { dg-error "args to be formatted is not ..." } */
-- (void) alert: (const char *) my_format __attribute__ ((format (printf, 1, 2))); /* { dg-error "args to be formatted is not ..." } */
++ (void) alert: (const char *) my_format __attribute__ ((format (printf, 1, 2))); /* { dg-error "does not refer to a variable argument list" } */
+- (void) alert: (const char *) my_format __attribute__ ((format (printf, 1, 2))); /* { dg-error "does not refer to a variable argument list" } */
 @end
 
 void test (LogObject *object)
Index: gcc/testsuite/objc.dg/attributes/method-nonnull-1.m
===================================================================
--- gcc/testsuite/objc.dg/attributes/method-nonnull-1.m	(revision 264901)
+++ gcc/testsuite/objc.dg/attributes/method-nonnull-1.m	(working copy)
@@ -19,17 +19,17 @@ 
 - (void) insertObject: (id)object  atIndex: (size_t)index  andObject: (id)anotherObject  atIndex: (size_t)anotherIndex __attribute__ ((nonnull (1, 3)));
 
 /* Test the behavior with invalid code.  */
-+ (void) removeObject: (id)object __attribute__ ((nonnull (0))); /* { dg-error "out-of-range" } */
-- (void) removeObject: (id)object __attribute__ ((nonnull (0))); /* { dg-error "out-of-range" } */
++ (void) removeObject: (id)object __attribute__ ((nonnull (0))); /* { dg-warning "does not refer to a function parameter" } */
+- (void) removeObject: (id)object __attribute__ ((nonnull (0))); /* { dg-warning "does not refer to a function parameter" } */
 
-+ (void) removeObject: (id)object __attribute__ ((nonnull (2))); /* { dg-error "out-of-range" } */
-- (void) removeObject: (id)object __attribute__ ((nonnull (2))); /* { dg-error "out-of-range" } */
++ (void) removeObject: (id)object __attribute__ ((nonnull (2))); /* { dg-error "exceeds the number of function parameters 3" } */
+- (void) removeObject: (id)object __attribute__ ((nonnull (2))); /* { dg-error "exceeds the number of function parameters 3" } */
 
-+ (void) removeObjectAtIndex: (size_t)object __attribute__ ((nonnull (1))); /* { dg-error "non-pointer operand" } */
-- (void) removeObjectAtIndex: (size_t)object __attribute__ ((nonnull (1))); /* { dg-error "non-pointer operand" } */
++ (void) removeObjectAtIndex: (size_t)object __attribute__ ((nonnull (1))); /* { dg-warning "refers to parameter type .size_t." } */
+- (void) removeObjectAtIndex: (size_t)object __attribute__ ((nonnull (1))); /* { dg-warning "refers to parameter type .size_t." } */
 
-+ (void) removeObject: (id)object __attribute__ ((nonnull (MyArray))); /* { dg-error "invalid operand" } */
-- (void) removeObject: (id)object __attribute__ ((nonnull (MyArray))); /* { dg-error "invalid operand" } */
++ (void) removeObject: (id)object __attribute__ ((nonnull (MyArray))); /* { dg-warning "is not an integer constant" } */
+- (void) removeObject: (id)object __attribute__ ((nonnull (MyArray))); /* { dg-warning "is not an integer constant" } */
 @end
 
 void test (MyArray *object)