diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 49f3e383901..e0225c58ffb 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,18 @@ +2018-11-09 Martin Sebor + + 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 * config/arm/parsecpu.awk (/alias/): Tighten invisible alias diff --git a/gcc/attribs.c b/gcc/attribs.c index 8b721274d3b..dfe13ad7a94 100644 --- a/gcc/attribs.c +++ b/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 diff --git a/gcc/attribs.h b/gcc/attribs.h index c277e1b752d..5b76c4c400c 100644 --- a/gcc/attribs.h +++ b/gcc/attribs.h @@ -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. */ diff --git a/gcc/c-family/ChangeLog b/gcc/c-family/ChangeLog index ab45ebe8937..6ce25c97783 100644 --- a/gcc/c-family/ChangeLog +++ b/gcc/c-family/ChangeLog @@ -1,3 +1,8 @@ +2018-11-09 Martin Sebor + + PR middle-end/81824 + * c-attribs.c (handle_copy_attribute): New function. + 2018-11-09 Martin Sebor PR c/87795 diff --git a/gcc/c-family/c-attribs.c b/gcc/c-family/c-attribs.c index 336c63ec0dd..5e3b127b144 100644 --- a/gcc/c-family/c-attribs.c +++ b/gcc/c-family/c-attribs.c @@ -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; +} diff --git a/gcc/cgraphunit.c b/gcc/cgraphunit.c index cb8495417b5..2f0b70ff850 100644 --- a/gcc/cgraphunit.c +++ b/gcc/cgraphunit.c @@ -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_, "% 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 (); diff --git a/gcc/common.opt b/gcc/common.opt index 98e8eb03ef3..081a9428a32 100644 --- a/gcc/common.opt +++ b/gcc/common.opt @@ -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 diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog index 1497dab500f..7cb57b8bf3f 100644 --- a/gcc/cp/ChangeLog +++ b/gcc/cp/ChangeLog @@ -1,3 +1,9 @@ +2018-11-09 Martin Sebor + + PR middle-end/81824 + * pt.c (warn_spec_missing_attributes): Move code to attribs.c. + Call decls_mismatched_attributes. + 2018-11-08 Jakub Jelinek * constexpr.c (potential_constant_expression_1): Handle OMP_DEPOBJ. diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c index fe330cd29d5..d4ae76a89f4 100644 --- a/gcc/cp/pt.c +++ b/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; diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi index 985d0418aa4..5ce506907d7 100644 --- a/gcc/doc/extend.texi +++ b/gcc/doc/extend.texi @@ -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 diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi index 4ea93a7b5cb..e071f125efe 100644 --- a/gcc/doc/invoke.texi +++ b/gcc/doc/invoke.texi @@ -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 diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index a27efa66456..1556e8a7d19 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,13 @@ +2018-11-09 Martin Sebor + + 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 PR c/87795 diff --git a/gcc/testsuite/gcc.dg/Wattribute-alias.c b/gcc/testsuite/gcc.dg/Wattribute-alias.c new file mode 100644 index 00000000000..175e40b4764 --- /dev/null +++ b/gcc/testsuite/gcc.dg/Wattribute-alias.c @@ -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); diff --git a/gcc/testsuite/gcc.dg/Wmissing-attributes.c b/gcc/testsuite/gcc.dg/Wmissing-attributes.c new file mode 100644 index 00000000000..2a981828a29 --- /dev/null +++ b/gcc/testsuite/gcc.dg/Wmissing-attributes.c @@ -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." } */ diff --git a/gcc/testsuite/gcc.dg/attr-copy-2.c b/gcc/testsuite/gcc.dg/attr-copy-2.c new file mode 100644 index 00000000000..39e5f087dea --- /dev/null +++ b/gcc/testsuite/gcc.dg/attr-copy-2.c @@ -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); diff --git a/gcc/testsuite/gcc.dg/attr-copy-3.c b/gcc/testsuite/gcc.dg/attr-copy-3.c new file mode 100644 index 00000000000..88e5e5ed215 --- /dev/null +++ b/gcc/testsuite/gcc.dg/attr-copy-3.c @@ -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; } diff --git a/gcc/testsuite/gcc.dg/attr-copy-4.c b/gcc/testsuite/gcc.dg/attr-copy-4.c new file mode 100644 index 00000000000..7020bad7aa9 --- /dev/null +++ b/gcc/testsuite/gcc.dg/attr-copy-4.c @@ -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); diff --git a/gcc/testsuite/gcc.dg/attr-copy.c b/gcc/testsuite/gcc.dg/attr-copy.c new file mode 100644 index 00000000000..27cd7bc5be6 --- /dev/null +++ b/gcc/testsuite/gcc.dg/attr-copy.c @@ -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" } */ diff --git a/libgomp/libgomp.h b/libgomp/libgomp.h index 828e9b0095b..7eacb45db66 100644 --- a/libgomp/libgomp.h +++ b/libgomp/libgomp.h @@ -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