PR middle-end/81824 - Warn for missing attributes with function aliases
gcc/c-family/ChangeLog: PR middle-end/81824 * c-attribs.c (handle_copy_attribute): New function. gcc/cp/ChangeLog: PR middle-end/81824 * pt.c (warn_spec_missing_attributes): Move code to attribs.c. Call decls_mismatched_attributes. gcc/ChangeLog: PR middle-end/81824 * attribs.c (has_attribute): New helper function. (decls_mismatched_attributes, maybe_diag_alias_attributes): Same. * attribs.h (decls_mismatched_attributes): Declare. * cgraphunit.c (handle_alias_pairs): Call maybe_diag_alias_attributes. (maybe_diag_incompatible_alias): Use OPT_Wattribute_alias_. * common.opt (-Wattribute-alias): Take an argument. (-Wno-attribute-alias): New option. * doc/extend.texi (Common Function Attributes): Document copy. (Common Variable Attributes): Same. * doc/invoke.texi (-Wmissing-attributes): Document enhancement. (-Wattribute-alias): Document new option argument. gcc/testsuite/ChangeLog: PR middle-end/81824 * gcc.dg/Wattribute-alias.c: New test. * gcc.dg/Wmissing-attributes.c: New test. * gcc.dg/attr-copy.c: New test. * gcc.dg/attr-copy-2.c: New test. * gcc.dg/attr-copy-3.c: New test. * gcc.dg/attr-copy-4.c: New test. From-SVN: r265980
This commit is contained in:
parent
900dab1338
commit
79a2c4281c
@ -1,3 +1,18 @@
|
||||
2018-11-09 Martin Sebor <msebor@redhat.com>
|
||||
|
||||
PR middle-end/81824
|
||||
* attribs.c (has_attribute): New helper function.
|
||||
(decls_mismatched_attributes, maybe_diag_alias_attributes): Same.
|
||||
* attribs.h (decls_mismatched_attributes): Declare.
|
||||
* cgraphunit.c (handle_alias_pairs): Call maybe_diag_alias_attributes.
|
||||
(maybe_diag_incompatible_alias): Use OPT_Wattribute_alias_.
|
||||
* common.opt (-Wattribute-alias): Take an argument.
|
||||
(-Wno-attribute-alias): New option.
|
||||
* doc/extend.texi (Common Function Attributes): Document copy.
|
||||
(Common Variable Attributes): Same.
|
||||
* doc/invoke.texi (-Wmissing-attributes): Document enhancement.
|
||||
(-Wattribute-alias): Document new option argument.
|
||||
|
||||
2018-11-09 Richard Earnshaw <rearnsha@arm.com>
|
||||
|
||||
* config/arm/parsecpu.awk (/alias/): Tighten invisible alias
|
||||
|
189
gcc/attribs.c
189
gcc/attribs.c
@ -30,6 +30,9 @@ along with GCC; see the file COPYING3. If not see
|
||||
#include "plugin.h"
|
||||
#include "selftest.h"
|
||||
#include "hash-set.h"
|
||||
#include "diagnostic.h"
|
||||
#include "pretty-print.h"
|
||||
#include "intl.h"
|
||||
|
||||
/* Table of the tables of attributes (common, language, format, machine)
|
||||
searched. */
|
||||
@ -1812,6 +1815,192 @@ private_lookup_attribute (const char *attr_name, size_t attr_len, tree list)
|
||||
return list;
|
||||
}
|
||||
|
||||
/* Return true if the function decl or type NODE has been declared
|
||||
with attribute ANAME among attributes ATTRS. */
|
||||
|
||||
static bool
|
||||
has_attribute (tree node, tree attrs, const char *aname)
|
||||
{
|
||||
if (!strcmp (aname, "const"))
|
||||
{
|
||||
if (DECL_P (node) && TREE_READONLY (node))
|
||||
return true;
|
||||
}
|
||||
else if (!strcmp (aname, "malloc"))
|
||||
{
|
||||
if (DECL_P (node) && DECL_IS_MALLOC (node))
|
||||
return true;
|
||||
}
|
||||
else if (!strcmp (aname, "noreturn"))
|
||||
{
|
||||
if (DECL_P (node) && TREE_THIS_VOLATILE (node))
|
||||
return true;
|
||||
}
|
||||
else if (!strcmp (aname, "nothrow"))
|
||||
{
|
||||
if (TREE_NOTHROW (node))
|
||||
return true;
|
||||
}
|
||||
else if (!strcmp (aname, "pure"))
|
||||
{
|
||||
if (DECL_P (node) && DECL_PURE_P (node))
|
||||
return true;
|
||||
}
|
||||
|
||||
return lookup_attribute (aname, attrs);
|
||||
}
|
||||
|
||||
/* Return the number of mismatched function or type attributes between
|
||||
the "template" function declaration TMPL and DECL. The word "template"
|
||||
doesn't necessarily refer to a C++ template but rather a declaration
|
||||
whose attributes should be matched by those on DECL. For a non-zero
|
||||
return value set *ATTRSTR to a string representation of the list of
|
||||
mismatched attributes with quoted names.
|
||||
ATTRLIST is a list of additional attributes that SPEC should be
|
||||
taken to ultimately be declared with. */
|
||||
|
||||
unsigned
|
||||
decls_mismatched_attributes (tree tmpl, tree decl, tree attrlist,
|
||||
const char* const blacklist[],
|
||||
pretty_printer *attrstr)
|
||||
{
|
||||
if (TREE_CODE (tmpl) != FUNCTION_DECL)
|
||||
return 0;
|
||||
|
||||
/* Avoid warning if either declaration or its type is deprecated. */
|
||||
if (TREE_DEPRECATED (tmpl)
|
||||
|| TREE_DEPRECATED (decl))
|
||||
return 0;
|
||||
|
||||
const tree tmpls[] = { tmpl, TREE_TYPE (tmpl) };
|
||||
const tree decls[] = { decl, TREE_TYPE (decl) };
|
||||
|
||||
if (TREE_DEPRECATED (tmpls[1])
|
||||
|| TREE_DEPRECATED (decls[1])
|
||||
|| TREE_DEPRECATED (TREE_TYPE (tmpls[1]))
|
||||
|| TREE_DEPRECATED (TREE_TYPE (decls[1])))
|
||||
return 0;
|
||||
|
||||
tree tmpl_attrs[] = { DECL_ATTRIBUTES (tmpl), TYPE_ATTRIBUTES (tmpls[1]) };
|
||||
tree decl_attrs[] = { DECL_ATTRIBUTES (decl), TYPE_ATTRIBUTES (decls[1]) };
|
||||
|
||||
if (!decl_attrs[0])
|
||||
decl_attrs[0] = attrlist;
|
||||
else if (!decl_attrs[1])
|
||||
decl_attrs[1] = attrlist;
|
||||
|
||||
/* Avoid warning if the template has no attributes. */
|
||||
if (!tmpl_attrs[0] && !tmpl_attrs[1])
|
||||
return 0;
|
||||
|
||||
/* Avoid warning if either declaration contains an attribute on
|
||||
the white list below. */
|
||||
const char* const whitelist[] = {
|
||||
"error", "warning"
|
||||
};
|
||||
|
||||
for (unsigned i = 0; i != 2; ++i)
|
||||
for (unsigned j = 0; j != sizeof whitelist / sizeof *whitelist; ++j)
|
||||
if (lookup_attribute (whitelist[j], tmpl_attrs[i])
|
||||
|| lookup_attribute (whitelist[j], decl_attrs[i]))
|
||||
return 0;
|
||||
|
||||
/* Put together a list of the black-listed attributes that the template
|
||||
is declared with and the declaration is not, in case it's not apparent
|
||||
from the most recent declaration of the template. */
|
||||
unsigned nattrs = 0;
|
||||
|
||||
for (unsigned i = 0; blacklist[i]; ++i)
|
||||
{
|
||||
for (unsigned j = 0; j != 2; ++j)
|
||||
{
|
||||
if (!has_attribute (tmpls[j], tmpl_attrs[j], blacklist[i]))
|
||||
continue;
|
||||
|
||||
unsigned kmax = 1 + !!decl_attrs[1];
|
||||
for (unsigned k = 0; k != kmax; ++k)
|
||||
{
|
||||
if (has_attribute (decls[k], decl_attrs[k], blacklist[i]))
|
||||
break;
|
||||
|
||||
if (!k && kmax > 1)
|
||||
continue;
|
||||
|
||||
if (nattrs)
|
||||
pp_string (attrstr, ", ");
|
||||
pp_begin_quote (attrstr, pp_show_color (global_dc->printer));
|
||||
pp_string (attrstr, blacklist[i]);
|
||||
pp_end_quote (attrstr, pp_show_color (global_dc->printer));
|
||||
++nattrs;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nattrs;
|
||||
}
|
||||
|
||||
/* Issue a warning for the declaration ALIAS for TARGET where ALIAS
|
||||
specifies either attributes that are incompatible with those of
|
||||
TARGET, or attributes that are missing and that declaring ALIAS
|
||||
with would benefit. */
|
||||
|
||||
void
|
||||
maybe_diag_alias_attributes (tree alias, tree target)
|
||||
{
|
||||
/* Do not expect attributes to match between aliases and ifunc
|
||||
resolvers. There is no obvious correspondence between them. */
|
||||
if (lookup_attribute ("ifunc", DECL_ATTRIBUTES (alias)))
|
||||
return;
|
||||
|
||||
const char* const blacklist[] = {
|
||||
"alloc_align", "alloc_size", "cold", "const", "hot", "leaf", "malloc",
|
||||
"nonnull", "noreturn", "nothrow", "pure", "returns_nonnull",
|
||||
"returns_twice", NULL
|
||||
};
|
||||
|
||||
pretty_printer attrnames;
|
||||
if (warn_attribute_alias > 1)
|
||||
{
|
||||
/* With -Wattribute-alias=2 detect alias declarations that are more
|
||||
restrictive than their targets first. Those indicate potential
|
||||
codegen bugs. */
|
||||
if (unsigned n = decls_mismatched_attributes (alias, target, NULL_TREE,
|
||||
blacklist, &attrnames))
|
||||
{
|
||||
auto_diagnostic_group d;
|
||||
if (warning_n (DECL_SOURCE_LOCATION (alias),
|
||||
OPT_Wattribute_alias_, n,
|
||||
"%qD specifies more restrictive attribute than "
|
||||
"its target %qD: %s",
|
||||
"%qD specifies more restrictive attributes than "
|
||||
"its target %qD: %s",
|
||||
alias, target, pp_formatted_text (&attrnames)))
|
||||
inform (DECL_SOURCE_LOCATION (target),
|
||||
"%qD target declared here", alias);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* Detect alias declarations that are less restrictive than their
|
||||
targets. Those suggest potential optimization opportunities
|
||||
(solved by adding the missing attribute(s) to the alias). */
|
||||
if (unsigned n = decls_mismatched_attributes (target, alias, NULL_TREE,
|
||||
blacklist, &attrnames))
|
||||
{
|
||||
auto_diagnostic_group d;
|
||||
if (warning_n (DECL_SOURCE_LOCATION (alias),
|
||||
OPT_Wmissing_attributes, n,
|
||||
"%qD specifies less restrictive attribute than "
|
||||
"its target %qD: %s",
|
||||
"%qD specifies less restrictive attributes than "
|
||||
"its target %qD: %s",
|
||||
alias, target, pp_formatted_text (&attrnames)))
|
||||
inform (DECL_SOURCE_LOCATION (target),
|
||||
"%qD target declared here", alias);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#if CHECKING_P
|
||||
|
||||
namespace selftest
|
||||
|
@ -105,6 +105,12 @@ extern int attribute_list_contained (const_tree, const_tree);
|
||||
extern tree private_lookup_attribute (const char *attr_name, size_t attr_len,
|
||||
tree list);
|
||||
|
||||
extern unsigned decls_mismatched_attributes (tree, tree, tree,
|
||||
const char* const[],
|
||||
pretty_printer*);
|
||||
|
||||
extern void maybe_diag_alias_attributes (tree, tree);
|
||||
|
||||
/* For a given IDENTIFIER_NODE, strip leading and trailing '_' characters
|
||||
so that we have a canonical form of attribute names. */
|
||||
|
||||
|
@ -1,3 +1,8 @@
|
||||
2018-11-09 Martin Sebor <msebor@redhat.com>
|
||||
|
||||
PR middle-end/81824
|
||||
* c-attribs.c (handle_copy_attribute): New function.
|
||||
|
||||
2018-11-09 Martin Sebor <msebor@redhat.com>
|
||||
|
||||
PR c/87795
|
||||
|
@ -146,6 +146,7 @@ static tree handle_designated_init_attribute (tree *, tree, tree, int, bool *);
|
||||
static tree handle_fallthrough_attribute (tree *, tree, tree, int, bool *);
|
||||
static tree handle_patchable_function_entry_attribute (tree *, tree, tree,
|
||||
int, bool *);
|
||||
static tree handle_copy_attribute (tree *, tree, tree, int, bool *);
|
||||
|
||||
/* Helper to define attribute exclusions. */
|
||||
#define ATTR_EXCL(name, function, type, variable) \
|
||||
@ -455,6 +456,8 @@ const struct attribute_spec c_common_attribute_table[] =
|
||||
NULL },
|
||||
{ "nocf_check", 0, 0, false, true, true, true,
|
||||
handle_nocf_check_attribute, NULL },
|
||||
{ "copy", 1, 1, false, false, false, false,
|
||||
handle_copy_attribute, NULL },
|
||||
{ NULL, 0, 0, false, false, false, false, NULL, NULL }
|
||||
};
|
||||
|
||||
@ -2137,6 +2140,151 @@ handle_alias_attribute (tree *node, tree name, tree args,
|
||||
return handle_alias_ifunc_attribute (true, node, name, args, no_add_attrs);
|
||||
}
|
||||
|
||||
/* Handle the "copy" attribute NAME by copying the set of attributes
|
||||
from the symbol referenced by ARGS to the declaration of *NODE. */
|
||||
|
||||
static tree
|
||||
handle_copy_attribute (tree *node, tree name, tree args,
|
||||
int flags, bool *no_add_attrs)
|
||||
{
|
||||
/* Do not apply the copy attribute itself. It serves no purpose
|
||||
other than to copy other attributes. */
|
||||
*no_add_attrs = true;
|
||||
|
||||
tree decl = *node;
|
||||
|
||||
tree ref = TREE_VALUE (args);
|
||||
if (ref == error_mark_node)
|
||||
return NULL_TREE;
|
||||
|
||||
if (TREE_CODE (ref) == STRING_CST)
|
||||
{
|
||||
/* Explicitly handle this case since using a string literal
|
||||
as an argument is a likely mistake. */
|
||||
error_at (DECL_SOURCE_LOCATION (decl),
|
||||
"%qE attribute argument cannot be a string",
|
||||
name);
|
||||
return NULL_TREE;
|
||||
}
|
||||
|
||||
if (CONSTANT_CLASS_P (ref)
|
||||
&& (INTEGRAL_TYPE_P (TREE_TYPE (ref))
|
||||
|| FLOAT_TYPE_P (TREE_TYPE (ref))))
|
||||
{
|
||||
/* Similar to the string case, since some function attributes
|
||||
accept literal numbers as arguments (e.g., alloc_size or
|
||||
nonnull) using one here is a likely mistake. */
|
||||
error_at (DECL_SOURCE_LOCATION (decl),
|
||||
"%qE attribute argument cannot be a constant arithmetic "
|
||||
"expression",
|
||||
name);
|
||||
return NULL_TREE;
|
||||
}
|
||||
|
||||
if (ref == node[1])
|
||||
{
|
||||
/* Another possible mistake (but indirect self-references aren't
|
||||
and diagnosed and shouldn't be). */
|
||||
if (warning_at (DECL_SOURCE_LOCATION (decl), OPT_Wattributes,
|
||||
"%qE attribute ignored on a redeclaration "
|
||||
"of the referenced symbol",
|
||||
name))
|
||||
inform (DECL_SOURCE_LOCATION (node[1]),
|
||||
"previous declaration here");
|
||||
return NULL_TREE;
|
||||
}
|
||||
|
||||
/* Consider address-of expressions in the attribute argument
|
||||
as requests to copy from the referenced entity. For constant
|
||||
expressions, consider those to be requests to copy from their
|
||||
type, such as in:
|
||||
struct __attribute__ (copy ((struct T *)0)) U { ... };
|
||||
which copies type attributes from struct T to the declaration
|
||||
of struct U. */
|
||||
if (TREE_CODE (ref) == ADDR_EXPR)
|
||||
ref = TREE_OPERAND (ref, 0);
|
||||
else if (CONSTANT_CLASS_P (ref))
|
||||
ref = TREE_TYPE (ref);
|
||||
|
||||
if (DECL_P (decl))
|
||||
{
|
||||
if ((VAR_P (decl)
|
||||
&& (TREE_CODE (ref) == FUNCTION_DECL
|
||||
|| (EXPR_P (ref)
|
||||
&& POINTER_TYPE_P (TREE_TYPE (ref))
|
||||
&& FUNC_OR_METHOD_TYPE_P (TREE_TYPE (TREE_TYPE (ref))))))
|
||||
|| (TREE_CODE (decl) == FUNCTION_DECL
|
||||
&& (VAR_P (ref)
|
||||
|| (EXPR_P (ref)
|
||||
&& !FUNC_OR_METHOD_TYPE_P (TREE_TYPE (ref))))))
|
||||
{
|
||||
/* It makes no sense to try to copy function attributes
|
||||
to a variable, or variable attributes to a function. */
|
||||
if (warning (OPT_Wattributes,
|
||||
"%qE attribute ignored on a declaration of "
|
||||
"a different kind than referenced symbol",
|
||||
name)
|
||||
&& DECL_P (ref))
|
||||
inform (DECL_SOURCE_LOCATION (ref),
|
||||
"symbol %qD referenced by %qD declared here", ref, decl);
|
||||
return NULL_TREE;
|
||||
}
|
||||
|
||||
tree attrs = NULL_TREE;
|
||||
if (DECL_P (ref))
|
||||
attrs = DECL_ATTRIBUTES (ref);
|
||||
else if (TYPE_P (ref))
|
||||
attrs = TYPE_ATTRIBUTES (ref);
|
||||
|
||||
/* Copy decl attributes from REF to DECL. */
|
||||
for (tree at = attrs; at; at = TREE_CHAIN (at))
|
||||
{
|
||||
/* Avoid copying attributes that affect a symbol linkage or
|
||||
visibility since those in all likelihood only apply to
|
||||
the target.
|
||||
FIXME: make it possible to specify which attributes to
|
||||
copy or not to copy in the copy attribute itself. */
|
||||
tree atname = get_attribute_name (at);
|
||||
if (is_attribute_p ("alias", atname)
|
||||
|| is_attribute_p ("ifunc", atname)
|
||||
|| is_attribute_p ("visibility", atname)
|
||||
|| is_attribute_p ("weak", atname)
|
||||
|| is_attribute_p ("weakref", 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. */
|
||||
tree attr = tree_cons (atname, copy_list (atargs), NULL_TREE);
|
||||
decl_attributes (node, attr, flags, ref);
|
||||
}
|
||||
|
||||
/* Proceed to copy type attributes below. */
|
||||
}
|
||||
else if (!TYPE_P (decl))
|
||||
{
|
||||
error_at (DECL_SOURCE_LOCATION (decl),
|
||||
"%qE attribute must apply to a declaration",
|
||||
name);
|
||||
return NULL_TREE;
|
||||
}
|
||||
|
||||
tree reftype = ref;
|
||||
if (DECL_P (ref) || EXPR_P (ref))
|
||||
reftype = TREE_TYPE (ref);
|
||||
|
||||
if (POINTER_TYPE_P (reftype))
|
||||
reftype = TREE_TYPE (reftype);
|
||||
|
||||
tree attrs = TYPE_ATTRIBUTES (reftype);
|
||||
|
||||
/* Copy type attributes from REF to DECL. */
|
||||
for (tree at = attrs; at; at = TREE_CHAIN (at))
|
||||
decl_attributes (node, at, flags, ref);
|
||||
|
||||
return NULL_TREE;
|
||||
}
|
||||
|
||||
/* Handle a "weakref" attribute; arguments as in struct
|
||||
attribute_spec.handler. */
|
||||
|
||||
@ -2962,34 +3110,11 @@ handle_deprecated_attribute (tree *node, tree name,
|
||||
return NULL_TREE;
|
||||
}
|
||||
|
||||
/* Handle a "vector_size" attribute; arguments as in
|
||||
struct attribute_spec.handler. */
|
||||
|
||||
/* Return the "base" type from TYPE that is suitable to apply attribute
|
||||
vector_size to by stripping arrays, function types, etc. */
|
||||
static tree
|
||||
handle_vector_size_attribute (tree *node, tree name, tree args,
|
||||
int ARG_UNUSED (flags),
|
||||
bool *no_add_attrs)
|
||||
type_for_vector_size (tree type)
|
||||
{
|
||||
unsigned HOST_WIDE_INT vecsize, nunits;
|
||||
machine_mode orig_mode;
|
||||
tree type = *node, new_type, size;
|
||||
|
||||
*no_add_attrs = true;
|
||||
|
||||
size = TREE_VALUE (args);
|
||||
if (size && TREE_CODE (size) != IDENTIFIER_NODE
|
||||
&& TREE_CODE (size) != FUNCTION_DECL)
|
||||
size = default_conversion (size);
|
||||
|
||||
if (!tree_fits_uhwi_p (size))
|
||||
{
|
||||
warning (OPT_Wattributes, "%qE attribute ignored", name);
|
||||
return NULL_TREE;
|
||||
}
|
||||
|
||||
/* Get the vector size (in bytes). */
|
||||
vecsize = tree_to_uhwi (size);
|
||||
|
||||
/* We need to provide for vector pointers, vector arrays, and
|
||||
functions returning vectors. For example:
|
||||
|
||||
@ -3005,8 +3130,25 @@ handle_vector_size_attribute (tree *node, tree name, tree args,
|
||||
|| TREE_CODE (type) == OFFSET_TYPE)
|
||||
type = TREE_TYPE (type);
|
||||
|
||||
return type;
|
||||
}
|
||||
|
||||
/* Given TYPE, return the base type to which the vector_size attribute
|
||||
ATNAME with ARGS, when non-null, can be applied, if one exists.
|
||||
On success and when both ARGS and PTRNUNITS are non-null, set
|
||||
*PTRNUNINTS to the number of vector units. When PTRNUNITS is not
|
||||
null, issue a warning when the attribute argument is not constant
|
||||
and an error if there is no such type. Otherwise issue a warning
|
||||
in the latter case and return null. */
|
||||
|
||||
static tree
|
||||
type_valid_for_vector_size (tree type, tree atname, tree args,
|
||||
unsigned HOST_WIDE_INT *ptrnunits)
|
||||
{
|
||||
bool error_p = ptrnunits != NULL;
|
||||
|
||||
/* Get the mode of the type being modified. */
|
||||
orig_mode = TYPE_MODE (type);
|
||||
machine_mode orig_mode = TYPE_MODE (type);
|
||||
|
||||
if ((!INTEGRAL_TYPE_P (type)
|
||||
&& !SCALAR_FLOAT_TYPE_P (type)
|
||||
@ -3017,14 +3159,38 @@ handle_vector_size_attribute (tree *node, tree name, tree args,
|
||||
|| !tree_fits_uhwi_p (TYPE_SIZE_UNIT (type))
|
||||
|| TREE_CODE (type) == BOOLEAN_TYPE)
|
||||
{
|
||||
error ("invalid vector type for attribute %qE", name);
|
||||
if (error_p)
|
||||
error ("invalid vector type for attribute %qE", atname);
|
||||
else
|
||||
warning (OPT_Wattributes, "invalid vector type for attribute %qE",
|
||||
atname);
|
||||
return NULL_TREE;
|
||||
}
|
||||
|
||||
/* When no argument has been provided this is just a request to validate
|
||||
the type above. Return TYPE to indicate success. */
|
||||
if (!args)
|
||||
return type;
|
||||
|
||||
tree size = TREE_VALUE (args);
|
||||
if (size && TREE_CODE (size) != IDENTIFIER_NODE
|
||||
&& TREE_CODE (size) != FUNCTION_DECL)
|
||||
size = default_conversion (size);
|
||||
|
||||
if (!tree_fits_uhwi_p (size))
|
||||
{
|
||||
/* FIXME: make the error message more informative. */
|
||||
if (error_p)
|
||||
warning (OPT_Wattributes, "%qE attribute ignored", atname);
|
||||
return NULL_TREE;
|
||||
}
|
||||
|
||||
unsigned HOST_WIDE_INT vecsize = tree_to_uhwi (size);
|
||||
if (vecsize % tree_to_uhwi (TYPE_SIZE_UNIT (type)))
|
||||
{
|
||||
error ("vector size not an integral multiple of component size");
|
||||
return NULL;
|
||||
if (error_p)
|
||||
error ("vector size not an integral multiple of component size");
|
||||
return NULL_TREE;
|
||||
}
|
||||
|
||||
if (vecsize == 0)
|
||||
@ -3034,14 +3200,44 @@ handle_vector_size_attribute (tree *node, tree name, tree args,
|
||||
}
|
||||
|
||||
/* Calculate how many units fit in the vector. */
|
||||
nunits = vecsize / tree_to_uhwi (TYPE_SIZE_UNIT (type));
|
||||
unsigned HOST_WIDE_INT nunits = vecsize / tree_to_uhwi (TYPE_SIZE_UNIT (type));
|
||||
if (nunits & (nunits - 1))
|
||||
{
|
||||
error ("number of components of the vector not a power of two");
|
||||
if (error_p)
|
||||
error ("number of components of the vector not a power of two");
|
||||
else
|
||||
warning (OPT_Wattributes,
|
||||
"number of components of the vector not a power of two");
|
||||
return NULL_TREE;
|
||||
}
|
||||
|
||||
new_type = build_vector_type (type, nunits);
|
||||
if (ptrnunits)
|
||||
*ptrnunits = nunits;
|
||||
|
||||
return type;
|
||||
}
|
||||
|
||||
/* Handle a "vector_size" attribute; arguments as in
|
||||
struct attribute_spec.handler. */
|
||||
|
||||
static tree
|
||||
handle_vector_size_attribute (tree *node, tree name, tree args,
|
||||
int ARG_UNUSED (flags),
|
||||
bool *no_add_attrs)
|
||||
{
|
||||
*no_add_attrs = true;
|
||||
|
||||
/* Determine the "base" type to apply the attribute to. */
|
||||
tree type = type_for_vector_size (*node);
|
||||
|
||||
/* Get the vector size (in bytes) and let the function compute
|
||||
the number of vector units. */
|
||||
unsigned HOST_WIDE_INT nunits;
|
||||
type = type_valid_for_vector_size (type, name, args, &nunits);
|
||||
if (!type)
|
||||
return NULL_TREE;
|
||||
|
||||
tree new_type = build_vector_type (type, nunits);
|
||||
|
||||
/* Build back pointers if needed. */
|
||||
*node = lang_hooks.types.reconstruct_complex_type (*node, new_type);
|
||||
@ -3512,3 +3708,324 @@ handle_patchable_function_entry_attribute (tree *, tree, tree, int, bool *)
|
||||
/* Nothing to be done here. */
|
||||
return NULL_TREE;
|
||||
}
|
||||
|
||||
/* Attempt to partially validate a single attribute ATTR as if
|
||||
it were to be applied to an entity OPER. */
|
||||
|
||||
static bool
|
||||
validate_attribute (location_t atloc, tree oper, tree attr)
|
||||
{
|
||||
/* Determine whether the name of the attribute is valid
|
||||
and fail with an error if not. */
|
||||
tree atname = get_attribute_name (attr);
|
||||
if (!lookup_attribute_spec (atname))
|
||||
{
|
||||
if (atloc != UNKNOWN_LOCATION)
|
||||
error_at (atloc, "unknown attribute %qE", atname);
|
||||
return false;
|
||||
}
|
||||
|
||||
tree args = TREE_VALUE (attr);
|
||||
if (!args)
|
||||
return true;
|
||||
|
||||
/* FIXME: Do some validation. */
|
||||
const char *atstr = IDENTIFIER_POINTER (atname);
|
||||
if (!strcmp (atstr, "format"))
|
||||
return true;
|
||||
|
||||
/* Only when attribute arguments have been provided try to validate
|
||||
the whole thing. decl_attributes doesn't return an indication of
|
||||
success or failure so proceed regardless. */
|
||||
const char tmpname[] = "__builtin_has_attribute_tmp.";
|
||||
tree tmpid = get_identifier (tmpname);
|
||||
tree tmpdecl;
|
||||
if (!strcmp (atstr, "vector_size"))
|
||||
{
|
||||
tree type = TYPE_P (oper) ? oper : TREE_TYPE (oper);
|
||||
/* Check for function type here since type_for_vector_size
|
||||
strips it while looking for a function's return type. */
|
||||
if (FUNC_OR_METHOD_TYPE_P (type))
|
||||
{
|
||||
warning_at (atloc, OPT_Wattributes,
|
||||
"invalid operand type %qT for %qs", type, atstr);
|
||||
return false;
|
||||
}
|
||||
|
||||
type = type_for_vector_size (type);
|
||||
if (VECTOR_TYPE_P (type))
|
||||
type = TREE_TYPE (type);
|
||||
/* Avoid trying to apply attribute vector_size to OPER since
|
||||
it's overly restrictive. Simply make sure it has the right
|
||||
type. */
|
||||
return type_valid_for_vector_size (type, atname, args, NULL);
|
||||
}
|
||||
|
||||
if (TYPE_P (oper))
|
||||
tmpdecl = build_decl (atloc, TYPE_DECL, tmpid, oper);
|
||||
else
|
||||
tmpdecl = build_decl (atloc, TREE_CODE (oper), tmpid, TREE_TYPE (oper));
|
||||
|
||||
/* Temporarily clear CURRENT_FUNCTION_DECL to make decl_attributes
|
||||
believe the DECL declared above is at file scope. (See bug 87526.) */
|
||||
tree save_curfunc = current_function_decl;
|
||||
current_function_decl = NULL_TREE;
|
||||
if (DECL_P (tmpdecl))
|
||||
{
|
||||
if (DECL_P (oper))
|
||||
/* An alias cannot be a defintion so declare the symbol extern. */
|
||||
DECL_EXTERNAL (tmpdecl) = true;
|
||||
/* Attribute visibility only applies to symbols visible from other
|
||||
translation units so make it "public." */
|
||||
TREE_PUBLIC (tmpdecl) = TREE_PUBLIC (oper);
|
||||
}
|
||||
decl_attributes (&tmpdecl, attr, 0);
|
||||
current_function_decl = save_curfunc;
|
||||
|
||||
/* FIXME: Change decl_attributes to indicate success or failure (and
|
||||
parameterize it to avoid failing with errors). */
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Return true if the DECL, EXPR, or TYPE t has been declared with
|
||||
attribute ATTR. For DECL, consider also its type. For EXPR,
|
||||
consider just its type. */
|
||||
|
||||
bool
|
||||
has_attribute (location_t atloc, tree t, tree attr, tree (*convert)(tree))
|
||||
{
|
||||
if (!attr || !t || t == error_mark_node)
|
||||
return false;
|
||||
|
||||
if (!validate_attribute (atloc, t, attr))
|
||||
return false;
|
||||
|
||||
tree type = NULL_TREE;
|
||||
tree expr = NULL_TREE;
|
||||
if (TYPE_P (t))
|
||||
type = t;
|
||||
else
|
||||
{
|
||||
do
|
||||
{
|
||||
/* Determine the array element/member declaration from
|
||||
an ARRAY/COMPONENT_REF. */
|
||||
STRIP_NOPS (t);
|
||||
tree_code code = TREE_CODE (t);
|
||||
if (code == ARRAY_REF)
|
||||
t = TREE_OPERAND (t, 0);
|
||||
else if (code == COMPONENT_REF)
|
||||
t = TREE_OPERAND (t, 1);
|
||||
else
|
||||
break;
|
||||
} while (true);
|
||||
expr = t;
|
||||
}
|
||||
|
||||
/* Set to true when an attribute is found in the referenced entity
|
||||
that matches the specified attribute. */
|
||||
bool found_match = false;
|
||||
|
||||
tree atname = get_attribute_name (attr);
|
||||
const char *namestr = IDENTIFIER_POINTER (atname);
|
||||
|
||||
/* Iterate once for a type and twice for a function or variable
|
||||
declaration: once for the DECL and the second time for its
|
||||
TYPE. */
|
||||
for (bool done = false; !found_match && !done; )
|
||||
{
|
||||
tree atlist;
|
||||
if (type)
|
||||
{
|
||||
if (type == error_mark_node)
|
||||
{
|
||||
/* This could be a label. FIXME: add support for labels. */
|
||||
warning_at (atloc, OPT_Wattributes,
|
||||
(TYPE_P (t)
|
||||
? G_("%qs attribute not supported for %qT "
|
||||
"in %<__builtin_has_attribute%>")
|
||||
: G_("%qs attribute not supported for %qE "
|
||||
"in %<__builtin_has_attribute%>")),
|
||||
namestr, t);
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Clear EXPR to prevent considering it again below. */
|
||||
atlist = TYPE_ATTRIBUTES (type);
|
||||
expr = NULL_TREE;
|
||||
done = true;
|
||||
}
|
||||
else if (DECL_P (expr))
|
||||
{
|
||||
/* Set TYPE to the DECL's type to process it on the next
|
||||
iteration. */
|
||||
atlist = DECL_ATTRIBUTES (expr);
|
||||
type = TREE_TYPE (expr);
|
||||
}
|
||||
else
|
||||
{
|
||||
atlist = TYPE_ATTRIBUTES (TREE_TYPE (expr));
|
||||
done = true;
|
||||
}
|
||||
|
||||
/* True when an attribute with the sought name (though not necessarily
|
||||
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. */
|
||||
bool attr_nonnull = !strcmp ("nonnull", namestr);
|
||||
bool ignore_mismatches = attr_nonnull;
|
||||
|
||||
/* Iterate over the instances of the sought attribute on the DECL or
|
||||
TYPE (there may be multiple instances with different arguments). */
|
||||
for (; (atlist = lookup_attribute (namestr, atlist));
|
||||
found_attr = true, atlist = TREE_CHAIN (atlist))
|
||||
{
|
||||
/* If there are no arguments to match the result is true except
|
||||
for nonnull where the attribute with no arguments must match. */
|
||||
if (!TREE_VALUE (attr))
|
||||
return attr_nonnull ? !TREE_VALUE (atlist) : true;
|
||||
|
||||
/* Attribute nonnull with no arguments subsumes all values of
|
||||
the attribute. FIXME: This is overly broad since it only
|
||||
applies to pointer arguments, but querying non-pointer
|
||||
arguments is diagnosed. */
|
||||
if (!TREE_VALUE (atlist) && attr_nonnull)
|
||||
return true;
|
||||
|
||||
/* Iterate over the DECL or TYPE attribute argument's values. */
|
||||
for (tree val = TREE_VALUE (atlist); val; val = TREE_CHAIN (val))
|
||||
{
|
||||
/* Iterate over the arguments in the sought attribute comparing
|
||||
their values to those specified for the DECL or TYPE. */
|
||||
for (tree arg = TREE_VALUE (attr); arg; arg = TREE_CHAIN (arg))
|
||||
{
|
||||
tree v1 = TREE_VALUE (val);
|
||||
tree v2 = TREE_VALUE (arg);
|
||||
if (v1 == v2)
|
||||
return true;
|
||||
|
||||
if (!v1 || !v2)
|
||||
break;
|
||||
|
||||
if (TREE_CODE (v1) == IDENTIFIER_NODE
|
||||
|| TREE_CODE (v2) == IDENTIFIER_NODE)
|
||||
/* Two identifiers are the same if their values are
|
||||
equal (that's handled above). Otherwise ther are
|
||||
either not the same or oneis not an identifier. */
|
||||
return false;
|
||||
|
||||
/* Convert to make them equality-comparable. */
|
||||
v1 = convert (v1);
|
||||
v2 = convert (v2);
|
||||
|
||||
/* A positive value indicates equality, negative means
|
||||
"don't know." */
|
||||
if (simple_cst_equal (v1, v2) == 1)
|
||||
return true;
|
||||
|
||||
if (!ignore_mismatches)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!found_attr)
|
||||
{
|
||||
/* Some attributes are encoded directly in the tree node. */
|
||||
if (!strcmp ("aligned", namestr))
|
||||
{
|
||||
if (tree arg = TREE_VALUE (attr))
|
||||
{
|
||||
arg = convert (TREE_VALUE (arg));
|
||||
if (expr && DECL_P (expr)
|
||||
&& DECL_USER_ALIGN (expr)
|
||||
&& tree_fits_uhwi_p (arg))
|
||||
found_match = DECL_ALIGN_UNIT (expr) == tree_to_uhwi (arg);
|
||||
else if (type && TYPE_USER_ALIGN (type))
|
||||
found_match = TYPE_ALIGN_UNIT (type) == tree_to_uhwi (arg);
|
||||
}
|
||||
else if (expr && DECL_P (expr))
|
||||
found_match = DECL_USER_ALIGN (expr);
|
||||
else if (type)
|
||||
found_match = TYPE_USER_ALIGN (type);
|
||||
}
|
||||
else if (!strcmp ("const", namestr))
|
||||
{
|
||||
if (expr && DECL_P (expr))
|
||||
found_match = TREE_READONLY (expr);
|
||||
}
|
||||
else if (!strcmp ("const", namestr))
|
||||
{
|
||||
if (expr && DECL_P (expr))
|
||||
found_match = DECL_PURE_P (expr);
|
||||
}
|
||||
else if (!strcmp ("deprecated", namestr))
|
||||
{
|
||||
found_match = TREE_DEPRECATED (expr ? expr : type);
|
||||
if (found_match)
|
||||
return true;
|
||||
}
|
||||
else if (!strcmp ("vector_size", namestr))
|
||||
{
|
||||
if (!type)
|
||||
continue;
|
||||
|
||||
/* Determine the base type from arrays, pointers, and such.
|
||||
Fail if the base type is not a vector. */
|
||||
type = type_for_vector_size (type);
|
||||
if (!VECTOR_TYPE_P (type))
|
||||
return false;
|
||||
|
||||
if (tree arg = TREE_VALUE (attr))
|
||||
{
|
||||
/* Compare the vector size argument for equality. */
|
||||
arg = convert (TREE_VALUE (arg));
|
||||
return tree_int_cst_equal (arg, TYPE_SIZE_UNIT (type)) == 1;
|
||||
}
|
||||
else
|
||||
return true;
|
||||
}
|
||||
else if (!strcmp ("warn_if_not_aligned", namestr))
|
||||
{
|
||||
if (tree arg = TREE_VALUE (attr))
|
||||
{
|
||||
arg = convert (TREE_VALUE (arg));
|
||||
if (expr && DECL_P (expr))
|
||||
found_match = (DECL_WARN_IF_NOT_ALIGN (expr)
|
||||
== tree_to_uhwi (arg) * BITS_PER_UNIT);
|
||||
else if (type)
|
||||
found_match = (TYPE_WARN_IF_NOT_ALIGN (type)
|
||||
== tree_to_uhwi (arg) * BITS_PER_UNIT);
|
||||
}
|
||||
else if (expr && DECL_P (expr))
|
||||
found_match = DECL_WARN_IF_NOT_ALIGN (expr);
|
||||
else if (type)
|
||||
found_match = TYPE_WARN_IF_NOT_ALIGN (type);
|
||||
}
|
||||
else if (!strcmp ("transparent_union", namestr))
|
||||
{
|
||||
if (type)
|
||||
found_match = TYPE_TRANSPARENT_AGGR (type) != 0;
|
||||
}
|
||||
else if (!strcmp ("mode", namestr))
|
||||
{
|
||||
/* Finally issue a warning for attributes that cannot
|
||||
be supported in this context. Attribute mode is not
|
||||
added to a symbol and cannot be determined from it. */
|
||||
warning_at (atloc, OPT_Wattributes,
|
||||
"%qs attribute not supported in "
|
||||
"%<__builtin_has_attribute%>", namestr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return found_match;
|
||||
}
|
||||
|
@ -1364,7 +1364,7 @@ maybe_diag_incompatible_alias (tree alias, tree target)
|
||||
|
||||
auto_diagnostic_group d;
|
||||
if (warning_at (DECL_SOURCE_LOCATION (target),
|
||||
OPT_Wattribute_alias,
|
||||
OPT_Wattribute_alias_,
|
||||
"%<ifunc%> resolver for %qD should return %qT",
|
||||
alias, funcptr))
|
||||
inform (DECL_SOURCE_LOCATION (alias),
|
||||
@ -1374,11 +1374,11 @@ maybe_diag_incompatible_alias (tree alias, tree target)
|
||||
{
|
||||
auto_diagnostic_group d;
|
||||
if (warning_at (DECL_SOURCE_LOCATION (alias),
|
||||
OPT_Wattribute_alias,
|
||||
OPT_Wattribute_alias_,
|
||||
"%qD alias between functions of incompatible "
|
||||
"types %qT and %qT", alias, altype, targtype))
|
||||
inform (DECL_SOURCE_LOCATION (target),
|
||||
"aliased declaration here");
|
||||
"aliased declaration here");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1441,6 +1441,8 @@ handle_alias_pairs (void)
|
||||
{
|
||||
maybe_diag_incompatible_alias (p->decl, target_node->decl);
|
||||
|
||||
maybe_diag_alias_attributes (p->decl, target_node->decl);
|
||||
|
||||
cgraph_node *src_node = cgraph_node::get (p->decl);
|
||||
if (src_node && src_node->definition)
|
||||
src_node->reset ();
|
||||
|
@ -550,9 +550,13 @@ Wattributes
|
||||
Common Var(warn_attributes) Init(1) Warning
|
||||
Warn about inappropriate attribute usage.
|
||||
|
||||
Wattribute-alias
|
||||
Common Var(warn_attributes) Init(1) Warning
|
||||
Warn about type safety and similar errors in attribute alias and related.
|
||||
Wattribute-alias=
|
||||
Common Joined RejectNegative UInteger Var(warn_attribute_alias) Init(1) Warning IntegerRange(0, 2)
|
||||
Warn about type safety and similar errors and mismatches in attribute alias and related.
|
||||
|
||||
Wno-attribute-alias
|
||||
Common Alias(Wattribute_alias=, 0, 0) Warning
|
||||
Disable -Wattribute-alias.
|
||||
|
||||
Wcannot-profile
|
||||
Common Var(warn_cannot_profile) Init(1) Warning
|
||||
|
@ -1,3 +1,9 @@
|
||||
2018-11-09 Martin Sebor <msebor@redhat.com>
|
||||
|
||||
PR middle-end/81824
|
||||
* pt.c (warn_spec_missing_attributes): Move code to attribs.c.
|
||||
Call decls_mismatched_attributes.
|
||||
|
||||
2018-11-08 Jakub Jelinek <jakub@redhat.com>
|
||||
|
||||
* constexpr.c (potential_constant_expression_1): Handle OMP_DEPOBJ.
|
||||
|
68
gcc/cp/pt.c
68
gcc/cp/pt.c
@ -2647,81 +2647,19 @@ warn_spec_missing_attributes (tree tmpl, tree spec, tree attrlist)
|
||||
if (DECL_FUNCTION_TEMPLATE_P (tmpl))
|
||||
tmpl = DECL_TEMPLATE_RESULT (tmpl);
|
||||
|
||||
if (TREE_CODE (tmpl) != FUNCTION_DECL)
|
||||
return;
|
||||
|
||||
/* Avoid warning if either declaration or its type is deprecated. */
|
||||
if (TREE_DEPRECATED (tmpl)
|
||||
|| TREE_DEPRECATED (spec))
|
||||
return;
|
||||
|
||||
tree tmpl_type = TREE_TYPE (tmpl);
|
||||
tree spec_type = TREE_TYPE (spec);
|
||||
|
||||
if (TREE_DEPRECATED (tmpl_type)
|
||||
|| TREE_DEPRECATED (spec_type)
|
||||
|| TREE_DEPRECATED (TREE_TYPE (tmpl_type))
|
||||
|| TREE_DEPRECATED (TREE_TYPE (spec_type)))
|
||||
return;
|
||||
|
||||
tree tmpl_attrs[] = { DECL_ATTRIBUTES (tmpl), TYPE_ATTRIBUTES (tmpl_type) };
|
||||
tree spec_attrs[] = { DECL_ATTRIBUTES (spec), TYPE_ATTRIBUTES (spec_type) };
|
||||
|
||||
if (!spec_attrs[0])
|
||||
spec_attrs[0] = attrlist;
|
||||
else if (!spec_attrs[1])
|
||||
spec_attrs[1] = attrlist;
|
||||
|
||||
/* Avoid warning if the primary has no attributes. */
|
||||
if (!tmpl_attrs[0] && !tmpl_attrs[1])
|
||||
return;
|
||||
|
||||
/* Avoid warning if either declaration contains an attribute on
|
||||
the white list below. */
|
||||
const char* const whitelist[] = {
|
||||
"error", "warning"
|
||||
};
|
||||
|
||||
for (unsigned i = 0; i != 2; ++i)
|
||||
for (unsigned j = 0; j != sizeof whitelist / sizeof *whitelist; ++j)
|
||||
if (lookup_attribute (whitelist[j], tmpl_attrs[i])
|
||||
|| lookup_attribute (whitelist[j], spec_attrs[i]))
|
||||
return;
|
||||
|
||||
/* Avoid warning if the difference between the primary and
|
||||
the specialization is not in one of the attributes below. */
|
||||
const char* const blacklist[] = {
|
||||
"alloc_align", "alloc_size", "assume_aligned", "format",
|
||||
"format_arg", "malloc", "nonnull"
|
||||
"format_arg", "malloc", "nonnull", NULL
|
||||
};
|
||||
|
||||
/* Put together a list of the black listed attributes that the primary
|
||||
template is declared with that the specialization is not, in case
|
||||
it's not apparent from the most recent declaration of the primary. */
|
||||
unsigned nattrs = 0;
|
||||
pretty_printer str;
|
||||
|
||||
for (unsigned i = 0; i != sizeof blacklist / sizeof *blacklist; ++i)
|
||||
{
|
||||
for (unsigned j = 0; j != 2; ++j)
|
||||
{
|
||||
if (!lookup_attribute (blacklist[i], tmpl_attrs[j]))
|
||||
continue;
|
||||
|
||||
for (unsigned k = 0; k != 1 + !!spec_attrs[1]; ++k)
|
||||
{
|
||||
if (lookup_attribute (blacklist[i], spec_attrs[k]))
|
||||
break;
|
||||
|
||||
if (nattrs)
|
||||
pp_string (&str, ", ");
|
||||
pp_begin_quote (&str, pp_show_color (global_dc->printer));
|
||||
pp_string (&str, blacklist[i]);
|
||||
pp_end_quote (&str, pp_show_color (global_dc->printer));
|
||||
++nattrs;
|
||||
}
|
||||
}
|
||||
}
|
||||
unsigned nattrs = decls_mismatched_attributes (tmpl, spec, attrlist,
|
||||
blacklist, &str);
|
||||
|
||||
if (!nattrs)
|
||||
return;
|
||||
|
@ -2552,6 +2552,40 @@ decorated with attribute @code{constructor} are invoked is unspecified.
|
||||
In mixed declarations, attribute @code{init_priority} can be used to
|
||||
impose a specific ordering.
|
||||
|
||||
@item copy
|
||||
@itemx copy (@var{function})
|
||||
@cindex @code{copy} function attribute
|
||||
The @code{copy} attribute applies the set of attributes with which
|
||||
@var{function} has been declared to the declaration of the function
|
||||
to which the attribute is applied. The attribute is designed for
|
||||
libraries that define aliases or function resolvers that are expected
|
||||
to specify the same set of attributes as their targets. The @code{copy}
|
||||
attribute can be used with functions, variables, or types. However,
|
||||
the kind of symbol to which the attribute is applied (either function
|
||||
or variable) must match the kind of symbol to which the argument refers.
|
||||
The @code{copy} attribute copies only syntaxtic and semantic attributes
|
||||
but attributes that affect a symbol's linkage or visibility such as
|
||||
@code{alias}, @code{visibility}, or @code{weak}. The @code{deprecated}
|
||||
attribute is also not copied. @xref{Common Type Attributes}.
|
||||
@xref{Common Variable Attributes}.
|
||||
|
||||
For example, the @var{StrongAlias} macro below makes use of the @code{alias}
|
||||
and @code{copy} attributes to define an alias named @var{alloc} for function
|
||||
@var{allocate} declated with attributes @var{alloc_size}, @var{malloc}, and
|
||||
@var{nothrow}. Thanks to the @code{__typeof__} operator the alias has
|
||||
the same type as the target function. As a result of the @code{copy}
|
||||
attribute the alias also shares the same attributes as the target.
|
||||
|
||||
@smallexample
|
||||
#define StrongAlias(TagetFunc, AliasDecl) \
|
||||
extern __typeof__ (TargetFunc) AliasDecl \
|
||||
__attribute__ ((alias (#TargetFunc), copy (TargetFunc)));
|
||||
|
||||
extern __attribute__ ((alloc_size (1), malloc, nothrow))
|
||||
void* allocate (size_t);
|
||||
StrongAlias (allocate, alloc);
|
||||
@end smallexample
|
||||
|
||||
@item deprecated
|
||||
@itemx deprecated (@var{msg})
|
||||
@cindex @code{deprecated} function attribute
|
||||
@ -6174,6 +6208,23 @@ opposite---to allocate space for it directly.
|
||||
These attributes override the default chosen by the
|
||||
@option{-fno-common} and @option{-fcommon} flags respectively.
|
||||
|
||||
@item copy
|
||||
@itemx copy (@var{variable})
|
||||
@cindex @code{copy} variable attribute
|
||||
The @code{copy} attribute applies the set of attributes with which
|
||||
@var{variable} has been declared to the declaration of the variable
|
||||
to which the attribute is applied. The attribute is designed for
|
||||
libraries that define aliases that are expected to specify the same
|
||||
set of attributes as the aliased symbols. The @code{copy} attribute
|
||||
can be used with variables, functions or types. However, the kind
|
||||
of symbol to which the attribute is applied (either varible or
|
||||
function) must match the kind of symbol to which the argument refers.
|
||||
The @code{copy} attribute copies only syntaxtic and semantic attributes
|
||||
but attributes that affect a symbol's linkage or visibility such as
|
||||
@code{alias}, @code{visibility}, or @code{weak}. The @code{deprecated}
|
||||
attribute is also not copied. @xref{Common Function Attributes}.
|
||||
@xref{Common Type Attributes}.
|
||||
|
||||
@item deprecated
|
||||
@itemx deprecated (@var{msg})
|
||||
@cindex @code{deprecated} variable attribute
|
||||
@ -7105,6 +7156,38 @@ struct foo
|
||||
|
||||
This warning can be disabled by @option{-Wno-if-not-aligned}.
|
||||
|
||||
@item copy
|
||||
@itemx copy (@var{expression})
|
||||
@cindex @code{copy} type attribute
|
||||
The @code{copy} attribute applies the set of attributes with which
|
||||
the type of the @var{expression} has been declared to the declaration
|
||||
of the type to which the attribute is applied. The attribute is
|
||||
designed for libraries that define aliases that are expected to
|
||||
specify the same set of attributes as the aliased symbols.
|
||||
The @code{copy} attribute can be used with types, variables, or
|
||||
functions. However, the kind of symbol to which the attribute is
|
||||
applied (either varible or function) must match the kind of symbol
|
||||
to which the argument refers.
|
||||
The @code{copy} attribute copies only syntaxtic and semantic attributes
|
||||
but attributes that affect a symbol's linkage or visibility such as
|
||||
@code{alias}, @code{visibility}, or @code{weak}. The @code{deprecated}
|
||||
attribute is also not copied. @xref{Common Function Attributes}.
|
||||
@xref{Common Variable Attributes}.
|
||||
|
||||
For example, suppose @code{struct A} below is defined in some third
|
||||
partly library header to have the alignment requirement @code{N} and
|
||||
to force a warning whenever a variable of the type is not so aligned
|
||||
due to attribute @code{packed}. Specifying the @code{copy} attribute
|
||||
on the definition on the unrelated @code{struct B} has the effect of
|
||||
copying all relevant attributes from the type referenced by the pointer
|
||||
expression to @code{struct B}.
|
||||
|
||||
@smallexample
|
||||
struct __attribute__ ((aligned (N), warn_if_not_aligned (N)))
|
||||
A @{ /* @r{@dots{}} */ @};
|
||||
struct __attribute__ ((copy ( (struct A *)0)) B @{ /* @r{@dots{}} */ @};
|
||||
@end smallexample
|
||||
|
||||
@item deprecated
|
||||
@itemx deprecated (@var{msg})
|
||||
@cindex @code{deprecated} type attribute
|
||||
|
@ -283,7 +283,8 @@ Objective-C and Objective-C++ Dialects}.
|
||||
-Walloc-zero -Walloc-size-larger-than=@var{byte-size}
|
||||
-Walloca -Walloca-larger-than=@var{byte-size} @gol
|
||||
-Wno-aggressive-loop-optimizations -Warray-bounds -Warray-bounds=@var{n} @gol
|
||||
-Wno-attributes -Wbool-compare -Wbool-operation @gol
|
||||
-Wno-attributes -Wno-attribute-alias @gol
|
||||
-Wbool-compare -Wbool-operation @gol
|
||||
-Wno-builtin-declaration-mismatch @gol
|
||||
-Wno-builtin-macro-redefined -Wc90-c99-compat -Wc99-c11-compat @gol
|
||||
-Wc++-compat -Wc++11-compat -Wc++14-compat -Wc++17-compat @gol
|
||||
@ -316,7 +317,7 @@ Objective-C and Objective-C++ Dialects}.
|
||||
-Winvalid-pch -Wlarger-than=@var{byte-size} @gol
|
||||
-Wlogical-op -Wlogical-not-parentheses -Wlong-long @gol
|
||||
-Wmain -Wmaybe-uninitialized -Wmemset-elt-size -Wmemset-transposed-args @gol
|
||||
-Wmisleading-indentation -Wmissing-attributes -Wmissing-braces @gol
|
||||
-Wmisleading-indentation -Wno-missing-attributes -Wmissing-braces @gol
|
||||
-Wmissing-field-initializers -Wmissing-include-dirs -Wmissing-profile @gol
|
||||
-Wno-multichar -Wmultistatement-macros -Wnonnull -Wnonnull-compare @gol
|
||||
-Wnormalized=@r{[}none@r{|}id@r{|}nfc@r{|}nfkc@r{]} @gol
|
||||
@ -4805,13 +4806,24 @@ about the layout of the file that the directive references.
|
||||
|
||||
This warning is enabled by @option{-Wall} in C and C++.
|
||||
|
||||
@item -Wmissing-attributes
|
||||
@item -Wno-missing-attributes
|
||||
@opindex Wmissing-attributes
|
||||
@opindex Wno-missing-attributes
|
||||
Warn when a declaration of a function is missing one or more attributes
|
||||
that a related function is declared with and whose absence may adversely
|
||||
affect the correctness or efficiency of generated code. For example, in
|
||||
C++, the warning is issued when an explicit specialization of a primary
|
||||
affect the correctness or efficiency of generated code. For example,
|
||||
the warning is issued for declarations of aliases that use attributes
|
||||
to specify less restrictive requirements than those of their targets.
|
||||
This typically represents a potential optimization oportunity rather
|
||||
than a hidden bug. The @option{-Wattribute-alias} option controls warnings
|
||||
issued for mismatches between declarations of aliases and their targets
|
||||
that might be indicative of code generation bugs.
|
||||
Attributes considered include @code{alloc_align}, @code{alloc_size},
|
||||
@code{cold}, @code{const}, @code{hot}, @code{leaf}, @code{malloc},
|
||||
@code{nonnull}, @code{noreturn}, @code{nothrow}, @code{pure},
|
||||
@code{returns_nonnull}, and @code{returns_twice}.
|
||||
|
||||
In C++, the warning is issued when an explicit specialization of a primary
|
||||
template declared with attribute @code{alloc_align}, @code{alloc_size},
|
||||
@code{assume_aligned}, @code{format}, @code{format_arg}, @code{malloc},
|
||||
or @code{nonnull} is declared without it. Attributes @code{deprecated},
|
||||
@ -5831,10 +5843,28 @@ pointers. This warning level may give a larger number of
|
||||
false positives and is deactivated by default.
|
||||
@end table
|
||||
|
||||
@item -Wattribute-alias
|
||||
@item -Wattribute-alias=@var{n}
|
||||
@itemx -Wno-attribute-alias
|
||||
@opindex -Wattribute-alias
|
||||
@opindex -Wno-attribute-alias
|
||||
Warn about declarations using the @code{alias} and similar attributes whose
|
||||
target is incompatible with the type of the alias. @xref{Function Attributes,
|
||||
,Declaring Attributes of Functions}.
|
||||
target is incompatible with the type of the alias.
|
||||
@xref{Function Attributes,,Declaring Attributes of Functions}.
|
||||
The @option{-Wattribute-alias=1} is enabled by @option{-Wall}.
|
||||
|
||||
@table @gcctabopt
|
||||
@item -Wattribute-alias=1
|
||||
The default warning level of the @option{-Wattribute-alias} option diagnoses
|
||||
incompatibilities between the type of the alias declaration and that of its
|
||||
target. Such incompatibilities are typically indicative of bugs.
|
||||
|
||||
@item -Wattribute-alias=2
|
||||
At this level @option{-Wattribute-alias} also diagnoses mismatches between
|
||||
the set of attributes of the alias declaration and the attributes applied
|
||||
to its target. Although in some cases such mismatches may indicate bugs,
|
||||
in other cases they may be benign and could be resolved simply by adding
|
||||
the missing attribute to the target.
|
||||
@end table
|
||||
|
||||
@item -Wbool-compare
|
||||
@opindex Wno-bool-compare
|
||||
|
@ -1,3 +1,13 @@
|
||||
2018-11-09 Martin Sebor <msebor@redhat.com>
|
||||
|
||||
PR middle-end/81824
|
||||
* gcc.dg/Wattribute-alias.c: New test.
|
||||
* gcc.dg/Wmissing-attributes.c: New test.
|
||||
* gcc.dg/attr-copy.c: New test.
|
||||
* gcc.dg/attr-copy-2.c: New test.
|
||||
* gcc.dg/attr-copy-3.c: New test.
|
||||
* gcc.dg/attr-copy-4.c: New test.
|
||||
|
||||
2018-11-09 Martin Sebor <msebor@redhat.com>
|
||||
|
||||
PR c/87795
|
||||
|
49
gcc/testsuite/gcc.dg/Wattribute-alias.c
Normal file
49
gcc/testsuite/gcc.dg/Wattribute-alias.c
Normal file
@ -0,0 +1,49 @@
|
||||
/* PR middle-end/81824 - Warn for missing attributes with function aliases
|
||||
{ dg-do compile }
|
||||
{ dg-options "-Wall -Wattribute-alias=2" } */
|
||||
|
||||
#define ATTR(...) __attribute__ ((__VA_ARGS__))
|
||||
|
||||
|
||||
void
|
||||
target_no_nothrow (void) /* { dg-message ".alias_nothrow. target declared here" } */
|
||||
{ }
|
||||
|
||||
ATTR (alias ("target_no_nothrow"), nothrow) void
|
||||
alias_nothrow (void); /* { dg-warning ".alias_nothrow. specifies more restrictive attribute than its target .target_no_nothrow.: .nothrow." } */
|
||||
|
||||
|
||||
ATTR (pure) int
|
||||
alias_pure (void);
|
||||
|
||||
int
|
||||
target_no_pure (void) /* { dg-message ".alias_pure. target declared here" } */
|
||||
{ return 0; }
|
||||
|
||||
ATTR (alias ("target_no_pure")) int
|
||||
alias_pure (void); /* { dg-warning ".alias_pure. specifies more restrictive attribute than its target .target_no_pure.: .pure." } */
|
||||
|
||||
|
||||
ATTR (const) int
|
||||
alias_const (void);
|
||||
|
||||
int
|
||||
target_pure (void) /* { dg-message ".alias_const. target declared here" } */
|
||||
{ return 0; }
|
||||
|
||||
ATTR (alias ("target_pure")) int
|
||||
alias_const (void); /* { dg-warning ".alias_const. specifies more restrictive attribute than its target .target_pure.: .const." } */
|
||||
|
||||
|
||||
/* There is no obvious relationship between the attributes on an ifunc
|
||||
resolver and those on its aliases. Verify that mismatches between
|
||||
aliases and ifunc resolvers do not trigger warnings. */
|
||||
|
||||
typedef int F (void);
|
||||
|
||||
ATTR (pure, leaf) F* resolve_to_const (void)
|
||||
{ return alias_const; }
|
||||
|
||||
ATTR (ifunc ("resolve_to_const")) F alias_no_const_ifunc;
|
||||
ATTR (const, ifunc ("resolve_to_const")) F alias_const_ifunc;
|
||||
ATTR (ifunc ("resolve_to_const")) int alias_no_leaf_ifunc (void);
|
95
gcc/testsuite/gcc.dg/Wmissing-attributes.c
Normal file
95
gcc/testsuite/gcc.dg/Wmissing-attributes.c
Normal file
@ -0,0 +1,95 @@
|
||||
/* PR middle-end/81824 - Warn for missing attributes with function aliases
|
||||
{ dg-do compile }
|
||||
{ dg-options "-Wall" } */
|
||||
|
||||
#define ATTR(list) __attribute__ (list)
|
||||
|
||||
|
||||
int alias_no_const (void);
|
||||
|
||||
ATTR ((const)) int
|
||||
target_const (void) /* { dg-message ".alias_no_const. target declared here" } */
|
||||
{ return 0; }
|
||||
|
||||
ATTR ((alias ("target_const"))) int
|
||||
alias_no_const (void); /* { dg-warning ".alias_no_const. specifies less restrictive attribute than its target .target_const.: .const." } */
|
||||
|
||||
|
||||
ATTR ((alloc_size (1), malloc)) void*
|
||||
target_malloc (int n) /* { dg-message ".alias_no_malloc. target declared here" } */
|
||||
{ return __builtin_malloc (n); }
|
||||
|
||||
ATTR ((alias ("target_malloc"))) void*
|
||||
alias_no_malloc (int); /* { dg-warning ".alias_no_malloc. specifies less restrictive attributes than its target .target_malloc.: .alloc_size., .malloc." } */
|
||||
|
||||
|
||||
ATTR ((leaf)) int
|
||||
target_leaf (void) /* { dg-message ".alias_no_leaf. target declared here" } */
|
||||
{ return 0; }
|
||||
|
||||
ATTR ((alias ("target_leaf"))) int
|
||||
alias_no_leaf (void); /* { dg-warning ".alias_no_leaf. specifies less restrictive attribute than its target .target_leaf.: .leaf." } */
|
||||
|
||||
|
||||
/* Verify that attributes noclone, noinline, and noipa on the target
|
||||
don't cause a warning for aliases without the attribute. */
|
||||
|
||||
ATTR ((noclone)) int
|
||||
target_noclone (void)
|
||||
{ return 0; }
|
||||
|
||||
ATTR ((alias ("target_noclone"))) int
|
||||
alias_no_noclone (void);
|
||||
|
||||
|
||||
ATTR ((noipa)) int
|
||||
target_noipa (void)
|
||||
{ return 0; }
|
||||
|
||||
ATTR ((alias ("target_noipa"))) int
|
||||
alias_no_noipa (void);
|
||||
|
||||
|
||||
ATTR ((noinline)) int
|
||||
target_noinline (void)
|
||||
{ return 0; }
|
||||
|
||||
ATTR ((alias ("target_noinline"))) int
|
||||
alias_no_noinline (void);
|
||||
|
||||
|
||||
ATTR ((nothrow)) int
|
||||
target_nothrow (void) /* { dg-message ".alias_no_nothrow. target declared here" } */
|
||||
{ return 0; }
|
||||
|
||||
ATTR ((alias ("target_nothrow"))) int
|
||||
alias_no_nothrow (void); /* { dg-warning ".alias_no_nothrow. specifies less restrictive attribute than its target .target_nothrow.: .nothrow." } */
|
||||
|
||||
|
||||
/* Verify that attribute weak on the target doesn't cause and isn't
|
||||
mentioned in a warning for aliases without the attribute. */
|
||||
|
||||
ATTR ((weak)) int
|
||||
target_weak (void)
|
||||
{ return 0; }
|
||||
|
||||
ATTR ((alias ("target_weak"))) int
|
||||
alias_no_weak (void);
|
||||
|
||||
|
||||
ATTR ((nothrow, weak)) int
|
||||
target_nothrow_weak (void) /* { dg-message ".alias_nothrow_no_weak. target declared here" } */
|
||||
{ return 0; }
|
||||
|
||||
ATTR ((alias ("target_nothrow_weak"))) int
|
||||
alias_nothrow_no_weak (void); /* { dg-warning ".alias_nothrow_no_weak. specifies less restrictive attribute than its target .target_nothrow_weak.: .nothrow." } */
|
||||
|
||||
|
||||
/* Verify that __typeof__ doesn't include attributes. */
|
||||
|
||||
ATTR ((cold)) int
|
||||
target_cold (void)
|
||||
{ return 0; }
|
||||
|
||||
__typeof__ (target_cold) ATTR ((alias ("target_cold")))
|
||||
alias_cold; /* { dg-warning ".alias_cold. specifies less restrictive attribute than its target .target_cold.: .cold." } */
|
209
gcc/testsuite/gcc.dg/attr-copy-2.c
Normal file
209
gcc/testsuite/gcc.dg/attr-copy-2.c
Normal file
@ -0,0 +1,209 @@
|
||||
/* PR middle-end/81824 - Warn for missing attributes with function aliases
|
||||
Exercise attribute copy for functions.
|
||||
{ dg-do compile }
|
||||
{ dg-options "-O2 -Wall" } */
|
||||
|
||||
#define Assert(expr) typedef char AssertExpr[2 * !!(expr) - 1]
|
||||
|
||||
#define ATTR(list) __attribute__ (list)
|
||||
|
||||
/* Verify that referencing a symbol with no attributes is accepted
|
||||
with no diagnostics. */
|
||||
|
||||
void ref0 (void);
|
||||
|
||||
ATTR ((copy (ref0))) void
|
||||
f0 (void);
|
||||
|
||||
/* Verify that referencing a symbol using the address-of and dereferencing
|
||||
operators is also accepted with no diagnostics. */
|
||||
|
||||
ATTR ((copy (&ref0))) void f1 (void);
|
||||
ATTR ((copy (*ref0))) void f2 (void);
|
||||
|
||||
/* Verify that referencing a symbol of a different kind than that
|
||||
of the one the attribute is applied to is diagnosed. */
|
||||
|
||||
int v0; /* { dg-message "symbol .v0. referenced by .f3. declared here" } */
|
||||
|
||||
ATTR ((copy (v0))) void
|
||||
f3 (void); /* { dg-warning ".copy. attribute ignored on a declaration of a different kind than referenced symbol" } */
|
||||
|
||||
void f4 (void); /* { dg-message "symbol .f4. referenced by .v1. declared here" } */
|
||||
|
||||
ATTR ((copy (f4))) int
|
||||
v1; /* { dg-warning ".copy. attribute ignored on a declaration of a different kind than referenced symbol" } */
|
||||
|
||||
|
||||
ATTR ((copy (v0 + 1)))
|
||||
void f5 (void); /* { dg-warning ".copy. attribute ignored on a declaration of a different kind than referenced symbol" } */
|
||||
|
||||
void f6 (void);
|
||||
|
||||
ATTR ((copy (f6 - 1)))
|
||||
int v1; /* { dg-warning ".copy. attribute ignored on a declaration of a different kind than referenced symbol" } */
|
||||
|
||||
|
||||
|
||||
/* Verify that circular references of the copy function attribute
|
||||
are handled gracefully (i.e., not by getting into an infinite
|
||||
recursion) by issuing a diagnostic. */
|
||||
|
||||
void xref1 (void); /* { dg-message "previous declaration here" } */
|
||||
ATTR ((copy (xref1))) void
|
||||
xref1 (void); /* { dg-warning ".copy. attribute ignored on a redeclaration of the referenced symbol" } */
|
||||
ATTR ((copy (xref1))) void
|
||||
xref1 (void); /* { dg-warning ".copy. attribute ignored on a redeclaration of the referenced symbol" } */
|
||||
ATTR ((copy (xref1), copy (xref1))) void
|
||||
xref1 (void); /* { dg-warning ".copy. attribute ignored on a redeclaration of the referenced symbol" } */
|
||||
|
||||
|
||||
/* Use attribute noreturn to verify that circular references propagate
|
||||
atttibutes as expected, and unlike in the self-referential instances
|
||||
above, without a warning. Also use the address-of operator to make
|
||||
sure it doesn't change anything. */
|
||||
|
||||
ATTR ((noreturn)) void xref2 (void);
|
||||
ATTR ((copy (xref2))) void xref3 (void);
|
||||
ATTR ((copy (&xref3))) void xref4 (void);
|
||||
ATTR ((copy (xref4))) void xref5 (void);
|
||||
ATTR ((copy (&xref5))) void xref6 (void);
|
||||
ATTR ((copy (xref6))) void xref7 (void);
|
||||
ATTR ((copy (&xref7))) void xref8 (void);
|
||||
ATTR ((copy (xref8))) void xref9 (void);
|
||||
ATTR ((copy (&xref9))) void xref2 (void);
|
||||
|
||||
int call_ref2 (void) { xref2 (); }
|
||||
int call_ref3 (void) { xref3 (); }
|
||||
int call_ref4 (void) { xref4 (); }
|
||||
int call_ref5 (void) { xref5 (); }
|
||||
int call_ref6 (void) { xref6 (); }
|
||||
int call_ref7 (void) { xref7 (); }
|
||||
int call_ref8 (void) { xref8 (); }
|
||||
int call_ref9 (void) { xref9 (); }
|
||||
|
||||
|
||||
/* Verify that copying attributes from multiple symbols into one works
|
||||
as expected. */
|
||||
|
||||
ATTR ((malloc)) void*
|
||||
xref10 (void);
|
||||
|
||||
ATTR ((alloc_size (1)))
|
||||
void* xref11 (int);
|
||||
|
||||
ATTR ((copy (xref10), copy (xref11)))
|
||||
void* xref12 (int);
|
||||
|
||||
void* call_xref12 (void)
|
||||
{
|
||||
void *p = xref12 (3);
|
||||
__builtin___strcpy_chk (p, "123", __builtin_object_size (p, 0)); /* { dg-warning "\\\[-Wstringop-overflow=]" } */
|
||||
return p;
|
||||
}
|
||||
|
||||
|
||||
/* Verify that attribute exclusions apply. */
|
||||
|
||||
ATTR ((const)) int
|
||||
fconst (void);
|
||||
|
||||
ATTR ((pure)) int
|
||||
fpure (void); /* { dg-message "previous declaration here" } */
|
||||
|
||||
ATTR ((copy (fconst), copy (fpure))) int
|
||||
fconst_pure (void); /* { dg-warning "ignoring attribute .pure. because it conflicts with attribute .const." } */
|
||||
|
||||
|
||||
/* Also verify that the note in the exclusion warning points to
|
||||
the declaration from which the conflicting attribute is copied.
|
||||
The wording in the note could be improved but it's the same as
|
||||
in ordinary exclusions so making it different between the two
|
||||
would take some API changes. */
|
||||
|
||||
ATTR ((const)) int
|
||||
gconst (void); /* { dg-message "previous declaration here" } */
|
||||
|
||||
ATTR ((pure, copy (gconst))) int
|
||||
gpure_const (void); /* { dg-warning "ignoring attribute .const. because it conflicts with attribute .pure." } */
|
||||
|
||||
|
||||
/* Verify that attribute deprecated isn't copied (but referencing
|
||||
the deprecated declaration still triggers a warning). */
|
||||
|
||||
ATTR ((deprecated)) void
|
||||
fdeprecated (void); /* { dg-message "declared here" } */
|
||||
|
||||
/* Unlike in most other instance the warning below is on the line
|
||||
with the copy attribute that references the deprecated function. */
|
||||
ATTR ((copy (fdeprecated))) /* { dg-warning "\\\[-Wdeprecated-declarations]" } */
|
||||
int fcurrent (void);
|
||||
|
||||
ATTR ((copy (fcurrent))) int
|
||||
fcurrent2 (void);
|
||||
|
||||
int call_fcurrent (void) { return fcurrent () + fcurrent2 (); }
|
||||
|
||||
|
||||
/* Verify that attributes are copied on a declaration using __typeof__
|
||||
and that -Wmissing-attributes is not issued. */
|
||||
|
||||
ATTR ((cold)) int
|
||||
target_cold (void)
|
||||
{ return 0; }
|
||||
|
||||
__typeof__ (target_cold) ATTR ((copy (target_cold), alias ("target_cold")))
|
||||
alias_cold; /* { dg-bogus "\\\[-Wmissing-attributes]." } */
|
||||
|
||||
|
||||
/* Verify that attribute alias is not copied. This also indirectly
|
||||
verifies that attribute copy itself isn't copied. */
|
||||
|
||||
ATTR ((noreturn)) void fnoret (void) { __builtin_abort (); }
|
||||
ATTR ((alias ("fnoret"), copy (fnoret))) void fnoret_alias (void);
|
||||
ATTR ((copy (fnoret_alias))) void fnoret2 (void) { __builtin_exit (1); }
|
||||
|
||||
/* Expect no warning below. */
|
||||
int call_noret (void) { fnoret2 (); }
|
||||
|
||||
|
||||
/* Verify that attribute nonnull (which is part of a function type,
|
||||
even when it's specified on a function declaration) is copied to
|
||||
the alias from its target. Expect no warning about the alias
|
||||
specifying less restrictive attributes than its target, but do
|
||||
verify that passing a null to the alias triggers -Wnonnull. */
|
||||
|
||||
ATTR ((nonnull))
|
||||
void* ftarget_nonnull (void *p) { return p; }
|
||||
|
||||
ATTR ((alias ("ftarget_nonnull"), copy (ftarget_nonnull)))
|
||||
void* falias_nonnull (void*);
|
||||
|
||||
void call_falias_nonnull (void)
|
||||
{
|
||||
falias_nonnull (0); /* { dg-warning "-Wnonnull" } */
|
||||
}
|
||||
|
||||
/* Same as above but for malloc. Also verify that the attribute
|
||||
on the alias is used by -Wstringop-overflow. */
|
||||
|
||||
ATTR ((malloc))
|
||||
void* ftarget_malloc (void) { return __builtin_malloc (1); }
|
||||
|
||||
ATTR ((alias ("ftarget_malloc"), copy (ftarget_malloc)))
|
||||
void* falias_malloc (void);
|
||||
|
||||
void* call_falias_malloc (void)
|
||||
{
|
||||
char *p = falias_malloc ();
|
||||
__builtin___strcpy_chk (p, "123", __builtin_object_size (p, 0)); /* { dg-warning "\\\[-Wstringop-overflow=]" } */
|
||||
return p;
|
||||
}
|
||||
|
||||
/* Same as above but for nothrow. */
|
||||
|
||||
ATTR ((nothrow))
|
||||
void ftarget_nothrow (void) { }
|
||||
|
||||
ATTR ((alias ("ftarget_nothrow"), copy (ftarget_nothrow)))
|
||||
void falias_nothrow (void);
|
75
gcc/testsuite/gcc.dg/attr-copy-3.c
Normal file
75
gcc/testsuite/gcc.dg/attr-copy-3.c
Normal file
@ -0,0 +1,75 @@
|
||||
/* PR middle-end/81824 - Warn for missing attributes with function aliases
|
||||
Exercise attribute copy for variables.
|
||||
{ dg-do compile }
|
||||
{ dg-options "-O2 -Wall" } */
|
||||
|
||||
#define ATTR(list) __attribute__ (list)
|
||||
|
||||
/* Verify that referencing a symbol with no attributes is accepted
|
||||
with no diagnostics. */
|
||||
|
||||
int ref0;
|
||||
|
||||
ATTR ((copy (ref0))) long
|
||||
var0;
|
||||
|
||||
/* Verify that referencing a symbol using the address-of and dereferencing
|
||||
operators is also accepted with no diagnostics. */
|
||||
|
||||
ATTR ((copy (&ref0))) void* ptr0;
|
||||
ATTR ((copy (*&ref0))) int arr[1];
|
||||
|
||||
/* Verify that referencing a symbol of a different kind than that
|
||||
of the one the attribute is applied to is diagnosed. */
|
||||
|
||||
int ref1; /* { dg-message "previous declaration here" } */
|
||||
|
||||
ATTR ((copy (ref1))) int
|
||||
ref1; /* { dg-warning ".copy. attribute ignored on a redeclaration of the referenced symbol " } */
|
||||
|
||||
|
||||
/* Verify that circular references of the copy variable attribute
|
||||
are handled gracefully (i.e., not by getting into an infinite
|
||||
recursion) by issuing a diagnostic. */
|
||||
|
||||
char xref1;
|
||||
ATTR ((copy (xref1))) char
|
||||
xref1; /* { dg-warning ".copy. attribute ignored on a redeclaration of the referenced symbol" } */
|
||||
ATTR ((copy (xref1))) char
|
||||
xref1; /* { dg-warning ".copy. attribute ignored on a redeclaration of the referenced symbol" } */
|
||||
ATTR ((copy (xref1), copy (xref1))) char
|
||||
xref1; /* { dg-warning ".copy. attribute ignored on a redeclaration of the referenced symbol" } */
|
||||
|
||||
|
||||
/* Use attribute unused to verify that circular references propagate
|
||||
atttibutes as expected (expect no warnings the circular reference
|
||||
or for any of the unused symbols). Also use the address-of operator
|
||||
to make sure it doesn't change anything. */
|
||||
|
||||
static ATTR ((unused)) int xref2;
|
||||
static ATTR ((copy (xref2))) int xref3;
|
||||
static ATTR ((copy (&xref3))) int xref4;
|
||||
static ATTR ((copy (xref4))) int xref5;
|
||||
static ATTR ((copy (&xref5))) int xref6;
|
||||
static ATTR ((copy (xref6))) int xref7;
|
||||
static ATTR ((copy (&xref7))) int xref8;
|
||||
static ATTR ((copy (xref8))) int xref9;
|
||||
static ATTR ((copy (&xref9))) int xref2;
|
||||
|
||||
/* Verify that attribute exclusions apply. */
|
||||
|
||||
ATTR ((common)) int common_var;
|
||||
ATTR ((nocommon)) double nocommon_var;
|
||||
|
||||
ATTR ((copy (common_var), copy (nocommon_var))) long
|
||||
common_copy; /* { dg-warning "ignoring attribute .nocommon. because it conflicts with attribute .common." } */
|
||||
|
||||
|
||||
/* Verify that attribute deprecated isn't copied. */
|
||||
|
||||
ATTR ((deprecated)) char deprecated_var;
|
||||
|
||||
ATTR ((copy (deprecated_var))) int current_var; /* { dg-warning "\\\[-Wdeprecated-declarations]" } */
|
||||
ATTR ((copy (current_var))) int current_var_2;
|
||||
|
||||
int return_current_vars (void) { return current_var + current_var_2; }
|
61
gcc/testsuite/gcc.dg/attr-copy-4.c
Normal file
61
gcc/testsuite/gcc.dg/attr-copy-4.c
Normal file
@ -0,0 +1,61 @@
|
||||
/* PR middle-end/81824 - Warn for missing attributes with function aliases
|
||||
Exercise attribute copy for types.
|
||||
{ dg-do compile }
|
||||
{ dg-options "-O2 -Wall" } */
|
||||
|
||||
#define Assert(expr) typedef char AssertExpr[2 * !!(expr) - 1]
|
||||
|
||||
#define ATTR(list) __attribute__ (list)
|
||||
|
||||
/* Use attribute packed to verify that type attributes are copied
|
||||
from one type to another. */
|
||||
|
||||
struct ATTR ((packed)) PackedA { int i; char c; };
|
||||
|
||||
Assert (__alignof (struct PackedA) == 1);
|
||||
|
||||
struct ATTR ((copy ((struct PackedA*)0))) PackedB { long i; char c; };
|
||||
|
||||
Assert (__alignof (struct PackedA) == __alignof (struct PackedB));
|
||||
|
||||
struct PackedMember
|
||||
{
|
||||
char c;
|
||||
ATTR ((copy ((struct PackedB*)0))) double packed_mem;
|
||||
};
|
||||
|
||||
Assert (__alignof (struct PackedMember) == 1);
|
||||
|
||||
|
||||
extern const struct PackedA packed;
|
||||
|
||||
struct Unpacked { int i; char c; };
|
||||
Assert (__alignof (struct Unpacked) > 1);
|
||||
|
||||
/* Verify that copying the packed attribute to the declaration
|
||||
of an object is ignored with a warning. (There should be
|
||||
a way to copy just the subset of attributes from a type that
|
||||
aren't ignored and won't cause a warning, maybe via attribute
|
||||
copy_except or something like that.) */
|
||||
extern ATTR ((copy ((struct PackedA*)0))) const struct Unpacked
|
||||
unpacked; /* { dg-warning ".packed. attribute ignored" } */
|
||||
|
||||
Assert (__alignof (packed) == 1);
|
||||
Assert (__alignof (unpacked) == __alignof (struct Unpacked));
|
||||
|
||||
|
||||
|
||||
/* Verify that attribute deprecated isn't copied (but referencing
|
||||
the deprecated type in the copy attribute still triggers a warning). */
|
||||
|
||||
struct ATTR ((aligned (8), deprecated))
|
||||
AlignedDeprecated { char c; };
|
||||
|
||||
struct ATTR ((copy ((struct AlignedDeprecated *)0))) /* { dg-warning "\\\[-Wdeprecated-declarations]" } */
|
||||
AlignedCopy { short s; };
|
||||
|
||||
Assert (__alignof (struct AlignedCopy) == 8);
|
||||
|
||||
struct AlignedCopy aligned_copy;
|
||||
|
||||
Assert (__alignof (aligned_copy) == 8);
|
33
gcc/testsuite/gcc.dg/attr-copy.c
Normal file
33
gcc/testsuite/gcc.dg/attr-copy.c
Normal file
@ -0,0 +1,33 @@
|
||||
/* PR middle-end/81824 - Warn for missing attributes with function aliases
|
||||
Exercise error handling for attribute copy.
|
||||
{ dg-do compile }
|
||||
{ dg-options "-O2 -Wall" } */
|
||||
|
||||
#define ATTR(list) __attribute__ (list)
|
||||
|
||||
/* Verify incorrect numbers of arguments. */
|
||||
ATTR ((copy)) void
|
||||
fno_args (void); /* { dg-error "wrong number of arguments specified for .copy. attribute" } */
|
||||
|
||||
ATTR ((copy ())) void
|
||||
fno_args2 (void); /* { dg-error "wrong number of arguments specified for .copy. attribute" } */
|
||||
|
||||
ATTR ((copy (fno_args, fno_args))) void
|
||||
fmlti_args (void); /* { dg-error "wrong number of arguments specified for .copy. attribute" } */
|
||||
|
||||
/* Verify that referencing an undeclared symbol is rejected with an error. */
|
||||
|
||||
ATTR ((copy (foobar))) /* { dg-error ".foobar. undeclared" } */
|
||||
void fundeclared (void);
|
||||
|
||||
/* Verify that using a string argument triggers a descriptive error
|
||||
(given attributes like alias and weakref using a string is a likely
|
||||
mistake). */
|
||||
|
||||
ATTR ((copy ("foobar")))
|
||||
void fstring (void); /* { dg-error ".copy. attribute argument cannot be a string" } */
|
||||
|
||||
/* Ditto for an integer. */
|
||||
|
||||
ATTR ((copy (123)))
|
||||
void fnumber (void); /* { dg-error ".copy. attribute argument cannot be a constant arithmetic expression" } */
|
@ -1149,16 +1149,26 @@ extern int gomp_test_nest_lock_25 (omp_nest_lock_25_t *) __GOMP_NOTHROW;
|
||||
# define attribute_hidden
|
||||
#endif
|
||||
|
||||
#if __GNUC__ >= 9
|
||||
# define HAVE_ATTRIBUTE_COPY
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_ATTRIBUTE_COPY
|
||||
# define attribute_copy(arg) __attribute__ ((copy (arg)))
|
||||
#else
|
||||
# define attribute_copy(arg)
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_ATTRIBUTE_ALIAS
|
||||
# define strong_alias(fn, al) \
|
||||
extern __typeof (fn) al __attribute__ ((alias (#fn)));
|
||||
extern __typeof (fn) al __attribute__ ((alias (#fn))) attribute_copy (fn);
|
||||
|
||||
# define ialias_ulp ialias_str1(__USER_LABEL_PREFIX__)
|
||||
# define ialias_str1(x) ialias_str2(x)
|
||||
# define ialias_str2(x) #x
|
||||
# define ialias(fn) \
|
||||
extern __typeof (fn) gomp_ialias_##fn \
|
||||
__attribute__ ((alias (#fn))) attribute_hidden;
|
||||
__attribute__ ((alias (#fn))) attribute_hidden attribute_copy (fn);
|
||||
# define ialias_redirect(fn) \
|
||||
extern __typeof (fn) fn __asm__ (ialias_ulp "gomp_ialias_" #fn) attribute_hidden;
|
||||
# define ialias_call(fn) gomp_ialias_ ## fn
|
||||
|
Loading…
Reference in New Issue
Block a user