On 10/22/2018 04:08 PM, Jason Merrill wrote:
On 10/13/18 8:19 PM, Martin Sebor wrote:
+ oper = cp_parser_type_id (parser);
+ parser->in_type_id_in_expr_p = saved_in_type_id_in_expr_p;
+
+ if (cp_parser_parse_definitely (parser))
+ {
+ /* If all went well, set OPER to the type. */
+ cp_decl_specifier_seq decl_specs;
+
+ /* Build a trivial decl-specifier-seq. */
+ clear_decl_specs (&decl_specs);
+ decl_specs.type = oper;
+
+ /* Call grokdeclarator to figure out what type this is. */
+ oper = grokdeclarator (NULL,
+ &decl_specs,
+ TYPENAME,
+ /*initialized=*/0,
+ /*attrlist=*/NULL);
+ }
Doesn't grokdeclarator here give you back the same type you already had
from cp_parser_type_id? The latter already calls grokdeclarator.
I don't know why cp_parser_sizeof_operand does this, either. Try
removing it from both places?
You're right, the call in cp_parser_has_attribute_expression
was unnecessary. cp_parser_sizeof_operand still needs it.
+ /* Consume the comma if it's there. */
+ if (!cp_parser_require (parser, CPP_COMMA, RT_COMMA))
+ {
+ parens.require_close (parser);
I think you want cp_parser_skip_to_closing_parenthesis for error
recovery, rather than require_close.
Thanks, the error messages look slightly better that way (there
are fewer of them), although still not as good as in C or other
compilers in some cases.
+ if (tree attr = cp_parser_gnu_attribute_list (parser,
/*exactly_one=*/true))
+ {
+ if (oper != error_mark_node)
+ {
+ /* Fold constant expressions used in attributes first. */
+ cp_check_const_attributes (attr);
+
+ /* Finally, see if OPER has been declared with ATTR. */
+ ret = has_attribute (atloc, oper, attr, default_conversion);
+ }
+ }
+ else
+ {
+ error_at (atloc, "expected identifier");
+ cp_parser_skip_to_closing_parenthesis (parser, true, false,
false);
+ }
+
+ parens.require_close (parser);
I think the require_close should be in the valid case, since *skip*
already consumes a closing paren.
Ah, I need to make it consume the paren by passing true as the last
argument. With that it works.
+is valuated. The @var{type-or-expression} argument is subject to the
same
evaluated
Thanks for the review.
Attached is an updated patch with the fixes above.
Martin
gcc/c/ChangeLog:
* c-parser.c (c_parser_has_attribute_expression): New function.
(c_parser_attribute): New function.
(c_parser_attributes): Move code into c_parser_attribute.
(c_parser_unary_expression): Handle RID_HAS_ATTRIBUTE_EXPRESSION.
gcc/c-family/ChangeLog:
* c-attribs.c (type_for_vector_size): New function.
(type_valid_for_vector_size): Same.
(handle_vector_size_attribute): Move code to the functions above
and call them.
(validate_attribute, has_attribute): New functions.
* c-common.h (has_attribute): Declare.
(rid): Add RID_HAS_ATTRIBUTE_EXPRESSION.
* c-common.c (c_common_resword): Same.
gcc/cp/ChangeLog:
* cp-tree.h (cp_check_const_attributes): Declare.
* decl2.c (cp_check_const_attributes): Declare extern.
* parser.c (cp_parser_has_attribute_expression): New function.
(cp_parser_unary_expression): Handle RID_HAS_ATTRIBUTE_EXPRESSION.
(cp_parser_gnu_attribute_list): Add argument.
gcc/ChangeLog:
* doc/extend.texi (Other Builtins): Add __builtin_has_attribute.
gcc/testsuite/ChangeLog:
* c-c++-common/builtin-has-attribute-2.c: New test.
* c-c++-common/builtin-has-attribute-3.c: New test.
* c-c++-common/builtin-has-attribute-4.c: New test.
* c-c++-common/builtin-has-attribute.c: New test.
* gcc.dg/builtin-has-attribute.c: New test.
* gcc/testsuite/gcc.target/i386/builtin-has-attribute.c: New test.
diff --git a/gcc/c-family/c-attribs.c b/gcc/c-family/c-attribs.c
index 3a88766..c0a1bb5 100644
--- a/gcc/c-family/c-attribs.c
+++ b/gcc/c-family/c-attribs.c
@@ -3128,34 +3128,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:
@@ -3171,8 +3148,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)
@@ -3183,14 +3177,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)
@@ -3200,14 +3218,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);
@@ -3678,3 +3726,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/c-family/c-common.c b/gcc/c-family/c-common.c
index 10a8bc2..7d139be 100644
--- a/gcc/c-family/c-common.c
+++ b/gcc/c-family/c-common.c
@@ -372,6 +372,7 @@ const struct c_common_resword c_common_reswords[] =
RID_BUILTIN_CALL_WITH_STATIC_CHAIN, D_CONLY },
{ "__builtin_choose_expr", RID_CHOOSE_EXPR, D_CONLY },
{ "__builtin_complex", RID_BUILTIN_COMPLEX, D_CONLY },
+ { "__builtin_has_attribute", RID_BUILTIN_HAS_ATTRIBUTE, 0 },
{ "__builtin_launder", RID_BUILTIN_LAUNDER, D_CXXONLY },
{ "__builtin_shuffle", RID_BUILTIN_SHUFFLE, 0 },
{ "__builtin_tgmath", RID_BUILTIN_TGMATH, D_CONLY },
diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h
index 9e86876..6447ecb 100644
--- a/gcc/c-family/c-common.h
+++ b/gcc/c-family/c-common.h
@@ -103,6 +103,7 @@ enum rid
RID_EXTENSION, RID_IMAGPART, RID_REALPART, RID_LABEL, RID_CHOOSE_EXPR,
RID_TYPES_COMPATIBLE_P, RID_BUILTIN_COMPLEX, RID_BUILTIN_SHUFFLE,
RID_BUILTIN_TGMATH,
+ RID_BUILTIN_HAS_ATTRIBUTE,
RID_DFLOAT32, RID_DFLOAT64, RID_DFLOAT128,
/* TS 18661-3 keywords, in the same sequence as the TI_* values. */
@@ -1333,6 +1334,7 @@ extern void maybe_suggest_missing_token_insertion (rich_location *richloc,
enum cpp_ttype token_type,
location_t prev_token_loc);
extern tree braced_list_to_string (tree, tree);
+extern bool has_attribute (location_t, tree, tree, tree (*)(tree));
#if CHECKING_P
namespace selftest {
diff --git a/gcc/c/c-parser.c b/gcc/c/c-parser.c
index 1f173fc..090eef2 100644
--- a/gcc/c/c-parser.c
+++ b/gcc/c/c-parser.c
@@ -1439,6 +1439,8 @@ static vec<tree, va_gc> *c_parser_expr_list (c_parser *, bool, bool,
vec<tree, va_gc> **, location_t *,
tree *, vec<location_t> *,
unsigned int * = NULL);
+static struct c_expr c_parser_has_attribute_expression (c_parser *);
+
static void c_parser_oacc_declare (c_parser *);
static void c_parser_oacc_enter_exit_data (c_parser *, bool);
static void c_parser_oacc_update (c_parser *);
@@ -4290,7 +4292,126 @@ c_parser_attribute_any_word (c_parser *parser)
type), a reserved word storage class specifier, type specifier or
type qualifier. ??? This still leaves out most reserved keywords
(following the old parser), shouldn't we include them, and why not
- allow identifiers declared as types to start the arguments? */
+ allow identifiers declared as types to start the arguments?
+ When EXPECT_COMMA is true, expect the attribute to be preceded
+ by a comma and fail if it isn't.
+ When EMPTY_OK is true, allow and consume any number of consecutive
+ commas with no attributes in between. */
+
+static tree
+c_parser_attribute (c_parser *parser, tree attrs,
+ bool expect_comma = false, bool empty_ok = true)
+{
+ bool comma_first = c_parser_next_token_is (parser, CPP_COMMA);
+ if (!comma_first
+ && !c_parser_next_token_is (parser, CPP_NAME)
+ && !c_parser_next_token_is (parser, CPP_KEYWORD))
+ return NULL_TREE;
+
+ while (c_parser_next_token_is (parser, CPP_COMMA))
+ {
+ c_parser_consume_token (parser);
+ if (!empty_ok)
+ return attrs;
+ }
+
+ tree attr_name = c_parser_attribute_any_word (parser);
+ if (attr_name == NULL_TREE)
+ return NULL_TREE;
+
+ attr_name = canonicalize_attr_name (attr_name);
+ c_parser_consume_token (parser);
+
+ tree attr;
+ if (c_parser_next_token_is_not (parser, CPP_OPEN_PAREN))
+ {
+ if (expect_comma && !comma_first)
+ {
+ /* A comma is missing between the last attribute on the chain
+ and this one. */
+ c_parser_skip_until_found (parser, CPP_CLOSE_PAREN,
+ "expected %<)%>");
+ return error_mark_node;
+ }
+ attr = build_tree_list (attr_name, NULL_TREE);
+ /* Add this attribute to the list. */
+ attrs = chainon (attrs, attr);
+ return attrs;
+ }
+ c_parser_consume_token (parser);
+
+ vec<tree, va_gc> *expr_list;
+ tree attr_args;
+ /* Parse the attribute contents. If they start with an
+ identifier which is followed by a comma or close
+ parenthesis, then the arguments start with that
+ identifier; otherwise they are an expression list.
+ In objective-c the identifier may be a classname. */
+ if (c_parser_next_token_is (parser, CPP_NAME)
+ && (c_parser_peek_token (parser)->id_kind == C_ID_ID
+ || (c_dialect_objc ()
+ && c_parser_peek_token (parser)->id_kind
+ == C_ID_CLASSNAME))
+ && ((c_parser_peek_2nd_token (parser)->type == CPP_COMMA)
+ || (c_parser_peek_2nd_token (parser)->type
+ == CPP_CLOSE_PAREN))
+ && (attribute_takes_identifier_p (attr_name)
+ || (c_dialect_objc ()
+ && c_parser_peek_token (parser)->id_kind
+ == C_ID_CLASSNAME)))
+ {
+ tree arg1 = c_parser_peek_token (parser)->value;
+ c_parser_consume_token (parser);
+ if (c_parser_next_token_is (parser, CPP_CLOSE_PAREN))
+ attr_args = build_tree_list (NULL_TREE, arg1);
+ else
+ {
+ tree tree_list;
+ c_parser_consume_token (parser);
+ expr_list = c_parser_expr_list (parser, false, true,
+ NULL, NULL, NULL, NULL);
+ tree_list = build_tree_list_vec (expr_list);
+ attr_args = tree_cons (NULL_TREE, arg1, tree_list);
+ release_tree_vector (expr_list);
+ }
+ }
+ else
+ {
+ if (c_parser_next_token_is (parser, CPP_CLOSE_PAREN))
+ attr_args = NULL_TREE;
+ else
+ {
+ expr_list = c_parser_expr_list (parser, false, true,
+ NULL, NULL, NULL, NULL);
+ attr_args = build_tree_list_vec (expr_list);
+ release_tree_vector (expr_list);
+ }
+ }
+
+ attr = build_tree_list (attr_name, attr_args);
+ if (c_parser_next_token_is (parser, CPP_CLOSE_PAREN))
+ c_parser_consume_token (parser);
+ else
+ {
+ parser->lex_untranslated_string = false;
+ c_parser_skip_until_found (parser, CPP_CLOSE_PAREN,
+ "expected %<)%>");
+ return error_mark_node;
+ }
+
+ if (expect_comma && !comma_first)
+ {
+ /* A comma is missing between the last attribute on the chain
+ and this one. */
+ c_parser_skip_until_found (parser, CPP_CLOSE_PAREN,
+ "expected %<)%>");
+ return error_mark_node;
+ }
+
+ /* Add this attribute to the list. */
+ attrs = chainon (attrs, attr);
+ return attrs;
+}
static tree
c_parser_attributes (c_parser *parser)
@@ -4315,97 +4436,19 @@ c_parser_attributes (c_parser *parser)
c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, NULL);
return attrs;
}
- /* Parse the attribute list. */
- while (c_parser_next_token_is (parser, CPP_COMMA)
- || c_parser_next_token_is (parser, CPP_NAME)
- || c_parser_next_token_is (parser, CPP_KEYWORD))
+ /* Parse the attribute list. Require a comma between successive
+ (possibly empty) attributes. */
+ for (bool expect_comma = false; ; expect_comma = true)
{
- tree attr, attr_name, attr_args;
- vec<tree, va_gc> *expr_list;
- if (c_parser_next_token_is (parser, CPP_COMMA))
- {
- c_parser_consume_token (parser);
- continue;
- }
-
- attr_name = c_parser_attribute_any_word (parser);
- if (attr_name == NULL)
+ /* Parse a single attribute. */
+ tree attr = c_parser_attribute (parser, attrs, expect_comma);
+ if (attr == error_mark_node)
+ return attrs;
+ if (!attr)
break;
- attr_name = canonicalize_attr_name (attr_name);
- c_parser_consume_token (parser);
- if (c_parser_next_token_is_not (parser, CPP_OPEN_PAREN))
- {
- attr = build_tree_list (attr_name, NULL_TREE);
- /* Add this attribute to the list. */
- attrs = chainon (attrs, attr);
- /* If the next token isn't a comma, we're done. */
- if (!c_parser_next_token_is (parser, CPP_COMMA))
- break;
- continue;
- }
- c_parser_consume_token (parser);
- /* Parse the attribute contents. If they start with an
- identifier which is followed by a comma or close
- parenthesis, then the arguments start with that
- identifier; otherwise they are an expression list.
- In objective-c the identifier may be a classname. */
- if (c_parser_next_token_is (parser, CPP_NAME)
- && (c_parser_peek_token (parser)->id_kind == C_ID_ID
- || (c_dialect_objc ()
- && c_parser_peek_token (parser)->id_kind
- == C_ID_CLASSNAME))
- && ((c_parser_peek_2nd_token (parser)->type == CPP_COMMA)
- || (c_parser_peek_2nd_token (parser)->type
- == CPP_CLOSE_PAREN))
- && (attribute_takes_identifier_p (attr_name)
- || (c_dialect_objc ()
- && c_parser_peek_token (parser)->id_kind
- == C_ID_CLASSNAME)))
- {
- tree arg1 = c_parser_peek_token (parser)->value;
- c_parser_consume_token (parser);
- if (c_parser_next_token_is (parser, CPP_CLOSE_PAREN))
- attr_args = build_tree_list (NULL_TREE, arg1);
- else
- {
- tree tree_list;
- c_parser_consume_token (parser);
- expr_list = c_parser_expr_list (parser, false, true,
- NULL, NULL, NULL, NULL);
- tree_list = build_tree_list_vec (expr_list);
- attr_args = tree_cons (NULL_TREE, arg1, tree_list);
- release_tree_vector (expr_list);
- }
- }
- else
- {
- if (c_parser_next_token_is (parser, CPP_CLOSE_PAREN))
- attr_args = NULL_TREE;
- else
- {
- expr_list = c_parser_expr_list (parser, false, true,
- NULL, NULL, NULL, NULL);
- attr_args = build_tree_list_vec (expr_list);
- release_tree_vector (expr_list);
- }
- }
+ attrs = attr;
+ }
- attr = build_tree_list (attr_name, attr_args);
- if (c_parser_next_token_is (parser, CPP_CLOSE_PAREN))
- c_parser_consume_token (parser);
- else
- {
- parser->lex_untranslated_string = false;
- c_parser_skip_until_found (parser, CPP_CLOSE_PAREN,
- "expected %<)%>");
- return attrs;
- }
- /* Add this attribute to the list. */
- attrs = chainon (attrs, attr);
- /* If the next token isn't a comma, we're done. */
- if (!c_parser_next_token_is (parser, CPP_COMMA))
- break;
- }
/* Look for the two `)' tokens. */
if (c_parser_next_token_is (parser, CPP_CLOSE_PAREN))
c_parser_consume_token (parser);
@@ -7240,6 +7283,8 @@ c_parser_unary_expression (c_parser *parser)
return c_parser_sizeof_expression (parser);
case RID_ALIGNOF:
return c_parser_alignof_expression (parser);
+ case RID_BUILTIN_HAS_ATTRIBUTE:
+ return c_parser_has_attribute_expression (parser);
case RID_EXTENSION:
c_parser_consume_token (parser);
ext = disable_extension_diagnostics ();
@@ -7439,6 +7484,123 @@ c_parser_alignof_expression (c_parser *parser)
}
}
+/* Parse the __builtin_has_attribute ([expr|type], attribute-spec)
+ expression. */
+
+static struct c_expr
+c_parser_has_attribute_expression (c_parser *parser)
+{
+ gcc_assert (c_parser_next_token_is_keyword (parser,
+ RID_BUILTIN_HAS_ATTRIBUTE));
+ c_parser_consume_token (parser);
+
+ c_inhibit_evaluation_warnings++;
+
+ matching_parens parens;
+ if (!parens.require_open (parser))
+ {
+ c_inhibit_evaluation_warnings--;
+ in_typeof--;
+
+ struct c_expr result;
+ result.set_error ();
+ result.original_code = ERROR_MARK;
+ result.original_type = NULL;
+ return result;
+ }
+
+ /* Treat the type argument the same way as in typeof for the purposes
+ of warnings. FIXME: Generalize this so the warning refers to
+ __builtin_has_attribute rather than typeof. */
+ in_typeof++;
+
+ /* The first operand: one of DECL, EXPR, or TYPE. */
+ tree oper = NULL_TREE;
+ if (c_parser_next_tokens_start_typename (parser, cla_prefer_id))
+ {
+ struct c_type_name *tname = c_parser_type_name (parser);
+ in_typeof--;
+ if (tname)
+ {
+ oper = groktypename (tname, NULL, NULL);
+ pop_maybe_used (variably_modified_type_p (oper, NULL_TREE));
+ }
+ }
+ else
+ {
+ struct c_expr cexpr = c_parser_expr_no_commas (parser, NULL);
+ c_inhibit_evaluation_warnings--;
+ in_typeof--;
+ if (cexpr.value != error_mark_node)
+ {
+ mark_exp_read (cexpr.value);
+ oper = cexpr.value;
+ tree etype = TREE_TYPE (oper);
+ bool was_vm = variably_modified_type_p (etype, NULL_TREE);
+ /* This is returned with the type so that when the type is
+ evaluated, this can be evaluated. */
+ if (was_vm)
+ oper = c_fully_fold (oper, false, NULL);
+ pop_maybe_used (was_vm);
+ }
+ }
+
+ struct c_expr result;
+ result.original_code = ERROR_MARK;
+ result.original_type = NULL;
+
+ if (!c_parser_require (parser, CPP_COMMA, "expected %<,%>"))
+ {
+ /* Consume the closing parenthesis if that's the next token
+ in the likely case the built-in was invoked with fewer
+ than two arguments. */
+ if (c_parser_next_token_is (parser, CPP_CLOSE_PAREN))
+ c_parser_consume_token (parser);
+ c_inhibit_evaluation_warnings--;
+ result.set_error ();
+ return result;
+ }
+
+ parser->lex_untranslated_string = true;
+
+ location_t atloc = c_parser_peek_token (parser)->location;
+ /* Parse a single attribute. Require no leading comma and do not
+ allow empty attributes. */
+ tree attr = c_parser_attribute (parser, NULL_TREE, false, false);
+
+ parser->lex_untranslated_string = false;
+
+ if (c_parser_next_token_is (parser, CPP_CLOSE_PAREN))
+ c_parser_consume_token (parser);
+ else
+ {
+ c_parser_error (parser, "expected identifier");
+ c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, NULL);
+
+ result.set_error ();
+ return result;
+ }
+
+ if (!attr)
+ {
+ error_at (atloc, "expected identifier");
+ c_parser_skip_until_found (parser, CPP_CLOSE_PAREN,
+ "expected %<)%>");
+ result.set_error ();
+ return result;
+ }
+
+ result.original_code = INTEGER_CST;
+ result.original_type = boolean_type_node;
+
+ if (has_attribute (atloc, oper, attr, default_conversion))
+ result.value = boolean_true_node;
+ else
+ result.value = boolean_false_node;
+
+ return result;
+}
+
/* Helper function to read arguments of builtins which are interfaces
for the middle-end nodes like COMPLEX_EXPR, VEC_PERM_EXPR and
others. The name of the builtin is passed using BNAME parameter.
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index efbdad8..8e8af94 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -6428,6 +6428,7 @@ extern int parm_index (tree);
extern tree vtv_start_verification_constructor_init_function (void);
extern tree vtv_finish_verification_constructor_init_function (tree);
extern bool cp_omp_mappable_type (tree);
+extern void cp_check_const_attributes (tree);
/* in error.c */
extern const char *type_as_string (tree, int);
diff --git a/gcc/cp/decl2.c b/gcc/cp/decl2.c
index a5ad0ee..baf303f 100644
--- a/gcc/cp/decl2.c
+++ b/gcc/cp/decl2.c
@@ -1378,7 +1378,7 @@ cp_reconstruct_complex_type (tree type, tree bottom)
/* Replaces any constexpr expression that may be into the attributes
arguments with their reduced value. */
-static void
+void
cp_check_const_attributes (tree attributes)
{
if (attributes == error_mark_node)
diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index 6696f17..f07df78 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -2049,6 +2049,8 @@ static cp_expr cp_parser_unary_expression
(cp_parser *, cp_id_kind * = NULL, bool = false, bool = false, bool = false);
static enum tree_code cp_parser_unary_operator
(cp_token *);
+static tree cp_parser_has_attribute_expression
+ (cp_parser *);
static tree cp_parser_new_expression
(cp_parser *);
static vec<tree, va_gc> *cp_parser_new_placement
@@ -2380,7 +2382,7 @@ static tree cp_parser_attributes_opt
static tree cp_parser_gnu_attributes_opt
(cp_parser *);
static tree cp_parser_gnu_attribute_list
- (cp_parser *);
+ (cp_parser *, bool = false);
static tree cp_parser_std_attribute
(cp_parser *, tree);
static tree cp_parser_std_attribute_spec
@@ -8065,6 +8067,9 @@ cp_parser_unary_expression (cp_parser *parser, cp_id_kind * pidk,
return ret_expr;
}
+ case RID_BUILTIN_HAS_ATTRIBUTE:
+ return cp_parser_has_attribute_expression (parser);
+
case RID_NEW:
return cp_parser_new_expression (parser);
@@ -8362,6 +8367,129 @@ cp_parser_unary_operator (cp_token* token)
}
}
+/* Parse a __builtin_has_attribute([expr|type], attribute-spec) expression.
+ Returns a representation of the expression. */
+
+static tree
+cp_parser_has_attribute_expression (cp_parser *parser)
+{
+ location_t start_loc = cp_lexer_peek_token (parser->lexer)->location;
+
+ /* Consume the __builtin_has_attribute token. */
+ cp_lexer_consume_token (parser->lexer);
+
+ matching_parens parens;
+ if (!parens.require_open (parser))
+ return error_mark_node;
+
+ /* Types cannot be defined in a `sizeof' expression. Save away the
+ old message. */
+ const char *saved_message = parser->type_definition_forbidden_message;
+ /* And create the new one. */
+ const int kwd = RID_BUILTIN_HAS_ATTRIBUTE;
+ char *tmp = concat ("types may not be defined in %<",
+ IDENTIFIER_POINTER (ridpointers[kwd]),
+ "%> expressions", NULL);
+ parser->type_definition_forbidden_message = tmp;
+
+ /* The restrictions on constant-expressions do not apply inside
+ sizeof expressions. */
+ bool saved_integral_constant_expression_p
+ = parser->integral_constant_expression_p;
+ bool saved_non_integral_constant_expression_p
+ = parser->non_integral_constant_expression_p;
+ parser->integral_constant_expression_p = false;
+
+ /* Do not actually evaluate the expression. */
+ ++cp_unevaluated_operand;
+ ++c_inhibit_evaluation_warnings;
+
+ tree oper = NULL_TREE;
+
+ /* We can't be sure yet whether we're looking at a type-id or an
+ expression. */
+ cp_parser_parse_tentatively (parser);
+
+ bool saved_in_type_id_in_expr_p = parser->in_type_id_in_expr_p;
+ parser->in_type_id_in_expr_p = true;
+ /* Look for the type-id. */
+ oper = cp_parser_type_id (parser);
+ parser->in_type_id_in_expr_p = saved_in_type_id_in_expr_p;
+
+ if (cp_parser_parse_definitely (parser))
+ {
+ /* If all went well, set OPER to the type. */
+ cp_decl_specifier_seq decl_specs;
+
+ /* Build a trivial decl-specifier-seq. */
+ clear_decl_specs (&decl_specs);
+ decl_specs.type = oper;
+ }
+
+ /* If the type-id production did not work out, then we must be
+ looking at the unary-expression production. */
+ if (!oper || oper == error_mark_node)
+ oper = cp_parser_unary_expression (parser);
+
+ /* Go back to evaluating expressions. */
+ --cp_unevaluated_operand;
+ --c_inhibit_evaluation_warnings;
+
+ /* Free the message we created. */
+ free (tmp);
+ /* And restore the old one. */
+ parser->type_definition_forbidden_message = saved_message;
+ parser->integral_constant_expression_p
+ = saved_integral_constant_expression_p;
+ parser->non_integral_constant_expression_p
+ = saved_non_integral_constant_expression_p;
+
+ /* Consume the comma if it's there. */
+ if (!cp_parser_require (parser, CPP_COMMA, RT_COMMA))
+ {
+ cp_parser_skip_to_closing_parenthesis (parser, false, false,
+ /*consume_paren=*/true);
+ return error_mark_node;
+ }
+
+ /* Parse the attribute specification. */
+ bool ret = false;
+ location_t atloc = cp_lexer_peek_token (parser->lexer)->location;
+ if (tree attr = cp_parser_gnu_attribute_list (parser, /*exactly_one=*/true))
+ {
+ if (oper != error_mark_node)
+ {
+ /* Fold constant expressions used in attributes first. */
+ cp_check_const_attributes (attr);
+
+ /* Finally, see if OPER has been declared with ATTR. */
+ ret = has_attribute (atloc, oper, attr, default_conversion);
+ }
+
+ parens.require_close (parser);
+ }
+ else
+ {
+ error_at (atloc, "expected identifier");
+ cp_parser_skip_to_closing_parenthesis (parser, true, false, true);
+ }
+
+ /* Construct a location e.g. :
+ __builtin_has_attribute (oper, attr)
+ ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ with start == caret at the start of the built-in token,
+ and with the endpoint at the final closing paren. */
+ location_t finish_loc
+ = cp_lexer_previous_token (parser->lexer)->location;
+ location_t compound_loc
+ = make_location (start_loc, start_loc, finish_loc);
+
+ cp_expr ret_expr (ret ? boolean_true_node : boolean_false_node);
+ ret_expr.set_location (compound_loc);
+ ret_expr = ret_expr.maybe_add_location_wrapper ();
+ return ret_expr;
+}
+
/* Parse a new-expression.
new-expression:
@@ -25154,7 +25282,7 @@ cp_parser_gnu_attributes_opt (cp_parser* parser)
the arguments, if any. */
static tree
-cp_parser_gnu_attribute_list (cp_parser* parser)
+cp_parser_gnu_attribute_list (cp_parser* parser, bool exactly_one /* = false */)
{
tree attribute_list = NULL_TREE;
bool save_translate_strings_p = parser->translate_strings_p;
@@ -25221,9 +25349,9 @@ cp_parser_gnu_attribute_list (cp_parser* parser)
token = cp_lexer_peek_token (parser->lexer);
}
- /* Now, look for more attributes. If the next token isn't a
- `,', we're done. */
- if (token->type != CPP_COMMA)
+ /* Unless EXACTLY_ONE is set look for more attributes.
+ If the next token isn't a `,', we're done. */
+ if (exactly_one || token->type != CPP_COMMA)
break;
/* Consume the comma and keep going. */
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index 8ffb0cd..05eb0ac 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -11069,6 +11069,7 @@ is called and the @var{flag} argument passed to it.
@findex __builtin_call_with_static_chain
@findex __builtin_extend_pointer
@findex __builtin_fpclassify
+@findex __builtin_has_attribute
@findex __builtin_isfinite
@findex __builtin_isnormal
@findex __builtin_isgreater
@@ -11726,6 +11727,45 @@ check its compatibility with @var{size}.
@end deftypefn
+@deftypefn {Built-in Function} bool __builtin_has_attribute (@var{type-or-expression}, @var{attribute})
+The @code{__builtin_has_attribute} function evaluates to an integer constant
+expression equal to @code{true} if the symbol or type referenced by
+the @var{type-or-expression} argument has been declared with
+the @var{attribute} referenced by the second argument. Neither argument
+is evaluated. The @var{type-or-expression} argument is subject to the same
+restrictions as the argument to @code{typeof} (@pxref{Typeof}). The
+@var{attribute} argument is an attribute name optionally followed by
+arguments. Both forms of attribute names---with and without double
+leading and trailing underscores---are recognized. See @xref{Attribute Syntax}
+for details. When no attribute arguments are specified for an attribute
+that expects one or more arguments the function returns @code{true} if
+@var{type-or-expression} has been declared with the attribute regardless
+of the attribute argument values. Arguments provided for an attribute
+that expects some are validated and matched up to the provided number.
+The function returns true if all provided arguments match. For example,
+the first call to the function below evaluates to @code{true} because
+@code{x} is declared with the @code{aligned} attribute but the second
+call evaluates to @code{false} because @code{x} is declared
+@code{aligned (8)} and not @code{aligned (4)}.
+
+@smallexample
+__attribute__ ((aligned (8))) int x;
+_Static_assert (__builtin_has_attribute (x, aligned), "aligned");
+_Static_assert (!__builtin_has_attribute (x, aligned (4)), "aligned (4)");
+@end smallexample
+
+Due to a limitation the @code{__builtin_has_attribute} function returns
+@code{false} for the @code{mode} attribute even if the type or variable
+referenced by the @var{type-or-expression} argument was declared with one.
+The function is also not supported with labels, and in C with enumerators.
+
+Note that unlike the @code{__has_attribute} preprocessor operator which
+is suitable for use in @code{#if} preprocessing directives
+@code{__builtin_has_attribute} is an intrinsic function that is not
+recognized in such contexts.
+
+@end deftypefn
+
@deftypefn {Built-in Function} @var{type} __builtin_speculation_safe_value (@var{type} val, @var{type} failval)
This built-in function can be used to help mitigate against unsafe