From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from EUR05-VI1-obe.outbound.protection.outlook.com (mail-vi1eur05on2060.outbound.protection.outlook.com [40.107.21.60]) by sourceware.org (Postfix) with ESMTPS id A96D43858C54 for ; Wed, 26 Jul 2023 14:01:02 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org A96D43858C54 Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=arm.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=arm.com DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=armh.onmicrosoft.com; s=selector2-armh-onmicrosoft-com; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=fVM7It+iWumuadHILegkwF+bB8HwaUkG5vKQCxN19mg=; b=ydTkHejDSz5Zii3voBPmqKcO7k3LQUmxylxX66lBb+Nuk2LBLh44qtraZ7h9qujfIT7EwWfarUVaM97ZyoohJ0RqF6dw90nDtYFC20PiWWSttoChUNgGMz4Hu0WsET/1OsiyYsrb7htRd5DcjwLafmukoKkBqLdDQTD6sDea/Gg= Received: from DUZPR01CA0345.eurprd01.prod.exchangelabs.com (2603:10a6:10:4b8::28) by DB8PR08MB5387.eurprd08.prod.outlook.com (2603:10a6:10:115::9) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.6631.29; Wed, 26 Jul 2023 14:00:57 +0000 Received: from DBAEUR03FT026.eop-EUR03.prod.protection.outlook.com (2603:10a6:10:4b8:cafe::64) by DUZPR01CA0345.outlook.office365.com (2603:10a6:10:4b8::28) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.6631.29 via Frontend Transport; Wed, 26 Jul 2023 14:00:57 +0000 X-MS-Exchange-Authentication-Results: spf=pass (sender IP is 63.35.35.123) smtp.mailfrom=arm.com; dkim=pass (signature was verified) header.d=armh.onmicrosoft.com;dmarc=pass action=none header.from=arm.com; Received-SPF: Pass (protection.outlook.com: domain of arm.com designates 63.35.35.123 as permitted sender) receiver=protection.outlook.com; client-ip=63.35.35.123; helo=64aa7808-outbound-1.mta.getcheckrecipient.com; pr=C Received: from 64aa7808-outbound-1.mta.getcheckrecipient.com (63.35.35.123) by DBAEUR03FT026.mail.protection.outlook.com (100.127.142.242) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.6631.29 via Frontend Transport; Wed, 26 Jul 2023 14:00:57 +0000 Received: ("Tessian outbound e1fdbe8a48d3:v145"); Wed, 26 Jul 2023 14:00:56 +0000 X-CheckRecipientChecked: true X-CR-MTA-CID: a66f9ab8779d88f3 X-CR-MTA-TID: 64aa7808 Received: from b3d53a32c0d9.1 by 64aa7808-outbound-1.mta.getcheckrecipient.com id F0D6A1E5-1B45-46D9-9624-98D138A00B73.1; Wed, 26 Jul 2023 14:00:50 +0000 Received: from EUR05-AM6-obe.outbound.protection.outlook.com by 64aa7808-outbound-1.mta.getcheckrecipient.com with ESMTPS id b3d53a32c0d9.1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384); Wed, 26 Jul 2023 14:00:50 +0000 ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=l7WraBRUkm6/Jq+IH3Z7fAmZrv+YPQ5Ju5WZyZH3XT0psUSiSoGJ8d3OJHisL5iYYSCC0Om0MZrijLtJHo7y8PEteRc+LVPAg7+1MI29zv6i/i4H213EZay+zCNzMTyYGyXLgFnq/aEkWwZ2Yt/QtqkEYVydIP8iS9qwsjHdo5+NS9ahdUdjsJTT6iDkCippa+uv2Ge4aiclyKhHLKFOLdja4nvWLBPWWBnNjfHb06o0nvoeg2G5Zzzdhdl/PaWBfOM35MPRxPXPod1GGJpm4EAz2f12fmReifdzLsEF3Bxcmj4lj8G4I6Y27kCwp6l+VXz5cAW4TE5avMFhgCE16A== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector9901; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=fVM7It+iWumuadHILegkwF+bB8HwaUkG5vKQCxN19mg=; b=Al/axJzQLjVGf87xPXgWXiJf00x8xgin4HgoW/Fpr9fA/WAGQ4XAWJ5pcaup+XNkRienVF43J+E7xeGxiOfuo8ZrFcMp8g1TAuvEV+BNBLAG4Y7Y4ZJd0YFOKYrPo/41cHvHuyC5FRTyjZk5xY4F8+CVKAyexoxinQLP6U1NDX2mL8w1xWQrXIQme+aB4GD0f45yrr0JnvjSn2olB8jeuZPUTNjVRBJJB6gWUVK+Si8Bq1fNcr4vA/VngltayXWfqNy0SewMYbAO+OTL8D2kczYmFwEfSZIzgKsn5ct2B4E3zpQN7+qigk31Fv4/C4wxP7/nSSDrry01QcVZmCEo4g== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=arm.com; dmarc=pass action=none header.from=arm.com; dkim=pass header.d=arm.com; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=armh.onmicrosoft.com; s=selector2-armh-onmicrosoft-com; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=fVM7It+iWumuadHILegkwF+bB8HwaUkG5vKQCxN19mg=; b=ydTkHejDSz5Zii3voBPmqKcO7k3LQUmxylxX66lBb+Nuk2LBLh44qtraZ7h9qujfIT7EwWfarUVaM97ZyoohJ0RqF6dw90nDtYFC20PiWWSttoChUNgGMz4Hu0WsET/1OsiyYsrb7htRd5DcjwLafmukoKkBqLdDQTD6sDea/Gg= Authentication-Results-Original: dkim=none (message not signed) header.d=none;dmarc=none action=none header.from=arm.com; Received: from PAWPR08MB8958.eurprd08.prod.outlook.com (2603:10a6:102:33e::15) by AS8PR08MB8876.eurprd08.prod.outlook.com (2603:10a6:20b:5b6::7) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.6631.29; Wed, 26 Jul 2023 14:00:47 +0000 Received: from PAWPR08MB8958.eurprd08.prod.outlook.com ([fe80::f352:a0c5:bddf:2510]) by PAWPR08MB8958.eurprd08.prod.outlook.com ([fe80::f352:a0c5:bddf:2510%7]) with mapi id 15.20.6609.032; Wed, 26 Jul 2023 14:00:47 +0000 Date: Wed, 26 Jul 2023 15:00:44 +0100 From: Alex Coplan To: gcc-patches@gcc.gnu.org, Jason Merrill , Nathan Sidwell , Joseph Myers , Iain Sandoe Subject: Re: [PATCH v2][RFC] c-family: Implement __has_feature and __has_extension [PR60512] Message-ID: References: Content-Type: text/plain; charset=utf-8 Content-Disposition: inline In-Reply-To: X-ClientProxiedBy: LO4P123CA0424.GBRP123.PROD.OUTLOOK.COM (2603:10a6:600:18b::15) To PAWPR08MB8958.eurprd08.prod.outlook.com (2603:10a6:102:33e::15) MIME-Version: 1.0 X-MS-TrafficTypeDiagnostic: PAWPR08MB8958:EE_|AS8PR08MB8876:EE_|DBAEUR03FT026:EE_|DB8PR08MB5387:EE_ X-MS-Office365-Filtering-Correlation-Id: 69f40b2f-9d19-4eff-98d6-08db8de0bc17 x-checkrecipientrouted: true NoDisclaimer: true X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam-Untrusted: BCL:0; X-Microsoft-Antispam-Message-Info-Original: Tp3ONSGv2rRJwATRRQm9zX+i8kWKvzS7ICBjgSbNQn6wkTodmUCEvz7Sb4KRY0Gk510AWPf+8VTVIyM6hhT8K/RRpd9qejO0m0fKiOWFo6OUUphP4MDpwYHu5CdB+5qBX9CdPHGZ+ezmeQeiLDXwIje+dg2jOILveYUL/kJYvSEe31ZQaLdeNgkVJYdd5pk48BYpyBmjpN/mN+ARXh2i9e4U1ZT91Sigw6MyZV0smgki8jzr8Ov06iJbkjfrNQCBcC8ShgSe53T/K8CZvYfdm5LlGBHE5qqDghfOxpYGK5mx5KbpvemM6gM+sDxORpmeJ2Xg2LJn2InffUYuKK74+D+1x7PzeUdhJpUlBMv8sIgGaLURedP2XvURPenPACJ2XTdB8iu+DWMvXE8hJJxk8hoPLgwKADaCa+WtlA64KhHoimeaWuZ3HiO7+oGxv91VHAjhBDlpKCYMitdCxONeypZLCVC6cxvQXSFQ8ZtyhfFyN9dfr2mAw6fZyO2LPjgcFYtiSSuHCLG5nZinyU2HFIvMVm1eOJ122A6kr8unqHlDpx/jMxI+eJnrr1nHNrq1Xw5M8XF1cv/ieYWKQOqAslUAgICAXF10CXjeTZZk6VHuiYBwRtpDHYk1FBQEYcuU X-Forefront-Antispam-Report-Untrusted: CIP:255.255.255.255;CTRY:;LANG:en;SCL:1;SRV:;IPV:NLI;SFV:NSPM;H:PAWPR08MB8958.eurprd08.prod.outlook.com;PTR:;CAT:NONE;SFS:(13230028)(4636009)(39860400002)(396003)(376002)(346002)(136003)(366004)(451199021)(26005)(186003)(6506007)(53546011)(2616005)(316002)(66946007)(66556008)(83380400001)(8936002)(41300700001)(44832011)(66476007)(6666004)(6512007)(8676002)(6486002)(966005)(5660300002)(2906002)(30864003)(478600001)(110136005)(38100700002)(36756003)(86362001)(84970400001)(21314003)(579004);DIR:OUT;SFP:1101; X-MS-Exchange-Transport-CrossTenantHeadersStamped: AS8PR08MB8876 Original-Authentication-Results: dkim=none (message not signed) header.d=none;dmarc=none action=none header.from=arm.com; X-EOPAttributedMessage: 0 X-MS-Exchange-Transport-CrossTenantHeadersStripped: DBAEUR03FT026.eop-EUR03.prod.protection.outlook.com X-MS-PublicTrafficType: Email X-MS-Office365-Filtering-Correlation-Id-Prvs: 3b9d9f72-4f99-4f05-79ea-08db8de0b5f9 X-Microsoft-Antispam: BCL:0; X-Microsoft-Antispam-Message-Info: e7hQ98BJz8buQt9aHNbPhXbNcJxYxzGS4BW/cK9tmUlXNQMxVHzCWFDzjcjHCWQMB0Hs0V96fgvjeOWllx5eacHICsvbeQFzrbs6H6veoK2Z1lldHKZNa6Cm1RjQIKyQ8uSpSRVFdGwVwvnXKJZiJSJxprq29x6OcqZoIbmlx7eYZZxeYTE6ZEoWGYL4aJizlxGcF7CfEJ9orQ2HSZUmCPQn06VAfkFNjcNrAZO+SaPfAyeBciOe1Lp4S1ENBxGWcDTu5grL3Zm2mq+s/xo94qnb3T0u+6a6PkpZmDZSlX+tp0BNbtGLWm+4rlJJ4ORUEdgHn9xkEmBDWNvVR39eF+fHDuDw9SBpoBSI2Y6gwESfNscXBbGxrZoJOwZNyMFRrV72JonmiIU1eu4cIE1FMxq7CDwswIbQ8GWGjhHlhQudTjW1Gg3LjEs6RLVWahEoBZkSSP9oWj6MdEEJizlI+q7h+elFflU/mM/ZKfxdlPWzOlhNOC5IIZn21esw/guohjewp7Hs+3BJ+cZfR0VbrD8rVvO5pBxEct4OtQ3bnHoWO9b/t2G7SevkrgmzloEjIBZOG9XkumA24QhpcWiXLMUIqZ/mfyaqmbfi+N2WG1OLKRs6WYPrBb/2eVDczsMAYFnzqsj0blYpbBO/BEbWKa944kBYfH7hGH9GQ41HMR4j/inn96o86SSnCM6+j6aER0W8r4eZ3ZofUJxCtkFtXGVREGDwS1N05z96YVciOi2FE4vS7sj/s89GQYbzEcGLBnzZzSs62SqBERK6CFVOgrzMTy3SympbH9GqYop3emA= X-Forefront-Antispam-Report: CIP:63.35.35.123;CTRY:IE;LANG:en;SCL:1;SRV:;IPV:CAL;SFV:NSPM;H:64aa7808-outbound-1.mta.getcheckrecipient.com;PTR:ec2-63-35-35-123.eu-west-1.compute.amazonaws.com;CAT:NONE;SFS:(13230028)(4636009)(39860400002)(396003)(376002)(136003)(346002)(82310400008)(451199021)(36840700001)(46966006)(40470700004)(40480700001)(40460700003)(110136005)(356005)(966005)(81166007)(6666004)(6512007)(6486002)(82740400003)(478600001)(41300700001)(2616005)(5660300002)(316002)(70206006)(70586007)(8676002)(8936002)(36860700001)(336012)(186003)(83380400001)(47076005)(53546011)(6506007)(26005)(86362001)(44832011)(2906002)(30864003)(36756003)(84970400001)(21314003)(579004);DIR:OUT;SFP:1101; X-OriginatorOrg: arm.com X-MS-Exchange-CrossTenant-OriginalArrivalTime: 26 Jul 2023 14:00:57.0754 (UTC) X-MS-Exchange-CrossTenant-Network-Message-Id: 69f40b2f-9d19-4eff-98d6-08db8de0bc17 X-MS-Exchange-CrossTenant-Id: f34e5979-57d9-4aaa-ad4d-b122a662184d X-MS-Exchange-CrossTenant-OriginalAttributedTenantConnectingIp: TenantId=f34e5979-57d9-4aaa-ad4d-b122a662184d;Ip=[63.35.35.123];Helo=[64aa7808-outbound-1.mta.getcheckrecipient.com] X-MS-Exchange-CrossTenant-AuthSource: DBAEUR03FT026.eop-EUR03.prod.protection.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Anonymous X-MS-Exchange-CrossTenant-FromEntityHeader: HybridOnPrem X-MS-Exchange-Transport-CrossTenantHeadersStamped: DB8PR08MB5387 X-Spam-Status: No, score=-12.1 required=5.0 tests=BAYES_00,DKIM_SIGNED,DKIM_VALID,FORGED_SPF_HELO,GIT_PATCH_0,KAM_DMARC_NONE,KAM_SHORT,RCVD_IN_DNSWL_NONE,RCVD_IN_MSPIKE_H2,SPF_HELO_PASS,SPF_NONE,TXREP,T_SCC_BODY_TEXT_LINE,UNPARSEABLE_RELAY autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org List-Id: On 28/06/2023 11:35, Alex Coplan via Gcc-patches wrote: > Hi, > > This patch implements clang's __has_feature and __has_extension in GCC. > This is a v2 of the original RFC posted here: Ping. The Objective-C parts have been approved, but the C, C++, and generic bits need review. Let me know if there's anything I can do to make it easier to review, e.g. would it help to split into a series which adds the language-specific bits in separate patches? Thanks, Alex > > https://gcc.gnu.org/pipermail/gcc-patches/2023-May/617878.html > > Changes since v1: > - Follow the clang behaviour where -pedantic-errors means that > __has_extension behaves exactly like __has_feature. > - We're now more conservative with reporting C++ features as extensions > available in C++98. For features where we issue a pedwarn in C++98 > mode, we no longer report these as available extensions for C++98. > - Switch to using a hash_map to store the features. As well as ensuring > lookup is constant time, this allows us to dynamically register > features (right now based on frontend, but later we could allow the > target to register additional features). > - Also implement some Objective-C features, add a langhook to dispatch > to each frontend to allow it to register language-specific features. > > There is an outstanding question around what to do with > cxx_binary_literals in the C frontend for C2x. Should we introduce a new > c_binary_literals feature that is a feature in C2x and an extension > below that, or should we just continue using the cxx_binary_literals > feature and mark that as a standard feature in C2x? See the comment in > c_feature_table in the patch. > > There is also some doubt over what to do with the undocumented "tls" > feature. In clang this is gated on whether the target supports TLS, but > in clang (unlike GCC) it is a hard error to use TLS when the target > doesn't support it. In GCC I believe you can always use TLS, you just > get emulated TLS in the case that the target doesn't support it > natively. So in this patch GCC always reports having the "tls" feature. > Would appreciate if anyone has feedback on this aspect. > > I know Iain was concerned that it should be possible to have > target-specific features. Hopefully it is clear that the design in this > patch is more amenable in this. I think for Darwin it should be possible > to add a targetcm hook to register additional features (either passing > through a callback to allow the target code to add to the hash_map, or > exposing a separate langhook that the target can call to register > features). > > Bootstrapped/regtested on aarch64-linux-gnu and x86_64-apple-darwin. Any > thoughts? > > Thanks, > Alex > > ------ > > Co-Authored-By: Iain Sandoe > > gcc/c-family/ChangeLog: > > PR c++/60512 > * c-common.cc (struct hf_feature_info): New. > (struct hf_table_entry): New. > (hf_generic_predicate): New. > (c_common_register_feature): New. > (init_has_feature): New. > (has_feature_p): New. > * c-common.h (c_common_has_feature): New. > (has_feature_p): New. > (c_common_register_feature): New. > (c_register_features): New. > (cp_register_features): New. > * c-lex.cc (init_c_lex): Plumb through has_feature callback. > (c_common_has_builtin): Generalize and move common part ... > (c_common_lex_availability_macro): ... here. > (c_common_has_feature): New. > * c-ppoutput.cc (init_pp_output): Plumb through has_feature. > > gcc/c/ChangeLog: > > PR c++/60512 > * c-lang.cc (LANG_HOOKS_REGISTER_FEATURES): Implement with > c_register_features. > * c-objc-common.cc (struct c_feature_info): New. > (c_has_feature): New. > (c_register_features): New. > > gcc/cp/ChangeLog: > > PR c++/60512 > * cp-lang.cc (LANG_HOOKS_REGISTER_FEATURES): Implement with > cp_register_features. > * cp-objcp-common.cc (struct cp_feature_selector): New. > (cp_feature_selector::has_feature): New. > (struct cp_feature_info): New. > (cp_has_feature): New. > (cp_register_features): New. > > gcc/ChangeLog: > > PR c++/60512 > * doc/cpp.texi: Document __has_{feature,extension}. > * langhooks-def.h (LANG_HOOKS_REGISTER_FEATURES): New. > (LANG_HOOKS_INITIALIZER): Add LANG_HOOKS_REGISTER_FEATURES. > * langhooks.h (struct lang_hooks): Add register_features hook. > > gcc/objc/ChangeLog: > > PR c++/60512 > * objc-act.cc (struct objc_feature_info): New. > (objc_nonfragile_abi_p): New. > (objc_has_feature): New. > (objc_common_register_features): New. > * objc-act.h (objc_register_features): New. > (objc_common_register_features): New. > * objc-lang.cc (LANG_HOOKS_REGISTER_FEATURES): Implement with > objc_register_features. > (objc_register_features): New. > > gcc/objcp/ChangeLog: > > PR c++/60512 > * objcp-lang.cc (objcxx_register_features): New. > (LANG_HOOKS_REGISTER_FEATURES): Implement with > objcxx_register_features. > > libcpp/ChangeLog: > > PR c++/60512 > * include/cpplib.h (struct cpp_callbacks): Add has_feature. > (enum cpp_builtin_type): Add BT_HAS_{FEATURE,EXTENSION}. > * init.cc: Add __has_{feature,extension}. > * macro.cc (_cpp_builtin_macro_text): Handle > BT_HAS_{FEATURE,EXTENSION}. > > gcc/testsuite/ChangeLog: > > PR c++/60512 > * c-c++-common/has-feature-common.c: New test. > * g++.dg/ext/has-feature.C: New test. > * gcc.dg/asan/has-feature-asan.c: New test. > * gcc.dg/has-feature.c: New test. > * gcc.dg/ubsan/has-feature-ubsan.c: New test. > * obj-c++.dg/has-feature.mm: New test. > * objc.dg/has-feature.m: New test. > > diff --git a/gcc/c-family/c-common.cc b/gcc/c-family/c-common.cc > index 34566a342bd..955713e9592 100644 > --- a/gcc/c-family/c-common.cc > +++ b/gcc/c-family/c-common.cc > @@ -311,6 +311,34 @@ const struct fname_var_t fname_vars[] = > {NULL, 0, 0}, > }; > > +enum > +{ > + HF_FLAG_EXT = 1, /* Available only as an extension. */ > + HF_FLAG_SANITIZE = 2, /* Availability depends on sanitizer flags. */ > +}; > + > +struct hf_feature_info > +{ > + const char *ident; > + unsigned flags; > + unsigned mask; > +}; > + > +static const hf_feature_info has_feature_table[] = > +{ > + { "address_sanitizer", HF_FLAG_SANITIZE, SANITIZE_ADDRESS }, > + { "thread_sanitizer", HF_FLAG_SANITIZE, SANITIZE_THREAD }, > + { "leak_sanitizer", HF_FLAG_SANITIZE, SANITIZE_LEAK }, > + { "hwaddress_sanitizer", HF_FLAG_SANITIZE, SANITIZE_HWADDRESS }, > + { "undefined_behavior_sanitizer", HF_FLAG_SANITIZE, SANITIZE_UNDEFINED }, > + { "attribute_deprecated_with_message", 0, 0 }, > + { "attribute_unavailable_with_message", 0, 0 }, > + { "enumerator_attributes", 0, 0 }, > + { "tls", 0, 0 }, > + { "gnu_asm_goto_with_outputs", HF_FLAG_EXT, 0 }, > + { "gnu_asm_goto_with_outputs_full", HF_FLAG_EXT, 0 } > +}; > + > /* Global visibility options. */ > struct visibility_flags visibility_options; > > @@ -9549,4 +9577,66 @@ c_strict_flex_array_level_of (tree array_field) > return strict_flex_array_level; > } > > +struct hf_table_entry > +{ > + hf_predicate predicate; > + const void *info; > +}; > + > +static bool hf_generic_predicate (bool strict_p, const void *param) > +{ > + auto info = static_cast (param); > + if ((info->flags & HF_FLAG_EXT) && strict_p) > + return false; > + > + if (info->flags & HF_FLAG_SANITIZE) > + return flag_sanitize & info->mask; > + > + return true; > +} > + > +typedef hash_map feature_map_t; > +feature_map_t *feature_map; > + > +void > +c_common_register_feature (const char *name, > + hf_predicate pred, > + const void *info) > +{ > + hf_table_entry e { pred, info }; > + bool dup = feature_map->put (get_identifier (name), e); > + gcc_checking_assert (!dup); > +} > + > +static void > +init_has_feature () > +{ > + feature_map = new feature_map_t; > + gcc_assert (feature_map); > + > + for (unsigned i = 0; i < ARRAY_SIZE (has_feature_table); i++) > + { > + const hf_feature_info *info = has_feature_table + i; > + c_common_register_feature (info->ident, > + hf_generic_predicate, > + static_cast (info)); > + } > + > + /* Register language-specific features. */ > + lang_hooks.register_features (); > +} > + > +bool > +has_feature_p (const char *feat, bool strict_p) > +{ > + if (!feature_map) > + init_has_feature (); > + > + const hf_table_entry *e = feature_map->get (get_identifier (feat)); > + if (!e) > + return false; > + > + return e->predicate (strict_p, e->info); > +} > + > #include "gt-c-family-c-common.h" > diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h > index b5ef5ff6b2c..5122767a8a5 100644 > --- a/gcc/c-family/c-common.h > +++ b/gcc/c-family/c-common.h > @@ -1123,6 +1123,15 @@ extern bool c_cpp_diagnostic (cpp_reader *, enum cpp_diagnostic_level, > ATTRIBUTE_GCC_DIAG(5,0); > extern int c_common_has_attribute (cpp_reader *, bool); > extern int c_common_has_builtin (cpp_reader *); > +extern int c_common_has_feature (cpp_reader *, bool); > +extern bool has_feature_p (const char *, bool); > + > +typedef bool (*hf_predicate) (bool, const void *); > +extern void c_common_register_feature (const char *, > + hf_predicate, > + const void *); > +extern void c_register_features (); > +extern void cp_register_features (); > > extern bool parse_optimize_options (tree, bool); > > diff --git a/gcc/c-family/c-lex.cc b/gcc/c-family/c-lex.cc > index dcd061c7cb1..ddc8b349dbe 100644 > --- a/gcc/c-family/c-lex.cc > +++ b/gcc/c-family/c-lex.cc > @@ -82,6 +82,7 @@ init_c_lex (void) > cb->read_pch = c_common_read_pch; > cb->has_attribute = c_common_has_attribute; > cb->has_builtin = c_common_has_builtin; > + cb->has_feature = c_common_has_feature; > cb->get_source_date_epoch = cb_get_source_date_epoch; > cb->get_suggestion = cb_get_suggestion; > cb->remap_filename = remap_macro_filename; > @@ -425,16 +426,16 @@ c_common_has_attribute (cpp_reader *pfile, bool std_syntax) > return result; > } > > -/* Callback for has_builtin. */ > +/* Helper for __has_{builtin,feature,extension}. */ > > -int > -c_common_has_builtin (cpp_reader *pfile) > +static const char * > +c_common_lex_availability_macro (cpp_reader *pfile, const char *builtin) > { > const cpp_token *token = get_token_no_padding (pfile); > if (token->type != CPP_OPEN_PAREN) > { > cpp_error (pfile, CPP_DL_ERROR, > - "missing '(' after \"__has_builtin\""); > + "missing '(' after \"__has_%s\"", builtin); > return 0; > } > > @@ -454,7 +455,7 @@ c_common_has_builtin (cpp_reader *pfile) > else > { > cpp_error (pfile, CPP_DL_ERROR, > - "macro \"__has_builtin\" requires an identifier"); > + "macro \"__has_%s\" requires an identifier", builtin); > if (token->type == CPP_CLOSE_PAREN) > return 0; > } > @@ -473,9 +474,38 @@ c_common_has_builtin (cpp_reader *pfile) > break; > } > > + return name; > +} > + > +/* Callback for has_builtin. */ > + > +int > +c_common_has_builtin (cpp_reader *pfile) > +{ > + const char *name = c_common_lex_availability_macro (pfile, "builtin"); > + if (!name) > + return 0; > + > return names_builtin_p (name); > } > > +/* Callback for has_feature. STRICT_P is true for has_feature and false > + for has_extension. */ > + > +int > +c_common_has_feature (cpp_reader *pfile, bool strict_p) > +{ > + const char *builtin = strict_p ? "feature" : "extension"; > + const char *name = c_common_lex_availability_macro (pfile, builtin); > + if (!name) > + return 0; > + > + /* If -pedantic-errors is given, __has_extension is equivalent to > + __has_feature. */ > + strict_p |= flag_pedantic_errors; > + return has_feature_p (name, strict_p); > +} > + > > /* Read a token and return its type. Fill *VALUE with its value, if > applicable. Fill *CPP_FLAGS with the token's flags, if it is > diff --git a/gcc/c-family/c-ppoutput.cc b/gcc/c-family/c-ppoutput.cc > index 4aa2bef2c0f..a1488c6f086 100644 > --- a/gcc/c-family/c-ppoutput.cc > +++ b/gcc/c-family/c-ppoutput.cc > @@ -162,6 +162,7 @@ init_pp_output (FILE *out_stream) > > cb->has_attribute = c_common_has_attribute; > cb->has_builtin = c_common_has_builtin; > + cb->has_feature = c_common_has_feature; > cb->get_source_date_epoch = cb_get_source_date_epoch; > cb->remap_filename = remap_macro_filename; > > diff --git a/gcc/c/c-lang.cc b/gcc/c/c-lang.cc > index b4e0c8cfb8a..e1b8bde31d7 100644 > --- a/gcc/c/c-lang.cc > +++ b/gcc/c/c-lang.cc > @@ -49,6 +49,9 @@ enum c_language_kind c_language = clk_c; > #undef LANG_HOOKS_GET_SARIF_SOURCE_LANGUAGE > #define LANG_HOOKS_GET_SARIF_SOURCE_LANGUAGE c_get_sarif_source_language > > +#undef LANG_HOOKS_REGISTER_FEATURES > +#define LANG_HOOKS_REGISTER_FEATURES c_register_features > + > /* Each front end provides its own lang hook initializer. */ > struct lang_hooks lang_hooks = LANG_HOOKS_INITIALIZER; > > diff --git a/gcc/c/c-objc-common.cc b/gcc/c/c-objc-common.cc > index e4aed61ed00..1ab28f0ce12 100644 > --- a/gcc/c/c-objc-common.cc > +++ b/gcc/c/c-objc-common.cc > @@ -34,6 +34,49 @@ along with GCC; see the file COPYING3. If not see > static bool c_tree_printer (pretty_printer *, text_info *, const char *, > int, bool, bool, bool, bool *, const char **); > > +struct c_feature_info > +{ > + const char *ident; > + const int *enable_flag; > +}; > + > +static const c_feature_info c_feature_table[] = > +{ > + { "c_alignas", &flag_isoc11 }, > + { "c_alignof", &flag_isoc11 }, > + { "c_atomic", &flag_isoc11 }, > + { "c_generic_selections", &flag_isoc11 }, > + { "c_static_assert", &flag_isoc11 }, > + { "c_thread_local", &flag_isoc11 }, > + > + /* XXX: Binary literals are available as a standard feature in > + C2x. They are standardised in C++14 and available as an extension > + in all C versions. Would it make more sense to have > + cxx_binary_literals always report as an extension (in C) and add a > + new c_binary_literals that reports as a feature for -std=c2x and an > + extension below that? */ > + { "cxx_binary_literals", &flag_isoc2x } > +}; > + > +static bool > +c_has_feature (bool strict_p, const void *arg) > +{ > + auto info = static_cast (arg); > + return !info->enable_flag || !strict_p || *info->enable_flag; > +} > + > +void > +c_register_features () > +{ > + for (unsigned i = 0; i < ARRAY_SIZE (c_feature_table); i++) > + { > + const c_feature_info *info = c_feature_table + i; > + c_common_register_feature (info->ident, > + c_has_feature, > + static_cast (info)); > + } > +} > + > bool > c_missing_noreturn_ok_p (tree decl) > { > diff --git a/gcc/cp/cp-lang.cc b/gcc/cp/cp-lang.cc > index 2f541460c4a..e3d65c8c8d2 100644 > --- a/gcc/cp/cp-lang.cc > +++ b/gcc/cp/cp-lang.cc > @@ -104,6 +104,9 @@ static const char *cp_get_sarif_source_language (const char *); > #undef LANG_HOOKS_GET_SARIF_SOURCE_LANGUAGE > #define LANG_HOOKS_GET_SARIF_SOURCE_LANGUAGE cp_get_sarif_source_language > > +#undef LANG_HOOKS_REGISTER_FEATURES > +#define LANG_HOOKS_REGISTER_FEATURES cp_register_features > + > /* Each front end provides its own lang hook initializer. */ > struct lang_hooks lang_hooks = LANG_HOOKS_INITIALIZER; > > diff --git a/gcc/cp/cp-objcp-common.cc b/gcc/cp/cp-objcp-common.cc > index 93b027b80ce..18f2a7d0fdc 100644 > --- a/gcc/cp/cp-objcp-common.cc > +++ b/gcc/cp/cp-objcp-common.cc > @@ -23,10 +23,131 @@ along with GCC; see the file COPYING3. If not see > #include "coretypes.h" > #include "cp-tree.h" > #include "cp-objcp-common.h" > +#include "c-family/c-common.h" > #include "dwarf2.h" > #include "stringpool.h" > #include "contracts.h" > > +struct cp_feature_selector > +{ > + enum > + { > + DIALECT, > + FLAG > + } kind; > + > + union > + { > + const int *enable_flag; > + struct { > + enum cxx_dialect feat; > + enum cxx_dialect ext; > + } dialect; > + }; > + > + constexpr cp_feature_selector (const int *flag) > + : kind (FLAG), enable_flag (flag) {} > + constexpr cp_feature_selector (enum cxx_dialect feat, > + enum cxx_dialect ext) > + : kind (DIALECT), dialect{feat, ext} {} > + constexpr cp_feature_selector (enum cxx_dialect feat) > + : cp_feature_selector (feat, feat) {} > + > + bool has_feature (bool strict_p) const; > +}; > + > +bool cp_feature_selector::has_feature (bool strict_p) const > +{ > + switch (kind) > + { > + case DIALECT: > + if (!strict_p) > + return cxx_dialect >= dialect.ext; > + return cxx_dialect >= dialect.feat; > + case FLAG: > + return *enable_flag; > + } > + gcc_unreachable (); > +} > + > +struct cp_feature_info > +{ > + const char *ident; > + cp_feature_selector selector; > +}; > + > +static const cp_feature_info cp_feature_table[] = > +{ > + { "cxx_exceptions", &flag_exceptions }, > + { "cxx_rtti", &flag_rtti }, > + { "cxx_access_control_sfinae", { cxx11, cxx98 } }, > + { "cxx_alias_templates", cxx11 }, > + { "cxx_alignas", cxx11 }, > + { "cxx_alignof", cxx11 }, > + { "cxx_attributes", cxx11 }, > + { "cxx_constexpr", cxx11 }, > + { "cxx_constexpr_string_builtins", cxx11 }, > + { "cxx_decltype", cxx11 }, > + { "cxx_decltype_incomplete_return_types", cxx11 }, > + { "cxx_default_function_template_args", cxx11 }, > + { "cxx_defaulted_functions", cxx11 }, > + { "cxx_delegating_constructors", cxx11 }, > + { "cxx_deleted_functions", cxx11 }, > + { "cxx_explicit_conversions", cxx11 }, > + { "cxx_generalized_initializers", cxx11 }, > + { "cxx_implicit_moves", cxx11 }, > + { "cxx_inheriting_constructors", cxx11 }, > + { "cxx_inline_namespaces", { cxx11, cxx98 } }, > + { "cxx_lambdas", cxx11 }, > + { "cxx_local_type_template_args", cxx11 }, > + { "cxx_noexcept", cxx11 }, > + { "cxx_nonstatic_member_init", cxx11 }, > + { "cxx_nullptr", cxx11 }, > + { "cxx_override_control", cxx11 }, > + { "cxx_reference_qualified_functions", cxx11 }, > + { "cxx_range_for", cxx11 }, > + { "cxx_raw_string_literals", cxx11 }, > + { "cxx_rvalue_references", cxx11 }, > + { "cxx_static_assert", cxx11 }, > + { "cxx_thread_local", cxx11 }, > + { "cxx_auto_type", cxx11 }, > + { "cxx_strong_enums", cxx11 }, > + { "cxx_trailing_return", cxx11 }, > + { "cxx_unicode_literals", cxx11 }, > + { "cxx_unrestricted_unions", cxx11 }, > + { "cxx_user_literals", cxx11 }, > + { "cxx_variadic_templates", { cxx11, cxx98 } }, > + { "cxx_binary_literals", { cxx14, cxx98 } }, > + { "cxx_contextual_conversions", { cxx14, cxx98 } }, > + { "cxx_decltype_auto", cxx14 }, > + { "cxx_aggregate_nsdmi", cxx14 }, > + { "cxx_init_captures", cxx14 }, > + { "cxx_generic_lambdas", cxx14 }, > + { "cxx_relaxed_constexpr", cxx14 }, > + { "cxx_return_type_deduction", cxx14 }, > + { "cxx_variable_templates", cxx14 }, > + { "modules", &flag_modules }, > +}; > + > +static bool > +cp_has_feature (bool strict_p, const void *arg) > +{ > + const auto info = static_cast (arg); > + return info->selector.has_feature (strict_p); > +} > + > +void > +cp_register_features () > +{ > + for (unsigned i = 0; i < ARRAY_SIZE (cp_feature_table); i++) > + { > + const cp_feature_info *info = cp_feature_table + i; > + c_common_register_feature (info->ident, > + cp_has_feature, > + static_cast (info)); > + } > +} > + > /* Special routine to get the alias set for C++. */ > > alias_set_type > diff --git a/gcc/doc/cpp.texi b/gcc/doc/cpp.texi > index 3f492b33470..76dbb9892d6 100644 > --- a/gcc/doc/cpp.texi > +++ b/gcc/doc/cpp.texi > @@ -3199,6 +3199,8 @@ directive}: @samp{#if}, @samp{#ifdef} or @samp{#ifndef}. > * @code{__has_cpp_attribute}:: > * @code{__has_c_attribute}:: > * @code{__has_builtin}:: > +* @code{__has_feature}:: > +* @code{__has_extension}:: > * @code{__has_include}:: > @end menu > > @@ -3561,6 +3563,33 @@ the operator is as follows: > #endif > @end smallexample > > +@node @code{__has_feature} > +@subsection @code{__has_feature} > +@cindex @code{__has_feature} > + > +The special operator @code{__has_feature (@var{operand})} may be used in > +constant integer contexts and in preprocessor @samp{#if} and @samp{#elif} > +expressions to test whether the identifier given in @var{operand} is recognized > +as a feature supported by GCC given the current options and, in the case of > +standard language features, whether the feature is available in the chosen > +version of the language standard. > + > +@node @code{__has_extension} > +@subsection @code{__has_extension} > +@cindex @code{__has_extension} > + > +The special operator @code{__has_extension (@var{operand})} may be used in > +constant integer contexts and in preprocessor @samp{#if} and @samp{#elif} > +expressions to test whether the identifier given in @var{operand} is recognized > +as an extension supported by GCC given the current options. In any given > +context, the features accepted by @code{__has_extension} are a strict superset > +of those accepted by @code{__has_feature}. Unlike @code{__has_feature}, > +@code{__has_extension} tests whether a given feature is available regardless of > +strict language standards conformance. > + > +If the @code{-pedantic-errors} flag is given, @code{__has_extension} is > +equivalent to @code{__has_feature}. > + > @node @code{__has_include} > @subsection @code{__has_include} > @cindex @code{__has_include} > diff --git a/gcc/langhooks-def.h b/gcc/langhooks-def.h > index c6d18526360..46f9af02afa 100644 > --- a/gcc/langhooks-def.h > +++ b/gcc/langhooks-def.h > @@ -151,6 +151,7 @@ extern const char *lhd_get_sarif_source_language (const char *); > #define LANG_HOOKS_GET_SUBSTRING_LOCATION lhd_get_substring_location > #define LANG_HOOKS_FINALIZE_EARLY_DEBUG lhd_finalize_early_debug > #define LANG_HOOKS_GET_SARIF_SOURCE_LANGUAGE lhd_get_sarif_source_language > +#define LANG_HOOKS_REGISTER_FEATURES lhd_do_nothing > > /* Attribute hooks. */ > #define LANG_HOOKS_ATTRIBUTE_TABLE NULL > @@ -394,7 +395,8 @@ extern void lhd_end_section (void); > LANG_HOOKS_RUN_LANG_SELFTESTS, \ > LANG_HOOKS_GET_SUBSTRING_LOCATION, \ > LANG_HOOKS_FINALIZE_EARLY_DEBUG, \ > - LANG_HOOKS_GET_SARIF_SOURCE_LANGUAGE \ > + LANG_HOOKS_GET_SARIF_SOURCE_LANGUAGE, \ > + LANG_HOOKS_REGISTER_FEATURES \ > } > > #endif /* GCC_LANG_HOOKS_DEF_H */ > diff --git a/gcc/langhooks.h b/gcc/langhooks.h > index cca75285fc2..009a03c0db6 100644 > --- a/gcc/langhooks.h > +++ b/gcc/langhooks.h > @@ -643,6 +643,8 @@ struct lang_hooks > languages. */ > const char *(*get_sarif_source_language) (const char *filename); > > + void (*register_features) (); > + > /* Whenever you add entries here, make sure you adjust langhooks-def.h > and langhooks.cc accordingly. */ > }; > diff --git a/gcc/objc/objc-act.cc b/gcc/objc/objc-act.cc > index e4c49e664e1..e80dde07728 100644 > --- a/gcc/objc/objc-act.cc > +++ b/gcc/objc/objc-act.cc > @@ -10359,5 +10359,51 @@ objc_common_tree_size (enum tree_code code) > } > } > > +typedef bool (*objc_feature_p)(); > + > +struct objc_feature_info > +{ > + const char *ident; > + objc_feature_p predicate; > + > + objc_feature_info (const char *name) : ident (name) {} > + objc_feature_info (const char *name, objc_feature_p p) > + : ident (name), predicate (p) {} > + > + bool has_feature (bool) const > + { > + return predicate ? predicate () : true; > + } > +}; > + > +static bool objc_nonfragile_abi_p () > +{ > + return flag_next_runtime && flag_objc_abi >= 2; > +} > + > +static bool objc_has_feature (bool strict_p, const void *arg) > +{ > + auto info = static_cast (arg); > + return info->has_feature (strict_p); > +} > + > +static const objc_feature_info objc_features[] = > +{ > + { "objc_default_synthesize_properties" }, > + { "objc_instancetype" }, > + { "objc_nonfragile_abi", objc_nonfragile_abi_p } > +}; > + > +void > +objc_common_register_features () > +{ > + for (unsigned i = 0; i < ARRAY_SIZE (objc_features); i++) > + { > + const objc_feature_info *info = objc_features + i; > + c_common_register_feature (info->ident, > + objc_has_feature, > + static_cast (info)); > + } > +} > > #include "gt-objc-objc-act.h" > diff --git a/gcc/objc/objc-act.h b/gcc/objc/objc-act.h > index e21ab52d8ca..799e289342e 100644 > --- a/gcc/objc/objc-act.h > +++ b/gcc/objc/objc-act.h > @@ -28,6 +28,10 @@ const char *objc_printable_name (tree, int); > int objc_gimplify_expr (tree *, gimple_seq *, gimple_seq *); > void objc_common_init_ts (void); > const char *objc_get_sarif_source_language (const char *); > +void objc_register_features (); > + > +/* Register features common to Objective-C and Objective-C++. */ > +void objc_common_register_features (); > > /* NB: The remaining public functions are prototyped in c-common.h, for the > benefit of stub-objc.cc and objc-act.cc. */ > diff --git a/gcc/objc/objc-lang.cc b/gcc/objc/objc-lang.cc > index 89b3be48b9e..103b5ca0673 100644 > --- a/gcc/objc/objc-lang.cc > +++ b/gcc/objc/objc-lang.cc > @@ -48,6 +48,8 @@ enum c_language_kind c_language = clk_objc; > #define LANG_HOOKS_TREE_SIZE objc_common_tree_size > #undef LANG_HOOKS_GET_SARIF_SOURCE_LANGUAGE > #define LANG_HOOKS_GET_SARIF_SOURCE_LANGUAGE objc_get_sarif_source_language > +#undef LANG_HOOKS_REGISTER_FEATURES > +#define LANG_HOOKS_REGISTER_FEATURES objc_register_features > > /* Each front end provides its own lang hook initializer. */ > struct lang_hooks lang_hooks = LANG_HOOKS_INITIALIZER; > @@ -58,6 +60,12 @@ objc_get_sarif_source_language (const char *) > return "objectivec"; > } > > +void objc_register_features () > +{ > + objc_common_register_features (); > + c_register_features (); > +} > + > /* Lang hook routines common to C and ObjC appear in c-objc-common.cc; > there should be very few (if any) routines below. */ > > diff --git a/gcc/objcp/objcp-lang.cc b/gcc/objcp/objcp-lang.cc > index 9887209b9c8..c1bf0914a47 100644 > --- a/gcc/objcp/objcp-lang.cc > +++ b/gcc/objcp/objcp-lang.cc > @@ -30,6 +30,7 @@ along with GCC; see the file COPYING3. If not see > > enum c_language_kind c_language = clk_objcxx; > static void objcxx_init_ts (void); > +static void objcxx_register_features (); > > /* Lang hooks common to C++ and ObjC++ are declared in cp/cp-objcp-common.h; > consequently, there should be very few hooks below. */ > @@ -42,6 +43,8 @@ static void objcxx_init_ts (void); > #define LANG_HOOKS_GIMPLIFY_EXPR objc_gimplify_expr > #undef LANG_HOOKS_INIT_TS > #define LANG_HOOKS_INIT_TS objcxx_init_ts > +#undef LANG_HOOKS_REGISTER_FEATURES > +#define LANG_HOOKS_REGISTER_FEATURES objcxx_register_features > > /* Each front end provides its own lang hook initializer. */ > struct lang_hooks lang_hooks = LANG_HOOKS_INITIALIZER; > @@ -87,4 +90,11 @@ objcxx_init_ts (void) > cp_common_init_ts (); > } > > +static void > +objcxx_register_features () > +{ > + objc_common_register_features (); > + cp_register_features (); > +} > + > #include "gtype-objcp.h" > diff --git a/gcc/testsuite/c-c++-common/has-feature-common.c b/gcc/testsuite/c-c++-common/has-feature-common.c > new file mode 100644 > index 00000000000..9a57b11e8e2 > --- /dev/null > +++ b/gcc/testsuite/c-c++-common/has-feature-common.c > @@ -0,0 +1,57 @@ > +/* { dg-do compile } */ > +/* Test __has_{feature,extension} for generic features. */ > + > +#define FEAT(x) (__has_feature (x) && __has_extension (x)) > +#define EXT(x) (__has_extension (x) && !__has_feature (x)) > + > +#if __has_feature (unknown_feature) || __has_extension (unknown_feature) > +#error unknown feature is known! > +#endif > + > +#if !__has_extension (gnu_asm_goto_with_outputs) > +#error > +#endif > + > +#if !EXT (gnu_asm_goto_with_outputs) > +#error > +#endif > + > +#if !EXT (gnu_asm_goto_with_outputs_full) > +#error > +#endif > + > +#if !FEAT (enumerator_attributes) > +#error > +#endif > + > +#if !FEAT (attribute_deprecated_with_message) > +#error > +#endif > + > +#if !FEAT (attribute_unavailable_with_message) > +#error > +#endif > + > +#if !FEAT (enumerator_attributes) > +#error > +#endif > + > +#if !FEAT (tls) > +#error > +#endif > + > +#if defined (__SANITIZE_ADDRESS__) != __has_feature (address_sanitizer) > +#error > +#endif > + > +#if defined (__SANITIZE_ADDRESS__) != __has_extension (address_sanitizer) > +#error > +#endif > + > +#if defined (__SANITIZE_THREAD__) != __has_feature (thread_sanitizer) > +#error > +#endif > + > +#if defined (__SANITIZE_THREAD__) != __has_extension (thread_sanitizer) > +#error > +#endif > diff --git a/gcc/testsuite/g++.dg/ext/has-feature.C b/gcc/testsuite/g++.dg/ext/has-feature.C > new file mode 100644 > index 00000000000..52191b78fd6 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/ext/has-feature.C > @@ -0,0 +1,206 @@ > +// { dg-do compile } > +// { dg-options "" } > + > +#define FEAT(x) (__has_feature(x) && __has_extension(x)) > +#define CXX11 (__cplusplus >= 201103L) > +#define CXX14 (__cplusplus >= 201402L) > + > +#if !FEAT(cxx_exceptions) || !FEAT(cxx_rtti) > +#error > +#endif > + > +#if __has_feature (cxx_access_control_sfinae) != CXX11 > +#error > +#endif > + > +#if !__has_extension (cxx_access_control_sfinae) > +#error > +#endif > + > +#if FEAT(cxx_alias_templates) != CXX11 > +#error > +#endif > + > +#if FEAT(cxx_alignas) != CXX11 > +#error > +#endif > + > +#if FEAT(cxx_alignof) != CXX11 > +#error > +#endif > + > +#if FEAT(cxx_attributes) != CXX11 > +#error > +#endif > + > +#if FEAT(cxx_constexpr) != CXX11 > +#error > +#endif > + > +#if FEAT(cxx_decltype) != CXX11 > +#error > +#endif > + > +#if FEAT(cxx_decltype_incomplete_return_types) != CXX11 > +#error > +#endif > + > +#if FEAT(cxx_default_function_template_args) != CXX11 > +#error > +#endif > + > +#if FEAT(cxx_defaulted_functions) != CXX11 > +#error > +#endif > + > +#if __has_feature (cxx_delegating_constructors) != CXX11 > +#error > +#endif > + > +#if FEAT (cxx_deleted_functions) != CXX11 > +#error > +#endif > + > +#if __has_feature (cxx_explicit_conversions) != CXX11 > +#error > +#endif > + > +#if FEAT (cxx_generalized_initializers) != CXX11 > +#error > +#endif > + > +#if FEAT (cxx_implicit_moves) != CXX11 > +#error > +#endif > + > +#if FEAT (cxx_inheriting_constructors) != CXX11 > +#error > +#endif > + > +#if !__has_extension (cxx_inline_namespaces) > +#error > +#endif > + > +#if __has_feature (cxx_inline_namespaces) != CXX11 > +#error > +#endif > + > +#if FEAT (cxx_lambdas) != CXX11 > +#error > +#endif > + > +#if FEAT (cxx_local_type_template_args) != CXX11 > +#error > +#endif > + > +#if FEAT (cxx_noexcept) != CXX11 > +#error > +#endif > + > +#if __has_feature (cxx_nonstatic_member_init) != CXX11 > +#error > +#endif > + > +#if FEAT (cxx_nullptr) != CXX11 > +#error > +#endif > + > +#if __has_feature (cxx_override_control) != CXX11 > +#error > +#endif > + > +#if FEAT (cxx_reference_qualified_functions) != CXX11 > +#error > +#endif > + > +#if FEAT (cxx_range_for) != CXX11 > +#error > +#endif > + > +#if FEAT (cxx_raw_string_literals) != CXX11 > +#error > +#endif > + > +#if FEAT (cxx_rvalue_references) != CXX11 > +#error > +#endif > + > +#if FEAT (cxx_static_assert) != CXX11 > +#error > +#endif > + > +#if FEAT (cxx_thread_local) != CXX11 > +#error > +#endif > + > +#if FEAT (cxx_auto_type) != CXX11 > +#error > +#endif > + > +#if FEAT (cxx_strong_enums) != CXX11 > +#error > +#endif > + > +#if FEAT (cxx_trailing_return) != CXX11 > +#error > +#endif > + > +#if FEAT (cxx_unicode_literals) != CXX11 > +#error > +#endif > + > +#if FEAT (cxx_unrestricted_unions) != CXX11 > +#error > +#endif > + > +#if FEAT (cxx_user_literals) != CXX11 > +#error > +#endif > + > +#if !__has_extension (cxx_variadic_templates) > +#error > +#endif > + > +#if __has_feature (cxx_variadic_templates) != CXX11 > +#error > +#endif > + > +#if !__has_extension (cxx_binary_literals) > +#error > +#endif > + > +#if __has_feature (cxx_binary_literals) != CXX14 > +#error > +#endif > + > +#if FEAT (cxx_decltype_auto) != CXX14 > +#error > +#endif > + > +#if FEAT (cxx_aggregate_nsdmi) != CXX14 > +#error > +#endif > + > +#if __has_extension (cxx_init_captures) != CXX11 > +#error > +#endif > + > +#if __has_feature (cxx_init_captures) != CXX14 > +#error > +#endif > + > +#if FEAT (cxx_generic_lambdas) != CXX14 > +#error > +#endif > + > +#if FEAT (cxx_relaxed_constexpr) != CXX14 > +#error > +#endif > + > +#if FEAT (cxx_return_type_deduction) != CXX14 > +#error > +#endif > + > +#if __has_feature (cxx_variable_templates) != CXX14 > +#error > +#endif > diff --git a/gcc/testsuite/gcc.dg/asan/has-feature-asan.c b/gcc/testsuite/gcc.dg/asan/has-feature-asan.c > new file mode 100644 > index 00000000000..810b69b8fc8 > --- /dev/null > +++ b/gcc/testsuite/gcc.dg/asan/has-feature-asan.c > @@ -0,0 +1,6 @@ > +/* { dg-do compile } */ > +/* { dg-options "-fsanitize=address" } */ > +#define FEAT(x) (__has_feature (x) && __has_extension (x)) > +#if !FEAT (address_sanitizer) > +#error > +#endif > diff --git a/gcc/testsuite/gcc.dg/has-feature.c b/gcc/testsuite/gcc.dg/has-feature.c > new file mode 100644 > index 00000000000..2fd0b4c7f1d > --- /dev/null > +++ b/gcc/testsuite/gcc.dg/has-feature.c > @@ -0,0 +1,62 @@ > +/* { dg-do compile } */ > +/* { dg-options "" } */ > +/* Test __has_{feature,extension} for C language features. */ > + > +#if !__has_extension (c_alignas) || !__has_extension (c_alignof) > +#error > +#endif > + > +#if !__has_extension (c_atomic) || !__has_extension (c_generic_selections) > +#error > +#endif > + > +#if !__has_extension (c_static_assert) || !__has_extension (c_thread_local) > +#error > +#endif > + > +#if !__has_extension (cxx_binary_literals) > +#error > +#endif > + > +#if __STDC_VERSION__ >= 201112L > +/* Have C11 features. */ > +#if !__has_feature (c_alignas) || !__has_feature (c_alignof) > +#error > +#endif > + > +#if !__has_feature (c_atomic) || !__has_feature (c_generic_selections) > +#error > +#endif > + > +#if !__has_feature (c_static_assert) || !__has_feature (c_thread_local) > +#error > +#endif > + > +#else > +/* Don't have C11 features. */ > +#if __has_feature (c_alignas) || __has_feature (c_alignof) > +#error > +#endif > + > +#if __has_feature (c_atomic) || __has_feature (c_generic_selections) > +#error > +#endif > + > +#if __has_feature (c_static_assert) || __has_feature (c_thread_local) > +#error > +#endif > + > +#endif > + > +#if __STDC_VERSION__ >= 202000L > +/* Have C2x features. */ > +#if !__has_feature (cxx_binary_literals) > +#error > +#endif > + > +#else > +/* Don't have C2x features. */ > +#if __has_feature (cxx_binary_literals) > +#error > +#endif > +#endif > diff --git a/gcc/testsuite/gcc.dg/ubsan/has-feature-ubsan.c b/gcc/testsuite/gcc.dg/ubsan/has-feature-ubsan.c > new file mode 100644 > index 00000000000..e5da1cc5628 > --- /dev/null > +++ b/gcc/testsuite/gcc.dg/ubsan/has-feature-ubsan.c > @@ -0,0 +1,6 @@ > +/* { dg-do compile } */ > +/* { dg-options "-fsanitize=undefined" } */ > +#define FEAT(x) (__has_feature (x) && __has_extension (x)) > +#if !FEAT (undefined_behavior_sanitizer) > +#error > +#endif > diff --git a/gcc/testsuite/obj-c++.dg/has-feature.mm b/gcc/testsuite/obj-c++.dg/has-feature.mm > new file mode 100644 > index 00000000000..77c76173bfb > --- /dev/null > +++ b/gcc/testsuite/obj-c++.dg/has-feature.mm > @@ -0,0 +1,21 @@ > +// { dg-do compile } > + > +#define CXX11 (__cplusplus >= 201103L) > + > +#if !__has_feature (objc_instancetype) > +#error > +#endif > + > +#if !__has_feature (objc_default_synthesize_properties) > +#error > +#endif > + > +// C features should not be available. > +#if __has_extension (c_alignas) || __has_feature (c_alignof) > +#error > +#endif > + > +// C++ features should be available (given the right standard). > +#if __has_feature (cxx_constexpr) != CXX11 > +#error > +#endif > diff --git a/gcc/testsuite/objc.dg/has-feature.m b/gcc/testsuite/objc.dg/has-feature.m > new file mode 100644 > index 00000000000..168b0ce16e7 > --- /dev/null > +++ b/gcc/testsuite/objc.dg/has-feature.m > @@ -0,0 +1,26 @@ > +/* { dg-do compile } */ > + > +#define HAVE_C11 (__STDC_VERSION__ >= 201112L) > + > +#if !__has_feature (objc_instancetype) > +#error > +#endif > + > +#if !__has_feature (objc_default_synthesize_properties) > +#error > +#endif > + > +/* C features should be available as extensions. */ > +#if !__has_extension (c_alignas) > +#error > +#endif > + > +/* And as features given the appropriate C standard. */ > +#if __has_feature (c_alignas) != HAVE_C11 > +#error > +#endif > + > +/* Shouldn't have C++ features even as extensions. */ > +#if __has_feature (cxx_constexpr) || __has_extension (cxx_constexpr) > +#error > +#endif > diff --git a/libcpp/include/cpplib.h b/libcpp/include/cpplib.h > index aef703f8111..3586e2e9399 100644 > --- a/libcpp/include/cpplib.h > +++ b/libcpp/include/cpplib.h > @@ -755,6 +755,9 @@ struct cpp_callbacks > /* Callback to determine whether a built-in function is recognized. */ > int (*has_builtin) (cpp_reader *); > > + /* Callback to determine whether a feature is available. */ > + int (*has_feature) (cpp_reader *, bool); > + > /* Callback that can change a user lazy into normal macro. */ > void (*user_lazy_macro) (cpp_reader *, cpp_macro *, unsigned); > > @@ -959,7 +962,9 @@ enum cpp_builtin_type > BT_HAS_STD_ATTRIBUTE, /* `__has_c_attribute(x)' */ > BT_HAS_BUILTIN, /* `__has_builtin(x)' */ > BT_HAS_INCLUDE, /* `__has_include(x)' */ > - BT_HAS_INCLUDE_NEXT /* `__has_include_next(x)' */ > + BT_HAS_INCLUDE_NEXT, /* `__has_include_next(x)' */ > + BT_HAS_FEATURE, /* `__has_feature(x)' */ > + BT_HAS_EXTENSION /* `__has_extension(x)' */ > }; > > #define CPP_HASHNODE(HNODE) ((cpp_hashnode *) (HNODE)) > diff --git a/libcpp/init.cc b/libcpp/init.cc > index 693feaa31ed..b7081db3907 100644 > --- a/libcpp/init.cc > +++ b/libcpp/init.cc > @@ -435,6 +435,8 @@ static const struct builtin_macro builtin_array[] = > B("__has_builtin", BT_HAS_BUILTIN, true), > B("__has_include", BT_HAS_INCLUDE, true), > B("__has_include_next",BT_HAS_INCLUDE_NEXT, true), > + B("__has_feature", BT_HAS_FEATURE, true), > + B("__has_extension", BT_HAS_EXTENSION, true), > /* Keep builtins not used for -traditional-cpp at the end, and > update init_builtins() if any more are added. */ > B("_Pragma", BT_PRAGMA, true), > diff --git a/libcpp/macro.cc b/libcpp/macro.cc > index dada8fea835..f8b86d0fbfe 100644 > --- a/libcpp/macro.cc > +++ b/libcpp/macro.cc > @@ -677,6 +677,12 @@ _cpp_builtin_macro_text (cpp_reader *pfile, cpp_hashnode *node, > number = builtin_has_include (pfile, node, > node->value.builtin == BT_HAS_INCLUDE_NEXT); > break; > + > + case BT_HAS_FEATURE: > + case BT_HAS_EXTENSION: > + number = pfile->cb.has_feature (pfile, > + node->value.builtin == BT_HAS_FEATURE); > + break; > } > > if (result == NULL)