/* C-family attributes handling.
- Copyright (C) 1992-2018 Free Software Foundation, Inc.
+ Copyright (C) 1992-2019 Free Software Foundation, Inc.
This file is part of GCC.
#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 *);
ATTR_EXCL (NULL, false, false, false)
};
-static const struct attribute_spec::exclusions attr_cold_hot_exclusions[] =
+extern const struct attribute_spec::exclusions attr_cold_hot_exclusions[] =
{
ATTR_EXCL ("cold", true, true, true),
ATTR_EXCL ("hot", true, true, true),
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. Treat CODE == INTEGER_TYPE as
+ matching all C integral types except bool. If successful, return
+ POS after default conversions, if any. Otherwise, issue appropriate
+ warnings and return null. A non-zero 1-based ARGNO should be passed
+ in 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
+ expected by attribute format (this includes possibly qualified
+ char pointers and, for targets like Darwin, also pointers to
+ struct CFString). */
+ bool type_match;
+ if (code == STRING_CST)
+ type_match = valid_format_string_type_p (argtype);
+ else if (code == INTEGER_TYPE)
+ /* For integers, accept enums, wide characters and other types
+ that match INTEGRAL_TYPE_P except for bool. */
+ type_match = (INTEGRAL_TYPE_P (argtype)
+ && TREE_CODE (argtype) != BOOLEAN_TYPE);
+ else
+ type_match = TREE_CODE (argtype) == code;
+
+ if (!type_match)
+ {
+ if (code == STRING_CST)
+ {
+ /* Reject invalid format strings with an error. */
+ if (argno < 1)
+ error ("%qE attribute argument value %qE refers to "
+ "parameter type %qT",
+ atname, pos, argtype);
+ else
+ error ("%qE attribute argument %i value %qE refers to "
+ "parameter type %qT",
+ atname, argno, pos, argtype);
+
+ return NULL_TREE;
+ }
+
+ 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
else if (!(flag_cf_protection & CF_BRANCH))
{
warning (OPT_Wattributes, "%qE attribute ignored. Use "
- "-fcf-protection option to enable it", name);
+ "%<-fcf-protection%> option to enable it",
+ name);
*no_add_attrs = true;
}
bool objfile = (TREE_CODE (*node) == FUNCTION_DECL
|| (VAR_P (*node) && TREE_STATIC (*node)));
/* Log2 of specified alignment. */
- int pow2align = check_user_alignment (align_expr, objfile, true);
+ int pow2align = check_user_alignment (align_expr, objfile,
+ /* warn_zero = */ true);
if (pow2align == -1
|| !check_cxx_fundamental_alignment_constraints (*node, pow2align, flags))
{
unsigned curalign = 0;
unsigned lastalign = 0;
+ /* True when SET_DECL_ALIGN() should be called for the decl when
+ *NO_ADD_ATTRS is false. */
+ bool set_align = true;
if (is_type)
{
if ((flags & (int) ATTR_FLAG_TYPE_IN_PLACE))
curalign = lastalign;
curalign /= BITS_PER_UNIT;
- bitalign /= BITS_PER_UNIT;
+ unsigned newalign = bitalign / BITS_PER_UNIT;
- bool diagd = true;
auto_diagnostic_group d;
- if (DECL_USER_ALIGN (decl) || DECL_USER_ALIGN (last_decl))
- diagd = warning (OPT_Wattributes,
- "ignoring attribute %<%E (%u)%> because it conflicts "
- "with attribute %<%E (%u)%>",
- name, bitalign, name, curalign);
+ if ((DECL_USER_ALIGN (decl)
+ || DECL_USER_ALIGN (last_decl)))
+ {
+ if (warning (OPT_Wattributes,
+ "ignoring attribute %<%E (%u)%> because it conflicts "
+ "with attribute %<%E (%u)%>",
+ name, newalign, name, curalign)
+ && note)
+ inform (DECL_SOURCE_LOCATION (last_decl),
+ "previous declaration here");
+ /* Only reject attempts to relax/override an alignment
+ explicitly specified previously and accept declarations
+ that appear to relax the implicit function alignment for
+ the target. Both increasing and increasing the alignment
+ set by -falign-functions setting is permitted. */
+ *no_add_attrs = true;
+ }
else if (!warn_if_not_aligned_p)
- /* Do not error out for attribute warn_if_not_aligned. */
- error ("alignment for %q+D must be at least %d", decl, curalign);
-
- if (diagd && note)
- inform (DECL_SOURCE_LOCATION (last_decl), "previous declaration here");
-
- *no_add_attrs = true;
+ {
+ /* Do not fail for attribute warn_if_not_aligned. Otherwise,
+ silently avoid applying the alignment to the declaration
+ because it's implicitly satisfied by the target. Apply
+ the attribute nevertheless so it can be retrieved by
+ __builtin_has_attribute. */
+ set_align = false;
+ }
}
else if (DECL_USER_ALIGN (decl)
&& DECL_ALIGN (decl) > bitalign)
&& TREE_CODE (decl) == FUNCTION_DECL
&& DECL_ALIGN (decl) > bitalign)
{
- /* Don't warn function alignment here if warn_if_not_aligned_p is
- true. It will be warned later. */
+ /* Don't warn for function alignment here if warn_if_not_aligned_p
+ is true. It will be warned about later. */
if (DECL_USER_ALIGN (decl))
- error ("alignment for %q+D was previously specified as %d "
- "and may not be decreased", decl,
- DECL_ALIGN (decl) / BITS_PER_UNIT);
- else
- error ("alignment for %q+D must be at least %d", decl,
- DECL_ALIGN (decl) / BITS_PER_UNIT);
- *no_add_attrs = true;
- }
- else
- {
- if (warn_if_not_aligned_p)
- {
- if (TREE_CODE (decl) == FIELD_DECL && !DECL_C_BIT_FIELD (decl))
- {
- SET_DECL_WARN_IF_NOT_ALIGN (decl, bitalign);
- warn_if_not_aligned_p = false;
- }
- }
- else
{
- SET_DECL_ALIGN (decl, bitalign);
- DECL_USER_ALIGN (decl) = 1;
+ /* Only reject attempts to relax/override an alignment
+ explicitly specified previously and accept declarations
+ that appear to relax the implicit function alignment for
+ the target. Both increasing and increasing the alignment
+ set by -falign-functions setting is permitted. */
+ error ("alignment for %q+D was previously specified as %d "
+ "and may not be decreased", decl,
+ DECL_ALIGN (decl) / BITS_PER_UNIT);
+ *no_add_attrs = true;
}
}
+ else if (warn_if_not_aligned_p
+ && TREE_CODE (decl) == FIELD_DECL
+ && !DECL_C_BIT_FIELD (decl))
+ {
+ SET_DECL_WARN_IF_NOT_ALIGN (decl, bitalign);
+ warn_if_not_aligned_p = false;
+ set_align = false;
+ }
if (warn_if_not_aligned_p)
{
decl);
*no_add_attrs = true;
}
+ else if (!is_type && !*no_add_attrs && set_align)
+ {
+ SET_DECL_ALIGN (decl, bitalign);
+ DECL_USER_ALIGN (decl) = 1;
+ }
return NULL_TREE;
}
|| is_attribute_p ("weakref", atname))
continue;
+ /* Attribute leaf only applies to extern functions.
+ Avoid copying it to static ones. */
+ if (!TREE_PUBLIC (decl)
+ && is_attribute_p ("leaf", atname))
+ continue;
+
tree atargs = TREE_VALUE (at);
/* Create a copy of just the one attribute ar AT, including
its argumentsm and add it to DECL. */
return NULL_TREE;
}
+ /* A function declared with attribute nothrow has the attribute
+ attached to it, but a C++ throw() function does not. */
+ if (TREE_NOTHROW (ref))
+ TREE_NOTHROW (decl) = true;
+
+ /* Similarly, a function declared with attribute noreturn has it
+ attached on to it, but a C11 _Noreturn function does not. */
tree reftype = ref;
+ if (DECL_P (ref)
+ && TREE_THIS_VOLATILE (ref)
+ && FUNC_OR_METHOD_TYPE_P (TREE_TYPE (reftype)))
+ TREE_THIS_VOLATILE (decl) = true;
+
if (DECL_P (ref) || EXPR_P (ref))
reftype = TREE_TYPE (ref);
if (POINTER_TYPE_P (reftype))
reftype = TREE_TYPE (reftype);
+ if (!TYPE_P (reftype))
+ return NULL_TREE;
+
tree attrs = TYPE_ATTRIBUTES (reftype);
/* Copy type attributes from REF to DECL. */
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;
}
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;
}
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;
}
/* 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
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;
with the sought attributes) has been found on the attribute chain. */
bool found_attr = false;
- /* For attribute aligned ignore the attribute list and consider
- the tree node itself instead. */
- if (type && !strcmp ("aligned", namestr))
- atlist = NULL_TREE;
-
/* When clear, the first mismatched attribute argument results
in failure. Otherwise, the first matched attribute argument
results in success. */
if (expr && DECL_P (expr))
found_match = TREE_READONLY (expr);
}
- else if (!strcmp ("const", namestr))
+ else if (!strcmp ("noreturn", namestr))
+ {
+ /* C11 _Noreturn sets the volatile bit without attaching
+ an attribute to the decl. */
+ if (expr
+ && DECL_P (expr)
+ && FUNC_OR_METHOD_TYPE_P (TREE_TYPE (expr)))
+ found_match = TREE_THIS_VOLATILE (expr);
+ }
+ else if (!strcmp ("pure", namestr))
{
if (expr && DECL_P (expr))
found_match = DECL_PURE_P (expr);