From 1d24950977c730f5e955060057b1dd0b5c051adb Mon Sep 17 00:00:00 2001 From: Martin Sebor Date: Thu, 15 Nov 2018 22:53:57 +0000 Subject: [PATCH] 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 --- gcc/ChangeLog | 9 + gcc/c-family/ChangeLog | 17 + gcc/c-family/c-attribs.c | 360 ++++++++++++++---- gcc/c-family/c-common.c | 3 +- gcc/c-family/c-common.h | 15 +- gcc/c-family/c-format.c | 88 +++-- gcc/doc/extend.texi | 80 ++-- gcc/testsuite/ChangeLog | 20 + gcc/testsuite/c-c++-common/attributes-1.c | 19 +- gcc/testsuite/c-c++-common/pr71574.c | 15 +- gcc/testsuite/g++.dg/ext/attr-alloc_size.C | 53 +++ gcc/testsuite/gcc.dg/attr-alloc_align-2.c | 6 +- gcc/testsuite/gcc.dg/attr-alloc_align-4.c | 43 +++ gcc/testsuite/gcc.dg/attr-alloc_size-12.c | 60 +++ gcc/testsuite/gcc.dg/attr-alloc_size-2.c | 7 +- gcc/testsuite/gcc.dg/attr-alloc_size.c | 6 +- gcc/testsuite/gcc.dg/attr-assume_aligned-4.c | 36 ++ gcc/testsuite/gcc.dg/format/attr-3.c | 12 +- gcc/testsuite/gcc.dg/nonnull-2.c | 7 +- gcc/testsuite/gcc.dg/torture/pr80612.c | 2 + .../obj-c++.dg/attributes/method-format-1.mm | 4 +- .../obj-c++.dg/attributes/method-nonnull-1.mm | 14 +- .../objc.dg/attributes/method-format-1.m | 4 +- .../objc.dg/attributes/method-nonnull-1.m | 16 +- gcc/tree.c | 42 +- gcc/tree.h | 1 + 26 files changed, 730 insertions(+), 209 deletions(-) create mode 100644 gcc/testsuite/g++.dg/ext/attr-alloc_size.C create mode 100644 gcc/testsuite/gcc.dg/attr-alloc_align-4.c create mode 100644 gcc/testsuite/gcc.dg/attr-alloc_size-12.c create mode 100644 gcc/testsuite/gcc.dg/attr-assume_aligned-4.c diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 7b46b671227..04d2166e501 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,12 @@ +2018-11-15 Martin Sebor + + 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 Kwok Cheung Yeung diff --git a/gcc/c-family/ChangeLog b/gcc/c-family/ChangeLog index 93ee0d4947b..057009b9d42 100644 --- a/gcc/c-family/ChangeLog +++ b/gcc/c-family/ChangeLog @@ -1,3 +1,20 @@ +2018-11-15 Martin Sebor + + 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 PR other/19165 diff --git a/gcc/c-family/c-attribs.c b/gcc/c-family/c-attribs.c index 1657df7f9df..c9afa1f78f4 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 cd88f3a5ed3..69be3d3b2a0 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 31cc27325c2..8eeeba75319 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, tree, int, tree *); -extern void check_function_format (tree, int, tree *, vec *); +extern void check_function_format (const_tree, tree, int, tree *, + vec *); 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 6613092c6fd..8b17f539974 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 *arglocs) +check_function_format (const_tree fntype, tree attrs, int nargs, + tree *argarray, vec *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 d4b1046b6ae..d0146baa06f 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{} 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 c2ec7861c12..a31b59d2cc9 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,23 @@ +2018-11-15 Martin Sebor + + 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 PR c/83656 diff --git a/gcc/testsuite/c-c++-common/attributes-1.c b/gcc/testsuite/c-c++-common/attributes-1.c index 1657da10d5e..c4b232dad00 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 320ae3853cd..f06624c2d77 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 00000000000..9a421109bdb --- /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 +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 *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 *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 3dc7a219839..01321e7785d 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 00000000000..7cdfc7d840f --- /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 00000000000..2e9be1e7864 --- /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 3cac807370f..3bbd3e2b987 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 f50ba7c53db..88a77715805 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 00000000000..84f4523e58e --- /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 bee5ff4841b..31cc05ec527 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 d570a467e6a..4e3e48dd88d 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 225b81127a6..e648e82559c 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 11ce8eebbb5..9ff34f92fb1 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 58b8f367439..f83c8537706 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 11ce8eebbb5..9ff34f92fb1 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 b60d5a6ba5b..e1974aa3cae 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 db680a9bae3..be89897d43a 100644 --- a/gcc/tree.c +++ b/gcc/tree.c @@ -4955,7 +4955,8 @@ build_nt_call_vec (tree fn, vec *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 b825cade61e..c21af9ff6d9 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); -- 2.30.2