From 98f08eb8939735c1e9cbc1ec5cadebe79e935c90 Mon Sep 17 00:00:00 2001 From: Martin Sebor Date: Wed, 21 Nov 2018 02:50:02 +0000 Subject: [PATCH] c-parser.c (c_parser_has_attribute_expression): New function. 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. From-SVN: r266335 --- gcc/ChangeLog | 4 + gcc/c-family/ChangeLog | 11 + gcc/c-family/c-attribs.c | 2 +- gcc/c-family/c-common.c | 1 + gcc/c-family/c-common.h | 2 + gcc/c/ChangeLog | 7 + gcc/c/c-parser.c | 340 +++++++++++++----- gcc/cp/ChangeLog | 8 + gcc/cp/cp-tree.h | 1 + gcc/cp/decl2.c | 2 +- gcc/cp/parser.c | 130 ++++++- gcc/doc/extend.texi | 41 +++ gcc/testsuite/ChangeLog | 9 + .../c-c++-common/builtin-has-attribute-2.c | 206 +++++++++++ .../c-c++-common/builtin-has-attribute-3.c | 314 ++++++++++++++++ .../c-c++-common/builtin-has-attribute-4.c | 285 +++++++++++++++ .../c-c++-common/builtin-has-attribute.c | 60 ++++ gcc/testsuite/gcc.dg/builtin-has-attribute.c | 45 +++ .../gcc.target/i386/builtin-has-attribute.c | 54 +++ 19 files changed, 1426 insertions(+), 96 deletions(-) create mode 100644 gcc/testsuite/c-c++-common/builtin-has-attribute-2.c create mode 100644 gcc/testsuite/c-c++-common/builtin-has-attribute-3.c create mode 100644 gcc/testsuite/c-c++-common/builtin-has-attribute-4.c create mode 100644 gcc/testsuite/c-c++-common/builtin-has-attribute.c create mode 100644 gcc/testsuite/gcc.dg/builtin-has-attribute.c create mode 100644 gcc/testsuite/gcc.target/i386/builtin-has-attribute.c diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 3075648e439..229e1b0e167 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,7 @@ +2018-11-20 Martin Sebor + + * doc/extend.texi (Other Builtins): Add __builtin_has_attribute. + 2018-11-20 Jan Hubicka PR lto/84044 diff --git a/gcc/c-family/ChangeLog b/gcc/c-family/ChangeLog index db465421051..6d60cf02ed5 100644 --- a/gcc/c-family/ChangeLog +++ b/gcc/c-family/ChangeLog @@ -1,3 +1,14 @@ +2018-11-20 Martin Sebor + + * 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. + 2018-11-16 Jason Merrill * c-lex.c (c_common_has_attribute): Handle likely/unlikely. diff --git a/gcc/c-family/c-attribs.c b/gcc/c-family/c-attribs.c index 88b24cd6cd4..373381df68e 100644 --- a/gcc/c-family/c-attribs.c +++ b/gcc/c-family/c-attribs.c @@ -4176,7 +4176,7 @@ has_attribute (location_t atloc, tree t, tree attr, tree (*convert)(tree)) if (expr && DECL_P (expr)) found_match = TREE_READONLY (expr); } - else if (!strcmp ("const", namestr)) + else if (!strcmp ("pure", namestr)) { if (expr && DECL_P (expr)) found_match = DECL_PURE_P (expr); diff --git a/gcc/c-family/c-common.c b/gcc/c-family/c-common.c index 4034b64bbe2..9d51815532d 100644 --- a/gcc/c-family/c-common.c +++ b/gcc/c-family/c-common.c @@ -376,6 +376,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 4aa61426114..4187343c0b3 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. */ @@ -1355,6 +1356,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/ChangeLog b/gcc/c/ChangeLog index 706839cd1cb..0b73df94db1 100644 --- a/gcc/c/ChangeLog +++ b/gcc/c/ChangeLog @@ -1,3 +1,10 @@ +2018-11-20 Martin Sebor + + * 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. + 2018-11-15 Martin Sebor PR c/83656 diff --git a/gcc/c/c-parser.c b/gcc/c/c-parser.c index 5f69e9d52fd..afc40710e83 100644 --- a/gcc/c/c-parser.c +++ b/gcc/c/c-parser.c @@ -1445,6 +1445,8 @@ static vec *c_parser_expr_list (c_parser *, bool, bool, vec **, location_t *, tree *, vec *, 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 *); @@ -4313,7 +4315,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 *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) @@ -4338,97 +4459,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 *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); @@ -7263,6 +7306,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 (); @@ -7462,6 +7507,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/ChangeLog b/gcc/cp/ChangeLog index a5ee86e7810..a8e054442d6 100644 --- a/gcc/cp/ChangeLog +++ b/gcc/cp/ChangeLog @@ -1,3 +1,11 @@ +2018-11-20 Martin Sebor + + * 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. + 2018-11-20 Jakub Jelinek PR c++/88110 diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index edb94940098..111a123bb34 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -6491,6 +6491,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 7686a908631..ffc0d0d6ec4 100644 --- a/gcc/cp/decl2.c +++ b/gcc/cp/decl2.c @@ -1368,7 +1368,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 292cce15676..0617f5636cd 100644 --- a/gcc/cp/parser.c +++ b/gcc/cp/parser.c @@ -2048,6 +2048,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 *cp_parser_new_placement @@ -2381,7 +2383,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 @@ -8110,6 +8112,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); @@ -8407,6 +8412,121 @@ 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; + + cp_parser_parse_definitely (parser); + + /* 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: @@ -25376,7 +25496,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; @@ -25443,9 +25563,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 0dff5e86ed1..858b2d15440 100644 --- a/gcc/doc/extend.texi +++ b/gcc/doc/extend.texi @@ -11140,6 +11140,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 @@ -11797,6 +11798,46 @@ 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 +a comma-separated list of arguments enclosed in parentheses. 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 @code{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 diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index d3ebbf0bcce..43831fcfbb4 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,12 @@ +2018-11-20 Martin Sebor + + * 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. + 2018-11-20 Jan Hubicka PR lto/84044 diff --git a/gcc/testsuite/c-c++-common/builtin-has-attribute-2.c b/gcc/testsuite/c-c++-common/builtin-has-attribute-2.c new file mode 100644 index 00000000000..0f692ffe444 --- /dev/null +++ b/gcc/testsuite/c-c++-common/builtin-has-attribute-2.c @@ -0,0 +1,206 @@ +/* Verify __builtin_has_attribute return value for types. + { dg-do compile } + { dg-options "-Wall -ftrack-macro-expansion=0" } + { dg-options "-Wall -Wno-narrowing -Wno-unused-local-typedefs -ftrack-macro-expansion=0" { target c++ } } */ + +#define ATTR(...) __attribute__ ((__VA_ARGS__)) + +#define A(expect, sym, attr) \ + typedef int Assert [1 - 2 * !(__builtin_has_attribute (sym, attr) == expect)] + +struct ATTR (packed) Packed { char c; int i; }; + +void fvoid (void); +struct Packed fpacked (void); + +union OrdinaryUnion { void *p; int i; }; +union ATTR (transparent_union) TransparentUnion { void *p; int i; }; + +/* Exercise __builtin_has_attribute with the first argument that + is a type. */ + +void test_type (int n) +{ + /* Verify both forms of the attribute spelling. Unlike the attribute + keyword that can be spelled three ways (with either leading or + trailing underscores, or with both), attribute names can only be + spelled two ways. */ + A (0, int, aligned); + A (0, int, __aligned__); + + A (0, int, aligned (1)); + A (0, int, aligned (2)); + A (0, int[1], aligned); + A (0, int[1], aligned (2)); + A (0, int[n], aligned); + A (0, int[n], aligned (4)); + + /* Again, verify both forms of the attribute spelling. */ + A (1, ATTR (aligned) char, aligned); + A (1, ATTR (aligned (2)) short, aligned); + A (1, ATTR (aligned (4)) int, __aligned__); + + A (0, int ATTR (aligned (4)), aligned (2)); + A (0, int ATTR (aligned (2)), aligned (4)); + /* GCC retains both attributes in the */ + A (0, int ATTR (aligned (2), aligned (4)), aligned (2)); + A (1, int ATTR (aligned (2), aligned (4)), aligned (4)); + /* The following fails due to bug 87524. + A (1, int ATTR (aligned (4), aligned (2))), aligned (4)); */ + A (0, int ATTR (aligned (4), aligned (2)), aligned (8)); + + A (1, int ATTR (aligned (8)), aligned (1 + 7)); + + enum { eight = 8 }; + A (1, int ATTR (aligned (8)), aligned (eight)); + A (1, int ATTR (aligned (eight)), aligned (1 + 7)); + + struct NotPacked { char c; int i; }; + A (0, struct NotPacked, packed); + A (1, struct Packed, packed); + + /* Exercise types returned from a function. */ + A (0, fvoid (), packed); + A (1, fpacked (), packed); + + struct ATTR (aligned (2), packed) Aligned2Packed { char c; int i; }; + A (1, struct Aligned2Packed, aligned); + A (1, struct Aligned2Packed, aligned (2)); + A (0, struct Aligned2Packed, aligned (4)); + A (1, struct Aligned2Packed, packed); + + A (0, int, may_alias); + A (1, ATTR (may_alias) int, may_alias); + + A (0, char, warn_if_not_aligned (1)); + A (0, char, warn_if_not_aligned (2)); + + A (1, ATTR (warn_if_not_aligned (2)) char, warn_if_not_aligned); + A (0, ATTR (warn_if_not_aligned (2)) char, warn_if_not_aligned (1)); + A (1, ATTR (warn_if_not_aligned (2)) char, warn_if_not_aligned (2)); + A (0, ATTR (warn_if_not_aligned (2)) char, warn_if_not_aligned (4)); + + A (0, union OrdinaryUnion, transparent_union); + + A (1, union TransparentUnion, transparent_union); + A (1, const union TransparentUnion, transparent_union); +} + +/* Exercise __builtin_has_attribute with the first argument that + is a typedef. */ + +void test_typedef (int n) +{ + typedef char A1[1]; + A (0, A1, aligned); + A (0, A1, aligned (1)); + A (0, A1, aligned (2)); + + typedef char An[n]; + A (0, An, aligned); + A (0, An, aligned (1)); + A (0, An, aligned (2)); + + typedef ATTR (aligned (8)) short AI8; + A (1, AI8, aligned); + A (0, AI8, aligned (4)); + A (1, AI8, aligned (8)); + A (0, AI8, aligned (16)); + + A (1, const AI8, aligned); + A (1, const volatile AI8, aligned); + + typedef ATTR (aligned (2), aligned (8), aligned (16)) int AI16; + A (1, AI16, aligned); + A (0, AI16, aligned (1)); + A (0, AI16, aligned (2)); + A (0, AI16, aligned (4)); + A (0, AI16, aligned (8)); + A (1, AI16, aligned (16)); + A (0, AI16, aligned (32)); + + typedef const AI16 CAI16; + A (1, CAI16, aligned); + A (0, CAI16, aligned (1)); + A (1, CAI16, aligned (16)); + + typedef int I; + A (0, I, may_alias); + A (0, AI8, may_alias); + + typedef ATTR (may_alias) int MAI; + A (1, MAI, may_alias); + + typedef ATTR (aligned (4), may_alias) char A4MAC; + A (0, A4MAC, aligned (0)); + A (0, A4MAC, aligned (1)); + A (0, A4MAC, aligned (2)); + A (1, A4MAC, aligned (4)); + A (0, A4MAC, aligned (8)); + A (1, A4MAC, may_alias); + + typedef ATTR (may_alias, aligned (8)) char A8MAC; + A (1, A8MAC, aligned); + A (0, A8MAC, aligned (0)); + A (0, A8MAC, aligned (1)); + A (0, A8MAC, aligned (2)); + A (0, A8MAC, aligned (4)); + A (1, A8MAC, aligned (8)); + A (0, A8MAC, aligned (16)); + A (1, A8MAC, may_alias); + + typedef ATTR (may_alias) const AI8 CMAI8; + A (1, CMAI8, aligned); + A (1, CMAI8, may_alias); + A (0, CMAI8, aligned (4)); + A (1, CMAI8, aligned (8)); + + typedef void Fnull (void*, void*, void*); + A (0, Fnull, nonnull); + A (0, Fnull, nonnull (1)); + A (0, Fnull, nonnull (2)); + A (0, Fnull, nonnull (3)); + + typedef ATTR (nonnull) Fnull Fnonnull; + A (1, Fnonnull, nonnull); + A (1, Fnonnull, nonnull (1)); + A (1, Fnonnull, nonnull (2)); + A (1, Fnonnull, nonnull (3)); + + typedef ATTR (nonnull (2)) void Fnonnull_2 (void*, void*, void*); + A (0, Fnonnull_2, nonnull); + A (0, Fnonnull_2, nonnull (1)); + A (1, Fnonnull_2, nonnull (2)); + A (0, Fnonnull_2, nonnull (3)); + + typedef ATTR (nonnull (1), nonnull (2), nonnull (3)) + void Fnonnull_1_2_3 (void*, void*, void*); + + /* The following fails because the built-in doesn't recognize that + a single nonnull with no arguments is the same as one nonnull for + each function parameter. Disable the testing for now. + A (1, Fnonnull_1_2_3, nonnull); + */ + A (1, Fnonnull_1_2_3, nonnull (1)); + A (1, Fnonnull_1_2_3, nonnull (2)); + A (1, Fnonnull_1_2_3, nonnull (3)); + + typedef void Freturns (void); + A (0, Fnull, noreturn); + A (0, Freturns, noreturn); + + typedef ATTR (warn_if_not_aligned (8)) char CWA8; + A (0, CWA8, warn_if_not_aligned (2)); + A (0, CWA8, warn_if_not_aligned (4)); + A (1, CWA8, warn_if_not_aligned (8)); + A (0, CWA8, warn_if_not_aligned (16)); + + typedef union OrdinaryUnion OrdUnion; + A (0, OrdUnion, transparent_union); + + /* The attribute is ignored on typedefs but GCC fails to diagnose + it (see bug ). */ + typedef union ATTR (transparent_union) + OrdinaryUnion TransUnion; /* { dg-warning "\\\[-Wattributes" "pr87578" { xfail { ! { c++ } } } } */ + A (0, TransUnion, transparent_union); +} diff --git a/gcc/testsuite/c-c++-common/builtin-has-attribute-3.c b/gcc/testsuite/c-c++-common/builtin-has-attribute-3.c new file mode 100644 index 00000000000..237dc72fb9f --- /dev/null +++ b/gcc/testsuite/c-c++-common/builtin-has-attribute-3.c @@ -0,0 +1,314 @@ +/* Verify __builtin_has_attribute return value for functions. + { dg-do compile } + { dg-options "-Wall -ftrack-macro-expansion=0" } + { dg-options "-Wall -Wno-narrowing -Wno-unused-local-typedefs -ftrack-macro-expansion=0" { target c++ } } */ + +#define ATTR(...) __attribute__ ((__VA_ARGS__)) + +void fnone (void); + +ATTR (aligned) void faligned (void); +ATTR (aligned (1)) void faligned_1 (void); +ATTR (aligned (2)) void faligned_2 (void); +ATTR (aligned (4)) void faligned_4 (void); +ATTR (aligned (8)) void faligned_8 (void); + +ATTR (alloc_size (1)) void* falloc_size_1 (int, int); +ATTR (alloc_size (2)) void* falloc_size_2 (int, int); +ATTR (alloc_size (2, 4)) void* falloc_size_2_4 (int, int, int, int); + +ATTR (alloc_align (1)) void* falloc_align_1 (int, int); +ATTR (alloc_align (2)) void* falloc_align_2 (int, int); +ATTR (alloc_align (1), alloc_size (2)) void* falloc_align_1_size_2 (int, int); +ATTR (alloc_align (2), alloc_size (1)) void* falloc_align_2_size_1 (int, int); + +#if __cplusplus +extern "C" +#endif +ATTR (noreturn) void fnoreturn (void) { __builtin_abort (); } + +ATTR (alias ("fnoreturn")) void falias (void); + +#define A(expect, sym, attr) \ + typedef int Assert [1 - 2 * !(__builtin_has_attribute (sym, attr) == expect)] + +void test_aligned (void) +{ + A (0, fnone, aligned); + A (0, fnone, aligned (0)); + A (0, fnone, aligned (1)); + A (0, fnone, aligned (2)); + A (0, fnone, aligned (4)); + A (0, fnone, aligned (8)); + A (0, fnone, aligned (16)); + + A (1, faligned, aligned); + A (0, faligned, aligned (0)); + A (0, faligned, aligned (1)); + A (0, faligned, aligned (2)); + + A (1, faligned_1, aligned); + A (0, faligned_1, aligned (0)); + A (1, faligned_1, aligned (1)); + A (0, faligned_1, aligned (2)); + A (0, faligned_1, aligned (4)); + + A (1, faligned_2, aligned); + A (0, faligned_2, aligned (0)); + A (0, faligned_2, aligned (1)); + A (1, faligned_2, aligned (2)); + A (0, faligned_2, aligned (4)); +} + + +void test_alloc_align (void) +{ + A (0, fnone, alloc_align); + A (0, falloc_size_1, alloc_align); + A (1, falloc_align_1, alloc_align); + A (1, falloc_align_2, alloc_align); + + A (0, fnone, alloc_align (1)); /* { dg-warning "\\\[-Wattributes" } */ + A (0, falloc_size_1, alloc_align (1)); + A (1, falloc_align_1, alloc_align (1)); + A (0, falloc_align_2, alloc_align (1)); + A (1, falloc_align_2, alloc_align (2)); +} + + +void test_alloc_size_malloc (void) +{ + A (0, fnone, alloc_size); + A (0, fnone, alloc_size (1)); /* { dg-warning "\\\[-Wattributes" } */ + A (0, fnone, alloc_size (2)); /* { dg-warning "\\\[-Wattributes" } */ + A (0, falloc_align_1, alloc_size (1)); + A (0, falloc_align_2, alloc_size (1)); + A (1, falloc_size_1, alloc_size (1)); + A (0, falloc_size_1, alloc_size (2)); + A (0, falloc_size_2, alloc_size (1)); + A (1, falloc_size_2, alloc_size (2)); + + A (1, falloc_size_2_4, alloc_size); + /* It would probably make more sense to have the built-in return + true only when both alloc_size arguments match, not just one + or the other. */ + A (0, falloc_size_2_4, alloc_size (1)); + A (1, falloc_size_2_4, alloc_size (2)); + A (0, falloc_size_2_4, alloc_size (3)); + A (1, falloc_size_2_4, alloc_size (4)); + A (1, falloc_size_2_4, alloc_size (2, 4)); + + extern ATTR (alloc_size (3)) + void* fmalloc_size_3 (int, int, int); + + A (1, fmalloc_size_3, alloc_size); + A (0, fmalloc_size_3, alloc_size (1)); + A (0, fmalloc_size_3, alloc_size (2)); + A (1, fmalloc_size_3, alloc_size (3)); + A (0, fmalloc_size_3, malloc); + + extern ATTR (malloc) + void* fmalloc_size_3 (int, int, int); + + A (1, fmalloc_size_3, alloc_size (3)); + A (1, fmalloc_size_3, malloc); +} + + +void test_alias (void) +{ + A (0, fnoreturn, alias); + A (1, falias, alias); + A (1, falias, alias ("fnoreturn")); + A (0, falias, alias ("falias")); + A (0, falias, alias ("fnone")); +} + + +void test_cold_hot (void) +{ + extern ATTR (cold) void fcold (void); + extern ATTR (hot) void fhot (void); + + A (0, fnone, cold); + A (0, fnone, hot); + + A (1, fcold, cold); + A (0, fcold, hot); + + A (0, fhot, cold); + A (1, fhot, hot); +} + + +void test_const_leaf_pure (void) +{ + extern ATTR (const) int fconst (void); + extern ATTR (leaf) int fleaf (void); + extern ATTR (pure) int fpure (void); + + A (0, fnone, const); + A (0, fnone, leaf); + A (0, fnone, pure); + + A (1, fconst, const); + A (0, fconst, leaf); + A (0, fconst, pure); + + A (0, fleaf, const); + A (1, fleaf, leaf); + A (0, fleaf, pure); + + A (0, fpure, const); + A (0, fpure, leaf); + A (1, fpure, pure); + + extern ATTR (const, leaf) int fconst_leaf (void); + + A (1, fconst_leaf, const); + A (1, fconst_leaf, leaf); + + extern ATTR (leaf, const) int fleaf_const (void); + + A (1, fleaf_const, const); + A (1, fleaf_const, leaf); +} + + +void test_ctor_dtor (void) +{ + extern ATTR (constructor) void fctor (void); + extern ATTR (destructor) void fdtor (void); + extern ATTR (constructor, destructor) void fctor_dtor (void); + + A (0, fnone, constructor); + A (0, fnone, destructor); + + A (1, fctor, constructor); + A (1, fdtor, destructor); + + extern ATTR (constructor) void fctor_dtor (void); + extern ATTR (destructor) void fctor_dtor (void); + extern ATTR (constructor, destructor) void fctor_dtor (void); + + A (1, fctor_dtor, constructor); + A (1, fctor_dtor, destructor); + + extern ATTR (constructor (123)) void fctor_123 (void); + A (1, fctor_123, constructor); + A (0, fctor_123, destructor); + A (1, fctor_123, constructor (123)); + A (0, fctor_123, constructor (124)); + + extern ATTR (destructor (234)) void fctor_123 (void); + A (1, fctor_123, constructor (123)); + A (1, fctor_123, destructor); + A (1, fctor_123, destructor (234)); + A (0, fctor_123, destructor (235)); +} + + +void test_externally_visible (void) +{ + extern void fexternally_visible (void); + + A (0, fexternally_visible, externally_visible); + + extern ATTR (externally_visible) void fexternally_visible (void); + + A (1, fexternally_visible, externally_visible); +} + + +void test_flatten (void) +{ + extern void fflatten (void); + + A (0, fflatten, flatten); + + extern ATTR (flatten) void fflatten (void); + + A (1, fflatten, flatten); + + extern void fflatten (void); + + A (1, fflatten, flatten); +} + + +ATTR (format (printf, 2, 4)) void +fformat_printf_2_3 (int, const char*, int, ...); + +void test_format (void) +{ + A (0, fnone, format); + A (0, fnone, format (printf)); + A (0, fnone, format (printf, 2)); +} + + +inline void finline (void) { } +inline ATTR (always_inline) void falways_inline (void) { } +inline ATTR (always_inline, gnu_inline) void falways_gnu_inline (void) { } +ATTR (noinline) void fnoinline () { } + +void test_inline (void) +{ + A (0, fnone, always_inline); + A (0, fnone, gnu_inline); + A (0, fnone, noinline); + + A (0, finline, always_inline); + A (0, finline, gnu_inline); + A (0, finline, noinline); + + A (1, falways_inline, always_inline); + A (0, falways_inline, gnu_inline); + A (0, falways_inline, noinline); + + A (1, falways_gnu_inline, always_inline); + A (1, falways_gnu_inline, gnu_inline); + A (0, falways_gnu_inline, noinline); + + A (0, fnoinline, always_inline); + A (0, fnoinline, gnu_inline); + A (1, fnoinline, noinline); +} + + +ATTR (no_instrument_function) void fno_instrument (void); + +ATTR (visibility ("default")) void fdefault (void); +ATTR (visibility ("hidden")) void fhidden (void); +ATTR (visibility ("internal")) void finternal (void); +ATTR (visibility ("protected")) void fprotected (void); + +void test_visibility (void) +{ + A (0, fnone, visibility ("default")); + A (0, fnone, visibility ("hidden")); + A (0, fnone, visibility ("internal")); + A (0, fnone, visibility ("protected")); + + A (1, fdefault, visibility ("default")); + A (0, fdefault, visibility ("hidden")); + A (0, fdefault, visibility ("internal")); + A (0, fdefault, visibility ("protected")); + + A (0, fhidden, visibility ("default")); + A (1, fhidden, visibility ("hidden")); + A (0, fhidden, visibility ("internal")); + A (0, fhidden, visibility ("protected")); + + A (0, finternal, visibility ("default")); + A (0, finternal, visibility ("hidden")); + A (1, finternal, visibility ("internal")); + A (0, finternal, visibility ("protected")); + + A (0, fprotected, visibility ("default")); + A (0, fprotected, visibility ("hidden")); + A (0, fprotected, visibility ("internal")); + A (1, fprotected, visibility ("protected")); +} + +/* { dg-prune-output "specifies less restrictive attribute" } */ diff --git a/gcc/testsuite/c-c++-common/builtin-has-attribute-4.c b/gcc/testsuite/c-c++-common/builtin-has-attribute-4.c new file mode 100644 index 00000000000..14bdd3f3e08 --- /dev/null +++ b/gcc/testsuite/c-c++-common/builtin-has-attribute-4.c @@ -0,0 +1,285 @@ +/* Verify __builtin_has_attribute return value for variables. + { dg-do compile } + { dg-options "-Wall -ftrack-macro-expansion=0" } + { dg-options "-Wall -Wno-narrowing -Wno-unused -ftrack-macro-expansion=0" { target c++ } } */ + +#define ATTR(...) __attribute__ ((__VA_ARGS__)) + +#define A(expect, sym, attr) \ + typedef int Assert [1 - 2 * !(__builtin_has_attribute (sym, attr) == expect)] + +int vnone; + +ATTR (aligned) char valigned; +ATTR (aligned (1)) char valigned_1; +ATTR (aligned (2)) char valigned_2; +ATTR (aligned (4)) char valigned_4; +ATTR (aligned (8)) char valigned_8; + +void test_aligned (void) +{ + A (0, vnone, aligned); + A (0, vnone, aligned (0)); + A (0, vnone, aligned (1)); + A (0, vnone, aligned (2)); + A (0, vnone, aligned (4)); + A (0, vnone, aligned (8)); + A (0, vnone, aligned (16)); + + A (1, valigned, aligned); + A (0, valigned, aligned (0)); + A (0, valigned, aligned (1)); + A (0, valigned, aligned (2)); + + A (1, valigned_1, aligned); + A (0, valigned_1, aligned (0)); + A (1, valigned_1, aligned (1)); + A (0, valigned_1, aligned (2)); + A (0, valigned_1, aligned (4)); + + A (1, valigned_2, aligned); + A (0, valigned_2, aligned (0)); + A (0, valigned_2, aligned (1)); + A (1, valigned_2, aligned (2)); + A (0, valigned_2, aligned (4)); +} + + +int vtarget; +extern ATTR (alias ("vtarget")) int valias; + +void test_alias (void) +{ + A (0, vnone, alias); + A (1, valias, alias); + A (1, valias, alias ("vtarget")); + A (0, valias, alias ("vnone")); +} + + +void test_cleanup (void) +{ + extern void fpv (void*); + extern void fcleanup (void*); + + int var; + ATTR (cleanup (fcleanup)) int var_cleanup; + A (0, var, cleanup); + A (1, var_cleanup, cleanup); + A (1, var_cleanup, cleanup (fcleanup)); + A (0, var_cleanup, cleanup (fpv)); +} + + +ATTR (common) int vcommon; +ATTR (nocommon) int vnocommon; + +void test_common (void) +{ + A (0, vnone, common); + A (0, vnone, nocommon); + + A (1, vcommon, common); + A (0, vcommon, nocommon); + + A (0, vnocommon, common); + A (1, vnocommon, nocommon); +} + + +void test_externally_visible (void) +{ + extern int vexternally_visible; + + A (0, vexternally_visible, externally_visible); + + extern ATTR (externally_visible) int vexternally_visible; + + A (1, vexternally_visible, externally_visible); +} + + +int test_mode (void) +{ + ATTR (mode (byte)) int i8; + return __builtin_has_attribute (i8, mode); /* { dg-warning ".mode. attribute not supported in .__builtin_has_attribute." } */ +} + + +void test_nonstring (void) +{ + char arr[1]; + char* ptr = arr; + + ATTR (nonstring) char arr_nonstring[1]; + ATTR (nonstring) char *ptr_nonstring = arr_nonstring; + + A (0, arr, nonstring); + A (0, ptr, nonstring); + + A (1, arr_nonstring, nonstring); + A (1, ptr_nonstring, nonstring); +} + +struct PackedMember +{ + char c; + short s; + int i; + ATTR (packed) int a[2]; +} gpak[2]; + +void test_packed (struct PackedMember *p) +{ + int vunpacked; + ATTR (packed) int vpacked; /* { dg-warning ".packed. attribute ignored" } */ + + A (0, vunpacked, packed); + A (0, vpacked, packed); + + int arr_unpacked[2]; + ATTR (packed) int arr_packed[2]; /* { dg-warning ".packed. attribute ignored" } */ + + A (0, arr_unpacked, packed); + A (0, arr_packed, packed); + A (0, arr_unpacked[0], packed); + A (0, arr_packed[0], packed); + + A (0, gpak, packed); + A (0, gpak[0], packed); + A (0, *gpak, packed); + A (0, gpak[0].c, packed); + A (0, gpak[1].s, packed); + A (1, gpak->a, packed); + A (1, (*gpak).a[0], packed); + + /* The following fails because in C it's represented as + INDIRECT_REF (POINTER_PLUS (NOP_EXPR (ADDR_EXPR (gpak)), ...)) + with no reference to the member. Avoid testing it. + A (1, *gpak[9].a, packed); */ + + A (0, p->c, packed); + A (0, p->s, packed); + A (1, p->a, packed); + A (1, p->a[0], packed); + /* Similar to the comment above. + A (1, *p->a, packed); */ +} + + +ATTR (section ("sectA")) int var_sectA; +ATTR (section ("sectB")) int var_sectB; + +void test_section (void) +{ + int var = 0; + A (0, var, section); + A (0, var, section ("sectA")); + + A (1, var_sectA, section); + A (1, var_sectA, section ("sectA")); + A (0, var_sectA, section ("sectB")); + + A (1, var_sectB, section); + A (0, var_sectB, section ("sectA")); + A (1, var_sectB, section ("sectB")); +} + + +void test_vector_size (void) +{ + char c; + extern int arrx[]; + extern int arr1[1]; + + A (0, c, vector_size); + A (0, c, vector_size (1)); + A (0, arrx, vector_size); + A (0, arrx, vector_size (4)); + A (0, arr1, vector_size); + A (0, arr1, vector_size (8)); + + ATTR (vector_size (4)) char cv4; + ATTR (vector_size (16)) int iv16; + + A (1, cv4, vector_size); + A (0, cv4, vector_size (1)); + A (0, cv4, vector_size (2)); + A (1, cv4, vector_size (4)); + A (0, cv4, vector_size (8)); + + A (1, iv16, vector_size); + A (0, iv16, vector_size (1)); + A (0, iv16, vector_size (8)); + A (1, iv16, vector_size (16)); + A (0, iv16, vector_size (32)); + + ATTR (vector_size (8)) float afv8[4]; + A (1, afv8, vector_size); + A (0, afv8, vector_size (1)); + A (0, afv8, vector_size (2)); + A (0, afv8, vector_size (4)); + A (1, afv8, vector_size (8)); + A (0, afv8, vector_size (16)); +} + + +ATTR (visibility ("default")) int vdefault; +ATTR (visibility ("hidden")) int vhidden; +ATTR (visibility ("internal")) int vinternal; +ATTR (visibility ("protected")) int vprotected; + +void test_visibility (void) +{ + A (0, vnone, visibility ("default")); + A (0, vnone, visibility ("hidden")); + A (0, vnone, visibility ("internal")); + A (0, vnone, visibility ("protected")); + + A (1, vdefault, visibility ("default")); + A (0, vdefault, visibility ("hidden")); + A (0, vdefault, visibility ("internal")); + A (0, vdefault, visibility ("protected")); + + A (0, vhidden, visibility ("default")); + A (1, vhidden, visibility ("hidden")); + A (0, vhidden, visibility ("internal")); + A (0, vhidden, visibility ("protected")); + + A (0, vinternal, visibility ("default")); + A (0, vinternal, visibility ("hidden")); + A (1, vinternal, visibility ("internal")); + A (0, vinternal, visibility ("protected")); + + A (0, vprotected, visibility ("default")); + A (0, vprotected, visibility ("hidden")); + A (0, vprotected, visibility ("internal")); + A (1, vprotected, visibility ("protected")); +} + + +int var_init_strong = 123; +int var_uninit_strong; +static int var_extern_strong; +static int var_static_strong; + +ATTR (weak) int var_init_weak = 234; +ATTR (weak) int var_uninit_weak; + +void test_weak (void) +{ + int var_local = 0; + static int var_static_local = 0; + + A (0, var_init_strong, weak); + A (0, var_uninit_strong, weak); + A (0, var_extern_strong, weak); + A (0, var_static_strong, weak); + A (0, var_local, weak); + A (0, var_static_local, weak); + + A (1, var_init_weak, weak); + A (1, var_uninit_weak, weak); +} + +/* { dg-prune-output "specifies less restrictive attribute" } */ diff --git a/gcc/testsuite/c-c++-common/builtin-has-attribute.c b/gcc/testsuite/c-c++-common/builtin-has-attribute.c new file mode 100644 index 00000000000..67c792f37df --- /dev/null +++ b/gcc/testsuite/c-c++-common/builtin-has-attribute.c @@ -0,0 +1,60 @@ +/* Verify __builtin_has_attribute error handling. + { dg-do compile } + { dg-options "-Wall -ftrack-macro-expansion=0" } */ + +#define ATTR(list) __attribute__ (list) + +void fnone (void); + +ATTR ((aligned)) void faligned (void); +ATTR ((aligned (8))) void faligned_8 (void); + +#define has_attr(x, attr) __builtin_has_attribute (x, attr) + +#define A(expect, sym, attr) \ + typedef int Assert [1 - 2 * !(has_attr (sym, attr) == expect)] + + +int b; + +/* Exercise syntactically invalid arguments. */ + +void test_bad_arguments (void) +{ + b = __builtin_has_attribute (); /* { dg-error "expected \(primary-\)?expression|expected .,." } */ + b = __builtin_has_attribute (1); /* { dg-error "expected .,." } */ + b = __builtin_has_attribute (void); /* { dg-error "expected .,." } */ + b = __builtin_has_attribute (foo); /* { dg-error ".foo. \(undeclared|was not declared\)" } */ + /* { dg-error "expected .,." "missing comma" { target *-*-* } .-1 } */ + + /* Verify the implementationm doesn't ICE. */ + b = __builtin_has_attribute (foobar, aligned); /* { dg-error ".foobar. \(undeclared|was not declared\)" } */ + + b = __builtin_has_attribute (1, 2, 3); /* { dg-error "expected identifier" } */ + b = __builtin_has_attribute (int, 1 + 2); /* { dg-error "expected identifier" } */ + b = __builtin_has_attribute (2, "aligned"); /* { dg-error "expected identifier" } */ +} + +/* Exercise syntactically valid arguments applied in invalid ways. */ + +void test_invalid_arguments (void) +{ + b = has_attr (fnone, align); /* { dg-error "unknown attribute .align." } */ + b = has_attr (b, aligned__); /* { dg-error "unknown attribute .aligned__." } */ + b = has_attr (fnone, aligned (3)); /* { dg-error "alignment .3. is not a positive power of 2" } */ + + /* Verify the out-of-bounds arguments are diagnosed and the result + of the built-in is false. */ + A (0, fnone, alloc_size (1)); /* { dg-warning "\\\[-Wattributes]" } */ + A (0, fnone, alloc_size (2)); /* { dg-warning "\\\[-Wattributes]" } */ + + A (0, int, alloc_size (1)); /* { dg-warning ".alloc_size. attribute only applies to function types" } */ + + int i = 1; + A (0, i, alloc_size (1)); /* { dg-warning ".alloc_size. attribute only applies to function types" } */ + + A (0, faligned_8, aligned (i)); /* { dg-error "alignment is not an integer constant" } */ + + typedef ATTR ((aligned (2))) char CA2; + b = has_attr (CA2[2], aligned); /* { dg-error "alignment of array elements is greater than element size" } */ +} diff --git a/gcc/testsuite/gcc.dg/builtin-has-attribute.c b/gcc/testsuite/gcc.dg/builtin-has-attribute.c new file mode 100644 index 00000000000..8c11cf82abe --- /dev/null +++ b/gcc/testsuite/gcc.dg/builtin-has-attribute.c @@ -0,0 +1,45 @@ +/* Verify that defining a type in __builtin_has_attribute triggers + the expected -Wc++-compat warning and evaluates as expected. + Also verify that the expression in __builtin_has_attribute is + not evaluated. + + { dg-do run } + { dg-options "-O2 -Wall -Wc++-compat" } */ + +#define ATTR(list) __attribute__ (list) + +#define A(expect, sym, attr) \ + typedef int Assert [1 - 2 * !(__builtin_has_attribute (sym, attr) == expect)] + +int nfails; + +#define assert(expr) \ + ((expr) \ + ? (void)0 \ + : (__builtin_printf ("Assertion failed on line %i: %s\n", \ + __LINE__, #expr), \ + ++nfails)) + +A (0, struct A { int i; }, aligned); /* { dg-warning "expression is invalid in C\\\+\\\+" } */ +A (1, struct ATTR ((aligned)) B { int i; }, aligned); /* { dg-warning "expression is invalid in C\\\+\\\+" } */ + + +int f (void) +{ + __builtin_abort (); +} + +int n = 1; + +int main (void) +{ + assert (0 == __builtin_has_attribute (int[n++], aligned)); + assert (1 == __builtin_has_attribute (ATTR ((aligned)) int[n++], aligned)); + assert (1 == __builtin_has_attribute (ATTR ((aligned)) int[f ()], aligned)); + assert (1 == 1); + + if (nfails) + __builtin_abort (); + + return 0; +} diff --git a/gcc/testsuite/gcc.target/i386/builtin-has-attribute.c b/gcc/testsuite/gcc.target/i386/builtin-has-attribute.c new file mode 100644 index 00000000000..2de1ba3a7f9 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/builtin-has-attribute.c @@ -0,0 +1,54 @@ +/* Verify __builtin_has_attribute return value for i386 function attributes. + { dg-do compile } + { dg-options "-Wall -ftrack-macro-expansion=0" } + { dg-options "-Wall -Wno-narrowing -Wno-unused -ftrack-macro-expansion=0" { target c++ } } */ + +#define ATTR(...) __attribute__ ((__VA_ARGS__)) + +#define A(expect, sym, attr) \ + typedef int Assert [1 - 2 * !(__builtin_has_attribute (sym, attr) == expect)] + +void fnone (void); + + +ATTR (interrupt) void finterrupt (void*); +ATTR (interrupt, naked) void fnaked_interrupt (void*); + +A (0, fnone, interrupt); +A (1, finterrupt, interrupt); +A (1, fnaked_interrupt, interrupt); +A (1, fnaked_interrupt, naked); + + +ATTR (naked) void fnaked (void); + +A (0, fnone, naked); +A (1, fnaked, naked); + + +ATTR (no_caller_saved_registers) void fnsr (int); + +A (0, fnone, no_caller_saved_registers); +A (1, fnsr, no_caller_saved_registers); + + +ATTR (target ("abm")) void ftarget_abm (void); +ATTR (target ("mmx")) void ftarget_mmx (void); +ATTR (target ("mmx"), target ("sse")) void ftarget_mmx_sse (void); + +A (0, fnone, target); +A (0, fnone, target ("abm")); +A (0, fnone, target ("mmx")); + +A (1, ftarget_abm, target); +A (0, ftarget_abm, target ("no-abm")); +A (1, ftarget_abm, target ("abm")); + +A (1, ftarget_mmx, target); +A (0, ftarget_mmx, target ("no-mmx")); +A (1, ftarget_mmx, target ("mmx")); + +A (1, ftarget_mmx_sse, target); +A (0, ftarget_mmx_sse, target ("no-mmx")); +A (1, ftarget_mmx_sse, target ("mmx")); +A (1, ftarget_mmx_sse, target ("sse")); -- 2.30.2