PR c/81544 - attribute noreturn and warn_unused_result on the same function accepted
gcc/c/ChangeLog:
PR c/81544
* c-decl.c (c_decl_attributes): Look up existing declaration and
pass it to decl_attributes.
gcc/c-family/ChangeLog:
PR c/81544
* c-attribs.c (attr_aligned_exclusions): New array.
(attr_alloc_exclusions, attr_cold_hot_exclusions): Same.
(attr_common_exclusions, attr_const_pure_exclusions): Same.
(attr_gnu_inline_exclusions, attr_inline_exclusions): Same.
(attr_noreturn_exclusions, attr_returns_twice_exclusions): Same.
(attr_warn_unused_result_exclusions): Same.
(handle_hot_attribute, handle_cold_attribute): Simplify.
(handle_const_attribute): Warn on function returning void.
(handle_pure_attribute): Same.
(handle_aligned_attribute): Warn on conflicting specifications.
* c-warn.c (diagnose_mismatched_attributes): Simplify.
gcc/cp/ChangeLog:
PR c/81544
* decl2.c (cplus_decl_attributes): Look up existing declaration and
pass it to decl_attributes.
* name-lookup.c (find_decl): New function.
* name-lookup.h (find_decl): Declare it.
* tree.c (cxx_attribute_table): Initialize new member of struct
attribute_spec.
gcc/testsuite/ChangeLog:
PR c/81544
* c-c++-common/Wattributes-2.c: New test.
* c-c++-common/Wattributes.c: New test.
* c-c++-common/attributes-3.c: Adjust.
* g++.dg/ext/mv11.s: New test.
* gcc.dg/attr-noinline.c: Adjust.
* gcc.dg/pr44964.c: Same.
* gcc.dg/torture/pr42363.c: Same.
* gcc.dg/tree-ssa/ssa-ccp-2.c: Same.
libstdc++-v3/ChangeLog:
PR c/81544
* include/ext/mt_allocator.h (_M_destroy_thread_key): Remove
pointless attribute const.
gcc/ChangeLog:
PR c/81544
* attribs.c (empty_attribute_table): Initialize new member of
struct attribute_spec.
(decl_attributes): Add argument. Handle mutually exclusive
combinations of attributes.
* attribs.h (decl_attributes): Add default argument.
* tree-core.h (attribute_spec::exclusions, exclude): New type and
member.
/* Associates a GNAT tree node to a GCC tree node. It is used in
@@ -94,7 +94,7 @@ static bool attributes_initialized = false;
static const struct attribute_spec empty_attribute_table[] =
{
- { NULL, 0, 0, false, false, false, NULL, false }
+ { NULL, 0, 0, false, false, false, NULL, false, NULL }
};
/* Return base name of the attribute. Ie '__attr__' is turned into 'attr'.
@@ -343,6 +343,97 @@ get_attribute_namespace (const_tree attr)
return get_identifier ("gnu");
}
+/* Check LAST_DECL and NODE of the same symbol for attributes that are
+ recorded in EXCL to be mutually exclusive with ATTRNAME, diagnose
+ them, and return true if any have been found. NODE can be a DECL
+ or a TYPE. */
+
+static bool
+diag_attr_exclusions (tree last_decl, tree node, tree attrname,
+ const attribute_spec *spec)
+{
+ const attribute_spec::exclusions *excl = spec->exclude;
+
+ tree_code code = TREE_CODE (node);
+
+ if ((code == FUNCTION_DECL && !excl->function
+ && (!excl->type || !spec->affects_type_identity))
+ || (code == VAR_DECL && !excl->variable
+ && (!excl->type || !spec->affects_type_identity))
+ || (((code == TYPE_DECL || RECORD_OR_UNION_TYPE_P (node)) && !excl->type)))
+ return false;
+
+ /* True if an attribute that's mutually exclusive with ATTRNAME
+ has been found. */
+ bool found = false;
+
+ if (last_decl && last_decl != node && TREE_TYPE (last_decl) != node)
+ {
+ /* Check both the last DECL and its type for conflicts with
+ the attribute being added to the current decl or type. */
+ found |= diag_attr_exclusions (last_decl, last_decl, attrname, spec);
+ tree decl_type = TREE_TYPE (last_decl);
+ found |= diag_attr_exclusions (last_decl, decl_type, attrname, spec);
+ }
+
+ /* NODE is either the current DECL to which the attribute is being
+ applied or its TYPE. For the former, consider the attributes on
+ both the DECL and its type. */
+ tree attrs[2];
+
+ if (DECL_P (node))
+ {
+ attrs[0] = DECL_ATTRIBUTES (node);
+ attrs[1] = TYPE_ATTRIBUTES (TREE_TYPE (node));
+ }
+ else
+ {
+ attrs[0] = TYPE_ATTRIBUTES (node);
+ attrs[1] = NULL_TREE;
+ }
+
+ /* Iterate over the mutually exclusive attribute names and verify
+ that the symbol doesn't contain it. */
+ for (unsigned i = 0; i != sizeof attrs / sizeof *attrs; ++i)
+ {
+ if (!attrs[i])
+ continue;
+
+ for ( ; excl->name; ++excl)
+ {
+ /* Avoid checking the attribute against itself. */
+ if (is_attribute_p (excl->name, attrname))
+ continue;
+
+ if (lookup_attribute (excl->name, attrs[i]))
+ {
+ found = true;
+
+ /* Print a note? */
+ bool note = last_decl != NULL_TREE;
+
+ if (TREE_CODE (node) == FUNCTION_DECL
+ && DECL_BUILT_IN (node))
+ note &= warning (OPT_Wattributes,
+ "ignoring attribute %qE in declaration of "
+ "a built-in function qD because it conflicts "
+ "with attribute %qs",
+ attrname, node, excl->name);
+ else
+ note &= warning (OPT_Wattributes,
+ "ignoring attribute %qE because "
+ "it conflicts with attribute %qs",
+ attrname, excl->name);
+
+ if (note)
+ inform (DECL_SOURCE_LOCATION (last_decl),
+ "previous declaration here");
+ }
+ }
+ }
+
+ return found;
+}
/* Process the attributes listed in ATTRIBUTES and install them in *NODE,
which is either a DECL (including a TYPE_DECL) or a TYPE. If a DECL,
@@ -354,7 +445,8 @@ get_attribute_namespace (const_tree attr)
a decl attribute to the declaration rather than to its type). */
tree
-decl_attributes (tree *node, tree attributes, int flags)
+decl_attributes (tree *node, tree attributes, int flags,
+ tree last_decl /* = NULL_TREE */)
{
tree a;
tree returned_attrs = NULL_TREE;
@@ -433,6 +525,8 @@ decl_attributes (tree *node, tree attributes, int flags)
targetm.insert_attributes (*node, &attributes);
+ /* Note that attributes on the same declaration are not necessarily
+ in the same order as in the source. */
for (a = attributes; a; a = TREE_CHAIN (a))
{
tree ns = get_attribute_namespace (a);
@@ -441,7 +535,6 @@ decl_attributes (tree *node, tree attributes, int flags)
tree *anode = node;
const struct attribute_spec *spec =
lookup_scoped_attribute_spec (ns, name);
- bool no_add_attrs = 0;
int fn_ptr_quals = 0;
tree fn_ptr_tmp = NULL_TREE;
@@ -490,7 +583,9 @@ decl_attributes (tree *node, tree attributes, int flags)
| (int) ATTR_FLAG_ARRAY_NEXT))
{
/* Pass on this attribute to be tried again. */
- returned_attrs = tree_cons (name, args, returned_attrs);
+ tree attr = tree_cons (name, args, NULL_TREE);
+ returned_attrs = chainon (returned_attrs, attr);
+ // returned_attrs = tree_cons (name, args, returned_attrs);
continue;
}
else
@@ -535,7 +630,9 @@ decl_attributes (tree *node, tree attributes, int flags)
else if (flags & (int) ATTR_FLAG_FUNCTION_NEXT)
{
/* Pass on this attribute to be tried again. */
- returned_attrs = tree_cons (name, args, returned_attrs);
+ tree attr = tree_cons (name, args, NULL_TREE);
+ returned_attrs = chainon (returned_attrs, attr);
+ // returned_attrs = tree_cons (name, args, returned_attrs);
continue;
}
@@ -557,15 +654,55 @@ decl_attributes (tree *node, tree attributes, int flags)
continue;
}
+ bool no_add_attrs = false;
+
if (spec->handler != NULL)
{
int cxx11_flag =
cxx11_attribute_p (a) ? ATTR_FLAG_CXX11 : 0;
- returned_attrs = chainon ((*spec->handler) (anode, name, args,
- flags|cxx11_flag,
- &no_add_attrs),
- returned_attrs);
+ /* Pass in an array of the current declaration followed
+ by the last pushed/merged declaration if one exists.
+ If the handler changes CUR_AND_LAST_DECL[0] replace
+ *ANODE with its value. */
+ tree cur_and_last_decl[] = { *anode, last_decl };
+ tree ret = (spec->handler) (cur_and_last_decl, name, args,
+ flags|cxx11_flag, &no_add_attrs);
+
+ *anode = cur_and_last_decl[0];
+ if (ret == error_mark_node)
+ {
+ warning (OPT_Wattributes, "%qE attribute ignored", name);
+ no_add_attrs = true;
+ }
+ else
+ returned_attrs = chainon (ret, returned_attrs);
+ }
+
+ /* If the attribute was sussceefully handled on its own and is
+ about to be added check for exclusions with other attributes
+ on the current declation as well as the last declaration of
+ the same symbol already processed (if one exists). */
+ bool built_in = flags & ATTR_FLAG_BUILT_IN;
+ if (spec->exclude
+ && !no_add_attrs
+ && (flag_checking || !built_in))
+ {
+ /* Always check attributes on user-defined functions.
+ Check them on built-ins only when -fchecking is set.
+ Ignore __builtin_unreachable -- it's both const and
+ noreturn. */
+
+ if (!built_in
+ || !DECL_P (*anode)
+ || (DECL_FUNCTION_CODE (*anode) != BUILT_IN_UNREACHABLE
+ && DECL_FUNCTION_CODE (*anode) != BUILT_IN_UBSAN_HANDLE_BUILTIN_UNREACHABLE))
+ {
+ bool no_add = diag_attr_exclusions (last_decl, *anode, name, spec);
+ if (!no_add && anode != node)
+ no_add = diag_attr_exclusions (last_decl, *node, name, spec);
+ no_add_attrs |= no_add;
+ }
}
/* Layout the decl in case anything changed. */
@@ -31,7 +31,7 @@ extern void init_attributes (void);
from tree.h. Depending on these flags, some attributes may be
returned to be applied at a later stage (for example, to apply
a decl attribute to the declaration rather than to its type). */
-extern tree decl_attributes (tree *, tree, int);
+extern tree decl_attributes (tree *, tree, int, tree = NULL_TREE);
extern bool cxx11_attribute_p (const_tree);
extern tree get_attribute_name (const_tree);
@@ -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 "bitmap.h"
static tree handle_packed_attribute (tree *, tree, tree, int, bool *);
static tree handle_nocommon_attribute (tree *, tree, tree, int, bool *);
@@ -146,6 +147,92 @@ static tree handle_fallthrough_attribute (tree *, tree, tree, int, bool *);
static tree handle_patchable_function_entry_attribute (tree *, tree, tree,
int, bool *);
+/* Helper to define attribute exclusions. */
+#define ATTR_EXCL(name, function, type, variable) \
+ { name, function, type, variable }
+
+/* Define attributes that are mutually exclusive with one another. */
+static const struct attribute_spec::exclusions attr_aligned_exclusions[] =
+{
+ /* Attribute name exclusion applies to:
+ function, type, variable */
+ ATTR_EXCL ("aligned", true, false, false),
+ ATTR_EXCL ("packed", true, false, false),
+ ATTR_EXCL (NULL, false, false, false)
+};
+
+static const struct attribute_spec::exclusions attr_cold_hot_exclusions[] =
+{
+ ATTR_EXCL ("cold", true, true, true),
+ ATTR_EXCL ("hot", true, true, true),
+ ATTR_EXCL (NULL, false, false, false)
+};
+
+static const struct attribute_spec::exclusions attr_common_exclusions[] =
+{
+ ATTR_EXCL ("common", true, true, true),
+ ATTR_EXCL ("nocommon", true, true, true),
+ ATTR_EXCL (NULL, false, false, false),
+};
+
+static const struct attribute_spec::exclusions attr_gnu_inline_exclusions[] =
+{
+ ATTR_EXCL ("gnu_inline", true, true, true),
+ ATTR_EXCL ("noinline", true, true, true),
+ ATTR_EXCL (NULL, false, false, false),
+};
+
+static const struct attribute_spec::exclusions attr_inline_exclusions[] =
+{
+ ATTR_EXCL ("always_inline", true, true, true),
+ ATTR_EXCL ("noinline", true, true, true),
+ ATTR_EXCL (NULL, false, false, false),
+};
+
+static const struct attribute_spec::exclusions attr_noreturn_exclusions[] =
+{
+ ATTR_EXCL ("noreturn", true, true, true),
+ ATTR_EXCL ("alloc_align", true, true, true),
+ ATTR_EXCL ("alloc_size", true, true, true),
+ ATTR_EXCL ("const", true, true, true),
+ ATTR_EXCL ("malloc", true, true, true),
+ ATTR_EXCL ("pure", true, true, true),
+ ATTR_EXCL ("returns_twice", true, true, true),
+ ATTR_EXCL ("warn_unused_result", true, true, true),
+ ATTR_EXCL (NULL, false, false, false),
+};
+
+static const struct attribute_spec::exclusions
+attr_warn_unused_result_exclusions[] =
+{
+ ATTR_EXCL ("noreturn", true, true, true),
+ ATTR_EXCL ("warn_unused_result", true, true, true),
+ ATTR_EXCL (NULL, false, false, false),
+};
+
+static const struct attribute_spec::exclusions attr_returns_twice_exclusions[] =
+{
+ ATTR_EXCL ("noreturn", true, true, true),
+ ATTR_EXCL (NULL, false, false, false),
+};
+
+/* Exclusions that apply to attribute alloc_align, alloc_size, and malloc. */
+static const struct attribute_spec::exclusions attr_alloc_exclusions[] =
+{
+ ATTR_EXCL ("const", true, true, true),
+ ATTR_EXCL ("noreturn", true, true, true),
+ ATTR_EXCL ("pure", true, true, true),
+ ATTR_EXCL (NULL, false, false, false),
+};
+
+static const struct attribute_spec::exclusions attr_const_pure_exclusions[] =
+{
+ ATTR_EXCL ("const", true, true, true),
+ ATTR_EXCL ("noreturn", true, true, true),
+ ATTR_EXCL ("pure", true, true, true),
+ ATTR_EXCL (NULL, false, false, false)
+};
+
/* Table of machine-independent attributes common to all C-like languages.
All attributes referencing arguments should be additionally processed
@@ -157,209 +244,230 @@ const struct attribute_spec c_common_attribute_table[] =
/* { name, min_len, max_len, decl_req, type_req, fn_type_req, handler,
affects_type_identity } */
{ "packed", 0, 0, false, false, false,
- handle_packed_attribute , false},
+ handle_packed_attribute , false,
+ attr_aligned_exclusions },
{ "nocommon", 0, 0, true, false, false,
- handle_nocommon_attribute, false},
+ handle_nocommon_attribute, false,
+ attr_common_exclusions },
{ "common", 0, 0, true, false, false,
- handle_common_attribute, false },
+ handle_common_attribute, false,
+ attr_common_exclusions },
/* FIXME: logically, noreturn attributes should be listed as
"false, true, true" and apply to function types. But implementing this
would require all the places in the compiler that use TREE_THIS_VOLATILE
on a decl to identify non-returning functions to be located and fixed
to check the function type instead. */
{ "noreturn", 0, 0, true, false, false,
- handle_noreturn_attribute, false },
+ handle_noreturn_attribute, false,
+ attr_noreturn_exclusions },
{ "volatile", 0, 0, true, false, false,
- handle_noreturn_attribute, false },
+ handle_noreturn_attribute, false, NULL },
{ "stack_protect", 0, 0, true, false, false,
- handle_stack_protect_attribute, false },
+ handle_stack_protect_attribute, false, NULL },
{ "noinline", 0, 0, true, false, false,
- handle_noinline_attribute, false },
+ handle_noinline_attribute, false,
+ attr_inline_exclusions },
{ "noclone", 0, 0, true, false, false,
- handle_noclone_attribute, false },
+ handle_noclone_attribute, false, NULL },
{ "no_icf", 0, 0, true, false, false,
- handle_noicf_attribute, false },
+ handle_noicf_attribute, false, NULL },
{ "noipa", 0, 0, true, false, false,
- handle_noipa_attribute, false },
+ handle_noipa_attribute, false, NULL },
{ "leaf", 0, 0, true, false, false,
- handle_leaf_attribute, false },
+ handle_leaf_attribute, false, NULL },
{ "always_inline", 0, 0, true, false, false,
- handle_always_inline_attribute, false },
+ handle_always_inline_attribute, false,
+ attr_inline_exclusions },
{ "gnu_inline", 0, 0, true, false, false,
- handle_gnu_inline_attribute, false },
+ handle_gnu_inline_attribute, false,
+ attr_gnu_inline_exclusions },
{ "artificial", 0, 0, true, false, false,
- handle_artificial_attribute, false },
+ handle_artificial_attribute, false, NULL },
{ "flatten", 0, 0, true, false, false,
- handle_flatten_attribute, false },
+ handle_flatten_attribute, false, NULL },
{ "used", 0, 0, true, false, false,
- handle_used_attribute, false },
+ handle_used_attribute, false, NULL },
{ "unused", 0, 0, false, false, false,
- handle_unused_attribute, false },
+ handle_unused_attribute, false, NULL },
{ "externally_visible", 0, 0, true, false, false,
- handle_externally_visible_attribute, false },
+ handle_externally_visible_attribute, false,
+ NULL },
{ "no_reorder", 0, 0, true, false, false,
- handle_no_reorder_attribute, false },
+ handle_no_reorder_attribute, false, NULL },
/* The same comments as for noreturn attributes apply to const ones. */
{ "const", 0, 0, true, false, false,
- handle_const_attribute, false },
+ handle_const_attribute, false,
+ attr_const_pure_exclusions },
{ "scalar_storage_order", 1, 1, false, false, false,
- handle_scalar_storage_order_attribute, false },
+ handle_scalar_storage_order_attribute, false,
+ NULL },
{ "transparent_union", 0, 0, false, false, false,
- handle_transparent_union_attribute, false },
+ handle_transparent_union_attribute, false, NULL },
{ "constructor", 0, 1, true, false, false,
- handle_constructor_attribute, false },
+ handle_constructor_attribute, false, NULL },
{ "destructor", 0, 1, true, false, false,
- handle_destructor_attribute, false },
+ handle_destructor_attribute, false, NULL },
{ "mode", 1, 1, false, true, false,
- handle_mode_attribute, false },
+ handle_mode_attribute, false, NULL },
{ "section", 1, 1, true, false, false,
- handle_section_attribute, false },
+ handle_section_attribute, false, NULL },
{ "aligned", 0, 1, false, false, false,
- handle_aligned_attribute, false },
+ handle_aligned_attribute, false,
+ attr_aligned_exclusions },
{ "weak", 0, 0, true, false, false,
- handle_weak_attribute, false },
+ handle_weak_attribute, false, NULL },
{ "noplt", 0, 0, true, false, false,
- handle_noplt_attribute, false },
+ handle_noplt_attribute, false, NULL },
{ "ifunc", 1, 1, true, false, false,
- handle_ifunc_attribute, false },
+ handle_ifunc_attribute, false, NULL },
{ "alias", 1, 1, true, false, false,
- handle_alias_attribute, false },
+ handle_alias_attribute, false, NULL },
{ "weakref", 0, 1, true, false, false,
- handle_weakref_attribute, false },
+ handle_weakref_attribute, false, NULL },
{ "no_instrument_function", 0, 0, true, false, false,
handle_no_instrument_function_attribute,
- false },
+ false, NULL },
{ "no_profile_instrument_function", 0, 0, true, false, false,
handle_no_profile_instrument_function_attribute,
- false },
+ false, NULL },
{ "malloc", 0, 0, true, false, false,
- handle_malloc_attribute, false },
+ handle_malloc_attribute, false,
+ attr_alloc_exclusions },
{ "returns_twice", 0, 0, true, false, false,
- handle_returns_twice_attribute, false },
+ handle_returns_twice_attribute, false,
+ attr_returns_twice_exclusions },
{ "no_stack_limit", 0, 0, true, false, false,
- handle_no_limit_stack_attribute, false },
+ handle_no_limit_stack_attribute, false, NULL },
{ "pure", 0, 0, true, false, false,
- handle_pure_attribute, false },
+ handle_pure_attribute, false,
+ attr_const_pure_exclusions },
{ "transaction_callable", 0, 0, false, true, false,
- handle_tm_attribute, false },
+ handle_tm_attribute, false, NULL },
{ "transaction_unsafe", 0, 0, false, true, false,
- handle_tm_attribute, true },
+ handle_tm_attribute, true, NULL },
{ "transaction_safe", 0, 0, false, true, false,
- handle_tm_attribute, true },
+ handle_tm_attribute, true, NULL },
{ "transaction_safe_dynamic", 0, 0, true, false, false,
- handle_tm_attribute, false },
+ handle_tm_attribute, false, NULL },
{ "transaction_may_cancel_outer", 0, 0, false, true, false,
- handle_tm_attribute, false },
+ handle_tm_attribute, false, NULL },
/* ??? These two attributes didn't make the transition from the
Intel language document to the multi-vendor language document. */
{ "transaction_pure", 0, 0, false, true, false,
- handle_tm_attribute, false },
+ handle_tm_attribute, false, NULL },
{ "transaction_wrap", 1, 1, true, false, false,
- handle_tm_wrap_attribute, false },
+ handle_tm_wrap_attribute, false, NULL },
/* For internal use (marking of builtins) only. The name contains space
to prevent its usage in source code. */
{ "no vops", 0, 0, true, false, false,
- handle_novops_attribute, false },
+ handle_novops_attribute, false, NULL },
{ "deprecated", 0, 1, false, false, false,
- handle_deprecated_attribute, false },
+ handle_deprecated_attribute, false, NULL },
{ "vector_size", 1, 1, false, true, false,
- handle_vector_size_attribute, true },
+ handle_vector_size_attribute, true, NULL },
{ "visibility", 1, 1, false, false, false,
- handle_visibility_attribute, false },
+ handle_visibility_attribute, false, NULL },
{ "tls_model", 1, 1, true, false, false,
- handle_tls_model_attribute, false },
+ handle_tls_model_attribute, false, NULL },
{ "nonnull", 0, -1, false, true, true,
- handle_nonnull_attribute, false },
+ handle_nonnull_attribute, false, NULL },
{ "nothrow", 0, 0, true, false, false,
- handle_nothrow_attribute, false },
- { "may_alias", 0, 0, false, true, false, NULL, false },
+ handle_nothrow_attribute, false, NULL },
+ { "may_alias", 0, 0, false, true, false, NULL, false, NULL },
{ "cleanup", 1, 1, true, false, false,
- handle_cleanup_attribute, false },
+ handle_cleanup_attribute, false, NULL },
{ "warn_unused_result", 0, 0, false, true, true,
- handle_warn_unused_result_attribute, false },
+ handle_warn_unused_result_attribute, false,
+ attr_warn_unused_result_exclusions },
{ "sentinel", 0, 1, false, true, true,
- handle_sentinel_attribute, false },
+ handle_sentinel_attribute, false, NULL },
/* For internal use (marking of builtins) only. The name contains space
to prevent its usage in source code. */
{ "type generic", 0, 0, false, true, true,
- handle_type_generic_attribute, false },
+ handle_type_generic_attribute, false, NULL },
{ "alloc_size", 1, 2, false, true, true,
- handle_alloc_size_attribute, false },
+ handle_alloc_size_attribute, false,
+ attr_alloc_exclusions },
{ "cold", 0, 0, true, false, false,
- handle_cold_attribute, false },
+ handle_cold_attribute, false,
+ attr_cold_hot_exclusions },
{ "hot", 0, 0, true, false, false,
- handle_hot_attribute, false },
+ handle_hot_attribute, false,
+ attr_cold_hot_exclusions },
{ "no_address_safety_analysis",
0, 0, true, false, false,
handle_no_address_safety_analysis_attribute,
- false },
+ false, NULL },
{ "no_sanitize", 1, 1, true, false, false,
handle_no_sanitize_attribute,
- false },
+ false, NULL },
{ "no_sanitize_address", 0, 0, true, false, false,
handle_no_sanitize_address_attribute,
- false },
+ false, NULL },
{ "no_sanitize_thread", 0, 0, true, false, false,
handle_no_sanitize_thread_attribute,
- false },
+ false, NULL },
{ "no_sanitize_undefined", 0, 0, true, false, false,
handle_no_sanitize_undefined_attribute,
- false },
+ false, NULL },
{ "asan odr indicator", 0, 0, true, false, false,
handle_asan_odr_indicator_attribute,
- false },
+ false, NULL },
{ "warning", 1, 1, true, false, false,
- handle_error_attribute, false },
+ handle_error_attribute, false, NULL },
{ "error", 1, 1, true, false, false,
- handle_error_attribute, false },
+ handle_error_attribute, false, NULL },
{ "target", 1, -1, true, false, false,
- handle_target_attribute, false },
+ handle_target_attribute, false, NULL },
{ "target_clones", 1, -1, true, false, false,
- handle_target_clones_attribute, false },
+ handle_target_clones_attribute, false, NULL },
{ "optimize", 1, -1, true, false, false,
- handle_optimize_attribute, false },
+ handle_optimize_attribute, false, NULL },
/* For internal use only. The leading '*' both prevents its usage in
source code and signals that it may be overridden by machine tables. */
{ "*tm regparm", 0, 0, false, true, true,
- ignore_attribute, false },
+ ignore_attribute, false, NULL },
{ "no_split_stack", 0, 0, true, false, false,
- handle_no_split_stack_attribute, false },
+ handle_no_split_stack_attribute, false, NULL },
/* For internal use (marking of builtins and runtime functions) only.
The name contains space to prevent its usage in source code. */
{ "fn spec", 1, 1, false, true, true,
- handle_fnspec_attribute, false },
+ handle_fnspec_attribute, false, NULL },
{ "warn_unused", 0, 0, false, false, false,
- handle_warn_unused_attribute, false },
+ handle_warn_unused_attribute, false, NULL },
{ "returns_nonnull", 0, 0, false, true, true,
- handle_returns_nonnull_attribute, false },
+ handle_returns_nonnull_attribute, false, NULL },
{ "omp declare simd", 0, -1, true, false, false,
- handle_omp_declare_simd_attribute, false },
+ handle_omp_declare_simd_attribute, false, NULL },
{ "cilk simd function", 0, -1, true, false, false,
- handle_omp_declare_simd_attribute, false },
+ handle_omp_declare_simd_attribute, false, NULL },
{ "simd", 0, 1, true, false, false,
- handle_simd_attribute, false },
+ handle_simd_attribute, false, NULL },
{ "omp declare target", 0, 0, true, false, false,
- handle_omp_declare_target_attribute, false },
+ handle_omp_declare_target_attribute, false,
+ NULL },
{ "omp declare target link", 0, 0, true, false, false,
- handle_omp_declare_target_attribute, false },
+ handle_omp_declare_target_attribute, false,
+ NULL },
{ "alloc_align", 1, 1, false, true, true,
- handle_alloc_align_attribute, false },
+ handle_alloc_align_attribute, false,
+ attr_alloc_exclusions },
{ "assume_aligned", 1, 2, false, true, true,
- handle_assume_aligned_attribute, false },
+ handle_assume_aligned_attribute, false, NULL },
{ "designated_init", 0, 0, false, true, false,
- handle_designated_init_attribute, false },
+ handle_designated_init_attribute, false, NULL },
{ "bnd_variable_size", 0, 0, true, false, false,
- handle_bnd_variable_size_attribute, false },
+ handle_bnd_variable_size_attribute, false, NULL },
{ "bnd_legacy", 0, 0, true, false, false,
- handle_bnd_legacy, false },
+ handle_bnd_legacy, false, NULL },
{ "bnd_instrument", 0, 0, true, false, false,
- handle_bnd_instrument, false },
+ handle_bnd_instrument, false, NULL },
{ "fallthrough", 0, 0, false, false, false,
- handle_fallthrough_attribute, false },
+ handle_fallthrough_attribute, false, NULL },
{ "patchable_function_entry", 1, 2, true, false, false,
handle_patchable_function_entry_attribute,
- false },
- { NULL, 0, 0, false, false, false, NULL, false }
+ false, NULL },
+ { NULL, 0, 0, false, false, false, NULL, false, NULL }
};
/* Give the specifications for the format attributes, used by C and all
@@ -374,10 +482,10 @@ const struct attribute_spec c_common_format_attribute_table[] =
/* { name, min_len, max_len, decl_req, type_req, fn_type_req, handler,
affects_type_identity } */
{ "format", 3, 3, false, true, true,
- handle_format_attribute, false },
+ handle_format_attribute, false, NULL },
{ "format_arg", 1, 1, false, true, true,
- handle_format_arg_attribute, false },
- { NULL, 0, 0, false, false, false, NULL, false }
+ handle_format_arg_attribute, false, NULL },
+ { NULL, 0, 0, false, false, false, NULL, false, NULL }
};
/* Returns TRUE iff the attribute indicated by ATTR_ID takes a plain
@@ -515,14 +623,7 @@ handle_hot_attribute (tree *node, tree name, tree ARG_UNUSED (args),
if (TREE_CODE (*node) == FUNCTION_DECL
|| TREE_CODE (*node) == LABEL_DECL)
{
- if (lookup_attribute ("cold", DECL_ATTRIBUTES (*node)) != NULL)
- {
- warning (OPT_Wattributes, "%qE attribute ignored due to conflict "
- "with attribute %qs", name, "cold");
- *no_add_attrs = true;
- }
- /* Most of the rest of the hot processing is done later with
- lookup_attribute. */
+ /* Attribute hot processing is done later with lookup_attribute. */
}
else
{
@@ -543,14 +644,7 @@ handle_cold_attribute (tree *node, tree name, tree ARG_UNUSED (args),
if (TREE_CODE (*node) == FUNCTION_DECL
|| TREE_CODE (*node) == LABEL_DECL)
{
- if (lookup_attribute ("hot", DECL_ATTRIBUTES (*node)) != NULL)
- {
- warning (OPT_Wattributes, "%qE attribute ignored due to conflict "
- "with attribute %qs", name, "hot");
- *no_add_attrs = true;
- }
- /* Most of the rest of the cold processing is done later with
- lookup_attribute. */
+ /* Attribute cold processing is done later with lookup_attribute. */
}
else
{
@@ -1064,7 +1158,7 @@ handle_no_reorder_attribute (tree *pnode,
static tree
handle_const_attribute (tree *node, tree name, tree ARG_UNUSED (args),
- int ARG_UNUSED (flags), bool *no_add_attrs)
+ int flags, bool *no_add_attrs)
{
tree type = TREE_TYPE (*node);
@@ -1085,6 +1179,14 @@ handle_const_attribute (tree *node, tree name, tree ARG_UNUSED (args),
*no_add_attrs = true;
}
+ /* void __builtin_unreachable(void) is const. Accept other such
+ built-ins but warn on user-defined functions that return void. */
+ if (!(flags & ATTR_FLAG_BUILT_IN)
+ && TREE_CODE (*node) == FUNCTION_DECL
+ && VOID_TYPE_P (TREE_TYPE (type)))
+ warning (OPT_Wattributes, "%qE attribute on function "
+ "returning %<void%>", name);
+
return NULL_TREE;
}
@@ -1667,14 +1769,18 @@ check_cxx_fundamental_alignment_constraints (tree node,
struct attribute_spec.handler. */
static tree
-handle_aligned_attribute (tree *node, tree ARG_UNUSED (name), tree args,
+handle_aligned_attribute (tree *node, tree name, tree args,
int flags, bool *no_add_attrs)
{
tree decl = NULL_TREE;
tree *type = NULL;
- int is_type = 0;
+ bool is_type = false;
tree align_expr;
- int i;
+
+ /* The last (already pushed) declaration with all validated attributes
+ merged in or the current about-to-be-pushed one if one hassn't been
+ yet. */
+ tree last_decl = node[1] ? node[1] : *node;
if (args)
{
@@ -1693,10 +1799,21 @@ handle_aligned_attribute (tree *node, tree ARG_UNUSED (name), tree args,
is_type = TREE_CODE (*node) == TYPE_DECL;
}
else if (TYPE_P (*node))
- type = node, is_type = 1;
+ type = node, is_type = true;
+
+ /* Log2 of specified alignment. */
+ int pow2align = check_user_alignment (align_expr, true);
+
+ /* The alignment in bits corresponding to the specified alignment. */
+ unsigned bitalign = (1U << pow2align) * BITS_PER_UNIT;
+
+ /* The alignment of the current declaration and that of the last
+ pushed declaration, determined on demand below. */
+ unsigned curalign = 0;
+ unsigned lastalign = 0;
- if ((i = check_user_alignment (align_expr, true)) == -1
- || !check_cxx_fundamental_alignment_constraints (*node, i, flags))
+ if (pow2align == -1
+ || !check_cxx_fundamental_alignment_constraints (*node, pow2align, flags))
*no_add_attrs = true;
else if (is_type)
{
@@ -1717,7 +1834,7 @@ handle_aligned_attribute (tree *node, tree ARG_UNUSED (name), tree args,
else
*type = build_variant_type_copy (*type);
- SET_TYPE_ALIGN (*type, (1U << i) * BITS_PER_UNIT);
+ SET_TYPE_ALIGN (*type, bitalign);
TYPE_USER_ALIGN (*type) = 1;
}
else if (! VAR_OR_FUNCTION_DECL_P (decl)
@@ -1726,8 +1843,34 @@ handle_aligned_attribute (tree *node, tree ARG_UNUSED (name), tree args,
error ("alignment may not be specified for %q+D", decl);
*no_add_attrs = true;
}
+ else if (TREE_CODE (decl) == FUNCTION_DECL
+ && ((curalign = DECL_ALIGN (decl)) > bitalign
+ || ((lastalign = DECL_ALIGN (last_decl)) > bitalign)))
+ {
+ /* Either a prior attribute on the same declaration or one
+ on a prior declaration of the same function specifies
+ stricter alignment than this attribute. */
+ bool note = lastalign != 0;
+ if (lastalign)
+ curalign = lastalign;
+
+ curalign /= BITS_PER_UNIT;
+ bitalign /= BITS_PER_UNIT;
+
+ if (DECL_USER_ALIGN (decl) || DECL_USER_ALIGN (last_decl))
+ warning (OPT_Wattributes,
+ "ignoring attribute %<%E (%u)%> because it conflicts with "
+ "attribute %<%E (%u)%>", name, bitalign, name, curalign);
+ else
+ error ("alignment for %q+D must be at least %d", decl, curalign);
+
+ if (note)
+ inform (DECL_SOURCE_LOCATION (last_decl), "previous declaration here");
+
+ *no_add_attrs = true;
+ }
else if (DECL_USER_ALIGN (decl)
- && DECL_ALIGN (decl) > (1U << i) * BITS_PER_UNIT)
+ && DECL_ALIGN (decl) > bitalign)
/* C++-11 [dcl.align/4]:
When multiple alignment-specifiers are specified for an
@@ -1737,21 +1880,9 @@ handle_aligned_attribute (tree *node, tree ARG_UNUSED (name), tree args,
This formally comes from the c++11 specification but we are
doing it for the GNU attribute syntax as well. */
*no_add_attrs = true;
- else if (TREE_CODE (decl) == FUNCTION_DECL
- && DECL_ALIGN (decl) > (1U << i) * BITS_PER_UNIT)
- {
- 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
{
- SET_DECL_ALIGN (decl, (1U << i) * BITS_PER_UNIT);
+ SET_DECL_ALIGN (decl, bitalign);
DECL_USER_ALIGN (decl) = 1;
}
@@ -2476,8 +2607,15 @@ handle_pure_attribute (tree *node, tree name, tree ARG_UNUSED (args),
int ARG_UNUSED (flags), bool *no_add_attrs)
{
if (TREE_CODE (*node) == FUNCTION_DECL)
- DECL_PURE_P (*node) = 1;
- /* ??? TODO: Support types. */
+ {
+ tree type = TREE_TYPE (*node);
+ if (VOID_TYPE_P (TREE_TYPE (type)))
+ warning (OPT_Wattributes, "%qE attribute on function "
+ "returning %<void%>", name);
+
+ DECL_PURE_P (*node) = 1;
+ /* ??? TODO: Support types. */
+ }
else
{
warning (OPT_Wattributes, "%qE attribute ignored", name);
@@ -2143,36 +2143,19 @@ diagnose_mismatched_attributes (tree olddecl, tree newdecl)
newdecl);
/* Diagnose inline __attribute__ ((noinline)) which is silly. */
+ const char* noinline = "noinline";
+
if (DECL_DECLARED_INLINE_P (newdecl)
&& DECL_UNINLINABLE (olddecl)
- && lookup_attribute ("noinline", DECL_ATTRIBUTES (olddecl)))
+ && lookup_attribute (noinline, DECL_ATTRIBUTES (olddecl)))
warned |= warning (OPT_Wattributes, "inline declaration of %qD follows "
- "declaration with attribute noinline", newdecl);
+ "declaration with attribute %qs", newdecl, noinline);
else if (DECL_DECLARED_INLINE_P (olddecl)
&& DECL_UNINLINABLE (newdecl)
&& lookup_attribute ("noinline", DECL_ATTRIBUTES (newdecl)))
warned |= warning (OPT_Wattributes, "declaration of %q+D with attribute "
- "noinline follows inline declaration ", newdecl);
- else if (lookup_attribute ("noinline", DECL_ATTRIBUTES (newdecl))
- && lookup_attribute ("always_inline", DECL_ATTRIBUTES (olddecl)))
- warned |= warning (OPT_Wattributes, "declaration of %q+D with attribute "
- "%qs follows declaration with attribute %qs",
- newdecl, "noinline", "always_inline");
- else if (lookup_attribute ("always_inline", DECL_ATTRIBUTES (newdecl))
- && lookup_attribute ("noinline", DECL_ATTRIBUTES (olddecl)))
- warned |= warning (OPT_Wattributes, "declaration of %q+D with attribute "
- "%qs follows declaration with attribute %qs",
- newdecl, "always_inline", "noinline");
- else if (lookup_attribute ("cold", DECL_ATTRIBUTES (newdecl))
- && lookup_attribute ("hot", DECL_ATTRIBUTES (olddecl)))
- warned |= warning (OPT_Wattributes, "declaration of %q+D with attribute "
- "%qs follows declaration with attribute %qs",
- newdecl, "cold", "hot");
- else if (lookup_attribute ("hot", DECL_ATTRIBUTES (newdecl))
- && lookup_attribute ("cold", DECL_ATTRIBUTES (olddecl)))
- warned |= warning (OPT_Wattributes, "declaration of %q+D with attribute "
- "%qs follows declaration with attribute %qs",
- newdecl, "hot", "cold");
+ "%qs follows inline declaration ", newdecl, noinline);
+
return warned;
}
@@ -4589,7 +4589,16 @@ c_decl_attributes (tree *node, tree attributes, int flags)
attributes = tree_cons (get_identifier ("omp declare target"),
NULL_TREE, attributes);
}
- return decl_attributes (node, attributes, flags);
+
+ /* Look up the current declaration with all the attributes merged
+ so far so that attributes on the current declaration that's
+ about to be pushed that conflict with the former can be detected,
+ diagnosed, and rejected as appropriate. */
+ tree last_decl = lookup_name (DECL_NAME (*node));
+ if (!last_decl)
+ last_decl = lookup_name_in_scope (DECL_NAME (*node), external_scope);
+
+ return decl_attributes (node, attributes, flags, last_decl);
}
@@ -6080,7 +6080,7 @@ extern void finish_scope (void);
extern void push_switch (tree);
extern void pop_switch (void);
extern tree make_lambda_name (void);
-extern int decls_match (tree, tree);
+extern int decls_match (tree, tree, bool = true);
extern tree duplicate_decls (tree, tree, bool);
extern tree declare_local_label (tree);
extern tree define_label (location_t, tree);
@@ -993,7 +993,7 @@ push_local_name (tree decl)
`const int&'. */
int
-decls_match (tree newdecl, tree olddecl)
+decls_match (tree newdecl, tree olddecl, bool record_versions /* = true */)
{
int types_match;
@@ -1088,6 +1088,7 @@ decls_match (tree newdecl, tree olddecl)
if (types_match
&& !DECL_EXTERN_C_P (newdecl)
&& !DECL_EXTERN_C_P (olddecl)
+ && record_versions
&& targetm.target_option.function_versions (newdecl, olddecl))
{
/* Mark functions as versions if necessary. Modify the mangled decl
@@ -1404,7 +1404,10 @@ cplus_decl_attributes (tree *decl, tree attributes, int flags)
attributes, flags);
}
else
- decl_attributes (decl, attributes, flags);
+ {
+ tree last_decl = find_decl (*decl);
+ decl_attributes (decl, attributes, flags, last_decl);
+ }
if (TREE_CODE (*decl) == TYPE_DECL)
SET_IDENTIFIER_TYPE_VALUE (DECL_NAME (*decl), TREE_TYPE (*decl));
@@ -2291,6 +2291,71 @@ set_local_extern_decl_linkage (tree decl, bool shadowed)
}
}
+/* Given a new DECL that hasn't been pushed yet, try to find the last
+ DECL for the same symbol and return it, oterwise return null. */
+
+tree
+find_decl (tree decl)
+{
+ if (!DECL_P (decl))
+ return NULL_TREE;
+
+ /* if (!DECL_TEMPLATE_PARM_P (decl) && current_function_decl) */
+ /* set_decl_context_in_fn (current_function_decl, decl); */
+
+ /* The binding level we will be pushing into. During local class
+ pushing, we want to push to the containing scope. */
+ cp_binding_level *level = current_binding_level;
+ while (level->kind == sk_class)
+ level = level->level_chain;
+
+ tree name = DECL_NAME (decl);
+ if (!name)
+ return NULL_TREE;
+
+ cxx_binding *binding = NULL; /* Local scope binding. */
+ tree ns = NULL_TREE; /* Searched namespace. */
+ tree *slot = NULL; /* Binding slot in namespace. */
+ tree old = NULL_TREE;
+
+ if (level->kind == sk_namespace)
+ {
+ /* We look in the decl's namespace for an existing
+ declaration, even though we push into the current
+ namespace. */
+ ns = (DECL_NAMESPACE_SCOPE_P (decl)
+ ? CP_DECL_CONTEXT (decl) : current_namespace);
+ /* Create the binding, if this is current namespace, because
+ that's where we'll be pushing anyway. */
+ slot = find_namespace_slot (ns, name, ns == current_namespace);
+ if (slot)
+ old = MAYBE_STAT_DECL (*slot);
+ }
+ else
+ {
+ binding = find_local_binding (level, name);
+ if (binding)
+ old = binding->value;
+ }
+
+ /* if (current_function_decl && VAR_OR_FUNCTION_DECL_P (decl) */
+ /* && DECL_EXTERNAL (decl)) */
+ /* set_local_extern_decl_linkage (decl, old != NULL_TREE); */
+
+ if (old == error_mark_node)
+ old = NULL_TREE;
+
+ for (ovl_iterator iter (old); iter; ++iter)
+ {
+ if (iter.using_p ())
+ ; /* Ignore using decls here. */
+ else if (decls_match (decl, *iter, /*record_decls=*/false))
+ return *iter;
+ }
+
+ return NULL_TREE;
+}
+
/* Record DECL as belonging to the current lexical scope. Check for
errors (such as an incompatible declaration for the same name
already seen in the same scope). IS_FRIEND is true if DECL is
@@ -339,4 +339,6 @@ extern void pop_nested_namespace (tree);
extern void push_to_top_level (void);
extern void pop_from_top_level (void);
+extern tree find_decl (tree);
+
#endif /* GCC_CP_NAME_LOOKUP_H */
@@ -4314,10 +4314,10 @@ const struct attribute_spec cxx_attribute_table[] =
/* { name, min_len, max_len, decl_req, type_req, fn_type_req, handler,
affects_type_identity } */
{ "init_priority", 1, 1, true, false, false,
- handle_init_priority_attribute, false },
+ handle_init_priority_attribute, false, NULL },
{ "abi_tag", 1, -1, false, false, false,
- handle_abi_tag_attribute, true },
- { NULL, 0, 0, false, false, false, NULL, false }
+ handle_abi_tag_attribute, true, NULL },
+ { NULL, 0, 0, false, false, false, NULL, false, NULL }
};
/* Table of C++ standard attributes. */
@@ -4326,10 +4326,10 @@ const struct attribute_spec std_attribute_table[] =
/* { name, min_len, max_len, decl_req, type_req, fn_type_req, handler,
affects_type_identity } */
{ "maybe_unused", 0, 0, false, false, false,
- handle_unused_attribute, false },
+ handle_unused_attribute, false, NULL },
{ "nodiscard", 0, 0, false, false, false,
- handle_nodiscard_attribute, false },
- { NULL, 0, 0, false, false, false, NULL, false }
+ handle_nodiscard_attribute, false, NULL },
+ { NULL, 0, 0, false, false, false, NULL, false, NULL }
};
/* Handle an "init_priority" attribute; arguments as in
@@ -1933,6 +1933,20 @@ struct attribute_spec {
int flags, bool *no_add_attrs);
/* Specifies if attribute affects type's identity. */
bool affects_type_identity;
+
+ /* Specifies the name of an attribute that's mutually exclusive with
+ this one, and whether the relationship applies to the function,
+ variable, or type form of the attribute. */
+ struct exclusions {
+ const char* name;
+ bool function;
+ bool variable;
+ bool type;
+ };
+
+ /* An array of attribute exclusions describing names of other attributes
+ that this attribute is mutually exclusive with. */
+ const exclusions* exclude;
};
/* These functions allow a front-end to perform a manual layout of a
@@ -355,7 +355,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
}
// XXX GLIBCXX_ABI Deprecated
- _GLIBCXX_CONST void
+ void
_M_destroy_thread_key(void*) throw ();
size_t
new file mode 100644
@@ -0,0 +1,74 @@
+/* PR c/81566 - invalid attribute aligned accepted on functions
+ { dg-do compile }
+ { dg-options "-Wall -Wattributes -ftrack-macro-expansion=0" } */
+
+#define ATTR(list) __attribute__ (list)
+#define ALIGN(n) ATTR ((aligned (n)))
+
+/* It's okay to increase the alignment of a function. */
+
+void ALIGN (16) ALIGN (32)
+falign32_1 (void);
+
+void ALIGN (16) falign32_2 (void);
+void ALIGN (32) falign32_2 (void);
+
+void falign32_2 (void) { }
+
+void ALIGN (32) falign32_2 (void);
+
+/* It's not okay to decrease it. */
+
+void ALIGN (32) ALIGN (16)
+falign64_3 (void); /* { dg-warning "ignoring attribute .aligned \\(16\\). because it conflicts with attribute .aligned \\(32\\)." } */
+
+void ALIGN (32)
+falign64_3 (void);
+
+void falign64_3 (void);
+
+void falign64_3 (void) { }
+
+
+void ALIGN (32)
+falign64_4 (void); /* { dg-message "previous declaration here" } */
+
+void ALIGN (16)
+falign64_4 (void); /* { dg-warning "ignoring attribute .aligned \\(16\\). because it conflicts with attribute .aligned \\(32\\)." } */
+
+void ALIGN (32)
+falign64_4 (void); /* { dg-message "previous declaration here" } */
+
+void ALIGN (16)
+falign64_4 (void); /* { dg-warning "ignoring attribute .aligned \\(16\\). because it conflicts with attribute .aligned \\(32\\)." } */
+
+void ALIGN (64)
+falign64_4 (void);
+
+void ALIGN (32)
+falign64_4 (void); /* { dg-warning "ignoring attribute .aligned \\(32\\). because it conflicts with attribute .aligned \\(64\\)." } */
+
+void falign64_4 (void);
+
+void ALIGN (64)
+falign64_4 (void) { }
+
+void falign64_4 (void);
+
+void ALIGN (64)
+falign64_4 (void);
+
+
+void ATTR ((aligned (16), aligned (32)))
+falign64_5 (void);
+
+void ATTR ((aligned (32), aligned (64)))
+falign64_5 (void);
+
+void ATTR ((aligned (16), aligned (32), aligned (64)))
+falign64_5 (void); /* { dg-warning "ignoring attribute .aligned \\(16\\). because it conflicts with attribute .aligned \\(64\\)." } */
+ /* { dg-warning "ignoring attribute .aligned \\(32\\). because it conflicts with attribute .aligned \\(64\\)." "" { target *-*-* } .-1 } */
+
+
+void ATTR ((aligned (16), aligned (32), aligned (16)))
+falign64_6 (void); /* { dg-warning "ignoring attribute .aligned \\(16\\). because it conflicts with attribute .aligned \\(32\\)." } */
new file mode 100644
@@ -0,0 +1,437 @@
+/* { dg-do compile }
+ { dg-options "-Wall -Wattributes -ftrack-macro-expansion=0" } */
+
+#define ATTR(attrlist) __attribute__ (attrlist)
+
+/* Exercise the handling of the mutually exclusive attributes
+ aligned and packed. */
+
+/* Pointless but benign. */
+struct ATTR ((aligned, aligned))
+AlignedAligned { int i; };
+
+struct ATTR ((aligned, packed))
+AlignedPacked { int i; }; /* { dg-warning "ignoring attribute .packed. because it conflicts with attribute .aligned." } */
+
+struct ATTR ((packed, aligned))
+PackedAligned { int i; }; /* { dg-warning "ignoring attribute .aligned. because it conflicts with attribute .packed." } */
+
+/* Silly but benign. */
+struct ATTR ((packed, packed))
+PackedPacked { int i; };
+
+
+/* Exercise the handling of the mutually exclusive attributes
+ always_inline and noinline. */
+
+inline void ATTR ((always_inline))
+falways_inline1 (void);
+
+inline void ATTR ((__always_inline__))
+falways_inline1 (void);
+
+inline void ATTR ((always_inline, __always_inline__))
+falways_inline1 (void);
+
+/* Verify that repeating attribute always_inline doesn't trigger a warning. */
+inline void ATTR ((always_inline))
+falways_inline1 (void); /* { dg-message "previous declaration here" } */
+
+void ATTR ((noinline))
+falways_inline1 (void) { } /* { dg-warning "ignoring attribute .noinline. because it conflicts with attribute .always_inline." } */
+
+/* And again. */
+void ATTR ((always_inline))
+falways_inline (void);
+
+
+/* Exercise the handling of the mutually exclusive attributes
+ noreturn and warn_unused_result. */
+
+int ATTR ((__noreturn__))
+fnoret1 (void);
+
+int ATTR ((noreturn))
+fnoret1 (void); /* { dg-message "previous declaration here" } */
+
+int ATTR ((warn_unused_result))
+fnoret1 (void); /* { dg-warning "ignoring attribute .warn_unused_result. because it conflicts with attribute .noreturn." } */
+
+/* Verify that repeating attribute noreturn doesn't trigger a warning. */
+int ATTR ((noreturn)) fnoret1 (void);
+
+int call_noret1 (void)
+{
+ /* Verify that attribute warn_unused_result was, in fact, ignored
+ on the second declaration of fnoret1. */
+ fnoret1 ();
+}
+
+int ATTR ((noreturn, warn_unused_result))
+fnoret2 (void); /* { dg-warning "ignoring attribute .warn_unused_result. because it conflicts with attribute .noreturn." } */
+
+/* Verify that repeating attribute noreturn doesn't trigger a warning. */
+int ATTR ((noreturn)) fnoret2 (void);
+
+int call_noret2 (void)
+{
+ /* Verify that attribute warn_unused_result was, in fact, ignored
+ on the second declaration of fnoret2. */
+ fnoret2 ();
+}
+
+/* Verify again but this time in different scopes. */
+
+int ATTR ((noreturn))
+fnoret3 (void); /* { dg-message "previous declaration here" } */
+
+void declare_noret3 (void)
+{
+ int ATTR ((warn_unused_result))
+ fnoret3 (void); /* { dg-warning "ignoring attribute .warn_unused_result. because it conflicts with attribute .noreturn." } */
+}
+
+void declare_noret4 (void)
+{
+ int ATTR ((warn_unused_result))
+ fnoret4 (void); /* { dg-message "previous declaration here" } */
+}
+
+int ATTR ((noreturn))
+fnoret4 (void); /* { dg-warning "ignoring attribute .noreturn. because it conflicts with attribute .warn_unused_result." } */
+
+
+/* And again, but with both declared in a different local scope. */
+
+void declare_noret5_1 (void)
+{
+ int ATTR ((noreturn))
+ fnoret5 (void); /* { dg-message "previous declaration here" } */
+}
+
+int declare_noret5_2 (void)
+{
+ int ATTR ((warn_unused_result))
+ fnoret5 (void); /* { dg-warning "ignoring attribute .warn_unused_result. because it conflicts with attribute .noreturn." } */
+
+ /* Verify that no warning is issued below (because the warn_unused_result
+ attribute above was dropped). */
+ fnoret5 ();
+}
+
+/* Verify that attribute noreturn isn't diagnosed on a declaration
+ that was previously declared warn_unused_result and that attribute
+ was dropped (because the function returs void). */
+
+void ATTR ((warn_unused_result))
+fnorety6 (void); /* { dg-warning ".warn_unused_result. attribute ignored" } */
+
+void ATTR ((noreturn))
+fnoret6 (void);
+
+
+/* Exercise the handling of the mutually exclusive attributes
+ noreturn and alloc_align. */
+
+void* ATTR ((noreturn))
+fnoret_alloc_align1 (int); /* { dg-message "previous declaration here" } */
+
+void* ATTR ((alloc_align (1)))
+fnoret_alloc_align1 (int); /* { dg-warning "ignoring attribute .alloc_align. because it conflicts with attribute .noreturn." } */
+
+void* ATTR ((noreturn, alloc_align (1)))
+fnoret_alloc_align2 (int); /* { dg-warning "ignoring attribute .alloc_align. because it conflicts with attribute .noreturn." } */
+
+
+void* ATTR ((noreturn)) ATTR ((alloc_align (1)))
+fnoret_alloc_align3 (int); /* { dg-warning "ignoring attribute .alloc_align. because it conflicts with attribute .noreturn." } */
+
+
+void* ATTR ((alloc_align (1)))
+falloc_align_noret1 (int); /* { dg-message "previous declaration here" } */
+
+void* ATTR ((noreturn))
+falloc_align_noret1 (int); /* { dg-warning "ignoring attribute .noreturn. because it conflicts with attribute .alloc_align." } */
+
+
+void* ATTR ((alloc_align (1), noreturn))
+falloc_align_noret2 (int); /* { dg-warning "ignoring attribute .(noreturn|alloc_align). because it conflicts with attribute .(alloc_align|noreturn)." } */
+
+void* ATTR ((alloc_align (1))) ATTR ((noreturn))
+falloc_align_noret3 (int); /* { dg-warning "ignoring attribute .(noreturn|alloc_align). because it conflicts with attribute .(noreturn|alloc_align)." } */
+
+
+/* Exercise the handling of the mutually exclusive attributes
+ noreturn and alloc_size. */
+
+void* ATTR ((noreturn))
+fnoret_alloc_size1 (int); /* { dg-message "previous declaration here" } */
+
+void* ATTR ((alloc_size (1)))
+fnoret_alloc_size1 (int); /* { dg-warning "ignoring attribute .alloc_size. because it conflicts with attribute .noreturn." } */
+
+void* ATTR ((noreturn, alloc_size (1)))
+fnoret_alloc_size2 (int); /* { dg-warning "ignoring attribute .alloc_size. because it conflicts with attribute .noreturn." } */
+
+
+void* ATTR ((noreturn)) ATTR ((alloc_size (1)))
+fnoret_alloc_size3 (int); /* { dg-warning "ignoring attribute .alloc_size. because it conflicts with attribute .noreturn." } */
+
+
+void* ATTR ((alloc_size (1)))
+falloc_size_noret1 (int); /* { dg-message "previous declaration here" } */
+
+void* ATTR ((noreturn))
+falloc_size_noret1 (int); /* { dg-warning "ignoring attribute .noreturn. because it conflicts with attribute .alloc_size." } */
+
+
+void* ATTR ((alloc_size (1), noreturn))
+falloc_size_noret2 (int); /* { dg-warning "ignoring attribute .noreturn. because it conflicts with attribute .alloc_size." } */
+
+void* ATTR ((alloc_size (1))) ATTR ((noreturn))
+falloc_size_noret3 (int); /* { dg-warning "ignoring attribute .noreturn. because it conflicts with attribute .alloc_size." } */
+
+
+/* Exercise the handling of the mutually exclusive attributes
+ noreturn and const. */
+
+int ATTR ((noreturn))
+fnoret_const1 (int); /* { dg-message "previous declaration here" } */
+
+int ATTR ((const))
+fnoret_const1 (int); /* { dg-warning "ignoring attribute .const. because it conflicts with attribute .noreturn." } */
+
+/* Unfortunately, attributes on a single declarations may not be processed
+ in the same order as specified... */
+int ATTR ((noreturn, const))
+fnoret_const2 (int); /* { dg-warning "ignoring attribute .const. because it conflicts with attribute .noreturn." } */
+
+
+int ATTR ((noreturn)) ATTR ((const))
+fnoret_const3 (int); /* { dg-warning "ignoring attribute .const. because it conflicts with attribute .noreturn." } */
+
+
+int ATTR ((const))
+fconst_noret1 (int); /* { dg-message "previous declaration here" } */
+
+int ATTR ((noreturn))
+fconst_noret1 (int); /* { dg-warning "ignoring attribute .noreturn. because it conflicts with attribute .const." } */
+
+
+int ATTR ((const, noreturn))
+fconst_noret2 (int); /* { dg-warning "ignoring attribute .noreturn. because it conflicts with attribute .const." } */
+
+int ATTR ((const)) ATTR ((noreturn))
+fconst_noret3 (int); /* { dg-warning "ignoring attribute .noreturn. because it conflicts with attribute .const." } */
+
+
+/* Exercise the handling of the mutually exclusive attributes
+ noreturn and malloc. */
+
+void* ATTR ((noreturn))
+fnoret_malloc1 (int); /* { dg-message "previous declaration here" } */
+
+void* ATTR ((malloc))
+fnoret_malloc1 (int); /* { dg-warning "ignoring attribute .malloc. because it conflicts with attribute .noreturn." } */
+
+/* Unfortunately, attributes on a single declarations may not be processed
+ in the same order as specified... */
+void* ATTR ((noreturn, malloc))
+fnoret_malloc2 (int); /* { dg-warning "ignoring attribute .malloc. because it conflicts with attribute .noreturn." } */
+
+
+void* ATTR ((noreturn)) ATTR ((malloc))
+fnoret_malloc3 (int); /* { dg-warning "ignoring attribute .malloc. because it conflicts with attribute .noreturn." } */
+
+
+void* ATTR ((__malloc__))
+fmalloc_noret1 (int);
+
+void* ATTR ((malloc))
+fmalloc_noret1 (int); /* { dg-message "previous declaration here" } */
+
+void* ATTR ((noreturn))
+fmalloc_noret1 (int); /* { dg-warning "ignoring attribute .noreturn. because it conflicts with attribute .malloc." } */
+
+
+void* ATTR ((malloc, noreturn))
+fmalloc_noret2 (int); /* { dg-warning "ignoring attribute .noreturn. because it conflicts with attribute .malloc." } */
+
+void* ATTR ((malloc)) ATTR ((noreturn))
+fmalloc_noret3 (int); /* { dg-warning "ignoring attribute .noreturn. because it conflicts with attribute .malloc." } */
+
+
+/* Exercise the handling of the mutually exclusive attributes
+ noreturn and pure. */
+
+int ATTR ((noreturn))
+fnoret_pure1 (int); /* { dg-message "previous declaration here" } */
+
+int ATTR ((pure))
+fnoret_pure1 (int); /* { dg-warning "ignoring attribute .pure. because it conflicts with attribute .noreturn." } */
+
+/* Unfortunately, attributes on a single declarations may not be processed
+ in the same order as specified... */
+int ATTR ((noreturn, pure))
+fnoret_pure2 (int); /* { dg-warning "ignoring attribute .pure. because it conflicts with attribute .noreturn." } */
+
+
+int ATTR ((noreturn)) ATTR ((pure))
+fnoret_pure3 (int); /* { dg-warning "ignoring attribute .pure. because it conflicts with attribute .noreturn." } */
+
+
+int ATTR ((__pure__))
+fpure_noret1 (int);
+
+int ATTR ((pure))
+fpure_noret1 (int); /* { dg-message "previous declaration here" } */
+
+int ATTR ((noreturn))
+fpure_noret1 (int); /* { dg-warning "ignoring attribute .noreturn. because it conflicts with attribute .pure." } */
+
+
+int ATTR ((pure, noreturn))
+fpure_noret2 (int); /* { dg-warning "ignoring attribute .noreturn. because it conflicts with attribute .pur." } */
+
+int ATTR ((pure)) ATTR ((noreturn))
+fpure_noret3 (int); /* { dg-warning "ignoring attribute .noreturn. because it conflicts with attribute .pure." } */
+
+
+/* Exercise the handling of the mutually exclusive attributes
+ noreturn and returns_twice. */
+
+int ATTR ((noreturn))
+fnoret_returns_twice1 (int); /* { dg-message "previous declaration here" } */
+
+int ATTR ((returns_twice))
+fnoret_returns_twice1 (int); /* { dg-warning "ignoring attribute .returns_twice. because it conflicts with attribute .noreturn." } */
+
+/* Unfortunately, attributes on a single declarations may not be processed
+ in the same order as specified... */
+int ATTR ((noreturn, returns_twice))
+fnoret_returns_twice2 (int); /* { dg-warning "ignoring attribute .returns_twice. because it conflicts with attribute .noreturn." } */
+
+
+int ATTR ((noreturn)) ATTR ((returns_twice))
+fnoret_returns_twice3 (int); /* { dg-warning "ignoring attribute .returns_twice. because it conflicts with attribute .noreturn." } */
+
+
+int ATTR ((returns_twice))
+freturns_twice_noret1 (int); /* { dg-message "previous declaration here" } */
+
+int ATTR ((noreturn))
+freturns_twice_noret1 (int); /* { dg-warning "ignoring attribute .noreturn. because it conflicts with attribute .returns_twice." } */
+
+
+int ATTR ((returns_twice, noreturn))
+freturns_twice_noret2 (int); /* { dg-warning "ignoring attribute .noreturn. because it conflicts with attribute .returns_twice." } */
+
+int ATTR ((returns_twice)) ATTR ((noreturn))
+freturns_twice_noret3 (int); /* { dg-warning "ignoring attribute .noreturn. because it conflicts with attribute .returns_twice." } */
+
+
+/* Exercise the interaction of multiple combinations of mutually
+ exclusive attributes specified on distinct declarations. */
+
+inline int ATTR ((always_inline))
+finline_cold_noreturn (int);
+
+inline int ATTR ((cold))
+finline_cold_noreturn (int);
+
+inline int ATTR ((noreturn))
+finline_cold_noreturn (int);
+
+inline int ATTR ((noinline))
+finline_cold_noreturn (int); /* { dg-warning "ignoring attribute .noinline. because it conflicts with attribute .always_inline." } */
+
+inline int ATTR ((hot))
+finline_cold_noreturn (int); /* { dg-warning "ignoring attribute .hot. because it conflicts with attribute .cold." } */
+
+inline int ATTR ((warn_unused_result))
+finline_cold_noreturn (int); /* { dg-warning "ignoring attribute .warn_unused_result. because it conflicts with attribute .noreturn." } */
+
+inline int ATTR ((always_inline))
+finline_cold_noreturn (int);
+
+/* Expect no warning for the missing return statement below because
+ the function is noreturn. */
+inline int ATTR ((noreturn))
+finline_cold_noreturn (int i) { (void)&i; __builtin_abort (); }
+
+
+/* Exercise the interaction of multiple combinations of mutually
+ exclusive attributes with some specified on the same declaration
+ and some on distinct declarations. */
+
+inline int ATTR ((always_inline, hot))
+finline_hot_noret_align (int);
+
+inline int ATTR ((noreturn, noinline))
+finline_hot_noret_align (int); /* { dg-warning "ignoring attribute .noinline. because it conflicts with attribute .always_inline." } */
+
+inline int ATTR ((cold, aligned (8)))
+finline_hot_noret_align (int); /* { dg-warning "ignoring attribute .cold. because it conflicts with attribute .hot." } */
+
+inline int ATTR ((warn_unused_result))
+finline_hot_noret_align (int); /* { dg-warning "ignoring attribute .warn_unused_result. because it conflicts with attribute .noreturn." } */
+
+inline int ATTR ((aligned (4)))
+finline_hot_noret_align (int); /* { dg-warning "ignoring attribute .aligned \\(4\\). because it conflicts with attribute .aligned \\(8\\)." } */
+
+inline int ATTR ((aligned (8)))
+finline_hot_noret_align (int);
+
+inline int ATTR ((const))
+finline_hot_noret_align (int); /* { dg-warning "ignoring attribute .const. because it conflicts with attribute .noreturn." } */
+
+/* Expect no warning for the missing return statement below because
+ the function is noreturn. */
+inline int ATTR ((noreturn))
+finline_hot_noret_align (int i) { (void)&i; __builtin_abort (); }
+
+
+/* Exercise variable attributes. */
+
+extern int ATTR ((common))
+decl_common1; /* { dg-message "previous declaration here" } */
+
+extern int ATTR ((nocommon))
+decl_common1; /* { dg-warning "ignoring attribute .nocommon. because it conflicts with attribute .common." } */
+
+
+extern int ATTR ((nocommon))
+decl_common2; /* { dg-message "previous declaration here" } */
+
+extern int ATTR ((common))
+decl_common2; /* { dg-warning "ignoring attribute .common. because it conflicts with attribute .nocommon." } */
+
+
+extern int ATTR ((common, nocommon))
+decl_common3; /* { dg-warning "ignoring attribute .nocommon. because it conflicts with attribute .common." } */
+
+
+extern int ATTR ((common, nocommon))
+decl_common4; /* { dg-warning "ignoring attribute .nocommon. because it conflicts with attribute .common." } */
+
+
+void declare_common5_in_local_scope (void)
+{
+ extern int ATTR ((common))
+ decl_common5; /* { dg-message "previous declaration here" } */
+ (void)&decl_common5;
+}
+
+extern int ATTR ((nocommon))
+decl_common5; /* { dg-warning "ignoring attribute .nocommon. because it conflicts with attribute .common." } */
+
+
+extern int ATTR ((nocommon))
+decl_common6; /* { dg-message "previous declaration here" } */
+
+void declare_common6_in_local_scope (void)
+{
+ extern int ATTR ((common))
+ decl_common6; /* { dg-warning "ignoring attribute .common. because it conflicts with attribute .nocommon." } */
+ (void)&decl_common6;
+}
@@ -12,16 +12,16 @@ extern __attribute__((noinline)) int fn1 (void); /* { dg-message "previous decla
extern inline int fn1 (void); /* { dg-warning "inline declaration of" } */
extern inline int fn2 (void); /* { dg-message "previous declaration" } */
-extern __attribute__((noinline)) int fn2 (void); /* { dg-warning "attribute noinline follows inline declaration" } */
+extern __attribute__((noinline)) int fn2 (void); /* { dg-warning "attribute .noinline. follows inline declaration" } */
extern __attribute__((always_inline)) int fn3 (void); /* { dg-message "previous declaration" } */
-extern __attribute__((noinline)) int fn3 (void); /* { dg-warning "attribute .noinline. follows declaration with attribute .always_inline." } */
+extern __attribute__((noinline)) int fn3 (void); /* { dg-warning "ignoring attribute .noinline. because it conflicts with attribute .always_inline." } */
extern __attribute__((noinline)) int fn4 (void); /* { dg-message "previous declaration" } */
-extern __attribute__((always_inline)) int fn4 (void); /* { dg-warning "attribute .always_inline. follows declaration with attribute .noinline." } */
+extern __attribute__((always_inline)) int fn4 (void); /* { dg-warning "ignoring attribute .always_inline. because it conflicts with attribute .noinline." } */
extern __attribute__((hot)) int fn5 (void); /* { dg-message "previous declaration" } */
-extern __attribute__((cold)) int fn5 (void); /* { dg-warning "attribute .cold. follows declaration with attribute .hot." } */
+extern __attribute__((cold)) int fn5 (void); /* { dg-warning "ignoring attribute .cold. because it conflicts with attribute .hot." } */
extern __attribute__((cold)) int fn6 (void); /* { dg-message "previous declaration" } */
-extern __attribute__((hot)) int fn6 (void); /* { dg-warning "attribute .hot. follows declaration with attribute .cold." } */
+extern __attribute__((hot)) int fn6 (void); /* { dg-warning "ignoring attribute .hot. because it conflicts with attribute .cold." } */
new file mode 100644
@@ -0,0 +1 @@
+ .file "mv11.C"
@@ -17,7 +17,7 @@ static void function_declaration_both_after(void) {t();}
static void function_declaration_noinline_before(void) __attribute__((__noinline__)); /* { dg-message "note: previous declaration" } */
-static inline void function_declaration_noinline_before(void) {t();} /* { dg-warning "follows declaration with attribute noinline" } */
+static inline void function_declaration_noinline_before(void) {t();} /* { dg-warning "follows declaration with attribute .noinline." } */
static inline void function_declaration_noinline_after(void) {t();} /* { dg-message "note: previous definition" } */
@@ -41,7 +41,7 @@ static void function_declaration_inline_noinline_after(void) __attribute__((__no
static void function_declaration_noinline_inline_before(void) __attribute__((__noinline__)); /* { dg-message "note: previous declaration" } */
-static inline void function_declaration_noinline_inline_before(void); /* { dg-warning "follows declaration with attribute noinline" } */
+static inline void function_declaration_noinline_inline_before(void); /* { dg-warning "follows declaration with attribute .noinline." } */
static void function_declaration_noinline_inline_before(void) {t();}
@@ -2,8 +2,9 @@
/* { dg-options "-fkeep-inline-functions -O" } */
static inline __attribute__ ((const))
-void baz (int i)
+int baz (int i)
{
+ return i;
}
static __attribute__ ((always_inline))
@@ -46,16 +46,18 @@ int bizr (void)
return i + 1;
}
-/* This might be regarded as pure and folded, rather than inlined.
- It's pure evil. */
+/* This might be regarded as pure and folded, rather than inlined,
+ but because it's pure evil it's diagnosed and the noreturn attribute
+ is dropped. The const attribute is dropped as well because it's
+ mutually exclusive with pure. */
static int __attribute__ ((pure, const, noreturn))
-barf (void)
-{
+barf (void) {
+ /* { dg-warning "ignoring attribute .const." "const" { target *-*-* } .-1 } */
+ /* { dg-warning "ignoring attribute .noreturn." "noreturn" { target *-*-* } .-2 } */
} /* { dg-warning "does return" } */
static int __attribute__ ((pure, const))
-bark (void)
-{
+bark (void) { /* { dg-warning "ignoring attribute .const." } */
barf ();
}
@@ -113,7 +113,7 @@ int test9 (int *intarr)
int test99 (int *intarr)
{
- extern int foo9 (int) __attribute__ ((pure));
+ extern int foo9 (int) __attribute__ ((const));
int h, v;
g9 = 9;
h = foo9 (g9);