From patchwork Thu Oct 11 02:55:32 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Martin Sebor X-Patchwork-Id: 982192 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (mailfrom) smtp.mailfrom=gcc.gnu.org (client-ip=209.132.180.131; helo=sourceware.org; envelope-from=gcc-patches-return-487304-incoming=patchwork.ozlabs.org@gcc.gnu.org; receiver=) Authentication-Results: ozlabs.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: ozlabs.org; dkim=pass (1024-bit key; unprotected) header.d=gcc.gnu.org header.i=@gcc.gnu.org header.b="HuIYYoLx"; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b="Ks9J1PM6"; dkim-atps=neutral Received: from sourceware.org (server1.sourceware.org [209.132.180.131]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 42VwZr30Jlz9sBk for ; Thu, 11 Oct 2018 13:56:02 +1100 (AEDT) DomainKey-Signature: a=rsa-sha1; c=nofws; d=gcc.gnu.org; h=list-id :list-unsubscribe:list-archive:list-post:list-help:sender:to :from:subject:message-id:date:mime-version:content-type; q=dns; s=default; b=XUdYxHi5oVs1hUW5uYtYyl/6XJFZCHNqmWKE74ZrpGB7DtH+WN FbILNtw6t67t/AX/C4IZswQVLF5UtsBmEXKQknADxoOrJXvAKgYkVbn0Zf13o+nK 68NNZzhCbLkwVzJjWfgWy7N179SYKDpyLUcCiZArrq07QSEEMQA2oZVJI= DKIM-Signature: v=1; a=rsa-sha1; c=relaxed; d=gcc.gnu.org; h=list-id :list-unsubscribe:list-archive:list-post:list-help:sender:to :from:subject:message-id:date:mime-version:content-type; s= default; bh=VaFI/OC/HZY+K8UD8fXvaNCl3gs=; b=HuIYYoLx66RimjF2PH7K 0CecenGY8xOUDViu82Rs9x0iyy0sGC/NrWKEZUK8DyEHd1iMarLhwNB9rBoyFbYj RCZWj5ohe5Qm+igfiieQoNnqI5IQ4DMXEryFd9/cJ8hZENo/hAnj5QBsAoBEustw IJ6mSzgiH2t6LqRtXj1R/I0= Received: (qmail 71833 invoked by alias); 11 Oct 2018 02:55:51 -0000 Mailing-List: contact gcc-patches-help@gcc.gnu.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Archive: List-Post: List-Help: Sender: gcc-patches-owner@gcc.gnu.org Delivered-To: mailing list gcc-patches@gcc.gnu.org Received: (qmail 71823 invoked by uid 89); 11 Oct 2018 02:55:50 -0000 Authentication-Results: sourceware.org; auth=none X-Spam-SWARE-Status: No, score=-25.1 required=5.0 tests=AWL, BAYES_00, FREEMAIL_FROM, GIT_PATCH_0, GIT_PATCH_1, GIT_PATCH_2, GIT_PATCH_3, RCVD_IN_DNSWL_NONE, SPF_PASS autolearn=ham version=3.3.2 spammy=sought, nearly, type-id, viewed X-HELO: mail-qt1-f173.google.com Received: from mail-qt1-f173.google.com (HELO mail-qt1-f173.google.com) (209.85.160.173) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with ESMTP; Thu, 11 Oct 2018 02:55:44 +0000 Received: by mail-qt1-f173.google.com with SMTP id l9-v6so8252852qtf.5 for ; Wed, 10 Oct 2018 19:55:44 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=to:from:subject:message-id:date:user-agent:mime-version; bh=7R/vQe+fMHQ8sds+nOqLrMM8ERcbypDFBfLm9K2WVxE=; b=Ks9J1PM6JpXM7Q0tR95/cJ/rPWh5i1HJJYODBfTNLrLo6wbCduqcSNEvXmsKeZ7k2f LNIMP59h/TyDg/D/c22FORfaqVvmen3mpMnIBrseWCyT7l0bllwjewX/zY7oxDcruJFW gTdH9geGNkzXJln1rRO6BtLJDlaFtYvDj85NDwVu6iUTi4la/yDJc0OtgGWgBaeeL7rJ 7aFFQAPb8YmWTU2nwVLIRRxVdJKCuO6+lwCGWagqyyGrRUGKukhUI1OByn9A8vUVBrmZ fwVXu+D4VbKZBfhjQsf5vZl+PfEAA1wbKUME5c9Y1aZK49rbsvADP6nozaPoELZiptQa Mlsg== Received: from localhost.localdomain (97-118-105-75.hlrn.qwest.net. [97.118.105.75]) by smtp.gmail.com with ESMTPSA id s13-v6sm19554026qte.29.2018.10.10.19.55.39 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Wed, 10 Oct 2018 19:55:41 -0700 (PDT) To: Gcc Patch List , Joseph Myers , Jason Merrill From: Martin Sebor Subject: [PATCH] add simple attribute introspection Message-ID: <55577012-4f85-bc8a-5c13-a53d5e0987f6@gmail.com> Date: Wed, 10 Oct 2018 20:55:32 -0600 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:45.0) Gecko/20100101 Thunderbird/45.8.0 MIME-Version: 1.0 X-IsSubscribed: yes While writing tests for fixes and enhancements for attribute handling I keep finding myself coming up with the same boiler- plate code to verify whether an attribute has or has not been successfully applied. It's often error-prone because it depends on the subtle and unique effects each attribute has on optimizers or code generation in general. Implementing an API just for testing might perhaps be excessive but I suspect that users would find use for such a feature too. I can even envision wanting to selectively apply attributes to one's own symbols depending on their presence or absence on other symbols (as an extension to the patch I posted for pr81824 that introduces the copy attribute to copy attributes from one symbol to another). The attached patch introduces a built-in function called __builtin_has_attribute that makes some of this possible. See the documentation and tests for details. The C++ implementation is only tested using the C tests and I'm pretty sure it doesn't do the right thing for templates but I think it can be extended to do that in a followup patch or an update to this one. The C tests don't exhaustively exercise all attributes so it's quite possible there are gaps/bugs where the built-in doesn't return the right value due to missing special handling. Attribute format validation is nearly non-existent. I view these shortcomings as minor and they too can be plugged in a followup patch. Ultimately, if this is viewed as useful as I'm hoping it will be, I'd like to move the special handling from has_attribute and to a callback function pointed from attribute_spec. That way each attribute, front-end and back-end alike, could, in addition the attribute handler, implement its own special query routine without the details leaking into the rest of the compiler. 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 (validate_attribute, has_attribute): New functions. * c-common.h (has_attribute): Declare. * c-common.c (c_common_resword): Add RID_HAS_ATTRIBUTE_EXPRESSION. * c-common.h (rid): 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.c: New test. * gcc.dg/builtin-has-attribute.c: New test. diff --git a/gcc/c-family/c-attribs.c b/gcc/c-family/c-attribs.c index 3a88766..56967fa 100644 --- a/gcc/c-family/c-attribs.c +++ b/gcc/c-family/c-attribs.c @@ -3678,3 +3678,220 @@ 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 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; + } + + if (!TREE_VALUE (attr)) + return true; + + /* FIXME: Do some validation. */ + if (!strcmp (IDENTIFIER_POINTER (atname), "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 (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)) + DECL_EXTERNAL (tmpdecl) = DECL_EXTERNAL (oper); + 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. */ + 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 + expr = t; + + /* Set to true when an attribute is found in the referenced entity + that matches the specified attribute. */ + bool found_match = false; + + /* 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) + { + /* 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; + } + + tree atname = get_attribute_name (attr); + const char *namestr = IDENTIFIER_POINTER (atname); + + /* 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) + { + /* Convert to make them equality-comparable. */ + v1 = convert (v1); + v2 = convert (v2); + } + + if (simple_cst_equal (v1, v2)) + 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 ("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; + } + } + } + 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..cf31ab8 100644 --- a/gcc/c/c-parser.c +++ b/gcc/c/c-parser.c @@ -1439,6 +1439,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 *); @@ -4293,6 +4295,100 @@ c_parser_attribute_any_word (c_parser *parser) allow identifiers declared as types to start the arguments? */ static tree +c_parser_attribute (c_parser *parser, tree attrs) +{ + if (!c_parser_next_token_is (parser, CPP_COMMA) + && !c_parser_next_token_is (parser, CPP_NAME) + && !c_parser_next_token_is (parser, CPP_KEYWORD)) + return NULL_TREE; + + vec *expr_list; + if (c_parser_next_token_is (parser, CPP_COMMA)) + { + c_parser_consume_token (parser); + 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)) + { + 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); + + 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; + } + /* Add this attribute to the list. */ + attrs = chainon (attrs, attr); + return attrs; +} + +static tree c_parser_attributes (c_parser *parser) { tree attrs = NULL_TREE; @@ -4316,96 +4412,17 @@ c_parser_attributes (c_parser *parser) 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)) + while (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); + 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 +7257,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 +7458,121 @@ 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; + tree attr = c_parser_attribute (parser, NULL_TREE); + + 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..aff098e 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 *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,135 @@ 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; + + /* Call grokdeclarator to figure out what type this is. */ + oper = grokdeclarator (NULL, + &decl_specs, + TYPENAME, + /*initialized=*/0, + /*attrlist=*/NULL); + } + + /* 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)) + { + parens.require_close (parser); + 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); + } + } + else + { + error_at (atloc, "expected identifier"); + cp_parser_skip_to_closing_parenthesis (parser, true, false, false); + } + + parens.require_close (parser); + + /* 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 +25288,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 +25355,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..3c64a289 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,31 @@ 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. 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. 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 + +@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/c-c++-common/builtin-has-attribute-2.c b/gcc/testsuite/c-c++-common/builtin-has-attribute-2.c new file mode 100644 index 0000000..7e77df2 --- /dev/null +++ b/gcc/testsuite/c-c++-common/builtin-has-attribute-2.c @@ -0,0 +1,199 @@ +/* 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) +{ + 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)); + + 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 0000000..7331478 --- /dev/null +++ b/gcc/testsuite/c-c++-common/builtin-has-attribute-3.c @@ -0,0 +1,143 @@ +/* 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)) 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_slign (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 (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)); +} + +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")); +} + +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)); +} + + +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.c b/gcc/testsuite/c-c++-common/builtin-has-attribute.c new file mode 100644 index 0000000..50c8aa0 --- /dev/null +++ b/gcc/testsuite/c-c++-common/builtin-has-attribute.c @@ -0,0 +1,59 @@ +/* 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 (fnone, aligned (3)); /* { dg-error "alignment 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 0000000..8c11cf8 --- /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; +}