aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMartin Sebor <msebor@redhat.com>2018-11-15 22:53:57 +0000
committerMartin Sebor <msebor@gcc.gnu.org>2018-11-15 15:53:57 -0700
commit1d24950977c730f5e955060057b1dd0b5c051adb (patch)
treeaecd56593f283b79ac0939bd57a5555c6b93239f
parentcd5da9837b26d77136d3a9394747ce4325a95118 (diff)
downloadgcc-1d24950977c730f5e955060057b1dd0b5c051adb.zip
gcc-1d24950977c730f5e955060057b1dd0b5c051adb.tar.gz
gcc-1d24950977c730f5e955060057b1dd0b5c051adb.tar.bz2
PR c++/87541 - ICE using a constant decl as an attribute alloc_size argument
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. From-SVN: r266195
-rw-r--r--gcc/ChangeLog9
-rw-r--r--gcc/c-family/ChangeLog17
-rw-r--r--gcc/c-family/c-attribs.c360
-rw-r--r--gcc/c-family/c-common.c3
-rw-r--r--gcc/c-family/c-common.h15
-rw-r--r--gcc/c-family/c-format.c88
-rw-r--r--gcc/doc/extend.texi80
-rw-r--r--gcc/testsuite/ChangeLog20
-rw-r--r--gcc/testsuite/c-c++-common/attributes-1.c19
-rw-r--r--gcc/testsuite/c-c++-common/pr71574.c15
-rw-r--r--gcc/testsuite/g++.dg/ext/attr-alloc_size.C53
-rw-r--r--gcc/testsuite/gcc.dg/attr-alloc_align-2.c6
-rw-r--r--gcc/testsuite/gcc.dg/attr-alloc_align-4.c43
-rw-r--r--gcc/testsuite/gcc.dg/attr-alloc_size-12.c60
-rw-r--r--gcc/testsuite/gcc.dg/attr-alloc_size-2.c7
-rw-r--r--gcc/testsuite/gcc.dg/attr-alloc_size.c6
-rw-r--r--gcc/testsuite/gcc.dg/attr-assume_aligned-4.c36
-rw-r--r--gcc/testsuite/gcc.dg/format/attr-3.c12
-rw-r--r--gcc/testsuite/gcc.dg/nonnull-2.c7
-rw-r--r--gcc/testsuite/gcc.dg/torture/pr80612.c2
-rw-r--r--gcc/testsuite/obj-c++.dg/attributes/method-format-1.mm4
-rw-r--r--gcc/testsuite/obj-c++.dg/attributes/method-nonnull-1.mm14
-rw-r--r--gcc/testsuite/objc.dg/attributes/method-format-1.m4
-rw-r--r--gcc/testsuite/objc.dg/attributes/method-nonnull-1.m16
-rw-r--r--gcc/tree.c42
-rw-r--r--gcc/tree.h1
26 files changed, 730 insertions, 209 deletions
diff --git a/gcc/ChangeLog b/gcc/ChangeLog
index 7b46b67..04d2166 100644
--- a/gcc/ChangeLog
+++ b/gcc/ChangeLog
@@ -1,3 +1,12 @@
+2018-11-15 Martin Sebor <msebor@redhat.com>
+
+ 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.
+
2018-11-15 Andrew Stubbs <ams@codesourcery.com>
Kwok Cheung Yeung <kcy@codesourcery.com>
diff --git a/gcc/c-family/ChangeLog b/gcc/c-family/ChangeLog
index 93ee0d4..057009b 100644
--- a/gcc/c-family/ChangeLog
+++ b/gcc/c-family/ChangeLog
@@ -1,3 +1,20 @@
+2018-11-15 Martin Sebor <msebor@redhat.com>
+
+ 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.
+
2018-11-15 David Malcolm <dmalcolm@redhat.com>
PR other/19165
diff --git a/gcc/c-family/c-attribs.c b/gcc/c-family/c-attribs.c
index 1657df7..c9afa1f 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 *);
@@ -495,6 +496,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
@@ -2563,27 +2746,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;
}
@@ -2591,24 +2787,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;
}
@@ -2616,20 +2811,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;
}
@@ -3262,12 +3497,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
@@ -3286,57 +3520,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 cd88f3a..69be3d3 100644
--- a/gcc/c-family/c-common.c
+++ b/gcc/c-family/c-common.c
@@ -5658,7 +5658,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 31cc273..8eeeba7 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 *);
@@ -1330,6 +1331,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 6613092..8b17f53 100644
--- a/gcc/c-family/c-format.c
+++ b/gcc/c-family/c-format.c
@@ -63,15 +63,17 @@ static GTY(()) tree local_gimple_ptr_node;
static GTY(()) tree local_cgraph_node_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;
@@ -133,16 +135,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;
}
@@ -171,7 +176,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;
@@ -264,19 +269,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
@@ -287,12 +293,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)
{
@@ -327,17 +335,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)
{
@@ -1083,11 +1092,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))
{
@@ -1095,7 +1106,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
@@ -4124,10 +4136,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
@@ -4153,7 +4165,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 d4b1046..d0146ba 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -2406,33 +2406,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
@@ -2443,8 +2449,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
@@ -2470,22 +2476,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.
@@ -3118,8 +3127,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 *
@@ -3354,13 +3364,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))
@@ -3372,10 +3383,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/ChangeLog b/gcc/testsuite/ChangeLog
index c2ec786..a31b59d 100644
--- a/gcc/testsuite/ChangeLog
+++ b/gcc/testsuite/ChangeLog
@@ -1,5 +1,25 @@
2018-11-15 Martin Sebor <msebor@redhat.com>
+ 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.
+
+2018-11-15 Martin Sebor <msebor@redhat.com>
+
PR c/83656
* gcc.dg/20021006-1.c
* gcc.dg/Wbuiltin-declaration-mismatch.c: New test.
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 db680a9..be89897 100644
--- a/gcc/tree.c
+++ b/gcc/tree.c
@@ -4955,7 +4955,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.
@@ -6944,12 +6945,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)))
@@ -6960,6 +6960,40 @@ 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;
+
+ function_args_iterator iter;
+
+ tree argtype;
+ unsigned i = 1;
+ FOREACH_FUNCTION_ARGS (fntype, argtype, iter)
+ {
+ /* A vararg function's argument list ends in a null. Otherwise,
+ 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 (!argtype)
+ break;
+
+ if (i == argno || VOID_TYPE_P (argtype))
+ return argtype;
+
+ ++i;
+ }
+
+ 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 b825cad..c21af9f 100644
--- a/gcc/tree.h
+++ b/gcc/tree.h
@@ -4834,6 +4834,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);